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 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[]): number[][];
23
- static broadcastShapes(shapeA: number[], shapeB: number[]): number[];
24
- static indexToCoords(index: number, shape: number[], strides: number[]): number[];
25
- static coordsToIndex(coords: number[], shape: number[], strides: number[]): number;
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
- // Convert flat index to array of coordinates
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
- // Convert array of coordinates to *unbroadcasted* flat index
109
- static coordsToIndex(coords, shape, strides) {
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 : coord;
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 = outputShape.reduce((a, b) => a * b, 1);
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.coordsToIndex(coordsOutput, paddedAShape, paddedAStrides);
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.coordsToIndex(coordsOutput, paddedBShape, paddedBStrides);
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 = outputShape.reduce((a, b) => a * b, 1);
343
+ const outputSize = Tensor.shapeToSize(outputShape);
327
344
  const outputValue = new Array(outputSize).fill(0);
328
- const originalSize = this.shape.reduce((a, b) => a * b, 1);
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 = outCoords.reduce((acc, val, i) => acc + val * outputStrides[i], 0);
341
- // Accumulate
342
- const realFlatIndex = coords.reduce((acc, val, i) => acc + val * this.strides[i], 0);
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 = matCShape.reduce((a, b) => a * b, 1);
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]];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "A cute autograd engine for Javascript",
5
5
  "main": "index.js",
6
6
  "scripts": {