Python · Tensors and Views

Tensors and Views

import numkongfrom numkong import Tensor, ScaledTensor 8.0.0-rc.1 · b1f866ab

Tensor is a memoryview-backed object with NumPy-like metadata. It is the central container for strided views, transpose, reshape, flatten, and axis reductions.

import numpy as np
import numkong as nk

t = nk.Tensor(np.arange(12, dtype=np.float32).reshape(3, 4))

print(t.shape, t.dtype, t.ndim, t.strides, t.itemsize, t.nbytes)
print(np.asarray(t))      # zero-copy array view when layout allows it
print(t.T.shape)          # transposed Tensor view
print(t.reshape(2, 6).shape)
print(t.flatten().shape)

# Slicing — row, column, and scalar access
row0 = t[0, :]            # first row, shape (4,)
col2 = t[:, 2]            # third column, strided view, shape (3,)
val  = t[1, 2]            # scalar element access → 6.0

# Reductions compose with sliced views
idx = col2.argmin()        # index of the minimum in the third column
mn, i0, mx, i1 = col2.minmax()

The important layout rules are:

  • Tensor preserves shape and byte strides.
  • Transpose and slicing can produce non-contiguous views.
  • General reductions accept those views.
  • Matrix-style packed kernels require row-contiguous left operands.
  • Packed and symmetric outputs require C-contiguous out buffers.

§ Memory Layout Requirements

API familyInput requirementOutput requirement
Dense distances (dot, euclidean, etc.)Rows must be contiguous (strides[last] <= itemsize). Strided rows (sliced columns) are rejected.out= can have any stride along dim 0, but inner dim must be contiguous.
cdistSame as dense distancesout= must be rank-2 with shape (a.count, b.count)
Elementwise (scale, blend, fma)Arbitrary strides (strided views are supported)out= must match input shape; strides are preserved
Packed matrix (dots_packed)Left operand: rank-2, contiguous rows, no negative stridesOutput: C-contiguous with expected dtype
Symmetric (dots_symmetric)Contiguous rowsout=: C-contiguous square matrix
Tensor reductions (sum, min, argmin, etc.)Arbitrary strides (strided views supported)N/A (returns scalar or reduced tensor)

§ GPU Tensors through DLPack

Builds configured with -C cmake.define.NUMKONG_CUDA_ARCH=all or -C cmake.define.NUMKONG_ROCM_ARCH=all also carry the CUDA or ROCm kernels. nk.cuda_count_devices() counts the devices this process sees, zero without the runtime or its kernels, and nk.cuda_capabilities_enabled(0) reports the mask device 0 dispatches with, raising ValueError past the last. nk.cuda_capabilities_detected and nk.cuda_capabilities_compiled answer as their CPU twins do, nk.rocm_* and nk.metal_* do the same for the other vendors, and cpu_configure_thread is CPU only. nk.from_dlpack imports a CUDA or ROCm tensor without a copy and records its device. Only dots_pack, the dots, angulars and euclideans _packed and _symmetric functions, and attention_pack, attention_packed and attention_packed_gradients run on it, with that device’s enabled capabilities. The shape functions also take it for their outputs, running the mask their packs were copied with. Element access, NumPy conversion, cdist, maxsim and every other CPU kernel raise BufferError rather than read device memory. No verb allocates, so every GPU call writes into an out= its device reaches. The dots and attention families take that as a Tensor on the same device, and mixing devices raises ValueError. The shape functions take any memory the device reaches, a Tensor or a buffer over managed or pinned memory, which the runtime checks per call, raising BufferError for the rest. Unified memory comes only from an explicit copy, as in nk.shapes_pack(clouds).copy(capabilities=gpu) and nk.zeros(shape).copy(capabilities=gpu), which frees with the tensor and its views and refuses resize() and reserve(). A GPU call queues one launch on stream=, an integer handle defaulting to 0, the legacy stream, and returns before it finishes, as PyTorch does, and nk.synchronize(capabilities, stream=stream) waits for it. nk.stream_init_cuda(ordinal) makes such a handle on any device by its ordinal, and nk.stream_free_cuda(stream) frees it once synchronized, with rocm_ and metal_ twins. Metal tensors are host-readable and keep running on the CPU kernels, except the shape functions, which run on Metal over blocks of its allocator.

import torch
import numkong as nk

left = torch.randn(4096, 768, device="cuda", dtype=torch.bfloat16)
right = torch.randn(8192, 768, device="cuda", dtype=torch.bfloat16)
capabilities = nk.cuda_capabilities_enabled(right.device.index)
packed_storage = torch.empty(nk.PackedMatrix.pack_size(8192, 768, "bf16", capabilities=capabilities),
                             device="cuda", dtype=torch.uint8)
result = torch.empty(4096, 8192, device="cuda", dtype=torch.float32)

stream = torch.cuda.current_stream().cuda_stream
packed = nk.dots_pack(nk.from_dlpack(right), out=nk.from_dlpack(packed_storage), stream=stream)
nk.dots_packed(nk.from_dlpack(left), packed, out=nk.from_dlpack(result), stream=stream)
nk.synchronize(capabilities, stream=stream)

API

ClassTensorN-dimensional tensor type returned by NumKong operations.44
__init__.pyi:220 ↗
class Tensor(memoryview)

Supports NumPy-like properties and buffer protocol for interoperability. Packed dtypes 'uint1', 'int4', 'uint4', and 'e2m1' count logical dimensions in shape; the last dimension must be a multiple of the values per byte, and the buffer protocol, __array_interface__, nbytes, and DLPack expose whole bytes.

TensorConstruct a Tensor by copying data from a buffer-protocol object; packed dtypes read its bytes.
__init__.pyi:229 ↗
Tensor(
    array_like: _BufferType,
    /,
    *,
    dtype: str | _MiniFloatType | None = None,
)
__len__Return the length of the first dimension.
__init__.pyi:268 ↗
def __len__() -> int
__iter__Iterate over the first dimension.
__init__.pyi:272 ↗
def __iter__() -> Any
__getitem__Get an element or sub-tensor by index or slice.
__init__.pyi:276 ↗
def __getitem__(
    key: int | slice | tuple[int | slice, ...],
) -> float | Tensor
__repr__*__repr__× 2Return a string representation.
__init__.pyi:280 ↗
def __repr____repr__() -> str
__*__× 2Convert 0D tensor to Python float.
__init__.pyi:284 ↗
def __float__() -> float
Symbol
__float__
__int__
__str__Return a pretty-printed string representation.
__init__.pyi:292 ↗
def __str__() -> str
__eq__Compare tensors for equality.
__init__.pyi:296 ↗
def __eq__(other: Any) -> bool
__ne__Compare tensors for inequality.
__init__.pyi:300 ↗
def __ne__(other: Any) -> bool
__add__Element-wise sum with another same-shape Tensor, or a broadcast scalar.
__init__.pyi:304 ↗
def __add__(other: Tensor | float | int) -> Tensor
__sub__Element-wise difference with another same-shape Tensor, or a broadcast scalar.
__init__.pyi:308 ↗
def __sub__(other: Tensor | float | int) -> Tensor
__mul__Element-wise product with another same-shape Tensor, or a broadcast scalar.
__init__.pyi:312 ↗
def __mul__(other: Tensor | float | int) -> Tensor
__matmul__Dot-product matrix multiplication against a pre-packed matrix; see dots_packed.
__init__.pyi:316 ↗
def __matmul__(other: PackedMatrix) -> Tensor
__neg__Return the element-wise negation.
__init__.pyi:320 ↗
def __neg__() -> Tensor
__pos__Return a copy of the tensor.
__init__.pyi:324 ↗
def __pos__() -> Tensor
__dlpack__Return a DLPack capsule for zero-copy exchange with PyTorch, JAX, NumPy, etc.
__init__.pyi:333 ↗
def __dlpack__(
    *,
    stream: Any | None = None,
    max_version: tuple[int, int] | None = None,
    dl_device: tuple[int, int] | None = None,
    copy: bool | None = None,
) -> Any
__dlpack_device__Return the DLPack device tuple.
__init__.pyi:344 ↗
def __dlpack_device__() -> tuple[int, int]

Always (1, 0) (kDLCPU).

__array__Convert to a NumPy array, sharing data when possible.
__init__.pyi:348 ↗
def __array__(
    dtype: str | None = None,
    *,
    copy: bool | None = None,
) -> Any
copyReturn a deep copy of the tensor, or write it into out and return out.
__init__.pyi:357 ↗
def copy(
    *,
    out: Tensor | None = None,
    capabilities: Capability | int | None = None,
    stream: int | None = None,
) -> Tensor
reshapeReturn tensor reshaped to given dimensions.
__init__.pyi:363 ↗
def reshape(*shape: int = ()) -> Tensor
flattenReturn a flattened 1D view, or a copy if non-contiguous or out is given.
__init__.pyi:367 ↗
def flatten(*, out: Tensor | None = None) -> Tensor
squeezeRemove the given size-1 dimensions, or every size-1 dimension when axes is empty.
__init__.pyi:371 ↗
def squeeze(*axes: int = ()) -> Tensor
astypeReturn a copy cast to the given dtype, quantizing to a ScaledTensor for a block-scaled target.
__init__.pyi:375 ↗
def astype(
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType | _BlockScaledTypeName,
    /,
    *,
    out: Tensor | None = None,
) -> Tensor | ScaledTensor
resizeReshape in place within capacity without moving storage; returns self.
__init__.pyi:400 ↗
def resize(*shape: int = ()) -> Tensor
reserveGrow the allocated capacity to at least capacity elements (may move); preserves contents.
__init__.pyi:404 ↗
def reserve(capacity: int) -> None
clearReset to an empty shape (size 0) while keeping the allocated capacity.
__init__.pyi:408 ↗
def clear() -> None
sumReturn the sum of all elements.
__init__.pyi:412 ↗
def sum(
    axis: int | tuple[int, ...] | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> float | int | Tensor

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
normReturn the L2 norm.
__init__.pyi:423 ↗
def norm(
    axis: int | tuple[int, ...] | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> float | Tensor

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
minReturn the minimum element, or None if all elements are NaN.
__init__.pyi:434 ↗
def min(
    axis: int | tuple[int, ...] | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> float | int | Tensor | None

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
maxReturn the maximum element, or None if all elements are NaN.
__init__.pyi:445 ↗
def max(
    axis: int | tuple[int, ...] | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> float | int | Tensor | None

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
argminReturn the index of the minimum element, or None if all elements are NaN.
__init__.pyi:456 ↗
def argmin(
    axis: int | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> int | Tensor | None

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
argmaxReturn the index of the maximum element, or None if all elements are NaN.
__init__.pyi:467 ↗
def argmax(
    axis: int | None = None,
    *,
    keepdims: bool = False,
    out: Tensor | None = None,
    capabilities: Capability | int | None = ...,
    stream: int | None = ...,
) -> int | Tensor | None

Parameters

capabilitiesCapability | int | NoneCapabilities to run within: every CPU one by default, or for packed operands the mask that packed them.
streamint | NoneA GPU stream pointer, left None on the CPU.
shapeShape of the tensor as a tuple of logical dimensions.
__init__.pyi:234 ↗
shape: tuple[int, ...]
dtypeData type of the tensor elements (e.g., 'float64').
__init__.pyi:239 ↗
dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName
ndimNumber of dimensions.
__init__.pyi:244 ↗
ndim: int
sizeTotal number of elements.
__init__.pyi:249 ↗
size: int
nbytesTotal number of bytes.
__init__.pyi:254 ↗
nbytes: int
stridesStrides of the tensor in bytes.
__init__.pyi:259 ↗
strides: tuple[int, ...]
itemsizeSize of each element in bytes.
__init__.pyi:264 ↗
itemsize: int
__array_interface__NumPy array interface dict for legacy interoperability.
__init__.pyi:329 ↗
__array_interface__: dict[str, Any]
TTranspose of the tensor.
__init__.pyi:353 ↗
T: Tensor
is_contiguousWhether the tensor is C-contiguous in memory.
__init__.pyi:386 ↗
is_contiguous: bool
data_ptrInteger address of the underlying data buffer.
__init__.pyi:391 ↗
data_ptr: int
capacityAllocated element capacity of the owned buffer (>= size).
__init__.pyi:396 ↗
capacity: int
ClassScaledTensorBlock-scaled tensor: the OCP MX family plus NVIDIA NVFP4.10
__init__.pyi:478 ↗
class ScaledTensor

Holds packed elements, per-block scales, and an optional per-tensor scale. Produced by Tensor.astype('nvfp4'), Tensor.astype('mxfp4'), and similar block-scaled targets.

astypeMaterialize to a dense dtype, or transcode to another block-scaled dtype.
__init__.pyi:520 ↗
def astype(
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType | _BlockScaledTypeName,
    /,
) -> Tensor | ScaledTensor
resizeResize the packed elements and block scales in lockstep, within capacity; returns self.
__init__.pyi:528 ↗
def resize(*shape: int = ()) -> ScaledTensor
__getitem__Slice elements and block_scales in lockstep; the last axis must stay block-aligned.
__init__.pyi:532 ↗
def __getitem__(
    key: int | slice | tuple[int | slice, ...],
) -> ScaledTensor
elementsPacked sub-byte element Tensor, DLPack-exportable.
__init__.pyi:486 ↗
elements: Tensor
block_scalesPer-block scale Tensor, DLPack-exportable.
__init__.pyi:491 ↗
block_scales: Tensor
tensor_scalePer-tensor float multiplier, None for MX formats.
__init__.pyi:496 ↗
tensor_scale: float | None
block_sizeNumber of elements per block.
__init__.pyi:501 ↗
block_size: int
dtypeComposite block-scaled dtype name.
__init__.pyi:506 ↗
dtype: _BlockScaledTypeName
shapeLogical, dense shape of the quantized tensor.
__init__.pyi:511 ↗
shape: tuple[int, ...]
capacityAllocated element capacity of the packed elements buffer.
__init__.pyi:516 ↗
capacity: int

Functions

from_pointerCreate a zero-copy Tensor view over memory at a raw address.
__init__.pyi:1410 ↗
def from_pointer(
    address: int,
    shape: int | tuple[int, ...],
    dtype: _DTypeLike,
    /,
    *,
    strides: tuple[int, ...] | None = None,
    owner: Any = None,
) -> Tensor

Parameters

addressintInteger address of the data buffer.
shapeint | tuple[int, ...]Shape of the view, an int or a tuple of ints.
dtype_DTypeLikeElement dtype, like 'float32' or 'bf16'.
stridestuple[int, ...] | NoneByte strides, C-contiguous when None.
ownerAnyObject the view keeps alive, so the memory it owns is not collected.
from_dlpackZero-copy import a tensor from any DLPack-compatible producer.
__init__.pyi:1430 ↗
def from_dlpack(obj: Any, /) -> Tensor

Accepts a PyCapsule named 'dltensor' or 'dltensor_versioned', or any object with a __dlpack__ method (e.g. torch.Tensor, numpy.ndarray, jax.Array). Only CPU tensors are supported.

emptyCreate an uninitialized Tensor of the given shape and dtype.
__init__.pyi:1439 ↗
def empty(
    shape: int | tuple[int, ...],
    /,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
*× 2Create a Tensor of the given shape and dtype filled with zeros.
__init__.pyi:1448 ↗
def zeros(
    shape: int | tuple[int, ...],
    /,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
Symbol
zeros
ones
fullCreate a Tensor of the given shape and dtype filled with fill_value.
__init__.pyi:1466 ↗
def full(
    shape: int | tuple[int, ...],
    fill_value: int | float,
    /,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
iotaTensor filled with seed, seed+1, seed+2, ... reshaped to shape.
__init__.pyi:1476 ↗
def iota(
    shape: int | tuple[int, ...],
    /,
    seed: int | float = 0,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
diagonalSquare n x n tensor with seed on the main diagonal and zeros elsewhere.
__init__.pyi:1486 ↗
def diagonal(
    n: int,
    /,
    seed: int | float = 1,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
hashTensor filled with deterministic pseudo-random bits (shape, dtype, seed → reproducible bytes).
__init__.pyi:1496 ↗
def hash(
    shape: int | tuple[int, ...],
    /,
    seed: int = 0,
    *,
    dtype: Literal['uint1', 'int4', 'uint4', 'int64', 'int32', 'int16', 'int8', 'uint64', 'uint32', 'uint16', 'uint8'] | _FloatTypeName | _ComplexTypeName | _MiniFloatType = 'float32',
) -> Tensor
astypeCast an N-D buffer to dtype without first copying it into a Tensor.
__init__.pyi:1506 ↗
def astype(
    a: _BufferType,
    dtype: _DTypeLike,
    /,
    *,
    out: _BufferType | None = None,
) -> Tensor

A supplied out must be writable, C-contiguous, have the exact input shape and requested dtype, and must not overlap a. Returns out when provided, otherwise a new Tensor.