metaheurustics/test_function/
beale.rs

1use ndarray::Array1;
2use crate::algorithm::ObjectiveFunction;
3use crate::test_function::TestFunction;
4
5/// Beale Function
6/// f(x₁,x₂) = (1.5 - x₁ + x₁x₂)² + (2.25 - x₁ + x₁x₂²)² + (2.625 - x₁ + x₁x₂³)²
7/// Global minimum: f(3, 0.5) = 0
8#[derive(Debug)]
9pub struct Beale;
10
11impl Beale {
12    /// Create a new instance of the Beale function
13    pub fn new() -> Self {
14        Beale
15    }
16}
17
18impl ObjectiveFunction for Beale {
19    fn evaluate(&self, x: &Array1<f64>) -> f64 {
20        if x.len() != 2 {
21            panic!("Beale function is only defined for 2 dimensions");
22        }
23
24        let x1 = x[0];
25        let x2 = x[1];
26        
27        let term1 = (1.5 - x1 + x1 * x2).powi(2);
28        let term2 = (2.25 - x1 + x1 * x2.powi(2)).powi(2);
29        let term3 = (2.625 - x1 + x1 * x2.powi(3)).powi(2);
30        
31        term1 + term2 + term3
32    }
33
34    fn dimensions(&self) -> usize {
35        2
36    }
37
38    fn bounds(&self) -> (Vec<f64>, Vec<f64>) {
39        (vec![-4.5; 2], vec![4.5; 2])
40    }
41}
42
43impl TestFunction for Beale {
44    fn global_minimum(&self) -> f64 {
45        0.0
46    }
47
48    fn global_minimum_position(&self) -> Array1<f64> {
49        Array1::from_vec(vec![3.0, 0.5])
50    }
51
52    fn name(&self) -> &'static str {
53        "Beale"
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use approx::assert_abs_diff_eq;
61    use ndarray::Array1;
62
63    #[test]
64    fn test_beale_global_minimum() {
65        let beale = Beale::new();
66        let global_min_pos = beale.global_minimum_position();
67        let global_min = beale.evaluate(&global_min_pos);
68        assert_abs_diff_eq!(global_min, beale.global_minimum(), epsilon = 1e-6);
69    }
70
71    #[test]
72    fn test_beale_known_points() {
73        let beale = Beale::new();
74        
75        // Test origin
76        let origin = Array1::zeros(2);
77        assert_abs_diff_eq!(beale.evaluate(&origin), 14.203125, epsilon = 1e-6);
78        
79        // Test point (1, 1)
80        let point1 = Array1::from_vec(vec![1.0, 1.0]);
81        assert_abs_diff_eq!(beale.evaluate(&point1), 14.203125, epsilon = 1e-6);
82        
83        // Test point (2, 2)
84        let point2 = Array1::from_vec(vec![2.0, 2.0]);
85        assert_abs_diff_eq!(beale.evaluate(&point2), 356.703125, epsilon = 1e-6);
86    }
87
88    #[test]
89    fn test_beale_bounds() {
90        let beale = Beale::new();
91        let (lower, upper) = beale.bounds();
92        
93        // Test bounds are correct
94        assert_eq!(lower, vec![-4.5; 2]);
95        assert_eq!(upper, vec![4.5; 2]);
96        
97        // Test points at bounds
98        let lower_bound = Array1::from_vec(lower.clone());
99        let upper_bound = Array1::from_vec(upper.clone());
100        
101        // Ensure function can be evaluated at bounds
102        let _ = beale.evaluate(&lower_bound);
103        let _ = beale.evaluate(&upper_bound);
104    }
105
106    #[test]
107    fn test_beale_dimensions() {
108        let beale = Beale::new();
109        assert_eq!(beale.dimensions(), 2);
110    }
111
112    #[test]
113    #[should_panic(expected = "Beale function is only defined for 2 dimensions")]
114    fn test_beale_wrong_dimensions() {
115        let beale = Beale::new();
116        let wrong_dim = Array1::zeros(3);
117        beale.evaluate(&wrong_dim);
118    }
119
120    #[test]
121    fn test_beale_symmetry() {
122        let beale = Beale::new();
123        let point1 = Array1::from_vec(vec![1.0, 2.0]);
124        let point2 = Array1::from_vec(vec![1.0, -2.0]);
125        
126        // Beale function is not symmetric
127        assert_ne!(beale.evaluate(&point1), beale.evaluate(&point2));
128    }
129}