diff --git a/.github/workflows/downgrade.yml b/.github/workflows/downgrade.yml index 9af107d..5b12d9d 100644 --- a/.github/workflows/downgrade.yml +++ b/.github/workflows/downgrade.yml @@ -21,7 +21,7 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} - - uses: cjdoris/julia-downgrade-compat-action@v1 + - uses: julia-actions/julia-downgrade-compat@v2 with: skip: Pkg,TOML,InteractiveUtils,Random,LinearAlgebra - uses: julia-actions/cache@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d3e8d..c536612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.4.12 - dev - Add Gabs extension for numerical translations of symbolic Gaussian states and operators. +- Add QuantumToolbox extension for state vector translations. ## v0.4.11 - 2025-06-22 diff --git a/Project.toml b/Project.toml index 8decb87..718f2cc 100644 --- a/Project.toml +++ b/Project.toml @@ -17,23 +17,26 @@ TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c" Gabs = "0eb812ee-a11f-4f5e-b8d4-bb8a44f06f50" QuantumClifford = "0525e862-1e90-11e9-3e4d-1b39d7109de1" QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" +QuantumToolbox = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab" [extensions] GabsExt = "Gabs" MixedCliffordOpticsExt = ["QuantumClifford", "QuantumOpticsBase"] QuantumCliffordExt = "QuantumClifford" QuantumOpticsExt = "QuantumOpticsBase" +QuantumToolboxExt = "QuantumToolbox" [compat] -Gabs = "1.3.1" +Gabs = "1.3.2" Latexify = "0.16" LinearAlgebra = "1.9" MacroTools = "0.5.13" PrecompileTools = "1.2" -QuantumClifford = "0.8.19, 0.9, 0.10" -QuantumInterface = "0.3.7, 0.4.1" -QuantumOpticsBase = "0.4.22, 0.5" -SymbolicUtils = "3.7" +QuantumClifford = "0.9.4, 0.10" +QuantumInterface = "0.4.1" +QuantumOpticsBase = "0.5.6" +QuantumToolbox = "0.34.0" +SymbolicUtils = "3.13" Symbolics = "6" TermInterface = "2" julia = "1.10" diff --git a/ext/QuantumToolboxExt/QuantumToolboxExt.jl b/ext/QuantumToolboxExt/QuantumToolboxExt.jl new file mode 100644 index 0000000..d2eca05 --- /dev/null +++ b/ext/QuantumToolboxExt/QuantumToolboxExt.jl @@ -0,0 +1,85 @@ +module QuantumToolboxExt + +using QuantumToolbox +using QuantumSymbolics +using QuantumSymbolics: + HGate, XGate, YGate, ZGate, CPHASEGate, CNOTGate, PauliP, PauliM, + XCXGate, XCYGate, XCZGate, YCXGate, YCYGate, YCZGate, ZCXGate, ZCYGate, ZCZGate, + XBasisState, YBasisState, ZBasisState, + NumberOp, CreateOp, DestroyOp, + FockState, + MixedState, IdentityOp, STensor, + qubit_basis +import QuantumSymbolics: express, express_nolookup +using TermInterface +using TermInterface: isexpr, head, operation, arguments, metadata + +const _l0 = QuantumToolbox.basis(2,0) +const _l1 = QuantumToolbox.basis(2,1) +const _s₊ = (_l0+_l1)/√2 +const _s₋ = (_l0-_l1)/√2 +const _i₊ = (_l0+im*_l1)/√2 +const _i₋ = (_l0-im*_l1)/√2 +const _σ₊ = QuantumToolbox.sigmap() +const _σ₋ = QuantumToolbox.sigmam() +const _l00 = QuantumToolbox.proj(_l0) +const _l11 = QuantumToolbox.proj(_l1) +const _id = QuantumToolbox.qeye(2) +const _z = QuantumToolbox.sigmaz() +const _x = QuantumToolbox.sigmax() +const _y = QuantumToolbox.sigmay() +const _hadamard = (_z+_x)/√2 +const _cnot = QuantumToolbox.tensor(_l00,_id) + QuantumToolbox.tensor(_l11,_x) +const _cphase = QuantumToolbox.tensor(_l00,_id) + QuantumToolbox.tensor(_l11,_z) +const _phase = _l00 + im*_l11 +const _iphase = _l00 - im*_l11 + +const _f0₂ = QuantumToolbox.basis(2, 0) +const _f1₂ = QuantumToolbox.basis(2, 1) +const _ad₂ = QuantumToolbox.create(2) +const _a₂ = QuantumToolbox.destroy(2) +const _n₂ = QuantumToolbox.num(2) + +express_nolookup(::HGate, ::QuantumToolboxRepr) = _hadamard +express_nolookup(::XGate, ::QuantumToolboxRepr) = _x +express_nolookup(::YGate, ::QuantumToolboxRepr) = _y +express_nolookup(::ZGate, ::QuantumToolboxRepr) = _z +express_nolookup(::CPHASEGate, ::QuantumToolboxRepr) = _cphase +express_nolookup(::CNOTGate, ::QuantumToolboxRepr) = _cnot + +const xyzopdict = Dict(:X=>_x, :Y=>_y, :Z=>_z) +const xyzstatedict = Dict(:X=>(_s₊,_s₋),:Y=>(_i₊,_i₋),:Z=>(_l0,_l1)) +for control in (:X, :Y, :Z) + for target in (:X, :Y, :Z) + k1, k2 = xyzstatedict[control] + o = xyzopdict[target] + gate = QuantumToolbox.tensor(proj(k1),_id) + QuantumToolbox.tensor(proj(k2),o) + structname = Symbol(control,"C",target,"Gate") + let gate=copy(gate) + @eval express_nolookup(::$(structname), ::QuantumToolboxRepr) = $gate + end + end +end + +express_nolookup(::PauliM, ::QuantumToolboxRepr) = _σ₋ +express_nolookup(::PauliP, ::QuantumToolboxRepr) = _σ₊ + +express_nolookup(s::XBasisState, ::QuantumToolboxRepr) = (_s₊,_s₋)[s.idx] +express_nolookup(s::YBasisState, ::QuantumToolboxRepr) = (_i₊,_i₋)[s.idx] +express_nolookup(s::ZBasisState, ::QuantumToolboxRepr) = (_l0,_l1)[s.idx] + +express_nolookup(s::FockState, r::QuantumToolboxRepr) = fock(r.cutoff, s.idx) +express_nolookup(s::CoherentState, r::QuantumToolboxRepr) = coherent(r.cutoff,s.alpha) +express_nolookup(s::SqueezedState, r::QuantumToolboxRepr) = (c = r.cutoff; squeeze(c, s.z)*fock(c, 0)) +express_nolookup(o::NumberOp, r::QuantumToolboxRepr) = num(r.cutoff) +express_nolookup(o::CreateOp, r::QuantumToolboxRepr) = create(r.cutoff) +express_nolookup(o::DestroyOp, r::QuantumToolboxRepr) = destroy(r.cutoff) +express_nolookup(o::DisplaceOp, r::QuantumToolboxRepr) = displace(r.cutoff, o.alpha) +express_nolookup(o::SqueezeOp, r::QuantumToolboxRepr) = squeeze(r.cutoff, o.z) +express_nolookup(x::MixedState, r::QuantumToolboxRepr) = (l = length(x.basis); return qeye(l)/l) +express_nolookup(x::IdentityOp, r::QuantumToolboxRepr) = qeye(length(x.basis)) + +express_nolookup(s::SOuterKetBra, r::QuantumToolboxRepr) = proj(express(s.ket, r), express(s.bra, r)) +express_nolookup(s::STensor, r::QuantumToolboxRepr) = QuantumToolbox.tensor((express(i, r) for i in s.terms)...) + +end diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 8b9da55..1d842f7 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -22,7 +22,7 @@ import QuantumInterface: export SymQObj,QObj, AbstractRepresentation,AbstractUse, - QuantumOpticsRepr,QuantumMCRepr,CliffordRepr,GabsRepr, + QuantumOpticsRepr,QuantumMCRepr,CliffordRepr,GabsRepr,QuantumToolboxRepr, UseAsState,UseAsObservable,UseAsOperation, apply!, express, @@ -52,6 +52,15 @@ export SymQObj,QObj, isunitary, KrausRepr,kraus + + + +# TODO: move this to QuantumInterface +"""Representation using kets, bras, density matrices, and superoperators governed by `QuantumToolbox.jl`.""" +Base.@kwdef struct QuantumToolboxRepr <: AbstractRepresentation + cutoff::Int = 2 +end + ## # Metadata cache helpers ## diff --git a/test/Project.toml b/test/Project.toml index e33cd01..83838e2 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -12,6 +12,7 @@ QuantumClifford = "0525e862-1e90-11e9-3e4d-1b39d7109de1" QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5" QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c" QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" +QuantumToolbox = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" diff --git a/test/test_conditional_cliffords.jl b/test/test_conditional_cliffords.jl index 4b19245..7daf531 100644 --- a/test/test_conditional_cliffords.jl +++ b/test/test_conditional_cliffords.jl @@ -1,15 +1,22 @@ @testitem "Conditional Cliffords" begin using QuantumClifford using QuantumOpticsBase + using QuantumToolbox using LinearAlgebra + P = [1. 0. 0. 0.; # Maps QuantumOptics to QuantumToolbox basis + 0. 0. 1. 0.; + 0. 1. 0. 0.; + 0. 0. 0. 1.] for control in (:X, :Y, :Z) for target in (:X, :Y, :Z) structname = Symbol(control,"C",target,"Gate") gate = eval(structname)() gate_qo = express(gate, QuantumOpticsRepr()) - gate_qc = Operator(CliffordOperator(express(gate, CliffordRepr(), UseAsOperation())(1,2),2)) + gate_qc = QuantumOpticsBase.Operator(CliffordOperator(express(gate, CliffordRepr(), UseAsOperation())(1,2),2)) + gate_qt = express(gate, QuantumToolboxRepr()) @test gate_qo ≈ gate_qc + @test P * gate_qo.data * P' ≈ gate_qt.data atol = 1e-10 end end end diff --git a/test/test_quantumtoolbox.jl b/test/test_quantumtoolbox.jl new file mode 100644 index 0000000..a9de735 --- /dev/null +++ b/test/test_quantumtoolbox.jl @@ -0,0 +1,49 @@ +@testitem "QuantumToolbox objects" begin + using QuantumSymbolics + using QuantumToolbox + + α = rand(ComplexF64) + n = rand(1:9) + + @testset "Fock states and operators" begin + repr = QuantumToolboxRepr(cutoff = 10) + @test express(vac, repr) ≈ fock(10, 0) + @test express(FockState(n), repr) ≈ fock(10, n) + @test express(CoherentState(α), repr) ≈ coherent(10, α) + @test express(SqueezedState(α), repr) ≈ squeeze(10, α) * fock(10, 0) + @test express(NumberOp(), repr) ≈ num(10) + @test express(CreateOp(), repr) ≈ create(10) + @test express(DestroyOp(), repr) ≈ destroy(10) + @test express(DisplaceOp(α), repr) ≈ displace(10, α) + end + + @testset "Quantum gates" begin + repr = QuantumToolboxRepr() + @test express(H, repr) ≈ to_sparse(Qobj([1.0+0.0im 1.; 1. -1.]/√2)) + @test express(X, repr) ≈ sigmax() + @test express(Y, repr) ≈ sigmay() + @test express(Z, repr) ≈ sigmaz() + @test express(CPHASE, repr) ≈ Qobj([1.0+0.0im 0. 0. 0.; 0. 1. 0. 0.; 0. 0. 1. 0.; 0. 0. 0. -1.], dims = (2,2)) + @test express(CNOT, repr) ≈ Qobj([1.0+0.0im 0. 0. 0.; 0. 1. 0. 0.; 0. 0. 0. 1.; 0. 0. 1. 0.], dims = (2,2)) + @test express(QuantumSymbolics.PauliM(), repr) ≈ sigmam() + @test express(QuantumSymbolics.PauliP(), repr) ≈ sigmap() + end + + @testset "Pure states" begin + repr = QuantumToolboxRepr() + @test express(XBasisState(1, SpinBasis(1//2)), repr) ≈ Qobj([1.0+0.0im, 1.0]/√2) + @test express(XBasisState(2, SpinBasis(1//2)), repr) ≈ Qobj([1.0+0.0im, -1.0]/√2) + @test express(YBasisState(1, SpinBasis(1//2)), repr) ≈ Qobj([1.0+0.0im, 1.0im]/√2) + @test express(YBasisState(2, SpinBasis(1//2)), repr) ≈ Qobj([1.0+0.0im, -1.0im]/√2) + @test express(ZBasisState(1, SpinBasis(1//2)), repr) ≈ Qobj([1.0+0.0im, 0.0]) + @test express(ZBasisState(2, SpinBasis(1//2)), repr) ≈ Qobj([0.0+0.0im, 1.0]) + end + + @testset "Linear algebra" begin + repr = QuantumToolboxRepr() + @test express(QuantumSymbolics.tensor(X, Y) + QuantumSymbolics.tensor(Z, X), repr) ≈ QuantumToolbox.tensor(express(X, repr), express(Y, repr)) + QuantumToolbox.tensor(express(Z, repr), express(X, repr)) + @test express(CreateOp() * (FockState(1) + CoherentState(α)), repr) ≈ express(CreateOp(), repr) * (express(FockState(1), repr) + express(CoherentState(α), repr)) + @test express(IdentityOp(QuantumSymbolics.tensor(X, Y)), repr) ≈ qeye(4) + @test express(MixedState(QuantumSymbolics.tensor(X, Y)), repr) ≈ qeye(4) / 4 + end +end \ No newline at end of file