Skip to content
Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# News

## v0.4.11 - dev

- Add new symbolic bosonic states and operators (`TwoSqueezeOp`, `TwoSqueezedState`, `BosonicThermalState`, `BeamSplitterOp`).

## v0.4.10 - 2025-05-11

- Polish `Base.show` methods for application products and scaled quantum objects.
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumSymbolics"
uuid = "efa7fd63-0460-4890-beb7-be1bbdfbaeae"
authors = ["QuantumSymbolics.jl contributors"]
version = "0.4.10"
version = "0.4.11-dev"

[deps]
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
Expand Down
3 changes: 2 additions & 1 deletion src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ export SymQObj,QObj,
MixedState,IdentityOp,
SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra,
HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate,
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,TwoSqueezedState,BosonicThermalState,
NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp,
TwoSqueezeOp,BeamSplitterOp,
XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate,
qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock,
qexpand,
Expand Down
75 changes: 44 additions & 31 deletions src/QSymbolicsBase/predefined_fock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,54 @@
# Predefined objects in the Fock space.
##

const inf_fock_basis = FockBasis(Inf,0)

abstract type AbstractSingleBosonState <: SpecialKet
abstract type AbstractTwoBosonState <: SpecialKet
basis(::AbstractSingleBosonState) = inf_fock_basis
basis(::AbstractTwoBosonState) = inf_fock_basis^2

"""Fock state in defined Fock basis."""
@withmetadata struct FockState <: SpecialKet
@withmetadata struct FockState <: AbstractSingleBosonState
idx::Int
basis::FockBasis
end
FockState(idx::Int) = FockState(idx, inf_fock_basis)
symbollabel(x::FockState) = "$(x.idx)"

"""Coherent state in defined Fock basis."""
@withmetadata struct CoherentState <: SpecialKet
@withmetadata struct CoherentState <: AbstractSingleBosonState
alpha::Number # TODO parameterize
basis::FockBasis
end
CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis)
symbollabel(x::CoherentState) = "$(x.alpha)"

"""Squeezed vacuum state in defined Fock basis."""
@withmetadata struct SqueezedState <: SpecialKet
@withmetadata struct SqueezedState <: AbstractSingleBosonState
z::Number
basis::FockBasis
end
SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis)
symbollabel(x::SqueezedState) = "0,$(x.z)"

const inf_fock_basis = FockBasis(Inf,0.)
"""Vacuum basis state of n"""
"""Two-mode squeezed vacuum state, or EPR state, in defined Fock basis."""
@withmetadata struct TwoSqueezedState <: AbstractTwoBosonState
z::Number
end
symbollabel(x::TwoSqueezedState) = "0,$(x.z)"

"""Vacuum basis state"""
const vac = const F₀ = const F0 = FockState(0)
"""Single photon basis state of n"""
"""Single photon basis state"""
const F₁ = const F1 = FockState(1)

##
# Gates and Operators on harmonic oscillators
##

abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end
abstract type AbstractTwoBosonOp <: Symbolic{AbstractOperator} end
abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice
abstract type AbstractTwoBosonGate <: AbstractTwoBosonOp end
isexpr(::AbstractSingleBosonGate) = false
basis(x::AbstractSingleBosonOp) = inf_fock_basis
isexpr(::AbstractTwoBosonGate) = false
basis(x::AbstractTwoBosonOp) = inf_fock_basis^2

"""Number operator.

Expand All @@ -54,10 +64,7 @@ julia> qsimplify(num*f, rewriter=qsimplify_fock)
2|2⟩
```
"""
@withmetadata struct NumberOp <: AbstractSingleBosonOp
basis::FockBasis
end
NumberOp() = NumberOp(inf_fock_basis)
@withmetadata struct NumberOp <: AbstractSingleBosonOp end
symbollabel(::NumberOp) = "n"

"""Creation (raising) operator.
Expand All @@ -73,10 +80,7 @@ julia> qsimplify(create*f, rewriter=qsimplify_fock)
(sqrt(3))|3⟩
```
"""
@withmetadata struct CreateOp <: AbstractSingleBosonOp
basis::FockBasis
end
CreateOp() = CreateOp(inf_fock_basis)
@withmetadata struct CreateOp <: AbstractSingleBosonOp end
symbollabel(::CreateOp) = "a†"

"""Annihilation (lowering or destroy) operator in defined Fock basis.
Expand All @@ -92,10 +96,7 @@ julia> qsimplify(destroy*f, rewriter=qsimplify_fock)
(sqrt(2))|1⟩
```
"""
@withmetadata struct DestroyOp <: AbstractSingleBosonOp
basis::FockBasis
end
DestroyOp() = DestroyOp(inf_fock_basis)
@withmetadata struct DestroyOp <: AbstractSingleBosonOp end
symbollabel(::DestroyOp) = "a"

"""Phase-shift operator in defined Fock basis.
Expand All @@ -113,9 +114,7 @@ julia> qsimplify(phase*c, rewriter=qsimplify_fock)
"""
@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp
phase::Number
basis::FockBasis
end
PhaseShiftOp(phase::Number) = PhaseShiftOp(phase, inf_fock_basis)
symbollabel(x::PhaseShiftOp) = "U($(x.phase))"

"""Displacement operator in defined Fock basis.
Expand All @@ -133,9 +132,7 @@ julia> qsimplify(displace*f, rewriter=qsimplify_fock)
"""
@withmetadata struct DisplaceOp <: AbstractSingleBosonOp
alpha::Number
basis::FockBasis
end
DisplaceOp(alpha::Number) = DisplaceOp(alpha, inf_fock_basis)
symbollabel(x::DisplaceOp) = "D($(x.alpha))"

"""Number operator, also available as the constant `n̂`, in an infinite dimension Fock basis."""
Expand All @@ -158,7 +155,23 @@ julia> qsimplify(S*vac, rewriter=qsimplify_fock)
"""
@withmetadata struct SqueezeOp <: AbstractSingleBosonOp
z::Number
basis::FockBasis
end
SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis)
symbollabel(x::SqueezeOp) = "S($(x.z))"
symbollabel(x::SqueezeOp) = "S($(x.z))"

"""Thermal bosonic state in defined Fock basis."""
@withmetadata struct BosonicThermalState <: AbstractSingleBosonOp
photons::Number
end
symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))"

"""Two-mode squeezing operator in defined Fock basis."""
@withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp
z::Number
end
symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))"

"""Two-mode beamsplitter operator in defined Fock basis."""
@withmetadata struct BeamSplitterOp <: AbstractTwoBosonOp
transmit::Number
end
symbollabel(x::BeamSplitterOp) = "B($(x.transmit))"
62 changes: 31 additions & 31 deletions src/QSymbolicsBase/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ function hasscalings(xs)
end
end
_isa(T) = x->isa(x,T)
_isequal(obj) = x->(x==obj)
_vecisa(T) = x->all(_isa(T), x)

##
Expand All @@ -33,36 +32,36 @@ RULES_PAULI = [
@rule(~o1::_isa(HGate)*~o2::_isa(YGate)*~o3::_isa(HGate) => -Y),
@rule(~o1::_isa(HGate)*~o2::_isa(ZGate)*~o3::_isa(HGate) => X),

@rule(~o::_isa(XGate)*~k::_isequal(X1) => X1),
@rule(~o::_isa(YGate)*~k::_isequal(X1) => -im*X2),
@rule(~o::_isa(ZGate)*~k::_isequal(X1) => X2),
@rule(~o::_isa(XGate)*~k::isequal(X1) => X1),
@rule(~o::_isa(YGate)*~k::isequal(X1) => -im*X2),
@rule(~o::_isa(ZGate)*~k::isequal(X1) => X2),

@rule(~o::_isa(XGate)*~k::_isequal(X2) => -X2),
@rule(~o::_isa(YGate)*~k::_isequal(X2) => im*X1),
@rule(~o::_isa(ZGate)*~k::_isequal(X2) => X1),
@rule(~o::_isa(XGate)*~k::isequal(X2) => -X2),
@rule(~o::_isa(YGate)*~k::isequal(X2) => im*X1),
@rule(~o::_isa(ZGate)*~k::isequal(X2) => X1),

@rule(~o::_isa(XGate)*~k::_isequal(Y1) => im*Y2),
@rule(~o::_isa(YGate)*~k::_isequal(Y1) => Y1),
@rule(~o::_isa(ZGate)*~k::_isequal(Y1) => Y2),
@rule(~o::_isa(XGate)*~k::isequal(Y1) => im*Y2),
@rule(~o::_isa(YGate)*~k::isequal(Y1) => Y1),
@rule(~o::_isa(ZGate)*~k::isequal(Y1) => Y2),

@rule(~o::_isa(XGate)*~k::_isequal(Y2) => -im*Y1),
@rule(~o::_isa(YGate)*~k::_isequal(Y2) => -Y2),
@rule(~o::_isa(ZGate)*~k::_isequal(Y2) => Y1),
@rule(~o::_isa(XGate)*~k::isequal(Y2) => -im*Y1),
@rule(~o::_isa(YGate)*~k::isequal(Y2) => -Y2),
@rule(~o::_isa(ZGate)*~k::isequal(Y2) => Y1),

@rule(~o::_isa(XGate)*~k::_isequal(Z1) => Z2),
@rule(~o::_isa(YGate)*~k::_isequal(Z1) => im*Z2),
@rule(~o::_isa(ZGate)*~k::_isequal(Z1) => Z1),
@rule(~o::_isa(XGate)*~k::isequal(Z1) => Z2),
@rule(~o::_isa(YGate)*~k::isequal(Z1) => im*Z2),
@rule(~o::_isa(ZGate)*~k::isequal(Z1) => Z1),

@rule(~o::_isa(XGate)*~k::_isequal(Z2) => Z1),
@rule(~o::_isa(YGate)*~k::_isequal(Z2) => -im*Z1),
@rule(~o::_isa(ZGate)*~k::_isequal(Z2) => -Z2),
@rule(~o::_isa(XGate)*~k::isequal(Z2) => Z1),
@rule(~o::_isa(YGate)*~k::isequal(Z2) => -im*Z1),
@rule(~o::_isa(ZGate)*~k::isequal(Z2) => -Z2),

@rule(~o::_isa(HGate)*~k::_isequal(X1) => Z1),
@rule(~o::_isa(HGate)*~k::_isequal(X2) => Z2),
@rule(~o::_isa(HGate)*~k::_isequal(Y1) => (X1+im*X2)/sqrt(2)),
@rule(~o::_isa(HGate)*~k::_isequal(Y2) => (X1-im*X2)/sqrt(2)),
@rule(~o::_isa(HGate)*~k::_isequal(Z1) => X1),
@rule(~o::_isa(HGate)*~k::_isequal(Z2) => X2)
@rule(~o::_isa(HGate)*~k::isequal(X1) => Z1),
@rule(~o::_isa(HGate)*~k::isequal(X2) => Z2),
@rule(~o::_isa(HGate)*~k::isequal(Y1) => (X1+im*X2)/sqrt(2)),
@rule(~o::_isa(HGate)*~k::isequal(Y2) => (X1-im*X2)/sqrt(2)),
@rule(~o::_isa(HGate)*~k::isequal(Z1) => X1),
@rule(~o::_isa(HGate)*~k::isequal(Z2) => X2)
]

# Commutator identities
Expand All @@ -86,27 +85,28 @@ RULES_ANTICOMMUTATOR = [
@rule(anticommutator(~o1::_isa(YGate), ~o2::_isa(XGate)) => 0),
@rule(anticommutator(~o1::_isa(ZGate), ~o2::_isa(YGate)) => 0),
@rule(anticommutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => 0),
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp((~o1).basis)),
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp((~o1).basis)),
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp(basis(~o1))),
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp(basis(~o1))),
@rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(DestroyOp)) => -(~o2)),
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => (~o1)),
@rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => (~o2)),
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(NumberOp)) => -(~o1))
]

RULES_FOCK = [
@rule(~o::_isa(DestroyOp) * ~k::_isequal(vac) => SZeroKet()),
@rule(~o::_isa(DestroyOp) * ~k::isequal(vac) => SZeroKet()),
@rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx+1])*FockState((~k).idx+1)),
@rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx])*FockState((~k).idx-1)),
@rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)),
@rule(~o::_isa(DestroyOp) * ~k::_isa(CoherentState) => (~k).alpha*(~k)),
@rule(~o::_isa(PhaseShiftOp) * ~k::_isa(CoherentState) => CoherentState((~k).alpha * exp(-im*(~o).phase))),
@rule(dagger(~o1::_isa(PhaseShiftOp)) * ~o2::_isa(DestroyOp) * ~o1 => ~o2*exp(-im*((~o1).phase))),
@rule(~o1::_isa(PhaseShiftOp) * ~o2::_isa(DestroyOp) * dagger(~o1) => ~o2*exp(im*((~o1).phase))),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp(basis(~o2))),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp(basis(~o2))),
@rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)),
@rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis))
@rule(~o::_isa(SqueezeOp) * ~k::isequal(vac) => SqueezedState((~o).z)),
@rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac ⊗ vac) => TwoSqueezedState((~o).z))
]

RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK]
Expand Down
3 changes: 3 additions & 0 deletions test/test_fock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
phase2 = PhaseShiftOp(pi)
displace = DisplaceOp(im)
squeezeop = SqueezeOp(pi)
twosqueezeop = TwoSqueezeOp(pi)
sstate = SqueezedState(pi)
tsstate = TwoSqueezedState(pi)

@testset "ladder and number operators" begin
@test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet())
Expand All @@ -28,5 +30,6 @@

@testset "Squeeze operators" begin
@test isequal(qsimplify(squeezeop*vac, rewriter=qsimplify_fock), sstate)
@test isequal(qsimplify(twosqueezeop*(vac ⊗ vac), rewriter=qsimplify_fock), tsstate)
end
end
Loading