alkahest
Python API

Noncommutative algebra API

Expressions over noncommutative symbols (pool.symbol(name, commutative=False)) read as noncommutative polynomials, with word-rewriting normal forms. See the Noncommutative algebra chapter of the user guide.

function
nc_expand(expr)

nc_expand(expr) -> Expr

Expand expr as a polynomial in noncommutative symbols: products distribute keeping factor order, non-negative integer powers of noncommutative factors unroll, equal words collect and their commutative coefficients are simplified.

Examples
Python
>>> sx, sy = (p.symbol(n, commutative=False) for n in ("sx", "sy"))
>>> nc_expand((sx + sy)**2)     # sx^2 + sx*sy + sy*sx + sy^2

Raises NcError E-NC-002 past the size caps.

function
nc_terms(expr)

nc_terms(expr) -> list[tuple[Expr, list[Expr]]]

The terms (coefficient, word) of expr read as a noncommutative polynomial (see nc_expand()): coefficient is commutative and word is the ordered list of noncommutative factors (empty for the scalar part).

function
nc_rewrite(expr, rules, *, max_steps=...)

nc_rewrite(expr, rules, *, max_steps=200000) -> Expr

Rewrite expr to a normal form with literal word rules made by make_rule(): every occurrence of a rule's left-hand word inside a longer word is replaced (make_rule(X*P, P*X + I*hbar) rewrites X*P*P), the result is re-expanded, and the process repeats until no rule applies.

Each left-hand side must be a single product of noncommutative factors with coefficient 1; symbols in these rules are literal (no wildcards).

Raises NcError: E-NC-003 for a malformed left-hand side, E-NC-001 when no normal form is reached within max_steps rewrites (non-terminating rules), E-NC-002 past the size caps.

function
commutator(a, b, rules=None)

commutator(a, b, rules=None) -> Expr

[a, b] = a*b - b*a, expanded as a noncommutative polynomial. With rules (from make_rule()) the result is rewritten to normal form with nc_rewrite(), e.g. commutator(X, P**2, [make_rule(X*P, P*X + I*hbar)])` gives `2*I*hbar*P.

function
anticommutator(a, b, rules=None)

anticommutator(a, b, rules=None) -> Expr

{a, b} = a*b + b*a, expanded; rules as for commutator().

function
normal_order(expr, bosons=None, fermions=None)

normal_order(expr, bosons=None, fermions=None) -> Expr

Normal-order expr with the canonical commutation relations: every creation operator moves left of every annihilation operator using [a, a†] = 1 for bosonic modes and {c, c†} = 1, c**2 = 0 for fermionic ones (Wick's theorem: the contractions appear as the extra terms). Operators of different modes are ordered by their position in the mode lists (bosons first), with a sign for each swap of two fermionic operators.

Parameters
expr

an expression in noncommutative symbols.

bosons

an (a, adag) pair or a list of them.

fermions

a (c, cdag) pair or a list of them.

Each operator must be a noncommutative symbol (commutative=False). Other noncommutative factors stay in place and block reordering.

Examples
Python
>>> a, ad = p.symbol("a", commutative=False), p.symbol("ad", commutative=False)
>>> normal_order(a * ad**3, bosons=(a, ad))     # ad^3*a + 3*ad^2

Raises NcError E-NC-004 for a malformed mode list.

function
vacuum_expectation(expr, bosons=None, fermions=None)

vacuum_expectation(expr, bosons=None, fermions=None) -> Expr

<0| expr |0>: the scalar part of normal_order() (expr). E.g. vacuum_expectation(a*a*ad*ad, bosons=(a, ad)) == 2.

Raises NcError E-NC-007 when expr contains a noncommutative factor that is not one of the listed ladder operators.

function
gamma_trace(slots, pool=None, *, dim=None, indices=None)

gamma_trace(slots, pool=None) -> Expr

Trace of a product of Dirac matrices in four dimensions, metric diag(+1, -1, -1, -1), {γ^μ, γ^ν} = 2 g^{μν}, tr 1 = 4, γ^5 = i γ^0 γ^1 γ^2 γ^3.

Parameters
slots

a list whose entries are 0–3 (γ^0 … γ^3), 5 (γ^5), or a commutative Expr p standing for the slashed vector p̸ = p_μ γ^μ (equally, a free Lorentz index).

pool

the pool for the result when slots holds no Expr.

Returns

A polynomial in dot(p, q) (p·q; dot(k, p) = p^k for an

explicit index k) and levi_civita(a, b, c, d) ``= ε^{μνρσ}

a_μ b_ν c_ρ d_σwithε^{0123} = +1, so tr(γ^5 a̸ b̸ c̸ d̸) =

-4i·levi_civita(a, b, c, d). With γ^5`` and more than four other

slots, γ^5 is expanded as iγ^0γ^1γ^2γ^3 and the answer uses

explicit components dot(k, p).

Examples
Python
>>> gamma_trace([p, q, k, l])   # 4*dot(p,q)*dot(k,l) - 4*dot(k,p)*dot(l,q) + ...
>>> gamma_trace([5, 0, 1, 2, 3], pool=pool)   # -4*I

With dim (an integer or a symbolic d) the trace is taken in dim dimensions (tr 1 = 4, as in FORM / FeynCalc): every slot is a commutative Expr label, indices lists the labels that are Lorentz indices, and an index occurring twice is summed with g^μ_μ = dim:

Python
gamma_trace([mu, mu], dim=d, indices=[mu])             # 4*d
gamma_trace([mu, nu, mu, nu], dim=d, indices=[mu, nu]) # 4*d*(2 - d)

An index occurring once stays free (dot(μ, ν) = g^{μν}); contract products of traces with lorentz_contract(). γ^5 and explicit components 0-3 are four-dimensional and are refused here.

Raises NcError: E-NC-005 for an invalid slot (or γ^5 / an explicit component with dim), E-NC-006 for more than 14 slots to expand.

function
lorentz_contract(expr, indices, dim)

lorentz_contract(expr, indices, dim) -> Expr

Contract the dummy Lorentz indices indices of a polynomial in dot(a, b) factors (dot(μ, μ) -> d; dot(μ, a)*dot(μ, b) -> dot(a, b)). An index occurring once is free and kept. Use it on products of traces, e.g. tr(γ^μγ^νγ^ργ^σ) tr(γ_μγ_νγ_ργ_σ) = 16 d (3d - 2):

Python
t = gamma_trace([m, n, r, s], dim=d)          # free indices
lorentz_contract(t * t, [m, n, r, s], d)       # 48*d**2 - 32*d  (up to form)

Raises NcError E-NC-005 for an index occurring more than twice or inside a factor that is not a dot (e.g. levi_civita).

function
gamma_contract(slots, indices, dim)

gamma_contract(slots, indices, dim) -> list[tuple[Expr, list[Expr]]]

Contract the dummy Lorentz indices of a product of gamma matrices in dim dimensions using only {γ^μ, γ^ν} = 2 g^{μν}. slots are commutative labels; a label listed in indices that occurs twice is a summed index. Returns terms (coefficient, labels) meaning coefficient * γ(labels[0]) γ(labels[1]) ..., with no index repeated:

  • γ^μ γ_μ = d -> [(d, [])]
  • γ^μ γ^ν γ_μ = (2 - d) γ^ν
  • γ^μ γ^ν γ^ρ γ_μ = 2 γ^ρ γ^ν + (d - 2) γ^ν γ^ρ (= 4 g^{νρ} - (4-d) γ^ν γ^ρ)

Raises NcError E-NC-005 for non-commutative labels or an index repeated more than twice, E-NC-006 past 14 slots.

function
lie_algebra(
    generators,
    structure_constants,
    *,
    check_jacobi=True
)

lie_algebra(generators, structure_constants, *, check_jacobi=True) -> LieAlgebra

A Lie algebra on noncommutative generators with [T_a, T_b] = i Σ_c f_{abc} T_c. structure_constants maps 1-based triples (a, b, c) to f_{abc} (Expr or int); an entry with a > b means f_{abc} = -f_{bac} and both orders may be given if they agree; absent entries are zero. The returned LieAlgebra reduces commutators of arbitrary noncommutative polynomials in the generators (commutator), normal-orders words (normal_order) and builds the Casimir.

The table is checked against the Jacobi identity (check_jacobi=False skips it). Raises NcError E-NC-008 for generators that are not distinct noncommutative symbols, bad or inconsistent entries, or a failed (or symbolically unverifiable) Jacobi identity.

Example:

Python
J = [p.symbol(f"J{i}", commutative=False) for i in (1, 2, 3)]
su2 = lie_algebra(J, {(1, 2, 3): 1, (2, 3, 1): 1, (3, 1, 2): 1})
su2.commutator(J[0], J[1])      # I*J3
function
lie_su2(pool, generators=None, *, prefix="J")

lie_su2(pool, generators=None, *, prefix="J") -> LieAlgebra

su(2): [J_a, J_b] = i ε_{abc} J_c (J = σ/2, spin matrices). With no generators it creates noncommutative symbols J1, J2, J3. The Casimir J1² + J2² + J3² commutes with every generator.

function
lie_su3(pool, generators=None, *, prefix="T")

lie_su3(pool, generators=None, *, prefix="T") -> LieAlgebra

su(3) in the Gell-Mann basis: [T_a, T_b] = i f_{abc} T_c with T = λ/2 (f_{123} = 1, f_{458} = f_{678} = √3/2, …; see su3_f()). With no generators it creates T1 … T8. The Casimir Σ T_a² equals 4/3 in the fundamental representation.

function
su3_f(pool)

su3_f(pool) -> dict[tuple[int, int, int], Expr]

The Gell-Mann structure constants f_{abc} (1-based, all nonzero entries in all six index orders): [λ_a/2, λ_b/2] = i f_{abc} λ_c/2.

function
su3_d(pool)

su3_d(pool) -> dict[tuple[int, int, int], Expr]

The Gell-Mann symmetric constants d_{abc} (1-based, all nonzero entries in every index order): {λ_a, λ_b} = (4/3) δ_{ab} + 2 d_{abc} λ_c.

make_rule(lhs, rhs, sequence=[X, ...]) declares noncommutative symbols of lhs to be sequence wildcards matching a contiguous, possibly empty run of factors (leftmost start, shortest run), usable through simplify_with; E-NC-009 for a malformed pattern.

simplify_pauli reduces any word over sx, sy, sz with the full Pauli algebra; make_rule / simplify_with match product patterns inside longer products (contiguous runs for noncommutative products, sub-multisets for commutative ones).

Refusals raise NcError (E-NC-001 … E-NC-009); see errors.

Lie algebras

class
class LieAlgebra

A Lie algebra given by [T_a, T_b] = i Σ_c f_{abc} T_c on noncommutative generators (see lie_algebra()).

Properties
generators

The generators, in table order.

rules

The ordering rules T_a T_b -> T_b T_a + [T_a, T_b] (a > b) as RewriteRule objects, usable with nc_rewrite().

Methods
commutator(a, b)

[a, b] reduced with the table: commutator(T_a, T_b) is i Σ f_abc T_c; for polynomials the result is in normal order (generators in increasing index order).

anticommutator(a, b)

{a, b} = a b + b a reduced with the table.

normal_order(expr)

Rewrite expr to normal order: generators in increasing table order (Poincaré–Birkhoff–Witt basis), commutator terms added.

casimir(inverse_metric=None)

The quadratic Casimir Σ g^{ab} T_a T_b. inverse_metric is an n × n nested list g^{ab} (ints or Expr); the default is the identity, i.e. Σ T_a², correct for the su(n) tables of this module (basis orthonormal under the Killing form).

verify_jacobi()

Check the Jacobi identity for every triple; raises NcError E-NC-008 if it fails or cannot be verified symbolically.