const GSL_LOG_DBL_MIN: f64 = -7.0839641853226408e+02;
pub struct SimAnnealing<T: Clone> {
x0_p: T,
params: SimAnnealingParams,
Efunc_t: gsl_siman_Efunc_t<T>,
step_t: gsl_siman_step_t<T>,
metric_t: gsl_siman_metric_t<T>,
print_t: Option<gsl_siman_print_t<T>>,
}
type gsl_siman_Efunc_t<T> = fn(&T) -> f64;
type gsl_siman_step_t<T> = fn(&mut crate::Rng, &mut T, f64);
type gsl_siman_metric_t<T> = fn(&T, &T) -> f64;
type gsl_siman_print_t<T> = fn(&T);
fn boltzmann(E: f64, new_E: f64, T: f64, params: &SimAnnealingParams) -> f64 {
let x = -(new_E - E) / (params.k * T);
if x < GSL_LOG_DBL_MIN {
0.0
} else {
x.exp()
}
}
impl<T> SimAnnealing<T>
where
T: Clone,
{
pub fn new(
x0_p: T,
ef: gsl_siman_Efunc_t<T>,
take_step: gsl_siman_step_t<T>,
distance: gsl_siman_metric_t<T>,
print_pos: Option<gsl_siman_print_t<T>>,
params: SimAnnealingParams,
) -> SimAnnealing<T> {
SimAnnealing {
x0_p,
params,
Efunc_t: ef,
step_t: take_step,
metric_t: distance,
print_t: print_pos,
}
}
pub fn solve(&self, rng: &mut crate::Rng) -> T {
let mut x = self.x0_p.clone();
let mut new_x = self.x0_p.clone();
let mut best_x = self.x0_p.clone();
let mut n_evals = 0_usize;
let mut E = (self.Efunc_t)(&self.x0_p);
let mut best_E = E;
let mut Temp = self.params.t_initial;
let Temp_factor = 1.0 / self.params.mu_t;
if self.print_t.is_some() {
println!(
"{i:^6} | {e:^7} | {t:^12} | {p:^15} | {E:^13}",
i = "#-iter",
e = "#-evals",
t = "temperature",
p = "position",
E = "energy"
);
}
let mut iter = 0;
loop {
for _ in 0..self.params.iters_fixed_T {
x = new_x.clone();
(self.step_t)(rng, &mut new_x, self.params.step_size);
let new_E = (self.Efunc_t)(&new_x);
n_evals += 1;
if new_E <= best_E {
best_x = new_x.clone();
best_E = new_E;
}
if new_E < E {
if new_E < best_E {
best_x = new_x.clone();
best_E = new_E;
}
x = new_x.clone();
E = new_E;
} else if rng.uniform() < boltzmann(E, new_E, Temp, &self.params) {
x = new_x.clone();
E = new_E;
}
}
if let Some(ref printf) = self.print_t {
print!("{:>06} | {:>07} | {:>12.10} | ", iter, n_evals, Temp);
printf(&x);
println!(" | {:+>13.12}", E);
}
Temp *= Temp_factor;
iter += 1;
if Temp < self.params.t_min {
break;
}
}
best_x
}
pub fn solve_many(&self, rng: &mut crate::Rng) -> T {
let mut x = self.x0_p.clone();
let mut new_x = Vec::with_capacity(self.params.n_tries);
let mut energies = Vec::with_capacity(self.params.n_tries);
let mut probs = Vec::with_capacity(self.params.n_tries);
let mut sum_probs = Vec::with_capacity(self.params.n_tries);
let mut Temp = self.params.t_initial;
let Temp_factor = 1.0 / self.params.mu_t;
if self.print_t.is_some() {
println!(
"{i:^6} | {t:^12} | {p:^15} | {d:^15} | {E:^13}",
i = "#-iter",
t = "temperature",
p = "position",
d = "delta_pos",
E = "energy"
);
}
let mut iter = 0;
loop {
let Ex = (self.Efunc_t)(&x);
for i in 0..self.params.n_tries - 1 {
sum_probs.push(0.0);
new_x.push(x.clone());
(self.step_t)(rng, &mut new_x[i], self.params.step_size);
energies.push((self.Efunc_t)(&new_x[i]));
probs.push(boltzmann(Ex, energies[i], Temp, &self.params));
}
new_x.push(x.clone());
energies.push(Ex);
probs.push(boltzmann(
Ex,
energies[self.params.n_tries - 2],
Temp,
&self.params,
));
sum_probs.push(0.0);
sum_probs[0] = probs[0];
for i in 1..self.params.n_tries {
sum_probs[i] = sum_probs[i - 1] + probs[i];
}
let u = rng.uniform() * *sum_probs.last().unwrap();
for i in 0..self.params.n_tries {
if u < sum_probs[i] {
x = new_x[i].clone();
break;
}
}
if let Some(ref printf) = self.print_t {
print!("{:>06} | {:>12.10} | ", iter, Temp);
printf(&x);
println!(
" | {: >15.12} | {: >13.12}",
(self.metric_t)(&x, &self.x0_p),
Ex
);
}
Temp *= Temp_factor;
iter += 1;
if Temp < self.params.t_min {
break;
}
}
x
}
}
pub struct SimAnnealingParams {
n_tries: usize,
iters_fixed_T: usize,
step_size: f64,
k: f64,
t_initial: f64,
mu_t: f64,
t_min: f64,
}
impl SimAnnealingParams {
pub fn new(
n_tries: usize,
iters: usize,
step_size: f64,
k: f64,
t_initial: f64,
mu_t: f64,
t_min: f64,
) -> SimAnnealingParams {
SimAnnealingParams {
n_tries,
iters_fixed_T: iters,
step_size,
k,
t_initial,
mu_t,
t_min,
}
}
}