use fugue_evo::prelude::*;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use std::path::{Path, PathBuf};
const DIM: usize = 10;
const POP_SIZE: usize = 100;
const SEED: u64 = 42;
const TOTAL_GENERATIONS: usize = 20;
const CHECKPOINT_AT: usize = 10;
type SphereGa = SimpleGA<
RealVector,
f64,
TournamentSelection,
SbxCrossover,
PolynomialMutation,
Sphere,
MaxGenerations,
>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Checkpointing and Reproducible Recovery ===\n");
let checkpoint_dir = PathBuf::from("/tmp/fugue_evo_checkpoints");
if checkpoint_dir.exists() {
std::fs::remove_dir_all(&checkpoint_dir)?;
}
std::fs::create_dir_all(&checkpoint_dir)?;
let straight_best = run_straight(TOTAL_GENERATIONS);
println!("Straight run ({TOTAL_GENERATIONS} gens): best = {straight_best:.12}");
let resumed_best = run_with_checkpoint(&checkpoint_dir, TOTAL_GENERATIONS, CHECKPOINT_AT)?;
println!(
"Resumed run ({CHECKPOINT_AT} + {} gens via disk): best = {resumed_best:.12}",
TOTAL_GENERATIONS - CHECKPOINT_AT
);
println!();
if straight_best.to_bits() == resumed_best.to_bits() {
println!("SUCCESS: resumed run is bit-identical to the uninterrupted run.");
} else {
return Err(format!(
"reproducibility broken: straight={straight_best} resumed={resumed_best}"
)
.into());
}
if checkpoint_dir.exists() {
std::fs::remove_dir_all(&checkpoint_dir)?;
println!("\nCheckpoint directory cleaned up.");
}
Ok(())
}
fn build_ga(total: usize) -> SphereGa {
SimpleGABuilder::real_valued()
.population_size(POP_SIZE)
.bounds(MultiBounds::symmetric(5.12, DIM))
.fitness(Sphere::new(DIM))
.elitism(true)
.elite_count(1)
.max_generations(total)
.build()
.expect("build GA")
}
fn run_straight(generations: usize) -> f64 {
let ga = build_ga(generations);
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
let mut state = ga.init_run(&mut rng).expect("init_run");
while ga.step_generation(&mut state, &mut rng).expect("step") {}
ga.finish_run(state).best_fitness
}
fn run_with_checkpoint(
checkpoint_dir: &Path,
total: usize,
checkpoint_at: usize,
) -> Result<f64, Box<dyn std::error::Error>> {
let ga = build_ga(total);
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
let mut state = ga.init_run(&mut rng)?;
for _ in 0..checkpoint_at {
ga.step_generation(&mut state, &mut rng)?;
}
let checkpoint = ga.checkpoint_run(&state, &rng)?;
let path = checkpoint_dir.join("resume.ckpt");
save_checkpoint(&checkpoint, &path, CheckpointFormat::Binary)?;
println!("Saved checkpoint at generation {checkpoint_at} -> {path:?}");
let checkpoint: Checkpoint<RealVector> = load_checkpoint(&path)?;
let resumed = ga.run_from_checkpoint::<ChaCha8Rng>(&checkpoint)?;
Ok(resumed.best_fitness)
}