#![cfg(all(feature = "serde", not(target_arch = "wasm32")))]
use basin::{
BasicState, CheckpointWriter, CostFunction, Executor, Gradient, GradientDescent, ObserverMode,
State, read_checkpoint,
};
struct Quadratic;
impl CostFunction for Quadratic {
type Param = Vec<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
Ok(0.5 * x.iter().map(|v| v * v).sum::<f64>())
}
}
impl Gradient for Quadratic {
type Gradient = Vec<f64>;
fn gradient(&self, x: &Vec<f64>) -> Result<Vec<f64>, std::convert::Infallible> {
Ok(x.clone())
}
}
#[test]
fn checkpoint_resume_matches_uninterrupted_run() {
let dir = std::env::temp_dir().join(format!("basin-ckpt-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("run.ckpt");
let start = vec![5.0, -3.0, 2.0];
let step = 0.1;
let reference = Executor::new(
Quadratic,
GradientDescent::new(step),
BasicState::new(start.clone()),
)
.max_iter(20)
.run()
.unwrap();
Executor::new(
Quadratic,
GradientDescent::new(step),
BasicState::new(start),
)
.max_iter(12)
.observe_with(CheckpointWriter::new(&path), ObserverMode::Every(4))
.run()
.unwrap();
let reloaded: BasicState<Vec<f64>> = read_checkpoint(&path).unwrap();
assert_eq!(reloaded.iter(), 12);
let resumed = Executor::new(Quadratic, GradientDescent::new(step), reloaded)
.max_iter(20)
.run()
.unwrap();
assert_eq!(resumed.iter(), 20);
for (a, b) in resumed.param().iter().zip(reference.param()) {
assert!((a - b).abs() < 1e-12, "resumed {a} vs reference {b}");
}
assert!((resumed.cost() - reference.cost()).abs() < 1e-12);
std::fs::remove_dir_all(&dir).ok();
}