Reference-counted tensor implementation owned by every Python-level lucid.Tensor.
The C++ op layer receives and returns TensorImplPtr
(shared_ptr<TensorImpl>) directly; the Python wrapper is a thin layer
that forwards attribute access here. TensorImpl extends
std::enable_shared_from_this so member functions can hand out
additional shared_ptr references to *this safely (e.g. when
building autograd graph nodes that capture the producing tensor).
See Also
Storage — backing memory variant.
TensorMeta — shape / stride / dtype / device bundle.
AutogradMeta — autograd bookkeeping.
Attributes
storageStorageCpuStorage, GpuStorage, or SharedStorage. Shared with every view.offsetstd::size_tstorage. Non-zero only for views created by make_view.metaTensorMetaTensorMeta independent of the base.autogradstd::optional<AutogradMeta>requires_grad, is_leaf, version, grad_fn, accumulated gradient). std::nullopt for tensors that have never participated in the autograd graph.Constructors
1TensorImpl
void TensorImpl(int storage, int shape, Dtype dtype, Device device, bool requires_grad)Constructs a TensorImpl from an already-allocated Storage.
The C-contiguous (row-major) stride is computed automatically from
shape and dtype. When requires_grad is true an
AutogradMeta is constructed immediately; otherwise it is
left unallocated until needed.
Parameters
storageStorageshapeShapedtypeDtypedeviceDevicestorage.requires_gradboolStatic methods
3int from_bytes(int data, int shape, Dtype dtype, Device device, bool requires_grad)Reconstructs a TensorImpl from a raw bytes blob + metadata.
Inverse of to_bytes. When device == Device::GPU the
buffer is uploaded to MLX before returning.
Parameters
datapy::bytesshape_numel(shape) * dtype_size(dtype) bytes long.shapeShapedtypeDtypedeviceDevicerequires_gradboolReturns
std::shared_ptr<TensorImpl>Newly constructed tensor.
int from_numpy(int arr, Device device, bool requires_grad)Constructs a TensorImpl by copying data from a NumPy array.
The data is always copied into a fresh CpuStorage buffer.
When device == Device::GPU the buffer is subsequently uploaded
to MLX via the GPU bridge, yielding a GpuStorage-backed
tensor.
Parameters
arrpy::arraydeviceDevicerequires_gradboolReturns
std::shared_ptr<TensorImpl>Newly constructed tensor.
Raises
DtypeErrorint make_view(const int & base, int shape, int stride, int offset_bytes)Creates a new TensorImpl that aliases base's Storage with a different shape, stride, and optional byte offset.
The view inherits base's requires_grad and is_leaf flags.
No data is copied — modifying either tensor through an in-place op
affects the other (which is precisely why the shared
VersionCounter exists).
Parameters
basestd::shared_ptr<TensorImpl>shapeShapestrideStrideoffset_bytesstd::size_t= Nonebase's storage origin. Defaults to 0.Returns
std::shared_ptr<TensorImpl>New view tensor sharing base's storage.
Methods
49Accumulates a graph-mode gradient.
First call sets the gradient outright; subsequent calls add g
into the existing grad_as_impl via the engine's add op
so the accumulation is differentiable when create_graph=true.
Parameters
gstd::shared_ptr<TensorImpl>Increments the autograd version counter.
Called by every in-place op so autograd can detect mutations of
tensors that were saved for backward. No-op when no
AutogradMeta exists — i.e. the tensor has never participated
in the autograd graph, so version tracking is unnecessary.
Drops the backward function pointer without removing the AutogradMeta.
Used when an in-place op detaches a tensor from the autograd graph
(tensor.detach_()-style semantics).
Copies data from other into this tensor.
Supports CpuStorage↔CpuStorage,
GpuStorage↔GpuStorage, and all combinations
involving SharedStorage. Bumps the version counter on
success.
Parameters
otherTensorImplRaises
DeviceMismatchother.device() != device().DtypeMismatchother.dtype() != dtype().ShapeMismatchother.shape() != shape().Returns the tensor data as a NumPy array.
For CPU tensors the returned array is a zero-copy view backed by a
py::capsule that keeps the TensorImpl alive. For
GPU tensors the data is synchronously downloaded to a temporary CPU
buffer first.
See Also
to_bytes — NumPy-free alternative.
tolist — nested Python list alternative.
Returns
py::objectA NumPy array (one of the supported dtypes, except for Dtype::C64 which is represented as a complex64 array).
Returns the device tag. Backs the Python .device attribute.
Mutable device accessor — returns the underlying Device by reference. Rarely used outside engine internals; pairs with the public const device.
Notes
Same footgun as dtype_field: changing the device WITHOUT also
migrating storage_ to the new device produces a TensorImpl that
claims to live on one stream while its bytes live on another. Only
call from paths that have already moved the storage.
Returns the element dtype. Backs the Python .dtype attribute.
Mutable dtype accessor — returns the underlying Dtype by reference. Rarely used outside engine internals; pairs with the public const dtype.
Notes
Changing the dtype WITHOUT also reinterpreting / reallocating
storage_ is a footgun: the bytes on disk no longer match the
declared element type, so subsequent ops will read garbage. Only call
from paths that have already updated storage to match the new dtype.
Returns a pointer to the AutogradMeta, constructing it in-place if it does not yet exist. The returned pointer is stable (optional stores in-place) as long as autograd_ is not reset.
Forces evaluation of this tensor's lazy MLX computation graph.
GPU tensors: calls mlx::core::eval() on the underlying array,
materialising the buffer.
CPU tensors: no-op (CPU storage is always materialised).
Notes
Required before any path that wants to dereference the GPU buffer
through a CPU pointer (e.g. to_bytes, data_as_python).
int grad_as_impl()Returns the graph-mode gradient as a full TensorImpl.
Populated when backward(create_graph=True) was called — allowing
the gradient itself to participate in further autograd (Hessian-vector
products, MAML, second-order ops).
Returns
std::shared_ptr<TensorImpl>The graph-mode gradient tensor, or nullptr when no graph-mode gradient has been accumulated.
Returns the accumulated gradient as a NumPy array.
Identical download semantics to data_as_python.
Returns
py::objectThe gradient array, or py::none when no gradient has been computed yet.
Returns the backward function pointer for this tensor.
Returns
const std::shared_ptr<Node>&Non-null for non-leaf tensors produced by a differentiable op, nullptr for leaves and tensors with no AutogradMeta.
Returns the output index that connects this tensor to its grad_fn.
Multi-output ops produce several tensors that share one
Node; this index tells the backward pass which of the
node's outputs the tensor corresponds to.
Returns
std::uint32_tOutput index, or 0 when no AutogradMeta exists.
Returns the accumulated gradient Storage, if any.
Returns
const std::optional<Storage>&std::nullopt when no gradient has been accumulated, or when the tensor has no AutogradMeta at all.
int grad_to_tensor()Wraps the tensor's accumulated gradient as a fresh TensorImpl that shares the underlying Storage.
Prefers the graph-mode grad_impl when present; otherwise wraps
the standard grad Storage directly. Replaces the
older NumPy round-trip used by the Python-side Tensor.grad
accessor.
Returns
std::shared_ptr<TensorImpl>Tensor view of the gradient, or nullptr when no gradient has been accumulated.
Predicate: does the current stride equal the row-major contiguous stride for this shape and dtype?
Returns
booltrue for canonically laid-out tensors; false for views that were transposed / sliced / otherwise reordered.
Returns the leaf-status flag.
A leaf tensor was constructed directly by the user; a non-leaf
tensor is the output of a differentiable op and carries a non-null
grad_fn.
Returns
booltrue for leaves, true (default) when there is no AutogradMeta yet.
Extracts a single-element tensor's value as a Python scalar.
GPU tensors are downloaded to CPU first. The Dtype::F16
IEEE-754 binary16 → float conversion is performed engine-side so
the Python wrapper avoids duplicating the bit-fiddling logic from
to_string.
Returns
py::objectint / float / bool / complex depending on dtype.
Raises
ValueErrornumel() != 1.Returns a const reference to the TensorMeta bundle.
Returns a mutable reference to the gradient Storage, constructing the AutogradMeta on demand.
Used by the engine's gradient accumulation paths.
Returns
std::optional<Storage>&Mutable reference; the optional may still be empty even though AutogradMeta is now allocated.
Returns a mutable reference to the backing Storage variant.
Used by in-place ops; callers are responsible for bumping the version counter when they mutate the underlying bytes.
Returns the total byte footprint of the tensor's logical data.
Equal to numel() * dtype_size(dtype()); does not include any
extra storage capacity beyond the tensor's view.
Returns
std::size_tLogical byte size of the tensor data.
Returns the total number of elements (product of shape dimensions).
Returns
std::size_t1 for a scalar (empty shape).
Returns the current requires_grad flag.
Returns
boolfalse when there is no AutogradMeta at all (the common inference path — avoids an indirection).
Returns whether this tensor will retain its gradient even when it is a non-leaf.
Mirrors the reference framework's tensor.retain_grad() API.
Returns
booltrue when the engine should accumulate gradients into grad_storage for this tensor regardless of leaf status.
Sets the device field. Does not move data; used after an in-place device transfer has updated storage_.
Sets the dtype field. Does not touch the underlying bytes; used by pure metadata reinterpretation paths.
Attaches the backward function pointer.
Marks the tensor as non-leaf (callers usually pair this with
set_leaf(false)).
Parameters
fnstd::shared_ptr<Node>Sets the graph-mode gradient tensor.
Parameters
gstd::shared_ptr<TensorImpl>Sets the output index associated with grad_fn.
Parameters
nrstd::uint32_tNode.Stores a gradient Storage directly.
Parameters
gradStorageSets the leaf-status flag, constructing AutogradMeta if it does not yet exist.
Parameters
vboolis_leaf value.Enables or disables gradient tracking.
Constructs AutogradMeta lazily when v is true.
Setting to false does not destroy an already-existing
AutogradMeta — only the flag is cleared, so historical
version counters / saved tensors remain valid.
Parameters
vboolrequires_grad value.Toggles gradient retention for non-leaf tensors.
Parameters
vboolretain_grad value.Returns the shape vector. Backs the Python .shape attribute.
Private mutable references to the meta fields — used only within TensorImpl's own methods; callers should use the public set_* functions.
Returns a const reference to the backing Storage variant.
Predicate: is this tensor's CPU storage aliased by another tensor?
Returns
booltrue when the underlying CpuStorage ptr is referenced by more than one shared_ptr — i.e. at least one other view of this tensor exists. Always false for GpuStorage and SharedStorage variants (those variants do not currently report sharing through this hook).
Returns the byte offset of this tensor's first element from the start of storage.
Returns
std::size_t0 for owning tensors; non-zero for views created with make_view.
Returns the byte-stride vector. Backs the Python .stride() method.
Mutable stride accessor — returns the underlying Stride by reference so engine-side ops can rewrite strides in place without going through the full set_meta path. Pairs with the public const stride.
Notes
Modifying the stride WITHOUT also touching storage_ or shape_
produces an invalid TensorImpl — only call this from internal kernel
code that holds the necessary invariants. Does NOT bump the version
counter; callers that change tensor geometry through this hook are
responsible for whatever bookkeeping autograd requires.
Returns the tensor data as a contiguous bytes blob, in row-major order.
GPU tensors are downloaded to a temporary CPU buffer first. No NumPy
dependency, by design — this path is what the serialization layer and
__repr__ use to avoid pulling NumPy into import lucid.
Returns
py::bytesRow-major bytes blob of length nbytes().
Renders the tensor data as a human-readable string suitable for __repr__.
The format roughly mirrors NumPy's array2string defaults but is
implemented entirely engine-side so neither Lucid nor its consumers
need NumPy.
Parameters
precisionint= None4.thresholdstd::size_t= Nonenumel > threshold, an edge-summary is rendered instead of the full data. Defaults to 1000.edgeitemsstd::size_t= None3.Returns
std::stringHuman-readable rendering of the tensor.
Converts the tensor to a nested Python list (or a Python scalar for 0-d tensors).
NumPy-free — mirrors item's dtype dispatch but walks the full
shape. Handles every supported dtype including Dtype::C64
(yields Python complex), so callers never need to fall back to
the NumPy bridge. GPU tensors are downloaded to a temporary
CpuStorage via the to_bytes snapshot path; the heavy
lifting then runs against a contiguous row-major byte buffer.
Returns
py::objectNested list of Python scalars, or a single scalar when the tensor is 0-d.
int transfer_to_device(Device target, bool requires_grad)Cross-device copy without round-tripping through Python or NumPy.
Dispatch summary:
- CPU → GPU: contiguous-snapshot the source view, then
gpu::upload_cpu_to_gpuinto a fresh GPU-private MLX array.- GPU → CPU:
gpu::download_gpu_to_cpuinto a freshCpuStorage. SharedStorageis handled by the separatetransfer_storageAPI (zero-copy relabel); calling this method on aSharedStoragetensor falls back to a contiguous CPU snapshot path.
- GPU → CPU:
Parameters
targetDevicerequires_gradboolReturns
std::shared_ptr<TensorImpl>New tensor on target with the same shape and dtype.
Returns the autograd version counter.
Bumped by bump_version on every in-place mutation; autograd
snapshots this at forward time and checks it at backward time to
catch illegal mutations.
Returns
std::int64_tCurrent version. 0 when no AutogradMeta exists.
Clears the accumulated gradient.
Sets grad to std::nullopt but leaves the
AutogradMeta itself in place so subsequent backward passes
continue to accumulate.