Sampler から Executor への移行
このガイドでは、量子サンプリングのワークロードを「 IBM Quantum® 」のSampler プリミティブからExecutorプリミティブへ移行する方法について説明します。
「Executor」プリミティブは、 指向型実行モデルの一部です。 指向型実行モデルのすべてのコンポーネントは現在ベータ版であり、安定していない可能性があります。 ぜひこれらを試用し、 Samplomatic または qiskit-ibm-runtime の GitHub リポジトリでイシューを登録して、フィードバックをお寄せください。
移行すべきでしょうか?
誰もがSamplerからExecutorへ移行すべきというわけではありません。 プリミティブ間には多くの違いがありますが、以下の指針を参考に、移行すべきかどうかを判断することができます
量子情報科学者の方で、 実用規模の実験を行い、パウリ・トゥワーリング、ノイズモデルの学習・注入、基底 変換といった手法に対して、きめ細やかで再現性のある制御を必要とする場合、あるいは Executorが提供する追加機能のいずれかを必要とする場合は、 Executorへの移行をご検討ください。
シンプルで高水準なインターフェースを求め、プリミティブにエラーの抑制や軽減を自動的に処理させたい場合は、 引き続きSamplerを使用してください。
制限事項および注意事項
Executor およびディレクテッド実行モデルはベータ版であるため、移行を決定する前に以下の点にご注意ください:
- まだシミュレータはサポートされていません :ローカル
シミュレーション
qiskit-aer用の実装がAerSampler用意されているSamplerとは異なり、Executor用のシミュレータ・バックエンドは現在存在しません。 シミュレーターの 対応はまもなく開始される見込みです。 その間も、ハードウェアに送信する前に、 ローカル環境でテンプレート回路を確認 ・試行して、 ワークフローの妥当性を検証することができます。 - このガイドでは、サンプラーについてのみ説明しており、エスティメーターについては扱っていません。 Estimator から Executor への移行は、Sampler からの移行よりもかなり手間がかかります。これは、Estimator が 生のサンプルを返すのではなく、期待値を計算するためです。 Estimatorの挙動を Executorで再現するには、追加の後処理が必要となります。 Estimator から Executor への移行を支援する ユーティリティ関数は現在開発中であるため、このガイドでは 意図的に Sampler のワークフローについてのみ説明しています。
ExecutorとSamplerの主な違い
「Sampler」と「Executor」はどちらも量子回路の出力レジスタからサンプルを取得しますが、 対象となるユーザーは異なります:
- サンプラーは高レベルの抽象化です。 これには次のような特徴があります。
- エラー抑制機能(動的デカップリングおよびツイリング)が組み込まれています。
- それは、あなたの代わりに暗黙のうちに決定を下します。
- この設計により、アルゴリズム開発者はデータではなくイノベーションに注力できるようになっています。 conversion.
- Executorは、 ディレクテッド実行モデルの一部です。 これはSamplerとは多くの点で異なり、次のような特徴があります:
-
エラーの抑制や軽減機能は組み込まれていません。 その代わりに、クライアント側で(回路アノテーションとサンプレックスを使用して)設計意図を 反映させ、コストのかかる回路バリアントの生成を サーバー側に移行します。
-
暗黙の決定は一切行いません。 お客様の指示に忠実に従い、完全な制御と透明性を提供します。
-
ExecutorとSamplomaticを組み合わせることで、Samplerにはない追加機能が利用可能になります。これには、以下(ただしこれらに限定されません)が含まれます:
- その他のツイリンググループ:「Samplomatic」では、ボックスごとに適用するツイリンググループを選択できます。
「Sampler」のように、システムが自動的に適用する単一の戦略に限定されることはありません。 また、パウリ以外のツイリング群、例えばツイ
"local_c1"リング 群などもサポートしています。 - カーネル化および分類済みの測定値をまとめて出力:この設定
QuantumProgram.meas_level = "both"( v0.48.0qiskit-ibm-runtimeで追加)では、 ジョブごとに単一の測定タイプを選択するのではなく、 結果に分類済みおよびカーネル化された測定値の両方が含まれるようにします。 - 分数ゲートを用いた回路のトゥワリング:Executorは、 分数ゲートを含む回路に対してトゥワリングを適用できます。
- きめ細やかで組み合わせ可能なエラー軽減:例えば、 どの回路層でエラー軽減を行うかを選択したり、回路に注入するノイズ率を調整したりすることなどです。
ノート- 今後追加される新機能は、まずExecutor向けにリリースされる予定であり、 Samplerには移植されない可能性があります。 最新の機能へのアクセスを重視するのであれば、 Executorの方が将来を見据えた選択となります。
- Qiskitの基本パッケージには、
Executorプリミティブ用の基底クラスはまだ用意されていません(ただし、[他のプリミティブ]用
SamplerV2には用意されています)。
- その他のツイリンググループ:「Samplomatic」では、ボックスごとに適用するツイリンググループを選択できます。
「Sampler」のように、システムが自動的に適用する単一の戦略に限定されることはありません。 また、パウリ以外のツイリング群、例えばツイ
-
概念マップ
次の表は、Samplerの概念がExecutorにどのように対応するかを示しています。
概念 | サンプラー | 実行プログラム |
|---|---|---|
| インポート | from qiskit_ibm_runtime import SamplerV2 | from qiskit_ibm_runtime import Executor |
| 入力 | PUB の一覧(タプル) | A個 QuantumProgram のオブジェクト QuantumProgramItem |
| 回路とパラメータ | (circuit, params, shots) タプル (tuple) | program.append_circuit_item(circuit, circuit_arguments=...) |
| くるくる回る | TwirlingOptions | 注釈付きボックスとsamplex(append_samplex_item)を用いて明示的に示す |
| 呼び出しの実行 | sampler.run([pub, ...]) | executor.run(program) |
| 結果のタイプ | PrimitiveResult/SamplerPubResult | QuantumProgramResult (反復可能) |
| データへのアクセス | result[0].data.<register> (BitArray) | result[0]["<register>"] (np.ndarray) |
| 騒音の管理 | 組み込みオプション | 手動で作成する必要があります(アノテーション、samplexなど NoiseLearnerV3) |
移行手順の概要
- Samplomatic をインストールしてください。
- インポートを変更してください。
- PUB のタプルを置き換える。
- ショットの表現方法を変える。
- 必要に応じて、その他のオプションも更新してください。
- コマンド
runを更新してください。 - 結果の解析を更新しました。
- くるくる回すのをやめる。
ステップ 1. 必要なパッケージをインストールしてください
Executor および指示型実行モデルでは、次のパッケージ samplomatic が必要です:
pip install qiskit qiskit-ibm-runtime samplomatic
# For visualization support:
# pip install samplomatic[vis]qiskit-ibm-runtimev0.48.0 このオプションmeas_level = "both"とlocal_c1ツイリンググループが追加されるため、推奨されます。qiskit >= 2.3.0は必須です。samplomatic >= 0.18.0は必須です。
ステップ 2. インポートを変更する
サンプル:
from qiskit_ibm_runtime import SamplerV2 as Sampler遺言執行者:
from qiskit_ibm_runtime import Executor, QuantumProgramステップ 3. PUB のタプルを、aに置き換えてください。 QuantumProgram
Executor を使用する場合、タプルのリスト(PUBs)を渡す代わりに、を構築し QuantumProgram 、そこに項目を追加していきます。
A は、 回路アイテムおよびサンプレックスアイテムを受け付けます QuantumProgram :
-
append_circuit_item: 回路である「CircuitItem,」と、(オプションで)その パラメータ値を追加します。 ランダム化は一切行わず、そのまま実行されます。これは、単に回路のサンプリングを行いたい場合に使用します。これは、Samplerが「 PUB 」に対して、 「twirling」を適用せずに処理を行うのと同じ動作です。例えば、単純なサンプリングジョブを送信する場合や、 必要なバリアントをすでに手動で含めている場合などが該当します。
-
append_samplex_item: テンプレート回路と、サーバー側でランダムなパラメータセットを生成する samplex を組み合わせた「samplexItem,」を追加します。回路の内容をランダムにしたい場合は、 これを使用してください。 主なケースとしては、 (ゲートまたは測定値の)変動やノイズの混入が挙げられます。 この機能は、 Samplerに組み込まれているツイーリング機能に代わるものです。
1つの は両方のアイテムタイプを受け入れる QuantumProgram ことができます。追加された各アイテムは
独立したタスクとして実行され、結果に独自のエントリを生成します。 一般的に、回路をランダム化する必要がない append_circuit_item 場合は、を使用してください。 それ以外の場合は、. を使用してください append_samplex_item。
次のセクションでは、それぞれについて順に説明します。すなわち、を使用
append_circuit_itemするパラメータ化回路と、を用いてツイリングを移行させる方法です append_samplex_item。
以下のコード例において、 は、ターゲットバックエンドの命令セットアーキテクチャ (ISA)に準拠するようにトランスパイルされた回路を指します isa_circuit 。 これには2つのパラメータが含ま isa_circuit れています。
ステップ 3a。 パラメータ化された回路の移行
Sampler では、パラメータ値は PUB タプルの 2 番目の要素となります。 Executor を使用する場合は、
それらを として circuit_arguments に渡します append_circuit_item。
サンプル:
params = np.random.rand(10, circuit.num_parameters) # 10 parameter sets
pubs = (isa_circuit, params)実行プログラム
program = QuantumProgram(shots=1024)
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, circuit.num_parameters), # 10 sets
)
# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]ステップ 3b。 組み込みの「twirling」を明示的なアノテーションに移行する
これが最も大きな変更点です。 Samplerでは、オプションを設定することで、自動的にツイリング効果を適用します。 Executor では、 アノテーション付きのボックスと ( Samplomatic の)samplex を使用することで、その意図を明示的に宣言します。
サンプル(オプションを使用して回転させる):
sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = Trueエグゼキューター(箱とサンプレックスを使ってくるくる回す):
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
enable_gates=True, # gate twirling
enable_measures=True, # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)
# 2. Build the (template circuit, samplex) pair.
# The template circuit's single-qubit gates are replaced by parameterized gates;
# the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)
# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(10, 2), # original circuit params
},
shape=(28, 10), # 28 randomizations x 10 parameter sets
)テンプレート回路とサンプレックスはクライアント側で構築されるため、 ハードウェアにデータを送信する前に、ローカルでそれらを検査・サンプリングして 出力を確認することができます。
検証:テンプレート回路をローカルで試作する
サンプレックスからランダムな値を抽出し、それらをテンプレート回路にバインドすることで、
サンプレックスが期待通りのパラメータ値を生成していることを確認できます。
によって返されるパラメータ値は、
テンプレート回路のパラメータと直接互換性がありますsamplex.sample。
# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())
# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
parameter_values=np.random.rand(2), # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)
# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)さらに踏み込んで、各ランダム化が元の回路と論理的に同値であることを検証するには、例えば、両方を オブジェクトOperatorに変換し、そのユニタリ実装を比較する(その際、
測定ツイリングを元に戻すための 補正outputs["measurement_flips.<register>"]を考慮した上で)、あるいはローカル
StatevectorSamplerまたは の実行StatevectorEstimatorからの期待値を比較する方法があります。 詳しい手順については、『Samplomatic
Samplex 入力・出力
ガイド』をご覧ください。
ステップ 4. ショットの注文方法を変更する
PUB にあるショットを に移動します QuantumProgram(shots=...)。 「Executor」では、この設定はジョブ全体に適用 shots されます。 ショット数が異なる場合は、複数のジョブを送信してください。
サンプル:
# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])遺言執行者:
# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)ステップ5. 必要に応じてオプションを更新してください
Executorで利用可能なオプションはSamplerよりも少ないです。これは、 エラー軽減のための設定が、オプションではなく、アノテーションやsamplex内に配置されるようになったためです。
また、設定が格納される場所にも構造的な違いがあります。
-
Samplerでは、 結果の後処理に影響を与える設定を含め、すべての項目が プリミティブのオプションまたは PUB で設定されます。
-
Executor では、ジョブの 結果の形成や後処理に影響を与える設定は、ではなく
QuantumProgram、で設定されますExecutorOptions。
例:
サンプラー | 実行プログラム |
|---|---|
shots | QuantumProgram(shots=...) |
meas_type | QuantumProgram(meas_level=...) |
ExecutorOptions 返されるデータの構造を変更しない、低レベルの実行および環境
設定のみを保持します。 これには3つの最上位グループがあります:
environment(EnvironmentOptions)execution(ExecutionOptions): 「Sampler」に比べてオプションの数が少ない。 たとえば、「meas_typeExecutor」というオプションはありません。experimental
特に、Sampler には および twirling オプション dynamical_decoupling が存在しますが、Executor には存在しません。 その代わりに、これらのオプションの値は、指向型実行モデルを通じて表現される。
例:
from qiskit_ibm_runtime import Executor, ExecutorOptions
options = ExecutorOptions(
environment={"log_level": "INFO"},
execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True
executor = Executor(mode=backend, options=options)ステップ 6. コマンド run を更新する
Executorジョブの入力は、PUBではなくプログラムです。
サンプル:
# Submit a job
sampler.run([(isa_circuit, parameter_values)])遺言執行者:
# Submit a job
executor.run(program)ステップ7. 検索結果の表示方法を変更する
Executor では、結果はオブジェクト BitArray ではなく、 NumPy の配列となります。 名前文字列をインデックス (result[0]["meas"]) として使用し、を np.ndarray 返します。 属性 .data.<register> のパスを覚えておく必要はありません。
SamplerからExecutorへ更新するには、 result[i].data.<reg> (BitArray) を に変更し result[i]["<reg>"] (np.ndarray)、- get_countsbasedの後処理を NumPy 操作として書き直してください。
タスク | サンプラー | 実行プログラム |
|---|---|---|
| 登録データを取得する | result[0].data.meas | result[0]["meas"] |
| データ・タイプ | BitArray | np.ndarray |
| カウント辞書 | result[0].data.meas.get_counts() | 配列を手動で後処理する |
| 複数のレジスタ | result[0].data.<name> レジスタごと | result[0]["<name>"] レジスタごと |
| CircuitItem 配列の形状 | - | (parameter_sets, shots, register_bits) |
| SamplexItem 配列の形状 | - | (randomizations, parameter_sets, shots, register_bits) |
| 測定の回転を元に戻す | 自動 | result[i]["measurement_flips.<name>"] + XOR |
Sampler's では、ヘルパー(get_counts、 slice_bits、 slice_shots、 expectation_values、および選択後マスク)を提供 BitArray しています。 Executorは NumPy の生の配列を返すため、標準の NumPy 演算を使用してこの後処理を行うことができます。
ステップ8. ビット反転による結果への対応(ビット反転の修正)
「twirling」による測定を適用すると SamplexItem、Executor は生の
(twirling 処理済みの)測定値に加え、twirling 処理を元に戻すために必要なビット反転補正値を返します。
これらを手動で適用する必要があります。自動的に修正されることはありません。
Executor を使用する際は、次の例に示すように、補正 measurement_flips.<reg> と XOR 演算を用いて、ツイリングを明示的に解除してください
# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"] # example: (28, 10, 1024, 2)
# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"] # example: (28, 10, 1, 2)
# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1Samplerには同等の手順はありません。なぜなら、Samplerが自動的にツイリングを解除してくれるからです。
完全な例:基本的なサンプリングジョブの移行
サンプラー
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()
# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()
# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()実行プログラム
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()
# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)
# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()
# 6. Access results: a plain np.ndarray keyed by register name
# shape = (shots, register_bits)
meas = result[0]["meas"]