Frobenius
The content on this page was written by AI under human supervision.
The frobenius_boundary package works out how the solutions of a Feynman-integral differential equation behave at infinite mass when that behavior is a sum of non-integer powers rather than an ordinary series. It reads an integration-by-parts reduction table and a list of master integrals. It returns the exact polynomial differential equation, the power-law exponents at infinity, a decision on which of them the physical solution can contain, and the series and matrix needed to fix the constants that remain. The purpose is to cut down the number of unknown boundary constants before any expensive numerical evaluation. The same directory also holds two arithmetic companions, asdminer and deform_transport, which study the Frobenius map of a differential operator modulo a prime; they are described at the end of the next section.
What it does
A family of Feynman integrals reduces to a finite set of master integralsa basis of independent integrals to which every integral of the family reduces through integration-by-parts identities $M_k(x)$ obeying a system of linear differential equations in a kinematic variable $x$, here a squared mass. Solving the system numerically means carrying a known solution from one point to another, and $x\to\infty$ is a convenient start because the integrals usually have a simple large-mass expansion there. On a maximal cutthe version of the integral with every propagator put on its mass shell; it obeys the homogeneous part of the same differential equation that can fail: the naive large-mass value vanishes and the solution is not analytic at infinity. It is instead a combination of Frobenius branches, solutions of the form $x^{\lambda}$ times a series in $1/x$, one per allowed exponent $\lambda$, each with its own free constant.
The package builds the exact polynomial system $D(x)\,dM/dx = G(x)\,M$ from a per-point Kira reduction table (kira2math format, every kinematic quantity other than $x$ already numeric) at fixed rational dimension $d = 4-2\varepsilon$. It expands $x\,G/D$ at infinity and forms a residue matrix $D_1$ whose eigenvalues are the exponents. Repeating this at several values of $\varepsilon$ (two at least; four is the tested pattern), it fits each exponent as $\lambda = a + b\,\varepsilon$ with rational $a, b$ and sorts the families. Rational-linear in $\varepsilon$ survives. Complex or irrational is excluded, because the physical large-mass expansion contains only powers $x^{p+q\varepsilon}$ with rational $p, q$. Integer and $\varepsilon$-independent is excluded only when kill_integer_eps_indep is true, which asserts that a vacuum-integral reduction shows those terms vanish for your family (it defaults to true in the Python functions, so pass False unless you have that check; the example configuration sets it to false). Other rational $\varepsilon$-independent exponents are only flagged for review. The package then builds the $1/x$ series of each surviving branch and evaluates them at a large starting value Mstart, giving an $N\times J$ matrix $\Phi$ whose columns span the space the physical solution lives in. The surviving constants $c$ are fixed afterward, outside this package, by a small linear solve $R\,\Phi\,c = O$ against a few independently known numbers.
Resonances (exponents differing by integers) are handled by a minimum-norm solve that sets log_branch_required if a logarithmic term would be needed. A non-Fuchsian system (a nilpotent block $D_0$ ahead of $D_1$ that is not fitting noise) is handled by an exact shear reduction, after which the driver classifies the reduced residue's eigenvalues and records the frame in classification_frame. The equation, $D_1$ and the exponents are exact to the stated precision, and the driver checks that $\mathrm{tr}\,D_1$ is exactly rational-linear in $\varepsilon$, which catches a mis-built equation. The rational $(a,b)$ of survivors come from numerical fits that agree to about $10^{-26}$ across the $\varepsilon$ values, with no symbolic derivation behind them, and the output labels them that way. Excluding $\varepsilon$-incommensurate branches relies on the completeness of the expansion by regionsthe standard technique that writes a Feynman integral's large-mass asymptotics as a sum over momentum regions, each contributing definite powers of the large scale, so keep more matching conditions than surviving constants in the downstream solve and confirm the extra ones hold.
Do not use the package for a Frobenius expansion around a finite singular point (that is frobenius and frob_scalar in Wayfinder), or when the solution is analytic at infinity, where ordinary large-mass values suffice. Reading a single boundary constant off the Picard–Fuchs operator reduced modulo primes (the $p$-adic Frobenius trace) is done elsewhere on this site, with GeoTriage and Lockpick.
The two companions work with that second sense of the name, the Frobenius map of a differential operator reduced modulo a prime $p$. asdminer takes an exact integer or rational series $a(n)$, a prime $p$ and a weight $k$, and solves the three-term Atkin–Swinnerton-Dyer congruences $a(np) - \gamma_p\,a(n) + p^{k-1}\,a(n/p) \equiv 0 \pmod{p^s}$ for a single $\gamma_p$. For each prime it reports one of four outcomes: a single $\gamma_p$ fits every congruence (listed with the integer candidates the Weil bounds allow), the congruences disagree (the offending ones are named), they are underdetermined, or $p$ divides a denominator and nothing can be read. A classification step then separates the trivial unit-root value, which every series shows at a degenerate point of a family and which is not evidence of modularity, from a genuinely modular $\gamma_p$. deform_transport carries the full Frobenius matrix of an operator along a one-parameter deformation and returns the complete local factor at $p$: an integer characteristic polynomial, checked against the functional equation and the Weil bounds and required to agree under two independent truncations. It costs far more than the congruence miner, so run asdminer first.
Examples
Run the pipeline from a configuration file. Copy frobenius_boundary/config_example.json, point kira_targets_m at your kira2math table and masters at your preferred-masters list, set var, mass_slots, n_loops, eps_list, dps, branch_n_terms and Mstart, then from tools/frobenius-boundary/:
python3 -m frobenius_boundary.cli config.json
The driver runs the $\varepsilon$ values in parallel (ncpu), writes <out_dir>/SPECTRUM_<tag>.json and, when branch_n_terms is nonzero, <out_dir>/BRANCHES_<tag>.json, and prints a [cli] wrote ... line for each. The spectrum file holds the per-$\varepsilon$ degrees, fit error and eigenvalues, the classified families (groups, each with a, b, mult, class and Jordan data), the trace check, and the counts n_free_constants, n_killed, n_flagged. The branches file holds each branch's series coefficients and resonance report and, if Mstart was set, Phi.
Classify a hand-built spectrum. From tools/wayfinder/tests/test_boundary_branches.py, which needs no data files; bb is Wayfinder's boundary_branches module, which forwards each call unchanged to frobenius_boundary.core:
eps_list = ["1/101", "1/103", "1/107", "1/109"]
with mp.workdps(60):
EVs = []
for e in eps_list:
_dd_rat, _dd, ev = bb.eps_to_d(e)
EVs.append([1 - 2 * ev, mp.mpf(1) / 2 + ev / 2, mp.mpf(3)])
fams, groups = bb.classify(EVs, eps_list, kill_integer_eps_indep=True)
with mp.workdps(60):
ex = bb.exclude_strata(groups)
assert ex["n_free_constants"] == 2, ex["n_free_constants"]
assert ex["n_killed"] == 1
Three exponents are given at four values of $\varepsilon$: $1-2\varepsilon$, $\tfrac12+\tfrac12\varepsilon$ and the constant $3$. classify returns one group per family; the first two carry class RATIONAL_LINEAR__SURVIVES_pinch and the integer one INT_EPS_INDEP__KILLED_vacuum_theorem, because kill_integer_eps_indep=True was passed. exclude_strata then counts two free constants and one excluded branch. The calls sit inside mp.workdps(60) because several entry points take their precision from the ambient mpmath setting.
Run the acceptance test.
cd tools/frobenius-boundary && python3 -m frobenius_boundary.selftest
The test reads the configuration named by FROB_CONFIG (default config_example.json). If the table it names does not exist, it stops with exit code 3 and a message naming the missing kira_targets_m path; a fresh clone does exactly that, because the reference reduction table is not distributed. With a table in place it runs the full driver and prints a [PASS] or [FAIL] line per check: polynomial degrees, fit error, exponent counts, the shear reduction, the surviving, flagged and excluded families, the trace rules, branch residuals and the column rank of Phi. The expected values in those checks are the reference family's, so a different table exercises the whole pipeline but reports FAIL on the comparisons. The run ends with SELFTEST PASS or the list of failures, writes SELFTEST_VERDICT.json, and takes roughly 15 to 25 minutes.
The two companions have their own quick self-tests, which run on a fresh clone from the same directory:
python3 asdminer/run_control.py python3 deform_transport/battery/run_battery_deform.py --stages S0,S1
The first needs gp (PARI/GP) on the PATH and takes a few seconds. It mines series whose congruences are proven theorems (point counts of two elliptic curves, Hecke eigenvalues of a weight-4 form, the Apéry numbers), confirms that a deliberately corrupted series and a structureless one are rejected, prints VERDICT: and control_pass = True, and writes asdminer/work/ASD_CONTROL.json. The second needs python-flint and gmpy2: stage S0 rebuilds the order-6 operator used as the rank-6 control, stage S1 transports the rank-2 Legendre family at $p=23$ and $p=29$, where the answer is a known theorem, and the run prints a BATTERY VERDICT: line beginning FAST-TIER-GREEN. The full run, --stages S0,S1,S2,S3,S4, takes hours, and S4 needs the FROB_L6_OPERATOR file described under Routines.
Routines
Command-line entry points (run from tools/frobenius-boundary/)
python3 -m frobenius_boundary.cli config.json— end-to-end driver; writesSPECTRUM_<tag>.jsonandBRANCHES_<tag>.json.python3 -m frobenius_boundary.selftest— acceptance test: runs the driver on the configured table and compares the result with the reference family's expected numbers;FROB_CONFIG=<path>selects the configuration.
Pipeline functions (all in frobenius_boundary.core; the first six are also importable from the package itself)
build_poly_DE(kira_targets_m_path, masters, var='m2', s_t_numeric=None, eps='1/101', prec=80, mass_slots=None, family=None, verbose=False)— exact polynomial system from a Kira table (s_t_numericis recorded only as a note of the kinematic point;mass_slotsdefaults to0..7, so set it for your family); returns(D, G, fit_err_log10), extras incore.LAST_BUILD.spectrum(D, G, ai, eps='1/101', n_loops=3, n_ord=10, prec=None, dps_eig=60)— expansion at infinity, the residue matrixD1and its eigenvalues.classify(exponents, eps_list, maxden=64, kill_integer_eps_indep=True)— fits $\lambda=a+b\varepsilon$ across the $\varepsilon$ lists; returns(families, groups)with a class per group.exclude_strata(groups, EVs=None, eps_vals=None, maxq=10**6, dist_tol=None)— survivors, excluded and flagged families,n_free_constants, and the rationality scan behind each exclusion.branch_series(D, G, lambda_j, v_j, n_terms, ai=None, eps='1/101', n_loops=3, prec=None, eigvals=None, seed_index=None)— the $1/x$ series of one branch from its seed vector, plus a diagnostic record (resonances,refine_rfinal,log_branch_required,reduce_failed).phi_matrix(Mstart, branch_list, ai, dd, n_loops=3)— the $N\times J$ matrix of branch solutions atMstart.seed_vectors(D1, lam, tol_exp=15)— null space of $D_1-\lambda I$, the seeds of branch $\lambda$; returns(V, dimension, residual); pass a column's index tobranch_seriesasseed_index.eps_to_d(eps)—'1/101', aFractionor a float to $d$ (sympy rational), $d$ and $\varepsilon$ (mpmath).trace_rule(D1s, eps_vals),jordan_info(D1, groups, eps)— the trace check; geometric multiplicity and log-branch count per family.kira_parse.parse_kira2math(path, family=None)— regex parser for kira2math output;de_core.load_mastersandde_core.alpha_intread the masters list and their integer weights.- Environment:
FROBENIUS_POLYDE_CACHE=<dir>caches the fitted system on disk;FROBENIUS_SAMPLE_NCPU=<n>samples in parallel. Both off by default; results unchanged.
Arithmetic companions (run from tools/frobenius-boundary/; outputs go to asdminer/work/ and deform_transport/work/ or deform_transport/battery/work/)
python3 asdminer/run_control.py— self-test of the congruence miner against proven congruences, a corrupted series and a null series (checksG1–G6); needsgpon the PATH.asdlib.mine(aval, p, N, k)— solves the three-term congruences at one prime for the seriesaval(n),nfrom 1 toN, at weightk; returns the outcome, the depth reached, $\gamma_p$ modulo $p^D$, the disagreeing congruences if any, and the Weil-bound candidates.asdlib.classify_gamma(entry, D, k)— labels a mined $\gamma_p$ as trivial (unit root or Eisenstein), non-trivial cuspidal, ambiguous or outside the cuspidal range.asdlib.load_operator(path),shift_operator(coeffs, x0),op_local_data(coeffs),frobenius_exact(coeffs, rho, N),frobenius_modular(coeffs, rho, N, p, S_target, resonances_cert)— read a polynomial-coefficient operator, move it to a point, get its indicial data, and expand the Frobenius-series solution of exponentrhoexactly or modulo $p^S$;apery_zeta3,apery_zeta2,eta_product_8_4,ell_omega_seriesbuild the control series.python3 asdminer/run_l6_gauge.py,run_l6_mine.py,run_l6_topup.py— operator-driven miners (local exponents and candidate series at $x_0 \in \{1,0,-1\}$, a pilot prime, then a fan of primes, then the primes a time budget trimmed); they needFROB_L6_OPERATORand stop with a message when it is unset.python3 deform_transport/battery/run_battery_deform.py [--stages S0,S1,...]— self-test suite of the matrix transport:S0operator rebuild,S1rank-2 theorem control,S2a re-introduced historical indexing bug that the rank-6 control must catch,S3all six eigenvalues of the rank-6 control at $p=23, 29$,S4the documented refusal case reproduced at reduced scale (needsFROB_L6_OPERATOR), then assembly ofBATTERY_RECEIPT.json.deform_kit.transport(op, p, K_true, fibers=None, ...)withop_legendre(),op_sym5(path=None),op_L6()— the matrix engine indeform_transport/code/: Frobenius basis at $x=0$, the matrix $F(x)$, evaluation at Teichmüller points and the certified characteristic polynomial to depth $p^{K}$;isolate_factorandgamma_normalizefinish the integer local factor.lfact_kit.py(multi_level_lift,sym5_control,pcurv_jordan,enumerate_sextics) — single-series routines: unit-root lifts at several levels, the exact rank-6 control, an independent $p$-curvature Jordan profile at one fiber, and integer isolation of a degree-6 local factor.code/build_sym5.py,run_cal_a.py,run_cal_b.py,run_cal_d.py,run_cal_d2.py,preflight_invariance.py,run_license.py,run_license2.py,emit_wall.py— the calibration chain as scripts (the last writes the refusal record when the calibration does not pass), each writing a JSON record underdeform_transport/work/together with a checksummed copy of the script that produced it (emit_receipt.py);run_cal_dandrun_cal_d2need comparator inputs named byDEFORM_R2_GRIDandDEFORM_F1_RECEIPTS, which are not distributed.- Environment:
FROB_L6_OPERATOR=<json>— an order-6 operator file ({"coeffs": [[...], ...]}, seven rows) used by therun_l6_*miners,op_L6()and stageS4; it is not distributed, and everything that needs it stops with a message when it is unset.FROB_ASDMINER_WORK=<dir>moves the miner's output directory.FROB_PRODUCER_LINT=<script>optionally runs a checker of your choice on each JSON record as it is written.
Related routine in a sibling package
richardson_boundary(partial_sums, Ms, tail_exponents=None)intools/eps-extrapolator/(see Gatekeeper) — sums a branch series at the edge of its disk of convergence by eliminating known $M^{-\alpha}$ tail powers from the partial sums.
Requirements and source
Python 3 with mpmath, sympy, gmpy2 and numpy. The code is tools/frobenius-boundary/ in BootLoops' bootloops-dev repository (GitHub organization BootLoops-ai), released under the MIT license; Wayfinder's boundary_branches module wraps the same functions. The acceptance test is python3 -m frobenius_boundary.selftest from that directory, with FROB_CONFIG pointing at a filled-in copy of config_example.json; it needs a per-point reduction table made with Kira and stops with exit code 3 until one is configured. The companions sit in the same directory: asdminer/ uses only the Python standard library, plus gp from PARI/GP on the PATH for its self-test python3 asdminer/run_control.py; deform_transport/ needs python-flint and gmpy2 (numpy and scipy only inside one control) and its quick self-test is python3 deform_transport/battery/run_battery_deform.py --stages S0,S1. The order-6 operator file some of their scripts read (FROB_L6_OPERATOR) is not distributed.