graphmind_optimization/algorithms/
bat.rs1use crate::common::{Individual, OptimizationResult, Problem, SolverConfig};
2use ndarray::Array1;
3use rand::prelude::*;
4
5pub struct BatSolver {
6 pub config: SolverConfig,
7 pub f_min: f64,
8 pub f_max: f64,
9 pub alpha: f64, pub gamma: f64, }
12
13impl BatSolver {
14 pub fn new(config: SolverConfig) -> Self {
15 Self {
16 config,
17 f_min: 0.0,
18 f_max: 2.0,
19 alpha: 0.9,
20 gamma: 0.9,
21 }
22 }
23
24 pub fn solve<P: Problem>(&self, problem: &P) -> OptimizationResult {
25 let mut rng = thread_rng();
26 let dim = problem.dim();
27 let (lower, upper) = problem.bounds();
28
29 let mut population: Vec<Individual> = (0..self.config.population_size)
31 .map(|_| {
32 let mut vars = Array1::zeros(dim);
33 for i in 0..dim {
34 vars[i] = rng.gen_range(lower[i]..upper[i]);
35 }
36 let fitness = problem.fitness(&vars);
37 Individual::new(vars, fitness)
38 })
39 .collect();
40
41 let mut velocities: Vec<Array1<f64>> = (0..self.config.population_size)
42 .map(|_| Array1::zeros(dim))
43 .collect();
44
45 let mut frequencies = vec![0.0; self.config.population_size];
46 let mut loudnesses = vec![1.0; self.config.population_size];
47 let mut emission_rates = vec![0.5; self.config.population_size];
48 let r0 = 0.5; let mut best_idx = 0;
52 for i in 1..population.len() {
53 if population[i].fitness < population[best_idx].fitness {
54 best_idx = i;
55 }
56 }
57 let mut best_vars = population[best_idx].variables.clone();
58 let mut best_fitness = population[best_idx].fitness;
59
60 let mut history = Vec::with_capacity(self.config.max_iterations);
61
62 for iter in 0..self.config.max_iterations {
63 history.push(best_fitness);
64
65 for i in 0..self.config.population_size {
66 let beta: f64 = rng.gen();
68 frequencies[i] = self.f_min + (self.f_max - self.f_min) * beta;
69
70 for j in 0..dim {
72 velocities[i][j] +=
73 (population[i].variables[j] - best_vars[j]) * frequencies[i];
74 population[i].variables[j] =
75 (population[i].variables[j] + velocities[i][j]).clamp(lower[j], upper[j]);
76 }
77
78 if rng.gen::<f64>() > emission_rates[i] {
80 let mut temp_vars = best_vars.clone();
81 let avg_loudness: f64 =
82 loudnesses.iter().sum::<f64>() / (self.config.population_size as f64);
83 for j in 0..dim {
84 let epsilon = (rng.gen::<f64>() - 0.5) * 2.0;
85 temp_vars[j] =
86 (temp_vars[j] + epsilon * avg_loudness).clamp(lower[j], upper[j]);
87 }
88
89 let temp_fitness = problem.fitness(&temp_vars);
90
91 if temp_fitness < population[i].fitness && rng.gen::<f64>() < loudnesses[i] {
93 population[i].variables = temp_vars;
94 population[i].fitness = temp_fitness;
95
96 loudnesses[i] *= self.alpha;
98 emission_rates[i] = r0 * (1.0 - (-self.gamma * (iter as f64)).exp());
99 }
100 } else {
101 population[i].fitness = problem.fitness(&population[i].variables);
102 }
103
104 if population[i].fitness < best_fitness {
106 best_vars = population[i].variables.clone();
107 best_fitness = population[i].fitness;
108 }
109 }
110 }
111
112 OptimizationResult {
113 best_variables: best_vars,
114 best_fitness,
115 history,
116 }
117 }
118}