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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Read-only side effects fired around the iteration loop.
//!
//! An [`Observe`] implementation watches the run as it happens (logging,
//! progress reporting, recording a trajectory, streaming iterates to a UI)
//! without influencing it. Observers do **not** decide whether to stop; that
//! is the job of [`TerminationCriterion`](crate::core::termination::TerminationCriterion).
//! The two extension points sit side-by-side on
//! [`Executor`](crate::core::executor::Executor):
//!
//! - [`TerminationCriterion`](crate::core::termination::TerminationCriterion):
//! returns `Option<TerminationReason>`; framework consumes the result and
//! stops the run if `Some`.
//! - [`Observe`]: returns `()`; the executor ignores any side effects on the
//! optimization itself. Failures must be handled inside the observer:
//! the trait is infallible by design so a misbehaving logger can't kill
//! the run.
//!
//! Like termination criteria, observers bind on the minimum
//! [`State`](crate::core::state::State) shape they need (tenet 3): a logger
//! that just wants `iter`/`cost` impls `Observe<S: State>`; a gradient-norm
//! observer impls `Observe<S: GradientState>` and is rejected at compile time
//! when attached to a derivative-free run.
//!
//! # Lifecycle
//!
//! Three hooks, called in this order during a run:
//!
//! 1. [`observe_init`](Observe::observe_init) fires once after
//! [`Solver::init`](crate::core::solver::Solver::init) returns and the
//! state's counter mirror is refreshed, before the first termination
//! check. The state shows `iter() == 0`.
//! 2. [`observe_iter`](Observe::observe_iter) fires after every successfully
//! completed iteration, after the iteration counter is incremented. On
//! the first call the state shows `iter() == 1`. Gated by
//! [`ObserverMode`].
//! 3. [`observe_final`](Observe::observe_final) fires once when the run
//! stops cleanly with a [`TerminationReason`]. The state shows the iter
//! count of the last fully-completed iteration.
//!
//! # Edge cases
//!
//! - **Mid-iter termination.** If
//! [`Solver::next_iter`](crate::core::solver::Solver::next_iter) returns
//! `(state, Some(reason))`, the iteration counter is *not* incremented,
//! [`observe_iter`](Observe::observe_iter) does *not* fire for that
//! partial iteration, and [`observe_final`](Observe::observe_final) fires
//! with the mid-iter reason.
//! - **Hard error from the problem.** If `next_iter` returns `Err(_)`, the
//! state is consumed by the failing call and there's nothing to observe;
//! [`observe_final`](Observe::observe_final) does *not* fire and the
//! error propagates out of
//! [`Executor::run`](crate::core::executor::Executor::run) /
//! [`Stepper::step`](crate::core::executor::Stepper::step). Observers
//! that need to react to hard aborts should track liveness themselves.
//! - **Reading the reason from inside observers.** The argument to
//! [`observe_final`](Observe::observe_final) is the
//! [`TerminationReason`]; state types do not carry it.
//!
//! # Starter observers
//!
//! Two zero-dependency observers ship in core, both binding on the minimum
//! [`State`](crate::core::state::State) shape:
//!
//! - [`Report`]: prints a one-line progress report (iter / cost / best) to
//! stderr on each fire.
//! - [`History`]: records the `(iter, cost, best_cost)` trajectory for later
//! plotting or analysis.
//!
//! Heavier integrations (tracing, slog, a TUI) live in satellite crates; a
//! `serde`-gated `CheckpointWriter` (non-wasm) snapshots the state to disk for
//! warm-starting a later run.
use crateTerminationReason;
pub use History;
pub use Report;
pub use ;
/// Hooks fired around the iteration loop. See the
/// [module docs](self) for lifecycle and edge cases.
///
/// All three methods default to no-ops, so an implementor fills in only what
/// they need. Bind on the minimum state shape required (`S: State`,
/// `S: GradientState`, `S: SimplexState`, …) so a mismatch with the solver is
/// a compile error rather than a runtime no-op.
/// Per-registration policy for [`observe_iter`](Observe::observe_iter).
///
/// Every variant gates the iteration callback only: `observe_init` and
/// `observe_final` always fire. A user who wants to fully disable an observer
/// should simply not register it.