diff --git a/crates/ml-alpha/src/data/loader.rs b/crates/ml-alpha/src/data/loader.rs index 5807dba0c..b779a53bf 100644 --- a/crates/ml-alpha/src/data/loader.rs +++ b/crates/ml-alpha/src/data/loader.rs @@ -557,7 +557,7 @@ mod inference_mode_tests { files, predecoded_dir: mbp10.clone(), seq_len: 1, - horizons: [30, 100, 300, 1000, 6000], + horizons: crate::heads::HORIZONS, n_max_sequences: 1, seed: 0xCAFEF00D, inference_only, @@ -637,7 +637,7 @@ mod inference_mode_tests { files: vec![std::path::PathBuf::from("/tmp/__nonexistent_foxhunt.dbn.zst")], predecoded_dir: std::path::PathBuf::from("/tmp"), seq_len: 1, - horizons: [30, 100, 300, 1000, 6000], + horizons: crate::heads::HORIZONS, n_max_sequences: 1, seed: 0, inference_only: false, diff --git a/crates/ml-alpha/tests/gpu_log_ring_invariants.rs b/crates/ml-alpha/tests/gpu_log_ring_invariants.rs index 80ce246e0..ff4176881 100644 --- a/crates/ml-alpha/tests/gpu_log_ring_invariants.rs +++ b/crates/ml-alpha/tests/gpu_log_ring_invariants.rs @@ -170,7 +170,7 @@ fn magic_validation_skips_torn() { assert_eq!(v["kname"], "smoothness_controller"); assert_eq!(v["rt_name"], "input"); // Payload field comes from the smoothness_input schema. - assert!((v["payload"]["raw_h30"].as_f64().unwrap() - 100.0).abs() < 1e-6); + assert!((v["payload"]["raw_h10"].as_f64().unwrap() - 100.0).abs() < 1e-6); } #[test] diff --git a/crates/ml-alpha/tests/multi_horizon_loader.rs b/crates/ml-alpha/tests/multi_horizon_loader.rs index 02c83d5ac..6f9f9e75b 100644 --- a/crates/ml-alpha/tests/multi_horizon_loader.rs +++ b/crates/ml-alpha/tests/multi_horizon_loader.rs @@ -5,6 +5,7 @@ use ml_alpha::data::loader::{ discover_mbp10_files_sorted, MultiHorizonLoader, MultiHorizonLoaderConfig, }; +use ml_alpha::heads::{HORIZONS, N_HORIZONS}; use std::path::PathBuf; fn cfg_from_env() -> Option { @@ -19,7 +20,7 @@ fn cfg_from_env() -> Option { files, predecoded_dir: predec, seq_len: 64, - horizons: [30, 100, 300, 1000, 6000], + horizons: HORIZONS, n_max_sequences: 100, seed: 0xA1A2_A3A4, inference_only: false, @@ -39,7 +40,7 @@ fn loader_yields_seq_with_valid_labels() { let mut count = 0; while let Some(seq) = loader.next_sequence().expect("next") { assert_eq!(seq.snapshots.len(), cfg.seq_len); - for h in 0..5 { + for h in 0..N_HORIZONS { assert_eq!(seq.labels[h].len(), cfg.seq_len); for &v in &seq.labels[h] { assert!(v.is_nan() || v == 0.0 || v == 1.0, "label not binary: {v}"); @@ -61,7 +62,7 @@ fn loader_errors_on_empty_files() { files: Vec::new(), predecoded_dir: PathBuf::from("/tmp/does_not_exist_foxhunt_test"), seq_len: 64, - horizons: [30, 100, 300, 1000, 6000], + horizons: HORIZONS, n_max_sequences: 10, seed: 0, inference_only: false, diff --git a/crates/ml-alpha/tests/output_smoothness_grad_finite_diff.rs b/crates/ml-alpha/tests/output_smoothness_grad_finite_diff.rs index 1e1038ade..e1d0b45fd 100644 --- a/crates/ml-alpha/tests/output_smoothness_grad_finite_diff.rs +++ b/crates/ml-alpha/tests/output_smoothness_grad_finite_diff.rs @@ -195,7 +195,7 @@ fn smoothness_grad_matches_finite_difference() { // Vary in k so derivatives are non-trivial. probs[i] = 0.2 + 0.05 * ((i % 7) as f32) + 0.03 * ((i / 7) as f32 % 5.0); } - let lambda: [f32; N_HORIZONS] = [0.1, 0.3, 1.0, 3.0, 10.0]; // mimic the production ratio + let lambda: [f32; N_HORIZONS] = [0.1, 1.0, 10.0]; // mimic the production ratio (3 horizons) let zero_init = vec![0.0_f32; probs.len()]; let base = run_smoothness_kernel(&dev, &probs, &lambda, k, b, &zero_init).unwrap(); diff --git a/crates/ml-alpha/tests/perception_overfit.rs b/crates/ml-alpha/tests/perception_overfit.rs index cef6a1669..27129b94a 100644 --- a/crates/ml-alpha/tests/perception_overfit.rs +++ b/crates/ml-alpha/tests/perception_overfit.rs @@ -1,12 +1,13 @@ //! PerceptionTrainer synthetic-overfit smoke. //! //! Mirrors `perception_overfit.rs` but on the stacked trainer. The -//! signal is constant direction=+1, label=[1; 5]. Asserts the BCE loss +//! signal is constant direction=+1, label=[1; N_HORIZONS]. Asserts the BCE loss //! shrinks at least 40% over the training budget — proves the //! full forward + backward (Mamba2 + CfC + heads) wires up correctly //! end-to-end and that all 6 AdamW optimizers actually move weights. use ml_alpha::cfc::snap_features::Mbp10RawInput; +use ml_alpha::heads::N_HORIZONS; use ml_alpha::trainer::perception::{PerceptionTrainer, PerceptionTrainerConfig}; use ml_core::device::MlDevice; @@ -18,7 +19,7 @@ fn synthetic_seq( seq_len: usize, mut prev_mid: f32, mut ts_ns: u64, -) -> (Vec, Vec<[f32; 5]>) { +) -> (Vec, Vec<[f32; N_HORIZONS]>) { let mut out = Vec::with_capacity(seq_len); for k in 0..seq_len { let next_mid = prev_mid + 0.25; @@ -49,7 +50,7 @@ fn synthetic_seq( // Per-position labels: every position knows the next K snapshots all // move up (synthetic monotone ramp), so label = 1.0 for every horizon // at every position. Drives the trainer to learn "always predict 1". - let labels = vec![[1.0; 5]; seq_len]; + let labels = vec![[1.0; N_HORIZONS]; seq_len]; (out, labels) } @@ -70,7 +71,7 @@ fn stacked_trainer_loss_shrinks_on_constant_signal() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x4242, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -146,7 +147,7 @@ fn stacked_trainer_loss_shrinks_with_stride_4() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0xC4C4, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -202,7 +203,7 @@ fn stacked_trainer_loss_shrinks_at_batch_32() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0xB32B, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 32, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -215,9 +216,9 @@ fn stacked_trainer_loss_shrinks_at_batch_32() { let make_batch = |prev_mid: &mut f32, ts_base: &mut u64, cfg: &PerceptionTrainerConfig| - -> (Vec>, Vec>) { + -> (Vec>, Vec>) { let mut seqs: Vec> = Vec::with_capacity(cfg.n_batch); - let mut labels: Vec> = Vec::with_capacity(cfg.n_batch); + let mut labels: Vec> = Vec::with_capacity(cfg.n_batch); for _ in 0..cfg.n_batch { let (seq, lbl) = synthetic_seq(cfg.seq_len, *prev_mid, *ts_base); *prev_mid = 0.5 * (seq.last().unwrap().bid_px[0] + seq.last().unwrap().ask_px[0]); @@ -232,7 +233,7 @@ fn stacked_trainer_loss_shrinks_at_batch_32() { for warmup in 0..4 { let (seqs, labels) = make_batch(&mut prev_mid, &mut ts_base, &cfg); let seq_refs: Vec<&[Mbp10RawInput]> = seqs.iter().map(|s| s.as_slice()).collect(); - let lbl_refs: Vec<&[[f32; 5]]> = labels.iter().map(|l| l.as_slice()).collect(); + let lbl_refs: Vec<&[[f32; N_HORIZONS]]> = labels.iter().map(|l| l.as_slice()).collect(); let l = trainer.step_batched(&seq_refs, &lbl_refs).expect("warm step"); if warmup >= 2 { initial += l; @@ -244,7 +245,7 @@ fn stacked_trainer_loss_shrinks_at_batch_32() { for _ in 0..200 { let (seqs, labels) = make_batch(&mut prev_mid, &mut ts_base, &cfg); let seq_refs: Vec<&[Mbp10RawInput]> = seqs.iter().map(|s| s.as_slice()).collect(); - let lbl_refs: Vec<&[[f32; 5]]> = labels.iter().map(|l| l.as_slice()).collect(); + let lbl_refs: Vec<&[[f32; N_HORIZONS]]> = labels.iter().map(|l| l.as_slice()).collect(); trainer.step_batched(&seq_refs, &lbl_refs).expect("train step"); } @@ -252,7 +253,7 @@ fn stacked_trainer_loss_shrinks_at_batch_32() { for _ in 0..4 { let (seqs, labels) = make_batch(&mut prev_mid, &mut ts_base, &cfg); let seq_refs: Vec<&[Mbp10RawInput]> = seqs.iter().map(|s| s.as_slice()).collect(); - let lbl_refs: Vec<&[[f32; 5]]> = labels.iter().map(|l| l.as_slice()).collect(); + let lbl_refs: Vec<&[[f32; N_HORIZONS]]> = labels.iter().map(|l| l.as_slice()).collect(); final_loss += trainer.step_batched(&seq_refs, &lbl_refs).expect("final step"); } final_loss /= 4.0; @@ -302,7 +303,7 @@ fn evaluate_alone_succeeds() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x6262, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -314,7 +315,7 @@ fn evaluate_alone_succeeds() { let (seq, labels) = synthetic_seq(cfg.seq_len, prev_mid, ts); let (loss, probs) = trainer.evaluate(&seq, labels.as_slice()).expect("eval alone"); assert!(loss.is_finite(), "eval loss must be finite, got {loss}"); - assert_eq!(probs.len(), cfg.seq_len * 5); + assert_eq!(probs.len(), cfg.seq_len * N_HORIZONS); } /// Regression test for z2w9w cluster run: training step (which captures @@ -332,7 +333,7 @@ fn evaluate_works_after_captured_training_step() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x5151, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -354,7 +355,7 @@ fn evaluate_works_after_captured_training_step() { let (seq, labels) = synthetic_seq(cfg.seq_len, prev_mid, ts); let (loss, probs) = trainer.evaluate(&seq, labels.as_slice()).expect("evaluate after train"); assert!(loss.is_finite(), "eval loss must be finite, got {loss}"); - assert_eq!(probs.len(), cfg.seq_len * 5, "eval probs must be [K, 5] flat"); + assert_eq!(probs.len(), cfg.seq_len * N_HORIZONS, "eval probs must be [K, N_HORIZONS] flat"); assert!(probs.iter().all(|p| p.is_finite()), "eval probs must be finite"); } @@ -369,7 +370,7 @@ fn evaluate_works_after_capture_no_replay() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x8181, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -401,7 +402,7 @@ fn horizon_ema_and_lambda_track_after_training() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x9292, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None, @@ -459,7 +460,7 @@ fn evaluate_works_after_warmup_only() { lr_cfc: 3e-3, lr_mamba2: 1e-3, seed: 0x7171, - horizon_weights: [1.0; 5], + horizon_weights: [1.0; N_HORIZONS], n_batch: 1, smoothness_base_lambda: 0.0, kernel_step_trace_path: None,