Transition de phase de Nishimori
Estimation d'utilisation : 3 minutes sur un processeur Heron r2 (REMARQUE : il ne s'agit que d'une estimation. Votre temps d'exécution peut varier.)
Contexte
Ce tutoriel montre comment réaliser une transition de phase de Nishimori sur un processeur quantique IBM®. Cette expérience a été décrite à l'origine dans Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits.
La transition de phase de Nishimori fait référence à la transition entre les phases ordonnées à courte et longue portée dans le modèle d'Ising à liaisons aléatoires. Sur un ordinateur quantique, la phase ordonnée à longue portée se manifeste comme un état dans lequel les qubits sont intriqués à travers l'ensemble du dispositif. Cet état fortement intriqué est préparé à l'aide du protocole de génération d'intrication par mesure (GEM). En utilisant des mesures en milieu de circuit, le protocole GEM est capable d'intriquer des qubits à travers l'ensemble du dispositif en utilisant des circuits de profondeur constante uniquement. Ce tutoriel utilise l'implémentation du protocole GEM fournie par le package logiciel GEM Suite.
Prérequis
Avant de commencer ce tutoriel, assurez-vous d'avoir installé les éléments suivants :
- Qiskit SDK v1.0 ou ultérieur, avec le support de visualisation
- Qiskit Runtime v0.22 ou ultérieur (
pip install qiskit-ibm-runtime) - GEM Suite (
pip install gem-suite)
Configuration
# Added by doQumentation — installs packages not in the Binder environment
%pip install -q gem-suite
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
Étape 1 : Transposer les entrées classiques en un problème quantique
Le protocole GEM fonctionne sur un processeur quantique dont la connectivité des qubits est décrite par un réseau. Les processeurs quantiques IBM actuels utilisent le réseau hexagonal lourd. Les qubits du processeur sont regroupés en plaquettes en fonction de la cellule unitaire du réseau à laquelle ils appartiennent. Comme un qubit peut apparaître dans plus d'une cellule unitaire, les plaquettes ne sont pas disjointes. Sur le réseau hexagonal lourd, une plaquette contient 12 qubits. Les plaquettes elles-mêmes forment également un réseau, où deux plaquettes sont connectées si elles partagent des qubits. Sur le réseau hexagonal lourd, les plaquettes voisines partagent 3 qubits.
Dans le package logiciel GEM Suite, la classe fondamentale pour implémenter le protocole GEM est PlaquetteLattice, qui représente le réseau de plaquettes (distinct du réseau hexagonal lourd). Un PlaquetteLattice peut être initialisé à partir d'une carte de couplage des qubits. Actuellement, seules les cartes de couplage hexagonales lourdes sont prises en charge.
La cellule de code suivante initialise un réseau de plaquettes à partir de la carte de couplage d'un processeur quantique IBM. Le réseau de plaquettes n'englobe pas toujours l'intégralité du matériel. Par exemple, ibm_torino possède 133 qubits au total, mais le plus grand réseau de plaquettes pouvant être inscrit sur le dispositif n'utilise que 125 d'entre eux et comprend un total de 18 plaquettes. Un comportement similaire peut être observé pour les dispositifs IBM Quantum® ayant un nombre différent de qubits.
# 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
Vous pouvez visualiser le réseau de plaquettes en générant un diagramme de sa représentation sous forme de graphe. Dans le diagramme, les plaquettes sont représentées par des hexagones étiquetés, et deux plaquettes sont reliées par une arête si elles partagent des qubits.
plaquette_lattice.draw_plaquettes()
Vous pouvez obtenir des informations sur les plaquettes individuelles, telles que les qubits qu'elles contiennent, en utilisant la méthode 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])
Vous pouvez également produire un diagramme des qubits sous-jacents qui forment le réseau de plaquettes.
plaquette_lattice.draw_qubits()

En plus des étiquettes de qubits et des arêtes indiquant quels qubits sont connectés, le diagramme contient trois informations supplémentaires pertinentes pour le protocole GEM :
- Chaque qubit est soit ombré (gris), soit non ombré. Les qubits ombrés sont des qubits « site » qui représentent les sites du modèle d'Ising, et les qubits non ombrés sont des qubits « liaison » utilisés pour médier les interactions entre les qubits site.
- Chaque qubit site est étiqueté soit (A), soit (B), indiquant l'un des deux rôles qu'un qubit site peut jouer dans le protocole GEM (les rôles sont expliqués plus loin).
- Chaque arête est colorée à l'aide de l'une des six couleurs, partitionnant ainsi les arêtes en six groupes. Ce partitionnement détermine comment les portes à deux qubits peuvent être parallélisées, ainsi que les différents schémas d'ordonnancement susceptibles d'engendrer des quantités différentes d'erreur sur un processeur quantique bruité. Comme les arêtes d'un groupe sont disjointes, une couche de portes à deux qubits peut être appliquée simultanément sur ces arêtes. En fait, il est possible de partitionner les six couleurs en trois groupes de deux couleurs de sorte que l'union de chaque groupe de deux couleurs reste disjointe. Par conséquent, seules trois couches de portes à deux qubits sont nécessaires pour activer chaque arête. Il existe 12 façons de partitionner ainsi les six couleurs, et chaque partition produit un schéma de portes à 3 couches différent.
Maintenant que vous avez créé un réseau de plaquettes, l'étape suivante consiste à initialiser un objet GemExperiment, en passant à la fois le réseau de plaquettes et le backend sur lequel vous avez l'intention d'exécuter l'expérience. La classe GemExperiment gère l'implémentation effective du protocole GEM, y compris la génération des circuits, la soumission des tâches et l'analyse des données. La cellule de code suivante initialise la classe d'expérience en restreignant le réseau de plaquettes à seulement deux plaquettes (21 qubits), réduisant ainsi la taille de l'expérience pour garantir que le bruit du matériel ne submerge pas le signal.
gem_exp = GemExperiment(plaquette_lattice.filter([9, 12]), backend=backend)
# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9, 12]).draw_qubits()

Un circuit du protocole GEM est construit en suivant les étapes suivantes :
- Préparer l'état tout- en appliquant une porte de Hadamard à chaque qubit.
- Appliquer une porte entre chaque paire de qubits connectés. Cela peut être réalisé en 3 couches de portes. Chaque porte agit sur un qubit site et un qubit liaison. Si le qubit site est étiqueté (B), alors l'angle est fixé à . Si le qubit site est étiqueté (A), alors l'angle est libre de varier, produisant différents circuits. Par défaut, la plage d'angles est définie sur 21 points également espacés entre et , inclus.
- Mesurer chaque qubit liaison dans la base de Pauli . Comme les qubits sont mesurés dans la base de Pauli , cela peut être accompli en appliquant une porte de Hadamard avant de mesurer le qubit.
Notez que l'article cité dans l'introduction de ce tutoriel utilise une convention différente pour l'angle , qui diffère de la convention utilisée dans ce tutoriel par un facteur de 2.
À l'étape 3, seuls les qubits liaison sont mesurés. Pour comprendre dans quel état se trouvent les qubits site restants, il est instructif de considérer le cas où l'angle appliqué aux qubits site (A) à l'étape 2 est égal à . Dans ce cas, les qubits site sont laissés dans un état fortement intriqué similaire à l'état GHZ,
En raison du caractère aléatoire des résultats de mesure, l'état réel des qubits site pourrait être un état différent présentant un ordre à longue portée, par exemple, . Cependant, l'état GHZ peut être récupéré en appliquant une opération de décodage basée sur les résultats de mesure. Lorsque l'angle est réduit à partir de , l'ordre à longue portée peut encore être récupéré jusqu'à un angle critique, qui, en l'absence de bruit, est d'environ . En dessous de cet angle, l'état résultant ne présente plus d'intrication à longue portée. Cette transition entre la présence et l'absence d'ordre à longue portée est la transition de phase de Nishimori.
Dans la description ci-dessus, les qubits site n'étaient pas mesurés, et l'opération de décodage peut être effectuée en appliquant des portes quantiques. Dans l'expérience telle qu'implémentée dans la GEM suite, que ce tutoriel suit, les qubits site sont en réalité mesurés, et l'opération de décodage est appliquée lors d'une étape de post-traitement classique.
Dans la description ci-dessus, l'opération de décodage peut être effectuée en appliquant des portes quantiques aux qubits site pour récupérer l'état quantique. Cependant, si l'objectif est de mesurer immédiatement l'état, par exemple à des fins de caractérisation, alors les qubits site sont mesurés en même temps que les qubits liaison, et l'opération de décodage peut être appliquée lors d'une étape de post-traitement classique. C'est ainsi que l'expérience est implémentée dans la GEM suite, que ce tutoriel suit.
En plus de dépendre de l'angle à l'étape 2, qui par défaut balaye 21 valeurs, le circuit du protocole GEM dépend également du schéma d'ordonnancement utilisé pour implémenter les 3 couches de portes . Comme discuté précédemment, il existe 12 schémas d'ordonnancement de ce type. Par conséquent, le nombre total de circuits dans l'expérience est de .
Les circuits de l'expérience peuvent être générés en utilisant la méthode circuits de la classe GemExperiment.
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
Total number of circuits: 252
Pour les besoins de ce tutoriel, il suffit de considérer un seul schéma d'ordonnancement. La cellule de code suivante restreint l'expérience au premier schéma d'ordonnancement. En conséquence, l'expérience ne comporte que 21 circuits, un pour chaque angle balayé.
# 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 cellule de code suivante dessine un diagramme du circuit à l'indice 5. Pour réduire la taille du diagramme, les portes de mesure à la fin du circuit sont supprimées.
# 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)
Étape 2 : Optimiser le problème pour l'exécution sur le matériel quantique
La transpilation des circuits quantiques pour l'exécution sur le matériel implique généralement un certain nombre d'étapes. En règle générale, les étapes qui entraînent le plus de surcharge de calcul sont le choix de la disposition des qubits, le routage des portes à deux qubits pour se conformer à la connectivité des qubits du matériel, et l'optimisation du circuit pour minimiser son nombre de portes et sa profondeur. Dans le protocole GEM, les étapes de disposition et de routage sont inutiles car la connectivité du matériel est déjà intégrée dans la conception du protocole. Les circuits disposent déjà d'une disposition de qubits, et les portes à deux qubits sont déjà mappées sur les connexions natives. De plus, afin de préserver la structure du circuit lorsque l'angle varie, seule une optimisation de circuit très basique doit être effectuée.
La classe GemExperiment transpile les circuits de manière transparente lors de l'exécution de l'expérience. Les étapes de disposition et de routage sont déjà remplacées par défaut pour ne rien faire, et l'optimisation du circuit est effectuée à un niveau qui n'optimise que les portes à un seul qubit. Cependant, vous pouvez remplacer ou passer des options supplémentaires en utilisant la méthode set_transpile_options. À des fins de visualisation, la cellule de code suivante transpile manuellement le circuit affiché précédemment et dessine le circuit transpilé.
# 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)

Étape 3 : Exécuter à l'aide des primitives Qiskit
Pour exécuter les circuits du protocole GEM sur le matériel, appelez la méthode run de l'objet GemExperiment. Vous pouvez spécifier le nombre de tirs que vous souhaitez échantillonner pour chaque circuit. La méthode run renvoie un objet ExperimentData que vous devez enregistrer dans une variable. Notez que la méthode run soumet uniquement les tâches sans attendre leur achèvement, il s'agit donc d'un appel non bloquant.
exp_data = gem_exp.run(shots=10_000)
Pour attendre les résultats, appelez la méthode block_for_results de l'objet ExperimentData. Cet appel fera attendre l'interpréteur jusqu'à ce que les tâches soient terminées.
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'])
Étape 4 : Post-traiter et renvoyer le résultat dans le format classique souhaité
À un angle de , l'état décodé serait l'état GHZ en l'absence de bruit. L'ordre à longue portée de l'état GHZ peut être visualisé en traçant l'aimantation des chaînes de bits mesurées. L'aimantation est définie comme la somme des opérateurs de Pauli à un seul qubit,
où est le nombre de qubits site. Sa valeur pour une chaîne de bits est égale à la différence entre le nombre de zéros et le nombre de uns. Mesurer l'état GHZ donne l'état tout zéros ou l'état tout uns avec une probabilité égale, de sorte que l'aimantation serait la moitié du temps et l'autre moitié du temps. En présence d'erreurs dues au bruit, d'autres valeurs apparaîtraient également, mais si le bruit n'est pas trop important, la distribution resterait piquée près de et .
Pour les chaînes de bits brutes avant décodage, la distribution de l'aimantation serait équivalente à celle de chaînes de bits uniformément aléatoires, en l'absence de bruit.
La cellule de code suivante trace l'aimantation des chaînes de bits brutes et des chaînes de bits décodées à l'angle de .
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')
Pour caractériser plus rigoureusement l'ordre à longue portée, vous pouvez examiner la corrélation moyenne à deux points , définie comme
Une valeur plus élevée indique un degré d'intrication plus important. La classe GemExperiment calcule automatiquement cette valeur pour les chaînes de bits décodées dans le cadre du traitement des données expérimentales. Elle stocke une figure accessible via la méthode figure de la classe de données expérimentales. Dans ce cas, le nom de la figure est two_point_correlation.
exp_data.figure("two_point_correlation")
Pour déterminer le point critique de la transition de phase de Nishimori, vous pouvez examiner la variance normalisée de , définie comme
qui quantifie la quantité de fluctuation de l'aimantation au carré. Cette valeur est maximisée au point critique de la transition de phase de Nishimori. En l'absence de bruit, le point critique se situe à environ . En présence de bruit, le point critique est décalé vers le haut, mais la transition de phase est toujours observée tant que le point critique se situe en dessous de .
exp_data.figure("normalized_variance")
Mise à l'échelle de l'expérience
Les cellules de code suivantes exécutent l'expérience pour six plaquettes (49 qubits) et l'ensemble des 12 plaquettes (125 qubits) et tracent la variance normalisée. À mesure que l'expérience est mise à l'échelle vers des tailles plus grandes, la quantité plus importante de bruit décale le point critique vers la droite.
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")
Conclusion
Dans ce tutoriel, vous avez réalisé une transition de phase de Nishimori sur un processeur quantique en utilisant le protocole GEM. Les métriques que vous avez examinées lors du post-traitement, en particulier la corrélation à deux points et la variance normalisée, servent de benchmarks de la capacité du dispositif à générer des états intriqués à longue portée. Ces benchmarks étendent l'utilité du protocole GEM au-delà de l'exploration de phénomènes physiques intéressants. Dans le cadre du protocole, vous avez intriqué des qubits à travers l'ensemble du dispositif en utilisant des circuits de profondeur constante uniquement. Cet exploit n'est possible que grâce à l'utilisation par le protocole de mesures en milieu de circuit. Dans cette expérience, l'état intriqué a été immédiatement mesuré, mais une piste intéressante à explorer serait de continuer à utiliser cet état dans un traitement quantique supplémentaire !
Enquête sur le tutoriel
Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce tutoriel. Vos retours nous aideront à améliorer nos contenus et l'expérience utilisateur.