Skip to main content

optimization/
types.rs

1use std::borrow::Borrow;
2
3
4/// Defines an objective function `f` that is subject to minimization.
5///
6/// For convenience every function with the same signature as `value()` qualifies as
7/// an objective function, e.g., minimizing a closure is perfectly fine.
8pub trait Function {
9    /// Computes the objective function at a given `position` `x`, i.e., `f(x) = y`.
10    fn value(&self, position: &[f64]) -> f64;
11}
12
13
14/// New-type to support optimization of arbitrary functions without requiring
15/// to implement a trait.
16pub struct Func<F: Fn(&[f64]) -> f64>(pub F);
17
18impl<F: Fn(&[f64]) -> f64> Function for Func<F> {
19    fn value(&self, position: &[f64]) -> f64 {
20        self.0(position)
21    }
22}
23
24
25/// Defines an objective function `f` that is able to compute the first derivative
26/// `f'(x)`.
27pub trait Function1: Function {
28    /// Computes the gradient of the objective function at a given `position` `x`,
29    /// i.e., `∀ᵢ ∂/∂xᵢ f(x) = ∇f(x)`.
30    fn gradient(&self, position: &[f64]) -> Vec<f64>;
31}
32
33
34/// Defines a summation of individual functions, i.e., f(x) = ∑ᵢ fᵢ(x).
35pub trait Summation: Function {
36    /// Returns the number of individual functions that are terms of the summation.
37    fn terms(&self) -> usize;
38
39    /// Comptues the value of one individual function indentified by its index `term`,
40    /// given the `position` `x`.
41    fn term_value(&self, position: &[f64], term: usize) -> f64;
42
43    /// Computes the partial sum over a set of individual functions identified by `terms`.
44    fn partial_value<T: IntoIterator<Item=I>, I: Borrow<usize>>(&self, position: &[f64], terms: T) -> f64 {
45        let mut value = 0.0;
46
47        for term in terms {
48            value += self.term_value(position, *term.borrow());
49        }
50
51        value
52    }
53}
54
55impl<S: Summation> Function for S {
56    fn value(&self, position: &[f64]) -> f64 {
57        self.partial_value(position, 0..self.terms())
58    }
59}
60
61
62/// Defines a summation of individual functions `fᵢ(x)`, assuming that each function has a first
63/// derivative.
64pub trait Summation1: Summation + Function1 {
65    /// Computes the gradient of one individual function identified by `term` at the given
66    /// `position`.
67    fn term_gradient(&self, position: &[f64], term: usize) -> Vec<f64>;
68
69    /// Computes the partial gradient over a set of `terms` at the given `position`.
70    fn partial_gradient<T: IntoIterator<Item=I>, I: Borrow<usize>>(&self, position: &[f64], terms: T) -> Vec<f64> {
71        let mut gradient = vec![0.0; position.len()];
72
73        for term in terms {
74            for (g, gi) in gradient.iter_mut().zip(self.term_gradient(position, *term.borrow())) {
75                *g += gi;
76            }
77        }
78
79        gradient
80    }
81}
82
83impl<S: Summation1> Function1 for S {
84    fn gradient(&self, position: &[f64]) -> Vec<f64> {
85        self.partial_gradient(position, 0..self.terms())
86    }
87}
88
89
90/// Defines an optimizer that is able to minimize a given objective function `F`.
91pub trait Minimizer<F: ?Sized> {
92    /// Type of the solution the `Minimizer` returns.
93    type Solution: Evaluation;
94
95    /// Performs the actual minimization and returns a solution that
96    /// might be better than the initially provided one.
97    fn minimize(&self, function: &F, initial_position: Vec<f64>) -> Self::Solution;
98}
99
100
101/// Captures the essence of a function evaluation.
102pub trait Evaluation {
103    /// Position `x` with the lowest corresponding value `f(x)`.
104    fn position(&self) -> &[f64];
105
106    /// The actual value `f(x)`.
107    fn value(&self) -> f64;
108}
109
110
111/// A solution of a minimization run providing only the minimal information.
112///
113/// Each `Minimizer` might yield different types of solution structs which provide more
114/// information.
115#[derive(Debug, Clone)]
116pub struct Solution {
117    /// Position `x` of the lowest corresponding value `f(x)` that has been found.
118    pub position: Vec<f64>,
119    /// The actual value `f(x)`.
120    pub value: f64
121}
122
123impl Solution {
124    /// Creates a new `Solution` given the `position` as well as the corresponding `value`.
125    pub fn new(position: Vec<f64>, value: f64) -> Solution {
126        Solution {
127            position,
128            value
129        }
130    }
131}
132
133impl Evaluation for Solution {
134    fn position(&self) -> &[f64] {
135        &self.position
136    }
137
138    fn value(&self) -> f64 {
139        self.value
140    }
141}