generalised_ltd.py builds, prints, exports, and evaluates JSON representations for
LTD, pure CFF, and the current hybrid LTD/CFF construction for repeated
propagator channels.
Raised propagators are encoded as repeated internal DOT edges with the same
momentum signature and mass key. Use explicit repeated edges rather than the
DOT pow attribute.
Validate a graph:
python3 generalised_ltd.py validate --dot examples/graphs/box_pow3.dotBuild a structure:
python3 generalised_ltd.py build --family ltd --dot examples/graphs/box.dot --pretty
python3 generalised_ltd.py build --family cff --dot examples/graphs/box.dot --pretty
python3 generalised_ltd.py build --family hybrid --dot examples/graphs/box_pow3.dot --prettyBuild a DOT graph from a propagator-signature expression:
python3 generalised_ltd.py graph_from_signatures \
--signatures 'prop(k1+p1,mA)*prop(k1+p1-q1,mB)*prop(k1-p2+q2,mC)*prop(k1,mD)' \
--dot-output demo/from_signatures.dotThe default output uses the same DOT conventions as the examples. Use
--format vakint for the compact single-ext node format with ordered edge ids
and lmb_id attributes.
Runnable CLI examples live in examples/scripts. The main showcase for the
repeated box topology is:
examples/scripts/box_pow3_cli_showcase.shIt prints pretty overviews for LTD, CFF, and hybrid structures, runs the three-way comparison, compiles real and complex Symbolica evaluators, and profiles built-in versus compiled Symbolica evaluation.
The five-loop runtime comparison script builds and compiles LTD, CFF, and hybrid evaluators for a non-repeated four-external five-loop graph and for the existing repeated five-loop graph:
examples/scripts/five_loop_symbolica_runtime_compare.shFor each graph it profiles three numerator choices: 1, a sum with one
dot-product term for every internal edge, and the same sum with three selected
edge terms squared. The squared-edge scenario intentionally exercises the
bounded-degree builders and can take substantially longer to compile than the
timed evaluation itself.
The one-loop many-external benchmark compares LTD and CFF for a numerator-free one-loop 10-gon with 10 propagators and 10 external momentum symbols:
examples/scripts/one_loop_10_external_runtime_compare.shThe analogous larger benchmark uses a numerator-free one-loop 15-gon with 15 propagators and 15 external momentum symbols:
examples/scripts/one_loop_15_external_runtime_compare.shThis script builds real-valued Symbolica evaluators for the two representations and profiles every Symbolica evaluator mode stored in the JSON.
Inspect one orientation in detail:
python3 generalised_ltd.py build --family cff --dot examples/graphs/box.dot \
--energy-degree-bounds 0:2,1:2,2:2 \
--pretty --show-details-for-orientation --++ --no-colorExport and evaluate JSON:
python3 generalised_ltd.py build --family hybrid --dot examples/graphs/box_pow3.dot \
--json-out demo/box_hybrid.json
python3 generalised_ltd.py evaluate \
--orientation-json demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--external '[[0.3,0.1,-0.2,0.05],[-0.15,0.2,0.05,-0.1],[0.25,-0.1,0.15,0.07]]' \
--loop3 '[[0.2,-0.3,0.1]]' \
--masses '{"m1":0.8,"m2":1.1,"m3":0.9,"m4":1.2}' \
--numerator-expr 'dot(edges[0], ext[0]) + dot(edges[3], ext[0])'Compile a fixed-numerator Symbolica evaluator and use it:
python3 generalised_ltd.py compile \
--orientation-json demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--numerator-expr 'dot(edges[0], ext[0]) + dot(edges[3], ext[0])' \
--value-type real \
--display-expression
python3 generalised_ltd.py evaluate \
--orientation-json demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--evaluator-backend symbolica_compiled \
--masses '{"m1":0.8,"m2":1.1,"m3":0.9,"m4":1.2}'
python3 generalised_ltd.py evaluate \
--orientation-json demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--evaluator-backend symbolica_eager_symjit \
--profiling 100 \
--masses '{"m1":0.8,"m2":1.1,"m3":0.9,"m4":1.2}'Profiling times 10 evaluator calls over the requested batch size and reports
both per-sample and per-call timings. --profiling without an explicit value
uses a batch size of 100.
Compare several compiled JSON evaluators in one timing table:
python3 generalised_ltd.py evaluate \
--orientation-json demo/box_ltd.json \
--profile-label ltd \
--profile-json cff=demo/box_cff.json \
--profile-json hybrid=demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--evaluator-backend symbolica \
--profiling 100 \
--masses '{"m1":0.8,"m2":1.1,"m3":0.9,"m4":1.2}'Monitor decimal precision loss with the saved Symbolica eager evaluator:
python3 generalised_ltd.py evaluate \
--orientation-json demo/box_ltd.json \
--profile-label ltd \
--profile-json cff=demo/box_cff.json \
--profile-json hybrid=demo/box_hybrid.json \
--dot examples/graphs/box_pow3.dot \
--stability \
--masses '{"m1":0.8,"m2":1.1,"m3":0.9,"m4":1.2}'--stability uses the serialized Symbolica eager evaluator, converts the real
input kinematics to precision-tracking decimal floats with 16 significant
digits by default, evaluates at 80 decimal digits of work precision, and reports
the number of significant digits carried by the final result. Pass
--stability 20 --stability-work-precision 100 to change those two numbers.
The compile command stores an evaluator compatibility block plus an
evaluators block in the JSON. It writes both a shared-library evaluator
(.so) and a serialized eager Symbolica evaluator (.sev) next to the JSON by
default. evaluate --evaluator-backend can select symbolica_compiled,
symbolica_eager, or symbolica_eager_symjit; symbolica is an alias for
compiled evaluation in a single run and means all available Symbolica modes in
the multi-JSON profiling table. The evaluator paths are stored relative to the
JSON file and are tied to the exact DOT graph, JSON structure, value type, and
numerator expression. If SYMBOLICA_LICENSE is set in the environment,
Symbolica can use multicore optimization during evaluator construction.
--display-expression prints the Symbolica top-level expression, parameter
order, constants, and function map before compilation.
Run diagnostics:
python3 generalised_ltd.py test --dot examples/graphs/proper_iterated_sandwiched_bubble.dot \
--masses '{"mA":0.8,"mB":0.9,"mC":1.1,"mD":0.75,"mE":1.0,"mF":0.6}' \
--numerator-expr 'dot(edges[1], ext[0]) + dot(edges[4], ext[0])'
python3 generalised_ltd.py test --dot examples/graphs/box_pow3.dot \
--energy-degree-bounds 0:1,1:1,2:0,3:4 \
--numerator-expr 'edges[0][0] * edges[1][0] * edges[3][0]**4' \
--dps 80
python3 generalised_ltd.py test-cff-ltd --dot examples/graphs/box.dot \
--energy-degree-bounds 0:2,1:2,2:2 \
--numerator-expr 'edges[0][0]**2 * edges[1][0]**2 * edges[2][0]**2' \
--dps 80The exported schema is intentionally evaluator-complete. Evaluation uses only:
- the DOT graph,
- the JSON surface cache,
- the orientation energy maps,
- half-edge factors,
- numerator-side surface factors,
- and the serialized factorization trees.
There is no hidden LTD/CFF backend call during evaluation.
For each variant, half_edges is an evaluated multiset, not debug metadata.
Every occurrence of edge e multiplies the variant by (2*E[e])^-1; repeated
ids encode powers of the same on-shell-energy residue factor. The full variant
factor is pref * prod_e (2*E[e])^-count(e) times numerator-side surfaces, the
factorized denominator tree, and the numerator sampled at the orientation
energy map.
The black-box numerator map is the EMR edge_q0 map. When a numerator uses
loops[i] and the DOT graph has an explicit LMB carrier edge whose label is the
corresponding loop name, the evaluator builds that loop four-vector from the
carrier edge's sampled energy and spatial momentum. This keeps loops[i][0]
and edges[carrier][0] equivalent in generalised finite-pole/contact samples,
where loop_q0 need not be globally integrable into every EMR edge map.
If the optional top-level evaluator block is present, it describes a compiled
Symbolica evaluator for one fixed numerator. The ordinary JSON semantics remain
authoritative: evaluate --use-symbolica only changes how that same serialized
orientation sum is numerically evaluated.
Schema v6 uses this invariant:
one orientation equals one unique EMR edge-energy numerator map.
All denominator contributions with that same numerator call are stored under
variants. Variant metadata records physical origin such as cff, ltd,
pinch[...], or hybrid basis/interpolation labels. The pretty table therefore
has one orientation id and possibly many variant rows.
Pretty labels use:
+: sample edgeeat+OSE[e],-: sample edgeeat-OSE[e],0: sample edgeeat zero energy,x: a non-trivial linear map, shown in detail with--show-details.
Surface classes are printed as e or h; numerator-only surfaces are printed
as (e) or (h). Causal denominator surfaces from the graph or a lower-sector
CFF component remain physical e/h surfaces. Interpolation or reconstruction
helpers add _h, and helper factors that are not original causal surfaces add
_sp, for example (e_h_sp).
--energy-degree-bounds supplies an upper bound for the EMR energy degree of
each edge in the numerator:
python3 generalised_ltd.py build --family cff --dot examples/graphs/box.dot \
--energy-degree-bounds 0:2,1:2,2:2 --pretty --no-colorThe builder checks both coordinate loop-energy UV degree and every one-parameter direction in loop-energy space. If a residue at infinity may contribute, the build fails.
For test and compare, --energy-degree-bounds is the common bounded-degree
configuration used for the CFF expression, the hybrid expression, and the
split-mass LTD reference. --cff-energy-degree-bounds is kept only for
CFF-only diagnostics.
Subcommands that evaluate a numerator (evaluate, compile, compare,
test, and test-cff-ltd) accept --numerator-expr auto. build accepts
the same option as metadata and records the resolved expression in
graph.numerator_expr without changing the numerator-independent orientation
structure. With
--energy-degree-bounds, this expands to a product of edge-external dot
products that saturates the requested EMR energy degree on every bounded edge.
Unspecified edges receive degree zero and contribute no factor. For example,
on box_pow3.dot,
python3 generalised_ltd.py test --dot examples/graphs/box_pow3.dot \
--energy-degree-bounds 3:3,4:4 --numerator-expr autouses
dot(edges[3], ext[0]) * dot(edges[3], ext[1]) * dot(edges[3], ext[2]) *
dot(edges[4], ext[1]) * dot(edges[4], ext[2]) * dot(edges[4], ext[0]) *
dot(edges[4], ext[1])
as the actual numerator expression reported in the JSON diagnostic. For
evaluate and compile, auto reads the normalized bounds from
graph.energy_degree_bounds in the orientation JSON.
Current exact bounded-degree support:
ltd: no structural change; bounds are reported only.hybridwithout repeated propagators: collapses to LTD even with bounds.hybridwith repeated propagators: bounded numerators are handled by the confluent hybrid formula with finite-difference numerator samples in the residue-basis energy coordinates. Cubic, quartic, and quintic caps are covered by tests against the split-mass LTD limiting proxy; larger caps are accepted subject to the UV check and practical expression size.cff: one-loop graphs support arbitrary UV-convergent caps with only regularE-surfaces in denominators.cff: multiloop and split-repeated graphs support quadratic-or-lower caps by summing finite-pole remainder/contact sectors. Lower sectors are graph minors: removed denominator edges are contracted before the remaining denominator is decomposed into loop-energy matroid components and rebuilt as auxiliary CFF causal minors. Denominator surfaces therefore remainE-only; numerator-side cached surfaces may still beEorH.cff: cubic, mixed cubic/quadratic, and quartic-or-higher caps are supported through a channel normal form for repeated signatures and a recursive lower-contact completion for ordinary high-power edges. This includes repeated-signature box, sunrise, kite, and iterated-bubble cases covered by tests. Terminal tadpoles are encoded as unit denominator-tree nodes, so pure CFF denominators remainE-surface-only. The same unit-node encoding is used when one denominator branch is a strict prefix of another branch, so both the terminal branch and the longer branch contribute to the tree sum.
Bounded pure CFF is constructed directly with E-surface denominator terms
rather than through a CFF + (LTD - CFF) correction.
Repeated-channel interpolation normally uses the dimensionless variable
q_C^0/OSE[C]. This is the default:
--uniform-numerator-sampling-scale noneFor high repeated-channel numerator degrees, the interpolation can instead use
a runtime scale M, so inverse interpolation powers are 1/M^r rather than
artificial 1/OSE[C]^r factors:
python3 generalised_ltd.py build --family cff \
--dot examples/graphs/box_pow3.dot \
--energy-degree-bounds 3:4 \
--uniform-numerator-sampling-scale beyond-quadratic \
--pretty --show-detailsAvailable policies are:
none: default, use the existingOSE[C]normalization.beyond-quadratic: useMonly for repeated-channel degree greater than 2.all: useMfor every repeated-channel interpolation degree greater than 1.
Uniform JSONs record graph.uniform_numerator_sampling_scale and
graph.uniform_scale_symbol="M". Energy maps can contain an integer "m"
coefficient, e.g. {"i":[[3,1]],"x":[[0,-1]],"m":2,"c":"0"} means
OSE[3] - E[0] + 2*M; variants can also carry uniform_scale_power, meaning
an extra factor M^-power.
Evaluate such JSONs with a non-zero scale:
python3 generalised_ltd.py evaluate \
--orientation-json tmp/box_pow3_uniform_cff.json \
--dot examples/graphs/box_pow3.dot \
--numerator-expr auto \
--uniform-scale -2.0test can compare the default representation and uniform variants in one run:
python3 generalised_ltd.py test --dot examples/graphs/box_pow3.dot \
--energy-degree-bounds 0:1,1:1,2:0,3:4 \
--numerator-expr auto \
--uniform-numerator-sampling-scale beyond-quadratic \
--uniform-scales 1.0,-2.0,2.75M=0 is rejected. Real evaluators accept positive or negative real values.
Complex Symbolica evaluators accept complex M; real Symbolica evaluators
reject complex M cleanly.
The CFF builder does not treat repeated signatures as a special obstruction:
they are repeated denominator factors in the CFF expression. For high-power
bounds, the builder first reduces a repeated channel as a whole, so a channel
such as the repeated k2 sector in sunrise_pow4.dot with
--energy-degree-bounds 2:5 keeps an E-surface-only CFF denominator instead of
falling into a lower-sector LTD correction. For non-repeated high-power
contacts it decomposes multiloop lower denominators into loop-energy matroid
components and serializes terminal tadpoles as unit denominator-tree nodes.
Every recursive lower sector uses the contracted graph minor of the removed
denominator edges, including repeated-channel copies.
For a repeated channel C with representative edge r, multiplicity nu, and
total requested channel degree d_C, bounded pure CFF builds a Lagrange
interpolation in q_C^0/OSE[r], rewrites every monomial in powers of
D_C=(q_C^0)^2-OSE[r]^2, and serializes each resulting term as a normal
orientation variant:
edge_q0is the unique black-box numerator map for the orientation.variants[*].meta.channel_reductionsrecords the derivation:members,representative,power,degree_bound,sample,remaining_power,parity,cancelled_power,inverse_ose_power, and the rationalcoefficient.half_edgescontains the ordinary CFF half-edge factors plusinverse_ose_powercopies of the representative channel edge.num_surfacescontains the cached numerator-only surface for the remaining factorq_C^0whenparity=1; denominator surfaces remainE-only.
Representative CLI checks:
python3 generalised_ltd.py build --family cff \
--dot examples/graphs/box_pow3.dot \
--energy-degree-bounds 3:4 --pretty --show-details
python3 generalised_ltd.py build --family cff \
--dot examples/graphs/sunrise_pow4.dot \
--energy-degree-bounds 2:5 --pretty --show-details
python3 generalised_ltd.py test \
--dot examples/graphs/sunrise_pow4.dot \
--energy-degree-bounds 2:5 --numerator-expr autoFor box_pow3.dot --energy-degree-bounds 3:4, the repeated channel is
[3,4,5], has power 3, and uses interpolation nodes
1,-1,0,2,-2. The first printed numerator map has five variants; for
sample 1 their channel metadata keeps denominator powers 1,2,2,3,3,
with inverse OSE powers 4,2,3,0,1.
For sunrise_pow4.dot --energy-degree-bounds 2:5, the repeated channel is
[2,3,4,5], has power 4, and uses nodes 1,-1,0,2,-2,3. The first
printed numerator map has six variants; for sample 1 their channel metadata
keeps denominator powers 2,2,3,3,4,4, with inverse OSE powers
4,5,2,3,0,1. This is the JSON form of the identity
(q_C^0)^5/D_C^4 = OSE_C^4 q_C^0/D_C^4 + 2 OSE_C^2 q_C^0/D_C^3 + q_C^0/D_C^2,
but implemented for a black-box numerator through interpolation samples.
Default suite:
PYTHONPATH=. pytest -q testsThe default suite includes:
- validation for all example DOT graphs except the intentionally invalid noisy example,
- JSON evaluator mutation tests,
- one-numerator-call-per-orientation tests,
- CFF/LTD/hybrid structural checks,
- repeated-propagator three-way diagnostics,
- bounded-degree CFF/LTD checks for supported caps,
- Symbolica compiled-evaluator checks when the optional package is installed,
- a non-repeated four-external five-loop graph,
- a four-loop repeated-channel stress topology.
The five-loop ultimate basis alignment test is still present but slow. Run it explicitly with:
HYBRID3D_RUN_SLOW=1 PYTHONPATH=. pytest -q tests/test_cli.py::test_ultimate_five_loop_bases_three_way_and_aligned_momentaSee docs/generalised_ltd.pdf for the derivation, JSON mapping, and bounded-degree
support structure.