1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
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() }