Skip to main content
IBM Quantum Platform

Circuiti dinamici di riferimento con coppie Bell tagliate

Stima di utilizzo: 22 secondi su un processore r2 Heron (NOTA: si tratta solo di una stima). Il tempo di esecuzione può variare.)


Risultati di apprendimento

Dopo aver seguito questo tutorial, gli utenti dovrebbero aver compreso:

  • Come realizzare circuiti dinamici con misurazioni a metà percorso e feedforward classico per teletrasportare l'entanglement tra qubit distanti;
  • Come calcolare e interpretare la metrica di errore per quantificare la fedeltà della coppia di Bell su un dispositivo;
  • Come individuare, sulla base dei risultati dei test di benchmark, quali coppie di qubit sono più adatte alle operazioni basate sul LOCC.

Prerequisiti

Consigliamo agli utenti di acquisire familiarità con i seguenti argomenti prima di seguire questo tutorial:


Sfondo

L'hardware quantistico è tipicamente limitato alle interazioni locali, ma molti algoritmi richiedono l'entanglement di qubit distanti o addirittura di qubit su processori separati. I circuiti dinamici, ovvero circuiti con misurazione a metà circuito e feedforward, consentono di superare tali limitazioni utilizzando la comunicazione classica in tempo reale per implementare in modo efficace operazioni quantistiche non locali. In questo approccio, i risultati delle misurazioni effettuate su una parte di un circuito (o su una QPU) possono attivare in modo condizionale i gate su un'altra parte, consentendoci di teletrasportare l'entanglement su lunghe distanze. Questo costituisce la base dei sistemi LOCC (Local Operations and Classical Communication), in cui utilizziamo stati di risorse intrecciate (coppie di Bell) e comunichiamo i risultati delle misurazioni in modo classico per collegare qubit distanti.

Un utilizzo promettente del LOCC è quello di realizzare porte CNOT virtuali a lungo raggio tramite teletrasporto, come mostrato nel tutorial sull'entanglement a lungo raggio. Invece di un CNOT diretto a lungo raggio (che la connettività hardware potrebbe non consentire), creiamo coppie di Bell ed eseguiamo un'implementazione del gate basata sul teletrasporto. Tuttavia, la fedeltà di tali operazioni dipende dalle caratteristiche dell'hardware. La decoerenza dei qubit durante il ritardo necessario (in attesa dei risultati delle misurazioni) e la latenza della comunicazione classica possono degradare lo stato di entanglement. Inoltre, gli errori nelle misurazioni intermedie del circuito sono più difficili da correggere rispetto agli errori nelle misurazioni finali, poiché si propagano al resto del circuito attraverso i gate condizionali.

Nell'esperimento di riferimento, gli autori introducono un parametro di riferimento della fedeltà della coppia di Bell per individuare quali parti di un dispositivo siano più adatte all'entanglement basato sulla tecnologia LOCC. L'idea è quella di far funzionare un piccolo circuito dinamico su ogni gruppo di quattro qubit collegati nel processore. Questo circuito a quattro qubit crea innanzitutto una coppia di Bell sui due qubit centrali, quindi li utilizza come risorsa per intrecciare i due qubit periferici tramite LOCC. In pratica, i qubit 1 e 2 vengono preparati localmente in una coppia di Bell non tagliata (una coppia di Bell creata direttamente con un Hadamard e un CNOT, senza teletrasporto), dopodiché una procedura di teletrasporto utilizza quella coppia di Bell per intrecciare i qubit 0 e 3. I qubit 1 e 2 vengono misurati durante l'esecuzione del circuito e, in base a tali risultati, vengono applicate le correzioni di Pauli (una X sul qubit 3 e una Z sul qubit 0). I qubit 0 e 3 rimangono quindi in uno stato di Bell alla fine del circuito.

Per quantificare la qualità di questa coppia intrecciata finale, misuriamo i suoi stabilizzatori: in particolare, la parità nella ZZ base ( Z0Z3Z_0Z_3 ) e nella XX base ( X0X3X_0X_3 ). Per una coppia di Bell perfetta, entrambe queste aspettative sono pari a +1. In pratica, il rumore dell'hardware ridurrà questi valori. Ripetiamo quindi il circuito due volte per ogni coppia di qubit: un circuito misura i qubit 0 e 3 nella ZZ base, mentre un altro li misura nella XX base. Dai risultati otteniamo una stima di Z0Z3\langle Z_0Z_3\rangle e X0X3\langle X_0X_3\rangle per quella coppia di qubit. Utilizziamo l'errore quadratico medio (MSE) di questi stabilizzatori rispetto al valore ideale (1) come semplice metrica della fedeltà dell'entanglement. Un MSE più basso significa che i due qubit hanno raggiunto uno stato di Bell più vicino all'ideale (maggiore fedeltà), mentre un MSE più alto indica un errore maggiore. Eseguendo la scansione di questo esperimento sul dispositivo, possiamo valutare le capacità di misurazione e feedforward di diversi gruppi di qubit e identificare le migliori coppie di qubit per le operazioni LOCC.

Questo tutorial illustra l'esperimento condotto su un dispositivo IBM Quantum® per mostrare come i circuiti dinamici possano essere utilizzati per generare e valutare l'entanglement tra qubit distanti. Mapperemo tutte le catene lineari a quattro qubit presenti sul dispositivo, eseguiremo il circuito di teletrasporto su ciascuna di esse e infine visualizzeremo la distribuzione dei valori di MSE. Questa procedura completa illustra come sfruttare le funzionalità del servizio di calcolo quantistico " IBM Quantum " e dei circuiti dinamici per orientare le scelte relative all'hardware, al fine di ridurre i circuiti o distribuire algoritmi quantistici all'interno di un sistema modulare.


Requisiti

Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:

  • Qiskit SDK v2.0 o versioni successive, con supporto alla visualizzazione
  • v0.40Qiskit Runtime o successivamente (pip install qiskit-ibm-runtime)
  • Qiskit Aer v0.17 o versioni successive (pip install qiskit-aer)

Configura

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

Esempio di simulatore su piccola scala

Prima di eseguire il codice sulla QPU reale, verifichiamo che il circuito produca una coppia di Bell testandolo su un simulatore privo di rumore con una catena di quattro qubit [0, 1, 2, 3]. Utilizziamo l' IBM Quantum Sampler con come AerSimulator modalità di backend per l'esecuzione dei circuiti.

Fase 1: mappare gli input classici su un problema quantistico

Il primo passo consiste nel creare una serie di circuiti quantistici per valutare tutte le possibili connessioni di coppie di Bell, adattate alla topologia del dispositivo. [0, 1, 2, 3]Iniziamo realizzando tali circuiti con una catena di quattro qubit.

La create_bell_stab() routine esegue le seguenti operazioni per ogni catena:

  • Prepara una coppia Bell centrale: applica un Hadamard sul qubit 1 e un CNOT dal qubit 1 al qubit 2. Questo intreccia i qubit 1 e 2 (creando uno stato Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2} di Bell).
  • Intreccia i qubit periferici: applica un CNOT dal qubit 0 al qubit 1 e un CNOT dal qubit 2 al qubit 3. Questo collega le coppie inizialmente separate in modo che i qubit 0 e 3 diventino intrecciati dopo i passaggi successivi. Viene applicato anche un Hadamard sul qubit 2 (questo, combinato con i precedenti CNOT, costituisce parte di una misurazione di Bell sui qubit 1 e 2). A questo punto, i qubit 0 e 3 non sono ancora intrecciati, ma i qubit 1 e 2 sono intrecciati con essi in uno stato più ampio a quattro qubit.
  • Misurazioni a metà circuito e feedforward: i qubit 1 e 2 (i qubit centrali) vengono misurati nella base computazionale, producendo due bit classici. Sulla base dei risultati di tali misurazioni, applichiamo operazioni condizionali: se la misurazione del qubit 1 (chiamiamola bit m12m_{12} ) è 1, applichiamo un XX gate sul qubit 3; se la misurazione del qubit 2 ( m21m_{21} ) è 1, applichiamo un ZZ gate sul qubit 0. Questi gate condizionali (realizzati utilizzando il Qiskit if_test/if_else construct) implementano le correzioni standard del teletrasporto. Essi "annullano" i ribaltamenti casuali di Pauli che si verificano a causa della proiezione dei qubit 1 e 2, assicurando che i qubit 0 e 3 finiscano in uno stato di Bell noto, indipendentemente dai risultati della misurazione. Dopo questo passaggio, i qubit 0 e 3 dovrebbero idealmente essere intrecciati nello stato di Bell Φ+|\Phi^+\rangle.
  • Misurare gli stabilizzatori della coppia di campane: Abbiamo quindi suddiviso il circuito in due versioni. Nella prima versione, misuriamo lo ZZZZ stabilizzatore sui qubit 0 e 3. Nella seconda versione, misuriamo lo XXXX stabilizzatore su questi qubit.

Per ogni configurazione iniziale a quattro qubit, la funzione sopra riportata restituisce due circuiti (uno per la misurazion ZZZZ e e uno per la misurazione stabilizzatrice XXXX ). Questi circuiti includono misurazioni a metà circuito e operazioni condizionali (if/else), che costituiscono le istruzioni fondamentali del circuito dinamico.

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:

Output of the previous code cell

Fase 2: Ottimizzare il problema per l'esecuzione su hardware quantistico

Prima di eseguire i nostri circuiti, dobbiamo traspilarli in operazioni a livello di porta supportate dal backend specificato. La transpilazione mapperà il circuito astratto sui qubit fisici e sull'insieme di porte del backend selezionato. Poiché abbiamo già scelto qubit fisici specifici per ciascuna catena (specificando un initial_layout nel generatore di circuiti), utilizziamo il transpiler optimization_level=0 con quella configurazione fissa. Questo indica a Qiskit di non riassegnare i qubit né di eseguire ottimizzazioni complesse che potrebbero alterare la struttura del circuito. Vogliamo mantenere la sequenza delle operazioni (in particolare i gate condizionali) esattamente come specificato.

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)

Passaggio 3: eseguire utilizzando Qiskit primitives

Ora possiamo eseguire l'esperimento sul backend del simulatore senza rumore.

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

Output:

layout [0, 1, 2, 3]
qubits: [0, 1, 2, 3], mse:, 0.0
 

Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato

Il passo finale consiste nel calcolare la metrica dell'errore quadratico medio (MSE) per ciascun gruppo di qubit testato e riassumere i risultati. Per ogni catena, ora abbiamo i valori misurati Z0Z3\langle Z_0Z_3\rangle e X0X3\langle X_0X_3\rangle. Se i qubit 0 e 3 fossero perfettamente intrecciati in uno stato Φ+|\Phi^+\rangle di Bell, ci aspetteremmo che entrambi fossero +1. Quantifichiamo la deviazione utilizzando l'MSE:

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

Questo valore è pari a 0 per una coppia di Bell perfetta e aumenta man mano che lo stato intrecciato diventa più rumoroso (con risultati casuali che danno un valore atteso intorno a 0, l'errore quadratico medio si avvicinerebbe a 1). Il codice calcola questo MSE per ciascun gruppo di quattro qubit. In assenza di rumore, in questo esempio di simulatore su piccola scala osserviamo che MSE = 0, come previsto.


Esempio di hardware su larga scala

A questo punto riuniamo tutti questi dettagli in un unico flusso di lavoro su scala più ampia, che viene poi eseguito su hardware quantistico reale.

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

Effettuiamo una ricerca automatica nella mappa di accoppiamento del dispositivo per individuare tutte le catene di quattro qubit collegate linearmente. Ciascuna di queste catene (contrassegnata dagli indici dei qubit [q0q1q2q3][q0-q1-q2-q3] ) funge da caso di prova per il circuito di scambio di entanglement. Identificando tutti i possibili percorsi di length-4, garantiamo la massima copertura per i possibili raggruppamenti di qubit in grado di attuare il protocollo.

Generiamo queste catene utilizzando una funzione di supporto che esegue una ricerca avida sul grafico dei dispositivi. Restituisce "strisce" di quattro catene da quattro qubit raggruppate in gruppi da 16 qubit. Il bundling ci permette di eseguire più esperimenti a quattro qubit in parallelo su parti distinte del chip e di sfruttare in modo efficiente l'intero dispositivo. Ogni striscia da 16 qubit contiene quattro catene disgiunte, il che significa che nessun qubit viene riutilizzato all'interno di quel gruppo. Ad esempio, una striscia potrebbe essere costituita dalle catene [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] e [12131415][12-13-14-15], tutte raggruppate insieme. Qualsiasi qubit che non sia stato incluso in una striscia viene restituito nella leftover variabile.

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)

Successivamente, costruiamo il circuito per ciascuna striscia da 16 qubit utilizzando la funzione create_bell_stab(). Al termine di questa fase, disponiamo di un elenco di circuiti che coprono tutte le catene a quattro qubit presenti sul dispositivo. Successivamente, trasponiamo ed eseguiamo i circuiti sul backend reale, quindi elaboriamo i risultati.

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

Output:

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
 

I risultati rivelano un'ampia gamma di qualità di entanglement in tutto il dispositivo. Ciò conferma la conclusione dell'articolo secondo cui può esserci una variazione di oltre un ordine di grandezza nella fedeltà dello stato di Bell a seconda dei qubit fisici utilizzati. In termini pratici, ciò significa che alcune regioni o collegamenti del chip sono molto più efficaci di altri nell'eseguire misurazioni a metà circuito e operazioni di feedforward. Fattori quali errori di lettura dei qubit, durata dei qubit e crosstalk contribuiscono probabilmente a queste differenze. Ad esempio, se una catena include un qubit di lettura particolarmente rumoroso, la misurazione a metà circuito potrebbe essere inaffidabile, portando a una scarsa fedeltà per quella coppia intrecciata (MSE elevato).

Infine, visualizziamo le prestazioni complessive tracciando la funzione di distribuzione cumulativa (CDF) dei valori MSE per tutte le catene. Il grafico CDF mostra la soglia MSE sull'asse x e la frazione di coppie di qubit che hanno al massimo quell'MSE sull'asse y. Questa curva parte da zero e si avvicina a uno man mano che la soglia cresce fino a comprendere tutti i punti dati. Un aumento ripido vicino a un MSE basso indicherebbe che molte coppie sono ad alta fedeltà; un aumento lento significa che molte coppie presentano errori più grandi. Annotiamo il CDF con le identità delle coppie migliori. Nel grafico, ogni punto nella CDF corrisponde all'MSE di una catena di quattro qubit e contrassegniamo il punto con la coppia di indici di [q0,q3][q0, q3] qubit che erano intrecciati in quell'esperimento. Questo rende facile individuare quali coppie di qubit fisici sono quelle con le prestazioni migliori (i punti all'estrema sinistra della CDF).

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output:

Output of the previous code cell

Passi successivi

Suggerimenti

Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti materiali:


Riferimenti

[1] Carrera Vázquez, A., Tornow, C., Ristè, D. et al. Combinazione di processori quantistici con comunicazioni classiche in tempo reale. Nature 636, 75-79 (2024).

Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.