graphmind_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 {
25 pop_size * dim / 2
26 } else {
27 self.limit
28 };
29
30 let mut foods: Vec<Individual> = (0..pop_size)
32 .map(|_| {
33 let mut vars = Array1::zeros(dim);
34 for i in 0..dim {
35 vars[i] = rng.gen_range(lower[i]..upper[i]);
36 }
37 let fitness = problem.fitness(&vars);
38 Individual::new(vars, fitness)
39 })
40 .collect();
41
42 let mut trial_counters = vec![0; pop_size];
43
44 let mut best_idx = 0;
45 for i in 1..pop_size {
46 if foods[i].fitness < foods[best_idx].fitness {
47 best_idx = i;
48 }
49 }
50 let mut best_vars = foods[best_idx].variables.clone();
51 let mut best_fitness = foods[best_idx].fitness;
52
53 let mut history = Vec::with_capacity(self.config.max_iterations);
54
55 for _iter in 0..self.config.max_iterations {
56 history.push(best_fitness);
57
58 for i in 0..pop_size {
60 let mut new_vars = foods[i].variables.clone();
61 let j = rng.gen_range(0..dim);
62 let mut k;
63 loop {
64 k = rng.gen_range(0..pop_size);
65 if k != i {
66 break;
67 }
68 }
69
70 let phi: f64 = rng.gen_range(-1.0..1.0);
71 new_vars[j] = (foods[i].variables[j]
72 + phi * (foods[i].variables[j] - foods[k].variables[j]))
73 .clamp(lower[j], upper[j]);
74
75 let new_fitness = problem.fitness(&new_vars);
76 if new_fitness < foods[i].fitness {
77 foods[i] = Individual::new(new_vars, new_fitness);
78 trial_counters[i] = 0;
79 } else {
80 trial_counters[i] += 1;
81 }
82 }
83
84 let mut probs = vec![0.0; pop_size];
88 let mut total_f = 0.0;
89 for i in 0..pop_size {
90 let f_val = if foods[i].fitness >= 0.0 {
91 1.0 / (1.0 + foods[i].fitness)
92 } else {
93 1.0 + foods[i].fitness.abs()
94 };
95 probs[i] = f_val;
96 total_f += f_val;
97 }
98 for prob in probs.iter_mut() {
99 *prob /= total_f;
100 }
101
102 let mut m = 0;
103 let mut t = 0;
104 while m < pop_size {
105 if rng.gen::<f64>() < probs[t] {
106 m += 1;
107 let i = t;
108 let mut new_vars = foods[i].variables.clone();
109 let j = rng.gen_range(0..dim);
110 let mut k;
111 loop {
112 k = rng.gen_range(0..pop_size);
113 if k != i {
114 break;
115 }
116 }
117
118 let phi: f64 = rng.gen_range(-1.0..1.0);
119 new_vars[j] = (foods[i].variables[j]
120 + phi * (foods[i].variables[j] - foods[k].variables[j]))
121 .clamp(lower[j], upper[j]);
122
123 let new_fitness = problem.fitness(&new_vars);
124 if new_fitness < foods[i].fitness {
125 foods[i] = Individual::new(new_vars, new_fitness);
126 trial_counters[i] = 0;
127 } else {
128 trial_counters[i] += 1;
129 }
130 }
131 t = (t + 1) % pop_size;
132 }
133
134 for i in 0..pop_size {
136 if trial_counters[i] > limit {
137 let mut vars = Array1::zeros(dim);
139 for j in 0..dim {
140 vars[j] = rng.gen_range(lower[j]..upper[j]);
141 }
142 let fitness = problem.fitness(&vars);
143 foods[i] = Individual::new(vars, fitness);
144 trial_counters[i] = 0;
145 }
146 }
147
148 for food in foods.iter() {
150 if food.fitness < best_fitness {
151 best_fitness = food.fitness;
152 best_vars = food.variables.clone();
153 }
154 }
155 }
156
157 OptimizationResult {
158 best_variables: best_vars,
159 best_fitness,
160 history,
161 }
162 }
163}