1use num_traits::float::FloatCore;
87use std::sync::{Arc, Mutex};
88use tokio_util::sync::CancellationToken;
89
90use crate::engine::checkpoint::CheckpointError;
91use crate::engine::policy::{CancellationPolicy, CompletionPolicy, EnginePolicy, PolicyStack};
92use crate::state::{ConvergenceState, RuntimeState};
93use crate::{
94 engine::{
95 checkpoint::{CheckpointBackend, CheckpointExtension},
96 extensions::Extensions,
97 Engine,
98 },
99 state::{Snapshotable, State, StateRestorer},
100 watchers::{Frequency, Observe, Observers},
101 FallibleProcedure, Infallible, Procedure, UserState,
102};
103
104pub trait GenerateBuilderFallible: Sized {
105 fn build_for<P>(self, problem: P) -> Builder<Self, P, Uninitialised>
106 where
107 Self: FallibleProcedure<P>,
108 Self::State: UserState;
109}
110
111impl<Proc> GenerateBuilderFallible for Proc {
112 fn build_for<P>(self, problem: P) -> Builder<Self, P, Uninitialised>
113 where
114 Proc: FallibleProcedure<P>,
115 Proc::State: UserState,
116 {
117 Builder {
118 procedure: self,
119 problem,
120 state: None,
121 runtime: None,
122 convergence: None,
123 time: true,
124 cancellation_token: None,
125
126 observers: Observers::new(),
127
128 policies: PolicyStack::new()
129 .add(CancellationPolicy)
130 .add(CompletionPolicy),
131
132 extensions: Extensions::new(),
133
134 _initialised: std::marker::PhantomData,
135 }
136 }
137}
138
139pub trait GenerateBuilder: Sized {
140 fn build_for<P>(self, problem: P) -> Builder<Infallible<Self>, P, Uninitialised>
141 where
142 Self: Procedure<P>,
143 Self::State: UserState;
144}
145
146impl<Proc> GenerateBuilder for Proc {
147 fn build_for<P>(self, problem: P) -> Builder<Infallible<Self>, P, Uninitialised>
148 where
149 Proc: Procedure<P>,
150 Proc::State: UserState,
151 {
152 Builder {
153 procedure: Infallible(self),
154 problem,
155 state: None,
156 runtime: None,
157 convergence: None,
158 time: true,
159 cancellation_token: None,
160
161 observers: Observers::new(),
162
163 policies: PolicyStack::new()
164 .add(CancellationPolicy)
165 .add(CompletionPolicy),
166
167 extensions: Extensions::new(),
168
169 _initialised: std::marker::PhantomData,
170 }
171 }
172}
173
174pub struct Uninitialised;
175pub struct Initialised;
176
177pub struct Builder<Proc, P, I>
178where
179 Proc: FallibleProcedure<P>,
180 Proc::State: UserState,
181 <Proc::State as UserState>::Float: FloatCore,
182{
183 procedure: Proc,
184 problem: P,
185 state: Option<Proc::State>,
186 runtime: Option<RuntimeState>,
187 convergence: Option<ConvergenceState<<Proc::State as UserState>::Float>>,
188 time: bool,
189 cancellation_token: Option<CancellationToken>,
190
191 observers: Observers<Proc::State>,
192
193 policies: PolicyStack<<Proc::State as UserState>::Float>,
194 extensions: Extensions<Proc::State>,
195
196 _initialised: std::marker::PhantomData<I>,
197}
198
199impl<Proc, P, I> Builder<Proc, P, I>
200where
201 Proc: FallibleProcedure<P>,
202 Proc::State: UserState,
203 <Proc::State as UserState>::Float: FloatCore + 'static,
204{
205 #[must_use]
206 pub fn time(mut self, time: bool) -> Self {
207 self.time = time;
208 self
209 }
210
211 #[must_use]
215 pub(crate) fn attach_observer<OBS>(mut self, observer: OBS, frequency: Frequency) -> Self
216 where
217 OBS: Observe<Proc::State> + 'static,
218 {
219 self.observers
220 .attach(Arc::new(Mutex::new(observer)), frequency);
221 self
222 }
223
224 #[must_use]
225 pub fn and_policy<Q>(mut self, policy: Q) -> Self
226 where
227 Q: EnginePolicy<<Proc::State as UserState>::Float> + 'static,
228 {
229 self.policies = self.policies.add(policy);
230 self
231 }
232
233 #[must_use]
234 pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
235 self.cancellation_token = Some(token);
236 self
237 }
238
239 #[must_use]
240 pub fn with_default_policies(
244 mut self,
245 max_iter: usize,
246 absolute_tolerance: <Proc::State as UserState>::Float,
247 window_size: usize,
248 ) -> Self {
249 self.policies = self.policies.merge(PolicyStack::standard(
250 max_iter,
251 absolute_tolerance,
252 window_size,
253 ));
254 self
255 }
256
257 #[must_use]
258 pub fn with_checkpoint_backend<C>(mut self, store: C) -> Self
264 where
265 C: CheckpointBackend<
266 <Proc::State as Snapshotable>::Snapshot,
267 <Proc::State as UserState>::Float,
268 > + 'static,
269 Proc::State: Snapshotable,
270 {
271 self.extensions = self.extensions.add(CheckpointExtension::new(store));
272 self
273 }
274}
275
276impl<Proc, P> Builder<Proc, P, Uninitialised>
277where
278 Proc: FallibleProcedure<P>,
279 Proc::State: UserState,
280 <Proc::State as UserState>::Float: FloatCore + 'static,
281{
282 #[must_use]
284 pub fn with_initial_state(self, user: Proc::State) -> Builder<Proc, P, Initialised> {
285 Builder {
286 procedure: self.procedure,
287 problem: self.problem,
288 state: Some(user),
289 runtime: self.runtime,
290 convergence: self.convergence,
291 time: self.time,
292 cancellation_token: self.cancellation_token,
293
294 observers: self.observers,
295
296 policies: self.policies,
297
298 extensions: self.extensions,
299
300 _initialised: std::marker::PhantomData,
301 }
302 }
303
304 #[must_use]
305 pub fn resume_from_snapshot(
306 self,
307 snapshot: <Proc::State as Snapshotable>::Snapshot,
308 ) -> Builder<Proc, P, Initialised>
309 where
310 Proc: FallibleProcedure<P>,
311 Proc::State: Snapshotable + StateRestorer<Proc::State>,
312 {
313 let user = Proc::State::restore(snapshot);
314
315 Builder {
316 procedure: self.procedure,
317 problem: self.problem,
318 state: Some(user),
319 runtime: self.runtime,
320 convergence: self.convergence,
321 time: self.time,
322 cancellation_token: self.cancellation_token,
323
324 observers: self.observers,
325
326 policies: self.policies,
327
328 extensions: self.extensions,
329
330 _initialised: std::marker::PhantomData,
331 }
332 }
333
334 pub fn resume_from_checkpoint<C>(
335 self,
336 store: C,
337 ) -> Result<Builder<Proc, P, Initialised>, CheckpointError>
338 where
339 C: CheckpointBackend<
340 <Proc::State as Snapshotable>::Snapshot,
341 <Proc::State as UserState>::Float,
342 > + 'static,
343 Proc: FallibleProcedure<P>,
344 Proc::State: Snapshotable + StateRestorer<Proc::State>,
345 {
346 if let Some(state) = store.load()? {
347 return Ok(Builder {
348 procedure: self.procedure,
349 problem: self.problem,
350 state: Some(Proc::State::restore(state.user)),
351 runtime: Some(state.runtime),
352 convergence: Some(state.convergence),
353 time: self.time,
354 cancellation_token: self.cancellation_token,
355
356 observers: self.observers,
357
358 policies: self.policies,
359
360 extensions: self.extensions,
361
362 _initialised: std::marker::PhantomData,
363 });
364 }
365 Err(CheckpointError::NoCheckpoint)
366 }
367}
368
369impl<Proc, P> Builder<Proc, P, Initialised>
370where
371 Proc: FallibleProcedure<P>,
372 Proc::State: UserState,
373 <Proc::State as UserState>::Float: FloatCore + 'static,
374{
375 pub fn finalise(mut self) -> Engine<Proc, P, PolicyStack<<Proc::State as UserState>::Float>>
380 where
381 <Proc::State as UserState>::Float: num_traits::FromPrimitive,
382 {
383 let user = self.state.take().expect("builder invariant: user is set");
384
385 let cancellation = self.cancellation_token.unwrap_or_default();
386
387 #[cfg(all(feature = "ctrlc", not(test)))]
388 {
389 let token = cancellation.clone();
390 ctrlc::set_handler(move || {
391 token.cancel();
392 })
393 .unwrap();
394 }
395
396 Engine {
397 procedure: self.procedure,
398 problem: self.problem,
399 state: {
400 match (self.convergence, self.runtime) {
401 (Some(convergence), Some(runtime)) => {
402 State::from_parts(user, runtime, convergence)
403 }
404 _ => State::new(user),
405 }
406 },
407
408 time: self.time,
409 start_time: None,
410
411 cancellation,
412
413 policy: self.policies,
414
415 observers: self.observers,
416 extensions: self.extensions,
417 }
418 }
419
420 pub fn finalise_with(
428 mut self,
429 policy: PolicyStack<<Proc::State as UserState>::Float>,
430 ) -> Engine<Proc, P, PolicyStack<<Proc::State as UserState>::Float>> {
431 let user = self.state.take().expect("builder invariant: user is set");
432 let cancellation = self.cancellation_token.unwrap_or_default();
433
434 #[cfg(all(feature = "ctrlc", not(test)))]
435 {
436 let token = cancellation.clone();
437 ctrlc::set_handler(move || {
438 token.cancel();
439 })
440 .unwrap();
441 }
442
443 Engine {
444 procedure: self.procedure,
445 problem: self.problem,
446 state: {
447 match (self.convergence, self.runtime) {
448 (Some(convergence), Some(runtime)) => {
449 State::from_parts(user, runtime, convergence)
450 }
451 _ => State::new(user),
452 }
453 },
454
455 time: self.time,
456 start_time: None,
457
458 cancellation,
459
460 policy,
461
462 observers: self.observers,
463 extensions: self.extensions,
464 }
465 }
466}
467