use crate::evolution::*;
use crate::hyper::*;
use crate::termination::*;
use std::hash::Hash;
use std::sync::Arc;
pub struct EvolutionConfig<C, O, S>
where
C: HeuristicContext<Objective = O, Solution = S>,
O: HeuristicObjective<Solution = S>,
S: HeuristicSolution,
{
pub initial: InitialConfig<C, O, S>,
pub processing: ProcessingConfig<C, O, S>,
pub context: C,
pub strategy: Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>,
pub termination: Box<dyn Termination<Context = C, Objective = O>>,
}
pub trait InitialOperator {
type Context: HeuristicContext<Objective = Self::Objective, Solution = Self::Solution>;
type Objective: HeuristicObjective<Solution = Self::Solution>;
type Solution: HeuristicSolution;
fn create(&self, heuristic_ctx: &Self::Context) -> Self::Solution;
}
pub type InitialOperators<C, O, S> =
Vec<(Box<dyn InitialOperator<Context = C, Objective = O, Solution = S> + Send + Sync>, usize)>;
pub struct InitialConfig<C, O, S>
where
C: HeuristicContext<Objective = O, Solution = S>,
O: HeuristicObjective<Solution = S>,
S: HeuristicSolution,
{
pub operators: InitialOperators<C, O, S>,
pub max_size: usize,
pub quota: f64,
pub individuals: Vec<S>,
}
pub struct ProcessingConfig<C, O, S>
where
C: HeuristicContext<Objective = O, Solution = S>,
O: HeuristicObjective<Solution = S>,
S: HeuristicSolution,
{
pub context: Vec<Box<dyn HeuristicContextProcessing<Context = C, Objective = O, Solution = S> + Send + Sync>>,
pub solution: Vec<Box<dyn HeuristicSolutionProcessing<Solution = S> + Send + Sync>>,
}
pub struct EvolutionConfigBuilder<C, O, S, K>
where
C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
O: HeuristicObjective<Solution = S> + 'static,
S: HeuristicSolution + 'static,
K: Hash + Eq + Clone + Send + Sync + 'static,
{
max_generations: Option<usize>,
max_time: Option<usize>,
min_cv: Option<(String, usize, f64, bool, K)>,
target_proximity: Option<(Vec<f64>, f64)>,
heuristic: Option<Box<dyn HyperHeuristic<Context = C, Objective = O, Solution = S>>>,
context: Option<C>,
termination: Option<Box<dyn Termination<Context = C, Objective = O>>>,
strategy: Option<Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>>,
search_operators: Option<HeuristicSearchOperators<C, O, S>>,
diversify_operators: Option<HeuristicDiversifyOperators<C, O, S>>,
objective: Option<Arc<dyn HeuristicObjective<Solution = S>>>,
initial: InitialConfig<C, O, S>,
processing: ProcessingConfig<C, O, S>,
}
impl<C, O, S, K> Default for EvolutionConfigBuilder<C, O, S, K>
where
C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
O: HeuristicObjective<Solution = S> + 'static,
S: HeuristicSolution + 'static,
K: Hash + Eq + Clone + Send + Sync + 'static,
{
fn default() -> Self {
Self {
max_generations: None,
max_time: None,
min_cv: None,
target_proximity: None,
heuristic: None,
context: None,
termination: None,
strategy: None,
search_operators: None,
diversify_operators: None,
objective: None,
initial: InitialConfig { operators: vec![], max_size: 4, quota: 0.05, individuals: vec![] },
processing: ProcessingConfig { context: vec![], solution: vec![] },
}
}
}
impl<C, O, S, K> EvolutionConfigBuilder<C, O, S, K>
where
C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
O: HeuristicObjective<Solution = S> + 'static,
S: HeuristicSolution + 'static,
K: Hash + Eq + Clone + Send + Sync + 'static,
{
pub fn with_max_generations(mut self, limit: Option<usize>) -> Self {
self.max_generations = limit;
self
}
pub fn with_max_time(mut self, limit: Option<usize>) -> Self {
self.max_time = limit;
self
}
pub fn with_min_cv(mut self, min_cv: Option<(String, usize, f64, bool)>, key: K) -> Self {
self.min_cv = min_cv.map(|min_cv| (min_cv.0, min_cv.1, min_cv.2, min_cv.3, key));
self
}
pub fn with_target_proximity(mut self, target_proximity: Option<(Vec<f64>, f64)>) -> Self {
self.target_proximity = target_proximity;
self
}
pub fn with_initial(mut self, max_size: usize, quota: f64, operators: InitialOperators<C, O, S>) -> Self {
self.initial.max_size = max_size;
self.initial.quota = quota;
self.initial.operators = operators;
self
}
pub fn with_processing(mut self, processing: ProcessingConfig<C, O, S>) -> Self {
self.processing = processing;
self
}
pub fn with_init_solutions(mut self, solutions: Vec<S>, max_init_size: Option<usize>) -> Self {
if let Some(max_size) = max_init_size {
self.initial.max_size = max_size;
}
self.initial.individuals = solutions;
self
}
pub fn with_objective(mut self, objective: Arc<dyn HeuristicObjective<Solution = S>>) -> Self {
self.objective = Some(objective);
self
}
pub fn with_context(mut self, context: C) -> Self {
self.context = Some(context);
self
}
pub fn with_termination(mut self, termination: Box<dyn Termination<Context = C, Objective = O>>) -> Self {
self.termination = Some(termination);
self
}
pub fn with_heuristic(
mut self,
heuristic: Box<dyn HyperHeuristic<Context = C, Objective = O, Solution = S>>,
) -> Self {
self.heuristic = Some(heuristic);
self
}
pub fn with_strategy(
mut self,
strategy: Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>,
) -> Self {
self.strategy = Some(strategy);
self
}
pub fn with_search_operators(mut self, search_operators: HeuristicSearchOperators<C, O, S>) -> Self {
self.search_operators = Some(search_operators);
self
}
pub fn with_diversify_operators(mut self, diversify_operators: HeuristicDiversifyOperators<C, O, S>) -> Self {
self.diversify_operators = Some(diversify_operators);
self
}
#[allow(clippy::type_complexity)]
fn get_termination(
logger: &InfoLogger,
max_generations: Option<usize>,
max_time: Option<usize>,
min_cv: Option<(String, usize, f64, bool, K)>,
target_proximity: Option<(Vec<f64>, f64)>,
) -> Result<Box<dyn Termination<Context = C, Objective = O> + Send + Sync>, GenericError> {
let terminations: Vec<Box<dyn Termination<Context = C, Objective = O> + Send + Sync>> = match (
max_generations,
max_time,
&min_cv,
&target_proximity,
) {
(None, None, None, None) => {
(logger)("configured to use default max-generations (3000) and max-time (300secs)");
vec![Box::new(MaxGeneration::new(3000)), Box::new(MaxTime::new(300.))]
}
_ => {
let mut terminations: Vec<Box<dyn Termination<Context = C, Objective = O> + Send + Sync>> = vec![];
if let Some(limit) = max_generations {
(logger)(format!("configured to use max-generations: {limit}").as_str());
terminations.push(Box::new(MaxGeneration::new(limit)))
}
if let Some(limit) = max_time {
(logger)(format!("configured to use max-time: {limit}s").as_str());
terminations.push(Box::new(MaxTime::new(limit as f64)));
}
if let Some((interval_type, value, threshold, is_global, key)) = min_cv.clone() {
(logger)(
format!(
"configured to use variation coefficient {interval_type} with sample: {value}, threshold: {threshold}",
)
.as_str(),
);
let variation: Box<dyn Termination<Context = C, Objective = O> + Send + Sync> =
match interval_type.as_str() {
"sample" => {
Box::new(MinVariation::<C, O, S, K>::new_with_sample(value, threshold, is_global, key))
}
"period" => {
Box::new(MinVariation::<C, O, S, K>::new_with_period(value, threshold, is_global, key))
}
_ => return Err(format!("unknown variation interval type: {interval_type}").into()),
};
terminations.push(variation)
}
if let Some((target_fitness, distance_threshold)) = target_proximity.clone() {
(logger)(
format!(
"configured to use target fitness: {target_fitness:?}, distance threshold: {distance_threshold}",
)
.as_str(),
);
terminations.push(Box::new(TargetProximity::new(target_fitness, distance_threshold)));
}
terminations
}
};
Ok(Box::new(CompositeTermination::new(terminations)))
}
pub fn build(self) -> Result<EvolutionConfig<C, O, S>, GenericError> {
let context = self.context.ok_or_else(|| "missing heuristic context".to_string())?;
let logger = context.environment().logger.clone();
let termination =
Self::get_termination(&logger, self.max_generations, self.max_time, self.min_cv, self.target_proximity)?;
Ok(EvolutionConfig {
initial: self.initial,
strategy: if let Some(strategy) = self.strategy {
(logger)("configured to use a custom strategy");
strategy
} else {
let heuristic = if let Some(heuristic) = self.heuristic {
(logger)("configured to use custom heuristic");
heuristic
} else {
Box::new(DynamicSelective::new(
self.search_operators.ok_or_else(|| "missing search operators or heuristic".to_string())?,
self.diversify_operators
.ok_or_else(|| "missing diversify operators or heuristic".to_string())?,
context.environment(),
))
};
Box::new(strategies::Iterative::new(heuristic, 1))
},
context,
termination,
processing: self.processing,
})
}
}