metaheurustics/test_function/
additional.rs

1use ndarray::{s, Array1};
2use std::f64::consts::PI;
3use super::TestFunction;
4use crate::algorithm::ObjectiveFunction;
5
6/// Griewank Function
7/// f(x) = 1 + ∑(x²/4000) - ∏cos(x/√i)
8#[derive(Debug)]
9pub struct Griewank {
10    dims: usize,
11}
12
13impl Griewank {
14    pub fn new(dimensions: usize) -> Self {
15        Self { dims: dimensions }
16    }
17}
18
19impl ObjectiveFunction for Griewank {
20    fn evaluate(&self, x: &Array1<f64>) -> f64 {
21        let sum = x.iter().map(|&xi| xi * xi / 4000.0).sum::<f64>();
22        let prod = x.iter().enumerate()
23            .map(|(i, &xi)| (xi / ((i + 1) as f64).sqrt()).cos())
24            .product::<f64>();
25        
26        1.0 + sum - prod
27    }
28    
29    fn dimensions(&self) -> usize {
30        self.dims
31    }
32    
33    fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
34        (
35            vec![-600.0; self.dims],
36            vec![600.0; self.dims]
37        )
38    }
39}
40
41impl TestFunction for Griewank {
42    fn global_minimum(&self) -> f64 {
43        0.0
44    }
45    
46    fn global_minimum_position(&self) -> Array1<f64> {
47        Array1::zeros(self.dims)
48    }
49    
50    fn name(&self) -> &'static str {
51        "Griewank Function"
52    }
53}
54
55/// Schwefel Function
56/// f(x) = 418.9829n - ∑(x_i * sin(√|x_i|))
57#[derive(Debug)]
58pub struct Schwefel {
59    dims: usize,
60}
61
62impl Schwefel {
63    pub fn new(dimensions: usize) -> Self {
64        Self { dims: dimensions }
65    }
66}
67
68impl ObjectiveFunction for Schwefel {
69    fn evaluate(&self, x: &Array1<f64>) -> f64 {
70        418.9829 * self.dims as f64
71            - x.iter()
72                .map(|&xi| xi * (xi.abs().sqrt()).sin())
73                .sum::<f64>()
74    }
75    
76    fn dimensions(&self) -> usize {
77        self.dims
78    }
79    
80    fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
81        (
82            vec![-500.0; self.dims],
83            vec![500.0; self.dims]
84        )
85    }
86}
87
88impl TestFunction for Schwefel {
89    fn global_minimum(&self) -> f64 {
90        0.0
91    }
92    
93    fn global_minimum_position(&self) -> Array1<f64> {
94        Array1::from_elem(self.dims, 420.9687)
95    }
96    
97    fn name(&self) -> &'static str {
98        "Schwefel Function"
99    }
100}
101
102/// Levy Function
103/// f(x) = sin²(πw₁) + ∑(w_i-1)²[1+10sin²(πw_i+1)] + (w_n-1)²[1+sin²(2πw_n)]
104/// where w_i = 1 + (x_i-1)/4
105#[derive(Debug)]
106pub struct Levy {
107    dims: usize,
108}
109
110impl Levy {
111    pub fn new(dimensions: usize) -> Self {
112        Self { dims: dimensions }
113    }
114}
115
116impl ObjectiveFunction for Levy {
117    fn evaluate(&self, x: &Array1<f64>) -> f64 {
118        let w: Array1<f64> = x.mapv(|xi| 1.0 + (xi - 1.0) / 4.0);
119        
120        let term1 = (PI * w[0]).sin().powi(2);
121        
122        let term2 = w.slice(s![..-1])
123            .iter()
124            .map(|&wi| {
125                (wi - 1.0).powi(2) * (1.0 + 10.0 * (PI * wi + 1.0).sin().powi(2))
126            })
127            .sum::<f64>();
128        
129        let n = w.len() - 1;
130        let term3 = (w[n] - 1.0).powi(2) * (1.0 + (2.0 * PI * w[n]).sin().powi(2));
131        
132        term1 + term2 + term3
133    }
134    
135    fn dimensions(&self) -> usize {
136        self.dims
137    }
138    
139    fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
140        (
141            vec![-10.0; self.dims],
142            vec![10.0; self.dims]
143        )
144    }
145}
146
147impl TestFunction for Levy {
148    fn global_minimum(&self) -> f64 {
149        0.0
150    }
151    
152    fn global_minimum_position(&self) -> Array1<f64> {
153        Array1::ones(self.dims)
154    }
155    
156    fn name(&self) -> &'static str {
157        "Levy Function"
158    }
159}
160
161/// Michalewicz Function
162/// f(x) = -∑sin(x_i)[sin(ix_i²/π)]^(2m)
163#[derive(Debug)]
164pub struct Michalewicz {
165    dims: usize,
166    m: f64,
167}
168
169impl Michalewicz {
170    pub fn new(dimensions: usize, m: f64) -> Self {
171        Self { dims: dimensions, m }
172    }
173}
174
175impl ObjectiveFunction for Michalewicz {
176    fn evaluate(&self, x: &Array1<f64>) -> f64 {
177        -x.iter().enumerate()
178            .map(|(i, &xi)| {
179                xi.sin() * ((i + 1) as f64 * xi * xi / PI).sin().powf(2.0 * self.m)
180            })
181            .sum::<f64>()
182    }
183    
184    fn dimensions(&self) -> usize {
185        self.dims
186    }
187    
188    fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
189        (
190            vec![0.0; self.dims],
191            vec![PI; self.dims]
192        )
193    }
194}
195
196impl TestFunction for Michalewicz {
197    fn global_minimum(&self) -> f64 {
198        match self.dims {
199            2 => -1.8013,
200            5 => -4.687658,
201            10 => -9.66015,
202            _ => f64::NEG_INFINITY, // Exact value depends on dimensions
203        }
204    }
205    
206    fn global_minimum_position(&self) -> Array1<f64> {
207        // Position depends on dimensions and m parameter
208        // Here we return a reasonable approximation
209        Array1::from_elem(self.dims, 2.20)
210    }
211    
212    fn name(&self) -> &'static str {
213        "Michalewicz Function"
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use approx::assert_relative_eq;
221    
222    #[test]
223    fn test_griewank_minimum() {
224        let griewank = Griewank::new(2);
225        let x = Array1::zeros(2);
226        assert_relative_eq!(griewank.evaluate(&x), 0.0, epsilon = 1e-10);
227    }
228    
229    #[test]
230    fn test_schwefel_bounds() {
231        let schwefel = Schwefel::new(2);
232        let (lower, upper) = schwefel.bounds();
233        assert_eq!(lower[0], -500.0);
234        assert_eq!(upper[0], 500.0);
235    }
236    
237    #[test]
238    fn test_levy_minimum() {
239        let levy = Levy::new(2);
240        let x = Array1::ones(2);
241        assert_relative_eq!(levy.evaluate(&x), 0.0, epsilon = 1e-10);
242    }
243    
244    #[test]
245    fn test_michalewicz_bounds() {
246        let michalewicz = Michalewicz::new(2, 10.0);
247        let (lower, upper) = michalewicz.bounds();
248        assert_eq!(lower[0], 0.0);
249        assert_relative_eq!(upper[0], PI, epsilon = 1e-10);
250    }
251}