Lumaktaw sa pangunahing nilalaman

Lumipat mula sa Sampler patungo sa Executor

Inilalarawan ng gabay na ito kung paano ilipat ang mga quantum sampling workload mula sa IBM Quantum® Sampler primitive patungo sa Executor primitive.

Paglabas ng Beta

Ang Executor primitive ay bahagi ng directed execution model. Ang lahat ng bahagi sa directed execution model ay kasalukuyang nasa beta at maaaring hindi matatag. Inaanyayahan kang subukan ang mga ito at magbigay ng feedback sa pamamagitan ng pagbukas ng isyu sa Samplomatic o qiskit-ibm-runtime GitHub repositories.

Dapat ka bang lumipat?

Hindi lahat ay dapat lumipat mula sa Sampler patungo sa Executor. Maraming pagkakaiba sa pagitan ng mga primitive, ngunit ang sumusunod na gabay ay makakatulong sa iyo na magpasya kung lilipat o hindi:

Lumipat sa Executor kung ikaw ay isang quantum information scientist na nagpapatakbo ng mga utility-scale na eksperimento at nangangailangan ng detalyado at maaaring ulitin na kontrol sa mga teknik tulad ng Pauli twirling, noise-model learning at injection, at mga pagbabago sa basis — o nangangailangan ng isa sa mga karagdagang kakayahang inaalok ng Executor.

Ipagpatuloy ang paggamit ng Sampler kung gusto mo ng simple, high-level na interface at gusto mong pangasiwaan ng primitive ang error suppression at mitigation para sa iyo.

Mga limitasyon at caveat

Dahil ang Executor at ang directed execution model ay nasa beta, tandaan ang mga sumusunod bago ka magpasyang lumipat:

  • Wala pang suporta sa simulator: Hindi tulad ng Sampler, na may AerSampler implementation sa qiskit-aer para sa lokal na simulation, kasalukuyang walang simulator backend para sa Executor. Inaasahang darating na malapit ang suporta sa simulator. Samantala, maaari mo pa ring suriin at sampol ang template circuit nang lokal upang i-validate ang iyong workflow bago ito isumite sa hardware.

  • Sinasaklaw lamang ng gabay na ito ang Sampler, hindi ang Estimator. Ang paglipat mula sa Estimator patungo sa Executor ay mas kumplikado kaysa sa paglipat mula sa Sampler dahil ang Estimator ay kumukuwenta ng mga expectation value sa halip na magbalik ng raw na mga sample. Ang pag-reproduce ng ugali ng Estimator gamit ang Executor ay nangangailangan ng karagdagang post-processing. Ang mga utility function na tutulong sa paglipat mula sa Estimator patungo sa Executor ay nasa proseso pa ng pagbuo, kaya sadyang inilalarawan lamang ng gabay na ito ang Sampler workflow.

Mga pangunahing pagkakaiba sa pagitan ng Executor at Sampler

Parehong sinasampol ng Sampler at Executor ang mga output register ng mga quantum circuit, ngunit itinatarget nila ang iba't ibang user:

  • Ang Sampler ay isang high-level abstraction. Mayroon itong sumusunod na mga katangian:

    • Mayroon itong built-in na error suppression (dynamical decoupling at twirling).

    • Gumagawa ito ng mga implicit na desisyon para sa iyo.

    • Dinisenyo ito upang makapag-focus ang mga algorithm developer sa innovation sa halip na sa data conversion.

  • Ang Executor ay bahagi ng directed execution model. Naiiba ito sa Sampler sa maraming paraan at mayroon ng sumusunod na mga katangian:

    • Wala itong built-in na error suppression o mitigation. Sa halip, kinukuha mo ang iyong design intent sa client side (sa pamamagitan ng paggamit ng circuit annotation at isang samplex), at ang mamahaling pagbuo ng mga variant ng circuit ay inililipat sa server side.

    • Wala itong ginagawang implicit na desisyon. Sinusunod nito nang eksakto ang iyong mga direktiba, na nagbibigay ng ganap na kontrol at transparency.

    • Ang Executor at Samplomatic ay magkasamang naglalantad ng mga karagdagang kakayahan na hindi inaalok ng Sampler, kabilang (ngunit hindi limitado sa) ang mga sumusunod:

      • Higit pang mga twirling group: Hinahayaan ka ng Samplomatic na piliin kung aling twirling group ang ilalapat bawat box, sa halip na limitado sa iisang estratehiyang inilalapat ng Sampler para sa iyo. Sinusuportahan din nito ang mga twirling group maliban sa Pauli, tulad ng "local_c1" twirling group.
      • Kerneled at classified na mga pagsukat nang magkasama: Ang pagtakda ng QuantumProgram.meas_level = "both" (idinagdag sa qiskit-ibm-runtime v0.48.0) ay humihiling na parehong classified at kerneled na mga pagsukat ang naroroon sa mga resulta, sa halip na pumili ng iisang uri ng pagsukat bawat job.
      • Twirling para sa mga circuit na may fractional gate: Maaaring maglapat ang Executor ng twirling sa mga circuit na naglalaman ng fractional gate.
      • Detalyado at composable na error mitigation: Halimbawa, ang pagpili kung aling circuit layer ang i-mitigate at pagsasaayos ng mga noise rate na ini-inject sa circuit.
      Mga Tala
      • Inaasahang ilalabas muna sa Executor ang mga bagong kakayahan sa hinaharap at maaaring hindi ma-port sa Sampler. Kung umaasa ka sa access sa pinakabagong mga feature, ang Executor ang mas future-proof na pagpipilian.
      • Ang base Qiskit package ay wala pang ibinibigay na base class para sa Executor primitive (mayroon ito para saSamplerV2).

Pagmamapa ng mga konsepto

Ipinapakita ng sumusunod na talahanayan kung paano nagmamapa ang mga konsepto ng Sampler sa Executor.

KonseptoSamplerExecutor
Importfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
InputListahan ng PUBs (mga tuple)Isang QuantumProgram ng mga QuantumProgramItem na object
Circuit at mga parameter(circuit, params, shots) tupleprogram.append_circuit_item(circuit, circuit_arguments=...)
TwirlingTwirlingOptionsExplicit sa pamamagitan ng mga annotated box at isang samplex (append_samplex_item)
Run callsampler.run([pub, ...])executor.run(program)
Uri ng resultaPrimitiveResult ng SamplerPubResultQuantumProgramResult (iterable)
I-access ang dataresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Pamahalaan ang noiseBuilt-in na mga opsyonKailangang manu-manong buuin (annotations, samplex, NoiseLearnerV3)

Pangkalahatang-ideya ng mga hakbang sa paglipat

  1. I-install ang Samplomatic.

  2. Baguhin ang mga import.

  3. Palitan ang mga PUB tuple.

  4. Baguhin kung paano ipinapahayag ang mga shot.

  5. I-update ang iba pang mga opsyon kung kinakailangan.

  6. I-update ang run command.

  7. I-update ang pag-parse ng resulta.

  8. Bawiin ang twirling.

Hakbang 1. I-install ang mga kinakailangang package

Kailangan ng Executor at ng directed execution model ang samplomatic package:

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Mga Tala sa Bersyon
  • Inirerekomenda ang qiskit-ibm-runtime v0.48.0 dahil idinadagdag nito ang opsyon na meas_level = "both" at ang twirling group na local_c1.
  • Kinakailangan ang qiskit >= 2.3.0.
  • Kinakailangan ang samplomatic >= 0.18.0.

Hakbang 2. Baguhin ang mga import

Sampler:

from qiskit_ibm_runtime import SamplerV2 as Sampler

Executor:

from qiskit_ibm_runtime import Executor, QuantumProgram

Hakbang 3. Palitan ang mga PUB tuple ng QuantumProgram

Sa halip na magpasa ng listahan ng mga tuple (PUBs), kapag gumagamit ng Executor, bumubuo ka ng QuantumProgram at nagdaragdag ng mga item dito.

Tumatanggap ang QuantumProgram ng mga item na circuit at mga item na samplex:

  • append_circuit_item: Nagdaragdag ng CircuitItem, na isang circuit at (opsyonal) ang mga parameter value nito. Isinasagawa ito nang as-is, nang walang anumang randomization.

    Gamitin ito kapag gusto mo lamang mag-sample ng circuit, katulad ng ginagawa ng Sampler sa isang PUB na walang twirling; halimbawa, kapag nagsusumite ng plain sampling job, o kapag manu-mano mo nang isinama ang anumang mga variant na gusto mo.

  • append_samplex_item: Nagdaragdag ng samplexItem, na isang template circuit kasama ang isang samplex na bumubuo ng randomized na mga parameter set sa server side.

    Gamitin ito kapag gusto mong i-randomize ang content ng circuit. Ang pangunahing kaso ay sa twirling (gate o measurement) o noise injection. Pinapalitan ng kakayahang ito ang built-in twirling ng Sampler.

Ang isang QuantumProgram ay maaaring tumanggap ng parehong uri ng item; bawat idinagdag na item ay isinasagawa bilang independent na task at gumagawa ng sarili nitong entry sa mga resulta. Sa pangkalahatan, gamitin ang append_circuit_item kapag hindi kailangang i-randomize ang iyong circuit. Kung hindi, gamitin ang append_samplex_item.

Ipinapakita ng mga susunod na seksyon ang bawat isa nang paisa-isa: mga parameterized circuit na gumagamit ng append_circuit_item, at ang paglipat ng twirling gamit ang append_samplex_item.

Sa mga sumusunod na halimbawa ng code, ang isa_circuit ay tumutukoy sa circuit na na-transpile upang sumunod sa Instruction Set Architecture (ISA) ng target backend. Ang isa_circuit na ito ay naglalaman ng dalawang parameter.

Hakbang 3a. Ilipat ang mga parameterized circuit

Sa Sampler, ang mga parameter value ay ang ikalawang elemento ng PUB tuple. Sa Executor, ipasa ang mga ito bilang circuit_arguments sa append_circuit_item.

Sampler:

params = np.random.rand(10, circuit.num_parameters) # 10 parameter sets
pubs = (isa_circuit, params)

Executor

program = QuantumProgram(shots=1024)
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, circuit.num_parameters), # 10 sets
)

# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]

Hakbang 3b. Ilipat ang built-in twirling patungo sa explicit na mga annotation

Ito ang pinakamahalagang pagbabago. Inilalapat ng Sampler ang twirling para sa iyo sa pamamagitan ng paggamit ng mga opsyon. Sa Executor, idineklara mo ang intensyong iyon nang explicit sa pamamagitan ng paggamit ng mga annotated box at isang samplex (mula sa Samplomatic).

Sampler (twirling gamit ang mga opsyon):

sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True

Executor (twirling gamit ang mga box at isang samplex):

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
enable_gates=True, # gate twirling
enable_measures=True, # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)

# 2. Build the (template circuit, samplex) pair.
# The template circuit's single-qubit gates are replaced by parameterized gates;
# the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)

# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(10, 2), # original circuit params
},
shape=(28, 10), # 28 randomizations x 10 parameter sets
)

Dahil ang template circuit at samplex ay binubuo sa client side, maaari mong suriin at i-sample ang mga ito nang lokal upang i-verify ang output bago magpadala ng anuman sa hardware.

Pag-verify: I-sample ang template circuit nang lokal

Maaari kang kumuha ng mga randomization mula sa samplex at i-bind ang mga ito sa template circuit upang kumpirmahin na gumagawa ang samplex ng mga parameter value na inaasahan mo. Ang mga parameter value na ibinabalik ng samplex.sample ay direktang compatible sa mga parameter ng template circuit.

# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())

# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
parameter_values=np.random.rand(2), # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)

# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)

Para umusad pa, maaari mong i-verify na ang bawat randomization ay logically equivalent sa orihinal na circuit sa pamamagitan ng, halimbawa, pagko-convert ng pareho sa mga Operator object at paghahambing ng kanilang unitary implementation (pagkatapos isaalang-alang ang mga outputs["measurement_flips.<register>"] na pagwawasto na bumabawi sa measurement twirling), o sa pamamagitan ng paghahambing ng mga expectation value mula sa isang lokal na pagpapatakbo ng StatevectorSampler o StatevectorEstimator. Tingnan ang Samplomatic Samplex inputs and outputs guide para sa kumpletong walkthrough.

Hakbang 4. Baguhin kung paano hinihiling ang mga shot

Ilipat ang shots mula sa PUB patungo sa QuantumProgram(shots=...). Sa Executor, ang shots ay nalalapat sa buong job. Magsumite ng maraming job kung kailangan mo ng magkakaibang bilang ng shot.

Sampler:

# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])

Executor:

# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

Hakbang 5. I-update ang mga opsyon kung kinakailangan

Mas kaunti ang mga opsyong available sa Executor kaysa sa Sampler, dahil ang mga pagpipilian sa error-mitigation ay nasa iyong mga annotation at samplex na ngayon sa halip na sa mga opsyon.

May pagkakaiba rin sa istruktura kung saan naninirahan ang mga setting.

  • Sa Sampler, lahat, kabilang ang mga pagpipiliang nakakaapekto sa post-processing ng resulta, ay naka-configure sa mga opsyon ng primitive o sa PUB.

  • Sa Executor, ang mga pagpipiliang nakakaapekto kung paano hinuhugis at pino-post-process ang mga resulta ng job ay itinatakda sa QuantumProgram, hindi sa ExecutorOptions.

Examples:

SamplerExecutor
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

Ang ExecutorOptions ay naghahawak lamang ng mas mababang-antas na execution at environment settings na hindi nagbabago sa istruktura ng ibinalik na data. May tatlong top-level na grupo ito:

Kapansin-pansin, ang mga opsyong twirling at dynamical_decoupling ay umiiral sa Sampler ngunit hindi sa Executor. Sa halip, ipinapahayag ang mga halaga ng opsyong iyon sa pamamagitan ng directed execution model.

Example:

from qiskit_ibm_runtime import Executor, ExecutorOptions

options = ExecutorOptions(
environment={"log_level": "INFO"},
execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True

executor = Executor(mode=backend, options=options)

Hakbang 6. I-update ang run command

Ang input sa isang Executor job ay ang program, imbes na mga PUB.

Sampler:

# Submit a job
sampler.run([(isa_circuit, parameter_values)])

Executor:

# Submit a job
executor.run(program)

Step 7. Baguhin kung paano mo ina-access ang mga resulta

Sa Executor, ang mga resulta ay mga NumPy arrays, hindi BitArray objects. Gamitin ang name string bilang index (result[0]["meas"]) at makakakuha ka ng np.ndarray pabalik. Hindi na kailangang tandaan ang .data.<register> attribute path.

Para mag-update mula sa Sampler patungo sa Executor, palitan ang result[i].data.<reg> (BitArray) ng result[i]["<reg>"] (np.ndarray), pagkatapos ay isulat muli ang get_counts-based post-processing bilang mga NumPy operations.

TaskSamplerExecutor
Get register dataresult[0].data.measresult[0]["meas"]
Data typeBitArraynp.ndarray
Counts dictionaryresult[0].data.meas.get_counts()Post-process the array manually
Multiple registersresult[0].data.<name> per registerresult[0]["<name>"] per register
CircuitItem array shape-(parameter_sets, shots, register_bits)
SamplexItem array shape-(randomizations, parameter_sets, shots, register_bits)
Undo measurement twirlingAutomaticresult[i]["measurement_flips.<name>"] + XOR
tala

Nag-aalok ang BitArray ng Sampler ng mga helper (get_counts, slice_bits, slice_shots, expectation_values, at post-selection masks). Nagbabalik ang Executor ng raw NumPy arrays kaya't magagawa mo ang post-processing na ito gamit ang standard na NumPy operations.

Step 8. Pangasiwaan ang mga twirled na resulta (bit-flip corrections)

Kapag nag-apply ka ng measurement twirling sa pamamagitan ng SamplexItem, ibinabalik ng Executor ang raw (twirled) na mga measurement kasama ang mga bit-flip correction na kailangan para i-undo ang twirling. Kailangan mong i-apply ang mga ito nang manu-mano; walang anumang itinutuwid nang implicit.

Kapag gumagamit ng Executor, i-undo nang explicit ang twirling gamit ang mga measurement_flips.<reg> correction at isang XOR, gaya ng ipinapakita sa sumusunod na halimbawa:

# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"] # example: (28, 10, 1024, 2)

# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"] # example: (28, 10, 1, 2)

# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1

Walang katumbas na hakbang sa Sampler dahil ito mismo ang nag-a-undo ng twirling para sa iyo.

Kumpletong halimbawa: I-migrate ang isang basic sampling job

Sampler

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()

# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()

Executor

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

# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()

# 6. Access results: a plain np.ndarray keyed by register name
# shape = (shots, register_bits)
meas = result[0]["meas"]

Mga susunod na hakbang