use crate::core::{DataValue, Individual, OError};
use crate::operators::{BinaryComparisonOperator, ParetoConstrainedDominance, PreferredSolution};
#[derive(Debug)]
pub struct NonDominatedSortResults {
pub fronts: Vec<Vec<Individual>>,
pub front_indexes: Vec<Vec<usize>>,
pub domination_counter: Vec<usize>,
}
const RANK_KEY: &str = "rank";
pub fn fast_non_dominated_sort(
individuals: &mut [Individual],
first_front_only: bool,
) -> Result<NonDominatedSortResults, OError> {
if individuals.len() < 2 {
return Err(OError::SurvivalOperator(
"fast non-dominated sort".to_string(),
format!(
"At least 2 individuals are needed for sorting, but {} given",
individuals.len()
),
));
}
let mut dominated_solutions: Vec<Vec<usize>> = individuals.iter().map(|_| Vec::new()).collect();
let mut domination_counter: Vec<usize> = individuals.iter().map(|_| 0).collect();
let mut current_front: Vec<usize> = Vec::new();
let mut all_fronts: Vec<Vec<usize>> = Vec::new();
for pi in 0..individuals.len() {
for qi in pi..individuals.len() {
match ParetoConstrainedDominance::compare(&individuals[pi], &individuals[qi])? {
PreferredSolution::First => {
dominated_solutions[pi].push(qi);
domination_counter[qi] += 1;
}
PreferredSolution::Second => {
dominated_solutions[qi].push(pi);
domination_counter[pi] += 1;
}
PreferredSolution::MutuallyPreferred => {
}
}
}
if domination_counter[pi] == 0 {
current_front.push(pi);
individuals[pi].set_data(RANK_KEY, DataValue::Integer(1));
}
}
if first_front_only {
let first_front = current_front
.iter()
.map(|idx| individuals[*idx].clone())
.collect();
return Ok(NonDominatedSortResults {
fronts: vec![first_front],
front_indexes: vec![current_front],
domination_counter,
});
}
all_fronts.push(current_front.clone());
let e_domination_counter = domination_counter.clone();
let mut i = 1;
loop {
let mut next_front: Vec<usize> = Vec::new();
for pi in current_front.iter() {
for qi in dominated_solutions[*pi].iter() {
domination_counter[*qi] -= 1;
if domination_counter[*qi] == 0 {
next_front.push(*qi);
individuals[*qi].set_data(RANK_KEY, DataValue::Integer(i + 1));
}
}
}
i += 1;
if next_front.is_empty() {
break;
}
all_fronts.push(next_front.clone());
current_front = next_front;
}
let mut fronts: Vec<Vec<Individual>> = Vec::new();
for front in &all_fronts {
let mut sub_front: Vec<Individual> = Vec::new();
for i in front {
sub_front.push(individuals[*i].clone());
}
fronts.push(sub_front);
}
Ok(NonDominatedSortResults {
fronts,
front_indexes: all_fronts,
domination_counter: e_domination_counter,
})
}
#[cfg(test)]
mod test {
use crate::core::test_utils::individuals_from_obj_values_dummy;
use crate::core::{DataValue, ObjectiveDirection};
use crate::utils::fast_non_dominated_sort;
use crate::utils::fast_non_dominated_sort::RANK_KEY;
#[test]
fn test_sorting_2obj() {
let objectives = vec![
vec![1.1, 8.1],
vec![2.1, 6.1],
vec![3.1, 4.1],
vec![3.1, 7.1],
vec![5.1, 3.1],
vec![5.1, 5.1],
vec![7.1, 7.1],
vec![8.1, 2.1],
vec![10.1, 6.1],
vec![11.1, 1.1],
vec![11.1, 3.1],
];
let mut individuals = individuals_from_obj_values_dummy(
&objectives,
&[ObjectiveDirection::Minimise, ObjectiveDirection::Minimise],
None,
);
let result = fast_non_dominated_sort(&mut individuals, false).unwrap();
let expected_first = vec![0, 1, 2, 4, 7, 9];
assert_eq!(result.front_indexes[0], expected_first);
for idx in &expected_first {
assert_eq!(
individuals[*idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(1)
);
}
let expected_second = vec![3, 5, 10];
assert_eq!(result.front_indexes[1], expected_second);
for idx in expected_second {
assert_eq!(
individuals[idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(2)
);
}
let expected_third = vec![6, 8];
assert_eq!(result.front_indexes[2], expected_third);
for idx in expected_third {
assert_eq!(
individuals[idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(3)
);
}
for idx in expected_first {
assert_eq!(result.domination_counter[idx], 0);
}
assert_eq!(result.domination_counter[5], 2);
assert_eq!(result.domination_counter[8], 5);
assert_eq!(result.domination_counter[3], 2);
}
#[test]
fn test_sorting_2obj_max_obj1() {
let objectives = vec![
vec![11.1, 8.1],
vec![8.1, 6.1],
vec![5.1, 4.1],
vec![3.1, 3.1],
vec![2.1, 2.1],
vec![1.1, 1.1],
vec![0.0, 5.1],
];
let mut individuals = individuals_from_obj_values_dummy(
&objectives,
&[ObjectiveDirection::Maximise, ObjectiveDirection::Minimise],
None,
);
let result = fast_non_dominated_sort(&mut individuals, false).unwrap();
let expected_first = (0..=5).collect::<Vec<usize>>();
assert_eq!(result.front_indexes[0], expected_first);
for idx in &expected_first {
assert_eq!(
individuals[*idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(1)
);
}
let expected_second = vec![6];
assert_eq!(result.front_indexes[1], expected_second);
}
#[test]
fn test_sorting_2obj_max_obj2() {
let objectives = vec![
vec![11.1, 8.1],
vec![8.1, 6.1],
vec![5.1, 4.1],
vec![3.1, 3.1],
vec![2.1, 2.1],
vec![1.1, 1.1],
vec![0.0, 5.1],
];
let mut individuals = individuals_from_obj_values_dummy(
&objectives,
&[ObjectiveDirection::Minimise, ObjectiveDirection::Maximise],
None,
);
let result = fast_non_dominated_sort(&mut individuals, false).unwrap();
let expected_first = vec![0, 1, 6];
assert_eq!(result.front_indexes[0], expected_first);
for idx in &expected_first {
assert_eq!(
individuals[*idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(1)
);
}
let expected_second = vec![2, 3, 4, 5];
assert_eq!(result.front_indexes[1], expected_second);
}
#[test]
fn test_sorting_3obj() {
let objectives = vec![
vec![2.1, 3.1, 4.1],
vec![-1.1, 4.1, 8.1],
vec![0.1, -1.1, -2.1],
vec![0.1, 0.1, 0.1],
];
let mut individuals = individuals_from_obj_values_dummy(
&objectives,
&[
ObjectiveDirection::Minimise,
ObjectiveDirection::Minimise,
ObjectiveDirection::Minimise,
],
None,
);
let result = fast_non_dominated_sort(&mut individuals, false).unwrap();
let expected_first = vec![1, 2];
assert_eq!(result.front_indexes[0], expected_first);
for idx in &expected_first {
assert_eq!(
individuals[*idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(1)
);
}
let expected_second = vec![3];
assert_eq!(result.front_indexes[1], expected_second);
for idx in expected_second {
assert_eq!(
individuals[idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(2)
);
}
let expected_third = vec![0];
assert_eq!(result.front_indexes[2], expected_third);
for idx in expected_third {
assert_eq!(
individuals[idx].get_data(RANK_KEY).unwrap(),
DataValue::Integer(3)
);
}
for idx in expected_first {
assert_eq!(result.domination_counter[idx], 0);
}
assert_eq!(result.domination_counter[0], 2);
assert_eq!(result.domination_counter[3], 1);
}
}