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