Repository navigation
parameter: logit raw coordinates are flat; delete the raw-cancellation clip (review 1.2.9) - #505
Merged
Merged
Conversation
…raw-cancellation clip (review 1.2.9) Every logit (two finite bounds) element was sampled through a pm.Normal(0, 1) raw coordinate whose -0.5*raw**2 section C cancelled with +0.5*raw**2, clipped at |raw| = 1e4 since the DC2018_128 PTDE runaway. The clip put a -0.5*(raw**2 - 1e8) wall in the "uniform" prior at 1e4 whitened widths from seed 0's anchor (a multi-seed microlensing start paid -1.5e8 nats at its close seeds) and had to be raised during the whitening probe (review 1.2.1). Stop cancelling. The raw RV keeps its name `<label>_raw` and one vector per parameter, but a vector with any logit element is now a pm.CustomDist whose density is -0.5*raw**2 on the non-logit elements only (a constant 0/1 mask, no pt.where) and -ln sqrt(2 pi) on every element -- kept so start logp, logged lp, per-term values at the start, the evidence normalization and the nested bridge are unchanged. Section C's potential is now the Jacobian plus the saturation guard, nothing else. With no large term to cancel there is no float64 residual for PTDE to ratchet, so there is nothing to clip. Removed: _RAW_CANCELLATION_CLIP, its shared variable, get/set/raised_ raw_cancellation_clip, whitening's _PROBE_RAW_CLIP and the probe-time raise; _CLIP_HI is now 0.5 * _WHITEN_MAX_STEP. The one thing the wall did that was needed (a contour within reach for a logit element too tight by more than one round) is replaced by _flat_logit_is_clipped: a logit element cannot be flat, so a flat probe of one is booked at the reach and escalates. Docs: parameter.md (new ruling section), whitening.md (third-limit bullet, the "What flat means" table re-measured), orbit.md (node-degeneracy labels, re-measured), system.py/polish.py/run.py comments, testing-cache.md. Tests: tests/test_flat_logit_raw.py (uniform out to 1e7 widths, a seed 2e4 widths away pays only the Jacobian, no runaway out to 1e17, NaN still propagates, mixed vectors, all four NUTS backends sample it); test_whitening.py's clip test replaced by the flat-booking tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S5pG2s9aJEo6Xqk6hWvnkW
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.
What
Review item 1.2.9 (JDE ruling 2026-10-27: do it now, verify no regression). A logit-transformed element (two finite bounds) no longer gives its raw coordinate an
N(0, 1)density that section C then cancels. Its raw coordinate is flat, so its whole prior is thelogit_uniform_prior.<label>potential: the sigmoid Jacobian plus the existing saturation guard. Non-logit elements keep theirN(0, 1), which is their prior.Why: the
-0.5*raw**2/+0.5*raw**2pair added up to zero, but in float64 it left a residual of~raw**2 * 2**-52. The DC2018_128 PTDE runaway fed on that residual. The clip at|raw| = 1e4that bounded it put a-0.5*(raw**2 - 1e8)wall into the "uniform" prior, 1e4 whitened widths from seed 0's anchor. The whitening probe had to raise that wall (review 1.2.1). It also cost the close seeds of a multi-seed OGLE-2013-BLG-0341 start -1.5e8 nats. Making the raw coordinate flat deletes both terms. With nothing left to cancel there is no residual and nothing to clip.Design
<label>_raw, untransformed, with the same initval. Trace reuse, restart files (mkparam), polish, PTDE start dicts, the persisted whitening (_whitening.jsonschema unchanged) andtrace_metaall key on that name, so a split was ruled out.pm.CustomDist._raw_logp(module-level, so it pickles) ismask * (-0.5*raw**2) - ln sqrt(2 pi). The mask is a constant 0/1 vector, 1 on the non-logit elements. There is nopt.where, and a NaN still propagates on every element. A vector with no logit element keeps the plainpm.Normal.-ln sqrt(2 pi)per logit element is kept on purpose. Because of it, start logp, logged lp, per-term values atraw = 0, the evidence normalization (evidence_log_norm_terms) and the nested sampler's u-space bridge are all unchanged.random=draws N(0, 1) on every element, so.eval()on a graph that still contains its RVs behaves as before. This is documented as NOT being the prior. Nothing in exozippy samples a prior predictive._RAW_CANCELLATION_CLIP, its shared variable,get_/set_/raised_raw_cancellation_clip, whitening's_PROBE_RAW_CLIPand the probe-time raise._CLIP_HIbecomes0.5 * _WHITEN_MAX_STEP.|delta lq| ~ 1.5. So the newwhitening._flat_logit_is_clippedbooks a flat probe of a logit element at the reach, and escalation proceeds as before. A flat non-logit element keeps NaN.Audit (what relied on a proper raw N(0, 1) for logit elements)
sample_prior_predictive/pm.draw: no callers insrc. Graph.eval()with RVs left in:tests/test_element_parameterization.pydid this. Keeping it working is the reason for the N(0, 1)random=.ptde.py,system._jitter_*,samplers/_common.py): these are jitter proposals around centers and never use the raw density.set_whiteninginvariance: still a pure change of variables. The flat raw plus the Jacobian gives a uniform prior for any anchor and scale.samplers/nested.py: it uses0.5*raw**2 + ln sqrt(2 pi)only for its own Normal-prior class, which it detects by saturation, so logit elements are unaffected. The constant is kept, so its likelihood is unchanged.evidence.py,parameter.md): unchanged, because the constant is kept.system._add_branch_mixtures): free-RV priors are not in the mixture. The logit potential depends only on raw and factors out, as before.diagnostics.point_logps, acceptance fixtures, example decomposition scripts): atraw = 0every term is unchanged. Away from the start,-0.5*raw**2leavesRV:<x>_rawand+0.5*raw**2leavesPOT:logit_uniform_prior.<x>, and the total stays the same. The example scripts already collapse the pair. Their comments are updated.mulensing/physics.py,mulensevent.py): still holds, becausemask * NaN**2is NaN. Pinned by a test.orbit.md,system.fold_degenerate_draws): re-measured. tc's raw-space terms now differ by only its logit Jacobian, -1.386 -> -2.604. On master they differed by -2.97e10 against a correction capped at +5e7. The docs are updated.No runaway. Far from the anchor a logit element's logp is
log q(1-q) ~ -|lq|plus the quadratic saturation guard. Both are computed directly, both are monotone past the logistic peak, and neither ever exceeds the prior's own maximum. A sampler that accepts increases has nothing to ratchet.tests/test_flat_logit_raw.pypins this out to|raw| = 1e17, plus 2000 random large excursions.test_runaway_logp_regression.py, which replays the historical DC2018_128 runaway draw, still gives a hugely negative logp, and its good-draw lp is unchanged.Bit-identity (start logp, repr, master bfa0ea1 vs this branch)
The same sweep driver as #498/#501/#502/#503 (every top-level shipped config), plus a second point displaced +0.37 on every raw element. Table and scripts are in the session scratch; summary:
transit.baseline_raw-7.35150826563738 -> -7.351508265637381). The cause is that the masked quadratic constant-folds away, so the n copies of-ln sqrt(2 pi)are summed one at a time.pm.Normal's graph producedn*c. This is float summation order, not a model change.Total-logp claim: identical inside the old clip, as measured above. Past it, the old wall is gone; that is the fix.
OGLE-2013-BLG-0341 (private config, built read-only from a scratch copy,
ulimit -v 12000000, serial; peak RSS 0.95 GB)The closes lose the -1.515e8 wall and land where their data fit puts them. Seeds 1 and 4 move by about 1e-10 nats. That is the old cancellation's round-off at
|raw| ~ 2e3, gone.Tests
tests/test_flat_logit_raw.py, which fails on master at every point past the old clip:+/-1e7widths from the anchor;pm.Normal;tests/test_whitening.py: the clip-raise test is replaced by tests for the flat-logit booking and for a non-logit NaN surviving. The ">= 3 escalation rounds" case moves from 1e-19 to 1e-28, because one round now reaches ~9 orders instead of 6. The "What flat means" table inwhitening.mdis re-measured; 1e-9 now resolves in one round.-m "not slow"-n6: 4513 passed, 1 skipped. Targeted slow set (mulens acceptance a/b, multiseed PTDE, ptde, ptde_async, nested, runaway regression, polish, nuts_start, node_degeneracy, whitening_anchor, evidence, mode evidence, the new file): 113 passed. CI is the gate for the rest.docs/testing-cache.md).Docs
parameter.md: new section "The raw coordinate of a logit element is FLAT -- do not give it back an N(0, 1), and do not re-add a clip", written as a why-it-is-this-way ruling.whitening.md: the third-limit bullet is now history plus what replaces it; the flat table is re-measured.orbit.md: node-degeneracy labels re-measured.system.py,polish.py,run.pyandmulensing/*.docs/testing-cache.md.Review item 1.2.9.
🤖 Generated with Claude Code
https://claude.ai/code/session_01S5pG2s9aJEo6Xqk6hWvnkW