Skip to content

Commit

Permalink
rearranging maketerm defs and doctest change
Browse files Browse the repository at this point in the history
  • Loading branch information
apkille committed Jun 28, 2024
1 parent 970601b commit 35dfb77
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 70 deletions.
75 changes: 26 additions & 49 deletions docs/src/express.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,42 @@ A principle feature of `QuantumSymbolics` is to numerically represent symbolic q

As a straightforward example, consider the spin-up state $|\uparrow\rangle = |0\rangle$, the eigenstate of the Pauli operator $Z$, which can be expressed in `QuantumSymbolics` as follows:

```jldoctest
julia> ψ = Z1
|Z₁⟩
```@example 1
ψ = Z1
```
Using [`express`](@ref), we can translate this symbolic object into its numerical state vector form in `QuantumOptics.jl`.

```jldoctest
julia> ψ = Z1;
julia> express(ψ)
Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0im
julia> ψ.metadata
QuantumSymbolics.Metadata(Dict{Tuple{AbstractRepresentation, AbstractUse}, Any}((QuantumOpticsRepr(), UseAsState()) => Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0im))
```@example 1
express(ψ)
```

By default, [`express`](@ref) converts a quantum object with [`QuantumOpticRepr`](@ref). It should be noted that [`express`](@ref) automatically caches this particular conversion of `ψ`. Thus, after running the above example, the numerical representation of the spin-up state is stored in the metadata of `ψ`.

```@example 1
ψ.metadata
```

The caching feature of [`express`](@ref) prevents a specific representation for a symbolic quantum object from being computed more than once. This becomes handy for translations of more complex operations, which can become computationally expensive. We also have the ability to express $|Z_1\rangle$ in the Clifford formalism with `QuantumClifford.jl`:
```jldoctest
julia> ψ = Z1;
julia> express(ψ, CliffordRepr())
𝒟ℯ𝓈𝓉𝒶𝒷
+ X
𝒮𝓉𝒶𝒷
+ Z
julia> ψ.metadata
QuantumSymbolics.Metadata(Dict{Tuple{AbstractRepresentation, AbstractUse}, Any}((CliffordRepr(), UseAsState()) => MixedDestablizer 1×1, (QuantumOpticsRepr(), UseAsState()) => Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0im))

```@example 1
express(ψ, CliffordRepr())
```

Here, we specified an instance of [`CliffordRepr`](@ref) in the second argument to convert `ψ` into a tableau of Pauli operators containing its stabilizer and destabilizer states. Now, both the state vector and Clifford representation of `ψ` have been cached.
Here, we specified an instance of [`CliffordRepr`](@ref) in the second argument to convert `ψ` into a tableau of Pauli operators containing its stabilizer and destabilizer states. Now, both the state vector and Clifford representation of `ψ` have been cached:

```@example 1
ψ.metadata
```

More involved examples can be explored. For instance, say we want to apply the tensor product $X\otimes Y$ of the Pauli operators $X$ and $Y$ to the Bell state $|\Phi^{+}\rangle = \dfrac{1}{\sqrt{2}}\left(|00\rangle + |11\rangle\right)$, and numerically express the result in the quantum optics formalism. This would be done as follows:

```@example 2
bellstate = (Z1⊗Z1+Z2⊗Z2)/√2
tp = σˣ⊗σʸ
express(tp*bellstate)
```

## Examples of Edge Cases
For Pauli operators, additional flexibility is given for translations to the Clifford formalism. Users have the option to convert a multi-qubit Pauli operator to an observable or operation with instances of [`UseAsObservable`](@ref) and [`UseAsOperation`](@ref), respectively. Take the Pauli operator $Y$, for example, which in `QuantumSymbolics` is the constants `Y` or `σʸ`:

```jldoctest
Expand All @@ -60,21 +54,4 @@ julia> express(σʸ, CliffordRepr(), UseAsObservable())
julia> express(σʸ, CliffordRepr(), UseAsOperation())
sY
```
More involved examples can be explored. For instance, say we want to apply the tensor product $X\otimes Y$ of the Pauli operators $X$ and $Y$ to the Bell state $|\Phi^{+}\rangle = \dfrac{1}{\sqrt{2}}\left(|00\rangle + |11\rangle\right)$, and numerically express the result in the quantum optics formalism. This would be done as follows:

```jldoctest
julia> bellstate = (Z1⊗Z1+Z2⊗Z2)/√2
0.7071067811865475(|Z₁⟩|Z₁⟩+|Z₂⟩|Z₂⟩)
julia> tp = σˣ⊗σʸ
X⊗Y
julia> express(tp*bellstate)
Ket(dim=4)
basis: [Spin(1/2) ⊗ Spin(1/2)]
0.0 - 0.7071067811865475im
0.0 + 0.0im
0.0 + 0.0im
0.0 + 0.7071067811865475im
```
1 change: 1 addition & 0 deletions src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Base.:(-)(x::SymQObj) = (-1)*x
Base.:(-)(x::SymQObj,y::SymQObj) = x + (-y)
Base.hash(x::SymQObj, h::UInt) = isexpr(x) ? hash((head(x), arguments(x)), h) :
hash((typeof(x),symbollabel(x),basis(x)), h)
maketerm(::Type{<:SymQObj}, f, a, t, m) = f(a...)

function Base.isequal(x::X,y::Y) where {X<:SymQObj, Y<:SymQObj}
if X==Y
Expand Down
13 changes: 0 additions & 13 deletions src/QSymbolicsBase/basic_ops_homogeneous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ Base.:(/)(x::Symbolic{T}, c) where {T<:QObj} = iszero(c) ? throw(DomainError(c,"
basis(x::SScaled) = basis(x.obj)

const SScaledKet = SScaled{AbstractKet}
maketerm(::Type{SScaledKet}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SScaledKet)
if x.coeff isa Real
print(io, "$(x.coeff)$(x.obj)")
Expand All @@ -51,7 +50,6 @@ function Base.show(io::IO, x::SScaledKet)
end
end
const SScaledOperator = SScaled{AbstractOperator}
maketerm(::Type{SScaledOperator}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SScaledOperator)
if x.coeff isa Real
print(io, "$(x.coeff)$(x.obj)")
Expand All @@ -60,7 +58,6 @@ function Base.show(io::IO, x::SScaledOperator)
end
end
const SScaledBra = SScaled{AbstractBra}
maketerm(::Type{SScaledBra}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SScaledBra)
if x.coeff isa Real
print(io, "$(x.coeff)$(x.obj)")
Expand Down Expand Up @@ -103,19 +100,16 @@ Base.:(+)(xs::Vararg{Symbolic{<:QObj},0}) = 0 # to avoid undefined type paramete
basis(x::SAdd) = basis(first(x.dict).first)

const SAddKet = SAdd{AbstractKet}
maketerm(::Type{SAddKet}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SAddKet)
ordered_terms = sort([repr(i) for i in arguments(x)])
print(io, "("*join(ordered_terms,"+")::String*")") # type assert to help inference
end
const SAddOperator = SAdd{AbstractOperator}
maketerm(::Type{SAddOperator}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SAddOperator)
ordered_terms = sort([repr(i) for i in arguments(x)])
print(io, "("*join(ordered_terms,"+")::String*")") # type assert to help inference
end
const SAddBra = SAdd{AbstractBra}
maketerm(::Type{SAddBra}, f, a, t, m) = f(a...)
function Base.show(io::IO, x::SAddBra)
ordered_terms = sort([repr(i) for i in arguments(x)])
print(io, "("*join(ordered_terms,"+")::String*")") # type assert to help inference
Expand Down Expand Up @@ -143,7 +137,6 @@ arguments(x::SMulOperator) = x.terms
operation(x::SMulOperator) = *
head(x::SMulOperator) = :*
children(x::SMulOperator) = [:*;x.terms]
maketerm(::Type{SMulOperator}, f, a, t, m) = f(a...)
function Base.:(*)(xs::Symbolic{AbstractOperator}...)
zero_ind = findfirst(x->iszero(x), xs)
isnothing(zero_ind) ? SMulOperator(collect(xs)) : SZeroOperator()
Expand Down Expand Up @@ -185,16 +178,12 @@ end
basis(x::STensor) = tensor(basis.(x.terms)...)

const STensorBra = STensor{AbstractBra}
maketerm(::Type{STensorBra}, f, a, t, m) = f(a...)
Base.show(io::IO, x::STensorBra) = print(io, join(map(string, arguments(x)),""))

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

View check run for this annotation

Codecov / codecov/patch

src/QSymbolicsBase/basic_ops_homogeneous.jl#L181

Added line #L181 was not covered by tests
const STensorKet = STensor{AbstractKet}
maketerm(::Type{STensorKet}, f, a, t, m) = f(a...)
Base.show(io::IO, x::STensorKet) = print(io, join(map(string, arguments(x)),""))
const STensorOperator = STensor{AbstractOperator}
maketerm(::Type{STensorOperator}, f, a, t, m) = f(a...)
Base.show(io::IO, x::STensorOperator) = print(io, join(map(string, arguments(x)),""))
const STensorSuperOperator = STensor{AbstractSuperOperator}
maketerm(::Type{STensorSuperOperator}, f, a, t, m) = f(a...)
Base.show(io::IO, x::STensorSuperOperator) = print(io, join(map(string, arguments(x)),""))

"""Symbolic commutator of two operators
Expand Down Expand Up @@ -223,7 +212,6 @@ arguments(x::SCommutator) = [x.op1, x.op2]
operation(x::SCommutator) = commutator
head(x::SCommutator) = :commutator
children(x::SCommutator) = [:commutator, x.op1, x.op2]
maketerm(::Type{SCommutator}, f, a, t, m) = f(a...)
commutator(o1::Symbolic{AbstractOperator}, o2::Symbolic{AbstractOperator}) = SCommutator(o1, o2)
commutator(o1::SZeroOperator, o2::Symbolic{AbstractOperator}) = SZeroOperator()
commutator(o1::Symbolic{AbstractOperator}, o2::SZeroOperator) = SZeroOperator()
Expand Down Expand Up @@ -255,7 +243,6 @@ arguments(x::SAnticommutator) = [x.op1, x.op2]
operation(x::SAnticommutator) = anticommutator
head(x::SAnticommutator) = :anticommutator
children(x::SAnticommutator) = [:anticommutator, x.op1, x.op2]
maketerm(::Type{SAnticommutator}, f, a, t, m) = f(a...)
anticommutator(o1::Symbolic{AbstractOperator}, o2::Symbolic{AbstractOperator}) = SAnticommutator(o1, o2)
anticommutator(o1::SZeroOperator, o2::Symbolic{AbstractOperator}) = SZeroOperator()
anticommutator(o1::Symbolic{AbstractOperator}, o2::SZeroOperator) = SZeroOperator()
Expand Down
5 changes: 0 additions & 5 deletions src/QSymbolicsBase/basic_ops_inhomogeneous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ arguments(x::SApplyKet) = [x.op,x.ket]
operation(x::SApplyKet) = *
head(x::SApplyKet) = :*
children(x::SApplyKet) = [:*,x.op,x.ket]
maketerm(::Type{SApplyKet}, f, a, t, m) = f(a...)
Base.:(*)(op::Symbolic{AbstractOperator}, k::Symbolic{AbstractKet}) = SApplyKet(op,k)
Base.:(*)(op::SZeroOperator, k::Symbolic{AbstractKet}) = SZeroKet()
Base.:(*)(op::Symbolic{AbstractOperator}, k::SZeroKet) = SZeroKet()
Expand Down Expand Up @@ -56,7 +55,6 @@ arguments(x::SApplyBra) = [x.bra,x.op]
operation(x::SApplyBra) = *
head(x::SApplyBra) = :*
children(x::SApplyBra) = [:*,x.bra,x.op]
maketerm(::Type{SApplyBra}, f, a, t, m) = f(a...)
Base.:(*)(b::Symbolic{AbstractBra}, op::Symbolic{AbstractOperator}) = SApplyBra(b,op)
Base.:(*)(b::SZeroBra, op::Symbolic{AbstractOperator}) = SZeroBra()
Base.:(*)(b::Symbolic{AbstractBra}, op::SZeroOperator) = SZeroBra()
Expand All @@ -83,7 +81,6 @@ arguments(x::SBraKet) = [x.bra,x.ket]
operation(x::SBraKet) = *
head(x::SBraKet) = :*
children(x::SBraKet) = [:*,x.bra,x.ket]
maketerm(::Type{SBraKet}, f, a, t, m) = f(a...)
Base.:(*)(b::Symbolic{AbstractBra}, k::Symbolic{AbstractKet}) = SBraKet(b,k)
Base.:(*)(b::SZeroBra, k::Symbolic{AbstractKet}) = 0
Base.:(*)(b::Symbolic{AbstractBra}, k::SZeroKet) = 0
Expand All @@ -102,7 +99,6 @@ arguments(x::SSuperOpApply) = [x.sop,x.op]
operation(x::SSuperOpApply) = *
head(x::SSuperOpApply) = :*
children(x::SSuperOpApply) = [:*,x.sop,x.op]
maketerm(::Type{SSuperOpApply}, f, a, t, m) = f(a...)
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))
Expand Down Expand Up @@ -132,7 +128,6 @@ arguments(x::SOuterKetBra) = [x.ket,x.bra]
operation(x::SOuterKetBra) = *
head(x::SOuterKetBra) = :*
children(x::SOuterKetBra) = [:*,x.ket,x.bra]
maketerm(::Type{SOuterKetBra}, f, a, t, m) = f(a...)
Base.:(*)(k::Symbolic{AbstractKet}, b::Symbolic{AbstractBra}) = SOuterKetBra(k,b)
Base.:(*)(k::SZeroKet, b::Symbolic{AbstractBra}) = SZeroOperator()
Base.:(*)(k::Symbolic{AbstractKet}, b::SZeroBra) = SZeroOperator()
Expand Down
3 changes: 0 additions & 3 deletions src/QSymbolicsBase/predefined.jl
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ arguments(x::SProjector) = [x.ket]
operation(x::SProjector) = projector
head(x::SProjector) = :projector
children(x::SProjector) = [:projector,x.ket]
maketerm(::Type{SProjector}, f, a, t, m) = f(a...)
projector(x::Symbolic{AbstractKet}) = SProjector(x)
projector(x::SZeroKet) = SZeroOperator()
basis(x::SProjector) = basis(x.ket)
Expand Down Expand Up @@ -262,7 +261,6 @@ arguments(x::SDagger) = [x.obj]
operation(x::SDagger) = dagger
head(x::SDagger) = :dagger
children(x::SDagger) = [:dagger, x.obj]
maketerm(::Type{SDagger}, f, a, t, m) = f(a...)
dagger(x::Symbolic{AbstractBra}) = SDagger{AbstractKet}(x)
dagger(x::Symbolic{AbstractKet}) = SDagger{AbstractBra}(x)
dagger(x::Symbolic{AbstractOperator}) = SDagger{AbstractOperator}(x)
Expand Down Expand Up @@ -311,7 +309,6 @@ arguments(x::SInvOperator) = [x.op]
operation(x::SInvOperator) = inv
head(x::SInvOperator) = :inv
children(x::SInvOperator) = [:inv, x.op]
maketerm(::Type{SInvOperator}, f, a, t, m) = f(a...)
basis(x::SInvOperator) = basis(x.op)
Base.show(io::IO, x::SInvOperator) = print(io, "$(x.op)⁻¹")
Base.:(*)(invop::SInvOperator, op::SOperator) = isequal(invop.op, op) ? IdentityOp(basis(op)) : SMulOperator(invop, op)
Expand Down

0 comments on commit 35dfb77

Please sign in to comment.