Skip to content

Commit

Permalink
enable Lifted Product Code tests and reintroduce piracy (QuantumSavor…
Browse files Browse the repository at this point in the history
…y#371)

* enable Lifted Product Code tests and reintroduce piracy

* do not use reuse the "lift" name in a non-idiomatic-for-Nemo way

* simplify the representation function call in LPCode
  • Loading branch information
Krastanov authored Sep 27, 2024
1 parent 57a0069 commit 4e06c01
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 71 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

# News

## v0.9.11-dev
## v0.9.11

- `hcat` of Tableaux objects
- `QuantumReedMuller` codes added to the ECC module
- **(breaking)** change the convention for how to provide a representation function in the constructor of `LPCode` -- strictly speaking a breaking change, but this is not an API that is publicly used in practice

## v0.9.10 - 2024-09-26

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumClifford"
uuid = "0525e862-1e90-11e9-3e4d-1b39d7109de1"
authors = ["Stefan Krastanov <[email protected]> and QuantumSavory community members"]
version = "0.9.11-dev"
version = "0.9.11"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand Down
3 changes: 2 additions & 1 deletion ext/QuantumCliffordHeckeExt/QuantumCliffordHeckeExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import QuantumClifford, LinearAlgebra
import Hecke: Group, GroupElem, AdditiveGroup, AdditiveGroupElem,
GroupAlgebra, GroupAlgebraElem, FqFieldElem, representation_matrix, dim, base_ring,
multiplication_table, coefficients, abelian_group, group_algebra
import Nemo: characteristic, matrix_repr, GF, ZZ
import Nemo
import Nemo: characteristic, matrix_repr, GF, ZZ, lift

import QuantumClifford.ECC: AbstractECC, CSS, ClassicalCode,
hgp, code_k, code_n, code_s, iscss, parity_checks, parity_checks_x, parity_checks_z, parity_checks_xz,
Expand Down
22 changes: 13 additions & 9 deletions ext/QuantumCliffordHeckeExt/lifted.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ The default `GA` is the group algebra of `A[1, 1]`, the default representation `
## The representation function `repr`
In this struct, we use the default representation function `default_repr` to convert a `GF(2)`-group algebra element to a binary matrix.
We use the default representation function `Hecke.representation_matrix` to convert a `GF(2)`-group algebra element to a binary matrix.
The default representation, provided by `Hecke`, is the permutation representation.
We also accept a custom representation function.
We also accept a custom representation function (the `repr` field of the constructor).
Whatever the representation, the matrix elements need to be convertible to Integers (e.g. permit `lift(ZZ, ...)`).
Such a customization would be useful to reduce the number of bits required by the code construction.
For example, if we use a D4 group for lifting, our default representation will be `8×8` permutation matrices,
Expand Down Expand Up @@ -54,14 +55,12 @@ struct LiftedCode <: ClassicalCode
end
end

default_repr(y::GroupAlgebraElem{FqFieldElem, <: GroupAlgebra}) = Matrix((x -> Bool(Int(lift(ZZ, x)))).(representation_matrix(y)))

"""
`LiftedCode` constructor using the default `GF(2)` representation (coefficients converted to a permutation matrix by `representation_matrix` provided by Hecke).
""" # TODO doctest example
function LiftedCode(A::Matrix{GroupAlgebraElem{FqFieldElem, <: GroupAlgebra}}; GA::GroupAlgebra=parent(A[1,1]))
!(characteristic(base_ring(A[1, 1])) == 2) && error("The default permutation representation applies only to GF(2) group algebra; otherwise, a custom representation function should be provided")
LiftedCode(A; GA=GA, repr=default_repr)
LiftedCode(A; GA=GA, repr=representation_matrix)
end

# TODO document and doctest example
Expand All @@ -71,7 +70,7 @@ function LiftedCode(group_elem_array::Matrix{<: GroupOrAdditiveGroupElem}; GA::G
A[i, j] = GA[A[i, j]]
end
if repr === nothing
return LiftedCode(A; GA=GA, repr=default_repr)
return LiftedCode(A; GA=GA, repr=representation_matrix)
else
return LiftedCode(A; GA=GA, repr=repr)
end
Expand All @@ -85,11 +84,16 @@ function LiftedCode(shift_array::Matrix{Int}, l::Int; GA::GroupAlgebra=group_alg
A[i, j] = GA[shift_array[i, j]%l+1]
end
end
return LiftedCode(A; GA=GA, repr=default_repr)
return LiftedCode(A; GA=GA, repr=representation_matrix)
end

function lift(repr::Function, mat::GroupAlgebraElemMatrix)
vcat([hcat([repr(mat[i, j]) for j in axes(mat, 2)]...) for i in axes(mat, 1)]...)
lift_to_bool(x) = Bool(Int(lift(ZZ,x)))

function concat_lift_repr(repr, mat)
x = repr.(mat)
y = hvcat(size(x,2), transpose(x)...)
z = Matrix(lift_to_bool.(y))
return z
end

function parity_checks(c::LiftedCode)
Expand Down
10 changes: 5 additions & 5 deletions ext/QuantumCliffordHeckeExt/lifted_product.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ julia> code_n(c2), code_k(c2)
## The representation function
In this struct, we use the default representation function `default_repr` to convert a `GF(2)`-group algebra element to a binary matrix.
We use the default representation function `Hecke.representation_matrix` to convert a `GF(2)`-group algebra element to a binary matrix.
The default representation, provided by `Hecke`, is the permutation representation.
We also accept a custom representation function as detailed in [`LiftedCode`](@ref).
Expand Down Expand Up @@ -107,24 +107,24 @@ end

# TODO document and doctest example
function LPCode(A::FqFieldGroupAlgebraElemMatrix, B::FqFieldGroupAlgebraElemMatrix; GA::GroupAlgebra=parent(A[1,1]))
LPCode(LiftedCode(A; GA=GA, repr=default_repr), LiftedCode(B; GA=GA, repr=default_repr); GA=GA, repr=default_repr)
LPCode(LiftedCode(A; GA=GA, repr=representation_matrix), LiftedCode(B; GA=GA, repr=representation_matrix); GA=GA, repr=representation_matrix)
end

# TODO document and doctest example
function LPCode(group_elem_array1::Matrix{<: GroupOrAdditiveGroupElem}, group_elem_array2::Matrix{<: GroupOrAdditiveGroupElem}; GA::GroupAlgebra=group_algebra(GF(2), parent(group_elem_array1[1,1])))
LPCode(LiftedCode(group_elem_array1; GA=GA), LiftedCode(group_elem_array2; GA=GA); GA=GA, repr=default_repr)
LPCode(LiftedCode(group_elem_array1; GA=GA), LiftedCode(group_elem_array2; GA=GA); GA=GA, repr=representation_matrix)
end

# TODO document and doctest example
function LPCode(shift_array1::Matrix{Int}, shift_array2::Matrix{Int}, l::Int; GA::GroupAlgebra=group_algebra(GF(2), abelian_group(l)))
LPCode(LiftedCode(shift_array1, l; GA=GA), LiftedCode(shift_array2, l; GA=GA); GA=GA, repr=default_repr)
LPCode(LiftedCode(shift_array1, l; GA=GA), LiftedCode(shift_array2, l; GA=GA); GA=GA, repr=representation_matrix)
end

iscss(::Type{LPCode}) = true

function parity_checks_xz(c::LPCode)
hx, hz = hgp(c.A, c.B')
hx, hz = lift(c.repr, hx), lift(c.repr, hz)
hx, hz = concat_lift_repr(c.repr,hx), concat_lift_repr(c.repr,hz)
return hx, hz
end

Expand Down
2 changes: 1 addition & 1 deletion ext/QuantumCliffordHeckeExt/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Compute the adjoint of a group algebra element.
The adjoint is defined as the conjugate of the element in the group algebra,
i.e. the inverse of the element in the associated group.
"""
function _adjoint(a::GroupAlgebraElem{T}) where T # TODO Is this used? Should it be deleted?
function Base.adjoint(a::GroupAlgebraElem{T}) where T # TODO we would like to use Base.adjoint, but that would be type piracy. Upstream this to Nemo or Hecke or AbstractAlgebra
A = parent(a)
d = dim(A)
v = Vector{T}(undef, d)
Expand Down
18 changes: 9 additions & 9 deletions test/test_ecc_base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ B = reshape([1 + x + x^6], (1, 1))
push!(other_lifted_product_codes, LPCode(A, B))

const code_instance_args = Dict(
Toric => [(3,3), (4,4), (3,6), (4,3), (5,5)],
Surface => [(3,3), (4,4), (3,6), (4,3), (5,5)],
Gottesman => [3, 4, 5],
CSS => (c -> (parity_checks_x(c), parity_checks_z(c))).([Shor9(), Steane7(), Toric(4, 4)]),
Concat => [(Perfect5(), Perfect5()), (Perfect5(), Steane7()), (Steane7(), Cleve8()), (Toric(2, 2), Shor9())],
CircuitCode => random_circuit_code_args,
LPCode => (c -> (c.A, c.B)).(vcat(LP04, LP118, test_gb_codes, other_lifted_product_codes)),
QuantumReedMuller => [3, 4, 5]
:Toric => [(3,3), (4,4), (3,6), (4,3), (5,5)],
:Surface => [(3,3), (4,4), (3,6), (4,3), (5,5)],
:Gottesman => [3, 4, 5],
:CSS => (c -> (parity_checks_x(c), parity_checks_z(c))).([Shor9(), Steane7(), Toric(4, 4)]),
:Concat => [(Perfect5(), Perfect5()), (Perfect5(), Steane7()), (Steane7(), Cleve8()), (Toric(2, 2), Shor9())],
:CircuitCode => random_circuit_code_args,
:LPCode => (c -> (c.A, c.B)).(vcat(LP04, LP118, test_gb_codes, other_lifted_product_codes)),
:QuantumReedMuller => [3, 4, 5]
)

function all_testablable_code_instances(;maxn=nothing)
codeinstances = []
for t in subtypes(QuantumClifford.ECC.AbstractECC)
for c in get(code_instance_args, t, [])
for c in get(code_instance_args, t.name.name, [])
codeinstance = t(c...)
!isnothing(maxn) && nqubits(codeinstance) > maxn && continue
push!(codeinstances, codeinstance)
Expand Down
2 changes: 1 addition & 1 deletion test/test_ecc_codeproperties.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
@test code_s(code) + code_k(code) >= code_n(code) # possibly exist redundant checks
_, _, rank = canonicalize!(copy(H), ranks=true)
@test rank <= size(H, 1)
@test QuantumClifford.stab_looks_good(copy(H))
@test QuantumClifford.stab_looks_good(copy(H), remove_redundant_rows=true)
end
end
end
64 changes: 21 additions & 43 deletions test/test_ecc_decoder_all_setups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#@show c
#@show s
#@show e
@assert max(e...) < noise/4
@test max(e...) < noise/4
end
end
end
Expand All @@ -35,31 +35,36 @@
##

@testset "belief prop decoders, good for sparse codes" begin
codes = [
# TODO
]
codes = vcat(LP04, LP118, test_gb_codes, other_lifted_product_codes)

noise = 0.001

setups = [
CommutationCheckECCSetup(noise),
NaiveSyndromeECCSetup(noise, 0),
ShorSyndromeECCSetup(noise, 0),
]
CommutationCheckECCSetup(noise),
NaiveSyndromeECCSetup(noise, 0),
ShorSyndromeECCSetup(noise, 0),
]
# lifted product codes currently trigger errors in syndrome circuits

for c in codes
for s in setups
for d in [c->PyBeliefPropOSDecoder(c, maxiter=10)]
e = evaluate_decoder(d(c), s, 100000)
@show c
@show s
@show e
@assert max(e...) < noise/4
for d in [c -> PyBeliefPropOSDecoder(c, maxiter=2)]
nsamples = 10000
if true
@test_broken false # TODO these are too slow to test in CI
continue
end
e = evaluate_decoder(d(c), s, nsamples)
# @show c
# @show s
# @show e
@test max(e...) <= noise
end
end
end
end


@testset "BitFlipDecoder decoder, good for sparse codes" begin
codes = [
QuantumReedMuller(3),
Expand All @@ -81,7 +86,7 @@
#@show c
#@show s
#@show e
@assert max(e...) < noise/4
@test max(e...) < noise/4
end
end
end
Expand Down Expand Up @@ -118,34 +123,7 @@
#@show c
#@show s
#@show e
@assert max(e...) < noise/5
end
end
end
end

@testset "belief prop decoders, good for sparse codes" begin
codes = vcat(LP04, LP118, test_gb_codes, other_lifted_product_codes)

noise = 0.001

setups = [
CommutationCheckECCSetup(noise),
NaiveSyndromeECCSetup(noise, 0),
ShorSyndromeECCSetup(noise, 0),
]
# lifted product codes currently trigger errors in syndrome circuits

for c in codes
for s in setups
for d in [c -> PyBeliefPropOSDecoder(c, maxiter=10)]
nsamples = code_n(c) > 400 ? 1000 : 100000
# take fewer samples for larger codes to save time
e = evaluate_decoder(d(c), s, nsamples)
# @show c
# @show s
# @show e
@assert max(e...) < noise / 4 (c, s, e)
@test max(e...) < noise/5
end
end
end
Expand Down

0 comments on commit 4e06c01

Please sign in to comment.