import ndarray from 'ndarray' ; import { NumberDataType , Type } from './yoloPostprocess' ; export class BroadcastUtil { param a param b param isMatMul returns static calcShape ( adims , bdims , isMatMul = false ) { const arank = adims . length ; const brank = bdims . length ; const crank = Math . max ( adims . length , bdims . length ) ; const cdims = new Array < number > ( crank ) ; if ( isMatMul ) { if ( arank < 2 || brank < 2 ) { return undefined ; } const cShapeMatMul = BroadcastUtil . calcMatMulShape ( [ adims [ arank - 2 ] , adims [ arank - 1 ] ] , [ bdims [ brank - 2 ] , bdims [ brank - 1 ] ] ) ; if ( cShapeMatMul === undefined ) { return undefined ; } [ cdims [ crank - 2 ] , cdims [ crank - 1 ] ] = cShapeMatMul ; } for ( let i = isMatMul ? 3 : 1 ; i <= crank ; i ++ ) { const aLen = arank - i < 0 ? 1 : adims [ arank - i ] ; const bLen = brank - i < 0 ? 1 : bdims [ brank - i ] ; if ( aLen !== bLen && aLen > 1 && bLen > 1 ) { return undefined ; } cdims [ crank - i ] = Math . max ( aLen , bLen ) ; } return cdims ; } param a param b returns static calcMatMulShape ( a , b ) { return ( a [ 1 ] !== b [ 0 ] ) ? undefined : [ a [ 0 ] , b [ 1 ] ] ; } param indices param shapeOrigin param isMatMul returns static index ( indices , shapeOrigin , isMatMul = false ) { const dimOffset = indices . length - shapeOrigin . length ; const indicesOrigin = indices . slice ( dimOffset ) ; const dimLen = isMatMul ? indicesOrigin . length - 2 : indicesOrigin . length ; for ( let i = 0 ; i < dimLen ; i ++ ) { indicesOrigin [ i ] = indices [ dimOffset + i ] % shapeOrigin [ i ] ; } return indicesOrigin ; } param a param b param op returns static calc ( a , b , op ) { const shape = BroadcastUtil . calcShape ( a . shape , b . shape ) ; if ( shape ) { const size = ShapeUtil . size ( shape ) ; const c = ndarray ( new ( a . data . constructor as Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor | Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor | Uint8ClampedArrayConstructor ) ( size ) , shape ) ; const indices = new Array < number > ( shape . length ) ; for ( let i = 0 ; i < size ; i ++ ) { let rest = i ; for ( let j = shape . length - 1 ; j >= 0 ; j -- ) { indices [ j ] = rest % shape [ j ] ; rest = Math . floor ( rest / shape [ j ] ) ; } const indicesA = BroadcastUtil . index ( indices , a . shape ) ; const indicesB = BroadcastUtil . index ( indices , b . shape ) ; c . set ( ... indices . concat ( op ( a . get ( ... indicesA ) , b . get ( ... indicesB ) ) ) ) ; } return c ; } return undefined ; } param shape param finalShape static isValidBroadcast ( shape , finalShape ) { const inputRank = shape . length ; const finalRank = finalShape . length ; if ( inputRank > finalRank ) { return false ; } for ( let i = 1 ; i <= inputRank ; i ++ ) { if ( shape [ inputRank - i ] !== 1 && shape [ inputRank - i ] !== finalShape [ finalRank - i ] ) { return false ; } } return true ; } } export function arrayCopyHelper ( target , source , targetIndex , sourceIndex , blockSize ) { if ( sourceIndex < 0 || sourceIndex >= source . length ) { throw new Error ( `sourceIndex out of bounds` ) ; } if ( targetIndex < 0 || targetIndex >= target . length ) { throw new Error ( `targetIndex out of bounds` ) ; } if ( sourceIndex + blockSize > source . length ) { throw new Error ( `source indices to be copied are outside bounds` ) ; } if ( targetIndex + blockSize > target . length ) { throw new Error ( `target array is too small to hold result` ) ; } for ( let offset = 0 ; offset < blockSize ; offset ++ ) { target [ targetIndex + offset ] = source [ sourceIndex + offset ] ; } } export class TypeUtil { static validateSameTypes ( typesArray ) { if ( typesArray . length < 2 ) { throw new Error ( 'must contain atleast 2 types to compare equality' ) ; } const baseType = typesArray [ 0 ] ; for ( let i = 0 ; i < typesArray . length ; ++ i ) { if ( typesArray [ i ] !== baseType ) { throw new Error ( 'input types are ' ) ; } } } } export class ShapeUtil { static validateEqualDims ( dimsArray ) { if ( dimsArray . length < 2 ) { throw new Error ( 'must contain atleast 2 shapes to compare equality' ) ; } const baseDims = dimsArray [ 0 ] ; const baseRank = baseDims . length ; for ( let i = 1 ; i < dimsArray . length ; ++ i ) { const dims = dimsArray [ i ] ; if ( dims . length !== baseRank ) { throw new Error ( 'rank is not the same for given inpu shapes' ) ; } for ( let j = 0 ; j < baseRank ; ++ j ) { if ( baseDims [ j ] !== dims [ j ] ) { throw new Error ( 'input shapes are not the same' ) ; } } } } static validateDims ( dims ) { if ( dims . length < 0 || dims . length > 6 ) { throw new TypeError ( `Only rank 0 to 6 is supported for tensor shape.` ) ; } if ( dims . length === 0 ) { throw new RangeError ( 'Scaler tensor is not implemented yet' ) ; } for ( const n of dims ) { if ( ! Number . isInteger ( n ) ) { throw new TypeError ( `Invalid shape: ${ n } is not an integer` ) ; } if ( n <= 0 || n > 2147483647 ) { throw new TypeError ( `Invalid shape: length ${ n } is not allowed` ) ; } } } static size ( dims ) { return ShapeUtil . getSizeFromDimensionRange ( dims , 0 , dims . length ) ; } static sizeFromDimension ( dims , axis ) { if ( axis > dims . length ) { throw new Error ( `invalid dimension of ${ axis } for sizeFromDimension as Tensor has ${ dims . length } dimensions.` ) ; } return ShapeUtil . getSizeFromDimensionRange ( dims , axis , dims . length ) ; } static sizeToDimension ( dims , axis ) { if ( axis > dims . length ) { throw new Error ( `invalid dimension of ${ axis } for sizeToDimension as Tensor has ${ dims . length } dimensions.` ) ; } return ShapeUtil . getSizeFromDimensionRange ( dims , 0 , axis ) ; } static getSizeFromDimensionRange ( dims , start , end ) { let size = 1 ; for ( let i = start ; i < end ; i ++ ) { if ( dims [ i ] <= 0 ) { throw new Error ( `cannot get valid size from specified dimension range. Most likely the range contains 0 or negative values in them.` ) ; } size *= dims [ i ] ; } return size ; } param index param stride param axis static computeOffset ( index , stride , axis ? ) { if ( axis === undefined ) { axis = index . length ; } let offset = 0 ; for ( let i = 0 ; i < axis ; ++ i ) { offset += ( index [ i ] * stride [ i ] ) ; } return offset ; } static computeStrides ( shape ) { const rank = shape . length ; if ( rank < 2 ) { return [ 1 ] ; } const strides = new Array ( rank ) ; strides [ rank - 1 ] = 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 ; } static transpose ( dims ) { return dims . reverse ( ) ; } static indicesToOffset ( indices , strides ) { const rank = strides . length ; if ( rank === 0 ) { return 0 ; } let index = indices [ indices . length - 1 ] ; for ( let i = 0 ; i < indices . length - 1 ; ++ i ) { index += strides [ i ] * indices [ i ] ; } return index ; } static offsetToIndices ( offset , strides ) { const rank = strides . length ; if ( rank === 0 ) { return [ ] ; } else if ( rank === 1 ) { return [ offset ] ; } const indices = new Array ( strides . length ) ; for ( let i = 0 ; i < indices . length - 1 ; ++ i ) { indices [ i ] = Math . floor ( offset / strides [ i ] ) ; offset -= indices [ i ] * strides [ i ] ; } indices [ indices . length - 1 ] = offset ; return indices ; } static getActualAxisFromNegativeValue ( axis , tensorRank ) { if ( axis < - tensorRank && axis >= tensorRank - 1 ) { throw new Error ( 'unsupported axis for this operation.' ) ; } return axis < 0 ? axis + tensorRank : axis ; } param index param dims param axisToIncrementOn static incrementIndex ( index , dims , axisToIncrementOn ? ) { if ( axisToIncrementOn === undefined ) { axisToIncrementOn = dims . length ; } for ( let k = axisToIncrementOn - 1 ; k >= 0 ; -- k ) { index [ k ] ++ ; if ( index [ k ] < dims [ k ] ) { break ; } index [ k ] = 0 ; } } param originalDims param shapeHints static calculateReshapedDims ( originalDims , shapeHints ) { const nDims = shapeHints . length ; const reshapedDims = new Array < number > ( nDims ) ; let unknownDimension = - 1 ; let size = 1 ; for ( let i = 0 ; i < nDims ; i ++ ) { if ( shapeHints [ i ] < - 1 ) { throw new Error ( 'a dimension cannot be less than -1' ) ; } if ( shapeHints [ i ] === - 1 ) { if ( unknownDimension !== - 1 ) { throw new Error ( 'at most one dimension can be -1' ) ; } unknownDimension = i ; } else { if ( shapeHints [ i ] === 0 ) { if ( i >= originalDims . length ) { throw new Error ( 'the dimension with value zero exceeds the dimension size of the input tensor' ) ; } reshapedDims [ i ] = originalDims [ i ] ; } else { reshapedDims [ i ] = shapeHints [ i ] ; } size *= reshapedDims [ i ] ; } } if ( unknownDimension !== - 1 ) { const originalTensorFlattenedSize = ShapeUtil . size ( originalDims ) ; if ( originalTensorFlattenedSize % size !== 0 ) { throw new Error ( `the input tensor cannot be reshaped to the requested shape. Input shape: [${ originalDims }] Output shape: [${ shapeHints }]` ) ; } reshapedDims [ unknownDimension ] = originalTensorFlattenedSize / size ; } return reshapedDims ; } param a param perm static sortBasedOnPerm ( a , perm ? ) { if ( perm ) { return perm . map ( ( v ) => a [ v ] ) ; } else { return a . slice ( ) . reverse ( ) ; } } param dims param pad static padShape ( dims , pad ) { const rank = dims . length ; return dims . map ( ( v , i ) => v + pad [ i ] + pad [ i + rank ] ) ; } param shape1 param shape2 static areEqual ( shape1 , shape2 ) { if ( shape1 . length !== shape2 . length ) { return false ; } return shape1 . every ( ( v , i ) => v === shape2 [ i ] ) ; } param dims param pick static splitDimsIntoTwo ( dims , pick ) { const picked = [ ] ; const remnants = [ ] ; for ( let i = 0 ; i < dims . length ; ++ i ) { if ( i === pick ) { picked . push ( dims [ i ] ) ; } else { remnants . push ( dims [ i ] ) ; } } return [ picked , remnants ] ; } } export class TypedArrayUtil { static createTypedArray ( type , size ) { switch ( type ) { case 'bool' : return new Uint8Array ( size ) ; case 'int32' : return new Int32Array ( size ) ; case 'float32' : return new Float32Array ( size ) ; default : throw new Error ( 'Unsupported type' ) ; } } }