@tensorflow/tfjs-core
Version:
Hardware-accelerated JavaScript library for machine intelligence
1,020 lines • 81.7 kB
JavaScript
"use strict";
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var _this = this;
Object.defineProperty(exports, "__esModule", { value: true });
var backend_webgl_1 = require("../backends/webgl/backend_webgl");
var tf = require("../index");
var jasmine_util_1 = require("../jasmine_util");
var test_util_1 = require("../test_util");
jasmine_util_1.describeWithFlags('matmul', jasmine_util_1.ALL_ENVS, function () {
it('A x B', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]);
c = tf.matMul(a, b);
expect(c.shape).toEqual([2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), [0, 8, -3, 20]]);
return [2 /*return*/];
}
});
}); });
it('[8,4]x[4,8]', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, cData;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
a = tf.tensor2d([
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 1, 2, 3, 4, 5, 6, 7, 8
], [8, 4]);
b = tf.tensor2d([
0, 1, -3, 2, 1, -1, 0, 5, 6, 7, 8, 0, -2, -2, 1, 9,
11, 10, 0, 1, -3, 2, 1, -1, 1, 2, 3, 4, 5, 6, 7, 8
], [4, 8]);
c = tf.matMul(a, b);
return [4 /*yield*/, c.data()];
case 1:
cData = _a.sent();
expect(c.shape).toEqual([8, 8]);
test_util_1.expectArraysClose(cData, [
49, 53, 25, 21, 8, 25, 33, 52, 121, 133, 57, 49, 12,
45, 69, 136, 193, 213, 89, 77, 16, 65, 105, 220, 265, 293,
121, 105, 20, 85, 141, 304, 337, 373, 153, 133, 24, 105, 177,
388, 409, 453, 185, 161, 28, 125, 213, 472, 49, 53, 25, 21,
8, 25, 33, 52, 121, 133, 57, 49, 12, 45, 69, 136
]);
return [2 /*return*/];
}
});
}); });
it('matmul followed by mul', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, f, d, dData;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
a = tf.tensor2d([1, 2, 3, 4], [2, 2]);
b = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
c = tf.matMul(a, b);
f = tf.tensor2d([0, 1, 0.5, 0, 0.25, 2], [2, 3]);
d = tf.mul(c, f);
return [4 /*yield*/, d.data()];
case 1:
dData = _a.sent();
expect(d.shape).toEqual([2, 3]);
test_util_1.expectArraysClose(dData, [0, 12, 7.5, 0, 6.5, 66]);
return [2 /*return*/];
}
});
}); });
it('upcasts when dtypes dont match', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
a = [1, 2, 3, 4, 5, 6];
b = [0, 1, -3, 2, 2, 1];
c = tf.matMul(tf.tensor(a, [2, 3], 'float32'), tf.tensor(b, [3, 2], 'int32'));
expect(c.shape).toEqual([2, 2]);
expect(c.dtype).toBe('float32');
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_c.sent(), [0, 8, -3, 20]]);
c = tf.matMul(tf.tensor(a, [2, 3], 'int32'), tf.tensor(b, [3, 2], 'bool'));
expect(c.shape).toEqual([2, 2]);
expect(c.dtype).toBe('int32');
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 2:
_b.apply(void 0, [_c.sent(), [5, 6, 11, 15]]);
return [2 /*return*/];
}
});
}); });
it('A x B^t', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, transposeA, transposeB, c, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]);
transposeA = false;
transposeB = true;
c = tf.matMul(a, b, transposeA, transposeB);
expected = [7, 10, 16, 31];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('A^t x B', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, transposeA, transposeB, c, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]);
transposeA = true;
transposeB = false;
c = tf.matMul(a, b, transposeA, transposeB);
expected = [17, 12, 2, 22, 15, 4, 27, 18, 6];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('A^t x B^t', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, transposeA, transposeB, c, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([1, 2, 3, 4, 5, 6], [3, 2]);
b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]);
transposeA = true;
transposeB = true;
c = tf.matMul(a, b, transposeA, transposeB);
expected = [11, 13, 14, 20];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('A x B^t shapes do not match', function () {
var a = tf.zeros([2, 3]);
var b = tf.zeros([3, 2]);
var f = function () {
var transposeA = false;
var transposeB = true;
tf.matMul(a, b, transposeA, transposeB);
};
expect(f).toThrowError();
});
it('A^t x B shapes do not match', function () {
var a = tf.zeros([2, 3]);
var b = tf.zeros([3, 2]);
var f = function () {
var transposeA = true;
var transposeB = false;
tf.matMul(a, b, transposeA, transposeB);
};
expect(f).toThrowError();
});
it('A^t x B^t shapes do not match', function () {
var a = tf.zeros([3, 2]);
var b = tf.zeros([3, 2]);
var f = function () {
var transposeA = true;
var transposeB = true;
tf.matMul(a, b, transposeA, transposeB);
};
expect(f).toThrowError();
});
it('matmul throws when inner dimensions dont match', function () {
var a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
var b = tf.tensor2d([0, 1, -3, 2, 2, 1, 2, 2], [4, 2]);
expect(function () { return tf.matMul(a, b); }).toThrowError();
});
it('matmul throws when passed non matrices', function () {
// tslint:disable-next-line:no-any
var a = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 3, 2]);
var b = tf.tensor2d([0, 1, -3, 2, 2, 1, 2, 2], [4, 2]);
expect(function () { return tf.matMul(a, b); }).toThrowError();
expect(function () { return tf.matMul(b, a); }).toThrowError();
});
it('matmul throws when passed a vector', function () {
// tslint:disable-next-line:no-any
var v = tf.tensor1d([2, 3]);
var matrix = tf.tensor2d([1, 2, 3, 4], [2, 2]);
expect(function () { return tf.matMul(matrix, v); }).toThrowError();
});
it('Vector times matrix', function () { return __awaiter(_this, void 0, void 0, function () {
var v, matrix, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v = tf.tensor1d([2, 3]);
matrix = tf.tensor2d([1, 2, 3, 4], [2, 2]);
result = tf.dot(v, matrix);
expected = [11, 16];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('Vector times matrix with implicit reshape', function () { return __awaiter(_this, void 0, void 0, function () {
var v, matrix, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v = tf.tensor1d([2, 3]);
matrix = tf.tensor2d([1, 2, 3, 4], [2, 2]);
result = tf.dot(v, matrix);
expected = [11, 16];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('Matrix times vector', function () { return __awaiter(_this, void 0, void 0, function () {
var matrix, v, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
matrix = tf.tensor2d([1, 2, 3, 4], [2, 2]);
v = tf.tensor1d([2, 3]);
result = tf.dot(matrix, v);
expected = [8, 18];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with the matrices being vectors', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, 1, sharedDim]);
b = tf.tensor(values, [batch, sharedDim, 1]);
result = tf.matMul(a, b);
expect(result.shape).toEqual([batch, 1, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [4, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul called twice so memory of output is reused', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, n, vals, a, b, result, _a, vals2, a2, b2, result2, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
batch = 3;
n = 2;
vals = new Float32Array(batch * n * n);
vals[0] = 2;
vals[4] = 3;
vals[8] = 4;
a = tf.tensor(vals, [batch, n, n]);
b = tf.tensor(vals, [batch, n, n]);
result = tf.matMul(a, b);
expect(result.shape).toEqual([batch, n, n]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_c.sent(), [4, 0, 0, 0, 9, 0, 0, 0, 16, 0, 0, 0]]);
// Dispose the first output, so memory of the second output (which has the
// same shape), could be reused.
result.dispose();
vals2 = new Float32Array(batch * n * n);
vals2[3] = 2;
vals2[7] = 3;
vals2[11] = 4;
a2 = tf.tensor(vals2, [batch, n, n]);
b2 = tf.tensor(vals2, [batch, n, n]);
result2 = tf.matMul(a2, b2);
expect(result2.shape).toEqual([batch, n, n]);
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, result2.data()];
case 2:
_b.apply(void 0, [_c.sent(), [0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 16]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with the matrices being vectors transposedA', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, sharedDim, 1]);
b = tf.tensor(values, [batch, sharedDim, 1]);
transposeA = true;
transposeB = false;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 1, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [4, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with the matrices being vectors transposedB', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, 1, sharedDim]);
b = tf.tensor(values, [batch, 1, sharedDim]);
transposeA = false;
transposeB = true;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 1, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [4, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with matrix x vector', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.ones([batch, 2, sharedDim]);
b = tf.tensor(values, [batch, sharedDim, 1]);
result = tf.matMul(a, b);
expect(result.shape).toEqual([batch, 2, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with matrix x vector transposedA', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.ones([batch, sharedDim, 2]);
b = tf.tensor(values, [batch, sharedDim, 1]);
transposeA = true;
transposeB = false;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 2, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with matrix x vector transposedB', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.ones([batch, 2, sharedDim]);
b = tf.tensor(values, [batch, 1, sharedDim]);
transposeA = false;
transposeB = true;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 2, 1]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with vector x matrix', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, 1, sharedDim]);
b = tf.ones([batch, sharedDim, 2]);
result = tf.matMul(a, b);
expect(result.shape).toEqual([batch, 1, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with vector x matrix transposedA', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, sharedDim, 1]);
b = tf.ones([batch, sharedDim, 2]);
transposeA = true;
transposeB = false;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 1, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('batched matmul with vector x matrix transposedB', function () { return __awaiter(_this, void 0, void 0, function () {
var batch, sharedDim, values, a, b, transposeA, transposeB, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
batch = 3;
sharedDim = backend_webgl_1.MATMUL_SHARED_DIM_THRESHOLD + 1;
values = new Float32Array(batch * sharedDim);
values[10] = 2;
a = tf.tensor(values, [batch, 1, sharedDim]);
b = tf.ones([batch, 2, sharedDim]);
transposeA = false;
transposeB = true;
result = tf.matMul(a, b, transposeA, transposeB);
expect(result.shape).toEqual([batch, 1, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [2, 2, 0, 0, 0, 0]]);
return [2 /*return*/];
}
});
}); });
it('Matrix * vector propagates NaNs', function () { return __awaiter(_this, void 0, void 0, function () {
var matrix, v, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
matrix = tf.tensor2d([1, 2, 3, 4], [2, 2]);
v = tf.tensor1d([2, NaN]);
result = tf.dot(matrix, v);
expected = [NaN, NaN];
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('matrix times vector throws when not passed a matrix', function () {
var v = tf.tensor1d([2, 3]);
// tslint:disable-next-line:no-any
var matrix = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8], [2, 2, 2]);
expect(function () { return tf.dot(matrix, v); }).toThrowError();
});
it('Dot product', function () { return __awaiter(_this, void 0, void 0, function () {
var v1, v2, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v1 = tf.tensor1d([2, 3]);
v2 = tf.tensor1d([2, 1]);
result = tf.dot(v1, v2);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [7]]);
return [2 /*return*/];
}
});
}); });
it('Dot product propagates NaNs', function () { return __awaiter(_this, void 0, void 0, function () {
var v1, v2, result, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v1 = tf.tensor1d([2, NaN]);
v2 = tf.tensor1d([2, 1]);
result = tf.dot(v1, v2);
_a = test_util_1.expectArraysEqual;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), [NaN]]);
return [2 /*return*/];
}
});
}); });
it('Dot product throws when vectors are different size', function () {
var v1 = tf.tensor1d([2, 3, 3]);
var v2 = tf.tensor1d([2, 1]);
expect(function () { return tf.dot(v1, v2); }).toThrowError();
expect(function () { return tf.dot(v2, v1); }).toThrowError();
});
it('Outer product', function () { return __awaiter(_this, void 0, void 0, function () {
var v1, v2, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v1 = tf.tensor1d([2, 3]);
v2 = tf.tensor1d([2, 1]);
result = tf.outerProduct(v1, v2);
expected = [4, 2, 6, 3];
expect(result.shape).toEqual([2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('outer product accepts a tensor-like object', function () { return __awaiter(_this, void 0, void 0, function () {
var v1, v2, result, expected, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
v1 = [2, 3];
v2 = [2, 1];
result = tf.outerProduct(v1, v2);
expected = [4, 2, 6, 3];
expect(result.shape).toEqual([2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, result.data()];
case 1:
_a.apply(void 0, [_b.sent(), expected]);
return [2 /*return*/];
}
});
}); });
it('gradients: A * B', function () { return __awaiter(_this, void 0, void 0, function () {
var aT, bT, dyT, transposeA, transposeB, grads, _a, da, db, a, dy, b, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
aT = tf.tensor2d([1, 2, 3, 10, 20, 30], [2, 3]);
bT = tf.tensor2d([2, 3, 4, 1, 2, 3], [3, 2]);
dyT = tf.tensor2d([1, 10, 20, 30], [2, 2]);
transposeA = false;
transposeB = false;
grads = tf.grads(function (a, b) {
return tf.matMul(a, b, transposeA, transposeB);
});
_a = grads([aT, bT], dyT), da = _a[0], db = _a[1];
// da = dy * bT
expect(da.shape).toEqual(aT.shape);
return [4 /*yield*/, aT.buffer()];
case 1:
a = _d.sent();
return [4 /*yield*/, dyT.buffer()];
case 2:
dy = _d.sent();
return [4 /*yield*/, bT.buffer()];
case 3:
b = _d.sent();
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, da.data()];
case 4:
_b.apply(void 0, [_d.sent(),
[
dy.get(0, 0) * b.get(0, 0) + dy.get(0, 1) * b.get(0, 1),
dy.get(0, 0) * b.get(1, 0) + dy.get(0, 1) * b.get(1, 1),
dy.get(0, 0) * b.get(2, 0) + dy.get(0, 1) * b.get(2, 1),
dy.get(1, 0) * b.get(0, 0) + dy.get(1, 1) * b.get(0, 1),
dy.get(1, 0) * b.get(1, 0) + dy.get(1, 1) * b.get(1, 1),
dy.get(1, 0) * b.get(2, 0) + dy.get(1, 1) * b.get(2, 1)
],
1e-1]);
// db = aT * dy
expect(db.shape).toEqual(b.shape);
_c = test_util_1.expectArraysClose;
return [4 /*yield*/, db.data()];
case 5:
_c.apply(void 0, [_d.sent(), [
a.get(0, 0) * dy.get(0, 0) + a.get(1, 0) * dy.get(1, 0),
a.get(0, 0) * dy.get(0, 1) + a.get(1, 0) * dy.get(1, 1),
a.get(0, 1) * dy.get(0, 0) + a.get(1, 1) * dy.get(1, 0),
a.get(0, 1) * dy.get(0, 1) + a.get(1, 1) * dy.get(1, 1),
a.get(0, 2) * dy.get(0, 0) + a.get(1, 2) * dy.get(1, 0),
a.get(0, 2) * dy.get(0, 1) + a.get(1, 2) * dy.get(1, 1)
]]);
return [2 /*return*/];
}
});
}); });
it('gradient with clones', function () {
var a = tf.tensor2d([1, 2, 3, 10, 20, 30], [2, 3]);
var b = tf.tensor2d([2, 3, 4, 1, 2, 3], [3, 2]);
var grads = tf.grads(function (a, b) {
return tf.matMul(a.clone(), b.clone()).clone();
});
var _a = grads([a, b]), da = _a[0], db = _a[1];
expect(da.shape).toEqual(a.shape);
expect(db.shape).toEqual(b.shape);
});
it('gradients: a * bT', function () { return __awaiter(_this, void 0, void 0, function () {
var aT, bT, dyT, transposeA, transposeB, grads, _a, da, db, a, dy, b, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
aT = tf.tensor2d([1, 2, 3, 10, 20, 30], [3, 2]);
bT = tf.tensor2d([2, 3, 4, 1, 2, 3], [3, 2]);
dyT = tf.tensor2d([1, 10, 20, 30, 40, 50, 60, 70, 80], [3, 3]);
transposeA = false;
transposeB = true;
grads = tf.grads(function (a, b) {
return tf.matMul(a, b, transposeA, transposeB);
});
_a = grads([aT, bT], dyT), da = _a[0], db = _a[1];
// da = dy * b
expect(da.shape).toEqual(aT.shape);
return [4 /*yield*/, aT.buffer()];
case 1:
a = _d.sent();
return [4 /*yield*/, dyT.buffer()];
case 2:
dy = _d.sent();
return [4 /*yield*/, bT.buffer()];
case 3:
b = _d.sent();
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, da.data()];
case 4:
_b.apply(void 0, [_d.sent(), [
dy.get(0, 0) * b.get(0, 0) + dy.get(0, 1) * b.get(1, 0) +
dy.get(0, 2) * b.get(2, 0),
dy.get(0, 0) * b.get(0, 1) + dy.get(0, 1) * b.get(1, 1) +
dy.get(0, 2) * b.get(2, 1),
dy.get(1, 0) * b.get(0, 0) + dy.get(1, 1) * b.get(1, 0) +
dy.get(1, 2) * b.get(2, 0),
dy.get(1, 0) * b.get(0, 1) + dy.get(1, 1) * b.get(1, 1) +
dy.get(1, 2) * b.get(2, 1),
dy.get(2, 0) * b.get(0, 0) + dy.get(2, 1) * b.get(1, 0) +
dy.get(2, 2) * b.get(2, 0),
dy.get(2, 0) * b.get(0, 1) + dy.get(2, 1) * b.get(1, 1) +
dy.get(2, 2) * b.get(2, 1)
]]);
// db = dyT * a
expect(db.shape).toEqual(b.shape);
_c = test_util_1.expectArraysClose;
return [4 /*yield*/, db.data()];
case 5:
_c.apply(void 0, [_d.sent(), [
dy.get(0, 0) * a.get(0, 0) + dy.get(1, 0) * a.get(1, 0) +
dy.get(2, 0) * a.get(2, 0),
dy.get(0, 0) * a.get(0, 1) + dy.get(1, 0) * a.get(1, 1) +
dy.get(2, 0) * a.get(2, 1),
dy.get(0, 1) * a.get(0, 0) + dy.get(1, 1) * a.get(1, 0) +
dy.get(2, 1) * a.get(2, 0),
dy.get(0, 1) * a.get(0, 1) + dy.get(1, 1) * a.get(1, 1) +
dy.get(2, 1) * a.get(2, 1),
dy.get(0, 2) * a.get(0, 0) + dy.get(1, 2) * a.get(1, 0) +
dy.get(2, 2) * a.get(2, 0),
dy.get(0, 2) * a.get(0, 1) + dy.get(1, 2) * a.get(1, 1) +
dy.get(2, 2) * a.get(2, 1)
]]);
return [2 /*return*/];
}
});
}); });
it('gradients: aT * b', function () { return __awaiter(_this, void 0, void 0, function () {
var aT, bT, dyT, transposeA, transposeB, grads, _a, da, db, a, dy, b, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
aT = tf.tensor2d([1, 2, 3, 10, 20, 30], [3, 2]);
bT = tf.tensor2d([2, 3, 4, 1, 2, 3], [3, 2]);
dyT = tf.tensor2d([1, 10, 20, 30], [2, 2]);
transposeA = true;
transposeB = false;
grads = tf.grads(function (a, b) {
return tf.matMul(a, b, transposeA, transposeB);
});
_a = grads([aT, bT], dyT), da = _a[0], db = _a[1];
// da = b * dyT
expect(da.shape).toEqual(aT.shape);
return [4 /*yield*/, aT.buffer()];
case 1:
a = _d.sent();
return [4 /*yield*/, dyT.buffer()];
case 2:
dy = _d.sent();
return [4 /*yield*/, bT.buffer()];
case 3:
b = _d.sent();
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, da.data()];
case 4:
_b.apply(void 0, [_d.sent(), [
dy.get(0, 0) * b.get(0, 0) + dy.get(0, 1) * b.get(0, 1),
dy.get(1, 0) * b.get(0, 0) + dy.get(1, 1) * b.get(0, 1),
dy.get(0, 0) * b.get(1, 0) + dy.get(0, 1) * b.get(1, 1),
dy.get(1, 0) * b.get(1, 0) + dy.get(1, 1) * b.get(1, 1),
dy.get(0, 0) * b.get(2, 0) + dy.get(0, 1) * b.get(2, 1),
dy.get(1, 0) * b.get(2, 0) + dy.get(1, 1) * b.get(2, 1)
]]);
// db = a * dy
expect(db.shape).toEqual(b.shape);
_c = test_util_1.expectArraysClose;
return [4 /*yield*/, db.data()];
case 5:
_c.apply(void 0, [_d.sent(), [
dy.get(0, 0) * a.get(0, 0) + dy.get(1, 0) * a.get(0, 1),
dy.get(0, 1) * a.get(0, 0) + dy.get(1, 1) * a.get(0, 1),
dy.get(0, 0) * a.get(1, 0) + dy.get(1, 0) * a.get(1, 1),
dy.get(0, 1) * a.get(1, 0) + dy.get(1, 1) * a.get(1, 1),
dy.get(0, 0) * a.get(2, 0) + dy.get(1, 0) * a.get(2, 1),
dy.get(0, 1) * a.get(2, 0) + dy.get(1, 1) * a.get(2, 1)
]]);
return [2 /*return*/];
}
});
}); });
it('gradients: aT * bT', function () { return __awaiter(_this, void 0, void 0, function () {
var aT, bT, dyT, transposeA, transposeB, grads, _a, da, db, a, dy, b, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
aT = tf.tensor2d([1, 2, 3, 10, 20, 30], [3, 2]);
bT = tf.tensor2d([2, 3, 4, 1, 2, 3], [2, 3]);
dyT = tf.tensor2d([1, 10, 20, 30], [2, 2]);
transposeA = true;
transposeB = true;
grads = tf.grads(function (a, b) {
return tf.matMul(a, b, transposeA, transposeB);
});
_a = grads([aT, bT], dyT), da = _a[0], db = _a[1];
// da = bT * dyT
expect(da.shape).toEqual(aT.shape);
return [4 /*yield*/, aT.buffer()];
case 1:
a = _d.sent();
return [4 /*yield*/, dyT.buffer()];
case 2:
dy = _d.sent();
return [4 /*yield*/, bT.buffer()];
case 3:
b = _d.sent();
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, da.data()];
case 4:
_b.apply(void 0, [_d.sent(), [
dy.get(0, 0) * b.get(0, 0) + dy.get(0, 1) * b.get(1, 0),
dy.get(1, 0) * b.get(0, 0) + dy.get(1, 1) * b.get(1, 0),
dy.get(0, 0) * b.get(0, 1) + dy.get(0, 1) * b.get(1, 1),
dy.get(1, 0) * b.get(0, 1) + dy.get(1, 1) * b.get(1, 1),
dy.get(0, 0) * b.get(0, 2) + dy.get(0, 1) * b.get(1, 2),
dy.get(1, 0) * b.get(0, 2) + dy.get(1, 1) * b.get(1, 2)
]]);
// db = dyT * aT
expect(db.shape).toEqual(b.shape);
_c = test_util_1.expectArraysClose;
return [4 /*yield*/, db.data()];
case 5:
_c.apply(void 0, [_d.sent(), [
dy.get(0, 0) * a.get(0, 0) + dy.get(1, 0) * a.get(0, 1),
dy.get(0, 0) * a.get(1, 0) + dy.get(1, 0) * a.get(1, 1),
dy.get(0, 0) * a.get(2, 0) + dy.get(1, 0) * a.get(2, 1),
dy.get(0, 1) * a.get(0, 0) + dy.get(1, 1) * a.get(0, 1),
dy.get(0, 1) * a.get(1, 0) + dy.get(1, 1) * a.get(1, 1),
dy.get(0, 1) * a.get(2, 0) + dy.get(1, 1) * a.get(2, 1)
]]);
return [2 /*return*/];
}
});
}); });
it('throws when passed a as a non-tensor', function () {
expect(function () { return tf.matMul({}, tf.tensor2d([2], [1, 1])); })
.toThrowError(/Argument 'a' passed to 'matMul' must be a Tensor/);
});
it('throws when passed b as a non-tensor', function () {
expect(function () { return tf.matMul(tf.tensor2d([2], [1, 1]), {}); })
.toThrowError(/Argument 'b' passed to 'matMul' must be a Tensor/);
});
it('accepts a tensor-like object', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = [[1, 2, 3], [4, 5, 6]];
b = [[0, 1], [-3, 2], [2, 1]];
c = tf.matMul(a, b);
expect(c.shape).toEqual([2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), [0, 8, -3, 20]]);
return [2 /*return*/];
}
});
}); });
it('accepts a tensor-like object chained', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([[1, 2, 3], [4, 5, 6]], [2, 3]);
b = [[0, 1], [-3, 2], [2, 1]];
c = a.matMul(b);
expect(c.shape).toEqual([2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), [0, 8, -3, 20]]);
return [2 /*return*/];
}
});
}); });
it('a * b where a has zero in its shape', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor2d([], [0, 3]);
b = tf.tensor2d([1, 2, 3, 4, 5, 6], [3, 2]);
c = tf.matMul(a, b);
expect(c.shape).toEqual([0, 2]);
expect(c.rank).toBe(2);
expect(c.size).toBe(0);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), []]);
return [2 /*return*/];
}
});
}); });
it('(a * b) * c where a has zero in its shape, so a*b does also', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, ab, _a, c, res, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
a = tf.tensor2d([], [0, 3]);
b = tf.tensor2d([1, 2, 3, 4, 5, 6], [3, 2]);
ab = tf.matMul(a, b);
expect(ab.shape).toEqual([0, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, ab.data()];
case 1:
_a.apply(void 0, [_c.sent(), []]);
c = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]);
res = tf.matMul(ab, c);
expect(res.shape).toEqual([0, 3]);
_b = test_util_1.expectArraysClose;
return [4 /*yield*/, res.data()];
case 2:
_b.apply(void 0, [_c.sent(), []]);
return [2 /*return*/];
}
});
}); });
it('throws error for string tensor', function () {
expect(function () { return tf.matMul([['a']], [['b']]); })
.toThrowError(/Argument 'a' passed to 'matMul' must be numeric tensor/);
});
});
jasmine_util_1.describeWithFlags('matmulBatch', jasmine_util_1.ALL_ENVS, function () {
it('A x B', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor3d([
-5, -5, -6, 8, -2, -8, 4, -7, -6, -9, -1, 3, 7, -2, 5,
-6, 3, 8, 7, -8, 1, 4, -4, 6, 4, -4, -9, -5, 2, -2
], [5, 2, 3]);
b = tf.tensor3d([
-8, -4, -1, 0, -7, 0, 3, 3, 6, 2, -1, 8, -4, 9, -6,
5, 8, 9, -9, 7, 0, -1, -1, -10, -7, 3, 4, 6, 3, -4
], [5, 3, 2]);
c = tf.matMul(a, b);
expect(c.shape).toEqual([5, 2, 2]);
_a = test_util_1.expectArraysClose;
return [4 /*yield*/, c.data()];
case 1:
_a.apply(void 0, [_b.sent(), [
87, 20, -6, -32, -24, -50, -36, -5, 24, 98,
70, 33, -64, 47, -42, -28, -71, 24, 37, 5
]]);
return [2 /*return*/];
}
});
}); });
it('A x B in 4D', function () { return __awaiter(_this, void 0, void 0, function () {
var a, b, transposeA, transposeB, c, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
a = tf.tensor4d([
-2, 3, 5, -5, 3, 9, -3, -5, 1, 1, -9, 9, -6, 6, -8,
-7, -1, 3, 9, -7, -7, 2, 10, -6, -8, -6, 9, -6, 4, -1,
9, -6, 10, 8, -9, 5, -8, -7, 0, 2, -5, -1, -9, -4, 3,
-2, 6, -4, 7, 1, -5, -4, 9, -8, -6, -8, 4, -1, 4, 3,
-7, 8, -7, 5, -3, -2, -4, 9, 2, -1, 1, -10, -3, 5, -4,
6, -8, -8, 9, -3, -5, 10, 3, -3, -3, 9, 3, -3, 2, -8,
10, 1, 9, -2, -2, -3, -4, 6, -10, -1, 8, -8, 7, 3, -2,
3, 6, -2, -2, -4, 1, -5, -4, 0, 5, 1, 9, -8, -2, -1
], [4, 5, 2, 3]);
b = tf.tensor4d([
-4, -3, -2, -6, 6, -1, -4, -1, 7, -4, 8, -9, -9, 0, -1,
-4, -6, -7, -3, -4, -7, 6, -8, 1, -2, 1, -1, -3, 8, -5,
9, -2, 5, 9, -2, 2, -5, -5, -8, -1, -2, -3, -2, -10, 6,
-3, 0, 1, 6, 7, 1, 2, -4, -5, 2, -5, -7, 9, 3, -6,
6, 4, -4, 6, 10, -3, -2, 8, 10, -8, 10, -1, -9, -7, -8,
-3, 1, 1, -2, -9, -7, -6, -1, 0, 7, -9, -7, -5, 0, -4,
-4, -7, 2, 4, 6, 6, -4, -6, -8, 3, -8, -9, 6, 9, -4,
1, -1, 0, 8, 9, 0, -5, 3, -1, 5, 0, -10, 7, -2, 6
], [4, 5, 3, 2]);
transposeA