Transizione di fase di Nishimori
Stima di utilizzo: 3 minuti su un processore Heron r2 (NOTA: questa è solo una stima. Il tempo di esecuzione effettivo potrebbe variare.)
Contesto​
Questo tutorial dimostra come realizzare una transizione di fase di Nishimori su un processore quantistico IBM®. Questo esperimento è stato originariamente descritto in Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits.
La transizione di fase di Nishimori si riferisce alla transizione tra fasi ordinate a corto e lungo raggio nel modello di Ising a legami casuali. Su un computer quantistico, la fase ordinata a lungo raggio si manifesta come uno stato in cui i qubit sono intrecciati attraverso l'intero dispositivo. Questo stato altamente intrecciato viene preparato utilizzando il protocollo generation of entanglement by measurement (GEM). Utilizzando misurazioni a metà circuito, il protocollo GEM è in grado di intrecciare qubit attraverso l'intero dispositivo utilizzando circuiti di sola profondità costante. Questo tutorial utilizza l'implementazione del protocollo GEM dal pacchetto software GEM Suite.
Requisiti​
Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:
- Qiskit SDK v1.0 o successivo, con supporto per la visualizzazione
- Qiskit Runtime v0.22 o successivo (
pip install qiskit-ibm-runtime) - GEM Suite (
pip install gem-suite)
Configurazione​
# Added by doQumentation — required packages for this notebook
!pip install -q gem-suite matplotlib qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt
from collections import defaultdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.transpiler import generate_preset_pass_manager
from gem_suite import PlaquetteLattice
from gem_suite.experiments import GemExperiment
Passo 1: Mappare gli input classici a un problema quantistico​
Il protocollo GEM funziona su un processore quantistico con connettività dei qubit descritta da un reticolo. I processori quantistici IBM di oggi utilizzano il reticolo esagonale pesante. I qubit del processore sono raggruppati in plaquette in base a quale cella unitaria del reticolo occupano. Poiché un qubit potrebbe apparire in più di una cella unitaria, le plaquette non sono disgiunte. Sul reticolo esagonale pesante, una plaquette contiene 12 qubit. Le plaquette stesse formano anche un reticolo, dove due plaquette sono connesse se condividono qubit. Sul reticolo esagonale pesante, le plaquette vicine condividono 3 qubit.
Nel pacchetto software GEM Suite, la classe fondamentale per implementare il protocollo GEM è PlaquetteLattice, che rappresenta il reticolo di plaquette (che è distinto dal reticolo esagonale pesante). Una PlaquetteLattice può essere inizializzata da una mappa di accoppiamento dei qubit. Attualmente, sono supportate solo le mappe di accoppiamento esagonali pesanti.
La seguente cella di codice inizializza un reticolo di plaquette dalla mappa di accoppiamento di un processore quantistico IBM. Il reticolo di plaquette non comprende sempre l'intero hardware. Ad esempio, ibm_torino ha 133 qubit totali, ma il più grande reticolo di plaquette che si adatta al dispositivo utilizza solo 125 di essi e comprende un totale di 18 plaquette. Risultati simili possono essere osservati anche per i dispositivi IBM Quantum® con conteggi di qubit diversi.
# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<YOUR_API_KEYN>", overwrite=True, set_as_default=True)
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)
print(f"Number of qubits in backend: {backend.num_qubits}")
print(
f"Number of qubits in plaquette lattice: {len(list(plaquette_lattice.qubits()))}"
)
print(f"Number of plaquettes: {len(list(plaquette_lattice.plaquettes()))}")
Number of qubits in backend: 133
Number of qubits in plaquette lattice: 125
Number of plaquettes: 18
Potete visualizzare il reticolo di plaquette generando un diagramma della sua rappresentazione grafica. Nel diagramma, le plaquette sono rappresentate da esagoni etichettati, e due plaquette sono connesse da un arco se condividono qubit.
plaquette_lattice.draw_plaquettes()
Potete recuperare informazioni sulle singole plaquette, come i qubit che contengono, utilizzando il metodo plaquettes.
# Get a list of the plaquettes
plaquettes = list(plaquette_lattice.plaquettes())
# Display information about plaquette 0
plaquettes[0]
PyPlaquette(index=0, qubits=[0, 1, 2, 3, 4, 15, 16, 19, 20, 21, 22, 23], neighbors=[3, 1])
Potete anche produrre un diagramma dei qubit sottostanti che formano il reticolo di plaquette.
plaquette_lattice.draw_qubits()

Oltre alle etichette dei qubit e agli archi che indicano quali qubit sono connessi, il diagramma contiene tre informazioni aggiuntive che sono rilevanti per il protocollo GEM:
- Ogni qubit è ombreggiato (grigio) o non ombreggiato. I qubit ombreggiati sono qubit "sito" che rappresentano i siti del modello di Ising, e i qubit non ombreggiati sono qubit "legame" utilizzati per mediare le interazioni tra i qubit sito.
- Ogni qubit sito è etichettato come (A) o (B), indicando uno dei due ruoli che un qubit sito può svolgere nel protocollo GEM (i ruoli sono spiegati più avanti).
- Ogni arco è colorato utilizzando uno di sei colori, partizionando così gli archi in sei gruppi. Questa partizione determina come le porte a due qubit possono essere parallelizzate, così come diversi schemi di schedulazione che probabilmente incorreranno in diverse quantità di errore su un processore quantistico rumoroso. Poiché gli archi in un gruppo sono disgiunti, uno strato di porte a due qubit può essere applicato su quegli archi simultaneamente. In effetti, è possibile partizionare i sei colori in tre gruppi di due colori tali che l'unione di ciascun gruppo di due colori sia ancora disgiunta. Pertanto, sono necessari solo tre strati di porte a due qubit per attivare ogni arco. Ci sono 12 modi per partizionare così i sei colori, e ogni tale partizione produce una diversa schedulazione di porte a 3 strati.
Ora che hai creato un reticolo di plaquette, il passo successivo è inizializzare un oggetto GemExperiment, passando sia il reticolo di plaquette che il backend su cui intendi eseguire l'esperimento. La classe GemExperiment gestisce l'effettiva implementazione del protocollo GEM, inclusa la generazione di circuiti, l'invio di job e l'analisi dei dati. La seguente cella di codice inizializza la classe dell'esperimento limitando il reticolo di plaquette a solo due delle plaquette (21 qubit), riducendo la dimensione dell'esperimento per garantire che il rumore nell'hardware non sopraffaccia il segnale.
gem_exp = GemExperiment(plaquette_lattice.filter([9, 12]), backend=backend)
# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9, 12]).draw_qubits()

Un circuito del protocollo GEM è costruito utilizzando i seguenti passi:
- Preparare lo stato per tutti applicando una porta di Hadamard a ogni qubit.
- Applicare una porta tra ogni coppia di qubit connessi. Questo può essere ottenuto utilizzando 3 strati di porte. Ogni porta agisce su un qubit sito e un qubit legame. Se il qubit sito è etichettato (B), allora l'angolo è fissato a . Se il qubit sito è etichettato (A), allora l'angolo può variare, producendo circuiti diversi. Per impostazione predefinita, l'intervallo degli angoli è impostato su 21 punti equidistanti tra e , inclusi.
- Misurare ogni qubit legame nella base di Pauli . Poiché i qubit sono misurati nella base di Pauli , questo può essere ottenuto applicando una porta di Hadamard prima di misurare il qubit.
Si noti che l'articolo citato nell'introduzione a questo tutorial utilizza una convenzione diversa per l'angolo , che differisce dalla convenzione utilizzata in questo tutorial per un fattore 2.
Nel passo 3, vengono misurati solo i qubit legame. Per capire in quale stato rimangono i qubit sito, è istruttivo considerare il caso in cui l'angolo applicato ai qubit sito (A) nel passo 2 sia uguale a . In questo caso, i qubit sito rimangono in uno stato altamente intrecciato simile allo stato GHZ,
A causa della casualità nei risultati delle misurazioni, lo stato effettivo dei qubit sito potrebbe essere uno stato diverso con ordine a lungo raggio, ad esempio, . Tuttavia, lo stato GHZ può essere recuperato applicando un'operazione di decodifica basata sui risultati delle misurazioni. Quando l'angolo viene ridotto da , l'ordine a lungo raggio può ancora essere recuperato fino a un angolo critico, che in assenza di rumore è circa . Al di sotto di questo angolo, lo stato risultante non mostra più entanglement a lungo raggio. Questa transizione tra la presenza e l'assenza di ordine a lungo raggio è la transizione di fase di Nishimori.
Nella descrizione sopra, i qubit sito sono stati lasciati non misurati, e l'operazione di decodifica può essere eseguita applicando porte quantistiche. Nell'esperimento come implementato nella suite GEM, che questo tutorial segue, i qubit sito vengono infatti misurati, e l'operazione di decodifica viene applicata in un passo di post-elaborazione classica.
Nella descrizione sopra, l'operazione di decodifica può essere eseguita applicando porte quantistiche ai qubit sito per recuperare lo stato quantistico. Tuttavia, se l'obiettivo è misurare immediatamente lo stato, ad esempio, per scopi di caratterizzazione, allora i qubit sito vengono misurati insieme ai qubit legame, e l'operazione di decodifica può essere applicata in un passo di post-elaborazione classica. Questo è come l'esperimento è implementato nella suite GEM, che questo tutorial segue.
Oltre a dipendere dall'angolo nel passo 2, che per impostazione predefinita varia attraverso 21 valori, il circuito del protocollo GEM dipende anche dallo schema di schedulazione utilizzato per implementare i 3 strati di porte . Come discusso in precedenza, ci sono 12 tali schemi di schedulazione. Pertanto, il numero totale di circuiti nell'esperimento è .
I circuiti dell'esperimento possono essere generati utilizzando il metodo circuits della classe GemExperiment.
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
Total number of circuits: 252
Per gli scopi di questo tutorial, è sufficiente considerare solo un singolo schema di schedulazione. La seguente cella di codice limita l'esperimento al primo schema di schedulazione. Di conseguenza, l'esperimento ha solo 21 circuiti, uno per ogni angolo variato.
# Restrict experiment to the first scheduling pattern
gem_exp.set_experiment_options(schedule_idx=0)
# There are less circuits now
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
# Print the RZZ angles swept over
print(f"RZZ angles:\n{gem_exp.parameters()}")
Total number of circuits: 21
RZZ angles:
[0. 0.07853982 0.15707963 0.23561945 0.31415927 0.39269908
0.4712389 0.54977871 0.62831853 0.70685835 0.78539816 0.86393798
0.9424778 1.02101761 1.09955743 1.17809725 1.25663706 1.33517688
1.41371669 1.49225651 1.57079633]
La seguente cella di codice disegna un diagramma del circuito all'indice 5. Per ridurre la dimensione del diagramma, le porte di misurazione alla fine del circuito vengono rimosse.
# Get the circuit at index 5
circuit = circuits[5]
# Remove the final measurements to ease visualization
circuit.remove_final_measurements()
# Draw the circuit
circuit.draw("mpl", fold=-1, scale=0.5)
Passo 2: Ottimizzare il problema per l'esecuzione su hardware quantistico​
La transpilazione di circuiti quantistici per l'esecuzione su hardware comporta tipicamente un certo numero di stadi. Tipicamente, gli stadi che comportano il maggior sovraccarico computazionale sono la scelta del layout dei qubit, il routing delle porte a due qubit per conformarsi alla connettività dei qubit dell'hardware, e l'ottimizzazione del circuito per minimizzare il suo conteggio di porte e profondità . Nel protocollo GEM, gli stadi di layout e routing non sono necessari perché la connettività dell'hardware è già incorporata nel design del protocollo. I circuiti hanno già un layout di qubit, e le porte a due qubit sono già mappate su connessioni native. Inoltre, per preservare la struttura del circuito mentre l'angolo viene variato, dovrebbe essere eseguita solo un'ottimizzazione del circuito molto basilare.
La classe GemExperiment transpila trasparentemente i circuiti durante l'esecuzione dell'esperimento. Gli stadi di layout e routing sono già sovrascritti per impostazione predefinita per non fare nulla, e l'ottimizzazione del circuito viene eseguita a un livello che ottimizza solo le porte a singolo qubit. Tuttavia, puoi sovrascrivere o passare opzioni aggiuntive utilizzando il metodo set_transpile_options. Per scopi di visualizzazione, la seguente cella di codice transpila manualmente il circuito visualizzato in precedenza e disegna il circuito transpilato.
# Demonstrate setting transpile options
gem_exp.set_transpile_options(
optimization_level=1 # This is the default optimization level
)
pass_manager = generate_preset_pass_manager(
backend=backend,
initial_layout=list(gem_exp.physical_qubits),
**dict(gem_exp.transpile_options),
)
transpiled = pass_manager.run(circuit)
transpiled.draw("mpl", idle_wires=False, fold=-1, scale=0.5)

Passo 3: Eseguire utilizzando le primitive Qiskit​
Per eseguire i circuiti del protocollo GEM sull'hardware, chiamate il metodo run dell'oggetto GemExperiment. Potete specificare il numero di shot che desiderate campionare da ciascun circuito. Il metodo run restituisce un oggetto ExperimentData che dovreste salvare in una variabile. Si noti che il metodo run invia solo job senza aspettare che finiscano, quindi è una chiamata non bloccante.
exp_data = gem_exp.run(shots=10_000)
Per attendere i risultati, chiamate il metodo block_for_results dell'oggetto ExperimentData. Questa chiamata causerà il blocco dell'interprete fino a quando i job non saranno terminati.
exp_data.block_for_results()
ExperimentData(GemExperiment, d0d5880a-34c1-4aab-a7b6-c4f58516bc03, job_ids=['cwg12ptmptp00082khhg'], metadata=<5 items>, figure_names=['two_point_correlation.svg', 'normalized_variance.svg', 'plaquette_ops.svg', 'bond_ops.svg'])
Passo 4: Post-elaborare e restituire il risultato nel formato classico desiderato​
A un angolo di , lo stato decodificato sarebbe lo stato GHZ in assenza di rumore. L'ordine a lungo raggio dello stato GHZ può essere visualizzato tracciando la magnetizzazione delle stringhe di bit misurate. La magnetizzazione è definita come la somma degli operatori di Pauli a singolo qubit,
dove è il numero di qubit sito. Il suo valore per una stringa di bit è uguale alla differenza tra il numero di zeri e il numero di uno. Misurare lo stato GHZ produce lo stato di tutti zeri o lo stato di tutti uno con uguale probabilità , quindi la magnetizzazione sarebbe metà del tempo e l'altra metà del tempo. In presenza di errori dovuti al rumore, apparirebbero anche altri valori, ma se il rumore non è troppo grande, la distribuzione sarebbe ancora centrata vicino a e .
Per le stringhe di bit grezze prima della decodifica, la distribuzione della magnetizzazione sarebbe equivalente a quella di stringhe di bit uniformemente casuali, in assenza di rumore.
La seguente cella di codice traccia la magnetizzazione delle stringhe di bit grezze e delle stringhe di bit decodificate all'angolo di .
def magnetization_distribution(
counts_dict: dict[str, int],
) -> dict[str, float]:
"""Compute magnetization distribution from counts dictionary."""
# Construct dictionary from magnetization to count
mag_dist = defaultdict(float)
for bitstring, count in counts_dict.items():
mag = bitstring.count("0") - bitstring.count("1")
mag_dist[mag] += count
# Normalize
shots = sum(counts_dict.values())
for mag in mag_dist:
mag_dist[mag] /= shots
return mag_dist
# Get counts dictionaries with and without decoding
data = exp_data.data()
# Get the last data point, which is at the angle for the GHZ state
raw_counts = data[-1]["counts"]
# Without decoding
site_indices = [
i for i, q in enumerate(gem_exp.plaquettes.qubits()) if q.role == "Site"
]
site_raw_counts = defaultdict(int)
for key, val in raw_counts.items():
site_str = "".join(key[-1 - i] for i in site_indices)
site_raw_counts[site_str] += val
# With decoding
_, site_decoded_counts = gem_exp.plaquettes.decode_outcomes(
raw_counts, return_counts=True
)
# Compute magnetization distribution
raw_magnetization = magnetization_distribution(site_raw_counts)
decoded_magnetization = magnetization_distribution(site_decoded_counts)
# Plot
plt.bar(*zip(*raw_magnetization.items()), label="raw")
plt.bar(*zip(*decoded_magnetization.items()), label="decoded", width=0.3)
plt.legend()
plt.xlabel("Magnetization")
plt.ylabel("Frequency")
plt.title("Magnetization distribution with and without decoding")
Text(0.5, 1.0, 'Magnetization distribution with and without decoding')
Per caratterizzare più rigorosamente l'ordine a lungo raggio, puoi esaminare la correlazione media a due punti , definita come
Un valore più alto indica un grado maggiore di entanglement. La classe GemExperiment calcola automaticamente questo valore per le stringhe di bit decodificate come parte dell'elaborazione dei dati sperimentali. Memorizza una figura accessibile tramite il metodo figure della classe dei dati sperimentali. In questo caso, il nome della figura è two_point_correlation.
exp_data.figure("two_point_correlation")
Per determinare il punto critico della transizione di fase di Nishimori, puoi osservare la varianza normalizzata di , definita come
che quantifica la quantità di fluttuazione nella magnetizzazione al quadrato. Questo valore è massimizzato al punto critico della transizione di fase di Nishimori. In assenza di rumore, il punto critico si verifica a circa . In presenza di rumore, il punto critico è spostato più in alto, ma la transizione di fase è ancora osservata finché il punto critico si verifica al di sotto di .
exp_data.figure("normalized_variance")
Ampliare l'esperimento​
Le seguenti celle di codice eseguono l'esperimento per sei plaquette (49 qubit) e le 12 plaquette complete (125 qubit) e tracciano la varianza normalizzata. Man mano che l'esperimento viene ampliato a dimensioni maggiori, la maggiore quantità di rumore sposta il punto critico verso destra.
gem_exp = GemExperiment(
plaquette_lattice.filter(range(3, 9)), backend=backend
)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")
gem_exp = GemExperiment(plaquette_lattice, backend=backend)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")
Conclusione​
In questo tutorial, hai realizzato una transizione di fase di Nishimori su un processore quantistico utilizzando il protocollo GEM. Le metriche che hai esaminato durante la post-elaborazione, in particolare la correlazione a due punti e la varianza normalizzata, servono come benchmark della capacità del dispositivo di generare stati intrecciati a lungo raggio. Questi benchmark estendono l'utilità del protocollo GEM oltre l'esplorazione di fisica interessante. Come parte del protocollo, hai intrecciato qubit attraverso l'intero dispositivo utilizzando circuiti di sola profondità costante. Questa impresa è possibile solo grazie all'uso da parte del protocollo di misurazioni a metà circuito. In questo esperimento, lo stato intrecciato è stato immediatamente misurato, ma un percorso interessante da esplorare sarebbe continuare a utilizzare lo stato in ulteriore elaborazione quantistica!
Sondaggio sul tutorial​
Ti preghiamo di compilare questo breve sondaggio per fornire feedback su questo tutorial. Il tuo feedback ci aiuterà a migliorare le nostre offerte di contenuti e l'esperienza utente.