mahf/configuration.rs
1//! Metaheuristic configurations.
2
3use std::{fs::File, path::Path};
4
5use eyre::WrapErr;
6
7use crate::{
8 component::ExecResult,
9 components::{evaluation, utils::debug, Block, Branch, Component, Loop, Scope},
10 conditions::Condition,
11 identifier,
12 identifier::Identifier,
13 logging,
14 problems::{Evaluate, MultiObjectiveProblem, SingleObjectiveProblem},
15 state::{common, random::Random},
16 Problem, State,
17};
18
19/// A (meta)heuristic configuration.
20///
21/// A grouping of components is called a metaheuristic configuration, and the `Configuration` struct
22/// provides useful abstractions for dealing with them.
23///
24/// A configuration therefore fully specifies a metaheuristic algorithm,
25/// with all its components and parameters.
26///
27/// # Application
28///
29/// Applying a `Configuration` to a problem is as simple as calling its [`optimize`] method with
30/// the problem and an evaluator as argument.
31/// The method returns the [`State`] after execution, enabling inspection of any custom state,
32/// including the final population, or the best individual found.
33///
34/// It is also possible to pre-initialize the State before execution using the [`optimize_with`]
35/// method, e.g. to set the random number seed or customize what state should be logged at runtime.
36///
37/// [`optimize`]: Configuration::optimize
38/// [`optimize_with`]: Configuration::optimize
39/// [`Evaluator`]: common::Evaluator
40///
41///
42/// # Building `Configuration`s
43///
44/// Because an object-oriented approach to specifying control flow is not very intuitive,
45/// `Configuration` also exposes a simple and idiomatic way to construct metaheuristics
46/// through Rust’s builder pattern.
47///
48/// See [`Configuration::builder`] for more information.
49///
50/// # Serialization
51///
52/// For the purpose of easily identifying which experiment was done with which components
53/// and parameters, it is serializable (but not deserializable).
54#[derive(Clone)]
55pub struct Configuration<P: Problem>(Box<dyn Component<P>>);
56
57impl<P: Problem> Configuration<P> {
58 /// Constructs a `Configuration` from some heuristic components.
59 ///
60 /// Use [`Configuration::builder`] for a more convenient construction.
61 pub fn new(heuristic: Box<dyn Component<P>>) -> Self {
62 Self(heuristic)
63 }
64
65 /// Creates a builder for constructing a `Configuration`.
66 ///
67 /// The builder exposes familiar control flow methods like [`do_`], [`while_`], [`if_`],
68 /// and [`if_else_`] along with shortcut methods to construct other often-used components.
69 ///
70 /// [`do_`]: ConfigurationBuilder::do_
71 /// [`while_`]: ConfigurationBuilder::while_
72 /// [`if_`]: ConfigurationBuilder::if_
73 /// [`if_else_`]: ConfigurationBuilder::if_else_
74 ///
75 /// # Examples
76 ///
77 /// Constructing a simple genetic algorithm using the builder syntax:
78 ///
79 /// ```
80 /// use mahf::prelude::*;
81 /// # use mahf::problems::{LimitedVectorProblem, ObjectiveFunction};
82 /// # use mahf::SingleObjectiveProblem;
83 ///
84 /// # fn example<P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64> + ObjectiveFunction>() -> Configuration<P> {
85 /// # let population_size = 30;
86 /// # let n = 5_000;
87 /// # let num_selected = 30;
88 /// # let size = 5;
89 /// # let std_dev = 0.1;
90 /// # let rm = 1.0;
91 /// # let max_population_size = 30;
92 /// let ga = Configuration::builder()
93 /// .do_(initialization::RandomSpread::new(population_size))
94 /// .evaluate()
95 /// .update_best_individual()
96 /// .while_(conditions::LessThanN::iterations(n), |builder| {
97 /// builder
98 /// .do_(selection::Tournament::new(num_selected, size))
99 /// .do_(recombination::ArithmeticCrossover::new_insert_both(1.))
100 /// .do_(mutation::NormalMutation::new(std_dev, rm))
101 /// .evaluate()
102 /// .update_best_individual()
103 /// .do_(replacement::MuPlusLambda::new(max_population_size))
104 /// })
105 /// .build();
106 /// # ga
107 /// # }
108
109 /// ```
110 pub fn builder() -> ConfigurationBuilder<P> {
111 ConfigurationBuilder::new()
112 }
113
114 /// Returns a reference to the root [`Component`].
115 pub fn heuristic(&self) -> &dyn Component<P> {
116 self.0.as_ref()
117 }
118
119 /// Consumes the `Configuration`, returning the root [`Component`].
120 pub fn into_inner(self) -> Box<dyn Component<P>> {
121 self.0
122 }
123
124 /// Creates a builder pre-initialized with the root [`Component`].
125 pub fn into_builder(self) -> ConfigurationBuilder<P> {
126 Self::builder().do_(self.0)
127 }
128
129 /// Serializes the `Configuration` into the file at `path` using [`ron`].
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// # use mahf::{ExecResult, Problem};
135 /// use mahf::Configuration;
136 ///
137 /// # fn example<P: Problem>(problem: P) -> ExecResult<Configuration<P>> {
138 /// let config = Configuration::builder()/* ... */.build();
139 /// config.to_ron("path/to/ron")?;
140 /// # Ok(config)
141 /// # }
142 /// ```
143 pub fn to_ron(&self, path: impl AsRef<Path>) -> ExecResult<()> {
144 ron::ser::to_writer_pretty(
145 std::io::BufWriter::new(
146 File::create(path).wrap_err("failed to create configuration file")?,
147 ),
148 self.heuristic(),
149 ron::ser::PrettyConfig::default().struct_names(true),
150 )
151 .wrap_err("failed to serialize configuration")
152 }
153
154 /// Runs the `Configuration` on the `problem` using a given [`State`].
155 ///
156 /// Note that the caller is responsible for initializing `state` properly.
157 pub fn run(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> {
158 self.0.init(problem, state)?;
159 self.0.require(problem, &state.requirements())?;
160 self.0.execute(problem, state)?;
161 Ok(())
162 }
163
164 /// Runs the heuristic `Configuration` on the given `problem`, returning the final [`State`]
165 /// after execution of the heuristic.
166 ///
167 /// # Initialization
168 ///
169 /// The state is pre-initialized with [`Populations`] and [`Log`].
170 ///
171 /// The random generator defaults to a randomly seeded RNG ([`Random::default`]).
172 ///
173 /// The `evaluator` is inserted wrapped inside an [`Evaluator`] with the [`Global`] identifier.
174 ///
175 /// For initializing the state with custom state, e.g. a fixed random seed,
176 /// see [`optimize_with`].
177 ///
178 /// [`Populations`]: common::Populations
179 /// [`Log`]: logging::Log
180 /// [`Evaluator`]: common::Evaluator
181 /// [`Global`]: identifier::Global
182 /// [`optimize_with`]: Self::optimize_with
183 ///
184 ///
185 /// # Examples
186 ///
187 /// Optimizing some `problem` with a sequential evaluator:
188 ///
189 /// ```
190 /// # use mahf::problems::ObjectiveFunction;
191 /// use mahf::{problems::Sequential, Configuration};
192 ///
193 /// # fn example<P: ObjectiveFunction>(problem: P) {
194 /// let config = Configuration::builder()
195 /// /* configuration definition */
196 /// .build();
197 /// let state = config.optimize(&problem, Sequential::new());
198 /// # }
199 /// ```
200 pub fn optimize<'a, T>(&self, problem: &P, evaluator: T) -> ExecResult<State<'a, P>>
201 where
202 T: Evaluate<Problem = P> + 'a,
203 {
204 let mut state = State::new();
205
206 state.insert(logging::Log::new());
207 state.insert(Random::default());
208 state.insert(common::Populations::<P>::new());
209 state.insert(common::Evaluator::<P, identifier::Global>::new(evaluator));
210
211 self.run(problem, &mut state)?;
212
213 Ok(state)
214 }
215
216 /// Runs the heuristic `Configuration` on the given `problem`, initializing the [`State`]
217 /// beforehand with a custom function, returning the final [`State`] after execution
218 /// of the heuristic.
219 ///
220 /// # Initialization
221 ///
222 /// The state is pre-initialized with [`Populations`] and [`Log`].
223 ///
224 /// If no random generator is inserted in `init_state`, it will default
225 /// to a randomly seeded RNG ([Random::default]).
226 ///
227 /// Note that the evaluator has to be inserted **manually** into the [`State`], using e.g. `{`[`State::insert_evaluator`], [`State::insert_evaluator_as`]`}`.
228 ///
229 /// [`Populations`]: common::Populations
230 /// [`Log`]: logging::Log
231 /// [`optimize_with`]: Self::optimize_with
232 ///
233 /// # Examples
234 ///
235 /// Optimizing some `problem` with a sequential evaluator and a random seed of `42`:
236 ///
237 /// ```
238 /// # use mahf::problems::ObjectiveFunction;
239 /// use mahf::{identifier::Global, problems::Sequential, Configuration, Random};
240 ///
241 /// # fn example<P: ObjectiveFunction>(problem: P) {
242 /// let config = Configuration::builder()/* ... */.build();
243 /// let state = config.optimize_with(&problem, |state| {
244 /// state.insert_evaluator(Sequential::new());
245 /// state.insert(Random::new(42));
246 /// Ok(())
247 /// });
248 /// # }
249 /// ```
250 pub fn optimize_with<'a>(
251 &self,
252 problem: &P,
253 init_state: impl FnOnce(&mut State<'a, P>) -> ExecResult<()>,
254 ) -> ExecResult<State<'a, P>> {
255 let mut state = State::new();
256
257 state.insert(logging::Log::new());
258 state.insert(common::Populations::<P>::new());
259
260 init_state(&mut state)?;
261
262 if !state.contains::<Random>() {
263 state.insert(Random::default());
264 }
265
266 self.run(problem, &mut state)?;
267
268 Ok(state)
269 }
270}
271
272impl<P: Problem> From<Box<dyn Component<P>>> for Configuration<P> {
273 fn from(heuristic: Box<dyn Component<P>>) -> Self {
274 Self::new(heuristic)
275 }
276}
277
278/// A simple DSL for building a (meta)heuristic [`Configuration`].
279///
280/// Its recommended usage is through the [`Configuration::builder`] method.
281pub struct ConfigurationBuilder<P: Problem> {
282 components: Vec<Box<dyn Component<P>>>,
283}
284
285impl<P: Problem> ConfigurationBuilder<P> {
286 /// Constructs a new builder.
287 fn new() -> Self {
288 Self {
289 components: Vec::new(),
290 }
291 }
292
293 /// Adds the `component` to the current [`Block`].
294 ///
295 /// # Examples
296 ///
297 /// ```no_run
298 /// # use mahf::Problem;
299 /// # fn component1<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
300 /// # fn component2<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
301 /// # fn component3<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
302 /// use mahf::Configuration;
303 ///
304 /// # pub fn example<P: Problem>() -> Configuration<P> {
305 /// Configuration::builder()
306 /// .do_(component1())
307 /// .do_(component2())
308 /// .do_(component3())
309 /// .build()
310 /// # }
311 /// ```
312 pub fn do_(mut self, component: Box<dyn Component<P>>) -> Self {
313 self.components.push(component);
314 self
315 }
316
317 /// Adds the `component` to the current [`Block`], or does nothing if it is `None`.
318 ///
319 /// # Examples
320 ///
321 /// ```no_run
322 /// # use mahf::Problem;
323 /// # fn component<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
324 /// use mahf::Configuration;
325 ///
326 /// # pub fn example<P: Problem>() -> Configuration<P> {
327 /// Configuration::builder()
328 /// .do_if_some_(Some(component()))
329 /// .do_if_some_(None)
330 /// .build()
331 /// # }
332 /// ```
333 pub fn do_if_some_(self, component: Option<Box<dyn Component<P>>>) -> Self {
334 if let Some(component) = component {
335 self.do_(component)
336 } else {
337 self
338 }
339 }
340
341 /// Adds all `components` to the current [`Block`].
342 ///
343 /// This is equivalent to calling [`do_`] repeatedly, but offers better visual grouping
344 /// for multiple components which serve the same purpose, e.g. calculating metrics.
345 ///
346 /// [`do_`]: Self::do_
347 ///
348 /// # Examples
349 ///
350 /// ```no_run
351 /// # use mahf::Problem;
352 /// # fn metric1<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
353 /// # fn metric2<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
354 /// # fn metric3<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
355 /// use mahf::Configuration;
356 ///
357 /// # pub fn example<P: Problem>() -> Configuration<P> {
358 /// Configuration::builder()
359 /// .do_many_([metric1(), metric2(), metric3()])
360 /// .build()
361 /// # }
362 /// ```
363 pub fn do_many_(mut self, components: impl IntoIterator<Item = Box<dyn Component<P>>>) -> Self {
364 for component in components {
365 self.components.push(component);
366 }
367 self
368 }
369
370 /// Loops the `body` while the `condition` is `true`.
371 ///
372 /// Internally, the [`Loop`] component is created with the given `condition` and `body`.
373 ///
374 /// # Examples
375 ///
376 /// ```no_run
377 /// # use mahf::Problem;
378 /// # fn condition<P: Problem>() -> Box<dyn mahf::Condition<P>> { unimplemented!() }
379 /// # fn component1<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
380 /// # fn component2<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
381 /// # fn component3<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
382 /// use mahf::Configuration;
383 ///
384 /// # pub fn example<P: Problem>() -> Configuration<P> {
385 /// Configuration::builder()
386 /// .while_(condition(), |builder| {
387 /// builder
388 /// .do_(component1())
389 /// .do_(component2())
390 /// .do_(component3())
391 /// })
392 /// .build()
393 /// # }
394 /// ```
395 pub fn while_(
396 self,
397 condition: Box<dyn Condition<P>>,
398 body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
399 ) -> Self {
400 let components = body(ConfigurationBuilder::new()).components;
401 self.do_(Loop::new(condition, components))
402 }
403
404 /// Executes the `body` if the `condition` is `true`.
405 ///
406 /// Internally, the [`Branch`] component is created with the given `condition` and `body`.
407 ///
408 /// # Examples
409 ///
410 /// ```no_run
411 /// # use mahf::Problem;
412 /// # fn condition<P: Problem>() -> Box<dyn mahf::Condition<P>> { unimplemented!() }
413 /// # fn component1<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
414 /// # fn component2<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
415 /// # fn component3<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
416 /// use mahf::Configuration;
417 ///
418 /// # pub fn example<P: Problem>() -> Configuration<P> {
419 /// Configuration::builder()
420 /// .if_(condition(), |builder| {
421 /// builder
422 /// .do_(component1())
423 /// .do_(component2())
424 /// .do_(component3())
425 /// })
426 /// .build()
427 /// # }
428 /// ```
429 pub fn if_(
430 self,
431 condition: Box<dyn Condition<P>>,
432 body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
433 ) -> Self {
434 let components = body(ConfigurationBuilder::new()).components;
435 self.do_(Branch::new(condition, components))
436 }
437
438 /// Executes the `if_body` or `else_body` depending on the `condition`.
439 ///
440 /// Internally, the [`Branch`] component is created with the given `condition`, `if_body`, and `else_body`.
441 ///
442 /// # Examples
443 ///
444 /// ```no_run
445 /// # use mahf::Problem;
446 /// # fn condition<P: Problem>() -> Box<dyn mahf::Condition<P>> { unimplemented!() }
447 /// # fn component1<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
448 /// # fn component2<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
449 /// # fn component3<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
450 /// # fn component4<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
451 /// use mahf::Configuration;
452 ///
453 /// # pub fn example<P: Problem>() -> Configuration<P> {
454 /// Configuration::builder()
455 /// .if_else_(condition(),
456 /// |if_builder| {
457 /// if_builder
458 /// .do_(component1())
459 /// .do_(component2())
460 /// },
461 /// |else_builder| {
462 /// else_builder
463 /// .do_(component3())
464 /// .do_(component4())
465 /// })
466 /// .build()
467 /// # }
468 /// ```
469 pub fn if_else_(
470 self,
471 condition: Box<dyn Condition<P>>,
472 if_body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
473 else_body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
474 ) -> Self {
475 let if_components = if_body(ConfigurationBuilder::new()).components;
476 let else_components = else_body(ConfigurationBuilder::new()).components;
477 self.do_(Branch::new_with_else(
478 condition,
479 if_components,
480 else_components,
481 ))
482 }
483
484 /// Executes the `body` in a new scope, where shadowing custom state is possible.
485 ///
486 /// This is especially useful for nested heuristics with an inner loop.
487 ///
488 /// Internally, the [`Scope`] component is created with the given `body`.
489 ///
490 /// ```no_run
491 /// # use mahf::Problem;
492 /// # fn heuristic<P: Problem>() -> Box<dyn mahf::Component<P>> { unimplemented!() }
493 /// use mahf::Configuration;
494 ///
495 /// # pub fn example<P: Problem>() -> Configuration<P> {
496 /// Configuration::builder()
497 /// .scope_(|builder| builder.do_(heuristic()))
498 /// .build()
499 /// # }
500 /// ```
501 pub fn scope_(
502 self,
503 body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
504 ) -> Self {
505 let components = body(ConfigurationBuilder::new()).components;
506 self.do_(Scope::new(components))
507 }
508
509 /// Consumes the builder, creating a [`Configuration`].
510 pub fn build(self) -> Configuration<P> {
511 Configuration::new(Block::new(self.components))
512 }
513
514 /// Consumes the builder, creating a [`Component`].
515 ///
516 /// This method is usually only used for heuristic templates.
517 pub fn build_component(self) -> Box<dyn Component<P>> {
518 Block::new(self.components)
519 }
520}
521
522impl<P: Problem> ConfigurationBuilder<P> {
523 /// Asserts some condition on the [`State`].
524 ///
525 /// Internally, the [`Debug`] component is created, passing an `assert!` closure.
526 /// See its documentation for limitations.
527 ///
528 /// Note that the execution panics when the assertion is violated.
529 ///
530 /// [`Debug`]: debug::Debug
531 ///
532 /// # Examples
533 ///
534 /// Asserting that the size of the current population equals `1`:
535 ///
536 /// ```no_run
537 /// # use mahf::Problem;
538 /// use mahf::Configuration;
539 ///
540 /// # pub fn example<P: Problem>() -> Configuration<P> {
541 /// Configuration::builder()
542 /// .assert(|state| state.populations().current().len() == 1)
543 /// .build()
544 /// # }
545 /// ```
546 #[track_caller]
547 pub fn assert(
548 self,
549 assert: impl Fn(&State<P>) -> bool + Send + Sync + Clone + 'static,
550 ) -> Self {
551 self.debug(move |_problem, state| assert!(assert(state)))
552 }
553
554 /// Enables arbitrary `behaviour` for debugging purposes.
555 ///
556 /// Internally, the [`Debug`] component is created with the given `behaviour`.
557 /// See its documentation for limitations.
558 ///
559 /// # Examples
560 ///
561 /// Printing the current population.
562 /// Note that most useful behaviour imposes some restriction on the [`Problem`]
563 /// which are usually not required, e.g. [`Problem::Encoding`]`: Debug` in this case:
564 ///
565 /// ```no_run
566 /// # use std::fmt::Debug;
567 /// # use mahf::Problem;
568 /// use mahf::Configuration;
569 ///
570 /// # pub fn example<P>() -> Configuration<P> where P: Problem, P::Encoding: Debug {
571 /// Configuration::builder()
572 /// .debug(|_problem, state| {
573 /// println!("Current Population: {:?}", state.populations().current())
574 /// })
575 /// .build()
576 /// # }
577 /// ```
578 pub fn debug(
579 self,
580 behaviour: impl Fn(&P, &mut State<P>) + Send + Sync + Clone + 'static,
581 ) -> Self {
582 self.do_(debug::Debug::new(behaviour))
583 }
584
585 /// Evaluates all [`Individual`]s in the [current population] using the [`Evaluator`] with [`Global`] identifier.
586 ///
587 /// Internally, the [`PopulationEvaluator`] component is created.
588 ///
589 /// [`Individual`]: crate::Individual
590 /// [current population]: common::Populations::current
591 /// [`Evaluator`]: common::Evaluator
592 /// [`Global`]: identifier::Global
593 /// [`PopulationEvaluator`]: evaluation::PopulationEvaluator
594 ///
595 /// # Examples
596 ///
597 /// ```no_run
598 /// # use mahf::Problem;
599 /// use mahf::Configuration;
600 ///
601 /// pub fn example<P: Problem>() -> Configuration<P> {
602 /// Configuration::builder()
603 /// .evaluate()
604 /// .build()
605 /// # }
606 /// ```
607 pub fn evaluate(self) -> Self {
608 self.do_(evaluation::PopulationEvaluator::new())
609 }
610
611 /// Evaluates all [`Individual`]s in the [current population] using the [`Evaluator`] with identifier `I`.
612 ///
613 /// Internally, the [`PopulationEvaluator`] component is created with the given identifier.
614 ///
615 /// The default identifier is [`Global`].
616 ///
617 /// [`Individual`]: crate::Individual
618 /// [current population]: common::Populations::current
619 /// [`Evaluator`]: common::Evaluator
620 /// [`PopulationEvaluator`]: evaluation::PopulationEvaluator
621 /// [`Global`]: identifier::Global
622 ///
623 /// # Examples
624 ///
625 /// Calling `evaluate` with the `Global` identifier:
626 ///
627 /// ```no_run
628 /// # use mahf::Problem;
629 /// use mahf::Configuration;
630 /// use mahf::identifier::Global;
631 ///
632 /// pub fn example<P: Problem>() -> Configuration<P> {
633 /// Configuration::builder()
634 /// .evaluate_with::<Global>()
635 /// .build()
636 /// # }
637 /// ```
638 pub fn evaluate_with<I>(self) -> Self
639 where
640 I: Identifier,
641 {
642 self.do_(evaluation::PopulationEvaluator::<I>::new_with())
643 }
644}
645
646impl<P: SingleObjectiveProblem> ConfigurationBuilder<P> {
647 /// Updates the [`BestIndividual`] yet found.
648 ///
649 /// Internally, the [`BestIndividualUpdate`] component is created.
650 ///
651 /// [`BestIndividual`]: common::BestIndividual
652 /// [`BestIndividualUpdate`]: evaluation::BestIndividualUpdate
653 ///
654 /// # Examples
655 ///
656 /// You also usually want to evaluate the individuals beforehand:
657 ///
658 /// ```no_run
659 /// # use mahf::SingleObjectiveProblem;
660 /// use mahf::Configuration;
661 ///
662 /// # pub fn example<P: SingleObjectiveProblem>() -> Configuration<P> {
663 /// Configuration::builder()
664 /// .evaluate()
665 /// .update_best_individual()
666 /// .build()
667 /// # }
668 /// ```
669 pub fn update_best_individual(self) -> Self {
670 self.do_(evaluation::BestIndividualUpdate::new())
671 }
672}
673
674impl<P: MultiObjectiveProblem> ConfigurationBuilder<P> {
675 /// Updates the current approximation of the [`ParetoFront`].
676 ///
677 /// Internally, the [`ParetoFrontUpdate`] component is created.
678 ///
679 /// [`ParetoFront`]: common::ParetoFront
680 /// [`ParetoFrontUpdate`]: evaluation::ParetoFrontUpdate
681 ///
682 /// # Examples
683 ///
684 /// You also usually want to evaluate the individuals beforehand:
685 ///
686 /// ```no_run
687 /// # use mahf::MultiObjectiveProblem;
688 /// use mahf::Configuration;
689 ///
690 /// # pub fn example<P: MultiObjectiveProblem>() -> Configuration<P> {
691 /// Configuration::builder()
692 /// .evaluate()
693 /// .update_pareto_front()
694 /// .build()
695 /// # }
696 /// ```
697 pub fn update_pareto_front(self) -> Self {
698 self.do_(evaluation::ParetoFrontUpdate::new())
699 }
700}