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

/// TODO DOCUMENTATION
///
use errors::*;
use std::fmt::Debug;
use std::default::Default;
use rand;
use rand::distributions::{IndependentSample, Range};
use ndarray::Array1;

/// This trait needs to be implemented for every parameter fed into the solvers.
/// This is highly *UNSTABLE* and will change in the future.
pub trait ArgminParameter: Clone + Default + Debug {
    /// Defines a modification of the parameter vector.
    ///
    /// The parameters:
    ///
    /// `&self`: reference to the object of type `Self`
    /// `lower_bound`: Lower bound of the parameter vector. Same type as parameter vector (`Self`)
    /// `upper_bound`: Upper bound of the parameter vector. Same type as parameter vector (`Self`)
    /// `constraint`: Additional (non)linear constraint whith the signature `&Fn(&Self) -> bool`.
    /// The provided function takes a parameter as input and returns `true` if the parameter vector
    /// satisfies the constraints and `false` otherwise.
    fn modify(&self, &Self, &Self, &Fn(&Self) -> bool) -> Self;

    /// Returns a completely random parameter vector
    ///
    /// The resulting parameter vector satisfies `lower_bound`, `upper_bound`.
    fn random(&Self, &Self) -> Result<Self>;
}

impl ArgminParameter for Vec<f64> {
    fn modify(
        &self,
        lower_bound: &Vec<f64>,
        upper_bound: &Vec<f64>,
        constraint: &Fn(&Vec<f64>) -> bool,
    ) -> Vec<f64> {
        let step = Range::new(0, self.len());
        let range = Range::new(-1.0_f64, 1.0_f64);
        let mut rng = rand::thread_rng();
        let mut param = self.clone();
        loop {
            let idx = step.ind_sample(&mut rng);
            param[idx] = self[idx] + range.ind_sample(&mut rng);
            if param[idx] < lower_bound[idx] {
                param[idx] = lower_bound[idx];
            }
            if param[idx] > upper_bound[idx] {
                param[idx] = upper_bound[idx];
            }
            if constraint(&param) {
                break;
            }
        }
        param
    }

    fn random(lower_bound: &Vec<f64>, upper_bound: &Vec<f64>) -> Result<Vec<f64>> {
        let mut out = vec![];
        let mut rng = rand::thread_rng();
        for elem in lower_bound.iter().zip(upper_bound.iter()) {
            if elem.0 >= elem.1 {
                return Err(ErrorKind::InvalidParameter(
                    "Parameter: lower_bound must be lower than upper_bound.".into(),
                ).into());
            }
            let range = Range::new(*elem.0, *elem.1);
            out.push(range.ind_sample(&mut rng));
        }
        Ok(out)
    }
}

impl ArgminParameter for Array1<f64> {
    fn modify(
        &self,
        lower_bound: &Array1<f64>,
        upper_bound: &Array1<f64>,
        constraint: &Fn(&Array1<f64>) -> bool,
    ) -> Array1<f64> {
        let step = Range::new(0, self.len());
        let range = Range::new(-1.0_f64, 1.0_f64);
        let mut rng = rand::thread_rng();
        let mut param = self.clone();
        loop {
            let idx = step.ind_sample(&mut rng);
            param[idx] = self[idx] + range.ind_sample(&mut rng);
            if param[idx] < lower_bound[idx] {
                param[idx] = lower_bound[idx];
            }
            if param[idx] > upper_bound[idx] {
                param[idx] = upper_bound[idx];
            }
            if constraint(&param) {
                break;
            }
        }
        param
    }

    fn random(lower_bound: &Array1<f64>, upper_bound: &Array1<f64>) -> Result<Array1<f64>> {
        // unimplemented!()
        let mut rng = rand::thread_rng();
        let out: Array1<f64> = lower_bound
            .iter()
            .zip(upper_bound.iter())
            .map(|(a, b)| {
                if a >= b {
                    panic!("Parameter: lower_bound must be lower than upper_bound.");
                    // Unfortunately the following doesnt work here!
                    // return Err(ErrorKind::InvalidParameter(
                    //     "Parameter: lower_bound must be lower than upper_bound.".into(),
                    // ).into());
                }
                let range = Range::new(*a, *b);
                range.ind_sample(&mut rng)
            })
            .collect();
        Ok(out)
    }
}