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.5.2
qiskit-ibm-runtime~=0.47.0
qiskit-addon-utils~=0.4.0

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 „Spectator“-Qubits misst, eine langsame Rotation auf jedes 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: dab7lmhl216s739p6q1g

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()
KeysView(QuantumProgramItemResult({'meas': array([[ True, True, True, True],
[ True, True, True, True],
[False, False, False, False],
...,
[ True, True, True, True],
[ True, True, True, True],
[False, False, False, False]], shape=(4000, 4)), 'spec': array([[False, False, False],
[False, False, False],
[False, False, False],
...,
[False, False, False],
[False, False, False],
[False, False, False]], shape=(4000, 3)), 'meas_ps': array([[False, False, False, False],
[False, False, False, False],
[ True, True, True, True],
...,
[False, False, False, False],
[False, False, False, False],
[False, True, True, True]], shape=(4000, 4)), 'spec_ps': array([[ True, True, True],
[ True, True, True],
[ True, True, True],
...,
[ True, True, True],
[ True, True, True],
[ True, True, True]], shape=(4000, 3))}, metadata=ItemMetadata()))

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 über die Nächste-Nachbarn-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) akzeptiert 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 False]
Percentage of the shots retained is after post selection 83.3

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.9385
Distance from ideal distribution before after-selection: 0.9315726290516206

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