genetic_algorithm/strategy/
reporter.rs

1//! Generic strategy reporters:
2//! * [Duration], only reports duration, non-strategy specific
3//! * [Noop], silences reporting, non-strategy specific
4//! * [Simple], prefer to use strategy specific implementations:
5//!     * [EvolveReporterSimple](crate::strategy::evolve::EvolveReporterSimple)
6//!     * [PermutateReporterSimple](crate::strategy::permutate::PermutateReporterSimple)
7//!     * [HillClimbReporterSimple](crate::strategy::hill_climb::HillClimbReporterSimple)
8//!
9use crate::genotype::Genotype;
10use crate::strategy::{StrategyConfig, StrategyReporter, StrategyState, STRATEGY_ACTIONS};
11use std::fmt::Arguments;
12use std::io::Write;
13use std::marker::PhantomData;
14
15/// The noop reporter, silences reporting
16#[derive(Clone)]
17pub struct Noop<G: Genotype>(pub PhantomData<G>);
18impl<G: Genotype> Default for Noop<G> {
19    fn default() -> Self {
20        Self(PhantomData)
21    }
22}
23impl<G: Genotype> Noop<G> {
24    pub fn new() -> Self {
25        Self::default()
26    }
27}
28impl<G: Genotype> StrategyReporter for Noop<G> {
29    type Genotype = G;
30}
31
32/// A Duration reporter generic over Genotype.
33#[derive(Clone)]
34pub struct Duration<G: Genotype> {
35    pub buffer: Option<Vec<u8>>,
36    _phantom: PhantomData<G>,
37}
38impl<G: Genotype> Default for Duration<G> {
39    fn default() -> Self {
40        Self {
41            buffer: None,
42            _phantom: PhantomData,
43        }
44    }
45}
46impl<G: Genotype> Duration<G> {
47    pub fn new() -> Self {
48        Self::default()
49    }
50    pub fn new_with_buffer() -> Self {
51        Self {
52            buffer: Some(Vec::new()),
53            ..Default::default()
54        }
55    }
56    fn writeln(&mut self, args: Arguments<'_>) {
57        if let Some(buffer) = self.buffer.as_mut() {
58            buffer.write_fmt(args).unwrap_or(());
59            writeln!(buffer).unwrap_or(())
60        } else {
61            std::io::stdout().write_fmt(args).unwrap_or(());
62            println!()
63        }
64    }
65}
66impl<G: Genotype> StrategyReporter for Duration<G> {
67    type Genotype = G;
68
69    fn flush(&mut self, output: &mut Vec<u8>) {
70        if let Some(buffer) = self.buffer.as_mut() {
71            output.append(buffer);
72        }
73    }
74    fn on_enter<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
75        &mut self,
76        _genotype: &Self::Genotype,
77        state: &S,
78        config: &C,
79    ) {
80        self.writeln(format_args!(
81            "enter - {}, iteration: {}",
82            config.variant(),
83            state.current_iteration()
84        ));
85    }
86    fn on_exit<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
87        &mut self,
88        _genotype: &Self::Genotype,
89        state: &S,
90        config: &C,
91    ) {
92        self.writeln(format_args!(
93            "exit - {}, iteration: {}",
94            config.variant(),
95            state.current_iteration()
96        ));
97        STRATEGY_ACTIONS.iter().for_each(|action| {
98            if let Some(duration) = state.durations().get(action) {
99                self.writeln(format_args!("  {:?}: {:.3?}", action, duration));
100            }
101        });
102        self.writeln(format_args!(
103            "  Total: {:.3?} ({:.0}% fitness)",
104            &state.total_duration(),
105            state.fitness_duration_rate() * 100.0
106        ));
107    }
108}
109
110/// A Simple Strategy reporter generic over Genotype.
111/// A report is triggered every period generations
112#[derive(Clone)]
113pub struct Simple<G: Genotype> {
114    pub buffer: Option<Vec<u8>>,
115    pub period: usize,
116    pub show_genes: bool,
117    pub show_equal_fitness: bool,
118    _phantom: PhantomData<G>,
119}
120impl<G: Genotype> Default for Simple<G> {
121    fn default() -> Self {
122        Self {
123            buffer: None,
124            period: 1,
125            show_genes: false,
126            show_equal_fitness: false,
127            _phantom: PhantomData,
128        }
129    }
130}
131impl<G: Genotype> Simple<G> {
132    pub fn new(period: usize) -> Self {
133        Self {
134            period,
135            ..Default::default()
136        }
137    }
138    pub fn new_with_buffer(period: usize) -> Self {
139        Self {
140            buffer: Some(Vec::new()),
141            period,
142            ..Default::default()
143        }
144    }
145    pub fn new_with_flags(
146        period: usize,
147        buffered: bool,
148        show_genes: bool,
149        show_equal_fitness: bool,
150    ) -> Self {
151        Self {
152            period,
153            buffer: if buffered { Some(Vec::new()) } else { None },
154            show_genes,
155            show_equal_fitness,
156            ..Default::default()
157        }
158    }
159    fn writeln(&mut self, args: Arguments<'_>) {
160        if let Some(buffer) = self.buffer.as_mut() {
161            buffer.write_fmt(args).unwrap_or(());
162            writeln!(buffer).unwrap_or(())
163        } else {
164            std::io::stdout().write_fmt(args).unwrap_or(());
165            println!()
166        }
167    }
168}
169impl<G: Genotype> StrategyReporter for Simple<G> {
170    type Genotype = G;
171
172    fn flush(&mut self, output: &mut Vec<u8>) {
173        if let Some(buffer) = self.buffer.as_mut() {
174            output.append(buffer);
175        }
176    }
177    fn on_enter<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
178        &mut self,
179        genotype: &Self::Genotype,
180        state: &S,
181        config: &C,
182    ) {
183        let number_of_seed_genes = genotype.seed_genes_list().len();
184        if number_of_seed_genes > 0 {
185            self.writeln(format_args!(
186                "enter - {}, iteration: {}, number of seed genes: {}",
187                config.variant(),
188                state.current_iteration(),
189                number_of_seed_genes
190            ));
191        } else {
192            self.writeln(format_args!(
193                "enter - {}, iteration: {}",
194                config.variant(),
195                state.current_iteration()
196            ));
197        }
198    }
199    fn on_exit<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
200        &mut self,
201        _genotype: &Self::Genotype,
202        state: &S,
203        config: &C,
204    ) {
205        self.writeln(format_args!(
206            "exit - {}, iteration: {}",
207            config.variant(),
208            state.current_iteration()
209        ));
210        STRATEGY_ACTIONS.iter().for_each(|action| {
211            if let Some(duration) = state.durations().get(action) {
212                self.writeln(format_args!("  {:?}: {:.3?}", action, duration));
213            }
214        });
215        self.writeln(format_args!(
216            "  Total: {:.3?} ({:.0}% fitness)",
217            &state.total_duration(),
218            state.fitness_duration_rate() * 100.0
219        ));
220    }
221
222    fn on_new_generation<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
223        &mut self,
224        _genotype: &Self::Genotype,
225        state: &S,
226        _config: &C,
227    ) {
228        if state.current_generation() % self.period == 0 {
229            self.writeln(format_args!(
230                "periodic - current_generation: {}, stale_generations: {}, best_generation: {}, scale_index: {:?}",
231                state.current_generation(),
232                state.stale_generations(),
233                state.best_generation(),
234                state.current_scale_index(),
235            ));
236        }
237    }
238
239    fn on_new_best_chromosome<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
240        &mut self,
241        genotype: &Self::Genotype,
242        state: &S,
243        _config: &C,
244    ) {
245        self.writeln(format_args!(
246            "new best - generation: {}, fitness_score: {:?}, scale_index: {:?}, genes: {:?}",
247            state.current_generation(),
248            state.best_fitness_score(),
249            state.current_scale_index(),
250            if self.show_genes {
251                Some(genotype.best_genes())
252            } else {
253                None
254            },
255        ));
256    }
257
258    fn on_new_best_chromosome_equal_fitness<S: StrategyState<Self::Genotype>, C: StrategyConfig>(
259        &mut self,
260        genotype: &Self::Genotype,
261        state: &S,
262        _config: &C,
263    ) {
264        if self.show_equal_fitness {
265            self.writeln(format_args!(
266                "equal best - generation: {}, fitness_score: {:?}, scale_index: {:?}, genes: {:?}",
267                state.current_generation(),
268                state.best_fitness_score(),
269                state.current_scale_index(),
270                if self.show_genes {
271                    Some(genotype.best_genes())
272                } else {
273                    None
274                },
275            ));
276        }
277    }
278}