1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! An observer that records the cost trajectory of a run.
use crateObserve;
use crateState;
/// Record `(iter, cost, best_cost)` at init and on every observed iteration.
///
/// [`OptimizationResult`](crate::core::executor::OptimizationResult) keeps only
/// the *final* iterate and the best-so-far; `History` fills the gap when you
/// want the whole trajectory for plotting or convergence analysis. Read it back
/// via [`records`](Self::records) after the run.
///
/// The scalar type defaults to `f64` (the crate-wide default), but the observer
/// is generic over any [`State::Float`]. Bind cadence with an
/// [`ObserverMode`](super::ObserverMode); `Always` captures every iteration,
/// `Every(n)` thins it.
///
/// Because the [`Executor`](crate::core::executor::Executor) takes ownership of
/// registered observers, wrap `History` in `Rc<RefCell<_>>` (or read it off the
/// stepper) if you need to reach the records after `run()`:
///
/// ```
/// # use basin::{BasicState, CostFunction, Executor, Gradient, GradientDescent};
/// use std::cell::RefCell;
/// use std::rc::Rc;
/// use basin::{History, Observe, ObserverMode, State};
/// # 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, Self::Error> {
/// # 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>, Self::Error> { Ok(x.clone()) }
/// # }
/// // A thin shared-handle wrapper so the records outlive the executor.
/// #[derive(Clone, Default)]
/// struct Shared(Rc<RefCell<History>>);
/// impl<S: State<Float = f64>> Observe<S> for Shared {
/// fn observe_init(&mut self, s: &S) { self.0.borrow_mut().observe_init(s); }
/// fn observe_iter(&mut self, s: &S) { self.0.borrow_mut().observe_iter(s); }
/// }
///
/// let history = Shared::default();
/// Executor::new(Quadratic, GradientDescent::new(0.1), BasicState::new(vec![1.0, 1.0]))
/// .max_iter(5)
/// .observe_with(history.clone(), ObserverMode::Always)
/// .run()
/// .unwrap();
/// assert_eq!(history.0.borrow().records().len(), 1 + 5); // init + 5 iters
/// ```