1use crate::evolution::*;
2use crate::hyper::*;
3use crate::termination::*;
4use std::hash::Hash;
5use std::sync::Arc;
6
7pub struct EvolutionConfig<C, O, S>
9where
10 C: HeuristicContext<Objective = O, Solution = S>,
11 O: HeuristicObjective<Solution = S>,
12 S: HeuristicSolution,
13{
14 pub initial: InitialConfig<C, O, S>,
16
17 pub processing: ProcessingConfig<C, O, S>,
19
20 pub context: C,
22
23 pub strategy: Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>,
25
26 pub termination: Box<dyn Termination<Context = C, Objective = O>>,
28}
29
30pub trait InitialOperator {
32 type Context: HeuristicContext<Objective = Self::Objective, Solution = Self::Solution>;
34 type Objective: HeuristicObjective<Solution = Self::Solution>;
36 type Solution: HeuristicSolution;
38
39 fn create(&self, heuristic_ctx: &Self::Context) -> Self::Solution;
41}
42
43pub type InitialOperators<C, O, S> =
45 Vec<(Box<dyn InitialOperator<Context = C, Objective = O, Solution = S> + Send + Sync>, usize)>;
46
47pub struct InitialConfig<C, O, S>
49where
50 C: HeuristicContext<Objective = O, Solution = S>,
51 O: HeuristicObjective<Solution = S>,
52 S: HeuristicSolution,
53{
54 pub operators: InitialOperators<C, O, S>,
56 pub max_size: usize,
58 pub quota: Float,
60 pub individuals: Vec<S>,
62}
63
64pub struct ProcessingConfig<C, O, S>
66where
67 C: HeuristicContext<Objective = O, Solution = S>,
68 O: HeuristicObjective<Solution = S>,
69 S: HeuristicSolution,
70{
71 pub context: Vec<Box<dyn HeuristicContextProcessing<Context = C, Objective = O, Solution = S> + Send + Sync>>,
73 pub solution: Vec<Box<dyn HeuristicSolutionProcessing<Solution = S> + Send + Sync>>,
75}
76
77pub struct EvolutionConfigBuilder<C, O, S, K>
79where
80 C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
81 O: HeuristicObjective<Solution = S> + 'static,
82 S: HeuristicSolution + 'static,
83 K: Hash + Eq + Clone + Send + Sync + 'static,
84{
85 max_generations: Option<usize>,
86 max_time: Option<usize>,
87 min_cv: Option<(String, usize, Float, bool, K)>,
88 target_proximity: Option<(Vec<Float>, Float)>,
89 heuristic: Option<Box<dyn HyperHeuristic<Context = C, Objective = O, Solution = S>>>,
90 context: Option<C>,
91 termination: Option<Box<dyn Termination<Context = C, Objective = O>>>,
92 strategy: Option<Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>>,
93
94 search_operators: Option<HeuristicSearchOperators<C, O, S>>,
95 diversify_operators: Option<HeuristicDiversifyOperators<C, O, S>>,
96
97 objective: Option<Arc<dyn HeuristicObjective<Solution = S>>>,
98
99 initial: InitialConfig<C, O, S>,
100 processing: ProcessingConfig<C, O, S>,
101}
102
103impl<C, O, S, K> Default for EvolutionConfigBuilder<C, O, S, K>
104where
105 C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
106 O: HeuristicObjective<Solution = S> + 'static,
107 S: HeuristicSolution + 'static,
108 K: Hash + Eq + Clone + Send + Sync + 'static,
109{
110 fn default() -> Self {
111 Self {
112 max_generations: None,
113 max_time: None,
114 min_cv: None,
115 target_proximity: None,
116 heuristic: None,
117 context: None,
118 termination: None,
119 strategy: None,
120 search_operators: None,
121 diversify_operators: None,
122 objective: None,
123 initial: InitialConfig { operators: vec![], max_size: 4, quota: 0.05, individuals: vec![] },
124 processing: ProcessingConfig { context: vec![], solution: vec![] },
125 }
126 }
127}
128
129impl<C, O, S, K> EvolutionConfigBuilder<C, O, S, K>
130where
131 C: HeuristicContext<Objective = O, Solution = S> + Stateful<Key = K> + 'static,
132 O: HeuristicObjective<Solution = S> + 'static,
133 S: HeuristicSolution + 'static,
134 K: Hash + Eq + Clone + Send + Sync + 'static,
135{
136 pub fn with_max_generations(mut self, limit: Option<usize>) -> Self {
138 self.max_generations = limit;
139 self
140 }
141
142 pub fn with_max_time(mut self, limit: Option<usize>) -> Self {
144 self.max_time = limit;
145 self
146 }
147
148 pub fn with_min_cv(mut self, min_cv: Option<(String, usize, Float, bool)>, key: K) -> Self {
150 self.min_cv = min_cv.map(|min_cv| (min_cv.0, min_cv.1, min_cv.2, min_cv.3, key));
151 self
152 }
153
154 pub fn with_target_proximity(mut self, target_proximity: Option<(Vec<Float>, Float)>) -> Self {
156 self.target_proximity = target_proximity;
157 self
158 }
159
160 pub fn with_initial(mut self, max_size: usize, quota: Float, operators: InitialOperators<C, O, S>) -> Self {
162 self.initial.max_size = max_size;
163 self.initial.quota = quota;
164 self.initial.operators = operators;
165
166 self
167 }
168
169 pub fn with_processing(mut self, processing: ProcessingConfig<C, O, S>) -> Self {
171 self.processing = processing;
172 self
173 }
174
175 pub fn with_init_solutions(mut self, solutions: Vec<S>, max_init_size: Option<usize>) -> Self {
177 if let Some(max_size) = max_init_size {
178 self.initial.max_size = max_size;
179 }
180 self.initial.individuals = solutions;
181
182 self
183 }
184
185 pub fn with_objective(mut self, objective: Arc<dyn HeuristicObjective<Solution = S>>) -> Self {
187 self.objective = Some(objective);
188 self
189 }
190
191 pub fn with_context(mut self, context: C) -> Self {
193 self.context = Some(context);
194 self
195 }
196
197 pub fn with_termination(mut self, termination: Box<dyn Termination<Context = C, Objective = O>>) -> Self {
199 self.termination = Some(termination);
200 self
201 }
202
203 pub fn with_heuristic(
205 mut self,
206 heuristic: Box<dyn HyperHeuristic<Context = C, Objective = O, Solution = S>>,
207 ) -> Self {
208 self.heuristic = Some(heuristic);
209 self
210 }
211
212 pub fn with_strategy(
214 mut self,
215 strategy: Box<dyn EvolutionStrategy<Context = C, Objective = O, Solution = S>>,
216 ) -> Self {
217 self.strategy = Some(strategy);
218 self
219 }
220
221 pub fn with_search_operators(mut self, search_operators: HeuristicSearchOperators<C, O, S>) -> Self {
223 self.search_operators = Some(search_operators);
224 self
225 }
226
227 pub fn with_diversify_operators(mut self, diversify_operators: HeuristicDiversifyOperators<C, O, S>) -> Self {
229 self.diversify_operators = Some(diversify_operators);
230 self
231 }
232
233 #[allow(clippy::type_complexity)]
235 fn get_termination(
236 logger: &InfoLogger,
237 max_generations: Option<usize>,
238 max_time: Option<usize>,
239 min_cv: Option<(String, usize, Float, bool, K)>,
240 target_proximity: Option<(Vec<Float>, Float)>,
241 ) -> Result<Box<dyn Termination<Context = C, Objective = O>>, GenericError> {
242 let terminations: Vec<Box<dyn Termination<Context = C, Objective = O>>> = match (
243 max_generations,
244 max_time,
245 &min_cv,
246 &target_proximity,
247 ) {
248 (None, None, None, None) => {
249 (logger)("configured to use default max-generations (3000) and max-time (300secs)");
250 vec![Box::new(MaxGeneration::new(3000)), Box::new(MaxTime::new(300.))]
251 }
252 _ => {
253 let mut terminations: Vec<Box<dyn Termination<Context = C, Objective = O>>> = vec![];
254
255 if let Some(limit) = max_generations {
256 (logger)(format!("configured to use max-generations: {limit}").as_str());
257 terminations.push(Box::new(MaxGeneration::new(limit)))
258 }
259
260 if let Some(limit) = max_time {
261 (logger)(format!("configured to use max-time: {limit}s").as_str());
262 terminations.push(Box::new(MaxTime::new(limit as Float)));
263 }
264
265 if let Some((interval_type, value, threshold, is_global, key)) = min_cv.clone() {
266 (logger)(
267 format!(
268 "configured to use variation coefficient {interval_type} with sample: {value}, threshold: {threshold}",
269 )
270 .as_str(),
271 );
272
273 let variation: Box<dyn Termination<Context = C, Objective = O>> = match interval_type.as_str() {
274 "sample" => {
275 Box::new(MinVariation::<C, O, S, K>::new_with_sample(value, threshold, is_global, key))
276 }
277 "period" => {
278 Box::new(MinVariation::<C, O, S, K>::new_with_period(value, threshold, is_global, key))
279 }
280 _ => return Err(format!("unknown variation interval type: {interval_type}").into()),
281 };
282
283 terminations.push(variation)
284 }
285
286 if let Some((target_fitness, distance_threshold)) = target_proximity.clone() {
287 (logger)(
288 format!(
289 "configured to use target fitness: {target_fitness:?}, distance threshold: {distance_threshold}",
290 )
291 .as_str(),
292 );
293 terminations.push(Box::new(TargetProximity::new(target_fitness, distance_threshold)));
294 }
295
296 terminations
297 }
298 };
299
300 Ok(Box::new(CompositeTermination::new(terminations)))
301 }
302
303 pub fn build(self) -> Result<EvolutionConfig<C, O, S>, GenericError> {
305 let context = self.context.ok_or_else(|| "missing heuristic context".to_string())?;
306 let logger = context.environment().logger.clone();
307 let termination =
308 Self::get_termination(&logger, self.max_generations, self.max_time, self.min_cv, self.target_proximity)?;
309
310 Ok(EvolutionConfig {
311 initial: self.initial,
312 strategy: if let Some(strategy) = self.strategy {
313 (logger)("configured to use a custom strategy");
314 strategy
315 } else {
316 let heuristic = if let Some(heuristic) = self.heuristic {
317 heuristic
318 } else {
319 Box::new(DynamicSelective::new(
320 self.search_operators.ok_or_else(|| "missing search operators or heuristic".to_string())?,
321 self.diversify_operators
322 .ok_or_else(|| "missing diversify operators or heuristic".to_string())?,
323 context.environment(),
324 ))
325 };
326 Box::new(strategies::Iterative::new(heuristic, 1))
327 },
328 context,
329 termination,
330 processing: self.processing,
331 })
332 }
333}