Circuit cutting เพื่อลดความลึกของ Circuit
ประมาณการใช้งาน: แปดนาทีบนโปรเซสเซอร์ Eagle (หมายเหตุ: นี่เป็นเพียงการประมาณเท่านั้น เวลาจริงอาจแตกต่างกัน)
พื้นหลัง
บทช่วยสอนนี้จะแสดงวิธีสร้าง Qiskit pattern สำหรับการตัด Gate ใน quantum circuit เพื่อลดความลึกของ Circuit สำหรับการพูดคุยเชิงลึกเพิ่มเติมเกี่ยวกับ circuit cutting สามารถเข้าชมได้ที่ เอกสาร circuit cutting Qiskit addon
ข้อกำหนดเบื้องต้น
ก่อนเริ่มบทช่วยสอนนี้ ให้ตรวจสอบว่าได้ติดตั้งสิ่งต่อไปนี้แล้ว:
- Qiskit SDK v2.0 ขึ้นไป พร้อมรองรับ visualization
- Qiskit Runtime v0.22 ขึ้นไป (
pip install qiskit-ibm-runtime) - Circuit cutting Qiskit addon v0.9.0 ขึ้นไป (
pip install qiskit-addon-cutting)
การตั้งค่า
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-ibm-runtime
import numpy as np
from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
ขั้นตอนที่ 1: แมปข้อมูล input แบบคลาสสิกไปสู่ปัญหา quantum
เราจะนำ Qiskit pattern ไปใช้โดยผ่านสี่ขั้นตอนที่ระบุไว้ใน เอกสาร ในกรณีนี้ เราจะจำลอง expectation value บน Circuit ที่มีความลึกหนึ่งๆ โดยการตัด Gate ที่ได้ผลลัพธ์เป็น swap gate และรัน subexperiment บน Circuit ที่ตื้นกว่า การตัด Gate มีความเกี่ยวข้องกับขั้นตอนที่ 2 (ปรับแต่ง Circuit สำหรับการรัน quantum โดยการแยกย่อย Gate ที่ห่างไกล) และขั้นตอนที่ 4 (การประมวลผลหลังเพื่อสร้าง expectation value บน Circuit ต้นฉบับขึ้นใหม่) ในขั้นตอนแรก เราจะสร้าง Circuit จาก Qiskit circuit library และกำหนด observable บางส่วน
- Input: พารามิเตอร์แบบคลาสสิกสำหรับกำหนด Circuit
- Output: Circuit abstract และ observable
circuit = EfficientSU2(num_qubits=4, entanglement="circular").decompose()
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
observables = PauliList(["ZZII", "IZZI", "IIZZ", "XIXI", "ZIZZ", "IXIX"])
circuit.draw("mpl", scale=0.8, style="iqp")
ขั้นตอนที่ 2: ปรับแต่งปัญหาสำหรับการรันบน quantum hardware
- Input: Circuit abstract และ observable
- Output: Target circuit และ observable ที่ผ่านการตัด Gate ที่ห่างไกลเพื่อลดความลึกของ Circuit หลัง transpile
เราเลือก initial layout ที่ต้องการ swap สองครั้งในการรัน Gate ระหว่าง Qubit 3 และ 0 และอีกสอง swap เพื่อนำ Qubit กลับสู่ตำแหน่งเดิม เราเลือก optimization_level=3 ซึ่งเป็นระดับการปรับแต่งสูงสุดที่มีให้ใช้งานกับ preset pass manager
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=circuit.num_qubits, simulator=False
)
pm = generate_preset_pass_manager(
optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend
)
transpiled_qc = pm.run(circuit)

print(f"Transpiled circuit depth: {transpiled_qc.depth()}")
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, style="iqp", fold=-1)
Transpiled circuit depth: 103
ค้นหาและตัด Gate ที่ห่างไกล: เราจะแทนที่ Gate ที่ห่างไกล (Gate ที่เชื่อมต่อ Qubit ที่ไม่ใช่ local ได้แก่ 0 และ 3) ด้วย object TwoQubitQPDGate โดยระบุ index ของ Gate เหล่านั้น cut_gates จะแทนที่ Gate ในตำแหน่ง index ที่กำหนดด้วย object TwoQubitQPDGate และยังส่งคืนรายการของ instance QPDBasis หนึ่งรายการสำหรับการแยกย่อย Gate แต่ละตัว object QPDBasis มีข้อมูลเกี่ยวกับวิธีการแยกย่อย Gate ที่ถูกตัดออกเป็น operation แบบ single-qubit
# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(circuit.data)
if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]
# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)
qpd_circuit.draw("mpl", scale=0.8)
สร้าง subexperiment สำหรับรันบน backend: generate_cutting_experiments รับ Circuit ที่มี instance TwoQubitQPDGate และ observable ในรูปแบบ PauliList
เพื่อจำลอง expectation value ของ Circuit ขนาดเต็ม จะมีการสร้าง subexperiment หลายรายการจาก joint quasiprobability distribution ของ Gate ที่ถูกแยกย่อย แล้วรันบน backend หนึ่งตัวหรือมากกว่า จำนวน sample ที่ดึงจาก distribution นั้นถูกควบคุมด้วย num_samples โดยจะมี coefficient รวมหนึ่งค่าสำหรับแต่ละ sample ที่ไม่ซ้ำกัน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการคำนวณ coefficient ให้ดูที่ เนื้อหาอธิบาย
# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observables, num_samples=np.inf
)
เพื่อเปรียบเทียบ เราจะเห็นว่า QPD subexperiment จะตื้นกว่าหลังจากตัด Gate ที่ห่างไกล: นี่คือตัวอย่างของ subexperiment ที่เลือกมาแบบสุ่มจา ก QPD circuit ความลึกลดลงมากกว่าครึ่ง ต้องสร้างและประเมิน subexperiment แบบ probabilistic จำนวนมากเหล่านี้เพื่อสร้าง expectation value ของ Circuit ที่ลึกกว่าขึ้นใหม่
# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pm.run(subexperiments[100])
print(f"Original circuit depth after transpile: {transpiled_qc.depth()}")
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}"
)
transpiled_qpd_circuit.draw(
"mpl", scale=0.6, style="iqp", idle_wires=False, fold=-1
)
Original circuit depth after transpile: 103
QPD subexperiment depth after transpile: 46
ในทางกลับกัน การตัดส่งผลให้ต้องการ sampling เพิ่มเติม. ที่นี่เราตัด CNOT gate สามตัว ส่งผลให้มี sampling overhead เป็น สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ sampling overhead ที่เกิดจาก circuit cutting ให้ดูที่ เอกสาร Circuit Knitting Toolbox
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0
ขั้นตอนที่ 3: รันโดยใช้ Qiskit primitives
รัน target circuit ("subexperiment") ด้วย Sampler Primitive
- Input: Target circuit
- Output: Quasi-probability distribution
# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)
# Set up the Qiskit Runtime Sampler primitive. For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)
# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()
print(job.job_id())
czypg1r6rr3g008mgp6g