Code viewer for World: Character recognition neur...
var showMatrix = false;
class ActivationFunction {
  constructor(func, errorFunc, i = false) {
    this.func = func;
    this.dfunc = errorFunc;
    this.use_X_values = i;
  }
}
let sigmoid = new ActivationFunction((value) => {
  return 1 / (1 + Math.exp(-value));
}, (i) => {
  return i * (1 - i);
});
let tanh = new ActivationFunction((val) => {
  return Math.tanh(val);
}, (rayY) => {
  return 1 - rayY * rayY;
});
let arctan = new ActivationFunction((lowEnd) => {
  return Math.atan(lowEnd);
}, (rayY) => {
  return 1 / (rayY * rayY + 1);
}, use_X_values = true);
let softsign = new ActivationFunction((mercatorX) => {
  return mercatorX / (1 + Math.abs(mercatorX));
}, (mercatorX) => {
  return 1 / Math.pow(Math.abs(mercatorX) + 1, 2);
}, use_X_values = true);
let relu = new ActivationFunction((delta) => {
  return delta < 0 ? 0 : delta;
}, (canCreateDiscussions) => {
  return canCreateDiscussions < 0 ? 0 : 1;
}, use_X_values = true);
let leaky_relu = new ActivationFunction((t) => {
  return t < 0 ? .01 * t : t;
}, (canCreateDiscussions) => {
  return canCreateDiscussions < 0 ? .01 : 1;
}, use_X_values = true);
let softplus = new ActivationFunction((prop_log_scale) => {
  return Math.log(1 + Math.exp(prop_log_scale));
}, (value) => {
  return 1 / (1 + Math.exp(-value));
}, use_X_values = true);
let gaussian = new ActivationFunction((rayY) => {
  return Math.exp(rayY * rayY * -1);
}, (rayY) => {
  return -2 * rayY * Math.exp(rayY * rayY * -1);
}, use_X_values = true);
class NeuralNetwork {
  constructor(inputs, outputs, propOuts) {
    if (inputs instanceof NeuralNetwork) {
      let that = inputs;
      this.input_nodes = that.input_nodes;
      this.hidden_nodes = that.hidden_nodes;
      this.output_nodes = that.output_nodes;
      this.weights_ih = that.weights_ih.copy();
      this.weights_ho = that.weights_ho.copy();
      this.bias_h = that.bias_h.copy();
      this.bias_o = that.bias_o.copy();
    } else {
      this.input_nodes = inputs;
      this.hidden_nodes = outputs;
      this.output_nodes = propOuts;
      this.weights_ih = new Matrix(this.hidden_nodes, this.input_nodes);
      this.weights_ho = new Matrix(this.output_nodes, this.hidden_nodes);
      this.weights_ih.randomize();
      this.weights_ho.randomize();
      this.bias_h = new Matrix(this.hidden_nodes, 1);
      this.bias_o = new Matrix(this.output_nodes, 1);
      this.bias_h.randomize();
      this.bias_o.randomize();
    }
    this.setLearningRate();
    this.setActivationFunction();
  }
  predict(inputs) {
    let i = Matrix.fromArray(inputs);
    let matrix = Matrix.multiply(this.weights_ih, i);
    matrix.add(this.bias_h);
    matrix.map(this.activation_function.func);
    let self = Matrix.multiply(this.weights_ho, matrix);
    return self.add(this.bias_o), self.map(this.activation_function.func), self.toArray();
  }
  setLearningRate(t = .1) {
    this.learning_rate = t;
  }
  setActivationFunction(t = sigmoid) {
    this.activation_function = t;
  }
  train(inputs, outputs) {
    let n = Matrix.fromArray(inputs);
    let matrix = Matrix.multiply(this.weights_ih, n);
    matrix.add(this.bias_h);
    matrix.map(this.activation_function.func);
    let r = Matrix.multiply(this.weights_ho, matrix);
    r.add(this.bias_o);
    r.map(this.activation_function.func);
    let targets = Matrix.fromArray(outputs);
    let m = Matrix.subtract(targets, r);
    let A = Matrix.map(r, this.activation_function.dfunc);
    A.multiply(m);
    A.multiply(this.learning_rate);
    let i = Matrix.transpose(matrix);
    let entrytwo = Matrix.multiply(A, i);
    this.weights_ho.add(entrytwo);
    this.bias_o.add(A);
    let lambda = Matrix.transpose(this.weights_ho);
    let theta = Matrix.multiply(lambda, m);
    let X = Matrix.map(matrix, this.activation_function.dfunc);
    X.multiply(theta);
    X.multiply(this.learning_rate);
    let delta = Matrix.transpose(n);
    let filtersSwitchs = Matrix.multiply(X, delta);
    this.weights_ih.add(filtersSwitchs);
    this.bias_h.add(X);
  }
  serialize() {
    return JSON.stringify(this);
  }
  static deserialize(json) {
    if ("string" == typeof json) {
      json = JSON.parse(json);
    }
    let opts = new NeuralNetwork(json.input_nodes, json.hidden_nodes, json.output_nodes);
    return opts.weights_ih = Matrix.deserialize(json.weights_ih), opts.weights_ho = Matrix.deserialize(json.weights_ho), opts.bias_h = Matrix.deserialize(json.bias_h), opts.bias_o = Matrix.deserialize(json.bias_o), opts.learning_rate = json.learning_rate, opts;
  }
  copy() {
    return new NeuralNetwork(this);
  }
  mutate(target) {
    this.weights_ih.map(target);
    this.weights_ho.map(target);
    this.bias_h.map(target);
    this.bias_o.map(target);
  }
}
class NeuralNetworkMulti {
  constructor(nodes, bitmap) {
    this.nodes = nodes;
    this.lr = bitmap || .01;
    this.activation = NeuralNetworkMulti.rhlu;
    this.dactivation = NeuralNetworkMulti.drhlu;
    this.weights = [];
    this.biases = [];
    for (let i = 0; i < this.nodes.length - 1; i++) {
      this.weights.push((new Matrix(this.nodes[i + 1], this.nodes[i])).randomize());
    }
    for (let i = 1; i < this.nodes.length; i++) {
      this.biases.push((new Matrix(this.nodes[i], 1)).randomize());
    }
  }
  static dtanh(x) {
    return 1 / pow(Math.cosh(x), 2);
  }
  static sigmoid(x) {
    return 1 / (1 + Math.exp(-x));
  }
  static dsigmoid(i) {
    return i * (1 - i);
  }
  static rhlu(delta) {
    return delta < 0 ? 0 : delta;
  }
  static drhlu(canCreateDiscussions) {
    return canCreateDiscussions < 0 ? 0 : 1;
  }
  predict(items) {
    let matrix = Matrix.fromArray(items);
    for (let i = 0; i < this.weights.length; i++) {
      (matrix = Matrix.multiply(this.weights[i], matrix)).add(this.biases[i]);
      matrix.map(this.activation);
    }
    return matrix.toArray();
  }
  train(inputs_array, targets_array) {
    let targets = Matrix.fromArray(targets_array);
    let outputs = Matrix.fromArray(this.predict(inputs_array));
    let keys = [];
    let m = Matrix.fromArray(inputs_array);
    for (let i = 0; i < this.weights.length; i++) {
      keys.push(m);
      (m = Matrix.multiply(this.weights[i], m)).add(this.biases[i]);
      m.map(this.activation);
    }
    let v = Matrix.subtract(targets, outputs);
    let r = Matrix.map(outputs, this.dactivation);
    r.multiply(v);
    r.multiply(this.lr);
    for (let i = keys.length - 1; i >= 0; i--) {
      let entrytwo = Matrix.multiply(r, Matrix.transpose(keys[i]));
      this.weights[i].add(entrytwo);
      this.biases[i].add(r);
      v = Matrix.multiply(Matrix.transpose(this.weights[i]), v);
      (r = Matrix.map(keys[i], this.dactivation)).multiply(v);
      r.multiply(this.lr);
    }
  }
  getModel() {
    let self = this;
    let net = {
      nodes : self.nodes,
      lr : self.lr,
      activation : self.activation,
      dactivation : self.dactivation,
      weights : [],
      biases : []
    };
    for (let size of self.weights) {
      let t = {
        rows : size.rows,
        cols : size.cols,
        data : []
      };
      for (let state of size.data) {
        let i = [];
        for (let t of state) {
          i.push(t);
        }
        t.data.push(i);
      }
      net.weights.push(t);
    }
    for (let data of self.biases) {
      let falseySection = {
        rows : data.rows,
        cols : data.cols,
        data : data.data
      };
      net.biases.push(falseySection);
    }
    return net;
  }
  static formModel(result) {
    let self = new NeuralNetworkMulti(result.nodes, result.lr);
    self.nodes = result.nodes;
    self.lr = result.lr;
    self.activation = result.activation;
    self.dactivation = result.dactivation;
    for (let i = 0; i < self.weights.length; i++) {
      self.weights[i].rows = result.weights[i].rows;
      self.weights[i].cols = result.weights[i].cols;
      for (let y = 0; y < result.weights[i].rows; y++) {
        for (let j = 0; j < result.weights[i].cols; j++) {
          self.weights[i].data[y][j] = result.weights[i].data[y][j];
        }
      }
      self.weights[i].rows = result.weights[i].rows;
    }
    return self;
  }
  copy() {
    let artistTrack = this.getModel();
    return NeuralNetworkMulti.formModel(artistTrack);
  }
  mutate(val) {
    for (let jq_results of this.weights) {
      jq_results.map(val);
    }
    for (let jq_results of this.biases) {
      jq_results.map(val);
    }
  }
  merge(matrix, e = .5) {
    let step = 1 - e;
    let dx = e;
    for (let i = 0; i < this.nodes.length; i++) {
      if (this.nodes[i] != matrix.nodes[i]) {
        return void console.error("Neural Networks can not be merged");
      }
    }
    this.lr = this.lr * step + matrix.lr * dx;
    for (let i = 0; i < this.weights.length; i++) {
      for (let y = 0; y < this.weights[i].rows; y++) {
        for (let j = 0; j < this.weights[i].cols; j++) {
          this.weights[i].data[y][j] = this.weights[i].data[y][j] * step + matrix.weights[i].data[y][j] * dx;
        }
      }
    }
    for (let i = 0; i < this.biases.length; i++) {
      for (let y = 0; y < this.biases[i].rows; y++) {
        for (let j = 0; j < this.biases[i].cols; j++) {
          this.biases[i].data[y][j] = this.biases[i].data[y][j] * step + matrix.biases[i].data[y][j] * dx;
        }
      }
    }
    return this;
  }
  setActivation(value, opt_pass) {
    this.activation = value;
    this.dactivation = opt_pass;
  }
  setLearningRate(learningRate) {
    this.lr = learningRate;
  }
}
const PIXELS = 28;
const PIXELSSQUARED = PIXELS * PIXELS;
const NOTRAIN = 6E4;
const NOTEST = 1E4;
const noinput = PIXELSSQUARED;
const nohidden = 24;
const nooutput = 10;
const learningrate = .1;
let do_training = true;
const TRAINPERSTEP = 30;
const TESTPERSTEP = 5;
const ZOOMFACTOR = 7;
const ZOOMPIXELS = ZOOMFACTOR * PIXELS;
const canvaswidth = 2 * ZOOMPIXELS + 120;
const canvasheight = 3 * ZOOMPIXELS + 102;
const doodlewidth = PIXELS;
const DOODLE_THICK = 15;
const DOODLE_BLUR = 3;
let mnist;
let nn;
let canvas;
let dst;
let src;
let hierarchy;
let contours;
let img;
let diffX;
let diffY;
let M;
let doodle;
let demo;
let hidden_no_slider;
let learning_rate_slider;
let cvOutput = [];
let trainrun = 1;
let train_index = 0;
let testrun = 1;
let test_index = 0;
let total_tests = 0;
let total_correct = 0;
let doodle_exists = false;
let demo_exists = false;
let mousedrag = false;
var train_inputs;
var test_inputs;
var demo_inputs;
var doodle_inputs;
var thehtml;
function randomWeight() {
  return AB.randomFloatAtoB(-.5, .5);
}
$("#runheaderbox").css({
  "max-height" : "95vh"
}), thehtml = "<hr> <h1> 1. Doodle </h1> Top row: Doodle (left) and shrunk (right). <br>  Draw your doodle in top LHS. <button onclick='wipeDoodle();' class='normbutton' >Clear doodle</button> <br> ", AB.msg(thehtml, 1), thehtml = "<hr> <h1> 2. Training </h1> Middle row: Training image magnified (left) and original (right). <br>   <button onclick='do_training = false;' class='normbutton' >Stop training</button> <br> ", AB.msg(thehtml, 3), thehtml = "<h3> Hidden tests </h3> ", AB.msg(thehtml, 5), 
thehtml = "<hr> <h1> 3. Demo </h1> Bottom row: Test image magnified (left) and  original (right). <br> The network is <i>not</i> trained on any of these images. <br>  <button onclick='makeDemo();' class='normbutton' >Demo test image</button> <br> ", AB.msg(thehtml, 7);
const greenspan = "<span style='font-weight:bold; font-size:x-large; color:darkgreen'> ";
function setup() {
  (canvas = createCanvas(canvaswidth, canvasheight)).position(10, 20);
  canvas.background(51, 51, 51);
  (doodle = createGraphics(ZOOMPIXELS, ZOOMPIXELS)).pixelDensity(1);
  wipeDoodle();
  strokeWeight(3);
  stroke(255, 0, 0);
  rect(0, 0, ZOOMPIXELS, ZOOMPIXELS);
  textSize(20);
  textAlign(CENTER);
  text("DOODLE AREA", ZOOMPIXELS / 2, ZOOMPIXELS / 2.2);
  rect(0, ZOOMPIXELS + 50, ZOOMPIXELS, ZOOMPIXELS);
  rect(0, canvasheight - ZOOMPIXELS - 2, ZOOMPIXELS, ZOOMPIXELS);
  textSize(16);
  textAlign(CENTER);
  text(" DEMO AREA ", 100, canvasheight - ZOOMPIXELS / 1.8);
  rect(ZOOMPIXELS + 20, ZOOMPIXELS + 100, ZOOMPIXELS + 90, ZOOMPIXELS + 100);
  text("HIDDEN_NO SLIDER ", ZOOMPIXELS + 120, ZOOMPIXELS + 130);
  (hidden_no_slider = createSlider(10, 300, nohidden)).position(ZOOMPIXELS + 100, ZOOMPIXELS + 180);
  text("LEARNING_RATE SLIDER ", ZOOMPIXELS + 120, ZOOMPIXELS + 230);
  (learning_rate_slider = createSlider(.01, .2, learningrate)).position(ZOOMPIXELS + 100, ZOOMPIXELS + 280);
  AB.loadingScreen();
  $.getScript("/uploads/finally/opencv.js", function() {
    $.getScript("/uploads/codingtrain/matrix.js", function() {
      $.getScript("/uploads/finally/nn.js", function() {
        $.getScript("/uploads/codingtrain/mnist.js", function() {
          console.log("All JS loaded");
          (nn = new NeuralNetwork(noinput, nohidden, nooutput)).setLearningRate(learningrate);
          loadData();
        });
      });
    });
  });
}
function loadData() {
  loadMNIST(function(canCreateDiscussions) {
    mnist = canCreateDiscussions;
    console.log("All data loaded into mnist object:");
    console.log(mnist);
    AB.removeLoading();
  });
}
function getImage(id) {
  let img = createImage(PIXELS, PIXELS);
  img.loadPixels();
  for (let i = 0; i < PIXELSSQUARED; i++) {
    let s = id[i];
    let index = 4 * i;
    img.pixels[index + 0] = s;
    img.pixels[index + 1] = s;
    img.pixels[index + 2] = s;
    img.pixels[index + 3] = 255;
  }
  return img.updatePixels(), img;
}
function getInputs(id) {
  let dash = [];
  for (let i = 0; i < PIXELSSQUARED; i++) {
    let val = id[i];
    dash[i] = val / 255;
  }
  return dash;
}
function trainit(canCreateDiscussions) {
  let id = mnist.train_images[train_index];
  let geneSymbol = mnist.train_labels[train_index];
  if (canCreateDiscussions) {
    var img = getImage(id);
    image(img, 0, ZOOMPIXELS + 50, ZOOMPIXELS, ZOOMPIXELS);
    image(img, ZOOMPIXELS + 50, ZOOMPIXELS + 50, PIXELS, PIXELS);
  }
  let data = getInputs(id);
  let sample = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  sample[geneSymbol] = 1;
  train_inputs = data;
  nn.train(data, sample);
  thehtml = " trainrun: " + trainrun + "<br> no: " + train_index;
  AB.msg(thehtml, 4);
  if (++train_index == NOTRAIN) {
    train_index = 0;
    console.log("finished trainrun: " + trainrun);
    trainrun++;
  }
}
function testit() {
  let id = mnist.test_images[test_index];
  let e = mnist.test_labels[test_index];
  let sample = getInputs(id);
  test_inputs = sample;
  let place = findMax(nn.predict(sample));
  total_tests++;
  if (place == e) {
    total_correct++;
  }
  let e_total = total_correct / total_tests * 100;
  thehtml = " testrun: " + testrun + "<br> no: " + total_tests + " <br>  correct: " + total_correct + "<br>  score: " + greenspan + e_total.toFixed(2) + "</span>";
  AB.msg(thehtml, 6);
  if (++test_index == NOTEST) {
    console.log("finished testrun: " + testrun + " score: " + e_total.toFixed(2));
    testrun++;
    test_index = 0;
    total_tests = 0;
    total_correct = 0;
  }
}
function find123(p) {
  let lastWhiteSpace = 0;
  let firstNonzero = 0;
  let xmax = 0;
  let ymax = 0;
  let bestcode = 0;
  let maxv = 0;
  for (let i = 0; i < p.length; i++) {
    if (p[i] > xmax) {
      lastWhiteSpace = i;
      xmax = p[i];
    } else {
      if (p[i] > ymax) {
        firstNonzero = i;
        ymax = p[i];
      } else {
        if (p[i] > maxv) {
          bestcode = i;
          maxv = p[i];
        }
      }
    }
  }
  return [lastWhiteSpace, firstNonzero, bestcode];
}
function findMax(array) {
  let maxI = 0;
  let maxValue = 0;
  for (let i = 0; i < array.length; i++) {
    if (array[i] > maxValue) {
      maxI = i;
      maxValue = array[i];
    }
  }
  return maxI;
}
function draw() {
  if (void 0 !== mnist) {
    if (strokeWeight(3), stroke(255, 0, 0), rect(0, 0, ZOOMPIXELS, ZOOMPIXELS), textSize(20), textAlign(CENTER), text("DOODLE AREA", ZOOMPIXELS / 2, ZOOMPIXELS / 2.2), do_training) {
      for (let t = 0; t < TRAINPERSTEP; t++) {
        trainit(0 === t);
      }
      for (let t = 0; t < TESTPERSTEP; t++) {
        testit();
      }
    }
    if (demo_exists && (drawDemo(), guessDemo()), doodle_exists && (drawDoodle(), guessDoodle()), mouseIsPressed) {
      var left = ZOOMPIXELS - 2;
      if (mouseX < left && mouseY < left && pmouseX < left && pmouseY < left) {
        mousedrag = true;
        doodle_exists = true;
        doodle.stroke("cream");
        strokeJoin(BEVEL);
        doodle.strokeWeight(DOODLE_THICK);
        doodle.line(mouseX, mouseY, pmouseX, pmouseY);
      }
    } else {
      if (mousedrag) {
        mousedrag = false;
        console.log("Doodle detected");
        (img = doodle.get()).resize(PIXELS, PIXELS);
        img.loadPixels();
        imagedata = img.imageData;
        src = cv.matFromImageData(imagedata);
        dst = cv.Mat.zeros(src.cols, src.rows, cv.CV_8UC3);
        cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
        cv.threshold(src, src, 120, 255, cv.THRESH_BINARY);
        contours = new cv.MatVector;
        hierarchy = new cv.Mat;
        cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);
        let newColIndexD = contours.get(0);
        let moments = cv.moments(newColIndexD, 0);
        M = moments.m00;
        let _lastleft = Math.round(moments.m10 / moments.m00 * 100) / 100;
        let _lasttop = Math.round(moments.m01 / moments.m00 * 100) / 100;
        let bbcx = PIXELS / 2;
        let bbcy = PIXELS / 2;
        diffX = Math.round(bbcx - _lastleft);
        diffY = Math.round(bbcy - _lasttop);
        let dt = cv.matFromArray(2, 3, cv.CV_64FC1, [1, 0, diffX, 0, 1, diffY]);
        return dsize = new cv.Size(src.rows, src.cols), cv.warpAffine(src, dst, dt, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar), cvOutput = getInputs(dst.data8S), image(img, ZOOMPIXELS + 120 + diffX * ZOOMFACTOR, 0 + diffY * ZOOMFACTOR, ZOOMPIXELS, ZOOMPIXELS), cvOutput;
      }
    }
  }
}
function makeDemo() {
  demo_exists = true;
  var i = AB.randomIntAtoB(0, NOTEST - 1);
  demo = mnist.test_images[i];
  var beforeTab = mnist.test_labels[i];
  thehtml = "Test image no: " + i + "<br>Classification: " + beforeTab + "<br>";
  AB.msg(thehtml, 8);
}
function drawDemo() {
  var sal = getImage(demo);
  image(sal, 0, canvasheight - ZOOMPIXELS, ZOOMPIXELS, ZOOMPIXELS);
  image(sal, ZOOMPIXELS + 50, canvasheight - ZOOMPIXELS, PIXELS, PIXELS);
}
function guessDemo() {
  let sample = getInputs(demo);
  demo_inputs = sample;
  let e = findMax(nn.predict(sample));
  thehtml = " We classify it as: " + greenspan + e + "</span>";
  AB.msg(thehtml, 9);
}
function drawDoodle() {
  let sal = doodle.get();
  image(sal, 0, 0, ZOOMPIXELS, ZOOMPIXELS);
  image(sal, ZOOMPIXELS + 50, 0, PIXELS, PIXELS);
}
let lastT = 0;
function guessDoodle() {
  (img = doodle.get()).resize(PIXELS, PIXELS);
  img.loadPixels();
  let inputs = [];
  for (let i = 0; i < PIXELSSQUARED; i++) {
    inputs[i] = img.pixels[4 * i] / 255;
  }
  let input = Array.from(inputs);
  for (let i = 0; i < input.length; i++) {
    input[i] = cvOutput[i];
  }
  inputs = Array.from(input);
  for (let i = 0; i < inputs.length; i++) {
    inputs[i] = -255 * inputs[i];
  }
  doodle_inputs = inputs;
  let i = find123(nn.predict(inputs));
  thehtml = " We classify it as: " + greenspan + i[0] + "</span> <br> No.2 guess is: " + greenspan + i[1] + "</span> <br>No.3 guess is: " + greenspan + i[2] + "</span>";
  AB.msg(thehtml, 2);
  AB.msg(thehtml, 2);
}
function wipeDoodle() {
  doodle_exists = false;
  doodle.background("black");
}
function showInputs(groups) {
  var stderr = "";
  for (let i = 0; i < groups.length; i++) {
    if (i % PIXELS == 0) {
      stderr = stderr + "\n";
    }
    stderr = stderr + " " + groups[i].toFixed(2);
  }
  console.log(stderr);
};