std :: ml::tensor
Struct std::DenseTensor
generic<E> struct DenseTensor {
&heap E data;
&heap unsigned long shape;
unsigned long ndim;
unsigned long size;
int requiresGrad;
&heap float grad;
?fn void(&DenseTensor<float>) gradFn;
struct Vec<&DenseTensor<E>> parents;
int visited;
float extraScalar;
?&TensorArena owner;
int dev;
unsigned long dbuf;
unsigned long dgrad;
int coh;
}A DenseTensor<E> stores its values as E (element.h) and computes in float32; its gradient is always float32. FloatTensor (E = float) is the float32 tensor every op has always used; HalfTensor (F16) and BF16Tensor (BF16) keep half the memory per value and run every op and its backward pass through float32. Generic code is written against trait Tensor and works with all three.
The graph fields below have the same layout for every E (data is one pointer whatever E is), so the backward pass walks a graph that mixes element types: to_float() / from_float() are graph nodes between them.
Fields
flat row-major buffer, length == size
shape[0..ndim)
product of shape
float32, length == size; only allocated if requiresGrad
Autograd graph edges. 'gradFn' is the node's backward function (TensorBackwardFn below), taking the node through its float32 layout so a graph can mix element types. null for a leaf.
inputs this tensor was computed from
topological-sort scratch flag, used and cleared by backward()
op-specific constant (e.g. scale's k) the gradFn needs
the TensorArena that owns this tensor's memory, or null (heap)
Where the values live (device.h). 'data' always has host storage; a tensor on a GPU also has 'dbuf' there, and 'coh' says which copies of the values and of the gradient are current (TS_* below), so each side is copied only when the other one changed. 16-bit tensors stay on the CPU (place() refuses), their ops computing through float32 tensors.
DEVICE_*; DEVICE_CPU: no device copy
the values on 'dev' (backend handle), 0 if none
the gradient on 'dev', 0 if none
TS_* bits
Methods
Element access as float32 (row-major; 'i' for a rank-1 tensor, 'r, c' for a rank-2 one).
= drop()
Must be a scalar (size == 1, e.g. a loss). Seeds grad = 1, walks the graph in reverse topological order, and accumulates gradients into every requiresGrad ancestor's 'grad' buffer (allocating it on first write). Gradients accumulate across calls, matching PyTorch's default (zero_grad() between optimizer steps if that's not what you want).
static &DenseTensor<E> new_2d(unsigned long rows, unsigned long cols, int requiresGrad)source · definitionCopies float32 'values' (length must equal the tensor's size) into a freshly allocated tensor of the given shape, rounded to E.
static &DenseTensor<E> from_2d(
[]const float values,
unsigned long rows,
unsigned long cols,
int requiresGrad,
)source · definitionvalues must hold rows * cols floats (a shorter slice panics).
in place
a * scalar k
-> scalar (1-element) tensor
The values as a float32 tensor, and a float32 tensor's values rounded to E: graph nodes (gradients pass through unchanged), the way an op on a 16-bit tensor computes in float32.
Moves this tensor itself -- values and gradient -- to 'device', keeping it the same object (parameters, other leaves). 1 on success; 0 for an unavailable device or a 16-bit tensor (they stay on the CPU).
Makes the host copy of the values current (waits for the device). 1 on success.
A copy on 'device', or this tensor if it is there already: a graph node, the gradient flows back to this tensor's device.
The values' device buffer handle (0 when the tensor is on the CPU).