Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "ENH: sparse: Add _array version of diags creation functions… #18599

Merged
merged 1 commit into from Jun 1, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 0 additions & 7 deletions scipy/sparse/__init__.py
Expand Up @@ -74,13 +74,6 @@
Functions
---------

Building sparse arrays:

.. autosummary::
:toctree: generated/

diags_array - Return a sparse array from diagonals

Building sparse matrices:

.. autosummary::
Expand Down
122 changes: 26 additions & 96 deletions scipy/sparse/_construct.py
@@ -1,11 +1,10 @@
"""Functions to construct sparse matrices and arrays
"""Functions to construct sparse matrices
"""

__docformat__ = "restructuredtext en"

__all__ = ['spdiags', 'eye', 'identity', 'kron', 'kronsum',
'hstack', 'vstack', 'bmat', 'rand', 'random', 'diags', 'block_diag',
'diags_array']
'hstack', 'vstack', 'bmat', 'rand', 'random', 'diags', 'block_diag']

import numbers
from functools import partial
Expand All @@ -15,11 +14,11 @@
from ._sputils import upcast, get_index_dtype, isscalarlike

from ._sparsetools import csr_hstack
from ._csr import csr_matrix
from ._csc import csc_matrix
from ._bsr import bsr_matrix
from ._coo import coo_matrix
from ._csc import csc_matrix
from ._csr import csr_matrix
from ._dia import dia_matrix, dia_array
from ._dia import dia_matrix

from ._base import issparse

Expand Down Expand Up @@ -71,47 +70,54 @@ def spdiags(data, diags, m=None, n=None, format=None):
return dia_matrix((data, diags), shape=(m, n)).asformat(format)


def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None):
def diags(diagonals, offsets=0, shape=None, format=None, dtype=None):
"""
Construct a sparse array from diagonals.
Construct a sparse matrix from diagonals.

Parameters
----------
diagonals : sequence of array_like
Sequence of arrays containing the array diagonals,
Sequence of arrays containing the matrix diagonals,
corresponding to `offsets`.
offsets : sequence of int or an int, optional
Diagonals to set:
- k = 0 the main diagonal (default)
- k > 0 the kth upper diagonal
- k < 0 the kth lower diagonal
shape : tuple of int, optional
Shape of the result. If omitted, a square array large enough
Shape of the result. If omitted, a square matrix large enough
to contain the diagonals is returned.
format : {"dia", "csr", "csc", "lil", ...}, optional
Matrix format of the result. By default (format=None) an
appropriate sparse array format is returned. This choice is
appropriate sparse matrix format is returned. This choice is
subject to change.
dtype : dtype, optional
Data type of the array.
Data type of the matrix.

See Also
--------
spdiags : construct matrix from diagonals

Notes
-----
The result from `diags_array` is the sparse equivalent of::
This function differs from `spdiags` in the way it handles
off-diagonals.

The result from `diags` is the sparse equivalent of::

np.diag(diagonals[0], offsets[0])
+ ...
+ np.diag(diagonals[k], offsets[k])

Repeated diagonal offsets are disallowed.

.. versionadded:: 1.11
.. versionadded:: 0.11

Examples
--------
>>> from scipy.sparse import diags_array
>>> from scipy.sparse import diags
>>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]]
>>> diags_array(diagonals, offsets=[0, -1, 2]).toarray()
>>> diags(diagonals, [0, -1, 2]).toarray()
array([[1, 0, 1, 0],
[1, 2, 0, 2],
[0, 2, 3, 0],
Expand All @@ -120,7 +126,7 @@ def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None)
Broadcasting of scalars is supported (but shape needs to be
specified):

>>> diags_array([1, -2, 1], offsets=[-1, 0, 1], shape=(4, 4)).toarray()
>>> diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)).toarray()
array([[-2., 1., 0., 0.],
[ 1., -2., 1., 0.],
[ 0., 1., -2., 1.],
Expand All @@ -130,7 +136,7 @@ def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None)
If only one diagonal is wanted (as in `numpy.diag`), the following
works as well:

>>> diags_array([1, 2, 3], offsets=1).toarray()
>>> diags([1, 2, 3], 1).toarray()
array([[ 0., 1., 0., 0.],
[ 0., 0., 2., 0.],
[ 0., 0., 0., 3.],
Expand Down Expand Up @@ -183,87 +189,11 @@ def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None)
if len(diagonal) != length and len(diagonal) != 1:
raise ValueError(
"Diagonal length (index %d: %d at offset %d) does not "
"agree with array size (%d, %d)." % (
"agree with matrix size (%d, %d)." % (
j, len(diagonal), offset, m, n)) from e
raise

return dia_array((data_arr, offsets), shape=(m, n)).asformat(format)


def diags(diagonals, offsets=0, shape=None, format=None, dtype=None):
"""
Construct a sparse matrix from diagonals.

Parameters
----------
diagonals : sequence of array_like
Sequence of arrays containing the matrix diagonals,
corresponding to `offsets`.
offsets : sequence of int or an int, optional
Diagonals to set:
- k = 0 the main diagonal (default)
- k > 0 the kth upper diagonal
- k < 0 the kth lower diagonal
shape : tuple of int, optional
Shape of the result. If omitted, a square matrix large enough
to contain the diagonals is returned.
format : {"dia", "csr", "csc", "lil", ...}, optional
Matrix format of the result. By default (format=None) an
appropriate sparse matrix format is returned. This choice is
subject to change.
dtype : dtype, optional
Data type of the matrix.

See Also
--------
spdiags : construct matrix from diagonals

Notes
-----
This function differs from `spdiags` in the way it handles
off-diagonals.

The result from `diags` is the sparse equivalent of::

np.diag(diagonals[0], offsets[0])
+ ...
+ np.diag(diagonals[k], offsets[k])

Repeated diagonal offsets are disallowed.

.. versionadded:: 0.11

Examples
--------
>>> from scipy.sparse import diags
>>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]]
>>> diags(diagonals, [0, -1, 2]).toarray()
array([[1, 0, 1, 0],
[1, 2, 0, 2],
[0, 2, 3, 0],
[0, 0, 3, 4]])

Broadcasting of scalars is supported (but shape needs to be
specified):

>>> diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)).toarray()
array([[-2., 1., 0., 0.],
[ 1., -2., 1., 0.],
[ 0., 1., -2., 1.],
[ 0., 0., 1., -2.]])


If only one diagonal is wanted (as in `numpy.diag`), the following
works as well:

>>> diags([1, 2, 3], 1).toarray()
array([[ 0., 1., 0., 0.],
[ 0., 0., 2., 0.],
[ 0., 0., 0., 3.],
[ 0., 0., 0., 0.]])
"""
A = diags_array(diagonals, offsets=offsets, shape=shape, dtype=dtype)
return dia_matrix(A).asformat(format)
return dia_matrix((data_arr, offsets), shape=(m, n)).asformat(format)


def identity(n, dtype='d', format=None):
Expand Down
37 changes: 9 additions & 28 deletions scipy/sparse/tests/test_construct.py
Expand Up @@ -147,12 +147,12 @@ def test_diags(self):

for d, o, shape, result in cases:
err_msg = f"{d!r} {o!r} {shape!r} {result!r}"
assert_equal(construct.diags(d, offsets=o, shape=shape).toarray(),
assert_equal(construct.diags(d, o, shape=shape).toarray(),
result, err_msg=err_msg)

if shape[0] == shape[1] and hasattr(d[0], '__len__') and len(d[0]) <= max(shape):
# should be able to find the shape automatically
assert_equal(construct.diags(d, offsets=o).toarray(), result,
assert_equal(construct.diags(d, o).toarray(), result,
err_msg=err_msg)

def test_diags_default(self):
Expand All @@ -178,9 +178,9 @@ def test_diags_bad(self):
cases.append(([a], 0, None))

for d, o, shape in cases:
assert_raises(ValueError, construct.diags, d, offsets=o, shape=shape)
assert_raises(ValueError, construct.diags, d, o, shape)

assert_raises(TypeError, construct.diags, [[None]], offsets=[0])
assert_raises(TypeError, construct.diags, [[None]], [0])

def test_diags_vs_diag(self):
# Check that
Expand All @@ -199,26 +199,26 @@ def test_diags_vs_diag(self):

diagonals = [np.random.rand(n - abs(q)) for q in offsets]

mat = construct.diags(diagonals, offsets=offsets)
mat = construct.diags(diagonals, offsets)
dense_mat = sum([np.diag(x, j) for x, j in zip(diagonals, offsets)])

assert_array_almost_equal_nulp(mat.toarray(), dense_mat)

if len(offsets) == 1:
mat = construct.diags(diagonals[0], offsets=offsets[0])
mat = construct.diags(diagonals[0], offsets[0])
dense_mat = np.diag(diagonals[0], offsets[0])
assert_array_almost_equal_nulp(mat.toarray(), dense_mat)

def test_diags_dtype(self):
x = construct.diags([2.2], offsets=[0], shape=(2, 2), dtype=int)
x = construct.diags([2.2], [0], shape=(2, 2), dtype=int)
assert_equal(x.dtype, int)
assert_equal(x.toarray(), [[2, 0], [0, 2]])

def test_diags_one_diagonal(self):
d = list(range(5))
for k in range(-5, 6):
assert_equal(construct.diags(d, offsets=k).toarray(),
construct.diags([d], offsets=[k]).toarray())
assert_equal(construct.diags(d, k).toarray(),
construct.diags([d], [k]).toarray())

def test_diags_empty(self):
x = construct.diags([])
Expand Down Expand Up @@ -580,22 +580,3 @@ def test_random_sparse_matrix_returns_correct_number_of_non_zero_elements(self):
sparse_matrix = construct.random(10, 10, density=0.1265)
assert_equal(sparse_matrix.count_nonzero(),13)


def test_diags_array():
"""Tests of diags_array that do not rely on diags wrapper."""
diag = np.arange(1, 5)

assert_array_equal(construct.diags_array(diag).toarray(), np.diag(diag))

assert_array_equal(
construct.diags_array(diag, offsets=2).toarray(), np.diag(diag, k=2)
)

assert_array_equal(
construct.diags_array(diag, offsets=2, shape=(4, 4)).toarray(),
np.diag(diag, k=2)[:4, :4]
)

# Offset outside bounds when shape specified
with pytest.raises(ValueError, match=".*out of bounds"):
construct.diags(np.arange(1, 5), 5, shape=(4, 4))