PageSourceSearch

https://otoro.net/pendulum-2.00.js

js otoro.net collected 2026-09-24 10:06:31 UTC 158,476 bytes, 4,845 lines download raw bytes

1// double pendulum - one file.
2
3/*    
4
5@licstart  The following is the entire license notice for the 
6JavaScript code in this page.
7
8Copyright (C) 2015 david ha, otoro.net, otoro labs
9
10The JavaScript code in this page is free software: you can
11redistribute it and/or modify it under the terms of the GNU
12General Public License (GNU GPL) as published by the Free Software
13Foundation, either version 3 of the License, or (at your option)
14any later version.  The code is distributed WITHOUT ANY WARRANTY;
15without even the implied warranty of MERCHANTABILITY or FITNESS
16FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
17
18As additional permission under GNU GPL version 3 section 7, you
19may distribute non-source (e.g., minimized or compacted) forms of
20that code without the copy of the GNU GPL normally required by
21section 4, provided you include this license notice and a URL
22through which recipients can access the Corresponding Source.   
23
24
25@licend  The above is the entire license notice
26for the JavaScript code in this page.
27*/
28
29
30// -----------------------------------------------------------------------------
31// Scale Methods
32// -----------------------------------------------------------------------------
33
34// supposed to translate (x_b2, y_b2) -> (x_pixel, y_pixel).  everything else scaled by a factor of scaleFactor
35var b2Camera = { 
36  scaleFactor : 10,
37  x_b2: 0,
38  y_b2: 0,
39  x_pixel: 0,
40  y_pixel: 0
41};
42
43var gravity;
44var gravityStrength = 20;
45
46var scaleToWorld = function(a,b) {
47  var newv;
48  if (a instanceof box2d.b2Vec2) {
49    newv = new box2d.b2Vec2();
50    newv.x = (a.x-b2Camera.x_pixel)/b2Camera.scaleFactor+b2Camera.x_b2;
51    newv.y = (a.y-b2Camera.y_pixel)/b2Camera.scaleFactor+b2Camera.y_b2;
52    return newv;
53  } else if ("undefined"!=typeof b) {
54    newv = new box2d.b2Vec2();
55    newv.x = (a-b2Camera.x_pixel)/b2Camera.scaleFactor+b2Camera.x_b2;
56    newv.y = (b-b2Camera.y_pixel)/b2Camera.scaleFactor+b2Camera.y_b2;
57    return newv;
58  } else {
59    return a/b2Camera.scaleFactor;
60  }
61};
62
63
64var makeB2Vec2 = function(a,b) {
65  var newv;
66  newv = new box2d.b2Vec2();
67  newv.x = (a)/1;
68  newv.y = (b)/1;
69  return newv;
70};
71
72var scaleToPixels = function(a,b) {
73  var newv;
74  if (a instanceof box2d.b2Vec2) {
75    newv = new box2d.b2Vec2();
76    newv.x = (a.x-b2Camera.x_b2)*b2Camera.scaleFactor+b2Camera.x_pixel;
77    newv.y = (a.y-b2Camera.y_b2)*b2Camera.scaleFactor+b2Camera.y_pixel;
78    return newv;
79  } else if ("undefined"!=typeof b) {
80    newv = new box2d.b2Vec2();
81    newv.x = (a-b2Camera.x_b2)*b2Camera.scaleFactor+b2Camera.x_pixel;
82    newv.y = (b-b2Camera.y_b2)*b2Camera.scaleFactor+b2Camera.y_pixel;
83    return newv;
84  } else {
85    return a*b2Camera.scaleFactor;
86  }
87};
88
89// -----------------------------------------------------------------------------
90// Create Methods
91// -----------------------------------------------------------------------------
92
93var createWorld = function() {
94
95	var worldAABB = new box2d.b2AABB();
96	worldAABB.lowerBound.SetXY(-this.bounds, -this.bounds);
97	worldAABB.upperBound.SetXY(this.bounds, this.bounds);
98	gravity = new box2d.b2Vec2(0,gravityStrength);
99	var doSleep = true;
100
101	return new box2d.b2World(gravity, doSleep);
102};
103
104// -----------------------------------------------------------------------------
105// Draw Methods
106// -----------------------------------------------------------------------------
107
108var debugDraw = function(canvas, scale, world) {
109
110	var context = canvas.getContext('2d');
111  var j, b, f;
112  context.fillStyle = '#DDD';
113  context.fillRect(0, 0, canvas.width, canvas.height);
114
115	// Draw joints
116	for( j=world.m_jointList; j; j=j.m_next) {
117    context.lineWidth = 0.25;
118    context.strokeStyle = '#00F';
119    drawJoint(context, scale, world, j);
120  }
121
122	// Draw body shapes
123	for( b=world.m_bodyList; b; b=b.m_next) {
124		for( f = b.GetFixtureList(); f!==null; f=f.GetNext()) {  
125      context.lineWidth = 0.5;
126			context.strokeStyle = '#F00';
127      drawShape(context, scale, world, b, f);
128    }
129  }
130};
131
132var drawJoint = function(context, scale, world, joint) {
133	context.save();
134  context.scale(scale,scale);
135  context.lineWidth /= scale;
136
137  var b1 = joint.m_bodyA;
138  var b2 = joint.m_bodyB;
139  var x1 = b1.GetPosition();
140  var x2 = b2.GetPosition();
141  var p1 = joint.GetAnchorA();
142  var p2 = joint.GetAnchorB();
143
144  context.beginPath();
145  switch (joint.m_type) {
146    case box2d.b2Joint.e_distanceJoint:
147      context.moveTo(p1.x, p1.y);
148      context.lineTo(p2.x, p2.y);
149      break;
150    default: {
151      if (b1 == world.m_groundBody) {
152        context.moveTo(p1.x, p1.y);
153        context.lineTo(x2.x, x2.y);
154      }
155      else if (b2 == world.m_groundBody) {
156        context.moveTo(p1.x, p1.y);
157        context.lineTo(x1.x, x1.y);
158      }
159      else {
160        context.moveTo(x1.x, x1.y);
161        context.lineTo(p1.x, p1.y);
162        context.lineTo(x2.x, x2.y);
163        context.lineTo(p2.x, p2.y);
164      }
165    } break;
166  }
167  context.closePath();
168  context.stroke();
169  context.restore();
170};
171
172var drawShape = function(context, scale, world, body, fixture) {
173
174  context.save();
175  context.scale(scale,scale);
176
177  var bPos = body.GetPosition();
178  context.translate(bPos.x, bPos.y);
179  context.rotate(body.GetAngleRadians());
180  
181  context.beginPath();
182  context.lineWidth /= scale;
183
184	var shape = fixture.m_shape;
185  var i;
186  switch(shape.m_type) {
187    case box2d.b2ShapeType.e_circleShape: {
188      var r = shape.m_radius;
189      var segments = 16.0;
190      var theta = 0.0;
191      var dtheta = 2.0 * Math.PI / segments;
192
193      context.moveTo(r, 0);
194      for (i = 0; i < segments; i++) {
195        context.lineTo(r + r * Math.cos(theta), r * Math.sin(theta));
196        theta += dtheta;
197      }
198      context.lineTo(r, 0);
199    } break;
200
201    case box2d.b2ShapeType.e_polygonShape:
202    case box2d.b2ShapeType.e_chainShape: {
203
204      var vertices = shape.m_vertices;
205      var vertexCount = shape.m_count;
206      if (!vertexCount) return;
207
208      context.moveTo(vertices[0].x, vertices[0].y);
209      for (i = 0; i < vertexCount; i++)
210        context.lineTo(vertices[i].x, vertices[i].y);
211    } break;
212  }
213
214  context.closePath();
215  context.stroke();
216  context.restore();
217};
218
219var convnetjs = convnetjs || { REVISION: 'ALPHA' };
220(function(global) {
221  "use strict";
222
223  // Random number utilities
224  var return_v = false;
225  var v_val = 0.0;
226  var gaussRandom = function() {
227    if(return_v) { 
228      return_v = false;
229      return v_val; 
230    }
231    var u = 2*Math.random()-1;
232    var v = 2*Math.random()-1;
233    var r = u*u + v*v;
234    if(r == 0 || r > 1) return gaussRandom();
235    var c = Math.sqrt(-2*Math.log(r)/r);
236    v_val = v*c; // cache this
237    return_v = true;
238    return u*c;
239  }
240  var randf = function(a, b) { return Math.random()*(b-a)+a; }
241  var randi = function(a, b) { return Math.floor(Math.random()*(b-a)+a); }
242  var randn = function(mu, std){ return mu+gaussRandom()*std; }
243
244  // Array utilities
245  var zeros = function(n) {
246    if(typeof(n)==='undefined' || isNaN(n)) { return []; }
247    if(typeof ArrayBuffer === 'undefined') {
248      // lacking browser support
249      var arr = new Array(n);
250      for(var i=0;i<n;i++) { arr[i]= 0; }
251      return arr;
252    } else {
253      return new Float64Array(n);
254    }
255  }
256
257  var arrContains = function(arr, elt) {
258    for(var i=0,n=arr.length;i<n;i++) {
259      if(arr[i]===elt) return true;
260    }
261    return false;
262  }
263
264  var arrUnique = function(arr) {
265    var b = [];
266    for(var i=0,n=arr.length;i<n;i++) {
267      if(!arrContains(b, arr[i])) {
268        b.push(arr[i]);
269      }
270    }
271    return b;
272  }
273
274  // return max and min of a given non-empty array.
275  var maxmin = function(w) {
276    if(w.length === 0) { return {}; } // ... ;s
277    var maxv = w[0];
278    var minv = w[0];
279    var maxi = 0;
280    var mini = 0;
281    var n = w.length;
282    for(var i=1;i<n;i++) {
283      if(w[i] > maxv) { maxv = w[i]; maxi = i; } 
284      if(w[i] < minv) { minv = w[i]; mini = i; } 
285    }
286    return {maxi: maxi, maxv: maxv, mini: mini, minv: minv, dv:maxv-minv};
287  }
288
289  // create random permutation of numbers, in range [0...n-1]
290  var randperm = function(n) {
291    var i = n,
292        j = 0,
293        temp;
294    var array = [];
295    for(var q=0;q<n;q++)array[q]=q;
296    while (i--) {
297        j = Math.floor(Math.random() * (i+1));
298        temp = array[i];
299        array[i] = array[j];
300        array[j] = temp;
301    }
302    return array;
303  }
304
305  // sample from list lst according to probabilities in list probs
306  // the two lists are of same size, and probs adds up to 1
307  var weightedSample = function(lst, probs) {
308    var p = randf(0, 1.0);
309    var cumprob = 0.0;
310    for(var k=0,n=lst.length;k<n;k++) {
311      cumprob += probs[k];
312      if(p < cumprob) { return lst[k]; }
313    }
314  }
315
316  // syntactic sugar function for getting default parameter values
317  var getopt = function(opt, field_name, default_value) {
318    return typeof opt[field_name] !== 'undefined' ? opt[field_name] : default_value;
319  }
320
321  global.randf = randf;
322  global.randi = randi;
323  global.randn = randn;
324  global.zeros = zeros;
325  global.maxmin = maxmin;
326  global.randperm = randperm;
327  global.weightedSample = weightedSample;
328  global.arrUnique = arrUnique;
329  global.arrContains = arrContains;
330  global.getopt = getopt;
331  
332})(convnetjs);
333(function(global) {
334  "use strict";
335
336  // Vol is the basic building block of all data in a net.
337  // it is essentially just a 3D volume of numbers, with a
338  // width (sx), height (sy), and depth (depth).
339  // it is used to hold data for all filters, all volumes,
340  // all weights, and also stores all gradients w.r.t. 
341  // the data. c is optionally a value to initialize the volume
342  // with. If c is missing, fills the Vol with random numbers.
343  var Vol = function(sx, sy, depth, c) {
344    // this is how you check if a variable is an array. Oh, Javascript :)
345    if(Object.prototype.toString.call(sx) === '[object Array]') {
346      // we were given a list in sx, assume 1D volume and fill it up
347      this.sx = 1;
348      this.sy = 1;
349      this.depth = sx.length;
350      // we have to do the following copy because we want to use
351      // fast typed arrays, not an ordinary javascript array
352      this.w = global.zeros(this.depth);
353      this.dw = global.zeros(this.depth);
354      for(var i=0;i<this.depth;i++) {
355        this.w[i] = sx[i];
356      }
357    } else {
358      // we were given dimensions of the vol
359      this.sx = sx;
360      this.sy = sy;
361      this.depth = depth;
362      var n = sx*sy*depth;
363      this.w = global.zeros(n);
364      this.dw = global.zeros(n);
365      if(typeof c === 'undefined') {
366        // weight normalization is done to equalize the output
367        // variance of every neuron, otherwise neurons with a lot
368        // of incoming connections have outputs of larger variance
369        var scale = Math.sqrt(1.0/(sx*sy*depth));
370        for(var i=0;i<n;i++) { 
371          this.w[i] = global.randn(0.0, scale);
372        }
373      } else {
374        for(var i=0;i<n;i++) { 
375          this.w[i] = c;
376        }
377      }
378    }
379  }
380
381  Vol.prototype = {
382    get: function(x, y, d) { 
383      var ix=((this.sx * y)+x)*this.depth+d;
384      return this.w[ix];
385    },
386    set: function(x, y, d, v) { 
387      var ix=((this.sx * y)+x)*this.depth+d;
388      this.w[ix] = v; 
389    },
390    add: function(x, y, d, v) { 
391      var ix=((this.sx * y)+x)*this.depth+d;
392      this.w[ix] += v; 
393    },
394    get_grad: function(x, y, d) { 
395      var ix = ((this.sx * y)+x)*this.depth+d;
396      return this.dw[ix]; 
397    },
398    set_grad: function(x, y, d, v) { 
399      var ix = ((this.sx * y)+x)*this.depth+d;
400      this.dw[ix] = v; 
401    },
402    add_grad: function(x, y, d, v) { 
403      var ix = ((this.sx * y)+x)*this.depth+d;
404      this.dw[ix] += v; 
405    },
406    cloneAndZero: function() { return new Vol(this.sx, this.sy, this.depth, 0.0)},
407    clone: function() {
408      var V = new Vol(this.sx, this.sy, this.depth, 0.0);
409      var n = this.w.length;
410      for(var i=0;i<n;i++) { V.w[i] = this.w[i]; }
411      return V;
412    },
413    addFrom: function(V) { for(var k=0;k<this.w.length;k++) { this.w[k] += V.w[k]; }},
414    addFromScaled: function(V, a) { for(var k=0;k<this.w.length;k++) { this.w[k] += a*V.w[k]; }},
415    setConst: function(a) { for(var k=0;k<this.w.length;k++) { this.w[k] = a; }},
416
417    toJSON: function() {
418      // todo: we may want to only save d most significant digits to save space
419      var json = {}
420      json.sx = this.sx; 
421      json.sy = this.sy;
422      json.depth = this.depth;
423      json.w = this.w;
424      return json;
425      // we wont back up gradients to save space
426    },
427    fromJSON: function(json) {
428      this.sx = json.sx;
429      this.sy = json.sy;
430      this.depth = json.depth;
431
432      var n = this.sx*this.sy*this.depth;
433      this.w = global.zeros(n);
434      this.dw = global.zeros(n);
435      // copy over the elements.
436      for(var i=0;i<n;i++) {
437        this.w[i] = json.w[i];
438      }
439    }
440  }
441
442  global.Vol = Vol;
443})(convnetjs);
444(function(global) {
445  "use strict";
446  var Vol = global.Vol; // convenience
447
448  // Volume utilities
449  // intended for use with data augmentation
450  // crop is the size of output
451  // dx,dy are offset wrt incoming volume, of the shift
452  // fliplr is boolean on whether we also want to flip left<->right
453  var augment = function(V, crop, dx, dy, fliplr) {
454    // note assumes square outputs of size crop x crop
455    if(typeof(fliplr)==='undefined') var fliplr = false;
456    if(typeof(dx)==='undefined') var dx = global.randi(0, V.sx - crop);
457    if(typeof(dy)==='undefined') var dy = global.randi(0, V.sy - crop);
458    
459    // randomly sample a crop in the input volume
460    var W;
461    if(crop !== V.sx || dx!==0 || dy!==0) {
462      W = new Vol(crop, crop, V.depth, 0.0);
463      for(var x=0;x<crop;x++) {
464        for(var y=0;y<crop;y++) {
465          if(x+dx<0 || x+dx>=V.sx || y+dy<0 || y+dy>=V.sy) continue; // oob
466          for(var d=0;d<V.depth;d++) {
467           W.set(x,y,d,V.get(x+dx,y+dy,d)); // copy data over
468          }
469        }
470      }
471    } else {
472      W = V;
473    }
474
475    if(fliplr) {
476      // flip volume horziontally
477      var W2 = W.cloneAndZero();
478      for(var x=0;x<W.sx;x++) {
479        for(var y=0;y<W.sy;y++) {
480          for(var d=0;d<W.depth;d++) {
481           W2.set(x,y,d,W.get(W.sx - x - 1,y,d)); // copy data over
482          }
483        }
484      }
485      W = W2; //swap
486    }
487    return W;
488  }
489
490  // img is a DOM element that contains a loaded image
491  // returns a Vol of size (W, H, 4). 4 is for RGBA
492  var img_to_vol = function(img, convert_grayscale) {
493
494    if(typeof(convert_grayscale)==='undefined') var convert_grayscale = false;
495
496    var canvas = document.createElement('canvas');
497    canvas.width = img.width;
498    canvas.height = img.height;
499    var ctx = canvas.getContext("2d");
500
501    // due to a Firefox bug
502    try {
503      ctx.drawImage(img, 0, 0);
504    } catch (e) {
505      if (e.name === "NS_ERROR_NOT_AVAILABLE") {
506        // sometimes happens, lets just abort
507        return false;
508      } else {
509        throw e;
510      }
511    }
512
513    try {
514      var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
515    } catch (e) {
516      if(e.name === 'IndexSizeError') {
517        return false; // not sure what causes this sometimes but okay abort
518      } else {
519        throw e;
520      }
521    }
522
523    // prepare the input: get pixels and normalize them
524    var p = img_data.data;
525    var W = img.width;
526    var H = img.height;
527    var pv = []
528    for(var i=0;i<p.length;i++) {
529      pv.push(p[i]/255.0-0.5); // normalize image pixels to [-0.5, 0.5]
530    }
531    var x = new Vol(W, H, 4, 0.0); //input volume (image)
532    x.w = pv;
533
534    if(convert_grayscale) {
535      // flatten into depth=1 array
536      var x1 = new Vol(W, H, 1, 0.0);
537      for(var i=0;i<W;i++) {
538        for(var j=0;j<H;j++) {
539          x1.set(i,j,0,x.get(i,j,0));
540        }
541      }
542      x = x1;
543    }
544
545    return x;
546  }
547  
548  global.augment = augment;
549  global.img_to_vol = img_to_vol;
550
551})(convnetjs);
552(function(global) {
553  "use strict";
554  var Vol = global.Vol; // convenience
555
556  // This file contains all layers that do dot products with input,
557  // but usually in a different connectivity pattern and weight sharing
558  // schemes: 
559  // - FullyConn is fully connected dot products 
560  // - ConvLayer does convolutions (so weight sharing spatially)
561  // putting them together in one file because they are very similar
562  var ConvLayer = function(opt) {
563    var opt = opt || {};
564
565    // required
566    this.out_depth = opt.filters;
567    this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.
568    this.in_depth = opt.in_depth;
569    this.in_sx = opt.in_sx;
570    this.in_sy = opt.in_sy;
571    
572    // optional
573    this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
574    this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume
575    this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
576    this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
577    this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
578
579    // computed
580    // note we are doing floor, so if the strided convolution of the filter doesnt fit into the input
581    // volume exactly, the output volume will be trimmed and not contain the (incomplete) computed
582    // final application.
583    this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
584    this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
585    this.layer_type = 'conv';
586
587    // initializations
588    var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
589    this.filters = [];
590    for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }
591    this.biases = new Vol(1, 1, this.out_depth, bias);
592  }
593  ConvLayer.prototype = {
594    forward: function(V, is_training) {
595      this.in_act = V;
596
597      var A = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
598      for(var d=0;d<this.out_depth;d++) {
599        var f = this.filters[d];
600        var x = -this.pad;
601        var y = -this.pad;
602        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
603          y = -this.pad;
604          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
605
606            // convolve centered at this particular location
607            // could be bit more efficient, going for correctness first
608            var a = 0.0;
609            for(var fx=0;fx<f.sx;fx++) {
610              for(var fy=0;fy<f.sy;fy++) {
611                for(var fd=0;fd<f.depth;fd++) {
612                  var oy = y+fy; // coordinates in the original input array coordinates
613                  var ox = x+fx;
614                  if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {
615                    //a += f.get(fx, fy, fd) * V.get(ox, oy, fd);
616                    // avoid function call overhead for efficiency, compromise modularity :(
617                    a += f.w[((f.sx * fy)+fx)*f.depth+fd] * V.w[((V.sx * oy)+ox)*V.depth+fd];
618                  }
619                }
620              }
621            }
622            a += this.biases.w[d];
623            A.set(ax, ay, d, a);
624          }
625        }
626      }
627      this.out_act = A;
628      return this.out_act;
629    },
630    backward: function() { 
631
632      // compute gradient wrt weights, biases and input data
633      var V = this.in_act;
634      V.dw = global.zeros(V.w.length); // zero out gradient wrt bottom data, we're about to fill it
635      for(var d=0;d<this.out_depth;d++) {
636        var f = this.filters[d];
637        var x = -this.pad;
638        var y = -this.pad;
639        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
640          y = -this.pad;
641          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
642            // convolve and add up the gradients. 
643            // could be more efficient, going for correctness first
644            var chain_grad = this.out_act.get_grad(ax,ay,d); // gradient from above, from chain rule
645            for(var fx=0;fx<f.sx;fx++) {
646              for(var fy=0;fy<f.sy;fy++) {
647                for(var fd=0;fd<f.depth;fd++) {
648                  var oy = y+fy;
649                  var ox = x+fx;
650                  if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {
651                    // forward prop calculated: a += f.get(fx, fy, fd) * V.get(ox, oy, fd);
652                    //f.add_grad(fx, fy, fd, V.get(ox, oy, fd) * chain_grad);
653                    //V.add_grad(ox, oy, fd, f.get(fx, fy, fd) * chain_grad);
654
655                    // avoid function call overhead and use Vols directly for efficiency
656                    var ix1 = ((V.sx * oy)+ox)*V.depth+fd;
657                    var ix2 = ((f.sx * fy)+fx)*f.depth+fd;
658                    f.dw[ix2] += V.w[ix1]*chain_grad;
659                    V.dw[ix1] += f.w[ix2]*chain_grad;
660                  }
661                }
662              }
663            }
664            this.biases.dw[d] += chain_grad;
665          }
666        }
667      }
668    },
669    getParamsAndGrads: function() {
670      var response = [];
671      for(var i=0;i<this.out_depth;i++) {
672        response.push({params: this.filters[i].w, grads: this.filters[i].dw, l2_decay_mul: this.l2_decay_mul, l1_decay_mul: this.l1_decay_mul});
673      }
674      response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
675      return response;
676    },
677    toJSON: function() {
678      var json = {};
679      json.sx = this.sx; // filter size in x, y dims
680      json.sy = this.sy;
681      json.stride = this.stride;
682      json.in_depth = this.in_depth;
683      json.out_depth = this.out_depth;
684      json.out_sx = this.out_sx;
685      json.out_sy = this.out_sy;
686      json.layer_type = this.layer_type;
687      json.l1_decay_mul = this.l1_decay_mul;
688      json.l2_decay_mul = this.l2_decay_mul;
689      json.pad = this.pad;
690      json.filters = [];
691      for(var i=0;i<this.filters.length;i++) {
692        json.filters.push(this.filters[i].toJSON());
693      }
694      json.biases = this.biases.toJSON();
695      return json;
696    },
697    fromJSON: function(json) {
698      this.out_depth = json.out_depth;
699      this.out_sx = json.out_sx;
700      this.out_sy = json.out_sy;
701      this.layer_type = json.layer_type;
702      this.sx = json.sx; // filter size in x, y dims
703      this.sy = json.sy;
704      this.stride = json.stride;
705      this.in_depth = json.in_depth; // depth of input volume
706      this.filters = [];
707      this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
708      this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
709      this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;
710      for(var i=0;i<json.filters.length;i++) {
711        var v = new Vol(0,0,0,0);
712        v.fromJSON(json.filters[i]);
713        this.filters.push(v);
714      }
715      this.biases = new Vol(0,0,0,0);
716      this.biases.fromJSON(json.biases);
717    }
718  }
719
720  var FullyConnLayer = function(opt) {
721    var opt = opt || {};
722
723    // required
724    // ok fine we will allow 'filters' as the word as well
725    this.out_depth = typeof opt.num_neurons !== 'undefined' ? opt.num_neurons : opt.filters;
726
727    // optional 
728    this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
729    this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
730
731    // computed
732    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
733    this.out_sx = 1;
734    this.out_sy = 1;
735    this.layer_type = 'fc';
736
737    // initializations
738    var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
739    this.filters = [];
740    for(var i=0;i<this.out_depth ;i++) { this.filters.push(new Vol(1, 1, this.num_inputs)); }
741    this.biases = new Vol(1, 1, this.out_depth, bias);
742  }
743
744  FullyConnLayer.prototype = {
745    forward: function(V, is_training) {
746      this.in_act = V;
747      var A = new Vol(1, 1, this.out_depth, 0.0);
748      var Vw = V.w;
749      for(var i=0;i<this.out_depth;i++) {
750        var a = 0.0;
751        var wi = this.filters[i].w;
752        for(var d=0;d<this.num_inputs;d++) {
753          a += Vw[d] * wi[d]; // for efficiency use Vols directly for now
754        }
755        a += this.biases.w[i];
756        A.w[i] = a;
757      }
758      this.out_act = A;
759      return this.out_act;
760    },
761    backward: function() {
762      var V = this.in_act;
763      V.dw = global.zeros(V.w.length); // zero out the gradient in input Vol
764      
765      // compute gradient wrt weights and data
766      for(var i=0;i<this.out_depth;i++) {
767        var tfi = this.filters[i];
768        var chain_grad = this.out_act.dw[i];
769        for(var d=0;d<this.num_inputs;d++) {
770          V.dw[d] += tfi.w[d]*chain_grad; // grad wrt input data
771          tfi.dw[d] += V.w[d]*chain_grad; // grad wrt params
772        }
773        this.biases.dw[i] += chain_grad;
774      }
775    },
776    getParamsAndGrads: function() {
777      var response = [];
778      for(var i=0;i<this.out_depth;i++) {
779        response.push({params: this.filters[i].w, grads: this.filters[i].dw, l1_decay_mul: this.l1_decay_mul, l2_decay_mul: this.l2_decay_mul});
780      }
781      response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
782      return response;
783    },
784    toJSON: function() {
785      var json = {};
786      json.out_depth = this.out_depth;
787      json.out_sx = this.out_sx;
788      json.out_sy = this.out_sy;
789      json.layer_type = this.layer_type;
790      json.num_inputs = this.num_inputs;
791      json.l1_decay_mul = this.l1_decay_mul;
792      json.l2_decay_mul = this.l2_decay_mul;
793      json.filters = [];
794      for(var i=0;i<this.filters.length;i++) {
795        json.filters.push(this.filters[i].toJSON());
796      }
797      json.biases = this.biases.toJSON();
798      return json;
799    },
800    fromJSON: function(json) {
801      this.out_depth = json.out_depth;
802      this.out_sx = json.out_sx;
803      this.out_sy = json.out_sy;
804      this.layer_type = json.layer_type;
805      this.num_inputs = json.num_inputs;
806      this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
807      this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
808      this.filters = [];
809      for(var i=0;i<json.filters.length;i++) {
810        var v = new Vol(0,0,0,0);
811        v.fromJSON(json.filters[i]);
812        this.filters.push(v);
813      }
814      this.biases = new Vol(0,0,0,0);
815      this.biases.fromJSON(json.biases);
816    }
817  }
818
819  global.ConvLayer = ConvLayer;
820  global.FullyConnLayer = FullyConnLayer;
821  
822})(convnetjs);
823(function(global) {
824  "use strict";
825  var Vol = global.Vol; // convenience
826  
827  var PoolLayer = function(opt) {
828
829    var opt = opt || {};
830
831    // required
832    this.sx = opt.sx;
832 // filter size
833    this.in_depth = opt.in_depth;
834    this.in_sx = opt.in_sx;
835    this.in_sy = opt.in_sy;
836
837    // optional
838    this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
839    this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 2;
840    this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
841
842    // computed
843    this.out_depth = this.in_depth;
844    this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
845    this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
846    this.layer_type = 'pool';
847    // store switches for x,y coordinates for where the max comes from, for each output neuron
848    this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth);
849    this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);
850  }
851
852  PoolLayer.prototype = {
853    forward: function(V, is_training) {
854      this.in_act = V;
855
856      var A = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
857      
858      var n=0; // a counter for switches
859      for(var d=0;d<this.out_depth;d++) {
860        var x = -this.pad;
861        var y = -this.pad;
862        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
863          y = -this.pad;
864          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
865
866            // convolve centered at this particular location
867            var a = -99999; // hopefully small enough ;\
868            var winx=-1,winy=-1;
869            for(var fx=0;fx<this.sx;fx++) {
870              for(var fy=0;fy<this.sy;fy++) {
871                var oy = y+fy;
872                var ox = x+fx;
873                if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {
874                  var v = V.get(ox, oy, d);
875                  // perform max pooling and store pointers to where
876                  // the max came from. This will speed up backprop 
877                  // and can help make nice visualizations in future
878                  if(v > a) { a = v; winx=ox; winy=oy;}
879                }
880              }
881            }
882            this.switchx[n] = winx;
883            this.switchy[n] = winy;
884            n++;
885            A.set(ax, ay, d, a);
886          }
887        }
888      }
889      this.out_act = A;
890      return this.out_act;
891    },
892    backward: function() { 
893      // pooling layers have no parameters, so simply compute 
894      // gradient wrt data here
895      var V = this.in_act;
896      V.dw = global.zeros(V.w.length); // zero out gradient wrt data
897      var A = this.out_act; // computed in forward pass 
898
899      var n = 0;
900      for(var d=0;d<this.out_depth;d++) {
901        var x = -this.pad;
902        var y = -this.pad;
903        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
904          y = -this.pad;
905          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
906
907            var chain_grad = this.out_act.get_grad(ax,ay,d);
908            V.add_grad(this.switchx[n], this.switchy[n], d, chain_grad);
909            n++;
910
911          }
912        }
913      }
914    },
915    getParamsAndGrads: function() {
916      return [];
917    },
918    toJSON: function() {
919      var json = {};
920      json.sx = this.sx;
921      json.sy = this.sy;
922      json.stride = this.stride;
923      json.in_depth = this.in_depth;
924      json.out_depth = this.out_depth;
925      json.out_sx = this.out_sx;
926      json.out_sy = this.out_sy;
927      json.layer_type = this.layer_type;
928      json.pad = this.pad;
929      return json;
930    },
931    fromJSON: function(json) {
932      this.out_depth = json.out_depth;
933      this.out_sx = json.out_sx;
934      this.out_sy = json.out_sy;
935      this.layer_type = json.layer_type;
936      this.sx = json.sx;
937      this.sy = json.sy;
938      this.stride = json.stride;
939      this.in_depth = json.in_depth;
940      this.pad = typeof json.pad !== 'undefined' ? json.pad : 0; // backwards compatibility
941      this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth); // need to re-init these appropriately
942      this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);
943    }
944  }
945
946  global.PoolLayer = PoolLayer;
947
948})(convnetjs);
949
950(function(global) {
951  "use strict";
952  var Vol = global.Vol; // convenience
953  
954  var InputLayer = function(opt) {
955    var opt = opt || {};
956
957    // this is a bit silly but lets allow people to specify either ins or outs
958    this.out_sx = typeof opt.out_sx !== 'undefined' ? opt.out_sx : opt.in_sx;
959    this.out_sy = typeof opt.out_sy !== 'undefined' ? opt.out_sy : opt.in_sy;
960    this.out_depth = typeof opt.out_depth !== 'undefined' ? opt.out_depth : opt.in_depth;
961    this.layer_type = 'input';
962  }
963  InputLayer.prototype = {
964    forward: function(V, is_training) {
965      this.in_act = V;
966      this.out_act = V;
967      return this.out_act; // dummy identity function for now
968    },
969    backward: function() { },
970    getParamsAndGrads: function() {
971      return [];
972    },
973    toJSON: function() {
974      var json = {};
975      json.out_depth = this.out_depth;
976      json.out_sx = this.out_sx;
977      json.out_sy = this.out_sy;
978      json.layer_type = this.layer_type;
979      return json;
980    },
981    fromJSON: function(json) {
982      this.out_depth = json.out_depth;
983      this.out_sx = json.out_sx;
984      this.out_sy = json.out_sy;
985      this.layer_type = json.layer_type; 
986    }
987  }
988
989  global.InputLayer = InputLayer;
990})(convnetjs);
991(function(global) {
992  "use strict";
993  var Vol = global.Vol; // convenience
994  
995  // Layers that implement a loss. Currently these are the layers that 
996  // can initiate a backward() pass. In future we probably want a more 
997  // flexible system that can accomodate multiple losses to do multi-task
998  // learning, and stuff like that. But for now, one of the layers in this
999  // file must be the final layer in a Net.
1000
1001  // This is a classifier, with N discrete classes from 0 to N-1
1002  // it gets a stream of N incoming numbers and computes the softmax
1003  // function (exponentiate and normalize to sum to 1 as probabilities should)
1004  var SoftmaxLayer = function(opt) {
1005    var opt = opt || {};
1006
1007    // computed
1008    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
1009    this.out_depth = this.num_inputs;
1010    this.out_sx = 1;
1011    this.out_sy = 1;
1012    this.layer_type = 'softmax';
1013  }
1014
1015  SoftmaxLayer.prototype = {
1016    forward: function(V, is_training) {
1017      this.in_act = V;
1018
1019      var A = new Vol(1, 1, this.out_depth, 0.0);
1020
1021      // compute max activation
1022      var as = V.w;
1023      var amax = V.w[0];
1024      for(var i=1;i<this.out_depth;i++) {
1025        if(as[i] > amax) amax = as[i];
1026      }
1027
1028      // compute exponentials (carefully to not blow up)
1029      var es = global.zeros(this.out_depth);
1030      var esum = 0.0;
1031      for(var i=0;i<this.out_depth;i++) {
1032        var e = Math.exp(as[i] - amax);
1033        esum += e;
1034        es[i] = e;
1035      }
1036
1037      // normalize and output to sum to one
1038      for(var i=0;i<this.out_depth;i++) {
1039        es[i] /= esum;
1040        A.w[i] = es[i];
1041      }
1042
1043      this.es = es; // save these for backprop
1044      this.out_act = A;
1045      return this.out_act;
1046    },
1047    backward: function(y) {
1048
1049      // compute and accumulate gradient wrt weights and bias of this layer
1050      var x = this.in_act;
1051      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
1052
1053      for(var i=0;i<this.out_depth;i++) {
1054        var indicator = i === y ? 1.0 : 0.0;
1055        var mul = -(indicator - this.es[i]);
1056        x.dw[i] = mul;
1057      }
1058
1059      // loss is the class negative log likelihood
1060      return -Math.log(this.es[y]);
1061    },
1062    getParamsAndGrads: function() { 
1063      return [];
1064    },
1065    toJSON: function() {
1066      var json = {};
1067      json.out_depth = this.out_depth;
1068      json.out_sx = this.out_sx;
1069      json.out_sy = this.out_sy;
1070      json.layer_type = this.layer_type;
1071      json.num_inputs = this.num_inputs;
1072      return json;
1073    },
1074    fromJSON: function(json) {
1075      this.out_depth = json.out_depth;
1076      this.out_sx = json.out_sx;
1077      this.out_sy = json.out_sy;
1078      this.layer_type = json.layer_type;
1079      this.num_inputs = json.num_inputs;
1080    }
1081  }
1082
1083  // implements an L2 regression cost layer,
1084  // so penalizes \sum_i(||x_i - y_i||^2), where x is its input
1085  // and y is the user-provided array of "correct" values.
1086  var RegressionLayer = function(opt) {
1087    var opt = opt || {};
1088
1089    // computed
1090    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
1091    this.out_depth = this.num_inputs;
1092    this.out_sx = 1;
1093    this.out_sy = 1;
1094    this.layer_type = 'regression';
1095  }
1096
1097  RegressionLayer.prototype = {
1098    forward: function(V, is_training) {
1099      this.in_act = V;
1100      this.out_act = V;
1101      return V; // identity function
1102    },
1103    // y is a list here of size num_inputs
1104    backward: function(y) { 
1105
1106      // compute and accumulate gradient wrt weights and bias of this layer
1107      var x = this.in_act;
1108      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
1109      var loss = 0.0;
1110      if(y instanceof Array || y instanceof Float64Array) {
1111        for(var i=0;i<this.out_depth;i++) {
1112          var dy = x.w[i] - y[i];
1113          x.dw[i] = dy;
1114          loss += 2*dy*dy;
1115        }
1116      } else {
1117        // assume it is a struct with entries .dim and .val
1118        // and we pass gradient only along dimension dim to be equal to val
1119        var i = y.dim;
1120        var yi = y.val;
1121        var dy = x.w[i] - yi;
1122        x.dw[i] = dy;
1123        loss += 2*dy*dy;
1124      }
1125      return loss;
1126    },
1127    getParamsAndGrads: function() { 
1128      return [];
1129    },
1130    toJSON: function() {
1131      var json = {};
1132      json.out_depth = this.out_depth;
1133      json.out_sx = this.out_sx;
1134      json.out_sy = this.out_sy;
1135      json.layer_type = this.layer_type;
1136      json.num_inputs = this.num_inputs;
1137      return json;
1138    },
1139    fromJSON: function(json) {
1140      this.out_depth = json.out_depth;
1141      this.out_sx = json.out_sx;
1142      this.out_sy = json.out_sy;
1143      this.layer_type = json.layer_type;
1144      this.num_inputs = json.num_inputs;
1145    }
1146  }
1147
1148  var SVMLayer = function(opt) {
1149    var opt = opt || {};
1150
1151    // computed
1152    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
1153    this.out_depth = this.num_inputs;
1154    this.out_sx = 1;
1155    this.out_sy = 1;
1156    this.layer_type = 'svm';
1157  }
1158
1159  SVMLayer.prototype = {
1160    forward: function(V, is_training) {
1161      this.in_act = V;
1162      this.out_act = V; // nothing to do, output raw scores
1163      return V;
1164    },
1165    backward: function(y) {
1166
1167      // compute and accumulate gradient wrt weights and bias of this layer
1168      var x = this.in_act;
1169      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
1170
1171      var yscore = x.w[y]; // score of ground truth
1172      var margin = 1.0;
1173      var loss = 0.0;
1174      for(var i=0;i<this.out_depth;i++) {
1175        if(-yscore + x.w[i] + margin > 0) {
1176          // violating example, apply loss
1177          // I love hinge loss, by the way. Truly.
1178          // Seriously, compare this SVM code with Softmax forward AND backprop code above
1179          // it's clear which one is superior, not only in code, simplicity
1180          // and beauty, but also in practice.
1181          x.dw[i] += 1;
1182          x.dw[y] -= 1;
1183          loss += -yscore + x.w[i] + margin;
1184        }
1185      }
1186
1187      return loss;
1188    },
1189    getParamsAndGrads: function() { 
1190      return [];
1191    },
1192    toJSON: function() {
1193      var json = {};
1194      json.out_depth = this.out_depth;
1195      json.out_sx = this.out_sx;
1196      json.out_sy = this.out_sy;
1197      json.layer_type = this.layer_type;
1198      json.num_inputs = this.num_inputs;
1199      return json;
1200    },
1201    fromJSON: function(json) {
1202      this.out_depth = json.out_depth;
1203      this.out_sx = json.out_sx;
1204      this.out_sy = json.out_sy;
1205      this.layer_type = json.layer_type;
1206      this.num_inputs = json.num_inputs;
1207    }
1208  }
1209  
1210  global.RegressionLayer = RegressionLayer;
1211  global.SoftmaxLayer = SoftmaxLayer;
1212  global.SVMLayer = SVMLayer;
1213
1214})(convnetjs);
1215
1216(function(global) {
1217  "use strict";
1218  var Vol = global.Vol; // convenience
1219  
1220  // Implements ReLU nonlinearity elementwise
1221  // x -> max(0, x)
1222  // the output is in [0, inf)
1223  var ReluLayer = function(opt) {
1224    var opt = opt || {};
1225
1226    // computed
1227    this.out_sx = opt.in_sx;
1228    this.out_sy = opt.in_sy;
1229    this.out_depth = opt.in_depth;
1230    this.layer_type = 'relu';
1231  }
1232  ReluLayer.prototype = {
1233    forward: function(V, is_training) {
1234      this.in_act = V;
1235      var V2 = V.clone();
1236      var N = V.w.length;
1237      var V2w = V2.w;
1238      for(var i=0;i<N;i++) { 
1239        if(V2w[i] < 0) V2w[i] = 0; // threshold at 0
1240      }
1241      this.out_act = V2;
1242      return this.out_act;
1243    },
1244    backward: function() {
1245      var V = this.in_act; // we need to set dw of this
1246      var V2 = this.out_act;
1247      var N = V.w.length;
1248      V.dw = global.zeros(N); // zero out gradient wrt data
1249      for(var i=0;i<N;i++) {
1250        if(V2.w[i] <= 0) V.dw[i] = 0; // threshold
1251        else V.dw[i] = V2.dw[i];
1252      }
1253    },
1254    getParamsAndGrads: function() {
1255      return [];
1256    },
1257    toJSON: function() {
1258      var json = {};
1259      json.out_depth = this.out_depth;
1260      json.out_sx = this.out_sx;
1261      json.out_sy = this.out_sy;
1262      json.layer_type = this.layer_type;
1263      return json;
1264    },
1265    fromJSON: function(json) {
1266      this.out_depth = json.out_depth;
1267      this.out_sx = json.out_sx;
1268      this.out_sy = json.out_sy;
1269      this.layer_type = json.layer_type; 
1270    }
1271  }
1272
1273  // Implements Sigmoid nnonlinearity elementwise
1274  // x -> 1/(1+e^(-x))
1275  // so the output is between 0 and 1.
1276  var SigmoidLayer = function(opt) {
1277    var opt = opt || {};
1278
1279    // computed
1280    this.out_sx = opt.in_sx;
1281    this.out_sy = opt.in_sy;
1282    this.out_depth = opt.in_depth;
1283    this.layer_type = 'sigmoid';
1284  }
1285  SigmoidLayer.prototype = {
1286    forward: function(V, is_training) {
1287      this.in_act = V;
1288      var V2 = V.cloneAndZero();
1289      var N = V.w.length;
1290      var V2w = V2.w;
1291      var Vw = V.w;
1292      for(var i=0;i<N;i++) { 
1293        V2w[i] = 1.0/(1.0+Math.exp(-Vw[i]));
1294      }
1295      this.out_act = V2;
1296      return this.out_act;
1297    },
1298    backward: function() {
1299      var V = this.in_act; // we need to set dw of this
1300      var V2 = this.out_act;
1301      var N = V.w.length;
1302      V.dw = global.zeros(N); // zero out gradient wrt data
1303      for(var i=0;i<N;i++) {
1304        var v2wi = V2.w[i];
1305        V.dw[i] =  v2wi * (1.0 - v2wi) * V2.dw[i];
1306      }
1307    },
1308    getParamsAndGrads: function() {
1309      return [];
1310    },
1311    toJSON: function() {
1312      var json = {};
1313      json.out_depth = this.out_depth;
1314      json.out_sx = this.out_sx;
1315      json.out_sy = this.out_sy;
1316      json.layer_type = this.layer_type;
1317      return json;
1318    },
1319    fromJSON: function(json) {
1320      this.out_depth = json.out_depth;
1321      this.out_sx = json.out_sx;
1322      this.out_sy = json.out_sy;
1323      this.layer_type = json.layer_type; 
1324    }
1325  }
1326
1327  // Implements Maxout nnonlinearity that computes
1328  // x -> max(x)
1329  // where x is a vector of size group_size. Ideally of course,
1330  // the input size should be exactly divisible by group_size
1331  var MaxoutLayer = function(opt) {
1332    var opt = opt || {};
1333
1334    // required
1335    this.group_size = typeof opt.group_size !== 'undefined' ? opt.group_size : 2;
1336
1337    // computed
1338    this.out_sx = opt.in_sx;
1339    this.out_sy = opt.in_sy;
1340    this.out_depth = Math.floor(opt.in_depth / this.group_size);
1341    this.layer_type = 'maxout';
1342
1343    this.switches = global.zeros(this.out_sx*this.out_sy*this.out_depth); // useful for backprop
1344  }
1345  MaxoutLayer.prototype = {
1346    forward: function(V, is_training) {
1347      this.in_act = V;
1348      var N = this.out_depth; 
1349      var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
1350
1351      // optimization branch. If we're operating on 1D arrays we dont have
1352      // to worry about keeping track of x,y,d coordinates inside
1353      // input volumes. In convnets we do :(
1354      if(this.out_sx === 1 && this.out_sy === 1) {
1355        for(var i=0;i<N;i++) {
1356          var ix = i * this.group_size; // base index offset
1357          var a = V.w[ix];
1358          var ai = 0;
1359          for(var j=1;j<this.group_size;j++) {
1360            var a2 = V.w[ix+j];
1361            if(a2 > a) {
1362              a = a2;
1363              ai = j;
1364            }
1365          }
1366          V2.w[i] = a;
1367          this.switches[i] = ix + ai;
1368        }
1369      } else {
1370        var n=0; // counter for switches
1371        for(var x=0;x<V.sx;x++) {
1372          for(var y=0;y<V.sy;y++) {
1373            for(var i=0;i<N;i++) {
1374              var ix = i * this.group_size;
1375              var a = V.get(x, y, ix);
1376              var ai = 0;
1377              for(var j=1;j<this.group_size;j++) {
1378                var a2 = V.get(x, y, ix+j);
1379                if(a2 > a) {
1380                  a = a2;
1381                  ai = j;
1382                }
1383              }
1384              V2.set(x,y,i,a);
1385              this.switches[n] = ix + ai;
1386              n++;
1387            }
1388          }
1389        }
1390
1391      }
1392      this.out_act = V2;
1393      return this.out_act;
1394    },
1395    backward: function() {
1396      var V = this.in_act; // we need to set dw of this
1397      var V2 = this.out_act;
1398      var N = this.out_depth;
1399      V.dw = global.zeros(V.w.length); // zero out gradient wrt data
1400
1401      // pass the gradient through the appropriate switch
1402      if(this.out_sx === 1 && this.out_sy === 1) {
1403        for(var i=0;i<N;i++) {
1404          var chain_grad = V2.dw[i];
1405          V.dw[this.switches[i]] = chain_grad;
1406        }
1407      } else {
1408        // bleh okay, lets do this the hard way
1409        var n=0; // counter for switches
1410        for(var x=0;x<V2.sx;x++) {
1411          for(var y=0;y<V2.sy;y++) {
1412            for(var i=0;i<N;i++) {
1413              var chain_grad = V2.get_grad(x,y,i);
1414              V.set_grad(x,y,this.switches[n],chain_grad);
1415              n++;
1416            }
1417          }
1418        }
1419      }
1420    },
1421    getParamsAndGrads: function() {
1422      return [];
1423    },
1424    toJSON: function() {
1425      var json = {};
1426      json.out_depth = this.out_depth;
1427      json.out_sx = this.out_sx;
1428      json.out_sy = this.out_sy;
1429      json.layer_type = this.layer_type;
1430      json.group_size = this.group_size;
1431      return json;
1432    },
1433    fromJSON: function(json) {
1434      this.out_depth = json.out_depth;
1435      this.out_sx = json.out_sx;
1436      this.out_sy = json.out_sy;
1437      this.layer_type = json.layer_type; 
1438      this.group_size = json.group_size;
1439      this.switches = global.zeros(this.group_size);
1440    }
1441  }
1442
1443  // a helper function, since tanh is not yet part of ECMAScript. Will be in v6.
1444  function tanh(x) {
1445    var y = Math.exp(2 * x);
1446    return (y - 1) / (y + 1);
1447  }
1448  // Implements Tanh nnonlinearity elementwise
1449  // x -> tanh(x) 
1450  // so the output is between -1 and 1.
1451  var TanhLayer = function(opt) {
1452    var opt = opt || {};
1453
1454    // computed
1455    this.out_sx = opt.in_sx;
1456    this.out_sy = opt.in_sy;
1457    this.out_depth = opt.in_depth;
1458    this.layer_type = 'tanh';
1459  }
1460  TanhLayer.prototype = {
1461    forward: function(V, is_training) {
1462      this.in_act = V;
1463      var V2 = V.cloneAndZero();
1464      var N = V.w.length;
1465      for(var i=0;i<N;i++) { 
1466        V2.w[i] = tanh(V.w[i]);
1467      }
1468      this.out_act = V2;
1469      return this.out_act;
1470    },
1471    backward: function() {
1472      var V = this.in_act; // we need to set dw of this
1473      var V2 = this.out_act;
1474      var N = V.w.length;
1475      V.dw = global.zeros(N); // zero out gradient wrt data
1476      for(var i=0;i<N;i++) {
1477        var v2wi = V2.w[i];
1478        V.dw[i] = (1.0 - v2wi * v2wi) * V2.dw[i];
1479      }
1480    },
1481    getParamsAndGrads: function() {
1482      return [];
1483    },
1484    toJSON: function() {
1485      var json = {};
1486      json.out_depth = this.out_depth;
1487      json.out_sx = this.out_sx;
1488      json.out_sy = this.out_sy;
1489      json.layer_type = this.layer_type;
1490      return json;
1491    },
1492    fromJSON: function(json) {
1493      this.out_depth = json.out_depth;
1494      this.out_sx = json.out_sx;
1495      this.out_sy = json.out_sy;
1496      this.layer_type = json.layer_type; 
1497    }
1498  }
1499  
1500  global.TanhLayer = TanhLayer;
1501  global.MaxoutLayer = MaxoutLayer;
1502  global.ReluLayer = ReluLayer;
1503  global.SigmoidLayer = SigmoidLayer;
1504
1505})(convnetjs);
1506
1507(function(global) {
1508  "use strict";
1509  var Vol = global.Vol; // convenience
1510
1511  // An inefficient dropout layer
1512  // Note this is not most efficient implementation since the layer before
1513  // computed all these activations and now we're just going to drop them :(
1514  // same goes for backward pass. Also, if we wanted to be efficient at test time
1515  // we could equivalently be clever and upscale during train and copy pointers during test
1516  // todo: make more efficient.
1517  var DropoutLayer = function(opt) {
1518    var opt = opt || {};
1519
1520    // computed
1521    this.out_sx = opt.in_sx;
1522    this.out_sy = opt.in_sy;
1523    this.out_depth = opt.in_depth;
1524    this.layer_type = 'dropout';
1525    this.drop_prob = typeof opt.drop_prob !== 'undefined' ? opt.drop_prob : 0.5;
1526    this.dropped = global.zeros(this.out_sx*this.out_sy*this.out_depth);
1527  }
1528  DropoutLayer.prototype = {
1529    forward: function(V, is_training) {
1530      this.in_act = V;
1531      if(typeof(is_training)==='undefined') { is_training = false; } // default is prediction mode
1532      var V2 = V.clone();
1533      var N = V.w.length;
1534      if(is_training) {
1535        // do dropout
1536        for(var i=0;i<N;i++) {
1537          if(Math.random()<this.drop_prob) { V2.w[i]=0; this.dropped[i] = true; } // drop!
1538          else {this.dropped[i] = false;}
1539        }
1540      } else {
1541        // scale the activations during prediction
1542        for(var i=0;i<N;i++) { V2.w[i]*=this.drop_prob; }
1543      }
1544      this.out_act = V2;
1545      return this.out_act; // dummy identity function for now
1546    },
1547    backward: function() {
1548      var V = this.in_act; // we need to set dw of this
1549      var chain_grad = this.out_act;
1550      var N = V.w.length;
1551      V.dw = global.zeros(N); // zero out gradient wrt data
1552      for(var i=0;i<N;i++) {
1553        if(!(this.dropped[i])) { 
1554          V.dw[i] = chain_grad.dw[i]; // copy over the gradient
1555        }
1556      }
1557    },
1558    getParamsAndGrads: function() {
1559      return [];
1560    },
1561    toJSON: function() {
1562      var json = {};
1563      json.out_depth = this.out_depth;
1564      json.out_sx = this.out_sx;
1565      json.out_sy = this.out_sy;
1566      json.layer_type = this.layer_type;
1567      json.drop_prob = this.drop_prob;
1568      return json;
1569    },
1570    fromJSON: function(json) {
1571      this.out_depth = json.out_depth;
1572      this.out_sx = json.out_sx;
1573      this.out_sy = json.out_sy;
1574      this.layer_type = json.layer_type; 
1575      this.drop_prob = json.drop_prob;
1576    }
1577  }
1578  
1579
1580  global.DropoutLayer = DropoutLayer;
1581})(convnetjs);
1582(function(global) {
1583  "use strict";
1584  var Vol = global.Vol; // convenience
1585  
1586  // a bit experimental layer for now. I think it works but I'm not 100%
1587  // the gradient check is a bit funky. I'll look into this a bit later.
1588  // Local Response Normalization in window, along depths of volumes
1589  var LocalResponseNormalizationLayer = function(opt) {
1590    var opt = opt || {};
1591
1592    // required
1593    this.k = opt.k;
1594    this.n = opt.n;
1595    this.alpha = opt.alpha;
1596    this.beta = opt.beta;
1597
1598    // computed
1599    this.out_sx = opt.in_sx;
1600    this.out_sy = opt.in_sy;
1601    this.out_depth = opt.in_depth;
1602    this.layer_type = 'lrn';
1603
1604    // checks
1605    if(this.n%2 === 0) { console.log('WARNING n should be odd for LRN layer'); }
1606  }
1607  LocalResponseNormalizationLayer.prototype = {
1608    forward: function(V, is_training) {
1609      this.in_act = V;
1610
1611      var A = V.cloneAndZero();
1612      this.S_cache_ = V.cloneAndZero();
1613      var n2 = Math.floor(this.n/2);
1614      for(var x=0;x<V.sx;x++) {
1615        for(var y=0;y<V.sy;y++) {
1616          for(var i=0;i<V.depth;i++) {
1617
1618            var ai = V.get(x,y,i);
1619
1620            // normalize in a window of size n
1621            var den = 0.0;
1622            for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
1623              var aa = V.get(x,y,j);
1624              den += aa*aa;
1625            }
1626            den *= this.alpha / this.n;
1627            den += this.k;
1628            this.S_cache_.set(x,y,i,den); // will be useful for backprop
1629            den = Math.pow(den, this.beta);
1630            A.set(x,y,i,ai/den);
1631          }
1632        }
1633      }
1634
1635      this.out_act = A;
1636      return this.out_act; // dummy identity function for now
1637    },
1638    backward: function() { 
1639      // evaluate gradient wrt data
1640      var V = this.in_act; // we need to set dw of this
1641      V.dw = global.zeros(V.w.length); // zero out gradient wrt data
1642      var A = this.out_act; // computed in forward pass 
1643
1644      var n2 = Math.floor(this.n/2);
1645      for(var x=0;x<V.sx;x++) {
1646        for(var y=0;y<V.sy;y++) {
1647          for(var i=0;i<V.depth;i++) {
1648
1649            var chain_grad = this.out_act.get_grad(x,y,i);
1650            var S = this.S_cache_.get(x,y,i);
1651            var SB = Math.pow(S, this.beta);
1652            var SB2 = SB*SB;
1653
1654            // normalize in a window of size n
1655            for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {              
1656              var aj = V.get(x,y,j); 
1657              var g = -aj*this.beta*Math.pow(S,this.beta-1)*this.alpha/this.n*2*aj;
1658              if(j===i) g+= SB;
1659              g /= SB2;
1660              g *= chain_grad;
1661              V.add_grad(x,y,j,g);
1662            }
1663
1664          }
1665        }
1666      }
1667    },
1668    getParamsAndGrads: function() { return []; },
1669    toJSON: function() {
1670      var json = {};
1671      json.k = this.k;
1672      json.n = this.n;
1673      json.alpha = this.alpha; // normalize by size
1674      json.beta = this.beta;
1675      json.out_sx = this.out_sx; 
1676      json.out_sy = this.out_sy;
1677      json.out_depth = this.out_depth;
1678      json.layer_type = this.layer_type;
1679      return json;
1680    },
1681    fromJSON: function(json) {
1682      this.k = json.k;
1683      this.n = json.n;
1684      this.alpha = json.alpha; // normalize by size
1685      this.beta = json.beta;
1686      this.out_sx = json.out_sx; 
1687      this.out_sy = json.out_sy;
1688      this.out_depth = json.out_depth;
1689      this.layer_type = json.layer_type;
1690    }
1691  }
1692  
1693
1694  global.LocalResponseNormalizationLayer = LocalResponseNormalizationLayer;
1695})(convnetjs);
1696(function(global) {
1697  "use strict";
1698  var Vol = global.Vol; // convenience
1699
1700  // transforms x-> [x, x_i*x_j forall i,j]
1701  // so the fully connected layer afters will essentially be doing tensor multiplies
1702  var QuadTransformLayer = function(opt) {
1703    var opt = opt || {};
1704
1705    // computed
1706    this.out_sx = opt.in_sx;
1707    this.out_sy = opt.in_sy;
1708    // linear terms, and then quadratic terms, of which there are 1/2*n*(n+1),
1709    // (offdiagonals and the diagonal total) and arithmetic series.
1710    // Actually never mind, lets not be fancy here yet and just include
1711    // terms x_ix_j and x_jx_i twice. Half as efficient but much less
1712    // headache.
1713    this.out_depth = opt.in_depth + opt.in_depth * opt.in_depth;
1714    this.layer_type = 'quadtransform';
1715
1716  }
1717  QuadTransformLayer.prototype = {
1718    forward: function(V, is_training) {
1719      this.in_act = V;
1720      var N = this.out_depth;
1721      var Ni = V.depth;
1722      var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
1723      for(var x=0;x<V.sx;x++) {
1724        for(var y=0;y<V.sy;y++) {
1725          for(var i=0;i<N;i++) {
1726            if(i<Ni) {
1727              V2.set(x,y,i,V.get(x,y,i)); // copy these over (linear terms)
1728            } else {
1729              var i0 = Math.floor((i-Ni)/Ni);
1730              var i1 = (i-Ni) - i0*Ni;
1731              V2.set(x,y,i,V.get(x,y,i0) * V.get(x,y,i1)); // quadratic
1732            }
1733          }
1734        }
1735      }
1736      this.out_act = V2;
1737      return this.out_act; // dummy identity function for now
1738    },
1739    backward: function() {
1740      var V = this.in_act;
1741      V.dw = global.zeros(V.w.length); // zero out gradient wrt data
1742      var V2 = this.out_act;
1743      var N = this.out_depth;
1744      var Ni = V.depth;
1745      for(var x=0;x<V.sx;x++) {
1746        for(var y=0;y<V.sy;y++) {
1747          for(var i=0;i<N;i++) {
1748            var chain_grad = V2.get_grad(x,y,i);
1749            if(i<Ni) {
1750              V.add_grad(x,y,i,chain_grad);
1751            } else {
1752              var i0 = Math.floor((i-Ni)/Ni);
1753              var i1 = (i-Ni) - i0*Ni;
1754              V.add_grad(x,y,i0,V.get(x,y,i1)*chain_grad);
1755              V.add_grad(x,y,i1,V.get(x,y,i0)*chain_grad);
1756            }
1757          }
1758        }
1759      }
1760    },
1761    getParamsAndGrads: function() {
1762      return [];
1763    },
1764    toJSON: function() {
1765      var json = {};
1766      json.out_depth = this.out_depth;
1767      json.out_sx = this.out_sx;
1768      json.out_sy = this.out_sy;
1769      json.layer_type = this.layer_type;
1770      return json;
1771    },
1772    fromJSON: function(json) {
1773      this.out_depth = json.out_depth;
1774      this.out_sx = json.out_sx;
1775      this.out_sy = json.out_sy;
1776      this.layer_type = json.layer_type; 
1777    }
1778  }
1779  
1780
1781  global.QuadTransformLayer = QuadTransformLayer;
1782})(convnetjs);
1783(function(global) {
1784  "use strict";
1785  var Vol = global.Vol; // convenience
1786  
1787  // Net manages a set of layers
1788  // For now constraints: Simple linear order of layers, first layer input last layer a cost layer
1789  var Net = function(options) {
1790    this.layers = [];
1791  }
1792
1793  Net.prototype = {
1794    
1795    // takes a list of layer definitions and creates the network layer objects
1796    makeLayers: function(defs) {
1797
1798      // few checks for now
1799      if(defs.length<2) {console.log('ERROR! For now at least have input and softmax layers.');}
1800      if(defs[0].type !== 'input') {console.log('ERROR! For now first layer should be input.');}
1801
1802      // desugar syntactic for adding activations and dropouts
1803      var desugar = function() {
1804        var new_defs = [];
1805        for(var i=0;i<defs.length;i++) {
1806          var def = defs[i];
1807          
1808          if(def.type==='softmax' || def.type==='svm') {
1809            // add an fc layer here, there is no reason the user should
1810            // have to worry about this and we almost always want to
1811            new_defs.push({type:'fc', num_neurons: def.num_classes});
1812          }
1813
1814          if(def.type==='regression') {
1815            // add an fc layer here, there is no reason the user should
1816            // have to worry about this and we almost always want to
1817            new_defs.push({type:'fc', num_neurons: def.num_neurons});
1818          }
1819
1820          if((def.type==='fc' || def.type==='conv') 
1821              && typeof(def.bias_pref) === 'undefined'){
1822            def.bias_pref = 0.0;
1823            if(typeof def.activation !== 'undefined' && def.activation === 'relu') {
1824              def.bias_pref = 0.1; // relus like a bit of positive bias to get gradients early
1825              // otherwise it's technically possible that a relu unit will never turn on (by chance)
1826              // and will never get any gradient and never contribute any computation. Dead relu.
1827            }
1828          }
1829          
1830          if(typeof def.tensor !== 'undefined') {
1831            // apply quadratic transform so that the upcoming multiply will include
1832            // quadratic terms, equivalent to doing a tensor product
1833            if(def.tensor) {
1834              new_defs.push({type: 'quadtransform'});
1835            }
1836          }
1837
1838          new_defs.push(def);
1839
1840          if(typeof def.activation !== 'undefined') {
1841            if(def.activation==='relu') { new_defs.push({type:'relu'}); }
1842            else if (def.activation==='sigmoid') { new_defs.push({type:'sigmoid'}); }
1843            else if (def.activation==='tanh') { new_defs.push({type:'tanh'}); }
1844            else if (def.activation==='maxout') {
1845              // create maxout activation, and pass along group size, if provided
1846              var gs = def.group_size !== 'undefined' ? def.group_size : 2;
1847              new_defs.push({type:'maxout', group_size:gs});
1848            }
1849            else { console.log('ERROR unsupported activation ' + def.activation); }
1850          }
1851          if(typeof def.drop_prob !== 'undefined' && def.type !== 'dropout') {
1852            new_defs.push({type:'dropout', drop_prob: def.drop_prob});
1853          }
1854
1855        }
1856        return new_defs;
1857      }
1858      defs = desugar(defs);
1859
1860      // create the layers
1861      this.layers = [];
1862      for(var i=0;i<defs.length;i++) {
1863        var def = defs[i];
1864        if(i>0) {
1865          var prev = this.layers[i-1];
1866          def.in_sx = prev.out_sx;
1867          def.in_sy = prev.out_sy;
1868          def.in_depth = prev.out_depth;
1869        }
1870
1871        switch(def.type) {
1872          case 'fc': this.layers.push(new global.FullyConnLayer(def)); break;
1873          case 'lrn': this.layers.push(new global.LocalResponseNormalizationLayer(def)); break;
1874          case 'dropout': this.layers.push(new global.DropoutLayer(def)); break;
1875          case 'input': this.layers.push(new global.InputLayer(def)); break;
1876          case 'softmax': this.layers.push(new global.SoftmaxLayer(def)); break;
1877          case 'regression': this.layers.push(new global.RegressionLayer(def)); break;
1878          case 'conv': this.layers.push(new global.ConvLayer(def)); break;
1879          case 'pool': this.layers.push(new global.PoolLayer(def)); break;
1880          case 'relu': this.layers.push(new global.ReluLayer(def)); break;
1881          case 'sigmoid': this.layers.push(new global.SigmoidLayer(def)); break;
1882          case 'tanh': this.layers.push(new global.TanhLayer(def)); break;
1883          case 'maxout': this.layers.push(new global.MaxoutLayer(def)); break;
1884          case 'quadtransform': this.layers.push(new global.QuadTransformLayer(def)); break;
1885          case 'svm': this.layers.push(new global.SVMLayer(def)); break;
1886          default: console.log('ERROR: UNRECOGNIZED LAYER TYPE!');
1887        }
1888      }
1889    },
1890
1891    // forward prop the network. A trainer will pass in is_training = true
1892    forward: function(V, is_training) {
1893      if(typeof(is_training)==='undefined') is_training = false;
1894      var act = this.layers[0].forward(V, is_training);
1895      for(var i=1;i<this.layers.length;i++) {
1896        act = this.layers[i].forward(act, is_training);
1897      }
1898      return act;
1899    },
1900
1901    getCostLoss: function(V, y) {
1902      this.forward(V, false);
1903      var N = this.layers.length;
1904      var loss = this.layers[N-1].backward(y);
1905      return loss;
1906    },
1907    
1908    // backprop: compute gradients wrt all parameters
1909    backward: function(y) {
1910      var N = this.layers.length;
1911      var loss = this.layers[N-1].backward(y); // last layer assumed softmax
1912      for(var i=N-2;i>=0;i--) { // first layer assumed input
1913        this.layers[i].backward();
1914      }
1915      return loss;
1916    },
1917    getParamsAndGrads: function() {
1918      // accumulate parameters and gradients for the entire network
1919      var response = [];
1920      for(var i=0;i<this.layers.length;i++) {
1921        var layer_reponse = this.layers[i].getParamsAndGrads();
1922        for(var j=0;j<layer_reponse.length;j++) {
1923          response.push(layer_reponse[j]);
1924        }
1925      }
1926      return response;
1927    },
1928    getPrediction: function() {
1929      var S = this.layers[this.layers.length-1]; // softmax layer
1930      var p = S.out_act.w;
1931      var maxv = p[0];
1932      var maxi = 0;
1933      for(var i=1;i<p.length;i++) {
1934        if(p[i] > maxv) { maxv = p[i]; maxi = i;}
1935      }
1936      return maxi;
1937    },
1938    toJSON: function() {
1939      var json = {};
1940      json.layers = [];
1941      for(var i=0;i<this.layers.length;i++) {
1942        json.layers.push(this.layers[i].toJSON());
1943      }
1944      return json;
1945    },
1946    fromJSON: function(json) {
1947      this.layers = [];
1948      for(var i=0;i<json.layers.length;i++) {
1949        var Lj = json.layers[i]
1950        var t = Lj.layer_type;
1951        var L;
1952        if(t==='input') { L = new global.InputLayer(); }
1953        if(t==='relu') { L = new global.ReluLayer(); }
1954        if(t==='sigmoid') { L = new global.SigmoidLayer(); }
1955        if(t==='tanh') { L = new global.TanhLayer(); }
1956        if(t==='dropout') { L = new global.DropoutLayer(); }
1957        if(t==='conv') { L = new global.ConvLayer(); }
1958        if(t==='pool') { L = new global.PoolLayer(); }
1959        if(t==='lrn') { L = new global.LocalResponseNormalizationLayer(); }
1960        if(t==='softmax') { L = new global.SoftmaxLayer(); }
1961        if(t==='regression') { L = new global.RegressionLayer(); }
1962        if(t==='fc') { L = new global.FullyConnLayer(); }
1963        if(t==='maxout') { L = new global.MaxoutLayer(); }
1964        if(t==='quadtransform') { L = new global.QuadTransformLayer(); }
1965        if(t==='svm') { L = new global.SVMLayer(); }
1966        L.fromJSON(Lj);
1967        this.layers.push(L);
1968      }
1969    }
1970  }
1971  
1972
1973  global.Net = Net;
1974})(convnetjs);
1975(function(global) {
1976  "use strict";
1977  var Vol = global.Vol; // convenience
1978
1979  var Trainer = function(net, options) {
1980
1981    this.net = net;
1982
1983    var options = options || {};
1984    this.learning_rate = typeof options.learning_rate !== 'undefined' ? options.learning_rate : 0.01;
1985    this.l1_decay = typeof options.l1_decay !== 'undefined' ? options.l1_decay : 0.0;
1986    this.l2_decay = typeof options.l2_decay !== 'undefined' ? options.l2_decay : 0.0;
1987    this.batch_size = typeof options.batch_size !== 'undefined' ? options.batch_size : 1;
1988    this.method = typeof options.method !== 'undefined' ? options.method : 'sgd'; // sgd/adagrad/adadelta/windowgrad
1989
1990    this.momentum = typeof options.momentum !== 'undefined' ? options.momentum : 0.9;
1991    this.ro = typeof options.ro !== 'undefined' ? options.ro : 0.95; // used in adadelta
1992    this.eps = typeof options.eps !== 'undefined' ? options.eps : 1e-6; // used in adadelta
1993
1994    this.k = 0; // iteration counter
1995    this.gsum = []; // last iteration gradients (used for momentum calculations)
1996    this.xsum = []; // used in adadelta
1997  }
1998
1999  Trainer.prototype = {
2000    train: function(x, y) {
2001
2002      var start = new Date().getTime();
2003      this.net.forward(x, true); // also set the flag that lets the net know we're just training
2004      var end = new Date().getTime();
2005      var fwd_time = end - start;
2006
2007      var start = new Date().getTime();
2008      var cost_loss = this.net.backward(y);
2009      var l2_decay_loss = 0.0;
2010      var l1_decay_loss = 0.0;
2011      var end = new Date().getTime();
2012      var bwd_time = end - start;
2013      
2014      this.k++;
2015      if(this.k % this.batch_size === 0) {
2016
2017        var pglist = this.net.getParamsAndGrads();
2018
2019        // initialize lists for accumulators. Will only be done once on first iteration
2020        if(this.gsum.length === 0 && (this.method !== 'sgd' || this.momentum > 0.0)) {
2021          // only vanilla sgd doesnt need either lists
2022          // momentum needs gsum
2023          // adagrad needs gsum
2024          // adadelta needs gsum and xsum
2025          for(var i=0;i<pglist.length;i++) {
2026            this.gsum.push(global.zeros(pglist[i].params.length));
2027            if(this.method === 'adadelta') {
2028              this.xsum.push(global.zeros(pglist[i].params.length));
2029            } else {
2030              this.xsum.push([]); // conserve memory
2031            }
2032          }
2033        }
2034
2035        // perform an update for all sets of weights
2036        for(var i=0;i<pglist.length;i++) {
2037          var pg = pglist[i]; // param, gradient, other options in future (custom learning rate etc)
2038          var p = pg.params;
2039          var g = pg.grads;
2040
2041          // learning rate for some parameters.
2042          var l2_decay_mul = typeof pg.l2_decay_mul !== 'undefined' ? pg.l2_decay_mul : 1.0;
2043          var l1_decay_mul = typeof pg.l1_decay_mul !== 'undefined' ? pg.l1_decay_mul : 1.0;
2044          var l2_decay = this.l2_decay * l2_decay_mul;
2045          var l1_decay = this.l1_decay * l1_decay_mul;
2046
2047          var plen = p.length;
2048          for(var j=0;j<plen;j++) {
2049            l2_decay_loss += l2_decay*p[j]*p[j]/2; // accumulate weight decay loss
2050            l1_decay_loss += l1_decay*Math.abs(p[j]);
2051            var l1grad = l1_decay * (p[j] > 0 ? 1 : -1);
2052            var l2grad = l2_decay * (p[j]);
2053
2054            var gij = (l2grad + l1grad + g[j]) / this.batch_size; // raw batch gradient
2055
2056            var gsumi = this.gsum[i];
2057            var xsumi = this.xsum[i];
2058            if(this.method === 'adagrad') {
2059              // adagrad update
2060              gsumi[j] = gsumi[j] + gij * gij;
2061              var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij;
2062              p[j] += dx;
2063            } else if(this.method === 'windowgrad') {
2064              // this is adagrad but with a moving window weighted average
2065              // so the gradient is not accumulated over the entire history of the run. 
2066              // it's also referred to as Idea #1 in Zeiler paper on Adadelta. Seems reasonable to me!
2067              gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
2068              var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij; // eps added for better conditioning
2069              p[j] += dx;
2070            } else if(this.method === 'adadelta') {
2071              // assume adadelta if not sgd or adagrad
2072              gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
2073              var dx = - Math.sqrt((xsumi[j] + this.eps)/(gsumi[j] + this.eps)) * gij;
2074              xsumi[j] = this.ro * xsumi[j] + (1-this.ro) * dx * dx; // yes, xsum lags behind gsum by 1.
2075              p[j] += dx;
2076            } else {
2077              // assume SGD
2078              if(this.momentum > 0.0) {
2079                // momentum update
2080                var dx = this.momentum * gsumi[j] - this.learning_rate * gij; // step
2081                gsumi[j] = dx; // back this up for next iteration of momentum
2082                p[j] += dx; // apply corrected gradient
2083              } else {
2084                // vanilla sgd
2085                p[j] +=  - this.learning_rate * gij;
2086              }
2087            }
2088            g[j] = 0.0; // zero out gradient so that we can begin accumulating anew
2089          }
2090        }
2091      }
2092
2093      // appending softmax_loss for backwards compatibility, but from now on we will always use cost_loss
2094      // in future, TODO: have to completely redo the way loss is done around the network as currently 
2095      // loss is a bit of a hack. Ideally, user should specify arbitrary number of loss functions on any layer
2096      // and it should all be computed correctly and automatically. 
2097      return {fwd_time: fwd_time, bwd_time: bwd_time, 
2098              l2_decay_loss: l2_decay_loss, l1_decay_loss: l1_decay_loss,
2099              cost_loss: cost_loss, softmax_loss: cost_loss, 
2100              loss: cost_loss + l1_decay_loss + l2_decay_loss}
2101    }
2102  }
2103  
2104  global.Trainer = Trainer;
2105  global.SGDTrainer = Trainer; // backwards compatibility
2106})(convnetjs);
2107
2108(function(global) {
2109  "use strict";
2110
2111  // used utilities, make explicit local references
2112  var randf = global.randf;
2113  var randi = global.randi;
2114  var Net = global.Net;
2115  var Trainer = global.Trainer;
2116  var maxmin = global.maxmin;
2117  var randperm = global.randperm;
2118  var weightedSample = global.weightedSample;
2119  var getopt = global.getopt;
2120  var arrUnique = global.arrUnique;
2121
2122  /*
2123  A MagicNet takes data: a list of convnetjs.Vol(), and labels
2124  which for now are assumed to be class indeces 0..K. MagicNet then:
2125  - creates data folds for cross-validation
2126  - samples candidate networks
2127  - evaluates candidate networks on all data folds
2128  - produces predictions by model-averaging the best networks
2129  */
2130  var MagicNet = function(data, labels, opt) {
2131    var opt = opt || {};
2132    if(typeof data === 'undefined') { data = []; }
2133    if(typeof labels === 'undefined') { labels = []; }
2134
2135    // required inputs
2136    this.data = data; // store these pointers to data
2137    this.labels = labels;
2138
2139    // optional inputs
2140    this.train_ratio = getopt(opt, 'train_ratio', 0.7);
2141    this.num_folds = getopt(opt, 'num_folds', 10);
2142    this.num_candidates = getopt(opt, 'num_candidates', 50); // we evaluate several in parallel
2143    // how many epochs of data to train every network? for every fold?
2144    // higher values mean higher accuracy in final results, but more expensive
2145    this.num_epochs = getopt(opt, 'num_epochs', 50); 
2146    // number of best models to average during prediction. Usually higher = better
2147    this.ensemble_size = getopt(opt, 'ensemble_size', 10);
2148
2149    // candidate parameters
2150    this.batch_size_min = getopt(opt, 'batch_size_min', 10);
2151    this.batch_size_max = getopt(opt, 'batch_size_max', 300);
2152    this.l2_decay_min = getopt(opt, 'l2_decay_min', -4);
2153    this.l2_decay_max = getopt(opt, 'l2_decay_max', 2);
2154    this.learning_rate_min = getopt(opt, 'learning_rate_min', -4);
2155    this.learning_rate_max = getopt(opt, 'learning_rate_max', 0);
2156    this.momentum_min = getopt(opt, 'momentum_min', 0.9);
2157    this.momentum_max = getopt(opt, 'momentum_max', 0.9);
2158    this.neurons_min = getopt(opt, 'neurons_min', 5);
2159    this.neurons_max = getopt(opt, 'neurons_max', 30);
2160
2161    // computed
2162    this.folds = []; // data fold indices, gets filled by sampleFolds()
2163    this.candidates = []; // candidate networks that are being currently evaluated
2164    this.evaluated_candidates = []; // history of all candidates that were fully evaluated on all folds
2165    this.unique_labels = arrUnique(labels);
2166    this.iter = 0; // iteration counter, goes from 0 -> num_epochs * num_training_data
2167    this.foldix = 0; // index of active fold
2168
2169    // callbacks
2170    this.finish_fold_callback = null;
2171    this.finish_batch_callback = null;
2172
2173    // initializations
2174    if(this.data.length > 0) {
2175      this.sampleFolds();
2176      this.sampleCandidates();
2177    }
2178  };
2179
2180  MagicNet.prototype = {
2181
2182    // sets this.folds to a sampling of this.num_folds folds
2183    sampleFolds: function() {
2184      var N = this.data.length;
2185      var num_train = Math.floor(this.train_ratio * N);
2186      this.folds = []; // flush folds, if any
2187      for(var i=0;i<this.num_folds;i++) {
2188        var p = randperm(N);
2189        this.folds.push({train_ix: p.slice(0, num_train), test_ix: p.slice(num_train, N)});
2190      }
2191    },
2192
2193    // returns a random candidate network
2194    sampleCandidate: function() {
2195      var input_depth = this.data[0].w.length;
2196      var num_classes = this.unique_labels.length;
2197
2198      // sample network topology and hyperparameters
2199      var layer_defs = [];
2200      layer_defs.push({type:'input', out_sx:1, out_sy:1, out_depth: input_depth});
2201      var nl = weightedSample([0,1,2,3], [0.2, 0.3, 0.3, 0.2]); // prefer nets with 1,2 hidden layers
2202      for(var q=0;q<nl;q++) {
2203        var ni = randi(this.neurons_min, this.neurons_max);
2204        var act = ['tanh','maxout','relu'][randi(0,3)];
2205        if(randf(0,1)<0.5) {
2206          var dp = Math.random();
2207          layer_defs.push({type:'fc', num_neurons: ni, activation: act, drop_prob: dp});
2208        } else {
2209          layer_defs.push({type:'fc', num_neurons: ni, activation: act});
2210        }
2211      }
2212      layer_defs.push({type:'softmax', num_classes: num_classes});
2213      var net = new Net();
2214      net.makeLayers(layer_defs);
2215
2216      // sample training hyperparameters
2217      var bs = randi(this.batch_size_min, this.batch_size_max); // batch size
2218      var l2 = Math.pow(10, randf(this.l2_decay_min, this.l2_decay_max)); // l2 weight decay
2219      var lr = Math.pow(10, randf(this.learning_rate_min, this.learning_rate_max)); // learning rate
2220      var mom = randf(this.momentum_min, this.momentum_max); // momentum. Lets just use 0.9, works okay usually ;p
2221      var tp = randf(0,1); // trainer type
2222      var trainer_def;
2223      if(tp<0.33) {
2224        trainer_def = {method:'adadelta', batch_size:bs, l2_decay:l2};
2225      } else if(tp<0.66) {
2226        trainer_def = {method:'adagrad', learning_rate: lr, batch_size:bs, l2_decay:l2};
2227      } else {
2228        trainer_def = {method:'sgd', learning_rate: lr, momentum: mom, batch_size:bs, l2_decay:l2};
2229      }
2230      
2231      var trainer = new Trainer(net, trainer_def);
2232
2233      var cand = {};
2234      cand.acc = [];
2235      cand.accv = 0; // this will maintained as sum(acc) for convenience
2236      cand.layer_defs = layer_defs;
2237      cand.trainer_def = trainer_def;
2238      cand.net = net;
2239      cand.trainer = trainer;
2240      return cand;
2241    },
2242
2243    // sets this.candidates with this.num_candidates candidate nets
2244    sampleCandidates: function() {
2245      this.candidates = []; // flush, if any
2246      for(var i=0;i<this.num_candidates;i++) {
2247        var cand = this.sampleCandidate();
2248        this.candidates.push(cand);
2249      }
2250    },
2251
2252    step: function() {
2253      
2254      // run an example through current candidate
2255      this.iter++;
2256
2257      // step all candidates on a random data point
2258      var fold = this.folds[this.foldix]; // active fold
2259      var dataix = fold.train_ix[randi(0, fold.train_ix.length)];
2260      for(var k=0;k<this.candidates.length;k++) {
2261        var x = this.data[dataix];
2262        var l = this.labels[dataix];
2263        this.candidates[k].trainer.train(x, l);
2264      }
2265
2266      // process consequences: sample new folds, or candidates
2267      var lastiter = this.num_epochs * fold.train_ix.length;
2268      if(this.iter >= lastiter) {
2269        // finished evaluation of this fold. Get final validation
2270        // accuracies, record them, and go on to next fold.
2271        var val_acc = this.evalValErrors();
2272        for(var k=0;k<this.candidates.length;k++) {
2273          var c = this.candidates[k];
2274          c.acc.push(val_acc[k]);
2275          c.accv += val_acc[k];
2276        }
2277        this.iter = 0; // reset step number
2278        this.foldix++; // increment fold
2279
2280        if(this.finish_fold_callback !== null) {
2281          this.finish_fold_callback();
2282        }
2283
2284        if(this.foldix >= this.folds.length) {
2285          // we finished all folds as well! Record these candidates
2286          // and sample new ones to evaluate.
2287          for(var k=0;k<this.candidates.length;k++) {
2288            this.evaluated_candidates.push(this.candidates[k]);
2289          }
2290          // sort evaluated candidates according to accuracy achieved
2291          this.evaluated_candidates.sort(function(a, b) { 
2292            return (a.accv / a.acc.length) 
2293                 > (b.accv / b.acc.length) 
2294                 ? -1 : 1;
2295          });
2296          // and clip only to the top few ones (lets place limit at 3*ensemble_size)
2297          // otherwise there are concerns with keeping these all in memory 
2298          // if MagicNet is being evaluated for a very long time
2299          if(this.evaluated_candidates.length > 3 * this.ensemble_size) {
2300            this.evaluated_candidates = this.evaluated_candidates.slice(0, 3 * this.ensemble_size);
2301          }
2302          if(this.finish_batch_callback !== null) {
2303            this.finish_batch_callback();
2304          }
2305          this.sampleCandidates(); // begin with new candidates
2306          this.foldix = 0; // reset this
2307        } else {
2308          // we will go on to another fold. reset all candidates nets
2309          for(var k=0;k<this.candidates.length;k++) {
2310            var c = this.candidates[k];
2311            var net = new Net();
2312            net.makeLayers(c.layer_defs);
2313            var trainer = new Trainer(net, c.trainer_def);
2314            c.net = net;
2315            c.trainer = trainer;
2316          }
2317        }
2318      }
2319    },
2320
2321    evalValErrors: function() {
2322      // evaluate candidates on validation data and return performance of current networks
2323      // as simple list
2324      var vals = [];
2325      var fold = this.folds[this.foldix]; // active fold
2326      for(var k=0;k<this.candidates.length;k++) {
2327        var net = this.candidates[k].net;
2328        var v = 0.0;
2329        for(var q=0;q<fold.test_ix.length;q++) {
2330          var x = this.data[fold.test_ix[q]];
2331          var l = this.labels[fold.test_ix[q]];
2332          net.forward(x);
2333          var yhat = net.getPrediction();
2334          v += (yhat === l ? 1.0 : 0.0); // 0 1 loss
2335        }
2336        v /= fold.test_ix.length; // normalize
2337        vals.push(v);
2338      }
2339      return vals;
2340    },
2341
2342    // returns prediction scores for given test data point, as Vol
2343    // uses an averaged prediction from the best ensemble_size models
2344    // x is a Vol.
2345    predict_soft: function(data) {
2346      // forward prop the best networks
2347      // and accumulate probabilities at last layer into a an output Vol
2348      var nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
2349      if(nv === 0) { return new convnetjs.Vol(0,0,0); } // not sure what to do here? we're not ready yet
2350      var xout, n;
2351      for(var j=0;j<nv;j++) {
2352        var net = this.evaluated_candidates[j].net;
2353        var x = net.forward(data);
2354        if(j===0) { 
2355          xout = x; 
2356          n = x.w.length; 
2357        } else {
2358          // add it on
2359          for(var d=0;d<n;d++) {
2360            xout.w[d] += x.w[d];
2361          }
2362        }
2363      }
2364      // produce average
2365      for(var d=0;d<n;d++) {
2366        xout.w[d] /= n;
2367      }
2368      return xout;
2369    },
2370
2371    predict: function(data) {
2372      var xout = this.predict_soft(data);
2373      if(xout.w.length !== 0) {
2374        var stats = maxmin(xout.w);
2375        var predicted_label = stats.maxi; 
2376      } else {
2377        var predicted_label = -1; // error out
2378      }
2379      return predicted_label;
2380
2381    },
2382
2383    toJSON: function() {
2384      // dump the top ensemble_size networks as a list
2385      var nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
2386      var json = {};
2387      json.nets = [];
2388      for(var i=0;i<nv;i++) {
2389        json.nets.push(this.evaluated_candidates[i].net.toJSON());
2390      }
2391      return json;
2392    },
2393
2394    fromJSON: function(json) {
2395      this.ensemble_size = json.nets.length;
2396      this.evaluated_candidates = [];
2397      for(var i=0;i<this.ensemble_size;i++) {
2398        var net = new Net();
2399        net.fromJSON(json.nets[i]);
2400        var dummy_candidate = {};
2401        dummy_candidate.net = net;
2402        this.evaluated_candidates.push(dummy_candidate);
2403      }
2404    },
2405
2406    // callback functions
2407    // called when a fold is finished, while evaluating a batch
2408    onFinishFold: function(f) { this.finish_fold_callback = f; },
2409    // called when a batch of candidates has finished evaluating
2410    onFinishBatch: function(f) { this.finish_batch_callback = f; }
2411    
2412  };
2413
2414  global.MagicNet = MagicNet;
2415})(convnetjs);
2416(function(lib) {
2417  "use strict";
2418  if (typeof module === "undefined" || typeof module.exports === "undefined") {
2419    window.jsfeat = lib; // in ordinary browser attach library to window
2420  } else {
2421    module.exports = lib; // in nodejs
2422  }
2423})(convnetjs);
2424
2425// GA addon for convnet.js
2426
2427(function(global) {
2428  "use strict";
2429  var Vol = convnetjs.Vol; // convenience
2430
2431  // used utilities, make explicit local references
2432  var randf = convnetjs.randf;
2433  var randn = convnetjs.randn;
2434  var randi = convnetjs.randi;
2435  var zeros = convnetjs.zeros;
2436  var Net = convnetjs.Net;
2437  var maxmin = convnetjs.maxmin;
2438  var randperm = convnetjs.randperm;
2439  var weightedSample = convnetjs.weightedSample;
2440  var getopt = convnetjs.getopt;
2441  var arrUnique = convnetjs.arrUnique;
2442
2443  function assert(condition, message) {
2444      if (!condition) {
2445          message = message || "Assertion failed";
2446          if (typeof Error !== "undefined") {
2447              throw new Error(message);
2448          }
2449          throw message; // Fallback
2450      }
2451  }
2452
2453  // returns a random cauchy random variable with gamma (control
2453s magnitude sort of like stdev in randn)
2454  // http://en.wikipedia.org/wiki/Cauchy_distribution
2455  var randc = function(m, gamma) {
2456    return m + gamma * 0.01 * randn(0.0, 1.0) / randn(0.0, 1.0);
2457  };
2458
2459  // chromosome implementation using an array of floats
2460  var Chromosome = function(floatArray) {
2461    this.fitness = 0; // default value
2462    this.nTrial = 0; // number of trials subjected to so far.
2463    this.gene = floatArray;
2464  };
2465
2466  Chromosome.prototype = {
2467    burst_mutate: function(burst_magnitude_) { // adds a normal random variable of stdev width, zero mean to each gene.
2468      var burst_magnitude = burst_magnitude_ || 0.1;
2469      var i, N;
2470      N = this.gene.length;
2471      for (i = 0; i < N; i++) {
2472        this.gene[i] += randn(0.0, burst_magnitude);
2473      }
2474    },
2475    randomize: function(burst_magnitude_) { // resets each gene to a random value with zero mean and stdev
2476      var burst_magnitude = burst_magnitude_ || 0.1;
2477      var i, N;
2478      N = this.gene.length;
2479      for (i = 0; i < N; i++) {
2480        this.gene[i] = randn(0.0, burst_magnitude);
2481      }
2482    },
2483    mutate: function(mutation_rate_, burst_magnitude_) { // adds random gaussian (0,stdev) to each gene with prob mutation_rate
2484      var mutation_rate = mutation_rate_ || 0.1;
2485      var burst_magnitude = burst_magnitude_ || 0.1;
2486      var i, N;
2487      N = this.gene.length;
2488      for (i = 0; i < N; i++) {
2489        if (randf(0,1) < mutation_rate) {
2490          this.gene[i] += randn(0.0, burst_magnitude);
2491        }
2492      }
2493    },
2494    crossover: function(partner, kid1, kid2) { // performs one-point crossover with partner to produce 2 kids
2495    //assumes all chromosomes are initialised with same array size. pls make sure of this before calling
2496      var i, N;
2497      N = this.gene.length;
2498      var l = randi(0, N); // crossover point
2499      for (i = 0; i < N; i++) {
2500        if (i < l) {
2501          kid1.gene[i] = this.gene[i];
2502          kid2.gene[i] = partner.gene[i];
2503        } else {
2504          kid1.gene[i] = partner.gene[i];
2505          kid2.gene[i] = this.gene[i];
2506        }
2507      }
2508    },
2509    copyFrom: function(c) { // copies c's gene into itself
2510      var i, N;
2511      this.copyFromGene(c.gene);
2512    },
2513    copyFromGene: function(gene) { // gene into itself
2514      var i, N;
2515      N = this.gene.length;
2516      for (i = 0; i < N; i++) {
2517        this.gene[i] = gene[i];
2518      }
2519    },
2520    clone: function() { // returns an exact copy of itself (into new memory, doesn't return reference)
2521      var newGene = zeros(this.gene.length);
2522      var i;
2523      for (i = 0; i < this.gene.length; i++) {
2524        newGene[i] = Math.round(10000*this.gene[i])/10000;
2525      }
2526      var c = new Chromosome(newGene);
2527      c.fitness = this.fitness;
2528      return c;
2529    }
2530  };
2531
2532  // counts the number of weights and biases in the network
2533  function getNetworkSize(net) {
2534    var layer = null;
2535    var filter = null;
2536    var bias = null;
2537    var w = null;
2538    var count = 0;
2539    var i, j, k;
2540    for ( i = 0; i < net.layers.length; i++) {
2541      layer = net.layers[i];
2542      filter = layer.filters;
2543      if (filter) {
2544        for ( j = 0; j < filter.length; j++) {
2545          w = filter[j].w;
2546          count += w.length;
2547        }
2548      }
2549      bias = layer.biases;
2550      if (bias) {
2551        w = bias.w;
2552        count += w.length;
2553      }
2554    }
2555    return count;
2556  }
2557
2558  function pushGeneToNetwork(net, gene) { // pushes the gene (floatArray) to fill up weights and biases in net
2559    var count = 0;
2560    var layer = null;
2561    var filter = null;
2562    var bias = null;
2563    var w = null;
2564    var i, j, k;
2565    for ( i = 0; i < net.layers.length; i++) {
2566      layer = net.layers[i];
2567      filter = layer.filters;
2568      if (filter) {
2569        for ( j = 0; j < filter.length; j++) {
2570          w = filter[j].w;
2571          for ( k = 0; k < w.length; k++) {
2572            w[k] = gene[count++];
2573          }
2574        }
2575      }
2576      bias = layer.biases;
2577      if (bias) {
2578        w = bias.w;
2579        for ( k = 0; k < w.length; k++) {
2580          w[k] = gene[count++];
2581        }  
2582      }
2583    }
2584  }
2585
2586  function getGeneFromNetwork(net) { // gets all the weight/biases from network in a floatArray
2587    var gene = [];
2588    var layer = null;
2589    var filter = null;
2590    var bias = null;
2591    var w = null;
2592    var i, j, k;
2593    for ( i = 0; i < net.layers.length; i++) {
2594      layer = net.layers[i];
2595      filter = layer.filters;
2596      if (filter) {
2597        for ( j = 0; j < filter.length; j++) {
2598          w = filter[j].w;
2599          for ( k = 0; k < w.length; k++) {
2600            gene.push(w[k]);
2601          }
2602        }
2603      }
2604      bias = layer.biases;
2605      if (bias) {
2606        w = bias.w;
2607        for ( k = 0; k < w.length; k++) {
2608          gene.push(w[k]);
2609        }  
2610      }
2611    }
2612    return gene;
2613  }
2614
2615  function copyFloatArray(x) { // returns a FloatArray copy of real numbered array x.
2616    var N = x.length;
2617    var y = zeros(N);
2618    for (var i = 0; i < N; i++) {
2619      y[i] = x[i];
2620    }
2621    return y;
2622  }
2623
2624  function copyFloatArrayIntoArray(x, y) { // copies a FloatArray copy of real numbered array x into y
2625    var N = x.length;
2626    for (var i = 0; i < N; i++) {
2627      y[i] = x[i];
2628    }
2629  }
2630
2631  // implementation of basic conventional neuroevolution algorithm (CNE)
2632  //
2633  // options:
2634  // population_size : positive integer
2635  // mutation_rate : [0, 1], when mutation happens, chance of each gene getting mutated
2636  // elite_percentage : [0, 0.3], only this group mates and produces offsprings
2637  // mutation_size : positive floating point.  stdev of gausian noise added for mutations
2638  // target_fitness : after fitness achieved is greater than this float value, learning stops
2639  // burst_generations : positive integer.  if best fitness doesn't improve after this number of generations
2640  //                    then mutate everything!
2641  // best_trial : default 1.  save best of best_trial's results for each chromosome.
2642  //
2643  // initGene:  init float array to initialize the chromosomes.  can be result obtained from pretrained sessions.
2644  var GATrainer = function(net, options_, initGene) {
2645
2646    this.net = net;
2647
2648    var options = options_ || {};
2649    this.population_size = typeof options.population_size !== 'undefined' ? options.population_size : 100;
2650    this.population_size = Math.floor(this.population_size/2)*2; // make sure even number
2651    this.mutation_rate = typeof options.mutation_rate !== 'undefined' ? options.mutation_rate : 0.01;
2652    this.elite_percentage = typeof options.elite_percentage !== 'undefined' ? options.elite_percentage : 0.2;
2653    this.mutation_size = typeof options.mutation_size !== 'undefined' ? options.mutation_size : 0.05;
2654    this.target_fitness = typeof options.target_fitness !== 'undefined' ? options.target_fitness : 10000000000000000;
2655    this.burst_generations = typeof options.burst_generations !== 'undefined' ? options.burst_generations : 10;
2656    this.best_trial = typeof options.best_trial !== 'undefined' ? options.best_trial : 1;
2657    this.chromosome_size = getNetworkSize(this.net);
2658
2659    var initChromosome = null;
2660    if (initGene) {
2661      initChromosome = new Chromosome(initGene);
2662    }
2663
2664    this.chromosomes = []; // population
2665    for (var i = 0; i < this.population_size; i++) {
2666      var chromosome = new Chromosome(zeros(this.chromosome_size));
2667      if (initChromosome) { // if initial gene supplied, burst mutate param.
2668        chromosome.copyFrom(initChromosome);
2669        pushGeneToNetwork(this.net, initChromosome.gene);
2670        if (i > 0) { // don't mutate the first guy.
2671          chromosome.burst_mutate(this.mutation_size);
2672        }
2673      } else {
2674        chromosome.randomize(1.0);
2675      }
2676      this.chromosomes.push(chromosome);
2677    }
2678
2679    this.bestFitness = -10000000000000000;
2680    this.bestFitnessCount = 0;
2681
2682  };
2683
2684  GATrainer.prototype = {
2685    train: function(fitFunc) { // has to pass in fitness function.  returns best fitness
2686      var bestFitFunc = function(nTrial, net) {
2687        var bestFitness = -10000000000000000;
2688        var fitness;
2689        for (var i = 0; i < nTrial; i++) {
2690          fitness = fitFunc(net);
2691          if (fitness > bestFitness) {
2692            bestFitness = fitness;
2693          }
2694        }
2695        return bestFitness;
2696      };
2697
2698      var i, N;
2699      var fitness;
2700      var c = this.chromosomes;
2701      N = this.population_size;
2702
2703      var bestFitness = -10000000000000000;
2704
2705      // process first net (the best one)
2706      pushGeneToNetwork(this.net, c[0].gene);
2707      fitness = bestFitFunc(this.best_trial, this.net);
2708      c[0].fitness = fitness;
2709      bestFitness = fitness;
2710      if (bestFitness > this.target_fitness) {
2711        return bestFitness;
2712      }
2713
2714      for (i = 1; i < N; i++) {
2715        pushGeneToNetwork(this.net, c[i].gene);
2716        fitness = bestFitFunc(this.best_trial, this.net);
2717        c[i].fitness = fitness;
2718        if (fitness > bestFitness) {
2719          bestFitness = fitness;
2720        }
2721      }
2722
2723      // sort the chromosomes by fitness
2724      c = c.sort(function (a, b) {
2725        if (a.fitness > b.fitness) { return -1; }
2726        if (a.fitness < b.fitness) { return 1; }
2727        return 0;
2728      });
2729
2730      var Nelite = Math.floor(Math.floor(this.elite_percentage*N)/2)*2; // even number
2731      for (i = Nelite; i < N; i+=2) {
2732        var p1 = randi(0, Nelite);
2733        var p2 = randi(0, Nelite);
2734        c[p1].crossover(c[p2], c[i], c[i+1]);
2735      }
2736
2737      for (i = 1; i < N; i++) { // keep best guy the same.  don't mutate the best one, so start from 1, not 0.
2738        c[i].mutate(this.mutation_rate, this.mutation_size);
2739      }
2740
2741      // push best one to network.
2742      pushGeneToNetwork(this.net, c[0].gene);
2743      if (bestFitness < this.bestFitness) { // didn't beat the record this time
2744        this.bestFitnessCount++;
2745        if (this.bestFitnessCount > this.burst_generations) { // stagnation, do burst mutate!
2746          for (i = 1; i < N; i++) { 
2747            c[i].copyFrom(c[0]);
2748            c[i].burst_mutate(this.mutation_size);
2749          }
2750          //c[0].burst_mutate(this.mutation_size); // don't mutate best solution.
2751        }
2752
2753      } else {
2754        this.bestFitnessCount = 0; // reset count for burst
2755        this.bestFitness = bestFitness; // record the best fitness score
2756      }
2757
2758      return bestFitness;
2759    }
2760  };
2761
2762  // variant of ESP network implemented
2763  // population of N sub neural nets, each to be co-evolved by ESPTrainer
2764  // fully recurrent.  outputs of each sub nn is also the input of all other sub nn's and itself.
2765  // inputs should be order of ~ -10 to +10, and expect output to be similar magnitude.
2766  // user can grab outputs of the the N sub networks and use them to accomplish some task for training
2767  //
2768  // Nsp: Number of sub populations (ie, 4)
2769  // Ninput: Number of real inputs to the system (ie, 2).  so actual number of input is Niput + Nsp
2770  // Nhidden:  Number of hidden neurons in each sub population (ie, 16)
2771  // genes: (optional) array of Nsp genes (floatArrays) to initialise the network (pretrained);
2772  var ESPNet = function(Nsp, Ninput, Nhidden, genes) {
2773    this.net = []; // an array of convnet.js feed forward nn's
2774    this.Ninput = Ninput;
2775    this.Nsp = Nsp;
2776    this.Nhidden = Nhidden;
2777    this.input = new convnetjs.Vol(1, 1, Nsp+Ninput); // hold most up to date input vector
2778    this.output = zeros(Nsp);
2779
2780    // define the architecture of each sub nn:
2781    var layer_defs = [];
2782    layer_defs.push({
2783      type: 'input',
2784      out_sx: 1,
2785      out_sy: 1,
2786      out_depth: (Ninput+Nsp)
2787    });
2788    layer_defs.push({
2789      type: 'fc',
2790      num_neurons: Nhidden,
2791      activation: 'sigmoid'
2792    });
2793    layer_defs.push({
2794      type: 'regression',
2795      num_neurons: 1 // one output for each sub nn, gets fed back into inputs.
2796    });
2797
2798    var network;
2799    for (var i = 0; i < Nsp; i++) {
2800      network = new convnetjs.Net();
2801      network.makeLayers(layer_defs);
2802      this.net.push(network);
2803    }
2804
2805    // if pretrained network is supplied:
2806    if (genes) {
2807      this.pushGenes(genes);
2808    }
2809  };
2810
2811  ESPNet.prototype = {
2812    feedback: function() { // feeds output back to last bit of input vector
2813      var i;
2814      var Ninput = this.Ninput;
2815      var Nsp = this.Nsp;
2816      for (i = 0; i < Nsp; i++) {
2817        this.input.w[i+Ninput] = this.output[i];
2818      }
2819    },
2820    setInput: function(input) { // input is a vector of length this.Ninput of real numbers
2821      // this function also grabs the previous most recent output and put it into the internal input vector
2822      var i;
2823      var Ninput = this.Ninput;
2824      var Nsp = this.Nsp;
2825      for (i = 0; i < Ninput; i++) {
2826        this.input.w[i] = input[i];
2827      }
2828      this.feedback();
2829    },
2830    forward: function() { // returns array of output of each Nsp neurons after a forward pass.
2831      var i, j;
2832      var Ninput = this.Ninput;
2833      var Nsp = this.Nsp;
2834      var y = zeros(Nsp);
2835      var a; // temp variable to old output of forward pass
2836      for (i = Nsp-1; i >= 0; i--) {
2837        if (i === 0) { // for the base network, forward with output of other support networks
2838          this.feedback();
2839        }
2840        a = this.net[i].forward(this.input); // forward pass sub nn # i
2841        y[i] = a.w[0]; // each sub nn only has one output.
2842        this.output[i] = y[i]; // set internal output to track output
2843      }
2844      return y;
2845    },
2846    getNetworkSize: function() { // return total number of weights and biases in a single sub nn.
2847      return getNetworkSize(this.net[0]); // each network has identical architecture.
2848    },
2849    getGenes: function() { // return an array of Nsp genes (floatArrays of length getNetworkSize())
2850      var i;
2851      var Nsp = this.Nsp;
2852      var result = [];
2853      for (i = 0; i < Nsp; i++) {
2854        result.push(getGeneFromNetwork(this.net[i]));
2855      }
2856      return result;
2857    },
2858    pushGenes: function(genes) { // genes is an array of Nsp genes (floatArrays)
2859      var i;
2860      var Nsp = this.Nsp;
2861      for (i = 0; i < Nsp; i++) {
2862        pushGeneToNetwork(this.net[i], genes[i]);
2863      }
2864    }
2865  };
2866
2867  // implementation of variation of Enforced Sub Population neuroevolution algorithm
2868  //
2869  // options:
2870  // population_size : population size of each subnetwork inside espnet
2871  // mutation_rate : [0, 1], when mutation happens, chance of each gene getting mutated
2872  // elite_percentage : [0, 0.3], only this group mates and produces offsprings
2873  // mutation_size : positive floating point.  stdev of gausian noise added for mutations
2874  // target_fitness : after fitness achieved is greater than this float value, learning stops
2875  // num_passes : number of times each neuron within a sub population is tested
2876  //          on average, each neuron will be tested num_passes * esp.Nsp times.
2877  // burst_generations : positive integer.  if best fitness doesn't improve after this number of generations
2878  //                    then start killing neurons that don't contribute to the bottom line! (reinit them with randoms)
2879  // best_mode : if true, this will assign each neuron to the best fitness trial it has experienced.
2880  //             if false, this will use the average of all trials experienced.
2881  // initGenes:  init Nsp array of floatarray to initialize the chromosomes.  can be result obtained from pretrained sessions.
2882  var ESPTrainer = function(espnet, options_, initGenes) {
2883
2884    this.espnet = espnet;
2885    this.Nsp = espnet.Nsp;
2886    var Nsp = this.Nsp;
2887
2888    var options = options_ || {};
2889    this.population_size = typeof options.population_size !== 'undefined' ? options.population_size : 50;
2890    this.population_size = Math.floor(this.population_size/2)*2; // make sure even number
2891    this.mutation_rate = typeof options.mutation_rate !== 'undefined' ? options.mutation_rate : 0.2;
2892    this.elite_percentage = typeof options.elite_percentage !== 'undefined' ? options.elite_percentage : 0.2;
2893    this.mutation_size = typeof options.mutation_size !== 'undefined' ? options.mutation_size : 0.02;
2894    this.target_fitness = typeof options.target_fitness !== 'undefined' ? options.target_fitness : 10000000000000000;
2895    this.num_passes = typeof options.num_passes !== 'undefined' ? options.num_passes : 2;
2896    this.burst_generations = typeof options.burst_generations !== 'undefined' ? options.burst_generations : 10;
2897    this.best_mode = typeof options.best_mode !== 'undefined' ? options.best_mode : false;
2898    this.chromosome_size = this.espnet.getNetworkSize();
2899
2900    this.initialize(initGenes);
2901  };
2902
2903  ESPTrainer.prototype = {
2904    initialize: function(initGenes) {
2905      var i, j;
2906      var y;
2907      var Nsp = this.Nsp;
2908      this.sp = []; // sub populations
2909      this.bestGenes = []; // array of Nsp number of genes, records the best combination of genes for the bestFitness achieved so far.
2910      var chromosomes, chromosome;
2911      for (i = 0; i < Nsp; i++) {
2912        chromosomes = []; // empty list of chromosomes
2913        for (j = 0; j < this.population_size; j++) {
2914          chromosome = new Chromosome(zeros(this.chromosome_size));
2915          if (initGenes) {
2916            chromosome.copyFromGene(initGenes[i]);
2917            if (j > 0) { // don't mutate first guy (pretrained)
2918              chromosome.burst_mutate(this.mutation_size);
2919            }
2920          } else { // push random genes to this.bestGenes since it has not been initalized.
2921            chromosome.randomize(1.0); // create random gene array if no pretrained one is supplied.
2922          }
2923          chromosomes.push(chromosome);
2924        }
2925        y = copyFloatArray(chromosomes[0].gene); // y should either be random init gene, or pretrained.
2926        this.bestGenes.push(y);
2927        this.sp.push(chromosomes); // push array of chromosomes into each population
2928      }
2929
2930      assert(this.bestGenes.length === Nsp);
2931      this.espnet.pushGenes(this.bestGenes); // initial
2932
2933      this.bestFitness = -10000000000000000;
2934      this.bestFitnessCount = 0;
2935    },
2936    train: function(fitFunc) { // has to pass in fitness function.  returns best fitness
2937
2938      var i, j, k, m, N, Nsp;
2939      var fitness;
2940      var c = this.sp; // array of arrays that holds every single chromosomes (Nsp x N);
2941      N = this.population_size; // number of chromosomes in each sub population
2942      Nsp = this.Nsp; // number of sub populations
2943
2944      var bestFitness = -10000000000000000;
2945      var bestSet, bestGenes;
2946      var cSet;
2947      var genes;
2948
2949      // helper function to return best fitness run nTrial times
2950      var bestFitFunc = function(nTrial, net) {
2951        var bestFitness = -10000000000000000;
2952        var fitness;
2953        for (var i = 0; i < nTrial; i++) {
2954          fitness = fitFunc(net);
2955          if (fitness > bestFitness) {
2956            bestFitness = fitness;
2957          }
2958        }
2959        return bestFitness;
2960      };
2961
2962      // helper function to create a new array filled with genes from an array of chromosomes
2963      // returns an array of Nsp floatArrays
2964      function getGenesFromChromosomes(s) {
2965        var g = [];
2966        for (var i = 0; i < s.length; i++) {
2967          g.push(copyFloatArray(s[i].gene));
2968        }
2969        return g;
2970      }
2971
2972      // makes a copy of an array of gene, helper function
2973      function makeCopyOfGenes(s) {
2974        var g = [];
2975        for (var i = 0; i < s.length; i++) {
2976          g.push(copyFloatArray(s[i]));
2977        }
2978        return g;
2979      }
2980
2981      // helper function, randomize all of nth sub population of entire chromosome set c
2982      function randomizeSubPopulation(n, c) {
2983        for (var i = 0; i < N; i++) {
2984          c[n][i].randomize(1.0);
2985        }
2986      }
2987
2988      // helper function used to sort the list of chromosomes according to their fitness
2989      function compareChromosomes(a, b) {
2990        if ((a.fitness/a.nTrial) > (b.fitness/b.nTrial)) { return -1; }
2991        if ((a.fitness/a.nTrial) < (b.fitness/b.nTrial)) { return 1; }
2992        return 0;
2993      }
2994
2995      // iterate over each gene in each sub population to initialise the nTrial to zero (will be incremented later)
2996      for (i = 0; i < Nsp; i++) { // loop over every sub population
2997        for (j = 0; j < N; j++) {
2998          if (this.best_mode) { // best mode turned on, no averaging, but just recording best score.
2999            c[i][j].nTrial = 1;
3000            c[i][j].fitness = -10000000000000000;
3001          } else {
3002            c[i][j].nTrial = 0;
3003            c[i][j].fitness = 0;
3004          }
3005        }
3006      }
3007
3008      // see if the global best gene has met target.  if so, can end it now.
3009      assert(this.bestGenes.length === Nsp);
3010      this.espnet.pushGenes(this.bestGenes); // put the random set of networks into the espnet
3011      fitness = fitFunc(this.espnet); // try out this set, and get the fitness
3012      if (fitness > this.target_fitness) {
3013        return fitness;
3014      }
3015      bestGenes = makeCopyOfGenes(this.bestGenes);
3016      bestFitness = fitness;
3017      //this.bestFitness = fitness;
3018
3019      // for each chromosome in a sub population, choose random chromosomes from all othet sub  populations to
3020      // build a espnet.  perform fitFunc on that esp net to get the fitness of that combination.  add the fitness
3021      // to this chromosome, and all participating chromosomes.  increment the nTrial of all participating
3022      // chromosomes by one, so afterwards they can be sorted by average fitness
3023      // repeat this process this.num_passes times
3024      for (k = 0; k < this.num_passes; k++) {
3025        for (i = 0; i < Nsp; i++) {
3026          for (j = 0; j < N; j++) {
3027            // build an array of chromosomes randomly
3028            cSet = [];
3029            for (m = 0; m < Nsp; m++) {
3030              if (m === i) { // push current iterated neuron
3031                cSet.push(c[m][j]);
3032              } else { // push random neuron in sub population m
3033                cSet.push(c[m][randi(0, N)]);
3034              }
3035            }
3036            genes = getGenesFromChromosomes(cSet);
3037            assert(genes.length === Nsp);
3038            this.espnet.pushGenes(genes); // put the random set of networks into the espnet
3039
3040            fitness = fitFunc(this.espnet); // try out this set, and get the fitness
3041
3042            for (m = 0; m < Nsp; m++) { // tally the scores into each participating neuron
3043              if (this.best_mode) {
3044                if (fitness > cSet[m].fitness) { // record best fitness this neuron participated in.
3045                  cSet[m].fitness = fitness;                
3046                }
3047              } else {
3048                cSet[m].nTrial += 1; // increase participation count for each participating neuron
3049                cSet[m].fitness += fitness;                
3050              }
3051            }
3052            if (fitness > bestFitness) {
3053              bestFitness = fitness;
3054              bestSet = cSet;
3055              bestGenes = genes;
3056            }
3057          }
3058        }
3059      }
3060
3061      // sort the chromosomes by average fitness
3062      for (i = 0; i < Nsp; i++) {
3063        c[i] = c[i].sort(compareChromosomes);
3064      }
3065
3066      var Nelite = Math.floor(Math.floor(this.elite_percentage*N)/2)*2; // even number
3067      for (i = 0; i < Nsp; i++) {
3068        for (j = Nelite; j < N; j+=2) {
3069          var p1 = randi(0, Nelite);
3070          var p2 = randi(0, Nelite);
3071          c[i][p1].crossover(c[i][p2], c[i][j], c[i][j+1]);
3072        }
3073      }
3074
3075      // mutate the population size after 2*Nelite (keep one set of crossovers unmutiliated!)
3076      for (i = 0; i < Nsp; i++) {
3077        for (j = 2*Nelite; j < N; j++) {
3078          c[i][j].mutate(this.mutation_rate, this.mutation_size);
3079        }
3080      }
3081
3082      // put global and local bestgenes in the last element of each gene
3083      for (i = 0; i < Nsp; i++) {
3084        c[i][N-1].copyFromGene( this.bestGenes[i] );
3085        c[i][N-2].copyFromGene( bestGenes[i] );
3086      }
3087
3088      if (bestFitness < this.bestFitness) { // didn't beat the record this time
3089        this.bestFitnessCount++;
3090        if (this.bestFitnessCount > this.burst_generations) { // stagnation, do burst mutate!
3091          // add code here when progress stagnates later.
3092          console.log('stagnating. burst mutate based on best solution.');
3093          var bestGenesCopy = makeCopyOfGenes(this.bestGenes);
3094          var bestFitnessCopy = this.bestFitness;
3095          this.initialize(bestGenesCopy);
3096          
3097          this.bestGenes = bestGenesCopy;
3098          this.bestFitness = this.bestFitnessCopy;
3099          
3100        }
3101
3102      } else {
3103        this.bestFitnessCount = 0; // reset count for burst
3104        this.bestFitness = bestFitness; // record the best fitness score
3105        this.bestGenes = bestGenes; // record the set of genes that generated the best fitness
3106      }
3107
3108      // push best one (found so far from all of history, not just this time) to network.
3109      assert(this.bestGenes.length === Nsp);
3110      this.espnet.pushGenes(this.bestGenes);
3111
3112      return bestFitness;
3113    }
3114  };
3115
3116  convnetjs.ESPNet = ESPNet;
3117  convnetjs.ESPTrainer = ESPTrainer;
3118  convnetjs.GATrainer = GATrainer;
3119})(convnetjs);
3120
3121// useful simple math functions
3122
3123var sign = Math.sign || function sign(x) {
3124	"use strict";
3125	x = +x; // convert to a number
3126	if (x === 0 || isNaN(x)) {
3127		return x;
3128	}
3129	return x > 0 ? 1 : -1;
3130};
3131
3132function rectify(x, minValue, maxValue) {
3133	"use strict";
3134	if (x > maxValue) return maxValue;
3135	if (x < minValue) return minValue;
3136	return x;
3137}
3138
3139function getWidth() {
3140	"use strict";
3141	return $(window).width() - 20 * 0;
3142}
3143
3144function getHeight() {
3145	"use strict";
3146	return $(window).height() - 20 * 0;
3147}
3148
3149// useful helper functions
3150var getRandom = function (min, max) {
3151	"use strict";
3152	return Math.random() * (max - min) + min;
3153};
3154
3155var getRandomInt = function (min, max) {
3156	"use strict";
3157	return Math.floor(Math.random() * (max - min)) + min;
3158};
3159
3160var getRandomColor = function () {
3161	"use strict";
3162	var c = color(random(127, 255), random(127, 255), random(127, 255));
3163	return c;
3164};
3165
3166var cosTable = new Array(360);
3167var sinTable = new Array(360);
3168var PI = Math.PI;
3169
3170// pre compute sine and cosine values to the nearest degree
3171for (i = 0; i < 360; i++) {
3172	cosTable[i] = Math.cos((i / 360) * 2 * PI);
3173	sinTable[i] = Math.sin((i / 360) * 2 * PI);
3174}
3175
3176var fastSin = function (xDeg) {
3177	"use strict";
3178	var deg = Math.round(xDeg);
3179	if (deg >= 0) {
3180		return sinTable[(deg % 360)];
3181	}
3182	return -sinTable[((-deg) % 360)];
3183};
3184
3185var fastCos = function (xDeg) {
3186	"use strict";
3187	var deg = Math.round(Math.abs(xDeg));
3188	return cosTable[deg % 360];
3189};
3190
3191// get orientation of mobile device
3192
3193/**
3194 * Determine the mobile operating system.
3195 * This function either returns 'iOS', 'Android' or 'unknown'
3196 *
3197 * @returns {String}
3198 */
3199function getMobileOperatingSystem() {
3200  var userAgent = navigator.userAgent || navigator.vendor || window.opera;
3201
3202  if( userAgent.match( /iPad/i ) || userAgent.match( /iPhone/i ) || userAgent.match( /iPod/i ) )
3203  {
3204    return 'iOS';
3205
3206  }
3207  else if( userAgent.match( /Android/i ) )
3208  {
3209
3210    return 'Android';
3211  }
3212  else
3213  {
3214    return 'unknown';
3215  }
3216}
3217
3218var Orientation = {
3219	enabled: false,
3220	x: 0,
3221	y: 0,
3222	z: 0,
3223	alpha: 0,
3224	beta: 0,
3225	gamma: 0,
3226	normalizer: 1,
3227	toString: function() {
3228		var result = "";
3229		if (this.enabled === true) {
3230			result = "a:"+this.alpha+"\tb:"+this.beta+"\tg:"+this.gamma+"\tx:"+this.x+"\ty:"+this.y+"\tz:"+this.z;
3231		}
3232		return result;
3233	},
3234	getX: function() {
3235		var result = 0;
3236		if (this.x) {
3237			result = this.x*this.normalizer;
3238		}
3239		return result;
3240	},
3241	getY: function() {
3242		var result = 0;
3243		if (this.y) {
3244			result = this.y*this.normalizer;
3245		}
3246		return result;
3247	},
3248	getZ: function() {
3249		var result = 0;
3250		if (this.z) {
3251			result = this.z;
3252		}
3253		return result;
3254	},
3255	getAlpha: function() {
3256		var result = 0;
3257		if (this.alpha) {
3258			result = this.alpha;
3259		}
3260		return result;
3261	},
3262	getBeta: function() {
3263		var result = 0;
3264		if (this.beta) {
3265			result = this.beta;
3266		}
3267		return result;
3268	},
3269	getGamma: function() {
3270		var result = 0;
3271		if (this.gamma) {
3272			result = this.gamma;
3273		}
3274		return result;
3275	},
3276	getMagnitude: function() {
3277		var result = 0.0;
3278		if (this.x && this.y) {
3279			return Math.sqrt(this.x*this.x+this.y*this.y);
3280		}
3281		return result;
3282	},
3283	getMag2: function() {
3284		var result = 0.0;
3285		if (this.x && this.y) {
3286			return this.x*this.x+this.y*this.y;
3287		}
3288		return result;
3289	}
3290
3291};
3292
3293if (getMobileOperatingSystem() === 'Android') {
3294	Orientation.normalizer = -1.0;
3295}
3296
3297window.addEventListener('devicemotion', function (e) {
3298    Orientation.x = Math.round(e.accelerationIncludingGravity.x*10)/10;
3299    Orientation.y = Math.round(e.accelerationIncludingGravity.y*10)/10;
3300    Orientation.z = Math.round(e.accelerationIncludingGravity.z*10)/10;       
3301    if (Orientation.getMag2() > 0.00000001) {
3302 		Orientation.enabled = true;
3303	} else {
3304		Orientation.enabled = false;
3305	}     
3306}, false);
3307
3308
3309window.addEventListener('deviceorientation', function (e) {
3310    Orientation.alpha = Math.round(e.alpha*10)/10;
3311    Orientation.beta = Math.round(e.beta*10)/10;
3312    Orientation.gamma = Math.round(e.gamma*10)/10;           
3313}, false);
3314
3315/*
3316if((window.DeviceMotionEvent) || ('listenForDeviceMovement' in window)){ // gyroscope support
3317    console.log('DeviceOrientationEvent support OK');
3318    Orientation.enabled = true;
3319} else {
3320    console.log('DeviceOrientationEvent support KO');
3321    Orientation.enabled = false;
3322}
3323*/
3324
3325// library of physical objects we can use in the p5 script.
3326
3327// constants:
3328// reference heights/widths
3329var ref_w = 36;
3330var ref_h = 48;
3331// define "one ref_u" relative to scale
3332var ref_u = 1;
3333
3334// A boundary is a simple rectangle with x,y,width,and height (box2d natural coordinates)
3335function Boundary(x_, y_, w_, h_) {
3336  "use strict";
3337	// But we also have to make a body for box2d to know about it
3338	// Body b;
3339	this.x = x_;
3340	this.y = y_;
3341	this.w = w_;
3342	this.h = h_;
3343	this.fillColor = getRandomColor();
3344
3345	var fd = new box2d.b2FixtureDef();
3346	fd.density = 1.0;
3347	fd.friction = 0.9;
3348	fd.restitution = 0.2;
3349
3350	var bd = new box2d.b2BodyDef();
3351
3352	bd.type = box2d.b2BodyType.b2_staticBody;
3353	bd.position.x = x_;
3354	bd.position.y = y_;
3355	fd.shape = new box2d.b2PolygonShape();
3356	fd.shape.SetAsBox(this.w / 2.0, this.h / 2.0);
3357	this.body = world.CreateBody(bd).CreateFixture(fd);
3358}
3359
3360// Draw the boundary, if it were at an angle we'd have to do something fancier
3361Boundary.prototype.display = function () {
3362  "use strict";
3363	var pos = scaleToPixels(this.x, this.y);
3364	fill(this.fillColor);
3365	stroke(40);
3366	strokeWeight(1);
3367	rectMode(CENTER);
3368	rect(pos.x, pos.y, scaleToPixels(this.w), scaleToPixels(this.h));
3369};
3370
3371// mouse spring object:
3372
3373// Constructor
3374function Spring(x, y) {
3375  "use strict";
3376	// At first it doesn't exist
3377	this.mouseJoint = null;
3378}
3379
3380// If it exists we set its target to the mouse location 
3381Spring.prototype.update = function (x, y) {
3382  "use strict";
3383	if (this.mouseJoint !== null) {
3384		// Always convert to world coordinates!
3385		var mouseWorld = scaleToWorld(x, y);
3386		this.mouseJoint.SetTarget(mouseWorld);
3387	}
3388};
3389
3390Spring.prototype.display = function () {
3391  "use strict";
3392	if (this.mouseJoint !== null) {
3393
3394		var posA = this.mouseJoint.GetAnchorA();
3395		var posB = this.mouseJoint.GetAnchorB();
3396
3397		// We can get the two anchor points
3398		var v1 = scaleToPixels(posA.x, posA.y);
3399		var v2 = scaleToPixels(posB.x, posB.y);
3400		// And just draw a line
3401		stroke(240);
3402		strokeWeight(1);
3403
3404		line(v1.x, v1.y, v2.x, v2.y);
3405	}
3406};
3407
3408// This is the key function where
3409// we attach the spring to an x,y location
3410// and the Box object's location
3411Spring.prototype.bind = function (x, y, box) {
3412  "use strict";
3413	// Define the joint
3414	var md = new box2d.b2MouseJointDef();
3415	// Body A is just a fake ground body for simplicity (there isn't anything at the mouse)
3416	md.bodyA = world.CreateBody(new box2d.b2BodyDef()); //world.GetGroundBody();
3417	// Body 2 is the box's boxy
3418	md.bodyB = box.body;
3419	// Get the mouse location in world coordinates
3420	var mp = scaleToWorld(x, y);
3421	// And that's the target
3422	//println(mp.x + " " + mp.y);
3423	md.target = mp;
3424	//println(md.target.x + " " + md.target.y);
3425
3426	// Some stuff about how strong and bouncy the spring should be
3427	md.maxForce = 2000.0 * box.body.m_mass;
3428	md.frequencyHz = 5;
3429	md.dampingRatio = 0.9;
3430
3431	// Make the joint!
3432	this.mouseJoint = world.CreateJoint(md);
3433};
3434
3435Spring.prototype.destroy = function () {
3436  "use strict";
3437	// We can get rid of the joint when the mouse is released
3438	if (this.mouseJoint !== null) {
3439		world.DestroyJoint(this.mouseJoint);
3440		this.mouseJoint = null;
3441	}
3442};
3443
3444// movable living objects below:
3445
3446// setup generic object that contains a set of reusable functions.  other shapes derive from B2Generic
3447var B2Generic = function () {
3448  "use strict";
3449	this.body = null;
3450	this.life = 60;
3451};
3452
3453// This function removes the particle from the box2d world, and also in paper.js
3454B2Generic.prototype.killBody = function () {
3455  "use strict";
3456	world.DestroyBody(this.body);
3457};
3458
3459B2Generic.prototype.contains = function (x, y) {
3460  "use strict";
3461	var worldPoint = scaleToWorld(x, y);
3462	var f = this.body.GetFixtureList();
3463	var inside = f.TestPoint(worldPoint);
3464	return inside;
3465};
3466
3467// returns screen position
3468B2Generic.prototype.getPosition = function () {
3469  "use strict";
3470	return scaleToPixels(this.body.GetPosition());
3471};
3472
3473// returns screen position
3474B2Generic.prototype.getWorldPosition = function () {
3475  "use strict";
3476	return this.body.GetPosition();
3477};
3478
3479// Is the particle ready for deletion?
3480B2Generic.prototype.done = function () {
3481  "use strict";
3482	var pos = this.body.GetPosition(); // world position
3483
3484	// kills it sooner if it rolls off the edge
3485
3486	// Is it off the bottom of the screen?  also kill it if the life is non positive
3487	/*
3488	if (pos.x > ref_w || pos.x < 0) {
3489		return true;
3490	}
3491	*/
3492
3493	// Is it off the bottom of the screen?  also kill it if the life is non positive
3494	if (this.life <= 0 || pos.y > scaleToWorld(height) + 10 * ref_u) {
3495		return true;
3496	}
3497	return false;
3498};
3499
3500B2Generic.prototype.contains = function (x, y) {
3501	var worldPoint = scaleToWorld(x, y);
3502	var f = this.body.GetFixtureList();
3503	var inside = f.TestPoint(worldPoint);
3504	return inside;
3505};
3506
3507// returns position relative to center, x-axis, in terms of ref_w
3508B2Generic.prototype.getRelativePosition = function () {
3509  "use strict";
3510	var bodyPos = this.body.GetPosition();
3511	return (bodyPos.x - ref_w / 2) / (64);
3512};
3513
3514var Setting = function (s) {
3515  "use strict";
3516	this.x1 = s.x1 || s.x || 0;
3517	this.x2 = s.x2 || s.x || ref_w;
3518	this.y1 = s.y1 || s.y || 0;
3519	this.y2 = s.y2 || s.y || ref_h;
3520	this.r = s.r || 2.4;
3521	this.w = s.w || 2.4;
3522	this.h = s.h || 3.6;
3523	this.numEdges = s.numEdges || 3;
3524	this.ignoreCollision = s.ignoreCollision || false;
3525	this.density = s.density || 1.0;
3526	this.friction = s.friction || 0.9;
3527	this.restitution = s.restitution || 0.1;
3528	this.fillColor = getRandomColor(); // always generate new color to spice things up!
3529	this.edgeColor = s.edgeColor || 40;
3530	this.initialMove = s.initialMove || false;
3531	this.parentObject = s.parentObject || null;
3532};
3533
3534// the box object:
3535
3536var Box = function (setting_) {
3537  "use strict";
3538	this.setting = new Setting(setting_);
3539
3540	//  console.log('new box created');
3541	//  console.log(this.setting);
3542
3543	// Define a body
3544	var bd = new box2d.b2BodyDef();
3545	bd.type = box2d.b2BodyType.b2_dynamicBody;
3546	bd.position.x = getRandom(this.setting.x1, this.setting.x2);
3547	bd.position.y = getRandom(this.setting.y1, this.setting.y2);
3548
3549	// Define a fixture
3550	var fd = new box2d.b2FixtureDef();
3551	// Fixture holds shape
3552	fd.shape = new box2d.b2PolygonShape();
3553	fd.shape.SetAsBox((this.setting.w / 2), (this.setting.h / 2));
3554
3555	// Some physics
3556	fd.density = this.setting.density;
3557	fd.friction = this.setting.friction;
3558	fd.restitution = this.setting.restitution;
3559
3560	// stick1 not subject to collision
3561	if (this.setting.ignoreCollision) {
3562		fd.filter.maskBits = 0;
3563		bd.linearDamping = 0.1;
3564	}
3565
3566	// Create the body
3567	this.body = world.CreateBody(bd);
3568	// Attach the fixture
3569	this.body.CreateFixture(fd);
3570	// needed for collision:
3571	this.body.SetUserData(this);
3572
3573	// Some additional stuff
3574	if (this.setting.initialMove) {
3575		this.body.SetLinearVelocity(new box2d.b2Vec2(random(-4, 4), random(-4, 0)));
3576		this.body.SetAngularVelocity(random(-4, 4));
3577	}
3578};
3579Box.prototype = new B2Generic();
3580
3581// Drawing the box
3582Box.prototype.display = function () {
3583  "use strict";
3584	// Get the body's position
3585	var pos = this.getPosition();
3586	// Get its angle of rotation
3587	var a = this.body.GetAngleRadians();
3588
3589	// Draw it!
3590	rectMode(CENTER);
3591	push();
3592	translate(pos.x, pos.y);
3593	rotate(a);
3594	fill(this.setting.fillColor);
3595	stroke(this.setting.edgeColor);
3596	strokeWeight(1);
3597	rect(0, 0, scaleToPixels(this.setting.w), scaleToPixels(this.setting.h));
3598	pop();
3599};
3600
3601// circle:
3602var Circle = function (s_) {
3603  "use strict";
3604	this.setting = new Setting(s_);
3605
3606	//  console.log('new circle created');
3607	//  console.log(this.setting);
3608
3609	// Define a body
3610	var bd = new box2d.b2BodyDef();
3611	bd.type = box2d.b2BodyType.b2_dynamicBody;
3612	bd.position.x = getRandom(this.setting.x1, this.setting.x2);
3613	bd.position.y = getRandom(this.setting.y1, this.setting.y2);
3614
3615	// Define a fixture
3616	var fd = new box2d.b2FixtureDef();
3617	// Fixture holds shape
3618	fd.shape = new box2d.b2CircleShape();
3619	fd.shape.m_radius = this.setting.r;
3620
3621	// Some physics
3622	fd.density = this.setting.density;
3623	fd.friction = this.setting.friction;
3624	fd.restitution = this.setting.restitution;
3625
3626	// stick1 not subject to collision
3627	if (this.setting.ignoreCollision) {
3628		fd.filter.maskBits = 0;
3629		bd.linearDamping = 0.1;
3630	}
3631
3632	// Create the body
3633	this.body = world.CreateBody(bd);
3634	// Attach the fixture
3635	this.body.CreateFixture(fd);
3636	// needed for collision:
3637	this.body.SetUserData(this);
3638
3639	// Some additional stuff
3640	if (this.setting.initialMove) {
3641		this.body.SetLinearVelocity(new box2d.b2Vec2(random(-4, 4), random(-4, 0)));
3642		this.body.SetAngularVelocity(random(-4, 4));
3643	}
3644};
3645Circle.prototype = new B2Generic();
3646
3647Circle.prototype.display = function () {
3648  "use strict";
3649	// Get the body's position in screen space
3650	var pos = this.getPosition();
3651	// Get its angle of rotation
3652	var a = this.body.GetAngleRadians();
3653
3654	// Draw it!
3655	rectMode(CENTER);
3656	push();
3657	translate(pos.x, pos.y);
3658	rotate(a);
3659	fill(this.setting.fillColor);
3660	stroke(this.setting.edgeColor);
3661	strokeWeight(1);
3662	ellipse(0, 0, scaleToPixels(this.setting.r * 2), scaleToPixels(this.setting.r * 2));
3663	// Let's add a line so we can see the rotation
3664	line(0, 0, scaleToPixels(this.setting.r), 0);
3665	pop();
3666};
3667
3668// NGon:
3669var NGon = function (s_) {
3670	this.setting = new Setting(s_);
3671
3672	//  console.log('new polygon created');
3673	//  console.log(this.setting);
3674
3675	// Define a body
3676	var bd = new box2d.b2BodyDef();
3677	bd.type = box2d.b2BodyType.b2_dynamicBody;
3678	bd.position.x = getRandom(this.setting.x1, this.setting.x2);
3679	bd.position.y = getRandom(this.setting.y1, this.setting.y2);
3680
3681	// Define a fixture
3682	var fd = new box2d.b2FixtureDef();
3683	// Fixture holds shape
3684	fd.shape = new box2d.b2PolygonShape();
3685
3686	var vertices = [];
3687	var angleStep = Math.PI * 2 / this.setting.numEdges;
3688	var i;
3689
3690	for (i = this.setting.numEdges; i >= 0; i--) {
3691		vertices.push(new box2d.b2Vec2(this.setting.r * Math.cos(i * angleStep), this.setting.r * Math.sin(i * angleStep)));
3692	}
3693
3694	fd.shape.SetAsArray(vertices, vertices.length);
3695
3696	// Some physics
3697	fd.density = this.setting.density;
3698	fd.friction = this.setting.friction;
3699	fd.restitution = this.setting.restitution;
3700
3701	// stick1 not subject to collision
3702	if (this.setting.ignoreCollision) {
3703		fd.filter.maskBits = 0;
3704		bd.linearDamping = 0.1;
3705	}
3706
3707	// Create the body
3708	this.body = world.CreateBody(bd);
3709	// Attach the fixture
3710	this.body.CreateFixture(fd);
3711	// needed for collision:
3712	this.body.SetUserData(this);
3713
3714	// Some additional stuff
3715	if (this.setting.initialMove) {
3716		this.body.SetLinearVelocity(new box2d.b2Vec2(random(-4, 4), random(-4, 0)));
3717		this.body.SetAngularVelocity(random(-4, 4));
3718	}
3719};
3720NGon.prototype = new B2Generic();
3721
3722NGon.prototype.display = function () {
3723  "use strict";
3724	// Get the body's position in screen space
3725	var pos = this.getPosition();
3726	// Get its angle of rotation
3727	var a = this.body.GetAngleRadians();
3728
3729	// Draw it!
3730	rectMode(CENTER);
3731	push();
3732	translate(pos.x, pos.y);
3733	rotate(a);
3734	fill(this.setting.fillColor);
3735	stroke(this.setting.edgeColor);
3736	strokeWeight(1);
3737
3738	var angleStep = Math.PI * 2 / this.setting.numEdges;
3739	var i;
3740	beginShape();
3741	for (i = this.setting.numEdges; i >= 0; i--) {
3742		vertex(scaleToPixels(this.setting.r * Math.cos(i * angleStep)), scaleToPixels(this.setting.r * Math.sin(i * angleStep)));
3743	}
3744	endShape(CLOSE);
3745
3746	// Let's add a line so we can see the rotation
3747	//line(0,0,scaleToPixels(this.setting.r),0);
3748
3749	pop();
3750};
3751
3752// Constructor
3753var Pendulum = function (s_) {
3754  "use strict";
3755	this.setting = s_;
3756	this.DEFAULTLIFE = 30 * 5; // default life ~ n seconds assumign 30fps
3757	this.CUTOFFANGLE = 70;
3758	this.life = this.setting.life || this.DEFAULTLIFE;
3759	this.dying = false;
3760	this.len = 9.6 * ref_u * 1.1;
3761	this.control = false; // when this is on, then pendulum is alive.
3762	this.manualControl = true; // manual override.  can override this.control
3763	this.controlDelay = -1; // delayed start upon contact.
3764	this.initControlDelay = 20;
3765	this.score = 0; // how well it is doing in the physical world (can do cool stuff like adj color)
3766
3767	// 1 means upward, -1 means facing ground.
3768	this.upright = -1.0;
3769
3770  	var moveEverythingAtBeginning = false;
3771  	if (this.setting.startMode && this.setting.startMode === "random") {
3772  		moveEverythingAtBeginning = true;
3773  	}
3774  	if (this.setting.startOrientation && this.setting.startOrientation === "upright") {
3775  		this.upright = 1.0;
3776  	}
3777
3778	// keep track of prev angles for velocity
3779	this.prevAngle = 0;
3780	this.prevLowerAngle = 0;
3781	this.angleVelocity = 0;
3782	this.lowerAngleVelocity = 0;
3783
3784	this.cart = new Box({
3785		x: this.setting.x,
3786		y: this.setting.y,
3787		w: 3 * ref_u,
3788		h: 1.6 * ref_u,
3789		density: 1.0 / 2,
3790		initialMove: moveEverythingAtBeginning,
3791		parentObject: this
3792	});
3793
3794	this.wheel = new Circle({
3795		x: this.setting.x,
3796		y: this.setting.y,
3797		r: 2.5 * ref_u,
3798		density: 5,
3799		initialMove: moveEverythingAtBeginning,
3800		parentObject: this
3801	});
3802	this.wheel.maxShaderLevel = 50;
3803	this.wheel.shaderLevel = 0;
3804	this.wheel.offColor = color(255 - this.wheel.maxShaderLevel, 190 - this.wheel.maxShaderLevel, 50 - this.wheel.maxShaderLevel);
3805
3806	this.wheel.setting.fillColor = this.wheel.offColor;
3807
3808	// joint is not selectable by mouse, and collision is ignored.
3809	var offCollision = true;
3810	this.stick1 = new Box({
3811		x: this.setting.x,
3812		y: this.setting.y,
3813		w: 0.8 * ref_u,
3814		h: this.len,
3815		density: 1.0 / 2,
3816		ignoreCollision: true,
3817		initialMove: moveEverythingAtBeginning,
3818		parentObject: this
3819	});
3820	this.stick2 = new Box({
3821		x: this.setting.x,
3822		y: this.setting.y,
3823		w: 0.8 * ref_u,
3824		h: this.len,
3825		density: 1.0 / 2,
3826		ignoreCollision: true,
3827		initialMove: moveEverythingAtBeginning,
3828		parentObject: this
3829	});
3830	this.handle = new Circle({
3831		x: this.setting.x,
3832		y: this.setting.y,
3833		w: 3.6 * ref_u / 2.5,
3834		r: 2.0 * ref_u,
3835		h: 3.6 * ref_u / 2,
3836		density: 1.0 / 6,
3837		ignoreCollision: offCollision,
3838		initialMove: moveEverythingAtBeginning,
3839		parentObject: this
3840	});
3841
3842	var wjd = new box2d.b2WeldJointDef();
3843	wjd.bodyA = this.cart.body;
3844	wjd.bodyB = this.stick1.body;
3845	wjd.localAnchorB = makeB2Vec2(0, this.upright * this.len / 2);
3846	var wj = world.CreateJoint(wjd);
3847
3848	var wjd2 = new box2d.b2RevoluteJointDef();
3849	wjd2.bodyA = this.stick2.body;
3850	wjd2.bodyB = this.stick1.body;
3851	wjd2.localAnchorA = makeB2Vec2(0, this.upright * this.len / 2);
3852	wjd2.localAnchorB = makeB2Vec2(0, -this.upright * this.len / 2);
3853	var wj2 = world.CreateJoint(wjd2);
3854
3855	var wjd3 = new box2d.b2WeldJointDef();
3856	wjd3.bodyA = this.handle.body;
3857	wjd3.bodyB = this.stick2.body;
3858	wjd3.localAnchorB = makeB2Vec2(0, -this.upright * this.len / 2);
3859	var wj3 = world.CreateJoint(wjd3);
3860
3861	var jd = new box2d.b2WheelJointDef(); // make wheel joint
3862	jd.bodyA = this.wheel.body;
3863	jd.bodyB = this.cart.body;
3864
3865	jd.frequencyHz = 30;
3866	jd.dampingRatio = 1.0;
3867	jd.maxMotorTorque = 3000;
3868
3869	this.motor = world.CreateJoint(jd);
3870	this.motor.EnableMotor(true);
3871	this.setMotorSpeed(0);
3872};
3873Pendulum.prototype = new B2Generic();
3874
3875// turns on and off control
3876Pendulum.prototype.enableControl = function () {
3877  "use strict";
3878	this.controlDelay = this.initControlDelay;
3879	this.initControlDelay = 1;
3880	//console.log('control enabled naturally.');
3881};
3882Pendulum.prototype.disableControl = function () {
3883  "use strict";
3884	this.control = false;
3885};
3886Pendulum.prototype.controlMode = function () {
3887  "use strict";
3888	return this.control && this.manualControl;
3889};
3890Pendulum.prototype.toggleControl = function () { // enable or disable controls manually override
3891  "use strict";
3892	this.manualControl = !this.manualControl;
3893};
3894
3895// This function removes the particle from the box2d world
3896Pendulum.prototype.killBody = function () {
3897  "use strict";
3898	this.cart.killBody();
3899	this.wheel.killBody();
3900	this.stick1.killBody();
3901  this.stick2.killBody();
3902	this.handle.killBody();
3903};
3904
3905Pendulum.prototype.done = function () {
3906  "use strict";
3907	return this.cart.done() || this.life <= 0;
3908};
3909
3910Pendulum.prototype.setMotorSpeed = function (speed) {
3911  "use strict";
3912	this.motor.SetMotorSpeed(speed);
3913};
3914
3915Pendulum.prototype.getMotorSpeed = function () {
3916  "use strict";
3917	return this.motor.GetMotorSpeed();
3918};
3919
3920// return angle of second handle, with no normalisation.
3921Pendulum.prototype.getRawAngleDegrees = function () {
3922  "use strict";
3923	var angle1 = (this.stick1.body.GetAngle() * 360 / (2 * PI));
3924	var angle2 = (this.stick2.body.GetAngle() * 360 / (2 * PI));
3925	return angle2;
3926};
3927
3928// return angle of second handle
3929Pendulum.prototype.getAngleDegrees = function () {
3930  "use strict";
3931	var angle = this.getRawAngleDegrees();
3932	angle += 90 * (1 - this.upright); // if pendulum is set to be inverted at the beginning, must rotate 180 deg.
3933	while (angle > 180) angle -= 360;
3934	while (angle < -180) angle += 360;
3935	return angle;
3936};
3937
3938// return angle of first handle, with no normalisation.
3939Pendulum.prototype.getRawLowerAngleDegrees = function () {
3940  "use strict";
3941	var angle1 = (this.stick1.body.GetAngle() * 360 / (2 * PI));
3942	var angle2 = (this.stick2.body.GetAngle() * 360 / (2 * PI));
3943	return angle1;
3944};
3945
3946// return angle of first handle
3947Pendulum.prototype.getLowerAngleDegrees = function () {
3948  "use strict";
3949	var angle = this.getRawLowerAngleDegrees();
3950	angle += 90 * (1 - this.upright); // if pendulum is set to be inverted at the beginning, must rotate 180 deg.
3951	while (angle > 180) angle -= 360;
3952	while (angle < -180) angle += 360;
3953	return angle;
3954};
3955
3956Pendulum.prototype.getPosition = function () {
3957  "use strict";
3958	var bodyPos = scaleToPixels(this.wheel.body.GetPosition());
3959	var handlePos = scaleToPixels(this.handle.body.GetPosition());
3960	var returnPos = new box2d.b2Vec2();
3961	returnPos.x = 0.9 * bodyPos.x + 0.1 * handlePos.x;
3962	returnPos.y = 0.9 * bodyPos.y + 0.1 * handlePos.y;
3963	return returnPos;
3964};
3965
3966Pendulum.prototype.getWorldPosition = function () {
3967  "use strict";
3968	var bodyPos = this.wheel.body.GetPosition();
3969	var handlePos = this.handle.body.GetPosition();
3970	var returnPos = new box2d.b2Vec2();
3971	returnPos.x = 0.9 * bodyPos.x + 0.1 * handlePos.x;
3972	returnPos.y = 0.9 * bodyPos.y + 0.1 * handlePos.y;
3973	return returnPos;
3974};
3975
3976// returns position relative to center, x-axis, in terms of ref_w
3977Pendulum.prototype.getRelativePosition = function () {
3978  "use strict";
3979	var bodyPos = this.wheel.body.GetPosition();
3980	return (bodyPos.x - ref_w / 2) / 64;
3981};
3982
3983Pendulum.prototype.getAngleVelocity = function () {
3984  "use strict";
3985	return this.angleVelocity;
3986};
3987
3988Pendulum.prototype.getLowerAngleVelocity = function () {
3989  "use strict";
3990	return this.lowerAngleVelocity;
3991};
3992
3993Pendulum.prototype.setScore = function(score) {
3994	"use strict";
3995	this.score = score;
3996};
3997
3998Pendulum.prototype.display = function () {
3999  "use strict";
4000	// Get the body's position
4001	//var pos1 = scaleToPixels(this.wheel.body.GetPosition());
4002	//var pos2 = scaleToPixels(this.stick1.body.GetPosition());
4003
4004	this.wheel.display();
4005	this.cart.display();
4006	this.handle.display();
4007	this.stick1.display();
4008  	this.stick2.display();
4009
4010	// change color slowly as it becomes enabled or disabled
4011	var enabled = this.controlMode();
4012	var r = red(this.wheel.setting.fillColor);
4013	var g = green(this.wheel.setting.fillColor);
4014	var b = blue(this.wheel.setting.fillColor);
4015	var shaderStep = 5;
4016
4017	if (enabled === true && this.wheel.shaderLevel <= this.wheel.maxShaderLevel) {
4018		this.wheel.shaderLevel += shaderStep;
4019		this.wheel.setting.fillColor = color(r + shaderStep, g + shaderStep, b + shaderStep);
4020	} else if (enabled === false && this.wheel.shaderLevel >= 0) {
4021		this.wheel.shaderLevel -= shaderStep;
4022		this.wheel.setting.fillColor = color(r - shaderStep, g - shaderStep, b - shaderStep);
4023	}
4024
4025	// cool trick to set upper handle's color level depending on score
4026	this.handle.setting.fillColor = color(255, 220, 50, min(50+this.score*10, 255));
4027
4028};
4029
4030Pendulum.prototype.update = function () {
4031  "use strict";
4032	// store previous angle and calculate angular velocity
4033	var currentAngle = this.getRawAngleDegrees();
4034	this.angleVelocity = 30 * (currentAngle - this.prevAngle);
4035	this.prevAngle = currentAngle;
4036	var currentLowerAngle = this.getRawLowerAngleDegrees();
4037	this.lowerAngleVelocity = 30 * (currentLowerAngle - this.prevLowerAngle);
4038	this.prevLowerAngle = currentLowerAngle;
4039
4040	// dying logic here.
4041	if (this.dying) {
4042		this.life--;
4043	}
4044
4045	if (this.controlDelay >= 0) {
4046		this.controlDelay--;
4047		if (this.controlDelay === 0) {
4048			this.control = true;
4049		}
4050	}
4051
4052	var currentAbsAngle = abs(this.getAngleDegrees());
4053	if (this.dying === false && currentAbsAngle > this.CUTOFFANGLE) {
4054//		this.dying = true;
4055	} else if (this.dying === true && currentAbsAngle <= this.CUTOFFANGLE) {
4056		this.dying = false;
4057		this.life = this.setting.life || this.DEFAULTLIFE;
4058	}
4059};
4060
4061function Timer() {
4062	"use strict";
4063	this.startTime = 0;
4064}
4065
4066Timer.prototype.start = function () {
4067	"use strict";
4068	this.startTime = millis();
4069};
4070
4071
4072Timer.prototype.reset = function () {
4073	"use strict";
4074	var timeTaken = millis() - this.startTime;
4075	this.startTime = millis();
4076	return timeTaken;
4077};
4078
4079function NeuroController(initGene_) {
4080	"use strict";
4081
4082	// load up json chromosome
4083
4084	// pretrain network goes here:
4085	var initGene = null || initGene_;
4086	this.geneData = null;
4087
4088	// set current internal states
4089	this.theta0 = 0;
4090	this.theta0_dot = 0;
4091	this.theta1 = 0;
4092	this.theta1_dot = 0;
4093	this.pos = 0;
4094
4095	this.isTraining = false;
4096	this.generation = 0;
4097
4098	// define the neural network architecture:
4099	this.Nsp = 2;
4100	this.espnet = new convnetjs.ESPNet(this.Nsp, 5, 16, initGene);
4101	// create initial network with this.Nsp subpopulations, 5 inputs, 32 hidden neurons
4102
4103	this.trainer = new convnetjs.ESPTrainer(this.espnet, {
4104			population_size: 50,
4105			mutation_rate: 0.20,
4106			mutation_size: 0.1,
4107			num_passes: 1,
4108			elite_percentage: 0.20,
4109			burst_generations: 8,
4110			best_mode: false
4111		},
4112		initGene);
4113
4114	this.geneData = this.espnet.getGenes();
4115
4116	// add random colors for each sub population, and keep them!
4117	this.colorR = convnetjs.zeros(this.Nsp);
4118	this.colorG = convnetjs.zeros(this.Nsp);
4119	this.colorB = convnetjs.zeros(this.Nsp);
4120	for (var i = 0; i < this.Nsp; i++ ) {
4121		this.colorR[i] = convnetjs.randi(0, 255);
4122		this.colorG[i] = convnetjs.randi(0, 255);
4123		this.colorB[i] = convnetjs.randi(0, 255);
4124	}
4125
4126}
4127
4128NeuroController.prototype.setNetwork = function (network) {
4129  "use strict";
4130	this.espnet = network;
4131};
4132
4133NeuroController.prototype.setInput = function (theta0, theta0_dot, theta1, theta1_dot, pos) {
4134  "use strict";
4135	this.theta0 = theta0;
4136	this.theta0_dot = theta0_dot;
4137	this.theta1 = theta1;
4138	this.theta1_dot = theta1_dot;
4139	this.pos = pos;
4140};
4141
4142NeuroController.prototype.train = function (fitFunc, nGeneration) {
4143  "use strict";
4144	var fitness = 0;
4145	for (var i = 0; i < nGeneration; i++) {
4146		this.isTraining = true;
4147		fitness = this.trainer.train(fitFunc);
4148		console.log('gen #' + (this.generation++) + ' fitness=' + Math.round(fitness * 100) / 100);
4149		this.isTraining = false;
4150	}
4151
4152	this.geneData = this.espnet.getGenes();
4153};
4154
4155NeuroController.prototype.getBestGenes = function () {
4156  "use strict";
4157	return this.trainer.bestGenes;
4158};
4159
4160NeuroController.prototype.getBestFitness = function () {
4161  "use strict";
4162	return this.trainer.bestFitness;
4163};
4164
4165// get current input for nn (normalised data so the values are in the order of -10 to +10)
4166NeuroController.prototype.getCurrentInputState = function () {
4167  "use strict";
4168  var x = [0, 0, 0, 0, 0];
4169  x[0] = this.theta0 / 18; // w is the field holding the actual data
4170  x[1] = this.theta0_dot / 18;
4171  x[2] = this.theta1 / 18; // w is the field holding the actual data
4172  x[3] = this.theta1_dot / 18;
4173  x[4] = this.pos * 10;
4174  return x;
4175};
4176
4177NeuroController.prototype.getOutput = function () {
4178  "use strict";
4179  // get output from neural network:
4180  var x = this.getCurrentInputState();
4181  this.espnet.setInput(x);
4182  var y = this.espnet.forward();
4183  return y;
4184};
4185
4186NeuroController.prototype.drawNetwork2 = function () {
4187	// draw network weights on p5.js canvas
4188	"use strict";
4189	// modify this code so that it works directly with bestGenes array.
4190	// modify code below:
4191
4192	var maxBoxSize = width/8;
4193
4194	function drawWeights(gene, level, numGene, r, g, b) {
4195	  var i, j, k;
4196
4197	  var shade;
4198	  var boxSize;
4199	  noStroke();
4200	  boxSize = Math.min(Math.floor(Math.sqrt((width-maxBoxSize)*height/(gene.length*numGene))/1.1), maxBoxSize);
4201
4202	  var nBoxRow = Math.floor((width-maxBoxSize)/boxSize);
4203
4204	  for(k = gene.length-1; k >= 0; k--) {
4205	    i = Math.floor(k/nBoxRow);
4206	    j = k % nBoxRow;
4207	    shade = 128*(gene[k]*0.5)+128;
4208	    fill(r, g, b, shade/6);
4209	    rect(j*boxSize,i*boxSize+level*(height/numGene), boxSize, boxSize);
4210	  }
4211	}
4212
4213	function drawIO(input, output) {
4214		var i;
4215		var shade;
4216		var inputLength = input.length;
4217		var outputLength = output.length;
4218		var boxSize = Math.min(Math.floor((min(height, width))/(inputLength+2)), maxBoxSize);
4219		noStroke();
4220		for(i = 0; i < inputLength; i++) {
4221			shade = min(255,max(128*(input[i])+128, 0));
4222			fill(0, 0, 255, shade/16);
4223			rect(width/2-inputLength*boxSize/2+(i+0)*boxSize, height*1/3, boxSize, boxSize);
4224		}
4225		/*
4226		for (i = 0; i < outputLength; i++) {
4227			shade = 128*(output[i])+128;
4228			fill(0, 0, shade, 40);
4229			rect(width-boxSize, (i+inputLength)*boxSize, boxSize, boxSize);		
4230		}
4231		*/
4232	}
4233/*
4234	var genes = this.geneData;
4235	var numGene = this.Nsp;
4236
4237	for (var i = 0; i < numGene; i++) {
4238		drawWeights(genes[i], i, numGene, this.colorR[i], this.colorG[i], this.colorB[i]);
4239	}
4240*/
4241
4242	drawIO(this.espnet.input.w, this.espnet.output);
4243};
4244
4245
4246// main sketch code:
4247
4248// A reference to our box2d world
4249var world;
4250
4251// gets the handy zeros function to generate a fast floating point array filled with zeros
4252var zeros = convnetjs.zeros;
4253
4254// A list we'll use to track fixed objects
4255var boundaries = [];
4256
4257var pendulum; // define the main character of this show.
4258var follow; // random ball that bounces around.
4259var baseplate; // the movable plate for which the pendulum moves around on.
4260
4261// A list of movable objects that can be moved by the spring.
4262var movable = [];
4263
4264// mouse manipulate objects via spring
4265var spring;
4266
4267// keep track of the number of steps that occured during simulation
4268var stepNumber = 0;
4269
4270// put in the time dimesion variables
4271var timeStep = 1.0 / 30;
4272
4273// which mode of control to use:
4274var controlMode = {
4275	mode: "neural",
4276	setMode: function (theMode) {
4277		"use strict";
4278		this.mode = theMode;
4279	},
4280	getMode: function () {
4281		return this.mode;
4282	}
4283};
4284
4285// timer object
4286var timer = null;
4287
4288// pretrain network goes here:
4289var initial_gene = null;
4290var geneData = null;
4291
4292// train at all?
4293var initTrainMode = false;
4294
4295var drawNetworkMode = true;
4296var printDetailMode = false;
4297
4298var numTrainBatch = 4; // how many generations should we train at at time before seeing the results in real time once.
4299
4300// define scores for controller functions
4301var initGeneJSON_random = '[{"0":-2.5980645610962028,"1":-1.9264891753902016,"2":0.07637552436792505,"3":0.6670183328050008,"4":1.272548645016405,"5":1.6183754716111167,"6":2.542840866344356,"7":3.131609365786479,"8":1.3847377992249301,"9":0.12702343241044295,"10":0.8289531277662427,"11":0.2029118421578732,"12":-0.5830151795937791,"13":-0.12260567543862913,"14":-0.8786616662640772,"15":0.10383988661702334,"16":-0.632761753397626,"17":-1.4426008075953374,"18":1.1091375288423577,"19":-0.7427014669762009,"20":0.15648628714750307,"21":-0.35100244461121183,"22":-0.852459818446918,"23":0.4157578076490787,"24":-1.1225277018214044,"25":1.1788974606583937,"26":-1.7648444492581297,"27":2.3916025233578133,"28":1.7338696636249264,"29":0.9215684838498516,"30":0.1374732826797588,"31":-0.2907554265453783,"32":-0.3771172599139394,"33":-0.12065569108686522,"34":-1.869302188132859,"35":1.638240576682271,"36":0.568057152884639,"37":0.17284346473953288,"38":-1.6236993686775565,"39":-1.4994773186046002,"40":-2.7868865187342204,"41":1.575399230188649,"42":0.0010487684544205622,"43":-2.009233794773864,"44":0.08197342955452065,"45":-1.6292205922916643,"46":1.0623567527107507,"47":0.1577245525406597,"48":-0.10325492710168246,"49":2.0934582449476298,"50":0.08353263763859753,"51":2.173676348498481,"52":0.9607505882081186,"53":-0.9377288674421345,"54":-1.501660708897993,"55":-1.524727681204356,"56":-0.25124433733472334,"57":0.10066986880918256,"58":-2.134790538345844,"59":0.8480114108823263,"60":-0.4687604512981024,"61":0.35378381538540316,"62":-0.26433614529804983,"63":1.2882343728106227,"64":-0.0042549034589568074,"65":0.13199777162646237,"66":-0.31918459640653396,"67":0.049394161799997796,"68":-0.13863791236614828,"69":0.40963362395503955,"70":-1.4814019532660947,"71":-1.768389520256723,"72":-1.2242436824069673,"73":0.35198684867580626,"74":0.539155609548777,"75":-1.5988305477624536,"76":1.389156071199981,"77":0.10812609349060548,"78":0.16776146157973493,"79":-2.170123919721531,"80":-0.15946596086893086,"81":-0.7769551195096259,"82":2.0780792191834903,"83":0.664414034195271,"84":-2.6328668626449194,"85":0.33801088448186867,"86":-1.1246558230288441,"87":0.8461585793027331,"88":-0.3076008136222011,"89":0.8026492926185533,"90":-0.7553006877811433,"91":-0.19898393497673722,"92":-2.256727156596284,"93":0.19742460918297827,"94":1.3032371217533767,"95":1.8842489350158047,"96":-0.4128087316752014,"97":-0.4040961012851025,"98":-1.6357366018261297,"99":0.42325959222887244,"100":0.3612276108897758,"101":0.10003982054735525,"102":0.5509588150755031,"103":-1.8776541279177055,"104":2.0004791161460638,"105":0.9
4301039100406447154,"106":-0.1787683096239166,"107":-0.9106764256697535,"108":-0.275514307910816,"109":1.2773983013172876,"110":0.018503239834874594,"111":0.006053170836477863,"112":0.9550501351332908,"113":0.27776248233975864,"114":-0.7611106674356081,"115":-1.6664791013584068,"116":-0.54962948449961,"117":1.222477772920564,"118":-2.353305199425632,"119":1.4283137929767176,"120":2.222349179140049,"121":0.05938042017814221,"122":-0.3444363670204805,"123":-0.7405626580209286,"124":-2.0511963778471607,"125":0.48807047758500327,"126":-1.9061212085492005,"127":-2.3809407528043236,"128":-1.0182779304518637,"129":3.5047836476582943,"130":-1.531657049124523,"131":0.08051953646775865,"132":1.2544752920897566,"133":0.08183301748676394,"134":-0.32771997751673865,"135":-2.0376675808989577,"136":-1.7505275193225567,"137":-0.9846016753214257,"138":0.682663979118413,"139":-0.523903906969104,"140":-1.8355520310429836,"141":0.2925721722912017,"142":-0.14888001236338255,"143":2.6910246828058657,"144":1.410697047559158},{"0":-0.06157752911113837,"1":0.9816390982148266,"2":-0.10918905854947444,"3":1.0876433487574215,"4":-0.3545354460698098,"5":0.7806313603113639,"6":-0.8757028449927096,"7":2.681712931710798,"8":0.
430107088197213461361,"9":-0.19543502868785814,"10":-0.7691718148476056,"11":-1.0390743256002968,"12":-0.8915587377390528,"13":-0.0047258225533586065,"14":-1.8044407754769554,"15":3.3295199119253267,"16":0.5510023740655496,"17":1.2428333282453492,"18":-0.41342807770531587,"19":0.5283327177729286,"20":1.447831820356721,"21":-0.9182796687646566,"22":-0.049164811347046145,"23":-0.3811200623277931,"24":-1.397958426043949,"25":0.2991314793169192,"26":1.5696734294473866,"27":0.2463370146590489,"28":-2.525222343579142,"29":-0.2830222147102804,"30":-1.4125218109640918,"31":-1.6839188026576106,"32":-0.8500761017247863,"33":1.699736561146109,"34":-0.874387356078126,"35":-0.42738832737307564,"36":1.3643081426524268,"37":-1.8714485202941478,"38":0.9640928508544087,"39":2.168715335672983,"40":-1.9436228617013391,"41":0.19285846043229032,"42":0.15407336896319623,"43":-2.263768347315484,"44":0.14019428446008217,"45":1.4604888845965205,"46":-0.2568896334410081,"47":0.48913357345470426,"48":-0.6212327735401325,"49":0.803673179889956,"50":-1.1494633728166528,"51":1.4879829815188477,"52":-0.6009487021342236,"53":0.9016342722774113,"54":0.5818570058943275,"55":0.9456305584364377,"56":2.1458649759860324,"57":-1.3862621074739219,"58":-0.027532504846727895,"59":1.7720923184971424,"60":1.8588657271286788,"61":-1.325776801803349,"62":-2.6633110404049694,"63":2.0997759530616356,"64":0.5307162805612904,"65":2.4930816140857592,"66":-2.6992247865220924,"67":-0.6151290151822345,"68":0.5678230213579526,"69":0.6809453114449404,"70":-0.6768835307727936,"71":0.3897255958034984,"72":0.9557127394562057,"73":0.6522894976567295,"74":-0.40815100777359437,"75":0.0661326076037508,"76":-0.5645614180531691,"77":2.0414719720678365,"78":3.7787739061288423,"79":0.2614192042998548,"80":-1.988381644991136,"81":1.107102375637584,"82":0.48604270474601513,"83":-0.014313581090933045,"84":0.5611695933719538,"85":-0.3333075597265268,"86":-1.23273697148324,"87":0.8321654803468709,"88":0.11476200142802381,"89":2.2604421730378084,"90":2.779299685297202,"91":0.9514189999367164,"92":0.17049035397196222,"93":-1.8229214423950433,"94":-0.4971782189327847,"95":-1.327358636081475,"96":0.28399546427844896,"97":0.04293109582458024,"98":-0.08571922154557592,"99":-0.4447146090625111,"100":-1.0241767617569302,"101":1.0635798511995047,"102":1.7612181015170636,"103":1.1040736056935907,"104":-1.7854924536840324,"105":-0.12429820690592522,"106":-0.8506719130110633,"107":-0.0007927947115421441,"108":0.47147547925242117,"109":-0.7131351090933178,"110":-1.915797847677861,"111":-2.022245650352164,"112":-0.9609992681441093,"113":1.5821533770703857,"114":2.877911245908887,"115":2.5829012795204633,"116":-0.7596120077117487,"117":2.128583222290037,"118":-0.6847442225608336,"119":-0.5521973077631342,"120":-1.9128536575481907,"121":1.0832895386459427,"122":0.35421286292343634,"123":-0.7520818876853157,"124":1.3280165348557889,"125":3.8989841337426703,"126":0.32346071724868286,"127":0.38634505512861583,"128":-1.2945308396569484,"129":0.8562925949183655,"130":1.1926989074465928,"131":-0.48440272528893497,"132":0.30483845568117274,"133":-0.36597679402518873,"134":-0.762273487708312,"135":0.038680956185777826,"136":0.4057500915151626,"137":-1.6417598773993691,"138":1.0884717847290397,"139":1.7583285208348245,"140":-0.7103201505130154,"141":-1.0258561519302345,"142":-1.451453296165857,"143":0.42367509510572904,"144":-1.1354318688444254}]';
4302
4303var initGene = JSON.parse(initGeneJSON_random);
4304
4305var swingController = new NeuroController(initGene);
4306
4307// the below implements the scoring system developed by Grual et al (1996).
4308swingController.resetScore = function() {
4309	// initialize variable to hold historical information
4310	"use strict";
4311	this.histScore = zeros(100);
4312	this.histIndex = 0;
4313	this.histLife = 0; // the time this thing has been alive for.
4314};
4315swingController.pushScore = function (thetaLower, thetaLowerDot, thetaUpper, thetaUpperDot, speed, relPos) {
4316	var wThetaLower = 1;
4317	var wThetaLowerDot = 1;
4318	var wThetaUpper = 1;
4319	var wThetaUpperDot = 1;
4320	var wSpeed = 1;
4321	var wRelPos = 1.0;
4322	//return (thetaLower * thetaLower * wThetaLower + thetaLowerDot * thetaLowerDot * wThetaLowerDot + thetaUpper * thetaUpper * wThetaUpper + thetaUpperDot * thetaUpperDot * wThetaUpperDot + speed * speed * wSpeed + relPos * relPos*wRelPos) * -1;
4323	var score = Math.abs(thetaLower)*wThetaLower+Math.abs(thetaLowerDot)*wThetaLowerDot+Math.abs(thetaUpper)*wThetaUpper+Math.abs(thetaUpperDot)*wThetaUpperDot+Math.abs(speed)*wSpeed+Math.abs(relPos)*wRelPos;
4324	this.histScore[this.histIndex] = score;
4325	this.histIndex++;
4326	if (this.histIndex >= 100) this.histIndex = 0;
4327	this.histLife++; // increase time this guy has been alive for by one.
4328};
4329swingController.getScore = function() {
4330	function f1(t) {
4331		return t / 1000;
4332	}
4333	function f2(t, x) {
4334		if (t < 100) {
4335			return 0;
4336		}
4337		var s = 0;
4338		for (var i = x.length-1; i >= 0; i--) {
4339			s += x[i]*0.01;
4340		}
4341		//console.log(s);
4342		return 10000*0.75 / s;
4343	}
4344	return 0.1 * f1(this.histLife) + 0.9 * f2(this.histLife, this.histScore);
4345};
4346swingController.controllerName = "swing up";
4347swingController.resetScore();
4348
4349
4350
4351
4352// define main controllers:
4353var neuroController = swingController; // train neuroController;
4354var trainNeuroController = true;
4355
4356var gravityFactor = 2;
4357
4358function setGravity(x, y) {
4359  "use strict";
4360	var gravityVec = new box2d.b2Vec2(x, y);
4361	world.SetGravity(gravityVec);
4362}
4363
4364function setupCamera() {
4365  "use strict";
4366	b2Camera.x_b2 = ref_w / 2;
4367	b2Camera.y_b2 = ref_h / 2;
4368
4369	b2Camera.x_pixel = width / 2;
4370	b2Camera.y_pixel = height / 2;
4371
4372	b2Camera.scaleFactor = min((width - 10) / ref_w, (height - 10) / ref_h);
4373}
4374
4375// simulation loop, with no drawing
4376function update(nStep) {
4377  "use strict";
4378
4379	var i;
4380  var step;
4381	// update important tracking positions:
4382
4383	var pendulumWorldPos;
4384	var pendulumRelPos;
4385	var currentAngle;
4386	var currentAngleVelocity;
4387	var currentLowerAngle;
4388	var currentLowerAngleVelocity;
4389	var targetSpeed = 0;
4390  	var relPosition = 0;
4391	var score = 0;
4392
4393	for (step = 0; step < nStep; step++) {
4394
4395		// We must always step through time!
4396		// 2nd and 3rd arguments are velocity and position iterations
4397		world.Step(timeStep, 10, 10);
4398
4399		// update important tracking positions:
4400
4401		pendulumWorldPos = pendulum.getWorldPosition();
4402		pendulumRelPos = pendulum.getRelativePosition();
4403		currentAngle = pendulum.getAngleDegrees();
4404		currentAngleVelocity = pendulum.getAngleVelocity();
4405		currentLowerAngle = pendulum.getLowerAngleDegrees();
4406		currentLowerAngleVelocity = pendulum.getLowerAngleVelocity();
4407		targetSpeed = 0;
4408		relPosition = pendulumRelPos*100; // try to center the pendulum.
4409
4410		pendulum.update();
4411
4412		// if pendulum is off, don't do any control
4413		if (!pendulum.controlMode()) {
4414			controlMode.setMode("off");
4415			pendulum.setMotorSpeed(0);
4416		} else {
4417			controlMode.setMode("neural");
4418		}
4419
4420		if (controlMode.getMode() === "neural") {
4421			// deal with control systems here:
4422
4423			neuroController.setInput(currentAngle, currentAngleVelocity, currentLowerAngle, currentLowerAngleVelocity, pendulumRelPos);
4424			var y = neuroController.getOutput();
4425			targetSpeed = 0;
4426
4427			targetSpeed += y[0];
4428
4429			//    if (abs(targetSpeed) > 1024) targetSpeed = 0;
4430			pendulum.setMotorSpeed(targetSpeed * 100);
4431
4432		}
4433
4434		// deal with dead objects here:
4435		for (i = movable.length - 1; i >= 0; i--) {
4436			if (movable[i].done()) { // object is dead
4437				if (movable[i] === baseplate) {
4438					movable[i].killBody();
4439					baseplate = new Box(movable[i].setting);
4440					movable[i] = baseplate;
4441				} else if (movable[i] instanceof Box) { // make boxes reborn
4442					movable[i].killBody();
4443					movable[i] = new Box(movable[i].setting);
4444				} else if (movable[i] instanceof Circle) { // make circle reborn
4445					movable[i].killBody();
4446					movable[i] = new Circle(movable[i].setting);
4447				} else if (movable[i] instanceof NGon) { // make circle reborn
4448					movable[i].killBody();
4449					movable[i] = new NGon(movable[i].setting);
4450				}
4451			}
4452		}
4453
4454		// calculate score
4455		neuroController.pushScore(currentLowerAngle, currentLowerAngleVelocity, currentAngle, currentAngleVelocity, targetSpeed*100, relPosition*10);
4456
4457		score = neuroController.getScore();
4458
4459		// deal with dead pendulum:
4460		if (pendulum.done() || (stepNumber > 60*300 && neuroController.getScore() < 50) ) {
4461			pendulum.killBody();
4462			makeNewPendulum(0); // downright
4463			if (neuroController.isTraining === false) { // if guy died while not being training, train another generation.
4464				trainNeuroController = true;
4465			}
4466			return score;
4467		}
4468
4469		if (neuroController.getScore() > 30) {
4470			stepNumber = 0;
4471		}
4472
4473	}
4474
4475	return score;
4476
4477}
4478
4479// create pendulum 3 different ways, to train for different modes.
4480function makeNewPendulum(mode) {
4481	"use strict";
4482	var startMode_ = "notrandom";
4483	var startOrientation_ = "downright";
4484	var randomInitPosX = 0.0; // if below is set to 1, then init position of pendulum will be random.  0 - center
4485	var randomInitPosY = 1.0;
4486
4487	var posX = ref_w * (4) / 8 + randomInitPosX*(random(-0.5, 0.5))* ref_w * (6) / 8;
4488
4489	if (mode === 1) {
4490		randomInitPosX = 0.95;
4491		randomInitPosY = 1.0;
4492		startOrientation_ = "upright";
4493		startMode_ = "notrandom";
4494		posX = ref_w * (4) / 8 + randomInitPosX*(getRandomInt(0, 1)-0.5)* ref_w * (6) / 8;
4495	} /*else if (mode === 2) {
4496		randomInitPos = 0.0;
4497		startMode_ = "random";
4498		startOrientation_ = "upright";
4499	}
4500*/
4501
4502	neuroController.resetScore(); // has to reset score to zero since new pendulum.
4503
4504	pendulum = new Pendulum({
4505		x: posX,
4506		y: -ref_h * (5.5) / 8 + randomInitPosY * getRandom(ref_h * (-2) / 8, ref_h * (2) / 8),
4507		startMode: startMode_,
4508		startOrientation: startOrientation_
4509	});
4510
4511}
4512
4513// fitness function for GA algorithm.
4514var pendulumFitness = function (network) {
4515	"use strict";
4516	//neuroController.setNetwork(network);
4517	var result = 1;
4518
4519	pendulum.killBody();
4520	makeNewPendulum(0); // downright
4521	result *= update(20 * 30);
4522
4523
4524	pendulum.killBody();
4525	makeNewPendulum(1); // upright
4526	result *= update(20 * 20);
4527
4528	pendulum.killBody();
4529	makeNewPendulum(0); // downright
4530
4531	return result;
4532};
4533
4534function setup() {
4535  "use strict";
4536
4537	//var myCanvas = createCanvas(getWidth() - 0, getHeight() * 1.0);
4538	//myCanvas.parent('p5Container');
4539  var myCanvas;
4540  if (mobileMode) {
4541    myCanvas = createCanvas(275,385);
4542  } else { // desktop
4543    myCanvas = createCanvas(max($(window).width()/1, 275), 385);
4544  }
4545  myCanvas.parent('p5Container');
4546	frameRate(60);
4547
4548	// Initialize box2d physics and create the world
4549	world = createWorld();
4550	setGravity(0, 9.81 * 2);
4551
4552	world.SetContactListener(new CustomListener()); // check to see if things collided.
4553
4554	// Make the spring (it doesn't really get initialized until the mouse is clicked)
4555	spring = new Spring();
4556
4557	// make a ball for pendulum to follow around
4558	follow = new Circle({
4559	  x1 : ref_w*2/8,
4560	  x2 : ref_w*6/8,
4561	  y1 : ref_h*5/8,
4562	  y2 : ref_h*7/8,
4563	  r : 5.0 * ref_u,
4564	  numEdges : 3,
4565	  initialMove : true
4566	});
4567	movable.push( follow );
4568
4569	makeNewPendulum(0); // downright
4570	pendulum.setMotorSpeed(random(0, 0));
4571
4572	// push base plate
4573	baseplate = new Box({
4574		x1: ref_w / 2 - 1.0 * ref_u * 0,
4575		x2: ref_w / 2 + 1.0 * ref_u * 0,
4576		y1: ref_h * 4.0 / 8,
4577		y2: ref_h * 4.5 / 8,
4578		w: ref_w - 2.75 * ref_u,
4579		h: 3 * ref_u,
4580		density: 5,
4581		initialMove: false
4582	});
4583	movable.push(baseplate);
4584
4585	// Add a bunch of fixed boundaries
4586	boundaries.push(new Boundary(ref_w / 2, ref_h - 0.6 * ref_u, ref_w - 0.2, 1.0 * ref_u));
4587
4588	boundaries.push(new Boundary(2.1 * ref_u, ref_h * 5 / 8 + 0.5 * ref_u, 2.0 * ref_u, 1.0 * ref_u));
4589	boundaries.push(new Boundary(ref_w - 2.1 * ref_u, ref_h * 5 / 8 + 0.5 * ref_u, 2.0 * ref_u, 1.0 * ref_u));
4590
4591	boundaries.push(new Boundary(0.6 * ref_u, ref_h * 6.5 / 8 - 1.0 * ref_u, 1.0 * ref_u, ref_h * 3 / 8));
4592	boundaries.push(new Boundary(ref_w - 0.6 * ref_u, ref_h * 6.5 / 8 - 1.0 * ref_u, 1.0 * ref_u, ref_h * 3 / 8));
4593
4594	setupCamera();
4595
4596	timer = new Timer();
4597	timer.start();
4598
4599/*
4600  	if (drawNetworkMode) $("#nn_weights").text(JSON.stringify(neuroController.getChromosome()));
4601*/
4602}
4603
4604function draw() {
4605  "use strict";
4606
4607  if (mainNavBarStatus === false) { // not at the main section yet
4608    return;
4609  }
4610
4611	if (trainNeuroController) {
4612	    stepNumber = 0;
4613	    var fitnessTimer = new Timer();
4614	    fitnessTimer.start();
4615		trainNeuroController = false;
4616	    if (initTrainMode) {
4617			background(255);
4618	  		neuroController.train(pendulumFitness, numTrainBatch);
4619	    }
4620	    var c = neuroController.getBestGenes();
4621	    var bestFitness = neuroController.getBestFitness();
4622		//if (drawNetworkMode) $("#nn_weights").text(JSON.stringify(c));
4623	    //console.log('best fit: ' + round(bestFitness*10)/10 + '\t time: ' + fitnessTimer.reset());
4624	}
4625
4626	var score = update(1);
4627
4628	// other info
4629	var pendulumRelPos = pendulum.getRelativePosition();
4630	var currentAngle = pendulum.getAngleDegrees();
4631	var currentAngleVelocity = pendulum.getAngleVelocity();
4632	var currentLowerAngle = pendulum.getLowerAngleDegrees();
4633	var currentLowerAngleVelocity = pendulum.getLowerAngleVelocity();
4634	var targetSpeed = pendulum.getMotorSpeed();
4635
4636	background(255);
4637
4638  // draw gravity
4639  if (true) {
4640    stroke(130, 170, 255);
4641    line(width*7/8, width*1/16, width*7/8+(Orientation.getX()/9.81)*(width*0.125), (0.5+Math.abs(Orientation.getY()/9.81))*(width*0.125));
4642    if (Orientation.enabled) {
4643      setGravity(Orientation.getX()/32,9.8*2);
4644    } else {
4645      setGravity(0, 9.8*2);
4646    }
4647  }
4648
4649	// show neural network:
4650	if (drawNetworkMode) {
4651		neuroController.drawNetwork2();
4652		//neuroController.drawNetwork();
4653	}
4654
4655	// draw gravity
4656	/*
4657	if (true) {
4658		stroke(130, 170, 255);
4659		line(width * 7 / 8, width * 1 / 16, width * 7 / 8 + (Orientation.getX() / 9.81) * (width * 0.125), (0.5 + Math.abs(Orientation.getY() / 9.81)) * (width * 0.125));
4660		//    if (Orientation.enabled) {
4661		//      setGravity(Orientation.getX()*gravityFactor,Math.abs(Orientation.getY()*gravityFactor));
4662		//    } else {
4663		//      setGravity(0, 9.8*gravityFactor*2);
4664		//    }
4665	}
4666	*/
4667
4668	var i;
4669
4670
4671	// Display all the boundaries
4672	for (i = boundaries.length - 1; i >= 0; i--) {
4673		boundaries[i].display();
4674	}
4675
4676	for (i = movable.length - 1; i >= 0; i--) {
4677		movable[i].display();
4678	}
4679
4680	// print fps
4681	fill(250, 0, 50, 128);
4682	stroke(250, 0, 50, 128);
4683	textFont("Courier New");
4684	textSize(16);
4685	//text('fps: ' + floor(1000 / timer.reset() * 1.0) / 1.0, 10, 20);
4686	
4687	if (printDetailMode) {
4688		text('generation #'+neuroController.generation, 10, 40);
4689		
4690		text('pos*100: ' + round(1000.0 * pendulumRelPos)/10.0, 10, 60);
4691		text('upperTheta: ' + round(100.0 * currentAngle) / 100.0, 10, 80);
4692		text('upperThetaDot: ' + round(1.0 * currentAngleVelocity) / 1.0, 10, 100);
4693		text('lowerTheta: ' + round(100.0 * currentLowerAngle) / 100.0, 10, 120);
4694		text('lowerThetaDot: ' + round(1.0 * currentLowerAngleVelocity) / 1.0, 10, 140);
4695		text('motorSpeed: ' + round(1.0 * targetSpeed) / 1.0, 10, 160);
4696		text('score: ' + round(100.0 * score) / 100.0, 10, 180);
4697		text('step: ' + stepNumber++, 10, 200);
4698		text('name: ' + neuroController.controllerName, 10, 220);
4699	}
4700
4701
4702
4703
4704	// show pendulum last since it is main character.
4705	pendulum.setScore(score);
4706	pendulum.display();
4707
4708
4709}
4710
4711// When the mouse is released we're done with the spring
4712var deviceReleased = function () {
4713  "use strict";
4714	spring.destroy();
4715};
4716
4717// When the mouse is pressed we. . .
4718var devicePressed = function (x, y) {
4719  "use strict";
4720	// Check to see if the mouse was clicked on the movable object
4721	var i;
4722	var movableObj;
4723	for (i = movable.length - 1; i >= 0; i--) {
4724		movableObj = movable[i];
4725		if (movableObj.contains(x, y)) {
4726			spring.bind(x, y, movableObj);
4727		}
4728	}
4729
4730	if (pendulum.wheel.contains(x, y)) {
4731		// And if so, bind the mouse location to the box with a spring
4732		spring.bind(x, y, pendulum.wheel);
4733		// toggle control when wheel is touched or pressed by mouse
4734		pendulum.toggleControl();
4735	} else if (pendulum.stick1.contains(x, y)) {
4736		// And if so, bind the mouse location to the box with a spring
4737		spring.bind(x, y, pendulum.stick1);
4738	} else if (pendulum.handle.contains(x, y)) {
4739		// And if so, bind the mouse location to the box with a spring
4740		spring.bind(x, y, pendulum.handle);
4741	}
4742
4743};
4744
4745
4746// ContactListener to listen for collisions!
4747
4748var CustomListener = function () {
4749  "use strict";
4750};
4751
4752// Collision event functions!
4753CustomListener.prototype.BeginContact = function (contact) {
4754  "use strict";
4755	// Get both fixtures
4756	var f1 = contact.GetFixtureA();
4757	var f2 = contact.GetFixtureB();
4758	// Get both bodies
4759	var b1 = f1.GetBody();
4760	var b2 = f2.GetBody();
4761
4762	// Get our objects that reference these bodies
4763	var o1 = b1.GetUserData();
4764	var o2 = b2.GetUserData();
4765
4766	if ((o1 === baseplate && o2 === pendulum.wheel) || (o2 === baseplate && o1 === pendulum.wheel)) {
4767		pendulum.enableControl();
4768	}
4769
4770};
4771
4772// Objects stop touching each other
4773CustomListener.prototype.EndContact = function (contact) {
4774  "use strict";
4775	// Get both fixtures
4776	var f1 = contact.GetFixtureA();
4777	var f2 = contact.GetFixtureB();
4778	// Get both bodies
4779	var b1 = f1.GetBody();
4780	var b2 = f2.GetBody();
4781
4782	// Get our objects that reference these bodies
4783	var o1 = b1.GetUserData();
4784	var o2 = b2.GetUserData();
4785
4786	if ((o1 === baseplate && o2 === pendulum.wheel) || (o2 === baseplate && o1 === pendulum.wheel)) {
4787		pendulum.disableControl();
4788	}
4789};
4790
4791CustomListener.prototype.PreSolve = function (contact, manifold) {};
4792
4793CustomListener.prototype.PostSolve = function (contact, manifold) {};
4794
4795
4796// interaction with touchpad and mosue:
4797
4798var deviceDragged = function (x, y) {
4799  "use strict";
4800	// Always alert the spring to the new mouse location
4801	spring.update(x, y);
4802};
4803
4804/*
4805if (mobileMode === false) {
4806  console.log('defining functions for pendulum interaction');
4807
4808var mouseDragged = function () {
4809  "use strict";
4810	deviceDragged(mouseX, mouseY);
4811	return false;
4812};
4813
4814var touchMoved = function () {
4815  "use strict";
4816	deviceDragged(touchX, touchY);
4817	return false;
4818};
4819
4820var mouseReleased = function () {
4821  "use strict";
4822	deviceReleased();
4823	return false;
4824};
4825
4826var touchEnded = function () {
4827  "use strict";
4828	deviceReleased();
4829	return false;
4830};
4831
4832var mousePressed = function () {
4833  "use strict";
4834	devicePressed(mouseX, mouseY);
4835	return false;
4836};
4837
4838var touchStarted = function () {
4839  "use strict";
4840	devicePressed(touchX, touchY);
4841	return false;
4842};
4843}
4844*/
4845

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.