From 854e5c5faf301ff0034d5dd6b0959e9d95eb224f Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Sat, 16 Nov 2024 13:24:00 +0500 Subject: [PATCH] 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