Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ These changes affect internal implementation details - external packages should

- `permutesystems` and `permutesystems!` are no implemented, deprecating `permute` and `permute!`

## Added
- BP-OTS (Belief Propagation with Oscillating Trapping Sets) decoder for quantum LDPC codes in the LDPCDecoders extension

## v0.9.18 - 2025-02-19

- Fixes for rare crashes in the python BP decoders.
Expand Down
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
HostCPUFeatures = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0"
ILog2 = "2cd5bd5f-40a1-5050-9e10-fc8cdb6109f5"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
LDPCDecoders = "3c486d74-64b9-4c60-8b1a-13a564e77efb"
Copy link
Member

Choose a reason for hiding this comment

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

this needs to be moved back to a weak dependency

LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a"
Expand All @@ -32,7 +33,6 @@ GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527"
Hecke = "3e1990a7-5d81-5526-99ce-9ba3ff248f21"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
LDPCDecoders = "3c486d74-64b9-4c60-8b1a-13a564e77efb"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Oscar = "f1435218-dba5-11e9-1e4d-f1a5fab5fc13"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Expand Down Expand Up @@ -73,7 +73,7 @@ ILog2 = "0.2.3, 1, 2"
InteractiveUtils = "1.10"
JuMP = "1.23"
KernelAbstractions = "0.9.35"
LDPCDecoders = "0.3.2"
LDPCDecoders = "0.3.3"
LinearAlgebra = "1.10"
MacroTools = "0.5.9"
Makie = "0.20, 0.21, 0.22, 0.23, 0.24"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,89 @@ struct BitFlipDecoder <: AbstractSyndromeDecoder # TODO all these decoders have
bfdecoderz
end

"""
BPOTSDecoder(code; errorrate=nothing, maxiter=nothing, T=9, C=2.0)

BP-OTS (Belief Propagation with Oscillating Trapping Sets) decoder for quantum LDPC codes.
This decoder uses oscillation tracking and biasing to escape trapping sets.

# Arguments
- `code`: A quantum code (e.g., Toric, Surface)
- `errorrate`: Physical error rate (depolarizing probability)
- `maxiter`: Maximum number of iterations (default: 200)
- `T`: Biasing period (default: 9)
- `C`: Bias constant (default: 2.0)

# Reference
Chytas et al., "Enhanced Message-Passing Decoding of Degenerate Quantum Codes
Utilizing Trapping Set Dynamics", IEEE Communications Letters, 2024
"""

Copy link
Member

Choose a reason for hiding this comment

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

this empty line makes the docstring not attach to the struct

struct BPOTSDecoder <: AbstractSyndromeDecoder
original_code
H::SparseMatrixCSC{Bool,Int}
faults_matrix::Matrix{Bool}
n::Int
s::Int
k::Int
cx::Int
cz::Int
bpots_x::LDPCDecoders.BPOTSDecoder
bpots_z::LDPCDecoders.BPOTSDecoder
end

function BPOTSDecoder(c; errorrate=nothing, maxiter=nothing, T=9, C=2.0)

Hx_raw = parity_checks_x(c)
Hz_raw = parity_checks_z(c)
H_raw = parity_checks(c)

if H_raw isa Stabilizer
H_gf2 = stab_to_gf2(H_raw)
H = sparse(Bool.(H_gf2))
else
H = sparse(Bool.(H_raw))
end

if Hx_raw isa Stabilizer
Hx_gf2 = stab_to_gf2(Hx_raw)
Hz_gf2 = stab_to_gf2(Hz_raw)
Hx = sparse(Bool.(Hx_gf2))
Hz = sparse(Bool.(Hz_gf2))
else
Hx = sparse(Bool.(Hx_raw))
Hz = sparse(Bool.(Hz_raw))
end

s, n = size(H)
k = code_k(c)

cx = size(Hx, 1)
cz = size(Hz, 1)

fm = BitMatrix(ones(Bool, s, 2*n))

errorrate = something(errorrate, 0.0)
maxiter = something(maxiter, 200)
bpots_x = LDPCDecoders.BPOTSDecoder(Hx, errorrate, maxiter; T=T, C=C)
bpots_z = LDPCDecoders.BPOTSDecoder(Hz, errorrate, maxiter; T=T, C=C)

return BPOTSDecoder(c, H, fm, n, s, k, cx, cz, bpots_x, bpots_z)
end

function decode(d::BPOTSDecoder, syndrome_sample::AbstractVector{Bool})
length(syndrome_sample) == d.cx + d.cz ||
throw(DimensionMismatch("Syndrome length ($(length(syndrome_sample))) does not match expected size ($(d.cx + d.cz))"))

row_x = @view syndrome_sample[1:d.cx]
row_z = @view syndrome_sample[d.cx+1:d.cx+d.cz]

guess_z, conv_z = LDPCDecoders.decode!(d.bpots_x, Vector(row_x))
guess_x, conv_x = LDPCDecoders.decode!(d.bpots_z, Vector(row_z))

return vcat(guess_x, guess_z)
end
Copy link
Member

Choose a reason for hiding this comment

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

repeated

Copy link
Author

Choose a reason for hiding this comment

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

removed this repetition


function BeliefPropDecoder(c; errorrate=nothing, maxiter=nothing)
Hx = parity_matrix_x(c)
Hz = parity_matrix_z(c)
Expand Down Expand Up @@ -65,14 +148,15 @@ function BitFlipDecoder(c; errorrate=nothing, maxiter=nothing)
isnothing(errorrate) || 0≤errorrate≤1 || error(lazy"BitFlipDecoder got an invalid error rate argument. `errorrate` must be in the range [0, 1].")
errorrate = isnothing(errorrate) ? 0.0 : errorrate
maxiter = isnothing(maxiter) ? n : maxiter
bfx = LDPCDecoders.BitFlipDecoder(Hx, errorrate, maxiter)
bfz = LDPCDecoders.BitFlipDecoder(Hz, errorrate, maxiter)
bfx = LDPCDecoders.BitFlipDecoder(Hx, errorrate, maxiter)
Comment on lines 68 to 150
Copy link
Member

Choose a reason for hiding this comment

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

undo the permutation


return BitFlipDecoder(H, fm, n, s, k, cx, cz, bfx, bfz)
end

parity_checks(d::BeliefPropDecoder) = d.H
parity_checks(d::BitFlipDecoder) = d.H
parity_checks(d::BPOTSDecoder) = d.H

function decode(d::BeliefPropDecoder, syndrome_sample)
row_x = @view syndrome_sample[1:d.cx]
Expand Down
2 changes: 1 addition & 1 deletion src/ecc/ECC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export parity_checks, parity_matrix_x, parity_matrix_z, iscss,
evaluate_decoder,
CommutationCheckECCSetup, NaiveSyndromeECCSetup, ShorSyndromeECCSetup,
TableDecoder,
BeliefPropDecoder, BitFlipDecoder,
BeliefPropDecoder, BitFlipDecoder, BPOTSDecoder,
PyBeliefPropDecoder, PyBeliefPropOSDecoder, PyMatchingDecoder, DecoderCorrectionGate

"""Parity check tableau of a code.
Expand Down
9 changes: 9 additions & 0 deletions src/ecc/decoder_pipeline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,15 @@ function BitFlipDecoder(args...; kwargs...)
return ext.BitFlipDecoder(args...; kwargs...)
end

"""A BP-OTS (Belief Propagation with Oscillating Trapping Sets) decoder built around tools from `LDPCDecoders.jl`.
"""
function BPOTSDecoder(args...; kwargs...)
ext = Base.get_extension(QuantumClifford, :QuantumCliffordLDPCDecodersExt)
if isnothing(ext)
throw("The `BPOTSDecoder` depends on the package `LDPCDecoders` but you have not installed or imported `LDPCDecoders` yet. Immediately after you import `LDPCDecoders`, the `BPOTSDecoder` will be available.")
end
return ext.BPOTSDecoder(args...; kwargs...)
end

"""A Belief Propagation decoder built around tools from the python package `ldpc` available from the julia package `PyQDecoders.jl`."""
function PyBeliefPropDecoder(args...; kwargs...)
Expand Down
30 changes: 30 additions & 0 deletions test/test_ecc_decoder_all_setups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,36 @@
end
end

##
@testset "BPOTSDecoder decoder, good for topological codes" begin
codes = [
Toric(6,6),
Toric(8,8),
Surface(6,6),
Surface(8,8)
]

noise = 0.01

setups = [
CommutationCheckECCSetup(noise),
NaiveSyndromeECCSetup(noise, 0),
ShorSyndromeECCSetup(noise, 0),
]

for c in codes
for s in setups
for d in [c->BPOTSDecoder(c, errorrate=noise, maxiter=200, T=9, C=2.0)]
e = evaluate_decoder(d(c), s, 10000)
#@show c
#@show s
#@show e
@test max(e...) < noise/2
end
end
end
end

##

using Test
Expand Down
Loading