Express functionality
A principle feature of QuantumSymbolics is to numerically represent symbolic quantum expressions in various formalisms using express. In particular, one can translate symbolic logic to back-end toolboxes such as QuantumOptics.jl or QuantumClifford.jl for simulating quantum systems with great flexibility.
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:
ψ = Z1\[\left|Z_1\right\rangle\]
Using express, we can translate this symbolic object into its numerical state vector form in QuantumOptics.jl.
express(ψ)Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0imBy default, express converts a quantum object with QuantumOpticRepr. It should be noted that express 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 ψ.
ψ.metadataQuantumSymbolics.Metadata(Dict{Tuple{AbstractRepresentation, AbstractUse}, Any}((QuantumOpticsRepr(2), UseAsState()) => Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0im))The caching feature of express 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:
express(ψ, CliffordRepr())𝒟ℯ𝓈𝓉𝒶𝒷
+ X
𝒮𝓉𝒶𝒷
+ ZHere, we specified an instance of CliffordRepr 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:
ψ.metadataQuantumSymbolics.Metadata(Dict{Tuple{AbstractRepresentation, AbstractUse}, Any}((QuantumOpticsRepr(2), UseAsState()) => Ket(dim=2)
basis: Spin(1/2)
1.0 + 0.0im
0.0 + 0.0im, (CliffordRepr(), UseAsState()) => MixedDestablizer 1×1))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:
bellstate = (Z1⊗Z1+Z2⊗Z2)/√2
tp = σˣ⊗σʸ
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.7071067811865475imExamples 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 and UseAsOperation, respectively. Take the Pauli operator $Y$, for example, which in QuantumSymbolics is the constants Y or σʸ:
julia> express(σʸ, CliffordRepr(), UseAsObservable())
+ Y
julia> express(σʸ, CliffordRepr(), UseAsOperation())
sYAnother edge case is translations with QuantumOpticsRepr, where we can additionally define a finite cutoff for bosonic states and operators, as discussed in the quantum harmonic oscillators page. The default cutoff for such objects is 2, however a different cutoff can be specified by passing an integer to QuantumOpticsRepr in an express call. Let us see an example with the number operator:
julia> express(N) |> dense
Operator(dim=3x3)
basis: Fock(cutoff=2)
0.0+0.0im 0.0+0.0im 0.0+0.0im
0.0+0.0im 1.0+0.0im 0.0+0.0im
0.0+0.0im 0.0+0.0im 2.0+0.0im
julia> express(N, QuantumOpticsRepr(cutoff=4)) |> dense
Operator(dim=5x5)
basis: Fock(cutoff=4)
0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im
0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im
0.0+0.0im 0.0+0.0im 2.0+0.0im 0.0+0.0im 0.0+0.0im
0.0+0.0im 0.0+0.0im 0.0+0.0im 3.0+0.0im 0.0+0.0im
0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 4.0+0.0im