1use 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.gen_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.gen_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.gen_range(0..n);
120 let end = rng.gen_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.gen::<f64>() < self.mutation_rate {
144 let n = tour.len();
145 if n < 2 {
146 return;
147 }
148
149 let i = rng.gen_range(0..n);
150 let j = rng.gen_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.gen::<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_entropy(),
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_entropy(),
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)]
301mod tests {
302 use super::*;
303
304 fn square_instance() -> TspInstance {
305 let coords = vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)];
306 TspInstance::from_coords("square", coords).expect("should create")
307 }
308
309 fn triangle_instance() -> TspInstance {
310 let coords = vec![(0.0, 0.0), (3.0, 0.0), (3.0, 4.0)];
311 TspInstance::from_coords("triangle", coords).expect("should create")
312 }
313
314 fn pentagon_instance() -> TspInstance {
315 let angle_step = 2.0 * std::f64::consts::PI / 5.0;
317 let coords: Vec<(f64, f64)> = (0..5)
318 .map(|i| {
319 let angle = i as f64 * angle_step;
320 (angle.cos(), angle.sin())
321 })
322 .collect();
323 TspInstance::from_coords("pentagon", coords).expect("should create")
324 }
325
326 #[test]
327 fn test_ga_default_params() {
328 let ga = GaSolver::default();
329 assert_eq!(ga.population_size, 50);
330 assert!((ga.crossover_rate - 0.9).abs() < 1e-10);
331 assert!((ga.mutation_rate - 0.1).abs() < 1e-10);
332 assert_eq!(ga.tournament_size, 5);
333 assert_eq!(ga.elitism, 2);
334 }
335
336 #[test]
337 fn test_ga_builder() {
338 let ga = GaSolver::new()
339 .with_population_size(100)
340 .with_crossover_rate(0.8)
341 .with_mutation_rate(0.2)
342 .with_tournament_size(3)
343 .with_elitism(5)
344 .with_seed(42);
345
346 assert_eq!(ga.population_size, 100);
347 assert!((ga.crossover_rate - 0.8).abs() < 1e-10);
348 assert!((ga.mutation_rate - 0.2).abs() < 1e-10);
349 assert_eq!(ga.tournament_size, 3);
350 assert_eq!(ga.elitism, 5);
351 assert_eq!(ga.seed, Some(42));
352 }
353
354 #[test]
355 fn test_ga_solves_square() {
356 let instance = square_instance();
357 let mut solver = GaSolver::new().with_seed(42).with_population_size(20);
358
359 let solution = solver
360 .solve(&instance, Budget::Iterations(50))
361 .expect("should solve");
362
363 assert!(solution.length <= 5.0, "Length {} > 5.0", solution.length);
365 assert_eq!(solution.tour.len(), 4);
366 assert!(instance.validate_tour(&solution.tour).is_ok());
367 }
368
369 #[test]
370 fn test_ga_solves_triangle() {
371 let instance = triangle_instance();
372 let mut solver = GaSolver::new().with_seed(42).with_population_size(20);
373
374 let solution = solver
375 .solve(&instance, Budget::Iterations(50))
376 .expect("should solve");
377
378 assert!(solution.length <= 13.0, "Length {} > 13.0", solution.length);
380 }
381
382 #[test]
383 fn test_ga_solves_pentagon() {
384 let instance = pentagon_instance();
385 let mut solver = GaSolver::new().with_seed(42).with_population_size(30);
386
387 let solution = solver
388 .solve(&instance, Budget::Iterations(100))
389 .expect("should solve");
390
391 assert_eq!(solution.tour.len(), 5);
392 assert!(instance.validate_tour(&solution.tour).is_ok());
393 }
394
395 #[test]
396 fn test_ga_deterministic_with_seed() {
397 let instance = square_instance();
398
399 let mut solver1 = GaSolver::new().with_seed(42).with_population_size(20);
400 let mut solver2 = GaSolver::new().with_seed(42).with_population_size(20);
401
402 let solution1 = solver1
403 .solve(&instance, Budget::Iterations(20))
404 .expect("should solve");
405 let solution2 = solver2
406 .solve(&instance, Budget::Iterations(20))
407 .expect("should solve");
408
409 assert!((solution1.length - solution2.length).abs() < 1e-10);
410 }
411
412 #[test]
413 fn test_ga_tracks_history() {
414 let instance = square_instance();
415 let mut solver = GaSolver::new().with_seed(42).with_population_size(20);
416
417 let solution = solver
418 .solve(&instance, Budget::Iterations(30))
419 .expect("should solve");
420
421 assert_eq!(solution.history.len(), 30);
422 for window in solution.history.windows(2) {
424 assert!(window[1] <= window[0] + 1e-10);
425 }
426 }
427
428 #[test]
429 fn test_order_crossover() {
430 let mut rng = StdRng::seed_from_u64(42);
431
432 let parent1 = vec![0, 1, 2, 3, 4];
433 let parent2 = vec![4, 3, 2, 1, 0];
434
435 let child = GaSolver::order_crossover(&parent1, &parent2, &mut rng);
436
437 assert_eq!(child.len(), 5);
439 let mut sorted = child.clone();
440 sorted.sort();
441 assert_eq!(sorted, vec![0, 1, 2, 3, 4]);
442 }
443
444 #[test]
445 fn test_mutation() {
446 let solver = GaSolver::new().with_mutation_rate(1.0); let mut rng = StdRng::seed_from_u64(42);
448
449 let mut tour = vec![0, 1, 2, 3, 4];
450 let original = tour.clone();
451
452 solver.mutate(&mut tour, &mut rng);
453
454 let mut sorted = tour.clone();
456 sorted.sort();
457 assert_eq!(sorted, vec![0, 1, 2, 3, 4]);
458
459 assert!(tour != original || true); }
463
464 #[test]
465 fn test_ga_evolve_population() {
466 let instance = square_instance();
467 let mut solver = GaSolver::new().with_seed(42).with_population_size(20);
468
469 let population = solver.evolve(&instance, 50).expect("should evolve");
470
471 assert_eq!(population.len(), 20);
472 for window in population.windows(2) {
474 assert!(window[0].1 <= window[1].1);
475 }
476 }
477
478 #[test]
479 fn test_ga_name() {
480 let solver = GaSolver::new();
481 assert_eq!(solver.name(), "Genetic Algorithm");
482 }
483
484 #[test]
485 fn test_ga_elitism_preserves_best() {
486 let instance = square_instance();
487 let mut solver = GaSolver::new()
488 .with_seed(42)
489 .with_population_size(20)
490 .with_elitism(2);
491
492 let solution = solver
493 .solve(&instance, Budget::Iterations(50))
494 .expect("should solve");
495
496 for window in solution.history.windows(2) {
498 assert!(window[1] <= window[0] + 1e-10);
499 }
500 }
501}