feat: v0.6.0 — observer / stop-conditions / tracing / IGD / R2
Theme: production lifecycle. heuropt becomes deployable for long- running, real-world workloads. No breaking changes — Optimizer trait gains a default-impl run_with method that falls back to run. Adds: - src/observer/ module: Snapshot, Observer trait, ControlFlow, plus built-in MaxTime / MaxIterations / TargetFitness / Stagnation / Periodic / AnyOf / AllOf and a closure impl. - Optimizer::run_with(problem, observer): default-impl on the trait, overridden for full per-gen visibility on Nsga2, RandomSearch, and DifferentialEvolution. Other algorithms inherit a final-only notification — full per-gen support follows incrementally. - New 'tracing' optional feature plus TracingObserver that emits structured debug! events per generation. - src/metrics/igd.rs: IGD + IGD+ performance indicators against a reference set. - src/metrics/r2.rs: R2 indicator using the weighted Tchebycheff utility; pair with das_dennis for the canonical weight set. - examples/constrained.rs: BNH constrained 2-objective problem solved with NSGA-II + observer composition (MaxTime.or(Periodic)). Bumps Cargo.toml to 0.6.0; CHANGELOG entry consolidates the above. Existing 247 unit + 38 doctest + 32 algorithm-property + property / metric / numerical-stability tests all pass; bit-identical compare output verified post-DE refactor.
This commit is contained in:
@@ -88,28 +88,49 @@ where
|
||||
I: Initializer<P::Decision>,
|
||||
{
|
||||
fn run(&mut self, problem: &P) -> OptimizationResult<P::Decision> {
|
||||
self.run_with(problem, &mut ())
|
||||
}
|
||||
|
||||
fn run_with<O>(&mut self, problem: &P, observer: &mut O) -> OptimizationResult<P::Decision>
|
||||
where
|
||||
O: crate::observer::Observer<P::Decision>,
|
||||
{
|
||||
use crate::observer::Snapshot;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
let objectives = problem.objectives();
|
||||
let mut rng = rng_from_seed(self.config.seed);
|
||||
let mut all: Vec<Candidate<P::Decision>> = Vec::new();
|
||||
let mut evaluations = 0usize;
|
||||
let started = std::time::Instant::now();
|
||||
let mut completed: usize = 0;
|
||||
|
||||
for _ in 0..self.config.iterations {
|
||||
for iteration in 1..=self.config.iterations {
|
||||
let decisions = self
|
||||
.initializer
|
||||
.initialize(self.config.batch_size, &mut rng);
|
||||
evaluations += decisions.len();
|
||||
all.extend(evaluate_batch(problem, decisions));
|
||||
completed = iteration;
|
||||
|
||||
let best = best_candidate(&all, &objectives);
|
||||
let snap = Snapshot {
|
||||
iteration,
|
||||
evaluations,
|
||||
elapsed: started.elapsed(),
|
||||
population: &all,
|
||||
pareto_front: None,
|
||||
best: best.as_ref(),
|
||||
objectives: &objectives,
|
||||
};
|
||||
if let ControlFlow::Break(()) = observer.observe(&snap) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let front = pareto_front(&all, &objectives);
|
||||
let best = best_candidate(&all, &objectives);
|
||||
OptimizationResult::new(
|
||||
Population::new(all),
|
||||
front,
|
||||
best,
|
||||
evaluations,
|
||||
self.config.iterations,
|
||||
)
|
||||
OptimizationResult::new(Population::new(all), front, best, evaluations, completed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user