ย้ายไปใช้ Qiskit Runtime V2 primitives
primitives เดิม (เรียกว่า V1 primitives) ได้แก่ V1 Sampler และ V1 Estimator ถูก deprecated ใน qiskit-ibm-runtime 0.23 แล้ว
การรองรับถูกลบออกเมื่อวันที่ 15 สิงหาคม 2024
เนื่องจาก V1 primitives ถูก deprecated แล้ว โค้ดทั้งหมดควรได้รับการย้ายไปใช้อินเทอร์เฟซ V2 คู่มือนี้อธิบายว่า Qiskit Runtime V2 primitives (ใช้งานได้กับ qiskit-ibm-runtime 0.21.0) มีอะไรเปลี่ยนไปบ้างและเพราะอะไร อธิบาย primitive ใหม่แต่ละตัวอย่างละเอียด และให้ตัวอย่างเพื่อช่วยให้ย้ายโค้ดจาก primitives เดิมไปเป็น V2 primitives ตัวอย่างในคู่มือทั้งหมดใช้ Qiskit Runtime primitives แต่โดยทั่วไปการเปลี่ยนแปลงเดียวกันนี้ก็ใช้กับ primitive implementations อื่นๆ ด้วย ฟังก์ชันที่เป็นเอกลักษณ์ของ Qiskit Runtime เช่น error mitigation ยังคงเป็นเอกลักษณ์ของ Qiskit Runtime
ดูข้อมูลเกี่ยวกับการเปลี่ยนแปลง Qiskit reference primitives (ปัจจุบันเรียกว่า statevector primitives) ได้ที่ ส่วน qiskit.primitives ในหน้าการเปลี่ยนแปลงฟีเจอร์ Qiskit 1.0 ดู StatevectorSampler และ StatevectorEstimator สำหรับ V2 primitive reference implementations
ภาพรวม
V2 ของ primitives นำเสนอพร้อมกับ base class ใหม่สำหรับทั้ง Sampler และ Estimator (BaseSamplerV2 และ BaseEstimatorV2) รวมถึงประเภทใหม่สำหรับ input และ output
อินเทอร์เฟซใหม่ให้กำหนด Circuit เดียวและ observables หลายตัว (ถ้าใช้ Estimator) รวมถึง parameter value sets สำหรับ Circuit นั้น ทำให้การ sweep ข้าม parameter value sets และ observables สามารถระบุได้อย่างมีประสิทธิภาพ ก่อนหน้านี้ต้องระบุ Circuit เดียวกันหลายครั้งเพื่อให้ตรงกับขนาดของข้อมูลที่จะรวมกัน นอกจากนี้ แม้จะยังใช้ resilience_level (ถ้าใช้ Estimator) เป็น knob แบบง่ายได้ V2 primitives ก็ให้ความยืดหยุ่นในการเปิดหรือปิดวิธี error mitigation / suppression แต่ละวิธีเพื่อปรับแต่งตามความต้องการ
เพื่อลดเวลาการรัน job รวม V2 primitives รับเฉพาะ Circuit และ observables ที่ใช้คำสั่งที่รองรับโดย QPU เป้าหมาย (quantum processing unit) Circuit และ observables ดังกล่าวเรียกว่า instruction set architecture (ISA) circuits และ observables V2 primitives ไม่ทำการ layout, routing, และ translation โปรดดู เอกสาร transpilation สำหรับคำแนะนำในการแปลง Circuit
Sampler V2 ถูกทำให้เรียบง่ายขึ้นเพื่อมุ่งเน้นที่งานหลักในการ sampling output register จากการรัน quantum circuits มันคืนค่า samples ซึ่งประเภทถูกกำหนดโดยโปรแกรม โดยไม่มี weights ข้อมูล output ยังแยกตามชื่อ output register ที่กำหนดโดยโปรแกรม การเปลี่ยนแปลงนี้รองรับ Circuit ที่มี classical control flow ในอนาคต
ดู EstimatorV2 API reference และ SamplerV2 API reference สำหรับรายละเอียดทั้งหมด
การเปลี่ยนแปลงหลัก
Import
เพื่อความเข้ากันได้แบบย้อนหลัง ต้อง import V2 primitives อย่างชัดเจน การระบุ import <primitive>V2 as <primitive> ไม่จำเป็น แต่ทำให้การย้ายโค้ดไป V2 ง่ายขึ้น
หลังจาก V1 primitives ไม่รองรับแล้ว import <primitive> จะ import เวอร์ชัน V2 ของ primitive ที่ระบุ
- Estimator V2
- Estimator (V1)
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Estimator
- Sampler V2
- Sampler (V1)
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import Sampler
Input และ output
Input
ทั้ง SamplerV2 และ EstimatorV2 รับ primitive unified blocs (PUBs) หนึ่งตัวหรือมากกว่าเป็น input แต่ละ PUB คือ tuple ที่มี Circuit หนึ่ง ตัวและข้อมูลที่ broadcast ไปยัง Circuit นั้น ซึ่งสามารถมี observables และ parameters หลายตัวได้ แต่ละ PUB คืนผลลัพธ์
- รูปแบบ Sampler V2 PUB: (
<circuit>,<parameter values>,<shots>) โดย<parameter values>และ<shots>เป็นตัวเลือก - รูปแบบ Estimator V2 PUB: (
<circuit>,<observables>,<parameter values>,<precision>) โดย<parameter values>และ<precision>เป็นตัวเลือก กฎ broadcasting ของ Numpy ถูกใช้เมื่อรวม observables และ parameter values
นอกจากนี้ มีการเปลี่ยนแปลงดังต่อไปนี้:
- Estimator V2 ได้รับ argument
precisionในเมธอดrun()ที่ระบุความแม่นยำของการประมาณค่า expectation value - Sampler V2 มี argument
shotsในเมธอดrun()
ตัวอย่าง
ตัวอย่าง Estimator V2 ที่ใช้ precision ใน run():
# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1),
(circuit2, obs_array_2)], precision=0.05)
ตัวอย่าง Sampler V2 ที่ใช้ shots ใน run():
# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)
# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])
Output
output ตอนนี้อยู่ในรูปแบบ PubResult PubResult คือข้อมูลและ metadata ที่ได้จากการรัน PUB เดียว
-
Estimator V2 ยังคงคืนค่า expectation values
-
ส่วน
dataของ Estimator V2 PubResult มีทั้ง expectation values และ standard errors (stds) V1 คืนค่า variance ใน metadata -
Sampler V2 คืน per-shot measurements ในรูปแบบ bitstrings แทนการแจกแจงความน่าจะเป็นแบบ quasi-probability จากอินเทอร์เฟซ V1 bitstrings แสดงผลลัพธ์การวัด โดยรักษาลำดับ shot ที่วัด
-
Sampler V2 มีเมธอดที่ช่วยอำนวยความสะดวก เช่น
get_counts()เพื่อช่วยในการย้าย -
ออบเจกต์ผลลัพธ์ของ Sampler V2 จัดระเบียบข้อมูลตาม ชื่อ classical register ของ circuit input เพื่อความเข้ากันได้กับ dynamic circuits ตามค่าเริ่มต้น ชื่อ classical register คือ
measดังตัวอย่างต่อไปนี้ เมื่อกำหนด circuit ถ้าสร้าง classical register หนึ่งตัวหรือมากกว่าด้วยชื่อที่ไม่ใช่ค่าเริ่มต้น ให้ใช้ชื่อนั้นในการดึงผลลัพธ์ หาชื่อ classical register ได้โดยรัน<circuit_name>.cregsเช่นqc.cregs# Define a quantum circuit with 2 qubitscircuit = QuantumCircuit(2)circuit.h(0)circuit.cx(0, 1)circuit.measure_all()circuit.draw()┌───┐ ░ ┌─┐q_0: ┤ H ├──■───░─┤M├───└───┘┌─┴─┐ ░ └╥┘┌─┐q_1: ─────┤ X ├─░──╫─┤M├└───┘ ░ ║ └╥┘meas: 2/══════════════╩══╩═0 1
ตัวอย่าง Estimator (input และ output)
- 1 circuit, 4 observables
- 1 circuit, 4 observables, 2 parameter sets
- 2 circuits, 2 observables
# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values
# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs
# Estimator V1: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v1.run([circuit] * 8, [obs1, obs2, obs3, obs4] * 2,
[vals1, vals2] * 4)
evs = job.result().values
# Estimator V2: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v2.run([(circuit, [[obs1], [obs2], [obs3], [obs4]],
[[vals1], [vals2]])])
evs = job.result()[0].data.evs
# Estimator V1: Cannot execute 2 circuits with different observables
# Estimator V2: Execute 2 circuits with 2 different observables. There are
# two PUBs because each PUB can have only one circuit.
job = estimator_v2.run([(circuit1, obs1), (circuit2, obs2)])
evs1 = job.result()[0].data.evs # result for pub 1 (circuit 1)
evs2 = job.result()[1].data.evs # result for pub 2 (circuit 2)
ตัวอย่าง Sampler (input และ output)
- 1 circuit, 3 parameter sets
- 2 circuits, 1 parameter set
- Convert V2 output to V1 format
# Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists
# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()
# Sampler V1: Execute 2 circuits with 1 parameter set
job = sampler_v1.run([circuit1, circuit2], [vals1] * 2)
dists = job.result().quasi_dists
# Sampler V2: Execute 2 circuits with 1 parameter set
job = sampler_v2.run([(circuit1, vals1), (circuit2, vals1)])
counts1 = job.result()[0].data.meas.get_counts() # result for pub 1 (circuit 1)
counts2 = job.result()[1].data.meas.get_counts() # result for pub 2 (circuit 2)
รูปแบบ output ของ V1 คือ dictionary ของ bitstrings (เป็น int) เป็น key และ quasi-probabilities เป็น value สำหรับแต่ละ circuit รูปแบบ V2 มี key เดียวกัน (แต่เป็น string) และ counts เป็น value เพื่อแปลงรูปแบบ V2 เป็น V1 ให้หาร counts ด้วยจำนวน shots โดยจำนวน shots ที่เลือกอธิบายไว้ในคู่มือ Sampler options
v2_result = sampler_v2_job.result()
v1_format = []
for pub_result in v2_result:
counts = pub_result.data.meas.get_counts()
v1_format.append( {int(key, 2): val/shots for key, val in counts.items()} )
ตัวอย่างที่ใช้ output registers ต่างกัน
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)
circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: "
f"{pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: "
f"{pub_result.data.beta.get_counts()}")
Options
Options ถูกระบุแตกต่างกันใน V2 primitives ดังนี้:
SamplerV2และEstimatorV2ตอนนี้มี options classes แยกกัน สามารถดู options ที่ใช้งานได้และอัปเดตค่า option ระหว่างหรือหลังการเริ่มต้น primitive- แทนที่จะใช้เมธอด
set_options()V2 primitive options มีเมธอดupdate()ที่ใช้การเปลี่ยนแปลงกับ attributeoptions - ถ้าไม่ระบุค่าสำหรับ option จะได้รับค่าพิเศษ
Unsetและใช้ค่าเริ่มต้นของ server - สำหรับ V2 primitives attribute
optionsเป็นประเภท Pythondataclassสามารถใช้เมธอดasdictในตัวเพื่อแปลงเป็น dictionary
ดู API reference สำหรับรายการ options ที่ใช้งานได้
- Estimator V2
- Estimator (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})
# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)
# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
estimator = Estimator(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
estimator.set_options(shots=4000)
- Sampler V2
- Sampler (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})
# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True
# This does bulk update.
# The value for default_shots is overridden
# if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024,
dynamical_decoupling={"sequence_type": "XpXm"})
# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
sampler = Sampler(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
sampler.set_options(shots=2000)
การลดและการยับยั้งข้อผิดพลาด
-
เนื่องจาก Sampler V2 คืนค่า samples โดยไม่ผ่านการประมวลผลเพิ่มเติม จึงไม่รองรับ resilience levels
-
Sampler V2 ไม่รองรับ
optimization_level -
Estimator V2 จะยกเลิกการรองรับ
optimization_levelในหรือประมาณวันที่ 30 กันยายน 2024 -
Estimator V2 ไม่รองรับ resilience level 3 เพราะ resilience level 3 ใน V1 Estimator ใช้ Probabilistic Error Cancellation (PEC) ซึ่งพิสูจน์แล้วว่าให้ผลลัพธ์ที่ไม่มี bias แต่แลกมาด้วยเวลาประมวลผลแบบ exponential Level 3 ถูกลบออกเพื่อเน้นให้เห็น tradeoff ดังกล่าว อย่างไรก็ตาม คุณยังคงสามารถใช้ PEC เป็นวิธีลดข้อผิดพลาดได้ โดยระบุ option
pec_mitigation -
Estimator V2 รองรับ
resilience_level0–2 ตามตารางด้านล่าง option เหล่านี้มีความสามารถมากกว่า V1 คุณยังสามารถเปิด/ปิดวิธีลดหรือยับยั้งข้อผิดพลาดแต่ละอย่างได้อย่างชัดเจนLevel 1 Level 2 Measurement twirling Measurement twirling Readout error mitigation Readout error mitigation ZNE
- Estimator V2
- Estimator (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend)
# Set resilience_level to 0
estimator.options.resilience_level = 0
# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import Estimator, Options
estimator = Estimator(backend, options=options)
options = Options()
options.resilience_level = 2
- Sampler V2
- Sampler (V1)
from qiskit_ibm_runtime import SamplerV2 as Sampler
sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
print(f">> dynamical decoupling sequence to use: "
f"{sampler.options.dynamical_decoupling.sequence_type}")
from qiskit_ibm_runtime import Sampler, Options
sampler = Sampler(backend, options=options)
options = Options()
options.resilience_level = 2
การ Transpile
V2 primitives รองรับเฉพาะ Circuit ที่สอดคล้องกับ Instruction Set Architecture (ISA) ของ Backend นั้น ๆ เท่านั้น เนื่องจาก primitives ไม่ทำการ layout, routing, และ translation จึงไม่รองรับ option การ transpile ที่มีใน V1
สถานะของ Job
V2 primitives มีคลาส RuntimeJobV2 ใหม่ที่ inherit จาก BasePrimitiveJob โดยเมธอด status() ของคลาสใหม่นี้คืนค่าเป็น string แทนที่จะเป็น JobStatus enum จาก Qiskit ดูรายละเอียดได้ที่ RuntimeJobV2 API reference
- V2 primitives
- V1 primitives
job = estimator.run(...)
# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")
from qiskit.providers.jobstatus import JobStatus
job = estimator.run(...)
#check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() is JobStatus.RUNNING}")
ขั้นตอนการย้ายไปใช้ Estimator V2
-
แทนที่
from qiskit_ibm_runtime import Estimatorด้วยfrom qiskit_ibm_runtime import EstimatorV2 as Estimator -
ลบ statement
from qiskit_ibm_runtime import Optionsออก เนื่องจากคลาสOptionsไม่ได้ถูกใช้งานใน V2 primitives คุณสามารถส่ง option เป็น dictionary ตอน initialize คลาสEstimatorV2แทน (เช่นestimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), หรือกำหนดหลังจาก initialize แล้ว:estimator = Estimator(backend)estimator.options.dynamical_decoupling.enable = True -
ตรวจสอบ option ที่รองรับทั้งหมด และอัปเดตตามความเหมาะสม
-
จัดกลุ่ม Circuit แต่ละอันที่ต้องการรันพร้อมกับ observable และค่า parameter ที่ต้องการใช้กับ Circuit ในรูปแบบ tuple (PUB) ตัวอย่างเช่น ใช้
(circuit1, observable1, parameter_set1)หากต้องการรันcircuit1กับobservable1และparameter_set1 -
คุณอาจต้อง reshape array ของ observable หรือ parameter sets หากต้องการใช้ outer product ของมัน ตัวอย่างเช่น array ของ observable ที่มี shape (4, 1) และ array ของ parameter sets ที่มี shape (1, 6) จะให้ผลลัพธ์เป็น expectation values ขนาด (4, 6) ดู Numpy broadcasting rules สำหรับรายละเอียดเพิ่มเติม
-
คุณสามารถระบุ precision ที่ต้องการสำหรับ PUB นั้น ๆ โดยไม่บังคับ
-
อัปเดตเมธอด
run()ของ Estimator ให้รับ list ของ PUBs เช่นrun([(circuit1, observable1, parameter_set1)])คุณสามารถระบุprecisionที่นี่ได้โดยไม่บังคับ ซึ่งจะใช้กับ PUBs ทั้งหมด -
ผลลัพธ์ของ Estimator V2 จะถูกจัดกลุ่มตาม PUBs คุณสามารถดู expectation value และ standard error ของแต่ละ PUB ได้โดยอ้างอิงด้วย index ตัวอย่างเช่น:
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
ตัวอย่างเต็มของ Estimator
รันการทดลองเดียว
ใช้ Estimator เพื่อหา expectation value ของคู่ Circuit-observable เดียว
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
estimator = Estimator(backend)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
estimator = Estimator(backend)
job = estimator.run(isa_circuit, isa_observable)
result = job.result()
print(f" > Observable: {observable.paulis}")
print(f" > Expectation value: {result.values}")
print(f" > Metadata: {result.metadata}")
รันหลายการทดลองในงานเดียว
ใช้ Estimator เพื่อหา expectation values ของคู่ Circuit-observable หลายคู่
- Estimator V2
- Estimator (V1)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]
isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1],
isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]
# Get ISA circuits
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
estimator = Estimator(backend)
job = estimator.run(isa_circuits, isa_observables)
result = job.result()
print(f" > Expectation values: {result.values}")
รัน Circuit แบบ Parameterized
ใช้ Estimator เพื่อรันการทดลองหลายชุดในงานเดียว โดยอาศัยค่า parameter เพื่อเพิ่มความสามารถนำ Circuit กลับมาใช้ซ้ำ ในตัวอย่างต่อไปนี้ ให้สังเกตว่าขั้นตอนที่ 1 และ 2 เหมือนกันทั้งใน V1 และ V2
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
theta = Parameter("θ")
chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout)
for operator in ops]
from qiskit_ibm_runtime import EstimatorV2 as Estimator
# Step 3: Execute using Qiskit primitives.
# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))
estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")
import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
theta = Parameter("θ")
chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout)
for operator in ops]
from qiskit_ibm_runtime import Estimator
# Step 3: Execute using Qiskit Primitives.
num_ops = len(isa_observables)
batch_circuits = [chsh_isa_circuit] * number_of_phases * num_ops
batch_ops = [op for op in isa_observables for _ in individual_phases]
batch_phases = individual_phases * num_ops
estimator = Estimator(backend, options={"shots": int(1e4)})
job = estimator.run(batch_circuits, batch_ops, batch_phases)
expvals = job.result().values
ใช้ Session และ Options ขั้นสูง
สำรวจ Session และ options ขั้นสูงเพื่อเพิ่มประสิทธิภาพการทำงานของ Circuit บน QPU
โค้ดบล็อกต่อไปนี้จะคืนค่าข้อผิดพลาดสำหรับผู้ใช้ที่อยู่ใน Open Plan เนื่องจากใช้ Session งานบน Open Plan สามารถรันได้เฉพาะใน job mode หรือ batch mode เท่านั้น
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import (
QiskitRuntimeService, Session, EstimatorV2 as Estimator
)
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(
another_isa_circuit.layout
)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
estimator = Estimator()
estimator.options.resilience_level = 1
job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(
another_isa_circuit, another_isa_observable
)])
result = job.result()
another_result = another_job.result()
# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
# second job
print(f" > Another Expectation value: "
f"{another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Estimator, Options
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
options = Options()
options.optimization_level = 2
options.resilience_level = 2
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
estimator = Estimator(options=options)
job = estimator.run(isa_circuit, isa_observable)
another_job = estimator.run(another_isa_circuit, another_isa_observable)
result = job.result()
another_result = another_job.result()
# first job
print(f" > Expectation values job 1: {result.values}")
# second job
print(f" > Expectation values job 2: {another_result.values}")
ขั้นตอนการย้ายไปใช้ Sampler V2
- แทนที่
from qiskit_ibm_runtime import Samplerด้วยfrom qiskit_ibm_runtime import SamplerV2 as Sampler - ลบคำสั่ง
from qiskit_ibm_runtime import Optionsออกทั้งหมด เนื่องจาก V2 primitives ไม่ใช้คลาสOptionsแล้ว แต่สามารถส่ง options เป็น dictionary ตอน initialize คลาสSamplerV2ได้ (เช่นsampler = Sampler(backend, options={"default_shots": 1024})) หรือจะตั้งค่าหลังจาก initialize แล้วก็ได้:sampler = Sampler(backend)sampler.options.default_shots = 1024 - ตรวจสอบ options ที่รองรับ ทั้งหมดและอัปเดตตามความเหมาะสม
- จัดกลุ่ม Circuit แต่ละวงกับ observables และค่า parameter ที่ต้องการนำไปใช้ในรูปแบบ tuple (PUB) ตัวอย่างเช่น ใช้
(circuit1, parameter_set1)ถ้าต้องการรันcircuit1ด้วยparameter_set1สามารถระบุจำนวน shots สำหรับ PUB นั้นโดยเฉพาะได้ด้วย - อัปเดต method
run()ของ Sampler ให้รับ list ของ PUBs เช่นrun([(circuit1, parameter_set1)])สามารถระบุshotsตรงนี้ได้ด้วย ซึ่งจะใช้กับ PUBs ทุกตัว - ผลลัพธ์ของ Sampler V2 ถูกจัดกลุ่มตาม PUBs สามารถดูข้อมูล output ของแต่ละ PUB ได้โดย index เข้าไป ในขณะที่ Sampler V2 คืนค่า unweighted samples แต่คลาสผลลัพธ์มี convenience method สำหรับดู counts ได้ ตัวอย่างเช่น:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
ต้องใช้ชื่อ classical register เพื่อดึงผลลัพธ์ โดย default จะตั้งชื่อว่า meas เมื่อใช้ measure_all() ถ้าสร้าง classical register หนึ่งตัวหรือมากกว่าด้วยชื่อที่ไม่ใช่ default ให้ใช้ชื่อนั้นในการดึงผลลัพธ์ สามารถหาชื่อ classical register ได้โดยรัน <circuit_name>.cregs เช่น qc.cregs
ตัวอย่างเต็มรูปแบบของ Sampler
รันการทดลองเดี่ยว
ใช้ Sampler เพื่อดู counts หรือการกระจาย quasi-probability ของ Circuit เดียว
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()
sampler = Sampler(backend)
job = sampler.run(circuit)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
รันการทดลองหลายรายการในงานเดียว
ใช้ Sampler เพื่อดู counts หรือการกระจาย quasi-probability ของ Circuit หลายวงในงานเดียว
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)
sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()
for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()
sampler = Sampler(backend)
job = sampler.run(circuits)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
รัน Circuit แบบ parameterized
รันการทดลองหลายรายการในงานเดียว โดยใช้ค่า parameter เพื่อเพิ่มความสามารถในการนำ Circuit กลับมาใช้ซ้ำ
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()
# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=num_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
# Step 3: Execute using Qiskit primitives.
from qiskit_ibm_runtime import SamplerV2 as Sampler
sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: "
f"{pub_result.data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
num_qubits = 5
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()
# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=num_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
# Step 3: Execute using Qiskit primitives.
from qiskit_ibm_runtime import Sampler
sampler = Sampler(backend)
job = sampler.run([isa_circuit] * 3, parameter_values)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
ใช้ Sessions และ advanced options
สำรวจ Sessions และ advanced options เพื่อเพิ่มประสิทธิภาพการทำงานของ Circuit บน QPU
โค้ดบล็อกต่อไปนี้จะคืนค่า error สำหรับผู้ใช้ที่ใช้ Open Plan เนื่องจากใช้ sessions งานใน Open Plan สามารถรันได้เฉพาะใน job mode หรือ batch mode เท่านั้น
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import (
QiskitRuntimeService, SamplerV2 as Sampler, Session
)
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
service = QiskitRuntimeService()
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()
# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")
# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session, Options
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()
options = Options()
options.optimization_level = 2
options.resilience_level = 0
service = QiskitRuntimeService()
backend = service.least_busy(operational=True,
simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
sampler = Sampler(options=options)
job = sampler.run(circuit)
another_job = sampler.run(another_circuit)
result = job.result()
another_result = another_job.result()
# first job
print(f" > Quasi-probability distribution job 1: {result.quasi_dists}")
# second job
print(f" > Quasi-probability distribution job 2: {another_result.quasi_dists}")
ขั้นตอนถัดไป
- เรียนรู้เพิ่มเติมเกี่ยวกับการตั้งค่า options ในคู่มือ Sampler options, Estimator options และ Executor options
- เรียนรู้รายละเอียดเพิ่มเติมเกี่ยวกับ Primitive inputs and outputs
- ทดลองกับบทเรียน CHSH inequality