SubTropica (external)
The content on this page was written by AI under human supervision.
SubTropica and its integration engine HyperFLINT are public software by Sebastian Mizera, Mathieu Giroux and Giulio Salvatori for computing Feynman-type parametric integrals symbolically. You give it an integrand that is a product of powers of polynomials over positive variables, with exponents linear in the dimensional regulator $\varepsilon$. It returns the exact coefficients of the $\varepsilon$-expansion as rational combinations of multiple zeta values and, when kinematic variables are present, logarithms and polylogarithms of them, with nothing fitted numerically. Upstream SubTropica is a Mathematica package; BootLoops runs the engine through a Julia driver of the same name that needs no Mathematica and adds the handling of divergent integrals, a reducibility screen, a graph front end and verification tools.
What it does
After Feynman parametrization a loop integral becomes an integral over $[0,\infty)^n$ of a product of polynomial powers; for a graph the polynomials are the Symanzik polynomials $\mathcal U$ and $\mathcal F$. SubTropica's input is this "Euler integrand",
$$ I(\varepsilon)= c(\varepsilon)\int_0^\infty d^n x\,\prod_i x_i^{\nu_i(\varepsilon)}\prod_j P_j(x,s)^{a_j+b_j\varepsilon}, $$
with every polynomial coefficient positive, which restricts the kinematic invariants $s$ to the Euclidean region. The driver expands in $\varepsilon$ and integrates each order one variable at a time, keeping every intermediate result as a hyperlogarithman iterated integral whose kernels are dx/(x − letter) for rational "letters"; logarithms and polylogarithms are the simplest cases. After the last variable the remaining constants are reduced to a fixed basis of multiple zeta valuesthe nested sums ζ(s₁,…,s_k) that appear as the constants of multiloop integrals; ζ(2) = π²/6 and ζ(3) are the first two. The result is a Laurent series in $\varepsilon$ with exact symbolic coefficients, plus a record of the integration order used, the engine version and timings.
The method needs the integrand to be linearly reduciblethere is an order of the integration variables in which, after each integration, all new singularities are again linear in the next variable. The driver has the engine search for such an order, saves it under a run directory and replays it on later calls; when none exists it raises NoLROrderError. A built-in retry admits quadratic letters, and a coefficient that would depend on the resulting algebraic constants is refused unless you pass strict_periods=false. A separate screen, lr_refine (the functions refine_letters and blocking_certificate), uses exact polynomial algebra and makes no engine calls. Given the polynomials and a "blocking" letter from a failed search, it decides in seconds whether that letter is certified spurious (an artifact of the search path) or survives the sharper compatibility-graph bound of Brown and Panzer.
Divergent integrals are classified before any integration. The driver builds the Newton polytope of the polynomials and classifies every ray of its normal fan as convergent, logarithmically divergent or power divergent. Convergent integrands go straight to the engine with its divergence check on. For logarithmic divergences it subtracts exact counterterms read off the divergent facets and integrates the finite remainders, so pole coefficients come out exactly. Power divergences, and rays that need a second regulator, stop with a specific exception (PowerDivergentRefusal, GeometricPropertyViolated) whose message says what to change. Calling again with allow_continuation=true rewrites the integral by Nilsson–Passare analytic continuation into pieces that are at most logarithmically divergent, each sent back through the same entry point.
The driver's defaults guard against two engine pitfalls. With its divergence check off, the engine returns zero on a divergent input, so the driver keeps the check on and decides from the response fields instead of the exit code, which is 0 even on failure. On an input that is not linearly reducible the engine can return a clean-looking result that drops algebraic branches, and nothing in the response flags this. The package's rule is therefore to run the lr_refine screen first and to check every new result to at least 30 digits against an independent numerical evaluation (verify.jl compares at an exact rational point). Numerators, physical-region kinematics and integrands with no admissible order are refused with a message; those cases belong to a numerical method.
Examples
Run the self-tests. From tools/subtropica/ in BootLoops' bootloops-dev repository (GitHub organization BootLoops-ai):
python3 selftest.py
Stage R checks the vendored upstream files against their recorded checksums. Stage F runs scripts/fibrate/fibrate.py --selfcheck: an integer-relation search that must recover a known four-term combination and must fail to identify the number $e$, a series check against the exact identity $\mathrm{ZIP}_{\rm reg}([-9/26])=\log(26/9)$, and a cross-check of its two polylogarithm evaluators. Stage E runs the Julia suite only if SUBTROPICA_HF_BIN points at a built engine, and otherwise prints a SKIP line naming what is missing. Exit code 0 means R and F passed and E passed or was skipped for that reason.
A five-variable integral. The package includes a convergent five-variable example of Smirnov's as fixtures/smirnov_tst2_5var.json (the integrand only; fixture files hold no result values). With scripts/env.sh sourced and the Julia project active:
using SubTropica
E = euler_integrand("fixtures/smirnov_tst2_5var.json")
L = subtropica_integrate(E; order=0)
SubTropica.canonical_text(L.coeffs[1])
subtropica_integrate checks the engine version, classifies the rays (all convergent here), finds and saves an integration order under ./subtropica_runs/, integrates, and reduces the constants. L is a LaurentSeries{HlogExpr} with L.minorder == 0, and L.evidence["lr_order"] records the order used. Its first coefficient is $1-\tfrac45\zeta(2)^2+\zeta(3)=1-2\zeta(4)+\zeta(3)$, printed with the engine's names mzv_2, mzv_3 for $\zeta(2)$, $\zeta(3)$. order=2 also returns the $\varepsilon^1$ and $\varepsilon^2$ coefficients. A single integration step can also be called directly, as test/test_bridge.jl does with cfg = SubTropica.HFConfig(): SubTropica.hf_integrate("Log[1+x]/(x(1+x))", [:x], [:x]; cfg=cfg) followed by SubTropica.resolve_periods(terms; cfg=cfg) gives mzv_2, and the integrand "1/((1+x)(2+x))" gives Log2.
Deciding whether a blocking letter is real. Panzer's HyperInt paper (arXiv:1403.3385, Example 4.2) reduces the pair $\{(1+x)^2+y,\;y+z^2\}$ with $z$ a parameter. test/test_lr_refine.jl reproduces it without the engine (the test reaches the functions through a module alias; with using SubTropica they are exported):
using SubTropica, Nemo R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]) polys = [(1 + x)^2 + y, y + z^2] vars = [:x, :y] # z = kinematic cert, removed, log = refine_letters(polys, vars, [:y, :x]) c = blocking_certificate(polys, vars, x + 1 + z)
log["order_walkable"] is true, and log["stages"] lists the letters $\{x+1,\,x+1+z,\,x+1-z\}$ after integrating $y$ and $\{1+z,\,z-1\}$ after both variables, as published; removed is empty because the naive and refined bounds agree here. With the order [:x, :y] instead, order_walkable is false, since the starting set is not linear in $x$. c["status"] is "SURVIVES" and c["real"] is true: the letter lies in the refined bound and cannot be certified spurious. A letter present in the naive bound but absent from every refined stage comes back "SPURIOUS_REMOVED", a proof that it cannot block the integration. is_blocking_real(polys, vars, letter) returns only the boolean.
Routines
Driver and input
subtropica_integrate(E; order, allow_continuation, strict_periods, run_dir, hf, ...)— the main entry: ray classification, routing, integration, assembly and zeta-value reduction; returnsLaurentSeries{HlogExpr}. A second method takes a two-regulatorEuler2Integrandwithaux_order.euler_integrand(prefactor, polys, nu, vars; kinvars)/euler_integrand(path)— build the input from polynomials or from asubtropica-euler-quad-v1JSON file.graph_to_quadruple(edges, nodes, masses, kinematics; nu, D, gauge, lee_pomeransky, gammaE_norm)— graph toEulerIntegrandwith its $\Gamma$-function prefactor inD − 2εdimensions;nu[e]=0pinches an edge; numerators are refused.symanzik_UF(edges, nodes, masses, kinematics)— $\mathcal U,\mathcal F$ by spanning trees and 2-forests, cross-checked against a Laplacian determinant.quadruple_json,write_quadruple_json,load_quadruple— write and read the integrand JSON format.b1_ray_census(E)— classify all rays and report which path applies (direct integration or subtraction), or raise one of the exceptions above;subtropica_integrate_b1andsubtropica_integrate_b2are the subtraction and continuation paths, callable directly.HFConfig(; bin, mzv_data_path, timeout_s, transport, cabi_lib)— engine location and limits; defaults readSUBTROPICA_HF_BIN,SUBTROPICA_MZV_DATA,SUBTROPICA_HF_TRANSPORT,SUBTROPICA_HF_CABI_LIB.default_run_dir()readsSUBTROPICA_RUNS.
Engine bridge and order search
hf_gate(cfg)— checks the engine version and one known evaluation (-x^2atx=3must give-9); raisesHFGateErrorotherwise.hf_call(op, fields; cfg, timeout_s, transport)— one JSON request to the engine, by process (:cli) or in-process library (:cabi); replies are classified intoHFError,HFFailed,HFDivergent,HFNarrowCtx,HFTimeout.SubTropica.hf_integrate(expr, vars_int, vars; cfg, check_divergences, algebraic_letters)— integrate one expression over the ordered variables (not exported; call with the module prefix).SubTropica.resolve_periods(terms; cfg, strict, on_period),SubTropica.zero_inf_period_value(word)— turn boundary words into named constants, refusing any word without an exact value (not exported).find_lr_order,verify_lr_order,ensure_lr_order(...; run_dir),persist_best_order,load_best_order— search for a linearly reducible order, replay a given one, or replay-else-search with the result saved.refine_letters(polys, vars, order; max_degree, algorithm)— refined letter bound along an order: certified letters, letters removed as spurious, per-stage log.blocking_certificate(polys, vars, letter),is_blocking_real(polys, vars, letter)— verdict for one letter over all reachable stages (SURVIVES,SPURIOUS_REMOVED,NOT_IN_BOUND); square-root letters raiseLRRefineRefusal.
Building blocks (called by the driver; exported except where written with the module prefix)
eps_expand,gamma_prefactor_series,loggamma_series,sign_split— per-order integrand lists, the exact prefactor series, the sign-definite letter split.tropical_data,trop_on_ray,trop_values,classify_ray,sigma_div,produce_ws,divergence_data,subtraction_terms— Newton polytope, ray exponents and classes, divergent faces, and the exact counterterms.continue_ray,continue_rays,find_first_np_continuation,expand_integral_b2,promote_second_regulator— Nilsson–Passare continuation steps, each output carrying its exact prefactor.assemble,assemble_faces,mzv_reduce,SubTropica.parse_coef,SubTropica.canonical_text,to_ginsh— sum the pieces, reduce zeta values, parse and print coefficients, export a GiNaC-evaluable string.
Verification (src/verify.jl)
eval_symbolic(L, point; dps)— evaluate a symbolic series at an exact rational point todpsdigits.compare_laurent,verify_fixture,verify_laurent(L, artifact_path)— compare with a stored reference evaluation order by order, reporting the number of matching digits at each order (rounded down); divergent fixtures are refused.load_fixture,load_oracle_artifact,run_oracle(:mpmath, fixture; dps)— read integrand and reference files, or produce a quadrature reference for small convergent fixtures.mutate_coefficient,synthetic_controls,pick_point— a perturbed copy the comparator must reject, the closed-form test cases that come with the package, a rational point where no letter vanishes.SubTropica.mzv_provenance_gate(; nrules, dps, table_path, expect_sha256)— checks a random sample of rules from the engine's zeta-value reduction table against the independent GiNaC evaluator todpsdigits, including one deliberately perturbed rule that must fail; stops with a message if the table's checksum differs fromSUBTROPICA_MZV_SHA256(not exported).
Scripts
selftest.py— the self-test entry above.scripts/env.sh— environment template (SUBTROPICA_HF_BIN,SUBTROPICA_MZV_DATA,SUBTROPICA_RUNS, the optional table checksumSUBTROPICA_MZV_SHA256,HF_PERIOD_TUPLES=0); edit the paths for your install.scripts/make_fixtures.py [--synthetic-only] [--dps N] [--oracle FIXTURE_JSON --out DIR]— regenerate the fixtures or write a quadrature reference evaluation.scripts/fibrate/fibrate.py WORDLIST.json [--out OUT.json] [--dps 130] [--validation-points 2/5 3/8], or--selfcheck— rewrite regularized words with parameter-dependent letters as $G(\ldots;z)$ times constants, validating every word numerically at two points (exit code 2 on any failure); Python APIEngine,run_wordlist,selfcheck,parse_letter, evaluatorszip_num.ZipEval,gpl_num.GEval.deps/build_cabi.sh,deps/verify_cabi.jl— build and check the optional in-process engine library (transport=:cabi). The library must be built from an engine source tree carrying a patch that is not distributed with the package (seedeps/BUILD.md), and the build script stops with a message unlessHF_CABI_WORKandHF_FLINT_PKGCONFIGare set. The command-line transport is the default and the supported route.test/runtests.jl— the Julia suite; severaltest/test_*.jlfiles also run stand-alone.
Used on this site
- Energy correlators — derived the closed form of one sector of the two-point energy-energy correlation by symbolic integration, where the constants had earlier come from integer-relation fits.
- The crossed light-by-light box — no admissible integration order exists for this family, whose top integral is not polylogarithmic, so it was computed by a different route.
Requirements and source
Julia 1.11 with Nemo, Oscar and JSON (pinned in Project.toml; the first instantiate compiles Oscar and is slow), and Python 3 with mpmath and sympy for the scripts. The HyperFLINT engine is not included. Obtain SubTropica and HyperFLINT upstream (github.com/SubTropica/SubTropica, MIT license; paper arXiv:2604.20954) and build the stand-alone command-line engine (C++17, FLINT 3.4 or newer, OpenMP) following the upstream README, a copy of which is in reference/; deps/BUILD.md covers only the optional in-process library. Then set SUBTROPICA_HF_BIN to the engine wrapper and SUBTROPICA_MZV_DATA to its zeta-value reduction table. Every engine call runs under ulimit -v 32505856 (about 31 GB); parallel work uses one process per worker, since threads sharing an engine handle crash. eval_symbolic and the zeta-value table check evaluate constants and polylogarithms with the independent ginac_gpl program of tools/gpl-eval/ in the same repository. That program needs g++ and the GiNaC/CLN libraries and is compiled from source there; set SUBTROPICA_GINAC_GPL if it lives elsewhere. Self-tests: python3 selftest.py inside tools/subtropica/; full suite: ulimit -v 32505856; julia --project=tools/subtropica tools/subtropica/test/runtests.jl (a few tests need SUBTROPICA_PROBES_DIR or SUBTROPICA_RAYCHECK_DIR and say so when unset). Code: tools/subtropica/ in the bootloops-dev repository, released under the MIT license; the vendored upstream files under reference/ and the external libraries the package calls (Oscar, GiNaC, FLINT) keep their own licenses. The algorithms are Brown's (arXiv:0804.1660, arXiv:0910.0114) and Panzer's HyperInt (arXiv:1403.3385).