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
81 changes: 47 additions & 34 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.0) # Inf is Float, so the second parameter has to be Float too

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

Check warning on line 10 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L10

Added line #L10 was not covered by tests

"""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)"

Check warning on line 34 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L34

Added line #L34 was not covered by tests

"""Single-mode vacuum state"""
const vac = const F₀ = const F0 = FockState(0)
"""Single photon basis state of n"""
"""Single photon 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

Check warning on line 51 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L51

Added line #L51 was not covered by tests
basis(x::AbstractTwoBosonOp) = inf_fock_basis^2

"""Number operator.

Expand All @@ -54,10 +64,7 @@
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 @@
(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 @@
(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 @@ -111,11 +112,9 @@
|1.2246467991473532e-16 - 1.0im⟩
```
"""
@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp
@withmetadata struct PhaseShiftOp <: AbstractSingleBosonGate
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 @@ -131,11 +130,9 @@
|im⟩
```
"""
@withmetadata struct DisplaceOp <: AbstractSingleBosonOp
@withmetadata struct DisplaceOp <: AbstractSingleBosonGate
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 @@ -156,9 +153,25 @@
|0,π⟩
```
"""
@withmetadata struct SqueezeOp <: AbstractSingleBosonOp
@withmetadata struct SqueezeOp <: AbstractSingleBosonGate
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))"

Check warning on line 165 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L165

Added line #L165 was not covered by tests

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

Check warning on line 171 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L171

Added line #L171 was not covered by tests

"""Two-mode beamsplitter operator in defined Fock basis."""
@withmetadata struct BeamSplitterOp <: AbstractTwoBosonGate
transmit::Number
end
symbollabel(x::BeamSplitterOp) = "B($(x.transmit))"

Check warning on line 177 in src/QSymbolicsBase/predefined_fock.jl

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/predefined_fock.jl#L177

Added line #L177 was not covered by tests
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
6 changes: 3 additions & 3 deletions test/test_express_opt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@

state = (3im*(2*dagger(Z1)+dagger(Y1))) * (3im*(2*X1+X2))

cstate = CoherentState(im, inf_fock_basis)
displace = DisplaceOp(im,inf_fock_basis)
phase = PhaseShiftOp(im, inf_fock_basis)
cstate = CoherentState(im)
displace = DisplaceOp(im)
phase = PhaseShiftOp(im)
@test express(N*F1) ≈ express(N)*express(F1)
@test express(Create*F1) ≈ express(Create)*express(F1)
@test express(Destroy*F1) ≈ express(Destroy)*express(F1)
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