use crate::{linear_algebra::Vector, Cost};
pub struct LeastSquares;
impl Cost<f64> for LeastSquares {
fn outer_derivative(&self, prediction: &f64, truth: f64) -> f64 {
let error = prediction - truth;
2.0 * error
}
fn cost(&self, prediction: f64, truth: f64) -> f64 {
(prediction - truth).powi(2)
}
}
pub struct LeastAbsoluteDeviation;
impl Cost<f64> for LeastAbsoluteDeviation {
fn outer_derivative(&self, prediction: &f64, truth: f64) -> f64 {
let error = prediction - truth;
if error == 0.0 {
0.0
} else {
error.signum()
}
}
fn cost(&self, prediction: f64, truth: f64) -> f64 {
(prediction - truth).abs()
}
}
pub struct MaxLikelihood;
impl Cost<f64> for MaxLikelihood {
fn outer_derivative(&self, prediction: &f64, truth: f64) -> f64 {
((1.0 - truth) / (1.0 - prediction) - truth / prediction)
}
fn cost(&self, prediction: f64, truth: f64) -> f64 {
-truth * prediction.ln() - (1.0 - truth) * (1.0 - prediction).ln()
}
}
impl Cost<bool> for MaxLikelihood {
fn outer_derivative(&self, prediction: &f64, truth: bool) -> f64 {
1. / if truth { -prediction } else { 1.0 - prediction }
}
fn cost(&self, prediction: f64, truth: bool) -> f64 {
-(if truth { prediction } else { 1.0 - prediction }).ln()
}
}
impl<V> Cost<usize, V> for MaxLikelihood
where
V: Vector,
{
fn outer_derivative(&self, prediction: &V, truth: usize) -> V {
let mut derivation = prediction.clone();
for i in 0..prediction.dimension() {
*derivation.at_mut(i) = self.outer_derivative(&prediction.at(i), truth == i);
}
derivation
}
fn cost(&self, prediction: V, truth: usize) -> f64 {
(0..prediction.dimension()).fold(0.0, |s, i| s + self.cost(prediction.at(i), i == truth))
}
}
#[cfg(test)]
mod test {
use super::super::Cost;
use super::{LeastAbsoluteDeviation, LeastSquares, MaxLikelihood};
fn approx_derivate<T: Copy>(cost: &Cost<T>, prediction: f64, truth: T) -> f64 {
let epsilon = 0.00001;
let f_plus_epsilon = cost.cost(prediction + epsilon, truth);
let f_minus_epsilon = cost.cost(prediction - epsilon, truth);
println!(
"f_x_plus_epsilon: {}, f_x_minus_epsilon:: {}",
f_plus_epsilon, f_minus_epsilon
);
(f_plus_epsilon - f_minus_epsilon) / (2.0 * epsilon)
}
fn check_derivate<T: Copy>(cost: &Cost<T>, prediction: f64, truth: T) -> f64 {
let derivate = cost.outer_derivative(&prediction, truth);
let approx = approx_derivate(cost, prediction, truth);
println!("derivation: {}, approximation: {}", derivate, approx);
(derivate - approx).abs()
}
#[test]
fn least_squares_derivation() {
let cost = LeastSquares {};
assert!(check_derivate(&cost, 10.0, 12.0) < 0.001);
}
#[test]
fn least_absolute_derivation() {
let cost = LeastAbsoluteDeviation {};
assert!(check_derivate(&cost, 0.0, 0.0) < 0.001);
assert!(check_derivate(&cost, 1.0, 0.0) < 0.001);
assert!(check_derivate(&cost, -1.0, 0.0) < 0.001);
}
#[test]
fn neg_log_likelihood_derivation() {
let cost = MaxLikelihood {};
assert!(check_derivate(&cost, 0.2, false) < 0.001);
assert!(check_derivate(&cost, 0.8, true) < 0.001);
assert!(check_derivate(&cost, 0.2, 0.0) < 0.001);
assert!(check_derivate(&cost, 0.8, 1.0) < 0.001);
assert_eq!(
cost.outer_derivative(&0.2, false),
cost.outer_derivative(&0.2, 0.0)
);
assert_eq!(
cost.outer_derivative(&0.8, true),
cost.outer_derivative(&0.8, 1.0)
);
}
}