Algebra and ordering
The Heisenberg equation of a Kerr oscillator, $\dot a = i[H, a]$ with $H = K a^{\dagger 2} a^2$, asks to move an annihilation operator past two creation operators. Using $[a, a^\dagger] = 1$ gives $\dot a = -2iK a^\dagger a^2$; the same result comes out in normal order directly:
using SecondQuantizedAlgebraa = Destroy(FockSpace(:cavity), :a)@variables K1im * commutator(K * a' * a' * a * a, a)\[ - 2 ~ K ~ \mathit{i} a^{\dagger}aa\]
Operator expressions are kept in a canonical form at all times. Every product is reordered and reduced the moment it is formed, so like terms combine without further work.
Canonical form
Each algebra brings its own rules, and all of them apply as soon as a product is formed.
using SecondQuantizedAlgebrah = FockSpace(:cavity) ⊗ NLevelSpace(:atom, 3) ⊗ PauliSpace(:qubit) ⊗ SpinSpace(:S) ⊗ PhaseSpace(:osc)@qnumbers a::Destroy(h, 1) b::Destroy(h, 1) x::Position(h) p::Momentum(h)σ(i, j) = Transition(h, :σ, i, j, 2)τ(k) = Pauli(h, :τ, k)S(k) = Spin(h, :S, k)Operators of different subsystems are sorted by their position in the product space.
x * a * σ(2, 1)\[a{\sigma}^{{21}}\hat{x}\]
Each bosonic mode is normal ordered, and modes with different names are grouped by name.
b' * a' * b * a\[a^{\dagger}ab^{\dagger}b\]
Transitions on the same system compose.
σ(1, 2) * σ(2, 3)\[{\sigma}^{{13}}\]
Products of Pauli matrices reduce to a single Pauli matrix or a number.
τ(1) * τ(2)\[\mathit{i} {\tau}_{{z}}\]
Spin components are sorted by axis.
S(2) * S(1)\[-\mathit{i} {S}_{{z}} + {S}_{{x}}{S}_{{y}}\]
Position stands to the left of momentum.
p * x\[-\mathit{i} + \hat{x}\hat{p}\]
normal_order applies the same rules explicitly, for code that expects a separate ordering step.
SecondQuantizedAlgebra.normal_order — Function
normal_order(expr::QField)Bring expr into canonical, normal-ordered form.
Arithmetic already returns this form: *, +, -, ^, adjoint, commutator, Σ and substitute all canonicalize as they go, so normal_order leaves their results unchanged. It is there for code that expects an explicit ordering step. Formal operator functions such as cos or expim keep their form; only their polynomial arguments are ordered.
Examples
julia> h = FockSpace(:f);julia> @qnumbers a::Destroy(h);julia> a * a'1 + a' * ajulia> normal_order(a * a')1 + a' * aSee also simplify, expand_completeness, normal_to_symmetric, symmetric_to_normal.
The ground-state projector is the one deliberate exception. It stays as it is, since rewriting it through completeness would multiply the number of terms in dissipators and equations of motion.
σ(1, 2) * σ(2, 1)\[{\sigma}^{{11}}\]
Completeness is applied only on request.
expand_completeness(σ(1, 2) * σ(2, 1))\[1 -{\sigma}^{{22}} -{\sigma}^{{33}}\]
SecondQuantizedAlgebra.expand_completeness — Function
expand_completeness(expr)Eliminate every ground-state projector in expr through completeness.
Each projector $\sigma^{gg}$ is replaced by $1 - \sum_{k \neq g} \sigma^{kk}$. Arithmetic never does this by itself, which keeps expressions short. Expand when the remaining projectors should be the independent variables, e.g. before deriving equations for their expectation values.
Examples
julia> h = NLevelSpace(:atom, 2);julia> σ11 = Transition(h, :σ, 1, 1);julia> expand_completeness(σ11)1 - σ₂₂See also assume_distinct_index, normal_order.
An expression in canonical form has its own type, which is what products, sums and commutators of operators return.
SecondQuantizedAlgebra.QAdd — Type
QAdd <: QFieldType of operator expressions that are polynomials in the operators, kept in canonical form.
Sums, products and commutators of operators return a QAdd, with every product ordered and reduced and like terms collected, so two equal polynomials compare equal. A single operator is an Op instead, and an expression that contains a formal function such as cos is a QExpr. length gives the number of terms, and sorted_arguments splits an expression into them. The type is public but not exported.
Examples
julia> using SecondQuantizedAlgebra: QAddjulia> a = Destroy(FockSpace(:cavity), :a);julia> x = a * a'1 + a' * ajulia> x isa QAdd, length(x)(true, 2)See also QExpr, normal_order, sorted_arguments.
Commutators
Since both orderings of a product are already canonical, a commutator costs no more than two products and a subtraction. The Pauli matrices show both brackets at once: different components anticommute, and their commutator is the third component.
commutator(τ(1), τ(2)), anticommutator(τ(1), τ(2))(2im * τz, 0)For a mode, the anticommutator of $a$ and $a^\dagger$ is the symmetric combination that enters vacuum fluctuations.
anticommutator(a, a')\[1 + 2 a^{\dagger}a\]
SecondQuantizedAlgebra.commutator — Function
commutator(a, b)Commutator of a and b.
Returns $[a, b] = ab - ba$ in canonical form. If either argument is a formal operator function, the result stays formal until lowered with taylor.
Examples
julia> h = FockSpace(:f);julia> @qnumbers a::Destroy(h);julia> commutator(a, a')1julia> commutator(a' * a, a)-aSee also anticommutator, normal_order, taylor.
SecondQuantizedAlgebra.anticommutator — Function
anticommutator(a, b)Anticommutator of a and b.
Returns $\{a, b\} = ab + ba$ in canonical form.
Examples
julia> h = FockSpace(:f);julia> @qnumbers a::Destroy(h);julia> anticommutator(a, a')1 + 2 * a' * aSee also commutator.
Finishing coefficients
Coefficients are exact numbers or Symbolics expressions, as described in Parameters and coefficients. Polynomial coefficients cancel as terms combine, while identities between functions are applied only by simplify.
@variables θsimplify((sin(θ)^2 + cos(θ)^2) * a' * a)\[a^{\dagger}a\]
expand multiplies out products that sit inside functions of the coefficients, which are otherwise kept as written.
@variables x yexpand(exp(x * (x + y)) * a)\[e^{x^{2} + x ~ y} a\]
SymbolicUtils.simplify — Method
simplify(expr::QField)Simplify the scalar coefficients of expr symbolically.
Polynomial coefficients already cancel as an expression is built. simplify goes further and applies symbolic identities, such as trigonometric ones, to every coefficient, then drops the terms that vanish. Because this step is expensive, it is best used once on a final result. Formal operator functions keep their form.
Examples
julia> h = FockSpace(:f);julia> @qnumbers a::Destroy(h);julia> @variables x;julia> expr = (sin(x)^2 + cos(x)^2) * a' * a(cos(x)^2 + sin(x)^2) * a' * ajulia> simplify(expr)a' * aSee also normal_order, expand, expand_completeness.
SymbolicUtils.expand — Method
expand(expr::QField)Multiply out the scalar coefficients of expr.
Polynomial coefficients are kept expanded already; expand also reaches products inside functions such as exp. Formal operator functions are not expanded.
Examples
julia> h = FockSpace(:f);julia> @qnumbers a::Destroy(h);julia> @variables x y;julia> expr = exp(x * (x + y)) * a;julia> expand(expr)exp(x^2 + x*y) * aSymmetric ordering
Phase-space methods such as the truncated Wigner approximation work with symmetrically ordered operators rather than normal-ordered ones. In symmetric order the photon number $a^\dagger a$ reads as $(a^\dagger a + a a^\dagger)/2 - 1/2$, so the half quantum of vacuum noise becomes explicit.
n_sym = normal_to_symmetric(a' * a)\[\frac{-1}{2} + a^{\dagger}a\]
The conversion is exact in both directions.
symmetric_to_normal(n_sym) == a' * atrueSecondQuantizedAlgebra.normal_to_symmetric — Function
normal_to_symmetric(expr)Rewrite a normal-ordered expression in symmetric (Weyl) order.
The result is written with the same monomials, but each now stands for its fully symmetrized product: a' * a reads as $(a^\dagger a + a a^\dagger)/2$ and x * p as $(xp + px)/2$. This is the form phase-space methods such as the truncated Wigner approximation work with. Bosonic modes and quadratures are converted; other operators pass through unchanged. The inverse is symmetric_to_normal.
Examples
julia> hf = FockSpace(:f);julia> @qnumbers a::Destroy(hf);julia> normal_to_symmetric(a' * a)-1//2 + a' * ajulia> hp = PhaseSpace(:osc);julia> @qnumbers x::Position(hp) p::Momentum(hp);julia> normal_to_symmetric(x * p)1//2im + x * pSee also symmetric_to_normal, normal_order.
SecondQuantizedAlgebra.symmetric_to_normal — Function
symmetric_to_normal(expr)Rewrite a symmetric (Weyl) ordered expression in normal order.
Each monomial of expr is read as its symmetrized product, as returned by normal_to_symmetric, and expanded back into normal order. The two functions are exact inverses.
Examples
julia> hf = FockSpace(:f);julia> @qnumbers a::Destroy(hf);julia> symmetric_to_normal(a' * a)1//2 + a' * ajulia> normal_to_symmetric(symmetric_to_normal(a' * a))a' * aSee also normal_to_symmetric, normal_order.