diff --git a/Project.toml b/Project.toml index d8f9e83e4..27def9410 100644 --- a/Project.toml +++ b/Project.toml @@ -48,7 +48,7 @@ Combinatorics = "1.0" DataStructures = "0.18" DocStringExtensions = "0.9" Graphs = "1.9" -Hecke = "0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34" +Hecke = "0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.34.3" HostCPUFeatures = "0.1.6" ILog2 = "0.2.3" InteractiveUtils = "1.9" @@ -56,7 +56,7 @@ LDPCDecoders = "0.3.1" LinearAlgebra = "1.9" MacroTools = "0.5.9" Makie = "0.20, 0.21" -Nemo = "0.42.1, 0.43, 0.44, 0.45, 0.46, 0.47" +Nemo = "0.42.1, 0.43, 0.44, 0.45, 0.46, 0.47, 0.47.1" Plots = "1.38.0" PrecompileTools = "1.2" PyQDecoders = "0.2.1" diff --git a/docs/src/references.bib b/docs/src/references.bib index 29500a034..5e96c6283 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -487,3 +487,28 @@ @article{anderson2014fault year={2014}, publisher={APS} } + +@article{bravyi2024high, + title={High-threshold and low-overhead fault-tolerant quantum memory}, + author={Bravyi, Sergey and Cross, Andrew W and Gambetta, Jay M and Maslov, Dmitri and Rall, Patrick and Yoder, Theodore J}, + journal={Nature}, + volume={627}, + number={8005}, + pages={778--782}, + year={2024}, + publisher={Nature Publishing Group UK London} +} + +@article{berthusen2024toward, + title={Toward a 2D local implementation of quantum LDPC codes}, + author={Berthusen, Noah and Devulapalli, Dhruv and Schoute, Eddie and Childs, Andrew M and Gullans, Michael J and Gorshkov, Alexey V and Gottesman, Daniel}, + journal={arXiv preprint arXiv:2404.17676}, + year={2024} + } + +@article{wang2024coprime, + title={Coprime Bivariate Bicycle Codes and their Properties}, + author={Wang, Ming and Mueller, Frank}, + journal={arXiv preprint arXiv:2408.10001}, + year={2024} +} diff --git a/docs/src/references.md b/docs/src/references.md index 35e944a21..4aaa895a8 100644 --- a/docs/src/references.md +++ b/docs/src/references.md @@ -40,6 +40,9 @@ For quantum code construction routines: - [steane1999quantum](@cite) - [campbell2012magic](@cite) - [anderson2014fault](@cite) +- [bravyi2024high](@cite) +- [berthusen2024toward](@cite) +- [wang2024coprime](@cite) For classical code construction routines: - [muller1954application](@cite) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index cdda7742e..ef27afff4 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -23,6 +23,7 @@ export parity_checks, parity_checks_x, parity_checks_z, iscss, Toric, Gottesman, Surface, Concat, CircuitCode, QuantumReedMuller, LPCode, two_block_group_algebra_codes, generalized_bicycle_codes, bicycle_codes, random_brickwork_circuit_code, random_all_to_all_circuit_code, + circulant_bivariate_bicycle, evaluate_decoder, CommutationCheckECCSetup, NaiveSyndromeECCSetup, ShorSyndromeECCSetup, TableDecoder, @@ -384,5 +385,6 @@ include("codes/quantumreedmuller.jl") # qLDPC include("codes/classical/lifted.jl") include("codes/lifted_product.jl") +include("codes/circulant_bivariate_bicycle.jl") end #module diff --git a/src/ecc/codes/circulant_bivariate_bicycle.jl b/src/ecc/codes/circulant_bivariate_bicycle.jl new file mode 100644 index 000000000..e048043a6 --- /dev/null +++ b/src/ecc/codes/circulant_bivariate_bicycle.jl @@ -0,0 +1,49 @@ +""" +A bivariate bicycle (BB) quantum LDPC code was introduced by Bravyi et al. in their 2024 paper [bravyi2024high](@cite). This code uses identity and cyclic shift matrices. Define `Iₗ` as the `l × l` identity matrix and `Sₗ` as the cyclic shift matrix of the same size, where each row of `Sₗ` has a single '1' at the column `(i + 1) mod l`. + +The matrices `x = Sₗ ⊗ Iₘ` and `y = Iₗ ⊗ Sₘ` are used. The BB code is represented by matrices `A` and `B`, defined as: `A = A₁ + A₂ + A₃` and `B = B₁ + B₂ + B₃`. The addition and multiplication operations on binary matrices are performed modulo 2. The check matrices are: `Hx = [A|B]` and `Hz = [B'|A']`. Both `Hx` and `Hz` are `(n/2)×n` matrices. + +The ECC Zoo has an [entry for this family](https://errorcorrectionzoo.org/c/qcga). +""" +struct circulant_bivariate_bicycle <: AbstractECC + l::Int + m::Int + A::Vector{Int} + B::Vector{Int} + function circulant_bivariate_bicycle(l,m,A,B) + (l >= 0 && m >= 0) || error("l and m must be non-negative") + (length(A) == 3 && length(B) == 3) || error("A and B must each have exactly 3 entries") + (all(x -> x >= 0, A) && all(x -> x >= 0, B)) || error("A and B must contain only non-negative integers") + (all(x -> x in 0:max(l, m), A) && all(x -> x in 0:max(l, m), B)) || error("Each element in A and B must be in the range [0, $(max(l, m))].") + new(l,m,A,B) + end +end + +function iscss(::Type{circulant_bivariate_bicycle}) + return true +end + +function parity_checks(c::circulant_bivariate_bicycle) + a₁,a₂,a₃ = c.A[1],c.A[2],c.A[3] + b₁,b₂,b₃ = c.B[1],c.B[2],c.B[3] + Iₗ = Matrix{Bool}(LinearAlgebra.I,c.l,c.l) + Iₘ = Matrix{Bool}(LinearAlgebra.I,c.m,c.m) + x = Dict{Bool, Matrix{Bool}}() + y = Dict{Bool, Matrix{Bool}}() + x = Dict(i => kron(circshift(Iₗ,(0,i)),Iₘ) for i in 0:(c.l)) + y = Dict(i => kron(Iₗ,circshift(Iₘ,(0,i))) for i in 0:(c.m)) + A = mod.(x[a₁]+y[a₂]+y[a₃],2) + B = mod.(y[b₁]+x[b₂]+x[b₃],2) + Hx = hcat(A,B) + Hz = hcat(B',A') + H = CSS(Hx,Hz) + Stabilizer(H) +end + +code_n(c::circulant_bivariate_bicycle) = 2*c.l*c.m + +code_k(c::circulant_bivariate_bicycle) = code_n(c) - LinearAlgebra.rank(matrix(GF(2), parity_checks_x(c))) - LinearAlgebra.rank(matrix(GF(2), parity_checks_z(c))) + +parity_checks_x(c::circulant_bivariate_bicycle) = stab_to_gf2(parity_checks(circulant_bivariate_bicycle(c.l, c.m, c.A, c.B)))[1:end÷2,:] + +parity_checks_z(c::circulant_bivariate_bicycle) = stab_to_gf2(parity_checks(circulant_bivariate_bicycle(c.l, c.m, c.A, c.B)))[end÷2+1:end,:] diff --git a/test/Project.toml b/test/Project.toml index cd15ae310..098184ba7 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,6 +5,7 @@ ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +GAP = "c863536a-3901-11e9-33e7-d5cd0df7b904" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" Hecke = "3e1990a7-5d81-5526-99ce-9ba3ff248f21" @@ -16,12 +17,15 @@ LDPCDecoders = "3c486d74-64b9-4c60-8b1a-13a564e77efb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a" +Oscar = "f1435218-dba5-11e9-1e4d-f1a5fab5fc13" +Polymake = "d720cf60-89b5-51f5-aff5-213f193123e7" PyQDecoders = "17f5de1a-9b79-4409-a58d-4d45812840f7" Quantikz = "b0d11df0-eea3-4d79-b4a5-421488cbf74b" QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5" QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +Singular = "bcd08a7b-43d2-5ff7-b6d4-c458787f915c" SIMD = "fdea26ae-647d-5447-a871-4b548cad5224" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" diff --git a/test/test_ecc_base.jl b/test/test_ecc_base.jl index f087ea627..ba0b81710 100644 --- a/test/test_ecc_base.jl +++ b/test/test_ecc_base.jl @@ -64,7 +64,8 @@ const code_instance_args = Dict( :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] + :QuantumReedMuller => [3, 4, 5], + :circulant_bivariate_bicycle => [(9,6,[3,1,2],[3,1,2]),(15,3,[9,1,2],[0,2,7]),(6,6,[3,1,2],[3,1,2]),(14,7,[6,5,6],[0,4,13]),(15,5,[5,2,3],[2,7,6])] ) function all_testablable_code_instances(;maxn=nothing) diff --git a/test/test_ecc_circulantbivariatebicycle.jl b/test/test_ecc_circulantbivariatebicycle.jl new file mode 100644 index 000000000..36c599ee6 --- /dev/null +++ b/test/test_ecc_circulantbivariatebicycle.jl @@ -0,0 +1,53 @@ +@testitem "ECC circulant_bivariate_bicycle" begin + using Test + using QuantumClifford: stab_to_gf2 + using QuantumClifford.ECC + using Nemo: nullspace, GF, matrix + using Oscar: hom, free_module, kernel, domain, map, gens + using QuantumClifford.ECC: AbstractECC, circulant_bivariate_bicycle, parity_checks_x, parity_checks_z + + # According to Lemma 1 from [bravyi2024high](@cite), k = 2·dim(ker(A)∩ker(B)). + function _formula_k(stab) + Hx = parity_checks_x(stab) + n = size(Hx,2)÷2 + A = matrix(GF(2), Hx[:,1:n]) + B = matrix(GF(2), Hx[:,n+1:end]) + k = GF(2) + hA = hom(free_module(k, size(A, 1)), free_module(k, size(A, 2)), A) + hB = hom(free_module(k, size(B, 1)), free_module(k, size(B, 2)), B) + ans = kernel(hA)[1] ∩ kernel(hB)[1] + k = 2*size(map(domain(hA), gens(ans[1])), 1) + return k + end + + @testset "Verify number of logical qubits `k` from Table 3: bravyi2024high" begin + # Refer to [bravyi2024high](@cite) for code constructions + @test code_k(circulant_bivariate_bicycle(9 , 6 , [3 , 1 , 2] , [3 , 1 , 2])) == 8 == _formula_k(circulant_bivariate_bicycle(9 , 6 , [3 , 1 , 2] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(15, 3 , [9 , 1 , 2] , [0 , 2 , 7])) == 8 == _formula_k(circulant_bivariate_bicycle(15, 3 , [9 , 1 , 2] , [0 , 2 , 7])) + @test code_k(circulant_bivariate_bicycle(12, 12, [3 , 2 , 7] , [3 , 1 , 2])) == 12 == _formula_k(circulant_bivariate_bicycle(12, 12, [3 , 2 , 7] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(12, 6 , [3 , 1 , 2] , [3 , 1 , 2])) == 12 == _formula_k(circulant_bivariate_bicycle(12, 6 , [3 , 1 , 2] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(6 , 6 , [3 , 1 , 2] , [3 , 1 , 2])) == 12 == _formula_k(circulant_bivariate_bicycle(6 , 6 , [3 , 1 , 2] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(30, 6 , [9 , 1 , 2] , [3 , 25, 26])) == 12 == _formula_k(circulant_bivariate_bicycle(30, 6 , [9 , 1 , 2] , [3 , 25, 26])) + @test code_k(circulant_bivariate_bicycle(21, 18, [3 , 10, 17], [5 , 3 , 19])) == 16 == _formula_k(circulant_bivariate_bicycle(21, 18, [3 , 10, 17], [5 , 3 , 19])) + @test code_k(circulant_bivariate_bicycle(28, 14, [26, 6 , 8] , [7 , 9 , 20])) == 24 == _formula_k(circulant_bivariate_bicycle(28, 14, [26, 6 , 8] , [7 , 9 , 20])) + end + + @testset "Verify number of logical qubits `k` from Table 1: berthusen2024toward" begin + # Refer to [berthusen2024toward](@cite) for code constructions + @test code_k(circulant_bivariate_bicycle(12, 3 , [9 , 1 , 2] , [0 , 1 , 11])) == 8 == _formula_k(circulant_bivariate_bicycle(12, 3 , [9 , 1 , 2] , [0 , 1 ,11])) + @test code_k(circulant_bivariate_bicycle(9 , 5 , [8 , 4 , 1] , [5 , 8 , 7])) == 8 == _formula_k(circulant_bivariate_bicycle(9 , 5 , [8 , 4 , 1], [5 , 8 , 7])) + @test code_k(circulant_bivariate_bicycle(12, 5 , [10, 4 , 1] , [0 , 1 , 2])) == 8 == _formula_k(circulant_bivariate_bicycle(12, 5 , [10, 4 , 1] , [0 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(15, 5 , [5 , 2 , 3] , [2 , 7 , 6])) == 8 == _formula_k(circulant_bivariate_bicycle(15, 5 , [5 , 2 , 3] , [2 , 7 , 6])) + @test code_k(circulant_bivariate_bicycle(14, 7 , [6 , 5 , 6] , [0 , 4, 13])) == 12 == _formula_k(circulant_bivariate_bicycle(14, 7 , [6 , 5 , 6] , [0 , 4, 13])) + end + + @testset "Verify number of logical qubits `k` from Table 1: wang2024coprime" begin + # # Refer to [wang2024coprime](@cite) for code constructions + @test code_k(circulant_bivariate_bicycle(3, 9 , [0 , 2 , 4] , [3 , 1 , 2])) == 8 == _formula_k(circulant_bivariate_bicycle(3, 9 , [0 , 2 , 4] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(7, 7 , [3 , 5 , 6] , [2 , 3 , 5])) == 6 == _formula_k(circulant_bivariate_bicycle(7, 7 , [3 , 5 , 6] , [2 , 3 , 5])) + @test code_k(circulant_bivariate_bicycle(3, 21 , [0 , 2 ,10] , [3 , 1 , 2])) == 8 == _formula_k(circulant_bivariate_bicycle(3, 21 , [0 , 2 ,10] , [3 , 1 , 2])) + @test code_k(circulant_bivariate_bicycle(5, 15 , [0 , 6 , 8] , [5 , 1 , 4])) == 16 == _formula_k(circulant_bivariate_bicycle(5, 15 , [0 , 6 , 8] , [5 , 1 , 4])) + @test code_k(circulant_bivariate_bicycle(3, 27 , [0 , 10,14] , [12, 1 , 2])) == 8 == _formula_k(circulant_bivariate_bicycle(3, 27 , [0 , 10,14] , [12, 1 , 2])) + @test code_k(circulant_bivariate_bicycle(6, 15 , [3 , 1 , 2] , [6 , 4 , 5])) == 8 == _formula_k(circulant_bivariate_bicycle(6, 15 , [3 , 1 , 2] , [6 , 4 , 5])) + end +end