license import { DataType , DataTypeMap , FlatVector , NumericDataType , RecursiveArray , TensorLike , TypedArray } from './types' ; param array doc export function shuffle ( array ) { let counter = array . length ; let index = 0 ; while ( counter > 0 ) { index = ( Math . random ( ) * counter ) | 0 ; counter -- ; swap ( array , counter , index ) ; } } param array param array2 doc export function shuffleCombo ( array , array2 ) { if ( array . length !== array2 . length ) { throw new Error ( `Array sizes must match to be shuffled together ` + `First array length was ${ array . length }` + `Second array length was ${ array2 . length }` ) ; } let counter = array . length ; let index = 0 ; while ( counter > 0 ) { index = ( Math . random ( ) * counter ) | 0 ; counter -- ; swap ( array , counter , index ) ; swap ( array2 , counter , index ) ; } } /** Clamps a value to a specified range. */ export function clamp ( min , x , max ) { return Math . max ( min , Math . min ( x , max ) ) ; } export function nearestLargerEven ( val ) { return val % 2 === 0 ? val : val + 1 ; } export function swap < T > ( object , left , right ) { const temp = object [ left ] ; object [ left ] = object [ right ] ; object [ right ] = temp ; } export function sum ( arr ) { let sum = 0 ; for ( let i = 0 ; i < arr . length ; i ++ ) { sum += arr [ i ] ; } return sum ; } param a param b return export function randUniform ( a , b ) { const r = Math . random ( ) ; return ( b * r ) + ( 1 - r ) * a ; } /** Returns the squared Euclidean distance between two vectors. */ export function distSquared ( a , b ) { let result = 0 ; for ( let i = 0 ; i < a . length ; i ++ ) { const diff = Number ( a [ i ] ) - Number ( b [ i ] ) ; result += diff * diff ; } return result ; } param expr param msg doc export function assert ( expr , msg ) { if ( ! expr ) { throw new Error ( typeof msg === 'string' ? msg : msg ( ) ) ; } } export function assertShapesMatch ( shapeA , shapeB , errorMessagePrefix = '' ) { assert ( arraysEqual ( shapeA , shapeB ) , ( ) => errorMessagePrefix + ` Shapes ${ shapeA } and ${ shapeB } must match` ) ; } export function assertNonNull ( a ) { assert ( a != null , ( ) => `The input to the tensor constructor must be a non-null value.` ) ; } param arr param result param skipTypedArray doc export function flatten < T extends number | boolean | string | Promise < number > | TypedArray > ( arr , result = [ ] , skipTypedArray = false ) { if ( result == null ) { result = [ ] ; } if ( Array . isArray ( arr ) || isTypedArray ( arr ) && ! skipTypedArray ) { for ( let i = 0 ; i < arr . length ; ++ i ) { flatten ( arr [ i ] , result , skipTypedArray ) ; } } else { result . push ( arr as T ) ; } return result ; } doc export function sizeFromShape ( shape ) { if ( shape . length === 0 ) { return 1 ; } let size = shape [ 0 ] ; for ( let i = 1 ; i < shape . length ; i ++ ) { size *= shape [ i ] ; } return size ; } export function isScalarShape ( shape ) { return shape . length === 0 ; } export function arraysEqual ( n1 , n2 ) { if ( n1 === n2 ) { return true ; } if ( n1 == null || n2 == null ) { return false ; } if ( n1 . length !== n2 . length ) { return false ; } for ( let i = 0 ; i < n1 . length ; i ++ ) { if ( n1 [ i ] !== n2 [ i ] ) { return false ; } } return true ; } export function isInt ( a ) { return a % 1 === 0 ; } export function tanh ( x ) { if ( ( Math as any ) . tanh != null ) { return ( Math as any ) . tanh ( x ) ; } if ( x === Infinity ) { return 1 ; } else if ( x === - Infinity ) { return - 1 ; } else { const e2x = Math . exp ( 2 * x ) ; return ( e2x - 1 ) / ( e2x + 1 ) ; } } export function sizeToSquarishShape ( size ) { const width = Math . ceil ( Math . sqrt ( size ) ) ; return [ width , Math . ceil ( size / width ) ] ; } param number doc export function createShuffledIndices ( n ) { const shuffledIndices = new Uint32Array ( n ) ; for ( let i = 0 ; i < n ; ++ i ) { shuffledIndices [ i ] = i ; } shuffle ( shuffledIndices ) ; return shuffledIndices ; } export function rightPad ( a , size ) { if ( size <= a . length ) { return a ; } return a + ' ' . repeat ( size - a . length ) ; } export function repeatedTry ( checkFn , delayFn = ( counter ) => 0 , maxCounter ? ) { return new Promise < void > ( ( resolve , reject ) => { let tryCount = 0 ; const tryFn = ( ) => { if ( checkFn ( ) ) { resolve ( ) ; return ; } tryCount ++ ; const nextBackoff = delayFn ( tryCount ) ; if ( maxCounter != null && tryCount >= maxCounter ) { reject ( ) ; return ; } setTimeout ( tryFn , nextBackoff ) ; } ; tryFn ( ) ; } ) ; } param shape param size return export function inferFromImplicitShape ( shape , size ) { let shapeProd = 1 ; let implicitIdx = - 1 ; for ( let 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 }` ) ; } const newShape = shape . slice ( ) ; newShape [ implicitIdx ] = size / shapeProd ; return newShape ; } export function parseAxisParam ( axis , shape ) { const rank = shape . length ; axis = axis == null ? shape . map ( ( s , i ) => i ) : [ ] . concat ( axis ) ; assert ( axis . every ( ax => ax >= - rank && ax < rank ) , ( ) => `All values in axis param must be in range [-${ rank }, ${ rank }) but ` + `got axis ${ axis }` ) ; assert ( axis . every ( ax => isInt ( ax ) ) , ( ) => `All values in axis param must be integers but ` + `got axis ${ axis }` ) ; return axis . map ( a => a < 0 ? rank + a : a ) ; } /** Reduces the shape by removing all dimensions of shape 1. */ export function squeezeShape ( shape , axis ? ) { const newShape = [ ] ; const keptDims = [ ] ; const isEmptyArray = axis != null && Array . isArray ( axis ) && axis . length === 0 ; const axes = ( axis == null || isEmptyArray ) ? null : parseAxisParam ( axis , shape ) . sort ( ) ; let j = 0 ; for ( let 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 , keptDims } ; } export function getTypedArrayFromDType < D extends NumericDataType > ( dtype , size ) { let 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 as DataTypeMap [ D ] ; } export function getArrayFromDType < D extends DataType > ( dtype , size ) { let 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 < 'string' > ( size ) ; } else { throw new Error ( `Unknown data type ${ dtype }` ) ; } return values as DataTypeMap [ D ] ; } export function checkConversionForErrors < D extends DataType > ( vals , dtype ) { for ( let i = 0 ; i < vals . length ; i ++ ) { const num = vals [ i ] as number ; if ( isNaN ( num ) || ! isFinite ( num ) ) { throw Error ( `A tensor of type ${ dtype } being uploaded contains ${ num }.` ) ; } } } /** Returns true if the dtype is valid. */ export function isValidDtype ( dtype ) { return dtype === 'bool' || dtype === 'complex64' || dtype === 'float32' || dtype === 'int32' || dtype === 'string' ; } /**
 * Returns true if the new type can't encode the old type without loss of
 * precision.
 */ export 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 ; } export function isTypedArray ( a ) { return a instanceof Float32Array || a instanceof Int32Array || a instanceof Uint8Array || a instanceof Uint8ClampedArray ; } export 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 }` ) ; } } /**
 * 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.
 */ export function bytesFromStringArray ( arr ) { if ( arr == null ) { return 0 ; } let bytes = 0 ; arr . forEach ( x => bytes += x . length ) ; return bytes ; } /** Returns true if the value is a string. */ export function isString ( value ) { return typeof value === 'string' || value instanceof String ; } export function isBoolean ( value ) { return typeof value === 'boolean' ; } export function isNumber ( value ) { return typeof value === 'number' ; } export 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 || values instanceof Uint8ClampedArray ) { return 'int32' ; } else if ( isNumber ( values ) ) { return 'float32' ; } else if ( isString ( values ) ) { return 'string' ; } else if ( isBoolean ( values ) ) { return 'bool' ; } return 'float32' ; } export function isFunction ( f ) { return ! ! ( f && f . constructor && f . call && f . apply ) ; } export function nearestDivisor ( size , start ) { for ( let i = start ; i < size ; ++ i ) { if ( size % i === 0 ) { return i ; } } return size ; } export function computeStrides ( shape ) { const rank = shape . length ; if ( rank < 2 ) { return [ ] ; } const strides = new Array ( rank - 1 ) ; strides [ rank - 2 ] = shape [ rank - 1 ] ; for ( let i = rank - 3 ; i >= 0 ; -- i ) { strides [ i ] = strides [ i + 1 ] * shape [ i + 1 ] ; } return strides ; } function createNestedArray ( offset , shape , a , isComplex = false ) { const ret = new Array ( ) ; if ( shape . length === 1 ) { const d = shape [ 0 ] * ( isComplex ? 2 : 1 ) ; for ( let i = 0 ; i < d ; i ++ ) { ret [ i ] = a [ offset + i ] ; } } else { const d = shape [ 0 ] ; const rest = shape . slice ( 1 ) ; const len = rest . reduce ( ( acc , c ) => acc * c ) * ( isComplex ? 2 : 1 ) ; for ( let i = 0 ; i < d ; i ++ ) { ret [ i ] = createNestedArray ( offset + i * len , rest , a , isComplex ) ; } } return ret ; } export function toNestedArray ( shape , a , isComplex = false ) { if ( shape . length === 0 ) { return a [ 0 ] ; } const size = shape . reduce ( ( acc , c ) => acc * c ) * ( isComplex ? 2 : 1 ) ; if ( size === 0 ) { return [ ] ; } if ( size !== a . length ) { throw new Error ( `[${ shape }] does not match the input size ${ a . length }${ isComplex ? ' for a complex tensor' : '' }.` ) ; } return createNestedArray ( 0 , shape , a , isComplex ) ; } export function makeOnesTypedArray < D extends DataType > ( size , dtype ) { const array = makeZerosTypedArray ( size , dtype ) ; for ( let i = 0 ; i < array . length ; i ++ ) { array [ i ] = 1 ; } return array ; } export function makeZerosTypedArray < D extends DataType > ( size , dtype ) { if ( dtype == null || dtype === 'float32' || dtype === 'complex64' ) { return new Float32Array ( size ) as DataTypeMap [ D ] ; } else if ( dtype === 'int32' ) { return new Int32Array ( size ) as DataTypeMap [ D ] ; } else if ( dtype === 'bool' ) { return new Uint8Array ( size ) as DataTypeMap [ D ] ; } else { throw new Error ( `Unknown data type ${ dtype }` ) ; } } param shape param dtype export function makeZerosNestedTypedArray < D extends DataType > ( shape , dtype ) { const size = shape . reduce ( ( prev , curr ) => prev * curr , 1 ) ; if ( dtype == null || dtype === 'float32' ) { return toNestedArray ( shape , new Float32Array ( size ) ) ; } else if ( dtype === 'int32' ) { return toNestedArray ( shape , new Int32Array ( size ) ) ; } else if ( dtype === 'bool' ) { return toNestedArray ( shape , new Uint8Array ( size ) ) ; } else { throw new Error ( `Unknown data type ${ dtype }` ) ; } } export function assertNonNegativeIntegerDimensions ( shape ) { shape . forEach ( dimSize => { assert ( Number . isInteger ( dimSize ) && dimSize >= 0 , ( ) => `Tensor must have a shape comprised of positive integers but got ` + `shape [${ shape }].` ) ; } ) ; } param locs param rank param strides export function locToIndex ( locs , rank , strides ) { if ( rank === 0 ) { return 0 ; } else if ( rank === 1 ) { return locs [ 0 ] ; } let index = locs [ locs . length - 1 ] ; for ( let i = 0 ; i < locs . length - 1 ; ++ i ) { index += strides [ i ] * locs [ i ] ; } return index ; } param index param rank param strides export function indexToLoc ( index , rank , strides ) { if ( rank === 0 ) { return [ ] ; } else if ( rank === 1 ) { return [ index ] ; } const locs = new Array ( rank ) ; for ( let 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 ; } param object export function isPromise ( object ) { return object && object . then && typeof object . then === 'function' ; }