Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6cb7859
feat: add dtype objects, backend properties, and dtype registration
nicola-bastianello Jun 12, 2026
9823697
docs: added discussion of supported dtypes
nicola-bastianello Jun 12, 2026
2767bad
Merge remote-tracking branch 'origin/main' into dtypes
nicola-bastianello Jun 12, 2026
1c18f8a
enh: next iteration of dtypes
nicola-bastianello Jun 14, 2026
b987a82
ref: change enums names
nicola-bastianello Jun 15, 2026
b963ff8
enh: change default dtype names
nicola-bastianello Jun 15, 2026
41aed3b
enh: first working version
nicola-bastianello Jun 17, 2026
86212da
enh: improvements
nicola-bastianello Jun 17, 2026
249993f
enh: added filtering in dtypes function
nicola-bastianello Jun 17, 2026
cf6c260
enh: user guide
nicola-bastianello Jun 17, 2026
8478f20
enh: added constants
nicola-bastianello Jun 18, 2026
9c189d1
fix: tests and sphinx
nicola-bastianello Jun 18, 2026
3d02be1
enh: add test for dtypes
nicola-bastianello Jun 18, 2026
f40712f
enh: add backend name and utils submodule
nicola-bastianello Jun 18, 2026
4066770
enh: dtypes docstring
nicola-bastianello Jun 18, 2026
5f68671
fix: remove ruff preview rules
nicola-bastianello Jun 19, 2026
b67c251
fix: address PR comments
nicola-bastianello Jul 1, 2026
3d1f1f1
fix: added version control
nicola-bastianello Jul 1, 2026
e259113
fix: properly lock versions, improve pyproject.toml, fix numpy-relate…
nicola-bastianello Jul 6, 2026
2417bc7
enh: add item to array, base coercion dunders on item
nicola-bastianello Jul 6, 2026
8542728
enh: unify error messaging
nicola-bastianello Jul 6, 2026
9a84679
enh: first steps for cupy support
nicola-bastianello Jul 6, 2026
2ab147c
enh: full cupy backend definition
nicola-bastianello Jul 9, 2026
a9644bc
fix: small fixes
nicola-bastianello Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
# version update of dependencies (as specified in pyproject.toml)
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"

# version update of github action tools (as specified in workflows/ci.yaml)
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
65 changes: 65 additions & 0 deletions decent_array/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,73 @@
from decent_array import interoperability, types
from decent_array._array import Array
from decent_array._constants import e, inf, nan, pi
from decent_array.types._dtypes import (
bfloat16,
bool_,
bytes_,
complex64,
complex128,
complex256,
float16,
float32,
float64,
float128,
int8,
int16,
int32,
int64,
object_,
qint8,
qint16,
qint32,
quint8,
quint16,
uint8,
uint16,
uint32,
uint64,
unicode_,
void,
)

__all_docs__ = [
"Array",
"interoperability",
"types",
]

__all__ = [
"Array",
"bfloat16",
"bool_",
"bytes_",
"complex64",
"complex128",
"complex256",
"e",
"float16",
"float32",
"float64",
"float128",
"inf",
"int8",
"int16",
"int32",
"int64",
"interoperability",
"nan",
"object_",
"pi",
"qint8",
"qint16",
"qint32",
"quint8",
"quint16",
"types",
"uint8",
"uint16",
"uint32",
"uint64",
"unicode_",
"void",
]
47 changes: 24 additions & 23 deletions decent_array/_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
from typing import TYPE_CHECKING, Any, Self

from decent_array.interoperability._backend_manager import register_backend_listener
from decent_array.types import _STRING_TO_DTYPE
from decent_array.types._dtypes import _BACKEND_DTYPE_TO_DTYPE

if TYPE_CHECKING:
from numpy.typing import NDArray

from decent_array.interoperability._abstracts import Backend
from decent_array.types import ArrayKey, DTypes, SupportedArrayTypes, SupportedDevices
from decent_array.types import ArrayKey, ArrayTypes, Devices, dtype


_BACKEND_INSTANCE: Backend | None = None
Expand All @@ -49,7 +49,7 @@ def _update_backend(backend: Backend | None) -> None:
register_backend_listener(_update_backend)


class Array: # noqa: PLR0904
class Array:
"""
Wrapper around a single backend-native array.

Expand All @@ -60,7 +60,7 @@ class Array: # noqa: PLR0904

__slots__ = ("_backend", "value")

def __init__(self, value: SupportedArrayTypes) -> None:
def __init__(self, value: ArrayTypes) -> None:
"""
Wrap ``value`` in an :class:`Array`.

Expand Down Expand Up @@ -355,23 +355,23 @@ def __len__(self) -> int:

def __float__(self) -> float:
"""Coerce a scalar array to a Python float."""
return float(self._backend.squeeze(self).value)
return float(self.item())

def __bool__(self) -> bool:
"""Coerce a scalar array to a Python bool."""
return bool(self._backend.squeeze(self).value)
return bool(self.item())

def __int__(self) -> int:
"""Coerce a scalar array to a Python int."""
return int(self._backend.squeeze(self).value)
return int(self.item())

def __complex__(self) -> complex:
"""Coerce a scalar array to a Python complex."""
return complex(self._backend.squeeze(self).value)
return complex(self.item())

def __index__(self) -> int:
"""Coerce a scalar array to a Python int."""
return int(self._backend.squeeze(self).value)
return int(self.item())

# Repr -----------------------------------------------------------------

Expand Down Expand Up @@ -401,21 +401,12 @@ def ndim(self) -> int:
return self._backend.ndim(self)

@property
def dtype(self) -> DTypes:
"""
Return dtype of the Array as item of DTypes enum.

Raises:
ValueError: for dtypes that are not supported by all decent-array functions
def dtype(self) -> dtype:
"""Return dtype of the Array."""
dtype = _BACKEND_DTYPE_TO_DTYPE.get(self.value.dtype)

"""
# get framework-native dtype as string
# split takes care of types with names like "torch.float32"
dtype_name = str(self.value.dtype).split(".")[-1]

dtype = _STRING_TO_DTYPE.get(dtype_name)
if dtype is None:
raise ValueError(f"dtype {self.value.dtype} is not supported by all decent-array functions.")
raise ValueError(f"dtype {self.value.dtype} is not supported.")

return dtype

Expand Down Expand Up @@ -445,11 +436,21 @@ def all(self) -> bool:
return self._backend.all(self)

@property
def device(self) -> SupportedDevices:
def device(self) -> Devices:
"""Return the device of the array."""
return self._backend.device_of(self)

@property
def numpy(self) -> NDArray[Any]:
"""Return a NumPy array view of the array's data."""
return self._backend.to_numpy(self)

def item(self) -> Any: # noqa: ANN401
"""
Convert 0-dim array to Python scalar.

Raises:
TypeError: if ``x`` is not 0-dimensional.

"""
return self._backend.to_scalar(self)
10 changes: 10 additions & 0 deletions decent_array/_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Numerical constants."""

import math

_CONSTANTS = ["e", "inf", "nan", "pi"]

e = math.e
inf = math.inf
nan = math.nan
pi = math.pi
37 changes: 37 additions & 0 deletions decent_array/_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Common errors used across backends."""

from typing import Any

from mypy_extensions import mypyc_attr


@mypyc_attr(native_class=False)
class NotScalarError(TypeError):
def __init__(self, ndim: int):
super().__init__(f"Only 0-dim arrays can be converted to Python scalars, got {ndim}-dim array.")


class NDimError(ValueError):
def __init__(self, required_ndim: int, actual_ndim: int):
super().__init__(f"A {required_ndim}-dim array is required, got {actual_ndim}-dim array.")


class MatrixTransposeError(ValueError):
def __init__(self, ndim: int):
super().__init__(f"An aray with at least 2 dimensions is required, got {ndim}-dim array.")


class UnsupportedDTypeCreationError(ValueError):
def __init__(self, dtype: Any, backend_name: str, device_name: str): # noqa: ANN401
super().__init__(f"Unsupported dtype '{dtype}' for {backend_name} on {device_name}.")


class UnsupportedDeviceError(ValueError):
def __init__(self, backend_name: str, device_name: str):
super().__init__(f"{backend_name} does not support device '{device_name}'.")


stack_empty_error = ValueError("Cannot stack an empty sequence of arrays.")


no_backend_error = RuntimeError("No backend active: call 'set_backend' with a supported framework to activate one.")
19 changes: 19 additions & 0 deletions decent_array/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Any

from decent_array._array import Array


def unwrap(x: Any) -> Any: # noqa: ANN401
"""
Return the underlying value of an :class:`Array`, or pass ``x`` through.

Typed as ``Any`` because operator dunders may pass either an :class:`Array` or a
Python scalar; the strict abstract signature would force a ``cast`` at every call
site without runtime benefit.
"""
return x.value if type(x) is Array else x


def is_scalar(x: Array) -> bool:
"""Return True if ``x`` is a 0-dim Array."""
return x.ndim == 0
2 changes: 1 addition & 1 deletion decent_array/interoperability/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
bitwise_right_shift,
bitwise_xor,
)
from ._iop.comparasion import equal, greater, greater_equal, less, less_equal, not_equal
from ._iop.comparison import equal, greater, greater_equal, less, less_equal, not_equal
from ._iop.creation import eye, ones, ones_like, zeros, zeros_like
from ._iop.linalg import dot, matmul, norm, vecdot, vector_norm
from ._iop.manipulations import (
Expand Down
Loading
Loading