Tensor dimensions, layout and storage
TensorSpec combines logical Shape, scalar DType and physical Layout.
The layout is not encoded into the dtype. BF16 and FP16 remain distinct types.
Six-dimensional capacity, not six mandatory axes
Shape stores up to six dimensions and their logical length. Shape{2, 3} is
rank two; it is not silently converted to {1, 1, 1, 1, 2, 3}. Shape{} is a
scalar with one element. Empty tensors have an admitted zero extent. Negative
extents and more than six dimensions are invalid. Constructing a shape with more
than six dimensions throws std::length_error; negative extents are rejected by
operation validation. Individual operations impose
additional rank and empty-tensor restrictions.
Physical representations
- Contiguous: dense logical order, last axis fastest.
- Strided: explicit byte strides for logical axes. Admission is operation specific. Cast accepts aligned, nonnegative, conservatively proven nonoverlapping views on either port; zero stride is allowed only on singleton/empty axes. CastTess accepts the same admitted strided input rules, with explicit MLA-tiled output. Argmax accepts aligned positive nonoverlapping input views. These contracts reject broadcast, negative-stride and overlapping views.
- MlaTiled: explicit D/H/W/C axis mapping and tile shape. Unmapped axes are outer dimensions. Tiles are visited C/D/H/W and contain D/H/W/C pixels. BF16 stores low channel bytes followed by high channel bytes per pixel. Channel padding and per-tile alignment affect capacity.
- MlaCBlocked: trailing H/W/C with C16-block/H/W/lane storage inside tiles.
BF16 stores low/high sixteen-byte stripes. Each channel slice is padded
independently. This is not the same byte arrangement as
MlaTiled.
Use the definitions in include/simaai/kernels/tensor.h and the selected
operation's admission rules. A shape or layout supported by one kernel is not
necessarily supported by another.
Requirements and bindings
For contiguous tensors the basic payload is element count × scalar width, with checked arithmetic. Strides, padding, tile rounding and byte planes can require more bytes. Allocate from the prepared plan's storage/alignment requirements.
Bindings contain an address and its available capacity. Capacity is checked on every call and may exceed the minimum. It must describe actual accessible storage. Nonempty buffers require non-null, sufficiently aligned addresses; address overflow and disallowed overlap are rejected before output writes.
Zero-capacity no-ops are admitted only where the operation permits empty tensors. Do not reinterpret format descriptors without rearranging data, and do not include poisoned/padded elements in a logical reduction.
Cast: payload, span and capacity
For a nonempty admitted strided view, the required physical span is scalar width
plus the sum of (extent - 1) * stride_bytes over logical axes. All arithmetic
is checked. Scalars need one scalar; empty tensors need zero bytes, without
multiplying irrelevant large dimensions. Unused stride slots do not address data.
For shape {2, 3}, FP32 strides {16, 4} require a 28-byte span but contain
24 bytes of logical values. BF16 output strides {8, 2} require 14 bytes but
write only 12 bytes. The row gap is not output padding to initialize: Cast leaves
it unchanged. Binding capacity may exceed either span, and remains a fact supplied
by the actual allocation view, not something the shape proves.
The non-overlap admission sorts nonsingleton axes by stride and requires each
stride to cover the already accumulated span. This is sufficient, not exhaustive:
FP32 shape {3, 2} with strides {8, 12} is an example of an injective view this
rule rejects. Cross-port bounding-span intersection is also rejected, even when
interleaved touched elements would be disjoint.