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::result::StopReason;
28use crate::timing::{evaluate_within, expired, Timer};
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32
33#[derive(Debug, Clone)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub struct GaConfig {
37 pub population_size: usize,
39 pub max_generations: u32,
41 pub crossover_rate: f64,
43 pub mutation_rate: f64,
45 pub elite_count: usize,
47 pub tournament_size: usize,
49 pub time_limit: Option<Duration>,
51 pub target_fitness: Option<f64>,
53 pub stagnation_limit: Option<u32>,
55}
56
57impl Default for GaConfig {
58 fn default() -> Self {
59 Self {
60 population_size: 100,
61 max_generations: 500,
62 crossover_rate: 0.85,
63 mutation_rate: 0.05,
64 elite_count: 5,
65 tournament_size: 3,
66 time_limit: None,
67 target_fitness: None,
68 stagnation_limit: Some(50),
69 }
70 }
71}
72
73impl GaConfig {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn with_population_size(mut self, size: usize) -> Self {
81 self.population_size = size;
82 self
83 }
84
85 pub fn with_max_generations(mut self, gen: u32) -> Self {
87 self.max_generations = gen;
88 self
89 }
90
91 pub fn with_crossover_rate(mut self, rate: f64) -> Self {
93 self.crossover_rate = rate;
94 self
95 }
96
97 pub fn with_mutation_rate(mut self, rate: f64) -> Self {
99 self.mutation_rate = rate;
100 self
101 }
102
103 pub fn with_elite_count(mut self, count: usize) -> Self {
105 self.elite_count = count;
106 self
107 }
108
109 pub fn with_time_limit(mut self, duration: Duration) -> Self {
111 self.time_limit = Some(duration);
112 self
113 }
114
115 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
117 self.target_fitness = Some(fitness);
118 self
119 }
120
121 pub fn validate(&self) -> Result<()> {
128 check_range("crossover_rate", self.crossover_rate, 0.0, 1.0)?;
129 check_range("mutation_rate", self.mutation_rate, 0.0, 1.0)?;
130 check_at_least("population_size", self.population_size as f64, 2.0)?;
131 check_at_least("tournament_size", self.tournament_size as f64, 1.0)?;
132 Ok(())
133 }
134}
135
136pub trait Individual: Clone + Send + Sync {
141 type Fitness: PartialOrd + Copy + Send;
143
144 fn fitness(&self) -> Self::Fitness;
146
147 fn random<R: Rng>(rng: &mut R) -> Self;
149
150 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
152
153 fn mutate<R: Rng>(&mut self, rng: &mut R);
155}
156
157pub trait GaProblem: Send + Sync {
162 type Individual: Individual;
164
165 fn evaluate(&self, individual: &mut Self::Individual);
167
168 fn evaluate_parallel(&self, individuals: &mut [Self::Individual]) {
171 #[cfg(feature = "parallel")]
172 individuals.par_iter_mut().for_each(|ind| {
173 self.evaluate(ind);
174 });
175 #[cfg(not(feature = "parallel"))]
176 for ind in individuals.iter_mut() {
177 self.evaluate(ind);
178 }
179 }
180
181 fn initialize_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<Self::Individual> {
183 (0..size).map(|_| Self::Individual::random(rng)).collect()
184 }
185
186 fn on_generation(
188 &self,
189 _generation: u32,
190 _best: &Self::Individual,
191 _population: &[Self::Individual],
192 ) {
193 }
195}
196
197#[derive(Debug, Clone)]
199pub struct GaProgress<F> {
200 pub generation: u32,
202 pub max_generations: u32,
204 pub best_fitness: F,
206 pub avg_fitness: f64,
208 pub elapsed: Duration,
210 pub running: bool,
212}
213
214#[derive(Debug, Clone)]
216pub struct GaResult<I: Individual> {
217 pub best: I,
219 pub generations: u32,
221 pub elapsed: Duration,
223 pub stop_reason: StopReason,
225 pub history: Vec<f64>,
227}
228
229pub struct GaRunner<P: GaProblem> {
234 config: GaConfig,
235 problem: P,
236 cancelled: Arc<AtomicBool>,
237}
238
239impl<P: GaProblem> GaRunner<P>
240where
241 <P::Individual as Individual>::Fitness: Into<f64>,
242{
243 pub fn new(config: GaConfig, problem: P) -> Result<Self> {
248 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
249 }
250
251 pub fn with_cancellation(
257 config: GaConfig,
258 problem: P,
259 cancelled: Arc<AtomicBool>,
260 ) -> Result<Self> {
261 config.validate()?;
262 Ok(Self {
263 config,
264 problem,
265 cancelled,
266 })
267 }
268
269 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
271 self.cancelled.clone()
272 }
273
274 pub fn run(&self) -> GaResult<P::Individual> {
276 self.run_with_rng(&mut rand::rng())
277 }
278
279 pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
281 where
282 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
283 {
284 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
285 }
286
287 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
289 self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
290 rng, None,
291 )
292 }
293
294 pub fn run_with_rng_and_progress<R: Rng, F>(
296 &self,
297 rng: &mut R,
298 progress_callback: Option<F>,
299 ) -> GaResult<P::Individual>
300 where
301 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
302 {
303 let start = Timer::now();
304 let mut history = Vec::new();
305
306 let heartbeat = |generation: u32,
311 best: Option<<P::Individual as Individual>::Fitness>,
312 batch: &[P::Individual]| {
313 let best = batch
314 .iter()
315 .map(Individual::fitness)
316 .fold(best, |b, f| match b {
317 Some(b) if b >= f => Some(b),
318 _ => Some(f),
319 });
320 if let (Some(callback), Some(best_fitness)) = (&progress_callback, best) {
321 let avg_fitness = batch.iter().map(|i| i.fitness().into()).sum::<f64>()
322 / batch.len().max(1) as f64;
323 callback(GaProgress {
324 generation,
325 max_generations: self.config.max_generations,
326 best_fitness,
327 avg_fitness,
328 elapsed: start.elapsed(),
329 running: true,
330 });
331 }
332 best
333 };
334
335 let mut population = self
337 .problem
338 .initialize_population(self.config.population_size, rng);
339
340 let mut best_so_far = None;
342 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
343 self.problem.evaluate_parallel(batch);
344 best_so_far = heartbeat(0, best_so_far, batch);
345 });
346
347 population.sort_by(|a, b| {
349 b.fitness()
350 .partial_cmp(&a.fitness())
351 .unwrap_or(std::cmp::Ordering::Equal)
352 });
353
354 let mut best = population[0].clone();
355 let mut best_fitness: f64 = best.fitness().into();
356 let mut stagnation_count = 0u32;
357 let mut generation = 0u32;
358 let reached = |fitness: f64| self.config.target_fitness.is_some_and(|t| fitness >= t);
359
360 let stop_reason = loop {
364 if reached(best_fitness) {
365 break StopReason::TargetReached;
366 }
367 if generation >= self.config.max_generations {
368 break StopReason::IterationLimit;
369 }
370 if self.cancelled.load(Ordering::Relaxed) {
371 break StopReason::Cancelled;
372 }
373 if expired(&start, self.config.time_limit) {
374 break StopReason::TimeLimit;
375 }
376
377 history.push(best_fitness);
379
380 let mut new_population = Vec::with_capacity(self.config.population_size);
382
383 for individual in population
385 .iter()
386 .take(self.config.elite_count.min(population.len()))
387 {
388 new_population.push(individual.clone());
389 }
390
391 let mut children: Vec<P::Individual> =
393 Vec::with_capacity(self.config.population_size - new_population.len());
394
395 while children.len() < self.config.population_size - new_population.len() {
396 let parent1 = self.tournament_select(&population, rng);
398 let parent2 = self.tournament_select(&population, rng);
399
400 let mut child = if rng.random::<f64>() < self.config.crossover_rate {
402 parent1.crossover(parent2, rng)
403 } else {
404 parent1.clone()
405 };
406
407 if rng.random::<f64>() < self.config.mutation_rate {
409 child.mutate(rng);
410 }
411
412 children.push(child);
413 }
414
415 let best_now = best.fitness();
418 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
419 self.problem.evaluate_parallel(batch);
420 heartbeat(generation, Some(best_now), batch);
421 });
422
423 new_population.extend(children);
425
426 new_population.sort_by(|a, b| {
428 b.fitness()
429 .partial_cmp(&a.fitness())
430 .unwrap_or(std::cmp::Ordering::Equal)
431 });
432
433 let new_best_fitness: f64 = new_population[0].fitness().into();
435 if new_best_fitness > best_fitness {
436 best = new_population[0].clone();
437 best_fitness = new_best_fitness;
438 stagnation_count = 0;
439 } else {
440 stagnation_count += 1;
441 }
442
443 if let Some(limit) = self.config.stagnation_limit {
445 if stagnation_count >= limit {
446 break StopReason::NoImprovement;
447 }
448 }
449
450 self.problem
452 .on_generation(generation, &best, &new_population);
453
454 if let Some(ref callback) = progress_callback {
456 let avg_fitness = new_population
457 .iter()
458 .map(|ind| ind.fitness().into())
459 .sum::<f64>()
460 / new_population.len() as f64;
461
462 callback(GaProgress {
463 generation,
464 max_generations: self.config.max_generations,
465 best_fitness: best.fitness(),
466 avg_fitness,
467 elapsed: start.elapsed(),
468 running: true,
469 });
470 }
471
472 population = new_population;
473 generation += 1;
474 };
475
476 history.push(best_fitness);
478
479 if let Some(ref callback) = progress_callback {
481 let avg_fitness = population
482 .iter()
483 .map(|ind| ind.fitness().into())
484 .sum::<f64>()
485 / population.len().max(1) as f64;
486
487 callback(GaProgress {
488 generation,
489 max_generations: self.config.max_generations,
490 best_fitness: best.fitness(),
491 avg_fitness,
492 elapsed: start.elapsed(),
493 running: false,
494 });
495 }
496
497 GaResult {
498 best,
499 generations: generation,
500 elapsed: start.elapsed(),
501 stop_reason,
502 history,
503 }
504 }
505
506 fn tournament_select<'a, R: Rng>(
508 &self,
509 population: &'a [P::Individual],
510 rng: &mut R,
511 ) -> &'a P::Individual {
512 let mut best_idx = rng.random_range(0..population.len());
513
514 for _ in 1..self.config.tournament_size {
515 let idx = rng.random_range(0..population.len());
516 if population[idx].fitness() > population[best_idx].fitness() {
517 best_idx = idx;
518 }
519 }
520
521 &population[best_idx]
522 }
523}
524
525#[derive(Debug, Clone)]
527pub struct PermutationChromosome {
528 pub genes: Vec<usize>,
530 pub rotations: Vec<usize>,
532 fitness: f64,
534}
535
536impl PermutationChromosome {
537 pub fn new(size: usize, _rotation_options: usize) -> Self {
539 Self {
540 genes: (0..size).collect(),
541 rotations: vec![0; size],
542 fitness: f64::NEG_INFINITY,
543 }
544 }
545
546 pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
548 let mut genes: Vec<usize> = (0..size).collect();
549 genes.shuffle(rng);
550
551 let rotations: Vec<usize> = (0..size)
552 .map(|_| rng.random_range(0..rotation_options.max(1)))
553 .collect();
554
555 Self {
556 genes,
557 rotations,
558 fitness: f64::NEG_INFINITY,
559 }
560 }
561
562 pub fn set_fitness(&mut self, fitness: f64) {
564 self.fitness = fitness;
565 }
566
567 pub fn len(&self) -> usize {
569 self.genes.len()
570 }
571
572 pub fn is_empty(&self) -> bool {
574 self.genes.is_empty()
575 }
576
577 pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
579 let n = self.genes.len();
580 if n < 2 {
581 return self.clone();
582 }
583
584 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
586 if p1 > p2 {
587 std::mem::swap(&mut p1, &mut p2);
588 }
589
590 let mut child_genes = vec![usize::MAX; n];
592 let mut used = vec![false; n];
593
594 for i in p1..=p2 {
595 child_genes[i] = self.genes[i];
596 used[self.genes[i]] = true;
597 }
598
599 let mut j = (p2 + 1) % n;
601 for i in 0..n {
602 let idx = (p2 + 1 + i) % n;
603 if child_genes[idx] == usize::MAX {
604 while used[other.genes[j]] {
605 j = (j + 1) % n;
606 }
607 child_genes[idx] = other.genes[j];
608 used[other.genes[j]] = true;
609 j = (j + 1) % n;
610 }
611 }
612
613 let rotations: Vec<usize> = self
615 .rotations
616 .iter()
617 .zip(&other.rotations)
618 .map(|(a, b)| if rng.random() { *a } else { *b })
619 .collect();
620
621 Self {
622 genes: child_genes,
623 rotations,
624 fitness: f64::NEG_INFINITY,
625 }
626 }
627
628 pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
630 if self.genes.len() < 2 {
631 return;
632 }
633
634 let i = rng.random_range(0..self.genes.len());
635 let j = rng.random_range(0..self.genes.len());
636 self.genes.swap(i, j);
637 self.fitness = f64::NEG_INFINITY;
638 }
639
640 pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
642 if self.rotations.is_empty() || rotation_options <= 1 {
643 return;
644 }
645
646 let idx = rng.random_range(0..self.rotations.len());
647 self.rotations[idx] = rng.random_range(0..rotation_options);
648 self.fitness = f64::NEG_INFINITY;
649 }
650
651 pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
653 let n = self.genes.len();
654 if n < 2 {
655 return;
656 }
657
658 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
659 if p1 > p2 {
660 std::mem::swap(&mut p1, &mut p2);
661 }
662
663 self.genes[p1..=p2].reverse();
664 self.fitness = f64::NEG_INFINITY;
665 }
666}
667
668impl Individual for PermutationChromosome {
669 type Fitness = f64;
670
671 fn fitness(&self) -> f64 {
672 self.fitness
673 }
674
675 fn random<R: Rng>(rng: &mut R) -> Self {
676 Self::random_with_options(0, 1, rng)
678 }
679
680 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
681 self.order_crossover(other, rng)
682 }
683
684 fn mutate<R: Rng>(&mut self, rng: &mut R) {
685 if rng.random::<f64>() < 0.7 {
687 self.swap_mutate(rng);
688 } else {
689 self.inversion_mutate(rng);
690 }
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697
698 #[derive(Clone)]
699 struct SimpleIndividual {
700 value: f64,
701 }
702
703 impl Individual for SimpleIndividual {
704 type Fitness = f64;
705
706 fn fitness(&self) -> f64 {
707 -self.value * self.value
709 }
710
711 fn random<R: Rng>(rng: &mut R) -> Self {
712 Self {
713 value: rng.random_range(-100.0..100.0),
714 }
715 }
716
717 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
718 Self {
719 value: if rng.random() {
720 self.value
721 } else {
722 other.value
723 },
724 }
725 }
726
727 fn mutate<R: Rng>(&mut self, rng: &mut R) {
728 self.value += rng.random_range(-10.0..10.0);
729 }
730 }
731
732 struct SimpleProblem;
733
734 impl GaProblem for SimpleProblem {
735 type Individual = SimpleIndividual;
736
737 fn evaluate(&self, _individual: &mut Self::Individual) {
738 }
740 }
741
742 #[test]
743 fn test_ga_basic() {
744 let config = GaConfig::default()
745 .with_population_size(50)
746 .with_max_generations(100)
747 .with_target_fitness(-0.01);
748
749 let runner = GaRunner::new(config, SimpleProblem).expect("valid config");
750 let result = runner.run();
751
752 assert!(result.best.value.abs() < 5.0);
754 }
755
756 #[test]
757 fn test_permutation_crossover() {
758 let mut rng = rand::rng();
759 let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
760 let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
761
762 let child = parent1.order_crossover(&parent2, &mut rng);
763
764 assert_eq!(child.genes.len(), 10);
766 let mut sorted = child.genes.clone();
767 sorted.sort();
768 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
769 }
770
771 #[test]
772 fn test_permutation_mutation() {
773 let mut rng = rand::rng();
774 let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
775
776 chromosome.swap_mutate(&mut rng);
777
778 let mut sorted = chromosome.genes.clone();
780 sorted.sort();
781 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
782 }
783}