matrix-lite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +272 -0
  2. package/package.json +39 -0
  3. package/src/index.js +178 -0
package/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # matrix-lite
2
+
3
+ **matrixlite** is a tiny, dependency-free JavaScript library for basic matrix operations.
4
+ It is designed to be simple, explicit, and predictable, without TypeScript, classes, or hidden magic.
5
+
6
+ Matrices are represented as plain 2D arrays (`number[][]`).
7
+
8
+ ## Features
9
+
10
+ * Works in Node.js (CommonJS & ESM compatible)
11
+ * Simple and explicit API
12
+ * Predictable data structures (plain arrays)
13
+ * Safe shape validation with clear errors
14
+ * Floating-point comparison with epsilon support
15
+
16
+ ### Supported operations:
17
+
18
+ * Matrix creation (zeros, identity)
19
+ * Shape inspection (shape)
20
+ * Matrix arithmetic (add, sub, scale, mul)
21
+ * Transformations (transpose)
22
+ * Vector math (dot, row, col)
23
+ * Utilities (clone, map, eq)
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install matrixlite
29
+ ```
30
+
31
+ ## Importing
32
+
33
+ ### CommonJS (Node.js)
34
+
35
+ ```js
36
+ const M = require('matrixlite');
37
+ ```
38
+
39
+ ### ES Modules
40
+
41
+ ```js
42
+ import * as M from 'matrixlite';
43
+ ```
44
+
45
+ ## Matrix Representation
46
+
47
+ A matrix is a rectangular 2D array of numbers:
48
+
49
+ ```js
50
+ const A = [
51
+ [1, 2, 3],
52
+ [4, 5, 6]
53
+ ];
54
+ ```
55
+
56
+ ### Rules
57
+
58
+ * All rows must have the same length
59
+ * Matrices must be non-empty
60
+ * All values should be numbers
61
+
62
+ ## API Reference
63
+
64
+ `shape(matrix)`
65
+
66
+ Returns matrix dimensions as `[rows, cols]`.
67
+
68
+ ```js
69
+ M.shape([[1, 2], [3, 4]]);
70
+ // → [2, 2]
71
+ ```
72
+
73
+ Throws if the matrix is invalid or ragged.
74
+
75
+ `zeros(rows, cols)`
76
+
77
+ Creates a matrix filled with zeros.
78
+
79
+ ```js
80
+ M.zeros(2, 3);
81
+ // [
82
+ // [0, 0, 0],
83
+ // [0, 0, 0]
84
+ // ]
85
+ ```
86
+
87
+ `identity(n)`
88
+
89
+ Creates an `n × n` identity matrix.
90
+
91
+ ```js
92
+ M.identity(3);
93
+ // [
94
+ // [1, 0, 0],
95
+ // [0, 1, 0],
96
+ // [0, 0, 1]
97
+ // ]
98
+ ```
99
+
100
+ `clone(matrix)`
101
+
102
+ Returns a deep copy of a matrix.
103
+
104
+ ```js
105
+ const B = M.clone(A);
106
+ ```
107
+
108
+ `map(matrix, fn)`
109
+
110
+ Applies a function to each element.
111
+
112
+ ```js
113
+ M.map(A, v => v * 2);
114
+ ```
115
+
116
+ Callback signature:
117
+
118
+ ```js
119
+ (value, rowIndex, colIndex) => number
120
+ ```
121
+
122
+ `add(A, B)`
123
+
124
+ Element-wise matrix addition.
125
+
126
+ ```js
127
+ M.add([[1, 2]], [[3, 4]]);
128
+ // [[4, 6]]
129
+ ```
130
+
131
+ Matrices must have the same shape.
132
+
133
+ `sub(A, B)`
134
+
135
+ Element-wise subtraction.
136
+
137
+ ```js
138
+ M.sub([[5, 5]], [[2, 3]]);
139
+ // [[3, 2]]
140
+ ```
141
+
142
+ `scale(matrix, k)`
143
+
144
+ Multiplies every element by a scalar.
145
+
146
+ ```js
147
+ M.scale([[1, 2]], 10);
148
+ // [[10, 20]]
149
+ ```
150
+
151
+ `transpose(matrix)`
152
+
153
+ Transposes a matrix.
154
+
155
+ ```js
156
+ M.transpose([
157
+ [1, 2, 3],
158
+ [4, 5, 6]
159
+ ]);
160
+ // [
161
+ // [1, 4],
162
+ // [2, 5],
163
+ // [3, 6]
164
+ // ]
165
+ ```
166
+
167
+ `mul(A, B)`
168
+
169
+ Matrix multiplication (`A × B`).
170
+
171
+ ```js
172
+ const A = [
173
+ [1, 2, 3],
174
+ [4, 5, 6]
175
+ ];
176
+
177
+ const B = [
178
+ [7, 8],
179
+ [9, 10],
180
+ [11, 12]
181
+ ];
182
+
183
+ M.mul(A, B);
184
+ // [
185
+ // [58, 64],
186
+ // [139, 154]
187
+ // ]
188
+ ```
189
+
190
+ ### Rules:
191
+
192
+ * `A.cols === B.rows`
193
+ * Uses classic O(n³) multiplication
194
+ * Optimized loop order for cache friendliness
195
+
196
+ `dot(a, b)`
197
+
198
+ Dot product of two vectors.
199
+
200
+ ```js
201
+ M.dot([1, 2, 3], [4, 5, 6]);
202
+ // 32
203
+ ```
204
+
205
+ Vectors must have equal length.
206
+
207
+ `row(matrix, index)`
208
+
209
+ Returns a copy of a matrix row.
210
+
211
+ ```js
212
+ M.row(A, 0);
213
+ // [1, 2, 3]
214
+ ```
215
+
216
+ `col(matrix, index)`
217
+
218
+ Returns a column as a vector.
219
+
220
+ ```js
221
+ M.col(A, 1);
222
+ // [2, 5]
223
+ ```
224
+
225
+ `eq(A, B, eps = 0)`
226
+
227
+ Checks matrix equality with optional tolerance.
228
+
229
+ ```js
230
+ M.eq([[1, 1.001]], [[1, 1.002]], 0.01);
231
+ // true
232
+ ```
233
+
234
+ Useful for floating-point comparisons.
235
+
236
+ ## Error Handling
237
+
238
+ All functions:
239
+
240
+ * Validate matrix shape
241
+ * Throw meaningful TypeError or RangeError
242
+ * Never silently fix invalid input
243
+
244
+ Example:
245
+ ```js
246
+ M.add([[1, 2]], [[1]]);
247
+ // RangeError: shape mismatch
248
+ ```
249
+
250
+ ## Performance Notes
251
+
252
+ * Intended for small to medium matrices
253
+ * Not optimized for large-scale numerical computing
254
+ * No SIMD, WASM, or GPU acceleration
255
+ * Perfect for:
256
+ * Education
257
+ * Prototyping
258
+ * Small math utilities
259
+ * Backend logic
260
+
261
+ What This Library Is NOT
262
+
263
+ * ❌ A NumPy replacement
264
+ * ❌ A linear algebra research toolkit
265
+ * ❌ A GPU-accelerated math engine
266
+ * ❌ A TypeScript-heavy framework
267
+
268
+ This is a minimalist utility library.
269
+
270
+
271
+ ## License
272
+ MIT
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "matrix-lite",
3
+ "version": "0.1.0",
4
+ "description": "Simple matrix utilities (JS only)",
5
+ "keywords": [
6
+ "matrix",
7
+ "math",
8
+ "linear-algebra"
9
+ ],
10
+ "license": "MIT",
11
+ "author": "",
12
+ "main": "./src/index.js",
13
+ "exports": {
14
+ ".": {
15
+ "require": "./src/index.js",
16
+ "import": "./src/index.js"
17
+ }
18
+ },
19
+ "type": "commonjs",
20
+ "files": [
21
+ "src",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "test": "node test/basic.test.js"
27
+ },
28
+ "dependencies": {
29
+ "bignumber.js": "^9.3.1",
30
+ "fast-math": "^1.0.2",
31
+ "fraction.js": "^5.3.4",
32
+ "gl-matrix": "^3.4.4",
33
+ "micro-math": "^0.10.0",
34
+ "ml-matrix": "^6.12.1",
35
+ "numeric": "^1.2.6",
36
+ "simple-statistics": "^7.8.8",
37
+ "tinyparrot": "^0.0.18"
38
+ }
39
+ }
package/src/index.js ADDED
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Matrix is represented as Array<Array<number>> (rows).
5
+ */
6
+
7
+ function isMatrix(A) {
8
+ return Array.isArray(A) && A.length > 0 && A.every(r => Array.isArray(r) && r.length > 0);
9
+ }
10
+
11
+ function shape(A) {
12
+ if (!isMatrix(A)) throw new TypeError('Expected a non-empty 2D array');
13
+ const rows = A.length;
14
+ const cols = A[0].length;
15
+ for (let i = 1; i < rows; i++) {
16
+ if (A[i].length !== cols) throw new TypeError('Ragged matrix (rows have different lengths)');
17
+ }
18
+ return [rows, cols];
19
+ }
20
+
21
+ function clone(A) {
22
+ shape(A);
23
+ return A.map(r => r.slice());
24
+ }
25
+
26
+ function zeros(rows, cols) {
27
+ if (!Number.isInteger(rows) || !Number.isInteger(cols) || rows <= 0 || cols <= 0) {
28
+ throw new TypeError('rows/cols must be positive integers');
29
+ }
30
+ const out = new Array(rows);
31
+ for (let i = 0; i < rows; i++) {
32
+ const row = new Array(cols);
33
+ for (let j = 0; j < cols; j++) row[j] = 0;
34
+ out[i] = row;
35
+ }
36
+ return out;
37
+ }
38
+
39
+ function identity(n) {
40
+ if (!Number.isInteger(n) || n <= 0) throw new TypeError('n must be a positive integer');
41
+ const I = zeros(n, n);
42
+ for (let i = 0; i < n; i++) I[i][i] = 1;
43
+ return I;
44
+ }
45
+
46
+ function map(A, fn) {
47
+ const [r, c] = shape(A);
48
+ const out = new Array(r);
49
+ for (let i = 0; i < r; i++) {
50
+ const row = new Array(c);
51
+ for (let j = 0; j < c; j++) row[j] = fn(A[i][j], i, j);
52
+ out[i] = row;
53
+ }
54
+ return out;
55
+ }
56
+
57
+ function add(A, B) {
58
+ const [ar, ac] = shape(A);
59
+ const [br, bc] = shape(B);
60
+ if (ar !== br || ac !== bc) throw new RangeError('add: shape mismatch');
61
+ const out = new Array(ar);
62
+ for (let i = 0; i < ar; i++) {
63
+ const row = new Array(ac);
64
+ for (let j = 0; j < ac; j++) row[j] = A[i][j] + B[i][j];
65
+ out[i] = row;
66
+ }
67
+ return out;
68
+ }
69
+
70
+ function sub(A, B) {
71
+ const [ar, ac] = shape(A);
72
+ const [br, bc] = shape(B);
73
+ if (ar !== br || ac !== bc) throw new RangeError('sub: shape mismatch');
74
+ const out = new Array(ar);
75
+ for (let i = 0; i < ar; i++) {
76
+ const row = new Array(ac);
77
+ for (let j = 0; j < ac; j++) row[j] = A[i][j] - B[i][j];
78
+ out[i] = row;
79
+ }
80
+ return out;
81
+ }
82
+
83
+ function scale(A, k) {
84
+ if (typeof k !== 'number' || !Number.isFinite(k)) throw new TypeError('k must be a finite number');
85
+ return map(A, v => v * k);
86
+ }
87
+
88
+ function transpose(A) {
89
+ const [r, c] = shape(A);
90
+ const out = new Array(c);
91
+ for (let j = 0; j < c; j++) {
92
+ const row = new Array(r);
93
+ for (let i = 0; i < r; i++) row[i] = A[i][j];
94
+ out[j] = row;
95
+ }
96
+ return out;
97
+ }
98
+
99
+ function mul(A, B) {
100
+ const [ar, ac] = shape(A);
101
+ const [br, bc] = shape(B);
102
+ if (ac !== br) throw new RangeError('mul: shape mismatch (A cols must equal B rows)');
103
+ const out = zeros(ar, bc);
104
+
105
+ // classic O(n^3) with a cache-friendly loop order
106
+ for (let i = 0; i < ar; i++) {
107
+ const Ai = A[i];
108
+ const outi = out[i];
109
+ for (let k = 0; k < ac; k++) {
110
+ const aik = Ai[k];
111
+ const Bk = B[k];
112
+ for (let j = 0; j < bc; j++) {
113
+ outi[j] += aik * Bk[j];
114
+ }
115
+ }
116
+ }
117
+ return out;
118
+ }
119
+
120
+ function dot(a, b) {
121
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length === 0 || b.length === 0) {
122
+ throw new TypeError('dot: expected non-empty arrays');
123
+ }
124
+ if (a.length !== b.length) throw new RangeError('dot: length mismatch');
125
+ let s = 0;
126
+ for (let i = 0; i < a.length; i++) s += a[i] * b[i];
127
+ return s;
128
+ }
129
+
130
+ function row(A, i) {
131
+ const [r] = shape(A);
132
+ if (!Number.isInteger(i) || i < 0 || i >= r) throw new RangeError('row: index out of range');
133
+ return A[i].slice();
134
+ }
135
+
136
+ function col(A, j) {
137
+ const [r, c] = shape(A);
138
+ if (!Number.isInteger(j) || j < 0 || j >= c) throw new RangeError('col: index out of range');
139
+ const out = new Array(r);
140
+ for (let i = 0; i < r; i++) out[i] = A[i][j];
141
+ return out;
142
+ }
143
+
144
+ function eq(A, B, eps = 0) {
145
+ const [ar, ac] = shape(A);
146
+ const [br, bc] = shape(B);
147
+ if (ar !== br || ac !== bc) return false;
148
+ if (typeof eps !== 'number' || eps < 0) throw new TypeError('eps must be a non-negative number');
149
+ for (let i = 0; i < ar; i++) {
150
+ for (let j = 0; j < ac; j++) {
151
+ const d = Math.abs(A[i][j] - B[i][j]);
152
+ if (d > eps) return false;
153
+ }
154
+ }
155
+ return true;
156
+ }
157
+
158
+ module.exports = {
159
+ // constructors
160
+ zeros,
161
+ identity,
162
+
163
+ // validators / helpers
164
+ shape,
165
+ clone,
166
+ map,
167
+ row,
168
+ col,
169
+ eq,
170
+
171
+ // ops
172
+ add,
173
+ sub,
174
+ scale,
175
+ transpose,
176
+ mul,
177
+ dot
178
+ };