diff --git a/docs/generated/sparse.COO.abs.rst b/docs/generated/sparse.COO.abs.rst deleted file mode 100644 index 7bfba439..00000000 --- a/docs/generated/sparse.COO.abs.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.abs -======== - -.. currentmodule:: sparse - -.. automethod:: COO.abs \ No newline at end of file diff --git a/docs/generated/sparse.COO.ceil.rst b/docs/generated/sparse.COO.ceil.rst deleted file mode 100644 index e999de48..00000000 --- a/docs/generated/sparse.COO.ceil.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.ceil -========= - -.. currentmodule:: sparse - -.. automethod:: COO.ceil \ No newline at end of file diff --git a/docs/generated/sparse.COO.conj.rst b/docs/generated/sparse.COO.conj.rst deleted file mode 100644 index 39a963af..00000000 --- a/docs/generated/sparse.COO.conj.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.conj -========= - -.. currentmodule:: sparse - -.. automethod:: COO.conj \ No newline at end of file diff --git a/docs/generated/sparse.COO.conjugate.rst b/docs/generated/sparse.COO.conjugate.rst deleted file mode 100644 index 476903c1..00000000 --- a/docs/generated/sparse.COO.conjugate.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.conjugate -============== - -.. currentmodule:: sparse - -.. automethod:: COO.conjugate \ No newline at end of file diff --git a/docs/generated/sparse.COO.exp.rst b/docs/generated/sparse.COO.exp.rst deleted file mode 100644 index 7a6067ef..00000000 --- a/docs/generated/sparse.COO.exp.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.exp -======== - -.. currentmodule:: sparse - -.. automethod:: COO.exp \ No newline at end of file diff --git a/docs/generated/sparse.COO.expm1.rst b/docs/generated/sparse.COO.expm1.rst deleted file mode 100644 index 46717d78..00000000 --- a/docs/generated/sparse.COO.expm1.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.expm1 -========== - -.. currentmodule:: sparse - -.. automethod:: COO.expm1 \ No newline at end of file diff --git a/docs/generated/sparse.COO.floor.rst b/docs/generated/sparse.COO.floor.rst deleted file mode 100644 index 7ff8a946..00000000 --- a/docs/generated/sparse.COO.floor.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.floor -========== - -.. currentmodule:: sparse - -.. automethod:: COO.floor \ No newline at end of file diff --git a/docs/generated/sparse.COO.log1p.rst b/docs/generated/sparse.COO.log1p.rst deleted file mode 100644 index ec27f8e4..00000000 --- a/docs/generated/sparse.COO.log1p.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.log1p -========== - -.. currentmodule:: sparse - -.. automethod:: COO.log1p \ No newline at end of file diff --git a/docs/generated/sparse.COO.rint.rst b/docs/generated/sparse.COO.rint.rst deleted file mode 100644 index 4302f0d4..00000000 --- a/docs/generated/sparse.COO.rint.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.rint -========= - -.. currentmodule:: sparse - -.. automethod:: COO.rint \ No newline at end of file diff --git a/docs/generated/sparse.COO.rst b/docs/generated/sparse.COO.rst index 86b12545..2da5bcf3 100644 --- a/docs/generated/sparse.COO.rst +++ b/docs/generated/sparse.COO.rst @@ -6,7 +6,7 @@ COO .. autoclass:: COO .. note:: - :obj:`COO` objects also support :doc:`operators <../user_manual/operations/basic>` + :obj:`COO` objects also support :doc:`operators <../user_manual/operations/operators>` and :doc:`indexing <../user_manual/operations/indexing>` .. rubric:: Attributes @@ -33,22 +33,8 @@ COO :toctree: COO.elemwise - COO.abs COO.astype - COO.ceil - COO.conj - COO.conjugate - COO.exp - COO.expm1 - COO.floor - COO.log1p - COO.rint COO.round - COO.sin - COO.sinh - COO.sqrt - COO.tan - COO.tanh .. rubric:: :doc:`Reductions <../user_manual/operations/reductions>` .. autosummary:: diff --git a/docs/generated/sparse.COO.sin.rst b/docs/generated/sparse.COO.sin.rst deleted file mode 100644 index 2c00972b..00000000 --- a/docs/generated/sparse.COO.sin.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.sin -======== - -.. currentmodule:: sparse - -.. automethod:: COO.sin \ No newline at end of file diff --git a/docs/generated/sparse.COO.sinh.rst b/docs/generated/sparse.COO.sinh.rst deleted file mode 100644 index e3d5b7f0..00000000 --- a/docs/generated/sparse.COO.sinh.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.sinh -========= - -.. currentmodule:: sparse - -.. automethod:: COO.sinh \ No newline at end of file diff --git a/docs/generated/sparse.COO.sqrt.rst b/docs/generated/sparse.COO.sqrt.rst deleted file mode 100644 index 10a8ccb2..00000000 --- a/docs/generated/sparse.COO.sqrt.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.sqrt -========= - -.. currentmodule:: sparse - -.. automethod:: COO.sqrt \ No newline at end of file diff --git a/docs/generated/sparse.COO.tan.rst b/docs/generated/sparse.COO.tan.rst deleted file mode 100644 index f91eb510..00000000 --- a/docs/generated/sparse.COO.tan.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.tan -======== - -.. currentmodule:: sparse - -.. automethod:: COO.tan \ No newline at end of file diff --git a/docs/generated/sparse.COO.tanh.rst b/docs/generated/sparse.COO.tanh.rst deleted file mode 100644 index db166d9a..00000000 --- a/docs/generated/sparse.COO.tanh.rst +++ /dev/null @@ -1,6 +0,0 @@ -COO\.tanh -========= - -.. currentmodule:: sparse - -.. automethod:: COO.tanh \ No newline at end of file diff --git a/docs/user_manual/operations.rst b/docs/user_manual/operations.rst index 3794a698..82f98c08 100644 --- a/docs/user_manual/operations.rst +++ b/docs/user_manual/operations.rst @@ -8,7 +8,7 @@ common operations `_ .. toctree:: :maxdepth: 2 - operations/basic + operations/operators operations/elemwise operations/reductions operations/indexing diff --git a/docs/user_manual/operations/elemwise.rst b/docs/user_manual/operations/elemwise.rst index b0c2a3f9..a1f2127f 100644 --- a/docs/user_manual/operations/elemwise.rst +++ b/docs/user_manual/operations/elemwise.rst @@ -10,54 +10,34 @@ To illustrate, the following are all possible, and will produce another .. code-block:: python - x.abs() + np.abs(x) np.sin(x) np.sqrt(x) - x.conj() - x.expm1() + np.conj(x) + np.expm1(x) np.log1p(x) However, the following are all unsupported and will raise a :obj:`ValueError`: .. code-block:: python - x.exp() + np.exp(x) np.cos(x) np.log(x) Notice that you can apply any unary or binary :obj:`numpy.ufunc` to :obj:`COO` -arrays, :obj:`scipy.sparse.spmatrix` objects and scalars and it will work so -long as the result is not dense. +arrays, and :obj:`numpy.ndarray` objects and scalars and it will work so +long as the result is not dense. When applying to :obj:`numpy.ndarray` objects, +we check that operating on the array with zero would always produce a zero. :obj:`COO.elemwise` ------------------- This function allows you to apply any arbitrary unary or binary function where the first object is :obj:`COO`, and the second is a scalar, :obj:`COO`, or -a :obj:`scipy.sparse.spmatrix`. For example, the following will add two +a :obj:`numpy.ndarray`. For example, the following will add two :obj:`COO` objects: .. code-block:: python x.elemwise(np.add, y) -Partial List of Supported :obj:`numpy.ufunc` s ----------------------------------------------- -Although any unary or binary :obj:`numpy.ufunc` should work if the result is -not dense, when calling in the form :code:`x.func()`, the following operations -are supported: - -* :obj:`COO.abs` -* :obj:`COO.expm1` -* :obj:`COO.log1p` -* :obj:`COO.sin` -* :obj:`COO.sinh` -* :obj:`COO.tan` -* :obj:`COO.tanh` -* :obj:`COO.sqrt` -* :obj:`COO.ceil` -* :obj:`COO.floor` -* :obj:`COO.round` -* :obj:`COO.rint` -* :obj:`COO.conj` -* :obj:`COO.conjugate` -* :obj:`COO.astype` diff --git a/docs/user_manual/operations/basic.rst b/docs/user_manual/operations/operators.rst similarity index 62% rename from docs/user_manual/operations/basic.rst rename to docs/user_manual/operations/operators.rst index c47913f3..1372854b 100644 --- a/docs/user_manual/operations/basic.rst +++ b/docs/user_manual/operations/operators.rst @@ -1,9 +1,9 @@ .. currentmodule:: sparse -Basic Operations -================ +Operators +========= :obj:`COO` objects can have a number of operators applied to them. They support -operations with scalars, :obj:`scipy.sparse.spmatrix` objects, and other +operations with scalars, :obj:`numpy.ndarray` objects, and other :obj:`COO` objects. For example, to get the sum of two :obj:`COO` objects, you would do the following: @@ -53,6 +53,46 @@ If densification is needed, it must be explicit. In other words, you must call :obj:`COO.todense` on the :obj:`COO` object. If both operands are :obj:`COO`, both must be densified. +Operations with :obj:`numpy.ndarray` +------------------------------------ +Certain operations with :obj:`numpy.ndarray` are also supported. For example, +the following are all allowed if :code:`x` is a :obj:`numpy.ndarray` and +:code:`(x == 0).all()` evaluates to :code:`True`: + +.. code-block:: python + + x + y + x - y + +The following is true so long as there are no infinities or NaNs in :code:`x`: + +.. code-block:: python + + x * y + +In general, if operating on the :code:`numpy.ndarray` with a zero would produce +all-zeros then the operation is supported. + +Operations with :obj:`scipy.sparse.spmatrix` +-------------------------------------------- +Certain operations with :obj:`scipy.sparse.spmatrix` are also supported. +For example, the following are all allowed if :code:`y` is a :obj:`scipy.sparse.spmatrix`: + +.. code-block:: python + + x + y + x - y + x * y + x > y + x < y + +In general, if operating on a :code:`scipy.sparse.spmatrix` is the same as operating +on :obj:`COO`, as long as it is to the right of the operator. + +.. note:: Results are not guaranteed if :code:`x` is a :obj:`scipy.sparse.spmatrix`. + For this reason, we recommend that all Scipy sparse matrices should be explicitly + converted to :obj:`COO` before any operations. + Broadcasting ------------ All binary operators support :obj:`broadcasting `. @@ -67,8 +107,9 @@ will raise a :obj:`ValueError`. Full List of Operators ---------------------- Here, :code:`x` and :code:`y` can be :obj:`COO` arrays, -:obj:`scipy.sparse.spmatrix` objects or scalars, keeping in mind :ref:`auto -densification rules `. The following operators are supported: +:obj:`numpy.ndarray` objects or scalars, keeping in mind :ref:`auto +densification rules `. In addition, :code:`y` can also +be a :obj:`scipy.sparse.spmatrix` The following operators are supported: * Basic algebraic operations @@ -99,3 +140,5 @@ densification rules `. The following operators are supported * :obj:`operator.lshift` (:code:`x << y`) * :obj:`operator.rshift` (:code:`x >> y`) + +.. note:: In-place operators are not supported at this time. diff --git a/requirements.txt b/requirements.txt index df1123ff..f1802810 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -numpy +numpy >= 1.13 scipy >= 0.19 six diff --git a/sparse/coo.py b/sparse/coo.py index f0b5f0b1..4a56ce05 100644 --- a/sparse/coo.py +++ b/sparse/coo.py @@ -7,6 +7,7 @@ import numpy as np import scipy.sparse +from numpy.lib.mixins import NDArrayOperatorsMixin from .slicing import normalize_index from .utils import _zero_of_dtype @@ -20,7 +21,7 @@ pass -class COO(object): +class COO(NDArrayOperatorsMixin): """ A sparse multidimensional array. @@ -1221,6 +1222,10 @@ def __rmatmul__(self, other): return NotImplemented def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + out = kwargs.pop('out', None) + if out is not None: + return NotImplemented + if method == '__call__': return COO._elemwise(ufunc, *inputs, **kwargs) elif method == 'reduce': @@ -1522,125 +1527,26 @@ def sum_duplicates(self): self.coords = coords self.has_duplicates = False - def __add__(self, other): - return self.elemwise(operator.add, other) - - def __radd__(self, other): - return self.elemwise(_reverse_self_other(operator.add), other) - - def __neg__(self): - return self.elemwise(operator.neg) - - def __sub__(self, other): - return self.elemwise(operator.sub, other) - - def __rsub__(self, other): - return self.elemwise(_reverse_self_other(operator.sub), other) - - def __mul__(self, other): - return self.elemwise(operator.mul, other) - - def __rmul__(self, other): - return self.elemwise(_reverse_self_other(operator.mul), other) - - def __truediv__(self, other): - return self.elemwise(operator.truediv, other) - - def __rtruediv__(self, other): - return self.elemwise(_reverse_self_other(operator.truediv), other) - - def __floordiv__(self, other): - return self.elemwise(operator.floordiv, other) - - def __rfloordiv__(self, other): - return self.elemwise(_reverse_self_other(operator.floordiv), other) - - __div__ = __truediv__ - __rdiv__ = __rtruediv__ - - def __pow__(self, other): - return self.elemwise(operator.pow, other) - - def __rpow__(self, other): - return self.elemwise(_reverse_self_other(operator.pow), other) - - def __mod__(self, other): - return self.elemwise(operator.mod, other) - - def __rmod__(self, other): - return self.elemwise(_reverse_self_other(operator.mod), other) - - def __and__(self, other): - return self.elemwise(operator.and_, other) - - def __rand__(self, other): - return self.elemwise(_reverse_self_other(operator.and_), other) - - def __xor__(self, other): - return self.elemwise(operator.xor, other) - - def __rxor__(self, other): - return self.elemwise(_reverse_self_other(operator.xor), other) - - def __or__(self, other): - return self.elemwise(operator.or_, other) - - def __ror__(self, other): - return self.elemwise(_reverse_self_other(operator.or_), other) - - def __invert__(self): - return self.elemwise(operator.invert) - - def __gt__(self, other): - return self.elemwise(operator.gt, other) - - def __ge__(self, other): - return self.elemwise(operator.ge, other) - - def __lt__(self, other): - return self.elemwise(operator.lt, other) - - def __le__(self, other): - return self.elemwise(operator.le, other) - - def __eq__(self, other): - return self.elemwise(operator.eq, other) - - def __ne__(self, other): - return self.elemwise(operator.ne, other) - - def __lshift__(self, other): - return self.elemwise(operator.lshift, other) - - def __rlshift__(self, other): - return self.elemwise(_reverse_self_other(operator.lshift), other) - - def __rshift__(self, other): - return self.elemwise(operator.rshift, other) - - def __rrshift__(self, other): - return self.elemwise(_reverse_self_other(operator.rshift), other) - @staticmethod def _elemwise(func, *args, **kwargs): if len(args) == 0: return func() self = args[0] - if isinstance(self, scipy.sparse.spmatrix): - self = COO.from_numpy(self) - elif np.isscalar(self) or (isinstance(self, np.ndarray) - and self.ndim == 0): + if np.isscalar(self) or (isinstance(self, np.ndarray) + and self.ndim == 0): func = partial(func, self) other = args[1] - if isinstance(other, scipy.sparse.spmatrix): - other = COO.from_scipy_sparse(other) return _elemwise_unary(func, other, *args[2:], **kwargs) + if isinstance(self, scipy.sparse.spmatrix): + self = COO.from_scipy_sparse(self) + if len(args) == 1: return _elemwise_unary(func, self, *args[1:], **kwargs) else: other = args[1] + if isinstance(other, scipy.sparse.spmatrix): other = COO.from_scipy_sparse(other) @@ -1659,7 +1565,7 @@ def elemwise(self, func, *args, **kwargs): The function to apply to one or two arguments. args : tuple, optional The extra arguments to pass to the function. If :code:`args[0]` is a COO object, - a scipy.sparse.spmatrix or a scalar; the function will be treated as a binary + or a scalar; the function will be treated as a binary function. Otherwise, it will be treated as a unary function. kwargs : dict, optional The kwargs to pass to the function. @@ -1678,6 +1584,11 @@ def elemwise(self, func, *args, **kwargs): -------- :obj:`numpy.ufunc` : A similar Numpy construct. Note that any :code:`ufunc` can be used as the :code:`func` input to this function. + + Notes + ----- + In the future, this function may support functions of more than two arguments. Therefore, + it is best to pass in any additional arguments as keyword arguments. """ return COO._elemwise(func, self, *args, **kwargs) @@ -1712,204 +1623,6 @@ def broadcast_to(self, shape): return COO(coords, data, shape=result_shape, has_duplicates=self.has_duplicates, sorted=self.sorted) - def __abs__(self): - """ - Calculate the absolute value element-wise. - - See also - -------- - :obj:`numpy.absolute` : NumPy equivalent ufunc. - """ - return self.elemwise(abs) - - abs = __abs__ - - def exp(self, out=None): - """ - Calculate the exponential of all elements in the array. - - See also - -------- - :obj:`numpy.exp` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.exp) - - def expm1(self, out=None): - """ - Calculate :code:`exp(x) - 1` for all elements in the array. - - See also - -------- - scipy.sparse.coo_matrix.expm1 : SciPy sparse equivalent function - :obj:`numpy.expm1` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.expm1) - - def log1p(self, out=None): - """ - Return the natural logarithm of one plus the input array, element-wise. - - Calculates :code:`log(1 + x)`. - - See also - -------- - scipy.sparse.coo_matrix.log1p : SciPy sparse equivalent function - :obj:`numpy.log1p` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.log1p) - - def sin(self, out=None): - """ - Trigonometric sine, element-wise. - - See also - -------- - scipy.sparse.coo_matrix.sin : SciPy sparse equivalent function - :obj:`numpy.sin` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.sin) - - def sinh(self, out=None): - """ - Hyperbolic sine, element-wise. - - See also - -------- - scipy.sparse.coo_matrix.sinh : SciPy sparse equivalent function - :obj:`numpy.sinh` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.sinh) - - def tan(self, out=None): - """ - Compute tangent element-wise. - - See also - -------- - scipy.sparse.coo_matrix.tan : SciPy sparse equivalent function - :obj:`numpy.tan` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - """ - assert out is None - return self.elemwise(np.tan) - - def tanh(self, out=None): - """ - Compute hyperbolic tangent element-wise. - - See also - -------- - scipy.sparse.coo_matrix.tanh : SciPy sparse equivalent function - :obj:`numpy.tanh` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.tanh) - - def sqrt(self, out=None): - """ - Return the positive square-root of an array, element-wise. - - See also - -------- - scipy.sparse.coo_matrix.sqrt : SciPy sparse equivalent function - :obj:`numpy.sqrt` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.sqrt) - - def ceil(self, out=None): - """ - Return the ceiling of the input, element-wise. - - See also - -------- - scipy.sparse.coo_matrix.ceil : SciPy sparse equivalent function - :obj:`numpy.ceil` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.ceil) - - def floor(self, out=None): - """ - Return the floor of the input, element-wise. - - See also - -------- - scipy.sparse.coo_matrix.floor : SciPy sparse equivalent function - :obj:`numpy.floor` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.floor) - def round(self, decimals=0, out=None): """ Evenly round to the given number of decimals. @@ -1928,65 +1641,6 @@ def round(self, decimals=0, out=None): assert out is None return self.elemwise(np.round, decimals) - def rint(self, out=None): - """ - Round elements of the array to the nearest integer. - - See also - -------- - scipy.sparse.coo_matrix.rint : SciPy sparse equivalent function - :obj:`numpy.rint` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.rint) - - def conj(self, out=None): - """ - Return the complex conjugate, element-wise. - - See also - -------- - conjugate : Equivalent function - scipy.sparse.coo_matrix.conj : SciPy sparse equivalent function - :obj:`numpy.conj` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.conj) - - def conjugate(self, out=None): - """ - Return the complex conjugate, element-wise. - - See also - -------- - conj : Equivalent function - scipy.sparse.coo_matrix.conjugate : SciPy sparse equivalent function - :obj:`numpy.conj` : NumPy equivalent ufunc. - :obj:`COO.elemwise`: Apply an arbitrary element-wise function to one or two - arguments. - - Notes - ----- - The :code:`out` parameter is provided just for compatibility with Numpy and isn't - actually supported. - """ - assert out is None - return self.elemwise(np.conjugate) - def astype(self, dtype, out=None): """ Copy of the array, cast to a specified type. @@ -2004,7 +1658,7 @@ def astype(self, dtype, out=None): actually supported. """ assert out is None - return self.elemwise(np.ndarray.astype, dtype) + return self.elemwise(np.ndarray.astype, dtype=dtype) def maybe_densify(self, max_size=1000, min_density=0.25): """ @@ -2464,7 +2118,8 @@ def _elemwise_binary(func, self, other, *args, **kwargs): self_zero = _zero_of_dtype(self.dtype) other_zero = _zero_of_dtype(other.dtype) - func_zero = _zero_of_dtype(func(self_zero, other_zero, *args, **kwargs).dtype) + func_value = func(self_zero, other_zero, *args, **kwargs) + func_zero = _zero_of_dtype(np.dtype(func_value)) if check and func(self_zero, other_zero, *args, **kwargs) != func_zero: raise ValueError("Performing this operation would produce " "a dense result: %s" % str(func)) diff --git a/sparse/tests/test_coo.py b/sparse/tests/test_coo.py index f5883d70..ae3889ca 100644 --- a/sparse/tests/test_coo.py +++ b/sparse/tests/test_coo.py @@ -242,20 +242,6 @@ def test_auto_densification_fails(func): func(xs, ys) -@pytest.mark.parametrize('func', [ - operator.mul, operator.add, operator.sub, operator.gt, - operator.lt, operator.ne -]) -def test_op_scipy_sparse(func): - xs = sparse.random((3, 4), density=0.5) - y = sparse.random((3, 4), density=0.5).todense() - - ys = scipy.sparse.csr_matrix(y) - x = xs.todense() - - assert_eq(func(x, y), func(xs, ys)) - - @pytest.mark.parametrize('func, scalar', [ (operator.mul, 5), (operator.add, 0), @@ -908,6 +894,38 @@ def test_scipy_sparse_interaction(scipy_format): assert_eq(sp, coo) +@pytest.mark.parametrize('func', [ + operator.mul, operator.add, operator.sub, operator.gt, + operator.lt, operator.ne +]) +def test_op_scipy_sparse(func): + xs = sparse.random((3, 4), density=0.5) + y = sparse.random((3, 4), density=0.5).todense() + + ys = scipy.sparse.csr_matrix(y) + x = xs.todense() + + assert_eq(func(x, y), func(xs, ys)) + + +@pytest.mark.parametrize('func', [ + operator.add, + operator.sub, + pytest.mark.xfail(operator.mul, reason='Scipy sparse auto-densifies in this case.'), + pytest.mark.xfail(operator.gt, reason='Scipy sparse doesn\'t support this yet.'), + pytest.mark.xfail(operator.lt, reason='Scipy sparse doesn\'t support this yet.'), + pytest.mark.xfail(operator.ne, reason='Scipy sparse doesn\'t support this yet.'), +]) +def test_op_scipy_sparse_left(func): + ys = sparse.random((3, 4), density=0.5) + x = sparse.random((3, 4), density=0.5).todense() + + xs = scipy.sparse.csr_matrix(x) + y = ys.todense() + + assert_eq(func(x, y), func(xs, ys)) + + def test_cache_csr(): x = sparse.random((10, 5), density=0.5).todense() s = COO(x, cache=True)