1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! # Firefly Algorithm
//!
//! <https://en.wikipedia.org/wiki/Firefly_algorithm>
//!
//! This method require exponential function.
use crate::prelude::*;
use alloc::vec::Vec;
use core::iter::zip;

/// Algorithm of the Firefly Algorithm.
pub type Method = Fa;

const DEF: Fa = Fa { alpha: 1., beta_min: 1., gamma: 0.01 };

/// Firefly Algorithm settings.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Fa {
    /// Alpha factor
    #[cfg_attr(feature = "clap", clap(long, default_value_t = DEF.alpha))]
    pub alpha: f64,
    /// Min beta value
    #[cfg_attr(feature = "clap", clap(long, default_value_t = DEF.beta_min))]
    pub beta_min: f64,
    /// Gamma factor
    #[cfg_attr(feature = "clap", clap(long, default_value_t = DEF.gamma))]
    pub gamma: f64,
}

impl Fa {
    /// Constant default value.
    pub const fn new() -> Self {
        DEF
    }

    impl_builders! {
        /// Alpha factor.
        fn alpha(f64)
        /// Minimum beta factor.
        fn beta_min(f64)
        /// Gamma factor.
        fn gamma(f64)
    }
}

impl Default for Fa {
    fn default() -> Self {
        DEF
    }
}

impl AlgCfg for Fa {
    type Algorithm<F: ObjFunc> = Method;
    fn algorithm<F: ObjFunc>(self) -> Self::Algorithm<F> {
        self
    }
    fn pop_num() -> usize {
        80
    }
}

impl Method {
    fn move_firefly<F: ObjFunc>(
        &self,
        ctx: &Ctx<F>,
        rng: &mut Rng,
        i: usize,
        j: usize,
    ) -> (Vec<f64>, F::Ys) {
        let (i, j) = if ctx.pool_y[j].is_dominated(&ctx.pool_y[i]) {
            (i, j)
        } else {
            (j, i)
        };
        let r = zip(&ctx.pool[i], &ctx.pool[j])
            .map(|(a, b)| a - b)
            .fold(0., |acc, x| acc + x * x);
        let beta = self.beta_min * (-self.gamma * r).exp();
        let xs = zip(ctx.bound(), zip(&ctx.pool[i], &ctx.pool[j]))
            .map(|(&[min, max], (a, b))| {
                let step = self.alpha * (max - min) * rng.range(-0.5..0.5);
                let surround = a + beta * (b - a);
                (surround + step).clamp(min, max)
            })
            .collect::<Vec<_>>();
        let ys = ctx.fitness(&xs);
        (xs, ys)
    }
}

impl<F: ObjFunc> Algorithm<F> for Method {
    fn generation(&mut self, ctx: &mut Ctx<F>, rng: &mut Rng) {
        // Move fireflies
        let mut pool = ctx.pool.clone();
        let mut pool_y = ctx.pool_y.clone();
        let rng = rng.stream(ctx.pop_num());
        #[cfg(not(feature = "rayon"))]
        let iter = rng.into_iter();
        #[cfg(feature = "rayon")]
        let iter = rng.into_par_iter();
        iter.zip(&mut pool)
            .zip(&mut pool_y)
            .enumerate()
            .for_each(|(i, ((mut rng, xs), ys))| {
                for j in i + 1..ctx.pop_num() {
                    let (xs_new, ys_new) = self.move_firefly(ctx, &mut rng, i, j);
                    if ys_new.is_dominated(ys) {
                        *xs = xs_new;
                        *ys = ys_new;
                    }
                }
            });
        ctx.pool = pool;
        ctx.pool_y = pool_y;
        ctx.find_best();
        self.alpha *= 0.95;
    }
}