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

ตัวอย่าง Executor

เวอร์ชันแพ็คเกจ

โค้ดในหน้านี้ได้รับการพัฒนาโดยใช้ข้อกำหนดต่อไปนี้ แนะนำให้ใช้เวอร์ชันเหล่านี้หรือใหม่กว่า

qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.21.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic

ตัวอย่างในส่วนนี้แสดงวิธีการใช้ Executor primitive ที่พบบ่อย ก่อนรันตัวอย่างเหล่านี้ ให้ทำตามคำแนะนำใน ติดตั้ง Qiskit และ Executor quickstart

ก่อนเริ่ม​

ตัวอย่างโค้ดบางส่วนในหน้านี้ใช้ samplex ซึ่งเป็นส่วนหนึ่งของแพ็คเกจ Samplomatic ดังนั้น ก่อนรันโค้ดบล็อกเหล่านั้น คุณต้องติดตั้ง Samplomatic ตามที่แสดงในโค้ดบล็อกต่อไปนี้ สำหรับข้อมูลเพิ่มเติม ดู เอกสาร Samplomatic

pip install samplomatic

# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]

ตัวอย่าง: Circuit แบบ parameterized​

ตัวอย่างนี้แสดงวิธีเพิ่ม circuit items ที่มีพารามิเตอร์ รวมถึงวิธีเพิ่ม samplex items ประกอบด้วยขั้นตอนเหล่านี้:

  1. ตั้งค่า circuit: สร้างและ transpile target circuit
  2. เตรียม samplex: จัดกลุ่ม gate และการวัดลงใน annotated boxes และสร้างคู่ circuit template กับ samplex
  3. รัน: เพิ่ม circuit item และ samplex item ลงใน QuantumProgram และรันทั้งสองในงานเดียว

ตั้งค่า circuit​

เตรียม 3-qubit GHZ state หมุน qubit รอบแกน Pauli-Z และวัด qubit ในฐาน computational

from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()

ระบุ backend และ transpile circuit ให้ใช้เฉพาะคำสั่งที่ QPU รองรับ (เรียกว่า instruction set architecture (ISA) circuit)

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)

เตรียม samplex​

ใช้ฟังก์ชันอำนวยความสะดวก generate_boxing_pass_manager และพารามิเตอร์ twirling เพื่อจัดกลุ่ม two-qubit gates และการวัดลงใน boxes และใส่ twirling annotations

boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)

boxed_circuit = boxing_pm.run(isa_circuit)

ใช้เมธอด build เพื่อสร้าง template circuit และ samplex

# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)

รัน circuits​

Executor รันออบเจ็กต์ QuantumProgram แต่ละ QuantumProgram สามารถมีหลาย items ตัวอย่างนี้เพิ่ม circuit item และ samplex item สำหรับการรัน สำหรับรายละเอียดเต็ม ดู Executor input and output

ขั้นตอนแรกคือ initialize โปรแกรมว่าง โดยขอ 1024 shots สำหรับแต่ละ configuration ของแต่ละ item

# Generate a quantum program
program = QuantumProgram(shots=1024)

เพิ่ม circuit item ลงใน QuantumProgram circuit item นี้ประกอบด้วยสองส่วน - ISA circuit และ 10 ชุดของค่าพารามิเตอร์

# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)

เพิ่ม samplex item ลงใน QuantumProgram ด้วย arguments เหล่านี้:

  • template circuit และ samplex ที่สร้างโดยฟังก์ชัน build
  • 10 ชุดของค่าพารามิเตอร์สำหรับ circuit ดั้งเดิม
  • จำนวน randomizations ที่จะดำเนินการ
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)

รัน Executor job​

# initialize an Executor with default options
executor = Executor(mode=backend)

# Submit the job
job = executor.run(program)

# Retrieve the result
result = job.result()

ดึงผลลัพธ์สำหรับแต่ละ task

# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]

# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]

ตัวอย่าง: การดำเนินการ PEC​

ตัวอย่างนี้แสดงวิธีใช้ samplex item เพื่อดำเนินการ probabilistic error cancellation (PEC) สำหรับการ mitigate errors

พิจารณา mirror version ของ circuit ที่มี 10 qubits และ unique layers ของ CX gates สองชั้น งานหลักคือ:

pipeline ประกอบด้วยขั้นตอนเหล่านี้:

  1. ตั้งค่า: สร้าง target circuit และจัดกลุ่ม operations ลงใน boxes
  2. เรียนรู้: เรียนรู้ noise ของคำสั่งที่ต้องการ mitigate ด้วย PEC
  3. รัน: รัน circuit บน backend
  4. วิเคราะห์: ประมวลผลหลังและวิเคราะห์ผลลัพธ์

สำหรับการเปรียบเทียบ เราจะรัน mirror circuit นี้สองครั้ง ครั้งแรกใช้เฉพาะ Pauli-twirling และครั้งที่สองใช้ PEC mitigation

หมายเหตุ

การใช้งานสำหรับตัวอย่างนี้ประมาณ 10 นาทีบน Heron r2 processor

ตั้งค่า circuit​

เลือก backend และเตรียม 10-qubit circuit

from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Prepare a circuit

num_qubits = 10
num_layers = 10

qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)

for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])

circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)

circuit.draw("mpl", scale=0.35, fold=100)

Output of the previous code cell

รวม circuit กับ inverse เพื่อสร้าง mirror circuit

mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()

mirror_circuit.draw("mpl", scale=0.35, fold=100)

Output of the previous code cell

ตั้งค่าพารามิเตอร์บางอย่าง:

import numpy as np

parameter_values = np.random.rand(mirror_circuit.num_parameters)

ใช้ pass manager เพื่อ transpile circuit เป็น ISA circuit

preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)

isa_circuit = preset_pass_manager.run(mirror_circuit)

ต่อไป จัดกลุ่ม gates และการวัดลงใน annotated boxes สามารถทำด้วยตนเองหรือใช้ฟังก์ชัน generate_boxing_pass_manager จาก Samplomatic เพื่อความสะดวก circuit แรกจะมีเพียง twirling เท่านั้นจึงต้องการเฉพาะ annotation Twirl circuit ที่สองจะรันด้วย full PEC mitigation และต้องการทั้ง Twirl และ InjectNoise annotations

# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)

mirror_circuit_twirl = boxing_pm.run(isa_circuit)

# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)

mirror_circuit_pec = boxing_pm.run(isa_circuit)

เรียนรู้ noise​

เพื่อลดจำนวนการทดลอง noise learning ให้น้อยที่สุด ระบุ unique instructions ใน circuit ที่สอง (ที่มี boxes annotated ด้วย InjectNoise) ในการกำหนด uniqueness box instructions สองตัวเท่ากันหากทั้งสองข้อต่อไปนี้เป็นจริง:

  • เนื้อหาเท่ากัน ยกเว้น single-qubit gates
  • Twirl annotation เท่ากัน (annotations อื่นทั้งหมดถูกละเว้น)

ซึ่งนำไปสู่ unique instructions สามตัว ได้แก่ odd และ even gate boxes และ final measurement box

from samplomatic.utils import find_unique_box_instructions

unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3

Initialize NoiseLearnerV3 เลือกพารามิเตอร์การเรียนรู้โดยตั้งค่า options และรัน noise learning job

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3

learner = NoiseLearnerV3(backend)

learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]

learner_job = learner.run(unique_box_instructions)

learner_job.job_id()
learner_result = learner_job.result()

แปลง result เป็นออบเจ็กต์ที่ samplex ต้องการโดยใช้เมธอด result.to_dict

noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

รัน circuits​

Executor รันออบเจ็กต์ QuantumProgram แต่ละ QuantumProgram สามารถมีหลาย items ที่ถูกเพิ่มเข้าโปรแกรม แต่ละ item คืองานที่โปรแกรมจะดำเนินการ

Initialize โปรแกรมว่าง โดยขอ 1000 shots สำหรับแต่ละ configuration ของแต่ละ item

from qiskit_ibm_runtime.quantum_program import QuantumProgram

# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)

ต่อไป build template circuit และ samplex สำหรับ mirror_circuit_twirl และเพิ่มลงในโปรแกรม ขอ 900 randomizations จาก samplex ด้วย ซึ่งหมายความว่า samplex จะสร้าง 900 ชุดของพารามิเตอร์ และแต่ละชุดจะถูกรัน 1000 ครั้ง (จำนวน shots) บน QPU

นี่คืองานแรกของโปรแกรม (ผลลัพธ์ 0)

template_twirl, samplex_twirl = build(mirror_circuit_twirl)

program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)

ในทำนองเดียวกัน เพิ่ม template circuit และ samplex ที่สร้างสำหรับ mirror_circuit_pec โดยขอ 900 randomizations นี่คืองานที่สองของโปรแกรม (ผลลัพธ์ 1)

template_pec, samplex_pec = build(mirror_circuit_pec)

program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)

Import Executor และส่ง job

from qiskit_ibm_runtime.executor import Executor

executor = Executor(backend)
executor_job = executor.run(program)

executor_job.job_id()

executor_results = executor_job.result()
executor_results

twirl_result = executor_results[0]

print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")

pec_result = executor_results[1]

print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")
Twirl result keys:
['meas', 'measurement_flips.meas']

Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']

Shape of results: (900, 1000, 10)

วิเคราะห์ผลลัพธ์​

สุดท้าย ประมวลผลหลังผลลัพธ์เพื่อประมาณค่าคาดหวังของ single-qubit Pauli-Z operators ที่กระทำบน qubit ทั้งสิบตัวที่ active (ค่าคาดหวัง: 1.0)

# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)

# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)

for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.71
Qubit 1 -> 0.72
Qubit 2 -> 0.7
Qubit 3 -> 0.68
Qubit 4 -> 0.65
Qubit 5 -> 0.64
Qubit 6 -> 0.62
Qubit 7 -> 0.66
Qubit 8 -> 0.69
Qubit 9 -> 0.75
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)

# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))

# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)

for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.98
Qubit 1 -> 1.0
Qubit 2 -> 0.99
Qubit 3 -> 0.97
Qubit 4 -> 0.97
Qubit 5 -> 0.97
Qubit 6 -> 0.96
Qubit 7 -> 0.97
Qubit 8 -> 0.97
Qubit 9 -> 0.98

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

คำแนะนำ