@cyfora/numera
v1.0.3
Published
NumPy for JavaScript/TypeScript: n-dimensional arrays, broadcasting, linear algebra, random and FFT backed by a native C++ core.
Downloads
813
Maintainers
Readme
numera
NumPy for JavaScript and TypeScript, backed by a native C++ core.
- 📊 Broad API: 1,045 of 1,054 tracked NumPy API names (99.1%), including
linalg,fft,random,ma,strings,char,rec,polynomial,emathandtesting - ⚙️ Native core: the numerical work runs in C++20 through Node-API, not per-element JavaScript
- ✅ NumPy-validated: about 9,500 differential cases generated by Python NumPy and replayed against numera
- 🎲 Same random numbers as NumPy:
default_rng(PCG64) andnp.random.seed(MT19937) streams match bit for bit - 🔒 Type-safe: full TypeScript type definitions, ESM
- 📦 No toolchain needed: prebuilt binaries for macOS and Linux (arm64 and x64), so you don't need a compiler, CMake or Python
Docs • Compatibility • Performance • Roadmap • Contributing
Install
npm install @cyfora/numera
# or: pnpm add @cyfora/numera · yarn add @cyfora/numera| Platform | Architectures | Node.js | | --- | --- | --- | | macOS 13.3+ | arm64 (Apple Silicon), x64 (Intel) | ≥ 18 | | Linux (glibc ≥ 2.28: Ubuntu 20.04+, Debian 10+, RHEL 8+) | x64, arm64 | ≥ 18 |
Windows and Alpine/musl Linux have no prebuilt binaries yet. On those, build from source (see Contributing).
Quick Start
import np from "@cyfora/numera";
// or: import { array, zeros, linalg } from "@cyfora/numera";
// Array creation with dtype support
const a = np.array([[1, 2], [3, 4]], { dtype: "float32" });
const b = np.ones([2, 2], { dtype: "int32" });
// Broadcasting and element-wise math
np.multiply(np.add(a, 5), 2).toArray(); // [[12, 14], [16, 18]]
// Linear algebra
np.matmul(a, b).toArray(); // [[3, 3], [7, 7]]
np.trace(a).item(); // 5
// Reductions with axis support
np.mean(a, { axis: 0 }).toArray(); // [2, 3]
// NumPy-style indexing: a[0], a[0:2, 1:]
a.get(0).toArray(); // [1, 2]
a.slice([[0, 2], [1, null]]).toArray(); // [[2], [4]]A quick tour
Arrays, shapes and views
const x = np.arange(12).reshape([3, 4]);
x.shape; // [3, 4]
x.T.shape; // [4, 3] (view, no copy)
x.get(1, 2).item(); // x[1, 2] -> 6
x.slice([[0, 2], [null, null, 2]]).toArray(); // x[0:2, ::2] -> [[0, 2], [4, 6]]
x.get(np.greater(x, 8)).toArray(); // x[x > 8] -> [9, 10, 11]
x.get(np.newaxis, np.ellipsis).shape; // x[None, ...] -> [1, 3, 4]
const c = np.zeros([3]);
c.set([[0, 2]], [7, 8]); // c[0:2] = [7, 8]
c.toArray(); // [7, 8, 0]A slice is a tuple [start, stop, step], where null means "omitted". Basic
indexing returns views; integer-array and boolean-mask indexing return copies,
as in NumPy.
Dtypes: bool, int8–int64, uint8–uint64, float16, float32,
float64, complex64, complex128, plus strings and datetime64 /
timedelta64.
Math, statistics and sorting
const v = np.array([3, 1, 2]);
np.sort(v).toArray(); // [1, 2, 3]
np.argsort(v).toArray(); // [1, 2, 0]
np.cumsum(v).toArray(); // [3, 4, 6]
np.where(np.array([true, false, true]), np.array([1, 2, 3]), 0).toArray(); // [1, 0, 3]
const m = np.array([[1, 2, 3], [4, 5, 6]]);
m.sum({ axis: 0 }).toArray(); // [5, 7, 9]
m.argmax().item(); // 5
np.std(m, { ddof: 1 }).item(); // 1.8708286933869707
const { hist, edges } = np.histogram(np.array([1, 2, 2, 3]), 3);
hist.toArray(); // [1, 2, 1]Linear algebra
const A = np.array([[3, 1], [1, 2]]);
np.linalg.solve(A, np.array([9, 8])).toArray(); // [2, 3]
np.linalg.det(A).item(); // 5
const { eigenvalues, eigenvectors } = np.linalg.eigh(A);
const { U, S, Vh } = np.linalg.svd(A);
np.einsum("ij,jk->ik", A, A).toArray(); // [[10, 5], [5, 5]]All linalg functions accept batched (stacked) inputs. On macOS they use
Apple Accelerate; elsewhere a portable built-in backend.
Random numbers (same streams as NumPy)
const rng = np.random.defaultRng(42);
rng.random([3]).toArray(); // [0.7739560485559633, 0.4388784397520523, 0.8585979199113825]
rng.integers(0, 10, [5]);
rng.normal(0, 1, [2, 2]);
np.random.seed(0); // legacy RandomState API
np.random.rand(2, 3);FFT
const spec = np.fft.fft(np.array([1, 0, 0, 0]));
spec.dtype.name; // "complex128"
spec.toArray(); // [Complex { re: 1, im: 0 }, ...]
np.fft.rfftfreq(8).toArray(); // [0, 0.125, 0.25, 0.375, 0.5]Typed errors
try {
np.add(np.zeros([2, 3]), np.zeros([4]));
} catch (e) {
e instanceof np.BroadcastError; // true
// "operands could not be broadcast together with shapes (2, 3) (4,)"
}Error classes: NativpyError (base), ValueError, ShapeError,
BroadcastError, DTypeError, IndexError, LinAlgError,
FloatingPointError, MemoryError and NotImplementedError.
The full API, with arguments, return values and an example for every function, is at numera.cyfora.in.
How it works
your code ──► TypeScript API (validation, NumPy-style ergonomics)
│ Node-API
▼
C++20 core (arrays, ufuncs, reductions, linalg, random, FFT)Array data lives in native memory and JavaScript holds handles to it. The addon for your platform ships in the package and is loaded automatically. It uses the stable Node-API, so one binary works on every Node ≥ 18.
Differences from NumPy
numera aims to match NumPy, but some behaviour differs because of
JavaScript. For example, a full integer index returns a 0-d array (call
.item() to get a number), and 64-bit integers are only exact up to 2^53 in
toArray() (use toTypedArray() for full precision). Every difference is
listed in COMPATIBILITY.md.
Performance
Benchmarks against NumPy are in PERFORMANCE.md. Some operations are faster than NumPy and others are slower. The numbers come from single local runs and are not general claims.
Contributing
Issues and pull requests are welcome. See the contributing guide for the development setup and test suites.
Links
- API reference: https://numera.cyfora.in
- Source code: https://github.com/Rajankr542/numera
- Issues: https://github.com/Rajankr542/numera/issues
License
MIT (see LICENSE in this package).
