ย้ายจาก server-side ไปยัง client-side Sampler และ Estimator
คู่มือนี้อธิบายวิธีการย้ายจาก implementation แบบ server-side ของ IBM Quantum®
Sampler และ Estimator ไปยัง implementation แบบ client-side ใหม่ใน
qiskit-ibm-runtime interface และตัวเลือกส่วนใหญ่ไม่เปลี่ยนแปลง ดังนั้นโค้ดส่วนใหญ่
จึงรันได้ตามเดิม แต่มีความแตกต่างด้านพฤติกรรมบางอย่างที่ควรทำความเข้าใจ
พื้นหลัง
Sampler และ Estimator เป็น primitive interface ที่กำหนดไว้ใน Qiskit IBM Quantum
Compute Service (เดิมชื่อ Qiskit Runtime) ได้ให้ implementation
ของ primitives เหล่านี้ภายใน runtime environment ของมันมาโดยตลอด เมื่อคุณเรียก sampler.run() หรือ
estimator.run() คำขอจะถูกส่งไปยัง service และการคำนวณ ทั้งหมด — รวมถึง
error suppression และ mitigation — เกิดขึ้นบน server side
ประสบการณ์แบบ black-box นี้สะดวก: คุณไม่ต้องกังวลเกี่ยวกับรายละเอียดของ implementation แต่ก็ทำให้ primitives debug ปรับแต่ง หรือเรียนรู้ได้ยาก เพราะคุณมองไม่เห็นว่าเกิดอะไรขึ้นระหว่างการประมวลผล
directed execution model ที่เพิ่งเปิดตัวใหม่ใช้แนวทางตรงกันข้ามและ มอบประสบการณ์แบบ white-box ให้ design intent ทั้งหมดถูกจับไว้บน client side และ server-side primitive เดียวคือ Executor ประมวลผล input เหล่านั้นตามที่ กำหนดไว้อย่างเคร่งครัด — มันไม่ตัดสินใจโดยนัยแทนคุณ
เริ่มตั้งแต่ qiskit-ibm-runtime v0.50.0 Sampler และ Estimator ถูก implement ใหม่
บน client side โดยอยู่บน Executor ทั้งสองยังคงให้ความสะดวกและ
abstraction เหมือนเดิม และตอนนี้คุณสามารถตรวจสอบรายละเอียดของ implementation ได้เมื่อจำเป็น
เนื่องจาก interface และตัวเลือกส่วนใหญ่ยังคงเหมือนเดิม การย้ายจึงควรจะ
ราบรื่น
หมายเหตุ: IBM Quantum รองรับเฉพาะ interface เวอร์ชัน 2 ของ Sampler และ Estimator (BaseSamplerV2 และ BaseEstimatorV2) ดังนั้นจึงเรียกง่ายๆ ว่า Sampler และ Estimator ในคู่มือนี้
อัปเดต imports
ในปัจจุบัน คุณต้อง import implementation ใหม่จาก module เฉพาะของมันอย่างชัดเจน:
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
ในอนาคตอันใกล้ top-level imports จะชี้ไปยัง client-side implementation ใหม่ และจะไม่จำเป็นต้องเปลี่ยนแปลงโค้ด:
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
ในทำนองเดียวกัน หากคุณสร้าง typed options object คุณต้อง import จาก
qiskit_ibm_runtime.options_models แทน หรือส่ง plain nested dict ก็ได้:
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
สิ่งที่ยังคงเหมือนเดิม
-
การสร้าง primitive ด้วย
modeและoptions -
signature ของ
run()และรูปแบบ PUB -
options tree (
options.twirling,options.resilience,options.default_shots, และอื่นๆ) -
โครงสร้างข้อมูลผลลัพธ์ที่ส่งกลับโดย
job.result()
การเปลี่ยนแปลงที่ไม่เข้ากันใน Sampler ใหม่
| การเปลี่ยนแปลง | การดำเนินการย้าย |
|---|---|
primitive พื้นฐานตอนนี้คือ Executor ทั้ง user interface ของ IBM Quantum Platform และ job.primitive_id จะแสดง executor แทน sampler | อัปเดตโค้ดใดๆ ที่อ้างอิง job.primitive_id |
implementation ใหม่ map input ของ Sampler ไปเป็น input ของ Executor ดังนั้น job.inputs จึงส่งคืน input ของ Executor | อัปเดตโค้ดใดๆ ที่อ้างอิง job.inputs ดู Job inputs |
การประมวลผลก่อนและหลังเพิ่มเติมตอนนี้เกิดขึ้นบน client side ดังนั้น sampler.run() และ job.result() อาจใช้เวลานานกว่าเดิม | เปิดใช้งาน INFO logging เพื่อติดตามความคืบหน้าของการประมวลผลบน client side ดู Enable INFO logging |
circuit metadata ถูกคัดลอกลงในผลลัพธ์ metadata ประเภทข้อมูลที่อนุญาตใน result metadata ตอนนี้จำกัดเฉพาะ str, float, int, bool และ list หรือ dictionary ของประเภทเหล่านั้น | หากคุณต้องการประเภทข้อมูลอื่น ให้เข้ารหัสเป็น string ก่อน (ตัวอย่างเช่น ด้วย base64) |
options class (options_models.SamplerOptions และอื่นๆ) ตอนนี้เป็น Pydantic model แทนที่จะเป็น dataclass ดังนั้นจึงไม่สามารถแปลงเป็น Python dictionary โดยใช้ asdict() ได้อีกต่อไป | ใช้ options.model_dump() แทน |
options class ที่เคยมี suffix V2 (ExecutionOptionsV2 และอื่นๆ) ไม่มีอีกต่อไป เนื่องจาก primitives V1 ไม่รองรับแล้ว | ลบ suffix V2 ของ options class เหล่านี้: แทนที่ ExecutionOptionsV2 ด้วย ExecutionOptions, ResilienceOptionsV2 ด้วย ResilienceOptions และ SamplerExecutionOptionsV2 ด้วย SamplerExecutionOptions |
หากเปิดใช้งาน twirling และมีการระบุ shots (ใน PUBs หรือใน run()), shots_per_randomization และ num_randomizations ทั้งหมด แล้ว num_randomizations * shots_per_randomization จะมีความสำคัญเหนือ shots | ละเว้น num_randomizations และ shots_per_randomization หากต้องการให้ใช้ค่า shots |
การตรวจสอบ input บางส่วนถูกย้ายไปที่ server side และตอนนี้จะ raise RuntimeError แทน IBMInputValueError | อัปเดตประเภท exception ที่โค้ดของคุณดักจับ |
| ไม่รองรับค่า shot ที่ผสมกันในงานเดียวอีกต่อไป | ส่งงานแยกสำหรับแต่ละค่า shot ดู Job splitting สำหรับข้อควรพิจารณา |
การเปลี่ยนแปลงที่ไม่เข้ากันใน Estimator ใหม่
| การเปลี่ยนแปลง | การดำเนินการย้าย |
|---|---|
primitive พื้นฐานตอนนี้คือ Executor ทั้ง user interface ของ IBM Quantum Platform และ job.primitive_id จะแสดง executor แทน estimator | อัปเดตโค้ดใดๆ ที่อ้างอิง job.primitive_id |
implementation ใหม่ map input ของ Estimator ไปเป็น input ของ Executor ดังนั้น job.inputs จึงส่งคืน input ของ Executor | อัปเดตโค้ดใดๆ ที่อ้างอิง job.inputs ดู Job inputs |
การประมวลผลก่อนและหลังเพิ่มเติมตอนนี้เกิดขึ้นบน client side ดังนั้น estimator.run() และ job.result() อาจใช้เวลานานกว่าเดิม | เปิดใช้งาน INFO logging เพื่อติดตามความคืบหน้าของการประมวลผลบน client side ดู Enable INFO logging |
circuit metadata ถูกคัดลอกลงในผลลัพธ์ metadata ประเภทข้อมูลที่อนุญาตใน result metadata ตอนนี้จำกัดเฉพาะ str, float, int, bool และ list หรือ dictionary ของประเภทเหล่านั้น | หากคุณต้องการประเภทข้อมูลอื่น ให้เข้ารหัสเป็น string ก่อน (ตัวอย่างเช่น ด้วย base64) |
options class (options_models.EstimatorOptions และอื่นๆ) ตอนนี้เป็น Pydantic model แทนที่จะเป็น dataclass ดังนั้นจึงไม่สามารถแปลงเป็น Python dictionary โดยใช้ asdict() ได้อีกต่อไป | ใช้ options.model_dump() แทน |
options class ที่เคยมี suffix V2 (ExecutionOptionsV2 และอื่นๆ) ไม่มีอีกต่อไป เนื่องจาก primitives V1 ไม่รองรับแล้ว | ลบ suffix V2 ของ options class เหล่านี้: แทนที่ ExecutionOptionsV2 ด้วย ExecutionOptions และ ResilienceOptionsV2 ด้วย ResilienceOptions |
| input option ทั้งหมดถูกส่งคืนใน result metadata แทนที่จะเป็นส่วนย่อยที่เลือกไว้ | ไม่มี — นี่เป็นข้อมูลเท่านั้น |
การตรวจสอบ input บางส่วนถูกย้ายไปที่ server side และตอนนี้จะ raise RuntimeError แทน IBMInputValueError | อัปเดตประเภท exception ที่โค้ดของคุณดักจับ |
| ไม่มีการเรียนรู้ noise โดยนัยสำหรับ PEA และ PEC อีกต่อไป การเรียนรู้ noise ของการวัดสำหรับ TREX ยังคงรองรับอยู่ | เรียนรู้ noise model แยกต่างหากและส่งให้กับ Estimator ดู Perform explicit noise learning for PEA and PEC |
ประเภท input ของ ResilienceOptions.layer_noise_model แตกต่างออกไปและสามารถสร้างได้จากผลลัพธ์ของ NoiseLearnerV3 | ดู Perform explicit noise learning for PEA and PEC เกี่ยวกับวิธีเรียนรู้ noise model โดยใช้ NoiseLearnerV3 และส่งให้กับ Estimator |
MeasureNoiseLearningOptions.shots_per_randomization ไม่รองรับอีกต่อไป | ใช้ค่า shot เดียวสำหรับ circuit ทั้งหมดในงาน รวมถึง circuit การเรียนรู้ noise ของการวัด หากคุณต้องใช้ค่า shot ที่แตกต่างกัน ให้ใช้ TREX ด้วย qiskit-mitigation นอกเหนือจาก Estimator |
| ไม่รองรับค่า precision ที่ผสมกันในงานเดียวอีกต่อไป | ส่งงานแยกสำหรับแต่ละ precision ที่ต้องการ ดู Job splitting สำหรับข้อควรพิจารณา |
ตัวเลือก seed_estimator ไม่รองรับอีกต่อไป | ลบการกำหนดค่า options.seed_estimator ใดๆ (การกำหนดค่าจะทำให้เกิด ValidationError) ไม่มีค่าเทียบเท่าฝั่ง client-side ดังนั้นผลลัพธ์จึงไม่สามารถทำซ้ำได้ผ่าน seed นี้อีกต่อไป |
เปิดใช้งาน INFO logging
เนื่องจากงานส่วนใหญ่ตอนนี้เกิดขึ้นบน client side จึงเป็นประโยชน์ที่จะเห็นความคืบหน้าของการ
ประมวลผลนั้น เปิดใช้งาน logging ระดับ INFO สำหรับ logger qiskit_ibm_runtime:
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
ทำการเรียนรู้ noise อย่างชัดเจนสำหรับ PEA และ PEC
Estimator ใหม่จะไม่ทำการเรียนรู้ noise แบบ โดยนัย อีกต่อไปเมื่อเลือกวิธี error
mitigation แบบ PEA หรือ PEC คุณต้องเรียนรู้ noise model อย่างชัดเจนและส่งเข้าไป
ใช้ NoiseLearnerV3 ใหม่เพื่อควบคุมว่า circuit
จะถูกแบ่งชั้นเป็น layer อย่างไร มันรับ list ของ boxed circuit instruction (ตัวอย่างเช่น
unique layers) เป็น input
ตอนนี้ PEA และ PEC จำเป็นต้อง ใช้ pattern ที่ชัดเจนนี้ อย่าข้ามขั้นตอนการเรียนรู้ noise ไม่เช่นนั้นโค้ดของคุณจะล้มเหลว การเรียนรู้ noise ของการวัดสำหรับ TREX ไม่ได้รับผลกระทบและยังคงทำงานเหมือนเดิม
ในทำนองเดียวกัน หากโค้ดของคุณใช้ NoiseLearner และส่ง noise model ที่ได้ไปยัง server-side Estimator คุณต้อง ย้ายไปใช้ NoiseLearnerV3 อย่าใช้ NoiseLearner รุ่นเก่า ซึ่งเข้ากันไม่ได้กับ Estimator ใหม่
ตัวเลือกการเรียนรู้ noise ทั้งหมดใน server-side Estimator (LayerNoiseLearningOptions) map ตรงกับตัวเลือก NoiseLearnerV3 (NoiseLearnerV3Options) ยกเว้น max_layers_to_learn จำนวน layer ที่จะเรียนรู้จะขึ้นอยู่กับจำนวน layer ที่ส่งให้กับ NoiseLearnerV3 แทน
ตัวอย่างเช่น:
Server-side Estimator (เปิดใช้งาน PEC):
from qiskit_ibm_runtime import Estimator
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64
job = estimator.run(pubs)
Client-side Estimator (เปิดใช้งาน PEC):
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)
# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()
# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)
# Now execute the target PUBs.
job = estimator.run(pubs)
ย้ายจาก NoiseLearner ไปยัง NoiseLearnerV3
NoiseLearner ทำงานได้เฉพาะกับ implementation แบบ server-side ของ Estimator เท่านั้น ดังนั้นหากโค้ดของคุณใช้ NoiseLearner เพื่อเรียนรู้ noise model และส่งให้กับ Estimator คุณต้องอัปเดตโค้ดให้ใช้ NoiseLearnerV3
ดูคู่มือ Migrate from NoiseLearner to NoiseLearnerV3 สำหรับรายละเอียด
การแบ่งงาน
เมื่อคุณต้องแบ่งงานหนึ่งงานออกเป็นหลายงานเนื่องจากไม่รองรับค่า shot หรือ precision ที่ผสมกันในงานเดียวอีกต่อไป ให้พิจารณาสิ่งต่อไปนี้:
-
จัดกลุ่ม PUB ตามค่าเป้าหมาย — หนึ่งงานต่อหนึ่งค่าที่แตกต่างกัน ไม่ใช่หนึ่งงานต่อหนึ่ง PUB การแบ่งงานคือการจัดกลุ่มใหม่ ดังนั้นจำนวน PUB ทั้งหมดที่คุณส่งจะไม่เปลี่ยนแปลง ตัวอย่างเช่น เมื่อมี
[A@0.01, B@0.05, C@0.01]ให้ส่งสองงาน:[A, C]ที่precision=0.01และ[B]ที่precision=0.05การส่งAและCเป็นงานแยกจะมีประสิทธิภาพน้อยกว่า เนื่องจากแต่ละงานมี overhead คงที่ -
เรียนรู้ครั้งเดียวและใช้ noise model ในงานที่แบ่งทั้งหมด การรันงาน
NoiseLearnerV3เพียงงานเดียวครอบคลุม layer ทั้งหมดจะมีประสิทธิภาพมากกว่า ผลลัพธ์ของงาน noise learner ประกอบด้วย list ของ objectNoiseLearnerV3Resultหนึ่งชิ้นต่อ instruction แต่ละตัวใน input และเรียงตามลำดับเดียวกับ list ที่ป้อนเข้าไป คุณสามารถ ใช้ output ของงาน noise learner นี้ในงาน (Estimator) ที่แบ่งทั้งหมด และ noise model สำหรับ layer ที่ไม่อยู่ใน PUB ของงานที่แบ่งจะถูกละเว้น -
ส่งงานที่แบ่งทั้งหมดใน
Batchก่อน แล้วจึงเก็บรวบรวมผลลัพธ์ โหมดการ execute แบบBatchให้การ execute แบบขนานที่มีประสิทธิภาพเมื่อมีหลายงาน อย่างไรก็ตามjob.result()เป็นแบบ blocking ดังนั้นการเรียกภายใน loop การส่งงานจะทำให้งานทำงานแบบเรียงลำดับและลบล้างประโยชน์ของการใช้Batchตรวจสอบให้แน่ใจว่าคุณใช้ pattern ส่งทั้งหมดก่อนแล้วจึงเก็บผลลัพธ์ (แสดงด้านล่าง)
ในตัวอย่างต่อไปนี้ pub1 และ pub2 ต้องการ precision=0.5 ในขณะที่ pub3 ต้องการ precision=0.1:
group1_pubs = [pub1, pub2]
group2_pubs = [pub3]
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True
# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)
# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))
# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]
โครงสร้าง input ของงาน
implementation ใหม่ map input ของ Sampler หรือ Estimator ไปเป็น input ของ Executor ดังนั้น job.inputs จึงส่งคืน dictionary ที่มี input ของ Executor dictionary นี้มี key ดังต่อไปนี้:
-
options: inputExecutorOption -
quantum_program: inputQuantumProgram -
schema_version: เวอร์ชัน schema ฝั่ง server-side ที่ใช้
หากโค้ดของคุณใช้ job.inputs['options'] เพื่อค้นหาตัวเลือกที่ระบุสำหรับงาน ตอนนี้คุณสามารถใช้ job.result().metadata['options'] แทนได้
ทดสอบในเครื่องด้วย fake backend
ก่อนที่จะส่งไปยัง hardware คุณสามารถตรวจสอบความถูกต้องของโค้ดที่ย้ายแล้วกับ Fake*
backend เพื่อตรวจจับข้อผิดพลาดทาง syntax ได้ล่วงหน้า โปรดทราบรายละเอียดต่อไปนี้เกี่ยวกับโหมดทดสอบในเครื่อง:
-
มันไม่ได้สร้างผลลัพธ์ของ hardware ซ้ำ การจำลองแบบ noisy ในเครื่องไม่สามารถ จำลอง noise ของอุปกรณ์จริงได้อย่างสมบูรณ์แบบ ดังนั้น output อาจแตกต่างกัน การรันจะตรวจสอบ ว่า option path และประเภทค่าถูกต้อง
-
NoiseLearnerV3ไม่มีโหมดทดสอบในเครื่อง:modeของมันรับเฉพาะBackend,SessionหรือBatchจริงเท่านั้น ดังนั้นคุณจึงไม่สามารถทดสอบขั้นตอนการเรียนรู้ noise กับ fake backend ได้ ให้ตรวจสอบส่วนนั้นของโค้ดกับNoiseLearnerV3API reference แทน ยืนยันว่า constructor รูปแบบ input ของrun(instructions)และ helper ใดๆ (เช่น unique-layer helper) ถูก ใช้ตามที่ระบุไว้ในเอกสาร
ทำ Circuit ให้เป็น Clifford เพื่อการจำลองในเครื่องอย่างมีประสิทธิภาพ
fake backend ใช้ simulator แบบ statevector (noisy) ซึ่งต้นทุนเพิ่มขึ้นแบบ exponential ตาม
จำนวน qubit และความลึก ดังนั้น circuit ของ workload ที่สมจริงอาจค้างหรือใช้หน่วยความจำหมด เนื่องจาก
การทดสอบในเครื่องจำเป็นต้องทดสอบเพียง option path เท่านั้น (ไม่ต้องสร้างผลลัพธ์ทางฟิสิกส์ซ้ำ)
ให้ลด circuit ให้เป็น Clifford ก่อนด้วย
ConvertISAToClifford
ซึ่งจะปัดมุมของ RZ/RZZ/RX แต่ละตัวให้เป็นจำนวนเท่าที่ใกล้ที่สุดของ π/2 circuit แบบ Clifford
จำลองได้อย่างมีประสิทธิภาพ (stabilizer simulation) ไม่ว่าจะมีขนาดเท่าใด
from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford
clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive
ConvertISAToClifford ต้องการ ISA circuit เป็น input (ผลลัพธ์ของ
generate_preset_pass_manager(...).run(...) ที่กำหนดเป้าหมายไปที่ backend) คุณต้องคำนึงถึงผลที่ตามมาต่อไปนี้
เมื่อสร้าง PUB ในเครื่อง:
-
attribute
.layoutจะถูกลบทิ้ง circuit ที่ทำเป็น Clifford แล้วยังคงมีจำนวน qubit เท่าเดิม แต่clifford.layoutเป็นNoneดังนั้นobservable.apply_layout(clifford.layout)จะล้มเหลว ให้ layout observable จาก circuit ISA ก่อนทำ Clifford แทน:isa_obs = observable.apply_layout(isa_circuit.layout)แล้วรัน(clifford, isa_obs) -
Parameter จะถูก bind ออกไป การปัดมุมการหมุนจะเปลี่ยน circuit ISA แบบมีพารามิเตอร์ ให้เป็น Clifford ที่เป็นรูปธรรม ดังนั้น
clifford.num_parametersจะกลายเป็น0PUB ที่ยังคง มี array ของค่าพารามิเตอร์อยู่จะ coerce ไม่สำเร็จ สำหรับการรันในเครื่อง ให้ลบ array พารามิเตอร์ออกจาก PUB การรันบน hardware จะยังคงเก็บ circuit แบบมีพารามิเตอร์เดิมและค่าของมันไว้