เริ่มต้นอย่างรวดเร็ว
คู่มือนี้สาธิตตัวอย่างการทำงานขั้นต่ำของแพ็กเกจ qiskit-addon-obp เราใช้ operator backpropagation (OBP) เพื่อลดความลึกของวงจรควอนตัมโดยดูดซับ Gate ท้ายวงจรเข้าไปใน observable
ค่าคาดหมาย จะไม่เปลี่ยนแปลง หากเราแยกบล็อก Gate ท้ายวงจรออกจากวงจร แล้วทำ conjugate ให้ observable ด้วยบล็อกนั้นแบบคลาสสิก OBP ใช้วิธีนี้ซ้ำ ๆ โดยประเมินวงจรบางส่วนแบบคลาสสิก เพื่อให้มีเพียงวงจรที่ตื้นกว่าเท่านั้นที่รันบนฮาร์ดแวร์ ราคาที่ต้องจ่ายคือ Gate ที่ถูกดูดซับแต่ละตัวอาจขยาย observable ให้มีเทอม Pauli มากขึ้น ดังนั้นต้องชั่งน้ำหนักระหว่างความลึกที่ลดลงกับการเติบโตของ observable
หากต้องการดูตัวอย่างการสร้าง workflow ที่สมจริงด้วยเครื่องมือนี้และรันบนฮาร์ดแวร์ควอนตัม ดูบทเรียนบน IBM Quantum Platform (บทเรียน OBP)
เตรียมอินพุตสำหรับ OBP
OBP รับรายการของ circuit slice และ observable เป็นอินพุต โดยจะ backpropagate slice ทีละอันจากท้ายวงจรเข้าไปใน observable ลดความลึกของวงจรโดยแลกกับเทอม Pauli ที่เพิ่มขึ้นใน observable ที่นี่เราสร้างวงจรการวิวัฒนาการตามเวลาสำหรับโมเดล Heisenberg 10 qubit และแบ่งเป็น slice ตามชนิดของ Gate
ด้านล่างเราวาดวงจรต้นฉบับ จากนั้นวาดวงจรเดียวกันที่รวมกลับพร้อม barrier ที่ทำเครื่องหมายขอบเขตของ slice — แต่ละ slice เป็นหน่วยที่สามารถดูดซับเข้าไปใน observable ได้ในขั้นตอน backpropagation เดียว
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
# Generate a circuit to reduce
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 5, 12, 8, 18]
)
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),
)
circuit = generate_time_evolution_circuit(
hamiltonian,
time=0.2,
synthesis=LieTrotter(reps=2),
)
# Slice the circuit and define an observable
slices = slice_by_gate_types(circuit)
observable = SparsePauliOp("IIIIIIIIIZ")
print(f"Original circuit depth: {circuit.depth()}")
print(f"Number of slices: {len(slices)}")
print(f"Observable terms: {len(observable)}")
Original circuit depth: 18
Number of slices: 18
Observable terms: 1
# Recombine the slices with barriers to make the slice boundaries visible
sliced_circuit = combine_slices(slices, include_barriers=True)
print("Original circuit:")
display(circuit.draw("mpl", scale=0.6, fold=-1))
print("Sliced circuit (recombined with barriers for visualization)")
sliced_circuit.draw("mpl", scale=0.6, fold=-1)
Original circuit:

Sliced circuit (recombined with barriers for visualization)

ลดความลึกของวงจรด้วย OBP
เราเรียก backpropagate เพื่อดูดซับ slice เข้าไปใน observable ฟังก์ชันจะคืนค่า observable ที่ขยายแล้ว, circuit slice ที่ไม่ได้ถูก propagate ผ่าน และ metadata เกี่ยวกับขั้นตอนการทำงาน
หากไม่มีการควบคุม observable อาจเติบโตไปสู่เทอม Pauli จำนวน operator_budget จำกัดการเติบโตนี้ — ที่นี่เราอนุญาตกลุ่ม qubit-wise commuting สูงสุด 8 กลุ่ม ซึ่งกำหนดจำนวน shot ที่ต้องใช้ในการประเมิน observable บน QPU โดยประมาณ Backpropagation จะหยุดทันทีที่การดูดซับ slice ถัดไปจะเกินงบประมาณ ซึ่งเป็นสิ่งที่เกิดขึ้นด้านล่างพอดี: มีเพียง 7 จาก 18 slice ที่ถูกดูดซับก่อนที่ observable จะเต็มทั้ง 8 กลุ่ม commuting และขั้นตอนหยุดลง
เพื่อเจาะลึกเข้าไปในวงจรมากขึ้น สามารถใช้ keyword argument truncation_error_budget ของ backpropagate เพื่อทิ้งเทอม Pauli ที่มีสัมประสิทธิ์เล็กออกจาก observable ขณะที่มันเติบโต วิธีนี้ลดการเติบโตของ observable โดยแลกกับความคลาดเคลื่อนที่เป็นสัดส่วนกับขนาดของเทอม Pauli ที่ถูกตัดทอน งบประมาณทั้งสองเสริมกันและใช้ร่วมกันได้: operator_budget ควบคุมขนาดของ observable ส่วน truncation_error_budget ควบคุมความคลาดเคลื่อนที่เกิดจากการทิ้งเทอม
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
max_qwc_groups = 8
bp_obs, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),
)
reduced_circuit = combine_slices(remaining_slices)
num_groups = len(bp_obs.group_commuting(qubit_wise=True))
print(
f"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices."
)
print(
f"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})"
)
print(f"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.")
print(
f"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget."
)
Backpropagated 7 of 18 slices.
Reduced circuit depth: 11 (was 18)
Observable grew from 1 to 18 Pauli terms.
Filled 8 of 8 commuting groups, exhausting the budget.
reduced_circuit.draw("mpl", scale=0.6, fold=-1)