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

เริ่มต้นใช้งาน OBP

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

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

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-addon-utils~=0.3.0
qiskit-addon-obp~=0.3.0

เมื่อเตรียม quantum workload ด้วย operator backpropagation (OBP) ขั้นแรกต้องเลือก "circuit slices" และขั้นที่สองควรระบุ truncation threshold หรือ "error budget" เพื่อลบ terms ที่มี coefficients ขนาดเล็กใน operator ที่ backpropagate แล้ว รวมถึงตั้งขอบเขตบนสำหรับขนาดรวมของ operator ที่ backpropagate แล้ว ในระหว่าง backpropagation จำนวน terms ใน operator ของ Circuit NN-Qubit จะเข้าหา 4N4^N ได้อย่างรวดเร็วในกรณีที่แย่ที่สุด คู่มือนี้แสดงขั้นตอนในการใช้ OBP กับ quantum workload

ส่วนประกอบหลักของแพ็กเกจ qiskit-addons-obp คือฟังก์ชัน backpropagate() ฟังก์ชันนี้รับ arguments สำหรับ final observable ที่ต้องการสร้างใหม่ ชุดของ circuit slices สำหรับคำนวณแบบ classical และแบบ optional คือ TruncationErrorBudget หรือ OperatorBudget เพื่อกำหนดเงื่อนไขของการ truncation เมื่อระบุแล้ว operator ที่ backpropagate แบบ classical OO' จะถูกคำนวณแบบวนซ้ำโดยการใช้ Gate จากแต่ละ slice ss ด้วยวิธีต่อไปนี้:

O(s)=USs+1O(s1)USs+1O'^{(s)} = \mathcal{U}_{S-s+1}^\dagger O'^{(s-1)} \mathcal{U}_{S-s+1}

โดยที่ SS คือจำนวน slices ทั้งหมด และ Us\mathcal{U}_{s} แทน slice เดียวของ Circuit ตัวอย่างนี้ใช้แพ็กเกจ qiskit-addons-utils เพื่อเตรียม circuit slices และสร้าง Circuit ตัวอย่าง

เพื่อเริ่มต้น ให้พิจารณาการวิวัฒนาการตามเวลาของ Heisenberg XYZ chain Hamiltonian นี้มีรูปแบบ

H^=(j,k)(JxXjXk+JyYjYk+JzZjZk)+j(hxXj+hyYj+hzZj) \hat{H} = \sum_{(j,k)} \left( J_xX_jX_k + J_yY_jY_k + J_z Z_jZ_k \right) + \sum_{j} \left(h_xX_j + h_yY_j + h_zZ_j\right)

และค่า expectation value ที่จะวัดคือ Z0\langle Z_0 \rangle

โค้ดด้านล่างสร้าง Hamiltonian ในรูปแบบของ SparsePauliOp โดยใช้ module qiskit_addons_utils.problem_generators และ CouplingMap ตั้งค่า coupling constants เป็น Jx=π/8J_x=\pi/8, Jy=π/4J_y=\pi/4, Jz=π/2J_z=\pi/2 และ external magnetic fields เป็น hx=π/3h_x=\pi/3, hy=π/6h_y=\pi/6, hz=π/9h_z=\pi/9 จากนั้นสร้าง Circuit ที่จำลองการวิวัฒนาการตามเวลาของมัน

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-obp qiskit-addon-utils qiskit-ibm-runtime
import numpy as np
from qiskit.transpiler import CouplingMap
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2
from qiskit.primitives import StatevectorEstimator
from qiskit.quantum_info import SparsePauliOp
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import slice_by_gate_types
from qiskit_addon_obp.utils.simplify import OperatorBudget
from qiskit_addon_obp.utils.truncating import setup_budget
from qiskit_addon_obp import backpropagate
from qiskit_addon_utils.slicing import combine_slices

coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)

# Choose a 10-qubit linear chain on this coupling map
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 5, 12, 8, 18]
)

# Get a qubit operator describing the Heisenberg XYZ model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),
ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),
)

# we evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=2), time=0.2
)

circuit.draw("mpl")

Output of the previous code cell

เตรียม inputs สำหรับ backpropagate

ถัดมา สร้าง circuit slices สำหรับ backpropagation โดยทั่วไปแล้ว วิธีการแบ่ง slice อาจส่งผลต่อประสิทธิภาพของ backpropagation สำหรับปัญหาที่กำหนด ในที่นี้ จัดกลุ่ม Gate ประเภทเดียวกันไว้ใน slice โดยใช้ฟังก์ชัน qiskit_addons_utils.slice_by_gate_types

slices = slice_by_gate_types(circuit)
print(f"Separated the circuit into {len(slices)} slices.")
Separated the circuit into 18 slices.

เมื่อสร้าง slices แล้ว ให้ระบุ OperatorBudget เพื่อกำหนดเงื่อนไขหยุด backpropagate operator และป้องกัน classical overhead ไม่ให้เพิ่มขึ้นอีก คุณยังสามารถระบุ truncation error budget สำหรับแต่ละ slice ซึ่ง Pauli terms ที่มี coefficients ขนาดเล็กจะถูก truncate ออกจากแต่ละ slice จนกว่า error budget จะเต็ม budget ที่เหลือจะถูกเพิ่มไปยัง budget ของ slice ถัดไป

ในที่นี้ ระบุว่า backpropagation ควรหยุดเมื่อจำนวนกลุ่ม qubit-wise commuting Pauli ใน operator เพิ่มเกิน 88 และจัดสรร error budget 0.0050.005 สำหรับแต่ละ slice

op_budget = OperatorBudget(max_qwc_groups=8)
truncation_error_budget = setup_budget(max_error_per_slice=0.005)

Backpropagate slices

ในขั้นตอนนี้คุณจะกำหนด final observable ที่จะวัดและรัน backpropagation บนแต่ละ slice ฟังก์ชัน backpropagate() ส่งคืนผลลัพธ์สามอย่าง ได้แก่ observable ที่ backpropagate แล้ว circuit slices ที่เหลือที่ยังไม่ได้ backpropagate (และควรรันบน quantum hardware) และ metadata เกี่ยวกับ backpropagation

สังเกตว่าทั้ง OperatorBudget และ TruncationErrorBudget เป็น parameters แบบ optional สำหรับเมธอด backpropagate() โดยทั่วไปแล้ว การเลือกที่ดีที่สุดสำหรับทั้งสองควรเลือกแบบ heuristic และต้องการการทดลองในระดับหนึ่ง ในตัวอย่างนี้เราจะ backpropagate ทั้งแบบมีและไม่มี TruncationErrorBudget

หมายเหตุ

โดย default แล้ว backpropagate() ใช้ L1L_1 norm ของ coefficients ที่ถูก truncate เพื่อกำหนดขอบเขต error รวมจากการ truncation แต่สามารถใช้ LpL_p อื่นได้หากต้องการปรับวิธีการคำนวณ truncation error

# Specify a single-qubit observable
observable = SparsePauliOp("IIIIIIIIIZ")

# Backpropagate without the truncation error budget
backpropagated_observable, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=op_budget,
)

# Recombine the slices remaining after backpropagation
bp_circuit = combine_slices(remaining_slices, include_barriers=True)

print(f"Backpropagated {metadata.num_backpropagated_slices} slices.")
print(
f"New observable has {len(backpropagated_observable.paulis)} terms, which can be combined into "
f"{len(backpropagated_observable.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata.accumulated_error(0):.3e}"
)
print(
f"Note that backpropagating one more slice would result in {metadata.backpropagation_history[-1].num_paulis[0]} terms "
f"across {metadata.backpropagation_history[-1].num_qwc_groups} groups."
)
Backpropagated 7 slices.
New observable has 18 terms, which can be combined into 8 groups.
After truncation, the error in our observable is bounded by 0.000e+00
Note that backpropagating one more slice would result in 27 terms across 12 groups.
print(
"The remaining circuit after backpropagation without truncation looks as follows:"
)
bp_circuit.draw("mpl", scale=0.6)
The remaining circuit after backpropagation without truncation looks as follows:

Output of the previous code cell

โค้ดด้านล่าง backpropagate Circuit พร้อม truncation error budget

# Backpropagate *with* the truncation error budget
backpropagated_observable_trunc, remaining_slices_trunc, metadata_trunc = (
backpropagate(
observable,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
)

# Recombine the slices remaining after backpropagation
bp_circuit_trunc = combine_slices(
remaining_slices_trunc, include_barriers=True
)

print(f"Backpropagated {metadata_trunc.num_backpropagated_slices} slices.")
print(
f"New observable has {len(backpropagated_observable_trunc.paulis)} terms, which can be combined into "
f"{len(backpropagated_observable_trunc.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata_trunc.accumulated_error(0):.3e}"
)
print(
f"Note that backpropagating one more slice would result in {metadata_trunc.backpropagation_history[-1].num_paulis[0]} terms "
f"across {metadata_trunc.backpropagation_history[-1].num_qwc_groups} groups."
)
Backpropagated 10 slices.
New observable has 19 terms, which can be combined into 8 groups.
After truncation, the error in our observable is bounded by 4.933e-02
Note that backpropagating one more slice would result in 27 terms across 13 groups.
print(
"The remaining circuit after backpropagation with truncation looks as follows:"
)
bp_circuit_trunc.draw("mpl", scale=0.6)
The remaining circuit after backpropagation with truncation looks as follows:

Output of the previous code cell

Transpile และรัน quantum workload

เมื่อ backpropagate operator แล้ว คุณสามารถรันส่วนที่เหลือของ Circuit บน QPU ได้ quantum workload โดยใช้ Estimator ควรรวม Circuit bp_circuit_trunc และต้องวัด operator ที่ backpropagate แล้ว backpropagated_observable

เพื่อแสดงประสิทธิภาพของ OBP เพียงอย่างเดียว โค้ดด้านล่าง transpile ทั้ง Circuit ต้นฉบับและ Circuit ที่ backpropagate แล้ว (แบบมีและไม่มี truncation) และจำลอง Circuit แบบ classical โดยใช้ StatevectorEstimator

# Specify a backend and a pass manager for transpilation
backend = FakeMelbourneV2()
# pm = generate_preset_pass_manager(backend=backend, optimization_level=1)

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

# Transpile original experiment
circuit_isa = pm.run(circuit)
observable_isa = observable.apply_layout(circuit_isa.layout)

# Transpile backpropagated experiment without truncation
bp_circuit_isa = pm.run(bp_circuit)
bp_obs_isa = backpropagated_observable.apply_layout(bp_circuit_isa.layout)

# Transpile the backpropagated experiment with truncated observable terms
bp_circuit_trunc_isa = pm.run(bp_circuit_trunc)
bp_obs_trunc_isa = backpropagated_observable_trunc.apply_layout(
bp_circuit_trunc_isa.layout
)

estimator = StatevectorEstimator()

# Run the experiments using the exact statevector estimator
result_exact = (
estimator.run([(circuit, observable)]).result()[0].data.evs.item()
)

result_bp = (
estimator.run([(bp_circuit_isa, bp_obs_isa)]).result()[0].data.evs.item()
)
result_bp_trunc = (
estimator.run([(bp_circuit_trunc_isa, bp_obs_trunc_isa)])
.result()[0]
.data.evs.item()
)

print(f"Exact expectation value: {result_exact}")
print(f"Backpropagated expectation value without truncation: {result_bp}")
print(f"Backpropagated expectation value with truncation: {result_bp_trunc}")
print(
f" - Expected Error for truncated observable: {metadata_trunc.accumulated_error(0):.3e}"
)
print(
f" - Observed Error for truncated observable: {abs(result_exact - result_bp_trunc):.3e}"
)
Exact expectation value: 0.8854160687717517
Backpropagated expectation value without truncation: 0.8854160687717533
Backpropagated expectation value with truncation: 0.8850236647156081
- Expected Error for truncated observable: 4.933e-02
- Observed Error for truncated observable: 3.924e-04

สุดท้าย โค้ดด้านล่างจะ transpile และรัน Circuit ที่ backpropagate แล้วบน QPU (ทั้งแบบมีและไม่มี truncation)

# Specify a backend and a pass manager for transpilation
service = QiskitRuntimeService()
backend = service.least_busy()
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)

# Transpile backpropagated experiment without truncation
bp_circuit_isa = pm.run(bp_circuit)
bp_obs_isa = backpropagated_observable.apply_layout(bp_circuit_isa.layout)

# Transpile the backpropagated experiment with truncated observable terms
bp_circuit_trunc_isa = pm.run(bp_circuit_trunc)
bp_obs_trunc_isa = backpropagated_observable_trunc.apply_layout(
bp_circuit_trunc_isa.layout
)

# Run the experiments using Estimator primitive
estimator = EstimatorV2(mode=backend)

result_bp_qpu = (
estimator.run([(bp_circuit_isa, bp_obs_isa)]).result()[0].data.evs.item()
)

result_bp_trunc_qpu = (
estimator.run([(bp_circuit_trunc_isa, bp_obs_trunc_isa)])
.result()[0]
.data.evs.item()
)

print(f"Exact expectation value: {result_exact}")
print(f"Backpropagated expectation value without truncation: {result_bp_qpu}")
print(
f"Backpropagated expectation value with truncation: {result_bp_trunc_qpu}"
)
print(
f" - Observed Error for observable without truncation: {abs(result_exact - result_bp_qpu):.3e}"
)
print(
f" - Observed Error for truncated observable: {abs(result_exact - result_bp_trunc_qpu):.3e}"
)
Exact expectation value: 0.8854160687717517
Backpropagated expectation value without truncation: 0.8790435084647706
Backpropagated expectation value with truncation: 0.8759838342768448
- Observed Error for observable without truncation: 6.373e-03
- Observed Error for truncated observable: 9.432e-03

ขั้นตอนต่อไป

คำแนะนำ
Source: IBM Quantum docs — updated 13 ก.พ. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569