catniff 0.6.11 → 0.6.13

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/README.md CHANGED
@@ -132,6 +132,7 @@ All available APIs are in [`./src/`](./src/) if you want to dig deeper.
132
132
 
133
133
  * [Shakespeare-style text generator](https://github.com/nguyenphuminh/shakespeare-lm).
134
134
  * [Simple neural net for XOR calculation](./examples/xornet.js).
135
+ * [N-th order derivative calculation](./examples/nthorder.js).
135
136
  * [Tensors](./examples/tensors.js).
136
137
  * [Optimizer](./examples/optim.js).
137
138
  * [Simple quadratic equation](./examples/quadratic.js).
package/dist/core.d.ts CHANGED
@@ -23,6 +23,8 @@ export declare class Tensor {
23
23
  children: Tensor[];
24
24
  device: string;
25
25
  static training: boolean;
26
+ static noGrad: boolean;
27
+ static createGraph: boolean;
26
28
  constructor(value: TensorValue, options?: TensorOptions);
27
29
  static flattenValue(tensor: TensorValue): number[] | number;
28
30
  static getShape(tensor: TensorValue): number[];
@@ -214,7 +216,6 @@ export declare class Tensor {
214
216
  zeroGrad?: boolean;
215
217
  }): void;
216
218
  val(): TensorValue;
217
- withGrad(requiresGrad: boolean): Tensor;
218
219
  detach(): Tensor;
219
220
  clone(): Tensor;
220
221
  replace(other: Tensor | TensorValue, allowShapeMismatch?: boolean): Tensor;
package/dist/core.js CHANGED
@@ -14,6 +14,8 @@ class Tensor {
14
14
  children;
15
15
  device;
16
16
  static training = false;
17
+ static noGrad = false;
18
+ static createGraph = false;
17
19
  constructor(value, options = {}) {
18
20
  // Storage
19
21
  this.value = Tensor.flattenValue(value);
@@ -183,14 +185,22 @@ class Tensor {
183
185
  static elementWiseSelf(tA, op) {
184
186
  if (typeof tA.value === "number")
185
187
  return new Tensor(op(tA.value));
188
+ const contiguous = tA.isContiguous();
186
189
  const outputShape = tA.shape;
187
- const outputStrides = Tensor.getStrides(outputShape);
190
+ const outputStrides = contiguous ? tA.strides : Tensor.getStrides(outputShape);
188
191
  const outputSize = tA.numel;
189
192
  const outputValue = new Array(outputSize);
190
- for (let index = 0; index < outputSize; index++) {
191
- const outputCoords = Tensor.indexToCoords(index, outputStrides);
192
- const originalIndex = tA.offset + Tensor.coordsToIndex(outputCoords, tA.strides);
193
- outputValue[index] = op(tA.value[originalIndex]);
193
+ if (contiguous) {
194
+ for (let index = 0; index < outputSize; index++) {
195
+ outputValue[index] = op(tA.value[index + tA.offset]);
196
+ }
197
+ }
198
+ else {
199
+ for (let index = 0; index < outputSize; index++) {
200
+ const outputCoords = Tensor.indexToCoords(index, outputStrides);
201
+ const originalIndex = tA.offset + Tensor.coordsToIndex(outputCoords, tA.strides);
202
+ outputValue[index] = op(tA.value[originalIndex]);
203
+ }
194
204
  }
195
205
  return new Tensor(outputValue, { shape: outputShape, strides: outputStrides, numel: tA.numel });
196
206
  }
@@ -210,12 +220,12 @@ class Tensor {
210
220
  out.gradFn = () => {
211
221
  // Disable gradient collecting of gradients themselves
212
222
  const outGrad = out.grad;
213
- const selfNoGrad = this.detach();
214
- const otherNoGrad = other.detach();
223
+ const selfWithGrad = Tensor.createGraph ? this : this.detach();
224
+ const otherWithGrad = Tensor.createGraph ? other : other.detach();
215
225
  if (this.requiresGrad)
216
- Tensor.addGrad(this, thisGrad(selfNoGrad, otherNoGrad, outGrad));
226
+ Tensor.addGrad(this, thisGrad(selfWithGrad, otherWithGrad, outGrad));
217
227
  if (other.requiresGrad)
218
- Tensor.addGrad(other, otherGrad(selfNoGrad, otherNoGrad, outGrad));
228
+ Tensor.addGrad(other, otherGrad(selfWithGrad, otherWithGrad, outGrad));
219
229
  };
220
230
  }
221
231
  return out;
@@ -231,9 +241,9 @@ class Tensor {
231
241
  out.gradFn = () => {
232
242
  // Disable gradient collecting of gradients themselves
233
243
  const outGrad = out.grad;
234
- const selfNoGrad = this.detach();
244
+ const selfWithGrad = Tensor.createGraph ? this : this.detach();
235
245
  if (this.requiresGrad)
236
- Tensor.addGrad(this, thisGrad(selfNoGrad, outGrad));
246
+ Tensor.addGrad(this, thisGrad(selfWithGrad, outGrad));
237
247
  };
238
248
  }
239
249
  return out;
@@ -289,9 +299,6 @@ class Tensor {
289
299
  // Contiguity-related ops
290
300
  isContiguous() {
291
301
  const expectedStrides = Tensor.getStrides(this.shape);
292
- if (expectedStrides.length !== this.strides.length) {
293
- return false;
294
- }
295
302
  for (let i = 0; i < this.strides.length; i++) {
296
303
  if (this.strides[i] !== expectedStrides[i]) {
297
304
  return false;
@@ -340,6 +347,7 @@ class Tensor {
340
347
  const out = new Tensor(this.value, {
341
348
  shape: newShape,
342
349
  strides: outputStrides,
350
+ offset: this.offset,
343
351
  numel: outputSize,
344
352
  device: this.device
345
353
  });
@@ -354,28 +362,7 @@ class Tensor {
354
362
  return out;
355
363
  }
356
364
  reshape(newShape) {
357
- // Verify shape size
358
- const originalSize = this.numel;
359
- const outputSize = Tensor.shapeToSize(newShape);
360
- if (originalSize !== outputSize || typeof this.value === "number") {
361
- throw new Error("Can not reshape: incompatible sizes");
362
- }
363
- // Create new tensor with forced compatibility (only contiguity for now)
364
- const outputStrides = Tensor.getStrides(newShape);
365
- const out = new Tensor(this.contiguous().value, {
366
- shape: newShape,
367
- strides: outputStrides,
368
- numel: outputSize
369
- });
370
- // Gradient reshaped and flow back to the original tensor
371
- if (this.requiresGrad) {
372
- out.requiresGrad = true;
373
- out.children.push(this);
374
- out.gradFn = () => {
375
- Tensor.addGrad(this, out.grad.reshape(this.shape));
376
- };
377
- }
378
- return out;
365
+ return this.contiguous().view(newShape);
379
366
  }
380
367
  flatten(startDim = 0, endDim = -1) {
381
368
  // Handle negative indices
@@ -550,7 +537,7 @@ class Tensor {
550
537
  }
551
538
  // Tensor indexing
552
539
  index(indices) {
553
- const tensorIndices = this.handleOther(indices).contiguous();
540
+ const tensorIndices = this.handleOther(indices).clone();
554
541
  if (typeof tensorIndices.value === "number") {
555
542
  return this.indexWithArray([tensorIndices.value]).squeeze(0);
556
543
  }
@@ -1353,12 +1340,12 @@ class Tensor {
1353
1340
  out.gradFn = () => {
1354
1341
  // Disable gradient collecting of gradients themselves
1355
1342
  const outGrad = out.grad;
1356
- const selfNoGrad = this.detach();
1357
- const otherNoGrad = other.detach();
1343
+ const selfWithGrad = Tensor.createGraph ? this : this.detach();
1344
+ const otherWithGrad = Tensor.createGraph ? other : other.detach();
1358
1345
  if (this.requiresGrad)
1359
- Tensor.addGrad(this, outGrad.mm(otherNoGrad.t()));
1346
+ Tensor.addGrad(this, outGrad.mm(otherWithGrad.t()));
1360
1347
  if (other.requiresGrad)
1361
- Tensor.addGrad(other, selfNoGrad.t().mm(outGrad));
1348
+ Tensor.addGrad(other, selfWithGrad.t().mm(outGrad));
1362
1349
  };
1363
1350
  }
1364
1351
  return out;
@@ -1411,12 +1398,12 @@ class Tensor {
1411
1398
  out.gradFn = () => {
1412
1399
  // Disable gradient collecting of gradients themselves
1413
1400
  const outGrad = out.grad;
1414
- const selfNoGrad = this.detach();
1415
- const otherNoGrad = other.detach();
1401
+ const selfWithGrad = Tensor.createGraph ? this : this.detach();
1402
+ const otherWithGrad = Tensor.createGraph ? other : other.detach();
1416
1403
  if (this.requiresGrad)
1417
- Tensor.addGrad(this, outGrad.bmm(otherNoGrad.transpose(1, 2)));
1404
+ Tensor.addGrad(this, outGrad.bmm(otherWithGrad.transpose(1, 2)));
1418
1405
  if (other.requiresGrad)
1419
- Tensor.addGrad(other, selfNoGrad.transpose(1, 2).bmm(outGrad));
1406
+ Tensor.addGrad(other, selfWithGrad.transpose(1, 2).bmm(outGrad));
1420
1407
  };
1421
1408
  }
1422
1409
  return out;
@@ -1511,12 +1498,12 @@ class Tensor {
1511
1498
  out.gradFn = () => {
1512
1499
  other = other;
1513
1500
  const outGrad = out.grad;
1514
- const selfNoGrad = self.detach();
1515
- const otherNoGrad = other.detach();
1501
+ const selfWithGrad = Tensor.createGraph ? self : self.detach();
1502
+ const otherWithGrad = Tensor.createGraph ? other : other.detach();
1516
1503
  if (this.requiresGrad)
1517
- Tensor.addGrad(this, outGrad.matmul(otherNoGrad.transpose(-2, -1)));
1504
+ Tensor.addGrad(this, outGrad.matmul(otherWithGrad.transpose(-2, -1)));
1518
1505
  if (other.requiresGrad)
1519
- Tensor.addGrad(other, selfNoGrad.transpose(-2, -1).matmul(outGrad));
1506
+ Tensor.addGrad(other, selfWithGrad.transpose(-2, -1).matmul(outGrad));
1520
1507
  };
1521
1508
  }
1522
1509
  return out;
@@ -1801,7 +1788,7 @@ class Tensor {
1801
1788
  const visited = new Set();
1802
1789
  function build(node) {
1803
1790
  // Only collects unvisited node and node that requires gradient
1804
- if (!visited.has(node) && node.requiresGrad) {
1791
+ if (!visited.has(node) && node.requiresGrad && !Tensor.noGrad) {
1805
1792
  visited.add(node);
1806
1793
  // Reset grad to zeros if specified
1807
1794
  if (zeroGrad) {
@@ -1841,17 +1828,6 @@ class Tensor {
1841
1828
  }
1842
1829
  return buildNested(this.value, this.shape, this.strides, this.offset);
1843
1830
  }
1844
- // Returns a view of the tensor with gradient turned on/off and detaches from autograd
1845
- withGrad(requiresGrad) {
1846
- return new Tensor(this.value, {
1847
- shape: this.shape,
1848
- strides: this.strides,
1849
- offset: this.offset,
1850
- numel: this.numel,
1851
- device: this.device,
1852
- requiresGrad
1853
- });
1854
- }
1855
1831
  // Returns a view of the tensor with gradient turned off and detaches from autograd
1856
1832
  detach() {
1857
1833
  return new Tensor(this.value, {
@@ -1863,15 +1839,39 @@ class Tensor {
1863
1839
  requiresGrad: false
1864
1840
  });
1865
1841
  }
1866
- // Returns a copy of the tensor (with new data allocation) and detaches from autograd
1842
+ // Returns a copy of the tensor (with new data allocation) and keeps grad connection
1867
1843
  clone() {
1868
- return new Tensor(typeof this.value === "number" ? this.value : [...this.value], {
1869
- shape: this.shape,
1870
- strides: this.strides,
1871
- offset: this.offset,
1872
- numel: this.numel,
1873
- requiresGrad: this.requiresGrad
1874
- });
1844
+ let out;
1845
+ if (typeof this.value === "number") {
1846
+ out = new Tensor(this.value);
1847
+ }
1848
+ else {
1849
+ const contiguous = this.isContiguous();
1850
+ const outputStrides = contiguous ? this.strides : Tensor.getStrides(this.shape);
1851
+ const outputSize = this.numel;
1852
+ const outputValue = new Array(outputSize);
1853
+ if (contiguous) {
1854
+ for (let index = 0; index < outputSize; index++) {
1855
+ outputValue[index] = this.value[this.offset + index];
1856
+ }
1857
+ }
1858
+ else {
1859
+ for (let index = 0; index < outputSize; index++) {
1860
+ const outputCoords = Tensor.indexToCoords(index, outputStrides);
1861
+ const originalIndex = Tensor.coordsToIndex(outputCoords, this.strides);
1862
+ outputValue[index] = this.value[this.offset + originalIndex];
1863
+ }
1864
+ }
1865
+ out = new Tensor(outputValue, { shape: this.shape, strides: outputStrides, numel: outputSize });
1866
+ }
1867
+ if (this.requiresGrad) {
1868
+ out.requiresGrad = true;
1869
+ out.children.push(this);
1870
+ out.gradFn = () => {
1871
+ Tensor.addGrad(this, out.grad);
1872
+ };
1873
+ }
1874
+ return out;
1875
1875
  }
1876
1876
  // Returns this tensor with value replaced with the value of another tensor
1877
1877
  replace(other, allowShapeMismatch = false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "A small Torch-like deep learning framework for Javascript",
5
5
  "main": "index.js",
6
6
  "scripts": {