feat: add Objective trait and unify optimize API
- Add `Objective<V>` trait with lifecycle hooks (`before_trial`, `after_trial`, `max_retries`) in new `src/objective.rs` - Replace 14+ optimize variants with 6 methods: `optimize`, `optimize_with`, and async/parallel counterparts - `optimize*` methods accept closures directly (FnMut for sync, Fn for async); `optimize_with*` methods accept `impl Objective<V>` for struct-based objectives with hooks and retries - Remove `optimize_until`, `optimize_with_callback`, `optimize_with_retries`, `optimize_with_checkpoint`, and all deprecated `_with_sampler` methods
This commit is contained in:
+33
-18
@@ -1,8 +1,8 @@
|
||||
//! Early stopping — halt an entire study once a target is reached.
|
||||
//!
|
||||
//! Use `optimize_with_callback` to inspect each completed trial and return
|
||||
//! `ControlFlow::Break(())` when the study should stop (e.g. a quality
|
||||
//! threshold is met or a time budget is exhausted).
|
||||
//! Implements the [`Objective`] trait on a custom struct and uses the
|
||||
//! [`after_trial`](Objective::after_trial) hook to return
|
||||
//! `ControlFlow::Break(())` when the best value drops below a threshold.
|
||||
//!
|
||||
//! Run with: `cargo run --example early_stopping`
|
||||
|
||||
@@ -10,26 +10,41 @@ use std::ops::ControlFlow;
|
||||
|
||||
use optimizer::prelude::*;
|
||||
|
||||
/// An objective that minimises `(x - 3)^2` and stops early once the
|
||||
/// value drops below `target`.
|
||||
struct EarlyStopObjective {
|
||||
x: FloatParam,
|
||||
target: f64,
|
||||
}
|
||||
|
||||
impl Objective<f64> for EarlyStopObjective {
|
||||
type Error = Error;
|
||||
|
||||
fn evaluate(&self, trial: &mut Trial) -> Result<f64> {
|
||||
let v = self.x.suggest(trial)?;
|
||||
Ok((v - 3.0).powi(2))
|
||||
}
|
||||
|
||||
fn after_trial(&self, _study: &Study<f64>, trial: &CompletedTrial<f64>) -> ControlFlow<()> {
|
||||
if trial.value < self.target {
|
||||
println!("Target {} reached at trial #{}", self.target, trial.id);
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> optimizer::Result<()> {
|
||||
let study: Study<f64> = Study::new(Direction::Minimize);
|
||||
let x = FloatParam::new(-10.0, 10.0).name("x");
|
||||
|
||||
let target = 0.01;
|
||||
let objective = EarlyStopObjective {
|
||||
x: x.clone(),
|
||||
target: 0.01,
|
||||
};
|
||||
|
||||
study.optimize_with_callback(
|
||||
100, // upper bound — we expect to stop much earlier
|
||||
|trial| {
|
||||
let xv = x.suggest(trial)?;
|
||||
Ok::<_, Error>((xv - 3.0).powi(2))
|
||||
},
|
||||
|_study, completed| {
|
||||
if completed.value < target {
|
||||
println!("Target {target} reached at trial #{}", completed.id);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
)?;
|
||||
study.optimize_with(100, objective)?;
|
||||
|
||||
let best = study.best_trial()?;
|
||||
println!(
|
||||
|
||||
Reference in New Issue
Block a user