benchmark_functions/
lib.rs

1//! The `benchmark_functions` crate provides functionality for several functions that are commonly
2//! used to benchmark new optimization algorithms. More specifically, function is part of a struct
3//! that contains the objective function as well as other important information (bounds of the
4//! canonical problem, the known minimum value, and a function that returns the global minimizer.
5
6/// This is a trait that ensures consistent implementation of different benchmark functions
7pub trait Function {
8    /// The bounds of the canonical optimization problem
9    const BOUNDS: (f64, f64);
10
11    /// The global minimum is constant and zero
12    const MINIMUM: f64;
13
14    /// Function for evaluating the objective function
15    fn f(x: Vec<f64>) -> f64;
16
17    /// This function returns the minimizer (argument that will return the global minimum)
18    fn minimizer(n: usize) -> Vec<f64>;
19}
20
21
22/// This is a constant used for low-dimensional testing
23const LOW_D: usize = 2;
24const HIGH_D: usize = 137;
25
26/// This is the Sphere function.
27///
28/// The function is borrowed from [here](https://en.wikipedia.org/wiki/Test_functions_for_optimization).
29/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
30/// like in 2D:
31///
32/// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Sphere_function_in_3D.pdf/page1-800px-Sphere_function_in_3D.pdf.jpg)
33pub struct Sphere {}
34
35impl Function for Sphere {
36    /// The bounds of the canonical sphere optimization problem are infinite.
37    const BOUNDS: (f64, f64) = (-f64::INFINITY, f64::INFINITY);
38
39    /// The global minimum is constant and zero
40    const MINIMUM: f64 = 0.0;
41
42    /// Function for evaluating
43    fn f(x: Vec<f64>) -> f64 {
44        let mut f = 0f64;
45        for i in 0..x.len() {
46            f -= x[i] * x[i];
47        }
48        f
49    }
50
51    /// This function returns the minimizer (argument that will return the global minimum
52    fn minimizer(n: usize) -> Vec<f64> {
53        vec![0.0; n]
54    }
55}
56
57#[cfg(test)]
58mod sphere_tests {
59    use super::*;
60
61    #[test]
62    fn low_d() {
63        assert_eq!(Sphere::f(Sphere::minimizer(LOW_D)), Sphere::MINIMUM)
64    }
65
66    #[test]
67    fn high_d() {
68        assert_eq!(Sphere::f(Sphere::minimizer(HIGH_D)), Sphere::MINIMUM)
69    }
70}
71
72/// This is the Rastrigin function.
73///
74/// The function is borrowed from [here](https://en.wikipedia.org/wiki/Test_functions_for_optimization).
75/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
76/// like in 2D:
77///
78/// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Rastrigin_function.png/800px-Rastrigin_function.png)
79pub struct Rastrigin {}
80
81impl Function for Rastrigin {
82    /// The bounds of the canonical sphere optimization problem are infinite.
83    const BOUNDS: (f64, f64) = (-5.12, 5.12);
84
85    /// The global minimum is constant and zero
86    const MINIMUM: f64 = 0.0;
87
88    /// Function for evaluating
89    fn f(x: Vec<f64>) -> f64 {
90        let a = 10.0;
91        let n = x.len() ;
92        let mut fx = a*(n as f64);
93
94        for i in 0..n {
95            fx += x[i].powi(2) - a*(2.0*x[i]*std::f64::consts::PI).cos();
96        }
97        fx
98    }
99
100    /// This function returns the minimizer (argument that will return the global minimum
101    fn minimizer(n: usize) -> Vec<f64> {
102        vec![0.0; n]
103    }
104}
105
106#[cfg(test)]
107mod rastrigin_tests {
108    use super::*;
109
110    #[test]
111    fn low_d() {
112        assert_eq!(Rastrigin::f(Rastrigin::minimizer(LOW_D)), Rastrigin::MINIMUM)
113    }
114
115    #[test]
116    fn high_d() {
117        assert_eq!(Rastrigin::f(Rastrigin::minimizer(HIGH_D)), Rastrigin::MINIMUM)
118    }
119}
120
121/// This is the Rosenbrock function.
122///
123/// The function is borrowed from [here](https://en.wikipedia.org/wiki/Test_functions_for_optimization).
124/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
125/// like in 2D:
126///
127/// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7e/Rosenbrock%27s_function_in_3D.pdf/page1-800px-Rosenbrock%27s_function_in_3D.pdf.jpg)
128pub struct Rosenbrock {}
129
130impl Function for Rosenbrock {
131    /// The bounds of the canonical sphere optimization problem are infinite.
132    const BOUNDS: (f64, f64) = (-f64::INFINITY, f64::INFINITY);
133
134    /// The global minimum is constant and zero
135    const MINIMUM: f64 = 0.0;
136
137    /// Function for evaluating
138    fn f(x: Vec<f64>) -> f64 {
139        let n = x.len();
140        let mut fx = 0.0;
141        for i in 0..(n-1) {
142            fx += 100.0*(x[i+1] - x[i].powi(2)).powi(2) + (1.0 - x[i]).powi(2);
143        }
144        fx
145    }
146
147    /// This function returns the minimizer (argument that will return the global minimum
148    fn minimizer(n: usize) -> Vec<f64> {
149        vec![1.0; n]
150    }
151}
152
153#[cfg(test)]
154mod rosenbrock_tests {
155    use super::*;
156
157    #[test]
158    fn low_d() {
159        assert_eq!(Rosenbrock::f(Rosenbrock::minimizer(LOW_D)), Rosenbrock::MINIMUM)
160    }
161
162    #[test]
163    fn high_d() {
164        assert_eq!(Rosenbrock::f(Rosenbrock::minimizer(HIGH_D)), Rosenbrock::MINIMUM)
165    }
166}
167
168/// This is the Ackley function.
169///
170/// The function is borrowed from [here](https://en.wikipedia.org/wiki/Test_functions_for_optimization).
171/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
172/// like in 2D:
173///
174/// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Ackley%27s_function.pdf/page1-800px-Ackley%27s_function.pdf.jpg)
175pub struct Ackley {}
176
177impl Function for Ackley {
178    /// The bounds of the canonical sphere optimization problem are infinite.
179    const BOUNDS: (f64, f64) = (-5.0, 5.0);
180
181    /// The global minimum is constant and zero
182    const MINIMUM: f64 = 0.0;
183
184    /// Function for evaluating
185    fn f(x: Vec<f64>) -> f64 {
186        let n=x.len();
187        let mut fx = 0.0;
188        let mut square_sum = 0.0;
189        let mut cosine_sum = 0.0;
190        for i in 0..n {
191            square_sum += x[i].powi(2);
192            cosine_sum += (2.0*std::f64::consts::PI*x[i]).cos();
193        }
194        fx += -20.0*(-0.2*(0.5*square_sum).sqrt()).exp();
195        fx -= (cosine_sum/(n as f64)).exp();
196        fx + std::f64::consts::E + 20.0
197    }
198
199    /// This function returns the minimizer (argument that will return the global minimum
200    fn minimizer(n: usize) -> Vec<f64> {
201        vec![0.0; n]
202    }
203}
204
205#[cfg(test)]
206mod ackley_tests {
207    use super::*;
208
209    #[test]
210    fn low_d() {
211        assert_eq!(Ackley::f(Ackley::minimizer(LOW_D)), Ackley::MINIMUM)
212    }
213
214    #[test]
215    fn high_d() {
216        assert_eq!(Ackley::f(Ackley::minimizer(HIGH_D)), Ackley::MINIMUM)
217    }
218}
219
220/// This is the Ackley function.
221///
222/// The function is borrowed from [here](https://en.wikipedia.org/wiki/Test_functions_for_optimization).
223/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
224/// like in 2D:
225///
226/// ![](https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Matyas_function.pdf/page1-800px-Matyas_function.pdf.jpg)
227pub struct Matyas {}
228
229impl Function for Matyas {
230    /// The bounds of the canonical sphere optimization problem are infinite.
231    const BOUNDS: (f64, f64) = (-10.0, 10.0);
232
233    /// The global minimum is constant and zero
234    const MINIMUM: f64 = 0.0;
235
236    /// Function for evaluating
237    fn f(x: Vec<f64>) -> f64 {
238        let n=x.len();
239        let mut square_sum = 0.0;
240        let mut prod = 1.0;
241        for i in 0..n {
242            square_sum += x[i].powi(2);
243            prod *= x[i];
244        }
245        0.26*square_sum - 0.48*prod
246    }
247
248    /// This function returns the minimizer (argument that will return the global minimum
249    fn minimizer(n: usize) -> Vec<f64> {
250        vec![0.0; n]
251    }
252}
253
254#[cfg(test)]
255mod matyas_tests {
256    use super::*;
257
258    #[test]
259    fn low_d() {
260        assert_eq!(Matyas::f(Matyas::minimizer(LOW_D)), Matyas::MINIMUM)
261    }
262
263    #[test]
264    fn high_d() {
265        assert_eq!(Matyas::f(Matyas::minimizer(HIGH_D)), Matyas::MINIMUM)
266    }
267}
268
269/// This is the Griewank function.
270///
271/// The function is borrowed from [here](http://benchmarkfcns.xyz/benchmarkfcns/griewankfcn.html).
272/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
273/// like in 2D:
274///
275/// ![](http://benchmarkfcns.xyz/benchmarkfcns/plots/griewankfcn_10_0.png)
276pub struct Griewank {}
277
278impl Function for Griewank {
279    /// The bounds of the canonical sphere optimization problem are infinite.
280    const BOUNDS: (f64, f64) = (-600.0, 600.0);
281
282    /// The global minimum is constant and zero
283    const MINIMUM: f64 = 0.0;
284
285    /// Function for evaluating
286    fn f(x: Vec<f64>) -> f64 {
287        let n=x.len();
288        let mut cosine_prod = 1.0;
289        let mut square_sum = 0.0;
290        for i in 0..n {
291            square_sum += x[i].powi(2);
292            cosine_prod *= (x[i]/((i+1) as f64).sqrt()).cos();
293        }
294        1.0 + square_sum/4000.0 - cosine_prod
295    }
296
297    /// This function returns the minimizer (argument that will return the global minimum
298    fn minimizer(n: usize) -> Vec<f64> {
299        vec![0.0; n]
300    }
301}
302
303#[cfg(test)]
304mod griewank_tests {
305    use super::*;
306
307    #[test]
308    fn low_d() {
309        assert_eq!(Griewank::f(Griewank::minimizer(LOW_D)), Griewank::MINIMUM)
310    }
311
312    #[test]
313    fn high_d() {
314        assert_eq!(Griewank::f(Griewank::minimizer(HIGH_D)), Griewank::MINIMUM)
315    }
316}
317
318/// This is the Ridge function.
319///
320/// The function is borrowed from [here](http://benchmarkfcns.xyz/benchmarkfcns/ridgefcn.html).
321/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
322/// like in 2D:
323///
324/// ![](http://benchmarkfcns.xyz/benchmarkfcns/plots/ridgefcn.png)
325pub struct Ridge {}
326
327impl Function for Ridge {
328    /// The bounds of the canonical sphere optimization problem are infinite.
329    const BOUNDS: (f64, f64) = (-5.0, 5.0);
330
331    /// The global minimum is constant and zero
332    const MINIMUM: f64 = -5.0;
333
334    /// Function for evaluating
335    fn f(x: Vec<f64>) -> f64 {
336        let n=x.len();
337        let D = 1.0;
338        let alpha = 0.0;
339        let mut square_sum = 0.0;
340        for i in 1..n {
341            square_sum += x[i].powi(2);
342        }
343        -1.0 + x[0] + D *square_sum.powf(alpha)
344    }
345
346    /// This function returns the minimizer (argument that will return the global minimum
347    fn minimizer(n: usize) -> Vec<f64> {
348        let mut v = vec![0.0; n];
349        v[0] = -5.0;
350        v
351    }
352}
353
354#[cfg(test)]
355mod ridge_tests {
356    use super::*;
357
358    #[test]
359    fn low_d() {
360        assert_eq!(Ridge::f(Ridge::minimizer(LOW_D)), Ridge::MINIMUM)
361    }
362
363    #[test]
364    fn high_d() {
365        assert_eq!(Ridge::f(Ridge::minimizer(HIGH_D)), Ridge::MINIMUM)
366    }
367}
368
369/// This is the Zakharov function.
370///
371/// The function is borrowed from [here](http://benchmarkfcns.xyz/benchmarkfcns/zakharov.html).
372/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
373/// like in 2D:
374///
375/// ![](http://benchmarkfcns.xyz/benchmarkfcns/plots/zakharovfcn.png)
376pub struct Zakharov {}
377
378impl Function for Zakharov {
379    /// The bounds of the canonical sphere optimization problem are infinite.
380    const BOUNDS: (f64, f64) = (-5.0, 10.0);
381
382    /// The global minimum is constant and zero
383    const MINIMUM: f64 = 0.0;
384
385    /// Function for evaluating
386    fn f(x: Vec<f64>) -> f64 {
387        let n=x.len();
388        let mut square_sum = 0.0;
389        let mut sum_ixi = 0.0;
390        for i in 0..n {
391            square_sum += x[i].powi(2);
392            sum_ixi += 0.5*x[i]*(i as f64);
393        }
394        square_sum + sum_ixi.powi(2) + sum_ixi.powi(4)
395    }
396
397    /// This function returns the minimizer (argument that will return the global minimum
398    fn minimizer(n: usize) -> Vec<f64> {
399        vec![0.0; n]
400    }
401}
402
403#[cfg(test)]
404mod zakharov_tests {
405    use super::*;
406
407    #[test]
408    fn low_d() {
409        assert_eq!(Zakharov::f(Zakharov::minimizer(LOW_D)), Zakharov::MINIMUM)
410    }
411
412    #[test]
413    fn high_d() {
414        assert_eq!(Zakharov::f(Zakharov::minimizer(HIGH_D)), Zakharov::MINIMUM)
415    }
416}
417
418/// This is the Salomon function.
419///
420/// The function is borrowed from [here](http://benchmarkfcns.xyz/benchmarkfcns/salomonfcn.html).
421/// Although the function accepts a vector with an arbitrary number of inputs, this is what it looks
422/// like in 2D:
423///
424/// ![](http://benchmarkfcns.xyz/benchmarkfcns/plots/salomonfcn.png)
425pub struct Salomon {}
426
427impl Function for Salomon {
428    /// The bounds of the canonical sphere optimization problem are infinite.
429    const BOUNDS: (f64, f64) = (-100.0, 100.0);
430
431    /// The global minimum is constant and zero
432    const MINIMUM: f64 = 0.0;
433
434    /// Function for evaluating
435    fn f(x: Vec<f64>) -> f64 {
436        let n=x.len();
437        let mut square_sum = 0.0;
438        for i in 0..n {
439            square_sum += x[i].powi(2);
440        }
441        1.0 - (2.0*std::f64::consts::PI*square_sum.sqrt()).cos() + 0.1*square_sum.sqrt()
442    }
443
444    /// This function returns the minimizer (argument that will return the global minimum
445    fn minimizer(n: usize) -> Vec<f64> {
446        vec![0.0; n]
447    }
448}
449
450#[cfg(test)]
451mod salomon_tests {
452    use super::*;
453
454    #[test]
455    fn low_d() {
456        assert_eq!(Salomon::f(Salomon::minimizer(LOW_D)), Salomon::MINIMUM)
457    }
458
459    #[test]
460    fn high_d() {
461        assert_eq!(Salomon::f(Salomon::minimizer(HIGH_D)), Salomon::MINIMUM)
462    }
463}