4f40063bc76d12c8325a638e1693bb67362ba8bc.svn-base 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307
  1. /**
  2. * Numpy like n-dimensional array proccessing class
  3. * http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html
  4. *
  5. * @author pissang (https://github.com/pissang/)
  6. */
  7. define(function (require) {
  8. 'use strict';
  9. var kwargs = require('./kwargs');
  10. var ArraySlice = Array.prototype.slice;
  11. // Polyfill of Typed Array
  12. this.Int32Array = window.Int32Array || Array;
  13. this.Int16Array = window.Int16Array || Array;
  14. this.Int8Array = window.Int8Array || Array;
  15. this.Uint32Array = window.Uint32Array || Array;
  16. this.Uint16Array = window.Uint16Array || Array;
  17. this.Uint8Array = window.Uint8Array || Array;
  18. this.Float32Array = window.Float32Array || Array;
  19. this.Float64Array = window.Float64Array || Array;
  20. // Map of numpy dtype and typed array
  21. // http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html#arrays-dtypes
  22. // http://www.khronos.org/registry/typedarray/specs/latest/
  23. var ArrayConstructor = {
  24. 'int32' : this.Int32Array,
  25. 'int16' : this.Int16Array,
  26. 'int8' : this.Int8Array,
  27. 'uint32' : this.Uint32Array,
  28. 'uint16' : this.Uint16Array,
  29. 'uint8' : this.Uint8Array,
  30. // 'uint8c' is not existed in numpy
  31. 'uint8c' : this.Uint8ClampedArray,
  32. 'float32' : this.Float32Array,
  33. 'float64' : this.Float64Array,
  34. 'number' : Array
  35. };
  36. var dTypeStrideMap = {
  37. 'int32' : 4,
  38. 'int16' : 2,
  39. 'int8' : 1,
  40. 'uint32' : 4,
  41. 'uint16' : 2,
  42. 'uint8' : 1,
  43. 'uint8c' : 1,
  44. 'float32' : 4,
  45. 'float64' : 8,
  46. // Consider array stride is 1
  47. 'number' : 1
  48. };
  49. var E_ADD = 0;
  50. var E_SUB = 1;
  51. var E_MUL = 2;
  52. var E_DIV = 3;
  53. var E_MOD = 4;
  54. var E_AND = 5;
  55. var E_OR = 6;
  56. var E_XOR = 7;
  57. var E_EQL = 8;
  58. function guessDataType(arr) {
  59. if (typeof(arr) === 'undefined') {
  60. return 'number';
  61. }
  62. switch(Object.prototype.toString.call(arr)) {
  63. case '[object Int32Array]':
  64. return 'int32';
  65. case '[object Int16Array]':
  66. return 'int16';
  67. case '[object Int8Array]':
  68. return 'int8';
  69. case '[object Uint32Array]':
  70. return 'uint32';
  71. case '[object Uint16Array]':
  72. return 'uint16';
  73. case '[object Uint8Array]':
  74. return 'uint8';
  75. case '[object Uint8ClampedArray]':
  76. return 'uint8c';
  77. case '[object Float32Array]':
  78. return 'float32';
  79. case '[object Float64Array]':
  80. return 'float64';
  81. default:
  82. return 'number';
  83. }
  84. }
  85. /**
  86. * NDArray
  87. * @param {Array|NDArray} array
  88. * @param {String} dtype
  89. */
  90. var NDArray = function (array) {
  91. // Last argument describe the data type of ndarray
  92. var dtype = arguments[arguments.length-1];
  93. if (typeof(dtype) == 'string') {
  94. this._dtype = dtype;
  95. } else {
  96. // Normal array
  97. this._dtype = guessDataType(array);
  98. }
  99. if (array && typeof(array) !== 'string') {
  100. if (array instanceof NDArray) {
  101. array._dtype = this._dtype;
  102. return array;
  103. } else if (typeof(array.length) !== 'undefined') {
  104. // Init from array
  105. this.initFromArray(array);
  106. } else if(typeof(array) === 'number') {
  107. // Init from shape
  108. this.initFromShape.apply(this, arguments);
  109. }
  110. } else {
  111. /**
  112. * _array
  113. * Initialized with an empty array
  114. * Data is continuous one-dimensional array, row-major
  115. * A [2, 2] dim empty array is stored like
  116. * [0,0, 0,0]
  117. * TODO : Consider column majors ?
  118. * @type {ArrayConstructor}
  119. */
  120. this._array = new ArrayConstructor[this._dtype]();
  121. /**
  122. * _shape
  123. * a tuple array describe the dimension and size of each dimension
  124. * [10, 10] means a 10x10 array
  125. * @type {Array}
  126. */
  127. this._shape = [0];
  128. /**
  129. * _size
  130. * size of the storage array length
  131. * @type {Number}
  132. */
  133. this._size = 0;
  134. }
  135. };
  136. NDArray.prototype = {
  137. /**
  138. * Initialize from a normal js array.
  139. *
  140. * @param {Array} input
  141. * @return {NDArray} this
  142. */
  143. initFromArray : function (input) {
  144. var dim = getDimension(input);
  145. var cursor = 0;
  146. function flatten(axis, _out, _in) {
  147. var len = _in.length;
  148. for (var i = 0; i < len; i++) {
  149. if (axis < dim-1) {
  150. flatten(axis+1, _out, _in[i]);
  151. } else {
  152. _out[cursor++] = _in[i];
  153. }
  154. }
  155. }
  156. var shape = getShape(input);
  157. var size = getSize(shape);
  158. this._array = new ArrayConstructor[this._dtype](size);
  159. flatten(0, this._array, input);
  160. this._shape = shape;
  161. this._size = size;
  162. return this;
  163. },
  164. /**
  165. * Initialize from the given shape description.
  166. * @param {Array} shape
  167. * @return {NDArray} this
  168. */
  169. initFromShape : function (shape) {
  170. if (typeof(shape) == 'number') {
  171. shape = Array.prototype.slice.call(arguments);
  172. }
  173. if(shape) {
  174. var size = getSize(shape);
  175. if (this._dtype === 'number') {
  176. this._array = [];
  177. var data = this._array;
  178. for (var i = 0; i < size; i++) {
  179. data[i] = 0;
  180. }
  181. } else {
  182. this._array = new ArrayConstructor[this._dtype](size);
  183. }
  184. }
  185. this._shape = shape;
  186. this._size = getSize(shape);
  187. return this;
  188. },
  189. /**
  190. * Fill the array with the given value.
  191. * @param {Number} value
  192. * @return {NDArray} this
  193. */
  194. fill : function (value) {
  195. var data = this._array;
  196. for (var i = 0; i < data.length; i++) {
  197. data[i] = value;
  198. }
  199. return this;
  200. },
  201. /**
  202. * Get ndarray shape copy.
  203. * @return {Array}
  204. */
  205. shape : function () {
  206. // Create a copy
  207. return this._shape.slice();
  208. },
  209. /**
  210. * Get array size
  211. * @return {Number}
  212. */
  213. size : function () {
  214. return this._size;
  215. },
  216. /**
  217. * Get array data type.
  218. * 'int32'
  219. * 'int16'
  220. * 'int8'
  221. * 'uint32'
  222. * 'uint16'
  223. * 'uint8'
  224. * 'float32'
  225. * 'float64'
  226. * @return {String}
  227. */
  228. dtype : function () {
  229. return this._dtype;
  230. },
  231. /**
  232. * Get array dimension.
  233. * @return {number} [description]
  234. */
  235. dimension : function () {
  236. return this._shape.length;
  237. },
  238. /**
  239. * Tuple of bytes to step in each dimension when traversing an array.
  240. * @return {Array}
  241. */
  242. strides : function () {
  243. var strides = calculateDimStrides(this._shape);
  244. var dTypeStride = dTypeStrideMap[this._dtype];
  245. for (var i = 0; i < strides.length; i++) {
  246. strides[i] *= dTypeStride;
  247. }
  248. return strides;
  249. },
  250. /**
  251. * Gives a new shape to an array without changing its data.
  252. * @param {Array} shape
  253. * @return {NDArray}
  254. */
  255. reshape : function (shape) {
  256. if (typeof(shape) == 'number') {
  257. shape = Array.prototype.slice.call(arguments);
  258. }
  259. if (this._isShapeValid(shape)) {
  260. this._shape = shape;
  261. } else {
  262. throw new Error('Total size of new array must be unchanged');
  263. }
  264. return this;
  265. },
  266. _isShapeValid : function (shape) {
  267. return getSize(shape) === this._size;
  268. },
  269. /**
  270. * Change shape and size of array in-place.
  271. * @param {Array} shape
  272. * @return {NDArray}
  273. */
  274. resize : function (shape) {
  275. if (typeof(shape) == 'number') {
  276. shape = Array.prototype.slice.call(arguments);
  277. }
  278. var len = getSize(shape);
  279. if (len < this._size) {
  280. if (this._dtype === 'number') {
  281. this._array.length = len;
  282. }
  283. } else {
  284. if (this._dtype === 'number') {
  285. for (var i = this._array.length; i < len; i++) {
  286. // Fill the rest with zero
  287. this._array[i] = 0;
  288. }
  289. } else {
  290. // Reallocate new buffer
  291. var newArr = new ArrayConstructor[this._dtype](len);
  292. var originArr = this._array;
  293. // Copy data
  294. for (var i = 0; i < originArr.length; i++) {
  295. newArr[i] = originArr[i];
  296. }
  297. this._array = newArr;
  298. }
  299. }
  300. this._shape = shape;
  301. this._size = len;
  302. return this;
  303. },
  304. /**
  305. * Returns a new array with axes transposed.
  306. * @param {Array} [axes]
  307. * @param {NDArray} [out]
  308. * @return {NDArray}
  309. */
  310. transpose : kwargs(function (axes, out) {
  311. var originAxes = [];
  312. for (var i = 0; i < this._shape.length; i++) {
  313. originAxes.push(i);
  314. }
  315. if (typeof(axes) === 'undefined') {
  316. axes = originAxes.slice();
  317. }
  318. // Check if any axis is out of bounds
  319. for (var i = 0; i < axes.length; i++) {
  320. if (axes[i] >= this._shape.length) {
  321. throw new Error(axisOutofBoundsErrorMsg(axes[i]));
  322. }
  323. }
  324. // Has no effect on 1-D transpose
  325. if (axes.length <= 1) {
  326. return this;
  327. }
  328. var targetAxes = originAxes.slice();
  329. for (var i = 0; i < Math.floor(axes.length / 2); i++) {
  330. for (var j = axes.length-1; j >= Math.ceil(axes.length / 2) ; j--) {
  331. // Swap axes
  332. targetAxes[axes[i]] = axes[j];
  333. targetAxes[axes[j]] = axes[i];
  334. }
  335. }
  336. return this._transposelike(targetAxes, out);
  337. }),
  338. /**
  339. * Return a new array with axis1 and axis2 interchanged.
  340. * @param {Number} axis1
  341. * @param {Number} axis2
  342. * @param {NDArray} out
  343. * @return {NDArray}
  344. */
  345. swapaxes : kwargs(function (axis1, axis2, out) {
  346. return this.transpose([axis1, axis2], out);
  347. }),
  348. /**
  349. * Roll the specified axis backwards, until it lies in a given position.
  350. * @param {Number} axis
  351. * @param {Number} [start=0]
  352. * @param {NDArray} out
  353. * @return {NDArray}
  354. */
  355. rollaxis : kwargs(function (axis, start, out) {
  356. if (axis >= this._shape.length) {
  357. throw new Error(axisOutofBoundsErrorMsg(axis));
  358. }
  359. var axes = [];
  360. for (var i = 0; i < this._shape.length; i++) {
  361. axes.push(i);
  362. }
  363. axes.splice(axis, 1);
  364. axes.splice(start, 0, axis);
  365. return this._transposelike(axes, out);
  366. }, { start : 0}),
  367. // Base function for transpose-like operations
  368. _transposelike : function (axes, out) {
  369. var source = this._array;
  370. var shape = this._shape.slice();
  371. var strides = calculateDimStrides(this._shape);
  372. var dim = shape.length;
  373. // Swap
  374. var tmpStrides = [];
  375. var tmpShape = [];
  376. for (var i = 0; i < axes.length; i++) {
  377. var axis = axes[i];
  378. // swap to target axis
  379. tmpShape[i] = shape[axis];
  380. tmpStrides[i] = strides[axis];
  381. }
  382. strides = tmpStrides;
  383. shape = tmpShape;
  384. this._shape = shape;
  385. var transposedStrides = calculateDimStrides(this._shape);
  386. if (!out) {
  387. out = new NDArray();
  388. out._shape = this._shape.slice();
  389. out._dtype = this._dtype;
  390. out._size = this._size;
  391. }
  392. // FIXME in-place transpose?
  393. var transposedData = new ArrayConstructor[this._dtype](this._size);
  394. out._array = transposedData;
  395. // @param Item offset in current axis offset of the original array
  396. // @param Item offset in current axis offset of the transposed array
  397. function transpose(axis, offset, transposedOffset) {
  398. var size = shape[axis];
  399. // strides in orginal array
  400. var stride = strides[axis];
  401. // strides in transposed array
  402. var transposedStride = transposedStrides[axis];
  403. if (axis < dim-1) {
  404. for (var i = 0; i < size; i++) {
  405. transpose(
  406. axis+1,
  407. offset + stride * i,
  408. transposedOffset + transposedStride * i
  409. );
  410. }
  411. } else {
  412. for (var i = 0; i < size; i++) {
  413. // offset + stride * i is the index of the original array
  414. // transposedOffset + i is the index of the transposed array
  415. transposedData[transposedOffset + i]
  416. = source[offset + stride * i];
  417. }
  418. }
  419. }
  420. transpose(0, 0, 0);
  421. return out;
  422. },
  423. /**
  424. * Repeat elements of an array along axis
  425. * @param {Number} repeats
  426. * The number of repetitions for each element.
  427. * repeats is broadcasted to fit the shape of the given axis.
  428. * @param {Number} [axis]
  429. * The axis along which to repeat values.
  430. * By default, use the flattened input array,
  431. * and return a flat output array.
  432. * @param {NDArray} [out]
  433. * @return {NDArray}
  434. */
  435. repeat : kwargs(function (repeats, axis, out) {
  436. var shape;
  437. // flattened input array
  438. if (typeof(axis) === 'undefined') {
  439. shape = [this._size];
  440. axis = 0;
  441. } else {
  442. shape = this._shape.slice();
  443. }
  444. var originShape = shape.slice();
  445. shape[axis] *= repeats;
  446. if (!out) {
  447. out = new NDArray(this._dtype);
  448. out.initFromShape(shape);
  449. } else {
  450. if (!arrayEqual(shape, out._shape)) {
  451. throw new Error(broadcastErrorMsg(shape, out._shape));
  452. }
  453. }
  454. var data = out._array;
  455. var stride = calculateDimStride(originShape, axis);
  456. var axisSize = originShape[axis];
  457. var source = this._array;
  458. var offsetStride = stride * axisSize;
  459. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  460. for (var k = 0; k < stride; k++) {
  461. var idx = offset + k;
  462. var idxRepeated = offset * repeats + k;
  463. for (var i = 0; i < axisSize; i++) {
  464. for (var j = 0; j < repeats; j++) {
  465. data[idxRepeated] = source[idx];
  466. idxRepeated += stride;
  467. }
  468. idx += stride;
  469. }
  470. }
  471. }
  472. return out;
  473. }),
  474. choose : function () {
  475. console.warn('TODO');
  476. },
  477. take : function () {
  478. console.warn('TODO');
  479. },
  480. tile : function () {
  481. console.warn('TODO');
  482. },
  483. /**
  484. * Preprocess for array calculation
  485. * max, min, argmax, argmin, sum, ptp, val, mean
  486. * Which will reduce one axis if the axis is given
  487. *
  488. * @param {Number} axis
  489. * @param {NDArray} out
  490. * @param {Function} funcWithAxis
  491. * @param {Function} funcFlatten
  492. * @return {Number|NDArray}
  493. */
  494. _withPreprocess1 : function (axis, out, funcWithAxis, funcFlatten) {
  495. var source = this._array;
  496. if (!this._size) {
  497. return;
  498. }
  499. if (typeof(axis)!=='undefined') {
  500. if (axis < 0) {
  501. axis = this._shape.length + axis;
  502. }
  503. if (axis >= this._shape.length || axis < 0) {
  504. throw new Error(axisOutofBoundsErrorMsg(axis));
  505. }
  506. var shape = this._shape.slice();
  507. shape.splice(axis, 1);
  508. if (out && !arrayEqual(shape, out._shape)) {
  509. throw new Error(broadcastErrorMsg(shape, out._shape));
  510. }
  511. if (!out) {
  512. out = new NDArray(this._dtype);
  513. out.initFromShape(shape);
  514. }
  515. var data = out._array;
  516. var stride = calculateDimStride(this._shape, axis);
  517. var axisSize = this._shape[axis];
  518. var offsetStride = stride * axisSize;
  519. funcWithAxis.call(
  520. this, data, source, offsetStride, axisSize, stride
  521. );
  522. return out;
  523. } else {
  524. return funcFlatten.call(this, source);
  525. }
  526. },
  527. /**
  528. * Preprocess for array calculation cumsum, cumprod
  529. * Which will keep the shape if axis is given
  530. * and flatten if axis is undefined
  531. * @param {Number} axis
  532. * @param {NDArray} out
  533. * @param {Function} funcWithAxis
  534. * @param {Function} funcFlatten
  535. * @return {NDArray}
  536. */
  537. _withPreprocess2 : function (axis, out, funcWithAxis, funcFlatten) {
  538. var source = this._array;
  539. if (!this._size) {
  540. return;
  541. }
  542. if (out && !arrayEqual(this._shape, out._shape)) {
  543. throw new Error(broadcastErrorMsg(this._shape, out._shape));
  544. }
  545. if (!out) {
  546. out = new NDArray(this._dtype);
  547. out.initFromShape(this._shape);
  548. }
  549. var data = out._array;
  550. if (typeof(axis)!=='undefined') {
  551. if (axis < 0) {
  552. axis = this._shape.length + axis;
  553. }
  554. if (axis >= this._shape.length || axis < 0) {
  555. throw new Error(axisOutofBoundsErrorMsg(axis));
  556. }
  557. if (axis >= this._shape.length) {
  558. throw new Error(axisOutofBoundsErrorMsg(axis));
  559. }
  560. var stride = calculateDimStride(this._shape, axis);
  561. var axisSize = this._shape[axis];
  562. var offsetStride = stride * axisSize;
  563. funcWithAxis.call(
  564. this, data, source, offsetStride, axisSize, stride
  565. );
  566. } else {
  567. out.reshape([this._size]);
  568. funcFlatten.call(this, data, source);
  569. }
  570. return out;
  571. },
  572. /**
  573. * Get the max value of ndarray
  574. * If the axis is given, the max is only calculate in this dimension
  575. * Example, for the given ndarray
  576. * [[3, 9],
  577. * [4, 8]]
  578. * >>> max(0)
  579. * [4, 9]
  580. * >>> max(1)
  581. * [9, 8]
  582. *
  583. * @param {Number} [axis]
  584. * @param {NDArray} out
  585. * @return {NDArray}
  586. */
  587. max : kwargs((function () {
  588. function withAxis(data, source, offsetStride, axisSize, stride) {
  589. var cursor = 0;
  590. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  591. for (var i = 0; i < stride; i++) {
  592. var idx = i + offset;
  593. var max = source[idx];
  594. for (var j = 0; j < axisSize; j++) {
  595. var d = source[idx];
  596. if (d > max) {
  597. max = d;
  598. }
  599. idx += stride;
  600. }
  601. data[cursor++] = max;
  602. }
  603. }
  604. }
  605. function withFlatten(source) {
  606. var max = source[0];
  607. for (var i = 1; i < this._size; i++) {
  608. if (source[i] > max) {
  609. max = source[i];
  610. }
  611. }
  612. return max;
  613. }
  614. return function (axis, out) {
  615. return this._withPreprocess1(
  616. axis, out,
  617. withAxis, withFlatten
  618. );
  619. };
  620. })()),
  621. /**
  622. * Return the minimum of an array or minimum along an axis.
  623. * @param {Number} [axis]
  624. * @param {NDArray} out
  625. * @return {NDArray}
  626. */
  627. min : kwargs((function () {
  628. function withAxis(data, source, offsetStride, axisSize, stride) {
  629. var cursor = 0;
  630. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  631. for (var i = 0; i < stride; i++) {
  632. var idx = i + offset;
  633. var min = source[idx];
  634. for (var j = 0; j < axisSize; j++) {
  635. var d = source[idx];
  636. if (d < min) {
  637. min = d;
  638. }
  639. idx += stride;
  640. }
  641. data[cursor++] = min;
  642. }
  643. }
  644. }
  645. function withFlatten(source) {
  646. var min = source[0];
  647. for (var i = 1; i < this._size; i++) {
  648. if (source[i] < min) {
  649. min = source[i];
  650. }
  651. }
  652. return min;
  653. }
  654. return function (axis, out) {
  655. return this._withPreprocess1(
  656. axis, out,
  657. withAxis, withFlatten
  658. );
  659. };
  660. })()),
  661. /**
  662. * Return indices of the maximum values along an axis.
  663. * @param {Number} [axis]
  664. * @param {NDArray} out
  665. * @return {NDArray}
  666. */
  667. argmax : kwargs((function () {
  668. function withAxis(data, source, offsetStride, axisSize, stride) {
  669. var cursor = 0;
  670. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  671. for (var i = 0; i < stride; i++) {
  672. var dataIdx = 0;
  673. var idx = i + offset;
  674. var max = source[idx];
  675. for (var j = 0; j < axisSize; j++) {
  676. var d = source[idx];
  677. if (d > max) {
  678. max = d;
  679. dataIdx = j;
  680. }
  681. idx += stride;
  682. }
  683. data[cursor++] = dataIdx;
  684. }
  685. }
  686. }
  687. function withFlatten(source) {
  688. var max = source[0];
  689. var idx = 0;
  690. for (var i = 1; i < this._size; i++) {
  691. if (source[i] > max) {
  692. idx = i;
  693. max = source[i];
  694. }
  695. }
  696. return idx;
  697. }
  698. return function (axis, out) {
  699. return this._withPreprocess1(
  700. axis, out,
  701. withAxis, withFlatten
  702. );
  703. };
  704. })()),
  705. /**
  706. * Indices of the minimum values along an axis.
  707. * @param {Number} [axis]
  708. * @param {NDArray} out
  709. * @return {NDArray}
  710. */
  711. argmin : kwargs((function () {
  712. function withAxis(data, source, offsetStride, axisSize, stride) {
  713. var cursor = 0;
  714. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  715. for (var i = 0; i < stride; i++) {
  716. var dataIdx = 0;
  717. var idx = i + offset;
  718. var min = source[idx];
  719. for (var j = 0; j < axisSize; j++) {
  720. var d = source[idx];
  721. if (d < min) {
  722. min = d;
  723. dataIdx = j;
  724. }
  725. idx += stride;
  726. }
  727. data[cursor++] = dataIdx;
  728. }
  729. }
  730. }
  731. function withFlatten(source) {
  732. var min = source[0];
  733. var idx = 0;
  734. for (var i = 1; i < this._size; i++) {
  735. if (source[i] < min) {
  736. idx = i;
  737. min = source[i];
  738. }
  739. }
  740. return idx;
  741. }
  742. return function (axis, out) {
  743. return this._withPreprocess1(
  744. axis, out,
  745. withAxis, withFlatten
  746. );
  747. };
  748. })()),
  749. /**
  750. * Return the sum of the array elements over the given axis.
  751. * @param {Number} [axis]
  752. * @param {NDArray} out
  753. * @return {NDArray}
  754. */
  755. sum : kwargs((function () {
  756. function withAxis(data, source, offsetStride, axisSize, stride) {
  757. var cursor = 0;
  758. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  759. for (var i = 0; i < stride; i++) {
  760. var sum = 0;
  761. var idx = i + offset;
  762. for (var j = 0; j < axisSize; j++) {
  763. sum += source[idx];
  764. idx += stride;
  765. }
  766. data[cursor++] = sum;
  767. }
  768. }
  769. }
  770. function withFlatten(source) {
  771. var sum = 0;
  772. for (var i = 0; i < this._size; i++) {
  773. sum += source[i];
  774. }
  775. return sum;
  776. }
  777. return function (axis, out) {
  778. return this._withPreprocess1(
  779. axis, out,
  780. withAxis, withFlatten
  781. );
  782. };
  783. })()),
  784. /**
  785. * Return the product of the array elements over the given axis.
  786. * @param {Number} [axis]
  787. * @param {NDArray} out
  788. * @return {NDArray}
  789. */
  790. prod : kwargs((function () {
  791. function withAxis(data, source, offsetStride, axisSize, stride) {
  792. var cursor = 0;
  793. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  794. for (var i = 0; i < stride; i++) {
  795. var prod = 1;
  796. var idx = i + offset;
  797. for (var j = 0; j < axisSize; j++) {
  798. prod *= source[idx];
  799. idx += stride;
  800. }
  801. data[cursor++] = prod;
  802. }
  803. }
  804. }
  805. function withFlatten(source) {
  806. var prod = 1;
  807. for (var i = 0; i < this._size; i++) {
  808. prod *= source[i];
  809. }
  810. return prod;
  811. }
  812. return function (axis, out) {
  813. return this._withPreprocess1(
  814. axis, out,
  815. withAxis, withFlatten
  816. );
  817. };
  818. })()),
  819. /**
  820. * Returns the average of the array elements along given axis.
  821. * @param {Number} [axis]
  822. * @param {NDArray} out
  823. * @return {NDArray}
  824. */
  825. mean : kwargs((function () {
  826. function withAxis(data, source, offsetStride, axisSize, stride) {
  827. var cursor = 0;
  828. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  829. for (var i = 0; i < stride; i++) {
  830. var sum = 0;
  831. var idx = i + offset;
  832. for (var j = 0; j < axisSize; j++) {
  833. sum += source[idx];
  834. idx += stride;
  835. }
  836. var mean = sum / axisSize;
  837. data[cursor++] = mean;
  838. }
  839. }
  840. }
  841. function withFlatten(source) {
  842. var sum = 0;
  843. var len = source.length;
  844. for (var i = 0; i < len; i++) {
  845. sum += source[i];
  846. }
  847. var mean = sum / len;
  848. return mean;
  849. }
  850. return function (axis, out) {
  851. return this._withPreprocess1(
  852. axis, out,
  853. withAxis, withFlatten
  854. );
  855. };
  856. })()),
  857. /**
  858. * Return the variance of the array elements over the given axis.
  859. * @param {Number} [axis]
  860. * @param {NDArray} out
  861. * @return {NDArray}
  862. */
  863. 'var' : kwargs((function () {
  864. function withAxis(data, source, offsetStride, axisSize, stride) {
  865. var cursor = 0;
  866. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  867. for (var i = 0; i < stride; i++) {
  868. var sum = 0;
  869. var idx = i + offset;
  870. for (var j = 0; j < axisSize; j++) {
  871. sum += source[idx];
  872. idx += stride;
  873. }
  874. var mean = sum / axisSize;
  875. var moments = 0;
  876. idx = i + offset;
  877. for (var j = 0; j < axisSize; j++) {
  878. var diff = source[idx] - mean;
  879. moments += diff * diff;
  880. idx += stride;
  881. }
  882. data[cursor++] = moments / axisSize;
  883. }
  884. }
  885. }
  886. function withFlatten(source) {
  887. var sum = 0;
  888. var len = source.length;
  889. for (var i = 0; i < len; i++) {
  890. sum += source[i];
  891. }
  892. var mean = sum / len;
  893. var moments = 0;
  894. for (var i = 0; i < len; i++) {
  895. var diff = source[i] - mean;
  896. moments += diff * diff;
  897. }
  898. return moments / len;
  899. }
  900. return function (axis, out) {
  901. return this._withPreprocess1(
  902. axis, out,
  903. withAxis, withFlatten
  904. );
  905. };
  906. })()),
  907. /**
  908. * Return the standard derivatione of the array elements
  909. * over the given axis.
  910. * @param {Number} [axis]
  911. * @param {NDArray} out
  912. * @return {NDArray}
  913. */
  914. std : kwargs((function () {
  915. function withAxis(data, source, offsetStride, axisSize, stride) {
  916. var cursor = 0;
  917. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  918. for (var i = 0; i < stride; i++) {
  919. var sum = 0;
  920. var idx = i + offset;
  921. for (var j = 0; j < axisSize; j++) {
  922. sum += source[idx];
  923. idx += stride;
  924. }
  925. var mean = sum / axisSize;
  926. var moments = 0;
  927. idx = i + offset;
  928. for (var j = 0; j < axisSize; j++) {
  929. var diff = source[idx] - mean;
  930. moments += diff * diff;
  931. idx += stride;
  932. }
  933. data[cursor++] = Math.sqrt(moments / axisSize);
  934. }
  935. }
  936. }
  937. function withFlatten(source) {
  938. var sum = 0;
  939. var len = source.length;
  940. for (var i = 0; i < len; i++) {
  941. sum += source[i];
  942. }
  943. var mean = sum / len;
  944. var moments = 0;
  945. for (var i = 0; i < len; i++) {
  946. var diff = source[i] - mean;
  947. moments += diff * diff;
  948. }
  949. return Math.sqrt(moments / len);
  950. }
  951. return function (axis, out) {
  952. return this._withPreprocess1(
  953. axis, out,
  954. withAxis, withFlatten
  955. );
  956. };
  957. })()),
  958. /**
  959. * Peak to peak (maximum - minimum) value along a given axis.
  960. * @param {Number} [axis]
  961. * @param {NDArray} out
  962. * @return {NDArray}
  963. */
  964. ptp : kwargs((function () {
  965. function withAxis(data, source, offsetStride, axisSize, stride) {
  966. var cursor = 0;
  967. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  968. for (var i = 0; i < stride; i++) {
  969. var idx = offset + i;
  970. var min = source[idx];
  971. var max = source[idx];
  972. for (var j = 0; j < axisSize; j++) {
  973. var d = source[idx];
  974. if (d < min) {
  975. min = d;
  976. }
  977. if (d > max) {
  978. max = d;
  979. }
  980. idx += stride;
  981. }
  982. data[cursor++] = max - min;
  983. }
  984. }
  985. }
  986. function withFlatten(source) {
  987. var min = source[0];
  988. var max = source[0];
  989. for (var i = 1; i < this._size; i++) {
  990. if (source[i] < min) {
  991. min = source[i];
  992. }
  993. if (source[i] > max) {
  994. max = source[i];
  995. }
  996. }
  997. return max - min;
  998. }
  999. return function (axis, out) {
  1000. return this._withPreprocess1(
  1001. axis, out,
  1002. withAxis, withFlatten
  1003. );
  1004. };
  1005. })()),
  1006. /**
  1007. *
  1008. * @param {Number} [axis=-1]
  1009. * @param {string} [order='ascending']
  1010. * 'ascending' | 'descending'
  1011. * @return {NDArray}
  1012. */
  1013. // FIXME : V8 is quick sort, firefox and safari is merge sort
  1014. // order : ascending or desc
  1015. sort : kwargs(function (axis, order) {
  1016. if (axis < 0) {
  1017. axis = this._shape.length + axis;
  1018. }
  1019. var compareFunc;
  1020. if (order === 'ascending') {
  1021. compareFunc = function (a, b) {
  1022. return a - b;
  1023. };
  1024. } else if( order === 'descending') {
  1025. compareFunc = function (a, b) {
  1026. return b - a;
  1027. };
  1028. }
  1029. var source = this._array;
  1030. var stride = calculateDimStride(this._shape, axis);
  1031. var axisSize = this._shape[axis];
  1032. var offsetStride = stride * axisSize;
  1033. var tmp = new Array(axisSize);
  1034. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  1035. for (var i = 0; i < stride; i++) {
  1036. var idx = offset + i;
  1037. for (var j = 0; j < axisSize; j++) {
  1038. tmp[j] = source[idx];
  1039. idx += stride;
  1040. }
  1041. tmp.sort(compareFunc);
  1042. var idx = offset + i;
  1043. // Copy back
  1044. for (var j = 0; j < axisSize; j++) {
  1045. source[idx] = tmp[j];
  1046. idx += stride;
  1047. }
  1048. }
  1049. }
  1050. return this;
  1051. }, {axis : -1, order : 'ascending'}),
  1052. /**
  1053. *
  1054. * @param {Number} [axis=-1]
  1055. * @param {string} [order='ascending']
  1056. * 'ascending' | 'descending'
  1057. * @param {NDArray} [out]
  1058. * @return {NDArray}
  1059. */
  1060. argsort : kwargs(function (axis, order, out) {
  1061. if (axis < 0) {
  1062. axis = this._shape.length + axis;
  1063. }
  1064. if (!this._size) {
  1065. return;
  1066. }
  1067. if (out && !arrayEqual(this._shape, out._shape)) {
  1068. throw new Error(broadcastErrorMsg(this._shape, out._shape));
  1069. }
  1070. if (!out) {
  1071. out = new NDArray(this._dtype);
  1072. out.initFromShape(this._shape);
  1073. }
  1074. var data = out._array;
  1075. var compareFunc;
  1076. if (order === 'ascending') {
  1077. compareFunc = function (a, b) {
  1078. return tmp[a] - tmp[b];
  1079. };
  1080. } else if( order === 'descending') {
  1081. compareFunc = function (a, b) {
  1082. return tmp[b] - tmp[a];
  1083. };
  1084. }
  1085. var source = this._array;
  1086. var stride = calculateDimStride(this._shape, axis);
  1087. var axisSize = this._shape[axis];
  1088. var offsetStride = stride * axisSize;
  1089. var tmp = new Array(axisSize);
  1090. var indexList = new Array(axisSize);
  1091. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  1092. for (var i = 0; i < stride; i++) {
  1093. var idx = offset + i;
  1094. for (var j = 0; j < axisSize; j++) {
  1095. tmp[j] = source[idx];
  1096. indexList[j] = j;
  1097. idx += stride;
  1098. }
  1099. indexList.sort(compareFunc);
  1100. // Copy back
  1101. var idx = offset + i;
  1102. for (var j = 0; j < axisSize; j++) {
  1103. data[idx] = indexList[j];
  1104. idx += stride;
  1105. }
  1106. }
  1107. }
  1108. return out;
  1109. }, {axis : -1, order : 'ascending'}),
  1110. /**
  1111. * Return the cumulative sum of the elements along the given axis.
  1112. * @param {Number} [axis]
  1113. * @param {NDArray} out
  1114. * @return {NDArray}
  1115. */
  1116. cumsum : kwargs((function () {
  1117. function withAxis(data, source, offsetStride, axisSize, stride) {
  1118. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  1119. for (var i = 0; i < stride; i++) {
  1120. var idx = offset + i;
  1121. var prevIdx = idx;
  1122. data[idx] = source[idx];
  1123. for (var j = 1; j < axisSize; j++) {
  1124. prevIdx = idx;
  1125. idx += stride;
  1126. data[idx] = data[prevIdx] + source[idx];
  1127. }
  1128. }
  1129. }
  1130. }
  1131. function withFlatten(data, source) {
  1132. data[0] = source[0];
  1133. for (var i = 1; i < data.length; i++) {
  1134. data[i] = data[i-1] + source[i];
  1135. }
  1136. }
  1137. return function (axis, out) {
  1138. return this._withPreprocess2(
  1139. axis, out,
  1140. withAxis, withFlatten
  1141. );
  1142. };
  1143. })()),
  1144. /**
  1145. * Return the cumulative product of the elements along the given axis.
  1146. * @param {Number} [axis]
  1147. * @param {NDArray} out
  1148. * @return {NDArray}
  1149. */
  1150. cumprod : kwargs((function () {
  1151. function withAxis(data, source, offsetStride, axisSize, stride) {
  1152. for (var offset = 0; offset < this._size; offset+=offsetStride) {
  1153. for (var i = 0; i < stride; i++) {
  1154. var idx = offset + i;
  1155. var prevIdx = idx;
  1156. data[idx] = source[idx];
  1157. for (var j = 1; j < axisSize; j++) {
  1158. prevIdx = idx;
  1159. idx += stride;
  1160. data[idx] = data[prevIdx] * source[idx];
  1161. }
  1162. }
  1163. }
  1164. }
  1165. function withFlatten(data, source) {
  1166. data[0] = source[0];
  1167. for (var i = 1; i < data.length; i++) {
  1168. data[i] = data[i-1] * source[i];
  1169. }
  1170. }
  1171. return function (axis, out) {
  1172. return this._withPreprocess2(
  1173. axis, out,
  1174. withAxis, withFlatten
  1175. );
  1176. };
  1177. })()),
  1178. /**
  1179. * Dot product of two arrays.
  1180. *
  1181. * @param {NDArray|Number} b
  1182. * @param {NDArray} [out]
  1183. * @return {NDArray|Number}
  1184. */
  1185. dot : function () {
  1186. console.warn('TODO');
  1187. },
  1188. /**
  1189. * Mapped to region [min, max]
  1190. * @param {Number} mappedMin
  1191. * @param {Number} mappedMax
  1192. */
  1193. map : function (mappedMin, mappedMax) {
  1194. var input = this._array;
  1195. var output = this._array;
  1196. var min = input[0];
  1197. var max = input[0];
  1198. var l = this._size;
  1199. for (var i = 1; i < l; i++) {
  1200. var val = input[i];
  1201. if (val < min) {
  1202. min = val;
  1203. }
  1204. if (val > max) {
  1205. max = val;
  1206. }
  1207. }
  1208. var range = max - min;
  1209. var mappedRange = mappedMax - mappedMin;
  1210. for (var i = 0; i < l; i++) {
  1211. if (range === 0) {
  1212. output[i] = mappedMin;
  1213. } else {
  1214. var val = input[i];
  1215. var percent = (val - min) / range;
  1216. output[i] = mappedRange * percent + mappedMin;
  1217. }
  1218. }
  1219. return this;
  1220. },
  1221. /**
  1222. * Add
  1223. */
  1224. add : function (rightOperand, out) {
  1225. return this.binaryOperation(
  1226. this, rightOperand, E_ADD, out
  1227. );
  1228. },
  1229. /**
  1230. * Substract
  1231. */
  1232. sub : function (rightOperand, out) {
  1233. return this.binaryOperation(
  1234. this, rightOperand, E_SUB, out
  1235. );
  1236. },
  1237. /**
  1238. * Multiply
  1239. */
  1240. mul : function (rightOperand, out) {
  1241. return this.binaryOperation(
  1242. this, rightOperand, E_MUL, out
  1243. );
  1244. },
  1245. /**
  1246. * Divide
  1247. */
  1248. div : function (rightOperand, out) {
  1249. return this.binaryOperation(
  1250. this, rightOperand, E_DIV, out
  1251. );
  1252. },
  1253. /**
  1254. * mod
  1255. */
  1256. mod : function (rightOperand, out) {
  1257. return this.binaryOperation(
  1258. this, rightOperand, E_MOD, out
  1259. );
  1260. },
  1261. /**
  1262. * and
  1263. */
  1264. and : function (rightOperand, out) {
  1265. return this.binaryOperation(
  1266. this, rightOperand, E_AND, out
  1267. );
  1268. },
  1269. /**
  1270. * or
  1271. */
  1272. or : function (rightOperand, out) {
  1273. return this.binaryOperation(
  1274. this, rightOperand, E_OR, out
  1275. );
  1276. },
  1277. /**
  1278. * xor
  1279. */
  1280. xor : function (rightOperand, out) {
  1281. return this.binaryOperation(
  1282. this, rightOperand, E_XOR, out
  1283. );
  1284. },
  1285. /**
  1286. * equal
  1287. */
  1288. equal : function (rightOperand, out) {
  1289. return this.binaryOperation(
  1290. this, rightOperand, E_EQL, out
  1291. );
  1292. },
  1293. binaryOperation : function (lo, ro, op, out) {
  1294. // Broadcasting
  1295. // http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
  1296. var shape = [];
  1297. var isLoScalar = typeof(lo) === 'number';
  1298. var isRoScalar = typeof(ro) === 'number';
  1299. if (isLoScalar) {
  1300. shape = ro._shape.slice();
  1301. } else if (isRoScalar) {
  1302. shape = lo._shape.slice();
  1303. } else {
  1304. // Starts with the trailing dimensions
  1305. var cl = lo._shape.length-1;
  1306. var cr = ro._shape.length-1;
  1307. var loBroadCasted = lo;
  1308. var roBroadCasted = ro;
  1309. while (cl >= 0 && cr >= 0) {
  1310. if (lo._shape[cl] == 1) {
  1311. shape.unshift(ro._shape[cr]);
  1312. loBroadCasted = lo.repeat(ro._shape[cr], cl);
  1313. } else if(ro._shape[cr] == 1) {
  1314. shape.unshift(lo._shape[cl]);
  1315. roBroadCasted = ro.repeat(lo._shape[cl], cr);
  1316. } else if(ro._shape[cr] == lo._shape[cl]) {
  1317. shape.unshift(lo._shape[cl]);
  1318. } else {
  1319. throw new Error(broadcastErrorMsg(lo._shape, ro._shape));
  1320. }
  1321. cl --;
  1322. cr --;
  1323. }
  1324. for (var i = cl; i >= 0; i--) {
  1325. shape.unshift(lo._shape[i]);
  1326. }
  1327. for (var i = cr; i >= 0; i--) {
  1328. shape.unshift(ro._shape[i]);
  1329. }
  1330. lo = loBroadCasted;
  1331. ro = roBroadCasted;
  1332. }
  1333. if (!out) {
  1334. out = new NDArray(this._dtype);
  1335. out.initFromShape(shape);
  1336. } else {
  1337. if (! arrayEqual(shape, out._shape)) {
  1338. throw new Error(broadcastErrorMsg(shape, out._shape));
  1339. }
  1340. }
  1341. var outData = out._array;
  1342. var diffAxis;
  1343. var isLoLarger;
  1344. var loData;
  1345. var roData;
  1346. if (isLoScalar) {
  1347. diffAxis = ro._shape.length-1;
  1348. isLoLarger = false;
  1349. loData = lo;
  1350. roData = ro._array;
  1351. } else if(isRoScalar) {
  1352. diffAxis = lo._shape.length-1;
  1353. isLoLarger = true;
  1354. roData = ro;
  1355. loData = lo._array;
  1356. } else {
  1357. diffAxis = Math.abs(lo._shape.length - ro._shape.length);
  1358. isLoLarger = lo._shape.length >= ro._shape.length;
  1359. loData = lo._array;
  1360. roData = ro._array;
  1361. }
  1362. var stride = calculateDimStride(shape, diffAxis);
  1363. var axisSize = shape[diffAxis];
  1364. var offsetStride = stride * axisSize;
  1365. var offsetRepeats = out._size / offsetStride;
  1366. var _a, _b, res;
  1367. var idx = 0;
  1368. if (isLoLarger) {
  1369. if(isRoScalar) {
  1370. for (var c = 0; c < offsetRepeats; c++) {
  1371. for (var i = 0; i < offsetStride; i++) {
  1372. _a = loData[idx]; _b = roData;
  1373. switch (op) {
  1374. case E_ADD: res = _a + _b; break;
  1375. case E_SUB: res = _a - _b; break;
  1376. case E_MUL: res = _a * _b; break;
  1377. case E_DIV: res = _a / _b; break;
  1378. case E_MOD: res = _a % _b; break;
  1379. case E_AND: res = _a & _b; break;
  1380. case E_OR: res = _a | _b; break;
  1381. case E_XOR: res = _a ^ _b; break;
  1382. case E_EQL: res = _a == _b; break;
  1383. default: throw new Error('Unkown operation ' + op);
  1384. }
  1385. outData[idx] = res;
  1386. idx ++;
  1387. }
  1388. }
  1389. } else {
  1390. for (var c = 0; c < offsetRepeats; c++) {
  1391. for (var i = 0; i < offsetStride; i++) {
  1392. _a = loData[idx]; _b = roData[i];
  1393. switch (op) {
  1394. case E_ADD: res = _a + _b; break;
  1395. case E_SUB: res = _a - _b; break;
  1396. case E_MUL: res = _a * _b; break;
  1397. case E_DIV: res = _a / _b; break;
  1398. case E_MOD: res = _a % _b; break;
  1399. case E_AND: res = _a & _b; break;
  1400. case E_OR: res = _a | _b; break;
  1401. case E_XOR: res = _a ^ _b; break;
  1402. case E_EQL: res = _a == _b; break;
  1403. default: throw new Error('Unkown operation ' + op);
  1404. }
  1405. outData[idx] = res;
  1406. idx ++;
  1407. }
  1408. }
  1409. }
  1410. } else {
  1411. if (isLoScalar) {
  1412. for (var c = 0; c < offsetRepeats; c++) {
  1413. for (var i = 0; i < offsetStride; i++) {
  1414. _a = loData; _b = roData[idx];
  1415. switch (op) {
  1416. case E_ADD: res = _a + _b; break;
  1417. case E_SUB: res = _a - _b; break;
  1418. case E_MUL: res = _a * _b; break;
  1419. case E_DIV: res = _a / _b; break;
  1420. case E_MOD: res = _a % _b; break;
  1421. case E_AND: res = _a & _b; break;
  1422. case E_OR: res = _a | _b; break;
  1423. case E_XOR: res = _a ^ _b; break;
  1424. case E_EQL: res = _a == _b; break;
  1425. default: throw new Error('Unkown operation ' + op);
  1426. }
  1427. outData[idx] = res;
  1428. idx ++;
  1429. }
  1430. }
  1431. } else {
  1432. for (var c = 0; c < offsetRepeats; c++) {
  1433. for (var i = 0; i < offsetStride; i++) {
  1434. _a = loData[idx]; _b = roData[i];
  1435. switch (op) {
  1436. case E_ADD: res = _a + _b; break;
  1437. case E_SUB: res = _a - _b; break;
  1438. case E_MUL: res = _a * _b; break;
  1439. case E_DIV: res = _a / _b; break;
  1440. case E_MOD: res = _a % _b; break;
  1441. case E_AND: res = _a & _b; break;
  1442. case E_OR: res = _a | _b; break;
  1443. case E_XOR: res = _a ^ _b; break;
  1444. case E_EQL: res = _a == _b; break;
  1445. default: throw new Error('Unkown operation ' + op);
  1446. }
  1447. outData[idx] = res;
  1448. idx ++;
  1449. }
  1450. }
  1451. }
  1452. }
  1453. return out;
  1454. },
  1455. /**
  1456. * negtive
  1457. */
  1458. neg : function () {
  1459. var data = this._array;
  1460. for (var i = 0; i < this._size; i++) {
  1461. data[i] = -data[i];
  1462. }
  1463. return this;
  1464. },
  1465. /**
  1466. * @return {NDArray} this
  1467. */
  1468. sin : function () {
  1469. return this._mathAdapter(Math.sin);
  1470. },
  1471. /**
  1472. * @return {NDArray} this
  1473. */
  1474. cos : function () {
  1475. return this._mathAdapter(Math.cos);
  1476. },
  1477. /**
  1478. * @return {NDArray} this
  1479. */
  1480. tan : function () {
  1481. return this._mathAdapter(Math.tan);
  1482. },
  1483. /**
  1484. * @return {NDArray} this
  1485. */
  1486. abs : function () {
  1487. return this._mathAdapter(Math.abs);
  1488. },
  1489. /**
  1490. * @return {NDArray} this
  1491. */
  1492. log : function () {
  1493. return this._mathAdapter(Math.log);
  1494. },
  1495. /**
  1496. * @return {NDArray} this
  1497. */
  1498. sqrt : function () {
  1499. return this._mathAdapter(Math.sqrt);
  1500. },
  1501. /**
  1502. * @return {NDArray} this
  1503. */
  1504. ceil : function () {
  1505. return this._mathAdapter(Math.ceil);
  1506. },
  1507. /**
  1508. * @return {NDArray} this
  1509. */
  1510. floor : function () {
  1511. return this._mathAdapter(Math.floor);
  1512. },
  1513. /**
  1514. * @return {NDArray} this
  1515. */
  1516. pow : function (exp) {
  1517. var data = this._array;
  1518. for (var i = 0; i < this._size; i++) {
  1519. data[i] = Math.pow(data[i], exp);
  1520. }
  1521. return this;
  1522. },
  1523. _mathAdapter : function (mathFunc) {
  1524. var data = this._array;
  1525. for (var i = 0; i < this._size; i++) {
  1526. data[i] = mathFunc(data[i]);
  1527. }
  1528. return this;
  1529. },
  1530. /**
  1531. * @param {Number} decimals
  1532. * @return {NDArray} this
  1533. */
  1534. round : function (decimals) {
  1535. decimals = Math.floor(decimals || 0);
  1536. var offset = Math.pow(10, decimals);
  1537. var data = this._array;
  1538. if (decimals === 0) {
  1539. for (var i = 0; i < this._size; i++) {
  1540. data[i] = Math.round(data[i]);
  1541. }
  1542. } else {
  1543. for (var i = 0; i < this._size; i++) {
  1544. data[i] = Math.round(data[i] * offset) / offset;
  1545. }
  1546. }
  1547. return this;
  1548. },
  1549. /**
  1550. * @param {Number} min
  1551. * @param {Number} max
  1552. * Clip to [min, max]
  1553. */
  1554. clip : function (min, max) {
  1555. // TODO : Support array_like param
  1556. var data = this._array;
  1557. for (var i = 0; i < this._size; i++) {
  1558. data[i] = Math.max(Math.min(data[i], max), min);
  1559. }
  1560. return this;
  1561. },
  1562. /**
  1563. * Indexing array, support range indexing
  1564. * @param {string} index
  1565. * Index syntax can be an integer 1, 2, 3
  1566. * Or more complex range indexing
  1567. * '1:2'
  1568. * '1:2, 1:2'
  1569. * '1:2, :'
  1570. * More about the indexing syntax can check the doc of numpy ndarray
  1571. * @param {NDArray} [out]
  1572. * @return {NDArray} New created sub array, or out if given
  1573. */
  1574. get : function (index, out) {
  1575. if (typeof(index) == 'number') {
  1576. index = index.toString();
  1577. }
  1578. var strides = calculateDimStrides(this._shape);
  1579. var res = this._parseRanges(index);
  1580. var ranges = res[0];
  1581. var shape = res[1];
  1582. if (ranges.length > this._shape.length) {
  1583. throw new Error('Too many indices');
  1584. }
  1585. // Get data
  1586. var len = ranges.length;
  1587. var data;
  1588. if (shape.length) {
  1589. out = new NDArray(this._dtype);
  1590. out.initFromShape(shape);
  1591. data = out._array;
  1592. } else {
  1593. data = [];
  1594. }
  1595. var source = this._array;
  1596. var cursor = 0;
  1597. function getPiece(axis, offset) {
  1598. var range = ranges[axis];
  1599. var stride = strides[axis];
  1600. if (axis < len-1) {
  1601. if (range[2] > 0) {
  1602. for (var i = range[0]; i < range[1]; i += range[2]) {
  1603. getPiece(axis+1, offset + stride * i);
  1604. }
  1605. } else {
  1606. for (var i = range[0]; i > range[1]; i += range[2]) {
  1607. getPiece(axis+1, offset + stride * i);
  1608. }
  1609. }
  1610. } else {
  1611. if (range[2] > 0) {
  1612. for (var i = range[0]; i < range[1]; i += range[2]) {
  1613. for (var j = 0; j < stride; j++) {
  1614. data[cursor++] = source[i*stride + j + offset];
  1615. }
  1616. }
  1617. } else {
  1618. for (var i = range[0]; i > range[1]; i += range[2]) {
  1619. for (var j = 0; j < stride; j++) {
  1620. data[cursor++] = source[i*stride + j + offset];
  1621. }
  1622. }
  1623. }
  1624. }
  1625. }
  1626. getPiece(0, 0);
  1627. if (shape.length) {
  1628. // Return scalar
  1629. return out;
  1630. } else {
  1631. return data[0];
  1632. }
  1633. },
  1634. /**
  1635. *
  1636. * @param {string} index
  1637. * index syntax can be an integer 1, 2, 3
  1638. * Or more complex range indexing
  1639. * '1:2'
  1640. * '1:2, 1:2'
  1641. * '1:2, :'
  1642. * More about the indexing syntax can check the doc of numpy ndarray
  1643. * @param {NDArray} ndarray Ndarray data source
  1644. * @return {NDArray} this
  1645. */
  1646. set : function (index, narray) {
  1647. if (typeof(index) == 'number') {
  1648. index = index.toString();
  1649. }
  1650. var strides = calculateDimStrides(this._shape);
  1651. var res = this._parseRanges(index);
  1652. var ranges = res[0];
  1653. var shape = res[1];
  1654. if (ranges.length > this._shape.length) {
  1655. throw new Error('Too many indices');
  1656. }
  1657. var isScalar = typeof(narray) == 'number';
  1658. var len = ranges.length;
  1659. var data = this._array;
  1660. if (isScalar) {
  1661. // Set with a single scalar
  1662. var source = narray;
  1663. } else {
  1664. if (!arrayEqual(shape, narray.shape())) {
  1665. throw new Error(broadcastErrorMsg(shape, narray.shape()));
  1666. }
  1667. var source = narray._array;
  1668. }
  1669. var cursor = 0;
  1670. var setPiece = function (axis, offset) {
  1671. var range = ranges[axis];
  1672. var stride = strides[axis];
  1673. if (axis < len-1) {
  1674. if (range[2] > 0) {
  1675. for (var i = range[0]; i < range[1]; i += range[2]) {
  1676. setPiece(axis+1, offset + stride * i);
  1677. }
  1678. } else {
  1679. for (var i = range[0]; i > range[1]; i += range[2]) {
  1680. setPiece(axis+1, offset + stride * i);
  1681. }
  1682. }
  1683. } else {
  1684. if (range[2] > 0) {
  1685. for (var i = range[0]; i < range[1]; i += range[2]) {
  1686. for (var j = 0; j < stride; j++) {
  1687. if (isScalar) {
  1688. data[i*stride + j + offset] = source;
  1689. } else {
  1690. data[i*stride + j + offset] = source[cursor++];
  1691. }
  1692. }
  1693. }
  1694. } else {
  1695. for (var i = range[0]; i > range[1]; i += range[2]) {
  1696. for (var j = 0; j < stride; j++) {
  1697. if (isScalar) {
  1698. data[i*stride + j + offset] = source;
  1699. } else {
  1700. data[i*stride + j + offset] = source[cursor++];
  1701. }
  1702. }
  1703. }
  1704. }
  1705. }
  1706. };
  1707. setPiece(0, 0);
  1708. return this;
  1709. },
  1710. /**
  1711. * Insert values along the given axis before the given indices.
  1712. * @param {Number|Array} obj
  1713. * Object that defines the index or indices before
  1714. * which values is inserted.
  1715. * @param {Number|Array|NDArray} values
  1716. * Values to insert
  1717. * @param {Number} [axis]
  1718. * @return {NDArray} this
  1719. */
  1720. insert : kwargs(function (obj, values, axis) {
  1721. var data = this._array;
  1722. var isObjScalar = false;
  1723. if (typeof(obj) === 'number') {
  1724. obj = [obj];
  1725. isObjScalar = true;
  1726. }
  1727. if (typeof(values) === 'number') {
  1728. values = new NDArray([values]);
  1729. } else if (values instanceof Array) {
  1730. values = new NDArray(values);
  1731. }
  1732. if (typeof(axis) === 'undefined') {
  1733. this._shape = [this._size];
  1734. axis = 0;
  1735. }
  1736. // Checking if indices is valid
  1737. var prev = obj[0];
  1738. var axisSize = this._shape[axis];
  1739. for (var i = 0; i < obj.length; i++) {
  1740. if (obj[i] < 0) {
  1741. obj[i] = axisSize + obj[i];
  1742. }
  1743. if (obj[i] > axisSize) {
  1744. throw new Error(indexOutofBoundsErrorMsg(obj[i]));
  1745. }
  1746. if (obj[i] < prev) {
  1747. throw new Error('Index must be in ascending order');
  1748. }
  1749. prev = obj[i];
  1750. }
  1751. // Broadcasting
  1752. var targetShape = this._shape.slice();
  1753. if (isObjScalar) {
  1754. targetShape.splice(axis, 1);
  1755. } else {
  1756. targetShape[axis] = obj.length;
  1757. }
  1758. var sourceShape = values._shape;
  1759. var cs = sourceShape.length - 1;
  1760. var ct = targetShape.length - 1;
  1761. var valueBroadcasted = values;
  1762. while (cs >= 0 && ct >= 0) {
  1763. if (sourceShape[cs] === 1) {
  1764. valueBroadcasted = values.repeat(targetShape[ct], cs);
  1765. } else if(sourceShape[cs] !== targetShape[ct]) {
  1766. throw new Error(broadcastErrorMsg(sourceShape, targetShape));
  1767. }
  1768. cs --;
  1769. ct --;
  1770. }
  1771. values = valueBroadcasted;
  1772. // Calculate indices to insert
  1773. var stride = calculateDimStride(this._shape, axis);
  1774. var axisSize = this._shape[axis];
  1775. var offsetStride = axisSize * stride;
  1776. var offsetRepeats = this._size / offsetStride;
  1777. var objLen = obj.length;
  1778. var indices = new Uint32Array(offsetRepeats * objLen);
  1779. var cursor = 0;
  1780. for (var offset = 0; offset < this._size; offset += offsetStride) {
  1781. for (var i = 0; i < objLen; i++) {
  1782. var objIdx = obj[i];
  1783. indices[cursor++] = offset + objIdx * stride;
  1784. }
  1785. }
  1786. var resShape = this._shape.slice();
  1787. resShape[axis] += obj.length;
  1788. var resSize = getSize(resShape);
  1789. if (this._array.length < resSize) {
  1790. var data = new ArrayConstructor[this._dtype](resSize);
  1791. } else {
  1792. var data = this._array;
  1793. }
  1794. var source = this._array;
  1795. var valuesArr = values._array;
  1796. var idxCursor = indices.length - 1;
  1797. var end = this._size;
  1798. var start = indices[idxCursor];
  1799. var dataCursor = resSize - 1;
  1800. var valueCursor = values._size - 1;
  1801. while (idxCursor >= 0) {
  1802. // Copy source data;
  1803. for (var i = end - 1; i >= start; i--) {
  1804. data[dataCursor--] = source[i];
  1805. }
  1806. end = start;
  1807. start = indices[--idxCursor];
  1808. // Copy inserted data;
  1809. for (var i = 0; i < stride; i++) {
  1810. if (valueCursor < 0) {
  1811. valueCursor = values._size - 1;
  1812. }
  1813. data[dataCursor--] = valuesArr[valueCursor--];
  1814. }
  1815. }
  1816. // Copy the rest
  1817. for (var i = end - 1; i >= 0; i--) {
  1818. data[dataCursor--] = source[i];
  1819. }
  1820. this._array = data;
  1821. this._shape = resShape;
  1822. this._size = resSize;
  1823. return this;
  1824. }),
  1825. append : function () {
  1826. console.warn('TODO');
  1827. },
  1828. /**
  1829. * Delete values along the axis
  1830. * @param {Array|Number} obj
  1831. * @param {Number} [axis]
  1832. * @return {NDArray} this
  1833. */
  1834. 'delete' : kwargs(function (obj, axis) {
  1835. var data = this._array;
  1836. if (typeof(obj) === 'number') {
  1837. obj = [obj];
  1838. }
  1839. var size = this._size;
  1840. if (typeof(axis) === 'undefined') {
  1841. this._shape = [size];
  1842. axis = 0;
  1843. }
  1844. var stride = calculateDimStride(this._shape, axis);
  1845. var axisSize = this._shape[axis];
  1846. var offsetStride = stride * axisSize;
  1847. var cursor = 0;
  1848. for (var offset = 0; offset < size; offset += offsetStride) {
  1849. var start = 0;
  1850. var end = obj[0];
  1851. var objCursor = 0;
  1852. while(objCursor < obj.length) {
  1853. if (end < 0) {
  1854. end = end + axisSize;
  1855. }
  1856. if (end > axisSize) {
  1857. throw new Error(indexOutofBoundsErrorMsg(end));
  1858. }
  1859. if (end < start) {
  1860. throw new Error('Index must be in ascending order');
  1861. }
  1862. for (var i = start; i < end; i++) {
  1863. for (var j = 0; j < stride; j++) {
  1864. data[cursor++] = data[i * stride + j + offset];
  1865. }
  1866. }
  1867. start = end + 1;
  1868. end = obj[++objCursor];
  1869. }
  1870. // Copy the rest
  1871. for (var i = start; i < axisSize; i++) {
  1872. for (var j = 0; j < stride; j++) {
  1873. data[cursor++] = data[i * stride + j + offset];
  1874. }
  1875. }
  1876. }
  1877. this._shape[axis] -= obj.length;
  1878. this._size = getSize(this._shape);
  1879. return this;
  1880. }),
  1881. _parseRanges : function (index) {
  1882. var rangesStr = index.split(/\s*,\s*/);
  1883. // Parse range of each axis
  1884. var ranges = [];
  1885. var shape = [];
  1886. var j = 0;
  1887. for (var i = 0; i < rangesStr.length; i++) {
  1888. if (rangesStr[i] === '...') {
  1889. var end = this._shape.length - (rangesStr.length - i);
  1890. while (j <= end) {
  1891. ranges.push([0, this._shape[j], 1]);
  1892. shape.push(this._shape[j]);
  1893. j++;
  1894. }
  1895. } else {
  1896. var range = parseRange(rangesStr[i], this._shape[j]);
  1897. ranges.push(range);
  1898. if(rangesStr[i].indexOf(':') >= 0) {
  1899. var size = Math.floor((range[1] - range[0]) / range[2]);
  1900. size = size < 0 ? 0 : size;
  1901. // Get a range not a item
  1902. shape.push(size);
  1903. }
  1904. j++;
  1905. }
  1906. }
  1907. // Copy the lower dimension size
  1908. for (; j < this._shape.length; j++) {
  1909. shape.push(this._shape[j]);
  1910. }
  1911. return [ranges, shape];
  1912. },
  1913. /**
  1914. * Export normal js array
  1915. * @return {Array}
  1916. */
  1917. toArray : function () {
  1918. var data = this._array;
  1919. var cursor = 0;
  1920. var shape = this._shape;
  1921. var dim = shape.length;
  1922. function create(axis, out) {
  1923. var len = shape[axis];
  1924. for (var i = 0; i < len; i++) {
  1925. if (axis < dim-1) {
  1926. create(axis+1, out[i] = []);
  1927. } else {
  1928. out[i] = data[cursor++];
  1929. }
  1930. }
  1931. }
  1932. var output = [];
  1933. create(0, output);
  1934. return output;
  1935. },
  1936. /**
  1937. * Create a copy of self
  1938. * @return {NDArray}
  1939. */
  1940. copy : function () {
  1941. var numArr = new NDArray();
  1942. numArr._array = ArraySlice.call(this._array);
  1943. numArr._shape = this._shape.slice();
  1944. numArr._dtype = this._dtype;
  1945. numArr._size = this._size;
  1946. return numArr;
  1947. },
  1948. constructor : NDArray
  1949. };
  1950. /**
  1951. *
  1952. * @param {Number} [min=0]
  1953. * @param {Number} max
  1954. * @param {Number} [step=1]
  1955. * @param {string} [dtype]
  1956. * @return {NDArray}
  1957. */
  1958. NDArray.range = kwargs(function (min, max, step, dtype) {
  1959. var args = ArraySlice.call(arguments);
  1960. // Last argument describe the data type of ndarray
  1961. var lastArg = args[args.length-1];
  1962. if (typeof(lastArg) == 'string') {
  1963. var dtype = lastArg;
  1964. args.pop();
  1965. }
  1966. if (args.length === 1) {
  1967. max = args[0];
  1968. step = 1;
  1969. min = 0;
  1970. } else if(args.length == 2) {
  1971. step = 1;
  1972. }
  1973. dtype = dtype || 'number';
  1974. var array = new ArrayConstructor[dtype](Math.ceil((max - min)/step));
  1975. var cursor = 0;
  1976. for (var i = min; i < max; i+=step) {
  1977. array[cursor++] = i;
  1978. }
  1979. var ndarray = new NDArray();
  1980. ndarray._array = array;
  1981. ndarray._shape = [array.length];
  1982. ndarray._dtype = dtype;
  1983. ndarray._size = array.length;
  1984. return ndarray;
  1985. });
  1986. /**
  1987. *
  1988. * @param {Array} shape
  1989. * @param {String} [dtype]
  1990. * @return {NDArray}
  1991. */
  1992. NDArray.zeros = kwargs(function (shape, dtype) {
  1993. var ret = new NDArray(dtype);
  1994. ret.initFromShape(shape);
  1995. return ret;
  1996. });
  1997. /**
  1998. * Python like array indexing
  1999. * http://www.python.org/dev/peps/pep-0204/
  2000. *
  2001. * @param {string} index
  2002. * index can be a simple integer 1,2,3,
  2003. * or a range 2:10, 2:10:1
  2004. * example :
  2005. * 2:10 => [2, 10, 1],
  2006. * 10:2:-2 => [10, 2, -2],
  2007. * : => [0, dimSize, 1],
  2008. * ::-1 => [dimSize-1, -1, -1],
  2009. * @param {number} dimSize
  2010. * @return {Array} a tuple array [startOffset, endOffset, sliceStep]
  2011. */
  2012. function parseRange(index, dimSize) {
  2013. if (index.indexOf(':') >= 0) {
  2014. // Range indexing;
  2015. var res = index.split(/\s*:\s*/);
  2016. var step = parseInt(res[2] || 1, 10);
  2017. var start, end;
  2018. if (step === 0) {
  2019. throw new Error('Slice step cannot be zero');
  2020. }
  2021. else if (step > 0) {
  2022. start = parseInt(res[0] || 0, 10);
  2023. end = parseInt(res[1] || dimSize, 10);
  2024. }
  2025. else {
  2026. start = parseInt(res[0] || dimSize - 1, 10);
  2027. end = parseInt(res[1] || -1, 10);
  2028. }
  2029. // Negtive offset
  2030. if (start < 0) {
  2031. start = dimSize + start;
  2032. }
  2033. // Negtive offset
  2034. if (end < 0 && res[1]) {
  2035. end = dimSize + end;
  2036. }
  2037. if (step > 0) {
  2038. // Clamp to [0-dimSize]
  2039. start = Math.max(Math.min(dimSize, start), 0);
  2040. // Clamp to [0-dimSize]
  2041. end = Math.max(Math.min(dimSize, end), 0);
  2042. } else {
  2043. // Clamp to [0-dimSize)
  2044. start = Math.max(Math.min(dimSize-1, start), -1);
  2045. // Clamp to [0-dimSize)
  2046. end = Math.max(Math.min(dimSize-1, end), -1);
  2047. }
  2048. return [start, end, step];
  2049. } else {
  2050. var start = parseInt(index, 10);
  2051. // Negtive offset
  2052. if (start < 0) {
  2053. start = dimSize + start;
  2054. }
  2055. if (start < 0 || start > dimSize) {
  2056. throw new Error(indexOutofBoundsErrorMsg(index));
  2057. }
  2058. // Clamp to [0-dimSize)
  2059. start = Math.max(Math.min(dimSize-1, start), 0);
  2060. return [start, start+1, 1];
  2061. }
  2062. }
  2063. function getSize(shape) {
  2064. var size = shape[0];
  2065. for (var i = 1; i < shape.length; i++) {
  2066. size *= shape[i];
  2067. }
  2068. return size;
  2069. }
  2070. function getDimension(array) {
  2071. var dim = 1;
  2072. var el = array[0];
  2073. while (el instanceof Array) {
  2074. el = el[0];
  2075. dim ++;
  2076. }
  2077. return dim;
  2078. }
  2079. function getShape(array) {
  2080. var shape = [array.length];
  2081. var el = array[0];
  2082. while (el instanceof Array) {
  2083. shape.push(el.length);
  2084. el = el[0];
  2085. }
  2086. return shape;
  2087. }
  2088. function calculateDimStride(shape, axis) {
  2089. if (axis == shape.length-1) {
  2090. return 1;
  2091. }
  2092. var stride = shape[axis+1];
  2093. for (var i = axis+2; i < shape.length; i++) {
  2094. stride *= shape[i];
  2095. }
  2096. return stride;
  2097. }
  2098. function calculateDimStrides(shape) {
  2099. // Calculate stride of each axis
  2100. var strides = [];
  2101. var tmp = 1;
  2102. var len = getSize(shape);
  2103. for (var i = 0; i < shape.length; i++) {
  2104. tmp *= shape[i];
  2105. strides.push(len / tmp);
  2106. }
  2107. return strides;
  2108. }
  2109. function arrayEqual(arr1, arr2) {
  2110. if (arr1.length !== arr2.length) {
  2111. return false;
  2112. }
  2113. for (var i = 0; i <arr1.length; i++) {
  2114. if (arr1[i] !== arr2[i]) {
  2115. return false;
  2116. }
  2117. }
  2118. return true;
  2119. }
  2120. function broadcastErrorMsg(shape1, shape2) {
  2121. return 'Shape ('
  2122. + shape1.toString() + ') (' + shape2.toString()
  2123. +') could not be broadcast together';
  2124. }
  2125. function axisOutofBoundsErrorMsg(axis) {
  2126. return 'Axis ' + axis + ' out of bounds';
  2127. }
  2128. function indexOutofBoundsErrorMsg(idx) {
  2129. return 'Index ' + idx + ' out of bounds';
  2130. }
  2131. return NDArray;
  2132. });