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::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 cancel_handle(&self) -> Arc<AtomicBool> {
238 self.cancelled.clone()
239 }
240
241 pub fn run(&self) -> GaResult<P::Individual> {
243 self.run_with_rng(&mut rand::rng())
244 }
245
246 pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
248 where
249 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
250 {
251 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
252 }
253
254 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
256 self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
257 rng, None,
258 )
259 }
260
261 pub fn run_with_rng_and_progress<R: Rng, F>(
263 &self,
264 rng: &mut R,
265 progress_callback: Option<F>,
266 ) -> GaResult<P::Individual>
267 where
268 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
269 {
270 let start = Timer::now();
271 let mut history = Vec::new();
272
273 let mut population = self
275 .problem
276 .initialize_population(self.config.population_size, rng);
277
278 self.problem.evaluate_parallel(&mut population);
280
281 population.sort_by(|a, b| {
283 b.fitness()
284 .partial_cmp(&a.fitness())
285 .unwrap_or(std::cmp::Ordering::Equal)
286 });
287
288 let mut best = population[0].clone();
289 let mut best_fitness: f64 = best.fitness().into();
290 let mut stagnation_count = 0u32;
291 let mut generation = 0u32;
292 let mut target_reached = false;
293
294 while generation < self.config.max_generations {
295 if self.cancelled.load(Ordering::Relaxed) {
297 break;
298 }
299
300 if let Some(limit) = self.config.time_limit {
302 if start.elapsed() > limit {
303 break;
304 }
305 }
306
307 if let Some(target) = self.config.target_fitness {
309 if best_fitness >= target {
310 target_reached = true;
311 break;
312 }
313 }
314
315 history.push(best_fitness);
317
318 let mut new_population = Vec::with_capacity(self.config.population_size);
320
321 for individual in population
323 .iter()
324 .take(self.config.elite_count.min(population.len()))
325 {
326 new_population.push(individual.clone());
327 }
328
329 let mut children: Vec<P::Individual> =
331 Vec::with_capacity(self.config.population_size - new_population.len());
332
333 while children.len() < self.config.population_size - new_population.len() {
334 let parent1 = self.tournament_select(&population, rng);
336 let parent2 = self.tournament_select(&population, rng);
337
338 let mut child = if rng.random::<f64>() < self.config.crossover_rate {
340 parent1.crossover(parent2, rng)
341 } else {
342 parent1.clone()
343 };
344
345 if rng.random::<f64>() < self.config.mutation_rate {
347 child.mutate(rng);
348 }
349
350 children.push(child);
351 }
352
353 self.problem.evaluate_parallel(&mut children);
355
356 new_population.extend(children);
358
359 new_population.sort_by(|a, b| {
361 b.fitness()
362 .partial_cmp(&a.fitness())
363 .unwrap_or(std::cmp::Ordering::Equal)
364 });
365
366 let new_best_fitness: f64 = new_population[0].fitness().into();
368 if new_best_fitness > best_fitness {
369 best = new_population[0].clone();
370 best_fitness = new_best_fitness;
371 stagnation_count = 0;
372 } else {
373 stagnation_count += 1;
374 }
375
376 if let Some(limit) = self.config.stagnation_limit {
378 if stagnation_count >= limit {
379 break;
380 }
381 }
382
383 self.problem
385 .on_generation(generation, &best, &new_population);
386
387 if let Some(ref callback) = progress_callback {
389 let avg_fitness = new_population
390 .iter()
391 .map(|ind| ind.fitness().into())
392 .sum::<f64>()
393 / new_population.len() as f64;
394
395 callback(GaProgress {
396 generation,
397 max_generations: self.config.max_generations,
398 best_fitness: best.fitness(),
399 avg_fitness,
400 elapsed: start.elapsed(),
401 running: true,
402 });
403 }
404
405 population = new_population;
406 generation += 1;
407 }
408
409 history.push(best_fitness);
411
412 if let Some(ref callback) = progress_callback {
414 let avg_fitness = population
415 .iter()
416 .map(|ind| ind.fitness().into())
417 .sum::<f64>()
418 / population.len().max(1) as f64;
419
420 callback(GaProgress {
421 generation,
422 max_generations: self.config.max_generations,
423 best_fitness: best.fitness(),
424 avg_fitness,
425 elapsed: start.elapsed(),
426 running: false,
427 });
428 }
429
430 GaResult {
431 best,
432 generations: generation,
433 elapsed: start.elapsed(),
434 target_reached,
435 history,
436 }
437 }
438
439 fn tournament_select<'a, R: Rng>(
441 &self,
442 population: &'a [P::Individual],
443 rng: &mut R,
444 ) -> &'a P::Individual {
445 let mut best_idx = rng.random_range(0..population.len());
446
447 for _ in 1..self.config.tournament_size {
448 let idx = rng.random_range(0..population.len());
449 if population[idx].fitness() > population[best_idx].fitness() {
450 best_idx = idx;
451 }
452 }
453
454 &population[best_idx]
455 }
456}
457
458#[derive(Debug, Clone)]
460pub struct PermutationChromosome {
461 pub genes: Vec<usize>,
463 pub rotations: Vec<usize>,
465 fitness: f64,
467}
468
469impl PermutationChromosome {
470 pub fn new(size: usize, _rotation_options: usize) -> Self {
472 Self {
473 genes: (0..size).collect(),
474 rotations: vec![0; size],
475 fitness: f64::NEG_INFINITY,
476 }
477 }
478
479 pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
481 let mut genes: Vec<usize> = (0..size).collect();
482 genes.shuffle(rng);
483
484 let rotations: Vec<usize> = (0..size)
485 .map(|_| rng.random_range(0..rotation_options.max(1)))
486 .collect();
487
488 Self {
489 genes,
490 rotations,
491 fitness: f64::NEG_INFINITY,
492 }
493 }
494
495 pub fn set_fitness(&mut self, fitness: f64) {
497 self.fitness = fitness;
498 }
499
500 pub fn len(&self) -> usize {
502 self.genes.len()
503 }
504
505 pub fn is_empty(&self) -> bool {
507 self.genes.is_empty()
508 }
509
510 pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
512 let n = self.genes.len();
513 if n < 2 {
514 return self.clone();
515 }
516
517 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
519 if p1 > p2 {
520 std::mem::swap(&mut p1, &mut p2);
521 }
522
523 let mut child_genes = vec![usize::MAX; n];
525 let mut used = vec![false; n];
526
527 for i in p1..=p2 {
528 child_genes[i] = self.genes[i];
529 used[self.genes[i]] = true;
530 }
531
532 let mut j = (p2 + 1) % n;
534 for i in 0..n {
535 let idx = (p2 + 1 + i) % n;
536 if child_genes[idx] == usize::MAX {
537 while used[other.genes[j]] {
538 j = (j + 1) % n;
539 }
540 child_genes[idx] = other.genes[j];
541 used[other.genes[j]] = true;
542 j = (j + 1) % n;
543 }
544 }
545
546 let rotations: Vec<usize> = self
548 .rotations
549 .iter()
550 .zip(&other.rotations)
551 .map(|(a, b)| if rng.random() { *a } else { *b })
552 .collect();
553
554 Self {
555 genes: child_genes,
556 rotations,
557 fitness: f64::NEG_INFINITY,
558 }
559 }
560
561 pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
563 if self.genes.len() < 2 {
564 return;
565 }
566
567 let i = rng.random_range(0..self.genes.len());
568 let j = rng.random_range(0..self.genes.len());
569 self.genes.swap(i, j);
570 self.fitness = f64::NEG_INFINITY;
571 }
572
573 pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
575 if self.rotations.is_empty() || rotation_options <= 1 {
576 return;
577 }
578
579 let idx = rng.random_range(0..self.rotations.len());
580 self.rotations[idx] = rng.random_range(0..rotation_options);
581 self.fitness = f64::NEG_INFINITY;
582 }
583
584 pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
586 let n = self.genes.len();
587 if n < 2 {
588 return;
589 }
590
591 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
592 if p1 > p2 {
593 std::mem::swap(&mut p1, &mut p2);
594 }
595
596 self.genes[p1..=p2].reverse();
597 self.fitness = f64::NEG_INFINITY;
598 }
599}
600
601impl Individual for PermutationChromosome {
602 type Fitness = f64;
603
604 fn fitness(&self) -> f64 {
605 self.fitness
606 }
607
608 fn random<R: Rng>(rng: &mut R) -> Self {
609 Self::random_with_options(0, 1, rng)
611 }
612
613 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
614 self.order_crossover(other, rng)
615 }
616
617 fn mutate<R: Rng>(&mut self, rng: &mut R) {
618 if rng.random::<f64>() < 0.7 {
620 self.swap_mutate(rng);
621 } else {
622 self.inversion_mutate(rng);
623 }
624 }
625}
626
627#[cfg(test)]
628mod tests {
629 use super::*;
630
631 #[derive(Clone)]
632 struct SimpleIndividual {
633 value: f64,
634 }
635
636 impl Individual for SimpleIndividual {
637 type Fitness = f64;
638
639 fn fitness(&self) -> f64 {
640 -self.value * self.value
642 }
643
644 fn random<R: Rng>(rng: &mut R) -> Self {
645 Self {
646 value: rng.random_range(-100.0..100.0),
647 }
648 }
649
650 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
651 Self {
652 value: if rng.random() {
653 self.value
654 } else {
655 other.value
656 },
657 }
658 }
659
660 fn mutate<R: Rng>(&mut self, rng: &mut R) {
661 self.value += rng.random_range(-10.0..10.0);
662 }
663 }
664
665 struct SimpleProblem;
666
667 impl GaProblem for SimpleProblem {
668 type Individual = SimpleIndividual;
669
670 fn evaluate(&self, _individual: &mut Self::Individual) {
671 }
673 }
674
675 #[test]
676 fn test_ga_basic() {
677 let config = GaConfig::default()
678 .with_population_size(50)
679 .with_max_generations(100)
680 .with_target_fitness(-0.01);
681
682 let runner = GaRunner::new(config, SimpleProblem);
683 let result = runner.run();
684
685 assert!(result.best.value.abs() < 5.0);
687 }
688
689 #[test]
690 fn test_permutation_crossover() {
691 let mut rng = rand::rng();
692 let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
693 let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
694
695 let child = parent1.order_crossover(&parent2, &mut rng);
696
697 assert_eq!(child.genes.len(), 10);
699 let mut sorted = child.genes.clone();
700 sorted.sort();
701 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
702 }
703
704 #[test]
705 fn test_permutation_mutation() {
706 let mut rng = rand::rng();
707 let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
708
709 chromosome.swap_mutate(&mut rng);
710
711 let mut sorted = chromosome.genes.clone();
713 sorted.sort();
714 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
715 }
716}