diff --git a/docs/src/references.bib b/docs/src/references.bib index c9773a7de..0cb04cdab 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -428,6 +428,7 @@ @misc{goodenough2024bipartiteentanglementnoisystabilizer url={https://arxiv.org/abs/2406.02427}, } + @article{panteleev2021degenerate, title = {Degenerate {{Quantum LDPC Codes With Good Finite Length Performance}}}, author = {Panteleev, Pavel and Kalachev, Gleb}, diff --git a/src/ecc/codes/classical/golay.jl b/src/ecc/codes/classical/golay.jl index ba96b1d07..709f582dc 100644 --- a/src/ecc/codes/classical/golay.jl +++ b/src/ecc/codes/classical/golay.jl @@ -1,17 +1,22 @@ """ -The family of classical binary Golay codes were discovered by Edouard Golay in his 1949 paper [golay1949notes](@cite), where he described the binary `[23, 12, 7]` Golay code. +The family of classical binary Golay codes were discovered by Edouard Golay +in his 1949 paper [golay1949notes](@cite), where he described the binary +`[23, 12, 7]` Golay code. There are two binary Golay codes: -1. Binary `[23, 12, 7]` Golay code: The perfect code with code length `n` 23 and dimension `k` 12. Minimum distance is 7, implying it can detect or correct up to 3 errors. By puncturing in any of the coordinates of parity check Matrix `H` = `[24, 12, 8]`, we obtain a `[23, 12, 7]` Golay code. +- Binary `[23, 12, 7]` Golay code: The perfect code with code length `n = 23` +and dimension `k = 12`. By puncturing in any of the coordinates of parity check +matrix `H` = `[24, 12, 8]`, we obtain a `[23, 12, 7]` Golay code. -2. Extended Binary `[24, 12, 8]` Golay code: Obtained by adding a parity check bit to `[23, 12, 7]`. The bordered reverse circulant matrix `(A)` of `[24, 12, 8]` Golay code is self-dual, i.e., A₂₄ is same as A₂₄'. +- Extended Binary `[24, 12, 8]` Golay code: Obtained by adding a parity check bit +to `[23, 12, 7]`. The bordered reverse circulant matrix `(A)` of `[24, 12, 8]` +Golay code is self-dual, i.e., `A₂₄` is same as A₂₄'. -Parity Check Matrix `(H)`: `H` is defined as follows: `H₂₄ = [I₁₂ | A']` where `I₁₂` is the 12 x 12 identity matrix and `A` is a bordered reverse circulant matrix. - -Construction method for `A` [huffman2010fundamentals](@cite): The columns of `A` are labeled by ∞, 0, 1, 2, ..., 10. The first row contains 0 in column ∞ and 1 elsewhere. To obtain the second row, a 1 is placed in column ∞ and a 1 is placed in columns 0, 1, 3, 4, 5, and 9; these numbers are precisely the squares of the integers modulo 11. That is, 0² = 0, 1² ≡ 10² ≡ 1 (mod 11), 2² ≡ 2² ≡ 4 (mod 11), etc. The third row of `A` is obtained by putting a 1 in column ∞ and then shifting the components in the second row one place to the left and wrapping the entry in column 0 around to column 10. The fourth row is obtained from the third in the same manner, as are the remaining rows. - -Puncturing and then extending any column in​ with an overall parity check `H₂₃` reconstructs the original parity check matrix `H₂₄`. Thus, all punctured codes are equivalent. +The parity check matrix is defined as follows: `H₂₄ = [I₁₂ | A']` where `I₁₂` is the +12 x 12 identity matrix and `A` is a bordered reverse circulant matrix. Puncturing +and then extending any column in​ with an overall parity check `H₂₃` reconstructs +the original parity check matrix `H₂₄`. Thus, all punctured codes are equivalent. The ECC Zoo has an [entry for this family](https://errorcorrectionzoo.org/c/golay). """ @@ -52,33 +57,35 @@ end function generator(g::Golay) if g.n == 24 A₂₄ = _create_A₂₄_golay(24) - I₁₂ = Diagonal(ones(Int, g.n ÷ 2)) + I₁₂ = LinearAlgebra.Diagonal(ones(Int, g.n ÷ 2)) G₂₄ = hcat(I₁₂, (A₂₄)') return G₂₄ else A₂₄ = _create_A₂₄_golay(24) A₂₃ = A₂₄[:, 1:end - 1] - I₁₂ = Diagonal(ones(Int, g.n ÷ 2)) + I₁₂ = LinearAlgebra.Diagonal(ones(Int, g.n ÷ 2)) G₂₃ = hcat(I₁₂, (A₂₃)') return G₂₃ end end function parity_checks(g::Golay) - if g.n == 24 + if g.n == 24 A₂₄ = _create_A₂₄_golay(24) - I₁₂ = Diagonal(ones(Int, g.n ÷ 2)) + I₁₂ = LinearAlgebra.Diagonal(ones(Int, g.n ÷ 2)) H₂₄ = hcat((A₂₄)', I₁₂) return H₂₄ else A₂₄ = _create_A₂₄_golay(24) A₂₃ = A₂₄[:, 1:end - 1] - I₁₂ = Diagonal(ones(Int, g.n ÷ 2)) + I₁₂ = LinearAlgebra.Diagonal(ones(Int, g.n ÷ 2)) H₂₃ = hcat((A₂₃)', I₁₂) return H₂₃ end end code_n(g::Golay) = g.n + code_k(g::Golay) = 12 + distance(g::Golay) = code_n(g::Golay) - code_k(g::Golay) - 4 diff --git a/test/test_ecc_golay.jl b/test/test_ecc_golay.jl index 129f03e48..2d89809f8 100644 --- a/test/test_ecc_golay.jl +++ b/test/test_ecc_golay.jl @@ -1,99 +1,103 @@ -using Test -using LinearAlgebra -using QuantumClifford -using QuantumClifford.ECC -using QuantumClifford.ECC: AbstractECC, Golay, generator -using Nemo: matrix, GF, echelon_form +@testitem "ECC Golay" begin -""" -- Theorem: Let `C` be a binary linear code. If `C` is self-orthogonal and has a generator matrix `G` where each row has weight divisible by four, then every codeword of `C` has weight -divisible by four. -- `H₂₄` is self-dual because its generator matrix has all rows with weight divisible by four. By above theorem, all codewords of `H₂₄` must have weights divisible by four. Refer to pg. 30 to 33 -of Ch1 of Fundamentals of Error Correcting Codes by Huffman, Cary and Pless, Vera. -""" -function code_weight_property(matrix) - for row in eachrow(matrix) - count = sum(row) - if count % 4 == 0 - return true + using LinearAlgebra + using Combinatorics + using QuantumClifford.ECC + using QuantumClifford.ECC: AbstractECC, Golay, generator + using Nemo: matrix, GF, echelon_form + + # Theorem: Let `C` be a binary linear code. If `C` is self-orthogonal and + # has a generator matrix `G` where each row has weight divisible by four, + # then every codeword of `C` has weight divisible by four. `H₂₄` is self-dual + # because its generator matrix has all rows with weight divisible by four.Thus, + # all codewords of `H₂₄` must have weights divisible by four. Refer to pg. 30 to + # 33 of Ch1 of Fundamentals of Error Correcting Codes by Huffman, Cary and Pless, Vera. + function code_weight_property(matrix) + for row in eachrow(matrix) + count = sum(row) + if count % 4 == 0 + return true + end end + return false end - return false -end -""" -Test the equivalence of punctured and extended code by verifying that puncturing the binary parity check matrix H₂₄ in any coordinate and then extending it by -adding an overall parity check in the same position yields the original matrix H₂₄. - -Steps: -1. Puncture the Code: Remove the i-th column from H₂₄ to create a punctured matrix H₂₃. Note: H₂₃ = H[:, [1:i-1; i+1:end]] -2. Extend the Code: Add a column in the same position to ensure each row has even parity. Note: H'₂₄ = [H₂₃[:, 1:i-1] c H₂₃[:, i:end]]. Here, c is a column vector added to ensure -each row in H'₂₄ has even parity. -3. Equivalence Check: Verify that H'₂₄ = H₂₄. -""" -function puncture_code(mat, i) - return mat[:, [1:i-1; i+1:end]] -end - -function extend_code(mat, i) - k, _ = size(mat) - extended_mat = hcat(mat[:, 1:i-1], zeros(Bool, k, 1), mat[:, i:end]) - # Calculate the parity for each row - for row in 1:k - row_parity = sum(mat[row, :]) % 2 == 1 - extended_mat[row, i] = row_parity + # Test the equivalence of punctured and extended code by verifying that puncturing + # the binary parity check matrix H₂₄ in any coordinate and then extending it by adding + # an overall parity check in the same position yields the original matrix H₂₄. + # Steps: + # 1. Puncture the Code: Remove the i-th column from H₂₄ to create a punctured matrix + # H₂₃. Note: H₂₃ = H[:, [1:i-1; i+1:end]] + # 2. Extend the Code: Add a column in the same position to ensure each row has even + # parity. Note: H'₂₄ = [H₂₃[:, 1:i-1] c H₂₃[:, i:end]]. Here, c is a column vector added + # to ensure each row in H'₂₄ has even parity. + # 3. Equivalence Check: Verify that H'₂₄ = H₂₄. + function puncture_code(mat, i) + return mat[:, [1:i-1; i+1:end]] end - return extended_mat -end -@testset "Testing binary Golay codes properties" begin - test_cases = [(24, 12), (23, 12)] - for (n, k) in test_cases - H = parity_checks(Golay(n)) - mat = matrix(GF(2), parity_checks(Golay(n))) - computed_rank = rank(mat) - @test computed_rank == n - k + function extend_code(mat, i) + k, _ = size(mat) + extended_mat = hcat(mat[:, 1:i-1], zeros(Bool, k, 1), mat[:, i:end]) + # Calculate the parity for each row + for row in 1:k + row_parity = sum(mat[row, :]) % 2 == 1 + extended_mat[row, i] = row_parity + end + return extended_mat end - # [[24, 12, 8]] binary Golay code is a self-dual code [huffman2010fundamentals](@cite). - H = parity_checks(Golay(24)) - @test code_weight_property(H) == true - @test H[:, (12 + 1):end] == H[:, (12 + 1):end]' - # Example taken from [huffman2010fundamentals](@cite). - @test parity_checks(Golay(24)) == [0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0; - 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0; - 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0; - 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0; - 1 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0; - 1 1 1 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0; - 1 1 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0; - 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0; - 1 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0; - 1 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0; - 1 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0; - 1 0 1 1 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1] - # cross-verifying the canonical equivalence of bordered reverse circulant matrix (A) from [huffman2010fundamentals](@cite) with matrix A taken from [bhatia2018mceliece](@cite). - A = [1 1 0 1 1 1 0 0 0 1 0 1; - 1 0 1 1 1 0 0 0 1 0 1 1; - 0 1 1 1 0 0 0 1 0 1 1 1; - 1 1 1 0 0 0 1 0 1 1 0 1; - 1 1 0 0 0 1 0 1 1 0 1 1; - 1 0 0 0 1 0 1 1 0 1 1 1; - 0 0 0 1 0 1 1 0 1 1 1 1; - 0 0 1 0 1 1 0 1 1 1 0 1; - 0 1 0 1 1 0 1 1 1 0 0 1; - 1 0 1 1 0 1 1 1 0 0 0 1; - 0 1 1 0 1 1 1 0 0 0 1 1; - 1 1 1 1 1 1 1 1 1 1 1 0] - @test echelon_form(matrix(GF(2), A)) == echelon_form(matrix(GF(2), H[1:12, 1:12])) - # test self-duality for extended Golay code, G == H - @test echelon_form(matrix(GF(2), generator(Golay(24)))) == echelon_form(matrix(GF(2), parity_checks(Golay(24)))) - # All punctured and extended matrices are equivalent to the H₂₄. - # Test each column for puncturing and extending - for i in 1:24 - punctured_mat = puncture_code(H, i) - extended_mat = extend_code(punctured_mat, i) - # Test equivalence - @test extended_mat == H + @testset "Testing binary Golay codes properties" begin + test_cases = [(24, 12), (23, 12)] + for (n, k) in test_cases + H = parity_checks(Golay(n)) + mat = matrix(GF(2), parity_checks(Golay(n))) + computed_rank = rank(mat) + @test computed_rank == n - k + end + + # [24, 12, 8] binary Golay code is a self-dual code [huffman2010fundamentals](@cite). + H = parity_checks(Golay(24)) + @test code_weight_property(H) == true + @test H[:, (12 + 1):end] == H[:, (12 + 1):end]' + # Example taken from [huffman2010fundamentals](@cite). + @test parity_checks(Golay(24)) == [0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0; + 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0; + 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0; + 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0; + 1 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0; + 1 1 1 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0; + 1 1 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0; + 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0; + 1 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0; + 1 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0; + 1 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0; + 1 0 1 1 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1] + + # cross-verifying the canonical equivalence of bordered reverse circulant matrix (A) + # from [huffman2010fundamentals](@cite) with matrix A taken from [bhatia2018mceliece](@cite). + A = [1 1 0 1 1 1 0 0 0 1 0 1; + 1 0 1 1 1 0 0 0 1 0 1 1; + 0 1 1 1 0 0 0 1 0 1 1 1; + 1 1 1 0 0 0 1 0 1 1 0 1; + 1 1 0 0 0 1 0 1 1 0 1 1; + 1 0 0 0 1 0 1 1 0 1 1 1; + 0 0 0 1 0 1 1 0 1 1 1 1; + 0 0 1 0 1 1 0 1 1 1 0 1; + 0 1 0 1 1 0 1 1 1 0 0 1; + 1 0 1 1 0 1 1 1 0 0 0 1; + 0 1 1 0 1 1 1 0 0 0 1 1; + 1 1 1 1 1 1 1 1 1 1 1 0] + + @test echelon_form(matrix(GF(2), A)) == echelon_form(matrix(GF(2), H[1:12, 1:12])) + # test self-duality for extended Golay code, G == H + @test echelon_form(matrix(GF(2), generator(Golay(24)))) == echelon_form(matrix(GF(2), parity_checks(Golay(24)))) + + # All punctured and extended matrices are equivalent to the H₂₄. Test each column for puncturing and extending. + for i in 1:24 + punctured_mat = puncture_code(H, i) + extended_mat = extend_code(punctured_mat, i) + @test extended_mat == H + end end end diff --git a/test/test_ecc_throws.jl b/test/test_ecc_throws.jl index 373e3e664..1669669a0 100644 --- a/test/test_ecc_throws.jl +++ b/test/test_ecc_throws.jl @@ -1,4 +1,5 @@ @testitem "ECC throws" begin + using QuantumClifford.ECC: ReedMuller, BCH, RecursiveReedMuller, Golay @test_throws ArgumentError ReedMuller(-1, 3)