Skip to main content

sciforge_lib/maths/optimization/
evolutionary.rs

1pub 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 &current_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}