1use rand::prelude::*;
4use rayon::prelude::*;
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::Arc;
7use std::time::{Duration, Instant};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15pub struct GaConfig {
16 pub population_size: usize,
18 pub max_generations: u32,
20 pub crossover_rate: f64,
22 pub mutation_rate: f64,
24 pub elite_count: usize,
26 pub tournament_size: usize,
28 pub time_limit: Option<Duration>,
30 pub target_fitness: Option<f64>,
32 pub stagnation_limit: Option<u32>,
34}
35
36impl Default for GaConfig {
37 fn default() -> Self {
38 Self {
39 population_size: 100,
40 max_generations: 500,
41 crossover_rate: 0.85,
42 mutation_rate: 0.05,
43 elite_count: 5,
44 tournament_size: 3,
45 time_limit: None,
46 target_fitness: None,
47 stagnation_limit: Some(50),
48 }
49 }
50}
51
52impl GaConfig {
53 pub fn new() -> Self {
55 Self::default()
56 }
57
58 pub fn with_population_size(mut self, size: usize) -> Self {
60 self.population_size = size.max(2);
61 self
62 }
63
64 pub fn with_max_generations(mut self, gen: u32) -> Self {
66 self.max_generations = gen;
67 self
68 }
69
70 pub fn with_crossover_rate(mut self, rate: f64) -> Self {
72 self.crossover_rate = rate.clamp(0.0, 1.0);
73 self
74 }
75
76 pub fn with_mutation_rate(mut self, rate: f64) -> Self {
78 self.mutation_rate = rate.clamp(0.0, 1.0);
79 self
80 }
81
82 pub fn with_elite_count(mut self, count: usize) -> Self {
84 self.elite_count = count;
85 self
86 }
87
88 pub fn with_time_limit(mut self, duration: Duration) -> Self {
90 self.time_limit = Some(duration);
91 self
92 }
93
94 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
96 self.target_fitness = Some(fitness);
97 self
98 }
99}
100
101pub trait Individual: Clone + Send + Sync {
103 type Fitness: PartialOrd + Copy + Send;
105
106 fn fitness(&self) -> Self::Fitness;
108
109 fn random<R: Rng>(rng: &mut R) -> Self;
111
112 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
114
115 fn mutate<R: Rng>(&mut self, rng: &mut R);
117}
118
119pub trait GaProblem: Send + Sync {
121 type Individual: Individual;
123
124 fn evaluate(&self, individual: &mut Self::Individual);
126
127 fn evaluate_parallel(&self, individuals: &mut [Self::Individual]) {
130 individuals.par_iter_mut().for_each(|ind| {
131 self.evaluate(ind);
132 });
133 }
134
135 fn initialize_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<Self::Individual> {
137 (0..size).map(|_| Self::Individual::random(rng)).collect()
138 }
139
140 fn on_generation(
142 &self,
143 _generation: u32,
144 _best: &Self::Individual,
145 _population: &[Self::Individual],
146 ) {
147 }
149}
150
151#[derive(Debug, Clone)]
153pub struct GaProgress<F> {
154 pub generation: u32,
156 pub max_generations: u32,
158 pub best_fitness: F,
160 pub avg_fitness: f64,
162 pub elapsed: Duration,
164 pub running: bool,
166}
167
168#[derive(Debug, Clone)]
170pub struct GaResult<I: Individual> {
171 pub best: I,
173 pub generations: u32,
175 pub elapsed: Duration,
177 pub target_reached: bool,
179 pub history: Vec<f64>,
181}
182
183pub struct GaRunner<P: GaProblem> {
185 config: GaConfig,
186 problem: P,
187 cancelled: Arc<AtomicBool>,
188}
189
190impl<P: GaProblem> GaRunner<P>
191where
192 <P::Individual as Individual>::Fitness: Into<f64>,
193{
194 pub fn new(config: GaConfig, problem: P) -> Self {
196 Self {
197 config,
198 problem,
199 cancelled: Arc::new(AtomicBool::new(false)),
200 }
201 }
202
203 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
205 self.cancelled.clone()
206 }
207
208 pub fn run(&self) -> GaResult<P::Individual> {
210 self.run_with_rng(&mut thread_rng())
211 }
212
213 pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
215 where
216 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
217 {
218 self.run_with_rng_and_progress(&mut thread_rng(), Some(progress_callback))
219 }
220
221 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
223 self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
224 rng, None,
225 )
226 }
227
228 pub fn run_with_rng_and_progress<R: Rng, F>(
230 &self,
231 rng: &mut R,
232 progress_callback: Option<F>,
233 ) -> GaResult<P::Individual>
234 where
235 F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
236 {
237 let start = Instant::now();
238 let mut history = Vec::new();
239
240 let mut population = self
242 .problem
243 .initialize_population(self.config.population_size, rng);
244
245 self.problem.evaluate_parallel(&mut population);
247
248 population.sort_by(|a, b| {
250 b.fitness()
251 .partial_cmp(&a.fitness())
252 .unwrap_or(std::cmp::Ordering::Equal)
253 });
254
255 let mut best = population[0].clone();
256 let mut best_fitness: f64 = best.fitness().into();
257 let mut stagnation_count = 0u32;
258 let mut generation = 0u32;
259 let mut target_reached = false;
260
261 while generation < self.config.max_generations {
262 if self.cancelled.load(Ordering::Relaxed) {
264 break;
265 }
266
267 if let Some(limit) = self.config.time_limit {
269 if start.elapsed() > limit {
270 break;
271 }
272 }
273
274 if let Some(target) = self.config.target_fitness {
276 if best_fitness >= target {
277 target_reached = true;
278 break;
279 }
280 }
281
282 history.push(best_fitness);
284
285 let mut new_population = Vec::with_capacity(self.config.population_size);
287
288 for individual in population
290 .iter()
291 .take(self.config.elite_count.min(population.len()))
292 {
293 new_population.push(individual.clone());
294 }
295
296 let mut children: Vec<P::Individual> =
299 Vec::with_capacity(self.config.population_size - new_population.len());
300
301 while children.len() < self.config.population_size - new_population.len() {
302 let parent1 = self.tournament_select(&population, rng);
304 let parent2 = self.tournament_select(&population, rng);
305
306 let mut child = if rng.gen::<f64>() < self.config.crossover_rate {
308 parent1.crossover(parent2, rng)
309 } else {
310 parent1.clone()
311 };
312
313 if rng.gen::<f64>() < self.config.mutation_rate {
315 child.mutate(rng);
316 }
317
318 children.push(child);
319 }
320
321 self.problem.evaluate_parallel(&mut children);
323
324 new_population.extend(children);
326
327 new_population.sort_by(|a, b| {
329 b.fitness()
330 .partial_cmp(&a.fitness())
331 .unwrap_or(std::cmp::Ordering::Equal)
332 });
333
334 let new_best_fitness: f64 = new_population[0].fitness().into();
336 if new_best_fitness > best_fitness {
337 best = new_population[0].clone();
338 best_fitness = new_best_fitness;
339 stagnation_count = 0;
340 } else {
341 stagnation_count += 1;
342 }
343
344 if let Some(limit) = self.config.stagnation_limit {
346 if stagnation_count >= limit {
347 break;
348 }
349 }
350
351 self.problem
353 .on_generation(generation, &best, &new_population);
354
355 if let Some(ref callback) = progress_callback {
357 let avg_fitness = new_population
358 .iter()
359 .map(|ind| ind.fitness().into())
360 .sum::<f64>()
361 / new_population.len() as f64;
362
363 callback(GaProgress {
364 generation,
365 max_generations: self.config.max_generations,
366 best_fitness: best.fitness(),
367 avg_fitness,
368 elapsed: start.elapsed(),
369 running: true,
370 });
371 }
372
373 population = new_population;
374 generation += 1;
375 }
376
377 history.push(best_fitness);
379
380 if let Some(ref callback) = progress_callback {
382 let avg_fitness = population
383 .iter()
384 .map(|ind| ind.fitness().into())
385 .sum::<f64>()
386 / population.len().max(1) as f64;
387
388 callback(GaProgress {
389 generation,
390 max_generations: self.config.max_generations,
391 best_fitness: best.fitness(),
392 avg_fitness,
393 elapsed: start.elapsed(),
394 running: false,
395 });
396 }
397
398 GaResult {
399 best,
400 generations: generation,
401 elapsed: start.elapsed(),
402 target_reached,
403 history,
404 }
405 }
406
407 fn tournament_select<'a, R: Rng>(
409 &self,
410 population: &'a [P::Individual],
411 rng: &mut R,
412 ) -> &'a P::Individual {
413 let mut best_idx = rng.gen_range(0..population.len());
414
415 for _ in 1..self.config.tournament_size {
416 let idx = rng.gen_range(0..population.len());
417 if population[idx].fitness() > population[best_idx].fitness() {
418 best_idx = idx;
419 }
420 }
421
422 &population[best_idx]
423 }
424}
425
426#[derive(Debug, Clone)]
428pub struct PermutationChromosome {
429 pub genes: Vec<usize>,
431 pub rotations: Vec<usize>,
433 fitness: f64,
435}
436
437impl PermutationChromosome {
438 pub fn new(size: usize, _rotation_options: usize) -> Self {
440 Self {
441 genes: (0..size).collect(),
442 rotations: vec![0; size],
443 fitness: f64::NEG_INFINITY,
444 }
445 }
446
447 pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
449 let mut genes: Vec<usize> = (0..size).collect();
450 genes.shuffle(rng);
451
452 let rotations: Vec<usize> = (0..size)
453 .map(|_| rng.gen_range(0..rotation_options.max(1)))
454 .collect();
455
456 Self {
457 genes,
458 rotations,
459 fitness: f64::NEG_INFINITY,
460 }
461 }
462
463 pub fn set_fitness(&mut self, fitness: f64) {
465 self.fitness = fitness;
466 }
467
468 pub fn len(&self) -> usize {
470 self.genes.len()
471 }
472
473 pub fn is_empty(&self) -> bool {
475 self.genes.is_empty()
476 }
477
478 pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
480 let n = self.genes.len();
481 if n < 2 {
482 return self.clone();
483 }
484
485 let (mut p1, mut p2) = (rng.gen_range(0..n), rng.gen_range(0..n));
487 if p1 > p2 {
488 std::mem::swap(&mut p1, &mut p2);
489 }
490
491 let mut child_genes = vec![usize::MAX; n];
493 let mut used = vec![false; n];
494
495 for i in p1..=p2 {
496 child_genes[i] = self.genes[i];
497 used[self.genes[i]] = true;
498 }
499
500 let mut j = (p2 + 1) % n;
502 for i in 0..n {
503 let idx = (p2 + 1 + i) % n;
504 if child_genes[idx] == usize::MAX {
505 while used[other.genes[j]] {
506 j = (j + 1) % n;
507 }
508 child_genes[idx] = other.genes[j];
509 used[other.genes[j]] = true;
510 j = (j + 1) % n;
511 }
512 }
513
514 let rotations: Vec<usize> = self
516 .rotations
517 .iter()
518 .zip(&other.rotations)
519 .map(|(a, b)| if rng.gen() { *a } else { *b })
520 .collect();
521
522 Self {
523 genes: child_genes,
524 rotations,
525 fitness: f64::NEG_INFINITY,
526 }
527 }
528
529 pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
531 if self.genes.len() < 2 {
532 return;
533 }
534
535 let i = rng.gen_range(0..self.genes.len());
536 let j = rng.gen_range(0..self.genes.len());
537 self.genes.swap(i, j);
538 self.fitness = f64::NEG_INFINITY;
539 }
540
541 pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
543 if self.rotations.is_empty() || rotation_options <= 1 {
544 return;
545 }
546
547 let idx = rng.gen_range(0..self.rotations.len());
548 self.rotations[idx] = rng.gen_range(0..rotation_options);
549 self.fitness = f64::NEG_INFINITY;
550 }
551
552 pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
554 let n = self.genes.len();
555 if n < 2 {
556 return;
557 }
558
559 let (mut p1, mut p2) = (rng.gen_range(0..n), rng.gen_range(0..n));
560 if p1 > p2 {
561 std::mem::swap(&mut p1, &mut p2);
562 }
563
564 self.genes[p1..=p2].reverse();
565 self.fitness = f64::NEG_INFINITY;
566 }
567}
568
569impl Individual for PermutationChromosome {
570 type Fitness = f64;
571
572 fn fitness(&self) -> f64 {
573 self.fitness
574 }
575
576 fn random<R: Rng>(rng: &mut R) -> Self {
577 Self::random_with_options(0, 1, rng)
579 }
580
581 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
582 self.order_crossover(other, rng)
583 }
584
585 fn mutate<R: Rng>(&mut self, rng: &mut R) {
586 if rng.gen::<f64>() < 0.7 {
588 self.swap_mutate(rng);
589 } else {
590 self.inversion_mutate(rng);
591 }
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[derive(Clone)]
600 struct SimpleIndividual {
601 value: f64,
602 }
603
604 impl Individual for SimpleIndividual {
605 type Fitness = f64;
606
607 fn fitness(&self) -> f64 {
608 -self.value * self.value
610 }
611
612 fn random<R: Rng>(rng: &mut R) -> Self {
613 Self {
614 value: rng.gen_range(-100.0..100.0),
615 }
616 }
617
618 fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
619 Self {
620 value: if rng.gen() { self.value } else { other.value },
621 }
622 }
623
624 fn mutate<R: Rng>(&mut self, rng: &mut R) {
625 self.value += rng.gen_range(-10.0..10.0);
626 }
627 }
628
629 struct SimpleProblem;
630
631 impl GaProblem for SimpleProblem {
632 type Individual = SimpleIndividual;
633
634 fn evaluate(&self, _individual: &mut Self::Individual) {
635 }
637 }
638
639 #[test]
640 fn test_ga_basic() {
641 let config = GaConfig::default()
642 .with_population_size(50)
643 .with_max_generations(100)
644 .with_target_fitness(-0.01);
645
646 let runner = GaRunner::new(config, SimpleProblem);
647 let result = runner.run();
648
649 assert!(result.best.value.abs() < 5.0);
651 }
652
653 #[test]
654 fn test_permutation_crossover() {
655 let mut rng = thread_rng();
656 let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
657 let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
658
659 let child = parent1.order_crossover(&parent2, &mut rng);
660
661 assert_eq!(child.genes.len(), 10);
663 let mut sorted = child.genes.clone();
664 sorted.sort();
665 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
666 }
667
668 #[test]
669 fn test_permutation_mutation() {
670 let mut rng = thread_rng();
671 let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
672
673 chromosome.swap_mutate(&mut rng);
674
675 let mut sorted = chromosome.genes.clone();
677 sorted.sort();
678 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
679 }
680}