1use num::{Float, FromPrimitive};
22use std::f64::consts::PI;
23use std::iter::Sum;
24
25pub fn ackley<T>(param: &[T]) -> T
38where
39 T: Float + FromPrimitive + Sum,
40{
41 ackley_abc(
42 param,
43 T::from_f64(20.0).unwrap(),
44 T::from_f64(0.2).unwrap(),
45 T::from_f64(2.0 * PI).unwrap(),
46 )
47}
48
49pub fn ackley_abc<T>(param: &[T], a: T, b: T, c: T) -> T
53where
54 T: Float + FromPrimitive + Sum,
55{
56 let num1 = T::from_f64(1.0).unwrap();
57 let n = T::from_usize(param.len()).unwrap();
58 -a * (-b * ((num1 / n) * param.iter().map(|x| x.powi(2)).sum()).sqrt()).exp()
59 - ((num1 / n) * param.iter().map(|x| (c * *x).cos()).sum()).exp()
60 + a
61 + num1.exp()
62}
63
64pub fn ackley_derivative<T>(param: &[T]) -> Vec<T>
68where
69 T: Float + FromPrimitive + Sum,
70{
71 ackley_abc_derivative(
72 param,
73 T::from_f64(20.0).unwrap(),
74 T::from_f64(0.2).unwrap(),
75 T::from_f64(2.0 * PI).unwrap(),
76 )
77}
78
79pub fn ackley_abc_derivative<T>(param: &[T], a: T, b: T, c: T) -> Vec<T>
83where
84 T: Float + FromPrimitive + Sum,
85{
86 let d = T::from_usize(param.len()).unwrap();
87 let n0 = T::from_f64(0.0).unwrap();
88 let eps = T::epsilon();
89
90 let norm = param.iter().map(|x| x.powi(2)).sum::<T>().sqrt();
91
92 let f1 = (c * (param.iter().map(|&x| (c * x).cos()).sum::<T>() / d).exp()) / d;
93 let f2 = if norm <= eps {
94 n0
95 } else {
96 (a * b * (-b * norm / d.sqrt()).exp()) / (d.sqrt() * norm)
97 };
98
99 param
100 .iter()
101 .map(|&x| ((c * x).sin() * f1) + x * f2)
102 .collect()
103}
104
105pub fn ackley_derivative_const<const N: usize, T>(param: &[T; N]) -> [T; N]
112where
113 T: Float + FromPrimitive + Sum,
114{
115 ackley_abc_derivative_const(
116 param,
117 T::from_f64(20.0).unwrap(),
118 T::from_f64(0.2).unwrap(),
119 T::from_f64(2.0 * PI).unwrap(),
120 )
121}
122
123pub fn ackley_abc_derivative_const<const N: usize, T>(param: &[T; N], a: T, b: T, c: T) -> [T; N]
130where
131 T: Float + FromPrimitive + Sum,
132{
133 let d = T::from_usize(param.len()).unwrap();
134 let n0 = T::from_f64(0.0).unwrap();
135 let eps = T::epsilon();
136
137 let norm = param.iter().map(|x| x.powi(2)).sum::<T>().sqrt();
138
139 let f1 = (c * (param.iter().map(|&x| (c * x).cos()).sum::<T>() / d).exp()) / d;
140 let f2 = if norm <= eps {
141 n0
142 } else {
143 (a * b * (-b * norm / d.sqrt()).exp()) / (d.sqrt() * norm)
144 };
145
146 let mut out = [n0; N];
147 param
148 .iter()
149 .zip(out.iter_mut())
150 .map(|(&x, o)| *o = ((c * x).sin() * f1) + x * f2)
151 .count();
152
153 out
154}
155
156pub fn ackley_hessian<T>(param: &[T]) -> Vec<Vec<T>>
160where
161 T: Float + FromPrimitive + Sum + std::fmt::Debug,
162{
163 ackley_abc_hessian(
164 param,
165 T::from_f64(20.0).unwrap(),
166 T::from_f64(0.2).unwrap(),
167 T::from_f64(2.0 * PI).unwrap(),
168 )
169}
170
171pub fn ackley_abc_hessian<T>(param: &[T], a: T, b: T, c: T) -> Vec<Vec<T>>
175where
176 T: Float + FromPrimitive + Sum,
177{
178 let du = param.len();
179 assert!(du >= 1);
180 let d = T::from_usize(du).unwrap();
181 let n0 = T::from_f64(0.0).unwrap();
182 let eps = T::epsilon();
183
184 let x = param;
186
187 let sqsum = param.iter().map(|x| x.powi(2)).sum::<T>();
188 let norm = sqsum.sqrt();
189 let nexp = (-b * norm / d.sqrt()).exp();
190
191 let f1 = -c.powi(2) * (x.iter().map(|&x| (c * x).cos()).sum::<T>() / d).exp();
192 let f2 = (a * b.powi(2) * nexp) / (d * sqsum);
193 let f3 = (a * b * nexp) / (d.sqrt() * norm.powi(3));
194 let f4 = (a * b * nexp) / (d.sqrt() * norm);
195 let f5 = (a * b.powi(2) * nexp) / (d * sqsum);
196 let f6 = (a * b * nexp) / (d.sqrt() * norm.powi(3));
197
198 let mut out = vec![vec![n0; du]; du];
199 for i in 0..du {
200 for j in 0..du {
201 if i == j {
202 out[i][j] = (c * x[i]).sin().powi(2) * f1 / d.powi(2)
203 - (c * x[i]).cos() * f1 / d
204 - if norm <= eps {
205 n0
206 } else {
207 x[i].powi(2) * (f2 + f3) - f4
208 };
209 } else {
210 let tmp = (c * x[i]).sin() * (c * x[j]).sin() * f1 / d.powi(2)
211 + x[i] * x[j] * if norm <= eps { n0 } else { -f5 - f6 };
212 out[i][j] = tmp;
213 out[j][i] = tmp;
214 };
215 }
216 }
217
218 out
219}
220
221pub fn ackley_hessian_const<const N: usize, T>(param: &[T; N]) -> [[T; N]; N]
228where
229 T: Float + FromPrimitive + Sum,
230{
231 ackley_abc_hessian_const(
232 param,
233 T::from_f64(20.0).unwrap(),
234 T::from_f64(0.2).unwrap(),
235 T::from_f64(2.0 * PI).unwrap(),
236 )
237}
238
239pub fn ackley_abc_hessian_const<const N: usize, T>(param: &[T; N], a: T, b: T, c: T) -> [[T; N]; N]
243where
244 T: Float + FromPrimitive + Sum,
245{
246 assert!(N >= 1);
247 let d = T::from_usize(N).unwrap();
248 let n0 = T::from_f64(0.0).unwrap();
249 let eps = T::epsilon();
250
251 let x = param;
253
254 let sqsum = param.iter().map(|x| x.powi(2)).sum::<T>();
255 let norm = sqsum.sqrt();
256 let nexp = (-b * norm / d.sqrt()).exp();
257
258 let f1 = -c.powi(2) * (x.iter().map(|&x| (c * x).cos()).sum::<T>() / d).exp();
259 let f2 = (a * b.powi(2) * nexp) / (d * sqsum);
260 let f3 = (a * b * nexp) / (d.sqrt() * norm.powi(3));
261 let f4 = (a * b * nexp) / (d.sqrt() * norm);
262 let f5 = (a * b.powi(2) * nexp) / (d * sqsum);
263 let f6 = (a * b * nexp) / (d.sqrt() * norm.powi(3));
264
265 let mut out = [[n0; N]; N];
266 for i in 0..N {
267 for j in 0..N {
268 if i == j {
269 out[i][j] = (c * x[i]).sin().powi(2) * f1 / d.powi(2)
270 - (c * x[i]).cos() * f1 / d
271 - if norm <= eps {
272 n0
273 } else {
274 x[i].powi(2) * (f2 + f3) - f4
275 };
276 } else {
277 let tmp = (c * x[i]).sin() * (c * x[j]).sin() * f1 / d.powi(2)
278 + x[i] * x[j] * if norm <= eps { n0 } else { -f5 - f6 };
279 out[i][j] = tmp;
280 out[j][i] = tmp;
281 };
282 }
283 }
284
285 out
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use approx::assert_relative_eq;
292 use finitediff::FiniteDiff;
293 use proptest::prelude::*;
294 use std::{f32, f64};
295
296 #[test]
297 fn test_ackley_optimum() {
298 assert_relative_eq!(
299 ackley(&[0.0_f32, 0.0_f32, 0.0_f32]),
300 0.0,
301 epsilon = f32::EPSILON * 10_f32
302 );
303 assert_relative_eq!(
304 ackley(&[0.0_f64, 0.0_f64, 0.0_f64]),
305 0.0,
306 epsilon = f64::EPSILON * 5_f64
307 );
308
309 let deriv = ackley_derivative(&[0.0_f64, 0.0_f64, 0.0_f64]);
310 for i in 0..3 {
311 assert_relative_eq!(deriv[i], 0.0, epsilon = f64::EPSILON * 3_f64);
312 }
313
314 let deriv = ackley_derivative_const(&[0.0_f64, 0.0_f64, 0.0_f64]);
315 for i in 0..3 {
316 assert_relative_eq!(deriv[i], 0.0, epsilon = f64::EPSILON * 3_f64);
317 }
318 }
319
320 proptest! {
321 #[test]
322 fn test_parameters(a in -5.0..5.0, b in -5.0..5.0, c in -5.0..5.0) {
323 let param = [a, b, c];
324 assert_relative_eq!(
325 ackley(¶m),
326 ackley_abc(¶m, 20.0, 0.2, 2.0 * PI),
327 epsilon = f64::EPSILON
328 );
329
330 let deriv1 = ackley_derivative(¶m);
331 let deriv2 = ackley_abc_derivative(¶m, 20.0, 0.2, 2.0 * PI);
332 for i in 0..3 {
333 assert_relative_eq!(deriv1[i], deriv2[i], epsilon = f64::EPSILON);
334 }
335
336 let deriv1 = ackley_derivative_const(¶m);
337 let deriv2 = ackley_abc_derivative_const(¶m, 20.0, 0.2, 2.0 * PI);
338 for i in 0..3 {
339 assert_relative_eq!(deriv1[i], deriv2[i], epsilon = f64::EPSILON);
340 }
341
342 let hessian1 = ackley_hessian(¶m);
343 let hessian2 = ackley_abc_hessian(¶m, 20.0, 0.2, 2.0 * PI);
344 for i in 0..3 {
345 for j in 0..3 {
346 assert_relative_eq!(hessian1[i][j], hessian2[i][j], epsilon = f64::EPSILON);
347 }
348 }
349
350 let hessian1 = ackley_hessian_const(¶m);
351 let hessian2 = ackley_abc_hessian_const(¶m, 20.0, 0.2, 2.0 * PI);
352 for i in 0..3 {
353 for j in 0..3 {
354 assert_relative_eq!(hessian1[i][j], hessian2[i][j], epsilon = f64::EPSILON);
355 }
356 }
357 }
358 }
359
360 proptest! {
361 #[test]
362 fn test_ackley_derivative_finitediff(a in -5.0..5.0,
363 b in -5.0..5.0,
364 c in -5.0..5.0,
365 d in -5.0..5.0,
366 e in -5.0..5.0,
367 f in -5.0..5.0,
368 g in -5.0..5.0,
369 h in -5.0..5.0) {
370 let param = [a, b, c, d, e, f, g, h];
371 let derivative = ackley_derivative(¶m);
372 let derivative_fd = Vec::from(param).central_diff(&|x| ackley(&x));
373 for i in 0..derivative.len() {
376 assert_relative_eq!(
377 derivative[i],
378 derivative_fd[i],
379 epsilon = 1e-5,
380 max_relative = 1e-2
381 );
382 }
383 }
384 }
385
386 proptest! {
387 #[test]
388 fn test_ackley_derivative_const_finitediff(a in -5.0..5.0,
389 b in -5.0..5.0,
390 c in -5.0..5.0,
391 d in -5.0..5.0,
392 e in -5.0..5.0,
393 f in -5.0..5.0,
394 g in -5.0..5.0,
395 h in -5.0..5.0) {
396 let param = [a, b, c, d, e, f, g, h];
397 let derivative = ackley_derivative_const(¶m);
398 let derivative_fd = Vec::from(param).central_diff(&|x| ackley(&x));
399 for i in 0..derivative.len() {
402 assert_relative_eq!(
403 derivative[i],
404 derivative_fd[i],
405 epsilon = 1e-5,
406 max_relative = 1e-2
407 );
408 }
409 }
410 }
411
412 proptest! {
413 #[test]
414 fn test_ackley_hessian_finitediff(a in -5.0..5.0,
415 b in -5.0..5.0,
416 c in -5.0..5.0,
417 d in -5.0..5.0,
418 e in -5.0..5.0,
419 f in -5.0..5.0,
420 g in -5.0..5.0,
421 h in -5.0..5.0) {
422 let param = [a, b, c, d, e, f, g, h];
423 let hessian = ackley_hessian(¶m);
424 let hessian_fd = Vec::from(param).central_hessian(&|x| ackley_derivative(&x));
425 for i in 0..hessian.len() {
428 for j in 0..hessian.len() {
429 assert_relative_eq!(
430 hessian[i][j],
431 hessian_fd[i][j],
432 epsilon = 1e-5,
433 max_relative = 1e-2
434 );
435 }
436 }
437 }
438 }
439
440 proptest! {
441 #[test]
442 fn test_ackley_hessian_const_finitediff(a in -5.0..5.0,
443 b in -5.0..5.0,
444 c in -5.0..5.0,
445 d in -5.0..5.0,
446 e in -5.0..5.0,
447 f in -5.0..5.0,
448 g in -5.0..5.0,
449 h in -5.0..5.0) {
450 let param = [a, b, c, d, e, f, g, h];
451 let hessian = ackley_hessian_const(¶m);
452 let hessian_fd = Vec::from(param).central_hessian(&|x| ackley_derivative(&x));
453 for i in 0..hessian.len() {
456 for j in 0..hessian.len() {
457 assert_relative_eq!(
458 hessian[i][j],
459 hessian_fd[i][j],
460 epsilon = 1e-5,
461 max_relative = 1e-2
462 );
463 }
464 }
465 }
466 }
467}