// Neutral tensor construction, explicit numerical operations, and one gradient update.
import aner.tensor
import aner.nn

let samples: Tensor = [[1.0, 2.0], [3.0, 4.0]]
let weights: Tensor = [[2.0], [1.0]]
let product = nn.matmul(samples, weights)
print("Product shape and values")
print(tensor.rows(product))
print(tensor.cols(product))
print(tensor.value(product, 0, 0))
print(tensor.value(product, 1, 0))
print("Default precision and device")
print(samples.dtype())
print(samples.device())
print("Explicit detached Float32 conversion")
print(samples.astype("Float32").dtype())

let input: Tensor = [[1.0, 2.0]]
let target: Tensor = [[1.0]]
let parameter = nn.parameter([[0.0], [0.0]])
let loss = nn.mse(nn.matmul(input, parameter), target)
nn.backward(loss)
let updated = nn.sgd(parameter, rate: 0.1)
print("Loss before and after one gradient update")
print(tensor.item(loss))
print(tensor.item(nn.mse(nn.matmul(input, updated), target)))
print("First weight gradient")
print(tensor.value(nn.grad(parameter), 0, 0))
