catniff 0.2.5 → 0.2.7
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 +4 -0
- package/dist/core.js +146 -3
- package/package.json +1 -1
package/dist/core.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export declare class Tensor {
|
|
|
39
39
|
squeeze(dims?: number[] | number): Tensor;
|
|
40
40
|
unsqueeze(dim: number): Tensor;
|
|
41
41
|
sum(dims?: number[] | number, keepDims?: boolean): Tensor;
|
|
42
|
+
prod(dims?: number[] | number, keepDims?: boolean): Tensor;
|
|
43
|
+
mean(dims?: number[] | number, keepDims?: boolean): Tensor;
|
|
42
44
|
add(other: TensorValue | Tensor): Tensor;
|
|
43
45
|
sub(other: TensorValue | Tensor): Tensor;
|
|
44
46
|
mul(other: TensorValue | Tensor): Tensor;
|
|
@@ -89,6 +91,8 @@ export declare class Tensor {
|
|
|
89
91
|
mv(other: TensorValue | Tensor): Tensor;
|
|
90
92
|
matmul(other: TensorValue | Tensor): Tensor;
|
|
91
93
|
static fullLike(tensor: Tensor, num: number, options?: TensorOptions): Tensor;
|
|
94
|
+
static onesLike(tensor: Tensor, options?: TensorOptions): Tensor;
|
|
95
|
+
static zerosLike(tensor: Tensor, options?: TensorOptions): Tensor;
|
|
92
96
|
backward(): void;
|
|
93
97
|
val(): any;
|
|
94
98
|
withGrad(requiresGrad: boolean): Tensor;
|
package/dist/core.js
CHANGED
|
@@ -351,6 +351,7 @@ class Tensor {
|
|
|
351
351
|
gradStrides = this.strides;
|
|
352
352
|
gradValue = new Array(originalSize).fill(0);
|
|
353
353
|
}
|
|
354
|
+
// Calculate new value after sum
|
|
354
355
|
for (let index = 0; index < originalSize; index++) {
|
|
355
356
|
const coords = Tensor.indexToCoords(index, this.shape, this.strides);
|
|
356
357
|
// Force 0 on reduced axes to collapse into size-1 dims
|
|
@@ -362,7 +363,7 @@ class Tensor {
|
|
|
362
363
|
outputValue[outFlatIndex] += this.value[realFlatIndex];
|
|
363
364
|
// Mark for gradient if needed
|
|
364
365
|
if (this.requiresGrad) {
|
|
365
|
-
|
|
366
|
+
gradValue[realFlatIndex] = 1;
|
|
366
367
|
}
|
|
367
368
|
}
|
|
368
369
|
const out = new Tensor(outputValue, {
|
|
@@ -380,6 +381,136 @@ class Tensor {
|
|
|
380
381
|
}
|
|
381
382
|
return keepDims ? out : out.squeeze(dims);
|
|
382
383
|
}
|
|
384
|
+
// Tensor product reduction
|
|
385
|
+
prod(dims, keepDims = false) {
|
|
386
|
+
if (typeof this.value === "number")
|
|
387
|
+
return new Tensor(this.value);
|
|
388
|
+
if (typeof dims === "number") {
|
|
389
|
+
dims = [dims];
|
|
390
|
+
}
|
|
391
|
+
if (typeof dims === "undefined") {
|
|
392
|
+
dims = Array.from({ length: this.shape.length }, (_, index) => index);
|
|
393
|
+
}
|
|
394
|
+
// Dims that are reduced now have size-1
|
|
395
|
+
const outputShape = this.shape.map((dim, i) => dims.includes(i) ? 1 : dim);
|
|
396
|
+
const outputStrides = Tensor.getStrides(outputShape);
|
|
397
|
+
const outputSize = Tensor.shapeToSize(outputShape);
|
|
398
|
+
const outputValue = new Array(outputSize).fill(1);
|
|
399
|
+
const originalSize = Tensor.shapeToSize(this.shape);
|
|
400
|
+
// Gradient data
|
|
401
|
+
let gradShape, gradStrides, gradValue = [];
|
|
402
|
+
// Allocate gradient data only when needed
|
|
403
|
+
if (this.requiresGrad) {
|
|
404
|
+
gradShape = this.shape;
|
|
405
|
+
gradStrides = this.strides;
|
|
406
|
+
gradValue = new Array(originalSize).fill(0);
|
|
407
|
+
}
|
|
408
|
+
// Calculate new value after multiplying
|
|
409
|
+
for (let index = 0; index < originalSize; index++) {
|
|
410
|
+
const coords = Tensor.indexToCoords(index, this.shape, this.strides);
|
|
411
|
+
// Force 0 on reduced axes to collapse into size-1 dims
|
|
412
|
+
const outCoords = coords.map((val, i) => dims.includes(i) ? 0 : val);
|
|
413
|
+
// Convert output coordinates to flat index
|
|
414
|
+
const outFlatIndex = Tensor.coordsToIndex(outCoords, outputStrides);
|
|
415
|
+
// Accumulate, outFlatIndex should match multiple realFlatIndexes
|
|
416
|
+
const realFlatIndex = Tensor.coordsToIndex(coords, this.strides);
|
|
417
|
+
outputValue[outFlatIndex] *= this.value[realFlatIndex];
|
|
418
|
+
}
|
|
419
|
+
const out = new Tensor(outputValue, {
|
|
420
|
+
shape: outputShape,
|
|
421
|
+
strides: outputStrides
|
|
422
|
+
});
|
|
423
|
+
// Set up gradient if needed
|
|
424
|
+
if (this.requiresGrad) {
|
|
425
|
+
// Grad is the product of other elements of the same axis, which is product of all els divided by the current value
|
|
426
|
+
for (let index = 0; index < originalSize; index++) {
|
|
427
|
+
const coords = Tensor.indexToCoords(index, this.shape, this.strides);
|
|
428
|
+
// Force 0 on reduced axes to collapse into size-1 dims
|
|
429
|
+
const outCoords = coords.map((val, i) => dims.includes(i) ? 0 : val);
|
|
430
|
+
// Convert output coordinates to flat index
|
|
431
|
+
const outFlatIndex = Tensor.coordsToIndex(outCoords, outputStrides);
|
|
432
|
+
// Accumulate, outFlatIndex should match multiple realFlatIndexes
|
|
433
|
+
const realFlatIndex = Tensor.coordsToIndex(coords, this.strides);
|
|
434
|
+
// Calculate gradient at position
|
|
435
|
+
gradValue[realFlatIndex] = outputValue[outFlatIndex] / this.value[realFlatIndex];
|
|
436
|
+
}
|
|
437
|
+
out.requiresGrad = true;
|
|
438
|
+
out.children.push(this);
|
|
439
|
+
out.gradFn = () => {
|
|
440
|
+
const localGrad = new Tensor(gradValue, { shape: gradShape, strides: gradStrides });
|
|
441
|
+
Tensor.addGrad(this, out.grad.withGrad(false).mul(localGrad));
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return keepDims ? out : out.squeeze(dims);
|
|
445
|
+
}
|
|
446
|
+
// Tensor mean reduction
|
|
447
|
+
mean(dims, keepDims = false) {
|
|
448
|
+
if (typeof this.value === "number")
|
|
449
|
+
return new Tensor(this.value);
|
|
450
|
+
if (typeof dims === "number") {
|
|
451
|
+
dims = [dims];
|
|
452
|
+
}
|
|
453
|
+
if (typeof dims === "undefined") {
|
|
454
|
+
dims = Array.from({ length: this.shape.length }, (_, index) => index);
|
|
455
|
+
}
|
|
456
|
+
// Dims that are reduced now have size-1
|
|
457
|
+
const outputShape = this.shape.map((dim, i) => dims.includes(i) ? 1 : dim);
|
|
458
|
+
const outputStrides = Tensor.getStrides(outputShape);
|
|
459
|
+
const outputSize = Tensor.shapeToSize(outputShape);
|
|
460
|
+
const outputValue = new Array(outputSize).fill(0);
|
|
461
|
+
const outputFeeders = new Array(outputSize).fill(0);
|
|
462
|
+
const originalSize = Tensor.shapeToSize(this.shape);
|
|
463
|
+
// Gradient data
|
|
464
|
+
let gradShape, gradStrides, gradValue = [];
|
|
465
|
+
// Allocate gradient data only when needed
|
|
466
|
+
if (this.requiresGrad) {
|
|
467
|
+
gradShape = this.shape;
|
|
468
|
+
gradStrides = this.strides;
|
|
469
|
+
gradValue = new Array(originalSize).fill(0);
|
|
470
|
+
}
|
|
471
|
+
// Calculate sums and how many elements contribute to specific positions
|
|
472
|
+
for (let index = 0; index < originalSize; index++) {
|
|
473
|
+
const coords = Tensor.indexToCoords(index, this.shape, this.strides);
|
|
474
|
+
// Force 0 on reduced axes to collapse into size-1 dims
|
|
475
|
+
const outCoords = coords.map((val, i) => dims.includes(i) ? 0 : val);
|
|
476
|
+
// Convert output coordinates to flat index
|
|
477
|
+
const outFlatIndex = Tensor.coordsToIndex(outCoords, outputStrides);
|
|
478
|
+
// Accumulate, outFlatIndex should match multiple realFlatIndexes
|
|
479
|
+
const realFlatIndex = Tensor.coordsToIndex(coords, this.strides);
|
|
480
|
+
outputValue[outFlatIndex] += this.value[realFlatIndex];
|
|
481
|
+
outputFeeders[outFlatIndex]++;
|
|
482
|
+
}
|
|
483
|
+
// Calculate mean by dividing sum by the number of contributors to the position
|
|
484
|
+
for (let index = 0; index < outputSize; index++) {
|
|
485
|
+
outputValue[index] /= outputFeeders[index];
|
|
486
|
+
}
|
|
487
|
+
const out = new Tensor(outputValue, {
|
|
488
|
+
shape: outputShape,
|
|
489
|
+
strides: outputStrides
|
|
490
|
+
});
|
|
491
|
+
// Set up gradient if needed
|
|
492
|
+
if (this.requiresGrad) {
|
|
493
|
+
// Calculate grad by assiging 1 divide by the number of contributors to the position
|
|
494
|
+
for (let index = 0; index < originalSize; index++) {
|
|
495
|
+
const coords = Tensor.indexToCoords(index, this.shape, this.strides);
|
|
496
|
+
// Force 0 on reduced axes to collapse into size-1 dims
|
|
497
|
+
const outCoords = coords.map((val, i) => dims.includes(i) ? 0 : val);
|
|
498
|
+
// Convert output coordinates to flat index
|
|
499
|
+
const outFlatIndex = Tensor.coordsToIndex(outCoords, outputStrides);
|
|
500
|
+
// Accumulate, outFlatIndex should match multiple realFlatIndexes
|
|
501
|
+
const realFlatIndex = Tensor.coordsToIndex(coords, this.strides);
|
|
502
|
+
// Mean = 1/n * (el1 + el2 + ... + eln) so grad = 1/n
|
|
503
|
+
gradValue[realFlatIndex] = 1 / outputFeeders[outFlatIndex];
|
|
504
|
+
}
|
|
505
|
+
out.requiresGrad = true;
|
|
506
|
+
out.children.push(this);
|
|
507
|
+
out.gradFn = () => {
|
|
508
|
+
const localGrad = new Tensor(gradValue, { shape: gradShape, strides: gradStrides });
|
|
509
|
+
Tensor.addGrad(this, out.grad.withGrad(false).mul(localGrad));
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return keepDims ? out : out.squeeze(dims);
|
|
513
|
+
}
|
|
383
514
|
// Tensor element-wise addition
|
|
384
515
|
add(other) {
|
|
385
516
|
return this.elementWiseABDAG(other, (a, b) => a + b, (self, other, outGrad) => outGrad, (self, other, outGrad) => outGrad);
|
|
@@ -741,6 +872,18 @@ class Tensor {
|
|
|
741
872
|
return new Tensor(num, options);
|
|
742
873
|
return new Tensor(new Array(tensor.value.length).fill(num), { shape: tensor.shape, strides: tensor.strides, ...options });
|
|
743
874
|
}
|
|
875
|
+
// Utility to create a new tensor with shape of another tensor, filled with 1
|
|
876
|
+
static onesLike(tensor, options = {}) {
|
|
877
|
+
if (typeof tensor.value === "number")
|
|
878
|
+
return new Tensor(1, options);
|
|
879
|
+
return new Tensor(new Array(tensor.value.length).fill(1), { shape: tensor.shape, strides: tensor.strides, ...options });
|
|
880
|
+
}
|
|
881
|
+
// Utility to create a new tensor with shape of another tensor, filled with 0
|
|
882
|
+
static zerosLike(tensor, options = {}) {
|
|
883
|
+
if (typeof tensor.value === "number")
|
|
884
|
+
return new Tensor(0, options);
|
|
885
|
+
return new Tensor(new Array(tensor.value.length).fill(0), { shape: tensor.shape, strides: tensor.strides, ...options });
|
|
886
|
+
}
|
|
744
887
|
// Reverse-mode autodiff call
|
|
745
888
|
backward() {
|
|
746
889
|
// Build topological order
|
|
@@ -749,7 +892,7 @@ class Tensor {
|
|
|
749
892
|
function build(node) {
|
|
750
893
|
if (!visited.has(node) && node.requiresGrad) {
|
|
751
894
|
visited.add(node);
|
|
752
|
-
node.grad = Tensor.
|
|
895
|
+
node.grad = Tensor.zerosLike(node); // Reset grad with 0
|
|
753
896
|
for (let child of node.children)
|
|
754
897
|
build(child);
|
|
755
898
|
topo.push(node);
|
|
@@ -757,7 +900,7 @@ class Tensor {
|
|
|
757
900
|
}
|
|
758
901
|
build(this);
|
|
759
902
|
// Feed backward to calculate gradient
|
|
760
|
-
this.grad = Tensor.
|
|
903
|
+
this.grad = Tensor.onesLike(this);
|
|
761
904
|
for (let index = topo.length - 1; index > -1; index--) {
|
|
762
905
|
topo[index].gradFn();
|
|
763
906
|
}
|