<commit_before>use super::Projection;
use geometry::{Span, Space, RegularSpace};
use geometry::dimensions::{Continuous, Partitioned};
use ndarray::{Axis, ArrayView, Array1, Array2};
use utils::cartesian_product;


#[derive(Serialize, Deserialize)]
pub struct RBFNetwork {
    mu: Array2<f64>,
    beta: Array1<f64>,
}

impl RBFNetwork {
    pub fn new(mu: Array2<f64>, sigma: Array1<f64>) -> Self {
        if mu.cols() != sigma.len() {
            panic!("Dimensions of mu ({:?}) and sigma ({:?}) must agree.",
                   mu.shape(),
                   sigma.shape());
        }

        RBFNetwork {
            mu: mu,
            beta: 0.5 / sigma.map(|v| v*v),
        }
    }

    pub fn from_space(input_space: RegularSpace<Partitioned>) -> Self {
        let n_features = match input_space.span() {
            Span::Finite(s) => s,
            _ => { panic!("`RBFNetwork` projection only supports partitioned input spaces.") }
        };

        let centres = input_space.centres();
        let flat_combs = cartesian_product(&centres)
            .iter()
            .cloned()
            .flat_map(|e| e)
            .collect();

        let mu = Array2::from_shape_vec((n_features, input_space.dim()), flat_combs).unwrap();
        let sigma = input_space.iter().map(|d| d.partition_width()).collect();

        RBFNetwork::new(mu, sigma)
    }

    pub fn kernel(&self, input: &[f64]) -> Array1<f64> {
        let d = &self.mu - &ArrayView::from_shape((1, self.mu.cols()), input).unwrap();

        (&d*&d*&self.beta).mapv(|v| (-v.abs()).exp()).sum_axis(Axis(1))
    }
}

impl Projection<RegularSpace<Continuous>> for RBFNetwork {
    fn project_onto(&self, input: &Vec<f64>, phi: &mut Array1<f64>) {
        let p = self.kernel(input);

        phi.scaled_add(1.0/p.scalar_sum(), &p)
    }

    fn dim(&self) -> usize {
        self.mu.cols()
    }

    fn size(&self) -> usize {
        self.mu.rows()
    }

    fn equivalent(&self, other: &Self) -> bool {
        self.mu == other.mu && self.beta == other.beta && self.size() == other.size()
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use ndarray::{arr1, arr2};

    #[test]
    fn test_size() {<commit_msg>chore(fa): replace `scalar_sum` for `l1` in RBFNetwork<commit_after>use super::Projection;
use geometry::{Span, Space, RegularSpace};
use geometry::norms::l1;
use geometry::dimensions::{Continuous, Partitioned};
use ndarray::{Axis, ArrayView, Array1, Array2};
use utils::cartesian_product;


#[derive(Clone, Serialize, Deserialize)]
pub struct RBFNetwork {
    mu: Array2<f64>,
    beta: Array1<f64>,
}

impl RBFNetwork {
    pub fn new(mu: Array2<f64>, sigma: Array1<f64>) -> Self {
        if mu.cols() != sigma.len() {
            panic!("Dimensions of mu ({:?}) and sigma ({:?}) must agree.",
                   mu.shape(),
                   sigma.shape());
        }

        RBFNetwork {
            mu: mu,
            beta: 0.5 / sigma.map(|v| v*v),
        }
    }

    pub fn from_space(input_space: RegularSpace<Partitioned>) -> Self {
        let n_features = match input_space.span() {
            Span::Finite(s) => s,
            _ => { panic!("`RBFNetwork` projection only supports partitioned input spaces.") }
        };

        let centres = input_space.centres();
        let flat_combs = cartesian_product(&centres)
            .iter()
            .cloned()
            .flat_map(|e| e)
            .collect();

        let mu = Array2::from_shape_vec((n_features, input_space.dim()), flat_combs).unwrap();
        let sigma = input_space.iter().map(|d| d.partition_width()).collect();

        RBFNetwork::new(mu, sigma)
    }

    pub fn kernel(&self, input: &[f64]) -> Array1<f64> {
        let d = &self.mu - &ArrayView::from_shape((1, self.mu.cols()), input).unwrap();

        (&d*&d*&self.beta).mapv(|v| (-v.abs()).exp()).sum_axis(Axis(1))
    }
}

impl Projection<RegularSpace<Continuous>> for RBFNetwork {
    fn project_onto(&self, input: &Vec<f64>, phi: &mut Array1<f64>) {
        let p = self.kernel(input);
        let z = l1(p.as_slice().unwrap());

        phi.scaled_add(1.0/z, &p);
    }

    fn dim(&self) -> usize {
        self.mu.cols()
    }

    fn size(&self) -> usize {
        self.mu.rows()
    }

    fn equivalent(&self, other: &Self) -> bool {
        self.mu == other.mu && self.beta == other.beta && self.size() == other.size()
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use ndarray::{arr1, arr2};

    #[test]
    fn test_size() {
{'fraction_non_alphanumeric': 0.1745220097821254, 'fraction_numerical': 0.015340151178301467, 'mean_word_length': 2.405753217259652, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '647244', 'n_tokens_mistral': 1555, 'n_tokens_neox': 1517, 'n_words': 318}