Skip to main content

math_audio_test_functions/functions/
ackley.rs

1//! Ackley test function
2
3use ndarray::Array1;
4
5/// Ackley function - N-dimensional multimodal
6/// Global minimum: f(x) = 0 at x = (0, 0, ..., 0)
7/// Bounds: x_i in [-32.768, 32.768]
8pub fn ackley(x: &Array1<f64>) -> f64 {
9    let n = x.len() as f64;
10    let sum_sq: f64 = x.iter().map(|&xi| xi.powi(2)).sum();
11    let sum_cos: f64 = x
12        .iter()
13        .map(|&xi| (2.0 * std::f64::consts::PI * xi).cos())
14        .sum();
15
16    -20.0 * (-0.2 * (sum_sq / n).sqrt()).exp() - (sum_cos / n).exp() + 20.0 + std::f64::consts::E
17}
18#[cfg(test)]
19mod tests {
20    use super::*;
21
22    #[test]
23    fn test_ackley_known_properties() {
24        use crate::{FunctionMetadata, get_function_metadata};
25        use ndarray::Array1;
26
27        // Get metadata for this function
28        let metadata = get_function_metadata();
29        let meta = metadata
30            .get("ackley")
31            .expect("Function ackley should have metadata");
32
33        // Test 1: Verify global minima are within bounds
34        for (minimum_coords, expected_value) in &meta.global_minima {
35            assert!(
36                minimum_coords.len() >= meta.bounds.len() || meta.bounds.len() == 1,
37                "Global minimum coordinates should match bounds dimensions"
38            );
39
40            for (i, &coord) in minimum_coords.iter().enumerate() {
41                if i < meta.bounds.len() {
42                    let (lower, upper) = meta.bounds[i];
43                    assert!(
44                        coord >= lower && coord <= upper,
45                        "Global minimum coordinate {} = {} should be within bounds [{} {}]",
46                        i,
47                        coord,
48                        lower,
49                        upper
50                    );
51                }
52            }
53        }
54
55        // Test 2: Verify function evaluates to expected values at global minima
56        let tolerance = 1e-6; // Reasonable tolerance for numerical precision
57        for (minimum_coords, expected_value) in &meta.global_minima {
58            let x = Array1::from_vec(minimum_coords.clone());
59            let actual_value = ackley(&x);
60
61            let error = (actual_value - expected_value).abs();
62            assert!(
63                error <= tolerance,
64                "Function value at global minimum {:?} should be {}, got {}, error: {}",
65                minimum_coords,
66                expected_value,
67                actual_value,
68                error
69            );
70        }
71
72        // Test 3: Basic function properties
73        if !meta.global_minima.is_empty() {
74            let (first_minimum, _) = &meta.global_minima[0];
75            let x = Array1::from_vec(first_minimum.clone());
76            let result = ackley(&x);
77
78            assert!(
79                result.is_finite(),
80                "Function should return finite values at global minimum"
81            );
82            assert!(
83                !result.is_nan(),
84                "Function should not return NaN at global minimum"
85            );
86        }
87    }
88}