-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod.rs
141 lines (133 loc) · 4.59 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Metaheuristic algorithm components.
//!
//! This module contains definition and implementation of [`Component`]s.
#![allow(clippy::new_ret_no_self)]
use crate::{
component::{AnyComponent, ExecResult},
state::StateReq,
Problem, State,
};
pub mod archive;
pub mod boundary;
pub mod control_flow;
pub mod evaluation;
pub mod generative;
pub mod initialization;
pub mod mapping;
pub mod measures;
pub mod misc;
pub mod mutation;
pub mod recombination;
pub mod replacement;
pub mod selection;
pub mod swarm;
pub mod utils;
pub use control_flow::{Block, Branch, Loop, Scope};
/// Trait to represent a *component*, a (small) functionality with a uniform interface.
///
/// `Component`s encapsulate any functionality that may arise in the context of metaheuristics,
/// which includes evolutionary operators, any metaheuristic-specific operator,
/// logging during the execution, calculation of metrics, and even control flow itself.
///
/// # Phases
///
/// `Component`s define three phases:
/// - `initialize` (optional): Used to initialize custom state, and is only called once*.
/// - `require` (optional): Used to check if the custom state was properly initialized in the
/// `initialize` phase, and is only called once*.
/// Specifically used to check if necessary custom state initialized by other components is present.
/// - `execute`: Executes the functionality the component constitutes.
/// This phase is usually called in a loop during the optimization process.
///
/// *More complex control flow might reinitialize components during execution, which means that
/// `initialize` and `require` get called multiple times.
/// See their documentation for more details.
///
/// # State
///
/// The interface of `Component`s can be this generic because the [`State`] erases the
/// types of the state contained within.
/// It therefore can be interpreted as an interface checked at runtime, as it
/// internally relies on boxed [`CustomState`] objects.
///
/// `Component`s are immutable and fully described by their parameters.
/// Therefore, all mutable state has to be stored in the [`State`].
///
/// [`CustomState`]: crate::CustomState
///
/// # Panic versus error
///
/// All methods on this trait return an [`ExecResult`], and therefore may fail.
/// In general, they should fail when the interface of the component was violated
/// (by other components).
///
/// It is advised to add an error description before propagating the error using the
/// [`wrap_err`] method or similar.
///
/// If a `Component` panics, it is assumed to be because of an internal implementation error,
/// not because its interface was violated.
///
/// [`wrap_err`]: eyre::WrapErr::wrap_err
///
/// # Construction
///
/// Contrary to [Rust convention], the `new` method of component structs should return a
/// `Box<dyn Component<P>>`, not `Self`, as components are usually used only in their boxed form.
/// `Component`s typically define a `from_params` method that is used to construct `Self` directly, e.g. for testing.
///
/// [Rust convention]: https://rust-unofficial.github.io/patterns/idioms/ctor.html
///
/// # Examples
///
/// A simple component that prints the current population:
///
/// ```
/// use std::fmt::Debug;
///
/// use mahf::{Component, ExecResult, Problem, State};
/// use serde::Serialize;
///
/// #[derive(Clone, Serialize)]
/// pub struct PrintPopulation;
///
/// impl PrintPopulation {
/// pub fn from_params() -> Self {
/// Self
/// }
///
/// pub fn new<P>() -> Box<dyn Component<P>>
/// where
/// P: Problem,
/// P::Encoding: Debug,
/// {
/// Box::new(Self::from_params())
/// }
/// }
///
/// impl<P> Component<P> for PrintPopulation
/// where
/// P: Problem,
/// P::Encoding: Debug,
/// {
/// fn execute(&self, _problem: &P, state: &mut State<P>) -> ExecResult<()> {
/// println!("Current population: {:?}", state.populations().current());
/// Ok(())
/// }
/// }
/// ```
pub trait Component<P: Problem>: AnyComponent {
/// Can be used to initialize custom state required by the component.
#[allow(unused_variables)]
fn init(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> {
Ok(())
}
/// Can be used to specify custom state requirements.
#[allow(unused_variables)]
fn require(&self, problem: &P, state_req: &StateReq<P>) -> ExecResult<()> {
Ok(())
}
/// Executes the component, performing the actual logic.
fn execute(&self, problem: &P, state: &mut State<P>) -> ExecResult<()>;
}
erased_serde::serialize_trait_object!(<P: Problem> Component<P>);
dyn_clone::clone_trait_object!(<P: Problem> Component<P>);