<commit_before>'use strict';

import d3 from 'd3';
import _ from 'lodash/fp';
import { dist, mult, diff, add, random } from './vector';


// A basic implementation of Kohonen map

// The main class
//
//
class Kohonen {

    // The constructor needs two params :
    // * size : the dimension size of the vectors
    // * neurons : an already built neurons grid as an array
    //
    // each neuron should provide :
    // * an x
    // * an y
    //
    // You should use an hexagon grid as it is the easier case
    // to deal with neighborhood.
    //
    // You also should normalized your neighborhood in such a way that 2 neighbors
    // got an euclidian distance of 1 between each other.
    constructor({ size, neurons }) {
        this.size = size;

        // On each neuron, generate a random vector v
        // of <size> dimension
        this.neurons = neurons.map(n => Object.assign({}, n, {
            v: random(size)
        }));

        // Initialize step
        this.step = 0;
    }

    learn(v) {
        // find bmu
        const bmu = this.findBestMatchingUnit(v);
        // compute current learning coef
        const currentLearningCoef = Kohonen.scaleStepLearningCoef(this.step);

        this.neurons.forEach(n => {
            // compute neighborhood
            const currentNeighborhood = Kohonen.neighborhood({bmu, n, step: this.step});
            // compute delta for the current neuron
            const delta = mult(
                diff(n.v, v),
                currentNeighborhood * currentLearningCoef
            );
            // update current vector
            n.v = [...add(n.v, delta)];
        });
        this.step += 1;
    }

    // Find closer neuron
    findBestMatchingUnit(v){
        return _.sortBy(n => dist(v, n.v))(this.neurons)[0];
    }

}

// The learning coef decreases with time
// TODO should be parametrizable ?
// TODO And remove from static
Kohonen.scaleStepLearningCoef = step => d3.scale.linear()
    .clamp(true)
    .domain([0, 10000])
    .range([1, .3])(step);

// Decrease neighborhood with time
// TODO should be parametrizable ?
// TODO And remove from static
Kohonen.scaleStepNeighborhood = step => d3.scale.linear()
    .clamp(true)
    .domain([0, 10000])
    .range([1, .3])(step);

// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
//
// http://mathworld.wolfram.com/GaussianFunction.html
//
// neighborhood function made with a gaussian
// TODO a, sigmaX, sigmaY should be parametrizable
Kohonen.neighborhood = ({ bmu, n, step }) => {
    const a = 1;
    const sigmaX = 1;
    const sigmaY = 1;
    return a
        * Math.exp(
            -(Math.pow(n.x - bmu.x, 2) / 2 * Math.pow(sigmaX, 2) + Math.pow(n.y - bmu.y, 2) / 2 * Math.pow(sigmaY, 2))
        )
        * Kohonen.scaleStepNeighborhood(step);
};


export default Kohonen;
<commit_msg>issue #2 : remove unused immutability<commit_after>'use strict';

import d3 from 'd3';
import _ from 'lodash/fp';
import { dist, mult, diff, add, random } from './vector';


// A basic implementation of Kohonen map

// The main class
//
//
class Kohonen {

    // The constructor needs two params :
    // * size : the dimension size of the vectors
    // * neurons : an already built neurons grid as an array
    // * maxStep : the max step that will be clamped in scaleStepLearningCoef and
    //             scaleStepNeighborhood
    // * minLearningCoef
    // * minNeighborhood
    //
    // each neuron should provide :
    // * an x
    // * an y
    //
    // You should use an hexagon grid as it is the easier case
    // to deal with neighborhood.
    //
    // You also should normalized your neighborhood in such a way that 2 neighbors
    // got an euclidian distance of 1 between each other.
    constructor({ size, neurons, maxStep = 10000, minLearningCoef = .3, minNeighborhood = .3 }) {

        // On each neuron, generate a random vector v
        // of <size> dimension
        this.neurons = neurons.map(n => Object.assign({}, n, {
            v: random(size)
        }));

        // Initialize step
        this.step = 0;

        // generate scaleStepLearningCoef,
        // as the learning coef decreases with time
        this.scaleStepLearningCoef = d3.scale.linear()
            .clamp(true)
            .domain([0, maxStep])
            .range([1, minLearningCoef]);

        // decrease neighborhood with time
        this.scaleStepNeighborhood = d3.scale.linear()
            .clamp(true)
            .domain([0, maxStep])
            .range([1, minNeighborhood]);

    }

    learn(v) {
        // find bmu
        const bmu = this.findBestMatchingUnit(v);
        // compute current learning coef
        const currentLearningCoef = this.scaleStepLearningCoef(this.step);

        this.neurons.forEach(n => {
            // compute neighborhood
            const currentNeighborhood = this.neighborhood({bmu, n});
            // compute delta for the current neuron
            const delta = mult(
                diff(n.v, v),
                currentNeighborhood * currentLearningCoef
            );
            // update current vector
            n.v = add(n.v, delta);
        });
        this.step += 1;
    }

    // Find closer neuron
    findBestMatchingUnit(v){
        return _.flow(_.sortBy(n => dist(v, n.v)), _.first)(this.neurons);
    }

    // http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
    //
    // http://mathworld.wolfram.com/GaussianFunction.html
    //
    // neighborhood function made with a gaussian
    neighborhood({ bmu, n}){
        const a = 1;
        const sigmaX = 1;
        const sigmaY = 1;
        return a
            * Math.exp(
                -(Math.pow(n.x - bmu.x, 2) / 2 * Math.pow(sigmaX, 2) + Math.pow(n.y - bmu.y, 2) / 2 * Math.pow(sigmaY, 2))
            )
            * this.scaleStepNeighborhood(this.step);
    }

}

export default Kohonen;

{'fraction_non_alphanumeric': 0.11968981793661497, 'fraction_numerical': 0.01062036412677006, 'mean_word_length': 1.9400396432111, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 14, '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': '3774919', 'n_tokens_mistral': 1874, 'n_tokens_neox': 1721, 'n_words': 522}