Hi,
Few things:

The .grad field is only populated for leafs (tensor with no history) that require gradients when you call .backward(). In particular that is why you y at the end does not have its .grad field populated: it is not a leaf. If you want its field to be populated, you can call y.retain_grad() before the backward call.
Pytorch uses AD. Which, for multi-dimensional functions, only computes vector Jacobian product. In your first example, you function is R^2 ->R^2 (EDIT: 2D -> R2). So the Jacobian is a 2x2 matrix. You thus need to provide the vector by which it should be multiplied. Since your Jacobian here is diagonal, you can give [1., 1.] as you did to get that diagonal.
For more mathematical computations like this, I would recommend using the autograd.grad API as it is simpler for higher level derivatives and makes it easier to know exactely what you’re doing:

x = torch.tensor([0.0, 1.0], requires_grad = True)
f = 2*x
grad = autograd.grd(outputs=f, inputs=x, grad_outputs=torch.tensor([1.0, 1.0]))[0]
print(grad)