diff --git a/CHANGELOG.md b/CHANGELOG.md index cbfd78447..92dfa53e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,11 @@ # News -## v0.8.13 - dev +## v0.8.13 +- **(fix)** There was a bug with incorrect scaling for `Operator(::CliffordOperator)` conversions. - A few more features to the `ECC` module's circuit generation routines. +- Quantikz circuit plotting improvements to `CliffordOperator` and `s*CY` and `sYC*`. ## v0.8.12 - 2023-07-12 diff --git a/Project.toml b/Project.toml index df3b0354c..bbaf2d7af 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumClifford" uuid = "0525e862-1e90-11e9-3e4d-1b39d7109de1" authors = ["Stefan Krastanov "] -version = "0.8.12" +version = "0.8.13" [deps] Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" @@ -44,7 +44,7 @@ Makie = "0.19" Nemo = "0.34, 0.35" Plots = "1.38.0" PrecompileTools = "1" -Quantikz = "1.2" +Quantikz = "1.3" QuantumInterface = "0.3.0" QuantumOpticsBase = "0.4" SIMD = "3.4.0" diff --git a/ext/QuantumCliffordQOpticsExt/QuantumCliffordQOpticsExt.jl b/ext/QuantumCliffordQOpticsExt/QuantumCliffordQOpticsExt.jl index 9b9ca40a4..b76094335 100644 --- a/ext/QuantumCliffordQOpticsExt/QuantumCliffordQOpticsExt.jl +++ b/ext/QuantumCliffordQOpticsExt/QuantumCliffordQOpticsExt.jl @@ -151,7 +151,7 @@ function cliff_to_unitary(cliff) b = bell(n, localorder=true) apply!(b, cliff, 1:n) ψ = Ket(b) - Operator(SpinBasis(1//2)^n,reshape(ψ.data, (2^n,2^n))) + Operator(SpinBasis(1//2)^n,reshape(ψ.data * sqrt(2)^n, (2^n,2^n))) end """ @@ -163,24 +163,24 @@ Convert a `QuantumClifford.CliffordOperator` to `QuantumOptics.Operator`. julia> Operator(tHadamard) Operator(dim=2x2) basis: Spin(1/2) - 0.5+0.0im 0.5+0.0im - 0.5+0.0im -0.5+0.0im + 0.707107+0.0im 0.707107+0.0im + 0.707107+0.0im -0.707107+0.0im julia> Operator(tId1⊗tHadamard) Operator(dim=4x4) basis: [Spin(1/2) ⊗ Spin(1/2)] - 0.353553+0.0im 0.0+0.0im 0.353553+0.0im 0.0+0.0im - 0.0+0.0im 0.353553+0.0im 0.0+0.0im 0.353553+0.0im - 0.353553+0.0im 0.0+0.0im -0.353553+0.0im 0.0+0.0im - 0.0+0.0im 0.353553+0.0im 0.0+0.0im -0.353553+0.0im + 0.707107+0.0im 0.0+0.0im 0.707107+0.0im 0.0+0.0im + 0.0+0.0im 0.707107+0.0im 0.0+0.0im 0.707107+0.0im + 0.707107+0.0im 0.0+0.0im -0.707107+0.0im 0.0+0.0im + 0.0+0.0im 0.707107+0.0im 0.0+0.0im -0.707107+0.0im julia> Operator(tCNOT) Operator(dim=4x4) basis: [Spin(1/2) ⊗ Spin(1/2)] - 0.5+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.5+0.0im - 0.0+0.0im 0.0+0.0im 0.5+0.0im 0.0+0.0im - 0.0+0.0im 0.5+0.0im 0.0+0.0im 0.0+0.0im + 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im + 0.0+0.0im 0.0+0.0im 1.0+0.0im 0.0+0.0im + 0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im ``` This conversion expects a dense tableau of type [`CliffordOperator`](@ref) as input. @@ -191,36 +191,36 @@ If you are working with some of the implicit (a.k.a. small/sparse/symbolic) type julia> Operator(CliffordOperator(sHadamard)) Operator(dim=2x2) basis: Spin(1/2) - 0.5+0.0im 0.5+0.0im - 0.5+0.0im -0.5+0.0im + 0.707107+0.0im 0.707107+0.0im + 0.707107+0.0im -0.707107+0.0im julia> Operator(CliffordOperator(sHadamard(1), 1)) Operator(dim=2x2) basis: Spin(1/2) - 0.5+0.0im 0.5+0.0im - 0.5+0.0im -0.5+0.0im + 0.707107+0.0im 0.707107+0.0im + 0.707107+0.0im -0.707107+0.0im julia> Operator(CliffordOperator(sHadamard(1), 2)) Operator(dim=4x4) basis: [Spin(1/2) ⊗ Spin(1/2)] - 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im - 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.353553+0.0im 0.353553+0.0im - 0.0+0.0im 0.0+0.0im 0.353553+0.0im -0.353553+0.0im + 0.707107+0.0im 0.707107+0.0im 0.0+0.0im 0.0+0.0im + 0.707107+0.0im -0.707107+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.707107+0.0im 0.707107+0.0im + 0.0+0.0im 0.0+0.0im 0.707107+0.0im -0.707107+0.0im julia> Operator(CliffordOperator(sHadamard(2), 2)) Operator(dim=4x4) basis: [Spin(1/2) ⊗ Spin(1/2)] - 0.353553+0.0im 0.0+0.0im 0.353553+0.0im 0.0+0.0im - 0.0+0.0im 0.353553+0.0im 0.0+0.0im 0.353553+0.0im - 0.353553+0.0im 0.0+0.0im -0.353553+0.0im 0.0+0.0im - 0.0+0.0im 0.353553+0.0im 0.0+0.0im -0.353553+0.0im + 0.707107+0.0im 0.0+0.0im 0.707107+0.0im 0.0+0.0im + 0.0+0.0im 0.707107+0.0im 0.0+0.0im 0.707107+0.0im + 0.707107+0.0im 0.0+0.0im -0.707107+0.0im 0.0+0.0im + 0.0+0.0im 0.707107+0.0im 0.0+0.0im -0.707107+0.0im julia> Operator(CliffordOperator(sHadamard(2), 1, compact=true)) Operator(dim=2x2) basis: Spin(1/2) - 0.5+0.0im 0.5+0.0im - 0.5+0.0im -0.5+0.0im + 0.707107+0.0im 0.707107+0.0im + 0.707107+0.0im -0.707107+0.0im ``` """ function Operator(c::CliffordOperator) diff --git a/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl b/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl index d65eb6e47..c5f88a871 100644 --- a/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl +++ b/ext/QuantumCliffordQuantikzExt/QuantumCliffordQuantikzExt.jl @@ -41,15 +41,12 @@ Quantikz.QuantikzOp(op::sCPHASE) = Quantikz.CPHASE(affectedqubits(op)...) Quantikz.QuantikzOp(op::sSWAP) = Quantikz.SWAP(affectedqubits(op)...) Quantikz.QuantikzOp(op::sZCZ) = Quantikz.CPHASE(affectedqubits(op)...) Quantikz.QuantikzOp(op::sXCX) = Quantikz.MultiControl([],[],collect(affectedqubits(op)),[]) # TODO make Quantikz work with tuples and remove the collect -Quantikz.QuantikzOp(op::sZCY) = Quantikz.MultiControlU("Y",affectedqubits(op)...) +Quantikz.QuantikzOp(op::sZCY) = Quantikz.MultiControlU("-iY",affectedqubits(op)...) Quantikz.QuantikzOp(op::sYCZ) = Quantikz.MultiControlU("Y",reverse(affectedqubits(op))...) +Quantikz.QuantikzOp(op::sXCY) = Quantikz.MultiControlU("-iY",[],[],[affectedqubits(op)[2]],[affectedqubits(op)[1]]) +Quantikz.QuantikzOp(op::sYCX) = Quantikz.MultiControlU("Y",[],[],[affectedqubits(op)[1]],[affectedqubits(op)[2]]) function Quantikz.QuantikzOp(op::AbstractTwoQubitOperator) - T = typeof(op) - if T in (sXCY,sYCX,sYCY,sYCZ,sZCY) - return Quantikz.U(string(T),collect(affectedqubits(op))) # TODO make Quantikz work with tuples and remove the collect - else - return Quantikz.U("{U}",collect(affectedqubits(op))) # TODO make Quantikz work with tuples and remove the collect - end + return Quantikz.U("{U}",collect(affectedqubits(op))) # TODO make Quantikz work with tuples and remove the collect end Quantikz.QuantikzOp(op::BellMeasurement) = Quantikz.ParityMeasurement(["\\mathtt{$(string(typeof(o))[3])}" for o in op.measurements], collect(affectedqubits(op))) # TODO make Quantikz work with tuples and remove the collect Quantikz.QuantikzOp(op::NoisyBellMeasurement) = Quantikz.QuantikzOp(op.meas) diff --git a/src/affectedqubits.jl b/src/affectedqubits.jl index 97a7dd23f..c5ff21299 100644 --- a/src/affectedqubits.jl +++ b/src/affectedqubits.jl @@ -11,6 +11,7 @@ affectedqubits(g::PauliMeasurement) = 1:length(g.pauli) 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) affectedbits(o) = () affectedbits(m::sMRZ) = (m.bit,) diff --git a/test/test_quantumoptics.jl b/test/test_quantumoptics.jl index 40b2a64f2..9637daba4 100644 --- a/test/test_quantumoptics.jl +++ b/test/test_quantumoptics.jl @@ -41,7 +41,8 @@ end ψ₁ = Ket(stab) ψ₂ = Ket(apply!(stab,cliff)) # test they are equal up to a phase - @test all(x->isnan(x)||abs(x)≈1/sqrt(2^n) , (U*ψ₁).data ./ ψ₂.data) + @test all(x->isnan(x)||abs(x)≈1 , (U*ψ₁).data ./ ψ₂.data) + @test abs(det(U.data))≈1 end end end diff --git a/test/test_symcontrolled.jl b/test/test_symcontrolled.jl index 908933bc0..15eabc1b0 100644 --- a/test/test_symcontrolled.jl +++ b/test/test_symcontrolled.jl @@ -1,5 +1,6 @@ using Test using QuantumClifford +using QuantumOpticsBase function transform_Zbasis(qubit) transformations = Dict(:X => [sHadamard(qubit),], :Y => [sInvPhase(qubit),sHadamard(qubit)], :Z => [sHadamard(qubit), sHadamard(qubit)]) @@ -96,3 +97,32 @@ end @test gate1dense == gate2dense end end + +@testset "Ket-based definition" begin + for control in (:X, :Y, :Z) + for target in (:X, :Y, :Z) + s = Stabilizer(QuantumClifford._T_str(string(control))) + k1 = Ket(s) + s.tab.phases[1] = 0x2 + k2 = Ket(s) + i = Operator(tId1) + o = Operator(CliffordOperator(eval(Symbol(:s,target,))(1),1)) + gate = projector(k1)⊗i + (target==:Y ? -im : 1) * projector(k2)⊗o + implemented_gate = Operator(CliffordOperator(eval(Symbol(:s,control,:C,target))(1,2),2)) + @test gate≈implemented_gate + + target, control = control, target + s = Stabilizer(QuantumClifford._T_str(string(control))) + k1 = Ket(s) + s.tab.phases[1] = 0x2 + k2 = Ket(s) + i = Operator(tId1) + o = Operator(CliffordOperator(eval(Symbol(:s,target,))(1),1)) + gate_perm = projector(k1)⊗i + (target==:Y ? -im : 1) * projector(k2)⊗o + implemented_gate_perm = Operator(CliffordOperator(eval(Symbol(:s,control,:C,target))(1,2),2)) + @test gate_perm≈implemented_gate_perm + + @test permutesystems(gate_perm,[2,1])≈gate + end + end +end