Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 14 additions & 2 deletions decent_array/interoperability/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
bitwise_xor,
)
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.creation import arange, empty, empty_like, eye, linspace, ones, ones_like, zeros, zeros_like
from ._iop.linalg import dot, eigvalsh, matmul, norm, solve, vecdot, vector_norm
from ._iop.manipulations import (
asarray,
astype,
Expand All @@ -49,17 +49,20 @@
absolute,
add,
divide,
exp,
floor_divide,
isfinite,
isinf,
isnan,
logaddexp,
multiply,
negative,
positive,
pow, # noqa: A004
remainder,
sqrt,
subtract,
where,
)
from ._iop.operators import argmax, argmin, maximum, sign
from ._iop.reductions import all, any, max, mean, min, sum # noqa: A004
Expand All @@ -84,6 +87,7 @@
"add",
"all",
"any",
"arange",
"argmax",
"argmin",
"asarray",
Expand All @@ -103,7 +107,11 @@
"diagonal",
"divide",
"dot",
"eigvalsh",
"empty",
"empty_like",
"equal",
"exp",
"expand_dims",
"eye",
"floor_divide",
Expand All @@ -119,6 +127,8 @@
"isnan",
"less",
"less_equal",
"linspace",
"logaddexp",
"matmul",
"matrix_transpose",
"max",
Expand All @@ -144,6 +154,7 @@
"shape",
"sign",
"size",
"solve",
"sqrt",
"squeeze",
"stack",
Expand All @@ -156,6 +167,7 @@
"unsqueeze",
"vecdot",
"vector_norm",
"where",
"zeros",
"zeros_like",
]
53 changes: 51 additions & 2 deletions decent_array/interoperability/_abstracts/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ def __init__(self, device: Devices = Devices.CPU, name: str = "") -> None:

# Array creation ------------------------------------------------------

@abstractmethod
def empty(self, shape: int | tuple[int, ...]) -> Array:
"""Create an uninitialized array with the given shape."""

@abstractmethod
def empty_like(self, x: Array, /) -> Array:
"""Create an uninitialized array with the same shape as ``x``."""

@abstractmethod
def zeros(self, shape: int | tuple[int, ...]) -> Array:
"""Create an array of zeros with the given shape."""
Expand All @@ -59,8 +67,24 @@ def ones_like(self, x: Array) -> Array:
"""Create an array of ones matching the shape and type of ``x``."""

@abstractmethod
def eye(self, n: int) -> Array:
"""Create an ``n x n`` identity matrix."""
def eye(self, n_rows: int, n_cols: int | None = None, /, *, k: int = 0) -> Array:
"""Create a two-dimensional array with ones on the diagonal at offset ``k``."""

@abstractmethod
def arange(self, start: int | float, /, stop: int | float | None = None, step: int | float = 1) -> Array:
"""Return evenly spaced values within a half-open interval."""

@abstractmethod
def linspace(
self,
start: int | float | complex,
stop: int | float | complex,
/,
num: int,
*,
endpoint: bool = True,
) -> Array:
"""Return evenly spaced numbers over a specified interval."""

@abstractmethod
def device_to_native(self, device: Devices) -> Any: # noqa: ANN401
Expand Down Expand Up @@ -186,6 +210,14 @@ def vector_norm(
) -> Array:
"""Compute the norm of ``x``."""

@abstractmethod
def solve(self, x1: Array, x2: Array) -> Array:
"""Solve the linear system ``x1 @ solution = x2``."""

@abstractmethod
def eigvalsh(self, x: Array) -> Array:
"""Return eigenvalues of a symmetric/Hermitian matrix."""

# Math reductions -----------------------------------------------------

@abstractmethod
Expand Down Expand Up @@ -284,6 +316,23 @@ def absolute(self, x: Array) -> Array:
def sqrt(self, x: Array) -> Array:
"""Element-wise square root."""

@abstractmethod
def exp(self, x: Array) -> Array:
"""Element-wise exponential."""

@abstractmethod
def logaddexp(self, x1: int | float | Array, x2: int | float | Array) -> Array:
"""Element-wise log(exp(x1) + exp(x2)) computed stably."""

@abstractmethod
def where(
self,
condition: Array,
x1: bool | int | float | complex | Array,
x2: bool | int | float | complex | Array,
) -> Array:
"""Select elements from ``x1`` or ``x2`` according to ``condition``."""

@abstractmethod
def isfinite(self, x: Array) -> Array:
"""Element-wise test for finite values."""
Expand Down
41 changes: 38 additions & 3 deletions decent_array/interoperability/_iop/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,43 @@ def ones_like(x: Array) -> Array:
return _BACKEND_INSTANCE.ones_like(x)


def eye(n: int) -> Array:
"""Create an ``n x n`` identity matrix."""
def empty(shape: int | tuple[int, ...]) -> Array:
"""Create an uninitialized array with the given shape."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.eye(n)
return _BACKEND_INSTANCE.empty(shape)


def empty_like(x: Array, /) -> Array:
"""Create an uninitialized array with the same shape as ``x``."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.empty_like(x)


def eye(n_rows: int, n_cols: int | None = None, /, *, k: int = 0) -> Array:
"""Create a two-dimensional array with ones on the diagonal at offset ``k``."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.eye(n_rows, n_cols, k=k)


def arange(start: int | float, /, stop: int | float | None = None, step: int | float = 1) -> Array:
"""Return evenly spaced values within a half-open interval."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.arange(start, stop, step)


def linspace(
start: int | float | complex,
stop: int | float | complex,
/,
num: int,
*,
endpoint: bool = True,
) -> Array:
"""Return evenly spaced numbers over a specified interval."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.linspace(start, stop, num, endpoint=endpoint)
14 changes: 14 additions & 0 deletions decent_array/interoperability/_iop/linalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,17 @@ def norm(
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.vector_norm(x, axis, keepdims, ord)


def solve(x1: Array, x2: Array) -> Array:
"""Solve the linear system ``x1 @ solution = x2``."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.solve(x1, x2)


def eigvalsh(x: Array) -> Array:
"""Return eigenvalues of a symmetric/Hermitian matrix."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.eigvalsh(x)
25 changes: 25 additions & 0 deletions decent_array/interoperability/_iop/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,31 @@ def sqrt(x: Array) -> Array:
return _BACKEND_INSTANCE.sqrt(x)


def exp(x: Array) -> Array:
"""Element-wise exponential."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.exp(x)


def logaddexp(x1: int | float | Array, x2: int | float | Array) -> Array:
"""Element-wise log(exp(x1) + exp(x2)) computed stably."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.logaddexp(x1, x2)


def where(
condition: Array,
x1: bool | int | float | complex | Array,
x2: bool | int | float | complex | Array,
) -> Array:
"""Select elements from ``x1`` or ``x2`` according to ``condition``."""
if _BACKEND_INSTANCE is None:
raise no_backend_error
return _BACKEND_INSTANCE.where(condition, x1, x2)


def isfinite(x: Array) -> Array:
"""Element-wise test for finite values."""
if _BACKEND_INSTANCE is None:
Expand Down
44 changes: 42 additions & 2 deletions decent_array/interoperability/_jax/jax_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ def __init__(self, device: Devices = Devices.CPU) -> None:

# Array creation

def empty(self, shape: int | tuple[int, ...]) -> Array:
return Array(jnp.empty(shape, device=self._native_device))

def empty_like(self, x: Array, /) -> Array:
return Array(jnp.empty_like(x.value, device=self._native_device))

def zeros(self, shape: int | tuple[int, ...]) -> Array:
return Array(jnp.zeros(shape, device=self._native_device))

Expand All @@ -58,8 +64,22 @@ def ones(self, shape: int | tuple[int, ...]) -> Array:
def ones_like(self, x: Array) -> Array:
return Array(jnp.ones_like(x.value))

def eye(self, n: int) -> Array:
return Array(jnp.eye(n, device=self._native_device))
def eye(self, n_rows: int, n_cols: int | None = None, /, *, k: int = 0) -> Array:
return Array(jnp.eye(n_rows, n_cols, k=k, device=self._native_device))

def arange(self, start: int | float, /, stop: int | float | None = None, step: int | float = 1) -> Array:
return Array(jnp.arange(start, stop, step, device=self._native_device))

def linspace(
self,
start: int | float | complex,
stop: int | float | complex,
/,
num: int,
*,
endpoint: bool = True,
) -> Array:
return Array(jnp.linspace(start, stop, num, endpoint=endpoint, device=self._native_device))

def device_to_native(self, device: Devices) -> jax.Device:
if device == Devices.CPU:
Expand Down Expand Up @@ -174,6 +194,12 @@ def vector_norm(
) -> Array:
return Array(jnp.linalg.norm(x.value, ord=ord, axis=axis, keepdims=keepdims))

def solve(self, x1: Array, x2: Array) -> Array:
return Array(jnp.linalg.solve(x1.value, x2.value))

def eigvalsh(self, x: Array) -> Array:
return Array(jnp.linalg.eigvalsh(x.value))

# Math reductions

def sum(self, x: Array, axis: int | tuple[int, ...] | None = None, keepdims: bool = False) -> Array:
Expand Down Expand Up @@ -256,6 +282,20 @@ def absolute(self, x: Array) -> Array:
def sqrt(self, x: Array) -> Array:
return Array(jnp.sqrt(x.value))

def exp(self, x: Array) -> Array:
return Array(jnp.exp(x.value))

def logaddexp(self, x1: int | float | Array, x2: int | float | Array) -> Array:
return Array(jnp.logaddexp(unwrap(x1), unwrap(x2)))

def where(
self,
condition: Array,
x1: bool | int | float | complex | Array,
x2: bool | int | float | complex | Array,
) -> Array:
return Array(jnp.where(condition.value, unwrap(x1), unwrap(x2)))

def isfinite(self, x: Array) -> Array:
"""Element-wise test for finite values."""
return Array(jnp.isfinite(x.value))
Expand Down
44 changes: 42 additions & 2 deletions decent_array/interoperability/_numpy/numpy_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ def __init__(self, device: Devices = Devices.CPU) -> None:

# Array creation

def empty(self, shape: int | tuple[int, ...]) -> Array:
return Array(np.empty(shape))

def empty_like(self, x: Array, /) -> Array:
return Array(np.empty_like(x.value))

def zeros(self, shape: int | tuple[int, ...]) -> Array:
return Array(np.zeros(shape))

Expand All @@ -52,8 +58,22 @@ def ones(self, shape: int | tuple[int, ...]) -> Array:
def ones_like(self, x: Array) -> Array:
return Array(np.ones_like(x.value))

def eye(self, n: int) -> Array:
return Array(np.eye(n))
def eye(self, n_rows: int, n_cols: int | None = None, /, *, k: int = 0) -> Array:
return Array(np.eye(n_rows, n_cols, k=k))

def arange(self, start: int | float, /, stop: int | float | None = None, step: int | float = 1) -> Array:
return Array(np.arange(start, stop, step))

def linspace(
self,
start: int | float | complex,
stop: int | float | complex,
/,
num: int,
*,
endpoint: bool = True,
) -> Array:
return Array(np.linspace(start, stop, num, endpoint=endpoint))

def device_to_native(self, device: Devices) -> Any: # noqa: ANN401
# NumPy has no explicit device management; surface the request unchanged.
Expand Down Expand Up @@ -169,6 +189,12 @@ def vector_norm(
# but is still typed as int | tuple[int, int] | None, hence the ignore
return Array(np.linalg.vector_norm(x.value, ord=ord, axis=axis, keepdims=keepdims)) # type: ignore[arg-type]

def solve(self, x1: Array, x2: Array) -> Array:
return Array(np.linalg.solve(x1.value, x2.value))

def eigvalsh(self, x: Array) -> Array:
return Array(np.linalg.eigvalsh(x.value))

# Math reductions

def sum(self, x: Array, axis: int | tuple[int, ...] | None = None, keepdims: bool = False) -> Array:
Expand Down Expand Up @@ -268,6 +294,20 @@ def absolute(self, x: Array) -> Array:
def sqrt(self, x: Array) -> Array:
return Array(np.sqrt(x.value))

def exp(self, x: Array) -> Array:
return Array(np.exp(x.value))

def logaddexp(self, x1: int | float | Array, x2: int | float | Array) -> Array:
return Array(np.logaddexp(unwrap(x1), unwrap(x2)))

def where(
self,
condition: Array,
x1: bool | int | float | complex | Array,
x2: bool | int | float | complex | Array,
) -> Array:
return Array(np.where(condition.value, unwrap(x1), unwrap(x2)))

def isfinite(self, x: Array) -> Array:
"""Element-wise test for finite values."""
return Array(np.isfinite(x.value))
Expand Down
Loading
Loading