use crate::construction::heuristics::InsertionContext;
use crate::construction::Quota;
use crate::models::{Problem, Solution};
use crate::solver::evolution::EvolutionConfig;
use crate::solver::hyper::HyperHeuristic;
use crate::solver::mutation::*;
use crate::solver::population::Population;
use crate::solver::termination::*;
use crate::solver::{Solver, Telemetry};
use crate::utils::{Environment, TimeQuota};
use std::sync::Arc;
pub struct Builder {
pub max_generations: Option<usize>,
pub max_time: Option<usize>,
pub cost_variation: Option<(usize, f64)>,
pub config: EvolutionConfig,
}
impl Builder {
pub fn new(problem: Arc<Problem>, environment: Arc<Environment>) -> Self {
Self {
max_generations: None,
max_time: None,
cost_variation: None,
config: EvolutionConfig::new(problem, environment),
}
}
}
impl Builder {
pub fn with_telemetry(mut self, telemetry: Telemetry) -> Self {
self.config.telemetry = telemetry;
self
}
pub fn with_max_generations(mut self, limit: Option<usize>) -> Self {
self.max_generations = limit;
self
}
pub fn with_cost_variation(mut self, variation: Option<(usize, f64)>) -> Self {
self.cost_variation = variation;
self
}
pub fn with_max_time(mut self, limit: Option<usize>) -> Self {
self.max_time = limit;
self
}
pub fn with_init_params(
mut self,
max_size: usize,
quota: f64,
methods: Vec<(Arc<dyn Recreate + Send + Sync>, usize)>,
) -> Self {
self.config.telemetry.log("configured to use custom initial population parameters");
self.config.population.initial.max_size = max_size;
self.config.population.initial.quota = quota;
self.config.population.initial.methods = methods;
self
}
pub fn with_init_solutions(mut self, solutions: Vec<Solution>, max_init_size: Option<usize>) -> Self {
self.config.telemetry.log(
format!(
"provided {} initial solutions to start with, max init size: {}",
solutions.len(),
if let Some(max_init_size) = max_init_size { max_init_size.to_string() } else { "default".to_string() }
)
.as_str(),
);
if let Some(max_size) = max_init_size {
self.config.population.initial.max_size = max_size;
}
self.config.population.initial.individuals = solutions
.into_iter()
.map(|solution| {
InsertionContext::new_from_solution(
self.config.problem.clone(),
(solution, None),
self.config.environment.clone(),
)
})
.collect();
self
}
pub fn with_population(mut self, population: Box<dyn Population + Send + Sync>) -> Self {
self.config.telemetry.log("configured to use custom population");
self.config.population.variation = Some(population);
self
}
pub fn with_hyper(mut self, hyper: Box<dyn HyperHeuristic + Send + Sync>) -> Self {
self.config.telemetry.log("configured to use custom hyper-heuristic");
self.config.hyper = hyper;
self
}
pub fn with_termination(mut self, termination: Arc<dyn Termination + Send + Sync>) -> Self {
self.config.telemetry.log("configured to use custom termination parameters");
self.config.termination = termination;
self
}
pub fn build(self) -> Result<Solver, String> {
let problem = self.config.problem.clone();
let (criterias, quota): (Vec<Box<dyn Termination + Send + Sync>>, _) =
match (self.max_generations, self.max_time, self.cost_variation) {
(None, None, None) => {
self.config
.telemetry
.log("configured to use default max-generations (3000) and max-time (300secs)");
(vec![Box::new(MaxGeneration::new(3000)), Box::new(MaxTime::new(300.))], None)
}
_ => {
let mut criterias: Vec<Box<dyn Termination + Send + Sync>> = vec![];
if let Some(limit) = self.max_generations {
self.config.telemetry.log(format!("configured to use max-generations: {}", limit).as_str());
criterias.push(Box::new(MaxGeneration::new(limit)))
}
let quota = if let Some(limit) = self.max_time {
self.config.telemetry.log(format!("configured to use max-time: {}s", limit).as_str());
criterias.push(Box::new(MaxTime::new(limit as f64)));
Some(create_time_quota(limit))
} else {
None
};
if let Some((sample, threshold)) = self.cost_variation {
self.config.telemetry.log(
format!(
"configured to use cost variation with sample: {}, threshold: {}",
sample, threshold
)
.as_str(),
);
criterias.push(Box::new(CostVariation::new(sample, threshold)))
}
(criterias, quota)
}
};
let mut config = self.config;
config.termination = Arc::new(CompositeTermination::new(criterias));
config.quota = quota;
Ok(Solver { problem, config })
}
}
fn create_time_quota(limit: usize) -> Arc<dyn Quota + Sync + Send> {
Arc::new(TimeQuota::new(limit as f64))
}