|
| 1 | +@testitem "ECC Golay" begin |
| 2 | + |
| 3 | + using LinearAlgebra |
| 4 | + using QuantumClifford.ECC |
| 5 | + using QuantumClifford.ECC: AbstractECC, Golay, generator |
| 6 | + using Nemo: matrix, GF, echelon_form |
| 7 | + |
| 8 | + # Theorem: Let `C` be a binary linear code. If `C` is self-orthogonal and |
| 9 | + # has a generator matrix `G` where each row has weight divisible by four, |
| 10 | + # then every codeword of `C` has weight divisible by four. `H₂₄` is self-dual |
| 11 | + # because its generator matrix has all rows with weight divisible by four. |
| 12 | + # Thus, all codewords of `H₂₄` must have weights divisible by four. Refer to |
| 13 | + # pg. 30 to 33 of Ch1 of Fundamentals of Error Correcting Codes by Huffman, |
| 14 | + # Cary and Pless, Vera. |
| 15 | + function code_weight_property(matrix) |
| 16 | + for row in eachrow(matrix) |
| 17 | + count = sum(row) |
| 18 | + if count % 4 == 0 |
| 19 | + return true |
| 20 | + end |
| 21 | + end |
| 22 | + return false |
| 23 | + end |
| 24 | + |
| 25 | + # Test the equivalence of punctured and extended code by verifying that puncturing |
| 26 | + # the binary parity check matrix H₂₄ in any coordinate and then extending it by adding |
| 27 | + # an overall parity check in the same position yields the original matrix H₂₄. |
| 28 | + # Steps: |
| 29 | + # 1) Puncture the Code: Remove the i-th column from H₂₄ to create a punctured matrix |
| 30 | + # H₂₃. Note: H₂₃ = H[:, [1:i-1; i+1:end]] |
| 31 | + # 2) Extend the Code: Add a column in the same position to ensure each row has even |
| 32 | + # parity. Note: H'₂₄ = [H₂₃[:, 1:i-1] c H₂₃[:, i:end]]. Here, c is a column vector |
| 33 | + # added to ensure each row in H'₂₄ has even parity. |
| 34 | + # 3) Equivalence Check: Verify that H'₂₄ = H₂₄. |
| 35 | + function puncture_code(mat, i) |
| 36 | + return mat[:, [1:i-1; i+1:end]] |
| 37 | + end |
| 38 | + |
| 39 | + function extend_code(mat, i) |
| 40 | + k, _ = size(mat) |
| 41 | + extended_mat = hcat(mat[:, 1:i-1], zeros(Bool, k, 1), mat[:, i:end]) |
| 42 | + # Calculate the parity for each row |
| 43 | + for row in 1:k |
| 44 | + row_parity = sum(mat[row, :]) % 2 == 1 |
| 45 | + extended_mat[row, i] = row_parity |
| 46 | + end |
| 47 | + return extended_mat |
| 48 | + end |
| 49 | + |
| 50 | + function minimum_distance(H) |
| 51 | + n = size(H, 2) |
| 52 | + min_dist = n + 1 |
| 53 | + for x_bits in 1:(2^n - 1) |
| 54 | + x = reverse(digits(x_bits, base=2, pad=n)) |
| 55 | + xᵀ = reshape(x, :, 1) |
| 56 | + if all(mod.(H * xᵀ, 2) .== 0) |
| 57 | + weight = sum(x) |
| 58 | + min_dist = min(min_dist, weight) |
| 59 | + end |
| 60 | + end |
| 61 | + return min_dist |
| 62 | + end |
| 63 | + |
| 64 | + @testset "Testing binary Golay codes properties" begin |
| 65 | + test_cases = [(24, 12), (23, 12)] |
| 66 | + for (n, k) in test_cases |
| 67 | + H = parity_checks(Golay(n)) |
| 68 | + mat = matrix(GF(2), parity_checks(Golay(n))) |
| 69 | + computed_rank = rank(mat) |
| 70 | + @test computed_rank == n - k |
| 71 | + end |
| 72 | + |
| 73 | + # [24, 12, 8] binary Golay code is a self-dual code [huffman2010fundamentals](@cite). |
| 74 | + H = parity_checks(Golay(24)) |
| 75 | + @test code_weight_property(H) == true |
| 76 | + @test H[:, (12 + 1):end] == H[:, (12 + 1):end]' |
| 77 | + # Example taken from [huffman2010fundamentals](@cite). |
| 78 | + @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; |
| 79 | + 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0; |
| 80 | + 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0; |
| 81 | + 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0; |
| 82 | + 1 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0; |
| 83 | + 1 1 1 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0; |
| 84 | + 1 1 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0; |
| 85 | + 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0; |
| 86 | + 1 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0; |
| 87 | + 1 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0; |
| 88 | + 1 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0; |
| 89 | + 1 0 1 1 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1] |
| 90 | + |
| 91 | + # minimum distance test |
| 92 | + # [24, 12, 8] |
| 93 | + H = parity_checks(Golay(24)) |
| 94 | + @test minimum_distance(H) == 8 |
| 95 | + # [23, 12, 7] |
| 96 | + H = parity_checks(Golay(23)) |
| 97 | + @test minimum_distance(H) == 7 |
| 98 | + |
| 99 | + # cross-verifying the canonical equivalence of bordered reverse circulant matrix (A) |
| 100 | + # from [huffman2010fundamentals](@cite) with matrix A taken from [bhatia2018mceliece](@cite). |
| 101 | + A = [1 1 0 1 1 1 0 0 0 1 0 1; |
| 102 | + 1 0 1 1 1 0 0 0 1 0 1 1; |
| 103 | + 0 1 1 1 0 0 0 1 0 1 1 1; |
| 104 | + 1 1 1 0 0 0 1 0 1 1 0 1; |
| 105 | + 1 1 0 0 0 1 0 1 1 0 1 1; |
| 106 | + 1 0 0 0 1 0 1 1 0 1 1 1; |
| 107 | + 0 0 0 1 0 1 1 0 1 1 1 1; |
| 108 | + 0 0 1 0 1 1 0 1 1 1 0 1; |
| 109 | + 0 1 0 1 1 0 1 1 1 0 0 1; |
| 110 | + 1 0 1 1 0 1 1 1 0 0 0 1; |
| 111 | + 0 1 1 0 1 1 1 0 0 0 1 1; |
| 112 | + 1 1 1 1 1 1 1 1 1 1 1 0] |
| 113 | + |
| 114 | + H = parity_checks(Golay(24)) |
| 115 | + @test echelon_form(matrix(GF(2), A)) == echelon_form(matrix(GF(2), H[1:12, 1:12])) |
| 116 | + # test self-duality for extended Golay code, G == H |
| 117 | + @test echelon_form(matrix(GF(2), generator(Golay(24)))) == echelon_form(matrix(GF(2), parity_checks(Golay(24)))) |
| 118 | + |
| 119 | + # All punctured and extended matrices are equivalent to the H₂₄. Test each column for puncturing and extending. |
| 120 | + for i in 1:24 |
| 121 | + punctured_mat = puncture_code(H, i) |
| 122 | + extended_mat = extend_code(punctured_mat, i) |
| 123 | + @test extended_mat == H |
| 124 | + end |
| 125 | + end |
| 126 | +end |
0 commit comments