1use rand::prelude::*;
20#[cfg(feature = "parallel")]
21use rayon::prelude::*;
22use std::sync::atomic::{AtomicBool, Ordering};
23use std::sync::Arc;
24use std::time::Duration;
25
26use crate::timing::{evaluate_within, expired, Timer};
27
28#[cfg(feature = "serde")]
29use serde::{Deserialize, Serialize};
30
31#[derive(Debug, Clone)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct GaConfig {
35 pub population_size: usize,
37 pub max_generations: u32,
39 pub crossover_rate: f64,
41 pub mutation_rate: f64,
43 pub elite_count: usize,
45 pub tournament_size: usize,
47 pub time_limit: Option<Duration>,
49 pub target_fitness: Option<f64>,
51 pub stagnation_limit: Option<u32>,
53}
54
55impl Default for GaConfig {
56 fn default() -> Self {
57 Self {
58 population_size: 100,
59 max_generations: 500,
60 crossover_rate: 0.85,
61 mutation_rate: 0.05,
62 elite_count: 5,
63 tournament_size: 3,
64 time_limit: None,
65 target_fitness: None,
66 stagnation_limit: Some(50),
67 }
68 }
69}
70
71impl GaConfig {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn with_population_size(mut self, size: usize) -> Self {
79 self.population_size = size.max(2);
80 self
81 }
82
83 pub fn with_max_generations(mut self, gen: u32) -> Self {
85 self.max_generations = gen;
86 self
87 }
88
89 pub fn with_crossover_rate(mut self, rate: f64) -> Self {
91 self.crossover_rate = rate.clamp(0.0, 1.0);
92 self
93 }
94
95 pub fn with_mutation_rate(mut self, rate: f64) -> Self {
97 self.mutation_rate = rate.clamp(0.0, 1.0);
98 self
99 }
100
101 pub fn with_elite_count(mut self, count: usize) -> Self {
103 self.elite_count = count;
104 self
105 }
106
107 pub fn with_time_limit(mut self, duration: Duration) -> Self {
109 self.time_limit = Some(duration);
110 self
111 }
112
113 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
115 self.target_fitness = Some(fitness);
116 self
117 }
118}
119
120pub trait Individual: Clone + Send + Sync {
125 type Fitness: PartialOrd + Copy + Send;
127
128 fn fitness(&self) -> Self::Fitness;
130
131 fn random<R: Rng>(rng: &mut R) -> Self;
133
134 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
136
137 fn mutate<R: Rng>(&mut self, rng: &mut R);
139}
140
141pub trait GaProblem: Send + Sync {
146 type Individual: Individual;
148
149 fn evaluate(&self, individual: &mut Self::Individual);
151
152 fn evaluate_parallel(&self, individuals: &mut [Self::Individual]) {
155 #[cfg(feature = "parallel")]
156 individuals.par_iter_mut().for_each(|ind| {
157 self.evaluate(ind);
158 });
159 #[cfg(not(feature = "parallel"))]
160 for ind in individuals.iter_mut() {
161 self.evaluate(ind);
162 }
163 }
164
165 fn initialize_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<Self::Individual> {
167 (0..size).map(|_| Self::Individual::random(rng)).collect()
168 }
169
170 fn on_generation(
172 &self,
173 _generation: u32,
174 _best: &Self::Individual,
175 _population: &[Self::Individual],
176 ) {
177 }
179}
180
181#[derive(Debug, Clone)]
183pub struct GaProgress<F> {
184 pub generation: u32,
186 pub max_generations: u32,
188 pub best_fitness: F,
190 pub avg_fitness: f64,
192 pub elapsed: Duration,
194 pub running: bool,
196}
197
198#[derive(Debug, Clone)]
200pub struct GaResult<I: Individual> {
201 pub best: I,
203 pub generations: u32,
205 pub elapsed: Duration,
207 pub target_reached: bool,
209 pub history: Vec<f64>,
211}
212
213pub struct GaRunner<P: GaProblem> {
218 config: GaConfig,
219 problem: P,
220 cancelled: Arc<AtomicBool>,
221}
222
223impl<P: GaProblem> GaRunner<P>
224where
225 <P::Individual as Individual>::Fitness: Into<f64>,
226{
227 pub fn new(config: GaConfig, problem: P) -> Self {
229 Self {
230 config,
231 problem,
232 cancelled: Arc::new(AtomicBool::new(false)),
233 }
234 }
235
236 pub fn with_cancellation(config: GaConfig, problem: P, cancelled: Arc<AtomicBool>) -> Self {
239 Self {
240 config,
241 problem,
242 cancelled,
243 }
244 }
245
246 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
248 self.cancelled.clone()
249 }
250
251 pub fn run(&self) -> GaResult<P::Individual> {
253 self.run_with_rng(&mut rand::rng())
254 }
255
256 pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
258 where
259 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
260 {
261 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
262 }
263
264 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
266 self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
267 rng, None,
268 )
269 }
270
271 pub fn run_with_rng_and_progress<R: Rng, F>(
273 &self,
274 rng: &mut R,
275 progress_callback: Option<F>,
276 ) -> GaResult<P::Individual>
277 where
278 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
279 {
280 let start = Timer::now();
281 let mut history = Vec::new();
282
283 let mut population = self
285 .problem
286 .initialize_population(self.config.population_size, rng);
287
288 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
290 self.problem.evaluate_parallel(batch)
291 });
292
293 population.sort_by(|a, b| {
295 b.fitness()
296 .partial_cmp(&a.fitness())
297 .unwrap_or(std::cmp::Ordering::Equal)
298 });
299
300 let mut best = population[0].clone();
301 let mut best_fitness: f64 = best.fitness().into();
302 let mut stagnation_count = 0u32;
303 let mut generation = 0u32;
304 let mut target_reached = false;
305
306 while generation < self.config.max_generations {
307 if self.cancelled.load(Ordering::Relaxed) {
309 break;
310 }
311
312 if expired(&start, self.config.time_limit) {
314 break;
315 }
316
317 if let Some(target) = self.config.target_fitness {
319 if best_fitness >= target {
320 target_reached = true;
321 break;
322 }
323 }
324
325 history.push(best_fitness);
327
328 let mut new_population = Vec::with_capacity(self.config.population_size);
330
331 for individual in population
333 .iter()
334 .take(self.config.elite_count.min(population.len()))
335 {
336 new_population.push(individual.clone());
337 }
338
339 let mut children: Vec<P::Individual> =
341 Vec::with_capacity(self.config.population_size - new_population.len());
342
343 while children.len() < self.config.population_size - new_population.len() {
344 let parent1 = self.tournament_select(&population, rng);
346 let parent2 = self.tournament_select(&population, rng);
347
348 let mut child = if rng.random::<f64>() < self.config.crossover_rate {
350 parent1.crossover(parent2, rng)
351 } else {
352 parent1.clone()
353 };
354
355 if rng.random::<f64>() < self.config.mutation_rate {
357 child.mutate(rng);
358 }
359
360 children.push(child);
361 }
362
363 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
366 self.problem.evaluate_parallel(batch)
367 });
368
369 new_population.extend(children);
371
372 new_population.sort_by(|a, b| {
374 b.fitness()
375 .partial_cmp(&a.fitness())
376 .unwrap_or(std::cmp::Ordering::Equal)
377 });
378
379 let new_best_fitness: f64 = new_population[0].fitness().into();
381 if new_best_fitness > best_fitness {
382 best = new_population[0].clone();
383 best_fitness = new_best_fitness;
384 stagnation_count = 0;
385 } else {
386 stagnation_count += 1;
387 }
388
389 if let Some(limit) = self.config.stagnation_limit {
391 if stagnation_count >= limit {
392 break;
393 }
394 }
395
396 self.problem
398 .on_generation(generation, &best, &new_population);
399
400 if let Some(ref callback) = progress_callback {
402 let avg_fitness = new_population
403 .iter()
404 .map(|ind| ind.fitness().into())
405 .sum::<f64>()
406 / new_population.len() as f64;
407
408 callback(GaProgress {
409 generation,
410 max_generations: self.config.max_generations,
411 best_fitness: best.fitness(),
412 avg_fitness,
413 elapsed: start.elapsed(),
414 running: true,
415 });
416 }
417
418 population = new_population;
419 generation += 1;
420 }
421
422 history.push(best_fitness);
424
425 if let Some(ref callback) = progress_callback {
427 let avg_fitness = population
428 .iter()
429 .map(|ind| ind.fitness().into())
430 .sum::<f64>()
431 / population.len().max(1) as f64;
432
433 callback(GaProgress {
434 generation,
435 max_generations: self.config.max_generations,
436 best_fitness: best.fitness(),
437 avg_fitness,
438 elapsed: start.elapsed(),
439 running: false,
440 });
441 }
442
443 GaResult {
444 best,
445 generations: generation,
446 elapsed: start.elapsed(),
447 target_reached,
448 history,
449 }
450 }
451
452 fn tournament_select<'a, R: Rng>(
454 &self,
455 population: &'a [P::Individual],
456 rng: &mut R,
457 ) -> &'a P::Individual {
458 let mut best_idx = rng.random_range(0..population.len());
459
460 for _ in 1..self.config.tournament_size {
461 let idx = rng.random_range(0..population.len());
462 if population[idx].fitness() > population[best_idx].fitness() {
463 best_idx = idx;
464 }
465 }
466
467 &population[best_idx]
468 }
469}
470
471#[derive(Debug, Clone)]
473pub struct PermutationChromosome {
474 pub genes: Vec<usize>,
476 pub rotations: Vec<usize>,
478 fitness: f64,
480}
481
482impl PermutationChromosome {
483 pub fn new(size: usize, _rotation_options: usize) -> Self {
485 Self {
486 genes: (0..size).collect(),
487 rotations: vec![0; size],
488 fitness: f64::NEG_INFINITY,
489 }
490 }
491
492 pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
494 let mut genes: Vec<usize> = (0..size).collect();
495 genes.shuffle(rng);
496
497 let rotations: Vec<usize> = (0..size)
498 .map(|_| rng.random_range(0..rotation_options.max(1)))
499 .collect();
500
501 Self {
502 genes,
503 rotations,
504 fitness: f64::NEG_INFINITY,
505 }
506 }
507
508 pub fn set_fitness(&mut self, fitness: f64) {
510 self.fitness = fitness;
511 }
512
513 pub fn len(&self) -> usize {
515 self.genes.len()
516 }
517
518 pub fn is_empty(&self) -> bool {
520 self.genes.is_empty()
521 }
522
523 pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
525 let n = self.genes.len();
526 if n < 2 {
527 return self.clone();
528 }
529
530 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
532 if p1 > p2 {
533 std::mem::swap(&mut p1, &mut p2);
534 }
535
536 let mut child_genes = vec![usize::MAX; n];
538 let mut used = vec![false; n];
539
540 for i in p1..=p2 {
541 child_genes[i] = self.genes[i];
542 used[self.genes[i]] = true;
543 }
544
545 let mut j = (p2 + 1) % n;
547 for i in 0..n {
548 let idx = (p2 + 1 + i) % n;
549 if child_genes[idx] == usize::MAX {
550 while used[other.genes[j]] {
551 j = (j + 1) % n;
552 }
553 child_genes[idx] = other.genes[j];
554 used[other.genes[j]] = true;
555 j = (j + 1) % n;
556 }
557 }
558
559 let rotations: Vec<usize> = self
561 .rotations
562 .iter()
563 .zip(&other.rotations)
564 .map(|(a, b)| if rng.random() { *a } else { *b })
565 .collect();
566
567 Self {
568 genes: child_genes,
569 rotations,
570 fitness: f64::NEG_INFINITY,
571 }
572 }
573
574 pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
576 if self.genes.len() < 2 {
577 return;
578 }
579
580 let i = rng.random_range(0..self.genes.len());
581 let j = rng.random_range(0..self.genes.len());
582 self.genes.swap(i, j);
583 self.fitness = f64::NEG_INFINITY;
584 }
585
586 pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
588 if self.rotations.is_empty() || rotation_options <= 1 {
589 return;
590 }
591
592 let idx = rng.random_range(0..self.rotations.len());
593 self.rotations[idx] = rng.random_range(0..rotation_options);
594 self.fitness = f64::NEG_INFINITY;
595 }
596
597 pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
599 let n = self.genes.len();
600 if n < 2 {
601 return;
602 }
603
604 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
605 if p1 > p2 {
606 std::mem::swap(&mut p1, &mut p2);
607 }
608
609 self.genes[p1..=p2].reverse();
610 self.fitness = f64::NEG_INFINITY;
611 }
612}
613
614impl Individual for PermutationChromosome {
615 type Fitness = f64;
616
617 fn fitness(&self) -> f64 {
618 self.fitness
619 }
620
621 fn random<R: Rng>(rng: &mut R) -> Self {
622 Self::random_with_options(0, 1, rng)
624 }
625
626 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
627 self.order_crossover(other, rng)
628 }
629
630 fn mutate<R: Rng>(&mut self, rng: &mut R) {
631 if rng.random::<f64>() < 0.7 {
633 self.swap_mutate(rng);
634 } else {
635 self.inversion_mutate(rng);
636 }
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[derive(Clone)]
645 struct SimpleIndividual {
646 value: f64,
647 }
648
649 impl Individual for SimpleIndividual {
650 type Fitness = f64;
651
652 fn fitness(&self) -> f64 {
653 -self.value * self.value
655 }
656
657 fn random<R: Rng>(rng: &mut R) -> Self {
658 Self {
659 value: rng.random_range(-100.0..100.0),
660 }
661 }
662
663 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
664 Self {
665 value: if rng.random() {
666 self.value
667 } else {
668 other.value
669 },
670 }
671 }
672
673 fn mutate<R: Rng>(&mut self, rng: &mut R) {
674 self.value += rng.random_range(-10.0..10.0);
675 }
676 }
677
678 struct SimpleProblem;
679
680 impl GaProblem for SimpleProblem {
681 type Individual = SimpleIndividual;
682
683 fn evaluate(&self, _individual: &mut Self::Individual) {
684 }
686 }
687
688 #[test]
689 fn test_ga_basic() {
690 let config = GaConfig::default()
691 .with_population_size(50)
692 .with_max_generations(100)
693 .with_target_fitness(-0.01);
694
695 let runner = GaRunner::new(config, SimpleProblem);
696 let result = runner.run();
697
698 assert!(result.best.value.abs() < 5.0);
700 }
701
702 #[test]
703 fn test_permutation_crossover() {
704 let mut rng = rand::rng();
705 let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
706 let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
707
708 let child = parent1.order_crossover(&parent2, &mut rng);
709
710 assert_eq!(child.genes.len(), 10);
712 let mut sorted = child.genes.clone();
713 sorted.sort();
714 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
715 }
716
717 #[test]
718 fn test_permutation_mutation() {
719 let mut rng = rand::rng();
720 let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
721
722 chromosome.swap_mutate(&mut rng);
723
724 let mut sorted = chromosome.genes.clone();
726 sorted.sort();
727 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
728 }
729}