1use rand::prelude::*;
19use std::sync::atomic::{AtomicBool, Ordering};
20use std::sync::Arc;
21use std::time::Duration;
22
23use crate::error::{check_above, check_at_least, check_range, Result};
24use crate::timing::{expired, Timer};
25
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Serialize};
28
29#[derive(Debug, Clone, Copy, Default)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub enum CoolingSchedule {
33 #[default]
35 Geometric,
36 Linear,
38 Adaptive,
40 LundyMees,
42}
43
44#[derive(Debug, Clone)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47pub struct SaConfig {
48 pub initial_temp: f64,
50 pub final_temp: f64,
52 pub cooling_rate: f64,
54 pub iterations_per_temp: usize,
56 pub max_iterations: Option<u64>,
58 pub cooling_schedule: CoolingSchedule,
60 pub time_limit: Option<Duration>,
62 pub target_fitness: Option<f64>,
64 pub enable_reheating: bool,
66 pub reheat_threshold: u64,
68 pub reheat_factor: f64,
70}
71
72impl Default for SaConfig {
73 fn default() -> Self {
74 Self {
75 initial_temp: 1000.0,
76 final_temp: 0.001,
77 cooling_rate: 0.95,
78 iterations_per_temp: 100,
79 max_iterations: Some(100_000),
80 cooling_schedule: CoolingSchedule::Geometric,
81 time_limit: None,
82 target_fitness: None,
83 enable_reheating: false,
84 reheat_threshold: 1000,
85 reheat_factor: 2.0,
86 }
87 }
88}
89
90impl SaConfig {
91 pub fn new() -> Self {
93 Self::default()
94 }
95
96 pub fn with_initial_temp(mut self, temp: f64) -> Self {
98 self.initial_temp = temp;
99 self
100 }
101
102 pub fn with_final_temp(mut self, temp: f64) -> Self {
104 self.final_temp = temp;
105 self
106 }
107
108 pub fn with_cooling_rate(mut self, rate: f64) -> Self {
110 self.cooling_rate = rate;
111 self
112 }
113
114 pub fn validate(&self) -> Result<()> {
123 check_range("cooling_rate", self.cooling_rate, 0.001, 0.9999)?;
124 check_above("initial_temp", self.initial_temp, 0.0)?;
125 check_above("final_temp", self.final_temp, 0.0)?;
126 check_range("final_temp", self.final_temp, 0.0, self.initial_temp)?;
127 check_at_least("iterations_per_temp", self.iterations_per_temp as f64, 1.0)?;
128 check_at_least("reheat_factor", self.reheat_factor, 1.0)?;
129 Ok(())
130 }
131
132 pub fn with_iterations_per_temp(mut self, iterations: usize) -> Self {
134 self.iterations_per_temp = iterations;
135 self
136 }
137
138 pub fn with_max_iterations(mut self, iterations: u64) -> Self {
140 self.max_iterations = Some(iterations);
141 self
142 }
143
144 pub fn with_cooling_schedule(mut self, schedule: CoolingSchedule) -> Self {
146 self.cooling_schedule = schedule;
147 self
148 }
149
150 pub fn with_time_limit(mut self, duration: Duration) -> Self {
152 self.time_limit = Some(duration);
153 self
154 }
155
156 pub fn with_target_fitness(mut self, fitness: f64) -> Self {
158 self.target_fitness = Some(fitness);
159 self
160 }
161
162 pub fn with_reheating(mut self, threshold: u64, factor: f64) -> Self {
164 self.enable_reheating = true;
165 self.reheat_threshold = threshold;
166 self.reheat_factor = factor;
167 self
168 }
169}
170
171pub trait SaSolution: Clone + Send + Sync {
173 fn objective(&self) -> f64;
176
177 fn set_objective(&mut self, value: f64);
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum NeighborhoodOperator {
184 Swap,
186 Relocate,
188 Inversion,
190 Rotation,
192 Chain,
194 MirrorFlip,
199}
200
201pub trait SaProblem: Send + Sync {
203 type Solution: SaSolution;
205
206 fn initial_solution<R: Rng>(&self, rng: &mut R) -> Self::Solution;
208
209 fn neighbor<R: Rng>(
211 &self,
212 solution: &Self::Solution,
213 operator: NeighborhoodOperator,
214 rng: &mut R,
215 ) -> Self::Solution;
216
217 fn evaluate(&self, solution: &mut Self::Solution);
219
220 fn available_operators(&self) -> Vec<NeighborhoodOperator> {
222 vec![
223 NeighborhoodOperator::Swap,
224 NeighborhoodOperator::Relocate,
225 NeighborhoodOperator::Inversion,
226 ]
227 }
228
229 fn on_temperature_change(
231 &self,
232 _temperature: f64,
233 _iteration: u64,
234 _best: &Self::Solution,
235 _current: &Self::Solution,
236 ) {
237 }
239}
240
241#[derive(Debug, Clone)]
243pub struct SaProgress {
244 pub temperature: f64,
246 pub iteration: u64,
248 pub best_fitness: f64,
250 pub current_fitness: f64,
252 pub acceptance_rate: f64,
254 pub elapsed: Duration,
256 pub running: bool,
258}
259
260#[derive(Debug, Clone)]
262pub struct SaResult<S: SaSolution> {
263 pub best: S,
265 pub final_temperature: f64,
267 pub iterations: u64,
269 pub elapsed: Duration,
271 pub target_reached: bool,
273 pub reheat_count: u32,
275 pub history: Vec<f64>,
277}
278
279pub struct SaRunner<P: SaProblem> {
281 config: SaConfig,
282 problem: P,
283 cancelled: Arc<AtomicBool>,
284}
285
286impl<P: SaProblem> SaRunner<P> {
287 pub fn new(config: SaConfig, problem: P) -> Result<Self> {
292 Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
293 }
294
295 pub fn with_cancellation(
301 config: SaConfig,
302 problem: P,
303 cancelled: Arc<AtomicBool>,
304 ) -> Result<Self> {
305 config.validate()?;
306 Ok(Self {
307 config,
308 problem,
309 cancelled,
310 })
311 }
312
313 pub fn cancel_handle(&self) -> Arc<AtomicBool> {
315 self.cancelled.clone()
316 }
317
318 pub fn run(&self) -> SaResult<P::Solution> {
320 self.run_with_rng(&mut rand::rng())
321 }
322
323 pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> SaResult<P::Solution> {
325 let start = Timer::now();
326 let mut history = Vec::new();
327
328 let mut current = self.problem.initial_solution(rng);
330 self.problem.evaluate(&mut current);
331 let mut best = current.clone();
332 let mut best_fitness = best.objective();
333
334 let mut temperature = self.config.initial_temp;
335 let mut iteration = 0u64;
336 let mut target_reached = false;
337 let mut reheat_count = 0u32;
338 let mut stagnation_count = 0u64;
339
340 let operators = self.problem.available_operators();
341 let temp_delta = if matches!(self.config.cooling_schedule, CoolingSchedule::Linear) {
342 (self.config.initial_temp - self.config.final_temp)
343 / (self.config.max_iterations.unwrap_or(10000) as f64
344 / self.config.iterations_per_temp as f64)
345 } else {
346 0.0
347 };
348
349 let mut accepted_count = 0usize;
351 let mut total_count = 0usize;
352
353 while temperature > self.config.final_temp {
354 if self.cancelled.load(Ordering::Relaxed) {
356 break;
357 }
358
359 if expired(&start, self.config.time_limit) {
361 break;
362 }
363
364 if let Some(max) = self.config.max_iterations {
366 if iteration >= max {
367 break;
368 }
369 }
370
371 if let Some(target) = self.config.target_fitness {
373 if best_fitness >= target {
374 target_reached = true;
375 break;
376 }
377 }
378
379 for _ in 0..self.config.iterations_per_temp {
382 if expired(&start, self.config.time_limit) {
383 break;
384 }
385 iteration += 1;
386 total_count += 1;
387
388 let operator = operators[rng.random_range(0..operators.len())];
390
391 let mut neighbor = self.problem.neighbor(¤t, operator, rng);
393 self.problem.evaluate(&mut neighbor);
394
395 let current_obj = current.objective();
396 let neighbor_obj = neighbor.objective();
397 let delta = neighbor_obj - current_obj;
398
399 let accept = if delta >= 0.0 {
401 true
403 } else {
404 let probability = (delta / temperature).exp();
406 rng.random::<f64>() < probability
407 };
408
409 if accept {
410 accepted_count += 1;
411 current = neighbor;
412
413 if current.objective() > best_fitness {
415 best = current.clone();
416 best_fitness = best.objective();
417 stagnation_count = 0;
418 } else {
419 stagnation_count += 1;
420 }
421 } else {
422 stagnation_count += 1;
423 }
424
425 if let Some(max) = self.config.max_iterations {
427 if iteration >= max {
428 break;
429 }
430 }
431 }
432
433 history.push(best_fitness);
435
436 self.problem
438 .on_temperature_change(temperature, iteration, &best, ¤t);
439
440 if self.config.enable_reheating && stagnation_count >= self.config.reheat_threshold {
442 temperature *= self.config.reheat_factor;
443 temperature = temperature.min(self.config.initial_temp);
444 stagnation_count = 0;
445 reheat_count += 1;
446 }
447
448 temperature = self.cool_down(temperature, temp_delta, accepted_count, total_count);
450
451 accepted_count = 0;
453 total_count = 0;
454 }
455
456 history.push(best_fitness);
458
459 SaResult {
460 best,
461 final_temperature: temperature,
462 iterations: iteration,
463 elapsed: start.elapsed(),
464 target_reached,
465 reheat_count,
466 history,
467 }
468 }
469
470 fn cool_down(&self, current_temp: f64, delta: f64, accepted: usize, total: usize) -> f64 {
472 match self.config.cooling_schedule {
473 CoolingSchedule::Geometric => current_temp * self.config.cooling_rate,
474 CoolingSchedule::Linear => (current_temp - delta).max(self.config.final_temp),
475 CoolingSchedule::Adaptive => {
476 let acceptance_rate = if total > 0 {
478 accepted as f64 / total as f64
479 } else {
480 0.5
481 };
482
483 let adjusted_rate = if acceptance_rate > 0.5 {
485 self.config.cooling_rate * 0.95 } else if acceptance_rate < 0.1 {
487 self.config.cooling_rate.powf(0.5) } else {
489 self.config.cooling_rate
490 };
491
492 current_temp * adjusted_rate
493 }
494 CoolingSchedule::LundyMees => {
495 current_temp / (1.0 + self.config.cooling_rate * current_temp)
497 }
498 }
499 }
500}
501
502#[derive(Debug, Clone)]
504pub struct PermutationSolution {
505 pub sequence: Vec<usize>,
507 pub rotations: Vec<usize>,
509 pub rotation_options: usize,
511 pub mirrors: Vec<bool>,
514 objective: f64,
516}
517
518impl PermutationSolution {
519 pub fn new(size: usize, rotation_options: usize) -> Self {
521 Self {
522 sequence: (0..size).collect(),
523 rotations: vec![0; size],
524 rotation_options,
525 mirrors: vec![false; size],
526 objective: f64::NEG_INFINITY,
527 }
528 }
529
530 pub fn random<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
532 let mut sequence: Vec<usize> = (0..size).collect();
533 sequence.shuffle(rng);
534
535 let rotations: Vec<usize> = (0..size)
536 .map(|_| rng.random_range(0..rotation_options.max(1)))
537 .collect();
538
539 let mirrors: Vec<bool> = (0..size).map(|_| rng.random()).collect();
540
541 Self {
542 sequence,
543 rotations,
544 rotation_options,
545 mirrors,
546 objective: f64::NEG_INFINITY,
547 }
548 }
549
550 pub fn len(&self) -> usize {
552 self.sequence.len()
553 }
554
555 pub fn is_empty(&self) -> bool {
557 self.sequence.is_empty()
558 }
559
560 pub fn apply_swap<R: Rng>(&self, rng: &mut R) -> Self {
562 let mut result = self.clone();
563 if result.sequence.len() < 2 {
564 return result;
565 }
566
567 let i = rng.random_range(0..result.sequence.len());
568 let j = rng.random_range(0..result.sequence.len());
569 result.sequence.swap(i, j);
570 result.objective = f64::NEG_INFINITY;
571 result
572 }
573
574 pub fn apply_relocate<R: Rng>(&self, rng: &mut R) -> Self {
576 let mut result = self.clone();
577 if result.sequence.len() < 2 {
578 return result;
579 }
580
581 let from = rng.random_range(0..result.sequence.len());
582 let to = rng.random_range(0..result.sequence.len());
583
584 if from != to {
585 let elem = result.sequence.remove(from);
586 let insert_pos = if to > from { to - 1 } else { to };
587 result
588 .sequence
589 .insert(insert_pos.min(result.sequence.len()), elem);
590 }
591
592 result.objective = f64::NEG_INFINITY;
593 result
594 }
595
596 pub fn apply_inversion<R: Rng>(&self, rng: &mut R) -> Self {
598 let mut result = self.clone();
599 let n = result.sequence.len();
600 if n < 2 {
601 return result;
602 }
603
604 let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
605 if p1 > p2 {
606 std::mem::swap(&mut p1, &mut p2);
607 }
608
609 result.sequence[p1..=p2].reverse();
610 result.objective = f64::NEG_INFINITY;
611 result
612 }
613
614 pub fn apply_rotation<R: Rng>(&self, rng: &mut R) -> Self {
616 let mut result = self.clone();
617 if result.rotations.is_empty() || result.rotation_options <= 1 {
618 return result;
619 }
620
621 let idx = rng.random_range(0..result.rotations.len());
622 result.rotations[idx] = rng.random_range(0..result.rotation_options);
623 result.objective = f64::NEG_INFINITY;
624 result
625 }
626
627 pub fn apply_mirror_flip<R: Rng>(&self, rng: &mut R) -> Self {
632 let mut result = self.clone();
633 if result.mirrors.is_empty() {
634 return result;
635 }
636
637 let idx = rng.random_range(0..result.mirrors.len());
638 result.mirrors[idx] = !result.mirrors[idx];
639 result.objective = f64::NEG_INFINITY;
640 result
641 }
642
643 pub fn apply_chain<R: Rng>(&self, rng: &mut R) -> Self {
645 let mut result = self.clone();
646 let n = result.sequence.len();
647 if n < 4 {
648 return self.apply_swap(rng);
650 }
651
652 let mut positions: Vec<usize> = (0..n).collect();
654 positions.shuffle(rng);
655 let mut selected: Vec<usize> = positions.into_iter().take(3).collect();
656 selected.sort();
657
658 let (p1, p2, p3) = (selected[0], selected[1], selected[2]);
659
660 let seg1: Vec<usize> = result.sequence[..p1].to_vec();
663 let seg2: Vec<usize> = result.sequence[p1..p2].to_vec();
664 let seg3: Vec<usize> = result.sequence[p2..p3].to_vec();
665 let seg4: Vec<usize> = result.sequence[p3..].to_vec();
666
667 result.sequence = [seg1, seg3, seg2, seg4].concat();
668 result.objective = f64::NEG_INFINITY;
669 result
670 }
671}
672
673impl SaSolution for PermutationSolution {
674 fn objective(&self) -> f64 {
675 self.objective
676 }
677
678 fn set_objective(&mut self, value: f64) {
679 self.objective = value;
680 }
681}
682
683#[cfg(test)]
684mod tests {
685 use super::*;
686
687 struct SimpleMaxProblem {
688 size: usize,
689 }
690
691 impl SaProblem for SimpleMaxProblem {
692 type Solution = PermutationSolution;
693
694 fn initial_solution<R: Rng>(&self, rng: &mut R) -> Self::Solution {
695 PermutationSolution::random(self.size, 1, rng)
696 }
697
698 fn neighbor<R: Rng>(
699 &self,
700 solution: &Self::Solution,
701 operator: NeighborhoodOperator,
702 rng: &mut R,
703 ) -> Self::Solution {
704 match operator {
705 NeighborhoodOperator::Swap => solution.apply_swap(rng),
706 NeighborhoodOperator::Relocate => solution.apply_relocate(rng),
707 NeighborhoodOperator::Inversion => solution.apply_inversion(rng),
708 NeighborhoodOperator::Rotation => solution.apply_rotation(rng),
709 NeighborhoodOperator::Chain => solution.apply_chain(rng),
710 NeighborhoodOperator::MirrorFlip => solution.apply_mirror_flip(rng),
711 }
712 }
713
714 fn evaluate(&self, solution: &mut Self::Solution) {
715 let mut inversions = 0i64;
718 for i in 0..solution.sequence.len() {
719 for j in (i + 1)..solution.sequence.len() {
720 if solution.sequence[i] > solution.sequence[j] {
721 inversions += 1;
722 }
723 }
724 }
725 solution.set_objective(-inversions as f64);
726 }
727 }
728
729 #[test]
730 fn test_sa_basic() {
731 let config = SaConfig::default()
732 .with_initial_temp(100.0)
733 .with_final_temp(0.1)
734 .with_cooling_rate(0.9)
735 .with_iterations_per_temp(50)
736 .with_max_iterations(5000);
737
738 let problem = SimpleMaxProblem { size: 10 };
739 let runner = SaRunner::new(config, problem).expect("valid config");
740 let result = runner.run();
741
742 assert!(result.best.objective() > -20.0);
744 assert!(result.iterations > 0);
745 }
746
747 #[test]
748 fn test_cooling_schedules() {
749 let problem = SimpleMaxProblem { size: 5 };
750
751 for schedule in [
752 CoolingSchedule::Geometric,
753 CoolingSchedule::Linear,
754 CoolingSchedule::Adaptive,
755 CoolingSchedule::LundyMees,
756 ] {
757 let config = SaConfig::default()
758 .with_cooling_schedule(schedule)
759 .with_max_iterations(1000);
760
761 let runner = SaRunner::new(config, problem.clone()).expect("valid config");
762 let result = runner.run();
763
764 assert!(result.iterations > 0);
766 }
767 }
768
769 #[test]
770 fn test_neighborhood_operators() {
771 let mut rng = rand::rng();
772 let solution = PermutationSolution::random(10, 4, &mut rng);
773
774 let swap = solution.apply_swap(&mut rng);
776 let relocate = solution.apply_relocate(&mut rng);
777 let inversion = solution.apply_inversion(&mut rng);
778 let rotation = solution.apply_rotation(&mut rng);
779 let chain = solution.apply_chain(&mut rng);
780
781 for sol in [&swap, &relocate, &inversion, &rotation, &chain] {
782 let mut sorted = sol.sequence.clone();
783 sorted.sort();
784 assert_eq!(sorted, (0..10).collect::<Vec<_>>());
785 }
786 }
787
788 #[test]
789 fn test_reheating() {
790 let config = SaConfig::default()
791 .with_initial_temp(10.0)
792 .with_final_temp(0.1)
793 .with_max_iterations(500)
794 .with_reheating(50, 1.5);
795
796 let problem = SimpleMaxProblem { size: 8 };
797 let runner = SaRunner::new(config, problem).expect("valid config");
798 let result = runner.run();
799
800 assert!(result.iterations > 0);
802 }
803
804 impl Clone for SimpleMaxProblem {
805 fn clone(&self) -> Self {
806 Self { size: self.size }
807 }
808 }
809}