From 1ac75574245d09c1d495f671942d804b887684e5 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Thu, 26 Sep 2024 10:18:32 +0500 Subject: [PATCH 1/7] remove hcat and vcat from decoders --- .../QuantumCliffordLDPCDecodersExt.jl | 18 ++++++++---- .../QuantumCliffordPyQDecodersExt.jl | 28 +++++++++++++------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl index 8c369cf82..aabd24aff 100644 --- a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl +++ b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl @@ -74,19 +74,25 @@ parity_checks(d::BeliefPropDecoder) = d.H parity_checks(d::BitFlipDecoder) = d.H function decode(d::BeliefPropDecoder, syndrome_sample) - row_x = syndrome_sample[1:d.cx] - row_z = syndrome_sample[d.cx+1: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, success = LDPCDecoders.decode!(d.bpdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bpdecoderz, row_z) - return vcat(guess_x, guess_z) + result = Matrix{Int}(undef, 2, length(guess_x)) + @inbounds result[1, 1:length(guess_x)] .= guess_x + @inbounds result[2, 1:length(guess_z)] .= guess_z + return result end function decode(d::BitFlipDecoder, syndrome_sample) - row_x = syndrome_sample[1:d.cx] - row_z = syndrome_sample[d.cx+1: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, success = LDPCDecoders.decode!(d.bfdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bfdecoderz, row_z) - return vcat(guess_x, guess_z) + result = Matrix{Int}(undef, 2, length(guess_x)) + @inbounds result[1, 1:length(guess_x)] .= guess_x + @inbounds result[2, 1:length(guess_z)] .= guess_z + return result end end diff --git a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl index bfa557d05..83fcc5064 100644 --- a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl +++ b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl @@ -69,11 +69,14 @@ end parity_checks(d::PyBP) = d.H function decode(d::PyBP, syndrome_sample) - row_x = syndrome_sample[1:d.nx] # TODO These copies and indirections might be costly! - row_z = syndrome_sample[d.nx+1:end] + row_x = @view syndrome_sample[1:d.nx] + row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(np.array(row_x))) guess_x_errors = PythonCall.PyArray(d.pyz.decode(np.array(row_z))) - vcat(guess_x_errors, guess_z_errors) + result = Matrix{Int}(undef, 2, length(guess_x_errors)) + @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors + @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors + return result end struct PyMatchingDecoder <: AbstractSyndromeDecoder # TODO all these decoders have the same fields, maybe we can factor out a common type @@ -106,19 +109,26 @@ end parity_checks(d::PyMatchingDecoder) = d.H function decode(d::PyMatchingDecoder, syndrome_sample) - row_x = syndrome_sample[1:d.nx] # TODO This copy is costly! - row_z = syndrome_sample[d.nx+1:end] + row_x = @view syndrome_sample[1:d.nx] + row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(row_x)) guess_x_errors = PythonCall.PyArray(d.pyz.decode(row_z)) - vcat(guess_x_errors, guess_z_errors) + result = Matrix{Int}(undef, 2, length(guess_x_errors)) + @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors + @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors + return result end function batchdecode(d::PyMatchingDecoder, syndrome_samples) - row_x = syndrome_samples[:,1:d.nx] # TODO This copy is costly! - row_z = syndrome_samples[:,d.nx+1:end] + row_x = @view syndrome_samples[:,1:d.nx] + row_z = @view syndrome_samples[:,d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode_batch(row_x)) guess_x_errors = PythonCall.PyArray(d.pyz.decode_batch(row_z)) - hcat(guess_x_errors, guess_z_errors) + n_cols_x = size(guess_x_errors, 2) + result = Matrix{Int}(undef, size(guess_x_errors, 1), n_cols_x + size(guess_z_errors, 2)) + @inbounds result[:,1:n_cols_x] .= guess_x_errors + @inbounds result[:,n_cols_x+1:end] .= guess_z_errors + return result end end From a5cbdd5728a3c7aa924cfcc07730bd9cbcffeded Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Thu, 26 Sep 2024 15:27:00 +0500 Subject: [PATCH 2/7] improve evaluate_guesses --- .../QuantumCliffordLDPCDecodersExt.jl | 4 ++-- .../QuantumCliffordPyQDecodersExt.jl | 4 ++-- src/ecc/decoder_pipeline.jl | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl index aabd24aff..d202584cf 100644 --- a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl +++ b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl @@ -78,7 +78,7 @@ function decode(d::BeliefPropDecoder, syndrome_sample) row_z = @view syndrome_sample[d.cx+1:d.cx+d.cz] guess_z, success = LDPCDecoders.decode!(d.bpdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bpdecoderz, row_z) - result = Matrix{Int}(undef, 2, length(guess_x)) + result = Matrix{Int}(undef, 2, length(guess_x)) @inbounds result[1, 1:length(guess_x)] .= guess_x @inbounds result[2, 1:length(guess_z)] .= guess_z return result @@ -89,7 +89,7 @@ function decode(d::BitFlipDecoder, syndrome_sample) row_z = @view syndrome_sample[d.cx+1:d.cx+d.cz] guess_z, success = LDPCDecoders.decode!(d.bfdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bfdecoderz, row_z) - result = Matrix{Int}(undef, 2, length(guess_x)) + result = Matrix{Int}(undef, 2, length(guess_x)) @inbounds result[1, 1:length(guess_x)] .= guess_x @inbounds result[2, 1:length(guess_z)] .= guess_z return result diff --git a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl index 83fcc5064..5963ca672 100644 --- a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl +++ b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl @@ -73,7 +73,7 @@ function decode(d::PyBP, syndrome_sample) row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(np.array(row_x))) guess_x_errors = PythonCall.PyArray(d.pyz.decode(np.array(row_z))) - result = Matrix{Int}(undef, 2, length(guess_x_errors)) + result = Matrix{Int}(undef, 2, length(guess_x_errors)) @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors return result @@ -113,7 +113,7 @@ function decode(d::PyMatchingDecoder, syndrome_sample) row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(row_x)) guess_x_errors = PythonCall.PyArray(d.pyz.decode(row_z)) - result = Matrix{Int}(undef, 2, length(guess_x_errors)) + result = Matrix{Int}(undef, 2, length(guess_x_errors)) @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors return result diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index 81fd1af30..bd81f7b09 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -171,10 +171,21 @@ end function evaluate_guesses(measured_faults, guesses, faults_matrix) nsamples = size(guesses, 1) - guess_faults = (faults_matrix * guesses') .% 2 # TODO this can be faster and non-allocating by turning it into a loop decoded = 0 - for i in 1:nsamples # TODO this can be faster and non-allocating by having the loop and the matrix multiplication on the line above work together and not store anything - (@view guess_faults[:,i]) == (@view measured_faults[i,:]) && (decoded += 1) + for i in 1:nsamples + is_decoded = true + for j in 1:size(faults_matrix, 1) + sum_mod = 0 + @inbounds @simd for k in 1:size(faults_matrix, 2) + sum_mod += faults_matrix[j, k] * guesses[i, k] + end + sum_mod %= 2 + if sum_mod != measured_faults[i, j] + is_decoded = false + break + end + end + decoded += is_decoded end return (nsamples - decoded) / nsamples end From fb1e0d86407bbd6524a216bd6d9ad2a3ccdde9b9 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Thu, 26 Sep 2024 16:39:10 +0500 Subject: [PATCH 3/7] improve decode --- src/ecc/decoder_pipeline.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index bd81f7b09..0369cc839 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -264,8 +264,8 @@ function create_lookup_table(code::Stabilizer) lookup_table end; -function decode(d::TableDecoder, syndrome_sample) - d.lookup_buffer .= syndrome_sample # TODO have this work without data copying, by supporting the correct types, especially in the batch decode case +function decode(d::TableDecoder, syndrome_sample::AbstractVector{Bool}) + copyto!(d.lookup_buffer, syndrome_sample) return get(d.lookup_table, d.lookup_buffer, nothing) end From 637cd3af521adcd20c75bd2bc2b0b0bfa2e329d0 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Thu, 26 Sep 2024 19:07:09 +0500 Subject: [PATCH 4/7] improve batchdecode --- src/ecc/decoder_pipeline.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index 0369cc839..6a350f3e2 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -16,7 +16,8 @@ function batchdecode(d::AbstractSyndromeDecoder, syndrome_samples) samples, _s = size(syndrome_samples) s == _s || throw(ArgumentError(lazy"The syndromes given to `batchdecode` have the wrong dimensions. The syndrome length is $(_s) while it should be $(s)")) results = falses(samples, 2n) - for (i,syndrome_sample) in enumerate(eachrow(syndrome_samples)) + @inbounds for i in 1:samples + syndrome_sample = @view syndrome_samples[i,:] guess = decode(d, syndrome_sample)# TODO use `decode!` isnothing(guess) || (results[i,:] = guess) end @@ -265,7 +266,7 @@ function create_lookup_table(code::Stabilizer) end; function decode(d::TableDecoder, syndrome_sample::AbstractVector{Bool}) - copyto!(d.lookup_buffer, syndrome_sample) + @inbounds copyto!(d.lookup_buffer, syndrome_sample) return get(d.lookup_table, d.lookup_buffer, nothing) end From 3fcf26c9165c53fffddced8b9e8e8eead2333afd Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 27 Sep 2024 02:00:16 +0500 Subject: [PATCH 5/7] revert back to vcat and hcat --- .../QuantumCliffordLDPCDecodersExt.jl | 10 ++-------- .../QuantumCliffordPyQDecodersExt.jl | 15 +++------------ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl index d202584cf..e80c13af6 100644 --- a/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl +++ b/ext/QuantumCliffordLDPCDecodersExt/QuantumCliffordLDPCDecodersExt.jl @@ -78,10 +78,7 @@ function decode(d::BeliefPropDecoder, syndrome_sample) row_z = @view syndrome_sample[d.cx+1:d.cx+d.cz] guess_z, success = LDPCDecoders.decode!(d.bpdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bpdecoderz, row_z) - result = Matrix{Int}(undef, 2, length(guess_x)) - @inbounds result[1, 1:length(guess_x)] .= guess_x - @inbounds result[2, 1:length(guess_z)] .= guess_z - return result + return vcat(guess_x, guess_z) end function decode(d::BitFlipDecoder, syndrome_sample) @@ -89,10 +86,7 @@ function decode(d::BitFlipDecoder, syndrome_sample) row_z = @view syndrome_sample[d.cx+1:d.cx+d.cz] guess_z, success = LDPCDecoders.decode!(d.bfdecoderx, row_x) guess_x, success = LDPCDecoders.decode!(d.bfdecoderz, row_z) - result = Matrix{Int}(undef, 2, length(guess_x)) - @inbounds result[1, 1:length(guess_x)] .= guess_x - @inbounds result[2, 1:length(guess_z)] .= guess_z - return result + return vcat(guess_x, guess_z) end end diff --git a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl index 5963ca672..e3496a733 100644 --- a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl +++ b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl @@ -73,10 +73,7 @@ function decode(d::PyBP, syndrome_sample) row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(np.array(row_x))) guess_x_errors = PythonCall.PyArray(d.pyz.decode(np.array(row_z))) - result = Matrix{Int}(undef, 2, length(guess_x_errors)) - @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors - @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors - return result + vcat(guess_x_errors, guess_z_errors) end struct PyMatchingDecoder <: AbstractSyndromeDecoder # TODO all these decoders have the same fields, maybe we can factor out a common type @@ -113,10 +110,7 @@ function decode(d::PyMatchingDecoder, syndrome_sample) row_z = @view syndrome_sample[d.nx+1:end] guess_z_errors = PythonCall.PyArray(d.pyx.decode(row_x)) guess_x_errors = PythonCall.PyArray(d.pyz.decode(row_z)) - result = Matrix{Int}(undef, 2, length(guess_x_errors)) - @inbounds result[1, 1:length(guess_x_errors)] .= guess_x_errors - @inbounds result[2, 1:length(guess_z_errors)] .= guess_z_errors - return result + vcat(guess_x_errors, guess_z_errors) end function batchdecode(d::PyMatchingDecoder, syndrome_samples) @@ -125,10 +119,7 @@ function batchdecode(d::PyMatchingDecoder, syndrome_samples) guess_z_errors = PythonCall.PyArray(d.pyx.decode_batch(row_x)) guess_x_errors = PythonCall.PyArray(d.pyz.decode_batch(row_z)) n_cols_x = size(guess_x_errors, 2) - result = Matrix{Int}(undef, size(guess_x_errors, 1), n_cols_x + size(guess_z_errors, 2)) - @inbounds result[:,1:n_cols_x] .= guess_x_errors - @inbounds result[:,n_cols_x+1:end] .= guess_z_errors - return result + hcat(guess_x_errors, guess_z_errors) end end From d827bde4fea61f9701babe2dc57d64b5988388f9 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 27 Sep 2024 02:23:24 +0500 Subject: [PATCH 6/7] undo changes in decode and batchdecode --- src/ecc/decoder_pipeline.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index 6a350f3e2..bd81f7b09 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -16,8 +16,7 @@ function batchdecode(d::AbstractSyndromeDecoder, syndrome_samples) samples, _s = size(syndrome_samples) s == _s || throw(ArgumentError(lazy"The syndromes given to `batchdecode` have the wrong dimensions. The syndrome length is $(_s) while it should be $(s)")) results = falses(samples, 2n) - @inbounds for i in 1:samples - syndrome_sample = @view syndrome_samples[i,:] + for (i,syndrome_sample) in enumerate(eachrow(syndrome_samples)) guess = decode(d, syndrome_sample)# TODO use `decode!` isnothing(guess) || (results[i,:] = guess) end @@ -265,8 +264,8 @@ function create_lookup_table(code::Stabilizer) lookup_table end; -function decode(d::TableDecoder, syndrome_sample::AbstractVector{Bool}) - @inbounds copyto!(d.lookup_buffer, syndrome_sample) +function decode(d::TableDecoder, syndrome_sample) + d.lookup_buffer .= syndrome_sample # TODO have this work without data copying, by supporting the correct types, especially in the batch decode case return get(d.lookup_table, d.lookup_buffer, nothing) end From 087aeae6411481281009050f2f4e5f05cd23e65c Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Thu, 26 Sep 2024 22:48:49 -0400 Subject: [PATCH 7/7] slightly simplify the evaluate_guess code --- src/ecc/decoder_pipeline.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index bd81f7b09..ad6fd870c 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -171,9 +171,8 @@ end function evaluate_guesses(measured_faults, guesses, faults_matrix) nsamples = size(guesses, 1) - decoded = 0 + fails = 0 for i in 1:nsamples - is_decoded = true for j in 1:size(faults_matrix, 1) sum_mod = 0 @inbounds @simd for k in 1:size(faults_matrix, 2) @@ -181,13 +180,12 @@ function evaluate_guesses(measured_faults, guesses, faults_matrix) end sum_mod %= 2 if sum_mod != measured_faults[i, j] - is_decoded = false + fails += 1 break end end - decoded += is_decoded end - return (nsamples - decoded) / nsamples + return fails / nsamples end function evaluate_decoder(d::AbstractSyndromeDecoder, setup::CommutationCheckECCSetup, nsamples::Int)