integrators: portfolio GMM dead-member fix (n_comp=None) + AV draw-density repair (bin-ordered head slice) - #33
Conversation
… invalid n_comp, fix uncorrelated-group bounds shape Three related repairs to a silent-degradation chain found by the shape-gate probes (2026-07-22): 1. mcsamplerEnsemble.setup(): n_comp=None (the default!) flowed into the integrator and disabled ALL GMM training downstream with no warning; in default portfolio wiring the GMM member never trained and 'portfolio' ran as AV-only. Now defaults to n_comp=1 with a loud notice; n_comp=0 remains the explicit off-switch. 2. update_sampling_prior(): warn (once) instead of silently no-opping when n_comp is invalid for a group. 3. update_sampling_prior(): up-shape bare (2,) bounds rows for per-dimension (uncorrelated) groups before GMM.fit — same guard _sample() and the q-scoring path already had; latent until (1) because this line was never reached in default portfolio config, and it would have turned the silent no-op into a crash. Regression test: test/integrators/test_portfolio_gmm_member_trains.py (default-wired AV+GMM portfolio must train its GMM member; n_comp=0 must still disable). Differential shape-recovery gate run attached to the PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ered cloud sample_from_bins emits points grouped in lexicographic bin order, so rv[:n_to_get] covered only the first ~half of the live volume while sampling_density (hence portfolio q_mix) claimed uniform coverage of all occupied bins -- a latent density lie that systematically biased shape recovery in ANY multi-member portfolio (pulls up to 0.6 sigma at d2; the previously-dead GMM member masked it). Random subsample collapses the gate-probe pull 0.644->0.0013, lnZ bias -0.299->-0.006, JS 0.081->1e-5. Diagnosis: analyses/integrator_shape_gate/gmm_member_fix_gate/diagnosis/ (RIFT_roboto_paper). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Auto-review finding: the new regression test cannot collect in a torch-free installation, because importing |
…ession test pytest-collectable Review finding on #33: importing mcsamplerPortfolio eagerly load()s all RIFT.integrator_plugins entry points, so a torch-free installation died with ModuleNotFoundError before the new regression test could even collect. Guard each pipeline.load() (skip + notice, matching the isolation #28 carries) and convert the regression script into two proper pytest test functions. Verified: pytest -q passes (2 passed) with a torch-import blocker on PYTHONPATH, reviewer's invocation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Fixed in 8ca6368: guarded each 🤖 Generated with Claude Code |
… by PR #33 PR #33 found two coupled bugs that invalidate the event-specific numbers in this document: * default-wired portfolio GMM members NEVER TRAINED (n_comp=None silently no-op'd), so the real-ILE portfolio runs here carried an untrained corpse member (found via this PR's freeze probe); * AV draw_simplified head-sliced a BIN-ORDERED cloud (~50-60% of live-volume bins) while sampling_density claimed uniform coverage of ALL bins -- a q_mix DENSITY LIE, which inflates the weight of any member drawing where AV never populates. My earlier "the outliers are real heavy tails" conclusion was based on counting q_mix UNDERFLOWS (zero found). That test was correct but incomplete: it rules out a density underflow, not a density LIE. Lesson recorded: when a weight looks impossible, test whether the member actually DRAWS where it claims density, not just for underflow. The clipping study stands as a METHODOLOGICAL result (which quantities may be clipped and why -- those arguments are analytic), but every S250114ax efficiency/ln Z figure needs re-measuring on top of #33. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…in gate probe Re-measured S250114ax on top of PR #33 (GMM members now actually train; AV draw_simplified no longer misreports its density). Result: the portfolio got WORSE when the GMM member came alive -- n_eff 2.1 (default) / 1.1 (adaptive) at 4M, vs 52.6 pre-#33 and 100.2 for standalone AV. Pre-#33 the GMM member was a corpse so the portfolio was effectively AV-only; now the allocation hands the live GMM ~0.84 of the budget and the pooled n_eff collapses. This isolates the DRAW-ALLOCATION pathology in clean form: never-freeze is working (AV updates every chunk) but both allocation rules score by per-chunk n_ess, which sits at ~1 through VARAHA's slow CUMULATIVE contraction, so a member that looks instantly good takes the budget. New opt-in lever portfolio_varaha_min_frac / --portfolio-varaha-min-frac reserves a combined draw fraction for VARAHA members, applied after either allocation rule. It works mechanically (AV share 0.97 at floor 0.5, 0.85 at floor 0.7) but does NOT rescue this event: even a 3-15% GMM share still poisons the pooled n_eff (~1-2). Conclusion recorded: the missing capability is member EXCLUSION, not re-weighting -- any nonzero share of a wrong member's draws enters q_mix and a handful of its samples dominate. Honest production guidance for AV-favorable high-SNR events: run standalone AV, not AV+GMM. The portfolio's demonstrated value stays the correlated regime (Benchmark 3). Also adds test/expensive_before_merging/integrators/probe_portfolio_optin_flags.py: the TESTING.md-required flag-ON probe for the opt-in features, reusing the merge-gate suite as a library (identical targets/metrics/thresholds) and running in-process because the gate's spawn workers would not carry a monkey-patch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…get (post-#33); de-circularize credit Post-#33 S250114ax, warm, same budget -- clipping the proposal-fit input is the lever that works: run Neff>=5 Neff>=10 final@4M standalone AV (reference) 0.695M 1.374M 100.2 portfolio, default -- -- 2.1 portfolio + defensive 0.05 -- -- 2.5 portfolio + PROPOSAL-FIT CLIP 0.420M 0.590M 14.4 n_eff=5 1.7x faster than standalone AV and n_eff=10 2.3x faster. The real O4 event configs run --n-eff 10, so AT THE TARGET THAT SHIPS the clipped AV+GMM portfolio beats standalone AV on this event. It plateaus at ~14 rather than reaching AV's 100, so AV alone still wins the stress target. This only became visible after #33: pre-#33 the GMM member was a corpse, so there was nothing to poison and clipping did nothing. Lever ordering on this event: clip (2.1->14.4) >> defensive mixture (2.1->2.5) ~ VARAHA draw floor (no rescue) > adaptive allocation (worse, 1.1). Also de-circularizes the 'credit' signal: the raw MIS share frac_m*q_m/q_mix scales with frac_m, so a member accrued credit BECAUSE it was dominant (a 0.95-share GMM scored 6e-4 vs AV 9e-10 and starved AV to the floor -- the same circularity n_ess has). Now normalized by frac_m => "integral explained per unit allocation", which is allocation-invariant and is what an allocation rule must compare. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ment Gate (PR #31 requirement), base rift_O4d @4bac7444 vs this branch, both incl. #33, run on an idle node: COMPARE_EXIT=0, 0 blocking regressions, base and PR identical in aggregate (strict 8/8, warn-only 5/5, starved 45/45), 22 of 23 portfolio rows bitwise identical to base. The one remaining difference is mix_d2_n3_s303 (n_eff 736->517, bias -0.0060->-0.0064): the row where never-freeze actually engages. Both PASS comfortably, but it quantifies the trade -- never-freeze buys starvation-immunity and costs ~30% n_eff where freezing would have been harmless. Also hardens probe_portfolio_optin_flags.py to export CUDA_VISIBLE_DEVICES/OMP_NUM_THREADS and put the checkout on sys.path itself. Library-mode use of the suite does NOT inherit what run_shape_recovery.sh exports; without it you import the INSTALLED RIFT and get confident, meaningless numbers (this bit me once -- caught only because the absolute n_eff did not match the gate's). 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>
Portfolio GMM dead-member fix + AV draw-density repair
Two coupled silent-degradation bugs found by the shape-recovery merge gate (
test/expensive_before_merging/integrators/, merged in #31), fixed together because the first was masking the second.Bug 1: portfolio GMM members never trained (dead member)
mcsamplerPortfolio.setup()forwards kwargs that lackn_comp;mcsamplerEnsemble.setup()defaultedn_comp=None;update_sampling_priorsilently no-ops forn_comp=None. Net effect in default production wiring (--sampler-method portfoliowithout explicit GMM args): the GMM member never trained and every portfolio ran as AV-only — no error, no warning. Discovered by the PR #28 freeze-territory probe (the freeze machinery was governing a corpse).Fix:
setup()defaultsn_comp=None → 1with a loud notice (n_comp=0remains the documented off-switch);update_sampling_priorwarns once instead of silently skipping; plus an up-shape guard for bare(2,)bounds rows in uncorrelated groups (same guard_sample()/q-scoring already had) — without it, fixing Bug 1 alone turns the silent no-op into a crash in default (per-dimension) configuration.Bug 2: AV
draw_simplifiedhead-sliced a bin-ordered cloud (density lie)sample_from_binsemits points grouped in lexicographic bin order;draw_simplifiedreturnedrv[:n_to_get], i.e. only the first ~50–60% of the live-volume bins, whilesampling_density(hence the portfolio balance-heuristicq_mix) claimed uniform coverage of all occupied bins. In any multi-member portfolio this systematically biased the recovered shape (pulls up to 0.64σ at d=2, dose-response with take-fraction; it also explains the +0.1-nat residual portfolio evidence bias seen in the #26 validation and the universal negative-pull signature on base). The dead GMM member of Bug 1 partially masked it — a live member re-weights exactly the region AV never populates.Fix: random subsample (without replacement) instead of the head slice. Validation probe: pull 0.644 → 0.0013, lnZ bias −0.299 → −0.006, JS 0.081 → 1e-5, n_eff unchanged. AV standalone paths are untouched (
draw_simplifiedserves portfolio members).Validation
test/integrators/test_portfolio_gmm_member_trains.py: default-wired AV+GMM portfolio must train its GMM member;n_comp=0must still disable. Both pass.rift_O4d@ 841bcfe vs this branch, portfolio held strict): portfolio d2 5/6 IMPROVED(fail→pass) — evidence bias +0.02..+0.12 → |bias| ≤ 0.007 nats, pulls 0.03–0.16σ → ≤ 0.007σ; three more targets starved→pass at d4/d6; AV and GMM standalone rows unchanged. One flagged row (mix_d2_n1_s303) is a threshold-straddler: n_eff 3022→1502 trips the n_eff<0.5×base heuristic at 0.497 while every shape metric improves (bias +0.085→−0.002, pull 0.045→0.001) — waived.analyses/integrator_shape_gate/gmm_member_fix_gate/(gate JSONs + comparisons + the completediagnosis/probe ladder isolating Bug 2: jitter check, single-member portfolio controls, coverage instrumentation, monkey-patch validation).Note for reviewers: the gate's two GMM d6_n3 verdict flips between rounds were shown to be OpenBLAS-pthreads jitter on n_eff≈100-marginal starved rows (bitwise-reproducible on an idle node), not code effects.
🤖 Generated with Claude Code