├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src ├── index.ts └── lib │ ├── config.ts │ ├── dtypes.ts │ ├── errors.ts │ ├── index.ts │ ├── ndarray.ts │ └── utils.ts ├── test └── mocha │ ├── abs.spec.ts │ ├── add.spec.ts │ ├── arange.spec.ts │ ├── arccos.spec.ts │ ├── arcsin.spec.ts │ ├── arctan.spec.ts │ ├── array.spec.ts │ ├── assign.spec.ts │ ├── clip.spec.ts │ ├── clone.spec.ts │ ├── concatenate.spec.ts │ ├── convolve.spec.ts │ ├── cos.spec.ts │ ├── diag.spec.ts │ ├── divide.spec.ts │ ├── dot.spec.ts │ ├── empty.spec.ts │ ├── equal.spec.ts │ ├── errors.spec.ts │ ├── exp.spec.ts │ ├── fft.spec.ts │ ├── flatten.spec.ts │ ├── flip.spec.ts │ ├── get.spec.ts │ ├── hi.spec.ts │ ├── identity.spec.ts │ ├── lo.spec.ts │ ├── log.spec.ts │ ├── max.spec.ts │ ├── mean.spec.ts │ ├── min.spec.ts │ ├── mod.spec.ts │ ├── multiply.spec.ts │ ├── ndim.spec.ts │ ├── negative.spec.ts │ ├── ones.spec.ts │ ├── power.spec.ts │ ├── random.spec.ts │ ├── reshape.spec.ts │ ├── rot90.spec.ts │ ├── round.spec.ts │ ├── shape.spec.ts │ ├── sigmoid.spec.ts │ ├── sin.spec.ts │ ├── size.spec.ts │ ├── slice.spec.ts │ ├── softmax.spec.ts │ ├── sqrt.spec.ts │ ├── stack.spec.ts │ ├── std.spec.ts │ ├── subtract.spec.ts │ ├── sum.spec.ts │ ├── tan.spec.ts │ ├── tanh.spec.ts │ ├── to-string.spec.ts │ ├── transpose.spec.ts │ ├── utils.spec.ts │ └── zeros.spec.ts ├── tsconfig.json ├── tsconfig.module.json └── yarn.lock /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: CI 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [12.x, 14.x, 15.x, 16.x] 15 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 16 | 17 | steps: 18 | # - uses: actions/checkout@v2 19 | # - name: Use Node.js ${{ matrix.node-version }} 20 | # uses: actions/setup-node@v2 21 | # with: 22 | # node-version: ${{ matrix.node-version }} 23 | # - run: npm ci 24 | # - run: npm run build --if-present 25 | # - run: npm test 26 | - uses: actions/checkout@v2 27 | - name: Use Node.js 28 | uses: actions/setup-node@v1 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | - name: Install dependencies 32 | run: yarn --frozen-lockfile 33 | - name: Run tests 34 | run: yarn test 35 | # - name: Run coverage 36 | # # env: 37 | # # REPO_TOKEN: ${{ secrets.REPO_TOKEN }} 38 | # run: yarn cov:lcov 39 | # - name: Publish to coveralls.io 40 | # uses: coverallsapp/github-action@v1.1.2 41 | # with: 42 | # github-token: ${{ github.token }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | .idea 11 | .grunt 12 | 13 | pids 14 | logs 15 | results 16 | 17 | npm-debug.log 18 | node_modules 19 | doc 20 | bower_components 21 | *.DS_Store 22 | .nyc_output 23 | build 24 | coverage -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "eamodio.gitlens", 6 | "streetsidesoftware.code-spell-checker", 7 | "editorconfig.editorconfig" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | // To debug, make sure a *.spec.ts file is active in the editor, then run a configuration 5 | { 6 | "type": "node", 7 | "request": "launch", 8 | "name": "Debug Active Spec", 9 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava", 10 | "runtimeArgs": ["debug", "--break", "--serial", "${file}"], 11 | "port": 9229, 12 | "outputCapture": "std", 13 | "skipFiles": ["/**/*.js"], 14 | "preLaunchTask": "npm: build" 15 | // "smartStep": true 16 | }, 17 | { 18 | // Use this one if you're already running `yarn watch` 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Debug Active Spec (no build)", 22 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava", 23 | "runtimeArgs": ["debug", "--break", "--serial", "${file}"], 24 | "port": 9229, 25 | "outputCapture": "std", 26 | "skipFiles": ["/**/*.js"] 27 | // "smartStep": true 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "ts-node", 33 | "runtimeArgs": ["-r", "${workspaceFolder}/node_modules/ts-node/register"], 34 | "args": ["${file}"] 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.userWords": [], // only use words from .cspell.json 3 | "cSpell.enabled": true, 4 | "editor.formatOnSave": true, 5 | "typescript.tsdk": "node_modules/typescript/lib", 6 | "typescript.enablePromptUseWorkspaceTsdk": true, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "[typescript]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### [0.17.34](https://github.com/grimmer0125/numjs/compare/v0.17.32...v0.17.34) (2021-11-22) 4 | 5 | - Improve exported nj as a default export module instead of nj object for better IDE intelligence 6 | ### [0.17.32](https://github.com/grimmer0125/numjs/compare/v0.17.30...v0.17.32) (2021-11-21) 7 | 8 | - Define DType instead of DataType. 9 | - Make codes compatible latest @types/ndarray. 10 | - Improve NdArray constructor typings. 11 | 12 | ### [0.17.30](https://github.com/grimmer0125/numjs/compare/v0.17.28...v0.17.30) (2021-11-20) 13 | 14 | - Use DataType instead of string for dtype autocomplete. 15 | - Fix some tpyo dtype "int" in unit tests and README. 16 | 17 | ### [0.17.28](https://github.com/grimmer0125/numjs/compare/v0.17.25...v0.17.28) (2021-11-20) 18 | 19 | - Improve NdArray constructor parameter typing. 20 | 21 | - Improve documentation. 22 | 23 | ### [0.17.25](https://github.com/grimmer0125/numjs/compare/v0.17.19...v0.17.25) (2021-11-20) 24 | 25 | - Fix wrong internal ndarray container when using TypedArray in nj.array #9 26 | 27 | - Improve documentation. 28 | 29 | ### [0.17.19](https://github.com/grimmer0125/numjs/compare/v0.17.16...v0.17.19) (2021-11-12) 30 | 31 | - Add CDN parcel build. 32 | ### [0.17.16](https://github.com/grimmer0125/numjs/compare/v0.17.14...v0.17.16) (2021-11-11) 33 | 34 | - Improve documentation. 35 | - Remove lodash dependency. 36 | 37 | ### [0.17.14](https://github.com/grimmer0125/numjs/compare/v0.17.11...v0.17.14) (2021-11-10) 38 | 39 | - Improve a little documentation and typings. 40 | - Rename nj.uint8_clamped to nj.uint8Clamped 41 | 42 | ### [0.17.11](https://github.com/grimmer0125/numjs/compare/v0.17.10...v0.17.11) (2021-11-09) 43 | 44 | Fix nj.array and nj.int8, nj.int16...nj.uint8_clamped parameter typings. 45 | 46 | ### [0.17.10](https://github.com/grimmer0125/numjs/compare/v0.17.0...v0.17.10) (2021-11-09) 47 | 48 | - Improve documentation. 49 | - Improve typings (mainly concatenate return type and dtype ArrayLikeConstructor). 50 | - Add data container "uint8_clamped" / Uint8ClampedArray support (experimental). 51 | ### [0.17.0](https://github.com/grimmer0125/numjs/compare/v0.16.0.1...v0.17.0) (2021-11-08) 52 | 53 | - Remove image manipulation which may cause mac m1 installation failure, also it may be not needed in some use cases. 54 | - Add TypeScript and typing for parameters and return value. 55 | - Use ES6 syntax: const/let, and class and `import` 56 | - Remove karma tests 57 | - Building results can be used by `require (commonjs)` and es6 `import`, including TypeScript typing. Use https://github.com/bitjson/typescript-starter to build instead of grunt. Also, it ships with a main.js, and a javascript tree-shakable javascript module. 58 | - Remove `expect.js` and use built-in chai test api (`expect().to.throw()`) instead. 59 | - Fix rot90 missing ndim comparison bug, [commit](https://github.com/grimmer0125/numjs/pull/4/commits/dbf70845cbb784748fbc16d87bfb69b47053f7c2) 60 | - Fix inspect log part using util.inspect.custom on Node.js 61 | - Add Istanbul nyc coverage 62 | - Use yarn stead 63 | - Fix function return type, [commit](https://github.com/grimmer0125/numjs/pull/4/commits/d77f2a0788353f4680ec0befd3b974969d8524d2) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Grimmer Kang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | This repository incorporates code from 24 | [NumJs](https://github.com/nicolaspanel/numjs) covered by the following 25 | copyright and permission notice: 26 | 27 | The MIT License (MIT) 28 | 29 | Copyright (c) 2016 Nicolas Panel 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @d4c/numjs 2 | 3 | [![npm version](https://img.shields.io/npm/v/%40d4c/numjs.svg)](https://www.npmjs.com/package/@d4c/numjs) ![example workflow](https://github.com/grimmer0125/numjs/actions/workflows/node.js.yml/badge.svg) 4 | 5 | __NumJs__ is built on top of [ndarray](https://scijs.net/packages/#scijs/ndarray) and uses many [scijs packages](https://scijs.net/packages/). `ndarray` is exported as `nj.ndarray`, and any `NdArray` instance's property `selection` is a `ndarray` object. 6 | 7 | This project is modified from https://github.com/nicolaspanel/numjs and does below modifications 8 | - Remove the feature of images manipulation whose dependencies may result in some installation failure on Mac M1 ARM machine. You could consider [ndarray-pixels](https://github.com/donmccurdy/ndarray-pixels) if you need this feature. 9 | - Add TypeScript typings and `.d.ts` is out of box, JavaScript is supported, too. Also, it includes 10 | - ES6 build (ES2015) with CommonJS module for main build in package.json. 11 | - ES6 build (ES2015) with ES6 module for module build. Some tools will follow the module field in package.json, like Rollup, Webpack, or Parcel. 12 | - Refactor internal code via ES6 syntax and does not change the core algorithm code. 13 | - Add "uint8_clamped" (Uint8ClampedArray) support. 14 | - Other improvements. 15 | 16 | You can check the [changelog](https://github.com/grimmer0125/numjs/blob/master/CHANGELOG.md). 17 | 18 | ## Features 19 | 20 | __NumJs__ is a npm package for scientific computing with JavaScript. It contains among other things: 21 | - a powerful N-dimensional array object, `NdArray` 22 | - linear algebra function 23 | - fast Fourier transform 24 | 25 | Besides its obvious scientific uses, __NumJs__ can also be used as an efficient multi-dimensional container of generic data. 26 | 27 | It works both in node.js and in the browser. 28 | 29 | __NumJs__ is licensed under the [MIT license](https://github.com/grimmer0125/numjs/blob/master/LICENSE), enabling reuse with almost no restrictions. 30 | 31 | Try this [jsfiddle](https://jsfiddle.net/grimmer0125/8jft09q3) to play around with the library. 32 | ## Installation 33 | 34 | ```sh 35 | npm install @d4c/numjs 36 | # or 37 | yarn add @d4c/numjs 38 | ``` 39 | 40 | then using ES6 import: 41 | 42 | ```ts 43 | import nj from "@d4c/numjs"; 44 | ``` 45 | Or CommonJS require: 46 | 47 | ```ts 48 | // TypeScript users will not get typings when using require 49 | const nj = require('@d4c/numjs').default; 50 | ``` 51 | 52 | Or download from CDN: 53 | ```html 54 | 58 | ``` 59 | 60 | If using `ES6 import` or `CommonJS require` resuls in some errors, please try to use `import nj from "@d4c/numjs/build/module/numjs.min.js` in your environments. 61 | ## Basics 62 | 63 | ### Array Creation 64 | 65 | ```ts 66 | > const a = nj.array([2,3,4]); 67 | > a 68 | array([ 2, 3, 4]) 69 | > const b = nj.array([[1,2,3], [4,5,6]]); 70 | > b 71 | array([[ 1, 2, 3], 72 | [ 4, 5, 6]]) 73 | ``` 74 | 75 | __Note__: Default data container is JavaScript `Array` object. If needed, you can also use typed array such as `Uint8Array`: 76 | 77 | ```ts 78 | > const a = nj.uint8([1,2,3]); 79 | > a 80 | array([ 1, 2, 3], dtype=uint8) 81 | ``` 82 | 83 | Below are alternative ways to create same `NdArray`. 84 | 85 | ```ts 86 | const a = nj.array([1, 2, 3], "uint8"); 87 | const b = nj.array([1, 2, 3], Uint8Array); 88 | const c = nj.array(new Uint8Array([1, 2, 3])); 89 | const d = nj.arange(3,"uint8"); // results in array([ 0, 1, 2], dtype=uint8) 90 | // but we want [1,2,3], how? 91 | // we can manually create a scijs/ndarray object, then assign it as selection property 92 | d.selection = nj.ndarray(new Uint8Array([1, 2, 3])); 93 | // d.selection.data is the stored raw Uint8Array([1, 2, 3]) 94 | ``` 95 | 96 | __Note__: possible types are int8, uint8, int16, uint16, int32, uint32, float32, float64, uint8_clamped and array (the default) 97 | 98 | To create arrays with a given shape, you can use `zeros`, `ones` or `random` functions: 99 | 100 | ```ts 101 | > nj.zeros([2,3]); 102 | array([[ 0, 0, 0], 103 | [ 0, 0, 0]]) 104 | > nj.ones([2,3,4], 'int32') // dtype can also be specified 105 | array([[[ 1, 1, 1, 1], 106 | [ 1, 1, 1, 1], 107 | [ 1, 1, 1, 1]], 108 | [[ 1, 1, 1, 1], 109 | [ 1, 1, 1, 1], 110 | [ 1, 1, 1, 1]]], dtype=int32) 111 | 112 | > nj.random([4,3]) 113 | array([[ 0.9182 , 0.85176, 0.22587], 114 | [ 0.50088, 0.74376, 0.84024], 115 | [ 0.74045, 0.23345, 0.20289], 116 | [ 0.00612, 0.37732, 0.06932]]) 117 | ``` 118 | 119 | To create sequences of numbers, __NumJs__ provides a function called `arange`: 120 | 121 | ```ts 122 | > nj.arange(4); 123 | array([ 0, 1, 2, 3]) 124 | 125 | > nj.arange( 10, 30, 5 ) 126 | array([ 10, 15, 20, 25]) 127 | 128 | > nj.arange(1, 5, 'uint8'); 129 | array([ 1, 2, 3, 4], dtype=uint8) 130 | ``` 131 | 132 | #### Optional NdArray constructor parameters, stride and offset 133 | 134 | The code is like 135 | 136 | ```ts 137 | // pass data, shape, stride, offset arguments. 138 | // this will call nj.ndarray([2, 3, 4], [3], [1], 0) and assign it to NdArray's selection property 139 | const a = new nj.NdArray([2, 3, 4], [3], [1], 0); 140 | ``` 141 | 142 | Or you want to apply on a raw scijs ndarray object 143 | 144 | ```ts 145 | // scijs ndarray object 146 | const a = nj.ndarray([2, 3, 4], [3], [1], 0); 147 | ``` 148 | 149 | 150 | `NdArray and ndarray` are also exported. You can also use 151 | ```ts 152 | import { NdArray, ndarray } from "@d4c/numjs" 153 | // or CommonJS: 154 | const NdArray = require('numjs').NdArray; 155 | const ndarray = require('numjs').ndarray; 156 | ``` 157 | to reduce typing "`nj.`". 158 | 159 | ### More info about the array 160 | 161 | __NumJs__’s array class is called `NdArray`. It is also known by the alias `array`. The more important properties of an `NdArray` object are: 162 | - `NdArray#ndim`: the number of axes (dimensions) of the array. 163 | - `NdArray#shape`: the dimensions of the array. This is a list of integers indicating the size of the array in each dimension. For a matrix with n rows and m columns, shape will be [n,m]. The length of the shape is therefore the number of dimensions, ndim. 164 | - `NdArray#size`: the total number of elements of the array. This is equal to the product of the elements of shape. 165 | - `NdArray#dtype`: a string describing the type of the elements in the array. `int32`, `int16`, and `float64` are some examples. Default dtype is `array`. 166 | 167 | An `NdArray` can always be converted to a native JavaScript `Array` using `NdArray#tolist()` method. 168 | 169 | 170 | Example: 171 | ```ts 172 | > a = nj.arange(15).reshape(3, 5); 173 | array([[ 0, 1, 2, 3, 4], 174 | [ 5, 6, 7, 8, 9], 175 | [ 10, 11, 12, 13, 14]]) 176 | 177 | > a.shape 178 | [ 3, 5] 179 | > a.ndim 180 | 2 181 | > a.dtype 182 | 'array' 183 | > a instanceof nj.NdArray 184 | true 185 | > a.tolist() instanceof Array 186 | true 187 | > a.get(1,1) 188 | 6 189 | > a.set(0,0,1) 190 | > a 191 | array([[ 1, 1, 2, 3, 4], 192 | [ 5, 6, 7, 8, 9], 193 | [ 10, 11, 12, 13, 14]]) 194 | 195 | ``` 196 | 197 | ### Printing arrays 198 | 199 | Use `nj.array([2,3,4]` as an example. In Node.js, `console.log(nj.array([2,3,4])` will print beautified content, `array([ 2, 3, 4])`. In browser or using debugger in Node.js, please use `console.log(nj.array([2,3,4].toString())` to print its beautified content. `toString()` is working in browser/Node.js. 200 | 201 | When you print the beautified content of an array, __NumJs__ displays it in a similar way to nested lists, but with the following layout: 202 | - the last axis is printed from left to right, 203 | - the second-to-last is printed from top to bottom, 204 | - the rest are also printed from top to bottom, with each slice separated from the next by an empty line. 205 | 206 | One-dimensional arrays are then printed as rows, bidimensionals as matrices and tridimensionals as lists of matrices. 207 | 208 | ```ts 209 | > const a = nj.arange(6); // 1d array 210 | > console.log(a); 211 | array([ 0, 1, 2, 3, 4, 5]) 212 | > 213 | > const b = nj.arange(12).reshape(4,3); // 2d array 214 | > console.log(b); 215 | array([[ 0, 1, 2], 216 | [ 3, 4, 5], 217 | [ 6, 7, 8], 218 | [ 9, 10, 11]]) 219 | > 220 | > const c = nj.arange(24).reshape(2,3,4); // 3d array 221 | > console.log(c); 222 | array([[[ 0, 1, 2, 3], 223 | [ 4, 5, 6, 7], 224 | [ 8, 9, 10, 11]], 225 | [[ 12, 13, 14, 15], 226 | [ 16, 17, 18, 19], 227 | [ 20, 21, 22, 23]]]) 228 | 229 | ``` 230 | 231 | If an array is too large to be printed, __NumJs__ automatically skips the central part of the array and only prints the corners: 232 | 233 | ```ts 234 | > console.log(nj.arange(10000).reshape(100,100)) 235 | array([[ 0, 1, ..., 98, 99], 236 | [ 100, 101, ..., 198, 199], 237 | ... 238 | [ 9800, 9801, ..., 9898, 9899], 239 | [ 9900, 9901, ..., 9998, 9999]]) 240 | ``` 241 | 242 | To customize this behaviour, you can change the printing options using `nj.config.printThreshold` (default is `7`): 243 | ```ts 244 | > nj.config.printThreshold = 9; 245 | > console.log(nj.arange(10000).reshape(100,100)) 246 | array([[ 0, 1, 2, 3, ..., 96, 97, 98, 99], 247 | [ 100, 101, 102, 103, ..., 196, 197, 198, 199], 248 | [ 200, 201, 202, 203, ..., 296, 297, 298, 299], 249 | [ 300, 301, 302, 303, ..., 396, 397, 398, 399], 250 | ... 251 | [ 9600, 9601, 9602, 9603, ..., 9696, 9697, 9698, 9699], 252 | [ 9700, 9701, 9702, 9703, ..., 9796, 9797, 9798, 9799], 253 | [ 9800, 9801, 9802, 9803, ..., 9896, 9897, 9898, 9899], 254 | [ 9900, 9901, 9902, 9903, ..., 9996, 9997, 9998, 9999]]) 255 | 256 | ``` 257 | 258 | ### Indexing 259 | 260 | Single element indexing uses `get` and `set` methods. It is 0-based, and accepts negative indices for indexing from the end of the array: 261 | ```ts 262 | > const a = nj.array([0,1,2]); 263 | > a.get(1) 264 | 1 265 | > 266 | > a.get(-1) 267 | 2 268 | > 269 | > const b = nj.arange(3*3).reshape(3,3); 270 | > b 271 | array([[ 0, 1, 2], 272 | [ 3, 4, 5], 273 | [ 6, 7, 8]) 274 | > 275 | > b.get(1, 1); 276 | 4 277 | > 278 | > b.get(-1, -1); 279 | 8 280 | > b.set(0,0,1); 281 | > b 282 | array([[ 1, 1, 2], 283 | [ 3, 4, 5], 284 | [ 6, 7, 8]]) 285 | ``` 286 | 287 | 288 | ### Slicing and Striding 289 | 290 | It is possible to slice and stride arrays to extract arrays of the same number of dimensions, but of different sizes than the original. The slicing and striding works exactly the same way it does in NumPy: 291 | 292 | ```ts 293 | > const a = nj.arange(5); 294 | > a 295 | array([ 0, 1, 2, 3, 4]) 296 | > 297 | > a.slice(1) // skip the first item, same as a[1:] 298 | array([ 1, 2, 3, 4]) 299 | > 300 | > a.slice(-3) // takes the last 3 items, same as a[-3:] 301 | array([ 2, 3, 4]) 302 | > 303 | > a.slice([4]) // takes the first 4 items, same as a[:4] 304 | array([ 0, 1, 2, 3]) 305 | > 306 | > a.slice([-2]) // skip the last 2 items, same as a[:-2] 307 | array([ 0, 1, 2]) 308 | > 309 | > a.slice([1,4]) // same as a[1:4] 310 | array([ 1, 2, 3]) 311 | > 312 | > a.slice([1,4,-1]) // same as a[1:4:-1] 313 | array([ 3, 2, 1]) 314 | > 315 | > a.slice([null,null,-1]) // same as a[::-1] 316 | array([ 4, 3, 2, 1, 0]) 317 | > 318 | > const b = nj.arange(5*5).reshape(5,5); 319 | > b 320 | array([[ 0, 1, 2, 3, 4], 321 | [ 5, 6, 7, 8, 9], 322 | [ 10, 11, 12, 13, 14], 323 | [ 15, 16, 17, 18, 19], 324 | [ 20, 21, 22, 23, 24]]) 325 | > 326 | > b.slice(1,2) // skip the first row and the 2 first columns, same as b[1:,2:] 327 | array([[ 7, 8, 9], 328 | [ 12, 13, 14], 329 | [ 17, 18, 19], 330 | [ 22, 23, 24]]) 331 | > 332 | > b.slice(null, [null, null, -1]) // reverse rows, same as b[:, ::-1] 333 | array([[ 4, 3, 2, 1, 0], 334 | [ 9, 8, 7, 6, 5], 335 | [ 14, 13, 12, 11, 10], 336 | [ 19, 18, 17, 16, 15], 337 | [ 24, 23, 22, 21, 20]]) 338 | ``` 339 | 340 | Note that slices do not copy the internal array data, it produces a new views of the original data. 341 | 342 | ### Basic operations 343 | 344 | Arithmetic operators such as `*` (`multiply`), `+` (`add`), `-` (`subtract`), `/` (`divide`), `**` (`pow`), `=` (`assign`) apply elemen-twise. A new array is created and filled with the result: 345 | 346 | ```ts 347 | > zeros = nj.zeros([3,4]); 348 | array([[ 0, 0, 0, 0], 349 | [ 0, 0, 0, 0], 350 | [ 0, 0, 0, 0]]) 351 | > 352 | > ones = nj.ones([3,4]); 353 | array([[ 1, 1, 1, 1], 354 | [ 1, 1, 1, 1], 355 | [ 1, 1, 1, 1]]) 356 | > 357 | > ones.add(ones) 358 | array([[ 2, 2, 2, 2], 359 | [ 2, 2, 2, 2], 360 | [ 2, 2, 2, 2]]) 361 | > 362 | > ones.subtract(ones) 363 | array([[ 0, 0, 0, 0], 364 | [ 0, 0, 0, 0], 365 | [ 0, 0, 0, 0]]) 366 | > 367 | > zeros.pow(zeros) 368 | array([[ 1, 1, 1, 1], 369 | [ 1, 1, 1, 1], 370 | [ 1, 1, 1, 1]]) 371 | > 372 | ``` 373 | 374 | To modify an existing array rather than create a new one you can set the `copy` parameter to `false`: 375 | 376 | ```ts 377 | > ones = nj.ones([3,4]); 378 | array([[ 1, 1, 1, 1], 379 | [ 1, 1, 1, 1], 380 | [ 1, 1, 1, 1]]) 381 | > 382 | > ones.add(ones, false) 383 | array([[ 2, 2, 2, 2], 384 | [ 2, 2, 2, 2], 385 | [ 2, 2, 2, 2]]) 386 | > 387 | > ones 388 | array([[ 2, 2, 2, 2], 389 | [ 2, 2, 2, 2], 390 | [ 2, 2, 2, 2]]) 391 | > 392 | > zeros = nj.zeros([3,4]) 393 | > zeros.slice([1,-1],[1,-1]).assign(1, false); 394 | > zeros 395 | array([[ 0, 0, 0, 0], 396 | [ 0, 1, 1, 0], 397 | [ 0, 0, 0, 0]]) 398 | ``` 399 | __Note__: available for `add`, `subtract`, `multiply`, `divide`, `assign` and `pow` methods. 400 | 401 | 402 | The matrix product can be performed using the `dot` function: 403 | 404 | ```ts 405 | > a = nj.arange(12).reshape(3,4); 406 | array([[ 0, 1, 2, 3], 407 | [ 4, 5, 6, 7], 408 | [ 8, 9, 10, 11]]) 409 | > 410 | > nj.dot(a.T, a) 411 | array([[ 80, 92, 104, 116], 412 | [ 92, 107, 122, 137], 413 | [ 104, 122, 140, 158], 414 | [ 116, 137, 158, 179]]) 415 | > 416 | > nj.dot(a, a.T) 417 | array([[ 14, 38, 62], 418 | [ 38, 126, 214], 419 | [ 62, 214, 366]]) 420 | ``` 421 | 422 | Many unary operations, such as computing the sum of all the elements in the array, are implemented as methods of the `NdArray` class: 423 | 424 | ```ts 425 | > a = nj.random([2,3]) 426 | array([[0.62755, 0.8278,0.21384], 427 | [ 0.7029,0.27584,0.46472]]) 428 | > a.sum() 429 | 3.1126488673035055 430 | > 431 | > a.min() 432 | 0.2138431086204946 433 | > 434 | > a.max() 435 | 0.8278025290928781 436 | > 437 | > a.mean() 438 | 0.5187748112172509 439 | > 440 | > a.std() 441 | 0.22216977543691244 442 | ``` 443 | 444 | ### Universal Functions 445 | __NumJs__ provides familiar mathematical functions such as `sin`, `cos`, and `exp`. These functions operate element-wise on an array, producing an `NdArray` as output: 446 | 447 | ```ts 448 | > a = nj.array([-1, 0, 1]) 449 | array([-1, 0, 1]) 450 | > 451 | > nj.negative(a) 452 | array([ 1, 0,-1]) 453 | > 454 | > nj.abs(a) 455 | array([ 1, 0, 1]) 456 | > 457 | > nj.exp(a) 458 | array([ 0.36788, 1, 2.71828]) 459 | > 460 | > nj.tanh(a) 461 | array([-0.76159, 0, 0.76159]) 462 | > 463 | > nj.softmax(a) 464 | array([ 0.09003, 0.24473, 0.66524]) 465 | > 466 | > nj.sigmoid(a) 467 | array([ 0.26894, 0.5, 0.73106]) 468 | > 469 | > nj.exp(a) 470 | array([ 0.36788, 1, 2.71828]) 471 | > 472 | > nj.log(nj.exp(a)) 473 | array([-1, 0, 1]) 474 | > 475 | > nj.sqrt(nj.abs(a)) 476 | array([ 1, 0, 1]) 477 | > 478 | > nj.sin(nj.arcsin(a)) 479 | array([-1, 0, 1]) 480 | > 481 | > nj.cos(nj.arccos(a)) 482 | array([-1, 0, 1]) 483 | > 484 | > nj.tan(nj.arctan(a)) 485 | array([-1, 0, 1]) 486 | ``` 487 | 488 | ### Shape Manipulation 489 | An array has a shape given by the number of elements along each axis: 490 | 491 | ```ts 492 | > a = nj.array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]); 493 | array([[ 0, 1, 2, 3], 494 | [ 4, 5, 6, 7], 495 | [ 8, 9, 10, 11]]) 496 | 497 | > a.shape 498 | [ 3, 4 ] 499 | ``` 500 | 501 | The shape of an array can be changed with constious commands: 502 | ```ts 503 | > a.flatten(); 504 | array([ 0, 1, 2, ..., 9, 10, 11]) 505 | > 506 | > a.T // equivalent to a.transpose(1,0) 507 | array([[ 0, 4, 8], 508 | [ 1, 5, 9], 509 | [ 2, 6, 10], 510 | [ 3, 7, 11]]) 511 | > 512 | > a.reshape(4,3) 513 | array([[ 0, 1, 2], 514 | [ 3, 4, 5], 515 | [ 6, 7, 8], 516 | [ 9, 10, 11]]) 517 | > 518 | ``` 519 | 520 | Since `a` is matrix we may want its diagonal: 521 | ```ts 522 | > nj.diag(a) 523 | array([ 0, 5, 10]) 524 | > 525 | ``` 526 | 527 | ### Identity matrix 528 | The identity array is a square array with ones on the main diagonal: 529 | 530 | ```ts 531 | > nj.identity(3) 532 | array([[ 1, 0, 0], 533 | [ 0, 1, 0], 534 | [ 0, 0, 1]]) 535 | ``` 536 | 537 | ### Concatenate different arrays 538 | 539 | Several arrays can be stacked together using `concatenate` function: 540 | 541 | ```ts 542 | > a = nj.arange(12).reshape(3,4) 543 | array([[ 0, 1, 2, 3], 544 | [ 4, 5, 6, 7], 545 | [ 8, 9, 10, 11]]) 546 | > 547 | > b = nj.arange(3) 548 | array([ 0, 1, 2]) 549 | > 550 | > nj.concatenate(a,b.reshape(3,1)) 551 | array([[ 0, 1, 2, 3, 0], 552 | [ 4, 5, 6, 7, 1], 553 | [ 8, 9, 10, 11, 2]]) 554 | ``` 555 | 556 | __Notes__: 557 | - the arrays must have the same shape, except in the last dimension 558 | - arrays are concatenated along the last axis 559 | 560 | It is still possible to concatenate along other dimensions using transpositions: 561 | 562 | ```ts 563 | > a = nj.arange(12).reshape(3,4) 564 | array([[ 0, 1, 2, 3], 565 | [ 4, 5, 6, 7], 566 | [ 8, 9, 10, 11]]) 567 | > 568 | > b = nj.arange(4) 569 | array([ 0, 1, 2, 3]) 570 | > 571 | > nj.concatenate(a.T,b.reshape(4,1)).T 572 | array([[ 0, 1, 2, 3], 573 | [ 4, 5, 6, 7], 574 | [ 8, 9, 10, 11], 575 | [ 0, 1, 2, 3]]) 576 | ``` 577 | 578 | 579 | ### Stack multiple arrays 580 | 581 | ```ts 582 | > a = nj.array([1, 2, 3]) 583 | > b = nj.array([2, 3, 4]) 584 | 585 | > nj.stack([a, b]) 586 | array([[1, 2, 3], 587 | [2, 3, 4]]) 588 | > nj.stack([a, b], -1) 589 | array([[1, 2], 590 | [2, 3], 591 | [3, 4]]) 592 | ``` 593 | 594 | __Notes__: 595 | - the arrays must have the same shape 596 | - take an optional axis argument which can be negative 597 | 598 | ### Deep Copy 599 | The `clone` method makes a complete copy of the array and its data. 600 | 601 | ```ts 602 | > a = nj.arange(12).reshape(3,4) 603 | array([[ 0, 1, 2, 3], 604 | [ 4, 5, 6, 7], 605 | [ 8, 9, 10, 11]]) 606 | > 607 | > b = a.clone() 608 | array([[ 0, 1, 2, 3], 609 | [ 4, 5, 6, 7], 610 | [ 8, 9, 10, 11]]) 611 | > 612 | > a === b 613 | false 614 | > 615 | > a.set(0,0,1) 616 | > a 617 | array([[ 1, 1, 2, 3], 618 | [ 4, 5, 6, 7], 619 | [ 8, 9, 10, 11]]) 620 | > b 621 | array([[ 0, 1, 2, 3], 622 | [ 4, 5, 6, 7], 623 | [ 8, 9, 10, 11]]) 624 | ``` 625 | 626 | ### Fast Fourier Transform (FFT) 627 | `fft` and `ifft` functions can be used to compute the N-dimensional discrete Fourier Transform and its inverse. 628 | 629 | Example: 630 | ```ts 631 | > RI = nj.concatenate(nj.ones([10,1]), nj.zeros([10,1])) 632 | array([[ 1, 0], 633 | [ 1, 0], 634 | [ 1, 0], 635 | ... 636 | [ 1, 0], 637 | [ 1, 0], 638 | [ 1, 0]]) 639 | > 640 | > fft = nj.fft(RI) 641 | array([[ 10, 0], 642 | [ 0, 0], 643 | [ 0, 0], 644 | ... 645 | [ 0, 0], 646 | [ 0, 0], 647 | [ 0, 0]]) 648 | > 649 | > nj.ifft(fft) 650 | array([[ 1, 0], 651 | [ 1, 0], 652 | [ 1, 0], 653 | ... 654 | [ 1, 0], 655 | [ 1, 0], 656 | [ 1, 0]]) 657 | ``` 658 | __Note__: both `fft` and `ifft` expect last dimension of the array to contain 2 values: the real and the imaginary value 659 | 660 | 661 | ### Convolution 662 | 663 | `convolve` function compute the discrete, linear convolution of two multi-dimensional arrays. 664 | 665 | __Note__: The convolution product is only given for points where the signals overlap completely. Values outside the signal boundary have no effect. This behaviour is also known as the 'valid' mode. 666 | 667 | 668 | Example: 669 | ```ts 670 | > x = nj.array([0,0,1,2,1,0,0]) 671 | array([ 0, 0, 1, 2, 1, 0, 0]) 672 | > 673 | > nj.convolve(x, [-1,0,1]) 674 | array([-1,-2, 0, 2, 1]) 675 | > 676 | > const a = nj.arange(25).reshape(5,5) 677 | > a 678 | array([[ 0, 1, 2, 3, 4], 679 | [ 5, 6, 7, 8, 9], 680 | [ 10, 11, 12, 13, 14], 681 | [ 15, 16, 17, 18, 19], 682 | [ 20, 21, 22, 23, 24]]) 683 | > nj.convolve(a, [[ 1, 2, 1], [ 0, 0, 0], [-1,-2,-1]]) 684 | array([[ 40, 40, 40], 685 | [ 40, 40, 40], 686 | [ 40, 40, 40]]) 687 | > nj.convolve(nj.convolve(a, [[1, 2, 1]]), [[1],[0],[-1]]) 688 | array([[ 40, 40, 40], 689 | [ 40, 40, 40], 690 | [ 40, 40, 40]]) 691 | ``` 692 | 693 | __Note__: `convolve` uses Fast Fourier Transform (FFT) to speed up computation on large arrays. 694 | 695 | 696 | ### Other utils 697 | `rot90` 698 | ```ts 699 | > m = nj.array([[1,2],[3,4]], 'int8') 700 | > m 701 | array([[1, 2], 702 | [3, 4]]) 703 | > nj.rot90(m) 704 | array([[2, 4], 705 | [1, 3]]) 706 | > nj.rot90(m, 2) 707 | array([[4, 3], 708 | [2, 1]]) 709 | > m = nj.arange(8).reshape([2,2,2]) 710 | > nj.rot90(m, 1, [1,2]) 711 | array([[[1, 3], 712 | [0, 2]], 713 | [[5, 7], 714 | [4, 6]]]) 715 | ``` 716 | 717 | `mod` (since v0.16.0) 718 | ```ts 719 | > nj.mod(nj.arange(7), 5) 720 | > m 721 | array([0, 1, 2, 3, 4, 0, 1]) 722 | ``` 723 | 724 | ## Documentation 725 | - [@d4c/numjs](https://grimmer0125.github.io/numjs/) 726 | - [numjs module (nj) doc](https://grimmer0125.github.io/numjs/modules/) 727 | - [NdArray doc](https://grimmer0125.github.io/numjs/classes/ndarray.NdArray.html) 728 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@d4c/numjs", 3 | "version": "0.17.34", 4 | "description": "Like NumPy, in TypeScript and JavaScript", 5 | "source": "src/index.ts", 6 | "main": "build/main/index.js", 7 | "typings": "build/main/index.d.ts", 8 | "module": "build/module/index.js", 9 | "targets": { 10 | "main": false, 11 | "module": { 12 | "context": "browser", 13 | "isLibrary": true, 14 | "sourceMap": false, 15 | "outputFormat": "esmodule", 16 | "includeNodeModules": true 17 | }, 18 | "types": false 19 | }, 20 | "repository": "https://github.com/grimmer0125/numjs", 21 | "homepage": "https://grimmer0125.github.io/numjs", 22 | "license": "MIT", 23 | "dependencies": { 24 | "@types/ndarray": "^1.0.11", 25 | "cwise": "^1.0.10", 26 | "ndarray": "^1.0.19", 27 | "ndarray-fft": "^1.0.3", 28 | "ndarray-gemm": "^1.0.0", 29 | "ndarray-ops": "^1.2.2", 30 | "typedarray-pool": "^1.2.0" 31 | }, 32 | "devDependencies": { 33 | "@ava/typescript": "^2.0.0", 34 | "@istanbuljs/nyc-config-typescript": "^1.0.1", 35 | "@types/chai": "^4.2.21", 36 | "@types/mocha": "^9.0.0", 37 | "@types/node": "^16.7.8", 38 | "ava": "^3.15.0", 39 | "chai": "^4.3.4", 40 | "codecov": "^3.8.3", 41 | "gh-pages": "^3.2.3", 42 | "mocha": "^9.1.3", 43 | "npm-run-all": "^4.1.5", 44 | "nyc": "^15.1.0", 45 | "parcel": "^2.0.1", 46 | "ts-node": "^10.2.1", 47 | "typedoc": "^0.22.7", 48 | "typescript": "^4.4.2" 49 | }, 50 | "scripts": { 51 | "build": "run-s build:parcel build:main build:module", 52 | "build:main": "tsc -p tsconfig.json", 53 | "build:module": "tsc -p tsconfig.module.json", 54 | "build:parcel": "parcel build && mv build/module/index.js build/module/numjs.min.js", 55 | "cov:html": "nyc report --reporter=html", 56 | "doc:html": "typedoc src/lib/index.ts src/lib/ndarray.ts src/lib/errors.ts --out build/docs", 57 | "doc:publish": "touch build/docs/.nojekyll && gh-pages -m \"[ci skip] Updates\" -d build/docs -t -r https://github.com/grimmer0125/numjs.git", 58 | "buildtest": "run-s build test:*", 59 | "test": "mocha --require ts-node/register test/mocha/*.spec.ts", 60 | "cov": "nyc mocha --require ts-node/register test/mocha/*.spec.ts", 61 | "postversion": "npm publish" 62 | }, 63 | "engines": { 64 | "node": ">=12" 65 | }, 66 | "keywords": [ 67 | "numpy", 68 | "ndarray", 69 | "array", 70 | "tensor", 71 | "matrix", 72 | "linear", 73 | "algebra", 74 | "multidimensional", 75 | "typescript", 76 | "numerical", 77 | "computing", 78 | "stride", 79 | "shape", 80 | "science", 81 | "javascript", 82 | "node", 83 | "browser", 84 | "multi", 85 | "dimension", 86 | "volume" 87 | ], 88 | "files": [ 89 | "build/main", 90 | "build/module", 91 | "!**/*.spec.*", 92 | "!**/*.json", 93 | "CHANGELOG.md", 94 | "LICENSE", 95 | "README.md" 96 | ], 97 | "ava": { 98 | "failFast": true, 99 | "timeout": "60s", 100 | "typescript": { 101 | "rewritePaths": { 102 | "src/": "build/main/" 103 | } 104 | }, 105 | "files": [ 106 | "!build/module/**" 107 | ] 108 | }, 109 | "config": { 110 | "commitizen": { 111 | "path": "cz-conventional-changelog" 112 | } 113 | }, 114 | "nyc": { 115 | "extends": "@istanbuljs/nyc-config-typescript", 116 | "exclude": [ 117 | "**/*.spec.js" 118 | ] 119 | } 120 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as nj from "./lib"; 2 | export default nj; 3 | export { NdArray, ndarray } from "./lib"; 4 | -------------------------------------------------------------------------------- /src/lib/config.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export default { 4 | printThreshold: 7, 5 | nFloatingValues: 5, 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/dtypes.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export default { 4 | int8: Int8Array, 5 | int16: Int16Array, 6 | int32: Int32Array, 7 | uint8: Uint8Array, 8 | uint16: Uint16Array, 9 | uint32: Uint32Array, 10 | float32: Float32Array, 11 | float64: Float64Array, 12 | uint8_clamped: Uint8ClampedArray, 13 | array: Array, 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/errors.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export class ValueError extends Error {} 4 | export class ConfigError extends Error {} 5 | export class NotImplementedError extends Error {} 6 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is default exported `nj` module page. Below References, Namespaces, Properties, Functions are all exported. 3 | * For example, after import `nj` via `import nj from "@d4c/numjs";` or `const nj = require('@d4c/numjs').default;`, 4 | * you can use `nj.array` to use create a `NdArray`. 5 | * 6 | * @packageDocumentation 7 | */ 8 | "use strict"; 9 | 10 | import cwise from "cwise"; 11 | import ops from "ndarray-ops"; 12 | import ndFFT from "ndarray-fft"; 13 | 14 | export { default as config } from "./config"; 15 | export { default as dtypes } from "./dtypes"; 16 | export { default as ndarray } from "ndarray"; 17 | import { 18 | NdArray, 19 | ArbDimNumArray, 20 | ArrayLikeConstructor, 21 | DType, 22 | } from "./ndarray"; 23 | export { NdArray }; 24 | import * as errors from "./errors"; 25 | export { errors }; 26 | 27 | import _ from "./utils"; 28 | 29 | export function broadcast(shape1: number[], shape2: number[]) { 30 | if (shape1.length === 0 || shape2.length === 0) { 31 | return; 32 | } 33 | const reversed1 = shape1.slice().reverse(); 34 | const reversed2 = shape2.slice().reverse(); 35 | const maxLength = Math.max(shape1.length, shape2.length); 36 | const outShape: number[] = new Array(maxLength); 37 | for (let i = 0; i < maxLength; i++) { 38 | if (!reversed1[i] || reversed1[i] === 1) { 39 | outShape[i] = reversed2[i]; 40 | } else if (!reversed2[i] || reversed2[i] === 1) { 41 | outShape[i] = reversed1[i]; 42 | } else if (reversed1[i] === reversed2[i]) { 43 | outShape[i] = reversed1[i]; 44 | } else { 45 | return; 46 | } 47 | } 48 | return outShape.reverse(); 49 | } 50 | 51 | /** 52 | * Add arguments, element-wise. 53 | */ 54 | export function add( 55 | a: NdArray | ArbDimNumArray | number, 56 | b: NdArray | ArbDimNumArray | number 57 | ): NdArray { 58 | return NdArray.new(a).add(b); 59 | } 60 | 61 | /** 62 | * Multiply arguments, element-wise. 63 | */ 64 | export function multiply( 65 | a: ArbDimNumArray | NdArray, 66 | b: ArbDimNumArray | NdArray | number 67 | ): NdArray { 68 | return NdArray.new(a).multiply(b); 69 | } 70 | 71 | /** 72 | * Divide `a` by `b`, element-wise. 73 | */ 74 | export function divide( 75 | a: ArbDimNumArray | NdArray, 76 | b: ArbDimNumArray | NdArray | number 77 | ) { 78 | return NdArray.new(a).divide(b); 79 | } 80 | 81 | /** 82 | * Subtract second argument from the first, element-wise. 83 | */ 84 | export function subtract( 85 | a: ArbDimNumArray | NdArray | number, 86 | b: ArbDimNumArray | NdArray | number 87 | ): NdArray { 88 | return NdArray.new(a).subtract(b); 89 | } 90 | 91 | /** 92 | * Return true if two arrays have the same shape and elements, false otherwise. 93 | */ 94 | export function equal( 95 | array1: ArbDimNumArray | NdArray, 96 | array2: ArbDimNumArray | NdArray 97 | ): boolean { 98 | return NdArray.new(array1).equal(array2); 99 | } 100 | 101 | /** 102 | * Return a copy of the array collapsed into one dimension using row-major order (C-style) 103 | */ 104 | export function flatten(array: ArbDimNumArray | NdArray): NdArray { 105 | return NdArray.new(array).flatten(); 106 | } 107 | 108 | /** 109 | * Gives a new shape to an array without changing its data. 110 | * @param array 111 | * @param shape - The new shape should be compatible with the original shape. If an integer, then the result will be a 1-D array of that length 112 | */ 113 | export function reshape( 114 | array: ArbDimNumArray | NdArray, 115 | shape: number[] | number 116 | ): NdArray { 117 | // TypeScript is not smart enought on parameters detection on overloading 118 | // workaround way 119 | if (typeof shape == "number") { 120 | return NdArray.new(array).reshape(shape); 121 | } else { 122 | return NdArray.new(array).reshape(shape); 123 | } 124 | } 125 | 126 | /** 127 | * Calculate the exponential of all elements in the input array, element-wise. 128 | */ 129 | export function exp(x: ArbDimNumArray | NdArray | number): NdArray { 130 | return NdArray.new(x).exp(); 131 | } 132 | 133 | /** 134 | * Calculate the natural logarithm of all elements in the input array, element-wise. 135 | */ 136 | export function log(x: ArbDimNumArray | NdArray | number): NdArray { 137 | return NdArray.new(x).log(); 138 | } 139 | 140 | /** 141 | * Calculate the positive square-root of all elements in the input array, element-wise. 142 | */ 143 | export function sqrt(x: ArbDimNumArray | NdArray | number): NdArray { 144 | return NdArray.new(x).sqrt(); 145 | } 146 | 147 | /** 148 | * Raise first array elements to powers from second array, element-wise. 149 | */ 150 | export function power( 151 | x1: ArbDimNumArray | NdArray | number, 152 | x2: ArbDimNumArray | NdArray | number 153 | ): NdArray { 154 | return NdArray.new(x1).pow(x2); 155 | } 156 | 157 | /** 158 | * Return the sum of input array elements. 159 | */ 160 | export function sum(x: ArbDimNumArray | NdArray | number): number { 161 | return NdArray.new(x).sum(); 162 | } 163 | 164 | /** 165 | * Return the arithmetic mean of input array elements. 166 | */ 167 | export function mean(x: ArbDimNumArray | NdArray | number): number { 168 | return NdArray.new(x).mean(); 169 | } 170 | 171 | /** 172 | * Returns the standard deviation, a measure of the spread of a distribution, of the input array elements. 173 | */ 174 | export function std( 175 | x: ArbDimNumArray | NdArray | number, 176 | options?: { ddof: number } 177 | ): number { 178 | return NdArray.new(x).std(options); 179 | } 180 | 181 | /** 182 | * Return the minimum value of the array 183 | */ 184 | export function min(x: ArbDimNumArray | NdArray | number): number { 185 | return NdArray.new(x).min(); 186 | } 187 | 188 | /** 189 | * Return the maximum value of the array 190 | */ 191 | export function max(x: ArbDimNumArray | NdArray | number): number { 192 | return NdArray.new(x).max(); 193 | } 194 | 195 | /** 196 | * Return element-wise remainder of division. 197 | * Computes the remainder complementary to the `floor` function. It is equivalent to the Javascript modulus operator``x1 % x2`` and has the same sign as the divisor x2. 198 | */ 199 | export function mod( 200 | x1: NdArray | ArbDimNumArray | number, 201 | x2: NdArray | ArbDimNumArray | number 202 | ): NdArray { 203 | return NdArray.new(x1).mod(x2); 204 | } 205 | 206 | /** 207 | * Permute the dimensions of the input array according to the given axes. 208 | */ 209 | export function transpose( 210 | x: NdArray | ArbDimNumArray | number, 211 | axes?: number[] 212 | ): NdArray { 213 | return NdArray.new(x).transpose(axes); 214 | } 215 | 216 | /** 217 | * Return the inverse of the input array, element-wise. 218 | */ 219 | export function negative(x: ArbDimNumArray | NdArray | number): NdArray { 220 | return NdArray.new(x).negative(); 221 | } 222 | 223 | /** 224 | * Return evenly spaced values within a given interval. 225 | * 226 | * @param start - Default 0 (int). Start of interval. The interval includes this value. 227 | * @param stop - End of interval (int). The interval does not include this value. 228 | * @param step - Default 1 (int). Spacing between values. The default step size is 1. If step is specified, start must also be given. 229 | * @param dtype Defaut is "array". The type of the output array. Either string (e.g. 'uint8') or a Constructor function (e.g. Uint8Array). 230 | */ 231 | export function arange( 232 | start: number, 233 | stop: number, 234 | step: number, 235 | dtype: DType | ArrayLikeConstructor 236 | ): NdArray; 237 | export function arange(start: number, stop: number, step: number): NdArray; 238 | export function arange( 239 | start: number, 240 | stop: number, 241 | dtype: DType | ArrayLikeConstructor 242 | ): NdArray; 243 | export function arange(start: number, stop: number): NdArray; 244 | export function arange( 245 | stop: number, 246 | dtype: DType | ArrayLikeConstructor 247 | ): NdArray; 248 | export function arange(stop: number): NdArray; 249 | export function arange(...args: any[]): NdArray { 250 | if (arguments.length === 1) { 251 | return arange(0, arguments[0], 1, undefined); 252 | } else if (arguments.length === 2 && _.isNumber(arguments[1])) { 253 | return arange(arguments[0], arguments[1], 1, undefined); 254 | } else if (arguments.length === 2) { 255 | return arange( 256 | 0, 257 | arguments[0], 258 | 1, 259 | arguments[1] as DType | ArrayLikeConstructor 260 | ); 261 | } else if (arguments.length === 3 && !_.isNumber(arguments[2])) { 262 | return arange( 263 | arguments[0], 264 | arguments[1], 265 | 1, 266 | arguments[2] as DType | ArrayLikeConstructor 267 | ); 268 | } 269 | 270 | let start: number = arguments[0]; 271 | const stop: number = arguments[1]; 272 | const step: number = arguments[2]; 273 | const dtype: DType | ArrayLikeConstructor = arguments[3]; 274 | 275 | const result = []; 276 | let i = 0; 277 | while (start < stop) { 278 | result[i++] = start; 279 | start += step; 280 | } 281 | return NdArray.new(result, dtype); 282 | } 283 | 284 | /** 285 | * Return a new array of given shape and type, filled with zeros. 286 | * 287 | * @param shape - Shape of the new array, e.g., [2, 3] or 2. 288 | * @param dtype Defaut is "array". The type of the output array. E.g., 'uint8' or Uint8Array. 289 | * 290 | */ 291 | export function zeros( 292 | shape: number | number[], 293 | dtype?: DType | ArrayLikeConstructor 294 | ): NdArray { 295 | if (_.isNumber(shape) && shape >= 0) { 296 | shape = [shape as number]; 297 | } 298 | const s = _.shapeSize(shape); 299 | const T = _.getType(dtype); 300 | const arr = new NdArray(new T(s), shape as number[]); 301 | if (arr.dtype === "array") { 302 | ops.assigns(arr.selection, 0); 303 | } 304 | return arr; 305 | } 306 | 307 | /** 308 | * Return a new array of given shape and type, filled with ones. 309 | * 310 | * @param shape - Shape of the new array, e.g., [2, 3] or 2. 311 | * @param dtype - Defaut is "array". The type of the output array. E.g., 'uint8' or Uint8Array. 312 | * 313 | * @return Array of ones with the given shape and dtype 314 | */ 315 | export function ones( 316 | shape: number[] | number, 317 | dtype?: DType | ArrayLikeConstructor 318 | ): NdArray { 319 | if (_.isNumber(shape) && shape >= 0) { 320 | shape = [shape as number]; 321 | } 322 | const s = _.shapeSize(shape); 323 | const T = _.getType(dtype); 324 | const arr = new NdArray(new T(s), shape as number[]); 325 | ops.assigns(arr.selection, 1); 326 | return arr; 327 | } 328 | 329 | /** 330 | * Return a new array of given shape and type, filled with `undefined` values. 331 | * 332 | * @param shape - Shape of the new array, e.g., [2, 3] or 2. 333 | * @param dtype - Defaut is "array". The type of the output array. E.g., 'uint8' or Uint8Array. 334 | * 335 | * @return Array of `undefined` values with the given shape and dtype 336 | */ 337 | export function empty( 338 | shape: number[] | number, 339 | dtype?: DType | ArrayLikeConstructor 340 | ): NdArray { 341 | if (_.isNumber(shape) && shape >= 0) { 342 | shape = [shape as number]; 343 | } 344 | const s = _.shapeSize(shape); 345 | const T = _.getType(dtype); 346 | return new NdArray(new T(s), shape as number[]); 347 | } 348 | 349 | /** 350 | * Create an array of the given shape and propagate it with random samples from a uniform distribution over [0, 1]. 351 | * @param shape - The dimensions of the returned array, should all be positive integers 352 | */ 353 | export function random(...shape: number[]): NdArray; 354 | export function random(shape?: number | number[]): NdArray; 355 | export function random(...args: any[]): NdArray { 356 | let shape; 357 | if (arguments.length === 0) { 358 | return NdArray.new(Math.random()); 359 | } else if (arguments.length === 1) { 360 | shape = _.isNumber(args[0]) ? [(args[0] as number) | 0] : args[0]; 361 | } else { 362 | shape = [].slice.call(arguments); 363 | } 364 | const s = _.shapeSize(shape); 365 | const arr = new NdArray(new Float64Array(s), shape); 366 | ops.random(arr.selection); 367 | return arr; 368 | } 369 | 370 | /** 371 | * Return the softmax, or normalized exponential, of the input array, element-wise. 372 | */ 373 | export function softmax(x: ArbDimNumArray | NdArray | number): NdArray { 374 | const e = NdArray.new(x).exp(); 375 | const se = e.sum(); // scalar 376 | ops.divseq(e.selection, se); 377 | return e; 378 | } 379 | 380 | /* istanbul ignore next */ 381 | const doSigmoid = cwise({ 382 | args: ["array", "scalar"], 383 | body: function sigmoidCwise(a, t) { 384 | a = a < -30 ? 0 : a > 30 ? 1 : 1 / (1 + Math.exp(-1 * t * a)); 385 | }, 386 | }); 387 | 388 | /** 389 | * Return the sigmoid of the input array, element-wise. 390 | * @param x 391 | * @param t - stifness parameter 392 | */ 393 | export function sigmoid(x: ArbDimNumArray | NdArray | number, t = 1): NdArray { 394 | x = NdArray.new(x).clone(); 395 | t = t || 1; 396 | doSigmoid(x.selection, t); 397 | return x; 398 | } 399 | 400 | /* istanbul ignore next */ 401 | const doClip = cwise({ 402 | args: ["array", "scalar", "scalar"], 403 | body: function clipCwise(a, min, max) { 404 | a = Math.min(Math.max(min, a), max); 405 | }, 406 | }); 407 | 408 | /** 409 | * Clip (limit) the values in an array between min and max, element-wise. 410 | */ 411 | export function clip( 412 | x: ArbDimNumArray | NdArray | number, 413 | min = 0, 414 | max = 1 415 | ): NdArray { 416 | if (arguments.length === 1) { 417 | min = 0; 418 | max = 1; 419 | } else if (arguments.length === 2) { 420 | max = 1; 421 | } 422 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 423 | doClip(s.selection, min, max); 424 | return s; 425 | } 426 | 427 | const doLeakyRelu = cwise({ 428 | args: ["array", "scalar"], 429 | body: function leakyReluCwise(xi, alpha) { 430 | xi = Math.max(alpha * xi, xi); 431 | }, 432 | }); 433 | 434 | export function leakyRelu( 435 | x: NdArray | ArbDimNumArray | number, 436 | alpha?: number 437 | ): NdArray { 438 | alpha = alpha || 1e-3; 439 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 440 | doLeakyRelu(s.selection, alpha); 441 | return s; 442 | } 443 | 444 | /* istanbul ignore next */ 445 | const doTanh = cwise({ 446 | args: ["array"], 447 | body: function tanhCwise(xi) { 448 | xi = (Math.exp(2 * xi) - 1) / (Math.exp(2 * xi) + 1); 449 | }, 450 | }); 451 | 452 | /** 453 | * Return hyperbolic tangent of the input array, element-wise. 454 | */ 455 | export function tanh(x: ArbDimNumArray | NdArray | number): NdArray { 456 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 457 | doTanh(s.selection); 458 | return s; 459 | } 460 | 461 | /** 462 | * Return absolute value of the input array, element-wise. 463 | */ 464 | export function abs(x: ArbDimNumArray | NdArray | number): NdArray { 465 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 466 | ops.abseq(s.selection); 467 | return s; 468 | } 469 | 470 | /** 471 | * Return trigonometric cosine of the input array, element-wise. 472 | */ 473 | export function cos(x: ArbDimNumArray | NdArray | number): NdArray { 474 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 475 | ops.coseq(s.selection); 476 | return s; 477 | } 478 | 479 | /** 480 | * Return trigonometric inverse cosine of the input array, element-wise. 481 | */ 482 | export function arccos(x: ArbDimNumArray | NdArray | number): NdArray { 483 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 484 | ops.acoseq(s.selection); 485 | return s; 486 | } 487 | 488 | /** 489 | * Return trigonometric sine of the input array, element-wise. 490 | */ 491 | export function sin(x: ArbDimNumArray | NdArray | number): NdArray { 492 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 493 | ops.sineq(s.selection); 494 | return s; 495 | } 496 | 497 | /** 498 | * Return trigonometric inverse sine of the input array, element-wise. 499 | */ 500 | export function arcsin(x: ArbDimNumArray | NdArray | number): NdArray { 501 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 502 | ops.asineq(s.selection); 503 | return s; 504 | } 505 | 506 | /** 507 | * Return trigonometric tangent of the input array, element-wise. 508 | */ 509 | export function tan(x: ArbDimNumArray | NdArray | number): NdArray { 510 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 511 | ops.taneq(s.selection); 512 | return s; 513 | } 514 | 515 | /** 516 | * Return trigonometric inverse tangent of the input array, element-wise. 517 | */ 518 | export function arctan(x: ArbDimNumArray | NdArray | number): NdArray { 519 | const s = x instanceof NdArray ? x.clone() : NdArray.new(x); 520 | ops.ataneq(s.selection); 521 | return s; 522 | } 523 | 524 | /** 525 | * Dot product of two arrays. 526 | * 527 | * WARNING: supported products are: 528 | * - matrix dot matrix 529 | * - vector dot vector 530 | * - matrix dot vector 531 | * - vector dot matrix 532 | */ 533 | export function dot( 534 | a: ArbDimNumArray | NdArray, 535 | b: ArbDimNumArray | NdArray 536 | ): NdArray { 537 | return NdArray.new(a).dot(b); 538 | } 539 | 540 | /** 541 | * Join given arrays along the last axis. 542 | */ 543 | export function concatenate( 544 | ...arrays: Array 545 | ): NdArray; 546 | export function concatenate( 547 | arrays: Array 548 | ): NdArray; 549 | export function concatenate(...args: any[]): NdArray { 550 | let arrays; 551 | if (args.length > 1) { 552 | arrays = [].slice.call(args); 553 | } else { 554 | arrays = args[0]; 555 | } 556 | let i, a; 557 | for (i = 0; i < arrays.length; i++) { 558 | a = arrays[i]; 559 | arrays[i] = a instanceof NdArray ? a.tolist() : _.isNumber(a) ? [a] : a; 560 | } 561 | let m = arrays[0]; 562 | for (i = 1; i < arrays.length; i++) { 563 | a = arrays[i]; 564 | const mShape = _.getShape(m); 565 | const aShape = _.getShape(a); 566 | if (mShape.length !== aShape.length) { 567 | throw new errors.ValueError( 568 | "all the input arrays must have same number of dimensions" 569 | ); 570 | } else if (mShape.length === 1 && aShape.length === 1) { 571 | m = m.concat(a); 572 | } else if ( 573 | (mShape.length === 2 && aShape.length === 2 && mShape[0] === aShape[0]) || 574 | (mShape.length === 1 && aShape.length === 2 && mShape[0] === aShape[0]) || 575 | (mShape.length === 2 && aShape.length === 1 && mShape[0] === aShape[0]) 576 | ) { 577 | for (let row = 0; row < mShape[0]; row++) { 578 | m[row] = m[row].concat(a[row]); 579 | } 580 | } else if ( 581 | (mShape.length === 3 && 582 | aShape.length === 3 && 583 | mShape[0] === aShape[0] && 584 | mShape[1] === aShape[1]) || 585 | (mShape.length === 2 && 586 | aShape.length === 3 && 587 | mShape[0] === aShape[0] && 588 | mShape[1] === aShape[1]) || 589 | (mShape.length === 3 && 590 | aShape.length === 2 && 591 | mShape[0] === aShape[0] && 592 | mShape[1] === aShape[1]) 593 | ) { 594 | for (let rowI = 0; rowI < mShape[0]; rowI++) { 595 | const rowV = new Array(mShape[1]); 596 | for (let colI = 0; colI < mShape[1]; colI++) { 597 | rowV[colI] = m[rowI][colI].concat(a[rowI][colI]); 598 | } 599 | m[rowI] = rowV; 600 | } 601 | } else { 602 | throw new errors.ValueError( 603 | 'cannot concatenate "' + mShape + '" with "' + aShape + '"' 604 | ); 605 | } 606 | } 607 | return NdArray.new(m, arrays[0].dtype); 608 | } 609 | 610 | /** 611 | * Round an array to the to the nearest integer. 612 | */ 613 | export function round(x: ArbDimNumArray | NdArray): NdArray { 614 | return NdArray.new(x).round(); 615 | } 616 | 617 | /** 618 | * Convolve 2 N-dimensionnal arrays 619 | * 620 | * @note: Arrays must have the same dimensions and a must be greater than b. 621 | * @note: The convolution product is only given for points where the signals overlap completely. Values outside the signal boundary have no effect. This behaviour is known as the 'valid' mode. 622 | */ 623 | export function convolve( 624 | a: ArbDimNumArray | NdArray, 625 | b: ArbDimNumArray | NdArray 626 | ): NdArray { 627 | return NdArray.new(a).convolve(b); 628 | } 629 | 630 | /** 631 | * Convolve 2 N-dimensionnal arrays using Fast Fourier Transform (FFT) 632 | * 633 | * @note: Arrays must have the same dimensions and a must be greater than b. 634 | * @note: The convolution product is only given for points where the signals overlap completely. Values outside the signal boundary have no effect. This behaviour is known as the 'valid' mode. 635 | */ 636 | export function fftconvolve( 637 | a: ArbDimNumArray | NdArray, 638 | b: ArbDimNumArray | NdArray 639 | ): NdArray { 640 | return NdArray.new(a).fftconvolve(b); 641 | } 642 | 643 | export function fft(x: ArbDimNumArray | NdArray): NdArray { 644 | x = x instanceof NdArray ? x.clone() : NdArray.new(x); 645 | const xShape = x.shape; 646 | const d = xShape.length; 647 | if (xShape[d - 1] !== 2) { 648 | throw new errors.ValueError( 649 | "expect last dimension of the array to have 2 values (for both real and imaginary part)" 650 | ); 651 | } 652 | let rPicker = new Array(d); 653 | let iPicker = new Array(d); 654 | rPicker[d - 1] = 0; 655 | iPicker[d - 1] = 1; 656 | ndFFT( 657 | 1, 658 | x.selection.pick.apply(x.selection, rPicker), 659 | x.selection.pick.apply(x.selection, iPicker) 660 | ); 661 | return x; 662 | } 663 | 664 | export function ifft(x: ArbDimNumArray | NdArray): NdArray { 665 | x = x instanceof NdArray ? x.clone() : NdArray.new(x); 666 | const xShape = x.shape; 667 | const d = xShape.length; 668 | if (xShape[d - 1] !== 2) { 669 | throw new errors.ValueError( 670 | "expect last dimension of the array to have 2 values (for both real and imaginary part)" 671 | ); 672 | } 673 | let rPicker = new Array(d); 674 | let iPicker = new Array(d); 675 | rPicker[d - 1] = 0; 676 | iPicker[d - 1] = 1; 677 | ndFFT( 678 | -1, 679 | x.selection.pick.apply(x.selection, rPicker), 680 | x.selection.pick.apply(x.selection, iPicker) 681 | ); 682 | return x; 683 | } 684 | 685 | /** 686 | * Extract a diagonal or construct a diagonal array. 687 | * @returns a view a of the original array when possible, a new array otherwise 688 | */ 689 | export function diag(x: ArbDimNumArray | NdArray): NdArray { 690 | return NdArray.new(x).diag(); 691 | } 692 | 693 | /** 694 | * The identity array is a square array with ones on the main diagonal. 695 | * @param n number of rows (and columns) in n x n output. 696 | * @param dtype Defaut is "array". The type of the output array. E.g., 'uint8' or Uint8Array. 697 | * @return n x n array with its main diagonal set to one, and all other elements 0 698 | */ 699 | export function identity( 700 | n: number, 701 | dtype?: DType | ArrayLikeConstructor 702 | ): NdArray { 703 | const arr = zeros([n, n], dtype); 704 | for (let i = 0; i < n; i++) arr.set(i, i, 1); 705 | return arr; 706 | } 707 | 708 | /** 709 | * Join a sequence of arrays along a new axis. 710 | * The axis parameter specifies the index of the new axis in the dimensions of the result. 711 | * For example, if axis=0 it will be the first dimension and if axis=-1 it will be the last dimension. 712 | * @param arrays Sequence of array_like 713 | * @param axis The axis in the result array along which the input arrays are stacked. 714 | * @return The stacked array has one more dimension than the input arrays. 715 | */ 716 | export function stack( 717 | arrays: Array, 718 | axis = 0 719 | ): NdArray { 720 | axis = axis || 0; 721 | if (!arrays || arrays.length === 0) { 722 | throw new errors.ValueError("need at least one array to stack"); 723 | } 724 | const arrays2 = arrays.map(function (a) { 725 | return (_.isNumber(a) ? a : NdArray.new(a)) as number | NdArray; 726 | }); 727 | const expectedShape = (arrays2[0] as NdArray).shape || []; // for numbers 728 | 729 | for (let i = 1; i < arrays2.length; i++) { 730 | const shape = (arrays2[i] as NdArray).shape || []; // for numbers 731 | const len = Math.max(expectedShape.length, shape.length); 732 | for (let j = 0; j < len; j++) { 733 | if (expectedShape[j] !== shape[j]) 734 | throw new errors.ValueError( 735 | "all input arrays must have the same shape" 736 | ); 737 | } 738 | } 739 | let stacked; 740 | if (expectedShape.length === 0) { 741 | // stacking numbers 742 | stacked = concatenate(arrays2); 743 | } else { 744 | stacked = zeros([arrays2.length].concat(expectedShape)); 745 | for (let i = 0; i < arrays2.length; i++) { 746 | stacked.pick(i).assign(arrays2[i], false); 747 | } 748 | } 749 | 750 | if (axis) { 751 | // recompute neg axis 752 | if (axis < 0) axis = stacked.ndim + axis; 753 | 754 | const d = stacked.ndim; 755 | const axes = new Array(d); 756 | for (let i = 0; i < d; i++) { 757 | axes[i] = i < axis ? i + 1 : i === axis ? 0 : i; 758 | } 759 | 760 | return stacked.transpose(axes); 761 | } 762 | return stacked; 763 | } 764 | 765 | /** 766 | * Reverse the order of elements in an array along the given axis. 767 | * The shape of the array is preserved, but the elements are reordered. 768 | * New in version 0.15.0. 769 | * @param m Input array. 770 | * @param axis Axis in array, which entries are reversed. 771 | * @return A view of `m` with the entries of axis reversed. Since a view is returned, this operation is done in constant time. 772 | */ 773 | export function flip(m: ArbDimNumArray | NdArray, axis: number): NdArray { 774 | m = NdArray.new(m); 775 | const indexer = ones(m.ndim).tolist(); 776 | let cleanaxis = axis; 777 | while (cleanaxis < 0) { 778 | cleanaxis += m.ndim; 779 | } 780 | if (indexer[cleanaxis] === undefined) { 781 | throw new errors.ValueError( 782 | "axis=" + axis + "invalid for the " + m.ndim + "-dimensional input array" 783 | ); 784 | } 785 | indexer[cleanaxis] = -1; 786 | return m.step.apply(m, indexer); 787 | } 788 | 789 | /** 790 | * Rotate an array by 90 degrees in the plane specified by axes. 791 | * Rotation direction is from the first towards the second axis. 792 | * New in version 0.15.0. 793 | * @param m array_like 794 | * @param k Number of times the array is rotated by 90 degrees. 795 | * @param axes Default [0, 1]. The array is rotated in the plane defined by the axes. Axes must be different. 796 | * @return A rotated view of m. 797 | */ 798 | export function rot90( 799 | m: ArbDimNumArray | NdArray, 800 | k = 1, 801 | axes: number[] | NdArray = [0, 1] 802 | ): NdArray { 803 | k = k || 1; 804 | while (k < 0) { 805 | k += 4; 806 | } 807 | k = k % 4; 808 | m = NdArray.new(m); 809 | let axes2: any = NdArray.new(axes || [0, 1]); 810 | if (axes2.shape.length !== 1 || axes2.shape[0] !== 2) { 811 | throw new errors.ValueError("len(axes) must be 2"); 812 | } 813 | axes2 = axes2.tolist(); 814 | if (axes2[0] === axes2[1] || abs(axes2[0] - axes2[1]).ndim === m.ndim) { 815 | throw new errors.ValueError("Axes must be different."); 816 | } 817 | 818 | if (k === 0) { 819 | return m; 820 | } 821 | if (k === 2) { 822 | return flip(flip(m, axes2[0]), axes2[1]); 823 | } 824 | const axesList = arange(m.ndim).tolist(); 825 | const keep = axesList[axes2[0]]; 826 | axesList[axes2[0]] = axesList[axes2[1]]; 827 | axesList[axes2[1]] = keep; 828 | if (k === 1) { 829 | return transpose(flip(m, axes2[1]), axesList as number[]); 830 | } else { 831 | return flip(transpose(m, axesList as number[]), axes2[1]); 832 | } 833 | } 834 | 835 | /** 836 | * @param dtype Defaut is "array". The type of the output array. E.g., 'uint8' or Uint8Array. 837 | */ 838 | export const array = NdArray.new; 839 | 840 | export const remainder = mod; 841 | 842 | export function int8(array: ArbDimNumArray | number): NdArray { 843 | return NdArray.new(array, "int8"); 844 | } 845 | export function uint8(array: ArbDimNumArray | number): NdArray { 846 | return NdArray.new(array, "uint8"); 847 | } 848 | export function int16(array: ArbDimNumArray | number): NdArray { 849 | return NdArray.new(array, "int16"); 850 | } 851 | export function uint16(array: ArbDimNumArray | number): NdArray { 852 | return NdArray.new(array, "uint16"); 853 | } 854 | export function int32(array: ArbDimNumArray | number): NdArray { 855 | return NdArray.new(array, "int32"); 856 | } 857 | export function uint32(array: ArbDimNumArray | number): NdArray { 858 | return NdArray.new(array, "uint32"); 859 | } 860 | export function float32(array: ArbDimNumArray | number): NdArray { 861 | return NdArray.new(array, "float32"); 862 | } 863 | export function float64(array: ArbDimNumArray | number): NdArray { 864 | return NdArray.new(array, "float64"); 865 | } 866 | export function uint8Clamped(array: ArbDimNumArray | number): NdArray { 867 | return NdArray.new(array, "uint8_clamped"); 868 | } 869 | -------------------------------------------------------------------------------- /src/lib/ndarray.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import cwise from "cwise"; 4 | import ops from "ndarray-ops"; 5 | import ndFFT from "ndarray-fft"; 6 | import gemm from "ndarray-gemm"; 7 | import ndPool from "typedarray-pool"; 8 | import ndarray, { NdArray as BaseNdArray } from "ndarray"; 9 | 10 | import util from "util"; 11 | 12 | import CONF from "./config"; 13 | import * as errors from "./errors"; 14 | import _ from "./utils"; 15 | 16 | export interface ArbitraryDimArray extends Array> {} 17 | export type ArbDimNumArray = ArbitraryDimArray; 18 | 19 | export type ArrayLikeConstructor = 20 | | ArrayConstructor 21 | | Int8ArrayConstructor 22 | | Uint8ArrayConstructor 23 | | Int16ArrayConstructor 24 | | Uint16ArrayConstructor 25 | | Int32ArrayConstructor 26 | | Uint32ArrayConstructor 27 | | Float32ArrayConstructor 28 | | Float64ArrayConstructor 29 | | Uint8ClampedArrayConstructor; 30 | 31 | export type TypedArray = 32 | | Int8Array 33 | | Uint8Array 34 | | Uint8ClampedArray 35 | | Int16Array 36 | | Uint16Array 37 | | Int32Array 38 | | Uint32Array 39 | | Float32Array 40 | | Float64Array; 41 | export type OneDimNumArray = Array | TypedArray; 42 | export type DType = D extends Int8Array 43 | ? "int8" 44 | : D extends Int16Array 45 | ? "int16" 46 | : D extends Int32Array 47 | ? "int32" 48 | : D extends Uint8Array 49 | ? "uint8" 50 | : D extends Uint8ClampedArray 51 | ? "uint8_clamped" 52 | : D extends Uint16Array 53 | ? "uint16" 54 | : D extends Uint32Array 55 | ? "uint32" 56 | : D extends Float32Array 57 | ? "float32" 58 | : D extends Float64Array 59 | ? "float64" 60 | : "array"; 61 | 62 | /** 63 | * Multidimensional, homogeneous array of fixed-size items 64 | * 65 | * The number of dimensions and items in an array is defined by its shape, which is a tuple of N positive 66 | * integers that specify the sizes of each dimension. The type of items in the array is specified by a separate 67 | * data-type object (dtype), one of which is associated with each NdArray. 68 | */ 69 | export class NdArray { 70 | selection: BaseNdArray; 71 | 72 | constructor(data: BaseNdArray); 73 | constructor( 74 | data: OneDimNumArray, 75 | shape: number[], 76 | stride?: number[], 77 | offset?: number 78 | ); 79 | constructor(...args: any[]) { 80 | if (args.length === 1) { 81 | this.selection = args[0]; 82 | } else if (args.length === 0) { 83 | throw new errors.ValueError("Required argument 'data' not found"); 84 | } else { 85 | this.selection = ndarray.apply(null, args); 86 | } 87 | } 88 | 89 | /** 90 | * @property NdArray#size - Number of elements in the array. 91 | */ 92 | get size(): number { 93 | return this.selection.size; 94 | } 95 | 96 | /** 97 | * The shape of the array 98 | * 99 | * @name NdArray#shape 100 | * @readonly 101 | */ 102 | get shape(): number[] { 103 | return this.selection.shape; 104 | } 105 | 106 | /** 107 | * Number of array dimensions. 108 | * 109 | * @name NdArray#ndim 110 | * @readonly 111 | */ 112 | get ndim(): number { 113 | return this.selection.shape.length; 114 | } 115 | 116 | /** 117 | * Data-type of the array’s elements. 118 | */ 119 | get dtype() { 120 | return this.selection.dtype; 121 | } 122 | set dtype(dtype) { 123 | const T = _.getType(dtype); 124 | if (T !== _.getType(this.dtype)) { 125 | this.selection = ndarray( 126 | new T(this.selection.data), 127 | this.selection.shape, 128 | this.selection.stride, 129 | this.selection.offset 130 | ); 131 | } 132 | } 133 | 134 | /** 135 | * Permute the dimensions of the array. 136 | * 137 | * @name NdArray#T 138 | * @readonly 139 | */ 140 | get T(): NdArray { 141 | return this.transpose(); 142 | } 143 | 144 | get(...args: number[]): number { 145 | const n = args.length; 146 | for (let i = 0; i < n; i++) { 147 | if (args[i] < 0) { 148 | args[i] += this.shape[i]; 149 | } 150 | } 151 | return this.selection.get.apply(this.selection, args); 152 | } 153 | 154 | set(...args: number[]): number { 155 | return this.selection.set.apply(this.selection, args); 156 | } 157 | 158 | slice(...args: Array): NdArray { 159 | const d = this.ndim; 160 | const hi = new Array(d); 161 | const lo = new Array(d); 162 | const step = new Array(d); 163 | const tShape = this.shape; 164 | 165 | for (let i = 0; i < d; i++) { 166 | let arg = args[i]; 167 | if (typeof arg === "undefined") { 168 | break; 169 | } 170 | if (arg === null) { 171 | continue; 172 | } 173 | if (_.isNumber(arg)) { 174 | lo[i] = arg < 0 ? (arg as number) + tShape[i] : arg; 175 | hi[i] = null; 176 | step[i] = 1; 177 | } else if ( 178 | (arg as number[]).length === 4 && 179 | arg[1] === null && 180 | arg[2] === null 181 | ) { 182 | // pattern: a[start::step] 183 | const s = arg[0] < 0 ? arg[0] + tShape[i] : arg[0]; 184 | lo[i] = s; 185 | hi[i] = null; 186 | step[i] = arg[3] || 1; 187 | } else { 188 | // pattern start:end:step 189 | const start = arg[0] < 0 ? arg[0] + tShape[i] : arg[0]; 190 | const end = arg[1] < 0 ? arg[1] + tShape[i] : arg[1]; 191 | lo[i] = end ? start : 0; 192 | hi[i] = end ? end - start : start; 193 | step[i] = arg[2] || 1; 194 | } 195 | } 196 | 197 | const slo = this.selection.lo.apply(this.selection, lo); 198 | const shi = slo.hi.apply(slo, hi); 199 | const sstep = shi.step.apply(shi, step); 200 | return new NdArray(sstep); 201 | } 202 | 203 | /** 204 | * Return a subarray by fixing a particular axis 205 | * @param axis a array whose element could be `null` or `number` 206 | * 207 | * @example 208 | * ```typescript 209 | * arr = nj.arange(4*4).reshape(4,4) 210 | * // array([[ 0, 1, 2, 3], 211 | * // [ 4, 5, 6, 7], 212 | * // [ 8, 9, 10, 11], 213 | * // [ 12, 13, 14, 15]]) 214 | * 215 | * arr.pick(1) 216 | * // array([ 4, 5, 6, 7]) 217 | * 218 | * arr.pick(null, 1) 219 | * // array([ 1, 5, 9, 13]) 220 | * ``` 221 | **/ 222 | pick(...axis: number[]): NdArray { 223 | return new NdArray(this.selection.pick.apply(this.selection, arguments)); 224 | } 225 | 226 | /** 227 | * Return a shifted view of the array. Think of it as taking the upper left corner of the image and dragging it inward 228 | * 229 | * @example 230 | * ```typescript 231 | * arr = nj.arange(4*4).reshape(4,4) 232 | * // array([[ 0, 1, 2, 3], 233 | * // [ 4, 5, 6, 7], 234 | * // [ 8, 9, 10, 11], 235 | * // [ 12, 13, 14, 15]]) 236 | * arr.lo(1,1) 237 | * // array([[ 5, 6, 7], 238 | * // [ 9, 10, 11], 239 | * // [ 13, 14, 15]]) 240 | * ``` 241 | **/ 242 | lo(...args: number[]): NdArray { 243 | return new NdArray(this.selection.lo.apply(this.selection, args)); 244 | } 245 | 246 | /** 247 | * Return a sliced view of the array. 248 | * 249 | * @example 250 | * ```typescript 251 | * arr = nj.arange(4*4).reshape(4,4) 252 | * // array([[ 0, 1, 2, 3], 253 | * // [ 4, 5, 6, 7], 254 | * // [ 8, 9, 10, 11], 255 | * // [ 12, 13, 14, 15]]) 256 | * 257 | * arr.hi(3,3) 258 | * // array([[ 0, 1, 2], 259 | * // [ 4, 5, 6], 260 | * // [ 8, 9, 10]]) 261 | * 262 | * arr.lo(1,1).hi(2,2) 263 | * // array([[ 5, 6], 264 | * // [ 9, 10]]) 265 | * ``` 266 | */ 267 | hi(...args: number[]): NdArray { 268 | return new NdArray(this.selection.hi.apply(this.selection, args)); 269 | } 270 | 271 | step(...args: number[]): NdArray { 272 | return new NdArray(this.selection.step.apply(this.selection, args)); 273 | } 274 | 275 | /** 276 | * Return a copy of the array collapsed into one dimension using row-major order (C-style) 277 | */ 278 | flatten(): NdArray { 279 | if (this.ndim === 1) { 280 | // already flattened 281 | return new NdArray(this.selection); 282 | } 283 | const T = _.getType(this.dtype); 284 | let arr = _.flatten(this.tolist(), true); 285 | if (!(arr instanceof T)) { 286 | arr = new T(arr); 287 | } 288 | return new NdArray(arr, [this.size]); 289 | } 290 | 291 | /** 292 | * Gives a new shape to the array without changing its data. 293 | * @param shape - The new shape should be compatible with the original shape. If an integer, then the result will be a 1-D array of that length. One shape dimension can be -1. In this case, the value is inferred from the length of the array and remaining dimensions. 294 | * @returns a new view object if possible, a copy otherwise, 295 | */ 296 | reshape(...shape: number[]): NdArray; 297 | reshape(shape: number[]): NdArray; 298 | reshape(...args: any[]): NdArray { 299 | if (arguments.length === 0) { 300 | throw new errors.ValueError( 301 | "function takes at least one argument (0 given)" 302 | ); 303 | } 304 | let shape: number[]; 305 | if ( 306 | arguments.length === 1 && 307 | _.isNumber(arguments[0]) && 308 | arguments[0] === -1 309 | ) { 310 | shape = [_.shapeSize(this.shape)]; 311 | } 312 | if (arguments.length === 1) { 313 | if (_.isNumber(arguments[0])) { 314 | shape = [arguments[0] as number]; 315 | } else { 316 | // grimmer refactor note: original logic does not check if it is an array 317 | shape = arguments[0]; 318 | } 319 | } 320 | if (arguments.length > 1) { 321 | shape = [].slice.call(arguments); 322 | } 323 | if ( 324 | (shape as number[]).filter(function (s) { 325 | return s === -1; 326 | }).length > 1 327 | ) { 328 | throw new errors.ValueError("can only specify one unknown dimension"); 329 | } 330 | const currentShapeSize = _.shapeSize(shape); 331 | shape = shape.map( 332 | function (s) { 333 | return s === -1 ? (-1 * this.size) / currentShapeSize : s; 334 | }.bind(this) 335 | ); 336 | if (this.size !== _.shapeSize(shape)) { 337 | throw new errors.ValueError("total size of new array must be unchanged"); 338 | } 339 | const selfShape = this.selection.shape; 340 | const selfOffset = this.selection.offset; 341 | const selfStride = this.selection.stride; 342 | const selfDim = selfShape.length; 343 | const d = shape.length; 344 | let stride; 345 | let offset; 346 | let i; 347 | let sz; 348 | if (selfDim === d) { 349 | let sameShapes = true; 350 | for (i = 0; i < d; ++i) { 351 | if (selfShape[i] !== shape[i]) { 352 | sameShapes = false; 353 | break; 354 | } 355 | } 356 | if (sameShapes) { 357 | return new NdArray( 358 | this.selection.data as OneDimNumArray, 359 | selfShape, 360 | selfStride, 361 | selfOffset 362 | ); 363 | } 364 | } else if (selfDim === 1) { 365 | // 1d view 366 | stride = new Array(d); 367 | for (i = d - 1, sz = 1; i >= 0; --i) { 368 | stride[i] = sz; 369 | sz *= shape[i]; 370 | } 371 | offset = selfOffset; 372 | for (i = 0; i < d; ++i) { 373 | if (stride[i] < 0) { 374 | offset -= (shape[i] - 1) * stride[i]; 375 | } 376 | } 377 | return new NdArray( 378 | this.selection.data as OneDimNumArray, 379 | shape, 380 | stride, 381 | offset 382 | ); 383 | } 384 | 385 | const minDim = Math.min(selfDim, d); 386 | let areCompatible = true; 387 | for (i = 0; i < minDim; i++) { 388 | if (selfShape[i] !== shape[i]) { 389 | areCompatible = false; 390 | break; 391 | } 392 | } 393 | if (areCompatible) { 394 | stride = new Array(d); 395 | for (i = 0; i < d; i++) { 396 | stride[i] = selfStride[i] || 1; 397 | } 398 | offset = selfOffset; 399 | return new NdArray( 400 | this.selection.data as OneDimNumArray, 401 | shape, 402 | stride, 403 | offset 404 | ); 405 | } 406 | return this.flatten().reshape(shape); 407 | } 408 | 409 | /** 410 | * Permute the dimensions of the array. 411 | * @example 412 | * ```typescript 413 | * arr = nj.arange(6).reshape(1,2,3) 414 | * // array([[[ 0, 1, 2], 415 | * // [ 3, 4, 5]]]) 416 | * 417 | * arr.T 418 | * // array([[[ 0], 419 | * // [ 3]], 420 | * // [[ 1], 421 | * // [ 4]], 422 | * // [[ 2], 423 | * // [ 5]]]) 424 | * 425 | * arr.transpose(1,0,2) 426 | * // array([[[ 0, 1, 2]], 427 | * // [[ 3, 4, 5]]]) 428 | * ``` 429 | */ 430 | transpose(...axes: number[]): NdArray; 431 | transpose(axes?: number[]): NdArray; 432 | transpose(...args: any[]): NdArray { 433 | let axes: any; 434 | if (args.length === 0) { 435 | const d = this.ndim; 436 | axes = new Array(d); 437 | for (let i = 0; i < d; i++) { 438 | axes[i] = d - i - 1; 439 | } 440 | } else if (args.length > 1) { 441 | axes = args as unknown as number[]; 442 | } else { 443 | axes = args[0]; 444 | } 445 | return new NdArray(this.selection.transpose.apply(this.selection, axes)); 446 | } 447 | 448 | /** 449 | * Dot product of two arrays. 450 | */ 451 | dot(x: ArbDimNumArray | NdArray): NdArray { 452 | x = x instanceof NdArray ? x : createArray(x, this.dtype as DType); 453 | const tShape = this.shape; 454 | const xShape = x.shape; 455 | 456 | if (tShape.length === 2 && xShape.length === 2 && tShape[1] === xShape[0]) { 457 | // matrix/matrix 458 | const T = _.getType(this.dtype); 459 | const c = new NdArray(new T(tShape[0] * xShape[1]), [ 460 | tShape[0], 461 | xShape[1], 462 | ]); 463 | gemm(c.selection, this.selection, x.selection); 464 | return c; 465 | } else if ( 466 | tShape.length === 1 && 467 | xShape.length === 2 && 468 | tShape[0] === xShape[0] 469 | ) { 470 | // vector/matrix 471 | return this.reshape([tShape[0], 1]).T.dot(x).reshape(xShape[1]); 472 | } else if ( 473 | tShape.length === 2 && 474 | xShape.length === 1 && 475 | tShape[1] === xShape[0] 476 | ) { 477 | // matrix/vector 478 | return this.dot(x.reshape([xShape[0], 1])).reshape(tShape[0]); 479 | } else if ( 480 | tShape.length === 1 && 481 | xShape.length === 1 && 482 | tShape[0] === xShape[0] 483 | ) { 484 | // vector/vector 485 | return this.reshape([tShape[0], 1]) 486 | .T.dot(x.reshape([xShape[0], 1])) 487 | .reshape([1]); 488 | } else { 489 | throw new errors.ValueError( 490 | "cannot compute the matrix product of given arrays" 491 | ); 492 | } 493 | } 494 | 495 | /** 496 | * Assign `x` to the array, element-wise. 497 | */ 498 | assign(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 499 | if (arguments.length === 1) { 500 | copy = true; 501 | } 502 | const arr = copy ? this.clone() : this; 503 | 504 | if (_.isNumber(x)) { 505 | ops.assigns(arr.selection, x); 506 | return arr; 507 | } 508 | x = createArray(x, this.dtype as DType); 509 | ops.assign(arr.selection, x.selection); 510 | return arr; 511 | } 512 | 513 | /** 514 | * Add `x` to the array, element-wise. 515 | */ 516 | add(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 517 | if (arguments.length === 1) { 518 | copy = true; 519 | } 520 | const arr = copy ? this.clone() : this; 521 | 522 | if (_.isNumber(x)) { 523 | ops.addseq(arr.selection, x); 524 | return arr; 525 | } 526 | x = createArray(x, this.dtype as DType); 527 | ops.addeq(arr.selection, x.selection); 528 | return arr; 529 | } 530 | 531 | /** 532 | * Subtract `x` to the array, element-wise. 533 | */ 534 | subtract(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 535 | if (arguments.length === 1) { 536 | copy = true; 537 | } 538 | const arr = copy ? this.clone() : this; 539 | 540 | if (_.isNumber(x)) { 541 | ops.subseq(arr.selection, x); 542 | return arr; 543 | } 544 | x = createArray(x, this.dtype as DType); 545 | ops.subeq(arr.selection, x.selection); 546 | return arr; 547 | } 548 | 549 | /** 550 | * Multiply array by `x`, element-wise. 551 | */ 552 | multiply(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 553 | if (arguments.length === 1) { 554 | copy = true; 555 | } 556 | const arr = copy ? this.clone() : this; 557 | if (_.isNumber(x)) { 558 | ops.mulseq(arr.selection, x); 559 | return arr; 560 | } 561 | 562 | x = createArray(x, this.dtype as DType); 563 | ops.muleq(arr.selection, x.selection); 564 | 565 | return arr; 566 | } 567 | 568 | /** 569 | * Divide array by `x`, element-wise. 570 | */ 571 | divide(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 572 | if (arguments.length === 1) { 573 | copy = true; 574 | } 575 | const arr = copy ? this.clone() : this; 576 | if (_.isNumber(x)) { 577 | ops.divseq(arr.selection, x); 578 | return arr; 579 | } 580 | 581 | x = createArray(x, this.dtype as DType); 582 | ops.diveq(arr.selection, x.selection); 583 | 584 | return arr; 585 | } 586 | 587 | /** 588 | * Raise array elements to powers from given array, element-wise. 589 | * 590 | * @param x 591 | * @param copy - set to false to modify the array rather than create a new one 592 | */ 593 | pow(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 594 | if (arguments.length === 1) { 595 | copy = true; 596 | } 597 | const arr = copy ? this.clone() : this; 598 | if (_.isNumber(x)) { 599 | ops.powseq(arr.selection, x); 600 | return arr; 601 | } 602 | 603 | x = createArray(x, this.dtype as DType); 604 | ops.poweq(arr.selection, x.selection); 605 | return arr; 606 | } 607 | 608 | /** 609 | * Calculate the exponential of all elements in the array, element-wise. 610 | * 611 | * @param copy - set to false to modify the array rather than create a new one 612 | */ 613 | exp(copy = true): NdArray { 614 | if (arguments.length === 0) { 615 | copy = true; 616 | } 617 | const arr = copy ? this.clone() : this; 618 | ops.expeq(arr.selection); 619 | return arr; 620 | } 621 | 622 | /** 623 | * Calculate the natural logarithm of all elements in the array, element-wise. 624 | * 625 | * @param copy - set to false to modify the array rather than create a new one 626 | */ 627 | log(copy = true): NdArray { 628 | if (arguments.length === 0) { 629 | copy = true; 630 | } 631 | const arr = copy ? this.clone() : this; 632 | ops.logeq(arr.selection); 633 | return arr; 634 | } 635 | 636 | /** 637 | * Calculate the positive square-root of all elements in the array, element-wise. 638 | * 639 | * @param copy set to false to modify the array rather than create a new one 640 | */ 641 | sqrt(copy = true): NdArray { 642 | if (arguments.length === 0) { 643 | copy = true; 644 | } 645 | const arr = copy ? this.clone() : this; 646 | ops.sqrteq(arr.selection); 647 | return arr; 648 | } 649 | 650 | /** 651 | * Return the maximum value of the array 652 | */ 653 | max(): number { 654 | if (this.selection.size === 0) { 655 | return null; 656 | } 657 | return ops.sup(this.selection); 658 | } 659 | 660 | /** 661 | * Return the minimum value of the array 662 | */ 663 | min(): number { 664 | if (this.selection.size === 0) { 665 | return null; 666 | } 667 | return ops.inf(this.selection); 668 | } 669 | 670 | /** 671 | * Sum of array elements. 672 | */ 673 | sum(): number { 674 | return ops.sum(this.selection); 675 | } 676 | 677 | /** 678 | * Returns the standard deviation, a measure of the spread of a distribution, of the array elements. 679 | * 680 | * @param {object} options default {ddof:0} 681 | */ 682 | std(options?: { ddof: number }) { 683 | if (!options?.ddof) { 684 | options = { ddof: 0 }; 685 | } 686 | const squares = this.clone(); 687 | ops.powseq(squares.selection, 2); 688 | const mean = this.mean(); 689 | const shapeSize = _.shapeSize(this.shape); 690 | const letiance = 691 | ops.sum(squares.selection) / (shapeSize - options.ddof) - 692 | (mean * mean * shapeSize) / (shapeSize - options.ddof); 693 | 694 | return letiance > 0 ? Math.sqrt(Math.abs(letiance)) : 0; 695 | } 696 | 697 | /** 698 | * Return the arithmetic mean of array elements. 699 | */ 700 | mean(): number { 701 | return ops.sum(this.selection) / _.shapeSize(this.shape); 702 | } 703 | 704 | /** 705 | * Return element-wise remainder of division. 706 | */ 707 | mod(x: NdArray | ArbDimNumArray | number, copy = true): NdArray { 708 | if (arguments.length === 1) { 709 | copy = true; 710 | } 711 | const arr = copy ? this.clone() : this; 712 | if (_.isNumber(x)) { 713 | ops.modseq(arr.selection, x); 714 | return arr; 715 | } 716 | 717 | x = createArray(x, this.dtype as DType); 718 | ops.modeq(arr.selection, x.selection); 719 | 720 | return arr; 721 | } 722 | 723 | /** 724 | * Converts {NdArray} to a native JavaScript {Array} 725 | */ 726 | tolist(): ArbDimNumArray { 727 | return unpackArray(this.selection); 728 | } 729 | 730 | valueOf(): ArbDimNumArray { 731 | return this.tolist(); 732 | } 733 | 734 | /** 735 | * Stringify the array to make it readable in the console, by a human. 736 | */ 737 | [util.inspect.custom]() { 738 | return this.toString(); 739 | } 740 | 741 | /** 742 | * Stringify the array to make it readable by a human. 743 | */ 744 | toString(): string { 745 | const nChars = formatNumber(this.max()).length; 746 | 747 | const reg1 = /\]\,(\s*)\[/g; 748 | const spacer1 = "],\n$1 ["; 749 | const reg3 = /\]\,(\s+)...\,(\s+)\[/g; 750 | const spacer3 = "],\n$2 ...\n$2 ["; 751 | const reg2 = /\[\s+\[/g; 752 | const spacer2 = "[["; 753 | 754 | function formatArray(k, v) { 755 | if (_.isString(v)) { 756 | return v; 757 | } 758 | if (_.isNumber(v)) { 759 | const s = formatNumber(v); 760 | return new Array(Math.max(0, nChars - s.length + 2)).join(" ") + s; 761 | } 762 | k = k || 0; 763 | let arr; 764 | const th = CONF.printThreshold; 765 | const hth = (th / 2) | 0; 766 | if (v.length > th) { 767 | arr = [].concat(v.slice(0, hth), [" ..."], v.slice(v.length - hth)); 768 | } else { 769 | arr = v; 770 | } 771 | return ( 772 | new Array(k + 1).join(" ") + 773 | "[" + 774 | arr 775 | .map(function (i, ii) { 776 | return formatArray(ii === 0 && k === 0 ? 1 : k + 1, i); 777 | }) 778 | .join(",") + 779 | "]" 780 | ); 781 | } 782 | 783 | let base = JSON.stringify(this.tolist(), formatArray) 784 | .replace(reg1, spacer1) 785 | .replace(reg2, spacer2) 786 | .replace(reg2, spacer2) 787 | .replace(reg3, spacer3) 788 | .slice(2, -1); 789 | switch (this.dtype) { 790 | case "array": 791 | return "array([" + base + ")"; 792 | default: 793 | return "array([" + base + ", dtype=" + this.dtype + ")"; 794 | } 795 | } 796 | 797 | /** 798 | * Stringify object to JSON 799 | */ 800 | toJSON(): string { 801 | return JSON.stringify(this.tolist()); 802 | } 803 | 804 | /** 805 | * Create a full copy of the array 806 | */ 807 | clone(): NdArray { 808 | const s = this.selection; 809 | if (typeof (s.data as OneDimNumArray).slice === "undefined") { 810 | return new NdArray( 811 | ndarray([].slice.apply(s.data), s.shape, s.stride, s.offset) 812 | ); // for legacy browsers 813 | } 814 | return new NdArray( 815 | ndarray((s.data as OneDimNumArray).slice(), s.shape, s.stride, s.offset) 816 | ); 817 | } 818 | 819 | /** 820 | * Return true if two arrays have the same shape and elements, false otherwise. 821 | */ 822 | equal(array: ArbDimNumArray | NdArray): boolean { 823 | array = createArray(array); 824 | if (this.size !== array.size || this.ndim !== array.ndim) { 825 | return false; 826 | } 827 | const d = this.ndim; 828 | for (let i = 0; i < d; i++) { 829 | if (this.shape[i] !== array.shape[i]) { 830 | return false; 831 | } 832 | } 833 | 834 | return ops.equals(this.selection, array.selection); 835 | } 836 | 837 | /** 838 | * Round array to the to the nearest integer. 839 | */ 840 | round(copy = true): NdArray { 841 | if (arguments.length === 0) { 842 | copy = true; 843 | } 844 | const arr = copy ? this.clone() : this; 845 | ops.roundeq(arr.selection); 846 | return arr; 847 | } 848 | 849 | /** 850 | * Return the inverse of the array, element-wise. 851 | */ 852 | negative(): NdArray { 853 | const c = this.clone(); 854 | ops.neg(c.selection, this.selection); 855 | return c; 856 | } 857 | 858 | diag(): NdArray { 859 | const d = this.ndim; 860 | if (d === 1) { 861 | // input is a vector => return a diagonal matrix 862 | const T = _.getType(this.dtype); 863 | const shape = [this.shape[0], this.shape[0]]; 864 | const arr = new NdArray(new T(_.shapeSize(shape)), shape); 865 | if (arr.dtype === "array") { 866 | ops.assigns(arr.selection, 0); 867 | } 868 | for (let i = 0; i < this.shape[0]; i++) arr.set(i, i, this.get(i)); 869 | return arr; 870 | } 871 | const mshape = this.shape; 872 | const mstride = this.selection.stride; 873 | let nshape = 1 << 30; 874 | let nstride = 0; 875 | for (let i = 0; i < d; ++i) { 876 | nshape = Math.min(nshape, mshape[i]) | 0; 877 | nstride += mstride[i]; 878 | } 879 | return new NdArray( 880 | this.selection.data as OneDimNumArray, 881 | [nshape], 882 | [nstride], 883 | this.selection.offset 884 | ); 885 | } 886 | 887 | iteraxis(axis: number, cb: (xi: NdArray, i: number) => void) { 888 | const shape = this.shape; 889 | if (axis === -1) { 890 | axis = shape.length - 1; 891 | } 892 | if (axis < 0 || axis > shape.length - 1) { 893 | throw new errors.ValueError("invalid axis"); 894 | } 895 | for (let i = 0; i < shape[axis]; i++) { 896 | const loc = new Array(axis + 1); 897 | for (let ii = 0; ii < axis + 1; ii++) { 898 | loc[ii] = ii === axis ? i : null; 899 | } 900 | const subArr = this.selection.pick.apply(this.selection, loc); 901 | const xi = createArray(unpackArray(subArr), this.dtype as DType); 902 | cb(xi, i); 903 | } 904 | } 905 | 906 | /** 907 | * Returns the discrete, linear convolution of the array using the given filter. 908 | * 909 | * @note: Arrays must have the same dimensions and `filter` must be smaller than the array. 910 | * @note: The convolution product is only given for points where the signals overlap completely. Values outside the signal boundary have no effect. This behaviour is known as the 'valid' mode. 911 | * @note: Use optimized code for 3x3, 3x3x1, 5x5, 5x5x1 filters, FFT otherwise. 912 | */ 913 | convolve(filter: ArbDimNumArray | NdArray): NdArray { 914 | filter = NdArray.new(filter); 915 | const ndim = this.ndim; 916 | if (ndim !== filter.ndim) { 917 | throw new errors.ValueError("arrays must have the same dimensions"); 918 | } 919 | const outShape = new Array(ndim); 920 | const step = new Array(ndim); 921 | const ts = this.selection; 922 | const tShape = this.shape; 923 | const fs = filter.selection; 924 | const fShape = filter.shape; 925 | 926 | for (let i = 0; i < ndim; i++) { 927 | const l = tShape[i] - fShape[i] + 1; 928 | if (l < 0) { 929 | throw new errors.ValueError("filter cannot be greater than the array"); 930 | } 931 | outShape[i] = l; 932 | step[i] = -1; 933 | } 934 | 935 | if (ndim === 2 && fShape[0] === 3 && fShape[1] === 3) { 936 | const out3x3 = new NdArray(new Float32Array(_.shapeSize(tShape)), tShape); 937 | doConvolve3x3( 938 | out3x3.selection, // c 939 | ts, // x 940 | fs.get(0, 0), // fa 941 | fs.get(0, 1), // fb 942 | fs.get(0, 2), // fc 943 | fs.get(1, 0), // fd 944 | fs.get(1, 1), // fe 945 | fs.get(1, 2), // ff 946 | fs.get(2, 0), // fg 947 | fs.get(2, 1), // fh 948 | fs.get(2, 2) // fi 949 | ); 950 | return out3x3.lo(1, 1).hi(outShape[0], outShape[1]); 951 | } else if ( 952 | ndim === 3 && 953 | fShape[2] === 1 && 954 | tShape[2] === 1 && 955 | fShape[0] === 3 && 956 | fShape[1] === 3 957 | ) { 958 | const out3x3x1 = new NdArray( 959 | new Float32Array(_.shapeSize(tShape)), 960 | tShape 961 | ); 962 | doConvolve3x3( 963 | out3x3x1.selection.pick(null, null, 0), // c 964 | ts.pick(null, null, 0), // x 965 | fs.get(0, 0, 0), // fa 966 | fs.get(0, 1, 0), // fb 967 | fs.get(0, 2, 0), // fc 968 | fs.get(1, 0, 0), // fd 969 | fs.get(1, 1, 0), // fe 970 | fs.get(1, 2, 0), // ff 971 | fs.get(2, 0, 0), // fg 972 | fs.get(2, 1, 0), // fh 973 | fs.get(2, 2, 0) // fi 974 | ); 975 | return out3x3x1.lo(1, 1).hi(outShape[0], outShape[1]); 976 | } else if (ndim === 2 && fShape[0] === 5 && fShape[1] === 5) { 977 | const out5x5 = new NdArray(new Float32Array(_.shapeSize(tShape)), tShape); 978 | doConvolve5x5( 979 | out5x5.selection, // c 980 | ts, // x 981 | fs.get(0, 0), // fa 982 | fs.get(0, 1), // fb 983 | fs.get(0, 2), // fc 984 | fs.get(0, 3), // fd 985 | fs.get(0, 4), // fe 986 | fs.get(1, 0), // ff 987 | fs.get(1, 1), // fg 988 | fs.get(1, 2), // fh 989 | fs.get(1, 3), // fi 990 | fs.get(1, 4), // fj 991 | fs.get(2, 0), // fk 992 | fs.get(2, 1), // fl 993 | fs.get(2, 2), // fm 994 | fs.get(2, 3), // fn 995 | fs.get(2, 4), // fo 996 | fs.get(3, 0), // fp 997 | fs.get(3, 1), // fq 998 | fs.get(3, 2), // fr 999 | fs.get(3, 3), // fs 1000 | fs.get(3, 4), // ft 1001 | fs.get(4, 0), // fu 1002 | fs.get(4, 1), // fv 1003 | fs.get(4, 2), // fw 1004 | fs.get(4, 3), // fx 1005 | fs.get(4, 4) // fy 1006 | ); 1007 | return out5x5.lo(2, 2).hi(outShape[0], outShape[1]); 1008 | } else if ( 1009 | ndim === 3 && 1010 | fShape[2] === 1 && 1011 | tShape[2] === 1 && 1012 | fShape[0] === 5 && 1013 | fShape[1] === 5 1014 | ) { 1015 | const out5x5x1 = new NdArray( 1016 | new Float32Array(_.shapeSize(tShape)), 1017 | tShape 1018 | ); 1019 | doConvolve5x5( 1020 | out5x5x1.selection, // c 1021 | ts, // x 1022 | fs.get(0, 0, 0), // fa 1023 | fs.get(0, 1, 0), // fb 1024 | fs.get(0, 2, 0), // fc 1025 | fs.get(0, 3, 0), // fd 1026 | fs.get(0, 4, 0), // fe 1027 | fs.get(1, 0, 0), // ff 1028 | fs.get(1, 1, 0), // fg 1029 | fs.get(1, 2, 0), // fh 1030 | fs.get(1, 3, 0), // fi 1031 | fs.get(1, 4, 0), // fj 1032 | fs.get(2, 0, 0), // fk 1033 | fs.get(2, 1, 0), // fl 1034 | fs.get(2, 2, 0), // fm 1035 | fs.get(2, 3, 0), // fn 1036 | fs.get(2, 4, 0), // fo 1037 | fs.get(3, 0, 0), // fp 1038 | fs.get(3, 1, 0), // fq 1039 | fs.get(3, 2, 0), // fr 1040 | fs.get(3, 3, 0), // fs 1041 | fs.get(3, 4, 0), // ft 1042 | fs.get(4, 0, 0), // fu 1043 | fs.get(4, 1, 0), // fv 1044 | fs.get(4, 2, 0), // fw 1045 | fs.get(4, 3, 0), // fx 1046 | fs.get(4, 4, 0) // fy 1047 | ); 1048 | return out5x5x1.lo(2, 2).hi(outShape[0], outShape[1]); 1049 | } else { 1050 | return this.fftconvolve(filter); 1051 | } 1052 | } 1053 | 1054 | fftconvolve(filter: ArbDimNumArray | NdArray): NdArray { 1055 | filter = NdArray.new(filter); 1056 | 1057 | if (this.ndim !== filter.ndim) { 1058 | throw new errors.ValueError("arrays must have the same dimensions"); 1059 | } 1060 | 1061 | const as = this.selection; 1062 | const bs = filter.selection; 1063 | const d = this.ndim; 1064 | let nsize = 1; 1065 | const nstride = new Array(d); 1066 | const nshape = new Array(d); 1067 | const oshape = new Array(d); 1068 | let i; 1069 | for (i = d - 1; i >= 0; --i) { 1070 | nshape[i] = as.shape[i]; 1071 | nstride[i] = nsize; 1072 | nsize *= nshape[i]; 1073 | oshape[i] = as.shape[i] - bs.shape[i] + 1; 1074 | } 1075 | 1076 | const T = _.getType(as.dtype); 1077 | const out = new NdArray(new T(_.shapeSize(oshape)), oshape); 1078 | const outs = out.selection; 1079 | 1080 | const xT = ndPool.mallocDouble(nsize); 1081 | const x = ndarray(xT, nshape, nstride, 0); 1082 | ops.assigns(x, 0); 1083 | ops.assign(x.hi.apply(x, as.shape), as); 1084 | 1085 | const yT = ndPool.mallocDouble(nsize); 1086 | const y = ndarray(yT, nshape, nstride, 0); 1087 | ops.assigns(y, 0); 1088 | 1089 | // FFT x/y 1090 | ndFFT(1, x, y); 1091 | 1092 | const uT = ndPool.mallocDouble(nsize); 1093 | const u = ndarray(uT, nshape, nstride, 0); 1094 | ops.assigns(u, 0); 1095 | ops.assign(u.hi.apply(u, bs.shape), bs); 1096 | 1097 | const vT = ndPool.mallocDouble(nsize); 1098 | const v = ndarray(vT, nshape, nstride, 0); 1099 | ops.assigns(v, 0); 1100 | 1101 | ndFFT(1, u, v); 1102 | 1103 | doConjMuleq(x, y, u, v); 1104 | 1105 | ndFFT(-1, x, y); 1106 | 1107 | const outShape = new Array(d); 1108 | const outOffset = new Array(d); 1109 | let needZeroFill = false; 1110 | for (i = 0; i < d; ++i) { 1111 | if (outs.shape[i] > nshape[i]) { 1112 | needZeroFill = true; 1113 | } 1114 | outOffset[i] = bs.shape[i] - 1; 1115 | outShape[i] = Math.min(outs.shape[i], nshape[i] - outOffset[i]); 1116 | } 1117 | 1118 | let croppedX; 1119 | if (needZeroFill) { 1120 | ops.assign(outs, 0.0); 1121 | } 1122 | croppedX = x.lo.apply(x, outOffset); 1123 | croppedX = croppedX.hi.apply(croppedX, outShape); 1124 | ops.assign(outs.hi.apply(outs, outShape), croppedX); 1125 | 1126 | ndPool.freeDouble(xT); 1127 | ndPool.freeDouble(yT); 1128 | ndPool.freeDouble(uT); 1129 | ndPool.freeDouble(vT); 1130 | return out; 1131 | } 1132 | 1133 | static new( 1134 | arr: NdArray | ArbDimNumArray | number | TypedArray, 1135 | dtype?: DType | ArrayLikeConstructor 1136 | ): NdArray { 1137 | return createArray(arr, dtype); 1138 | } 1139 | } 1140 | 1141 | /* istanbul ignore next */ 1142 | const doConjMuleq = cwise({ 1143 | args: ["array", "array", "array", "array"], 1144 | body: function (xi, yi, ui, vi) { 1145 | const a = ui; 1146 | const b = vi; 1147 | const c = xi; 1148 | const d = yi; 1149 | const k1 = c * (a + b); 1150 | xi = k1 - b * (c + d); 1151 | yi = k1 + a * (d - c); 1152 | }, 1153 | }); 1154 | 1155 | /* istanbul ignore next */ 1156 | const doConvolve3x3 = cwise({ 1157 | args: [ 1158 | "array", // c 1159 | "array", // xe 1160 | "scalar", // fa 1161 | "scalar", // fb 1162 | "scalar", // fc 1163 | "scalar", // fd 1164 | "scalar", // fe 1165 | "scalar", // ff 1166 | "scalar", // fg 1167 | "scalar", // fh 1168 | "scalar", // fi 1169 | { offset: [-1, -1], array: 1 }, // xa 1170 | { offset: [-1, 0], array: 1 }, // xb 1171 | { offset: [-1, 1], array: 1 }, // xc 1172 | { offset: [0, -1], array: 1 }, // xd 1173 | // {offset:[ 9, 0], array:1}, // useless since available already 1174 | { offset: [0, 1], array: 1 }, // xf 1175 | { offset: [1, -1], array: 1 }, // xg 1176 | { offset: [1, 0], array: 1 }, // xh 1177 | { offset: [1, 1], array: 1 }, // xi 1178 | ], 1179 | body: function ( 1180 | c, 1181 | xe, 1182 | fa, 1183 | fb, 1184 | fc, 1185 | fd, 1186 | fe, 1187 | ff, 1188 | fg, 1189 | fh, 1190 | fi, 1191 | xa, 1192 | xb, 1193 | xc, 1194 | xd, 1195 | xf, 1196 | xg, 1197 | xh, 1198 | xi 1199 | ) { 1200 | c = 1201 | xa * fi + 1202 | xb * fh + 1203 | xc * fg + 1204 | xd * ff + 1205 | xe * fe + 1206 | xf * fd + 1207 | xg * fc + 1208 | xh * fb + 1209 | xi * fa; 1210 | }, 1211 | }); 1212 | 1213 | /* istanbul ignore next */ 1214 | const doConvolve5x5 = cwise({ 1215 | args: [ 1216 | "index", 1217 | "array", // c 1218 | "array", // xm 1219 | "scalar", // fa 1220 | "scalar", // fb 1221 | "scalar", // fc 1222 | "scalar", // fd 1223 | "scalar", // fe 1224 | "scalar", // ff 1225 | "scalar", // fg 1226 | "scalar", // fh 1227 | "scalar", // fi 1228 | "scalar", // fj 1229 | "scalar", // fk 1230 | "scalar", // fl 1231 | "scalar", // fm 1232 | "scalar", // fn 1233 | "scalar", // fo 1234 | "scalar", // fp 1235 | "scalar", // fq 1236 | "scalar", // fr 1237 | "scalar", // fs 1238 | "scalar", // ft 1239 | "scalar", // fu 1240 | "scalar", // fv 1241 | "scalar", // fw 1242 | "scalar", // fx 1243 | "scalar", // fy 1244 | { offset: [-2, -2], array: 1 }, // xa 1245 | { offset: [-2, -1], array: 1 }, // xb 1246 | { offset: [-2, 0], array: 1 }, // xc 1247 | { offset: [-2, 1], array: 1 }, // xd 1248 | { offset: [-2, 2], array: 1 }, // xe 1249 | { offset: [-1, -2], array: 1 }, // xf 1250 | { offset: [-1, -1], array: 1 }, // xg 1251 | { offset: [-1, 0], array: 1 }, // xh 1252 | { offset: [-1, 1], array: 1 }, // xi 1253 | { offset: [-1, 2], array: 1 }, // xj 1254 | { offset: [0, -2], array: 1 }, // xk 1255 | { offset: [0, -1], array: 1 }, // xl 1256 | // {offset:[ 0, 0], array:1}, 1257 | { offset: [0, 1], array: 1 }, // xn 1258 | { offset: [0, 2], array: 1 }, // xo 1259 | { offset: [1, -2], array: 1 }, // xp 1260 | { offset: [1, -1], array: 1 }, // xq 1261 | { offset: [1, 0], array: 1 }, // xr 1262 | { offset: [1, 1], array: 1 }, // xs 1263 | { offset: [1, 2], array: 1 }, // xt 1264 | { offset: [2, -2], array: 1 }, // xu 1265 | { offset: [2, -1], array: 1 }, // xv 1266 | { offset: [2, 0], array: 1 }, // xw 1267 | { offset: [2, 1], array: 1 }, // xx 1268 | { offset: [2, 2], array: 1 }, // xy 1269 | ], 1270 | body: function ( 1271 | index, 1272 | c, 1273 | xm, 1274 | fa, 1275 | fb, 1276 | fc, 1277 | fd, 1278 | fe, 1279 | ff, 1280 | fg, 1281 | fh, 1282 | fi, 1283 | fj, 1284 | fk, 1285 | fl, 1286 | fm, 1287 | fn, 1288 | fo, 1289 | fp, 1290 | fq, 1291 | fr, 1292 | fs, 1293 | ft, 1294 | fu, 1295 | fv, 1296 | fw, 1297 | fx, 1298 | fy, 1299 | xa, 1300 | xb, 1301 | xc, 1302 | xd, 1303 | xe, 1304 | xf, 1305 | xg, 1306 | xh, 1307 | xi, 1308 | xj, 1309 | xk, 1310 | xl, 1311 | xn, 1312 | xo, 1313 | xp, 1314 | xq, 1315 | xr, 1316 | xs, 1317 | xt, 1318 | xu, 1319 | xv, 1320 | xw, 1321 | xx, 1322 | xy 1323 | ) { 1324 | c = 1325 | index[0] < 2 || index[1] < 2 1326 | ? 0 1327 | : xa * fy + 1328 | xb * fx + 1329 | xc * fw + 1330 | xd * fv + 1331 | xe * fu + 1332 | xf * ft + 1333 | xg * fs + 1334 | xh * fr + 1335 | xi * fq + 1336 | xj * fp + 1337 | xk * fo + 1338 | xl * fn + 1339 | xm * fm + 1340 | xn * fl + 1341 | xo * fk + 1342 | xp * fj + 1343 | xq * fi + 1344 | xr * fh + 1345 | xs * fg + 1346 | xt * ff + 1347 | xu * fe + 1348 | xv * fd + 1349 | xw * fc + 1350 | xx * fb + 1351 | xy * fa; 1352 | }, 1353 | }); 1354 | 1355 | function createArray( 1356 | arr: NdArray | ArbDimNumArray | number | TypedArray, 1357 | dtype?: DType | ArrayLikeConstructor 1358 | ): NdArray { 1359 | if (arr instanceof NdArray) { 1360 | return arr; 1361 | } 1362 | let T: any; 1363 | // this condition is to fix https://github.com/grimmer0125/numjs/pull/9 1364 | if (dtype) { 1365 | T = _.getType(dtype); 1366 | } 1367 | if (_.isNumber(arr)) { 1368 | if (T && T !== Array) { 1369 | return new NdArray(new T([arr as number]), [1]); 1370 | } else { 1371 | return new NdArray([arr as number], [1]); 1372 | } 1373 | } 1374 | 1375 | const shape = _.getShape(arr); 1376 | if (shape.length > 1) { 1377 | arr = _.flatten(arr, true); 1378 | } 1379 | 1380 | if (T && !(arr instanceof T)) { 1381 | // below is to fix https://github.com/grimmer0125/numjs/pull/9 1382 | if (arr instanceof Array) { 1383 | arr = new T(arr); 1384 | } else if (T === Array) { 1385 | arr = Array.from(arr as TypedArray); 1386 | } else { 1387 | throw new errors.ValueError( 1388 | "Passed TypedArray and (Typed) dtype are not the same types, not support these conversions yet" 1389 | ); 1390 | } 1391 | } 1392 | return new NdArray(arr as OneDimNumArray, shape); 1393 | } 1394 | 1395 | // NdArray.new = createArray; 1396 | 1397 | /* utils */ 1398 | function initNativeArray(shape, i) { 1399 | i = i || 0; 1400 | const c = shape[i] | 0; 1401 | if (c <= 0) { 1402 | return []; 1403 | } 1404 | const result = new Array(c); 1405 | let j; 1406 | if (i === shape.length - 1) { 1407 | for (j = 0; j < c; ++j) { 1408 | result[j] = 0; 1409 | } 1410 | } else { 1411 | for (j = 0; j < c; ++j) { 1412 | result[j] = initNativeArray(shape, i + 1); 1413 | } 1414 | } 1415 | return result; 1416 | } 1417 | 1418 | /* istanbul ignore next */ 1419 | const doUnpack = cwise({ 1420 | args: ["array", "scalar", "index"], 1421 | body: function unpackCwise(arr, a, idx) { 1422 | let v = a; 1423 | let i; 1424 | for (i = 0; i < idx.length - 1; ++i) { 1425 | v = v[idx[i]]; 1426 | } 1427 | v[idx[idx.length - 1]] = arr; 1428 | }, 1429 | }); 1430 | 1431 | function unpackArray(arr) { 1432 | const result = initNativeArray(arr.shape, 0); 1433 | doUnpack(arr, result); 1434 | return result; 1435 | } 1436 | 1437 | function formatNumber(v) { 1438 | return String(Number((v || 0).toFixed(CONF.nFloatingValues))); 1439 | } 1440 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import DTYPES from "./dtypes"; 3 | 4 | function isNumber(value) { 5 | return typeof value === "number"; 6 | } 7 | function isString(value) { 8 | return typeof value === "string"; 9 | } 10 | function isFunction(value) { 11 | return typeof value === "function"; 12 | } 13 | 14 | function baseFlatten(array, isDeep, result?: number[]) { 15 | result = result || []; 16 | let index = -1; 17 | const length = array.length; 18 | 19 | while (++index < length) { 20 | let value = array[index]; 21 | if (isNumber(value)) { 22 | result[result.length] = value; 23 | } else if (isDeep) { 24 | // Recursively flatten arrays (susceptible to call stack limits). 25 | baseFlatten(value, isDeep, result); 26 | } else { 27 | result.push(value); 28 | } 29 | } 30 | 31 | return result; 32 | } 33 | 34 | function shapeSize(shape) { 35 | let s = 1; 36 | for (let i = 0; i < shape.length; i++) { 37 | s *= shape[i]; 38 | } 39 | return s; 40 | } 41 | 42 | function getType(dtype) { 43 | return isFunction(dtype) ? dtype : DTYPES[dtype] || Array; 44 | } 45 | 46 | function _dim(x) { 47 | const ret = []; 48 | while (typeof x === "object") { 49 | ret.push(x.length); 50 | x = x[0]; 51 | } 52 | return ret; 53 | } 54 | 55 | function getShape(array) { 56 | let y, z; 57 | if (typeof array === "object") { 58 | y = array[0]; 59 | if (typeof y === "object") { 60 | z = y[0]; 61 | if (typeof z === "object") { 62 | return _dim(array); 63 | } 64 | return [array.length, y.length]; 65 | } 66 | return [array.length]; 67 | } 68 | return []; 69 | } 70 | 71 | // function haveSameShape (shape1, shape2) { 72 | // if (shapeSize(shape1) !== shapeSize(shape2) || shape1.length !== shape2.length) { 73 | // return false; 74 | // } 75 | // let d = shape1.length; 76 | // for (let i = 0; i < d; i++) { 77 | // if (shape1[i] !== shape2[i]) { 78 | // return false; 79 | // } 80 | // } 81 | // return true; 82 | // } 83 | 84 | export default { 85 | isNumber: isNumber, 86 | isString: isString, 87 | isFunction: isFunction, 88 | flatten: baseFlatten, 89 | shapeSize: shapeSize, 90 | getType: getType, 91 | getShape: getShape, 92 | }; 93 | -------------------------------------------------------------------------------- /test/mocha/abs.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('abs', function () { 8 | it('should work on vectors', function () { 9 | const x = nj.array([-1, 0, 1]); 10 | expect(nj.abs(x).tolist()) 11 | .to.eql([1, 0, 1]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/mocha/add.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('add', function () { 8 | let v; 9 | let m; 10 | beforeEach(function () { 11 | v = nj.arange(3); 12 | m = nj.arange(3 * 2).reshape([3, 2]); 13 | }); 14 | it('can add a scalar to a vector and create a new copy', function () { 15 | const newV = nj.add(v, 1); 16 | expect(newV).not.to.equal(v); // should have create a copy 17 | expect(newV.tolist()) 18 | .to.eql([1, 2, 3]); 19 | }); 20 | it('can add a scalar to a vector without crating a copy', function () { 21 | const newV = v.add(1, false); 22 | expect(newV).to.equal(v); // should NOT have create a copy 23 | expect(v.tolist()) 24 | .to.eql([1, 2, 3]); 25 | }); 26 | 27 | it('can sum 2 vector', function () { 28 | const newV = v.add(v); 29 | expect(newV).not.to.equal(v); // should have create a copy 30 | expect(newV.tolist()) 31 | .to.eql([0, 2, 4]); 32 | }); 33 | 34 | it('can add a scalar to a matrix', function () { 35 | const newMatrix = m.add(1); 36 | expect(newMatrix).not.to.equal(m); // should have create a copy 37 | expect(newMatrix.tolist()) 38 | .to.eql([[1, 2], [3, 4], [5, 6]]); 39 | }); 40 | 41 | it('can sum 2 matrx', function () { 42 | const newV = m.add(m); 43 | expect(newV).not.to.equal(m); // should have create a copy 44 | expect(newV.tolist()) 45 | .to.eql([[0, 2], [4, 6], [8, 10]]); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/mocha/arange.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('arange', function () { 8 | it('should work if only stop given', function () { 9 | const arr = nj.arange(3); 10 | expect(arr.tolist()).to.eql([0, 1, 2]); 11 | expect(arr.shape).to.eql([3]); 12 | }); 13 | it('should work if both start and stop are given', function () { 14 | const arr = nj.arange(3, 7); 15 | expect(arr.tolist()).to.eql([3, 4, 5, 6]); 16 | expect(arr.shape).to.eql([4]); 17 | }); 18 | it('should work if start, stop and step are given', function () { 19 | const arr = nj.arange(3, 7, 2); 20 | expect(arr.tolist()).to.eql([3, 5]); 21 | expect(arr.shape).to.eql([2]); 22 | }); 23 | 24 | it('should accept a dtype', function () { 25 | expect(nj.arange(3, 'uint8').dtype).to.equal('uint8'); 26 | expect(nj.arange(0, 3, 'uint8').dtype).to.equal('uint8'); 27 | expect(nj.arange(0, 3, 1, 'uint8').dtype).to.equal('uint8'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/mocha/arccos.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('arccos', function () { 8 | it('should work on vectors', function () { 9 | const x = nj.array([-1, 0, 1]); 10 | expect(nj.arccos(x).tolist()) 11 | .to.eql([ Math.PI, Math.PI / 2, 0 ]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/mocha/arcsin.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('arcsin', function () { 8 | it('should work on vectors', function () { 9 | const x = nj.array([-1, 0, 1]); 10 | expect(nj.arcsin(x).tolist()) 11 | .to.eql([ -Math.PI / 2, 0, Math.PI / 2 ]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/mocha/arctan.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('arctan', function () { 8 | it('should work on vectors', function () { 9 | const x = nj.array([-1, 0, 1]); 10 | expect(nj.arctan(x).tolist()) 11 | .to.eql([ -Math.PI / 4, 0, Math.PI / 4 ]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/mocha/array.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('array', function () { 8 | it('can be empty', function () { 9 | const arr = nj.array([]); 10 | expect(arr.tolist()).to.eql([]); 11 | expect(arr.toString()).to.eql('array([])'); 12 | expect(arr.toJSON()).to.eql('[]'); 13 | expect(arr.shape).to.eql([0]); 14 | }); 15 | 16 | it('can use a given dtype', function () { 17 | expect(nj.array([], 'uint8').dtype).to.eql('uint8'); 18 | expect(nj.uint8([]).dtype).to.eql('uint8'); 19 | }); 20 | 21 | it('should create a copy of the given multi dimensionnal array', function () { 22 | const init = [[0, 1], [2, 3]]; 23 | const arr = nj.array(init); 24 | expect(arr.shape).to.eql([2, 2]); 25 | expect(arr.tolist()).to.eql(init); 26 | init[0][0] = 1; 27 | expect(arr.tolist()).to.eql([[0, 1], [2, 3]]); 28 | }); 29 | 30 | it('should not create a copy of the given vector', function () { 31 | const init = [0, 1, 2, 3]; 32 | const arr = nj.array(init); 33 | expect(arr.shape).to.eql([4]); 34 | expect(arr.tolist()).to.eql(init); 35 | init[0] = 1; 36 | expect(arr.tolist()).to.eql(init); 37 | }); 38 | it('should considere numbers as a 1 element array', function () { 39 | const arr = nj.array(2); 40 | expect(arr.shape).to.eql([1]); 41 | expect(arr.tolist()).to.eql([2]); 42 | }); 43 | it('can be converted to uint8', function () { 44 | const vect = nj.zeros(12); 45 | expect(vect.dtype).to.equal('array'); 46 | vect.dtype = 'uint8'; 47 | expect(vect.dtype).to.equal('uint8'); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/mocha/assign.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('assign', function () { 8 | let v; 9 | beforeEach(function () { 10 | v = nj.arange(3); 11 | }); 12 | it('can assign a scalar to a vector and create a new copy', function () { 13 | const newV = v.assign(1); 14 | expect(newV).not.to.equal(v); // should have create a copy 15 | expect(newV.tolist()) 16 | .to.eql([1, 1, 1]); 17 | }); 18 | it('can assign a scalar to a vector without crating a copy', function () { 19 | const newV = v.assign(1, false); 20 | expect(newV).to.equal(v); // should NOT have create a copy 21 | expect(v.tolist()) 22 | .to.eql([1, 1, 1]); 23 | }); 24 | 25 | it('can assign a vector to another', function () { 26 | const newV = v.assign(v.add(1)); 27 | expect(newV).not.to.equal(v); // should have create a copy 28 | expect(newV.tolist()) 29 | .to.eql([1, 2, 3]); 30 | }); 31 | 32 | it('can assign to a matrix without creating a copy', function () { 33 | const zeros = nj.zeros([3, 4]); 34 | zeros.slice([1, -1], [1, -1]).assign(1, false); 35 | 36 | expect(zeros.tolist()) 37 | .to.eql([ 38 | [0, 0, 0, 0], 39 | [0, 1, 1, 0], 40 | [0, 0, 0, 0]]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/mocha/clip.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('clip', function () { 8 | it('should work on vectors', function () { 9 | const x = nj.array([-1, 0, 1]); 10 | expect(nj.clip(x, 0, Number.POSITIVE_INFINITY).tolist()) 11 | .to.eql([0, 0, 1]); 12 | expect(nj.clip(x, Number.NEGATIVE_INFINITY, 0).tolist()) 13 | .to.eql([-1, 0, 0]); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/clone.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('clone', function () { 8 | let x, c; 9 | beforeEach(function () { 10 | x = nj.arange(3, 'uint8'); 11 | c = x.clone(); 12 | }); 13 | it('should create a deep copy', function () { 14 | expect(c.get(0)).to.equal(0); 15 | c.set(0, 1); 16 | expect(c.get(0)).to.equal(1); 17 | expect(x.get(0)).to.equal(0); 18 | }); 19 | it('should preserve dtype', function () { 20 | expect(c.dtype).to.equal('uint8'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/mocha/concatenate.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | import { ValueError } from '../../src/lib/errors'; 8 | 9 | describe('concat', function () { 10 | describe('with numbers', function () { 11 | let c; 12 | beforeEach(function () { 13 | c = nj.concatenate([1, 0]); 14 | }); 15 | it('should produce a vector', function () { 16 | expect(c.tolist()).to.eql([1, 0]); 17 | }); 18 | }); 19 | 20 | it('can concatenate 2 vectors', function () { 21 | expect(nj.concatenate([[0], [1]]).tolist()) 22 | .to.eql([0, 1]); 23 | expect(nj.concatenate([[0], [1, 2, 3]]).tolist()) 24 | .to.eql([0, 1, 2, 3]); 25 | expect(nj.concatenate([[0], [1, 2, 3], [4]]).tolist()) 26 | .to.eql([0, 1, 2, 3, 4]); 27 | }); 28 | 29 | it('should raise an error when trying to concat array with different dims', function () { 30 | const a = nj.arange(12).reshape([4, 3]); 31 | const b = nj.arange(4).add(1); 32 | expect(function () { 33 | nj.concatenate([a, b]); 34 | }).to.throw(ValueError,'all the input arrays must have same number of dimensions'); 35 | }); 36 | 37 | it('should concatenate multidimensional arrays along the last axis', function () { 38 | const a = nj.arange(12).reshape([4, 3]); 39 | const b = nj.arange(4).add(1).reshape([4, 1]); 40 | const c = nj.arange(4 * 3 * 2).reshape([4, 3, 2]); // (4,3,2) 41 | 42 | expect(nj.concatenate([a, b]).tolist()) 43 | .to.eql([ 44 | [0, 1, 2, 1], 45 | [3, 4, 5, 2], 46 | [6, 7, 8, 3], 47 | [9, 10, 11, 4]]); 48 | expect(nj.concatenate([b, a]).tolist()) 49 | .to.eql([ 50 | [1, 0, 1, 2], 51 | [2, 3, 4, 5], 52 | [3, 6, 7, 8], 53 | [4, 9, 10, 11]]); 54 | expect(nj.concatenate([b, b]).tolist()) 55 | .to.eql([ 56 | [1, 1], 57 | [2, 2], 58 | [3, 3], 59 | [4, 4]]); 60 | expect(nj.concatenate([a, a]).tolist()) 61 | .to.eql([ 62 | [0, 1, 2, 0, 1, 2], 63 | [3, 4, 5, 3, 4, 5], 64 | [6, 7, 8, 6, 7, 8], 65 | [9, 10, 11, 9, 10, 11]]); 66 | expect(nj.concatenate([c, c]).tolist()) 67 | .to.eql([ 68 | [[0, 1, 0, 1], 69 | [2, 3, 2, 3], 70 | [4, 5, 4, 5]], 71 | [[6, 7, 6, 7], 72 | [8, 9, 8, 9], 73 | [10, 11, 10, 11]], 74 | [[12, 13, 12, 13], 75 | [14, 15, 14, 15], 76 | [16, 17, 16, 17]], 77 | [[18, 19, 18, 19], 78 | [20, 21, 20, 21], 79 | [22, 23, 22, 23]]]); 80 | expect(nj.concatenate([a.reshape([4, 3, 1]), c]).tolist()) 81 | .to.eql([ 82 | [[0, 0, 1], 83 | [1, 2, 3], 84 | [2, 4, 5]], 85 | [[3, 6, 7], 86 | [4, 8, 9], 87 | [5, 10, 11]], 88 | [[6, 12, 13], 89 | [7, 14, 15], 90 | [8, 16, 17]], 91 | [[9, 18, 19], 92 | [10, 20, 21], 93 | [11, 22, 23]]]); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /test/mocha/convolve.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('convolve', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.float32([0, 0, 1, 2, 1, 0, 0]); 11 | const filter = [-1, 0, 1]; 12 | const conv = nj.convolve(x, filter); 13 | expect(conv.round().tolist()) 14 | .to.eql([-1, -2, 0, 2, 1]); 15 | expect(conv.dtype).to.equal('float32'); 16 | }); 17 | 18 | it('should work with 3x3 filter', function () { 19 | const x = nj.arange(5 * 5).reshape(5, 5); 20 | const filter = nj.arange(9).reshape(3, 3); 21 | expect(x.convolve(filter).round().tolist()) 22 | .to.eql([ 23 | [120, 156, 192], 24 | [300, 336, 372], 25 | [480, 516, 552]]); 26 | }); 27 | 28 | it('should work with 2x3 filter', function () { 29 | const x = nj.arange(5 * 5).reshape(5, 5); 30 | const filter = nj.arange(6).reshape(2, 3); 31 | expect(x.convolve(filter).round().tolist()) 32 | .to.eql([ 33 | [26, 41, 56], 34 | [101, 116, 131], 35 | [176, 191, 206], 36 | [251, 266, 281]]); 37 | }); 38 | 39 | it('should work with 3x2 filter', function () { 40 | const x = nj.arange(5 * 5).reshape(5, 5); 41 | const filter = nj.arange(6).reshape(3, 2); 42 | expect(x.convolve(filter).round().tolist()) 43 | .to.eql([ 44 | [41, 56, 71, 86], 45 | [116, 131, 146, 161], 46 | [191, 206, 221, 236]]); 47 | }); 48 | 49 | it('should work with 3x3x1 filter', function () { 50 | const x = nj.arange(5 * 5).reshape(5, 5, 1); 51 | const filter = nj.arange(9).reshape(3, 3, 1); 52 | const c = x.convolve(filter); 53 | expect(c.shape).to.eql([5 - 3 + 1, 5 - 3 + 1, 1]); 54 | expect(c.tolist()) 55 | .to.eql(nj.array([ 56 | [120, 156, 192], 57 | [300, 336, 372], 58 | [480, 516, 552]]).reshape(3, 3, 1).tolist()); 59 | }); 60 | 61 | it('should work with 5x5 filter', function () { 62 | const x = nj.arange(25).reshape(5, 5); 63 | const filter = nj.arange(25).reshape(5, 5); 64 | expect(x.convolve(filter).tolist()) 65 | .to.eql([[2300]]); 66 | }); 67 | 68 | it('should work with 5x5x1 filter', function () { 69 | const x = nj.arange(25).reshape(5, 5, 1); 70 | const filter = nj.arange(25).reshape(5, 5, 1); 71 | const c = x.convolve(filter); 72 | expect(c.shape).to.eql([1, 1, 1]); 73 | expect(c.tolist()) 74 | .to.eql([[[2300]]]); 75 | }); 76 | 77 | it('should be fast with 3x3 filter even if X is large', function () { 78 | this.timeout(1000); 79 | const N = 1000; 80 | const x = nj.arange(N * N).reshape(N, N); 81 | const filter = nj.arange(9).reshape(3, 3); 82 | x.convolve(filter); 83 | }); 84 | 85 | it('should be fast with 3x3x1 filter even if X is large', function () { 86 | this.timeout(1000); 87 | const N = 1000; 88 | const x = nj.arange(N * N).reshape(N, N, 1); 89 | const filter = nj.arange(9).reshape(3, 3, 1); 90 | x.convolve(filter); 91 | }); 92 | 93 | it('should be fast with 5x5 filter even if X is large', function () { 94 | this.timeout(1000); 95 | const N = 1000; 96 | const x = nj.arange(N * N).reshape(N, N); 97 | const filter = nj.arange(25).reshape(5, 5); 98 | x.convolve(filter); 99 | }); 100 | 101 | it('should be fast with 5x5x1 filter even if X is large', function () { 102 | this.timeout(1000); 103 | const N = 1000; 104 | const x = nj.arange(N * N).reshape(N, N, 1); 105 | const filter = nj.arange(25).reshape(5, 5, 1); 106 | x.convolve(filter); 107 | }); 108 | }); 109 | 110 | describe('fftconvolve', function () { 111 | it('should work on vectors', function () { 112 | const x = nj.float32([0, 0, 1, 2, 1, 0, 0]); 113 | const filter = [-1, 0, 1]; 114 | const conv = nj.fftconvolve(x, filter); 115 | expect(conv.round().tolist()) 116 | .to.eql([-1, -2, 0, 2, 1]); 117 | expect(conv.dtype).to.equal('float32'); 118 | }); 119 | 120 | it('should work with 3x3 filter', function () { 121 | const x = nj.arange(5 * 5).reshape(5, 5); 122 | const filter = nj.arange(9).reshape(3, 3); 123 | expect(x.fftconvolve(filter).round().tolist()) 124 | .to.eql([ 125 | [120, 156, 192], 126 | [300, 336, 372], 127 | [480, 516, 552]]); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /test/mocha/cos.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('cos', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.array([0, Math.PI / 2, Math.PI]); 11 | expect(nj.cos(x).round().tolist()) 12 | .to.eql([1, 0, -1]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/diag.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | import nj from "../../src"; 6 | 7 | describe('diag', () => { 8 | it('should extract a diagonal if input is a matrix', () => { 9 | const x = nj.arange(12).reshape([4, 3]); 10 | expect(nj.diag(x).tolist()) 11 | .to.eql([0, 4, 8]); 12 | }); 13 | 14 | it('should produce a matrix if input is a vector', () => { 15 | const x = nj.arange(3); 16 | expect(nj.diag(x).tolist()) 17 | .to.eql([[0, 0, 0], 18 | [0, 1, 0], 19 | [0, 0, 2]]); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/mocha/divide.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('divide', function () { 9 | it('can divide a vector with a scalar and create a new copy', function () { 10 | const x = nj.arange(3); 11 | const scalar = 2; 12 | const newX = x.divide(scalar); 13 | expect(newX).not.to.equal(x); 14 | expect(newX.tolist()) 15 | .to.eql([0, 0.5, 1]); 16 | }); 17 | it('can divide a vector with a scalar without creating a copy', function () { 18 | const x = nj.arange(3); 19 | const scalar = 2; 20 | const newX = x.divide(scalar, false); 21 | expect(newX).to.equal(x); 22 | expect(newX.tolist()) 23 | .to.eql([0, 0.5, 1]); 24 | }); 25 | it('can divide two vectors', function () { 26 | const v = nj.ones([3]); 27 | expect(v.divide(v).tolist()) 28 | .to.eql(v.tolist()); 29 | }); 30 | it('can divide two matrix with the same shape', function () { 31 | const m = nj.ones(6).reshape([3, 2]); 32 | expect(m.divide(m).tolist()) 33 | .to.eql(m.tolist()); 34 | }); 35 | it('should throw an error when dividing an array with a vector', function () { 36 | expect(function () { 37 | const x1 = nj.arange(9).reshape(3, 3); 38 | const x2 = nj.arange(3); 39 | nj.divide(x1, x2); 40 | }).to.throw(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/mocha/dot.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src" 7 | 8 | describe('dot', function () { 9 | describe('on vectors', function () { 10 | let v3, v12; 11 | 12 | beforeEach(function () { 13 | v3 = nj.arange(3); v12 = nj.arange(12); 14 | }); 15 | 16 | it('should work if vectors have the same length', function () { 17 | expect(nj.dot(v3, v3).tolist()).to.eql([5]); 18 | expect(nj.dot(v12, v12).tolist()).to.eql([506]); 19 | }); 20 | 21 | it('should throw an error lengths are different', function () { 22 | expect(function () { nj.dot(v3, v12); }).to.throw(); 23 | }); 24 | }); 25 | 26 | it('should work on matrix', function () { 27 | const a = nj.arange(12).reshape([4, 3]); 28 | const b = nj.arange(12).reshape([3, 4]); 29 | expect(nj.dot(a, b).tolist()).to.eql([ 30 | [20, 23, 26, 29], 31 | [56, 68, 80, 92], 32 | [92, 113, 134, 155], 33 | [128, 158, 188, 218] 34 | ]); 35 | expect(a.dot(b).tolist()).to.eql([ 36 | [20, 23, 26, 29], 37 | [56, 68, 80, 92], 38 | [92, 113, 134, 155], 39 | [128, 158, 188, 218] 40 | ]); 41 | expect(nj.dot(b, a).tolist()).to.eql([ 42 | [42, 48, 54], 43 | [114, 136, 158], 44 | [186, 224, 262] 45 | ]); 46 | expect(b.dot(a).tolist()).to.eql([ 47 | [42, 48, 54], 48 | [114, 136, 158], 49 | [186, 224, 262] 50 | ]); 51 | }); 52 | 53 | it('should be able to multiply a vector with a matrix', function () { 54 | const a = nj.arange(2); 55 | const b = nj.arange(6).reshape([2, 3]); 56 | expect(nj.dot(a, b).tolist()) 57 | .to.eql([3, 4, 5]); 58 | expect(nj.dot(b.T, a).tolist()) 59 | .to.eql([3, 4, 5]); 60 | }); 61 | it('should be fast on vectors even if they are very large', function () { 62 | const N = 100000; 63 | const a = nj.ones([N]); 64 | expect(a.dot(a).tolist()).to.eql([N]); 65 | }); 66 | it('should be fast on V.M even if they are very large', function () { 67 | const n = 1000; 68 | const m = 1000; 69 | const V = nj.ones([n]); 70 | const M = nj.ones([n, m]); 71 | const VdotM = nj.ones([m]).multiply(n, false); 72 | expect(V.dot(M).tolist()) 73 | .to.eql(VdotM.tolist()); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/mocha/empty.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('empty', function () { 9 | it('can generate a vectors', function () { 10 | expect(nj.empty(0).tolist()).to.eql([]); 11 | expect(nj.empty(2).tolist()).to.eql([undefined, undefined]); 12 | expect(nj.empty([2]).tolist()).to.eql([undefined, undefined]); 13 | }); 14 | 15 | it('can generate matrix', function () { 16 | expect(nj.empty([2, 2]).tolist()) 17 | .to.eql([[undefined, undefined], [undefined, undefined]]); 18 | }); 19 | 20 | it('should accept a dtype', function () { 21 | expect(nj.empty(0, 'uint8').dtype).to.equal('uint8'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/mocha/equal.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('equal', function () { 9 | it('should be true if all items are the same', function () { 10 | const arr = nj.arange(3); 11 | expect(nj.equal(arr, [0, 1, 2])).to.equal(true); 12 | expect(nj.equal(arr.reshape(3, 1), arr.reshape(3, 1))).to.equal(true); 13 | }); 14 | it('should not changes arrays values', function () { 15 | let a = nj.array([1, 2, 3, 4, 0, 0, 0, 0]).reshape(4, 2); 16 | let b = nj.array([1, 2, 3, 4, 0, 0, 0, 0]).reshape(4, 2); 17 | expect(nj.equal(a, b)).to.equal(true); 18 | expect(nj.equal(a, b)).to.equal(true); // see https://github.com/nicolaspanel/numjs/issues/88 19 | }); 20 | it('should be false if arrays do not have the same shape', function () { 21 | const arr = nj.arange(3); 22 | expect(nj.equal(arr, [0, 1])).to.equal(false); 23 | expect(nj.equal(arr, arr.reshape(3, 1))).to.equal(false); 24 | expect(nj.equal(arr, arr.reshape(1, 3))).to.equal(false); 25 | expect(nj.equal(arr.reshape(1, 3), arr.reshape(3, 1))).to.equal(false); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/mocha/errors.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import * as errors from "../../src/lib/errors"; 7 | import { ValueError, ConfigError, NotImplementedError} from "../../src/lib/errors" 8 | 9 | describe('errors', function () { 10 | it('can be a ValueError', function () { 11 | expect(function () { 12 | throw new errors.ValueError('txt...'); 13 | }).to.throw(ValueError, 'txt...'); 14 | }); 15 | it('can be a ConfigError', function () { 16 | expect(function () { 17 | throw new errors.ConfigError('txt...'); 18 | }).to.throw(ConfigError,'txt...'); 19 | }); 20 | it('can be a NotImplementedError', function () { 21 | expect(function () { 22 | throw new errors.NotImplementedError(); 23 | }).to.throw(NotImplementedError); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/mocha/exp.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('exp', function () { 9 | it('should work on scalars', function () { 10 | expect(nj.exp(0).tolist()) 11 | .to.eql([1]); 12 | }); 13 | it('should work on vectors', function () { 14 | const x = nj.arange(3); 15 | expect(nj.exp(x).tolist()) 16 | .to.eql([1, Math.exp(1), Math.exp(2)]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mocha/fft.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('fft', function () { 9 | it('should work on vectors', function () { 10 | const C = nj.random([10, 2]); 11 | const fft = nj.fft(C); 12 | const ifft = nj.ifft(fft); 13 | expect(ifft.multiply(10000).round().tolist()) 14 | .to.eql(C.multiply(10000).round().tolist()); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/mocha/flatten.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('flatten', function () { 9 | it('should work on vectors', function () { 10 | expect(nj.flatten([[0], [1], [2]]).tolist()) 11 | .to.eql([0, 1, 2]); 12 | 13 | expect(nj.flatten([[0, 1, 2]]).tolist()) 14 | .to.eql([0, 1, 2]); 15 | }); 16 | it('should work on slided matrix', function () { 17 | expect(nj.arange(25).reshape(5, 5).lo(1, 1).hi(2, 2).flatten().tolist()) 18 | .to.eql([6, 7, 11, 12]); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/mocha/flip.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('flip', function () { 9 | 10 | it('should work with ndarray', function () { 11 | const m = nj.arange(8).reshape([2,2,2]); 12 | expect(nj.flip(m, 0).tolist()).to.eql([ 13 | [[4, 5], 14 | [6, 7]], 15 | [[0, 1], 16 | [2, 3]]]); 17 | expect(nj.flip(m, 1).tolist()).to.eql([ 18 | [[2, 3], 19 | [0, 1]], 20 | [[6, 7], 21 | [4, 5]]]); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/mocha/get.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('get', function () { 9 | describe('on 1d array', function () { 10 | let a; 11 | beforeEach(function () { 12 | a = nj.arange(3); 13 | }); 14 | it('can locate with positive index', function () { 15 | expect(a.get(1)).to.equal(1); 16 | }); 17 | it('can locate with positive index', function () { 18 | expect(a.get(1)).to.equal(1); 19 | }); 20 | it('can locate with negative index', function () { 21 | expect(a.get(-1)).to.equal(2); 22 | }); 23 | }); 24 | describe('on 2d array', function () { 25 | let a; 26 | beforeEach(function () { 27 | a = nj.arange(3 * 3).reshape(3, 3); 28 | }); 29 | it('should work with positive index', function () { 30 | expect(a.get(1, 1)).to.equal(4); 31 | }); 32 | it('should accept negative integer', function () { 33 | expect(a.get(-1, -1)).to.equal(8); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/mocha/hi.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('hi', function () { 9 | it('should truncates from the bottom-right of the array', function () { 10 | expect(nj.arange(4 * 4).reshape([4, 4]).hi(2, 2).tolist()) 11 | .to.eql([ 12 | [0, 1], 13 | [4, 5]]); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/identity.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('identity', function () { 9 | it('should return an n x n array with its main diagonal set to one, and all other elements 0.', function () { 10 | expect(nj.identity(3).tolist()) 11 | .to.eql([[1, 0, 0], 12 | [0, 1, 0], 13 | [0, 0, 1]]); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/lo.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('lo', function () { 9 | it('should creates a shifted view of the array', function () { 10 | expect(nj.arange(4 * 4).reshape([4, 4]).lo(2, 2).tolist()) 11 | .to.eql([ 12 | [10, 11], 13 | [14, 15]]); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/log.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('log', function () { 9 | it('should work on scalars', function () { 10 | expect(nj.log(1).tolist()) 11 | .to.eql([0]); 12 | }); 13 | it('should work on vectors', function () { 14 | const x = nj.arange(3); 15 | expect(nj.log(nj.exp(x)).tolist()) 16 | .to.eql(x.tolist()); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mocha/max.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('max', function () { 9 | it('should be null for an empty array', function () { 10 | const arr = nj.array([]); 11 | expect(arr.max()).to.equal(null); 12 | }); 13 | it('should return the max element in array', function () { 14 | const arr = nj.arange(10); 15 | expect(arr.max()).to.equal(9); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/mocha/mean.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('mean', function () { 9 | it('should work on vectors', function () { 10 | expect(nj.array([-1, 1]).mean()).to.equal(0); 11 | expect(nj.arange(7).mean()).to.equal(3); 12 | expect(nj.arange(10).mean()).to.equal(4.5); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/min.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('min', function () { 9 | it('should be null for an empty array', function () { 10 | const arr = nj.array([]); 11 | expect(arr.min()).to.equal(null); 12 | }); 13 | it('should return the min element in array', function () { 14 | const arr = nj.arange(10); 15 | expect(arr.min()).to.equal(0); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/mocha/mod.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('mod', function () { 9 | [{ 10 | x1: [4, 7], 11 | x2: [2, 3], 12 | expected: [0, 1], 13 | }, { 14 | x1: nj.arange(7), 15 | x2: 5, 16 | expected: [0, 1, 2, 3, 4, 0, 1], 17 | }].forEach(function (test) { 18 | it(`should compute (${nj.array(test.x1)} % ${nj.array(test.x1)}) => ${nj.array(test.expected)}`, function () { 19 | expect(nj.mod(test.x1, test.x2).tolist()).to.eql(test.expected); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/mocha/multiply.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('multiply', function () { 9 | it('can multiply a vector with a scalar and create a new copy', function () { 10 | const x = nj.arange(3); 11 | const scalar = 2; 12 | const expected = [0, 2, 4]; 13 | const newX = x.multiply(scalar); 14 | expect(newX).not.to.equal(x); 15 | expect(newX.tolist()) 16 | .to.eql(expected); 17 | }); 18 | it('can multiply a vector with a scalar without creating a copy', function () { 19 | const x = nj.arange(3); 20 | const scalar = 2; 21 | const expected = [0, 2, 4]; 22 | const newX = x.multiply(scalar, false); 23 | expect(newX).to.equal(x); 24 | expect(newX.tolist()) 25 | .to.eql(expected); 26 | }); 27 | it('can multiply two vectors', function () { 28 | const v = nj.arange(3); 29 | expect(v.multiply(v).tolist()) 30 | .to.eql([0, 1, 4]); 31 | }); 32 | it('can multiply two matrix with the same shape', function () { 33 | const m = nj.arange(6).reshape([3, 2]); 34 | expect(m.multiply(m).tolist()) 35 | .to.eql([ 36 | [0, 1], 37 | [4, 9], 38 | [16, 25]]); 39 | }); 40 | it('should throw an error when multiplying an array with a vector', function () { 41 | expect(function () { 42 | const x1 = nj.arange(9).reshape(3, 3); 43 | const x2 = nj.arange(3); 44 | nj.multiply(x1, x2); 45 | }).to.throw(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/mocha/ndim.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('ndim', function () { 9 | it('should be readable', function () { 10 | const a = nj.arange(15); 11 | expect(a.ndim).to.equal(1); 12 | expect(a.reshape(3, 5).ndim).to.equal(2); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/negative.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('negative', function () { 9 | it('should numerical negative, element-wise.', function () { 10 | expect(nj.arange(3).negative().tolist()) 11 | .to.eql([-0, -1, -2]); 12 | expect(nj.negative(nj.arange(3)).tolist()) 13 | .to.eql([-0, -1, -2]); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/ones.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('ones', function () { 9 | it('can generate a vectors', function () { 10 | expect(nj.ones(0).tolist()).to.eql([]); 11 | expect(nj.ones(2).tolist()).to.eql([1, 1]); 12 | expect(nj.ones([2]).tolist()).to.eql([1, 1]); 13 | }); 14 | 15 | it('can generate matrix', function () { 16 | expect(nj.ones([2, 2]).tolist()) 17 | .to.eql([[1, 1], [1, 1]]); 18 | }); 19 | 20 | it('should accept a dtype', function () { 21 | expect(nj.ones(0, 'uint8').dtype).to.equal('uint8'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/mocha/power.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('power', function () { 9 | it('can pow a vector with a scalar', function () { 10 | const x = nj.array([-1, 0, 1, 2]); 11 | const newX = nj.power(x, 2); 12 | expect(newX.tolist()) 13 | .to.eql([1, 0, 1, 4]); 14 | expect(newX).not.to.equal(x); // should have create a copy 15 | expect(x.tolist()) 16 | .to.eql([-1, 0, 1, 2]); 17 | }); 18 | it('can pow a vector with another vector', function () { 19 | const x = nj.array([-1, 0, 1, 2]); 20 | const newX = nj.power(x, x); 21 | expect(newX.tolist()) 22 | .to.eql([-1, 1, 1, 4]); 23 | expect(newX).not.to.equal(x); // should have create a copy 24 | }); 25 | it('can pow a vector with a scalar without crating a copy', function () { 26 | const x = nj.array([-1, 0, 1, 2]); 27 | x.pow(2, false); 28 | expect(x.tolist()) 29 | .to.eql([1, 0, 1, 4]); 30 | }); 31 | it('can pow a vector with another vector without crating a copy', function () { 32 | const x = nj.array([-1, 0, 1, 2]); 33 | x.pow(x, false); 34 | expect(x.tolist()) 35 | .to.eql([-1, 1, 1, 4]); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/mocha/random.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('random', function () { 9 | it('can generate vectors', function () { 10 | expect(nj.random(3).shape).to.eql([3]); 11 | }); 12 | 13 | it('should vectors', function () { 14 | expect(nj.random(3).shape).to.eql([3]); 15 | }); 16 | 17 | it('can generate matrix', function () { 18 | expect(nj.random([2, 1]).shape).to.eql([2, 1]); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/mocha/reshape.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | import { ValueError} from "../../src/lib/errors" 8 | 9 | function _range(n:number){ 10 | return Array.from(Array(n).keys()) 11 | } 12 | 13 | describe('reshape', function () { 14 | it('should accept native array as input', function () { 15 | const arr = nj.reshape([0, 1, 2, 3], [2, 2]); 16 | expect(arr.tolist()) 17 | .to.eql([[0, 1], [2, 3]]); 18 | expect(arr.shape).to.eql([2, 2]); 19 | }); 20 | 21 | it('should work on vectors', function () { 22 | const vector = nj.array(_range(12)); 23 | const init = vector.reshape([4, 3]); 24 | expect(init.shape) 25 | .to.eql([4, 3]); 26 | expect(init.tolist()) 27 | .to.eql([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]); 28 | }); 29 | it('should work on matrix', function () { 30 | const vector = nj.array(_range(12)); 31 | const reshaped = vector.reshape([3, 4]); 32 | expect(reshaped.shape) 33 | .to.eql([3, 4]); 34 | expect(reshaped.tolist()) 35 | .to.eql([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 36 | }); 37 | it('should preserve type', function () { 38 | expect(nj.arange(12, 'float32').reshape([4, 3]).dtype) 39 | .to.equal('float32'); 40 | }); 41 | it('should work on a sliced array but create a cpy', function () { 42 | const x = nj.arange(15).reshape([3, 5]); 43 | const reshaped = x.hi(3, 4).reshape([4, 3]); 44 | 45 | expect(x.tolist()) 46 | .to.eql([ 47 | [0, 1, 2, 3, 4], 48 | [5, 6, 7, 8, 9], 49 | [10, 11, 12, 13, 14]]); 50 | 51 | expect(reshaped.tolist()) 52 | .to.eql([ 53 | [0, 1, 2], 54 | [3, 5, 6], 55 | [7, 8, 10], 56 | [11, 12, 13]]); 57 | 58 | x.set(0, 0, 1); 59 | 60 | expect(x.tolist()) 61 | .to.eql([ 62 | [1, 1, 2, 3, 4], 63 | [5, 6, 7, 8, 9], 64 | [10, 11, 12, 13, 14]]); 65 | 66 | expect(reshaped.tolist()) 67 | .to.eql([ 68 | [0, 1, 2], 69 | [3, 5, 6], 70 | [7, 8, 10], 71 | [11, 12, 13]]); 72 | }); 73 | it('should not create a copy of the data if adding a new dim at the end', function () { 74 | const x = nj.arange(15).reshape([3, 5]); 75 | const reshaped = x.reshape([3, 5, 1]); 76 | 77 | x.set(0, 0, 1); 78 | expect(reshaped.tolist()) 79 | .to.eql([ 80 | [[1], [1], [2], [3], [4]], 81 | [[5], [6], [7], [8], [9]], 82 | [[10], [11], [12], [13], [14]]]); 83 | }); 84 | it('should not create a removing the last dim', function () { 85 | const x = nj.arange(15).reshape([3, 5, 1]); 86 | const reshaped = x.reshape([3, 5]); 87 | 88 | x.set(0, 0, 0, 1); 89 | expect(reshaped.tolist()) 90 | .to.eql([ 91 | [1, 1, 2, 3, 4], 92 | [5, 6, 7, 8, 9], 93 | [10, 11, 12, 13, 14]]); 94 | }); 95 | it('should flatten the array if shape is -1', () => { 96 | const arr = nj.arange(8) 97 | .reshape([2, 2, 2]) 98 | .reshape(-1); 99 | expect(arr.tolist()) 100 | .to.eql(nj.arange(8).tolist()); 101 | expect(arr.shape).to.eql([8]); 102 | }); 103 | it('should throw an error if more than 1 dimension is set to -1', () => { 104 | expect(() => nj.arange(8).reshape([-1, -1])) 105 | .to.throw( 106 | ValueError, 'can only specify one unknown dimension' 107 | ); 108 | }); 109 | it('should replace unknown dimension with the right value', () => { 110 | expect(nj.arange(8).reshape([4, -1]).tolist()) 111 | .to.eql([[0, 1], 112 | [2, 3], 113 | [4, 5], 114 | [6, 7]]); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /test/mocha/rot90.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | import { ValueError} from "../../src/lib/errors" 8 | 9 | describe('rot90', function () { 10 | 11 | it('should work with default params', function () { 12 | const m = nj.array([[1,2],[3,4]], 'int8'); 13 | expect(nj.rot90(m).tolist()).to.eql([[2, 4], [1, 3]]); 14 | }); 15 | 16 | it('should accept native array as input', function () { 17 | const arr = nj.rot90([[1,2],[3,4]]); 18 | expect(arr.tolist()).to.eql([[2, 4], [1, 3]]); 19 | }); 20 | 21 | it('should work when k = 2 ', function () { 22 | const m = nj.array([[1,2],[3,4]], 'int8'); 23 | expect(nj.rot90(m, 2).tolist()).to.eql([[4, 3], [2, 1]]); 24 | }); 25 | 26 | it('should work when k = 3', function () { 27 | const m = nj.array([[1,2],[3,4]], 'int8'); 28 | expect(nj.rot90(m, 3).tolist()).to.eql([[3,1],[4,2]]); 29 | }); 30 | 31 | it('should work when k = 4', function () { 32 | const m = nj.array([[1,2],[3,4]], 'int8'); 33 | expect(nj.rot90(m, 4).tolist()).to.eql([[1,2],[3,4]]); 34 | }); 35 | 36 | it('should raise an error if custom axes is not a 1d array of length 2', function () { 37 | expect(function () { 38 | nj.rot90([[1,2],[3,4]], 1, [0,1,2]); 39 | }).to.throw(ValueError,'len(axes) must be 2'); 40 | }); 41 | it('should raise an error axes are the same', function () { 42 | expect(function () { 43 | nj.rot90([[1,2],[3,4]], 1, [0, 0]); 44 | }).to.throw(ValueError, 'Axes must be different.'); 45 | }); 46 | it('should support custom axes', function () { 47 | const m = nj.array([[1,2],[3,4]], 'int8'); 48 | expect(nj.rot90(m, 1, [1,0]).tolist()).to.eql([[3,1],[4,2]]); 49 | }); 50 | it('should work on ndarrays', function () { 51 | const m = nj.arange(8).reshape([2,2,2]); 52 | expect(nj.rot90(m, 1, [1,2]).tolist()).to.eql([ 53 | [[1, 3], 54 | [0, 2]], 55 | [[5, 7], 56 | [4, 6]]]); 57 | }); 58 | }); -------------------------------------------------------------------------------- /test/mocha/round.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('round', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.arange(6).divide(5); 11 | expect(nj.round(x).tolist()) 12 | .to.eql([ 0, 0, 0, 1, 1, 1 ]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/shape.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('shape', function () { 9 | it('should be readable', function () { 10 | expect(nj.arange(3).shape) 11 | .to.eql([3]); 12 | }); 13 | it('should not be writableable', function () { 14 | expect(function () { (nj.arange(4) as any).shape = [2, 2]; }).to.throw(); 15 | }); 16 | 17 | it('should be correct if array empty', function () { 18 | expect(nj.array([]).shape) 19 | .to.eql([0]); 20 | }); 21 | it('should be correct if array is a vector', function () { 22 | expect(nj.array([0, 1, 2]).shape) 23 | .to.eql([3]); 24 | }); 25 | it('should be correct if array is a matrix', function () { 26 | expect(nj.array([[0, 1], [2, 3]]).shape) 27 | .to.eql([2, 2]); 28 | }); 29 | it('should be correct if array is a 3d array', function () { 30 | expect(nj.array([[[0], [1]], 31 | [[2], [3]]]).shape) 32 | .to.eql([2, 2, 1]); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/mocha/sigmoid.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('sigmoid', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.array([-100, -1, 0, 1, 100]); 11 | expect(nj.sigmoid(x).tolist()) 12 | .to.eql([0, 1 / (1 + Math.exp(1)), 0.5, 1 / (1 + Math.exp(-1)), 1]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/sin.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('sin', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.array([-Math.PI / 2, 0, Math.PI / 2]); 11 | expect(nj.sin(x).round().tolist()) 12 | .to.eql([-1, 0, 1]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/size.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('size', function () { 9 | it('should be readable', function () { 10 | expect(nj.arange(3).size).to.equal(3); 11 | }); 12 | it('should not be writableable', function () { 13 | expect(function () { (nj.arange(3) as any).size = 3; }).to.throw(); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha/slice.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('slice', function () { 9 | describe('on 1d array', function () { 10 | let a; 11 | beforeEach(function () { 12 | a = nj.arange(5); 13 | }); 14 | it('can skip first items', function () { 15 | expect(a.slice(1).tolist()).to.eql([1, 2, 3, 4]); // same as a[1:] 16 | expect(a.slice(-4).tolist()).to.eql([1, 2, 3, 4]); // same as a[-4:] 17 | }); 18 | it('can take first items', function () { 19 | expect(a.slice([4]).tolist()).to.eql([0, 1, 2, 3]); // same as a[:4] 20 | expect(a.slice([-1]).tolist()).to.eql([0, 1, 2, 3]); // same as a[:-1] 21 | expect(a.slice([0]).tolist()).to.eql([]); // same as a[:0] 22 | }); 23 | it('can slice using both start and end indexes', function () { 24 | expect(a.slice([1, 4]).tolist()).to.eql([1, 2, 3]); // same as a[1:4] 25 | expect(a.slice([1, -1]).tolist()).to.eql([1, 2, 3]); // same as a[1:-1] 26 | }); 27 | it('can slice using start, end and step', function () { 28 | expect(a.slice([1, 4, 2]).tolist()).to.eql([1, 3]); // same as a[1:4:2] 29 | expect(a.slice([0, 5, 2]).tolist()).to.eql([0, 2, 4]); // same as a[0:5:2] 30 | expect(a.slice([1, -1, 2]).tolist()).to.eql([1, 3]); // same as a[1:-1:2] 31 | }); 32 | it('can just provide the step', function () { 33 | expect(a.slice([null, null, -1]).tolist()).to.eql([4, 3, 2, 1, 0]); // same as a[::-1] 34 | expect(a.slice([null, null, 2]).tolist()).to.eql([0, 2, 4]); // same as a[::2] 35 | }); 36 | it('can slice using start and step', function () { 37 | expect(a.slice([1, null, null, 2]).tolist()).to.eql([1, 3]); // same as a[1::2] 38 | }); 39 | }); 40 | describe('on 2d array', function () { 41 | let a; 42 | beforeEach(function () { 43 | a = nj.arange(5 * 5).reshape(5, 5); 44 | }); 45 | it('can skip first rows', function () { 46 | expect(a.slice(2).tolist()) 47 | .to.eql([ 48 | [10, 11, 12, 13, 14], 49 | [15, 16, 17, 18, 19], 50 | [20, 21, 22, 23, 24]]); // same as a[2:] 51 | expect(a.slice(-4).tolist()) 52 | .to.eql([ 53 | [5, 6, 7, 8, 9], 54 | [10, 11, 12, 13, 14], 55 | [15, 16, 17, 18, 19], 56 | [20, 21, 22, 23, 24]]); // same as a[-4:] 57 | }); 58 | it('can skip first cols', function () { 59 | expect(a.slice(null, 2).tolist()) // same as a[:,2:] 60 | .to.eql([ 61 | [2, 3, 4], 62 | [7, 8, 9], 63 | [12, 13, 14], 64 | [17, 18, 19], 65 | [22, 23, 24]]); 66 | expect(a.slice(null, -2).tolist()) // same as a[:,-2:] 67 | .to.eql([ 68 | [3, 4], 69 | [8, 9], 70 | [13, 14], 71 | [18, 19], 72 | [23, 24]]); 73 | }); 74 | it('can skip first rows and cols', function () { 75 | expect(a.slice(2, 2).tolist()) 76 | .to.eql([ 77 | [12, 13, 14], 78 | [17, 18, 19], 79 | [22, 23, 24]]); // same as a[2:,2:] 80 | expect(a.slice(-4, 2).tolist()) 81 | .to.eql([ 82 | [7, 8, 9], 83 | [12, 13, 14], 84 | [17, 18, 19], 85 | [22, 23, 24]]); // same as a[-4:,2:] 86 | }); 87 | it('can slice in all dimensions using start, end and step', function () { 88 | expect(a.slice([1, 4, 2], [1, 4, 2]).tolist()) // same as a[1:4:2, 1:4:2] 89 | .to.eql([ 90 | [6, 8], 91 | [16, 18]]); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /test/mocha/softmax.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src" 7 | 8 | describe('softmax', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.zeros(3); 11 | const expected = [1 / 3, 1 / 3, 1 / 3]; 12 | expect(nj.softmax(x).tolist()) 13 | .to.eql(expected); 14 | }); 15 | 16 | it('should work on matrix', function () { 17 | const x = nj.zeros(4).reshape([2, 2]); 18 | const expected = [[1 / 4, 1 / 4], [1 / 4, 1 / 4]]; 19 | expect(nj.softmax(x).tolist()) 20 | .to.eql(expected); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/mocha/sqrt.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('sqrt', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.array([1, 4, 9]); 11 | expect(nj.sqrt(x).tolist()) 12 | .to.eql([1, 2, 3]); 13 | expect(x.sqrt().tolist()) 14 | .to.eql([1, 2, 3]); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/mocha/stack.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | import { ValueError} from "../../src/lib/errors" 8 | 9 | describe('stack', function () { 10 | it('should throw an error if no array given', function () { 11 | expect(function () { 12 | nj.stack([]); 13 | }) 14 | .to.throw(ValueError, 'need at least one array to stack'); 15 | }); 16 | 17 | it('should throw an error if arrays do not have the same shape', function () { 18 | expect(function () { 19 | nj.stack([1, [2]]); 20 | }) 21 | .to.throw(ValueError, 'all input arrays must have the same shape'); 22 | }); 23 | 24 | it('should work with numbers', function () { 25 | const x = [ 0, 1, 2 ]; 26 | const stacked = nj.stack(x); 27 | expect(stacked.shape).to.eql([3]); 28 | expect(stacked.tolist()).to.eql([0,1,2]); 29 | }); 30 | it('should work with vectors', function () { 31 | const x = [ [0,1], [2,3], [4,5] ]; 32 | expect(nj.stack(x).tolist()) 33 | .to.eql([[0, 1], 34 | [2, 3], 35 | [4, 5]]); 36 | }); 37 | 38 | it('should enable custom axis', function() { 39 | const a = nj.array([1, 2, 3]); 40 | const b = nj.array([2, 3, 4]); 41 | const s = nj.stack([a, b]); 42 | expect(s.shape).to.eql([2,3]); 43 | expect(s.tolist()) 44 | .to.eql([[1, 2, 3], 45 | [2, 3, 4]]); 46 | const sInv = nj.stack([a, b], -1); 47 | expect(sInv.shape).to.eql([3,2]); 48 | expect(sInv.tolist()) 49 | .to.eql([ 50 | [1, 2], 51 | [2, 3], 52 | [3, 4]]); 53 | }); 54 | 55 | it('should work with matrices', function() { 56 | const x = [1,2,3].map(function (i) { return nj.zeros([2,2]).assign(i); }); 57 | const s = nj.stack(x); 58 | expect(s.shape).to.eql([3,2,2]); 59 | expect(s.tolist()) 60 | .to.eql([[[ 1, 1], 61 | [ 1, 1]], 62 | [[ 2, 2], 63 | [ 2, 2]], 64 | [[ 3, 3], 65 | [ 3, 3]]]); 66 | 67 | const sInv = nj.stack(x, -1); 68 | expect(sInv.shape).to.eql([2,2,3]); 69 | expect(sInv.tolist()) 70 | .to.eql([[[ 1, 2, 3], 71 | [ 1, 2, 3]], 72 | [[ 1, 2, 3], 73 | [ 1, 2, 3]]]); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/mocha/std.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('std', function () { 9 | it('should work on vectors', function () { 10 | expect(nj.array([-1, 1]).std()).to.equal(1); 11 | expect(nj.arange(7).std()).to.equal(2); 12 | }); 13 | it('should be zeros if the array is full of 0s', function () { 14 | expect(nj.zeros(10).std()) 15 | .to.eql(0); 16 | }); 17 | describe('support for both sample and population variance', function () { 18 | beforeEach(function() { 19 | this.array = [3,3,3,2,4,4,3,3,3,3,4,3,3,5,3,4,2,3,4,4,3,2,3,4,3,3,3,3,3,3,2,1,4,3,4,3,3,4,4,4,2,3,4,3,2,4,3]; 20 | }); 21 | it('should default to using the sample variance', function() { 22 | const expectation = 0.752842809061879; 23 | expect(nj.array(this.array).std()).to.equal(expectation); 24 | expect(nj.std(this.array)).to.equal(expectation); 25 | }) 26 | it('should accomodate using the population variance', function() { 27 | const expectation = 0.7609818867801011; 28 | const options = { ddof: 1 }; 29 | expect(nj.array(this.array).std(options)).to.equal(expectation); 30 | expect(nj.std(this.array, options)).to.equal(expectation); 31 | }) 32 | }) 33 | }); 34 | -------------------------------------------------------------------------------- /test/mocha/subtract.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src" 7 | 8 | describe('subtract', function () { 9 | let v, m; 10 | beforeEach(function () { 11 | v = nj.arange(3); 12 | m = nj.arange(3 * 2).reshape([3, 2]); 13 | }); 14 | it('can subtract a scalar to a vector and create a new copy', function () { 15 | const newV = nj.subtract(v, 1); 16 | expect(newV).not.to.equal(v); // should have create a copy 17 | expect(newV.tolist()) 18 | .to.eql([-1, 0, 1]); 19 | }); 20 | it('can subtract a scalar to a vector without crating a copy', function () { 21 | const newV = v.subtract(1, false); 22 | expect(newV).to.equal(v); // should NOT have create a copy 23 | expect(v.tolist()) 24 | .to.eql([-1, 0, 1]); 25 | }); 26 | 27 | it('can sum 2 vector', function () { 28 | const newV = v.subtract(v); 29 | expect(newV).not.to.equal(v); // should have create a copy 30 | expect(newV.tolist()) 31 | .to.eql([0, 0, 0]); 32 | }); 33 | it('can subtract a scalar to a matrix', function () { 34 | const newMatrix = m.subtract(1); 35 | expect(newMatrix).not.to.equal(m); // should have create a copy 36 | expect(newMatrix.tolist()) 37 | .to.eql([[-1, 0], [1, 2], [3, 4]]); 38 | }); 39 | it('can subtract 2 matrix', function () { 40 | const newV = m.subtract(m); 41 | expect(newV).not.to.equal(m); // should have create a copy 42 | expect(newV.tolist()) 43 | .to.eql([[0, 0], [0, 0], [0, 0]]); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/mocha/sum.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('sum', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.arange(3); 11 | expect(nj.sum(x)).to.eql(3); 12 | }); 13 | 14 | it('should work on matrix', function () { 15 | const x = nj.ones([10, 10]); 16 | expect(nj.sum(x)).to.eql(100); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mocha/tan.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('tan', function () { 9 | it('should work on vectors', function () { 10 | expect(nj.tan([0, Math.PI / 4, Math.PI]).round().tolist()) 11 | .to.eql([0, 1, -0]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/mocha/tanh.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('tanh', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.array([-20, 0, 20]); 11 | expect(nj.tanh(x).tolist()) 12 | .to.eql([Math.tanh(-20), Math.tanh(0), Math.tanh(20)]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/mocha/to-string.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('toString', function () { 9 | it('should properly format 1d array', function () { 10 | expect(nj.array([0, 1, 2]).toString()) 11 | .to.eql('array([ 0, 1, 2])'); 12 | }); 13 | it('should properly format 2d array', function () { 14 | const arr = nj.arange(12).reshape(4, 3); 15 | expect(arr.toString()) 16 | .to.eql('' + 17 | 'array([[ 0, 1, 2],\n' + 18 | ' [ 3, 4, 5],\n' + 19 | ' [ 6, 7, 8],\n' + 20 | ' [ 9, 10, 11]])' + 21 | ''); 22 | }); 23 | it('should properly format 3d arrays', function () { 24 | const a0 = nj.arange(12).reshape(1, 4, 3); 25 | expect(a0.toString()) 26 | .to.eql('' + 27 | 'array([[[ 0, 1, 2],\n' + 28 | ' [ 3, 4, 5],\n' + 29 | ' [ 6, 7, 8],\n' + 30 | ' [ 9, 10, 11]]])' + 31 | ''); 32 | expect(nj.arange(18).reshape(3, 3, 2).toString()) 33 | .to.eql('' + 34 | '' + 35 | 'array([[[ 0, 1],\n' + 36 | ' [ 2, 3],\n' + 37 | ' [ 4, 5]],\n' + 38 | ' [[ 6, 7],\n' + 39 | ' [ 8, 9],\n' + 40 | ' [ 10, 11]],\n' + 41 | ' [[ 12, 13],\n' + 42 | ' [ 14, 15],\n' + 43 | ' [ 16, 17]]])' + 44 | ''); 45 | }); 46 | it('should properly format typed array', function () { 47 | expect(nj.uint8([0, 1, 2]).toString()) 48 | .to.eql('array([ 0, 1, 2], dtype=uint8)'); 49 | }); 50 | it('should hide some data if it is too big', function () { 51 | expect(nj.arange(100).toString()) 52 | .to.eql('array([ 0, 1, 2, ..., 97, 98, 99])'); 53 | expect(nj.arange(100).reshape(10, 10).toString()) 54 | .to.eql('' + 55 | 'array([[ 0, 1, 2, ..., 7, 8, 9],\n' + 56 | ' [ 10, 11, 12, ..., 17, 18, 19],\n' + 57 | ' [ 20, 21, 22, ..., 27, 28, 29],\n' + 58 | ' ...\n' + 59 | ' [ 70, 71, 72, ..., 77, 78, 79],\n' + 60 | ' [ 80, 81, 82, ..., 87, 88, 89],\n' + 61 | ' [ 90, 91, 92, ..., 97, 98, 99]])'); 62 | }); 63 | describe('with custom printThreshold', function () { 64 | let def; 65 | before(function () { 66 | def = nj.config.printThreshold; 67 | nj.config.printThreshold = 5; 68 | }); 69 | after(function () { 70 | nj.config.printThreshold = def; 71 | }); 72 | it('should give less information', function () { 73 | expect(nj.arange(100).toString()) 74 | .to.eql('array([ 0, 1, ..., 98, 99])'); 75 | expect(nj.arange(100).reshape(10, 10).toString()) 76 | .to.eql('' + 77 | 'array([[ 0, 1, ..., 8, 9],\n' + 78 | ' [ 10, 11, ..., 18, 19],\n' + 79 | ' ...\n' + 80 | ' [ 80, 81, ..., 88, 89],\n' + 81 | ' [ 90, 91, ..., 98, 99]])'); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /test/mocha/transpose.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('transpose', function () { 9 | it('should work on vectors', function () { 10 | const x = nj.arange(12); 11 | const y = x.transpose(); 12 | expect(y.shape).to.eql([12]); 13 | expect(y.tolist()).to.eql(x.tolist()); 14 | }); 15 | it('should work on matrix', function () { 16 | const x = nj.arange(12).reshape([4, 3]); 17 | const y = x.transpose(); 18 | expect(x.shape).to.eql([4, 3]); 19 | expect(y.shape).to.eql([3, 4]); 20 | expect(y.tolist()) 21 | .to.eql([ 22 | [0, 3, 6, 9], 23 | [1, 4, 7, 10], 24 | [2, 5, 8, 11]]); 25 | }); 26 | it('should work on multdimensional array with custom axes passed as a list', function () { 27 | const x = nj.arange(5 * 4 * 3 * 2).reshape([5, 4, 3, 2]); 28 | const y = x.transpose([0, 2, 1, 3]); 29 | expect(x.shape).to.eql([5, 4, 3, 2]); 30 | expect(y.shape).to.eql([5, 3, 4, 2]); 31 | }); 32 | it('should work on multdimensional array with custom axes', function () { 33 | const x = nj.arange(5 * 4 * 3 * 2).reshape([5, 4, 3, 2]); 34 | const y = x.transpose(0, 2, 1, 3); 35 | expect(x.shape).to.eql([5, 4, 3, 2]); 36 | expect(y.shape).to.eql([5, 3, 4, 2]); 37 | }); 38 | it('should work after slicing', function () { 39 | const x = nj.arange(16).reshape([4, 4]); 40 | const subX = x.lo(1, 1).hi(2, 2); 41 | expect(subX.tolist()) 42 | .to.eql([ 43 | [5, 6], 44 | [9, 10]]); 45 | expect(subX.transpose().tolist()) 46 | .to.eql([ 47 | [5, 9], 48 | [6, 10]]); 49 | }); 50 | it('should be used with T shortcut', function () { 51 | const x = nj.arange(12).reshape([4, 3]); 52 | expect(x.T.tolist()).to.eql(x.transpose().tolist()); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/mocha/utils.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | import { ValueError} from "../../src/lib/errors" 8 | 9 | describe('utils', function () { 10 | it('broadcast', function () { 11 | expect(nj.broadcast([], [])).to.equal(undefined); 12 | expect(nj.broadcast([256, 256, 3], [3])).to.eql([256, 256, 3]); 13 | expect(nj.broadcast([8, 1, 6, 1], [7, 1, 5])).to.eql([8, 7, 6, 5]); 14 | expect(nj.broadcast([5, 4], [1])).to.eql([5, 4]); 15 | expect(nj.broadcast([15, 3, 5], [15, 1, 5])).to.eql([15, 3, 5]); 16 | }); 17 | 18 | describe('iteraxis', function () { 19 | let x; 20 | beforeEach(function () { 21 | x = nj.arange(12).reshape([4, 3]); 22 | }); 23 | it('should raise an error if axis NOT valid', function () { 24 | expect(function () { 25 | x.iteraxis(2, function (xi) {}); 26 | }).to.throw(ValueError, 'invalid axis'); 27 | }); 28 | it('can iterate over rows', function () { 29 | const y = []; 30 | x.iteraxis(0, function (xr, i) { 31 | y[i] = xr.tolist(); 32 | }); 33 | expect(x.tolist()).to.eql(y); 34 | }); 35 | it('can iterate over columns', function () { 36 | const y = []; 37 | x.iteraxis(1, function (xc, i) { 38 | y[i] = xc.tolist(); 39 | }); 40 | expect(x.transpose().tolist()).to.eql(y); 41 | }); 42 | it('can iterate over the last axis', function () { 43 | const y = []; 44 | x.iteraxis(-1, function (xc, i) { 45 | y[i] = xc.tolist(); 46 | }); 47 | expect(x.transpose().tolist()).to.eql(y); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/mocha/zeros.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | import nj from "../../src"; 7 | 8 | describe('zeros', function () { 9 | it('can generate a vectors', function () { 10 | expect(nj.zeros(0).tolist()).to.eql([]); 11 | expect(nj.zeros(2).tolist()).to.eql([0, 0]); 12 | expect(nj.zeros([2]).tolist()).to.eql([0, 0]); 13 | }); 14 | 15 | it('can generate matrix', function () { 16 | expect(nj.zeros([2, 2]).tolist()) 17 | .to.eql([ 18 | [0, 0], 19 | [0, 0]]); 20 | }); 21 | 22 | it('should accept a dtype', function () { 23 | expect(nj.zeros(0, 'uint8').dtype).to.equal('uint8'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es6", 5 | "outDir": "build/main", 6 | "rootDir": "src", 7 | "moduleResolution": "node", 8 | "module": "commonjs", 9 | "declaration": true, 10 | "inlineSourceMap": true, 11 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 12 | "resolveJsonModule": true /* Include modules imported with .json extension. */, 13 | 14 | // "strict": true /* Enable all strict type-checking options. */, 15 | 16 | /* Strict Type-Checking Options */ 17 | // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 18 | // "strictNullChecks": true /* Enable strict null checks. */, 19 | // "strictFunctionTypes": true /* Enable strict checking of function types. */, 20 | // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, 21 | // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, 22 | // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 23 | 24 | /* Additional Checks */ 25 | // "noUnusedLocals": true /* Report errors on unused locals. */, 26 | // "noUnusedParameters": true /* Report errors on unused parameters. */, 27 | "noImplicitReturns": false /* Report error when not all code paths in function return a value. */, 28 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 29 | 30 | /* Debugging Options */ 31 | "traceResolution": false /* Report module resolution log messages. */, 32 | "listEmittedFiles": false /* Print names of generated files part of the compilation. */, 33 | "listFiles": false /* Print names of files part of the compilation. */, 34 | "pretty": true /* Stylize errors and messages using color and context. */, 35 | 36 | /* Experimental Options */ 37 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, 38 | // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, 39 | 40 | "lib": ["es6", "DOM"], 41 | "types": ["node", "mocha"], 42 | "typeRoots": ["node_modules/@types", "src/types"] 43 | }, 44 | "include": ["src/**/*.ts"], 45 | "exclude": ["node_modules/**"], 46 | "compileOnSave": false 47 | } 48 | -------------------------------------------------------------------------------- /tsconfig.module.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "target": "es6", 5 | "outDir": "build/module", 6 | "module": "es6" 7 | }, 8 | "exclude": [ 9 | "node_modules/**" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------