integrators: probe for a CUDA device before selecting cupy; fail loudly when GMM refit never succeeds - #32
Conversation
…ly when GMM refit never succeeds Regression (found by the shape-recovery merge gate's O4c back-check): the May-June 2026 GPU-port commits (5d55c04/9dd7188a, f44c7a8) select cupy at module import time whenever `import cupy` succeeds. On a cupy-installed node without a usable GPU (shared install, or CUDA_VISIBLE_DEVICES masked) every GMM refit then dies in gaussian_mixture_model._near_psd_impl via the import-time binding _xpy_eigvals = cupy.linalg.eigvalsh (cudaErrorNoDevice); the integrator swallows the exception ('Error training, resetting...'), the proposal never adapts, and integrate() returns n_eff~1 with no error flag. Fixes, validated by the discriminator probe (mix_d6_n1_s101, CPU-forced: n_eff 1.05 -> 2037, js_max 0.63 -> 0.0002): * MonteCarloEnsemble / gaussian_mixture_model / mcsamplerEnsemble: gate the cupy backend on cupy.cuda.runtime.getDeviceCount() > 0 (broad except: the probe raises CUDARuntimeError, not ImportError, on GPU-less nodes). * gaussian_mixture_model: drop the import-time _xpy_eigvals/_xpy_eig bindings; resolve eigvalsh/eigh at call time through the caller's xpy, matching how _xpy_logsumexp already dispatches. * MonteCarloEnsemble.integrate: after 5 consecutive refit failures (max_consecutive_train_failures) raise RuntimeError and set self.integration_error instead of silently returning n_eff~1. * mcsamplerEnsemble: pass tripwire_epsilon under its real name (was the silently-ignored typo tripwire_epsion, so callers' values never applied). With a GPU visible the backend selection is unchanged (cupy still chosen). Known residual, out of scope here: ~1.5x GMM adaptation slowdown from f28ad8d (correct log-domain weights). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Auto-review completed the mandatory integrator shape-recovery gate. The standard CPU-only matrix ran on the refreshed base and this candidate (96 runs each, |
|
2026-08-04 18:41 UTC heartbeat checkpoint: PR #43 remains OPEN/CLEAN/MERGEABLE with all 19 CI checks successful. The isolated Issue #32 worktree has only the known excluded Roadblock: owner merge decision, followed by required human-accessible validation. Next: detect merge or actionable PR change; after merge, verify the Sphinx integration and record the human-validation gate. |
Regression
The May–June 2026 GPU-port commits (5d55c04/9dd7188a, f44c7a8) made the GMM integrator stack select cupy at module import time whenever
import cupysucceeds. On a cupy-installed node without a usable GPU (shared install, orCUDA_VISIBLE_DEVICESmasked — a configuration that has repeatedly bitten production workers), every GMM refit dies ingaussian_mixture_model._near_psd_implthrough the import-time binding_xpy_eigvals = cupy.linalg.eigvalsh(cudaErrorNoDevice).MonteCarloEnsemble.integrateswallows the exception ("Error training, resetting..."), the proposal never adapts, andintegrate()returns n_eff ≈ 1 with no error flag.Found by the shape-recovery merge gate's O4c back-check (#31): the O4c code recovers the
mix_d6_n1_s101discriminator target with n_eff ≈ 3047 in the identical environment where O4d returns n_eff ≈ 1.05. Full bisect, probe table and probe script live in the RIFT_roboto_paper worktree underanalyses/integrator_shape_gate/gmm_regression_bisect/.Fix
MonteCarloEnsemble,gaussian_mixture_model,mcsamplerEnsemble): gate cupy selection oncupy.cuda.runtime.getDeviceCount() > 0. Broadexceptbecause the probe raisesCUDARuntimeError, notImportError, on GPU-less nodes.gaussian_mixture_model): drop the import-time_xpy_eigvals/_xpy_eigglobals; resolveeigvalsh/eighthrough the caller'sxpy, mirroring how_xpy_logsumexpalready dispatches.MonteCarloEnsemble.integrate): after 5 consecutive refit failures (max_consecutive_train_failureskwarg) raiseRuntimeErrorand setself.integration_errorinstead of silently returning n_eff ≈ 1. A separateintegration_errorstring is also set on the existing max-error exit path.mcsamplerEnsemblepassedtripwire_epsion=(typo) intointegrate(), so caller-suppliedtripwire_epsilonvalues were silently discarded; now passed under the real name.Verification (ldas-pcdev11, env
~/RIFT_develUWM,OMP_NUM_THREADS=1,CUDA_VISIBLE_DEVICES="")mix_d6_n1_s101: n_eff 1.05 → 2037.0, js_max 0.63 → 0.00017, no error (matches the validatedfiles_f28ad8d0_eigvals_npbisect point exactly).gmm.fitforced to raise (emulatingcudaErrorNoDevice) on the d6 target,integrate()now raisesRuntimeError: GMM proposal refit failed 5 consecutive times; proposal never adaptedinstead of returning n_eff ≈ 1. On an easy target that legitimately converges before adaptation matters, the tripwire correctly does not fire.cupy_ok=True), identical to base. Full on-GPU end-to-end could not be exercised on pcdev11: the env's cupy predates the Blackwell GPUs and fails NVRTC compilation on both base and this branch identically (pre-existing environment limitation, unrelated to this change).Known residual (out of scope)
A ~1.5× GMM adaptation slowdown relative to O4c remains, introduced by f28ad8d (correct log-domain weight handling): n_eff ≈ 2037 vs O4c's ≈ 3047 on the discriminator at equal budget. That is a correctness-motivated change and is tracked separately from this regression fix.
🤖 Generated with Claude Code