Array annotations#
An annotation names the array you expect: its dtype, its shape, and on PyTorch its device.
from typing import Literal as Shape
import numpy as np
from tenspec import validate
from tenspec.numpy import Float
print(validate(np.ones((3, 4)), Float[Shape["rows features"]]).shape)
Float is the dtype. Shape["rows features"] is the shape, with one name per axis. A
declaration takes the shape first, then any transforms, then any properties:
Float[Shape["rows features"], Finite]. Custom checks and
transforms covers those operations, and Runtime
validation covers the boundaries that run the check.
The names in a shape relate the axes of different arrays. That is what a declaration adds over an ordinary array type, and it holds only inside one boundary.
Shape#
A shape is a string of whitespace-separated axis tokens.
Spelling |
Meaning |
|---|---|
|
two axes that bind the names |
|
an axis of exactly four |
|
one axis of any extent. It binds nothing |
|
any leading axes, bound together as |
|
any leading axes, bound to nothing |
|
an extent of one passes and binds nothing. Any other extent binds |
|
an axis computed from dimensions that are bound already |
|
a scalar, which is an array of rank zero |
A shape declares at most one variadic group. A named group binds the whole tuple of extents it
covered, so axis_size cannot read it as one number. A shape expression evaluates; it never
solves. 2*width therefore needs width bound by an earlier axis or an earlier value in the
same boundary. Its grammar accepts whole numbers, dimension names, +, -, * and
parentheses, and Tenspec evaluates that bounded tree itself. It calls no Python eval, and it
runs no other construct.
Dtype#
Each backend module carries the same alias names. A family alias accepts every format of its family. An exact alias accepts one format. Neither is a cast: a declaration accepts or refuses the array it received.
Family alias |
Accepts |
|---|---|
|
float16, float32, float64, and bfloat16 on Torch |
|
int8, int16, int32, int64 |
|
uint8, uint16, uint32, uint64 |
|
complex64, complex128 |
|
bool |
Exact alias |
Backend |
|---|---|
|
both |
|
Torch only, because NumPy represents no bfloat16 |
|
both |
|
both |
|
both |
A backend representing a format is not the same as your installed NumPy or Torch being able to compute with it. Tenspec answers the first question alone.
The generic NumPy declaration is NDArray, so it permits a supported ndarray subclass. An
exact concrete class is not a built-in requirement. A property of your own can express one,
and that stays your class.
Device#
Only a backend that places arrays takes a device requirement. PyTorch does. NumPy does not, and
it reports no placement rather than reporting the CPU. tenspec.torch names CPU, CUDA and
MPS. A device kind compares the kind alone, so CUDA accepts any CUDA device.