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)
672 .map(|i| {
673 let two_pow_j = (1u64 << (i + 1)) as f64; let exp_num = two_pow_j - 2.0; let exp_den = two_pow_j; 2.0_f64.powf(-exp_num / exp_den)
677 })
678 .collect();
679 Some(optimal)
680 }
681
682 fn evaluate_raw(&self, x: &[f64]) -> f64 {
683 let term1 = (x[0] - 1.0).powi(2);
684
685 let sum: f64 = x
686 .windows(2)
687 .enumerate()
688 .map(|(i, w)| (i + 2) as f64 * (2.0 * w[1] * w[1] - w[0]).powi(2))
689 .sum();
690
691 term1 + sum
692 }
693}
694
695impl Fitness for DixonPrice {
696 type Genome = RealVector;
697 type Value = f64;
698
699 fn evaluate(&self, genome: &Self::Genome) -> f64 {
700 -self.evaluate_raw(genome.genes())
701 }
702}
703
704#[derive(Clone, Debug)]
708pub struct StyblinskiTang {
709 dimension: usize,
710}
711
712impl StyblinskiTang {
713 pub fn new(dimension: usize) -> Self {
715 Self { dimension }
716 }
717}
718
719impl BenchmarkFunction for StyblinskiTang {
720 fn name(&self) -> &'static str {
721 "Styblinski-Tang"
722 }
723
724 fn dimension(&self) -> usize {
725 self.dimension
726 }
727
728 fn bounds(&self) -> (f64, f64) {
729 (-5.0, 5.0)
730 }
731
732 fn optimal_fitness(&self) -> f64 {
733 -39.16617 * self.dimension as f64
735 }
736
737 fn optimal_solution(&self) -> Option<Vec<f64>> {
738 Some(vec![-2.903534; self.dimension])
739 }
740
741 fn evaluate_raw(&self, x: &[f64]) -> f64 {
742 x.iter()
743 .map(|xi| xi.powi(4) - 16.0 * xi.powi(2) + 5.0 * xi)
744 .sum::<f64>()
745 / 2.0
746 }
747}
748
749impl Fitness for StyblinskiTang {
750 type Genome = RealVector;
751 type Value = f64;
752
753 fn evaluate(&self, genome: &Self::Genome) -> f64 {
754 -self.evaluate_raw(genome.genes())
755 }
756}
757
758#[derive(Clone, Debug)]
772pub struct RoyalRoad {
773 pub schema_size: usize,
775 pub num_schemas: usize,
777}
778
779impl RoyalRoad {
780 pub fn new(schema_size: usize, num_schemas: usize) -> Self {
786 Self {
787 schema_size,
788 num_schemas,
789 }
790 }
791
792 pub fn standard() -> Self {
794 Self::new(8, 8)
795 }
796
797 pub fn genome_length(&self) -> usize {
799 self.schema_size * self.num_schemas
800 }
801
802 fn is_complete_schema(&self, bits: &[bool], schema_index: usize) -> bool {
804 let start = schema_index * self.schema_size;
805 let end = start + self.schema_size;
806
807 if end > bits.len() {
808 return false;
809 }
810
811 bits[start..end].iter().all(|&b| b)
812 }
813
814 pub fn count_complete_schemas(&self, bits: &[bool]) -> usize {
816 (0..self.num_schemas)
817 .filter(|&i| self.is_complete_schema(bits, i))
818 .count()
819 }
820}
821
822impl Fitness for RoyalRoad {
823 type Genome = BitString;
824 type Value = usize;
825
826 fn evaluate(&self, genome: &Self::Genome) -> usize {
827 self.count_complete_schemas(genome.bits())
828 }
829}
830
831#[derive(Clone, Debug)]
843pub struct NkLandscape {
844 n: usize,
846 k: usize,
848 neighbors: Vec<Vec<usize>>,
850 contributions: Vec<std::collections::HashMap<Vec<bool>, f64>>,
853}
854
855impl NkLandscape {
856 pub fn new(n: usize, k: usize, seed: u64) -> Self {
863 assert!(k < n, "K must be less than N");
864
865 use rand::SeedableRng;
866 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
867
868 let mut neighbors = Vec::with_capacity(n);
870 for i in 0..n {
871 let mut gene_neighbors: Vec<usize> = (0..n).filter(|&j| j != i).collect();
872 use rand::seq::SliceRandom;
874 gene_neighbors.shuffle(&mut rng);
875 gene_neighbors.truncate(k);
876 gene_neighbors.sort();
877 neighbors.push(gene_neighbors);
878 }
879
880 let mut contributions = Vec::with_capacity(n);
882 for _i in 0..n {
883 let num_configs = 1 << (k + 1); let mut table = std::collections::HashMap::with_capacity(num_configs);
885
886 for config_bits in 0..num_configs {
888 let config: Vec<bool> = (0..=k).map(|j| (config_bits >> j) & 1 == 1).collect();
889 use rand::Rng;
890 table.insert(config, rng.gen::<f64>());
891 }
892
893 contributions.push(table);
894 }
895
896 Self {
897 n,
898 k,
899 neighbors,
900 contributions,
901 }
902 }
903
904 pub fn with_adjacent_neighbors(n: usize, k: usize, seed: u64) -> Self {
907 assert!(k < n, "K must be less than N");
908
909 use rand::SeedableRng;
910 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
911
912 let mut neighbors = Vec::with_capacity(n);
914 for i in 0..n {
915 let gene_neighbors: Vec<usize> = (1..=k).map(|offset| (i + offset) % n).collect();
916 neighbors.push(gene_neighbors);
917 }
918
919 let mut contributions = Vec::with_capacity(n);
921 for _i in 0..n {
922 let num_configs = 1 << (k + 1);
923 let mut table = std::collections::HashMap::with_capacity(num_configs);
924
925 for config_bits in 0..num_configs {
926 let config: Vec<bool> = (0..=k).map(|j| (config_bits >> j) & 1 == 1).collect();
927 use rand::Rng;
928 table.insert(config, rng.gen::<f64>());
929 }
930
931 contributions.push(table);
932 }
933
934 Self {
935 n,
936 k,
937 neighbors,
938 contributions,
939 }
940 }
941
942 pub fn genome_length(&self) -> usize {
944 self.n
945 }
946
947 pub fn epistasis(&self) -> usize {
949 self.k
950 }
951
952 pub fn evaluate_bits(&self, bits: &[bool]) -> f64 {
954 assert!(bits.len() >= self.n);
955
956 let mut total = 0.0;
957
958 for i in 0..self.n {
959 let mut config = Vec::with_capacity(self.k + 1);
961 config.push(bits[i]);
962 for &j in &self.neighbors[i] {
963 config.push(bits[j]);
964 }
965
966 if let Some(&contribution) = self.contributions[i].get(&config) {
968 total += contribution;
969 }
970 }
971
972 total / self.n as f64
974 }
975}
976
977impl Fitness for NkLandscape {
978 type Genome = BitString;
979 type Value = f64;
980
981 fn evaluate(&self, genome: &Self::Genome) -> f64 {
982 self.evaluate_bits(genome.bits())
983 }
984}
985
986#[cfg(test)]
987mod tests {
988 use super::*;
989 use crate::fitness::traits::Fitness;
990 use approx::assert_relative_eq;
991
992 #[test]
994 fn test_sphere_at_optimum() {
995 let sphere = Sphere::new(3);
996 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
997 assert_relative_eq!(sphere.evaluate(&optimum), 0.0);
998 }
999
1000 #[test]
1001 fn test_sphere_non_optimum() {
1002 let sphere = Sphere::new(3);
1003 let point = RealVector::new(vec![1.0, 2.0, 3.0]);
1004 assert_relative_eq!(sphere.evaluate(&point), -14.0);
1006 }
1007
1008 #[test]
1009 fn test_sphere_metadata() {
1010 let sphere = Sphere::new(5);
1011 assert_eq!(sphere.name(), "Sphere");
1012 assert_eq!(sphere.dimension(), 5);
1013 assert_eq!(sphere.bounds(), (-5.12, 5.12));
1014 assert_relative_eq!(sphere.optimal_fitness(), 0.0);
1015 assert_eq!(sphere.optimal_solution(), Some(vec![0.0; 5]));
1016 }
1017
1018 #[test]
1020 fn test_rastrigin_at_optimum() {
1021 let rastrigin = Rastrigin::new(3);
1022 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1023 assert_relative_eq!(rastrigin.evaluate(&optimum), 0.0, epsilon = 1e-10);
1024 }
1025
1026 #[test]
1027 fn test_rastrigin_non_optimum() {
1028 let rastrigin = Rastrigin::new(2);
1029 let point = RealVector::new(vec![1.0, 1.0]);
1030 let expected = 10.0 * 2.0
1033 + (1.0 * 1.0 - 10.0 * (2.0 * PI * 1.0).cos())
1034 + (1.0 * 1.0 - 10.0 * (2.0 * PI * 1.0).cos());
1035 assert_relative_eq!(rastrigin.evaluate(&point), -expected, epsilon = 1e-10);
1036 }
1037
1038 #[test]
1039 fn test_rastrigin_metadata() {
1040 let rastrigin = Rastrigin::new(10);
1041 assert_eq!(rastrigin.name(), "Rastrigin");
1042 assert_eq!(rastrigin.dimension(), 10);
1043 assert_eq!(rastrigin.bounds(), (-5.12, 5.12));
1044 }
1045
1046 #[test]
1048 fn test_rosenbrock_at_optimum() {
1049 let rosenbrock = Rosenbrock::new(3);
1050 let optimum = RealVector::new(vec![1.0, 1.0, 1.0]);
1051 assert_relative_eq!(rosenbrock.evaluate(&optimum), 0.0, epsilon = 1e-10);
1052 }
1053
1054 #[test]
1055 fn test_rosenbrock_non_optimum() {
1056 let rosenbrock = Rosenbrock::new(2);
1057 let point = RealVector::new(vec![0.0, 0.0]);
1058 assert_relative_eq!(rosenbrock.evaluate(&point), -1.0, epsilon = 1e-10);
1060 }
1061
1062 #[test]
1063 fn test_rosenbrock_metadata() {
1064 let rosenbrock = Rosenbrock::new(5);
1065 assert_eq!(rosenbrock.name(), "Rosenbrock");
1066 assert_eq!(rosenbrock.dimension(), 5);
1067 assert_eq!(rosenbrock.bounds(), (-5.0, 10.0));
1068 assert_eq!(rosenbrock.optimal_solution(), Some(vec![1.0; 5]));
1069 }
1070
1071 #[test]
1073 fn test_ackley_at_optimum() {
1074 let ackley = Ackley::new(3);
1075 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1076 assert_relative_eq!(ackley.evaluate(&optimum), 0.0, epsilon = 1e-10);
1077 }
1078
1079 #[test]
1080 fn test_ackley_metadata() {
1081 let ackley = Ackley::new(10);
1082 assert_eq!(ackley.name(), "Ackley");
1083 assert_eq!(ackley.dimension(), 10);
1084 assert_eq!(ackley.bounds(), (-32.768, 32.768));
1085 }
1086
1087 #[test]
1089 fn test_griewank_at_optimum() {
1090 let griewank = Griewank::new(3);
1091 let optimum = RealVector::new(vec![0.0, 0.0, 0.0]);
1092 assert_relative_eq!(griewank.evaluate(&optimum), 0.0, epsilon = 1e-10);
1093 }
1094
1095 #[test]
1096 fn test_griewank_metadata() {
1097 let griewank = Griewank::new(10);
1098 assert_eq!(griewank.name(), "Griewank");
1099 assert_eq!(griewank.dimension(), 10);
1100 assert_eq!(griewank.bounds(), (-600.0, 600.0));
1101 }
1102
1103 #[test]
1105 fn test_schwefel_metadata() {
1106 let schwefel = Schwefel::new(10);
1107 assert_eq!(schwefel.name(), "Schwefel");
1108 assert_eq!(schwefel.dimension(), 10);
1109 assert_eq!(schwefel.bounds(), (-500.0, 500.0));
1110 }
1111
1112 #[test]
1114 fn test_onemax_all_ones() {
1115 let onemax = OneMax::new(10);
1116 let genome = BitString::ones(10);
1117 assert_eq!(onemax.evaluate(&genome), 10);
1118 }
1119
1120 #[test]
1121 fn test_onemax_all_zeros() {
1122 let onemax = OneMax::new(10);
1123 let genome = BitString::zeros(10);
1124 assert_eq!(onemax.evaluate(&genome), 0);
1125 }
1126
1127 #[test]
1128 fn test_onemax_mixed() {
1129 let onemax = OneMax::new(5);
1130 let genome = BitString::new(vec![true, false, true, false, true]);
1131 assert_eq!(onemax.evaluate(&genome), 3);
1132 }
1133
1134 #[test]
1136 fn test_leadingones_all_ones() {
1137 let lo = LeadingOnes::new(10);
1138 let genome = BitString::ones(10);
1139 assert_eq!(lo.evaluate(&genome), 10);
1140 }
1141
1142 #[test]
1143 fn test_leadingones_all_zeros() {
1144 let lo = LeadingOnes::new(10);
1145 let genome = BitString::zeros(10);
1146 assert_eq!(lo.evaluate(&genome), 0);
1147 }
1148
1149 #[test]
1150 fn test_leadingones_mixed() {
1151 let lo = LeadingOnes::new(5);
1152 let genome = BitString::new(vec![true, true, false, true, true]);
1153 assert_eq!(lo.evaluate(&genome), 2); }
1155
1156 #[test]
1157 fn test_leadingones_starts_with_zero() {
1158 let lo = LeadingOnes::new(5);
1159 let genome = BitString::new(vec![false, true, true, true, true]);
1160 assert_eq!(lo.evaluate(&genome), 0);
1161 }
1162
1163 #[test]
1165 fn test_zdt1_pareto_front() {
1166 let zdt1 = Zdt1::new(10);
1167 let x = vec![0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
1169 let [f1, f2] = zdt1.evaluate(&x);
1170
1171 assert_relative_eq!(f1, 0.5, epsilon = 1e-10);
1173
1174 assert_relative_eq!(f2, 1.0 - f1.sqrt(), epsilon = 1e-10);
1176 }
1177
1178 #[test]
1180 fn test_zdt2_pareto_front() {
1181 let zdt2 = Zdt2::new(10);
1182 let x = vec![0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
1183 let [f1, f2] = zdt2.evaluate(&x);
1184
1185 assert_relative_eq!(f1, 0.5, epsilon = 1e-10);
1186 assert_relative_eq!(f2, 1.0 - f1 * f1, epsilon = 1e-10);
1188 }
1189
1190 #[test]
1192 fn test_schaffer_n1() {
1193 let schaffer = SchafferN1::new();
1194 let [f1, f2] = schaffer.evaluate(0.0);
1195 assert_relative_eq!(f1, 0.0, epsilon = 1e-10);
1196 assert_relative_eq!(f2, 4.0, epsilon = 1e-10);
1197 }
1198
1199 #[test]
1201 fn test_levy_at_optimum() {
1202 let levy = Levy::new(3);
1203 let optimum = RealVector::new(vec![1.0, 1.0, 1.0]);
1204 assert_relative_eq!(levy.evaluate(&optimum), 0.0, epsilon = 1e-10);
1205 }
1206
1207 #[test]
1209 fn test_dixonprice_metadata() {
1210 let dp = DixonPrice::new(5);
1211 assert_eq!(dp.name(), "Dixon-Price");
1212 assert_eq!(dp.dimension(), 5);
1213 }
1214
1215 #[test]
1220 fn test_dixonprice_optimal_solution_is_optimum() {
1221 for dim in 2..=6 {
1222 let dp = DixonPrice::new(dim);
1223 let opt = dp
1224 .optimal_solution()
1225 .expect("Dixon-Price exposes an optimal solution");
1226 let value = dp.evaluate_raw(&opt);
1227 assert!(
1228 value < 1e-12,
1229 "f(optimal_solution()) for dim={dim} was {value}, expected < 1e-12"
1230 );
1231 }
1232 }
1233
1234 #[test]
1236 fn test_styblinskitang_near_optimum() {
1237 let st = StyblinskiTang::new(2);
1238 let near_opt = RealVector::new(vec![-2.9, -2.9]);
1239 let fitness = st.evaluate(&near_opt);
1241 let optimal = st.optimal_fitness();
1242 assert!(
1244 fitness > optimal - 1.0,
1245 "Fitness {} should be close to optimal {}",
1246 fitness,
1247 optimal
1248 );
1249 }
1250
1251 #[test]
1253 fn test_royal_road_all_ones() {
1254 let rr = RoyalRoad::new(4, 4); let genome = BitString::ones(16);
1256 let fitness: usize = rr.evaluate(&genome);
1257 assert_eq!(fitness, 4); }
1259
1260 #[test]
1261 fn test_royal_road_all_zeros() {
1262 let rr = RoyalRoad::new(4, 4);
1263 let genome = BitString::zeros(16);
1264 let fitness: usize = rr.evaluate(&genome);
1265 assert_eq!(fitness, 0); }
1267
1268 #[test]
1269 fn test_royal_road_partial() {
1270 let rr = RoyalRoad::new(4, 4);
1271 let bits = vec![
1273 true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, false, ];
1278 let genome = BitString::new(bits);
1279 let fitness: usize = rr.evaluate(&genome);
1280 assert_eq!(fitness, 2);
1281 }
1282
1283 #[test]
1284 fn test_royal_road_standard() {
1285 let rr = RoyalRoad::standard();
1286 assert_eq!(rr.genome_length(), 64);
1287 assert_eq!(rr.schema_size, 8);
1288 assert_eq!(rr.num_schemas, 8);
1289 }
1290
1291 #[test]
1293 fn test_nk_landscape_creation() {
1294 let nk = NkLandscape::new(10, 2, 42);
1295 assert_eq!(nk.genome_length(), 10);
1296 assert_eq!(nk.epistasis(), 2);
1297 }
1298
1299 #[test]
1300 fn test_nk_landscape_deterministic() {
1301 let nk1 = NkLandscape::new(8, 2, 123);
1303 let nk2 = NkLandscape::new(8, 2, 123);
1304
1305 let genome = BitString::new(vec![true, false, true, false, true, false, true, false]);
1306 let f1: f64 = nk1.evaluate(&genome);
1307 let f2: f64 = nk2.evaluate(&genome);
1308
1309 assert_relative_eq!(f1, f2);
1310 }
1311
1312 #[test]
1313 fn test_nk_landscape_fitness_range() {
1314 let nk = NkLandscape::new(10, 3, 42);
1315 let genome = BitString::new(vec![true; 10]);
1316 let fitness: f64 = nk.evaluate(&genome);
1317
1318 assert!((0.0..=1.0).contains(&fitness));
1320 }
1321
1322 #[test]
1323 fn test_nk_landscape_adjacent() {
1324 let nk = NkLandscape::with_adjacent_neighbors(10, 2, 42);
1325 let genome = BitString::ones(10);
1326 let fitness: f64 = nk.evaluate(&genome);
1327
1328 assert!((0.0..=1.0).contains(&fitness));
1329 }
1330
1331 #[test]
1332 #[should_panic(expected = "K must be less than N")]
1333 fn test_nk_landscape_invalid_k() {
1334 let _nk = NkLandscape::new(5, 5, 42); }
1336}