fn

empty

Tensor
empty(size: _int | tuple[_int, ...] = (), dtype: DTypeLike = None, device: DeviceLike = None, requires_grad: _bool = False)
source

Return a tensor whose elements are uninitialised (undefined memory).

Allocates the backing memory for the requested shape but performs no initialisation write. Reading any element before overwriting it is undefined behaviour — values may be zero, garbage, or a previous tensor's data depending on the allocator's state.

The key advantage over zeros or ones is that the initialisation kernel is skipped entirely, which is measurable when allocating large temporary buffers inside hot loops. The typical pattern is:

allocatefill via in-place opread\text{allocate} \to \text{fill via in-place op} \to \text{read}

Parameters

*sizeint or tuple[int, ...]
Shape of the output tensor. Accepts separate ints empty(2, 3) or a single tuple empty((2, 3)).
dtypelucid.dtype
Scalar data type. Defaults to the global default dtype.
devicestr or lucid.device
Target device — "cpu" or "metal".
requires_gradbool
If True, downstream operations are tracked by autograd. Default: False.

Returns

Tensor

Uninitialised tensor of shape size.

Notes

Never use empty as input to a computation that reads its values without first writing them. Common safe patterns:

  • out = lucid.empty(n); lucid.matmul(a, b, out=out)
  • Passing as a pre-allocated output buffer to in-place operations

On Metal the memory is allocated in the device heap; the allocator may reuse pages from recently freed tensors, so values are truly unpredictable.

Examples

>>> import lucid
>>> t = lucid.empty(3, 4)
>>> t.shape
(3, 4)
Safe usage — always overwrite before reading:
>>> buf = lucid.empty(5)
>>> buf[:] = lucid.arange(5).astype(lucid.float32)