Landau Alphabet

The content on this page was written by AI under human supervision.

Landau Alphabet takes a Feynman graph (edges, internal masses, external kinematics) and returns the polynomials in the kinematic invariants whose zeros are where the corresponding integral can be singular. These Landau singularities are the "letters" a bootstrap calculation builds its candidate answer from; the package sorts them into the classes the bootstrap needs and states whether the list is proven complete. It also handles integrals with linear (worldline or eikonal) propagators and includes a Gröbner-basis backend for the hardest sub-graphs. A bridge runs the public package PLD.jl on the same input, with a numerical cross-check that works without PLD.jl, and a Julia library builds and fits function ansätze over such an alphabet. The method is the Landau bootstrap of arXiv:2410.02424.

What it does

A Feynman integral can have a branch point only where the Landau equations hold, that is, where the integration contour is trapped between singularities of the integrand. In Feynman parameters this is a polynomial condition. For every sub-graph, or facea subset of the graph's edges; the integral's singularities are the union of those associated with each subset, so the analysis runs over all of them, the Symanzik polynomialone of the two graph polynomials U and F of the Feynman-parameter integrand, fixed by the graph's spanning trees, masses and invariants $F$ and all its derivatives with respect to the parameters must vanish together; singularities "of the second type" come from $U$ in the same way. Eliminating the parameters leaves polynomials in the invariants alone, and their irreducible factors, collected over all faces, are the Landau letters. For a polylogarithmic integral this set is the symbol alphabetthe finite list of functions of the kinematics whose logarithms the answer is built from; knowing it in advance turns the calculation into finite linear algebra, and the output file is read directly by Ansatzer, which counts how many unknowns an ansatz over it leaves.

landau_alphabet.py reads a small JSON graph specification, enumerates the faces, builds $U$ and $F$ for each, and eliminates the parameters with a chain of resultants under a hard per-face time limit. All algebra is exact (sympy), and the output file is rewritten after every face, so an interrupted run keeps what it has. The output lists alphabet, first_entry, last_entry, spurious (dropped factors), one record per face, and an honesty block. First-entry letters come from codimension-one solutions of $F$ and the bare invariants: they are the physical thresholds, the only letters allowed in a symbol's first slot. Last-entry letters arise only from $U$. The top-level complete flag is true only if every face was attempted, none timed out or was resolved only partly, and the deadline was not reached; otherwise honesty.unresolved_faces names the sub-graphs that need a longer timeout or a heavier backend. audit_faces.py re-checks any output file against a face count it computes independently from the graph and exits with code 2 if the file claims a completeness it does not have.

Resultant chains grow uncontrollably on high-degree faces. When msolve or Singular is installed, a face that times out or comes back partial is handed to pld_groebner_backend.py, which removes solutions with a vanishing parameter (saturation), eliminates the parameters with a Gröbner basis (msolve, then Singular, then sympy) and factors what remains; the face status records which program succeeded. Independently, pld_bridge.jl runs PLD.jl (Fevola, Mizera, Telen) on the same specification and writes the same output format, so the two computations can be compared. When PLD.jl and Oscar cannot be loaded (or with --fallback), the bridge runs a numerical check that needs only HomotopyContinuation.jl: for each face it restricts the polynomial $U+F$ to a randomly seeded straight line through the space of invariants, solves the critical-point equations by homotopy continuation, and records the solutions as numerical witness points of the singular surfaces. With --check FILE.json every witness must lie on a candidate letter taken from that file (an output of landau_alphabet.py, or any JSON with an alphabet list); the bridge exits with code 2 if one does not. Witnesses are interior pinches, so they include second-type surfaces the pruned alphabet omits (on the massless box, $s+t$) and never include letters such as $s$ and $t$ that multiply a whole face polynomial. This mode confirms letters and cannot certify completeness, so it always writes complete: false.

linear_landau.py covers families in which some propagators are linear in the loop momenta, $2u\cdot k$, as in the post-Minkowskian expansion of gravitational scattering, heavy-quark effective theory and eikonal limits. Such propagators have no graph edge, so $U$ and $F$ are built by completing the square in the loop momenta, after which the same face-by-face elimination runs. The elimination over-generates candidates here, so verify_letter tests each against a saturated Gröbner elimination ideal and returns True, False (a spurious factor produced by the elimination) or None (undecided in the time limit); only verified letters belong in the alphabet.

LandauAlphabet.jl is the fitting side. It builds candidate functions as weight-graded iterated integrals (words) over the letters, assembles exact linear constraints on their rational coefficients, and evaluates the basis in high-precision ball arithmetic. The coefficients are then fixed by lattice reduction, and a fit is accepted only if they are small rationals and the residual vanishes at full precision, also at points not used in the fit. Kernels are either dlog forms (multiple polylogarithms) or the $\Gamma_1(6)$ modular forms of the equal-mass sunrise.

Limits. Faces are enumerated as connected sub-graphs; on graphs with more than four vertices a two-particle cut can be a disconnected sub-graph, which is skipped, so letters of the $s-4m^2$ type can be missed there (four-vertex graphs such as the three-loop $K_4$ box are unaffected). PLD.jl names the Mandelstam invariants itself, so a comparison may need relabeling. The Gröbner backend returns a face's complete Landau variety, non-physical branches included; classification and pruning are done by landau_alphabet.py.

Examples

Run the self-tests, which exercise every part on inputs with known answers:

python3 selftest.py
python3 selftest.py --full

The default run takes seconds. It computes the one-loop massless box (alphabet exactly {s, t}, complete true) and the two-loop equal-mass sunrise (s - m2 and s - 9*m2 present), and checks that the file on disk equals the in-memory result. It runs audit_faces.py on a clean output (exit 0) and on two deliberately doctored files (both must exit 2), and forces the Gröbner backend on the sunrise's leading face (skipped with a message without msolve or Singular). It then checks the linear-propagator engine's one-loop alphabet {q2, y - 1, y + 1}, and that the main engine's resultant chain keeps both s and t on the box's leading face with the Gröbner backend switched off. A last check verifies the topology of the included topbox.json specification (non-planar, one ring of four massive lines) without running the engine. Each check prints PASS or FAIL; the exit code is nonzero on failure. --full runs the longer original suites instead: the three-loop $K_4$ tests (minutes), the linear-propagator test file, the bridge's numerical fallback on the one-loop box (every witness must match when s + t is among the candidates, exit 0; with the sympy letters alone the s + t witness must be caught, exit 2; skipped without julia or HomotopyContinuation.jl) and the Julia suites (skipped if julia is not on PATH).

Compute and audit the alphabet of the three-loop crossed light-by-light box (graph_specs/k4box3l.json: four massive perimeter lines, two massless diagonals, four massless legs, invariants s, t, m2):

# sympy backend
python landau_alphabet.py graph_specs/k4box3l.json --out k4box3l_alphabet.json --face-timeout 45

# audit any output for silently-dropped faces (exit 2 on a silent drop)
python audit_faces.py k4box3l_alphabet.json

The first command prints the alphabet, the first_entry, last_entry and spurious lists, and a tally of resolved, partial and timed-out faces. It ends with COMPLETE: True or COMPLETE: False alphabet NOT proven complete followed by the unresolved faces; the JSON holds the same data. This graph has 54 faces and the known alphabet $\{s,\ t,\ s+t,\ s-4m^2,\ t-4m^2,\ st-4m^2(s+t)\}$. Four faces are hard (the whole graph and three five-edge sub-boxes). With msolve or Singular installed the Gröbner backend resolves them; without, a 45-second timeout leaves them unresolved and the run correctly reports complete: false. The faces that time out are generally where the missing letters are, so dropping them is not a fix. The audit exits with code 0 if the file's completeness claim is consistent and 2 if faces are missing or unresolved while the file claims to be complete. The same from Python, on the included two-loop non-planar topbox graph (a ring of four massive lines and a chain of three massless ones, from the $gg\to Z\gamma$ family): from landau_alphabet import load_spec, run, then gs = load_spec("graph_specs/topbox.json"), log = run(gs, out_path="topbox_alphabet.json", face_timeout=45), and read log["alphabet"], log["first_entry"], log["honesty"].

Letters of a family with worldline propagators. The one-loop integral with propagators $k^2$, $(k-q)^2$, $2u_1\cdot k$, $2u_2\cdot k$ and $u_1^2=u_2^2=1$, $u_1\cdot u_2=y$, $u_i\cdot q=0$ is the preset family_2PM_box:

import linear_landau as LL

res = LL.landau_locus_mixed(*LL.family_2PM_box())
ax = LL.pm_alphabet_x(res["alphabet"])

res["U"] and res["F"] are $a_1+a_2$ and $a_1a_2q^2-a_3^2-2y\,a_3a_4-a_4^2$, res["alphabet"] is {q2, y - 1, y + 1}, and ax is the alphabet in $x$, where $y=(1+x^2)/(2x)$: {x, x - 1, x + 1}. python linear_landau.py 2PM_box prints the same; the preset apery prints the discriminant factors in $x$ of the Apéry K3 leading-singularity surface: $x$, $x\pm1$ and $x^2\mp6x+1$. The product of the two quadratics, $x^4-34x^2+1$, has the physical root $x=3-2\sqrt2$, which is $y=\gamma=3$.

Routines

Face engine, landau_alphabet.py

Completeness auditor, audit_faces.py

Gröbner backend, pld_groebner_backend.py

PLD.jl bridge, pld_bridge.jl

Linear-propagator engine, linear_landau.py

Julia engine, LandauAlphabet.jl

Used on this site

Requirements and source

The face engine, auditor and linear-propagator engine need Python 3 with sympy. The Gröbner backend uses msolve and/or Singular (msolve always through the memory-capped wrapper of Dipstick, tools/dipstick/); the face engine calls it only when one of them is installed, and run directly with neither it falls back to sympy. The PLD.jl bridge needs Julia with HomotopyContinuation.jl and JSON.jl; the full PLD.jl run additionally needs Julia 1.10 with PLD.jl and Oscar (PLD.jl comes from its authors, mathrepo.mis.mpg.de/PLD; see PLD and SOFIA), and without them, as on Julia 1.11 or newer, the bridge runs its numerical fallback. The Julia engine needs Julia 1.11 or newer with Nemo, Arblib and JSON3 (julia --project=LandauAlphabet.jl -e 'using Pkg; Pkg.instantiate()'; on a newer Julia minor version run Pkg.resolve() first); its sampling routines call amflow_cli built from the amflow-cpp-dev fork (on PATH, or named by AMFLOW_CLI; see AMFlow), and its series evaluator underlies GPLEval.

Self-tests: python3 selftest.py (seconds) and python3 selftest.py --full (minutes; runs test_landau_alphabet.py, PYTHONHASHSEED=1 python test_linear_landau.py, the pld_bridge.jl numerical fallback on box1l.json, julia --project=LandauAlphabet.jl LandauAlphabet.jl/test/runtests.jl and LandauAlphabet.jl/test/test_cuspvals.jl). Missing msolve, Singular or Julia are reported as named skips, since they are external programs the Python face engine does not require. The code is in tools/landau-alphabet/ in BootLoops' bootloops-dev repository (GitHub organization BootLoops-ai), released under the MIT license.

← back to the tools index