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
//! 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.
use crateTerminationReason;
/// 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).
///
/// `Never` and `Every` gate 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.