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

การตัดสายสัญญาณสำหรับการประมาณค่าความคาดหวัง

ประมาณการใช้งาน: 22 วินาทีบนโปรเซสเซอร์ Heron (หมายเหตุ: นี่เป็นเพียงการประมาณการเท่านั้น เวลาจริงอาจแตกต่างออกไป)

ผลการเรียนรู้

หลังจากผ่าน tutorial นี้ ผู้ใช้ควรเข้าใจ:

  • วิธีใช้ qiskit-addon-cutting เพื่อแบ่ง Circuit ขนาดใหญ่ออกเป็น subcircuit ขนาดเล็ก ซึ่งช่วยลดผลกระทบจากสัญญาณรบกวน

ข้อกำหนดเบื้องต้น

เราแนะนำให้ผู้ใช้คุ้นเคยกับหัวข้อต่อไปนี้ก่อนผ่าน tutorial นี้:

  • การใช้ Sampler primitive ซึ่งใช้ใน workflow นี้

พื้นหลัง

Circuit-knitting เป็นคำรวมที่ครอบคลุมวิธีการต่างๆ ในการแบ่ง Circuit ออกเป็น subcircuit ขนาดเล็กหลายๆ ชิ้น ซึ่งประกอบด้วย Gate หรือ Qubit น้อยลง แต่ละ subcircuit สามารถรันได้อิสระ และผลลัพธ์สุดท้ายได้จากการประมวลผลแบบคลาสสิกผ่านผลลัพธ์ของแต่ละ subcircuit เทคนิคนี้เข้าถึงได้ผ่าน Circuit cutting Qiskit addon ดูที่ เอกสารประกอบ พร้อมด้วย เนื้อหาเบื้องต้นอื่นๆ สำหรับคำอธิบายอย่างละเอียดของเทคนิคนี้

tutorial นี้เน้นที่วิธีที่เรียกว่า wire cutting ซึ่ง Circuit ถูกแบ่งตามสายสัญญาณ [1], [2] สังเกตว่าในวงจรคลาสสิก การแบ่งนั้นง่ายเพราะผลลัพธ์ที่จุดแบ่งสามารถกำหนดได้แน่นอน คือ 0 หรือ 1 อย่างไรก็ตาม สถานะของ Qubit ที่จุดตัดนั้น โดยทั่วไปจะเป็น mixed state ดังนั้นแต่ละ subcircuit จึงต้องวัดหลายครั้งใน basis ที่แตกต่างกัน (โดยทั่วไปจะเป็น basis ที่ครบถ้วนสำหรับ tomography เช่น Pauli basis [3], [4]) และเตรียมใน eigenstate ที่สอดคล้องกัน ภาพด้านล่าง (ที่มา: [7]) แสดงตัวอย่างของ wire cutting สำหรับ GHZ state ขนาดสี่ Qubit ออกเป็นสาม subcircuit ที่นี่ MjM_j แทนชุดของ basis (โดยทั่วไปคือ Pauli X, Y และ Z) และ PiP_i แทนชุดของ eigenstate (โดยทั่วไปคือ 0|0\rangle, 1|1\rangle, +|+\rangle และ +i|+i\rangle)

wc-1.png wc-2.png

เนื่องจากแต่ละ subcircuit มี Qubit และ Gate น้อยกว่า จึงคาดว่าจะได้รับผลกระทบจากสัญญาณรบกวนน้อยลง tutorial นี้แสดงตัวอย่างที่วิธีนี้สามารถใช้เพื่อลดสัญญาณรบกวนในระบบได้อย่างมีประสิทธิภาพ

สิ่งที่ต้องการ

ก่อนเริ่มบทเรียนนี้ ต้องติดตั้งสิ่งต่อไปนี้:

  • Qiskit SDK v2.0 หรือใหม่กว่า พร้อมรองรับ visualization
  • Qiskit Runtime v0.22 หรือใหม่กว่า ( pip install qiskit-ibm-runtime )
  • Circuit cutting Qiskit addon v0.10.0 หรือใหม่กว่า (pip install qiskit-addon-cutting)
  • Qiskit addon utils 0.3 หรือใหม่กว่า (pip install qiskit-addon-utils)
  • Qiskit Aer (pip install qiskit-aer )

ตั้งค่า

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
import matplotlib.pyplot as plt

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.quantum_info import PauliList, SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator
from qiskit.result import sampled_expectation_value

from qiskit_addon_cutting.instructions import CutWire
from qiskit_addon_cutting import (
cut_wires,
expand_observables,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, Batch

ตัวอย่างขนาดเล็กด้วย simulator

tutorial นี้ใช้ Qiskit pattern เพื่อจำลอง Circuit Many-Body Localization (MBL) แบบหนึ่งมิติ (1D) วงจร MBL เป็นวงจรที่มีประสิทธิภาพสำหรับฮาร์ดแวร์และมีพารามิเตอร์สองตัวคือ θ\theta และ ϕ\vec{\phi} เมื่อกำหนด θ\theta เป็น 00 และเตรียมสถานะเริ่มต้นใน 0|0\rangle สำหรับ Qubit ทั้งหมด ค่าความคาดหวังในอุดมคติของ Zi\langle Z_i \rangle จะเท่ากับ +1+1 สำหรับทุก Qubit ตำแหน่ง ii โดยไม่ขึ้นกับค่าของ ϕ\vec{\phi} รายละเอียดเพิ่มเติมเกี่ยวกับ Circuit นี้มีอยู่ใน บทความ นี้

โปรดทราบว่าใน simulator ที่ไม่มีสัญญาณรบกวน ค่าความคาดหวังที่ได้จากการใช้และไม่ใช้ circuit cutting จะเท่ากัน

ขั้นตอนที่ 1: แมปอินพุตแบบคลาสสิกไปยังปัญหาควอนตัม

สร้าง Circuit MBL แบบ 1D

ก่อนอื่น เราแสดงฟังก์ชันสำหรับสร้าง Circuit MBL แบบ 1D

class MBLChainCircuit(QuantumCircuit):
def __init__(
self, num_qubits: int, depth: int, use_cut: bool = False
) -> None:
super().__init__(
num_qubits, name=f"MBLChainCircuit<{num_qubits}, {depth}>"
)
evolution = MBLChainEvolution(num_qubits, depth, use_cut)
self.compose(evolution, inplace=True)

class MBLChainEvolution(QuantumCircuit):
def __init__(self, num_qubits: int, depth: int, use_cut) -> None:
super().__init__(
num_qubits, name=f"MBLChainEvolution<{num_qubits}, {depth}>"
)

theta = Parameter("θ")
phis = ParameterVector("φ", num_qubits)

for layer in range(depth):
layer_parity = layer % 2
# print("layer parity", layer_parity)
for qubit in range(layer_parity, num_qubits - 1, 2):
# print(qubit)
self.cz(qubit, qubit + 1)
self.u(theta, 0, np.pi, qubit)
self.u(theta, 0, np.pi, qubit + 1)
if (
use_cut
and layer_parity == 0
and (
qubit == num_qubits // 2 - 1
or qubit == num_qubits // 2
)
):
self.append(CutWire(), [num_qubits // 2])
if use_cut and layer < depth - 1 and layer_parity == 1:
if qubit == num_qubits // 2:
self.append(CutWire(), [qubit])
for qubit in range(num_qubits):
self.p(phis[qubit], qubit)
num_qubits = 10
depth = 2
mbl = MBLChainCircuit(num_qubits, depth)
mbl.draw("mpl", fold=-1)

Output of the previous code cell

เราคำนวณค่าความคาดหวังเฉลี่ย O=1niZiO = \frac{1}{n} \sum_i Z_i สำหรับ Qubit ทั้งหมดสำหรับ θ=0\theta = 0 เนื่องจากค่าความคาดหวังในอุดมคติของ Zi=1\langle Z_i \rangle = 1 \forall ii ค่าความคาดหวังในอุดมคติของ OO ก็เป็น 11 เช่นกัน พารามิเตอร์ ϕ\phi ถูกเลือกแบบสุ่ม

np.random.seed(42)
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

Circuit ต้องถูกระบุตำแหน่งโดยแทรก CutWire ในตำแหน่งที่ต้องการเพื่อแบ่งพาร์ติชัน สำหรับ tutorial นี้ เราเลือกการแบ่งที่เท่ากัน Circuit MBL ถูกออกแบบมาเพื่อให้การตั้งค่า use_cut=True ในฟังก์ชันแทรกการระบุตำแหน่งอย่างถูกต้องหลังจาก n2\frac{n}{2} Qubit โดยที่ nn คือจำนวน Qubit ในวงจรดั้งเดิม เรายังกำหนดพารามิเตอร์ที่สร้างแบบสุ่มให้กับ Circuit

mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_cut.draw("mpl", fold=-1)

Output of the previous code cell

ขั้นตอนที่ 2: ปรับ Circuit ให้เหมาะสมสำหรับการรันบนฮาร์ดแวร์ควอนตัม

ตัด Circuit ออกเป็น subcircuit ขนาดเล็ก

ตอนนี้เราแบ่ง Circuit ออกเป็น subcircuit ขนาดเล็กสองส่วนโดยใช้ qiskit-addon-cutting qiskit-addon-cutting เพิ่ม gate Move เสมือนเพื่อแยกตำแหน่ง wire cut โดยปรับจำนวน Qubit ให้เหมาะสม ตอนนี้เราสร้าง Circuit ด้วย gate เสมือนนี้ เนื่องจากมี wire cut หนึ่งจุด จำนวน Qubit ที่เกี่ยวข้องจะเพิ่มขึ้น 1

mbl_move = cut_wires(mbl_cut)
mbl_move.draw("mpl", fold=-1)

Output of the previous code cell

สร้างและขยาย observable

observable ตามที่กำหนดไว้ก่อนหน้า จะเป็นค่าเฉลี่ยของ ZZ บนแต่ละ Qubit อย่างไรก็ตาม หลังจากแทรก gate Move เสมือน จำนวน Qubit ที่มีผลในวงจรจะเพิ่มขึ้น observable จึงต้องขยายตามไปด้วยเพื่อรองรับการเปลี่ยนแปลงจำนวน Qubit นี้ โปรดทราบว่า observable จะกระทำอย่างง่าย (เหมือน II) บน Qubit เพิ่มเติมที่เพิ่มมาสำหรับ gate Move เสมือน

observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
observable
PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',
'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',
'IIIIIIIIZI', 'IIIIIIIIIZ'])
new_obs = expand_observables(observable, mbl, mbl_move)
new_obs
PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',
'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',
'IIIIIIIIIZI', 'IIIIIIIIIIZ'])

ตอนนี้ Circuit สามารถแบ่งพาร์ติชันตาม gate Move และเราได้ subcircuit รวมถึง subobservable ซึ่งเป็นส่วนของ observable เดิมที่เกี่ยวข้องกับแต่ละ subcircuit

partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

ที่นี่เราแสดง subcircuit ทั้งสอง:

subcircuits[0].draw("mpl", fold=-1)

Output of the previous code cell

subcircuits[1].draw("mpl", fold=-1)

Output of the previous code cell

การขยาย observable โดยใช้การดำเนินการ Move ต้องใช้โครงสร้างข้อมูล PauliList ในการ reconstruct ค่าความคาดหวังของ Circuit เดิม เราต้องการ observable ในรูปแบบ SparsePauliOp

M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

ดังที่กล่าวไว้ก่อนหน้า สำหรับแต่ละ cut Circuit ด้านต้นน้ำต้องวัดใน Pauli basis และ Circuit ด้านปลายน้ำต้องเตรียมใน eigenstate ของ basis นั้น ฟังก์ชัน generate_cutting_experiments สร้าง Circuit ที่จำเป็นทั้งหมดและ coefficient ที่เกี่ยวข้องกับแต่ละ Circuit ที่ต้องใช้ในการ reconstruct ดูรายละเอียดเพิ่มเติมใน บทความนี้

subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)

Transpile Circuit ลงบน backend

สำหรับตัวอย่างแรกที่เกี่ยวข้องกับการจำลองเท่านั้น เรา transpile Circuit ไปยัง basis gate set ของ backend:

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
)

print(backend)
<IBMBackend('ibm_fez')>

ขั้นตอนที่ 3: รันโดยใช้ Qiskit primitives

ตอนนี้รัน subexperiment แต่ละชุด:

pm_basis = generate_preset_pass_manager(
optimization_level=2, basis_gates=backend.configuration().basis_gates
)
basis_subexperiments = {
label: pm_basis.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
sampler = SamplerV2(mode=AerSimulator())
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in basis_subexperiments.items()
}

ขั้นตอนที่ 4: ประมวลผลหลังการรันและส่งคืนผลลัพธ์ในรูปแบบคลาสสิกที่ต้องการ

ตอนนี้เราดึงผลลัพธ์ของการรัน subexperiment แต่ละชุดและ reconstruct ค่าความคาดหวังของ Circuit ที่ไม่ได้ตัด:

# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
reconstructed_expval
np.float64(0.9953821063041687)
methods = [
"Uncut",
"Wire cut",
]
values = [
1,
reconstructed_expval,
] # since the ideal expectation value in noiseless simulation is +1

ax = plt.gca()
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
ax.set_ylabel(r"$M_Z$", fontsize=12)
Text(0, 0.5, '$M_Z$')

Output of the previous code cell

ตัวอย่างขนาดใหญ่บนฮาร์ดแวร์

ตอนนี้เราสาธิต wire cutting สำหรับ Circuit MBL ขนาด 60 Qubit ทั้ง Circuit ที่ไม่ถูกตัดและ Circuit ที่ถูกตัดจะรันบนฮาร์ดแวร์ IBM Quantum®:

num_qubits = 60
depth = 2

# construct the circuit
mbl = MBLChainCircuit(num_qubits, depth)

# create parameters
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

# construct the cut circuit
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_move = cut_wires(mbl_cut)

# Define observable and expand to account for the wire cut
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
new_obs = expand_observables(observable, mbl, mbl_move)

# Construct a SparsePauliOp version of the observable for later use in reconstruction
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

# Partition the circuit and get subcircuits and subobservables
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

# Obtain subexperiments and coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)

# Transpile the subexperiments to the backend
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
isa_subexperiments = {
label: pm.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

# Execute the subexperiments and retrieve results
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
sampler.options.environment.job_tags = ["TUT_WC"]
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
results = {label: job.result() for label, job in jobs.items()}

# Reconstruct the expectation value of the original observable
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real

# Compute the uncut circuit to obtain the noisy expectation value for comparison
sampler = SamplerV2(mode=backend)
sampler.options.environment.job_tags = ["TUT_WC"]

if mbl.num_clbits == 0:
mbl.measure_all()
isa_mbl = pm.run(mbl)

pub = (isa_mbl, params)
uncut_job = sampler.run([pub])

uncut_counts = uncut_job.result()[0].data.meas.get_counts()
uncut_expval = sampled_expectation_value(uncut_counts, M_z)

# visualize the results
ax = plt.gca()
methods = ["uncut", "cut"]
values = [uncut_expval, reconstructed_expval]

plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
plt.axhline(y=1, color="k", linestyle="--")
plt.text(0.3, 0.95, "Exact result")
plt.show()

Output of the previous code cell

uncut_expval
0.9202473958333336

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

Recommendations

ถ้าพบว่าเนื้อหานี้น่าสนใจ คุณอาจสนใจเนื้อหาต่อไปนี้:

References

[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). Simulating large quantum circuits on a small quantum computer. Physical review letters, 125(15), 150504.

[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., & Martonosi, M. (2021, April). Cutqc: using small quantum computers for large quantum circuit evaluations. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).

[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Quantum circuit cutting with maximum-likelihood tomography. npj Quantum Information, 7(1), 64.

[4] Majumdar, R., & Wood, C. J. (2022). Error mitigated quantum circuit cutting. arXiv preprint arXiv:2211.13431.

[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Parallelizing Quantum-Classical Workloads: Profiling the Impact of Splitting Techniques. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.

[6] Bhoumik, D., Majumdar, R., Saha, A., & Sur-Kolay, S. (2023). Distributed Scheduling of Quantum Circuits with Noise and Time Optimization. arXiv preprint arXiv:2309.06005.

[7] Majumdar, R. (2024). Efficient Reduction of Resources and Noise in Discrete Quantum Computing Circuits (Doctoral dissertation, Indian Statistical Institute - Kolkata). https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar&cbl=2026366&diss=y