Aller au contenu principal

Benchmarker les circuits dynamiques avec des paires de Bell coupées

Estimation d'utilisation : 22 secondes sur un processeur Heron r2 (REMARQUE : il s'agit uniquement d'une estimation. Votre temps d'exécution peut varier.)

Contexte

Le matériel quantique est généralement limité aux interactions locales, mais de nombreux algorithmes nécessitent d'intriquer des qubits distants ou même des qubits sur des processeurs séparés. Les circuits dynamiques — c'est-à-dire des circuits avec des mesures en milieu de circuit et de la rétroaction conditionnelle — offrent un moyen de surmonter ces limitations en utilisant la communication classique en temps réel pour implémenter efficacement des opérations quantiques non locales. Dans cette approche, les résultats de mesure d'une partie du circuit (ou d'un QPU) peuvent déclencher conditionnellement des portes sur une autre partie, ce qui permet de téléporter l'intrication sur de longues distances. Cela constitue la base des schémas d'opérations locales et communication classique (LOCC), où l'on consomme des états de ressource intriqués (paires de Bell) et communique les résultats de mesure de manière classique pour relier des qubits distants.

Une utilisation prometteuse du LOCC est la réalisation de portes CNOT virtuelles à longue portée par téléportation, comme illustré dans le tutoriel sur l'intrication à longue portée. Au lieu d'un CNOT direct à longue portée (que la connectivité du matériel pourrait ne pas permettre), on crée des paires de Bell et on effectue une implémentation de porte basée sur la téléportation. Cependant, la fidélité de telles opérations dépend des caractéristiques du matériel. La décohérence des qubits pendant le délai nécessaire (en attendant les résultats de mesure) et la latence de communication classique peuvent dégrader l'état intriqué. De plus, les erreurs sur les mesures en milieu de circuit sont plus difficiles à corriger que les erreurs sur les mesures finales, car elles se propagent au reste du circuit à travers les portes conditionnelles.

Dans l'expérience de référence, les auteurs introduisent un benchmark de fidélité de paire de Bell pour identifier quelles parties d'un dispositif sont les mieux adaptées à l'intrication basée sur le LOCC. L'idée est d'exécuter un petit circuit dynamique sur chaque groupe de quatre qubits connectés du processeur. Ce circuit à quatre qubits crée d'abord une paire de Bell sur les deux qubits centraux, puis utilise celle-ci comme ressource pour intriquer les deux qubits aux extrémités en utilisant le LOCC. Concrètement, les qubits 1 et 2 sont préparés dans une paire de Bell non coupée localement (en utilisant une porte Hadamard et un CNOT), puis une routine de téléportation consomme cette paire de Bell pour intriquer les qubits 0 et 3. Les qubits 1 et 2 sont mesurés pendant l'exécution du circuit, et en fonction de ces résultats, des corrections de Pauli (un XX sur le qubit 3 et un ZZ sur le qubit 0) sont appliquées. Les qubits 0 et 3 se retrouvent alors dans un état de Bell à la fin du circuit.

Pour quantifier la qualité de cette paire intriquée finale, nous mesurons ses stabilisateurs : plus précisément, la parité dans la base ZZ (Z0Z3Z_0Z_3) et dans la base XX (X0X3X_0X_3). Pour une paire de Bell parfaite, ces deux valeurs d'espérance sont égales à +1. En pratique, le bruit du matériel réduira ces valeurs. Nous répétons donc le circuit deux fois pour chaque paire de qubits : un circuit mesure les qubits 0 et 3 dans la base ZZ, et un autre les mesure dans la base XX. À partir des résultats, nous obtenons une estimation de Z0Z3\langle Z_0Z_3\rangle et X0X3\langle X_0X_3\rangle pour cette paire de qubits. Nous utilisons l'erreur quadratique moyenne (MSE) de ces stabilisateurs par rapport à la valeur idéale (1) comme métrique simple de fidélité d'intrication. Un MSE plus faible signifie que les deux qubits ont atteint un état de Bell plus proche de l'idéal (fidélité plus élevée), tandis qu'un MSE plus élevé indique davantage d'erreurs. En balayant cette expérience sur l'ensemble du dispositif, nous pouvons benchmarker la capacité de mesure et de rétroaction conditionnelle des différents groupes de qubits et identifier les meilleures paires de qubits pour les opérations LOCC.

Ce tutoriel démontre l'expérience sur un dispositif IBM Quantum® pour illustrer comment les circuits dynamiques peuvent être utilisés pour générer et évaluer l'intrication entre des qubits distants. Nous allons recenser toutes les chaînes linéaires de quatre qubits sur le dispositif, exécuter le circuit de téléportation sur chacune d'elles, puis visualiser la distribution des valeurs de MSE. Cette procédure de bout en bout montre comment exploiter Qiskit Runtime et les fonctionnalités des circuits dynamiques pour guider des choix tenant compte du matériel lors de la découpe de circuits ou de la distribution d'algorithmes quantiques sur un système modulaire.

Prérequis

Avant de commencer ce tutoriel, assurez-vous d'avoir installé les éléments suivants :

  • Qiskit SDK v2.0 ou ultérieur, avec le support de visualisation
  • Qiskit Runtime v0.40 ou ultérieur (pip install qiskit-ibm-runtime)

Configuration

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()

Étape 1 : Associer les entrées classiques à un problème quantique

La première étape consiste à créer un ensemble de circuits quantiques pour benchmarker tous les liens candidats de paires de Bell, adaptés à la topologie du dispositif. Nous recherchons programmatiquement dans la carte de couplage du dispositif toutes les chaînes linéairement connectées de quatre qubits. Chaque chaîne de ce type (identifiée par les indices de qubits [q0q1q2q3][q0-q1-q2-q3]) sert de cas de test pour le circuit d'échange d'intrication. En identifiant tous les chemins possibles de longueur 4, nous assurons une couverture maximale pour tout regroupement possible de qubits susceptible de réaliser le protocole.

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

Nous générons ces chaînes à l'aide d'une fonction auxiliaire qui effectue une recherche gloutonne sur le graphe du dispositif. Elle renvoie des « bandes » de quatre chaînes de quatre qubits regroupées en groupes de 16 qubits (les circuits dynamiques limitent actuellement la taille du registre de mesure à 16 qubits). Le regroupement nous permet d'exécuter plusieurs expériences à quatre qubits en parallèle sur différentes parties de la puce et d'utiliser efficacement l'ensemble du dispositif. Chaque bande de 16 qubits contient quatre chaînes disjointes, ce qui signifie qu'aucun qubit n'est réutilisé au sein de ce groupe. Par exemple, une bande pourrait être composée des chaînes [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] et [12131415][12-13-14-15], toutes regroupées ensemble. Tout qubit non inclus dans une bande est renvoyé dans la variable 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)

Ensuite, nous construisons le circuit pour chaque bande de 16 qubits. La routine effectue les opérations suivantes pour chaque chaîne :

  • Préparer une paire de Bell centrale : Appliquer une porte Hadamard sur le qubit 1 et un CNOT du qubit 1 vers le qubit 2. Cela intrique les qubits 1 et 2 (créant un état de Bell Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2}).
  • Intriquer les qubits aux extrémités : Appliquer un CNOT du qubit 0 vers le qubit 1, et un CNOT du qubit 2 vers le qubit 3. Cela relie les paires initialement séparées de sorte que les qubits 0 et 3 deviennent intriqués après les étapes suivantes. Une porte Hadamard est également appliquée sur le qubit 2 (celle-ci, combinée aux CNOT précédents, fait partie d'une mesure de Bell sur les qubits 1 et 2). À ce stade, les qubits 0 et 3 ne sont pas encore intriqués, mais les qubits 1 et 2 sont intriqués avec eux dans un état à quatre qubits plus large.
  • Mesures en milieu de circuit et rétroaction conditionnelle : Les qubits 1 et 2 (les qubits centraux) sont mesurés dans la base computationnelle, produisant deux bits classiques. En fonction de ces résultats de mesure, nous appliquons des opérations conditionnelles : si la mesure du qubit 1 (appelons ce bit m12m_{12}) vaut 1, nous appliquons une porte XX sur le qubit 3 ; si la mesure du qubit 2 (m21m_{21}) vaut 1, nous appliquons une porte ZZ sur le qubit 0. Ces portes conditionnelles (réalisées à l'aide du mécanisme if_test/if_else de Qiskit) implémentent les corrections standard de téléportation. Elles « annulent » les retournements de Pauli aléatoires qui se produisent lors de la projection des qubits 1 et 2, garantissant que les qubits 0 et 3 se retrouvent dans un état de Bell connu, indépendamment des résultats de mesure. Après cette étape, les qubits 0 et 3 devraient idéalement être intriqués dans l'état de Bell Φ+|\Phi^+\rangle.
  • Mesurer les stabilisateurs de la paire de Bell : Nous séparons ensuite le circuit en deux versions. Dans la première version, nous mesurons le stabilisateur ZZZZ sur les qubits 0 et 3. Dans la seconde version, nous mesurons le stabilisateur XXXX sur ces qubits.

Pour chaque disposition initiale de quatre qubits, la fonction ci-dessus renvoie deux circuits (un pour la mesure du stabilisateur ZZZZ, un pour celle du stabilisateur XXXX). À la fin de cette étape, nous disposons d'une liste de circuits couvrant chaque chaîne de quatre qubits sur le dispositif. Ces circuits incluent des mesures en milieu de circuit et des opérations conditionnelles (if/else), qui sont les instructions clés des circuits dynamiques.

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

Output of the previous code cell

Étape 2 : Optimiser le problème pour l'exécution sur le matériel quantique

Avant d'exécuter nos circuits sur du matériel réel, nous devons les transpiler pour qu'ils correspondent aux contraintes physiques du dispositif. La transpilation va associer le circuit abstrait aux qubits physiques et au jeu de portes du dispositif choisi. Puisque nous avons déjà choisi des qubits physiques spécifiques pour chaque chaîne (en fournissant un initial_layout au générateur de circuits), nous utilisons le transpileur avec optimization_level=0 et cette disposition fixe. Cela indique à Qiskit de ne pas réassigner les qubits ni d'effectuer d'optimisations lourdes qui pourraient altérer la structure du circuit. Nous voulons conserver la séquence d'opérations (en particulier les portes conditionnelles) exactement telle que spécifiée.

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

Étape 3 : Exécuter à l'aide des primitives Qiskit

Nous pouvons maintenant exécuter l'expérience sur le dispositif quantique. Nous utilisons Qiskit Runtime et sa primitive Sampler pour exécuter le lot de circuits de manière efficace.

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

Étape 4 : Post-traiter et renvoyer le résultat dans le format classique souhaité

La dernière étape consiste à calculer la métrique d'erreur quadratique moyenne (MSE) pour chaque groupe de qubits testé et à résumer les résultats. Pour chaque chaîne, nous disposons maintenant des valeurs mesurées de Z0Z3\langle Z_0Z_3\rangle et X0X3\langle X_0X_3\rangle. Si les qubits 0 et 3 étaient parfaitement intriqués dans un état de Bell Φ+|\Phi^+\rangle, nous nous attendrions à ce que ces deux valeurs soient égales à +1. Nous quantifions l'écart à l'aide du MSE :

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

Cette valeur est 0 pour une paire de Bell parfaite et augmente à mesure que l'état intriqué devient plus bruité (avec des résultats aléatoires donnant une espérance autour de 0, le MSE tendrait vers 1). Le code calcule ce MSE pour chaque groupe de quatre qubits.

Les résultats révèlent une grande variété de qualité d'intrication à travers le dispositif. Cela confirme la conclusion de l'article selon laquelle il peut y avoir plus d'un ordre de grandeur de variation dans la fidélité de l'état de Bell en fonction des qubits physiques utilisés. En termes pratiques, cela signifie que certaines régions ou liens de la puce sont bien meilleurs que d'autres pour effectuer des opérations de mesure en milieu de circuit et de rétroaction conditionnelle. Des facteurs tels que l'erreur de lecture des qubits, le temps de vie des qubits et la diaphonie contribuent probablement à ces différences. Par exemple, si une chaîne inclut un qubit de lecture particulièrement bruité, la mesure en milieu de circuit pourrait être peu fiable, conduisant à une mauvaise fidélité pour cette paire intriquée (MSE élevé).

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

Enfin, nous visualisons la performance globale en traçant la fonction de répartition empirique (CDF) des valeurs de MSE pour toutes les chaînes. Le graphique CDF montre le seuil de MSE sur l'axe des abscisses et la fraction de paires de qubits ayant au plus ce MSE sur l'axe des ordonnées. Cette courbe commence à zéro et tend vers un à mesure que le seuil augmente pour englober tous les points de données. Une montée abrupte près d'un faible MSE indiquerait que de nombreuses paires sont de haute fidélité ; une montée lente signifie que de nombreuses paires présentent des erreurs plus importantes. Nous annotons la CDF avec l'identité des meilleures paires. Sur le graphique, chaque point de la CDF correspond au MSE d'une chaîne de quatre qubits, et nous étiquetons le point avec la paire d'indices de qubits [q0,q3][q0, q3] qui ont été intriqués dans cette expérience. Cela permet de repérer facilement quelles paires de qubits physiques sont les plus performantes (les points les plus à gauche sur la CDF).

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

Références

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