Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
3.6 KiB
Rust
122 lines
3.6 KiB
Rust
//! 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
|
|
);
|
|
}
|