metaheurustics/test_function/
mod.rs1use ndarray::{Array1, Array2};
2use std::f64::consts::PI;
3use crate::algorithm::ObjectiveFunction;
4
5mod additional;
6pub use additional::*;
7
8mod beale;
9pub use beale::*;
10
11pub trait TestFunction: ObjectiveFunction {
13 fn global_minimum(&self) -> f64;
15
16 fn global_minimum_position(&self) -> Array1<f64>;
18
19 fn name(&self) -> &'static str;
21
22 fn test_3d_surface(&self, n_points: usize) -> (Array2<f64>, Array2<f64>, Array2<f64>) {
23 let (min_bounds, max_bounds) = self.bounds();
24 let x_min = min_bounds[0];
25 let x_max = max_bounds[0];
26 let y_min = min_bounds[1];
27 let y_max = max_bounds[1];
28
29 let x_step = (x_max - x_min) / (n_points - 1) as f64;
30 let y_step = (y_max - y_min) / (n_points - 1) as f64;
31
32 let mut x = Array2::zeros((n_points, n_points));
33 let mut y = Array2::zeros((n_points, n_points));
34 let mut z = Array2::zeros((n_points, n_points));
35
36 for i in 0..n_points {
37 for j in 0..n_points {
38 let x_val = x_min + i as f64 * x_step;
39 let y_val = y_min + j as f64 * y_step;
40 x[[i, j]] = x_val;
41 y[[i, j]] = y_val;
42 z[[i, j]] = self.evaluate(&Array1::from_vec(vec![x_val, y_val]));
43 }
44 }
45
46 (x, y, z)
47 }
48}
49
50#[derive(Debug)]
53pub struct Ackley {
54 dims: usize,
55}
56
57impl Ackley {
58 pub fn new(dimensions: usize) -> Self {
59 Self { dims: dimensions }
60 }
61}
62
63impl ObjectiveFunction for Ackley {
64 fn evaluate(&self, x: &Array1<f64>) -> f64 {
65 let a = 20.0;
66 let b = 0.2;
67 let c = 2.0 * PI;
68
69 let n = x.len() as f64;
70 let sum_sq = x.iter().map(|xi| xi * xi).sum::<f64>();
71 let sum_cos = x.iter().map(|xi| (c * xi).cos()).sum::<f64>();
72
73 -a * (-b * (sum_sq / n).sqrt()).exp() - (sum_cos / n).exp() + a + std::f64::consts::E
74 }
75
76 fn dimensions(&self) -> usize {
77 self.dims
78 }
79
80 fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
81 (
82 vec![-32.768; self.dims],
83 vec![32.768; self.dims]
84 )
85 }
86}
87
88impl TestFunction for Ackley {
89 fn global_minimum(&self) -> f64 {
90 0.0
91 }
92
93 fn global_minimum_position(&self) -> Array1<f64> {
94 Array1::zeros(self.dims)
95 }
96
97 fn name(&self) -> &'static str {
98 "Ackley Function"
99 }
100}
101
102#[derive(Debug)]
105pub struct Sphere {
106 dims: usize,
107}
108
109impl Sphere {
110 pub fn new(dimensions: usize) -> Self {
111 Self { dims: dimensions }
112 }
113}
114
115impl ObjectiveFunction for Sphere {
116 fn evaluate(&self, x: &Array1<f64>) -> f64 {
117 x.iter().map(|xi| xi * xi).sum()
118 }
119
120 fn dimensions(&self) -> usize {
121 self.dims
122 }
123
124 fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
125 (
126 vec![-5.12; self.dims],
127 vec![5.12; self.dims]
128 )
129 }
130}
131
132impl TestFunction for Sphere {
133 fn global_minimum(&self) -> f64 {
134 0.0
135 }
136
137 fn global_minimum_position(&self) -> Array1<f64> {
138 Array1::zeros(self.dims)
139 }
140
141 fn name(&self) -> &'static str {
142 "Sphere Function"
143 }
144}
145
146#[derive(Debug)]
149pub struct Rosenbrock {
150 dims: usize,
151}
152
153impl Rosenbrock {
154 pub fn new(dimensions: usize) -> Self {
155 if dimensions < 2 {
156 panic!("Rosenbrock function requires at least 2 dimensions");
157 }
158 Self { dims: dimensions }
159 }
160}
161
162impl ObjectiveFunction for Rosenbrock {
163 fn evaluate(&self, x: &Array1<f64>) -> f64 {
164 let mut sum = 0.0;
165 for i in 0..self.dims - 1 {
166 sum += 100.0 * (x[i + 1] - x[i].powi(2)).powi(2) + (x[i] - 1.0).powi(2);
167 }
168 sum
169 }
170
171 fn dimensions(&self) -> usize {
172 self.dims
173 }
174
175 fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
176 (
177 vec![-2.048; self.dims],
178 vec![2.048; self.dims]
179 )
180 }
181}
182
183impl TestFunction for Rosenbrock {
184 fn global_minimum(&self) -> f64 {
185 0.0
186 }
187
188 fn global_minimum_position(&self) -> Array1<f64> {
189 Array1::ones(self.dims)
190 }
191
192 fn name(&self) -> &'static str {
193 "Rosenbrock Function"
194 }
195}
196
197#[derive(Debug)]
200pub struct Rastrigin {
201 dims: usize,
202}
203
204impl Rastrigin {
205 pub fn new(dimensions: usize) -> Self {
206 Self { dims: dimensions }
207 }
208}
209
210impl ObjectiveFunction for Rastrigin {
211 fn evaluate(&self, x: &Array1<f64>) -> f64 {
212 let a = 10.0;
213 let n = x.len() as f64;
214 let sum = x.iter()
215 .map(|xi| xi * xi - a * (2.0 * PI * xi).cos())
216 .sum::<f64>();
217 a * n + sum
218 }
219
220 fn dimensions(&self) -> usize {
221 self.dims
222 }
223
224 fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
225 (
226 vec![-5.12; self.dims],
227 vec![5.12; self.dims]
228 )
229 }
230}
231
232impl TestFunction for Rastrigin {
233 fn global_minimum(&self) -> f64 {
234 0.0
235 }
236
237 fn global_minimum_position(&self) -> Array1<f64> {
238 Array1::zeros(self.dims)
239 }
240
241 fn name(&self) -> &'static str {
242 "Rastrigin Function"
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use approx::assert_relative_eq;
250
251 #[test]
252 fn test_sphere_function() {
253 let sphere = Sphere::new(2);
254 let x = Array1::zeros(2);
255 assert_eq!(sphere.evaluate(&x), 0.0);
256
257 let x = Array1::from_vec(vec![1.0, 1.0]);
258 assert_eq!(sphere.evaluate(&x), 2.0);
259 }
260
261 #[test]
262 fn test_ackley_function() {
263 let ackley = Ackley::new(2);
264 let x = Array1::zeros(2);
265 assert_relative_eq!(ackley.evaluate(&x), 0.0, epsilon = 1e-10);
266 }
267
268 #[test]
269 fn test_rosenbrock_function() {
270 let rosenbrock = Rosenbrock::new(2);
271 let x = Array1::ones(2);
272 assert_eq!(rosenbrock.evaluate(&x), 0.0);
273 }
274
275 #[test]
276 fn test_rastrigin_function() {
277 let rastrigin = Rastrigin::new(2);
278 let x = Array1::zeros(2);
279 assert_eq!(rastrigin.evaluate(&x), 0.0);
280 }
281}