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 +2 -2
- package/dist/core.js +29 -15
- package/package.json +1 -1
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
|
-
|
|
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
|
|
284
|
-
// Inserting at the back:
|
|
285
|
-
newDimStride =
|
|
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:
|
|
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 =
|
|
321
|
-
gradStrides =
|
|
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:
|
|
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:
|
|
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.
|
|
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:
|
|
755
|
-
strides:
|
|
768
|
+
shape: this.shape,
|
|
769
|
+
strides: this.strides,
|
|
756
770
|
requiresGrad
|
|
757
771
|
});
|
|
758
772
|
}
|