rust-als 0.1.0

Primitive Alternating Least Squares implementation
Documentation
#[cfg(test)]
mod tests{
    use std::ops::{Index, IndexMut};
    use nalgebra::{DMatrix, Matrix};
    use ndarray::Array1;
    use rand::{random, Rng, thread_rng};
    use rayon::prelude::*;
    use crate::als::ALS;

    const N : usize = 10;
    const M : usize = 20;
    const K : usize = 6;


    #[test]
    fn test_basic() {
        /*
            1 - 2 -
            - 7 - -
            2 - - 5
            - - 3 -

         */
        let mut als = ALS::new(4, 4, 2);
        als.add((0, 0, 1.0));
        als.add((0, 2, 2.0));
        als.add((1, 1, 7.0));
        als.add((2, 0, 2.0));
        als.add((2, 3, 5.0));
        als.add((3, 1, 3.0));

        let cost_before = als.cost();
        als.train();
        let cost_after = als.cost();

        assert!(cost_before > cost_after);
    }

    #[test]
    fn test_intermediate() {
        /*
            0 - 2 - 4 - ....
            - 3 - 5 - 7 ....
            4 - 6 - 8 - ....
            ...
         */
        let n = 10;
        let m = 10;
        let k = 5;
        let mut als = ALS::new(n, m, k);

        for i_n in 0..n {
            for i_m in 0..m {
                if (i_n + i_m) % 2 == 0 {
                    als.add((i_n, i_m, (2*i_n + i_m) as f64));
                }
            }
        }

        let cost_before = als.cost();
        als.train();
        let cost_after = als.cost();
        assert!(cost_before > cost_after);
    }

    #[test]
    fn test_rand_big() {
        let m = 1000;
        let n = 20_000;
        let k = 10;
        let mut als = ALS::new(m, n, k);
        for n in 0..N {
            for m in 0..M {
                if random::<f64>() < 0.1 {
                    let val = thread_rng().gen_range(1.0..10.0);
                    als.add((n, m, val));
                }
            }
        }
        let cost_before = als.cost();
        als.train();
        let cost_after = als.cost();
        assert!(cost_before > cost_after);
    }

    fn bm25(data : &mut Vec<(usize, usize, f64)>, n: usize, m :usize, b : f64, k1 : f64) {
        let mut idf = Array1::<f64>::zeros((m));
        data.iter().for_each(|(_, c, _)| { *idf.index_mut((*c)) += 1.0;});
        let doc_amounts_ln = (n as f64).ln();
        idf.par_map_inplace(|e| {*e = doc_amounts_ln - e.ln_1p();});

        let mut length_norm =  Array1::<f64>::zeros((n));
        data.iter().for_each(|(r, _, _)| { *length_norm.index_mut((*r)) += 1.0;});
        let ave_len : f64 = length_norm.par_iter().sum::<f64>() / (n as f64);
        length_norm.par_map_inplace(|e| {*e  = (1.0 - b) + b * *e / ave_len;});

        data.par_iter_mut().for_each(|(r, c, val)|
            {*val  = *val * (k1 + 1.0) / (k1 * *length_norm.index(*r) + *val) * *idf.index(*c);});
    }

    //#[test]
    fn test_singular() {
        let n = 10;
        let m = 10;
        let k = 6;
        let mut als = ALS::new(n, m, k);
        let mut sparse:DMatrix<f64> = DMatrix::zeros(n, m);
        let mut coo = vec![];
        for i_n in 0..n {
            for i_m in 0..m {
                if thread_rng().gen_bool(0.2) {
                    coo.push((i_n, i_m,1.0));
                    //sparse[(i_n, i_m)] = 1.0;
                }
            }
        }

        //bm25(&mut coo, n, m, 0.5, 100.0 );

        for (i_n, i_m, val) in coo {
            als.add((i_n, i_m, val));
            sparse[(i_n, i_m)] = val;
        }



        let cost_before = als.cost();
        als.train_for_fill_missing(50);
        let cost_after = als.cost();

        let trained_r = DMatrix::from_fn(n, m, |i_n, i_m| als.predict_r_val(i_n, i_m));
        println!("{}", sparse);
        println!("{}", trained_r);
        assert!(cost_before > cost_after);
    }
}