catniff 0.2.4 → 0.2.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/dist/core.d.ts +17 -10
- package/dist/core.js +37 -15
- package/package.json +1 -1
package/dist/core.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type TensorValue = number | TensorValue[];
|
|
2
2
|
export interface TensorOptions {
|
|
3
|
-
shape?: number[];
|
|
4
|
-
strides?: number[];
|
|
3
|
+
shape?: readonly number[];
|
|
4
|
+
strides?: readonly number[];
|
|
5
5
|
grad?: Tensor;
|
|
6
6
|
requiresGrad?: boolean;
|
|
7
7
|
gradFn?: Function;
|
|
@@ -9,20 +9,27 @@ export interface TensorOptions {
|
|
|
9
9
|
}
|
|
10
10
|
export declare class Tensor {
|
|
11
11
|
value: number[] | number;
|
|
12
|
-
readonly shape: number[];
|
|
13
|
-
readonly strides: number[];
|
|
12
|
+
readonly shape: readonly number[];
|
|
13
|
+
readonly strides: readonly number[];
|
|
14
14
|
grad?: Tensor;
|
|
15
15
|
requiresGrad: boolean;
|
|
16
16
|
gradFn: Function;
|
|
17
17
|
children: Tensor[];
|
|
18
18
|
constructor(value: TensorValue, options?: TensorOptions);
|
|
19
19
|
static flatten(tensor: TensorValue): number[] | number;
|
|
20
|
-
static getShape(tensor: TensorValue): number[];
|
|
21
|
-
static getStrides(shape: number[]): number[];
|
|
22
|
-
static padShape(stridesA: number[], stridesB: number[], shapeA: number[], shapeB: number[]):
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
static getShape(tensor: TensorValue): readonly number[];
|
|
21
|
+
static getStrides(shape: readonly number[]): readonly number[];
|
|
22
|
+
static padShape(stridesA: readonly number[], stridesB: readonly number[], shapeA: readonly number[], shapeB: readonly number[]): [
|
|
23
|
+
readonly number[],
|
|
24
|
+
readonly number[],
|
|
25
|
+
readonly number[],
|
|
26
|
+
readonly number[]
|
|
27
|
+
];
|
|
28
|
+
static broadcastShapes(shapeA: readonly number[], shapeB: readonly number[]): readonly number[];
|
|
29
|
+
static indexToCoords(index: number, shape: readonly number[], strides: readonly number[]): number[];
|
|
30
|
+
static coordsToUnbroadcastedIndex(coords: number[], shape: readonly number[], strides: readonly number[]): number;
|
|
31
|
+
static coordsToIndex(coords: number[], strides: readonly number[]): number;
|
|
32
|
+
static shapeToSize(shape: readonly number[]): number;
|
|
26
33
|
static elementWiseAB(tA: Tensor, tB: Tensor, op: (tA: number, tB: number) => number): Tensor;
|
|
27
34
|
static elementWiseSelf(tA: Tensor, op: (tA: number) => number): Tensor;
|
|
28
35
|
elementWiseABDAG(other: TensorValue | Tensor, op: (a: number, b: number) => number, thisGrad?: (self: Tensor, other: Tensor, outGrad: Tensor) => Tensor, otherGrad?: (self: Tensor, other: Tensor, outGrad: Tensor) => Tensor): Tensor;
|
package/dist/core.js
CHANGED
|
@@ -93,7 +93,7 @@ class Tensor {
|
|
|
93
93
|
}
|
|
94
94
|
return newShape;
|
|
95
95
|
}
|
|
96
|
-
//
|
|
96
|
+
// Utility to convert flat index to array of coordinates
|
|
97
97
|
static indexToCoords(index, shape, strides) {
|
|
98
98
|
const coords = new Array(shape.length);
|
|
99
99
|
let remaining = index;
|
|
@@ -105,17 +105,33 @@ class Tensor {
|
|
|
105
105
|
}
|
|
106
106
|
return coords;
|
|
107
107
|
}
|
|
108
|
-
//
|
|
109
|
-
static
|
|
108
|
+
// Utility to convert array of coordinates to *unbroadcasted* flat index
|
|
109
|
+
static coordsToUnbroadcastedIndex(coords, shape, strides) {
|
|
110
110
|
let index = 0;
|
|
111
111
|
for (let i = 0; i < coords.length; i++) {
|
|
112
|
-
const coord = coords[i];
|
|
113
112
|
// Handle broadcasting
|
|
114
|
-
const actualCoord = shape[i] === 1 ? 0 :
|
|
113
|
+
const actualCoord = shape[i] === 1 ? 0 : coords[i];
|
|
115
114
|
index += actualCoord * strides[i];
|
|
116
115
|
}
|
|
117
116
|
return index;
|
|
118
117
|
}
|
|
118
|
+
// Utility to convert array of coordinates to flat index
|
|
119
|
+
static coordsToIndex(coords, strides) {
|
|
120
|
+
let index = 0;
|
|
121
|
+
for (let i = 0; i < coords.length; i++) {
|
|
122
|
+
index += coords[i] * strides[i];
|
|
123
|
+
}
|
|
124
|
+
return index;
|
|
125
|
+
}
|
|
126
|
+
// Utility to convert shape into 1D value array size
|
|
127
|
+
static shapeToSize(shape) {
|
|
128
|
+
let prod = 1;
|
|
129
|
+
for (let i = 0; i < shape.length; i++) {
|
|
130
|
+
prod *= shape[i];
|
|
131
|
+
}
|
|
132
|
+
return prod;
|
|
133
|
+
}
|
|
134
|
+
;
|
|
119
135
|
// Utility for binary (two operators involved) element-wise ops
|
|
120
136
|
static elementWiseAB(tA, tB, op) {
|
|
121
137
|
if (typeof tA.value === "number" && typeof tB.value === "number") {
|
|
@@ -132,15 +148,15 @@ class Tensor {
|
|
|
132
148
|
const outputShape = Tensor.broadcastShapes(paddedAShape, paddedBShape);
|
|
133
149
|
// Get other output info
|
|
134
150
|
const outputStrides = Tensor.getStrides(outputShape);
|
|
135
|
-
const outputSize =
|
|
151
|
+
const outputSize = Tensor.shapeToSize(outputShape);
|
|
136
152
|
const outputValue = new Array(outputSize);
|
|
137
153
|
for (let i = 0; i < outputSize; i++) {
|
|
138
154
|
// Get coordinates from 1D index
|
|
139
155
|
const coordsOutput = Tensor.indexToCoords(i, outputShape, outputStrides);
|
|
140
156
|
// Convert the coordinates to 1D index of flattened A with respect to A's shape
|
|
141
|
-
const indexA = Tensor.
|
|
157
|
+
const indexA = Tensor.coordsToUnbroadcastedIndex(coordsOutput, paddedAShape, paddedAStrides);
|
|
142
158
|
// Convert the coordinates to 1D index of flattened B with respect to B's shape
|
|
143
|
-
const indexB = Tensor.
|
|
159
|
+
const indexB = Tensor.coordsToUnbroadcastedIndex(coordsOutput, paddedBShape, paddedBStrides);
|
|
144
160
|
// Calculate with op
|
|
145
161
|
outputValue[i] = op(tA.value[indexA], tB.value[indexB]);
|
|
146
162
|
}
|
|
@@ -321,12 +337,15 @@ class Tensor {
|
|
|
321
337
|
if (typeof dims === "undefined") {
|
|
322
338
|
dims = Array.from({ length: this.shape.length }, (_, index) => index);
|
|
323
339
|
}
|
|
340
|
+
// Dims that are reduced now have size-1
|
|
324
341
|
const outputShape = this.shape.map((dim, i) => dims.includes(i) ? 1 : dim);
|
|
325
342
|
const outputStrides = Tensor.getStrides(outputShape);
|
|
326
|
-
const outputSize =
|
|
343
|
+
const outputSize = Tensor.shapeToSize(outputShape);
|
|
327
344
|
const outputValue = new Array(outputSize).fill(0);
|
|
328
|
-
const originalSize = this.shape
|
|
345
|
+
const originalSize = Tensor.shapeToSize(this.shape);
|
|
346
|
+
// Gradient data
|
|
329
347
|
let gradShape, gradStrides, gradValue = [];
|
|
348
|
+
// Allocate gradient data only when needed
|
|
330
349
|
if (this.requiresGrad) {
|
|
331
350
|
gradShape = this.shape;
|
|
332
351
|
gradStrides = this.strides;
|
|
@@ -337,11 +356,11 @@ class Tensor {
|
|
|
337
356
|
// Force 0 on reduced axes to collapse into size-1 dims
|
|
338
357
|
const outCoords = coords.map((val, i) => dims.includes(i) ? 0 : val);
|
|
339
358
|
// Convert output coordinates to flat index
|
|
340
|
-
const outFlatIndex =
|
|
341
|
-
// Accumulate
|
|
342
|
-
const realFlatIndex =
|
|
359
|
+
const outFlatIndex = Tensor.coordsToIndex(outCoords, outputStrides);
|
|
360
|
+
// Accumulate, outFlatIndex should match multiple realFlatIndexes
|
|
361
|
+
const realFlatIndex = Tensor.coordsToIndex(coords, this.strides);
|
|
343
362
|
outputValue[outFlatIndex] += this.value[realFlatIndex];
|
|
344
|
-
// Mark for gradient
|
|
363
|
+
// Mark for gradient if needed
|
|
345
364
|
if (this.requiresGrad) {
|
|
346
365
|
(gradValue)[realFlatIndex] = 1;
|
|
347
366
|
}
|
|
@@ -578,6 +597,7 @@ class Tensor {
|
|
|
578
597
|
if (this.shape.length !== 1 || other.shape.length !== 1) {
|
|
579
598
|
throw new Error("Inputs are not 1D tensors");
|
|
580
599
|
}
|
|
600
|
+
// Simple vector dot product
|
|
581
601
|
const vectLen = this.shape[0];
|
|
582
602
|
const vectA = this.value;
|
|
583
603
|
const vectB = other.value;
|
|
@@ -615,6 +635,7 @@ class Tensor {
|
|
|
615
635
|
if (this.shape.length !== 2 || other.shape.length !== 2) {
|
|
616
636
|
throw new Error("Inputs are not matrices");
|
|
617
637
|
}
|
|
638
|
+
// Simple matrix multiplication
|
|
618
639
|
const matA = this.value;
|
|
619
640
|
const matB = other.value;
|
|
620
641
|
const matAStrides = this.strides;
|
|
@@ -627,11 +648,12 @@ class Tensor {
|
|
|
627
648
|
throw new Error("Invalid matrices shape for multiplication");
|
|
628
649
|
const matCShape = [matARows, matBCols];
|
|
629
650
|
const matCStrides = Tensor.getStrides(matCShape);
|
|
630
|
-
const matCSize =
|
|
651
|
+
const matCSize = Tensor.shapeToSize(matCShape);
|
|
631
652
|
const matC = new Array(matCSize).fill(0);
|
|
632
653
|
for (let i = 0; i < matARows; i++) {
|
|
633
654
|
for (let j = 0; j < matBCols; j++) {
|
|
634
655
|
for (let k = 0; k < matACols; k++) {
|
|
656
|
+
// Tensor values are 1D arrays so we have to get real index using strides
|
|
635
657
|
matC[i * matCStrides[0] + j * matCStrides[1]] +=
|
|
636
658
|
matA[i * matAStrides[0] + k * matAStrides[1]] *
|
|
637
659
|
matB[k * matBStrides[0] + j * matBStrides[1]];
|