hurray-python: Dtype, Device, and Tensor

This entry covers three foundational Python types:

  • hurray.Dtype — element type descriptor class + hurray.dtype submodule
  • hurray.Device — device descriptor class + hurray.device submodule
  • hurray.Tensor — tensor constructor and core properties

Dtype system

Tier 1 vs Tier 2

The Hurray element type system is split into two tiers, matching the spec:

TierCriterionPython location
1Array API-compatiblehurray.<name> and hurray.dtype.<name>
2Extended / sub-bytehurray.dtype.<name> only

Tier 1 types (accessible at the top level):

NameBit widthKind
bool1boolean
int8, uint88integer
int16, uint1616integer
int32, uint3232integer
int64, uint6464integer
float16, bfloat1616float
float3232float
float6464float

Tier 2 types (only on hurray.dtype.*):

NameBit widthKind
int4, uint44integer
int2, uint22integer
float8_e4m3, float8_e5m2, float8_e8m08float
float4_e2m14float
float6_e2m3, float6_e3m26float
float128128float
complex64, complex12864 / 128complex

Singleton identity

Tier 1 dtype constants are the same Python object on the top-level module and on hurray.dtype:

import hurray

assert hurray.float32 is hurray.dtype.float32   # same object, not just ==
assert hurray.int8 is hurray.dtype.int8

This ensures that set membership and dict key lookups work correctly regardless of which path is used to import the constant.

Using Dtype as a dict key

Dtype is frozen (immutable) and implements __hash__ via the wire tag byte:

import hurray

lookup = {hurray.float32: "fp32 weights", hurray.dtype.int4: "quantized"}
assert lookup[hurray.float32] == "fp32 weights"
assert lookup[hurray.dtype.int4] == "quantized"

from_name round-trip

import hurray

d = hurray.Dtype.from_name("float32")
assert d == hurray.float32
assert d.name == "float32"
assert d.bit_width == 32
assert d.is_float
assert d.is_array_api

# Unknown name raises hurray.InvalidDescriptorError
try:
    hurray.Dtype.from_name("not_a_type")
except hurray.InvalidDescriptorError as e:
    print(f"rejected: {e}")

Submodule import

The hurray.dtype submodule is registered in sys.modules, so the following import forms all work:

import hurray.dtype
from hurray.dtype import int4, float8_e4m3

Device system

A Device is a (kind, device_id, memory_class) triple. It is frozen and hashable.

Well-known constants

hurray.device exposes a constant for each supported device kind, all at device_id=0 and memory_class="standard":

import hurray

hurray.device.cpu         # CPU host memory
hurray.device.cuda        # CUDA device 0
hurray.device.rocm        # ROCm device 0
hurray.device.metal       # Metal (Apple Silicon)
hurray.device.vulkan
hurray.device.webgpu
hurray.device.hexagon
hurray.device.level_zero
hurray.device.opencl

Constructor

import hurray

# Default: device_id=0, memory_class="standard"
cpu = hurray.Device("cpu")

# Specific GPU
gpu1 = hurray.Device("cuda", 1)

# Unified memory (hardware-coherent CPU+GPU access)
gpu_um = hurray.Device("cuda", 0, "unified")

# Pinned host memory (CPU-accessible, device-mapped)
pinned = hurray.Device("cuda", 0, "host_pinned")

Equality and hashing

import hurray

assert hurray.Device("cpu") == hurray.device.cpu      # same triple
assert hurray.Device("cuda", 0) != hurray.Device("cuda", 1)  # different id

# Usable as dict key
device_names = {hurray.device.cpu: "host", hurray.device.cuda: "gpu0"}
assert device_names[hurray.Device("cpu")] == "host"

Submodule import

import hurray.device
from hurray.device import cpu, cuda

Constructing a Tensor

import struct
import hurray

# Pack six float32 values as raw bytes (little-endian)
buf = struct.pack("6f", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)

# Construct a 2×3 float32 tensor on the CPU
t = hurray.Tensor(buf, hurray.float32, [2, 3])

assert t.shape == (2, 3)    # tuple[int | None, ...]
assert t.ndim == 2
assert t.size == 6          # total element count (None if any dim is dynamic)
assert t.dtype == hurray.float32
assert t.device == hurray.device.cpu

Explicit device

import hurray, struct

buf = struct.pack("4f", 1.0, 2.0, 3.0, 4.0)
t_gpu = hurray.Tensor(buf, hurray.float32, [4], hurray.Device("cuda", 0))
assert t_gpu.device.kind == "cuda"

Sub-byte types

Buffer sizes for sub-byte types are computed with ceiling division over the bit width. For int4, two elements pack into one byte:

import hurray

buf = bytes([0xAB, 0xCD])           # 2 bytes = 4 nibbles = 4 int4 elements
t = hurray.Tensor(buf, hurray.dtype.int4, [4])
assert t.size == 4
assert t.dtype.is_sub_byte

Dynamic dimensions

If the tensor shape contains a DYNAMIC dimension (wire value u64::MAX), that dimension maps to None in the Python shape tuple and size returns None:

# shape tuple with a dynamic dim:  (1, None, 768)
# t.size == None

Running the examples

Build the wheel with maturin, then run:

cd hurray-python
maturin develop           # build + install in-place (requires a venv)

python examples/dtype.py
python examples/device.py
python examples/tensor.py

All three examples print a confirmation line and exit with code 0 on success.

Spec references

  • docs/spec/element-types.md — Tier 1 / Tier 2 partition and type properties
  • docs/spec/buffer-protocol.md — DeviceTag and MemoryClass wire tables
  • docs/spec/data-model.md — DYNAMIC dimension sentinel (u64::MAX)
  • docs/impl/python-bindings.md — Python binding implementation guide