Skip to content

portfolio: high-SNR L0 auto-rescue (landed 4/9 -> 8/9), save-samples export fix, extrinsic-stability tooling - #34

Merged
oshaughnessy-junior merged 25 commits into
rift_O4dfrom
rift_O4d_portfolio_freeze_tuning
Jul 29, 2026
Merged

portfolio: high-SNR L0 auto-rescue (landed 4/9 -> 8/9), save-samples export fix, extrinsic-stability tooling#34
oshaughnessy-junior merged 25 commits into
rift_O4dfrom
rift_O4d_portfolio_freeze_tuning

Conversation

@oshaughnessy-junior

@oshaughnessy-junior oshaughnessy-junior commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Follow-on to #28 (merged). Started as multi-event harness tooling; the branch now also carries the
high-SNR extrinsic study and the fix that came out of it. No default behaviour changes for
typical events — the one behaviour change is an opt-in flag gate (below).

1. L0 auto-rescue now works for the portfolio (the high-SNR fix)

On a very sharp high-SNR peak (SNR~80 best-fit point), AV contracts onto the peak or the wrong spot
~50/50, so a large fraction of independent runs collapse to n_eff~1. This is a bimodal lottery,
reproduced across every GMM cap, correlate-all, and coordinate configuration tried — one run is not
a posterior at any budget.

--sampler-warmstart-retry-neff (L0 auto-rescue) re-seeds a collapsed run from the peak it did
find
and re-runs — same-problem reuse, cannot bias, frame-safe. It was gated to standalone AV;
now enabled for a portfolio carrying an AV member.

Result on a 9-copy pool: landed fraction (n_eff>=5) 4/9 -> 8/9. 6 rescues fired, 5 converted to
clean lands, chronic n_eff~1 collapsers landed at 25-33. Cost: a rescued run does 2 passes.

Gate bug found doing this: opts.sampler_method is clobbered to 'GMM' during portfolio
member setup (the GMM branch sets it to force GMM arg-parsing), so for an AV+GMM portfolio every
downstream sampler_method == 'portfolio' test is False. The gate now detects a portfolio via
opts.sampler_portfolio (the member list, which is not clobbered). A separate cleanup of the clobber
itself is in progress — it also makes the portfolio-only setup block dead code (currently harmless,
since the GMM branch covers the gmm_adaptive forwarding).

2. --save-samples 0-row export fix (PR #35, cherry-picked)

The _rvs cleanup cumsummed log-weights and any -inf poisoned it, so peaked portfolio runs
exported 0 rows (even at n_eff~9). Fixed to cumsum linear weights with an -inf guard. This also
unblocked --extrinsic-proposal-output, which is the weight-correct export (the --save-samples XML
carries lnL only and must not be reweighted by likelihood for a shape check).

3. Study results (DESIGN_portfolio_freeze_policy.md)

  • n_eff is a lottery for every cap — a 3-seed read is survivorship bias; GPU runs are non-deterministic
    even at fixed --seed. Take >=8 draws.
  • Low n_eff == extrinsic MODE COLLAPSE: collapsed copies fit 1 GMM mode/group (lost sky ring,
    dL-inclination arc, phase-pol); landed copies fit 3-4 and agree copy-to-copy. Mode count detects it.
  • Pool copies weighted by reliability (n_eff); naive equal-weight pooling is biased by collapsed copies.
  • --internal-gmm-correlate-all is strictly worse here (needs too many eff-samples/component).
  • --force-adapt-all --internal-rotate-phase improves the landers (n_eff ~50 vs ~30) but does not
    change the collapse rate. Coordinate-transform flags need a frame-matched warm-start seed
    --sampler-warmstart-samples maps by column NAME without transforming values, so a physical seed
    poisons a rotated proposal (0/9 landed until the seed was phase-rotated).

4. Tooling

bench_onsource.sh / bench_onsource_ensemble.sh (seed ensembles on the pinned best-fit point),
compare_extrinsic_breadcrumbs.py (cross-copy extrinsic-posterior stability + reliability-weighted
pooling), bench_coverfrac.sh, bench_multi_event.py container mode.

Transferable integrator lore extracted to a public repo:
https://github.com/oshaughnessy-junior/rift-integrator-lore

Validation note

The shape-recovery merge gate has not been re-run since the export fix + rescue gate change;
worth running before merge. The rescue gate change only activates with the opt-in
--sampler-warmstart-retry-neff flag (default None = off), so default behaviour is unchanged.

🤖 Generated with Claude Code

oshaughnessy-junior and others added 14 commits July 23, 2026 07:09
…1) for condor follow-up

The multi-event follow-up runs under condor with the container supplied by MY.SingularityImage, so
the harness must build the FULL production args (gwsignal / SEOBNRv5PHM / cosmo prior) while exec'ing
python DIRECTLY rather than nesting singularity inside the job.  container= now selects the ARGS and
wrap= selects whether to nest singularity; NO_SINGULARITY=1 sets wrap=False.

Condor wiring lives outside the repo (~/rift_mev_followup/) and deliberately pins ONE image with a
require_gpus capability range instead of the container family's $$/ifThenElse per-machine selection:
per CITLOCAL_BREADCRUMB_gpus_capability_undefined_holds.md that expression HOLDS on ~45% of CIT GPU
slots because it reads machine-level TARGET.GPUs_Capability (frequently undefined), whereas
require_gpus matches the per-GPU Capability.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…es NOT generalise

4 typical O4 events, warm, --n-eff 30, real SEOBNRv5PHM (cuda128 container on idle Blackwell nodes):

  event      no-clip lnZ(neff)  clip lnZ(neff)  dlnZ   neff_ratio
  S231026ab  17.54 (28.9)       17.49 (29.8)    -0.05  x1.03
  S240426s   29.74 (31.2)       29.56 (30.3)    -0.18  x0.97
  S240513ei  83.76 (3.1)        83.83 (1.3)     +0.07  x0.42
  S240703ad  41.89 (3.3)        42.27 (5.0)     +0.38  x1.53

Clipping's dramatic S250114ax result (2.3x at the production target) is SPECIFIC to that event's
extreme heavy-tailed pathology.  On typical events it is a near-noop; on the two under-converged hard
events it is a wash within the n_eff~1-5 scatter.  ln Z agrees everywhere (|dlnZ| <= 0.38, MC error)
-- the portfolio replicates the AV integral with or without clipping.  Confirms opt-in/default-off was
the right call.  Closes the study's last open question.

Also confirmed en route: the cuda128/cc90-120 container runs SEOBNRv5PHM+cupy on Blackwell (CC 12.0),
matching the A100/cuda118 result -- the path that let this run bypass the saturated A100 condor pool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… floor on GPU

Resolves a cross-box reproduction gap reported from cardassia on PR #26 and answers its ask.

ROOT CAUSE of the gap: TARGET-POINT MISMATCH, not CPU-vs-GPU and not the warm start.  cardassia
integrated the loud on-source coinc point (lnLmax~3020); the reference matrix and all quoted numbers
use the iteration-0 worker point (overlap-grid-0.xml.gz --event 0, lnLmax~1212).  Their own COLD run
(n_eff=1.00 vs reference ~6 / CIT av_cold 3.7) proves it: cold uses no seed, so a cold discrepancy
can only come from the likelihood.  Seed volumes agree to 3 digits across boxes (0.056/0.649 vs
0.0567/0.647), so there is no backend divergence in the warm start.

MEASURED (reference point, GPU, rescue OFF, --n-chunk 10000, nmax 4M), varying only the coverage floor:

  run           cover rescue  Neff>=5   >=10    >=20    >=50    >=100   final  lnZ
  av_warm(ref)  0.05  ON      0.695M  1.374M  1.691M  2.268M  3.638M  100.2  1191.79
  cf_cover005   0.05  OFF     0.837M  1.146M  1.546M  2.234M    --     64.2  1192.16
  cf_cover050   0.5   OFF     1.350M    --      --      --      --      8.1  1193.83

=> The warm-start demo does NOT survive the bias-safe floor (8.1 @4m vs the 100 @3.35M headline).
   Shape-gate verdict: n_eff 8.1 << 100 => STARVED, shape untestable at bias-safe coverage even on GPU.
   Part of the headline was the RESCUE, not the seed (cover 0.05 rescue-OFF gives 64.2, not 100).
   lnZ 1191.8/1192.2 at cover 0.05 vs 1193.8 at 0.5 -- under-covered runs sit ~2 nats LOW, the
   direction under-coverage predicts (caveat: 0.5 run n_eff=8.1, ~0.35 nat MC error; repeat before
   quoting as a bias measurement).

Note --sampler-warmstart-cover-frac ALREADY defaults to 0.5; the demo ini's 0.05 was an override into
under-covered territory.

The harness pins the reference point explicitly and documents that it is deliberately NOT the
on-source coinc point, so this mismatch cannot recur silently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…GMM both fail alone

Distinguishes the two problems that were conflated across this study and PR #26:
 * TRIAL grid point (overlap-grid-0.xml.gz --event 0, m1/m2 28.29/26.69, lnLmax~1212): integrator
   policy A/B; not the science target.
 * BEST-FIT on-source point (target_params.xml.gz, m1/m2 37.71/34.03, lnLmax~3040, rho~78): the
   evaluation that must work for a loud event, and where AV stalls.

Measured (GPU, warm, bias-safe cover 0.5):
  AV alone                 : n_eff 1.0  @4m  (stalls; confirms cardassia CPU result on GPU)
  GMM alone (adaptive)     : NaN chunk-1     (no coverage floor -> weights blow up)
  AV+GMM portfolio adaptive: n_eff 14.7 @4m  lnZ 3016  (~15x AV; works where NEITHER works alone)

Mechanism: a GMM-peak + AV-coverage event.  GMM wraps the correlated peak AV cannot; the "dead" AV
member (nan per-chunk n_ess in 381/400 chunks, parked at the 1% floor) is NOT wasted -- its broad
warm density enters q_mix = frac_AV q_AV + frac_GMM q_GMM and provides the coverage floor that keeps
GMM's weights bounded.  Drop AV -> GMM NaNs; drop GMM -> AV stalls.  This is the clearest
demonstration of why the portfolio exists, and why the never-freeze/allocation machinery matters (it
hands the budget to whichever member is working -- here GMM).

Harness bench_onsource.sh pins the best-fit point and documents it is deliberately NOT the trial
point, so the mismatch that broke the PR #26 reproduction cannot recur.  14.7 is a rescue, not yet
target; whether more adaptive GMM coverage pushes higher is under test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (14.7 -> 56.1), unbiased

Best-fit point (target_params.xml.gz, lnLmax~3040), AV+GMM portfolio, warm cover 0.5, GPU:
  GMM BIC cap 8,  inflate 1.0 : n_eff 14.7  lnZ 3016.13
  GMM BIC cap 16, inflate 1.3 : n_eff 56.1  lnZ 3016.08   <-- 56x standalone AV (which stalls at 1.0)

lnZ identical -> real efficiency gain, not a coverage-shortcut bias.  Confirms the "GMM event with
ADAPTIVE coverage" framing quantitatively: the rescue lever is GMM adaptive coverage (BIC component
count + inflation).  Remaining gap to production n_eff looks like coverage/budget tuning, not a
fundamental barrier.  Pushing further (cap 24 / inflate 1.5) under test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1.3, over-cranking biases

Extending the coverage ladder on the best-fit high-SNR point:
  cap 8 / infl 1.0 : n_eff 14.7  lnZ 3016.13
  cap 16/ infl 1.3 : n_eff 56.1  lnZ 3016.08   <-- sweet spot, 56x AV, unbiased
  cap 24/ infl 1.5 : n_eff  2.3  lnZ 3009.5     <-- collapse; lnZ -6.6 nats => biasing, not just noise

More coverage is NOT better: over-inflation produces an over-wide GMM proposal whose rare enormous
weights both crush n_eff and bias lnZ low (the same heavy-tail mode weight clipping targets).  So the
"GMM event with adaptive coverage" rescue is real but the coverage knobs need tuning to a sweet spot,
not maximizing.  Isolating whether BIC cap or inflation drives the collapse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ples exports

n_eff alone hides a wrong/absent posterior SHAPE.  This reads the extrinsic cloud saved by
--save-samples (weights in the alpha1/loglikelihood column of the sim_inspiral table) and reports,
per extrinsic parameter, the WEIGHTED marginal (mean/std) plus global failure signatures: Kish n_eff,
max single-sample weight FRACTION (outlier dominance -- the over-broad-GMM failure), and raw count.

First use already paid off: a flaky-collapsed high-SNR run (cap16/inflate1.3, seed 1, n_eff 1.3)
saved ZERO rows -- after fairdraw pruning nothing survives.  So a collapse is not a wrong shape, it
is a DEGENERATE/empty posterior, which the shape check makes unmistakable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…le in this regime

cap16/inflate1.3 n_eff across 5 seeds: {1.2,1.3,1.5,9.2,56.1}, median ~1.5 -- the
56.1 sweet-spot was a lucky draw, not typical. One low-n_eff run on a high-SNR event
is not a posterior; pool many copies (weighted by reliability, not naively) or find a
proposal that reliably lands high n_eff. cap-too-high confirmed as the collapse mode.

Documents that --save-samples exports 0 rows for peaked portfolio runs at 4 layers
(fairdraw / save-P=0.1 / log-weight cumsum bug / XML carries lnL-not-IS-weight);
correct weight-aware shape check is --extrinsic-proposal-output. Bug flagged separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…inf guard)

Step 2 of the _rvs cleanup in mcsamplerPortfolio.py paired the sorted index
with the LOG-weight ln_wt[k] and cumsummed that, so the "cumulative weight"
was never a cumulative probability. Any -inf ln_wt entry (rejected/underflowed
sample) poisoned the cumsum (cum_sum[-1] = -inf -> nan on normalize), and the
cum_sum > deltaP mask kept 0 rows -- 0 samples exported even at n_eff~9.

Fix: pair/cumsum the LINEAR weight wt[k] (already computed one line earlier),
matching the canonical mcsampler/mcsamplerEnsemble truncation, and map any
non-finite log-weight to -inf (-> zero linear weight) with a degenerate
fallback that keeps all rows if no finite-weight sample survives. Verified to
reproduce canonical linear-weight index selection exactly on finite peaked
input and to keep all real samples (dropping only zero-weight rejects) when
-inf entries are present. Change is confined to the portfolio Step 2 block;
AV/GMM standalone samplers are untouched.

Also document (integrate_likelihood_extrinsic_batchmode) that the sparse
sim_inspiral XML from --save-samples carries lnL only and must not be
reweighted by likelihood for a weighted-posterior/shape check; point users to
the ASCII --extrinsic-proposal-output (full log-weight) / --calibration-export-posterior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ab28fec)
…tory; add ensemble+pooling harness

Correcting my own 3-seed 'cap8 is reliably modest' claim (survivorship bias): 10 cap8
draws span 1->70 with ~40% collapsed to ~1 -- statistically the same bimodal lottery as
cap16. correlate-all is strictly worse (collapses 2/3, lnZ 11 nats low). So no single-run
config is a posterior on this high-SNR best-fit point; the robust recipe is MANY copies
pooled by reliability (the reviewer's original point, now on data).

Harness: bench_onsource_ensemble.sh (portfolio must be selected explicitly -- the driver
default adaptive_cartesian_gpu is NOT the portfolio/AV) + compare_extrinsic_breadcrumbs.py
(weight-correct per-group posterior via --extrinsic-proposal-output; reliability-weighted
vs naive pooling; collapse detector = out-of-bounds degenerate GMM). Weight-correct export
unblocked by PR #35 (cherry-picked).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pool by reliability

9-copy cap8 pool (4 landed n_eff 15-41, 5 collapsed 1-3): weight-correct per-group GMM
fits show collapsed runs fit 1 mode/group (lost sky ring / dL-i arc / phase-pol) while
landed runs fit 3-4 modes -- so low n_eff IS an extrinsic-degeneracy collapse, detectable
by n_eff or mode count. Landed copies agree (sky mean ~0.01, incl ~0.02); phase-pol is the
one genuinely-broad direction. Reliability-weighted pooling tracks good-only; naive pooling
is biased by collapsed copies. Kish-over-n_eff effective #copies = 4.1 of 9.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k lottery; enable L0 auto-rescue for portfolio

Frame-matched rotate-phase + force-adapt-all: landed fraction unchanged (4/9) but landed n_eff
up (41-52 vs 15-41). Lottery is robust across all configs -> root cause is AV peak-lock, not
proposal/coordinates. Relax --sampler-warmstart-retry-neff (L0 auto-rescue) gate from AV-only to
AV|portfolio so a collapsed portfolio run re-seeds from its own peak (frame-safe, cannot bias).
Documents the coordinate-flag / warm-start-seed frame-mismatch confound.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lio (sampler_method is clobbered to GMM)

The prior gate (sampler_method in AV|portfolio) never matched for an AV+GMM portfolio: line 1231
clobbers opts.sampler_method to 'GMM' during member setup (to force GMM arg-parsing). Detect the
portfolio via opts.sampler_portfolio instead. Verified: rescue now fires for the portfolio
(--sampler-warmstart-retry-neff 100 forced a 2nd pass; n_eff 7.2 -> 43.4 after peak re-seed).
Same clobber makes the portfolio block at ~1641 dead code (harmless: the GMM branch picks up the
gmm_adaptive forwarding) -- flagged for separate cleanup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…> 8/9

--sampler-warmstart-retry-neff 5 (portfolio-enabled) re-seeds each collapsed run from its own
peak: 6/9 rescues fired, 5 -> clean lands (chronic n_eff~1 collapsers s11/s14 now 25-33). The
rescue attacks the COLLAPSE RATE (coordinates/adaptation only helped the landers). Validated
high-SNR recipe recorded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oshaughnessy-junior oshaughnessy-junior changed the title bench_multi_event: already-inside-container mode for the condor multi-event follow-up portfolio: high-SNR L0 auto-rescue (landed 4/9 -> 8/9), save-samples export fix, extrinsic-stability tooling Jul 26, 2026
oshaughnessy-junior and others added 11 commits July 26, 2026 03:08
The portfolio member loop set opts.sampler_method='GMM' to force the GMM-specific argument
blocks to run for a GMM member. That silently broke EVERY downstream 'portfolio' test:
 - the portfolio-only setup block became dead code,
 - the L0 auto-rescue gate never fired for a portfolio (fixed by workaround in 76e1ea4),
 - a portfolio with --internal-use-lnL took GMM's branch and got return_lnI, which the
   portfolio does not consume.

Replace with a non-destructive use_gmm_member flag and use_gmm_args = (method=='GMM') or
use_gmm_member. Re-key the three blocks that genuinely need GMM handling for a portfolio
(GMM arg-parsing, per-iteration distance/inclination reset, --force-reset-all) -- all touch
gmm_dict, so they must still fire. Guard the now-live portfolio block against
double-specifying gmm_adaptive: the GMM block's per-group DICT is the path every portfolio
benchmark exercised, so the portfolio block's scalar cap now only applies when there is no
GMM member. L0 gate simplified back to sampler_method in ('AV','portfolio').

Regression (seed 13, cap8+force-adapt-all+rotate-phase+retry-neff 5): identical setup
forwarding ('gmm_adaptive': {(4,5):8,(3,2):8,(0,1):8}), rescue fired (cold n_eff 2.9<5,
2 passes), landed n_eff 29.3 lnZ 3017.30 (pre-cleanup 47.0/GPU-nondeterministic, same regime).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… lnZ-bias claim retracted)

The n_eff lottery was found late, so several earlier claims rest on n=1. Explicitly downgrade
the cap-ladder numbers (incl. the cap24 'over-cranking biases lnZ' claim) and correlate-all
(n=3) to suggestive; keep never-freeze, the multi-event lnZ replication (unbiasedness, structural),
the lottery, mode-collapse and the L0 rescue as robust. Record that the sampler_method clobber
does NOT invalidate anything (return_lnI is never read by the portfolio; use_lnL was set either
way) and flag the open question of whether the lottery is high-SNR-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ime True)

Nearest-sample-bin time evaluation injects a superfluous non-smoothness into the extrinsic
likelihood surface, so the samplers chase a discretization artifact rather than physics.
Cubic interpolation removes it and makes convergence more robust. Requires the maintained
NoLoop likelihood (the --vectorized --gpu --force-xpy combo the bench already sets).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… fires on degenerate termination

(1) mcsamplerEnsemble.update_sampling_prior filtered NaN-weight samples by REASSIGNING
ln_weights, which is loop-invariant (built once, before the loop over dim_groups). The first
group containing any NaN shrank it (10000 -> 8686); every later group rebuilt temp_samples at
full length but reused the stale shorter weights -> IndexError 'boolean index did not match
indexed array' inside GMM.update/fit. Only reachable when weights contain NaN, i.e. a
degenerate/cold pass -- which is why warm runs never hit it and every COLD AV+GMM portfolio
start on the high-SNR point died at chunk ~8 with no output. Filter into a loop-LOCAL
ln_weights_group instead. Verified PRE-EXISTING: base rift_O4d crashes identically.

(2) L0 auto-rescue treated neff=None as 'skip'. But mcsamplerPortfolio/AV return
(None,None,None,None) from their early-termination branch precisely when the live volume never
finds finite in-volume samples -- the cold, very sharp peak the rescue exists for. Such a pass
still populates _rvs, so the peak-seed is available. Treat neff=None as below-threshold.

(3) The FAILED ANALYSIS handler now prints the traceback; the bare message ('boolean index did
not match...') was not enough to locate a fault inside the sampler stack, and this handler is
often the only record a batch job leaves.

(4) bench_onsource.sh: WT is env-overridable so the same command line can be run against a base
worktree for A/B.

Verified: cold portfolio start now runs to completion (n_eff 2.5 @300K) and the rescue fires.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…py was 11 nats wrong

Post-fix cold: 9/9 produce output (was 0/9, the pre-existing crash), 5/9 land. But among landers
lnZ spans 12 nats and the HIGHEST-n_eff copy (58) is the outlier, 11 nats below a 4-copy consensus
that agrees to 1.4 nats. n_eff measures weight concentration, not coverage: a run that locks onto
one narrow region looks confident and is wrong. Recommend consensus/median across copies over
n_eff-argmax or n_eff-weighted pooling. Copies are needed to DETECT a bad run, not just find a good one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collected asynchronously. Raw measured numbers only (no interpretation): per-config n_eff, lnZ and
final AV draw fraction for the cold high-SNR ensemble, plus the lnZ spread per config against the
12.2-nat no-floor baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…not just a floor

Measured on the loud-event best-fit point (cold, 4 seeds, judged by lnZ consensus not n_eff):
  no floor, cap8   : AV share -> 0.0099 (GMM-alone), lnZ spread 12.2 nats, highest-n_eff copy 11 low
  cap3, no floor   : AV share -> 0.0099,             lnZ spread 11.9 nats, n_eff 123.6 with lnZ 10 low
  floor 0.25, cap8 : 3 seeds at floor + 1 RUNAWAY to AV=0.99, lnZ spread 5.1 nats
  floor 0.25, cap3 : AV pinned at floor on all 4,    lnZ spread 2.27 nats
Both degeneracies (peaked-member-only and VARAHA-only) strip q_mix of a component and let a missed
mode go uncovered -> lnZ silently low while n_eff looks good. A one-sided floor only blocks one of
them. --portfolio-varaha-max-frac adds the cap so the share is constrained to a BAND and the mixture
stays genuinely mixed by construction. Default 0 = no cap (fully back-compatible).

The band applies to the COMBINED VARAHA share, so it composes with MULTIPLE INSTANCES of the same
integrator: --sampler-portfolio AV --sampler-portfolio AV --sampler-portfolio GMM constructs two
independent AV members (sampler_types is not deduped; --sampler-portfolio-args aligns positionally),
and the band held their combined share at 0.34 in [0.25,0.75] in a smoke test. Kept as an available
option for hard cases rather than a default.

Also generalizes the enforcement to handle an all-zero group on either side.

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

Per TESTING.md, opt-in code must be probed with the flag ON -- the default-path merge gate is
bitwise-identical for opt-in paths and proves nothing about them. The proposed high-SNR settings
(--portfolio-varaha-min-frac / --portfolio-varaha-max-frac, and a reduced GMM BIC cap) were measured
only on a real event, never through the gate. Add three configurations:
  varaha floor .25 / varaha band .25-.75 / band + gmm cap3
scored by the gate's own evaluate(), so a PASS here passes by exactly the gate's criteria.

The GMM cap needed a small extension: it is not a portfolio attribute but lives on the GMM MEMBER's
integrator (gmm_adaptive), which the portfolio forwards via setup(); the probe patches AFTER build,
so it now reaches into portfolio_realizations for the reserved key '_gmm_adaptive_cap'.

The gate targets are NOT pathological, so these constraints should be near no-ops there; the point
is to prove they do not REGRESS shape recovery before proposing them anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… confirmation running)

band3 (floor .25 + cap .75 + GMM cap3) sd 0.85 / spread 1.79 nats; bkmd (floor + cap3) sd 1.02.
Neither lever alone helps: cap3 with unconstrained share is no better than baseline (5.43 vs 5.96)
and produced the study's worst confidently-wrong case (n_eff 123.6, lnZ 10 low). Records the n=4
fragility explicitly and that these settings have NOT yet cleared the gate probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…; gate PASSED

Extends baseline/bkmd/band3 to the same nine seeds. sd 5.04 -> 3.01/3.08, worst deviation from
median 10.4 -> 6.1/7.2 nats, but F-test p=0.083/0.092: NOT significant at n=9, and the cap adds
nothing measurable over the floor (p=0.48). Records that no config makes a single run trustworthy
(6+ nat worst deviation) so consensus pooling stays mandatory. Also records the gate PASS
(0 blocking regressions over 96 rows) and that the gate needs --jobs 1 to avoid its pool deadlock.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l three bars

varaha floor / varaha band / band+gmm cap3 all match flags-OFF verdicts on every probe target,
scored by the gate's own evaluate(). Settings are SAFE (merge gate + flag-ON probe both clear) but
the real-event benefit remains unproven (p~0.08 at n=9). Posture: keep opt-in, change no defaults.

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

Copy link
Copy Markdown
Owner Author

Auto-review update: the required 96-row CPU base-vs-candidate shape-recovery comparison is PASS: 0 blocking regressions with AV,GMM,portfolio strict (pre-existing failures/starved rows were unchanged). I also verified a clean merge with rift_O4d and compilation of changed Python.

This PR’s portfolio changes are opt-in, so default-path equivalence alone is insufficient. The required flag-on probe is currently running; I will record that final result before clearing the integrator gate. Non-blocking cleanup: git diff --check reports one trailing-whitespace line in DESIGN_portfolio_freeze_policy.md.

@oshaughnessy-junior

Copy link
Copy Markdown
Owner Author

Final integrator-gate update: the required flag-on portfolio probe also passed with 0 opt-in regressions across adaptive allocation, weight clipping, VARAHA floor/band, and band+GMM-cap configurations. Together with the base/candidate gate, the mandatory integrator validation is clear for this head. The only finding remains the non-blocking trailing whitespace in the design note.

@oshaughnessy-junior
oshaughnessy-junior merged commit 6a278a5 into rift_O4d Jul 29, 2026
19 checks passed
@oshaughnessy-junior
oshaughnessy-junior deleted the rift_O4d_portfolio_freeze_tuning branch August 1, 2026 10:33
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.

1 participant