Zum Hauptinhalt springen

Postselection in Workloads verwenden

Paketversionen

Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
qiskit-addon-utils~=0.3.1

Bei der Optimierung der Fehlerminderungsstrategie eines Workloads ist es oft nützlich, Messungen herauszufiltern, die bekanntermaßen durch nicht-Markovsche (korrelierte) Rauschprozesse kontaminiert wurden. Eine solche Methode besteht darin, einen Circuit mit einem Nachverarbeitungsschritt zu erweitern, der aktive und benachbarte „Spektator"-Qubits misst, eine langsame Rotation auf jeden Qubit anwendet und sie dann erneut misst. In Fällen, in denen die beiden Messungen keinen erwarteten Qubit-Flip bestätigen, wird der Shot verworfen, indem eine Maske auf die Ergebnisse angewendet wird.

Das Paket Qiskit addon utilities stellt eine Reihe von Transpiler-Passes und eine Postselection-Funktion zur Verfügung, um die Maske anzuwenden. Diese Seite zeigt, wie du Postselection in deine Quanten-Workloads integrierst, am Beispiel eines Vier-Qubit-GHZ-Zustands.

Workload erstellen

Beginne damit, den auszuführenden Circuit vorzubereiten und ihn gegen ein Backend zu transpilieren, das fraktionale Gates unterstützt.

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

Output of the previous code cell

Postselection-Transpiler-Passes hinzufügen

Erstelle als Nächstes einen Preset-Pass-Manager, der die Passes AddPostSelectionMeasures und AddSpectatorMeasures aus dem Paket qiskit-addon-utils enthält. Dieser erweitert den Circuit um eine Sequenz kleiner RX-Rotationen (die effektiv ein langes X-Gate erzeugen) sowie einen zweiten Satz von Messungen.

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)

Output of the previous code cell

Quantenprogramm ausführen

Bereite als Nächstes ein QuantumProgram-Objekt vor, das den auszuführenden Circuit enthält.

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

Jetzt kannst du die Ergebnisse interpretieren. Das Executor-Ergebnis ist ein Dictionary mit mehreren Schlüsseln.

executor_result = executor_job.result()[0]
executor_result.keys()
dict_keys(['meas', 'spec', 'meas_ps', 'spec_ps'])

Diese Schlüssel entsprechen den aktiven und Spektator-Qubits vor den rx-Anweisungen (meas und spec) und nach den rx-Anweisungen (meas_ps und spec_ps). Jedes davon ist ein Array von Arrays, basierend auf der Anzahl der Shots und Qubits. In diesem Fall hat es die Form (1000, 4).

Postselection-Maske erstellen

Aus diesen Messungen kannst du eine Maske mithilfe der Klasse PostSelector aus qiskit-addon-utils erstellen. Diese Maske ist ein boolesches Array, bei dem jeder Shot als True oder False markiert wird, basierend auf einer von zwei Postselection-Strategien. Die erste Strategie, node, verwendet Qubit-Informationen, um zu entscheiden, ob ein Mess-Shot verworfen werden soll — und die zweite, edge, verwendet Informationen zur nächsten-Nachbar-Konnektivität für diese Entscheidung.

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

Sowohl die Node- als auch die Edge-Strategie verwerfen oft unterschiedliche Shots. Du kannst eine davon auswählen. In diesem Notebook wird ein bitweises AND verwendet, eine konservative Strategie, die einen Shot nur dann beibehält, wenn er von beiden Strategien (Node und Edge) bestanden wird.

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

Vergleiche die Wahrscheinlichkeitsverteilung mit und ohne Postselection. Der folgende Code berechnet die Wahrscheinlichkeitsverteilung vor und nach der Postselection sowie den Abstand zwischen der gemessenen und der idealen Verteilung.

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)

Um zu zeigen, wie die Postselection deine Ergebnisse verändert hat, berechne den Abstand zwischen der idealen Wahrscheinlichkeitsverteilung und den gemessenen.

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

Output of the previous code cell

Obwohl Postselection die Ergebnisqualität erheblich verbessern kann, indem Messergebnisse herausgefiltert werden, die durch nicht-Markovsches Rauschen beeinflusst wurden, ist sie allein keine vollständige Lösung für die Fehlerminderung. Postselection verringert den Einfluss bestimmter Fehler, indem ungültige Messergebnisse verworfen werden, aber dies geht auf Kosten eines erhöhten Sampling-Aufwands und adressiert nicht alle Fehlermechanismen in Quantenhardware der nahen Zukunft. Daher ist es wahrscheinlich unzureichend, sich bei komplexeren oder tieferen Circuits ausschließlich auf Postselection zu verlassen. Stattdessen ist Postselection am wirksamsten, wenn sie als Teil einer umfassenderen Fehlerminderungsstrategie eingesetzt wird — als Ergänzung zu Techniken wie Messfehlerminderung, rauschbewusste Circuit-Kompilierung oder probabilistische Fehlerauslöschung — um die Zuverlässigkeit von Quanten-Workloads zu verbessern und dabei Genauigkeit und Ressourcenaufwand in Balance zu halten.

Nächste Schritte

Empfehlungen