Een QEC-protocol ontwikkelen en valideren

Gebruik qodec en de qdk.ec module uit de Microsoft Quantum Development-kit (QDK) om een QEC-code (Quantum Error Correction) te definiëren en om te zetten in een uitvoerbaar protocol. In dit artikel definieert u een foutcode, bouwt u een basislijn qodec, valideert u de declaraties, berekent u de gadgetafstand en slaat u het protocol op.

In de voorbeelden in dit artikel wordt de code $[[4,2,2]]$ C4 gebruikt, waarmee twee logische qubits in vier fysieke qubits worden opgeslagen en elke fysieke Pauli fout wordt gedetecteerd.

Prerequisites

Voer deze taken uit voordat u begint:

  • Installeer QDK voor foutcorrectie.
  • Gebruik een omgeving met Python 3.11 of hoger.
  • Controleer of de qodecStim parser beschikbaar is. Op Arm64Windows of LinuxArm64 installeert u Stim vanaf de bron en start u vervolgens Jupyter of de Python kernel opnieuw voordat u build_qodec aanroept.

Code definiëren

Maak een qodec.Code object met de codestabilisatoren en één logische X- en Z-vertegenwoordiger voor elke logische qubit.

import qodec

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

Het qodec pakket maakt gebruik van sparse-notatie voor Pauli tekenreeksen. Elke term heeft een Pauli letteraanduiding en een fysiek qubitlabel. U kunt bijvoorbeeld X_0 X_2 X toepassen op fysieke qubits 0 en 2.

De codedefinitie is een statisch wiskundig object. Het bevat geen circuits die gecodeerde statussen voorbereiden, behouden of meten.

De code analyseren

Als u een snapshot van de codeoperators wilt inspecteren, maakt u een CodeProfile.

from qdk import ec

profile = ec.CodeProfile(c4)

print("Physical support:", sorted(profile.support))
print("Physical qubits:", profile.length)
print("Logical qubits:", profile.logical_qubit_count)
print("Stabilizers:", profile.stabilizers)
print("Logical X operators:", profile.x)
print("Logical Z operators:", profile.z)

Het profiel wordt niet gewijzigd als u het oorspronkelijke qodec.Code object later wijzigt.

De codeafstand berekenen

Als u het minimale aantal toegestane fysieke fouten wilt vinden dat een niet-triviaal logisch effect veroorzaakt met het syndroom van nul, roep distance aan.

distance = profile.distance()
print("Code distance:", distance)

error = distance.witness.product
syndrome = profile.syndrome_of(error)
logical_effect = profile.logical_effect_of(error)

print("Witness:", error)
print("Syndrome:", sorted(syndrome))
print("Logical effect:", logical_effect)

assert distance.value == 2
assert profile.is_logical(error)

Standaard kent de zoekfunctie kostenwaarde 1 toe aan elke fout met één qubit X, Y of Z. De geretourneerde Distance heeft gecertificeerde grenzen en een bewijs voor elke eindige bovengrens.

Wanneer een volledige exacte zoekopdracht te veel kost, gebruikt u distance_bounds.

bounds = profile.distance_bounds(upper_bound=4)
print("Distance bounds:", bounds)
print("Lower bound:", bounds.lower_bound)
print("Upper bound:", bounds.upper_bound)

De standaard solver is HiGHS. Om een andere oplosser te gebruiken, stelt u solver="enumeration" in, of installeert u MWPF en stelt u solver="mwpf" in.

Een baseline bouwen qodec

Een qodec beschrijft hoe u gecodeerde instructies uitvoert. Het bevat instructiesets, codes, lagen, gadgets, circuits, controles en uitleesvergelijkingen.

Gebruik build_qodec om een protocol met twee lagen te maken op basis van de C4 code.

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

In dit voorbeeld wordt bare-css/v1 gebruikt om u een eenvoudig protocol zonder flag qubits te laten inspecteren.

Caution

De bare-css/v1 strategie belooft geen fouttolerante circuits. Bereken de afstand van elk gadget voordat u het protocol gebruikt voor lawaaierige experimenten.

De builder probeert deze logische instructies te maken:

  • prepare_z_all
  • prepare_x_all
  • syndrome
  • measure_z_all
  • measure_x_all
  • h_all
  • cx_all

Het _all achtervoegsel betekent dat een instructie op elke logische qubit in een blok handelt. Zo past CNOT bijvoorbeeld cx_all toe tussen de bijbehorende logische qubits in twee blokken.

Niet elke code ondersteunt elke kandidaat-instructie. Met strict=True, wat de standaardwaarde is, geeft de builder een fout wanneer deze geen kandidaat kan bouwen en verifiëren. Met strict=False, bewaart de builder de ondersteunde instructies en registreert weglatingen in protocol.metadata["qdk.ec"]["build"]["omitted"].

Inspecteer de logische laag en de bijbehorende gadgets.

logical_layer = protocol.layers[0]

print("Instructions:")
for mnemonic in logical_layer.instruction_set.instructions:
    print(" -", mnemonic)

print("Gadgets:")
for mnemonic in logical_layer.gadgets:
    print(" -", mnemonic)

De tweede laag bevat de fysieke Stim instructieset die door de gegenereerde circuits wordt gebruikt.

Gadget inspecteren

Een logische instructie definieert een contract. Het gadget geeft het circuit dat het contract implementeert.

Inspecteer het prepare_z_all gadget.

gadget = logical_layer.gadgets["prepare_z_all"]
gadget_profile = ec.GadgetProfile(gadget)

print("Declared objective:")
print(gadget_profile.objective)

print("Computed action:")
print(gadget_profile.action)

Vergelijk de actie die is berekend vanuit het circuit met het opgegeven instructiedoel.

objective = gadget_profile.objective
assert objective is not None

if gadget_profile.action.is_equivalent_to(objective):
    print("The gadget implements its declared instruction.")
else:
    print(objective.why_not_equivalent_to(gadget_profile.action))

A GadgetProfile maakt een momentopname van de invoer, dus deze geeft geen latere wijzigingen weer. Nadat u een gadget hebt bewerkt, maakt u een nieuw profiel.

Het protocol controleren

Gebruik audit om het hele qodec te controleren.

report = ec.audit(protocol)
print(report)

Als er tijdens de controle geen fouten of waarschuwingen worden gevonden, wordt het volgende afgedrukt:

audit: ok

De controle controleert deze gebieden:

  • Code algebra
  • Instructie- en laagstructuur
  • Circuitoproepen en parameterbindingen
  • Gadgetacties
  • Vergelijkingen voor controles en uitlezingen
  • Gemarkeerde vergelijkingen
  • Relaties tussen invoer- en uitvoerframes
  • Referentiegrenzen en protocolvolledigheid

Als u actie wilt ondernemen op specifieke ernstniveaus, gebruikt u de rapportcollecties.

if report.errors:
    for diagnostic in report.errors:
        print(diagnostic)
    raise ValueError("The qodec has audit errors.")

if report.warnings:
    for diagnostic in report.warnings:
        print(diagnostic)

De report.ok waarde betekent dat het rapport geen fouten bevat. Waarschuwingen wijzigen die eigenschap niet.

Ontbrekende vergelijkingen afleiden

Gebruik filled wanneer een conceptgadget of qodec controles weglaat of logische leesvergelijkingen die een exacte simulatie kan afleiden.

In het volgende voorbeeld wordt een onvolledige kopie van het protocol gemaakt.

draft = qodec.Qodec.loads(protocol.dumps())
draft_gadgets = draft.layers[0].gadgets

draft_gadgets["measure_x_all"].readouts = []
draft_gadgets["cx_all"].checks = []

print(ec.audit(draft))

Vul de ondersteunde vergelijkingen in en controleer het resultaat.

completed = ec.filled(draft)
print(ec.audit(completed))

De filled functie retourneert een nieuw gadget of qodec en wijzigt de invoer ervan niet. Het bepaalt tests, uitleesvergelijkingen en framerelaties. Er blijven flag bindings behouden, omdat ruisloze simulatie niet kan identificeren welke fouten een flag moet detecteren.

Als het oorspronkelijke protocol al geverifieerde directe vergelijkingen heeft, filled behoudt deze indien mogelijk.

Gadgetfouten analyseren

Een controle zonder fouten bewijst ruisloze consistentie. Het bewijst fouttolerantie niet.

Bel GadgetProfile.distance om het minimale aantal toegestane circuitfouten te vinden dat een logische fout veroorzaakt, terwijl alle gedeclareerde controles en vlaggen nul blijven. De gecombineerde fout moet er ook voor zorgen dat elke uitvoer binnen zijn coderuimte blijft.

protocol = completed

for mnemonic, gadget in sorted(protocol.layers[0].gadgets.items()):
    result = ec.GadgetProfile(gadget).distance()
    witness = result.witness if result.upper_bound is not None else "none"
    print(f"{mnemonic}: distance={result}, witness={witness}")

Het standaardfoutmodel bevat deze gebeurtenissen na elke circuitaanroep:

  • Een Pauli fout op de qubits die de aanroep beïnvloedt
  • Een flip van een uitleesbit die door de aanroep wordt geproduceerd
  • Een combinatie van samenhangende die fouten

Elke gebeurtenis bij één aanroep kost één, zelfs wanneer deze een gecorreleerde multiqubit-fout heeft.

Een code kan een afstand van twee hebben terwijl een van de gadgets een afstand van één heeft. Eén circuitfout kan bijvoorbeeld worden verspreid via een ancilla en een niet-gedetecteerde logische fout veroorzaken. Voeg checkpunten of vlagcircuits toe en maak vervolgens een nieuwe GadgetProfile en bereken de afstand opnieuw.

Een specifieke fout inspecteren

Gebruik FaultEvent.after om een fout te definiëren na een op nul gebaseerde circuitaanroepindex.

gadget = protocol.layers[0].gadgets["syndrome"]
profile = ec.GadgetProfile(gadget)

fault = ec.FaultEvent.after(0, ec.Pauli("X_0"))
(effect,) = profile.effects_of([fault])

print("Fault:", fault)
print("Changed quantities:", effect)

Een FaultEffect kan gewijzigde controlesignalen, logische uitlezingen en tekens van het uitvoerframe bevatten. Gebruik expliciete foutsets wanneer het standaardmodel op circuitniveau niet overeenkomt met uw hardwareveronderstellingen.

result = profile.distance(faults=[fault])
print(result)

Exacte gadgetzoekopdrachten kunnen lang duren, omdat de standaardfoutset exponentieel groeit bij elke aanroep. Gebruik distance_bounds, een expliciete foutset of een upper_bound wanneer u de zoekopdracht wilt beperken.

Het protocol opslaan

Controleer de definitieve qodec voordat u deze opslaat.

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

Sla het protocol op als één YAML bundel en laad het opnieuw om te controleren of het overeenkomt met het origineel.

from pathlib import Path

output_directory = Path("qodec-output")
output_directory.mkdir(exist_ok=True)

saved_path = protocol.save(str(output_directory), single_file=True)
loaded = qodec.Qodec.load(saved_path)

assert loaded == protocol
print("Saved qodec:", saved_path)

In een bundel met één bestand worden het manifest, codedefinities, instructiesets, gadgets en circuitbronnen opgeslagen in één YAML bestand, waardoor het eenvoudig is om te delen en opnieuw te laden.

Voer het protocol uit

Nadat de qodec de audit heeft doorstaan en voldoet aan uw afstandsvereisten, compileert u een logisch programma naar Quantum Intermediate Representation (QIR) en voert u het uit via het protocol. Zie Logische programma's uitvoeren en interpreteren.

Zie de stapsgewijze handleiding voor het QEC-protocol in de QDK GitHub-repository voor een volledig voorbeeld waarmee flagcircuits worden toegevoegd en logische foutpercentages worden vergeleken.