latest (dev)
Copy
Latest development documentation · Updated 2026-10-08
Meta device
The “meta” device is an abstract device whose tensors record only metadata — shape, dtype, strides — and no data. Meta tensors answer “what would the result look like” without spending compute or memory on values, which makes them the tool for abstract analysis: tracing a model’s shapes end to end, checking where dtypes change, or sizing activations before allocating anything.
What works on meta
All the factory functions accept device="meta", and so does the
tensorplay.device() context manager, which redirects construction calls that do
not name a device:
import tensorplay as tp
m = tp.zeros(3, 4, device="meta")
print(m.shape, m.numel(), m.is_meta) # (3, 4) 12 True
with tp.device("meta"):
t = tp.randn(30, 30) # factories without a device= land on meta
print(t.device) # meta
Most shape-only operations run on meta tensors and produce new meta tensors carrying the resulting metadata:
with tp.device("meta"):
x = tp.randn(8, 4)
w = tp.randn(2, 4)
print((x + 1).shape) # (8, 4)
print((x @ w.T).shape) # (8, 2)
print(x.sum(dim=0).shape) # (4,)
Coverage is not total: some operations whose results are shape-only in principle still
lack a meta kernel in this build — softmax, stack, unsqueeze, chunk among them —
and raise the same NotImplementedError as the data-dependent ones below. When a trace
hits such a gap, rewrite the step in terms of the primitives that do work (slicing and
reshape cover most reshaping needs).
tensorplay.zeros_like / empty_like on a meta tensor stay on meta, so a shape-tracing
pass can build its intermediates the way real code would.
What does not
A meta tensor has no data, so anything that must read a value fails with
NotImplementedError (“Kernel not found for op: … on backend: Meta”):
data-dependent shapes:
nonzero,item,masked_select-style operations;.to("cpu")on a meta tensor — copying out would require data to copy (the copy kernel reports it only supports CPU/CUDA-style sources). Useempty_like(t, device="cpu")and fill it yourself instead;tp.load(..., map_location="meta")— the deserializer only supports thecpuand CUDA map targets and refusesmetaoutright.
Module construction under a meta context hits the same wall in this build: layers
initialize their parameters by drawing random numbers (uniform_ and friends), and
those kernels have no meta implementation. Build the module on a real device and reason
about shapes with meta tensors rather than moving whole modules to meta.
Idioms
Shape-checking a sequence of operations before running them for real:
def infer_shapes(seq_len, d_model, n_heads):
with tp.device("meta"):
x = tp.randn(seq_len, d_model)
qkv = x @ tp.randn(d_model, 3 * d_model)
head_dim = d_model // n_heads
q = qkv[:, :d_model].reshape(seq_len, n_heads, head_dim)
k = qkv[:, d_model:2 * d_model].reshape(seq_len, n_heads, head_dim)
return q.transpose(0, 1).shape, k.transpose(0, 1).shape
print(infer_shapes(128, 512, 8))
# (tensorplay.Size([8, 128, 64]), tensorplay.Size([8, 128, 64]))
The compiler and the fake-tensor machinery behind shape specialization use exactly this idea — metadata-only execution — to reason about programs without running them.
Help improve this page
Found an error, an unclear step, or a missing example?

