madspace: evaluate the matrix element on the momenta the event is written with - #170
Open
oliviermattelaer wants to merge 1 commit into
Open
oliviermattelaer wants to merge 1 commit into
oliviermattelaer wants to merge 1 commit into
Conversation
…tten with
The initial-state mirror covers the beam-swapped copy of an asymmetric
initial state by rotating the event by pi about x (py, pz -> -py, -pz,
kernel_mirror_momenta), which moves each leg onto the other beam. Only the
written event was rotated: Integrand::build_common_part kept feeding the
cross section the momenta as generated.
That is sound only if |M|^2 is invariant under the rotation. It is for any
Lorentz invariant matrix element, and for a longitudinally polarised one.
It is not for a transversely polarised particle evaluated in a frame that
holds it at rest: HELAS quantises such a particle along the *frame* z axis
(the pp == 0 branch of vxxxxx) rather than along its own momentum, the
mirror flips that axis, and the + and - states swap. Measured in FP64 on
g q > z{+} q evaluated in the Z rest frame: 48% between |M|^2 on the
mirrored and unmirrored momenta, against 1e-15 for the unpolarised process,
for z{0}, and for z{+} in any frame that does not hold the Z at rest.
End to end this broke a symmetry that must hold: on a symmetric pp collider
the two beams cannot be told apart, and the mirror is what averages over
them, so sigma(p p > z{+} j) and sigma(p p > z{-} j) have to be equal. They
came out 4485 +- 39 pb against 6060 +- 54 pb, 23 sigma apart; they now agree
within 1 sigma. The sum over the three polarisations was already correct,
which is why no unpolarised result moves (p p > z j: 12420 +- 69 pb before,
12430 +- 110 pb after) and why nothing caught this.
Two other things already assumed the fixed behaviour:
- madevent flips the momentum array itself and passes that same array to
both the matrix element and the event record
(super_auto_dsig_group_v4.inc, "Flip momenta (rotate around x axis)"),
so this aligns madspace with it rather than inventing a convention;
- SystematicsCalculator::matrix_elements reads the momenta back out of the
event buffer, i.e. the mirrored ones, so the nominal weight and its
scale/PDF variations were computed on different momenta for the same
event.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The initial-state mirror covers the beam-swapped copy of an asymmetric initial
state by rotating the event by pi about x (
py, pz -> -py, -pz,kernel_mirror_momenta), which moves each leg onto the other beam. Only thewritten event was rotated:
Integrand::build_common_partkept feeding thecross section
momenta_acc, the momenta as generated.That is sound only if
|M|^2is invariant under the rotation.Why it is wrong
It is invariant for any Lorentz invariant matrix element, and for a
longitudinally polarised one. It is not for a transversely polarised
particle evaluated in a frame that holds it at rest: HELAS quantises such a
particle along the frame z axis (the
pp == 0branch ofvxxxxx) ratherthan along its own momentum, the mirror flips that axis, and the + and - states
swap.
Measured in FP64,
|M(mirror p)|^2 / |M(p)|^2 - 1ong q > z{+} q:z{0}z{+}So it is not "any boosted frame" — it is specifically a transversely polarised
particle held at rest, which is what asking for the Z rest frame does.
What it cost
On a symmetric pp collider the two beams cannot be told apart, and the mirror is
precisely what averages over them, so
sigma(p p > z{+} j)andsigma(p p > z{-} j)have to be equal. They were not:p p > z{+} jp p > z{-} jThe sum over the three polarisations was always right (10545 +- 67 vs
10564 +- 46), so the bug only misallocated between + and -. That is why no
unpolarised result moves —
p p > z jis 12420 +- 69 pb before and12430 +- 110 pb after — and why nothing ever caught it.
The fix
Evaluate the matrix element on the momenta the event is written with. No
symmetry assumption at all. Two things already assumed this:
passes that same array to both the matrix element and the event record
(
super_auto_dsig_group_v4.inc, "Flip momenta (rotate around x axis)";write_leshouche's "flip back particle 1 and 2" only relabels the two beamlegs, it does not undo the rotation). So this aligns madspace with madevent
rather than inventing a convention.
SystematicsCalculator::matrix_elementsreads the momenta back out of theevent buffer, i.e. the mirrored ones, so the nominal weight and its
scale/PDF variations were computed on different momenta for the same event.
Impact and testing
Half the events of a mirrored subprocess now have their matrix element
evaluated on sign-flipped momenta, so existing results shift in the last bits
and LHE hashes move. The reproducibility tests compare runs to each other and
the xsec references have a 1% tolerance, so nothing should break — the
unpolarised control above is the direct check.
The regression test that guards this needs the
me_framerun-card setting toreach the broken branch at all, so it travels with the follow-up PR that adds
me_frame(test_polarised_mirror_symmetry_mg7, CI jobacceptancetest_mg7_polarised_mirror). It fails on this PR's parent commit andpasses with it.
madspace test suite: 1545 pass. Full unit suite: unchanged against main (the
two reds on main — a missing scipy and a MadLoop IOTest golden drift that only
appears in a full-suite run — reproduce on a clean checkout).
🤖 Generated with Claude Code