"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.
|
* =============================================================================
|
*/
|
Object.defineProperty(exports, "__esModule", { value: true });
|
var environment_1 = require("./environment");
|
/**
|
* Shuffles the array in-place using Fisher-Yates algorithm.
|
*
|
* ```js
|
* const a = [1, 2, 3, 4, 5];
|
* tf.util.shuffle(a);
|
* console.log(a);
|
* ```
|
*
|
* @param array The array to shuffle in-place.
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
// tslint:disable-next-line:no-any
|
function shuffle(array) {
|
var counter = array.length;
|
var temp = 0;
|
var index = 0;
|
// While there are elements in the array
|
while (counter > 0) {
|
// Pick a random index
|
index = (Math.random() * counter) | 0;
|
// Decrease counter by 1
|
counter--;
|
// And swap the last element with it
|
temp = array[counter];
|
array[counter] = array[index];
|
array[index] = temp;
|
}
|
}
|
exports.shuffle = shuffle;
|
/** Clamps a value to a specified range. */
|
function clamp(min, x, max) {
|
return Math.max(min, Math.min(x, max));
|
}
|
exports.clamp = clamp;
|
function nearestLargerEven(val) {
|
return val % 2 === 0 ? val : val + 1;
|
}
|
exports.nearestLargerEven = nearestLargerEven;
|
function sum(arr) {
|
var sum = 0;
|
for (var i = 0; i < arr.length; i++) {
|
sum += arr[i];
|
}
|
return sum;
|
}
|
exports.sum = sum;
|
/**
|
* Returns a sample from a uniform [a, b) distribution.
|
*
|
* @param a The minimum support (inclusive).
|
* @param b The maximum support (exclusive).
|
* @return A pseudorandom number on the half-open interval [a,b).
|
*/
|
function randUniform(a, b) {
|
var r = Math.random();
|
return (b * r) + (1 - r) * a;
|
}
|
exports.randUniform = randUniform;
|
/** Returns the squared Euclidean distance between two vectors. */
|
function distSquared(a, b) {
|
var result = 0;
|
for (var i = 0; i < a.length; i++) {
|
var diff = Number(a[i]) - Number(b[i]);
|
result += diff * diff;
|
}
|
return result;
|
}
|
exports.distSquared = distSquared;
|
/**
|
* Asserts that the expression is true. Otherwise throws an error with the
|
* provided message.
|
*
|
* ```js
|
* const x = 2;
|
* tf.util.assert(x === 2, 'x is not 2');
|
* ```
|
*
|
* @param expr The expression to assert (as a boolean).
|
* @param msg A function that returns the message to report when throwing an
|
* error. We use a function for performance reasons.
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
function assert(expr, msg) {
|
if (!expr) {
|
throw new Error(typeof msg === 'string' ? msg : msg());
|
}
|
}
|
exports.assert = assert;
|
function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) {
|
if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; }
|
assert(arraysEqual(shapeA, shapeB), function () { return errorMessagePrefix + (" Shapes " + shapeA + " and " + shapeB + " must match"); });
|
}
|
exports.assertShapesMatch = assertShapesMatch;
|
function assertNonNull(a) {
|
assert(a != null, function () { return "The input to the tensor constructor must be a non-null value."; });
|
}
|
exports.assertNonNull = assertNonNull;
|
// NOTE: We explicitly type out what T extends instead of any so that
|
// util.flatten on a nested array of number doesn't try to infer T as a
|
// number[][], causing us to explicitly type util.flatten<number>().
|
/**
|
* Flattens an arbitrarily nested array.
|
*
|
* ```js
|
* const a = [[1, 2], [3, 4], [5, [6, [7]]]];
|
* const flat = tf.util.flatten(a);
|
* console.log(flat);
|
* ```
|
*
|
* @param arr The nested array to flatten.
|
* @param result The destination array which holds the elements.
|
* @param skipTypedArray If true, avoids flattening the typed arrays. Defaults
|
* to false.
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
function flatten(arr, result, skipTypedArray) {
|
if (result === void 0) { result = []; }
|
if (skipTypedArray === void 0) { skipTypedArray = false; }
|
if (result == null) {
|
result = [];
|
}
|
if (Array.isArray(arr) || isTypedArray(arr) && !skipTypedArray) {
|
for (var i = 0; i < arr.length; ++i) {
|
flatten(arr[i], result, skipTypedArray);
|
}
|
}
|
else {
|
result.push(arr);
|
}
|
return result;
|
}
|
exports.flatten = flatten;
|
/**
|
* Returns the size (number of elements) of the tensor given its shape.
|
*
|
* ```js
|
* const shape = [3, 4, 2];
|
* const size = tf.util.sizeFromShape(shape);
|
* console.log(size);
|
* ```
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
function sizeFromShape(shape) {
|
if (shape.length === 0) {
|
// Scalar.
|
return 1;
|
}
|
var size = shape[0];
|
for (var i = 1; i < shape.length; i++) {
|
size *= shape[i];
|
}
|
return size;
|
}
|
exports.sizeFromShape = sizeFromShape;
|
function isScalarShape(shape) {
|
return shape.length === 0;
|
}
|
exports.isScalarShape = isScalarShape;
|
function arraysEqual(n1, n2) {
|
if (n1 === n2) {
|
return true;
|
}
|
if (n1 == null || n2 == null) {
|
return false;
|
}
|
if (n1.length !== n2.length) {
|
return false;
|
}
|
for (var i = 0; i < n1.length; i++) {
|
if (n1[i] !== n2[i]) {
|
return false;
|
}
|
}
|
return true;
|
}
|
exports.arraysEqual = arraysEqual;
|
function isInt(a) {
|
return a % 1 === 0;
|
}
|
exports.isInt = isInt;
|
function tanh(x) {
|
// tslint:disable-next-line:no-any
|
if (Math.tanh != null) {
|
// tslint:disable-next-line:no-any
|
return Math.tanh(x);
|
}
|
if (x === Infinity) {
|
return 1;
|
}
|
else if (x === -Infinity) {
|
return -1;
|
}
|
else {
|
var e2x = Math.exp(2 * x);
|
return (e2x - 1) / (e2x + 1);
|
}
|
}
|
exports.tanh = tanh;
|
function sizeToSquarishShape(size) {
|
var width = Math.ceil(Math.sqrt(size));
|
return [width, Math.ceil(size / width)];
|
}
|
exports.sizeToSquarishShape = sizeToSquarishShape;
|
/**
|
* Creates a new array with randomized indicies to a given quantity.
|
*
|
* ```js
|
* const randomTen = tf.util.createShuffledIndices(10);
|
* console.log(randomTen);
|
* ```
|
*
|
* @param number Quantity of how many shuffled indicies to create.
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
function createShuffledIndices(n) {
|
var shuffledIndices = new Uint32Array(n);
|
for (var i = 0; i < n; ++i) {
|
shuffledIndices[i] = i;
|
}
|
shuffle(shuffledIndices);
|
return shuffledIndices;
|
}
|
exports.createShuffledIndices = createShuffledIndices;
|
function rightPad(a, size) {
|
if (size <= a.length) {
|
return a;
|
}
|
return a + ' '.repeat(size - a.length);
|
}
|
exports.rightPad = rightPad;
|
function repeatedTry(checkFn, delayFn, maxCounter) {
|
if (delayFn === void 0) { delayFn = function (counter) { return 0; }; }
|
return new Promise(function (resolve, reject) {
|
var tryCount = 0;
|
var tryFn = function () {
|
if (checkFn()) {
|
resolve();
|
return;
|
}
|
tryCount++;
|
var nextBackoff = delayFn(tryCount);
|
if (maxCounter != null && tryCount >= maxCounter) {
|
reject();
|
return;
|
}
|
setTimeout(tryFn, nextBackoff);
|
};
|
tryFn();
|
});
|
}
|
exports.repeatedTry = repeatedTry;
|
/**
|
* Given the full size of the array and a shape that may contain -1 as the
|
* implicit dimension, returns the inferred shape where -1 is replaced.
|
* E.g. For shape=[2, -1, 3] and size=24, it will return [2, 4, 3].
|
*
|
* @param shape The shape, which may contain -1 in some dimension.
|
* @param size The full size (number of elements) of the array.
|
* @return The inferred shape where -1 is replaced with the inferred size.
|
*/
|
function inferFromImplicitShape(shape, size) {
|
var shapeProd = 1;
|
var implicitIdx = -1;
|
for (var i = 0; i < shape.length; ++i) {
|
if (shape[i] >= 0) {
|
shapeProd *= shape[i];
|
}
|
else if (shape[i] === -1) {
|
if (implicitIdx !== -1) {
|
throw Error("Shapes can only have 1 implicit size. " +
|
("Found -1 at dim " + implicitIdx + " and dim " + i));
|
}
|
implicitIdx = i;
|
}
|
else if (shape[i] < 0) {
|
throw Error("Shapes can not be < 0. Found " + shape[i] + " at dim " + i);
|
}
|
}
|
if (implicitIdx === -1) {
|
if (size > 0 && size !== shapeProd) {
|
throw Error("Size(" + size + ") must match the product of shape " + shape);
|
}
|
return shape;
|
}
|
if (shapeProd === 0) {
|
throw Error("Cannot infer the missing size in [" + shape + "] when " +
|
"there are 0 elements");
|
}
|
if (size % shapeProd !== 0) {
|
throw Error("The implicit shape can't be a fractional number. " +
|
("Got " + size + " / " + shapeProd));
|
}
|
var newShape = shape.slice();
|
newShape[implicitIdx] = size / shapeProd;
|
return newShape;
|
}
|
exports.inferFromImplicitShape = inferFromImplicitShape;
|
function parseAxisParam(axis, shape) {
|
var rank = shape.length;
|
// Normalize input
|
axis = axis == null ? shape.map(function (s, i) { return i; }) : [].concat(axis);
|
// Check for valid range
|
assert(axis.every(function (ax) { return ax >= -rank && ax < rank; }), function () {
|
return "All values in axis param must be in range [-" + rank + ", " + rank + ") but " +
|
("got axis " + axis);
|
});
|
// Check for only integers
|
assert(axis.every(function (ax) { return isInt(ax); }), function () { return "All values in axis param must be integers but " +
|
("got axis " + axis); });
|
// Handle negative axis.
|
return axis.map(function (a) { return a < 0 ? rank + a : a; });
|
}
|
exports.parseAxisParam = parseAxisParam;
|
/** Reduces the shape by removing all dimensions of shape 1. */
|
function squeezeShape(shape, axis) {
|
var newShape = [];
|
var keptDims = [];
|
var isEmptyArray = axis != null && Array.isArray(axis) && axis.length === 0;
|
var axes = (axis == null || isEmptyArray) ?
|
null :
|
parseAxisParam(axis, shape).sort();
|
var j = 0;
|
for (var i = 0; i < shape.length; ++i) {
|
if (axes != null) {
|
if (axes[j] === i && shape[i] !== 1) {
|
throw new Error("Can't squeeze axis " + i + " since its dim '" + shape[i] + "' is not 1");
|
}
|
if ((axes[j] == null || axes[j] > i) && shape[i] === 1) {
|
newShape.push(shape[i]);
|
keptDims.push(i);
|
}
|
if (axes[j] <= i) {
|
j++;
|
}
|
}
|
if (shape[i] !== 1) {
|
newShape.push(shape[i]);
|
keptDims.push(i);
|
}
|
}
|
return { newShape: newShape, keptDims: keptDims };
|
}
|
exports.squeezeShape = squeezeShape;
|
function getTypedArrayFromDType(dtype, size) {
|
var values = null;
|
if (dtype == null || dtype === 'float32') {
|
values = new Float32Array(size);
|
}
|
else if (dtype === 'int32') {
|
values = new Int32Array(size);
|
}
|
else if (dtype === 'bool') {
|
values = new Uint8Array(size);
|
}
|
else {
|
throw new Error("Unknown data type " + dtype);
|
}
|
return values;
|
}
|
exports.getTypedArrayFromDType = getTypedArrayFromDType;
|
function getArrayFromDType(dtype, size) {
|
var values = null;
|
if (dtype == null || dtype === 'float32') {
|
values = new Float32Array(size);
|
}
|
else if (dtype === 'int32') {
|
values = new Int32Array(size);
|
}
|
else if (dtype === 'bool') {
|
values = new Uint8Array(size);
|
}
|
else if (dtype === 'string') {
|
values = new Array(size);
|
}
|
else {
|
throw new Error("Unknown data type " + dtype);
|
}
|
return values;
|
}
|
exports.getArrayFromDType = getArrayFromDType;
|
function checkConversionForErrors(vals, dtype) {
|
for (var i = 0; i < vals.length; i++) {
|
var num = vals[i];
|
if (isNaN(num) || !isFinite(num)) {
|
throw Error("A tensor of type " + dtype + " being uploaded contains " + num + ".");
|
}
|
}
|
}
|
exports.checkConversionForErrors = checkConversionForErrors;
|
/** Returns true if the dtype is valid. */
|
function isValidDtype(dtype) {
|
return dtype === 'bool' || dtype === 'complex64' || dtype === 'float32' ||
|
dtype === 'int32' || dtype === 'string';
|
}
|
exports.isValidDtype = isValidDtype;
|
/**
|
* Returns true if the new type can't encode the old type without loss of
|
* precision.
|
*/
|
function hasEncodingLoss(oldType, newType) {
|
if (newType === 'complex64') {
|
return false;
|
}
|
if (newType === 'float32' && oldType !== 'complex64') {
|
return false;
|
}
|
if (newType === 'int32' && oldType !== 'float32' && oldType !== 'complex64') {
|
return false;
|
}
|
if (newType === 'bool' && oldType === 'bool') {
|
return false;
|
}
|
return true;
|
}
|
exports.hasEncodingLoss = hasEncodingLoss;
|
function isTypedArray(a) {
|
return a instanceof Float32Array || a instanceof Int32Array ||
|
a instanceof Uint8Array;
|
}
|
exports.isTypedArray = isTypedArray;
|
function bytesPerElement(dtype) {
|
if (dtype === 'float32' || dtype === 'int32') {
|
return 4;
|
}
|
else if (dtype === 'complex64') {
|
return 8;
|
}
|
else if (dtype === 'bool') {
|
return 1;
|
}
|
else {
|
throw new Error("Unknown dtype " + dtype);
|
}
|
}
|
exports.bytesPerElement = bytesPerElement;
|
/**
|
* Returns the approximate number of bytes allocated in the string array - 2
|
* bytes per character. Computing the exact bytes for a native string in JS is
|
* not possible since it depends on the encoding of the html page that serves
|
* the website.
|
*/
|
function bytesFromStringArray(arr) {
|
if (arr == null) {
|
return 0;
|
}
|
var bytes = 0;
|
arr.forEach(function (x) { return bytes += x.length; });
|
return bytes;
|
}
|
exports.bytesFromStringArray = bytesFromStringArray;
|
/** Returns true if the value is a string. */
|
function isString(value) {
|
return typeof value === 'string' || value instanceof String;
|
}
|
exports.isString = isString;
|
function isBoolean(value) {
|
return typeof value === 'boolean';
|
}
|
exports.isBoolean = isBoolean;
|
function isNumber(value) {
|
return typeof value === 'number';
|
}
|
exports.isNumber = isNumber;
|
function inferDtype(values) {
|
if (Array.isArray(values)) {
|
return inferDtype(values[0]);
|
}
|
if (values instanceof Float32Array) {
|
return 'float32';
|
}
|
else if (values instanceof Int32Array || values instanceof Uint8Array) {
|
return 'int32';
|
}
|
else if (isNumber(values)) {
|
return 'float32';
|
}
|
else if (isString(values)) {
|
return 'string';
|
}
|
else if (isBoolean(values)) {
|
return 'bool';
|
}
|
return 'float32';
|
}
|
exports.inferDtype = inferDtype;
|
function isFunction(f) {
|
return !!(f && f.constructor && f.call && f.apply);
|
}
|
exports.isFunction = isFunction;
|
function nearestDivisor(size, start) {
|
for (var i = start; i < size; ++i) {
|
if (size % i === 0) {
|
return i;
|
}
|
}
|
return size;
|
}
|
exports.nearestDivisor = nearestDivisor;
|
function computeStrides(shape) {
|
var rank = shape.length;
|
if (rank < 2) {
|
return [];
|
}
|
// Last dimension has implicit stride of 1, thus having D-1 (instead of D)
|
// strides.
|
var strides = new Array(rank - 1);
|
strides[rank - 2] = shape[rank - 1];
|
for (var i = rank - 3; i >= 0; --i) {
|
strides[i] = strides[i + 1] * shape[i + 1];
|
}
|
return strides;
|
}
|
exports.computeStrides = computeStrides;
|
function toTypedArray(a, dtype, debugMode) {
|
if (dtype === 'string') {
|
throw new Error('Cannot convert a string[] to a TypedArray');
|
}
|
if (Array.isArray(a)) {
|
a = flatten(a);
|
}
|
if (debugMode) {
|
checkConversionForErrors(a, dtype);
|
}
|
if (noConversionNeeded(a, dtype)) {
|
return a;
|
}
|
if (dtype == null || dtype === 'float32' || dtype === 'complex64') {
|
return new Float32Array(a);
|
}
|
else if (dtype === 'int32') {
|
return new Int32Array(a);
|
}
|
else if (dtype === 'bool') {
|
var bool = new Uint8Array(a.length);
|
for (var i = 0; i < bool.length; ++i) {
|
if (Math.round(a[i]) !== 0) {
|
bool[i] = 1;
|
}
|
}
|
return bool;
|
}
|
else {
|
throw new Error("Unknown data type " + dtype);
|
}
|
}
|
exports.toTypedArray = toTypedArray;
|
function createNestedArray(offset, shape, a) {
|
var ret = new Array();
|
if (shape.length === 1) {
|
var d = shape[0];
|
for (var i = 0; i < d; i++) {
|
ret[i] = a[offset + i];
|
}
|
}
|
else {
|
var d = shape[0];
|
var rest = shape.slice(1);
|
var len = rest.reduce(function (acc, c) { return acc * c; });
|
for (var i = 0; i < d; i++) {
|
ret[i] = createNestedArray(offset + i * len, rest, a);
|
}
|
}
|
return ret;
|
}
|
// Provide a nested array of TypedArray in given shape.
|
function toNestedArray(shape, a) {
|
if (shape.length === 0) {
|
// Scalar type should return a single number.
|
return a[0];
|
}
|
var size = shape.reduce(function (acc, c) { return acc * c; });
|
if (size === 0) {
|
// A tensor with shape zero should be turned into empty list.
|
return [];
|
}
|
if (size !== a.length) {
|
throw new Error("[" + shape + "] does not match the input size.");
|
}
|
return createNestedArray(0, shape, a);
|
}
|
exports.toNestedArray = toNestedArray;
|
function noConversionNeeded(a, dtype) {
|
return (a instanceof Float32Array && dtype === 'float32') ||
|
(a instanceof Int32Array && dtype === 'int32') ||
|
(a instanceof Uint8Array && dtype === 'bool');
|
}
|
function makeOnesTypedArray(size, dtype) {
|
var array = makeZerosTypedArray(size, dtype);
|
for (var i = 0; i < array.length; i++) {
|
array[i] = 1;
|
}
|
return array;
|
}
|
exports.makeOnesTypedArray = makeOnesTypedArray;
|
function makeZerosTypedArray(size, dtype) {
|
if (dtype == null || dtype === 'float32' || dtype === 'complex64') {
|
return new Float32Array(size);
|
}
|
else if (dtype === 'int32') {
|
return new Int32Array(size);
|
}
|
else if (dtype === 'bool') {
|
return new Uint8Array(size);
|
}
|
else {
|
throw new Error("Unknown data type " + dtype);
|
}
|
}
|
exports.makeZerosTypedArray = makeZerosTypedArray;
|
/**
|
* Returns the current high-resolution time in milliseconds relative to an
|
* arbitrary time in the past. It works across different platforms (node.js,
|
* browsers).
|
*
|
* ```js
|
* console.log(tf.util.now());
|
* ```
|
*/
|
/** @doc {heading: 'Util', namespace: 'util'} */
|
function now() {
|
return environment_1.env().platform.now();
|
}
|
exports.now = now;
|
function assertNonNegativeIntegerDimensions(shape) {
|
shape.forEach(function (dimSize) {
|
assert(Number.isInteger(dimSize) && dimSize >= 0, function () {
|
return "Tensor must have a shape comprised of positive integers but got " +
|
("shape [" + shape + "].");
|
});
|
});
|
}
|
exports.assertNonNegativeIntegerDimensions = assertNonNegativeIntegerDimensions;
|
/**
|
* Returns a platform-specific implementation of
|
* [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
*
|
* If `fetch` is defined on the global object (`window`, `process`, etc.),
|
* `tf.util.fetch` returns that function.
|
*
|
* If not, `tf.util.fetch` returns a platform-specific solution.
|
*
|
* ```js
|
* const resource = await tf.util.fetch('https://unpkg.com/@tensorflow/tfjs');
|
* // handle response
|
* ```
|
*/
|
/** @doc {heading: 'Util'} */
|
function fetch(path, requestInits) {
|
return environment_1.env().platform.fetch(path, requestInits);
|
}
|
exports.fetch = fetch;
|
/**
|
* Encodes the provided string into bytes using the provided encoding scheme.
|
*
|
* @param s The string to encode.
|
* @param encoding The encoding scheme. Defaults to utf-8.
|
*
|
*/
|
/** @doc {heading: 'Util'} */
|
function encodeString(s, encoding) {
|
if (encoding === void 0) { encoding = 'utf-8'; }
|
encoding = encoding || 'utf-8';
|
return environment_1.env().platform.encode(s, encoding);
|
}
|
exports.encodeString = encodeString;
|
/**
|
* Decodes the provided bytes into a string using the provided encoding scheme.
|
* @param bytes The bytes to decode.
|
*
|
* @param encoding The encoding scheme. Defaults to utf-8.
|
*/
|
/** @doc {heading: 'Util'} */
|
function decodeString(bytes, encoding) {
|
if (encoding === void 0) { encoding = 'utf-8'; }
|
encoding = encoding || 'utf-8';
|
return environment_1.env().platform.decode(bytes, encoding);
|
}
|
exports.decodeString = decodeString;
|
/**
|
* Computes flat index for a given location (multidimentionsal index) in a
|
* Tensor/multidimensional array.
|
*
|
* @param locs Location in the tensor.
|
* @param rank Rank of the tensor.
|
* @param strides Tensor strides.
|
*/
|
function locToIndex(locs, rank, strides) {
|
if (rank === 0) {
|
return 0;
|
}
|
else if (rank === 1) {
|
return locs[0];
|
}
|
var index = locs[locs.length - 1];
|
for (var i = 0; i < locs.length - 1; ++i) {
|
index += strides[i] * locs[i];
|
}
|
return index;
|
}
|
exports.locToIndex = locToIndex;
|
/**
|
* Computes the location (multidimensional index) in a tensor/multidimentional
|
* array for a given flat index.
|
*
|
* @param index Index in flat array.
|
* @param rank Rank of tensor.
|
* @param strides Strides of tensor.
|
*/
|
function indexToLoc(index, rank, strides) {
|
if (rank === 0) {
|
return [];
|
}
|
else if (rank === 1) {
|
return [index];
|
}
|
var locs = new Array(rank);
|
for (var i = 0; i < locs.length - 1; ++i) {
|
locs[i] = Math.floor(index / strides[i]);
|
index -= locs[i] * strides[i];
|
}
|
locs[locs.length - 1] = index;
|
return locs;
|
}
|
exports.indexToLoc = indexToLoc;
|
//# sourceMappingURL=util.js.map
|