Samplerの入力と出力
パッケージバージョン
このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以降を使用することをお勧めします。
qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
このページでは、IBM Quantum® Compute Service上でワークロードを実行するqiskit-ibm-runtimeのSamplerプリミティブの入力と出力の概要を説明します。Samplerでは、Primitive Unified Bloc (PUB)と呼ばれるデータ構造を使用して、ベクトル化されたワークロードを効率的に定義できます。これらは、定義されたワークロードをジョブとして実行するSamplerプリミティブのrun()メソッドへの入力として使用されます。その後、ジョブが完了すると、使用されたPUBとプリミティブで指定されたランタイムオプションの両方に依存する形式で結果が返されます。
入力
各PUBは次の形式です:
(<single circuit>, <one or more optional parameter value>, <optional shots>),
複数のparameter valuesアイテムを含めることができ、各アイテムは選択したcircuitに応じて配列または単一パラメータのいずれかになります。また、入力には測定値が含まれている必要があります。
Samplerプリミティブの場合、PUBには最大3つの値を含めることができます:
- 1つ以上の
Parameterオブジェクトを含む場合がある単一のQuantumCircuit注意:これらのcircuitには、サンプリングする各qubitの測定命令も含める必要があります。 - circuitに対してバインドするパラメータ値のコレクション (ランタイムでバインドする必要がある
Parameterオブジェクトが使用されている場合のみ必要) - (オプション)circuitを測定するshotsの数
以下のコードは、Sampler プリミティブに対するベクトル化された入力の例のセットを示しており、これらを単一の RuntimeJobV2 オブジェクトとしてIBM® backendで実行します。
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit.circuit import (
Parameter,
QuantumCircuit,
ClassicalRegister,
QuantumRegister,
)
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives.containers import BitArray
from qiskit_ibm_runtime import QiskitRuntimeService
# Legacy server-side primitive before qiskit-ibm-runtime 0.50.0
# from qiskit_ibm_runtime import Sampler
# Client-side primitive introduced in qiskit-ibm-runtime 0.50.0
from qiskit_ibm_runtime.executor_sampler import Sampler
import numpy as np
# Instantiate runtime service and get
# the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Define a circuit with two parameters.
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.ry(Parameter("a"), 0)
circuit.rz(Parameter("b"), 0)
circuit.cx(0, 1)
circuit.h(0)
circuit.measure_all()
# Transpile the circuit
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
transpiled_circuit = pm.run(circuit)
layout = transpiled_circuit.layout
# Now define a sweep over parameter values, the last axis of dimension 2 is
# for the two parameters "a" and "b"
params = np.vstack(
[
np.linspace(-np.pi, np.pi, 100),
np.linspace(-4 * np.pi, 4 * np.pi, 100),
]
).T
sampler_pub = (transpiled_circuit, params)
# Instantiate the new Sampler object, then run the transpiled circuit
# using the set of parameters and observables.
sampler = Sampler(mode=backend)
job = sampler.run([sampler_pub])
result = job.result()
出力
1つ以上のPUBがQPUに送信されてジョブが正常に完了すると、データはRuntimeJobV2.result()メソッドを呼び出してアクセスするPrimitiveResultコンテナオブジェクトとして返されます。PrimitiveResultには、各PUBの実行結果を含むSamplerPubResultオブジェクトの反復可能なリストが含まれています。これらのデータはcircuit出力のサンプルです。
このリストの各要素は、プリミティブのrun()メソッドに送信されたPUBに対応しています(たとえば、20個のPUBで送信されたジョブは、各PUBに対応する20個のSamplerPubResultオブジェクトのリストを含むPrimitiveResultオブジェクトを返します)。
各SamplerPubResultオブジェクトはdata属性とmetadata属性の両方を持っています。
data属性は、実際の測定値、標準偏差などを含む、カスタマイズされたDataBinです。データビンはcircuit内のClassicalRegisterごとに1つのBitArrayを含むdict状のオブジェクトです。BitArrayクラスは、順序付きshotデータのコンテナです。サンプリングされたビット文字列を2次元配列内のバイトとして格納します。この配列の左端の軸は順序付きshotを横断し、右端の軸はバイトを横断します。metadata属性には、使用されたランタイムオプションに関する情報が含まれています(このページの後半の結果メタデータセクションで説明します)。
以下はPrimitiveResultデータ構造の視覚的な概要です:
└── PrimitiveResult
├── SamplerPubResult[0]
│ ├── metadata
│ └── data ## In the form of a DataBin object
│ ├── NAME_OF_CLASSICAL_REGISTER
│ │ └── BitArray of count data (default is 'meas')
| |
│ └── NAME_OF_ANOTHER_CLASSICAL_REGISTER
│ └── BitArray of count data (exists only if more than one
| ClassicalRegister was specified in the circuit)
├── SamplerPubResult[1]
| ├── metadata
| └── data ## In the form of a DataBin object
| └── NAME_OF_CLASSICAL_REGISTER
| └── BitArray of count data for second pub
├── ...
├── ...
└── ...
簡単に言えば、単一のジョブはPrimitiveResultオブジェクトを返し、1つ以上のSamplerPubResultオブジェクトのリストを含みます。これらのSamplerPubResultオブジェクトは、ジョブに送信された各PUBの測定データを格納します。
最初の例として、次の10qubit circuitを見てみましょう:
# generate a ten-qubit GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# append measurements with the `measure_all` method
circuit.measure_all()
# transpile the circuit
transpiled_circuit = pm.run(circuit)
# run the Sampler job and retrieve the results
sampler = Sampler(mode=backend)
job = sampler.run([transpiled_circuit])
result = job.result()
# the data bin contains one BitArray
data = result[0].data
print(f"Databin: {data}\n")
# to access the BitArray, use the key "meas", which is the default name of
# the classical register when this is added by the `measure_all` method
array = data.meas
print(f"BitArray: {array}\n")
print(f"The shape of register `meas` is {data.meas.array.shape}.\n")
print(f"The bytes in register `alpha`, shot by shot:\n{data.meas.array}\n")
Databin: DataBin(meas=BitArray(<shape=(), num_shots=4096, num_bits=10>))
BitArray: BitArray(<shape=(), num_shots=4096, num_bits=10>)
The shape of register `meas` is (4096, 2).
The bytes in register `alpha`, shot by shot:
[[ 3 247]
[ 0 0]
[ 3 223]
...
[ 0 255]
[ 0 0]
[ 0 0]]
BitArrayのバイト形式からビット文字列に変換することが便利な場合があります。get_countメソッドは、ビット文字列をその出現回数にマッピングする辞書を返します。
# optionally, convert away from the native BitArray format to a dictionary format
counts = data.meas.get_counts()
print(f"Counts: {counts}")
Counts: {'1111110111': 28, '0000000000': 1680, '1111011111': 35, '1111111111': 1467, '1110000000': 28, '0000000010': 15, '1111111011': 15, '1110111111': 7, '1000000000': 52, '1111101111': 16, '1111110000': 22, '0000000101': 3, '0001111101': 1, '0000000111': 17, '0000000001': 66, '1111111110': 119, '0000001000': 15, '0011111111': 24, '1111111101': 38, '0000001111': 32, '1100000000': 39, '1101111111': 51, '0000011111': 34, '1110000010': 2, '0010000000': 25, '0111111111': 21, '0001111111': 34, '1011111111': 16, '1000011111': 3, '0000001101': 1, '1111100000': 22, '1111111100': 22, '1111111000': 8, '0010001011': 1, '0011000000': 1, '0000000011': 16, '0011111100': 2, '1011110001': 1, '1101111100': 1, '1101111101': 1, '0000000110': 2, '0000000100': 2, '0001111110': 6, '0000001110': 4, '0011110111': 2, '1101111110': 3, '0111111011': 1, '0110000000': 1, '0000001011': 2, '1111110110': 3, '0111111000': 1, '1010000000': 2, '0001110111': 1, '1111101000': 1, '0010011110': 1, '1111100111': 1, '0111110111': 1, '0100000000': 5, '1101110111': 1, '0000001001': 1, '0010000001': 1, '1111000001': 1, '0000010000': 3, '1111101110': 1, '1111000000': 8, '0001011111': 3, '1000000001': 1, '1111010111': 2, '0010011111': 1, '1011111110': 1, '1101101111': 1, '1111011110': 2, '1111111010': 1, '1111010000': 1, '1101000000': 1, '1100001111': 1, '1100011111': 1, '0000111111': 4, '0010111111': 1, '1000000110': 1, '1110111110': 1, '1101011111': 2, '0000011000': 1, '1101110000': 2, '1011111011': 1, '1000111111': 1, '1011000000': 1, '1110000001': 2, '0101111111': 2, '1111110101': 1, '0010000011': 1, '0000011110': 1, '0000011101': 1, '0111000000': 1, '0001110000': 1, '0001000000': 4, '1101111000': 1, '1011111000': 1, '0111110000': 1, '1101110110': 1, '0011011111': 1, '0000100000': 2, '1111101100': 1, '1100001000': 1, '1010000011': 1, '0011111110': 1, '1010001110': 1, '1000001000': 1, '1000000011': 1, '1111101011': 1}
circuitに複数の古典レジスタが含まれている場合、結果は異なるBitArrayオブジェクトに格納されます。次の例では、前のスニペットを変更して古典レジスタを2つの個別のレジスタに分割します:
# generate a ten-qubit GHZ circuit with two classical registers
circuit = QuantumCircuit(
qreg := QuantumRegister(10),
alpha := ClassicalRegister(1, "alpha"),
beta := ClassicalRegister(9, "beta"),
)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# append measurements with the `measure_all` method
circuit.measure([0], alpha)
circuit.measure(range(1, 10), beta)
# transpile the circuit
transpiled_circuit = pm.run(circuit)
# run the Sampler job and retrieve the results
sampler = Sampler(mode=backend)
job = sampler.run([transpiled_circuit])
result = job.result()
# the data bin contains two BitArrays, one per register, and can be accessed
# as attributes using the registers' names
data = result[0].data
print(f"BitArray for register 'alpha': {data.alpha}")
print(f"BitArray for register 'beta': {data.beta}")
BitArray for register 'alpha': BitArray(<shape=(), num_shots=4096, num_bits=1>)
BitArray for register 'beta': BitArray(<shape=(), num_shots=4096, num_bits=9>)
高パフォーマンスな後処理のためのBitArrayオブジェクトの使用
配列は一般的に辞書よりもパフォーマンスが優れているため、カウントの辞書ではなくBitArrayオブジェクトに対して直接後処理を行うことが推奨されます。BitArrayクラスは、一般的な後処理操作を実行するためのさまざまなメソッドを提供します:
print(f"The shape of register `alpha` is {data.alpha.array.shape}.")
print(f"The bytes in register `alpha`, shot by shot:\n{data.alpha.array}\n")
print(f"The shape of register `beta` is {data.beta.array.shape}.")
print(f"The bytes in register `beta`, shot by shot:\n{data.beta.array}\n")
# post-select the bitstrings of `beta` based on having sampled "1" in `alpha`
mask = data.alpha.array == 0b1
ps_beta = data.beta[mask[:, 0]]
print(f"The shape of `beta` after post-selection is {ps_beta.array.shape}.")
print(f"The bytes in `beta` after post-selection:\n{ps_beta.array}")
# get a slice of `beta` to retrieve the first three bits
beta_sl_bits = data.beta.slice_bits([0, 1, 2])
print(
f"The shape of `beta` after bit-wise slicing is {beta_sl_bits.array.shape}."
)
print(f"The bytes in `beta` after bit-wise slicing:\n{beta_sl_bits.array}\n")
# get a slice of `beta` to retrieve the bytes of the first five shots
beta_sl_shots = data.beta.slice_shots([0, 1, 2, 3, 4])
print(
f"The shape of `beta` after shot-wise slicing is {beta_sl_shots.array.shape}."
)
print(
f"The bytes in `beta` after shot-wise slicing:\n{beta_sl_shots.array}\n"
)
# calculate the expectation value of diagonal operators on `beta`
ops = [SparsePauliOp("ZZZZZZZZZ"), SparsePauliOp("IIIIIIIIZ")]
exp_vals = data.beta.expectation_values(ops)
for o, e in zip(ops, exp_vals):
print(f"Exp. val. for observable `{o}` is: {e}")
# concatenate the bitstrings in `alpha` and `beta` to "merge" the results of the two
# registers
merged_results = BitArray.concatenate_bits([data.alpha, data.beta])
print(f"\nThe shape of the merged results is {merged_results.array.shape}.")
print(f"The bytes of the merged results:\n{merged_results.array}\n")
The shape of register `alpha` is (4096, 1).
The bytes in register `alpha`, shot by shot:
[[1]
[0]
[1]
...
[0]
[1]
[0]]
The shape of register `beta` is (4096, 2).
The bytes in register `beta`, shot by shot:
[[ 1 255]
[ 0 0]
[ 1 255]
...
[ 0 0]
[ 1 255]
[ 0 0]]
The shape of `beta` after post-selection is (0, 2).
The bytes in `beta` after post-selection:
[]
The shape of `beta` after bit-wise slicing is (4096, 1).
The bytes in `beta` after bit-wise slicing:
[[7]
[0]
[7]
...
[0]
[7]
[0]]
The shape of `beta` after shot-wise slicing is (5, 2).
The bytes in `beta` after shot-wise slicing:
[[ 1 255]
[ 0 0]
[ 1 255]
[ 1 255]
[ 1 255]]
Exp. val. for observable `SparsePauliOp(['ZZZZZZZZZ'],
coeffs=[1.+0.j])` is: 0.0595703125
Exp. val. for observable `SparsePauliOp(['IIIIIIIIZ'],
coeffs=[1.+0.j])` is: 0.02783203125
The shape of the merged results is (4096, 2).
The bytes of the merged results:
[[ 3 255]
[ 0 0]
[ 3 255]
...
[ 0 0]
[ 3 255]
[ 0 0]]
結果メタデータ
実行結果に加えて、PrimitiveResultオブジェクトとSamplerPubResultオブジェクトの両方に、送信されたジョブに関するメタデータ属性が含まれています。送信されたすべてのPUBの情報(利用可能な各種ランタイム・オプションなど)を含むメタデータはPrimitiveResult.metatadaにあり、各PUBに固有のメタデータはSamplerPubResult.metadataにあります。
クライアント側の Sampler は、そのワークロードを Executor ジョブとして投入します。基盤となる Executor の結果のメタデータには、実行スパンと呼ばれる実行タイミング情報が含まれており、PrimitiveResult.metadata の "executor" キーの下で利用できます。
metadataフィールドでは、プリミティブの実装は実行に関連する任意の情報を返すことができ、基本プリミティブによって保証されるキーと値のペアはありません。したがって、返されるmetadataはプリミティブの実装によって異なる場合があります。
# Print out the results metadata
print("The metadata of the PrimitiveResult is:")
for key, val in result.metadata.items():
print(f"'{key}' : {val},")
print("\nThe metadata of the PubResult result is:")
for key, val in result[0].metadata.items():
print(f"'{key}' : {val},")
The metadata of the PrimitiveResult is:
'execution' : {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2026-09-01 07:46:47', stop='2026-09-01 07:46:49', size=4096>)])},
'version' : 2,
The metadata of the PubResult result is:
'circuit_metadata' : {},
実行スパンの表示
クライアント側の Sampler はジョブの実行に Executor を使用し、Executor はその処理を QPU 上で_チャンク_単位で実行します。各チャンクのタイミングは result.metadata["executor"].chunk_timing にあり、ChunkSpan オブジェクトのリストです。各 ChunkSpan には start と stop のタイムスタンプと parts のリストがあり、各 part は、アイテムのインデックス(idx_item)と、そのチャンクで実行されたそのアイテムの構成の数(size)を示します。
Sampler は PUB ごとに1つの Executor プログラムアイテムを作成するため、idx_item は、PUB が run() に渡された順序での PUB のインデックスです。ツワーリングが有効な場合、各パラメーター値セットの各ランダム化が、別々の構成として数えられます。
タイミングを可視化するには、次の例のように、リストを draw_chunk_timings に渡します。
チャンクのタイムスタンプの意味、プロットの読み方、チャンク間に間隔が生じる理由については、Executor の入力と出力のガイドの実行スパンを表示するを参照してください。
# Define a circuit with one parameter.
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.ry(Parameter("a"), 0)
circuit.cx(0, 1)
circuit.h(0)
circuit.measure_all()
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
transpiled_circuit = pm.run(circuit)
# Define two PUBs, each with a different set of parameter values
sampler_pubs = [
(transpiled_circuit, np.random.uniform(size=(10, 1))),
(transpiled_circuit, np.random.uniform(size=(20, 1))),
]
job = sampler.run(sampler_pubs, shots=1024)
result = job.result()
from qiskit_ibm_runtime.visualization import draw_chunk_timings
# Note the "executor" key: the timing comes from the underlying Executor job
chunk_timing = result.metadata["executor"].chunk_timing
for idx, span in enumerate(chunk_timing):
duration = (span.stop - span.start).total_seconds()
print(f"Chunk {idx}: {span.start} to {span.stop} ({duration:.3f} s)")
for part in span.parts:
print(f" PUB {part.idx_item}: {part.size} configurations")
fig = draw_chunk_timings(chunk_timing, names="Sampler job")
# Uncomment the following line to display the figure
# fig.show(renderer="notebook")
# Save to a file
# fig.write_html("chunk_timings.html")
Chunk 0: 2026-11-06 01:36:36.203348+00:00 to 2026-11-06 01:36:45.420844+00:00 (9.217 s)
PUB 0: 10 configurations
PUB 1: 20 configurations
レガシー Sampler の実行スパン
qiskit-ibm-runtime v0.50.0 以降、レガシーのサーバー側 Sampler は非推奨です。クライアント側の Sampler は ExecutionSpans オブジェクトを返しません。代わりに、前述のチャンクのタイミングを使用してください。移行手順については、クライアント側プリミティブへの移行を参照してください。
詳細はこちらをクリック。
レガシーのサーバー側 Sampler のジョブの結果には、実行タイミング情報が result.metadata["execution"]["execution_spans"] に、ExecutionSpans オブジェクトの形式で含まれています。このオブジェクトは、ExecutionSpan オブジェクトのリストのようなコンテナーで、各オブジェクトは開始時刻、終了時刻、およびそのスパンで収集されたショットの指定を示します。
実行スパンは、ExecutionSpan.maskメソッドを提供することで、そのウィンドウ中にどのデータが実行されたかを指定します。このメソッドは、任意のPUBインデックスを指定すると、そのウィンドウ中に実行されたすべてのshotに対してTrueのブールマスクを返します。たとえば、PUBの形状が(2, 3)で4 shotsで実行された場合、マスクの形状は(2, 3, 4)になります。詳細については、execution_span APIページをご覧ください。
from qiskit.primitives import BitArray
spans = result.metadata["execution"]["execution_spans"]
# Get the mask of the 1st PUB for the 0th span.
mask = spans[0].mask(0)
# Create a new bit array containing only the PUB-0 data collected during this span.
bits = result[0].data.meas
filtered_data = BitArray(bits.array[mask], bits.num_bits)
# Take the subset of spans that reference data in PUBs 0 or 2
spans.filter_by_pub([0, 2])
# View global information about the collection of execution spans
print("Number of execution spans:", len(spans))
print(" Start of the first span:", spans.start)
print(" End of the last span:", spans.stop)
print(" Total duration (s):", spans.duration)
従来の実行スパンを可視化するには、draw_execution_spans関数を使用します。
異なる実行スパンで指定されたタイムウィンドウが重なる可能性があります。これは QPU が複数の実行を同時に行っているからではなく、量子実行と並行して発生しうる特定の古典的処理の副産物です。保証されているのは、参照されたデータが報告された実行スパン内で確実に発生したということであり、タイムウィンドウの範囲が可能な限り厳密であるとは限りません。