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
// Copyright 2018 Stefan Kroboth
//
// 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;

use crate::{ArgminKV, ArgminOp, Error, IterState};
use serde::{Deserialize, Serialize};
use std::default::Default;
use std::sync::Arc;

pub use file::*;
pub use slog_logger::*;

/// Defines the interface every Observer needs to expose
pub trait Observe<O: ArgminOp>: Send + Sync {
    /// 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(&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<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(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.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(&self, state: &IterState<O>, kv: &ArgminKV) -> Result<(), Error> {
        use ObserverMode::*;
        for l in self.observers.iter() {
            let iter = state.get_iter();
            match l.1 {
                Always => l.0.observe_iter(state, kv),
                Every(i) if iter % i == 0 => l.0.observe_iter(state, kv),
                NewBest if state.is_best() => l.0.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,
    Always,
    Every(u64),
    NewBest,
}

impl Default for ObserverMode {
    /// The default is `Always`
    fn default() -> ObserverMode {
        ObserverMode::Always
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::MinimalNoOperator;

    send_sync_test!(observer, Observer<MinimalNoOperator>);
    send_sync_test!(observermode, ObserverMode);
}