 ) -> (value: Tensor<R>, gradient: (T.TangentVector, U.TangentVector))
     where T: Differentiable, U: Differentiable, R: TensorFlowFloatingPoint {
     let (y, pullback) = valueWithPullback(at: x, y, in: f)
-    precondition(y.rank == 0)
+    precondition(y.rank == 0, """
+        The function being differentiated produced a tensor with shape \(y.shape). \
+        You can only compute the gradient of functions that return scalar values.
+        """)
     return (y, pullback(Tensor<R>(1)))
 }
 

) -> (value: Tensor<R>, gradient: T.TangentVector)
where T: Differentiable, R: TensorFlowFloatingPoint {
    let (y, pullback) = valueWithPullback(at: x, in: f)
    precondition(y.rank == 0)
    return (y, pullback(Tensor<R>(1)))
}


        in f: @differentiable (Self, T) -> Tensor<R>
    ) -> (value: Tensor<R>, gradient: (TangentVector, T.TangentVector)) {
        let (y, pb) = self.valueWithPullback(at: x, in: f)
        precondition(y.rank == 0, """
            The function being differentiated produced a tensor with shape \(y.shape). \
            You can only compute the gradient of functions that return scalar values.
            """)
        return (y, pb(Tensor<R>(1)))
    }
}
