refactor(ml): delete legacy SimpleNeuralNetwork and TrainingPipeline dead code

Remove 520 lines of ndarray-based placeholder code that never performed real
gradient descent. Production training uses Candle-based trainers in ml::trainers/.

Deleted: SimpleNeuralNetwork, TrainingPipeline, NetworkConfig, ActivationType,
TrainingMetrics, NetworkInterface, MockNetwork, and 9 associated tests.
Kept: DeviceCapabilities, TrainingConfig, sub-module re-exports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-23 00:07:59 +01:00
parent 200deaafe2
commit 20c09d8a54
2 changed files with 6 additions and 529 deletions

View File

@@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(())
}
}

View File

@@ -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<usize>,
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<Array1<f64>>,
pub biases: Vec<Array1<f64>>,
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<Self, CommonError> {
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<f64>) -> Result<Array1<f64>, 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<f64>) -> Result<Array1<f64>, 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<Vec<f64>, 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<f64>,
pub validation_losses: Vec<f64>,
pub train_accuracies: Vec<f64>,
pub validation_accuracies: Vec<f64>,
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<Instant>,
}
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<Vec<f32>, 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<Vec<f32>, 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<String, SimpleNeuralNetwork>,
device_caps: DeviceCapabilities,
statistics: HashMap<String, f64>,
}
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<MockNetwork, CommonError> {
let network = MockNetwork::new(config);
Ok(network)
}
pub async fn train_all_models(
&mut self,
_training_data: &[(Array1<f64>, Array1<f64>)],
) -> Result<HashMap<String, TrainingMetrics>, 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<String, f64> {
&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);
}
}