1use std::f64::consts::PI;
6
7use crate::fitness::traits::Fitness;
8use crate::genome::bit_string::BitString;
9use crate::genome::real_vector::RealVector;
10use crate::genome::traits::{BinaryGenome, RealValuedGenome};
11
12pub trait BenchmarkFunction: Send + Sync {
14 fn name(&self) -> &'static str;
16
17 fn dimension(&self) -> usize;
19
20 fn bounds(&self) -> (f64, f64);
22
23 fn optimal_fitness(&self) -> f64;
25
26 fn optimal_solution(&self) -> Option<Vec<f64>>;
28
29 fn evaluate_raw(&self, x: &[f64]) -> f64;
31}
32
33#[derive(Clone, Debug)]
37pub struct Sphere {
38 dimension: usize,
39}
40
41impl Sphere {
42 pub fn new(dimension: usize) -> Self {
44 Self { dimension }
45 }
46}
47
48impl BenchmarkFunction for Sphere {
49 fn name(&self) -> &'static str {
50 "Sphere"
51 }
52
53 fn dimension(&self) -> usize {
54 self.dimension
55 }
56
57 fn bounds(&self) -> (f64, f64) {
58 (-5.12, 5.12)
59 }
60
61 fn optimal_fitness(&self) -> f64 {
62 0.0
63 }
64
65 fn optimal_solution(&self) -> Option<Vec<f64>> {
66 Some(vec![0.0; self.dimension])
67 }
68
69 fn evaluate_raw(&self, x: &[f64]) -> f64 {
70 x.iter().map(|xi| xi * xi).sum()
71 }
72}
73
74impl Fitness for Sphere {
75 type Genome = RealVector;
76 type Value = f64;
77
78 fn evaluate(&self, genome: &Self::Genome) -> f64 {
79 -self.evaluate_raw(genome.genes())
81 }
82}
83
84#[derive(Clone, Debug)]
88pub struct Rastrigin {
89 dimension: usize,
90}
91
92impl Rastrigin {
93 pub fn new(dimension: usize) -> Self {
95 Self { dimension }
96 }
97}
98
99impl BenchmarkFunction for Rastrigin {
100 fn name(&self) -> &'static str {
101 "Rastrigin"
102 }
103
104 fn dimension(&self) -> usize {
105 self.dimension
106 }
107
108 fn bounds(&self) -> (f64, f64) {
109 (-5.12, 5.12)
110 }
111
112 fn optimal_fitness(&self) -> f64 {
113 0.0
114 }
115
116 fn optimal_solution(&self) -> Option<Vec<f64>> {
117 Some(vec![0.0; self.dimension])
118 }
119
120 fn evaluate_raw(&self, x: &[f64]) -> f64 {
121 let a = 10.0;
122 let n = x.len() as f64;
123 a * n
124 + x.iter()
125 .map(|xi| xi * xi - a * (2.0 * PI * xi).cos())
126 .sum::<f64>()
127 }
128}
129
130impl Fitness for Rastrigin {
131 type Genome = RealVector;
132 type Value = f64;
133
134 fn evaluate(&self, genome: &Self::Genome) -> f64 {
135 -self.evaluate_raw(genome.genes())
136 }
137}
138
139#[derive(Clone, Debug)]
143pub struct Rosenbrock {
144 dimension: usize,
145}
146
147impl Rosenbrock {
148 pub fn new(dimension: usize) -> Self {
150 assert!(dimension >= 2, "Rosenbrock requires at least 2 dimensions");
151 Self { dimension }
152 }
153}
154
155impl BenchmarkFunction for Rosenbrock {
156 fn name(&self) -> &'static str {
157 "Rosenbrock"
158 }
159
160 fn dimension(&self) -> usize {
161 self.dimension
162 }
163
164 fn bounds(&self) -> (f64, f64) {
165 (-5.0, 10.0)
166 }
167
168 fn optimal_fitness(&self) -> f64 {
169 0.0
170 }
171
172 fn optimal_solution(&self) -> Option<Vec<f64>> {
173 Some(vec![1.0; self.dimension])
174 }
175
176 fn evaluate_raw(&self, x: &[f64]) -> f64 {
177 x.windows(2)
178 .map(|w| {
179 let xi = w[0];
180 let xi1 = w[1];
181 100.0 * (xi1 - xi * xi).powi(2) + (1.0 - xi).powi(2)
182 })
183 .sum()
184 }
185}
186
187impl Fitness for Rosenbrock {
188 type Genome = RealVector;
189 type Value = f64;
190
191 fn evaluate(&self, genome: &Self::Genome) -> f64 {
192 -self.evaluate_raw(genome.genes())
193 }
194}
195
196#[derive(Clone, Debug)]
200pub struct Ackley {
201 dimension: usize,
202 a: f64,
203 b: f64,
204 c: f64,
205}
206
207impl Ackley {
208 pub fn new(dimension: usize) -> Self {
210 Self {
211 dimension,
212 a: 20.0,
213 b: 0.2,
214 c: 2.0 * PI,
215 }
216 }
217
218 pub fn with_params(dimension: usize, a: f64, b: f64, c: f64) -> Self {
220 Self { dimension, a, b, c }
221 }
222}
223
224impl BenchmarkFunction for Ackley {
225 fn name(&self) -> &'static str {
226 "Ackley"
227 }
228
229 fn dimension(&self) -> usize {
230 self.dimension
231 }
232
233 fn bounds(&self) -> (f64, f64) {
234 (-32.768, 32.768)
235 }
236
237 fn optimal_fitness(&self) -> f64 {
238 0.0
239 }
240
241 fn optimal_solution(&self) -> Option<Vec<f64>> {
242 Some(vec![0.0; self.dimension])
243 }
244
245 fn evaluate_raw(&self, x: &[f64]) -> f64 {
246 let n = x.len() as f64;
247 let sum_sq = x.iter().map(|xi| xi * xi).sum::<f64>();
248 let sum_cos = x.iter().map(|xi| (self.c * xi).cos()).sum::<f64>();
249
250 -self.a * (-self.b * (sum_sq / n).sqrt()).exp() - (sum_cos / n).exp()
251 + self.a
252 + std::f64::consts::E
253 }
254}
255
256impl Fitness for Ackley {
257 type Genome = RealVector;
258 type Value = f64;
259
260 fn evaluate(&self, genome: &Self::Genome) -> f64 {
261 -self.evaluate_raw(genome.genes())
262 }
263}
264
265#[derive(Clone, Debug)]
269pub struct Griewank {
270 dimension: usize,
271}
272
273impl Griewank {
274 pub fn new(dimension: usize) -> Self {
276 Self { dimension }
277 }
278}
279
280impl BenchmarkFunction for Griewank {
281 fn name(&self) -> &'static str {
282 "Griewank"
283 }
284
285 fn dimension(&self) -> usize {
286 self.dimension
287 }
288
289 fn bounds(&self) -> (f64, f64) {
290 (-600.0, 600.0)
291 }
292
293 fn optimal_fitness(&self) -> f64 {
294 0.0
295 }
296
297 fn optimal_solution(&self) -> Option<Vec<f64>> {
298 Some(vec![0.0; self.dimension])
299 }
300
301 fn evaluate_raw(&self, x: &[f64]) -> f64 {
302 let sum_sq: f64 = x.iter().map(|xi| xi * xi).sum::<f64>() / 4000.0;
303 let prod_cos: f64 = x
304 .iter()
305 .enumerate()
306 .map(|(i, xi)| (xi / ((i + 1) as f64).sqrt()).cos())
307 .product();
308 sum_sq - prod_cos + 1.0
309 }
310}
311
312impl Fitness for Griewank {
313 type Genome = RealVector;
314 type Value = f64;
315
316 fn evaluate(&self, genome: &Self::Genome) -> f64 {
317 -self.evaluate_raw(genome.genes())
318 }
319}
320
321#[derive(Clone, Debug)]
325pub struct Schwefel {
326 dimension: usize,
327}
328
329impl Schwefel {
330 pub fn new(dimension: usize) -> Self {
332 Self { dimension }
333 }
334}
335
336impl BenchmarkFunction for Schwefel {
337 fn name(&self) -> &'static str {
338 "Schwefel"
339 }
340
341 fn dimension(&self) -> usize {
342 self.dimension
343 }
344
345 fn bounds(&self) -> (f64, f64) {
346 (-500.0, 500.0)
347 }
348
349 fn optimal_fitness(&self) -> f64 {
350 0.0
351 }
352
353 fn optimal_solution(&self) -> Option<Vec<f64>> {
354 Some(vec![420.9687; self.dimension])
355 }
356
357 fn evaluate_raw(&self, x: &[f64]) -> f64 {
358 let n = x.len() as f64;
359 418.9829 * n - x.iter().map(|xi| xi * xi.abs().sqrt().sin()).sum::<f64>()
360 }
361}
362
363impl Fitness for Schwefel {
364 type Genome = RealVector;
365 type Value = f64;
366
367 fn evaluate(&self, genome: &Self::Genome) -> f64 {
368 -self.evaluate_raw(genome.genes())
369 }
370}
371
372#[derive(Clone, Debug)]
376pub struct OneMax {
377 length: usize,
378}
379
380impl OneMax {
381 pub fn new(length: usize) -> Self {
383 Self { length }
384 }
385
386 pub fn length(&self) -> usize {
388 self.length
389 }
390}
391
392impl Fitness for OneMax {
393 type Genome = BitString;
394 type Value = usize;
395
396 fn evaluate(&self, genome: &Self::Genome) -> usize {
397 genome.count_ones()
398 }
399}
400
401#[derive(Clone, Debug)]
405pub struct LeadingOnes {
406 #[allow(dead_code)]
407 length: usize,
408}
409
410impl LeadingOnes {
411 pub fn new(length: usize) -> Self {
413 Self { length }
414 }
415}
416
417impl Fitness for LeadingOnes {
418 type Genome = BitString;
419 type Value = usize;
420
421 fn evaluate(&self, genome: &Self::Genome) -> usize {
422 genome.bits().iter().take_while(|&&b| b).count()
423 }
424}
425
426#[derive(Clone, Debug)]
435pub struct Zdt1 {
436 dimension: usize,
437}
438
439impl Zdt1 {
440 pub fn new(dimension: usize) -> Self {
442 assert!(dimension >= 2, "ZDT1 requires at least 2 dimensions");
443 Self { dimension }
444 }
445
446 pub fn evaluate(&self, x: &[f64]) -> [f64; 2] {
448 let n = x.len() as f64;
449 let f1 = x[0];
450 let g = 1.0 + 9.0 * x[1..].iter().sum::<f64>() / (n - 1.0);
451 let f2 = g * (1.0 - (f1 / g).sqrt());
452 [f1, f2]
453 }
454
455 pub fn bounds(&self) -> (f64, f64) {
457 (0.0, 1.0)
458 }
459
460 pub fn dimension(&self) -> usize {
462 self.dimension
463 }
464}
465
466#[derive(Clone, Debug)]
470pub struct Zdt2 {
471 dimension: usize,
472}
473
474impl Zdt2 {
475 pub fn new(dimension: usize) -> Self {
477 assert!(dimension >= 2, "ZDT2 requires at least 2 dimensions");
478 Self { dimension }
479 }
480
481 pub fn evaluate(&self, x: &[f64]) -> [f64; 2] {
483 let n = x.len() as f64;
484 let f1 = x[0];
485 let g = 1.0 + 9.0 * x[1..].iter().sum::<f64>() / (n - 1.0);
486 let f2 = g * (1.0 - (f1 / g).powi(2));
487 [f1, f2]
488 }
489
490 pub fn bounds(&self) -> (f64, f64) {
492 (0.0, 1.0)
493 }
494
495 pub fn dimension(&self) -> usize {
497 self.dimension
498 }
499}
500
501#[derive(Clone, Debug)]
505pub struct Zdt3 {
506 dimension: usize,
507}
508
509impl Zdt3 {
510 pub fn new(dimension: usize) -> Self {
512 assert!(dimension >= 2, "ZDT3 requires at least 2 dimensions");
513 Self { dimension }
514 }
515
516 pub fn evaluate(&self, x: &[f64]) -> [f64; 2] {
518 let n = x.len() as f64;
519 let f1 = x[0];
520 let g = 1.0 + 9.0 * x[1..].iter().sum::<f64>() / (n - 1.0);
521 let h = 1.0 - (f1 / g).sqrt() - (f1 / g) * (10.0 * PI * f1).sin();
522 let f2 = g * h;
523 [f1, f2]
524 }
525
526 pub fn bounds(&self) -> (f64, f64) {
528 (0.0, 1.0)
529 }
530
531 pub fn dimension(&self) -> usize {
533 self.dimension
534 }
535}
536
537#[derive(Clone, Debug)]
541pub struct SchafferN1;
542
543impl SchafferN1 {
544 pub fn new() -> Self {
546 Self
547 }
548
549 pub fn evaluate(&self, x: f64) -> [f64; 2] {
551 let f1 = x * x;
552 let f2 = (x - 2.0) * (x - 2.0);
553 [f1, f2]
554 }
555
556 pub fn bounds(&self) -> (f64, f64) {
558 (-10.0, 10.0)
559 }
560}
561
562impl Default for SchafferN1 {
563 fn default() -> Self {
564 Self::new()
565 }
566}
567
568#[derive(Clone, Debug)]
576pub struct Levy {
577 dimension: usize,
578}
579
580impl Levy {
581 pub fn new(dimension: usize) -> Self {
583 Self { dimension }
584 }
585}
586
587impl BenchmarkFunction for Levy {
588 fn name(&self) -> &'static str {
589 "Levy"
590 }
591
592 fn dimension(&self) -> usize {
593 self.dimension
594 }
595
596 fn bounds(&self) -> (f64, f64) {
597 (-10.0, 10.0)
598 }
599
600 fn optimal_fitness(&self) -> f64 {
601 0.0
602 }
603
604 fn optimal_solution(&self) -> Option<Vec<f64>> {
605 Some(vec![1.0; self.dimension])
606 }
607
608 fn evaluate_raw(&self, x: &[f64]) -> f64 {
609 let w: Vec<f64> = x.iter().map(|xi| 1.0 + (xi - 1.0) / 4.0).collect();
610 let n = w.len();
611
612 let term1 = (PI * w[0]).sin().powi(2);
613
614 let sum: f64 = w[..n - 1]
615 .iter()
616 .map(|wi| (wi - 1.0).powi(2) * (1.0 + 10.0 * (PI * wi + 1.0).sin().powi(2)))
617 .sum();
618
619 let term3 = (w[n - 1] - 1.0).powi(2) * (1.0 + (2.0 * PI * w[n - 1]).sin().powi(2));
620
621 term1 + sum + term3
622 }
623}
624
625impl Fitness for Levy {
626 type Genome = RealVector;
627 type Value = f64;
628
629 fn evaluate(&self, genome: &Self::Genome) -> f64 {
630 -self.evaluate_raw(genome.genes())
631 }
632}
633
634#[derive(Clone, Debug)]
638pub struct DixonPrice {
639 dimension: usize,
640}
641
642impl DixonPrice {
643 pub fn new(dimension: usize) -> Self {
645 Self { dimension }
646 }
647}
648
649impl BenchmarkFunction for DixonPrice {
650 fn name(&self) -> &'static str {
651 "Dixon-Price"
652 }
653
654 fn dimension(&self) -> usize {
655 self.dimension
656 }
657
658 fn bounds(&self) -> (f64, f64) {
659 (-10.0, 10.0)
660 }
661
662 fn optimal_fitness(&self) -> f64 {
663 0.0
664 }
665
666 fn optimal_solution(&self) -> Option<Vec<f64>> {
667 let optimal: Vec<f64> = (0..self.dimension)
669 .map(|i| {
670 let exp_num = (1u64 << i) as f64 - 2.0;
671 let exp_den = (1u64 << (i + 1)) as f64;
672 2.0_f64.powf(-exp_num / exp_den)
673 })
674 .collect();
675 Some(optimal)
676 }
677
678 fn evaluate_raw(&self, x: &[f64]) -> f64 {
679 let term1 = (x[0] - 1.0).powi(2);
680
681 let sum: f64 = x
682 .windows(2)
683 .enumerate()
684 .map(|(i, w)| (i + 2) as f64 * (2.0 * w[1] * w[1] - w[0]).powi(2))
685 .sum();
686
687 term1 + sum
688 }
689}
690
691impl Fitness for DixonPrice {
692 type Genome = RealVector;
693 type Value = f64;
694
695 fn evaluate(&self, genome: &Self::Genome) -> f64 {
696 -self.evaluate_raw(genome.genes())
697 }
698}
699
700#[derive(Clone, Debug)]
704pub struct StyblinskiTang {
705 dimension: usize,
706}
707
708impl StyblinskiTang {
709 pub fn new(dimension: usize) -> Self {
711 Self { dimension }
712 }
713}
714
715impl BenchmarkFunction for StyblinskiTang {
716 fn name(&self) -> &'static str {
717 "Styblinski-Tang"
718 }
719
720 fn dimension(&self) -> usize {
721 self.dimension
722 }
723
724 fn bounds(&self) -> (f64, f64) {
725 (-5.0, 5.0)
726 }
727
728 fn optimal_fitness(&self) -> f64 {
729 -39.16617 * self.dimension as f64
731 }
732
733 fn optimal_solution(&self) -> Option<Vec<f64>> {
734 Some(vec![-2.903534; self.dimension])
735 }
736
737 fn evaluate_raw(&self, x: &[f64]) -> f64 {
738 x.iter()
739 .map(|xi| xi.powi(4) - 16.0 * xi.powi(2) + 5.0 * xi)
740 .sum::<f64>()
741 / 2.0
742 }
743}
744
745impl Fitness for StyblinskiTang {
746 type Genome = RealVector;
747 type Value = f64;
748
749 fn evaluate(&self, genome: &Self::Genome) -> f64 {
750 -self.evaluate_raw(genome.genes())
751 }
752}
753
754#[derive(Clone, Debug)]
768pub struct RoyalRoad {
769 pub schema_size: usize,
771 pub num_schemas: usize,
773}
774
775impl RoyalRoad {
776 pub fn new(schema_size: usize, num_schemas: usize) -> Self {
782 Self {
783 schema_size,
784 num_schemas,
785 }
786 }
787
788 pub fn standard() -> Self {
790 Self::new(8, 8)
791 }
792
793 pub fn genome_length(&self) -> usize {
795 self.schema_size * self.num_schemas
796 }
797
798 fn is_complete_schema(&self, bits: &[bool], schema_index: usize) -> bool {
800 let start = schema_index * self.schema_size;
801 let end = start + self.schema_size;
802
803 if end > bits.len() {
804 return false;
805 }
806
807 bits[start..end].iter().all(|&b| b)
808 }
809
810 pub fn count_complete_schemas(&self, bits: &[bool]) -> usize {
812 (0..self.num_schemas)
813 .filter(|&i| self.is_complete_schema(bits, i))
814 .count()
815 }
816}
817
818impl Fitness for RoyalRoad {
819 type Genome = BitString;
820 type Value = usize;
821
822 fn evaluate(&self, genome: &Self::Genome) -> usize {
823 self.count_complete_schemas(genome.bits())
824 }
825}
826
827#[derive(Clone, Debug)]
839pub struct NkLandscape {
840 n: usize,
842 k: usize,
844 neighbors: Vec<Vec<usize>>,
846 contributions: Vec<std::collections::HashMap<Vec<bool>, f64>>,
849}
850
851impl NkLandscape {
852 pub fn new(n: usize, k: usize, seed: u64) -> Self {
859 assert!(k < n, "K must be less than N");
860
861 use rand::SeedableRng;
862 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
863
864 let mut neighbors = Vec::with_capacity(n);
866 for i in 0..n {
867 let mut gene_neighbors: Vec<usize> = (0..n).filter(|&j| j != i).collect();
868 use rand::seq::SliceRandom;
870 gene_neighbors.shuffle(&mut rng);
871 gene_neighbors.truncate(k);
872 gene_neighbors.sort();
873 neighbors.push(gene_neighbors);
874 }
875
876 let mut contributions = Vec::with_capacity(n);
878 for _i in 0..n {
879 let num_configs = 1 << (k + 1); let mut table = std::collections::HashMap::with_capacity(num_configs);
881
882 for config_bits in 0..num_configs {
884 let config: Vec<bool> = (0..=k).map(|j| (config_bits >> j) & 1 == 1).collect();
885 use rand::Rng;
886 table.insert(config, rng.gen::<f64>());
887 }
888
889 contributions.push(table);
890 }
891
892 Self {
893 n,
894 k,
895 neighbors,
896 contributions,
897 }
898 }
899
900 pub fn with_adjacent_neighbors(n: usize, k: usize, seed: u64) -> Self {
903 assert!(k < n, "K must be less than N");
904
905 use rand::SeedableRng;
906 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
907
908 let mut neighbors = Vec::with_capacity(n);
910 for i in 0..n {
911 let gene_neighbors: Vec<usize> = (1..=k).map(|offset| (i + offset) % n).collect();
912 neighbors.push(gene_neighbors);
913 }
914
915 let mut contributions = Vec::with_capacity(n);
917 for _i in 0..n {
918 let num_configs = 1 << (k + 1);
919 let mut table = std::collections::HashMap::with_capacity(num_configs);
920
921 for config_bits in 0..num_configs {
922 let config: Vec<bool> = (0..=k).map(|j| (config_bits >> j) & 1 == 1).collect();
923 use rand::Rng;
924 table.insert(config, rng.gen::<f64>());
925 }
926
927 contributions.push(table);
928 }
929
930 Self {
931 n,
932 k,
933 neighbors,
934 contributions,
935 }
936 }
937
938 pub fn genome_length(&self) -> usize {
940 self.n
941 }
942
943 pub fn epistasis(&self) -> usize {
945 self.k
946 }
947
948 pub fn evaluate_bits(&self, bits: &[bool]) -> f64 {
950 assert!(bits.len() >= self.n);
951
952 let mut total = 0.0;
953
954 for i in 0..self.n {
955 let mut config = Vec::with_capacity(self.k + 1);
957 config.push(bits[i]);
958 for &j in &self.neighbors[i] {
959 config.push(bits[j]);
960 }
961
962 if let Some(&contribution) = self.contributions[i].get(&config) {
964 total += contribution;
965 }
966 }
967
968 total / self.n as f64
970 }
971}
972
973impl Fitness for NkLandscape {
974 type Genome = BitString;
975 type Value = f64;
976
977 fn evaluate(&self, genome: &Self::Genome) -> f64 {
978 self.evaluate_bits(genome.bits())
979 }
980}
981
982#[cfg(test)]
983mod tests {
984 use super::*;
985 use crate::fitness::traits::Fitness;
986 use approx::assert_relative_eq;
987
988 #[test]
990 fn test_sphere_at_optimum() {
991 let sphere = Sphere::new(3);
992 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
993 assert_relative_eq!(sphere.evaluate(&optimum), 0.0);
994 }
995
996 #[test]
997 fn test_sphere_non_optimum() {
998 let sphere = Sphere::new(3);
999 let point = RealVector::new(vec![1.0, 2.0, 3.0]);
1000 assert_relative_eq!(sphere.evaluate(&point), -14.0);
1002 }
1003
1004 #[test]
1005 fn test_sphere_metadata() {
1006 let sphere = Sphere::new(5);
1007 assert_eq!(sphere.name(), "Sphere");
1008 assert_eq!(sphere.dimension(), 5);
1009 assert_eq!(sphere.bounds(), (-5.12, 5.12));
1010 assert_relative_eq!(sphere.optimal_fitness(), 0.0);
1011 assert_eq!(sphere.optimal_solution(), Some(vec![0.0; 5]));
1012 }
1013
1014 #[test]
1016 fn test_rastrigin_at_optimum() {
1017 let rastrigin = Rastrigin::new(3);
1018 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1019 assert_relative_eq!(rastrigin.evaluate(&optimum), 0.0, epsilon = 1e-10);
1020 }
1021
1022 #[test]
1023 fn test_rastrigin_non_optimum() {
1024 let rastrigin = Rastrigin::new(2);
1025 let point = RealVector::new(vec![1.0, 1.0]);
1026 let expected = 10.0 * 2.0
1029 + (1.0 * 1.0 - 10.0 * (2.0 * PI * 1.0).cos())
1030 + (1.0 * 1.0 - 10.0 * (2.0 * PI * 1.0).cos());
1031 assert_relative_eq!(rastrigin.evaluate(&point), -expected, epsilon = 1e-10);
1032 }
1033
1034 #[test]
1035 fn test_rastrigin_metadata() {
1036 let rastrigin = Rastrigin::new(10);
1037 assert_eq!(rastrigin.name(), "Rastrigin");
1038 assert_eq!(rastrigin.dimension(), 10);
1039 assert_eq!(rastrigin.bounds(), (-5.12, 5.12));
1040 }
1041
1042 #[test]
1044 fn test_rosenbrock_at_optimum() {
1045 let rosenbrock = Rosenbrock::new(3);
1046 let optimum = RealVector::new(vec![1.0, 1.0, 1.0]);
1047 assert_relative_eq!(rosenbrock.evaluate(&optimum), 0.0, epsilon = 1e-10);
1048 }
1049
1050 #[test]
1051 fn test_rosenbrock_non_optimum() {
1052 let rosenbrock = Rosenbrock::new(2);
1053 let point = RealVector::new(vec![0.0, 0.0]);
1054 assert_relative_eq!(rosenbrock.evaluate(&point), -1.0, epsilon = 1e-10);
1056 }
1057
1058 #[test]
1059 fn test_rosenbrock_metadata() {
1060 let rosenbrock = Rosenbrock::new(5);
1061 assert_eq!(rosenbrock.name(), "Rosenbrock");
1062 assert_eq!(rosenbrock.dimension(), 5);
1063 assert_eq!(rosenbrock.bounds(), (-5.0, 10.0));
1064 assert_eq!(rosenbrock.optimal_solution(), Some(vec![1.0; 5]));
1065 }
1066
1067 #[test]
1069 fn test_ackley_at_optimum() {
1070 let ackley = Ackley::new(3);
1071 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1072 assert_relative_eq!(ackley.evaluate(&optimum), 0.0, epsilon = 1e-10);
1073 }
1074
1075 #[test]
1076 fn test_ackley_metadata() {
1077 let ackley = Ackley::new(10);
1078 assert_eq!(ackley.name(), "Ackley");
1079 assert_eq!(ackley.dimension(), 10);
1080 assert_eq!(ackley.bounds(), (-32.768, 32.768));
1081 }
1082
1083 #[test]
1085 fn test_griewank_at_optimum() {
1086 let griewank = Griewank::new(3);
1087 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1088 assert_relative_eq!(griewank.evaluate(&optimum), 0.0, epsilon = 1e-10);
1089 }
1090
1091 #[test]
1092 fn test_griewank_metadata() {
1093 let griewank = Griewank::new(10);
1094 assert_eq!(griewank.name(), "Griewank");
1095 assert_eq!(griewank.dimension(), 10);
1096 assert_eq!(griewank.bounds(), (-600.0, 600.0));
1097 }
1098
1099 #[test]
1101 fn test_schwefel_metadata() {
1102 let schwefel = Schwefel::new(10);
1103 assert_eq!(schwefel.name(), "Schwefel");
1104 assert_eq!(schwefel.dimension(), 10);
1105 assert_eq!(schwefel.bounds(), (-500.0, 500.0));
1106 }
1107
1108 #[test]
1110 fn test_onemax_all_ones() {
1111 let onemax = OneMax::new(10);
1112 let genome = BitString::ones(10);
1113 assert_eq!(onemax.evaluate(&genome), 10);
1114 }
1115
1116 #[test]
1117 fn test_onemax_all_zeros() {
1118 let onemax = OneMax::new(10);
1119 let genome = BitString::zeros(10);
1120 assert_eq!(onemax.evaluate(&genome), 0);
1121 }
1122
1123 #[test]
1124 fn test_onemax_mixed() {
1125 let onemax = OneMax::new(5);
1126 let genome = BitString::new(vec![true, false, true, false, true]);
1127 assert_eq!(onemax.evaluate(&genome), 3);
1128 }
1129
1130 #[test]
1132 fn test_leadingones_all_ones() {
1133 let lo = LeadingOnes::new(10);
1134 let genome = BitString::ones(10);
1135 assert_eq!(lo.evaluate(&genome), 10);
1136 }
1137
1138 #[test]
1139 fn test_leadingones_all_zeros() {
1140 let lo = LeadingOnes::new(10);
1141 let genome = BitString::zeros(10);
1142 assert_eq!(lo.evaluate(&genome), 0);
1143 }
1144
1145 #[test]
1146 fn test_leadingones_mixed() {
1147 let lo = LeadingOnes::new(5);
1148 let genome = BitString::new(vec![true, true, false, true, true]);
1149 assert_eq!(lo.evaluate(&genome), 2); }
1151
1152 #[test]
1153 fn test_leadingones_starts_with_zero() {
1154 let lo = LeadingOnes::new(5);
1155 let genome = BitString::new(vec![false, true, true, true, true]);
1156 assert_eq!(lo.evaluate(&genome), 0);
1157 }
1158
1159 #[test]
1161 fn test_zdt1_pareto_front() {
1162 let zdt1 = Zdt1::new(10);
1163 let x = vec![0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
1165 let [f1, f2] = zdt1.evaluate(&x);
1166
1167 assert_relative_eq!(f1, 0.5, epsilon = 1e-10);
1169
1170 assert_relative_eq!(f2, 1.0 - f1.sqrt(), epsilon = 1e-10);
1172 }
1173
1174 #[test]
1176 fn test_zdt2_pareto_front() {
1177 let zdt2 = Zdt2::new(10);
1178 let x = vec![0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
1179 let [f1, f2] = zdt2.evaluate(&x);
1180
1181 assert_relative_eq!(f1, 0.5, epsilon = 1e-10);
1182 assert_relative_eq!(f2, 1.0 - f1 * f1, epsilon = 1e-10);
1184 }
1185
1186 #[test]
1188 fn test_schaffer_n1() {
1189 let schaffer = SchafferN1::new();
1190 let [f1, f2] = schaffer.evaluate(0.0);
1191 assert_relative_eq!(f1, 0.0, epsilon = 1e-10);
1192 assert_relative_eq!(f2, 4.0, epsilon = 1e-10);
1193 }
1194
1195 #[test]
1197 fn test_levy_at_optimum() {
1198 let levy = Levy::new(3);
1199 let optimum = RealVector::new(vec![1.0, 1.0, 1.0]);
1200 assert_relative_eq!(levy.evaluate(&optimum), 0.0, epsilon = 1e-10);
1201 }
1202
1203 #[test]
1205 fn test_dixonprice_metadata() {
1206 let dp = DixonPrice::new(5);
1207 assert_eq!(dp.name(), "Dixon-Price");
1208 assert_eq!(dp.dimension(), 5);
1209 }
1210
1211 #[test]
1213 fn test_styblinskitang_near_optimum() {
1214 let st = StyblinskiTang::new(2);
1215 let near_opt = RealVector::new(vec![-2.9, -2.9]);
1216 let fitness = st.evaluate(&near_opt);
1218 let optimal = st.optimal_fitness();
1219 assert!(
1221 fitness > optimal - 1.0,
1222 "Fitness {} should be close to optimal {}",
1223 fitness,
1224 optimal
1225 );
1226 }
1227
1228 #[test]
1230 fn test_royal_road_all_ones() {
1231 let rr = RoyalRoad::new(4, 4); let genome = BitString::ones(16);
1233 let fitness: usize = rr.evaluate(&genome);
1234 assert_eq!(fitness, 4); }
1236
1237 #[test]
1238 fn test_royal_road_all_zeros() {
1239 let rr = RoyalRoad::new(4, 4);
1240 let genome = BitString::zeros(16);
1241 let fitness: usize = rr.evaluate(&genome);
1242 assert_eq!(fitness, 0); }
1244
1245 #[test]
1246 fn test_royal_road_partial() {
1247 let rr = RoyalRoad::new(4, 4);
1248 let bits = vec![
1250 true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, false, ];
1255 let genome = BitString::new(bits);
1256 let fitness: usize = rr.evaluate(&genome);
1257 assert_eq!(fitness, 2);
1258 }
1259
1260 #[test]
1261 fn test_royal_road_standard() {
1262 let rr = RoyalRoad::standard();
1263 assert_eq!(rr.genome_length(), 64);
1264 assert_eq!(rr.schema_size, 8);
1265 assert_eq!(rr.num_schemas, 8);
1266 }
1267
1268 #[test]
1270 fn test_nk_landscape_creation() {
1271 let nk = NkLandscape::new(10, 2, 42);
1272 assert_eq!(nk.genome_length(), 10);
1273 assert_eq!(nk.epistasis(), 2);
1274 }
1275
1276 #[test]
1277 fn test_nk_landscape_deterministic() {
1278 let nk1 = NkLandscape::new(8, 2, 123);
1280 let nk2 = NkLandscape::new(8, 2, 123);
1281
1282 let genome = BitString::new(vec![true, false, true, false, true, false, true, false]);
1283 let f1: f64 = nk1.evaluate(&genome);
1284 let f2: f64 = nk2.evaluate(&genome);
1285
1286 assert_relative_eq!(f1, f2);
1287 }
1288
1289 #[test]
1290 fn test_nk_landscape_fitness_range() {
1291 let nk = NkLandscape::new(10, 3, 42);
1292 let genome = BitString::new(vec![true; 10]);
1293 let fitness: f64 = nk.evaluate(&genome);
1294
1295 assert!((0.0..=1.0).contains(&fitness));
1297 }
1298
1299 #[test]
1300 fn test_nk_landscape_adjacent() {
1301 let nk = NkLandscape::with_adjacent_neighbors(10, 2, 42);
1302 let genome = BitString::ones(10);
1303 let fitness: f64 = nk.evaluate(&genome);
1304
1305 assert!((0.0..=1.0).contains(&fitness));
1306 }
1307
1308 #[test]
1309 #[should_panic(expected = "K must be less than N")]
1310 fn test_nk_landscape_invalid_k() {
1311 let _nk = NkLandscape::new(5, 5, 42); }
1313}