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}