metaheuristics/simulated_annealing/mod.rs
1//! Find an approximate solution to your optimisation problem using Simulated Annealing
2//!
3//! When metal is heated to melting point, its atoms are let loose to move freely, and do so in a
4//! random fashion. If cooled too quickly (a process known as quenching), the random positioning of
5//! atoms gets frozen in time, creating a hard and brittle metal. However if allowed to cool at a
6//! slower rate (a process known as annealing), the atoms arrange in a more uniform fashion,
7//! creating a soft and malleable metal. Simulated annealing borrows from this process.
8//!
9//! Here we duplicate the functionality of the `metaheuristics::hill_climbing` module but with
10//! slight modification - at each iteration, we introduce a probability of going downhill! This
11//! probability mimicks the cooling temperature from its physical counterpart. At first, the
12//! probability of going downhill is 1. But as time moves on, we lower that probability until time
13//! has run out.
14//!
15//! The probability of going downhill, or cooling temperature, is given by the following function:
16//!ignore
17//! P(t) = e^(-10*(t^3))
18//!
19//! and can be seen by the following gnuplot:
20//!
21//!```bash
22//!cat <<EOF | gnuplot -p
23//! set xrange [0:1];
24//! set yrange [0:1];
25//! set xlabel "Runtime";
26//! set ylabel "Probability of going downhill";
27//! set style line 12 lc rgb '#9bffff' lt 0 lw 1;
28//! set grid back ls 12;
29//! set style line 11 lc rgb '#5980d4' lt 1;
30//! set border 3 back ls 11;
31//! set tics nomirror;
32//! set key off;
33//! f(x) = exp(-10*(x**3));
34//! plot f(x) with lines lc rgb '#d52339';
35//!EOF
36//!```
37//!
38//! For more info on Simulated Annealing, please see the [Simulated
39//! Annealing](https://wikipedia.org/wiki/Simulated_annealing) Wikipedia article.
40//!
41//!# Examples
42//!
43//!```ignore
44//!let solution = metaheuristics::simulated_annealing::solve(&mut problem, runtime);
45//!```
46
47use super::Metaheuristics;
48use rand::{thread_rng, Rng};
49use time::{Duration, Instant};
50
51/// Returns an approximate solution to your optimisation problem using Simulated Annealing
52///
53///# Parameters
54///
55/// `problem` is the type that implements the `Metaheuristics` trait.
56///
57/// `runtime` is a `time::Duration` specifying how long to spend searching for a solution.
58///
59///# Examples
60///
61///```ignore
62///let solution = metaheuristics::simulated_annealing::solve(&mut problem, runtime);
63///```
64pub fn solve<T>(problem: &mut dyn Metaheuristics<T>, runtime: Duration) -> T {
65 let mut best_candidate = problem.generate_candidate();
66 let mut annealing_candidate = problem.tweak_candidate(&best_candidate);
67 let start_time = Instant::now();
68 let runtime_in_milliseconds = runtime.whole_milliseconds() as f64;
69
70 loop {
71 let portion_elapsed =
72 (start_time.elapsed().whole_milliseconds() as f64) / runtime_in_milliseconds;
73
74 if portion_elapsed >= 1.0 {
75 break;
76 }
77
78 let next_candidate = problem.tweak_candidate(&annealing_candidate);
79 let next_is_better =
80 problem.rank_candidate(&next_candidate) > problem.rank_candidate(&annealing_candidate);
81 let replacement_threshold = 1.0f64.exp().powf(-10.0 * portion_elapsed.powf(3.0));
82
83 if next_is_better || (thread_rng().gen_range(0.0..1.0) < replacement_threshold) {
84 annealing_candidate = next_candidate;
85 }
86
87 if problem.rank_candidate(&annealing_candidate) > problem.rank_candidate(&best_candidate) {
88 best_candidate = problem.clone_candidate(&annealing_candidate);
89 }
90 }
91
92 best_candidate
93}