From ab65f0dd21082a0ee8267e5cae287ce17b3810a9 Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 17 Apr 2024 16:37:09 -0400 Subject: [PATCH 1/3] misc issues noticed when preparing the decoder wiki --- CHANGELOG.md | 4 ++++ Project.toml | 4 ++-- docs/.gitignore | 1 + docs/src/graphs.md | 2 +- docs/src/references.bib | 4 ++++ ext/QuantumCliffordMakieExt/QuantumCliffordMakieExt.jl | 4 ++-- .../QuantumCliffordPyQDecodersExt.jl | 1 - 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e2a1f076..666b0afec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ # News +## v0.9.4 - dev + +- Remove printing of spurious debug info from the PyBP decoder. + ## v0.9.3 - 2024-04-10 - **(fix)** One of `random_pauli`'s methods was disregarding the error probability and had incorrect kwarg defaults. diff --git a/Project.toml b/Project.toml index aec02d96d..18db0fa02 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumClifford" uuid = "0525e862-1e90-11e9-3e4d-1b39d7109de1" authors = ["Stefan Krastanov and QuantumSavory community members"] -version = "0.9.3" +version = "0.9.4" [deps] Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" @@ -41,7 +41,7 @@ QuantumCliffordQOpticsExt = "QuantumOpticsBase" QuantumCliffordQuantikzExt = "Quantikz" [compat] -CUDA = "4.4.0" +CUDA = "4.4.0, 5" Combinatorics = "1.0" DataStructures = "0.18" DocStringExtensions = "0.9" diff --git a/docs/.gitignore b/docs/.gitignore index a303fff20..ef7b19fee 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +1,3 @@ build/ site/ +.CondaPkg/ \ No newline at end of file diff --git a/docs/src/graphs.md b/docs/src/graphs.md index d785a07ee..8fa7e5a90 100644 --- a/docs/src/graphs.md +++ b/docs/src/graphs.md @@ -20,7 +20,7 @@ graphstate(ghz(4))[1] ```@eval using Random; Random.seed!(1); using QuantumClifford, GraphMakie, CairoMakie; -f = Figure(resolution=(200,200)) +f = Figure(size=(200,200)) a = Axis(f[1,1]) graphplot!(a,graphstate(ghz(4))[1]) hidedecorations!(a); hidespines!(a) diff --git a/docs/src/references.bib b/docs/src/references.bib index 9b550181a..09820c204 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -272,6 +272,8 @@ @article{kitaev2003fault abstract = {A two-dimensional quantum system with anyonic excitations can be considered as a quantum computer. Unitary transformations can be performed by moving the excitations around each other. Measurements can be performed by joining excitations in pairs and observing the result of fusion. Such computation is fault-tolerant by its physical nature.}, pages = {2--30}, number = {1}, + year = {2003}, + journal={Annals of Physics}, journaltitle = {Annals of Physics}, shortjournal = {Annals of Physics}, author = {Kitaev, A.Yu.} @@ -286,6 +288,8 @@ @article{fowler2012surface shorttitle = {Surface codes}, pages = {032324}, number = {3}, + year = {2012}, + journal={Physical Review A}, journaltitle = {Physical Review A}, shortjournal = {Phys. Rev. A}, author = {Fowler, Austin G. and Mariantoni, Matteo and Martinis, John M. and Cleland, Andrew N.}, diff --git a/ext/QuantumCliffordMakieExt/QuantumCliffordMakieExt.jl b/ext/QuantumCliffordMakieExt/QuantumCliffordMakieExt.jl index 03b48943a..0e3437a0b 100644 --- a/ext/QuantumCliffordMakieExt/QuantumCliffordMakieExt.jl +++ b/ext/QuantumCliffordMakieExt/QuantumCliffordMakieExt.jl @@ -57,8 +57,8 @@ function stabilizerplot_axis(subfig, s; colorbar=true, args...) Makie.hidedecorations!(ax) Makie.hidespines!(ax) ax.aspect = Makie.DataAspect() - colorbar && Makie.Colorbar(subfig[2, 1], p, ticks = (0:3, ["I", "X", "Z", "Y"]), vertical = false, flipaxis = false) - Makie.colsize!(subfig.layout, 1, Makie.Aspect(1, min(1,size(s,2)/size(s,1)))) + colorbar && Makie.Colorbar(subfig[1, 2], p, ticks = ((0.5:3.51)*3/4, ["I", "X", "Z", "Y"]), vertical = true, flipaxis = true) + #Makie.colsize!(subfig.layout, 1, Makie.Aspect(1, min(1,size(s,2)/size(s,1)))) subfig,ax,p end diff --git a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl index 332c28ac7..bfa557d05 100644 --- a/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl +++ b/ext/QuantumCliffordPyQDecodersExt/QuantumCliffordPyQDecodersExt.jl @@ -71,7 +71,6 @@ parity_checks(d::PyBP) = d.H function decode(d::PyBP, syndrome_sample) row_x = syndrome_sample[1:d.nx] # TODO These copies and indirections might be costly! row_z = syndrome_sample[d.nx+1:end] - @show (size(row_x), size(row_z)) guess_z_errors = PythonCall.PyArray(d.pyx.decode(np.array(row_x))) guess_x_errors = PythonCall.PyArray(d.pyz.decode(np.array(row_z))) vcat(guess_x_errors, guess_z_errors) From 8ea1a6ac214070a2990865297928b2a8c3f755ae Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Thu, 18 Apr 2024 11:30:58 -0400 Subject: [PATCH 2/3] rename some type parameters to be more descriptive and make spellchecker happy --- .typos.toml | 1 + ext/QuantumCliffordGPUExt/apply.jl | 42 +++++++++--------- ext/QuantumCliffordGPUExt/apply_noise.jl | 6 +-- ext/QuantumCliffordGPUExt/pauli_frames.jl | 14 +++--- ext/QuantumCliffordGPUExt/types.jl | 4 +- src/QuantumClifford.jl | 46 +++++++++---------- src/dense_cliffords.jl | 1 - src/fastmemlayout.jl | 8 ++-- src/misc_ops.jl | 4 +- src/pauli_operator.jl | 28 ++++++------ src/project_trace_reset.jl | 14 +++--- src/symbolic_cliffords.jl | 54 +++++++++++------------ test/test_ecc_syndromes.jl | 8 ++-- test/test_paulis.jl | 20 ++++----- 14 files changed, 125 insertions(+), 125 deletions(-) diff --git a/.typos.toml b/.typos.toml index df2fc6063..24ca1bfcd 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,5 +1,6 @@ [default.extend-words] ket = "ket" +anc = "anc" [type.ipynb] # It detects false possitives in the base64 encoded images inside notebooks diff --git a/ext/QuantumCliffordGPUExt/apply.jl b/ext/QuantumCliffordGPUExt/apply.jl index b817262ee..d614d1f2d 100644 --- a/ext/QuantumCliffordGPUExt/apply.jl +++ b/ext/QuantumCliffordGPUExt/apply.jl @@ -4,19 +4,19 @@ using QuantumClifford: getxbit, getzbit, setxbit, setzbit # SingleQubitOperator Kernel ############################## -function single_qubit_gpu_kernel(xzs::DeviceMatrix{Tme}, - phases::DeviceVector{Tmz}, +function single_qubit_gpu_kernel(xzs::DeviceMatrix{Tₘₑ}, + phases::DeviceVector{Tₚₑ}, op::SingleQubitOperator, rows::Int, - compute_phases::Bool) where {Tme <: Unsigned, Tmz <: Unsigned} + compute_phases::Bool) where {Tₘₑ <: Unsigned, Tₚₑ <: Unsigned} idx = (blockIdx().x - 1) * blockDim().x + threadIdx().x if idx > rows return nothing end c = op.q r = idx - sh = QuantumClifford.getshift(Tme, c) - xx,zx,xz,zz = Tme.((op.xx,op.zx,op.xz,op.zz)) .<< sh # maybe putting this in parent function call will improve speed? + sh = QuantumClifford.getshift(Tₘₑ, c) + xx,zx,xz,zz = Tₘₑ.((op.xx,op.zx,op.xz,op.zz)) .<< sh # maybe putting this in parent function call will improve speed? anticom = ~iszero((~zz & xz & ~xx & zx) | ( zz & ~xz & xx & zx) | (zz & xz & xx & ~zx)) x = getxbit(xzs, r, c) @@ -45,11 +45,11 @@ end # AbstractSingleQubitOperator Kernel ############################## -function abstract_single_qubit_gpu_kernel(xzs::DeviceMatrix{Tme}, - phases::DeviceVector{Tmz}, +function abstract_single_qubit_gpu_kernel(xzs::DeviceMatrix{Tₘₑ}, + phases::DeviceVector{Tₚₑ}, gate::QuantumClifford.AbstractSingleQubitOperator, rows::Int, - compute_phases::Bool) where {Tme <: Unsigned, Tmz <: Unsigned} + compute_phases::Bool) where {Tₘₑ <: Unsigned, Tₚₑ <: Unsigned} idx = (blockIdx().x - 1) * blockDim().x + threadIdx().x if idx > rows return nothing @@ -57,8 +57,8 @@ function abstract_single_qubit_gpu_kernel(xzs::DeviceMatrix{Tme}, c = gate.q r = idx - x::Tme = getxbit(xzs, r, c) - z::Tme = getzbit(xzs, r, c) + x::Tₘₑ = getxbit(xzs, r, c) + z::Tₘₑ = getzbit(xzs, r, c) x,z,phase::Bool = QuantumClifford.qubit_kernel(gate,x,z) setxbit(xzs, r, c, x) setzbit(xzs, r, c, z) @@ -70,11 +70,11 @@ end # AbstractTwoQubitOperator Kernel ############################## -function two_qubit_gpu_kernel(xzs::DeviceMatrix{Tme}, - phases::DeviceVector{Tze}, +function two_qubit_gpu_kernel(xzs::DeviceMatrix{Tₘₑ}, + phases::DeviceVector{Tₚ}, gate::QuantumClifford.AbstractTwoQubitOperator, rows::Int, - compute_phases::Bool=true) where {Tme <: Unsigned, Tze <: Unsigned} + compute_phases::Bool=true) where {Tₘₑ <: Unsigned, Tₚ <: Unsigned} idx = (blockIdx().x - 1) * blockDim().x + threadIdx().x if idx > rows return nothing @@ -82,14 +82,14 @@ function two_qubit_gpu_kernel(xzs::DeviceMatrix{Tme}, q1 = gate.q1 q2 = gate.q2 - shift = QuantumClifford.getshift(Tme, q1) - QuantumClifford.getshift(Tme, q2) + shift = QuantumClifford.getshift(Tₘₑ, q1) - QuantumClifford.getshift(Tₘₑ, q2) r = idx; - _x1::Tme = getxbit(xzs, r, q1) - _z1::Tme = getzbit(xzs, r, q1) - _x2::Tme = getxbit(xzs, r, q2)< rows diff --git a/ext/QuantumCliffordGPUExt/pauli_frames.jl b/ext/QuantumCliffordGPUExt/pauli_frames.jl index 574d2e70c..34a0565fd 100644 --- a/ext/QuantumCliffordGPUExt/pauli_frames.jl +++ b/ext/QuantumCliffordGPUExt/pauli_frames.jl @@ -2,16 +2,16 @@ # sMZ ############################## -function apply_sMZ_kernel!(xzs::DeviceMatrix{Tme}, +function apply_sMZ_kernel!(xzs::DeviceMatrix{Tₘₑ}, measurements::DeviceMatrix{Bool}, op::sMZ, ibig::Int, - ismallm::Tme, - rows::Int) where {Tme <: Unsigned} + ismallm::Tₘₑ, + rows::Int) where {Tₘₑ <: Unsigned} f = (blockIdx().x - 1) * blockDim().x + threadIdx().x; if f > rows return nothing - end + end should_flip = !iszero(xzs[ibig,f] & ismallm) measurements[f,op.bit] = should_flip return nothing @@ -33,12 +33,12 @@ end # sMRZ ############################## -function apply_sMRZ_kernel!(xzs::DeviceMatrix{Tme}, +function apply_sMRZ_kernel!(xzs::DeviceMatrix{Tₘₑ}, measurements::DeviceMatrix{Bool}, op::QuantumClifford.sMRZ, ibig::Int, # todo change to Int - ismallm::Tme, - rows::Int) where {Tme <: Unsigned} + ismallm::Tₘₑ, + rows::Int) where {Tₘₑ <: Unsigned} f = (blockIdx().x - 1) * blockDim().x + threadIdx().x; if f > rows return nothing diff --git a/ext/QuantumCliffordGPUExt/types.jl b/ext/QuantumCliffordGPUExt/types.jl index 0b193a214..a426c21cb 100644 --- a/ext/QuantumCliffordGPUExt/types.jl +++ b/ext/QuantumCliffordGPUExt/types.jl @@ -20,13 +20,13 @@ DeviceMatrix{T} = Union{CuDeviceArray{T, 2, 1}, Adjoint{T, CuDeviceArray{T, 2, 1 function getTableauGPU(GPUValue, GPUVector, GPUMatrix) - TableauGPU{T} = QuantumClifford.Tableau{Tzv, Tm} where {T <: Unsigned, Tzv <: GPUVector{PhaseInnerType}, Tm <: GPUMatrix{T}} + TableauGPU{T} = QuantumClifford.Tableau{Tₚᵥ, Tₘ} where {T <: Unsigned, Tₚᵥ <: GPUVector{PhaseInnerType}, Tₘ <: GPUMatrix{T}} end function getStabilizerGPU(GPUValue, GPUVector, GPUMatrix, GPUTableau) StabilizerGPU{T} = QuantumClifford.Stabilizer{<:GPUTableau{T}} where {T <: Unsigned} end function getPauliOperatorGPU(GPUValue, GPUVector, GPUMatrix, GPUTableau) - PauliOperatorGPU{T} = QuantumClifford.PauliOperator{Tz, Tv} where {T <: Unsigned, Tz<:GPUValue{PhaseInnerType}, Tv<:GPUVector{T}} + PauliOperatorGPU{T} = QuantumClifford.PauliOperator{Tₚ, Tᵥ} where {T <: Unsigned, Tₚ<:GPUValue{PhaseInnerType}, Tᵥ<:GPUVector{T}} end # todo. type definition here is stronger than the code in pauliframes.jl this will cause serious problems diff --git a/src/QuantumClifford.jl b/src/QuantumClifford.jl index 01659c7d9..458a10ff5 100644 --- a/src/QuantumClifford.jl +++ b/src/QuantumClifford.jl @@ -130,18 +130,18 @@ include("pauli_operator.jl") """Internal Tableau type for storing a list of Pauli operators in a compact form. No special semantic meaning is attached to this type, it is just a convenient way to store a list of Pauli operators. E.g. it is not used to represent a stabilizer state, or a stabilizer group, or a Clifford circuit.""" -struct Tableau{Tzv<:AbstractVector{UInt8}, Tm<:AbstractMatrix{<:Unsigned}} - phases::Tzv +struct Tableau{Tₚᵥ<:AbstractVector{UInt8}, Tₘ<:AbstractMatrix{<:Unsigned}} + phases::Tₚᵥ nqubits::Int - xzs::Tm + xzs::Tₘ end -function Tableau(paulis::AbstractVector{PauliOperator{Tz,Tv}}) where {Tz<:AbstractArray{UInt8,0},Tve<:Unsigned,Tv<:AbstractVector{Tve}} +function Tableau(paulis::AbstractVector{PauliOperator{Tₚ,Tᵥ}}) where {Tₚ<:AbstractArray{UInt8,0},Tᵥₑ<:Unsigned,Tᵥ<:AbstractVector{Tᵥₑ}} r = length(paulis) n = nqubits(paulis[1]) - tab = zero(Tableau{Vector{UInt8},Matrix{Tve}},r,n)::Tableau{Vector{UInt8},Matrix{Tve}} # typeassert for JET + tab = zero(Tableau{Vector{UInt8},Matrix{Tᵥₑ}},r,n)::Tableau{Vector{UInt8},Matrix{Tᵥₑ}} # typeassert for JET for i in eachindex(paulis) - tab[i] = paulis[i]::PauliOperator{Tz,Tv} # typeassert for JET + tab[i] = paulis[i]::PauliOperator{Tₚ,Tᵥ} # typeassert for JET end tab end @@ -172,9 +172,9 @@ end Base.getindex(tab::Tableau, i::Int) = PauliOperator(tab.phases[i], nqubits(tab), tab.xzs[:,i]) @inline function Base.getindex(tab::Tableau, r::Int, c::Int) # TODO this has code repetition with the Pauli getindex - Tme = eltype(tab.xzs) - x = (tab.xzs[_div(Tme,c-1)+1,r] & Tme(0x1)<<_mod(Tme,c-1))!=0x0 - z = (tab.xzs[end÷2+_div(Tme,c-1)+1,r] & Tme(0x1)<<_mod(Tme,c-1))!=0x0 + Tₘₑ = eltype(tab.xzs) + x = (tab.xzs[_div(Tₘₑ,c-1)+1,r] & Tₘₑ(0x1)<<_mod(Tₘₑ,c-1))!=0x0 + z = (tab.xzs[end÷2+_div(Tₘₑ,c-1)+1,r] & Tₘₑ(0x1)<<_mod(Tₘₑ,c-1))!=0x0 return (x,z) end Base.getindex(tab::Tableau, r) = Tableau(tab.phases[r], nqubits(tab), tab.xzs[:,r]) @@ -200,16 +200,16 @@ function Base.setindex!(tab::Tableau, t::Tableau, i) tab end -function Base.setindex!(tab::Tableau{Tzv,Tm}, (x,z)::Tuple{Bool,Bool}, i, j) where {Tzv<:AbstractVector{UInt8}, Tme<:Unsigned, Tm<:AbstractMatrix{Tme}} # TODO this has code repetitions with the Pauli setindex +function Base.setindex!(tab::Tableau{Tₚᵥ,Tₘ}, (x,z)::Tuple{Bool,Bool}, i, j) where {Tₚᵥ<:AbstractVector{UInt8}, Tₘₑ<:Unsigned, Tₘ<:AbstractMatrix{Tₘₑ}} # TODO this has code repetitions with the Pauli setindex if x - tab.xzs[_div(Tme,j-1)+1, i] |= Tme(0x1)<<_mod(Tme,j-1) + tab.xzs[_div(Tₘₑ,j-1)+1, i] |= Tₘₑ(0x1)<<_mod(Tₘₑ,j-1) else - tab.xzs[_div(Tme,j-1)+1, i] &= ~(Tme(0x1)<<_mod(Tme,j-1)) + tab.xzs[_div(Tₘₑ,j-1)+1, i] &= ~(Tₘₑ(0x1)<<_mod(Tₘₑ,j-1)) end if z - tab.xzs[end÷2+_div(Tme,j-1)+1, i] |= Tme(0x1)<<_mod(Tme,j-1) + tab.xzs[end÷2+_div(Tₘₑ,j-1)+1, i] |= Tₘₑ(0x1)<<_mod(Tₘₑ,j-1) else - tab.xzs[end÷2+_div(Tme,j-1)+1, i] &= ~(Tme(0x1)<<_mod(Tme,j-1)) + tab.xzs[end÷2+_div(Tₘₑ,j-1)+1, i] &= ~(Tₘₑ(0x1)<<_mod(Tₘₑ,j-1)) end tab end @@ -235,7 +235,7 @@ Base.hash(t::Tableau, h::UInt) = hash(t.nqubits, hash(t.phases, hash(t.xzs, h))) Base.copy(t::Tableau) = Tableau(copy(t.phases), t.nqubits, copy(t.xzs)) -function Base.zero(::Type{Tableau{Tzv, Tm}}, r, q) where {Tzv,T<:Unsigned,Tm<:AbstractMatrix{T}} +function Base.zero(::Type{Tableau{Tₚᵥ, Tₘ}}, r, q) where {Tₚᵥ,T<:Unsigned,Tₘ<:AbstractMatrix{T}} phases = zeros(UInt8,r) xzs = zeros(UInt, _nchunks(q,T), r) Tableau(phases, q, xzs)::Tableau{Vector{UInt8},Matrix{UInt}} @@ -354,8 +354,8 @@ struct Stabilizer{T<:Tableau} <: AbstractStabilizer tab::T end -Stabilizer(phases::Tzv, nqubits::Int, xzs::Tm) where {Tzv<:AbstractVector{UInt8}, Tm<:AbstractMatrix{<:Unsigned}} = Stabilizer(Tableau(phases, nqubits, xzs)) -Stabilizer(paulis::AbstractVector{PauliOperator{Tz,Tv}}) where {Tz,Tv} = Stabilizer(Tableau(paulis)) +Stabilizer(phases::Tₚᵥ, nqubits::Int, xzs::Tₘ) where {Tₚᵥ<:AbstractVector{UInt8}, Tₘ<:AbstractMatrix{<:Unsigned}} = Stabilizer(Tableau(phases, nqubits, xzs)) +Stabilizer(paulis::AbstractVector{PauliOperator{Tₚ,Tᵥ}}) where {Tₚ,Tᵥ} = Stabilizer(Tableau(paulis)) Stabilizer(phases::AbstractVector{UInt8}, xs::AbstractMatrix{Bool}, zs::AbstractMatrix{Bool}) = Stabilizer(Tableau(phases, xs, zs)) Stabilizer(phases::AbstractVector{UInt8}, xzs::AbstractMatrix{Bool}) = Stabilizer(Tableau(phases, xzs)) Stabilizer(xs::AbstractMatrix{Bool}, zs::AbstractMatrix{Bool}) = Stabilizer(Tableau(xs,zs)) @@ -832,13 +832,13 @@ end # TODO is this used anywhere? """Swap two columns of a stabilizer in place.""" @inline function colswap!(s::Tableau, i, j) - Tme = eltype(s.xzs) - lowbit = Tme(1) - ibig = _div(Tme,i-1)+1 - ismall = _mod(Tme,i-1) + Tₘₑ = eltype(s.xzs) + lowbit = Tₘₑ(1) + ibig = _div(Tₘₑ,i-1)+1 + ismall = _mod(Tₘₑ,i-1) ismallm = lowbit<<(ismall) - jbig = _div(Tme,j-1)+1 - jsmall = _mod(Tme,j-1) + jbig = _div(Tₘₑ,j-1)+1 + jsmall = _mod(Tₘₑ,j-1) jsmallm = lowbit<<(jsmall) for off in [0,size(s.xzs,2)÷2] ibig += off diff --git a/src/dense_cliffords.jl b/src/dense_cliffords.jl index 4510819bc..20d2cc305 100644 --- a/src/dense_cliffords.jl +++ b/src/dense_cliffords.jl @@ -136,7 +136,6 @@ function _apply!(stab::AbstractStabilizer, c::CliffordOperator; phases::Val{B}=V end # TODO Added a lot of type assertions to help Julia infer types, but they are much too strict for cases where bitpacking varies (check tests) -#@inline function apply_row_kernel!(new_stabrow::PauliOperator{Array{UInt8,0},Vector{Tme}}, row::Int, s_tab::Stabilizer{Tv,Tm}, c_tab::Stabilizer{Tv,Tm}; phases=true) where {Tme,Tv<:AbstractVector{UInt8},Tm<:AbstractMatrix{Tme}} @inline function apply_row_kernel!(new_stabrow, row, s_tab, c_tab; phases::Val{B}=Val(true)) where B B && (new_stabrow.phase[] = s_tab.phases[row]) n = nqubits(c_tab) diff --git a/src/fastmemlayout.jl b/src/fastmemlayout.jl index 3a6f6e71b..f7019cd1a 100644 --- a/src/fastmemlayout.jl +++ b/src/fastmemlayout.jl @@ -15,10 +15,10 @@ but it is still optimal given that some bitwrangling is required to extract a gi See also: [`fastrow`](@ref)""" function fastcolumn end -fastrow(t::Tableau{Tzv,Tm}) where {Tzv, Tm} = t -fastrow(t::Tableau{Tzv,Tm}) where {Tzv, Tm<:Adjoint} = Tableau(t.phases, t.nqubits, collect(t.xzs)) -fastcolumn(t::Tableau{Tzv,Tm}) where {Tzv, Tm} = Tableau(t.phases, t.nqubits, collect(t.xzs')') -fastcolumn(t::Tableau{Tzv,Tm}) where {Tzv, Tm<:Adjoint} = t +fastrow(t::Tableau{Tₚᵥ,Tₘ}) where {Tₚᵥ, Tₘ} = t +fastrow(t::Tableau{Tₚᵥ,Tₘ}) where {Tₚᵥ, Tₘ<:Adjoint} = Tableau(t.phases, t.nqubits, collect(t.xzs)) +fastcolumn(t::Tableau{Tₚᵥ,Tₘ}) where {Tₚᵥ, Tₘ} = Tableau(t.phases, t.nqubits, collect(t.xzs')') +fastcolumn(t::Tableau{Tₚᵥ,Tₘ}) where {Tₚᵥ, Tₘ<:Adjoint} = t fastrow(s::Stabilizer) = Stabilizer(fastrow(s.tab)) fastcolumn(s::Stabilizer) = Stabilizer(fastcolumn(s.tab)) diff --git a/src/misc_ops.jl b/src/misc_ops.jl index b59d59aeb..530e0d0b2 100644 --- a/src/misc_ops.jl +++ b/src/misc_ops.jl @@ -4,8 +4,8 @@ Particularly useful when acting on [`Register`](@ref). See also: [`apply!`](@ref), [`projectrand!`](@ref).""" -struct PauliMeasurement{Tz<:AbstractArray{UInt8,0}, Tv<:AbstractVector{<:Unsigned}} <: AbstractMeasurement - pauli::PauliOperator{Tz,Tv} +struct PauliMeasurement{Tₚ<:AbstractArray{UInt8,0}, Tᵥ<:AbstractVector{<:Unsigned}} <: AbstractMeasurement + pauli::PauliOperator{Tₚ,Tᵥ} bit::Int end diff --git a/src/pauli_operator.jl b/src/pauli_operator.jl index 689d71263..640092b55 100644 --- a/src/pauli_operator.jl +++ b/src/pauli_operator.jl @@ -39,13 +39,13 @@ julia> p[1] = (true, true); p + YYZ ``` """ -struct PauliOperator{Tz<:AbstractArray{UInt8,0}, Tv<:AbstractVector{<:Unsigned}} <: AbstractCliffordOperator - phase::Tz +struct PauliOperator{Tₚ<:AbstractArray{UInt8,0}, Tᵥ<:AbstractVector{<:Unsigned}} <: AbstractCliffordOperator + phase::Tₚ nqubits::Int - xz::Tv + xz::Tᵥ end -PauliOperator(phase::UInt8, nqubits::Int, xz::Tv) where Tv<:AbstractVector{<:Unsigned} = PauliOperator(fill(UInt8(phase),()), nqubits, xz) +PauliOperator(phase::UInt8, nqubits::Int, xz::Tᵥ) where Tᵥ<:AbstractVector{<:Unsigned} = PauliOperator(fill(UInt8(phase),()), nqubits, xz) function PauliOperator(phase::UInt8, x::AbstractVector{Bool}, z::AbstractVector{Bool}) phase = fill(UInt8(phase),()) xs = reinterpret(UInt,BitVector(x).chunks)::Vector{UInt} @@ -87,19 +87,19 @@ macro P_str(a) quote _P_str($a) end end -Base.getindex(p::PauliOperator{Tz,Tv}, i::Int) where {Tz, Tve<:Unsigned, Tv<:AbstractVector{Tve}} = (p.xz[_div(Tve, i-1)+1] & Tve(0x1)<<_mod(Tve,i-1))!=0x0, (p.xz[end÷2+_div(Tve,i-1)+1] & Tve(0x1)<<_mod(Tve,i-1))!=0x0 -Base.getindex(p::PauliOperator{Tz,Tv}, r) where {Tz, Tve<:Unsigned, Tv<:AbstractVector{Tve}} = PauliOperator(p.phase[], xbit(p)[r], zbit(p)[r]) +Base.getindex(p::PauliOperator{Tₚ,Tᵥ}, i::Int) where {Tₚ, Tᵥₑ<:Unsigned, Tᵥ<:AbstractVector{Tᵥₑ}} = (p.xz[_div(Tᵥₑ, i-1)+1] & Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1))!=0x0, (p.xz[end÷2+_div(Tᵥₑ,i-1)+1] & Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1))!=0x0 +Base.getindex(p::PauliOperator{Tₚ,Tᵥ}, r) where {Tₚ, Tᵥₑ<:Unsigned, Tᵥ<:AbstractVector{Tᵥₑ}} = PauliOperator(p.phase[], xbit(p)[r], zbit(p)[r]) -function Base.setindex!(p::PauliOperator{Tz,Tv}, (x,z)::Tuple{Bool,Bool}, i) where {Tz, Tve, Tv<:AbstractVector{Tve}} +function Base.setindex!(p::PauliOperator{Tₚ,Tᵥ}, (x,z)::Tuple{Bool,Bool}, i) where {Tₚ, Tᵥₑ, Tᵥ<:AbstractVector{Tᵥₑ}} if x - p.xz[_div(Tve,i-1)+1] |= Tve(0x1)<<_mod(Tve,i-1) + p.xz[_div(Tᵥₑ,i-1)+1] |= Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1) else - p.xz[_div(Tve,i-1)+1] &= ~(Tve(0x1)<<_mod(Tve,i-1)) + p.xz[_div(Tᵥₑ,i-1)+1] &= ~(Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1)) end if z - p.xz[end÷2+_div(Tve,i-1)+1] |= Tve(0x1)<<_mod(Tve,i-1) + p.xz[end÷2+_div(Tᵥₑ,i-1)+1] |= Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1) else - p.xz[end÷2+_div(Tve,i-1)+1] &= ~(Tve(0x1)<<_mod(Tve,i-1)) + p.xz[end÷2+_div(Tᵥₑ,i-1)+1] &= ~(Tᵥₑ(0x1)<<_mod(Tᵥₑ,i-1)) end p end @@ -123,13 +123,13 @@ Base.hash(p::PauliOperator, h::UInt) = hash(p.phase,hash(p.nqubits,hash(p.xz, h) Base.copy(p::PauliOperator) = PauliOperator(copy(p.phase),p.nqubits,copy(p.xz)) _nchunks(i::Int,T::Type{<:Unsigned}) = 2*( (i-1) ÷ (8*sizeof(T)) + 1 ) -Base.zero(::Type{PauliOperator{Tz, Tv}}, q) where {Tz,T<:Unsigned,Tv<:AbstractVector{T}} = PauliOperator(zeros(UInt8), q, zeros(T, _nchunks(q,T))) +Base.zero(::Type{PauliOperator{Tₚ, Tᵥ}}, q) where {Tₚ,T<:Unsigned,Tᵥ<:AbstractVector{T}} = PauliOperator(zeros(UInt8), q, zeros(T, _nchunks(q,T))) Base.zero(::Type{PauliOperator}, q) = zero(PauliOperator{Array{UInt8, 0}, Vector{UInt}}, q) Base.zero(p::P) where {P<:PauliOperator} = zero(P, nqubits(p)) """Zero-out the phases and single-qubit operators in a [`PauliOperator`](@ref)""" -@inline function zero!(p::PauliOperator{Tz,Tv}) where {Tz, Tve<:Unsigned, Tv<:AbstractVector{Tve}} - fill!(p.xz, zero(Tve)) +@inline function zero!(p::PauliOperator{Tₚ,Tᵥ}) where {Tₚ, Tᵥₑ<:Unsigned, Tᵥ<:AbstractVector{Tᵥₑ}} + fill!(p.xz, zero(Tᵥₑ)) p.phase[] = 0x0 p end diff --git a/src/project_trace_reset.jl b/src/project_trace_reset.jl index d28df58c3..8ea01f38c 100644 --- a/src/project_trace_reset.jl +++ b/src/project_trace_reset.jl @@ -38,19 +38,19 @@ function generate!(pauli::PauliOperator, stabilizer::Stabilizer; phases::Bool=tr @valbooldispatch _generate!(pauli, stabilizer; phases=Val(phases), saveindices=Val(saveindices)) phases saveindices end -function _generate!(pauli::PauliOperator{Tz,Tv}, stabilizer::Stabilizer{Tableau{Tzv,Tm}}; phases::Val{PHASES}=Val(true), saveindices::Val{SAVEIDX}=Val(true)) where {Tz<:AbstractArray{UInt8,0}, Tzv<:AbstractVector{UInt8}, Tme<:Unsigned, Tv<:AbstractVector{Tme}, Tm<:AbstractMatrix{Tme}, PHASES, SAVEIDX} # TODO there is stuff that can be abstracted away here and in canonicalize! +function _generate!(pauli::PauliOperator{Tₚ,Tᵥ}, stabilizer::Stabilizer{Tableau{Tₚᵥ,Tₘ}}; phases::Val{PHASES}=Val(true), saveindices::Val{SAVEIDX}=Val(true)) where {Tₚ<:AbstractArray{UInt8,0}, Tₚᵥ<:AbstractVector{UInt8}, Tₘₑ<:Unsigned, Tᵥ<:AbstractVector{Tₘₑ}, Tₘ<:AbstractMatrix{Tₘₑ}, PHASES, SAVEIDX} # TODO there is stuff that can be abstracted away here and in canonicalize! xzs = tab(stabilizer).xzs xs = @view xzs[1:end÷2,:] zs = @view xzs[end÷2+1:end,:] - lowbit = Tme(0x1) - zerobit = Tme(0x0) + lowbit = Tₘₑ(0x1) + zerobit = Tₘₑ(0x0) px,pz = xview(pauli), zview(pauli) used_indices = Int[] used = 0 # remove Xs while (i=unsafe_bitfindnext_(px,1); i !== nothing) # TODO awkward notation due to https://github.com/JuliaLang/julia/issues/45499 - jbig = _div(Tme,i-1)+1 - jsmall = lowbit<<_mod(Tme,i-1) + jbig = _div(Tₘₑ,i-1)+1 + jsmall = lowbit<<_mod(Tₘₑ,i-1) candidate = findfirst(e->e&jsmall!=zerobit, # TODO some form of reinterpret might be faster than equality check xs[jbig,used+1:end]) if isnothing(candidate) @@ -63,8 +63,8 @@ function _generate!(pauli::PauliOperator{Tz,Tv}, stabilizer::Stabilizer{Tableau{ end # remove Zs while (i=unsafe_bitfindnext_(pz,1); i !== nothing) # TODO awkward notation due to https://github.com/JuliaLang/julia/issues/45499 - jbig = _div(Tme,i-1)+1 - jsmall = lowbit<<_mod(Tme,i-1) + jbig = _div(Tₘₑ,i-1)+1 + jsmall = lowbit<<_mod(Tₘₑ,i-1) candidate = findfirst(e->e&jsmall!=zerobit, # TODO some form of reinterpret might be faster than equality check zs[jbig,used+1:end]) if isnothing(candidate) diff --git a/src/symbolic_cliffords.jl b/src/symbolic_cliffords.jl index 0d69879a9..b143589b3 100644 --- a/src/symbolic_cliffords.jl +++ b/src/symbolic_cliffords.jl @@ -12,26 +12,26 @@ abstract type AbstractMeasurement <: AbstractOperation end # Stim has a good list of specialized single and two qubit operations at https://github.com/quantumlib/Stim/blob/e51ea66d213b25920e72c08e53266ec56fd14db4/src/stim/stabilizers/tableau_specialized_prepend.cc # Note that their specialized operations are for prepends (right multiplications), while we implement append (left multiplication) operations. -@inline getshift(::Type{Tme},col::Int) where {Tme} = _mod(Tme,col-1) -@inline getmask(::Type{Tme},col::Int) where {Tme} = Tme(0x1)< Date: Thu, 18 Apr 2024 13:12:16 -0400 Subject: [PATCH 3/3] configure two gate noise in various code evaluation setups (#253) --------- Co-authored-by: Stefan Krastanov --- CHANGELOG.md | 1 + .../QuantumCliffordQuantikzExt.jl | 2 +- src/ecc/decoder_pipeline.jl | 38 ++++++++++++++++--- src/noise.jl | 8 ++-- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 666b0afec..75c4fd6a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## v0.9.4 - dev +- Gate errors are now conveniently supported by the various ECC benchmark setups in the `ECC` module. - Remove printing of spurious debug info from the PyBP decoder. ## v0.9.3 - 2024-04-10 diff --git a/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl b/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl index 8f78f858b..f5d9ca858 100644 --- a/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl +++ b/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl @@ -71,7 +71,7 @@ function Quantikz.QuantikzOp(op::Reset) # TODO This is complicated because quant Quantikz.Initialize("$str",affectedqubits(op)) # TODO make Quantikz work with tuples and remove the collect end end -Quantikz.QuantikzOp(op::NoiseOp) = Quantikz.Noise(op.indices) +Quantikz.QuantikzOp(op::NoiseOp) = Quantikz.Noise(collect(op.indices)) Quantikz.QuantikzOp(op::NoiseOpAll) = Quantikz.NoiseAll() Quantikz.QuantikzOp(op::ClassicalXORConcreteWorkaround) = Quantikz.ClassicalDecision(sort([op.store, op.bits...])) diff --git a/src/ecc/decoder_pipeline.jl b/src/ecc/decoder_pipeline.jl index 1c9828b67..d1fbe897f 100644 --- a/src/ecc/decoder_pipeline.jl +++ b/src/ecc/decoder_pipeline.jl @@ -79,18 +79,46 @@ struct ShorSyndromeECCSetup <: AbstractECCSetup end end +function add_two_qubit_gate_noise(g, gate_error) + return () +end + +"""Applies gate_error to a given two-qubit gate g.""" +function add_two_qubit_gate_noise(g::AbstractTwoQubitOperator, gate_error) + qubits = affectedqubits(g) + return (PauliError(qubits, gate_error), ) +end + function physical_ECC_circuit(H, setup::NaiveSyndromeECCSetup) syndrome_circ, n_anc, syndrome_bits = naive_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)]; + noisy_syndrome_circ = [] + + for op in syndrome_circ + push!(noisy_syndrome_circ, op) + for noise_op in add_two_qubit_gate_noise(op, setup.two_qubit_gate_noise) + push!(noisy_syndrome_circ, noise_op) + end + end + + mem_error_circ = [PauliError(i, setup.mem_noise) for i in 1:nqubits(H)] circ = [mem_error_circ..., noisy_syndrome_circ...] - circ, syndrome_bits, n_anc + return circ, syndrome_bits, n_anc end + 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)]; + + noisy_syndrome_circ = [] + for op in syndrome_circ + push!(noisy_syndrome_circ, op) + for noise_op in add_two_qubit_gate_noise(op, setup.two_qubit_gate_noise) + push!(noisy_syndrome_circ, noise_op) + end + end + + 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 diff --git a/src/noise.jl b/src/noise.jl index fabc748c9..c4e7b095a 100644 --- a/src/noise.jl +++ b/src/noise.jl @@ -52,11 +52,13 @@ function applynoise!(s::AbstractStabilizer,noise::UnbiasedUncorrelatedNoise,i::I end """An operator that applies the given `noise` model to the qubits at the selected `indices`.""" -struct NoiseOp <: AbstractNoiseOp - noise::AbstractNoise - indices::AbstractVector{Int} +struct NoiseOp{N, Q} <: AbstractNoiseOp where {N, Q} + noise::N #<:AbstractNoise + indices::NTuple{Q, Int} end +NoiseOp(noise, indices::AbstractVector{Int}) = NoiseOp(noise, tuple(indices...)) + """A convenient constructor for various types of Pauli errors, that can be used as circuit gates in simulations. Returns more specific types when necessary."""