metaheurustics/algorithm/
fa.rs

1use ndarray::Array1;
2use rand::Rng;
3use std::f64;
4
5pub struct FireflyAlgorithm {
6    pub population_size: usize,
7    pub max_iterations: usize,
8    pub alpha: f64,  // light absorption coefficient
9    pub beta0: f64,  // attractiveness at distance 0
10    pub gamma: f64,  // light absorption coefficient
11}
12
13impl Default for FireflyAlgorithm {
14    fn default() -> Self {
15        FireflyAlgorithm {
16            population_size: 30,
17            max_iterations: 1000,
18            alpha: 0.2,
19            beta0: 1.0,
20            gamma: 1.0,
21        }
22    }
23}
24
25impl FireflyAlgorithm {
26    pub fn new(population_size: usize, max_iterations: usize) -> Self {
27        FireflyAlgorithm {
28            population_size,
29            max_iterations,
30            ..Default::default()
31        }
32    }
33
34    pub fn optimize<F>(&self, objective_fn: F, bounds: &[(f64, f64)]) -> Array1<f64>
35    where
36        F: Fn(&Array1<f64>) -> f64,
37    {
38        let dim = bounds.len();
39        let mut rng = rand::thread_rng();
40        
41        // Initialize population
42        let mut population: Vec<(Array1<f64>, f64)> = (0..self.population_size)
43            .map(|_| {
44                let position = Array1::from_vec(
45                    bounds.iter()
46                        .map(|(min, max)| rng.gen_range(*min..*max))
47                        .collect()
48                );
49                let fitness = objective_fn(&position);
50                (position, fitness)
51            })
52            .collect();
53
54        let mut best_solution = population
55            .iter()
56            .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
57            .unwrap()
58            .clone();
59
60        // Main iteration loop
61        for _ in 0..self.max_iterations {
62            // For each firefly
63            for i in 0..self.population_size {
64                // Compare with all other fireflies
65                for j in 0..self.population_size {
66                    if population[j].1 < population[i].1 {
67                        // Calculate distance
68                        let r = (&population[i].0 - &population[j].0)
69                            .mapv(|x| x * x)
70                            .sum()
71                            .sqrt();
72
73                        // Calculate attractiveness
74                        let beta = self.beta0 * (-self.gamma * r * r).exp();
75
76                        // Update position
77                        let mut new_position = Array1::zeros(dim);
78                        for d in 0..dim {
79                            let rand_step = rng.gen_range(-0.5..0.5);
80                            new_position[d] = population[i].0[d] +
81                                beta * (population[j].0[d] - population[i].0[d]) +
82                                self.alpha * rand_step;
83                            
84                            // Bound checking
85                            if new_position[d] < bounds[d].0 {
86                                new_position[d] = bounds[d].0;
87                            }
88                            if new_position[d] > bounds[d].1 {
89                                new_position[d] = bounds[d].1;
90                            }
91                        }
92
93                        let new_fitness = objective_fn(&new_position);
94                        if new_fitness < population[i].1 {
95                            population[i] = (new_position, new_fitness);
96                        }
97                    }
98                }
99            }
100
101            // Update best solution
102            if let Some(current_best) = population
103                .iter()
104                .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
105            {
106                if current_best.1 < best_solution.1 {
107                    best_solution = current_best.clone();
108                }
109            }
110        }
111
112        best_solution.0
113    }
114}