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::timing::{evaluate_within, expired, Timer};
36
37#[cfg(feature = "serde")]
38use serde::{Deserialize, Serialize};
39
40#[derive(Debug, Clone)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub struct BrkgaConfig {
44 pub population_size: usize,
46 pub max_generations: u32,
48 pub elite_fraction: f64,
50 pub mutant_fraction: f64,
52 pub elite_bias: f64,
54 pub time_limit: Option<Duration>,
56 pub target_fitness: Option<f64>,
58 pub stagnation_limit: Option<u32>,
60}
61
62impl Default for BrkgaConfig {
63 fn default() -> Self {
64 Self {
65 population_size: 100,
66 max_generations: 500,
67 elite_fraction: 0.2, mutant_fraction: 0.15, elite_bias: 0.7, time_limit: None,
71 target_fitness: None,
72 stagnation_limit: Some(50),
73 }
74 }
75}
76
77impl BrkgaConfig {
78 pub fn new() -> Self {
80 Self::default()
81 }
82
83 pub fn with_population_size(mut self, size: usize) -> Self {
85 self.population_size = size;
86 self
87 }
88
89 pub fn with_max_generations(mut self, gen: u32) -> Self {
91 self.max_generations = gen;
92 self
93 }
94
95 pub fn with_elite_fraction(mut self, fraction: f64) -> Self {
97 self.elite_fraction = fraction;
98 self
99 }
100
101 pub fn with_mutant_fraction(mut self, fraction: f64) -> Self {
103 self.mutant_fraction = fraction;
104 self
105 }
106
107 pub fn with_elite_bias(mut self, bias: f64) -> Self {
109 self.elite_bias = bias;
110 self
111 }
112
113 pub fn validate(&self) -> Result<()> {
124 check_range("elite_fraction", self.elite_fraction, 0.01, 0.5)?;
125 check_range("mutant_fraction", self.mutant_fraction, 0.0, 0.5)?;
126 check_range("elite_bias", self.elite_bias, 0.5, 1.0)?;
127 check_at_least("population_size", self.population_size as f64, 2.0)?;
128 let (elite, mutants) = (self.elite_count(), self.mutant_count());
129 if elite + mutants > self.population_size {
130 return Err(crate::Error::invalid_option(
131 "population_size",
132 format!(
133 "a population of {} has no room for {elite} elite (elite_fraction {}) \
134 and {mutants} mutant (mutant_fraction {}) individuals; give a larger \
135 population or smaller fractions",
136 self.population_size, self.elite_fraction, self.mutant_fraction
137 ),
138 ));
139 }
140 Ok(())
141 }
142
143 pub fn with_time_limit(mut self, duration: Duration) -> Self {
145 self.time_limit = Some(duration);
146 self
147 }
148
149 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
151 self.target_fitness = Some(fitness);
152 self
153 }
154
155 pub fn with_stagnation_limit(mut self, limit: u32) -> Self {
157 self.stagnation_limit = Some(limit);
158 self
159 }
160
161 pub fn elite_count(&self) -> usize {
163 ((self.population_size as f64) * self.elite_fraction).ceil() as usize
164 }
165
166 pub fn mutant_count(&self) -> usize {
168 ((self.population_size as f64) * self.mutant_fraction).ceil() as usize
169 }
170}
171
172#[derive(Debug, Clone)]
177pub struct RandomKeyChromosome {
178 pub keys: Vec<f64>,
180 fitness: f64,
182}
183
184impl RandomKeyChromosome {
185 pub fn new(num_keys: usize) -> Self {
187 Self {
188 keys: vec![0.0; num_keys],
189 fitness: f64::NEG_INFINITY,
190 }
191 }
192
193 pub fn random<R: Rng>(num_keys: usize, rng: &mut R) -> Self {
195 let keys: Vec<f64> = (0..num_keys).map(|_| rng.random::<f64>()).collect();
196 Self {
197 keys,
198 fitness: f64::NEG_INFINITY,
199 }
200 }
201
202 pub fn len(&self) -> usize {
204 self.keys.len()
205 }
206
207 pub fn is_empty(&self) -> bool {
209 self.keys.is_empty()
210 }
211
212 pub fn fitness(&self) -> f64 {
214 self.fitness
215 }
216
217 pub fn set_fitness(&mut self, fitness: f64) {
219 self.fitness = fitness;
220 }
221
222 pub fn biased_crossover<R: Rng>(&self, other: &Self, elite_bias: f64, rng: &mut R) -> Self {
227 let keys: Vec<f64> = self
228 .keys
229 .iter()
230 .zip(&other.keys)
231 .map(|(&elite_key, &non_elite_key)| {
232 if rng.random::<f64>() < elite_bias {
233 elite_key
234 } else {
235 non_elite_key
236 }
237 })
238 .collect();
239
240 Self {
241 keys,
242 fitness: f64::NEG_INFINITY,
243 }
244 }
245
246 pub fn decode_as_permutation(&self) -> Vec<usize> {
251 let mut indices: Vec<usize> = (0..self.keys.len()).collect();
252 indices.sort_by(|&a, &b| {
253 self.keys[a]
254 .partial_cmp(&self.keys[b])
255 .unwrap_or(std::cmp::Ordering::Equal)
256 });
257 indices
258 }
259
260 pub fn decode_as_discrete(&self, key_idx: usize, num_options: usize) -> usize {
265 if key_idx >= self.keys.len() || num_options == 0 {
266 return 0;
267 }
268 let key = self.keys[key_idx].clamp(0.0, 0.9999999);
269 (key * num_options as f64) as usize
270 }
271
272 pub fn discrete_key(choice: usize, num_options: usize) -> f64 {
280 if num_options == 0 {
281 return 0.0;
282 }
283 (((choice as f64) + 0.5) / num_options as f64).clamp(0.0, 0.9999999)
284 }
285
286 pub fn permutation_keys(order: &[usize], num_items: usize) -> Vec<f64> {
290 let mut keys = vec![0.0; num_items];
291 let span = order.len().max(1) as f64;
292 for (rank, &item) in order.iter().enumerate() {
293 if let Some(key) = keys.get_mut(item) {
294 *key = ((rank as f64 + 0.5) / span).clamp(0.0, 0.9999999);
295 }
296 }
297 keys
298 }
299}
300
301pub trait BrkgaProblem: Send + Sync {
303 fn num_keys(&self) -> usize;
305
306 fn evaluate(&self, chromosome: &mut RandomKeyChromosome);
308
309 fn evaluate_parallel(&self, chromosomes: &mut [RandomKeyChromosome]) {
312 #[cfg(feature = "parallel")]
313 chromosomes.par_iter_mut().for_each(|c| {
314 self.evaluate(c);
315 });
316 #[cfg(not(feature = "parallel"))]
317 for c in chromosomes.iter_mut() {
318 self.evaluate(c);
319 }
320 }
321
322 fn initial_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<RandomKeyChromosome> {
331 (0..size)
332 .map(|_| RandomKeyChromosome::random(self.num_keys(), rng))
333 .collect()
334 }
335
336 fn on_generation(
338 &self,
339 _generation: u32,
340 _best: &RandomKeyChromosome,
341 _population: &[RandomKeyChromosome],
342 ) {
343 }
345}
346
347#[derive(Debug, Clone)]
349pub struct BrkgaResult {
350 pub best: RandomKeyChromosome,
352 pub generations: u32,
354 pub elapsed: Duration,
356 pub target_reached: bool,
358 pub history: Vec<f64>,
360}
361
362#[derive(Debug, Clone)]
364pub struct BrkgaProgress {
365 pub generation: u32,
367 pub max_generations: u32,
369 pub best_fitness: f64,
371 pub avg_fitness: f64,
373 pub elapsed: Duration,
375 pub running: bool,
377}
378
379pub struct BrkgaRunner<P: BrkgaProblem> {
381 config: BrkgaConfig,
382 problem: P,
383 cancelled: Arc<AtomicBool>,
384}
385
386impl<P: BrkgaProblem> BrkgaRunner<P> {
387 pub fn new(config: BrkgaConfig, problem: P) -> Result<Self> {
392 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
393 }
394
395 pub fn with_cancellation(
400 config: BrkgaConfig,
401 problem: P,
402 cancelled: Arc<AtomicBool>,
403 ) -> Result<Self> {
404 config.validate()?;
405 Ok(Self {
406 config,
407 problem,
408 cancelled,
409 })
410 }
411
412 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
414 self.cancelled.clone()
415 }
416
417 pub fn run(&self) -> BrkgaResult {
419 self.run_with_rng(&mut rand::rng())
420 }
421
422 pub fn run_with_progress<F>(&self, progress_callback: F) -> BrkgaResult
424 where
425 F: Fn(BrkgaProgress),
426 {
427 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
428 }
429
430 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> BrkgaResult {
432 self.run_with_rng_and_progress::<R, fn(BrkgaProgress)>(rng, None)
433 }
434
435 pub fn run_with_rng_and_progress<R: Rng, F>(
437 &self,
438 rng: &mut R,
439 progress_callback: Option<F>,
440 ) -> BrkgaResult
441 where
442 F: Fn(BrkgaProgress),
443 {
444 let start = Timer::now();
445 let mut history = Vec::new();
446 let num_keys = self.problem.num_keys();
447
448 let mut population = self
450 .problem
451 .initial_population(self.config.population_size, rng);
452 population.truncate(self.config.population_size);
455 while population.len() < self.config.population_size {
456 population.push(RandomKeyChromosome::random(num_keys, rng));
457 }
458
459 evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
461 self.problem.evaluate_parallel(batch)
462 });
463
464 population.sort_by(|a, b| {
466 b.fitness()
467 .partial_cmp(&a.fitness())
468 .unwrap_or(std::cmp::Ordering::Equal)
469 });
470
471 let mut best = population[0].clone();
472 let mut best_fitness = best.fitness();
473 let mut stagnation_count = 0u32;
474 let mut generation = 0u32;
475 let mut target_reached = false;
476
477 let elite_count = self.config.elite_count();
478 let mutant_count = self.config.mutant_count();
479
480 while generation < self.config.max_generations {
481 if self.cancelled.load(Ordering::Relaxed) {
483 break;
484 }
485
486 if expired(&start, self.config.time_limit) {
488 break;
489 }
490
491 if let Some(target) = self.config.target_fitness {
493 if best_fitness >= target {
494 target_reached = true;
495 break;
496 }
497 }
498
499 history.push(best_fitness);
501
502 let mut new_population = Vec::with_capacity(self.config.population_size);
504
505 for elite in population.iter().take(elite_count) {
507 new_population.push(elite.clone());
508 }
509
510 let mut mutants: Vec<RandomKeyChromosome> = (0..mutant_count)
512 .map(|_| RandomKeyChromosome::random(num_keys, rng))
513 .collect();
514
515 let crossover_count = self.config.population_size - elite_count - mutant_count;
517 let mut children: Vec<RandomKeyChromosome> = (0..crossover_count)
518 .map(|_| {
519 let elite_idx = rng.random_range(0..elite_count);
521 let elite_parent = &population[elite_idx];
522
523 let non_elite_idx = rng.random_range(elite_count..population.len());
525 let non_elite_parent = &population[non_elite_idx];
526
527 elite_parent.biased_crossover(non_elite_parent, self.config.elite_bias, rng)
529 })
530 .collect();
531
532 evaluate_within(&mut mutants, &start, self.config.time_limit, |batch| {
535 self.problem.evaluate_parallel(batch)
536 });
537 if expired(&start, self.config.time_limit) {
538 children.clear();
539 }
540 evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
541 self.problem.evaluate_parallel(batch)
542 });
543
544 new_population.extend(mutants);
546 new_population.extend(children);
547
548 new_population.sort_by(|a, b| {
550 b.fitness()
551 .partial_cmp(&a.fitness())
552 .unwrap_or(std::cmp::Ordering::Equal)
553 });
554
555 let new_best_fitness = new_population[0].fitness();
557 if new_best_fitness > best_fitness {
558 best = new_population[0].clone();
559 best_fitness = new_best_fitness;
560 stagnation_count = 0;
561 } else {
562 stagnation_count += 1;
563 }
564
565 if let Some(limit) = self.config.stagnation_limit {
567 if stagnation_count >= limit {
568 break;
569 }
570 }
571
572 self.problem
574 .on_generation(generation, &best, &new_population);
575
576 if let Some(ref callback) = progress_callback {
578 let avg_fitness = new_population.iter().map(|c| c.fitness()).sum::<f64>()
579 / new_population.len() as f64;
580
581 callback(BrkgaProgress {
582 generation,
583 max_generations: self.config.max_generations,
584 best_fitness,
585 avg_fitness,
586 elapsed: start.elapsed(),
587 running: true,
588 });
589 }
590
591 population = new_population;
592 generation += 1;
593 }
594
595 history.push(best_fitness);
597
598 if let Some(ref callback) = progress_callback {
600 let avg_fitness = population.iter().map(|c| c.fitness()).sum::<f64>()
601 / population.len().max(1) as f64;
602
603 callback(BrkgaProgress {
604 generation,
605 max_generations: self.config.max_generations,
606 best_fitness,
607 avg_fitness,
608 elapsed: start.elapsed(),
609 running: false,
610 });
611 }
612
613 BrkgaResult {
614 best,
615 generations: generation,
616 elapsed: start.elapsed(),
617 target_reached,
618 history,
619 }
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626
627 struct MaxSumProblem {
629 num_keys: usize,
630 }
631
632 impl BrkgaProblem for MaxSumProblem {
633 fn num_keys(&self) -> usize {
634 self.num_keys
635 }
636
637 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
638 let sum: f64 = chromosome.keys.iter().sum();
639 chromosome.set_fitness(sum);
640 }
641 }
642
643 #[test]
644 fn test_brkga_basic() {
645 let config = BrkgaConfig::default()
646 .with_population_size(50)
647 .with_max_generations(50);
648
649 let problem = MaxSumProblem { num_keys: 10 };
650 let runner = BrkgaRunner::new(config, problem).expect("valid config");
651 let result = runner.run();
652
653 assert!(result.best.fitness() > 5.0);
655 }
656
657 #[test]
658 fn test_random_key_chromosome() {
659 let mut rng = rand::rng();
660 let chromosome = RandomKeyChromosome::random(10, &mut rng);
661
662 assert_eq!(chromosome.len(), 10);
663 for &key in &chromosome.keys {
664 assert!((0.0..1.0).contains(&key));
665 }
666 }
667
668 #[test]
669 fn test_biased_crossover() {
670 let mut rng = rand::rng();
671 let elite = RandomKeyChromosome::random(10, &mut rng);
672 let non_elite = RandomKeyChromosome::random(10, &mut rng);
673
674 let child = elite.biased_crossover(&non_elite, 0.7, &mut rng);
675
676 assert_eq!(child.len(), 10);
677 for &key in &child.keys {
678 assert!((0.0..1.0).contains(&key));
679 }
680 }
681
682 #[test]
683 fn test_decode_as_permutation() {
684 let mut chromosome = RandomKeyChromosome::new(5);
685 chromosome.keys = vec![0.3, 0.1, 0.9, 0.5, 0.2];
686
687 let perm = chromosome.decode_as_permutation();
688
689 assert_eq!(perm, vec![1, 4, 0, 3, 2]);
691 }
692
693 #[test]
694 fn test_decode_as_discrete() {
695 let mut chromosome = RandomKeyChromosome::new(3);
696 chromosome.keys = vec![0.0, 0.5, 0.99];
697
698 assert_eq!(chromosome.decode_as_discrete(0, 6), 0);
700 assert_eq!(chromosome.decode_as_discrete(1, 6), 3);
701 assert_eq!(chromosome.decode_as_discrete(2, 6), 5);
702 }
703}