1use rand::{self, Rng};
6
7#[derive(PartialEq)]
8pub enum Goal {
9 Maximize,
10 Minimize,
11}
12
13#[derive(Debug, Clone)]
15pub struct Individual {
16 pub values: Vec<f32>,
19 pub fitness: f32,
21}
22
23#[derive(Debug)]
25pub struct Solution {
26 pub champion: Individual,
29 pub history: Option<Vec<(f32, f32)>>,
32}
33
34#[derive(Debug)]
36pub struct Config {
37 pub population_size: u32,
39 pub generation_count: u32,
41 pub crossover_rate: f32,
44 pub mutation_rate: f32,
46 pub record_history: bool,
49}
50
51impl Default for Config {
52 fn default() -> Self {
53 Config {
54 population_size: 200,
55 generation_count: 1_000,
56 crossover_rate: 0.70,
57 mutation_rate: 0.10,
58 record_history: false,
59 }
60 }
61}
62
63pub fn solve<F: Fn(&[f32]) -> f32>(
92 f: F,
93 sliders: &[(f32, f32)],
94 goal: Goal,
95 cfg: Config,
96) -> Solution {
97 assert!(
98 cfg.population_size > 2,
99 "Population size should be at least two"
100 );
101 assert!(
102 cfg.population_size % 2 == 0,
103 "We only support even-numbered population size at the moment"
104 );
105 assert!(cfg.generation_count > 1, "Need at least one generation");
106
107 let evaluate_fitness = |idv: &mut Individual| -> () {
108 idv.fitness = f(&idv.values);
109 };
110
111 let compete = |xs: Vec<Individual>| -> Individual {
114 assert!(xs.len() >= 2);
115 match goal {
116 Goal::Maximize => xs
117 .iter()
118 .max_by(|x, y| x.fitness.partial_cmp(&y.fitness).unwrap())
119 .unwrap()
120 .clone(),
121 Goal::Minimize => xs
122 .iter()
123 .min_by(|x, y| x.fitness.partial_cmp(&y.fitness).unwrap())
124 .unwrap()
125 .clone(),
126 }
127 };
128
129 let mut rng = rand::thread_rng();
130
131 let mut parents: Vec<Individual> = Vec::with_capacity(cfg.population_size as usize);
133 for _ in 0..cfg.population_size {
134 let mut xs = Vec::with_capacity(sliders.len());
135 for j in 0..sliders.len() {
136 xs.push(rng.gen_range(sliders[j].0..=sliders[j].1));
137 }
138 let mut i = Individual {
139 values: xs,
140 fitness: 0.0,
141 };
142 evaluate_fitness(&mut i);
143 parents.push(i);
144 }
145
146 let mut history: Vec<(f32, f32)> = if cfg.record_history {
147 Vec::with_capacity(cfg.generation_count as usize)
148 } else {
149 Vec::with_capacity(0)
150 };
151
152 for generation in 0..cfg.generation_count as usize {
153 let mut children: Vec<Individual> = Vec::with_capacity(cfg.population_size as usize);
155 for _ in 0..cfg.population_size {
156 let i = rng.gen_range(0..cfg.population_size as usize);
157 let j = rng.gen_range(0..cfg.population_size as usize);
158 let lhs = &parents[i];
159 let rhs = &parents[j];
160 let winner = compete(vec![lhs.clone(), rhs.clone()]);
161 children.push(winner);
162 }
163 let champion = compete(parents);
164
165 if cfg.record_history {
166 history.push((generation as f32, champion.fitness))
167 }
168
169 for i in 0..(cfg.population_size as usize / 2) {
171 if rng.gen::<f32>() < cfg.crossover_rate {
173 let mut mom = children[i * 2].clone();
174 let mut dad = children[i * 2 + 1].clone();
175
176 let point = rng.gen_range(0..sliders.len());
177 for j in 0..sliders.len() {
178 mom.values[j] = if j < point {
179 mom.values[j]
180 } else {
181 dad.values[j]
182 };
183 dad.values[j] = if j < point {
184 dad.values[j]
185 } else {
186 mom.values[j]
187 };
188 }
189
190 children[i * 2] = mom;
191 children[i * 2 + 1] = dad;
192 }
193 }
194
195 for i in 0..cfg.population_size as usize {
197 let x = &mut children[i];
198 for j in 0..sliders.len() {
199 if rng.gen::<f32>() < cfg.mutation_rate {
200 x.values[j] =
201 (x.values[j] + rng.gen_range(-0.5..=0.5)).clamp(sliders[j].0, sliders[j].1);
202 }
203 }
204 evaluate_fitness(x);
205 }
206 parents = children;
207 }
208 let champion = compete(parents);
209 Solution {
210 champion,
211 history: if cfg.record_history {
212 Some(history)
213 } else {
214 None
215 },
216 }
217}