Simulation
ALM
OJS
Interactive
Modified

January 9, 2024

Code
pacman::p_load(tidyverse)
d <- tibble(x=1:20,y=x^2)
ojs_define(d = d)

inputNodes = seq(1,7,1)  # 
outputNodes = seq(50,1600,50)
#wm=matrix(rnorm(length(inputNodes)*length(outputNodes),5,2),nrow=length(outputNodes),ncol=length(inputNodes))
#ojs_define(iN = inputNodes, outputNodes = outputNodes, wm = wm)
Code
d3 = require("d3@7")
math = require('mathjs')
// let inputNodes = Array.from({ length: 7 }, (_, i) => i + 1);
// let outputNodes = Array.from({ length: 32 }, (_, i) => (i + 1) * 50);
// let wm = Array.from({ length: outputNodes.length }, () =>
//   Array.from({ length: inputNodes.length }, () => 0.0)
// );

function inputActivation(xTarget, c) {
  console.log(inputNodes)
  return inputNodes.map((inputNode) =>
    Math.exp(-1 * c * Math.pow(xTarget - inputNode, 2))
  );
}


function outputActivation(xTarget, weights, c) {
  const inputAct = inputActivation(xTarget, c);
  return math.multiply(weights, inputAct);
}

function meanPrediction(xTarget, weights, c) {
  const outputAct = outputActivation(xTarget, weights, c);
  const probability = math.divide(outputAct, math.sum(outputAct));
  return math.multiply(outputNodes, probability);
}


function updateWeights(xNew, yNew, weights, c, lr) {
  const yFeedbackActivation = outputNodes.map(
    (outputNode) => Math.exp(-1 * c * Math.pow(yNew - outputNode, 2))
  );
 //console.log(yFeedbackActivation)
  const xFeedbackActivation = outputActivation(xNew, weights, c);
  const inputAct = inputActivation(xNew, c);
  const inputActReshaped = math.reshape(inputAct, [inputAct.length, 1]);
  const yFeedbackActivationReshaped = math.reshape(yFeedbackActivation, [yFeedbackActivation.length, 1]);
  const xFeedbackActivationReshaped = math.reshape(xFeedbackActivation, [xFeedbackActivation.length, 1]);
  const error = math.reshape(math.subtract(yFeedbackActivationReshaped, xFeedbackActivationReshaped), [yFeedbackActivation.length, 1]);
  // console.log(math.size(math.transpose(inputActReshaped)))
  const weightUpdate = math.multiply(error, math.transpose(inputActReshaped));
  //console.log(weightUpdate)
  const raw_Weights = math.add(weights, math.multiply(lr, weightUpdate));

  const new_Weights = raw_Weights
  //return JSON.parse(result);
  return(new_Weights)
}

function randomNormal(mean, sd) {
  let u = 0,
    v = 0;
  while (u === 0) u = Math.random();
  while (v === 0) v = Math.random();
  const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
  return mean + z * sd;
}

function examPrediction(xTarget, weights, c, trainVec) {
  const nearestTrain = trainVec[math.argmin(math.abs(trainVec - xTarget))];
  const aResp = meanPrediction(nearestTrain, weights, c);
  const xUnder = math.min(trainVec) === nearestTrain ? nearestTrain : trainVec[math.findIndex(trainVec, (d) => d === nearestTrain) - 1];
  const xOver = math.max(trainVec) === nearestTrain ? nearestTrain : trainVec[math.findIndex(trainVec, (d) => d === nearestTrain) + 1];
  const mUnder = meanPrediction(xUnder, weights, c);
  const mOver = meanPrediction(xOver, weights, c);
  const examOutput = math.round(aResp + ((mOver - mUnder) / (xOver - xUnder)) * (xTarget - nearestTrain), 3);
  return examOutput;
}


function trainALM(dat, c, lr, weights) {
    console.log('training')
  const almTrain = new Array(dat.input.length).fill(NaN);
  for (let i = 0; i < dat.input.length; i++) {
    console.log('i: ', i, ' dat.input[i]: ', dat.input[i], ' dat.vx[i]: ', dat.vx[i], ' c: ', c, ' lr: ', lr)
    weights = updateWeights(dat.input[i], dat.vx[i], weights, c, lr);
    const resp = math.round(meanPrediction(dat.input[i], weights, c),0);
    // round resp to 1 decimal place
    almTrain[i] = resp;
      weights = math.map(weights, (value) => {
        return value < 0 ? 0 : value;
      });
  }
  console.log('almTrain: ', almTrain)
  console.log(weights)
  return {almTrain, weights};
}

function trainTestALM(dat, c = 0.05, lr = 0.5, weights, testVec) {
  const almTrain = new Array(dat.length).fill(NaN);
  
  for (let i = 0; i < dat.length; i++) {
    weights = updateWeights(dat[i].input, dat[i].vx, weights, c, lr);
    const resp = meanPrediction(dat[i].input, weights, c);
    almTrain[i] = resp;
    weights = math.map(weights, (value) => {
      return value < 0 ? 0 : value;
    });
  }

  const almPred = testVec.map((value) => {
    return meanPrediction(value, weights, c);
  });

  const examPred = testVec.map((value) => {
    return examPrediction(value, weights, c, [1, ...math.sort(math.unique(dat.map((d) => d.input)))]);
  });
    
  return { almTrain, almPred, examPred };
}

// Modify the sim_data function to accept the dataset as an argument
// function sim_data(dat, c=0.5, lr=0.2, inNodes=7, outNodes=32, trainVec=[5,6,7]) {
//   inputNodes = math.range(1,7,inNodes).toArray();  
//   outputNodes = math.range(50,1600,outNodes).toArray(); 
//   wm = math.zeros(outputNodes.length, inputNodes.length)._data;
//   tt = trainTest_alm(dat, c, lr, wm, trainVec);
// }

function gen_train(trainVec, trainRep, noise) {
   let bandVec=[0,100,350,600,800,1000,1200];
   let ts = [];
   for (let i=0; i<trainRep; i++) {
       ts.push(...trainVec);
   }
    let mean = 0;
    let stdDev = 1;
   //let noiseVec = math.random([ts.length])._data;
   //noiseVec = math.multiply(noiseVec, noise)._data;
   //if(noise==0) {noiseVec=noiseVec*0}
   let inputArr = [];
   let vxArr = [];
   for (let i=0; i<ts.length; i++) {
       inputArr.push(ts[i]);
       vxArr.push(bandVec[ts[i]]);
       //vxArr.push(bandVec[ts[i]]+noiseVec[i]);
   }
   return {input: inputArr, vx: vxArr};
}

Simulation

Code
viewof c = Inputs.range([.0001, 2], {value: .00005, step: .05, label: "c value:"})
viewof lr = Inputs.range([.001, 2], {value: .05, step: .01, label: "lr value:"})

viewof n_inputNodes = Inputs.range([1, 50], {value: 7, step: 1, label: "N Input Nodes:"})
viewof n_outputNodes = Inputs.range([1, 200], {value: 32, step: 1, label: "N Output Nodes:"})

viewof weight_mean = Inputs.range([0, 1], {value: 0, step: .0005, label: "initial weight mean:"})
viewof weight_sd = Inputs.range([.00000001, 1], {value: .000001, step: .0001, label: "initial weight sd:"})

viewof trainRep = Inputs.range([4, 50], {value: 1, step: 1, label: "Train Reps:"})

 //inputNodes = Array.from({ length: n_inputNodes }, (_, i) => i + 1);
 inputNodes = Array.from({ length: n_inputNodes }, (_, i) => 1 + i * (7 - 1) / (n_inputNodes - 1));



start = 0;
end = 1800;
N_Steps = n_outputNodes; // replace with desired length
stepSize = (end - start) / (N_Steps - 1);
outputNodes = Array.from({ length: N_Steps }, (_, i) => start + i * stepSize);

console.log(inputNodes)
Code
console.log(outputNodes)
Code
wm = outputNodes.map(() => {
  return inputNodes.map(() => randomNormal(weight_mean, weight_sd));
});

noise=0
viewof trainVec = Inputs.checkbox([1, 2, 3, 4, 5, 6], {value: [4,5,6], label: "Select training examples:"});
gd = gen_train(trainVec, trainRep, noise);

//trainVec = [1,2,4,5,6];
//gd = gen_train(trainVec, trainRep, noise)
// w2= updateWeights(4, 800, wm,c,lr)

talm = trainALM(gd, c, lr, wm);

//inputNodes = transpose(iN)
ia = inputActivation(inputX, c, inputNodes)
oa = outputActivation(inputX, wm, c)
mp = meanPrediction(inputX, wm, c)
Code
tdat = gd.vx.map((value, index) => {
  return { Trial: index, Vx: value, Response: talm.almTrain[index], Error: Math.abs(value -  talm.almTrain[index]) };
});
Code
Plot.plot({
  marks: [
    Plot.line(tdat, {
      x: "Trial",      // feature for the x channel
      y: "Response",     // feature for the y channel
      stroke: "Vx",     
    }),
  ],
  x: {label: "Trial Number"},
  y: {label: "Vx", domain: [0, 1800],grid: true},
  color: {legend: true, scheme: "Turbo",type: "categorical"},
  width: 400,
  height: 400
});
  //caption: html`Figure 1. This chart has a <i>fancy</i> caption.`
Plot.plot({
  marks: [
    Plot.line(tdat, {
      x: "Trial",      // feature for the x channel
      y: "Error",     // feature for the y channel
      stroke: "Vx",     // feature for the fill channel
    }),
  ],
  y: {label: "Error",grid: true},
  color: {legend: true, scheme: "Turbo",type: "categorical"},
  width: 400,
  height: 400
});

Vx Across Training

Training Error

Weight Matrices

Code
Plotly = require("https://cdn.plot.ly/plotly-latest.min.js")
//div = DOM.element('div');
P1=Plotly.newPlot("plot-canvas", [{
  z: wm,
  x: outputNodes,
  y: inputNodes,
  type: 'heatmap',
  colorscale: 'Viridis'
}],{width:500});

console.log(inputNodes)
Code
console.log(outputNodes)
Code
P2=Plotly.newPlot("plot-tw", [{
  z: talm.weights,
  x: inputNodes,
  y: outputNodes,
  type: 'heatmap',
  colorscale: 'Viridis'
}],{width:600});

Starting Weights

Final Weights

Input and Output layer activations

Code
in_data = ia.map((value, index) => {
  return { Node: inputNodes[index], Activation: value };
});

 out_data = oa.map((value, index) => {
  return { Node: outputNodes[index], Activation: value };
});

viewof inputX = Inputs.range([1, 7], {value: 4, step: 1, label: "input value:"})
Code
Plot.plot({
  marks: [
    Plot.dot(in_data, {
      x: "Node",      // feature for the x channel
      y: "Activation",     // feature for the y channel
    }),
  ],
  width: 400,
  height: 200,
  title: "Input Activation Plot",
});
Plot.plot({
  marks: [
    Plot.dot(out_data, {
      x: "Node",      // feature for the x channel
      y: "Activation",     // feature for the y channel
    }),
  ],
  width: 400,
  height: 200,
  title: "Output Activation Plot",
});

Input Activation

Second

Charts

Code
console.log(wm)
Code
console.log(talm.weights)

Testing

Code
viewof xV = Inputs.range(
  [1, 20], 
  {value: 1, step: 1, label: "x range:"}
)
viewof yV = Inputs.range(
  [1, 400], 
  {value: 1, step: 10, label: "y range:"}
)

dO = transpose(d)
filtered = dO.filter(function(dO) {
  return dO.x>=xV && dO.y >= yV;
})
Code
Plot.plot({
  marks: [
    Plot.dot(filtered, 
      { x: "x", y: "y"}, 
      { stroke: "black" }
    )
  ]
})

x: y:

Code
//Plot = require("plot")
Plot.plot({
  marks: [
    Plot.line(transpose(d), 
      { x: "x", y: "y"}, 
      { stroke: "black" }
    )
  ]
})
```{ojs}
//| include: false
```