catniff 0.1.3 → 0.1.5

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.
package/README.md CHANGED
@@ -26,7 +26,7 @@ console.log(x.grad); // 5
26
26
 
27
27
  Tensors in Catniff are either numbers (scalars/0-D tensors) or multidimensional number arrays (n-D tensors).
28
28
 
29
- There is a built-in `TensorMath` class to help with Tensor arithmetic, for example:
29
+ There is a built-in `TensorMath` class to help with tensor arithmetic, for example:
30
30
  ```js
31
31
  const { TensorMath } = require("catniff");
32
32
 
@@ -69,6 +69,9 @@ All available APIs are in `./src/autograd.ts`.
69
69
 
70
70
  I'm mostly just learning and playing with this currently, so there are no concrete plans yet, but here are what I currently have in mind:
71
71
 
72
+ * Fix whatever is the problem right now (there are a lot of problems right now lol).
73
+ * Add more tensor ops.
74
+ * Proper documentation.
72
75
  * GPU acceleration.
73
76
  * Some general neural net APIs.
74
77
 
@@ -11,7 +11,9 @@ export declare enum OP {
11
11
  LOG = 8,
12
12
  RELU = 9,
13
13
  SIGMOID = 10,
14
- TANH = 11
14
+ TANH = 11,
15
+ T = 12,
16
+ MM = 13
15
17
  }
16
18
  export declare class Node {
17
19
  value: Tensor;
@@ -32,6 +34,9 @@ export declare class Node {
32
34
  relu(): Node;
33
35
  sigmoid(): Node;
34
36
  tanh(): Node;
37
+ t(): Node;
38
+ mm(other: Node | number): Node;
35
39
  backward(): void;
36
- forceNode(value: Node | number): Node;
40
+ static forceNode(value: Node | number): Node;
41
+ static addGrad(node: Node, accumGrad: Tensor): void;
37
42
  }
package/dist/autograd.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Node = exports.OP = void 0;
4
4
  const tensor_1 = require("./tensor");
5
- const { add, sub, mul, pow, div, neg, exp, log, relu, sigmoid, tanh, ge } = tensor_1.TensorMath;
5
+ const { add, sub, mul, pow, div, neg, exp, log, relu, sigmoid, tanh, ge, t, mm } = tensor_1.TensorMath;
6
6
  var OP;
7
7
  (function (OP) {
8
8
  OP[OP["NONE"] = 0] = "NONE";
@@ -17,6 +17,8 @@ var OP;
17
17
  OP[OP["RELU"] = 9] = "RELU";
18
18
  OP[OP["SIGMOID"] = 10] = "SIGMOID";
19
19
  OP[OP["TANH"] = 11] = "TANH";
20
+ OP[OP["T"] = 12] = "T";
21
+ OP[OP["MM"] = 13] = "MM";
20
22
  })(OP || (exports.OP = OP = {}));
21
23
  class Node {
22
24
  value;
@@ -34,35 +36,35 @@ class Node {
34
36
  this.feedBackward = () => { };
35
37
  }
36
38
  add(other) {
37
- other = this.forceNode(other);
39
+ other = Node.forceNode(other);
38
40
  const out = new Node(add(this.value, other.value), [this, other], OP.ADD);
39
41
  out.feedBackward = () => {
40
42
  // x + y d/dx = 1, note that we apply the chain rule continuously so out.grad is multiplied into our derivative
41
- this.grad = add(this.grad, out.grad);
43
+ Node.addGrad(this, out.grad);
42
44
  // x + y d/dy = 1
43
- other.grad = add(other.grad, out.grad);
45
+ Node.addGrad(other, out.grad);
44
46
  };
45
47
  return out;
46
48
  }
47
49
  sub(other) {
48
- other = this.forceNode(other);
50
+ other = Node.forceNode(other);
49
51
  const out = new Node(sub(this.value, other.value), [this, other], OP.SUB);
50
52
  out.feedBackward = () => {
51
53
  // x - y d/dx = 1
52
- this.grad = add(this.grad, out.grad);
54
+ Node.addGrad(this, out.grad);
53
55
  // x - y d/dy = -1
54
- other.grad = add(other.grad, neg(out.grad));
56
+ Node.addGrad(other, neg(out.grad));
55
57
  };
56
58
  return out;
57
59
  }
58
60
  mul(other) {
59
- other = this.forceNode(other);
61
+ other = Node.forceNode(other);
60
62
  const out = new Node(mul(this.value, other.value), [this, other], OP.MUL);
61
63
  out.feedBackward = () => {
62
64
  // x * y d/dx = y
63
- this.grad = add(this.grad, mul(out.grad, other.value));
64
- // x + y d/dy = x
65
- other.grad = add(other.grad, mul(out.grad, this.value));
65
+ Node.addGrad(this, mul(out.grad, other.value));
66
+ // x * y d/dy = x
67
+ Node.addGrad(other, mul(out.grad, this.value));
66
68
  };
67
69
  return out;
68
70
  }
@@ -71,26 +73,26 @@ class Node {
71
73
  const out = new Node(pow(this.value, other.value), [this, other], OP.POW);
72
74
  out.feedBackward = () => {
73
75
  // x^a d/dx = a*x^(a-1)
74
- this.grad = add(this.grad, mul(out.grad, mul(other.value, pow(this.value, sub(other.value, 1)))));
76
+ Node.addGrad(this, mul(out.grad, mul(other.value, pow(this.value, sub(other.value, 1)))));
75
77
  // x^a d/da = x^a*lnx
76
- other.grad = add(other.grad, mul(out.grad, mul(pow(this.value, other.value), log(this.value))));
78
+ Node.addGrad(other, mul(out.grad, mul(pow(this.value, other.value), log(this.value))));
77
79
  };
78
80
  return out;
79
81
  }
80
82
  const out = new Node(pow(this.value, other), [this], OP.POW);
81
83
  out.feedBackward = () => {
82
- this.grad = add(this.grad, mul(out.grad, mul(other, pow(this.value, sub(other, 1)))));
84
+ Node.addGrad(this, mul(out.grad, mul(other, pow(this.value, sub(other, 1)))));
83
85
  };
84
86
  return out;
85
87
  }
86
88
  div(other) {
87
- other = this.forceNode(other);
89
+ other = Node.forceNode(other);
88
90
  const out = new Node(div(this.value, other.value), [this, other], OP.DIV);
89
91
  out.feedBackward = () => {
90
92
  // x/y d/dx = 1/y
91
- this.grad = add(this.grad, div(out.grad, other.value));
93
+ Node.addGrad(this, div(out.grad, other.value));
92
94
  // x/y d/dy = -x/y^2
93
- other.grad = add(other.grad, mul(out.grad, div(neg(this.value), pow(other.value, 2))));
95
+ Node.addGrad(other, mul(out.grad, div(neg(this.value), pow(other.value, 2))));
94
96
  };
95
97
  return out;
96
98
  }
@@ -98,7 +100,7 @@ class Node {
98
100
  const out = new Node(neg(this.value), [this], OP.NEG);
99
101
  out.feedBackward = () => {
100
102
  // -x d/dx = -1
101
- this.grad = add(this.grad, neg(out.grad));
103
+ Node.addGrad(this, neg(out.grad));
102
104
  };
103
105
  return out;
104
106
  }
@@ -107,7 +109,7 @@ class Node {
107
109
  const out = new Node(expResult, [this], OP.EXP);
108
110
  out.feedBackward = () => {
109
111
  // e^x d/dx = e^x
110
- this.grad = add(this.grad, mul(out.grad, expResult));
112
+ Node.addGrad(this, mul(out.grad, expResult));
111
113
  };
112
114
  return out;
113
115
  }
@@ -115,14 +117,14 @@ class Node {
115
117
  const out = new Node(log(this.value), [this], OP.LOG);
116
118
  out.feedBackward = () => {
117
119
  // lnx d/dx = 1/x
118
- this.grad = add(this.grad, div(out.grad, this.value));
120
+ Node.addGrad(this, div(out.grad, this.value));
119
121
  };
120
122
  return out;
121
123
  }
122
124
  relu() {
123
125
  const out = new Node(relu(this.value), [this], OP.RELU);
124
126
  out.feedBackward = () => {
125
- this.grad = add(this.grad, mul(out.grad, ge(this.value, 0)));
127
+ Node.addGrad(this, mul(out.grad, ge(this.value, 0)));
126
128
  };
127
129
  return out;
128
130
  }
@@ -130,7 +132,7 @@ class Node {
130
132
  const sigmoidResult = sigmoid(this.value);
131
133
  const out = new Node(sigmoidResult, [this], OP.SIGMOID);
132
134
  out.feedBackward = () => {
133
- this.grad = add(this.grad, mul(mul(out.grad, sigmoidResult), sub(1, sigmoidResult)));
135
+ Node.addGrad(this, mul(mul(out.grad, sigmoidResult), sub(1, sigmoidResult)));
134
136
  };
135
137
  return out;
136
138
  }
@@ -138,7 +140,23 @@ class Node {
138
140
  const tanhResult = tanh(this.value);
139
141
  const out = new Node(tanhResult, [this], OP.TANH);
140
142
  out.feedBackward = () => {
141
- this.grad = add(this.grad, mul(out.grad, sub(1, mul(tanhResult, tanhResult))));
143
+ Node.addGrad(this, mul(out.grad, sub(1, mul(tanhResult, tanhResult))));
144
+ };
145
+ return out;
146
+ }
147
+ t() {
148
+ const out = new Node(t(this.value), [this], OP.T);
149
+ out.feedBackward = () => {
150
+ Node.addGrad(this, t(out.grad));
151
+ };
152
+ return out;
153
+ }
154
+ mm(other) {
155
+ other = Node.forceNode(other);
156
+ const out = new Node(mm(this.value, other.value), [this, other], OP.MM);
157
+ out.feedBackward = () => {
158
+ Node.addGrad(this, mm(out.grad, t(other.value)));
159
+ Node.addGrad(other, mm(t(this.value), out.grad));
142
160
  };
143
161
  return out;
144
162
  }
@@ -162,10 +180,29 @@ class Node {
162
180
  topo[index].feedBackward();
163
181
  }
164
182
  }
165
- forceNode(value) {
183
+ static forceNode(value) {
166
184
  if (value instanceof Node)
167
185
  return value;
168
186
  return new Node(value);
169
187
  }
188
+ static addGrad(node, accumGrad) {
189
+ const axesToSqueeze = [];
190
+ const axesToReduce = [];
191
+ const shape = node.shape;
192
+ const gradShape = tensor_1.TensorMath.getShape(accumGrad);
193
+ const paddedDims = gradShape.length - shape.length;
194
+ for (let i = 0; i < paddedDims; i++) {
195
+ axesToReduce.push(i);
196
+ axesToSqueeze.push(i);
197
+ }
198
+ for (let i = 0; i < shape.length; i++) {
199
+ if (shape[i] === 1 && gradShape[i + paddedDims] > 1) {
200
+ axesToReduce.push(i + paddedDims);
201
+ }
202
+ }
203
+ const reducedGrad = tensor_1.TensorMath.sum(accumGrad, axesToReduce, true);
204
+ const squeezedGrad = tensor_1.TensorMath.squeeze(reducedGrad, axesToSqueeze);
205
+ node.grad = add(squeezedGrad, node.grad);
206
+ }
170
207
  }
171
208
  exports.Node = Node;
package/dist/tensor.d.ts CHANGED
@@ -19,4 +19,10 @@ export declare class TensorMath {
19
19
  static relu(tA: Tensor): Tensor;
20
20
  static sigmoid(tA: Tensor): Tensor;
21
21
  static tanh(tA: Tensor): Tensor;
22
+ static squeezeAxis(tA: Tensor, axis: number): Tensor;
23
+ static squeeze(tA: Tensor, dims?: number[] | number): Tensor;
24
+ static sumAxis(tA: Tensor, axis: number): Tensor;
25
+ static sum(tA: Tensor, dims?: number[] | number, keepDims?: boolean): Tensor;
26
+ static t(tA: Tensor): Tensor;
27
+ static mm(tA: Tensor, tB: Tensor): Tensor;
22
28
  }
package/dist/tensor.js CHANGED
@@ -253,5 +253,105 @@ class TensorMath {
253
253
  return tA.map(subA => TensorMath.tanh(subA));
254
254
  }
255
255
  }
256
+ static squeezeAxis(tA, axis) {
257
+ if (typeof tA === "number")
258
+ return tA;
259
+ if (axis === 0) {
260
+ return tA[0];
261
+ }
262
+ else {
263
+ return tA.map(slice => TensorMath.squeezeAxis(slice, axis - 1));
264
+ }
265
+ }
266
+ static squeeze(tA, dims) {
267
+ if (typeof tA === "number")
268
+ return tA;
269
+ if (typeof dims === "number") {
270
+ dims = [dims];
271
+ }
272
+ if (typeof dims === "undefined") {
273
+ const shape = TensorMath.getShape(tA);
274
+ dims = [];
275
+ for (let index = 0; index < shape.length; index++) {
276
+ if (shape[index] === 1) {
277
+ dims.push(index);
278
+ }
279
+ }
280
+ }
281
+ dims = [...dims].sort((a, b) => b - a);
282
+ let out = tA;
283
+ for (const axis of dims) {
284
+ out = TensorMath.squeezeAxis(out, axis);
285
+ }
286
+ return out;
287
+ }
288
+ static sumAxis(tA, axis) {
289
+ if (typeof tA === "number")
290
+ return tA;
291
+ if (axis === 0) {
292
+ let result = tA[0];
293
+ for (let i = 1; i < tA.length; i++) {
294
+ result = TensorMath.add(result, tA[i]);
295
+ }
296
+ return [result];
297
+ }
298
+ else {
299
+ return tA.map(slice => TensorMath.sumAxis(slice, axis - 1));
300
+ }
301
+ }
302
+ static sum(tA, dims, keepDims = false) {
303
+ if (typeof tA === "number")
304
+ return tA;
305
+ if (typeof dims === "number") {
306
+ dims = [dims];
307
+ }
308
+ if (typeof dims === "undefined") {
309
+ dims = Array.from({ length: TensorMath.getShape(tA).length }, (_, index) => index);
310
+ }
311
+ dims = [...dims].sort((a, b) => b - a);
312
+ let out = tA;
313
+ for (const axis of dims) {
314
+ out = TensorMath.sumAxis(out, axis);
315
+ }
316
+ return keepDims ? out : TensorMath.squeeze(out, dims);
317
+ }
318
+ static t(tA) {
319
+ const shapeA = TensorMath.getShape(tA);
320
+ if (shapeA.length !== 2)
321
+ throw new Error("Input is not a matrix");
322
+ const matA = tA;
323
+ const matARows = matA.length;
324
+ const matACols = matA[0].length;
325
+ const matATranspose = Array.from({ length: matACols }, () => new Array(matARows).fill(0));
326
+ for (let i = 0; i < matARows; i++) {
327
+ for (let j = 0; j < matACols; j++) {
328
+ matATranspose[j][i] = matA[i][j];
329
+ }
330
+ }
331
+ return matATranspose;
332
+ }
333
+ static mm(tA, tB) {
334
+ const shapeA = TensorMath.getShape(tA);
335
+ const shapeB = TensorMath.getShape(tB);
336
+ if (shapeA.length !== 2 || shapeB.length !== 2)
337
+ throw new Error("Inputs are not matrices");
338
+ const matA = tA;
339
+ const matB = tB;
340
+ const matARows = matA.length;
341
+ const matACols = matA[0].length;
342
+ const matBRows = matB.length;
343
+ const matBCols = matB[0].length;
344
+ if (matACols !== matBRows)
345
+ throw new Error("Invalid matrices shape for multiplication");
346
+ const matC = Array.from({ length: matARows }, () => new Array(matBCols).fill(0));
347
+ for (let i = 0; i < matARows; i++) {
348
+ for (let j = 0; j < matBCols; j++) {
349
+ for (let k = 0; k < matACols; k++) {
350
+ matC[i][j] += matA[i][k] * matB[k][j];
351
+ }
352
+ }
353
+ }
354
+ return matC;
355
+ }
256
356
  }
257
357
  exports.TensorMath = TensorMath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "A cute autograd engine for Javascript",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,12 +14,18 @@
14
14
  "cats",
15
15
  "catniff",
16
16
  "autograd",
17
+ "autodiff",
17
18
  "ml",
18
19
  "dl",
19
20
  "ai",
20
21
  "maths",
21
22
  "gradient",
22
- "tensors"
23
+ "tensors",
24
+ "library",
25
+ "framework",
26
+ "neural-network",
27
+ "machine-learning",
28
+ "deep-learning"
23
29
  ],
24
30
  "author": "nguyenphuminh",
25
31
  "license": "GPL-3.0",