Input dan output Sampler
Versi paket
Kode di halaman ini dikembangkan menggunakan persyaratan berikut. Kami sarankan pakai versi ini atau yang lebih baru.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
Halaman ini memberikan gambaran umum input dan output primitif Sampler Qiskit Runtime, yang mengeksekusi workload pada sumber daya komputasi IBM Quantum®. Sampler memungkinkan Anda mendefinisikan workload vektor secara efisien menggunakan struktur data yang dikenal sebagai Primitive Unified Bloc (PUB). Ini digunakan sebagai input ke metode run() untuk primitif Sampler, yang mengeksekusi workload yang didefinisikan sebagai job. Kemudian, setelah job selesai, hasilnya dikembalikan dalam format yang bergantung pada PUB yang digunakan serta opsi runtime yang ditentukan dari primitif.
Input
Setiap PUB dalam format:
(<sirkuit tunggal>, <satu atau lebih nilai parameter opsional>, <shots opsional>),
Bisa ada beberapa item parameter values, dan setiap item bisa berupa array atau parameter tunggal, tergantung sirkuit yang dipilih. Selain itu, input harus mengandung pengukuran.
Untuk primitif Sampler, PUB bisa mengandung paling banyak tiga nilai:
- Satu
QuantumCircuit, yang bisa mengandung satu atau lebih objekParameterCatatan: Sirkuit ini juga harus menyertakan instruksi pengukuran untuk masing-masing qubit yang akan disampling. - Kumpulan nilai parameter untuk mengikat sirkuit terhadap (hanya diperlukan jika ada objek
Parameteryang harus diikat saat runtime) - (Opsional) jumlah shots untuk mengukur sirkuit
Kode berikut mendemonstrasikan contoh set input yang divektorkan ke primitif Sampler dan mengeksekusinya pada Backend IBM® sebagai satu objek RuntimeJobV2.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
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,
SamplerV2 as 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()
Output
Setelah satu atau lebih PUB dikirim ke QPU untuk eksekusi dan job berhasil selesai, data dikembalikan sebagai objek kontainer PrimitiveResult yang diakses dengan memanggil metode RuntimeJobV2.result(). PrimitiveResult berisi daftar yang bisa diiterasi dari objek SamplerPubResult yang berisi hasil eksekusi untuk setiap PUB. Data ini adalah sampel dari output sirkuit.
Setiap elemen dalam daftar ini sesuai dengan PUB yang dikirimkan ke metode run() primitif (misalnya, job yang dikirim dengan 20 PUB akan mengembalikan objek PrimitiveResult yang berisi daftar 20 objek SamplerPubResult, satu untuk setiap PUB).
Setiap objek SamplerPubResult memiliki atribut data dan metadata.
- Atribut
dataadalahDataBinyang dikustomisasi berisi nilai pengukuran aktual, deviasi standar, dan sebagainya. Data bin adalah objek seperti dict yang berisi satuBitArrayperClassicalRegisterdalam sirkuit. - Kelas
BitArrayadalah kontainer untuk data shot yang terurut. Ia menyimpan bitstring yang disampling sebagai byte di dalam array dua dimensi. Sumbu paling kiri array ini berjalan di atas shot yang terurut, sementara sumbu paling kanan berjalan di atas byte. - Atribut
metadataberisi informasi tentang opsi runtime yang digunakan (dijelaskan nanti di bagian Metadata hasil di halaman ini).
Berikut adalah garis besar visual dari struktur data 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
├── ...
├── ...
└── ...
Sederhananya, satu job mengembalikan objek PrimitiveResult dan berisi daftar satu atau lebih objek SamplerPubResult. Objek SamplerPubResult ini kemudian menyimpan data pengukuran untuk setiap PUB yang dikirimkan ke job.
Sebagai contoh pertama, mari lihat sirkuit sepuluh qubit berikut:
# 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:
[[ 0 0]
[ 3 255]
[ 0 0]
...
[ 3 255]
[ 2 255]
[ 3 255]]
Terkadang lebih mudah untuk mengonversi dari format byte di BitArray ke bitstring. Metode get_count mengembalikan dictionary yang memetakan bitstring ke jumlah kemunculannya.
# optionally, convert away from the native BitArray format to a dictionary format
counts = data.meas.get_counts()
print(f"Counts: {counts}")
Counts: {'0000000000': 1649, '1111111111': 1344, '1111111000': 26, '1101111111': 40, '1111110000': 20, '0010000000': 32, '1000000000': 67, '1111110110': 4, '0000011110': 4, '0000000001': 78, '0010100000': 1, '1100000000': 37, '1111111110': 126, '1111110111': 35, '1111011111': 32, '0011111000': 1, '1011110111': 1, '0000011111': 48, '1111000000': 14, '0110000000': 1, '1110111110': 2, '1110011111': 4, '1111100000': 19, '1101111000': 1, '1111111011': 8, '0001011111': 3, '1110000000': 31, '0000000111': 25, '1110000001': 3, '0011111111': 24, '0000100000': 7, '1111111101': 30, '1111101111': 16, '0111111111': 37, '0000011101': 4, '0101111111': 4, '1011111110': 2, '0000000010': 17, '1011111111': 20, '0000100111': 1, '0010000111': 1, '1011010000': 1, '1101101111': 2, '1011110000': 1, '1000000001': 4, '0000001000': 23, '0011111110': 8, '1111111001': 1, '1100111111': 2, '0000011000': 2, '0001111110': 2, '0000111111': 20, '0001111111': 33, '1110111111': 11, '1010000000': 3, '0111011111': 2, '0000000100': 2, '0000000110': 2, '0000001111': 22, '0111101111': 1, '0000010111': 1, '0000000011': 15, '0001000010': 1, '1111111100': 19, '1111101000': 1, '0000001110': 2, '1011110100': 1, '0001000000': 11, '1001111111': 2, '0100000000': 6, '1100000011': 2, '1000001110': 1, '1100001111': 1, '0000010000': 3, '1101111110': 5, '0001111101': 1, '0001110111': 1, '0011000000': 2, '0111101110': 1, '1100000001': 1, '1111000001': 1, '0000000101': 1, '1101110111': 2, '0011111011': 1, '0000111110': 1, '1111101110': 3, '1111001000': 1, '1011111100': 1, '1111110101': 2, '1101001111': 1, '1111011110': 3, '1000011111': 1, '0000001001': 2, '1111010000': 1, '1110100010': 1, '1111110001': 2, '1101110000': 2, '0000010100': 1, '0111111110': 2, '0001000001': 1, '1000010000': 1, '1111011100': 1, '0111111100': 1, '1011101111': 1, '0000111101': 1, '1100011111': 2, '1101100000': 1, '1111011011': 1, '0010011111': 1, '0000110111': 3, '1111100010': 1, '1110111101': 1, '0000111001': 1, '1111100001': 1, '0001111100': 1, '1110011110': 1, '1100000010': 1, '0011110000': 1, '0001100111': 1, '1111010111': 1, '0010000001': 1, '0010000011': 1, '1101000111': 1, '1011111101': 1, '0000001100': 1}
Ketika sirkuit mengandung lebih dari satu classical register, hasilnya disimpan dalam objek BitArray yang berbeda. Contoh berikut memodifikasi cuplikan sebelumnya dengan membagi classical register menjadi dua register yang berbeda:
# 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>)
Menggunakan objek BitArray untuk post-processing yang efisien
Karena array umumnya menawarkan performa yang lebih baik dibandingkan dictionary, disarankan untuk melakukan post-processing langsung pada objek BitArray daripada pada dictionary jumlah. Kelas BitArray menawarkan berbagai metode untuk melakukan beberapa operasi post-processing umum:
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:
[[0]
[0]
[0]
...
[0]
[0]
[0]]
The shape of register `beta` is (4096, 2).
The bytes in register `beta`, shot by shot:
[[ 0 0]
[ 1 248]
[ 0 0]
...
[ 0 0]
[ 0 0]
[ 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:
[[0]
[0]
[0]
...
[0]
[0]
[0]]
The shape of `beta` after shot-wise slicing is (5, 2).
The bytes in `beta` after shot-wise slicing:
[[ 0 0]
[ 1 248]
[ 0 0]
[ 0 0]
[ 0 0]]
Exp. val. for observable `SparsePauliOp(['ZZZZZZZZZ'],
coeffs=[1.+0.j])` is: 0.07470703125
Exp. val. for observable `SparsePauliOp(['IIIIIIIIZ'],
coeffs=[1.+0.j])` is: 0.0244140625
The shape of the merged results is (4096, 2).
The bytes of the merged results:
[[ 0 0]
[ 3 240]
[ 0 0]
...
[ 0 0]
[ 0 0]
[ 0 0]]
Metadata hasil
Selain hasil eksekusi, kedua objek PrimitiveResult dan SamplerPubResult mengandung atribut metadata tentang job yang dikirimkan. Metadata yang mengandung informasi untuk semua PUB yang dikirimkan (seperti berbagai opsi runtime yang tersedia) dapat ditemukan di PrimitiveResult.metatada, sementara metadata yang spesifik untuk setiap PUB ditemukan di SamplerPubResult.metadata.
Metadata hasil Sampler juga mencakup informasi waktu eksekusi yang disebut rentang eksekusi.
Di bidang metadata, implementasi primitif bisa mengembalikan informasi apa pun yang berkaitan dengan eksekusi yang relevan bagi mereka, dan tidak ada pasangan kunci-nilai yang dijamin oleh primitif dasar. Oleh karena itu, metadata yang dikembalikan dalam implementasi primitif yang berbeda mungkin berbeda.
# 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-05-13 14:23:00', stop='2026-05-13 14:23:02', size=4096>)])},
'version' : 2,
The metadata of the PubResult result is:
'circuit_metadata' : {},
Melihat rentang eksekusi
Hasil job SamplerV2 yang dieksekusi di Qiskit Runtime mengandung informasi waktu eksekusi dalam metadatanya. Informasi waktu ini bisa digunakan untuk menetapkan batas waktu atas dan bawah pada waktu eksekusi shots tertentu pada QPU. Shots dikelompokkan ke dalam objek ExecutionSpan, masing-masing menunjukkan waktu mulai, waktu berhenti, dan shots mana yang dikumpulkan dalam rentang tersebut.
Rentang eksekusi mengidentifikasi data yang dieksekusi selama jendela waktunya dengan menyediakan metode ExecutionSpan.mask. Metode ini, ketika diberi indeks Primitive Unified Block (PUB) apa pun, mengembalikan mask boolean yang True untuk semua shots yang dieksekusi selama jendela waktunya. PUB diindeks dalam urutan di mana mereka diberikan ke panggilan run Sampler. Jika PUB memiliki bentuk (2, 3) dan dijalankan dengan empat shots, bentuk mask adalah (2, 3, 4). Lihat halaman API execution_span untuk detail lengkap.
Untuk melihat informasi rentang eksekusi, lihat metadata hasil yang dikembalikan oleh SamplerV2, yang datang dalam bentuk objek ExecutionSpans. Objek ini adalah kontainer seperti daftar yang berisi instance subkelas ExecutionSpan, seperti SliceSpan.
Contoh:
# Define two circuits, each with one parameter with two parameters.
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)
params = np.random.uniform(size=(2, 3)).T
sampler_pub = (transpiled_circuit, params)
# Instantiate the new Estimator object, then run the transpiled circuit
# using the set of parameters and observables.
job = sampler.run([sampler_pub], shots=4)
result = job.result()
spans = job.result().metadata["execution"]["execution_spans"]
print(spans)
ExecutionSpans([DoubleSliceSpan(<start='2026-05-13 14:23:20', stop='2026-05-13 14:23:21', size=24>)])
from qiskit.primitives import BitArray
# Get the mask of the 1st PUB for the 0th span.
mask = spans[0].mask(0)
# Decide whether the 0th shot of parameter set (1, 2) occurred in this span.
in_this_span = mask[2, 1, 0]
# Create a new bit array containing only the PUB-1 data collected during this span.
bits = result[0].data.meas
filtered_data = BitArray(bits.array[mask], bits.num_bits)
Rentang eksekusi bisa difilter untuk menyertakan informasi yang berkaitan dengan PUB tertentu, yang diidentifikasi dengan indeksnya:
# take the subset of spans that reference data in PUBs 0 or 2
spans.filter_by_pub([0, 2])
ExecutionSpans([DoubleSliceSpan(<start='2026-05-13 14:23:20', stop='2026-05-13 14:23:21', size=24>)])
Tampilkan informasi umum tentang kumpulan rentang eksekusi:
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)
Number of execution spans: 1
Start of the first span: 2026-05-13 14:23:20.441518
End of the last span: 2026-05-13 14:23:21.564845
Total duration (s): 1.123327
Ekstrak dan periksa rentang tertentu:
spans.sort()
print(" Start of first span:", spans[0].start)
print(" End of first span:", spans[0].stop)
print("#shots in first span:", spans[0].size)
Start of first span: 2026-05-13 14:23:20.441518
End of first span: 2026-05-13 14:23:21.564845
#shots in first span: 24
Ada kemungkinan bahwa jendela waktu yang ditentukan oleh rentang eksekusi yang berbeda bisa tumpang tindih. Ini bukan karena QPU mengeksekusi beberapa operasi secara bersamaan, melainkan merupakan produk dari pemrosesan klasik tertentu yang mungkin terjadi secara bersamaan dengan eksekusi kuantum. Jaminan yang diberikan adalah bahwa data yang ditunjukkan pasti terjadi dalam rentang eksekusi yang dilaporkan, tetapi tidak harus bahwa batas jendela waktu sesempit mungkin.