Skip to content

dmft/hybridization: apply the MC sign once in measure_Gl - #99

Merged
Ooolab merged 2 commits into
ALPSim:masterfrom
skilledwolf:fix/hyb-legendre-sign
Jul 22, 2026
Merged

dmft/hybridization: apply the MC sign once in measure_Gl#99
Ooolab merged 2 commits into
ALPSim:masterfrom
skilledwolf:fix/hyb-legendre-sign

Conversation

@skilledwolf

Copy link
Copy Markdown
Collaborator

Problem

hybmatrix::measure_Gl multiplies the Monte Carlo configuration sign into both factors of every contribution: once into M_ji and once into bubble_sign:

double M_ji = operator() (j, i) * sign;
...
double bubble_sign = sign;            // (flipped on tau wraparound)
...
double gl = M_ji*legendre_p*bubble_sign;   // carries sign^2 = 1

Each accumulated term therefore carries sign² = 1, so the Legendre estimators Gl/Fl ignore the configuration sign entirely — while measure_G (binned τ) and measure_Gw (Matsubara) apply it exactly once via bubble_sign. In any run with ⟨sign⟩ ≠ 1 the Legendre channel accumulates the unweighted average while the other channels accumulate the sign-weighted numerator: the channels are mutually inconsistent and Gl/Fl are biased.

Fix

One line: drop the sign from M_ji, so it enters exactly once through bubble_sign, matching measure_G/measure_Gw. The τ-wraparound anticommutator flip on bubble_sign is unchanged.

Sign-free runs (the common density-density segment case, where sign = 1 throughout) are bit-identical — which is how this survived; MEASURE_legendre also defaults off.

How to verify

There is deliberately no runtime reproducer in this PR: exercising the bug end-to-end needs a model with ⟨sign⟩ ≠ 1 and MEASURE_legendre=1, and there is no signful reference result in-tree to compare against. The bug is instead verifiable by inspection in under a minute at the quoted lines of measure_Gl: M_ji carries sign, bubble_sign carries sign again, and every accumulated term is M_ji * legendre_p * bubble_sign — so the configuration sign enters squared and cancels identically, while measure_G/measure_Gw in the same file apply it exactly once via bubble_sign. Equivalently: flip the sign of sign at the call site and observe that Gl/Fl are unchanged while G/Gw negate.

The downstream fork pins the fix with a sign-linearity test: a deterministic two-operator-pair hybridization matrix built through the production insert path must satisfy measure(sign=−1) == −measure(sign=+1) elementwise for all of G/F/Gl/Fl. Before the change Gl/Fl return identical values for both signs (G/F already pass); after it all four channels are exactly sign-antisymmetric. (That test rig depends on fork-side scaffolding and is not ported here; happy to port it if wanted.) The solver builds cleanly with the change on current master (73b331006).

Provenance

Found in a systematic estimator audit in a downstream ALPS modernization fork; the fix is identical there and covered by the sign-linearity lock described above.

hybmatrix::measure_Gl multiplied the Monte Carlo configuration sign
into BOTH factors of every contribution: once into M_ji and once into
bubble_sign. Each accumulated term therefore carried sign^2 = 1, so
the Legendre estimators Gl/Fl ignored the configuration sign entirely,
while measure_G (binned tau) and measure_Gw (Matsubara) apply it
exactly once via bubble_sign. In any run with <sign> != 1 the Legendre
channel accumulates the unweighted average while the other channels
accumulate the sign-weighted numerator: the channels are mutually
inconsistent and Gl/Fl are biased.

Apply the sign once, through bubble_sign, matching measure_G. The
tau-wraparound anticommutator flip on bubble_sign is unchanged.
Sign-free runs (the common density-density segment case, where
sign = 1 throughout) are bit-identical, which is how this survived:
MEASURE_legendre also defaults off.

Found by the ALPS modernization fork in a systematic estimator audit
and confirmed there with a sign-linearity test: a deterministic
two-operator-pair hybridization matrix built through the production
insert path must satisfy measure(sign=-1) == -measure(sign=+1)
elementwise for all of G/F/Gl/Fl. Before this change Gl/Fl return
identical values for sign = +1 and sign = -1 (G/F already pass);
after it all four channels are exactly sign-antisymmetric.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@skilledwolf
skilledwolf marked this pull request as ready for review July 19, 2026 13:44
@egull egull self-assigned this Jul 19, 2026
@egull
egull requested review from Ooolab and egull July 20, 2026 06:50

@egull egull 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 good. Likely hard to test in a 'real world' scenario since the typical cases we use for this code are sign-free.

@Ooolab
Ooolab merged commit 20ba7ff into ALPSim:master Jul 22, 2026
44 checks passed
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.

3 participants