Code viewer for World: XOR multi-layer network (c...
// Cloned by Tristan Everitt on 2 Nov 2022 from World "XOR multi-layer network" by "Coding Train" project
// Please leave this clone trail here.


// XOR multi-layer network

// Port from:
// https://github.com/CodingTrain/Toy-Neural-Network-JS/tree/master/examples/xor
// with modifications

// libraries from:
// https://github.com/CodingTrain/Toy-Neural-Network-JS/tree/master/lib
// ported to here:
// https://ancientbrain.com/uploads.php?userid=codingtrain


//=== Tweaker's box ============================================

// number of nodes in each layer:
const noinput = 2;
const nohidden = 4;
const nooutput = 1;

// define the exemplars to learn from:
let training_data = [
    {inputs: [0, 0], outputs: [0]},
    {inputs: [0, 1], outputs: [1]},
    {inputs: [1, 0], outputs: [1]},
    {inputs: [1, 1], outputs: [0]}
];

let nn;     // global var

let learningrate = 0.2;

// train this number of times per draw()
const notrain = 10;

// Take screenshot on this step:
AB.screenshotStep = 300;


// divide 0,1 into squares
// show all squares or just the corner squares:
let showall = true;

const cols = 30;
const rows = cols;

const squaresize = 20;

const canvassize = rows * squaresize;


// Matrix.randomize() is changed to point to this. Must be defined by user of Matrix.

function randomWeight() {
    return (AB.randomFloatAtoB(-0.1, 0.9));
    // Coding Train default is -1 to 1
}

//=== End of tweaker's box ============================================


function setup() {
    createCanvas(canvassize, canvassize);

    $.getScript("/uploads/codingtrain/matrix.js", function () {
        $.getScript("/uploads/codingtrain/nn.js", function () {
            nn = new NeuralNetwork(noinput, nohidden, nooutput);
        });
    });
}


function draw() {
    // check if libraries loaded yet:
    if (typeof nn == 'undefined') return;
    nn.setLearningRate(learningrate);

    background('#ffffcc');

    // train n times
    for (let i = 0; i < notrain; i++) {
        let data = random(training_data);
        nn.train(data.inputs, data.outputs);
    }

// draw either some squares or all squares:

    if (showall) {
        // redraw all squares each time round
        for (let i = 0; i < cols; i++)
            for (let j = 0; j < rows; j++)
                drawquare(i, j);
    } else {
        // redraw just the 4 squares
        for (let i = 0; i < cols; i = i + cols - 1)
            for (let j = 0; j < rows; j = j + rows - 1)
                drawquare(i, j);
    }

    let msg = '<ul>';
    msg += '<li><b>noinput</b>: ' + noinput + '</li>';
    msg += '<li><b>nohidden</b>: ' + nohidden + '</li>';
    msg += '<li><b>nooutput</b>: ' + nooutput + '</li>';
    msg += '<li><b>learningrate</b>: ' + learningrate + '</li>';
    msg += '<li><b>notrain</b>: ' + notrain + '</li>';
    msg += '<li><b>canvassize</b>: ' + canvassize + '</li>';
    msg += '<li><b>squaresize</b>: ' + squaresize + '</li>';
    msg += '<li><b>grid</b>: cols=' + cols + '; rows=' + rows + '</li>';
    msg += '</ul>';

    msg += "--------------<br/>";

    msg += 'Training Data:<br/>';
    msg += '<pre>' + JSON.stringify(training_data,null, 2) + '</pre>';


    AB.msg(msg);

    //reduceX_value = reduceX_value - reduceRate;
    //learningrate = Math.exp(reduceX_value);
}


function drawquare(i, j) {
    let x1 = i / cols;
    let x2 = j / rows;
    let inputs = [x1, x2];
    let y = nn.predict(inputs);
    //console.log ( "input (" +x1 + "," + x2 + ") output " + y );
    // background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(1,112,152,1) 23%, rgba(0,212,255,1) 100%);


    strokeWeight(2);
    stroke('black');
    //fill(y * 255);      // 0 is black, 1 is white

    const rgb = y < 0.5 ? '0,0,255' : '255,0,0';
    //const alpha = y < 0.5 ? y : (1 - y);
    const alpha = 1 - y;

    fill('rgba('+rgb+', '+alpha +')');
    rect(i * squaresize, j * squaresize, squaresize, squaresize);
}