#[cfg(test)]
use crate::optim::Optimizer;
#[cfg(test)]
use crate::Tensor;
#[cfg(test)]
pub fn test_quadratic_convergence<O: Optimizer>(
mut optimizer: O,
iterations: usize,
threshold: f32,
) -> bool {
let mut params = vec![Tensor::from_vec(vec![3.0, -2.0, 1.5, -2.5], true)];
for _ in 0..iterations {
let grad = params[0].data().mapv(|x| 2.0 * x);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
params[0].data().iter().all(|&val| val.abs() < threshold)
}
#[cfg(test)]
pub fn test_loss_decreases<O: Optimizer>(mut optimizer: O, iterations: usize) -> bool {
let mut params = vec![Tensor::from_vec(vec![10.0], true)];
let mut prev_loss = f32::INFINITY;
for _ in 0..iterations {
let x = params[0].data()[0];
let loss = x * x;
let grad = ndarray::arr1(&[2.0 * x]);
if loss > prev_loss + 1e-3 {
return false; }
prev_loss = loss;
params[0].set_grad(grad);
optimizer.step(&mut params);
}
true
}
#[cfg(test)]
#[allow(dead_code)]
pub fn test_rosenbrock_convergence<O: Optimizer>(
mut optimizer: O,
iterations: usize,
threshold: f32,
) -> bool {
let mut params = vec![Tensor::from_vec(vec![0.0, 0.0], true)];
let a = 1.0f32;
let b = 100.0f32;
for _ in 0..iterations {
let x = params[0].data()[0];
let y = params[0].data()[1];
let dx = -2.0 * (a - x) - 4.0 * b * x * (y - x * x);
let dy = 2.0 * b * (y - x * x);
let grad = ndarray::arr1(&[dx, dy]);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
let x = params[0].data()[0];
let y = params[0].data()[1];
(x - 1.0).abs() < threshold && (y - 1.0).abs() < threshold
}
#[cfg(test)]
pub fn test_ill_conditioned_convergence<O: Optimizer>(
mut optimizer: O,
iterations: usize,
threshold: f32,
) -> bool {
let mut params = vec![Tensor::from_vec(vec![10.0, 10.0], true)];
for _ in 0..iterations {
let x = params[0].data()[0];
let y = params[0].data()[1];
let grad = ndarray::arr1(&[x, 100.0 * y]);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
params[0].data().iter().all(|&val| val.abs() < threshold)
}
#[cfg(test)]
pub fn test_high_dim_convergence<O: Optimizer>(
mut optimizer: O,
dim: usize,
iterations: usize,
threshold: f32,
) -> bool {
let init: Vec<f32> = (0..dim).map(|i| (i as f32 + 1.0) * 0.5).collect();
let mut params = vec![Tensor::from_vec(init, true)];
for _ in 0..iterations {
let grad = params[0].data().mapv(|x| 2.0 * x);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
params[0].data().iter().all(|&val| val.abs() < threshold)
}
#[cfg(test)]
pub fn test_small_gradient_stability<O: Optimizer>(mut optimizer: O) -> bool {
let mut params = vec![Tensor::from_vec(vec![1e-6, 1e-6], true)];
for _ in 0..100 {
let grad = params[0].data().mapv(|x| 2.0 * x);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
params[0].data().iter().all(|&val| val.is_finite())
}
#[cfg(test)]
pub fn test_large_gradient_stability<O: Optimizer>(mut optimizer: O) -> bool {
let mut params = vec![Tensor::from_vec(vec![1e4, 1e4], true)];
for _ in 0..100 {
let grad = params[0].data().mapv(|x| 2.0 * x);
params[0].set_grad(grad);
optimizer.step(&mut params);
}
params[0].data().iter().all(|&val| val.is_finite())
}