feat(phase-e-4-a): mirror --temporal in backtest binary + T11 ISV-continual
Phase E.4.A Tasks 11+12: backtest binary gains the same
--temporal Mamba2 forward chain as the smoke binary, plus the
--isv-continual flag that fires the stacker-threshold controller
at the end of each eval episode (Pillar B).
Changes:
- Imports: ml_alpha::mamba2_block::{Mamba2Block, Mamba2BlockConfig},
ml_core::cuda_autograd::gpu_tensor::GpuTensor
- CLI flags: --temporal, --window-k, --mamba2-hidden-dim,
--mamba2-state-dim, --isv-continual
- Cubin loading: alpha_window_push + stacker_threshold_controller
- Q-net sizing: c51_input_dim = mamba2_hidden_dim when --c51 --temporal
- Buffers: window_tensor GpuTensor, h_enriched_buf_dev, isv_dev,
ctl_wiener_dev
- Training inference path: push + Mamba2 forward + h_enriched →
C51 forward (mirrors smoke binary)
- Training batched compute: terminal Mamba2 forward, h_enriched_buf
for current/next, c51_input_dim threading
- Eval inference path: push + Mamba2 forward + h_enriched → C51
forward + Thompson select (with scoped borrow guard)
- T11 ISV-continual: stacker-threshold controller fires at end of
each eval episode; ISV slot 543 (threshold), 545 (observed-rate),
546 (Kelly atten) update with realized rollout stats. Co-exists
with the τ-grid sweep (τ-grid still gates; ISV updates parallel
observation of "live deployment" behaviour).
- JSON output: new fields temporal, window_k, mamba2_hidden_dim,
mamba2_state_dim, isv_continual.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,10 @@ use clap::Parser;
|
|||||||
use cudarc::driver::{CudaContext, DevicePtr, DevicePtrMut};
|
use cudarc::driver::{CudaContext, DevicePtr, DevicePtrMut};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
// Phase E.4.A.8/T12: Mamba2 temporal encoder (ml-alpha Phase 1d.1).
|
||||||
|
use ml_alpha::mamba2_block::{Mamba2Block, Mamba2BlockConfig};
|
||||||
|
use ml_core::cuda_autograd::gpu_tensor::GpuTensor;
|
||||||
|
|
||||||
// ── Mapped-pinned helpers (mirror gpu_training_guard.rs MappedBuffer) ──
|
// ── Mapped-pinned helpers (mirror gpu_training_guard.rs MappedBuffer) ──
|
||||||
|
|
||||||
struct MappedI32 {
|
struct MappedI32 {
|
||||||
@@ -234,6 +238,22 @@ struct Cli {
|
|||||||
/// ±0.125-tick. Phase E.3 Path 3 follow-up.
|
/// ±0.125-tick. Phase E.3 Path 3 follow-up.
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
real_spread: bool,
|
real_spread: bool,
|
||||||
|
/// Phase E.4.A: enable the temporal-encoder stack (sliding window +
|
||||||
|
/// Mamba2 + C51).
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
temporal: bool,
|
||||||
|
#[arg(long, default_value_t = 16)]
|
||||||
|
window_k: usize,
|
||||||
|
#[arg(long, default_value_t = 32)]
|
||||||
|
mamba2_hidden_dim: usize,
|
||||||
|
#[arg(long, default_value_t = 16)]
|
||||||
|
mamba2_state_dim: usize,
|
||||||
|
/// Phase E.4.A Pillar B: fire stacker-threshold controller at the
|
||||||
|
/// end of EVERY eval episode (not just training). Slot 543
|
||||||
|
/// (threshold) adapts during the 2D sweep based on observed trade
|
||||||
|
/// rate / Sharpe.
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
isv_continual: bool,
|
||||||
#[arg(long, default_value = "config/ml/alpha_compose_backtest.json")]
|
#[arg(long, default_value = "config/ml/alpha_compose_backtest.json")]
|
||||||
out_path: PathBuf,
|
out_path: PathBuf,
|
||||||
}
|
}
|
||||||
@@ -353,6 +373,17 @@ fn main() -> Result<()> {
|
|||||||
let c51_project_kernel = c51_module.load_function("alpha_c51_project_kernel")?;
|
let c51_project_kernel = c51_module.load_function("alpha_c51_project_kernel")?;
|
||||||
let c51_grad_kernel = c51_module.load_function("alpha_c51_grad_kernel")?;
|
let c51_grad_kernel = c51_module.load_function("alpha_c51_grad_kernel")?;
|
||||||
let c51_thompson_kernel = c51_module.load_function("alpha_c51_thompson_select_kernel")?;
|
let c51_thompson_kernel = c51_module.load_function("alpha_c51_thompson_select_kernel")?;
|
||||||
|
// Phase E.4.A.6: window push kernel (used in --temporal path).
|
||||||
|
let push_module = ctx
|
||||||
|
.load_cubin(ml::cuda_pipeline::alpha_kernels::ALPHA_WINDOW_PUSH_CUBIN.to_vec())
|
||||||
|
.context("alpha_window_push cubin")?;
|
||||||
|
let push_kernel = push_module.load_function("alpha_window_push_kernel")?;
|
||||||
|
// Phase E.4.A.7 controller (used in --isv-continual eval path).
|
||||||
|
let ctl_module = ctx
|
||||||
|
.load_cubin(ml::cuda_pipeline::alpha_kernels::STACKER_THRESHOLD_CONTROLLER_CUBIN.to_vec())
|
||||||
|
.context("stacker controller cubin")?;
|
||||||
|
let ctl_kernel = ctl_module
|
||||||
|
.load_function("stacker_threshold_controller_update")?;
|
||||||
let c51_n_atoms = cli.c51_n_atoms;
|
let c51_n_atoms = cli.c51_n_atoms;
|
||||||
let c51_v_min = cli.c51_vmin;
|
let c51_v_min = cli.c51_vmin;
|
||||||
let c51_v_max = cli.c51_vmax;
|
let c51_v_max = cli.c51_vmax;
|
||||||
@@ -414,14 +445,19 @@ fn main() -> Result<()> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// --- Initialize Q-network ---
|
// --- Initialize Q-network ---
|
||||||
|
let c51_input_dim: usize = if cli.c51 && cli.temporal {
|
||||||
|
cli.mamba2_hidden_dim
|
||||||
|
} else {
|
||||||
|
STATE_DIM
|
||||||
|
};
|
||||||
let n_weights_eff: usize = if cli.c51 {
|
let n_weights_eff: usize = if cli.c51 {
|
||||||
N_ACTIONS * c51_n_atoms * STATE_DIM
|
N_ACTIONS * c51_n_atoms * c51_input_dim
|
||||||
} else {
|
} else {
|
||||||
N_WEIGHTS
|
N_WEIGHTS
|
||||||
};
|
};
|
||||||
let n_biases_eff: usize = if cli.c51 { N_ACTIONS * c51_n_atoms } else { N_BIASES };
|
let n_biases_eff: usize = if cli.c51 { N_ACTIONS * c51_n_atoms } else { N_BIASES };
|
||||||
let mut rng = SmokeRng::new(cli.seed.wrapping_add(0xDEAD_BEEF));
|
let mut rng = SmokeRng::new(cli.seed.wrapping_add(0xDEAD_BEEF));
|
||||||
let xavier_scale = (2.0_f32 / STATE_DIM as f32).sqrt();
|
let xavier_scale = (2.0_f32 / c51_input_dim as f32).sqrt();
|
||||||
let w_init: Vec<f32> = (0..n_weights_eff)
|
let w_init: Vec<f32> = (0..n_weights_eff)
|
||||||
.map(|_| xavier_scale * 2.0 * (rng.next_f32() - 0.5))
|
.map(|_| xavier_scale * 2.0 * (rng.next_f32() - 0.5))
|
||||||
.collect();
|
.collect();
|
||||||
@@ -455,6 +491,37 @@ fn main() -> Result<()> {
|
|||||||
let state_pinned = unsafe { MappedF32::new(STATE_DIM)? };
|
let state_pinned = unsafe { MappedF32::new(STATE_DIM)? };
|
||||||
let action_pinned = unsafe { MappedI32::new()? };
|
let action_pinned = unsafe { MappedI32::new()? };
|
||||||
|
|
||||||
|
// Phase E.4.A.8 (T12): temporal buffers, allocated unconditionally.
|
||||||
|
let mut window_tensor = GpuTensor::zeros(
|
||||||
|
&[1, cli.window_k, STATE_DIM], &stream
|
||||||
|
).map_err(|e| anyhow::anyhow!("alloc window: {e}"))?;
|
||||||
|
let h_enriched_buf_capacity = (cli.horizon + 1) * cli.mamba2_hidden_dim;
|
||||||
|
let mut h_enriched_buf_dev = stream
|
||||||
|
.alloc_zeros::<f32>(h_enriched_buf_capacity)
|
||||||
|
.context("alloc h_enriched buffer")?;
|
||||||
|
let mamba2_block: Option<Mamba2Block> = if cli.temporal {
|
||||||
|
let cfg = Mamba2BlockConfig {
|
||||||
|
in_dim: STATE_DIM,
|
||||||
|
hidden_dim: cli.mamba2_hidden_dim,
|
||||||
|
state_dim: cli.mamba2_state_dim,
|
||||||
|
seq_len: cli.window_k,
|
||||||
|
};
|
||||||
|
info!("TEMPORAL Mamba2: in={} hidden={} state={} K={}",
|
||||||
|
cfg.in_dim, cfg.hidden_dim, cfg.state_dim, cfg.seq_len);
|
||||||
|
Some(Mamba2Block::new(cfg, stream.clone())
|
||||||
|
.map_err(|e| anyhow::anyhow!("Mamba2Block init: {e}"))?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let n_atoms_i = c51_n_atoms as i32;
|
||||||
|
// ISV buffer + Wiener state for the eval-time controller path.
|
||||||
|
let mut isv_host: Vec<f32> = vec![0.0; 552];
|
||||||
|
isv_host[ml::cuda_pipeline::alpha_isv_slots::RANDOM_BASELINE_MEAN_INDEX] = -5191.53;
|
||||||
|
isv_host[ml::cuda_pipeline::alpha_isv_slots::RANDOM_BASELINE_STD_INDEX] = 4963.62;
|
||||||
|
isv_host[ml::cuda_pipeline::alpha_isv_slots::TRADE_RATE_TARGET_INDEX] = 0.08;
|
||||||
|
let mut isv_dev = stream.clone_htod(&isv_host).context("upload isv")?;
|
||||||
|
let mut ctl_wiener_dev = stream.alloc_zeros::<f32>(3).context("alloc ctl wiener")?;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Phase 1: TRAIN DQN on train segment.
|
// Phase 1: TRAIN DQN on train segment.
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
@@ -475,11 +542,51 @@ fn main() -> Result<()> {
|
|||||||
let mut actions_host: Vec<i32> = Vec::with_capacity(cli.horizon);
|
let mut actions_host: Vec<i32> = Vec::with_capacity(cli.horizon);
|
||||||
let mut rewards_host: Vec<f32> = Vec::with_capacity(cli.horizon);
|
let mut rewards_host: Vec<f32> = Vec::with_capacity(cli.horizon);
|
||||||
let mut dones_host: Vec<f32> = Vec::with_capacity(cli.horizon);
|
let mut dones_host: Vec<f32> = Vec::with_capacity(cli.horizon);
|
||||||
|
// Phase E.4.A.7 (T12): zero window at episode start.
|
||||||
|
if cli.temporal {
|
||||||
|
stream.memset_zeros(window_tensor.data_mut())
|
||||||
|
.context("zero window on episode reset (train)")?;
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
let s_vec = env.state(&state).to_vec();
|
let s_vec = env.state(&state).to_vec();
|
||||||
let action: u8 = if cli.c51 {
|
let action: u8 = if cli.c51 {
|
||||||
state_pinned.write(&s_vec);
|
state_pinned.write(&s_vec);
|
||||||
{
|
// Phase E.4.A.7 (T12): push and run Mamba2.
|
||||||
|
if cli.temporal {
|
||||||
|
let (w_ptr, _g1) = window_tensor.data_mut().device_ptr_mut(&stream);
|
||||||
|
unsafe {
|
||||||
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_window_push(
|
||||||
|
&stream, &push_kernel,
|
||||||
|
state_pinned.dev_u64(), w_ptr,
|
||||||
|
cli.window_k as i32, state_dim_i,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cli.temporal {
|
||||||
|
let block = mamba2_block.as_ref().expect("Mamba2Block missing");
|
||||||
|
let (_logit, cache) = block.forward_train(&window_tensor)
|
||||||
|
.map_err(|e| anyhow::anyhow!("mamba2 forward: {e}"))?;
|
||||||
|
// Store h_enriched for batched training.
|
||||||
|
let h_host = stream.clone_dtoh(cache.h_enriched.cuda_data())?;
|
||||||
|
let slot_offset = state.step * cli.mamba2_hidden_dim;
|
||||||
|
let mut buf_host = stream.clone_dtoh(&h_enriched_buf_dev)?;
|
||||||
|
for j in 0..cli.mamba2_hidden_dim {
|
||||||
|
buf_host[slot_offset + j] = h_host[j];
|
||||||
|
}
|
||||||
|
stream.memcpy_htod(&buf_host, &mut h_enriched_buf_dev)?;
|
||||||
|
// C51 forward on h_enriched.
|
||||||
|
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
||||||
|
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
||||||
|
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
||||||
|
let (h_ptr, _g3) = cache.h_enriched.cuda_data().device_ptr(&stream);
|
||||||
|
unsafe {
|
||||||
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
|
&stream, &c51_fwd_kernel,
|
||||||
|
w_ptr, b_ptr, h_ptr, p_ptr,
|
||||||
|
1, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
||||||
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
||||||
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
||||||
@@ -487,7 +594,7 @@ fn main() -> Result<()> {
|
|||||||
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
&stream, &c51_fwd_kernel,
|
&stream, &c51_fwd_kernel,
|
||||||
w_ptr, b_ptr, state_pinned.dev_u64(), p_ptr,
|
w_ptr, b_ptr, state_pinned.dev_u64(), p_ptr,
|
||||||
1, state_dim_i, n_act_i, n_atoms_i,
|
1, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,30 +671,64 @@ fn main() -> Result<()> {
|
|||||||
stream.memcpy_htod(&rewards_norm, &mut rewards_dev)?;
|
stream.memcpy_htod(&rewards_norm, &mut rewards_dev)?;
|
||||||
stream.memcpy_htod(&dones_host, &mut dones_dev)?;
|
stream.memcpy_htod(&dones_host, &mut dones_dev)?;
|
||||||
if cli.c51 {
|
if cli.c51 {
|
||||||
// C51 forward/project/grad
|
// Phase E.4.A.8 (T12): in temporal mode, the batched C51
|
||||||
|
// forwards read h_enriched_buf_dev populated during the
|
||||||
|
// per-step inference. Run one extra Mamba2 forward on the
|
||||||
|
// terminal window to fill slot ep_len.
|
||||||
|
if cli.temporal {
|
||||||
|
let block = mamba2_block.as_ref().expect("Mamba2Block missing");
|
||||||
|
let (_logit, cache) = block.forward_train(&window_tensor)
|
||||||
|
.map_err(|e| anyhow::anyhow!("mamba2 terminal forward: {e}"))?;
|
||||||
|
let h_host_term = stream.clone_dtoh(cache.h_enriched.cuda_data())?;
|
||||||
|
let term_offset = (ep_len as usize) * cli.mamba2_hidden_dim;
|
||||||
|
let mut buf_host = stream.clone_dtoh(&h_enriched_buf_dev)?;
|
||||||
|
for j in 0..cli.mamba2_hidden_dim {
|
||||||
|
buf_host[term_offset + j] = h_host_term[j];
|
||||||
|
}
|
||||||
|
stream.memcpy_htod(&buf_host, &mut h_enriched_buf_dev)?;
|
||||||
|
}
|
||||||
|
let (curr_input_ptr_guard, next_input_ptr_guard);
|
||||||
|
let (curr_input_ptr, next_input_ptr): (u64, u64) = if cli.temporal {
|
||||||
|
let (h_ptr_curr, g_curr) = h_enriched_buf_dev.device_ptr(&stream);
|
||||||
|
let (h_ptr_next_base, g_next) = h_enriched_buf_dev.device_ptr(&stream);
|
||||||
|
curr_input_ptr_guard = g_curr;
|
||||||
|
next_input_ptr_guard = g_next;
|
||||||
|
let _ = (&curr_input_ptr_guard, &next_input_ptr_guard);
|
||||||
|
(
|
||||||
|
h_ptr_curr,
|
||||||
|
h_ptr_next_base + (cli.mamba2_hidden_dim as u64) * 4u64,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let (s_ptr, g_curr) = states_dev.device_ptr(&stream);
|
||||||
|
let (n_ptr, g_next) = next_states_dev.device_ptr(&stream);
|
||||||
|
curr_input_ptr_guard = g_curr;
|
||||||
|
next_input_ptr_guard = g_next;
|
||||||
|
let _ = (&curr_input_ptr_guard, &next_input_ptr_guard);
|
||||||
|
(s_ptr, n_ptr)
|
||||||
|
};
|
||||||
|
// C51 forward online → probs_current
|
||||||
{
|
{
|
||||||
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
||||||
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
||||||
let (s_ptr, _g2) = states_dev.device_ptr(&stream);
|
|
||||||
let (p_ptr, _g3) = probs_current_dev.device_ptr_mut(&stream);
|
let (p_ptr, _g3) = probs_current_dev.device_ptr_mut(&stream);
|
||||||
unsafe {
|
unsafe {
|
||||||
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
&stream, &c51_fwd_kernel,
|
&stream, &c51_fwd_kernel,
|
||||||
w_ptr, b_ptr, s_ptr, p_ptr,
|
w_ptr, b_ptr, curr_input_ptr, p_ptr,
|
||||||
ep_len, state_dim_i, n_act_i, n_atoms_i,
|
ep_len, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// C51 forward target → probs_next
|
||||||
{
|
{
|
||||||
let (w_ptr, _g0) = w_target_dev.device_ptr(&stream);
|
let (w_ptr, _g0) = w_target_dev.device_ptr(&stream);
|
||||||
let (b_ptr, _g1) = b_target_dev.device_ptr(&stream);
|
let (b_ptr, _g1) = b_target_dev.device_ptr(&stream);
|
||||||
let (s_ptr, _g2) = next_states_dev.device_ptr(&stream);
|
|
||||||
let (p_ptr, _g3) = probs_next_dev.device_ptr_mut(&stream);
|
let (p_ptr, _g3) = probs_next_dev.device_ptr_mut(&stream);
|
||||||
unsafe {
|
unsafe {
|
||||||
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
&stream, &c51_fwd_kernel,
|
&stream, &c51_fwd_kernel,
|
||||||
w_ptr, b_ptr, s_ptr, p_ptr,
|
w_ptr, b_ptr, next_input_ptr, p_ptr,
|
||||||
ep_len, state_dim_i, n_act_i, n_atoms_i,
|
ep_len, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -612,11 +753,12 @@ fn main() -> Result<()> {
|
|||||||
let (s_ptr, _g3) = states_dev.device_ptr(&stream);
|
let (s_ptr, _g3) = states_dev.device_ptr(&stream);
|
||||||
let (dw_ptr, _g4) = dw_dev.device_ptr_mut(&stream);
|
let (dw_ptr, _g4) = dw_dev.device_ptr_mut(&stream);
|
||||||
let (db_ptr, _g5) = db_dev.device_ptr_mut(&stream);
|
let (db_ptr, _g5) = db_dev.device_ptr_mut(&stream);
|
||||||
|
let grad_input_ptr = if cli.temporal { curr_input_ptr } else { s_ptr };
|
||||||
unsafe {
|
unsafe {
|
||||||
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_grad(
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_grad(
|
||||||
&stream, &c51_grad_kernel,
|
&stream, &c51_grad_kernel,
|
||||||
p_ptr, m_ptr, a_ptr, s_ptr, dw_ptr, db_ptr,
|
p_ptr, m_ptr, a_ptr, grad_input_ptr, dw_ptr, db_ptr,
|
||||||
ep_len, state_dim_i, n_act_i, n_atoms_i,
|
ep_len, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
1.0 / ep_len as f32,
|
1.0 / ep_len as f32,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -752,11 +894,44 @@ fn main() -> Result<()> {
|
|||||||
let mut state = EpisodeState::new();
|
let mut state = EpisodeState::new();
|
||||||
let mut terminal_r = 0.0_f32;
|
let mut terminal_r = 0.0_f32;
|
||||||
let mut ep_n_trades = 0_u32;
|
let mut ep_n_trades = 0_u32;
|
||||||
|
// Phase E.4.A.7 (T12): zero window at eval episode start.
|
||||||
|
if cli.temporal {
|
||||||
|
stream.memset_zeros(window_tensor.data_mut())
|
||||||
|
.context("zero window on eval episode reset")?;
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
let s_vec = env.state(&state).to_vec();
|
let s_vec = env.state(&state).to_vec();
|
||||||
let action: u8 = if cli.c51 {
|
let action: u8 = if cli.c51 {
|
||||||
state_pinned.write(&s_vec);
|
state_pinned.write(&s_vec);
|
||||||
{
|
// Phase E.4.A (T12): push + Mamba2 + C51. The push
|
||||||
|
// is scoped in its own block so the mutable borrow
|
||||||
|
// guard drops before the &window_tensor read.
|
||||||
|
if cli.temporal {
|
||||||
|
{
|
||||||
|
let (w_ptr, _g1) = window_tensor.data_mut().device_ptr_mut(&stream);
|
||||||
|
unsafe {
|
||||||
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_window_push(
|
||||||
|
&stream, &push_kernel,
|
||||||
|
state_pinned.dev_u64(), w_ptr,
|
||||||
|
cli.window_k as i32, state_dim_i,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let block = mamba2_block.as_ref().expect("Mamba2Block missing");
|
||||||
|
let (_logit, cache) = block.forward_train(&window_tensor)
|
||||||
|
.map_err(|e| anyhow::anyhow!("mamba2 forward (eval): {e}"))?;
|
||||||
|
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
||||||
|
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
||||||
|
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
||||||
|
let (h_ptr, _g3) = cache.h_enriched.cuda_data().device_ptr(&stream);
|
||||||
|
unsafe {
|
||||||
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
|
&stream, &c51_fwd_kernel,
|
||||||
|
w_ptr, b_ptr, h_ptr, p_ptr,
|
||||||
|
1, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
let (w_ptr, _g0) = w_dev.device_ptr(&stream);
|
||||||
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
let (b_ptr, _g1) = b_dev.device_ptr(&stream);
|
||||||
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
let (p_ptr, _g2) = single_probs_dev.device_ptr_mut(&stream);
|
||||||
@@ -764,7 +939,7 @@ fn main() -> Result<()> {
|
|||||||
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
ml::cuda_pipeline::alpha_kernels::launch_alpha_c51_forward(
|
||||||
&stream, &c51_fwd_kernel,
|
&stream, &c51_fwd_kernel,
|
||||||
w_ptr, b_ptr, state_pinned.dev_u64(), p_ptr,
|
w_ptr, b_ptr, state_pinned.dev_u64(), p_ptr,
|
||||||
1, state_dim_i, n_act_i, n_atoms_i,
|
1, c51_input_dim as i32, n_act_i, n_atoms_i,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,6 +1007,36 @@ fn main() -> Result<()> {
|
|||||||
if terminal_r > 0.0 {
|
if terminal_r > 0.0 {
|
||||||
win_count += 1;
|
win_count += 1;
|
||||||
}
|
}
|
||||||
|
// Phase E.4.A Pillar B (T11): fire the stacker-threshold
|
||||||
|
// controller at end of each eval episode when
|
||||||
|
// --isv-continual. Adapts ISV slot 543/545/546 based on
|
||||||
|
// observed trade-rate and Sharpe; the τ-grid still
|
||||||
|
// overrides via `threshold` for the actual gate, but the
|
||||||
|
// ISV evolution is the "live deployment" simulation.
|
||||||
|
if cli.isv_continual {
|
||||||
|
let trade_count_ep = ep_n_trades as f32;
|
||||||
|
let decisions_ep = (state.step as f32).max(1.0);
|
||||||
|
let baseline_std = isv_host[ml::cuda_pipeline::alpha_isv_slots::RANDOM_BASELINE_STD_INDEX].max(1e-6);
|
||||||
|
let rollout_sharpe = terminal_r / baseline_std;
|
||||||
|
unsafe {
|
||||||
|
let (isv_ptr, _gi) = isv_dev.device_ptr_mut(&stream);
|
||||||
|
let (wv_ptr, _gw) = ctl_wiener_dev.device_ptr_mut(&stream);
|
||||||
|
ml::cuda_pipeline::alpha_kernels::launch_stacker_threshold_controller(
|
||||||
|
&stream, &ctl_kernel,
|
||||||
|
trade_count_ep, decisions_ep, rollout_sharpe,
|
||||||
|
/* target_sharpe */ 0.5,
|
||||||
|
/* k_threshold */ 0.01,
|
||||||
|
/* k_atten */ 0.005,
|
||||||
|
/* wiener_alpha_floor */ 0.4,
|
||||||
|
/* ctl_alpha_meta */ 0.1,
|
||||||
|
ml::cuda_pipeline::alpha_isv_slots::STACKER_THRESHOLD_INDEX as i32,
|
||||||
|
ml::cuda_pipeline::alpha_isv_slots::TRADE_RATE_TARGET_INDEX as i32,
|
||||||
|
ml::cuda_pipeline::alpha_isv_slots::TRADE_RATE_OBSERVED_EMA_INDEX as i32,
|
||||||
|
ml::cuda_pipeline::alpha_isv_slots::STACKER_KELLY_ATTENUATION_INDEX as i32,
|
||||||
|
isv_ptr, wv_ptr,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let n = rewards.len() as f64;
|
let n = rewards.len() as f64;
|
||||||
let mean = rewards.iter().map(|r| *r as f64).sum::<f64>() / n;
|
let mean = rewards.iter().map(|r| *r as f64).sum::<f64>() / n;
|
||||||
@@ -919,6 +1124,11 @@ fn main() -> Result<()> {
|
|||||||
"c51_n_atoms": if cli.c51 { c51_n_atoms } else { 0 },
|
"c51_n_atoms": if cli.c51 { c51_n_atoms } else { 0 },
|
||||||
"c51_vmin": if cli.c51 { c51_v_min } else { 0.0 },
|
"c51_vmin": if cli.c51 { c51_v_min } else { 0.0 },
|
||||||
"c51_vmax": if cli.c51 { c51_v_max } else { 0.0 },
|
"c51_vmax": if cli.c51 { c51_v_max } else { 0.0 },
|
||||||
|
"temporal": cli.temporal,
|
||||||
|
"window_k": if cli.temporal { cli.window_k } else { 0 },
|
||||||
|
"mamba2_hidden_dim": if cli.temporal { cli.mamba2_hidden_dim } else { 0 },
|
||||||
|
"mamba2_state_dim": if cli.temporal { cli.mamba2_state_dim } else { 0 },
|
||||||
|
"isv_continual": cli.isv_continual,
|
||||||
"horizon": cli.horizon,
|
"horizon": cli.horizon,
|
||||||
"train_frac": cli.train_frac,
|
"train_frac": cli.train_frac,
|
||||||
"n_train_episodes": cli.n_train_episodes,
|
"n_train_episodes": cli.n_train_episodes,
|
||||||
|
|||||||
Reference in New Issue
Block a user