use crate::construction::heuristics::InsertionContext;
use crate::construction::Quota;
use crate::models::{Problem, Solution};
use crate::solver::evolution::EvolutionConfig;
use crate::solver::mutation::*;
use crate::solver::selection::Selection;
use crate::solver::termination::*;
use crate::solver::{Solver, Telemetry};
use crate::utils::{DefaultRandom, 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 seed: Option<u64>,
pub config: EvolutionConfig,
}
impl Builder {
pub fn new(problem: Arc<Problem>) -> Self {
Self {
max_generations: None,
max_time: None,
cost_variation: None,
seed: None,
config: EvolutionConfig::new(problem),
}
}
}
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,
size: Option<usize>,
initial_methods: Option<Vec<(Box<dyn Recreate + Send + Sync>, usize)>>,
) -> Self {
self.config.telemetry.log("configured to use custom initial population parameters");
if let Some(size) = size {
self.config.population.initial.size = size;
}
if let Some(initial_methods) = initial_methods {
self.config.population.initial.methods = initial_methods;
}
self
}
pub fn with_init_solutions(mut self, solutions: Vec<Solution>) -> Self {
self.config.telemetry.log(format!("provided {} initial solutions to start with", solutions.len()).as_str());
self.config.population.initial.individuals = solutions
.into_iter()
.map(|solution| {
InsertionContext::new_from_solution(
self.config.problem.clone(),
(solution, None),
Arc::new(DefaultRandom::default()),
)
})
.collect();
self
}
pub fn with_population_size(mut self, size: usize) -> Self {
self.config.telemetry.log(&format!("configured to use max population size: {}", size));
self.config.population.max_size = size;
self
}
pub fn with_selection(mut self, selection: Arc<dyn Selection + Send + Sync>) -> Self {
self.config.telemetry.log("configured to use custom selection");
self.config.selection = selection;
self
}
pub fn with_mutation(mut self, mutation: Arc<dyn Mutation + Send + Sync>) -> Self {
self.config.telemetry.log("configured to use custom mutation");
self.config.mutation = mutation;
self
}
pub fn with_termination(mut self, termination: Arc<dyn Termination>) -> Self {
self.config.telemetry.log("configured to use custom termination parameters");
self.config.termination = termination;
self
}
pub fn with_seed(mut self, seed: Option<u64>) -> Self {
if seed.is_some() {
self.config.telemetry.log("configured to use custom seed parameters");
}
self.seed = seed;
self
}
pub fn build(self) -> Result<Solver, String> {
let problem = self.config.problem.clone();
let (criterias, quota): (Vec<Box<dyn Termination>>, _) =
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>> = 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)));
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;
config.random = Arc::new(if let Some(seed) = self.seed {
config.telemetry.log(format!("configured to use seed: {}", seed).as_str());
DefaultRandom::new_with_seed(seed)
} else {
DefaultRandom::default()
});
Ok(Solver { problem, config })
}
}
fn create_time_quota(limit: usize) -> Option<Arc<dyn Quota + Sync + Send>> {
Some(Arc::new(TimeQuota::new(limit as f64)))
}