license import { DataType , TypedArray } from './types' ; import { computeStrides , isString , rightPad , sizeFromShape } from './util' ; const FORMAT_LIMIT_NUM_VALS = 20 ; const FORMAT_NUM_FIRST_LAST_VALS = 3 ; const FORMAT_NUM_SIG_DIGITS = 7 ; export function tensorToString ( vals , shape , dtype , verbose ) { const strides = computeStrides ( shape ) ; const padPerCol = computeMaxSizePerColumn ( vals , shape , dtype , strides ) ; const rank = shape . length ; const valsLines = subTensorToString ( vals , shape , dtype , strides , padPerCol ) ; const lines = [ 'Tensor' ] ; if ( verbose ) { lines . push ( `  dtype: ${ dtype }` ) ; lines . push ( `  rank: ${ rank }` ) ; lines . push ( `  shape: [${ shape }]` ) ; lines . push ( `  values:` ) ; } lines . push ( valsLines . map ( l => '    ' + l ) . join ( '\n' ) ) ; return lines . join ( '\n' ) ; } function computeMaxSizePerColumn ( vals , shape , dtype , strides ) { const n = sizeFromShape ( shape ) ; const numCols = strides [ strides . length - 1 ] ; const padPerCol = new Array ( numCols ) . fill ( 0 ) ; const rank = shape . length ; const valuesOrTuples = dtype === 'complex64' ? createComplexTuples ( vals ) : vals ; if ( rank > 1 ) { for ( let row = 0 ; row < n / numCols ; row ++ ) { const offset = row * numCols ; for ( let j = 0 ; j < numCols ; j ++ ) { padPerCol [ j ] = Math . max ( padPerCol [ j ] , valToString ( valuesOrTuples [ offset + j ] , 0 , dtype ) . length ) ; } } } return padPerCol ; } function valToString ( val , pad , dtype ) { let valStr ; if ( Array . isArray ( val ) ) { valStr = `${ parseFloat ( val [ 0 ] . toFixed ( FORMAT_NUM_SIG_DIGITS ) ) } + ` + `${ parseFloat ( val [ 1 ] . toFixed ( FORMAT_NUM_SIG_DIGITS ) ) }j` ; } else if ( isString ( val ) ) { valStr = `'${ val }'` ; } else if ( dtype === 'bool' ) { valStr = boolNumToString ( val ) ; } else { valStr = parseFloat ( val . toFixed ( FORMAT_NUM_SIG_DIGITS ) ) . toString ( ) ; } return rightPad ( valStr , pad ) ; } function boolNumToString ( v ) { return v === 0 ? 'false' : 'true' ; } function subTensorToString ( vals , shape , dtype , strides , padPerCol , isLast = true ) { const storagePerElement = dtype === 'complex64' ? 2 : 1 ; const size = shape [ 0 ] ; const rank = shape . length ; if ( rank === 0 ) { if ( dtype === 'complex64' ) { const complexTuple = createComplexTuples ( vals ) ; return [ valToString ( complexTuple [ 0 ] , 0 , dtype ) ] ; } if ( dtype === 'bool' ) { return [ boolNumToString ( vals [ 0 ] as number ) ] ; } return [ vals [ 0 ] . toString ( ) ] ; } if ( rank === 1 ) { if ( size > FORMAT_LIMIT_NUM_VALS ) { const firstValsSize = FORMAT_NUM_FIRST_LAST_VALS * storagePerElement ; let firstVals = Array . from < number | string | [ number , number ] > ( vals . slice ( 0 , firstValsSize ) ) ; let lastVals = Array . from < number | string | [ number , number ] > ( vals . slice ( ( size - FORMAT_NUM_FIRST_LAST_VALS ) * storagePerElement , size * storagePerElement ) ) ; if ( dtype === 'complex64' ) { firstVals = createComplexTuples ( firstVals ) ; lastVals = createComplexTuples ( lastVals ) ; } return [ '[' + firstVals . map ( ( x , i ) => valToString ( x , padPerCol [ i ] , dtype ) ) . join ( ', ' ) + ', ..., ' + lastVals . map ( ( x , i ) => valToString ( x , padPerCol [ size - FORMAT_NUM_FIRST_LAST_VALS + i ] , dtype ) ) . join ( ', ' ) + ']' ] ; } const displayVals = dtype === 'complex64' ? createComplexTuples ( vals ) : Array . from < number | string > ( vals ) ; return [ '[' + displayVals . map ( ( x , i ) => valToString ( x , padPerCol [ i ] , dtype ) ) . join ( ', ' ) + ']' ] ; } const subshape = shape . slice ( 1 ) ; const substrides = strides . slice ( 1 ) ; const stride = strides [ 0 ] * storagePerElement ; const lines = [ ] ; if ( size > FORMAT_LIMIT_NUM_VALS ) { for ( let i = 0 ; i < FORMAT_NUM_FIRST_LAST_VALS ; i ++ ) { const start = i * stride ; const end = start + stride ; lines . push ( ... subTensorToString ( vals . slice ( start , end ) , subshape , dtype , substrides , padPerCol , false ) ) ; } lines . push ( '...' ) ; for ( let i = size - FORMAT_NUM_FIRST_LAST_VALS ; i < size ; i ++ ) { const start = i * stride ; const end = start + stride ; lines . push ( ... subTensorToString ( vals . slice ( start , end ) , subshape , dtype , substrides , padPerCol , i === size - 1 ) ) ; } } else { for ( let i = 0 ; i < size ; i ++ ) { const start = i * stride ; const end = start + stride ; lines . push ( ... subTensorToString ( vals . slice ( start , end ) , subshape , dtype , substrides , padPerCol , i === size - 1 ) ) ; } } const sep = rank === 2 ? ',' : '' ; lines [ 0 ] = '[' + lines [ 0 ] + sep ; for ( let i = 1 ; i < lines . length - 1 ; i ++ ) { lines [ i ] = ' ' + lines [ i ] + sep ; } let newLineSep = ',\n' ; for ( let i = 2 ; i < rank ; i ++ ) { newLineSep += '\n' ; } lines [ lines . length - 1 ] = ' ' + lines [ lines . length - 1 ] + ']' + ( isLast ? '' : newLineSep ) ; return lines ; } function createComplexTuples ( vals ) { const complexTuples = [ ] ; for ( let i = 0 ; i < vals . length ; i += 2 ) { complexTuples . push ( [ vals [ i ] , vals [ i + 1 ] ] as [ number , number ] ) ; } return complexTuples ; }