portfolio: build the fair-draw weights on the sampler's own backend (backport from rift_O4d) - #168
Conversation
Plugin discovery calls pipeline.load() unguarded, so `import
RIFT.integrators.mcsamplerPortfolio` raises whatever the plugin raises. On every
environment here that is
ModuleNotFoundError: No module named 'nflows'
from the optional NF pipeline -- which makes --sampler-method portfolio
unselectable, and mcsamplerPortfolio untestable, on any install without torch/nflows.
Prerequisite for the fair-draw fix that follows: without it that code cannot be
reached or tested at all on this branch.
Guard each load and report the skip, as rift_O4d already does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The fair-draw block BUILDS the weights with the module-global converter but DRAWS
with self.xpy, and the two are independent. identity_convert_togpu is cupy.asarray
whenever cupy imports, while self.xpy is numpy for any sampler whose driver did not
set it (MCSampler.__init__ defaults it to numpy) and under --sampler-xpy numpy,
which sets sampler.xpy = numpy while set_xpy_to_numpy() leaves the module globals on
cupy. Whenever they disagree, ln_wt went to the device, numpy.exp dispatched
through cupy's __array_ufunc__ and returned a cupy array, and numpy.random.choice
was handed a device array as p=:
TypeError: Implicit conversion to a NumPy array is not allowed.
Please use `.get()` to construct a NumPy array explicitly.
That is an abort, not a degraded result: the traceback runs analyze_event ->
sampler.integrate -> integrate_log, so the ILE process dies and no extrinsic samples
are written.
Backport of the rift_O4d fix. There integrate_log ADDITIONALLY forces self.xpy =
numpy (the portfolio aggregates on the host), so the backends always disagree and
--sampler-method portfolio could not run on a GPU host at all: measured on
ldas-pcdev13 with the extrinsic-collapse demo at rho_net 146.8, 6/6 replicates of
--sampler-portfolio AV,GMM died at this line and 6/6 complete with the fix. This
branch leaves self.xpy alone, so the trigger is narrower -- same defect, conditional
rather than unconditional.
Two parts, as in the O4d fix:
* build the weights on self.xpy, never on the module-global backend; and
* gather on the HOST -- _rvs entries need not share a backend with the index array
(sample_n is written through the INSTANCE identity_convert_togpu, which the ILE
sets to cupy.asarray, while the aggregated keys arrive host-typed), and indexing
a numpy array with a cupy array raises the same TypeError.
Also host-converts the three _rvs operands before the arithmetic one line above, for
the same reason: numpy.array(<cupy>) raises identically one line earlier.
The regression suite uses a _DeviceArray stand-in reproducing the two cupy behaviours
the bug turns on, so it hits the exact production traceback on a CPU-only host. All
five runnable tests fail without this change.
Two tests SKIP on this branch, with the reason recorded in the code: driving the
INSTANCE converter device-typed trips a separate, pre-existing defect in draw(),
which converts member draws with self.identity_convert_togpu and then assigns them
into host-typed buffers. That is untouched here (rift_O4d solves it by aggregating
on the host) and would be wrong to attribute to the fair draw. The gather itself is
pinned regardless by the device-typed INDEX test, which does run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Heads-up on an overlap, and a question about how you'd like it handled. I opened #172, the driver-side half of portfolio selectability (
I had independently written a plugin-discovery guard identical to your One thing the combined tree turned up that neither PR fixes: with both applied the portfolio gets as far as the integration loop and then aborts in the AV member's prior evaluation —
|
|
Correction / follow-up to my note above, since I chased the It is not a single site, and the O4d fix is not a safe backport. Fixing each abort just revealed the next:
all the same More importantly — backporting O4d's
The reason is So I'm not proposing any of it. I've left the reproduction on a local branch and written it up in cd ~/rift_O4c_base/MonteCarloMarginalizeCode/Code/RIFT/integrators
grep -nE '(temp_ret|p_out|joint_p_s|joint_p_prior)\s*\*=' mcsampler*.py |
Backport of the
rift_O4dfix in oshaughnessy-junior#76. Two commits, separable if you'd rather take them apart.1. Plugin discovery guard (prerequisite)
import RIFT.integrators.mcsamplerPortfoliocurrently raises whatever an optional plugin raises, becausepipeline.load()is unguarded. On every environment available here that is:from the optional NF pipeline — so
--sampler-method portfoliois unselectable andmcsamplerPortfoliountestable on any install without torch/nflows. This is a hard prerequisite: without it the fair-draw code below cannot be reached or tested on this branch at all.rift_O4dalready carries this guard; this is the same four lines.2. The fair-draw backend fix
The fair-draw block builds the weights with the module-global converter but draws with
self.xpy, and the two are independent:identity_convert_togpuiscupy.asarraywhenever cupy imports, whileself.xpyis numpy for any sampler whose driver did not set it (MCSampler.__init__defaults it to numpy) and under--sampler-xpy numpy, which setssampler.xpy = numpywhileset_xpy_to_numpy()only rebinds local variables and leaves the module globals on cupy. When they disagree,ln_wtgoes to the device,numpy.expdispatches through cupy's__array_ufunc__and returns a cupy array, andnumpy.random.choiceis handed a device array asp=:That is an abort, not a degraded result — the traceback runs
analyze_event -> sampler.integrate -> integrate_log, so the ILE process dies and no extrinsic samples are written.Two parts, matching the O4d fix:
self.xpy, never the module-global backend._rvsentries need not share a backend with the index array (sample_nis written through the instanceidentity_convert_togpu, which the ILE sets tocupy.asarray, while the aggregated keys arrive host-typed), and indexing a numpy array with a cupy array raises the sameTypeError.Plus one line beyond that: the three
_rvsoperands are host-converted before the arithmetic one line above, sincenumpy.array(<cupy>)raises identically one line earlier.Severity differs from O4d — stated plainly
On
rift_O4d,integrate_logadditionally forcesself.xpy = numpy(the portfolio aggregates on the host), so the two backends always disagree and--sampler-method portfoliocannot run on a GPU host at all. That is where this was measured: ldas-pcdev13, extrinsic-collapse demo at rho_net 146.8, 6/6 replicates of--sampler-portfolio AV,GMMdied at this line, and 6/6 complete with the fix.This branch leaves
self.xpyalone, so the abort is conditional on the two backends disagreeing rather than unconditional. Same defect, narrower trigger. I did not reproduce it end-to-end on an O4c GPU run — the evidence here is the unit reproduction plus the O4d campaign.Testing
New
test/test_portfolio_fairdraw_backend.py: 5 passed, 3 skipped (numpy 1.24.4 env). All five runnable tests fail without commit 2.A
_DeviceArraystand-in reproduces the two cupy behaviours the bug turns on (ufunc dispatch via__array_ufunc__,__array__raising), so the tests hit the exact production traceback — samemtrand.pyxframe, same message — on a CPU-only host rather than skipping.Two tests skip on this branch, and the reason is recorded in the code. Driving the instance converter device-typed trips a separate, pre-existing defect in
draw(), which converts member draws withself.identity_convert_togpuand then assigns them into host-typed buffers — so it raises insidedraw(), long before the fair draw. That is untouched here (rift_O4dsolves it by aggregating on the host) and it would be wrong to attribute it to the fair draw. The skip is conditional on the traceback actually passing throughdraw(), so these re-enable themselves if that is ever fixed. The gather is pinned regardless by the device-typed index test, which does run.The test file deliberately differs from the O4d copy in its prose and in those two skips, because the branches genuinely differ in how
self.xpyis bound.Note
There is an unpushed local branch (
rift_O4c_portfolio_usable) carrying a similar plugin guard alongside other O4c portfolio work. This PR stands alone againstrift_O4cand does not depend on it; if that branch lands first, commit 1 here will likely conflict trivially or become redundant.🤖 Generated with Claude Code