x = Variable(torch.ones(2,2), requires_grad=True) # requires_grad: calculate gradients
print(x)

print(x.data)

print(x.grad)

y = x + 2
print(y)

z = y * y * 3
out = z.sum()
print(z, out)

out.backward() # backpropagation
print(x.grad)

"""
Explanation: PyTorch Autograd Introduction
Example from official documentation
End of explanation
"""


Q = torch.eye(3)

Q

"""
Explanation: $z = (x + 2)^2 * 3$
$out = 3(x+2)^2$
$dout/dx = 6(x+2)' * 1 = 6(x+2)$
$= 6*3 = 18$
Tensors and Variables have the same API. Variables have the Autograd option, in that they keep track of how they were created.
If you want to use Autograd you have to use Variables.
End of explanation
"""


