aprender_tsp/solver/
ga.rs1use crate::error::TspResult;
6use crate::instance::TspInstance;
7use crate::solver::{Budget, TspSolution, TspSolver};
8use rand::prelude::*;
9
10#[derive(Debug, Clone)]
12pub struct GaSolver {
13 pub population_size: usize,
15 pub crossover_rate: f64,
17 pub mutation_rate: f64,
19 pub tournament_size: usize,
21 pub elitism: usize,
23 seed: Option<u64>,
25}
26
27impl Default for GaSolver {
28 fn default() -> Self {
29 Self {
30 population_size: 50,
31 crossover_rate: 0.9,
32 mutation_rate: 0.1,
33 tournament_size: 5,
34 elitism: 2,
35 seed: None,
36 }
37 }
38}
39
40impl GaSolver {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn with_population_size(mut self, size: usize) -> Self {
48 self.population_size = size;
49 self
50 }
51
52 pub fn with_crossover_rate(mut self, rate: f64) -> Self {
54 self.crossover_rate = rate;
55 self
56 }
57
58 pub fn with_mutation_rate(mut self, rate: f64) -> Self {
60 self.mutation_rate = rate;
61 self
62 }
63
64 pub fn with_tournament_size(mut self, size: usize) -> Self {
66 self.tournament_size = size;
67 self
68 }
69
70 pub fn with_elitism(mut self, count: usize) -> Self {
72 self.elitism = count;
73 self
74 }
75
76 pub fn with_seed(mut self, seed: u64) -> Self {
78 self.seed = Some(seed);
79 self
80 }
81
82 fn random_tour(n: usize, rng: &mut StdRng) -> Vec<usize> {
84 let mut tour: Vec<usize> = (0..n).collect();
85 tour.shuffle(rng);
86 tour
87 }
88
89 fn tournament_select<'a>(
91 &self,
92 population: &'a [(Vec<usize>, f64)],
93 rng: &mut StdRng,
94 ) -> &'a Vec<usize> {
95 let mut best_idx = rng.random_range(0..population.len());
96 let mut best_fitness = population[best_idx].1;
97
98 for _ in 1..self.tournament_size {
99 let idx = rng.random_range(0..population.len());
100 if population[idx].1 < best_fitness {
101 best_fitness = population[idx].1;
102 best_idx = idx;
103 }
104 }
105
106 &population[best_idx].0
107 }
108
109 fn order_crossover(parent1: &[usize], parent2: &[usize], rng: &mut StdRng) -> Vec<usize> {
111 let n = parent1.len();
112 if n < 2 {
113 return parent1.to_vec();
114 }
115
116 let mut child = vec![usize::MAX; n];
117
118 let start = rng.random_range(0..n);
120 let end = rng.random_range(start..n);
121
122 child[start..=end].copy_from_slice(&parent1[start..=end]);
124
125 let mut pos = (end + 1) % n;
127 let mut p2_pos = (end + 1) % n;
128
129 while child.contains(&usize::MAX) {
130 let city = parent2[p2_pos];
131 if !child.contains(&city) {
132 child[pos] = city;
133 pos = (pos + 1) % n;
134 }
135 p2_pos = (p2_pos + 1) % n;
136 }
137
138 child
139 }
140
141 fn mutate(&self, tour: &mut [usize], rng: &mut StdRng) {
143 if rng.random::<f64>() < self.mutation_rate {
144 let n = tour.len();
145 if n < 2 {
146 return;
147 }
148
149 let i = rng.random_range(0..n);
150 let j = rng.random_range(0..n);
151
152 if i != j {
153 let (i, j) = if i < j { (i, j) } else { (j, i) };
154 tour[i..=j].reverse();
155 }
156 }
157 }
158
159 fn evolve_generation(
161 &self,
162 population: &mut Vec<(Vec<usize>, f64)>,
163 instance: &TspInstance,
164 rng: &mut StdRng,
165 ) -> usize {
166 let mut evaluations = 0;
167
168 population.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
170
171 let mut new_population = Vec::with_capacity(self.population_size);
172
173 for individual in population.iter().take(self.elitism) {
175 new_population.push(individual.clone());
176 }
177
178 while new_population.len() < self.population_size {
180 let parent1 = self.tournament_select(population, rng);
181 let parent2 = self.tournament_select(population, rng);
182
183 let mut child = if rng.random::<f64>() < self.crossover_rate {
184 Self::order_crossover(parent1, parent2, rng)
185 } else {
186 parent1.clone()
187 };
188
189 self.mutate(&mut child, rng);
190
191 let fitness = instance.tour_length(&child);
192 evaluations += 1;
193
194 new_population.push((child, fitness));
195 }
196
197 *population = new_population;
198 evaluations
199 }
200
201 pub fn evolve(
203 &mut self,
204 instance: &TspInstance,
205 generations: usize,
206 ) -> TspResult<Vec<(Vec<usize>, f64)>> {
207 let n = instance.num_cities();
208
209 let mut rng = match self.seed {
210 Some(s) => StdRng::seed_from_u64(s),
211 None => StdRng::from_os_rng(),
212 };
213
214 let mut population: Vec<(Vec<usize>, f64)> = (0..self.population_size)
216 .map(|_| {
217 let tour = Self::random_tour(n, &mut rng);
218 let fitness = instance.tour_length(&tour);
219 (tour, fitness)
220 })
221 .collect();
222
223 for _ in 0..generations {
225 self.evolve_generation(&mut population, instance, &mut rng);
226 }
227
228 population.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
230
231 Ok(population)
232 }
233}
234
235impl TspSolver for GaSolver {
236 fn solve(&mut self, instance: &TspInstance, budget: Budget) -> TspResult<TspSolution> {
237 let n = instance.num_cities();
238 let max_generations = budget.limit();
239
240 let mut rng = match self.seed {
241 Some(s) => StdRng::seed_from_u64(s),
242 None => StdRng::from_os_rng(),
243 };
244
245 let mut population: Vec<(Vec<usize>, f64)> = (0..self.population_size)
247 .map(|_| {
248 let tour = Self::random_tour(n, &mut rng);
249 let fitness = instance.tour_length(&tour);
250 (tour, fitness)
251 })
252 .collect();
253
254 let mut best_tour = population[0].0.clone();
255 let mut best_length = population[0].1;
256 let mut history = Vec::with_capacity(max_generations);
257 let mut evaluations = self.population_size;
258
259 for (tour, fitness) in &population {
261 if *fitness < best_length {
262 best_length = *fitness;
263 best_tour.clone_from(tour);
264 }
265 }
266 history.push(best_length);
267
268 for _ in 1..max_generations {
270 evaluations += self.evolve_generation(&mut population, instance, &mut rng);
271
272 for (tour, fitness) in &population {
274 if *fitness < best_length {
275 best_length = *fitness;
276 best_tour.clone_from(tour);
277 }
278 }
279
280 history.push(best_length);
281 }
282
283 Ok(TspSolution {
284 tour: best_tour,
285 length: best_length,
286 evaluations,
287 history,
288 })
289 }
290
291 fn name(&self) -> &'static str {
292 "Genetic Algorithm"
293 }
294
295 fn reset(&mut self) {
296 }
298}
299
300#[cfg(test)]
301#[path = "ga_tests.rs"]
302mod tests;