Skip to content

Commit

Permalink
Many changes applied
Browse files Browse the repository at this point in the history
1. improve performance of `buffer.js`
2. cutdown dependence on `util.js`
3. Add functools, record, encode utils
4. Fix a bug in `request.js`
5. bring in library for function ploting
  • Loading branch information
suquark committed Feb 23, 2017
1 parent 659c9bb commit c0298b7
Show file tree
Hide file tree
Showing 13 changed files with 479 additions and 166 deletions.
13 changes: 3 additions & 10 deletions RL/deepqlearn.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import {
getopt,
randi,
randf,
sample_from_dist,
sample,
random_replace,
one_hot,
AvgWindow
} from 'util.js';
import { getopt, AvgWindow } from 'util.js';
import { randi, randf, sample_from_dist, sample, random_replace } from 'util/random.js';
import { one_hot } from 'util/array.js';

import { Vol } from 'vol.js';

Expand Down
2 changes: 1 addition & 1 deletion RL/helper/objects/item.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { randf, randi } from 'util.js';
import { randf, randi } from 'util/random.js';
import { Circle, Line, Vec } from '../geometry.js';

class Item {
Expand Down
83 changes: 32 additions & 51 deletions backend/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import { checkTypeStrict } from 'util/assert.js';

// FIXME: not very efficient
function bufCopy(dst, src, start=0) {
let b_out = new Int8Array(dst), b_in = new Int8Array(src);
let N = b_in.byteLength;
for (let i = 0; i < N; i++) {
b_out[i + start] = b_in[i];
}
new Uint8Array(dst).set(new Uint8Array(src), start);
}

class Buffer {
Expand All @@ -16,73 +12,57 @@ class Buffer {
this.offset = 0;
}

align(len) {
let new_offset = Math.ceil(this.offset / len) | 0;
this.offset = new_offset * len;
return this.offset;
_read(TypedArr, count) {
let W = TypedArr.BYTES_PER_ELEMENT;
// assume that W is 2^k, so we can do align faster with bitwise ops
if (this.offset & (W - 1)) this.offset = (this.offset & W) + W;
let v = new TypedArr(this.buffer, this.offset, count);
this.offset += W * count;
return v;
}

read(count, type) {
var value;
switch (type) {
case 'Uint8Array':
value = new Uint8Array(this.buffer, this.offset, count);
this.offset += count;
break;
return this._read(Uint8Array, count);
case 'Uint8ClampedArray':
value = new Uint8ClampedArray(this.buffer, this.offset, count);
this.offset += count;
break;
return this._read(Uint8ClampedArray, count);
case 'Int8Array':
value = new Int8Array(this.buffer, this.offset, count);
this.offset += count;
break;
return this._read(Int8Array, count);
case 'Uint16Array':
value = new Uint16Array(this.buffer, this.align(2), count);
this.offset += 2 * count;
break;
return this._read(Uint16Array, count);
case 'Int16Array':
value = new Int16Array(this.buffer, this.align(2), count);
this.offset += 2 * count;
break;
return this._read(Int16Array, count);
case 'Uint32Array':
value = new Uint32Array(this.buffer, this.align(4), count);
this.offset += 4 * count;
break;
return this._read(Uint32Array, count);
case 'Int32Array':
value = new Int32Array(this.buffer, this.align(4), count);
this.offset += 4 * count;
break;
return this._read(Int32Array, count);
case 'Float32Array':
value = new Float32Array(this.buffer, this.align(4), count);
this.offset += 4 * count;
break;
return this._read(Float32Array, count);
case 'Float64Array':
value = new Float32Array(this.buffer, this.align(8), count);
this.offset += 8 * count;
break;
return this._read(Float64Array, count);
default:
throw 'unexpected type';
}
return value;
}

/**
* Alloc new space at overflow
*/
_refit(length) {
// FIXME: not very efficient.
length = 1 << Math.ceil(Math.log2(length) + 0.5);
let buf_new = new ArrayBuffer(length);
bufCopy(buf_new, this.buffer);
this.buffer = buf_new;
_check_fit(length) {
if (length > this.byteLength) {
length = 1 << Math.ceil(Math.log2(length) + 0.5);
let buf_new = new ArrayBuffer(length);
bufCopy(buf_new, this.buffer);
this.buffer = buf_new;
}
}

/**
* @param { ArrayBuffer } buffer
*/
write(buffer) {
if (!checkTypeStrict(buffer, 'ArrayBuffer')) buffer = buffer.slice().buffer;
if (this.offset + buffer.byteLength > this.byteLength) {
this._refit(this.offset + buffer.byteLength);
}
this._check_fit(this.offset + buffer.byteLength);
bufCopy(this.buffer, buffer, this.offset);
this.offset += buffer.byteLength; // update offset
}
Expand All @@ -97,11 +77,12 @@ class Buffer {

// Try to load typed array
static load(v, buf) {
return buf.read(v.length, v.type);
if (v.type === 'ArrayBuffer')
return buf.read(v.byteLength, Uint8Array).buffer;
else
return buf.read(v.length, v.type);
}

//getNativeType

static fromURL(url) {
return getBinary(url).then(buf => new Buffer(buf));
}
Expand Down
2 changes: 1 addition & 1 deletion backend/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ImageBuffer {
return ib;
}

save(buf) {
__save__(buf) {
for (let i of this.images) buf.write(i);
return {name: this.name, shape: this.shape, count: this.images.length, type: 'images'};
}
Expand Down
31 changes: 17 additions & 14 deletions backend/symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ function storeAt(dir, path, value) {
////// Load Part //////

function batchLoadFile2Global(pairs, callback) {
return Promise.all(pairs.map(p => loadFile2Global(p[0], p[1])));
return Promise.all(pairs.map(([mapfile, rawfile]) => loadFile2Global(mapfile, rawfile)));
}


function loadFile2Global(mapfile, rawfile) {
// create a pair of file access
let task_pair = [getJSON(mapfile), Buffer.fromURL(rawfile)];
// wait for both of then to be done and return a `Promise`
return Promise.all(task_pair).then(pair => load2Global(pair[0], pair[1]));
return Promise.all(task_pair).then(([json, buf]) => load2Global(json, buf));
}


Expand All @@ -114,8 +114,7 @@ function load2Global(map, buf) {
* @param { Buffer } buf - The Buffer contains data
*/
function _loadDir(dir, map, buf) {
for (let i in map) {
let item = map[i];
for (let item of map) {
let name = item.name;
if (name.endsWith('[]/')) {
dir[name] = [];
Expand All @@ -137,8 +136,7 @@ function _loadDir(dir, map, buf) {
* @param { BufferReader } buf - The ArrayBuffer contains data
*/
function _loadList(list, map, buf) {
for (let i in map) {
let item = map[i];
for (let item of map) {
let name = item.name;
if (name.endsWith('[]/')) {
let l = [];
Expand Down Expand Up @@ -168,6 +166,8 @@ function _loadValue(v, buf) {
return Tensor.load(v, buf);
case 'images':
return ImageBuffer.load(v, buf);
case 'json':
return v.content;
default:
return Buffer.load(v, buf);
}
Expand Down Expand Up @@ -208,26 +208,29 @@ function _saveDir(dir, maplist, buf) {
packet = { name: name, nodes: [] };
_saveDir(dir[name], packet.nodes, buf);
} else {
packet = _saveValue(dir[name], buf);
// We attach save() method to all objects...
packet = dir[name].save(buf);
packet.name = name; // override name
}
maplist.push(packet);
}
}

function _saveValue(value, buf) {
// we use the simple way at present.
return value.save(buf)

// never should we use arrow function here!!!
Object.getPrototypeOf(Int8Array.prototype).__save__ = function(buf) {
// really ... hacking ways
buf.write(this.slice().buffer);
return {type: getNativeType(this), name: this.name, length: this.length};
}


// never use arrow function here!!!
Object.getPrototypeOf(Int8Array.prototype).save = function(buf) {
// really ... hacking ways
ArrayBuffer.prototype.__save__ = function(buf) {
buf.write(this);
return {type: getNativeType(this), name: this.name, length: this.length};
return { name: this.name, type:'ArrayBuffer', byteLength:this.byteLength };
}

Object.prototype.__save__ = function() { return { name: this.name, type:'json', content: this }; }

export {
globals,
Expand Down
3 changes: 2 additions & 1 deletion optimizer/adam.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Adam extends grad_optimizer {
super(len, opt);
this.k = 0;
this.learning_rate = getopt(opt, 'learning_rate', 0.01);
this.lr_decay = getopt(opt, 'lr_decay', 0);
this.beta1 = getopt(opt, 'beta1', 0.9);
this.beta2 = getopt(opt, 'beta2', 0.999);
this.v = zeros(len);
Expand All @@ -28,8 +29,8 @@ class Adam extends grad_optimizer {
update(x, g) {
++this.k; // k > 0, or 1 / biasCorr will be NaN
let v = this.v, w = this.w;
let lr = this.learning_rate;
let k = this.k;
let lr = this.lr_decay > 0 ? this.learning_rate / (1 + this.lr_decay * k) : this.learning_rate;
let beta1 = this.beta1, beta2 = this.beta2;
/**
* initialization bias correction terms,
Expand Down
60 changes: 4 additions & 56 deletions util.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,61 +29,9 @@ function normalize_angle(angle) {
}


class AvgWindow {
// a window stores _size_ number of values
// and returns averages. Useful for keeping running
// track of validation or training accuracy during SGD
constructor(size=100, minsize=20) {
this.v = [];
this.sum = 0;
}
add(x) {
this.v.push(x);
this.sum += x;
if(this.v.length>this.size) {
var xold = this.v.shift();
this.sum -= xold;
}
}
get_average() {
if(this.v.length < this.minsize) return -1;
else return this.sum/this.v.length;
}
reset(x) {
this.v = [];
this.sum = 0;
}
}



function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ? nChr - 65 :
nChr > 96 && nChr < 123 ? nChr - 71 :
nChr > 47 && nChr < 58 ? nChr + 4:
nChr === 43 ? 62 :
nChr === 47 ? 63 : 0;
}

function base64DecToArr (sBase64, nBlocksSize) {
var
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);

for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}


export {
getopt, clip, normalize_angle, AvgWindow
getopt, clip, normalize_angle,
};

// TODO: remove later
export { AvgWindow } from 'util/record.js';
25 changes: 25 additions & 0 deletions util/encode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ? nChr - 65 :
nChr > 96 && nChr < 123 ? nChr - 71 :
nChr > 47 && nChr < 58 ? nChr + 4:
nChr === 43 ? 62 :
nChr === 47 ? 63 : 0;
}

function base64DecToArr (sBase64, nBlocksSize) {
var
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);

for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}
7 changes: 7 additions & 0 deletions util/functools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function zip(arrays) {
return Array.apply(null,Array(arrays[0].length)).map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}

export { zip };
Loading

0 comments on commit c0298b7

Please sign in to comment.