1use rand::prelude::*;
20#[cfg(feature = "parallel")]
21use rayon::prelude::*;
22use std::sync::atomic::{AtomicBool, Ordering};
23use std::sync::Arc;
24use std::time::Duration;
25
26use crate::timing::{expired, Timer};
27
28#[cfg(feature = "serde")]
29use serde::{Deserialize, Serialize};
30
31#[derive(Debug, Clone, Copy, Default)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub enum CoolingSchedule {
35 #[default]
37 Geometric,
38 Linear,
40 Adaptive,
42 LundyMees,
44}
45
46#[derive(Debug, Clone)]
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49pub struct SaConfig {
50 pub initial_temp: f64,
52 pub final_temp: f64,
54 pub cooling_rate: f64,
56 pub iterations_per_temp: usize,
58 pub max_iterations: Option<u64>,
60 pub cooling_schedule: CoolingSchedule,
62 pub time_limit: Option<Duration>,
64 pub target_fitness: Option<f64>,
66 pub enable_reheating: bool,
68 pub reheat_threshold: u64,
70 pub reheat_factor: f64,
72}
73
74impl Default for SaConfig {
75 fn default() -> Self {
76 Self {
77 initial_temp: 1000.0,
78 final_temp: 0.001,
79 cooling_rate: 0.95,
80 iterations_per_temp: 100,
81 max_iterations: Some(100_000),
82 cooling_schedule: CoolingSchedule::Geometric,
83 time_limit: None,
84 target_fitness: None,
85 enable_reheating: false,
86 reheat_threshold: 1000,
87 reheat_factor: 2.0,
88 }
89 }
90}
91
92impl SaConfig {
93 pub fn new() -> Self {
95 Self::default()
96 }
97
98 pub fn with_initial_temp(mut self, temp: f64) -> Self {
100 self.initial_temp = temp.max(0.001);
101 self
102 }
103
104 pub fn with_final_temp(mut self, temp: f64) -> Self {
106 self.final_temp = temp.max(0.0001);
107 self
108 }
109
110 pub fn with_cooling_rate(mut self, rate: f64) -> Self {
112 self.cooling_rate = rate.clamp(0.001, 0.9999);
113 self
114 }
115
116 pub fn with_iterations_per_temp(mut self, iterations: usize) -> Self {
118 self.iterations_per_temp = iterations.max(1);
119 self
120 }
121
122 pub fn with_max_iterations(mut self, iterations: u64) -> Self {
124 self.max_iterations = Some(iterations);
125 self
126 }
127
128 pub fn with_cooling_schedule(mut self, schedule: CoolingSchedule) -> Self {
130 self.cooling_schedule = schedule;
131 self
132 }
133
134 pub fn with_time_limit(mut self, duration: Duration) -> Self {
136 self.time_limit = Some(duration);
137 self
138 }
139
140 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
142 self.target_fitness = Some(fitness);
143 self
144 }
145
146 pub fn with_reheating(mut self, threshold: u64, factor: f64) -> Self {
148 self.enable_reheating = true;
149 self.reheat_threshold = threshold;
150 self.reheat_factor = factor.max(1.1);
151 self
152 }
153}
154
155pub trait SaSolution: Clone + Send + Sync {
157 fn objective(&self) -> f64;
160
161 fn set_objective(&mut self, value: f64);
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum NeighborhoodOperator {
168 Swap,
170 Relocate,
172 Inversion,
174 Rotation,
176 Chain,
178 MirrorFlip,
183}
184
185pub trait SaProblem: Send + Sync {
187 type Solution: SaSolution;
189
190 fn initial_solution<R: Rng>(&self, rng: &mut R) -> Self::Solution;
192
193 fn neighbor<R: Rng>(
195 &self,
196 solution: &Self::Solution,
197 operator: NeighborhoodOperator,
198 rng: &mut R,
199 ) -> Self::Solution;
200
201 fn evaluate(&self, solution: &mut Self::Solution);
203
204 fn available_operators(&self) -> Vec<NeighborhoodOperator> {
206 vec![
207 NeighborhoodOperator::Swap,
208 NeighborhoodOperator::Relocate,
209 NeighborhoodOperator::Inversion,
210 ]
211 }
212
213 fn on_temperature_change(
215 &self,
216 _temperature: f64,
217 _iteration: u64,
218 _best: &Self::Solution,
219 _current: &Self::Solution,
220 ) {
221 }
223}
224
225#[derive(Debug, Clone)]
227pub struct SaProgress {
228 pub temperature: f64,
230 pub iteration: u64,
232 pub best_fitness: f64,
234 pub current_fitness: f64,
236 pub acceptance_rate: f64,
238 pub elapsed: Duration,
240 pub running: bool,
242}
243
244#[derive(Debug, Clone)]
246pub struct SaResult<S: SaSolution> {
247 pub best: S,
249 pub final_temperature: f64,
251 pub iterations: u64,
253 pub elapsed: Duration,
255 pub target_reached: bool,
257 pub reheat_count: u32,
259 pub history: Vec<f64>,
261}
262
263pub struct SaRunner<P: SaProblem> {
265 config: SaConfig,
266 problem: P,
267 cancelled: Arc<AtomicBool>,
268}
269
270impl<P: SaProblem> SaRunner<P> {
271 pub fn new(config: SaConfig, problem: P) -> Self {
273 Self {
274 config,
275 problem,
276 cancelled: Arc::new(AtomicBool::new(false)),
277 }
278 }
279
280 pub fn with_cancellation(config: SaConfig, problem: P, cancelled: Arc<AtomicBool>) -> Self {
283 Self {
284 config,
285 problem,
286 cancelled,
287 }
288 }
289
290 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
292 self.cancelled.clone()
293 }
294
295 pub fn run(&self) -> SaResult<P::Solution> {
297 self.run_with_rng(&mut rand::rng())
298 }
299
300 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> SaResult<P::Solution> {
302 let start = Timer::now();
303 let mut history = Vec::new();
304
305 let mut current = self.problem.initial_solution(rng);
307 self.problem.evaluate(&mut current);
308 let mut best = current.clone();
309 let mut best_fitness = best.objective();
310
311 let mut temperature = self.config.initial_temp;
312 let mut iteration = 0u64;
313 let mut target_reached = false;
314 let mut reheat_count = 0u32;
315 let mut stagnation_count = 0u64;
316
317 let operators = self.problem.available_operators();
318 let temp_delta = if matches!(self.config.cooling_schedule, CoolingSchedule::Linear) {
319 (self.config.initial_temp - self.config.final_temp)
320 / (self.config.max_iterations.unwrap_or(10000) as f64
321 / self.config.iterations_per_temp as f64)
322 } else {
323 0.0
324 };
325
326 let mut accepted_count = 0usize;
328 let mut total_count = 0usize;
329
330 while temperature > self.config.final_temp {
331 if self.cancelled.load(Ordering::Relaxed) {
333 break;
334 }
335
336 if expired(&start, self.config.time_limit) {
338 break;
339 }
340
341 if let Some(max) = self.config.max_iterations {
343 if iteration >= max {
344 break;
345 }
346 }
347
348 if let Some(target) = self.config.target_fitness {
350 if best_fitness >= target {
351 target_reached = true;
352 break;
353 }
354 }
355
356 for _ in 0..self.config.iterations_per_temp {
359 if expired(&start, self.config.time_limit) {
360 break;
361 }
362 iteration += 1;
363 total_count += 1;
364
365 let operator = operators[rng.random_range(0..operators.len())];
367
368 let mut neighbor = self.problem.neighbor(¤t, operator, rng);
370 self.problem.evaluate(&mut neighbor);
371
372 let current_obj = current.objective();
373 let neighbor_obj = neighbor.objective();
374 let delta = neighbor_obj - current_obj;
375
376 let accept = if delta >= 0.0 {
378 true
380 } else {
381 let probability = (delta / temperature).exp();
383 rng.random::<f64>() < probability
384 };
385
386 if accept {
387 accepted_count += 1;
388 current = neighbor;
389
390 if current.objective() > best_fitness {
392 best = current.clone();
393 best_fitness = best.objective();
394 stagnation_count = 0;
395 } else {
396 stagnation_count += 1;
397 }
398 } else {
399 stagnation_count += 1;
400 }
401
402 if let Some(max) = self.config.max_iterations {
404 if iteration >= max {
405 break;
406 }
407 }
408 }
409
410 history.push(best_fitness);
412
413 self.problem
415 .on_temperature_change(temperature, iteration, &best, ¤t);
416
417 if self.config.enable_reheating && stagnation_count >= self.config.reheat_threshold {
419 temperature *= self.config.reheat_factor;
420 temperature = temperature.min(self.config.initial_temp);
421 stagnation_count = 0;
422 reheat_count += 1;
423 }
424
425 temperature = self.cool_down(temperature, temp_delta, accepted_count, total_count);
427
428 accepted_count = 0;
430 total_count = 0;
431 }
432
433 history.push(best_fitness);
435
436 SaResult {
437 best,
438 final_temperature: temperature,
439 iterations: iteration,
440 elapsed: start.elapsed(),
441 target_reached,
442 reheat_count,
443 history,
444 }
445 }
446
447 fn cool_down(&self, current_temp: f64, delta: f64, accepted: usize, total: usize) -> f64 {
449 match self.config.cooling_schedule {
450 CoolingSchedule::Geometric => current_temp * self.config.cooling_rate,
451 CoolingSchedule::Linear => (current_temp - delta).max(self.config.final_temp),
452 CoolingSchedule::Adaptive => {
453 let acceptance_rate = if total > 0 {
455 accepted as f64 / total as f64
456 } else {
457 0.5
458 };
459
460 let adjusted_rate = if acceptance_rate > 0.5 {
462 self.config.cooling_rate * 0.95 } else if acceptance_rate < 0.1 {
464 self.config.cooling_rate.powf(0.5) } else {
466 self.config.cooling_rate
467 };
468
469 current_temp * adjusted_rate
470 }
471 CoolingSchedule::LundyMees => {
472 current_temp / (1.0 + self.config.cooling_rate * current_temp)
474 }
475 }
476 }
477
478 #[cfg(feature = "parallel")]
491 pub fn run_parallel(&self, num_restarts: usize) -> SaResult<P::Solution>
492 where
493 P: Clone,
494 {
495 let num_restarts = num_restarts.max(1);
496
497 let results: Vec<SaResult<P::Solution>> = (0..num_restarts)
499 .into_par_iter()
500 .map(|_| {
501 let mut rng = rand::rng();
502 self.run_with_rng(&mut rng)
503 })
504 .collect();
505
506 results
508 .into_iter()
509 .max_by(|a, b| {
510 a.best
511 .objective()
512 .partial_cmp(&b.best.objective())
513 .unwrap_or(std::cmp::Ordering::Equal)
514 })
515 .expect("At least one result should exist")
516 }
517}
518
519#[derive(Debug, Clone)]
521pub struct PermutationSolution {
522 pub sequence: Vec<usize>,
524 pub rotations: Vec<usize>,
526 pub rotation_options: usize,
528 pub mirrors: Vec<bool>,
531 objective: f64,
533}
534
535impl PermutationSolution {
536 pub fn new(size: usize, rotation_options: usize) -> Self {
538 Self {
539 sequence: (0..size).collect(),
540 rotations: vec![0; size],
541 rotation_options,
542 mirrors: vec![false; size],
543 objective: f64::NEG_INFINITY,
544 }
545 }
546
547 pub fn random<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
549 let mut sequence: Vec<usize> = (0..size).collect();
550 sequence.shuffle(rng);
551
552 let rotations: Vec<usize> = (0..size)
553 .map(|_| rng.random_range(0..rotation_options.max(1)))
554 .collect();
555
556 let mirrors: Vec<bool> = (0..size).map(|_| rng.random()).collect();
557
558 Self {
559 sequence,
560 rotations,
561 rotation_options,
562 mirrors,
563 objective: f64::NEG_INFINITY,
564 }
565 }
566
567 pub fn len(&self) -> usize {
569 self.sequence.len()
570 }
571
572 pub fn is_empty(&self) -> bool {
574 self.sequence.is_empty()
575 }
576
577 pub fn apply_swap<R: Rng>(&self, rng: &mut R) -> Self {
579 let mut result = self.clone();
580 if result.sequence.len() < 2 {
581 return result;
582 }
583
584 let i = rng.random_range(0..result.sequence.len());
585 let j = rng.random_range(0..result.sequence.len());
586 result.sequence.swap(i, j);
587 result.objective = f64::NEG_INFINITY;
588 result
589 }
590
591 pub fn apply_relocate<R: Rng>(&self, rng: &mut R) -> Self {
593 let mut result = self.clone();
594 if result.sequence.len() < 2 {
595 return result;
596 }
597
598 let from = rng.random_range(0..result.sequence.len());
599 let to = rng.random_range(0..result.sequence.len());
600
601 if from != to {
602 let elem = result.sequence.remove(from);
603 let insert_pos = if to > from { to - 1 } else { to };
604 result
605 .sequence
606 .insert(insert_pos.min(result.sequence.len()), elem);
607 }
608
609 result.objective = f64::NEG_INFINITY;
610 result
611 }
612
613 pub fn apply_inversion<R: Rng>(&self, rng: &mut R) -> Self {
615 let mut result = self.clone();
616 let n = result.sequence.len();
617 if n < 2 {
618 return result;
619 }
620
621 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
622 if p1 > p2 {
623 std::mem::swap(&mut p1, &mut p2);
624 }
625
626 result.sequence[p1..=p2].reverse();
627 result.objective = f64::NEG_INFINITY;
628 result
629 }
630
631 pub fn apply_rotation<R: Rng>(&self, rng: &mut R) -> Self {
633 let mut result = self.clone();
634 if result.rotations.is_empty() || result.rotation_options <= 1 {
635 return result;
636 }
637
638 let idx = rng.random_range(0..result.rotations.len());
639 result.rotations[idx] = rng.random_range(0..result.rotation_options);
640 result.objective = f64::NEG_INFINITY;
641 result
642 }
643
644 pub fn apply_mirror_flip<R: Rng>(&self, rng: &mut R) -> Self {
649 let mut result = self.clone();
650 if result.mirrors.is_empty() {
651 return result;
652 }
653
654 let idx = rng.random_range(0..result.mirrors.len());
655 result.mirrors[idx] = !result.mirrors[idx];
656 result.objective = f64::NEG_INFINITY;
657 result
658 }
659
660 pub fn apply_chain<R: Rng>(&self, rng: &mut R) -> Self {
662 let mut result = self.clone();
663 let n = result.sequence.len();
664 if n < 4 {
665 return self.apply_swap(rng);
667 }
668
669 let mut positions: Vec<usize> = (0..n).collect();
671 positions.shuffle(rng);
672 let mut selected: Vec<usize> = positions.into_iter().take(3).collect();
673 selected.sort();
674
675 let (p1, p2, p3) = (selected[0], selected[1], selected[2]);
676
677 let seg1: Vec<usize> = result.sequence[..p1].to_vec();
680 let seg2: Vec<usize> = result.sequence[p1..p2].to_vec();
681 let seg3: Vec<usize> = result.sequence[p2..p3].to_vec();
682 let seg4: Vec<usize> = result.sequence[p3..].to_vec();
683
684 result.sequence = [seg1, seg3, seg2, seg4].concat();
685 result.objective = f64::NEG_INFINITY;
686 result
687 }
688}
689
690impl SaSolution for PermutationSolution {
691 fn objective(&self) -> f64 {
692 self.objective
693 }
694
695 fn set_objective(&mut self, value: f64) {
696 self.objective = value;
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 struct SimpleMaxProblem {
705 size: usize,
706 }
707
708 impl SaProblem for SimpleMaxProblem {
709 type Solution = PermutationSolution;
710
711 fn initial_solution<R: Rng>(&self, rng: &mut R) -> Self::Solution {
712 PermutationSolution::random(self.size, 1, rng)
713 }
714
715 fn neighbor<R: Rng>(
716 &self,
717 solution: &Self::Solution,
718 operator: NeighborhoodOperator,
719 rng: &mut R,
720 ) -> Self::Solution {
721 match operator {
722 NeighborhoodOperator::Swap => solution.apply_swap(rng),
723 NeighborhoodOperator::Relocate => solution.apply_relocate(rng),
724 NeighborhoodOperator::Inversion => solution.apply_inversion(rng),
725 NeighborhoodOperator::Rotation => solution.apply_rotation(rng),
726 NeighborhoodOperator::Chain => solution.apply_chain(rng),
727 NeighborhoodOperator::MirrorFlip => solution.apply_mirror_flip(rng),
728 }
729 }
730
731 fn evaluate(&self, solution: &mut Self::Solution) {
732 let mut inversions = 0i64;
735 for i in 0..solution.sequence.len() {
736 for j in (i + 1)..solution.sequence.len() {
737 if solution.sequence[i] > solution.sequence[j] {
738 inversions += 1;
739 }
740 }
741 }
742 solution.set_objective(-inversions as f64);
743 }
744 }
745
746 #[test]
747 fn test_sa_basic() {
748 let config = SaConfig::default()
749 .with_initial_temp(100.0)
750 .with_final_temp(0.1)
751 .with_cooling_rate(0.9)
752 .with_iterations_per_temp(50)
753 .with_max_iterations(5000);
754
755 let problem = SimpleMaxProblem { size: 10 };
756 let runner = SaRunner::new(config, problem);
757 let result = runner.run();
758
759 assert!(result.best.objective() > -20.0);
761 assert!(result.iterations > 0);
762 }
763
764 #[test]
765 fn test_cooling_schedules() {
766 let problem = SimpleMaxProblem { size: 5 };
767
768 for schedule in [
769 CoolingSchedule::Geometric,
770 CoolingSchedule::Linear,
771 CoolingSchedule::Adaptive,
772 CoolingSchedule::LundyMees,
773 ] {
774 let config = SaConfig::default()
775 .with_cooling_schedule(schedule)
776 .with_max_iterations(1000);
777
778 let runner = SaRunner::new(config, problem.clone());
779 let result = runner.run();
780
781 assert!(result.iterations > 0);
783 }
784 }
785
786 #[test]
787 fn test_neighborhood_operators() {
788 let mut rng = rand::rng();
789 let solution = PermutationSolution::random(10, 4, &mut rng);
790
791 let swap = solution.apply_swap(&mut rng);
793 let relocate = solution.apply_relocate(&mut rng);
794 let inversion = solution.apply_inversion(&mut rng);
795 let rotation = solution.apply_rotation(&mut rng);
796 let chain = solution.apply_chain(&mut rng);
797
798 for sol in [&swap, &relocate, &inversion, &rotation, &chain] {
799 let mut sorted = sol.sequence.clone();
800 sorted.sort();
801 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
802 }
803 }
804
805 #[test]
806 fn test_reheating() {
807 let config = SaConfig::default()
808 .with_initial_temp(10.0)
809 .with_final_temp(0.1)
810 .with_max_iterations(500)
811 .with_reheating(50, 1.5);
812
813 let problem = SimpleMaxProblem { size: 8 };
814 let runner = SaRunner::new(config, problem);
815 let result = runner.run();
816
817 assert!(result.iterations > 0);
819 }
820
821 impl Clone for SimpleMaxProblem {
822 fn clone(&self) -> Self {
823 Self { size: self.size }
824 }
825 }
826}