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
//! # Firefly Algorithm
//!
//! <https://en.wikipedia.org/wiki/Firefly_algorithm>
//!
//! This method require exponential function.
use crate::utility::prelude::*;

/// Firefly Algorithm type.
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 Setting for Fa {
    type Algorithm<F: ObjFunc> = Method;

    fn algorithm<F: ObjFunc>(self) -> Self::Algorithm<F> {
        self
    }

    fn default_pop() -> usize {
        80
    }
}

impl Method {
    fn move_firefly<F: ObjFunc>(
        &self,
        ctx: &Ctx<F>,
        rng: &Rng,
        i: usize,
        j: usize,
    ) -> (Array1<f64>, F::Fitness) {
        let (i, j) = if ctx.pool_f[i] > ctx.pool_f[j] {
            (i, j)
        } else {
            (j, i)
        };
        let mut v = Array1::zeros(ctx.dim());
        let r = (&ctx.pool.slice(s![i, ..]) - &ctx.pool.slice(s![j, ..]))
            .mapv(|v| v * v)
            .sum();
        let beta = self.beta_min * (-self.gamma * r).exp();
        for s in 0..ctx.dim() {
            let step = self.alpha * ctx.func.bound_width(s) * rng.range(-0.5..0.5);
            let surround = ctx.pool[[i, s]] + beta * (ctx.pool[[j, s]] - ctx.pool[[i, s]]);
            v[s] = ctx.clamp(s, surround + step);
        }
        let f = ctx.func.fitness(v.as_slice().unwrap());
        (v, f)
    }
}

impl<F: ObjFunc> Algorithm<F> for Method {
    fn generation(&mut self, ctx: &mut Ctx<F>, rng: &Rng) {
        // Move fireflies
        let mut fitness = ctx.pool_f.clone();
        let mut pool = ctx.pool.clone();
        #[cfg(feature = "rayon")]
        let iter = fitness.par_iter_mut();
        #[cfg(not(feature = "rayon"))]
        let iter = fitness.iter_mut();
        iter.zip(pool.axis_iter_mut(Axis(0)))
            .zip(rng.stream(ctx.pop_num()))
            .enumerate()
            .for_each(|(i, ((fitness, mut pool), rng))| {
                for j in i + 1..ctx.pop_num() {
                    let (v, f) = self.move_firefly(ctx, &rng, i, j);
                    if f < *fitness {
                        *fitness = f;
                        pool.assign(&v);
                    }
                }
            });
        ctx.pool_f = fitness;
        ctx.pool = pool;
        self.alpha *= 0.95;
        ctx.find_best();
    }
}