Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/downgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
version: ['1.10']
version: ['1.12']
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v3
Expand Down
12 changes: 6 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
DocStringExtensions = "0.9"
DocStringExtensions = "0.9.5"
Graphs = "1.9"
LinearAlgebra = "1.1"
LinearAlgebra = "1.10"
Multigraphs = "0.3.0"
Nemo = "0.45.5, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54"
Oscar = "1.1.1"
PrecompileTools = "1.2"
ProgressMeter = "1.11.0"
QECCore = "0.1.1"
QuantumClifford = "0.10.0"
QECCore = "0.1.3"
QuantumClifford = "0.11.3"
Random = "1.1"
SparseArrays = "1.1"
julia = "1.10"
SparseArrays = "1.10"
julia = "1.12"
225 changes: 26 additions & 199 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ pkg> add https://github.com/QuantumSavory/QuantumExpanders.jl.git

To update, just type `up` in the package mode.

The library provides the following methods to construct explicit instances of *Quantum Tanner codes*.
```mermaid
graph TD
QuantumTannerCodes["Quantum Tanner Codes"] --> RandomMethods["Random Methods"]
QuantumTannerCodes --> DeterministicMethods["Deterministic Methods"]

subgraph "Random construction"
RandomMethods --> RandomQuantumTannerCode["`random_quantum_Tanner_code`"]
end

subgraph "Deterministic construction"
DeterministicMethods --> QuantumTannerCode["`QuantumTannerCode`"]
DeterministicMethods --> GeneralizedQuantumTannerCode["`GeneralizedQuantumTannerCode`"]
end
```

- `random_quantum_Tanner_code` constructs a quantum CSS code by instantiating two classical
Tanner codes, 𝒞ᶻ and 𝒞ˣ, on the graphs 𝒢₀□ and 𝒢₁□ of a left-right Cayley complex [leverrier2022quantum](https://arxiv.org/pdf/2202.13641).
This complex is generated from a group G, and two generating sets A and B of sizes Δ_A and Δ_B, which,
Expand All @@ -55,7 +71,7 @@ QT code implementation provides a simplified variant of the Panteleev-Kalachev q
[panteleev2022asymptoticallygoodquantumlocally](https://arxiv.org/pdf/2111.03654) and is related to
the locally testable code of [dinur2022locally](https://arxiv.org/pdf/2111.04808).

Here is the novel `[[360, 61, 10]]` quantum Tanner code constructed from [Morgenstern Ramanujan graphs](https://www.sciencedirect.com/science/article/pii/S0095895684710549)
Here is the novel `[[360, 61, (3, 10)]]` quantum Tanner code constructed from [Morgenstern Ramanujan graphs](https://www.sciencedirect.com/science/article/pii/S0095895684710549)
for even prime power q.

```julia
Expand All @@ -78,17 +94,17 @@ julia> A = alternative_morgenstern_generators(B, FirstOnly())
[o+1 o+1; o 0]
[0 o+1; o o+1]

julia> rng = MersenneTwister(21);
julia> rng = MersenneTwister(892529278);

julia> hx, hz = random_quantum_Tanner_code(0.74, SL₂, A, B, rng=rng);
julia> hx, hz = random_quantum_Tanner_code(0.75, SL₂, A, B, rng=rng);
Comment on lines +97 to +99

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty weird change, a very good example of some of the weird changes in this PR.

You seem to have changed many of the broken tests with new similar but not the same tests (that now pass). But the broken tests were not actually fixed, they were replaced.

Given the state of the library, this is probably the best way forward and I have no qualms with it. But for the sake of documentation, could you explain why you have made these changes (in comments here). In particular, were the old broken tests wrong? Or were they in a format that was just difficult to reproduce? Are the new tests more "canonical" in some way, or are they also just some arbitrary tests? Why should be expect the new tests to not break in the future given that the old tests broke? Basically, I have the impression that you deleted some example codes and added new different example codes, and it feels kinda arbitrary.

Please try to be brief in your answer. Feel free to just answer whatever you perceive as the most important question above, not to answer them one-by-one. I am looking for a simple reason to trust all these numerous minute changes -- if they were necessary, why are you sure similar changes will not be needed in another year.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “broken” tests were not incorrect implementation of QuantumTannerCode that causes the tests to broke, but examples of quantum Tanner codes whose true minimum distance is poor in one basis. After the compatibility-bound changes in QuantumClifford.jl, I retested the codes more systematically and started reporting the full minimum-distance ($d_X$, $d_Z$) instead of only $d_Z$.

In most cases, I did not replace the tests: they are the same codes as before, but with separate verification of $d_X$ and $d_Z$. For some tests, including the example you pointed out, I changed the parameters to use codes with more meaningful distances (for example, ([[360,8,10,3]])) rather than keeping examples with poor distance in both bases. My goal was to keep examples that are more representative of good codes (from Morgensterns generators) and useful for documentation and regression testing.

Since I have been building much of this library almost from scratch alongside developing new methods for searching quantum Tanner codes, many of the original test instances were exploratory examples generated during that process. At the time, I had not yet systematically validated both their X- and Z-basis minimum distances, whereas these tests now do.

After retesting the codes more carefully, I removed some whose true minimum distance turned out to be 1, since I do not think such examples are especially useful as regression tests. A smaller number of tests were also removed because the HiGHS/MIP minimum-distance computation occasionally returns unstable “INFEASIBLE/UNBOUNDED” behavior on those instances, making the tests unreliable.

(length(group), length(A), length(B)) = (60, 4, 3)
length(group) * length(A) * length(B) = 720
[ Info: |V₀| = |V₁| = |G| = 60
[ Info: |E_A| = Δ|G| = 240, |E_B| = Δ|G| = 180
[ Info: |Q| = Δ²|G|/2 = 360
Hᴬ = [0 1 1 0]
Hᴮ = [1 1 0; 0 1 1]
Cᴬ = [1 0 0 0; 0 1 1 0; 0 0 0 1]
Hᴬ = [1 1 1 0]
Hᴮ = [0 1 1; 1 1 0]
Cᴬ = [1 1 0 0; 1 0 1 0; 0 0 0 1]
Cᴮ = [1 1 1]
size(Cˣ) = (3, 12)
size(Cᶻ) = (2, 12)
Expand All @@ -99,200 +115,11 @@ julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(360, 61, 10)
```

# Comparison with existing work

Our method produces quantum Tanner codes with **significantly higher rates** than those reported in recent literature (Leverrier et al., *[Small quantum Tanner codes from left–right Cayley complexes](https://arxiv.org/pdf/2512.20532)*). For example:

- `[[252, 70, 6]]` → higher rate than `[[252, 2, 20]]`
- `[[392, 96, 5]]` → higher rate than `[[396, 2, 29]]`
- `[[576, 126, 7]]` → higher rate than `[[576, 28, 24]]`

While [recent work](https://arxiv.org/pdf/2512.20532) showcases codes with high distance, our method achieves higher rates, offering a complementary approach in the quantum Tanner code design.

## Quantum Tanner codes using other Frobenius groups

Expanding upon the work of [radebold2025explicit](https://arxiv.org/pdf/2508.05095), which was confined to
[dihedral groups](https://en.wikipedia.org/wiki/Dihedral_group), we have constructed new explicit quantum Tanner
codes based on a broader class of [Frobenius groups](https://en.wikipedia.org/wiki/Frobenius_group).

### Symmetric Group

Here is the `[[150, 48, 4]]` using [symmetric group](https://en.wikipedia.org/wiki/Symmetric_group) of order 3.

```julia
julia> rng = MersenneTwister(43);

julia> G = symmetric_group(3);

julia> S = normal_cayley_subset(G);

julia> hx, hz = random_quantum_Tanner_code(0.65, G, S, S, bipartite=false, use_same_local_code=true, rng=rng);
(length(group), length(A), length(B)) = (6, 5, 5)
length(group) * length(A) * length(B) = 150
[ Info: |Q| = |G||A||B| = 150
Hᴬ = [1 1 0 1 1]
Hᴮ = [1 1 0 1 0; 0 0 0 1 0; 1 1 0 1 1]
Cᴬ = [1 1 0 0 0; 0 0 1 0 0; 1 0 0 1 0; 1 0 0 0 1]
Cᴮ = [1 1 0 0 0; 0 0 1 0 0; 1 0 0 1 0; 1 0 0 0 1]
size(Cˣ) = (16, 25)
size(Cᶻ) = (1, 25)
r1 = rank(𝒞ˣ) = 96
r2 = rank(𝒞ᶻ) = 6

julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(150, 48, 4)
```

### Permutation Group

Here is the `[[36, 16, 3]]` code based on [permutation group](https://en.wikipedia.org/wiki/Permutation_group)
of order 4 that improves upon the `[[36, 8, 3]]` parameters presented in `Table 5` of [radebold2025explicit](@cite),
achieving twice the number of logical qubits while maintaining the same distance.

```julia
julia> rng = MersenneTwister(52);

julia> x = cperm([1,2,3,4]);

julia> G = permutation_group(4, [x]);

julia> S = normal_cayley_subset(G)
3-element Vector{PermGroupElem}:
(1,2,3,4)
(1,3)(2,4)
(1,4,3,2)

julia> hx, hz = random_quantum_Tanner_code(0.65, G, S, S, bipartite=false, use_same_local_code=true, rng=rng);
(length(group), length(A), length(B)) = (4, 3, 3)
length(group) * length(A) * length(B) = 36
[ Info: |Q| = |G||A||B| = 36
Hᴬ = [1 1 1]
Hᴮ = [1 0 1]
Cᴬ = [1 1 0; 1 0 1]
Cᴮ = [1 1 0; 1 0 1]
size(Cˣ) = (4, 9)
size(Cᶻ) = (1, 9)
r1 = rank(𝒞ˣ) = 16
r2 = rank(𝒞ᶻ) = 4

julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(36, 16, 3)
```

### Cyclic Group

Here is the `[[252, 70, 6]]` using [cyclic group](https://en.wikipedia.org/wiki/Cyclic_group) of order 7.

```julia
julia> rng = MersenneTwister(54);

julia> G = cyclic_group(7);

julia> S = normal_cayley_subset(G);

julia> hx, hz = random_quantum_Tanner_code(0.7, G, S, S, bipartite=false, use_same_local_code=true, rng=rng);
(length(group), length(A), length(B)) = (7, 6, 6)
length(group) * length(A) * length(B) = 252
[ Info: |Q| = |G||A||B| = 252
Hᴬ = [1 1 1 1 1 1]
Hᴮ = [1 0 0 1 1 0; 1 1 0 1 1 0; 0 1 1 0 1 0; 1 0 1 1 1 0]
Cᴬ = [1 1 0 0 0 0; 1 0 1 0 0 0; 1 0 0 1 0 0; 1 0 0 0 1 0; 1 0 0 0 0 1]
Cᴮ = [1 1 0 0 0 0; 1 0 1 0 0 0; 1 0 0 1 0 0; 1 0 0 0 1 0; 1 0 0 0 0 1]
size(Cˣ) = (25, 36)
size(Cᶻ) = (1, 36)
r1 = rank(𝒞ˣ) = 175
r2 = rank(𝒞ᶻ) = 7

julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(252, 70, 6)
```

### Small Groups

Here is a `[[392, 96, 5]]` code using [quaternion group](https://en.wikipedia.org/wiki/Quaternion_group) of order 8.

```julia
julia> rng = MersenneTwister(42);

julia> G = small_group(8, 4);

julia> describe(G), small_group_identification(G)
("Q8", (8, 4))

julia> S = normal_cayley_subset(G);

julia> hx, hz = random_quantum_Tanner_code(0.72, G, S, S, bipartite=false, use_same_local_code=true, rng=rng);
(length(group), length(A), length(B)) = (8, 7, 7)
length(group) * length(A) * length(B) = 392
[ Info: |Q| = |G||A||B| = 392
Hᴬ = [0 1 0 1 1 1 1]
Hᴮ = [1 0 0 1 0 1 0; 0 0 1 0 0 0 1; 0 0 1 0 1 0 1; 0 0 0 1 0 0 1; 1 1 1 1 0 1 0]
Cᴬ = [1 0 0 0 0 0 0; 0 0 1 0 0 0 0; 0 1 0 1 0 0 0; 0 1 0 0 1 0 0; 0 1 0 0 0 1 0; 0 1 0 0 0 0 1]
Cᴮ = [1 0 0 0 0 0 0; 0 0 1 0 0 0 0; 0 1 0 1 0 0 0; 0 1 0 0 1 0 0; 0 1 0 0 0 1 0; 0 1 0 0 0 0 1]
size(Cˣ) = (36, 49)
size(Cᶻ) = (1, 49)
r1 = rank(𝒞ˣ) = 288
r2 = rank(𝒞ᶻ) = 8

julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> c = CSS(hx, hz);

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(392, 96, 5)
```

Here is a `[[576, 126, 7]]` code using the [direct product](https://en.wikipedia.org/wiki/Direct_product_of_groups) of two cyclic groups (**C₃ × C₃**).

```julia
julia> rng = MersenneTwister(20);

julia> G = small_group(9, 2);

julia> describe(G), small_group_identification(G)
("C3 x C3", (9, 2))

julia> S = normal_cayley_subset(G);

julia> hx, hz = random_quantum_Tanner_code(0.8, G, S, S, bipartite=false, use_same_local_code=true, rng=rng);
(length(group), length(A), length(B)) = (9, 8, 8)
length(group) * length(A) * length(B) = 576
[ Info: |Q| = |G||A||B| = 576
Hᴬ = [1 1 1 0 1 1 1 1]
Hᴮ = [0 0 0 1 0 1 1 1; 1 1 0 1 1 1 0 0; 1 0 1 0 1 1 1 1; 1 0 1 0 0 0 1 0; 1 1 1 0 1 1 1 1; 1 0 1 1 1 0 1 0]
Cᴬ = [1 1 0 0 0 0 0 0; 1 0 1 0 0 0 0 0; 0 0 0 1 0 0 0 0; 1 0 0 0 1 0 0 0; 1 0 0 0 0 1 0 0; 1 0 0 0 0 0 1 0; 1 0 0 0 0 0 0 1]
Cᴮ = [1 1 0 0 0 0 0 0; 1 0 1 0 0 0 0 0; 0 0 0 1 0 0 0 0; 1 0 0 0 1 0 0 0; 1 0 0 0 0 1 0 0; 1 0 0 0 0 0 1 0; 1 0 0 0 0 0 0 1]
size(Cˣ) = (49, 64)
size(Cᶻ) = (1, 64)
r1 = rank(𝒞ˣ) = 441
r2 = rank(𝒞ᶻ) = 9

julia> c = CSS(hx, hz);

julia> import JuMP; import HiGHS;

julia> c = CSS(hx, hz);
julia> code_n(c), code_k(c)
(360, 61)

julia> code_n(c), code_k(c), distance(c, DistanceMIPAlgorithm(solver=HiGHS, time_limit=120))
(576, 126, 7)
julia> distance(c, DistanceMIPAlgorithm(solver = name ,logical_operator_type = :Z,time_limit = 900)), distance(c, DistanceMIPAlgorithm(solver = name ,logical_operator_type = :X,time_limit = 900))
(3, 10)
```

## Support
Expand Down
Loading
Loading