pub use algebra::{
dot_product, perpendicular_distance, solve_linear_system, vector_magnitude,
LinearSolverTolerance,
};
pub use fast_non_dominated_sort::{fast_non_dominated_sort, NonDominatedSortResults};
pub use reference_points::{DasDarren1998, NumberOfPartitions, TwoLayerPartitions};
use std::cmp::Ordering;
use std::collections::{BTreeSet, HashMap};
use std::hash::{BuildHasher, Hash};
use crate::core::OError;
mod algebra;
mod fast_non_dominated_sort;
mod reference_points;
#[derive(PartialEq)]
pub enum Sort {
Ascending,
Descending,
}
pub fn argsort(data: &[f64], sort_type: Sort) -> Vec<usize> {
let mut indices = (0..data.len()).collect::<Vec<_>>();
indices.sort_by(|a, b| data[*a].total_cmp(&data[*b]));
if sort_type == Sort::Descending {
indices.reverse();
}
indices
}
pub fn vector_min(v: &[f64]) -> Result<f64, OError> {
Ok(*v
.iter()
.min_by(|a, b| a.total_cmp(b))
.ok_or(OError::Generic(
"Cannot calculate vector min value".to_string(),
))?)
}
pub fn vector_max(v: &[f64]) -> Result<f64, OError> {
Ok(*v
.iter()
.max_by(|a, b| a.total_cmp(b))
.ok_or(OError::Generic(
"Cannot calculate vector max value".to_string(),
))?)
}
pub fn all_close(a: &[f64], b: &[f64], r_tol: Option<f64>, a_tol: Option<f64>) -> bool {
let r_tol = r_tol.unwrap_or(1e-05);
let a_tol = a_tol.unwrap_or(1e-08);
a.iter()
.zip(b)
.any(|(v1, v2)| (v1 - v2).abs() <= (a_tol + r_tol * v2.abs()))
}
pub fn argmin(vector: &[f64]) -> (usize, f64) {
let mut min_value = f64::INFINITY;
let mut min_index: usize = 0;
for (index, value) in vector.iter().enumerate() {
if *value < min_value {
min_value = *value;
min_index = index;
}
}
(min_index, min_value)
}
pub fn hasmap_eq_with_nans<K, S>(a: &HashMap<K, f64, S>, b: &HashMap<K, f64, S>) -> bool
where
K: Eq + Hash,
S: BuildHasher,
{
if a.len() != b.len() {
return false;
}
a.iter().all(|(key, value)| {
b.get(key)
.map_or(false, |v| (v.is_nan() && value.is_nan()) || (*value == *v))
})
}
pub fn argmin_by<I, F>(vector: &[I], f: F) -> Option<(usize, &I)>
where
I: Sized,
F: FnMut((usize, &I)) -> f64,
{
#[inline]
fn key<T>(
mut f: impl FnMut((usize, &T)) -> f64,
) -> impl FnMut((usize, &T)) -> (f64, (usize, &T)) {
move |(index, it)| (f((index, it)), (index, it))
}
#[inline]
fn compare<T>((x_p, _): &(f64, T), (y_p, _): &(f64, T)) -> Ordering {
x_p.total_cmp(y_p)
}
let (_, (index, x)) = vector.iter().enumerate().map(key(f)).min_by(compare)?;
Some((index, x))
}
pub fn index_of<I: Sized + PartialEq>(vector: &[I], item: &I) -> Option<usize> {
vector.iter().position(|i| i == item)
}
pub fn has_unique_elements_by_key<I: Sized, J: Sized + Ord, F: Fn(&I) -> J>(
vector: &[I],
f: F,
) -> bool {
let values: Vec<J> = vector.iter().map(f).collect();
let mut unique = BTreeSet::new();
values.iter().all(move |x| unique.insert(x))
}
#[cfg(test)]
mod test {
use crate::utils::{argmin_by, argsort, Sort};
#[test]
fn test_argsort() {
let vec = vec![99.0, 11.0, 456.2, 19.0, 0.5];
assert_eq!(argsort(&vec, Sort::Ascending), vec![4, 1, 3, 0, 2]);
assert_eq!(argsort(&vec, Sort::Descending), vec![2, 0, 3, 1, 4]);
}
#[test]
fn test_armin_by() {
#[derive(PartialEq, Debug)]
struct A {
distance: f64,
other: f64,
}
let values = [10.0, -99.0, 55.2, -1.0];
let a_values: Vec<A> = values
.iter()
.map(|v| A {
distance: *v,
other: 0.0,
})
.collect();
let (min_index, min_distance) = argmin_by(&a_values, |(_, a)| a.distance).unwrap();
assert_eq!(min_index, 1);
assert_eq!(min_distance, &a_values[1]);
}
}