Skip to content

Commit

Permalink
edited comments, names, and simplification
Browse files Browse the repository at this point in the history
  • Loading branch information
apkille committed Jun 17, 2024
1 parent 8484d53 commit d90092f
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 101 deletions.
30 changes: 17 additions & 13 deletions src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ import QuantumInterface:
AbstractKet, AbstractOperator, AbstractSuperOperator, AbstractBra

export SymQObj,QObj,
AbstractRepresentation, AbstractUse,
QuantumOpticsRepr, QuantumMCRepr, CliffordRepr,
UseAsState, UseAsObservable, UseAsOperation,
AbstractRepresentation,AbstractUse,
QuantumOpticsRepr,QuantumMCRepr,CliffordRepr,
UseAsState,UseAsObservable,UseAsOperation,
apply!,
express,
tensor,,
dagger,projector,commutator, anticommutator, expand,
dagger,projector,commutator,anticommutator,expand,
I,X,Y,Z,σˣ,σʸ,σᶻ,Pm,Pp,σ₋,σ₊,
H,CNOT,CPHASE,XCX,XCY,XCZ,YCX,YCY,YCZ,ZCX,ZCY,ZCZ,
X1,X2,Y1,Y2,Z1,Z2,X₁,X₂,Y₁,Y₂,Z₁,Z₂,L0,L1,Lp,Lm,Lpi,Lmi,L₀,L₁,L₊,L₋,L₊ᵢ,L₋ᵢ,
vac,F₀,F0,F₁,F1,
N,n̂,Create,âꜛ,Destroy,â, SpinBasis, FockBasis,
N,n̂,Create,âꜛ,Destroy,â,SpinBasis,FockBasis,
SBra,SKet,SOperator,
SAddBra,SAddKet,SAddOperator,
SScaledBra,SScaledOperator,SScaledKet,
SScaled,SScaledBra,SScaledOperator,SScaledKet,
STensorBra,STensorKet,STensorOperator,
SProjector,MixedState,IdentityOp,SInvOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator,
SApplyKet,SApplyBra,SMulOperator,SApplySuperop,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra,
HGate, XGate, YGate, ZGate, CPHASEGate, CNOTGate,
XBasisState, YBasisState, ZBasisState,
NumberOp, CreateOp, DestroyOp,
XCXGate, XCYGate, XCZGate, YCXGate, YCYGate, YCZGate, ZCXGate, ZCYGate, ZCZGate,
pauli_simplify, commutator_simplify, anticommutator_simplify,
SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra,
HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate,
XBasisState,YBasisState,ZBasisState,
NumberOp,CreateOp,DestroyOp,
XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate,
qsimplify,qsimplify_pauli,qsimplify_flatten,qsimplify_commutator,qsimplify_anticommutator,
isunitary

function countmap(samples) # A simpler version of StatsBase.countmap, because StatsBase is slow to import
Expand Down Expand Up @@ -143,7 +143,11 @@ function Base.isequal(x::X,y::Y) where {X<:Union{SymQObj, Symbolic{Complex}}, Y<
if isexpr(x)
if operation(x)==operation(y)
ax,ay = arguments(x),arguments(y)
(length(ax) == length(ay)) && all(zip(ax,ay)) do xy isequal(xy...) end
if operation(x) === :+
Set(ax) == Set(ay)
else
(length(ax) == length(ay)) && all(zip(ax,ay)) do xy isequal(xy...) end
end
else
false
end
Expand Down
39 changes: 7 additions & 32 deletions src/QSymbolicsBase/basic_ops_homogeneous.jl
Original file line number Diff line number Diff line change
@@ -1,4 +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 @@ -27,8 +29,8 @@ 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.:(*)(c, x::Symbolic{T}) where {T<:QObj} = c == 0 ? 0 : SScaled{T}(c,x)
Base.:(*)(x::Symbolic{T}, c) where {T<:QObj} = c == 0 ? 0 : SScaled{T}(c,x)
Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = SScaled{T}(1/c,x)
basis(x::SScaled) = basis(x.obj)

Expand Down Expand Up @@ -87,33 +89,6 @@ Base.show(io::IO, x::SAddOperator) = print(io, "("*join(map(string, arguments(x)
const SAddBra = SAdd{AbstractBra}
Base.show(io::IO, x::SAddBra) = print(io, "("*join(map(string, arguments(x)),"+")::String*")") # type assert to help inference

# defines commutativity for addition of quantum objects
const CommQObj = Union{SAddBra, SAddKet, SAddOperator}
function _in(x::SymQObj, sadd::CommQObj)
for i in arguments(sadd)
if isequal(x, i)
return true
end
end
false
end
function Base.isequal(x::CommQObj, y::CommQObj)
if typeof(x)==typeof(y)
if isexpr(x)
if operation(x)==operation(y)
ax,ay = arguments(x),arguments(y)
(length(ax) == length(ay)) && all(x -> _in(x, y), ax)
else
false
end
else
propsequal(x,y) # this is unholy
end
else
false
end
end

"""Symbolic application of operator on operator
```jldoctest
Expand Down Expand Up @@ -195,7 +170,7 @@ julia> commutator(A, A)
op1
op2
function SCommutator(o1, o2)
coeff, cleanterms = prefactorscalings([o1 o2])
coeff, cleanterms = prefactorscalings([o1 o2], scalar=true)
cleanterms[1] === cleanterms[2] ? 0 : coeff*new(cleanterms...)
end
end
Expand Down Expand Up @@ -223,7 +198,7 @@ julia> anticommutator(A, B)
op1
op2
function SAnticommutator(o1, o2)
coeff, cleanterms = prefactorscalings([o1 o2])
coeff, cleanterms = prefactorscalings([o1 o2], scalar=true)
coeff*new(cleanterms...)
end
end
Expand Down
26 changes: 14 additions & 12 deletions src/QSymbolicsBase/basic_ops_inhomogeneous.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""This file defines the symbolic operations for quantum objects (kets, operators, and bras) that are inhomogeneous in their arguments."""
##
# This file defines the symbolic operations for quantum objects (kets, operators, and bras) that are inhomogeneous in their arguments.
##

"""Symbolic application of an operator on a ket (from the left)
Expand Down Expand Up @@ -76,20 +78,20 @@ Base.:(*)(b::Symbolic{AbstractBra}, k::Symbolic{AbstractKet}) = SBraKet(b,k)
Base.show(io::IO, x::SBraKet) = begin print(io,x.bra); print(io,x.ket) end

"""Symbolic application of a superoperator on an operator"""
@withmetadata struct SApplySuperop <: Symbolic{AbstractOperator}
@withmetadata struct SSuperOpApply <: Symbolic{AbstractOperator}
sop
op
end
isexpr(::SApplySuperop) = true
iscall(::SApplySuperop) = true
arguments(x::SApplySuperop) = [x.sop,x.op]
operation(x::SApplySuperop) = *
head(x::SApplySuperop) = :*
children(x::SApplySuperop) = [:*,x.sop,x.op]
Base.:(*)(sop::Symbolic{AbstractSuperOperator}, op::Symbolic{AbstractOperator}) = SApplySuperop(sop,op)
Base.:(*)(sop::Symbolic{AbstractSuperOperator}, k::Symbolic{AbstractKet}) = SApplySuperop(sop,SProjector(k))
Base.show(io::IO, x::SApplySuperop) = begin print(io, x.sop); print(io, x.op) end
basis(x::SApplySuperop) = basis(x.op)
isexpr(::SSuperOpApply) = true
iscall(::SSuperOpApply) = true
arguments(x::SSuperOpApply) = [x.sop,x.op]
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}, k::Symbolic{AbstractKet}) = SSuperOpApply(sop,SProjector(k))
Base.show(io::IO, x::SSuperOpApply) = begin print(io, x.sop); print(io, x.op) end
basis(x::SSuperOpApply) = basis(x.op)

"""Symbolic outer product of a ket and a bra
```jldoctest
Expand Down
8 changes: 5 additions & 3 deletions src/QSymbolicsBase/express.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""This file defines the expression of quantum objects (kets, operators, and bras) in various representations.
The main function is `express`, which takes a quantum object and a representation and returns an expression of the object in that representation."""
##
# This file defines the expression of quantum objects (kets, operators, and bras) in various representations.
#
# The main function is `express`, which takes a quantum object and a representation and returns an expression of the object in that representation.
##

export express, express_nolookup, consistent_representation

Expand Down
4 changes: 0 additions & 4 deletions src/QSymbolicsBase/predefined.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ basis(::AbstractSingleQubitGate) = qubit_basis
basis(::AbstractTwoQubitGate) = qubit_basisqubit_basis
Base.show(io::IO, x::AbstractSingleQubitOp) = print(io, "$(symbollabel(x))")
Base.show(io::IO, x::AbstractTwoQubitOp) = print(io, "$(symbollabel(x))")
Base.:(*)(xs::AbstractSingleQubitGate...) = pauli_simplify(SMulOperator(collect(xs)))
commutator(o1::AbstractSingleQubitGate, o2::AbstractSingleQubitGate) = commutator_simplify(SCommutator(o1, o2))
anticommutator(o1::AbstractSingleQubitGate, o2::AbstractSingleQubitGate) = anticommutator_simplify(SAnticommutator(o1, o2))


@withmetadata struct OperatorEmbedding <: Symbolic{AbstractOperator}
gate::Symbolic{AbstractOperator} # TODO parameterize
Expand Down
86 changes: 65 additions & 21 deletions src/QSymbolicsBase/rules.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
"""This file defines automatic simplification rules for specific operations of quantum objects"""
##
# This file defines automatic simplification rules for specific operations of quantum objects.
##

"""Predicate functions"""
##
# Predicate functions
##
function hasscalings(xs)
any(xs) do x
operation(x) == *
end
end
_isa(T) = x->isa(x,T)

"""Flattening terms"""
function prefactorscalings(xs)
##
# Determining factors for expressions containing quantum objects
##

function prefactorscalings(xs; scalar=false) # If the scalar keyword is true, then only scalar factors will be returned as coefficients
terms = []
coeff = 1::Any
for x in xs
if isexpr(x) && operation(x) == *
c,t = arguments(x)
coeff *= c
push!(terms,t)
if scalar == false
coeff *= c
push!(terms,t)
elseif scalar == true && c isa Number
coeff *= c
push!(terms, t)
else
push!(terms,(*)(arguments(x)...))
end
else
push!(terms,x)
end
Expand All @@ -42,15 +56,18 @@ function isnotflat_precheck(*)
end
end

FLATTEN_RULES = [
##
# Simplification rules
##

# Flattening expressions
RULES_FLATTEN = [
@rule(~x::isnotflat_precheck() => flatten_term(, ~x)),
@rule (~~xs::hasscalings) => prefactorscalings_rule(xs) # Used to perform (a*|k⟩) ⊗ (b*|l⟩) → (a*b) * (|k⟩⊗|l⟩)
@rule (~~xs::hasscalings) => prefactorscalings_rule(xs)
]

tensor_simplify = Fixpoint(Chain(FLATTEN_RULES))

"""Pauli identities"""
PAULI_RULES = [
# Pauli identities
RULES_PAULI = [
@rule(~o1::_isa(XGate)*~o2::_isa(XGate) => I),
@rule(~o1::_isa(YGate)*~o2::_isa(YGate) => I),
@rule(~o1::_isa(ZGate)*~o2::_isa(ZGate) => I),
Expand All @@ -65,10 +82,8 @@ PAULI_RULES = [
@rule(~o1::_isa(HGate)*~o2::_isa(ZGate)*~o3::_isa(HGate) => X)
]

pauli_simplify = Fixpoint(Chain(PAULI_RULES))

"""Commutator identities"""
COMMUTATOR_RULES = [
# Commutator identities
RULES_COMMUTATOR = [
@rule(commutator(~o1::_isa(XGate), ~o2::_isa(YGate)) => 2*im*Z),
@rule(commutator(~o1::_isa(YGate), ~o2::_isa(ZGate)) => 2*im*X),
@rule(commutator(~o1::_isa(ZGate), ~o2::_isa(XGate)) => 2*im*Y),
Expand All @@ -77,10 +92,8 @@ COMMUTATOR_RULES = [
@rule(commutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => -2*im*Y)
]

commutator_simplify = Fixpoint(Chain(COMMUTATOR_RULES))

"""Anticommutator identities"""
ANTICOMMUTATOR_RULES = [
# Anticommutator identities
RULES_ANTICOMMUTATOR = [
@rule(anticommutator(~o1::_isa(XGate), ~o2::_isa(XGate)) => 2*I),
@rule(anticommutator(~o1::_isa(YGate), ~o2::_isa(YGate)) => 2*I),
@rule(anticommutator(~o1::_isa(ZGate), ~o2::_isa(ZGate)) => 2*I),
Expand All @@ -92,4 +105,35 @@ ANTICOMMUTATOR_RULES = [
@rule(anticommutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => 0)
]

anticommutator_simplify = Fixpoint(Chain(ANTICOMMUTATOR_RULES))
RULES_ALL = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR]

##
# Rewriters
##
qsimplify_flatten = Chain(RULES_FLATTEN)
qsimplify_anticommutator = Chain(RULES_ANTICOMMUTATOR)
qsimplify_pauli = Chain(RULES_PAULI)
qsimplify_commutator = Chain(RULES_COMMUTATOR)

"""Manually simplify a symbolic expression of quantum objects.
If the keyword `rewriter` is not specified, then `qsimplify` will apply every defined rule to the expression.
For performance or single-purpose motivations, the user has the option to define a specific rewriter for `qsimplify` to apply to the expression.
```jldoctest
julia> qsimplify(anticommutator(σˣ, σˣ), rewriter=qsimplify_anticommutator)
2𝕀
```
"""
function qsimplify(s; rewriter=nothing)
if QuantumSymbolics.isexpr(s)
if rewriter == nothing
Fixpoint(Chain(RULES_ALL))(s)
else
Fixpoint(rewriter)(s)
end
else
error("Object $(s) of type $(typeof(s)) is not an expression.")
end
end

18 changes: 9 additions & 9 deletions test/test_anticommutator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ B = SOperator(:B, SpinBasis(1//2))
end

@testset "anticommutator Pauli tests" begin
@test isequal(anticommutator(X, X), 2*I)
@test isequal(anticommutator(Y, Y), 2*I)
@test isequal(anticommutator(Z, Z), 2*I)
@test isequal(anticommutator(X, Y), 0)
@test isequal(anticommutator(Y, X), 0)
@test isequal(anticommutator(Y, Z), 0)
@test isequal(anticommutator(Z, Y), 0)
@test isequal(anticommutator(Z, X), 0)
@test isequal(anticommutator(X, Z), 0)
@test isequal(qsimplify(anticommutator(X, X), rewriter=qsimplify_anticommutator), 2*I)
@test isequal(qsimplify(anticommutator(Y, Y), rewriter=qsimplify_anticommutator), 2*I)
@test isequal(qsimplify(anticommutator(Z, Z), rewriter=qsimplify_anticommutator), 2*I)
@test isequal(qsimplify(anticommutator(X, Y), rewriter=qsimplify_anticommutator), 0)
@test isequal(qsimplify(anticommutator(Y, X), rewriter=qsimplify_anticommutator), 0)
@test isequal(qsimplify(anticommutator(Y, Z), rewriter=qsimplify_anticommutator), 0)
@test isequal(qsimplify(anticommutator(Z, Y), rewriter=qsimplify_anticommutator), 0)
@test isequal(qsimplify(anticommutator(Z, X), rewriter=qsimplify_anticommutator), 0)
@test isequal(qsimplify(anticommutator(X, Z), rewriter=qsimplify_anticommutator), 0)
end
13 changes: 6 additions & 7 deletions test/test_commutator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ B = SOperator(:B, SpinBasis(1//2))
end

@testset "commutator Pauli tests" begin
@test commutator(X, X) == 0 && commutator(Y, Y) == 0 && commutator(Z, Z) == 0
@test isequal(commutator(X, Y), 2*im*Z)
@test isequal(commutator(Y, X), -2*im*Z)
@test isequal(commutator(Y, Z), 2*im*X)
@test isequal(commutator(Z, Y), -2*im*X)
@test isequal(commutator(Z, X), 2*im*Y)
@test isequal(commutator(X, Z), -2*im*Y)
@test isequal(qsimplify(commutator(X, Y), rewriter=qsimplify_commutator), 2*im*Z)
@test isequal(qsimplify(commutator(Y, X), rewriter=qsimplify_commutator), -2*im*Z)
@test isequal(qsimplify(commutator(Y, Z), rewriter=qsimplify_commutator), 2*im*X)
@test isequal(qsimplify(commutator(Z, Y), rewriter=qsimplify_commutator), -2*im*X)
@test isequal(qsimplify(commutator(Z, X), rewriter=qsimplify_commutator), 2*im*Y)
@test isequal(qsimplify(commutator(X, Z), rewriter=qsimplify_commutator), -2*im*Y)
end

0 comments on commit d90092f

Please sign in to comment.