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

    /// Returns true if `observers` is empty
    pub fn is_empty(&self) -> bool {
        self.observers.is_empty()
    }
}

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