license import { CustomGradientFunc , ENGINE } from './engine' ; import { Scalar , Tensor , Variable } from './tensor' ; import { NamedTensorMap } from './tensor_types' ; import { convertToTensor , convertToTensorArray } from './tensor_util_env' ; import { TensorLike } from './types' ; import * as util from './util' ; param f doc function grad ( f ) { util . assert ( util . isFunction ( f ) , ( ) => 'The f passed in grad(f) must be a function' ) ; return ( x , dy ? ) : Tensor => { const $x = convertToTensor ( x , 'x' , 'tf.grad' , 'string_or_numeric' ) ; const $dy = ( dy != null ) ? convertToTensor ( dy , 'dy' , 'tf.grad' ) : null ; return ENGINE . tidy ( ( ) => { const { value , grads } = ENGINE . gradients ( ( ) => f ( $x ) , [ $x ] , $dy ) ; if ( $dy != null ) { util . assertShapesMatch ( value . shape , $dy . shape , 'The shape of dy passed in grad(f)(x, dy) must match the shape ' + 'returned by f(x)' ) ; } checkGrads ( grads ) ; return grads [ 0 ] ; } ) ; } ; } param f doc function grads ( f ) { util . assert ( util . isFunction ( f ) , ( ) => 'The f passed in grads(f) must be a function' ) ; return ( args , dy ? ) : Tensor [ ] => { util . assert ( Array . isArray ( args ) , ( ) => 'The args passed in grads(f)(args) must be an array ' + 'of `Tensor`s or `TensorLike`s' ) ; const $args = convertToTensorArray ( args , 'args' , 'tf.grads' , 'string_or_numeric' ) ; const $dy = ( dy != null ) ? convertToTensor ( dy , 'dy' , 'tf.grads' ) : null ; return ENGINE . tidy ( ( ) => { const { value , grads } = ENGINE . gradients ( ( ) => f ( ... $args ) , $args , $dy ) ; if ( $dy != null ) { util . assertShapesMatch ( value . shape , $dy . shape , 'The shape of dy passed in grads(f)([x1,...], dy) must ' + 'match the shape returned by f([x1,...])' ) ; } checkGrads ( grads ) ; return grads ; } ) ; } ; } doc function valueAndGrad < I extends Tensor , O extends Tensor > ( f ) { util . assert ( util . isFunction ( f ) , ( ) => 'The f passed in valueAndGrad(f) must be a function' ) ; return ( x , dy ? ) => { util . assert ( x instanceof Tensor , ( ) => 'The x passed in valueAndGrad(f)(x) must be a tensor' ) ; util . assert ( dy == null || dy instanceof Tensor , ( ) => 'The dy passed in valueAndGrad(f)(x, dy) must be a tensor' ) ; const { grads , value } = ENGINE . gradients ( ( ) => f ( x ) , [ x ] , dy ) ; checkGrads ( grads ) ; return { grad : grads [ 0 ] as I , value } ; } ; } doc function valueAndGrads < O extends Tensor > ( f ) { util . assert ( util . isFunction ( f ) , ( ) => 'The f passed in valueAndGrads(f) must be a function' ) ; return ( args , dy ? ) => { util . assert ( Array . isArray ( args ) && args . every ( arg => arg instanceof Tensor ) , ( ) => 'The args passed in valueAndGrads(f)(args) must be array of ' + 'tensors' ) ; util . assert ( dy == null || dy instanceof Tensor , ( ) => 'The dy passed in valueAndGrads(f)(args, dy) must be a tensor' ) ; const res = ENGINE . gradients ( ( ) => f ( ... args ) , args , dy ) ; if ( dy != null ) { util . assertShapesMatch ( res . value . shape , dy . shape , 'The shape of dy passed in valueAndGrads(f)([x1,...], dy) must ' + 'match the shape returned by f([x1,...])' ) ; } checkGrads ( res . grads ) ; return res ; } ; } param f param varList returns doc function variableGrads ( f , varList ? ) { util . assert ( util . isFunction ( f ) , ( ) => 'The f passed in variableGrads(f) must be a function' ) ; util . assert ( varList == null || Array . isArray ( varList ) && varList . every ( v => v instanceof Variable ) , ( ) => 'The varList passed in variableGrads(f, varList) must be an array ' + 'of variables' ) ; const specifiedVarList = varList != null ; if ( ! specifiedVarList ) { varList = [ ] ; for ( const varName in ENGINE . registeredVariables ) { varList . push ( ENGINE . registeredVariables [ varName ] ) ; } } const specifiedNonTrainable = specifiedVarList ? varList . filter ( variable => ! variable . trainable ) : null ; const originalVarCount = varList . length ; varList = varList . filter ( variable => variable . trainable ) ; util . assert ( varList . length > 0 , ( ) => `variableGrads() expects at least one of the input variables to ` + `be trainable, but none of the ${ originalVarCount } variables is ` + `trainable.` ) ; const allowNoGradients = true ; const { value , grads } = ENGINE . gradients ( f , varList , null , allowNoGradients ) ; util . assert ( grads . some ( g => g != null ) , ( ) => 'Cannot find a connection between any variable and the result of ' + 'the loss function y=f(x). Please make sure the operations that ' + 'use variables are inside the function f passed to minimize().' ) ; util . assert ( value . rank === 0 , ( ) => `The f passed in variableGrads(f) must return a scalar, but it ` + `returned a rank-${ value . rank } tensor` ) ; const namedGrads = { } ; varList . forEach ( ( v , i ) => { if ( grads [ i ] != null ) { namedGrads [ v . name ] = grads [ i ] ; } } ) ; if ( specifiedNonTrainable != null ) { specifiedNonTrainable . forEach ( v => namedGrads [ v . name ] = null ) ; } return { value , grads : namedGrads } ; } param f doc function customGrad < T extends Tensor > ( f ) { return ENGINE . customGrad ( f ) ; } function checkGrads ( grads ) { const numNullGradients = grads . filter ( g => g == null ) . length ; if ( numNullGradients > 0 ) { throw new Error ( `Cannot compute gradient of y=f(x) with respect to x. Make sure that
    the f you passed encloses all operations that lead from x to y.` ) ; } } export { customGrad , variableGrads , valueAndGrad , valueAndGrads , grad , grads , } ;