Lumaktaw sa pangunahing nilalaman
Ang proyektong ito ay nasa beta. Nakakita ng bug? Mag-bukas ng GitHub issue. May tanong o ideya? Magtanong sa Discussions — gusto naming marinig ang iyong feedback!
Gaano kaganda ang kalidad ng pagsasalin sa pahinang ito?

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.5.2
qiskit-ibm-runtime~=0.47.0

Ang pahinang ito ay nagbibigay ng pangkalahatang-ideya ng mga input at output ng qiskit-ibm-runtime Sampler primitive, na nagpapatakbo ng mga workload sa IBM Quantum® Compute Service. 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. Kapag natapos na ang trabaho, ibinabalik ang mga resulta sa isang format na nakadepende 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:

(<single circuit>, <one or more optional parameter value>, <optional 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 pagsukat.

Para sa Sampler primitive, ang isang PUB ay maaaring maglaman ng hanggang tatlong halaga:

  • Isang QuantumCircuit na maaaring maglaman ng isa o higit pang mga bagay na Parameter Tandaan: 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 θk\theta_k (kinakailangan lamang kung anumang mga bagay na Parameter ay ginagamit na dapat ma-bind sa runtime)
  • (Opsyonal) isang bilang ng shots na gagamitin sa pagsukat ng circuit

Ipinapakita ng sumusunod na code ang isang halimbawang set ng vectorized inputs sa Sampler primitive at pinapatakbo ang mga ito sa isang IBM® backend bilang isang solong RuntimeJobV2 object.

# 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()

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 data ay isang customized na DataBin na naglalaman ng aktwal na mga halaga ng pagsukat, mga karaniwang paglihis, at iba pa. Ang mga data bin ay mga bagay na katulad ng dict na naglalaman ng isang BitArray bawat ClassicalRegister sa circuit.
  • Ang klase ng BitArray ay 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 metadata ay 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 pagsukat 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:
[[ 3 247]
[ 0 0]
[ 3 223]
...
[ 0 255]
[ 0 0]
[ 0 0]]

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: {'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}

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:
[[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]]

Metadata ng Resulta​

Bukod sa mga resulta ng execution, ang parehong PrimitiveResult at SamplerPubResult objects ay naglalaman ng metadata attribute tungkol sa job na isinumite. Ang metadata na naglalaman ng impormasyon para sa lahat ng isinumiteng PUB (tulad ng iba't ibang runtime options na available) ay matatagpuan sa PrimitiveResult.metatada, habang ang metadata na tukoy sa bawat PUB ay matatagpuan sa SamplerPubResult.metadata.

Isinusumite ng client-side Sampler ang workload nito bilang isang Executor job. Ang metadata ng pinagbabatayang resulta ng Executor, na kinabibilangan ng impormasyon sa timing ng pagpapatakbo na tinatawag na execution spans, ay makukuha sa ilalim ng key na "executor" ng PrimitiveResult.metadata.

tala

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-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' : {},

Tingnan ang mga execution span​

Gumagamit ang client-side Sampler ng Executor para magpatakbo ng mga job, na nagsasagawa ng trabaho nito sa QPU sa mga chunk. Ang timing ng bawat chunk ay nasa result.metadata["executor"].chunk_timing, isang listahan ng mga object na ChunkSpan. Bawat ChunkSpan ay may mga timestamp na start at stop at isang listahan ng parts, kung saan ang bawat part ay nagbibigay ng index ng item (idx_item) at ang bilang ng mga configuration mula sa item na iyon (size) na tumakbo sa chunk.

Gumagawa ang Sampler ng isang Executor program item bawat PUB, kaya ang idx_item ay ang index ng PUB, ayon sa pagkakasunud-sunod ng pagbibigay ng mga PUB sa run(). Kung naka-enable ang twirling, bawat randomization ng bawat set ng halaga ng parameter ay binibilang bilang hiwalay na configuration.

Para i-visualize ang mga timing, ipasa ang listahan sa draw_chunk_timings, tulad ng ipinapakita sa sumusunod na halimbawa.

Para malaman kung ano ang ibig sabihin ng mga timestamp ng chunk, paano basahin ang plot, at kung bakit maaaring lumitaw ang mga puwang sa pagitan ng mga chunk, tingnan ang View execution spans sa gabay na Executor inputs and outputs.

# 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

Mga execution span sa legacy Sampler​

Simula sa qiskit-ibm-runtime v0.50.0, deprecated na ang legacy server-side Sampler. Hindi nagbabalik ang client-side Sampler ng mga object na ExecutionSpans; gamitin na lang ang chunk timing na inilarawan kanina. Tingnan ang Migrate to client-side primitives para sa mga tagubilin sa migration.

I-click para matuto pa.

Ang mga resulta ng mga job mula sa legacy server-side Sampler ay naglalaman ng impormasyon sa timing ng pagpapatakbo sa result.metadata["execution"]["execution_spans"], sa anyo ng isang object na ExecutionSpans. Ang object na ito ay isang list-like na container ng mga object na ExecutionSpan, na ang bawat isa ay nagpapahiwatig ng oras ng pagsisimula, oras ng paghinto, at isang detalye kung aling mga shot ang nakolekta sa span.

Tinutukoy ng isang execution span kung aling data ang naisakatuparan sa panahon ng window nito sa pamamagitan ng pagbibigay ng method na ExecutionSpan.mask. Ang method na ito, kapag binigyan ng anumang index ng PUB, ay nagbabalik ng boolean mask na True para sa lahat ng shot na naisakatuparan sa panahon ng window nito. Halimbawa, kung ang isang PUB ay may hugis na (2, 3) at pinatakbo nang may apat na shot, ang hugis ng mask ay (2, 3, 4). Tingnan ang pahina ng API ng execution_span para sa buong detalye.

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)

Para i-visualize ang mga legacy execution span, gamitin ang function na draw_execution_spans.

Posible na ang mga time window na tinukoy ng magkakaibang execution span ay mag-overlap. Hindi ito dahil sabay-sabay na gumaganap ang isang QPU ng maraming pagpapatakbo, kundi isang artifact ng ilang classical processing na maaaring mangyari nang sabay sa quantum execution. Ang garantiya ay na tiyak na nangyari ang mga na-reference na data sa iniulat na execution span, ngunit hindi kinakailangang kasing-tight hangga't maaari ang mga limitasyon ng time window.