//! Mamba2 SSM model integration test //! //! Verifies: sequence buffering -> SSM forward pass -> sigmoid direction/confidence //! Mamba2 requires sequence_length observations before producing real predictions. //! Uses lightweight config for fast execution (<10s) use ml::ensemble::adapters::Mamba2InferenceAdapter; use ml::ensemble::inference_adapter::{FeatureVector, ModelInferenceAdapter}; use ml::mamba::Mamba2Config; fn small_mamba2_config() -> Mamba2Config { Mamba2Config { d_model: 32, d_state: 8, d_head: 8, num_heads: 2, expand: 2, num_layers: 1, max_seq_len: 8, dropout: 0.0, ..Default::default() } } const SEQ_LEN: usize = 4; #[test] fn test_mamba2_adapter_buffers_before_predicting() { let adapter = Mamba2InferenceAdapter::new(small_mamba2_config(), SEQ_LEN) .expect("Mamba2InferenceAdapter::new should succeed"); assert_eq!(adapter.model_name(), "MAMBA-2"); assert!( !adapter.is_ready(), "Mamba2 should not be ready with empty buffer" ); // Feed SEQ_LEN - 1 feature vectors: should return neutral for i in 0..(SEQ_LEN - 1) { let fv = FeatureVector { values: vec![0.1 * (i as f64 + 1.0); 51], timestamp: 1_700_000_000 + i as i64, }; let pred = adapter.predict(&fv).expect("predict should not error"); assert_eq!(pred.direction, 0.0, "Neutral direction while buffering"); assert_eq!(pred.confidence, 0.0, "Zero confidence while buffering"); } } #[test] fn test_mamba2_produces_valid_prediction_after_warmup() { let adapter = Mamba2InferenceAdapter::new(small_mamba2_config(), SEQ_LEN) .expect("Mamba2InferenceAdapter::new should succeed"); // Fill buffer for i in 0..SEQ_LEN { let fv = FeatureVector { values: vec![0.1; 51], timestamp: 1_700_000_000 + i as i64, }; let _ = adapter.predict(&fv); } assert!( adapter.is_ready(), "Mamba2 should be ready after filling buffer" ); // Real prediction let fv = FeatureVector { values: vec![0.2; 51], timestamp: 1_700_000_000 + SEQ_LEN as i64, }; let pred = adapter.predict(&fv).expect("Mamba2 predict should succeed"); assert!( pred.direction >= -1.0 && pred.direction <= 1.0, "direction {} out of [-1,1]", pred.direction ); assert!( pred.confidence >= 0.0 && pred.confidence <= 1.0, "confidence {} out of [0,1]", pred.confidence ); assert!(pred.direction.is_finite(), "direction must not be NaN/Inf"); assert!( pred.confidence.is_finite(), "confidence must not be NaN/Inf" ); } #[test] fn test_mamba2_deterministic_with_same_sequence() { let adapter = Mamba2InferenceAdapter::new(small_mamba2_config(), SEQ_LEN) .expect("Mamba2InferenceAdapter::new should succeed"); // Fill buffer with identical values for _ in 0..SEQ_LEN { let fv = FeatureVector { values: vec![0.1; 51], timestamp: 1_700_000_000, }; let _ = adapter.predict(&fv); } // Two predictions with same input should be deterministic let fv = FeatureVector { values: vec![0.1; 51], timestamp: 1_700_000_000, }; let pred1 = adapter.predict(&fv).expect("predict1"); let pred2 = adapter.predict(&fv).expect("predict2"); assert!( (pred1.direction - pred2.direction).abs() < 1e-6, "Mamba2 should be deterministic: {} vs {}", pred1.direction, pred2.direction ); }