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 (0, _tensor.Variable)), ()=>"The varList passed in variableGrads(f, varList) must be an array of variables");
    const specifiedVarList = varList != null;
    if (!specifiedVarList) {
        // Get all of the trainable variables.
        varList = [];
        for(const varName in (0, _engine.ENGINE).registeredVariables)varList.push((0, _engine.ENGINE).registeredVariables[varName]);
    }
    const specifiedNonTrainable = specifiedVarList ? varList.filter((variable)=>!variable.trainable) : null;
    // Prune non-trainable variables.
    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 } = (0, _engine.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) // If varList is explicitly provided and contains non-trainable values,
    // add them to the returned gradients with `null` values.
    specifiedNonTrainable.forEach((v)=>namedGrads[v.name] = null);
    return {
        value,
        grads: namedGrads
    };
}
Computes and returns the gradient of f(x) with respect to the list of
trainable variables provided by `varList`. If no list is provided, it
defaults to all trainable variables.

```js
const a = tf.variable(tf.tensor1d([3, 4]));
const b = tf.variable(tf.tensor1d([5, 6]));
const x = tf.tensor1d([1, 2]);

// f(a, b) = a * x ^ 2 + b * x
const f = () => a.mul(x.square()).add(b.mul(x)).sum();
// df/da = x ^ 2, df/db = x
const {value, grads} = tf.variableGrads(f);

Object.keys(grads).forEach(varName => grads[varName].print());
```

@param f The function to execute. f() should return a scalar.
@param varList The list of variables to compute the gradients with respect
    to. Defaults to all trainable variables.
@returns An object with the following keys and values:
  - `value`: The value of the function `f`.
  - `grads`: A map from the names of the variables to the gradients.
    If the `varList` argument is provided explicitly and contains a subset of
    non-trainable variables, this map in the return value will contain keys
    that map the names of the non-trainable variables to `null`.

@doc {heading: 'Training', subheading: 'Gradients'}