1use rand::prelude::*;
18use rayon::prelude::*;
19use std::sync::atomic::{AtomicBool, Ordering};
20use std::sync::Arc;
21use std::time::{Duration, Instant};
22
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25
26#[derive(Debug, Clone)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct BrkgaConfig {
30 pub population_size: usize,
32 pub max_generations: u32,
34 pub elite_fraction: f64,
36 pub mutant_fraction: f64,
38 pub elite_bias: f64,
40 pub time_limit: Option<Duration>,
42 pub target_fitness: Option<f64>,
44 pub stagnation_limit: Option<u32>,
46}
47
48impl Default for BrkgaConfig {
49 fn default() -> Self {
50 Self {
51 population_size: 100,
52 max_generations: 500,
53 elite_fraction: 0.2, mutant_fraction: 0.15, elite_bias: 0.7, time_limit: None,
57 target_fitness: None,
58 stagnation_limit: Some(50),
59 }
60 }
61}
62
63impl BrkgaConfig {
64 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn with_population_size(mut self, size: usize) -> Self {
71 self.population_size = size.max(4);
72 self
73 }
74
75 pub fn with_max_generations(mut self, gen: u32) -> Self {
77 self.max_generations = gen;
78 self
79 }
80
81 pub fn with_elite_fraction(mut self, fraction: f64) -> Self {
83 self.elite_fraction = fraction.clamp(0.01, 0.5);
84 self
85 }
86
87 pub fn with_mutant_fraction(mut self, fraction: f64) -> Self {
89 self.mutant_fraction = fraction.clamp(0.0, 0.5);
90 self
91 }
92
93 pub fn with_elite_bias(mut self, bias: f64) -> Self {
95 self.elite_bias = bias.clamp(0.5, 1.0);
96 self
97 }
98
99 pub fn with_time_limit(mut self, duration: Duration) -> Self {
101 self.time_limit = Some(duration);
102 self
103 }
104
105 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
107 self.target_fitness = Some(fitness);
108 self
109 }
110
111 pub fn with_stagnation_limit(mut self, limit: u32) -> Self {
113 self.stagnation_limit = Some(limit);
114 self
115 }
116
117 pub fn elite_count(&self) -> usize {
119 ((self.population_size as f64) * self.elite_fraction).ceil() as usize
120 }
121
122 pub fn mutant_count(&self) -> usize {
124 ((self.population_size as f64) * self.mutant_fraction).ceil() as usize
125 }
126}
127
128#[derive(Debug, Clone)]
133pub struct RandomKeyChromosome {
134 pub keys: Vec<f64>,
136 fitness: f64,
138}
139
140impl RandomKeyChromosome {
141 pub fn new(num_keys: usize) -> Self {
143 Self {
144 keys: vec![0.0; num_keys],
145 fitness: f64::NEG_INFINITY,
146 }
147 }
148
149 pub fn random<R: Rng>(num_keys: usize, rng: &mut R) -> Self {
151 let keys: Vec<f64> = (0..num_keys).map(|_| rng.gen::<f64>()).collect();
152 Self {
153 keys,
154 fitness: f64::NEG_INFINITY,
155 }
156 }
157
158 pub fn len(&self) -> usize {
160 self.keys.len()
161 }
162
163 pub fn is_empty(&self) -> bool {
165 self.keys.is_empty()
166 }
167
168 pub fn fitness(&self) -> f64 {
170 self.fitness
171 }
172
173 pub fn set_fitness(&mut self, fitness: f64) {
175 self.fitness = fitness;
176 }
177
178 pub fn biased_crossover<R: Rng>(&self, other: &Self, elite_bias: f64, rng: &mut R) -> Self {
183 let keys: Vec<f64> = self
184 .keys
185 .iter()
186 .zip(&other.keys)
187 .map(|(&elite_key, &non_elite_key)| {
188 if rng.gen::<f64>() < elite_bias {
189 elite_key
190 } else {
191 non_elite_key
192 }
193 })
194 .collect();
195
196 Self {
197 keys,
198 fitness: f64::NEG_INFINITY,
199 }
200 }
201
202 pub fn decode_as_permutation(&self) -> Vec<usize> {
207 let mut indices: Vec<usize> = (0..self.keys.len()).collect();
208 indices.sort_by(|&a, &b| {
209 self.keys[a]
210 .partial_cmp(&self.keys[b])
211 .unwrap_or(std::cmp::Ordering::Equal)
212 });
213 indices
214 }
215
216 pub fn decode_as_discrete(&self, key_idx: usize, num_options: usize) -> usize {
221 if key_idx >= self.keys.len() || num_options == 0 {
222 return 0;
223 }
224 let key = self.keys[key_idx].clamp(0.0, 0.9999999);
225 (key * num_options as f64) as usize
226 }
227}
228
229pub trait BrkgaProblem: Send + Sync {
231 fn num_keys(&self) -> usize;
233
234 fn evaluate(&self, chromosome: &mut RandomKeyChromosome);
236
237 fn evaluate_parallel(&self, chromosomes: &mut [RandomKeyChromosome]) {
240 chromosomes.par_iter_mut().for_each(|c| {
241 self.evaluate(c);
242 });
243 }
244
245 fn on_generation(
247 &self,
248 _generation: u32,
249 _best: &RandomKeyChromosome,
250 _population: &[RandomKeyChromosome],
251 ) {
252 }
254}
255
256#[derive(Debug, Clone)]
258pub struct BrkgaResult {
259 pub best: RandomKeyChromosome,
261 pub generations: u32,
263 pub elapsed: Duration,
265 pub target_reached: bool,
267 pub history: Vec<f64>,
269}
270
271#[derive(Debug, Clone)]
273pub struct BrkgaProgress {
274 pub generation: u32,
276 pub max_generations: u32,
278 pub best_fitness: f64,
280 pub avg_fitness: f64,
282 pub elapsed: Duration,
284 pub running: bool,
286}
287
288pub struct BrkgaRunner<P: BrkgaProblem> {
290 config: BrkgaConfig,
291 problem: P,
292 cancelled: Arc<AtomicBool>,
293}
294
295impl<P: BrkgaProblem> BrkgaRunner<P> {
296 pub fn new(config: BrkgaConfig, problem: P) -> Self {
298 Self {
299 config,
300 problem,
301 cancelled: Arc::new(AtomicBool::new(false)),
302 }
303 }
304
305 pub fn with_cancellation(config: BrkgaConfig, problem: P, cancelled: Arc<AtomicBool>) -> Self {
307 Self {
308 config,
309 problem,
310 cancelled,
311 }
312 }
313
314 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
316 self.cancelled.clone()
317 }
318
319 pub fn run(&self) -> BrkgaResult {
321 self.run_with_rng(&mut thread_rng())
322 }
323
324 pub fn run_with_progress<F>(&self, progress_callback: F) -> BrkgaResult
326 where
327 F: Fn(BrkgaProgress),
328 {
329 self.run_with_rng_and_progress(&mut thread_rng(), Some(progress_callback))
330 }
331
332 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> BrkgaResult {
334 self.run_with_rng_and_progress::<R, fn(BrkgaProgress)>(rng, None)
335 }
336
337 pub fn run_with_rng_and_progress<R: Rng, F>(
339 &self,
340 rng: &mut R,
341 progress_callback: Option<F>,
342 ) -> BrkgaResult
343 where
344 F: Fn(BrkgaProgress),
345 {
346 let start = Instant::now();
347 let mut history = Vec::new();
348 let num_keys = self.problem.num_keys();
349
350 let mut population: Vec<RandomKeyChromosome> = (0..self.config.population_size)
352 .map(|_| RandomKeyChromosome::random(num_keys, rng))
353 .collect();
354
355 self.problem.evaluate_parallel(&mut population);
357
358 population.sort_by(|a, b| {
360 b.fitness()
361 .partial_cmp(&a.fitness())
362 .unwrap_or(std::cmp::Ordering::Equal)
363 });
364
365 let mut best = population[0].clone();
366 let mut best_fitness = best.fitness();
367 let mut stagnation_count = 0u32;
368 let mut generation = 0u32;
369 let mut target_reached = false;
370
371 let elite_count = self.config.elite_count();
372 let mutant_count = self.config.mutant_count();
373
374 while generation < self.config.max_generations {
375 if self.cancelled.load(Ordering::Relaxed) {
377 break;
378 }
379
380 if let Some(limit) = self.config.time_limit {
382 if start.elapsed() > limit {
383 break;
384 }
385 }
386
387 if let Some(target) = self.config.target_fitness {
389 if best_fitness >= target {
390 target_reached = true;
391 break;
392 }
393 }
394
395 history.push(best_fitness);
397
398 let mut new_population = Vec::with_capacity(self.config.population_size);
400
401 for elite in population.iter().take(elite_count) {
403 new_population.push(elite.clone());
404 }
405
406 let mut mutants: Vec<RandomKeyChromosome> = (0..mutant_count)
408 .map(|_| RandomKeyChromosome::random(num_keys, rng))
409 .collect();
410
411 let crossover_count = self.config.population_size - elite_count - mutant_count;
413 let mut children: Vec<RandomKeyChromosome> = (0..crossover_count)
414 .map(|_| {
415 let elite_idx = rng.gen_range(0..elite_count);
417 let elite_parent = &population[elite_idx];
418
419 let non_elite_idx = rng.gen_range(elite_count..population.len());
421 let non_elite_parent = &population[non_elite_idx];
422
423 elite_parent.biased_crossover(non_elite_parent, self.config.elite_bias, rng)
425 })
426 .collect();
427
428 self.problem.evaluate_parallel(&mut mutants);
430 self.problem.evaluate_parallel(&mut children);
431
432 new_population.extend(mutants);
434 new_population.extend(children);
435
436 new_population.sort_by(|a, b| {
438 b.fitness()
439 .partial_cmp(&a.fitness())
440 .unwrap_or(std::cmp::Ordering::Equal)
441 });
442
443 let new_best_fitness = new_population[0].fitness();
445 if new_best_fitness > best_fitness {
446 best = new_population[0].clone();
447 best_fitness = new_best_fitness;
448 stagnation_count = 0;
449 } else {
450 stagnation_count += 1;
451 }
452
453 if let Some(limit) = self.config.stagnation_limit {
455 if stagnation_count >= limit {
456 break;
457 }
458 }
459
460 self.problem
462 .on_generation(generation, &best, &new_population);
463
464 if let Some(ref callback) = progress_callback {
466 let avg_fitness = new_population.iter().map(|c| c.fitness()).sum::<f64>()
467 / new_population.len() as f64;
468
469 callback(BrkgaProgress {
470 generation,
471 max_generations: self.config.max_generations,
472 best_fitness,
473 avg_fitness,
474 elapsed: start.elapsed(),
475 running: true,
476 });
477 }
478
479 population = new_population;
480 generation += 1;
481 }
482
483 history.push(best_fitness);
485
486 if let Some(ref callback) = progress_callback {
488 let avg_fitness = population.iter().map(|c| c.fitness()).sum::<f64>()
489 / population.len().max(1) as f64;
490
491 callback(BrkgaProgress {
492 generation,
493 max_generations: self.config.max_generations,
494 best_fitness,
495 avg_fitness,
496 elapsed: start.elapsed(),
497 running: false,
498 });
499 }
500
501 BrkgaResult {
502 best,
503 generations: generation,
504 elapsed: start.elapsed(),
505 target_reached,
506 history,
507 }
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 struct MaxSumProblem {
517 num_keys: usize,
518 }
519
520 impl BrkgaProblem for MaxSumProblem {
521 fn num_keys(&self) -> usize {
522 self.num_keys
523 }
524
525 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
526 let sum: f64 = chromosome.keys.iter().sum();
527 chromosome.set_fitness(sum);
528 }
529 }
530
531 #[test]
532 fn test_brkga_basic() {
533 let config = BrkgaConfig::default()
534 .with_population_size(50)
535 .with_max_generations(50);
536
537 let problem = MaxSumProblem { num_keys: 10 };
538 let runner = BrkgaRunner::new(config, problem);
539 let result = runner.run();
540
541 assert!(result.best.fitness() > 5.0);
543 }
544
545 #[test]
546 fn test_random_key_chromosome() {
547 let mut rng = thread_rng();
548 let chromosome = RandomKeyChromosome::random(10, &mut rng);
549
550 assert_eq!(chromosome.len(), 10);
551 for &key in &chromosome.keys {
552 assert!(key >= 0.0 && key < 1.0);
553 }
554 }
555
556 #[test]
557 fn test_biased_crossover() {
558 let mut rng = thread_rng();
559 let elite = RandomKeyChromosome::random(10, &mut rng);
560 let non_elite = RandomKeyChromosome::random(10, &mut rng);
561
562 let child = elite.biased_crossover(&non_elite, 0.7, &mut rng);
563
564 assert_eq!(child.len(), 10);
565 for &key in &child.keys {
566 assert!(key >= 0.0 && key < 1.0);
567 }
568 }
569
570 #[test]
571 fn test_decode_as_permutation() {
572 let mut chromosome = RandomKeyChromosome::new(5);
573 chromosome.keys = vec![0.3, 0.1, 0.9, 0.5, 0.2];
574
575 let perm = chromosome.decode_as_permutation();
576
577 assert_eq!(perm, vec![1, 4, 0, 3, 2]);
579 }
580
581 #[test]
582 fn test_decode_as_discrete() {
583 let mut chromosome = RandomKeyChromosome::new(3);
584 chromosome.keys = vec![0.0, 0.5, 0.99];
585
586 assert_eq!(chromosome.decode_as_discrete(0, 6), 0);
588 assert_eq!(chromosome.decode_as_discrete(1, 6), 3);
589 assert_eq!(chromosome.decode_as_discrete(2, 6), 5);
590 }
591}