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

ตัวอย่าง Executor

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

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

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
samplomatic~=0.18.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.77
Qubit 1 -> 0.76
Qubit 2 -> 0.66
Qubit 3 -> 0.71
Qubit 4 -> 0.69
Qubit 5 -> 0.67
Qubit 6 -> 0.62
Qubit 7 -> 0.59
Qubit 8 -> 0.62
Qubit 9 -> 0.68
# 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 -> 0.99
Qubit 2 -> 0.96
Qubit 3 -> 0.98
Qubit 4 -> 0.98
Qubit 5 -> 0.98
Qubit 6 -> 0.98
Qubit 7 -> 0.95
Qubit 8 -> 0.95
Qubit 9 -> 0.94

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

คำแนะนำ