samyama_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] += (population[i].variables[j] - best_vars[j]) * frequencies[i];
73 population[i].variables[j] = (population[i].variables[j] + velocities[i][j]).clamp(lower[j], upper[j]);
74 }
75
76 if rng.gen::<f64>() > emission_rates[i] {
78 let mut temp_vars = best_vars.clone();
79 let avg_loudness: f64 = loudnesses.iter().sum::<f64>() / (self.config.population_size as f64);
80 for j in 0..dim {
81 let epsilon = (rng.gen::<f64>() - 0.5) * 2.0;
82 temp_vars[j] = (temp_vars[j] + epsilon * avg_loudness).clamp(lower[j], upper[j]);
83 }
84
85 let temp_fitness = problem.fitness(&temp_vars);
86
87 if temp_fitness < population[i].fitness && rng.gen::<f64>() < loudnesses[i] {
89 population[i].variables = temp_vars;
90 population[i].fitness = temp_fitness;
91
92 loudnesses[i] *= self.alpha;
94 emission_rates[i] = r0 * (1.0 - (-self.gamma * (iter as f64)).exp());
95 }
96 } else {
97 population[i].fitness = problem.fitness(&population[i].variables);
98 }
99
100 if population[i].fitness < best_fitness {
102 best_vars = population[i].variables.clone();
103 best_fitness = population[i].fitness;
104 }
105 }
106 }
107
108 OptimizationResult {
109 best_variables: best_vars,
110 best_fitness,
111 history,
112 }
113 }
114}