use std::fs::read_to_string;
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use float_cmp::approx_eq;
use crate::core::builtin_problems::ZTD1Problem;
use crate::core::{
utils, BoundedNumber, Individual, Objective, ObjectiveDirection, Problem, VariableType,
VariableValue,
};
pub(crate) fn assert_approx_array_eq(
calculated_values: &[f64],
expected_values: &[f64],
epsilon: Option<f64>,
) {
let epsilon = epsilon.unwrap_or(0.00001);
for (i, (calculated, expected)) in calculated_values.iter().zip(expected_values).enumerate() {
if !approx_eq!(f64, *calculated, *expected, epsilon = epsilon) {
panic!(
r#"assertion failed on item #{i:?}
actual: `{calculated:?}`,
expected: `{expected:?}`"#,
)
}
}
}
pub(crate) fn check_value_in_range(vector: &[f64], range: &Range<f64>) -> Vec<f64> {
vector
.iter()
.filter_map(|v| if !range.contains(v) { Some(*v) } else { None })
.collect()
}
pub(crate) fn check_exact_value(
vector: &[f64],
strict_range: &Range<f64>,
loose_range: &Range<f64>,
max_outside_strict_range: usize,
) -> (Vec<f64>, Range<f64>, String) {
if strict_range == loose_range {
panic!("Bounds are identical");
}
let v_outside = check_value_in_range(vector, strict_range);
if v_outside.len() > max_outside_strict_range {
(v_outside, strict_range.clone(), "strict".to_string())
} else {
let v_loose_outside = check_value_in_range(&v_outside, loose_range);
(v_loose_outside, loose_range.clone(), "loose".to_string())
}
}
pub(crate) fn read_csv_test_file(
file_name: &PathBuf,
skip_first_col: Option<bool>,
) -> Vec<Vec<f64>> {
let skip_first_col = skip_first_col.unwrap_or(true);
let mut values: Vec<Vec<f64>> = vec![];
for (li, line) in read_to_string(file_name)
.unwrap_or_else(|_| panic!("Cannot find {:?}", file_name))
.lines()
.enumerate()
{
if li == 0 {
continue;
}
let point = line
.split(',')
.skip(if skip_first_col { 1 } else { 0 })
.map(|v| v.to_string().parse::<f64>())
.collect::<Result<Vec<f64>, _>>()
.unwrap();
values.push(point);
}
values
}
pub(crate) fn individuals_from_obj_values_dummy(
objective_values: &[Vec<f64>],
objective_direction: &[ObjectiveDirection],
variable_values: Option<&[Vec<f64>]>,
) -> Vec<Individual> {
if objective_values.first().unwrap().len() != objective_direction.len() {
panic!("The objective values must match the direction vector length")
}
let mut objectives = Vec::new();
for (i, direction) in objective_direction.iter().enumerate() {
objectives.push(Objective::new(format!("obj{i}").as_str(), *direction));
}
let variables = if let Some(variable_values) = variable_values {
(0..variable_values.len())
.map(|i| {
VariableType::Real(BoundedNumber::new(format!("X{i}").as_str(), 0.0, 2.0).unwrap())
})
.collect()
} else {
vec![VariableType::Real(
BoundedNumber::new("X", 0.0, 2.0).unwrap(),
)]
};
let problem =
Arc::new(Problem::new(objectives, variables, None, utils::dummy_evaluator()).unwrap());
let mut individuals: Vec<Individual> = Vec::new();
for (ind_idx, data) in objective_values.iter().enumerate() {
let mut individual = Individual::new(problem.clone());
for (oi, obj_value) in data.iter().enumerate() {
individual
.update_objective(format!("obj{oi}").as_str(), *obj_value)
.unwrap();
}
if let Some(variable_values) = variable_values {
for (vi, var_value) in variable_values[ind_idx].iter().enumerate() {
individual
.update_variable(format!("X{vi}").as_str(), VariableValue::Real(*var_value))
.unwrap();
}
}
individuals.push(individual);
}
individuals
}
pub(crate) fn individuals_from_obj_values_ztd1(obj_values: &[Vec<f64>]) -> Vec<Individual> {
let problem = Arc::new(ZTD1Problem::create(obj_values.len()).unwrap());
let mut individuals = vec![];
for value in obj_values {
let mut i = Individual::new(problem.clone());
i.update_objective("f1", value[0]).unwrap();
i.update_objective("f2", value[1]).unwrap();
individuals.push(i);
}
individuals
}