use std::cmp::Ordering;
pub mod error;
pub mod models;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
pub use error::ClevelError;
pub use models::{Model, NormalizedModel};
trait FloatIterExt {
fn float_min(&mut self) -> f64;
fn float_max(&mut self) -> f64;
}
impl<T> FloatIterExt for T
where
T: Iterator<Item = f64>,
{
fn float_max(&mut self) -> f64 {
self.fold(std::f64::NAN, f64::max)
}
fn float_min(&mut self) -> f64 {
self.fold(std::f64::NAN, f64::min)
}
}
lazy_static::lazy_static! {
pub static ref VERSION: &'static str = {
let mut version = env!("CARGO_PKG_VERSION").to_string();
if let Some(v) = option_env!("GIT_HASH") {
version.push_str("-");
version.push_str(v);
}
if cfg!(debug_assertions) {
version.push_str("-debug");
}
Box::leak(Box::new(version))
};
}
fn phase_data(crisp: u32, a: u32, b: u32) -> Option<f64> {
match Some(f64::default()) {
Some(_) if crisp <= a => Some(0.0),
Some(_) if a < crisp && crisp <= (a + b) / 2 => {
let first = (crisp - a) as f64;
let second = (b - a) as f64;
return Some(2.0 * f64::powf(first / second, 2.0));
}
Some(_) if (a + b) / 2 < crisp && crisp < b => {
let first = (b - crisp) as f64;
let second = (b - a) as f64;
return Some(1.0 - 2.0 * f64::powf(first / second, 2.0));
}
Some(_) if crisp >= b => Some(1.0),
_ => None,
}
}
fn phase_data_vec(models: &Vec<Model>) -> Vec<NormalizedModel> {
let mut normalized_models: Vec<NormalizedModel> = vec![];
for item in models {
let phased_crisps = item
.crisps
.iter()
.map(|num| phase_data(*num, item.a, item.b).unwrap())
.collect::<Vec<f64>>();
let phased_t = phase_data(item.t, item.a, item.b).unwrap();
normalized_models.push(NormalizedModel {
phased_crisps,
phased_t,
})
}
normalized_models
}
fn weighting_normalization(models: &Vec<Model>) -> Vec<f64> {
let pvec = models.iter().map(|model| model.p).collect::<Vec<u32>>();
let sum_of_pvec = pvec.iter().sum::<u32>();
pvec.iter()
.map(|p| *p as f64 / sum_of_pvec as f64)
.collect::<Vec<f64>>()
}
fn calc_desired_value(crisp: f64, min_crisp: f64, max_crisp: f64, t: f64) -> f64 {
let first = (t - crisp).abs();
let second: Vec<f64> = vec![t - min_crisp, max_crisp - t];
let max_second = second.iter().cloned().float_max();
1.0 - (first / max_second)
}
fn desired_values(models: &Vec<Model>) -> Vec<Vec<f64>> {
let mut vec_of_vec: Vec<Vec<f64>> = vec![];
let normalized_models = phase_data_vec(models);
for nm in normalized_models {
let vv: Vec<f64> = nm
.phased_crisps
.iter()
.map(|num: &f64| {
let max_val = nm.phased_crisps.iter().cloned().float_max();
let min_val = nm.phased_crisps.iter().cloned().float_min();
calc_desired_value(*num, min_val, max_val, nm.phased_t)
})
.collect();
vec_of_vec.push(vv)
}
vec_of_vec
}
fn simplify_desired_values(dv: Vec<Vec<f64>>) -> Vec<f64> {
let mut new_dv: Vec<f64> = vec![];
for v in dv {
for i in v {
new_dv.push(i)
}
}
new_dv
}
fn initialize_matrix(models: &Vec<Model>) -> simple_matrix::Matrix<f64> {
let dv = desired_values(&models);
let simple_dv = simplify_desired_values(dv);
let rows = models.len();
let cols = models[0].crisps.len();
let matrix: simple_matrix::Matrix<f64> =
simple_matrix::Matrix::from_iter(rows, cols, simple_dv.iter().cloned());
matrix
}
pub fn aggregated_score(models: &Vec<Model>) -> Vec<f64> {
let weights = weighting_normalization(&models);
let matrix = initialize_matrix(&models);
let mut temp: Vec<f64> = vec![];
for col in 0..matrix.cols() {
let get_col = matrix.get_col(col).unwrap().collect::<Vec<&f64>>();
let mut res: Vec<f64> = vec![];
for row in 0..matrix.rows() {
res.push(*get_col[row] * weights[row])
}
temp.push(res.iter().sum())
}
temp
}
pub fn find_head_from_models(models: Vec<Model>) -> (String, f64) {
let aggregation_estimates = aggregated_score(&models);
let maxf = aggregation_estimates.iter().cloned().float_max();
let mut expert: String = String::new();
for (index, item) in aggregation_estimates.iter().enumerate() {
if *item == maxf {
expert = format!("Expert {}", index + 1);
}
}
(expert, maxf)
}
pub fn ranking_of_experts(models: &Vec<Model>) -> Vec<f64> {
let aggregation_estimates = aggregated_score(&models);
let mut sorted_aggregation_estimates = aggregation_estimates.clone();
sorted_aggregation_estimates.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
sorted_aggregation_estimates.iter().rev().cloned().collect()
}