123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307 |
- /**
- * Numpy like n-dimensional array proccessing class
- * http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html
- *
- * @author pissang (https://github.com/pissang/)
- */
- define(function (require) {
- 'use strict';
- var kwargs = require('./kwargs');
- var ArraySlice = Array.prototype.slice;
- // Polyfill of Typed Array
- this.Int32Array = window.Int32Array || Array;
- this.Int16Array = window.Int16Array || Array;
- this.Int8Array = window.Int8Array || Array;
- this.Uint32Array = window.Uint32Array || Array;
- this.Uint16Array = window.Uint16Array || Array;
- this.Uint8Array = window.Uint8Array || Array;
- this.Float32Array = window.Float32Array || Array;
- this.Float64Array = window.Float64Array || Array;
- // Map of numpy dtype and typed array
- // http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html#arrays-dtypes
- // http://www.khronos.org/registry/typedarray/specs/latest/
- var ArrayConstructor = {
- 'int32' : this.Int32Array,
- 'int16' : this.Int16Array,
- 'int8' : this.Int8Array,
- 'uint32' : this.Uint32Array,
- 'uint16' : this.Uint16Array,
- 'uint8' : this.Uint8Array,
- // 'uint8c' is not existed in numpy
- 'uint8c' : this.Uint8ClampedArray,
- 'float32' : this.Float32Array,
- 'float64' : this.Float64Array,
- 'number' : Array
- };
- var dTypeStrideMap = {
- 'int32' : 4,
- 'int16' : 2,
- 'int8' : 1,
- 'uint32' : 4,
- 'uint16' : 2,
- 'uint8' : 1,
- 'uint8c' : 1,
- 'float32' : 4,
- 'float64' : 8,
- // Consider array stride is 1
- 'number' : 1
- };
- var E_ADD = 0;
- var E_SUB = 1;
- var E_MUL = 2;
- var E_DIV = 3;
- var E_MOD = 4;
- var E_AND = 5;
- var E_OR = 6;
- var E_XOR = 7;
- var E_EQL = 8;
- function guessDataType(arr) {
- if (typeof(arr) === 'undefined') {
- return 'number';
- }
- switch(Object.prototype.toString.call(arr)) {
- case '[object Int32Array]':
- return 'int32';
- case '[object Int16Array]':
- return 'int16';
- case '[object Int8Array]':
- return 'int8';
- case '[object Uint32Array]':
- return 'uint32';
- case '[object Uint16Array]':
- return 'uint16';
- case '[object Uint8Array]':
- return 'uint8';
- case '[object Uint8ClampedArray]':
- return 'uint8c';
- case '[object Float32Array]':
- return 'float32';
- case '[object Float64Array]':
- return 'float64';
- default:
- return 'number';
- }
- }
- /**
- * NDArray
- * @param {Array|NDArray} array
- * @param {String} dtype
- */
- var NDArray = function (array) {
- // Last argument describe the data type of ndarray
- var dtype = arguments[arguments.length-1];
- if (typeof(dtype) == 'string') {
- this._dtype = dtype;
- } else {
- // Normal array
- this._dtype = guessDataType(array);
- }
- if (array && typeof(array) !== 'string') {
- if (array instanceof NDArray) {
- array._dtype = this._dtype;
- return array;
- } else if (typeof(array.length) !== 'undefined') {
- // Init from array
- this.initFromArray(array);
- } else if(typeof(array) === 'number') {
- // Init from shape
- this.initFromShape.apply(this, arguments);
- }
- } else {
- /**
- * _array
- * Initialized with an empty array
- * Data is continuous one-dimensional array, row-major
- * A [2, 2] dim empty array is stored like
- * [0,0, 0,0]
- * TODO : Consider column majors ?
- * @type {ArrayConstructor}
- */
- this._array = new ArrayConstructor[this._dtype]();
- /**
- * _shape
- * a tuple array describe the dimension and size of each dimension
- * [10, 10] means a 10x10 array
- * @type {Array}
- */
- this._shape = [0];
- /**
- * _size
- * size of the storage array length
- * @type {Number}
- */
- this._size = 0;
- }
- };
- NDArray.prototype = {
- /**
- * Initialize from a normal js array.
- *
- * @param {Array} input
- * @return {NDArray} this
- */
- initFromArray : function (input) {
- var dim = getDimension(input);
- var cursor = 0;
- function flatten(axis, _out, _in) {
- var len = _in.length;
- for (var i = 0; i < len; i++) {
- if (axis < dim-1) {
- flatten(axis+1, _out, _in[i]);
- } else {
- _out[cursor++] = _in[i];
- }
- }
- }
- var shape = getShape(input);
- var size = getSize(shape);
- this._array = new ArrayConstructor[this._dtype](size);
- flatten(0, this._array, input);
- this._shape = shape;
- this._size = size;
- return this;
- },
- /**
- * Initialize from the given shape description.
- * @param {Array} shape
- * @return {NDArray} this
- */
- initFromShape : function (shape) {
- if (typeof(shape) == 'number') {
- shape = Array.prototype.slice.call(arguments);
- }
- if(shape) {
- var size = getSize(shape);
- if (this._dtype === 'number') {
- this._array = [];
- var data = this._array;
- for (var i = 0; i < size; i++) {
- data[i] = 0;
- }
- } else {
- this._array = new ArrayConstructor[this._dtype](size);
- }
- }
- this._shape = shape;
- this._size = getSize(shape);
- return this;
- },
- /**
- * Fill the array with the given value.
- * @param {Number} value
- * @return {NDArray} this
- */
- fill : function (value) {
- var data = this._array;
- for (var i = 0; i < data.length; i++) {
- data[i] = value;
- }
- return this;
- },
- /**
- * Get ndarray shape copy.
- * @return {Array}
- */
- shape : function () {
- // Create a copy
- return this._shape.slice();
- },
- /**
- * Get array size
- * @return {Number}
- */
- size : function () {
- return this._size;
- },
- /**
- * Get array data type.
- * 'int32'
- * 'int16'
- * 'int8'
- * 'uint32'
- * 'uint16'
- * 'uint8'
- * 'float32'
- * 'float64'
- * @return {String}
- */
- dtype : function () {
- return this._dtype;
- },
- /**
- * Get array dimension.
- * @return {number} [description]
- */
- dimension : function () {
- return this._shape.length;
- },
- /**
- * Tuple of bytes to step in each dimension when traversing an array.
- * @return {Array}
- */
- strides : function () {
- var strides = calculateDimStrides(this._shape);
- var dTypeStride = dTypeStrideMap[this._dtype];
- for (var i = 0; i < strides.length; i++) {
- strides[i] *= dTypeStride;
- }
- return strides;
- },
- /**
- * Gives a new shape to an array without changing its data.
- * @param {Array} shape
- * @return {NDArray}
- */
- reshape : function (shape) {
- if (typeof(shape) == 'number') {
- shape = Array.prototype.slice.call(arguments);
- }
- if (this._isShapeValid(shape)) {
- this._shape = shape;
- } else {
- throw new Error('Total size of new array must be unchanged');
- }
- return this;
- },
- _isShapeValid : function (shape) {
- return getSize(shape) === this._size;
- },
- /**
- * Change shape and size of array in-place.
- * @param {Array} shape
- * @return {NDArray}
- */
- resize : function (shape) {
- if (typeof(shape) == 'number') {
- shape = Array.prototype.slice.call(arguments);
- }
- var len = getSize(shape);
- if (len < this._size) {
- if (this._dtype === 'number') {
- this._array.length = len;
- }
- } else {
- if (this._dtype === 'number') {
- for (var i = this._array.length; i < len; i++) {
- // Fill the rest with zero
- this._array[i] = 0;
- }
- } else {
- // Reallocate new buffer
- var newArr = new ArrayConstructor[this._dtype](len);
- var originArr = this._array;
- // Copy data
- for (var i = 0; i < originArr.length; i++) {
- newArr[i] = originArr[i];
- }
- this._array = newArr;
- }
- }
- this._shape = shape;
- this._size = len;
- return this;
- },
- /**
- * Returns a new array with axes transposed.
- * @param {Array} [axes]
- * @param {NDArray} [out]
- * @return {NDArray}
- */
- transpose : kwargs(function (axes, out) {
- var originAxes = [];
- for (var i = 0; i < this._shape.length; i++) {
- originAxes.push(i);
- }
- if (typeof(axes) === 'undefined') {
- axes = originAxes.slice();
- }
- // Check if any axis is out of bounds
- for (var i = 0; i < axes.length; i++) {
- if (axes[i] >= this._shape.length) {
- throw new Error(axisOutofBoundsErrorMsg(axes[i]));
- }
- }
- // Has no effect on 1-D transpose
- if (axes.length <= 1) {
- return this;
- }
- var targetAxes = originAxes.slice();
- for (var i = 0; i < Math.floor(axes.length / 2); i++) {
- for (var j = axes.length-1; j >= Math.ceil(axes.length / 2) ; j--) {
- // Swap axes
- targetAxes[axes[i]] = axes[j];
- targetAxes[axes[j]] = axes[i];
- }
- }
- return this._transposelike(targetAxes, out);
- }),
- /**
- * Return a new array with axis1 and axis2 interchanged.
- * @param {Number} axis1
- * @param {Number} axis2
- * @param {NDArray} out
- * @return {NDArray}
- */
- swapaxes : kwargs(function (axis1, axis2, out) {
- return this.transpose([axis1, axis2], out);
- }),
- /**
- * Roll the specified axis backwards, until it lies in a given position.
- * @param {Number} axis
- * @param {Number} [start=0]
- * @param {NDArray} out
- * @return {NDArray}
- */
- rollaxis : kwargs(function (axis, start, out) {
- if (axis >= this._shape.length) {
- throw new Error(axisOutofBoundsErrorMsg(axis));
- }
- var axes = [];
- for (var i = 0; i < this._shape.length; i++) {
- axes.push(i);
- }
- axes.splice(axis, 1);
- axes.splice(start, 0, axis);
- return this._transposelike(axes, out);
- }, { start : 0}),
- // Base function for transpose-like operations
- _transposelike : function (axes, out) {
- var source = this._array;
- var shape = this._shape.slice();
- var strides = calculateDimStrides(this._shape);
- var dim = shape.length;
- // Swap
- var tmpStrides = [];
- var tmpShape = [];
- for (var i = 0; i < axes.length; i++) {
- var axis = axes[i];
- // swap to target axis
- tmpShape[i] = shape[axis];
- tmpStrides[i] = strides[axis];
- }
- strides = tmpStrides;
- shape = tmpShape;
- this._shape = shape;
- var transposedStrides = calculateDimStrides(this._shape);
- if (!out) {
- out = new NDArray();
- out._shape = this._shape.slice();
- out._dtype = this._dtype;
- out._size = this._size;
- }
- // FIXME in-place transpose?
- var transposedData = new ArrayConstructor[this._dtype](this._size);
- out._array = transposedData;
- // @param Item offset in current axis offset of the original array
- // @param Item offset in current axis offset of the transposed array
- function transpose(axis, offset, transposedOffset) {
- var size = shape[axis];
- // strides in orginal array
- var stride = strides[axis];
- // strides in transposed array
- var transposedStride = transposedStrides[axis];
- if (axis < dim-1) {
- for (var i = 0; i < size; i++) {
- transpose(
- axis+1,
- offset + stride * i,
- transposedOffset + transposedStride * i
- );
- }
- } else {
- for (var i = 0; i < size; i++) {
- // offset + stride * i is the index of the original array
- // transposedOffset + i is the index of the transposed array
- transposedData[transposedOffset + i]
- = source[offset + stride * i];
- }
- }
- }
- transpose(0, 0, 0);
- return out;
- },
- /**
- * Repeat elements of an array along axis
- * @param {Number} repeats
- * The number of repetitions for each element.
- * repeats is broadcasted to fit the shape of the given axis.
- * @param {Number} [axis]
- * The axis along which to repeat values.
- * By default, use the flattened input array,
- * and return a flat output array.
- * @param {NDArray} [out]
- * @return {NDArray}
- */
- repeat : kwargs(function (repeats, axis, out) {
- var shape;
- // flattened input array
- if (typeof(axis) === 'undefined') {
- shape = [this._size];
- axis = 0;
- } else {
- shape = this._shape.slice();
- }
- var originShape = shape.slice();
- shape[axis] *= repeats;
- if (!out) {
- out = new NDArray(this._dtype);
- out.initFromShape(shape);
- } else {
- if (!arrayEqual(shape, out._shape)) {
- throw new Error(broadcastErrorMsg(shape, out._shape));
- }
- }
- var data = out._array;
- var stride = calculateDimStride(originShape, axis);
- var axisSize = originShape[axis];
- var source = this._array;
- var offsetStride = stride * axisSize;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var k = 0; k < stride; k++) {
- var idx = offset + k;
- var idxRepeated = offset * repeats + k;
- for (var i = 0; i < axisSize; i++) {
- for (var j = 0; j < repeats; j++) {
- data[idxRepeated] = source[idx];
- idxRepeated += stride;
- }
- idx += stride;
- }
- }
- }
- return out;
- }),
- choose : function () {
- console.warn('TODO');
- },
- take : function () {
- console.warn('TODO');
- },
- tile : function () {
- console.warn('TODO');
- },
- /**
- * Preprocess for array calculation
- * max, min, argmax, argmin, sum, ptp, val, mean
- * Which will reduce one axis if the axis is given
- *
- * @param {Number} axis
- * @param {NDArray} out
- * @param {Function} funcWithAxis
- * @param {Function} funcFlatten
- * @return {Number|NDArray}
- */
- _withPreprocess1 : function (axis, out, funcWithAxis, funcFlatten) {
- var source = this._array;
- if (!this._size) {
- return;
- }
- if (typeof(axis)!=='undefined') {
- if (axis < 0) {
- axis = this._shape.length + axis;
- }
- if (axis >= this._shape.length || axis < 0) {
- throw new Error(axisOutofBoundsErrorMsg(axis));
- }
- var shape = this._shape.slice();
- shape.splice(axis, 1);
- if (out && !arrayEqual(shape, out._shape)) {
- throw new Error(broadcastErrorMsg(shape, out._shape));
- }
- if (!out) {
- out = new NDArray(this._dtype);
- out.initFromShape(shape);
- }
- var data = out._array;
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = stride * axisSize;
- funcWithAxis.call(
- this, data, source, offsetStride, axisSize, stride
- );
- return out;
- } else {
- return funcFlatten.call(this, source);
- }
- },
- /**
- * Preprocess for array calculation cumsum, cumprod
- * Which will keep the shape if axis is given
- * and flatten if axis is undefined
- * @param {Number} axis
- * @param {NDArray} out
- * @param {Function} funcWithAxis
- * @param {Function} funcFlatten
- * @return {NDArray}
- */
- _withPreprocess2 : function (axis, out, funcWithAxis, funcFlatten) {
- var source = this._array;
- if (!this._size) {
- return;
- }
- if (out && !arrayEqual(this._shape, out._shape)) {
- throw new Error(broadcastErrorMsg(this._shape, out._shape));
- }
- if (!out) {
- out = new NDArray(this._dtype);
- out.initFromShape(this._shape);
- }
- var data = out._array;
- if (typeof(axis)!=='undefined') {
- if (axis < 0) {
- axis = this._shape.length + axis;
- }
- if (axis >= this._shape.length || axis < 0) {
- throw new Error(axisOutofBoundsErrorMsg(axis));
- }
- if (axis >= this._shape.length) {
- throw new Error(axisOutofBoundsErrorMsg(axis));
- }
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = stride * axisSize;
- funcWithAxis.call(
- this, data, source, offsetStride, axisSize, stride
- );
- } else {
- out.reshape([this._size]);
- funcFlatten.call(this, data, source);
- }
- return out;
- },
- /**
- * Get the max value of ndarray
- * If the axis is given, the max is only calculate in this dimension
- * Example, for the given ndarray
- * [[3, 9],
- * [4, 8]]
- * >>> max(0)
- * [4, 9]
- * >>> max(1)
- * [9, 8]
- *
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- max : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = i + offset;
- var max = source[idx];
- for (var j = 0; j < axisSize; j++) {
- var d = source[idx];
- if (d > max) {
- max = d;
- }
- idx += stride;
- }
- data[cursor++] = max;
- }
- }
- }
- function withFlatten(source) {
- var max = source[0];
- for (var i = 1; i < this._size; i++) {
- if (source[i] > max) {
- max = source[i];
- }
- }
- return max;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
-
- /**
- * Return the minimum of an array or minimum along an axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- min : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = i + offset;
- var min = source[idx];
- for (var j = 0; j < axisSize; j++) {
- var d = source[idx];
- if (d < min) {
- min = d;
- }
- idx += stride;
- }
- data[cursor++] = min;
- }
- }
- }
- function withFlatten(source) {
- var min = source[0];
- for (var i = 1; i < this._size; i++) {
- if (source[i] < min) {
- min = source[i];
- }
- }
- return min;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Return indices of the maximum values along an axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- argmax : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var dataIdx = 0;
- var idx = i + offset;
- var max = source[idx];
- for (var j = 0; j < axisSize; j++) {
- var d = source[idx];
- if (d > max) {
- max = d;
- dataIdx = j;
- }
- idx += stride;
- }
- data[cursor++] = dataIdx;
- }
- }
- }
- function withFlatten(source) {
- var max = source[0];
- var idx = 0;
- for (var i = 1; i < this._size; i++) {
- if (source[i] > max) {
- idx = i;
- max = source[i];
- }
- }
- return idx;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Indices of the minimum values along an axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- argmin : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var dataIdx = 0;
- var idx = i + offset;
- var min = source[idx];
- for (var j = 0; j < axisSize; j++) {
- var d = source[idx];
- if (d < min) {
- min = d;
- dataIdx = j;
- }
- idx += stride;
- }
- data[cursor++] = dataIdx;
- }
- }
- }
- function withFlatten(source) {
- var min = source[0];
- var idx = 0;
- for (var i = 1; i < this._size; i++) {
- if (source[i] < min) {
- idx = i;
- min = source[i];
- }
- }
- return idx;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Return the sum of the array elements over the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- sum : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var sum = 0;
- var idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- sum += source[idx];
- idx += stride;
- }
- data[cursor++] = sum;
- }
- }
- }
- function withFlatten(source) {
- var sum = 0;
- for (var i = 0; i < this._size; i++) {
- sum += source[i];
- }
- return sum;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Return the product of the array elements over the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- prod : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var prod = 1;
- var idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- prod *= source[idx];
- idx += stride;
- }
- data[cursor++] = prod;
- }
- }
- }
- function withFlatten(source) {
- var prod = 1;
- for (var i = 0; i < this._size; i++) {
- prod *= source[i];
- }
- return prod;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Returns the average of the array elements along given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- mean : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var sum = 0;
- var idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- sum += source[idx];
- idx += stride;
- }
- var mean = sum / axisSize;
- data[cursor++] = mean;
- }
- }
- }
- function withFlatten(source) {
- var sum = 0;
- var len = source.length;
- for (var i = 0; i < len; i++) {
- sum += source[i];
- }
- var mean = sum / len;
- return mean;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Return the variance of the array elements over the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- 'var' : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var sum = 0;
- var idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- sum += source[idx];
- idx += stride;
- }
- var mean = sum / axisSize;
- var moments = 0;
- idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- var diff = source[idx] - mean;
- moments += diff * diff;
- idx += stride;
- }
- data[cursor++] = moments / axisSize;
- }
- }
- }
- function withFlatten(source) {
- var sum = 0;
- var len = source.length;
- for (var i = 0; i < len; i++) {
- sum += source[i];
- }
- var mean = sum / len;
- var moments = 0;
- for (var i = 0; i < len; i++) {
- var diff = source[i] - mean;
- moments += diff * diff;
- }
- return moments / len;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
-
- /**
- * Return the standard derivatione of the array elements
- * over the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- std : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var sum = 0;
- var idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- sum += source[idx];
- idx += stride;
- }
- var mean = sum / axisSize;
- var moments = 0;
- idx = i + offset;
- for (var j = 0; j < axisSize; j++) {
- var diff = source[idx] - mean;
- moments += diff * diff;
- idx += stride;
- }
- data[cursor++] = Math.sqrt(moments / axisSize);
- }
- }
- }
- function withFlatten(source) {
- var sum = 0;
- var len = source.length;
- for (var i = 0; i < len; i++) {
- sum += source[i];
- }
- var mean = sum / len;
- var moments = 0;
- for (var i = 0; i < len; i++) {
- var diff = source[i] - mean;
- moments += diff * diff;
- }
- return Math.sqrt(moments / len);
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
-
- /**
- * Peak to peak (maximum - minimum) value along a given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- ptp : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = offset + i;
- var min = source[idx];
- var max = source[idx];
- for (var j = 0; j < axisSize; j++) {
- var d = source[idx];
- if (d < min) {
- min = d;
- }
- if (d > max) {
- max = d;
- }
- idx += stride;
- }
- data[cursor++] = max - min;
- }
- }
- }
- function withFlatten(source) {
- var min = source[0];
- var max = source[0];
- for (var i = 1; i < this._size; i++) {
- if (source[i] < min) {
- min = source[i];
- }
- if (source[i] > max) {
- max = source[i];
- }
- }
- return max - min;
- }
- return function (axis, out) {
- return this._withPreprocess1(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- *
- * @param {Number} [axis=-1]
- * @param {string} [order='ascending']
- * 'ascending' | 'descending'
- * @return {NDArray}
- */
- // FIXME : V8 is quick sort, firefox and safari is merge sort
- // order : ascending or desc
- sort : kwargs(function (axis, order) {
- if (axis < 0) {
- axis = this._shape.length + axis;
- }
- var compareFunc;
- if (order === 'ascending') {
- compareFunc = function (a, b) {
- return a - b;
- };
- } else if( order === 'descending') {
- compareFunc = function (a, b) {
- return b - a;
- };
- }
- var source = this._array;
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = stride * axisSize;
- var tmp = new Array(axisSize);
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = offset + i;
- for (var j = 0; j < axisSize; j++) {
- tmp[j] = source[idx];
- idx += stride;
- }
- tmp.sort(compareFunc);
- var idx = offset + i;
- // Copy back
- for (var j = 0; j < axisSize; j++) {
- source[idx] = tmp[j];
- idx += stride;
- }
- }
- }
- return this;
- }, {axis : -1, order : 'ascending'}),
- /**
- *
- * @param {Number} [axis=-1]
- * @param {string} [order='ascending']
- * 'ascending' | 'descending'
- * @param {NDArray} [out]
- * @return {NDArray}
- */
- argsort : kwargs(function (axis, order, out) {
- if (axis < 0) {
- axis = this._shape.length + axis;
- }
- if (!this._size) {
- return;
- }
- if (out && !arrayEqual(this._shape, out._shape)) {
- throw new Error(broadcastErrorMsg(this._shape, out._shape));
- }
- if (!out) {
- out = new NDArray(this._dtype);
- out.initFromShape(this._shape);
- }
- var data = out._array;
- var compareFunc;
- if (order === 'ascending') {
- compareFunc = function (a, b) {
- return tmp[a] - tmp[b];
- };
- } else if( order === 'descending') {
- compareFunc = function (a, b) {
- return tmp[b] - tmp[a];
- };
- }
- var source = this._array;
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = stride * axisSize;
- var tmp = new Array(axisSize);
- var indexList = new Array(axisSize);
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = offset + i;
- for (var j = 0; j < axisSize; j++) {
- tmp[j] = source[idx];
- indexList[j] = j;
- idx += stride;
- }
- indexList.sort(compareFunc);
- // Copy back
- var idx = offset + i;
- for (var j = 0; j < axisSize; j++) {
- data[idx] = indexList[j];
- idx += stride;
- }
- }
- }
- return out;
- }, {axis : -1, order : 'ascending'}),
- /**
- * Return the cumulative sum of the elements along the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- cumsum : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = offset + i;
- var prevIdx = idx;
- data[idx] = source[idx];
- for (var j = 1; j < axisSize; j++) {
- prevIdx = idx;
- idx += stride;
- data[idx] = data[prevIdx] + source[idx];
- }
- }
- }
- }
- function withFlatten(data, source) {
- data[0] = source[0];
- for (var i = 1; i < data.length; i++) {
- data[i] = data[i-1] + source[i];
- }
- }
- return function (axis, out) {
- return this._withPreprocess2(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Return the cumulative product of the elements along the given axis.
- * @param {Number} [axis]
- * @param {NDArray} out
- * @return {NDArray}
- */
- cumprod : kwargs((function () {
- function withAxis(data, source, offsetStride, axisSize, stride) {
- for (var offset = 0; offset < this._size; offset+=offsetStride) {
- for (var i = 0; i < stride; i++) {
- var idx = offset + i;
- var prevIdx = idx;
- data[idx] = source[idx];
- for (var j = 1; j < axisSize; j++) {
- prevIdx = idx;
- idx += stride;
- data[idx] = data[prevIdx] * source[idx];
- }
- }
- }
- }
- function withFlatten(data, source) {
- data[0] = source[0];
- for (var i = 1; i < data.length; i++) {
- data[i] = data[i-1] * source[i];
- }
- }
- return function (axis, out) {
- return this._withPreprocess2(
- axis, out,
- withAxis, withFlatten
- );
- };
- })()),
- /**
- * Dot product of two arrays.
- *
- * @param {NDArray|Number} b
- * @param {NDArray} [out]
- * @return {NDArray|Number}
- */
- dot : function () {
- console.warn('TODO');
- },
- /**
- * Mapped to region [min, max]
- * @param {Number} mappedMin
- * @param {Number} mappedMax
- */
- map : function (mappedMin, mappedMax) {
- var input = this._array;
- var output = this._array;
- var min = input[0];
- var max = input[0];
- var l = this._size;
- for (var i = 1; i < l; i++) {
- var val = input[i];
- if (val < min) {
- min = val;
- }
- if (val > max) {
- max = val;
- }
- }
- var range = max - min;
- var mappedRange = mappedMax - mappedMin;
- for (var i = 0; i < l; i++) {
- if (range === 0) {
- output[i] = mappedMin;
- } else {
- var val = input[i];
- var percent = (val - min) / range;
- output[i] = mappedRange * percent + mappedMin;
- }
- }
- return this;
- },
- /**
- * Add
- */
- add : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_ADD, out
- );
- },
- /**
- * Substract
- */
- sub : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_SUB, out
- );
- },
- /**
- * Multiply
- */
- mul : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_MUL, out
- );
- },
- /**
- * Divide
- */
- div : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_DIV, out
- );
- },
- /**
- * mod
- */
- mod : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_MOD, out
- );
- },
- /**
- * and
- */
- and : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_AND, out
- );
- },
- /**
- * or
- */
- or : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_OR, out
- );
- },
- /**
- * xor
- */
- xor : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_XOR, out
- );
- },
- /**
- * equal
- */
- equal : function (rightOperand, out) {
- return this.binaryOperation(
- this, rightOperand, E_EQL, out
- );
- },
- binaryOperation : function (lo, ro, op, out) {
- // Broadcasting
- // http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
- var shape = [];
- var isLoScalar = typeof(lo) === 'number';
- var isRoScalar = typeof(ro) === 'number';
- if (isLoScalar) {
- shape = ro._shape.slice();
- } else if (isRoScalar) {
- shape = lo._shape.slice();
- } else {
- // Starts with the trailing dimensions
- var cl = lo._shape.length-1;
- var cr = ro._shape.length-1;
- var loBroadCasted = lo;
- var roBroadCasted = ro;
- while (cl >= 0 && cr >= 0) {
- if (lo._shape[cl] == 1) {
- shape.unshift(ro._shape[cr]);
- loBroadCasted = lo.repeat(ro._shape[cr], cl);
- } else if(ro._shape[cr] == 1) {
- shape.unshift(lo._shape[cl]);
- roBroadCasted = ro.repeat(lo._shape[cl], cr);
- } else if(ro._shape[cr] == lo._shape[cl]) {
- shape.unshift(lo._shape[cl]);
- } else {
- throw new Error(broadcastErrorMsg(lo._shape, ro._shape));
- }
- cl --;
- cr --;
- }
- for (var i = cl; i >= 0; i--) {
- shape.unshift(lo._shape[i]);
- }
- for (var i = cr; i >= 0; i--) {
- shape.unshift(ro._shape[i]);
- }
- lo = loBroadCasted;
- ro = roBroadCasted;
- }
- if (!out) {
- out = new NDArray(this._dtype);
- out.initFromShape(shape);
- } else {
- if (! arrayEqual(shape, out._shape)) {
- throw new Error(broadcastErrorMsg(shape, out._shape));
- }
- }
- var outData = out._array;
-
- var diffAxis;
- var isLoLarger;
- var loData;
- var roData;
- if (isLoScalar) {
- diffAxis = ro._shape.length-1;
- isLoLarger = false;
- loData = lo;
- roData = ro._array;
- } else if(isRoScalar) {
- diffAxis = lo._shape.length-1;
- isLoLarger = true;
- roData = ro;
- loData = lo._array;
- } else {
- diffAxis = Math.abs(lo._shape.length - ro._shape.length);
- isLoLarger = lo._shape.length >= ro._shape.length;
- loData = lo._array;
- roData = ro._array;
- }
- var stride = calculateDimStride(shape, diffAxis);
- var axisSize = shape[diffAxis];
- var offsetStride = stride * axisSize;
- var offsetRepeats = out._size / offsetStride;
- var _a, _b, res;
- var idx = 0;
- if (isLoLarger) {
- if(isRoScalar) {
- for (var c = 0; c < offsetRepeats; c++) {
- for (var i = 0; i < offsetStride; i++) {
- _a = loData[idx]; _b = roData;
- switch (op) {
- case E_ADD: res = _a + _b; break;
- case E_SUB: res = _a - _b; break;
- case E_MUL: res = _a * _b; break;
- case E_DIV: res = _a / _b; break;
- case E_MOD: res = _a % _b; break;
- case E_AND: res = _a & _b; break;
- case E_OR: res = _a | _b; break;
- case E_XOR: res = _a ^ _b; break;
- case E_EQL: res = _a == _b; break;
- default: throw new Error('Unkown operation ' + op);
- }
- outData[idx] = res;
- idx ++;
- }
- }
- } else {
- for (var c = 0; c < offsetRepeats; c++) {
- for (var i = 0; i < offsetStride; i++) {
- _a = loData[idx]; _b = roData[i];
- switch (op) {
- case E_ADD: res = _a + _b; break;
- case E_SUB: res = _a - _b; break;
- case E_MUL: res = _a * _b; break;
- case E_DIV: res = _a / _b; break;
- case E_MOD: res = _a % _b; break;
- case E_AND: res = _a & _b; break;
- case E_OR: res = _a | _b; break;
- case E_XOR: res = _a ^ _b; break;
- case E_EQL: res = _a == _b; break;
- default: throw new Error('Unkown operation ' + op);
- }
- outData[idx] = res;
- idx ++;
- }
- }
- }
- } else {
- if (isLoScalar) {
- for (var c = 0; c < offsetRepeats; c++) {
- for (var i = 0; i < offsetStride; i++) {
- _a = loData; _b = roData[idx];
- switch (op) {
- case E_ADD: res = _a + _b; break;
- case E_SUB: res = _a - _b; break;
- case E_MUL: res = _a * _b; break;
- case E_DIV: res = _a / _b; break;
- case E_MOD: res = _a % _b; break;
- case E_AND: res = _a & _b; break;
- case E_OR: res = _a | _b; break;
- case E_XOR: res = _a ^ _b; break;
- case E_EQL: res = _a == _b; break;
- default: throw new Error('Unkown operation ' + op);
- }
- outData[idx] = res;
- idx ++;
- }
- }
- } else {
- for (var c = 0; c < offsetRepeats; c++) {
- for (var i = 0; i < offsetStride; i++) {
- _a = loData[idx]; _b = roData[i];
- switch (op) {
- case E_ADD: res = _a + _b; break;
- case E_SUB: res = _a - _b; break;
- case E_MUL: res = _a * _b; break;
- case E_DIV: res = _a / _b; break;
- case E_MOD: res = _a % _b; break;
- case E_AND: res = _a & _b; break;
- case E_OR: res = _a | _b; break;
- case E_XOR: res = _a ^ _b; break;
- case E_EQL: res = _a == _b; break;
- default: throw new Error('Unkown operation ' + op);
- }
- outData[idx] = res;
- idx ++;
- }
- }
- }
- }
- return out;
- },
- /**
- * negtive
- */
- neg : function () {
- var data = this._array;
- for (var i = 0; i < this._size; i++) {
- data[i] = -data[i];
- }
- return this;
- },
- /**
- * @return {NDArray} this
- */
- sin : function () {
- return this._mathAdapter(Math.sin);
- },
- /**
- * @return {NDArray} this
- */
- cos : function () {
- return this._mathAdapter(Math.cos);
- },
- /**
- * @return {NDArray} this
- */
- tan : function () {
- return this._mathAdapter(Math.tan);
- },
- /**
- * @return {NDArray} this
- */
- abs : function () {
- return this._mathAdapter(Math.abs);
- },
- /**
- * @return {NDArray} this
- */
- log : function () {
- return this._mathAdapter(Math.log);
- },
- /**
- * @return {NDArray} this
- */
- sqrt : function () {
- return this._mathAdapter(Math.sqrt);
- },
- /**
- * @return {NDArray} this
- */
- ceil : function () {
- return this._mathAdapter(Math.ceil);
- },
- /**
- * @return {NDArray} this
- */
- floor : function () {
- return this._mathAdapter(Math.floor);
- },
-
- /**
- * @return {NDArray} this
- */
- pow : function (exp) {
- var data = this._array;
- for (var i = 0; i < this._size; i++) {
- data[i] = Math.pow(data[i], exp);
- }
- return this;
- },
- _mathAdapter : function (mathFunc) {
- var data = this._array;
- for (var i = 0; i < this._size; i++) {
- data[i] = mathFunc(data[i]);
- }
- return this;
- },
- /**
- * @param {Number} decimals
- * @return {NDArray} this
- */
- round : function (decimals) {
- decimals = Math.floor(decimals || 0);
- var offset = Math.pow(10, decimals);
- var data = this._array;
- if (decimals === 0) {
- for (var i = 0; i < this._size; i++) {
- data[i] = Math.round(data[i]);
- }
- } else {
- for (var i = 0; i < this._size; i++) {
- data[i] = Math.round(data[i] * offset) / offset;
- }
- }
- return this;
- },
- /**
- * @param {Number} min
- * @param {Number} max
- * Clip to [min, max]
- */
- clip : function (min, max) {
- // TODO : Support array_like param
- var data = this._array;
- for (var i = 0; i < this._size; i++) {
- data[i] = Math.max(Math.min(data[i], max), min);
- }
- return this;
- },
- /**
- * Indexing array, support range indexing
- * @param {string} index
- * Index syntax can be an integer 1, 2, 3
- * Or more complex range indexing
- * '1:2'
- * '1:2, 1:2'
- * '1:2, :'
- * More about the indexing syntax can check the doc of numpy ndarray
- * @param {NDArray} [out]
- * @return {NDArray} New created sub array, or out if given
- */
- get : function (index, out) {
- if (typeof(index) == 'number') {
- index = index.toString();
- }
- var strides = calculateDimStrides(this._shape);
- var res = this._parseRanges(index);
- var ranges = res[0];
- var shape = res[1];
- if (ranges.length > this._shape.length) {
- throw new Error('Too many indices');
- }
- // Get data
- var len = ranges.length;
- var data;
- if (shape.length) {
- out = new NDArray(this._dtype);
- out.initFromShape(shape);
- data = out._array;
- } else {
- data = [];
- }
- var source = this._array;
- var cursor = 0;
- function getPiece(axis, offset) {
- var range = ranges[axis];
- var stride = strides[axis];
- if (axis < len-1) {
- if (range[2] > 0) {
- for (var i = range[0]; i < range[1]; i += range[2]) {
- getPiece(axis+1, offset + stride * i);
- }
- } else {
- for (var i = range[0]; i > range[1]; i += range[2]) {
- getPiece(axis+1, offset + stride * i);
- }
- }
- } else {
- if (range[2] > 0) {
- for (var i = range[0]; i < range[1]; i += range[2]) {
- for (var j = 0; j < stride; j++) {
- data[cursor++] = source[i*stride + j + offset];
- }
- }
- } else {
- for (var i = range[0]; i > range[1]; i += range[2]) {
- for (var j = 0; j < stride; j++) {
- data[cursor++] = source[i*stride + j + offset];
- }
- }
- }
- }
- }
- getPiece(0, 0);
- if (shape.length) {
- // Return scalar
- return out;
- } else {
- return data[0];
- }
-
- },
- /**
- *
- * @param {string} index
- * index syntax can be an integer 1, 2, 3
- * Or more complex range indexing
- * '1:2'
- * '1:2, 1:2'
- * '1:2, :'
- * More about the indexing syntax can check the doc of numpy ndarray
- * @param {NDArray} ndarray Ndarray data source
- * @return {NDArray} this
- */
- set : function (index, narray) {
- if (typeof(index) == 'number') {
- index = index.toString();
- }
- var strides = calculateDimStrides(this._shape);
- var res = this._parseRanges(index);
- var ranges = res[0];
- var shape = res[1];
- if (ranges.length > this._shape.length) {
- throw new Error('Too many indices');
- }
- var isScalar = typeof(narray) == 'number';
- var len = ranges.length;
- var data = this._array;
- if (isScalar) {
- // Set with a single scalar
- var source = narray;
- } else {
- if (!arrayEqual(shape, narray.shape())) {
- throw new Error(broadcastErrorMsg(shape, narray.shape()));
- }
- var source = narray._array;
- }
- var cursor = 0;
- var setPiece = function (axis, offset) {
- var range = ranges[axis];
- var stride = strides[axis];
- if (axis < len-1) {
- if (range[2] > 0) {
- for (var i = range[0]; i < range[1]; i += range[2]) {
- setPiece(axis+1, offset + stride * i);
- }
- } else {
- for (var i = range[0]; i > range[1]; i += range[2]) {
- setPiece(axis+1, offset + stride * i);
- }
- }
- } else {
- if (range[2] > 0) {
- for (var i = range[0]; i < range[1]; i += range[2]) {
- for (var j = 0; j < stride; j++) {
- if (isScalar) {
- data[i*stride + j + offset] = source;
- } else {
- data[i*stride + j + offset] = source[cursor++];
- }
- }
- }
- } else {
- for (var i = range[0]; i > range[1]; i += range[2]) {
- for (var j = 0; j < stride; j++) {
- if (isScalar) {
- data[i*stride + j + offset] = source;
- } else {
- data[i*stride + j + offset] = source[cursor++];
- }
- }
- }
- }
- }
- };
- setPiece(0, 0);
- return this;
- },
- /**
- * Insert values along the given axis before the given indices.
- * @param {Number|Array} obj
- * Object that defines the index or indices before
- * which values is inserted.
- * @param {Number|Array|NDArray} values
- * Values to insert
- * @param {Number} [axis]
- * @return {NDArray} this
- */
- insert : kwargs(function (obj, values, axis) {
- var data = this._array;
- var isObjScalar = false;
- if (typeof(obj) === 'number') {
- obj = [obj];
- isObjScalar = true;
- }
- if (typeof(values) === 'number') {
- values = new NDArray([values]);
- } else if (values instanceof Array) {
- values = new NDArray(values);
- }
- if (typeof(axis) === 'undefined') {
- this._shape = [this._size];
- axis = 0;
- }
- // Checking if indices is valid
- var prev = obj[0];
- var axisSize = this._shape[axis];
- for (var i = 0; i < obj.length; i++) {
- if (obj[i] < 0) {
- obj[i] = axisSize + obj[i];
- }
- if (obj[i] > axisSize) {
- throw new Error(indexOutofBoundsErrorMsg(obj[i]));
- }
- if (obj[i] < prev) {
- throw new Error('Index must be in ascending order');
- }
- prev = obj[i];
- }
- // Broadcasting
- var targetShape = this._shape.slice();
- if (isObjScalar) {
- targetShape.splice(axis, 1);
- } else {
- targetShape[axis] = obj.length;
- }
- var sourceShape = values._shape;
- var cs = sourceShape.length - 1;
- var ct = targetShape.length - 1;
- var valueBroadcasted = values;
- while (cs >= 0 && ct >= 0) {
- if (sourceShape[cs] === 1) {
- valueBroadcasted = values.repeat(targetShape[ct], cs);
- } else if(sourceShape[cs] !== targetShape[ct]) {
- throw new Error(broadcastErrorMsg(sourceShape, targetShape));
- }
- cs --;
- ct --;
- }
- values = valueBroadcasted;
- // Calculate indices to insert
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = axisSize * stride;
- var offsetRepeats = this._size / offsetStride;
- var objLen = obj.length;
- var indices = new Uint32Array(offsetRepeats * objLen);
- var cursor = 0;
- for (var offset = 0; offset < this._size; offset += offsetStride) {
- for (var i = 0; i < objLen; i++) {
- var objIdx = obj[i];
- indices[cursor++] = offset + objIdx * stride;
- }
- }
- var resShape = this._shape.slice();
- resShape[axis] += obj.length;
- var resSize = getSize(resShape);
- if (this._array.length < resSize) {
- var data = new ArrayConstructor[this._dtype](resSize);
- } else {
- var data = this._array;
- }
- var source = this._array;
- var valuesArr = values._array;
- var idxCursor = indices.length - 1;
- var end = this._size;
- var start = indices[idxCursor];
- var dataCursor = resSize - 1;
- var valueCursor = values._size - 1;
- while (idxCursor >= 0) {
- // Copy source data;
- for (var i = end - 1; i >= start; i--) {
- data[dataCursor--] = source[i];
- }
- end = start;
- start = indices[--idxCursor];
- // Copy inserted data;
- for (var i = 0; i < stride; i++) {
- if (valueCursor < 0) {
- valueCursor = values._size - 1;
- }
- data[dataCursor--] = valuesArr[valueCursor--];
- }
- }
- // Copy the rest
- for (var i = end - 1; i >= 0; i--) {
- data[dataCursor--] = source[i];
- }
- this._array = data;
- this._shape = resShape;
- this._size = resSize;
- return this;
- }),
- append : function () {
- console.warn('TODO');
- },
- /**
- * Delete values along the axis
- * @param {Array|Number} obj
- * @param {Number} [axis]
- * @return {NDArray} this
- */
- 'delete' : kwargs(function (obj, axis) {
- var data = this._array;
- if (typeof(obj) === 'number') {
- obj = [obj];
- }
- var size = this._size;
- if (typeof(axis) === 'undefined') {
- this._shape = [size];
- axis = 0;
- }
- var stride = calculateDimStride(this._shape, axis);
- var axisSize = this._shape[axis];
- var offsetStride = stride * axisSize;
- var cursor = 0;
- for (var offset = 0; offset < size; offset += offsetStride) {
- var start = 0;
- var end = obj[0];
- var objCursor = 0;
- while(objCursor < obj.length) {
- if (end < 0) {
- end = end + axisSize;
- }
- if (end > axisSize) {
- throw new Error(indexOutofBoundsErrorMsg(end));
- }
- if (end < start) {
- throw new Error('Index must be in ascending order');
- }
- for (var i = start; i < end; i++) {
- for (var j = 0; j < stride; j++) {
- data[cursor++] = data[i * stride + j + offset];
- }
- }
- start = end + 1;
- end = obj[++objCursor];
- }
- // Copy the rest
- for (var i = start; i < axisSize; i++) {
- for (var j = 0; j < stride; j++) {
- data[cursor++] = data[i * stride + j + offset];
- }
- }
- }
- this._shape[axis] -= obj.length;
- this._size = getSize(this._shape);
- return this;
- }),
- _parseRanges : function (index) {
- var rangesStr = index.split(/\s*,\s*/);
-
- // Parse range of each axis
- var ranges = [];
- var shape = [];
- var j = 0;
- for (var i = 0; i < rangesStr.length; i++) {
- if (rangesStr[i] === '...') {
- var end = this._shape.length - (rangesStr.length - i);
- while (j <= end) {
- ranges.push([0, this._shape[j], 1]);
- shape.push(this._shape[j]);
- j++;
- }
- } else {
- var range = parseRange(rangesStr[i], this._shape[j]);
- ranges.push(range);
- if(rangesStr[i].indexOf(':') >= 0) {
- var size = Math.floor((range[1] - range[0]) / range[2]);
- size = size < 0 ? 0 : size;
- // Get a range not a item
- shape.push(size);
- }
- j++;
- }
- }
- // Copy the lower dimension size
- for (; j < this._shape.length; j++) {
- shape.push(this._shape[j]);
- }
- return [ranges, shape];
- },
- /**
- * Export normal js array
- * @return {Array}
- */
- toArray : function () {
- var data = this._array;
- var cursor = 0;
- var shape = this._shape;
- var dim = shape.length;
- function create(axis, out) {
- var len = shape[axis];
- for (var i = 0; i < len; i++) {
- if (axis < dim-1) {
- create(axis+1, out[i] = []);
- } else {
- out[i] = data[cursor++];
- }
- }
- }
- var output = [];
- create(0, output);
- return output;
- },
- /**
- * Create a copy of self
- * @return {NDArray}
- */
- copy : function () {
- var numArr = new NDArray();
- numArr._array = ArraySlice.call(this._array);
- numArr._shape = this._shape.slice();
- numArr._dtype = this._dtype;
- numArr._size = this._size;
- return numArr;
- },
- constructor : NDArray
- };
- /**
- *
- * @param {Number} [min=0]
- * @param {Number} max
- * @param {Number} [step=1]
- * @param {string} [dtype]
- * @return {NDArray}
- */
- NDArray.range = kwargs(function (min, max, step, dtype) {
- var args = ArraySlice.call(arguments);
- // Last argument describe the data type of ndarray
- var lastArg = args[args.length-1];
- if (typeof(lastArg) == 'string') {
- var dtype = lastArg;
- args.pop();
- }
- if (args.length === 1) {
- max = args[0];
- step = 1;
- min = 0;
- } else if(args.length == 2) {
- step = 1;
- }
- dtype = dtype || 'number';
- var array = new ArrayConstructor[dtype](Math.ceil((max - min)/step));
- var cursor = 0;
- for (var i = min; i < max; i+=step) {
- array[cursor++] = i;
- }
- var ndarray = new NDArray();
- ndarray._array = array;
- ndarray._shape = [array.length];
- ndarray._dtype = dtype;
- ndarray._size = array.length;
- return ndarray;
- });
- /**
- *
- * @param {Array} shape
- * @param {String} [dtype]
- * @return {NDArray}
- */
- NDArray.zeros = kwargs(function (shape, dtype) {
- var ret = new NDArray(dtype);
- ret.initFromShape(shape);
- return ret;
- });
- /**
- * Python like array indexing
- * http://www.python.org/dev/peps/pep-0204/
- *
- * @param {string} index
- * index can be a simple integer 1,2,3,
- * or a range 2:10, 2:10:1
- * example :
- * 2:10 => [2, 10, 1],
- * 10:2:-2 => [10, 2, -2],
- * : => [0, dimSize, 1],
- * ::-1 => [dimSize-1, -1, -1],
- * @param {number} dimSize
- * @return {Array} a tuple array [startOffset, endOffset, sliceStep]
- */
- function parseRange(index, dimSize) {
- if (index.indexOf(':') >= 0) {
- // Range indexing;
- var res = index.split(/\s*:\s*/);
- var step = parseInt(res[2] || 1, 10);
- var start, end;
- if (step === 0) {
- throw new Error('Slice step cannot be zero');
- }
- else if (step > 0) {
- start = parseInt(res[0] || 0, 10);
- end = parseInt(res[1] || dimSize, 10);
- }
- else {
- start = parseInt(res[0] || dimSize - 1, 10);
- end = parseInt(res[1] || -1, 10);
- }
- // Negtive offset
- if (start < 0) {
- start = dimSize + start;
- }
- // Negtive offset
- if (end < 0 && res[1]) {
- end = dimSize + end;
- }
- if (step > 0) {
- // Clamp to [0-dimSize]
- start = Math.max(Math.min(dimSize, start), 0);
- // Clamp to [0-dimSize]
- end = Math.max(Math.min(dimSize, end), 0);
- } else {
- // Clamp to [0-dimSize)
- start = Math.max(Math.min(dimSize-1, start), -1);
- // Clamp to [0-dimSize)
- end = Math.max(Math.min(dimSize-1, end), -1);
- }
- return [start, end, step];
- } else {
- var start = parseInt(index, 10);
- // Negtive offset
- if (start < 0) {
- start = dimSize + start;
- }
- if (start < 0 || start > dimSize) {
- throw new Error(indexOutofBoundsErrorMsg(index));
- }
- // Clamp to [0-dimSize)
- start = Math.max(Math.min(dimSize-1, start), 0);
- return [start, start+1, 1];
- }
- }
- function getSize(shape) {
- var size = shape[0];
- for (var i = 1; i < shape.length; i++) {
- size *= shape[i];
- }
- return size;
- }
- function getDimension(array) {
- var dim = 1;
- var el = array[0];
- while (el instanceof Array) {
- el = el[0];
- dim ++;
- }
- return dim;
- }
- function getShape(array) {
- var shape = [array.length];
- var el = array[0];
- while (el instanceof Array) {
- shape.push(el.length);
- el = el[0];
- }
- return shape;
- }
- function calculateDimStride(shape, axis) {
- if (axis == shape.length-1) {
- return 1;
- }
- var stride = shape[axis+1];
- for (var i = axis+2; i < shape.length; i++) {
- stride *= shape[i];
- }
- return stride;
- }
- function calculateDimStrides(shape) {
- // Calculate stride of each axis
- var strides = [];
- var tmp = 1;
- var len = getSize(shape);
- for (var i = 0; i < shape.length; i++) {
- tmp *= shape[i];
- strides.push(len / tmp);
- }
- return strides;
- }
- function arrayEqual(arr1, arr2) {
- if (arr1.length !== arr2.length) {
- return false;
- }
- for (var i = 0; i <arr1.length; i++) {
- if (arr1[i] !== arr2[i]) {
- return false;
- }
- }
- return true;
- }
- function broadcastErrorMsg(shape1, shape2) {
- return 'Shape ('
- + shape1.toString() + ') (' + shape2.toString()
- +') could not be broadcast together';
- }
- function axisOutofBoundsErrorMsg(axis) {
- return 'Axis ' + axis + ' out of bounds';
- }
- function indexOutofBoundsErrorMsg(idx) {
- return 'Index ' + idx + ' out of bounds';
- }
- return NDArray;
- });
|