samyama_optimization/algorithms/
abc.rs1use crate::common::{Individual, OptimizationResult, Problem, SolverConfig};
2use ndarray::Array1;
3use rand::prelude::*;
4
5pub struct ABCSolver {
6 pub config: SolverConfig,
7 pub limit: usize, }
9
10impl ABCSolver {
11 pub fn new(config: SolverConfig) -> Self {
12 Self {
14 config,
15 limit: 100, }
17 }
18
19 pub fn solve<P: Problem>(&self, problem: &P) -> OptimizationResult {
20 let mut rng = thread_rng();
21 let dim = problem.dim();
22 let (lower, upper) = problem.bounds();
23 let pop_size = self.config.population_size;
24 let limit = if self.limit == 100 { pop_size * dim / 2 } else { self.limit };
25
26 let mut foods: Vec<Individual> = (0..pop_size)
28 .map(|_| {
29 let mut vars = Array1::zeros(dim);
30 for i in 0..dim {
31 vars[i] = rng.gen_range(lower[i]..upper[i]);
32 }
33 let fitness = problem.fitness(&vars);
34 Individual::new(vars, fitness)
35 })
36 .collect();
37
38 let mut trial_counters = vec![0; pop_size];
39
40 let mut best_idx = 0;
41 for i in 1..pop_size {
42 if foods[i].fitness < foods[best_idx].fitness {
43 best_idx = i;
44 }
45 }
46 let mut best_vars = foods[best_idx].variables.clone();
47 let mut best_fitness = foods[best_idx].fitness;
48
49 let mut history = Vec::with_capacity(self.config.max_iterations);
50
51 for _iter in 0..self.config.max_iterations {
52 history.push(best_fitness);
53
54 for i in 0..pop_size {
56 let mut new_vars = foods[i].variables.clone();
57 let j = rng.gen_range(0..dim);
58 let mut k;
59 loop {
60 k = rng.gen_range(0..pop_size);
61 if k != i { break; }
62 }
63
64 let phi: f64 = rng.gen_range(-1.0..1.0);
65 new_vars[j] = (foods[i].variables[j] + phi * (foods[i].variables[j] - foods[k].variables[j])).clamp(lower[j], upper[j]);
66
67 let new_fitness = problem.fitness(&new_vars);
68 if new_fitness < foods[i].fitness {
69 foods[i] = Individual::new(new_vars, new_fitness);
70 trial_counters[i] = 0;
71 } else {
72 trial_counters[i] += 1;
73 }
74 }
75
76 let mut probs = vec![0.0; pop_size];
80 let mut total_f = 0.0;
81 for i in 0..pop_size {
82 let f_val = if foods[i].fitness >= 0.0 {
83 1.0 / (1.0 + foods[i].fitness)
84 } else {
85 1.0 + foods[i].fitness.abs()
86 };
87 probs[i] = f_val;
88 total_f += f_val;
89 }
90 for i in 0..pop_size {
91 probs[i] /= total_f;
92 }
93
94 let mut m = 0;
95 let mut t = 0;
96 while m < pop_size {
97 if rng.gen::<f64>() < probs[t] {
98 m += 1;
99 let i = t;
100 let mut new_vars = foods[i].variables.clone();
101 let j = rng.gen_range(0..dim);
102 let mut k;
103 loop {
104 k = rng.gen_range(0..pop_size);
105 if k != i { break; }
106 }
107
108 let phi: f64 = rng.gen_range(-1.0..1.0);
109 new_vars[j] = (foods[i].variables[j] + phi * (foods[i].variables[j] - foods[k].variables[j])).clamp(lower[j], upper[j]);
110
111 let new_fitness = problem.fitness(&new_vars);
112 if new_fitness < foods[i].fitness {
113 foods[i] = Individual::new(new_vars, new_fitness);
114 trial_counters[i] = 0;
115 } else {
116 trial_counters[i] += 1;
117 }
118 }
119 t = (t + 1) % pop_size;
120 }
121
122 for i in 0..pop_size {
124 if trial_counters[i] > limit {
125 let mut vars = Array1::zeros(dim);
127 for j in 0..dim {
128 vars[j] = rng.gen_range(lower[j]..upper[j]);
129 }
130 let fitness = problem.fitness(&vars);
131 foods[i] = Individual::new(vars, fitness);
132 trial_counters[i] = 0;
133 }
134 }
135
136 for i in 0..pop_size {
138 if foods[i].fitness < best_fitness {
139 best_fitness = foods[i].fitness;
140 best_vars = foods[i].variables.clone();
141 }
142 }
143 }
144
145 OptimizationResult {
146 best_variables: best_vars,
147 best_fitness,
148 history,
149 }
150 }
151}