Wave 64-65 cleanup: Proto regeneration and build system updates from Tonic 0.12→0.14 upgrade Files updated: - Cargo.lock: Dependency resolution for Tonic 0.14.2 - All build.rs: Updated for tonic-prost-build - Proto files: Regenerated with tonic-prost 0.14 - Examples/tests: Updated for new gRPC API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
342 lines
9.7 KiB
Rust
342 lines
9.7 KiB
Rust
//! Liquid Networks Integration Tests
|
|
//!
|
|
//! Tests for Liquid Time-constant (LTC) and Closed-form Continuous-time (CfC)
|
|
//! neural networks with fixed-point arithmetic.
|
|
#![allow(unused_crate_dependencies)]
|
|
|
|
use ml::liquid::{
|
|
cells::{CfCConfig, LTCConfig},
|
|
ode_solvers::SolverType,
|
|
ActivationType, FixedPoint, LiquidNetworkConfig, NetworkType,
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_arithmetic() {
|
|
// Test basic fixed-point operations with Result handling
|
|
let a = FixedPoint::from_f64(1.5);
|
|
let b = FixedPoint::from_f64(2.5);
|
|
|
|
let sum = (a + b).expect("addition should not overflow");
|
|
assert!((sum.to_f64() - 4.0).abs() < 1e-6);
|
|
|
|
let diff = (b - a).expect("subtraction should not overflow");
|
|
assert!((diff.to_f64() - 1.0).abs() < 1e-6);
|
|
|
|
let product = (a * b).expect("multiplication should not overflow");
|
|
assert!((product.to_f64() - 3.75).abs() < 1e-6);
|
|
|
|
let quotient = (b / a).expect("division should not overflow");
|
|
assert!((quotient.to_f64() - (5.0 / 3.0)).abs() < 1e-6);
|
|
|
|
// Test precision handling
|
|
let precise = FixedPoint::from_f64(0.12345678);
|
|
let recovered = precise.to_f64();
|
|
assert!((recovered - 0.12345678).abs() < 1e-7);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_special_values() {
|
|
let zero = FixedPoint::zero();
|
|
assert_eq!(zero.to_f64(), 0.0);
|
|
|
|
let one = FixedPoint::one();
|
|
assert!((one.to_f64() - 1.0).abs() < 1e-8);
|
|
|
|
// Test is_finite
|
|
assert!(zero.is_finite());
|
|
assert!(one.is_finite());
|
|
assert!(FixedPoint::from_f64(1000.0).is_finite());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_overflow_handling() {
|
|
let large = FixedPoint::from_f64(1e10);
|
|
let result = large * large;
|
|
|
|
// Should return an error for overflow
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_division_by_zero() {
|
|
let a = FixedPoint::from_f64(1.0);
|
|
let zero = FixedPoint::zero();
|
|
|
|
let result = a / zero;
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ltc_config_creation() {
|
|
let config = LTCConfig {
|
|
input_size: 10,
|
|
hidden_size: 20,
|
|
tau_min: FixedPoint::from_f64(0.1),
|
|
tau_max: FixedPoint::from_f64(1.0),
|
|
use_bias: true,
|
|
solver_type: SolverType::Euler,
|
|
activation: ActivationType::Tanh,
|
|
};
|
|
|
|
assert_eq!(config.input_size, 10);
|
|
assert_eq!(config.hidden_size, 20);
|
|
assert!(config.use_bias);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_cfc_config_creation() {
|
|
let config = CfCConfig {
|
|
input_size: 15,
|
|
hidden_size: 30,
|
|
backbone_layers: vec![64, 32],
|
|
mixed_memory: true,
|
|
use_gate: true,
|
|
solver_type: SolverType::RK4,
|
|
};
|
|
|
|
assert_eq!(config.input_size, 15);
|
|
assert_eq!(config.hidden_size, 30);
|
|
assert_eq!(config.backbone_layers.len(), 2);
|
|
assert!(config.mixed_memory);
|
|
assert!(config.use_gate);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_liquid_network_config_creation() {
|
|
let config = LiquidNetworkConfig {
|
|
network_type: NetworkType::LTC,
|
|
input_size: 64,
|
|
output_size: 32,
|
|
layer_configs: vec![],
|
|
output_layer: ml::liquid::network::OutputLayerConfig {
|
|
use_linear_output: true,
|
|
output_activation: Some(ActivationType::Linear),
|
|
dropout_rate: None,
|
|
},
|
|
default_dt: FixedPoint::from_f64(0.01),
|
|
market_regime_adaptation: false,
|
|
};
|
|
|
|
assert_eq!(config.input_size, 64);
|
|
assert_eq!(config.output_size, 32);
|
|
assert!(matches!(config.network_type, NetworkType::LTC));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_comparison() {
|
|
let a = FixedPoint::from_f64(1.5);
|
|
let b = FixedPoint::from_f64(2.5);
|
|
let c = FixedPoint::from_f64(1.5);
|
|
|
|
assert!(a < b);
|
|
assert!(b > a);
|
|
assert_eq!(a, c);
|
|
assert_ne!(a, b);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_ordering() {
|
|
let mut values = vec![
|
|
FixedPoint::from_f64(3.0),
|
|
FixedPoint::from_f64(1.0),
|
|
FixedPoint::from_f64(2.0),
|
|
];
|
|
|
|
values.sort();
|
|
|
|
assert_eq!(values[0].to_f64(), 1.0);
|
|
assert_eq!(values[1].to_f64(), 2.0);
|
|
assert_eq!(values[2].to_f64(), 3.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_activation_types() {
|
|
// Test that all activation types can be created
|
|
let activations = vec![
|
|
ActivationType::Tanh,
|
|
ActivationType::Sigmoid,
|
|
ActivationType::ReLU,
|
|
ActivationType::LeakyReLU,
|
|
ActivationType::Linear,
|
|
];
|
|
|
|
for activation in activations {
|
|
// Just verify they can be constructed
|
|
let config = LTCConfig {
|
|
input_size: 4,
|
|
hidden_size: 8,
|
|
tau_min: FixedPoint::from_f64(0.1),
|
|
tau_max: FixedPoint::from_f64(1.0),
|
|
use_bias: true,
|
|
solver_type: SolverType::Euler,
|
|
activation,
|
|
};
|
|
assert_eq!(config.input_size, 4);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_solver_types() {
|
|
let solvers = vec![SolverType::Euler, SolverType::RK4, SolverType::Adaptive];
|
|
|
|
for solver in solvers {
|
|
let config = LTCConfig {
|
|
input_size: 5,
|
|
hidden_size: 10,
|
|
tau_min: FixedPoint::from_f64(0.1),
|
|
tau_max: FixedPoint::from_f64(1.0),
|
|
use_bias: false,
|
|
solver_type: solver,
|
|
activation: ActivationType::Tanh,
|
|
};
|
|
assert_eq!(config.hidden_size, 10);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_network_types() {
|
|
let types = vec![NetworkType::LTC, NetworkType::CfC, NetworkType::Mixed];
|
|
|
|
for network_type in types {
|
|
let config = LiquidNetworkConfig {
|
|
network_type,
|
|
input_size: 16,
|
|
output_size: 8,
|
|
layer_configs: vec![],
|
|
output_layer: ml::liquid::network::OutputLayerConfig {
|
|
use_linear_output: true,
|
|
output_activation: Some(ActivationType::Linear),
|
|
dropout_rate: None,
|
|
},
|
|
default_dt: FixedPoint::from_f64(0.01),
|
|
market_regime_adaptation: false,
|
|
};
|
|
assert_eq!(config.input_size, 16);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_negative_values() {
|
|
let neg = FixedPoint::from_f64(-5.5);
|
|
let pos = FixedPoint::from_f64(3.0);
|
|
|
|
let sum = (neg + pos).expect("addition should work");
|
|
assert!((sum.to_f64() - (-2.5)).abs() < 1e-6);
|
|
|
|
let product = (neg * pos).expect("multiplication should work");
|
|
assert!((product.to_f64() - (-16.5)).abs() < 1e-6);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_chain_operations() {
|
|
let a = FixedPoint::from_f64(2.0);
|
|
let b = FixedPoint::from_f64(3.0);
|
|
let c = FixedPoint::from_f64(4.0);
|
|
|
|
// Test: (a + b) * c
|
|
let sum = (a + b).expect("addition should work");
|
|
let result = (sum * c).expect("multiplication should work");
|
|
assert!((result.to_f64() - 20.0).abs() < 1e-6);
|
|
|
|
// Test: (a * b) + c
|
|
let product = (a * b).expect("multiplication should work");
|
|
let result2 = (product + c).expect("addition should work");
|
|
assert!((result2.to_f64() - 10.0).abs() < 1e-6);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_with_varying_sizes() {
|
|
let sizes = vec![(2, 4), (10, 20), (50, 100), (128, 256)];
|
|
|
|
for (input_size, hidden_size) in sizes {
|
|
let config = LTCConfig {
|
|
input_size,
|
|
hidden_size,
|
|
tau_min: FixedPoint::from_f64(0.1),
|
|
tau_max: FixedPoint::from_f64(1.0),
|
|
use_bias: true,
|
|
solver_type: SolverType::Euler,
|
|
activation: ActivationType::Tanh,
|
|
};
|
|
|
|
assert_eq!(config.input_size, input_size);
|
|
assert_eq!(config.hidden_size, hidden_size);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_time_constant_ranges() {
|
|
let tau_ranges = vec![(0.01, 0.1), (0.1, 1.0), (1.0, 10.0)];
|
|
|
|
for (tau_min, tau_max) in tau_ranges {
|
|
let config = LTCConfig {
|
|
input_size: 8,
|
|
hidden_size: 16,
|
|
tau_min: FixedPoint::from_f64(tau_min),
|
|
tau_max: FixedPoint::from_f64(tau_max),
|
|
use_bias: true,
|
|
solver_type: SolverType::Euler,
|
|
activation: ActivationType::Tanh,
|
|
};
|
|
|
|
assert!((config.tau_min.to_f64() - tau_min).abs() < 1e-6);
|
|
assert!((config.tau_max.to_f64() - tau_max).abs() < 1e-6);
|
|
assert!(config.tau_min < config.tau_max);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_cfc_backbone_configurations() {
|
|
let backbone_configs = vec![vec![32], vec![64, 32], vec![128, 64, 32]];
|
|
|
|
for backbone in backbone_configs {
|
|
let expected_len = backbone.len();
|
|
let config = CfCConfig {
|
|
input_size: 16,
|
|
hidden_size: 32,
|
|
backbone_layers: backbone.clone(),
|
|
mixed_memory: true,
|
|
use_gate: true,
|
|
solver_type: SolverType::RK4,
|
|
};
|
|
|
|
assert_eq!(config.backbone_layers.len(), expected_len);
|
|
assert_eq!(config.backbone_layers, backbone);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fixed_point_precision_limits() {
|
|
// Test values near precision limits
|
|
let small = FixedPoint::from_f64(1e-7);
|
|
assert!(small.to_f64() > 0.0);
|
|
assert!(small.to_f64() < 1e-6);
|
|
|
|
let large = FixedPoint::from_f64(1e6);
|
|
assert!(large.to_f64() > 999999.0);
|
|
assert!(large.to_f64() < 1000001.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_dt_values() {
|
|
let dt_values = vec![0.001, 0.01, 0.05, 0.1];
|
|
|
|
for dt in dt_values {
|
|
let config = LiquidNetworkConfig {
|
|
network_type: NetworkType::LTC,
|
|
input_size: 10,
|
|
output_size: 5,
|
|
layer_configs: vec![],
|
|
output_layer: ml::liquid::network::OutputLayerConfig {
|
|
use_linear_output: true,
|
|
output_activation: Some(ActivationType::Linear),
|
|
dropout_rate: None,
|
|
},
|
|
default_dt: FixedPoint::from_f64(dt),
|
|
market_regime_adaptation: false,
|
|
};
|
|
|
|
assert!((config.default_dt.to_f64() - dt).abs() < 1e-8);
|
|
}
|
|
}
|