argmin/core/observers/mod.rs
1// Copyright 2018-2020 argmin developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! # Observers
9//!
10//! Observers are called after an iteration of a solver was performed and enable the user to observe
11//! the current state of the optimization. This can be used for logging or writing the current
12//! parameter vector to disk.
13
14pub mod file;
15pub mod slog_logger;
16#[cfg(feature = "visualizer")]
17pub mod visualizer;
18
19use crate::core::{ArgminKV, ArgminOp, Error, IterState};
20use serde::{Deserialize, Serialize};
21use std::default::Default;
22use std::sync::{Arc, Mutex};
23
24pub use file::*;
25pub use slog_logger::*;
26#[cfg(feature = "visualizer")]
27pub use visualizer::*;
28
29/// Defines the interface every Observer needs to expose
30pub trait Observe<O: ArgminOp> {
31 /// Called once at the beginning of the execution of the solver.
32 ///
33 /// Parameters:
34 ///
35 /// `name`: Name of the solver
36 /// `kv`: Key-Value storage of initial configurations defined by the `Solver`
37 fn observe_init(&self, _name: &str, _kv: &ArgminKV) -> Result<(), Error> {
38 Ok(())
39 }
40
41 /// Called at every iteration of the solver
42 ///
43 /// Parameters
44 ///
45 /// `state`: Current state of the solver. See documentation of `IterState` for details.
46 /// `kv`: Key-Value store of relevant variables defined by the `Solver`
47 fn observe_iter(&mut self, _state: &IterState<O>, _kv: &ArgminKV) -> Result<(), Error> {
48 Ok(())
49 }
50}
51
52/// Container for observers which acts just like a single `Observe`r by implementing `Observe` on
53/// it.
54#[derive(Clone, Default)]
55pub struct Observer<O> {
56 /// Vector of `Observe`rs with the corresponding `ObserverMode`
57 observers: Vec<(Arc<Mutex<dyn Observe<O>>>, ObserverMode)>,
58}
59
60impl<O: ArgminOp> Observer<O> {
61 /// Constructor
62 pub fn new() -> Self {
63 Observer { observers: vec![] }
64 }
65
66 /// Push another `Observe` to the `observer` field
67 pub fn push<OBS: Observe<O> + 'static>(
68 &mut self,
69 observer: OBS,
70 mode: ObserverMode,
71 ) -> &mut Self {
72 self.observers.push((Arc::new(Mutex::new(observer)), mode));
73 self
74 }
75}
76
77/// By implementing `Observe` for `Observer` we basically allow a set of `Observer`s to be used
78/// just like a single `Observe`r.
79impl<O: ArgminOp> Observe<O> for Observer<O> {
80 /// Initial observation
81 /// This is called after the initialization in an `Executor` and gets the name of the solver as
82 /// string and a `ArgminKV` which includes some solver-specific information.
83 fn observe_init(&self, msg: &str, kv: &ArgminKV) -> Result<(), Error> {
84 for l in self.observers.iter() {
85 l.0.lock().unwrap().observe_init(msg, kv)?
86 }
87 Ok(())
88 }
89
90 /// This is called after every iteration and gets the current `state` of the solver as well as
91 /// a `KV` which can include solver-specific information
92 /// This respects the `ObserverMode`: Every `Observe`r is only called as often as specified.
93 fn observe_iter(&mut self, state: &IterState<O>, kv: &ArgminKV) -> Result<(), Error> {
94 use ObserverMode::*;
95 for l in self.observers.iter_mut() {
96 let iter = state.get_iter();
97 let observer = &mut l.0.lock().unwrap();
98 match l.1 {
99 Always => observer.observe_iter(state, kv),
100 Every(i) if iter % i == 0 => observer.observe_iter(state, kv),
101 NewBest if state.is_best() => observer.observe_iter(state, kv),
102 Never | Every(_) | NewBest => Ok(()),
103 }?
104 }
105 Ok(())
106 }
107}
108
109/// This is used to indicate how often the observer will observe the status. `Never` deactivates
110/// the observer, `Always` and `Every(i)` will call the observer in every or every ith iteration,
111/// respectively. `NewBest` will call the observer only, if a new best solution is found.
112#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd)]
113pub enum ObserverMode {
114 /// Never call the observer
115 Never,
116 /// Call observer in every iteration
117 Always,
118 /// Call observer every N iterations
119 Every(u64),
120 /// Call observer when new best is found
121 NewBest,
122}
123
124impl Default for ObserverMode {
125 /// The default is `Always`
126 fn default() -> ObserverMode {
127 ObserverMode::Always
128 }
129}
130
131// #[cfg(test)]
132// mod tests {
133// use super::*;
134// use crate::core::MinimalNoOperator;
135//
136// send_sync_test!(observer, Observer<MinimalNoOperator>);
137// send_sync_test!(observermode, ObserverMode);
138// }