use serde::{Serialize, Deserialize};
use crate::Sample;
pub trait Classifier {
fn confidence(&self, sample: &Sample, row: usize) -> f64;
fn predict(&self, sample: &Sample, row: usize) -> i64 {
let conf = self.confidence(sample, row);
if conf >= 0.0 { 1 } else { -1 }
}
fn confidence_all(&self, sample: &Sample) -> Vec<f64> {
let n_sample = sample.shape().0;
(0..n_sample).into_iter()
.map(|row| self.confidence(sample, row))
.collect::<Vec<_>>()
}
fn predict_all(&self, sample: &Sample) -> Vec<i64>
{
let n_sample = sample.shape().0;
(0..n_sample).into_iter()
.map(|row| self.predict(sample, row))
.collect::<Vec<_>>()
}
}
pub trait Regressor {
fn predict(&self, sample: &Sample, row: usize) -> f64;
fn predict_all(&self, sample: &Sample) -> Vec<f64>
{
let n_sample = sample.shape().0;
(0..n_sample).into_iter()
.map(|row| self.predict(sample, row))
.collect::<Vec<_>>()
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CombinedHypothesis<H> {
pub weights: Vec<f64>,
pub hypotheses: Vec<H>,
}
impl<H: Clone> CombinedHypothesis<H> {
#[inline]
pub fn from_slices(weights: &[f64], hypotheses: &[H]) -> Self {
let mut new_weights = Vec::with_capacity(weights.len());
let mut new_hypotheses = Vec::with_capacity(hypotheses.len());
weights.into_iter()
.copied()
.zip(hypotheses)
.for_each(|(w, h)| {
if w > 0.0 {
new_weights.push(w);
new_hypotheses.push(h.clone());
}
});
Self { weights: new_weights, hypotheses: new_hypotheses, }
}
}
impl<H> CombinedHypothesis<H> {
#[inline]
pub fn push(&mut self, weight: f64, hypothesis: H) {
self.weights.push(weight);
self.hypotheses.push(hypothesis);
}
#[inline]
pub fn normalize(&mut self) {
let norm = self.weights.iter()
.map(|w| w.abs())
.sum::<f64>();
if norm <= 0.0 { return; }
self.weights.iter_mut()
.for_each(|w| { *w /= norm; });
}
#[inline]
pub fn decompose(self) -> (Vec<f64>, Vec<H>) {
(self.weights, self.hypotheses)
}
}
impl<F> Classifier for CombinedHypothesis<F>
where F: Classifier,
{
fn confidence(&self, sample: &Sample, row: usize) -> f64 {
self.weights.iter()
.zip(&self.hypotheses[..])
.map(|(w, h)| *w * h.confidence(sample, row))
.sum::<f64>()
}
}
impl<F> Regressor for CombinedHypothesis<F>
where F: Regressor,
{
fn predict(&self, sample: &Sample, row: usize) -> f64 {
self.weights.iter()
.zip(&self.hypotheses[..])
.map(|(w, h)| *w * h.predict(sample, row))
.sum::<f64>()
}
}