File size: 5,585 Bytes
2a87ede
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Stabilizer-code quantum error correction utilities: generic Pauli-string

commutation/syndrome primitives, and an erasure-aware decoder.



Promoted from Dense-Evolution-Discovery's Steane [[7,1,3]] code

investigation (scripts/steane_code_block6_erasure_conversion.py), where a

Steane-specific version of `erasure_aware_decode` was first built and

verified: on shots with exactly 2 simultaneous heralded erasures, it

achieved exactly 0 decoding failures across 94-12,469 such shots at every

tested physical error rate (0 failures out of >60,000 double-erasure

shots total, 40,000 trials x 10 p-values), versus ~25% failure for a

standard syndrome-only decoder blind to the erasure locations -- a clean

confirmation of the real erasure-correction bound below. This version is

code-agnostic (works from any stabilizer generator list, not a

hand-built Steane-specific table), so it moved here instead of staying

Discovery-repo-specific research code.



Erasure-aware decoding exploits a real, foundational fact: Grassl, Beth &

Pellizzari, "Codes for the quantum erasure channel", Phys. Rev. A 56, 33

(1997) -- a distance-d stabilizer code can correct up to (d-1) ERASURES

(known-location errors, e.g. a heralded lost photon in a dual-rail

photonic qubit), versus only floor((d-1)/2) arbitrary (unlocated)

errors. Erasure location information is worth roughly twice as much as

an ordinary syndrome bit, because knowing WHERE the error is removes

exactly the ambiguity a blind syndrome-only decoder has to guess at.

"""
import itertools
from typing import Optional, Sequence

_ANTICOMMUTING_PAIRS = {
    frozenset(('X', 'Z')), frozenset(('X', 'Y')), frozenset(('Y', 'Z')),
}
PAULIS = ('I', 'X', 'Y', 'Z')


def pauli_commutes(p1: str, p2: str) -> bool:
    """Whether two equal-length Pauli strings (each character in IXYZ, no

    global phase) commute -- the standard symplectic rule: they commute

    iff the number of qubit positions where the local single-qubit

    Paulis anticommute (X/Z, X/Y, or Y/Z, in either order; I commutes

    with everything) is EVEN.



    >>> pauli_commutes('XX', 'ZZ')   # X,Z anticommute at both qubits -> 2 (even) -> commute

    True

    >>> pauli_commutes('XI', 'ZI')   # X,Z anticommute at 1 qubit -> 1 (odd) -> anticommute

    False

    """
    if len(p1) != len(p2):
        raise ValueError(f"Pauli strings must be equal length: {len(p1)} != {len(p2)}")
    n_anticommuting = sum(
        1 for a, b in zip(p1, p2)
        if a != 'I' and b != 'I' and a != b and frozenset((a, b)) in _ANTICOMMUTING_PAIRS
    )
    return n_anticommuting % 2 == 0


def compute_syndrome(pauli_error: str, stabilizers: Sequence[str]) -> tuple:
    """The syndrome (one bit per stabilizer generator, 1 = anticommutes /

    detected, 0 = commutes / undetected) a given Pauli error string would

    produce against `stabilizers` (a list of equal-length Pauli strings,

    the code's stabilizer generators -- X-type, Z-type, or mixed; this

    function doesn't assume a CSS structure)."""
    return tuple(0 if pauli_commutes(pauli_error, g) else 1 for g in stabilizers)


def _pauli_string(n_qubits: int, assignment: dict) -> str:
    chars = ['I'] * n_qubits
    for q, p in assignment.items():
        chars[q] = p
    return ''.join(chars)


def erasure_aware_decode(

    observed_syndrome: tuple,

    heralded_qubits: Sequence[int],

    n_qubits: int,

    stabilizers: Sequence[str],

) -> Optional[str]:
    """Erasure-aware decoder for any stabilizer code. Given the observed

    syndrome and a list of qubits KNOWN to have been erased (e.g. a

    heralded photon-loss event on a dual-rail-encoded qubit), brute-forces

    every Pauli assignment (I/X/Y/Z, 4**len(heralded_qubits) combinations)

    on just the heralded qubits and returns the unique full-length Pauli

    string reproducing `observed_syndrome` exactly.



    Returns `None` -- not a guess -- when there are zero heralded qubits,

    when the observed syndrome is not explained by any assignment on the

    heralded qubits alone, or when more than one assignment explains it

    (ambiguous). Both `None` cases mean: fall back to a standard

    syndrome-only decoder, or treat as a detected-but-uncorrectable

    event -- this function will not silently return a wrong-but-plausible

    correction. The number of heralded qubits this can actually resolve

    unambiguously is bounded by the code's real distance (Grassl, Beth &

    Pellizzari 1997: up to d-1 erasures) -- that bound emerges naturally

    from the brute-force search itself (more heralded qubits than the

    code can resolve typically yields zero or multiple matches), it is

    not hard-coded here.



    Cost is 4**len(heralded_qubits) syndrome computations, each O(n_qubits

    * len(stabilizers)) -- fine for the small numbers of simultaneous

    erasures a real per-shot noise rate produces (verified up to 2 in the

    original Steane investigation; tractable up to 4-5 for most small

    codes before it's worth switching to a smarter search).

    """
    if not heralded_qubits:
        return None

    matches = []
    for assignment_paulis in itertools.product(PAULIS, repeat=len(heralded_qubits)):
        assignment = dict(zip(heralded_qubits, assignment_paulis))
        candidate = _pauli_string(n_qubits, assignment)
        if compute_syndrome(candidate, stabilizers) == tuple(observed_syndrome):
            matches.append(candidate)

    if len(matches) != 1:
        return None
    return matches[0]