Sari la conținutul principal

Benchmarkează circuite dinamice cu perechi Bell tăiate

Estimare utilizare: 22 de secunde pe un procesor Heron r2 (NOTĂ: Aceasta este doar o estimare. Timpul tău de rulare poate varia.)

Rezultate de învățare

După parcurgerea acestui tutorial, utilizatorii ar trebui să înțeleagă:

  • Cum să construiești circuite dinamice cu măsurători în mijlocul circuitului și feedforward clasic pentru a teleporta entanglementul între Qubiți aflați la distanță;
  • Cum să calculezi și să interpretezi metrica de eroare pentru a cuantifica fidelitatea perechii Bell pe un dispozitiv;
  • Cum să identifici care perechi de Qubiți sunt cel mai potrivite pentru operațiunile bazate pe LOCC, folosind rezultatele benchmarkului.

Cerințe preliminare

Sugerăm utilizatorilor să fie familiarizați cu următoarele subiecte înainte de a parcurge acest tutorial:

Context

Hardware-ul cuantic este de obicei limitat la interacțiuni locale, dar mulți algoritmi necesită entanglamentul unor Qubiți aflați la distanță sau chiar a Qubiților de pe procesoare separate. Circuitele dinamice — adică circuitele cu măsurători la mijlocul circuitului și feedforward — oferă o metodă de a depăși aceste limitări prin utilizarea comunicării clasice în timp real, pentru a implementa efectiv operații cuantice non-locale. În această abordare, rezultatele măsurătorilor dintr-o parte a circuitului (sau de pe un QPU) pot declanșa condiționat porți pe altul, permițându-ne să teleportăm entanglementul pe distanțe mari. Aceasta stă la baza schemelor de operații locale și comunicare clasică (LOCC), unde consumăm stări resursă entanglate (perechi Bell) și comunicăm clasic rezultatele măsurătorilor pentru a conecta Qubiți aflați la distanță.

O utilizare promițătoare a LOCC este realizarea porților CNOT virtuale pe distanță lungă prin teleportare, așa cum este prezentat în tutorialul de entanglement pe distanță lungă. În loc de un CNOT direct pe distanță lungă (pe care conectivitatea hardware-ului s-ar putea să nu o permită), creăm perechi Bell și realizăm o implementare a porții bazată pe teleportare. Totuși, fidelitatea unor astfel de operații depinde de caracteristicile hardware-ului. Decoerența Qubitului în timpul întârzierii necesare (în timp ce se așteaptă rezultatele măsurătorilor) și latența comunicării clasice pot degrada starea entanglată. De asemenea, erorile la măsurătorile la mijlocul circuitului sunt mai greu de corectat decât erorile la măsurătorile finale, deoarece se propagă în restul circuitului prin porțile condiționale.

În experimentul de referință, autorii introduc un benchmark de fidelitate a perechii Bell pentru a identifica care părți ale unui dispozitiv sunt cel mai potrivite pentru entanglamentul bazat pe LOCC. Ideea este de a rula un mic circuit dinamic pe fiecare grup de patru Qubiți conectați din procesor. Acest circuit de patru Qubiți creează mai întâi o pereche Bell pe doi Qubiți din mijloc, apoi îi folosește ca resursă pentru a entangla cei doi Qubiți de la margine, utilizând LOCC. Concret, Qubiții 1 și 2 sunt pregătiți local într-o pereche Bell netăiată (o pereche Bell creată direct cu un Hadamard și CNOT, fără teleportare), iar apoi o rutină de teleportare consumă acea pereche Bell pentru a entangla Qubiții 0 și 3. Qubiții 1 și 2 sunt măsurați în timpul execuției circuitului, iar pe baza acelor rezultate, se aplică corecții Pauli (un X pe Qubitul 3 și Z pe Qubitul 0). Qubiții 0 și 3 sunt lăsați astfel într-o stare Bell la finalul circuitului.

Pentru a cuantifica calitatea acestei perechi finale entanglate, măsurăm stabilizatorii săi: mai precis, paritatea în baza ZZ (Z0Z3Z_0Z_3) și în baza XX (X0X3X_0X_3). Pentru o pereche Bell perfectă, ambele așteptări sunt egale cu +1. În practică, zgomotul hardware-ului va reduce aceste valori. Prin urmare, repetăm circuitul de două ori pentru fiecare pereche de Qubiți: un circuit măsoară Qubiții 0 și 3 în baza ZZ, iar altul îi măsoară în baza XX. Din rezultate, obținem o estimare a Z0Z3\langle Z_0Z_3\rangle și X0X3\langle X_0X_3\rangle pentru acea pereche de Qubiți. Folosim eroarea medie pătratică (MSE) a acestor stabilizatori față de valoarea ideală (1) ca o metrică simplă a fidelității de entanglement. Un MSE mai mic înseamnă că cei doi Qubiți au atins o stare Bell mai apropiată de ideal (fidelitate mai mare), în timp ce un MSE mai mare indică mai multe erori. Prin scanarea acestui experiment pe dispozitiv, putem benchmarka capacitatea de măsurare și feedforward a diferitelor grupuri de Qubiți și identifica cele mai bune perechi de Qubiți pentru operațiile LOCC.

Acest tutorial demonstrează experimentul pe un dispozitiv IBM Quantum® pentru a ilustra cum pot fi utilizate circuitele dinamice pentru a genera și evalua entanglementul între Qubiți aflați la distanță. Vom cartografia toate lanțurile liniare de patru Qubiți de pe dispozitiv, vom rula circuitul de teleportare pe fiecare și vom vizualiza distribuția valorilor MSE. Această procedură end-to-end arată cum se poate folosi Qiskit Runtime și funcțiile circuitelor dinamice pentru a informa alegerile conștiente de hardware la tăierea circuitelor sau distribuirea algoritmilor cuantici pe un sistem modular.

Cerințe

Înainte de a începe acest tutorial, asigură-te că ai instalat următoarele:

  • Qiskit SDK v2.0 sau versiune ulterioară, cu suport de vizualizare
  • Qiskit Runtime v0.40 sau versiune ulterioară (pip install qiskit-ibm-runtime)
  • Qiskit Aer v0.17 sau versiune ulterioară (pip install qiskit-aer)

Configurare

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer 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
), "The length of the chain must be a multiple of 4, "
f"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:, "
f"{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()

Exemplu la scară mică cu simulator

Înainte de a rula pe QPU-ul real, verificăm că circuitul produce o pereche Bell testând-ul pe un simulator fără zgomot cu un lanț de patru Qubiți [0, 1, 2, 3]. Folosim Sampler-ul Qiskit Runtime cu AerSimulator ca mod backend pentru a executa circuitele.

Pasul 1: Maparea intrărilor clasice la o problemă cuantică

Primul pas este să creezi un set de circuite cuantice pentru a evalua toate legăturile candidate de perechi Bell, adaptate topologiei dispozitivului. Începem prin construirea unor astfel de circuite cu un lanț de patru Qubiți [0, 1, 2, 3].

Rutina create_bell_stab() face următoarele pentru fiecare lanț:

  • Pregătirea unei perechi Bell de mijloc: Aplică un Hadamard pe Qubitul 1 și un CNOT de la Qubitul 1 la Qubitul 2. Aceasta entanglează Qubiții 1 și 2 (creând o stare Bell Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2}).
  • Entanglarea Qubiților de margine: Aplică un CNOT de la Qubitul 0 la Qubitul 1, și un CNOT de la Qubitul 2 la Qubitul 3. Aceasta leagă perechile inițial separate, astfel încât Qubiții 0 și 3 să devină entanglați după pașii următori. Se aplică și un Hadamard pe Qubitul 2 (acesta, combinat cu CNOT-urile anterioare, formează parte dintr-o măsurătoare Bell pe Qubiții 1 și 2). În acest punct, Qubiții 0 și 3 nu sunt încă entanglați, dar Qubiții 1 și 2 sunt entanglați cu ei într-o stare de patru Qubiți mai largă.
  • Măsurători în mijlocul circuitului și feedforward: Qubiții 1 și 2 (Qubiții de mijloc) sunt măsurați în baza computațională, producând doi biți clasici. Pe baza acelor rezultate ale măsurătorilor, aplicăm operații condiționale: dacă măsurătoarea Qubitului 1 (numim acest bit m12m_{12}) este 1, aplicăm o poartă XX pe Qubitul 3; dacă măsurătoarea Qubitului 2 (m21m_{21}) este 1, aplicăm o poartă ZZ pe Qubitul 0. Aceste porți condiționale (realizate folosind constructul Qiskit if_test/if_else) implementează corecțiile standard de teleportare. Ele „anulează" flip-urile Pauli aleatorii care apar din cauza proiectării Qubiților 1 și 2, asigurând că Qubiții 0 și 3 ajung într-o stare Bell cunoscută, indiferent de rezultatele măsurătorilor. După acest pas, Qubiții 0 și 3 ar trebui să fie idealmente entanglați în starea Bell Φ+|\Phi^+\rangle.
  • Măsurarea stabilizatorilor perechii Bell: Împărțim apoi în două versiuni ale circuitului. În prima versiune, măsurăm stabilizatorul ZZZZ pe Qubiții 0 și 3. În a doua versiune, măsurăm stabilizatorul XXXX pe acești Qubiți.

Pentru fiecare aranjament inițial de patru Qubiți, funcția de mai sus returnează două circuite (unul pentru măsurarea stabilizatorului ZZZZ, unul pentru XXXX). Aceste circuite includ măsurători în mijlocul circuitului și operații condiționale (if/else), care sunt instrucțiunile cheie ale circuitului dinamic.

from qiskit_aer import AerSimulator

# 4-qubit chain for simulation
sim_layout = [[0, 1, 2, 3]]

aer_backend = AerSimulator()
sim_circuits = create_bell_stab(sim_layout)
sim_circuits[1].draw("mpl", fold=-1, idle_wires=False)

Output of the previous code cell

Pasul 2: Optimizarea problemei pentru execuția pe hardware cuantic

Înainte de a executa circuitele noastre, trebuie să le transpilăm la operații de porți suportate pe backend-ul specificat. Transpilarea va mapa circuitul abstract pe Qubiții fizici și setul de porți al backend-ului ales. Deoarece am ales deja Qubiții fizici specifici pentru fiecare lanț (furnizând un initial_layout generatorului de circuite), folosim optimization_level=0 al transpilerului cu acel aranjament fix. Astfel îi spunem lui Qiskit să nu reasigneze Qubiți sau să efectueze optimizări grele care ar putea altera structura circuitului. Dorim să păstrăm secvența de operații (în special porțile condiționale) exact așa cum a fost specificată.

pm_sim = generate_preset_pass_manager(
optimization_level=0, backend=aer_backend, initial_layout=sim_layout[0]
)
isa_sim_circuits = pm_sim.run(sim_circuits)
isa_sim_circuits[1].draw("mpl", fold=-1, idle_wires=False)

Pasul 3: Execuția folosind primitivele Qiskit

Putem acum rula experimentul pe backend-ul simulator fără zgomot.

# Run on noiseless simulator
sampler_sim = Sampler(mode=aer_backend)
sim_job = sampler_sim.run(isa_sim_circuits)
sim_mse = get_mse(sim_job.result(), sim_layout)
layout [0, 1, 2, 3]
qubits: [0, 1, 2, 3], mse:, 0.0

Pasul 4: Post-procesare și returnarea rezultatului în formatul clasic dorit

Ultimul pas este să calculăm metrica erorii medii pătratice (MSE) pentru fiecare grup de Qubiți testat și să rezumăm rezultatele. Pentru fiecare lanț, avem acum Z0Z3\langle Z_0Z_3\rangle și X0X3\langle X_0X_3\rangle măsurate. Dacă Qubiții 0 și 3 ar fi perfect entanglați într-o stare Bell Φ+|\Phi^+\rangle, ne-am aștepta ca ambele să fie +1. Cuantificăm deviația folosind MSE:

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

Această valoare este 0 pentru o pereche Bell perfectă și crește pe măsură ce starea entanglată devine mai zgomotoasă (cu rezultate aleatorii care dau o valoare de așteptare în jurul valorii 0, MSE s-ar apropia de 1). Codul calculează acest MSE pentru fiecare grup de patru Qubiți. Fără zgomot, vedem MSE = 0 în acest exemplu la scară mică cu simulator, așa cum era de așteptat.

Exemplu la scară largă pe hardware real

Aici reunim toate aceste detalii într-un flux de lucru unitar la o scară mai mare, care este apoi rulat pe hardware cuantic real.

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

Căutăm programatic în harta de cuplare a dispozitivului toate lanțurile de patru Qubiți conectați liniar. Fiecare astfel de lanț (etichetat prin indicii Qubiților [q0q1q2q3][q0-q1-q2-q3]) servește drept caz de test pentru circuitul de schimb de entanglement. Identificând toate căile de lungime 4 posibile, asigurăm acoperirea maximă pentru posibilele grupări de Qubiți care ar putea implementa protocolul.

Generăm aceste lanțuri folosind o funcție auxiliară care efectuează o căutare greedy pe graful dispozitivului. Ea returnează „dungi" de patru lanțuri de câte patru Qubiți, grupate în grupuri de 16 Qubiți. Gruparea ne permite să rulăm mai multe experimente de câte patru Qubiți în paralel pe porțiuni distincte ale cipului și să utilizăm eficient întregul dispozitiv. Fiecare dungă de 16 Qubiți conține patru lanțuri disjuncte, ceea ce înseamnă că niciun qubit nu este reutilizat în cadrul acelui grup. De exemplu, o dungă ar putea consta din lanțurile [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] și [12131415][12-13-14-15] toate împachetate împreună. Orice qubit care nu a fost inclus într-o dungă este returnat în variabila 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)

Apoi, construim circuitul pentru fiecare dungă de 16 Qubiți folosind funcția create_bell_stab(). La sfârșitul acestui pas, avem o listă de circuite care acoperă fiecare lanț de patru Qubiți de pe dispozitiv. Transpilăm și rulăm apoi circuitele pe backend-ul real și post-procesăm rezultatele.

# -------------------------Step 1-------------------------
circuits = create_bell_stab(initial_layouts)

# -------------------------Step 2-------------------------
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)

# -------------------------Step 3-------------------------
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["TUT_BDC"]
job = sampler.run(isa_circuits)

# -------------------------Step 4-------------------------
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.6302
qubits: [4, 5, 6, 7], mse:, 0.0949
qubits: [8, 9, 10, 11], mse:, 0.1729
qubits: [12, 13, 14, 15], mse:, 0.0473

layout [16, 23, 22, 21, 17, 27, 26, 25, 18, 31, 30, 29, 19, 35, 34, 33]
qubits: [16, 23, 22, 21], mse:, 0.0533
qubits: [17, 27, 26, 25], mse:, 0.2966
qubits: [18, 31, 30, 29], mse:, 0.0447
qubits: [19, 35, 34, 33], mse:, 0.0392

layout [36, 41, 42, 43, 37, 45, 46, 47, 38, 49, 50, 51, 39, 53, 54, 55]
qubits: [36, 41, 42, 43], mse:, 0.1577
qubits: [37, 45, 46, 47], mse:, 0.0705
qubits: [38, 49, 50, 51], mse:, 0.2914
qubits: [39, 53, 54, 55], mse:, 0.1711

layout [56, 63, 62, 61, 57, 67, 66, 65, 58, 71, 70, 69, 59, 75, 74, 73]
qubits: [56, 63, 62, 61], mse:, 0.1236
qubits: [57, 67, 66, 65], mse:, 0.9969
qubits: [58, 71, 70, 69], mse:, 0.0631
qubits: [59, 75, 74, 73], mse:, 0.0301

layout [76, 81, 82, 83, 77, 85, 86, 87, 78, 89, 90, 91, 79, 93, 94, 95]
qubits: [76, 81, 82, 83], mse:, 0.2787
qubits: [77, 85, 86, 87], mse:, 0.0497
qubits: [78, 89, 90, 91], mse:, 0.1271
qubits: [79, 93, 94, 95], mse:, 0.0468

layout [96, 103, 102, 101, 97, 107, 106, 105, 98, 111, 110, 109, 99, 115, 114, 113]
qubits: [96, 103, 102, 101], mse:, 0.8657
qubits: [97, 107, 106, 105], mse:, 0.0399
qubits: [98, 111, 110, 109], mse:, 0.0667
qubits: [99, 115, 114, 113], mse:, 0.2444

layout [116, 121, 122, 123, 117, 125, 126, 127, 118, 129, 130, 131, 119, 133, 134, 135]
qubits: [116, 121, 122, 123], mse:, 0.0429
qubits: [117, 125, 126, 127], mse:, 0.0487
qubits: [118, 129, 130, 131], mse:, 0.0823
qubits: [119, 133, 134, 135], mse:, 0.0583

layout [136, 143, 142, 141, 137, 147, 146, 145, 138, 151, 150, 149, 139, 155, 154, 153]
qubits: [136, 143, 142, 141], mse:, 0.0209
qubits: [137, 147, 146, 145], mse:, 0.0384
qubits: [138, 151, 150, 149], mse:, 0.4941
qubits: [139, 155, 154, 153], mse:, 0.1062

Rezultatele dezvăluie o gamă largă de calitate a entanglementului pe întreg dispozitivul. Aceasta confirmă descoperirea din articol că poate exista o variație de peste un ordin de mărime în fidelitatea stării Bell, în funcție de ce Qubiți fizici sunt folosiți. Din punct de vedere practic, aceasta înseamnă că anumite regiuni sau legături din cip sunt mult mai bune pentru operațiunile de măsurare în mijlocul circuitului și feedforward decât altele. Factori precum eroarea de citire a Qubitului, durata de viață a Qubitului și crosstalk-ul contribuie probabil la aceste diferențe. De exemplu, dacă un lanț include un qubit de citire deosebit de zgomotos, măsurătoarea din mijlocul circuitului poate fi nesigură, ducând la o fidelitate slabă pentru acea pereche entanglată (MSE ridicat).

În final, vizualizăm performanța globală prin reprezentarea funcției de distribuție cumulativă (CDF) a valorilor MSE pentru toate lanțurile. Graficul CDF arată pragul MSE pe axa x și fracțiunea perechilor de Qubiți cu MSE cel mult egal cu acel prag pe axa y. Această curbă pornește de la zero și se apropie de unu pe măsură ce pragul crește pentru a cuprinde toate punctele de date. O creștere abruptă lângă un MSE scăzut ar indica că multe perechi au fidelitate ridicată; o creștere lentă înseamnă că multe perechi au erori mai mari. Adnotăm CDF-ul cu identitățile celor mai bune perechi. În grafic, fiecare punct din CDF corespunde MSE-ului unui lanț de patru Qubiți, și etichetăm punctul cu perechea de indici de Qubiți [q0,q3][q0, q3] care au fost entanglați în acel experiment. Aceasta facilitează identificarea perechilor de Qubiți fizici cu cele mai bune performanțe (punctele din extrema stângă a CDF-ului).

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

Pași următori

Recomandări

Dacă ai găsit această lucrare interesantă, s-ar putea să te intereseze și următoarele materiale:

Referințe

[1] Carrera Vazquez, A., Tornow, C., Ristè, D. et al. Combining quantum processors with real-time classical communication. Nature 636, 75-79 (2024).