tinguely 0.1.0

Machine learning library
Documentation
use mathru::algebra::linear::{Vector, Matrix};
use mathru::algebra::abstr::Sign;
use crate::SupervisedLearn;

use rand::Rng;

/// Support Vector Machine
///
/// Contains an implementation of Support Vector Machine algorithm proposed in
/// http://ttic.uchicago.edu/~nati/Publications/PegasosMPB.pdf
///
/// The SVM models supports binary classification.
/// The model inputs should be a matrix and the training targets y
/// in the form of a vector of y \in \{-1, 1. \}^m
///
/// # Examples
///
/// ```
/// use tinguely::SupervisedLearn;
/// use tinguely::classification::SVM;
///
/// use mathru::algebra::linear::Matrix;
/// use mathru::algebra::linear::Vector;
///
/// let mut svm = SVM::new(3.0, 100);
/// ```
#[derive(Debug)]
pub struct SVM
{
    alpha: Option<Vector<f64>>,
    lambda: f64,
    /// Number of training iterations.
    iter: usize
}

impl SVM
{
    /// Constructs an untrained SVM instance with specified lambda which determines
    ///
    /// # Parameters
    /// * 'lambda':
    /// * 'iter': Number of iterations
    ///
    /// # Examples
    ///
    /// ```
    /// use mathru::algebra::linear::{Vector, Matrix};
    /// use tinguely::classification::SVM;
    /// use tinguely::model::SupervisedLearn;
    ///
    /// let input: Matrix<f64> = Matrix::new(4,1, vec![-1.0, -2.0, 5.0, 7.0]);
    /// let target: Vector<f64> = Vector::new_row(4, vec![-1.0, -1.0, 1.0, 1.0]);
    ///
    /// let mut svm = SVM::new(0.3f64, 100);
    ///
    /// svm.train(&input, &target);
    /// ```
    pub fn new(lambda: f64, iter: usize) -> SVM
    {
        SVM
        {
            alpha: None,
            lambda: lambda,
            iter: iter,
        }
    }

    /// Returns the parameters
    ///
    /// # Returns
    pub fn get_parameter(self: &Self) -> Vector<f64>
    {
        if self.alpha == None
        {
            panic!("Model is not trained");
        }

        return self.alpha.clone().unwrap();

    }
}

impl SupervisedLearn<Matrix<f64>, Vector<f64>> for SVM
{
        /// Predict output from inputs.
        fn predict(self: &Self, input: &Matrix<f64>) -> Vector<f64> // Result<U, &str>;
        {
		    let (m, n) : (usize, usize) = input.dim();

            if self.alpha == None
            {
                panic!("Model is not trained");
            }
      	    let ones: Matrix<f64> = Matrix::<f64>::ones(m, n  + 1);

      	    let full_input: Matrix<f64> = ones.set_slice(input, 0, 0);

            return (full_input * self.alpha.clone().unwrap()).sgn();

        }

        /// Train the model using inputs and targets.
        fn train(self: &mut Self, input: &Matrix<f64>, target: &Vector<f64>) //Result<(), ()>;
        {
            let mut rng =  rand::thread_rng();

            let (m, n): (usize, usize) = input.dim();

      	    let ones: Matrix<f64> = Matrix::<f64>::ones(m, n + 1);

      	    let full_input: Matrix<f64> = ones.set_slice( input, 0, 0);

            let mut alpha: Vector<f64> = Vector::zero(n + 1);


            for t in 0..self.iter
            {

                let i_t: usize = rng.gen_range(0, m);

                let n_t: f64 = 1.0f64 / (self.lambda * (t as f64 + 1.0f64));

                let x_i_t: Vector<f64> = full_input.get_row(&i_t).transpose();
                let y_i_t: f64 = *target.get(&i_t);


                if y_i_t * alpha.dotp(&x_i_t) < 1.0
                {
                    alpha = alpha * (1.0f64 - n_t * self.lambda) + x_i_t * y_i_t * n_t;

                }
                else
                {
                    if y_i_t * alpha.dotp(&x_i_t) >= 1.0
                    {
                        alpha = alpha * (1.0 - n_t * self.lambda);
                    }
                }
            }

            self.alpha = Some(alpha);
        }
}