Skip to main content

sklears_feature_selection/genetic_optimization/
fairnessawareobjective_traits.rs

1//! # FairnessAwareObjective - Trait Implementations
2//!
3//! This module contains trait implementations for `FairnessAwareObjective`.
4//!
5//! ## Implemented Traits
6//!
7//! - `ObjectiveFunction`
8//!
9//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
10
11use super::functions::*;
12use super::types::*;
13use scirs2_core::ndarray::{Array1, Array2};
14use sklears_core::error::{Result as SklResult, SklearsError};
15
16impl ObjectiveFunction for FairnessAwareObjective {
17    fn evaluate(
18        &self,
19        x: &Array2<f64>,
20        y_classif: Option<&Array1<i32>>,
21        y_regression: Option<&Array1<f64>>,
22        feature_mask: &[bool],
23    ) -> SklResult<f64> {
24        let selected_indices: Vec<usize> = feature_mask
25            .iter()
26            .enumerate()
27            .filter_map(|(i, &selected)| if selected { Some(i) } else { None })
28            .collect();
29        if selected_indices.is_empty() {
30            return Ok(1.0);
31        }
32        let mut total_performance = 0.0;
33        if let Some(y) = y_classif {
34            for &idx in &selected_indices {
35                let feature = x.column(idx).to_owned();
36                let y_float = y.mapv(|v| v as f64);
37                let correlation = compute_pearson_correlation(&feature, &y_float).abs();
38                total_performance += correlation;
39            }
40        } else if let Some(y) = y_regression {
41            for &idx in &selected_indices {
42                let feature = x.column(idx).to_owned();
43                let correlation = compute_pearson_correlation(&feature, y).abs();
44                total_performance += correlation;
45            }
46        } else {
47            return Err(SklearsError::InvalidInput(
48                "Need either classification or regression target".to_string(),
49            ));
50        }
51        let avg_performance = total_performance / selected_indices.len() as f64;
52        let fairness_score = match self.fairness_metric {
53            FairnessMetric::DemographicParity | FairnessMetric::StatisticalParityDifference => {
54                let protected_float = self.protected_groups.mapv(|v| v as f64);
55                let mut total_bias = 0.0;
56                for &idx in &selected_indices {
57                    let feature = x.column(idx).to_owned();
58                    let correlation = compute_pearson_correlation(&feature, &protected_float).abs();
59                    total_bias += correlation;
60                }
61                let avg_bias = total_bias / selected_indices.len() as f64;
62                1.0 - avg_bias
63            }
64            _ => {
65                let protected_float = self.protected_groups.mapv(|v| v as f64);
66                let mut total_bias = 0.0;
67                for &idx in &selected_indices {
68                    let feature = x.column(idx).to_owned();
69                    let correlation = compute_pearson_correlation(&feature, &protected_float).abs();
70                    total_bias += correlation;
71                }
72                let avg_bias = total_bias / selected_indices.len() as f64;
73                1.0 - avg_bias
74            }
75        };
76        let fairness_aware_score =
77            (1.0 - self.fairness_weight) * avg_performance + self.fairness_weight * fairness_score;
78        Ok(fairness_aware_score)
79    }
80    fn name(&self) -> &str {
81        "fairness_aware"
82    }
83    fn clone_box(&self) -> Box<dyn ObjectiveFunction> {
84        Box::new(self.clone())
85    }
86}