sciforge_lib/maths/optimization/
evolutionary.rs1pub fn genetic_algorithm(
2 fitness: fn(&[f64]) -> f64,
3 bounds: &[(f64, f64)],
4 pop_size: usize,
5 generations: usize,
6 mutation_rate: f64,
7 seed: u64,
8) -> Vec<f64> {
9 let n = bounds.len();
10 let mut rng = LcgRng::new(seed);
11 let mut population: Vec<Vec<f64>> = (0..pop_size)
12 .map(|_| {
13 (0..n)
14 .map(|j| bounds[j].0 + rng.next_f64() * (bounds[j].1 - bounds[j].0))
15 .collect()
16 })
17 .collect();
18 for _ in 0..generations {
19 let mut scored: Vec<(f64, usize)> = population
20 .iter()
21 .enumerate()
22 .map(|(i, ind)| (fitness(ind), i))
23 .collect();
24 scored.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
25 let elite_count = pop_size / 5;
26 let mut new_pop: Vec<Vec<f64>> = scored
27 .iter()
28 .take(elite_count)
29 .map(|&(_, i)| population[i].clone())
30 .collect();
31 while new_pop.len() < pop_size {
32 let p1 = &population[scored[rng.next_usize(elite_count)].1];
33 let p2 = &population[scored[rng.next_usize(elite_count)].1];
34 let child: Vec<f64> = (0..n)
35 .map(|j| {
36 let val = if rng.next_f64() < 0.5 { p1[j] } else { p2[j] };
37 if rng.next_f64() < mutation_rate {
38 let range = bounds[j].1 - bounds[j].0;
39 (val + (rng.next_f64() - 0.5) * range * 0.1).clamp(bounds[j].0, bounds[j].1)
40 } else {
41 val
42 }
43 })
44 .collect();
45 new_pop.push(child);
46 }
47 population = new_pop;
48 }
49 population
50 .iter()
51 .max_by(|a, b| {
52 fitness(a)
53 .partial_cmp(&fitness(b))
54 .unwrap_or(std::cmp::Ordering::Equal)
55 })
56 .unwrap()
57 .clone()
58}
59
60pub fn differential_evolution(
61 objective: fn(&[f64]) -> f64,
62 bounds: &[(f64, f64)],
63 pop_size: usize,
64 generations: usize,
65 f_weight: f64,
66 cr: f64,
67 seed: u64,
68) -> Vec<f64> {
69 let n = bounds.len();
70 let mut rng = LcgRng::new(seed);
71 let mut population: Vec<Vec<f64>> = (0..pop_size)
72 .map(|_| {
73 (0..n)
74 .map(|j| bounds[j].0 + rng.next_f64() * (bounds[j].1 - bounds[j].0))
75 .collect()
76 })
77 .collect();
78 let mut fitness: Vec<f64> = population.iter().map(|ind| objective(ind)).collect();
79 for _ in 0..generations {
80 for i in 0..pop_size {
81 let mut idxs = [0usize; 3];
82 for slot in &mut idxs {
83 loop {
84 let v = rng.next_usize(pop_size);
85 if v != i {
86 *slot = v;
87 break;
88 }
89 }
90 }
91 let j_rand = rng.next_usize(n);
92 let trial: Vec<f64> = (0..n)
93 .map(|j| {
94 if rng.next_f64() < cr || j == j_rand {
95 let v = population[idxs[0]][j]
96 + f_weight * (population[idxs[1]][j] - population[idxs[2]][j]);
97 v.clamp(bounds[j].0, bounds[j].1)
98 } else {
99 population[i][j]
100 }
101 })
102 .collect();
103 let trial_f = objective(&trial);
104 if trial_f <= fitness[i] {
105 population[i] = trial;
106 fitness[i] = trial_f;
107 }
108 }
109 }
110 let best = fitness
111 .iter()
112 .enumerate()
113 .min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
114 .unwrap()
115 .0;
116 population[best].clone()
117}
118
119struct LcgRng {
120 state: u64,
121}
122impl LcgRng {
123 fn new(seed: u64) -> Self {
124 Self { state: seed }
125 }
126 fn next_u64(&mut self) -> u64 {
127 self.state = self
128 .state
129 .wrapping_mul(6364136223846793005)
130 .wrapping_add(1442695040888963407);
131 self.state
132 }
133 fn next_f64(&mut self) -> f64 {
134 (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
135 }
136 fn next_usize(&mut self, max: usize) -> usize {
137 (self.next_f64() * max as f64) as usize % max
138 }
139}
140
141pub fn evolution_strategy(
142 objective: fn(&[f64]) -> f64,
143 bounds: &[(f64, f64)],
144 mu: usize,
145 lambda: usize,
146 sigma: f64,
147 generations: usize,
148 seed: u64,
149) -> Vec<f64> {
150 let n = bounds.len();
151 let mut rng = LcgRng::new(seed);
152 let mut population: Vec<Vec<f64>> = (0..mu)
153 .map(|_| {
154 (0..n)
155 .map(|j| bounds[j].0 + rng.next_f64() * (bounds[j].1 - bounds[j].0))
156 .collect()
157 })
158 .collect();
159 for _ in 0..generations {
160 let mut offspring = Vec::with_capacity(lambda);
161 for _ in 0..lambda {
162 let parent_idx = rng.next_usize(mu);
163 let child: Vec<f64> = (0..n)
164 .map(|j| {
165 let noise = (rng.next_f64() - 0.5) * 2.0 * sigma;
166 (population[parent_idx][j] + noise).clamp(bounds[j].0, bounds[j].1)
167 })
168 .collect();
169 offspring.push(child);
170 }
171 offspring.sort_by(|a, b| {
172 objective(a)
173 .partial_cmp(&objective(b))
174 .unwrap_or(std::cmp::Ordering::Equal)
175 });
176 population = offspring.into_iter().take(mu).collect();
177 }
178 population
179 .into_iter()
180 .min_by(|a, b| {
181 objective(a)
182 .partial_cmp(&objective(b))
183 .unwrap_or(std::cmp::Ordering::Equal)
184 })
185 .unwrap()
186}
187
188pub fn scatter_search(
189 objective: fn(&[f64]) -> f64,
190 bounds: &[(f64, f64)],
191 pop_size: usize,
192 ref_size: usize,
193 generations: usize,
194 seed: u64,
195) -> Vec<f64> {
196 let n = bounds.len();
197 let mut rng = LcgRng::new(seed);
198 let mut pop: Vec<Vec<f64>> = (0..pop_size)
199 .map(|_| {
200 (0..n)
201 .map(|j| bounds[j].0 + rng.next_f64() * (bounds[j].1 - bounds[j].0))
202 .collect()
203 })
204 .collect();
205 for _ in 0..generations {
206 pop.sort_by(|a, b| {
207 objective(a)
208 .partial_cmp(&objective(b))
209 .unwrap_or(std::cmp::Ordering::Equal)
210 });
211 let ref_set: Vec<Vec<f64>> = pop.iter().take(ref_size).cloned().collect();
212 let mut new_pop = ref_set.clone();
213 for i in 0..ref_size {
214 for j in (i + 1)..ref_size {
215 let child: Vec<f64> = (0..n)
216 .map(|k| {
217 let alpha = rng.next_f64();
218 (alpha * ref_set[i][k] + (1.0 - alpha) * ref_set[j][k])
219 .clamp(bounds[k].0, bounds[k].1)
220 })
221 .collect();
222 new_pop.push(child);
223 }
224 }
225 new_pop.sort_by(|a, b| {
226 objective(a)
227 .partial_cmp(&objective(b))
228 .unwrap_or(std::cmp::Ordering::Equal)
229 });
230 pop = new_pop.into_iter().take(pop_size).collect();
231 }
232 pop.into_iter()
233 .min_by(|a, b| {
234 objective(a)
235 .partial_cmp(&objective(b))
236 .unwrap_or(std::cmp::Ordering::Equal)
237 })
238 .unwrap()
239}
240
241pub fn multi_objective_dominates(a: &[f64], b: &[f64]) -> bool {
242 let mut at_least_one_better = false;
243 for (ai, bi) in a.iter().zip(b.iter()) {
244 if ai > bi {
245 return false;
246 }
247 if ai < bi {
248 at_least_one_better = true;
249 }
250 }
251 at_least_one_better
252}
253
254pub fn crowding_distance(objectives: &[Vec<f64>]) -> Vec<f64> {
255 let n = objectives.len();
256 if n == 0 {
257 return Vec::new();
258 }
259 let m = objectives[0].len();
260 let mut dist = vec![0.0; n];
261 for obj_idx in 0..m {
262 let col: Vec<f64> = objectives.iter().map(|o| o[obj_idx]).collect();
263 let mut indices: Vec<usize> = (0..n).collect();
264 indices.sort_by(|&a, &b| {
265 col[a]
266 .partial_cmp(&col[b])
267 .unwrap_or(std::cmp::Ordering::Equal)
268 });
269 let range = col[indices[n - 1]] - col[indices[0]];
270 if range < 1e-30 {
271 continue;
272 }
273 dist[indices[0]] = f64::INFINITY;
274 dist[indices[n - 1]] = f64::INFINITY;
275 for i in 1..(n - 1) {
276 dist[indices[i]] += (col[indices[i + 1]] - col[indices[i - 1]]) / range;
277 }
278 }
279 dist
280}
281
282pub fn nsga2_non_dominated_sort(objectives: &[Vec<f64>]) -> Vec<Vec<usize>> {
283 let n = objectives.len();
284 let mut dominated_count = vec![0usize; n];
285 let mut dominates: Vec<Vec<usize>> = vec![Vec::new(); n];
286 for i in 0..n {
287 for j in 0..n {
288 if i == j {
289 continue;
290 }
291 if multi_objective_dominates(&objectives[i], &objectives[j]) {
292 dominates[i].push(j);
293 } else if multi_objective_dominates(&objectives[j], &objectives[i]) {
294 dominated_count[i] += 1;
295 }
296 }
297 }
298 let mut fronts = Vec::new();
299 let mut current_front: Vec<usize> = (0..n).filter(|&i| dominated_count[i] == 0).collect();
300 while !current_front.is_empty() {
301 let mut next_front = Vec::new();
302 for &i in ¤t_front {
303 for &j in &dominates[i] {
304 dominated_count[j] -= 1;
305 if dominated_count[j] == 0 {
306 next_front.push(j);
307 }
308 }
309 }
310 fronts.push(current_front);
311 current_front = next_front;
312 }
313 fronts
314}
315
316pub fn hypervolume_2d(points: &[(f64, f64)], ref_point: (f64, f64)) -> f64 {
317 let mut filtered: Vec<(f64, f64)> = points
318 .iter()
319 .filter(|p| p.0 < ref_point.0 && p.1 < ref_point.1)
320 .cloned()
321 .collect();
322 filtered.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
323 let mut vol = 0.0;
324 let mut last_y = ref_point.1;
325 for p in &filtered {
326 if p.1 < last_y {
327 vol += (ref_point.0 - p.0) * (last_y - p.1);
328 last_y = p.1;
329 }
330 }
331 vol
332}
333
334pub fn island_model(
335 objective: fn(&[f64]) -> f64,
336 bounds: &[(f64, f64)],
337 n_islands: usize,
338 pop_per_island: usize,
339 generations: usize,
340 migration_interval: usize,
341 seed: u64,
342) -> Vec<f64> {
343 let n = bounds.len();
344 let mut rng = LcgRng::new(seed);
345 let mut islands: Vec<Vec<Vec<f64>>> = (0..n_islands)
346 .map(|_| {
347 (0..pop_per_island)
348 .map(|_| {
349 (0..n)
350 .map(|j| bounds[j].0 + rng.next_f64() * (bounds[j].1 - bounds[j].0))
351 .collect()
352 })
353 .collect()
354 })
355 .collect();
356 for generation in 0..generations {
357 for island in islands.iter_mut() {
358 let mut scored: Vec<(f64, usize)> = island
359 .iter()
360 .enumerate()
361 .map(|(i, ind)| (objective(ind), i))
362 .collect();
363 scored.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
364 let best_idx = scored[0].1;
365 let best = island[best_idx].clone();
366 let worst_idx = scored.last().unwrap().1;
367 let mutant: Vec<f64> = (0..n)
368 .map(|j| {
369 (best[j] + (rng.next_f64() - 0.5) * (bounds[j].1 - bounds[j].0) * 0.1)
370 .clamp(bounds[j].0, bounds[j].1)
371 })
372 .collect();
373 island[worst_idx] = mutant;
374 }
375 if generation > 0 && generation % migration_interval == 0 {
376 for i in 0..n_islands {
377 let next = (i + 1) % n_islands;
378 let best_i = islands[i]
379 .iter()
380 .min_by(|a, b| {
381 objective(a)
382 .partial_cmp(&objective(b))
383 .unwrap_or(std::cmp::Ordering::Equal)
384 })
385 .unwrap()
386 .clone();
387 let worst_idx = islands[next]
388 .iter()
389 .enumerate()
390 .max_by(|a, b| {
391 objective(a.1)
392 .partial_cmp(&objective(b.1))
393 .unwrap_or(std::cmp::Ordering::Equal)
394 })
395 .unwrap()
396 .0;
397 islands[next][worst_idx] = best_i;
398 }
399 }
400 }
401 islands
402 .into_iter()
403 .flat_map(|island| island.into_iter())
404 .min_by(|a, b| {
405 objective(a)
406 .partial_cmp(&objective(b))
407 .unwrap_or(std::cmp::Ordering::Equal)
408 })
409 .unwrap()
410}