"""Micro-proofs quoted in Part 0: storage sharing, view errors,
mutation rewriting history, the no_grad layer, float32 absorption."""
import torch

print(f"torch {torch.__version__}\n")

# 1. a tensor is a window over storage
x = torch.arange(6, dtype=torch.float32)
v = x.view(2, 3)
print("same bytes under both:", x.data_ptr() == v.data_ptr())
print("v.stride():", v.stride(), " v.t().stride():", v.t().stride())
try:
    v.t().view(-1)
except RuntimeError as e:
    print("v.t().view(-1) ->", str(e).split(".")[0])

# 2. mutation rewrites the recorded program
a = torch.ones(3, requires_grad=True)
y = a * 2
print("\nbefore add_:", type(y.grad_fn).__name__)
y.add_(1)
print("after  add_:", type(y.grad_fn).__name__)

# 3. no_grad removes one dispatcher layer
with torch.no_grad():
    z = a * 2
print("\ninside no_grad, grad_fn:", z.grad_fn)

# 4. float32 absorbs small numbers
t = torch.tensor(1e8)
print("\n(1e8 + 1) - 1e8 in float32 =", ((t + 1) - t).item())

# 5. the size of the operation list (idea 3)
print("\nregistered operation names:",
      len(torch._C._dispatch_get_all_op_names()))
