Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="[A Markov Model Simulator]" />
<meta charset=utf-8 />
  <title>JS Bin</title>
  <script type="text/javascript" src="http://dat-gui.googlecode.com/git/build/dat.gui.min.js"></script>
  <script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.0-beta2.js"></script>
  <script src="https://raw.github.com/anomal/RainbowVis-JS/master/rainbowvis.js"></script>  
  <script src="http://numericjs.com/numeric/lib/numeric-1.2.6.min.js"></script>
</head>
<body>
  <div id="container"></div>
</body>
</html>
 
#container {
  border: 2px solid gray;
  width: 500px;
  height: 800px;
}
 
//generate a transition matrix that is stickier as sigma is lower.
function randomBandedTransMatrix(n, sigma) {
 
  var A = [];
  for(i = 0; i < n; i++) {
    var row = [];
    var sum = 0;
    for(j = 0; j < n; j++) {
      var dist = j - i;
      dist = dist - Math.round(dist / n) * n;
      row[j] = Math.exp(-dist * dist / ( 2 * sigma * sigma));
      sum += row[j];
    }
    A[i] = numeric.div(row, sum);
  }
  
  return(A);
  
}
//generate a path given an initial distribution, a transition probability matrix and number of elements.
function genPath(pi, A, n) {
  var r = Math.random();
  var x = pi[0];
  var path = [0];
  while(x < r && path[0] < pi.length - 1) {
    path[0] += 1;
    x += pi[ path[0] ];
  }
  
  for(i = 1; i < n; i++) {
    r = Math.random();
    x = A[ path[ i - 1 ] ][ 0 ];
    path.push(0);
    while(x < r && path[i] < A.length - 1) {
      path[i] += 1;
      x += A[ path[i - 1] ][ path[i] ];
    }
  }
  return(path);
}
//sample and then move a path forward
function updatePath(path, A) {
  var i;
  for(i = 0; i < path.length - 2; i++) {
    path[i] = path[i + 1]; 
  }
  
  //sample last section
  //i = path.length - 1;
  var r = Math.random();
  var x = A[ path[ i - 1 ] ][ 0 ];
  path[i] = 0;
  while(x < r && path[i] < A.length - 1) {
    path[i] += 1;
    x += A[ path[i - 1] ][ path[i] ];
  }
  return(path);
  
}
var Grid = function (nx, ny, dx, dy) {
  this.nx = nx;
  this.ny = ny;
  this.dx = dx;
  this.dy = dy;
  this.colorSpec = new Rainbow();
  this.colorSpec.setSpectrum('blue', 'red');
  this.colorSpec.setNumberRange(0, 125 / ny);
  this.radius = 10;
  this.nodes = [];
  this.lines = [];
  for(i = 0; i < nx; i++) {
    for(j = 0; j < ny; j++) {
      this.nodes.push(new Kinetic.Circle({
        x: i * dx + this.radius*1.5,
        y: j * dy + this.radius*1.5,
        radius: this.radius,
        fill: 'white',
        stroke: 'black',
        strokeWidth: 2
      }));
    }
  }
};
Grid.prototype = {
  render: function(nodeLayer, pathLayer) {
    for(i = 0; i < this.lines.length; i++) {
      pathLayer.add(this.lines[i]);
    }
    for(i = 0; i < this.nodes.length; i++) {
      nodeLayer.add(this.nodes[i]);
    }
  },
  cleanup: function() {
    for(i = 0; i < this.lines.length; i++) {
      this.lines[i].remove();
    }
    for(i = 0; i < this.nodes.length; i++) {
      this.nodes[i].remove();
    }
  },
  setNodeValue: function(i,j,v) {
    this.nodes[i * this.ny + j].setFill(this.colorSpec.colourAt(v));
  },
  setNodeVector: function(i, v) {
    for(j = 0; j < v.length; j++) {
      this.nodes[i * this.ny + j].setFill(this.colorSpec.colourAt(v[j]));
    }
  },
  setColourRange: function(lo, hi) {
   this.colorSpec.setNumberRange(lo, hi);
  },
  enablePath: function(i, j, k, layer) {
    this.lines.push(new Kinetic.Line({
      points: [i * this.dx + this.radius *2, j * this.dy + this.radius *1.5,(i + 1) * this.dx + this.radius, k * this.dy + this.radius*1.5],
       stroke: 'black',
       strokeWidth: 2
          }));
    layer.add(this.lines[this.lines.length - 1]);
  }, 
  clearPaths: function() {
    for(i = 0; i < this.lines.length; i++) {
      this.lines[i].remove(); 
    }
    this.lines = [];
  },
  clearNodes: function() {
    for(i = 0; i < this.nodes.length; i++) {
      this.nodes[i].setFill('white');
    }
  },
  enablePathVector: function(path, layer) {
    for(i = 0; i < path.length - 1; i++) {
      this.lines.push(new Kinetic.Line({
      points: [i * this.dx + this.radius *2, path[i] * this.dy + this.radius *1.5,(i + 1) * this.dx + this.radius,  path[i + 1] * this.dy + this.radius*1.5],
       stroke: 'black',
       strokeWidth: 2
       }));
      layer.add(this.lines[this.lines.length - 1]);
    }
  }
};
var Model = function(nx, ny, sigma, nodeLayer, pathLayer) {
  this.nx = nx;
  this.dx = stage.getWidth() / (nx - 2);
  this.ny = ny;
  this.dy = stage.getHeight() / ny;
  this.grid = new Grid(nx, ny, this.dx, this.dy);
  this.nodeLayer = nodeLayer;
  this.pathLayer = pathLayer;
  this.grid.render(this.nodeLayer, this.pathLayer);
  this.A = randomBandedTransMatrix(this.ny, sigma);
  this.pi = numeric.random([this.ny]);
  this.pi = numeric.div(this.pi, numeric.sum(this.pi));
  this.path = genPath(this.pi, this.A, this.nx);
  
  var temp = this.pi;
  for(i = 0; i < nx; i++) {
    this.grid.setNodeVector(i, numeric.mul(temp, 100));
    temp = numeric.dotVM(temp, this.A);
  }
};
var stage = new Kinetic.Stage({
  container: 'container',
  width: 500,
  height: 800
});
Model.prototype = {
  cleanup: function() {
    this.grid.cleanup();
  }
};
var nodeLayer = new Kinetic.Layer();
var pathLayer = new Kinetic.Layer();
stage.add(pathLayer);
stage.add(nodeLayer);
function makeAnimation(speed, model, marginals) {
  var nx_index = 0;
   var anim = new Kinetic.Animation(function(frame) {
     var time = frame.time,
          timeDiff = frame.timeDiff,
          frameRate = frame.frameRate;
     var trans = (stage.getX() - (timeDiff) * speed) % model.dx >= stage.getX();
     stage.setX((stage.getX()- timeDiff * speed) % model.dx);
     if(trans) {
       if(marginals) {
         var temp = model.pi;
          for(i = 0; i < model.nx; i++) {
          model.grid.setNodeVector(i, numeric.mul(temp, 100));
          temp = numeric.dotVM(temp, model.A);
          }
          model.pi = numeric.dotVM(model.pi, model.A); 
       }
       nx_index++;
       model.grid.clearPaths();
       model.path = updatePath(model.path, model.A);
       model.grid.enablePathVector(model.path, model.pathLayer);
    
      }
}, stage);
  return anim;
}
function setupModel(data) {
  
  data.model.cleanup();
  data.anim.stop();
  
  var new_model = new Model(data.xDensity + 1, data.yDensity, data.variance / 5, nodeLayer, pathLayer);
  
  //copy path if possible
  if(data.xDensity + 1 == data.model.nx && data.yDensity == data.model.ny) {
    new_model.path = data.model.path;
    new_model.grid.enablePathVector(new_model.path, new_model.pathLayer);
  }
  
  delete data.model;
  
  data.model = new_model;
  
  if(!data.marginals) {
      data.model.grid.clearNodes();
  }
  
  stage.draw();
  
  data.anim = makeAnimation(data.speed/100, data.model, data.marginals);
  if(data.running) {
    data.anim.start(); 
  }
  
}
var GUIData = function() {
  this.model = new Model(9,5, 1, nodeLayer, pathLayer);
  this.model.grid.clearNodes();
  stage.draw();
  this.xDensity = 8;
  this.yDensity = 5;
  this.variance = 5;
  this.speed = 10;
  this.marginals = false;
  this.running = false;
  this.anim = makeAnimation(this.speed/100, this.model, this.marginals);
  this.start = function() {
    this.running = true;
    this.anim.start();}; 
  this.stop = function() {
    this.running = false;
    this.anim.stop();};
};
window.onload = function() {
  var data = new GUIData();
  var gui = new dat.GUI();
  
  gui.add(data, "start");
  gui.add(data, "stop");
  
  var nxcon = gui.add(data, "xDensity").min(3).max(20).step(1);
  nxcon.onChange(function(value) {
    data.xDensity = value;
    setupModel(data);
  });
  
  var nycon = gui.add(data, "yDensity").min(2).max(20).step(1);
  nycon.onChange(function(value) {
    data.yDensity = value;
    setupModel(data);
  });
  
  var speedcon = gui.add(data, "speed").min(5).max(100).step(5);
  speedcon.onChange(function(value) {
    data.speed = value;
    setupModel(data);
  });
    
  var stickycon = gui.add(data, "variance").min(1).max(25).step(1);
  stickycon.onChange(function(value) {
    data.variance = value;
    setupModel(data);
  });
  var marcon = gui.add(data, "marginals");
  marcon.onChange(function(value) {
    data.marginals = value;
    setupModel(data);
  });  
  
  data.anim.start();
  data.running = true;
  gui.close();
};
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
white.d.andrewpro
0viewers