use std::fmt::{Display, Formatter};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use nsga2::as_algorithm_args;
use crate::algorithms::nsga2::crowding::{CrowdingDistanceOperator, DistanceData};
use crate::algorithms::nsga2::survival::{EpsilonData, NSGA2Survival};
use crate::algorithms::nsga2::{CoreNSGA2, CoreNSGA2Arg};
use crate::algorithms::Algorithm;
use crate::core::{OError, Population, Problem};
use crate::operators::{PolynomialMutationArgs, SimulatedBinaryCrossoverArgs};
#[cfg(feature = "python")]
use pyo3::prelude::*;
#[as_algorithm_args]
#[cfg_attr(feature = "python", pyclass(get_all))]
pub struct RNSGA2Arg {
pub number_of_individuals: usize,
pub reference_points: Vec<Vec<f64>>,
pub epsilon: u64,
pub weights: Option<Vec<f64>>,
pub crossover_operator_options: Option<SimulatedBinaryCrossoverArgs>,
pub mutation_operator_options: Option<PolynomialMutationArgs>,
pub resume_from_file: Option<PathBuf>,
pub seed: Option<u64>,
}
#[cfg(feature = "python")]
#[pymethods]
impl RNSGA2Arg {
#[new]
#[pyo3(signature = (number_of_individuals, reference_points, epsilon, stopping_condition, weights=None, crossover_operator_options=None, mutation_operator_options=None, resume_from_file=None, parallel=None, export_history=None, seed=None))]
fn py_new(
number_of_individuals: usize,
reference_points: Vec<Vec<f64>>,
epsilon: f64,
stopping_condition: StoppingCondition,
weights: Option<Vec<f64>>,
crossover_operator_options: Option<SimulatedBinaryCrossoverArgs>,
mutation_operator_options: Option<PolynomialMutationArgs>,
resume_from_file: Option<PathBuf>,
parallel: Option<bool>,
export_history: Option<ExportHistory>,
seed: Option<u64>,
) -> PyResult<Self> {
Ok(RNSGA2Arg {
number_of_individuals,
epsilon,
weights,
reference_points,
crossover_operator_options,
mutation_operator_options,
resume_from_file,
seed,
stopping_condition,
parallel,
export_history,
})
}
pub fn __repr__(&self) -> PyResult<String> {
Ok(format!(
"RNSGA2Arg(number_of_individuals={}, stopping_condition={})",
self.number_of_individuals, self.stopping_condition
))
}
fn __str__(&self) -> String {
self.__repr__().unwrap()
}
}
pub struct RNSGA2 {
reference_points: Vec<Vec<f64>>,
core: CoreNSGA2,
args: RNSGA2Arg,
}
impl RNSGA2 {
pub fn new(problem: Problem, options: RNSGA2Arg) -> Result<Self, OError> {
let name = "R-NSGA2".to_string();
let nsga2_options = options.clone();
if options.reference_points.is_empty() {
return Err(OError::AlgorithmInit(
name,
"The vector of reference point is empty".to_string(),
));
}
if options.reference_points.get(0).unwrap().len() != problem.number_of_objectives() {
return Err(OError::AlgorithmInit(
name,
format!(
"The number of coordinates of the reference points must match the number of objectives ({})",
problem.number_of_objectives()
),
));
}
if options.epsilon == 0 {
return Err(OError::AlgorithmInit(
name,
"Epsilon must be strictly larger than zero".to_string(),
));
}
let weights = options.weights.unwrap_or(vec![
1.0 / problem.number_of_objectives() as f64;
problem.number_of_objectives()
]);
if weights.len() == problem.number_of_objectives() {
return Err(OError::AlgorithmInit(
name,
format!(
"The number of weights coordinates must match the number of objectives ({})",
problem.number_of_objectives()
),
));
}
let core_options = CoreNSGA2Arg {
stopping_condition: options.stopping_condition,
number_of_individuals: options.number_of_individuals,
crossover_operator_options: options.crossover_operator_options,
mutation_operator_options: options.mutation_operator_options,
resume_from_file: options.resume_from_file,
seed: options.seed,
survival_operator: NSGA2Survival::Epsilon(EpsilonData {
epsilon: nsga2_options.epsilon.clone(),
reference_points: nsga2_options.reference_points.clone(),
weights: weights.clone(),
}),
crowding_operator: CrowdingDistanceOperator::Rank(DistanceData {
reference_points: nsga2_options.reference_points.clone(),
weights,
}),
parallel: options.parallel,
export_history: options.export_history,
};
Ok(Self {
reference_points: options.reference_points,
args: nsga2_options,
core: CoreNSGA2::new(problem, core_options)?,
})
}
}
impl Algorithm<RNSGA2Arg> for RNSGA2 {
fn initialise(&mut self) -> Result<(), OError> {
self.core.initialise()
}
fn evolve(&mut self) -> Result<(), OError> {
self.core.evolve()
}
fn stopping_condition(&self) -> &StoppingCondition {
&self.core.stopping_condition
}
fn name(&self) -> String {
"R-NSGA2".to_string()
}
fn problem(&self) -> Arc<Problem> {
self.core.problem.clone()
}
fn population(&self) -> &Population {
&self.core.population
}
fn export_history(&self) -> Option<&ExportHistory> {
self.core.export_history.as_ref()
}
fn generation(&self) -> u32 {
self.core.generation
}
fn number_of_function_evaluations(&self) -> u32 {
self.core.nfe
}
fn algorithm_options(&self) -> RNSGA2Arg {
self.args.clone()
}
fn start_time(&self) -> &Instant {
&self.core.start_time
}
}
impl RNSGA2 {
pub fn reference_points(&self) -> Vec<Vec<f64>> {
self.args.reference_points.clone()
}
}
impl Display for RNSGA2 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name().as_str())
}
}