use mathru::algebra::linear::{Vector, Matrix};
use rand::Rng;
use crate::model::SupervisedLearn;
use mathru::optim::{Optimizable, OptimAlgorithm};
pub struct LinearRegression<O>
where O: OptimAlgorithm<LinearRegressionBase>
{
base: LinearRegressionBase,
optim: O
}
impl<O> LinearRegression<O>
where O: OptimAlgorithm<LinearRegressionBase>
{
pub fn new(optim: O) -> LinearRegression<O>
{
LinearRegression
{
base: LinearRegressionBase::new(),
optim: optim
}
}
pub fn parameters(self: &Self) -> Vector<f64>
{
self.base.beta.clone()
}
}
impl<O> SupervisedLearn<Matrix<f64>, Vector<f64>> for LinearRegression<O>
where O: OptimAlgorithm<LinearRegressionBase>
{
fn train<'a, 'b>(self: &'a mut Self, x: &'b Matrix<f64>, y: &'b Vector<f64>)
{
let (x_m, x_n): (usize, usize) = x.dim();
let (y_m, _y_n): (usize, usize) = x.dim();
if x_m != y_m
{
panic!("Dimension mismatch")
}
let ones: Matrix<f64> = Matrix::<f64>::ones(x_m, x_n + 1);
let full_input: Matrix<f64> = ones.set_slice( x, 0, 1);
let mut rng = rand::thread_rng();
let beta_0: Vector<f64> = Vector::new_column(x_n + 1, vec![rng.gen_range(0.0, 1.0); x_n + 1]);
let beta: Vector<f64> = self.optim.minimize(&self.base, &beta_0, &full_input, y);
self.base.beta = beta;
}
fn predict<'a, 'b>(self: &'a Self, x: &'b Matrix<f64>) -> Vector<f64>
{
let (m, n) : (usize, usize) = x.dim();
let ones: Matrix<f64> = Matrix::<f64>::ones(m, n + 1);
let full_input: Matrix<f64> = ones.set_slice( x, 0, 1);
let y_hat: Vector<f64> = &full_input * &self.base.beta;
return y_hat;
}
}
pub struct LinearRegressionBase
{
pub beta: Vector<f64>,
}
impl LinearRegressionBase
{
fn new() -> LinearRegressionBase
{
LinearRegressionBase
{
beta: Vector::zero(0)
}
}
}
impl Optimizable for LinearRegressionBase
{
type Input = Matrix<f64>;
type Target = Vector<f64>;
fn value(&self, param: &Vector<f64>, input: &Self::Input, target: &Self::Target) -> f64
{
let target_hat: Vector<f64> = input * param;
let diff = &target_hat - target;
let cost: f64 = (&target_hat - target).dotp(&diff);
return cost;
}
fn gradient(&self, param: &Vector<f64>, input: &Self::Input, target: &Self::Target) -> Vector<f64>
{
let y_hat: Vector<f64> = input * param;
let diff = &y_hat - target;
let grad: Vector<f64> = &(input.transpose()) * &diff;
return grad;
}
}