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) -> Vec<usize> {
252 let mut indices: Vec<usize> = (0..self.keys.len()).collect();
253 indices.sort_by(|&a, &b| {
254 self.keys[a]
255 .partial_cmp(&self.keys[b])
256 .unwrap_or(std::cmp::Ordering::Equal)
257 });
258 indices
259 }
260
261 pub fn decode_as_discrete(&self, key_idx: usize, num_options: usize) -> usize {
266 if key_idx >= self.keys.len() || num_options == 0 {
267 return 0;
268 }
269 let key = self.keys[key_idx].clamp(0.0, 0.9999999);
270 (key * num_options as f64) as usize
271 }
272
273 pub fn discrete_key(choice: usize, num_options: usize) -> f64 {
281 if num_options == 0 {
282 return 0.0;
283 }
284 (((choice as f64) + 0.5) / num_options as f64).clamp(0.0, 0.9999999)
285 }
286
287 pub fn permutation_keys(order: &[usize], num_items: usize) -> Vec<f64> {
291 let mut keys = vec![0.0; num_items];
292 let span = order.len().max(1) as f64;
293 for (rank, &item) in order.iter().enumerate() {
294 if let Some(key) = keys.get_mut(item) {
295 *key = ((rank as f64 + 0.5) / span).clamp(0.0, 0.9999999);
296 }
297 }
298 keys
299 }
300}
301
302pub trait BrkgaProblem: Send + Sync {
304 fn num_keys(&self) -> usize;
306
307 fn evaluate(&self, chromosome: &mut RandomKeyChromosome);
309
310 fn evaluate_parallel(&self, chromosomes: &mut [RandomKeyChromosome]) {
313 #[cfg(feature = "parallel")]
314 chromosomes.par_iter_mut().for_each(|c| {
315 self.evaluate(c);
316 });
317 #[cfg(not(feature = "parallel"))]
318 for c in chromosomes.iter_mut() {
319 self.evaluate(c);
320 }
321 }
322
323 fn initial_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<RandomKeyChromosome> {
332 (0..size)
333 .map(|_| RandomKeyChromosome::random(self.num_keys(), rng))
334 .collect()
335 }
336
337 fn on_generation(
339 &self,
340 _generation: u32,
341 _best: &RandomKeyChromosome,
342 _population: &[RandomKeyChromosome],
343 ) {
344 }
346}
347
348#[derive(Debug, Clone)]
350pub struct BrkgaResult {
351 pub best: RandomKeyChromosome,
353 pub generations: u32,
355 pub elapsed: Duration,
357 pub stop_reason: StopReason,
359 pub history: Vec<f64>,
361}
362
363#[derive(Debug, Clone)]
365pub struct BrkgaProgress {
366 pub generation: u32,
368 pub max_generations: u32,
370 pub best_fitness: f64,
372 pub avg_fitness: f64,
374 pub elapsed: Duration,
376 pub running: bool,
378}
379
380pub struct BrkgaRunner<P: BrkgaProblem> {
382 config: BrkgaConfig,
383 problem: P,
384 cancelled: Arc<AtomicBool>,
385}
386
387impl<P: BrkgaProblem> BrkgaRunner<P> {
388 pub fn new(config: BrkgaConfig, problem: P) -> Result<Self> {
393 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
394 }
395
396 pub fn with_cancellation(
401 config: BrkgaConfig,
402 problem: P,
403 cancelled: Arc<AtomicBool>,
404 ) -> Result<Self> {
405 config.validate()?;
406 Ok(Self {
407 config,
408 problem,
409 cancelled,
410 })
411 }
412
413 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
415 self.cancelled.clone()
416 }
417
418 pub fn run(&self) -> BrkgaResult {
420 self.run_with_rng(&mut rand::rng())
421 }
422
423 pub fn run_with_progress<F>(&self, progress_callback: F) -> BrkgaResult
425 where
426 F: Fn(BrkgaProgress),
427 {
428 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
429 }
430
431 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> BrkgaResult {
433 self.run_with_rng_and_progress::<R, fn(BrkgaProgress)>(rng, None)
434 }
435
436 pub fn run_with_rng_and_progress<R: Rng, F>(
438 &self,
439 rng: &mut R,
440 progress_callback: Option<F>,
441 ) -> BrkgaResult
442 where
443 F: Fn(BrkgaProgress),
444 {
445 let start = Timer::now();
446 let mut history = Vec::new();
447 let num_keys = self.problem.num_keys();
448
449 let mut population = self
451 .problem
452 .initial_population(self.config.population_size, rng);
453 population.truncate(self.config.population_size);
456 while population.len() < self.config.population_size {
457 population.push(RandomKeyChromosome::random(num_keys, rng));
458 }
459
460 let heartbeat = |generation: u32, best: f64, batch: &[RandomKeyChromosome]| {
465 let best = batch.iter().map(|c| c.fitness()).fold(best, f64::max);
466 if let Some(ref callback) = progress_callback {
467 callback(BrkgaProgress {
468 generation,
469 max_generations: self.config.max_generations,
470 best_fitness: best,
471 avg_fitness: batch.iter().map(|c| c.fitness()).sum::<f64>()
472 / batch.len().max(1) as f64,
473 elapsed: start.elapsed(),
474 running: true,
475 });
476 }
477 best
478 };
479
480 let mut best_so_far = f64::NEG_INFINITY;
482 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
483 self.problem.evaluate_parallel(batch);
484 best_so_far = heartbeat(0, best_so_far, batch);
485 });
486
487 population.sort_by(|a, b| {
489 b.fitness()
490 .partial_cmp(&a.fitness())
491 .unwrap_or(std::cmp::Ordering::Equal)
492 });
493
494 let mut best = population[0].clone();
495 let mut best_fitness = best.fitness();
496 let mut stagnation_count = 0u32;
497 let mut generation = 0u32;
498 let reached = |fitness: f64| self.config.target_fitness.is_some_and(|t| fitness >= t);
499
500 let elite_count = self.config.elite_count();
501 let mutant_count = self.config.mutant_count();
502
503 let stop_reason = loop {
507 if reached(best_fitness) {
508 break StopReason::TargetReached;
509 }
510 if generation >= self.config.max_generations {
511 break StopReason::IterationLimit;
512 }
513 if self.cancelled.load(Ordering::Relaxed) {
514 break StopReason::Cancelled;
515 }
516 if expired(&start, self.config.time_limit) {
517 break StopReason::TimeLimit;
518 }
519
520 history.push(best_fitness);
522
523 let mut new_population = Vec::with_capacity(self.config.population_size);
525
526 for elite in population.iter().take(elite_count) {
528 new_population.push(elite.clone());
529 }
530
531 let mut mutants: Vec<RandomKeyChromosome> = (0..mutant_count)
533 .map(|_| RandomKeyChromosome::random(num_keys, rng))
534 .collect();
535
536 let crossover_count = self.config.population_size - elite_count - mutant_count;
538 let mut children: Vec<RandomKeyChromosome> = (0..crossover_count)
539 .map(|_| {
540 let elite_idx = rng.random_range(0..elite_count);
542 let elite_parent = &population[elite_idx];
543
544 let non_elite_idx = rng.random_range(elite_count..population.len());
546 let non_elite_parent = &population[non_elite_idx];
547
548 elite_parent.biased_crossover(non_elite_parent, self.config.elite_bias, rng)
550 })
551 .collect();
552
553 evaluate_within(&mut mutants, &start, self.config.time_limit, |batch| {
556 self.problem.evaluate_parallel(batch);
557 heartbeat(generation, best_fitness, batch);
558 });
559 if expired(&start, self.config.time_limit) {
560 children.clear();
561 }
562 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
563 self.problem.evaluate_parallel(batch);
564 heartbeat(generation, best_fitness, batch);
565 });
566
567 new_population.extend(mutants);
569 new_population.extend(children);
570
571 new_population.sort_by(|a, b| {
573 b.fitness()
574 .partial_cmp(&a.fitness())
575 .unwrap_or(std::cmp::Ordering::Equal)
576 });
577
578 let new_best_fitness = new_population[0].fitness();
580 if new_best_fitness > best_fitness {
581 best = new_population[0].clone();
582 best_fitness = new_best_fitness;
583 stagnation_count = 0;
584 } else {
585 stagnation_count += 1;
586 }
587
588 if let Some(limit) = self.config.stagnation_limit {
590 if stagnation_count >= limit {
591 break StopReason::NoImprovement;
592 }
593 }
594
595 self.problem
597 .on_generation(generation, &best, &new_population);
598
599 if let Some(ref callback) = progress_callback {
601 let avg_fitness = new_population.iter().map(|c| c.fitness()).sum::<f64>()
602 / new_population.len() as f64;
603
604 callback(BrkgaProgress {
605 generation,
606 max_generations: self.config.max_generations,
607 best_fitness,
608 avg_fitness,
609 elapsed: start.elapsed(),
610 running: true,
611 });
612 }
613
614 population = new_population;
615 generation += 1;
616 };
617
618 history.push(best_fitness);
620
621 if let Some(ref callback) = progress_callback {
623 let avg_fitness = population.iter().map(|c| c.fitness()).sum::<f64>()
624 / population.len().max(1) as f64;
625
626 callback(BrkgaProgress {
627 generation,
628 max_generations: self.config.max_generations,
629 best_fitness,
630 avg_fitness,
631 elapsed: start.elapsed(),
632 running: false,
633 });
634 }
635
636 BrkgaResult {
637 best,
638 generations: generation,
639 elapsed: start.elapsed(),
640 stop_reason,
641 history,
642 }
643 }
644}
645
646#[cfg(test)]
647mod tests {
648 use super::*;
649
650 struct MaxSumProblem {
652 num_keys: usize,
653 }
654
655 impl BrkgaProblem for MaxSumProblem {
656 fn num_keys(&self) -> usize {
657 self.num_keys
658 }
659
660 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
661 let sum: f64 = chromosome.keys.iter().sum();
662 chromosome.set_fitness(sum);
663 }
664 }
665
666 #[test]
667 fn test_brkga_basic() {
668 let config = BrkgaConfig::default()
669 .with_population_size(50)
670 .with_max_generations(50);
671
672 let problem = MaxSumProblem { num_keys: 10 };
673 let runner = BrkgaRunner::new(config, problem).expect("valid config");
674 let result = runner.run();
675
676 assert!(result.best.fitness() > 5.0);
678 }
679
680 #[test]
681 fn test_random_key_chromosome() {
682 let mut rng = rand::rng();
683 let chromosome = RandomKeyChromosome::random(10, &mut rng);
684
685 assert_eq!(chromosome.len(), 10);
686 for &key in &chromosome.keys {
687 assert!((0.0..1.0).contains(&key));
688 }
689 }
690
691 #[test]
692 fn test_biased_crossover() {
693 let mut rng = rand::rng();
694 let elite = RandomKeyChromosome::random(10, &mut rng);
695 let non_elite = RandomKeyChromosome::random(10, &mut rng);
696
697 let child = elite.biased_crossover(&non_elite, 0.7, &mut rng);
698
699 assert_eq!(child.len(), 10);
700 for &key in &child.keys {
701 assert!((0.0..1.0).contains(&key));
702 }
703 }
704
705 #[test]
706 fn test_decode_as_permutation() {
707 let mut chromosome = RandomKeyChromosome::new(5);
708 chromosome.keys = vec![0.3, 0.1, 0.9, 0.5, 0.2];
709
710 let perm = chromosome.decode_as_permutation();
711
712 assert_eq!(perm, vec![1, 4, 0, 3, 2]);
714 }
715
716 #[test]
717 fn test_decode_as_discrete() {
718 let mut chromosome = RandomKeyChromosome::new(3);
719 chromosome.keys = vec![0.0, 0.5, 0.99];
720
721 assert_eq!(chromosome.decode_as_discrete(0, 6), 0);
723 assert_eq!(chromosome.decode_as_discrete(1, 6), 3);
724 assert_eq!(chromosome.decode_as_discrete(2, 6), 5);
725 }
726}