From c6c3c5ef634b4472707404c1c6e615905dbcb051 Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 19 Jun 2024 16:21:00 -0400 Subject: [PATCH 01/10] add zero structs --- src/QSymbolicsBase/QSymbolicsBase.jl | 1 + src/QSymbolicsBase/basic_ops_homogeneous.jl | 47 +++++++++++++++---- src/QSymbolicsBase/basic_ops_inhomogeneous.jl | 14 ++++++ src/QSymbolicsBase/literal_objects.jl | 19 +++++++- src/QSymbolicsBase/predefined.jl | 2 + test/test_zero_obj.jl | 39 +++++++++++++++ 6 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 test/test_zero_obj.jl diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 5a4f490..50d5155 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -33,6 +33,7 @@ export SymQObj,QObj, SAdd,SAddBra,SAddKet,SAddOperator, SScaled,SScaledBra,SScaledOperator,SScaledKet, STensorBra,STensorKet,STensorOperator, + SZeroBra,SZeroKet,SZeroOperator, SProjector,MixedState,IdentityOp,SInvOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index 60fb88a..c3206b9 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -1,5 +1,6 @@ ## -# This file defines the symbolic operations for quantum objects (kets, operators, and bras) that are homogeneous in their arguments. +# This file defines the symbolic operations for quantum objects (kets, operators, and bras) +# that are homogeneous in their arguments. ## """Scaling of a quantum object (ket, operator, or bra) by a number @@ -29,9 +30,22 @@ arguments(x::SScaled) = [x.coeff,x.obj] operation(x::SScaled) = * head(x::SScaled) = :* children(x::SScaled) = [:*,x.coeff,x.obj] -Base.:(*)(c, x::Symbolic{T}) where {T<:QObj} = SScaled{T}(c,x) -Base.:(*)(x::Symbolic{T}, c) where {T<:QObj} = SScaled{T}(c,x) -Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = SScaled{T}(1/c,x) +function Base.:(*)(c, x::Symbolic{T}) where {T<:QObj} + if iszero(c) || isa(x,SymZeroObj) + first(filter(i->i<:Symbolic{T}, Base.uniontypes(SymZeroObj)))() + else + SScaled{T}(c, x) + end +end +function Base.:(*)(x::Symbolic{T}, c) where {T<:QObj} + if iszero(c) || isa(x,SymZeroObj) + first(filter(i->i<:Symbolic{T}, Base.uniontypes(SymZeroObj)))() + else + SScaled{T}(c, x) + end +end +Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = iszero(c) ? error("cannot divide by zero") : SScaled{T}(1/c,x) +Base.:(/)(x::SymZeroObj, c) = x basis(x::SScaled) = basis(x.obj) const SScaledKet = SScaled{AbstractKet} @@ -78,7 +92,10 @@ arguments(x::SAdd) = [SScaledKet(v,k) for (k,v) in pairs(x.dict)] operation(x::SAdd) = + head(x::SAdd) = :+ children(x::SAdd) = [:+,SScaledKet(v,k) for (k,v) in pairs(x.dict)] -Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N} = SAdd{T}(countmap_flatten(xs, SScaled{T})) +function Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N} + nonzero_terms = filter!(x->!isa(x,SymZeroObj),collect(xs)) + isempty(nonzero_terms) ? xs[1] : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T})) +end Base.:(+)(xs::Vararg{Symbolic{<:QObj},0}) = 0 # to avoid undefined type parameters issue in the above method basis(x::SAdd) = basis(first(x.dict).first) @@ -120,7 +137,10 @@ arguments(x::SMulOperator) = x.terms operation(x::SMulOperator) = * head(x::SMulOperator) = :* children(x::SMulOperator) = [:*;x.terms] -Base.:(*)(xs::Symbolic{AbstractOperator}...) = SMulOperator(collect(xs)) +function Base.:(*)(xs::Symbolic{AbstractOperator}...) + zero_ind = findfirst(x->isa(x,SZeroOperator), xs) + isnothing(zero_ind) ? SMulOperator(collect(xs)) : SZeroOperator() +end Base.show(io::IO, x::SMulOperator) = print(io, join(map(string, arguments(x)),"")) basis(x::SMulOperator) = basis(x.terms) @@ -151,7 +171,10 @@ arguments(x::STensor) = x.terms operation(x::STensor) = ⊗ head(x::STensor) = :⊗ children(x::STensor) = pushfirst!(x.terms,:⊗) -⊗(xs::Symbolic{T}...) where {T<:QObj} = STensor{T}(collect(xs)) +function ⊗(xs::Symbolic{T}...) where {T<:QObj} + zero_ind = findfirst(x->isa(x,SymZeroObj), xs) + isnothing(zero_ind) ? STensor{T}(collect(xs)) : xs[zero_ind] +end basis(x::STensor) = tensor(basis.(x.terms)...) const STensorKet = STensor{AbstractKet} @@ -172,7 +195,7 @@ julia> commutator(A, B) [A,B] julia> commutator(A, A) -0 +𝟎 ``` """ @withmetadata struct SCommutator <: Symbolic{AbstractOperator} @@ -180,7 +203,7 @@ julia> commutator(A, A) op2 function SCommutator(o1, o2) coeff, cleanterms = prefactorscalings([o1 o2], scalar=true) - cleanterms[1] === cleanterms[2] ? 0 : coeff*new(cleanterms...) + cleanterms[1] === cleanterms[2] ? SZeroOperator() : coeff*new(cleanterms...) end end isexpr(::SCommutator) = true @@ -190,6 +213,9 @@ operation(x::SCommutator) = commutator head(x::SCommutator) = :commutator children(x::SCommutator) = [:commutator, x.op1, x.op2] commutator(o1::Symbolic{AbstractOperator}, o2::Symbolic{AbstractOperator}) = SCommutator(o1, o2) +commutator(o1::SZeroOperator, o2::Symbolic{AbstractOperator}) = SZeroOperator() +commutator(o1::Symbolic{AbstractOperator}, o2::SZeroOperator) = SZeroOperator() +commutator(o1::SZeroOperator, o2::SZeroOperator) = SZeroOperator() Base.show(io::IO, x::SCommutator) = print(io, "[$(x.op1),$(x.op2)]") basis(x::SCommutator) = basis(x.op1) expand(x::SCommutator) = x == 0 ? x : x.op1*x.op2 - x.op2*x.op1 @@ -218,6 +244,9 @@ operation(x::SAnticommutator) = anticommutator head(x::SAnticommutator) = :anticommutator children(x::SAnticommutator) = [:anticommutator, x.op1, x.op2] anticommutator(o1::Symbolic{AbstractOperator}, o2::Symbolic{AbstractOperator}) = SAnticommutator(o1, o2) +anticommutator(o1::SZeroOperator, o2::Symbolic{AbstractOperator}) = SZeroOperator() +anticommutator(o1::Symbolic{AbstractOperator}, o2::SZeroOperator) = SZeroOperator() +anticommutator(o1::SZeroOperator, o2::SZeroOperator) = SZeroOperator() Base.show(io::IO, x::SAnticommutator) = print(io, "{$(x.op1),$(x.op2)}") basis(x::SAnticommutator) = basis(x.op1) expand(x::SAnticommutator) = x == 0 ? x : x.op1*x.op2 + x.op2*x.op1 diff --git a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl index 25a3e04..50b0b15 100644 --- a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl @@ -26,6 +26,9 @@ operation(x::SApplyKet) = * head(x::SApplyKet) = :* children(x::SApplyKet) = [:*,x.op,x.ket] Base.:(*)(op::Symbolic{AbstractOperator}, k::Symbolic{AbstractKet}) = SApplyKet(op,k) +Base.:(*)(op::SZeroOperator, k::Symbolic{AbstractKet}) = SZeroKet() +Base.:(*)(op::Symbolic{AbstractOperator}, k::SZeroKet) = SZeroKet() +Base.:(*)(op::SZeroOperator, k::SZeroKet) = SZeroKet() Base.show(io::IO, x::SApplyKet) = begin print(io, x.op); print(io, x.ket) end basis(x::SApplyKet) = basis(x.ket) @@ -52,6 +55,9 @@ operation(x::SApplyBra) = * head(x::SApplyBra) = :* children(x::SApplyBra) = [:*,x.bra,x.op] Base.:(*)(b::Symbolic{AbstractBra}, op::Symbolic{AbstractOperator}) = SApplyBra(b,op) +Base.:(*)(b::SZeroBra, op::Symbolic{AbstractOperator}) = SZeroBra() +Base.:(*)(b::Symbolic{AbstractBra}, op::SZeroOperator) = SZeroBra() +Base.:(*)(b::SZeroBra, op::SZeroOperator) = SZeroBra() Base.show(io::IO, x::SApplyBra) = begin print(io, x.bra); print(io, x.op) end basis(x::SApplyBra) = basis(x.bra) @@ -75,6 +81,9 @@ operation(x::SBraKet) = * head(x::SBraKet) = :* children(x::SBraKet) = [:*,x.bra,x.ket] Base.:(*)(b::Symbolic{AbstractBra}, k::Symbolic{AbstractKet}) = SBraKet(b,k) +Base.:(*)(b::SZeroBra, k::Symbolic{AbstractKet}) = 0 +Base.:(*)(b::Symbolic{AbstractBra}, k::SZeroKet) = 0 +Base.:(*)(b::SZeroBra, k::SZeroKet) = 0 Base.show(io::IO, x::SBraKet) = begin print(io,x.bra); print(io,x.ket) end """Symbolic application of a superoperator on an operator""" @@ -89,7 +98,9 @@ operation(x::SSuperOpApply) = * head(x::SSuperOpApply) = :* children(x::SSuperOpApply) = [:*,x.sop,x.op] Base.:(*)(sop::Symbolic{AbstractSuperOperator}, op::Symbolic{AbstractOperator}) = SSuperOpApply(sop,op) +Base.:(*)(sop::Symbolic{AbstractSuperOperator}, op::SZeroOperator) = SZeroOperator() Base.:(*)(sop::Symbolic{AbstractSuperOperator}, k::Symbolic{AbstractKet}) = SSuperOpApply(sop,SProjector(k)) +Base.:(*)(sop::Symbolic{AbstractSuperOperator}, k::SZeroKet) = SZeroKet() Base.show(io::IO, x::SSuperOpApply) = begin print(io, x.sop); print(io, x.op) end basis(x::SSuperOpApply) = basis(x.op) @@ -115,5 +126,8 @@ operation(x::SOuterKetBra) = * head(x::SOuterKetBra) = :* children(x::SOuterKetBra) = [:*,x.ket,x.bra] Base.:(*)(k::Symbolic{AbstractKet}, b::Symbolic{AbstractBra}) = SOuterKetBra(k,b) +Base.:(*)(k::SZeroKet, b::Symbolic{AbstractBra}) = SZeroOperator() +Base.:(*)(k::Symbolic{AbstractKet}, b::SZeroBra) = SZeroOperator() +Base.:(*)(k::SZeroKet, b::SZeroBra) = SZeroOperator() Base.show(io::IO, x::SOuterKetBra) = begin print(io, x.ket); print(io, x.bra) end basis(x::SOuterKetBra) = basis(x.ket) diff --git a/src/QSymbolicsBase/literal_objects.jl b/src/QSymbolicsBase/literal_objects.jl index ad67c49..4320119 100644 --- a/src/QSymbolicsBase/literal_objects.jl +++ b/src/QSymbolicsBase/literal_objects.jl @@ -40,7 +40,7 @@ end ishermitian(::SHermitianUnitaryOperator) = true isunitary(::SHermitianUnitaryOperator) = true -const SymQ = Union{SKet, SBra, SOperator, SHermitianOperator, SUnitaryOperator, SHermitianUnitaryOperator} +const SymQ = Union{SKet,SBra,SOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator} isexpr(::SymQ) = false metadata(::SymQ) = nothing symbollabel(x::SymQ) = x.name @@ -48,5 +48,20 @@ basis(x::SymQ) = x.basis Base.show(io::IO, x::SKet) = print(io, "|$(symbollabel(x))⟩") Base.show(io::IO, x::SBra) = print(io, "⟨$(symbollabel(x))|") -Base.show(io::IO, x::Union{SOperator, SHermitianOperator, SUnitaryOperator, SHermitianUnitaryOperator}) = print(io, "$(symbollabel(x))") +Base.show(io::IO, x::Union{SOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator}) = print(io, "$(symbollabel(x))") Base.show(io::IO, x::SymQObj) = print(io, symbollabel(x)) # fallback that probably is not great + +struct SZeroKet <: Symbolic{AbstractKet} end + +struct SZeroBra <: Symbolic{AbstractBra} end + +struct SZeroOperator <: Symbolic{AbstractOperator} end + +const SymZeroObj = Union{SZeroKet,SZeroBra,SZeroOperator} + +isexpr(::SymZeroObj) = false +metadata(::SymZeroObj) = nothing +symbollabel(x::SymZeroObj) = "𝟎" +basis(x::SymZeroObj) = nothing + +Base.show(io::IO, x::SymZeroObj) = print(io, symbollabel(x)) diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index fc011d6..32a8c4f 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -222,6 +222,7 @@ operation(x::SProjector) = projector head(x::SProjector) = :projector children(x::SProjector) = [:projector,x.ket] projector(x::Symbolic{AbstractKet}) = SProjector(x) +projector(x::SZeroKet) = SZeroOperator() basis(x::SProjector) = basis(x.ket) function Base.show(io::IO, x::SProjector) print(io,"𝐏[") @@ -268,6 +269,7 @@ dagger(x::SAddKet) = SAddBra(Dict(dagger(k)=>v for (k,v) in pairs(x.dict))) dagger(x::SScaledBra) = SScaledKet(conj(x.coeff), dagger(x.obj)) dagger(x::SAddBra) = SAddKet(Dict(dagger(b)=>v for (b,v) in pairs(x.dict))) dagger(x::SAddOperator) = SAddOperator(Dict(dagger(o)=>v for (o,v) in pairs(x.dict))) +dagger(x::SZeroOperator) = x dagger(x::SHermitianOperator) = x dagger(x::SHermitianUnitaryOperator) = x dagger(x::SUnitaryOperator) = inv(x) diff --git a/test/test_zero_obj.jl b/test/test_zero_obj.jl new file mode 100644 index 0000000..2225297 --- /dev/null +++ b/test/test_zero_obj.jl @@ -0,0 +1,39 @@ +using QuantumSymbolics +using Test + +A = SOperator(:A, SpinBasis(1//2)) +k = SKet(:k, SpinBasis(1//2)) +b = SBra(:b, SpinBasis(1//2)) + +Oop = SZeroOperator() +Ok = SZeroKet() +Ob = SZeroBra() + +@testset "zero operator tests" begin + @test isequal(0*A, Oop) && isequal(A*0, Oop) + @test isequal(2*Oop, Oop) && isequal(Oop*2, Oop) && isequal(Oop/2, Oop) + @test isequal(Oop + A, A) && isequal(A + Oop, A) && isequal(Oop + Oop, Oop) + @test isequal(Oop*A, Oop) && isequal(A*Oop, Oop) + @test isequal(Oop ⊗ A, Oop) && isequal(A ⊗ Oop, Oop) && isequal(Oop*Oop, Oop) + @test isequal(commutator(A, Oop), Oop) && isequal(commutator(Oop, A), Oop) && isequal(commutator(Oop, Oop), Oop) + @test isequal(anticommutator(A, Oop), Oop) && isequal(anticommutator(Oop, A), Oop) && isequal(anticommutator(Oop, Oop), Oop) + @test isequal(projector(Ok), Oop) + @test isequal(dagger(Oop), Oop) +end + +@testset "zero bra and ket tests" begin + @test isequal(0*k, Ok) && isequal(k*0, Ok) + @test isequal(2*Ok, Ok) && isequal(Ok*2, Ok) && isequal(Ok/2, Ok) + @test isequal(Ok + k, k) && isequal(k + Ok, k) && isequal(Ok + Ok, Ok) + @test isequal(Ok ⊗ k, Ok) && isequal(k ⊗ Ok, Ok) + @test isequal(0*b, Ob) && isequal(b*0, Ob) + @test isequal(2*Ob, Ob) && isequal(Ob*2, Ob) && isequal(Ob/2, Ob) + @test isequal(Ob + b, b) && isequal(b + Ob, b) && isequal(Ob + Ob, Ob) + @test isequal(Ob ⊗ b, Ob) && isequal(b ⊗ Ob, Ob) + @test isequal(Oop*k, Ok) && isequal(A*Ok, Ok) && isequal(Oop*Ok, Ok) + @test isequal(b*Oop, Ob) && isequal(Ob*A, Ob) && isequal(Ob*Oop, Ob) + @test isequal(Ok*b, Oop) && isequal(k*Ob, Oop) && isequal(Ok*Ob, Oop) + @test isequal(Ob*k, 0) && isequal(b*Ok, 0) && isequal(Ob*Ok, 0) +end + + From 1a8599dca4646f3cd719a036fcd5517a885a058d Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 19 Jun 2024 22:42:53 -0400 Subject: [PATCH 02/10] set fixes --- src/QSymbolicsBase/QSymbolicsBase.jl | 14 +------------- src/QSymbolicsBase/basic_ops_homogeneous.jl | 10 ++++++---- src/QSymbolicsBase/predefined.jl | 4 +--- test/runtests.jl | 1 + 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 50d5155..e37a989 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -139,24 +139,12 @@ const SymQObj = Symbolic{<:QObj} # TODO Should we use Sym or Symbolic... Sym has Base.:(-)(x::SymQObj) = (-1)*x Base.:(-)(x::SymQObj,y::SymQObj) = x + (-y) -function _in(x::SymQObj, y::SymQObj) - for i in arguments(y) - if isequal(x, i) - return true - end - end - false -end function Base.isequal(x::X,y::Y) where {X<:Union{SymQObj, Symbolic{Complex}}, Y<:Union{SymQObj, Symbolic{Complex}}} if X==Y if isexpr(x) if operation(x)==operation(y) ax,ay = arguments(x),arguments(y) - if (operation(x) === +) && (length(ax) == length(ay)) - all(x -> _in(x, y), ax) - else - all(zip(ax,ay)) do xy isequal(xy...) end - end + (operation(x) === +) ? x._set_precomputed == y._set_precomputed : all(zip(ax,ay)) do xy isequal(xy...) end else false end diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index c3206b9..3f70578 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -84,17 +84,19 @@ julia> k₁ + k₂ """ @withmetadata struct SAdd{T<:QObj} <: Symbolic{T} dict - SAdd{S}(d) where S = length(d)==1 ? SScaled{S}(reverse(first(d))...) : new{S}(d) + _set_precomputed + _arguments_precomputed + SAdd{S}(d,s,a) where S = length(d)==1 ? SScaled{S}(reverse(first(d))...) : new{S}(d,s,a) end isexpr(::SAdd) = true iscall(::SAdd) = true -arguments(x::SAdd) = [SScaledKet(v,k) for (k,v) in pairs(x.dict)] +arguments(x::SAdd) = x._arguments_precomputed operation(x::SAdd) = + head(x::SAdd) = :+ -children(x::SAdd) = [:+,SScaledKet(v,k) for (k,v) in pairs(x.dict)] +children(x::SAdd) = [:+; x._arguments_precomputed] function Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N} nonzero_terms = filter!(x->!isa(x,SymZeroObj),collect(xs)) - isempty(nonzero_terms) ? xs[1] : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T})) + isempty(nonzero_terms) ? xs[1] : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T}), Set(collect(xs)), collect(xs)) end Base.:(+)(xs::Vararg{Symbolic{<:QObj},0}) = 0 # to avoid undefined type parameters issue in the above method basis(x::SAdd) = basis(first(x.dict).first) diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 32a8c4f..f03803d 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -265,10 +265,8 @@ dagger(x::Symbolic{AbstractBra}) = SDagger{AbstractKet}(x) dagger(x::Symbolic{AbstractKet}) = SDagger{AbstractBra}(x) dagger(x::Symbolic{AbstractOperator}) = SDagger{AbstractOperator}(x) dagger(x::SScaledKet) = SScaledBra(conj(x.coeff), dagger(x.obj)) -dagger(x::SAddKet) = SAddBra(Dict(dagger(k)=>v for (k,v) in pairs(x.dict))) +dagger(x::SAdd) = (+)([dagger(i) for i in arguments(x)]...) dagger(x::SScaledBra) = SScaledKet(conj(x.coeff), dagger(x.obj)) -dagger(x::SAddBra) = SAddKet(Dict(dagger(b)=>v for (b,v) in pairs(x.dict))) -dagger(x::SAddOperator) = SAddOperator(Dict(dagger(o)=>v for (o,v) in pairs(x.dict))) dagger(x::SZeroOperator) = x dagger(x::SHermitianOperator) = x dagger(x::SHermitianUnitaryOperator) = x diff --git a/test/runtests.jl b/test/runtests.jl index f34261c..bee2e0d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,6 +34,7 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA @doset "commutator" @doset "anticommutator" @doset "dagger" +@doset "zero_obj" VERSION >= v"1.9" && @doset "doctests" get(ENV,"JET_TEST","")=="true" && @doset "jet" From a76a5303991fc9aaf5f68cf65054bdd2032731c7 Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 02:23:39 -0400 Subject: [PATCH 03/10] hash method --- src/QSymbolicsBase/QSymbolicsBase.jl | 1 + test/test_commutator.jl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index e37a989..019ec06 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -138,6 +138,7 @@ const QObj = Union{AbstractBra,AbstractKet,AbstractOperator,AbstractSuperOperato const SymQObj = Symbolic{<:QObj} # TODO Should we use Sym or Symbolic... Sym has a lot of predefined goodies, including metadata support Base.:(-)(x::SymQObj) = (-1)*x Base.:(-)(x::SymQObj,y::SymQObj) = x + (-y) +Base.hash(x::SymQObj, h::UInt) = isexpr(x) ? hash(arguments(x), h) : hash([typeof(x),basis(x)], h) function Base.isequal(x::X,y::Y) where {X<:Union{SymQObj, Symbolic{Complex}}, Y<:Union{SymQObj, Symbolic{Complex}}} if X==Y diff --git a/test/test_commutator.jl b/test/test_commutator.jl index 3df80da..2cbbd71 100644 --- a/test/test_commutator.jl +++ b/test/test_commutator.jl @@ -6,7 +6,7 @@ B = SOperator(:B, SpinBasis(1//2)) @testset "symbolic commutator tests" begin @test isequal(commutator(2*A, B), commutator(A, 2*B)) && isequal(2*commutator(A, B), commutator(2*A, B)) && isequal(commutator(A, 2*B), 2*commutator(A, B)) - @test commutator(A, A) == 0 + @test commutator(A, A) == SZeroOperator() end @testset "commutator Pauli tests" begin From a17f364412bbdca1e6fe204319cda1ff9d94df0b Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 15:00:03 -0400 Subject: [PATCH 04/10] isequal and idiomatic changes --- src/QSymbolicsBase/QSymbolicsBase.jl | 8 +++-- src/QSymbolicsBase/basic_ops_homogeneous.jl | 36 ++++++++----------- src/QSymbolicsBase/basic_ops_inhomogeneous.jl | 1 + src/QSymbolicsBase/literal_objects.jl | 19 +++++----- src/extensions.jl | 3 +- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 019ec06..a712623 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -138,9 +138,10 @@ const QObj = Union{AbstractBra,AbstractKet,AbstractOperator,AbstractSuperOperato const SymQObj = Symbolic{<:QObj} # TODO Should we use Sym or Symbolic... Sym has a lot of predefined goodies, including metadata support Base.:(-)(x::SymQObj) = (-1)*x Base.:(-)(x::SymQObj,y::SymQObj) = x + (-y) -Base.hash(x::SymQObj, h::UInt) = isexpr(x) ? hash(arguments(x), h) : hash([typeof(x),basis(x)], h) +Base.hash(x::SymQObj, h::UInt) = isexpr(x) ? hash((head(x), arguments(x)), h) : +hash((typeof(x),symbollabel(x),basis(x)), h) -function Base.isequal(x::X,y::Y) where {X<:Union{SymQObj, Symbolic{Complex}}, Y<:Union{SymQObj, Symbolic{Complex}}} +function Base.isequal(x::X,y::Y) where {X<:SymQObj, Y<:SymQObj} if X==Y if isexpr(x) if operation(x)==operation(y) @@ -156,6 +157,9 @@ function Base.isequal(x::X,y::Y) where {X<:Union{SymQObj, Symbolic{Complex}}, Y< false end end +Base.isequal(::SymQObj, ::Symbolic{Complex}) = false +Base.isequal(::Symbolic{Complex}, ::SymQObj) = false + # TODO check that this does not cause incredibly bad runtime performance # use a macro to provide specializations if that is indeed the case diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index 3f70578..e049969 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -30,22 +30,9 @@ arguments(x::SScaled) = [x.coeff,x.obj] operation(x::SScaled) = * head(x::SScaled) = :* children(x::SScaled) = [:*,x.coeff,x.obj] -function Base.:(*)(c, x::Symbolic{T}) where {T<:QObj} - if iszero(c) || isa(x,SymZeroObj) - first(filter(i->i<:Symbolic{T}, Base.uniontypes(SymZeroObj)))() - else - SScaled{T}(c, x) - end -end -function Base.:(*)(x::Symbolic{T}, c) where {T<:QObj} - if iszero(c) || isa(x,SymZeroObj) - first(filter(i->i<:Symbolic{T}, Base.uniontypes(SymZeroObj)))() - else - SScaled{T}(c, x) - end -end -Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = iszero(c) ? error("cannot divide by zero") : SScaled{T}(1/c,x) -Base.:(/)(x::SymZeroObj, c) = x +Base.:(*)(c, x::Symbolic{T}) where {T<:QObj} = iszero(c) || iszero(x) ? SZero{T}() : SScaled{T}(c, x) +Base.:(*)(x::Symbolic{T}, c) where {T<:QObj} = c*x +Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = iszero(c) ? throw(DomainError(c,"cannot divide QSymbolics expressions by zero")) : (1/c)*x basis(x::SScaled) = basis(x.obj) const SScaledKet = SScaled{AbstractKet} @@ -86,7 +73,10 @@ julia> k₁ + k₂ dict _set_precomputed _arguments_precomputed - SAdd{S}(d,s,a) where S = length(d)==1 ? SScaled{S}(reverse(first(d))...) : new{S}(d,s,a) +end +function SAdd{S}(d) where S + xs = [c*obj for (c,obj) in d] + length(d)==1 ? SScaled{S}(reverse(first(d))...) : SAdd{S}(d,Set(xs),xs) end isexpr(::SAdd) = true iscall(::SAdd) = true @@ -95,8 +85,10 @@ operation(x::SAdd) = + head(x::SAdd) = :+ children(x::SAdd) = [:+; x._arguments_precomputed] function Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N} - nonzero_terms = filter!(x->!isa(x,SymZeroObj),collect(xs)) - isempty(nonzero_terms) ? xs[1] : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T}), Set(collect(xs)), collect(xs)) + xs = collect(xs) + f = first(xs) + nonzero_terms = filter!(x->!iszero(x),xs) + isempty(nonzero_terms) ? f : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T})) end Base.:(+)(xs::Vararg{Symbolic{<:QObj},0}) = 0 # to avoid undefined type parameters issue in the above method basis(x::SAdd) = basis(first(x.dict).first) @@ -140,7 +132,7 @@ operation(x::SMulOperator) = * head(x::SMulOperator) = :* children(x::SMulOperator) = [:*;x.terms] function Base.:(*)(xs::Symbolic{AbstractOperator}...) - zero_ind = findfirst(x->isa(x,SZeroOperator), xs) + zero_ind = findfirst(x->iszero(x), xs) isnothing(zero_ind) ? SMulOperator(collect(xs)) : SZeroOperator() end Base.show(io::IO, x::SMulOperator) = print(io, join(map(string, arguments(x)),"")) @@ -174,8 +166,8 @@ operation(x::STensor) = ⊗ head(x::STensor) = :⊗ children(x::STensor) = pushfirst!(x.terms,:⊗) function ⊗(xs::Symbolic{T}...) where {T<:QObj} - zero_ind = findfirst(x->isa(x,SymZeroObj), xs) - isnothing(zero_ind) ? STensor{T}(collect(xs)) : xs[zero_ind] + zero_ind = findfirst(x->iszero(x), xs) + isnothing(zero_ind) ? STensor{T}(collect(xs)) : SZero{T}() end basis(x::STensor) = tensor(basis.(x.terms)...) diff --git a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl index 50b0b15..3fbeb93 100644 --- a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl @@ -85,6 +85,7 @@ Base.:(*)(b::SZeroBra, k::Symbolic{AbstractKet}) = 0 Base.:(*)(b::Symbolic{AbstractBra}, k::SZeroKet) = 0 Base.:(*)(b::SZeroBra, k::SZeroKet) = 0 Base.show(io::IO, x::SBraKet) = begin print(io,x.bra); print(io,x.ket) end +Base.isequal(x::SBraKet, y::SBraKet) = isequal(x.bra, y.bra) && isequal(x.ket, y.ket) """Symbolic application of a superoperator on an operator""" @withmetadata struct SSuperOpApply <: Symbolic{AbstractOperator} diff --git a/src/QSymbolicsBase/literal_objects.jl b/src/QSymbolicsBase/literal_objects.jl index 4320119..e032f28 100644 --- a/src/QSymbolicsBase/literal_objects.jl +++ b/src/QSymbolicsBase/literal_objects.jl @@ -51,17 +51,18 @@ Base.show(io::IO, x::SBra) = print(io, "⟨$(symbollabel(x))|") Base.show(io::IO, x::Union{SOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator}) = print(io, "$(symbollabel(x))") Base.show(io::IO, x::SymQObj) = print(io, symbollabel(x)) # fallback that probably is not great -struct SZeroKet <: Symbolic{AbstractKet} end +struct SZero{T<:QObj} <: Symbolic{T} end -struct SZeroBra <: Symbolic{AbstractBra} end +const SZeroBra = SZero{AbstractBra} -struct SZeroOperator <: Symbolic{AbstractOperator} end +const SZeroKet = SZero{AbstractKet} -const SymZeroObj = Union{SZeroKet,SZeroBra,SZeroOperator} +const SZeroOperator = SZero{AbstractOperator} -isexpr(::SymZeroObj) = false -metadata(::SymZeroObj) = nothing -symbollabel(x::SymZeroObj) = "𝟎" -basis(x::SymZeroObj) = nothing +isexpr(::SZero) = false +metadata(::SZero) = nothing +symbollabel(x::SZero) = "𝟎" +basis(x::SZero) = nothing -Base.show(io::IO, x::SymZeroObj) = print(io, symbollabel(x)) +Base.show(io::IO, x::SZero) = print(io, symbollabel(x)) +Base.iszero(x::SymQObj) = isa(x, SZero) diff --git a/src/extensions.jl b/src/extensions.jl index 80aceea..cbc8654 100644 --- a/src/extensions.jl +++ b/src/extensions.jl @@ -23,6 +23,7 @@ Ket(dim=2) end isexpr(::StabilizerState) = false basis(x::StabilizerState) = SpinBasis(1//2)^nqubits(x.stabilizer) -Base.show(io::IO, x::StabilizerState) = print(io, "𝒮$(num_to_sub(nqubits(x.stabilizer)))") +symbollabel(x::StabilizerState) = "𝒮$(num_to_sub(nqubits(x.stabilizer)))" +Base.show(io::IO, x::StabilizerState) = print(io, symbollabel(x)) StabilizerState(s::T) where {T} = StabilizerState{T}(s) # TODO this is necessary because the @withmetadata macro is not very smart From b06cc264033a444da2d3ec771b43b17e96c00eb6 Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 15:05:37 -0400 Subject: [PATCH 05/10] generator comp for dagger --- src/QSymbolicsBase/predefined.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index f03803d..23a10b5 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -265,7 +265,7 @@ dagger(x::Symbolic{AbstractBra}) = SDagger{AbstractKet}(x) dagger(x::Symbolic{AbstractKet}) = SDagger{AbstractBra}(x) dagger(x::Symbolic{AbstractOperator}) = SDagger{AbstractOperator}(x) dagger(x::SScaledKet) = SScaledBra(conj(x.coeff), dagger(x.obj)) -dagger(x::SAdd) = (+)([dagger(i) for i in arguments(x)]...) +dagger(x::SAdd) = (+)(collect(dagger(i) for i in arguments(x))) dagger(x::SScaledBra) = SScaledKet(conj(x.coeff), dagger(x.obj)) dagger(x::SZeroOperator) = x dagger(x::SHermitianOperator) = x From 462fdf22ceee4b38bf3036d23947152e7113bdd6 Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 15:10:20 -0400 Subject: [PATCH 06/10] more generators and fix --- src/QSymbolicsBase/predefined.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 23a10b5..b1d89af 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -265,19 +265,19 @@ dagger(x::Symbolic{AbstractBra}) = SDagger{AbstractKet}(x) dagger(x::Symbolic{AbstractKet}) = SDagger{AbstractBra}(x) dagger(x::Symbolic{AbstractOperator}) = SDagger{AbstractOperator}(x) dagger(x::SScaledKet) = SScaledBra(conj(x.coeff), dagger(x.obj)) -dagger(x::SAdd) = (+)(collect(dagger(i) for i in arguments(x))) +dagger(x::SAdd) = (+)((dagger(i) for i in arguments(x))...) dagger(x::SScaledBra) = SScaledKet(conj(x.coeff), dagger(x.obj)) dagger(x::SZeroOperator) = x dagger(x::SHermitianOperator) = x dagger(x::SHermitianUnitaryOperator) = x dagger(x::SUnitaryOperator) = inv(x) -dagger(x::STensorBra) = STensorKet([dagger(i) for i in x.terms]) -dagger(x::STensorKet) = STensorBra([dagger(i) for i in x.terms]) -dagger(x::STensorOperator) = STensorOperator([dagger(i) for i in x.terms]) +dagger(x::STensorBra) = STensorKet(collect(dagger(i) for i in x.terms)) +dagger(x::STensorKet) = STensorBra(collect(dagger(i) for i in x.terms)) +dagger(x::STensorOperator) = STensorOperator(collect(dagger(i) for i in x.terms)) dagger(x::SScaledOperator) = SScaledOperator(conj(x.coeff), dagger(x.obj)) dagger(x::SApplyKet) = dagger(x.ket)*dagger(x.op) dagger(x::SApplyBra) = dagger(x.op)*dagger(x.bra) -dagger(x::SMulOperator) = SMulOperator([dagger(i) for i in reverse(x.terms)]) +dagger(x::SMulOperator) = SMulOperator(collect(dagger(i) for i in reverse(x.terms))) dagger(x::SBraKet) = SBraKet(dagger(x.ket), dagger(x.bra)) dagger(x::SOuterKetBra) = SOuterKetBra(dagger(x.bra), dagger(x.ket)) dagger(x::SDagger) = x.obj From 140201a4f535988cb9e779ac08df65d47d00e232 Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 15:16:12 -0400 Subject: [PATCH 07/10] organize SAdd --- src/QSymbolicsBase/basic_ops_homogeneous.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index e049969..ae71ef9 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -76,7 +76,7 @@ julia> k₁ + k₂ end function SAdd{S}(d) where S xs = [c*obj for (c,obj) in d] - length(d)==1 ? SScaled{S}(reverse(first(d))...) : SAdd{S}(d,Set(xs),xs) + length(d)==1 ? first(xs) : SAdd{S}(d,Set(xs),xs) end isexpr(::SAdd) = true iscall(::SAdd) = true From b6bbcac5bd23669b6dcc42c1bc31cca8fcc1322b Mon Sep 17 00:00:00 2001 From: apkille Date: Thu, 20 Jun 2024 21:41:00 -0400 Subject: [PATCH 08/10] doctest fix --- src/QSymbolicsBase/basic_ops_homogeneous.jl | 2 +- src/QSymbolicsBase/basic_ops_inhomogeneous.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index ae71ef9..db89a41 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -17,7 +17,7 @@ A julia> 2*A 2A -```` +``` """ @withmetadata struct SScaled{T<:QObj} <: Symbolic{T} coeff diff --git a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl index 3fbeb93..3dd6d63 100644 --- a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl @@ -39,6 +39,7 @@ julia> b = SBra(:b, SpinBasis(1//2)); A = SOperator(:A, SpinBasis(1//2)); julia> b*A ⟨b|A +``` """ @withmetadata struct SApplyBra <: Symbolic{AbstractBra} bra @@ -111,6 +112,7 @@ julia> b = SBra(:b, SpinBasis(1//2)); k = SKet(:k, SpinBasis(1//2)); julia> k*b |k⟩⟨b| +``` """ @withmetadata struct SOuterKetBra <: Symbolic{AbstractOperator} ket From 44860c556ee0234f2e01f9963ce3e4e41a0c7239 Mon Sep 17 00:00:00 2001 From: apkille Date: Fri, 21 Jun 2024 12:19:02 -0400 Subject: [PATCH 09/10] change JET threshold to 7 --- test/test_jet.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_jet.jl b/test/test_jet.jl index fd52658..60d19c5 100644 --- a/test/test_jet.jl +++ b/test/test_jet.jl @@ -29,4 +29,4 @@ rep = report_package("QuantumSymbolics"; ) @show rep @test_broken length(JET.get_reports(rep)) == 0 -@test length(JET.get_reports(rep)) <= 6 +@test length(JET.get_reports(rep)) <= 7 From 900fa6c531d88fa849b8bad15b03a8ac1849cd4b Mon Sep 17 00:00:00 2001 From: apkille Date: Fri, 21 Jun 2024 12:33:38 -0400 Subject: [PATCH 10/10] new version updates --- CHANGELOG.md | 7 +++++++ Project.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4b405a..38ea614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # News +## v0.3.1 - 2024-06-21 + +- Macros for defining symbolic quantum objects. +- Implement zero objects. +- Equality for commutative operations, hashing, and lexicographic ordering when printing. +- Added tests. + ## v0.3.0 - 2024-06-12 - Bump compat for symbolics-related foundational packages. diff --git a/Project.toml b/Project.toml index 4e03231..90372d4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumSymbolics" uuid = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" authors = ["QuantumSymbolics.jl contributors"] -version = "0.3.0" +version = "0.3.1" [deps] Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"