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

วัดประสิทธิภาพ Dynamic Circuit ด้วย Cut Bell Pairs

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

ภูมิหลัง

ฮาร์ดแวร์ควอนตัมมักถูกจำกัดให้ใช้ interaction เฉพาะในพื้นที่ใกล้เคียง แต่อัลกอริทึมหลายตัวต้องการ entangle Qubit ที่อยู่ห่างไกล หรือแม้แต่Qubit บนโปรเซสเซอร์แยกต่างหาก Dynamic circuit — กล่าวคือ Circuit ที่มีการวัดกลางวงจรและ feedforward — เปิดทางให้เอาชนะข้อจำกัดเหล่านี้ได้ โดยใช้การสื่อสารแบบคลาสสิกแบบ real-time เพื่อ implement การดำเนินการควอนตัมแบบ non-local อย่างมีประสิทธิผล ในแนวทางนี้ ผลลัพธ์การวัดจากส่วนหนึ่งของ Circuit (หรือจาก QPU หนึ่ง) สามารถ trigger Gate บนส่วนอื่นได้แบบมีเงื่อนไข ทำให้เราสามารถส่ง entanglement ข้ามระยะทางไกลได้ นี่คือพื้นฐานของโครงการ local operations and classical communication (LOCC) ที่เราใช้ resource state แบบ entangled (Bell pairs) และสื่อสารผลการวัดผ่านช่องคลาสสิกเพื่อเชื่อม Qubit ที่อยู่ห่างไกลกัน

การประยุกต์ใช้ LOCC ที่น่าสนใจอย่างหนึ่งคือการสร้าง CNOT Gate ระยะไกลแบบเสมือนด้วย teleportation ดังที่แสดงในบทช่วยสอน long-range entanglement แทนที่จะใช้ long-range CNOT โดยตรง (ซึ่งการเชื่อมต่อของฮาร์ดแวร์อาจไม่รองรับ) เราสร้าง Bell pair และ implement Gate ด้วยวิธีอิงการ teleportation อย่างไรก็ดี ความแม่นยำของการดำเนินการเหล่านี้ขึ้นอยู่กับคุณสมบัติของฮาร์ดแวร์ การ decoherence ของ Qubit ระหว่างการหน่วง (ขณะรอผลการวัด) และความหน่วงของการสื่อสารแบบคลาสสิกอาจทำให้ state แบบ entangled เสื่อมคุณภาพ นอกจากนี้ ข้อผิดพลาดในการวัดกลางวงจรยังแก้ไขได้ยากกว่าข้อผิดพลาดในการวัดขั้นสุดท้าย เพราะข้อผิดพลาดเหล่านี้แพร่กระจายไปยังส่วนที่เหลือของ Circuit ผ่าน conditional gate

ในการทดลองอ้างอิง ผู้เขียนได้นำเสนอ benchmark ค่าความแม่นยำของ Bell pair เพื่อระบุว่าส่วนใดของอุปกรณ์เหมาะสมที่สุดสำหรับ entanglement แบบ LOCC แนวคิดคือการรัน dynamic circuit ขนาดเล็กบนทุกกลุ่มของ Qubit ที่เชื่อมต่อกันสี่ตัวในโปรเซสเซอร์ Circuit สี่ Qubit นี้จะสร้าง Bell pair บน Qubit กลางสองตัวก่อน จากนั้นใช้ Bell pair นั้นเป็น resource เพื่อ entangle Qubit ขอบสองตัวโดยใช้ LOCC โดยเฉพาะอย่างยิ่ง Qubit 1 และ 2 จะถูก prepare ให้เป็น Bell pair แบบ uncut ในพื้นที่ (ด้วย Hadamard และ CNOT) จากนั้นรูทีน teleportation จะใช้ Bell pair นั้น entangle Qubit 0 และ 3 Qubit 1 และ 2 จะถูกวัดระหว่างการ execute Circuit และตามผลลัพธ์เหล่านั้น Pauli correction (X บน Qubit 3 และ Z บน Qubit 0) จะถูก apply จากนั้น Qubit 0 และ 3 จะอยู่ใน Bell state ณ จุดสิ้นสุดของ Circuit

เพื่อวัดคุณภาพของคู่ entangled สุดท้ายนี้ เราวัด stabilizer ของมัน: โดยเฉพาะอย่างยิ่ง parity ในฐาน ZZ (Z0Z3Z_0Z_3) และในฐาน XX (X0X3X_0X_3) สำหรับ Bell pair ที่สมบูรณ์แบบ ค่าความคาดหวังทั้งสองนี้เท่ากับ +1 ในทางปฏิบัติ noise ของฮาร์ดแวร์จะลดค่าเหล่านี้ลง ดังนั้นเราจึงทำซ้ำ Circuit สองครั้งสำหรับแต่ละคู่ Qubit: Circuit แรกวัด Qubit 0 และ 3 ในฐาน ZZ และ Circuit ที่สองวัดในฐาน XX จากผลลัพธ์ เราได้ค่าประมาณ Z0Z3\langle Z_0Z_3\rangle และ X0X3\langle X_0X_3\rangle สำหรับคู่ Qubit นั้น เราใช้ mean squared error (MSE) ของ stabilizer เหล่านี้เทียบกับค่า ideal (1) เป็น metric ง่ายๆ ของค่าความแม่นยำ entanglement MSE ต่ำหมายความว่า Qubit ทั้งสองบรรลุ Bell state ที่ใกล้เคียง ideal มากขึ้น (ความแม่นยำสูงกว่า) ในขณะที่ MSE สูงบ่งชี้ข้อผิดพลาดมากขึ้น การสแกนการทดลองนี้ทั่วทั้งอุปกรณ์ทำให้เรา benchmark ความสามารถในการวัดและ feedforward ของกลุ่ม Qubit ต่างๆ และระบุคู่ Qubit ที่ดีที่สุดสำหรับการดำเนินการ LOCC

บทช่วยสอนนี้แสดงการทดลองบนอุปกรณ์ IBM Quantum® เพื่อแสดงให้เห็นว่า dynamic circuit สามารถใช้สร้างและประเมิน entanglement ระหว่าง Qubit ที่อยู่ห่างไกลได้อย่างไร เราจะ map ออกทุกสายโซ่เชิงเส้นสี่ Qubit บนอุปกรณ์ รัน Circuit teleportation บนแต่ละสายโซ่ จากนั้นแสดงภาพกระจายของค่า MSE กระบวนการ end-to-end นี้แสดงให้เห็นวิธีใช้ประโยชน์จาก Qiskit Runtime และฟีเจอร์ dynamic circuit เพื่อให้ข้อมูลสำหรับการตัดสินใจที่คำนึงถึงฮาร์ดแวร์ในการตัด Circuit หรือกระจายอัลกอริทึมควอนตัมข้ามระบบแบบ modular

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

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

  • Qiskit SDK v2.0 หรือใหม่กว่า พร้อมการรองรับvisualization
  • Qiskit Runtime v0.40 หรือใหม่กว่า (pip install qiskit-ibm-runtime)

การตั้งค่า

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler import generate_preset_pass_manager

import numpy as np
import matplotlib.pyplot as plt

def create_bell_stab(initial_layouts):
"""
Create a circuit for a 1D chain of qubits (number of qubits must be a multiple of 4),
where a middle Bell pair is consumed to create a Bell at the edge.
Takes as input a list of lists, where each element of the list is a
1D chain of physical qubits that is used as the initial_layout for the transpiled circuit.
Returns a list of length-2 tuples, each tuple contains a circuit to measure the ZZ stabilizer and
a circuit to measure the XX stabilizer of the edge Bell state.
"""
bell_circuits = []
for (
initial_layout
) in initial_layouts: # Iterate over chains of physical qubits
assert (
len(initial_layout) % 4 == 0
), f"The length of the chain must be a multiple of 4, len(inital_layout)={len(initial_layout)}"
num_pairs = len(initial_layout) // 4

bell_parallel = QuantumCircuit(4 * num_pairs, 4 * num_pairs)

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.h(q0)
bell_parallel.h(q1)
bell_parallel.cx(q1, q2)
bell_parallel.cx(q0, q1)
bell_parallel.cx(q2, q3)
bell_parallel.h(q2)

# add barrier BEFORE measurements and add id in conditional
bell_parallel.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.measure(q1, ca0)
bell_parallel.measure(q2, ca1)
# bell_parallel.barrier() #remove barrier after measurement

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
with bell_parallel.if_test((ca0, 1)):
bell_parallel.x(q3)
with bell_parallel.if_test((ca1, 1)):
bell_parallel.z(q0)
bell_parallel.id(q0) # add id here for correct alignment

bell_zz = bell_parallel.copy()
bell_zz.barrier()
bell_xx = bell_parallel.copy()
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
bell_xx.h(q0)
bell_xx.h(q3)
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits

bell_zz.measure(q0, c0)
bell_zz.measure(q3, c1)

bell_xx.measure(q0, c0)
bell_xx.measure(q3, c1)

bell_circuits.append(bell_zz)
bell_circuits.append(bell_xx)

return bell_circuits

def get_mse(result, initial_layouts):
"""
given a result object and the initial layouts, returns a dict of layouts and their mse
"""
layout_mse = {}
for layout_idx, initial_layout in enumerate(initial_layouts):
layout_mse[tuple(initial_layout)] = {}

num_pairs = len(initial_layout) // 4

counts_zz = result[2 * layout_idx].data.c.get_counts()
total_shots = sum(counts_zz.values())

# Get ZZ expectation value
exp_zz_list = []
for pair_idx in range(num_pairs):
exp_zz = 0
for bitstr, shots in counts_zz.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
z_val0 = 1 if b0 == "0" else -1
z_val1 = 1 if b1 == "0" else -1
exp_zz += z_val0 * z_val1 * shots
exp_zz /= total_shots
exp_zz_list.append(exp_zz)

counts_xx = result[2 * layout_idx + 1].data.c.get_counts()
total_shots = sum(counts_xx.values())

# Get XX expectation value
exp_xx_list = []
for pair_idx in range(num_pairs):
exp_xx = 0
for bitstr, shots in counts_xx.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
x_val0 = 1 if b0 == "0" else -1
x_val1 = 1 if b1 == "0" else -1
exp_xx += x_val0 * x_val1 * shots
exp_xx /= total_shots
exp_xx_list.append(exp_xx)

mse_list = [
((exp_zz - 1) ** 2 + (exp_xx - 1) ** 2) / 2
for exp_zz, exp_xx in zip(exp_zz_list, exp_xx_list)
]

print(f"layout {initial_layout}")
for idx in range(num_pairs):
layout_mse[tuple(initial_layout)][
tuple(initial_layout[4 * idx : 4 * idx + 4])
] = mse_list[idx]
print(
f"qubits: {initial_layout[4*idx:4*idx+4]}, mse:, {round(mse_list[idx],4)}"
)
# print(f'exp_zz: {round(exp_zz_list[idx],4)}, exp_xx: {round(exp_xx_list[idx],4)}')
print(" ")
return layout_mse

def plot_mse_ecdfs(layouts_mse, combine_layouts=False):
"""
Plot CDF of MSE data for multiple layouts. Optionally combine all data in a single CDF
"""

if not combine_layouts:
for initial_layout, layouts in layouts_mse.items():
sorted_layouts = dict(
sorted(layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {initial_layout}",
)

# add qubits labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

elif combine_layouts:
all_layouts = {}
all_initial_layout = []
for (
initial_layout,
layouts,
) in layouts_mse.items(): # puts together all layout information
all_layouts.update(layouts)
all_initial_layout += initial_layout

sorted_layouts = dict(
sorted(all_layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {sorted(list(set(all_initial_layout)))}",
)

# add qubit labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

plt.xscale("log")
plt.xlabel("Mean squared error of ⟨ZZ⟩ and ⟨XX⟩")
plt.ylabel("Cumulative distribution function")
plt.title("CDF for different initial layouts")
plt.grid(alpha=0.3)
plt.show()

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

ขั้นตอนแรกคือสร้างชุด quantum circuit เพื่อ benchmark ลิงก์ Bell pair ที่เป็นตัวเลือกทั้งหมดที่ปรับให้เหมาะสมกับ topology ของอุปกรณ์ เราค้นหาโดยโปรแกรม coupling map ของอุปกรณ์เพื่อหาสายโซ่ที่เชื่อมต่อเชิงเส้นของ Qubit สี่ตัวทั้งหมด สายโซ่แต่ละสาย (ระบุด้วย index Qubit [q0q1q2q3][q0-q1-q2-q3]) ทำหน้าที่เป็น test case สำหรับ Circuit entanglement-swapping โดยการระบุ path ความยาว 4 ที่เป็นไปได้ทั้งหมด เราให้ครอบคลุมสูงสุดสำหรับการจัดกลุ่ม Qubit ที่เป็นไปได้ซึ่งสามารถ implement โปรโตคอลได้

service = QiskitRuntimeService()
backend = service.least_busy(operational=True)

เราสร้างสายโซ่เหล่านี้โดยใช้ฟังก์ชันช่วยเหลือที่ทำการค้นหาแบบ greedy บน device graph ฟังก์ชันนี้ส่งคืน "stripes" ของสายโซ่ Qubit สี่ตัว รวมกันเป็นกลุ่ม 16 Qubit (dynamic circuit ในปัจจุบันจำกัดขนาดของ measurement register ให้ไม่เกิน 16 Qubit) การรวมกลุ่มช่วยให้เราสามารถรันการทดลอง Qubit สี่ตัวหลายรายการพร้อมกันในส่วนต่างๆ ของชิป และใช้ประโยชน์จากอุปกรณ์ทั้งหมดอย่างมีประสิทธิภาพ แต่ละ 16-Qubit stripe ประกอบด้วยสายโซ่สี่ตัวที่ไม่ซ้ำกัน หมายความว่าไม่มี Qubit ใดถูกใช้ซ้ำในกลุ่มนั้น ตัวอย่างเช่น stripe หนึ่งอาจประกอบด้วยสายโซ่ [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] และ [12131415][12-13-14-15] ที่รวมกัน Qubit ที่ไม่ได้รวมอยู่ใน stripe ใดจะถูกส่งคืนในตัวแปร leftover

from itertools import chain
from collections import defaultdict

def stripes16_from_backend(backend):
"""
Creates stripes of 16 qubits, four non-overlapping four-qubit chains, that cover as much of
the coupling map as possible. Returns any unused qubits as leftovers.
"""
# get the undirected adjacency list
edges = backend.coupling_map.get_edges()
graph = defaultdict(set)
for u, v in edges:
graph[u].add(v)
graph[v].add(u)

qubits = sorted(graph) # all qubit indices that appear

# greedy search for 4-long linear chains (blocks) ────────────
used = set() # qubits already placed in a block
blocks = [] # each block is a four-qubit list

for q in qubits: # deterministic order for reproducibility
if q in used:
continue # already consumed by earlier block

# depth-first "straight" walk of length 3 without revisiting nodes
def extend(path):
if len(path) == 4:
return path
tip = path[-1]
for nbr in sorted(graph[tip]): # deterministic
if nbr not in path and nbr not in used:
maybe = extend(path + [nbr])
if maybe:
return maybe
return None

block = extend([q])
if block: # found a 4-node path
blocks.append(block)
used.update(block)

# bundle four four-qubit blocks into one 16-qubit stripe (max number of measurement compatible with if-else)
stripes = [
list(chain.from_iterable(blocks[i : i + 4]))
for i in range(0, len(blocks) // 4 * 4, 4) # full groups of four
]

leftovers = set(qubits) - set(chain.from_iterable(stripes))
return stripes, leftovers
initial_layouts, leftover = stripes16_from_backend(backend)

ต่อไป เราสร้าง Circuit สำหรับแต่ละ 16-Qubit stripe รูทีนดำเนินการดังต่อไปนี้สำหรับแต่ละสายโซ่:

  • สร้าง middle Bell pair: Apply Hadamard บน Qubit 1 และ CNOT จาก Qubit 1 ไปยัง Qubit 2 นี่จะ entangle Qubit 1 และ 2 (สร้าง Bell state Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2})
  • Entangle Qubit ขอบ: Apply CNOT จาก Qubit 0 ไปยัง Qubit 1 และ CNOT จาก Qubit 2 ไปยัง Qubit 3 ซึ่งเชื่อมคู่ที่แยกจากกันในตอนแรกเข้าด้วยกัน เพื่อให้ Qubit 0 และ 3 จะกลาย entangle หลังจากขั้นตอนถัดไป นอกจากนี้ยังมีการ apply Hadamard บน Qubit 2 ด้วย (สิ่งนี้ร่วมกับ CNOT ก่อนหน้า ประกอบเป็นส่วนหนึ่งของการวัด Bell บน Qubit 1 และ 2) ณ จุดนี้ Qubit 0 และ 3 ยังไม่ entangle แต่ Qubit 1 และ 2 entangle กับพวกมันใน state สี่ Qubit ที่ใหญ่กว่า
  • การวัดกลางวงจรและ feedforward: Qubit 1 และ 2 (Qubit กลาง) ถูกวัดในฐาน computational basis ให้ classical bits สองตัว ตามผลลัพธ์การวัดเหล่านั้น เราทำการดำเนินการแบบมีเงื่อนไข: ถ้าการวัด Qubit 1 (เรียก bit นี้ว่า m12m_{12}) เป็น 1 เราทำ Gate XX บน Qubit 3; ถ้าการวัด Qubit 2 (m21m_{21}) เป็น 1 เราทำ Gate ZZ บน Qubit 0 Gate แบบมีเงื่อนไขเหล่านี้ (implement โดยใช้ construct if_test/if_else ของ Qiskit) ทำ teleportation correction มาตรฐาน พวกมัน "ยกเลิก" Pauli flip แบบสุ่มที่เกิดขึ้นจากการ project Qubit 1 และ 2 ทำให้มั่นใจว่า Qubit 0 และ 3 จบลงใน Bell state ที่ทราบ ไม่ว่าผลการวัดจะเป็นอย่างไร หลังจากขั้นตอนนี้ Qubit 0 และ 3 ควร entangle อยู่ใน Bell state Φ+|\Phi^+\rangle อย่างอุดมคติ
  • วัด Bell pair stabilizer: จากนั้นเราแยกออกเป็น Circuit สองเวอร์ชัน ในเวอร์ชันแรก เราวัด ZZZZ stabilizer บน Qubit 0 และ 3 ในเวอร์ชันที่สอง เราวัด XXXX stabilizer บน Qubit เหล่านี้

สำหรับแต่ละ initial layout สี่ Qubit ฟังก์ชันข้างต้นส่งคืน Circuit สองอัน (อันหนึ่งสำหรับ ZZZZ และอีกอันสำหรับการวัด XXXX stabilizer) ณ จุดสิ้นสุดของขั้นตอนนี้ เรามีรายการ Circuit ที่ครอบคลุมทุกสายโซ่ Qubit สี่ตัวบนอุปกรณ์ Circuit เหล่านี้รวมถึงการวัดกลางวงจรและการดำเนินการแบบมีเงื่อนไข (if/else) ซึ่งเป็นคำสั่งสำคัญของ dynamic circuit

circuits = create_bell_stab(initial_layouts)
circuits[-1].draw("mpl", fold=-1)

Output of the previous code cell

Step 2: ปรับแต่งปัญหาเพื่อรันบนฮาร์ดแวร์ควอนตัม

ก่อนที่จะรัน Circuit บนฮาร์ดแวร์จริง เราต้องทำการ Transpile เพื่อให้ตรงกับข้อจำกัดทางกายภาพของอุปกรณ์ Transpilation จะแมป Circuit นามธรรมไปยัง Qubit ทางกายภาพและชุด Gate ของอุปกรณ์ที่เลือก เนื่องจากเราได้เลือก Qubit ทางกายภาพเฉพาะสำหรับแต่ละเชนแล้ว (โดยระบุ initial_layout ให้กับตัวสร้าง Circuit) เราจึงใช้ Transpiler ด้วย optimization_level=0 พร้อม layout ที่ตรึงไว้ ซึ่งบอกให้ Qiskit ไม่ต้องจัดสรร Qubit ใหม่หรือทำการปรับแต่งหนักที่อาจเปลี่ยนแปลงโครงสร้าง Circuit เราต้องการรักษาลำดับการดำเนินงาน (โดยเฉพาะ Gate แบบมีเงื่อนไข) ให้ตรงตามที่ระบุไว้พอดี

isa_circuits = []
for ind, init_layout in enumerate(initial_layouts):
pm = generate_preset_pass_manager(
optimization_level=0, backend=backend, initial_layout=init_layout
)
isa_circ = pm.run(circuits[ind * 2 : ind * 2 + 2])
isa_circuits.extend(isa_circ)
isa_circuits[1].draw("mpl", fold=-1, idle_wires=False)

Output of the previous code cell

Step 3: รันด้วย Qiskit primitives

ตอนนี้เราสามารถรันการทดลองบนอุปกรณ์ควอนตัมได้แล้ว เราใช้ Qiskit Runtime และ Sampler primitive เพื่อรัน Circuit ชุดหนึ่งได้อย่างมีประสิทธิภาพ

sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["cut-bell-pair-test"]
job = sampler.run(isa_circuits)

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

ขั้นตอนสุดท้ายคือการคำนวณค่าตัวชี้วัด mean squared error (MSE) สำหรับแต่ละกลุ่ม Qubit ที่ทดสอบและสรุปผลลัพธ์ สำหรับแต่ละเชน ตอนนี้เรามีค่า Z0Z3\langle Z_0Z_3\rangle และ X0X3\langle X_0X_3\rangle ที่วัดได้ ถ้า Qubit 0 และ 3 พันกันอย่างสมบูรณ์แบบในสถานะ Bell Φ+|\Phi^+\rangle เราคาดว่าทั้งสองค่าจะเป็น +1 เราวัดปริมาณความเบี่ยงเบนโดยใช้ MSE:

MSE=(Z0Z31)2+(X0X31)22.\text{MSE} = \frac{( \langle Z_0Z_3\rangle - 1)^2 + (\langle X_0X_3\rangle - 1)^2}{2}.

ค่านี้เป็น 0 สำหรับ Bell pair ที่สมบูรณ์แบบ และเพิ่มขึ้นเมื่อสถานะพันกันมีสัญญาณรบกวนมากขึ้น (เมื่อผลลัพธ์สุ่ม ค่าความคาดหวังจะอยู่ประมาณ 0 ทำให้ MSE เข้าใกล้ 1) โค้ดจะคำนวณ MSE นี้สำหรับแต่ละกลุ่มสี่ Qubit

ผลลัพธ์แสดงให้เห็นถึงความหลากหลายอย่างกว้างขวางของคุณภาพการพันกันทั่วทั้งอุปกรณ์ ซึ่งยืนยันผลการค้นพบของงานวิจัยว่าความแตกต่างของ fidelity ของสถานะ Bell อาจมากกว่าหนึ่งระดับขนาด ขึ้นอยู่กับว่าใช้ Qubit ทางกายภาพใด ในทางปฏิบัติ หมายความว่าบางบริเวณหรือเชื่อมต่อบนชิปเก่งกว่าในการทำการวัดกลาง Circuit และการดำเนินการ feedforward ปัจจัยต่างๆ เช่น ข้อผิดพลาดในการอ่าน Qubit อายุขัยของ Qubit และ crosstalk น่าจะมีส่วนทำให้เกิดความแตกต่างเหล่านี้ ตัวอย่างเช่น ถ้าเชนหนึ่งมี Qubit อ่านค่าที่มีสัญญาณรบกวนมากเป็นพิเศษ การวัดกลาง Circuit อาจไม่น่าเชื่อถือ ส่งผลให้ fidelity ของคู่พันกันนั้นต่ำ (MSE สูง)

layouts_mse = get_mse(job.result(), initial_layouts)
layout [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
qubits: [0, 1, 2, 3], mse:, 0.0312
qubits: [4, 5, 6, 7], mse:, 0.0491
qubits: [8, 9, 10, 11], mse:, 0.0711
qubits: [12, 13, 14, 15], mse:, 0.0436

layout [16, 23, 22, 21, 17, 27, 26, 25, 18, 31, 30, 29, 19, 35, 34, 33]
qubits: [16, 23, 22, 21], mse:, 0.0197
qubits: [17, 27, 26, 25], mse:, 0.113
qubits: [18, 31, 30, 29], mse:, 0.0287
qubits: [19, 35, 34, 33], mse:, 0.0433

layout [36, 41, 42, 43, 37, 45, 46, 47, 38, 49, 50, 51, 39, 53, 54, 55]
qubits: [36, 41, 42, 43], mse:, 0.1645
qubits: [37, 45, 46, 47], mse:, 0.0409
qubits: [38, 49, 50, 51], mse:, 0.0519
qubits: [39, 53, 54, 55], mse:, 0.0829

layout [56, 63, 62, 61, 57, 67, 66, 65, 58, 71, 70, 69, 59, 75, 74, 73]
qubits: [56, 63, 62, 61], mse:, 0.8663
qubits: [57, 67, 66, 65], mse:, 0.0375
qubits: [58, 71, 70, 69], mse:, 0.0664
qubits: [59, 75, 74, 73], mse:, 0.0291

layout [76, 81, 82, 83, 77, 85, 86, 87, 78, 89, 90, 91, 79, 93, 94, 95]
qubits: [76, 81, 82, 83], mse:, 0.0598
qubits: [77, 85, 86, 87], mse:, 0.313
qubits: [78, 89, 90, 91], mse:, 0.0679
qubits: [79, 93, 94, 95], mse:, 0.0505

layout [96, 103, 102, 101, 97, 107, 106, 105, 98, 111, 110, 109, 99, 115, 114, 113]
qubits: [96, 103, 102, 101], mse:, 0.0302
qubits: [97, 107, 106, 105], mse:, 0.0384
qubits: [98, 111, 110, 109], mse:, 0.0375
qubits: [99, 115, 114, 113], mse:, 0.1051

layout [116, 121, 122, 123, 117, 125, 126, 127, 118, 129, 130, 131, 119, 133, 134, 135]
qubits: [116, 121, 122, 123], mse:, 0.1624
qubits: [117, 125, 126, 127], mse:, 0.7246
qubits: [118, 129, 130, 131], mse:, 0.5919
qubits: [119, 133, 134, 135], mse:, 0.5277

layout [136, 143, 142, 141, 137, 147, 146, 145, 138, 151, 150, 149, 139, 155, 154, 153]
qubits: [136, 143, 142, 141], mse:, 0.0383
qubits: [137, 147, 146, 145], mse:, 1.0187
qubits: [138, 151, 150, 149], mse:, 0.1531
qubits: [139, 155, 154, 153], mse:, 0.0471

สุดท้าย เราแสดงภาพผลการทำงานโดยรวมด้วยการพล็อต cumulative distribution function (CDF) ของค่า MSE สำหรับทุกเชน กราฟ CDF แสดงค่าเกณฑ์ MSE บนแกน x และสัดส่วนของคู่ Qubit ที่มี MSE ไม่เกินค่านั้นบนแกน y เส้นโค้งนี้เริ่มจากศูนย์และเข้าใกล้หนึ่งเมื่อเกณฑ์ขยายใหญ่ขึ้นจนครอบคลุมจุดข้อมูลทั้งหมด การขึ้นสูงชันใกล้ MSE ต่ำบ่งบอกว่าหลายคู่มี fidelity สูง ส่วนการขึ้นช้าหมายความว่าหลายคู่มีข้อผิดพลาดมากกว่า เราระบุ CDF ด้วยตัวตนของคู่ที่ดีที่สุด ในกราฟ แต่ละจุดใน CDF สอดคล้องกับ MSE ของเชนสี่ Qubit หนึ่งเชน และเราระบุจุดด้วยดัชนี Qubit คู่ [q0,q3][q0, q3] ที่พันกันในการทดลองนั้น ทำให้ง่ายต่อการระบุว่าคู่ Qubit ทางกายภาพใดเป็นผู้ทำงานได้ดีที่สุด (จุดซ้ายสุดบน CDF)

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

References

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