FAIR tutorial
A hands-on walk-through, from a fresh checkout to reading reduced integrals in your CAS. Every command below is copy-pasteable and matches the shipped examples. For the reference description of options and file formats, see README.md; for the algorithm, see doc/fair.tex.
FAIR (Fast Automatic Integral Reduction) is a self-contained C++/FLINT realization of the A.I.R. IBP-reduction algorithm (hep-ph/0404258). It needs no external computer-algebra system — only FLINT/GMP/MPFR at build time.
0. Prerequisites
You need a C++17 compiler and FLINT 3.1 or later (tested with 3.1.3 and 3.5.0; the 3.0.x series does not work) with GMP and MPFR. The easiest way to get them on any platform is conda-forge:
micromamba create -n fair flint gmp mpfr cxx-compiler # or: conda / mamba
micromamba activate fair
(System packages also work: Debian/Ubuntu libflint-dev libgmp-dev libmpfr-dev, Homebrew brew install flint. FAIR uses the fmpz_mpoly_q API, so make sure your FLINT is 3.1 or newer; the 3.0.x series is too old.)
1. Build and self-check
./build.sh # auto-detects FLINT; bundles libs into ./lib (portable)
./test/run_tests.sh # runs 4 known reductions, diffs against golden output
If you see ALL TESTS PASSED, your build is correct. build.sh works on Linux ($ORIGIN rpath, bundled .so) and macOS (@loader_path, bundled .dylib, ad-hoc codesign); the binaries plus ./lib are portable as a unit.
If FLINT lives somewhere unusual:
FLINT_PREFIX=/path/to/prefix ./build.sh.
2. Your first reduction (one-loop box)
You describe the physics in a runcard; fair-run derives the scalar products, writes the low-level topology, seeds, and runs the reduction. Here is the shipped two-mass-easy box, examples/box2me.run:
name Box2me
loops k1
legs p1 p2 p3 p4 # all incoming, sum = 0 (p4 dependent)
onshell p2 p4 # the two massless legs
invariant s = (p1+p2)^2
invariant t = (p2+p3)^2
invariant m1 = p1^2 # off-shell virtuality of leg 1
invariant m2 = p3^2 # off-shell virtuality of leg 3
prop k1
prop k1+p1
prop k1+p1+p2
prop k1+p1+p2+p3
maxtensor [2, +1] # targets tensor 0..2 ; seed +1 above for closure
maxdots [0, 0] # targets dots 0..0 ; no extra dotted seeds
Run it:
./fair-run examples/box2me.run -o out/ --format mathematica
fair-run prints the scalar products it derived (you never hand-compute a dot product) and the seeding scope, then writes into out/:
out/masters.m MASTERS = { ... }
out/reductions.m red = { target -> Σ cᵢ masterᵢ, ... }
out/manifest.txt human summary: masters, target counts by rank, closure
out/Box2me.topo the generated low-level topology (for reference)
out/Box2me.fair the generated problem file (templates + seeds)
The manifest.txt tells you at a glance what happened:
masters (5): Box2me(0,0,1,1) ... Box2me(1,1,1,1)
targets melted: 32 (not closed/unreduced: 0)
by numerator rank: rank 0: 4 rank 1: 12 rank 2: 16
not closed: 0 is what you want — every target was fully reduced to masters.
3. Reading the result
The integral Box2me(a,b,c,d) is the scalar integral with propagator powers a,b,c,d (negative = numerator, i.e. an inverse propagator / dot product). A line of reductions.m looks like:
Box2me[1,0,1,-2] -> ((d*m2^2-2*d*m2*s+d*s^2)/(4*d-4))*Box2me[1,0,1,0],
Use it in Mathematica:
Get["out/masters.m"] (* -> MASTERS *)
Get["out/reductions.m"] (* -> red = { target -> rhs, ... } *)
myexpr2 = myexpr /. red; (* rewrite onto masters *)
Want a different format? Add format form (or sympy) to the runcard, or pass --format on the command line:
./fair-run examples/box2me.run -o out_mma/ --format mathematica # out_mma/reductions.m
./fair-run examples/box2me.run -o out_frm/ --format form # out_frm/reductions.h
| format | files | load with |
|---|---|---|
maple (default) | .mpl | read "...": |
mathematica | .m | Get["..."] → red, MASTERS |
form | .h | #include reductions.h (sparse Table/Fill) |
sympy | .py | from reductions import reductions (dict) |
4. A two-loop example, and controlling the seeding
The planar double box, examples/pbox.run, is a genuine two-loop topology. You list only the 7 genuine propagators; FAIR auto-completes the 2 irreducible numerators it needs:
./fair-run examples/pbox.run -o pbox_out/
The two knobs that matter most:
maxtensor [max, offset] # reduce numerator rank 0..max ; also SEED offset above max
maxdots [max, offset] # reduce up to max dots ; also SEED offset above max
max= how high you want the *targets* (the integrals you actually get reduced).offset= how far *above* the targets to place *seed* integrals. Closing rank-Ntargets generally needs rank-N+1seeds, sooffset +1is the usual choice;offset 0means "seed no higher than the targets" (faster, but the top rank may not fully close). A negative offset is clamped to 0.
So maxtensor [3, +1] means: *give me rank 0–3, seed up to rank 4.* You can also override on the CLI: --seed-tensor, --seed-dots, --format, --factor, --gzip.
The shipped runcards mbox, pentagon, xbox, pentabox, and the one-off-shell xbox1m follow the same pattern. (The 3-loop *.topo files — ladder3, tetra3 — are low-level topology demos; see §7.)
5. Going further
These are documented fully in the README; here is when you reach for each.
- Restrict to a sub-topology / sector. Add
sector <indices>lines to the runcard (orSECTORin a.topo) to reduce only a chosen sub-sector; out-of-sector genuine lines become numerators. Seeexamples/xbox_subsector.run. - Cutkosky cuts (reverse unitarity). Mark a propagator as cut by adding a third field:
prop k1+k2-p1 | 0 | cut. Cut lines must always be present; integrals missing a cut line are set to zero throughout the reduction. - Independent external momenta. Scattering runcards use
legs(all incoming, sum = 0, last leg dependent). For forward / inclusive (reverse-unitarity) topologies whose external momenta are *not* conserved among themselves, declare them withexternalinstead — e.g.external p1 p2(no dependent momentum). Seeexamples/higgs_n3lo.run. - User-defined numerators. Add
isp m1.m2lines to fix specific irreducible scalar products in the first ISP slots; the rest are auto-completed. - Incremental save/resume. Reduce to some rank, save the entire in-RAM state, and later extend to higher rank reusing everything:
``sh ./fair --problem p.fair --out base --tenmax 3 --save state.fairstate ./fair --problem p.fair --out ext --tenmax 4 --resume state.fairstate # only new seeds ``
The simplest cut family: NLO real emission
examples/dy_real.run is a one-loop reverse-unitarity family: p1 + p2 → V(M) + parton, with the radiated parton and the massive boson line as cut lines and the t-channel propagator as the only uncut line. FAIR finds one master (the two-body phase space DYreal(1,1,0)) and reduces the 27 targets to it in a fraction of a second, e.g. DYreal(1,1,1) = 2(d-3)/((d-4)(M-s)) * DYreal(1,1,0). A cut line is written prop mom | mass2 | cut (or prop mom | cut when massless).
A worked reverse-unitarity example
examples/higgs_rr.run is an NNLO double-real phase-space topology — gg → H + 2 partons — with the Higgs and the two radiated partons entering as cut lines. Because the incoming momenta p1, p2 are independent (their sum carries s into the final state), it uses external rather than legs:
name HiggsRR
loops k1 k2
external p1 p2 # independent incoming momenta (no conservation)
onshell p1 p2
invariant s = (p1+p2)^2
prop k1 | 0 | cut # radiated parton 1
prop k2 | 0 | cut # radiated parton 2
prop k1+k2+p1+p2 | Mh | cut # the Higgs (mass^2 = Mh)
prop k1+p1
prop k1+k2+p1
prop k1+p2
prop k1+k2+p2
maxtensor [2, 1]
maxdots [0, 0]
Run it the same way as the box:
./fair-run examples/higgs_rr.run -o out/
The three cut lines are pinned — any integral missing one is set to zero — and FAIR reduces the phase-space integrals by IBP exactly as for a virtual topology:
masters: 4 ... MELTED 100 targets ... (0 not closed)
4 masters, all 100 rank-0..2 targets closed in well under a second. Read the result just as in §3. The three-loop N³LO triple-real analogue (the Higgs plus three cut partons) is examples/higgs_n3lo.run.
6. Huge reductions
When the output is too large for a CAS to parse, combine these (see README → *Huge reductions* for measured numbers):
./fair-run examples/pbox.run -o pbox_out/ --factor --gzip
--gzip— gzip the output (≈4.4× smaller); the biggest single lever.--factor— intern shared coefficients into acf[]table (coeffs.*);reductions.*becomes a small index.--split(onfair) — one file per target, so consumers read only what they need.--format form— for the truly enormous, FORM scales where an interactive CAS cannot; build a load-on-demand TableBase from the emitted sparseTable.
Those control the output size. A different bottleneck is the melt-time memory peak (many long coefficients normalized at once); split the melt into two stages with --partial-melt (on fair):
--partial-melt— two-stage melt: stage 1 flattens theK-handles to one coefficient per master while keeping theq-coefficients frozen (mod-p, no rational normalization), so growth stays in the frozen algebra as long as possible; stage 2 melts the frozenq-form once per coefficient.--stage1-only— run just the memory-heavy stage 1; combined with--save state.fairstateit serializes the state so a very large reduction can be resumed (melt later, or on a bigger machine).
7. The low-level tools (optional)
fair-run is just a friendly front end over two binaries you can call directly:
./fairgen --spec examples/pbox3.topo --out pbox.fair # .topo -> problem file
./fair --problem pbox.fair --out pbox_out --tenmax 3 --dotmax 0
A .topo file states the topology directly (loop momenta, external legs, propagators with | mass² | cut, scalar-product values, seed ranks). This is the right level for exotic kinematics or three-loop topologies (ladder3.topo, tetra3.topo), where you may prefer to write the scalar products yourself. The full .topo grammar is in the README.
8. Troubleshooting
| symptom | fix |
|---|---|
ERROR: FLINT not found | install FLINT/GMP/MPFR; FLINT_PREFIX=/path ./build.sh |
build OK but ./fair can't find a library | re-run ./build.sh (it bundles libs into ./lib); keep lib/ next to the binary |
not closed > 0 in the manifest | raise the seed offset (e.g. maxtensor [N, +1]) |
| results look wrong | run ./test/run_tests.sh — if that fails, the build is broken |
| huge output won't load | see §6 (--gzip, --split, --format form) |
9. Citing FAIR
If FAIR is useful in your work, please cite the A.I.R. paper it implements (Anastasiou & Lazopoulos, JHEP 07 (2004) 046, hep-ph/0404258) and this software — see CITATION.cff.