Skip to content

Integrate a channel whose matrix element is identically zero - #135

Merged
oliviermattelaer merged 1 commit into
mainfrom
claude/madspace-zero-me-channel-nan
Sep 11, 2026
Merged

oliviermattelaer merged 1 commit into
mainfrom
claude/madspace-zero-me-channel-nan

Conversation

@oliviermattelaer

Copy link
Copy Markdown
Contributor

The bug

p p > t t~ DIM6=0 in dim6top_LO_UFO aborted the mg7/madspace survey:

RuntimeError: non-finite integral in channel '2.0' after 1000 samples
(mean=nan, variance=nan)

output madevent integrated the same process fine.

dim6top carries an FCNC coupling order that DIM6=0 does not constrain, so MG5 keeps the four flavour-off-diagonal channels (u c~, c u~, d s~, s d~ > t t~). Each has 7 diagrams but an amplitude of exactly zero, because their Wilson coefficients are zero in the default param card. A channel like that should contribute nothing; instead it poisoned the whole run.

This is not specific to dim6top — any process with a subprocess whose matrix element vanishes identically hits it.

Three 0/0 divisions in series

All of them on a channel with no amplitude anywhere, each only visible once the one before it was fixed:

  1. umami.cc builds the per-diagram amp2 that madspace samples from as numerator/denominator, the denominator being the sum of the numerators.
  2. kernel_collect_channel_weights normalises the channel weights by the sum of those amp2.
  3. survey() divides by the subprocess mean to get its relative spread — a plain ZeroDivisionError once the nan is gone and the channel correctly integrates to zero.

The matrix-element multichannel weights in process_sigmaKin_function.inc and process_function_definitions.inc carry the same division and are guarded too, for the consumers that ask for mulChannelWeight. mg7 does not, so only the three above were load-bearing for this reproducer.

Reviewers: the C++ guards must not become a test for zero

This is the part that will look wrong and isn't.

The generated code is compiled with -ffast-math, and its -ffinite-math-only lets the compiler assume the quotient is finite — and therefore delete a zero-check as dead code. An explicit denominator == 0. ? 0. : ... in umami.cc was silently removed, and only began working when a debug printf next to it perturbed the optimiser. That is the same trap as #117 and #516, which is why CrossSectionKernels.o is already built -fno-fast-math.

These denominators are sums of |amp|^2 and so never negative, so they are floored by adding the smallest normal instead:

numerator / ( denominator + std::numeric_limits<double>::min() )

0/tiny is 0, and every denominator a real amplitude produces is unchanged bit for bit. That keeps the guard robust without taking -ffast-math off a hot file. Rewriting it as an if would compile to nothing and silently reintroduce the bug.

madspace's own kernel is not built that way, so it substitutes the norm and falls back to the uniform distribution — the channel weights have to remain a normalised distribution over the channels, and with no amplitude to prefer one, none is preferred.

Verification

dim6top, ctG = 1, 13 TeV, fixed mu = 91.188, NNPDF23_lo_as_0130_qed:

mg7 / madspace madevent
p p > t t~ DIM6=0 (9 subprocesses, 4 identically zero) 736.223 +- 1.686 pb 736.2 +- 0.61 pb

Each guard was confirmed load-bearing by reverting it and rebuilding — reverting the madspace one alone brings the nan straight back.

No regression: plain sm p p > t t~ gives 718.8675807362097 pb, bit-identical to the same run before this change.

🤖 Generated with Claude Code

`p p > t t~ DIM6=0` in dim6top_LO_UFO aborted the mg7/madspace survey with

  RuntimeError: non-finite integral in channel '2.0' after 1000 samples
  (mean=nan, variance=nan)

while madevent integrated the same process fine. dim6top carries an FCNC
coupling order that DIM6=0 does not constrain, so MG5 keeps the four
flavour-off-diagonal channels (u c~, c u~, d s~, s d~ > t t~). They have 7
diagrams each but an amplitude of exactly zero, because their Wilson
coefficients are zero in the default param card. A channel like that should
contribute nothing; instead it poisoned the whole run.

The chain, all of it 0/0 on a channel with no amplitude anywhere:

  umami.cc built the per-diagram amp2 madspace samples from as
  numerator/denominator, the denominator being the sum of the numerators.

  kernel_collect_channel_weights then normalised the channel weights by the sum
  of those amp2.

  survey() divided by the subprocess mean to get its relative spread, which is
  a plain ZeroDivisionError once the nan above is gone and the channel
  correctly integrates to zero.

The matrix-element multichannel weights in process_sigmaKin_function.inc and
process_function_definitions.inc had the same division; they are guarded too,
for the consumers that ask for mulChannelWeight (mg7 does not, so only the
three above were load-bearing for this reproducer).

NONE OF THE C++ GUARDS MAY BE WRITTEN AS A TEST FOR ZERO. The generated code
is compiled with -ffast-math, and its -ffinite-math-only lets the compiler
assume the quotient is finite and delete such a guard as dead -- an explicit
`denominator == 0. ? 0. : ...` in umami.cc was silently removed, and only
reappeared when a printf next to it perturbed the optimiser. This is the same
trap as #117 and #516, which is why CrossSectionKernels.o is already built
-fno-fast-math. The denominators here are sums of |amp|^2 and so never
negative, so they are floored by adding the smallest normal instead: 0/tiny is
0, and every denominator a real amplitude produces is unchanged bit for bit.
That keeps the guard robust without taking -ffast-math off a hot file.

madspace's own kernel is not compiled that way, so it substitutes the norm and
falls back to the uniform distribution -- the channel weights have to remain a
normalised distribution, and with no amplitude to prefer one, none is.

Verified with dim6top_LO_UFO, ctG = 1, 13 TeV, fixed mu = 91.188,
NNPDF23_lo_as_0130_qed:

  p p > t t~ DIM6=0   mg7 736.223 +- 1.686 pb   madevent 736.2 +- 0.61 pb

and no regression: plain `sm` `p p > t t~` gives 718.8675807362097 pb, bit
identical to the same run before this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@theoheimel theoheimel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks reasonable

@oliviermattelaer
oliviermattelaer merged commit d5ac3e5 into main Sep 11, 2026
513 checks passed
@oliviermattelaer
oliviermattelaer deleted the claude/madspace-zero-me-channel-nan branch September 11, 2026 09:51
theoheimel added a commit that referenced this pull request Sep 11, 2026
roiser pushed a commit to roiser/MadGraph7 that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants