catniff 0.2.2 → 0.2.4

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/dist/core.d.ts CHANGED
@@ -9,8 +9,8 @@ export interface TensorOptions {
9
9
  }
10
10
  export declare class Tensor {
11
11
  value: number[] | number;
12
- shape: number[];
13
- strides: number[];
12
+ readonly shape: number[];
13
+ readonly strides: number[];
14
14
  grad?: Tensor;
15
15
  requiresGrad: boolean;
16
16
  gradFn: Function;
package/dist/core.js CHANGED
@@ -20,8 +20,13 @@ class Tensor {
20
20
  }
21
21
  // Utility to flatten an nD array to be 1D
22
22
  static flatten(tensor) {
23
+ // Handle scalar tensors
23
24
  if (typeof tensor === "number")
24
25
  return tensor;
26
+ // If value is already 1D, we just need to return the value ('s reference)
27
+ if (typeof tensor[0] === "number")
28
+ return tensor;
29
+ // Or else recursively traverse through the nD array to flatten
25
30
  const result = [];
26
31
  function traverse(arr) {
27
32
  if (typeof arr === "number") {
@@ -148,7 +153,11 @@ class Tensor {
148
153
  static elementWiseSelf(tA, op) {
149
154
  if (typeof tA.value === "number")
150
155
  return new Tensor(op(tA.value));
151
- return new Tensor(tA.value.map(el => op(el)), { shape: [...tA.shape], strides: [...tA.strides] });
156
+ const newValue = new Array(tA.value.length);
157
+ for (let index = 0; index < tA.value.length; index++) {
158
+ newValue[index] = op(tA.value[index]);
159
+ }
160
+ return new Tensor(newValue, { shape: tA.shape, strides: tA.strides });
152
161
  }
153
162
  // Utility to do element-wise operation and build a dag node with another tensor
154
163
  elementWiseABDAG(other, op, thisGrad = () => new Tensor(0), otherGrad = () => new Tensor(0)) {
@@ -241,12 +250,14 @@ class Tensor {
241
250
  }
242
251
  }
243
252
  }
253
+ // Remove size-1 dims only
244
254
  const outShape = this.shape.filter((dim, i) => {
245
255
  const shouldSqueeze = dims.includes(i);
246
256
  if (shouldSqueeze && dim !== 1)
247
257
  throw new Error(`Can not squeeze dim with size ${dim}`);
248
258
  return !shouldSqueeze;
249
259
  });
260
+ // Remove strides of size-1 dims
250
261
  const outStrides = this.strides.filter((stride, i) => !dims.includes(i));
251
262
  const outValue = outShape.length === 0 ? this.value[0] : this.value;
252
263
  const out = new Tensor(outValue, {
@@ -280,12 +291,12 @@ class Tensor {
280
291
  // New stride
281
292
  const newStrides = [...this.strides];
282
293
  let newDimStride;
283
- if (dim === this.shape.length) {
284
- // Inserting at the back: reuse last stride or 1
285
- newDimStride = this.strides[this.strides.length - 1] ?? 1;
294
+ if (dim >= this.shape.length) {
295
+ // Inserting at the back: use 1
296
+ newDimStride = 1;
286
297
  }
287
298
  else {
288
- // Inserting before dim: copy current stride at that dim
299
+ // Inserting before dim: use current stride * current shape
289
300
  newDimStride = this.strides[dim] * this.shape[dim];
290
301
  }
291
302
  newStrides.splice(dim, 0, newDimStride);
@@ -317,8 +328,8 @@ class Tensor {
317
328
  const originalSize = this.shape.reduce((a, b) => a * b, 1);
318
329
  let gradShape, gradStrides, gradValue = [];
319
330
  if (this.requiresGrad) {
320
- gradShape = [...this.shape];
321
- gradStrides = [...this.strides];
331
+ gradShape = this.shape;
332
+ gradStrides = this.strides;
322
333
  gradValue = new Array(originalSize).fill(0);
323
334
  }
324
335
  for (let index = 0; index < originalSize; index++) {
@@ -533,7 +544,7 @@ class Tensor {
533
544
  }
534
545
  // If same dimension, return copy
535
546
  if (dim1 === dim2) {
536
- return new Tensor(this.value, { shape: [...this.shape], strides: [...this.strides] });
547
+ return new Tensor(this.value, { shape: this.shape, strides: this.strides });
537
548
  }
538
549
  // Create new shape and strides by swapping
539
550
  const newShape = [...this.shape];
@@ -585,6 +596,7 @@ class Tensor {
585
596
  }
586
597
  if (out.requiresGrad) {
587
598
  out.gradFn = () => {
599
+ // Disable gradient collecting of gradients themselves
588
600
  const outGrad = out.grad.withGrad(false);
589
601
  const selfNoGrad = this.withGrad(false);
590
602
  const otherNoGrad = other.withGrad(false);
@@ -637,6 +649,7 @@ class Tensor {
637
649
  }
638
650
  if (out.requiresGrad) {
639
651
  out.gradFn = () => {
652
+ // Disable gradient collecting of gradients themselves
640
653
  const outGrad = out.grad.withGrad(false);
641
654
  const selfNoGrad = this.withGrad(false);
642
655
  const otherNoGrad = other.withGrad(false);
@@ -656,7 +669,7 @@ class Tensor {
656
669
  throw new Error("Input is not a 2D and 1D tensor pair");
657
670
  }
658
671
  // MM with no grad
659
- const thisMat = new Tensor(this.value, { shape: [...this.shape], strides: [...this.strides] });
672
+ const thisMat = new Tensor(this.value, { shape: this.shape, strides: this.strides });
660
673
  const otherMat = new Tensor(other.value, { shape: [other.shape[0], 1], strides: [other.strides[0], 1] });
661
674
  const out = thisMat.mm(otherMat).squeeze(1);
662
675
  // Handle grad with original tensors
@@ -670,6 +683,7 @@ class Tensor {
670
683
  }
671
684
  if (out.requiresGrad) {
672
685
  out.gradFn = () => {
686
+ // Disable gradient collecting of gradients themselves
673
687
  const outGrad = out.grad.withGrad(false);
674
688
  const selfNoGrad = this.withGrad(false);
675
689
  const otherNoGrad = other.withGrad(false);
@@ -703,7 +717,7 @@ class Tensor {
703
717
  static fullLike(tensor, num, options = {}) {
704
718
  if (typeof tensor.value === "number")
705
719
  return new Tensor(num, options);
706
- return new Tensor(tensor.value.map(el => num), { shape: [...tensor.shape], strides: [...tensor.strides], ...options });
720
+ return new Tensor(new Array(tensor.value.length).fill(num), { shape: tensor.shape, strides: tensor.strides, ...options });
707
721
  }
708
722
  // Reverse-mode autodiff call
709
723
  backward() {
@@ -713,7 +727,7 @@ class Tensor {
713
727
  function build(node) {
714
728
  if (!visited.has(node) && node.requiresGrad) {
715
729
  visited.add(node);
716
- node.grad = Tensor.fullLike(node, 0);
730
+ node.grad = Tensor.fullLike(node, 0); // Reset grad with 0
717
731
  for (let child of node.children)
718
732
  build(child);
719
733
  topo.push(node);
@@ -726,7 +740,7 @@ class Tensor {
726
740
  topo[index].gradFn();
727
741
  }
728
742
  }
729
- // Returns the number/nD array form of tensor
743
+ // Returns the raw number/nD array form of tensor
730
744
  val() {
731
745
  if (typeof this.value === "number")
732
746
  return this.value;
@@ -748,11 +762,11 @@ class Tensor {
748
762
  }
749
763
  return buildNested(this.value, this.shape, this.strides);
750
764
  }
751
- // Returns a copy of the tensor with gradient turned on/off
765
+ // Returns a copy of the tensor with gradient turned on/off and detaches from autograd
752
766
  withGrad(requiresGrad) {
753
767
  return new Tensor(this.value, {
754
- shape: [...this.shape],
755
- strides: [...this.strides],
768
+ shape: this.shape,
769
+ strides: this.strides,
756
770
  requiresGrad
757
771
  });
758
772
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A cute autograd engine for Javascript",
5
5
  "main": "index.js",
6
6
  "scripts": {