Uruchamianie i debugowanie programów logicznych

Użyj Microsoft Quantum Development Kit (QDK), aby skompilować program logiczny Q# lub OpenQASM do Quantum Intermediate Representation (QIR), a następnie użyj polecenia qdk.simulation.run_qir do uruchomienia programu przez qodec. Skonfiguruj model szumu, a następnie wybierz wbudowany dekoder syndromu lub opcjonalny deq dekoder na poziomie obwodu.

Prerequisites

Przed rozpoczęciem wykonaj następujące zadania:

W Q# przykładach użyto protocol o nazwie qodec, zawierającego instrukcje dotyczące kodu C4. Aby go utworzyć, zobacz Tworzenie i weryfikowanie protokołu QEC.

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

Przeprowadź audyt qodec przed jego uruchomieniem. Środowisko uruchomieniowe ufa qodec sprawdzaniom, odczytom, flagom i równaniom ramek, więc nieprawidłowe deklaracje mogą powodować nieprawidłowe wyniki logiczne lub błędy dekodera.

Ten przepływ pracy używa qdk.simulation.run_qir wraz z qodec. Narzędzie qodec używa oddzielnego StimPython pakietu do analizowania obwodów gadżetów w formacie Stim. Nie wywołuje qdk.stim.run. W systemie LinuxArm64 lub Arm64Windows zainstaluj program Stim ze źródła i uruchom ponownie Python lub jądro Jupyter przed uruchomieniem qodec, który zawiera obwody w formacie Stim.

Dopasuj QIR wywołania do qodec instrukcji

Dopasuj każde wywołanie kwantowe w programie QIR do instrukcji w górnym zestawie instrukcji elementu qodec. Mnemonik instrukcji musi dokładnie odpowiadać nazwie wywoływanej funkcji QIR.

W poniższej tabeli przedstawiono przykładowe mapowania:

Wywołanie źródła Wymagany qodec mnemonik
Q# X(q) lub OpenQASMx q; __quantum__qis__x__body
Q# H(q) lub OpenQASMh q; __quantum__qis__h__body
Q# CNOT(a, b) lub OpenQASMcx a, b; __quantum__qis__cx__body
Q# Adjoint S(q) lub OpenQASMsdg q; __quantum__qis__s__adj
Q# M(q) lub OpenQASMc = measure q; __quantum__qis__m__body
Q# operacja intrinsiczna Foo Foo
OpenQASM bramka intrinsic lub def o nazwie foo foo

QIR argumenty są przypisywane do qodec elementów instrukcji w następującej kolejności:

  1. Argumenty wskaźnikowe qubitu wiążą się z operandami blokowymi.
  2. Argumenty skalarne wiążą się z parametrami instrukcji.
  3. Argumenty będące wskaźnikami wyniku otrzymują wyniki instrukcji i wszystkie zwrócone flagi.

Kompilator zgłasza błąd, jeśli wywołanie kwantowe nie pasuje do instrukcji w nadrzędnym zestawie instrukcji.

Deklarowanie instrukcji logicznych w Q#

Utworzony qodec na podstawie C4 kodu udostępnia niestandardowe instrukcje, takie jak prepare_z_all, measure_z_all, syndromei cx_all. Zadeklaruj Q# operacje intrinsic o tych samych nazwach.

Zainicjuj Q# za pomocą adaptacyjnego profilu docelowego, ponieważ wymagają tego operacje pomiaru niestandardowego.

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

Zadeklaruj C4 instrukcje jako Q# operacje.

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

W tym programie każde Q#Qubit oznacza jeden zakodowany blok. Blok C4 ma dwa kubity logiczne, więc każda operacja pomiaru zwraca dwa wyniki logiczne. Każda operacja przygotowywania zwraca jedną flagę odrzucenia.

Skompiluj eksperyment logiczny Q#

Zdefiniuj eksperyment przygotowywania i pomiaru stanu (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])
}
""")

Skompiluj operację TestSpamZ do QIR.

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

Funkcja QIR zachowuje wywołania do prepare_z_all i measure_z_all. Po uruchomieniu programu środowisko uruchomieniowe qodec porównuje te nazwy z najwyższym zestawem instrukcji i zastępuje każde wywołanie logiczne odpowiadającym mu gadżetem.

Uruchom za pomocą wbudowanego dekodera

Wywołaj run_qir i przekaż qodec. Jeśli decoder jest to None, środowisko uruchomieniowe używa wbudowanego dekodera minimum weight Pauli.

from qdk.simulation import run_qir

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

print(results[:5])

Aby uruchomić qodec, użyj następujących opcji symulatora:

Wartość: type Behavior
None lub "stabilizer" Użyj backendu stabilizatora
"cpu" Użyj backendu wektora stanu dla obsługiwanych programów
"gpu" Nieobsługiwane przez qodec

Środowisko uruchomieniowe przygotowuje każdy kubit programu przed pierwszym użyciem, chyba że pierwsza instrukcja tworzy jego blok. Instrukcja przygotowania zawiera bloki wyjściowe, brak bloków wejściowych i akcję, która stabilizuje logiczne Z.

Poprawki dekodera z jednym qubitem Pauli i qodec aktualizacje ramek używają bezszumowej Pauli ramki, a nie hałaśliwych bram fizycznych.

Konfiguracja szumu symulatora

Zdefiniuj szum dla operacji symulatora, a nie qodec dla nazw instrukcji. Środowisko uruchomieniowe tłumaczy każdą instrukcję warstwy dolnej na co najmniej jedną operację symulatora, dlatego ustawienia szumu mają zastosowanie do tych operacji.

W C4 instrukcji krok po kroku są używane następujące mapowania:

Instrukcja dla dolnej warstwy Obsługa symulatora NoiseConfig Pole
R Przygotuj lub zresetuj do Z+ mresetz
H Hadamard h
CX Sterowane X cx
M Pomiar w bazie Z mresetz

Utwórz prosty model szumu 1%:

from qdk.simulation import NoiseConfig

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

Uruchom eksperyment z szumem:

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

Szum na mresetz oddziałuje na kubit po pomiarze lub przygotowaniu. Nie odwraca bitu pomiaru, który symulator już zarejestrował. Aby zbadać zarejestrowane przekłamania bitów, użyj qodec modeli błędów odczytu i analizy gadżetów.

Niepowodzenia strzałów kontrolnych

Użyj polecenia on_shot_failure, aby wybrać sposób reagowania środowiska uruchomieniowego, gdy flaga qodec, dekoder lub odczyt nie generuje zaakceptowanego wyniku.

Value Behavior
"discard" Pomiń nieudane zdjęcia ze zwróconej listy. Jest to wartość domyślna.
"raise" Zatrzymaj się, gdy pierwsza próba się nie powiedzie.
"retry" Uruchom ponownie nieudany strzał, do max_retries dodatkowych prób.

W przypadku zasad "discard" argument shots zlicza podjęte próby, więc zwrócona lista może zawierać mniej elementów niż shots.

Flaga wyzwala politykę shot tylko wtedy, gdy program logiczny nie otrzymuje tej flagi. Q# @Measurement() Jeśli operacja zwraca dodatkową Result oznaczającą flagę, program otrzymuje flagę i decyduje, czy odrzucić strzał.

W tym przykładzie TestSpamZ program zwraca flagę przygotowania w pierwszej tablicy. on_shot_failure="discard" Zatem ustawienie nie usuwa wierszy, w których zwracana flaga to One.

Wybierz dekoder

Aby wybrać sposób dekodowania błędów, użyj parametru decoder, aby przekazać obiekt wywoływalny przygotowujący dekoder dla każdej zakodowanej warstwy.

Element QDK ma następujące dekodery:

Dekoder Wykorzystaj ją do
Domyślny dekoder syndromu Wnioskowanie poprawek minimalnej wagi Pauli na podstawie syndromu. Użyj decoder=None.
Dekoder ramki Śledź ramki bez zakłóceń bez wnioskowania o usterkach fizycznych. Użyj prepare_frame_decoder.
deq dekoder Dekoduj modele błędów na poziomie obwodu za pomocą deq. Użyj prepare_deq_decoder.

Użyj konfiguracji domyślnej deq

Zainstaluj deq i deq-runtime, a następnie przekaż 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",
)

Domyślnie adapter deq używa relay belief propagation i window coordinator. Aby zmienić te ustawienia, użyj runtime factory.

Skonfiguruj monolityczne środowisko uruchomieniowe deq

Aby bezpośrednio wybrać ustawienia deq, przekaż fabrykę środowiska uruchomieniowego do 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",
)

Zwróć nowe Runtime z każdego wywołania funkcji fabrykującej. QDK uruchamia i zamyka każde środowisko uruchomieniowe.

Uwaga / Notatka

Adapter deq wymaga:

  • Jedna zakodowana warstwa
  • Ślad wykonywania niezależny od pomiaru Clifford
  • Backend stabilizatora
  • Model szumu bez utraty kubitów

Adapter deq nie obsługuje zasad ponawiania. Nieobsługiwane programy zgłaszają błąd zamiast przełączać się na inny dekoder.

Odrzuć deq wyniki o niskim poziomie pewności

Adapter deq zwraca wynik dla każdego odczytu logicznego. Niższe wyniki wskazują silniejsze preferencje dla wybranej poprawki. Wyniki nie są skalibrowanymi prawdopodobieństwami awarii.

Aby odrzucić wyniki o niskiej pewności, przekaż max_readout_score podczas przygotowywania dekodera, jak w poniższym przykładzie:

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

Środowisko uruchomieniowe akceptuje próbkę tylko wtedy, gdy każdy zwrócony odczyt logiczny ma wynik na poziomie lub poniżej progu. Próg nie ma zastosowania do nieprzetworzonych flag ani nieużywanych pośrednich odczytów.

Odrzucanie oparte na wynikach może obniżyć wskaźnik błędów logicznych zaakceptowanych prób. Ale zmniejsza również liczbę akceptowanych ujęć i dodaje pracę dekodera.

Kompiluj OpenQASM programy

Operacje standardowe OpenQASM używają tych samych QIR reguł mnemonicznych co standardowe Q# operacje.

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)

Aby uruchomić ten program przez qodec, upewnij się, że najwyższy zestaw instrukcji elementu qodec ma __quantum__qis__x__body i __quantum__qis__m__body, ze zgodnymi operandami blokowymi i jednym wynikiem pomiaru.

Wbudowana brama OpenQASM lub podprogram o nazwie foo używa mnemoniku foo. Argumenty kubitu wiążą się z operandami blokowymi. Argumenty float, int i bool wiążą się z parametrami instrukcji w kolejności deklaracji.

OpenQASM ma następujące ograniczenia dotyczące qodec wywołań:

  • Niestandardowe intrinsics nie mogą zwracać QIRResult. Aby uzyskać jeden wynik pomiaru, użyj standardowej measure instrukcji.
  • OpenQASM programy nie mogą uruchamiać pomiarów niestandardowych, odbierać kilka wyników z jednego wywołania lub odbierać zwrócone flagi. W takich przypadkach należy używać operacji Q#@Measurement().
  • Środowisko uruchomieniowe kontroluje wszystkie flagi, których program OpenQASM nie może odebrać, a te flagi mogą odrzucić próbę.

Aby zapoznać się ze szczegółowymi przykładami mapowania, zobacz następujące QDK notesy:

Przegląd wymagań i ograniczeń

Przed uruchomieniem dużego eksperymentu zapoznaj się z tymi wymaganiami, ograniczeniami i różnicami w wynikach.

Wymagania:

  • Każde QIR wywołanie kwantowe musi odpowiadać instrukcji najwyższej warstwy qodec.
  • Przeprowadź audyt protokołu przed jego uruchomieniem. Środowisko qodec uruchomieniowe ufa równaniom, które piszesz i nie sprawdza ich.

Ograniczenia:

  • GPU wykonanie symulowane nie obsługuje qodec wykonania.
  • Domyślny zestaw błędów gadgetu może rosnąć wykładniczo, więc dokładne wyszukiwanie według odległości może trwać długo.
  • Pakiety deq i Stim mają ograniczenia platformowe. Aby uzyskać więcej informacji, zobacz Instalowanie QDK na potrzeby korekcji błędów.

Różnice wyników:

  • Dokładne liczby danych wyjściowych mogą się różnić w zależności od dekodera.
  • Zasady odrzucania i ponawiania prób mogą zmieniać zwracany rozkład wyników.

Aby zapoznać się z kompletnym Q# eksperymentem z zamiataniami szumów, deq dekodowaniem i odrzuceniem opartym na ufności, zobacz przewodnik po protokole QEC.