メインコンテンツへスキップ

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 プリミティブは、エラー緩和ワークフローをカスタマイズする際により高い柔軟性を提供する指示型実行モデルの一部です。

Executor プリミティブの入力と出力は、Sampler や Estimator プリミティブとは大きく異なります。例えば、PUB のリストを入力として受け取る代わりに、Executor は QuantumProgramItem オブジェクトのリストを含む QuantumProgram を受け取ります。これらのコンテナ・クラスは、単純なタプル・データ構造である PUB よりも高い柔軟性を提供します。

Executor の出力は QuantumProgramResult で、これはイテラブルであり、各入力 QuantumProgramItem に対して 1 つの要素を含んでいます。

入力:量子プログラム​

前述のように、Executor プリミティブへの入力は QuantumProgram です。これは QuantumProgramItem オブジェクトのイテラブルです。これらのオブジェクトは 2 種類あります。

  • CircuitItem:通常、回路とそのパラメーター値(あれば)を格納します。
  • SamplexItem:通常、以下を格納します。
    • テンプレート回路
    • ランタイムにランダム化されたパラメーターセットを生成するために使用される samplex オブジェクト(例えばツワーリングの実行やノイズの注入に使用)
    • samplex の引数(元の回路のパラメーター値を含む場合があります)

これらの各アイテムは、Executor が実行する異なるタスクを表します。

始める前に​

このページのコード例の一部では、Samplomatic パッケージの一部である samplex を使用しています。そのため、これらのコード・ブロックを実行する前に、以下のコード・ブロックに示すように Samplomatic をインストールする必要があります。詳細については、Samplomatic ドキュメントを参照してください。

pip install samplomatic

# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]

例:2 つの異なるタスクを持つ QuantumProgram の作成​

まず量子プログラムを初期化し、次に append_circuit_item または append_samplex_item(samplex がある場合)を使用してプログラム・アイテムを追加します。以下の例を参照してください。

次のセルは QuantumProgram を初期化し、プログラム内の各アイテムのすべての設定に対して 1024 ショットを実行するよう指定します。

備考

Sampler とは異なり、QuantumProgram は単一のショット値のみを受け取ります。異なるショット値が必要な場合は、別の QuantumProgram が必要であり、それは別のジョブとなります。

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit_ibm_runtime import Executor, QiskitRuntimeService
from qiskit.circuit import Parameter, QuantumCircuit
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# Initialize an empty program
program = QuantumProgram(shots=1024)

# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)

# `measure_all` adds a 3-bit classical register named "meas"
circuit.measure_all()

# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an
# equivalent Instruction Set Architecture (ISA) circuit.
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)

# Transpile the circuit
isa_circuit = preset_pass_manager.run(circuit)

CircuitItem の追加​

次に、バックエンドの命令セット・アーキテクチャー(ISA)に従ってトランスパイルされたターゲット回路を QuantumProgram に追加します。この回路には 2 つのパラメーターがあるため、パラメーター値も提供する必要があります(この例では 10 セット)。この CircuitItem の実行がプログラムが最初に実行するタスクです。

# Append the transpiled circuit and an array
# containing 10 sets of parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(
10, 2
), # 10 sets of parameter values and 2 parameters
)

SamplexItem の追加​

回路アイテムはランダム化なしで実行されます。一方、samplex アイテムを使用すると、その内容をランダム化する方法を指定できます。次のセルでは、generate_boxing_pass_manager() 関数を使用して回路のゲートと測定をボックスにグループ化し、各ボックスにツワーリング・アノテーションを追加します。次に、build() 関数を使用してテンプレート回路と samplex ペアを生成します。

この SamplexItem の実行がプログラムが 2 番目に実行するタスクです。

samplex とその引数の詳細については、Samplomatic の API ドキュメントを参照してください。generate_boxing_pass_manager() 関数の使用方法については、Samplomatic の Transpiler ガイドを参照してください。

# Transpile the circuit, additionally grouping gates and measurements into annotated boxes
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)

# Use the boxing pass manager to group gates
# and measurements into boxes and add
# a`Twirl` annotation.
preset_pass_manager.post_scheduling = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = preset_pass_manager.run(circuit)

# Build the template circuit and the samplex. The template circuit has parametric gates
# without fixed values and the samplex randomly generates the parameter
# values on the server side at runtime to perform twirling.
template_circuit, samplex = build(boxed_circuit)

# Determine what arguments are required by the samplex.
# Input the arguments in samplex_arguments.
print(samplex.inputs())
TensorInterface(<
- 'parameter_values' <float64[2]>: Input parameter values to use during sampling.
>)
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
# the arguments required by the samplex.sample method
"parameter_values": np.random.rand(10, 2),
},
shape=(28, 10), # 28 randomizations and 10 sets of parameter values
)
# Initialize an Executor with the default options
executor = Executor(mode=backend)

# Submit the job
job = executor.run(program)

# Retrieve the result
result = job.result()

出力​

Executor の出力は QuantumProgramResult で、イテラブルです。入力の順序と同じ順序で、各入力 QuantumProgramItem に対して 1 つのエントリを含みます。これらの出力アイテムはそれぞれ辞書で、キーは入力回路の古典レジスターの名前(およびその他)に対応する文字列です。Sampler の出力のようにこれらの名前を覚える必要はなくなりました。辞書の値は np.ndarray 型です。

前の例の結果には以下のアイテムが含まれています。

CircuitItem の結果​

最初の項目には、プログラム内で最初のタスク(CircuitItem)を実行した結果が含まれます。これは単一のキーmeasを含み、これは入力回路の古典レジスタの名前です。このキーの値は、(parameter sets, shots, register bits)の形状を持つnp.ndarrayにマッピングされ、上記の例では(10, 1024, 3)になります。

以下のコードでこの情報へのアクセス方法を示します。

# Access the results of the classical register of task #0, a CircuitItem
result_0 = result[0]["meas"]
print(f"Result shape: {result_0.shape}")
Result shape: (10, 1024, 3)

SamplexItem の結果​

2番目の項目には、プログラム内で2番目のタスク(SamplexItem)を実行した結果が含まれます。この項目には複数のキーが含まれます。入力回路の古典レジスタの名前であるmeasキーは、そのレジスタの結果の配列にマッピングされます。この配列は、この例では(randomizations, parameter sets, shots, classical bits)の形状、つまり(28, 10, 1024, 3)を持ちます。さらに、出力にはmeasurement_flips.measキーが含まれており、これはmeasレジスタの測定トワリングを元に戻すためのビットフリップ補正です。この出力の形状は、ビットフリップを実行するのに1ショットのみが必要なため、この例では(28, 10, 1, 3)になります。

# Access the results of the classical register of task #1
result_1 = result[1]["meas"]
print(f"Result shape: {result_1.shape}")

# Access the bit-flip corrections
flips_1 = result[1]["measurement_flips.meas"]
print(f"Bit-flip corrections shape: {flips_1.shape}")

# Undo the bit flips via classical XOR
unflipped_result_1 = result_1 ^ flips_1
Result shape: (28, 10, 1024, 3)
Bit-flip corrections shape: (28, 10, 1, 3)

次のステップ​

おすすめ