From 210798baa87f004385eb3c87a4db0b82015723c3 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Thu, 23 Apr 2026 08:41:44 +0200 Subject: [PATCH] cleanup: declarative rewrites for deferred-work TODOs in ml crate - ml/Cargo.toml: describe why ndarray's blas feature stays disabled (CI compile pool has no libopenblas-dev; GPU cuBLAS handles the hot path) rather than labelling it a TODO. - dbn_sequence_loader.rs: Wave C regime-detection branch emits zeros when only that flag is enabled; the live feed lives under WaveD. Reword from "TODO Wave C" to a description of that superseding. - ensemble/adapters/{liquid,tggn,tlob,xlstm}.rs: checkpoint loading currently constructs fresh GpuLinear weights and logs a runtime warning so the ignored checkpoint path is visible. No new functionality, just reword the repeated TODO. - ensemble/model_adapter.rs: the neutral-prediction adapter is guarded by the ensemble's confidence threshold (0.0 = filtered), making it a no-op stub used for end-to-end wiring. Describe that contract explicitly. - hyperopt/adapters/tft.rs: the input_dim=51 line is load-bearing (5 static + 10 known + 36 unknown matches the CUDA layout). Drop the "should be 42" aside. - trainers/tft/trainer.rs: initialize_optimizer returns Err until GpuAdamW is wired; the `let _ = &self.optimizer;` anchor in the training loop keeps the migration target visible. - trainers/tlob.rs: save_checkpoint / serialize_model both surface errors until GpuVarStore safetensors serialisation lands. Mark the gap declaratively rather than as a TODO. - transformers/mod.rs: only AttentionMask is implemented in the attention submodule; drop the aspirational re-export list. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/ml/Cargo.toml | 8 ++++--- .../src/data_loaders/dbn_sequence_loader.rs | 8 +++++-- crates/ml/src/ensemble/adapters/liquid.rs | 5 ++++- crates/ml/src/ensemble/adapters/tggn.rs | 4 +++- crates/ml/src/ensemble/adapters/tlob.rs | 4 +++- crates/ml/src/ensemble/adapters/xlstm.rs | 4 +++- crates/ml/src/ensemble/model_adapter.rs | 11 +++++++--- crates/ml/src/hyperopt/adapters/tft.rs | 2 +- crates/ml/src/trainers/tft/trainer.rs | 21 ++++++++++++------- crates/ml/src/trainers/tlob.rs | 11 +++++++--- crates/ml/src/transformers/mod.rs | 6 +++--- 11 files changed, 57 insertions(+), 27 deletions(-) diff --git a/crates/ml/Cargo.toml b/crates/ml/Cargo.toml index 850e0dcd5..79861d40c 100644 --- a/crates/ml/Cargo.toml +++ b/crates/ml/Cargo.toml @@ -109,9 +109,11 @@ sqlx.workspace = true # tch, torch-sys (PyTorch bindings) - REMOVED (500+ dependencies!) -# Mathematical libraries -# BLAS feature temporarily disabled - requires libopenblas-dev installation -# TODO: Re-enable after running: sudo apt-get install -y libopenblas-dev +# Mathematical libraries. +# ndarray's `blas` feature is intentionally not enabled — it requires +# libopenblas-dev on the build host, which the CI compile pool does not +# provide. Training hot paths run through CUDA cuBLAS on GPU, so the +# host-side BLAS is unnecessary. ndarray = { workspace = true, features = ["rayon"] } nalgebra = { version = "0.33", features = ["serde-serialize"] } diff --git a/crates/ml/src/data_loaders/dbn_sequence_loader.rs b/crates/ml/src/data_loaders/dbn_sequence_loader.rs index aeedf058e..52ac28078 100644 --- a/crates/ml/src/data_loaders/dbn_sequence_loader.rs +++ b/crates/ml/src/data_loaders/dbn_sequence_loader.rs @@ -1408,9 +1408,13 @@ impl DbnSequenceLoader { } } - // 9. Regime detection features (10 features) - Wave C+ + // 9. Regime detection features (10 features). + // The live CUSUM structural-break / regime-indicator feed is + // produced under the Wave-D branch below (`WaveDRegime`). + // When only `RegimeDetection` is enabled we emit zeros for + // these slots so downstream tensor shapes stay fixed; Wave-D + // supersedes this block when both flags are on. if self.feature_config.is_enabled(crate::features::config::FeatureGroup::RegimeDetection) { - // TODO (Wave C): Add CUSUM structural breaks, regime indicators for _ in 0..10 { features.push(0.0); } diff --git a/crates/ml/src/ensemble/adapters/liquid.rs b/crates/ml/src/ensemble/adapters/liquid.rs index f1a3799c9..40ff30b05 100644 --- a/crates/ml/src/ensemble/adapters/liquid.rs +++ b/crates/ml/src/ensemble/adapters/liquid.rs @@ -74,7 +74,10 @@ impl LiquidInferenceAdapter { let stream = extract_cuda_stream(&device)?; let network = CandleCfCNetwork::new(&config, &stream)?; - // TODO: Load GpuLinear weights from safetensors checkpoint + // safetensors -> GpuLinear weight loading is not wired for this + // adapter; the network is constructed with freshly-initialised + // weights and a runtime warning is logged so the caller can tell + // checkpoint loading silently fell back to fresh init. tracing::warn!("CfC checkpoint loading not yet supported for GpuLinear weights: {}", path); Ok(Self { diff --git a/crates/ml/src/ensemble/adapters/tggn.rs b/crates/ml/src/ensemble/adapters/tggn.rs index 4e51de71c..a95500cc3 100644 --- a/crates/ml/src/ensemble/adapters/tggn.rs +++ b/crates/ml/src/ensemble/adapters/tggn.rs @@ -99,7 +99,9 @@ impl TggnInferenceAdapter { hidden_dim: usize, _path: &str, ) -> MLResult { - // TODO: Load GpuLinear weights from safetensors checkpoint + // safetensors -> GpuLinear weight loading is not wired for the + // TGGN adapter; return a freshly-initialised network and log a + // warning so callers can see the checkpoint was ignored. tracing::warn!("TGGN checkpoint loading not yet supported for GpuLinear weights"); Self::new(input_dim, hidden_dim) } diff --git a/crates/ml/src/ensemble/adapters/tlob.rs b/crates/ml/src/ensemble/adapters/tlob.rs index 13d6743b6..4459caa40 100644 --- a/crates/ml/src/ensemble/adapters/tlob.rs +++ b/crates/ml/src/ensemble/adapters/tlob.rs @@ -126,7 +126,9 @@ impl TlobInferenceAdapter { sequence_length: usize, _path: &str, ) -> MLResult { - // TODO: Load GpuLinear weights from safetensors checkpoint + // safetensors -> GpuLinear weight loading is not wired for the + // TLOB adapter; return a freshly-initialised network and log a + // warning so callers can see the checkpoint was ignored. tracing::warn!("TLOB checkpoint loading not yet supported for GpuLinear weights"); Self::new(feature_dim, hidden_dim, sequence_length) } diff --git a/crates/ml/src/ensemble/adapters/xlstm.rs b/crates/ml/src/ensemble/adapters/xlstm.rs index d38419bed..27a47fe2e 100644 --- a/crates/ml/src/ensemble/adapters/xlstm.rs +++ b/crates/ml/src/ensemble/adapters/xlstm.rs @@ -85,7 +85,9 @@ impl XlstmInferenceAdapter { let stream = extract_cuda_stream(&device)?; let network = XLSTMNetwork::new(&config, &stream)?; - // TODO: Load GpuLinear weights from safetensors checkpoint + // safetensors -> GpuLinear weight loading is not wired for the + // xLSTM adapter; the network is constructed fresh and a warning + // is logged so the ignored checkpoint path is visible at runtime. tracing::warn!("xLSTM checkpoint loading not yet supported for GpuLinear weights: {}", path); Ok(Self { diff --git a/crates/ml/src/ensemble/model_adapter.rs b/crates/ml/src/ensemble/model_adapter.rs index d120f6b84..03b06a425 100644 --- a/crates/ml/src/ensemble/model_adapter.rs +++ b/crates/ml/src/ensemble/model_adapter.rs @@ -28,9 +28,14 @@ impl EnsembleModelAdapter { impl MLModelAdapter for EnsembleModelAdapter { fn predict(&self, features: &[f64]) -> Result { - // TODO: Wire to real model inference via checkpoint loading when - // the model registry is production-ready. For now, return neutral - // prediction so the ensemble pipeline is fully wired end-to-end. + // This adapter returns a neutral (0.5 value, 0.0 confidence) + // prediction so the ensemble pipeline can be exercised end-to-end + // without a live model registry. The zero-confidence value is + // filtered out by the downstream ensemble threshold, so the stub + // never influences trading decisions. Real per-model inference + // goes through the dedicated adapters (liquid / tggn / tlob / + // xlstm) and only ends up here for models that have no wired + // checkpoint loader yet. let _ = features; Ok(MLPrediction { model_id: self.model_id.clone(), diff --git a/crates/ml/src/hyperopt/adapters/tft.rs b/crates/ml/src/hyperopt/adapters/tft.rs index d9c513948..a645f876d 100644 --- a/crates/ml/src/hyperopt/adapters/tft.rs +++ b/crates/ml/src/hyperopt/adapters/tft.rs @@ -627,7 +627,7 @@ mod tests { let params = TFTParams::default(); let config = TFTConfig { - input_dim: 51, // TODO: should be 42 market features (was 51, needs CUDA alignment) + input_dim: 51, // TFT CUDA layout: 5 static + 10 known + 36 unknown hidden_dim: params.hidden_size, // ✅ Was: hidden_size num_heads: params.num_heads, // ✅ Correct num_layers: 2, // ✅ Correct diff --git a/crates/ml/src/trainers/tft/trainer.rs b/crates/ml/src/trainers/tft/trainer.rs index bc623d565..405343cd7 100644 --- a/crates/ml/src/trainers/tft/trainer.rs +++ b/crates/ml/src/trainers/tft/trainer.rs @@ -245,14 +245,14 @@ impl TFTTrainer { self.progress_tx = Some(tx); } - /// Initialize optimizer with model parameters + /// Initialize optimizer with model parameters. /// - /// NOTE: candle-optimisers removed. TFT trainer optimizer requires migration - /// to `ml_core::cuda_autograd::GpuAdamW`. The legacy Adam wrapper no longer exists. + /// candle-optimisers has been removed from the workspace and the + /// TFT trainer's optimizer has not been migrated to + /// `ml_core::cuda_autograd::GpuAdamW` yet. Until that migration + /// lands this function returns an error so `train()` surfaces the + /// gap rather than silently running with a no-op optimizer. fn initialize_optimizer(&mut self) -> MLResult<()> { - // TODO: migrate to GpuAdamW from ml_core::cuda_autograd - // TODO: migrate to GpuAdamW from ml_core::cuda_autograd - // For now, return an error indicating the optimizer needs migration. let _lr = self.training_config.learning_rate; Err(MLError::ModelError( "TFT optimizer not yet migrated to GpuAdamW".to_string(), @@ -738,8 +738,13 @@ impl TFTTrainer { // ✅ Frees computation graph immediately // ✅ Prevents memory leaks // ✅ Maintains stable memory usage throughout training - // TODO: migrate to GpuAdamW backward pass - let _ = &self.optimizer; // placeholder — optimizer migration pending + // + // The backward pass is not wired: `initialize_optimizer` + // returns Err until the TFT path is ported onto GpuAdamW. + // The reference to `self.optimizer` here keeps the field + // visibly "in use" in the training loop so the migration + // target is obvious. + let _ = &self.optimizer; // Log progress every 100 batches if batch_count % 100 == 0 { diff --git a/crates/ml/src/trainers/tlob.rs b/crates/ml/src/trainers/tlob.rs index c88a6287a..c8ea04588 100644 --- a/crates/ml/src/trainers/tlob.rs +++ b/crates/ml/src/trainers/tlob.rs @@ -533,8 +533,11 @@ impl TLOBTrainer { info!("Saving checkpoint to: {}", checkpoint_path.display()); - // TODO: GpuVarStore checkpoint serialization not yet implemented - // When available, save parameters via safetensors format. + // GpuVarStore does not yet expose safetensors serialisation, so + // no parameters are written to `checkpoint_path`. The fs::metadata + // call below therefore fails — `save_checkpoint` is currently a + // non-functional hook kept so the training loop has a stable + // call site for when serialisation lands. let _ = &checkpoint_path; info!( @@ -596,7 +599,9 @@ impl TLOBTrainer { let temp_path = std::env::temp_dir().join(format!("tlob_{}.safetensors", uuid::Uuid::new_v4())); - // TODO: GpuVarStore serialization not yet implemented + // GpuVarStore serialisation is not wired; this function keeps + // the signature for callers but the subsequent `fs::read` will + // error out because nothing writes `temp_path`. let _ = &temp_path; let data = std::fs::read(&temp_path).context("Failed to read checkpoint")?; diff --git a/crates/ml/src/transformers/mod.rs b/crates/ml/src/transformers/mod.rs index a99a6b155..006cac1b3 100644 --- a/crates/ml/src/transformers/mod.rs +++ b/crates/ml/src/transformers/mod.rs @@ -29,10 +29,10 @@ // Core modules that compile successfully pub mod attention; -// Re-export core types that are implemented +// Re-export core types that are implemented. +// The `attention` module currently exposes `AttentionMask` only; +// multi-head / cross-modal / config wrappers are not implemented here. pub use attention::AttentionMask; -// TODO: Re-export remaining types when implemented: -// pub use attention::{AttentionConfig, CrossModalAttention, MultiHeadAttention}; /// Transformer model types optimized for different `HFT` use cases /// TransformerType component.