Repository URL to install this package:
|
Version:
2022.10.0 ▾
|
import numpy as np
import pytest
from packaging.version import parse as parse_version
pytestmark = pytest.mark.gpu
import dask.array as da
from dask.array.numpy_compat import _numpy_120
from dask.array.utils import assert_eq
cupy = pytest.importorskip("cupy")
cupy_version = parse_version(cupy.__version__)
@pytest.mark.skipif(not _numpy_120, reason="NEP-35 is not available")
@pytest.mark.skipif(
cupy_version < parse_version("6.1.0"),
reason="Requires CuPy 6.1.0+ (with https://github.com/cupy/cupy/pull/2209)",
)
@pytest.mark.parametrize(
"m,n,chunks,error_type",
[
(20, 10, 10, None), # tall-skinny regular blocks
(20, 10, (3, 10), None), # tall-skinny regular fat layers
(20, 10, ((8, 4, 8), 10), None), # tall-skinny irregular fat layers
(40, 10, ((15, 5, 5, 8, 7), 10), None), # tall-skinny non-uniform chunks (why?)
(128, 2, (16, 2), None), # tall-skinny regular thin layers; recursion_depth=1
(
129,
2,
(16, 2),
None,
), # tall-skinny regular thin layers; recursion_depth=2 --> 17x2
(
130,
2,
(16, 2),
None,
), # tall-skinny regular thin layers; recursion_depth=2 --> 18x2 next
(
131,
2,
(16, 2),
None,
), # tall-skinny regular thin layers; recursion_depth=2 --> 18x2 next
(300, 10, (40, 10), None), # tall-skinny regular thin layers; recursion_depth=2
(300, 10, (30, 10), None), # tall-skinny regular thin layers; recursion_depth=3
(300, 10, (20, 10), None), # tall-skinny regular thin layers; recursion_depth=4
(10, 5, 10, None), # single block tall
(5, 10, 10, None), # single block short
(10, 10, 10, None), # single block square
(10, 40, (10, 10), ValueError), # short-fat regular blocks
(10, 40, (10, 15), ValueError), # short-fat irregular blocks
(
10,
40,
(10, (15, 5, 5, 8, 7)),
ValueError,
), # short-fat non-uniform chunks (why?)
(20, 20, 10, ValueError), # 2x2 regular blocks
],
)
def test_tsqr(m, n, chunks, error_type):
mat = cupy.random.rand(m, n)
data = da.from_array(mat, chunks=chunks, name="A", asarray=False)
# qr
m_q = m
n_q = min(m, n)
m_r = n_q
n_r = n
# svd
m_u = m
n_u = min(m, n)
n_s = n_q
m_vh = n_q
n_vh = n
d_vh = max(m_vh, n_vh) # full matrix returned
if error_type is None:
# test QR
q, r = da.linalg.tsqr(data)
assert_eq((m_q, n_q), q.shape) # shape check
assert_eq((m_r, n_r), r.shape) # shape check
assert_eq(mat, da.dot(q, r)) # accuracy check
assert_eq(cupy.eye(n_q, n_q), da.dot(q.T, q)) # q must be orthonormal
assert_eq(r, np.triu(r.rechunk(r.shape[0]))) # r must be upper triangular
# test SVD
u, s, vh = da.linalg.tsqr(data, compute_svd=True)
s_exact = np.linalg.svd(mat)[1]
assert_eq(s, s_exact) # s must contain the singular values
assert_eq((m_u, n_u), u.shape) # shape check
assert_eq((n_s,), s.shape) # shape check
assert_eq((d_vh, d_vh), vh.shape) # shape check
assert_eq(
np.eye(n_u, n_u), da.dot(u.T, u), check_type=False
) # u must be orthonormal
assert_eq(
np.eye(d_vh, d_vh), da.dot(vh, vh.T), check_type=False
) # vh must be orthonormal
assert_eq(mat, da.dot(da.dot(u, da.diag(s)), vh[:n_q])) # accuracy check
else:
with pytest.raises(error_type):
q, r = da.linalg.tsqr(data)
with pytest.raises(error_type):
u, s, vh = da.linalg.tsqr(data, compute_svd=True)
@pytest.mark.parametrize(
"m_min,n_max,chunks,vary_rows,vary_cols,error_type",
[
(10, 5, (10, 5), True, False, None), # single block tall
(10, 5, (10, 5), False, True, None), # single block tall
(10, 5, (10, 5), True, True, None), # single block tall
(40, 5, (10, 5), True, False, None), # multiple blocks tall
(40, 5, (10, 5), False, True, None), # multiple blocks tall
(40, 5, (10, 5), True, True, None), # multiple blocks tall
(
300,
10,
(40, 10),
True,
False,
None,
), # tall-skinny regular thin layers; recursion_depth=2
(
300,
10,
(30, 10),
True,
False,
None,
), # tall-skinny regular thin layers; recursion_depth=3
(
300,
10,
(20, 10),
True,
False,
None,
), # tall-skinny regular thin layers; recursion_depth=4
(
300,
10,
(40, 10),
False,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=2
(
300,
10,
(30, 10),
False,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=3
(
300,
10,
(20, 10),
False,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=4
(
300,
10,
(40, 10),
True,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=2
(
300,
10,
(30, 10),
True,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=3
(
300,
10,
(20, 10),
True,
True,
None,
), # tall-skinny regular thin layers; recursion_depth=4
],
)
def test_tsqr_uncertain(m_min, n_max, chunks, vary_rows, vary_cols, error_type):
mat = cupy.random.rand(m_min * 2, n_max)
m, n = m_min * 2, n_max
mat[0:m_min, 0] += 1
_c0 = mat[:, 0]
_r0 = mat[0, :]
c0 = da.from_array(_c0, chunks=m_min, name="c", asarray=False)
r0 = da.from_array(_r0, chunks=n_max, name="r", asarray=False)
data = da.from_array(mat, chunks=chunks, name="A", asarray=False)
if vary_rows:
data = data[c0 > 0.5, :]
mat = mat[_c0 > 0.5, :]
m = mat.shape[0]
if vary_cols:
data = data[:, r0 > 0.5]
mat = mat[:, _r0 > 0.5]
n = mat.shape[1]
# qr
m_q = m
n_q = min(m, n)
m_r = n_q
n_r = n
# svd
m_u = m
n_u = min(m, n)
n_s = n_q
m_vh = n_q
n_vh = n
d_vh = max(m_vh, n_vh) # full matrix returned
if error_type is None:
# test QR
q, r = da.linalg.tsqr(data)
q = q.compute() # because uncertainty
r = r.compute()
assert_eq((m_q, n_q), q.shape) # shape check
assert_eq((m_r, n_r), r.shape) # shape check
assert_eq(mat, np.dot(q, r)) # accuracy check
assert_eq(
np.eye(n_q, n_q), np.dot(q.T, q), check_type=False
) # q must be orthonormal
assert_eq(r, np.triu(r)) # r must be upper triangular
# test SVD
u, s, vh = da.linalg.tsqr(data, compute_svd=True)
u = u.compute() # because uncertainty
s = s.compute()
vh = vh.compute()
s_exact = np.linalg.svd(mat)[1]
assert_eq(s, s_exact) # s must contain the singular values
assert_eq((m_u, n_u), u.shape) # shape check
assert_eq((n_s,), s.shape) # shape check
assert_eq((d_vh, d_vh), vh.shape) # shape check
assert_eq(
np.eye(n_u, n_u), np.dot(u.T, u), check_type=False
) # u must be orthonormal
assert_eq(
np.eye(d_vh, d_vh), np.dot(vh, vh.T), check_type=False
) # vh must be orthonormal
assert_eq(
mat, np.dot(np.dot(u, np.diag(s)), vh[:n_q]), check_type=False
) # accuracy check
else:
with pytest.raises(error_type):
q, r = da.linalg.tsqr(data)
with pytest.raises(error_type):
u, s, vh = da.linalg.tsqr(data, compute_svd=True)
@pytest.mark.parametrize(
"m,n,chunks,error_type",
[
(20, 10, 10, ValueError), # tall-skinny regular blocks
(20, 10, (3, 10), ValueError), # tall-skinny regular fat layers
(20, 10, ((8, 4, 8), 10), ValueError), # tall-skinny irregular fat layers
(
40,
10,
((15, 5, 5, 8, 7), 10),
ValueError,
), # tall-skinny non-uniform chunks (why?)
(
128,
2,
(16, 2),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=1
(
129,
2,
(16, 2),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=2 --> 17x2
(
130,
2,
(16, 2),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=2 --> 18x2 next
(
131,
2,
(16, 2),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=2 --> 18x2 next
(
300,
10,
(40, 10),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=2
(
300,
10,
(30, 10),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=3
(
300,
10,
(20, 10),
ValueError,
), # tall-skinny regular thin layers; recursion_depth=4
(10, 5, 10, None), # single block tall
(5, 10, 10, None), # single block short
(10, 10, 10, None), # single block square
(10, 40, (10, 10), None), # short-fat regular blocks
(10, 40, (10, 15), None), # short-fat irregular blocks
(10, 40, (10, (15, 5, 5, 8, 7)), None), # short-fat non-uniform chunks (why?)
(20, 20, 10, ValueError), # 2x2 regular blocks
],
)
def test_sfqr(m, n, chunks, error_type):
mat = np.random.rand(m, n)
data = da.from_array(mat, chunks=chunks, name="A")
m_q = m
n_q = min(m, n)
m_r = n_q
n_r = n
m_qtq = n_q
if error_type is None:
q, r = da.linalg.sfqr(data)
assert_eq((m_q, n_q), q.shape) # shape check
assert_eq((m_r, n_r), r.shape) # shape check
assert_eq(mat, da.dot(q, r)) # accuracy check
assert_eq(np.eye(m_qtq, m_qtq), da.dot(q.T, q)) # q must be orthonormal
assert_eq(r, da.triu(r.rechunk(r.shape[0]))) # r must be upper triangular
else:
with pytest.raises(error_type):
q, r = da.linalg.sfqr(data)
@pytest.mark.skipif(not _numpy_120, reason="NEP-35 is not available")
@pytest.mark.parametrize("iscomplex", [False, True])
@pytest.mark.parametrize(("nrow", "ncol", "chunk"), [(20, 10, 5), (100, 10, 10)])
def test_lstsq(nrow, ncol, chunk, iscomplex):
cupy.random.seed(1)
A = cupy.random.randint(1, 20, (nrow, ncol))
b = cupy.random.randint(1, 20, nrow)
if iscomplex:
A = A + 1.0j * cupy.random.randint(1, 20, A.shape)
b = b + 1.0j * cupy.random.randint(1, 20, b.shape)
dA = da.from_array(A, (chunk, ncol))
db = da.from_array(b, chunk)
x, r, rank, s = cupy.linalg.lstsq(A, b, rcond=-1)
dx, dr, drank, ds = da.linalg.lstsq(dA, db)
assert_eq(dx, x)
assert_eq(dr, r)
assert drank.compute() == rank
assert_eq(ds, s)
# reduce rank causes multicollinearity, only compare rank
A[:, 1] = A[:, 2]
dA = da.from_array(A, (chunk, ncol))
db = da.from_array(b, chunk)
x, r, rank, s = cupy.linalg.lstsq(
A, b, rcond=cupy.finfo(cupy.double).eps * max(nrow, ncol)
)
assert rank == ncol - 1
dx, dr, drank, ds = da.linalg.lstsq(dA, db)
assert drank.compute() == rank
# 2D case
A = cupy.random.randint(1, 20, (nrow, ncol))
b2D = cupy.random.randint(1, 20, (nrow, ncol // 2))
if iscomplex:
A = A + 1.0j * cupy.random.randint(1, 20, A.shape)
b2D = b2D + 1.0j * cupy.random.randint(1, 20, b2D.shape)
dA = da.from_array(A, (chunk, ncol))
db2D = da.from_array(b2D, (chunk, ncol // 2))
x, r, rank, s = cupy.linalg.lstsq(A, b2D, rcond=-1)
dx, dr, drank, ds = da.linalg.lstsq(dA, db2D)
assert_eq(dx, x)
assert_eq(dr, r)
assert drank.compute() == rank
assert_eq(ds, s)
def _get_symmat(size):
cupy.random.seed(1)
A = cupy.random.randint(1, 21, (size, size))
lA = cupy.tril(A)
return lA.dot(lA.T)
@pytest.mark.parametrize(("shape", "chunk"), [(20, 10), (12, 3), (30, 3), (30, 6)])
def test_cholesky(shape, chunk):
scipy_linalg = pytest.importorskip("scipy.linalg")
A = _get_symmat(shape)
dA = da.from_array(A, (chunk, chunk))
# Need to take the transpose because default in `cupy.linalg.cholesky` is
# to return lower triangle
assert_eq(
da.linalg.cholesky(dA),
cupy.linalg.cholesky(A).T,
check_graph=False,
check_chunks=False,
)
assert_eq(
da.linalg.cholesky(dA, lower=True).map_blocks(cupy.asnumpy),
scipy_linalg.cholesky(cupy.asnumpy(A), lower=True),
check_graph=False,
check_chunks=False,
)