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}