การทดลองระดับ Utility-Scale ครั้งที่ 1
Tamiya Onodera (5 กรกฎาคม 2024)
ดาวน์โหลด pdf ของเลคเชอร์ต้นฉบับ โปรดทราบว่าโค้ดบางส่วนอาจล้าสมัย เนื่องจากเป็นภาพนิ่ง
เวลา QPU โดยประมาณสำหรับการทดลองนี้คือ 45 วินาที
1. บทนำสู่ utility paper
ในบทเรียนนี้ เราจะรัน Circuit ระดับ utility-scale ที่ปรากฏในสิ่งที่เราเรียกกันอย่างไม่เป็นทางการว่า "utility paper" ซึ่งตีพิมพ์ใน Nature เล่มที่ 618 วันที่ 15 มิถุนายน 2023 บทความนี้กล่าวถึงวิวัฒนาการตามเวลาของแบบจำลอง Ising แบบสองมิติในสนามขวาง โดยเฉพาะอย่างยิ่งพวกเขาพิจารณา dynamics ตามเวลาของ Hamiltonian
โดยที่ คือการคัปปลิ้งของสปินที่อยู่ติดกันที่ และ คือสนามขวางแบบ global พวกเขาจำลอง spin dynamics จากสถานะเริ่มต้นโดยใช้ first-order Trotter decomposition ของ time-evolution operator
โดยที่เวลาวิวัฒนาการ ถูกแบ่งเป็น Trotter steps และ และ คือ และ rotation Gate ตามลำดับ
พวกเขารันการทดลองบน IBM Quantum® Eagle processor ซึ่งเป็นอุปกรณ์ 127 Qubit ที่มีการเชื่อมต่อแบบ heavy-hex โดยใช้ interactions กับ Qubit ทั้งหมด และ interactions สำหรับทุกขอบของ coupling map โปรดทราบว่า interactions ทั้งหมดไม่สามารถใช้พร้อมกันได้เนื่องจาก "data dependance" ดังนั้นพวกเขาจึงจัดสีให้ coupling map เพื่อจัดกลุ่มเป็น layer ต่าง ๆ โดย layer เดียวกันจะได้รับสีเดียวกันและสามารถรันพร้อมกันได้
นอกจากนี้ เพื่อความง่ายในการทดลอง พวกเขาเน้นที่กรณี
ผลงานสำคัญของบทความนี้คือพวกเขาสร้าง quantum circuit ในขนาดที่เกินความสามารถของ statevector simulation รันบนคอมพิวเตอร์ควอนตัมที่มีนอยส์ และประสบความสำเร็จในการดึงผลลัพธ์ที่เชื่อถือได้ นั่นคือพวกเขาแสดงให้เห็นถึงประโยชน์ของคอมพิวเตอร์ควอนตัมที่มีนอยส์ ในการทำเช่นนี้ พวกเขาใช้ zero-noise extrapolation (ZNE) ร่วมกับ probabilistic error amplification (PEA) เพื่อลดความผิดพลาดจากอุปกรณ์ที่มีนอยส์
ตั้งแต่นั้นมา เราเรียกการทดลองและ Circuit ลักษณะนี้ว่า "utility-scale"
1.1 เป้าหมายของคุณ
เป้าหมายในบทเรียนนี้คือการสร้าง Circuit ระดับ utility-scale และรันบน Eagle processor การดึงผลลัพธ์ที่เชื่อถือได้นั้นอยู่นอกขอบเขตของ notebook นี้ ส่วนหนึ่งเพราะ PEA เป็นฟีเจอร์ทดลองใน Qiskit ณ ขณะที่เขียน และส่วนหนึ่งเพราะการใช้ ZNE กับ PEA จะใช้เวลามาก
โดยเฉพาะ คุณจะต้องสร้างและรัน Circuit ที่ตรงกับ Figure 4b ของบทความ และพล็อตจุด "unmitigated" ของตัวเอง ดังที่เห็น นี่คือ Circuit ขนาด 127 Qubit × 60 layer (20 Trotter steps) โดยมี เป็น observable
ฟังดูยาก? ไม่ต้องกังวล สามบทเรียนสุดท้ายของคอร์สนี้จะเป็นขั้นตอนนำทาง เราจะเริ่มสาธิตการทดลองขนาดเล็กกว่า ซึ่งเป็นการสร้างและรันบน fake device ซึ่งเป็น Circuit ขนาด 27 Qubit × 6 layer (2 Trotter steps) โดยมี เป็น observable
เพียงเท่านี้สำหรับบทนำ ขอให้สนุกกับการผจญภัยระดับ utility-scale!
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
import qiskit
qiskit.__version__
'2.0.2'
#!pip install qiskit_ibm_runtime
#!pip install qiskit_aer
import matplotlib.pyplot as plt
import numpy as np
import rustworkx as rx
from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library import YGate
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import (
QiskitRuntimeService,
fake_provider,
EstimatorV2 as Estimator,
)
from qiskit_aer import AerSimulator
service = QiskitRuntimeService()
2. การเตรียมการ
2.1 สร้าง RZZ(- / 2)
ก่อนอื่น สังเกตว่า RZZ gate โดยทั่วไปต้องใช้ gate สองตัว
from qiskit.circuit.library import RZZGate
θ_h = Parameter("$\\theta_h$")
qc1 = QuantumCircuit(2)
qc1.append(RZZGate(θ_h), [0, 1])
qc1.decompose(reps=1).draw("mpl")
ดังที่กล่าวไว้ข้างต้น เราเน้นที่ RZZ gate ที่มีมุมเฉพาะ - / 2 สำหรับการทดลองนี้ ดังที่แสดงในบทความ สามารถสร้างได้ด้วย gate เพียงตัวเดียว
qc2 = QuantumCircuit(2)
qc2.sdg([0, 1])
qc2.append(YGate().power(1 / 2), [1])
qc2.cx(0, 1)
qc2.append(YGate().power(1 / 2).adjoint(), [1])
qc2.draw("mpl")
เราสร้าง Gate จาก Circuit นี้สำหรับการอ้างอิงในอนาคต
rzz = qc2.to_gate(label="RZZ")
ลองใช้ rzz ที่สร้างขึ้นใหม่แบบสุ่ม
qc3 = QuantumCircuit(3)
qc3.append(rzz, [0, 1])
qc3.append(rzz, [0, 2])
display(qc3.draw("mpl"))
# display(qc.decompose(reps=1).draw("mpl"))
ก่อนนำไปใช้ต่อ ลองตรวจสอบความเท่าเทียมเชิงตรรกะของ qc1 (RZZ gate) สำหรับ -pi/2 กับ rzz หรือ qc2 gate ที่สร้างขึ้น:
from qiskit.quantum_info import Operator
op1 = Operator(qc1.assign_parameters([-np.pi / 2]))
op2 = Operator(qc2)
op1.equiv(op2)
True
2.2 การจัดสีให้ coupling map
ลองศึกษาวิธีจัดสีให้ coupling map ของ Backend ซึ่งจำเป็นสำหรับการจัดกลุ่ม interactions เป็น layer
เริ่มต้น ลองแสดงภาพ coupling map ของ Backend โปรดทราบว่า coupling map ของอุปกรณ์ IBM Quantum ปัจจุบันทุกตัวเป็นแบบ heavy-hexagonal
backend = service.least_busy(operational=True, simulator=False)
backend.coupling_map.draw()

สำหรับการจัดสี coupling map เราใช้ rustworkx ซึ่งเป็น Python package สำหรับทำงานกับกราฟและ complex networks โดยมีอัลกอริทึมการจัดสีหลายแบบ ซึ่งล้วนเป็น heuristic และจึงไม่รับประกันว่าจะหาการจัดสีที่น้อยที่สุด
เนื่องจาก heavy-hex graph เป็น bipartite เราจึงเลือกใช้ graph_bipartite_edge_color ซึ่งควรหาการจัดสีที่น้อยที่สุดสำหรับกราฟเหล่านี้
def color_coupling_map(backend):
graph = backend.coupling_map.graph
undirected_graph = graph.to_undirected(multigraph=False)
edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)
if edge_color_map is None:
edge_color_map = rx.graph_greedy_edge_color(undirected_graph)
# build a map from color to a list of edges
edge_index_map = undirected_graph.edge_index_map()
color_edges_map = {color: [] for color in edge_color_map.values()}
for edge_index, color in edge_color_map.items():
color_edges_map[color].append(
(edge_index_map[edge_index][0], edge_index_map[edge_index][1])
)
return edge_color_map, color_edges_map
Heavy-hexagonal graph ควรถูกทาสีสามสี ลองตรวจสอบสิ่งนี้กับ coupling map ข้างต้น
edge_color_map, color_edges_map = color_coupling_map(backend)
print(
f"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned."
)
ibm_strasbourg, 127-qubit device, 3 colors assigned.
ใช่แล้ว!
เพื่อความสนุก ลองทาสี coupling map ตามการจัดสีที่ได้ โดยใช้ฟีเจอร์ visualization ของ rustworkx
color_str_map = {0: "green", 1: "red", 2: "blue"}
undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
for i in undirected_graph.edge_indices():
undirected_graph.get_edge_data_by_index(i)["color"] = color_str_map[
edge_color_map[i]
]
rx.visualization.graphviz_draw(
undirected_graph, method="neato", edge_attr_fn=lambda edge: {"color": edge["color"]}
)

3. แก้ปัญหาวิวัฒนาการตามเวลาแบบ Trotterized ของแบบจำลอง Ising แบบ 2D
ลองสร้าง routine เพื่อสร้าง Circuit ของ utility paper สำหรับวิวัฒนาการตามเวลาของแบบจำลอง Ising แบบ 2D โดย routine รับสามพารามิเตอร์ ได้แก่ Backend จำนวนเต็มที่บอกจำนวน Trotter steps และค่า Boolean ที่ควบคุมการแทรก barrier
def get_utility_circuit(backend, num_steps: int, barrier: bool = False):
num_qubits = backend.num_qubits
_, color_edges_map = color_coupling_map(backend)
θ_h = Parameter("$\\theta_h$")
qc = QuantumCircuit(num_qubits)
for i in range(num_steps):
qc.rx(θ_h, range(num_qubits))
for _, edge_list in color_edges_map.items():
for edge in edge_list:
qc.append(rzz, edge)
if barrier:
qc.barrier()
return qc
โปรดทราบว่าเราได้ทำ qubit mapping และ routing ของ Circuit ที่สร้างขึ้นเองแล้ว ดังนั้นเมื่อเรา transpile Circuit ในภายหลัง เราจะไม่ (และ__ไม่ควร__) ขอให้ Transpiler ทำ qubit mapping และ routing ดังที่คุณจะเห็นในอีกสักครู่ เราเรียกใช้ด้วย optimization level 1 และ layout method เป็น "trivial"
ถัดไป เราสร้าง routine ง่าย ๆ เพื่อรับข้อมูลเกี่ยวกับ Circuit ที่สร้างขึ้นเพื่อตรวจสอบเบื้องต้น
def get_circuit_info(qc: QuantumCircuit, reps: int = 0):
qc0 = qc.decompose(reps=reps)
return (
f"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)"
+ ", "
+ f"""Gate breakdown: {", ".join([f"{k.upper()} {v}" for k, v in qc0.count_ops().items()])}"""
)
ลองทดสอบ routine เหล่านี้ คุณควรเห็น Circuit ขนาด 27 Qubit × 15 layer (5 Trotter steps) เนื่องจาก fake device มี 28 ขอบ ควรมี entangling gate 28*5 ตัว
backend = fake_provider.FakeTorontoV2()
num_steps = 5
qc = get_utility_circuit(backend, num_steps, True)
display(qc.draw(output="mpl", fold=-1))
print(get_circuit_info(qc, reps=0))
print(get_circuit_info(qc, reps=1))

27 qubits × 15 layers (20-depth), Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5
27 qubits × 15 layers (60-depth), Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5
4. แก้ปัญหาเวอร์ชัน 27 Qubit
เราจะสาธิตการทดลองขนาดเล็กกว่าของ utility experiment เราสร้าง Circuit ขนาด 27 Qubit × 6 layer (2 Trotter steps) โดยมี เป็น observable และรันทั้งบน AerSimulator และ fake device
แน่นอน เราทำตาม workflow สี่ขั้นตอน "Qiskit pattern" ซึ่งประกอบด้วย Map, Optimize, Execute และ Post-Process โดยเฉพาะ:
- แมป input แบบคลาสสิกไปยังการคำนวณเชิงควอนตัม
- ปรับปรุง Circuit สำหรับการคำนวณเชิงควอนตัม
- รัน Circuit โดยใช้ primitive
- ประมวลผลและส่งคืนผลลัพธ์ในรูปแบบคลาสสิก
ด้านล่าง เรามีขั้นตอน Map สำหรับสร้าง Circuit ของการทดลองขนาดเล็ก จากนั้นมีชุด Optimize และ Execute ชุดหนึ่งสำหรับ AerSimulator และอีกชุดสำหรับ fake device สุดท้ายมีขั้นตอน Post-Process เพื่อพล็อตผลลัพธ์
4.1 ขั้นตอนที่ 1: Map
backend = fake_provider.FakeTorontoV2() # a 27 qubit fake device.
num_steps = 2
qc = get_utility_circuit(backend, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [13], 1)], num_qubits=backend.num_qubits
) # Falcon
angles = [
0,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
1.0,
np.pi / 2,
] # We try 11 angles for theta_h.
4.2 ขั้นตอนที่ 2 และ 3: Optimize และ Execute (Simulator)
backend_sim = AerSimulator()
transpiled_qc_sim = transpile(
qc, backend_sim, optimization_level=1, layout_method="trivial"
)
transpiled_obs_sim = obs.apply_layout(layout=transpiled_qc_sim.layout)
print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_sim, reps=1))
27 qubits × 6 layers (23-depth), Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (16-depth), Gate breakdown: U3 80, CX 56, R 54, U1 32, U 28
ผู้ใช้รายหนึ่งรัน cell ถัดไปบน MacBook Pro พร้อม Intel Core i7 Processor 2.3 GHz quad-core พร้อม RAM 32GB 3LPDDR4X ที่รัน macOS 14.5 ใช้เวลา wall time 161ms แต่ละเครื่องจะแตกต่างกันเล็กน้อย
%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_sim)
pub = (transpiled_qc_sim, transpiled_obs_sim, params)
result_sim = estimator.run([pub]).result()
CPU times: user 231 ms, sys: 186 ms, total: 417 ms
Wall time: 111 ms
4.3 ขั้นตอนที่ 2 และ 3: Optimize และ Execute (fake device)
backend_fake = fake_provider.FakeTorontoV2()
transpiled_qc_fake = transpile(
qc, backend_fake, optimization_level=1, layout_method="trivial"
)
transpiled_obs_fake = obs.apply_layout(layout=transpiled_qc_fake.layout)
print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_fake, reps=1))
27 qubits × 6 layers (23-depth), Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (49-depth), Gate breakdown: SDG 324, U1 274, H 162, CX 56, U3 14
เมื่อผู้ใช้รายเดียวกันรัน cell ถัดไปในสภาพแวดล้อมเดียวกัน ใช้เวลา wall time 2 นาที 19 วินาที การรัน Circuit บน fake device เรียกใช้การจำลองแบบมีนอยส์ซึ่งใช้เวลานานกว่าการจำลองแบบ exact มาก เราแนะนำว่าอย่ารัน Circuit ขนาดใหญ่กว่า (เช่น 27 Qubit × 9 layer พร้อม 3 Trotter steps) บน fake device
%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_fake)
pub = (transpiled_qc_fake, transpiled_obs_fake, params)
result_fake = estimator.run([pub]).result()
CPU times: user 4min 42s, sys: 9.35 s, total: 4min 51s
Wall time: 38.3 s
4.4 ขั้นตอนที่ 4: Post-process
เราพล็อตผลลัพธ์จากการจำลองแบบ exact และมีนอยส์ คุณจะเห็นผลกระทบจากนอยส์ที่รุนแรงบน FakeToronto
plt.plot(angles, result_fake[0].data.evs, "o", label="Fake Device")
plt.plot(angles, result_sim[0].data.evs, "o", label="AerSimulator")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{13} \\rangle$")
plt.legend()
plt.show()
5. แก้ปัญหาเวอร์ชัน 127 Qubit
เป้าหมายของคุณคือการรัน utility-scale experiment ตามที่กล่าวไว้ในตอนต้น คุณจะสร้างและรัน Circuit ขนาด 127 Qubit และ 60 layer (20 Trotter steps) โดยมี เป็น observable เราแนะนำให้ลองทำเองโดยใช้โค้ดเวอร์ชัน 27 Qubit เมื่อเหมาะสม แต่มีเฉลยให้ที่นี่
เฉลย:
5.1 ขั้นตอนที่ 1: Map
# backend_map = service.backend("ibm_brisbane")
backend_map = service.least_busy(operational=True, simulator=False)
num_steps = 20
qc = get_utility_circuit(backend_map, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [62], 1)], num_qubits=backend_map.num_qubits
) # Eagle
angles = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, np.pi / 2]
5.2 ขั้นตอนที่ 2 และ 3: Optimize และ Execute
โปรดทราบว่า coupling map ของ Eagle processor มี 144 ขอบ
# backend = service.backend("ibm_brisbane")
backend = backend_map
transpiled_qc = transpile(qc, backend, optimization_level=1, layout_method="trivial")
transpiled_obs = obs.apply_layout(layout=transpiled_qc.layout)
print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc))
156 qubits × 60 layers (221-depth), Gate breakdown: SDG 7040, UNITARY 7040, CX 3520, R 3120
156 qubits × 60 layers (201-depth), Gate breakdown: RZ 11933, SX 6240, CZ 3520
params = [[p] for p in angles]
estimator = Estimator(mode=backend)
pub = (transpiled_qc, transpiled_obs, params)
job = estimator.run([pub])
job_id = job.job_id()
print(f"job id={job_id}")
job id=d1479n6qf56g0081sxa0
5.3 Post-process
เราให้ค่าสำหรับจุด "mitigated" ใน Figure 4b ของ utility paper พล็อตค่าเหล่านี้ร่วมกับผลลัพธ์ของคุณ
result_paper = [
1.0171,
1.0044,
0.9563,
0.9602,
0.8394,
0.8120,
0.5466,
0.4556,
0.1953,
0.0141,
0.0117,
]
# REPLACE WITH YOUR OWN JOB ID
job = service.job(job_id)
plt.plot(angles, job.result()[0].data.evs, "o", label=f"{job.backend().name}")
plt.plot(angles, result_paper, "o", label="Utility Paper")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{62} \\rangle$")
plt.legend()
plt.show()
ผลลัพธ์ของคุณคล้ายกับ "unmitigated" ใน Figure 4b ไหม? อาจแตกต่างกันมาก ขึ้นอยู่กับอุปกรณ์และสภาพของมันในขณะทดลอง ไม่ต้องกังวลกับผลลัพธ์ สิ่งที่เราจะตรวจสอบคือว่าคุณทำโค้ดได้ถูกต้องหรือไม่ ถ้าทำได้ ขอแสดงความยินดี คุณได้ก้าวถึงจุดเริ่มต้นของยุค utility แล้ว
เช่นเดียวกับใน Utility paper นักวิทยาศาสตร์ทั่วโลกได้ทุ่มเทความคิดสร้างสรรค์อย่างมากในการดึงผลลัพธ์ที่มีความหมายแม้จะมีนอยส์ เป้าหมายสูงสุดของความพยายามร่วมกันนี้คือ quantum advantage: สถานะที่คอมพิวเตอร์ควอนตัมสามารถแก้ปัญหาบางอย่างที่มีประโยชน์ในอุตสาหกรรมได้เร็วกว่า แม่นยำกว่า หรือถูกกว่าคอมพิวเตอร์คลาสสิก สิ่งนี้ไม่น่าจะเป็นเหตุการณ์เดียว แต่เป็นยุคสมัยที่การจำลองผลลัพธ์ควอนตัมแบบคลาสสิกใช้เวลานานขึ้นเรื่อย ๆ จนกว่าจะถึงจุดที่ความได้เปรียบของควอนตัมนั้นมีความสำคัญอย่างยิ่ง สิ่งหนึ่งที่ชัดเจนเกี่ยวกับ quantum advantage: เราจะไปถึงจุดนั้นได้ก็ต่อเมื่อผ่านการทดลองระดับ utility-scale เท่านั้น หากคอร์สนี้ทำให้คุณเข้าร่วมการแสวงหาที่เต็มไปด้วยความท้าทายและความสนุก เราจะยินดีมาก
อ้างอิง
- Kim, Y., Eddins, A., Anand, S. et al. Evidence for the utility of quantum computing before fault tolerance. Nature 618, 500–505 (2023). https://doi.org/10.1038/s41586-023-06096-3