Skip to main content

optirs_nas/multi_objective/
core.rs

1//! Shared core types: the MultiObjectiveOptimizer trait and the Pareto-front / statistics data model used by every algorithm in this module.
2
3use crate::error::Result;
4use crate::nas_engine::{
5    MultiObjectiveConfig, ObjectiveConfig, ObjectiveType, OptimizationDirection,
6    OptimizerArchitecture, SearchResult,
7};
8use crate::EvaluationMetric;
9use scirs2_core::numeric::Float;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::fmt::Debug;
13
14/// Coverage metrics
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CoverageMetrics<T: Float + Debug + Send + Sync + 'static> {
17    /// Coverage of objective space
18    pub objective_space_coverage: T,
19    /// Distance from reference front
20    pub reference_distance: T,
21    /// Epsilon dominance measure
22    pub epsilon_dominance: T,
23}
24/// Solution creation methods
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26pub enum CreationMethod {
27    RandomGeneration,
28    Crossover,
29    Mutation,
30    LocalSearch,
31    Repair,
32    Custom,
33}
34/// Front quality metrics
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct FrontMetrics<T: Float + Debug + Send + Sync + 'static> {
37    /// Hypervolume
38    pub hypervolume: T,
39    /// Spread (diversity measure)
40    pub spread: T,
41    /// Spacing (uniformity measure)
42    pub spacing: T,
43    /// Convergence measure
44    pub convergence: T,
45    /// Number of non-dominated solutions
46    pub num_solutions: usize,
47    /// Coverage metrics
48    pub coverage: CoverageMetrics<T>,
49}
50/// Individual in the population
51#[derive(Debug, Clone)]
52pub struct Individual<T: Float + Debug + Send + Sync + 'static> {
53    /// Architecture
54    pub architecture: OptimizerArchitecture<T>,
55    /// Objective values
56    pub objectives: Vec<T>,
57    /// Constraint violations
58    pub constraints: Vec<T>,
59    /// Dominance rank
60    pub rank: usize,
61    /// Crowding distance
62    pub crowding_distance: T,
63    /// Fitness value (for single-objective algorithms)
64    pub fitness: T,
65    /// Individual ID
66    pub id: String,
67}
68/// Multi-objective optimization statistics
69#[derive(Debug, Clone)]
70pub struct MultiObjectiveStatistics<T: Float + Debug + Send + Sync + 'static> {
71    /// Current generation
72    pub generation: usize,
73    /// Total evaluations
74    pub total_evaluations: usize,
75    /// Pareto front size
76    pub pareto_front_size: usize,
77    /// Best hypervolume achieved
78    pub best_hypervolume: T,
79    /// Convergence history
80    pub convergence_history: Vec<T>,
81    /// Diversity history
82    pub diversity_history: Vec<T>,
83    /// Algorithm-specific metrics
84    pub algorithm_metrics: HashMap<String, T>,
85}
86impl<T: Float + Debug + Default + Send + Sync> Default for MultiObjectiveStatistics<T> {
87    fn default() -> Self {
88        Self {
89            generation: 0,
90            total_evaluations: 0,
91            pareto_front_size: 0,
92            best_hypervolume: T::zero(),
93            convergence_history: Vec::new(),
94            diversity_history: Vec::new(),
95            algorithm_metrics: HashMap::new(),
96        }
97    }
98}
99/// Objective space bounds
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ObjectiveBounds<T: Float + Debug + Send + Sync + 'static> {
102    /// Minimum values for each objective
103    pub min_values: Vec<T>,
104    /// Maximum values for each objective
105    pub max_values: Vec<T>,
106    /// Ideal point
107    pub ideal_point: Vec<T>,
108    /// Nadir point
109    pub nadir_point: Vec<T>,
110}
111/// Pareto front representation
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ParetoFront<T: Float + Debug + Send + Sync + 'static> {
114    /// Pareto-optimal solutions
115    pub solutions: Vec<ParetoSolution<T>>,
116    /// Objective space bounds
117    pub objective_bounds: ObjectiveBounds<T>,
118    /// Front metrics
119    pub metrics: FrontMetrics<T>,
120    /// Generation when last updated
121    pub generation: usize,
122    /// Timestamp of last update
123    pub last_updated: std::time::SystemTime,
124}
125impl<T: Float + Debug + Default + Send + Sync> ParetoFront<T> {
126    pub(super) fn new() -> Self {
127        Self {
128            solutions: Vec::new(),
129            objective_bounds: ObjectiveBounds {
130                min_values: Vec::new(),
131                max_values: Vec::new(),
132                ideal_point: Vec::new(),
133                nadir_point: Vec::new(),
134            },
135            metrics: FrontMetrics {
136                hypervolume: T::zero(),
137                spread: T::zero(),
138                spacing: T::zero(),
139                convergence: T::zero(),
140                num_solutions: 0,
141                coverage: CoverageMetrics {
142                    objective_space_coverage: T::zero(),
143                    reference_distance: T::zero(),
144                    epsilon_dominance: T::zero(),
145                },
146            },
147            generation: 0,
148            last_updated: std::time::SystemTime::now(),
149        }
150    }
151}
152impl<T: Float + Debug + Default + Send + Sync> Default for ParetoFront<T> {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157/// Individual Pareto-optimal solution
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct ParetoSolution<T: Float + Debug + Send + Sync + 'static> {
160    /// Optimizer architecture
161    pub architecture: OptimizerArchitecture<T>,
162    /// Objective values
163    pub objectives: Vec<T>,
164    /// Constraint violations (if any)
165    pub constraint_violations: Vec<T>,
166    /// Dominance rank
167    pub rank: usize,
168    /// Crowding distance
169    pub crowding_distance: T,
170    /// Solution metadata
171    pub metadata: SolutionMetadata,
172}
173/// Solution metadata
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SolutionMetadata {
176    /// Solution ID
177    pub id: String,
178    /// Generation when found
179    pub generation: usize,
180    /// Evaluation count when found
181    pub evaluation_count: usize,
182    /// Parent solutions (if offspring)
183    pub parents: Vec<String>,
184    /// Creation method
185    pub creation_method: CreationMethod,
186}
187/// The [`EvaluationMetric`] an [`ObjectiveType`] is read from.
188///
189/// Every optimizer in this module reads its objectives through this one mapping,
190/// so NSGA-II, NSGA-III, MOEA/D and the weighted sum can never disagree about what
191/// a configured objective means.
192pub fn evaluation_metric_for_objective(objective_type: &ObjectiveType) -> EvaluationMetric {
193    match objective_type {
194        ObjectiveType::Accuracy => EvaluationMetric::Accuracy,
195        ObjectiveType::Loss => EvaluationMetric::FinalPerformance,
196        ObjectiveType::TrainingTime => EvaluationMetric::TrainingTime,
197        ObjectiveType::InferenceTime => EvaluationMetric::ComputationTime,
198        ObjectiveType::MemoryUsage => EvaluationMetric::MemoryUsage,
199        ObjectiveType::EnergyConsumption => EvaluationMetric::ComputationTime,
200        ObjectiveType::ModelSize => EvaluationMetric::MemoryUsage,
201        ObjectiveType::Performance => EvaluationMetric::FinalPerformance,
202        ObjectiveType::Efficiency => EvaluationMetric::ComputationalEfficiency,
203        ObjectiveType::Robustness => EvaluationMetric::Robustness,
204        ObjectiveType::Interpretability => EvaluationMetric::FinalPerformance,
205        ObjectiveType::Fairness => EvaluationMetric::FinalPerformance,
206        ObjectiveType::Privacy => EvaluationMetric::FinalPerformance,
207        ObjectiveType::Sustainability => EvaluationMetric::ComputationalEfficiency,
208        ObjectiveType::Cost => EvaluationMetric::ComputationalEfficiency,
209        ObjectiveType::Custom(_) => EvaluationMetric::FinalPerformance,
210    }
211}
212
213/// Objective vector of `result` in the **raw** objective space the configuration
214/// declares (directions untouched), one entry per configured objective.
215///
216/// A metric the evaluation did not report contributes `0`, which is the
217/// convention the rest of the module already used.
218pub fn objective_vector_for_result<T: Float + Debug + Send + Sync + 'static>(
219    objectives: &[ObjectiveConfig<T>],
220    result: &SearchResult<T>,
221) -> Vec<T> {
222    objectives
223        .iter()
224        .map(|objective| {
225            let metric = evaluation_metric_for_objective(&objective.objective_type);
226            result
227                .evaluation_results
228                .metric_scores
229                .get(&metric)
230                .copied()
231                .unwrap_or_else(T::zero)
232        })
233        .collect()
234}
235
236/// Per-objective optimization directions of a configuration, in order.
237pub fn objective_directions<T: Float + Debug + Send + Sync + 'static>(
238    objectives: &[ObjectiveConfig<T>],
239) -> Vec<OptimizationDirection> {
240    objectives
241        .iter()
242        .map(|objective| objective.direction.clone())
243        .collect()
244}
245
246/// Base trait for multi-objective optimizers
247pub trait MultiObjectiveOptimizer<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
248    /// Initialize the optimizer
249    fn initialize(&mut self, config: &MultiObjectiveConfig<T>) -> Result<()>;
250
251    /// Update Pareto front with new results
252    fn update_pareto_front(&mut self, results: &[SearchResult<T>]) -> Result<ParetoFront<T>>;
253
254    /// Get current Pareto front
255    fn get_pareto_front(&self) -> &ParetoFront<T>;
256
257    /// Select next candidates for evaluation
258    fn select_candidates(
259        &mut self,
260        population: &[OptimizerArchitecture<T>],
261        objectives: &[T],
262    ) -> Result<Vec<OptimizerArchitecture<T>>>;
263
264    /// Get algorithm name
265    fn name(&self) -> &str;
266
267    /// Get optimization statistics
268    fn get_statistics(&self) -> MultiObjectiveStatistics<T>;
269}