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
144
145
146
147
148
149
150
151
152
153
154
155
156
use std::fmt;

/// Iterative definition
///
/// One might want to limit the update steps, by either:
/// - limiting the range of values to avoid non-sense values
/// - limiting the size of an update step
///
/// Two implementations of this trait are provided:
/// - `IterativeParams`
/// - `IterativeParamsFD`
pub trait Iterative {
    /// Compute the new value based on the current value and the step size proposed
    ///
    /// The iteratives variables implement a way to reduce this step according to the parametrization
    fn step_limitation(&self, value_current: f64, raw_step: f64) -> f64;
    /// Compute the perturbation (only valid if it is working with finite differences)
    ///
    /// according to the parametrization
    fn compute_perturbation(&self, #[allow(unused_variables)] x: f64) -> f64 {
        unimplemented!();
    }
    /// Method to differente without panicking if it is working with finite differences
    fn with_finite_diff(&self) -> bool {
        false
    }
}

/// A slice of iteratives
///
/// This struct is used as a wrapper to act on a slice of several iteratives
///
/// It provides the same method as the `Iterative` trait with the plural suffix:
/// - `step_limitations`
/// - `compute_perturbations`
pub struct Iteratives<'a, T: Iterative> {
    iteratives_params: &'a [T],
}

impl<'a, T> Iteratives<'a, T>
where
    T: Iterative,
{
    pub fn new(iteratives_params: &'a [T]) -> Self {
        Iteratives { iteratives_params }
    }

    pub fn len(&self) -> usize {
        self.iteratives_params.len()
    }
    /// Compute a limited step for several iteratives
    ///
    /// Return the new value after the application of the step limitation (and not the step).
    ///
    /// This is required as it can be limited by an interval for the iteratives.
    pub fn step_limitations<D>(
        &self,
        values: &nalgebra::OVector<f64, D>,
        raw_step: &nalgebra::OVector<f64, D>,
    ) -> nalgebra::OVector<f64, D>
    where
        D: nalgebra::Dim,
        nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator<f64, D>,
    {
        let mut step_lim: nalgebra::OVector<f64, D> = super::super::ovector_zeros_like(values);

        for (i, iterative_params) in (self.iteratives_params).iter().enumerate() {
            step_lim[i] = iterative_params.step_limitation(values[i], raw_step[i]);
        }
        step_lim
    }

    /// Compute the perturbation for several iteratives
    pub fn compute_perturbations<D>(
        &self,
        iterative_values: &nalgebra::OVector<f64, D>,
    ) -> nalgebra::OVector<f64, D>
    where
        D: nalgebra::Dim,
        nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator<f64, D>,
    {
        let mut perturbations: nalgebra::OVector<f64, D> =
            super::super::ovector_zeros_like(iterative_values);

        for (i, iterative_var) in (self.iteratives_params).iter().enumerate() {
            perturbations[i] = iterative_var.compute_perturbation(iterative_values[i]);
        }
        perturbations
    }
}

impl<'a, T> fmt::Display for Iteratives<'a, T>
where
    T: Iterative + fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let finite_diff = self.iteratives_params[0].with_finite_diff();

        let mut content = String::from("Iteratives parameters\n");
        content.push_str("=====================\n\n");

        let column_float = String::from("--------------+");

        let separation_line = if finite_diff {
            "+-----------+".to_owned()
                + &column_float
                + &column_float
                + &column_float
                + &column_float
                + "-----------------+"
                + &column_float
                + &column_float
                + "\n"
        } else {
            "+-----------+".to_owned()
                + &column_float
                + &column_float
                + &column_float
                + &column_float
                + "\n"
        };

        content.push_str(&separation_line);
        let width = column_float.len() - 2;
        content.push_str("| Iterative ");
        content.push_str(&format!("| {:width$}", &"max_step_abs", width = width));
        content.push_str(&format!("| {:width$}", &"max_step_rel", width = width));
        content.push_str(&format!("| {:width$}", &"min_value", width = width));
        content.push_str(&format!("| {:width$}", &"max_value", width = width));

        if finite_diff {
            content.push_str(&format!(
                "| {:width$}",
                &"perturbation",
                width = "-----------------+".len() - 2
            ));
            content.push_str(&format!("| {:width$}", &"dx_abs", width = width));
            content.push_str(&format!("| {:width$}|", &"dx_rel", width = width));
        } else {
            content.push('|');
        }

        content.push('\n');
        content.push_str(&separation_line);

        for (i, elt) in self.iteratives_params.iter().enumerate() {
            content.push_str(&format!("| {:width$}|", &i.to_string(), width = 10));
            content.push_str(&elt.to_string());
            content.push('\n');
            content.push_str(&separation_line);
        }

        content.push('\n');
        write!(f, "{}", content)
    }
}