275 lines
8.3 KiB
Python
275 lines
8.3 KiB
Python
import numpy as np
|
|
import pytest
|
|
from scipy.sparse import coo_array
|
|
|
|
|
|
def test_shape_constructor():
|
|
empty1d = coo_array((3,))
|
|
assert empty1d.shape == (3,)
|
|
assert np.array_equal(empty1d.toarray(), np.zeros((3,)))
|
|
|
|
empty2d = coo_array((3, 2))
|
|
assert empty2d.shape == (3, 2)
|
|
assert np.array_equal(empty2d.toarray(), np.zeros((3, 2)))
|
|
|
|
with pytest.raises(TypeError, match='invalid input format'):
|
|
coo_array((3, 2, 2))
|
|
|
|
|
|
def test_dense_constructor():
|
|
res1d = coo_array([1, 2, 3])
|
|
assert res1d.shape == (3,)
|
|
assert np.array_equal(res1d.toarray(), np.array([1, 2, 3]))
|
|
|
|
res2d = coo_array([[1, 2, 3], [4, 5, 6]])
|
|
assert res2d.shape == (2, 3)
|
|
assert np.array_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]]))
|
|
|
|
with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'):
|
|
coo_array([[[3]], [[4]]])
|
|
|
|
|
|
def test_dense_constructor_with_shape():
|
|
res1d = coo_array([1, 2, 3], shape=(3,))
|
|
assert res1d.shape == (3,)
|
|
assert np.array_equal(res1d.toarray(), np.array([1, 2, 3]))
|
|
|
|
res2d = coo_array([[1, 2, 3], [4, 5, 6]], shape=(2, 3))
|
|
assert res2d.shape == (2, 3)
|
|
assert np.array_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]]))
|
|
|
|
with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'):
|
|
coo_array([[[3]], [[4]]], shape=(2, 1, 1))
|
|
|
|
|
|
def test_dense_constructor_with_inconsistent_shape():
|
|
with pytest.raises(ValueError, match='inconsistent shapes'):
|
|
coo_array([1, 2, 3], shape=(4,))
|
|
|
|
with pytest.raises(ValueError, match='inconsistent shapes'):
|
|
coo_array([1, 2, 3], shape=(3, 1))
|
|
|
|
with pytest.raises(ValueError, match='inconsistent shapes'):
|
|
coo_array([[1, 2, 3]], shape=(3,))
|
|
|
|
with pytest.raises(ValueError,
|
|
match='axis 0 index 2 exceeds matrix dimension 2'):
|
|
coo_array(([1], ([2],)), shape=(2,))
|
|
|
|
with pytest.raises(ValueError, match='negative axis 0 index: -1'):
|
|
coo_array(([1], ([-1],)))
|
|
|
|
|
|
def test_1d_sparse_constructor():
|
|
empty1d = coo_array((3,))
|
|
res = coo_array(empty1d)
|
|
assert res.shape == (3,)
|
|
assert np.array_equal(res.toarray(), np.zeros((3,)))
|
|
|
|
|
|
def test_1d_tuple_constructor():
|
|
res = coo_array(([9,8], ([1,2],)))
|
|
assert res.shape == (3,)
|
|
assert np.array_equal(res.toarray(), np.array([0, 9, 8]))
|
|
|
|
|
|
def test_1d_tuple_constructor_with_shape():
|
|
res = coo_array(([9,8], ([1,2],)), shape=(4,))
|
|
assert res.shape == (4,)
|
|
assert np.array_equal(res.toarray(), np.array([0, 9, 8, 0]))
|
|
|
|
def test_non_subscriptability():
|
|
coo_2d = coo_array((2, 2))
|
|
|
|
with pytest.raises(TypeError,
|
|
match="'coo_array' object does not support item assignment"):
|
|
coo_2d[0, 0] = 1
|
|
|
|
with pytest.raises(TypeError,
|
|
match="'coo_array' object is not subscriptable"):
|
|
coo_2d[0, :]
|
|
|
|
def test_reshape():
|
|
arr1d = coo_array([1, 0, 3])
|
|
assert arr1d.shape == (3,)
|
|
|
|
col_vec = arr1d.reshape((3, 1))
|
|
assert col_vec.shape == (3, 1)
|
|
assert np.array_equal(col_vec.toarray(), np.array([[1], [0], [3]]))
|
|
|
|
row_vec = arr1d.reshape((1, 3))
|
|
assert row_vec.shape == (1, 3)
|
|
assert np.array_equal(row_vec.toarray(), np.array([[1, 0, 3]]))
|
|
|
|
arr2d = coo_array([[1, 2, 0], [0, 0, 3]])
|
|
assert arr2d.shape == (2, 3)
|
|
|
|
flat = arr2d.reshape((6,))
|
|
assert flat.shape == (6,)
|
|
assert np.array_equal(flat.toarray(), np.array([1, 2, 0, 0, 0, 3]))
|
|
|
|
|
|
def test_nnz():
|
|
arr1d = coo_array([1, 0, 3])
|
|
assert arr1d.shape == (3,)
|
|
assert arr1d.nnz == 2
|
|
|
|
arr2d = coo_array([[1, 2, 0], [0, 0, 3]])
|
|
assert arr2d.shape == (2, 3)
|
|
assert arr2d.nnz == 3
|
|
|
|
|
|
def test_transpose():
|
|
arr1d = coo_array([1, 0, 3]).T
|
|
assert arr1d.shape == (3,)
|
|
assert np.array_equal(arr1d.toarray(), np.array([1, 0, 3]))
|
|
|
|
arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).T
|
|
assert arr2d.shape == (3, 2)
|
|
assert np.array_equal(arr2d.toarray(), np.array([[1, 0], [2, 0], [0, 3]]))
|
|
|
|
|
|
def test_transpose_with_axis():
|
|
arr1d = coo_array([1, 0, 3]).transpose(axes=(0,))
|
|
assert arr1d.shape == (3,)
|
|
assert np.array_equal(arr1d.toarray(), np.array([1, 0, 3]))
|
|
|
|
arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(0, 1))
|
|
assert arr2d.shape == (2, 3)
|
|
assert np.array_equal(arr2d.toarray(), np.array([[1, 2, 0], [0, 0, 3]]))
|
|
|
|
with pytest.raises(ValueError, match="axes don't match matrix dimensions"):
|
|
coo_array([1, 0, 3]).transpose(axes=(0, 1))
|
|
|
|
with pytest.raises(ValueError, match="repeated axis in transpose"):
|
|
coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(1, 1))
|
|
|
|
|
|
def test_1d_row_and_col():
|
|
res = coo_array([1, -2, -3])
|
|
assert np.array_equal(res.col, np.array([0, 1, 2]))
|
|
assert np.array_equal(res.row, np.zeros_like(res.col))
|
|
assert res.row.dtype == res.col.dtype
|
|
assert res.row.flags.writeable is False
|
|
|
|
res.col = [1, 2, 3]
|
|
assert len(res.coords) == 1
|
|
assert np.array_equal(res.col, np.array([1, 2, 3]))
|
|
assert res.row.dtype == res.col.dtype
|
|
|
|
with pytest.raises(ValueError, match="cannot set row attribute"):
|
|
res.row = [1, 2, 3]
|
|
|
|
|
|
def test_1d_toformats():
|
|
res = coo_array([1, -2, -3])
|
|
for f in [res.tocsc, res.tocsr, res.todia, res.tolil, res.tobsr]:
|
|
with pytest.raises(ValueError, match='Cannot convert'):
|
|
f()
|
|
for f in [res.tocoo, res.todok]:
|
|
assert np.array_equal(f().toarray(), res.toarray())
|
|
|
|
|
|
@pytest.mark.parametrize('arg', [1, 2, 4, 5, 8])
|
|
def test_1d_resize(arg: int):
|
|
den = np.array([1, -2, -3])
|
|
res = coo_array(den)
|
|
den.resize(arg, refcheck=False)
|
|
res.resize(arg)
|
|
assert res.shape == den.shape
|
|
assert np.array_equal(res.toarray(), den)
|
|
|
|
|
|
@pytest.mark.parametrize('arg', zip([1, 2, 3, 4], [1, 2, 3, 4]))
|
|
def test_1d_to_2d_resize(arg: tuple[int, int]):
|
|
den = np.array([1, 0, 3])
|
|
res = coo_array(den)
|
|
|
|
den.resize(arg, refcheck=False)
|
|
res.resize(arg)
|
|
assert res.shape == den.shape
|
|
assert np.array_equal(res.toarray(), den)
|
|
|
|
|
|
@pytest.mark.parametrize('arg', [1, 4, 6, 8])
|
|
def test_2d_to_1d_resize(arg: int):
|
|
den = np.array([[1, 0, 3], [4, 0, 0]])
|
|
res = coo_array(den)
|
|
den.resize(arg, refcheck=False)
|
|
res.resize(arg)
|
|
assert res.shape == den.shape
|
|
assert np.array_equal(res.toarray(), den)
|
|
|
|
|
|
def test_sum_duplicates():
|
|
arr1d = coo_array(([2, 2, 2], ([1, 0, 1],)))
|
|
assert arr1d.nnz == 3
|
|
assert np.array_equal(arr1d.toarray(), np.array([2, 4]))
|
|
arr1d.sum_duplicates()
|
|
assert arr1d.nnz == 2
|
|
assert np.array_equal(arr1d.toarray(), np.array([2, 4]))
|
|
|
|
|
|
def test_eliminate_zeros():
|
|
arr1d = coo_array(([0, 0, 1], ([1, 0, 1],)))
|
|
assert arr1d.nnz == 3
|
|
assert arr1d.count_nonzero() == 1
|
|
assert np.array_equal(arr1d.toarray(), np.array([0, 1]))
|
|
arr1d.eliminate_zeros()
|
|
assert arr1d.nnz == 1
|
|
assert arr1d.count_nonzero() == 1
|
|
assert np.array_equal(arr1d.toarray(), np.array([0, 1]))
|
|
assert np.array_equal(arr1d.col, np.array([1]))
|
|
assert np.array_equal(arr1d.row, np.array([0]))
|
|
|
|
|
|
def test_1d_add_dense():
|
|
den_a = np.array([0, -2, -3, 0])
|
|
den_b = np.array([0, 1, 2, 3])
|
|
exp = den_a + den_b
|
|
res = coo_array(den_a) + den_b
|
|
assert type(res) == type(exp)
|
|
assert np.array_equal(res, exp)
|
|
|
|
|
|
def test_1d_add_sparse():
|
|
den_a = np.array([0, -2, -3, 0])
|
|
den_b = np.array([0, 1, 2, 3])
|
|
# Currently this routes through CSR format, so 1d sparse addition
|
|
# isn't supported.
|
|
with pytest.raises(ValueError,
|
|
match='Cannot convert a 1d sparse array'):
|
|
coo_array(den_a) + coo_array(den_b)
|
|
|
|
|
|
def test_1d_matmul_vector():
|
|
den_a = np.array([0, -2, -3, 0])
|
|
den_b = np.array([0, 1, 2, 3])
|
|
exp = den_a @ den_b
|
|
res = coo_array(den_a) @ den_b
|
|
assert np.ndim(res) == 0
|
|
assert np.array_equal(res, exp)
|
|
|
|
|
|
def test_1d_matmul_multivector():
|
|
den = np.array([0, -2, -3, 0])
|
|
other = np.array([[0, 1, 2, 3], [3, 2, 1, 0]]).T
|
|
exp = den @ other
|
|
res = coo_array(den) @ other
|
|
assert type(res) == type(exp)
|
|
assert np.array_equal(res, exp)
|
|
|
|
|
|
def test_2d_matmul_multivector():
|
|
den = np.array([[0, 1, 2, 3], [3, 2, 1, 0]])
|
|
arr2d = coo_array(den)
|
|
exp = den @ den.T
|
|
res = arr2d @ arr2d.T
|
|
assert np.array_equal(res.toarray(), exp)
|
|
|
|
|
|
def test_1d_diagonal():
|
|
den = np.array([0, -2, -3, 0])
|
|
with pytest.raises(ValueError, match='diagonal requires two dimensions'):
|
|
coo_array(den).diagonal()
|