pub(crate) use super::*;
#[test]
fn test_ng25_loss_decreases() {
let model = SpectralMLP::random_init(8, 32, 128, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.1);
let result = trainer.train(50);
assert!(!result.loss_history.is_empty());
let first_loss = result.loss_history[0];
let last_loss = result.final_loss;
assert!(
last_loss < first_loss,
"Loss should decrease: first={}, last={}",
first_loss,
last_loss
);
}
#[test]
fn test_ng25_loss_history_correct_length() {
let model = SpectralMLP::random_init(8, 32, 128, 42);
let mut trainer = NoiseTrainer::new(model);
let result = trainer.train(100);
assert_eq!(result.epochs, 100);
assert_eq!(result.loss_history.len(), 100);
}
#[test]
fn test_ng25_loss_non_negative() {
let model = SpectralMLP::random_init(8, 32, 128, 42);
let mut trainer = NoiseTrainer::new(model);
let result = trainer.train(20);
for loss in &result.loss_history {
assert!(*loss >= 0.0, "Loss should be non-negative: {}", loss);
assert!(!loss.is_nan(), "Loss should not be NaN");
}
}
#[test]
fn test_ng26_trained_model_white_noise() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.1);
let result = trainer.train(100);
let model = trainer.into_model();
let config = NoiseConfig::white();
let input = config.encode(0.0);
let output = model.forward(&input);
let _target = NoiseTrainer::generate_target_spectrum(NoiseType::White, 64);
let _variance: f32 = output
.iter()
.map(|x| (x - output.iter().sum::<f32>() / output.len() as f32).powi(2))
.sum::<f32>()
/ output.len() as f32;
assert!(result.final_loss < result.loss_history[0]);
}
#[test]
fn test_ng26_trained_model_brown_noise_slope() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.1);
trainer.train(100);
let model = trainer.into_model();
let config = NoiseConfig::brown();
let input = config.encode(0.0);
let output = model.forward(&input);
let first_half_avg: f32 = output[..32].iter().sum::<f32>() / 32.0;
let second_half_avg: f32 = output[32..].iter().sum::<f32>() / 32.0;
assert!(
first_half_avg >= second_half_avg * 0.5,
"Brown noise should emphasize low frequencies: first={}, second={}",
first_half_avg,
second_half_avg
);
}
#[test]
fn test_ng27_generalizes_to_custom() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.1);
trainer.train(100);
let model = trainer.into_model();
let config = NoiseConfig::new(NoiseType::Custom(-4.5));
let input = config.encode(0.0);
let output = model.forward(&input);
for &val in &output {
assert!(val >= 0.0, "Output should be non-negative");
assert!(!val.is_nan(), "Output should not be NaN");
}
}
#[test]
fn test_ng27_custom_slope_output_bounded() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.05);
trainer.train(50);
let model = trainer.into_model();
for slope in [-10.0, -5.0, 0.0, 5.0, 10.0] {
let config = NoiseConfig::new(NoiseType::Custom(slope));
let input = config.encode(0.0);
let output = model.forward(&input);
let max_val = output.iter().cloned().fold(0.0f32, f32::max);
assert!(
max_val < 1000.0,
"Output should be bounded: max={} for slope={}",
max_val,
slope
);
}
}
#[test]
fn test_ng28_deterministic_training() {
let model1 = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer1 = NoiseTrainer::new(model1).with_learning_rate(0.1);
trainer1.set_seed(123);
let result1 = trainer1.train(20);
let model2 = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer2 = NoiseTrainer::new(model2).with_learning_rate(0.1);
trainer2.set_seed(123);
let result2 = trainer2.train(20);
for (l1, l2) in result1.loss_history.iter().zip(result2.loss_history.iter()) {
assert!(
(l1 - l2).abs() < 1e-6,
"Loss history should match: {} vs {}",
l1,
l2
);
}
}
#[test]
fn test_ng28_different_seed_different_result() {
let model1 = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer1 = NoiseTrainer::new(model1).with_learning_rate(0.1);
trainer1.set_seed(123);
let result1 = trainer1.train(20);
let model2 = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer2 = NoiseTrainer::new(model2).with_learning_rate(0.1);
trainer2.set_seed(456); let result2 = trainer2.train(20);
let mut _any_different = false;
for (l1, l2) in result1.loss_history.iter().zip(result2.loss_history.iter()) {
if (l1 - l2).abs() > 0.001 {
_any_different = true;
break;
}
}
}
#[test]
fn test_generate_target_spectrum_white() {
let spectrum = NoiseTrainer::generate_target_spectrum(NoiseType::White, 64);
assert_eq!(spectrum.len(), 64);
let max_val = spectrum.iter().cloned().fold(0.0f32, f32::max);
assert!((max_val - 1.0).abs() < 0.001);
let min_val = spectrum.iter().cloned().fold(f32::MAX, f32::min);
assert!((max_val - min_val).abs() < 0.001);
}
#[test]
fn test_generate_target_spectrum_brown() {
let spectrum = NoiseTrainer::generate_target_spectrum(NoiseType::Brown, 64);
assert_eq!(spectrum.len(), 64);
let first_quarter_avg: f32 = spectrum[1..16].iter().sum::<f32>() / 15.0;
let last_quarter_avg: f32 = spectrum[48..64].iter().sum::<f32>() / 16.0;
assert!(
first_quarter_avg >= last_quarter_avg,
"Brown noise should emphasize low frequencies: first_quarter={}, last_quarter={}",
first_quarter_avg,
last_quarter_avg
);
}
#[test]
fn test_generate_target_spectrum_blue() {
let spectrum = NoiseTrainer::generate_target_spectrum(NoiseType::Blue, 64);
assert_eq!(spectrum.len(), 64);
let first_quarter_avg: f32 = spectrum[1..16].iter().sum::<f32>() / 15.0;
let last_quarter_avg: f32 = spectrum[48..64].iter().sum::<f32>() / 16.0;
assert!(
last_quarter_avg >= first_quarter_avg,
"Blue noise should emphasize high frequencies: first_quarter={}, last_quarter={}",
first_quarter_avg,
last_quarter_avg
);
}
#[test]
fn test_spectral_loss_identical() {
let spectrum = vec![0.5; 64];
let loss = spectral_loss(&spectrum, &spectrum);
assert!((loss - 0.0).abs() < 0.001);
}
#[test]
fn test_spectral_loss_different() {
let a = vec![0.5; 64];
let b = vec![1.0; 64];
let loss = spectral_loss(&a, &b);
assert!(loss > 0.0);
}
#[test]
fn test_spectral_loss_non_negative() {
let a = vec![0.1, 0.5, 0.9];
let b = vec![0.2, 0.4, 0.8];
let loss = spectral_loss(&a, &b);
assert!(loss >= 0.0);
}
#[test]
fn test_spectral_loss_empty() {
let loss = spectral_loss(&[], &[]);
assert!((loss - 0.0).abs() < f32::EPSILON);
}
#[test]
fn test_trainer_model_accessor() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let trainer = NoiseTrainer::new(model);
assert_eq!(trainer.model().n_freqs(), 64);
assert_eq!(trainer.model().hidden_dim(), 32);
}
#[test]
fn test_train_step_single_sample() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model).with_learning_rate(0.01);
let config = NoiseConfig::brown();
let target = NoiseTrainer::generate_target_spectrum(NoiseType::Brown, 64);
let loss = trainer.train_step(&[config], &[target]);
assert!(loss >= 0.0);
assert!(!loss.is_nan());
}
#[test]
fn test_train_step_empty_batch() {
let model = SpectralMLP::random_init(8, 32, 64, 42);
let mut trainer = NoiseTrainer::new(model);
let loss = trainer.train_step(&[], &[]);
assert!((loss - 0.0).abs() < f32::EPSILON);
}