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::error::{check_at_least, check_range, Result};
27use crate::timing::{evaluate_within, expired, Timer};
28
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32#[derive(Debug, Clone)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35pub struct GaConfig {
36 pub population_size: usize,
38 pub max_generations: u32,
40 pub crossover_rate: f64,
42 pub mutation_rate: f64,
44 pub elite_count: usize,
46 pub tournament_size: usize,
48 pub time_limit: Option<Duration>,
50 pub target_fitness: Option<f64>,
52 pub stagnation_limit: Option<u32>,
54}
55
56impl Default for GaConfig {
57 fn default() -> Self {
58 Self {
59 population_size: 100,
60 max_generations: 500,
61 crossover_rate: 0.85,
62 mutation_rate: 0.05,
63 elite_count: 5,
64 tournament_size: 3,
65 time_limit: None,
66 target_fitness: None,
67 stagnation_limit: Some(50),
68 }
69 }
70}
71
72impl GaConfig {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_population_size(mut self, size: usize) -> Self {
80 self.population_size = size.max(2);
81 self
82 }
83
84 pub fn with_max_generations(mut self, gen: u32) -> Self {
86 self.max_generations = gen;
87 self
88 }
89
90 pub fn with_crossover_rate(mut self, rate: f64) -> Self {
92 self.crossover_rate = rate;
93 self
94 }
95
96 pub fn with_mutation_rate(mut self, rate: f64) -> Self {
98 self.mutation_rate = rate;
99 self
100 }
101
102 pub fn with_elite_count(mut self, count: usize) -> Self {
104 self.elite_count = count;
105 self
106 }
107
108 pub fn with_time_limit(mut self, duration: Duration) -> Self {
110 self.time_limit = Some(duration);
111 self
112 }
113
114 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
116 self.target_fitness = Some(fitness);
117 self
118 }
119
120 pub fn validate(&self) -> Result<()> {
127 check_range("crossover_rate", self.crossover_rate, 0.0, 1.0)?;
128 check_range("mutation_rate", self.mutation_rate, 0.0, 1.0)?;
129 check_at_least("population_size", self.population_size as f64, 2.0)?;
130 check_at_least("tournament_size", self.tournament_size as f64, 1.0)?;
131 Ok(())
132 }
133}
134
135pub trait Individual: Clone + Send + Sync {
140 type Fitness: PartialOrd + Copy + Send;
142
143 fn fitness(&self) -> Self::Fitness;
145
146 fn random<R: Rng>(rng: &mut R) -> Self;
148
149 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
151
152 fn mutate<R: Rng>(&mut self, rng: &mut R);
154}
155
156pub trait GaProblem: Send + Sync {
161 type Individual: Individual;
163
164 fn evaluate(&self, individual: &mut Self::Individual);
166
167 fn evaluate_parallel(&self, individuals: &mut [Self::Individual]) {
170 #[cfg(feature = "parallel")]
171 individuals.par_iter_mut().for_each(|ind| {
172 self.evaluate(ind);
173 });
174 #[cfg(not(feature = "parallel"))]
175 for ind in individuals.iter_mut() {
176 self.evaluate(ind);
177 }
178 }
179
180 fn initialize_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<Self::Individual> {
182 (0..size).map(|_| Self::Individual::random(rng)).collect()
183 }
184
185 fn on_generation(
187 &self,
188 _generation: u32,
189 _best: &Self::Individual,
190 _population: &[Self::Individual],
191 ) {
192 }
194}
195
196#[derive(Debug, Clone)]
198pub struct GaProgress<F> {
199 pub generation: u32,
201 pub max_generations: u32,
203 pub best_fitness: F,
205 pub avg_fitness: f64,
207 pub elapsed: Duration,
209 pub running: bool,
211}
212
213#[derive(Debug, Clone)]
215pub struct GaResult<I: Individual> {
216 pub best: I,
218 pub generations: u32,
220 pub elapsed: Duration,
222 pub target_reached: bool,
224 pub history: Vec<f64>,
226}
227
228pub struct GaRunner<P: GaProblem> {
233 config: GaConfig,
234 problem: P,
235 cancelled: Arc<AtomicBool>,
236}
237
238impl<P: GaProblem> GaRunner<P>
239where
240 <P::Individual as Individual>::Fitness: Into<f64>,
241{
242 pub fn new(config: GaConfig, problem: P) -> Result<Self> {
247 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
248 }
249
250 pub fn with_cancellation(
256 config: GaConfig,
257 problem: P,
258 cancelled: Arc<AtomicBool>,
259 ) -> Result<Self> {
260 config.validate()?;
261 Ok(Self {
262 config,
263 problem,
264 cancelled,
265 })
266 }
267
268 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
270 self.cancelled.clone()
271 }
272
273 pub fn run(&self) -> GaResult<P::Individual> {
275 self.run_with_rng(&mut rand::rng())
276 }
277
278 pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
280 where
281 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
282 {
283 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
284 }
285
286 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
288 self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
289 rng, None,
290 )
291 }
292
293 pub fn run_with_rng_and_progress<R: Rng, F>(
295 &self,
296 rng: &mut R,
297 progress_callback: Option<F>,
298 ) -> GaResult<P::Individual>
299 where
300 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
301 {
302 let start = Timer::now();
303 let mut history = Vec::new();
304
305 let mut population = self
307 .problem
308 .initialize_population(self.config.population_size, rng);
309
310 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
312 self.problem.evaluate_parallel(batch)
313 });
314
315 population.sort_by(|a, b| {
317 b.fitness()
318 .partial_cmp(&a.fitness())
319 .unwrap_or(std::cmp::Ordering::Equal)
320 });
321
322 let mut best = population[0].clone();
323 let mut best_fitness: f64 = best.fitness().into();
324 let mut stagnation_count = 0u32;
325 let mut generation = 0u32;
326 let mut target_reached = false;
327
328 while generation < self.config.max_generations {
329 if self.cancelled.load(Ordering::Relaxed) {
331 break;
332 }
333
334 if expired(&start, self.config.time_limit) {
336 break;
337 }
338
339 if let Some(target) = self.config.target_fitness {
341 if best_fitness >= target {
342 target_reached = true;
343 break;
344 }
345 }
346
347 history.push(best_fitness);
349
350 let mut new_population = Vec::with_capacity(self.config.population_size);
352
353 for individual in population
355 .iter()
356 .take(self.config.elite_count.min(population.len()))
357 {
358 new_population.push(individual.clone());
359 }
360
361 let mut children: Vec<P::Individual> =
363 Vec::with_capacity(self.config.population_size - new_population.len());
364
365 while children.len() < self.config.population_size - new_population.len() {
366 let parent1 = self.tournament_select(&population, rng);
368 let parent2 = self.tournament_select(&population, rng);
369
370 let mut child = if rng.random::<f64>() < self.config.crossover_rate {
372 parent1.crossover(parent2, rng)
373 } else {
374 parent1.clone()
375 };
376
377 if rng.random::<f64>() < self.config.mutation_rate {
379 child.mutate(rng);
380 }
381
382 children.push(child);
383 }
384
385 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
388 self.problem.evaluate_parallel(batch)
389 });
390
391 new_population.extend(children);
393
394 new_population.sort_by(|a, b| {
396 b.fitness()
397 .partial_cmp(&a.fitness())
398 .unwrap_or(std::cmp::Ordering::Equal)
399 });
400
401 let new_best_fitness: f64 = new_population[0].fitness().into();
403 if new_best_fitness > best_fitness {
404 best = new_population[0].clone();
405 best_fitness = new_best_fitness;
406 stagnation_count = 0;
407 } else {
408 stagnation_count += 1;
409 }
410
411 if let Some(limit) = self.config.stagnation_limit {
413 if stagnation_count >= limit {
414 break;
415 }
416 }
417
418 self.problem
420 .on_generation(generation, &best, &new_population);
421
422 if let Some(ref callback) = progress_callback {
424 let avg_fitness = new_population
425 .iter()
426 .map(|ind| ind.fitness().into())
427 .sum::<f64>()
428 / new_population.len() as f64;
429
430 callback(GaProgress {
431 generation,
432 max_generations: self.config.max_generations,
433 best_fitness: best.fitness(),
434 avg_fitness,
435 elapsed: start.elapsed(),
436 running: true,
437 });
438 }
439
440 population = new_population;
441 generation += 1;
442 }
443
444 history.push(best_fitness);
446
447 if let Some(ref callback) = progress_callback {
449 let avg_fitness = population
450 .iter()
451 .map(|ind| ind.fitness().into())
452 .sum::<f64>()
453 / population.len().max(1) as f64;
454
455 callback(GaProgress {
456 generation,
457 max_generations: self.config.max_generations,
458 best_fitness: best.fitness(),
459 avg_fitness,
460 elapsed: start.elapsed(),
461 running: false,
462 });
463 }
464
465 GaResult {
466 best,
467 generations: generation,
468 elapsed: start.elapsed(),
469 target_reached,
470 history,
471 }
472 }
473
474 fn tournament_select<'a, R: Rng>(
476 &self,
477 population: &'a [P::Individual],
478 rng: &mut R,
479 ) -> &'a P::Individual {
480 let mut best_idx = rng.random_range(0..population.len());
481
482 for _ in 1..self.config.tournament_size {
483 let idx = rng.random_range(0..population.len());
484 if population[idx].fitness() > population[best_idx].fitness() {
485 best_idx = idx;
486 }
487 }
488
489 &population[best_idx]
490 }
491}
492
493#[derive(Debug, Clone)]
495pub struct PermutationChromosome {
496 pub genes: Vec<usize>,
498 pub rotations: Vec<usize>,
500 fitness: f64,
502}
503
504impl PermutationChromosome {
505 pub fn new(size: usize, _rotation_options: usize) -> Self {
507 Self {
508 genes: (0..size).collect(),
509 rotations: vec![0; size],
510 fitness: f64::NEG_INFINITY,
511 }
512 }
513
514 pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
516 let mut genes: Vec<usize> = (0..size).collect();
517 genes.shuffle(rng);
518
519 let rotations: Vec<usize> = (0..size)
520 .map(|_| rng.random_range(0..rotation_options.max(1)))
521 .collect();
522
523 Self {
524 genes,
525 rotations,
526 fitness: f64::NEG_INFINITY,
527 }
528 }
529
530 pub fn set_fitness(&mut self, fitness: f64) {
532 self.fitness = fitness;
533 }
534
535 pub fn len(&self) -> usize {
537 self.genes.len()
538 }
539
540 pub fn is_empty(&self) -> bool {
542 self.genes.is_empty()
543 }
544
545 pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
547 let n = self.genes.len();
548 if n < 2 {
549 return self.clone();
550 }
551
552 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
554 if p1 > p2 {
555 std::mem::swap(&mut p1, &mut p2);
556 }
557
558 let mut child_genes = vec![usize::MAX; n];
560 let mut used = vec![false; n];
561
562 for i in p1..=p2 {
563 child_genes[i] = self.genes[i];
564 used[self.genes[i]] = true;
565 }
566
567 let mut j = (p2 + 1) % n;
569 for i in 0..n {
570 let idx = (p2 + 1 + i) % n;
571 if child_genes[idx] == usize::MAX {
572 while used[other.genes[j]] {
573 j = (j + 1) % n;
574 }
575 child_genes[idx] = other.genes[j];
576 used[other.genes[j]] = true;
577 j = (j + 1) % n;
578 }
579 }
580
581 let rotations: Vec<usize> = self
583 .rotations
584 .iter()
585 .zip(&other.rotations)
586 .map(|(a, b)| if rng.random() { *a } else { *b })
587 .collect();
588
589 Self {
590 genes: child_genes,
591 rotations,
592 fitness: f64::NEG_INFINITY,
593 }
594 }
595
596 pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
598 if self.genes.len() < 2 {
599 return;
600 }
601
602 let i = rng.random_range(0..self.genes.len());
603 let j = rng.random_range(0..self.genes.len());
604 self.genes.swap(i, j);
605 self.fitness = f64::NEG_INFINITY;
606 }
607
608 pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
610 if self.rotations.is_empty() || rotation_options <= 1 {
611 return;
612 }
613
614 let idx = rng.random_range(0..self.rotations.len());
615 self.rotations[idx] = rng.random_range(0..rotation_options);
616 self.fitness = f64::NEG_INFINITY;
617 }
618
619 pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
621 let n = self.genes.len();
622 if n < 2 {
623 return;
624 }
625
626 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
627 if p1 > p2 {
628 std::mem::swap(&mut p1, &mut p2);
629 }
630
631 self.genes[p1..=p2].reverse();
632 self.fitness = f64::NEG_INFINITY;
633 }
634}
635
636impl Individual for PermutationChromosome {
637 type Fitness = f64;
638
639 fn fitness(&self) -> f64 {
640 self.fitness
641 }
642
643 fn random<R: Rng>(rng: &mut R) -> Self {
644 Self::random_with_options(0, 1, rng)
646 }
647
648 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
649 self.order_crossover(other, rng)
650 }
651
652 fn mutate<R: Rng>(&mut self, rng: &mut R) {
653 if rng.random::<f64>() < 0.7 {
655 self.swap_mutate(rng);
656 } else {
657 self.inversion_mutate(rng);
658 }
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665
666 #[derive(Clone)]
667 struct SimpleIndividual {
668 value: f64,
669 }
670
671 impl Individual for SimpleIndividual {
672 type Fitness = f64;
673
674 fn fitness(&self) -> f64 {
675 -self.value * self.value
677 }
678
679 fn random<R: Rng>(rng: &mut R) -> Self {
680 Self {
681 value: rng.random_range(-100.0..100.0),
682 }
683 }
684
685 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
686 Self {
687 value: if rng.random() {
688 self.value
689 } else {
690 other.value
691 },
692 }
693 }
694
695 fn mutate<R: Rng>(&mut self, rng: &mut R) {
696 self.value += rng.random_range(-10.0..10.0);
697 }
698 }
699
700 struct SimpleProblem;
701
702 impl GaProblem for SimpleProblem {
703 type Individual = SimpleIndividual;
704
705 fn evaluate(&self, _individual: &mut Self::Individual) {
706 }
708 }
709
710 #[test]
711 fn test_ga_basic() {
712 let config = GaConfig::default()
713 .with_population_size(50)
714 .with_max_generations(100)
715 .with_target_fitness(-0.01);
716
717 let runner = GaRunner::new(config, SimpleProblem).expect("valid config");
718 let result = runner.run();
719
720 assert!(result.best.value.abs() < 5.0);
722 }
723
724 #[test]
725 fn test_permutation_crossover() {
726 let mut rng = rand::rng();
727 let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
728 let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
729
730 let child = parent1.order_crossover(&parent2, &mut rng);
731
732 assert_eq!(child.genes.len(), 10);
734 let mut sorted = child.genes.clone();
735 sorted.sort();
736 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
737 }
738
739 #[test]
740 fn test_permutation_mutation() {
741 let mut rng = rand::rng();
742 let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
743
744 chromosome.swap_mutate(&mut rng);
745
746 let mut sorted = chromosome.genes.clone();
748 sorted.sort();
749 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
750 }
751}