revonet/ea.rs
1use rand::{Rng};
2use rand::distributions::{IndependentSample, Range};
3use serde::de::{DeserializeOwned};
4use serde::ser::Serialize;
5use std;
6
7use context::*;
8use math::*;
9use neuro::{MultilayeredNetwork};
10use problem::*;
11use result::*;
12use settings::*;
13
14
15/// Trait representing functionality required to evolve an individual for optimization
16/// and NN tuning tasks.
17///
18/// Contains functions to retrieve genes or neural network from an individual and get/set its fitness.
19#[allow(dead_code, unused_variables)]
20pub trait Individual{
21 /// Creates a new individual with empty set of genes and NAN fitness.
22 fn new() -> Self;
23 /// Initializes an individual by allocating random vector of genes using Gaussian distribution.
24 ///
25 /// # Arguments
26 /// * `size` - number of genes.
27 /// * `rng` - mutable reference to the external RNG.
28 fn init<R: Rng>(&mut self, size: usize, &mut R);
29 /// Return current fitness value.
30 fn get_fitness(&self) -> f32;
31 /// Update fitness value.
32 fn set_fitness(&mut self, fitness: f32);
33 /// Return vector of genes.
34 fn to_vec(&self) -> Option<&[f32]>;
35 /// Return mutable vector of genes.
36 fn to_vec_mut(&mut self) -> Option<&mut Vec<f32>>;
37 /// Return `MultilayeredNetwork` object with weights assigned according to the genes' values.
38 fn to_net(&mut self) -> Option<&MultilayeredNetwork> {None}
39 /// Return mutable `MultilayeredNetwork` object with weights assigned according to the genes' values.
40 fn to_net_mut(&mut self) -> Option<&mut MultilayeredNetwork> {None}
41 /// Update individual's `MultilayeredNetwork` object and update genes according to the network weights.
42 ///
43 /// # Arguments:
44 /// * `net` - neural network to update from.
45 fn set_net(&mut self, net: MultilayeredNetwork) {}
46}
47
48/// Represents real-coded individual with genes encoded as vector of real numbers.
49#[derive(Clone, Debug, Deserialize, Serialize)]
50pub struct RealCodedIndividual {
51 /// Collection of individual genes.
52 pub genes: Vec<f32>,
53 /// Fitness value associated with the individual.
54 pub fitness: f32,
55}
56
57impl RealCodedIndividual {
58}
59
60impl Individual for RealCodedIndividual{
61 fn new() -> Self {
62 RealCodedIndividual{genes: Vec::new(), fitness: std::f32::NAN}
63 }
64
65 fn init<R: Rng>(&mut self, size: usize, mut rng: &mut R) {
66 self.genes = rand_vector_std_gauss(size as usize, rng);
67 }
68
69 fn get_fitness(&self) -> f32 {
70 self.fitness
71 }
72
73 fn set_fitness(&mut self, fitness: f32) {
74 self.fitness = fitness;
75 }
76
77 fn to_vec(&self) -> Option<&[f32]> {
78 Some(&self.genes)
79 }
80
81 fn to_vec_mut(&mut self) -> Option<&mut Vec<f32>> {
82 Some(&mut self.genes)
83 }
84}
85
86//======================================================================
87
88/// Trait for an evolutionary algorithm.
89/// Defines functions which are typical for running a common EA.
90/// To implement a trait a function `breed` should be implemented.
91pub trait EA<'a, P> where P: Problem {
92 type IndType: Individual+Clone+Serialize+DeserializeOwned;
93
94 fn run_multiple(&mut self, settings: EASettings, run_num: u32) -> Result<EAResultMultiple<Self::IndType>, ()> {
95 let run_ress = (0..run_num).into_iter()
96 .map(|_| {
97 self.run(settings.clone()).expect("Error during GA run").clone()
98 })
99 .collect::<Vec<EAResult<Self::IndType>>>();
100 let res = EAResultMultiple::new(&run_ress);
101 Ok(res)
102 }
103
104 /// "Main" function for the EA which runs a cycle for an evolutionary search.
105 ///
106 /// # Arguments:
107 /// * `ctx` - `EAContext` object containing information regarding current EA run.
108 /// * `problem` - reference to the `Problem` trait which specifies an objective function.
109 /// * `gen_count` - number of generations (iterations) for search.
110 fn run_with_context(&self, ctx: &mut EAContext<Self::IndType>, problem: &P, gen_count: u32) { // -> Result<Rc<&'a EAResult<T>>, ()> {
111 // let mut ctx = self.get_context_mut();
112 // println!("run_with_context");
113 for t in 0..gen_count {
114 // evaluation
115 // println!("evaluation");
116 self.evaluate(ctx, problem);
117
118 // selection
119 // println!("selection");
120 let sel_inds = self.select(ctx);
121
122 // crossover
123 // println!("crossover");
124 let mut children: Vec<Self::IndType> = Vec::with_capacity(ctx.settings.pop_size as usize);
125 self.breed(ctx, &sel_inds, &mut children);
126
127 // next gen
128 // println!("next_generation");
129 self.next_generation(ctx, &children);
130
131 println!("> {} : {:?}", t, ctx.fitness);
132 println!(" Best fitness at generation {} : {}\n", t, min(&ctx.fitness));
133 }
134 // Ok(Rc::new(&ctx.result))
135 }
136
137 /// Function to evaluate current population for the given `problem`. In result of evaluation
138 /// fitness for every individual is updated as well as statistics regarding mean, min, max
139 /// fitness values.
140 ///
141 /// # Arguments:
142 /// * `ctx` - `EAContext` object containing information regarding current EA run.
143 /// * `problem` - reference to the `Problem` trait which specifies an objective function.
144 fn evaluate(&self, ctx: &mut EAContext<Self::IndType>, problem: &P) {
145 // ctx.fitness = evaluate(&mut ctx.population, problem, &mut ctx.result);
146 let cur_result = &mut ctx.result;
147 let popul = &mut ctx.population;
148
149 ctx.fitness = popul.iter_mut().map(|ref mut ind| {
150 let f = problem.compute(ind as &mut Self::IndType);
151 // println!(".");
152 ind.set_fitness(f);
153 f
154 }).collect::<Vec<f32>>();
155 let fits = &ctx.fitness;
156 // println!("{:?}", fits);
157 if cur_result.first_hit_fe_count == 0 {
158 for k in 0..fits.len() {
159 if problem.is_solution(fits[k]) {
160 cur_result.first_hit_fe_count = cur_result.fe_count + (k+1) as u32;
161 break;
162 }
163 }
164 }
165
166 cur_result.avg_fitness.push(mean(&fits));
167 cur_result.max_fitness.push(max(&fits));
168
169 let last_min_fitness = min(&fits);
170 cur_result.min_fitness.push(last_min_fitness);
171 if cur_result.best.get_fitness().is_nan() || (cur_result.best.get_fitness() > last_min_fitness) {
172 let idx = (&fits).iter().position(|&x| x == last_min_fitness).expect("Min fitness is not found");
173 cur_result.best = popul[idx].clone();
174 cur_result.best_fe_count = cur_result.fe_count + (idx+1) as u32;
175 }
176 cur_result.best.set_fitness(last_min_fitness);
177 cur_result.fe_count += fits.len() as u32;
178 }
179
180 /// Function to select individuals for breeding. Updates given `ctx`.
181 ///
182 /// # Arguments:
183 /// * `ctx` - `EAContext` object containing information regarding current EA run.
184 fn select(&self, ctx: &mut EAContext<Self::IndType>) -> Vec<usize> {
185 select_tournament(&ctx.fitness, ctx.settings.tour_size, &mut ctx.rng)
186 }
187
188 /// Function to prepare population for the next generation. By default copies children obtained
189 /// in result of `breed` to the `ctx.population`.
190 ///
191 /// # Arguments:
192 /// * `ctx` - `EAContext` object containing information regarding current EA run.
193 /// * `children` - reference to the vector of children individuals which should
194 /// get to the next generation.
195 fn next_generation(&self, ctx: &mut EAContext<Self::IndType>, children: &Vec<Self::IndType>) {
196 ctx.population = Vec::with_capacity(children.len());
197 for k in 0..children.len() {
198 ctx.population.push(children[k].clone());
199 }
200 // ctx.population = children.iter().map(|ref c| c.clone()).collect::<Vec<T>>();
201 ctx.population.truncate(ctx.settings.pop_size as usize);
202 }
203
204 /// Function to create children individuals using current context and selected individuals.
205 ///
206 /// # Arguments:
207 /// * `ctx` - `EAContext` object containing information regarding current EA run.
208 /// * `sel_inds` - vector of indices of individuals from `ctx.population` selected for breeding.
209 /// * `children` - reference to the container to store resulting children individuals.
210 fn breed(&self, ctx: &mut EAContext<Self::IndType>, sel_inds: &Vec<usize>, children: &mut Vec<Self::IndType>);
211
212 /// Run evolutionary algorithm and return `EAResult` object.
213 ///
214 /// # Arguments:
215 /// * `settings` - `EASettings` object.
216 // fn run(&mut self, settings: EASettings) -> Result<&EAResult<Self::IndType>, ()>;
217 fn run(&mut self, settings: EASettings) -> Result<&EAResult<Self::IndType>, ()>;
218}
219
220/// Creates population of given size. Uses `problem.get_random_individual` to generate a
221/// new individual
222///
223/// # Arguments:
224/// * `pop_size` - population size.
225/// * `ind_size` - default size (number of genes) of individuals.
226/// * `rng` - reference to pre-initialized RNG.
227/// * `problem` - reference to the object implementing `Problem` trait.
228pub fn create_population<T: Individual, P: Problem, R: Rng+Sized>(pop_size: u32, ind_size: u32, mut rng: &mut R, problem: &P) -> Vec<T> {
229 println!("Creating population of {} individuals having size {}", pop_size, ind_size);
230 let mut res = Vec::with_capacity(pop_size as usize);
231 for _ in 0..pop_size {
232 res.push(problem.get_random_individual::<T, R>(ind_size as usize, rng));
233 }
234 res
235}
236
237/// Implementation of the [tournament selection](https://en.wikipedia.org/wiki/Tournament_selection).
238///
239/// # Arguments:
240/// * `fits` - fitness values. i-th element should be equal to the fitness of the i-th individual
241/// in population.
242/// * `tour_size` - tournament size. The bigger the higher is the selective pressure (more strict
243/// selection). Minimal acceptable value is 2.
244/// * `rng` - reference to pre-initialized RNG.
245fn select_tournament(fits: &Vec<f32>, tour_size: u32, mut rng: &mut Rng) -> Vec<usize> {
246 let range = Range::new(0, fits.len());
247 let mut sel_inds: Vec<usize> = Vec::with_capacity(fits.len()); // vector of indices of selected inds. +1 in case of elite RealCodedindividual is used.
248 for _ in 0..fits.len() {
249 let tour_inds = (0..tour_size).map(|_| range.ind_sample(&mut rng)).collect::<Vec<usize>>();
250 let winner = tour_inds.iter().fold(tour_inds[0], |w_idx, &k|
251 if fits[w_idx] < fits[k] {w_idx}
252 else {k}
253 );
254 sel_inds.push(winner);
255 }
256 sel_inds
257}
258
259/// Get copy of the individual having the best fitness value.
260///
261/// # Arguments:
262/// * `popul` - vector of individuals to select from.
263pub fn get_best_individual<T: Individual+Clone>(popul: &Vec<T>) -> T {
264 let min_fitness = popul.into_iter().fold(std::f32::MAX, |s, ref ind| if s < ind.get_fitness() {s} else {ind.get_fitness()});
265 let idx = popul.into_iter().position(|ref x| x.get_fitness() == min_fitness).expect("Min fitness is not found");
266 popul[idx].clone()
267}
268
269//========================================================
270
271#[allow(unused_imports)]
272#[cfg(test)]
273mod test {
274 use rand;
275
276 use ea::*;
277 use math::*;
278
279 #[test]
280 fn test_tournament_selection() {
281 const IND_COUNT: usize = 100;
282 const TRIAL_COUNT: u32 = 100;
283
284 let mut prev_mean = 0.5f32; // avg value in a random array in [0; 1].
285 let mut rng = rand::thread_rng();
286 for t in 2..10 {
287 let mut cur_mean = 0f32;
288 // compute mean fitness for the selected population for TRIAL_COUNT trials.
289 for _ in 0..TRIAL_COUNT {
290 let fitness_vals = rand_vector(IND_COUNT, &mut rng);
291 let sel_inds = select_tournament(&fitness_vals, t, &mut rng);
292 let tmp_mean = sel_inds.iter().fold(0f32, |s, &idx| s + fitness_vals[idx]) / IND_COUNT as f32;
293 cur_mean += tmp_mean;
294 }
295 cur_mean /= TRIAL_COUNT as f32;
296 // bigger tournaments should give smaller average fitness in selected population.
297 assert!(cur_mean < prev_mean);
298 prev_mean = cur_mean;
299 }
300
301 }
302}