#[cfg(feature = "bayesian")]
use {
friedrich::gaussian_process::GaussianProcess,
friedrich::kernel::Gaussian,
friedrich::prior::ConstantPrior,
statrs::distribution::{Continuous, ContinuousCDF, Normal},
};
#[cfg(feature = "bayesian")]
#[macro_export]
macro_rules! bayesian_search {
(
$init_parameters: tt,
$objective_function: tt,
$gen_params: tt,
$n_iter: expr
) => {{
use $crate::{
friedrich::gaussian_process::GaussianProcess, friedrich::kernel::Gaussian,
friedrich::prior::ConstantPrior,
};
let mut x_init: Vec<Vec<f64>> = $init_parameters();
let mut y_init: Vec<f64> = Vec::with_capacity(x_init.len());
for x in &x_init {
y_init.push($objective_function(x));
}
let mut y_max = f64::MIN;
let mut y_index = 0;
for (i, y) in y_init.clone().iter_mut().enumerate() {
if *y > y_max {
y_max = *y;
y_index = i;
}
}
let mut x_max = x_init[y_index].clone();
let mut gp = GaussianProcess::default(x_init.clone(), y_init.clone());
for i in 0..$n_iter {
println!("-----\nIteration {i}");
let x_samples = $gen_params(&x_init);
let x_next = acquisition_function(&x_init, &x_samples, &gp);
let y_next = $objective_function(&x_next);
println!("New point {:?}", &x_next);
println!("f(x) = {y_next}");
let y_pred = gp.predict(&x_next);
println!("Predicted f(x) = {y_pred}");
gp.add_samples(&vec![x_next.clone()], &vec![y_next]);
x_init.push(x_next.clone());
if y_next > y_max {
y_max = y_next;
x_max = x_next;
}
}
(x_max, y_max)
}};
(
$init_parameters: tt,
$objective_function: tt,
$num_of_params: expr,
$n_iter: expr,
$batch_size: expr,
$scale: expr
) => {{
use $crate::{
friedrich::gaussian_process::GaussianProcess, friedrich::kernel::Gaussian,
friedrich::prior::ConstantPrior,
};
let mut x_init: Vec<Vec<f64>> = $init_parameters();
let mut y_init: Vec<f64> = Vec::with_capacity(x_init.len());
for x in &x_init {
y_init.push($objective_function(x));
}
let num_of_params: usize = $num_of_params;
let mut y_max = f64::MIN;
let mut y_index = 0;
for (i, y) in y_init.clone().iter_mut().enumerate() {
if *y > y_max {
y_max = *y;
y_index = i;
}
}
let mut x_max = x_init[y_index].clone();
let mut gp = GaussianProcess::default(x_init.clone(), y_init.clone());
for i in 0..$n_iter {
println!("-----\nIteration {i}");
let x_samples =
$crate::explore::bayesian::generate_samples($batch_size, $scale, num_of_params);
let x_next = acquisition_function(&x_init, &x_samples, &gp);
let y_next = $objective_function(&x_next);
println!("New point {:?}", &x_next);
println!("f(x) = {y_next}");
let y_pred = gp.predict(&x_next);
println!("Predicted f(x) = {y_pred}");
gp.add_samples(&vec![x_next.clone()], &vec![y_next]);
x_init.push(x_next.clone());
if y_next > y_max {
y_max = y_next;
x_max = x_next;
}
}
(x_max, y_max)
}};
}
#[cfg(any(feature = "bayesian"))]
pub fn generate_samples(batch_size: usize, scale: f64, num_of_params: usize) -> Vec<Vec<f64>> {
(0..batch_size)
.into_iter()
.map(|_| {
let mut t_x = Vec::with_capacity(num_of_params);
let mut rng = rand::rng();
for _ in 0..num_of_params {
t_x.push(rand::Rng::random_range(&mut rng, -1.0..=1.0) * scale);
}
t_x
})
.collect()
}
#[cfg(feature = "bayesian")]
pub fn acquisition_function(
x_init: &Vec<Vec<f64>>,
x_samples: &[Vec<f64>],
gp: &GaussianProcess<Gaussian, ConstantPrior>,
) -> Vec<f64> {
let scores = x_samples
.iter()
.map(|x| expected_improvement(x_init, x, gp));
let mut max_score = f64::MIN;
let mut max_index = 0;
for (i, score) in scores.enumerate() {
if score > max_score {
max_score = score;
max_index = i;
}
}
x_samples[max_index].clone()
}
#[cfg(feature = "bayesian")]
pub fn expected_improvement(
x_init: &Vec<Vec<f64>>,
x: &Vec<f64>,
gp: &GaussianProcess<Gaussian, ConstantPrior>,
) -> f64 {
let mut sigma_y_new: f64;
let mean_y_new = gp.predict(x);
sigma_y_new = gp.predict_variance(x); sigma_y_new = sigma_y_new.sqrt();
if sigma_y_new == 0. {
return 0.;
}
let mut mean_y: Vec<f64> = Vec::with_capacity(x_init.len());
for x in x_init {
mean_y.push(gp.predict(x));
}
let mut mean_y_max = f64::MIN;
for m_y in &mean_y {
if *m_y > mean_y_max {
mean_y_max = *m_y;
}
}
let z = (mean_y_new - mean_y_max) / sigma_y_new;
let normal = Normal::new(0.0, 1.0)
.expect("Error building normal distribution inside acquisition function");
let z_cfd = normal.cdf(z);
let z_pdf = normal.pdf(z);
(mean_y_new - mean_y_max) * z_cfd + sigma_y_new * z_pdf
}