catniff 0.3.1 → 0.4.1

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
@@ -72,6 +72,28 @@ G.backward();
72
72
  console.log(X.grad.val(), Y.grad.val());
73
73
  ```
74
74
 
75
+ ## Optimizer
76
+
77
+ Catniff comes bundled with optimizers as well:
78
+ ```js
79
+ const { Tensor, Optim } = require("catniff");
80
+
81
+ // Define some parameter
82
+ const w = new Tensor([1.0], { requiresGrad: true });
83
+ // Define a fake loss function: L = (w - 3)^2
84
+ const loss = w.sub(3).pow(2);
85
+ // Calculate gradient
86
+ loss.backward();
87
+ // Use Adam optimizer
88
+ const optim = new Optim.Adam([w]);
89
+ // Optimization step
90
+ optim.step();
91
+
92
+ console.log("Updated weight:", w.data); // Should move toward 3.0
93
+ ```
94
+
95
+ And it can still do much more, check out the docs mentioned below for more information.
96
+
75
97
  ## Documentation
76
98
 
77
99
  Full documentation is available in [`./docs/documentation.md`](./docs/documentation.md).
@@ -83,7 +105,6 @@ All available APIs are in [`./src/`](./src/) if you want to dig deeper.
83
105
  * Bug fixes.
84
106
  * More tensor ops.
85
107
  * GPU acceleration.
86
- * Option to load more backends.
87
108
  * Some general neural net APIs.
88
109
  * More detailed documentation.
89
110
  * Code refactoring.
@@ -0,0 +1,4 @@
1
+ import { Tensor } from "./core";
2
+ export interface Backend {
3
+ transfer(tensor: Tensor): Tensor;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/core.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Backend } from "./backend";
1
2
  export type TensorValue = number | TensorValue[];
2
3
  export interface TensorOptions {
3
4
  shape?: readonly number[];
@@ -6,6 +7,7 @@ export interface TensorOptions {
6
7
  requiresGrad?: boolean;
7
8
  gradFn?: Function;
8
9
  children?: Tensor[];
10
+ device?: string;
9
11
  }
10
12
  export declare class Tensor {
11
13
  value: number[] | number;
@@ -15,6 +17,7 @@ export declare class Tensor {
15
17
  requiresGrad: boolean;
16
18
  gradFn: Function;
17
19
  children: Tensor[];
20
+ device: string;
18
21
  constructor(value: TensorValue, options?: TensorOptions);
19
22
  static flatten(tensor: TensorValue): number[] | number;
20
23
  static getShape(tensor: TensorValue): readonly number[];
@@ -161,4 +164,6 @@ export declare class Tensor {
161
164
  detach(): Tensor;
162
165
  clone(): Tensor;
163
166
  replace(other: Tensor, allowShapeMismatch?: boolean): Tensor;
167
+ static backends: Map<string, Backend>;
168
+ to(device: string): Tensor;
164
169
  }
package/dist/core.js CHANGED
@@ -10,6 +10,7 @@ class Tensor {
10
10
  requiresGrad;
11
11
  gradFn;
12
12
  children;
13
+ device;
13
14
  constructor(value, options = {}) {
14
15
  this.value = Tensor.flatten(value);
15
16
  this.shape = options.shape || Tensor.getShape(value);
@@ -18,6 +19,7 @@ class Tensor {
18
19
  this.requiresGrad = options.requiresGrad ?? false;
19
20
  this.gradFn = options.gradFn || (() => { });
20
21
  this.children = options.children || [];
22
+ this.device = options.device || "cpu";
21
23
  }
22
24
  // Utility to flatten an nD array to be 1D
23
25
  static flatten(tensor) {
@@ -52,6 +54,8 @@ class Tensor {
52
54
  }
53
55
  // Utility to get strides from shape
54
56
  static getStrides(shape) {
57
+ if (shape.length === 0)
58
+ return [];
55
59
  const strides = new Array(shape.length);
56
60
  strides[strides.length - 1] = 1;
57
61
  for (let i = strides.length - 2; i >= 0; i--) {
@@ -282,7 +286,8 @@ class Tensor {
282
286
  const outValue = outShape.length === 0 ? this.value[0] : this.value;
283
287
  const out = new Tensor(outValue, {
284
288
  shape: outShape,
285
- strides: outStrides
289
+ strides: outStrides,
290
+ device: this.device
286
291
  });
287
292
  // Set up gradient if needed
288
293
  if (this.requiresGrad) {
@@ -319,7 +324,7 @@ class Tensor {
319
324
  newDimStride = this.strides[dim] * this.shape[dim];
320
325
  }
321
326
  newStrides.splice(dim, 0, newDimStride);
322
- const out = new Tensor(thisValue, { shape: newShape, strides: newStrides });
327
+ const out = new Tensor(thisValue, { shape: newShape, strides: newStrides, device: this.device });
323
328
  // Set up gradient if needed
324
329
  if (this.requiresGrad) {
325
330
  out.requiresGrad = true;
@@ -981,7 +986,7 @@ class Tensor {
981
986
  [newShape[dim1], newShape[dim2]] = [newShape[dim2], newShape[dim1]];
982
987
  [newStrides[dim1], newStrides[dim2]] = [newStrides[dim2], newStrides[dim1]];
983
988
  // Create new tensor with same data but swapped shape/strides
984
- const out = new Tensor(this.value, { shape: newShape, strides: newStrides });
989
+ const out = new Tensor(this.value, { shape: newShape, strides: newStrides, device: this.device });
985
990
  out.requiresGrad = this.requiresGrad;
986
991
  // Handle gradient if needed
987
992
  if (this.requiresGrad) {
@@ -1160,32 +1165,7 @@ class Tensor {
1160
1165
  if (this.shape.length !== 2 || other.shape.length !== 1) {
1161
1166
  throw new Error("Input is not a 2D and 1D tensor pair");
1162
1167
  }
1163
- // MM with no grad
1164
- const thisMat = new Tensor(this.value, { shape: this.shape, strides: this.strides });
1165
- const otherMat = new Tensor(other.value, { shape: [other.shape[0], 1], strides: [other.strides[0], 1] });
1166
- const out = thisMat.mm(otherMat).squeeze(1);
1167
- // Handle grad with original tensors
1168
- if (this.requiresGrad) {
1169
- out.requiresGrad = true;
1170
- out.children.push(this);
1171
- }
1172
- if (other.requiresGrad) {
1173
- out.requiresGrad = true;
1174
- out.children.push(other);
1175
- }
1176
- if (out.requiresGrad) {
1177
- out.gradFn = () => {
1178
- // Disable gradient collecting of gradients themselves
1179
- const outGrad = out.grad.withGrad(false);
1180
- const selfNoGrad = this.withGrad(false);
1181
- const otherNoGrad = other.withGrad(false);
1182
- if (this.requiresGrad)
1183
- Tensor.addGrad(this, outGrad.unsqueeze(1).mm(otherNoGrad.unsqueeze(0)));
1184
- if (other.requiresGrad)
1185
- Tensor.addGrad(other, selfNoGrad.t().mv(outGrad));
1186
- };
1187
- }
1188
- return out;
1168
+ return this.mm(other.unsqueeze(1)).squeeze(1);
1189
1169
  }
1190
1170
  // General matrix multiplication with different shapes
1191
1171
  matmul(other) {
@@ -1461,6 +1441,7 @@ class Tensor {
1461
1441
  return new Tensor(this.value, {
1462
1442
  shape: this.shape,
1463
1443
  strides: this.strides,
1444
+ device: this.device,
1464
1445
  requiresGrad
1465
1446
  });
1466
1447
  }
@@ -1469,6 +1450,7 @@ class Tensor {
1469
1450
  return new Tensor(this.value, {
1470
1451
  shape: this.shape,
1471
1452
  strides: this.strides,
1453
+ device: this.device,
1472
1454
  requiresGrad: false
1473
1455
  });
1474
1456
  }
@@ -1493,5 +1475,15 @@ class Tensor {
1493
1475
  this.value = other.value;
1494
1476
  return this;
1495
1477
  }
1478
+ // Holds all available backends
1479
+ static backends = new Map();
1480
+ // Op to transfer tensor to another device
1481
+ to(device) {
1482
+ const backend = Tensor.backends.get(device);
1483
+ if (backend && backend.transfer) {
1484
+ return backend.transfer(this);
1485
+ }
1486
+ throw new Error(`No device found to transfer tensor to or a handler is not implemented for device.`);
1487
+ }
1496
1488
  }
1497
1489
  exports.Tensor = Tensor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "A small Torch-like deep learning framework for Javascript with tensor and autograd support",
5
5
  "main": "index.js",
6
6
  "scripts": {