From 2c4cb47f459d8c36d47794be712004d38c08ef43 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 31 Oct 2023 19:40:50 -0400 Subject: [PATCH 01/21] Added code_evaluation and code_generation methods Added multiple methods: code_generation: - Methods for creating Bicycle and Unicycle codes - Methods for assembling CSS codes - Utility methods for working with codes code_evaluation: - Methods for evaluating codes with a belief propagation decoder --- src/ecc/ECC.jl | 15 +- src/ecc/code_evaluation.jl | 178 ++++++++++++++++++++++ src/ecc/code_generation.jl | 298 +++++++++++++++++++++++++++++++++++++ 3 files changed, 488 insertions(+), 3 deletions(-) create mode 100644 src/ecc/code_evaluation.jl create mode 100644 src/ecc/code_generation.jl diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index b83f4e793..897b00748 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -1,18 +1,25 @@ module ECC using LinearAlgebra -using QuantumClifford -using QuantumClifford: AbstractOperation, AbstractStabilizer +using QuantumClifford, CairoMakie, SparseArrays, LDPCDecoders +using QuantumClifford: AbstractOperation, AbstractStabilizer, Stabilizer import QuantumClifford: Stabilizer, MixedDestabilizer +import QuantumClifford.ECC: parity_checks using DocStringExtensions using Combinatorics: combinations +using Statistics: std +using LinearAlgebra: rank abstract type AbstractECC end export Shor9, Steane7, Cleve8, Perfect5, Bitflip3, parity_checks, naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, code_n, code_s, code_k, rate, distance, - isdegenerate, faults_matrix + isdegenerate, faults_matrix, CSS_Code, Bicycle_Code, Unicycle_Code, Circ2BicycleH0, + Circ2UnicycleH0, AssembleCSS, BicycleSetGen, BicycleSetGenRand, GetCodeTableau, + GetXTableau, GetZTableau, parity_checks, ReduceBicycle, ReduceUnicycle, + create_lookup_table, evaluate_code_decoder_w_ecirc_pf, plot_code_performance, pf_encoding_plot + """Parity check tableau of a code.""" function parity_checks end @@ -289,6 +296,8 @@ function isdegenerate(H::Stabilizer, d::Int=1) end include("circuits.jl") +include("code_generation.jl") +include("code_evaluation.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") diff --git a/src/ecc/code_evaluation.jl b/src/ecc/code_evaluation.jl new file mode 100644 index 000000000..206d55934 --- /dev/null +++ b/src/ecc/code_evaluation.jl @@ -0,0 +1,178 @@ +# using QuantumClifford.ECC: faults_matrix, naive_syndrome_circuit, parity_checks, AbstractECC, naive_encoding_circuit, Cleve8, Steane7, Shor9, Perfect5 + +"""Generate a lookup table for decoding single qubit errors. Maps s⃗ → e⃗.""" +function create_lookup_table(code::Stabilizer) + lookup_table = Dict() + constraints, qubits = size(code) + # In the case of no errors + lookup_table[ zeros(UInt8, constraints) ] = zero(PauliOperator, qubits) + # In the case of single bit errors + for bit_to_be_flipped in 1:qubits + for error_type in [single_x, single_y, single_z] + # Generate e⃗ + error = error_type(qubits, bit_to_be_flipped) + # Calculate s⃗ + # (check which stabilizer rows do not commute with the Pauli error) + syndrome = comm(error, code) + # Store s⃗ → e⃗ + lookup_table[syndrome] = error + end + end + lookup_table +end + + """Currently uses a 1 qubit lookup table decoder. Assumes scirc is generated with naive_syndrome_circuit - I have a different function for fault tolerant syndromes circuits""" +function evaluate_code_decoder_w_ecirc_pf(checks::Stabilizer, ecirc, scirc, p_error; nframes=10_000, encoding_locs=nothing) + s, n = size(checks) + k = n-s + + pcm = stab_to_gf2(checks) + pcm_X = pcm[1:Int(s/2), 1:n] + pcm_Z = pcm[Int(s/2) + 1:end, n + 1:end] + + O = faults_matrix(checks) + circuit_Z = Base.copy(scirc) + circuit_X = Base.copy(scirc) + + # This is a where the bits to be encoded are, ecircs genereated by naive_encoding_circuit() will put those at the bottom + # Thus, the default is to apply this to the bottom k qubits + if isnothing(encoding_locs) + pre_X = [sHadamard(i) for i in n-k+1:n] + else + pre_X = [sHadamard(i) for i in encoding_locs] + end + + md = MixedDestabilizer(checks) + logview_Z = [ logicalzview(md);] + logcirc_Z, numLogBits, _ = naive_syndrome_circuit(logview_Z) # numLogBits shoudl equal k + + logview_X = [ logicalxview(md);] + logcirc_X, _ = naive_syndrome_circuit(logview_X) + + # Z logic circuit + for gate in logcirc_Z + type = typeof(gate) + if type == sMRZ + push!(circuit_Z, sMRZ(gate.qubit+s, gate.bit+s)) + else + push!(circuit_Z, type(gate.q1, gate.q2+s)) + end + end + + # X logic circuit + for gate in logcirc_X + type = typeof(gate) + if type == sMRZ + push!(circuit_X, sMRZ(gate.qubit+s, gate.bit+s)) + else + push!(circuit_X, type(gate.q1, gate.q2+s)) + end + end + + # Z simulation + errors = [PauliError(i,p_error) for i in 1:n] + + fullcircuit_Z = vcat(ecirc, errors, circuit_Z) + + frames = PauliFrame(nframes, n+s+k, s+k) + pftrajectories(frames, fullcircuit_Z) + syndromes = pfmeasurements(frames)[:, 1:s] + logicalSyndromes = pfmeasurements(frames)[:, s+1: s+k] + + decoded = 0 + for i in 1:nframes + row = syndromes[i,:] + + guess, success = syndrome_decode(sparse(pcm_Z), sparse(pcm_Z'), row[Int(s/2)+1:end], 50, fill(p_error, n), zeros(Int(s/2), n), zeros(Int(s/2), n), zeros(n), zeros(n)) + + guess = vcat(convert(Vector{Bool}, fill(0, n)), convert(Vector{Bool}, guess)) + + if isnothing(guess) + continue + else + result_Z = (O * guess)[k+1:2k] + if result_Z == logicalSyndromes[i,:] + if(i == 1) + end + decoded += 1 + end + end + end + z_error = 1 - decoded / nframes + + # X simulation + fullcircuit_X = vcat(pre_X, ecirc, errors, circuit_X) + frames = PauliFrame(nframes, n+s+k, s+k) + pftrajectories(frames, fullcircuit_X) + syndromes = pfmeasurements(frames)[:, 1:s] + logicalSyndromes = pfmeasurements(frames)[:, s+1: s+k] + + decoded = 0 + for i in 1:nframes + row = syndromes[i,:] + + guess, success = syndrome_decode(sparse(pcm_X), sparse(pcm_X'), row[1:Int(s/2)], 50, fill(p_error, n), zeros(Int(s/2), n), zeros(Int(s/2), n), zeros(n), zeros(n)) + + guess = vcat(convert(Vector{Bool}, guess), convert(Vector{Bool}, fill(0, n))) + + if isnothing(guess) + continue + else + result_X = (O * guess)[1:k] + if result_X == logicalSyndromes[i,:] + decoded += 1 + end + end + end + x_error = 1 - decoded / nframes + + return x_error, z_error +end + +"""Taken from the QEC Seminar notebook for plotting logical vs physical error""" +function plot_code_performance(error_rates, post_ec_error_rates; title="") + f = Figure(resolution=(500,300)) + ax = f[1,1] = Axis(f, xlabel="single (qu)bit error rate", ylabel="Logical error rate",title=title) + ax.aspect = DataAspect() + lim = max(error_rates[end],post_ec_error_rates[end]) + lines!([0,lim], [0,lim], label="single bit", color=:black) + plot!(error_rates, post_ec_error_rates, label="after decoding", color=:black) + xlims!(0,lim) + ylims!(0,lim) + f[1,2] = Legend(f, ax, "Error Rates") + f +end + +function pf_encoding_plot(code::AbstractECC, name=string(typeof(code))) + checks = parity_checks(code) + pf_encoding_plot(checks, name) +end + +function pf_encoding_plot(checks, name="") + (scirc, _), time1, _ = @timed naive_syndrome_circuit(checks) + # a = @timed naive_syndrome_circuit(checks) + # println(a) + # println(scirc) + # println(time1) + ecirc, time2, _ = @timed naive_encoding_circuit(checks) + # a = @timed naive_encoding_circuit(checks) + # println(a) + # println(ecirc) + # println(time2) + + error_rates = 0.000:0.0025:0.2 + post_ec_error_rates, time3, _ = @timed [evaluate_code_decoder_w_ecirc_pf(checks, ecirc, scirc, p) for p in error_rates] + # println(time3) + + total_time = round(time1 + time2 + time3, sigdigits=4) + + x_error = [post_ec_error_rates[i][1] for i in eachindex(post_ec_error_rates)] + z_error = [post_ec_error_rates[i][2] for i in eachindex(post_ec_error_rates)] + a_error = (x_error + z_error) / 2 + + f_x = plot_code_performance(error_rates, x_error,title=""*name*": Belief Decoder X @$total_time"*"s") + f_z = plot_code_performance(error_rates, z_error,title=""*name*": Belief Decoder Z @$total_time"*"s") + f_a = plot_code_performance(error_rates, a_error,title=""*name*": Belief Decoder @$total_time"*"s") + + return f_x, f_z, f_a, total_time +end \ No newline at end of file diff --git a/src/ecc/code_generation.jl b/src/ecc/code_generation.jl new file mode 100644 index 000000000..b0797714a --- /dev/null +++ b/src/ecc/code_generation.jl @@ -0,0 +1,298 @@ +# using QuantumClifford: Stabilizer +# using QuantumClifford.ECC: AbstractECC +# import QuantumClifford.ECC: parity_checks +# using Statistics:std +# using Nemo: residue_ring, matrix +# using LinearAlgebra: rank + +"""Struct for arbitrary CSS error correcting codes. + +This struct holds: + - tab: Boolean matrix with the X part taking up the left side and the Z part taking up the right side + - stab: Stabilizer of the parity check matrix + - n: Block length + - d: Code distance""" +struct CSS <: AbstractECC + tab::Matrix{Bool} + stab::Stabilizer + n::Int +end + +"""Takes an untrimmed bicycle matrix and removes the row which keeps the spread of the column weights minimal. + +Required before the bicycle code can be used. + +Typical usage: +ReduceBicycle(Circ2BicycleH0(array_indices, (block length / 2) ) )""" +function ReduceBicycle(H0::Matrix{Bool}) + m, n = size(H0) + r_i = 0 + std_min = Inf + for i in 1:m + t_H0 = vcat(H0[1:i-1, :], H0[i+1:end, :]) + std_temp = std(convert(Array, sum(t_H0, dims = 1))) + if std_temp < std_min + std_min = std_temp + r_i = i + end + end + return vcat(H0[1:r_i-1, :], H0[r_i+1:end, :]) +end + +"""Takes a list of indices and creates the base of the bicycle matrix. + +For example: +Circ2BicycleH0([1, 2, 4], 7) + +See https://arxiv.org/abs/quant-ph/0304161 for more details""" +function Circ2BicycleH0(circ_indices::Array{Int}, n::Int) + circ_arr = Array{Bool}(undef, n) + circ_matrix = Matrix{Bool}(undef, n, n) + comp_matrix = Matrix{Bool}(undef, n, 2*n) + for i = 1:n + if Int(i-1) in circ_indices + circ_arr[i] = true + else + circ_arr[i] = false + end + end + for i = 1:n + circ_matrix[i,1:n] = circ_arr + li = circ_arr[end] + circ_arr[2:end] = circ_arr[1:end-1] + circ_arr[1] = li + end + comp_matrix[1:n,1:n] = circ_matrix + comp_matrix[1:n,n+1:2*n] = transpose(circ_matrix) + return comp_matrix +end + +"""Takes an untrimmed unicycle matrix and removes linearly dependent rows. + +Required before the unicycle code can be used. + +Typical usage: +ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )""" +function ReduceUnicycle(m::Matrix{Bool}) + r = LinearAlgebra.rank(nm7) + rrzz = Nemo.residue_ring(Nemo.ZZ, 2) + for i in 1:size(u7)[1] + tm = vcat(m[1:i-1,:], m[i+1:end,:]) + tr = LinearAlgebra.rank(Nemo.matrix(rrzz, tm)) + if(tr == r) + m = tm + i -= 1 + if(size(m)[1] == r) + break + end + end + end + return m +end + +"""Takes a list of indices and creates the base of the unicycle matrix. + +For example: +Circ2UnicycleH0([1, 2, 4], 7) + +See https://arxiv.org/abs/quant-ph/0304161 for more details""" +function Circ2UnicycleH0(circ_indices::Array{Int}, n::Int) + circ_arr = fill(false, n) + one_col = transpose(fill(true, n)) + circ_matrix = Matrix{Bool}(undef, n, n) + comp_matrix = Matrix{Bool}(undef, n, n+1) + for i = 1:n + if i in circ_indices + circ_arr[i] = true + else + circ_arr[i] = false + end + end + for i = 1:n + circ_matrix[i,1:n] = circ_arr + li = circ_arr[end] + circ_arr[2:end] = circ_arr[1:end-1] + circ_arr[1] = li + end + comp_matrix[1:n,1:n] = circ_matrix + comp_matrix[1:n,n+1] = one_col + return comp_matrix +end + +function AssembleCSS end + +"""Creates a CSS code using the two provided matrices where H contains the X checks and G contains the Z checks.""" +function AssembleCSS(H::Matrix{Bool}, G::Matrix{Bool})::CSS + Hy, Hx = size(H) + Gy, Gx = size(G) + comp_matrix = fill(false, (Hy + Gy, Hx + Gx)) + # comp_matrix = Matrix{Bool}(undef, Hy + Gy, Hx + Gx) + comp_matrix[1:Hy, 1:Hx] = H + comp_matrix[Hy+1:end, Hx+1:end] = G + pcm_stab = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(comp_matrix), GetZTableau(comp_matrix)) + return CSS(comp_matrix, pcm_stab, Hx) + # return comp_matrix +end + +"""Creates a CSS code using the provided matrix for the X and Z checks.""" +function AssembleCSS(H::Matrix{Bool})::CSS + return AssembleCSS(H, H) +end + +"""Attempts to generate a list of indices to be used in a bicycle code using a search method""" +function BicycleSetGen(N::Int) + circ_arr::Array{Int} = [0] + diff_arr::Array{Int} = [] + circ_arr[1] = 0 + # test new elements + for add_i = (circ_arr[end] + 1):N - 1 + valid = true + temp_circ_arr = copy(circ_arr) + temp_diff_arr::Array{Int} = [] + push!(temp_circ_arr, add_i) + for j = 1:size(temp_circ_arr)[1] + temp_arr = copy(temp_circ_arr) + # add lesser elements + N to temp_arr + for k = 1:size(temp_circ_arr)[1] + if k < j + push!(temp_arr, temp_circ_arr[k] + N) + else + break + end + end + # test if new index is valid + for k = 1:(size(temp_circ_arr)[1] - 2) + t_diff = (temp_arr[j + k] - temp_arr[j]) % N + if ((t_diff) in temp_diff_arr) + valid = false + break + else + push!(temp_diff_arr, t_diff) + end + end + if !valid + break + end + end + if valid + circ_arr = copy(temp_circ_arr) + diff_arr = copy(temp_diff_arr) + end + end + return circ_arr +end + +"""Attempts to generate a list of indices to be used in a bicycle code using a randomized check method + +Note: This is very slow for large N""" +function BicycleSetGenRand(N::Int, d::Int) + circ_arr::Array{Int} = [0] + diff_arr::Array{Int} = [] + atmp_add::Array{Int} = [0] + circ_arr[1] = 0 + # test new elements + for i = (circ_arr[end] + 1):(N^2) + valid = true + temp_circ_arr = copy(circ_arr) + temp_diff_arr::Array{Int} = [] + add_i = rand(1: N-1) + atmp_add = push!(atmp_add, add_i) + if add_i in circ_arr + continue + end + push!(temp_circ_arr, add_i) + for j = 1:size(temp_circ_arr)[1] + temp_arr = copy(temp_circ_arr) + # add lesser elements + N to temp_arr + for k = 1:size(temp_circ_arr)[1] + if k < j + push!(temp_arr, temp_circ_arr[k] + N) + else + break + end + end + # test if new index is valid + for k = 1:(size(temp_circ_arr)[1] - 2) + t_diff = (temp_arr[j + k] - temp_arr[j]) % N + if ((t_diff) in temp_diff_arr) + valid = false + break + else + push!(temp_diff_arr, t_diff) + end + end + if !valid + break + end + end + if valid + circ_arr = copy(temp_circ_arr) + diff_arr = copy(temp_diff_arr) + if (size(atmp_add)[1] == N) || (size(circ_arr)[1] == d) + break + end + end + end + return circ_arr +end + +"""Takes in a boolean Matrix and returns the parity check tableau as a string of characters. + +Note: Only works when the block length for the X and Z checks are the same!""" +function GetCodeTableau(ecc::Matrix{Bool}) + eccx = size(ecc)[2] + eccy = size(ecc)[1] + ps::String = "" + for i = 1:size(ecc)[1] + for j = 1:(Int(size(ecc)[2]/2)) + if (ecc[i, j] == 0) && (ecc[i, j + Int(eccx / 2)] == 0) + ps = string(ps, "I") + elseif (ecc[i, j] == 1) && (ecc[i, j + Int(eccx / 2)] == 0) + ps = string(ps, "X") + elseif (ecc[i, j] == 1) && (ecc[i, j + Int(eccx / 2)] == 1) + ps = string(ps, "Y") + else + ps = string(ps, "Z") + end + end + ps = string(ps,"\n") + end + return ps +end + +"""Takes in a matrix and returns just the X checks portion while keeping the full height of the matrix. + +Note: Only works when the block length for the X and Z checks are the same!""" +function GetXTableau(ecc::Matrix{Bool}) + return ecc[1:size(ecc)[1], 1:Int(size(ecc)[2]/2)] +end + +"""Takes in a matrix and returns just the Z checks portion while keeping the full height of the matrix. + +Note: Only works when the block length for the X and Z checks are the same!""" +function GetZTableau(ecc::Matrix{Bool}) + return ecc[1:size(ecc)[1], Int(size(ecc)[2]/2) + 1:end] +end + +"""Takes in a matrix and returns just the X checks portion while keeping the full height of the matrix. + +Note: Only works when the block length for the X and Z checks are the same!""" +function GetXTableau(ecc::CSS) + return GetXTableau(ecc.tab) +end + +"""Takes in a matrix and returns just the Z checks portion while keeping the full height of the matrix. + +Note: Only works when the block length for the X and Z checks are the same!""" +function GetZTableau(ecc::CSS) + return GetZTableau(ecc.tab) +end + +"""Returns the matrix form of the X and Z checks.""" +tableau(c::CSS) = c.tab + +"""Returns the stabilizer making up the parity check tableau.""" +parity_checks(c::CSS) = c.stab + +"""Returns the block length of the code.""" +code_n(c::CSS) = c.n \ No newline at end of file From 92f6eeb8afcc315fbebb24ef7c38645f62b82f38 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Wed, 1 Nov 2023 00:24:23 -0400 Subject: [PATCH 02/21] Added dependencies Added the following dependencies: CairoMakie LDPCDecoders SparseArrays Statistics --- Project.toml | 4 ++++ src/ecc/ECC.jl | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ba999e0f4..c424d65c8 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Stefan Krastanov "] version = "0.8.18" [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" @@ -11,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" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a" @@ -18,6 +20,8 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SIMD = "fdea26ae-647d-5447-a871-4b548cad5224" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" SumTypes = "8e1ec7a9-0e02-4297-b0fe-6433085c89f2" [weakdeps] diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 897b00748..57304165c 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -4,7 +4,6 @@ using LinearAlgebra using QuantumClifford, CairoMakie, SparseArrays, LDPCDecoders using QuantumClifford: AbstractOperation, AbstractStabilizer, Stabilizer import QuantumClifford: Stabilizer, MixedDestabilizer -import QuantumClifford.ECC: parity_checks using DocStringExtensions using Combinatorics: combinations using Statistics: std From 1c0fcdb2d102960b9be007803dd33a0d4c2fd1a4 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Wed, 1 Nov 2023 15:41:46 -0400 Subject: [PATCH 03/21] Removed pauli frame code During discussion with Anthony, he mentioned that he would add his code himself later on --- Project.toml | 1 - src/ecc/ECC.jl | 3 +- src/ecc/code_evaluation.jl | 178 ------------------------------------- 3 files changed, 1 insertion(+), 181 deletions(-) delete mode 100644 src/ecc/code_evaluation.jl diff --git a/Project.toml b/Project.toml index c424d65c8..80baa6e81 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,6 @@ authors = ["Stefan Krastanov "] version = "0.8.18" [deps] -CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 57304165c..fb78cf303 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -1,7 +1,7 @@ module ECC using LinearAlgebra -using QuantumClifford, CairoMakie, SparseArrays, LDPCDecoders +using QuantumClifford, SparseArrays, LDPCDecoders using QuantumClifford: AbstractOperation, AbstractStabilizer, Stabilizer import QuantumClifford: Stabilizer, MixedDestabilizer using DocStringExtensions @@ -296,7 +296,6 @@ end include("circuits.jl") include("code_generation.jl") -include("code_evaluation.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") diff --git a/src/ecc/code_evaluation.jl b/src/ecc/code_evaluation.jl deleted file mode 100644 index 206d55934..000000000 --- a/src/ecc/code_evaluation.jl +++ /dev/null @@ -1,178 +0,0 @@ -# using QuantumClifford.ECC: faults_matrix, naive_syndrome_circuit, parity_checks, AbstractECC, naive_encoding_circuit, Cleve8, Steane7, Shor9, Perfect5 - -"""Generate a lookup table for decoding single qubit errors. Maps s⃗ → e⃗.""" -function create_lookup_table(code::Stabilizer) - lookup_table = Dict() - constraints, qubits = size(code) - # In the case of no errors - lookup_table[ zeros(UInt8, constraints) ] = zero(PauliOperator, qubits) - # In the case of single bit errors - for bit_to_be_flipped in 1:qubits - for error_type in [single_x, single_y, single_z] - # Generate e⃗ - error = error_type(qubits, bit_to_be_flipped) - # Calculate s⃗ - # (check which stabilizer rows do not commute with the Pauli error) - syndrome = comm(error, code) - # Store s⃗ → e⃗ - lookup_table[syndrome] = error - end - end - lookup_table -end - - """Currently uses a 1 qubit lookup table decoder. Assumes scirc is generated with naive_syndrome_circuit - I have a different function for fault tolerant syndromes circuits""" -function evaluate_code_decoder_w_ecirc_pf(checks::Stabilizer, ecirc, scirc, p_error; nframes=10_000, encoding_locs=nothing) - s, n = size(checks) - k = n-s - - pcm = stab_to_gf2(checks) - pcm_X = pcm[1:Int(s/2), 1:n] - pcm_Z = pcm[Int(s/2) + 1:end, n + 1:end] - - O = faults_matrix(checks) - circuit_Z = Base.copy(scirc) - circuit_X = Base.copy(scirc) - - # This is a where the bits to be encoded are, ecircs genereated by naive_encoding_circuit() will put those at the bottom - # Thus, the default is to apply this to the bottom k qubits - if isnothing(encoding_locs) - pre_X = [sHadamard(i) for i in n-k+1:n] - else - pre_X = [sHadamard(i) for i in encoding_locs] - end - - md = MixedDestabilizer(checks) - logview_Z = [ logicalzview(md);] - logcirc_Z, numLogBits, _ = naive_syndrome_circuit(logview_Z) # numLogBits shoudl equal k - - logview_X = [ logicalxview(md);] - logcirc_X, _ = naive_syndrome_circuit(logview_X) - - # Z logic circuit - for gate in logcirc_Z - type = typeof(gate) - if type == sMRZ - push!(circuit_Z, sMRZ(gate.qubit+s, gate.bit+s)) - else - push!(circuit_Z, type(gate.q1, gate.q2+s)) - end - end - - # X logic circuit - for gate in logcirc_X - type = typeof(gate) - if type == sMRZ - push!(circuit_X, sMRZ(gate.qubit+s, gate.bit+s)) - else - push!(circuit_X, type(gate.q1, gate.q2+s)) - end - end - - # Z simulation - errors = [PauliError(i,p_error) for i in 1:n] - - fullcircuit_Z = vcat(ecirc, errors, circuit_Z) - - frames = PauliFrame(nframes, n+s+k, s+k) - pftrajectories(frames, fullcircuit_Z) - syndromes = pfmeasurements(frames)[:, 1:s] - logicalSyndromes = pfmeasurements(frames)[:, s+1: s+k] - - decoded = 0 - for i in 1:nframes - row = syndromes[i,:] - - guess, success = syndrome_decode(sparse(pcm_Z), sparse(pcm_Z'), row[Int(s/2)+1:end], 50, fill(p_error, n), zeros(Int(s/2), n), zeros(Int(s/2), n), zeros(n), zeros(n)) - - guess = vcat(convert(Vector{Bool}, fill(0, n)), convert(Vector{Bool}, guess)) - - if isnothing(guess) - continue - else - result_Z = (O * guess)[k+1:2k] - if result_Z == logicalSyndromes[i,:] - if(i == 1) - end - decoded += 1 - end - end - end - z_error = 1 - decoded / nframes - - # X simulation - fullcircuit_X = vcat(pre_X, ecirc, errors, circuit_X) - frames = PauliFrame(nframes, n+s+k, s+k) - pftrajectories(frames, fullcircuit_X) - syndromes = pfmeasurements(frames)[:, 1:s] - logicalSyndromes = pfmeasurements(frames)[:, s+1: s+k] - - decoded = 0 - for i in 1:nframes - row = syndromes[i,:] - - guess, success = syndrome_decode(sparse(pcm_X), sparse(pcm_X'), row[1:Int(s/2)], 50, fill(p_error, n), zeros(Int(s/2), n), zeros(Int(s/2), n), zeros(n), zeros(n)) - - guess = vcat(convert(Vector{Bool}, guess), convert(Vector{Bool}, fill(0, n))) - - if isnothing(guess) - continue - else - result_X = (O * guess)[1:k] - if result_X == logicalSyndromes[i,:] - decoded += 1 - end - end - end - x_error = 1 - decoded / nframes - - return x_error, z_error -end - -"""Taken from the QEC Seminar notebook for plotting logical vs physical error""" -function plot_code_performance(error_rates, post_ec_error_rates; title="") - f = Figure(resolution=(500,300)) - ax = f[1,1] = Axis(f, xlabel="single (qu)bit error rate", ylabel="Logical error rate",title=title) - ax.aspect = DataAspect() - lim = max(error_rates[end],post_ec_error_rates[end]) - lines!([0,lim], [0,lim], label="single bit", color=:black) - plot!(error_rates, post_ec_error_rates, label="after decoding", color=:black) - xlims!(0,lim) - ylims!(0,lim) - f[1,2] = Legend(f, ax, "Error Rates") - f -end - -function pf_encoding_plot(code::AbstractECC, name=string(typeof(code))) - checks = parity_checks(code) - pf_encoding_plot(checks, name) -end - -function pf_encoding_plot(checks, name="") - (scirc, _), time1, _ = @timed naive_syndrome_circuit(checks) - # a = @timed naive_syndrome_circuit(checks) - # println(a) - # println(scirc) - # println(time1) - ecirc, time2, _ = @timed naive_encoding_circuit(checks) - # a = @timed naive_encoding_circuit(checks) - # println(a) - # println(ecirc) - # println(time2) - - error_rates = 0.000:0.0025:0.2 - post_ec_error_rates, time3, _ = @timed [evaluate_code_decoder_w_ecirc_pf(checks, ecirc, scirc, p) for p in error_rates] - # println(time3) - - total_time = round(time1 + time2 + time3, sigdigits=4) - - x_error = [post_ec_error_rates[i][1] for i in eachindex(post_ec_error_rates)] - z_error = [post_ec_error_rates[i][2] for i in eachindex(post_ec_error_rates)] - a_error = (x_error + z_error) / 2 - - f_x = plot_code_performance(error_rates, x_error,title=""*name*": Belief Decoder X @$total_time"*"s") - f_z = plot_code_performance(error_rates, z_error,title=""*name*": Belief Decoder Z @$total_time"*"s") - f_a = plot_code_performance(error_rates, a_error,title=""*name*": Belief Decoder @$total_time"*"s") - - return f_x, f_z, f_a, total_time -end \ No newline at end of file From f77b13530d7e295b79b99aa70e7806ef061c0855 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sun, 5 Nov 2023 14:39:32 -0500 Subject: [PATCH 04/21] Fixed naming and organization issues + - Fixed naming and organization issued identified. - Limited exports - Added additional methods to easily create unicycle and bicycle codes --- src/ecc/ECC.jl | 12 +- src/ecc/css.jl | 31 ++++ ...e_generation.jl => simple_sparse_codes.jl} | 140 +++++------------- 3 files changed, 74 insertions(+), 109 deletions(-) create mode 100644 src/ecc/css.jl rename src/ecc/{code_generation.jl => simple_sparse_codes.jl} (60%) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index fb78cf303..55a1f283d 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -14,10 +14,11 @@ abstract type AbstractECC end export Shor9, Steane7, Cleve8, Perfect5, Bitflip3, parity_checks, naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, code_n, code_s, code_k, rate, distance, - isdegenerate, faults_matrix, CSS_Code, Bicycle_Code, Unicycle_Code, Circ2BicycleH0, - Circ2UnicycleH0, AssembleCSS, BicycleSetGen, BicycleSetGenRand, GetCodeTableau, - GetXTableau, GetZTableau, parity_checks, ReduceBicycle, ReduceUnicycle, - create_lookup_table, evaluate_code_decoder_w_ecirc_pf, plot_code_performance, pf_encoding_plot + Unicycle, Bicycle, + CSS, get_xzs + # is_degenerate, faults_matrix, CSSCode, BicycleCode, UnicycleCode, circ_to_bicycle_h0, + # circ_to_unicycle_h0, assemble_css, bicycle_set_gen, bicycle_set_gen_rand, + # get_x_tableau, get_z_tableau, parity_checks, reduce_bicycle, reduce_unicycle """Parity check tableau of a code.""" @@ -295,7 +296,8 @@ function isdegenerate(H::Stabilizer, d::Int=1) end include("circuits.jl") -include("code_generation.jl") +include("css.jl") +include("simple_sparse_codes.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") diff --git a/src/ecc/css.jl b/src/ecc/css.jl new file mode 100644 index 000000000..7b1e41800 --- /dev/null +++ b/src/ecc/css.jl @@ -0,0 +1,31 @@ +"""Struct for arbitrary CSS error correcting codes. + +This struct holds: + - tab: Boolean matrix with the X part taking up the left side and the Z part taking up the right side""" +struct CSS <: ECC + tab::Matrix{Bool} +end + +function CSS end + +"""Creates a CSS code using the two provided matrices where H contains the X checks and G contains the Z checks.""" +function CSS(H::Matrix{Bool}, G::Matrix{Bool})::CSS + Hy, Hx = size(H) + Gy, Gx = size(G) + comp_matrix = fill(false, (Hy + Gy, Hx + Gx)) + # comp_matrix = Matrix{Bool}(undef, Hy + Gy, Hx + Gx) + comp_matrix[1:Hy, 1:Hx] = H + comp_matrix[Hy+1:end, Hx+1:end] = G + pcm_stab = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(comp_matrix), GetZTableau(comp_matrix)) + return CSS(comp_matrix) + # return comp_matrix +end + +"""Returns the matrix form of the X and Z checks.""" +get_xzs(c::CSS) = c.tab + +"""Returns the stabilizer making up the parity check tableau.""" +parity_checks(c::CSS) = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(c.tab), GetZTableau(c.tab)) + +"""Returns the block length of the code.""" +code_n(c::CSS) = code_n(Stabilizer(fill(0x0, Hy+Gy), GetXTableau(c.tab), GetZTableau(c.tab))) \ No newline at end of file diff --git a/src/ecc/code_generation.jl b/src/ecc/simple_sparse_codes.jl similarity index 60% rename from src/ecc/code_generation.jl rename to src/ecc/simple_sparse_codes.jl index b0797714a..96dae4f94 100644 --- a/src/ecc/code_generation.jl +++ b/src/ecc/simple_sparse_codes.jl @@ -1,21 +1,34 @@ -# using QuantumClifford: Stabilizer -# using QuantumClifford.ECC: AbstractECC -# import QuantumClifford.ECC: parity_checks -# using Statistics:std -# using Nemo: residue_ring, matrix -# using LinearAlgebra: rank +# Currently just has Bicycle and Unicycle codes, but open to all types of rudimentary sparse codes -"""Struct for arbitrary CSS error correcting codes. +"""Takes a height and width of matrix and generates a bicycle code to the specified height and width. -This struct holds: - - tab: Boolean matrix with the X part taking up the left side and the Z part taking up the right side - - stab: Stabilizer of the parity check matrix - - n: Block length - - d: Code distance""" -struct CSS <: AbstractECC - tab::Matrix{Bool} - stab::Stabilizer - n::Int +Parameters: +- n: width of array, should be >= 2 +- m: height of array, should be >= 2 and a multiple of 2""" +function Bicycle(n::Integer, m::Integer) + if m%2 == 1 + throw(DomainError(m, " M should be a multiple for 2 for bicycle codes.")) + end + if m < 2 + throw(DomainError(m, " M is too small, make it greater than 1.")) + end + bs = bicycle_set_gen(n/2) + bsc = circ_to_bicycle_h0(bs, n/2) + while size(bsc)[2] > m/2 + bsc = reduce_bicycle(bsc) + end + return assemble_css(bsc, bsc) +end + +"""Takes a height and width of matrix and generates a bicycle code to the specified height and width. + +Parameters: +- n: width of array, should be >= 1 +- set: array of indices that are 'active' checks in the circulant code""" +function Unicycle(n::Integer, set::Array{Integer}) + usc = circ_to_unicycle_h0(bs, n) + usc = reduce_bicycle(usc) + return assemble_css(usc, usc) end """Takes an untrimmed bicycle matrix and removes the row which keeps the spread of the column weights minimal. @@ -24,7 +37,7 @@ Required before the bicycle code can be used. Typical usage: ReduceBicycle(Circ2BicycleH0(array_indices, (block length / 2) ) )""" -function ReduceBicycle(H0::Matrix{Bool}) +function reduce_bicycle(H0::Matrix{Bool}) m, n = size(H0) r_i = 0 std_min = Inf @@ -45,7 +58,7 @@ For example: Circ2BicycleH0([1, 2, 4], 7) See https://arxiv.org/abs/quant-ph/0304161 for more details""" -function Circ2BicycleH0(circ_indices::Array{Int}, n::Int) +function circ_to_bicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = Array{Bool}(undef, n) circ_matrix = Matrix{Bool}(undef, n, n) comp_matrix = Matrix{Bool}(undef, n, 2*n) @@ -73,7 +86,7 @@ Required before the unicycle code can be used. Typical usage: ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )""" -function ReduceUnicycle(m::Matrix{Bool}) +function reduce_unicycle(m::Matrix{Bool}) r = LinearAlgebra.rank(nm7) rrzz = Nemo.residue_ring(Nemo.ZZ, 2) for i in 1:size(u7)[1] @@ -96,7 +109,7 @@ For example: Circ2UnicycleH0([1, 2, 4], 7) See https://arxiv.org/abs/quant-ph/0304161 for more details""" -function Circ2UnicycleH0(circ_indices::Array{Int}, n::Int) +function circ_to_unicycleH0(circ_indices::Array{Int}, n::Int) circ_arr = fill(false, n) one_col = transpose(fill(true, n)) circ_matrix = Matrix{Bool}(undef, n, n) @@ -119,28 +132,8 @@ function Circ2UnicycleH0(circ_indices::Array{Int}, n::Int) return comp_matrix end -function AssembleCSS end - -"""Creates a CSS code using the two provided matrices where H contains the X checks and G contains the Z checks.""" -function AssembleCSS(H::Matrix{Bool}, G::Matrix{Bool})::CSS - Hy, Hx = size(H) - Gy, Gx = size(G) - comp_matrix = fill(false, (Hy + Gy, Hx + Gx)) - # comp_matrix = Matrix{Bool}(undef, Hy + Gy, Hx + Gx) - comp_matrix[1:Hy, 1:Hx] = H - comp_matrix[Hy+1:end, Hx+1:end] = G - pcm_stab = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(comp_matrix), GetZTableau(comp_matrix)) - return CSS(comp_matrix, pcm_stab, Hx) - # return comp_matrix -end - -"""Creates a CSS code using the provided matrix for the X and Z checks.""" -function AssembleCSS(H::Matrix{Bool})::CSS - return AssembleCSS(H, H) -end - """Attempts to generate a list of indices to be used in a bicycle code using a search method""" -function BicycleSetGen(N::Int) +function bicycle_set_gen(N::Int) circ_arr::Array{Int} = [0] diff_arr::Array{Int} = [] circ_arr[1] = 0 @@ -185,7 +178,7 @@ end """Attempts to generate a list of indices to be used in a bicycle code using a randomized check method Note: This is very slow for large N""" -function BicycleSetGenRand(N::Int, d::Int) +function bicycle_set_gen_rand(N::Int, d::Int) circ_arr::Array{Int} = [0] diff_arr::Array{Int} = [] atmp_add::Array{Int} = [0] @@ -234,65 +227,4 @@ function BicycleSetGenRand(N::Int, d::Int) end end return circ_arr -end - -"""Takes in a boolean Matrix and returns the parity check tableau as a string of characters. - -Note: Only works when the block length for the X and Z checks are the same!""" -function GetCodeTableau(ecc::Matrix{Bool}) - eccx = size(ecc)[2] - eccy = size(ecc)[1] - ps::String = "" - for i = 1:size(ecc)[1] - for j = 1:(Int(size(ecc)[2]/2)) - if (ecc[i, j] == 0) && (ecc[i, j + Int(eccx / 2)] == 0) - ps = string(ps, "I") - elseif (ecc[i, j] == 1) && (ecc[i, j + Int(eccx / 2)] == 0) - ps = string(ps, "X") - elseif (ecc[i, j] == 1) && (ecc[i, j + Int(eccx / 2)] == 1) - ps = string(ps, "Y") - else - ps = string(ps, "Z") - end - end - ps = string(ps,"\n") - end - return ps -end - -"""Takes in a matrix and returns just the X checks portion while keeping the full height of the matrix. - -Note: Only works when the block length for the X and Z checks are the same!""" -function GetXTableau(ecc::Matrix{Bool}) - return ecc[1:size(ecc)[1], 1:Int(size(ecc)[2]/2)] -end - -"""Takes in a matrix and returns just the Z checks portion while keeping the full height of the matrix. - -Note: Only works when the block length for the X and Z checks are the same!""" -function GetZTableau(ecc::Matrix{Bool}) - return ecc[1:size(ecc)[1], Int(size(ecc)[2]/2) + 1:end] -end - -"""Takes in a matrix and returns just the X checks portion while keeping the full height of the matrix. - -Note: Only works when the block length for the X and Z checks are the same!""" -function GetXTableau(ecc::CSS) - return GetXTableau(ecc.tab) -end - -"""Takes in a matrix and returns just the Z checks portion while keeping the full height of the matrix. - -Note: Only works when the block length for the X and Z checks are the same!""" -function GetZTableau(ecc::CSS) - return GetZTableau(ecc.tab) -end - -"""Returns the matrix form of the X and Z checks.""" -tableau(c::CSS) = c.tab - -"""Returns the stabilizer making up the parity check tableau.""" -parity_checks(c::CSS) = c.stab - -"""Returns the block length of the code.""" -code_n(c::CSS) = c.n \ No newline at end of file +end \ No newline at end of file From 6c3434c51e0970ef8224a0beb169ba67ff2544ea Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 8 Nov 2023 13:58:57 -0500 Subject: [PATCH 05/21] minor cleanup --- src/ecc/ECC.jl | 7 ++----- src/ecc/css.jl | 25 ++++++++----------------- src/ecc/simple_sparse_codes.jl | 6 +++--- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 55a1f283d..65e084e23 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -14,12 +14,9 @@ abstract type AbstractECC end export Shor9, Steane7, Cleve8, Perfect5, Bitflip3, parity_checks, naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, code_n, code_s, code_k, rate, distance, + isdegenerate, faults_matrix, Unicycle, Bicycle, - CSS, get_xzs - # is_degenerate, faults_matrix, CSSCode, BicycleCode, UnicycleCode, circ_to_bicycle_h0, - # circ_to_unicycle_h0, assemble_css, bicycle_set_gen, bicycle_set_gen_rand, - # get_x_tableau, get_z_tableau, parity_checks, reduce_bicycle, reduce_unicycle - + CSS """Parity check tableau of a code.""" function parity_checks end diff --git a/src/ecc/css.jl b/src/ecc/css.jl index 7b1e41800..58b46b408 100644 --- a/src/ecc/css.jl +++ b/src/ecc/css.jl @@ -1,31 +1,22 @@ -"""Struct for arbitrary CSS error correcting codes. - -This struct holds: - - tab: Boolean matrix with the X part taking up the left side and the Z part taking up the right side""" +"""An arbitrary CSS error correcting code defined by its X and Z checks.""" struct CSS <: ECC - tab::Matrix{Bool} + tab end -function CSS end - """Creates a CSS code using the two provided matrices where H contains the X checks and G contains the Z checks.""" -function CSS(H::Matrix{Bool}, G::Matrix{Bool})::CSS +function CSS(H, G) Hy, Hx = size(H) Gy, Gx = size(G) - comp_matrix = fill(false, (Hy + Gy, Hx + Gx)) - # comp_matrix = Matrix{Bool}(undef, Hy + Gy, Hx + Gx) + comp_matrix = falses(Hy + Gy, Hx + Gx) comp_matrix[1:Hy, 1:Hx] = H comp_matrix[Hy+1:end, Hx+1:end] = G - pcm_stab = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(comp_matrix), GetZTableau(comp_matrix)) return CSS(comp_matrix) - # return comp_matrix end -"""Returns the matrix form of the X and Z checks.""" -get_xzs(c::CSS) = c.tab - """Returns the stabilizer making up the parity check tableau.""" -parity_checks(c::CSS) = Stabilizer(fill(0x0, Hy+Gy), GetXTableau(c.tab), GetZTableau(c.tab)) +function parity_checks(c::CSS) + Stabilizer(fill(0x0, size(c.tab, 2)), c.tab[:,1:end÷2], c.tab[:,end÷2+1:end]) +end """Returns the block length of the code.""" -code_n(c::CSS) = code_n(Stabilizer(fill(0x0, Hy+Gy), GetXTableau(c.tab), GetZTableau(c.tab))) \ No newline at end of file +code_n(c::CSS) = size(c.tab,2)÷2 diff --git a/src/ecc/simple_sparse_codes.jl b/src/ecc/simple_sparse_codes.jl index 96dae4f94..2330de25e 100644 --- a/src/ecc/simple_sparse_codes.jl +++ b/src/ecc/simple_sparse_codes.jl @@ -5,7 +5,7 @@ Parameters: - n: width of array, should be >= 2 - m: height of array, should be >= 2 and a multiple of 2""" -function Bicycle(n::Integer, m::Integer) +function Bicycle(n::Int, m::Int) if m%2 == 1 throw(DomainError(m, " M should be a multiple for 2 for bicycle codes.")) end @@ -25,7 +25,7 @@ end Parameters: - n: width of array, should be >= 1 - set: array of indices that are 'active' checks in the circulant code""" -function Unicycle(n::Integer, set::Array{Integer}) +function Unicycle(n::Int, set) usc = circ_to_unicycle_h0(bs, n) usc = reduce_bicycle(usc) return assemble_css(usc, usc) @@ -227,4 +227,4 @@ function bicycle_set_gen_rand(N::Int, d::Int) end end return circ_arr -end \ No newline at end of file +end From 077e2871820d378a58f3605daa305d15a2c70ea7 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Mon, 13 Nov 2023 02:40:09 -0500 Subject: [PATCH 06/21] Fixed bugs preventing the generation of Bicycle and Unicycle codes - Fixed issues with typing in bicycle gen methods - Fixed issue with incorrectly copied methods for unicycle generation - Fixed imports for Nemo methods --- src/ecc/ECC.jl | 1 + src/ecc/css.jl | 2 +- src/ecc/simple_sparse_codes.jl | 25 +++++++++++++------------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 65e084e23..a57956d2b 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -8,6 +8,7 @@ using DocStringExtensions using Combinatorics: combinations using Statistics: std using LinearAlgebra: rank +using Nemo: ZZ, residue_ring, matrix abstract type AbstractECC end diff --git a/src/ecc/css.jl b/src/ecc/css.jl index 58b46b408..290c8f2b0 100644 --- a/src/ecc/css.jl +++ b/src/ecc/css.jl @@ -1,5 +1,5 @@ """An arbitrary CSS error correcting code defined by its X and Z checks.""" -struct CSS <: ECC +struct CSS <: AbstractECC tab end diff --git a/src/ecc/simple_sparse_codes.jl b/src/ecc/simple_sparse_codes.jl index 2330de25e..8ac48f358 100644 --- a/src/ecc/simple_sparse_codes.jl +++ b/src/ecc/simple_sparse_codes.jl @@ -12,12 +12,12 @@ function Bicycle(n::Int, m::Int) if m < 2 throw(DomainError(m, " M is too small, make it greater than 1.")) end - bs = bicycle_set_gen(n/2) - bsc = circ_to_bicycle_h0(bs, n/2) - while size(bsc)[2] > m/2 + bs = bicycle_set_gen(Int(n/2)) + bsc = circ_to_bicycle_h0(bs, Int(n/2)) + while size(bsc)[1] > m/2 bsc = reduce_bicycle(bsc) end - return assemble_css(bsc, bsc) + return CSS(bsc, bsc) end """Takes a height and width of matrix and generates a bicycle code to the specified height and width. @@ -26,9 +26,9 @@ Parameters: - n: width of array, should be >= 1 - set: array of indices that are 'active' checks in the circulant code""" function Unicycle(n::Int, set) - usc = circ_to_unicycle_h0(bs, n) - usc = reduce_bicycle(usc) - return assemble_css(usc, usc) + usc = circ_to_unicycle_h0(set, n) + rusc = reduce_unicycle(usc) + return CSS(rusc, rusc) end """Takes an untrimmed bicycle matrix and removes the row which keeps the spread of the column weights minimal. @@ -87,11 +87,12 @@ Required before the unicycle code can be used. Typical usage: ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )""" function reduce_unicycle(m::Matrix{Bool}) - r = LinearAlgebra.rank(nm7) - rrzz = Nemo.residue_ring(Nemo.ZZ, 2) - for i in 1:size(u7)[1] + rrzz = residue_ring(ZZ, 2) + nm = matrix(rrzz, m) + r = LinearAlgebra.rank(nm) + for i in 1:size(m)[1] tm = vcat(m[1:i-1,:], m[i+1:end,:]) - tr = LinearAlgebra.rank(Nemo.matrix(rrzz, tm)) + tr = LinearAlgebra.rank(matrix(rrzz, tm)) if(tr == r) m = tm i -= 1 @@ -109,7 +110,7 @@ For example: Circ2UnicycleH0([1, 2, 4], 7) See https://arxiv.org/abs/quant-ph/0304161 for more details""" -function circ_to_unicycleH0(circ_indices::Array{Int}, n::Int) +function circ_to_unicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = fill(false, n) one_col = transpose(fill(true, n)) circ_matrix = Matrix{Bool}(undef, n, n) From 81773e368fa64f6067d22e97e01d4babb593d910 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:14:16 -0500 Subject: [PATCH 07/21] Update Project.toml Remove LDPCDecoders import Co-authored-by: Stefan Krastanov --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 80baa6e81..b628645a2 100644 --- a/Project.toml +++ b/Project.toml @@ -11,7 +11,6 @@ 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" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a" From cac41337092e833e819178c89b97afd832d968f2 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:14:31 -0500 Subject: [PATCH 08/21] Update src/ecc/ECC.jl Remove LDPCDecoders import Co-authored-by: Stefan Krastanov --- src/ecc/ECC.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index a57956d2b..dbdc25f09 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -1,7 +1,7 @@ module ECC using LinearAlgebra -using QuantumClifford, SparseArrays, LDPCDecoders +using QuantumClifford, SparseArrays using QuantumClifford: AbstractOperation, AbstractStabilizer, Stabilizer import QuantumClifford: Stabilizer, MixedDestabilizer using DocStringExtensions From 2eb3eb4b34742c6f9b1a2a4c0877e78db1f5a41e Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:16:40 -0500 Subject: [PATCH 09/21] Update src/ecc/simple_sparse_codes.jl Updated use case description Co-authored-by: Stefan Krastanov --- src/ecc/simple_sparse_codes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecc/simple_sparse_codes.jl b/src/ecc/simple_sparse_codes.jl index 8ac48f358..1930cda95 100644 --- a/src/ecc/simple_sparse_codes.jl +++ b/src/ecc/simple_sparse_codes.jl @@ -107,7 +107,7 @@ end """Takes a list of indices and creates the base of the unicycle matrix. For example: -Circ2UnicycleH0([1, 2, 4], 7) +`Circ2UnicycleH0([1, 2, 4], 7)` See https://arxiv.org/abs/quant-ph/0304161 for more details""" function circ_to_unicycle_h0(circ_indices::Array{Int}, n::Int) From 49046fece58515071f32e1dec1664861e8b5b6f0 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:17:18 -0500 Subject: [PATCH 10/21] Update src/ecc/ECC.jl Remove unused import Co-authored-by: Stefan Krastanov --- src/ecc/ECC.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index dbdc25f09..8df97db5b 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -7,7 +7,6 @@ import QuantumClifford: Stabilizer, MixedDestabilizer using DocStringExtensions using Combinatorics: combinations using Statistics: std -using LinearAlgebra: rank using Nemo: ZZ, residue_ring, matrix abstract type AbstractECC end From f86cd86d505a6149da6b2cc8a22bb31c092e0efe Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:19:28 -0500 Subject: [PATCH 11/21] Update src/ecc/simple_sparse_codes.jl Update use case of Bicycle matrix Co-authored-by: Stefan Krastanov --- src/ecc/simple_sparse_codes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ecc/simple_sparse_codes.jl b/src/ecc/simple_sparse_codes.jl index 1930cda95..ff1ecd3ab 100644 --- a/src/ecc/simple_sparse_codes.jl +++ b/src/ecc/simple_sparse_codes.jl @@ -85,7 +85,7 @@ end Required before the unicycle code can be used. Typical usage: -ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )""" +`ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )`""" function reduce_unicycle(m::Matrix{Bool}) rrzz = residue_ring(ZZ, 2) nm = matrix(rrzz, m) From bd823ef53d9aacf8e5fd1e0c3a3d03e13b95f807 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Tue, 19 Dec 2023 02:20:49 -0500 Subject: [PATCH 12/21] Moved CSS files to codes folder and updated CSS struct Updated CSS struct to use Hx and Hz instead of just the full tableau form. Updated CSS methods to work with the new struct form --- .vscode/settings.json | 3 ++ src/ecc/ECC.jl | 5 +-- src/ecc/codes/css.jl | 37 ++++++++++++++++++++++ src/ecc/{ => codes}/simple_sparse_codes.jl | 0 src/ecc/css.jl | 22 ------------- 5 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/ecc/codes/css.jl rename src/ecc/{ => codes}/simple_sparse_codes.jl (100%) delete mode 100644 src/ecc/css.jl diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..b4527bc30 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "julia.environmentPath": "c:\\Users\\benku\\Desktop\\College Documents\\Krastanov Independent Study\\QuantumClifford.jl-CSS_Builder" +} \ No newline at end of file diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 8df97db5b..6ca1f650d 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -293,8 +293,6 @@ function isdegenerate(H::Stabilizer, d::Int=1) end include("circuits.jl") -include("css.jl") -include("simple_sparse_codes.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") @@ -302,4 +300,7 @@ include("codes/steanecode.jl") include("codes/shorcode.jl") include("codes/clevecode.jl") +include("codes/css.jl") +include("codes/simple_sparse_codes.jl") + end #module diff --git a/src/ecc/codes/css.jl b/src/ecc/codes/css.jl new file mode 100644 index 000000000..7ed481abf --- /dev/null +++ b/src/ecc/codes/css.jl @@ -0,0 +1,37 @@ +"""An arbitrary CSS error correcting code defined by its X and Z checks.""" +struct CSS <: AbstractECC + Hx + Hz + """Creates a CSS code using the two provided matrices where Hx contains the X checks and Hz contains the Z checks.""" + function CSS(Hx, Hz) + n = size(Hx, 2) + if n != size(Hz, 2) error("When constructing a CSS quantum code, the two classical codes are required to have the same block size") end + if size(Hx,1)+size(Hz,1) >= n error("When constructing a CSS quantum code, the total number of checks (rows) in the parity checks of the two classical codes have to be lower than the block size (the number of columns).") end + return new(Hx, Hz) + end +end + +function boolean_tableau(c::CSS) + Hx_height, Hx_width = size(c.Hx) + Hz_height, Hz_width = size(x.Hz) + checks_matrix = falses(Hx_height + Hz_height, Hx_width + Hz_width) + checks_matrix[1:Hx_height, 1:Hx_width] = c.Hx + checks_matrix[Hx_height+1:end, Hx_width+1:end] = c.Hz + return CSS(checks_matrix) +end + +"""Returns the stabilizer making up the parity check tableau.""" +function parity_checks(c::CSS) + extended_Hx = Matrix{Bool}(vcat(c.Hx, zeros(size(c.Hz)))) + extended_Hz = Matrix{Bool}(vcat(zeros(size(c.Hx)), c.Hz)) + Stabilizer(fill(0x0, size(c.Hx, 2) + size(c.Hz, 2)), extended_Hx, extended_Hz) +end + +"""Returns the block length of the code.""" +code_n(c::CSS) = size(c.Hx,2) + +"""Returns the depth of the parity check matrix""" +code_m(c::CSS) = size(c.Hx, 1) + size(c.Hz, 1) + +"""Returns the number of encoded qubits""" +code_k(c::CSS) = (2 * size(c.Hx,2)) - code_m(c) \ No newline at end of file diff --git a/src/ecc/simple_sparse_codes.jl b/src/ecc/codes/simple_sparse_codes.jl similarity index 100% rename from src/ecc/simple_sparse_codes.jl rename to src/ecc/codes/simple_sparse_codes.jl diff --git a/src/ecc/css.jl b/src/ecc/css.jl deleted file mode 100644 index 290c8f2b0..000000000 --- a/src/ecc/css.jl +++ /dev/null @@ -1,22 +0,0 @@ -"""An arbitrary CSS error correcting code defined by its X and Z checks.""" -struct CSS <: AbstractECC - tab -end - -"""Creates a CSS code using the two provided matrices where H contains the X checks and G contains the Z checks.""" -function CSS(H, G) - Hy, Hx = size(H) - Gy, Gx = size(G) - comp_matrix = falses(Hy + Gy, Hx + Gx) - comp_matrix[1:Hy, 1:Hx] = H - comp_matrix[Hy+1:end, Hx+1:end] = G - return CSS(comp_matrix) -end - -"""Returns the stabilizer making up the parity check tableau.""" -function parity_checks(c::CSS) - Stabilizer(fill(0x0, size(c.tab, 2)), c.tab[:,1:end÷2], c.tab[:,end÷2+1:end]) -end - -"""Returns the block length of the code.""" -code_n(c::CSS) = size(c.tab,2)÷2 From 8a8f89d07391ffe51d70ed04fb709169de175e3f Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 30 Dec 2023 00:54:37 -0500 Subject: [PATCH 13/21] Small update to fix Stabilizer creation error Instead of adding the widths of the two CSS sub-matrices, instead adds the heights --- src/ecc/codes/css.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecc/codes/css.jl b/src/ecc/codes/css.jl index 7ed481abf..4c1a92fa3 100644 --- a/src/ecc/codes/css.jl +++ b/src/ecc/codes/css.jl @@ -24,7 +24,7 @@ end function parity_checks(c::CSS) extended_Hx = Matrix{Bool}(vcat(c.Hx, zeros(size(c.Hz)))) extended_Hz = Matrix{Bool}(vcat(zeros(size(c.Hx)), c.Hz)) - Stabilizer(fill(0x0, size(c.Hx, 2) + size(c.Hz, 2)), extended_Hx, extended_Hz) + Stabilizer(fill(0x0, size(c.Hx, 1) + size(c.Hz, 1)), extended_Hx, extended_Hz) end """Returns the block length of the code.""" @@ -34,4 +34,4 @@ code_n(c::CSS) = size(c.Hx,2) code_m(c::CSS) = size(c.Hx, 1) + size(c.Hz, 1) """Returns the number of encoded qubits""" -code_k(c::CSS) = (2 * size(c.Hx,2)) - code_m(c) \ No newline at end of file +code_k(c::CSS) = (2 * size(c.Hx,2)) - code_m(c) From 938ed97d45704d2312a32d19490b5a12856ae85e Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 30 Dec 2023 01:00:31 -0500 Subject: [PATCH 14/21] Added decoder pipeline code from LDPCDecoders PR Added decoder pipeline, still needs to be tested as this is just an initial commit to build off of --- src/ecc/ECC.jl | 1 + src/ecc/decoder_pipeline.jl | 228 ++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/ecc/decoder_pipeline.jl diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 6ca1f650d..6a4938fed 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -293,6 +293,7 @@ function isdegenerate(H::Stabilizer, d::Int=1) end include("circuits.jl") +include("decoder_pipeline.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl new file mode 100644 index 000000000..af3538b80 --- /dev/null +++ b/src/ecc/decoder_pipeline.jl @@ -0,0 +1,228 @@ +abstract type AbstractSyndromeDecoder end + +function evaluate_decoder(d::AbstractSyndromeDecoder, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func) + pre_X = [sHadamard(i) for i in n-k+1:n] + X_error = evaluate_classical_decoder(d, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logicalxview, 1, d.k, pre_X) + Z_error = evaluate_classical_decoder(d, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logicalzview, d.k + 1, 2 * d.k) + return (X_error, Z_error) +end + +function evaluate_classical_decoder(d::AbstractSyndromeDecoder, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logical_view_function, guess_start, guess_stop, pre_circuit = nothing) + H = d.H + O = d.faults_matrix + syndrome_circuit = syndrome_circuit_func(H) + + n = d.n + s = d.s + k = d.k + + errors = [PauliError(i, init_error) for i in 1:n]; + + md = MixedDestabilizer(H) + + full_circuit = [] + + logview = logical_view_function(md) + logcirc, _ = syndrome_circuit_func(logview) + + noisy_syndrome_circuit = add_two_qubit_gate_noise(syndrome_circuit, gate_error); + + for gate in logcirc + type = typeof(gate) + if type == sMRZ + push!(syndrome_circuit, sMRZ(gate.qubit+s, gate.bit+s)) + else + push!(syndrome_circuit, type(gate.q1, gate.q2+s)) + end + end + + ecirc = encoding_circuit_func(syndrome_circuit) + if isnothing(pre_circuit) + full_circuit = vcat(pre_circuits, ecirc, errors, noisy_syndrome_circuit) + else + full_circuit = vcat(ecirc, errors, noisy_syndrome_circuit) + end + + frames = PauliFrame(nframes, n+s+k, s+k) + pftrajectories(frames, full_circuit) + syndromes = pfmeasurements(frames)[:, 1:s] + logical_syndromes = pfmeasurements(frames)[:, s+1: s+k] + + for i in 1:nsamples + guess = decode(d, syndromes[i]) + + # result should be concatinated guess of the X and Z checks + result = (O * (guess))[guess_start:guess_stop] + + if result == logical_syndromes[i] + decoded += 1 + end + end + + return (nsamples - decoded) / nsamples +end + +struct TableDecoder <: AbstractSyndromeDecoder + """Stabilizer tableau defining the code""" + H + """Faults matrix corresponding to the code""" + faults_matrix + """The number of qubits in the code""" + n + """The depth of the code""" + s + """The number of encoded qubits""" + k + """The lookup table corresponding to the code, slow to create""" + lookup_table + """The time taken to create the lookup table + decode the code a specified number of time""" + time +end + +function TableDecoder(Hx, Hz) + c = CSS(Hx, Hz) + H = parity_checks(c) + s, n = size(H) + _, _, r = canonicalize!(Base.copy(H), ranks=true) + k = n - r + lookup_table, time, _ = @timed create_lookup_table(H) + faults_matrix = faults_matrix(H) + return TableDecoder(H, n, s, k, faults_matrix, lookup_table, time) +end + +struct BeliefPropDecoder <: AbstractSyndromeDecoder + """Stabilizer tableau defining the code""" + H + """Faults matrix corresponding to the code""" + faults_matrix + """The number of qubits in the code""" + n + """The depth of the code""" + s + """The number of encoded qubits""" + k + """Empty array to hold temporary values in belief decoding""" + log_probabs + """Error probabilities of each channel""" + channel_probs + """Number of X checks, used to get the syndrome corresponding to the X checks""" + numchecks_X + """Empty matrix used to hold error probabilities for the X channels""" + b2c_X + """Empty matrix used to temporary belief propagation values""" + c2b_X + """Number of X checks, used to get the syndrome corresponding to the X checks""" + numchecks_Z + """Empty matrix used to hold error probabilities for the Z channels""" + b2c_Z + """Empty matrix used to temporary belief propagation values""" + c2b_Z + """The measured error syndrome""" + err + """Sparse array of Cx matrix""" + sparse_Cx + """Sparse array of the transpose of the Cx matrix""" + sparse_CxT + """Sparse array of Cz matrix""" + sparse_Cz + """Sparse array of the transpose of the Cx matrix""" + sparse_CzT +end + +function BeliefPropDecoder(Hx, Hz) + c = CSS(Hx, Hz) + H = parity_checks(c) + s, n = size(H) + _, _, r = canonicalize!(Base.copy(H), ranks=true) + k = n - r + println(typeof(H)) + faults_matrix = faults_matrix(H) + log_probabs = zeros(n) + channel_probs = fill(p_init, n) + + numchecks_X = size(Cx)[1] + b2c_X = zeros(numchecks_X, n) + c2b_X = zeros(numchecks_X, n) + + numchecks_Z = size(Cz)[1] + b2c_Z = zeros(numchecks_Z, n) + c2b_Z = zeros(numchecks_Z, n) + err = zeros(n) + + sparse_Cx = sparse(Hx) + sparse_CxT = sparse(Hx') + sparse_Cz = sparse(Hz) + sparse_CzT = sparse(Hz') + return BeliefPropDecoder(H, faults_matrix, n, s, k, log_probabs, channel_probs, numchecks_X, b2c_X, c2b_X, numchecks_Z, b2c_Z, c2b_Z, err, sparse_Cx, sparse_CxT, sparse_Cz, sparse_CzT) +end + +function decode(d::TableDecoder, syndrome_sample) + return get(d.lookup_table, syndrome_sample, nothing) +end + +function decode(d::BeliefPropDecoder, syndrome_sample) + row_x = syndrome_sample[1:d.numchecks_X] + row_z = syndrome_sample[d.numchecks_X+1:d.numchecks_X+d.numchecks_Z] + + KguessX, success = syndrome_decode(d.sparse_Cx, d.sparse_CxT, d.row_x, d.max_iters, d.channel_probs, d.b2c_X, d.c2b_X, d.log_probabs, Base.copy(d.err)) + KguessZ, success = syndrome_decode(d.sparse_Cz, d.sparse_CzT, d.row_z, d.max_iters, d.channel_probs, d.b2c_Z, d.c2b_Z, d.log_probabs, Base.copy(d.err)) + guess = vcat(KguessZ, KguessX) +end + + +## NOT WORKING +function evaluate_classical_decoder(H, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logical_view_func, decoder_func, pre_circuit = nothing) + decoded = 0 + + H_stab = Stabilizer(fill(0x0, size(Hx, 2)), H, zeros(Bool, size(H))) + + O = faults_matrix(H_stab) + syndrome_circuit = syndrome_circuit_func(H_stab) + + s, n = size(H) + k = n - s + + errors = [PauliError(i, init_error) for i in 1:n]; + + md = MixedDestabilizer(H_stab) + + full_circuit = [] + + logview = logical_view_func(md) + logcirc, _ = syndrome_circuit_func(logview) + + noisy_syndrome_circuit = add_two_qubit_gate_noise(syndrome_circuit, gate_error); + + for gate in logcirc + type = typeof(gate) + if type == sMRZ + push!(circuit, sMRZ(gate.qubit+s, gate.bit+s)) + else + push!(circuit, type(gate.q1, gate.q2+s)) + end + end + + ecirc = encoding_circuit_func(syndrome_circuit) + if isnothing(pre_circuit) + full_circuit = vcat(pre_circuits, ecirc, errors, noisy_syndrome_circuit) + else + full_circuit = vcat(ecirc, errors, noisy_syndrome_circuit) + end + + frames = PauliFrame(nframes, n+s+k, s+k) + pftrajectories(frames, full_circuit) + syndromes = pfmeasurements(frames)[:, 1:s] + logical_syndromes = pfmeasurements(frames)[:, s+1: s+k] + + for i in 1:nsamples + guess = decode(decoder_obj, syndromes[i]) # TODO: replace 'decoder_obj' with proper object + + result = (O * (guess)) + + if result == logical_syndromes[i] + decoded += 1 + end + end + + return (nsamples - decoded) / nsamples +end From 38f69722640b80ec99719546b7ce28c4cd7c8dba Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 6 Jan 2024 02:04:57 -0500 Subject: [PATCH 15/21] Added bicycle and unicycle tests, minor consistency changes to CSS struct -Added tests for each of the methods used in creating bicycle or unicycle ECCs. - Fixed the final return step of some CSS methods, specifically the constructor and boolean_tableau - Changed name of `code_m` to `code_s` --- .gitignore | 3 +- src/ecc/codes/css.jl | 16 ++++-- src/ecc/codes/simple_sparse_codes.jl | 83 ++++++++++++++++++++++++---- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index ebcf4e4b5..5d180000f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ Manifest.toml LocalPreferences.toml */.*swp scratch/ -*.cov \ No newline at end of file +*.cov +.vscode/settings.json \ No newline at end of file diff --git a/src/ecc/codes/css.jl b/src/ecc/codes/css.jl index 4c1a92fa3..bd8ce4cf0 100644 --- a/src/ecc/codes/css.jl +++ b/src/ecc/codes/css.jl @@ -1,13 +1,17 @@ -"""An arbitrary CSS error correcting code defined by its X and Z checks.""" +"""An arbitrary CSS error correcting code defined by its X and Z checks. + +Hx: A boolean matrix describing the X checks +Hz: A boolean matrix describing the Z checks +""" struct CSS <: AbstractECC - Hx - Hz + Hx::Matrix{Bool} + Hz::Matrix{Bool} """Creates a CSS code using the two provided matrices where Hx contains the X checks and Hz contains the Z checks.""" function CSS(Hx, Hz) n = size(Hx, 2) if n != size(Hz, 2) error("When constructing a CSS quantum code, the two classical codes are required to have the same block size") end if size(Hx,1)+size(Hz,1) >= n error("When constructing a CSS quantum code, the total number of checks (rows) in the parity checks of the two classical codes have to be lower than the block size (the number of columns).") end - return new(Hx, Hz) + new(Hx, Hz) end end @@ -17,7 +21,7 @@ function boolean_tableau(c::CSS) checks_matrix = falses(Hx_height + Hz_height, Hx_width + Hz_width) checks_matrix[1:Hx_height, 1:Hx_width] = c.Hx checks_matrix[Hx_height+1:end, Hx_width+1:end] = c.Hz - return CSS(checks_matrix) + return checks_matrix end """Returns the stabilizer making up the parity check tableau.""" @@ -31,7 +35,7 @@ end code_n(c::CSS) = size(c.Hx,2) """Returns the depth of the parity check matrix""" -code_m(c::CSS) = size(c.Hx, 1) + size(c.Hz, 1) +code_s(c::CSS) = size(c.Hx, 1) + size(c.Hz, 1) """Returns the number of encoded qubits""" code_k(c::CSS) = (2 * size(c.Hx,2)) - code_m(c) diff --git a/src/ecc/codes/simple_sparse_codes.jl b/src/ecc/codes/simple_sparse_codes.jl index ff1ecd3ab..47bbad63d 100644 --- a/src/ecc/codes/simple_sparse_codes.jl +++ b/src/ecc/codes/simple_sparse_codes.jl @@ -4,13 +4,35 @@ Parameters: - n: width of array, should be >= 2 -- m: height of array, should be >= 2 and a multiple of 2""" +- m: height of array, should be >= 2 and a multiple of 2 + + +``` jldoctest Bicycle +julia> using QuantumClifford.ECC +julia> parity_checks(Bicycle(6, 4)) ++ XX_X_X ++ X_X_XX ++ ZZ_Z_Z ++ Z_Z_ZZ + +julia> Bicycle(6,4).Hx +2×6 Matrix{Bool}: + 1 1 0 1 0 1 + 1 0 1 0 1 1 + + julia> typeof(Bicycle(6, 4)) + CSS +``` +""" function Bicycle(n::Int, m::Int) + if n%2 == 1 + throw(DomainError(m, "The number of qubits should be a multiple for 2 for bicycle codes.")) + end if m%2 == 1 - throw(DomainError(m, " M should be a multiple for 2 for bicycle codes.")) + throw(DomainError(m, "Code depth should be a multiple for 2 for CSS codes.")) end if m < 2 - throw(DomainError(m, " M is too small, make it greater than 1.")) + throw(DomainError(m, "Code depth is too small, make it greater than 1.")) end bs = bicycle_set_gen(Int(n/2)) bsc = circ_to_bicycle_h0(bs, Int(n/2)) @@ -24,11 +46,21 @@ end Parameters: - n: width of array, should be >= 1 -- set: array of indices that are 'active' checks in the circulant code""" +- set: array of indices that are 'active' checks in the circulant code + +``` jldoctest Unicycle +julia> Unicycle(7, [1, 2, 4]) +4×8 Matrix{Bool}: + 0 1 1 0 1 0 0 1 + 0 0 0 1 1 0 1 1 + 0 1 0 0 0 1 1 1 + 1 0 1 0 0 0 1 1 +``` +""" function Unicycle(n::Int, set) usc = circ_to_unicycle_h0(set, n) rusc = reduce_unicycle(usc) - return CSS(rusc, rusc) + return rusc end """Takes an untrimmed bicycle matrix and removes the row which keeps the spread of the column weights minimal. @@ -36,7 +68,15 @@ end Required before the bicycle code can be used. Typical usage: -ReduceBicycle(Circ2BicycleH0(array_indices, (block length / 2) ) )""" + +``` jldoctest reduce_bicycle +julia> reduce_bicycle(Bool[1 1 0 1 0 1; 0 1 1 1 1 0; 1 0 1 0 1 1]) +Bool[1 1 0 1 0 1; 1 0 1 0 1 1] + +julia> reduce_bicycle(Bool[1 1 0 0 1 0 0 1; 0 1 1 0 1 1 0 0; 0 0 1 1 0 1 1 0; 1 0 0 1 0 0 1 1]) +Bool[0 1 1 0 1 1 0 0; 0 0 1 1 0 1 1 0; 1 0 0 1 0 0 1 1] +``` +""" function reduce_bicycle(H0::Matrix{Bool}) m, n = size(H0) r_i = 0 @@ -55,8 +95,13 @@ end """Takes a list of indices and creates the base of the bicycle matrix. For example: -Circ2BicycleH0([1, 2, 4], 7) +``` jldoctest circ_to_bicycle_h0 +julia> circ_to_bicycle_h0([0, 1], 3) +Bool[1 1 0 1 0 1; 0 1 1 1 1 0; 1 0 1 0 1 1] +julia> circ_to_bicycle_h0([0, 1], 4) +Bool[1 1 0 0 1 0 0 1; 0 1 1 0 1 1 0 0; 0 0 1 1 0 1 1 0; 1 0 0 1 0 0 1 1] +``` See https://arxiv.org/abs/quant-ph/0304161 for more details""" function circ_to_bicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = Array{Bool}(undef, n) @@ -85,7 +130,12 @@ end Required before the unicycle code can be used. Typical usage: -`ReduceUnicycle(Circ2UnicycleH0(array_indices, block length) )`""" +`reduce_unicycle(circ_to_unicycle_h0(array_indices, block length) )` + +``` jldoctest reduce_unicycle +julia> reduce_unicycle(Bool[1 1 0 1 0 0 0 1; 0 1 1 0 1 0 0 1; 0 0 1 1 0 1 0 1; 0 0 0 1 1 0 1 1; 1 0 0 0 1 1 0 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1]) +Bool[0 1 1 0 1 0 0 1; 0 0 0 1 1 0 1 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1] +```""" function reduce_unicycle(m::Matrix{Bool}) rrzz = residue_ring(ZZ, 2) nm = matrix(rrzz, m) @@ -107,8 +157,12 @@ end """Takes a list of indices and creates the base of the unicycle matrix. For example: -`Circ2UnicycleH0([1, 2, 4], 7)` +`circ_to_unicycle_h0([1, 2, 4], 7)` +``` jldoctest circ_to_unicycle +julia> circ_to_unicycle([1, 2, 4], 7) +Bool[1 1 0 1 0 0 0 1; 0 1 1 0 1 0 0 1; 0 0 1 1 0 1 0 1; 0 0 0 1 1 0 1 1; 1 0 0 0 1 1 0 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1] +``` See https://arxiv.org/abs/quant-ph/0304161 for more details""" function circ_to_unicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = fill(false, n) @@ -133,7 +187,16 @@ function circ_to_unicycle_h0(circ_indices::Array{Int}, n::Int) return comp_matrix end -"""Attempts to generate a list of indices to be used in a bicycle code using a search method""" +"""Attempts to generate a list of indices to be used in a bicycle code using a search method + +``` jldoctest bicycle_set_gen +julia> bicycle_set_gen(3) +[0, 1] + +julia> bicycle_set_gen(1000) +[0, 1, 3, 7, 12, 20, 30, 44, 65, 80, 96, 122, 147, 181, 203, 289] +``` +""" function bicycle_set_gen(N::Int) circ_arr::Array{Int} = [0] diff_arr::Array{Int} = [] From fd62561089f43c38f5496dfe9962a07fc101ac3d Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 6 Jan 2024 02:19:07 -0500 Subject: [PATCH 16/21] Updated compact with relevant libraries --- Project.toml | 2 ++ src/ecc/codes/simple_sparse_codes.jl | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index b628645a2..44647d2c1 100644 --- a/Project.toml +++ b/Project.toml @@ -55,3 +55,5 @@ QuantumOpticsBase = "0.4" SIMD = "3.4.0" SumTypes = "0.4.4, 0.5" julia = "1.9" +Statistics = "1" +Nemo = "1" diff --git a/src/ecc/codes/simple_sparse_codes.jl b/src/ecc/codes/simple_sparse_codes.jl index 47bbad63d..f1432ca06 100644 --- a/src/ecc/codes/simple_sparse_codes.jl +++ b/src/ecc/codes/simple_sparse_codes.jl @@ -20,8 +20,14 @@ julia> Bicycle(6,4).Hx 1 1 0 1 0 1 1 0 1 0 1 1 - julia> typeof(Bicycle(6, 4)) - CSS +julia> typeof(Bicycle(6, 4)) +CSS + +julia> QuantumClifford.stab_looks_good(parity_checks(Bicycle(6, 4))) +true + +julia> QuantumClifford.stab_looks_good(parity_checks(Bicycle(10, 6))) +true ``` """ function Bicycle(n::Int, m::Int) From 794ac6ec0e763c36fc1d9829444ed51c1730cab3 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 6 Jan 2024 02:21:40 -0500 Subject: [PATCH 17/21] Added tests for whether Bicycle and Unicycle codes looks good --- src/ecc/codes/simple_sparse_codes.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ecc/codes/simple_sparse_codes.jl b/src/ecc/codes/simple_sparse_codes.jl index f1432ca06..52d9fef11 100644 --- a/src/ecc/codes/simple_sparse_codes.jl +++ b/src/ecc/codes/simple_sparse_codes.jl @@ -61,6 +61,15 @@ julia> Unicycle(7, [1, 2, 4]) 0 0 0 1 1 0 1 1 0 1 0 0 0 1 1 1 1 0 1 0 0 0 1 1 + +julia> Stabilizer(Unicycle(7, [1, 2, 4])) ++ ZXXZ ++ Z_ZY ++ _YZZ ++ X_YZ + +julia> QuantumClifford.stab_looks_good(Stabilizer(Unicycle(7, [1, 2, 4]))) +false ``` """ function Unicycle(n::Int, set) From d9fb55e0714c98c058ae351b049c30ea1e610d89 Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 6 Jan 2024 02:26:09 -0500 Subject: [PATCH 18/21] Removed nemo library requirement --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 44647d2c1..48129a26b 100644 --- a/Project.toml +++ b/Project.toml @@ -56,4 +56,3 @@ SIMD = "3.4.0" SumTypes = "0.4.4, 0.5" julia = "1.9" Statistics = "1" -Nemo = "1" From b0159929951615cf01151785b19602589283999a Mon Sep 17 00:00:00 2001 From: Benzillaist Date: Sat, 6 Jan 2024 02:27:21 -0500 Subject: [PATCH 19/21] Updated version of statistics package --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 48129a26b..c693e2641 100644 --- a/Project.toml +++ b/Project.toml @@ -55,4 +55,4 @@ QuantumOpticsBase = "0.4" SIMD = "3.4.0" SumTypes = "0.4.4, 0.5" julia = "1.9" -Statistics = "1" +Statistics = "1.9.0" From e834c31b50ccacfc4cfedfa54a4b4c8bcae3ecaf Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 17 Jan 2024 06:24:07 -0500 Subject: [PATCH 20/21] some polish - remove unrelated files and update .gitignore - add an auto-generated API documentation for the ECC submodule - remove unused `boolean_tableau` - remove docstrings for already documented functions - remove a redundant function definition (code_k is already defined in terms of code_n and code_s) - minor stilystic cleanup in simple_sparse_codes - add some rudimentary CSS tests --- .gitignore | 2 +- .vscode/settings.json | 3 --- docs/make.jl | 5 +++- docs/src/ECC_API copy.md | 6 +++++ src/ecc/ECC.jl | 15 +++++------ src/ecc/codes/css.jl | 25 +++++------------- src/ecc/codes/simple_sparse_codes.jl | 38 ++++++++++++---------------- test/test_doctests.jl | 2 +- test/test_ecc.jl | 1 + test/test_ecc_encoding.jl | 1 + test/test_ecc_syndromes.jl | 3 ++- 11 files changed, 45 insertions(+), 56 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 docs/src/ECC_API copy.md diff --git a/.gitignore b/.gitignore index 5d180000f..8bf2daafc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ LocalPreferences.toml */.*swp scratch/ *.cov -.vscode/settings.json \ No newline at end of file +.vscode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b4527bc30..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "julia.environmentPath": "c:\\Users\\benku\\Desktop\\College Documents\\Krastanov Independent Study\\QuantumClifford.jl-CSS_Builder" -} \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index 48920028c..2690c5a4f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -20,7 +20,7 @@ doctest = false, clean = true, sitename = "QuantumClifford.jl", format = Documenter.HTML(size_threshold_ignore = ["API.md"]), -modules = [QuantumClifford, QuantumClifford.Experimental.NoisyCircuits, QuantumInterface], +modules = [QuantumClifford, QuantumClifford.Experimental.NoisyCircuits, QuantumClifford.ECC, QuantumInterface], warnonly = [:missing_docs], authors = "Stefan Krastanov", pages = [ @@ -41,6 +41,9 @@ pages = [ "Circuit Operations" => "noisycircuits_ops.md", "API" => "noisycircuits_API.md" ], +"ECC compendium" => [ + "API" => "ECC_API.md" +], "All Gates" => "allops.md", "Visualizations" => "plotting.md", "API" => "API.md", diff --git a/docs/src/ECC_API copy.md b/docs/src/ECC_API copy.md new file mode 100644 index 000000000..d77b26097 --- /dev/null +++ b/docs/src/ECC_API copy.md @@ -0,0 +1,6 @@ +# Full ECC API (autogenerated) + +```@autodocs +Modules = [QuantumClifford.ECC] +Private = false +``` diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 6a4938fed..5854076d3 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -1,22 +1,22 @@ module ECC using LinearAlgebra -using QuantumClifford, SparseArrays +using QuantumClifford using QuantumClifford: AbstractOperation, AbstractStabilizer, Stabilizer import QuantumClifford: Stabilizer, MixedDestabilizer using DocStringExtensions using Combinatorics: combinations +using SparseArrays using Statistics: std using Nemo: ZZ, residue_ring, matrix abstract type AbstractECC end -export Shor9, Steane7, Cleve8, Perfect5, Bitflip3, - parity_checks, naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, - code_n, code_s, code_k, rate, distance, +export parity_checks, code_n, code_s, code_k, rate, distance, isdegenerate, faults_matrix, - Unicycle, Bicycle, - CSS + naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, + CSS, Unicycle, Bicycle, + Shor9, Steane7, Cleve8, Perfect5, Bitflip3 """Parity check tableau of a code.""" function parity_checks end @@ -295,13 +295,12 @@ end include("circuits.jl") include("decoder_pipeline.jl") +include("codes/css.jl") include("codes/bitflipcode.jl") include("codes/fivequbit.jl") include("codes/steanecode.jl") include("codes/shorcode.jl") include("codes/clevecode.jl") - -include("codes/css.jl") include("codes/simple_sparse_codes.jl") end #module diff --git a/src/ecc/codes/css.jl b/src/ecc/codes/css.jl index bd8ce4cf0..58bd17f6b 100644 --- a/src/ecc/codes/css.jl +++ b/src/ecc/codes/css.jl @@ -1,12 +1,14 @@ """An arbitrary CSS error correcting code defined by its X and Z checks. -Hx: A boolean matrix describing the X checks -Hz: A boolean matrix describing the Z checks -""" +```jldoctest +julia> CSS([0 1 1 0; 1 1 0 0], [1 1 1 1]) |> parity_checks ++ _XX_ ++ XX__ ++ ZZZZ +```""" struct CSS <: AbstractECC Hx::Matrix{Bool} Hz::Matrix{Bool} - """Creates a CSS code using the two provided matrices where Hx contains the X checks and Hz contains the Z checks.""" function CSS(Hx, Hz) n = size(Hx, 2) if n != size(Hz, 2) error("When constructing a CSS quantum code, the two classical codes are required to have the same block size") end @@ -15,27 +17,12 @@ struct CSS <: AbstractECC end end -function boolean_tableau(c::CSS) - Hx_height, Hx_width = size(c.Hx) - Hz_height, Hz_width = size(x.Hz) - checks_matrix = falses(Hx_height + Hz_height, Hx_width + Hz_width) - checks_matrix[1:Hx_height, 1:Hx_width] = c.Hx - checks_matrix[Hx_height+1:end, Hx_width+1:end] = c.Hz - return checks_matrix -end - -"""Returns the stabilizer making up the parity check tableau.""" function parity_checks(c::CSS) extended_Hx = Matrix{Bool}(vcat(c.Hx, zeros(size(c.Hz)))) extended_Hz = Matrix{Bool}(vcat(zeros(size(c.Hx)), c.Hz)) Stabilizer(fill(0x0, size(c.Hx, 1) + size(c.Hz, 1)), extended_Hx, extended_Hz) end -"""Returns the block length of the code.""" code_n(c::CSS) = size(c.Hx,2) -"""Returns the depth of the parity check matrix""" code_s(c::CSS) = size(c.Hx, 1) + size(c.Hz, 1) - -"""Returns the number of encoded qubits""" -code_k(c::CSS) = (2 * size(c.Hx,2)) - code_m(c) diff --git a/src/ecc/codes/simple_sparse_codes.jl b/src/ecc/codes/simple_sparse_codes.jl index 52d9fef11..77c1d802d 100644 --- a/src/ecc/codes/simple_sparse_codes.jl +++ b/src/ecc/codes/simple_sparse_codes.jl @@ -1,14 +1,12 @@ -# Currently just has Bicycle and Unicycle codes, but open to all types of rudimentary sparse codes +"""Generate a bicycle code of the specified height and width (returns an instance of [`CSS`](@ref), not a `Bicycle` type). -"""Takes a height and width of matrix and generates a bicycle code to the specified height and width. +This is not a deterministic function (random sampling is involved in the creation of a bicycle code). Parameters: -- n: width of array, should be >= 2 -- m: height of array, should be >= 2 and a multiple of 2 - +- `n`: width of array, should be ≥ 2 +- `m`: height of array, should be ≥ 2 and a multiple of 2 -``` jldoctest Bicycle -julia> using QuantumClifford.ECC +```jldoctest julia> parity_checks(Bicycle(6, 4)) + XX_X_X + X_X_XX @@ -22,12 +20,6 @@ julia> Bicycle(6,4).Hx julia> typeof(Bicycle(6, 4)) CSS - -julia> QuantumClifford.stab_looks_good(parity_checks(Bicycle(6, 4))) -true - -julia> QuantumClifford.stab_looks_good(parity_checks(Bicycle(10, 6))) -true ``` """ function Bicycle(n::Int, m::Int) @@ -54,7 +46,7 @@ Parameters: - n: width of array, should be >= 1 - set: array of indices that are 'active' checks in the circulant code -``` jldoctest Unicycle +```jldoctest julia> Unicycle(7, [1, 2, 4]) 4×8 Matrix{Bool}: 0 1 1 0 1 0 0 1 @@ -84,7 +76,7 @@ Required before the bicycle code can be used. Typical usage: -``` jldoctest reduce_bicycle +```jldoctest julia> reduce_bicycle(Bool[1 1 0 1 0 1; 0 1 1 1 1 0; 1 0 1 0 1 1]) Bool[1 1 0 1 0 1; 1 0 1 0 1 1] @@ -110,14 +102,16 @@ end """Takes a list of indices and creates the base of the bicycle matrix. For example: -``` jldoctest circ_to_bicycle_h0 + +```jldoctest julia> circ_to_bicycle_h0([0, 1], 3) Bool[1 1 0 1 0 1; 0 1 1 1 1 0; 1 0 1 0 1 1] julia> circ_to_bicycle_h0([0, 1], 4) Bool[1 1 0 0 1 0 0 1; 0 1 1 0 1 1 0 0; 0 0 1 1 0 1 1 0; 1 0 0 1 0 0 1 1] ``` -See https://arxiv.org/abs/quant-ph/0304161 for more details""" + +See `https://arxiv.org/abs/quant-ph/0304161` for more details""" function circ_to_bicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = Array{Bool}(undef, n) circ_matrix = Matrix{Bool}(undef, n, n) @@ -145,9 +139,9 @@ end Required before the unicycle code can be used. Typical usage: -`reduce_unicycle(circ_to_unicycle_h0(array_indices, block length) )` +`reduce_unicycle(circ_to_unicycle_h0(array_indices, block length))` -``` jldoctest reduce_unicycle +```jldoctest julia> reduce_unicycle(Bool[1 1 0 1 0 0 0 1; 0 1 1 0 1 0 0 1; 0 0 1 1 0 1 0 1; 0 0 0 1 1 0 1 1; 1 0 0 0 1 1 0 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1]) Bool[0 1 1 0 1 0 0 1; 0 0 0 1 1 0 1 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1] ```""" @@ -174,11 +168,11 @@ end For example: `circ_to_unicycle_h0([1, 2, 4], 7)` -``` jldoctest circ_to_unicycle +```jldoctest julia> circ_to_unicycle([1, 2, 4], 7) Bool[1 1 0 1 0 0 0 1; 0 1 1 0 1 0 0 1; 0 0 1 1 0 1 0 1; 0 0 0 1 1 0 1 1; 1 0 0 0 1 1 0 1; 0 1 0 0 0 1 1 1; 1 0 1 0 0 0 1 1] ``` -See https://arxiv.org/abs/quant-ph/0304161 for more details""" +See `https://arxiv.org/abs/quant-ph/0304161` for more details""" function circ_to_unicycle_h0(circ_indices::Array{Int}, n::Int) circ_arr = fill(false, n) one_col = transpose(fill(true, n)) @@ -204,7 +198,7 @@ end """Attempts to generate a list of indices to be used in a bicycle code using a search method -``` jldoctest bicycle_set_gen +```jldoctest julia> bicycle_set_gen(3) [0, 1] diff --git a/test/test_doctests.jl b/test/test_doctests.jl index 1ce6d1e86..7f9891469 100644 --- a/test/test_doctests.jl +++ b/test/test_doctests.jl @@ -4,6 +4,6 @@ using QuantumClifford ENV["LINES"] = 80 # for forcing `displaysize(io)` to be big enough ENV["COLUMNS"] = 80 @testset "Doctests" begin - DocMeta.setdocmeta!(QuantumClifford, :DocTestSetup, :(using QuantumClifford); recursive=true) + DocMeta.setdocmeta!(QuantumClifford, :DocTestSetup, :(using QuantumClifford; using QuantumClifford.ECC); recursive=true) doctest(QuantumClifford) end diff --git a/test/test_ecc.jl b/test/test_ecc.jl index 3b7ac29c6..f634e3ac5 100644 --- a/test/test_ecc.jl +++ b/test/test_ecc.jl @@ -8,6 +8,7 @@ codes = [ Shor9(), Perfect5(), Cleve8(), + CSS([0 1 1 0; 1 1 0 0], [1 1 1 1]), ] ## diff --git a/test/test_ecc_encoding.jl b/test/test_ecc_encoding.jl index 12e693347..99da625f9 100644 --- a/test/test_ecc_encoding.jl +++ b/test/test_ecc_encoding.jl @@ -19,6 +19,7 @@ using QuantumClifford.ECC: AbstractECC, Cleve8, Steane7, Shor9, Bitflip3, Perfec :(S"Y_"), :(S"Z_"), :(S"X_"), + :(CSS([0 1 1 0; 1 1 0 0], [1 1 1 1])), fill(:(random_stabilizer(5,7)), 100)... ] diff --git a/test/test_ecc_syndromes.jl b/test/test_ecc_syndromes.jl index fd89a2262..7966bf8b0 100644 --- a/test/test_ecc_syndromes.jl +++ b/test/test_ecc_syndromes.jl @@ -8,7 +8,8 @@ codes = [ Steane7(), Shor9(), Perfect5(), - Cleve8() + Cleve8(), + CSS([0 1 1 0; 1 1 0 0], [1 1 1 1]) ] ## From 84376907811a2ac938157d56e6772fc2b5e4fb58 Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 17 Jan 2024 22:13:45 -0500 Subject: [PATCH 21/21] flesh out `evaluate_decoder` --- src/affectedqubits.jl | 2 + src/ecc/ECC.jl | 3 +- src/ecc/circuits.jl | 4 +- src/ecc/decoder_pipeline.jl | 223 +++++++++++++++++------------------- src/pauli_frames.jl | 2 +- 5 files changed, 115 insertions(+), 119 deletions(-) diff --git a/src/affectedqubits.jl b/src/affectedqubits.jl index 09c9217e6..da6314a65 100644 --- a/src/affectedqubits.jl +++ b/src/affectedqubits.jl @@ -12,7 +12,9 @@ affectedqubits(p::PauliOperator) = 1:length(p) affectedqubits(m::Union{AbstractMeasurement,sMRX,sMRY,sMRZ}) = (m.qubit,) affectedqubits(v::VerifyOp) = v.indices affectedqubits(c::CliffordOperator) = 1:nqubits(c) +affectedqubits(c::ClassicalXOR) = () affectedbits(o) = () affectedbits(m::sMRZ) = (m.bit,) affectedbits(m::sMZ) = (m.bit,) +affectedbits(c::ClassicalXOR) = (c.bits..., c.store) diff --git a/src/ecc/ECC.jl b/src/ecc/ECC.jl index 5854076d3..f9878d98e 100644 --- a/src/ecc/ECC.jl +++ b/src/ecc/ECC.jl @@ -16,7 +16,8 @@ export parity_checks, code_n, code_s, code_k, rate, distance, isdegenerate, faults_matrix, naive_syndrome_circuit, shor_syndrome_circuit, naive_encoding_circuit, CSS, Unicycle, Bicycle, - Shor9, Steane7, Cleve8, Perfect5, Bitflip3 + Shor9, Steane7, Cleve8, Perfect5, Bitflip3, + evaluate_decoder, TableDecoder """Parity check tableau of a code.""" function parity_checks end diff --git a/src/ecc/circuits.jl b/src/ecc/circuits.jl index 0f62a7057..f71cc6b99 100644 --- a/src/ecc/circuits.jl +++ b/src/ecc/circuits.jl @@ -142,7 +142,7 @@ Use the `ancillary_index` and `bit_index` arguments to offset where the correspo Ancillary qubits Returns: - - The cat state preparation circuit. + - The ancillary cat state preparation circuit. - The Shor syndrome measurement circuit. - The number of ancillary qubits that were added. - The list of bit indices that store the final measurement results. @@ -165,7 +165,7 @@ and stores the measurement result into classical bits starting at `bit_index`. The final measurement result is the XOR of all the bits. Returns: - - The cat state preparation circuit. + - The ancillary cat state preparation circuit. - The Shor syndrome measurement circuit. - One more than the index of the last added ancillary qubit. - One more than the index of the last added classical bit. diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index af3538b80..211f4b48a 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -1,60 +1,90 @@ +"""An abstract type for QECC syndrome decoding algorithms. + +All `AbstractSyndromeDecoder` types are expected to: +- have a `parity_checks` method giving the parity checks for the code under study +- have a `decode` method that guesses error which caused the syndrome +- have an `evaluate_decoder` method which runs a full simulation but it supports only a small number of ECC protocols""" abstract type AbstractSyndromeDecoder end -function evaluate_decoder(d::AbstractSyndromeDecoder, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func) - pre_X = [sHadamard(i) for i in n-k+1:n] - X_error = evaluate_classical_decoder(d, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logicalxview, 1, d.k, pre_X) - Z_error = evaluate_classical_decoder(d, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logicalzview, d.k + 1, 2 * d.k) - return (X_error, Z_error) +"""An abstract type mostly used by [`evaluate_decoder`](@ref) to specify in what context to evaluate an ECC.""" +abstract type AbstractECCSetup end + +"""A helper function that takes a parity check tableau and an `AbstractECCSetup` type and provides the circuit that needs to be simulated.""" +function physical_ECC_circuit end # XXX Do not export! This might need to be refactored as we add more interesting setups! + +"""Configuration for ECC evaluators that simulate the Shor-style syndrome measurement (without a flag qubit). + +The simulated circuit includes: +- perfect noiseless encoding (encoding and its fault tolerance are not being studied here) +- one round of "memory noise" after the encoding but before the syndrome measurement +- perfect preparation of entangled ancillary qubits +- noisy Shor-style syndrome measurement (only two-qubit gate noise) +- noiseless "logical state measurement" (providing the comparison data when evaluating the decoder) +""" +struct ShorSyndromeECCSetup <: AbstractECCSetup + mem_noise::Float64 + two_qubit_gate_noise::Float64 + function ShorSyndromeECCSetup(mem_noise, two_qubit_gate_noise) + 0<=mem_noise<=1 || throw(DomainError(mem_noise, "The memory noise in `ShorSyndromeECCSetup` should be between 0 and 1.")) + 0<=two_qubit_gate_noise<=1 || throw(DomainError(two_qubit_gate_noise, "The two-qubit gate noise in `ShorSyndromeECCSetup` should be between 0 and 1.")) + new(mem_noise, two_qubit_gate_noise) + end end -function evaluate_classical_decoder(d::AbstractSyndromeDecoder, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logical_view_function, guess_start, guess_stop, pre_circuit = nothing) - H = d.H - O = d.faults_matrix - syndrome_circuit = syndrome_circuit_func(H) - - n = d.n - s = d.s - k = d.k - - errors = [PauliError(i, init_error) for i in 1:n]; - - md = MixedDestabilizer(H) - - full_circuit = [] - - logview = logical_view_function(md) - logcirc, _ = syndrome_circuit_func(logview) +function physical_ECC_circuit(H, setup::ShorSyndromeECCSetup) + prep_anc, syndrome_circ, n_anc, syndrome_bits = shor_syndrome_circuit(H) + noisy_syndrome_circ = syndrome_circ # add_two_qubit_gate_noise(syndrome_circ, gate_error) + mem_error_circ = [PauliError(i, setup.mem_noise) for i in 1:nqubits(H)]; + circ = [prep_anc..., mem_error_circ..., noisy_syndrome_circ...] + circ, syndrome_bits, n_anc +end - noisy_syndrome_circuit = add_two_qubit_gate_noise(syndrome_circuit, gate_error); +"""Evaluate the performance of a given decoder (e.g. [`TableDecoder`](@ref)) and a given style of running an ECC code (e.g. [`ShorSyndromeECCSetup`](@ref))""" +function evaluate_decoder(d::AbstractSyndromeDecoder, setup::AbstractECCSetup, nsamples::Int) + H = parity_checks(d) + n = code_n(H) + k = code_k(H) + O = faults_matrix(H) + + physical_noisy_circ, syndrome_bits, n_anc = physical_ECC_circuit(H, setup) + encoding_circ = naive_encoding_circuit(H) + preX = [sHadamard(i) for i in n-k+1:n] + + mdH = MixedDestabilizer(H) + logX_circ, _, logX_bits = naive_syndrome_circuit(logicalxview(mdH), n_anc+1, last(syndrome_bits)+1) + logZ_circ, _, logZ_bits = naive_syndrome_circuit(logicalzview(mdH), n_anc+1, last(syndrome_bits)+1) + + X_error = evaluate_decoder( + d, nsamples, + [encoding_circ..., physical_noisy_circ..., logZ_circ...], + syndrome_bits, logZ_bits, O[length(logZ_bits)+1:end,:]) + Z_error = evaluate_decoder( + d, nsamples, + [preX..., encoding_circ..., physical_noisy_circ..., logX_circ...], + syndrome_bits, logX_bits, O[1:length(logZ_bits),:]) + return (X_error, Z_error) +end - for gate in logcirc - type = typeof(gate) - if type == sMRZ - push!(syndrome_circuit, sMRZ(gate.qubit+s, gate.bit+s)) - else - push!(syndrome_circuit, type(gate.q1, gate.q2+s)) - end - end +"""Evaluate the performance of an error-correcting circuit. - ecirc = encoding_circuit_func(syndrome_circuit) - if isnothing(pre_circuit) - full_circuit = vcat(pre_circuits, ecirc, errors, noisy_syndrome_circuit) - else - full_circuit = vcat(ecirc, errors, noisy_syndrome_circuit) - end +This method requires you give the circuit that performs both syndrome measurements and (probably noiseless) logical state measurements. +The faults matrix that translates an error vector into corresponding logical errors is necessary as well. - frames = PauliFrame(nframes, n+s+k, s+k) - pftrajectories(frames, full_circuit) - syndromes = pfmeasurements(frames)[:, 1:s] - logical_syndromes = pfmeasurements(frames)[:, s+1: s+k] +This is a relatively barebones method that assumes the user prepares necessary circuits, etc. +It is a method that is used internally by more user-frienly methods providing automatic conversion of codes and noise models +to the necessary noisy circuits. +""" +function evaluate_decoder(d::AbstractSyndromeDecoder, nsamples, circuit, syndrome_bits, logical_bits, faults_submatrix) + frames = pftrajectories(circuit;trajectories=nsamples,threads=true) + syndromes = @view pfmeasurements(frames)[:, syndrome_bits] + measured_faults = @view pfmeasurements(frames)[:, logical_bits] + decoded = 0 for i in 1:nsamples - guess = decode(d, syndromes[i]) - - # result should be concatinated guess of the X and Z checks - result = (O * (guess))[guess_start:guess_stop] - - if result == logical_syndromes[i] + guess = decode(d, @view syndromes[i,:]) + isnothing(guess) && continue + guess_faults = faults_submatrix * guess + if guess_faults == @view measured_faults[i,:] decoded += 1 end end @@ -75,19 +105,42 @@ struct TableDecoder <: AbstractSyndromeDecoder k """The lookup table corresponding to the code, slow to create""" lookup_table - """The time taken to create the lookup table + decode the code a specified number of time""" - time end -function TableDecoder(Hx, Hz) - c = CSS(Hx, Hz) +function TableDecoder(c) H = parity_checks(c) s, n = size(H) _, _, r = canonicalize!(Base.copy(H), ranks=true) k = n - r - lookup_table, time, _ = @timed create_lookup_table(H) - faults_matrix = faults_matrix(H) - return TableDecoder(H, n, s, k, faults_matrix, lookup_table, time) + lookup_table = create_lookup_table(H) + fm = faults_matrix(H) + return TableDecoder(H, n, s, k, fm, lookup_table) +end + +parity_checks(d::TableDecoder) = d.H + +function create_lookup_table(code::Stabilizer) + lookup_table = Dict() + constraints, qubits = size(code) + # In the case of no errors + lookup_table[ zeros(UInt8, constraints) ] = stab_to_gf2(zero(PauliOperator, qubits)) + # In the case of single bit errors + for bit_to_be_flipped in 1:qubits + for error_type in [single_x, single_y, single_z] + # Generate e⃗ + error = error_type(qubits, bit_to_be_flipped) + # Calculate s⃗ + # (check which stabilizer rows do not commute with the Pauli error) + syndrome = comm(error, code) + # Store s⃗ → e⃗ + lookup_table[syndrome] = stab_to_gf2(error) + end + end + lookup_table +end; + +function decode(d::TableDecoder, syndrome_sample) + return get(d.lookup_table, syndrome_sample, nothing) end struct BeliefPropDecoder <: AbstractSyndromeDecoder @@ -156,9 +209,7 @@ function BeliefPropDecoder(Hx, Hz) return BeliefPropDecoder(H, faults_matrix, n, s, k, log_probabs, channel_probs, numchecks_X, b2c_X, c2b_X, numchecks_Z, b2c_Z, c2b_Z, err, sparse_Cx, sparse_CxT, sparse_Cz, sparse_CzT) end -function decode(d::TableDecoder, syndrome_sample) - return get(d.lookup_table, syndrome_sample, nothing) -end +parity_checks(d::BeliefPropDecoder) = d.H function decode(d::BeliefPropDecoder, syndrome_sample) row_x = syndrome_sample[1:d.numchecks_X] @@ -168,61 +219,3 @@ function decode(d::BeliefPropDecoder, syndrome_sample) KguessZ, success = syndrome_decode(d.sparse_Cz, d.sparse_CzT, d.row_z, d.max_iters, d.channel_probs, d.b2c_Z, d.c2b_Z, d.log_probabs, Base.copy(d.err)) guess = vcat(KguessZ, KguessX) end - - -## NOT WORKING -function evaluate_classical_decoder(H, nsamples, init_error, gate_error, syndrome_circuit_func, encoding_circuit_func, logical_view_func, decoder_func, pre_circuit = nothing) - decoded = 0 - - H_stab = Stabilizer(fill(0x0, size(Hx, 2)), H, zeros(Bool, size(H))) - - O = faults_matrix(H_stab) - syndrome_circuit = syndrome_circuit_func(H_stab) - - s, n = size(H) - k = n - s - - errors = [PauliError(i, init_error) for i in 1:n]; - - md = MixedDestabilizer(H_stab) - - full_circuit = [] - - logview = logical_view_func(md) - logcirc, _ = syndrome_circuit_func(logview) - - noisy_syndrome_circuit = add_two_qubit_gate_noise(syndrome_circuit, gate_error); - - for gate in logcirc - type = typeof(gate) - if type == sMRZ - push!(circuit, sMRZ(gate.qubit+s, gate.bit+s)) - else - push!(circuit, type(gate.q1, gate.q2+s)) - end - end - - ecirc = encoding_circuit_func(syndrome_circuit) - if isnothing(pre_circuit) - full_circuit = vcat(pre_circuits, ecirc, errors, noisy_syndrome_circuit) - else - full_circuit = vcat(ecirc, errors, noisy_syndrome_circuit) - end - - frames = PauliFrame(nframes, n+s+k, s+k) - pftrajectories(frames, full_circuit) - syndromes = pfmeasurements(frames)[:, 1:s] - logical_syndromes = pfmeasurements(frames)[:, s+1: s+k] - - for i in 1:nsamples - guess = decode(decoder_obj, syndromes[i]) # TODO: replace 'decoder_obj' with proper object - - result = (O * (guess)) - - if result == logical_syndromes[i] - decoded += 1 - end - end - - return (nsamples - decoded) / nsamples -end diff --git a/src/pauli_frames.jl b/src/pauli_frames.jl index 71d5dc10b..461978032 100644 --- a/src/pauli_frames.jl +++ b/src/pauli_frames.jl @@ -164,7 +164,7 @@ function pftrajectories(circuit;trajectories=5000,threads=true) end function _create_pauliframe(ccircuit; trajectories=5000) - qmax=maximum((maximum(affectedqubits(g)) for g in ccircuit)) + qmax=maximum((maximum(affectedqubits(g),init=1) for g in ccircuit)) bmax=maximum((maximum(affectedbits(g),init=1) for g in ccircuit)) return PauliFrame(trajectories, qmax, bmax) end