darwin_rs/
individual.rs

1//! This module defines the trait and the data structure needed for specifying each individual in a population.
2//!
3//! darwin-rs: evolutionary algorithms with Rust
4//!
5//! Written by Willi Kappler, Version 0.4 (2017.06.26)
6//!
7//! Repository: https://github.com/willi-kappler/darwin-rs
8//!
9//! License: MIT
10//!
11//! This library allows you to write evolutionary algorithms (EA) in Rust.
12//! Examples provided: TSP, Sudoku, Queens Problem, OCR
13//!
14//!
15
16// external modules
17use std::cmp::Ordering;
18
19/// A wrapper helper struct for the individuals.
20/// It does the book keeping of the fitness and the number of mutations this individual
21/// has to run in one iteration.
22#[derive(Debug,Clone)]
23pub struct IndividualWrapper<T: Individual> {
24    /// The actual individual, user defined struct.
25    pub individual: T,
26    /// The current calculated fitness for this individual.
27    pub fitness: f64,
28    /// The number of mutation this individual is doing in one iteration.
29    pub num_of_mutations: u32,
30    /// The id of the population that this individual belongs to. Just for statistics.
31    pub id: u32,
32}
33
34/// Implement this for sorting
35impl<T: Individual> PartialEq for IndividualWrapper<T> {
36    fn eq(&self, other: &IndividualWrapper<T>) -> bool {
37        self.fitness == other.fitness
38    }
39}
40
41/// Implement this for sorting
42impl<T: Individual> Eq for IndividualWrapper<T> {}
43
44/// Implement this for sorting
45impl<T: Individual> Ord for IndividualWrapper<T> {
46    fn cmp(&self, other: &IndividualWrapper<T>) -> Ordering {
47        self.partial_cmp(other).expect("Fitness of Individual is NaN")
48    }
49}
50
51/// Implement this for sorting
52impl<T: Individual> PartialOrd for IndividualWrapper<T> {
53    fn partial_cmp(&self, other: &IndividualWrapper<T>) -> Option<Ordering> {
54        self.fitness.partial_cmp(&other.fitness)
55    }
56}
57
58/// This trait has to be implemented for the user defined struct.
59/// In order to share common data between all individuals use Arc. See TSP and OCR exmaples.
60///
61/// TODO: add serialization, see https://github.com/willi-kappler/darwin-rs/issues/11
62pub trait Individual {
63    /// This method mutates the individual. Usually this is a cheap and easy to implement
64    /// function. In order to improve the simulation, the user can make this function a bit
65    /// "smarter". This is nicely shown in the tsp and tsp2 example. The tsp2 example contains
66    /// two types of mutation, tsp just one:
67    ///
68    /// examples/tsp: 1. swap position
69    ///
70    /// examples/tsp2: 1. swap position, 2. rotate (shift) positions
71    ///
72    /// By just adding this one additional mutation type the simulation converges much faster
73    /// to the optimum. Of course rotation can be "simulated" by a number of swaps, but re-doing
74    /// all these steps takes time and the chances that these steps are taken in the correct
75    /// order by just randomly swaping positions are very slim. So just start with one simple
76    /// mutation function (one operation) and add more and more "smarter" mutation types to the
77    /// mutate function.
78    fn mutate(&mut self);
79    /// This method calculates the fitness for the individual. Usually this is an expensive
80    /// operation and a bit more difficult to implement, compared to the mutation method above.
81    /// The lower the fitness value, the better (healthier) the individual is and the closer
82    /// the individual is to the perfect solution. This can also correspont to the number of
83    /// errors like for example in the sudoku or queens problem case.
84    fn calculate_fitness(&mut self) -> f64;
85    /// This method resets each individual to an initial state.
86    /// For example in the "queens" case it would reset the queens position randomly
87    /// (or all in the first row).
88    fn reset(&mut self);
89    /// This method is called whenever a new fittest individual is found. It is usefull when you
90    /// want to provide some additional information or do some statistics.
91    /// It is optional and the default implementation does nothing.
92    fn new_fittest_found(&mut self) {
93
94    }
95}
96
97#[cfg(test)]
98mod test {
99    use super::{IndividualWrapper, Individual};
100
101    struct IndividualTest1;
102
103    impl Individual for IndividualTest1 {
104        fn mutate(&mut self) {
105        }
106
107        fn calculate_fitness(&mut self) -> f64 {
108            0.0
109        }
110
111        fn reset(&mut self) {
112
113        }
114    }
115
116    #[test]
117    fn compare1() {
118        let individual1 = IndividualWrapper{individual: IndividualTest1, fitness: 1.2, num_of_mutations: 21, id: 1};
119        let individual2 = IndividualWrapper{individual: IndividualTest1, fitness: 5.93, num_of_mutations: 7, id: 1};
120
121        assert!(individual2 > individual1);
122    }
123
124    #[test]
125    fn compare2() {
126        let individual1 = IndividualWrapper{individual: IndividualTest1, fitness: 3.78, num_of_mutations: 21, id: 1};
127        let individual2 = IndividualWrapper{individual: IndividualTest1, fitness: 7.12, num_of_mutations: 7, id: 1};
128
129        assert!(individual1 < individual2);
130    }
131
132    #[test]
133    fn compare3() {
134        let individual1 = IndividualWrapper{individual: IndividualTest1, fitness: 21.996, num_of_mutations: 11, id: 1};
135        let individual2 = IndividualWrapper{individual: IndividualTest1, fitness: 21.996, num_of_mutations: 34, id: 1};
136
137        assert!(individual1 == individual2);
138    }
139}