argmin/core/observers/mod.rs
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
// Copyright 2018-2020 argmin developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! # Observers
//!
//! Observers are called after an iteration of a solver was performed and enable the user to observe
//! the current state of the optimization. This can be used for logging or writing the current
//! parameter vector to disk.
pub mod file;
pub mod slog_logger;
#[cfg(feature = "visualizer")]
pub mod visualizer;
use crate::core::{ArgminKV, ArgminOp, Error, IterState};
use serde::{Deserialize, Serialize};
use std::default::Default;
use std::sync::{Arc, Mutex};
pub use file::*;
pub use slog_logger::*;
#[cfg(feature = "visualizer")]
pub use visualizer::*;
/// Defines the interface every Observer needs to expose
pub trait Observe<O: ArgminOp> {
/// Called once at the beginning of the execution of the solver.
///
/// Parameters:
///
/// `name`: Name of the solver
/// `kv`: Key-Value storage of initial configurations defined by the `Solver`
fn observe_init(&self, _name: &str, _kv: &ArgminKV) -> Result<(), Error> {
Ok(())
}
/// Called at every iteration of the solver
///
/// Parameters
///
/// `state`: Current state of the solver. See documentation of `IterState` for details.
/// `kv`: Key-Value store of relevant variables defined by the `Solver`
fn observe_iter(&mut self, _state: &IterState<O>, _kv: &ArgminKV) -> Result<(), Error> {
Ok(())
}
}
/// Container for observers which acts just like a single `Observe`r by implementing `Observe` on
/// it.
#[derive(Clone, Default)]
pub struct Observer<O> {
/// Vector of `Observe`rs with the corresponding `ObserverMode`
observers: Vec<(Arc<Mutex<dyn Observe<O>>>, ObserverMode)>,
}
impl<O: ArgminOp> Observer<O> {
/// Constructor
pub fn new() -> Self {
Observer { observers: vec![] }
}
/// Push another `Observe` to the `observer` field
pub fn push<OBS: Observe<O> + 'static>(
&mut self,
observer: OBS,
mode: ObserverMode,
) -> &mut Self {
self.observers.push((Arc::new(Mutex::new(observer)), mode));
self
}
}
/// By implementing `Observe` for `Observer` we basically allow a set of `Observer`s to be used
/// just like a single `Observe`r.
impl<O: ArgminOp> Observe<O> for Observer<O> {
/// Initial observation
/// This is called after the initialization in an `Executor` and gets the name of the solver as
/// string and a `ArgminKV` which includes some solver-specific information.
fn observe_init(&self, msg: &str, kv: &ArgminKV) -> Result<(), Error> {
for l in self.observers.iter() {
l.0.lock().unwrap().observe_init(msg, kv)?
}
Ok(())
}
/// This is called after every iteration and gets the current `state` of the solver as well as
/// a `KV` which can include solver-specific information
/// This respects the `ObserverMode`: Every `Observe`r is only called as often as specified.
fn observe_iter(&mut self, state: &IterState<O>, kv: &ArgminKV) -> Result<(), Error> {
use ObserverMode::*;
for l in self.observers.iter_mut() {
let iter = state.get_iter();
let observer = &mut l.0.lock().unwrap();
match l.1 {
Always => observer.observe_iter(state, kv),
Every(i) if iter % i == 0 => observer.observe_iter(state, kv),
NewBest if state.is_best() => observer.observe_iter(state, kv),
Never | Every(_) | NewBest => Ok(()),
}?
}
Ok(())
}
}
/// This is used to indicate how often the observer will observe the status. `Never` deactivates
/// the observer, `Always` and `Every(i)` will call the observer in every or every ith iteration,
/// respectively. `NewBest` will call the observer only, if a new best solution is found.
#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ObserverMode {
/// Never call the observer
Never,
/// Call observer in every iteration
Always,
/// Call observer every N iterations
Every(u64),
/// Call observer when new best is found
NewBest,
}
impl Default for ObserverMode {
/// The default is `Always`
fn default() -> ObserverMode {
ObserverMode::Always
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::core::MinimalNoOperator;
//
// send_sync_test!(observer, Observer<MinimalNoOperator>);
// send_sync_test!(observermode, ObserverMode);
// }