use crate::{Distribution, Pruner, Sampler, StudyState, Trial, Value};
use std::sync::Mutex;
pub struct TrialContext<'a> {
trial: Trial,
sampler: &'a Mutex<Box<dyn Sampler>>,
study_state: &'a StudyState,
pruner: &'a dyn Pruner,
}
impl<'a> TrialContext<'a> {
pub(crate) fn new(
number: usize,
sampler: &'a Mutex<Box<dyn Sampler>>,
study_state: &'a StudyState,
pruner: &'a dyn Pruner,
) -> Self {
TrialContext {
trial: Trial::new(number),
sampler,
study_state,
pruner,
}
}
pub fn number(&self) -> usize {
self.trial.number
}
fn suggest(&mut self, name: &str, distribution: Distribution) -> Value {
let value = {
let mut sampler = self
.sampler
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
sampler.suggest(self.study_state, &self.trial, name, &distribution)
};
self.trial.record(name, distribution, value.clone());
value
}
pub fn suggest_float(&mut self, name: &str, low: f64, high: f64) -> f64 {
let v = self.suggest(name, Distribution::Uniform { low, high });
coerce_float(&v)
}
pub fn suggest_loguniform(&mut self, name: &str, low: f64, high: f64) -> f64 {
let v = self.suggest(name, Distribution::LogUniform { low, high });
coerce_float(&v)
}
pub fn suggest_int(&mut self, name: &str, low: i64, high: i64) -> i64 {
let v = self.suggest(name, Distribution::IntUniform { low, high });
match v {
Value::Int(x) => x,
Value::Float(x) => x.round() as i64,
Value::Categorical(_) => low,
}
}
pub fn suggest_categorical(&mut self, name: &str, choices: &[&str]) -> String {
let dist = Distribution::Categorical {
choices: choices.iter().map(|s| s.to_string()).collect(),
};
let v = self.suggest(name, dist);
match v {
Value::Categorical(s) => s,
Value::Int(i) => choices
.get(i.max(0) as usize)
.map(|s| s.to_string())
.unwrap_or_default(),
Value::Float(f) => choices
.get(f as usize)
.map(|s| s.to_string())
.unwrap_or_default(),
}
}
pub fn report(&mut self, step: usize, value: f64) {
if let Some(slot) = self
.trial
.intermediate_values
.iter_mut()
.find(|(s, _)| *s == step)
{
slot.1 = value;
} else {
self.trial.intermediate_values.push((step, value));
}
}
pub fn should_prune(&self) -> bool {
self.pruner.should_prune(self.study_state, &self.trial)
}
pub fn study_state(&self) -> &StudyState {
self.study_state
}
pub(crate) fn into_trial(self) -> Trial {
self.trial
}
}
fn coerce_float(v: &Value) -> f64 {
match v {
Value::Float(x) => *x,
Value::Int(x) => *x as f64,
Value::Categorical(_) => f64::NAN,
}
}