Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addresses #46 and fixes #47 #50

Merged
merged 13 commits into from
Jun 21, 2024
16 changes: 3 additions & 13 deletions src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -137,25 +138,14 @@ 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)
apkille marked this conversation as resolved.
Show resolved Hide resolved

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}}}
apkille marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
55 changes: 43 additions & 12 deletions src/QSymbolicsBase/basic_ops_homogeneous.jl
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,9 +30,22 @@
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)))()
apkille marked this conversation as resolved.
Show resolved Hide resolved
else
SScaled{T}(c, x)
end
end
function Base.:(*)(x::Symbolic{T}, c) where {T<:QObj}
apkille marked this conversation as resolved.
Show resolved Hide resolved
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)
apkille marked this conversation as resolved.
Show resolved Hide resolved
Base.:(/)(x::SymZeroObj, c) = x
basis(x::SScaled) = basis(x.obj)

const SScaledKet = SScaled{AbstractKet}
Expand Down Expand Up @@ -70,15 +84,20 @@
"""
@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)
apkille marked this conversation as resolved.
Show resolved Hide resolved
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)]
Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N} = SAdd{T}(countmap_flatten(xs, SScaled{T}))
children(x::SAdd) = [:+; x._arguments_precomputed]

Check warning on line 96 in src/QSymbolicsBase/basic_ops_homogeneous.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/basic_ops_homogeneous.jl#L96

Added line #L96 was not covered by tests
function Base.:(+)(xs::Vararg{Symbolic{T},N}) where {T<:QObj,N}
nonzero_terms = filter!(x->!isa(x,SymZeroObj),collect(xs))
apkille marked this conversation as resolved.
Show resolved Hide resolved
isempty(nonzero_terms) ? xs[1] : SAdd{T}(countmap_flatten(nonzero_terms, SScaled{T}), Set(collect(xs)), collect(xs))
end
apkille marked this conversation as resolved.
Show resolved Hide resolved
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)

Expand Down Expand Up @@ -120,7 +139,10 @@
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)

Expand Down Expand Up @@ -151,7 +173,10 @@
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}
Expand All @@ -172,15 +197,15 @@
[A,B]

julia> commutator(A, A)
0
𝟎
```
"""
@withmetadata struct SCommutator <: Symbolic{AbstractOperator}
op1
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
Expand All @@ -190,6 +215,9 @@
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
Expand Down Expand Up @@ -218,6 +246,9 @@
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
14 changes: 14 additions & 0 deletions src/QSymbolicsBase/basic_ops_inhomogeneous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
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)

Expand All @@ -52,6 +55,9 @@
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)

Expand All @@ -75,6 +81,9 @@
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"""
Expand All @@ -89,7 +98,9 @@
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()

Check warning on line 101 in src/QSymbolicsBase/basic_ops_inhomogeneous.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/basic_ops_inhomogeneous.jl#L101

Added line #L101 was not covered by tests
Base.:(*)(sop::Symbolic{AbstractSuperOperator}, k::Symbolic{AbstractKet}) = SSuperOpApply(sop,SProjector(k))
Base.:(*)(sop::Symbolic{AbstractSuperOperator}, k::SZeroKet) = SZeroKet()

Check warning on line 103 in src/QSymbolicsBase/basic_ops_inhomogeneous.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/basic_ops_inhomogeneous.jl#L103

Added line #L103 was not covered by tests
Base.show(io::IO, x::SSuperOpApply) = begin print(io, x.sop); print(io, x.op) end
basis(x::SSuperOpApply) = basis(x.op)

Expand All @@ -115,5 +126,8 @@
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)
19 changes: 17 additions & 2 deletions src/QSymbolicsBase/literal_objects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,28 @@
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
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
apkille marked this conversation as resolved.
Show resolved Hide resolved

const SymZeroObj = Union{SZeroKet,SZeroBra,SZeroOperator}

isexpr(::SymZeroObj) = false
metadata(::SymZeroObj) = nothing

Check warning on line 63 in src/QSymbolicsBase/literal_objects.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/literal_objects.jl#L63

Added line #L63 was not covered by tests
symbollabel(x::SymZeroObj) = "𝟎"
apkille marked this conversation as resolved.
Show resolved Hide resolved
basis(x::SymZeroObj) = nothing

Base.show(io::IO, x::SymZeroObj) = print(io, symbollabel(x))
6 changes: 3 additions & 3 deletions src/QSymbolicsBase/predefined.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,"𝐏[")
Expand Down Expand Up @@ -264,10 +265,9 @@ 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)]...)
apkille marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion test/test_commutator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions test/test_zero_obj.jl
Original file line number Diff line number Diff line change
@@ -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


Loading