Skip to content

Commit 59e399d

Browse files
royessKrastanov
andauthored
add random Clifford circuit codes (#298)
--------- Co-authored-by: Stefan Krastanov <[email protected]>
1 parent dddaedb commit 59e399d

10 files changed

+184
-15
lines changed

CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55

66
# News
77

8+
## v0.9.5 - 2024-07-04
9+
10+
- Implementation of random all-to-all and brickwork Clifford circuits and corresponding ECC codes.
11+
812
## v0.9.4 - 2024-06-28
913

10-
- Addition of a constructor for concatenated quantum codes -- `Concat`.
14+
- Addition of a constructor for concatenated quantum codes `Concat`.
1115
- Addition of multiple unexported classical code constructors.
12-
- Failed compactification of gates now only raises a warning instead of throwing an error. Defaults to slower non-compactified gates.
1316
- Gate errors are now conveniently supported by the various ECC benchmark setups in the `ECC` module.
14-
- Remove printing of spurious debug info from the PyBP decoder.
1517
- Significant improvements to the low-level circuit compiler (the sumtype compactifier), leading to faster Pauli frame simulation of noisy circuits.
1618
- Bump `QuantumOpticsBase.jl` package extension compat bound.
19+
- **(fix)** Remove printing of spurious debug info from the PyBP decoder.
20+
- **(fix)** Failed compactification of gates now only raises a warning instead of throwing an error. Defaults to slower non-compactified gates.
1721

1822
## v0.9.3 - 2024-04-10
1923

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "QuantumClifford"
22
uuid = "0525e862-1e90-11e9-3e4d-1b39d7109de1"
33
authors = ["Stefan Krastanov <[email protected]> and QuantumSavory community members"]
4-
version = "0.9.4"
4+
version = "0.9.5"
55

66
[deps]
77
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"

docs/src/references.bib

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,17 @@ @article{grassl2002algorithmic
165165

166166
% Examples of results that employ the tableaux formalism
167167
168-
@article{gullans2020quantum,
169-
title={Quantum coding with low-depth random circuits},
170-
author={Gullans, Michael J and Krastanov, Stefan and Huse, David A and Jiang, Liang and Flammia, Steven T},
171-
journal={arXiv preprint arXiv:2010.09775},
172-
year={2020}
168+
@article{gullans2021quantum,
169+
title = {Quantum {{Coding}} with {{Low-Depth Random Circuits}}},
170+
author = {Gullans, Michael J. and Krastanov, Stefan and Huse, David A. and Jiang, Liang and Flammia, Steven T.},
171+
year = {2021},
172+
month = sep,
173+
journal = {Physical Review X},
174+
volume = {11},
175+
number = {3},
176+
pages = {031066},
177+
issn = {2160-3308},
178+
doi = {10.1103/PhysRevX.11.031066}
173179
}
174180

175181
@article{krastanov2020heterogeneous,
@@ -386,3 +392,13 @@ @article{knill1996concatenated
386392
journal={arXiv preprint quant-ph/9608012},
387393
year={1996}
388394
}
395+
396+
@inproceedings{brown2013short,
397+
title = {Short Random Circuits Define Good Quantum Error Correcting Codes},
398+
booktitle = {2013 {{IEEE International Symposium}} on {{Information Theory}}},
399+
author = {Brown, Winton and Fawzi, Omar},
400+
year = {2013},
401+
month = jul,
402+
pages = {346--350},
403+
doi = {10.1109/ISIT.2013.6620245}
404+
}

docs/src/tutandpub.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This list has a number of notebooks with tutorials, examples, and reproduction o
44

55
## On the topic of explicit use of the Tableaux formalism for Stabilizer states
66

7-
- [Quantum coding with low-depth random circuits](https://github.com/QuantumSavory/QuantumClifford.jl/blob/master/docs/src/notebooks/Stabilizer_Codes_Based_on_Random_Circuits.ipynb) reproducing results from [gullans2020quantum](@cite). [view on nbviewer.jupyter.org](https://nbviewer.jupyter.org/github/QuantumSavory/QuantumClifford.jl/blob/master/docs/src/notebooks/Stabilizer_Codes_Based_on_Random_Circuits.ipynb)
7+
- [Quantum coding with low-depth random circuits](https://github.com/QuantumSavory/QuantumClifford.jl/blob/master/docs/src/notebooks/Stabilizer_Codes_Based_on_Random_Circuits.ipynb) reproducing results from [gullans2021quantum](@cite). [view on nbviewer.jupyter.org](https://nbviewer.jupyter.org/github/QuantumSavory/QuantumClifford.jl/blob/master/docs/src/notebooks/Stabilizer_Codes_Based_on_Random_Circuits.ipynb)
88

99

1010
## On the Monte Carlo and Perturbative Expansions for **Noisy** Clifford circuits

src/QuantumClifford.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export
6363
random_invertible_gf2,
6464
random_pauli, random_pauli!,
6565
random_stabilizer, random_destabilizer, random_clifford,
66+
random_brickwork_clifford_circuit, random_all_to_all_clifford_circuit,
6667
# Noise
6768
applynoise!, UnbiasedUncorrelatedNoise, NoiseOp, NoiseOpAll, NoisyGate,
6869
PauliNoise, PauliError,

src/ecc/ECC.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export parity_checks, parity_checks_x, parity_checks_z, iscss,
1919
RepCode,
2020
CSS,
2121
Shor9, Steane7, Cleve8, Perfect5, Bitflip3,
22-
Toric, Gottesman, Surface, Concat,
22+
Toric, Gottesman, Surface, Concat, CircuitCode,
23+
random_brickwork_circuit_code, random_all_to_all_circuit_code,
2324
evaluate_decoder,
2425
CommutationCheckECCSetup, NaiveSyndromeECCSetup, ShorSyndromeECCSetup,
2526
TableDecoder,
@@ -359,6 +360,7 @@ include("codes/toric.jl")
359360
include("codes/gottesman.jl")
360361
include("codes/surface.jl")
361362
include("codes/concat.jl")
363+
include("codes/random_circuit.jl")
362364
include("codes/classical/reedmuller.jl")
363365
include("codes/classical/bch.jl")
364366
end #module

src/ecc/codes/random_circuit.jl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using Random: AbstractRNG, GLOBAL_RNG
2+
3+
4+
"""
5+
`CircuitCode` is defined by a given encoding circuit `circ`.
6+
7+
- `n`: qubit number
8+
- `circ`: the encoding circuit
9+
- `encode_qubits`: the qubits to be encoded
10+
11+
See also: [`random_all_to_all_circuit_code`](@ref), [`random_brickwork_circuit_code`](@ref)
12+
"""
13+
struct CircuitCode <: AbstractECC
14+
n::Int
15+
circ::Vector{QuantumClifford.AbstractOperation}
16+
encode_qubits::AbstractArray
17+
end
18+
19+
iscss(::Type{CircuitCode}) = nothing
20+
21+
code_n(c::CircuitCode) = c.n
22+
23+
code_k(c::CircuitCode) = length(c.encode_qubits)
24+
25+
function parity_checks(c::CircuitCode)
26+
n = code_n(c)
27+
checks = one(Stabilizer, n)[setdiff(1:n, c.encode_qubits)]
28+
for op in c.circ
29+
apply!(checks, op)
30+
end
31+
checks
32+
end
33+
34+
"""
35+
Random all-to-all Clifford circuit code [brown2013short](@cite).
36+
37+
The code of `n` qubits is generated by an all-to-all random Clifford circuit of `ngates` gates that encodes a subset of qubits `encode_qubits` into logical qubits.
38+
39+
Because of the random picking, the size of `encode_qubits` is the only thing that matters for the code, referred to as `k`.
40+
41+
See also: [`random_all_to_all_clifford_circuit`](@ref), [`CircuitCode`](@ref)
42+
"""
43+
function random_all_to_all_circuit_code end
44+
45+
function random_all_to_all_circuit_code(rng::AbstractRNG, n::Int, ngates::Int, k::Int)
46+
CircuitCode(n, random_all_to_all_clifford_circuit(rng, n, ngates), collect(1:k))
47+
end
48+
49+
function random_all_to_all_circuit_code(n::Int, ngates::Int, k::Int)
50+
CircuitCode(n, random_all_to_all_clifford_circuit(n, ngates), collect(1:k))
51+
end
52+
53+
function random_all_to_all_circuit_code(rng::AbstractRNG, n::Int, ngates::Int, encode_qubits::AbstractArray)
54+
CircuitCode(n, random_all_to_all_clifford_circuit(rng, n, ngates), encode_qubits)
55+
end
56+
57+
function random_all_to_all_circuit_code(n::Int, ngates::Int, encode_qubits::AbstractArray)
58+
CircuitCode(n, random_all_to_all_clifford_circuit(n, ngates), encode_qubits)
59+
end
60+
61+
62+
"""
63+
Random brickwork Clifford circuit code [brown2013short](@cite).
64+
65+
The code is generated by a brickwork random Clifford circuit of `nlayers` layers that encodes a subset of qubits `encode_qubits` into logical qubits.
66+
67+
See also: [`random_brickwork_clifford_circuit`](@ref), [`CircuitCode`](@ref)
68+
"""
69+
function random_brickwork_circuit_code end
70+
71+
# TODO it would be nicer if we can use CartesianIndex for `encode_qubits` in brickworks,
72+
# but its conversion to LinearIndex is limited, not supporting non-one step.
73+
function random_brickwork_circuit_code(rng::AbstractRNG, lattice_size::NTuple{N,Int} where {N}, nlayers::Int, encode_qubits::AbstractArray)
74+
CircuitCode(prod(lattice_size), random_brickwork_clifford_circuit(rng, lattice_size, nlayers), encode_qubits)
75+
end
76+
77+
function random_brickwork_circuit_code(lattice_size::NTuple{N,Int} where {N}, nlayers::Int, encode_qubits::AbstractArray)
78+
CircuitCode(prod(lattice_size), random_brickwork_clifford_circuit(lattice_size, nlayers), encode_qubits)
79+
end

src/entanglement.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ It is the list of endpoints of a tableau in the clipped gauge.
139139
If `clip=true` (the default) the tableau is converted to the clipped gauge in-place before calculating the bigram.
140140
Otherwise, the clip gauge conversion is skipped (for cases where the input is already known to be in the correct gauge).
141141
142-
Introduced in [nahum2017quantum](@cite), with a more detailed explanation of the algorithm in [li2019measurement](@cite) and [gullans2020quantum](@cite).
142+
Introduced in [nahum2017quantum](@cite), with a more detailed explanation of the algorithm in [li2019measurement](@cite) and [gullans2021quantum](@cite).
143143
144144
See also: [`canonicalize_clip!`](@ref)
145145
"""
@@ -186,7 +186,7 @@ function entanglement_entropy(state::AbstractStabilizer, subsystem_range::UnitRa
186186
# JET-XXX The ::Matrix{Int} should not be necessary, but they help with inference
187187
bg = bigram(state; clip=clip)::Matrix{Int}
188188
# If the state is mixed, this formula is valid only for contiguous regions that don't wrap around.
189-
# See Eq. E7 of gullans2020quantum.
189+
# See Eq. E7 of gullans2021quantum.
190190
# As subsystem_range is UnitRange, we know the formula will be valid.
191191
length(subsystem_range) - count(r->(r[1] in subsystem_range && r[2] in subsystem_range), eachrow(bg))
192192
end

src/randoms.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,60 @@ function fill_tril(rng, matrix, n; symmetric::Bool=false)
228228
end
229229
matrix
230230
end
231+
232+
##############################
233+
# Random circuit
234+
##############################
235+
236+
"""
237+
Random brickwork Clifford circuit.
238+
239+
The connectivity of the random circuit is brickwork in some dimensions. Each gate in the circuit is a random 2-qubit Clifford gate.
240+
241+
The brickwork is defined as follows: The qubits are arranged as a lattice, and `lattice_size` contains side length in each dimension.
242+
For example, a chain of length five will have `lattice_size = (5,)`, and a 5×5 lattice will have `lattice_size = (5, 5)`.
243+
244+
In multi-dimensional cases, gate layers act alternatively along each direction.
245+
The nearest two layers along the same direction are offset by one qubit, forming a so-called brickwork.
246+
The boundary condition is chosen as open.
247+
"""
248+
function random_brickwork_clifford_circuit(rng::AbstractRNG, lattice_size::NTuple{N,Int} where {N}, nlayers::Int)
249+
circ = QuantumClifford.SparseGate[]
250+
cartesian = CartesianIndices(lattice_size)
251+
dim = length(lattice_size)
252+
nqubits = prod(lattice_size)
253+
for i in 1:nlayers
254+
gate_direction = (i - 1) % dim + 1
255+
l = lattice_size[gate_direction]
256+
brickwise_parity = dim == 1 ? i % 2 : 1 - (i ÷ dim) % 2
257+
for j in 1:nqubits
258+
cardj = collect(cartesian[j].I)
259+
if cardj[gate_direction] % 2 == brickwise_parity && cardj[gate_direction] != l # open boundary
260+
cardk = cardj
261+
cardk[gate_direction] = cardk[gate_direction] + 1
262+
k = LinearIndices(cartesian)[cardk...]
263+
push!(circ, SparseGate(random_clifford(rng, 2), [j, k]))
264+
end
265+
end
266+
end
267+
circ
268+
end
269+
270+
random_brickwork_clifford_circuit(lattice_size::NTuple{N,Int} where {N}, nlayers::Int) = random_brickwork_clifford_circuit(GLOBAL_RNG, lattice_size, nlayers)
271+
272+
"""
273+
Random all-to-all Clifford circuit.
274+
275+
The circuit contains `nqubits` qubits and `ngates` gates. The connectivity is all to all. Each gate in the circuit is a random 2-qubit Clifford gate on randomly picked two qubits.
276+
"""
277+
function random_all_to_all_clifford_circuit(rng::AbstractRNG, nqubits::Int, ngates::Int)
278+
circ = QuantumClifford.SparseGate[]
279+
for i in 1:ngates
280+
j = rand(1:nqubits)
281+
k = rand(1:nqubits-1)
282+
push!(circ, SparseGate(random_clifford(rng, 2), [j, (j + k - 1) % nqubits + 1]))
283+
end
284+
circ
285+
end
286+
287+
random_all_to_all_clifford_circuit(nqubits::Int, ngates::Int) = random_all_to_all_clifford_circuit(GLOBAL_RNG, nqubits, ngates)

test/test_ecc_base.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ using InteractiveUtils
55

66
# generate instances of all implemented codes to make sure nothing skips being checked
77

8+
# We do not include smaller random circuit code because some of them has a bad distance and fails the TableDecoder test
9+
const random_brickwork_circuit_args = repeat([((20,), 50, [1]), ((20,), 50, 1:2:20), ((5, 5), 50, [1]), ((3, 3, 3), 50, [1])], 10)
10+
const random_all_to_all_circuit_args = repeat([(20, 200, 1), (40, 200, [1, 20])], 10)
11+
12+
random_circuit_code_args = vcat(
13+
[map(f -> getfield(random_brickwork_circuit_code(c...), f), fieldnames(CircuitCode)) for c in random_brickwork_circuit_args],
14+
[map(f -> getfield(random_all_to_all_circuit_code(c...), f), fieldnames(CircuitCode)) for c in random_all_to_all_circuit_args]
15+
)
16+
817
const code_instance_args = Dict(
918
Toric => [(3,3), (4,4), (3,6), (4,3), (5,5)],
1019
Surface => [(3,3), (4,4), (3,6), (4,3), (5,5)],
1120
Gottesman => [3, 4, 5],
12-
CSS => (c -> (parity_checks_x(c), parity_checks_z(c))).([Shor9(), Steane7(), Toric(4,4)]),
13-
Concat => [(Perfect5(), Perfect5()), (Perfect5(), Steane7()), (Steane7(), Cleve8()), (Toric(2,2), Shor9())],
21+
CSS => (c -> (parity_checks_x(c), parity_checks_z(c))).([Shor9(), Steane7(), Toric(4, 4)]),
22+
Concat => [(Perfect5(), Perfect5()), (Perfect5(), Steane7()), (Steane7(), Cleve8()), (Toric(2, 2), Shor9())],
23+
CircuitCode => random_circuit_code_args
1424
)
1525

1626
function all_testablable_code_instances(;maxn=nothing)

0 commit comments

Comments
 (0)