From ed0c0ba9ff530f39e3fe51582c65bd82d9d0d51e Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sun, 8 Sep 2024 15:14:22 +0500 Subject: [PATCH 01/17] implement project! for genstab --- src/nonclifford.jl | 81 ++++++++++++++++++++++++-- test/test_nonclifford_quantumoptics.jl | 76 +++++++++++++----------- 2 files changed, 116 insertions(+), 41 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index eba12bd39..e8afc6f70 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -98,13 +98,13 @@ end Expectation value for the [PauliOperator](@ref) observable given the [`GeneralizedStabilizer`](@ref) state `s`.""" function expect(p::PauliOperator, s::GeneralizedStabilizer) # TODO optimize - e = zero(_dictvaltype(s.destabweights)) + χ′ = zero(_dictvaltype(s.destabweights)) phase, b, c = rowdecompose(p, s.stab) for ((dᵢ,dⱼ), χ) in s.destabweights _allthreesumtozero(dᵢ,dⱼ,b) || continue - e += χ * (-1)^(dᵢ'*c) + χ′ += χ * (-1)^(dᵢ'*c) end - return (-1)^(phase÷2) * e + return (im)^(phase) * χ′ end """Same as `all(==(0), (a.+b.+c) .% 2)`""" @@ -119,15 +119,84 @@ function _dictvaltype(dict) return eltype(dict).parameters[2] # TODO there must be a cleaner way to do this end +""" +Project the state of a [`GeneralizedStabilizer`](@ref) on the two eigenspaces of a +Pauli operator. + +The projection is determined based on the expectation value of `p` with respect to +the[`GeneralizedStabilizer`](@ref). + +When the expectation value of `p` is close to 1, the [`GeneralizedStabilizer`](@ref) +`sm` is projected onto the +1 eigenspace of `p`. Conversely, if the expectation +value is near -1, the [`GeneralizedStabilizer`](@ref) `sm` projected onto the +1 +eigenspace of `-p`, which corresponds to the -1 eigenspace of `p`. + +For expectationvalues that fall between these extremes, the projection is +determined probabilistically according to the magnitude of the expectation +value. + + +```jldoctest +julia> sm = GeneralizedStabilizer(S"X") +A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is +𝒟ℯ𝓈𝓉𝒶𝒷 ++ Z +𝒮𝓉𝒶𝒷 ++ X +with ϕᵢⱼ | Pᵢ | Pⱼ: + 1.0+0.0im | + _ | + _ + +julia> apply!(sm, pcT) +A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is +𝒟ℯ𝓈𝓉𝒶𝒷 ++ Z +𝒮𝓉𝒶𝒷 ++ X +with ϕᵢⱼ | Pᵢ | Pⱼ: + 0.0+0.353553im | + _ | + Z + 0.0-0.353553im | + Z | + _ + 0.853553+0.0im | + _ | + _ + 0.146447+0.0im | + Z | + Z + +julia> project!(sm, P"Y")[1] +A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is +𝒟ℯ𝓈𝓉𝒶𝒷 ++ X +𝒮𝓉𝒶𝒷 ++ Y +with ϕᵢⱼ | Pᵢ | Pⱼ: + 0.0+0.353553im | + _ | + X + 0.0-0.353553im | + X | + _ + 0.853553+0.0im | + _ | + _ + 0.146447+0.0im | + X | + X +``` +""" function project!(sm::GeneralizedStabilizer, p::PauliOperator) eval = expect(p, sm) prob₁ = (real(eval)+1)/2 - error("This functionality is not implemented yet") + if prob₁ ≈ 0 + return _proj₊(sm, -p) + elseif prob₁ ≈ 1 + return _proj₊(sm, p) + else + if rand() < prob₁ + return _proj₊(sm, p) + else + return _proj₊(sm, -p) + end + end end -function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) -end function _proj₊(sm::GeneralizedStabilizer, p::PauliOperator) + newstab, anticom_idx, res = project!(sm.stab, p) + sm.stab = newstab + return sm, anticom_idx, res +end + +function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) + newstab, anticom_idx, res = project!(sm.stab, -p) + sm.stab = newstab + return sm, anticom_idx, res end abstract type AbstractPauliChannel <: AbstractOperation end diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 48f51881c..b4f99ba36 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -13,48 +13,54 @@ qo_tgate.data[2,2] = exp(im*pi/4) ## -for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] - for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] - gs = GeneralizedStabilizer(s) - apply!(gs, pcT) - ρ = dm(qo_tgate*Ket(s)) - @test Operator(gs) ≈ ρ - if isapprox(expect(p, gs), expect(Operator(p),ρ); atol=1e-5) - else - println("failure of expectation calculation for state = T*(", s, ") and observable = ", p) +@testset "expect" begin + for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] + for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] + gs = GeneralizedStabilizer(s) + apply!(gs, pcT) + ρ = dm(qo_tgate*Ket(s)) + @test Operator(gs) ≈ ρ + @test isapprox(expect(p, gs), expect(Operator(p),ρ); atol=1e-5) end end -end - -## -for _ in 1:10 - for n in 1:1 - i = rand(1:n) - stab = random_stabilizer(n) - genstab = GeneralizedStabilizer(stab) - ket = Ket(stab) - @test dm(ket) ≈ Operator(stab) - @test dm(ket) ≈ Operator(genstab) + for _ in 1:10 + for n in 1:1 + i = rand(1:n) + stab = random_stabilizer(n) + genstab = GeneralizedStabilizer(stab) + ket = Ket(stab) + @test dm(ket) ≈ Operator(stab) + @test dm(ket) ≈ Operator(genstab) - pauli = random_pauli(n; nophase=false, realphase=true) - qo_pauli = Operator(pauli) + pauli = random_pauli(n; nophase=false, realphase=true) + qo_pauli = Operator(pauli) - qo_bigtgate = n==1 ? qo_tgate : embed(qo_basis^n, i, qo_tgate) - bigtgate = embed(n,i, pcT) - @test qo_bigtgate ≈ Operator(bigtgate) + qo_bigtgate = n==1 ? qo_tgate : embed(qo_basis^n, i, qo_tgate) + bigtgate = embed(n,i, pcT) + @test qo_bigtgate ≈ Operator(bigtgate) - for step in 1:10 + for step in 1:10 # apply!(ket, qo_bigtgate) TODO implement this API - ket = qo_bigtgate*ket - apply!(genstab, bigtgate) - @test dm(ket) ≈ Operator(genstab) - if isapprox(expect(qo_pauli, ket), expect(pauli, genstab); atol=1e-5) - else - @show step - ref[] = (stab,pauli) - break - end + ket = qo_bigtgate*ket + apply!(genstab, bigtgate) + @test dm(ket) ≈ Operator(genstab) + @test isapprox(expect(qo_pauli, ket), expect(pauli, genstab); atol=1e-5) + end + end + end +end + +@testset "project" begin + for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] + for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] + gs = GeneralizedStabilizer(s) + apply!(gs, pcT) + ρ = Operator(gs) + gs_proj, _, _ = project!(gs, p) + ρ_proj = Operator(gs_proj) + @test isapprox(Operator(gs_proj), ρ_proj; atol=1e-5) + @test isapprox(expect(p, gs_proj), expect(Operator(p), ρ_proj); atol=1e-5) end end end From 22df2fbc6b0b859820854b8e2fae48dad42ddc34 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 27 Sep 2024 01:49:02 +0500 Subject: [PATCH 02/17] improve documentation and code quality --- src/nonclifford.jl | 54 +++++++++++--------------- test/test_nonclifford_quantumoptics.jl | 4 +- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index e8afc6f70..4e5a56067 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -120,29 +120,23 @@ function _dictvaltype(dict) end """ -Project the state of a [`GeneralizedStabilizer`](@ref) on the two eigenspaces of a -Pauli operator. - -The projection is determined based on the expectation value of `p` with respect to -the[`GeneralizedStabilizer`](@ref). - -When the expectation value of `p` is close to 1, the [`GeneralizedStabilizer`](@ref) -`sm` is projected onto the +1 eigenspace of `p`. Conversely, if the expectation -value is near -1, the [`GeneralizedStabilizer`](@ref) `sm` projected onto the +1 -eigenspace of `-p`, which corresponds to the -1 eigenspace of `p`. - -For expectationvalues that fall between these extremes, the projection is -determined probabilistically according to the magnitude of the expectation -value. +Projects the state of a [`GeneralizedStabilizer`](@ref) onto the eigenspaces of a +given Pauli operator `p`. The projection is determined by the expectation value of +`p` with respect to the [`GeneralizedStabilizer`](@ref). +If the expectation value `⟨p⟩` is close to `1`, the state is projected onto the `+1` + eigenspace of `p`. Conversely, if `⟨p⟩` is near `−1`, the state is projected onto +the `−1` eigenspace of `p`. For intermediate values of `⟨p⟩`, the projection is +performed probabilistically, with the likelihood of projecting onto the `+1` or +`−1` eigenspace proportional to the expectation value. ```jldoctest -julia> sm = GeneralizedStabilizer(S"X") +julia> sm = GeneralizedStabilizer(S"-X") A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 + Z 𝒮𝓉𝒶𝒷 -+ X +- X with ϕᵢⱼ | Pᵢ | Pⱼ: 1.0+0.0im | + _ | + _ @@ -151,39 +145,36 @@ A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 + Z 𝒮𝓉𝒶𝒷 -+ X +- X with ϕᵢⱼ | Pᵢ | Pⱼ: 0.0+0.353553im | + _ | + Z 0.0-0.353553im | + Z | + _ 0.853553+0.0im | + _ | + _ 0.146447+0.0im | + Z | + Z -julia> project!(sm, P"Y")[1] +julia> project!(sm, P"-Y")[1] A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 -+ X +- X 𝒮𝓉𝒶𝒷 + Y with ϕᵢⱼ | Pᵢ | Pⱼ: - 0.0+0.353553im | + _ | + X - 0.0-0.353553im | + X | + _ + 0.0+0.353553im | + _ | - X + 0.0-0.353553im | - X | + _ 0.853553+0.0im | + _ | + _ - 0.146447+0.0im | + X | + X + 0.146447+0.0im | - X | - X ``` + """ function project!(sm::GeneralizedStabilizer, p::PauliOperator) eval = expect(p, sm) - prob₁ = (real(eval)+1)/2 - if prob₁ ≈ 0 - return _proj₊(sm, -p) - elseif prob₁ ≈ 1 + prob = (real(eval)+1)/2 + if prob ≈ 0 + return _proj₋(sm, p) + elseif prob ≈ 1 return _proj₊(sm, p) else - if rand() < prob₁ - return _proj₊(sm, p) - else - return _proj₊(sm, -p) - end + return rand() < prob ? _proj₊(sm, p) : _proj₋(sm, p) end end @@ -241,6 +232,7 @@ function embed(n::Int,idx,pc::PauliChannel) end nqubits(pc::PauliChannel) = nqubits(pc.paulis[1][1]) +nqubits(sm::GeneralizedStabilizer) = nqubits(sm.stab) function apply!(state::GeneralizedStabilizer, gate::PauliChannel) dict = state.destabweights diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index dc3534a0b..7e5625089 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -50,8 +50,8 @@ qo_tgate.data[2,2] = exp(im*pi/4) end end end - -@testset "project" begin + +@testset "project!" begin for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] gs = GeneralizedStabilizer(s) From dd48b53b2c52000c33950e4d7b24b586caadbdb7 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 27 Sep 2024 19:27:37 +0500 Subject: [PATCH 03/17] fix misc errors --- src/nonclifford.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 2b23531e2..3d2307f17 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -206,7 +206,7 @@ A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 - X 𝒮𝓉𝒶𝒷 -+ Y +- Y with ϕᵢⱼ | Pᵢ | Pⱼ: 0.0+0.353553im | + _ | - X 0.0-0.353553im | - X | + _ @@ -426,4 +426,3 @@ const pcT = UnitaryPauliChannel( (I, Z), ((1+exp(im*π/4))/2, (1-exp(im*π/4))/2) ) - \ No newline at end of file From 8047c52902cc78b5aabff449763a8d305a62de0f Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Tue, 1 Oct 2024 09:52:43 +0500 Subject: [PATCH 04/17] add codereview suggestions: project! and projectrand! --- src/nonclifford.jl | 51 +++++++++++++------------- test/test_nonclifford_quantumoptics.jl | 1 - 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 3d2307f17..594cb2b05 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -170,14 +170,7 @@ end """ Projects the state of a [`GeneralizedStabilizer`](@ref) onto the eigenspaces of a -given Pauli operator `p`. The projection is determined by the expectation value of -`p` with respect to the [`GeneralizedStabilizer`](@ref). - -If the expectation value `⟨p⟩` is close to `1`, the state is projected onto the `+1` - eigenspace of `p`. Conversely, if `⟨p⟩` is near `−1`, the state is projected onto -the `−1` eigenspace of `p`. For intermediate values of `⟨p⟩`, the projection is -performed probabilistically, with the likelihood of projecting onto the `+1` or -`−1` eigenspace proportional to the expectation value. +given Pauli operator `p`. ```jldoctest julia> sm = GeneralizedStabilizer(S"-X") @@ -201,45 +194,51 @@ with ϕᵢⱼ | Pᵢ | Pⱼ: 0.853553+0.0im | + _ | + _ 0.146447+0.0im | + Z | + Z -julia> project!(sm, P"-Y")[1] +julia> project!(sm, P"-X")[1] A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 -- X ++ Z 𝒮𝓉𝒶𝒷 -- Y +- X with ϕᵢⱼ | Pᵢ | Pⱼ: - 0.0+0.353553im | + _ | - X - 0.0-0.353553im | - X | + _ - 0.853553+0.0im | + _ | + _ - 0.146447+0.0im | - X | - X + 0.707107+0.0im | + _ | + _ ``` """ function project!(sm::GeneralizedStabilizer, p::PauliOperator) + newstab, anticom_idx, res = project!(sm.stab, p) + χ′ = expect(p, sm) + n = nqubits(newstab) + newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) + return newsm, anticom_idx, res +end + +""" +$TYPEDSIGNATURES + +Measure `pauli` operator on `state` and randomize the phase if necessary. +""" +function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) eval = expect(p, sm) prob = (real(eval)+1)/2 - if prob ≈ 0 - return _proj₋(sm, p) - elseif prob ≈ 1 - return _proj₊(sm, p) - else - return rand() < prob ? _proj₊(sm, p) : _proj₋(sm, p) - end + return rand() < prob ? _proj₊(sm, p) : _proj₋(sm, p) end function _proj₊(sm::GeneralizedStabilizer, p::PauliOperator) newstab, anticom_idx, res = project!(sm.stab, p) sm.stab = newstab - return sm, anticom_idx, res + n = nqubits(newstab) + newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>expect(p, sm))) + return newsm, anticom_idx, res end function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) newstab, anticom_idx, res = project!(sm.stab, -p) sm.stab = newstab - return sm, anticom_idx, res + n = nqubits(newstab) + newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>expect(p, sm))) + return newsm, anticom_idx, res end - -nqubits(sm::GeneralizedStabilizer) = nqubits(sm.stab) abstract type AbstractPauliChannel <: AbstractOperation end diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 9f3d1929f..445379f81 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -64,4 +64,3 @@ end end end end - From 9bb0e1ab6b27d341955726cc4835d7c0313fd96a Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Tue, 1 Oct 2024 18:41:14 +0500 Subject: [PATCH 05/17] improve code quality and add tests for project! and projectrand! --- src/nonclifford.jl | 23 +++++++------ test/test_nonclifford_quantumoptics.jl | 46 +++++++++++++++++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 594cb2b05..2f69dcbd8 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -206,11 +206,11 @@ with ϕᵢⱼ | Pᵢ | Pⱼ: """ function project!(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, anticom_idx, res = project!(sm.stab, p) + newstab, anticom, res = project!(sm.stab, p) χ′ = expect(p, sm) n = nqubits(newstab) newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) - return newsm, anticom_idx, res + return newsm, anticom, res end """ @@ -225,21 +225,24 @@ function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) end function _proj₊(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, anticom_idx, res = project!(sm.stab, p) - sm.stab = newstab + newstab, res = projectrand!(sm.stab, p) + χ′ = expect(p, sm) n = nqubits(newstab) - newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>expect(p, sm))) - return newsm, anticom_idx, res + newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) + return newsm, res end function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, anticom_idx, res = project!(sm.stab, -p) - sm.stab = newstab + newstab, res = projectrand!(sm.stab, -p) + χ′ = expect(p, sm) n = nqubits(newstab) - newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>expect(p, sm))) - return newsm, anticom_idx, res + newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) + return newsm, res end +Base.copy(sm::GeneralizedStabilizer) = GeneralizedStabilizer(copy(sm.stab),copy(sm.destabweights)) +Base.:(==)(sm₁::GeneralizedStabilizer, sm₂::GeneralizedStabilizer) = sm₁.stab==sm₂.stab && sm₁.destabweights==sm₂.destabweights + abstract type AbstractPauliChannel <: AbstractOperation end """A Pauli channel datastructure, mainly for use with [`GeneralizedStabilizer`](@ref) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 445379f81..c89092411 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -52,15 +52,45 @@ qo_tgate.data[2,2] = exp(im*pi/4) end @testset "project!" begin - for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] - for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] + for n in 1:4 + for repetition in 1:5 + s = random_stabilizer(n) + p = random_pauli(n) gs = GeneralizedStabilizer(s) - apply!(gs, pcT) - ρ = Operator(gs) - gs_proj, _, _ = project!(gs, p) - ρ_proj = Operator(gs_proj) - @test isapprox(Operator(gs_proj), ρ_proj; atol=1e-5) - @test isapprox(expect(p, gs_proj), expect(Operator(p), ρ_proj); atol=1e-5) + for i in 1:rand(1:3) + apply!(copy(gs), embed(n, i, pcT)) + end + qo_state = Operator(gs) + project!(copy(gs), copy(p))[1] + qo_state_after_proj = Operator(gs) + qo_pauli = Operator(gs) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 + end + end +end + +@testset "projectrand!" begin + for n in 1:4 + for repetition in 1:5 + s = random_stabilizer(n) + p = random_pauli(n) + gs = GeneralizedStabilizer(s) + for i in 1:rand(1:3) + apply!(copy(gs), embed(n, i, pcT)) + end + qo_state = Operator(gs) + projectrand!(copy(gs), copy(p))[1] + qo_state_after_proj = Operator(gs) + qo_pauli = Operator(gs) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 end end end From b1901cb88c920af320b3eeb4e9b3d34833a4256e Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 25 Oct 2024 12:38:25 +0500 Subject: [PATCH 06/17] remove projectrand! --- src/nonclifford.jl | 24 +----------- test/test_nonclifford_quantumoptics.jl | 53 +++++++------------------- 2 files changed, 14 insertions(+), 63 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 2f69dcbd8..20bf08937 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -213,31 +213,9 @@ function project!(sm::GeneralizedStabilizer, p::PauliOperator) return newsm, anticom, res end -""" -$TYPEDSIGNATURES - -Measure `pauli` operator on `state` and randomize the phase if necessary. -""" -function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) - eval = expect(p, sm) - prob = (real(eval)+1)/2 - return rand() < prob ? _proj₊(sm, p) : _proj₋(sm, p) +function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) end - function _proj₊(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, res = projectrand!(sm.stab, p) - χ′ = expect(p, sm) - n = nqubits(newstab) - newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) - return newsm, res -end - -function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, res = projectrand!(sm.stab, -p) - χ′ = expect(p, sm) - n = nqubits(newstab) - newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) - return newsm, res end Base.copy(sm::GeneralizedStabilizer) = GeneralizedStabilizer(copy(sm.stab),copy(sm.destabweights)) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index c89092411..562255d5f 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -52,45 +52,18 @@ qo_tgate.data[2,2] = exp(im*pi/4) end @testset "project!" begin - for n in 1:4 - for repetition in 1:5 - s = random_stabilizer(n) - p = random_pauli(n) - gs = GeneralizedStabilizer(s) - for i in 1:rand(1:3) - apply!(copy(gs), embed(n, i, pcT)) - end - qo_state = Operator(gs) - project!(copy(gs), copy(p))[1] - qo_state_after_proj = Operator(gs) - qo_pauli = Operator(gs) - qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 - qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 - result1 = qo_proj1*qo_state*qo_proj1' - result2 = qo_proj2*qo_state*qo_proj2' - @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 - end - end -end - -@testset "projectrand!" begin - for n in 1:4 - for repetition in 1:5 - s = random_stabilizer(n) - p = random_pauli(n) - gs = GeneralizedStabilizer(s) - for i in 1:rand(1:3) - apply!(copy(gs), embed(n, i, pcT)) - end - qo_state = Operator(gs) - projectrand!(copy(gs), copy(p))[1] - qo_state_after_proj = Operator(gs) - qo_pauli = Operator(gs) - qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 - qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 - result1 = qo_proj1*qo_state*qo_proj1' - result2 = qo_proj2*qo_state*qo_proj2' - @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 - end + checks = [(S"X",P"X"),(S"Y",P"Y"),(S"Z",P"Z"),(S"-X",P"-X"),(S"-Y",P"-Y"),(S"-Z",P"-Z")] + for (s, p) in checks + gs = GeneralizedStabilizer(s) + apply!(gs, p) + qo_state = Operator(gs) + project!(gs, p)[1] + qo_state_after_proj = Operator(gs) + qo_pauli = Operator(p) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 end end From d4a580023c6f7f1ac6942a7af3a6da1f464ce94e Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Mon, 28 Oct 2024 22:09:29 +0500 Subject: [PATCH 07/17] improve code quality of project! --- src/nonclifford.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 2ea8edc59..b6483e494 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -210,9 +210,9 @@ with ϕᵢⱼ | Pᵢ | Pⱼ: """ function project!(sm::GeneralizedStabilizer, p::PauliOperator) - newstab, anticom, res = project!(sm.stab, p) + n = nqubits(sm) χ′ = expect(p, sm) - n = nqubits(newstab) + newstab, anticom, res = project!(sm.stab, p) newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) return newsm, anticom, res end From ba879f8c518c83f68309878f5a1ce4fd6796542e Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sun, 3 Nov 2024 13:08:17 +0500 Subject: [PATCH 08/17] add test to show result1 == zero or result2 == zero for stabilizer states --- test/test_nonclifford_quantumoptics.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 562255d5f..871ea090c 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -51,8 +51,8 @@ qo_tgate.data[2,2] = exp(im*pi/4) end end -@testset "project!" begin - checks = [(S"X",P"X"),(S"Y",P"Y"),(S"Z",P"Z"),(S"-X",P"-X"),(S"-Y",P"-Y"),(S"-Z",P"-Z")] +@testset "Single-qubit projections of stabilizer states" begin + checks = [(random_stabilizer(1), random_pauli(1))] for (s, p) in checks gs = GeneralizedStabilizer(s) apply!(gs, p) @@ -64,6 +64,7 @@ end qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 result1 = qo_proj1*qo_state*qo_proj1' result2 = qo_proj2*qo_state*qo_proj2' + @test result1 == zero(qo_tgate) || result2 == zero(qo_tgate) # https://github.com/QuantumSavory/QuantumClifford.jl/pull/355#discussion_r1826568296 @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 end end From a6c5feda7e6853da4aff28b513ab11d94f336c74 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sun, 3 Nov 2024 23:29:16 +0500 Subject: [PATCH 09/17] implement projectrand! method and add tests for single-qubit base as starting point --- src/nonclifford.jl | 57 ++++---------------------- test/test_nonclifford_quantumoptics.jl | 2 +- 2 files changed, 9 insertions(+), 50 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index b6483e494..44d374a5d 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -172,60 +172,19 @@ function _allthreesumtozero(a,b,c) true end -""" -Projects the state of a [`GeneralizedStabilizer`](@ref) onto the eigenspaces of a -given Pauli operator `p`. -```jldoctest -julia> sm = GeneralizedStabilizer(S"-X") -A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is -𝒟ℯ𝓈𝓉𝒶𝒷 -+ Z -𝒮𝓉𝒶𝒷 -- X -with ϕᵢⱼ | Pᵢ | Pⱼ: - 1.0+0.0im | + _ | + _ - -julia> apply!(sm, pcT) -A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is -𝒟ℯ𝓈𝓉𝒶𝒷 -+ Z -𝒮𝓉𝒶𝒷 -- X -with ϕᵢⱼ | Pᵢ | Pⱼ: - 0.0+0.353553im | + _ | + Z - 0.0-0.353553im | + Z | + _ - 0.853553+0.0im | + _ | + _ - 0.146447+0.0im | + Z | + Z - -julia> project!(sm, P"-X")[1] -A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is -𝒟ℯ𝓈𝓉𝒶𝒷 -+ Z -𝒮𝓉𝒶𝒷 -- X -with ϕᵢⱼ | Pᵢ | Pⱼ: - 0.707107+0.0im | + _ | + _ -``` - -""" -function project!(sm::GeneralizedStabilizer, p::PauliOperator) - n = nqubits(sm) - χ′ = expect(p, sm) - newstab, anticom, res = project!(sm.stab, p) - newsm = GeneralizedStabilizer(newstab, DefaultDict(0.0im, (falses(n),falses(n))=>χ′)) - return newsm, anticom, res +function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) + prob = (real(expect(p, sm))+1)/2 + return _proj(sm, rand() < prob ? p : -p) end -function _proj₋(sm::GeneralizedStabilizer, p::PauliOperator) -end -function _proj₊(sm::GeneralizedStabilizer, p::PauliOperator) +function _proj(sm::GeneralizedStabilizer, p::PauliOperator) + n = nqubits(sm) + state, res = projectrand!(sm.stab, p) + newsm = GeneralizedStabilizer(state, DefaultDict(0.0im, (falses(n), falses(n)) => expect(p, sm))) + return newsm, res end -Base.copy(sm::GeneralizedStabilizer) = GeneralizedStabilizer(copy(sm.stab),copy(sm.destabweights)) - -Base.:(==)(sm₁::GeneralizedStabilizer, sm₂::GeneralizedStabilizer) = sm₁.stab==sm₂.stab && sm₁.destabweights==sm₂.destabweights - nqubits(sm::GeneralizedStabilizer) = nqubits(sm.stab) abstract type AbstractPauliChannel <: AbstractOperation end diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 871ea090c..04da15204 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -57,7 +57,7 @@ end gs = GeneralizedStabilizer(s) apply!(gs, p) qo_state = Operator(gs) - project!(gs, p)[1] + projectrand!(gs, p)[1] qo_state_after_proj = Operator(gs) qo_pauli = Operator(p) qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 From 6966d11527550a0099fff5c56b930922825b384d Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 8 Nov 2024 12:23:26 +0500 Subject: [PATCH 10/17] Multi-qubit projections using GeneralizedStabilizer for stabilizer states --- src/nonclifford.jl | 16 +++++-------- test/test_jet.jl | 2 +- test/test_nonclifford_quantumoptics.jl | 33 +++++++++++++------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index f6b3364c9..b79e37c13 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -232,16 +232,12 @@ function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) return _proj(sm, rand() < prob₁ ? p : -p) end -# Returns the updated `GeneralizedStabilizer` state sm' = (χ', B(S', D')), where (S', D') -# is derived from (S, D) through the traditional stabilizer update, and χ' represents the -# updated density matrix. function _proj(sm::GeneralizedStabilizer, p::PauliOperator) - state', res = projectrand!(sm.stab, p) + # Returns the updated `GeneralizedStabilizer` state sm' = (χ', B(S', D')), + # where (S', D') is derived from (S, D) through the traditional stabilizer update. + updated_state, res = projectrand!(sm.stab, p) # sm'.stab' is derived from sm.stab through the traditional stabilizer update. - sm.stab = state' # in-place - # χ' = expect(p, sm) represents the updated density matrix. - # Note: The density of χ does not increase, as χ′ after measurement is never more sparse than χ before measurement; thus, Λ(χ′) ≤ Λ(χ). - sm.destabweights[([0], [0])] = expect(p, sm) # in-place + sm.stab = updated_state # in-place return sm, res end @@ -434,8 +430,8 @@ of `χ`. It provides a measure of the state's complexity, with bounds `Λ(χ) ≤ 4ⁿ`. ```jldoctest heuristic -julia> using QuantumClifford: invsparsity # hide - +julia> using QuantumClifford: invsparsity; # hide + julia> sm = GeneralizedStabilizer(S"X") A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is 𝒟ℯ𝓈𝓉𝒶𝒷 diff --git a/test/test_jet.jl b/test/test_jet.jl index 61b5ce1c0..f2d1079a5 100644 --- a/test/test_jet.jl +++ b/test/test_jet.jl @@ -40,5 +40,5 @@ end ) @show rep @test_broken length(JET.get_reports(rep)) == 0 - @test length(JET.get_reports(rep)) <= 24 + @test length(JET.get_reports(rep)) <= 29 end diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index a778f0ecd..c2a35ba90 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -87,21 +87,22 @@ end end end -@testset "Single-qubit projections of stabilizer states" begin - checks = [(random_stabilizer(1), random_pauli(1))] - for (s, p) in checks - gs = GeneralizedStabilizer(s) - apply!(gs, p) - qo_state = Operator(gs) - projectrand!(gs, p)[1] - qo_state_after_proj = Operator(gs) - qo_pauli = Operator(p) - qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 - qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 - result1 = qo_proj1*qo_state*qo_proj1' - result2 = qo_proj2*qo_state*qo_proj2' - @test result1 == zero(qo_tgate) || result2 == zero(qo_tgate) # https://github.com/QuantumSavory/QuantumClifford.jl/pull/355#discussion_r1826568296 - @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 +@testset "Multi-qubit projections using GeneralizedStabilizer for stabilizer states" begin + for n in 1:10 + for repetition in 1:5 + s = random_stabilizer(n) + p = random_pauli(n) + gs = GeneralizedStabilizer(s) + apply!(gs, p) + qo_state = Operator(gs) + projectrand!(gs, p)[1] + qo_state_after_proj = Operator(gs) + qo_pauli = Operator(p) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 || qo_state_after_proj ≈ 2*result2 || qo_state_after_proj ≈ 2*result1 + end end end - From 1afdf8a5bfcdc5f8b8e8218f64fd8ea255d2a438 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 8 Nov 2024 23:44:39 +0500 Subject: [PATCH 11/17] use random_clifford in the multi-qubit projection test and normalize projected states --- test/test_nonclifford_quantumoptics.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index c2a35ba90..e46e90d26 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -93,7 +93,7 @@ end s = random_stabilizer(n) p = random_pauli(n) gs = GeneralizedStabilizer(s) - apply!(gs, p) + apply!(gs, random_clifford(n)) qo_state = Operator(gs) projectrand!(gs, p)[1] qo_state_after_proj = Operator(gs) @@ -102,7 +102,11 @@ end qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 result1 = qo_proj1*qo_state*qo_proj1' result2 = qo_proj2*qo_state*qo_proj2' - @test qo_state_after_proj ≈ result2 || qo_state_after_proj ≈ result1 || qo_state_after_proj ≈ 2*result2 || qo_state_after_proj ≈ 2*result1 + # Normalize to ensure consistent comparison of the projected state, independent of scaling factors + norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) + norm_result1 = result1/tr(result1) + norm_result2 = result2/tr(result2) + @test norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 end end end From 7ed70331e6ab7a152d4fac5ae14aa9c0c87e7221 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sat, 9 Nov 2024 11:56:07 +0500 Subject: [PATCH 12/17] show consistent comparison by using MixedDestabilizer, and GeneralizedStabilizer states for stabilizer simulation --- src/nonclifford.jl | 2 +- test/test_nonclifford_quantumoptics.jl | 36 ++++++++++++++------------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index b79e37c13..c44f54f25 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -430,7 +430,7 @@ of `χ`. It provides a measure of the state's complexity, with bounds `Λ(χ) ≤ 4ⁿ`. ```jldoctest heuristic -julia> using QuantumClifford: invsparsity; # hide +julia> using QuantumClifford: invsparsity; julia> sm = GeneralizedStabilizer(S"X") A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index e46e90d26..1ba529014 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -90,23 +90,25 @@ end @testset "Multi-qubit projections using GeneralizedStabilizer for stabilizer states" begin for n in 1:10 for repetition in 1:5 - s = random_stabilizer(n) - p = random_pauli(n) - gs = GeneralizedStabilizer(s) - apply!(gs, random_clifford(n)) - qo_state = Operator(gs) - projectrand!(gs, p)[1] - qo_state_after_proj = Operator(gs) - qo_pauli = Operator(p) - qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 - qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 - result1 = qo_proj1*qo_state*qo_proj1' - result2 = qo_proj2*qo_state*qo_proj2' - # Normalize to ensure consistent comparison of the projected state, independent of scaling factors - norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) - norm_result1 = result1/tr(result1) - norm_result2 = result2/tr(result2) - @test norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 + for state in [MixedDestabilizer, GeneralizedStabilizer] + s = random_stabilizer(n) + p = random_pauli(n) + gs = state(s) + apply!(gs, random_clifford(n)) + qo_state = Operator(gs) + projectrand!(gs, p)[1] + qo_state_after_proj = Operator(gs) + qo_pauli = Operator(p) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + # Normalize to ensure consistent comparison of the projected state, independent of scaling factors + norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) + norm_result1 = result1/tr(result1) + norm_result2 = result2/tr(result2) + @test norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 + end end end end From 6fe89f72a98b197c9fcb89bf893f5f9a81c6b2fa Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sat, 9 Nov 2024 15:17:26 +0500 Subject: [PATCH 13/17] improve code quality and formatting --- test/test_nonclifford_quantumoptics.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 1ba529014..5dddd705f 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -90,14 +90,14 @@ end @testset "Multi-qubit projections using GeneralizedStabilizer for stabilizer states" begin for n in 1:10 for repetition in 1:5 - for state in [MixedDestabilizer, GeneralizedStabilizer] + for state in [Stabilizer, MixedDestabilizer, GeneralizedStabilizer] s = random_stabilizer(n) p = random_pauli(n) - gs = state(s) - apply!(gs, random_clifford(n)) - qo_state = Operator(gs) - projectrand!(gs, p)[1] - qo_state_after_proj = Operator(gs) + τ = state(s) + apply!(τ, random_clifford(n)) + qo_state = Operator(τ) + projectrand!(τ, p)[1] + qo_state_after_proj = Operator(τ) qo_pauli = Operator(p) qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 From 4da6a8155184b951c6bf6403b702b5c1ea192485 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Mon, 11 Nov 2024 11:32:08 +0500 Subject: [PATCH 14/17] pardon - added remaining task: probabilistic non-stabilizer simulation as mentiond in earlier conversation comment --- test/Project.toml | 1 + test/test_nonclifford_quantumoptics.jl | 65 ++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index aad3cdc34..2f3508da0 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -25,5 +25,6 @@ SIMD = "fdea26ae-647d-5447-a871-4b548cad5224" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StridedViews = "4db3bf67-4bd7-4b4e-b153-31dc3fb37143" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 5dddd705f..f22eeef9c 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -6,6 +6,7 @@ using LinearAlgebra using Test using InteractiveUtils using Random +using Statistics qo_basis = SpinBasis(1//2) qo_tgate = sparse(identityoperator(qo_basis)) @@ -87,6 +88,18 @@ end end end +function multiqubit_projrand(τ,p) + qo_state = Operator(τ) + projectrand!(τ, p)[1] + qo_state_after_proj = Operator(τ) + qo_pauli = Operator(p) + qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 + qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 + result1 = qo_proj1*qo_state*qo_proj1' + result2 = qo_proj2*qo_state*qo_proj2' + return qo_state_after_proj, result1, result2 +end + @testset "Multi-qubit projections using GeneralizedStabilizer for stabilizer states" begin for n in 1:10 for repetition in 1:5 @@ -95,14 +108,7 @@ end p = random_pauli(n) τ = state(s) apply!(τ, random_clifford(n)) - qo_state = Operator(τ) - projectrand!(τ, p)[1] - qo_state_after_proj = Operator(τ) - qo_pauli = Operator(p) - qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 - qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 - result1 = qo_proj1*qo_state*qo_proj1' - result2 = qo_proj2*qo_state*qo_proj2' + qo_state_after_proj, result1, result2 = multiqubit_projrand(τ,p) # Normalize to ensure consistent comparison of the projected state, independent of scaling factors norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) norm_result1 = result1/tr(result1) @@ -112,3 +118,46 @@ end end end end + +function non_stabilizer_simulator(num_trials,n) + count = 0 + for n in 1:n # exponential cost in this term + for _ in 1:num_trials + s = random_stabilizer(n) + p = random_pauli(n) + gs = GeneralizedStabilizer(s) + i = rand(1:n) + nc = embed(n, i, pcT) # multi-qubit random non-Clifford gate + apply!(gs, nc) + qo_state_after_proj, result1, result2 = multiqubit_projrand(gs,p) + # Normalize to ensure consistent comparison of the projected state, independent of scaling factors + norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) + norm_result1 = result1/tr(result1) + norm_result2 = result2/tr(result2) + if norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 + count += 1 + end + end + end + prob = count/(num_trials*n) + return prob +end + +@testset "probabilistic non-stabilizer simulator" begin +# As described in https://www.scottaaronson.com/showcase2/report/ted-yoder.pdf, +# "The set of states that are stabilizer circuit efficient is related to the +# set of magic states, those states that, when combined with stabilizer circuits, +# allow universal quantum computation. The problems are essentially complementary; +# no magic state will be simulatable through every stabilizer circuit (assuming +# BQP is larger than BPP, that is). With qudits of odd prime dimension, it was +# recently found in https://arxiv.org/pdf/1201.1256 that a sufficient condition +# for a state to be efficiently (weakly) simulated through stabilizer circuits is +# that a certain quasi-probability representation of the state be positive." +# Therefore, non-Clifford simulators generally require probabilistic sampling +# techniques. + + num_qubits = 10 + num_trials = 10 + prob = non_stabilizer_simulator(num_trials, num_qubits) + @test prob > 0.5 +end From 875f0b6912686099fb1d9f2a3a44cd6b2dbeef70 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Tue, 12 Nov 2024 21:17:42 +0500 Subject: [PATCH 15/17] enhance comments for _proj --- src/nonclifford.jl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 4507474ec..14f088fbb 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -233,8 +233,19 @@ function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) end function _proj(sm::GeneralizedStabilizer, p::PauliOperator) - # Returns the updated `GeneralizedStabilizer` state sm' = (χ', B(S', D')), - # where (S', D') is derived from (S, D) through the traditional stabilizer update. + # As detailed in https://www.scottaaronson.com/showcase2/report/ted-yoder.pdf, "A generalized + # stabilizer (χ, B(S, D)) separates the “classical” part of the quantum state from the quantum + # state." In this framework, the quasi-classical tableau T = (S, D) is updated through Clifford + # gates and measurements, while the χ-matrix is updated solely by non-Clifford operations. + + # In this divided simulation approach: + # 1) When encountering a Clifford gate or measurement, we update the tableau as per usual. + # 2) For a non-Clifford gate or channel, the Kraus operator decompositions and operator-sum + # coefficients are stored for use with apply!(::GeneralizedStabilizer, ::AbstractPauliChannel). + + # This function returns an updated `GeneralizedStabilizer` state sm' = (χ', B(S', D')), where + # (S', D') is derived from (S, D) through traditional stabilizer update. The χ' matrix is + # updated whenever a non-Clifford gate is applied via apply!(sm, appropriately_padded_pcT). updated_state, res = projectrand!(sm.stab, p) # sm'.stab' is derived from sm.stab through the traditional stabilizer update. sm.stab = updated_state # in-place From 854e5c5faf301ff0034d5dd6b0959e9d95eb224f Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sat, 16 Nov 2024 13:24:00 +0500 Subject: [PATCH 16/17] apply code review suggestion --- src/nonclifford.jl | 22 +++++------------ test/test_nonclifford_quantumoptics.jl | 34 +++++++++++++++++++------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/nonclifford.jl b/src/nonclifford.jl index 14f088fbb..49ea2280c 100644 --- a/src/nonclifford.jl +++ b/src/nonclifford.jl @@ -233,22 +233,12 @@ function projectrand!(sm::GeneralizedStabilizer, p::PauliOperator) end function _proj(sm::GeneralizedStabilizer, p::PauliOperator) - # As detailed in https://www.scottaaronson.com/showcase2/report/ted-yoder.pdf, "A generalized - # stabilizer (χ, B(S, D)) separates the “classical” part of the quantum state from the quantum - # state." In this framework, the quasi-classical tableau T = (S, D) is updated through Clifford - # gates and measurements, while the χ-matrix is updated solely by non-Clifford operations. - - # In this divided simulation approach: - # 1) When encountering a Clifford gate or measurement, we update the tableau as per usual. - # 2) For a non-Clifford gate or channel, the Kraus operator decompositions and operator-sum - # coefficients are stored for use with apply!(::GeneralizedStabilizer, ::AbstractPauliChannel). - - # This function returns an updated `GeneralizedStabilizer` state sm' = (χ', B(S', D')), where - # (S', D') is derived from (S, D) through traditional stabilizer update. The χ' matrix is - # updated whenever a non-Clifford gate is applied via apply!(sm, appropriately_padded_pcT). - updated_state, res = projectrand!(sm.stab, p) - # sm'.stab' is derived from sm.stab through the traditional stabilizer update. - sm.stab = updated_state # in-place + # Returns the updated `GeneralizedStabilizer` state sm′ = (χ′, B(S′, D′)), + # where (S′, D′) is derived from (S, D) through the traditional stabilizer update, + # and χ′ is the updated density matrix after measurement. Note: Λ(χ′) ≤ Λ(χ). + n = nqubits(sm) + state, res = projectrand!(sm.stab, p) + sm = GeneralizedStabilizer(state, DefaultDict(0.0im, (falses(n),falses(n))=>expect(p, sm))) return sm, res end diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index f22eeef9c..9d051cb2b 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -1,5 +1,5 @@ using QuantumClifford -using QuantumClifford: GeneralizedStabilizer, rowdecompose, PauliChannel, mul_left!, mul_right! +using QuantumClifford: GeneralizedStabilizer, rowdecompose, PauliChannel, mul_left!, mul_right!, invsparsity using QuantumClifford: @S_str, random_stabilizer using QuantumOpticsBase using LinearAlgebra @@ -90,8 +90,7 @@ end function multiqubit_projrand(τ,p) qo_state = Operator(τ) - projectrand!(τ, p)[1] - qo_state_after_proj = Operator(τ) + qo_state_after_proj = Operator(projectrand!(τ, p)[1]) qo_pauli = Operator(p) qo_proj1 = (identityoperator(qo_pauli) - qo_pauli)/2 qo_proj2 = (identityoperator(qo_pauli) + qo_pauli)/2 @@ -100,6 +99,23 @@ function multiqubit_projrand(τ,p) return qo_state_after_proj, result1, result2 end +@testset "Single-qubit projections using for stabilizer states wrt to evolution of χ by unitary channel" begin + n = 1 + for s in [S"X", S"Y", S"Z", S"-X", S"-Y", S"-Z"] + for p in [P"X", P"Y", P"Z", P"-X", P"-Y", P"-Z"] + gs = GeneralizedStabilizer(s) + apply!(gs, pcT) + qo_state_after_proj, result1, result2 = multiqubit_projrand(gs,p) + # Normalize to ensure consistent comparison of the projected state, independent of scaling factors + norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) + norm_result1 = result1/tr(result1) + norm_result2 = result2/tr(result2) + @test projectrand!(gs, p)[1] |> invsparsity <= gs |> invsparsity # Note: Λ(χ′) ≤ Λ(χ). + @test norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 + end + end +end + @testset "Multi-qubit projections using GeneralizedStabilizer for stabilizer states" begin for n in 1:10 for repetition in 1:5 @@ -119,15 +135,16 @@ end end end +# exclusively multi-qubit function non_stabilizer_simulator(num_trials,n) count = 0 - for n in 1:n # exponential cost in this term + for n in 2:n # exponential cost in this term for _ in 1:num_trials s = random_stabilizer(n) p = random_pauli(n) gs = GeneralizedStabilizer(s) i = rand(1:n) - nc = embed(n, i, pcT) # multi-qubit random non-Clifford gate + nc = embed(n, i, pcT) apply!(gs, nc) qo_state_after_proj, result1, result2 = multiqubit_projrand(gs,p) # Normalize to ensure consistent comparison of the projected state, independent of scaling factors @@ -139,7 +156,7 @@ function non_stabilizer_simulator(num_trials,n) end end end - prob = count/(num_trials*n) + prob = count/(num_trials*(n-1)) return prob end @@ -156,8 +173,7 @@ end # Therefore, non-Clifford simulators generally require probabilistic sampling # techniques. - num_qubits = 10 - num_trials = 10 + num_qubits = 5 + num_trials = 5 prob = non_stabilizer_simulator(num_trials, num_qubits) - @test prob > 0.5 end From 1d50d065f76a072876de860307aaf614d33ac015 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sat, 16 Nov 2024 21:15:38 +0500 Subject: [PATCH 17/17] remove redundant code --- test/test_nonclifford_quantumoptics.jl | 44 -------------------------- 1 file changed, 44 deletions(-) diff --git a/test/test_nonclifford_quantumoptics.jl b/test/test_nonclifford_quantumoptics.jl index 9d051cb2b..7194f86c6 100644 --- a/test/test_nonclifford_quantumoptics.jl +++ b/test/test_nonclifford_quantumoptics.jl @@ -106,7 +106,6 @@ end gs = GeneralizedStabilizer(s) apply!(gs, pcT) qo_state_after_proj, result1, result2 = multiqubit_projrand(gs,p) - # Normalize to ensure consistent comparison of the projected state, independent of scaling factors norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) norm_result1 = result1/tr(result1) norm_result2 = result2/tr(result2) @@ -134,46 +133,3 @@ end end end end - -# exclusively multi-qubit -function non_stabilizer_simulator(num_trials,n) - count = 0 - for n in 2:n # exponential cost in this term - for _ in 1:num_trials - s = random_stabilizer(n) - p = random_pauli(n) - gs = GeneralizedStabilizer(s) - i = rand(1:n) - nc = embed(n, i, pcT) - apply!(gs, nc) - qo_state_after_proj, result1, result2 = multiqubit_projrand(gs,p) - # Normalize to ensure consistent comparison of the projected state, independent of scaling factors - norm_qo_state_after_proj = qo_state_after_proj/tr(qo_state_after_proj) - norm_result1 = result1/tr(result1) - norm_result2 = result2/tr(result2) - if norm_qo_state_after_proj ≈ norm_result2 || norm_qo_state_after_proj ≈ norm_result1 - count += 1 - end - end - end - prob = count/(num_trials*(n-1)) - return prob -end - -@testset "probabilistic non-stabilizer simulator" begin -# As described in https://www.scottaaronson.com/showcase2/report/ted-yoder.pdf, -# "The set of states that are stabilizer circuit efficient is related to the -# set of magic states, those states that, when combined with stabilizer circuits, -# allow universal quantum computation. The problems are essentially complementary; -# no magic state will be simulatable through every stabilizer circuit (assuming -# BQP is larger than BPP, that is). With qudits of odd prime dimension, it was -# recently found in https://arxiv.org/pdf/1201.1256 that a sufficient condition -# for a state to be efficiently (weakly) simulated through stabilizer circuits is -# that a certain quasi-probability representation of the state be positive." -# Therefore, non-Clifford simulators generally require probabilistic sampling -# techniques. - - num_qubits = 5 - num_trials = 5 - prob = non_stabilizer_simulator(num_trials, num_qubits) -end