Logische Programme ausführen und dekodieren

Verwenden Sie das Microsoft Quantum Development Kit (QDK) zum Kompilieren eines logischen Q#- oder OpenQASM-Programms in Quantum Intermediate Representation (QIR), und verwenden Sie dann qdk.simulation.run_qir, um das Programm über qodec auszuführen. Richten Sie ein Rauschmodell ein, und wählen Sie dann den integrierten Syndromdecoder oder den optionalen deq Decoder auf Schaltkreisebene aus.

Prerequisites

Führen Sie diese Aufgaben aus, bevor Sie beginnen:

In den Q# Beispielen wird ein qodec namens protocol verwendet, der Anweisungen für den C4 Code enthält. Um ein solches zu erstellen, lesen Sie bitte Entwicklung und Validierung eines QEC-Protokolls.

import qodec
from qdk import ec

c4 = qodec.Code(
    "C4",
    stabilizers=["X_0 X_1 X_2 X_3", "Z_0 Z_1 Z_2 Z_3"],
    x=["X_0 X_1", "X_0 X_2"],
    z=["Z_0 Z_2", "Z_0 Z_1"],
)

protocol = ec.build_qodec(
    c4,
    strategy="bare-css/v1",
    strict=False,
)

report = ec.audit(protocol)
if report.errors or report.warnings:
    raise ValueError(str(report))

Prüfen Sie qodec, bevor Sie es ausführen. Die Laufzeit vertraut den Prüfungen, Ausgaben, Flags und Framegleichungen von qodec, sodass ungültige Deklarationen zu falschen logischen Ergebnissen oder Decoderfehlern führen können.

Dieser Workflow verwendet qdk.simulation.run_qir mit einer qodec. Die qodec Tools verwenden das separate StimPython Paket zum Analysieren von Gadgetschaltungen im Stim-Format. Es ruft qdk.stim.run nicht auf. Unter WindowsArm64 oder PythonJupyter installieren Sie qodec aus der Quelle und starten Stim oder den Stim-Kernel neu, bevor Sie ein Linux ausführen, das Schaltkreise im Arm64-Format enthält.

Abgleichen von QIR Aufrufen mit qodec Instruktionen

Ordnen Sie jeden Quantumaufruf im QIR-Programm einer Anweisung im obersten Befehlssatz von qodec zu. Die Anweisungs-Mnemonik muss exakt mit dem QIR-Namen des aufgerufenen Ziels übereinstimmen.

Die folgende Tabelle zeigt Beispielzuordnungen:

Quellaufruf Erforderlich: qodec-Mnemonik
Q# X(q) oder OpenQASMx q; __quantum__qis__x__body
Q# H(q) oder OpenQASMh q; __quantum__qis__h__body
Q# CNOT(a, b) oder OpenQASMcx a, b; __quantum__qis__cx__body
Q# Adjoint S(q) oder OpenQASMsdg q; __quantum__qis__s__adj
Q# M(q) oder OpenQASMc = measure q; __quantum__qis__m__body
Q# intrinsische Operation Foo Foo
OpenQASM intrinsisches Gate oder def namens foo foo

QIR Argumente binden in dieser Reihenfolge an qodec Anweisungselemente:

  1. Qubit-Zeigerargumente werden an Blockoperanden gebunden.
  2. Skalare Argumente werden an Anweisungsparameter gebunden.
  3. Zeigerargumente für Ergebnisse erhalten Anweisungsergebnisse und alle zurückgegebenen Flags.

Der Compiler löst einen Fehler aus, wenn ein Quantumaufruf nicht mit einer Anweisung in der obersten Befehlsmenge übereinstimmt.

Logische Anweisungen deklarieren in Q#

Ein aus dem C4-Code erstelltes qodec stellt benutzerdefinierte Anweisungen wie prepare_z_all, measure_z_all, syndrome und cx_all bereit. Deklarieren Sie Q# intrinsische Operationen mit denselben Namen.

Initialisieren Sie Q# mit dem adaptiven Zielprofil, da benutzerdefinierte Messvorgänge dies erfordern.

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

Deklarieren Sie die C4 Anweisungen als Q# Operationen.

qsharp.eval("""
@Measurement()
operation prepare_z_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation prepare_x_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation measure_z_all(block : Qubit) : (Result, Result) { body intrinsic; }

@Measurement()
operation measure_x_all(block : Qubit) : (Result, Result) { body intrinsic; }

operation cx_all(control : Qubit, target : Qubit) : Unit { body intrinsic; }
operation syndrome(block : Qubit) : Unit { body intrinsic; }
""")

In diesem Programm stellt jeder Q#Qubit einen codierten Block dar. Der C4 Block verfügt über zwei logische Qubits, sodass jeder Messvorgang zwei logische Ergebnisse zurückgibt. Jeder Vorbereitungsvorgang gibt ein Ablehnungs-Flag zurück.

Kompilieren eines logischen Q# Experiments

Definieren Sie ein Zustandsvorbereitungs- und Messexperiment (SPAM).

qsharp.eval("""
operation TestSpamZ() : (Result[], Result[]) {
    use block = Qubit();
    let reject = prepare_z_all(block);
    let (left, right) = measure_z_all(block);
    ([reject], [left, right])
}
""")

Kompilieren Sie den TestSpamZ-Vorgang in QIR.

qir = str(qsharp.compile("TestSpamZ()"))

QIR behält die Anrufe an prepare_z_all und measure_z_all. Wenn das Programm ausgeführt wird, gleicht die qodec-Laufzeit diese Namen mit dem obersten Befehlssatz ab und ersetzt jeden logischen Aufruf durch sein Gadget.

Ausführen mit dem integrierten Decoder

Rufen Sie run_qir an und übergeben Sie die qodec. Wenn decoder gleich None ist, verwendet die Laufzeit den integrierten Minimalgewichtsdecoder Pauli.

from qdk.simulation import run_qir

results = run_qir(
    qir,
    qodec=protocol,
    type="stabilizer",
    shots=100,
    seed=42,
)

print(results[:5])

Verwenden Sie zum Ausführen eines qodecSimulators die folgenden Simulatoroptionen:

type Wert Behavior
None oder "stabilizer" Verwenden Sie das Stabilisierungs-Backend
"cpu" Verwenden Sie das Zustandsvektor-Backend für unterstützte Programme
"gpu" Nicht unterstützt mit einem qodec

Die Laufzeit bereitet jedes Programm qubit vor der ersten Verwendung vor, es sei denn, die erste Anweisung erstellt seinen Block. Eine Vorbereitungsanweisung verfügt über Ausgabeblöcke, keine Eingabeblöcke und eine Aktion, die logische Z-Zustände stabilisiert.

Single-Qubit-Decoderkorrekturen Pauli und qodec Frameaktualisierungen verwenden einen rauschlosen Pauli Frame, nicht laute physische Gatter.

Simulatorgeräusche konfigurieren

Legen Sie Rauschen für Simulatorvorgänge fest, nicht für qodec-Anweisungsnamen. Die Laufzeit übersetzt jede Anweisung der unteren Ebene in einen oder mehrere Simulatorvorgänge, sodass Rauscheinstellungen für diese Vorgänge gelten.

Die C4 Schritt-für-Schritt-Anleitung verwendet die folgenden Zuordnungen:

Anweisung der unteren Schicht Simulatorbetrieb NoiseConfig-Feld
R Vorbereiten oder Zurücksetzen auf Z+ mresetz
H Hadamard h
CX Kontrolliertes X cx
M Z-Basis-Messung mresetz

Erstellen Sie ein einfaches 1% Rauschmodell:

from qdk.simulation import NoiseConfig

noise = NoiseConfig()
for gate in (noise.h, noise.cx):
    gate.set_depolarizing(0.01)
noise.mresetz.x = 0.01

Führen Sie das Experiment mit Rauschen aus:

results = run_qir(
    qir,
    qodec=protocol,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

Rauschen auf mresetz wirkt auf das Qubit nach Messung oder Vorbereitung. Es ändert kein Messbit, das der Simulator bereits aufgezeichnet hat. Verwenden Sie qodec Readout-Fehlermodelle und Gadgetanalysen, um aufgezeichnete Bit-Flips zu untersuchen.

Schussfehler kontrollieren

Verwenden Sie on_shot_failure, um festzulegen, wie die Laufzeitumgebung reagieren soll, wenn ein qodec-Flag, ein Decoder oder eine Auslesung keine akzeptierte Aufnahme liefert.

Value Behavior
"discard" Fehlgeschlagene Aufnahmen aus der zurückgegebenen Liste weglassen. Dies ist der Standardwert.
"raise" Beenden Sie, wenn der erste Versuch fehlschlägt.
"retry" Starten Sie die fehlgeschlagene Aufnahme mit bis zu max_retries zusätzlichen Versuchen neu.

Mit der "discard"-Richtlinie zählt das shots-Argument die Anzahl der Versuche, sodass die zurückgegebene Liste weniger Elemente enthalten kann als shots.

Ein Flag löst die Shot-Richtlinie nur aus, wenn das logische Programm diese Kennzeichnung nicht empfängt. Wenn eine Q#@Measurement()-Operation ein zusätzliches Result für das Flag zurückgibt, erhält das Programm das Flag und entscheidet, ob der Schuss abgelehnt werden soll.

Im TestSpamZ Beispiel gibt das Programm die Vorbereitungskennzeichnung im ersten Array zurück. Daher entfernt die on_shot_failure="discard" Einstellung keine Zeilen, bei denen das zurückgegebene Flag One ist.

Decoder auswählen

Um auszuwählen, wie Fehler decodiert werden, verwenden Sie den decoder Parameter, um für jede codierte Ebene eine aufrufbare Funktion zur Decodervorbereitung zu übergeben.

Das QDK hat die folgenden Decoder:

Decoder Verwenden Sie es, um
Decoder für Standardvorgabesyndrome Ableiten von Pauli-Minimalgewichtskorrekturen aus Syndrom-Evidenz. Verwenden Sie decoder=None.
Frame-Decoder Verfolgen Sie rauschfreie Bilder, ohne auf physische Fehler zu schließen. Verwenden Sie prepare_frame_decoder.
deq Decoder DecodierenSie Fehlermodelle auf Schaltkreisebene mit deq. Verwenden Sie prepare_deq_decoder.

Verwenden Sie die deq-Standardkonfiguration

Installieren Sie deq und deq-runtime, dann übergeben Sie prepare_deq_decoder.

from qdk.simulation.decoders import prepare_deq_decoder

results = run_qir(
    qir,
    qodec=protocol,
    decoder=prepare_deq_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

Standardmäßig verwendet der Adapter deq Relay-Belief-Propagation und einen Window Coordinator. Verwenden Sie eine Laufzeitfactory, um diese Einstellungen zu ändern.

Monolithische deq-Laufzeit konfigurieren

Um deq Einstellungen direkt auszuwählen, übergeben Sie eine Laufzeitfactory an prepare_deq_decoder.

from functools import partial
from deq.runtime import Runtime
from qdk.simulation import decoders


def make_runtime(seed: int) -> Runtime:
    return Runtime(
        decoder="black-box-relay-bp",
        decoder_config={"parallel": 1, "seed": seed},
        coordinator="monolithic",
        controller="jit",
    )


results = run_qir(
    qir,
    qodec=protocol,
    decoder=partial(
        decoders.prepare_deq_decoder,
        runtime_factory=make_runtime,
    ),
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

Geben Sie bei jedem Factory-Aufruf ein neues Runtime zurück. QDK wird bei jeder Laufzeit gestartet und geschlossen.

Note

Der deq Adapter erfordert Folgendes:

  • Eine codierte Ebene
  • Eine messunabhängige Clifford Ausführungsspur
  • Das Stabilisierungs-Backend
  • Ein Rauschmodell ohne Qubitverlust

Der deq Adapter unterstützt die Wiederholungsrichtlinie nicht. Nicht unterstützte Programme lösen einen Fehler aus, anstatt auf einen anderen Decoder zurückzufallen.

Ergebnisse mit niedriger Konfidenz deq ablehnen

Der deq Adapter gibt eine Bewertung für jeden logischen Ausgabewert zurück. Niedrigere Bewertungen deuten auf eine stärkere Präferenz für die ausgewählte Korrektur hin. Die Bewertungen sind keine kalibrierten Fehlerwahrscheinlichkeiten.

Wenn Sie Ergebnisse mit niedriger Konfidenz ablehnen möchten, übergeben Sie max_readout_score beim Vorbereiten des Decoders, wie im folgenden Beispiel:

from functools import partial
from qdk.simulation import decoders

scored_decoder = partial(
    decoders.prepare_deq_decoder,
    max_readout_score=0.2,
)

results = run_qir(
    qir,
    qodec=protocol,
    decoder=scored_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    on_shot_failure="discard",
)

Die Laufzeit akzeptiert nur dann einen Shot, wenn jeder zurückgegebene logische Readout einen Wert bei oder unter dem Schwellenwert aufweist. Der Schwellenwert gilt nicht für Roh-Flags oder ungenutzte Zwischenausgaben.

Die scorebasierte Ablehnung kann die logische Fehlerrate der akzeptierten Versuche verringern. Es reduziert aber auch die Anzahl der akzeptierten Aufnahmen und fügt Decoderarbeit hinzu.

Kompilieren von OpenQASM Programmen

Standardvorgänge OpenQASM verwenden dieselben QIR Mnemonic-Regeln wie Standardvorgänge Q# .

from qdk import TargetProfile, openqasm

source = """
OPENQASM 3.0;
include "stdgates.inc";
qubit q;
bit c;
x q;
c = measure q;
"""

qir = openqasm.compile(
    source,
    target_profile=TargetProfile.Adaptive_RI,
)

print(qir)

Für die Ausführung dieses Programms über qodec stellen Sie sicher, dass der oberste Befehlssatz von qodec die Elemente __quantum__qis__x__body und __quantum__qis__m__body enthält, mit kompatiblen Blockoperanden und einem Messergebnis.

Ein OpenQASM-internes Gate oder eine Subroutine mit dem Namen foo verwendet die Mnemonik foo. Qubit-Argumente werden an Blockoperanden gebunden. Die Argumente int, bool und float werden den Anweisungsparametern in der Deklarationsreihenfolge zugeordnet.

OpenQASM hat die folgenden Einschränkungen für qodec Anrufe:

  • Benutzerdefinierte Intrinsics können keine QIRResult zurückgeben. Verwenden Sie zum Abrufen eines Messergebnisses die Standard-Anweisung measure .
  • OpenQASM Programme können keine benutzerdefinierten Messungen ausführen, mehrere Ergebnisse aus einem Aufruf empfangen oder zurückgegebene Flags empfangen. Verwenden Sie für diese Fälle Q#@Measurement()-Vorgänge.
  • Die Laufzeit steuert alle Flags, die das OpenQASM-Programm nicht empfangen kann, und diese Flags können eine Aufnahme ablehnen.

Ausführliche Zuordnungsbeispiele finden Sie in den folgenden QDK Notizbüchern:

Überprüfen von Anforderungen und Einschränkungen

Überprüfen Sie diese Anforderungen, Einschränkungen und Ergebnisunterschiede, bevor Sie ein großes Experiment ausführen.

Anforderungen:

  • Jeder QIR Quantenaufruf muss zu einer Anweisung der obersten Ebene qodec aufgelöst werden.
  • Prüfen Sie das Protokoll, bevor Sie es ausführen. Die qodec Laufzeit vertraut den Formeln, die Sie schreiben, und überprüft sie nicht.

Limitations:

  • GPU Die qodec Ausführung wird von der Simulation nicht unterstützt.
  • Die standardmäßige Gadget-Fehlermenge kann exponentiell wachsen, sodass genaue Distanzsuchen lange dauern können.
  • Die Pakete deq und Stim weisen Plattformbeschränkungen auf. Weitere Informationen finden Sie unter Installation von QDK zur Fehlerkorrektur.

Ergebnisunterschiede:

  • Genaue Ausgabeanzahlen können je nach Decoder variieren.
  • Verwerfungs- und Wiederholungsrichtlinien können die zurückgegebene Ergebnisverteilung ändern.

Für ein vollständiges Q# Experiment mit Rausch-Sweeps, deq Decodierung und konfidenzbasierter Ablehnung siehe die exemplarische Vorgehensweise des QEC-Protokolls.