function grad(f) {
    _util.assert(_util.isFunction(f), ()=>"The f passed in grad(f) must be a function");
    return (x, dy)=>{
        // x can be of any dtype, thus null as the last argument.
        const $x = (0, _tensorUtilEnv.convertToTensor)(x, "x", "tf.grad", "string_or_numeric");
        const $dy = dy != null ? (0, _tensorUtilEnv.convertToTensor)(dy, "dy", "tf.grad") : null;
        return (0, _engine.ENGINE).tidy(()=>{
            const { value, grads } = (0, _engine.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];
        });
    };
}
Provided `f(x)`, returns another function `g(x, dy?)`, which gives the
gradient of `f(x)` with respect to `x`.

If `dy` is provided, the gradient of `f(x).mul(dy).sum()` with respect to
`x` is computed instead. `f(x)` must take a single tensor `x` and return a
single tensor `y`. If `f()` takes multiple inputs, use `tf.grads` instead.

```js
// f(x) = x ^ 2
const f = x => x.square();
// f'(x) = 2x
const g = tf.grad(f);

const x = tf.tensor1d([2, 3]);
g(x).print();
```

```js
// f(x) = x ^ 3
const f = x => x.pow(tf.scalar(3, 'int32'));
// f'(x) = 3x ^ 2
const g = tf.grad(f);
// f''(x) = 6x
const gg = tf.grad(g);

const x = tf.tensor1d([2, 3]);
gg(x).print();
```

@param f The function f(x), to compute gradient for.

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