Pular para o conteúdo principal

Avalie circuitos dinâmicos com pares de Bell cortados

Estimativa de uso: 22 segundos em um processador Heron r2 (NOTA: Esta é apenas uma estimativa. Seu tempo de execução pode variar.)

Contexto

O hardware quântico é tipicamente limitado a interações locais, mas muitos algoritmos requerem o emaranhamento de qubits distantes ou até mesmo qubits em processadores separados. Circuitos dinâmicos - isto é, circuitos com medições intermediárias e feedforward - fornecem uma maneira de superar essas limitações usando comunicação clássica em tempo real para implementar efetivamente operações quânticas não-locais. Nesta abordagem, resultados de medições de uma parte de um circuito (ou uma QPU) podem condicionalmente acionar portas em outra, permitindo-nos teleportar o emaranhamento através de longas distâncias. Isso forma a base dos esquemas de operações locais e comunicação clássica (LOCC), onde consumimos estados de recursos emaranhados (pares de Bell) e comunicamos resultados de medições classicamente para conectar qubits distantes.

Um uso promissor de LOCC é realizar portas CNOT virtuais de longo alcance por teletransporte, como mostrado no tutorial de emaranhamento de longo alcance. Em vez de uma CNOT direta de longo alcance (que a conectividade do hardware pode não permitir), criamos pares de Bell e realizamos uma implementação de porta baseada em teletransporte. No entanto, a fidelidade de tais operações depende das características do hardware. A decoerência do qubit durante o atraso necessário (enquanto aguardamos os resultados das medições) e a latência de comunicação clássica podem degradar o estado emaranhado. Além disso, erros em medições intermediárias são mais difíceis de corrigir do que erros em medições finais, pois eles se propagam para o resto do circuito através das portas condicionais.

No experimento de referência, os autores introduzem um benchmark de fidelidade de pares de Bell para identificar quais partes de um dispositivo são mais adequadas para emaranhamento baseado em LOCC. A ideia é executar um pequeno circuito dinâmico em cada grupo de quatro qubits conectados no processador. Este circuito de quatro qubits primeiro cria um par de Bell nos dois qubits do meio, depois usa esses como um recurso para emaranhar os dois qubits das extremidades usando LOCC. Concretamente, os qubits 1 e 2 são preparados em um par de Bell não cortado localmente (usando um Hadamard e CNOT), e então uma rotina de teletransporte consome esse par de Bell para emaranhar os qubits 0 e 3. Os qubits 1 e 2 são medidos durante a execução do circuito, e com base nesses resultados, correções de Pauli (um X no qubit 3 e Z no qubit 0) são aplicadas. Os qubits 0 e 3 são então deixados em um estado de Bell no final do circuito.

Para quantificar a qualidade deste par emaranhado final, medimos seus estabilizadores: especificamente, a paridade na base ZZ (Z0Z3Z_0Z_3) e na base XX (X0X3X_0X_3). Para um par de Bell perfeito, ambas essas expectativas são iguais a +1. Na prática, o ruído do hardware reduzirá esses valores. Portanto, repetimos o circuito duas vezes para cada par de qubits: um circuito mede os qubits 0 e 3 na base ZZ, e outro os mede na base XX. A partir dos resultados, obtemos uma estimativa de Z0Z3\langle Z_0Z_3\rangle e X0X3\langle X_0X_3\rangle para esse par de qubits. Usamos o erro quadrático médio (MSE) desses estabilizadores em relação ao valor ideal (1) como uma métrica simples de fidelidade de emaranhamento. Um MSE menor significa que os dois qubits alcançaram um estado de Bell mais próximo do ideal (maior fidelidade), enquanto um MSE maior indica mais erro. Ao escanear este experimento pelo dispositivo, podemos avaliar a capacidade de medição e feedforward de diferentes grupos de qubits e identificar os melhores pares de qubits para operações LOCC.

Este tutorial demonstra o experimento em um dispositivo IBM Quantum® para ilustrar como circuitos dinâmicos podem ser usados para gerar e avaliar o emaranhamento entre qubits distantes. Mapearemos todas as cadeias lineares de quatro qubits no dispositivo, executaremos o circuito de teletransporte em cada uma e então visualizaremos a distribuição dos valores de MSE. Este procedimento completo mostra como aproveitar o Qiskit Runtime e recursos de circuitos dinâmicos para informar escolhas conscientes do hardware para cortar circuitos ou distribuir algoritmos quânticos em um sistema modular.

Requisitos

Antes de iniciar este tutorial, certifique-se de ter o seguinte instalado:

  • Qiskit SDK v2.0 ou posterior, com suporte para visualização
  • Qiskit Runtime v0.40 ou posterior (pip install qiskit-ibm-runtime)

Configuração

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

Passo 1: Mapear entradas clássicas para um problema quântico

O primeiro passo é criar um conjunto de circuitos quânticos para avaliar todos os links candidatos de pares de Bell adaptados à topologia do dispositivo. Pesquisamos programaticamente o mapa de acoplamento do dispositivo para todas as cadeias linearmente conectadas de quatro qubits. Cada uma dessas cadeias (rotuladas por índices de qubit [q0q1q2q3][q0-q1-q2-q3]) serve como um caso de teste para o circuito de troca de emaranhamento. Ao identificar todos os possíveis caminhos de comprimento 4, garantimos cobertura máxima para possíveis agrupamentos de qubits que poderiam realizar o protocolo.

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

Geramos essas cadeias usando uma função auxiliar que realiza uma busca gulosa no grafo do dispositivo. Ela retorna "faixas" de quatro cadeias de quatro qubits agrupadas em grupos de 16 qubits (circuitos dinâmicos atualmente restringem o tamanho do registrador de medição a 16 qubits). O agrupamento nos permite executar múltiplos experimentos de quatro qubits em paralelo em partes distintas do chip, fazendo uso eficiente de todo o dispositivo. Cada faixa de 16 qubits contém quatro cadeias disjuntas, o que significa que nenhum qubit é reutilizado dentro desse grupo. Por exemplo, uma faixa pode consistir nas cadeias [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] e [12131415][12-13-14-15] todas empacotadas juntas. Qualquer qubit que não foi incluído em uma faixa é retornado na variável 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)

Em seguida, construímos o circuito para cada faixa de 16 qubits. A rotina faz o seguinte para cada cadeia:

  • Preparar um par de Bell intermediário: Aplicar uma porta Hadamard no qubit 1 e uma porta CNOT do qubit 1 para o qubit 2. Isso emaranha os qubits 1 e 2 (criando um estado de Bell Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2}).
  • Emaranhar os qubits das extremidades: Aplicar uma porta CNOT do qubit 0 para o qubit 1, e uma porta CNOT do qubit 2 para o qubit 3. Isso conecta os pares inicialmente separados para que os qubits 0 e 3 se tornem emaranhados após os próximos passos. Uma porta Hadamard no qubit 2 também é aplicada (isso, combinado com as CNOTs anteriores, forma parte de uma medição de Bell nos qubits 1 e 2). Neste ponto, os qubits 0 e 3 ainda não estão emaranhados, mas os qubits 1 e 2 estão emaranhados com eles em um estado maior de quatro qubits.
  • Medições em meio ao circuito e propagação: Os qubits 1 e 2 (os qubits intermediários) são medidos na base computacional, produzindo dois bits clássicos. Com base nesses resultados de medição, aplicamos operações condicionais: se a medição do qubit 1 (chamemos esse bit de m12m_{12}) for 1, aplicamos uma porta XX no qubit 3; se a medição do qubit 2 (m21m_{21}) for 1, aplicamos uma porta ZZ no qubit 0. Essas portas condicionais (realizadas usando o construto if_test/if_else do Qiskit) implementam as correções padrão de teletransporte. Elas "desfazem" as inversões de Pauli aleatórias que ocorrem devido à projeção dos qubits 1 e 2, garantindo que os qubits 0 e 3 terminem em um estado de Bell conhecido, independentemente dos resultados da medição. Após este passo, os qubits 0 e 3 devem idealmente estar emaranhados no estado de Bell Φ+|\Phi^+\rangle.
  • Medir estabilizadores do par de Bell: Em seguida, dividimos em duas versões do circuito. Na primeira versão, medimos o estabilizador ZZZZ nos qubits 0 e 3. Na segunda versão, medimos o estabilizador XXXX nesses qubits.

Para cada layout inicial de quatro qubits, a função acima retorna dois circuitos (um para ZZZZ, um para medição do estabilizador XXXX). Ao final deste passo, temos uma lista de circuitos cobrindo cada cadeia de quatro qubits no dispositivo. Esses circuitos incluem medições em meio ao circuito e operações condicionais (if/else), que são as instruções-chave do circuito dinâmico.

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

Output of the previous code cell

Passo 2: Otimizar o problema para execução em hardware quântico

Antes de executar nossos circuitos em hardware real, precisamos transpilá-los para corresponder às restrições físicas do dispositivo. A transpilação mapeará o circuito abstrato para os qubits físicos e o conjunto de portas do dispositivo escolhido. Como já escolhemos qubits físicos específicos para cada cadeia (fornecendo um initial_layout ao gerador de circuitos), usamos o transpilador com optimization_level=0 com esse layout fixo. Isso instrui o Qiskit a não reatribuir qubits ou realizar otimizações pesadas que possam alterar a estrutura do circuito. Queremos manter a sequência de operações (especialmente as portas condicionais) exatamente como especificado.

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

Passo 3: Executar usando primitivos do Qiskit

Agora podemos executar o experimento no dispositivo quântico. Usamos o Qiskit Runtime e seu primitivo Sampler para executar o lote de circuitos de forma eficiente.

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

Passo 4: Pós-processar e retornar resultado no formato clássico desejado

O passo final é calcular a métrica de erro quadrático médio (MSE) para cada grupo de qubits testado e resumir os resultados. Para cada cadeia, agora temos o Z0Z3\langle Z_0Z_3\rangle e X0X3\langle X_0X_3\rangle medidos. Se os qubits 0 e 3 estivessem perfeitamente emaranhados em um estado de Bell Φ+|\Phi^+\rangle, esperaríamos que ambos fossem +1. Quantificamos o desvio usando o MSE:

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

Este valor é 0 para um par de Bell perfeito, e aumenta à medida que o estado emaranhado fica mais ruidoso (com resultados aleatórios dando uma expectativa em torno de 0, o MSE se aproximaria de 1). O código calcula este MSE para cada grupo de quatro qubits.

Os resultados revelam uma ampla faixa de qualidade de emaranhamento em todo o dispositivo. Isso confirma a descoberta do artigo de que pode haver uma variação de mais de uma ordem de grandeza na fidelidade do estado de Bell, dependendo de quais qubits físicos são usados. Em termos práticos, isso significa que certas regiões ou links no chip são muito melhores em fazer medições em meio ao circuito e operações de propagação do que outras. Fatores como erro de leitura do qubit, tempo de vida do qubit e crosstalk provavelmente contribuem para essas diferenças. Por exemplo, se uma cadeia incluir um qubit de leitura particularmente ruidoso, a medição em meio ao circuito pode ser não confiável, levando a uma fidelidade ruim para aquele par emaranhado (MSE alto).

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

Finalmente, visualizamos o desempenho geral plotando a função de distribuição cumulativa (CDF) dos valores de MSE para todas as cadeias. O gráfico CDF mostra o limiar de MSE no eixo x, e a fração de pares de qubits que têm no máximo aquele MSE no eixo y. Esta curva começa em zero e se aproxima de um à medida que o limiar cresce para abranger todos os pontos de dados. Uma subida íngreme perto de um MSE baixo indicaria que muitos pares são de alta fidelidade; uma subida lenta significa que muitos pares têm erros maiores. Anotamos a CDF com as identidades dos melhores pares. No gráfico, cada ponto na CDF corresponde ao MSE de uma cadeia de quatro qubits, e rotulamos o ponto com o par de índices de qubit [q0,q3][q0, q3] que foram emaranhados naquele experimento. Isso torna fácil identificar quais pares de qubits físicos são os melhores desempenhos (os pontos mais à esquerda na CDF).

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

References