Executorの例
パッケージバージョン
このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以降を使用することをお勧めします。
qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.21.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
このセクションの例は、Executorプリミティブのいくつかの一般的な使い方を示しています。これらの例を実行する前に、QiskitのインストールとExecutorクイックスタートの指示に従ってください。
始める前に
このページのコード例の一部は、Samplomaticパッケージの一部であるsamplexを使用しています。したがって、これらのコードブロックを実行する前に、次のコードブロックに示すようにSamplomaticをインストールする必要があります。詳細については、Samplomaticドキュメントを参照してください。
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]
例:パラメータ化されたcircuit
この例は、パラメータを持つcircuitアイテムの追加方法と、samplexアイテムの追加方法を示しています。次のステップで構成されています:
- circuitの設定:ターゲットcircuitを生成してトランスパイルする。
- samplexの準備:ゲートと測定をアノテーション付きボックスにグループ化し、circuitテンプレートとsamplexのペアを生成する。
- 実行:
QuantumProgramにcircuitアイテムとsamplexアイテムを追加し、単一のジョブで両方を実行する。
circuitの設定
3-qubit GHZ状態を準備し、qubitをPauli-Z軸の周りに回転させ、計算基底でqubitを測定します。
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()
バックエンドを指定し、QPUがサポートする命令のみを使用するようにcircuitをトランスパイルします(命令セットアーキテクチャ(ISA)circuitと呼ばれます)。
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)
samplexの準備
generate_boxing_pass_manager便利関数とそのtwirlingパラメータを使用して、2-qubitゲートと測定をボックスにグループ化し、twirlingアノテーションを適用します。
boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = boxing_pm.run(isa_circuit)
buildメソッドを使用して、テンプレートcircuitとsamplexを生成します。
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)
circuitの実行
Executorは QuantumProgram オブジェクトを実行します。各 QuantumProgram は複数の項目を含むことができます。この例では、実行用の回路項目とsamplex項目を追加します。詳細については、Executorの入力と出力を参照してください。
最初のステップは、空のプログラムを初期化し、各アイテムの各設定に対して1024ショットを要求することです。
# Generate a quantum program
program = QuantumProgram(shots=1024)
QuantumProgramにcircuitアイテムを追加します。このcircuitアイテムは、ISA circuitと10セットのパラメータ値の2つの部分で構成されています。
# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)
次の引数でsamplexアイテムをQuantumProgramに追加します:
build関数によって生成されたテンプレートcircuitとsamplex- 元のcircuitの10セットのパラメータ値
- 実行するランダム化の数
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)
Executorジョブの実行
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()
各タスクの結果を取得します。
# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]
# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]
例:PECの実行
この例は、samplexアイテムを使用して確率的エラーキャンセル(PEC)によるエラー軽減を実行する方法を示しています。
10個のqubitと2つのCXゲートのユニークなレイヤーを持つcircuitのミラーバージョンを考えてみましょう。これらが主なタスクです:
- twirlingを適用してcircuitを実行する。
- 論文"Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors"のようにPEC軽減を適用してcircuitを実行する。
パイプラインは次のステップで構成されています:
- 設定:ターゲットcircuitを生成し、その操作をボックスにグループ化する。
- 学習:PECで軽減したい命令のノイズを学習する。
- 実行:バックエンドでcircuitを実行する。
- 分析:結果を後処理して分析する。
比較のために、このミラーcircuitを2回実行します。1回目はPauli-twirlingのみ適用し、2回目はPEC軽減を適用します。
この例の使用量は、Heron r2プロセッサで約10分です。
circuitの設定
バックエンドを選択し、10-qubit circuitを準備します。
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Prepare a circuit
num_qubits = 10
num_layers = 10
qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)
for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])
circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)
circuit.draw("mpl", scale=0.35, fold=100)
circuitとその逆を結合してミラーcircuitを作成します。
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)
いくつかのパラメータ値を設定します:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)
パスマネージャーを使用して、circuitをISA circuitにトランスパイルします。
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)
次に、ゲートと測定をアノテーション付きボックスにグループ化します。これは手動で行うか、generate_boxing_pass_manager関数を使用して便利に行うことができます。最初のcircuitにはtwirlingのみが適用されるため、Twirlアノテーションのみが必要です。2番目のcircuitはPEC軽減で実行され、TwirlとInjectNoiseアノテーションの両方が必要です。
# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
mirror_circuit_twirl = boxing_pm.run(isa_circuit)
# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
mirror_circuit_pec = boxing_pm.run(isa_circuit)
ノイズの学習
ノイズ学習実験の数を最小限に抑えるために、2番目のcircuit(InjectNoiseでアノテーション付きのボックスを持つもの)のユニークな命令を特定します。ユニーク性を定義する際、2つのボックス命令は次の両方が真の場合に等しいとみなされます:
- 内容が1-qubitゲートまで等しい。
Twirlアノテーションが等しい(他のすべてのアノテーションは無視される)。
これにより、奇数と偶数のゲートボックス、および最終測定ボックスの3つのユニークな命令が生まれます。
from samplomatic.utils import find_unique_box_instructions
unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3
NoiseLearnerV3を初期化し、そのオプションを設定して学習パラメータを選択し、ノイズ学習ジョブを実行します。
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]
learner_job = learner.run(unique_box_instructions)
learner_job.job_id()
learner_result = learner_job.result()
result.to_dictメソッドを使用して、resultをsamplexが必要とするオブジェクトに変換します。
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)
circuitの実行
ExecutorはQuantumProgramオブジェクトを実行します。各QuantumProgramには、プログラムに追加される複数の項目を含めることができます。各項目は、プログラムが実行するタスクです。
空のプログラムを初期化し、各アイテムの各設定に対して1000ショットを要求します。
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)
次に、mirror_circuit_twirlのテンプレートcircuitとsamplexをビルドし、プログラムに追加します。また、samplexから900ランダム化を要求します。これは、samplexが900セットのパラメータを生成し、各セットがQPUで1000回(shots数)実行されることを意味します。
これはプログラムの最初のタスク(結果0)です。
template_twirl, samplex_twirl = build(mirror_circuit_twirl)
program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)
同様に、mirror_circuit_pecのテンプレートcircuitとsamplexを追加し、900ランダム化を要求します。これはプログラムの2番目のタスク(結果1)です。
template_pec, samplex_pec = build(mirror_circuit_pec)
program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)
Executorをインポートしてジョブを送信します。
from qiskit_ibm_runtime.executor import Executor
executor = Executor(backend)
executor_job = executor.run(program)
executor_job.job_id()
executor_results = executor_job.result()
executor_results
twirl_result = executor_results[0]
print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")
pec_result = executor_results[1]
print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")
Twirl result keys:
['meas', 'measurement_flips.meas']
Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']
Shape of results: (900, 1000, 10)
結果の分析
最後に、10個のアクティブなqubitそれぞれに作用する1-qubit Pauli-Z演算子の期待値(期待値:1.0)を推定するために結果を後処理します。
# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)
# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.71
Qubit 1 -> 0.72
Qubit 2 -> 0.7
Qubit 3 -> 0.68
Qubit 4 -> 0.65
Qubit 5 -> 0.64
Qubit 6 -> 0.62
Qubit 7 -> 0.66
Qubit 8 -> 0.69
Qubit 9 -> 0.75
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)
# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))
# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.98
Qubit 1 -> 1.0
Qubit 2 -> 0.99
Qubit 3 -> 0.97
Qubit 4 -> 0.97
Qubit 5 -> 0.97
Qubit 6 -> 0.96
Qubit 7 -> 0.97
Qubit 8 -> 0.97
Qubit 9 -> 0.98
次のステップ
- ブロードキャスティングの概要を確認する。
- Executorオプションの使い方を学ぶ。
- directed execution modelを理解する。
- Samplomaticドキュメントを確認する。
- directed execution modelを使用する際の異なるエラー軽減技術の組み合わせ方をProbabilistic error cancellation with shaded lightconesチュートリアルで学ぶ。