tinguely 0.1.0

Machine learning library
Documentation

use mathru::algebra::linear::{Vector, Matrix};
use rand::Rng;
use crate::model::SupervisedLearn;
use mathru::optim::{Optimizable, OptimAlgorithm};

/// Linear regression
///
/// For more information: <br>
/// <a href="https://en.wikipedia.org/wiki/Linear_regression">https://en.wikipedia.org/wiki/Linear_regression</a>
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>
{

	/// Train the linear regression model.
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// use tinguely::regression::LinearRegression;
    /// use mathru::algebra::linear::{Vector, Matrix};
    /// use mathru::optim::StochasticGradientDesc;
    /// use tinguely::SupervisedLearn;
    ///
    /// let optimizer = StochasticGradientDesc::new(0.01, 0.0, 100);
    /// let mut lin_mod = LinearRegression::new(optimizer);
    /// let inputs = Matrix::new(3, 1, vec![2.0, 3.0, 4.0]);
    /// let targets = Vector::new_column(3, vec![5.0, 6.0, 7.0]);
    ///
    /// lin_mod.train(&inputs, &targets);
    /// ```
    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
{
	/// The input data type to the model.
    type Input = Matrix<f64>;

    /// The target data type to the model.
    type Target = Vector<f64>;

    /// Compute the gradient for the model.
    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;
	}
}