function valueAndGrads(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 (0, _tensor.Tensor)), ()=>"The args passed in valueAndGrads(f)(args) must be array of tensors");
        _util.assert(dy == null || dy instanceof (0, _tensor.Tensor), ()=>"The dy passed in valueAndGrads(f)(args, dy) must be a tensor");
        const res = (0, _engine.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;
    };
}
Like `tf.grads`, but returns also the value of `f()`. Useful when `f()`
returns a metric you want to show.

The result is a rich object with the following properties:
- grads: The gradients of `f()` w.r.t. each input (result of `tf.grads`).
- value: The value returned by `f(x)`.

```js
// f(a, b) = a * b
const f = (a, b) => a.mul(b);
// df/da = b, df/db = a
const g = tf.valueAndGrads(f);

const a = tf.tensor1d([2, 3]);
const b = tf.tensor1d([-2, -3]);
const {value, grads} = g([a, b]);

const [da, db] = grads;

console.log('value');
value.print();

console.log('da');
da.print();
console.log('db');
db.print();
```

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