argmin_testfunctions/
sphere.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//! # Sphere function
9//!
10//! Defined as
11//!
12//! $$
13//! f(x_1,\\,x_2,\\,\ldots,\\,x_d) = \sum_{i=1}^d x_i^2
14//! $$
15//!
16//! where $x_i \in (-\infty, \infty)$ and $d > 0$.
17//!
18//! The global minimum is at $f(x_1,\\,x_2,\\,\ldots,\\,x_d) = f(0,\\,0,\\,\ldots,\\,0) = 0$.
19
20use num::{Float, FromPrimitive};
21use std::iter::Sum;
22
23/// Sphere test function.
24///
25/// Defined as
26///
27/// $$
28/// f(x_1,\\,x_2,\\,\ldots,\\,x_d) = \sum_{i=1}^d x_i^2
29/// $$
30///
31/// where $x_i \in (-\infty, \infty)$ and $d > 0$.
32///
33/// The global minimum is at $f(x_1,\\,x_2,\\,\ldots,\\,x_d) = f(0,\\,0,\\,\ldots,\\,0) = 0$.
34pub fn sphere<T>(param: &[T]) -> T
35where
36    T: Float + FromPrimitive + Sum,
37{
38    param.iter().map(|x| x.powi(2)).sum()
39}
40
41/// Derivative of sphere test function.
42///
43/// Defined as
44///
45/// $$
46/// f(x_1,\\,x_2,\\,\ldots,\\,x_d) = (2x_1,\\,2x_2,\\,\ldots,\\,2x_d)
47/// $$
48///
49/// where $x_i \in (-\infty, \infty)$ and $d > 0$.
50pub fn sphere_derivative<T>(param: &[T]) -> Vec<T>
51where
52    T: Float + FromPrimitive,
53{
54    let num2 = T::from_f64(2.0).unwrap();
55    param.iter().map(|x| num2 * *x).collect()
56}
57
58/// Derivative of sphere test function.
59///
60/// Defined as
61///
62/// $$
63/// f(x_1,\\,x_2,\\,\ldots,\\,x_d) = (2x_1,\\,2x_2,\\,\ldots,\\,2x_d)
64/// $$
65///
66/// where $x_i \in (-\infty, \infty)$ and $d > 0$.
67///
68/// This is the const generics version, which requires the number of parameters to be known
69/// at compile time.
70pub fn sphere_derivative_const<const N: usize, T>(param: &[T; N]) -> [T; N]
71where
72    T: Float + FromPrimitive,
73{
74    let num2 = T::from_f64(2.0).unwrap();
75    let mut deriv = [T::from_f64(0.0).unwrap(); N];
76    for i in 0..N {
77        deriv[i] = num2 * param[i];
78    }
79    deriv
80}
81
82/// Hessian of sphere test function.
83pub fn sphere_hessian<T>(param: &[T]) -> Vec<Vec<T>>
84where
85    T: Float + FromPrimitive,
86{
87    let n = param.len();
88    let mut hessian = vec![vec![T::from_f64(0.0).unwrap(); n]; n];
89    for (i, row) in hessian.iter_mut().enumerate().take(n) {
90        row[i] = T::from_f64(2.0).unwrap();
91    }
92    hessian
93}
94
95/// Hessian of sphere test function.
96///
97/// This is the const generics version, which requires the number of parameters to be known
98/// at compile time.
99pub fn sphere_hessian_const<const N: usize, T>(_param: &[T; N]) -> [[T; N]; N]
100where
101    T: Float + FromPrimitive,
102{
103    let mut hessian = [[T::from_f64(0.0).unwrap(); N]; N];
104    for (i, row) in hessian.iter_mut().enumerate().take(N) {
105        row[i] = T::from_f64(2.0).unwrap();
106    }
107    hessian
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use approx::assert_relative_eq;
114    use finitediff::FiniteDiff;
115    use proptest::prelude::*;
116
117    #[test]
118    fn test_sphere_optimum() {
119        assert_relative_eq!(sphere(&[0.0_f32, 0.0_f32]), 0.0, epsilon = f32::EPSILON);
120        assert_relative_eq!(sphere(&[0.0_f64, 0.0_f64]), 0.0, epsilon = f64::EPSILON);
121    }
122
123    proptest! {
124        #[test]
125        fn test_sphere(a in -10.0..10.0,
126                       b in -10.0..10.0,
127                       c in -10.0..10.0,
128                       d in -10.0..10.0,
129                       e in -10.0..10.0,
130                       f in -10.0..10.0,
131                       g in -10.0..10.0,
132                       h in -10.0..10.0) {
133            let param: [f64; 8] = [a, b, c, d, e, f, g, h];
134            let v1 = sphere(&param);
135            let v2 = a.powi(2) + b.powi(2) + c.powi(2) + d.powi(2) + e.powi(2) + f.powi(2) + g.powi(2) + h.powi(2);
136            assert_relative_eq!(v1, v2, epsilon = f64::EPSILON);
137        }
138    }
139
140    proptest! {
141        #[test]
142        fn test_sphere_derivative(a in -10.0..10.0,
143                                  b in -10.0..10.0,
144                                  c in -10.0..10.0,
145                                  d in -10.0..10.0,
146                                  e in -10.0..10.0,
147                                  f in -10.0..10.0,
148                                  g in -10.0..10.0,
149                                  h in -10.0..10.0) {
150            let param = [a, b, c, d, e, f, g, h];
151            let derivative = sphere_derivative(&param);
152            let derivative_fd =
153                [2.0 * a, 2.0 * b, 2.0 * c, 2.0 * d, 2.0 * e, 2.0 * f, 2.0 * g, 2.0 * h];
154            for i in 0..derivative.len() {
155                assert_relative_eq!(
156                    derivative[i],
157                    derivative_fd[i],
158                    epsilon = 1e-5,
159                    max_relative = 1e-2
160                );
161            }
162        }
163    }
164
165    proptest! {
166        #[test]
167        fn test_sphere_derivative_const(a in -10.0..10.0,
168                                        b in -10.0..10.0,
169                                        c in -10.0..10.0,
170                                        d in -10.0..10.0,
171                                        e in -10.0..10.0,
172                                        f in -10.0..10.0,
173                                        g in -10.0..10.0,
174                                        h in -10.0..10.0) {
175            let param = [a, b, c, d, e, f, g, h];
176            let derivative = sphere_derivative_const(&param);
177            let derivative_fd =
178                [2.0 * a, 2.0 * b, 2.0 * c, 2.0 * d, 2.0 * e, 2.0 * f, 2.0 * g, 2.0 * h];
179            for i in 0..derivative.len() {
180                assert_relative_eq!(
181                    derivative[i],
182                    derivative_fd[i],
183                    epsilon = 1e-5,
184                    max_relative = 1e-2,
185                );
186            }
187        }
188    }
189
190    proptest! {
191        #[test]
192        fn test_sphere_derivative_finitediff(a in -10.0..10.0,
193                                             b in -10.0..10.0,
194                                             c in -10.0..10.0,
195                                             d in -10.0..10.0,
196                                             e in -10.0..10.0,
197                                             f in -10.0..10.0,
198                                             g in -10.0..10.0,
199                                             h in -10.0..10.0) {
200            let param = [a, b, c, d, e, f, g, h];
201            let derivative = sphere_derivative(&param);
202            let derivative_fd = Vec::from(param).central_diff(&|x| sphere(&x));
203            for i in 0..derivative.len() {
204                assert_relative_eq!(
205                    derivative[i],
206                    derivative_fd[i],
207                    epsilon = 1e-5,
208                    max_relative = 1e-2
209                );
210            }
211        }
212    }
213
214    proptest! {
215        #[test]
216        fn test_sphere_derivative_const_finitediff(a in -10.0..10.0,
217                                                   b in -10.0..10.0,
218                                                   c in -10.0..10.0,
219                                                   d in -10.0..10.0,
220                                                   e in -10.0..10.0,
221                                                   f in -10.0..10.0,
222                                                   g in -10.0..10.0,
223                                                   h in -10.0..10.0) {
224            let param = [a, b, c, d, e, f, g, h];
225            let derivative = sphere_derivative_const(&param);
226            let derivative_fd = Vec::from(param).central_diff(&|x| sphere(&x));
227            for i in 0..derivative.len() {
228                assert_relative_eq!(
229                    derivative[i],
230                    derivative_fd[i],
231                    epsilon = 1e-5,
232                    max_relative = 1e-2
233                );
234            }
235        }
236    }
237
238    proptest! {
239        #[test]
240        fn test_sphere_hessian(a in -10.0..10.0,
241                                  b in -10.0..10.0,
242                                  c in -10.0..10.0,
243                                  d in -10.0..10.0,
244                                  e in -10.0..10.0,
245                                  f in -10.0..10.0,
246                                  g in -10.0..10.0,
247                                  h in -10.0..10.0) {
248            let param = [a, b, c, d, e, f, g, h];
249            let hessian = sphere_hessian(&param);
250            for i in 0..hessian.len() {
251                for j in 0..hessian.len() {
252                    if i == j {
253                        assert_relative_eq!(hessian[i][j], 2.0, epsilon = f64::EPSILON);
254                    } else {
255                        assert_relative_eq!(hessian[i][j], 0.0, epsilon = f64::EPSILON);
256                    }
257                }
258            }
259        }
260    }
261
262    proptest! {
263        #[test]
264        fn test_sphere_hessian_const(a in -10.0..10.0,
265                                     b in -10.0..10.0,
266                                     c in -10.0..10.0,
267                                     d in -10.0..10.0,
268                                     e in -10.0..10.0,
269                                     f in -10.0..10.0,
270                                     g in -10.0..10.0,
271                                     h in -10.0..10.0) {
272            let param = [a, b, c, d, e, f, g, h];
273            let hessian = sphere_hessian_const(&param);
274            for i in 0..hessian.len() {
275                for j in 0..hessian.len() {
276                    if i == j {
277                        assert_relative_eq!(hessian[i][j], 2.0, epsilon = f64::EPSILON);
278                    } else {
279                        assert_relative_eq!(hessian[i][j], 0.0, epsilon = f64::EPSILON);
280                    }
281                }
282            }
283        }
284    }
285
286    proptest! {
287        #[test]
288        fn test_sphere_hessian_finitediff(a in -10.0..10.0,
289                                          b in -10.0..10.0,
290                                          c in -10.0..10.0,
291                                          d in -10.0..10.0,
292                                          e in -10.0..10.0,
293                                          f in -10.0..10.0,
294                                          g in -10.0..10.0,
295                                          h in -10.0..10.0) {
296            let param = [a, b, c, d, e, f, g, h];
297            let hessian = sphere_hessian(&param);
298            let hessian_fd = Vec::from(param).central_hessian(&|x| sphere_derivative(&x));
299            for i in 0..hessian.len() {
300                for j in 0..hessian.len() {
301                    assert_relative_eq!(
302                        hessian[i][j],
303                        hessian_fd[i][j],
304                        epsilon = 1e-5,
305                        max_relative = 1e-2
306                    );
307                }
308            }
309        }
310    }
311
312    proptest! {
313        #[test]
314        fn test_sphere_hessian_const_finitediff(a in -10.0..10.0,
315                                                b in -10.0..10.0,
316                                                c in -10.0..10.0,
317                                                d in -10.0..10.0,
318                                                e in -10.0..10.0,
319                                                f in -10.0..10.0,
320                                                g in -10.0..10.0,
321                                                h in -10.0..10.0) {
322            let param = [a, b, c, d, e, f, g, h];
323            let hessian = sphere_hessian_const(&param);
324            let hessian_fd = Vec::from(param).central_hessian(&|x| sphere_derivative(&x));
325            for i in 0..hessian.len() {
326                for j in 0..hessian.len() {
327                    assert_relative_eq!(
328                        hessian[i][j],
329                        hessian_fd[i][j],
330                        epsilon = 1e-5,
331                        max_relative = 1e-2
332                    );
333                }
334            }
335        }
336    }
337}