Add CudaSlice<f32> accessor methods to GpuTrainResult and GradientResult
(loss_cuda_slice, grad_norm_cuda_slice) in ml-dqn, eliminating redundant
tensor_to_cuda_slice_f32 calls at every training guard check site.
- GpuTrainResult: add from_fused_scalars() constructor and CudaSlice extractors
- GradientResult: add CudaSlice extractors that delegate to GpuTrainResult
- fused_training.rs: use from_fused_scalars() instead of Tensor::new() wrapping
- train_step.rs: use result.loss_cuda_slice() instead of tensor_to_cuda_slice_f32
- training_loop.rs: same CudaSlice accessor pattern for both fused and accum paths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- gpu_weights.rs: Remove slow-path Candle fallback (flatten/cast/contiguous)
from extract_one() and sync_one(). F32-contiguous is now a hard requirement
enforced by ensure_f32_contiguous() at network construction. Non-F32 or
non-contiguous tensors produce a clear error instead of silently falling
back to the Candle conversion pipeline.
- gpu_action_selector.rs: Remove unused ensure_contiguous_f32(). Keep
stream_from_device/cuda_u32_to_tensor/cuda_f32_to_tensor as boundary
converters (they have external callers).
- gpu_experience_collector.rs: Clean up doc comments referencing Candle
where only cudarc CudaSlice is used.
- mod.rs: Update module docs to clarify Candle Tensor/Device are only
at the upload boundary (DqnGpuData, PpoGpuData, tensor_to_cuda_slice).
- tlob/trainable_adapter.rs: Fix double candle_core:: path that broke build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All VarBuilder/NoisyLinear init changed from BF16 to F32.
The fused CUDA trainer's Adam kernel operates on F32 master weights
with separate BF16 mirrors for forward kernels. BF16 VarBuilder
caused dtype mismatches in Candle forward paths and broke ensure_f32.
381 ml-dqn tests pass, 0 errors, 0 warnings workspace-wide.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace deprecated cudarc memcpy_stod with clone_htod across all GPU
upload paths (14 call sites in trainers, cuda_pipeline, hyperopt).
Replace deprecated memcpy_dtov with clone_dtoh in ml-supervised
gpu_tensor.rs.
Bridge GpuTensor-migrated submodules (xLSTM, Liquid CfC, TFT GRN)
with Candle Tensor callers via from_candle_tensor/to_candle_tensor
conversion utilities at API boundaries. Fix CudaDevice->CudaContext
in ml-dqn distributional_dueling.rs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace VarMap/VarBuilder weight storage with GpuVarStore (native CUDA)
for RMSNorm, LayerNorm, and ResidualBlock. Linear layers in ResidualBlock
now use GpuLinear with cuBLAS sgemm. Cold-path forwards convert between
GpuTensor and Candle Tensor at the boundary since downstream ops (GELU,
LayerNorm, dropout, broadcast) still use Candle.
Changes:
- ml-core cuda_autograd: add GpuTensor::to_candle/from_candle interop,
export GpuParam/AdamWConfig/ActivationKernels/LossKernels/LossResult,
make init::upload_to_gpu public
- rmsnorm.rs: RMSNorm/LayerNorm now take (Arc<CudaStream>, Device, dim)
instead of (VarBuilder, dim). Weights stored in GpuVarStore.
- residual.rs: ResidualBlock now takes (Arc<CudaStream>, Device, config, name).
fc1/fc2 are GpuLinear with cuBLAS sgemm forward. LayerNorm params in GpuVarStore.
- distributional_dueling.rs: updated RMSNorm constructor calls to new API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace PolicyNetwork and ValueNetwork with CudaPolicyNetwork/CudaValueNetwork
backed implementations. Each struct now stores a cuda_nn GPU-native network
(cuBLAS sgemm + CUDA kernels) alongside shadow Candle layers for autograd
training compatibility. Adds forward_cuda() inference paths that bypass Candle
entirely. Wire CudaTrajectoryTensors into TrajectoryBatch with to_cuda_tensors().
Re-export CudaLSTM from lstm_networks and all cuda_nn types from crate root.
Import GpuContext into continuous_policy, continuous_action_masking, flow_policy,
coupling_layer, and adaptive_entropy for future GPU migration. Add CudaVec::to_tensor()
bridge for cuda_nn→Candle interop. Fix upload_host_to_gpu→upload_to_gpu rename
in ml-core cuda_autograd init.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VarBuilder and NoisyLinear were creating BF16 weights, then ensure_f32
tried Var::set() which rejects dtype changes. Fix: create F32 from the
start. BF16 mirrors are managed separately by GpuDqnTrainer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eliminate candle-core/candle-nn from the KAN and Diffusion model forward
paths in ml-supervised. All dense layers now use cuBLAS sgemm via the new
gpu_tensor module. Element-wise ops (SiLU, sigmoid, tanh, exp) use host
roundtrips for now; fused CUDA kernels are a follow-up.
Changes:
- Add gpu_tensor.rs: GpuTensor (CudaSlice<f32> + shape), GpuLinear
(cuBLAS sgemm), and ~30 element-wise GPU ops
- Rewrite kan/{spline,layer,network}.rs to use GpuTensor instead of
candle_core::Tensor and candle_nn::{VarBuilder,Linear}
- Rewrite diffusion/{denoiser,noise,sampler}.rs to use GpuTensor and
GpuLinear instead of candle_nn::Linear
- Update ml crate trainable adapters (kan/trainable.rs,
diffusion/trainable.rs) to bridge Candle<->GpuTensor at the
UnifiedTrainable interface boundary
- Update ensemble inference adapters for both models
- Add cudarc 0.17 with cublas feature to ml-supervised Cargo.toml
- Candle deps retained in ml-supervised for unconverted models (TFT,
Liquid, Mamba, xLSTM) -- will be removed once all 8 models are
converted
Net: -424 lines, 509 -> ~453 Candle refs remaining (KAN: 0, Diffusion: 0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Candle's backward()/GradStore/VarMap/VarBuilder system with CUDA-native
primitives in ml-core::cuda_autograd. This eliminates the Candle dispatch overhead
(~2100 kernel launches per batch) for models that adopt the new API.
Components:
- GpuTensor: CudaSlice<f32> wrapper with shape metadata (replaces candle Tensor)
- GpuVarStore: named parameter store with flatten/unflatten (replaces VarMap/VarBuilder)
- GpuLinear: cuBLAS sgemm forward + manual backward (replaces candle_nn::Linear)
- GpuAdamW: per-parameter CUDA kernel optimizer (replaces candle_optimisers::Adam)
- ActivationKernels: ReLU/LeakyReLU/GELU/Sigmoid/Tanh forward+backward CUDA kernels
- LossKernels: MSE/Huber loss with fused gradient computation
- init: Xavier/Kaiming/near-zero initialization via CPU generate + GPU upload
Added cublas feature to ml-core's cudarc dependency for sgemm support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds CudaTrajectoryTensors — GPU-resident trajectory batch storage that
replaces the Candle-based TrajectoryTensors. All trajectory data (states,
actions, log_probs, advantages, returns) uploaded as CudaSlice<f32> with
zero Candle tensor overhead.
The cuda_nn module is now complete with all primitives needed to replace
Candle in the PPO training loop:
- CudaLinear (cuBLAS sgemm)
- CudaLSTM (fused gate kernel)
- CudaPolicyNetwork / CudaValueNetwork (MLP stacks)
- CudaAdam (GPU-resident optimizer)
- CudaTrajectoryTensors (GPU batch storage)
- Softmax / log-softmax / ReLU / tanh / sigmoid kernels
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add dqn_forward_only_kernel to dqn_training_kernel.cu — runs a single
branching forward pass (shared layers -> value head -> 3 branch heads ->
C51 distributional -> expected Q) and writes per-branch Q-values to
q_out[B, 11]. No loss computation, no activation saves, no backward.
~3x faster than forward_loss for pure inference.
Wire the kernel into GpuDqnTrainer via forward_only_q() which takes
CudaSlice<f32> states directly and returns &CudaSlice<f32> Q-values,
replacing the Candle BranchingDuelingQNetwork::forward_branches() call
chain (~160 Candle kernel dispatches -> 1 fused CUDA launch).
Mark all 6 ml-dqn Candle forward methods as #[cold] with documentation
that hot-path compute is handled by fused CUDA kernels:
- noisy_layers.rs: NoisyLinear::forward() -> dqn_experience_kernel.cu
- quantile_regression.rs: QuantileNetwork::forward() -> iqn_dual_head_kernel.cu
- distributional.rs: CategoricalDistribution::to_scalar() -> dqn_forward_only_kernel
- curiosity.rs: ForwardDynamicsModel::predict() -> curiosity_training_kernel.cu
- residual.rs: ResidualBlock::forward() -> dqn_forward_only_kernel
- rmsnorm.rs: RMSNorm::forward() / LayerNorm::forward() -> dqn_experience_kernel.cu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GPU-native MLP networks that use CudaLinear layers + cuBLAS sgemm
internally. These are drop-in replacements for the Candle-based
PolicyNetwork and ValueNetwork, providing the same forward/softmax/
log_softmax API surface but with zero Candle overhead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CUDA infrastructure in ml-core (cuda_compile, gpu/capabilities, gpu/l2_cache)
previously accessed cudarc types through candle_core::cuda_backend::cudarc --
a transitive re-export that coupled low-level CUDA driver calls to Candle.
Changes:
- Add cudarc 0.17 as direct optional dep (gated behind cuda feature)
- Replace all candle_core::cuda_backend::cudarc paths with direct cudarc::
- Generalize OOM detection to accept &dyn Debug (was &CandleError)
- Add generic computation_error_to_common_error() alongside legacy alias
Candle references: 163 -> 72 (remaining are autograd-dependent: Tensor,
Var, GradStore, VarBuilder -- cannot be replaced with cudarc raw ops)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces the cuda_nn module as the foundation for eliminating Candle from
the ml-ppo crate. All primitives store weights as CudaSlice<f32> and use
cuBLAS sgemm for matrix multiplication, custom CUDA kernels for activations.
Components:
- GpuContext: shared cuBLAS handle + CUDA stream wrapper
- CudaLinear: fully connected layer via cuBLAS sgemm + bias-add kernel
- CudaLSTM: fused gate LSTM cell via cuBLAS + gate nonlinearity kernel
- CudaAdam: GPU-resident Adam optimizer (m/v state on GPU, single kernel per step)
- cuda_softmax/cuda_log_softmax: warp-shuffle numerically stable kernels
- cuda_relu/cuda_tanh/cuda_sigmoid: element-wise activation kernels
- CudaVec: GPU buffer wrapper with known length
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update all callers to match the new pure-cudarc APIs introduced by the
hive agent CudaSlice migration. Key changes:
- GpuTrainingGuard::new() now takes Arc<CudaStream>; callers use from_device()
- check_and_accumulate/qvalue_stats/qvalue_divergence take &CudaSlice<f32>
instead of &Tensor; callers convert via tensor_to_cuda_slice_f32()
- accumulate_q_value takes f32 scalar, returns () (no Result)
- GpuReplayBuffer::insert_batch gains batch_size arg, takes CudaSlice params
- signal_adapter functions take &Arc<CudaStream> (cudarc 0.17 Arc requirement)
- Add tensor_to_cuda_slice_u32() and cuda_f32_to_tensor() utility functions
- Replace CudaView usage with owned CudaSlice via tensor_to_cuda_slice_f32()
- Fix CudaStorage.device field access (was method call in older API)
- Fix borrow-after-move in copy_actions_out via scoped DtoD copy
Zero errors, zero warnings across lib + tests + examples + full workspace.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BranchingDuelingQNetwork now converts all weight tensors to F32 contiguous
at construction via ensure_f32_contiguous(). This guarantees the invariant
that extract_one/sync_one/reverse_sync_one in gpu_weights.rs can bypass
the flatten_all().to_dtype(F32).contiguous() Candle pipeline and do a
single direct DtoD memcpy from the Var's CUDA storage.
Key changes:
- branching.rs: add ensure_f32_contiguous() + ensure_f32() for NoisyLinear
sigma/epsilon, forward_branches casts to F32 instead of BF16
- noisy_layers.rs: sample_noise/reset_noise/disable_noise use weight_mu
dtype (F32 after ensure_f32) instead of hardcoded BF16, add ensure_f32()
to convert sigma vars and epsilon buffers
- gpu_weights.rs: extract_one/sync_one fast path skips Candle ops when
tensor is already F32 contiguous, reverse_sync_one fixed to access Var's
own CUDA storage directly (was writing to a temporary F32 tensor before)
- fused_training.rs: updated doc comments for CudaSlice-as-source-of-truth
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eliminate ALL Candle Tensor usage from gpu_experience_collector.rs to fix
cross-stream deadlocks. The old code created Candle Tensors on the forked
CudaStream, which conflicted with Candle's default stream (stream 0) used
by GpuReplayBuffer::insert_batch.
Changes to gpu_experience_collector.rs:
- GpuExperienceBatch now holds CudaSlice<f32>/CudaSlice<i32> instead of Tensor
- Remove cuda_slice_to_tensor_f32() and cuda_slice_i32_to_tensor_u32() helpers
- Add dtod_clone_f32(), dtod_clone_i32() pure-cudarc DtoD copy helpers
- Add build_next_states_dtod() for episode-aware state shift via pointer math
- Remove device: &Device parameter from collect_experiences_gpu()
- Remove candle_core::{DType, Device, Tensor} import entirely
Changes to training_loop.rs:
- Move CudaSlice->Tensor conversion to training_loop boundary (post stream-sync)
- Add cuda_slice_to_tensor_f32() and cuda_slice_i32_to_tensor_u32() at boundary
- Conversions happen AFTER stream.synchronize(), on the synced stream -- no
cross-stream hazard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
action.rs:
- Replace Tensor-based selector calls with CudaSlice extraction via
extract_cuda_f32! macro + ensure_contiguous_f32() helper
- Constructor now passes Arc<CudaStream> via stream_from_device()
- Replace per-element narrow+squeeze+to_scalar readback with bulk
readback_actions() DtoH memcpy (single transfer vs N scalar reads)
- select_actions_branching() now takes explicit batch_size parameter
metrics.rs:
- Same CudaSlice extraction pattern for validation action selection
- Wrap CudaSlice<u32> result back to Candle Tensor via cuda_u32_to_tensor()
for downstream GPU PnL/Sharpe computation that needs Tensor ops
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eliminate all 26 Candle Tensor references from signal_adapter.rs.
Three functions (ppo_to_exposure_scores, signal_to_action_scores,
tft_quantile_to_signal) now take CudaSlice<f32> + CudaStream and
return CudaSlice<f32>, backed by three fused CUDA kernels in
signal_adapter_kernel.cu. Dead code evaluate_supervised_gpu_backtest
removed (zero callers). Added cuda_f32_to_tensor helper to
gpu_action_selector for DtoD copy back to Candle Tensor at API
boundaries (PPO hyperopt adapter, evaluate_baseline example).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GpuReplayBuffer uses Candle Tensor ops (slice_scatter, cumsum, narrow)
which execute on Candle's default stream (stream 0). The experience
collector outputs data on a forked CudaStream. Without an explicit
synchronization barrier, the default stream may read partially-written
data from the forked stream, causing cross-stream deadlocks or data
corruption.
Add stream.synchronize() in three places:
- training_loop.rs: after experience collection, before insert_batch_tensors
- gpu_experience_collector.rs: cuda_slice_to_tensor_f32 after DtoD copy
- gpu_experience_collector.rs: cuda_slice_i32_to_tensor_u32 after DtoD copy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
With unified forked stream, cudarc event tracking is self-referencing
(no cross-stream conflicts). disable_event_tracking caused more
problems than it solved — stale events, Drop failures, hangs.
Let cudarc manage events naturally on the single stream.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moving disable_event_tracking() from GpuDqnTrainer::new() to the
stream fork in constructor.rs ensures ALL CudaSlice allocations
(experience collector, replay buffer, curiosity trainer) are created
with tracking already disabled. Previous placement caused hangs because
earlier allocations had stale events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- upload_batch_gpu: BF16→F32 via CUDA kernel, no Candle to_dtype()
- IQL: reuses DQN trainer's F32 buffers via train_value_step_raw()
- Re-enable disable_event_tracking() — safe with unified stream
- Reduce smoke test epochs to 1 (debug mode is too slow for 3)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all Candle tensor operations in the fused training loop with
pure cudarc CudaSlice operations to prevent Candle tensor Drops from
recording events on the default stream (which conflicts with our forked
training stream via cudarc event tracking).
Key changes:
- Re-enable disable_event_tracking() in GpuDqnTrainer::new() — safe now
that no Candle tensors are created during training
- Rewrite upload_batch_gpu(): BF16 states/next_states use DtoD + bf16→f32
kernel instead of Candle to_dtype(F32); F32 rewards/dones/weights use
direct DtoD copy with layout offset handling
- Load bf16_to_f32_kernel from training module (was defined in CUH but
not loaded)
- Add train_value_step_raw() to GpuIqlTrainer that takes CudaSlice<f32>
directly, bypassing Candle tensor manipulation
- IQL in fused training now reuses DQN trainer's already-converted F32
states_buf/rewards_buf instead of creating Candle temporaries
- Fix dtod_from_candle_f32/u32 to respect Candle layout start_offset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fork a dedicated CudaStream once in DQNTrainer constructor and share it
via Arc::clone with all GPU components (experience collector, fused
trainer, portfolio sim, monitoring). This eliminates:
1. Candle's default stream (stream 0) from the training hot path
2. Dual-stream cudarc event tracking conflicts during CUDA Graph capture
3. Implicit serialization points between default and forked streams
The fused training context no longer forks its own stream — it receives
the trainer's unified stream. With all GPU work on a single stream,
cudarc event tracking is safely disabled in GpuDqnTrainer::new(),
removing the cuStreamWaitEvent/cuEventRecord overhead that broke CUDA
Graph capture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The fused DQN trainer uses a forked stream (for CUDA Graph capture)
while the experience collector + curiosity trainer use Candle's
default stream (stream 0). cudarc's context-wide event tracking
creates conflicts between the two streams.
disable_event_tracking() is context-wide — it fixes the forked
stream but breaks the default stream (curiosity trainer hangs).
Proper fix: create ONE forked stream at DQNTrainer construction,
pass it to ALL GPU components (experience collector, curiosity
trainer, portfolio sim, fused trainer). No Candle default stream
in the hot path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 13 per-feature toggle tests with 1 production-config test.
There is no code path without curiosity, IQN, branching, CQL, Kelly,
action masking, circuit breaker — these are always on in production.
smoke_params() now enables everything: Rainbow DQN + branching + IQN +
curiosity + CQL + Kelly + action masking + circuit breaker.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DuelingQNetwork uses advantage_fc.* VarMap keys, but
GpuExperienceCollector expects branch_0_fc.* (branching always on).
Also remove stale use_noisy_nets/use_distributional fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI PVC structure is /data/test-data/ohlcv/ES.FUT, local is
test_data/ES.FUT. test_data_dir() now searches both layouts
and checks both FOXHUNT_TEST_DATA and TEST_DATA_DIR env vars.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause 1 — CUDA_ERROR_ILLEGAL_ADDRESS:
shmem_max_in_dim only included trunk dims (state_dim, shared_h1,
shared_h2) but not head dims (value_h, adv_h). BF16 weight tile
for branch output overflowed shared memory on RTX 3050 (48KB).
Root cause 2 — CUDA_ERROR_INVALID_VALUE on EMA kernel:
cudarc 0.17's automatic event tracking records read/write events on
CudaSlice buffers. During CUDA Graph capture (events disabled) then
replay (events re-enabled), stale write events from CudaSlice Drops
poison the context error_state. Next bind_to_thread() propagates it.
Fix: disable_event_tracking() at GpuDqnTrainer construction —
single-owner forked stream, all sync points are explicit.
Also:
- Remove all #[ignore] from smoke tests, use real ES.FUT .dbn data
- Validate DBN schema at file level (skip non-OHLCV)
- Organize test_data/ into per-symbol subdirectories
- Fix pre-existing gpu_kernel_parity_test + evaluate_baseline errors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MBP-10 .dbn files mixed into test_data/ caused OHLCV loader to crash
on unknown RType 0x42. Now silently skips non-OHLCV records.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: shmem_max_in_dim only included trunk dims (state_dim,
shared_h1, shared_h2) but not head dims (value_h, adv_h). When
hidden_dim_base=32 made the trunk narrow while heads stayed at 128,
the BF16 weight tile for branch output (255×128=32640 BF16 elements)
overflowed the shared memory region (12288 BF16 elements). On H100
the overflow landed in unused-but-mapped hardware shmem (silent
corruption). On RTX 3050 (48KB physical shmem) it hit unmapped
memory → CUDA_ERROR_ILLEGAL_ADDRESS.
Changes:
- gpu_dqn_trainer.rs: shmem_max_in_dim includes value_h/adv_h
- Remove all #[ignore] from smoke tests (feature_coverage,
training_stability, gpu_residency)
- Smoke tests use real .dbn data from test_data/ (hard error if missing)
- Remove synthetic_data() fallback — no fake data in tests
- GPU-direct DtoD training path (train_step_gpu, FusedTrainScalars)
- GPU-native PER priority update kernel (zero CPU readback)
- IQN dual-head integration (gpu_iqn_head.rs)
- BF16 dtype fixes across 6 model adapters
- Hyperopt 30D→31D (iqn_lambda)
- portfolio_transformer: unconditional BF16 (remove dead CPU branches)
- liquid/adapter: all tests use Cuda(0) directly
- Fix pre-existing gpu_kernel_parity_test.rs (stale args)
- Fix pre-existing evaluate_baseline.rs (removed fields)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove ALL #[cfg(feature = "cuda")] guards (~400+ occurrences)
- Remove ALL #[cfg_attr(not(feature = "cuda"), ignore)] test annotations (~250)
- Make cuda default feature in 9 ML crates (ml, ml-core, ml-dqn, ml-ppo, etc.)
- Convert nvrtc JIT compilation to precompiled nvcc (searchsorted, prefix_sum)
- Move compile_ptx_for_device() to ml-core for shared access
- Delete dead CPU code: multi_step.rs, self_supervised_pretraining.rs,
training_guard_gpu_tests.rs, CPU PER buffer paths, CPU Q-diagnostics
- Replace unwrap_or(Device::Cpu) with hard errors everywhere
- Remove dead is_cuda() else branches in DQN/PPO/hyperopt trainers
- Change config defaults from "cpu" to "cuda" (rainbow, tlob, pipeline)
- Port IQL value network to GPU kernel (5 CUDA entry points)
- Port HER goal relabeling to GPU kernel (warp-per-sample)
- Wire DSR GPU-to-CPU sync in training loop
- cfg!(feature = "cuda") → true in inference_validator
Zero warnings, zero errors across entire workspace.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After removing ensure_training_dtype(), forward methods that receive F32
inputs from tests/callers now fail with dtype mismatch against BF16 weights.
Add to_dtype(BF16) at forward entry of xLSTM (slstm, mlstm, block, network),
CfC cell, and diffusion time embedding. Fix quantization to accept BF16
tensors by casting to F32 before INT8 conversion. Update guard to catch
#[cfg(not(feature = "cuda"))] dead code. Bump DQN emergency_safe_defaults
replay_buffer_capacity from 1000 to 2048 (GPU PER floor = 1024).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete every CPU fallback block across 14 files (-286 lines):
- dqn.rs: CPU replay buffer, tensor construction, PER fallback, gradient paths
- hyperopt/adapters/ppo.rs: CPU curiosity modules, trajectory generation
- hyperopt/adapters/dqn.rs: CPU backtest fallback, sync no-op
- trainers/ppo.rs: CPU training error stub
- l2_cache.rs: CPU stub functions (gate callers behind cuda too)
- replay_buffer_type.rs: CPU is_gpu_prioritized fallback
- validation/harness.rs: CPU regime breakdown fallback
- build.rs: cpu_only_build cfg (never referenced)
- evaluate_baseline.rs: CPU gpu_handled fallback
- testing/integration/gpu: CPU test stubs
Zero #[cfg(not(feature = "cuda"))] remains in the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AutoReplaySizer inflated smoke test buffer_size=500 to 10M on H100 (75GB VRAM),
causing GPU PER empty-sample failures and multi-hour hangs. Root cause: the sizer's
100K minimum was always above the test buffer size, so the guard never triggered.
Fixes:
- constructor.rs: skip auto-sizing when buffer_size < 100K (sizer's own minimum)
- config.rs: insert_batch_tensors falls back to CPU PER (download + add_batch)
when GPU PER unavailable, instead of hard error
- train_step.rs: skip fused CUDA Graph init when GPU PER not active (stream
capture can't include CPU→GPU transfers from CPU PER sampling)
- dqn.rs: allow CPU tensor construction path on CUDA when GPU PER falls back;
remove hard errors in PER priority update (2 locations)
- metrics.rs: fix portfolio tensor shape (broadcast_left→expand for 2D concat);
add CPU PER fallback for Q-value statistics sampling
- agent.rs, entropy_regularization.rs: GPU-native Gumbel-max via Tensor::rand
(eliminates per-call CPU Vec allocation + GPU upload)
- smoke test helpers: defense-in-depth replay_buffer_vram_fraction = 0.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nvcc (C++11 mode) parses `25f` as an attempt to use user-defined literal
suffix `f` instead of the standard float suffix. When V_MIN/V_MAX are
injected as integer-formatted values (e.g., `-25f` from Rust's Display
trait on f32), nvcc fails with "user-defined literal operator not found".
Fix: use `{v_min:.1}f` format to guarantee a decimal point (`-25.0f`),
which is unambiguously a float literal in all C++ standards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Non-DQN kernels (curiosity, PPO, epsilon-greedy, backtest-PPO) include
common_device_functions.cuh but don't define SHARED_H1/SHARED_H2/VALUE_H/ADV_H.
The unguarded q_forward_dueling_warp_shmem() references these constants,
causing nvcc compilation failures (undefined identifiers + cascading
"user-defined literal operator not found" errors).
Wraps the function in #if defined(SHARED_H1) && defined(SHARED_H2) &&
defined(VALUE_H) && defined(ADV_H) ... #endif so it's only compiled
when the DQN architecture constants are injected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The BF16 matvec helpers (warp_matvec_bf16_shmem, warp_matvec_bf16_broadcast_shmem)
call warp_reduce_sum_all which is defined later in the header. nvcc on H100
rejects this without a forward declaration — broke experience kernel compilation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Argo artifact logs were stored in foxhunt-training-results which is
meant for model checkpoints. Separate bucket makes logs findable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test module used `super::*` but TradingState lives in
crate::dqn, not in the trainer module — broke `cargo test --lib`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>