use super::types::*;
use crate::base::SelectorMixin;
use scirs2_core::ndarray::{Array1, Array2};
use sklears_core::{
error::{validate, Result as SklResult, SklearsError},
traits::{Fit, Trained, Transform, Untrained},
};
use std::marker::PhantomData;
impl Default for MultiObjectiveFeatureSelector<Untrained> {
fn default() -> Self {
Self::new()
}
}
impl Fit<Array2<f64>, Array1<i32>> for MultiObjectiveFeatureSelector<Untrained> {
type Fitted = MultiObjectiveFeatureSelector<Trained>;
fn fit(self, x: &Array2<f64>, y: &Array1<i32>) -> SklResult<Self::Fitted> {
validate::check_consistent_length(x, y)?;
let n_features = x.ncols();
if self.objectives.is_empty() {
return Err(SklearsError::InvalidInput(
"No objectives added".to_string(),
));
}
let (pareto_front, best_solutions, objective_values) =
self.optimize(x, Some(y), None, n_features)?;
Ok(MultiObjectiveFeatureSelector {
objectives: self.objectives,
optimization_method: self.optimization_method,
population_size: self.population_size,
n_generations: self.n_generations,
crossover_prob: self.crossover_prob,
mutation_prob: self.mutation_prob,
random_state: self.random_state,
state: PhantomData,
pareto_front_: Some(pareto_front),
best_solutions_: Some(best_solutions),
objective_values_: Some(objective_values),
n_features_: Some(n_features),
})
}
}
impl Fit<Array2<f64>, Array1<f64>> for MultiObjectiveFeatureSelector<Untrained> {
type Fitted = MultiObjectiveFeatureSelector<Trained>;
fn fit(self, x: &Array2<f64>, y: &Array1<f64>) -> SklResult<Self::Fitted> {
validate::check_consistent_length(x, y)?;
let n_features = x.ncols();
if self.objectives.is_empty() {
return Err(SklearsError::InvalidInput(
"No objectives added".to_string(),
));
}
let (pareto_front, best_solutions, objective_values) =
self.optimize(x, None, Some(y), n_features)?;
Ok(MultiObjectiveFeatureSelector {
objectives: self.objectives,
optimization_method: self.optimization_method,
population_size: self.population_size,
n_generations: self.n_generations,
crossover_prob: self.crossover_prob,
mutation_prob: self.mutation_prob,
random_state: self.random_state,
state: PhantomData,
pareto_front_: Some(pareto_front),
best_solutions_: Some(best_solutions),
objective_values_: Some(objective_values),
n_features_: Some(n_features),
})
}
}
impl Transform<Array2<f64>> for MultiObjectiveFeatureSelector<Trained> {
fn transform(&self, x: &Array2<f64>) -> SklResult<Array2<f64>> {
let n_features = self.n_features_.expect("operation should succeed");
if x.ncols() != n_features {
return Err(SklearsError::InvalidInput(format!(
"Expected {} features, got {}",
n_features,
x.ncols()
)));
}
let selected = self.balanced_solution().unwrap_or(&[]);
let n_samples = x.nrows();
let n_selected = selected.len();
let mut x_new = Array2::<f64>::zeros((n_samples, n_selected));
for (new_idx, &old_idx) in selected.iter().enumerate() {
x_new.column_mut(new_idx).assign(&x.column(old_idx));
}
Ok(x_new)
}
}
impl SelectorMixin for MultiObjectiveFeatureSelector<Trained> {
fn get_support(&self) -> SklResult<Array1<bool>> {
let selected_features = self.balanced_solution().unwrap_or(&[]);
let n_features = self.n_features_.expect("operation should succeed");
let mut support = vec![false; n_features];
for &idx in selected_features {
support[idx] = true;
}
Ok(Array1::from(support))
}
fn transform_features(&self, indices: &[usize]) -> SklResult<Vec<usize>> {
let selected = self.balanced_solution().unwrap_or(&[]);
Ok(indices
.iter()
.filter_map(|&idx| selected.iter().position(|&f| f == idx))
.collect())
}
}