ย้ายจาก Sampler ไปยัง Executor
คู่มือนี้อธิบายวิธีย้าย workload การสุ่มตัวอย่างเชิงควอนตัมจาก IBM Quantum® Sampler primitive ไปยัง Executor primitive
Executor primitive เป็นส่วนหนึ่งของ directed execution model คอมโพเนนต์ทั้งหมดใน directed execution model อยู่ในสถานะ beta ในขณะนี้และอาจไม่เสถียร เราขอเชิญคุณทดสอบและให้ข้อเสนอแนะโดยการเปิด issue ใน repository Samplomatic หรือ qiskit-ibm-runtime บน GitHub
คุณควรย้ายหรือไม่?
ไม่ใช่ทุกคนที่ควรย้ายจาก Sampler ไปยัง Executor มีความแตกต่างมากมายระหว่าง primitive ทั้งสอง แต่คำแนะนำต่อไปนี้จะช่วยให้คุณตัดสินใจได้ว่าควรย้ายหรือไม่:
ย้ายไปยัง Executor หากคุณเป็นนักวิทยาศาสตร์ด้านสารสนเทศควอนตัม (quantum information scientist) ที่รัน การทดลองระดับ utility-scale และต้องการการควบคุมที่ละเอียดและทำซ้ำได้เกี่ยวกับ เทคนิคต่าง ๆ เช่น Pauli twirling การเรียนรู้และการฉีด noise-model และการเปลี่ยน basis — หรือผู้ที่ต้องการความสามารถเพิ่มเติมที่ Executor มีให้
ใช้ Sampler ต่อไป หากคุณต้องการอินเทอร์เฟซระดับสูงที่เรียบง่าย และต้องการให้ primitive จัดการการปราบปราม (error suppression) และการบรรเทา (mitigation) ข้อผิดพลาดให้คุณ
ข้อจำกัดและข้อควรระวัง
เนื่องจาก Executor และ directed execution model อยู่ในสถานะ beta โปรดสังเกตสิ่งต่อไปนี้ก่อนที่คุณจะตัดสินใจย้าย:
-
ยังไม่รองรับตัวจำลอง: ต่างจาก Sampler ซึ่งมี implementation
AerSamplerในqiskit-aerสำหรับการจำลองในเครื่อง ในขณะนี้ยังไม่มี backend สำหรับตัวจำลองสำหรับ Executor คาดว่าการรองรับตัวจำลอง จะมาถึงเร็ว ๆ นี้ ในระหว่างนี้ คุณยังคงสามารถตรวจสอบและสุ่มตัวอย่าง template circuit ในเครื่อง เพื่อ ตรวจสอบความถูกต้องของ workflow ของคุณก่อนส่งไปยังฮาร์ดแวร์ -
คู่มือนี้ครอบคลุมเฉพาะ Sampler ไม่ใช่ Estimator การย้ายจาก Estimator ไปยัง Executor ซับซ้อนกว่าการย้ายจาก Sampler มาก เนื่องจาก Estimator คำนวณค่าคาดหวัง (expectation values) แทนที่จะคืนตัวอย่างดิบ การจำลองพฤติกรรมของ Estimator ด้วย Executor ต้องมีการประมวลผลภายหลังเพิ่มเติม ฟังก์ชันยูทิลิตี้ที่จะช่วยย้ายจาก Estimator ไปยัง Executor ยังอยู่ระหว่างการพัฒนา ดังนั้นคู่มือนี้ จึงอธิบายเฉพาะ workflow ของ Sampler โดยเจตนา
ความแตกต่างหลักระหว่าง Executor และ Sampler
ทั้ง Sampler และ Executor สุ่มตัวอย่าง output registers ของ quantum circuit แต่ทั้งสอง เจาะจงกลุ่มผู้ใช้ที่ต่างกัน:
-
Sampler เป็นสิ่งที่เป็นนามธรรมระดับสูง มีลักษณะดังนี้:
-
มีการระงับข้อผิดพลาดในตัว (dynamical decoupling และ twirling)
-
ตัดสินใจโดยนัยให้คุณ
-
ออกแบบมาเพื่อให้นักพัฒนาอัลกอริทึมสามารถมุ่งเน้นไปที่นวัตกรรมแทนที่จะเป็นการแปลง ข้อมูล
-
-
Executor เป็นส่วนหนึ่งของ directed execution model มันแตกต่างจาก Sampler ในหลาย ๆ ด้านและมีลักษณะดังนี้:
-
ไม่มีการระงับหรือบรรเทาข้อผิดพลาดในตัว แต่คุณจะระบุความตั้งใจในการออกแบบที่ฝั่ง client แทน (โดยใช้ circuit annotations และ samplex) และการสร้าง circuit variant ที่มีค่าใช้จ่ายสูงจะถูกย้ายไปทำที่ฝั่งเซิร์ฟเวอร์แทน
-
ไม่มีการตัดสินใจโดยนัย มันทำตามคำสั่งของคุณอย่างเคร่งครัด ให้การควบคุมและความโปร่งใสอย่างเต็มที่
-
Executor และ Samplomatic ร่วมกันเปิดเผยความสามารถเพิ่มเติมที่ Sampler ไม่มี ซึ่งรวมถึง (แต่ไม่จำกัดเพียง) สิ่งต่อไปนี้:
- กลุ่ม twirling เพิ่มเติม: Samplomatic ให้คุณเลือกกลุ่ม twirling ที่จะใช้
ต่อ box แทนที่จะถูกจำกัดให้ใช้กลยุทธ์เดียวที่ Sampler ใช้ให้คุณ นอกจากนี้ยังรองรับกลุ่ม twirling อื่น ๆ นอกเหนือจาก Pauli เช่นกลุ่ม twirling
"local_c1" - การวัดแบบ kerneled และ classified พร้อมกัน: การตั้งค่า
QuantumProgram.meas_level = "both"(เพิ่มในqiskit-ibm-runtimev0.48.0) จะร้องขอ ให้มีทั้งการวัดแบบ classified และ kerneled อยู่ในผลลัพธ์ แทนที่ จะเลือกประเภทการวัดเดียวต่อ job - Twirling สำหรับ circuit ที่มี fractional gate: Executor สามารถใช้ twirling กับ circuit ที่มี fractional gate ได้
- การบรรเทาข้อผิดพลาดแบบละเอียดและประกอบได้: ตัวอย่างเช่น เลือกว่า circuit layer ใดที่จะบรรเทา และปรับ noise rate ที่ฉีดเข้าไปใน circuit
หมายเหตุ- คาดว่าความสามารถใหม่ในอนาคตจะถูกปล่อยให้กับ Executor ก่อน และอาจไม่ถูกนำไปใส่ใน Sampler หากคุณพึ่งพาการเข้าถึง ฟีเจอร์ล่าสุด Executor คือตัวเลือกที่รองรับอนาคตได้ดีกว่า
- แพ็กเกจ Qiskit พื้นฐานยังไม่มี
base class สำหรับ Executor primitive (แต่มีสำหรับ
SamplerV2)
- กลุ่ม twirling เพิ่มเติม: Samplomatic ให้คุณเลือกกลุ่ม twirling ที่จะใช้
ต่อ box แทนที่จะถูกจำกัดให้ใช้กลยุทธ์เดียวที่ Sampler ใช้ให้คุณ นอกจากนี้ยังรองรับกลุ่ม twirling อื่น ๆ นอกเหนือจาก Pauli เช่นกลุ่ม twirling
-
การจับคู่เชิงแนวคิด
ตารางต่อไปนี้แสดงให้เห็นว่าแนวคิดของ Sampler จับคู่กับ Executor อย่างไร
| Concept | Sampler | Executor |
|---|---|---|
| Import | from qiskit_ibm_runtime import SamplerV2 | from qiskit_ibm_runtime import Executor |
| Input | รายการ PUB (tuple) | QuantumProgram ของอ็อบเจกต์ QuantumProgramItem |
| Circuit และพารามิเตอร์ | tuple (circuit, params, shots) | program.append_circuit_item(circuit, circuit_arguments=...) |
| Twirling | TwirlingOptions | ชัดเจนผ่าน annotated box และ samplex (append_samplex_item) |
| Run call | sampler.run([pub, ...]) | executor.run(program) |
| ประเภทผลลัพธ์ | PrimitiveResult ของ SamplerPubResult | QuantumProgramResult (iterable) |
| เข้าถึงข้อมูล | result[0].data.<register> (BitArray) | result[0]["<register>"] (np.ndarray) |
| จัดการ noise | ตัวเลือกในตัว | ต้องประกอบด้วยตนเอง (annotation, samplex, NoiseLearnerV3) |
ภาพรวมของขั้นตอนการย้าย
ขั้นตอนที่ 1 ติดตั้งแพ็กเกจที่จำเป็น
Executor และ directed execution model ต้องการแพ็กเกจ samplomatic:
pip install qiskit qiskit-ibm-runtime samplomatic
# For visualization support:
# pip install samplomatic[vis]
- แนะนำให้ใช้
qiskit-ibm-runtimev0.48.0 เนื่องจากเพิ่มตัวเลือกmeas_level = "both"และกลุ่ม twirlinglocal_c1 - ต้องการ
qiskit >= 2.3.0 - ต้องการ
samplomatic >= 0.18.0
ขั้นตอนที่ 2 เปลี่ยน imports
Sampler:
from qiskit_ibm_runtime import SamplerV2 as Sampler
Executor:
from qiskit_ibm_runtime import Executor, QuantumProgram
ขั้นตอนที่ 3 แทนที่ PUB tuple ด้วย QuantumProgram
แทนที่จะส่งรายการ tuple (PUB) เมื่อใช้ Executor คุณจะสร้าง QuantumProgram และเพิ่ม item เข้าไป
QuantumProgram รับ item ประเภท circuit และ samplex:
-
append_circuit_item: เพิ่มCircuitItemซึ่งเป็น circuit และ (ถ้ามี) ค่าพารามิเตอร์ของมัน มันจะถูกรันตามที่เป็นอยู่ โดยไม่มีการสุ่มใด ๆใช้สิ่งนี้เมื่อคุณต้องการเพียงสุ่มตัวอย่าง circuit เหมือนที่ Sampler ทำกับ PUB ที่ไม่มี twirling เช่น เมื่อส่ง job สุ่มตัวอย่างธรรมดา หรือเมื่อคุณได้รวม variant ที่ต้องการด้วยตนเองไว้แล้ว
-
append_samplex_item: เพิ่มsamplexItemซึ่งเป็น template circuit บวกกับ samplex ที่สร้างชุดพารามิเตอร์แบบสุ่มที่ฝั่งเซิร์ฟเวอร์ใช้สิ่งนี้ เมื่อคุณต้องการให้เนื้อหาของ circuit ถูกสุ่ม กรณีหลักคือ twirling (gate หรือ measurement) หรือการฉีด noise ความสามารถนี้แทนที่ twirling ในตัวของ Sampler
QuantumProgram เดียวสามารถรับ item ทั้งสองประเภทได้ item แต่ละอันที่เพิ่มเข้าไปจะถูกรันเป็น
task อิสระ และสร้างรายการผลลัพธ์ของตัวเอง โดยทั่วไป ให้ใช้ append_circuit_item เมื่อ circuit ของคุณไม่จำเป็นต้องถูกสุ่ม มิฉะนั้นให้ใช้ append_samplex_item
ส่วนถัดไปแสดงแต่ละอย่างตามลำดับ: circuit ที่มีพารามิเตอร์ที่ใช้
append_circuit_item และการย้าย twirling โดยใช้ append_samplex_item
ในตัวอย่างโค้ดต่อไปนี้ isa_circuit หมายถึง circuit ที่ถูก transpile ให้สอดคล้องกับ Instruction Set Architecture (ISA) ของ target backend isa_circuit นี้มีพารามิเตอร์สองตัว
ขั้นตอนที่ 3a ย้าย circuit ที่มีพารามิเตอร์
ด้วย Sampler ค่าพารามิเตอร์เป็นองค์ประกอบที่สองของ PUB tuple ด้วย Executor
ให้ส่งเป็น circuit_arguments ไปยัง 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"]
ขั้นตอนที่ 3b ย้าย twirling ในตัวไปเป็น annotation ที่ชัดเจน
นี่คือการเปลี่ยนแปลงที่สำคัญที่สุด Sampler ใช้ twirling ให้คุณโดยใช้ตัวเลือก ด้วย Executor คุณจะประกาศความตั้งใจนั้นอย่างชัดเจนโดยใช้ annotated box และ samplex (จาก Samplomatic)
Sampler (twirling โดยใช้ตัวเลือก):
sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True
Executor (twirling โดยใช้ box และ 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
)
เนื่องจาก template circuit และ samplex ถูกสร้างที่ฝั่ง client คุณสามารถ ตรวจสอบและสุ่มตัวอย่างในเครื่องเพื่อยืนยัน output ก่อนที่จะส่งอะไรไปยัง ฮาร์ดแวร์
การตรวจสอบ: สุ่มตัวอย่าง template circuit ในเครื่อง
คุณสามารถดึงการสุ่มจาก samplex และผูกเข้ากับ template
circuit เพื่อยืนยันว่า samplex กำลังสร้างค่าพารามิเตอร์ตามที่คุณคาดหวัง
ค่าพารามิเตอร์ที่ samplex.sample คืนกลับมานั้นเข้ากันได้โดยตรงกับ
พารามิเตอร์ของ 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)
หากต้องการตรวจสอบเพิ่มเติม คุณสามารถยืนยันว่าการสุ่มแต่ละครั้งมีความเทียบเท่าเชิงตรรกะกับ
circuit ต้นฉบับได้ โดยเช่น การแปลงทั้งสองเป็นอ็อบเจกต์ Operator และเปรียบเทียบ implementation แบบ unitary (หลังจาก
คำนึงถึงการแก้ไข outputs["measurement_flips.<register>"] ที่ยกเลิก
measurement twirling) หรือโดยการเปรียบเทียบค่าคาดหวังจากการรัน
StatevectorSampler หรือ StatevectorEstimator ในเครื่อง ดูคู่มือ Samplomatic
Samplex inputs and outputs
สำหรับคำอธิบายแบบครบถ้วน
ขั้นตอนที่ 4 เปลี่ยนวิธีระบุ shot
ย้าย shot จาก PUB ไปยัง QuantumProgram(shots=...) ใน Executor shots จะใช้กับ job ทั้งหมด ส่ง job หลาย ๆ อันหากคุณต้องการจำนวน 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)
ขั้นตอนที่ 5 อัปเดตตัวเลือกตามความจำเป็น
Executor มีตัวเลือกให้ใช้น้อยกว่า Sampler เนื่องจาก การเลือกเกี่ยวกับ error-mitigation ตอนนี้อยู่ใน annotation และ samplex ของคุณแทนที่จะเป็นตัวเลือก
นอกจากนี้ยังมีความแตกต่างเชิงโครงสร้างในตำแหน่งที่การตั้งค่าอยู่
-
ด้วย Sampler ทุกอย่าง รวมถึงการเลือกที่ส่งผลต่อการประมวลผลผลลัพธ์ภายหลัง จะถูกตั้งค่าบน ตัวเลือกของ primitive หรือใน PUB
-
ด้วย Executor การเลือกที่ส่งผลต่อรูปแบบและการประมวลผลผลลัพธ์ของ job จะถูกตั้งค่าบน
QuantumProgramไม่ใช่บนExecutorOptions
ตัวอย่าง:
| Sampler | Executor |
|---|---|
shots | QuantumProgram(shots=...) |
meas_type | QuantumProgram(meas_level=...) |
ExecutorOptions มีเฉพาะการตั้งค่าการรันและ environment ระดับล่างเท่านั้น
ที่ไม่เปลี่ยนโครงสร้างของข้อมูลที่คืนกลับมา มันมีสามกลุ่มระดับบนสุด:
-
environment(EnvironmentOptions) -
execution(ExecutionOptions): มีตัวเลือกน้อยกว่า Sampler ตัวอย่างเช่น ไม่มีตัวเลือกmeas_typeสำหรับ Executor
ที่น่าสังเกตคือ ตัวเลือก twirling และ dynamical_decoupling มีอยู่ใน Sampler แต่ไม่มีใน Executor โดยค่าตัวเลือกเหล่านั้นจะถูกแสดงผ่าน 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)
ขั้นตอนที่ 6 อัปเดตคำสั่ง run
input สำหรับ Executor job คือ program แทนที่จะเป็น PUB
Sampler:
# Submit a job
sampler.run([(isa_circuit, parameter_values)])
Executor:
# Submit a job
executor.run(program)
ขั้นตอนที่ 7 เปลี่ยนวิธีเข้าถึงผลลัพธ์
ใน Executor ผลลัพธ์เป็น NumPy array ไม่ใช่อ็อบเจกต์ BitArray ใช้สตริงชื่อเป็น index (result[0]["meas"]) แล้วจะได้ np.ndarray กลับมา ไม่จำเป็นต้องจำ path ของแอตทริบิวต์ .data.<register>
เพื่ออัปเดตจาก Sampler ไปเป็น Executor ให้เปลี่ยน result[i].data.<reg> (BitArray) เป็น result[i]["<reg>"] (np.ndarray) จากนั้นเขียนการประมวลผลภายหลังที่ใช้ get_counts ใหม่เป็นการดำเนินการแบบ NumPy
| Task | Sampler | Executor |
|---|---|---|
| ดึงข้อมูล register | result[0].data.meas | result[0]["meas"] |
| ประเภทข้อมูล | BitArray | np.ndarray |
| Counts dictionary | result[0].data.meas.get_counts() | ประมวลผล array ภายหลังด้วยตนเอง |
| Register หลายตัว | result[0].data.<name> ต่อ register | result[0]["<name>"] ต่อ register |
| รูปร่าง CircuitItem array | - | (parameter_sets, shots, register_bits) |
| รูปร่าง SamplexItem array | - | (randomizations, parameter_sets, shots, register_bits) |
| ยกเลิก measurement twirling | อัตโนมัติ | result[i]["measurement_flips.<name>"] + XOR |
BitArray ของ Sampler มี helper ให้ใช้ (get_counts, slice_bits, slice_shots, expectation_values และ post-selection mask) Executor คืนค่าเป็น raw NumPy array ทำให้คุณสามารถทำ post-processing นี้ได้ด้วย NumPy operation มาตรฐาน
ขั้นตอนที่ 8 จัดการผลลัพธ์ที่ถูก twirled (การแก้ไข bit-flip)
เมื่อคุณใช้ measurement twirling ผ่าน SamplexItem Executor จะคืนค่าการวัดดิบ
(ที่ถูก twirled) พร้อมกับการแก้ไข bit-flip ที่จำเป็นในการยกเลิก twirling
คุณต้องนำไปใช้ด้วยตนเอง ไม่มีอะไรถูกแก้ไขโดยนัย
เมื่อใช้ Executor ให้ยกเลิก twirling อย่างชัดเจนโดยใช้การแก้ไข measurement_flips.<reg> และ XOR ดังตัวอย่างต่อไปนี้:
# 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
ไม่มีขั้นตอนที่เทียบเท่าใน Sampler เพราะมันยกเลิก twirling ให้คุณ
ตัวอย่างแบบเต็ม: ย้าย 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"]