Mga Input at Output ng Sampler
Mga bersyon ng package
Ang code sa pahinang ito ay binuo gamit ang mga sumusunod na kinakailangan. Inirerekomenda naming gamitin ang mga bersyong ito o mas bago.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
Ang pahinang ito ay nagbibigay ng pangkalahatang-ideya ng mga input at output ng Qiskit Runtime Sampler primitive, na nagpapatakbo ng mga workload sa mga compute resource ng IBM Quantum®. Ang Sampler ay nagbibigay-daan sa iyong mahusay na tukuyin ang mga vectorized na workload sa pamamagitan ng paggamit ng isang istraktura ng data na kilala bilang Primitive Unified Bloc (PUB). Ginagamit ang mga ito bilang mga input sa pamamaraan ng run() para sa Sampler primitive, na nagpapatakbo ng tinukoy na workload bilang isang trabaho. Pagkatapos, pagkatapos makumpleto ang trabaho, ang mga resulta ay ibinabalik sa isang format na nakasalalay sa parehong mga PUB na ginamit at ang mga runtime na opsyon na tinukoy mula sa primitive.
Mga Input
Ang bawat PUB ay nasa format na:
(<isang circuit>, <isa o higit pang opsyonal na halaga ng parameter>, <opsyonal na shots>),
Maaaring magkaroon ng maraming item na parameter values, at ang bawat item ay maaaring maging array o isang parameter, depende sa napiling circuit. Bukod dito, ang input ay dapat maglaman ng mga sukat.
Para sa Sampler primitive, ang isang PUB ay maaaring maglaman ng hanggang tatlong halaga:
- Isang
QuantumCircuitna maaaring maglaman ng isa o higit pang mga bagay naParameterTandaan: Ang mga circuit na ito ay dapat ding magsama ng mga tagubilin sa pagsukat para sa bawat isa sa mga qubit na ise-sample. - Isang koleksyon ng mga halaga ng parameter upang i-bind ang circuit laban sa (kinakailangan lamang kung anumang mga bagay na
Parameteray ginagamit na dapat ma-bind sa runtime) - (Opsyonal) isang bilang ng mga shot upang sukatin ang circuit gamit
Ipinapakita ng sumusunod na code ang isang halimbawa ng mga vectorized na input sa Sampler primitive at isinasagawa ang mga ito sa isang IBM® backend bilang isang RuntimeJobV2 na bagay.
# 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()
Mga Output
Pagkatapos maipadala ang isa o higit pang PUB sa isang QPU para sa pagpapatakbo at matagumpay na makumpleto ang isang trabaho, ang data ay ibinabalik bilang isang bagay na container na PrimitiveResult na na-access sa pamamagitan ng pagtawag sa pamamaraan ng RuntimeJobV2.result(). Ang PrimitiveResult ay naglalaman ng isang iterable na listahan ng mga bagay na SamplerPubResult na naglalaman ng mga resulta ng pagpapatakbo para sa bawat PUB. Ang mga data na ito ay mga sample ng output ng circuit.
Ang bawat elemento ng listahang ito ay tumutugma sa isang PUB na isinumite sa pamamaraan ng run() ng primitive (halimbawa, ang isang trabahong isinumite na may 20 na PUB ay magbabalik ng isang bagay na PrimitiveResult na naglalaman ng isang listahan ng 20 bagay na SamplerPubResult, isa na tumutugma sa bawat PUB).
Ang bawat bagay na SamplerPubResult ay may parehong atributo na data at metadata.
- Ang atributo na
dataay isang customized naDataBinna naglalaman ng aktwal na mga halaga ng sukat, mga karaniwang paglihis, at iba pa. Ang mga data bin ay mga bagay na katulad ng dict na naglalaman ng isangBitArraybawatClassicalRegistersa circuit. - Ang klase ng
BitArrayay isang container para sa mga ordenadong data ng shot. Ini-istore nito ang mga na-sample na bitstring bilang mga byte sa loob ng isang dalawang-dimensional na array. Ang pinakakaliwang axis ng array na ito ay tumatakbo sa mga ordenadong shot, habang ang pinakakanang axis ay tumatakbo sa mga byte. - Ang atributo ng
metadataay naglalaman ng impormasyon tungkol sa mga opsyon ng runtime na ginamit (ipinaliwanag sa ibang pagkakataon sa seksyon ng Metadata ng Resulta ng pahinang ito).
Ang sumusunod ay isang visual na balangkas ng istraktura ng data ng 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
├── ...
├── ...
└── ...
Sa madaling salita, ang isang trabaho ay nagbabalik ng isang bagay na PrimitiveResult at naglalaman ng isang listahan ng isa o higit pang mga bagay na SamplerPubResult. Ang mga bagay na SamplerPubResult na ito ay nag-iimbak ng data ng sukat para sa bawat PUB na isinumite sa trabaho.
Bilang unang halimbawa, tingnan natin ang sumusunod na sampung-qubit na 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:
[[ 0 0]
[ 3 255]
[ 0 0]
...
[ 3 255]
[ 2 255]
[ 3 255]]
Minsan ay maginhawa ang pag-convert mula sa format ng bytes sa BitArray patungo sa mga bitstring. Ang pamamaraan ng get_count ay nagbabalik ng isang dictionary na nag-map ng mga bitstring sa bilang ng beses na nangyari ang mga ito.
# 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}
Kapag ang isang circuit ay naglalaman ng higit sa isang classical register, ang mga resulta ay nakaimbak sa iba't ibang mga bagay na BitArray. Ang sumusunod na halimbawa ay nagbabago ng nakaraang snippet sa pamamagitan ng paghahati ng classical register sa dalawang natatanging register:
# 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>)
Gamitin ang mga bagay na BitArray para sa mahusay na post-processing
Dahil ang mga array ay karaniwang nag-aalok ng mas magandang performance kumpara sa mga dictionary, ipinapayo na magsagawa ng anumang post-processing nang direkta sa mga bagay na BitArray kaysa sa mga dictionary ng mga count. Ang klase ng BitArray ay nag-aalok ng hanay ng mga pamamaraan upang magsagawa ng ilang karaniwang mga operasyon sa post-processing:
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 ng Resulta
Bilang karagdagan sa mga resulta ng pagpapatakbo, parehong ang mga bagay na PrimitiveResult at SamplerPubResult ay naglalaman ng atributo ng metadata tungkol sa trabahong isinumite. Ang metadata na naglalaman ng impormasyon para sa lahat ng mga isinumiteng PUB (tulad ng iba't ibang opsyon sa runtime na available) ay matatagpuan sa PrimitiveResult.metadata, habang ang metadata na tukoy sa bawat PUB ay matatagpuan sa SamplerPubResult.metadata.
Ang metadata ng resulta ng Sampler ay kinabibilangan din ng impormasyon sa timing ng pagpapatakbo na tinatawag na execution span.
Sa field ng metadata, ang mga implementasyon ng primitive ay maaaring magbalik ng anumang impormasyon tungkol sa pagpapatakbo na may kaugnayan sa kanila, at walang mga pares ng key-value na ginagarantiyahan ng base primitive. Kaya, ang ibinalik na metadata ay maaaring mag-iba sa iba't ibang implementasyon ng primitive.
# 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' : {},
Tingnan ang mga execution span
Ang mga resulta ng mga trabaho ng SamplerV2 na naisakatuparan sa Qiskit Runtime ay naglalaman ng impormasyon sa timing ng pagpapatakbo sa kanilang metadata.
Ang impormasyong ito sa timing ay maaaring gamitin upang maglagay ng mga hangganan ng timestamp na itaas at ibaba sa kung kailan partikular na mga shot ay naisakatuparan sa QPU.
Ang mga shot ay ini-grupo sa mga bagay na ExecutionSpan, na ang bawat isa ay nagpapahiwatig ng oras ng pagsisimula, oras ng paghinto, at isang detalye ng kung aling mga shot ang nakolekta sa span.
Ang isang execution span ay nagtatukoy ng kung aling data ang naisakatuparan sa panahon ng window nito sa pamamagitan ng pagbibigay ng isang pamamaraan na ExecutionSpan.mask. Ang pamamaraang ito, na binibigyan ng anumang index na Primitive Unified Block (PUB), ay nagbabalik ng isang boolean mask na True para sa lahat ng mga shot na naisakatuparan sa panahon ng window nito. Ang mga PUB ay ini-index ayon sa pagkakasunud-sunod kung saan sila ibinigay sa tawag ng Sampler run. Kung, halimbawa, ang isang PUB ay may hugis na (2, 3) at pinatakbo nang may apat na shot, kung gayon ang hugis ng mask ay (2, 3, 4). Tingnan ang pahina ng API ng execution_span para sa buong detalye.
Upang makita ang impormasyon ng execution span, suriin ang metadata ng resulta na ibinalik ng SamplerV2, na darating sa anyo ng isang bagay na ExecutionSpans. Ang bagay na ito ay isang container na katulad ng listahan na naglalaman ng mga halimbawa ng mga subclass ng ExecutionSpan, tulad ng SliceSpan.
Halimbawa:
# 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)
Ang mga execution span ay maaaring i-filter upang magsama ng impormasyong nauugnay sa mga tiyak na PUB, na pinili ayon sa kanilang mga index:
# 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>)])
Tingnan ang pandaigdigang impormasyon tungkol sa koleksyon ng mga execution span:
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
I-extract at suriin ang isang partikular na span:
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
Posible na ang mga window ng oras na tinukoy ng mga natatanging execution span ay mag-overlap. Hindi ito dahil sabay-sabay na gumaganap ang isang QPU ng maraming pagpapatakbo, ngunit sa halip ay isang artifact ng ilang classical na pagpoproseso na maaaring mangyari nang sabay-sabay sa quantum execution. Ang garantiya na ginagawa ay na ang mga na-reference na data ay talagang nangyari sa iniulat na execution span, ngunit hindi kinakailangan na ang mga limitasyon ng window ng oras ay kasing-tight na posible.