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// }