Utiliser la post-sélection dans les charges de travail
Versions des packages
Le code sur cette page a été développé en utilisant les exigences suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
qiskit-addon-utils~=0.3.1
Lors de l'optimisation de la stratégie d'atténuation d'erreurs d'une charge de travail, il est souvent utile de filtrer les mesures dont on sait qu'elles ont été contaminées par des processus de bruit non markoviens (corrélés). Une méthode pour ce faire consiste à ajouter au circuit une étape de post-traitement qui mesure les qubits actifs et adjacents « spectateurs », applique une rotation lente à chaque qubit, puis les mesure à nouveau. Dans les cas où les deux mesures ne confirment pas un qubit renversé comme prévu, le shot est rejeté en appliquant un masque aux résultats.
Le package Qiskit addon utilities fournit un ensemble de passes de transpiler et une fonction de post-sélection pour appliquer le masque. Cette page fournit des conseils sur la façon d'incorporer la post-sélection dans tes charges de travail quantiques en utilisant un état GHZ à quatre qubits comme exemple.
Créer la charge de travail
Commence par préparer le circuit à exécuter et à transpiler par rapport à un backend qui prend en charge les gates fractionnaires.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-utils qiskit-ibm-runtime
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
circuit = QuantumCircuit(4)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.measure_all()
service = QiskitRuntimeService()
backend = service.least_busy(use_fractional_gates=True)
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
transpiled_circuit = pm.run(circuit)
transpiled_circuit.draw("mpl")
Ajouter des passes de transpiler de post-sélection
Ensuite, crée un gestionnaire de passes prédéfini qui inclut les passes AddPostSelectionMeasures et AddSpectatorMeasures du package qiskit-addon-utils. Cela ajoutera au circuit une séquence de petites rotations angulaires RX (produisant effectivement un long gate X) ainsi qu'un deuxième ensemble de mesures.
from qiskit.transpiler import PassManager
from qiskit_addon_utils.noise_management.post_selection import PostSelector
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
AddPostSelectionMeasures,
AddSpectatorMeasures,
)
post_selection_pm = PassManager(
[
AddSpectatorMeasures(backend.coupling_map, add_barrier=True),
AddPostSelectionMeasures(x_pulse_type="rx"),
]
)
template_circuit_ps = post_selection_pm.run(transpiled_circuit)
template_circuit_ps.draw("mpl", fold=-1, idle_wires=False)
Exécuter le programme quantique
Ensuite, prépare un objet QuantumProgram contenant le circuit à exécuter.
from qiskit_ibm_runtime import QuantumProgram, Executor
shots = 4000
program = QuantumProgram(shots=shots)
program.append_circuit_item(template_circuit_ps)
# Initialize the Executor job and run
executor = Executor(backend)
executor_job = executor.run(program)
print(f"Job ID: {executor_job.job_id()}")
Job ID: d82dumugbeec73alm5g0
Tu peux maintenant interpréter les résultats. Le résultat d'executor est un dictionnaire avec plusieurs clés.
executor_result = executor_job.result()[0]
executor_result.keys()
dict_keys(['meas', 'spec', 'meas_ps', 'spec_ps'])
Ces clés correspondent aux qubits actifs et spectateurs avant les instructions rx (meas et spec) et après les instructions rx (meas_ps et spec_ps). Chacun d'eux est un tableau de tableaux basé sur le nombre de shots et de qubits. Dans ce cas, la forme est (1000, 4).
Créer le masque de post-sélection
À partir de ces mesures, tu peux créer un masque en utilisant la classe PostSelector de qiskit-addon-utils. Ce masque est un tableau booléen où chaque shot est marqué comme True ou False en fonction de l'une des deux stratégies de post-sélection. La première stratégie, node, utilise les informations de qubit pour décider si un shot de mesure doit être rejeté — et la seconde, edge, utilise les informations de connectivité entre voisins les plus proches pour prendre cette décision.
post_selector = PostSelector.from_circuit(
circuit=template_circuit_ps, coupling_map=backend.coupling_map
)
mask_node = post_selector.compute_mask(executor_result, strategy="node")
mask_edge = post_selector.compute_mask(executor_result, strategy="edge")
Les stratégies node et edge rejettent souvent des shots différents. Tu peux choisir n'importe laquelle d'entre elles. Ce notebook effectue un ET bit à bit, ce qui est une stratégie conservatrice qui retient un shot uniquement s'il passe les deux stratégies node et edge.
mask = mask_node & mask_edge
print(f"The combined mask: {mask}")
count_retained = 0
for m in mask:
count_retained += m
print(
f"Percentage of the shots retained is after post selection "
f"{100 * count_retained / shots}"
)
The combined mask: [ True True True ... True True True]
Percentage of the shots retained is after post selection 75.225
Compare la distribution de probabilité avec et sans post-sélection. L'extrait suivant calcule la distribution de probabilité avant et après la post-sélection, ainsi que la distance entre les distributions mesurées et idéales.
counts = {}
counts_ps = {}
for idx, measurement in enumerate(executor_result["meas"]):
bitstring = ""
for bit in measurement:
bitstring += str(int(bit))
if bitstring in counts:
counts[bitstring] += 1
else:
counts[bitstring] = 1
# Compute count data for postselected shots based on the mask
if mask[idx]:
bitstring = ""
for bit in measurement:
bitstring += str(int(bit))
if bitstring in counts_ps:
counts_ps[bitstring] += 1
else:
counts_ps[bitstring] = 1
for key, val in counts.items():
counts[key] = val / shots
for key, val in counts_ps.items():
counts_ps[key] = float(val / count_retained)
Pour démontrer comment la post-sélection a changé tes résultats, calcule la distance entre la distribution de probabilité idéale et les distributions mesurées.
import itertools
from qiskit.visualization import plot_histogram
bitstrings = ["".join(i) for i in itertools.product("01", repeat=4)]
counts_ideal = {}
for bitstring in bitstrings:
counts_ideal[bitstring] = 0.0
counts_ideal["1111"] = 0.5
counts_ideal["0000"] = 0.5
prob_distance = 0.0
prob_distance_ps = 0.0
for bitstring in counts_ideal.keys():
dist = 0.0
dist_ps = 0.0
if bitstring in counts:
dist = abs(counts[bitstring] - counts_ideal[bitstring])
if bitstring in counts_ps:
dist_ps = abs(counts_ps[bitstring] - counts_ideal[bitstring])
prob_distance += dist
prob_distance_ps += dist_ps
print(
f"Distance from ideal distribution before postselection: "
f"{1-prob_distance*0.5}"
)
print(
f"Distance from ideal distribution before after-selection: "
f"{1-prob_distance_ps*0.5}"
)
plot_histogram([counts, counts_ps], legend=["Normal", "Post selected"])
Distance from ideal distribution before postselection: 0.9015
Distance from ideal distribution before after-selection: 0.9416749750747756
Bien que la post-sélection puisse améliorer significativement la qualité des résultats en filtrant les mesures de résultats affectées par le bruit non markovien, ce n'est pas une solution complète à l'atténuation d'erreurs à elle seule. La post-sélection réduit l'impact de certaines erreurs en rejetant les résultats de mesure invalides, mais cela se fait au prix d'une surcharge d'échantillonnage accrue et ne traite pas tous les mécanismes d'erreur présents dans le matériel quantique à court terme. En conséquence, il est probablement insuffisant de se fier uniquement à la post-sélection pour des circuits plus complexes ou plus profonds. Au lieu de cela, la post-sélection est plus efficace lorsqu'elle est utilisée dans le cadre d'une stratégie d'atténuation d'erreurs plus large — en complément de techniques telles que l'atténuation des erreurs de mesure, la compilation de circuits tenant compte du bruit ou l'annulation d'erreur probabiliste — pour améliorer la fiabilité des charges de travail quantiques tout en équilibrant la précision et le coût des ressources.
Étapes suivantes
- Comprends comment incorporer l'apprentissage du bruit dans une charge de travail quantique.
- Parcours les autres techniques d'atténuation et de suppression d'erreurs disponibles.
- Apprends comment utiliser les codes espace-temps pour une approche à faible surcharge de la détection d'erreurs