How to calculate code distance from the state.

WIP

This document is not fully completed.

Definition of code distance.

Let's consider encoding circuit with $1$ logical bit and $k$ physical qubits[1]. Then this encoding has two physical basis, $\ket{0}_L$ and $\ket{1}_L$. The distance of this encoding is the minimum bit flip required to convert between $\ket{0}_L$ and $\ket{1}_L$.

Classification of the stabilizers.

When treating State, the logical leg is not assigned and one can treat all stabilizers equally. However, if logical leg is assigned to the state, these stabilizers can be classified to $4$ groups.

  1. stabilizers on physical qubits
  2. $\bar{X}$, which corresponds to logical $X$
  3. $\bar{Z}$, which corresponds to logical $Z$
  4. $\bar{Y}$, which corresponds to logical $Y$

Let $\ket{V}$ is the dual state of the channel or encoding map $[[n, 1, d]]$,

\[distance = \min_{S \in stabilizers} \#\left\{ i \mid \bar{Z}_i ≠ S_i \right\}\]

Calculating code distance from the check matrix.

TODO: nor required if the performance doesn't matter.

Examples

$[[5, 1, 3]]$ code

$[[5, 1, 3]]$ code has $4$ stabilizers generators, $XZZXI, IXZZX, XIXZZ, ZXIXZ$ and $2$ logical operators, $\bar{X} = XXXXX$ and $\bar{Z} = ZZZZZ$. Therefore, stabilizer generators for the corresponding state $[[6, 0]]$ is $IXZZXI, IIXZZX, IXIXZZ, IZXIXZ, XXXXXX, ZZZZZZ$.

Let's construct $[[6, 0]]$ state on QuantumLegos.jl.

julia> using QuantumLegos

julia> stab_513 = pauliop.(["IXZZXI", "IIXZZX", "IXIXZZ", "IZXIXZ", "XXXXXX", "ZZZZZZ"])
6-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("IXZZXI")
 pauliop("IIXZZX")
 pauliop("IXIXZZ")
 pauliop("IZXIXZ")
 pauliop("XXXXXX")
 pauliop("ZZZZZZ")

julia> lego_513 = Lego(stab_513)
Lego{6}(6, StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}[pauliop("IXZZXI"), pauliop("IIXZZX"), pauliop("IXIXZZ"), pauliop("IZXIXZ"), pauliop("XXXXXX"), pauliop("ZZZZZZ")])

julia> state_513 = State([lego_513], edge.([]))
State(Lego[Lego{6}(6, StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}[pauliop("IXZZXI"), pauliop("IIXZZX"), pauliop("IXIXZZ"), pauliop("IZXIXZ"), pauliop("XXXXXX"), pauliop("ZZZZZZ")])], Tuple{LegoLeg, LegoLeg}[], CheckMatrix(Bool[0 1 … 0 0; 0 0 … 1 0; … ; 1 1 … 0 0; 0 0 … 1 1], 6, 6))

Then collect generators of the state.

julia> normalizers = state_513.cmat |> generators |> GeneratedPauliGroup |> collect
64-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("IIIIII")
 pauliop("IXZZXI")
 pauliop("IIXZZX")
 pauliop("IXYIYX")
 pauliop("IXIXZZ")
 pauliop("IIZYYZ")
 pauliop("IXXYIY")
 pauliop("IIYXXY")
 pauliop("IZXIXZ")
 pauliop("IYYZIZ")
 ⋮
 pauliop("YYIZZI")
 pauliop("YXZYZX")
 pauliop("YIIXYX")
 pauliop("YXYXII")
 pauliop("YIXYXI")
 pauliop("YIZZIY")
 pauliop("YXIIXY")
 pauliop("YIYIZZ")
 pauliop("YXXZYZ")

Get stabilizer and normalizers of the $[[5, 1, 3]]$ code by assigning the first leg as logical.

julia> stabs = filter(x -> x[1] == PauliOps.I, normalizers)
16-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("IIIIII")
 pauliop("IXZZXI")
 pauliop("IIXZZX")
 pauliop("IXYIYX")
 pauliop("IXIXZZ")
 pauliop("IIZYYZ")
 pauliop("IXXYIY")
 pauliop("IIYXXY")
 pauliop("IZXIXZ")
 pauliop("IYYZIZ")
 pauliop("IZIZYY")
 pauliop("IYZIZY")
 pauliop("IYXXYI")
 pauliop("IZYYZI")
 pauliop("IYIYXX")
 pauliop("IZZXIX")

julia> norm_x = filter(x -> x[1] == PauliOps.X, normalizers)
16-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("XXXXXX")
 pauliop("XIYYIX")
 pauliop("XXIYYI")
 pauliop("XIZXZI")
 pauliop("XIXIYY")
 pauliop("XXYZZY")
 pauliop("XIIZXZ")
 pauliop("XXZIIZ")
 pauliop("XYIXIY")
 pauliop("XZZYXY")
 pauliop("XYXYZZ")
 pauliop("XZYXYZ")
 pauliop("XZIIZX")
 pauliop("XYZZYX")
 pauliop("XZXZII")
 pauliop("XYYIXI")

julia> norm_y = filter(x -> x[1] == PauliOps.Y, normalizers)
16-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("YYYYYY")
 pauliop("YZXXZY")
 pauliop("YYZXXZ")
 pauliop("YZIYIZ")
 pauliop("YZYZXX")
 pauliop("YYXIIX")
 pauliop("YZZIYI")
 pauliop("YYIZZI")
 pauliop("YXZYZX")
 pauliop("YIIXYX")
 pauliop("YXYXII")
 pauliop("YIXYXI")
 pauliop("YIZZIY")
 pauliop("YXIIXY")
 pauliop("YIYIZZ")
 pauliop("YXXZYZ")

julia> norm_z = filter(x -> x[1] == PauliOps.Z, normalizers)
16-element Vector{StaticArraysCore.SVector{6, QuantumLegos.PauliOps.SinglePauliOp}}:
 pauliop("ZZZZZZ")
 pauliop("ZYIIYZ")
 pauliop("ZZYIIY")
 pauliop("ZYXZXY")
 pauliop("ZYZYII")
 pauliop("ZZIXXI")
 pauliop("ZYYXZX")
 pauliop("ZZXYYX")
 pauliop("ZIYZYI")
 pauliop("ZXXIZI")
 pauliop("ZIZIXX")
 pauliop("ZXIZIX")
 pauliop("ZXYYXZ")
 pauliop("ZIXXIZ")
 pauliop("ZXZXYY")
 pauliop("ZIIYZY")

These normalizers are generated from one logical operator and stabilizers.

julia> map(x -> x .* pauliop("XXXXXX"), stabs) |> Set == Set(norm_x)
true

julia> map(x -> x .* pauliop("ZZZZZZ"), stabs) |> Set == Set(norm_x)
false

julia> map(x -> x .* pauliop("ZZZZZZ"), stabs) |> Set == Set(norm_z)
true

julia> map(x -> x .* pauliop("YYYYYY"), stabs) |> Set == Set(norm_y)
true

julia> using IterTools

julia> groupby(x -> x[1], normalizers) .|> Set == Set.([stabs, norm_x, norm_z, norm_y])
true

Define a function to get weight of the operator.

julia> function weight(x, i = 1)
           count(x[i:end] .!= PauliOps.I)
       end
weight (generic function with 2 methods)

julia> weight(pauliop("XIXIXI"))
3

julia> weight(pauliop("XIXIXI"), 2)
2

Calculate coefficients of enumerator polynomial.

julia> using DataStructures

julia> stabs .|> weight |> counter
Accumulator{Int64, Int64} with 2 entries:
  0 => 1
  4 => 15

julia> function weight(i::Integer)
           Base.Fix2(weight, i)
       end
weight (generic function with 3 methods)

julia> normalizers .|> weight(2) |> counter
Accumulator{Int64, Int64} with 4 entries:
  0 => 1
  4 => 15
  5 => 18
  3 => 30

julia> [norm_x..., norm_y..., norm_z...] .|> weight(2) |> counter
Accumulator{Int64, Int64} with 2 entries:
  5 => 18
  3 => 30

julia> [norm_x..., norm_y..., norm_z...] .|> weight(2) |> counter |> keys |> minimum
3

Code distance of the encoding is the minimum degree of the non-zero term in the normalizer's polynomial($B$) and not in the stabilizer's polynomial($A$). So the code distance of this encoding is $3$.

  • 1Not all state can be formalized like this. TODO