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

การคอมไพล์วงจรควอนตัมแบบประมาณสำหรับการวิวัฒน์ตามเวลา

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

ภูมิหลัง

บทช่วยสอนนี้สาธิตวิธีการใช้งาน Approximate Quantum Compilation โดยใช้ tensor network (AQC-Tensor) กับ Qiskit เพื่อเพิ่มประสิทธิภาพของวงจรควอนตัม เราใช้ AQC-Tensor ในบริบทของ Trotterized time evolution เพื่อลดความลึกของวงจรในขณะที่รักษาความแม่นยำของการจำลอง ตามกรอบงาน Qiskit สำหรับการเตรียมสถานะและการปรับปรุงค่า คุณจะเรียนรู้วิธีสร้างวงจร ansatz ที่มีความลึกต่ำจากวงจร Trotter เริ่มต้น ปรับปรุงด้วย tensor network และเตรียมสำหรับการรันบนฮาร์ดแวร์ควอนตัม

เป้าหมายหลักคือการจำลอง time evolution สำหรับ model Hamiltonian ที่มีความลึกของวงจรลดลง ซึ่งทำได้โดยใช้ AQC-Tensor Qiskit addon ที่ชื่อ qiskit-addon-aqc-tensor ซึ่งใช้ประโยชน์จาก tensor network โดยเฉพาะ matrix product states (MPS) เพื่อบีบอัดและปรับปรุงวงจรเริ่มต้น ผ่านการปรับค่าซ้ำๆ วงจร ansatz ที่บีบอัดแล้วจะรักษา fidelity กับวงจรต้นฉบับไว้ในขณะที่ยังคงทำได้บนฮาร์ดแวร์ควอนตัมในยุคใกล้ ดูรายละเอียดเพิ่มเติมได้ใน docs ที่เกี่ยวข้อง พร้อมด้วย ตัวอย่างง่ายๆ สำหรับการเริ่มต้น

Approximate Quantum Compilation มีประโยชน์อย่างยิ่งในการจำลองควอนตัมที่เกินเวลา coherence ของฮาร์ดแวร์ เนื่องจากช่วยให้สามารถทำการจำลองที่ซับซ้อนได้อย่างมีประสิทธิภาพมากขึ้น บทช่วยสอนนี้จะนำทางคุณผ่านการตั้งค่าขั้นตอนการทำงาน AQC-Tensor ใน Qiskit ครอบคลุมการเริ่มต้น Hamiltonian การสร้างวงจร Trotter และการ transpile ของวงจรที่ปรับปรุงแล้วสำหรับอุปกรณ์เป้าหมาย

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

ก่อนเริ่มบทช่วยสอนนี้ ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งสิ่งต่อไปนี้:

  • Qiskit SDK v1.0 หรือใหม่กว่า พร้อมรองรับ visualization
  • Qiskit Runtime v0.22 หรือใหม่กว่า (pip install qiskit-ibm-runtime)
  • AQC-Tensor Qiskit addon (pip install 'qiskit-addon-aqc-tensor[aer,quimb-jax]')

การตั้งค่า

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-ibm-runtime quimb rustworkx scipy
import numpy as np
import quimb.tensor
import datetime
import matplotlib.pyplot as plt

from scipy.optimize import OptimizeResult, minimize

from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit.synthesis import SuzukiTrotter

from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor.ansatz_generation import (
generate_ansatz_from_circuit,
)
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

from rustworkx.visualization import graphviz_draw
# L is the number of sites, also the length of the 1D spin chain
L = 10

# Generate the coupling map
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

# Generate a ZZ observable between the two middle qubits
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

print("Hamiltonian:", hamiltonian)
print("Observable:", observable)
graphviz_draw(coupling_map.graph, method="circo")

ส่วนที่ 1 ตัวอย่างขนาดเล็ก

ส่วนแรกของบทช่วยสอนนี้ใช้ตัวอย่างขนาดเล็กที่มี 10 ไซต์เพื่อแสดงกระบวนการแมปปัญหาการจำลองควอนตัมไปยังวงจรควอนตัมที่สามารถรันได้ ที่นี่เราจะสำรวจพลวัตของโมเดล XXZ ขนาด 10 ไซต์ ซึ่งช่วยให้เราสามารถสร้างและปรับปรุงวงจรควอนตัมที่จัดการได้ก่อนที่จะขยายไปยังระบบที่ใหญ่ขึ้น

โมเดล XXZ ได้รับการศึกษาอย่างแพร่หลายในฟิสิกส์สำหรับการตรวจสอบปฏิสัมพันธ์ spin และคุณสมบัติทางแม่เหล็ก เราตั้งค่า Hamiltonian ให้มีเงื่อนไขขอบเขตแบบเปิดพร้อมปฏิสัมพันธ์ที่ขึ้นกับไซต์ระหว่างไซต์ที่อยู่ติดกันตามห่วงโซ่

Model Hamiltonian และ observable

Hamiltonian สำหรับโมเดล XXZ ขนาด 10 ไซต์ของเรานิยามเป็น:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

โดยที่ Ji,(i+1)J_{i,(i+1)} คือสัมประสิทธิ์แบบสุ่มที่สอดคล้องกับเส้น (i,i+1)(i, i+1) และ L=10L=10 คือจำนวนไซต์

ด้วยการจำลองวิวัฒนาการของระบบนี้ที่มีความลึกของวงจรลดลง เราสามารถหาข้อมูลเชิงลึกเกี่ยวกับการใช้ AQC-Tensor เพื่อบีบอัดและปรับปรุงวงจร

ตั้งค่า Hamiltonian และ observable

ก่อนที่เราจะแมปปัญหาของเรา เราต้องตั้งค่า coupling map, Hamiltonian, และ observable สำหรับโมเดล XXZ ขนาด 10 ไซต์

Hamiltonian: SparsePauliOp(['IIIIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII', 'IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII'],
coeffs=[1. +0.j, 0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j,
0.60759468+0.j, 0.60759468+0.j, 1.21518937+0.j, 0.55138169+0.j,
0.55138169+0.j, 1.10276338+0.j, 0.52244159+0.j, 0.52244159+0.j,
1.04488318+0.j, 0.4618274 +0.j, 0.4618274 +0.j, 0.9236548 +0.j,
0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j, 0.46879361+0.j,
0.46879361+0.j, 0.93758721+0.j, 0.6958865 +0.j, 0.6958865 +0.j,
1.391773 +0.j, 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j])
Observable: SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])
# Generate an initial state
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

Output of the previous code cell

เมื่อนิยาม Hamiltonian แล้ว เราสามารถดำเนินการสร้างสถานะเริ่มต้นได้

# Generate the AQC target circuit (initial segment)
aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

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

ตอนนี้ที่เราได้สร้าง Hamiltonian แล้ว ซึ่งนิยามปฏิสัมพันธ์ spin-spin และสนามแม่เหล็กภายนอกที่เป็นลักษณะเฉพาะของระบบ เราทำตามสามขั้นตอนหลักในขั้นตอนการทำงาน AQC-Tensor:

  1. สร้างวงจร AQC ที่ปรับปรุงแล้ว: โดยใช้ Trotterization เราประมาณการวิวัฒนาการเริ่มต้น ซึ่งจากนั้นถูกบีบอัดเพื่อลดความลึกของวงจร
  2. สร้างวงจร time evolution ที่เหลือ: จับการวิวัฒนาการสำหรับเวลาที่เหลือหลังจากส่วนแรก
  3. รวมวงจร: รวมวงจร AQC ที่ปรับปรุงแล้วกับวงจรวิวัฒนาการที่เหลือเป็นวงจร time-evolution ที่สมบูรณ์พร้อมสำหรับการรัน

วิธีการนี้สร้าง ansatz ที่มีความลึกต่ำสำหรับการวิวัฒนาการเป้าหมาย ซึ่งสนับสนุนการจำลองที่มีประสิทธิภาพภายในข้อจำกัดของฮาร์ดแวร์ควอนตัมในยุคใกล้

กำหนดส่วนของ time evolution ที่จะจำลองแบบคลาสสิก

เป้าหมายของเราคือการจำลอง time evolution ของ model Hamiltonian ที่นิยามก่อนหน้านี้โดยใช้ Trotter evolution เพื่อทำให้กระบวนการนี้มีประสิทธิภาพสำหรับฮาร์ดแวร์ควอนตัม เราแบ่งการวิวัฒนาการออกเป็นสองส่วน:

  • ส่วนแรก: ส่วนเริ่มต้นของการวิวัฒนาการนี้ ตั้งแต่ ti=0.0t_i = 0.0 ถึง tf=0.2t_f = 0.2 สามารถจำลองได้ด้วย MPS และสามารถ "คอมไพล์" ได้อย่างมีประสิทธิภาพโดยใช้ AQC-Tensor โดยใช้ AQC-Tensor Qiskit addon เราสร้างวงจรที่บีบอัดสำหรับส่วนนี้ ซึ่งเรียกว่า aqc_target_circuit เนื่องจากส่วนนี้จะถูกจำลองบน tensor-network simulator เราจึงสามารถใช้จำนวน Trotter layer ที่สูงกว่าโดยไม่กระทบต่อทรัพยากรฮาร์ดแวร์อย่างมีนัยสำคัญ เราตั้งค่า aqc_target_num_trotter_steps = 32 สำหรับส่วนนี้

  • ส่วนถัดไป: ส่วนที่เหลือของการวิวัฒนาการนี้ ตั้งแต่ t=0.2t = 0.2 ถึง t=0.4t = 0.4 จะถูกรันบนฮาร์ดแวร์ควอนตัม ซึ่งเรียกว่า subsequent_circuit เนื่องจากข้อจำกัดของฮาร์ดแวร์ เราต้องการใช้ Trotter layer น้อยที่สุดเท่าที่เป็นไปได้เพื่อรักษาความลึกของวงจรให้จัดการได้ สำหรับส่วนนี้เราใช้ subsequent_num_trotter_steps = 3

เลือกเวลาที่แบ่ง

เราเลือก t=0.2t = 0.2 เป็นเวลาที่แบ่งเพื่อสร้างสมดุลระหว่างความสามารถในการจำลองแบบคลาสสิกกับความเป็นไปได้ของฮาร์ดแวร์ ในช่วงเริ่มต้นของการวิวัฒนาการ entanglement ในโมเดล XXZ ยังคงต่ำพอที่วิธีคลาสสิกอย่าง MPS จะสามารถประมาณได้อย่างแม่นยำ

เมื่อเลือกเวลาที่แบ่ง แนวทางที่ดีคือการเลือกจุดที่ entanglement ยังจัดการได้แบบคลาสสิกแต่จับการวิวัฒนาการได้เพียงพอที่จะทำให้ส่วนที่รันบนฮาร์ดแวร์ง่ายขึ้น อาจต้องลองผิดลองถูกเพื่อหาสมดุลที่ดีที่สุดสำหรับ Hamiltonian ที่แตกต่างกัน

# Generate the subsequent circuit
subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
subsequent_circuit.draw("mpl", fold=-1)
# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)

Output of the previous code cell

เพื่อให้การเปรียบเทียบมีความหมาย เราจะสร้างวงจรเพิ่มเติมอีกสองวงจร:

  • วงจรเปรียบเทียบ AQC: วงจรนี้วิวัฒน์ไปถึง aqc_evolution_time แต่ใช้ระยะเวลา Trotter step เดียวกันกับ subsequent_circuit มันทำหน้าที่เป็นการเปรียบเทียบกับ aqc_target_circuit แสดงการวิวัฒนาการที่เราจะสังเกตเห็นโดยไม่ใช้จำนวน Trotter step ที่เพิ่มขึ้น เราจะเรียกวงจรนี้ว่า aqc_comparison_circuit

  • วงจรอ้างอิง: วงจรนี้ใช้เป็นเส้นฐานเพื่อให้ได้ผลลัพธ์ที่แม่นยำ มันจำลองการวิวัฒนาการแบบเต็มโดยใช้ tensor network เพื่อคำนวณผลลัพธ์ที่แม่นยำ ซึ่งให้ข้อมูลอ้างอิงสำหรับการประเมินประสิทธิภาพของ AQC-Tensor เราจะเรียกวงจรนี้ว่า reference_circuit

Number of Trotter steps for comparison: 3
# Generate the reference circuit
evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)
aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
aqc_ansatz.draw("mpl", fold=-1)

สร้าง ansatz และพารามิเตอร์เริ่มต้นจากวงจร Trotter ที่มีขั้นตอนน้อยกว่า

ตอนนี้ที่เราได้สร้างวงจรทั้งสี่แล้ว มาดำเนินการกับขั้นตอนการทำงาน AQC-Tensor กันต่อ ก่อนอื่น เราสร้างวงจร "good" ที่มีเวลาวิวัฒนาการเดียวกันกับวงจรเป้าหมาย แต่มี Trotter step น้อยกว่า (และด้วยเหตุนี้จึงมี layer น้อยกว่า)

จากนั้นเราส่งวงจร "good" นี้ไปยังฟังก์ชัน generate_ansatz_from_circuit ของ AQC-Tensor ฟังก์ชันนี้วิเคราะห์การเชื่อมต่อ two-qubit ของวงจรและส่งคืนสองสิ่ง:

  1. วงจร ansatz ที่มีพารามิเตอร์ทั่วไป พร้อมการเชื่อมต่อ two-qubit เดียวกันกับวงจร input
  2. พารามิเตอร์ที่เมื่อใส่เข้าไปใน ansatz จะให้ผลลัพธ์เป็นวงจร input (good)

เร็วๆ นี้เราจะนำพารามิเตอร์เหล่านี้และปรับค่าซ้ำๆ เพื่อทำให้วงจร ansatz ใกล้เคียงกับ target MPS มากที่สุดเท่าที่เป็นไปได้

print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)

Output of the previous code cell

AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 156 parameters
simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

เลือกการตั้งค่าสำหรับการจำลอง tensor network

ที่นี่เราใช้ matrix-product state circuit simulator ของ Quimb พร้อมกับ jax เพื่อให้ gradient

aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()
print("Reference MPS maximum bond dimension:", reference_mps.psi.max_bond())

ต่อไป เราสร้างการแสดง MPS ของสถานะเป้าหมายที่จะถูกประมาณโดยใช้ AQC-Tensor การแสดงนี้ช่วยให้จัดการ entanglement ได้อย่างมีประสิทธิภาพ โดยให้คำอธิบายที่กระชับของสถานะควอนตัมสำหรับการปรับปรุงค่าต่อไป

Target MPS maximum bond dimension: 5
Reference MPS maximum bond dimension: 7
good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)

โปรดสังเกตว่าด้วยการเลือกจำนวน Trotter step ที่มากขึ้นสำหรับสถานะเป้าหมาย เราได้ลด Trotter error ของมันลงเมื่อเทียบกับวงจรเริ่มต้นอย่างมีประสิทธิภาพ เราสามารถประเมิน fidelity (ψ1ψ22|\langle \psi_1 | \psi_2 \rangle|^2) ระหว่างสถานะที่เตรียมโดยวงจรเริ่มต้นกับสถานะเป้าหมายเพื่อหาปริมาณความแตกต่างนี้

Starting fidelity: 0.9982464959067222
# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x

ปรับปรุงพารามิเตอร์ของ ansatz โดยใช้การคำนวณ MPS

ในขั้นตอนนี้ เราปรับปรุงพารามิเตอร์ ansatz โดยการย่อให้เล็กสุดของฟังก์ชันต้นทุนง่ายๆ MaximizeStateFidelity โดยใช้ตัวปรับ L-BFGS จาก SciPy เราเลือกเกณฑ์การหยุดสำหรับ fidelity ที่รับประกันว่ามันจะเกิน fidelity ของวงจรเริ่มต้นโดยไม่มี AQC-Tensor เมื่อถึงเกณฑ์นี้ วงจรที่บีบอัดแล้วจะแสดง Trotter error ที่ต่ำกว่าและความลึกที่ลดลงเมื่อเทียบกับวงจรต้นฉบับ ด้วยการใช้เวลา CPU เพิ่มเติม การปรับปรุงค่าต่อไปสามารถเพิ่ม fidelity ได้อีก

2025-04-14 11:46:52.174235 Intermediate result: Fidelity 0.99795851
2025-04-14 11:46:52.218249 Intermediate result: Fidelity 0.99822826
2025-04-14 11:46:52.280924 Intermediate result: Fidelity 0.99829675
2025-04-14 11:46:52.356214 Intermediate result: Fidelity 0.99832474
2025-04-14 11:46:52.411609 Intermediate result: Fidelity 0.99836131
2025-04-14 11:46:52.453747 Intermediate result: Fidelity 0.99839954
2025-04-14 11:46:52.496184 Intermediate result: Fidelity 0.99846517
2025-04-14 11:46:52.542046 Intermediate result: Fidelity 0.99865029
2025-04-14 11:46:52.583679 Intermediate result: Fidelity 0.99872332
2025-04-14 11:46:52.628732 Intermediate result: Fidelity 0.99892359
2025-04-14 11:46:52.690386 Intermediate result: Fidelity 0.99900640
2025-04-14 11:46:52.759398 Intermediate result: Fidelity 0.99907169
2025-04-14 11:46:52.819496 Intermediate result: Fidelity 0.99911423
2025-04-14 11:46:52.884505 Intermediate result: Fidelity 0.99918716
2025-04-14 11:46:52.947919 Intermediate result: Fidelity 0.99921278
2025-04-14 11:46:53.012808 Intermediate result: Fidelity 0.99924853
2025-04-14 11:46:53.083626 Intermediate result: Fidelity 0.99928797
2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028
2025-04-14 11:46:53.221371 Intermediate result: Fidelity 0.99935757
2025-04-14 11:46:53.286211 Intermediate result: Fidelity 0.99938140
2025-04-14 11:46:53.352391 Intermediate result: Fidelity 0.99940964
2025-04-14 11:46:53.420472 Intermediate result: Fidelity 0.99944051
2025-04-14 11:46:53.486279 Intermediate result: Fidelity 0.99946828
2025-04-14 11:46:53.552338 Intermediate result: Fidelity 0.99948723
2025-04-14 11:46:53.618688 Intermediate result: Fidelity 0.99951011
2025-04-14 11:46:53.690878 Intermediate result: Fidelity 0.99954718
2025-04-14 11:46:53.762725 Intermediate result: Fidelity 0.99956267
2025-04-14 11:46:53.829784 Intermediate result: Fidelity 0.99958949
2025-04-14 11:46:53.897477 Intermediate result: Fidelity 0.99960498
2025-04-14 11:46:53.954633 Intermediate result: Fidelity 0.99961308
2025-04-14 11:46:54.010125 Intermediate result: Fidelity 0.99962894
2025-04-14 11:46:54.064717 Intermediate result: Fidelity 0.99964121
2025-04-14 11:46:54.118892 Intermediate result: Fidelity 0.99964348
2025-04-14 11:46:54.183236 Intermediate result: Fidelity 0.99964860
2025-04-14 11:46:54.245521 Intermediate result: Fidelity 0.99965695
2025-04-14 11:46:54.305792 Intermediate result: Fidelity 0.99966398
2025-04-14 11:46:54.355819 Intermediate result: Fidelity 0.99967816
2025-04-14 11:46:54.409580 Intermediate result: Fidelity 0.99968293
2025-04-14 11:46:54.457979 Intermediate result: Fidelity 0.99968936
2025-04-14 11:46:54.505891 Intermediate result: Fidelity 0.99969223
2025-04-14 11:46:54.551084 Intermediate result: Fidelity 0.99970009
2025-04-14 11:46:54.601817 Intermediate result: Fidelity 0.99970724
2025-04-14 11:46:54.650097 Intermediate result: Fidelity 0.99970987
2025-04-14 11:46:54.714727 Intermediate result: Fidelity 0.99971237
2025-04-14 11:46:54.780052 Intermediate result: Fidelity 0.99971916
2025-04-14 11:46:54.871994 Intermediate result: Fidelity 0.99971940
2025-04-14 11:46:54.958244 Intermediate result: Fidelity 0.99972465
2025-04-14 11:46:55.011057 Intermediate result: Fidelity 0.99972763
2025-04-14 11:46:55.175339 Intermediate result: Fidelity 0.99972894
2025-04-14 11:46:56.688912 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]
print("Final parameters:", parameters)
Final parameters: [-7.853983035039254, 1.5707966468427772, 1.5707962768868613, -1.570798010835122, 1.570794480409574, 1.5707972214146968, -1.570796593027083, 1.5707968206822998, -1.5707959018046258, -1.5707991700969144, 1.5707965852600927, 4.712386891737442, -7.853980840717957, 1.5707967508132654, 1.5707943162503217, -1.5707955382023582, 1.5707958007156742, 1.570796096113293, -1.5707928509846847, 1.5707971042943747, -1.570797909276557, -1.5707941020637393, 1.5707980179540793, 4.712389823219363, -1.5707928752386107, 1.5707996426312891, -1.5707975640471001, -1.570794132802984, 1.5707944361599957, 4.712390747060803, 0.1048818190315936, 0.06686710468840577, -0.0668645844756557, -3.1415923537135466, 1.2374931269696063, 6.323169390432535e-07, 3.53229204771738e-08, 2.1091105688681484, 6.283186439944202, 0.12152258846156239, 0.07961752617254866, -0.07961775088604585, -1.6564278051174865e-06, 2.0771163596472384, 3.141592651630471, -6.283185775192653, 1.7691609006726954, 3.1415922910116216, 0.19837572065074083, 0.11114901449078964, -0.11115124544944892, -3.141591983034976, 0.8570788408766729, 4.201601390404146e-07, -3.141593736550978, 0.34652010942396333, 6.283186232785291, 0.13606356527241956, 0.03891676349289617, -0.03891524189533726, -1.5707965732853424, 1.5707968967088564, -0.3086133992238162, 1.5707957152428194, 1.5707968398959653, -0.32062737993080026, 0.11027416939993417, 0.0726167290795046, -0.07262020423334464, -2.3729431959735024e-06, 1.8204437429254703, 9.299060301196612e-07, -3.141592899563451, 2.103269568939461, 3.1415937539734626, 0.11536891854817125, 0.09099022308254198, -0.09098864958606581, -3.1415913307373127, 2.078429034357281, -1.509777998069368e-06, -3.1415922600663255, 1.5189162645358172, -3.1415878461323583, 0.09999070991480716, 0.04352011445148391, -0.04351849541849812, -1.570797642506462, 1.570795238023824, 0.8903442644396505, 1.5707962698006606, 1.5707946765132268, 0.9098791754570567, 0.10448284343424026, 0.07317037684936827, -0.07316718173961152, -3.141592682240966, 2.1665363080039612, -7.450882112394189e-07, -5.771181304929921e-07, 2.615334999517103, -3.1415914971653898, 0.1890887078648001, 0.13578163074571992, -0.13578078143610256, 7.156734195912883e-07, 1.7915385305413096, -5.188866034727312e-07, 1.2827742939197711e-06, 1.2348316581417487, 6.28318357406372, 0.08061187643781703, 0.03820789039271876, -0.03820731868804904, 1.5707964027727628, 1.570798734462218, 4.387336153720882, -1.570795722044763, 1.570798457375325, 4.450361734163248, 0.092360147257953, 0.06047700345049011, -0.06048592856713045, -3.141591214829027, 2.6593289993286047, -2.366937342261038e-07, 8.112162974032695e-08, 1.8907014631413432, 8.355881261853104e-07, 0.23303641819370874, 0.14331998953606456, -0.1433194488304741, -3.141591621822901, 0.7455776479558791, 3.1415914520163586, -3.1415933560496105, 0.7603938554148255, -1.6230983177616282e-06, 0.07186349688535713, 0.03197144517771341, -0.031971177878588546, -4.712389048748508, 1.5707948403165752, 1.2773619319829186, -1.5707990802172127, 1.5707957676951863, 1.289083769394045, 0.13644999397718796, 0.032761460443590046, -0.032762060585195645, -1.5707977610073176, 1.5707964181578042, -3.4826435600366983, -4.712389691708343, 1.570794277502252, 2.799088046133275]
aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_final_circuit.draw("mpl", fold=-1)

ณ จุดนี้ จำเป็นต้องหาพารามิเตอร์สุดท้ายสำหรับวงจร ansatz เท่านั้น จากนั้นเราสามารถรวมวงจร AQC ที่ปรับปรุงแล้วกับวงจรวิวัฒนาการที่เหลือเพื่อสร้างวงจร time-evolution ที่สมบูรณ์สำหรับการรันบนฮาร์ดแวร์ควอนตัม

aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.draw("mpl", fold=-1)

Output of the previous code cell

เราจำเป็นต้องรวม aqc_comparison_circuit กับวงจรวิวัฒนาการที่เหลือด้วย วงจรนี้จะถูกใช้เพื่อเปรียบเทียบประสิทธิภาพของวงจรที่ปรับปรุงด้วย AQC-Tensor กับวงจรต้นฉบับ

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

Output of the previous code cell

ขั้นตอนที่ 2: ปรับแต่งปัญหาสำหรับการรันบน quantum hardware

เลือก hardware ที่จะใช้ ในที่นี้เราจะใช้อุปกรณ์ IBM Quantum® ที่มีอยู่ซึ่งต้องมี Qubit อย่างน้อย 127 ตัว

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)

เราทำการ transpile PUBs (Circuit และ observables) ให้ตรงกับ ISA (Instruction Set Architecture) ของ Backend โดยการตั้งค่า optimization_level=3 Transpiler จะปรับแต่ง Circuit ให้เหมาะกับการวาง Qubit แบบสายโซ่หนึ่งมิติ ซึ่งช่วยลด noise ที่ส่งผลต่อความแม่นยำของ Circuit เมื่อแปลง Circuit ให้อยู่ในรูปแบบที่ compatible กับ Backend แล้ว เราจะใช้การแปลงที่สอดคล้องกันกับ observables เพื่อให้แน่ใจว่า observables สอดคล้องกับ layout ของ Qubit ที่ปรับเปลี่ยนไป

Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 111
isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)

ผลลัพธ์จาก code cell ก่อนหน้า

ทำการ transpile สำหรับ comparison circuit

Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 158
estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()

ผลลัพธ์จาก code cell ก่อนหน้า

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

ในขั้นตอนนี้ เราจะรัน Circuit ที่ transpile แล้วบน quantum hardware (หรือ simulated backend) โดยใช้คลาส EstimatorV2 จาก qiskit_ibm_runtime เราตั้งค่า Estimator เพื่อรัน Circuit และวัด observable ที่ระบุไว้ ผลลัพธ์ของ job จะให้ค่า expected outcome สำหรับ observable ซึ่งช่วยให้เราเข้าใจประสิทธิภาพของ Circuit บน hardware ที่เป้าหมาย

Job ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})
job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()

ทำการรันสำหรับ comparison circuit

Job Comparison ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})
# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)

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

ในกรณีนี้ไม่จำเป็นต้อง reconstruct ผลลัพธ์ เราสามารถตรวจสอบผลได้โดยตรงจากการเข้าถึงค่า expectation value จาก output ของการรัน

Exact:         	-0.5252
AQC: -0.4903, |∆| = 0.0349
AQC Comparison: 0.5424, |∆| = 1.0676
plt.style.use("seaborn-v0_8")

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)
plt.show()

Bar plot เพื่อเปรียบเทียบผลลัพธ์ของ AQC, comparison และ exact circuit

L = 50  # L = length of our 1D spin chain

# Generate the edge list for this spin-chain
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]

# Instantiate a CouplingMap object
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)

hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

# Generate an initial state
L = hamiltonian.num_qubits
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

ผลลัพธ์จาก code cell ก่อนหน้า

ส่วนที่ 2: ขยายขนาด

ขั้นตอนที่ 1: แปลง input คลาสสิกเป็นปัญหาควอนตัม

สำหรับปัญหาขนาดใหญ่กว่านี้ เราเริ่มต้นด้วยการสร้าง Hamiltonian สำหรับโมเดล XXZ แบบ 50 ตำแหน่ง โดยกำหนดปฏิสัมพันธ์ระหว่าง spin กับ spin และสนามแม่เหล็กภายนอกทั่วทุกตำแหน่ง จากนั้นทำตามสามขั้นตอนหลัก ดังนี้

  1. สร้าง Circuit AQC ที่ได้รับการปรับแต่ง: ใช้ Trotterization เพื่อประมาณการ evolution เบื้องต้น แล้วบีบอัด segment นี้เพื่อลด circuit depth
  2. สร้าง Circuit สำหรับ time evolution ที่เหลือ: บันทึก time evolution ที่เหลืออยู่นอกเหนือจาก segment แรก
  3. รวม Circuit เข้าด้วยกัน: ผสาน circuit AQC ที่ปรับแต่งแล้วกับ circuit evolution ที่เหลือ เพื่อสร้าง circuit time-evolution ที่สมบูรณ์พร้อมสำหรับการรัน

สร้าง AQC target circuit (segment เบื้องต้น)

aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

สร้าง circuit ถัดไป (segment ที่เหลือ)

subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)

สร้าง AQC comparison circuit (segment เบื้องต้น แต่ใช้จำนวน Trotter steps เท่ากับ circuit ถัดไป)

# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3

สร้าง reference circuit

evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)

สร้าง ansatz และพารามิเตอร์เริ่มต้นจาก Trotter circuit ที่มี steps น้อยกว่า

aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 816 parameters

กำหนดค่าสำหรับการจำลอง tensor network แล้วสร้าง matrix product state representation ของ target state เพื่อใช้ในการปรับแต่ง จากนั้นประเมิน fidelity ระหว่าง initial circuit กับ target state เพื่อวัดความแตกต่างของ Trotter error

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

# Build the matrix-product representation of the state to be approximated by AQC
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()

# Compute the starting fidelity
good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Target MPS maximum bond dimension: 5
Starting fidelity: 0.9926466919924161

เพื่อปรับแต่งพารามิเตอร์ของ ansatz เราใช้การ minimize ฟังก์ชัน cost แบบ MaximizeStateFidelity ด้วย L-BFGS optimizer จาก SciPy โดยกำหนดเงื่อนไขหยุดให้ผ่าน fidelity ของ initial circuit ที่ไม่ใช้ AQC-Tensor เพื่อให้ circuit ที่บีบอัดแล้วมีทั้ง Trotter error ที่ต่ำกว่าและ depth ที่ลดลง

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:48:28.705807 Intermediate result: Fidelity 0.99795851
2025-04-14 11:48:28.743265 Intermediate result: Fidelity 0.99822826
2025-04-14 11:48:28.776629 Intermediate result: Fidelity 0.99829675
2025-04-14 11:48:28.816153 Intermediate result: Fidelity 0.99832474
2025-04-14 11:48:28.856437 Intermediate result: Fidelity 0.99836131
2025-04-14 11:48:28.896432 Intermediate result: Fidelity 0.99839954
2025-04-14 11:48:28.936670 Intermediate result: Fidelity 0.99846517
2025-04-14 11:48:28.982069 Intermediate result: Fidelity 0.99865029
2025-04-14 11:48:29.026130 Intermediate result: Fidelity 0.99872332
2025-04-14 11:48:29.067426 Intermediate result: Fidelity 0.99892359
2025-04-14 11:48:29.110742 Intermediate result: Fidelity 0.99900640
2025-04-14 11:48:29.161362 Intermediate result: Fidelity 0.99907169
2025-04-14 11:48:29.207933 Intermediate result: Fidelity 0.99911423
2025-04-14 11:48:29.266772 Intermediate result: Fidelity 0.99918716
2025-04-14 11:48:29.331727 Intermediate result: Fidelity 0.99921278
2025-04-14 11:48:29.401694 Intermediate result: Fidelity 0.99924853
2025-04-14 11:48:29.467980 Intermediate result: Fidelity 0.99928797
2025-04-14 11:48:29.533281 Intermediate result: Fidelity 0.99933028
2025-04-14 11:48:29.600833 Intermediate result: Fidelity 0.99935757
2025-04-14 11:48:29.670816 Intermediate result: Fidelity 0.99938140
2025-04-14 11:48:29.736928 Intermediate result: Fidelity 0.99940964
2025-04-14 11:48:29.802931 Intermediate result: Fidelity 0.99944051
2025-04-14 11:48:29.869177 Intermediate result: Fidelity 0.99946828
2025-04-14 11:48:29.940156 Intermediate result: Fidelity 0.99948723
2025-04-14 11:48:30.005751 Intermediate result: Fidelity 0.99951011
2025-04-14 11:48:30.070853 Intermediate result: Fidelity 0.99954718
2025-04-14 11:48:30.139171 Intermediate result: Fidelity 0.99956267
2025-04-14 11:48:30.210506 Intermediate result: Fidelity 0.99958949
2025-04-14 11:48:30.279647 Intermediate result: Fidelity 0.99960498
2025-04-14 11:48:30.348016 Intermediate result: Fidelity 0.99961308
2025-04-14 11:48:30.414311 Intermediate result: Fidelity 0.99962894
2025-04-14 11:48:30.488910 Intermediate result: Fidelity 0.99964121
2025-04-14 11:48:30.561298 Intermediate result: Fidelity 0.99964348
2025-04-14 11:48:30.632214 Intermediate result: Fidelity 0.99964860
2025-04-14 11:48:30.705703 Intermediate result: Fidelity 0.99965695
2025-04-14 11:48:30.775679 Intermediate result: Fidelity 0.99966398
2025-04-14 11:48:30.842629 Intermediate result: Fidelity 0.99967816
2025-04-14 11:48:30.912357 Intermediate result: Fidelity 0.99968293
2025-04-14 11:48:30.979420 Intermediate result: Fidelity 0.99968936
2025-04-14 11:48:31.049196 Intermediate result: Fidelity 0.99969223
2025-04-14 11:48:31.125391 Intermediate result: Fidelity 0.99970009
2025-04-14 11:48:31.201256 Intermediate result: Fidelity 0.99970724
2025-04-14 11:48:31.272424 Intermediate result: Fidelity 0.99970987
2025-04-14 11:48:31.338907 Intermediate result: Fidelity 0.99971237
2025-04-14 11:48:31.404800 Intermediate result: Fidelity 0.99971916
2025-04-14 11:48:31.475226 Intermediate result: Fidelity 0.99971940
2025-04-14 11:48:31.547746 Intermediate result: Fidelity 0.99972465
2025-04-14 11:48:31.622827 Intermediate result: Fidelity 0.99972763
2025-04-14 11:48:31.819516 Intermediate result: Fidelity 0.99972894
2025-04-14 11:48:33.444538 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]

สร้าง circuit สุดท้ายสำหรับ transpilation โดยประกอบ ansatz ที่ปรับแต่งแล้วเข้ากับ circuit time evolution ที่เหลือ

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)

ขั้นตอนที่ 2: ปรับแต่งปัญหาสำหรับการรันบน quantum hardware

เลือก Backend

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

Transpile circuit ที่สมบูรณ์บน target hardware เพื่อเตรียมพร้อมสำหรับการรัน จากนั้น ISA circuit ที่ได้สามารถส่งไปรันบน Backend ได้เลย

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 122

Output of the previous code cell

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 158

Output of the previous code cell

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

ในขั้นตอนนี้ เรารัน circuit ที่ transpile แล้วบน quantum hardware (หรือ Backend จำลอง) โดยใช้ EstimatorV2 จาก qiskit_ibm_runtime เพื่อวัด observable ที่กำหนด ผลลัพธ์จาก job จะให้ข้อมูลเชิงลึกที่มีคุณค่าเกี่ยวกับประสิทธิภาพของ circuit บน target hardware

สำหรับตัวอย่างขนาดใหญ่กว่านี้ เราจะสำรวจวิธีใช้ EstimatorOptions เพื่อจัดการและควบคุมพารามิเตอร์ของการทดลองบน hardware ได้ดียิ่งขึ้น แม้การตั้งค่าเหล่านี้จะเป็น optional แต่ก็มีประโยชน์สำหรับการติดตามพารามิเตอร์การทดลองและปรับแต่ง execution options เพื่อผลลัพธ์ที่ดีที่สุด

สำหรับรายการ execution options ทั้งหมดที่มี ดูที่ qiskit-ibm-runtime documentation

twirling_options = {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": 300,
"shots_per_randomization": 100,
"strategy": "active",
}

zne_options = {
"amplifier": "gate_folding",
"noise_factors": [1, 2, 3],
"extrapolated_noise_factors": list(np.linspace(0, 3, 31)),
"extrapolator": ["exponential", "linear", "fallback"],
}

meas_learning_options = {
"num_randomizations": 512,
"shots_per_randomization": 512,
}

resilience_options = {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": zne_options,
"measure_noise_learning": meas_learning_options,
}

estimator_options = {
"resilience": resilience_options,
"twirling": twirling_options,
}

estimator = Estimator(backend, options=estimator_options)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})
job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})

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

ในที่นี้ไม่จำเป็นต้องมีการ reconstruction เหมือนกับก่อนหน้า เราสามารถเข้าถึง expectation value ได้โดยตรงจาก execution output เพื่อตรวจสอบผลลัพธ์

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5888
AQC: -0.4809, |∆| = 0.1078
AQC Comparison: 1.1764, |∆| = 1.7652

พล็อตผลลัพธ์ของ AQC, comparison และ exact circuit สำหรับโมเดล XXZ แบบ 50 ตำแหน่ง

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)

plt.show()

Output of the previous code cell

บทสรุป

บทเรียนนี้ได้สาธิตวิธีใช้ Approximate Quantum Compilation ร่วมกับ tensor networks (AQC-Tensor) เพื่อบีบอัดและปรับแต่ง circuit สำหรับการจำลอง quantum dynamics ในขนาดใหญ่ โดยใช้ทั้งโมเดล Heisenberg ขนาดเล็กและขนาดใหญ่ เราประยุกต์ AQC-Tensor เพื่อลด circuit depth ที่จำเป็นสำหรับ Trotterized time evolution ด้วยการสร้าง parametrized ansatz จาก Trotter circuit แบบง่ายและปรับแต่งด้วยเทคนิค matrix product state (MPS) เราได้ approximation ของ target evolution ที่มี depth ต่ำ ซึ่งทั้งแม่นยำและมีประสิทธิภาพ

workflow ที่นำเสนอนี้ชี้ให้เห็นข้อดีหลักของ AQC-Tensor สำหรับการ scale quantum simulations:

  • การบีบอัด Circuit อย่างมีนัยสำคัญ: AQC-Tensor ลด circuit depth ที่ต้องการสำหรับ time evolution ที่ซับซ้อน เพิ่มความเป็นไปได้บน device ปัจจุบัน
  • การปรับแต่งที่มีประสิทธิภาพ: แนวทาง MPS มอบ framework ที่แข็งแกร่งสำหรับการปรับแต่งพารามิเตอร์ โดยสมดุลระหว่าง fidelity กับประสิทธิภาพการคำนวณ
  • พร้อมรันบน Hardware: การ transpile circuit ที่ปรับแต่งแล้วทำให้แน่ใจว่าตรงตามข้อจำกัดของ target quantum hardware

เมื่อ quantum device ขนาดใหญ่ขึ้นและ algorithm ขั้นสูงกว่าเกิดขึ้น เทคนิคอย่าง AQC-Tensor จะกลายเป็นสิ่งจำเป็นสำหรับการรัน quantum simulations ที่ซับซ้อนบน near-term hardware แสดงให้เห็นถึงความก้าวหน้าที่น่าสนใจในการจัดการ depth และ fidelity สำหรับ quantum applications ที่ scalable

แบบสำรวจบทเรียน

Link to survey

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.

กรุณาทำแบบสำรวจสั้นนี้เพื่อให้ feedback เกี่ยวกับบทเรียนนี้ ข้อมูลของคุณจะช่วยให้เราปรับปรุงเนื้อหาและประสบการณ์การใช้งานได้ดียิ่งขึ้น

Source: IBM Quantum docs — updated 13 ก.พ. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 9 เม.ย. 2569