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