プロバイダーインターフェース
qiskit.providers
このモジュールには、Qiskitの外部プロバイダを構築するためのクラスが含まれています。 プロバイダとは、Qiskitに外部サービスを提供するものです。 この典型的な例は、バックエンド・プロバイダーである。 Backend オブジェクトを提供します。 QuantumCircuit オブジェクトを提供する。 このモジュールには、プロバイダとQiskitの間のインターフェイスを定義するために使用される抽象クラスが含まれています。
バージョン・サポート
プロバイダー・インターフェースの抽象クラスは、それぞれ個別にバージョン管理されている。 インターフェイスを変更する必要がある場合、新しいインターフェイスを定義するために新しい抽象クラスが作成される。 これらのインターフェイスの変更は、バージョン間の後方互換性を保証するものではありません。
バージョン変更
qiskit の各マイナーバージョンリリースは、バックエンドインターフェースのバージョンを1つのバージョン番号でインクリメントすることができる。 これは、そのインターフェイスにおける、そのリリースのすべてのインターフェイスの変更の集約となる。
バージョン・サポート・ポリシー
プロバイダーがこのインターフェイスの変更に適応する時間を持てるようにするため、Qiskitは各クラスの複数のバージョンを一度にサポートします。 リリースごとに1バージョンという性質を考えると、バージョンの非推奨方針は標準的な非推奨方針よりも少し保守的だ。 Qiskitは、最低3回のマイナーリリース、またはバージョンを導入したリリースから6ヶ月後の最初のリリースのいずれか長い方の期間、非推奨になる可能性がある前にプロバイダインターフェースのバージョンをサポートします。 それ以降は、標準の非推奨ポリシーがそのインターフェイスのバージョンに適用される。 これにより、プロバイダーとユーザーは、インターフェイスの変更に対応するための十分な時間を得ることができる。 例えば、 0.19.0 BackendV2 。 0.19.0 のリリースの3ヶ月後に、 0.20.0、 0.21.0、 0.22.0 をリリースし、 0.19.0 の7ヶ月後に、 0.23.0 をリリースするとする。 0.23.0 では、 BackendV2 を非推奨とすることができる。非推奨ポリシーが完了するまでは、まだサポートされる必要があり、削除することはできない。
Qiskitのバージョンサポート方針は、プロバイダー自身が同じサポートストーリーを持つことを意味するものではなく、プロバイダーはできる限り早く新しいバージョンにアップデートすることができる(そして間違いなくそうすべきです)。 非推奨化の前にこの長い期間を設けるのは、プロバイダーがバージョンを上げる前に、エンドユーザーに影響を与える可能性のあるインターフェイスの変更を、自ら非推奨化するのに十分な時間を与えるためである。 例えば、 BackendV34 、後方互換性のない方法でシグネチャーを Backend.run() に変更したとしよう。 Aerがその AerSimulator クラスをバージョン34をベースにしたものに更新する前に、古いシグネチャを非推奨にする必要がある。 そのため、バージョン33を削除する(つまりバージョン34を必須/最小バージョンとする)前に、Aerが非推奨サイクルを完了するのに十分な時間を確保する必要があります。
抽象クラス
バックエンド
Backend() | すべてのバージョン管理された Backend 抽象クラスの基本共通型。 |
BackendV2([プロバイダ, 名前, 説明,...] ) | バックエンドの抽象クラス |
QubitProperties( [t1, t2, frequency] ) | QubitProperties オブジェクトの表現。 |
オプション
Options(**kwargs) | ベース・オプション・オブジェクト |
ジョブ
ジョブ ステータス
JobStatus(*値) | ジョブ・ステータス列挙型のクラス。 |
例外
QiskitBackendNotFoundError
exception qiskit.providers.QiskitBackendNotFoundError(*message)
ベース: QiskitError
バックエンドを探す際に発生するエラーの基底クラス。
エラーメッセージを設定する。
JobError
exception qiskit.providers.JobError(*message)
ベース: QiskitError
ジョブによって発生するエラーの基本クラス。
エラーメッセージを設定する。
JobTimeoutError
exception qiskit.providers.JobTimeoutError(*message)
ベース: JobError
ジョブが発生させるタイムアウトエラーの基底クラス。
エラーメッセージを設定する。
新しいバックエンドの記述
Qiskitと統合したい量子デバイスやシミュレータがある場合は、バックエンドを書く必要があります。 プロバイダはバックエンドの集合体であり、利用可能なオブジェクトを取得するメソッドをQiskitに提供する。 BackendV2 オブジェクトを取得するメソッドをQiskitに提供します。 この BackendV2 オブジェクトは、バックエンドを説明する情報とその動作を transpiler バックエンドで実行できるように最適化された回路にコンパイルできるようにするためです。 また run() メソッドも提供している。 QuantumCircuit オブジェクトを実行するメソッドも提供している。 これにより、ユーザーや他のQiskit APIは、バックエンドがどのように実装されているかに関係なく、デバイス上で回路を実行した結果を標準的な方法で取得することができます。 プロバイダーの基本的な書き方は次の通りだ:
バックエンドへのアクセスを処理する
Providerクラスを実装する。サブクラスと
BackendV2サブクラスとそのrun()メソッドを実装する。
- バックエンドの基礎となるカスタムゲートをセッションに追加する。
EquivalenceLibraryインスタンスに追加します。実行中のジョブとのやりとりを処理する
JobV1実行中のジョブとのやり取りを処理するサブクラスを実装します。
プロバイダの簡単な例については、 qiskit-aqt-provider
プロバイダー
プロバイダ・クラスの目的はただ一つ、デバイスやシミュレータ上で回路を実行するためのバックエンド・オブジェクトを取得することです。 期待されるのは、必要とされるクレデンシャルや認証は、プロバイダ・オブジェクトの初期化で処理されることである。 プロバイダオブジェクトは、バックエンドのリストと、バックエンドをフィルタリングして取得するメソッドを提供します(必要であれば、提供された認証情報を使用します)。 プロバイダー・クラスの例は次のようになる:
from qiskit.providers.providerutils import filter_backends
from .backend import MyBackend
class MyProvider:
def __init__(self, token=None):
super().__init__()
self.token = token
self.backends = [MyBackend(provider=self)]
def backends(self, name=None, **kwargs):
if name:
backends = [
backend for backend in backends if backend.name() == name]
return filter_backends(backends, filters=filters, **kwargs)(必要な場合)認証に必要な情報がクラス内に含まれていること、およびバックエンドのメソッドが要求されるインターフェースに準拠していることを確認してください。 具体的な実装方法については、各プロバイダーの判断に委ねられます。
バックエンド
バックエンドのクラスはプロバイダーの中核をなすものだ。 これらのクラスは、Qiskitと回路を実行するハードウェアやシミュレータとのインターフェースを提供するものです。 これには、バックエンドを記述するのに必要な情報をコンパイラに提供し、コンパイラがバックエンド用の回路を組み込んで最適化できるようにすることも含まれる。 バックエンドオブジェクトには4つの必須項目がある。 target プロパティ、バックエンドが1つのバッチジョブで実行できる回路数の制限を定義するための max_circuits プロパティ、バックエンドが1つのバッチジョブで実行できる回路数の制限を定義するプロパティ (制限がない場合は None を使用できます)、ジョブの投入を受け付けるメソッド、そして run() メソッド、および _default_options メソッドで、ユーザーが設定可能なオプションとそのデフォルト値を定義します。 例えば、最低限実用的な例としては次のようなものがある:
from qiskit.providers import BackendV2 as Backend
from qiskit.transpiler import Target
from qiskit.providers import Options
from qiskit.circuit import Parameter, Measure
from qiskit.circuit.library import PhaseGate, SXGate, UGate, CXGate, IGate
class Mybackend(Backend):
def __init__(self):
super().__init__()
# Create Target
self._target = Target("Target for My Backend")
# Instead of None for this and below instructions you can define
# a qiskit.transpiler.InstructionProperties object to define properties
# for an instruction.
lam = Parameter("λ")
p_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(PhaseGate(lam), p_props)
sx_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(SXGate(), sx_props)
phi = Parameter("φ")
theta = Parameter("ϴ")
u_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(UGate(theta, phi, lam), u_props)
cx_props = {edge: None for edge in [(0, 1), (1, 2), (2, 3), (3, 4)]}
self._target.add_instruction(CXGate(), cx_props)
meas_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(Measure(), meas_props)
id_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(IGate(), id_props)
# Set option validators
self.options.set_validator("shots", (1, 4096))
self.options.set_validator("memory", bool)
@property
def target(self):
return self._target
@property
def max_circuits(self):
return 1024
@classmethod
def _default_options(cls):
return Options(shots=1024, memory=False)
def run(self, circuits, **kwargs):
# serialize circuits submit to backend and create a job
for kwarg in kwargs:
if not hasattr(self.options, kwarg):
warnings.warn(
"Option %s is not used by this backend" % kwarg,
UserWarning, stacklevel=2)
options = {
'shots': kwargs.get('shots', self.options.shots),
'memory': kwargs.get('memory', self.options.memory),
}
job_json = convert_to_wire_format(circuit, options)
job_handle = submit_to_backend(job_json)
return MyJob(self.job_handle, job_json, circuit)バックエンドのトランスパイラインターフェース
オブジェクトの重要な部分は Backend オブジェクトの重要な部分は、コンパイラにどのように自分自身を説明するかということである。 これは Target このクラスはトランスパイラのバックエンドのモデルを定義します。 バックエンドオブジェクトは Target オブジェクトを返す必要があります。 target 属性からオブジェクトを返す必要があります。 transpile() オブジェクトを返す必要があります。
カスタムベースゲート
-
バックエンドがQiskit回路ライブラリ(
qiskit.circuit.library)のゲートを使用しない場合は、このサポートをプロバイダに統合することができます。 これを行う基本的な方法は、まず基底セット内の各カスタム・ゲートに対してGateサブクラスを定義することです。 例:import numpy as np from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit class SYGate(Gate): def __init__(self, label=None): super().__init__("sy", 1, [], label=label) def _define(self): qc = QuantumCircuit(1) qc.ry(np.pi / 2, 0) self.definition = qc重要なことは、バックエンドの基本設定にあるカスタム・ゲートのname属性(上記の
__init__定義のsuper().__init__()の最初のパラメータ)が、他のゲートの名前と衝突しないようにすることです。 name属性は、トランスパイラの基底セットでゲートを識別するために使用されます。 もし競合があれば、トランスパイラーはどちらのゲートを使うべきかわからなくなる。 -
カスタムゲートをバックエンドのターゲットに追加します。 これは
Target.add_instruction()メソッドで行うことができる。 トランスパイラがその存在を認識できるように、SYGateのインスタンスとそのパラメータをターゲットに追加する必要がある。 例えば、これがバックエンドの実装の一部だとするとBackendV2実装の一部だとします:from qiskit.transpiler import InstructionProperties sy_props = { (0,): InstructionProperties(duration=2.3e-6, error=0.0002), (1,): InstructionProperties(duration=2.1e-6, error=0.0001), (2,): InstructionProperties(duration=2.5e-6, error=0.0003), (3,): InstructionProperties(duration=2.2e-6, error=0.0004), } self.target.add_instruction(SYGate(), sy_props)sy_propsのキーは、バックエンドSYGateが使用できる量子ビットを定義し、値はその量子ビット上のSYGateのプロパティを定義する。 マルチクビット・ゲートの場合、タプル・キーはゲートが動作するすべての量子ビットの組み合わせを含む(順序は重要である。(0, 1)は(1, 0)と異なる。) -
バックエンドの基本セットに使用するカスタム・ゲートを定義したら、標準の等価ライブラリに等価ルールを追加する必要がある。
transpile()関数とtranspilerモジュールがカスタム基底セットを使って任意の回路を変換できるようにする必要があります。 これは、標準的なゲートに対して、カスタム・ゲートの観点から等価回路を定義することによって行うことができる。 通常CXGate(あなたのベースに標準的な2量子ビットゲートが含まれていない場合)また、よく使われる1量子ビットの回転ゲートであるHGateやUGateなどのよく使われる単一量子ビット回転ゲートから変換できれば、トランスパイラがどんな回路もカスタム基底ゲートに変換するのに十分でしょう。 しかし、標準ゲートからあなたの基底への等価ルールがより多く定義されていればいるほど、任意の回路からターゲット基底への翻訳がより効率的になる(ただし、必ずしもそうなるとは限らず、その差は次第に小さくなる)。例えば、上記のカスタム
SYGateのルールを追加する場合、次のように定義できます。U2GateとHGate:from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary from qiskit.circuit.library import HGate from qiskit.circuit.library import ZGate from qiskit.circuit.library import RZGate from qiskit.circuit.library import U2Gate # H => Z SY q = qiskit.QuantumRegister(1, "q") def_sy_h = qiskit.QuantumCircuit(q) def_sy_h.append(ZGate(), [q[0]], []) def_sy_h.append(SYGate(), [q[0]], []) SessionEquivalenceLibrary.add_equivalence( HGate(), def_sy_h) # u2 => Z SY Z phi = qiskit.circuit.Parameter('phi') lam = qiskit.circuit.Parameter('lambda') q = qiskit.QuantumRegister(1, "q") def_sy_u2 = qiskit.QuantumCircuit(q) def_sy_u2.append(RZGate(lam), [q[0]], []) def_sy_u2.append(SYGate(), [q[0]], []) def_sy_u2.append(RZGate(phi), [q[0]], []) SessionEquivalenceLibrary.add_equivalence( U2Gate(phi, lam), def_sy_u2)プロバイダーのパッケージがインポートされるとすぐに実行されるように、インポート時に実行されるようにしたい。 これにより
BasisTranslatorパスがカスタム・ゲートで実行されるたびに、等価ルールが定義される。また、使用しているベースによっては、トランスパイラの最適化パス、たとえば
Optimize1qGatesDecompositionなどのトランスパイラの最適化パスは、あなたのカスタム・ベースでは動作しないかもしれません。SYGateの例ではOptimize1qGatesDecompositionは単一量子ビットゲートの実行をSY基底に単純化することはできない。 これは、OneQubitEulerDecomposerのクラスがSYベースで働く方法を知らないからだ。 これを解決するには、SYGateクラスを Qiskit に追加し、OneQubitEulerDecomposerを更新して、SYGateへの分解をサポートする必要があります。 長期的には、カスタムベースゲートにとってその方が良い方向性である可能性が高く、トランスパイラでの定義とサポートに貢献することで、Qiskitが今後もしっかりとサポートし続けることができるでしょう。
カスタムトランスパイラパス
トランスパイラーは、ハードウェア固有の最適化や回路変換を容易にするために、バックエンドがカスタムのトランスパイラー・ステージ実装を提供する機能をサポートしている。 現在、 get_translation_stage_plugin() と get_scheduling_stage_plugin() の2つのステージがサポートされており、バックエンドはそれぞれデフォルトの翻訳ステージとスケジューリングステージとして使用する文字列プラグイン名を指定することができます。 このような BackendV2 クラスのこれらのフック・ポイントは、バックエンドのコンパイル要件が現在のバックエンド/インターフェイスでは満たされない場合に使うことができます。Target のインターフェイスでは対応できないようなコンパイル要件がある場合に使用します。 また、より多くのハードウェア・アーキテクチャをより深く説明できるように、これらのインターフェースを改善することに関心があるので、あなたの使用例を記述したGithub issueを提出することも検討してください。
これらのフック・ポイントを活用するには、メソッドをあなたの BackendV2 を追加し、文字列のプラグイン名を返すようにすればよい。 例:
class Mybackend(BackendV2):
def get_scheduling_stage_plugin(self):
return "SpecialDD"
def get_translation_stage_plugin(self):
return "BasisTranslatorWithCustom1qOptimization"このバックエンド実装のスニペットは、ターゲットが に設定されているときに transpile() 関数は、ターゲットが Mybackend に設定されている場合、デフォルトでスケジューリングステージに SpecialDD プラグインを使い、翻訳ステージに BasisTranslatorWithCustom1qOptimization プラグインを使います。 ユーザーは、別のプラグイン名を明示的に選択することで、これらの選択肢を上書きすることができることに注意してください。 このインターフェイスが機能するためには、返されたプラグイン名に対してトランスパイラ・ステージ・プラグインが実装されていなければならない。 プラグインの実装方法の詳細については qiskit.transpiler.preset_passmanagers.plugin モジュールのドキュメントを参照してください。 バックエンドがコンパイル・ステージの一部としてカスタム・パスを必要とする場合、プロバイダー・パッケージには、それらのパスを使用するトランスパイラー・ステージのプラグインが含まれるというのが一般的な予想です。 しかし、これは必須ではなく、(組み込みメソッドや外部プラグインの)有効なメソッドを使用することができます。
こうすることで、 Mybackend で実行したり、効率的な出力を提供するために、これら2つのコンパイル・ステップが必要になった場合、トランスパイラーは、ユーザーによる手動入力なしに、これらのカスタム・ステップを実行できるようになる。
リアルタイム変数
トランスパイラーは、リアルタイムに型付けされた古典変数を自動的に処理し ( qiskit.circuit.classical参照)を処理し Store 命令を組み込みの「指令」として扱います。 Barrier. これを許可するために、バックエンドが特別な処理をする必要はない。
バックエンドが古典的な変数やストレージを扱えない場合は、ドキュメントでそのことをコメントし、それらを含む回路を熱心に拒否するために、メソッドにチェックを挿入することをお勧めします( Methodを参照)。 run() メソッドにチェックを入れることをお勧めします ( Backend.run メソッドを参照)。 トップレベルで QuantumCircuit.num_vars トップレベルの変数の有無を調べることができる。 コントロール・フロー操作を受け入れる場合、それぞれの内部を再帰的に検索する必要があるかもしれない。 blocks でスコープ・ローカル変数を再帰的に検索する必要があるかもしれない。 QuantumCircuit.num_declared_vars.
例えば、手動で記憶している場所があるかどうかをチェックする機能や、メモリに手動で記憶している場所があるかどうかをチェックする機能などである:
from qiskit.circuit import Store, ControlFlowOp, QuantumCircuit
def has_realtime_logic(circuit: QuantumCircuit) -> bool:
if circuit.num_vars:
return True
for instruction in circuit.data:
if isinstance(instruction.operation, Store):
return True
elif isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
if has_realtime_logic(block):
return True
return Falseゲイツの角度境界
バックエンドがターゲットのどのゲートに対しても許容されるパラメータ値に制約を持っている場合、これを Target. 命令を add_instruction()angle_bounds キーワード引数を使用すると、ゲートのパラメータの上限と下限のタプルのリストを取ることができます。
例えば、上の例で PhaseGate を追加する代わりに
lam = Parameter("λ")
p_props = {(qubit,): None for qubit in range(5)}
self._target.add_instruction(PhaseGate(lam), p_props, angle_bounds=[(0, math.pi)])は PhaseGate の境界を0から (を含む)の間に設定する。 これは Target の lam パラメータの角度値に関するものである。 PhaseGate. トランス WrapAngles トランスパイラー・パスは PhaseGate を変換します。 ゲートの角度の値を取り込み、その値を返す関数を書く必要がある。 DAGCircuit. 例:
from qiskit.transpiler.passes import WrapAngles
def fold_phase(angles: List[float], qubits: List[int]) -> DAGCircuit:
angle = angles[0]
if angle > 0:
number_of_gates = angle / math.pi
else:
number_of_gates = (6.28 - angle) / math.pi
dag = DAGCircuit()
dag.add_qubits([Qubit()])
for _ in range(int(number_of_gates)):
dag.apply_operation_back(PhaseGate(math.pi), [dag.qubits[0]])
return dag
WrapAngles.DEFAULT_REGISTRY.add_wrapper("phase", fold_phase)この関数は、アウトオブバウンズのゲートを、ターゲットの角度境界とターゲットの他の制約を(特にうまくはないが)尊重するものに変換する。
Backend.run 方法
最も重要なのは、回路をデバイスやシミュレータに実際に提出するために使用される手法 run() である。 runメソッドは、実行のためにバックエンドへ回路を送信し、オブジェクト Job を返す処理を担当します。 バックエンドの種類に応じて、これは通常、回路オブジェクトをバックエンドが使用するAPI形式にシリアライズすることを伴います。 バックエンドのシリアライズ要件は異なる可能性があるため(ローカルシミュレータの場合、シリアライズ自体が必要ない場合もある)、この変換処理はバックエンドの run メソッドで処理されることが想定される。
実行メソッドの例は次のようなものだ:
def run(self, circuits, **kwargs):
for kwarg in kwargs:
if not hasattr(self.options, kwarg):
warnings.warn(
"Option %s is not used by this backend" % kwarg,
UserWarning, stacklevel=2)
options = {
'shots': kwargs.get('shots', self.options.shots),
'memory': kwargs.get('memory', self.options.memory),
}
job_json = convert_to_wire_format(circuit, options)
job_handle = submit_to_backend(job_json)
return MyJob(self.job_handle, job_json, circuit)バックエンドオプション
回路の動作を制御するバックエンドには、多くの場合、いくつかの選択肢があります。 その典型的な例として、回路が何回実行されるかを shots 示す「回数」などが挙げられます。 バックエンドで利用可能なオプションは、オブジェクト Options を使用して定義されます。 このオブジェクトは、バックエンドクラスの _default_options メソッドによって最初に作成されます。 デフォルトのオプションは、バックエンドがサポートするすべてのオプションに対してデフォルト値が設定された初期化済み Options オブジェクトを返します。 たとえば、バックエンドが のみをサポートしている場合 shots 、その _default_options メソッドは次のようになります:
@classmethod
def _default_options(cls):
return Options(shots=1024)また Options オブジェクトにバリデータを設定することもできます。 たとえば、上で定義した "shots" オプションに 1 から 4096 までの任意の値を設定できる場合、 バックエンドのオプションオブジェクトにバリデータを設定することができます:
self.options.set_validator("shots", (1, 4096))検証オプションの全リストは set_validator() ドキュメントを参照してください。
ジョブ
メソッドからの出力は run メソッドからの出力は JobV1 オブジェクトです。 各プロバイダーは、プロバイダーの動作を定義するカスタムジョブサブクラスを実装することが期待される。 ジョブはバックエンドの実行方法によって2種類に分けられる。 デフォルトでは、ジョブは非同期とみなされ、 Backend.run() で提出された回路の非同期実行へのハンドルを表していると期待される。 非同期ジョブオブジェクトは、実行状況を問い合わせたり、実行中のジョブをキャンセルしたり、実行が終了するまでブロックしたりする機能をユーザーに提供します。 を返します。 result は、実行が完了するまでブロックし、ジョブの結果を持つ Result オブジェクトを返します。
一部のバックエンド(主にローカル・シミュレータ)では、回路の実行は同期操作であり、実行中のジョブへのハンドルを他の場所に返す必要はありません。 同期ジョブでは run オブジェクトが生成されるまでバックエンドの Result オブジェクトが生成され、同期ジョブがその内部 Result オブジェクトを返します。
非同期APIベースのバックエンドのジョブクラスの例は以下のようになる:
from qiskit.providers import JobV1 as Job
from qiskit.providers import JobError
from qiskit.providers import JobTimeoutError
from qiskit.providers.jobstatus import JobStatus
from qiskit.result import Result
class MyJob(Job):
def __init__(self, backend, job_id, job_json, circuits):
super().__init__(backend, job_id)
self._backend = backend
self.job_json = job_json
self.circuits = circuits
def _wait_for_result(self, timeout=None, wait=5):
start_time = time.time()
result = None
while True:
elapsed = time.time() - start_time
if timeout and elapsed >= timeout:
raise JobTimeoutError('Timed out waiting for result')
result = get_job_status(self._job_id)
if result['status'] == 'complete':
break
if result['status'] == 'error':
raise JobError('Job error')
time.sleep(wait)
return result
def result(self, timeout=None, wait=5):
result = self._wait_for_result(timeout, wait)
results = [{'success': True, 'shots': len(result['counts']),
'data': result['counts']}]
return Result.from_dict({
'results': results,
'backend_name': self._backend.configuration().backend_name,
'backend_version': self._backend.configuration().backend_version,
'job_id': self._job_id,
'success': True,
})
def status(self):
result = get_job_status(self._job_id)
if result['status'] == 'running':
status = JobStatus.RUNNING
elif result['status'] == 'complete':
status = JobStatus.DONE
else:
status = JobStatus.ERROR
return status
def submit(self):
raise NotImplementedErrorそして同期の仕事だ:
class MySyncJob(Job):
_async = False
def __init__(self, backend, job_id, result):
super().__init__(backend, job_id)
self._result = result
def submit(self):
return
def result(self):
return self._result
def status(self):
return JobStatus.DONEプリミティブ
プロバイダー・インターフェースの直接の一部ではないが、モジュールはプロバイダーと緊密に結合している。 qiskit.primitives モジュールはプロバイダと緊密に結合している。 具体的には、 BaseSampler や BaseEstimator のようなプリミティブなインターフェイスは、プロバイダの実装が、プロバイダのバックエンドに最適化されたカスタム実装を提供できるように設計されている。 これには、回路変換、追加の前後処理、バッチ処理、キャッシング、エラー緩和などのカスタマイズが含まれる。 モジュールのコンセプトは qiskit.primitives プリミティブオブジェクトは、処理されたより高いレベルの出力(確率分布や期待値など)を生成するためのより高いレベルの抽象化であり、効率的に最良の結果を得るための仕組みを抽象化し、これらの出力を使用したより高いレベルのアプリケーションに集中するためである。
例えば、バックエンドが mthreeの測定緩和を活用して結果の質を向上させるのに適している場合、 M3Mitigation クラスを内部的に活用して回路を実行し、mthreeから直接準確率を結果に返すプロバイダ固有の Sampler 実装を実装することができる。 こうすることで、バックエンドから直接適用される軽減措置によって、アルゴリズムが最良の結果を得ることができる。 カスタム実装の書き方については qiskit.primitives のドキュメントを参照されたい。 また、組み込みの実装もある: Sampler Estimator, BackendSampler, BackendEstimator も、これらの実装方法のリファレンス/モデルとして役立つ。