diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cf751e1c2..87ae016f3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -52,6 +52,11 @@ jobs:
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
+ - name: "Inject Oscar as test dependency (if not Windows and not threaded)"
+ if: ${{ matrix.os != 'windows-latest' && matrix.threads == 1 }}
+ continue-on-error: true
+ run: |
+ sed -i -e "s/\[deps\]/[deps]\nOscar = \"f1435218-dba5-11e9-1e4d-f1a5fab5fc13\"/" test/Project.toml
- uses: julia-actions/julia-runtest@v1
env:
JULIA_NUM_THREADS: ${{ matrix.threads }}
diff --git a/Project.toml b/Project.toml
index e49f22503..052e5e394 100644
--- a/Project.toml
+++ b/Project.toml
@@ -27,6 +27,7 @@ CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
Hecke = "3e1990a7-5d81-5526-99ce-9ba3ff248f21"
LDPCDecoders = "3c486d74-64b9-4c60-8b1a-13a564e77efb"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
+Oscar = "f1435218-dba5-11e9-1e4d-f1a5fab5fc13"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PyQDecoders = "17f5de1a-9b79-4409-a58d-4d45812840f7"
Quantikz = "b0d11df0-eea3-4d79-b4a5-421488cbf74b"
@@ -37,6 +38,7 @@ QuantumCliffordGPUExt = "CUDA"
QuantumCliffordHeckeExt = "Hecke"
QuantumCliffordLDPCDecodersExt = "LDPCDecoders"
QuantumCliffordMakieExt = "Makie"
+QuantumCliffordOscarExt = ["Hecke", "Oscar"]
QuantumCliffordPlotsExt = "Plots"
QuantumCliffordPyQDecodersExt = "PyQDecoders"
QuantumCliffordQOpticsExt = "QuantumOpticsBase"
@@ -57,6 +59,7 @@ 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"
+Oscar = "1.1.1"
Plots = "1.38.0"
PrecompileTools = "1.2"
PyQDecoders = "0.2.1"
diff --git a/docs/make.jl b/docs/make.jl
index b8d13fc30..d186708e9 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -12,6 +12,11 @@ import Hecke
const QuantumCliffordHeckeExt = Base.get_extension(QuantumClifford, :QuantumCliffordHeckeExt)
+ENV["OSCAR_PRINT_BANNER"] = "false"
+import Oscar
+
+const QuantumCliffordOscarExt = Base.get_extension(QuantumClifford, :QuantumCliffordOscarExt)
+
#DocMeta.setdocmeta!(QuantumClifford, :DocTestSetup, :(using QuantumClifford); recursive=true)
ENV["LINES"] = 80 # for forcing `displaysize(io)` to be big enough
@@ -25,7 +30,7 @@ doctest = false,
clean = true,
sitename = "QuantumClifford.jl",
format = Documenter.HTML(size_threshold_ignore = ["API.md"]),
-modules = [QuantumClifford, QuantumClifford.Experimental.NoisyCircuits, QuantumClifford.ECC, QuantumInterface, QuantumCliffordHeckeExt],
+modules = [QuantumClifford, QuantumClifford.Experimental.NoisyCircuits, QuantumClifford.ECC, QuantumInterface, QuantumCliffordHeckeExt, QuantumCliffordOscarExt],
warnonly = [:missing_docs],
linkcheck = true,
authors = "Stefan Krastanov",
diff --git a/docs/src/ECC_API.md b/docs/src/ECC_API.md
index c400a2bf4..9a17afdfa 100644
--- a/docs/src/ECC_API.md
+++ b/docs/src/ECC_API.md
@@ -10,4 +10,11 @@ Private = false
```@autodocs
Modules = [QuantumCliffordHeckeExt]
Private = true
+```
+
+## Implemented in an extension requiring `Oscar.jl`
+
+```@autodocs
+Modules = [QuantumCliffordOscarExt]
+Private = true
```
\ No newline at end of file
diff --git a/docs/src/references.bib b/docs/src/references.bib
index 05276ff75..dea89e53b 100644
--- a/docs/src/references.bib
+++ b/docs/src/references.bib
@@ -561,3 +561,23 @@ @article{haah2011local
pages={042330},
year={2011},
}
+
+@inproceedings{wang2023abelian,
+ title={Abelian and non-Abelian quantum two-block codes},
+ author={Wang, Renyu and Lin, Hsiang-Ku and Pryadko, Leonid P},
+ booktitle={2023 12th International Symposium on Topics in Coding (ISTC)},
+ pages={1--5},
+ year={2023},
+ organization={IEEE}
+}
+
+@article{naghipour2015quantum,
+ title={Quantum stabilizer codes from Abelian and non-Abelian groups association schemes},
+ author={Naghipour, Avaz and Jafarizadeh, Mohammad Ali and Shahmorad, Sedaghat},
+ journal={International Journal of Quantum Information},
+ volume={13},
+ number={03},
+ pages={1550021},
+ year={2015},
+ publisher={World Scientific}
+}
diff --git a/docs/src/references.md b/docs/src/references.md
index 97c679136..993fd5989 100644
--- a/docs/src/references.md
+++ b/docs/src/references.md
@@ -45,6 +45,8 @@ For quantum code construction routines:
- [lin2024quantum](@cite)
- [bravyi2024high](@cite)
- [haah2011local](@cite)
+- [wang2023abelian](@cite)
+- [naghipour2015quantum](@cite)
For classical code construction routines:
- [muller1954application](@cite)
diff --git a/ext/QuantumCliffordOscarExt/QuantumCliffordOscarExt.jl b/ext/QuantumCliffordOscarExt/QuantumCliffordOscarExt.jl
new file mode 100644
index 000000000..1e45d958a
--- /dev/null
+++ b/ext/QuantumCliffordOscarExt/QuantumCliffordOscarExt.jl
@@ -0,0 +1,19 @@
+module QuantumCliffordOscarExt
+
+using DocStringExtensions
+
+import Nemo
+import Nemo: FqFieldElem
+import Hecke: group_algebra, GF, abelian_group, gens, quo, one, GroupAlgebra,
+ GroupAlgebraElem, direct_product, sub
+import Oscar
+import Oscar: free_group, small_group_identification, describe, order, FPGroupElem, FPGroup,
+ BasicGAPGroupElem, DirectProductGroup, cyclic_group
+
+import QuantumClifford.ECC: two_block_group_algebra_codes
+
+include("types.jl")
+include("direct_product.jl")
+include("group_presentation.jl")
+
+end # module
diff --git a/ext/QuantumCliffordOscarExt/direct_product.jl b/ext/QuantumCliffordOscarExt/direct_product.jl
new file mode 100644
index 000000000..0d5d9f334
--- /dev/null
+++ b/ext/QuantumCliffordOscarExt/direct_product.jl
@@ -0,0 +1,110 @@
+"""
+# Direct Product of Groups
+
+The direct product of groups is instrumental in constructing group algebra of two-block
+group algebra code. Lin and Pryadko illustrate this method in Appendix C, Table 2 of
+[lin2024quantum](@cite), where they utilize the direct product of two cyclic groups,
+expressed as `C₂ₘ = Cₘ × C₂`, with an order of `2m`.
+
+`Hecke.jl` contains only abelian groups and a list of all finite groups of order up to 100.
+`Oscar.jl` brings in comprehensive functionality for computational group theory, including
+support for **arbitrary finitely presented groups** (groups of the form `⟨X | S⟩`. `Oscar.jl`
+supports the **direct product** operation between two or more arbitrary **general** groups,
+including non-abelian groups such as `alternating_group`, `dihedral_group`, `symmetric_group`,
+and even arbitrary finitely presented groups (e.g., `free_group`). This capability is not
+available in `Hecke.jl`. The 2BGA codes discovered in [lin2024quantum](@cite) rely on direct
+products of two or more *general* groups, which necessitate the use of `Oscar.direct_product`.
+
+The schematic below illustrates the limitations of `Hecke.direct_product` compared to
+`Oscar.direct_product`:
+
+```@raw html
+
+graph TB
+ root[Direct Product of Groups]
+
+ root --> A[Hecke.direct_product]
+ root --> B[Oscar.direct_product]
+
+ %% Hecke Branch
+ A --> A1[Supports mostly abelian groups and list of finite groups]
+ A1--> A2[abelian_group symmetric_group small_group]
+ A2 --> A3[C × C, C × S]
+
+ %% Oscar Branch
+ B --> B1[Supports finite general groups, including non-abelian groups]
+ B1--> B2[alternating_group
dihedral_group
free_group
cyclic_group
permutation_group
quaternion_group
symmetric_group
abelian_group
small_group]
+ B2--> B3[A × C, A × D
D × C, D × D
F × F, F × A, F × D
F × S, F × C
C × C, C × S]
+
+```
+
+# Example
+
+The [[56, 28, 2]] abelian 2BGA code from Appendix C, Table II in [lin2024quantum](@cite)
+can be constructed using the direct product of two cyclic groups. Specifically, the group
+`C₂₈` of order `l = 28` can be represented as `C₁₄ × C₂`, where the first group has order
+`m = 14` and the second group has order `n = 2`.
+
+```jldoctest directprod
+julia> import Oscar: cyclic_group, small_group_identification, describe, order; # hide
+
+julia> import Hecke: gens, quo, group_algebra, GF, one, direct_product, sub; # hide
+
+julia> m = 14; n = 2;
+
+julia> C₁₄ = cyclic_group(m);
+
+julia> C₂ = cyclic_group(n);
+
+julia> G = direct_product(C₁₄, C₂);
+
+julia> GA = group_algebra(GF(2), G);
+
+julia> x, s = gens(GA)[1], gens(GA)[3];
+
+julia> a = [one(GA), x^7];
+
+julia> b = [one(GA), x^7, s, x^8, s * x^7, x];
+
+julia> c = twobga_from_direct_product(a, b, GA);
+
+julia> order(G)
+28
+
+julia> code_n(c), code_k(c)
+(56, 28)
+
+julia> describe(G), small_group_identification(G)
+("C14 x C2", (28, 4))
+```
+
+!!! note When using the direct product of two cyclic groups, it is essential to verify
+the group presentation `Cₘ = ⟨x, s | xᵐ = s² = xsx⁻¹s⁻¹ = 1⟩` is satisfied, where the
+order is `2m`. Ensure that the selected generators have the correct orders of `m = 14`
+and `n = 2`, respectively. If the group presentation is not satisfied, the resulting
+group algebra over `GF(2)` will not represent the intended group, `C₂₈ = C₁₄ × C₂`. In
+addition, `Oscar.sub` can be used to determine if `H` is a subgroup of `G` and to
+confirm that both `C₁₄` and `C₂` are subgroups of `C₂₈`.
+
+```jldoctest directprod
+julia> order(gens(G)[1])
+14
+
+julia> order(gens(G)[3])
+2
+
+julia> x^14 == s^2 == x * s * x^-1 * s^-1
+true
+
+julia> H, _ = sub(G, [gens(G)[1], gens(G)[3]]);
+
+julia> H == G
+true
+```
+"""
+function twobga_from_direct_product(a_elts::VectorDirectProductGroupElem, b_elts::VectorDirectProductGroupElem, F2G::DirectProductGroupAlgebra)
+ a = sum(F2G(x) for x in a_elts)
+ b = sum(F2G(x) for x in b_elts)
+ c = two_block_group_algebra_codes(a,b)
+ return c
+end
diff --git a/ext/QuantumCliffordOscarExt/group_presentation.jl b/ext/QuantumCliffordOscarExt/group_presentation.jl
new file mode 100644
index 000000000..b6b586650
--- /dev/null
+++ b/ext/QuantumCliffordOscarExt/group_presentation.jl
@@ -0,0 +1,151 @@
+"""
+# Specific Group Presentations
+
+For quantum error-correcting codes like the two-block group algebra (2BGA) code, designing specific
+group presentations for both abelian and non-abelian groups is crucial. These presentations are essential
+for constructing the group algebra of the 2BGA code for a given finite general group, `G`.
+
+Lin and Pryadko, in their seminal paper titled "Quantum Two-Block Group Algebra Codes" [lin2024quantum](@cite),
+employ specific presentations that necessitate the use of `Oscar.free_group`. The diagram below distinguishes
+between small groups (`Hecke/Oscar.small_group`) and finitely presented groups (`Oscar.free_group`) by highlighting
+the existence of extra relations in their presentations.
+
+```@raw html
+
+graph TD
+ A[Group Presentation ⟨S ∣ R⟩] --> B{Are there
extra relations?}
+ B -- No --> C[Small groups
Hecke/Oscar.small_group]
+ C --> D[Independent generators]
+ C --> E["Example:
⟨r, s ∣ s⁴, r⁹⟩"]
+ B -- Yes --> F[Finitely presented groups
Oscar.free_group]
+ F --> G[Defined by interactions]
+ F --> H["Example:
⟨r, s ∣ s⁴, r⁹, s⁻¹rsr⟩"]
+
+```
+
+# Example
+
+The [[96, 12, 10]] 2BGA code from Table I in [lin2024quantum](@cite) has the group presentation
+`⟨r, s | s⁶ = r⁸ = r⁻¹srs = 1⟩` and a group structure of `C₂ × (C₃ ⋉ C₈)`.
+
+```jldoctest finitegrp
+julia> import Oscar: free_group, small_group_identification, describe, order; # hide
+
+julia> import Hecke: gens, quo, group_algebra, GF, one; # hide
+
+julia> F = free_group(["r", "s"]);
+
+julia> r, s = gens(F); # generators
+
+julia> G, = quo(F, [s^6, r^8, r^(-1) * s * r * s]); # relations
+
+julia> GA = group_algebra(GF(2), G);
+
+julia> r, s = gens(G);
+
+julia> a = [one(G), r, s^3 * r^2, s^2 * r^3];
+
+julia> b = [one(G), r, s^4 * r^6, s^5 * r^3];
+
+julia> c = twobga_from_fp_group(a, b, GA);
+
+julia> order(G)
+48
+
+julia> code_n(c), code_k(c)
+(96, 12)
+
+julia> describe(G), small_group_identification(G)
+("C2 x (C3 : C8)", (48, 9))
+```
+
+# Cyclic Groups
+
+Cyclic groups with specific group presentations, given by `Cₘ = ⟨x, s | xᵐ = s² = xsx⁻¹s⁻¹ = 1⟩`,
+where the order is `2m`, are supported.
+
+To construct the group algebra for a cyclic group, specify the group presentation `⟨S | R⟩`, using
+its generators `S` and defining relations `R`.
+
+# Example
+
+The [[56, 28, 2]] abelian 2BGA code from Appendix C, Table II in [lin2024quantum](@cite) is constructed using
+the cyclic group `C₂₈ = C₁₄ × C₂`.
+
+```jldoctest finitegrp
+julia> m = 14;
+
+julia> F = free_group(["x", "s"]);
+
+julia> x, s = gens(F); # generators
+
+julia> G, = quo(F, [x^m, s^2, x * s * x^-1 * s^-1]); # relations
+
+julia> GA = group_algebra(GF(2), G);
+
+julia> x, s = gens(G);
+
+julia> a = [one(G), x^7];
+
+julia> b = [one(G), x^7, s, x^8, s * x^7, x];
+
+julia> c = twobga_from_fp_group(a, b, GA);
+
+julia> order(G)
+28
+
+julia> code_n(c), code_k(c)
+(56, 28)
+
+julia> describe(G), small_group_identification(G)
+("C14 x C2", (28, 4))
+```
+
+# Dihedral Groups
+
+Dihedral groups with specific group presentations, given by `Dₘ = ⟨r, s | rᵐ = s² = (rs)² = 1⟩`,
+where the order is `2m`, are supported.
+
+To construct the group algebra for a dihedral group, specify the group presentation `⟨S | R⟩`
+using its generators `S` and defining relations `R`.
+
+# Example
+
+The [[24, 8, 3]] 2BGA code from Appendix C, Table III in [lin2024quantum](@cite) is constructed
+using the dihedral group `D₆ = C₆ ⋉ C₂`.
+
+```jldoctest finitegrp
+julia> m = 6;
+
+julia> F = free_group(["r", "s"]);
+
+julia> r, s = gens(F); # generators
+
+julia> G, = quo(F, [r^m, s^2, (r*s)^2]); # relations
+
+julia> GA = group_algebra(GF(2), G);
+
+julia> r, s = gens(G);
+
+julia> a = [one(G), r^4];
+
+julia> b = [one(G), s*r^4, r^3, r^4, s*r^2, r];
+
+julia> c = twobga_from_fp_group(a, b, GA);
+
+julia> order(G)
+12
+
+julia> code_n(c), code_k(c)
+(24, 8)
+
+julia> describe(G), small_group_identification(G)
+("D12", (12, 4))
+```
+"""
+function twobga_from_fp_group(a_elts::VectorFPGroupElem, b_elts::VectorFPGroupElem, F2G::FqFieldFPGroupAlgebra)
+ a = sum(F2G(x) for x in a_elts)
+ b = sum(F2G(x) for x in b_elts)
+ c = two_block_group_algebra_codes(a,b)
+ return c
+end
diff --git a/ext/QuantumCliffordOscarExt/types.jl b/ext/QuantumCliffordOscarExt/types.jl
new file mode 100644
index 000000000..47b9ed77c
--- /dev/null
+++ b/ext/QuantumCliffordOscarExt/types.jl
@@ -0,0 +1,7 @@
+const VectorFPGroupElem = Vector{FPGroupElem}
+
+const VectorDirectProductGroupElem = Vector{GroupAlgebraElem{FqFieldElem, GroupAlgebra{FqFieldElem, DirectProductGroup, BasicGAPGroupElem{DirectProductGroup}}}}
+
+const FqFieldFPGroupAlgebra = GroupAlgebra{FqFieldElem, FPGroup, FPGroupElem}
+
+const DirectProductGroupAlgebra = GroupAlgebra{FqFieldElem, DirectProductGroup, BasicGAPGroupElem{DirectProductGroup}}
diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl
index 231816318..ce68c9ff8 100644
--- a/src/ecc/ECC.jl
+++ b/src/ecc/ECC.jl
@@ -29,7 +29,7 @@ export parity_checks, parity_checks_x, parity_checks_z, iscss,
Shor9, Steane7, Cleve8, Perfect5, Bitflip3,
Toric, Gottesman, Surface, Concat, CircuitCode, QuantumReedMuller,
LPCode, two_block_group_algebra_codes, generalized_bicycle_codes, bicycle_codes,
- haah_cubic_codes,
+ haah_cubic_codes, twobga_from_fp_group, twobga_from_direct_product,
random_brickwork_circuit_code, random_all_to_all_circuit_code,
evaluate_decoder,
CommutationCheckECCSetup, NaiveSyndromeECCSetup, ShorSyndromeECCSetup,
@@ -393,4 +393,10 @@ include("codes/quantumreedmuller.jl")
include("codes/classical/lifted.jl")
include("codes/lifted_product.jl")
+# group presentation
+include("codes/twobga_ext/group_presentation.jl")
+
+# direct product
+include("codes/twobga_ext/direct_product.jl")
+
end #module
diff --git a/src/ecc/codes/twobga_ext/direct_product.jl b/src/ecc/codes/twobga_ext/direct_product.jl
new file mode 100644
index 000000000..0105c63e4
--- /dev/null
+++ b/src/ecc/codes/twobga_ext/direct_product.jl
@@ -0,0 +1,11 @@
+"""
+The method `twobga_from_direct_product` constructs the direct product `G₁ × G₂ × … × Gₙ` of multiple groups, where each `Gᵢ` represents a group in the product.
+
+Implemented as a package extension with Oscar. Check the [QuantumClifford documentation](http://qc.quantumsavory.org/stable/ECC_API/) for more details on that extension."""
+function twobga_from_direct_product(args...)
+ ext = Base.get_extension(QuantumClifford, :QuantumCliffordOscarExt)
+ if isnothing(ext)
+ throw("The `twobga_from_direct_product` depends on the package `Oscar` but you have not installed or imported it yet. Immediately after you import `Oscar`, the `twobga_from_direct_product` will be available.")
+ end
+ return ext.twobga_from_direct_product(args...)
+end
diff --git a/src/ecc/codes/twobga_ext/group_presentation.jl b/src/ecc/codes/twobga_ext/group_presentation.jl
new file mode 100644
index 000000000..2b7b8e7ce
--- /dev/null
+++ b/src/ecc/codes/twobga_ext/group_presentation.jl
@@ -0,0 +1,11 @@
+"""
+The method `twobga_from_fp_group` provides functionality of forming group algebra via group presentation, extending the capabilities of 2BGA codes.
+
+Implemented as a package extension with Oscar. Check the [QuantumClifford documentation](http://qc.quantumsavory.org/stable/ECC_API/) for more details on that extension."""
+function twobga_from_fp_group(args...)
+ ext = Base.get_extension(QuantumClifford, :QuantumCliffordOscarExt)
+ if isnothing(ext)
+ throw("The `twobga_from_fp_group` depends on the package `Oscar` but you have not installed or imported it yet. Immediately after you import `Oscar`, the `twobga_from_fp_group` will be available.")
+ end
+ return ext.twobga_from_fp_group(args...)
+end
diff --git a/test/test_ecc_base.jl b/test/test_ecc_base.jl
index 7c1d12ef5..10d6cd967 100644
--- a/test/test_ecc_base.jl
+++ b/test/test_ecc_base.jl
@@ -6,7 +6,7 @@ using InteractiveUtils
import Nemo: GF
import LinearAlgebra
-import Hecke: group_algebra, abelian_group, gens
+import Hecke: group_algebra, abelian_group, gens, quo, one
# generate instances of all implemented codes to make sure nothing skips being checked
@@ -147,6 +147,85 @@ bb3 = two_block_group_algebra_codes(A,B)
test_bb_codes = [bb1, bb2, bb3]
+# Add some codes that require Oscar, hence do not work on Windows
+
+test_twobga_codes = []
+
+@static if !Sys.iswindows()
+ try
+ import Oscar: free_group, cyclic_group, direct_product
+ @info "Add group theoretic codes requiring Oscar"
+ # [[72, 8, 9]] 2BGA code taken from Table I Block 1 of [lin2024quantum](@cite)
+ F = free_group(["r"])
+ r = gens(F)[1]
+ G, = quo(F, [r^36])
+ GA = group_algebra(GF(2), G)
+ r = gens(G)[1]
+ a = [one(G), r^28]
+ b = [one(G), r, r^18, r^12, r^29, r^14]
+ t1b1 = twobga_from_fp_group(a, b, GA)
+
+ # [[54, 6, 9]] 2BGA code taken from Table I Block 3 of [lin2024quantum](@cite)
+ F = free_group(["r"])
+ r = gens(F)[1]
+ G, = quo(F, [r^27])
+ GA = group_algebra(GF(2), G)
+ r = gens(G)[1]
+ a = [one(G), r, r^3, r^7]
+ b = [one(G), r, r^12, r^19]
+ t1b3 = twobga_from_fp_group(a, b, GA)
+
+ # [[16, 4, 4]] 2BGA taken from Appendix C, Table II of [lin2024quantum](@cite)
+ F = free_group(["x", "s"])
+ x, s = gens(F)
+ G, = quo(F, [x^4, s^2, x * s * x^-1 * s^-1])
+ GA = group_algebra(GF(2), G)
+ x, s = gens(G)
+ a = [one(G), x]
+ b = [one(G), x, s, x^2, s*x, x^3]
+ tb21 = twobga_from_fp_group(a, b, GA)
+
+ # [[32, 8, 4]] 2BGA taken from Appendix C, Table II of [lin2024quantum](@cite)
+ F = free_group(["x", "s"])
+ x, s = gens(F)
+ G, = quo(F, [x^8, s^2, x * s * x^-1 * s^-1])
+ GA = group_algebra(GF(2), G)
+ x, s = gens(G)
+ a = [one(G), x^6]
+ b = [one(G), s * x^7, s * x^4, x^6, s * x^5, s * x^2]
+ tb22 = twobga_from_fp_group(a, b, GA)
+
+ # Examples of Abelian 2BGA codes constructed from the Direct Product of two cyclic groups, denoted as `C₂ₘ = Cₘ × C₂`.
+ # [[56, 8, 7]] 2BGA taken from Appendix C, Table II of [lin2024quantum](@cite)
+ m = 14; n = 2
+ C₁₄ = cyclic_group(m)
+ C₂ = cyclic_group(n)
+ G = direct_product(C₁₄, C₂)
+ GA = group_algebra(GF(2), G)
+ x, s = gens(GA)[1], gens(GA)[3]
+ a = [one(GA), x^8]
+ b = [one(GA), x^7, s, x^8, x^9, s * x^4]
+ dprod1 = twobga_from_direct_product(a, b, GA)
+
+ # [[48, 24, 2]] 2BGA taken from Appendix C, Table II of [lin2024quantum](@cite)
+ m = 12; n = 2
+ C₁₂ = cyclic_group(m)
+ C₂ = cyclic_group(n)
+ G = direct_product(C₁₂, C₂)
+ GA = group_algebra(GF(2), G)
+ x, s = gens(GA)[1], gens(GA)[4]
+ a = [one(GA), s * x^6]
+ b = [one(GA), x^3, s * x^6, x^4, s * x^9, s * x^10]
+ dprod2 = twobga_from_direct_product(a, b, GA)
+
+ append!(test_twobga_codes, [t1b1, t1b3, tb21, tb22, dprod1, dprod2])
+ catch e
+ @warn(e)
+ end
+end
+
+@info "length(test_twobga_codes): $(length(test_twobga_codes))"
+
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)],
@@ -154,7 +233,7 @@ const code_instance_args = Dict(
: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, test_bb_codes, test_mbb_codes, test_coprimeBB_codes, test_hcubic_codes, other_lifted_product_codes)),
+ :LPCode => (c -> (c.A, c.B)).(vcat(LP04, LP118, test_gb_codes, test_bb_codes, test_mbb_codes, test_coprimeBB_codes, test_hcubic_codes, test_twobga_codes, other_lifted_product_codes)),
:QuantumReedMuller => [3, 4, 5]
)