ข้ามไปยังเนื้อหาหลัก

ย้ายจาก 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 ของ object NoiseLearnerV3Result หนึ่งชิ้นต่อ 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: input ExecutorOption

  • quantum_program: input QuantumProgram

  • 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 ได้ ให้ตรวจสอบส่วนนั้นของโค้ดกับ NoiseLearnerV3 API 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 จะกลายเป็น 0 PUB ที่ยังคง มี array ของค่าพารามิเตอร์อยู่จะ coerce ไม่สำเร็จ สำหรับการรันในเครื่อง ให้ลบ array พารามิเตอร์ออกจาก PUB การรันบน hardware จะยังคงเก็บ circuit แบบมีพารามิเตอร์เดิมและค่าของมันไว้

ขั้นตอนถัดไป​