Skip to main content

math_audio_test_functions/functions/
schwefel.rs

1//! Schwefel test function
2
3use ndarray::Array1;
4
5/// Schwefel function - multimodal with many local minima
6/// Global minimum: f(x) = 0 at x = (420.9687, 420.9687, ..., 420.9687)
7/// Bounds: x_i in [-500, 500]
8pub fn schwefel(x: &Array1<f64>) -> f64 {
9    let n = x.len() as f64;
10    let sum: f64 = x.iter().map(|&xi| xi * xi.abs().sqrt().sin()).sum();
11    418.9829 * n - sum
12}
13#[cfg(test)]
14mod tests {
15    use super::*;
16
17    #[test]
18    fn test_schwefel_known_properties() {
19        use crate::{FunctionMetadata, get_function_metadata};
20        use ndarray::Array1;
21
22        // Get metadata for this function
23        let metadata = get_function_metadata();
24        let meta = metadata
25            .get("schwefel")
26            .expect("Function schwefel should have metadata");
27
28        // Test 1: Verify global minima are within bounds
29        for (minimum_coords, expected_value) in &meta.global_minima {
30            assert!(
31                minimum_coords.len() >= meta.bounds.len() || meta.bounds.len() == 1,
32                "Global minimum coordinates should match bounds dimensions"
33            );
34
35            for (i, &coord) in minimum_coords.iter().enumerate() {
36                if i < meta.bounds.len() {
37                    let (lower, upper) = meta.bounds[i];
38                    assert!(
39                        coord >= lower && coord <= upper,
40                        "Global minimum coordinate {} = {} should be within bounds [{} {}]",
41                        i,
42                        coord,
43                        lower,
44                        upper
45                    );
46                }
47            }
48        }
49
50        // Test 2: Verify function evaluates to expected values at global minima
51        for (minimum_coords, expected_value) in &meta.global_minima {
52            let x = Array1::from_vec(minimum_coords.clone());
53            let actual_value = schwefel(&x);
54
55            let error = (actual_value - expected_value).abs();
56            // Use adaptive tolerance based on magnitude of expected value
57            let tolerance = if expected_value.abs() > 1.0 {
58                1e-4 * expected_value.abs() // Relative tolerance for large values
59            } else if expected_value.abs() == 0.0 {
60                1e-4 // Higher tolerance for zero expected values (schwefel case)
61            } else {
62                1e-6 // Absolute tolerance for small non-zero values
63            };
64
65            assert!(
66                error <= tolerance,
67                "Function value at global minimum {:?} should be {}, got {}, error: {} (tolerance: {})",
68                minimum_coords,
69                expected_value,
70                actual_value,
71                error,
72                tolerance
73            );
74        }
75
76        // Test 3: Basic function properties
77        if !meta.global_minima.is_empty() {
78            let (first_minimum, _) = &meta.global_minima[0];
79            let x = Array1::from_vec(first_minimum.clone());
80            let result = schwefel(&x);
81
82            assert!(
83                result.is_finite(),
84                "Function should return finite values at global minimum"
85            );
86            assert!(
87                !result.is_nan(),
88                "Function should not return NaN at global minimum"
89            );
90        }
91    }
92}