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::timing::Timer;
35
36#[cfg(feature = "serde")]
37use serde::{Deserialize, Serialize};
38
39#[derive(Debug, Clone)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct BrkgaConfig {
43 pub population_size: usize,
45 pub max_generations: u32,
47 pub elite_fraction: f64,
49 pub mutant_fraction: f64,
51 pub elite_bias: f64,
53 pub time_limit: Option<Duration>,
55 pub target_fitness: Option<f64>,
57 pub stagnation_limit: Option<u32>,
59}
60
61impl Default for BrkgaConfig {
62 fn default() -> Self {
63 Self {
64 population_size: 100,
65 max_generations: 500,
66 elite_fraction: 0.2, mutant_fraction: 0.15, elite_bias: 0.7, time_limit: None,
70 target_fitness: None,
71 stagnation_limit: Some(50),
72 }
73 }
74}
75
76impl BrkgaConfig {
77 pub fn new() -> Self {
79 Self::default()
80 }
81
82 pub fn with_population_size(mut self, size: usize) -> Self {
84 self.population_size = size.max(4);
85 self
86 }
87
88 pub fn with_max_generations(mut self, gen: u32) -> Self {
90 self.max_generations = gen;
91 self
92 }
93
94 pub fn with_elite_fraction(mut self, fraction: f64) -> Self {
96 self.elite_fraction = fraction.clamp(0.01, 0.5);
97 self
98 }
99
100 pub fn with_mutant_fraction(mut self, fraction: f64) -> Self {
102 self.mutant_fraction = fraction.clamp(0.0, 0.5);
103 self
104 }
105
106 pub fn with_elite_bias(mut self, bias: f64) -> Self {
108 self.elite_bias = bias.clamp(0.5, 1.0);
109 self
110 }
111
112 pub fn with_time_limit(mut self, duration: Duration) -> Self {
114 self.time_limit = Some(duration);
115 self
116 }
117
118 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
120 self.target_fitness = Some(fitness);
121 self
122 }
123
124 pub fn with_stagnation_limit(mut self, limit: u32) -> Self {
126 self.stagnation_limit = Some(limit);
127 self
128 }
129
130 pub fn elite_count(&self) -> usize {
132 ((self.population_size as f64) * self.elite_fraction).ceil() as usize
133 }
134
135 pub fn mutant_count(&self) -> usize {
137 ((self.population_size as f64) * self.mutant_fraction).ceil() as usize
138 }
139}
140
141#[derive(Debug, Clone)]
146pub struct RandomKeyChromosome {
147 pub keys: Vec<f64>,
149 fitness: f64,
151}
152
153impl RandomKeyChromosome {
154 pub fn new(num_keys: usize) -> Self {
156 Self {
157 keys: vec![0.0; num_keys],
158 fitness: f64::NEG_INFINITY,
159 }
160 }
161
162 pub fn random<R: Rng>(num_keys: usize, rng: &mut R) -> Self {
164 let keys: Vec<f64> = (0..num_keys).map(|_| rng.random::<f64>()).collect();
165 Self {
166 keys,
167 fitness: f64::NEG_INFINITY,
168 }
169 }
170
171 pub fn len(&self) -> usize {
173 self.keys.len()
174 }
175
176 pub fn is_empty(&self) -> bool {
178 self.keys.is_empty()
179 }
180
181 pub fn fitness(&self) -> f64 {
183 self.fitness
184 }
185
186 pub fn set_fitness(&mut self, fitness: f64) {
188 self.fitness = fitness;
189 }
190
191 pub fn biased_crossover<R: Rng>(&self, other: &Self, elite_bias: f64, rng: &mut R) -> Self {
196 let keys: Vec<f64> = self
197 .keys
198 .iter()
199 .zip(&other.keys)
200 .map(|(&elite_key, &non_elite_key)| {
201 if rng.random::<f64>() < elite_bias {
202 elite_key
203 } else {
204 non_elite_key
205 }
206 })
207 .collect();
208
209 Self {
210 keys,
211 fitness: f64::NEG_INFINITY,
212 }
213 }
214
215 pub fn decode_as_permutation(&self) -> Vec<usize> {
220 let mut indices: Vec<usize> = (0..self.keys.len()).collect();
221 indices.sort_by(|&a, &b| {
222 self.keys[a]
223 .partial_cmp(&self.keys[b])
224 .unwrap_or(std::cmp::Ordering::Equal)
225 });
226 indices
227 }
228
229 pub fn decode_as_discrete(&self, key_idx: usize, num_options: usize) -> usize {
234 if key_idx >= self.keys.len() || num_options == 0 {
235 return 0;
236 }
237 let key = self.keys[key_idx].clamp(0.0, 0.9999999);
238 (key * num_options as f64) as usize
239 }
240}
241
242pub trait BrkgaProblem: Send + Sync {
244 fn num_keys(&self) -> usize;
246
247 fn evaluate(&self, chromosome: &mut RandomKeyChromosome);
249
250 fn evaluate_parallel(&self, chromosomes: &mut [RandomKeyChromosome]) {
253 #[cfg(feature = "parallel")]
254 chromosomes.par_iter_mut().for_each(|c| {
255 self.evaluate(c);
256 });
257 #[cfg(not(feature = "parallel"))]
258 for c in chromosomes.iter_mut() {
259 self.evaluate(c);
260 }
261 }
262
263 fn on_generation(
265 &self,
266 _generation: u32,
267 _best: &RandomKeyChromosome,
268 _population: &[RandomKeyChromosome],
269 ) {
270 }
272}
273
274#[derive(Debug, Clone)]
276pub struct BrkgaResult {
277 pub best: RandomKeyChromosome,
279 pub generations: u32,
281 pub elapsed: Duration,
283 pub target_reached: bool,
285 pub history: Vec<f64>,
287}
288
289#[derive(Debug, Clone)]
291pub struct BrkgaProgress {
292 pub generation: u32,
294 pub max_generations: u32,
296 pub best_fitness: f64,
298 pub avg_fitness: f64,
300 pub elapsed: Duration,
302 pub running: bool,
304}
305
306pub struct BrkgaRunner<P: BrkgaProblem> {
308 config: BrkgaConfig,
309 problem: P,
310 cancelled: Arc<AtomicBool>,
311}
312
313impl<P: BrkgaProblem> BrkgaRunner<P> {
314 pub fn new(config: BrkgaConfig, problem: P) -> Self {
316 Self {
317 config,
318 problem,
319 cancelled: Arc::new(AtomicBool::new(false)),
320 }
321 }
322
323 pub fn with_cancellation(config: BrkgaConfig, problem: P, cancelled: Arc<AtomicBool>) -> Self {
325 Self {
326 config,
327 problem,
328 cancelled,
329 }
330 }
331
332 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
334 self.cancelled.clone()
335 }
336
337 pub fn run(&self) -> BrkgaResult {
339 self.run_with_rng(&mut rand::rng())
340 }
341
342 pub fn run_with_progress<F>(&self, progress_callback: F) -> BrkgaResult
344 where
345 F: Fn(BrkgaProgress),
346 {
347 self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
348 }
349
350 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> BrkgaResult {
352 self.run_with_rng_and_progress::<R, fn(BrkgaProgress)>(rng, None)
353 }
354
355 pub fn run_with_rng_and_progress<R: Rng, F>(
357 &self,
358 rng: &mut R,
359 progress_callback: Option<F>,
360 ) -> BrkgaResult
361 where
362 F: Fn(BrkgaProgress),
363 {
364 let start = Timer::now();
365 let mut history = Vec::new();
366 let num_keys = self.problem.num_keys();
367
368 let mut population: Vec<RandomKeyChromosome> = (0..self.config.population_size)
370 .map(|_| RandomKeyChromosome::random(num_keys, rng))
371 .collect();
372
373 self.problem.evaluate_parallel(&mut population);
375
376 population.sort_by(|a, b| {
378 b.fitness()
379 .partial_cmp(&a.fitness())
380 .unwrap_or(std::cmp::Ordering::Equal)
381 });
382
383 let mut best = population[0].clone();
384 let mut best_fitness = best.fitness();
385 let mut stagnation_count = 0u32;
386 let mut generation = 0u32;
387 let mut target_reached = false;
388
389 let elite_count = self.config.elite_count();
390 let mutant_count = self.config.mutant_count();
391
392 while generation < self.config.max_generations {
393 if self.cancelled.load(Ordering::Relaxed) {
395 break;
396 }
397
398 if let Some(limit) = self.config.time_limit {
400 if start.elapsed() > limit {
401 break;
402 }
403 }
404
405 if let Some(target) = self.config.target_fitness {
407 if best_fitness >= target {
408 target_reached = true;
409 break;
410 }
411 }
412
413 history.push(best_fitness);
415
416 let mut new_population = Vec::with_capacity(self.config.population_size);
418
419 for elite in population.iter().take(elite_count) {
421 new_population.push(elite.clone());
422 }
423
424 let mut mutants: Vec<RandomKeyChromosome> = (0..mutant_count)
426 .map(|_| RandomKeyChromosome::random(num_keys, rng))
427 .collect();
428
429 let crossover_count = self.config.population_size - elite_count - mutant_count;
431 let mut children: Vec<RandomKeyChromosome> = (0..crossover_count)
432 .map(|_| {
433 let elite_idx = rng.random_range(0..elite_count);
435 let elite_parent = &population[elite_idx];
436
437 let non_elite_idx = rng.random_range(elite_count..population.len());
439 let non_elite_parent = &population[non_elite_idx];
440
441 elite_parent.biased_crossover(non_elite_parent, self.config.elite_bias, rng)
443 })
444 .collect();
445
446 self.problem.evaluate_parallel(&mut mutants);
448 self.problem.evaluate_parallel(&mut children);
449
450 new_population.extend(mutants);
452 new_population.extend(children);
453
454 new_population.sort_by(|a, b| {
456 b.fitness()
457 .partial_cmp(&a.fitness())
458 .unwrap_or(std::cmp::Ordering::Equal)
459 });
460
461 let new_best_fitness = new_population[0].fitness();
463 if new_best_fitness > best_fitness {
464 best = new_population[0].clone();
465 best_fitness = new_best_fitness;
466 stagnation_count = 0;
467 } else {
468 stagnation_count += 1;
469 }
470
471 if let Some(limit) = self.config.stagnation_limit {
473 if stagnation_count >= limit {
474 break;
475 }
476 }
477
478 self.problem
480 .on_generation(generation, &best, &new_population);
481
482 if let Some(ref callback) = progress_callback {
484 let avg_fitness = new_population.iter().map(|c| c.fitness()).sum::<f64>()
485 / new_population.len() as f64;
486
487 callback(BrkgaProgress {
488 generation,
489 max_generations: self.config.max_generations,
490 best_fitness,
491 avg_fitness,
492 elapsed: start.elapsed(),
493 running: true,
494 });
495 }
496
497 population = new_population;
498 generation += 1;
499 }
500
501 history.push(best_fitness);
503
504 if let Some(ref callback) = progress_callback {
506 let avg_fitness = population.iter().map(|c| c.fitness()).sum::<f64>()
507 / population.len().max(1) as f64;
508
509 callback(BrkgaProgress {
510 generation,
511 max_generations: self.config.max_generations,
512 best_fitness,
513 avg_fitness,
514 elapsed: start.elapsed(),
515 running: false,
516 });
517 }
518
519 BrkgaResult {
520 best,
521 generations: generation,
522 elapsed: start.elapsed(),
523 target_reached,
524 history,
525 }
526 }
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532
533 struct MaxSumProblem {
535 num_keys: usize,
536 }
537
538 impl BrkgaProblem for MaxSumProblem {
539 fn num_keys(&self) -> usize {
540 self.num_keys
541 }
542
543 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
544 let sum: f64 = chromosome.keys.iter().sum();
545 chromosome.set_fitness(sum);
546 }
547 }
548
549 #[test]
550 fn test_brkga_basic() {
551 let config = BrkgaConfig::default()
552 .with_population_size(50)
553 .with_max_generations(50);
554
555 let problem = MaxSumProblem { num_keys: 10 };
556 let runner = BrkgaRunner::new(config, problem);
557 let result = runner.run();
558
559 assert!(result.best.fitness() > 5.0);
561 }
562
563 #[test]
564 fn test_random_key_chromosome() {
565 let mut rng = rand::rng();
566 let chromosome = RandomKeyChromosome::random(10, &mut rng);
567
568 assert_eq!(chromosome.len(), 10);
569 for &key in &chromosome.keys {
570 assert!((0.0..1.0).contains(&key));
571 }
572 }
573
574 #[test]
575 fn test_biased_crossover() {
576 let mut rng = rand::rng();
577 let elite = RandomKeyChromosome::random(10, &mut rng);
578 let non_elite = RandomKeyChromosome::random(10, &mut rng);
579
580 let child = elite.biased_crossover(&non_elite, 0.7, &mut rng);
581
582 assert_eq!(child.len(), 10);
583 for &key in &child.keys {
584 assert!((0.0..1.0).contains(&key));
585 }
586 }
587
588 #[test]
589 fn test_decode_as_permutation() {
590 let mut chromosome = RandomKeyChromosome::new(5);
591 chromosome.keys = vec![0.3, 0.1, 0.9, 0.5, 0.2];
592
593 let perm = chromosome.decode_as_permutation();
594
595 assert_eq!(perm, vec![1, 4, 0, 3, 2]);
597 }
598
599 #[test]
600 fn test_decode_as_discrete() {
601 let mut chromosome = RandomKeyChromosome::new(3);
602 chromosome.keys = vec![0.0, 0.5, 0.99];
603
604 assert_eq!(chromosome.decode_as_discrete(0, 6), 0);
606 assert_eq!(chromosome.decode_as_discrete(1, 6), 3);
607 assert_eq!(chromosome.decode_as_discrete(2, 6), 5);
608 }
609}