1use rand::prelude::*;
28#[cfg(feature = "parallel")]
29use rayon::prelude::*;
30use std::sync::atomic::{AtomicBool, Ordering};
31use std::sync::Arc;
32use std::time::Duration;
33
34use crate::error::{check_at_least, check_range, Result};
35use crate::result::StopReason;
36use crate::timing::{evaluate_within, expired, Timer};
37
38#[cfg(feature = "serde")]
39use serde::{Deserialize, Serialize};
40
41#[derive(Debug, Clone)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44pub struct BrkgaConfig {
45 pub population_size: usize,
47 pub max_generations: u32,
49 pub elite_fraction: f64,
51 pub mutant_fraction: f64,
53 pub elite_bias: f64,
55 pub time_limit: Option<Duration>,
57 pub target_fitness: Option<f64>,
59 pub stagnation_limit: Option<u32>,
61}
62
63impl Default for BrkgaConfig {
64 fn default() -> Self {
65 Self {
66 population_size: 100,
67 max_generations: 500,
68 elite_fraction: 0.2, mutant_fraction: 0.15, elite_bias: 0.7, time_limit: None,
72 target_fitness: None,
73 stagnation_limit: Some(50),
74 }
75 }
76}
77
78impl BrkgaConfig {
79 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub fn with_population_size(mut self, size: usize) -> Self {
86 self.population_size = size;
87 self
88 }
89
90 pub fn with_max_generations(mut self, gen: u32) -> Self {
92 self.max_generations = gen;
93 self
94 }
95
96 pub fn with_elite_fraction(mut self, fraction: f64) -> Self {
98 self.elite_fraction = fraction;
99 self
100 }
101
102 pub fn with_mutant_fraction(mut self, fraction: f64) -> Self {
104 self.mutant_fraction = fraction;
105 self
106 }
107
108 pub fn with_elite_bias(mut self, bias: f64) -> Self {
110 self.elite_bias = bias;
111 self
112 }
113
114 pub fn validate(&self) -> Result<()> {
125 check_range("elite_fraction", self.elite_fraction, 0.01, 0.5)?;
126 check_range("mutant_fraction", self.mutant_fraction, 0.0, 0.5)?;
127 check_range("elite_bias", self.elite_bias, 0.5, 1.0)?;
128 check_at_least("population_size", self.population_size as f64, 2.0)?;
129 let (elite, mutants) = (self.elite_count(), self.mutant_count());
130 if elite + mutants > self.population_size {
131 return Err(crate::Error::invalid_option(
132 "population_size",
133 format!(
134 "a population of {} has no room for {elite} elite (elite_fraction {}) \
135 and {mutants} mutant (mutant_fraction {}) individuals; give a larger \
136 population or smaller fractions",
137 self.population_size, self.elite_fraction, self.mutant_fraction
138 ),
139 ));
140 }
141 Ok(())
142 }
143
144 pub fn with_time_limit(mut self, duration: Duration) -> Self {
146 self.time_limit = Some(duration);
147 self
148 }
149
150 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
152 self.target_fitness = Some(fitness);
153 self
154 }
155
156 pub fn with_stagnation_limit(mut self, limit: u32) -> Self {
158 self.stagnation_limit = Some(limit);
159 self
160 }
161
162 pub fn elite_count(&self) -> usize {
164 ((self.population_size as f64) * self.elite_fraction).ceil() as usize
165 }
166
167 pub fn mutant_count(&self) -> usize {
169 ((self.population_size as f64) * self.mutant_fraction).ceil() as usize
170 }
171}
172
173#[derive(Debug, Clone)]
178pub struct RandomKeyChromosome {
179 pub keys: Vec<f64>,
181 fitness: f64,
183}
184
185impl RandomKeyChromosome {
186 pub fn new(num_keys: usize) -> Self {
188 Self {
189 keys: vec![0.0; num_keys],
190 fitness: f64::NEG_INFINITY,
191 }
192 }
193
194 pub fn random<R: Rng>(num_keys: usize, rng: &mut R) -> Self {
196 let keys: Vec<f64> = (0..num_keys).map(|_| rng.random::<f64>()).collect();
197 Self {
198 keys,
199 fitness: f64::NEG_INFINITY,
200 }
201 }
202
203 pub fn len(&self) -> usize {
205 self.keys.len()
206 }
207
208 pub fn is_empty(&self) -> bool {
210 self.keys.is_empty()
211 }
212
213 pub fn fitness(&self) -> f64 {
215 self.fitness
216 }
217
218 pub fn set_fitness(&mut self, fitness: f64) {
220 self.fitness = fitness;
221 }
222
223 pub fn biased_crossover<R: Rng>(&self, other: &Self, elite_bias: f64, rng: &mut R) -> Self {
228 let keys: Vec<f64> = self
229 .keys
230 .iter()
231 .zip(&other.keys)
232 .map(|(&elite_key, &non_elite_key)| {
233 if rng.random::<f64>() < elite_bias {
234 elite_key
235 } else {
236 non_elite_key
237 }
238 })
239 .collect();
240
241 Self {
242 keys,
243 fitness: f64::NEG_INFINITY,
244 }
245 }
246
247 pub fn decode_as_permutation(&self, n: usize) -> Vec<usize> {
256 let n = n.min(self.keys.len());
257 let mut indices: Vec<usize> = (0..n).collect();
258 indices.sort_by(|&a, &b| {
259 self.keys[a]
260 .partial_cmp(&self.keys[b])
261 .unwrap_or(std::cmp::Ordering::Equal)
262 });
263 indices
264 }
265
266 pub fn decode_as_discrete(&self, key_idx: usize, num_options: usize) -> usize {
271 if key_idx >= self.keys.len() || num_options == 0 {
272 return 0;
273 }
274 let key = self.keys[key_idx].clamp(0.0, 0.9999999);
275 (key * num_options as f64) as usize
276 }
277
278 pub fn discrete_key(choice: usize, num_options: usize) -> f64 {
286 if num_options == 0 {
287 return 0.0;
288 }
289 (((choice as f64) + 0.5) / num_options as f64).clamp(0.0, 0.9999999)
290 }
291
292 pub fn permutation_keys(order: &[usize], num_items: usize) -> Vec<f64> {
296 let mut keys = vec![0.0; num_items];
297 let span = order.len().max(1) as f64;
298 for (rank, &item) in order.iter().enumerate() {
299 if let Some(key) = keys.get_mut(item) {
300 *key = ((rank as f64 + 0.5) / span).clamp(0.0, 0.9999999);
301 }
302 }
303 keys
304 }
305}
306
307pub trait BrkgaProblem: Send + Sync {
309 fn num_keys(&self) -> usize;
311
312 fn evaluate(&self, chromosome: &mut RandomKeyChromosome);
314
315 fn evaluate_parallel(&self, chromosomes: &mut [RandomKeyChromosome]) {
318 #[cfg(feature = "parallel")]
319 chromosomes.par_iter_mut().for_each(|c| {
320 self.evaluate(c);
321 });
322 #[cfg(not(feature = "parallel"))]
323 for c in chromosomes.iter_mut() {
324 self.evaluate(c);
325 }
326 }
327
328 fn initial_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<RandomKeyChromosome> {
337 (0..size)
338 .map(|_| RandomKeyChromosome::random(self.num_keys(), rng))
339 .collect()
340 }
341
342 fn on_generation(
344 &self,
345 _generation: u32,
346 _best: &RandomKeyChromosome,
347 _population: &[RandomKeyChromosome],
348 ) {
349 }
351}
352
353#[derive(Debug, Clone)]
355pub struct BrkgaResult {
356 pub best: RandomKeyChromosome,
358 pub generations: u32,
360 pub elapsed: Duration,
362 pub stop_reason: StopReason,
364 pub history: Vec<f64>,
366}
367
368#[derive(Debug, Clone)]
370pub struct BrkgaProgress {
371 pub generation: u32,
373 pub max_generations: u32,
375 pub best_fitness: f64,
377 pub avg_fitness: f64,
379 pub elapsed: Duration,
381 pub running: bool,
383}
384
385pub struct BrkgaRunner<P: BrkgaProblem> {
387 config: BrkgaConfig,
388 problem: P,
389 cancelled: Arc<AtomicBool>,
390}
391
392impl<P: BrkgaProblem> BrkgaRunner<P> {
393 pub fn new(config: BrkgaConfig, problem: P) -> Result<Self> {
398 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
399 }
400
401 pub fn with_cancellation(
406 config: BrkgaConfig,
407 problem: P,
408 cancelled: Arc<AtomicBool>,
409 ) -> Result<Self> {
410 config.validate()?;
411 Ok(Self {
412 config,
413 problem,
414 cancelled,
415 })
416 }
417
418 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
420 self.cancelled.clone()
421 }
422
423 pub fn run(&self) -> BrkgaResult {
425 self.run_with_rng(&mut rand::rng())
426 }
427
428 pub fn run_with_progress<F>(&self, progress_callback: F) -> BrkgaResult
430 where
431 F: Fn(BrkgaProgress),
432 {
433 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
434 }
435
436 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> BrkgaResult {
438 self.run_with_rng_and_progress::<R, fn(BrkgaProgress)>(rng, None)
439 }
440
441 pub fn run_with_rng_and_progress<R: Rng, F>(
443 &self,
444 rng: &mut R,
445 progress_callback: Option<F>,
446 ) -> BrkgaResult
447 where
448 F: Fn(BrkgaProgress),
449 {
450 let start = Timer::now();
451 let mut history = Vec::new();
452 let num_keys = self.problem.num_keys();
453
454 let mut population = self
456 .problem
457 .initial_population(self.config.population_size, rng);
458 population.truncate(self.config.population_size);
461 while population.len() < self.config.population_size {
462 population.push(RandomKeyChromosome::random(num_keys, rng));
463 }
464
465 let heartbeat = |generation: u32, best: f64, batch: &[RandomKeyChromosome]| {
470 let best = batch.iter().map(|c| c.fitness()).fold(best, f64::max);
471 if let Some(ref callback) = progress_callback {
472 callback(BrkgaProgress {
473 generation,
474 max_generations: self.config.max_generations,
475 best_fitness: best,
476 avg_fitness: batch.iter().map(|c| c.fitness()).sum::<f64>()
477 / batch.len().max(1) as f64,
478 elapsed: start.elapsed(),
479 running: true,
480 });
481 }
482 best
483 };
484
485 let mut best_so_far = f64::NEG_INFINITY;
487 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
488 self.problem.evaluate_parallel(batch);
489 best_so_far = heartbeat(0, best_so_far, batch);
490 });
491
492 population.sort_by(|a, b| {
494 b.fitness()
495 .partial_cmp(&a.fitness())
496 .unwrap_or(std::cmp::Ordering::Equal)
497 });
498
499 let mut best = population[0].clone();
500 let mut best_fitness = best.fitness();
501 let mut stagnation_count = 0u32;
502 let mut generation = 0u32;
503 let reached = |fitness: f64| self.config.target_fitness.is_some_and(|t| fitness >= t);
504
505 let elite_count = self.config.elite_count();
506 let mutant_count = self.config.mutant_count();
507
508 let stop_reason = loop {
512 if reached(best_fitness) {
513 break StopReason::TargetReached;
514 }
515 if generation >= self.config.max_generations {
516 break StopReason::IterationLimit;
517 }
518 if self.cancelled.load(Ordering::Relaxed) {
519 break StopReason::Cancelled;
520 }
521 if expired(&start, self.config.time_limit) {
522 break StopReason::TimeLimit;
523 }
524
525 history.push(best_fitness);
527
528 let mut new_population = Vec::with_capacity(self.config.population_size);
530
531 for elite in population.iter().take(elite_count) {
533 new_population.push(elite.clone());
534 }
535
536 let mut mutants: Vec<RandomKeyChromosome> = (0..mutant_count)
538 .map(|_| RandomKeyChromosome::random(num_keys, rng))
539 .collect();
540
541 let crossover_count = self.config.population_size - elite_count - mutant_count;
543 let mut children: Vec<RandomKeyChromosome> = (0..crossover_count)
544 .map(|_| {
545 let elite_idx = rng.random_range(0..elite_count);
547 let elite_parent = &population[elite_idx];
548
549 let non_elite_idx = rng.random_range(elite_count..population.len());
551 let non_elite_parent = &population[non_elite_idx];
552
553 elite_parent.biased_crossover(non_elite_parent, self.config.elite_bias, rng)
555 })
556 .collect();
557
558 evaluate_within(&mut mutants, &start, self.config.time_limit, |batch| {
561 self.problem.evaluate_parallel(batch);
562 heartbeat(generation, best_fitness, batch);
563 });
564 if expired(&start, self.config.time_limit) {
565 children.clear();
566 }
567 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
568 self.problem.evaluate_parallel(batch);
569 heartbeat(generation, best_fitness, batch);
570 });
571
572 new_population.extend(mutants);
574 new_population.extend(children);
575
576 new_population.sort_by(|a, b| {
578 b.fitness()
579 .partial_cmp(&a.fitness())
580 .unwrap_or(std::cmp::Ordering::Equal)
581 });
582
583 let new_best_fitness = new_population[0].fitness();
585 if new_best_fitness > best_fitness {
586 best = new_population[0].clone();
587 best_fitness = new_best_fitness;
588 stagnation_count = 0;
589 } else {
590 stagnation_count += 1;
591 }
592
593 if let Some(limit) = self.config.stagnation_limit {
595 if stagnation_count >= limit {
596 break StopReason::NoImprovement;
597 }
598 }
599
600 self.problem
602 .on_generation(generation, &best, &new_population);
603
604 if let Some(ref callback) = progress_callback {
606 let avg_fitness = new_population.iter().map(|c| c.fitness()).sum::<f64>()
607 / new_population.len() as f64;
608
609 callback(BrkgaProgress {
610 generation,
611 max_generations: self.config.max_generations,
612 best_fitness,
613 avg_fitness,
614 elapsed: start.elapsed(),
615 running: true,
616 });
617 }
618
619 population = new_population;
620 generation += 1;
621 };
622
623 history.push(best_fitness);
625
626 if let Some(ref callback) = progress_callback {
628 let avg_fitness = population.iter().map(|c| c.fitness()).sum::<f64>()
629 / population.len().max(1) as f64;
630
631 callback(BrkgaProgress {
632 generation,
633 max_generations: self.config.max_generations,
634 best_fitness,
635 avg_fitness,
636 elapsed: start.elapsed(),
637 running: false,
638 });
639 }
640
641 BrkgaResult {
642 best,
643 generations: generation,
644 elapsed: start.elapsed(),
645 stop_reason,
646 history,
647 }
648 }
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654
655 struct MaxSumProblem {
657 num_keys: usize,
658 }
659
660 impl BrkgaProblem for MaxSumProblem {
661 fn num_keys(&self) -> usize {
662 self.num_keys
663 }
664
665 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
666 let sum: f64 = chromosome.keys.iter().sum();
667 chromosome.set_fitness(sum);
668 }
669 }
670
671 #[test]
672 fn test_brkga_basic() {
673 let config = BrkgaConfig::default()
674 .with_population_size(50)
675 .with_max_generations(50);
676
677 let problem = MaxSumProblem { num_keys: 10 };
678 let runner = BrkgaRunner::new(config, problem).expect("valid config");
679 let result = runner.run();
680
681 assert!(result.best.fitness() > 5.0);
683 }
684
685 #[test]
686 fn test_random_key_chromosome() {
687 let mut rng = rand::rng();
688 let chromosome = RandomKeyChromosome::random(10, &mut rng);
689
690 assert_eq!(chromosome.len(), 10);
691 for &key in &chromosome.keys {
692 assert!((0.0..1.0).contains(&key));
693 }
694 }
695
696 #[test]
697 fn test_biased_crossover() {
698 let mut rng = rand::rng();
699 let elite = RandomKeyChromosome::random(10, &mut rng);
700 let non_elite = RandomKeyChromosome::random(10, &mut rng);
701
702 let child = elite.biased_crossover(&non_elite, 0.7, &mut rng);
703
704 assert_eq!(child.len(), 10);
705 for &key in &child.keys {
706 assert!((0.0..1.0).contains(&key));
707 }
708 }
709
710 #[test]
711 fn test_decode_as_permutation() {
712 let mut chromosome = RandomKeyChromosome::new(5);
713 chromosome.keys = vec![0.3, 0.1, 0.9, 0.5, 0.2];
714
715 let perm = chromosome.decode_as_permutation(5);
716
717 assert_eq!(perm, vec![1, 4, 0, 3, 2]);
719 assert_eq!(chromosome.decode_as_permutation(3), vec![1, 0, 2]);
721 }
722
723 #[test]
724 fn test_decode_as_discrete() {
725 let mut chromosome = RandomKeyChromosome::new(3);
726 chromosome.keys = vec![0.0, 0.5, 0.99];
727
728 assert_eq!(chromosome.decode_as_discrete(0, 6), 0);
730 assert_eq!(chromosome.decode_as_discrete(1, 6), 3);
731 assert_eq!(chromosome.decode_as_discrete(2, 6), 5);
732 }
733}