From 60b9796c8eb79be9dc5566ce82f3662f76618cee Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sun, 31 Aug 2025 18:14:30 -0400 Subject: [PATCH] correctness tests for classical ECCs --- test/test_cecc.jl | 27 ++++++++++++++++ test/test_cecc_base.jl | 73 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 test/test_cecc.jl create mode 100644 test/test_cecc_base.jl diff --git a/test/test_cecc.jl b/test/test_cecc.jl new file mode 100644 index 000000000..a86d2a5b6 --- /dev/null +++ b/test/test_cecc.jl @@ -0,0 +1,27 @@ +@testitem "ECC" tags=[:ecc] begin + using Nemo + using QuantumClifford.ECC + using QuantumClifford.ECC: AbstractCECC, QECCore + + include("test_cecc_base.jl") + + codes = all_testable_classical_code_instances() + + @testset "correctness checks for classical ECCs" begin + for code in codes + H = parity_matrix(code) + n, k, s = code_n(code), code_k(code), size(H, 1) + @test all(col -> any(H[:, col] .!= 0), 1:n) + @test size(H, 2) == n + @test size(H, 1) <= size(H, 2) + H = matrix(GF(2), H) + computed_rank = rank(H) + @test n - k == computed_rank + @test computed_rank <= s && computed_rank <= n + @test n > 0 && k > 0 + @test k <= n + rate = k/n + @test 0 < rate <= 1.0 + end + end +end diff --git a/test/test_cecc_base.jl b/test/test_cecc_base.jl new file mode 100644 index 000000000..a943a309a --- /dev/null +++ b/test/test_cecc_base.jl @@ -0,0 +1,73 @@ +using Test +using QuantumClifford.ECC.QECCore +using QuantumClifford +using QuantumClifford.ECC +using QuantumClifford.ECC: BCH +using InteractiveUtils +using SparseArrays + +import Nemo: GF, matrix, rank, finite_field, polynomial_ring +import LinearAlgebra + +# generate instances of all implemented codes to make sure nothing skips being checked + +# Goppa Codes +m₁ = 4 +t₁ = 2 +F, α = finite_field(2, m₁, :α) +R, x = polynomial_ring(F, :x) +g₁ = x^2 + x + α^3 +L₁ = [α^i for i in 2:13] + +m₂ = 6 +F, α = finite_field(2, m₂, :α) +R, x = polynomial_ring(F, :x) +t₂ = 9 +g₂ = x^t₂ + 1 + +m₃ = 3 +F, α = finite_field(2, m₃, :α) +R, x = polynomial_ring(F, :x) +t₃ = 2 +g₃ = x^t₃ + x + 1 + +m₄ = 4 +t₄ = 2 +F, α = finite_field(2, m₄, :α) +R, z = polynomial_ring(F, :z) +g₄ = z^2 + α^7*z + 1 +L₄ = [α^i for i in 2:13] + +const classical_code_instance_args = Dict( + :RepCode => [3, 4, 5, 6, 7, 8, 9, 10], + :BCH => [(3, 1), (3, 2), (4, 1), (4, 1), (5, 1), (5, 2), (6, 1), (6, 2)], + :ReedMuller => [(1, 3), (1, 4), (2, 3), (1, 5), (2, 4), (2, 5), (3, 5)], + :RecursiveReedMuller => [(1, 3), (1, 4), (2, 3), (1, 5), (2, 4), (2, 5), (3, 5)], + :Golay => [(23), (24)], + :Hamming => [2, 3, 4, 5, 6, 7, 8], + :GallagerLDPC => [(3, 3, 4), (3, 4, 5), (4, 5, 7), (4, 6, 7)], + :GoppaCode => [(m₁, t₁, g₁, L₁), (m₂, t₂, g₂), (m₃, t₃, g₃), (m₄, t₄, g₄, L₄)] +) + +function all_testable_classical_code_instances(;maxn=nothing) + types = subtypes(AbstractCECC) + concrete = Type[] + while !isempty(types) + t = popfirst!(types) + isabstracttype(t) ? append!(types, subtypes(t)) : push!(concrete, t) + end + codeinstances = [] + for t in concrete + t_name = Symbol(split(string(t), ".")[end]) + for args in get(classical_code_instance_args, t_name, []) + try + codeinstance = t(args...) + !isnothing(maxn) && code_n(codeinstance) > maxn && continue + push!(codeinstances, codeinstance) + catch e + @warn "Failed to create $t with args $args: $e" + end + end + end + return codeinstances +end