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
181
182
183
184
//! A `serde`-gated observer that snapshots the state to disk.
//!
//! Available only with the `serde` feature and off `wasm32` (it does file
//! I/O). The companion [`read_checkpoint`] reloads a state snapshot to warm
//! start a later run. Exact continuation instead uses the solver-aware
//! [`ExactCheckpointWriter`](crate::ExactCheckpointWriter).
use fs;
use io;
use ;
use Serialize;
use DeserializeOwned;
use crateObserve;
use crateState;
use crateTerminationReport;
const MAGIC: & = b"BASINST\0";
const FORMAT_VERSION: u32 = 1;
/// Write the current state to a file with [`postcard`], overwriting the
/// previous snapshot, so the file always holds the latest checkpoint.
///
/// Files start with `BASINST\0` and a little-endian `u32` format version of 1,
/// followed by the postcard payload. Basin 2.0 reads only this format. See
/// [`read_checkpoint`] for migration from legacy 1.x files.
///
/// State-only checkpointing is an observer's job in Basin. It records an
/// iterate for a later warm start without promising an identical trajectory.
/// Register it with an [`ObserverMode`](super::ObserverMode) to pick the
/// cadence ([`Every(n)`](super::ObserverMode::Every) for every `n`th iteration,
/// [`NewBest`](super::ObserverMode::NewBest) to snapshot only on improvement).
/// The writer always snapshots on `observe_final` as well.
///
/// The state type must be [`Serialize`]. Shared progress types such as
/// [`PointState`](crate::PointState), [`FirstOrderState`](crate::FirstOrderState),
/// and [`ProposalState`](crate::ProposalState) support serialization when their
/// parameter and scalar types do. Solver-owned models, neighbors, and RNGs are
/// absent from these snapshots; use [`ExactCheckpointWriter`](crate::ExactCheckpointWriter)
/// to retain them.
/// Handing a non-serializable state is a compile error.
///
/// # Resume
///
/// Read the file with [`read_checkpoint`], deserialize into the concrete state,
/// and hand it to [`Executor::new`](crate::Executor::new). The solver runs its
/// normal `init` path and begins a new run from the restored iterate.
///
/// ```no_run
/// # use basin::{FirstOrderState, CostFunction, Executor, Gradient, GradientDescent};
/// use basin::{CheckpointWriter, ObserverMode, read_checkpoint};
/// # struct Quadratic;
/// # impl basin::ProblemConstraints for Quadratic {
/// type Constraints = basin::NoConstraints;
/// fn constraint_set(&self) -> basin::NoConstraints { basin::NoConstraints::EMPTY }
/// }
/// 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()) }
/// # }
/// // First run: checkpoint every 10 iterations.
/// Executor::new(Quadratic, GradientDescent::new(0.1), FirstOrderState::new(vec![5.0, 5.0]))
/// .max_iter(50)
/// .observe_with(CheckpointWriter::new("run.ckpt"), ObserverMode::Every(10))
/// .run()
/// .unwrap();
///
/// // Later: reload and continue from where it stopped.
/// let state: FirstOrderState<Vec<f64>> = read_checkpoint("run.ckpt").unwrap();
/// Executor::new(Quadratic, GradientDescent::new(0.1), state)
/// .max_iter(50)
/// .run()
/// .unwrap();
/// ```
/// Load a checkpoint previously written by [`CheckpointWriter`] into a concrete
/// state, ready for [`Executor::new`](crate::Executor::new) as a warm start.
///
/// Reads postcard state format version 1 without modifying the file. Missing
/// format markers, unsupported versions, malformed data, and trailing bytes
/// return [`io::ErrorKind::InvalidData`].
///
/// # Migrating from Basin 1.x
///
/// Basin 2.0 no longer reads unprefixed bincode files. Before upgrading, load
/// them with a compatible 1.x release and rewrite them using
/// [`CheckpointWriter`] from Basin 1.15 or later in the 1.x series, which writes
/// postcard. The concrete state's serialized layout must still match. If it
/// changes, export parameters from the 1.x application and construct a fresh
/// state in 2.0. State snapshots do not promise exact continuation.