Mga input at output ng Executor
Mga bersyon ng package
Ang code sa pahinang ito ay binuo gamit ang mga sumusunod na kinakailangan. Inirerekumenda naming gamitin ang mga bersyong ito o mas bago.
qiskit[all]~=2.6.1
qiskit-ibm-runtime~=0.50.0
samplomatic~=0.21.0
Ang Executor primitive ay bahagi ng directed execution model, na nagbibigay ng mas maraming kakayahang umangkop kapag nag-cu-customize ng isang workflow ng error mitigation.
Ang mga input at output ng Executor primitive ay napaka-iba mula sa mga Sampler at Estimator primitive. Halimbawa, sa halip na tumanggap ng listahan ng mga PUB bilang input, ang Executor ay tumatanggap ng isang QuantumProgram, na naglalaman ng listahan ng mga object na QuantumProgramItem. Ang mga container class na ito ay nagbibigay sa iyo ng mas maraming kakayahang umangkop kaysa sa isang PUB, na isang simpleng tuple data structure.
Ang output ng Executor ay isang QuantumProgramResult, na isang iterable at naglalaman ng isang elemento para sa bawat input na QuantumProgramItem.
Mga input: Mga quantum program
Tulad ng nabanggit kanina, ang input sa isang Executor primitive ay isang QuantumProgram, na isang iterable ng mga object na QuantumProgramItem. Ang mga object na ito ay maaaring may dalawang uri:
CircuitItem, na karaniwang nag-iimbak ng isang circuit at ng mga halaga ng parameter nito (kung mayroon).SamplexItem, na karaniwang nag-iimbak ng mga sumusunod:- Isang template circuit
- Isang samplex object, na ginagamit para makabuo ng mga randomized na set ng parameter sa runtime (halimbawa para magsagawa ng twirling o mag-inject ng noise)
- Mga argumento para sa samplex, na maaaring magsama ng mga halaga ng parameter para sa orihinal na circuit
Ang bawat isa sa mga item na ito ay kumakatawan sa ibang gawain para isagawa ng Executor.
Bago ka magsimula
Ang ilang halimbawa ng code sa pahinang ito ay gumagamit ng samplex, na bahagi ng Samplomatic package. Samakatuwid, bago patakbuhin ang mga code block na iyon, kailangan mong i-install ang Samplomatic, tulad ng ipinapakita sa sumusunod na code block. Para sa karagdagang impormasyon, tingnan ang dokumentasyon ng Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]
Halimbawa: Lumikha ng isang QuantumProgram na may dalawang magkaibang gawain
Una, i-initialize ang iyong quantum program, pagkatapos ay i-append ang mga program item sa pamamagitan ng paggamit ng alinman sa append_circuit_item o append_samplex_item (kung may samplex), tulad ng ipinapakita sa mga sumusunod na halimbawa.
Ang sumusunod na cell ay nag-i-initialize ng isang QuantumProgram at nagtatakda na dapat itong magpatakbo ng 1024 shot para sa bawat configuration ng bawat item sa program.
Hindi tulad ng Sampler, ang isang QuantumProgram ay tumatanggap lamang ng isang halaga ng shot. Kung gusto mo ng ibang halaga ng shot, kailangan mo ng hiwalay na QuantumProgram, na magiging isang hiwalay na job.
# 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", "samplomatic": "samplomatic"}
_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.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit_ibm_runtime import Executor, QiskitRuntimeService
from qiskit.circuit import Parameter, QuantumCircuit
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Initialize an empty program
program = QuantumProgram(shots=1024)
# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
# `measure_all` adds a 3-bit classical register named "meas"
circuit.measure_all()
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Generate a preset pass manager
# This will be used to convert the abstract circuit to an
# equivalent Instruction Set Architecture (ISA) circuit.
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Transpile the circuit
isa_circuit = preset_pass_manager.run(circuit)
Mag-append ng CircuitItem
Susunod, i-append ang target na circuit, na na-transpile ayon sa instruction set architecture (ISA) ng backend, sa QuantumProgram. Dahil ang circuit na ito ay may dalawang parameter, kailangan din nating magbigay ng mga halaga ng parameter (10 set sa halimbawang ito). Ang pagpapatakbo ng CircuitItem na ito ang unang gawain na isasagawa ng program.
# Append the transpiled circuit and an array
# containing 10 sets of parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(
10, 2
), # 10 sets of parameter values and 2 parameters
)
Mag-append ng SamplexItem
Ang mga circuit item ay isinasagawa nang walang anumang uri ng randomization. Sa kabaligtaran, ang mga samplex item ay nagpapahintulot sa iyo na tukuyin kung paano gawing random ang kanilang nilalaman. Ang susunod na cell ay gumagamit ng function na generate_boxing_pass_manager() para grupuhin ang mga gate at measurement ng circuit sa mga box at magdagdag ng twirling annotation sa bawat box. Pagkatapos ay bumubuo ito ng pares ng template circuit at samplex gamit ang function na build().
Ang pagpapatakbo ng SamplexItem na ito ang pangalawang gawain na isasagawa ng program.
Tingnan ang dokumentasyon ng Samplomatic API para sa buong detalye tungkol sa samplex at ang mga argumento nito. Tingnan ang Gabay sa Transpiler ng Samplomatic para sa impormasyon tungkol sa paggamit ng function na generate_boxing_pass_manager().
# Transpile the circuit, additionally grouping gates and measurements into annotated boxes
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Use the boxing pass manager to group gates
# and measurements into boxes and add
# a`Twirl` annotation.
preset_pass_manager.post_scheduling = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = preset_pass_manager.run(circuit)
# Build the template circuit and the samplex. The template circuit has parametric gates
# without fixed values and the samplex randomly generates the parameter
# values on the server side at runtime to perform twirling.
template_circuit, samplex = build(boxed_circuit)
# Determine what arguments are required by the samplex.
# Input the arguments in samplex_arguments.
print(samplex.inputs())
TensorInterface(<
- 'parameter_values' <float64[2]>: Input parameter values to use during sampling.
>)
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
# the arguments required by the samplex.sample method
"parameter_values": np.random.rand(10, 2),
},
shape=(28, 10), # 28 randomizations and 10 sets of parameter values
)
# Initialize an Executor with the default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()
Mga output
Ang output ng Executor ay isang QuantumProgramResult, na isang iterable. Naglalaman ito ng isang entry bawat input na QuantumProgramItem sa parehong pagkakasunod ng mga input item. Ang bawat isa sa mga output item na ito ay isang dictionary kung saan ang mga key ay mga string na tumutugma sa mga pangalan ng classical register sa mga input circuit (bukod sa iba pa), kaya hindi mo na kailangang kabisahin ang mga pangalan na ito tulad ng ginagawa mo sa output ng Sampler. Ang mga halaga ng dictionary ay may uri na np.ndarray.
Ang resulta para sa nakaraang halimbawa ay naglalaman ng mga item na ito:
Resulta ng CircuitItem
Naglalaman ang unang item ng mga resulta ng pagpapatakbo ng unang gawain (isang CircuitItem) sa program. Naglalaman ito ng iisang key, ang meas, na siyang pangalan ng classical register sa input circuit. Ang value ng key na ito ay nagma-map sa isang np.ndarray na may shape na (parameter sets, shots, register bits), na (10, 1024, 3) para sa halimbawa sa itaas.
Ang sumusunod na code ay naglalarawan kung paano ma-access ang impormasyong ito:
# Access the results of the classical register of task #0, a CircuitItem
result_0 = result[0]["meas"]
print(f"Result shape: {result_0.shape}")
Result shape: (10, 1024, 3)
Ang pinakakanang axis ng result_0 ay tumatakbo sa mga classical bit sa little-endian na pagkakasunod-sunod. Kaya, hawak ng index 0 ang kinalabasan ng classical bit 0, ang least-significant bit.
Resulta ng SamplexItem
Naglalaman ang ikalawang item ng mga resulta ng pagpapatakbo ng ikalawang gawain (isang SamplexItem) sa program. Naglalaman ang item na ito ng maraming key. Ang meas key, na siyang pangalan ng classical register ng input circuit, ay nagma-map sa array ng resulta ng register na iyon. Ang array na ito ay may shape na (randomizations, parameter sets, shots, classical bits), o (28, 10, 1024, 3) sa halimbawang ito.
Bukod dito, naglalaman ang output ng key na measurement_flips.meas, na siyang bit-flip correction para i-undo ang measurement twirling para sa meas register. Ang shape ng output na ito ay magiging (28, 10, 1, 3) para sa ating halimbawa dahil isang shot lamang ang kailangan para maisagawa ang bit-flip.
Tandaan na ang pinakakanang axis ng result_1 ay tumatakbo sa mga classical bit sa little-endian na pagkakasunod-sunod.
# Access the results of the classical register of task #1
result_1 = result[1]["meas"]
print(f"Result shape: {result_1.shape}")
# Access the bit-flip corrections
flips_1 = result[1]["measurement_flips.meas"]
print(f"Bit-flip corrections shape: {flips_1.shape}")
# Undo the bit flips via classical XOR
unflipped_result_1 = result_1 ^ flips_1
Result shape: (28, 10, 1024, 3)
Bit-flip corrections shape: (28, 10, 1, 3)
Tingnan ang execution spans
Ang isang Executor.run() call ay nagsusumite ng isang job. Sa server side, gayunman, hindi kinakailangang patakbuhin ng service ang bawat configuration ng quantum program nang sabay-sabay. Sa halip, pinagpapangkat nito ang mga bound circuit ng mga program item sa mga chunk, at nagpapatakbo ng isang chunk sa bawat pagkakataon sa QPU. Hindi mo kailangang hatiin ang iyong program o magsumite ng mga karagdagang job; awtomatikong nangyayari ang chunking sa loob ng isang job. Ang attribute na metadata ng result ay naglalaman ng impormasyon sa timing tungkol sa mga chunk na ito sa result.metadata.chunk_timing. Ang mga timestamp na ito ay ang aktwal na oras ng pagsisimula at pagtatapos ng bawat chunk sa QPU. Magagamit mo ang impormasyong ito para makita kung kailan nakolekta ang partikular na data sa QPU, para maunawaan kung paano ipinamahagi ng Executor ang gawain, o para i-diagnose ang mga isyu sa performance.
Ang chunk_timing ay isang listahan na naglalaman ng isang ChunkSpan bawat naisagawang chunk. Ang bawat ChunkSpan ay may mga sumusunod na attribute:
-
startatstop: Ang mga UTC timestamp kung kailan nagsimula at huminto ang pagpapatakbo ng chunk sa QPU. -
parts: Isang listahan ng mga object naChunkPartna naglalarawan kung aling mga program item ang nag-ambag sa chunk. Ang bawatChunkPartay naglalaman ng sumusunod:-
idx_item: Ang index ng program item, ayon sa pagkakasunod-sunod ng pagdaragdag ng mga item saQuantumProgram. -
size: Ang bilang ng mga configuration (mga elemento ng shape ng item) mula sa item na iyon na naisagawa sa chunk na ito. Halimbawa, angSamplexItemsa naunang halimbawa ay may shape na(28, 10), kaya may 280 configuration ito sa kabuuan.
-
Piniprint ng sumusunod na code ang tagal ng bawat chunk at ang laman ng bawat chunk para sa job na isinumite kanina:
for idx, span in enumerate(result.metadata.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" Item {part.idx_item}: {part.size} configurations")
Chunk 0: 2026-11-06 01:47:46.009718+00:00 to 2026-11-06 01:49:05.577720+00:00 (79.568 s)
Item 0: 10 configurations
Item 1: 280 configurations
Binabalot ng property na QuantumProgramResult.timing ang parehong listahan sa isang object na ChunkTiming, na nag-uulat din ng pandaigdigang impormasyon tungkol sa koleksyon ng mga chunk:
timing = result.timing
print("Number of chunks:", len(timing))
print(" Start of the first chunk:", timing.start)
print(" End of the last chunk:", timing.stop)
print(" Total duration (s):", timing.duration)
Number of chunks: 1
Start of the first chunk: 2026-11-06 01:47:46.009718+00:00
End of the last chunk: 2026-11-06 01:49:05.577720+00:00
Total duration (s): 79.568002
I-visualize ang execution spans
Gamitin ang function na draw_chunk_timings para i-visualize ang mga chunk timing sa isang interactive na bar plot. Ang x-axis ay oras, at ang bawat chunk ay iginuguhit bilang pahalang na segment na sumasaklaw sa oras ng pagsisimula at pagtatapos nito. Ipinapakita ng y-axis ang pinagsama-samang bilang ng mga nakumpletong configuration (o ang porsyento ng kabuuang workload, kung itatakda mo ang normalize_y=True). I-hover ang isang segment para makita ang mga timestamp, tagal, at mga part ng chunk na iyon.
Nagbabalik ang function ng isang plotly figure, na maaari mong ipakita nang direkta, i-save sa file, o pareho. Tumatanggap ito ng alinman sa listahang chunk_timing o ng object na ChunkTiming. Puwede kang magpasa ng ilan sa mga ito, halimbawa mula sa iba't ibang job, para iguhit ang mga ito sa iisang plot. Itakda ang common_start=True para i-align ang mga ito sa iisang start time para sa paghahambing.
Kailangan ng draw_chunk_timings ang plotly. Puwede mo itong i-install sa pamamagitan ng pagpapatakbo ng pip install plotly.
Maaaring magpakita ang visualization ng mga puwang sa pagitan ng pagpapatakbo ng mga chunk. Ang lahat ng job na isinumite sa totoong QPU ay dumadaan sa ilang classical preprocessing, tulad ng pagsasalin sa mga pulse na maipapadala sa hardware. Dahil maaaring gawin nang parallel ang preprocessing na ito, sabay-sabay na dine-dequeue at sinisimulan ng service ang maraming job. Gayunman, isang yunit lang ng gawain ang kayang iproseso ng QPU sa bawat pagkakataon. Kaya, kung gumagawa ang isang job ng maraming chunk, maaaring maghalo-halo ang mga chunk nito sa mga job ng ibang user, at maaaring kailangang maghintay ng bawat chunk para sa turn nito sa QPU. Ito ang dahilan kung bakit maaaring lumitaw ang mga puwang sa pagitan ng mga chunk.
from qiskit_ibm_runtime.visualization import draw_chunk_timings
fig = draw_chunk_timings(result.timing, names="Executor job")
# Alternatively, call the `draw()` method of the ChunkTiming object
# fig = result.timing.draw()
# Uncomment the following line to display the figure
# fig.show(renderer="notebook")
# Save to a file
# fig.write_html("chunk_timings.html")
Mga susunod na hakbang
- I-explore ang mga halimbawa na gumagamit ng Executor.
- Matuto tungkol sa directed execution model.
- Unawain ang Executor broadcasting.