Circuits of locations: preparation, gates, measurement, wait; faults and output distributions
DefinitionQAlgorithms_Defs_LocCircuitCircuits of locations: preparation, gates, measurement, wait; faults and output distributions
QAlgorithms.LocKind
An enumerated type with exactly six distinct values:
- (preparation),
- (Hadamard),
- ( gate),
- ,
- (measurement),
- .
Equality between values is decidable.
QAlgorithms.LocKind.arity
The function on the six location kinds that gives the number of qubits a kind acts on:
- has arity .
- Each of , , , , has arity .
QAlgorithms.LocKind.IsGate
The Boolean predicate on location kinds:
- It is true for , and .
- It is false for , and .
QAlgorithms.LocKind.idealMatrix
For each location kind , this gives a complex matrix, where is the arity of . Rows and columns are indexed by bit strings of length .
. The Hadamard
on one qubit.
. The gate on one qubit.
. The two-qubit CNOT with the first qubit as control:
- the entry at row , column is if and , and otherwise;
- equivalently, .
, , . The identity matrix.
QAlgorithms.Loc
For a number of wires , a location consists of three pieces of data:
- Kind. A kind . Let be its arity: for , otherwise.
- Wires. An injective map , giving an ordered tuple of distinct wires.
- Condition. An arbitrary function , which takes a partial assignment of bits to pairs of natural numbers and returns a Boolean. Its input is a map .
Edge cases. No further constraints are imposed. If , no location exists, because no map into the empty set exists.
QAlgorithms.Loc.qubits
For a location on wires, this is the finite set of wires it touches:
Here is the wire map of and is the arity of its kind. Because is injective, this set has exactly elements: one for non-CNOT kinds, two for CNOT.
QAlgorithms.LocCircuit
A location circuit on wires is a finite list of time steps. Each time step is a finite list of locations on wires.
No constraints are imposed. In particular:
- locations in the same step may share wires;
- steps may be empty;
- the list of steps may be empty.
QAlgorithms.LocCircuit.locAt
Setting. A located circuit on qubits consists of a finite list of time steps . Each step is a finite list of locations. A location has three parts:
- a kind, one of preparation, Hadamard, gate, CNOT, measurement, or wait;
- an injective assignment of its (or, for CNOT, ) wires to qubits in ;
- a classical condition.
Definition. For a pair of natural numbers, is the -th entry of step , counted from , if it exists, and "none" otherwise. Explicitly:
- If , step is treated as the empty list, so the result is "none".
- If but , the result is also "none".
QAlgorithms.LocCircuit.locs
For a located circuit with steps , each a list of locations as above, is the finite set of index pairs
These are exactly the pairs for which is defined. Empty steps contribute no pairs.
QAlgorithms.LocCircuit.numQubits
For any located circuit on qubits, . This is the number fixed in the type of the circuit. It does not depend on the steps or locations of , and in particular it does not count only the qubits that are actually used.
QAlgorithms.LocCircuit.numSteps
For a located circuit with step list , . This is the length of the step list. Every step is counted, including steps that contain no locations.
QAlgorithms.LocCircuit.size
For a located circuit with steps , is the cardinality of the set . Since these index pairs are distinct, this equals
the total number of locations of every kind, including preparations, measurements and waits.
QAlgorithms.LocCircuit.measIds
For a located circuit with steps , is the list of all index pairs such that the -th location of step (both counted from ) has kind "measurement".
The list is ordered by step increasing, and within a step by position increasing. It contains no repetitions. Locations of any other kind do not appear.
QAlgorithms.LocCircuit.measCount
For a located circuit , is the length of the list . That list holds the index pairs of all measurement-kind locations, in step-then-position order. So is the total number of measurement locations in .
QAlgorithms.LocCircuit.outcomeAt
Let be a located circuit and a finite list of bits. Let be its measurement locations in step-then-position order.
Pair the two lists positionally, for ; the longer list is truncated. For an index pair , define
The are distinct, so is unique when it exists. The "none" case covers three situations:
- is not a measurement location;
- is not a location at all;
- is the -th measurement with , i.e. is too short.
Bits with are ignored.
QAlgorithms.qubitsOf
For a finite list of locations on qubits, is the union of their qubit sets:
Here is the set of qubits that location acts on, i.e. the image of its wire assignment: one qubit, or two for a CNOT. The empty list gives .
QAlgorithms.stepLive
For a set of qubits and a list of locations on qubits, define
where is the set of qubits acted on by .
Qubits of preparation locations are added and qubits of measurement locations are removed, with the removal applied last. Two consequences follow:
- A qubit that is both prepared and measured in is not in the result.
- A measured qubit is removed whether or not it was in .
Locations of all other kinds have no effect.
QAlgorithms.LocCircuit.liveBefore
Let be a located circuit with steps , and let be an initial set of qubits. The sets , for , are defined recursively by
Here . For , is taken to be the empty list, so the sequence is constant from on.
QAlgorithms.LocCircuit.IsWellFormed
Let be a located circuit on qubits with steps , and let . Write:
- for the set of qubits a location acts on;
- for the live set before step . Here and .
Definition. is well-formed from to if both of the following hold.
- For every step index , all four of these conditions hold:
- (a) Disjointness. Any two locations occupying different positions in the list act on disjoint qubit sets.
- (b) Coverage. Every qubit lies in for some location , of any kind, including wait.
- (c) Preparations. Every location of kind preparation satisfies .
- (d) Other kinds. Every location of any other kind (Hadamard, , CNOT, measurement, wait) satisfies .
- Final live set. .
Not constrained. The classical conditions attached to locations are not restricted by this definition. When , condition 1 is vacuous and the definition reduces to .
QAlgorithms.LocCircuit.IsIdealCircuit
This is a property of a location circuit on qubits (wires ). Such a circuit is a list of time steps . Each step is a list of locations , and each location has three parts:
- a kind: preparation, Hadamard, gate, CNOT, measurement, or wait;
- an injective list of wires (two for CNOT, one otherwise);
- a classical condition , a Boolean-valued function of a partial table of measurement outcomes.
is ideal when two conditions hold. The first is that is well-formed with empty input live set and empty output live set. This means the following. Define live sets by
where and are the unions of the wire sets of the preparation and measurement locations of the step. Then for every step :
- the locations of have pairwise disjoint wire sets;
- every qubit in is a wire of some location of ;
- every preparation location of has wires disjoint from ;
- every non-preparation location of has all its wires in .
In addition, .
The second condition is that every location in every step has the constant condition .
When has no steps, the first condition holds trivially because .
QAlgorithms.ErrAssign
For and a type (an environment register), an error assignment is any function
where . It assigns to each address (read as step , position ) either "no error" or a superoperator.
Here a superoperator on is any map from complex matrices to matrices of the same shape. Nothing requires it to be linear, completely positive, or trace-preserving. Addresses that do not correspond to any location are also allowed to carry a value.
QAlgorithms.CQState
For and a type , a classical–quantum state is any function
It assigns to every finite list of bits (read as a measurement record) a complex matrix on the joint system of qubits and the environment , with .
No positivity, trace, or normalization condition is imposed on the matrices .
QAlgorithms.qubitOp1
Let be a complex matrix indexed by bits, and let be a wire, . Then is the matrix indexed by with
That is, it is acting on qubit , tensored with the identity on all other qubits.
QAlgorithms.resetKraus
For a bit , is the matrix with entries
In other words, .
QAlgorithms.measProj
For a bit , is the matrix with entry at and elsewhere. In other words, , the projector onto computational basis state .
QAlgorithms.Loc.apply
This defines how a single location on qubits acts on a classical–quantum state. The environment is a finite type with decidable equality. The inputs are:
- an outcome lookup , which maps each record to a partial table ;
- an optional error , either "none" or an arbitrary map on -matrices;
- a state , which is a function from bit lists to such matrices.
Some notation used below:
- is the error map: if is none, and if .
- is the first wire of .
- is the extension of an -qubit matrix to the joint system.
- is the reset Kraus operator on qubit .
- is the measurement projector on qubit .
- denotes the conjugate transpose.
The resulting state is defined case by case on the kind of :
- Preparation: the qubit is reset to and the error is applied afterwards:
- Measurement: the error is applied before the projection, and the last record bit selects the outcome. If is empty, . If (last bit ), then
Only the first (and only) wire is used.
-
Wait: .
-
Hadamard, , CNOT: the gate is applied only if the classical condition holds, and the error is applied afterwards:
Here is the ideal gate placed on the wires of (identity elsewhere):
- the Hadamard ;
- the gate ;
- CNOT, with control the first wire of and target the second.
QAlgorithms.runStep
This runs one time step, applying its locations one after another. The inputs are:
- an outcome lookup ;
- an error assignment ;
- a step number ;
- a list of locations ;
- a starting position ;
- a state .
The result is
Each factor is defined by
So receives the error at address . All locations use the same outcome lookup . For an empty list the state is returned unchanged.
QAlgorithms.LocCircuit.runFrom
This runs a list of steps , starting at step number , on a state , for a circuit on qubits and an error assignment . Steps are processed in order. Step is executed as
which starts at position , so its -th location gets error .
The outcome lookup for step number is
Here is defined as follows:
- List the measurement locations of as addresses , in step order and then in position order within each step: .
- Pair this list with the bits of .
- is if with , and undefined otherwise.
As a result, conditions only see outcomes of measurements from strictly earlier step numbers that also lie within the record. If the list of steps is empty, the state is returned unchanged.
QAlgorithms.LocCircuit.run
For a location circuit on qubits with steps , an error assignment , and a classical–quantum state :
That is, all steps of are executed in order, starting at step number . Step 's -th location receives error , and gate conditions see only outcomes from steps , as described under runFrom.
QAlgorithms.initState
For and an arbitrary complex matrix on a type , the initial classical–quantum state is
Here is the matrix with entries for ; this is at and elsewhere. No condition on is imposed: it need not be positive or have trace .
QAlgorithms.LocCircuit.outDist
Let be a location circuit on qubits, a finite type with decidable equality, an error assignment, and an arbitrary complex matrix on . Let be the number of measurement locations of .
assigns a real number to each bit string :
This is the real part of the trace of the final matrix at the full record , read as a list. The trace is over the joint system .
No normalization is performed. Since and the error maps are unrestricted, the values need not be nonnegative and need not sum to . When there is a single, empty record.
QAlgorithms.LocCircuit.idealOutDist
This defines the noiseless measurement-outcome function of a location circuit on qubits. Let be the number of measurement locations of . The result is a real-valued function on bit strings :
Here comes from the following run.
The state. A classical–quantum state is a function assigning to every finite bit list a matrix on . The environment is a single point (one-dimensional) with environment state the identity .
The initial state. It is
The steps. The steps of are processed in order, and within each step the locations are processed in list order. Every location is error-free (the error assignment is "none" everywhere). A location transforms into as follows, where denotes the first wire of :
- Preparation: , where acts on qubit (identity elsewhere).
- Measurement:
- If is empty, .
- Otherwise, let be the last entry of and be with that entry removed. Then , with on qubit .
- Wait: .
- Hadamard, , CNOT:
- The gate is applied only if the location's classical condition evaluates to true on . In that case .
- Otherwise .
- is the ideal gate embedded on 's wires: , , or CNOT with the first wire as control.
- is the map that sends a location to the bit of recorded for it, but only if lies in an earlier step than ; otherwise it sends to "none".
- The bit recorded for a measurement location is found by pairing the list of all measurement locations of (ordered by step, then position within the step) with the entries of .
No well-formedness of is assumed. Nothing asserts that the values are nonnegative or sum to .
QAlgorithms.pushDist
This defines the pushforward of a real function along a list decoder. The inputs are:
- natural numbers ;
- a decoding map from finite bit lists to finite bit lists;
- a real function on .
The result is the real function on given by
Equality is equality of lists, so it includes equality of lengths. If never outputs a list of length , the function is identically . No condition is placed on (it need not be nonnegative or normalized) or on .
QAlgorithms.PauliArrangement
A Pauli arrangement is any function assigning to each pair of natural numbers one of two things:
- either "no fault";
- or an ordered pair of single-qubit Pauli labels, each from .
There are no constraints. In particular:
- the pair is allowed and still counts as a fault entry;
- the function may be non-"none" at infinitely many pairs.
QAlgorithms.faultyLocs
For a location circuit on qubits and a Pauli arrangement , this is the finite set of faulty locations:
Here is the set of pairs with less than the number of steps of and less than the number of locations in step .
Some consequences of the literal definition:
- Entries of outside are ignored.
- A location assigned is counted as faulty.
QAlgorithms.locPauli
For and Pauli labels , this is the matrix on qubits
Here denote the usual matrices with , indexed with row/column first. In other words, the matrix is , with on wire . Special cases:
- : it is the identity.
- : it is .
- : it is .
QAlgorithms.pauliErr
For a location circuit on qubits and a Pauli arrangement , this is the error assignment with a one-dimensional environment, defined at each pair as follows.
When there is an error. If has a location at and , the error at is the conjugation map
- is the -qubit matrix that acts as on the wires of , in their listed order, and as the identity on all other wires.
- is the arity of : for CNOT, for every other kind.
Concretely, equals if and agree off the wire set , and equals otherwise.
When there is no error. In all other cases (no location at , or is none) the assignment at is "no error".
This applies to every location kind, including preparation, measurement and wait.
QAlgorithms.IsLocError
Let be an injective listing of wires among wires, let be a finite environment index set, and let be a map from matrices on to matrices on the same space. The proposition asserts that there exists a map on matrices over satisfying two conditions.
Condition 1: is a channel. This means there exist and matrices on such that
Condition 2: is the lift of along . This means that for all matrices and all indices , :
where
- is the restriction of to the wires .
- is the string equal to at wire and equal to on all other wires.
import Definitions.Def_QAlgorithms_Defs_BinaryStabilizer
/-!
# Circuits of locations: preparation, gates, measurement, wait; faults and output distributions
(shared definition layer)
D. Gottesman, *An introduction to quantum error correction and fault-tolerant quantum
computation* (arXiv:0904.2557), cited "Gottesman intro p.N" (PDF pages), §4.1 and Def. 6.
Model. A circuit on `N` qubits (wires `Fin N`) is a list of time steps, each a list of locations
(§4.1, p.17): preparation of `|0⟩`, the gates `H`, `R_{π/8} = diag(1, e^{iπ/4})` (frozen `gateT`)
and CNOT, standard-basis measurement, and wait. A location is identified by `(s, j) : ℕ × ℕ`
(time step `s`, position `j` in the step). The measurement record lists the outcomes in
step-major order. Gate locations may be classically controlled by earlier outcomes (classical
computation is perfect and free, p.17); the condition reads the outcomes by location id and sees
only the outcomes of earlier time steps. The state of the computer is classical–quantum: for each
measurement record, a subnormalized density matrix on the qubits and an environment register `E`
(trivial, `Fin 1`, when no environment is needed). An error assignment puts a superoperator on
qubits ⊗ environment at some locations: after the location's ideal action, and before it for a
measurement location.
-/
namespace QAlgorithms
open scoped Matrix
/-- Gottesman intro p.17 (§4.1) and p.33 (Def. 6): the types of location: preparation of `|0⟩`,
the gates `H`, `R_{π/8}`, CNOT (the universal set of p.17), measurement in the standard basis, and
wait. -/
inductive LocKind where
| prep
| had
| tgate
| cnot
| meas
| wait
deriving DecidableEq
/-- Gottesman intro p.18: the number of qubits a location involves (two for CNOT, one
otherwise). -/
def LocKind.arity : LocKind → ℕ
| .cnot => 2
| _ => 1
theorem LocKind.arity_pos (κ : LocKind) : 0 < κ.arity := by
cases κ <;> decide
/-- Gottesman intro p.17 (§4.1): the gate locations `H`, `R_{π/8}`, CNOT (classical control is
meaningful for these only). -/
def LocKind.IsGate : LocKind → Bool
| .had => true
| .tgate => true
| .cnot => true
| _ => false
/-- Gottesman intro p.17 (§4.1): the ideal unitary of a location on its qubits: `H` (frozen
`hadamard`), `R_{π/8} = diag(1, e^{iπ/4})` (frozen `gateT`), CNOT (frozen `cnot`, control on the
first wire), and the identity for wait (and, unused, for preparation and measurement). -/
noncomputable def LocKind.idealMatrix : (κ : LocKind) → Matrix (Qubits κ.arity) (Qubits κ.arity) ℂ
| .had => (Gate.ofMatrix1 hadamard).mat
| .tgate => (Gate.ofMatrix1 gateT).mat
| .cnot => (Gate.ofMatrix2 QAlgorithms.cnot).mat
| .prep => 1
| .meas => 1
| .wait => 1
/-- Gottesman intro p.17 (§4.1): a location on `N` qubits: its type, the qubits it acts on (in
order: control first for CNOT), and, for gate locations, a classical condition on the earlier
measurement outcomes (given by location id; `none` for an outcome not available). The gate is
applied iff the condition holds; the location exists, and can be faulty, either way. The
condition is ignored for preparation, measurement and wait locations. -/
structure Loc (N : ℕ) where
kind : LocKind
wires : Fin kind.arity ↪ Fin N
cond : (ℕ × ℕ → Option Bool) → Bool
/-- The set of qubits a location acts on. -/
def Loc.qubits {N : ℕ} (l : Loc N) : Finset (Fin N) :=
Finset.univ.map l.wires
/-- Gottesman intro p.17 (§4.1) and p.33 (Def. 6): a circuit of locations on `N` qubits, organised
in time steps. -/
structure LocCircuit (N : ℕ) where
steps : List (List (Loc N))
namespace LocCircuit
variable {N : ℕ}
/-- The location with id `(s, j)` (time step `s`, position `j`), if any. -/
def locAt (C : LocCircuit N) (ℓ : ℕ × ℕ) : Option (Loc N) :=
(C.steps.getD ℓ.1 [])[ℓ.2]?
/-- Gottesman intro p.17: the set of (ids of) locations of the circuit. -/
def locs (C : LocCircuit N) : Finset (ℕ × ℕ) :=
(Finset.range C.steps.length).biUnion fun s =>
(Finset.range (C.steps.getD s []).length).image fun j => (s, j)
/-- The number of qubits of the circuit. -/
def numQubits (_ : LocCircuit N) : ℕ :=
N
/-- The number of time steps of the circuit. -/
def numSteps (C : LocCircuit N) : ℕ :=
C.steps.length
/-- Gottesman intro p.41 (Thm 10): `|C|`, the number of locations. -/
def size (C : LocCircuit N) : ℕ :=
C.locs.card
/-- The ids of the measurement locations, in the order of the measurement record (step-major,
then by position). -/
def measIds (C : LocCircuit N) : List (ℕ × ℕ) :=
C.steps.zipIdx.flatMap fun p =>
p.1.zipIdx.filterMap fun q => if q.1.kind = .meas then some (p.2, q.2) else none
/-- The number of measurement locations, i.e. the length of the measurement record. -/
def measCount (C : LocCircuit N) : ℕ :=
C.measIds.length
/-- The outcome recorded in `r` at the measurement location `ℓ` (`none` if `ℓ` is not a
measurement location or `r` is too short). -/
def outcomeAt (C : LocCircuit N) (r : List Bool) (ℓ : ℕ × ℕ) : Option Bool :=
(C.measIds.zip r).lookup ℓ
end LocCircuit
/-- The qubits acted on by a list of locations. -/
def qubitsOf {N : ℕ} (ls : List (Loc N)) : Finset (Fin N) :=
ls.foldr (fun l acc => l.qubits ∪ acc) ∅
/-- Gottesman intro p.33 (Def. 6: "the preparation locations introduce new qubits into the
circuit, and the measurement locations remove qubits"): the live qubits after a time step. -/
def stepLive {N : ℕ} (live : Finset (Fin N)) (st : List (Loc N)) : Finset (Fin N) :=
(live ∪ qubitsOf (st.filter fun l => decide (l.kind = .prep))) \
qubitsOf (st.filter fun l => decide (l.kind = .meas))
namespace LocCircuit
variable {N : ℕ}
/-- The live qubits at the start of time step `s`, starting from the live set `liveIn`. -/
def liveBefore (C : LocCircuit N) (liveIn : Finset (Fin N)) : ℕ → Finset (Fin N)
| 0 => liveIn
| s + 1 => stepLive (C.liveBefore liveIn s) (C.steps.getD s [])
/-- Gottesman intro p.33 (Def. 6), relative to boundary live sets: within each time step the
locations act on pairwise disjoint qubits; every live qubit ("not counting those to be added at
later time steps or removed at earlier time steps") is involved in a location of the step (hence
in exactly one); a preparation acts only on a qubit that is not live and every other location only
on live qubits; and the live qubits after the last step are `liveOut`. A full circuit has
`liveIn = liveOut = ∅` (the first location of every qubit is a preparation and the last a
measurement); a gadget has the data qubits of its input and output blocks. -/
def IsWellFormed (C : LocCircuit N) (liveIn liveOut : Finset (Fin N)) : Prop :=
(∀ s < C.steps.length,
(C.steps.getD s []).Pairwise (fun l l' => Disjoint l.qubits l'.qubits) ∧
(∀ q ∈ C.liveBefore liveIn s, ∃ l ∈ C.steps.getD s [], q ∈ l.qubits) ∧
(∀ l ∈ C.steps.getD s [], l.kind = .prep → Disjoint l.qubits (C.liveBefore liveIn s)) ∧
(∀ l ∈ C.steps.getD s [], l.kind ≠ .prep → l.qubits ⊆ C.liveBefore liveIn s)) ∧
C.liveBefore liveIn C.steps.length = liveOut
/-- Gottesman intro p.19 ("the original circuit takes no input: all qubits used in it must be
prepared using preparation locations"; its output is the outcome of the final measurements) and
p.33 (Def. 6): an ideal circuit: well formed with empty boundaries, and without classical
control. -/
def IsIdealCircuit (C : LocCircuit N) : Prop :=
C.IsWellFormed ∅ ∅ ∧ ∀ st ∈ C.steps, ∀ l ∈ st, l.cond = fun _ => true
end LocCircuit
/-- Gottesman intro p.18 (§4.1) and p.40 (Def. 12): an error assignment for a circuit on `N`
qubits with environment `E`: `some Φ` at a location applies the superoperator `Φ` (on qubits ⊗
environment) right after the location's ideal operation (right before it, for a measurement):
"the actual error should be considered to be the action of the failed component times the inverse
of the desired component". -/
abbrev ErrAssign (N : ℕ) (E : Type*) :=
ℕ × ℕ → Option (Superop (Qubits N × E) (Qubits N × E))
/-- A classical–quantum state: for each measurement record, a subnormalized density matrix on
qubits ⊗ environment. -/
abbrev CQState (N : ℕ) (E : Type*) :=
List Bool → Matrix (Qubits N × E) (Qubits N × E) ℂ
/-- The single-qubit operator `M` on wire `q` (identity elsewhere). -/
def qubitOp1 {N : ℕ} (M : Matrix Bool Bool ℂ) (q : Fin N) : Matrix (Qubits N) (Qubits N) ℂ :=
embedOp (Gate.ofMatrix1 M).mat (Stabilizer.wire1 q)
/-- The reset Kraus operators `|0⟩⟨b|` (a preparation location resets its qubit to `|0⟩`). -/
def resetKraus (b : Bool) : Matrix Bool Bool ℂ :=
Matrix.of fun y z => if y = false ∧ z = b then 1 else 0
/-- The standard-basis projectors `|b⟩⟨b|` (p.17: measurement of individual qubits in the
standard basis). -/
def measProj (b : Bool) : Matrix Bool Bool ℂ :=
Matrix.of fun y z => if y = b ∧ z = b then 1 else 0
/-- Gottesman intro pp.17–18: the action of one location on a classical–quantum state, with the
condition reading the outcomes through `O` and with the assigned error `e`. Preparation: reset
the qubit to `|0⟩`, then the error. Gate: the ideal unitary on its qubits if the condition holds
(identity otherwise), then the error. Wait: the error only. Measurement: the error, then the
branch of record `r ++ [b]` receives `Π_b ρ_r Π_b`. -/
noncomputable def Loc.apply {N : ℕ} {E : Type*} [Fintype E] [DecidableEq E] (l : Loc N)
(O : List Bool → ℕ × ℕ → Option Bool) (e : Option (Superop (Qubits N × E) (Qubits N × E)))
(ρ : CQState N E) : CQState N E :=
let errF : Matrix (Qubits N × E) (Qubits N × E) ℂ → Matrix (Qubits N × E) (Qubits N × E) ℂ :=
fun X => match e with
| none => X
| some Φ => Φ X
let q : Fin N := l.wires ⟨0, l.kind.arity_pos⟩
let lift : Matrix (Qubits N) (Qubits N) ℂ → Matrix (Qubits N × E) (Qubits N × E) ℂ :=
fun M => Matrix.kronecker M 1
match l.kind with
| .prep => fun r => errF (krausApply (fun b : Bool => lift (qubitOp1 (resetKraus b) q)) (ρ r))
| .meas => fun r => match r.getLast? with
| none => 0
| some b => conjSuperop (lift (qubitOp1 (measProj b) q)) (errF (ρ r.dropLast))
| .wait => fun r => errF (ρ r)
| .had | .tgate | .cnot => fun r => errF (if l.cond (O r) then
conjSuperop (lift (embedOp l.kind.idealMatrix l.wires)) (ρ r) else ρ r)
/-- The locations of one time step `s`, applied in list order (position `j` upward). -/
noncomputable def runStep {N : ℕ} {E : Type*} [Fintype E] [DecidableEq E]
(O : List Bool → ℕ × ℕ → Option Bool) (err : ErrAssign N E) (s : ℕ) :
List (Loc N) → ℕ → CQState N E → CQState N E
| [], _, ρ => ρ
| l :: ls, j, ρ => runStep O err s ls (j + 1) (l.apply O (err (s, j)) ρ)
namespace LocCircuit
variable {N : ℕ} {E : Type*} [Fintype E] [DecidableEq E]
/-- The time steps `s, s + 1, …` given by the list, in order; at step `s` the conditions see the
outcomes of the measurement locations of steps `< s`. -/
noncomputable def runFrom (C : LocCircuit N) (err : ErrAssign N E) :
List (List (Loc N)) → ℕ → CQState N E → CQState N E
| [], _, ρ => ρ
| st :: rest, s, ρ =>
C.runFrom err rest (s + 1)
(runStep (fun r ℓ => if ℓ.1 < s then C.outcomeAt r ℓ else none) err s st 0 ρ)
/-- Gottesman intro pp.17–18: the classical–quantum state after running the whole circuit with the
error assignment `err` from the state `ρ`. -/
noncomputable def run (C : LocCircuit N) (err : ErrAssign N E) (ρ : CQState N E) : CQState N E :=
C.runFrom err C.steps 0 ρ
end LocCircuit
/-- The initial state: all qubits `|0⟩` (immaterial for circuits that prepare every qubit before
use), the environment in `σ0`, the empty record. -/
noncomputable def initState (N : ℕ) {E : Type*} (σ0 : Matrix E E ℂ) : CQState N E :=
fun r => if r = [] then Matrix.kronecker (pureDensity (zeroKet N)) σ0 else 0
namespace LocCircuit
variable {N : ℕ}
/-- Gottesman intro p.19 and p.36 (Thm 7): the output distribution of the circuit: the
probability of each measurement record (outcomes in step-major order) under the error assignment
`err`, the environment starting in `σ0`. -/
noncomputable def outDist {E : Type*} [Fintype E] [DecidableEq E] (C : LocCircuit N)
(err : ErrAssign N E) (σ0 : Matrix E E ℂ) : (Fin C.measCount → Bool) → ℝ :=
fun r => (C.run err (initState N σ0) (List.ofFn r)).trace.re
/-- Gottesman intro p.19: the output distribution of the circuit without errors. -/
noncomputable def idealOutDist (C : LocCircuit N) : (Fin C.measCount → Bool) → ℝ :=
C.outDist (E := Fin 1) (fun _ => none) 1
end LocCircuit
/-- Gottesman intro p.19 ("the final fault-tolerant measurement gadgets will produce classical
information which should … give the same outcome as the original circuit"): the distribution of
the classically decoded record `dec r′` when `r′` has distribution `μ`. -/
def pushDist {M M' : ℕ} (dec : List Bool → List Bool) (μ : (Fin M' → Bool) → ℝ) :
(Fin M → Bool) → ℝ :=
fun r => ∑ r' ∈ Finset.univ.filter (fun r' : Fin M' → Bool => dec (List.ofFn r') = List.ofFn r), μ r'
/-- Gottesman intro p.20 ("restrict attention to cases where the error associated to each fault is
a Pauli operator"): a Pauli fault arrangement: `some (P, Q)` makes the location faulty with the
Pauli error `P` on its first qubit and `Q` on its second (`Q` is ignored for one-qubit
locations). -/
abbrev PauliArrangement :=
ℕ × ℕ → Option (Stabilizer.Pauli × Stabilizer.Pauli)
/-- The faulty locations of the circuit under a Pauli arrangement (p.20: the `s` of a gadget is
their number). -/
def faultyLocs {N : ℕ} (C : LocCircuit N) (fa : PauliArrangement) : Finset (ℕ × ℕ) :=
C.locs.filter fun ℓ => fa ℓ ≠ none
/-- The Pauli operator `P` (first qubit) `⊗ Q` (other qubits) on the qubits of a location. -/
noncomputable def locPauli (k : ℕ) (P Q : Stabilizer.Pauli) : Matrix (Qubits k) (Qubits k) ℂ :=
Matrix.of fun y z => ∏ i : Fin k, (if (i : ℕ) = 0 then P else Q).mat (y i) (z i)
/-- Gottesman intro p.20: the error assignment of a Pauli arrangement: conjugation by the Pauli
error on the qubits of each faulty location (trivial environment). -/
noncomputable def pauliErr {N : ℕ} (C : LocCircuit N) (fa : PauliArrangement) :
ErrAssign N (Fin 1) :=
fun ℓ => match C.locAt ℓ, fa ℓ with
| some l, some pq =>
some (conjSuperop (Matrix.kronecker (embedOp (locPauli l.kind.arity pq.1 pq.2) l.wires)
(1 : Matrix (Fin 1) (Fin 1) ℂ)))
| _, _ => none
/-- Gottesman intro p.18 ("an error that can affect all of the qubits involved in the action") and
p.40 (Def. 12: "any quantum operation"): `Φ` is an admissible error at a location with qubits `w`:
some quantum operation on those qubits jointly with the environment, the identity on the other
qubits. -/
def IsLocError {N k : ℕ} (w : Fin k ↪ Fin N) {E : Type*} [Fintype E] [DecidableEq E]
(Φ : Superop (Qubits N × E) (Qubits N × E)) : Prop :=
∃ Ψ : Superop ((Fin k → Bool) × E) ((Fin k → Bool) × E), IsChannel Ψ ∧ Φ = liftSuperop w Ψ
/-! ### Sanity tests -/
example : LocKind.cnot.arity = 2 ∧ LocKind.meas.arity = 1 := ⟨rfl, rfl⟩
example (y z : Qubits 1) : LocKind.had.idealMatrix y z = hadamard (y 0) (z 0) := rfl
example {N : ℕ} {E : Type*} [Fintype E] [DecidableEq E] (err : ErrAssign N E) (ρ : CQState N E) :
(⟨[]⟩ : LocCircuit N).run err ρ = ρ := rfl
example {N : ℕ} : (⟨[]⟩ : LocCircuit N).IsWellFormed ∅ ∅ :=
⟨fun s hs => absurd hs (by simp), rfl⟩
example : (⟨[[⟨.prep, Stabilizer.wire1 0, fun _ => true⟩], [⟨.meas, Stabilizer.wire1 0, fun _ => true⟩]]⟩ :
LocCircuit 1).measIds = [(1, 0)] := rfl
example {M : ℕ} (μ : (Fin M → Bool) → ℝ) : pushDist id μ = μ := by
funext r
simp [pushDist, List.ofFn_inj, Finset.filter_eq']
example {N : ℕ} (C : LocCircuit N) : faultyLocs C (fun _ => none) = ∅ := by
simp [faultyLocs]
example {N : ℕ} (C : LocCircuit N) (ℓ : ℕ × ℕ) : pauliErr C (fun _ => none) ℓ = none := by
simp only [pauliErr]
split <;> simp_all
end QAlgorithms