Skip to main content
IBM Quantum Platform

シングルトンに関する指示

qiskit.circuit.singleton

このモジュールに含まれる仕組みは、インスタンス化された際に、共有される不変のシングルトンインスタンスを優先的に返す、および Gate の Instruction サブクラスを定義するためのものです。 XGateを例にとると、ユーザーに提示される最終的な結果は次のようになります:

  • GateXGateという名前の正規クラスが存在し、これは から派生しています。
  • XGateXGateのような XGate(label="my_gate") 処理を行うと、型がまさに であるオブジェクトが生成され、可変性の挙動はすべて期待どおりに動作します。すべてのメソッドは、 Gate、、または親クラスによって定義されたもののみが呼び出されます。
  • これを実行 XGate() すると、型が合成 _SingletonXGate クラスであるシングルトンオブジェクトが生成されます。このクラスは から派生 XGate していますが、 をオーバーライド __setattr__() して自身を不変にしています。 このオブジェクト自体は、シングルトン処理が行われていなかった場合に持つはずだったものと XGate() 、まったく同じインスタンス属性を持っています。 picklecopy()このオブジェクトは、``の下ではそれ自体を返し、 deepcopy() を経由して往復します。

GateMeasure例えば、についても同様のことが言えるが、それは の Instruction サブクラスであるだけで、 のサブクラスではないという点だけが異なる。

注

このモジュールのクラスは上級者向けで、Qiskitの回路データモデルの核心と密接に結びついているからです。

ライブラリ作成者の観点から言えば、この挙動を Gate または Instruction に追加するために最低限必要なのは、 (SingletonInstruction) ではなく Gate (Instruction) から SingletonGate 継承すること、そして メソッドの __init__ すべての引数にデフォルト値を設定することです(これらはシングルトンインスタンスの状態となります)。 例:

class XGate(SingletonGate):
    def __init__(self, label=None):
        super().__init__("x", 1, [], label=label)

assert XGate() is XGate()

インターフェース

Gateパブリッククラスは、それぞれ標準クラス Instruction および に対応しており、これらのサブクラスです。

SingletonInstruction

class qiskit.circuit.singleton.SingletonInstruction(*args, _force_mutable=False, **kwargs)

GitHub

ベース: Instruction, _SingletonBase

デフォルトでシングルトンインスタンスとなるオブジェクトに Instruction 使用する基底クラス。

このクラスは、固定された定義を持ち、固有の状態を含まない命令クラスに使用されるべきである。 このようなものの典型的な例は Measure です。 Measure のどのインスタンスも同じである。 この種のゲートクラスの基本クラスとしてシングルトン命令を使用することで、複数命令のメモリフットプリントにおいて大きな利点が得られる。

ただし、このクラスに関して注意すべき例外として、ゲートの特定のインスタンスごとに異なる設定が可能となる属性 labelInstruction があります。 to_mutable()正しい使用法とするためには SingletonInstruction 、これらの属性を設定することはできず、作成時、あるいは . を使用して明示的に変更可能にされたオブジェクトに対してのみ設定可能です。 作成時にこれらの属性のいずれかが使用された場合、同じゲートの単一の共有グローバルインスタンスを使用する代わりに、新しい個別のインスタンスが作成されます。

SingletonGate

class qiskit.circuit.singleton.SingletonGate(*args, _force_mutable=False, **kwargs)

GitHub

ベース: Gate, _SingletonBase

デフォルトでシングルトンインスタンスとなるオブジェクトに Gate 使用する基底クラス。

SingletonInstructionこのクラスは と非常に似ていますが、 はユニタリ Gate なセマンティクスも意味します。 そのクラスで属性を設定する際の注意点については、ここでも同様です。

SingletonControlledGate

class qiskit.circuit.singleton.SingletonControlledGate(*args, _force_mutable=False, **kwargs)

GitHub

ベース: ControlledGate, _SingletonBase

デフォルトでシングルトンインスタンスとなるオブジェクトに ControlledGate 使用する基底クラス

SingletonInstructionこのクラスは と非常に似ていますが、 はユニタリ ControlledGate なセマンティクスも意味します。 そのクラスで属性を設定する際の注意点については、ここでも同様です。

これらのクラスのいずれかを継承する場合、生成されるクラスは、シングルトンになるように定義された引数でクラスが構築されるたびに返される、eagerlyに作成されたシングルトンインスタンスを持つことになります。 通常、これがデフォルトとなる。 これらのインスタンスは不変です。 TypeError.

の Instruction* すべての*サブクラスは、プロパティ mutable を持っています。 False``Trueほとんどの命令ではこれは ですが、シングルトンインスタンスの場合は です。 この to_mutable() メソッドを使用することで、所有権があり、安全に変更できる命令のインスタンスを取得できます。

シングルトン・インスタンスはベース・クラスの正確なインスタンスではなく、新しいオブジェクトを構築できない特別なサブクラスである。 つまり、以下のようになります。

type(XGate()) is not XGate

正確な値があることに type 頼ってはいけません。型チェックには代わりに を使用 isinstance() してください。 Instructionから基底クラスを確実に取得する必要がある場合は、属性 Instruction.base_class をご覧ください。シングルトンインスタンスでは、これが正しく設定されています。 Qiskit を使用する際、 Instruction.name ほとんどの場合、回路における命令の「意味」を判断する上で、の方がより適切な指標となります。

新しいシングルトンの導出

新しいシングルトン命令を派生させる最も単純な例は、単に正しいベースから継承し、引数に対して不変のデフォルトを持つ __init__() メソッドを提供することである。 例:

from qiskit.circuit.singleton import SingletonInstruction

class MyInstruction(SingletonInstruction):
    def __init__(self, label=None):
        super().__init__("my_instruction", 1, 0, label=label)

assert MyInstruction() is MyInstruction()
assert MyInstruction(label="some label") is not MyInstruction()
assert MyInstruction(label="some label").mutable

シングルトンインスタンスは、コンストラクタのデフォルトをすべて使用する。

また、それ自体がシングルトンである命令から派生することもできる。 クラスのシングルトン性は継承されるが、2つのクラスのシングルトン・インスタンスは異なる:

class MyOtherInstruction(MyInstruction):
    pass

assert MyOtherInstruction() is MyOtherInstruction()
assert MyOtherInstruction() is not MyInstruction()

何らかの理由で SingletonInstructionから派生させたいが、新しい抽象基底クラスを定義する場合など、デフォルトのシングルトン・インスタンスを生成させたくない場合は、クラス定義でキーワード引数 create_default_singleton=False を設定することができる:

class NotASingleton(SingletonInstruction, create_default_singleton=False):
    def __init__(self):
        return super().__init__("my_mutable", 1, 0, [])

assert NotASingleton() is not NotASingleton()

コンストラクタのすべての引数にデフォルトがない場合は、 create_default_singleton=False を設定する必要があります。

および SingletonInstruction その他の関連クラスのサブクラスは、コンストラクタの引数の解釈方法を制御することで、オプションの引数が明示的にデフォルト値に設定された場合でも、シングルトン機構がシングルトンを返せるようにすることができます。

_singleton_lookup_key

static SingletonInstruction._singleton_lookup_key(*_args, **_kwargs)

GitHub

コンストラクタの引数が与えられた場合、取得するシングルトンインスタンスを特定するキータプルを返します。引数がミュータブルオブジェクトを作成しなければならないことを意味する場合は、 None 。

パフォーマンスのため、特別なケースとして、クラス・コンストラクタにゼロ引数が与えられた場合、このメソッドは呼び出されず(例えば、コンストラクション XGate() はこのメソッドを呼び出さないが、 XGate(label=None) は呼び出す)、デフォルトのシングルトンが即座に返される。

この静的メソッドはサブクラスでオーバーライドすることができる(そしておそらくそうすべきである)。 派生シグネチャは、そのクラスの __init__ と一致しなければならない。このメソッドは次に、引数を調べて、それがミュータビリティを必要とするかどうか、あるいは(もしあれば)キャッシュ・キーがどうあるべきかを判断しなければならない。

この関数は、 None または有効な dict キー(すなわち、ハッシュ可能で等式を実装している)のいずれかを返す必要があります。 None を返すということは、生成されたインスタンスはミュータブルでなければならないということだ。 それ以降のシングルトンベースの処理は行われず、クラスの作成はシングルトン処理がなかったかのように進行する。 そうでなければ、返されるキーはハッシュ可能なものであれば何でもよく、特別な意味はない。 このメソッドが同じキーを返すたびに、同じシングルトンインスタンスが返される。 シングルトン性を維持しつつ、設定可能なすべての引数の値のタプルを使用することをお勧めする。

デフォルトの引数や、クラス作成時に additional_singletons に与えられた引数にマッチするキーのみが、実際にシングルトンを返します。その他の値は、標準的なミュータブル・インスタンスを返します。

注

シングルトンマシナリーは、この関数からのハッシュ不可能なリターンを、ミュータブルなインスタンスを返すことで潔く処理する。 サブクラスは、そのキーがハッピーパスでハッシュ可能であることを保証しなければならないが、ユーザーが提供する引数がハッシュ可能であることを手動で検証する必要はない。 例えば、このように実装するのが無難だ:

@staticmethod
def _singleton_lookup_key(*args, **kwargs):
    return None if kwargs else args

たとえユーザーが args。

これはすべてのQiskit標準ライブラリゲートによって設定され、 label および類似のキーワード引数がデフォルトの場合はキー計算で無視され、そうでない場合はミュータブルインスタンスが返されます。

クラス定義の additional_singletons 引数を使えば、シングルトン・インスタンスを生成するコンストラクタ引数の他の組み合わせを指定することもできる。 これは、 (args, kwargs) タプルのイテラブルを受け取り、 cls(*args, **kwargs) と同等のシングルトンを構築する。 この場合、デフォルトの引数のケースを処理する必要はない。 例えば、クラスの定義があるとする:

class MySingleton(SingletonGate, additional_singletons=[((2,), {"label": "two"})]):
    def __init__(self, n=1, label=None):
        super().__init__("my", n, [], label=label)

    @staticmethod
    def _singleton_lookup_key(n=1, label=None):
        return (n, label)

の2つのシングルトンインスタンスがインスタンス化される。 一つは n=1 と label=None に対応し、もう一つは n=2 と label="two" に対応する。これら2つのケースのどちらかに一致する引数で MySingleton。 例:

assert MySingleton() is MySingleton(1, label=None)
assert MySingleton(2, "two") is MySingleton(n=2, label="two")

引数がゼロのクラスがインスタンス化された場合は、特別に処理され、内部ループのパフォーマンスを絶対的に高速にすることができる(ただし、一般的な機械はいずれにせよ絶望的に遅いわけではない)。


実装

注

このセクションは、主に開発者向けのコード説明である。ここで説明されている機械はどれも公開されておらず、そのいずれかを直接継承することは安全ではない。

ここでは、取り組むべき課題がいくつかあります。 type.__call__が、 *を *__init__ 呼び出さずに、の XGate (不正確な)インスタンスであるシングルトンオブジェクトを返すという XGate() 挙動を実現するには、をオーバーライドする必要があります。 つまり、には XGate 、シングルトンインスタンスを返すように定義 __call__ するメタクラスが存在しなければならない。

次に、 XGate() が返すシングルトンインスタンスがあることを確認する必要がある。 これは呼び出しのたびに動的に行うこともできるが(つまり、インスタンスが存在するかどうかをチェックし、存在しない場合はインスタンスを作成する)、インスタンスを非常に特別なものにしたいので、 XGate 型オブジェクトの定義中にフックして作成する方が簡単である。 これはまた、シングルトン・オブジェクトをピックル可能にする必要がないという利点もある。ベース・タイプ・オブジェクトの生成によってシングルトンが再作成されるため、ピックル解除時にそれをどこから取り出すかを指定するだけでよい。

シングルトン・インスタンスにさせたい:

  • 自分自身を変異させようとする試みはすべて拒否しなければならない。
  • は、シングルトンを扱わない場合の XGate() とまったく同じ状態になる。

これを3つのステップで行う:

  1. シングルトンを作成する前に、と Instruction を Gate 不変にするために必要なオーバーライドを個別に定義します。 これは およびその他の _*Overrides クラスです _SingletonInstructionOverrides 。
  2. XGate 、 そのサブクラスを動的に作成する。このサブクラスは、メソッド解決順序の不変オーバーライドを適切な場所に配置する。 これらは、ミュータブルゲートで定義されている標準的なメソッドやプロパティをオーバーライドします(作成する型オブジェクトに余分なインプレースメソッドがある場合は、オーバーライドしようとしません)。
  3. この新しいサブクラスをインスタンス化することはできない。というのも、このサブクラスが XGate.__init__。 その代わりに、まず完全に通常の XGate インスタンスを作成し、その型を動的にシングルトン・クラスに変更して凍結させる。

これを完全にメタクラスの仕組みの中で行うこともできるが、その場合は XGate :

class XGate(Gate, metaclass=_SingletonMeta, overrides=_SingletonGateOverrides): ...

これはすごく不便です(そうでなければ、非常に扱いにくいイントロスペクションをたくさん行わ _SingletonMeta なければならないことになります)。 abc.ABCその代わりに、メタクラスを設定し、適用するオーバーライドを選択し、上記のシングルトンサブクラス生成手順を適用するメソッド __init_subclass__() を持つ具体的なミドルクラス(SingletonGate この XGate 場合は)を定義する「/abc.ABCMeta 」パターンを採用しています。 オーバーライドは別々のクラスに定義されているため、 XGate* 変更可能な*インスタンスのメソッド解決順序にはそれらが含まれません。この方法の方が実装は簡単ですが、インスタンスの変更が許可されているかどうかを検証するために、実行時にすべてのセッターやチェッカーが複雑に連携して動作する必要があります。

Instruction_SingletonMeta最後に、これらすべての仕組みを実際に構築するために、その基盤となるのが であり、これは のあらゆるメタクラスと互換性のあるメタクラスである。 これは、シングルトンインスタンスを返すように を上書き type.__call__ する メカニズムを __call__() 定義するものです。 __new__()もう一つの構成要素は、その であり、これは および SingletonInstruction の SingletonGate 生成時に(非自明な形で)呼び出され、その overrides キーワード引数が設定されることで、上記の性質を持つそれらのクラスの __init_subclass__ が定義される。 superこのメソッドを動的に追加するためにメタクラスを使用しています。これは、この __init_subclass__() 仕組みが抽象的なものであり、および基底クラ overrides スをクロージャとして閉じ込めつつも、依然としてを呼び出せるようにするためです。 superこれを動的に行う方が便利です。目的のクラス変数をキャプチャし、2引数の形式を使用します。なぜなら、引数なしの形式では、その関数が定義された場所に基づいて「魔法のような」イントロスペクションが行われてしまうからです。

複数のシングルトンを扱うには、メソッドやピクル化を to_mutable() 定義できるように、初期化引数を何らかの形で保存しておく必要があります。 これは*、シング*ルトン型のオブジェクトに対するルックアップ辞書として実装しています。 これは論理的にはインスタンス属性ですが、基底型のインスタンスに対して動的に _Singleton 型を切り替える必要があるため、かなり複雑になってしまいます。基底型にすでにインスタンス辞書が存在することを必須条件とするか、さもなければ切り替えの際にレイアウトが __slots__ 崩れてしまうリスクを負うことになります。 シングルトンの存続期間は、その基底クラスの型オブジェクトがガベージコレクションされるまで続くため、インスタンスポインタを保存したいデータにマッピングした型オブジェクト辞書を用いて、このインスタンス辞書を擬似的に作成することができます。 別の方法としては、各シングルトンごとに、初期化子の引数を閉じ込める(あるいは格納する)新しい型オブジェクトを作成することも考えられますが、型オブジェクトはかなり負荷が高く、いずれにせよ原理はほぼ同じです。

このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。