Skip to content

Commit

Permalink
Enforce deprecations; clean up warnings.
Browse files Browse the repository at this point in the history
  • Loading branch information
hameerabbasi committed Aug 12, 2024
1 parent 0612373 commit fca0acb
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 173 deletions.
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[pytest]
addopts = --cov-report term-missing --cov-report html --cov-report=term:skip-covered --cov sparse --cov-config .coveragerc --junitxml=junit/test-results.xml
addopts = -W error --cov-report term-missing --cov-report html --cov-report=term:skip-covered --cov sparse --cov-config .coveragerc --junitxml=junit/test-results.xml
filterwarnings =
ignore::PendingDeprecationWarning
ignore::sparse.SparseFutureWarning
testpaths =
sparse
junit_family=xunit2
9 changes: 8 additions & 1 deletion sparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ class BackendType(Enum):

_ENV_VAR_NAME = "SPARSE_BACKEND"


class SparseFutureWarning(FutureWarning):
pass


if os.environ.get(_ENV_VAR_NAME, "") != "":
warnings.warn(
"Changing back-ends is a development feature, please do not rely on it in production.",
FutureWarning,
SparseFutureWarning,
stacklevel=1,
)
_backend_name = os.environ[_ENV_VAR_NAME]
Expand Down Expand Up @@ -51,3 +56,5 @@ class BackendType(Enum):
_umath,
_utils,
)

__all__ = __all__ + ["BACKEND", "BackendType", "SparseFutureWarning"]
30 changes: 19 additions & 11 deletions sparse/numba_backend/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import numpy as np

from ._coo.common import asCOO
from ._coo import as_coo
from ._sparse_array import SparseArray
from ._utils import (
_zero_of_dtype,
Expand All @@ -19,6 +19,9 @@
normalize_axis,
)

_EINSUM_SYMBOLS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
_EINSUM_SYMBOLS_SET = set(_EINSUM_SYMBOLS)


def _is_scipy_sparse_obj(x):
"""
Expand Down Expand Up @@ -189,7 +192,12 @@ def tensordot(a, b, axes=2, *, return_type=None):
oldb = [bs[axis] for axis in notin]

if builtins.any(dim == 0 for dim in chain(newshape_a, newshape_b)):
res = asCOO(np.empty(olda + oldb), check=False)
from sparse import COO

dt = (np.empty((), dtype=a.dtype) + np.empty((), dtype=b.dtype)).dtype
res = COO(
np.empty((len(olda) + len(oldb), 0), dtype=np.uintp), data=np.empty(0, dtype=dt), shape=tuple(olda + oldb)
)
if isinstance(a, np.ndarray) or isinstance(b, np.ndarray):
res = res.todense()

Expand Down Expand Up @@ -309,9 +317,9 @@ def dot(a, b):

if a.ndim == 1 and b.ndim == 1:
if isinstance(a, SparseArray):
a = asCOO(a)
a = as_coo(a)
if isinstance(b, SparseArray):
b = asCOO(b)
b = as_coo(b)
return (a * b).sum()

a_axis = -1
Expand Down Expand Up @@ -1182,7 +1190,7 @@ def _parse_einsum_input(operands):
for s in subscripts:
if s in ".,->":
continue
if s not in np.core.einsumfunc.einsum_symbols:
if not s.isalpha():
raise ValueError(f"Character {s} is not a valid symbol.")

else:
Expand All @@ -1206,7 +1214,7 @@ def _parse_einsum_input(operands):
s = index(s)
except TypeError as e:
raise TypeError("For this input type lists must contain either int or Ellipsis") from e
subscripts += np.core.einsumfunc.einsum_symbols[s]
subscripts += _EINSUM_SYMBOLS[s]
if num != last:
subscripts += ","

Expand All @@ -1220,7 +1228,7 @@ def _parse_einsum_input(operands):
s = index(s)
except TypeError as e:
raise TypeError("For this input type lists must contain either int or Ellipsis") from e
subscripts += np.core.einsumfunc.einsum_symbols[s]
subscripts += _EINSUM_SYMBOLS[s]
# Check for proper "->"
if ("-" in subscripts) or (">" in subscripts):
invalid = (subscripts.count("-") > 1) or (subscripts.count(">") > 1)
Expand All @@ -1230,7 +1238,7 @@ def _parse_einsum_input(operands):
# Parse ellipses
if "." in subscripts:
used = subscripts.replace(".", "").replace(",", "").replace("->", "")
unused = list(np.core.einsumfunc.einsum_symbols_set - set(used))
unused = list(_EINSUM_SYMBOLS_SET - set(used))
ellipse_inds = "".join(unused)
longest = 0

Expand Down Expand Up @@ -1275,7 +1283,7 @@ def _parse_einsum_input(operands):
output_subscript = ""
tmp_subscripts = subscripts.replace(",", "")
for s in sorted(set(tmp_subscripts)):
if s not in (np.core.einsumfunc.einsum_symbols):
if not s.isalpha():
raise ValueError(f"Character {s} is not a valid symbol.")
if tmp_subscripts.count(s) == 1:
output_subscript += s
Expand All @@ -1292,7 +1300,7 @@ def _parse_einsum_input(operands):
tmp_subscripts = subscripts.replace(",", "")
output_subscript = ""
for s in sorted(set(tmp_subscripts)):
if s not in np.core.einsumfunc.einsum_symbols:
if not s.isalpha():
raise ValueError(f"Character {s} is not a valid symbol.")
if tmp_subscripts.count(s) == 1:
output_subscript += s
Expand Down Expand Up @@ -1340,7 +1348,7 @@ def _einsum_single(lhs, rhs, operand):

# else require COO for operations, but check if should convert back
to_output_format = getattr(operand, "from_coo", lambda x: x)
operand = asCOO(operand)
operand = as_coo(operand)

# check if repeated / 'trace' indices mean we are only taking a subset
where = {}
Expand Down
76 changes: 13 additions & 63 deletions sparse/numba_backend/_coo/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,17 +207,18 @@ def __init__(
fill_value=None,
idx_dtype=None,
):
if isinstance(coords, COO):
self._make_shallow_copy_of(coords)
if data is not None or shape is not None:
raise ValueError("If `coords` is `COO`, then no other arguments should be provided.")
if fill_value is not None:
self.fill_value = self.data.dtype.type(fill_value)
return

self._cache = None
if cache:
self.enable_caching()

if not isinstance(coords, np.ndarray):
warnings.warn(
"coords should be an ndarray. This will raise a ValueError in the future.",
DeprecationWarning,
stacklevel=1,
)

if data is None:
arr = as_coo(coords, shape=shape, fill_value=fill_value, idx_dtype=idx_dtype)
self._make_shallow_copy_of(arr)
Expand All @@ -238,15 +239,10 @@ def __init__(
self.data = np.broadcast_to(self.data, self.coords.shape[1])

if self.data.ndim != 1:
raise ValueError("data must be a scalar or 1-dimensional.")
raise ValueError("`data` must be a scalar or 1-dimensional.")

if shape is None:
warnings.warn(
"shape should be provided. This will raise a ValueError in the future.",
DeprecationWarning,
stacklevel=1,
)
shape = tuple(self.coords.max(axis=1) + 1) if self.coords.nbytes else ()
raise ValueError("`shape` was not provided.")

if not isinstance(shape, Iterable):
shape = (shape,)
Expand All @@ -256,7 +252,6 @@ def __init__(

if shape and not self.coords.size:
self.coords = np.zeros((len(shape) if isinstance(shape, Iterable) else 1, 0), dtype=np.intp)

super().__init__(shape, fill_value=fill_value)
if idx_dtype:
if not can_store(idx_dtype, max(shape)):
Expand Down Expand Up @@ -417,11 +412,12 @@ def todense(self):
coords = tuple([self.coords[i, :] for i in range(self.ndim)])
data = self.data

if coords != ():
if len(coords) != 0:
x[coords] = data
else:
if len(data) != 0:
x[coords] = data
assert data.shape == (1,)
x[...] = data[0]

return x

Expand Down Expand Up @@ -1157,52 +1153,6 @@ def squeeze(self, axis=None):
fill_value=self.fill_value,
)

def resize(self, *args, refcheck=True, coords_dtype=np.intp):
"""
This method changes the shape and size of an array in-place.
Parameters
----------
args : tuple, or series of integers
The desired shape of the output array.
See Also
--------
[`numpy.ndarray.resize`][] : The equivalent Numpy function.
"""
warnings.warn("resize is deprecated on all SpraseArray objects.", DeprecationWarning, stacklevel=1)
if len(args) == 1 and isinstance(args[0], tuple):
shape = args[0]
elif all(isinstance(arg, int) for arg in args):
shape = tuple(args)
else:
raise ValueError("Invalid input")

if any(d < 0 for d in shape):
raise ValueError("negative dimensions not allowed")

new_size = reduce(operator.mul, shape, 1)

# TODO: this self.size enforces a 2**64 limit to array size
linear_loc = self.linear_loc()
end_idx = np.searchsorted(linear_loc, new_size, side="left")
linear_loc = linear_loc[:end_idx]

idx_dtype = self.coords.dtype
if shape != () and not can_store(idx_dtype, max(shape)):
idx_dtype = np.min_scalar_type(max(shape))
coords = np.empty((len(shape), len(linear_loc)), dtype=idx_dtype)
strides = 1
for i, d in enumerate(shape[::-1]):
coords[-(i + 1), :] = (linear_loc // strides) % d
strides *= d

self.shape = shape
self.coords = coords

if len(self.data) != len(linear_loc):
self.data = self.data[:end_idx].copy()

def to_scipy_sparse(self, /, *, accept_fv=None):
"""
Converts this [`sparse.COO`][] object into a [`scipy.sparse.coo_matrix`][].
Expand Down
4 changes: 3 additions & 1 deletion sparse/numba_backend/_sparse_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ def density(self):
>>> s.density
0.125
"""
return self.nnz / self.size
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
return float(np.float64(self.nnz) / np.float64(self.size))

def _repr_html_(self):
"""
Expand Down
13 changes: 5 additions & 8 deletions sparse/numba_backend/_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import functools
import operator
import warnings
from collections.abc import Iterable
from functools import reduce
from numbers import Integral

import numba
Expand Down Expand Up @@ -476,14 +474,12 @@ def html_table(arr):
table = ["<table><tbody>"]
headings = ["Format", "Data Type", "Shape", "nnz", "Density", "Read-only"]

density = np.float64(arr.nnz) / np.float64(arr.size)

info = [
type(arr).__name__.lower(),
str(arr.dtype),
str(arr.shape),
str(arr.nnz),
str(density),
str(arr.density),
]

# read-only
Expand All @@ -493,9 +489,10 @@ def html_table(arr):
headings.append("Size")
info.append(human_readable_size(arr.nbytes))
headings.append("Storage ratio")
info.append(
f"{np.float64(arr.nbytes) / np.float64(reduce(operator.mul, arr.shape, 1) * arr.dtype.itemsize):.2f}"
)
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=RuntimeWarning)
ratio = float(np.float64(arr.nbytes) / np.float64(arr.size * arr.dtype.itemsize))
info.append(f"{ratio:.2f}")

# compressed_axes
if type(arr).__name__ == "GCXS":
Expand Down
Loading

0 comments on commit fca0acb

Please sign in to comment.