diff --git a/docs/src/express.md b/docs/src/express.md index 521323c..4ab705a 100644 --- a/docs/src/express.md +++ b/docs/src/express.md @@ -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 @@ -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 ``` \ No newline at end of file diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 3a78be5..bb913bc 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -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 diff --git a/src/QSymbolicsBase/basic_ops_homogeneous.jl b/src/QSymbolicsBase/basic_ops_homogeneous.jl index af9c6a8..63aa58a 100644 --- a/src/QSymbolicsBase/basic_ops_homogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_homogeneous.jl @@ -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)") @@ -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)") @@ -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)") @@ -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 @@ -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() @@ -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)),"")) 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 @@ -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() @@ -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() diff --git a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl index 6679794..49c48ae 100644 --- a/src/QSymbolicsBase/basic_ops_inhomogeneous.jl +++ b/src/QSymbolicsBase/basic_ops_inhomogeneous.jl @@ -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() @@ -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() @@ -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 @@ -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)) @@ -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() diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 8a48d9c..e7c28c3 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -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) @@ -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) @@ -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)