1use std::cmp::Ordering;
2
3pub mod error;
4pub mod models;
5
6#[cfg(target_arch = "wasm32")]
7pub mod wasm;
8
9pub use error::ClevelError;
10pub use models::{Model, NormalizedModel};
11
12trait FloatIterExt {
13 fn float_min(&mut self) -> f64;
14 fn float_max(&mut self) -> f64;
15}
16
17impl<T> FloatIterExt for T
18where
19 T: Iterator<Item = f64>,
20{
21 fn float_max(&mut self) -> f64 {
22 self.fold(std::f64::NAN, f64::max)
23 }
24
25 fn float_min(&mut self) -> f64 {
26 self.fold(std::f64::NAN, f64::min)
27 }
28}
29
30lazy_static::lazy_static! {
31 pub static ref VERSION: &'static str = {
32 let mut version = env!("CARGO_PKG_VERSION").to_string();
33 if let Some(v) = option_env!("GIT_HASH") {
34 version.push_str("-");
35 version.push_str(v);
36 }
37 if cfg!(debug_assertions) {
38 version.push_str("-debug");
39 }
40 Box::leak(Box::new(version))
41 };
42}
43
44fn phase_data(crisp: u32, a: u32, b: u32) -> Option<f64> {
45 match Some(f64::default()) {
46 Some(_) if crisp <= a => Some(0.0),
47 Some(_) if a < crisp && crisp <= (a + b) / 2 => {
48 let first = (crisp - a) as f64;
49 let second = (b - a) as f64;
50 return Some(2.0 * f64::powf(first / second, 2.0));
51 }
52 Some(_) if (a + b) / 2 < crisp && crisp < b => {
53 let first = (b - crisp) as f64;
54 let second = (b - a) as f64;
55 return Some(1.0 - 2.0 * f64::powf(first / second, 2.0));
56 }
57 Some(_) if crisp >= b => Some(1.0),
58 _ => None,
59 }
60}
61
62fn phase_data_vec(models: &Vec<Model>) -> Vec<NormalizedModel> {
63 let mut normalized_models: Vec<NormalizedModel> = vec![];
64 for item in models {
65 let phased_crisps = item
66 .crisps
67 .iter()
68 .map(|num| phase_data(*num, item.a, item.b).unwrap())
69 .collect::<Vec<f64>>();
70 let phased_t = phase_data(item.t, item.a, item.b).unwrap();
71 normalized_models.push(NormalizedModel {
72 phased_crisps,
73 phased_t,
74 })
75 }
76 normalized_models
77}
78
79fn weighting_normalization(models: &Vec<Model>) -> Vec<f64> {
80 let pvec = models.iter().map(|model| model.p).collect::<Vec<u32>>();
81 let sum_of_pvec = pvec.iter().sum::<u32>();
82 pvec.iter()
83 .map(|p| *p as f64 / sum_of_pvec as f64)
84 .collect::<Vec<f64>>()
85}
86
87fn calc_desired_value(crisp: f64, min_crisp: f64, max_crisp: f64, t: f64) -> f64 {
88 let first = (t - crisp).abs();
89 let second: Vec<f64> = vec![t - min_crisp, max_crisp - t];
90 let max_second = second.iter().cloned().float_max();
91 1.0 - (first / max_second)
92}
93
94fn desired_values(models: &Vec<Model>) -> Vec<Vec<f64>> {
95 let mut vec_of_vec: Vec<Vec<f64>> = vec![];
96 let normalized_models = phase_data_vec(models);
97 for nm in normalized_models {
98 let vv: Vec<f64> = nm
99 .phased_crisps
100 .iter()
101 .map(|num: &f64| {
102 let max_val = nm.phased_crisps.iter().cloned().float_max();
103 let min_val = nm.phased_crisps.iter().cloned().float_min();
104 calc_desired_value(*num, min_val, max_val, nm.phased_t)
105 })
106 .collect();
107 vec_of_vec.push(vv)
108 }
109 vec_of_vec
110}
111
112fn simplify_desired_values(dv: Vec<Vec<f64>>) -> Vec<f64> {
113 let mut new_dv: Vec<f64> = vec![];
114
115 for v in dv {
116 for i in v {
117 new_dv.push(i)
118 }
119 }
120
121 new_dv
122}
123
124fn initialize_matrix(models: &Vec<Model>) -> simple_matrix::Matrix<f64> {
125 let dv = desired_values(&models);
126 let simple_dv = simplify_desired_values(dv);
127
128 let rows = models.len();
129 let cols = models[0].crisps.len();
130
131 let matrix: simple_matrix::Matrix<f64> =
132 simple_matrix::Matrix::from_iter(rows, cols, simple_dv.iter().cloned());
133
134 matrix
135}
136
137pub fn aggregated_score(models: &Vec<Model>) -> Vec<f64> {
138 let weights = weighting_normalization(&models);
139 let matrix = initialize_matrix(&models);
140
141 let mut temp: Vec<f64> = vec![];
142
143 for col in 0..matrix.cols() {
144 let get_col = matrix.get_col(col).unwrap().collect::<Vec<&f64>>();
145 let mut res: Vec<f64> = vec![];
146
147 for row in 0..matrix.rows() {
148 res.push(*get_col[row] * weights[row])
149 }
150
151 temp.push(res.iter().sum())
152 }
153
154 temp
155}
156
157pub fn find_head_from_models(models: Vec<Model>) -> (String, f64) {
158 let aggregation_estimates = aggregated_score(&models);
159 let maxf = aggregation_estimates.iter().cloned().float_max();
160 let mut expert: String = String::new();
161
162 for (index, item) in aggregation_estimates.iter().enumerate() {
163 if *item == maxf {
164 expert = format!("Expert {}", index + 1);
165 }
166 }
167
168 (expert, maxf)
169}
170
171pub fn ranking_of_experts(models: &Vec<Model>) -> Vec<f64> {
172 let aggregation_estimates = aggregated_score(&models);
173 let mut sorted_aggregation_estimates = aggregation_estimates.clone();
174 sorted_aggregation_estimates.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
175
176 sorted_aggregation_estimates.iter().rev().cloned().collect()
177}