diff --git a/ml/src/gpu_benchmarks/gpu_performance.rs b/ml/src/gpu_benchmarks/gpu_performance.rs index 9b3f2fe70..8890956da 100644 --- a/ml/src/gpu_benchmarks/gpu_performance.rs +++ b/ml/src/gpu_benchmarks/gpu_performance.rs @@ -1,6 +1,6 @@ //! GPU Performance Benchmarks for HFT ML Inference //! -//! Validates sub-100μs inference requirements with real workloads +//! Validates sub-100us inference requirements with real workloads #[cfg(any(test, feature = "benchmarks"))] use std::time::{Duration, Instant}; @@ -14,59 +14,3 @@ use tokio::runtime::Runtime; use crate::liquid::network::LiquidNetworkConfig; #[cfg(any(test, feature = "benchmarks"))] use crate::liquid::training::{ActivationType, LiquidTrainer, LiquidTrainingConfig}; - -#[cfg(test)] -mod tests { - use super::*; - use crate::training::{TrainingConfig, TrainingPipeline}; - - #[tokio::test] - async fn test_gpu_infrastructure_initialization() { - let infrastructure = TrainingPipeline::new(TrainingConfig::default()); - - assert!(infrastructure.device_capabilities().performance_score > 0.0); - } - - #[tokio::test] - async fn test_network_creation() -> Result<(), Box> { - let infrastructure = TrainingPipeline::new(TrainingConfig::default()); - - let network_config = crate::training::NetworkConfig { - input_dim: 10, - hidden_dims: vec![], - output_dim: 3, - activation: crate::training::ActivationType::ReLU, - dropout_rate: 0.0, - }; - - let network = infrastructure.create_network(network_config).await?; - // Network creation successful - Ok(()) - } - - #[tokio::test] - async fn test_inference_timing() -> Result<(), Box> { - let infrastructure = TrainingPipeline::new(TrainingConfig::default()); - - let network_config = crate::training::NetworkConfig { - input_dim: 25, - hidden_dims: vec![], - output_dim: 3, - activation: crate::training::ActivationType::ReLU, - dropout_rate: 0.0, - }; - - let network = infrastructure.create_network(network_config).await?; - let input = vec![0.5f32; 25]; - - let start = Instant::now(); - let result = network.inference_hft(&input).await?; - let inference_time = start.elapsed(); - - assert_eq!(result.len(), 3); - - println!("Inference time: {:?}", inference_time); - // Note: Actual performance depends on hardware - Ok(()) - } -} diff --git a/ml/src/training.rs b/ml/src/training.rs index c1ebff110..410b6be92 100644 --- a/ml/src/training.rs +++ b/ml/src/training.rs @@ -17,36 +17,7 @@ pub mod unified_trainer; // NEW: Unified training trait for all models // NEW: M // NO RE-EXPORTS - Use explicit imports: unified_data_loader::{...} -use std::collections::HashMap; -use std::time::Instant; - -use async_trait::async_trait; -use ndarray::Array1; use serde::{Deserialize, Serialize}; -use tokio::time::Duration; -use tracing::info; - -// Import CommonError for consistent error handling -use common::error::CommonError; - -/// Activation function types -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum ActivationType { - ReLU, - Sigmoid, - Tanh, - LeakyReLU, -} - -/// Network configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NetworkConfig { - pub input_dim: usize, - pub hidden_dims: Vec, - pub output_dim: usize, - pub dropout_rate: f64, - pub activation: ActivationType, -} /// Training configuration #[derive(Debug, Clone, Serialize, Deserialize)] @@ -72,162 +43,6 @@ impl Default for TrainingConfig { } } -/// Simple neural network implementation -#[derive(Debug, Clone)] -pub struct SimpleNeuralNetwork { - pub config: NetworkConfig, - pub weights: Vec>, - pub biases: Vec>, - pub is_trained: bool, -} - -impl SimpleNeuralNetwork { - /// Create a new neural network with the specified configuration - /// - /// # Errors - /// - /// Returns `CommonError` if: - /// - Configuration is invalid - /// - Weight initialization fails - /// - Memory allocation fails - /// - Layer dimensions are incompatible - pub fn new(config: NetworkConfig) -> Result { - let mut weights = Vec::new(); - let mut biases = Vec::new(); - - let mut layer_dims = vec![config.input_dim]; - layer_dims.extend(&config.hidden_dims); - layer_dims.push(config.output_dim); - - for i in 0..layer_dims.len() - 1 { - let input_size = layer_dims[i]; - let output_size = layer_dims[i + 1]; - - // Initialize weights with random values - let weight = Array1::from_vec( - (0..input_size * output_size) - .map(|_| fastrand::f64() * 2.0 - 1.0) - .collect(), - ); - weights.push(weight); - - // Initialize biases to zero - let bias = Array1::zeros(output_size); - biases.push(bias); - } - - Ok(Self { - config, - weights, - biases, - is_trained: false, - }) - } - - pub fn forward(&self, input: &Array1) -> Result, CommonError> { - let mut current = input.clone(); - - for (i, (weight, bias)) in self.weights.iter().zip(&self.biases).enumerate() { - // Simple matrix multiplication (simplified) - let output_size = bias.len(); - let mut output = Array1::zeros(output_size); - - for j in 0..output_size { - let mut sum = bias[j]; - for k in 0..current.len() { - sum += current[k] * weight[k * output_size + j]; - } - output[j] = sum; - } - - // Apply activation if not the last layer - if i < self.weights.len() - 1 { - current = self.apply_activation(&output)?; - } else { - current = output; - } - } - - Ok(current) - } - - pub fn apply_activation(&self, input: &Array1) -> Result, CommonError> { - let result = match self.config.activation { - ActivationType::ReLU => input.mapv(|x| x.max(0.0)), - ActivationType::Sigmoid => input.mapv(|x| 1.0 / (1.0 + (-x).exp())), - ActivationType::Tanh => input.mapv(|x| x.tanh()), - ActivationType::LeakyReLU => input.mapv(|x| if x > 0.0 { x } else { x * 0.01 }), - }; - Ok(result) - } - - pub async fn predict_fast(&self, input: &[f64]) -> Result, CommonError> { - if !self.is_trained { - return Err(CommonError::validation( - "Model must be trained before prediction".to_string(), - )); - } - - let input_array = Array1::from_vec(input.to_vec()); - let output = self.forward(&input_array)?; - Ok(output.to_vec()) - } -} - -/// Training metrics -#[derive(Debug, Default, Clone)] -pub struct TrainingMetrics { - pub train_losses: Vec, - pub validation_losses: Vec, - pub train_accuracies: Vec, - pub validation_accuracies: Vec, - pub best_validation_accuracy: f64, - pub best_epoch: usize, - pub final_train_loss: f64, - pub final_validation_loss: f64, - pub early_stopped: bool, - pub training_time_seconds: f64, - start_time: Option, -} - -impl TrainingMetrics { - pub fn new() -> Self { - Self { - start_time: Some(Instant::now()), - ..Default::default() - } - } - - pub fn add_epoch_results( - &mut self, - train_loss: f64, - val_loss: f64, - train_acc: f64, - val_acc: f64, - epoch: usize, - ) { - self.train_losses.push(train_loss); - self.validation_losses.push(val_loss); - self.train_accuracies.push(train_acc); - self.validation_accuracies.push(val_acc); - - if val_acc > self.best_validation_accuracy { - self.best_validation_accuracy = val_acc; - self.best_epoch = epoch; - } - - self.final_train_loss = train_loss; - self.final_validation_loss = val_loss; - } - - pub fn complete_training(&mut self, early_stopped: bool) { - self.early_stopped = early_stopped; - if let Some(start) = self.start_time { - self.training_time_seconds = start.elapsed().as_secs_f64(); - } - } -} - /// Device capabilities for performance scoring #[derive(Debug, Clone)] pub struct DeviceCapabilities { @@ -246,125 +61,6 @@ impl DeviceCapabilities { } } -/// Network interface trait -#[async_trait] -pub trait NetworkInterface { - async fn inference_hft(&self, input: &[f32]) -> Result, CommonError>; -} - -#[cfg(test)] -/// Mock network for testing only - isolated from production -#[derive(Debug, Clone)] -pub struct MockNetwork { - config: NetworkConfig, -} - -#[cfg(test)] -impl MockNetwork { - pub fn new(config: NetworkConfig) -> Self { - Self { config } - } -} - -#[cfg(test)] -#[async_trait] -impl NetworkInterface for MockNetwork { - async fn inference_hft(&self, _input: &[f32]) -> Result, CommonError> { - // Mock inference - just return zeros of expected output size - Ok(vec![0.0; self.config.output_dim]) - } -} - -/// Training pipeline -#[derive(Debug)] -pub struct TrainingPipeline { - pub config: TrainingConfig, - models: HashMap, - device_caps: DeviceCapabilities, - statistics: HashMap, -} - -impl TrainingPipeline { - pub fn new(config: TrainingConfig) -> Self { - let mut statistics = HashMap::new(); - statistics.insert("total_models".to_string(), 0.0); - statistics.insert("trained_models".to_string(), 0.0); - - Self { - config, - models: HashMap::new(), - device_caps: DeviceCapabilities::cpu_default(), - statistics, - } - } - - pub fn device_capabilities(&self) -> &DeviceCapabilities { - &self.device_caps - } - - pub fn register_model( - &mut self, - name: String, - model: SimpleNeuralNetwork, - ) -> Result<(), CommonError> { - self.models.insert(name, model); - if let Some(total_models) = self.statistics.get_mut("total_models") { - *total_models += 1.0; - } - Ok(()) - } - - #[cfg(test)] - pub async fn create_network(&self, config: NetworkConfig) -> Result { - let network = MockNetwork::new(config); - Ok(network) - } - - pub async fn train_all_models( - &mut self, - _training_data: &[(Array1, Array1)], - ) -> Result, CommonError> { - let mut results = HashMap::new(); - - for (name, model) in &mut self.models { - info!("Training model: {}", name); - - let mut metrics = TrainingMetrics::new(); - - // SIMPLIFIED: Basic training loop - full implementation requires actual model training - // TODO: Implement proper gradient descent, backpropagation, and loss calculation - tracing::warn!( - "train_all: using placeholder training loop. Use model-specific trainers for production." - ); - for epoch in 0..self.config.epochs.min(3) { - // Placeholder metrics - real implementation needs actual training - let train_loss = 1.0 / (epoch as f64 + 1.0); - let val_loss = train_loss * 1.1; - let train_acc = 0.5 + 0.3 * epoch as f64 / self.config.epochs as f64; - let val_acc = train_acc * 0.9; - - metrics.add_epoch_results(train_loss, val_loss, train_acc, val_acc, epoch); - - tokio::time::sleep(Duration::from_millis(10)).await; - } - - model.is_trained = true; - metrics.complete_training(false); - - results.insert(name.clone(), metrics); - if let Some(trained_models) = self.statistics.get_mut("trained_models") { - *trained_models += 1.0; - } - } - - Ok(results) - } - - pub fn get_statistics(&self) -> &HashMap { - &self.statistics - } -} - #[cfg(test)] mod tests { use super::*; @@ -380,173 +76,10 @@ mod tests { } #[test] - fn test_network_creation() -> Result<(), CommonError> { - let config = NetworkConfig { - input_dim: 5, - hidden_dims: vec![10, 8], - output_dim: 3, - activation: ActivationType::ReLU, - dropout_rate: 0.1, - }; - - let network = SimpleNeuralNetwork::new(config.clone())?; - assert_eq!(network.config.input_dim, 5); - assert_eq!(network.config.output_dim, 3); - assert!(!network.is_trained); - assert_eq!(network.weights.len(), 3); // 2 hidden + 1 output - assert_eq!(network.biases.len(), 3); - - Ok(()) - } - - #[test] - fn test_forward_pass() -> Result<(), CommonError> { - let config = NetworkConfig { - input_dim: 3, - hidden_dims: vec![5], - output_dim: 2, - activation: ActivationType::ReLU, - dropout_rate: 0.0, - }; - - let network = SimpleNeuralNetwork::new(config)?; - let input = Array1::from(vec![1.0, 2.0, 3.0]); - let output = network.forward(&input)?; - - assert_eq!(output.len(), 2); - - Ok(()) - } - - #[test] - fn test_activation_functions() -> Result<(), CommonError> { - let input = Array1::from(vec![-2.0, -1.0, 0.0, 1.0, 2.0]); - - // Test ReLU - let relu_config = NetworkConfig { - input_dim: 5, - hidden_dims: vec![], - output_dim: 5, - activation: ActivationType::ReLU, - dropout_rate: 0.0, - }; - let relu_network = SimpleNeuralNetwork::new(relu_config)?; - let relu_output = relu_network.apply_activation(&input)?; - - // ReLU should clip negative values to 0 - assert!(relu_output[0] >= 0.0); - assert!(relu_output[1] >= 0.0); - - // Test Sigmoid - let sigmoid_config = NetworkConfig { - input_dim: 5, - hidden_dims: vec![], - output_dim: 5, - activation: ActivationType::Sigmoid, - dropout_rate: 0.0, - }; - let sigmoid_network = SimpleNeuralNetwork::new(sigmoid_config)?; - let sigmoid_output = sigmoid_network.apply_activation(&input)?; - - // Sigmoid output should be between 0 and 1 - for &val in &sigmoid_output { - assert!(val >= 0.0 && val <= 1.0); - } - - Ok(()) - } - - #[tokio::test] - async fn test_training_pipeline() -> Result<(), CommonError> { - // Create a simple training dataset - let mut training_data = Vec::new(); - for i in 0..100 { - let x = i as f64 * 0.1; - let input = Array1::from(vec![x, x * x]); - let output = Array1::from(vec![x * 2.0]); // Simple linear relationship - training_data.push((input, output)); - } - - let config = TrainingConfig { - epochs: 10, - learning_rate: 0.01, - batch_size: 10, - validation_split: 0.2, - early_stopping_patience: Some(5), - random_seed: Some(42), - }; - - let mut pipeline = TrainingPipeline::new(config); - - // Create and register a simple model - let model_config = NetworkConfig { - input_dim: 2, - hidden_dims: vec![4], - output_dim: 1, - activation: ActivationType::ReLU, - dropout_rate: 0.0, - }; - - let model = SimpleNeuralNetwork::new(model_config)?; - pipeline.register_model("test_model".to_string(), model)?; - - // Train the model - let results = pipeline.train_all_models(&training_data).await?; - - assert_eq!(results.len(), 1); - assert!(results.contains_key("test_model")); - - let stats = pipeline.get_statistics(); - assert_eq!(stats.get("total_models").unwrap(), &1.0); - assert_eq!(stats.get("trained_models").unwrap(), &1.0); - - Ok(()) - } - - #[tokio::test] - async fn test_fast_inference() -> Result<(), CommonError> { - let config = NetworkConfig { - input_dim: 4, - hidden_dims: vec![8], - output_dim: 2, - activation: ActivationType::ReLU, - dropout_rate: 0.0, - }; - - let mut network = SimpleNeuralNetwork::new(config)?; - - // Test prediction before training (should fail) - let input = vec![1.0, 2.0, 3.0, 4.0]; - let result = network.predict_fast(&input).await; - assert!(result.is_err()); - - // Mock training by setting is_trained to true - network.is_trained = true; - - // Test prediction after "training" - let result = network.predict_fast(&input).await?; - assert_eq!(result.len(), 2); - - Ok(()) - } - - #[test] - fn test_training_metrics() { - let mut metrics = TrainingMetrics::new(); - - // Add some epoch results - metrics.add_epoch_results(0.8, 0.9, 0.7, 0.6, 0); - metrics.add_epoch_results(0.6, 0.7, 0.8, 0.75, 1); - metrics.add_epoch_results(0.5, 0.6, 0.85, 0.8, 2); - - assert_eq!(metrics.train_losses.len(), 3); - assert_eq!(metrics.best_validation_accuracy, 0.8); - assert_eq!(metrics.best_epoch, 2); - assert_relative_eq!(metrics.final_train_loss, 0.5, epsilon = 1e-10); - assert_relative_eq!(metrics.final_validation_loss, 0.6, epsilon = 1e-10); - - metrics.complete_training(false); - assert!(!metrics.early_stopped); - assert!(metrics.training_time_seconds >= 0.0); + fn test_device_capabilities_cpu_default() { + let caps = DeviceCapabilities::cpu_default(); + assert_eq!(caps.performance_score, 1.0); + assert_eq!(caps.memory_gb, 8.0); + assert!(caps.compute_units > 0); } }