argmin_testfunctions/
styblinskitang.rs

1// Copyright 2018-2024 argmin developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! # Styblinski-Tang test function
9//!
10//! Defined as
11//!
12//! $$
13//! f(x_1,\\,x_2,\\,\ldots,\\,x_d) = \frac{1}{2}\sum_{i=1}^{d} \left(x_i^4 - 16x_i^2 + 5x_i\right)
14//! $$
15//!
16//! where $x_i \in [-5, 5]$.
17//!
18//! The global minimum is at $f(x_1, x_2, ..., x_d) = f(-2.903534,\\,-2.903534,\\,\ldots,\\,-2.903534) =
19//! -39.16616d$.
20
21use num::{Float, FromPrimitive};
22use std::iter::Sum;
23
24/// Styblinski-Tang test function.
25///
26/// Defined as
27///
28/// $$
29/// f(x_1,\\,x_2,\\,\ldots,\\,x_d) = \frac{1}{2}\sum_{i=1}^{d} \left(x_i^4 - 16x_i^2 + 5x_i\right)
30/// $$
31///
32/// where $x_i \in [-5, 5]$.
33///
34/// The global minimum is at $f(x_1, x_2, ..., x_d) = f(-2.903534,\\,-2.903534,\\,\ldots,\\,-2.903534) =
35/// -39.16616d$.
36pub fn styblinski_tang<T>(param: &[T]) -> T
37where
38    T: Float + FromPrimitive + Sum,
39{
40    T::from_f64(0.5).unwrap()
41        * param
42            .iter()
43            .map(|x| {
44                x.powi(4) - T::from_f64(16.0).unwrap() * x.powi(2) + T::from_f64(5.0).unwrap() * *x
45            })
46            .sum()
47}
48
49/// Derivative of Styblinski-Tang test function.
50pub fn styblinski_tang_derivative<T>(param: &[T]) -> Vec<T>
51where
52    T: Float + FromPrimitive + Sum,
53{
54    let n2 = T::from_f64(2.0).unwrap();
55    let n2_5 = T::from_f64(2.5).unwrap();
56    let n16 = T::from_f64(16.0).unwrap();
57
58    param
59        .iter()
60        .map(|x| n2 * x.powi(3) - n16 * *x + n2_5)
61        .collect()
62}
63
64/// Derivative of Styblinski-Tang test function.
65///
66/// This is the const generics version, which requires the number of parameters to be known
67/// at compile time.
68pub fn styblinski_tang_derivative_const<const N: usize, T>(param: &[T; N]) -> [T; N]
69where
70    T: Float + FromPrimitive + Sum,
71{
72    let n0 = T::from_f64(0.0).unwrap();
73    let n2 = T::from_f64(2.0).unwrap();
74    let n2_5 = T::from_f64(2.5).unwrap();
75    let n16 = T::from_f64(16.0).unwrap();
76
77    let mut out = [n0; N];
78
79    param
80        .iter()
81        .zip(out.iter_mut())
82        .map(|(x, o)| *o = n2 * x.powi(3) - n16 * *x + n2_5)
83        .count();
84
85    out
86}
87
88/// Hessian of Styblinski-Tang test function.
89pub fn styblinski_tang_hessian<T>(param: &[T]) -> Vec<Vec<T>>
90where
91    T: Float + FromPrimitive + Sum,
92{
93    let n0 = T::from_f64(0.0).unwrap();
94    let n6 = T::from_f64(6.0).unwrap();
95    let n16 = T::from_f64(16.0).unwrap();
96
97    let n = param.len();
98    let mut out = vec![vec![n0; n]; n];
99
100    param
101        .iter()
102        .enumerate()
103        .map(|(i, x)| out[i][i] = n6 * x.powi(2) - n16)
104        .count();
105
106    out
107}
108
109/// Hessian of Styblinski-Tang test function.
110///
111/// This is the const generics version, which requires the number of parameters to be known
112/// at compile time.
113pub fn styblinski_tang_hessian_const<const N: usize, T>(param: &[T; N]) -> [[T; N]; N]
114where
115    T: Float + FromPrimitive + Sum,
116{
117    let n0 = T::from_f64(0.0).unwrap();
118    let n6 = T::from_f64(6.0).unwrap();
119    let n16 = T::from_f64(16.0).unwrap();
120
121    let mut out = [[n0; N]; N];
122
123    param
124        .iter()
125        .enumerate()
126        .map(|(i, x)| out[i][i] = n6 * x.powi(2) - n16)
127        .count();
128
129    out
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use approx::assert_relative_eq;
136    use finitediff::FiniteDiff;
137    use proptest::prelude::*;
138    use std::f32;
139
140    #[test]
141    fn test_styblinski_tang_optimum() {
142        assert_relative_eq!(
143            styblinski_tang(&[-2.903534_f32, -2.903534_f32, -2.903534_f32]),
144            -117.49849,
145            epsilon = f32::EPSILON
146        );
147        assert_relative_eq!(
148            styblinski_tang(&[-2.903534_f64, -2.903534_f64, -2.903534_f64]),
149            -117.4984971113142,
150            epsilon = f64::EPSILON
151        );
152
153        let deriv = styblinski_tang_derivative(&[-2.903534_f64, -2.903534_f64, -2.903534_f64]);
154        for i in 0..3 {
155            assert_relative_eq!(deriv[i], 0.0, epsilon = 1e-5, max_relative = 1e-2);
156        }
157    }
158
159    proptest! {
160        #[test]
161        fn test_styblinski_tang_derivative_finitediff(a in -5.0..5.0,
162                                                      b in -5.0..5.0,
163                                                      c in -5.0..5.0,
164                                                      d in -5.0..5.0,
165                                                      e in -5.0..5.0,
166                                                      f in -5.0..5.0,
167                                                      g in -5.0..5.0,
168                                                      h in -5.0..5.0) {
169            let param = [a, b, c, d, e, f, g, h];
170            let derivative = styblinski_tang_derivative(&param);
171            let derivative_fd = Vec::from(param).central_diff(&|x| styblinski_tang(&x));
172            for i in 0..derivative.len() {
173                assert_relative_eq!(
174                    derivative[i],
175                    derivative_fd[i],
176                    epsilon = 1e-5,
177                    max_relative = 1e-2
178                );
179            }
180        }
181    }
182
183    proptest! {
184        #[test]
185        fn test_styblinski_tang_derivative_const_finitediff(a in -5.0..5.0,
186                                                            b in -5.0..5.0,
187                                                            c in -5.0..5.0,
188                                                            d in -5.0..5.0,
189                                                            e in -5.0..5.0,
190                                                            f in -5.0..5.0,
191                                                            g in -5.0..5.0,
192                                                            h in -5.0..5.0) {
193            let param = [a, b, c, d, e, f, g, h];
194            let derivative = styblinski_tang_derivative_const(&param);
195            let derivative_fd = Vec::from(param).central_diff(&|x| styblinski_tang(&x));
196            for i in 0..derivative.len() {
197                assert_relative_eq!(
198                    derivative[i],
199                    derivative_fd[i],
200                    epsilon = 1e-5,
201                    max_relative = 1e-2
202                );
203            }
204        }
205    }
206
207    proptest! {
208        #[test]
209        fn test_styblinski_tang_hessian_finitediff(a in -5.0..5.0,
210                                                   b in -5.0..5.0,
211                                                   c in -5.0..5.0,
212                                                   d in -5.0..5.0,
213                                                   e in -5.0..5.0,
214                                                   f in -5.0..5.0,
215                                                   g in -5.0..5.0,
216                                                   h in -5.0..5.0) {
217            let param = [a, b, c, d, e, f, g, h];
218            let derivative = styblinski_tang_hessian(&param);
219            let derivative_fd = Vec::from(param).central_hessian(&|x| styblinski_tang_derivative(&x));
220            for i in 0..derivative.len() {
221                for j in 0..derivative[i].len() {
222                    assert_relative_eq!(
223                        derivative[i][j],
224                        derivative_fd[i][j],
225                        epsilon = 1e-5,
226                        max_relative = 1e-2
227                    );
228                }
229            }
230        }
231    }
232
233    proptest! {
234        #[test]
235        fn test_styblinski_tang_hessian_const_finitediff(a in -5.0..5.0,
236                                                         b in -5.0..5.0,
237                                                         c in -5.0..5.0,
238                                                         d in -5.0..5.0,
239                                                         e in -5.0..5.0,
240                                                         f in -5.0..5.0,
241                                                         g in -5.0..5.0,
242                                                         h in -5.0..5.0) {
243            let param = [a, b, c, d, e, f, g, h];
244            let derivative = styblinski_tang_hessian_const(&param);
245            let derivative_fd = Vec::from(param).central_hessian(&|x| styblinski_tang_derivative(&x));
246            for i in 0..derivative.len() {
247                for j in 0..derivative[i].len() {
248                    assert_relative_eq!(
249                        derivative[i][j],
250                        derivative_fd[i][j],
251                        epsilon = 1e-5,
252                        max_relative = 1e-2
253                    );
254                }
255            }
256        }
257    }
258}