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.
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
AerSamplerimplementation saqiskit-aerpara 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 saqiskit-ibm-runtimev0.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 sa
SamplerV2).
- 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
-
Pagmamapa ng mga konsepto
Ipinapakita ng sumusunod na talahanayan kung paano nagmamapa ang mga konsepto ng Sampler sa Executor.
| Konsepto | Sampler | Executor |
|---|---|---|
| Import | from qiskit_ibm_runtime import SamplerV2 | from qiskit_ibm_runtime import Executor |
| Input | Listahan ng PUBs (mga tuple) | Isang QuantumProgram ng mga QuantumProgramItem na object |
| Circuit at mga parameter | (circuit, params, shots) tuple | program.append_circuit_item(circuit, circuit_arguments=...) |
| Twirling | TwirlingOptions | Explicit sa pamamagitan ng mga annotated box at isang samplex (append_samplex_item) |
| Run call | sampler.run([pub, ...]) | executor.run(program) |
| Uri ng resulta | PrimitiveResult ng SamplerPubResult | QuantumProgramResult (iterable) |
| I-access ang data | result[0].data.<register> (BitArray) | result[0]["<register>"] (np.ndarray) |
| Pamahalaan ang noise | Built-in na mga opsyon | Kailangang manu-manong buuin (annotations, samplex, NoiseLearnerV3) |
Pangkalahatang-ideya ng mga hakbang sa paglipat
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]
- Inirerekomenda ang
qiskit-ibm-runtimev0.48.0 dahil idinadagdag nito ang opsyon nameas_level = "both"at ang twirling group nalocal_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 ngCircuitItem, 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 ngsamplexItem, 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 saExecutorOptions.
Examples:
| Sampler | Executor |
|---|---|
shots | QuantumProgram(shots=...) |
meas_type | QuantumProgram(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:
-
environment(EnvironmentOptions) -
execution(ExecutionOptions): Naglalaman ng mas kaunting opsyon kaysa sa Sampler. Halimbawa, walangmeas_typena opsyon ang Executor.
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.
| Task | Sampler | Executor |
|---|---|---|
| Get register data | result[0].data.meas | result[0]["meas"] |
| Data type | BitArray | np.ndarray |
| Counts dictionary | result[0].data.meas.get_counts() | Post-process the array manually |
| Multiple registers | result[0].data.<name> per register | result[0]["<name>"] per register |
| CircuitItem array shape | - | (parameter_sets, shots, register_bits) |
| SamplexItem array shape | - | (randomizations, parameter_sets, shots, register_bits) |
| Undo measurement twirling | Automatic | result[i]["measurement_flips.<name>"] + XOR |
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"]