feat(rl): regime_observer trainer wiring — loaders + struct + launchers (F1.4)

Adds:
- include_bytes! for rl_regime_flat_count + rl_regime_observer cubins
- IntegratedTrainer struct fields: 2 modules + 2 functions + flat_count_d helper buffer
- new() cubin loading + struct field assignment
- launch_rl_regime_flat_count(): Grid=(1), Block=(256), smem=256*sizeof(int)
- launch_rl_regime_observer(): Grid=(1), Block=(1), smem=0

Does NOT wire launchers into step_with_lobsim (that's F1.5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-31 12:05:49 +02:00
parent 6ded2c55c2
commit 6e0c9abff2

View File

@@ -216,6 +216,14 @@ const RL_KELLY_FRACTION_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_kelly_fraction_controller.cubin"));
const RL_EVAL_WARMUP_DECAY_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_eval_warmup_decay.cubin"));
// Regime observer (F1.2 + F1.3) — flat-count block-reduce + state-machine
// signal emitter. Launched each env step: flat_count first (writes
// flat_count_d), then regime_observer (reads flat_count_d + ISV state,
// emits 6 surface signals + Welford state + ε_recovery_live).
const RL_REGIME_FLAT_COUNT_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_regime_flat_count.cubin"));
const RL_REGIME_OBSERVER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_regime_observer.cubin"));
const RL_WIN_RATE_EMA_UPDATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_win_rate_ema_update.cubin"));
const RL_AVG_WIN_LOSS_EMA_UPDATE_CUBIN: &[u8] =
@@ -683,6 +691,15 @@ pub struct IntegratedTrainer {
// v9 (2026-05-31): defensive eval-boundary calibration kernel.
_rl_eval_warmup_decay_module: Arc<CudaModule>,
rl_eval_warmup_decay_fn: CudaFunction,
// Regime observer (F1.4) — emits unified state-machine signals consumed
// by Kelly (F2), v9 (F3), popart (F4), IQN τ (F5).
_rl_regime_flat_count_module: Arc<CudaModule>,
rl_regime_flat_count_fn: CudaFunction,
_rl_regime_observer_module: Arc<CudaModule>,
rl_regime_observer_fn: CudaFunction,
/// Per-step flat-count helper buffer: holds the block-reduce output
/// (number of accounts with lots==0) for one step. Length 1.
flat_count_d: CudaSlice<i32>,
// EMA producers feeding the new controllers.
_rl_win_rate_ema_update_module: Arc<CudaModule>,
rl_win_rate_ema_update_fn: CudaFunction,
@@ -1607,6 +1624,22 @@ impl IntegratedTrainer {
let rl_eval_warmup_decay_fn = rl_eval_warmup_decay_module
.load_function("rl_eval_warmup_decay")
.context("load rl_eval_warmup_decay")?;
// Regime observer (F1.4).
let rl_regime_flat_count_module = ctx
.load_cubin(RL_REGIME_FLAT_COUNT_CUBIN.to_vec())
.context("load rl_regime_flat_count cubin")?;
let rl_regime_flat_count_fn = rl_regime_flat_count_module
.load_function("rl_regime_flat_count")
.context("load rl_regime_flat_count function")?;
let rl_regime_observer_module = ctx
.load_cubin(RL_REGIME_OBSERVER_CUBIN.to_vec())
.context("load rl_regime_observer cubin")?;
let rl_regime_observer_fn = rl_regime_observer_module
.load_function("rl_regime_observer")
.context("load rl_regime_observer function")?;
let flat_count_d = stream
.alloc_zeros::<i32>(1)
.context("alloc flat_count_d")?;
let rl_win_rate_ema_update_module = ctx
.load_cubin(RL_WIN_RATE_EMA_UPDATE_CUBIN.to_vec())
.context("load rl_win_rate_ema_update cubin")?;
@@ -2685,6 +2718,11 @@ impl IntegratedTrainer {
rl_kelly_fraction_controller_fn,
_rl_eval_warmup_decay_module: rl_eval_warmup_decay_module,
rl_eval_warmup_decay_fn,
_rl_regime_flat_count_module: rl_regime_flat_count_module,
rl_regime_flat_count_fn,
_rl_regime_observer_module: rl_regime_observer_module,
rl_regime_observer_fn,
flat_count_d,
_rl_win_rate_ema_update_module: rl_win_rate_ema_update_module,
rl_win_rate_ema_update_fn,
_rl_avg_win_loss_ema_update_module: rl_avg_win_loss_ema_update_module,
@@ -3793,6 +3831,47 @@ impl IntegratedTrainer {
Ok(())
}
/// Regime observer helper — block-reduce per-account flat count.
/// Output: `flat_count_d[0]` = number of accounts with lots==0.
/// Launch: Grid=(1,1,1), Block=(256,1,1), smem=256*sizeof(int).
pub fn launch_rl_regime_flat_count(&mut self, lots_d: &CudaSlice<i32>, b_size: usize) -> Result<()> {
let mut args = RawArgs::new();
args.push_ptr(lots_d.raw_ptr());
args.push_ptr(self.flat_count_d.raw_ptr());
args.push_i32(b_size as i32);
let mut ptrs = args.build_arg_ptrs();
let smem_bytes = (256 * std::mem::size_of::<i32>()) as u32;
unsafe {
raw_launch(
self.rl_regime_flat_count_fn.cu_function(),
(1, 1, 1), (256, 1, 1), smem_bytes,
self.raw_stream,
&mut ptrs[..args.len()],
).map_err(|e| anyhow::anyhow!("rl_regime_flat_count: {:?}", e))?;
}
Ok(())
}
/// Regime observer — emits 6 surface signals + Welford state + ε_recovery_live.
/// Reads: kelly_f (prior step), cooldown, flat_count, worst_pnl, Welford state.
/// Launch: Grid=(1,1,1), Block=(1,1,1), smem=0.
pub fn launch_rl_regime_observer(&self, b_size: usize) -> Result<()> {
let mut args = RawArgs::new();
args.push_ptr(self.isv_dev_ptr);
args.push_ptr(self.flat_count_d.raw_ptr());
args.push_i32(b_size as i32);
let mut ptrs = args.build_arg_ptrs();
unsafe {
raw_launch(
self.rl_regime_observer_fn.cu_function(),
(1, 1, 1), (1, 1, 1), 0,
self.raw_stream,
&mut ptrs[..args.len()],
).map_err(|e| anyhow::anyhow!("rl_regime_observer: {:?}", e))?;
}
Ok(())
}
/// Adaptive risk management — Layer 4 input EMA producer (win_rate).
/// Reads `rewards_d`, `dones_d` [b_size] f32; outcome derived inline.
/// Writes ISV[RL_WIN_RATE_EMA_INDEX = 677].