Atténuation des erreurs de lecture pour la primitive Sampler à l'aide d' M3
Estimation de l'utilisation : moins d'une minute sur un processeur Heron r2 (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)
Arrière-plan
Contrairement à la primitive Estimateur, la primitive Échantillonneur ne dispose pas d'un support intégré pour l'atténuation des erreurs. Plusieurs des méthodes utilisées par l'estimateur sont spécifiquement conçues pour les valeurs d'espérance et ne sont donc pas applicables à l'échantillonneur primitif. L'atténuation des erreurs de lecture constitue une exception : il s'agit d'une méthode très efficace qui s'applique également à l'échantillonneur primitif.
Le module complémentaire Qiskit de M3 met en œuvre une méthode efficace de réduction des erreurs de lecture. Ce tutoriel explique comment utiliser l'addon Qiskit de M3 pour réduire les erreurs de lecture de la primitive Sampler.
Qu'est-ce qu'une erreur de lecture?
Immédiatement avant la mesure, l'état d'un registre de qubits est décrit par une superposition d'états de base de calcul décrit par une superposition d'états de base de calcul, ou par une matrice de densité. La mesure du registre de qubits en un registre de bits classique se fait ensuite en deux étapes. La mesure quantique proprement dite est d'abord effectuée. Cela signifie que l'état du registre de qubits est projeté sur un état de base unique caractérisé par une chaîne de par une chaîne de s et s. La deuxième étape consiste à lire la chaîne de bits caractérisant cet état de base et à l'écrire dans la mémoire classique de l'ordinateur. Nous appelons cette étape " lecture ". Il s'avère que la deuxième étape (lecture) comporte plus d'erreurs que la première étape (projection sur les états de base). Cela est logique si l'on se souvient que la lecture nécessite la détection d'un état quantique microscopique et son amplification dans le domaine macroscopique microscopique et de l'amplifier dans le domaine macroscopique. Un résonateur de lecture est couplé au qubit (transmon) le qubit (transmon), subissant ainsi un très faible décalage de fréquence. Une impulsion de micro-ondes est ensuite rebondie sur le résonateur, qui subit à son tour de légères modifications de ses caractéristiques caractéristiques. L'impulsion réfléchie est ensuite amplifiée et analysée. Il s'agit d'un processus il s'agit d'un processus délicat et sujet à de nombreuses erreurs.
Le point important est que, bien que la mesure quantique et la lecture soient toutes deux sujettes à l'erreur, c'est la dernière qui subit l'erreur dominante, appelée erreur de lecture cette dernière subit l'erreur dominante, appelée erreur de lecture, qui fait l'objet de ce tutoriel.
Contexte théorique
Si la chaîne de bits échantillonnée (stockée dans la mémoire classique) diffère de la chaîne de bits caractérisant l'état quantique projeté, nous disons qu'une erreur de lecture s'est produite l'état quantique projeté, nous disons qu'une erreur de lecture s'est produite. On observe que ces erreurs sont aléatoires et non corrélées d'un échantillon à l'autre. Il s'est avéré utile de modéliser l'erreur de lecture comme un canal classique bruyant. En d'autres termes, pour chaque paire de chaînes de bits chaînes de bits et , il existe une probabilité fixe qu'une valeur réelle de soit lue à tort comme soit incorrectement lue comme .
Plus précisément, pour chaque paire de chaînes de bits , il existe une probabilité (conditionnelle) que soit lu, étant donné que la vraie valeur est C'est-à-dire,
où est le nombre de bits dans le registre de lecture. Pour être concret, nous supposons que est un nombre entier décimal dont la représentation binaire est la chaîne de bits qui désigne les états de la base de calcul. Nous appelons la matrice la matrice d'affectation. Pour une valeur réelle fixe , la somme de la probabilité sur tous les résultats bruyants doit donner . C'est-à-dire
Une matrice sans entrées négatives qui satisfait à (1) est appelée stochastique gauche. Une matrice stochastique à gauche est également appelée stochastique à colonnes car chacune de ses colonnes s'additionne à . Nous déterminons expérimentalement des valeurs approximatives pour chaque élément en préparant de manière répétée chaque état de base et en calculant les fréquences d'apparition des chaînes de bits échantillonnées de l'occurrence des chaînes de bits échantillonnées.
Si une expérience consiste à estimer une distribution de probabilité sur les chaînes de bits de sortie par échantillonnage répété, nous pouvons utiliser pour atténuer l'erreur de lecture au niveau de la distribution, nous pouvons utiliser pour atténuer l'erreur de lecture au niveau de la distribution. La première étape consiste à répéter plusieurs fois un circuit fixe intéressant, créer un histogramme de chaînes de bits échantillonnées. L'histogramme normalisé est la distribution de probabilité mesurée sur les chaînes de bits possibles les chaînes de bits possibles, que nous désignons par . La probabilité (estimée) d'échantillonner une chaîne de bits est égale à la somme de toutes les chaînes binaires réelles , chacune étant pondérée par la probabilité qu'elle soit confondue avec la probabilité qu'elle soit confondue avec . Cet énoncé sous forme de matrice est le suivant
où est la vraie distribution. En d'autres termes, l'erreur de lecture a pour effet de multiplier la distribution idéale sur les chaînes de bits par la matrice d'affectation pour pour produire la distribution observée . Nous avons mesuré et , mais nous n'avons pas d'accès direct à . En principe, nous obtiendrons la véritable distribution des chaînes de bits pour notre circuit obtenir la véritable distribution des chaînes de bits pour notre circuit en résolvant numériquement l'équation (2) pour .
Avant de poursuivre, il convient de noter quelques caractéristiques importantes de cette approche naïve.
- Dans la pratique, l'équation (2) n'est pas résolue en inversant . Les routines d'algèbre linéaire dans les bibliothèques de logiciels utilisent des méthodes plus stables, plus précises et plus efficaces.
- Lors de l'estimation de , nous avons supposé que seules des erreurs de lecture se produisaient. En particulier, nous supposons qu'il n'y a pas eu d'erreurs de préparation de l'état et de mesure quantique - ou du moins qu'elles ont été atténuées ou du moins qu'elles ont été atténuées. Dans la mesure où il s'agit d'une bonne hypothèse, ne représente en réalité qu'une erreur de lecture seulement une erreur de lecture. Mais lorsque nous utilisons pour corriger une distribution mesurée sur des chaînes de bits, nous ne faisons pas cette hypothèse. En fait, nous nous attendons à ce qu'un circuit intéressant qu'un circuit intéressant introduise du bruit, par exemple des erreurs de porte. La "vraie" distribution comprend toujours les effets des erreurs qui ne sont pas atténuées d'une autre manière.
Cette méthode, bien qu'utile dans certaines circonstances, souffre de quelques limitations.
L'espace et le temps nécessaires à l'estimation de augmentent de façon exponentielle dans :
- L'estimation de et est sujette à des erreurs statistiques dues à un échantillonnage limité. Ce bruit peut être rendu aussi faible que souhaité au prix d'un plus grand nombre de tirs (jusqu'à l'échelle de temps de la dérive des paramètres matériels qui entraînent des erreurs systématiques dans ). Toutefois, si aucune hypothèse n'est faite sur les chaînes de bits observées lors de l'exécution de l'atténuation, le nombre de tirs requis pour estimer croît au moins de manière exponentielle au moins de manière exponentielle dans .
- est une matrice . Lorsque , la quantité de mémoire nécessaire pour stocker est supérieure à la mémoire disponible dans un ordinateur portable puissant est supérieure à la mémoire disponible dans un ordinateur portable puissant.
Les autres limitations sont les suivantes :
- La distribution récupérée peut avoir une ou plusieurs probabilités négatives (tout en étant égale à un) ou plusieurs probabilités négatives (dont la somme est toujours égale à un). Une solution est de minimiser sous la contrainte que chaque entrée de soit non négative. Cependant, la durée d'exécution d'une telle méthode est beaucoup plus longue que la résolution directe de l'équation (2) est beaucoup plus longue que la résolution directe de l'équation (2).
- Cette procédure d'atténuation fonctionne au niveau d'une distribution de probabilité sur les chaînes de bits. En particulier, il ne peut pas corriger une erreur dans une chaîne de bits individuelle observée individuelle observée.
Module complémentaire Qiskit M3 : mise à l'échelle vers des chaînes de bits plus longues
La résolution de l'équation (2) à l'aide de routines d'algèbre linéaire numérique standard est limitée à des chaînes de bits d'une longueur maximale d'environ 10 bits. M3 peut cependant traiter des chaînes de bits beaucoup plus longues. Les deux principales propriétés de M3 qui rendent cela possible sont les suivantes :
- Les corrélations de l'erreur de lecture d'ordre trois et plus entre les collections de bits sont considérées comme négligeables et sont ignorées. En principe, au prix d'un plus grand nombre de tirs, on pourrait également estimer des corrélations plus élevées.
- Plutôt que de construire explicitement , nous utilisons une matrice effective beaucoup plus petite qui enregistre les probabilités uniquement pour les chaînes de bits collectées lors de la construction de probabilités uniquement pour les chaînes de bits collectées lors de la construction de .
À un niveau élevé, la procédure fonctionne comme suit.
Tout d'abord, nous construisons des blocs de construction à partir desquels nous pouvons construire une description simplifiée et efficace de . Ensuite, nous exécutons de manière répétée le circuit qui nous intéresse et collectons des chaînes de bits que nous utilisons pour construire à la fois et, à l'aide des blocs de construction, une description efficace de et, à l'aide des blocs de construction, une description efficace de .
Plus précisément,
-
Les matrices d'affectation d'un qubit unique sont estimées pour chaque qubit. Pour ce faire, nous préparons à plusieurs reprises préparons le registre de qubits dans l'état tout-zéro puis dans l'état tout-un , et enregistrons la probabilité que chaque qubit soit lu de manière incorrecte incorrecte.
-
Les corrélations d'ordre trois et plus sont supposées négligeables et sont ignorées.
Au lieu de cela, nous construisons un certain nombre de de matrices d'affectation à un qubit et un certain nombre de de matrices d'affectation à deux qubits deux qubits. Ces matrices d'affectation à un ou deux qubits sont stockées pour une utilisation ultérieure pour une utilisation ultérieure.
-
Après avoir échantillonné de façon répétée un circuit pour construire , nous construisons une approximation efficace de en utilisant uniquement des chaînes de bits échantillonnées lors de la construction de . Cette matrice effective est construite à l'aide des matrices à un et deux qubits décrites au point précédent. La dimension linéaire de cette matrice est au plus de l'ordre du nombre de clichés utilisés dans la construction de, ce qui est beaucoup plus petit que le nombre de clichés utilisés dans la construction de de plans utilisés pour construire , ce qui est beaucoup plus petit que la dimension la dimension de la matrice d'affectation complète .
Pour plus de détails techniques sur M3, vous pouvez consulter Scalable Mitigation of Measurement Errors on Quantum Computers.
Application de l'algorithme de l'échec de l'interférence quantique ( M3 ) à un algorithme quantique
Nous appliquerons l'atténuation de la lecture de M3 au problème du décalage caché. Le problème du décalage caché et les problèmes étroitement liés tels que le problème du sous-groupe caché ont été conçus à l'origine dans un cadre tolérant aux pannes (plus précisément, avant que les QPU tolérantes aux pannes ne s'avèrent possibles!) Mais ils sont également étudiés avec les processeurs disponibles. Un exemple d'accélération exponentielle algorithmique obtenue pour une variante du problème du décalage caché sur des QPUs IBM® de 127 qubits est présenté dans cet article ( version arXiv ).
Dans ce qui suit, toute l'arithmétique est booléenne. En d'autres termes, pour , l'addition, est la fonction logique XOR. En outre, la multiplication (ou ) est la fonction logique ET. Pour , est défini par l'application bit à bit de XOR. Le produit de points est défini par .
Opérateur de Hadamard et transformée de Fourier
Lors de la mise en œuvre d'algorithmes quantiques, il est très courant d'utiliser l'opérateur de Hadamard comme transformée de Fourier. Les états de base de calcul sont parfois appelés états classiques. Ils ont une relation biunivoque avec les chaînes de bits classiques une relation biunivoque avec les chaînes de bits classiques. L'opérateur de Hadamard -qubit sur les états classiques peut être considéré comme une transformée de Fourier sur l'hypercube booléen :
Considérons un état correspondant à une chaîne de bits fixe . En appliquant , et en utilisant , nous voyons que la transformée de Fourier de peut être écrite comme suit
Le Hadamard est son propre inverse, c'est-à-dire, . Ainsi, la transformée de Fourier inverse est le même opérateur, . Explicitement, nous avons,
Le problème du changement caché
Nous considérons un exemple simple de problème de décalage caché. Le problème consiste à identifier un changement constant dans l'entrée d'une fonction. La fonction que nous considérons est le produit de points. C'est le membre le plus simple d'une grande classe de fonctions qui admettent une accélération quantique pour le problème de la grâce à des techniques similaires à celles présentées ci-dessous.
Soit des chaînes de bits de longueur . Nous définissons par
Soit des chaînes de bits fixes de longueur . Nous définissons en outre par
où et sont des paramètres (cachés). Nous disposons de deux boîtes noires, l'une implémentant , et l'autre . Nous supposons que nous savons qu'elles calculent les fonctions définies ci-dessus, sauf que nous ne connaissons ni ni . Le jeu consiste à déterminer les chaînes de bits cachées (décalages) et en interrogeant et . Il est clair que si nous jouons le jeu de manière classique, nous avons besoin des requêtes pour déterminer et . Par exemple, nous pouvons interroger avec toutes les paires de chaînes de caractères telles qu'un élément de la paire est entièrement composé de zéros et que l'autre élément a exactement un élément défini sur . À chaque interrogation, nous apprenons un élément de ou de . Cependant, nous verrons que, si les boîtes noires sont implémentées comme des circuits quantiques, nous pouvons déterminer et en interrogeant une seule fois et .
Dans le contexte de la complexité algorithmique, une boîte noire est appelée oracle. En plus d'être opaque, un oracle a la propriété de consommer l'entrée et de produire la sortie instantanément produit la sortie instantanément, n'ajoutant rien au budget de complexité de l'algorithme dans lequel il est intégré dans lequel il est intégré. En fait, dans le cas présent, les oracles implémentant et seront considérés comme efficaces.
Circuits quantiques pour l' et l'
Nous avons besoin des ingrédients suivants pour mettre en œuvre et en tant que circuits quantiques.
Pour les états classiques à un qubit , avec , la porte contrôlée peut être écrite comme suit
Nous opérerons avec des portes CZ, une sur , et une sur , et ainsi de suite, jusqu'à . Nous appelons cet opérateur .
est une version quantique de :
Nous devons également mettre en œuvre un décalage de chaîne de bits. Nous désignons l'opérateur sur le registre par et de même sur le registre . Ces opérateurs appliquent partout où un seul bit est , et l'identité partout où elle est . Nous avons donc
La deuxième boîte noire est mise en œuvre par l'unité , donnée par
Pour ce faire, nous appliquons les opérateurs de droite à gauche à l'état . D'abord
Ensuite,
Enfin,
qui est en effet la version quantique de .
L'algorithme de changement caché
Nous allons maintenant assembler les pièces du puzzle pour résoudre le problème du décalage caché. Nous commençons par appliquer les Hadamards aux registres initialisés à l'état zéro.
Ensuite, nous interrogeons l'oracle pour obtenir
Dans la dernière ligne, nous avons omis le facteur de phase global constant , et dénotons l'égalité jusqu'à une phase par . Ensuite, l'application de l'oracle introduit un autre facteur de , annulant celui déjà présent déjà présent. Nous avons alors :
L'étape finale consiste à appliquer la transformée de Fourier inverse, , ce qui donne
Le circuit est terminé. En l'absence de bruit, l'échantillonnage des registres quantiques renvoie les chaînes de bits retournera les chaînes de bits avec la probabilité .
Le produit intérieur booléen est un exemple de ce que l'on appelle les fonctions coudées. Nous ne définirons pas ici les fonctions coudées mais nous nous contenterons de noter qu'elles "sont maximalement résistantes aux attaques qui cherchent à exploiter une dépendance des sorties sur un sous-espace linéaire des entrées des sorties sur un sous-espace linéaire des entrées" Cette citation est tirée de l'article Quantum algorithms for highly non-linear Boolean functions, qui donne des algorithmes efficaces de décalage caché pour plusieurs classes de fonctions coudées. L'algorithme de ce tutoriel figure à la section 3.1 de l'article.
Dans le cas plus général, le circuit permettant de trouver un décalage caché est le suivant
Dans le cas général, et sont des fonctions d'une seule variable. Notre exemple de produit intérieur a cette forme si nous laissons , avec égal à la concaténation de et , et égal à la concaténation de et de et . Le cas général nécessite exactement deux oracles : Un oracle pour et un pour , où ce dernier est une fonction connue comme le dual de la fonction coudée . La fonction produit intérieur possède la propriété d'auto-dualité .
Dans notre circuit pour le décalage caché sur le produit intérieur, nous avons omis la couche intermédiaire de Hadamards qui apparaît dans le circuit pour le cas général de Hadamards qui apparaît dans le circuit pour le cas général. Bien que dans le cas général cette couche est nécessaire, nous avons gagné un peu de profondeur en l'omettant, au prix d'un peu de post-traitement, car le résultat est au lieu de de post-traitement car la sortie est au lieu de .
Exigences
Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :
- Qiskit SDK v2.1 ou plus tard, avec prise en charge de la visualisation
- Qiskit Runtime v0.41 ou plus tard (
pip install qiskit-ibm-runtime) - M3 Qiskit addon v3.0 (
pip install mthree)
Configuration
from collections.abc import Iterator, Sequence
from random import Random
from qiskit.circuit import (
CircuitInstruction,
QuantumCircuit,
QuantumRegister,
Qubit,
)
from qiskit.circuit.library import CZGate, HGate, XGate
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
import timeit
import matplotlib.pyplot as plt
from qiskit_ibm_runtime import SamplerV2 as Sampler
import mthreeÉtape 1 : Mettre en correspondance les entrées classiques avec un problème quantique
Tout d'abord, nous écrivons les fonctions permettant de mettre en œuvre le problème du décalage caché sous la forme d'un site QuantumCircuit.
def apply_hadamards(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
"""Apply a Hadamard gate to every qubit."""
for q in qubits:
yield CircuitInstruction(HGate(), [q], [])
def apply_shift(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Apply X gates where the bits of the shift are equal to 1."""
for i, q in zip(range(shift.bit_length()), qubits):
if shift >> i & 1:
yield CircuitInstruction(XGate(), [q], [])
def oracle_f(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
"""Apply the f oracle."""
for i in range(0, len(qubits) - 1, 2):
yield CircuitInstruction(CZGate(), [qubits[i], qubits[i + 1]])
def oracle_g(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Apply the g oracle."""
yield from apply_shift(qubits, shift)
yield from oracle_f(qubits)
yield from apply_shift(qubits, shift)
def determine_hidden_shift(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Determine the hidden shift."""
yield from apply_hadamards(qubits)
yield from oracle_g(qubits, shift)
# We omit this layer in exchange for post processing
# yield from apply_hadamards(qubits)
yield from oracle_f(qubits)
yield from apply_hadamards(qubits)
def run_hidden_shift_circuit(n_qubits, rng):
hidden_shift = rng.getrandbits(n_qubits)
qubits = QuantumRegister(n_qubits, name="q")
circuit = QuantumCircuit.from_instructions(
determine_hidden_shift(qubits, hidden_shift), qubits=qubits
)
circuit.measure_all()
# Format the hidden shift as a string.
hidden_shift_string = format(hidden_shift, f"0{n_qubits}b")
return (circuit, hidden_shift, hidden_shift_string)
def display_circuit(circuit):
return circuit.remove_final_measurements(inplace=False).draw(
"mpl", idle_wires=False, scale=0.5, fold=-1
)Commençons par un petit exemple :
n_qubits = 6
random_seed = 12345
rng = Random(random_seed)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
n_qubits, rng
)
print(f"Hidden shift string {hidden_shift_string}")
display_circuit(circuit)Output:
Hidden shift string 011010
Étape 2 : Optimiser les circuits pour l'exécution sur du matériel quantique
job_tags = [
f"shift {hidden_shift_string}",
f"n_qubits {n_qubits}",
f"seed = {random_seed}",
]
job_tagsOutput:
['shift 011010', 'n_qubits 6', 'seed = 12345']
# Uncomment this to run the circuits on a quantum computer on IBMCloud.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=100
)
# from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2
# backend = FakeMelbourneV2()
# backend.refresh(service)
print(f"Using backend {backend.name}")
def get_isa_circuit(circuit, backend):
pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=1234
)
isa_circuit = pass_manager.run(circuit)
return isa_circuit
isa_circuit = get_isa_circuit(circuit, backend)
display_circuit(isa_circuit)Output:
Using backend ibm_kingston
Étape 3 : Exécutez les circuits à l'aide d' Qiskit primitives
# submit job for solving the hidden shift problem using the Sampler primitive
NUM_SHOTS = 50_000
def run_sampler(backend, isa_circuit, num_shots):
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags
pubs = [(isa_circuit, None, NUM_SHOTS)]
job = sampler.run(pubs)
return job
def setup_mthree_mitigation(isa_circuit, backend):
# retrieve the final qubit mapping so mthree knows which qubits to calibrate
qubit_mapping = mthree.utils.final_measurement_mapping(isa_circuit)
# submit jobs for readout error calibration
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(qubit_mapping, rep_delay=None)
return mit, qubit_mappingjob = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)Étape 4 : Post-traitement et restitution des résultats dans un format classique
Dans la discussion théorique ci-dessus, nous avons déterminé que pour l'entrée , nous attendons la sortie . Une complication supplémentaire est que, afin d'avoir un circuit plus simple (pré-transposé), nous avons inséré les portes CZ requises entre les paires de qubits voisines paires de qubits voisines. Cela revient à entrelacer les chaînes de bits et sous la forme . La chaîne de sortie sera entrelacée de la même manière : . La fonction unscramble ci-dessous transforme la chaîne de sortie de en afin que les chaînes d'entrée et de sortie puissent être comparées directement.
# retrieve bitstring counts
def get_bitstring_counts(job):
result = job.result()
pub_result = result[0]
counts = pub_result.data.meas.get_counts()
return counts, pub_resultcounts, pub_result = get_bitstring_counts(job)La distance de Hamming entre deux chaînes de bits est le nombre d'indices auxquels les bits diffèrent.
def hamming_distance(s1, s2):
weight = 0
for c1, c2 in zip(s1, s2):
(c1, c2) = (int(c1), int(c2))
if (c1 == 1 and c2 == 1) or (c1 == 0 and c2 == 0):
weight += 1
return weight# Replace string of form a1b1a2b2... with b1a1b2a1...
# That is, reverse order of successive pairs of bits.
def unscramble(bitstring):
ps = [bitstring[i : i + 2][::-1] for i in range(0, len(bitstring), 2)]
return "".join(ps)
def find_hidden_shift_bitstring(counts, hidden_shift_string):
# convert counts to probabilities
probs = {
unscramble(bitstring): count / NUM_SHOTS
for bitstring, count in counts.items()
}
# Retrieve the most probable bitstring.
most_probable = max(probs, key=lambda x: probs[x])
print(f"Expected hidden shift string: {hidden_shift_string}")
if most_probable == hidden_shift_string:
print("Most probable bitstring matches hidden shift 😊.")
else:
print("Most probable bitstring didn't match hidden shift ☹️.")
print("Top 10 bitstrings and their probabilities:")
display(
{
k: (v, hamming_distance(hidden_shift_string, k))
for k, v in sorted(
probs.items(), key=lambda x: x[1], reverse=True
)[:10]
}
)
return probs, most_probableprobs, most_probable = find_hidden_shift_bitstring(
counts, hidden_shift_string
)Output:
Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'011010': (0.9743, 6),
'001010': (0.00812, 5),
'010010': (0.0063, 5),
'011000': (0.00554, 5),
'011011': (0.00492, 5),
'011110': (0.00044, 5),
'001000': (0.00012, 4),
'010000': (8e-05, 4),
'001011': (6e-05, 4),
'000010': (6e-05, 4)}
Enregistrons la probabilité de la chaîne de bits la plus probable avant d'appliquer la réduction des erreurs de lecture à l'aide de M3.
max_probability_before_M3 = probs[most_probable]
max_probability_before_M3Output:
0.9743
Nous appliquons maintenant la correction de lecture apprise par M3 aux comptages.
La fonction apply_corrections renvoie une distribution quasi-probabiliste. Voici une liste float d'objets dont la somme est égale à . Cependant, certaines valeurs peuvent être négatives.
def perform_mitigation(mit, counts, qubit_mapping):
# mitigate readout error
quasis = mit.apply_correction(counts, qubit_mapping)
# print results
most_probable_after_m3 = unscramble(max(quasis, key=lambda x: quasis[x]))
is_hidden_shift_identified = most_probable_after_m3 == hidden_shift_string
if is_hidden_shift_identified:
print("Most probable bitstring matches hidden shift 😊.")
else:
print("Most probable bitstring didn't match hidden shift ☹️.")
print("Top 10 bitstrings and their quasi-probabilities:")
topten = {
unscramble(k): f"{v:.2e}"
for k, v in sorted(quasis.items(), key=lambda x: x[1], reverse=True)[
:10
]
}
max_probability_after_M3 = float(topten[most_probable_after_m3])
display(topten)
return max_probability_after_M3, is_hidden_shift_identifiedprint(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
mit, counts, qubit_mapping
)Output:
Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'011010': '1.01e+00',
'001010': '8.75e-04',
'001000': '7.38e-05',
'010000': '4.51e-05',
'111000': '2.18e-05',
'001011': '1.74e-05',
'000010': '6.42e-06',
'011001': '-7.18e-06',
'011000': '-4.53e-04',
'010010': '-1.28e-03'}
Comparez l'identification de la chaîne de décalage cachée avant et après l'application de la correction d' M3
def compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
):
is_probability_improved = (
max_probability_after_M3 > max_probability_before_M3
)
print(f"Most probable probability before M3: {max_probability_before_M3}")
print(f"Most probable probability after M3: {max_probability_after_M3}")
if is_hidden_shift_identified and is_probability_improved:
print("Readout error mitigation effective! 😊")
else:
print("Readout error mitigation not effective. ☹️")compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
)Output:
Most probable probability before M3: 0.9743
Most probable probability after M3: 1.01
Readout error mitigation effective! 😊
Tracer la courbe représentant l'évolution du temps CPU requis par M3 en fonction du nombre de clichés
# Collect samples for numbers of shots varying from 5000 to 25000.
shots_range = range(5000, NUM_SHOTS + 1, 2500)
times = []
for shots in shots_range:
print(f"Applying M3 correction to {shots} shots...")
t0 = timeit.default_timer()
_ = mit.apply_correction(
pub_result.data.meas.slice_shots(range(shots)).get_counts(),
qubit_mapping,
)
t1 = timeit.default_timer()
print(f"\tDone in {t1 - t0} seconds.")
times.append(t1 - t0)
fig, ax = plt.subplots()
ax.plot(shots_range, times, "o--")
ax.set_xlabel("Shots")
ax.set_ylabel("Time (s)")
ax.set_title("Time to apply M3 correction")Output:
Applying M3 correction to 5000 shots...
Done in 0.003321983851492405 seconds.
Applying M3 correction to 7500 shots...
Done in 0.004425413906574249 seconds.
Applying M3 correction to 10000 shots...
Done in 0.006366567220538855 seconds.
Applying M3 correction to 12500 shots...
Done in 0.0071477219462394714 seconds.
Applying M3 correction to 15000 shots...
Done in 0.00860048783943057 seconds.
Applying M3 correction to 17500 shots...
Done in 0.010026784148067236 seconds.
Applying M3 correction to 20000 shots...
Done in 0.011459112167358398 seconds.
Applying M3 correction to 22500 shots...
Done in 0.012727141845971346 seconds.
Applying M3 correction to 25000 shots...
Done in 0.01406092382967472 seconds.
Applying M3 correction to 27500 shots...
Done in 0.01546052098274231 seconds.
Applying M3 correction to 30000 shots...
Done in 0.016769016161561012 seconds.
Applying M3 correction to 32500 shots...
Done in 0.019537431187927723 seconds.
Applying M3 correction to 35000 shots...
Done in 0.019739801064133644 seconds.
Applying M3 correction to 37500 shots...
Done in 0.021093040239065886 seconds.
Applying M3 correction to 40000 shots...
Done in 0.022840639110654593 seconds.
Applying M3 correction to 42500 shots...
Done in 0.023974396288394928 seconds.
Applying M3 correction to 45000 shots...
Done in 0.026412792038172483 seconds.
Applying M3 correction to 47500 shots...
Done in 0.026364430785179138 seconds.
Applying M3 correction to 50000 shots...
Done in 0.02820305060595274 seconds.
Text(0.5, 1.0, 'Time to apply M3 correction')
Interpréter l'intrigue
Le graphique ci-dessus montre que le temps nécessaire à l'application de la correction M3 augmente linéairement avec le nombre de prises de vue.
Mise à l'échelle par augmentation
n_qubits = 80
rng = Random(12345)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
n_qubits, rng
)
print(f"Hidden shift string {hidden_shift_string}")Output:
Hidden shift string 00000010100110101011101110010001010000110011101001101010101001111001100110000111
isa_circuit = get_isa_circuit(circuit, backend)job = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)counts, pub_result = get_bitstring_counts(job)probs, most_probable = find_hidden_shift_bitstring(
counts, hidden_shift_string
)Output:
Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': (0.50402,
80),
'00000010100110101011101110010001010000110011100001101010101001111001100110000111': (0.0396,
79),
'00000010100110101011101110010001010000110011101001101010101001111001100100000111': (0.0323,
79),
'00000010100110101011101110010001010000110011101001101010101001101001100110000111': (0.01936,
79),
'00000010100110101011101110010011010000110011101001101010101001111001100110000111': (0.01432,
79),
'00000010100110101011101110010001010000110011101001101010101001011001100110000111': (0.0101,
79),
'00000010100110101011101110010001010000110011101001101010101001110001100110000111': (0.00924,
79),
'00000010100110101011101110010001010000010011101001101010101001111001100110000111': (0.00908,
79),
'00000010100110101011100110010001010000110011101001101010101001111001100110000111': (0.00888,
79),
'00000010100110101011101110010001010000110011101001100010101001111001100110000111': (0.0082,
79)}
Nous constatons que la chaîne de caractères cachée correcte a été trouvée. En outre, les neuf chaînes de bits les plus probables qui suivent ne sont erronées que dans une seule position.
Enregistrez la probabilité la plus probable :
max_probability_before_M3 = probs[most_probable]
max_probability_before_M3Output:
0.50402
print(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
mit, counts, qubit_mapping
)Output:
Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': '9.85e-01',
'00000010100110101011101110010001010000110011100001101010101001111001100110000111': '6.84e-03',
'00000010100110101011100110010001010000110011101001101010101001111001100110000111': '3.87e-03',
'00000010100110101011101110010011010000110011101001101010101001111001100110000111': '3.42e-03',
'00000010100110101011101110010001010000110011101001101010101001111001100100000111': '3.30e-03',
'00000010100110101011101110010001010000110011101001101010101001110001100110000111': '3.28e-03',
'00000010100010101011101110010001010000110011101001101010101001111001100110000111': '2.62e-03',
'00000010100110101011101110010001010000110011101001101010101001101001100110000111': '2.43e-03',
'00000010100110101011101110010000010000110011101001101010101001111001100110000111': '1.73e-03',
'00000010100110101011101110010001010000110011101001101010101001111001000110000111': '1.63e-03'}
compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
)Output:
Most probable probability before M3: 0.54348
Most probable probability after M3: 0.99
Readout error mitigation effective! 😊
Les résultats montrent que l'erreur de lecture est la principale source d'erreur et que l'atténuation M3 a été efficace.