Expression Simplification¶
The simplify() function applies a multi-pass
pipeline to clean up the raw output of Wick contractions. It is called
automatically inside compute_moment(), but
you can also call it directly on any expression.
from sft_wick import simplify
simplified = simplify(raw_expr)
The Pipeline¶
simplify() applies four passes in sequence:
Pass 1 — Flatten¶
Recursively flattens nested Sum and
Product structures:
Sum(Sum(a, b), c)becomesSum(a, b, c)Product(Product(a, b), c)becomesProduct(a, b, c)
Also descends into the bodies of
IntegralOver and
SumOverIndex.
Pass 2 — Absorb Rationals¶
In any Product, multiplies all Rational
factors into a single coefficient:
Product(Rational(1,2), Rational(3,1), C)becomesProduct(Rational(3,2), C)If the total coefficient is 0, the whole product becomes
ZERO.If the total coefficient is 1 and only one other factor remains, the product is unwrapped.
Pass 3 — Eliminate Zeros¶
Removes zero terms from sums
Collapses any product containing a zero factor to
ZERORemoves
Rational(1)factors from productsCollapses wrappers whose body is zero
Pass 4 — Collect Like Terms¶
Groups sum terms by their structural signature (the non-coefficient part) and sums the rational coefficients within each group:
For example:
2 C(x,y) R(x,z) + 3 C(x,y) R(x,z)becomes5 C(x,y) R(x,z)
The signature includes propagator kinds, indices, spatial arguments, symbol names, integral variables, and summation indices — all in canonical order. Terms that cannot be matched are left unchanged.
Term Signatures¶
The internal function _term_signature() extracts a hashable
signature from each term for grouping. It handles:
Bare propagators
Products of rationals, propagators, symbols, integrals, and summations
Products containing nested sums or other complex sub-expressions are marked as “ungroupable” and passed through without merging.
Diagram-Based Collection¶
collect_by_diagram() is a separate pass
(not part of simplify()) that groups terms by Feynman diagram
isomorphism. Two terms are equivalent if their propagator graphs
are isomorphic under relabeling of dummy integration variables and
summation indices.
The algorithm computes a canonical graph form for each term by trying all permutations of internal spatial variables (integration variables), using sorted C-edge endpoints to account for the symmetry \(C(x, y) = C(y, x)\). Terms with the same canonical form are merged: the propagators are factored out with canonical component indices, and the coupling coefficients are summed with indices (and spatial arguments for non-local couplings) permuted to match.
This handles three cases that matter in practice:
Within-vertex index routing: different Wick pairings that share the same spatial topology but differ in component-index assignments are collected, producing sums like \((F_{i_0 i_1 i_2} + F_{i_0 i_2 i_1})\).
Cross-vertex spatial relabeling: at order \(n \ge 2\) with the same vertex type, swapping vertex-copy spatial variables (e.g. \(y_0 \leftrightarrow y_1\)) is a valid relabeling that merges additional equivalent pairings.
C propagator symmetry: pairings that differ only by the orientation of a correlation propagator (\(C(a, b)\) vs \(C(b, a)\)) are identified.
from sft_wick import collect_by_diagram
collected = collect_by_diagram(simplified_expr)
# Backward-compatible alias:
from sft_wick import collect_by_topology # same function
When Simplification Runs¶
compute_moment() applies the following
pipeline to each order expression:
Wick contraction — when
collect_topology=True(default), uses the spatial-level engine which enumerates spatial topologies with multiplicities, then groups by canonical diagram form. Whencollect_topology=False, uses the operator-level engine followed bycollect_by_diagram.simplify — flatten, absorb rationals, eliminate zeros, collect like terms
apply_response_phase (
response_phase=True) — multiply by \((-\mathrm{i})^n\)
You do not normally need to call these yourself, but they are all exposed in the public API for manual use.