scirs2_optimize/multi_objective/algorithms/
spea2.rs1use super::{MultiObjectiveOptimizer, MultiObjectiveResult};
6use crate::error::OptimizeError;
7use crate::multi_objective::solutions::{Population, Solution};
8use ndarray::{Array1, ArrayView1};
9
10#[derive(Debug, Clone)]
12pub struct SPEA2 {
13 population_size: usize,
14 archive_size: usize,
15 n_objectives: usize,
16 n_variables: usize,
17 archive: Vec<Solution>,
18 population: Population,
19 generation: usize,
20 evaluations: usize,
21}
22
23impl SPEA2 {
24 pub fn new(population_size: usize, n_objectives: usize, n_variables: usize) -> Self {
26 Self {
27 population_size,
28 archive_size: population_size,
29 n_objectives,
30 n_variables,
31 archive: Vec::new(),
32 population: Population::with_capacity(population_size, n_objectives, n_variables),
33 generation: 0,
34 evaluations: 0,
35 }
36 }
37
38 fn calculate_strength(&self, population: &[Solution]) -> Vec<usize> {
40 let mut strengths = vec![0; population.len()];
41
42 for (i, sol_i) in population.iter().enumerate() {
43 for (j, sol_j) in population.iter().enumerate() {
44 if i != j && self.dominates(sol_i, sol_j) {
45 strengths[i] += 1;
46 }
47 }
48 }
49
50 strengths
51 }
52
53 fn dominates(&self, a: &Solution, b: &Solution) -> bool {
55 let mut at_least_one_better = false;
56
57 for i in 0..self.n_objectives {
58 if a.objectives[i] > b.objectives[i] {
59 return false;
60 }
61 if a.objectives[i] < b.objectives[i] {
62 at_least_one_better = true;
63 }
64 }
65
66 at_least_one_better
67 }
68
69 fn kth_nearest_distance(&self, index: usize, population: &[Solution], k: usize) -> f64 {
71 let mut distances = Vec::new();
72 let current = &population[index];
73
74 for (i, other) in population.iter().enumerate() {
75 if i != index {
76 let dist = self.euclidean_distance(
77 current.objectives.as_slice().unwrap(),
78 other.objectives.as_slice().unwrap(),
79 );
80 distances.push(dist);
81 }
82 }
83
84 distances.sort_by(|a, b| a.partial_cmp(b).unwrap());
85 distances
86 .get(k.min(distances.len() - 1))
87 .copied()
88 .unwrap_or(0.0)
89 }
90
91 fn euclidean_distance(&self, a: &[f64], b: &[f64]) -> f64 {
92 a.iter()
93 .zip(b.iter())
94 .map(|(x, y)| (x - y).powi(2))
95 .sum::<f64>()
96 .sqrt()
97 }
98}
99
100impl MultiObjectiveOptimizer for SPEA2 {
101 fn optimize<F>(&mut self, objective_function: F) -> Result<MultiObjectiveResult, OptimizeError>
102 where
103 F: Fn(&ArrayView1<f64>) -> Array1<f64> + Send + Sync,
104 {
105 Ok(MultiObjectiveResult {
107 pareto_front: Vec::new(),
108 population: Vec::new(),
109 n_evaluations: 0,
110 n_generations: 0,
111 success: true,
112 message: "SPEA2 not yet implemented".to_string(),
113 hypervolume: Some(0.0),
114 metrics: Default::default(),
115 })
116 }
117
118 fn evolve_generation<F>(&mut self, _objective_function: &F) -> Result<(), OptimizeError>
119 where
120 F: Fn(&ArrayView1<f64>) -> Array1<f64> + Send + Sync,
121 {
122 self.generation += 1;
123 Ok(())
124 }
125
126 fn initialize_population(&mut self) -> Result<(), OptimizeError> {
127 Ok(())
129 }
130
131 fn check_convergence(&self) -> bool {
132 false
134 }
135
136 fn get_population(&self) -> &Population {
137 &self.population
138 }
139
140 fn get_generation(&self) -> usize {
141 self.generation
142 }
143
144 fn get_evaluations(&self) -> usize {
145 self.evaluations
146 }
147
148 fn name(&self) -> &str {
149 "SPEA2"
150 }
151}