API
Hilbert Spaces
SecondQuantizedAlgebra.HilbertSpace — Type
HilbertSpaceAbstract supertype for Hilbert spaces. Concrete subtypes: FockSpace, NLevelSpace, PauliSpace, SpinSpace, PhaseSpace, and ProductSpace for tensor products. Compose with ⊗ or tensor.
SecondQuantizedAlgebra.ProductSpace — Type
ProductSpace{T} <: HilbertSpaceComposite Hilbert space formed by the tensor product of multiple subspaces. The type parameter T is a concrete Tuple type for type-stable storage. Constructed via ⊗ or tensor, not directly.
Operators on a ProductSpace take a positional index specifying which subspace they act on, e.g. Destroy(h, :a, 1).
Examples
julia> FockSpace(:cavity) ⊗ NLevelSpace(:atom, 2)
ℋ(cavity) ⊗ ℋ(atom)SecondQuantizedAlgebra.NLevelSpace — Type
NLevelSpace(name::Symbol, n::Int)
NLevelSpace(name::Symbol, n::Int, ground_state::Int)
NLevelSpace(name::Symbol, levels::Tuple{Vararg{Symbol}})Hilbert space for an N-level system (atoms, qubits, qudits). Hosts Transition operators $|i\rangle\langle j| \cdot |k\rangle\langle l| = \delta_{jk} |i\rangle\langle l|$.
The ground_state (default 1) selects the projector $|g\rangle\langle g|$ that the canonical basis omits; arithmetic keeps it atomic and expand_completeness materialises $|g\rangle\langle g| = 1 - \sum_{k \neq g}|k\rangle\langle k|$ on demand. Levels can be integers (default 1:n) or symbolic names.
Examples
julia> NLevelSpace(:atom, 3)
ℋ(atom)
julia> NLevelSpace(:atom, (:g, :e))
ℋ(atom)See also Transition, expand_completeness.
SecondQuantizedAlgebra.PauliSpace — Type
PauliSpace(name::Symbol) <: HilbertSpaceHilbert space for a two-level system. Hosts Pauli operators satisfying $\sigma_j \sigma_k = \delta_{jk} I + i \epsilon_{jkl} \sigma_l$.
Examples
julia> PauliSpace(:spin)
ℋ(spin)SecondQuantizedAlgebra.SpinSpace — Type
SpinSpace(name::Symbol) <: HilbertSpaceHilbert space for collective spin angular momentum. Hosts Spin operators satisfying $[S_j, S_k] = i \epsilon_{jkl} S_l$. The algebra is independent of the spin size $S$, which enters only via the QuantumOpticsBase.SpinBasis chosen for numeric evaluation.
Examples
julia> SpinSpace(:S)
ℋ(S)See also Spin, PauliSpace.
SecondQuantizedAlgebra.PhaseSpace — Type
PhaseSpace(name::Symbol) <: HilbertSpaceHilbert space for position and momentum quadratures. Hosts Position and Momentum operators satisfying $[p, x] = -i$; arithmetic canonicalizes products to place position left of momentum.
Examples
julia> PhaseSpace(:osc)
ℋ(osc)QuantumInterface.:⊗ — Function
⊗(spaces::HilbertSpace...)Create a ProductSpace from multiple Hilbert spaces. Flattens nested ProductSpace arguments so that (A ⊗ B) ⊗ C == A ⊗ B ⊗ C. Unicode input \otimes<tab>; ASCII alias tensor.
Examples
julia> FockSpace(:a) ⊗ FockSpace(:b) ⊗ NLevelSpace(:atom, 2)
ℋ(a) ⊗ ℋ(b) ⊗ ℋ(atom)See also ProductSpace, tensor.
QuantumInterface.tensor — Function
tensor(spaces::HilbertSpace...)ASCII alias for ⊗: create a ProductSpace from multiple Hilbert spaces.
Examples
julia> tensor(FockSpace(:a), FockSpace(:b))
ℋ(a) ⊗ ℋ(b)See also ⊗, ProductSpace.
Operators
SecondQuantizedAlgebra.QField — Type
QFieldAbstract supertype for all second-quantized operator expressions.
Subtypes:
QSym: leaf operators (e.g.Destroy,Create,Transition)QAdd: compound expressions (a sum ofQTermproducts)
Supports arithmetic (+, -, *, ^, /), adjoint, and comparison via ==/isequal. All arithmetic eagerly applies normal ordering and returns QAdd.
SecondQuantizedAlgebra.QSym — Type
QSym <: QFieldAbstract type for fundamental (leaf) operators in the expression tree.
Every QSym carries three fields identifying its site:
name::Symbol— display namespace_index::Int— position inProductSpace(1 for single spaces)index::Index— symbolic summation index (Index), orNO_INDEX
Concrete subtypes: Destroy, Create, Transition, Pauli, Spin, Position, Momentum.
SecondQuantizedAlgebra.QTerm — Type
QTermA single entry of a QAdd sum: an ordered operator product plus the pairwise index inequality constraints that scope that product.
Fields
ops::Vector{Op}— the ordered operator productne::Vector{NonEqualPair}— pairwiseα ≠ βconstraints (canonicalized)
QTerm is the dict key in QTermDict; iterating a QAdd yields Pair{QTerm, CNum}, and callers reach term.ops / term.ne directly.
SecondQuantizedAlgebra.QTermDict — Type
QTermDictAlias for Dict{QTerm, CNum} — the storage type backing every QAdd. Each entry maps an exact term identity (ops, ne) to its prefactor. Iteration yields Pair{QTerm, CNum}; callers reach term.ops and term.ne directly.
SecondQuantizedAlgebra.QAdd — Type
QAdd <: QFieldThe sole compound expression type: a sum of eagerly-ordered operator products.
All arithmetic on QSym operators produces a QAdd. Iterating over a QAdd yields Pair{QTerm, CNum} entries; read term.ops for the operator sequence and term.ne for the scoped index constraints.
See also QTerm, prefactor, operators, Σ, constraint_pairs.
SecondQuantizedAlgebra.@qnumbers — Macro
@qnumbers ops...Convenience macro for constructing named quantum operators.
Each argument has the form name::OperatorType(hilbert_space, args...). The macro calls OperatorType(hilbert_space, :name, args...) and binds the result to name in the calling scope. Multiple operators can be declared in one call.
Examples
julia> h = FockSpace(:fock);
julia> @qnumbers a::Destroy(h)
(a,)See also Destroy, Transition, Pauli, Spin.
SecondQuantizedAlgebra.Destroy — Function
Destroy(h::FockSpace, name::Symbol) -> OpBosonic annihilation operator $a$ on a FockSpace. Satisfies the canonical commutation relation $[a, a^\dagger] = 1$. The adjoint a' returns the corresponding Create operator. Returns an Op tagged OP_DESTROY.
Examples
julia> h = FockSpace(:cavity);
julia> @qnumbers a::Destroy(h);
julia> a * a'
1 + a' * aSecondQuantizedAlgebra.Transition — Function
Transition(h::NLevelSpace, name::Symbol, i, j) -> OpTransition operator $|i\rangle\langle j|$ on an NLevelSpace. Satisfies the composition rule $|i\rangle\langle j| \cdot |k\rangle\langle l| = \delta_{jk} |i\rangle\langle l|$, with adjoint $|i\rangle\langle j|^\dagger = |j\rangle\langle i|$. Returns an Op tagged OP_TRANSITION, packing i, j, the ground state, and the number of levels into l1, l2, g, nlev.
The carried ground-state level and level count let the eager arithmetic keep $\sigma^{gg}$ atomic in canonical form (use expand_completeness to materialise $|g\rangle\langle g| = 1 - \sum_{k \neq g}|k\rangle\langle k|$ when needed).
Examples
julia> h = NLevelSpace(:atom, 3);
julia> σ = Transition(h, :σ, 1, 2);
julia> σ * σ'
σ₁₁
julia> σ' * σ
σ₂₂See also NLevelSpace, expand_completeness, @qnumbers.
SecondQuantizedAlgebra.Pauli — Function
Pauli(h::PauliSpace, name::Symbol, axis::Int) -> OpPauli operator $\sigma_x, \sigma_y, \sigma_z$ on a PauliSpace. The axis argument selects the component: 1 = x, 2 = y, 3 = z. Hermitian (σ' == σ) and satisfies $\sigma_j \sigma_k = \delta_{jk} I + i \epsilon_{jkl} \sigma_l$. Returns an Op tagged OP_PAULI with the axis stored in l1.
Examples
julia> h = PauliSpace(:s);
julia> σx = Pauli(h, :σ, 1); σy = Pauli(h, :σ, 2);
julia> σx * σy
im * σz
julia> σx * σx
1See also PauliSpace, Spin.
SecondQuantizedAlgebra.Spin — Function
Spin(h::SpinSpace, name::Symbol, axis::Int) -> OpAngular momentum operator $S_x, S_y, S_z$ on a SpinSpace. The axis argument selects the component: 1 = x, 2 = y, 3 = z. Hermitian (S' == S) and satisfies $[S_j, S_k] = i \epsilon_{jkl} S_l$ (applied eagerly by *). Returns an Op tagged OP_SPIN with the axis stored in l1.
Examples
julia> h = SpinSpace(:S);
julia> Sx = Spin(h, :S, 1); Sy = Spin(h, :S, 2);
julia> Sy * Sx
-im * Sz + Sx * SySecondQuantizedAlgebra.Position — Function
Position(h::PhaseSpace, name::Symbol) -> OpPosition (quadrature) operator $x$ on a PhaseSpace. Hermitian (x' == x) and related to Fock operators by $x = (a + a^\dagger)/\sqrt{2}$. Canonical pair with Momentum: $[p, x] = -i$. Returns an Op tagged OP_POSITION.
Examples
julia> h = PhaseSpace(:osc);
julia> @qnumbers x::Position(h) p::Momentum(h);
julia> p * x
-im + x * pSee also Momentum, PhaseSpace.
SecondQuantizedAlgebra.Momentum — Function
Momentum(h::PhaseSpace, name::Symbol) -> OpMomentum (quadrature) operator $p$ on a PhaseSpace. Hermitian (p' == p) and related to Fock operators by $p = i(a^\dagger - a)/\sqrt{2}$. Canonical pair with Position: $[p, x] = -i$. Returns an Op tagged OP_MOMENTUM.
See also Position, PhaseSpace.
SecondQuantizedAlgebra.Op — Type
Op <: QSymThe single concrete leaf operator. A kind::OpKind tag selects the physical role (annihilation, creation, transition, Pauli, spin, position, momentum); the remaining fields are shared storage interpreted per kind:
name::Symbol: display namespace_index::Int: position in aProductSpaceindex::Index: symbolic site index, orNO_INDEXl1, l2, g, nlev::Int32: packed level/axis data. Transition uses all four (i,j, ground state, number of levels); Pauli/Spin store the axis inl1; Fock/PhaseSpace leave them zero.
Construct via the role-named functions Destroy, Create, Transition, Pauli, Spin, Position, Momentum; test the role via is_destroy and siblings, or read it with optype. Collapsing the former per-type hierarchy into one concrete struct makes the operator vector concrete-eltype, so the per-operator hooks dispatch statically. See docs/src/devdocs.md.
SecondQuantizedAlgebra.OpKind — Type
OpKindRuntime tag distinguishing the seven operator roles carried by the single concrete leaf type Op. The integer values double as the cross-family sort order (see _type_order/order_key).
SecondQuantizedAlgebra.operator_name — Function
operator_name(op::Op) -> SymbolThe display name of op (recovered from the intern table).
SecondQuantizedAlgebra.optype — Function
optype(o::Op) -> OpKindReturn the OpKind tag of o.
SecondQuantizedAlgebra.is_destroy — Function
is_destroy(o::Op) -> BoolTrue iff o is a Destroy (annihilation) operator. One of the role predicates that replace isa on the collapsed Op type; see also is_create, is_transition, is_pauli, is_spin, is_position, is_momentum, optype.
SecondQuantizedAlgebra.is_create — Function
is_create(o::Op) -> BoolTrue iff o is a Create (creation) operator. See is_destroy.
SecondQuantizedAlgebra.is_transition — Function
is_transition(o::Op) -> BoolTrue iff o is a Transition operator. See is_destroy.
SecondQuantizedAlgebra.is_pauli — Function
is_pauli(o::Op) -> BoolTrue iff o is a Pauli operator. See is_destroy.
SecondQuantizedAlgebra.is_spin — Function
is_spin(o::Op) -> BoolTrue iff o is a Spin operator. See is_destroy.
SecondQuantizedAlgebra.is_position — Function
is_position(o::Op) -> BoolTrue iff o is a Position operator. See is_destroy.
SecondQuantizedAlgebra.is_momentum — Function
is_momentum(o::Op) -> BoolTrue iff o is a Momentum operator. See is_destroy.
Algebra
SymbolicUtils.simplify — Function
simplify(expr::QField) -> QAddNormal-order expr, then simplify each coefficient symbolically and drop summation indices that no surviving term depends on.
The operator-level work (commutation, same-site composition, like-term collection) all happens inside normal_order. What simplify adds on top is purely at the symbolic-coefficient layer: Symbolics.expand followed by SymbolicUtils.simplify runs on each surviving prefactor, and any term whose coefficient simplifies to zero is dropped. A final pass removes summation indices that no remaining term references.
That symbolic step is expensive, so reach for simplify as a finalizer when cancellations or accumulated symbolic factors need to be folded; use normal_order for intermediate steps.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> @variables x y;
julia> expr = (x^2 + 2x*y + y^2) * a' * a - (x + y)^2 * a' * a;
julia> simplify(expr)
0See also normal_order, expand, expand_completeness.
SecondQuantizedAlgebra.normal_order — Function
normal_order(expr::QField) -> QAddRoute every term of expr through the canonicalization pipeline.
In practice this is the identity on anything built through public arithmetic: *, +, -, ^, commutator, Σ, substitute, and adjoint all canonicalize eagerly, so the result of any such call is already normal-ordered. Reach for normal_order explicitly only when an expression was assembled through low-level internals that bypass the arithmetic, or when interfacing with code that expects a finalizer call. simplify uses it internally before simplifying coefficients.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> a * a'
1 + a' * a
julia> normal_order(a * a')
1 + a' * aSee also simplify, expand_completeness, normal_to_symmetric, symmetric_to_normal.
SecondQuantizedAlgebra.normal_to_symmetric — Function
normal_to_symmetric(expr) -> QAddConvert expr from normal to symmetric (Weyl) ordering. Exact inverse of symmetric_to_normal.
Examples
julia> hf = FockSpace(:f);
julia> @qnumbers a::Destroy(hf);
julia> normal_to_symmetric(a' * a)
-0.5 + a' * a
julia> hp = PhaseSpace(:osc);
julia> @qnumbers x::Position(hp) p::Momentum(hp);
julia> normal_to_symmetric(x * p)
0.5im + x * pSee also symmetric_to_normal, normal_order.
SecondQuantizedAlgebra.symmetric_to_normal — Function
symmetric_to_normal(expr) -> QAddConvert expr from symmetric (Weyl) to normal ordering. Exact inverse of normal_to_symmetric.
Examples
julia> hf = FockSpace(:f);
julia> @qnumbers a::Destroy(hf);
julia> symmetric_to_normal(normal_to_symmetric(a' * a))
a' * a
julia> hp = PhaseSpace(:osc);
julia> @qnumbers x::Position(hp) p::Momentum(hp);
julia> symmetric_to_normal(normal_to_symmetric(x * p))
x * pSee also normal_to_symmetric, normal_order.
SecondQuantizedAlgebra.commutator — Function
commutator(a, b) -> QAddReturn the commutator $[a, b] = a\,b - b\,a$ as a QAdd.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> commutator(a, a')
1See also anticommutator, normal_order.
SecondQuantizedAlgebra.anticommutator — Function
anticommutator(a, b) -> QAddReturn the anticommutator $\{a, b\} = a\,b + b\,a$ as a QAdd.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> anticommutator(a, a')
1 + 2 * a' * aSee also commutator.
SymbolicUtils.expand — Function
expand(expr::QField) -> QAddExpand the symbolic prefactor of each term via Symbolics.expand.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> @variables x y;
julia> expand((x + y)^2 * a)
(x^2 + 2x*y + y^2) * aSee also simplify.
SecondQuantizedAlgebra.expand_completeness — Function
expand_completeness(q) -> QAddRewrite every ground-state projector $\sigma^{gg}$ in q via the completeness relation $\sigma^{gg} = 1 - \sum_{k \neq g} \sigma^{kk}$.
* keeps $\sigma^{gg}$ atomic; reach for expand_completeness when downstream code needs the projector eliminated, e.g. before converting to a numeric basis where the $\sigma^{kk}$ for $k \neq g$ form the independent degrees of freedom.
Examples
julia> h = NLevelSpace(:atom, 2);
julia> σ11 = Transition(h, :σ, 1, 1);
julia> expand_completeness(σ11)
1 - σ₂₂See also assume_distinct_index, normal_order.
SecondQuantizedAlgebra.assume_distinct_index — Function
assume_distinct_index(q::QAdd, pairs::Vector{Tuple{Index, Index}}) -> QAddRe-canonicalize q under the declared pairwise ≠ constraints on free indices, then run expand_completeness so any ground-state projectors that emerge from same-site composition are expanded.
Use this when two free indices semantically range over distinct atoms or modes but no Σ supplies the constraint. SQA cannot infer "different symbol implies different site" automatically: two operators carrying different free indices on the same Hilbert subspace remain in their physical order, and no same-site collapse fires between them. Declaring the pair distinct here lets the canonical sort resolve their relationship and triggers any composition or completeness rewriting it unlocks.
Examples
julia> h = NLevelSpace(:atom, 2);
julia> j = Index(h, :j, 5, h); k = Index(h, :k, 5, h);
julia> σ(i, m, idx) = IndexedOperator(Transition(h, :σ, i, m), idx);
julia> q = σ(2, 1, k) * σ(1, 2, j);
julia> assume_distinct_index(q, [(j, k)])
σ_j₁₂ * σ_k₂₁See also expand_completeness.
SecondQuantizedAlgebra.qadjoint — Function
qadjoint(x)Hermitian conjugate that distributes through mixed operator/symbolic expressions.
On a QField returns adjoint(x). On a Number returns adjoint(x). On a SymbolicUtils.BasicSymbolic tree, recurses into arguments so coefficients distribute (e.g. qadjoint(2im * a) becomes -2im * a') rather than producing an opaque conj(...) wrapper.
Aliased as qconj and dagger.
SecondQuantizedAlgebra.inner_adjoint — Function
inner_adjoint(x)Push the adjoint inside ⟨...⟩ averages: rewrites conj(⟨X⟩) as ⟨X†⟩ so the result stays expressed as an average of an operator rather than a conj wrapper around one. Used when building equations of motion where both sides must share the canonical "average-of-operator" form for substitution and hashing.
On non-average sub-expressions, behaves like qadjoint.
Average
SecondQuantizedAlgebra.average — Function
average(expr) -> BasicSymbolic | NumberBuild the symbolic average $\langle \mathrm{expr} \rangle$. Distributes over sums, pulls c-number prefactors out, leaves scalars unchanged. Displayed as ⟨…⟩.
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> avg = average(a' * a);
julia> is_average(avg)
true
julia> avg + 1
1 + ⟨a' * a⟩See also undo_average, numeric_average.
SecondQuantizedAlgebra.undo_average — Function
undo_average(expr) -> QAddRecursively strip symbolic averages and return the underlying operator expression. Summation metadata is restored. Also accepts a Symbolics.Equation, returning a Pair{QAdd, QAdd}.
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> undo_average(average(a' * a)) == a' * a
trueSecondQuantizedAlgebra.make_time_dependent — Function
make_time_dependent(expr, iv) -> exprLift every iv-free average leaf in expr into a time-dependent Number variable name(iv) carrying the operator in AverageOperator metadata (and the VariableSource set by @variables, so it reads as a ModelingToolkit unknown). Non-average structure is rebuilt only where a child changed. The lifted node is a leaf: the walk does not descend into iv. The default name is uniqueness-only; downstream code may rename for display without changing identity.
SecondQuantizedAlgebra.is_average — Function
is_average(x) -> BoolWhether x is a symbolic average object created by average.
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> is_average(average(a)), is_average(a)
(true, false)SecondQuantizedAlgebra.is_indexed_sum — Function
is_indexed_sum(x) -> BoolWhether x is a moment-layer indexed-sum node created by average on a summed QAdd. The summation scope is recovered with get_sum_indices and get_sum_non_equal.
Utility Functions
SecondQuantizedAlgebra.acts_on — Function
acts_on(expr) -> Vector{Int}Sorted unique space_index values that expr acts on. Works on QSym, QAdd, averaged BasicSymbolic expressions, and Numbers (Int[]).
julia> h = FockSpace(:a) ⊗ NLevelSpace(:b, 2);
julia> @qnumbers a::Destroy(h, 1) σ::Transition(h, 1, 2, 2);
julia> acts_on(a' * a), acts_on(a' * σ)
([1], [1, 2])SecondQuantizedAlgebra.find_operators — Function
find_operators(h::HilbertSpace, order::Int; names=nothing) -> VectorGenerate all unique operator products up to order factors for Hilbert space h.
Starts from fundamental_operators(h) and their adjoints, forms all products with up to order factors, then filters out zero terms and adjoint-duplicates. Useful for constructing the operator basis needed for cumulant expansions or moment equations. Pass names to override auto-generated operator names.
Examples
julia> h = FockSpace(:f);
julia> length(find_operators(h, 1))
1See also fundamental_operators, unique_ops.
SecondQuantizedAlgebra.unique_ops — Function
unique_ops(ops) -> VectorReturn unique operators from ops, treating op and op' (adjoint) as the same degree of freedom. Only the first occurrence of each operator/adjoint pair is kept.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> length(unique_ops([a, a', a]))
1See also unique_ops!, fundamental_operators.
SecondQuantizedAlgebra.unique_ops! — Function
unique_ops!(ops) -> VectorIn-place version of unique_ops.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> v = [a, a'];
julia> SecondQuantizedAlgebra.unique_ops!(v);
julia> length(v)
1SecondQuantizedAlgebra.fundamental_operators — Function
fundamental_operators(h::HilbertSpace; names=nothing) -> Vector{Op}Return the minimal generating set of operators for Hilbert space h.
Returns one Destroy per FockSpace; n(n+1)/2 - 1 Transition operators per NLevelSpace (upper-triangular plus diagonals, excluding the ground-state projector); three Pauli or Spin operators per PauliSpace/SpinSpace; one Position and one Momentum per PhaseSpace; and the concatenation of the above for a ProductSpace. Pass names to override the auto-generated operator names.
Examples
julia> h = FockSpace(:cavity) ⊗ NLevelSpace(:atom, 2);
julia> length(fundamental_operators(h))
3See also find_operators, unique_ops.
SecondQuantizedAlgebra.prefactor — Function
prefactor(s::QAdd) -> Complex{Num}Return the Complex{Num} prefactor of a single-term QAdd.
Throws ArgumentError if s contains more than one term. For multi-term expressions, iterate over the QAdd directly.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> prefactor(2 * a' * a)
2See also operators, sorted_arguments.
SecondQuantizedAlgebra.operators — Function
operators(s::QAdd) -> Vector{Op}Return the ordered operator sequence of a single-term QAdd.
Throws ArgumentError if s contains more than one term. For multi-term expressions, iterate over the QAdd directly and read term.ops from each QTerm.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> length(operators(a' * a))
2See also prefactor, sorted_arguments.
SecondQuantizedAlgebra.constraint_pairs — Function
constraint_pairs(q::QAdd) -> Vector{Tuple{Index, Index}}Return the deduplicated union of every term's non_equal pairs in q. This is an introspection helper only; it does not define the expression semantics.
Examples
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h); j = Index(h, :j, 3, h);
julia> q = Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i, [j]);
julia> length(SecondQuantizedAlgebra.constraint_pairs(q))
1SecondQuantizedAlgebra.sorted_arguments — Function
sorted_arguments(q::QAdd) -> Vector{QAdd}Return each term of q as a single-entry QAdd, in deterministic sort order.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> length(SecondQuantizedAlgebra.sorted_arguments(a + a'))
2SecondQuantizedAlgebra.order_key — Function
order_key(op::Op) -> TupleTotal, identity-faithful ordering key: a comparable tuple that orders operators reproducibly and ties two exactly when they are isequal. The leading kind value gives the cross-family order; the packed l1..nlev fields keep distinct levels/axes distinct.
SecondQuantizedAlgebra.term_order_key — Function
term_order_key(t::QTerm) -> TupleTotal ordering key for an operator product, built from order_key: length, then operator-by-operator, then the canonical-sorted non-equal constraints.
SecondQuantizedAlgebra.qadd_order_key — Function
qadd_order_key(q::QAdd) -> VectorTotal, reproducible ordering key for a sum: its term/coefficient pairs in sorted order, so two QAdds compare with < on their keys and tie exactly when they are isequal. The coefficient contributes its printed form (a reproducible tiebreak, not a numeric order).
SymbolicUtils.substitute — Function
substitute(expr, d::Dict)Substitute symbolic parameters and/or operators in expr using dictionary d.
Supports both symbolic coefficient replacement (for example x => 2) and operator replacement. The result is re-canonicalized and returned as a QAdd.
Examples
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> @variables x;
julia> substitute(x * a' * a, Dict(x => 2))
2 * a' * aSee also change_index.
SecondQuantizedAlgebra.transition_superscript — Function
transition_superscript(x::Bool) -> BoolSet whether Transition level indices are rendered as superscripts (true, default) or subscripts (false) in LaTeX output via Latexify.jl.
true: ${\sigma}^{{ij}}$false: ${\sigma}_{{ij}}$
Examples
julia> SecondQuantizedAlgebra.transition_superscript(false)
false
julia> SecondQuantizedAlgebra.transition_superscript(true)
trueSecondQuantizedAlgebra.to_numeric — Function
to_numeric(op, basis [, d::AbstractDict{<:QSym}])
to_numeric(op, state [, d::AbstractDict{<:QSym}])Convert a symbolic operator expression to a numeric QuantumOpticsBase operator. d substitutes individual QSyms with custom numeric operators. Throws ArgumentError if any prefactor cannot be reduced to a concrete ComplexF64.
Examples
julia> using QuantumOpticsBase
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> b = FockBasis(5); ψ = fockstate(b, 2);
julia> real(QuantumOpticsBase.expect(to_numeric(a' * a, ψ), ψ)) ≈ 2
trueSee also numeric_average.
SecondQuantizedAlgebra.numeric_average — Function
numeric_average(op, state[, d::AbstractDict{<:QSym}]) -> ComplexF64
numeric_average(op, states::AbstractVector[, d::AbstractDict{<:QSym}]) -> VectorExpectation value $\langle \psi | \hat{O} | \psi \rangle$ of a symbolic operator expression. Averaged BasicSymbolic expressions (from average) are unwrapped automatically. d substitutes symbolic operators with custom numeric operators before evaluation.
Examples
julia> using QuantumOpticsBase
julia> h = FockSpace(:f);
julia> @qnumbers a::Destroy(h);
julia> b = FockBasis(5); ψ = fockstate(b, 2);
julia> real(numeric_average(a' * a, ψ)) ≈ 2
trueSee also to_numeric, average.
Symbolic Summations
SecondQuantizedAlgebra.Index — Type
Index(h::HilbertSpace, name::Symbol, range, space)
Index(h::HilbertSpace, name::Symbol, range, space_index::Int)Symbolic summation index for site- or mode-resolved operator expressions.
Represents labels such as i, j, or k that attach to operators and parameters. Pass a subspace type or integer position to specify which factor of a ProductSpace the index ranges over. Use with IndexedOperator to build objects like $a_i$ and with Σ to build sums $\sum_i a_i^\dagger a_i$.
Examples
julia> h = FockSpace(:site) ⊗ FockSpace(:cavity);
julia> i = Index(h, :i, 10, FockSpace(:site));
julia> j = Index(h, :j, 10, 1); # same subspace via integer position
julia> i == j
falseSee also has_index, IndexedOperator, Σ, change_index.
SecondQuantizedAlgebra.IndexedOperator — Function
IndexedOperator(op::Op, i::Index) -> OpReturn the indexed version of an operator.
IndexedOperator keeps the operator kind and quantum numbers, and only changes the symbolic index label. Use it to build objects such as a_i, σ_j₁₂, or x_k before forming sums with Σ.
Use IndexedOperator(op, NO_INDEX) to remove an index.
Examples
julia> h = FockSpace(:cavity);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 5, h);
julia> IndexedOperator(a, i)
a_iSee also Index, Σ, change_index.
SecondQuantizedAlgebra.IndexedVariable — Function
IndexedVariable(name::Symbol, i::Index) -> NumCreate a symbolic single-indexed parameter $\mathrm{name}(i)$.
Use this for site-dependent quantities in Hamiltonians, for example detunings or local couplings.
Examples
julia> h = FockSpace(:site) ⊗ FockSpace(:cavity);
julia> i = Index(h, :i, 10, 1);
julia> ω = IndexedVariable(:ω, i)
ω(i)See also DoubleIndexedVariable.
SecondQuantizedAlgebra.DoubleIndexedVariable — Function
DoubleIndexedVariable(name::Symbol, i::Index, j::Index; identical::Bool=true) -> NumCreate a symbolic two-index parameter $\mathrm{name}(i, j)$.
Use this for pairwise interactions such as hopping amplitudes or coupling matrices.
Keyword arguments
identical::Bool = true— iffalse, the variable evaluates to zero wheni == j, enforcing that the parameter is only defined for distinct sites.
Examples
julia> h = FockSpace(:site) ⊗ FockSpace(:cavity);
julia> i = Index(h, :i, 10, 1);
julia> j = Index(h, :j, 10, 1);
julia> J = DoubleIndexedVariable(:J, i, j; identical = false)
J(i, j)See also IndexedVariable.
SecondQuantizedAlgebra.Σ — Function
Σ(expr, i::Index, non_equal::Vector{Index} = Index[])
Σ(expr, i::Index, j::Index, rest::Index...)
∑(expr, i::Index, ...)Build symbolic sums over indices, for example $\sum_i expr$.
Returns a QAdd carrying i in its indices field. If expr does not depend on i, the sum is evaluated eagerly as i.range * expr.
The optional non_equal records pairwise inequality constraints i ≠ j per term. Diagonal splitting is performed automatically: if expr carries another free index j on the same Hilbert subspace as i, the contribution at i = j is emitted as a separate diagonal term and the off-diagonal term gains the constraint (i, j).
Multiple positional indices create nested sums: Σ(expr, i, j) is equivalent to Σ(Σ(expr, i), j). The Unicode alias ∑ is also exported.
Examples
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h);
julia> Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i)
Σ(i=1:3) a_i' * a_iSee also Index, IndexedOperator, constraint_pairs.
SecondQuantizedAlgebra.∑ — Function
Σ(expr, i::Index, non_equal::Vector{Index} = Index[])
Σ(expr, i::Index, j::Index, rest::Index...)
∑(expr, i::Index, ...)Build symbolic sums over indices, for example $\sum_i expr$.
Returns a QAdd carrying i in its indices field. If expr does not depend on i, the sum is evaluated eagerly as i.range * expr.
The optional non_equal records pairwise inequality constraints i ≠ j per term. Diagonal splitting is performed automatically: if expr carries another free index j on the same Hilbert subspace as i, the contribution at i = j is emitted as a separate diagonal term and the off-diagonal term gains the constraint (i, j).
Multiple positional indices create nested sums: Σ(expr, i, j) is equivalent to Σ(Σ(expr, i), j). The Unicode alias ∑ is also exported.
Examples
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h);
julia> Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i)
Σ(i=1:3) a_i' * a_iSee also Index, IndexedOperator, constraint_pairs.
SecondQuantizedAlgebra.change_index — Function
change_index(expr, from::Index, to::Index)Rename an index in an expression by replacing from with to everywhere.
Operator indices are swapped directly. Symbolic prefactors (e.g. IndexedVariable, DoubleIndexedVariable) are substituted via Symbolics.substitute using the sym fields of the indices. DoubleIndexedVariable nodes with identical=false automatically evaluate to zero if the substitution makes both arguments equal.
Examples
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 5, h); j = Index(h, :j, 5, h);
julia> expr = IndexedVariable(:ω, i) * IndexedOperator(a, i);
julia> change_index(expr, i, j)
ω(j) * a_jSee also get_indices.
change_index(expr, pairs::AbstractDict{Index, Index})Simultaneous (batched) variant of change_index: substitute every key in pairs by its mapped value in one pass.
Unlike a sequence of single-pair change_index calls, this never produces an intermediate state where a renamed-into index temporarily collides with another encountered index. Use it when the rename describes a permutation or any map whose image overlaps its domain, e.g. swapping two indices:
julia> change_index(expr, Dict(i => j, j => i)) # swap, not fuseA pair (idx => idx) is a no-op; change_index(expr, Dict()) returns expr unchanged.
SecondQuantizedAlgebra.get_indices — Function
get_indices(expr) -> Vector{Index}Return all symbolic indices that appear in expr.
Returns a Vector{Index} of unique indices found in operator fields and summation metadata. Excludes the sentinel NO_INDEX.
Examples
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 5, h);
julia> get_indices(IndexedOperator(a, i))
1-element Vector{Index}:
iSecondQuantizedAlgebra.has_index — Function
has_index(idx::Index) -> BoolReturn true when idx is an actual symbolic index.
Returns false for the sentinel NO_INDEX, used for operators without an attached index.
SecondQuantizedAlgebra.index_slot — Function
index_slot(x) -> Union{Int, Nothing}
index_slot(idx::Index) -> Union{Int, Nothing}Concrete slot integer of a per-slot index, or nothing for an abstract index. On an Index reads the slot field directly; on a symbol reads the IndexSlot metadata minted by (i::Index)(k), so consumers can recover the position k without parsing the symbol's name.
SecondQuantizedAlgebra.index_name — Function
index_name(idx::Index) -> SymbolThe display name of idx (recovered from the intern table).
SecondQuantizedAlgebra.index_range — Function
index_range(idx::Index) -> NumThe summation range of idx (the user's Num, recovered from the intern table). Returns Num(0) for the sentinel NO_INDEX.
SecondQuantizedAlgebra.index_sym — Function
index_sym(idx::Index) -> NumThe symbolic variable for idx, reconstructed from its interned name (plus the IndexSlot metadata for a per-slot index). SymbolicUtils hashconsing makes the reconstruction identical (===) to the originally minted symbol, so substitution and get_variables are unaffected. An anonymous concrete site (name_id == 0, slot == k) reconstructs to the integer Num(k), matching to_numeric's resolved-site convention.
SecondQuantizedAlgebra.has_sum_metadata — Function
has_sum_metadata(x) -> BoolWhether x is a moment-layer indexed-sum node (see is_indexed_sum) created by average on a summed expression. Retained as the public predicate name; equivalent to is_indexed_sum(x).
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h);
julia> x = average(Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i));
julia> SecondQuantizedAlgebra.has_sum_metadata(x)
trueSee also get_sum_indices, get_sum_non_equal.
SecondQuantizedAlgebra.get_sum_indices — Function
get_sum_indices(x::BasicSymbolic) -> Vector{Index}Summation indices carried by the indexed-sum node x. Only valid when has_sum_metadata(x) is true.
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h);
julia> x = average(Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i));
julia> SecondQuantizedAlgebra.get_sum_indices(x) == [i]
trueSee also get_sum_non_equal, has_sum_metadata.
SecondQuantizedAlgebra.get_sum_non_equal — Function
get_sum_non_equal(x::BasicSymbolic) -> Vector{Tuple{Index, Index}}Pairwise index-inequality constraints carried by the indexed-sum node x. An empty vector means no constraints. Only valid when has_sum_metadata(x) is true.
julia> h = FockSpace(:site);
julia> @qnumbers a::Destroy(h);
julia> i = Index(h, :i, 3, h);
julia> x = average(Σ(IndexedOperator(a', i) * IndexedOperator(a, i), i));
julia> isempty(SecondQuantizedAlgebra.get_sum_non_equal(x))
trueSee also get_sum_indices, has_sum_metadata.