Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
from subprocess import call, PIPE, Popen
import sys
import re
import pytest
from numpy.testing import assert_
from numpy.compat import asbytes
from scipy.linalg import _flapack as flapack
# XXX: this is copied from numpy trunk. Can be removed when we will depend on
# numpy 1.3
class FindDependenciesLdd:
def __init__(self):
self.cmd = ['ldd']
try:
call(self.cmd, stdout=PIPE, stderr=PIPE)
except OSError:
raise RuntimeError("command %s cannot be run" % self.cmd)
def get_dependencies(self, file):
p = Popen(self.cmd + [file], stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if not (p.returncode == 0):
raise RuntimeError("Failed to check dependencies for %s" % file)
return stdout
def grep_dependencies(self, file, deps):
stdout = self.get_dependencies(file)
rdeps = dict([(asbytes(dep), re.compile(asbytes(dep))) for dep in deps])
founds = []
for l in stdout.splitlines():
for k, v in rdeps.items():
if v.search(l):
founds.append(k)
return founds
class TestF77Mismatch(object):
@pytest.mark.skipif(not(sys.platform[:5] == 'linux'),
reason="Skipping fortran compiler mismatch on non Linux platform")
def test_lapack(self):
f = FindDependenciesLdd()
deps = f.grep_dependencies(flapack.__file__,
['libg2c', 'libgfortran'])
assert_(not (len(deps) > 1),
"""Both g77 and gfortran runtimes linked in scipy.linalg.flapack ! This is
likely to cause random crashes and wrong results. See numpy INSTALL.rst.txt for
more information.""")

View file

@ -0,0 +1,120 @@
import numpy as np
from numpy.testing import (assert_allclose,
assert_equal)
import scipy.linalg.cython_blas as blas
class TestDGEMM(object):
def test_transposes(self):
a = np.arange(12, dtype='d').reshape((3, 4))[:2,:2]
b = np.arange(1, 13, dtype='d').reshape((4, 3))[:2,:2]
c = np.empty((2, 4))[:2,:2]
blas._test_dgemm(1., a, b, 0., c)
assert_allclose(c, a.dot(b))
blas._test_dgemm(1., a.T, b, 0., c)
assert_allclose(c, a.T.dot(b))
blas._test_dgemm(1., a, b.T, 0., c)
assert_allclose(c, a.dot(b.T))
blas._test_dgemm(1., a.T, b.T, 0., c)
assert_allclose(c, a.T.dot(b.T))
blas._test_dgemm(1., a, b, 0., c.T)
assert_allclose(c, a.dot(b).T)
blas._test_dgemm(1., a.T, b, 0., c.T)
assert_allclose(c, a.T.dot(b).T)
blas._test_dgemm(1., a, b.T, 0., c.T)
assert_allclose(c, a.dot(b.T).T)
blas._test_dgemm(1., a.T, b.T, 0., c.T)
assert_allclose(c, a.T.dot(b.T).T)
def test_shapes(self):
a = np.arange(6, dtype='d').reshape((3, 2))
b = np.arange(-6, 2, dtype='d').reshape((2, 4))
c = np.empty((3, 4))
blas._test_dgemm(1., a, b, 0., c)
assert_allclose(c, a.dot(b))
blas._test_dgemm(1., b.T, a.T, 0., c.T)
assert_allclose(c, b.T.dot(a.T).T)
class TestWfuncPointers(object):
""" Test the function pointers that are expected to fail on
Mac OS X without the additional entry statement in their definitions
in fblas_l1.pyf.src. """
def test_complex_args(self):
cx = np.array([.5 + 1.j, .25 - .375j, 12.5 - 4.j], np.complex64)
cy = np.array([.8 + 2.j, .875 - .625j, -1. + 2.j], np.complex64)
assert_allclose(blas._test_cdotc(cx, cy),
-17.6468753815+21.3718757629j, 5)
assert_allclose(blas._test_cdotu(cx, cy),
-6.11562538147+30.3156242371j, 5)
assert_equal(blas._test_icamax(cx), 3)
assert_allclose(blas._test_scasum(cx), 18.625, 5)
assert_allclose(blas._test_scnrm2(cx), 13.1796483994, 5)
assert_allclose(blas._test_cdotc(cx[::2], cy[::2]),
-18.1000003815+21.2000007629j, 5)
assert_allclose(blas._test_cdotu(cx[::2], cy[::2]),
-6.10000038147+30.7999992371j, 5)
assert_allclose(blas._test_scasum(cx[::2]), 18., 5)
assert_allclose(blas._test_scnrm2(cx[::2]), 13.1719398499, 5)
def test_double_args(self):
x = np.array([5., -3, -.5], np.float64)
y = np.array([2, 1, .5], np.float64)
assert_allclose(blas._test_dasum(x), 8.5, 10)
assert_allclose(blas._test_ddot(x, y), 6.75, 10)
assert_allclose(blas._test_dnrm2(x), 5.85234975815, 10)
assert_allclose(blas._test_dasum(x[::2]), 5.5, 10)
assert_allclose(blas._test_ddot(x[::2], y[::2]), 9.75, 10)
assert_allclose(blas._test_dnrm2(x[::2]), 5.0249376297, 10)
assert_equal(blas._test_idamax(x), 1)
def test_float_args(self):
x = np.array([5., -3, -.5], np.float32)
y = np.array([2, 1, .5], np.float32)
assert_equal(blas._test_isamax(x), 1)
assert_allclose(blas._test_sasum(x), 8.5, 5)
assert_allclose(blas._test_sdot(x, y), 6.75, 5)
assert_allclose(blas._test_snrm2(x), 5.85234975815, 5)
assert_allclose(blas._test_sasum(x[::2]), 5.5, 5)
assert_allclose(blas._test_sdot(x[::2], y[::2]), 9.75, 5)
assert_allclose(blas._test_snrm2(x[::2]), 5.0249376297, 5)
def test_double_complex_args(self):
cx = np.array([.5 + 1.j, .25 - .375j, 13. - 4.j], np.complex128)
cy = np.array([.875 + 2.j, .875 - .625j, -1. + 2.j], np.complex128)
assert_equal(blas._test_izamax(cx), 3)
assert_allclose(blas._test_zdotc(cx, cy), -18.109375+22.296875j, 10)
assert_allclose(blas._test_zdotu(cx, cy), -6.578125+31.390625j, 10)
assert_allclose(blas._test_zdotc(cx[::2], cy[::2]),
-18.5625+22.125j, 10)
assert_allclose(blas._test_zdotu(cx[::2], cy[::2]),
-6.5625+31.875j, 10)

View file

@ -0,0 +1,17 @@
from numpy.testing import assert_allclose
from scipy.linalg import cython_lapack as cython_lapack
from scipy.linalg import lapack
class TestLamch(object):
def test_slamch(self):
for c in [b'e', b's', b'b', b'p', b'n', b'r', b'm', b'u', b'l', b'o']:
assert_allclose(cython_lapack._test_slamch(c),
lapack.slamch(c))
def test_dlamch(self):
for c in [b'e', b's', b'b', b'p', b'n', b'r', b'm', b'u', b'l', b'o']:
assert_allclose(cython_lapack._test_dlamch(c),
lapack.dlamch(c))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,202 @@
from numpy.testing import assert_array_almost_equal, assert_array_equal
from pytest import raises as assert_raises
from numpy import array, transpose, dot, conjugate, zeros_like, empty
from numpy.random import random
from scipy.linalg import cholesky, cholesky_banded, cho_solve_banded, \
cho_factor, cho_solve
from scipy.linalg._testutils import assert_no_overwrite
class TestCholesky(object):
def test_simple(self):
a = [[8, 2, 3], [2, 9, 3], [3, 3, 6]]
c = cholesky(a)
assert_array_almost_equal(dot(transpose(c), c), a)
c = transpose(c)
a = dot(c, transpose(c))
assert_array_almost_equal(cholesky(a, lower=1), c)
def test_check_finite(self):
a = [[8, 2, 3], [2, 9, 3], [3, 3, 6]]
c = cholesky(a, check_finite=False)
assert_array_almost_equal(dot(transpose(c), c), a)
c = transpose(c)
a = dot(c, transpose(c))
assert_array_almost_equal(cholesky(a, lower=1, check_finite=False), c)
def test_simple_complex(self):
m = array([[3+1j, 3+4j, 5], [0, 2+2j, 2+7j], [0, 0, 7+4j]])
a = dot(transpose(conjugate(m)), m)
c = cholesky(a)
a1 = dot(transpose(conjugate(c)), c)
assert_array_almost_equal(a, a1)
c = transpose(c)
a = dot(c, transpose(conjugate(c)))
assert_array_almost_equal(cholesky(a, lower=1), c)
def test_random(self):
n = 20
for k in range(2):
m = random([n, n])
for i in range(n):
m[i, i] = 20*(.1+m[i, i])
a = dot(transpose(m), m)
c = cholesky(a)
a1 = dot(transpose(c), c)
assert_array_almost_equal(a, a1)
c = transpose(c)
a = dot(c, transpose(c))
assert_array_almost_equal(cholesky(a, lower=1), c)
def test_random_complex(self):
n = 20
for k in range(2):
m = random([n, n])+1j*random([n, n])
for i in range(n):
m[i, i] = 20*(.1+abs(m[i, i]))
a = dot(transpose(conjugate(m)), m)
c = cholesky(a)
a1 = dot(transpose(conjugate(c)), c)
assert_array_almost_equal(a, a1)
c = transpose(c)
a = dot(c, transpose(conjugate(c)))
assert_array_almost_equal(cholesky(a, lower=1), c)
class TestCholeskyBanded(object):
"""Tests for cholesky_banded() and cho_solve_banded."""
def test_check_finite(self):
# Symmetric positive definite banded matrix `a`
a = array([[4.0, 1.0, 0.0, 0.0],
[1.0, 4.0, 0.5, 0.0],
[0.0, 0.5, 4.0, 0.2],
[0.0, 0.0, 0.2, 4.0]])
# Banded storage form of `a`.
ab = array([[-1.0, 1.0, 0.5, 0.2],
[4.0, 4.0, 4.0, 4.0]])
c = cholesky_banded(ab, lower=False, check_finite=False)
ufac = zeros_like(a)
ufac[list(range(4)), list(range(4))] = c[-1]
ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
assert_array_almost_equal(a, dot(ufac.T, ufac))
b = array([0.0, 0.5, 4.2, 4.2])
x = cho_solve_banded((c, False), b, check_finite=False)
assert_array_almost_equal(x, [0.0, 0.0, 1.0, 1.0])
def test_upper_real(self):
# Symmetric positive definite banded matrix `a`
a = array([[4.0, 1.0, 0.0, 0.0],
[1.0, 4.0, 0.5, 0.0],
[0.0, 0.5, 4.0, 0.2],
[0.0, 0.0, 0.2, 4.0]])
# Banded storage form of `a`.
ab = array([[-1.0, 1.0, 0.5, 0.2],
[4.0, 4.0, 4.0, 4.0]])
c = cholesky_banded(ab, lower=False)
ufac = zeros_like(a)
ufac[list(range(4)), list(range(4))] = c[-1]
ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
assert_array_almost_equal(a, dot(ufac.T, ufac))
b = array([0.0, 0.5, 4.2, 4.2])
x = cho_solve_banded((c, False), b)
assert_array_almost_equal(x, [0.0, 0.0, 1.0, 1.0])
def test_upper_complex(self):
# Hermitian positive definite banded matrix `a`
a = array([[4.0, 1.0, 0.0, 0.0],
[1.0, 4.0, 0.5, 0.0],
[0.0, 0.5, 4.0, -0.2j],
[0.0, 0.0, 0.2j, 4.0]])
# Banded storage form of `a`.
ab = array([[-1.0, 1.0, 0.5, -0.2j],
[4.0, 4.0, 4.0, 4.0]])
c = cholesky_banded(ab, lower=False)
ufac = zeros_like(a)
ufac[list(range(4)), list(range(4))] = c[-1]
ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
assert_array_almost_equal(a, dot(ufac.conj().T, ufac))
b = array([0.0, 0.5, 4.0-0.2j, 0.2j + 4.0])
x = cho_solve_banded((c, False), b)
assert_array_almost_equal(x, [0.0, 0.0, 1.0, 1.0])
def test_lower_real(self):
# Symmetric positive definite banded matrix `a`
a = array([[4.0, 1.0, 0.0, 0.0],
[1.0, 4.0, 0.5, 0.0],
[0.0, 0.5, 4.0, 0.2],
[0.0, 0.0, 0.2, 4.0]])
# Banded storage form of `a`.
ab = array([[4.0, 4.0, 4.0, 4.0],
[1.0, 0.5, 0.2, -1.0]])
c = cholesky_banded(ab, lower=True)
lfac = zeros_like(a)
lfac[list(range(4)), list(range(4))] = c[0]
lfac[(1, 2, 3), (0, 1, 2)] = c[1, :3]
assert_array_almost_equal(a, dot(lfac, lfac.T))
b = array([0.0, 0.5, 4.2, 4.2])
x = cho_solve_banded((c, True), b)
assert_array_almost_equal(x, [0.0, 0.0, 1.0, 1.0])
def test_lower_complex(self):
# Hermitian positive definite banded matrix `a`
a = array([[4.0, 1.0, 0.0, 0.0],
[1.0, 4.0, 0.5, 0.0],
[0.0, 0.5, 4.0, -0.2j],
[0.0, 0.0, 0.2j, 4.0]])
# Banded storage form of `a`.
ab = array([[4.0, 4.0, 4.0, 4.0],
[1.0, 0.5, 0.2j, -1.0]])
c = cholesky_banded(ab, lower=True)
lfac = zeros_like(a)
lfac[list(range(4)), list(range(4))] = c[0]
lfac[(1, 2, 3), (0, 1, 2)] = c[1, :3]
assert_array_almost_equal(a, dot(lfac, lfac.conj().T))
b = array([0.0, 0.5j, 3.8j, 3.8])
x = cho_solve_banded((c, True), b)
assert_array_almost_equal(x, [0.0, 0.0, 1.0j, 1.0])
class TestOverwrite(object):
def test_cholesky(self):
assert_no_overwrite(cholesky, [(3, 3)])
def test_cho_factor(self):
assert_no_overwrite(cho_factor, [(3, 3)])
def test_cho_solve(self):
x = array([[2, -1, 0], [-1, 2, -1], [0, -1, 2]])
xcho = cho_factor(x)
assert_no_overwrite(lambda b: cho_solve(xcho, b), [(3,)])
def test_cholesky_banded(self):
assert_no_overwrite(cholesky_banded, [(2, 3)])
def test_cho_solve_banded(self):
x = array([[0, -1, -1], [2, 2, 2]])
xcho = cholesky_banded(x)
assert_no_overwrite(lambda b: cho_solve_banded((xcho, False), b),
[(3,)])
class TestEmptyArray(object):
def test_cho_factor_empty_square(self):
a = empty((0, 0))
b = array([])
c = array([[]])
d = []
e = [[]]
x, _ = cho_factor(a)
assert_array_equal(x, a)
for x in ([b, c, d, e]):
assert_raises(ValueError, cho_factor, x)

View file

@ -0,0 +1,154 @@
import pytest
import numpy as np
from numpy.random import seed
from numpy.testing import assert_allclose
from scipy.linalg.lapack import _compute_lwork
from scipy.stats import ortho_group, unitary_group
from scipy.linalg import cossin, get_lapack_funcs
REAL_DTYPES = (np.float32, np.float64)
COMPLEX_DTYPES = (np.complex64, np.complex128)
DTYPES = REAL_DTYPES + COMPLEX_DTYPES
@pytest.mark.parametrize('dtype_', DTYPES)
@pytest.mark.parametrize('m, p, q',
[
(2, 1, 1),
(3, 2, 1),
(3, 1, 2),
(4, 2, 2),
(4, 1, 2),
(40, 12, 20),
(40, 30, 1),
(40, 1, 30),
(100, 50, 1),
(100, 50, 50),
])
@pytest.mark.parametrize('swap_sign', [True, False])
def test_cossin(dtype_, m, p, q, swap_sign):
seed(1234)
if dtype_ in COMPLEX_DTYPES:
x = np.array(unitary_group.rvs(m), dtype=dtype_)
else:
x = np.array(ortho_group.rvs(m), dtype=dtype_)
u, cs, vh = cossin(x, p, q,
swap_sign=swap_sign)
assert_allclose(x, u @ cs @ vh, rtol=0., atol=m*1e3*np.finfo(dtype_).eps)
assert u.dtype == dtype_
# Test for float32 or float 64
assert cs.dtype == np.real(u).dtype
assert vh.dtype == dtype_
u, cs, vh = cossin([x[:p, :q], x[:p, q:], x[p:, :q], x[p:, q:]],
swap_sign=swap_sign)
assert_allclose(x, u @ cs @ vh, rtol=0., atol=m*1e3*np.finfo(dtype_).eps)
assert u.dtype == dtype_
assert cs.dtype == np.real(u).dtype
assert vh.dtype == dtype_
_, cs2, vh2 = cossin(x, p, q,
compute_u=False,
swap_sign=swap_sign)
assert_allclose(cs, cs2, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(vh, vh2, rtol=0., atol=10*np.finfo(dtype_).eps)
u2, cs2, _ = cossin(x, p, q,
compute_vh=False,
swap_sign=swap_sign)
assert_allclose(u, u2, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(cs, cs2, rtol=0., atol=10*np.finfo(dtype_).eps)
_, cs2, _ = cossin(x, p, q,
compute_u=False,
compute_vh=False,
swap_sign=swap_sign)
assert_allclose(cs, cs2, rtol=0., atol=10*np.finfo(dtype_).eps)
def test_cossin_mixed_types():
seed(1234)
x = np.array(ortho_group.rvs(4), dtype=np.float64)
u, cs, vh = cossin([x[:2, :2],
np.array(x[:2, 2:], dtype=np.complex128),
x[2:, :2],
x[2:, 2:]])
assert u.dtype == np.complex128
assert cs.dtype == np.float64
assert vh.dtype == np.complex128
assert_allclose(x, u @ cs @ vh, rtol=0.,
atol=1e4 * np.finfo(np.complex128).eps)
def test_cossin_error_incorrect_subblocks():
with pytest.raises(ValueError, match="be due to missing p, q arguments."):
cossin(([1, 2], [3, 4, 5], [6, 7], [8, 9, 10]))
def test_cossin_error_empty_subblocks():
with pytest.raises(ValueError, match="x11.*empty"):
cossin(([], [], [], []))
with pytest.raises(ValueError, match="x12.*empty"):
cossin(([1, 2], [], [6, 7], [8, 9, 10]))
with pytest.raises(ValueError, match="x21.*empty"):
cossin(([1, 2], [3, 4, 5], [], [8, 9, 10]))
with pytest.raises(ValueError, match="x22.*empty"):
cossin(([1, 2], [3, 4, 5], [2], []))
def test_cossin_error_missing_partitioning():
with pytest.raises(ValueError, match=".*exactly four arrays.* got 2"):
cossin(unitary_group.rvs(2))
with pytest.raises(ValueError, match=".*might be due to missing p, q"):
cossin(unitary_group.rvs(4))
def test_cossin_error_non_iterable():
with pytest.raises(ValueError, match="containing the subblocks of X"):
cossin(12j)
def test_cossin_error_non_square():
with pytest.raises(ValueError, match="only supports square"):
cossin(np.array([[1, 2]]), 1, 1)
def test_cossin_error_partitioning():
x = np.array(ortho_group.rvs(4), dtype=np.float64)
with pytest.raises(ValueError, match="invalid p=0.*0<p<4.*"):
cossin(x, 0, 1)
with pytest.raises(ValueError, match="invalid p=4.*0<p<4.*"):
cossin(x, 4, 1)
with pytest.raises(ValueError, match="invalid q=-2.*0<q<4.*"):
cossin(x, 1, -2)
with pytest.raises(ValueError, match="invalid q=5.*0<q<4.*"):
cossin(x, 1, 5)
@pytest.mark.parametrize("dtype_", DTYPES)
def test_cossin_separate(dtype_):
m, p, q = 250, 80, 170
pfx = 'or' if dtype_ in REAL_DTYPES else 'un'
X = ortho_group.rvs(m) if pfx == 'or' else unitary_group.rvs(m)
X = np.array(X, dtype=dtype_)
drv, dlw = get_lapack_funcs((pfx + 'csd', pfx + 'csd_lwork'),[X])
lwval = _compute_lwork(dlw, m, p, q)
lwvals = {'lwork': lwval} if pfx == 'or' else dict(zip(['lwork',
'lrwork'],
lwval))
*_, theta, u1, u2, v1t, v2t, _ = \
drv(X[:p, :q], X[:p, q:], X[p:, :q], X[p:, q:], **lwvals)
(u1_2, u2_2), theta2, (v1t_2, v2t_2) = cossin(X, p, q, separate=True)
assert_allclose(u1_2, u1, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(u2_2, u2, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(v1t_2, v1t, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(v2t_2, v2t, rtol=0., atol=10*np.finfo(dtype_).eps)
assert_allclose(theta2, theta, rtol=0., atol=10*np.finfo(dtype_).eps)

View file

@ -0,0 +1,135 @@
import itertools
from numpy.testing import assert_array_almost_equal, assert_allclose, assert_
from numpy import (array, eye, zeros, empty_like, empty, tril_indices_from,
tril, triu_indices_from, spacing, float32, float64,
complex64, complex128)
from numpy.random import rand, randint, seed
from scipy.linalg import ldl
from pytest import raises as assert_raises, warns
from numpy import ComplexWarning
def test_args():
A = eye(3)
# Nonsquare array
assert_raises(ValueError, ldl, A[:, :2])
# Complex matrix with imaginary diagonal entries with "hermitian=True"
with warns(ComplexWarning):
ldl(A*1j)
def test_empty_array():
a = empty((0, 0), dtype=complex)
l, d, p = ldl(empty((0, 0)))
assert_array_almost_equal(l, empty_like(a))
assert_array_almost_equal(d, empty_like(a))
assert_array_almost_equal(p, array([], dtype=int))
def test_simple():
a = array([[-0.39-0.71j, 5.14-0.64j, -7.86-2.96j, 3.80+0.92j],
[5.14-0.64j, 8.86+1.81j, -3.52+0.58j, 5.32-1.59j],
[-7.86-2.96j, -3.52+0.58j, -2.83-0.03j, -1.54-2.86j],
[3.80+0.92j, 5.32-1.59j, -1.54-2.86j, -0.56+0.12j]])
b = array([[5., 10, 1, 18],
[10., 2, 11, 1],
[1., 11, 19, 9],
[18., 1, 9, 0]])
c = array([[52., 97, 112, 107, 50],
[97., 114, 89, 98, 13],
[112., 89, 64, 33, 6],
[107., 98, 33, 60, 73],
[50., 13, 6, 73, 77]])
d = array([[2., 2, -4, 0, 4],
[2., -2, -2, 10, -8],
[-4., -2, 6, -8, -4],
[0., 10, -8, 6, -6],
[4., -8, -4, -6, 10]])
e = array([[-1.36+0.00j, 0+0j, 0+0j, 0+0j],
[1.58-0.90j, -8.87+0j, 0+0j, 0+0j],
[2.21+0.21j, -1.84+0.03j, -4.63+0j, 0+0j],
[3.91-1.50j, -1.78-1.18j, 0.11-0.11j, -1.84+0.00j]])
for x in (b, c, d):
l, d, p = ldl(x)
assert_allclose(l.dot(d).dot(l.T), x, atol=spacing(1000.), rtol=0)
u, d, p = ldl(x, lower=False)
assert_allclose(u.dot(d).dot(u.T), x, atol=spacing(1000.), rtol=0)
l, d, p = ldl(a, hermitian=False)
assert_allclose(l.dot(d).dot(l.T), a, atol=spacing(1000.), rtol=0)
u, d, p = ldl(a, lower=False, hermitian=False)
assert_allclose(u.dot(d).dot(u.T), a, atol=spacing(1000.), rtol=0)
# Use upper part for the computation and use the lower part for comparison
l, d, p = ldl(e.conj().T, lower=0)
assert_allclose(tril(l.dot(d).dot(l.conj().T)-e), zeros((4, 4)),
atol=spacing(1000.), rtol=0)
def test_permutations():
seed(1234)
for _ in range(10):
n = randint(1, 100)
# Random real/complex array
x = rand(n, n) if randint(2) else rand(n, n) + rand(n, n)*1j
x = x + x.conj().T
x += eye(n)*randint(5, 1e6)
l_ind = tril_indices_from(x, k=-1)
u_ind = triu_indices_from(x, k=1)
# Test whether permutations lead to a triangular array
u, d, p = ldl(x, lower=0)
# lower part should be zero
assert_(not any(u[p, :][l_ind]), 'Spin {} failed'.format(_))
l, d, p = ldl(x, lower=1)
# upper part should be zero
assert_(not any(l[p, :][u_ind]), 'Spin {} failed'.format(_))
def test_ldl_type_size_combinations():
seed(1234)
sizes = [30, 750]
real_dtypes = [float32, float64]
complex_dtypes = [complex64, complex128]
for n, dtype in itertools.product(sizes, real_dtypes):
msg = ("Failed for size: {}, dtype: {}".format(n, dtype))
x = rand(n, n).astype(dtype)
x = x + x.T
x += eye(n, dtype=dtype)*dtype(randint(5, 1e6))
l, d1, p = ldl(x)
u, d2, p = ldl(x, lower=0)
rtol = 1e-4 if dtype is float32 else 1e-10
assert_allclose(l.dot(d1).dot(l.T), x, rtol=rtol, err_msg=msg)
assert_allclose(u.dot(d2).dot(u.T), x, rtol=rtol, err_msg=msg)
for n, dtype in itertools.product(sizes, complex_dtypes):
msg1 = ("Her failed for size: {}, dtype: {}".format(n, dtype))
msg2 = ("Sym failed for size: {}, dtype: {}".format(n, dtype))
# Complex hermitian upper/lower
x = (rand(n, n)+1j*rand(n, n)).astype(dtype)
x = x+x.conj().T
x += eye(n, dtype=dtype)*dtype(randint(5, 1e6))
l, d1, p = ldl(x)
u, d2, p = ldl(x, lower=0)
rtol = 1e-4 if dtype is complex64 else 1e-10
assert_allclose(l.dot(d1).dot(l.conj().T), x, rtol=rtol, err_msg=msg1)
assert_allclose(u.dot(d2).dot(u.conj().T), x, rtol=rtol, err_msg=msg1)
# Complex symmetric upper/lower
x = (rand(n, n)+1j*rand(n, n)).astype(dtype)
x = x+x.T
x += eye(n, dtype=dtype)*dtype(randint(5, 1e6))
l, d1, p = ldl(x, hermitian=0)
u, d2, p = ldl(x, lower=0, hermitian=0)
assert_allclose(l.dot(d1).dot(l.T), x, rtol=rtol, err_msg=msg2)
assert_allclose(u.dot(d2).dot(u.T), x, rtol=rtol, err_msg=msg2)

View file

@ -0,0 +1,90 @@
import numpy as np
from numpy.linalg import norm
from numpy.testing import (assert_, assert_allclose, assert_equal)
from scipy.linalg import polar, eigh
diag2 = np.array([[2, 0], [0, 3]])
a13 = np.array([[1, 2, 2]])
precomputed_cases = [
[[[0]], 'right', [[1]], [[0]]],
[[[0]], 'left', [[1]], [[0]]],
[[[9]], 'right', [[1]], [[9]]],
[[[9]], 'left', [[1]], [[9]]],
[diag2, 'right', np.eye(2), diag2],
[diag2, 'left', np.eye(2), diag2],
[a13, 'right', a13/norm(a13[0]), a13.T.dot(a13)/norm(a13[0])],
]
verify_cases = [
[[1, 2], [3, 4]],
[[1, 2, 3]],
[[1], [2], [3]],
[[1, 2, 3], [3, 4, 0]],
[[1, 2], [3, 4], [5, 5]],
[[1, 2], [3, 4+5j]],
[[1, 2, 3j]],
[[1], [2], [3j]],
[[1, 2, 3+2j], [3, 4-1j, -4j]],
[[1, 2], [3-2j, 4+0.5j], [5, 5]],
[[10000, 10, 1], [-1, 2, 3j], [0, 1, 2]],
]
def check_precomputed_polar(a, side, expected_u, expected_p):
# Compare the result of the polar decomposition to a
# precomputed result.
u, p = polar(a, side=side)
assert_allclose(u, expected_u, atol=1e-15)
assert_allclose(p, expected_p, atol=1e-15)
def verify_polar(a):
# Compute the polar decomposition, and then verify that
# the result has all the expected properties.
product_atol = np.sqrt(np.finfo(float).eps)
aa = np.asarray(a)
m, n = aa.shape
u, p = polar(a, side='right')
assert_equal(u.shape, (m, n))
assert_equal(p.shape, (n, n))
# a = up
assert_allclose(u.dot(p), a, atol=product_atol)
if m >= n:
assert_allclose(u.conj().T.dot(u), np.eye(n), atol=1e-15)
else:
assert_allclose(u.dot(u.conj().T), np.eye(m), atol=1e-15)
# p is Hermitian positive semidefinite.
assert_allclose(p.conj().T, p)
evals = eigh(p, eigvals_only=True)
nonzero_evals = evals[abs(evals) > 1e-14]
assert_((nonzero_evals >= 0).all())
u, p = polar(a, side='left')
assert_equal(u.shape, (m, n))
assert_equal(p.shape, (m, m))
# a = pu
assert_allclose(p.dot(u), a, atol=product_atol)
if m >= n:
assert_allclose(u.conj().T.dot(u), np.eye(n), atol=1e-15)
else:
assert_allclose(u.dot(u.conj().T), np.eye(m), atol=1e-15)
# p is Hermitian positive semidefinite.
assert_allclose(p.conj().T, p)
evals = eigh(p, eigvals_only=True)
nonzero_evals = evals[abs(evals) > 1e-14]
assert_((nonzero_evals >= 0).all())
def test_precomputed_cases():
for a, side, expected_u, expected_p in precomputed_cases:
check_precomputed_polar(a, side, expected_u, expected_p)
def test_verify_cases():
for a in verify_cases:
verify_polar(a)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,607 @@
# Test interfaces to fortran blas.
#
# The tests are more of interface than they are of the underlying blas.
# Only very small matrices checked -- N=3 or so.
#
# !! Complex calculations really aren't checked that carefully.
# !! Only real valued complex numbers are used in tests.
from numpy import float32, float64, complex64, complex128, arange, array, \
zeros, shape, transpose, newaxis, common_type, conjugate
from scipy.linalg import _fblas as fblas
from numpy.testing import assert_array_equal, \
assert_allclose, assert_array_almost_equal, assert_
import pytest
# decimal accuracy to require between Python and LAPACK/BLAS calculations
accuracy = 5
# Since numpy.dot likely uses the same blas, use this routine
# to check.
def matrixmultiply(a, b):
if len(b.shape) == 1:
b_is_vector = True
b = b[:, newaxis]
else:
b_is_vector = False
assert_(a.shape[1] == b.shape[0])
c = zeros((a.shape[0], b.shape[1]), common_type(a, b))
for i in range(a.shape[0]):
for j in range(b.shape[1]):
s = 0
for k in range(a.shape[1]):
s += a[i, k] * b[k, j]
c[i, j] = s
if b_is_vector:
c = c.reshape((a.shape[0],))
return c
##################################################
# Test blas ?axpy
class BaseAxpy(object):
''' Mixin class for axpy tests '''
def test_default_a(self):
x = arange(3., dtype=self.dtype)
y = arange(3., dtype=x.dtype)
real_y = x*1.+y
y = self.blas_func(x, y)
assert_array_equal(real_y, y)
def test_simple(self):
x = arange(3., dtype=self.dtype)
y = arange(3., dtype=x.dtype)
real_y = x*3.+y
y = self.blas_func(x, y, a=3.)
assert_array_equal(real_y, y)
def test_x_stride(self):
x = arange(6., dtype=self.dtype)
y = zeros(3, x.dtype)
y = arange(3., dtype=x.dtype)
real_y = x[::2]*3.+y
y = self.blas_func(x, y, a=3., n=3, incx=2)
assert_array_equal(real_y, y)
def test_y_stride(self):
x = arange(3., dtype=self.dtype)
y = zeros(6, x.dtype)
real_y = x*3.+y[::2]
y = self.blas_func(x, y, a=3., n=3, incy=2)
assert_array_equal(real_y, y[::2])
def test_x_and_y_stride(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
real_y = x[::4]*3.+y[::2]
y = self.blas_func(x, y, a=3., n=3, incx=4, incy=2)
assert_array_equal(real_y, y[::2])
def test_x_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=4, incx=5)
def test_y_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=3, incy=5)
try:
class TestSaxpy(BaseAxpy):
blas_func = fblas.saxpy
dtype = float32
except AttributeError:
class TestSaxpy:
pass
class TestDaxpy(BaseAxpy):
blas_func = fblas.daxpy
dtype = float64
try:
class TestCaxpy(BaseAxpy):
blas_func = fblas.caxpy
dtype = complex64
except AttributeError:
class TestCaxpy:
pass
class TestZaxpy(BaseAxpy):
blas_func = fblas.zaxpy
dtype = complex128
##################################################
# Test blas ?scal
class BaseScal(object):
''' Mixin class for scal testing '''
def test_simple(self):
x = arange(3., dtype=self.dtype)
real_x = x*3.
x = self.blas_func(3., x)
assert_array_equal(real_x, x)
def test_x_stride(self):
x = arange(6., dtype=self.dtype)
real_x = x.copy()
real_x[::2] = x[::2]*array(3., self.dtype)
x = self.blas_func(3., x, n=3, incx=2)
assert_array_equal(real_x, x)
def test_x_bad_size(self):
x = arange(12., dtype=self.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(2., x, n=4, incx=5)
try:
class TestSscal(BaseScal):
blas_func = fblas.sscal
dtype = float32
except AttributeError:
class TestSscal:
pass
class TestDscal(BaseScal):
blas_func = fblas.dscal
dtype = float64
try:
class TestCscal(BaseScal):
blas_func = fblas.cscal
dtype = complex64
except AttributeError:
class TestCscal:
pass
class TestZscal(BaseScal):
blas_func = fblas.zscal
dtype = complex128
##################################################
# Test blas ?copy
class BaseCopy(object):
''' Mixin class for copy testing '''
def test_simple(self):
x = arange(3., dtype=self.dtype)
y = zeros(shape(x), x.dtype)
y = self.blas_func(x, y)
assert_array_equal(x, y)
def test_x_stride(self):
x = arange(6., dtype=self.dtype)
y = zeros(3, x.dtype)
y = self.blas_func(x, y, n=3, incx=2)
assert_array_equal(x[::2], y)
def test_y_stride(self):
x = arange(3., dtype=self.dtype)
y = zeros(6, x.dtype)
y = self.blas_func(x, y, n=3, incy=2)
assert_array_equal(x, y[::2])
def test_x_and_y_stride(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
y = self.blas_func(x, y, n=3, incx=4, incy=2)
assert_array_equal(x[::4], y[::2])
def test_x_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=4, incx=5)
def test_y_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=3, incy=5)
# def test_y_bad_type(self):
## Hmmm. Should this work? What should be the output.
# x = arange(3.,dtype=self.dtype)
# y = zeros(shape(x))
# self.blas_func(x,y)
# assert_array_equal(x,y)
try:
class TestScopy(BaseCopy):
blas_func = fblas.scopy
dtype = float32
except AttributeError:
class TestScopy:
pass
class TestDcopy(BaseCopy):
blas_func = fblas.dcopy
dtype = float64
try:
class TestCcopy(BaseCopy):
blas_func = fblas.ccopy
dtype = complex64
except AttributeError:
class TestCcopy:
pass
class TestZcopy(BaseCopy):
blas_func = fblas.zcopy
dtype = complex128
##################################################
# Test blas ?swap
class BaseSwap(object):
''' Mixin class for swap tests '''
def test_simple(self):
x = arange(3., dtype=self.dtype)
y = zeros(shape(x), x.dtype)
desired_x = y.copy()
desired_y = x.copy()
x, y = self.blas_func(x, y)
assert_array_equal(desired_x, x)
assert_array_equal(desired_y, y)
def test_x_stride(self):
x = arange(6., dtype=self.dtype)
y = zeros(3, x.dtype)
desired_x = y.copy()
desired_y = x.copy()[::2]
x, y = self.blas_func(x, y, n=3, incx=2)
assert_array_equal(desired_x, x[::2])
assert_array_equal(desired_y, y)
def test_y_stride(self):
x = arange(3., dtype=self.dtype)
y = zeros(6, x.dtype)
desired_x = y.copy()[::2]
desired_y = x.copy()
x, y = self.blas_func(x, y, n=3, incy=2)
assert_array_equal(desired_x, x)
assert_array_equal(desired_y, y[::2])
def test_x_and_y_stride(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
desired_x = y.copy()[::2]
desired_y = x.copy()[::4]
x, y = self.blas_func(x, y, n=3, incx=4, incy=2)
assert_array_equal(desired_x, x[::4])
assert_array_equal(desired_y, y[::2])
def test_x_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=4, incx=5)
def test_y_bad_size(self):
x = arange(12., dtype=self.dtype)
y = zeros(6, x.dtype)
with pytest.raises(Exception, match='failed for 1st keyword'):
self.blas_func(x, y, n=3, incy=5)
try:
class TestSswap(BaseSwap):
blas_func = fblas.sswap
dtype = float32
except AttributeError:
class TestSswap:
pass
class TestDswap(BaseSwap):
blas_func = fblas.dswap
dtype = float64
try:
class TestCswap(BaseSwap):
blas_func = fblas.cswap
dtype = complex64
except AttributeError:
class TestCswap:
pass
class TestZswap(BaseSwap):
blas_func = fblas.zswap
dtype = complex128
##################################################
# Test blas ?gemv
# This will be a mess to test all cases.
class BaseGemv(object):
''' Mixin class for gemv tests '''
def get_data(self, x_stride=1, y_stride=1):
mult = array(1, dtype=self.dtype)
if self.dtype in [complex64, complex128]:
mult = array(1+1j, dtype=self.dtype)
from numpy.random import normal, seed
seed(1234)
alpha = array(1., dtype=self.dtype) * mult
beta = array(1., dtype=self.dtype) * mult
a = normal(0., 1., (3, 3)).astype(self.dtype) * mult
x = arange(shape(a)[0]*x_stride, dtype=self.dtype) * mult
y = arange(shape(a)[1]*y_stride, dtype=self.dtype) * mult
return alpha, beta, a, x, y
def test_simple(self):
alpha, beta, a, x, y = self.get_data()
desired_y = alpha*matrixmultiply(a, x)+beta*y
y = self.blas_func(alpha, a, x, beta, y)
assert_array_almost_equal(desired_y, y)
def test_default_beta_y(self):
alpha, beta, a, x, y = self.get_data()
desired_y = matrixmultiply(a, x)
y = self.blas_func(1, a, x)
assert_array_almost_equal(desired_y, y)
def test_simple_transpose(self):
alpha, beta, a, x, y = self.get_data()
desired_y = alpha*matrixmultiply(transpose(a), x)+beta*y
y = self.blas_func(alpha, a, x, beta, y, trans=1)
assert_array_almost_equal(desired_y, y)
def test_simple_transpose_conj(self):
alpha, beta, a, x, y = self.get_data()
desired_y = alpha*matrixmultiply(transpose(conjugate(a)), x)+beta*y
y = self.blas_func(alpha, a, x, beta, y, trans=2)
assert_array_almost_equal(desired_y, y)
def test_x_stride(self):
alpha, beta, a, x, y = self.get_data(x_stride=2)
desired_y = alpha*matrixmultiply(a, x[::2])+beta*y
y = self.blas_func(alpha, a, x, beta, y, incx=2)
assert_array_almost_equal(desired_y, y)
def test_x_stride_transpose(self):
alpha, beta, a, x, y = self.get_data(x_stride=2)
desired_y = alpha*matrixmultiply(transpose(a), x[::2])+beta*y
y = self.blas_func(alpha, a, x, beta, y, trans=1, incx=2)
assert_array_almost_equal(desired_y, y)
def test_x_stride_assert(self):
# What is the use of this test?
alpha, beta, a, x, y = self.get_data(x_stride=2)
with pytest.raises(Exception, match='failed for 3rd argument'):
y = self.blas_func(1, a, x, 1, y, trans=0, incx=3)
with pytest.raises(Exception, match='failed for 3rd argument'):
y = self.blas_func(1, a, x, 1, y, trans=1, incx=3)
def test_y_stride(self):
alpha, beta, a, x, y = self.get_data(y_stride=2)
desired_y = y.copy()
desired_y[::2] = alpha*matrixmultiply(a, x)+beta*y[::2]
y = self.blas_func(alpha, a, x, beta, y, incy=2)
assert_array_almost_equal(desired_y, y)
def test_y_stride_transpose(self):
alpha, beta, a, x, y = self.get_data(y_stride=2)
desired_y = y.copy()
desired_y[::2] = alpha*matrixmultiply(transpose(a), x)+beta*y[::2]
y = self.blas_func(alpha, a, x, beta, y, trans=1, incy=2)
assert_array_almost_equal(desired_y, y)
def test_y_stride_assert(self):
# What is the use of this test?
alpha, beta, a, x, y = self.get_data(y_stride=2)
with pytest.raises(Exception, match='failed for 2nd keyword'):
y = self.blas_func(1, a, x, 1, y, trans=0, incy=3)
with pytest.raises(Exception, match='failed for 2nd keyword'):
y = self.blas_func(1, a, x, 1, y, trans=1, incy=3)
try:
class TestSgemv(BaseGemv):
blas_func = fblas.sgemv
dtype = float32
def test_sgemv_on_osx(self):
from itertools import product
import sys
import numpy as np
if sys.platform != 'darwin':
return
def aligned_array(shape, align, dtype, order='C'):
# Make array shape `shape` with aligned at `align` bytes
d = dtype()
# Make array of correct size with `align` extra bytes
N = np.prod(shape)
tmp = np.zeros(N * d.nbytes + align, dtype=np.uint8)
address = tmp.__array_interface__["data"][0]
# Find offset into array giving desired alignment
for offset in range(align):
if (address + offset) % align == 0:
break
tmp = tmp[offset:offset+N*d.nbytes].view(dtype=dtype)
return tmp.reshape(shape, order=order)
def as_aligned(arr, align, dtype, order='C'):
# Copy `arr` into an aligned array with same shape
aligned = aligned_array(arr.shape, align, dtype, order)
aligned[:] = arr[:]
return aligned
def assert_dot_close(A, X, desired):
assert_allclose(self.blas_func(1.0, A, X), desired,
rtol=1e-5, atol=1e-7)
testdata = product((15, 32), (10000,), (200, 89), ('C', 'F'))
for align, m, n, a_order in testdata:
A_d = np.random.rand(m, n)
X_d = np.random.rand(n)
desired = np.dot(A_d, X_d)
# Calculation with aligned single precision
A_f = as_aligned(A_d, align, np.float32, order=a_order)
X_f = as_aligned(X_d, align, np.float32, order=a_order)
assert_dot_close(A_f, X_f, desired)
except AttributeError:
class TestSgemv:
pass
class TestDgemv(BaseGemv):
blas_func = fblas.dgemv
dtype = float64
try:
class TestCgemv(BaseGemv):
blas_func = fblas.cgemv
dtype = complex64
except AttributeError:
class TestCgemv:
pass
class TestZgemv(BaseGemv):
blas_func = fblas.zgemv
dtype = complex128
"""
##################################################
### Test blas ?ger
### This will be a mess to test all cases.
class BaseGer(object):
def get_data(self,x_stride=1,y_stride=1):
from numpy.random import normal, seed
seed(1234)
alpha = array(1., dtype = self.dtype)
a = normal(0.,1.,(3,3)).astype(self.dtype)
x = arange(shape(a)[0]*x_stride,dtype=self.dtype)
y = arange(shape(a)[1]*y_stride,dtype=self.dtype)
return alpha,a,x,y
def test_simple(self):
alpha,a,x,y = self.get_data()
# tranpose takes care of Fortran vs. C(and Python) memory layout
desired_a = alpha*transpose(x[:,newaxis]*y) + a
self.blas_func(x,y,a)
assert_array_almost_equal(desired_a,a)
def test_x_stride(self):
alpha,a,x,y = self.get_data(x_stride=2)
desired_a = alpha*transpose(x[::2,newaxis]*y) + a
self.blas_func(x,y,a,incx=2)
assert_array_almost_equal(desired_a,a)
def test_x_stride_assert(self):
alpha,a,x,y = self.get_data(x_stride=2)
with pytest.raises(ValueError, match='foo'):
self.blas_func(x,y,a,incx=3)
def test_y_stride(self):
alpha,a,x,y = self.get_data(y_stride=2)
desired_a = alpha*transpose(x[:,newaxis]*y[::2]) + a
self.blas_func(x,y,a,incy=2)
assert_array_almost_equal(desired_a,a)
def test_y_stride_assert(self):
alpha,a,x,y = self.get_data(y_stride=2)
with pytest.raises(ValueError, match='foo'):
self.blas_func(a,x,y,incy=3)
class TestSger(BaseGer):
blas_func = fblas.sger
dtype = float32
class TestDger(BaseGer):
blas_func = fblas.dger
dtype = float64
"""
##################################################
# Test blas ?gerc
# This will be a mess to test all cases.
"""
class BaseGerComplex(BaseGer):
def get_data(self,x_stride=1,y_stride=1):
from numpy.random import normal, seed
seed(1234)
alpha = array(1+1j, dtype = self.dtype)
a = normal(0.,1.,(3,3)).astype(self.dtype)
a = a + normal(0.,1.,(3,3)) * array(1j, dtype = self.dtype)
x = normal(0.,1.,shape(a)[0]*x_stride).astype(self.dtype)
x = x + x * array(1j, dtype = self.dtype)
y = normal(0.,1.,shape(a)[1]*y_stride).astype(self.dtype)
y = y + y * array(1j, dtype = self.dtype)
return alpha,a,x,y
def test_simple(self):
alpha,a,x,y = self.get_data()
# tranpose takes care of Fortran vs. C(and Python) memory layout
a = a * array(0.,dtype = self.dtype)
#desired_a = alpha*transpose(x[:,newaxis]*self.transform(y)) + a
desired_a = alpha*transpose(x[:,newaxis]*y) + a
#self.blas_func(x,y,a,alpha = alpha)
fblas.cgeru(x,y,a,alpha = alpha)
assert_array_almost_equal(desired_a,a)
#def test_x_stride(self):
# alpha,a,x,y = self.get_data(x_stride=2)
# desired_a = alpha*transpose(x[::2,newaxis]*self.transform(y)) + a
# self.blas_func(x,y,a,incx=2)
# assert_array_almost_equal(desired_a,a)
#def test_y_stride(self):
# alpha,a,x,y = self.get_data(y_stride=2)
# desired_a = alpha*transpose(x[:,newaxis]*self.transform(y[::2])) + a
# self.blas_func(x,y,a,incy=2)
# assert_array_almost_equal(desired_a,a)
class TestCgeru(BaseGerComplex):
blas_func = fblas.cgeru
dtype = complex64
def transform(self,x):
return x
class TestZgeru(BaseGerComplex):
blas_func = fblas.zgeru
dtype = complex128
def transform(self,x):
return x
class TestCgerc(BaseGerComplex):
blas_func = fblas.cgerc
dtype = complex64
def transform(self,x):
return conjugate(x)
class TestZgerc(BaseGerComplex):
blas_func = fblas.zgerc
dtype = complex128
def transform(self,x):
return conjugate(x)
"""

View file

@ -0,0 +1,295 @@
#******************************************************************************
# Copyright (C) 2013 Kenneth L. Ho
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer. Redistributions in binary
# form must reproduce the above copyright notice, this list of conditions and
# the following disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# None of the names of the copyright holders may be used to endorse or
# promote products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#******************************************************************************
import scipy.linalg.interpolative as pymatrixid
import numpy as np
from scipy.linalg import hilbert, svdvals, norm
from scipy.sparse.linalg import aslinearoperator
from scipy.linalg.interpolative import interp_decomp
import time
import itertools
from numpy.testing import assert_, assert_allclose
from pytest import raises as assert_raises
def _debug_print(s):
if 0:
print(s)
class TestInterpolativeDecomposition(object):
def test_id(self):
for dtype in [np.float64, np.complex128]:
self.check_id(dtype)
def check_id(self, dtype):
# Test ID routines on a Hilbert matrix.
# set parameters
n = 300
eps = 1e-12
# construct Hilbert matrix
A = hilbert(n).astype(dtype)
if np.issubdtype(dtype, np.complexfloating):
A = A * (1 + 1j)
L = aslinearoperator(A)
# find rank
S = np.linalg.svd(A, compute_uv=False)
try:
rank = np.nonzero(S < eps)[0][0]
except IndexError:
rank = n
# print input summary
_debug_print("Hilbert matrix dimension: %8i" % n)
_debug_print("Working precision: %8.2e" % eps)
_debug_print("Rank to working precision: %8i" % rank)
# set print format
fmt = "%8.2e (s) / %5s"
# test real ID routines
_debug_print("-----------------------------------------")
_debug_print("Real ID routines")
_debug_print("-----------------------------------------")
# fixed precision
_debug_print("Calling iddp_id / idzp_id ...",)
t0 = time.time()
k, idx, proj = pymatrixid.interp_decomp(A, eps, rand=False)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddp_aid / idzp_aid ...",)
t0 = time.time()
k, idx, proj = pymatrixid.interp_decomp(A, eps)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddp_rid / idzp_rid ...",)
t0 = time.time()
k, idx, proj = pymatrixid.interp_decomp(L, eps)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
# fixed rank
k = rank
_debug_print("Calling iddr_id / idzr_id ...",)
t0 = time.time()
idx, proj = pymatrixid.interp_decomp(A, k, rand=False)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddr_aid / idzr_aid ...",)
t0 = time.time()
idx, proj = pymatrixid.interp_decomp(A, k)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddr_rid / idzr_rid ...",)
t0 = time.time()
idx, proj = pymatrixid.interp_decomp(L, k)
t = time.time() - t0
B = pymatrixid.reconstruct_matrix_from_id(A[:, idx[:k]], idx, proj)
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
# check skeleton and interpolation matrices
idx, proj = pymatrixid.interp_decomp(A, k, rand=False)
P = pymatrixid.reconstruct_interp_matrix(idx, proj)
B = pymatrixid.reconstruct_skel_matrix(A, k, idx)
assert_(np.allclose(B, A[:,idx[:k]], eps))
assert_(np.allclose(B.dot(P), A, eps))
# test SVD routines
_debug_print("-----------------------------------------")
_debug_print("SVD routines")
_debug_print("-----------------------------------------")
# fixed precision
_debug_print("Calling iddp_svd / idzp_svd ...",)
t0 = time.time()
U, S, V = pymatrixid.svd(A, eps, rand=False)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddp_asvd / idzp_asvd...",)
t0 = time.time()
U, S, V = pymatrixid.svd(A, eps)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddp_rsvd / idzp_rsvd...",)
t0 = time.time()
U, S, V = pymatrixid.svd(L, eps)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
# fixed rank
k = rank
_debug_print("Calling iddr_svd / idzr_svd ...",)
t0 = time.time()
U, S, V = pymatrixid.svd(A, k, rand=False)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddr_asvd / idzr_asvd ...",)
t0 = time.time()
U, S, V = pymatrixid.svd(A, k)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
_debug_print("Calling iddr_rsvd / idzr_rsvd ...",)
t0 = time.time()
U, S, V = pymatrixid.svd(L, k)
t = time.time() - t0
B = np.dot(U, np.dot(np.diag(S), V.T.conj()))
_debug_print(fmt % (t, np.allclose(A, B, eps)))
assert_(np.allclose(A, B, eps))
# ID to SVD
idx, proj = pymatrixid.interp_decomp(A, k, rand=False)
Up, Sp, Vp = pymatrixid.id_to_svd(A[:, idx[:k]], idx, proj)
B = U.dot(np.diag(S).dot(V.T.conj()))
assert_(np.allclose(A, B, eps))
# Norm estimates
s = svdvals(A)
norm_2_est = pymatrixid.estimate_spectral_norm(A)
assert_(np.allclose(norm_2_est, s[0], 1e-6))
B = A.copy()
B[:,0] *= 1.2
s = svdvals(A - B)
norm_2_est = pymatrixid.estimate_spectral_norm_diff(A, B)
assert_(np.allclose(norm_2_est, s[0], 1e-6))
# Rank estimates
B = np.array([[1, 1, 0], [0, 0, 1], [0, 0, 1]], dtype=dtype)
for M in [A, B]:
ML = aslinearoperator(M)
rank_tol = 1e-9
rank_np = np.linalg.matrix_rank(M, norm(M, 2)*rank_tol)
rank_est = pymatrixid.estimate_rank(M, rank_tol)
rank_est_2 = pymatrixid.estimate_rank(ML, rank_tol)
assert_(rank_est >= rank_np)
assert_(rank_est <= rank_np + 10)
assert_(rank_est_2 >= rank_np - 4)
assert_(rank_est_2 <= rank_np + 4)
def test_rand(self):
pymatrixid.seed('default')
assert_(np.allclose(pymatrixid.rand(2), [0.8932059, 0.64500803], 1e-4))
pymatrixid.seed(1234)
x1 = pymatrixid.rand(2)
assert_(np.allclose(x1, [0.7513823, 0.06861718], 1e-4))
np.random.seed(1234)
pymatrixid.seed()
x2 = pymatrixid.rand(2)
np.random.seed(1234)
pymatrixid.seed(np.random.rand(55))
x3 = pymatrixid.rand(2)
assert_allclose(x1, x2)
assert_allclose(x1, x3)
def test_badcall(self):
A = hilbert(5).astype(np.float32)
assert_raises(ValueError, pymatrixid.interp_decomp, A, 1e-6, rand=False)
def test_rank_too_large(self):
# svd(array, k) should not segfault
a = np.ones((4, 3))
with assert_raises(ValueError):
pymatrixid.svd(a, 4)
def test_full_rank(self):
eps = 1.0e-12
# fixed precision
A = np.random.rand(16, 8)
k, idx, proj = pymatrixid.interp_decomp(A, eps)
assert_(k == A.shape[1])
P = pymatrixid.reconstruct_interp_matrix(idx, proj)
B = pymatrixid.reconstruct_skel_matrix(A, k, idx)
assert_allclose(A, B.dot(P))
# fixed rank
idx, proj = pymatrixid.interp_decomp(A, k)
P = pymatrixid.reconstruct_interp_matrix(idx, proj)
B = pymatrixid.reconstruct_skel_matrix(A, k, idx)
assert_allclose(A, B.dot(P))
def test_bug_9793(self):
dtypes = [np.float_, np.complex_]
rands = [True, False]
epss = [1, 0.1]
for dtype, eps, rand in itertools.product(dtypes, epss, rands):
A = np.array([[-1, -1, -1, 0, 0, 0],
[0, 0, 0, 1, 1, 1],
[1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 0, 1]],
dtype=dtype, order="C")
B = A.copy()
interp_decomp(A.T, eps, rand=rand)
assert_(np.array_equal(A, B))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,895 @@
#
# Created by: Pearu Peterson, March 2002
#
""" Test functions for linalg.matfuncs module
"""
import random
import functools
import numpy as np
from numpy import array, identity, dot, sqrt
from numpy.testing import (
assert_array_equal, assert_array_less, assert_equal,
assert_array_almost_equal,
assert_allclose, assert_, assert_warns)
import pytest
import scipy.linalg
from scipy.linalg import (funm, signm, logm, sqrtm, fractional_matrix_power,
expm, expm_frechet, expm_cond, norm, khatri_rao)
from scipy.linalg import _matfuncs_inv_ssq
import scipy.linalg._expm_frechet
from scipy.optimize import minimize
def _get_al_mohy_higham_2012_experiment_1():
"""
Return the test matrix from Experiment (1) of [1]_.
References
----------
.. [1] Awad H. Al-Mohy and Nicholas J. Higham (2012)
"Improved Inverse Scaling and Squaring Algorithms
for the Matrix Logarithm."
SIAM Journal on Scientific Computing, 34 (4). C152-C169.
ISSN 1095-7197
"""
A = np.array([
[3.2346e-1, 3e4, 3e4, 3e4],
[0, 3.0089e-1, 3e4, 3e4],
[0, 0, 3.2210e-1, 3e4],
[0, 0, 0, 3.0744e-1]], dtype=float)
return A
class TestSignM(object):
def test_nils(self):
a = array([[29.2, -24.2, 69.5, 49.8, 7.],
[-9.2, 5.2, -18., -16.8, -2.],
[-10., 6., -20., -18., -2.],
[-9.6, 9.6, -25.5, -15.4, -2.],
[9.8, -4.8, 18., 18.2, 2.]])
cr = array([[11.94933333,-2.24533333,15.31733333,21.65333333,-2.24533333],
[-3.84266667,0.49866667,-4.59066667,-7.18666667,0.49866667],
[-4.08,0.56,-4.92,-7.6,0.56],
[-4.03466667,1.04266667,-5.59866667,-7.02666667,1.04266667],
[4.15733333,-0.50133333,4.90933333,7.81333333,-0.50133333]])
r = signm(a)
assert_array_almost_equal(r,cr)
def test_defective1(self):
a = array([[0.0,1,0,0],[1,0,1,0],[0,0,0,1],[0,0,1,0]])
signm(a, disp=False)
#XXX: what would be the correct result?
def test_defective2(self):
a = array((
[29.2,-24.2,69.5,49.8,7.0],
[-9.2,5.2,-18.0,-16.8,-2.0],
[-10.0,6.0,-20.0,-18.0,-2.0],
[-9.6,9.6,-25.5,-15.4,-2.0],
[9.8,-4.8,18.0,18.2,2.0]))
signm(a, disp=False)
#XXX: what would be the correct result?
def test_defective3(self):
a = array([[-2., 25., 0., 0., 0., 0., 0.],
[0., -3., 10., 3., 3., 3., 0.],
[0., 0., 2., 15., 3., 3., 0.],
[0., 0., 0., 0., 15., 3., 0.],
[0., 0., 0., 0., 3., 10., 0.],
[0., 0., 0., 0., 0., -2., 25.],
[0., 0., 0., 0., 0., 0., -3.]])
signm(a, disp=False)
#XXX: what would be the correct result?
class TestLogM(object):
def test_nils(self):
a = array([[-2., 25., 0., 0., 0., 0., 0.],
[0., -3., 10., 3., 3., 3., 0.],
[0., 0., 2., 15., 3., 3., 0.],
[0., 0., 0., 0., 15., 3., 0.],
[0., 0., 0., 0., 3., 10., 0.],
[0., 0., 0., 0., 0., -2., 25.],
[0., 0., 0., 0., 0., 0., -3.]])
m = (identity(7)*3.1+0j)-a
logm(m, disp=False)
#XXX: what would be the correct result?
def test_al_mohy_higham_2012_experiment_1_logm(self):
# The logm completes the round trip successfully.
# Note that the expm leg of the round trip is badly conditioned.
A = _get_al_mohy_higham_2012_experiment_1()
A_logm, info = logm(A, disp=False)
A_round_trip = expm(A_logm)
assert_allclose(A_round_trip, A, rtol=1e-5, atol=1e-14)
def test_al_mohy_higham_2012_experiment_1_funm_log(self):
# The raw funm with np.log does not complete the round trip.
# Note that the expm leg of the round trip is badly conditioned.
A = _get_al_mohy_higham_2012_experiment_1()
A_funm_log, info = funm(A, np.log, disp=False)
A_round_trip = expm(A_funm_log)
assert_(not np.allclose(A_round_trip, A, rtol=1e-5, atol=1e-14))
def test_round_trip_random_float(self):
np.random.seed(1234)
for n in range(1, 6):
M_unscaled = np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
# Eigenvalues are related to the branch cut.
W = np.linalg.eigvals(M)
err_msg = 'M:{0} eivals:{1}'.format(M, W)
# Check sqrtm round trip because it is used within logm.
M_sqrtm, info = sqrtm(M, disp=False)
M_sqrtm_round_trip = M_sqrtm.dot(M_sqrtm)
assert_allclose(M_sqrtm_round_trip, M)
# Check logm round trip.
M_logm, info = logm(M, disp=False)
M_logm_round_trip = expm(M_logm)
assert_allclose(M_logm_round_trip, M, err_msg=err_msg)
def test_round_trip_random_complex(self):
np.random.seed(1234)
for n in range(1, 6):
M_unscaled = np.random.randn(n, n) + 1j * np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
M_logm, info = logm(M, disp=False)
M_round_trip = expm(M_logm)
assert_allclose(M_round_trip, M)
def test_logm_type_preservation_and_conversion(self):
# The logm matrix function should preserve the type of a matrix
# whose eigenvalues are positive with zero imaginary part.
# Test this preservation for variously structured matrices.
complex_dtype_chars = ('F', 'D', 'G')
for matrix_as_list in (
[[1, 0], [0, 1]],
[[1, 0], [1, 1]],
[[2, 1], [1, 1]],
[[2, 3], [1, 2]]):
# check that the spectrum has the expected properties
W = scipy.linalg.eigvals(matrix_as_list)
assert_(not any(w.imag or w.real < 0 for w in W))
# check float type preservation
A = np.array(matrix_as_list, dtype=float)
A_logm, info = logm(A, disp=False)
assert_(A_logm.dtype.char not in complex_dtype_chars)
# check complex type preservation
A = np.array(matrix_as_list, dtype=complex)
A_logm, info = logm(A, disp=False)
assert_(A_logm.dtype.char in complex_dtype_chars)
# check float->complex type conversion for the matrix negation
A = -np.array(matrix_as_list, dtype=float)
A_logm, info = logm(A, disp=False)
assert_(A_logm.dtype.char in complex_dtype_chars)
def test_complex_spectrum_real_logm(self):
# This matrix has complex eigenvalues and real logm.
# Its output dtype depends on its input dtype.
M = [[1, 1, 2], [2, 1, 1], [1, 2, 1]]
for dt in float, complex:
X = np.array(M, dtype=dt)
w = scipy.linalg.eigvals(X)
assert_(1e-2 < np.absolute(w.imag).sum())
Y, info = logm(X, disp=False)
assert_(np.issubdtype(Y.dtype, np.inexact))
assert_allclose(expm(Y), X)
def test_real_mixed_sign_spectrum(self):
# These matrices have real eigenvalues with mixed signs.
# The output logm dtype is complex, regardless of input dtype.
for M in (
[[1, 0], [0, -1]],
[[0, 1], [1, 0]]):
for dt in float, complex:
A = np.array(M, dtype=dt)
A_logm, info = logm(A, disp=False)
assert_(np.issubdtype(A_logm.dtype, np.complexfloating))
def test_exactly_singular(self):
A = np.array([[0, 0], [1j, 1j]])
B = np.asarray([[1, 1], [0, 0]])
for M in A, A.T, B, B.T:
expected_warning = _matfuncs_inv_ssq.LogmExactlySingularWarning
L, info = assert_warns(expected_warning, logm, M, disp=False)
E = expm(L)
assert_allclose(E, M, atol=1e-14)
def test_nearly_singular(self):
M = np.array([[1e-100]])
expected_warning = _matfuncs_inv_ssq.LogmNearlySingularWarning
L, info = assert_warns(expected_warning, logm, M, disp=False)
E = expm(L)
assert_allclose(E, M, atol=1e-14)
def test_opposite_sign_complex_eigenvalues(self):
# See gh-6113
E = [[0, 1], [-1, 0]]
L = [[0, np.pi*0.5], [-np.pi*0.5, 0]]
assert_allclose(expm(L), E, atol=1e-14)
assert_allclose(logm(E), L, atol=1e-14)
E = [[1j, 4], [0, -1j]]
L = [[1j*np.pi*0.5, 2*np.pi], [0, -1j*np.pi*0.5]]
assert_allclose(expm(L), E, atol=1e-14)
assert_allclose(logm(E), L, atol=1e-14)
E = [[1j, 0], [0, -1j]]
L = [[1j*np.pi*0.5, 0], [0, -1j*np.pi*0.5]]
assert_allclose(expm(L), E, atol=1e-14)
assert_allclose(logm(E), L, atol=1e-14)
class TestSqrtM(object):
def test_round_trip_random_float(self):
np.random.seed(1234)
for n in range(1, 6):
M_unscaled = np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
M_sqrtm, info = sqrtm(M, disp=False)
M_sqrtm_round_trip = M_sqrtm.dot(M_sqrtm)
assert_allclose(M_sqrtm_round_trip, M)
def test_round_trip_random_complex(self):
np.random.seed(1234)
for n in range(1, 6):
M_unscaled = np.random.randn(n, n) + 1j * np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
M_sqrtm, info = sqrtm(M, disp=False)
M_sqrtm_round_trip = M_sqrtm.dot(M_sqrtm)
assert_allclose(M_sqrtm_round_trip, M)
def test_bad(self):
# See https://web.archive.org/web/20051220232650/http://www.maths.man.ac.uk/~nareports/narep336.ps.gz
e = 2**-5
se = sqrt(e)
a = array([[1.0,0,0,1],
[0,e,0,0],
[0,0,e,0],
[0,0,0,1]])
sa = array([[1,0,0,0.5],
[0,se,0,0],
[0,0,se,0],
[0,0,0,1]])
n = a.shape[0]
assert_array_almost_equal(dot(sa,sa),a)
# Check default sqrtm.
esa = sqrtm(a, disp=False, blocksize=n)[0]
assert_array_almost_equal(dot(esa,esa),a)
# Check sqrtm with 2x2 blocks.
esa = sqrtm(a, disp=False, blocksize=2)[0]
assert_array_almost_equal(dot(esa,esa),a)
def test_sqrtm_type_preservation_and_conversion(self):
# The sqrtm matrix function should preserve the type of a matrix
# whose eigenvalues are nonnegative with zero imaginary part.
# Test this preservation for variously structured matrices.
complex_dtype_chars = ('F', 'D', 'G')
for matrix_as_list in (
[[1, 0], [0, 1]],
[[1, 0], [1, 1]],
[[2, 1], [1, 1]],
[[2, 3], [1, 2]],
[[1, 1], [1, 1]]):
# check that the spectrum has the expected properties
W = scipy.linalg.eigvals(matrix_as_list)
assert_(not any(w.imag or w.real < 0 for w in W))
# check float type preservation
A = np.array(matrix_as_list, dtype=float)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(A_sqrtm.dtype.char not in complex_dtype_chars)
# check complex type preservation
A = np.array(matrix_as_list, dtype=complex)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(A_sqrtm.dtype.char in complex_dtype_chars)
# check float->complex type conversion for the matrix negation
A = -np.array(matrix_as_list, dtype=float)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(A_sqrtm.dtype.char in complex_dtype_chars)
def test_sqrtm_type_conversion_mixed_sign_or_complex_spectrum(self):
complex_dtype_chars = ('F', 'D', 'G')
for matrix_as_list in (
[[1, 0], [0, -1]],
[[0, 1], [1, 0]],
[[0, 1, 0], [0, 0, 1], [1, 0, 0]]):
# check that the spectrum has the expected properties
W = scipy.linalg.eigvals(matrix_as_list)
assert_(any(w.imag or w.real < 0 for w in W))
# check complex->complex
A = np.array(matrix_as_list, dtype=complex)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(A_sqrtm.dtype.char in complex_dtype_chars)
# check float->complex
A = np.array(matrix_as_list, dtype=float)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(A_sqrtm.dtype.char in complex_dtype_chars)
def test_blocksizes(self):
# Make sure I do not goof up the blocksizes when they do not divide n.
np.random.seed(1234)
for n in range(1, 8):
A = np.random.rand(n, n) + 1j*np.random.randn(n, n)
A_sqrtm_default, info = sqrtm(A, disp=False, blocksize=n)
assert_allclose(A, np.linalg.matrix_power(A_sqrtm_default, 2))
for blocksize in range(1, 10):
A_sqrtm_new, info = sqrtm(A, disp=False, blocksize=blocksize)
assert_allclose(A_sqrtm_default, A_sqrtm_new)
def test_al_mohy_higham_2012_experiment_1(self):
# Matrix square root of a tricky upper triangular matrix.
A = _get_al_mohy_higham_2012_experiment_1()
A_sqrtm, info = sqrtm(A, disp=False)
A_round_trip = A_sqrtm.dot(A_sqrtm)
assert_allclose(A_round_trip, A, rtol=1e-5)
assert_allclose(np.tril(A_round_trip), np.tril(A))
def test_strict_upper_triangular(self):
# This matrix has no square root.
for dt in int, float:
A = np.array([
[0, 3, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]], dtype=dt)
A_sqrtm, info = sqrtm(A, disp=False)
assert_(np.isnan(A_sqrtm).all())
def test_weird_matrix(self):
# The square root of matrix B exists.
for dt in int, float:
A = np.array([
[0, 0, 1],
[0, 0, 0],
[0, 1, 0]], dtype=dt)
B = np.array([
[0, 1, 0],
[0, 0, 0],
[0, 0, 0]], dtype=dt)
assert_array_equal(B, A.dot(A))
# But scipy sqrtm is not clever enough to find it.
B_sqrtm, info = sqrtm(B, disp=False)
assert_(np.isnan(B_sqrtm).all())
def test_disp(self):
np.random.seed(1234)
A = np.random.rand(3, 3)
B = sqrtm(A, disp=True)
assert_allclose(B.dot(B), A)
def test_opposite_sign_complex_eigenvalues(self):
M = [[2j, 4], [0, -2j]]
R = [[1+1j, 2], [0, 1-1j]]
assert_allclose(np.dot(R, R), M, atol=1e-14)
assert_allclose(sqrtm(M), R, atol=1e-14)
def test_gh4866(self):
M = np.array([[1, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 1]])
R = np.array([[sqrt(0.5), 0, 0, sqrt(0.5)],
[0, 0, 0, 0],
[0, 0, 0, 0],
[sqrt(0.5), 0, 0, sqrt(0.5)]])
assert_allclose(np.dot(R, R), M, atol=1e-14)
assert_allclose(sqrtm(M), R, atol=1e-14)
def test_gh5336(self):
M = np.diag([2, 1, 0])
R = np.diag([sqrt(2), 1, 0])
assert_allclose(np.dot(R, R), M, atol=1e-14)
assert_allclose(sqrtm(M), R, atol=1e-14)
def test_gh7839(self):
M = np.zeros((2, 2))
R = np.zeros((2, 2))
assert_allclose(np.dot(R, R), M, atol=1e-14)
assert_allclose(sqrtm(M), R, atol=1e-14)
class TestFractionalMatrixPower(object):
def test_round_trip_random_complex(self):
np.random.seed(1234)
for p in range(1, 5):
for n in range(1, 5):
M_unscaled = np.random.randn(n, n) + 1j * np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
M_root = fractional_matrix_power(M, 1/p)
M_round_trip = np.linalg.matrix_power(M_root, p)
assert_allclose(M_round_trip, M)
def test_round_trip_random_float(self):
# This test is more annoying because it can hit the branch cut;
# this happens when the matrix has an eigenvalue
# with no imaginary component and with a real negative component,
# and it means that the principal branch does not exist.
np.random.seed(1234)
for p in range(1, 5):
for n in range(1, 5):
M_unscaled = np.random.randn(n, n)
for scale in np.logspace(-4, 4, 9):
M = M_unscaled * scale
M_root = fractional_matrix_power(M, 1/p)
M_round_trip = np.linalg.matrix_power(M_root, p)
assert_allclose(M_round_trip, M)
def test_larger_abs_fractional_matrix_powers(self):
np.random.seed(1234)
for n in (2, 3, 5):
for i in range(10):
M = np.random.randn(n, n) + 1j * np.random.randn(n, n)
M_one_fifth = fractional_matrix_power(M, 0.2)
# Test the round trip.
M_round_trip = np.linalg.matrix_power(M_one_fifth, 5)
assert_allclose(M, M_round_trip)
# Test a large abs fractional power.
X = fractional_matrix_power(M, -5.4)
Y = np.linalg.matrix_power(M_one_fifth, -27)
assert_allclose(X, Y)
# Test another large abs fractional power.
X = fractional_matrix_power(M, 3.8)
Y = np.linalg.matrix_power(M_one_fifth, 19)
assert_allclose(X, Y)
def test_random_matrices_and_powers(self):
# Each independent iteration of this fuzz test picks random parameters.
# It tries to hit some edge cases.
np.random.seed(1234)
nsamples = 20
for i in range(nsamples):
# Sample a matrix size and a random real power.
n = random.randrange(1, 5)
p = np.random.randn()
# Sample a random real or complex matrix.
matrix_scale = np.exp(random.randrange(-4, 5))
A = np.random.randn(n, n)
if random.choice((True, False)):
A = A + 1j * np.random.randn(n, n)
A = A * matrix_scale
# Check a couple of analytically equivalent ways
# to compute the fractional matrix power.
# These can be compared because they both use the principal branch.
A_power = fractional_matrix_power(A, p)
A_logm, info = logm(A, disp=False)
A_power_expm_logm = expm(A_logm * p)
assert_allclose(A_power, A_power_expm_logm)
def test_al_mohy_higham_2012_experiment_1(self):
# Fractional powers of a tricky upper triangular matrix.
A = _get_al_mohy_higham_2012_experiment_1()
# Test remainder matrix power.
A_funm_sqrt, info = funm(A, np.sqrt, disp=False)
A_sqrtm, info = sqrtm(A, disp=False)
A_rem_power = _matfuncs_inv_ssq._remainder_matrix_power(A, 0.5)
A_power = fractional_matrix_power(A, 0.5)
assert_array_equal(A_rem_power, A_power)
assert_allclose(A_sqrtm, A_power)
assert_allclose(A_sqrtm, A_funm_sqrt)
# Test more fractional powers.
for p in (1/2, 5/3):
A_power = fractional_matrix_power(A, p)
A_round_trip = fractional_matrix_power(A_power, 1/p)
assert_allclose(A_round_trip, A, rtol=1e-2)
assert_allclose(np.tril(A_round_trip, 1), np.tril(A, 1))
def test_briggs_helper_function(self):
np.random.seed(1234)
for a in np.random.randn(10) + 1j * np.random.randn(10):
for k in range(5):
x_observed = _matfuncs_inv_ssq._briggs_helper_function(a, k)
x_expected = a ** np.exp2(-k) - 1
assert_allclose(x_observed, x_expected)
def test_type_preservation_and_conversion(self):
# The fractional_matrix_power matrix function should preserve
# the type of a matrix whose eigenvalues
# are positive with zero imaginary part.
# Test this preservation for variously structured matrices.
complex_dtype_chars = ('F', 'D', 'G')
for matrix_as_list in (
[[1, 0], [0, 1]],
[[1, 0], [1, 1]],
[[2, 1], [1, 1]],
[[2, 3], [1, 2]]):
# check that the spectrum has the expected properties
W = scipy.linalg.eigvals(matrix_as_list)
assert_(not any(w.imag or w.real < 0 for w in W))
# Check various positive and negative powers
# with absolute values bigger and smaller than 1.
for p in (-2.4, -0.9, 0.2, 3.3):
# check float type preservation
A = np.array(matrix_as_list, dtype=float)
A_power = fractional_matrix_power(A, p)
assert_(A_power.dtype.char not in complex_dtype_chars)
# check complex type preservation
A = np.array(matrix_as_list, dtype=complex)
A_power = fractional_matrix_power(A, p)
assert_(A_power.dtype.char in complex_dtype_chars)
# check float->complex for the matrix negation
A = -np.array(matrix_as_list, dtype=float)
A_power = fractional_matrix_power(A, p)
assert_(A_power.dtype.char in complex_dtype_chars)
def test_type_conversion_mixed_sign_or_complex_spectrum(self):
complex_dtype_chars = ('F', 'D', 'G')
for matrix_as_list in (
[[1, 0], [0, -1]],
[[0, 1], [1, 0]],
[[0, 1, 0], [0, 0, 1], [1, 0, 0]]):
# check that the spectrum has the expected properties
W = scipy.linalg.eigvals(matrix_as_list)
assert_(any(w.imag or w.real < 0 for w in W))
# Check various positive and negative powers
# with absolute values bigger and smaller than 1.
for p in (-2.4, -0.9, 0.2, 3.3):
# check complex->complex
A = np.array(matrix_as_list, dtype=complex)
A_power = fractional_matrix_power(A, p)
assert_(A_power.dtype.char in complex_dtype_chars)
# check float->complex
A = np.array(matrix_as_list, dtype=float)
A_power = fractional_matrix_power(A, p)
assert_(A_power.dtype.char in complex_dtype_chars)
@pytest.mark.xfail(reason='Too unstable across LAPACKs.')
def test_singular(self):
# Negative fractional powers do not work with singular matrices.
for matrix_as_list in (
[[0, 0], [0, 0]],
[[1, 1], [1, 1]],
[[1, 2], [3, 6]],
[[0, 0, 0], [0, 1, 1], [0, -1, 1]]):
# Check fractional powers both for float and for complex types.
for newtype in (float, complex):
A = np.array(matrix_as_list, dtype=newtype)
for p in (-0.7, -0.9, -2.4, -1.3):
A_power = fractional_matrix_power(A, p)
assert_(np.isnan(A_power).all())
for p in (0.2, 1.43):
A_power = fractional_matrix_power(A, p)
A_round_trip = fractional_matrix_power(A_power, 1/p)
assert_allclose(A_round_trip, A)
def test_opposite_sign_complex_eigenvalues(self):
M = [[2j, 4], [0, -2j]]
R = [[1+1j, 2], [0, 1-1j]]
assert_allclose(np.dot(R, R), M, atol=1e-14)
assert_allclose(fractional_matrix_power(M, 0.5), R, atol=1e-14)
class TestExpM(object):
def test_zero(self):
a = array([[0.,0],[0,0]])
assert_array_almost_equal(expm(a),[[1,0],[0,1]])
def test_single_elt(self):
# See gh-5853
from scipy.sparse import csc_matrix
vOne = -2.02683397006j
vTwo = -2.12817566856j
mOne = csc_matrix([[vOne]], dtype='complex')
mTwo = csc_matrix([[vTwo]], dtype='complex')
outOne = expm(mOne)
outTwo = expm(mTwo)
assert_equal(type(outOne), type(mOne))
assert_equal(type(outTwo), type(mTwo))
assert_allclose(outOne[0, 0], complex(-0.44039415155949196,
-0.8978045395698304))
assert_allclose(outTwo[0, 0], complex(-0.52896401032626006,
-0.84864425749518878))
def test_empty_matrix_input(self):
# handle gh-11082
A = np.zeros((0, 0))
result = expm(A)
assert result.size == 0
class TestExpmFrechet(object):
def test_expm_frechet(self):
# a test of the basic functionality
M = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[0, 0, 1, 2],
[0, 0, 5, 6],
], dtype=float)
A = np.array([
[1, 2],
[5, 6],
], dtype=float)
E = np.array([
[3, 4],
[7, 8],
], dtype=float)
expected_expm = scipy.linalg.expm(A)
expected_frechet = scipy.linalg.expm(M)[:2, 2:]
for kwargs in ({}, {'method':'SPS'}, {'method':'blockEnlarge'}):
observed_expm, observed_frechet = expm_frechet(A, E, **kwargs)
assert_allclose(expected_expm, observed_expm)
assert_allclose(expected_frechet, observed_frechet)
def test_small_norm_expm_frechet(self):
# methodically test matrices with a range of norms, for better coverage
M_original = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[0, 0, 1, 2],
[0, 0, 5, 6],
], dtype=float)
A_original = np.array([
[1, 2],
[5, 6],
], dtype=float)
E_original = np.array([
[3, 4],
[7, 8],
], dtype=float)
A_original_norm_1 = scipy.linalg.norm(A_original, 1)
selected_m_list = [1, 3, 5, 7, 9, 11, 13, 15]
m_neighbor_pairs = zip(selected_m_list[:-1], selected_m_list[1:])
for ma, mb in m_neighbor_pairs:
ell_a = scipy.linalg._expm_frechet.ell_table_61[ma]
ell_b = scipy.linalg._expm_frechet.ell_table_61[mb]
target_norm_1 = 0.5 * (ell_a + ell_b)
scale = target_norm_1 / A_original_norm_1
M = scale * M_original
A = scale * A_original
E = scale * E_original
expected_expm = scipy.linalg.expm(A)
expected_frechet = scipy.linalg.expm(M)[:2, 2:]
observed_expm, observed_frechet = expm_frechet(A, E)
assert_allclose(expected_expm, observed_expm)
assert_allclose(expected_frechet, observed_frechet)
def test_fuzz(self):
# try a bunch of crazy inputs
rfuncs = (
np.random.uniform,
np.random.normal,
np.random.standard_cauchy,
np.random.exponential)
ntests = 100
for i in range(ntests):
rfunc = random.choice(rfuncs)
target_norm_1 = random.expovariate(1.0)
n = random.randrange(2, 16)
A_original = rfunc(size=(n,n))
E_original = rfunc(size=(n,n))
A_original_norm_1 = scipy.linalg.norm(A_original, 1)
scale = target_norm_1 / A_original_norm_1
A = scale * A_original
E = scale * E_original
M = np.vstack([
np.hstack([A, E]),
np.hstack([np.zeros_like(A), A])])
expected_expm = scipy.linalg.expm(A)
expected_frechet = scipy.linalg.expm(M)[:n, n:]
observed_expm, observed_frechet = expm_frechet(A, E)
assert_allclose(expected_expm, observed_expm)
assert_allclose(expected_frechet, observed_frechet)
def test_problematic_matrix(self):
# this test case uncovered a bug which has since been fixed
A = np.array([
[1.50591997, 1.93537998],
[0.41203263, 0.23443516],
], dtype=float)
E = np.array([
[1.87864034, 2.07055038],
[1.34102727, 0.67341123],
], dtype=float)
scipy.linalg.norm(A, 1)
sps_expm, sps_frechet = expm_frechet(
A, E, method='SPS')
blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(
A, E, method='blockEnlarge')
assert_allclose(sps_expm, blockEnlarge_expm)
assert_allclose(sps_frechet, blockEnlarge_frechet)
@pytest.mark.slow
@pytest.mark.skip(reason='this test is deliberately slow')
def test_medium_matrix(self):
# profile this to see the speed difference
n = 1000
A = np.random.exponential(size=(n, n))
E = np.random.exponential(size=(n, n))
sps_expm, sps_frechet = expm_frechet(
A, E, method='SPS')
blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(
A, E, method='blockEnlarge')
assert_allclose(sps_expm, blockEnlarge_expm)
assert_allclose(sps_frechet, blockEnlarge_frechet)
def _help_expm_cond_search(A, A_norm, X, X_norm, eps, p):
p = np.reshape(p, A.shape)
p_norm = norm(p)
perturbation = eps * p * (A_norm / p_norm)
X_prime = expm(A + perturbation)
scaled_relative_error = norm(X_prime - X) / (X_norm * eps)
return -scaled_relative_error
def _normalized_like(A, B):
return A * (scipy.linalg.norm(B) / scipy.linalg.norm(A))
def _relative_error(f, A, perturbation):
X = f(A)
X_prime = f(A + perturbation)
return norm(X_prime - X) / norm(X)
class TestExpmConditionNumber(object):
def test_expm_cond_smoke(self):
np.random.seed(1234)
for n in range(1, 4):
A = np.random.randn(n, n)
kappa = expm_cond(A)
assert_array_less(0, kappa)
def test_expm_bad_condition_number(self):
A = np.array([
[-1.128679820, 9.614183771e4, -4.524855739e9, 2.924969411e14],
[0, -1.201010529, 9.634696872e4, -4.681048289e9],
[0, 0, -1.132893222, 9.532491830e4],
[0, 0, 0, -1.179475332],
])
kappa = expm_cond(A)
assert_array_less(1e36, kappa)
def test_univariate(self):
np.random.seed(12345)
for x in np.linspace(-5, 5, num=11):
A = np.array([[x]])
assert_allclose(expm_cond(A), abs(x))
for x in np.logspace(-2, 2, num=11):
A = np.array([[x]])
assert_allclose(expm_cond(A), abs(x))
for i in range(10):
A = np.random.randn(1, 1)
assert_allclose(expm_cond(A), np.absolute(A)[0, 0])
@pytest.mark.slow
def test_expm_cond_fuzz(self):
np.random.seed(12345)
eps = 1e-5
nsamples = 10
for i in range(nsamples):
n = np.random.randint(2, 5)
A = np.random.randn(n, n)
A_norm = scipy.linalg.norm(A)
X = expm(A)
X_norm = scipy.linalg.norm(X)
kappa = expm_cond(A)
# Look for the small perturbation that gives the greatest
# relative error.
f = functools.partial(_help_expm_cond_search,
A, A_norm, X, X_norm, eps)
guess = np.ones(n*n)
out = minimize(f, guess, method='L-BFGS-B')
xopt = out.x
yopt = f(xopt)
p_best = eps * _normalized_like(np.reshape(xopt, A.shape), A)
p_best_relerr = _relative_error(expm, A, p_best)
assert_allclose(p_best_relerr, -yopt * eps)
# Check that the identified perturbation indeed gives greater
# relative error than random perturbations with similar norms.
for j in range(5):
p_rand = eps * _normalized_like(np.random.randn(*A.shape), A)
assert_allclose(norm(p_best), norm(p_rand))
p_rand_relerr = _relative_error(expm, A, p_rand)
assert_array_less(p_rand_relerr, p_best_relerr)
# The greatest relative error should not be much greater than
# eps times the condition number kappa.
# In the limit as eps approaches zero it should never be greater.
assert_array_less(p_best_relerr, (1 + 2*eps) * eps * kappa)
class TestKhatriRao(object):
def test_basic(self):
a = khatri_rao(array([[1, 2], [3, 4]]),
array([[5, 6], [7, 8]]))
assert_array_equal(a, array([[5, 12],
[7, 16],
[15, 24],
[21, 32]]))
b = khatri_rao(np.empty([2, 2]), np.empty([2, 2]))
assert_array_equal(b.shape, (4, 2))
def test_number_of_columns_equality(self):
with pytest.raises(ValueError):
a = array([[1, 2, 3],
[4, 5, 6]])
b = array([[1, 2],
[3, 4]])
khatri_rao(a, b)
def test_to_assure_2d_array(self):
with pytest.raises(ValueError):
# both arrays are 1-D
a = array([1, 2, 3])
b = array([4, 5, 6])
khatri_rao(a, b)
with pytest.raises(ValueError):
# first array is 1-D
a = array([1, 2, 3])
b = array([
[1, 2, 3],
[4, 5, 6]
])
khatri_rao(a, b)
with pytest.raises(ValueError):
# second array is 1-D
a = array([
[1, 2, 3],
[7, 8, 9]
])
b = array([4, 5, 6])
khatri_rao(a, b)
def test_equality_of_two_equations(self):
a = array([[1, 2], [3, 4]])
b = array([[5, 6], [7, 8]])
res1 = khatri_rao(a, b)
res2 = np.vstack([np.kron(a[:, k], b[:, k])
for k in range(b.shape[1])]).T
assert_array_equal(res1, res2)

View file

@ -0,0 +1,191 @@
from itertools import product, permutations
import numpy as np
from numpy.testing import assert_array_less, assert_allclose
from pytest import raises as assert_raises
from scipy.linalg import inv, eigh, norm
from scipy.linalg import orthogonal_procrustes
from scipy.sparse.sputils import matrix
def test_orthogonal_procrustes_ndim_too_large():
np.random.seed(1234)
A = np.random.randn(3, 4, 5)
B = np.random.randn(3, 4, 5)
assert_raises(ValueError, orthogonal_procrustes, A, B)
def test_orthogonal_procrustes_ndim_too_small():
np.random.seed(1234)
A = np.random.randn(3)
B = np.random.randn(3)
assert_raises(ValueError, orthogonal_procrustes, A, B)
def test_orthogonal_procrustes_shape_mismatch():
np.random.seed(1234)
shapes = ((3, 3), (3, 4), (4, 3), (4, 4))
for a, b in permutations(shapes, 2):
A = np.random.randn(*a)
B = np.random.randn(*b)
assert_raises(ValueError, orthogonal_procrustes, A, B)
def test_orthogonal_procrustes_checkfinite_exception():
np.random.seed(1234)
m, n = 2, 3
A_good = np.random.randn(m, n)
B_good = np.random.randn(m, n)
for bad_value in np.inf, -np.inf, np.nan:
A_bad = A_good.copy()
A_bad[1, 2] = bad_value
B_bad = B_good.copy()
B_bad[1, 2] = bad_value
for A, B in ((A_good, B_bad), (A_bad, B_good), (A_bad, B_bad)):
assert_raises(ValueError, orthogonal_procrustes, A, B)
def test_orthogonal_procrustes_scale_invariance():
np.random.seed(1234)
m, n = 4, 3
for i in range(3):
A_orig = np.random.randn(m, n)
B_orig = np.random.randn(m, n)
R_orig, s = orthogonal_procrustes(A_orig, B_orig)
for A_scale in np.square(np.random.randn(3)):
for B_scale in np.square(np.random.randn(3)):
R, s = orthogonal_procrustes(A_orig * A_scale, B_orig * B_scale)
assert_allclose(R, R_orig)
def test_orthogonal_procrustes_array_conversion():
np.random.seed(1234)
for m, n in ((6, 4), (4, 4), (4, 6)):
A_arr = np.random.randn(m, n)
B_arr = np.random.randn(m, n)
As = (A_arr, A_arr.tolist(), matrix(A_arr))
Bs = (B_arr, B_arr.tolist(), matrix(B_arr))
R_arr, s = orthogonal_procrustes(A_arr, B_arr)
AR_arr = A_arr.dot(R_arr)
for A, B in product(As, Bs):
R, s = orthogonal_procrustes(A, B)
AR = A_arr.dot(R)
assert_allclose(AR, AR_arr)
def test_orthogonal_procrustes():
np.random.seed(1234)
for m, n in ((6, 4), (4, 4), (4, 6)):
# Sample a random target matrix.
B = np.random.randn(m, n)
# Sample a random orthogonal matrix
# by computing eigh of a sampled symmetric matrix.
X = np.random.randn(n, n)
w, V = eigh(X.T + X)
assert_allclose(inv(V), V.T)
# Compute a matrix with a known orthogonal transformation that gives B.
A = np.dot(B, V.T)
# Check that an orthogonal transformation from A to B can be recovered.
R, s = orthogonal_procrustes(A, B)
assert_allclose(inv(R), R.T)
assert_allclose(A.dot(R), B)
# Create a perturbed input matrix.
A_perturbed = A + 1e-2 * np.random.randn(m, n)
# Check that the orthogonal procrustes function can find an orthogonal
# transformation that is better than the orthogonal transformation
# computed from the original input matrix.
R_prime, s = orthogonal_procrustes(A_perturbed, B)
assert_allclose(inv(R_prime), R_prime.T)
# Compute the naive and optimal transformations of the perturbed input.
naive_approx = A_perturbed.dot(R)
optim_approx = A_perturbed.dot(R_prime)
# Compute the Frobenius norm errors of the matrix approximations.
naive_approx_error = norm(naive_approx - B, ord='fro')
optim_approx_error = norm(optim_approx - B, ord='fro')
# Check that the orthogonal Procrustes approximation is better.
assert_array_less(optim_approx_error, naive_approx_error)
def _centered(A):
mu = A.mean(axis=0)
return A - mu, mu
def test_orthogonal_procrustes_exact_example():
# Check a small application.
# It uses translation, scaling, reflection, and rotation.
#
# |
# a b |
# |
# d c | w
# |
# --------+--- x ----- z ---
# |
# | y
# |
#
A_orig = np.array([[-3, 3], [-2, 3], [-2, 2], [-3, 2]], dtype=float)
B_orig = np.array([[3, 2], [1, 0], [3, -2], [5, 0]], dtype=float)
A, A_mu = _centered(A_orig)
B, B_mu = _centered(B_orig)
R, s = orthogonal_procrustes(A, B)
scale = s / np.square(norm(A))
B_approx = scale * np.dot(A, R) + B_mu
assert_allclose(B_approx, B_orig, atol=1e-8)
def test_orthogonal_procrustes_stretched_example():
# Try again with a target with a stretched y axis.
A_orig = np.array([[-3, 3], [-2, 3], [-2, 2], [-3, 2]], dtype=float)
B_orig = np.array([[3, 40], [1, 0], [3, -40], [5, 0]], dtype=float)
A, A_mu = _centered(A_orig)
B, B_mu = _centered(B_orig)
R, s = orthogonal_procrustes(A, B)
scale = s / np.square(norm(A))
B_approx = scale * np.dot(A, R) + B_mu
expected = np.array([[3, 21], [-18, 0], [3, -21], [24, 0]], dtype=float)
assert_allclose(B_approx, expected, atol=1e-8)
# Check disparity symmetry.
expected_disparity = 0.4501246882793018
AB_disparity = np.square(norm(B_approx - B_orig) / norm(B))
assert_allclose(AB_disparity, expected_disparity)
R, s = orthogonal_procrustes(B, A)
scale = s / np.square(norm(B))
A_approx = scale * np.dot(B, R) + A_mu
BA_disparity = np.square(norm(A_approx - A_orig) / norm(A))
assert_allclose(BA_disparity, expected_disparity)
def test_orthogonal_procrustes_skbio_example():
# This transformation is also exact.
# It uses translation, scaling, and reflection.
#
# |
# | a
# | b
# | c d
# --+---------
# |
# | w
# |
# | x
# |
# | z y
# |
#
A_orig = np.array([[4, -2], [4, -4], [4, -6], [2, -6]], dtype=float)
B_orig = np.array([[1, 3], [1, 2], [1, 1], [2, 1]], dtype=float)
B_standardized = np.array([
[-0.13363062, 0.6681531],
[-0.13363062, 0.13363062],
[-0.13363062, -0.40089186],
[0.40089186, -0.40089186]])
A, A_mu = _centered(A_orig)
B, B_mu = _centered(B_orig)
R, s = orthogonal_procrustes(A, B)
scale = s / np.square(norm(A))
B_approx = scale * np.dot(A, R) + B_mu
assert_allclose(B_approx, B_orig)
assert_allclose(B / norm(B), B_standardized)

View file

@ -0,0 +1,118 @@
"""Tests for _sketches.py."""
import numpy as np
from numpy.testing import assert_, assert_equal
from scipy.linalg import clarkson_woodruff_transform
from scipy.linalg._sketches import cwt_matrix
from scipy.sparse import issparse, rand
from scipy.sparse.linalg import norm
class TestClarksonWoodruffTransform(object):
"""
Testing the Clarkson Woodruff Transform
"""
# set seed for generating test matrices
rng = np.random.RandomState(seed=1179103485)
# Test matrix parameters
n_rows = 2000
n_cols = 100
density = 0.1
# Sketch matrix dimensions
n_sketch_rows = 200
# Seeds to test with
seeds = [1755490010, 934377150, 1391612830, 1752708722, 2008891431,
1302443994, 1521083269, 1501189312, 1126232505, 1533465685]
A_dense = rng.randn(n_rows, n_cols)
A_csc = rand(
n_rows, n_cols, density=density, format='csc', random_state=rng,
)
A_csr = rand(
n_rows, n_cols, density=density, format='csr', random_state=rng,
)
A_coo = rand(
n_rows, n_cols, density=density, format='coo', random_state=rng,
)
# Collect the test matrices
test_matrices = [
A_dense, A_csc, A_csr, A_coo,
]
# Test vector with norm ~1
x = rng.randn(n_rows, 1) / np.sqrt(n_rows)
def test_sketch_dimensions(self):
for A in self.test_matrices:
for seed in self.seeds:
sketch = clarkson_woodruff_transform(
A, self.n_sketch_rows, seed=seed
)
assert_(sketch.shape == (self.n_sketch_rows, self.n_cols))
def test_seed_returns_identical_transform_matrix(self):
for A in self.test_matrices:
for seed in self.seeds:
S1 = cwt_matrix(
self.n_sketch_rows, self.n_rows, seed=seed
).todense()
S2 = cwt_matrix(
self.n_sketch_rows, self.n_rows, seed=seed
).todense()
assert_equal(S1, S2)
def test_seed_returns_identically(self):
for A in self.test_matrices:
for seed in self.seeds:
sketch1 = clarkson_woodruff_transform(
A, self.n_sketch_rows, seed=seed
)
sketch2 = clarkson_woodruff_transform(
A, self.n_sketch_rows, seed=seed
)
if issparse(sketch1):
sketch1 = sketch1.todense()
if issparse(sketch2):
sketch2 = sketch2.todense()
assert_equal(sketch1, sketch2)
def test_sketch_preserves_frobenius_norm(self):
# Given the probabilistic nature of the sketches
# we run the test multiple times and check that
# we pass all/almost all the tries.
n_errors = 0
for A in self.test_matrices:
if issparse(A):
true_norm = norm(A)
else:
true_norm = np.linalg.norm(A)
for seed in self.seeds:
sketch = clarkson_woodruff_transform(
A, self.n_sketch_rows, seed=seed,
)
if issparse(sketch):
sketch_norm = norm(sketch)
else:
sketch_norm = np.linalg.norm(sketch)
if np.abs(true_norm - sketch_norm) > 0.1 * true_norm:
n_errors += 1
assert_(n_errors == 0)
def test_sketch_preserves_vector_norm(self):
n_errors = 0
n_sketch_rows = int(np.ceil(2. / (0.01 * 0.5**2)))
true_norm = np.linalg.norm(self.x)
for seed in self.seeds:
sketch = clarkson_woodruff_transform(
self.x, n_sketch_rows, seed=seed,
)
sketch_norm = np.linalg.norm(sketch)
if np.abs(true_norm - sketch_norm) > 0.5 * true_norm:
n_errors += 1
assert_(n_errors == 0)

View file

@ -0,0 +1,121 @@
"""Test functions for linalg._solve_toeplitz module
"""
import numpy as np
from scipy.linalg._solve_toeplitz import levinson
from scipy.linalg import solve, toeplitz, solve_toeplitz
from numpy.testing import assert_equal, assert_allclose
import pytest
from pytest import raises as assert_raises
def test_solve_equivalence():
# For toeplitz matrices, solve_toeplitz() should be equivalent to solve().
random = np.random.RandomState(1234)
for n in (1, 2, 3, 10):
c = random.randn(n)
if random.rand() < 0.5:
c = c + 1j * random.randn(n)
r = random.randn(n)
if random.rand() < 0.5:
r = r + 1j * random.randn(n)
y = random.randn(n)
if random.rand() < 0.5:
y = y + 1j * random.randn(n)
# Check equivalence when both the column and row are provided.
actual = solve_toeplitz((c,r), y)
desired = solve(toeplitz(c, r=r), y)
assert_allclose(actual, desired)
# Check equivalence when the column is provided but not the row.
actual = solve_toeplitz(c, b=y)
desired = solve(toeplitz(c), y)
assert_allclose(actual, desired)
def test_multiple_rhs():
random = np.random.RandomState(1234)
c = random.randn(4)
r = random.randn(4)
for offset in [0, 1j]:
for yshape in ((4,), (4, 3), (4, 3, 2)):
y = random.randn(*yshape) + offset
actual = solve_toeplitz((c,r), b=y)
desired = solve(toeplitz(c, r=r), y)
assert_equal(actual.shape, yshape)
assert_equal(desired.shape, yshape)
assert_allclose(actual, desired)
def test_native_list_arguments():
c = [1,2,4,7]
r = [1,3,9,12]
y = [5,1,4,2]
actual = solve_toeplitz((c,r), y)
desired = solve(toeplitz(c, r=r), y)
assert_allclose(actual, desired)
def test_zero_diag_error():
# The Levinson-Durbin implementation fails when the diagonal is zero.
random = np.random.RandomState(1234)
n = 4
c = random.randn(n)
r = random.randn(n)
y = random.randn(n)
c[0] = 0
assert_raises(np.linalg.LinAlgError,
solve_toeplitz, (c, r), b=y)
def test_wikipedia_counterexample():
# The Levinson-Durbin implementation also fails in other cases.
# This example is from the talk page of the wikipedia article.
random = np.random.RandomState(1234)
c = [2, 2, 1]
y = random.randn(3)
assert_raises(np.linalg.LinAlgError, solve_toeplitz, c, b=y)
def test_reflection_coeffs():
# check that that the partial solutions are given by the reflection
# coefficients
random = np.random.RandomState(1234)
y_d = random.randn(10)
y_z = random.randn(10) + 1j
reflection_coeffs_d = [1]
reflection_coeffs_z = [1]
for i in range(2, 10):
reflection_coeffs_d.append(solve_toeplitz(y_d[:(i-1)], b=y_d[1:i])[-1])
reflection_coeffs_z.append(solve_toeplitz(y_z[:(i-1)], b=y_z[1:i])[-1])
y_d_concat = np.concatenate((y_d[-2:0:-1], y_d[:-1]))
y_z_concat = np.concatenate((y_z[-2:0:-1].conj(), y_z[:-1]))
_, ref_d = levinson(y_d_concat, b=y_d[1:])
_, ref_z = levinson(y_z_concat, b=y_z[1:])
assert_allclose(reflection_coeffs_d, ref_d[:-1])
assert_allclose(reflection_coeffs_z, ref_z[:-1])
@pytest.mark.xfail(reason='Instability of Levinson iteration')
def test_unstable():
# this is a "Gaussian Toeplitz matrix", as mentioned in Example 2 of
# I. Gohbert, T. Kailath and V. Olshevsky "Fast Gaussian Elimination with
# Partial Pivoting for Matrices with Displacement Structure"
# Mathematics of Computation, 64, 212 (1995), pp 1557-1576
# which can be unstable for levinson recursion.
# other fast toeplitz solvers such as GKO or Burg should be better.
random = np.random.RandomState(1234)
n = 100
c = 0.9 ** (np.arange(n)**2)
y = random.randn(n)
solution1 = solve_toeplitz(c, b=y)
solution2 = solve(toeplitz(c), y)
assert_allclose(solution1, solution2)

View file

@ -0,0 +1,766 @@
import os
import numpy as np
from numpy.testing import assert_array_almost_equal
import pytest
from pytest import raises as assert_raises
from scipy.linalg import solve_sylvester
from scipy.linalg import solve_continuous_lyapunov, solve_discrete_lyapunov
from scipy.linalg import solve_continuous_are, solve_discrete_are
from scipy.linalg import block_diag, solve, LinAlgError
from scipy.sparse.sputils import matrix
def _load_data(name):
"""
Load npz data file under data/
Returns a copy of the data, rather than keeping the npz file open.
"""
filename = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'data', name)
with np.load(filename) as f:
return dict(f.items())
class TestSolveLyapunov(object):
cases = [
(np.array([[1, 2], [3, 4]]),
np.array([[9, 10], [11, 12]])),
# a, q all complex.
(np.array([[1.0+1j, 2.0], [3.0-4.0j, 5.0]]),
np.array([[2.0-2j, 2.0+2j], [-1.0-1j, 2.0]])),
# a real; q complex.
(np.array([[1.0, 2.0], [3.0, 5.0]]),
np.array([[2.0-2j, 2.0+2j], [-1.0-1j, 2.0]])),
# a complex; q real.
(np.array([[1.0+1j, 2.0], [3.0-4.0j, 5.0]]),
np.array([[2.0, 2.0], [-1.0, 2.0]])),
# An example from Kitagawa, 1977
(np.array([[3, 9, 5, 1, 4], [1, 2, 3, 8, 4], [4, 6, 6, 6, 3],
[1, 5, 2, 0, 7], [5, 3, 3, 1, 5]]),
np.array([[2, 4, 1, 0, 1], [4, 1, 0, 2, 0], [1, 0, 3, 0, 3],
[0, 2, 0, 1, 0], [1, 0, 3, 0, 4]])),
# Companion matrix example. a complex; q real; a.shape[0] = 11
(np.array([[0.100+0.j, 0.091+0.j, 0.082+0.j, 0.073+0.j, 0.064+0.j,
0.055+0.j, 0.046+0.j, 0.037+0.j, 0.028+0.j, 0.019+0.j,
0.010+0.j],
[1.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 1.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 1.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 1.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 1.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
1.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 1.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 1.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 1.000+0.j, 0.000+0.j,
0.000+0.j],
[0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j,
0.000+0.j, 0.000+0.j, 0.000+0.j, 0.000+0.j, 1.000+0.j,
0.000+0.j]]),
np.eye(11)),
# https://github.com/scipy/scipy/issues/4176
(matrix([[0, 1], [-1/2, -1]]),
(matrix([0, 3]).T @ matrix([0, 3]).T.T)),
# https://github.com/scipy/scipy/issues/4176
(matrix([[0, 1], [-1/2, -1]]),
(np.array(matrix([0, 3]).T @ matrix([0, 3]).T.T))),
]
def test_continuous_squareness_and_shape(self):
nsq = np.ones((3, 2))
sq = np.eye(3)
assert_raises(ValueError, solve_continuous_lyapunov, nsq, sq)
assert_raises(ValueError, solve_continuous_lyapunov, sq, nsq)
assert_raises(ValueError, solve_continuous_lyapunov, sq, np.eye(2))
def check_continuous_case(self, a, q):
x = solve_continuous_lyapunov(a, q)
assert_array_almost_equal(
np.dot(a, x) + np.dot(x, a.conj().transpose()), q)
def check_discrete_case(self, a, q, method=None):
x = solve_discrete_lyapunov(a, q, method=method)
assert_array_almost_equal(
np.dot(np.dot(a, x), a.conj().transpose()) - x, -1.0*q)
def test_cases(self):
for case in self.cases:
self.check_continuous_case(case[0], case[1])
self.check_discrete_case(case[0], case[1])
self.check_discrete_case(case[0], case[1], method='direct')
self.check_discrete_case(case[0], case[1], method='bilinear')
def test_solve_continuous_are():
mat6 = _load_data('carex_6_data.npz')
mat15 = _load_data('carex_15_data.npz')
mat18 = _load_data('carex_18_data.npz')
mat19 = _load_data('carex_19_data.npz')
mat20 = _load_data('carex_20_data.npz')
cases = [
# Carex examples taken from (with default parameters):
# [1] P.BENNER, A.J. LAUB, V. MEHRMANN: 'A Collection of Benchmark
# Examples for the Numerical Solution of Algebraic Riccati
# Equations II: Continuous-Time Case', Tech. Report SPC 95_23,
# Fak. f. Mathematik, TU Chemnitz-Zwickau (Germany), 1995.
#
# The format of the data is (a, b, q, r, knownfailure), where
# knownfailure is None if the test passes or a string
# indicating the reason for failure.
#
# Test Case 0: carex #1
(np.diag([1.], 1),
np.array([[0], [1]]),
block_diag(1., 2.),
1,
None),
# Test Case 1: carex #2
(np.array([[4, 3], [-4.5, -3.5]]),
np.array([[1], [-1]]),
np.array([[9, 6], [6, 4.]]),
1,
None),
# Test Case 2: carex #3
(np.array([[0, 1, 0, 0],
[0, -1.89, 0.39, -5.53],
[0, -0.034, -2.98, 2.43],
[0.034, -0.0011, -0.99, -0.21]]),
np.array([[0, 0], [0.36, -1.6], [-0.95, -0.032], [0.03, 0]]),
np.array([[2.313, 2.727, 0.688, 0.023],
[2.727, 4.271, 1.148, 0.323],
[0.688, 1.148, 0.313, 0.102],
[0.023, 0.323, 0.102, 0.083]]),
np.eye(2),
None),
# Test Case 3: carex #4
(np.array([[-0.991, 0.529, 0, 0, 0, 0, 0, 0],
[0.522, -1.051, 0.596, 0, 0, 0, 0, 0],
[0, 0.522, -1.118, 0.596, 0, 0, 0, 0],
[0, 0, 0.522, -1.548, 0.718, 0, 0, 0],
[0, 0, 0, 0.922, -1.64, 0.799, 0, 0],
[0, 0, 0, 0, 0.922, -1.721, 0.901, 0],
[0, 0, 0, 0, 0, 0.922, -1.823, 1.021],
[0, 0, 0, 0, 0, 0, 0.922, -1.943]]),
np.array([[3.84, 4.00, 37.60, 3.08, 2.36, 2.88, 3.08, 3.00],
[-2.88, -3.04, -2.80, -2.32, -3.32, -3.82, -4.12, -3.96]]
).T * 0.001,
np.array([[1.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.1],
[0.0, 1.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
[0.5, 0.1, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.5, 0.0, 0.0, 0.1, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0],
[0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1]]),
np.eye(2),
None),
# Test Case 4: carex #5
(np.array(
[[-4.019, 5.120, 0., 0., -2.082, 0., 0., 0., 0.870],
[-0.346, 0.986, 0., 0., -2.340, 0., 0., 0., 0.970],
[-7.909, 15.407, -4.069, 0., -6.450, 0., 0., 0., 2.680],
[-21.816, 35.606, -0.339, -3.870, -17.800, 0., 0., 0., 7.390],
[-60.196, 98.188, -7.907, 0.340, -53.008, 0., 0., 0., 20.400],
[0, 0, 0, 0, 94.000, -147.200, 0., 53.200, 0.],
[0, 0, 0, 0, 0, 94.000, -147.200, 0, 0],
[0, 0, 0, 0, 0, 12.800, 0.000, -31.600, 0],
[0, 0, 0, 0, 12.800, 0.000, 0.000, 18.800, -31.600]]),
np.array([[0.010, -0.011, -0.151],
[0.003, -0.021, 0.000],
[0.009, -0.059, 0.000],
[0.024, -0.162, 0.000],
[0.068, -0.445, 0.000],
[0.000, 0.000, 0.000],
[0.000, 0.000, 0.000],
[0.000, 0.000, 0.000],
[0.000, 0.000, 0.000]]),
np.eye(9),
np.eye(3),
None),
# Test Case 5: carex #6
(mat6['A'], mat6['B'], mat6['Q'], mat6['R'], None),
# Test Case 6: carex #7
(np.array([[1, 0], [0, -2.]]),
np.array([[1e-6], [0]]),
np.ones((2, 2)),
1.,
'Bad residual accuracy'),
# Test Case 7: carex #8
(block_diag(-0.1, -0.02),
np.array([[0.100, 0.000], [0.001, 0.010]]),
np.array([[100, 1000], [1000, 10000]]),
np.ones((2, 2)) + block_diag(1e-6, 0),
None),
# Test Case 8: carex #9
(np.array([[0, 1e6], [0, 0]]),
np.array([[0], [1.]]),
np.eye(2),
1.,
None),
# Test Case 9: carex #10
(np.array([[1.0000001, 1], [1., 1.0000001]]),
np.eye(2),
np.eye(2),
np.eye(2),
None),
# Test Case 10: carex #11
(np.array([[3, 1.], [4, 2]]),
np.array([[1], [1]]),
np.array([[-11, -5], [-5, -2.]]),
1.,
None),
# Test Case 11: carex #12
(np.array([[7000000., 2000000., -0.],
[2000000., 6000000., -2000000.],
[0., -2000000., 5000000.]]) / 3,
np.eye(3),
np.array([[1., -2., -2.], [-2., 1., -2.], [-2., -2., 1.]]).dot(
np.diag([1e-6, 1, 1e6])).dot(
np.array([[1., -2., -2.], [-2., 1., -2.], [-2., -2., 1.]])) / 9,
np.eye(3) * 1e6,
'Bad Residual Accuracy'),
# Test Case 12: carex #13
(np.array([[0, 0.4, 0, 0],
[0, 0, 0.345, 0],
[0, -0.524e6, -0.465e6, 0.262e6],
[0, 0, 0, -1e6]]),
np.array([[0, 0, 0, 1e6]]).T,
np.diag([1, 0, 1, 0]),
1.,
None),
# Test Case 13: carex #14
(np.array([[-1e-6, 1, 0, 0],
[-1, -1e-6, 0, 0],
[0, 0, 1e-6, 1],
[0, 0, -1, 1e-6]]),
np.ones((4, 1)),
np.ones((4, 4)),
1.,
None),
# Test Case 14: carex #15
(mat15['A'], mat15['B'], mat15['Q'], mat15['R'], None),
# Test Case 15: carex #16
(np.eye(64, 64, k=-1) + np.eye(64, 64)*(-2.) + np.rot90(
block_diag(1, np.zeros((62, 62)), 1)) + np.eye(64, 64, k=1),
np.eye(64),
np.eye(64),
np.eye(64),
None),
# Test Case 16: carex #17
(np.diag(np.ones((20, )), 1),
np.flipud(np.eye(21, 1)),
np.eye(21, 1) * np.eye(21, 1).T,
1,
'Bad Residual Accuracy'),
# Test Case 17: carex #18
(mat18['A'], mat18['B'], mat18['Q'], mat18['R'], None),
# Test Case 18: carex #19
(mat19['A'], mat19['B'], mat19['Q'], mat19['R'],
'Bad Residual Accuracy'),
# Test Case 19: carex #20
(mat20['A'], mat20['B'], mat20['Q'], mat20['R'],
'Bad Residual Accuracy')
]
# Makes the minimum precision requirements customized to the test.
# Here numbers represent the number of decimals that agrees with zero
# matrix when the solution x is plugged in to the equation.
#
# res = array([[8e-3,1e-16],[1e-16,1e-20]]) --> min_decimal[k] = 2
#
# If the test is failing use "None" for that entry.
#
min_decimal = (14, 12, 13, 14, 11, 6, None, 5, 7, 14, 14,
None, 9, 14, 13, 14, None, 12, None, None)
def _test_factory(case, dec):
"""Checks if 0 = XA + A'X - XB(R)^{-1} B'X + Q is true"""
a, b, q, r, knownfailure = case
if knownfailure:
pytest.xfail(reason=knownfailure)
x = solve_continuous_are(a, b, q, r)
res = x.dot(a) + a.conj().T.dot(x) + q
out_fact = x.dot(b)
res -= out_fact.dot(solve(np.atleast_2d(r), out_fact.conj().T))
assert_array_almost_equal(res, np.zeros_like(res), decimal=dec)
for ind, case in enumerate(cases):
_test_factory(case, min_decimal[ind])
def test_solve_discrete_are():
cases = [
# Darex examples taken from (with default parameters):
# [1] P.BENNER, A.J. LAUB, V. MEHRMANN: 'A Collection of Benchmark
# Examples for the Numerical Solution of Algebraic Riccati
# Equations II: Discrete-Time Case', Tech. Report SPC 95_23,
# Fak. f. Mathematik, TU Chemnitz-Zwickau (Germany), 1995.
# [2] T. GUDMUNDSSON, C. KENNEY, A.J. LAUB: 'Scaling of the
# Discrete-Time Algebraic Riccati Equation to Enhance Stability
# of the Schur Solution Method', IEEE Trans.Aut.Cont., vol.37(4)
#
# The format of the data is (a, b, q, r, knownfailure), where
# knownfailure is None if the test passes or a string
# indicating the reason for failure.
#
# TEST CASE 0 : Complex a; real b, q, r
(np.array([[2, 1-2j], [0, -3j]]),
np.array([[0], [1]]),
np.array([[1, 0], [0, 2]]),
np.array([[1]]),
None),
# TEST CASE 1 :Real a, q, r; complex b
(np.array([[2, 1], [0, -1]]),
np.array([[-2j], [1j]]),
np.array([[1, 0], [0, 2]]),
np.array([[1]]),
None),
# TEST CASE 2 : Real a, b; complex q, r
(np.array([[3, 1], [0, -1]]),
np.array([[1, 2], [1, 3]]),
np.array([[1, 1+1j], [1-1j, 2]]),
np.array([[2, -2j], [2j, 3]]),
None),
# TEST CASE 3 : User-reported gh-2251 (Trac #1732)
(np.array([[0.63399379, 0.54906824, 0.76253406],
[0.5404729, 0.53745766, 0.08731853],
[0.27524045, 0.84922129, 0.4681622]]),
np.array([[0.96861695], [0.05532739], [0.78934047]]),
np.eye(3),
np.eye(1),
None),
# TEST CASE 4 : darex #1
(np.array([[4, 3], [-4.5, -3.5]]),
np.array([[1], [-1]]),
np.array([[9, 6], [6, 4]]),
np.array([[1]]),
None),
# TEST CASE 5 : darex #2
(np.array([[0.9512, 0], [0, 0.9048]]),
np.array([[4.877, 4.877], [-1.1895, 3.569]]),
np.array([[0.005, 0], [0, 0.02]]),
np.array([[1/3, 0], [0, 3]]),
None),
# TEST CASE 6 : darex #3
(np.array([[2, -1], [1, 0]]),
np.array([[1], [0]]),
np.array([[0, 0], [0, 1]]),
np.array([[0]]),
None),
# TEST CASE 7 : darex #4 (skipped the gen. Ric. term S)
(np.array([[0, 1], [0, -1]]),
np.array([[1, 0], [2, 1]]),
np.array([[-4, -4], [-4, 7]]) * (1/11),
np.array([[9, 3], [3, 1]]),
None),
# TEST CASE 8 : darex #5
(np.array([[0, 1], [0, 0]]),
np.array([[0], [1]]),
np.array([[1, 2], [2, 4]]),
np.array([[1]]),
None),
# TEST CASE 9 : darex #6
(np.array([[0.998, 0.067, 0, 0],
[-.067, 0.998, 0, 0],
[0, 0, 0.998, 0.153],
[0, 0, -.153, 0.998]]),
np.array([[0.0033, 0.0200],
[0.1000, -.0007],
[0.0400, 0.0073],
[-.0028, 0.1000]]),
np.array([[1.87, 0, 0, -0.244],
[0, 0.744, 0.205, 0],
[0, 0.205, 0.589, 0],
[-0.244, 0, 0, 1.048]]),
np.eye(2),
None),
# TEST CASE 10 : darex #7
(np.array([[0.984750, -.079903, 0.0009054, -.0010765],
[0.041588, 0.998990, -.0358550, 0.0126840],
[-.546620, 0.044916, -.3299100, 0.1931800],
[2.662400, -.100450, -.9245500, -.2632500]]),
np.array([[0.0037112, 0.0007361],
[-.0870510, 9.3411e-6],
[-1.198440, -4.1378e-4],
[-3.192700, 9.2535e-4]]),
np.eye(4)*1e-2,
np.eye(2),
None),
# TEST CASE 11 : darex #8
(np.array([[-0.6000000, -2.2000000, -3.6000000, -5.4000180],
[1.0000000, 0.6000000, 0.8000000, 3.3999820],
[0.0000000, 1.0000000, 1.8000000, 3.7999820],
[0.0000000, 0.0000000, 0.0000000, -0.9999820]]),
np.array([[1.0, -1.0, -1.0, -1.0],
[0.0, 1.0, -1.0, -1.0],
[0.0, 0.0, 1.0, -1.0],
[0.0, 0.0, 0.0, 1.0]]),
np.array([[2, 1, 3, 6],
[1, 2, 2, 5],
[3, 2, 6, 11],
[6, 5, 11, 22]]),
np.eye(4),
None),
# TEST CASE 12 : darex #9
(np.array([[95.4070, 1.9643, 0.3597, 0.0673, 0.0190],
[40.8490, 41.3170, 16.0840, 4.4679, 1.1971],
[12.2170, 26.3260, 36.1490, 15.9300, 12.3830],
[4.1118, 12.8580, 27.2090, 21.4420, 40.9760],
[0.1305, 0.5808, 1.8750, 3.6162, 94.2800]]) * 0.01,
np.array([[0.0434, -0.0122],
[2.6606, -1.0453],
[3.7530, -5.5100],
[3.6076, -6.6000],
[0.4617, -0.9148]]) * 0.01,
np.eye(5),
np.eye(2),
None),
# TEST CASE 13 : darex #10
(np.kron(np.eye(2), np.diag([1, 1], k=1)),
np.kron(np.eye(2), np.array([[0], [0], [1]])),
np.array([[1, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, -1, 0],
[0, 0, 0, -1, 1, 0],
[0, 0, 0, 0, 0, 0]]),
np.array([[3, 0], [0, 1]]),
None),
# TEST CASE 14 : darex #11
(0.001 * np.array(
[[870.1, 135.0, 11.59, .5014, -37.22, .3484, 0, 4.242, 7.249],
[76.55, 897.4, 12.72, 0.5504, -40.16, .3743, 0, 4.53, 7.499],
[-127.2, 357.5, 817, 1.455, -102.8, .987, 0, 11.85, 18.72],
[-363.5, 633.9, 74.91, 796.6, -273.5, 2.653, 0, 31.72, 48.82],
[-960, 1645.9, -128.9, -5.597, 71.42, 7.108, 0, 84.52, 125.9],
[-664.4, 112.96, -88.89, -3.854, 84.47, 13.6, 0, 144.3, 101.6],
[-410.2, 693, -54.71, -2.371, 66.49, 12.49, .1063, 99.97, 69.67],
[-179.9, 301.7, -23.93, -1.035, 60.59, 22.16, 0, 213.9, 35.54],
[-345.1, 580.4, -45.96, -1.989, 105.6, 19.86, 0, 219.1, 215.2]]),
np.array([[4.7600, -0.5701, -83.6800],
[0.8790, -4.7730, -2.7300],
[1.4820, -13.1200, 8.8760],
[3.8920, -35.1300, 24.8000],
[10.3400, -92.7500, 66.8000],
[7.2030, -61.5900, 38.3400],
[4.4540, -36.8300, 20.2900],
[1.9710, -15.5400, 6.9370],
[3.7730, -30.2800, 14.6900]]) * 0.001,
np.diag([50, 0, 0, 0, 50, 0, 0, 0, 0]),
np.eye(3),
None),
# TEST CASE 15 : darex #12 - numerically least accurate example
(np.array([[0, 1e6], [0, 0]]),
np.array([[0], [1]]),
np.eye(2),
np.array([[1]]),
None),
# TEST CASE 16 : darex #13
(np.array([[16, 10, -2],
[10, 13, -8],
[-2, -8, 7]]) * (1/9),
np.eye(3),
1e6 * np.eye(3),
1e6 * np.eye(3),
None),
# TEST CASE 17 : darex #14
(np.array([[1 - 1/1e8, 0, 0, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0]]),
np.array([[1e-08], [0], [0], [0]]),
np.diag([0, 0, 0, 1]),
np.array([[0.25]]),
None),
# TEST CASE 18 : darex #15
(np.eye(100, k=1),
np.flipud(np.eye(100, 1)),
np.eye(100),
np.array([[1]]),
None)
]
# Makes the minimum precision requirements customized to the test.
# Here numbers represent the number of decimals that agrees with zero
# matrix when the solution x is plugged in to the equation.
#
# res = array([[8e-3,1e-16],[1e-16,1e-20]]) --> min_decimal[k] = 2
#
# If the test is failing use "None" for that entry.
#
min_decimal = (12, 14, 13, 14, 13, 16, 18, 14, 14, 13,
14, 13, 13, 14, 12, 2, 5, 6, 10)
def _test_factory(case, dec):
"""Checks if X = A'XA-(A'XB)(R+B'XB)^-1(B'XA)+Q) is true"""
a, b, q, r, knownfailure = case
if knownfailure:
pytest.xfail(reason=knownfailure)
x = solve_discrete_are(a, b, q, r)
res = a.conj().T.dot(x.dot(a)) - x + q
res -= a.conj().T.dot(x.dot(b)).dot(
solve(r+b.conj().T.dot(x.dot(b)), b.conj().T).dot(x.dot(a))
)
assert_array_almost_equal(res, np.zeros_like(res), decimal=dec)
for ind, case in enumerate(cases):
_test_factory(case, min_decimal[ind])
# An infeasible example taken from https://arxiv.org/abs/1505.04861v1
A = np.triu(np.ones((3, 3)))
A[0, 1] = -1
B = np.array([[1, 1, 0], [0, 0, 1]]).T
Q = np.full_like(A, -2) + np.diag([8, -1, -1.9])
R = np.diag([-10, 0.1])
assert_raises(LinAlgError, solve_continuous_are, A, B, Q, R)
def test_solve_generalized_continuous_are():
cases = [
# Two random examples differ by s term
# in the absence of any literature for demanding examples.
(np.array([[2.769230e-01, 8.234578e-01, 9.502220e-01],
[4.617139e-02, 6.948286e-01, 3.444608e-02],
[9.713178e-02, 3.170995e-01, 4.387444e-01]]),
np.array([[3.815585e-01, 1.868726e-01],
[7.655168e-01, 4.897644e-01],
[7.951999e-01, 4.455862e-01]]),
np.eye(3),
np.eye(2),
np.array([[6.463130e-01, 2.760251e-01, 1.626117e-01],
[7.093648e-01, 6.797027e-01, 1.189977e-01],
[7.546867e-01, 6.550980e-01, 4.983641e-01]]),
np.zeros((3, 2)),
None),
(np.array([[2.769230e-01, 8.234578e-01, 9.502220e-01],
[4.617139e-02, 6.948286e-01, 3.444608e-02],
[9.713178e-02, 3.170995e-01, 4.387444e-01]]),
np.array([[3.815585e-01, 1.868726e-01],
[7.655168e-01, 4.897644e-01],
[7.951999e-01, 4.455862e-01]]),
np.eye(3),
np.eye(2),
np.array([[6.463130e-01, 2.760251e-01, 1.626117e-01],
[7.093648e-01, 6.797027e-01, 1.189977e-01],
[7.546867e-01, 6.550980e-01, 4.983641e-01]]),
np.ones((3, 2)),
None)
]
min_decimal = (10, 10)
def _test_factory(case, dec):
"""Checks if X = A'XA-(A'XB)(R+B'XB)^-1(B'XA)+Q) is true"""
a, b, q, r, e, s, knownfailure = case
if knownfailure:
pytest.xfail(reason=knownfailure)
x = solve_continuous_are(a, b, q, r, e, s)
res = a.conj().T.dot(x.dot(e)) + e.conj().T.dot(x.dot(a)) + q
out_fact = e.conj().T.dot(x).dot(b) + s
res -= out_fact.dot(solve(np.atleast_2d(r), out_fact.conj().T))
assert_array_almost_equal(res, np.zeros_like(res), decimal=dec)
for ind, case in enumerate(cases):
_test_factory(case, min_decimal[ind])
def test_solve_generalized_discrete_are():
mat20170120 = _load_data('gendare_20170120_data.npz')
cases = [
# Two random examples differ by s term
# in the absence of any literature for demanding examples.
(np.array([[2.769230e-01, 8.234578e-01, 9.502220e-01],
[4.617139e-02, 6.948286e-01, 3.444608e-02],
[9.713178e-02, 3.170995e-01, 4.387444e-01]]),
np.array([[3.815585e-01, 1.868726e-01],
[7.655168e-01, 4.897644e-01],
[7.951999e-01, 4.455862e-01]]),
np.eye(3),
np.eye(2),
np.array([[6.463130e-01, 2.760251e-01, 1.626117e-01],
[7.093648e-01, 6.797027e-01, 1.189977e-01],
[7.546867e-01, 6.550980e-01, 4.983641e-01]]),
np.zeros((3, 2)),
None),
(np.array([[2.769230e-01, 8.234578e-01, 9.502220e-01],
[4.617139e-02, 6.948286e-01, 3.444608e-02],
[9.713178e-02, 3.170995e-01, 4.387444e-01]]),
np.array([[3.815585e-01, 1.868726e-01],
[7.655168e-01, 4.897644e-01],
[7.951999e-01, 4.455862e-01]]),
np.eye(3),
np.eye(2),
np.array([[6.463130e-01, 2.760251e-01, 1.626117e-01],
[7.093648e-01, 6.797027e-01, 1.189977e-01],
[7.546867e-01, 6.550980e-01, 4.983641e-01]]),
np.ones((3, 2)),
None),
# user-reported (under PR-6616) 20-Jan-2017
# tests against the case where E is None but S is provided
(mat20170120['A'],
mat20170120['B'],
mat20170120['Q'],
mat20170120['R'],
None,
mat20170120['S'],
None),
]
min_decimal = (11, 11, 16)
def _test_factory(case, dec):
"""Checks if X = A'XA-(A'XB)(R+B'XB)^-1(B'XA)+Q) is true"""
a, b, q, r, e, s, knownfailure = case
if knownfailure:
pytest.xfail(reason=knownfailure)
x = solve_discrete_are(a, b, q, r, e, s)
if e is None:
e = np.eye(a.shape[0])
if s is None:
s = np.zeros_like(b)
res = a.conj().T.dot(x.dot(a)) - e.conj().T.dot(x.dot(e)) + q
res -= (a.conj().T.dot(x.dot(b)) + s).dot(
solve(r+b.conj().T.dot(x.dot(b)),
(b.conj().T.dot(x.dot(a)) + s.conj().T)
)
)
assert_array_almost_equal(res, np.zeros_like(res), decimal=dec)
for ind, case in enumerate(cases):
_test_factory(case, min_decimal[ind])
def test_are_validate_args():
def test_square_shape():
nsq = np.ones((3, 2))
sq = np.eye(3)
for x in (solve_continuous_are, solve_discrete_are):
assert_raises(ValueError, x, nsq, 1, 1, 1)
assert_raises(ValueError, x, sq, sq, nsq, 1)
assert_raises(ValueError, x, sq, sq, sq, nsq)
assert_raises(ValueError, x, sq, sq, sq, sq, nsq)
def test_compatible_sizes():
nsq = np.ones((3, 2))
sq = np.eye(4)
for x in (solve_continuous_are, solve_discrete_are):
assert_raises(ValueError, x, sq, nsq, 1, 1)
assert_raises(ValueError, x, sq, sq, sq, sq, sq, nsq)
assert_raises(ValueError, x, sq, sq, np.eye(3), sq)
assert_raises(ValueError, x, sq, sq, sq, np.eye(3))
assert_raises(ValueError, x, sq, sq, sq, sq, np.eye(3))
def test_symmetry():
nsym = np.arange(9).reshape(3, 3)
sym = np.eye(3)
for x in (solve_continuous_are, solve_discrete_are):
assert_raises(ValueError, x, sym, sym, nsym, sym)
assert_raises(ValueError, x, sym, sym, sym, nsym)
def test_singularity():
sing = np.full((3, 3), 1e12)
sing[2, 2] -= 1
sq = np.eye(3)
for x in (solve_continuous_are, solve_discrete_are):
assert_raises(ValueError, x, sq, sq, sq, sq, sing)
assert_raises(ValueError, solve_continuous_are, sq, sq, sq, sing)
def test_finiteness():
nm = np.full((2, 2), np.nan)
sq = np.eye(2)
for x in (solve_continuous_are, solve_discrete_are):
assert_raises(ValueError, x, nm, sq, sq, sq)
assert_raises(ValueError, x, sq, nm, sq, sq)
assert_raises(ValueError, x, sq, sq, nm, sq)
assert_raises(ValueError, x, sq, sq, sq, nm)
assert_raises(ValueError, x, sq, sq, sq, sq, nm)
assert_raises(ValueError, x, sq, sq, sq, sq, sq, nm)
class TestSolveSylvester(object):
cases = [
# a, b, c all real.
(np.array([[1, 2], [0, 4]]),
np.array([[5, 6], [0, 8]]),
np.array([[9, 10], [11, 12]])),
# a, b, c all real, 4x4. a and b have non-trival 2x2 blocks in their
# quasi-triangular form.
(np.array([[1.0, 0, 0, 0],
[0, 1.0, 2.0, 0.0],
[0, 0, 3.0, -4],
[0, 0, 2, 5]]),
np.array([[2.0, 0, 0, 1.0],
[0, 1.0, 0.0, 0.0],
[0, 0, 1.0, -1],
[0, 0, 1, 1]]),
np.array([[1.0, 0, 0, 0],
[0, 1.0, 0, 0],
[0, 0, 1.0, 0],
[0, 0, 0, 1.0]])),
# a, b, c all complex.
(np.array([[1.0+1j, 2.0], [3.0-4.0j, 5.0]]),
np.array([[-1.0, 2j], [3.0, 4.0]]),
np.array([[2.0-2j, 2.0+2j], [-1.0-1j, 2.0]])),
# a and b real; c complex.
(np.array([[1.0, 2.0], [3.0, 5.0]]),
np.array([[-1.0, 0], [3.0, 4.0]]),
np.array([[2.0-2j, 2.0+2j], [-1.0-1j, 2.0]])),
# a and c complex; b real.
(np.array([[1.0+1j, 2.0], [3.0-4.0j, 5.0]]),
np.array([[-1.0, 0], [3.0, 4.0]]),
np.array([[2.0-2j, 2.0+2j], [-1.0-1j, 2.0]])),
# a complex; b and c real.
(np.array([[1.0+1j, 2.0], [3.0-4.0j, 5.0]]),
np.array([[-1.0, 0], [3.0, 4.0]]),
np.array([[2.0, 2.0], [-1.0, 2.0]])),
# not square matrices, real
(np.array([[8, 1, 6], [3, 5, 7], [4, 9, 2]]),
np.array([[2, 3], [4, 5]]),
np.array([[1, 2], [3, 4], [5, 6]])),
# not square matrices, complex
(np.array([[8, 1j, 6+2j], [3, 5, 7], [4, 9, 2]]),
np.array([[2, 3], [4, 5-1j]]),
np.array([[1, 2j], [3, 4j], [5j, 6+7j]])),
]
def check_case(self, a, b, c):
x = solve_sylvester(a, b, c)
assert_array_almost_equal(np.dot(a, x) + np.dot(x, b), c)
def test_cases(self):
for case in self.cases:
self.check_case(case[0], case[1], case[2])
def test_trivial(self):
a = np.array([[1.0, 0.0], [0.0, 1.0]])
b = np.array([[1.0]])
c = np.array([2.0, 2.0]).reshape(-1, 1)
x = solve_sylvester(a, b, c)
assert_array_almost_equal(x, np.array([1.0, 1.0]).reshape(-1, 1))

View file

@ -0,0 +1,690 @@
import pytest
import numpy as np
from numpy import arange, add, array, eye, copy, sqrt
from numpy.testing import (assert_equal, assert_array_equal,
assert_array_almost_equal, assert_allclose)
from pytest import raises as assert_raises
from scipy.fft import fft
from scipy.special import comb
from scipy.linalg import (toeplitz, hankel, circulant, hadamard, leslie, dft,
companion, tri, triu, tril, kron, block_diag,
helmert, hilbert, invhilbert, pascal, invpascal,
fiedler, fiedler_companion, eigvals,
convolution_matrix)
from numpy.linalg import cond
def get_mat(n):
data = arange(n)
data = add.outer(data, data)
return data
class TestTri(object):
def test_basic(self):
assert_equal(tri(4), array([[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 1]]))
assert_equal(tri(4, dtype='f'), array([[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 1]], 'f'))
def test_diag(self):
assert_equal(tri(4, k=1), array([[1, 1, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 1],
[1, 1, 1, 1]]))
assert_equal(tri(4, k=-1), array([[0, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0]]))
def test_2d(self):
assert_equal(tri(4, 3), array([[1, 0, 0],
[1, 1, 0],
[1, 1, 1],
[1, 1, 1]]))
assert_equal(tri(3, 4), array([[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0]]))
def test_diag2d(self):
assert_equal(tri(3, 4, k=2), array([[1, 1, 1, 0],
[1, 1, 1, 1],
[1, 1, 1, 1]]))
assert_equal(tri(4, 3, k=-2), array([[0, 0, 0],
[0, 0, 0],
[1, 0, 0],
[1, 1, 0]]))
class TestTril(object):
def test_basic(self):
a = (100*get_mat(5)).astype('l')
b = a.copy()
for k in range(5):
for l in range(k+1, 5):
b[k, l] = 0
assert_equal(tril(a), b)
def test_diag(self):
a = (100*get_mat(5)).astype('f')
b = a.copy()
for k in range(5):
for l in range(k+3, 5):
b[k, l] = 0
assert_equal(tril(a, k=2), b)
b = a.copy()
for k in range(5):
for l in range(max((k-1, 0)), 5):
b[k, l] = 0
assert_equal(tril(a, k=-2), b)
class TestTriu(object):
def test_basic(self):
a = (100*get_mat(5)).astype('l')
b = a.copy()
for k in range(5):
for l in range(k+1, 5):
b[l, k] = 0
assert_equal(triu(a), b)
def test_diag(self):
a = (100*get_mat(5)).astype('f')
b = a.copy()
for k in range(5):
for l in range(max((k-1, 0)), 5):
b[l, k] = 0
assert_equal(triu(a, k=2), b)
b = a.copy()
for k in range(5):
for l in range(k+3, 5):
b[l, k] = 0
assert_equal(triu(a, k=-2), b)
class TestToeplitz(object):
def test_basic(self):
y = toeplitz([1, 2, 3])
assert_array_equal(y, [[1, 2, 3], [2, 1, 2], [3, 2, 1]])
y = toeplitz([1, 2, 3], [1, 4, 5])
assert_array_equal(y, [[1, 4, 5], [2, 1, 4], [3, 2, 1]])
def test_complex_01(self):
data = (1.0 + arange(3.0)) * (1.0 + 1.0j)
x = copy(data)
t = toeplitz(x)
# Calling toeplitz should not change x.
assert_array_equal(x, data)
# According to the docstring, x should be the first column of t.
col0 = t[:, 0]
assert_array_equal(col0, data)
assert_array_equal(t[0, 1:], data[1:].conj())
def test_scalar_00(self):
"""Scalar arguments still produce a 2D array."""
t = toeplitz(10)
assert_array_equal(t, [[10]])
t = toeplitz(10, 20)
assert_array_equal(t, [[10]])
def test_scalar_01(self):
c = array([1, 2, 3])
t = toeplitz(c, 1)
assert_array_equal(t, [[1], [2], [3]])
def test_scalar_02(self):
c = array([1, 2, 3])
t = toeplitz(c, array(1))
assert_array_equal(t, [[1], [2], [3]])
def test_scalar_03(self):
c = array([1, 2, 3])
t = toeplitz(c, array([1]))
assert_array_equal(t, [[1], [2], [3]])
def test_scalar_04(self):
r = array([10, 2, 3])
t = toeplitz(1, r)
assert_array_equal(t, [[1, 2, 3]])
class TestHankel(object):
def test_basic(self):
y = hankel([1, 2, 3])
assert_array_equal(y, [[1, 2, 3], [2, 3, 0], [3, 0, 0]])
y = hankel([1, 2, 3], [3, 4, 5])
assert_array_equal(y, [[1, 2, 3], [2, 3, 4], [3, 4, 5]])
class TestCirculant(object):
def test_basic(self):
y = circulant([1, 2, 3])
assert_array_equal(y, [[1, 3, 2], [2, 1, 3], [3, 2, 1]])
class TestHadamard(object):
def test_basic(self):
y = hadamard(1)
assert_array_equal(y, [[1]])
y = hadamard(2, dtype=float)
assert_array_equal(y, [[1.0, 1.0], [1.0, -1.0]])
y = hadamard(4)
assert_array_equal(y, [[1, 1, 1, 1],
[1, -1, 1, -1],
[1, 1, -1, -1],
[1, -1, -1, 1]])
assert_raises(ValueError, hadamard, 0)
assert_raises(ValueError, hadamard, 5)
class TestLeslie(object):
def test_bad_shapes(self):
assert_raises(ValueError, leslie, [[1, 1], [2, 2]], [3, 4, 5])
assert_raises(ValueError, leslie, [3, 4, 5], [[1, 1], [2, 2]])
assert_raises(ValueError, leslie, [1, 2], [1, 2])
assert_raises(ValueError, leslie, [1], [])
def test_basic(self):
a = leslie([1, 2, 3], [0.25, 0.5])
expected = array([[1.0, 2.0, 3.0],
[0.25, 0.0, 0.0],
[0.0, 0.5, 0.0]])
assert_array_equal(a, expected)
class TestCompanion(object):
def test_bad_shapes(self):
assert_raises(ValueError, companion, [[1, 1], [2, 2]])
assert_raises(ValueError, companion, [0, 4, 5])
assert_raises(ValueError, companion, [1])
assert_raises(ValueError, companion, [])
def test_basic(self):
c = companion([1, 2, 3])
expected = array([
[-2.0, -3.0],
[1.0, 0.0]])
assert_array_equal(c, expected)
c = companion([2.0, 5.0, -10.0])
expected = array([
[-2.5, 5.0],
[1.0, 0.0]])
assert_array_equal(c, expected)
class TestBlockDiag:
def test_basic(self):
x = block_diag(eye(2), [[1, 2], [3, 4], [5, 6]], [[1, 2, 3]])
assert_array_equal(x, [[1, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 2, 0, 0, 0],
[0, 0, 3, 4, 0, 0, 0],
[0, 0, 5, 6, 0, 0, 0],
[0, 0, 0, 0, 1, 2, 3]])
def test_dtype(self):
x = block_diag([[1.5]])
assert_equal(x.dtype, float)
x = block_diag([[True]])
assert_equal(x.dtype, bool)
def test_mixed_dtypes(self):
actual = block_diag([[1]], [[1j]])
desired = np.array([[1, 0], [0, 1j]])
assert_array_equal(actual, desired)
def test_scalar_and_1d_args(self):
a = block_diag(1)
assert_equal(a.shape, (1, 1))
assert_array_equal(a, [[1]])
a = block_diag([2, 3], 4)
assert_array_equal(a, [[2, 3, 0], [0, 0, 4]])
def test_bad_arg(self):
assert_raises(ValueError, block_diag, [[[1]]])
def test_no_args(self):
a = block_diag()
assert_equal(a.ndim, 2)
assert_equal(a.nbytes, 0)
def test_empty_matrix_arg(self):
# regression test for gh-4596: check the shape of the result
# for empty matrix inputs. Empty matrices are no longer ignored
# (gh-4908) it is viewed as a shape (1, 0) matrix.
a = block_diag([[1, 0], [0, 1]],
[],
[[2, 3], [4, 5], [6, 7]])
assert_array_equal(a, [[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 2, 3],
[0, 0, 4, 5],
[0, 0, 6, 7]])
def test_zerosized_matrix_arg(self):
# test for gh-4908: check the shape of the result for
# zero-sized matrix inputs, i.e. matrices with shape (0,n) or (n,0).
# note that [[]] takes shape (1,0)
a = block_diag([[1, 0], [0, 1]],
[[]],
[[2, 3], [4, 5], [6, 7]],
np.zeros([0, 2], dtype='int32'))
assert_array_equal(a, [[1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 2, 3, 0, 0],
[0, 0, 4, 5, 0, 0],
[0, 0, 6, 7, 0, 0]])
class TestKron:
def test_basic(self):
a = kron(array([[1, 2], [3, 4]]), array([[1, 1, 1]]))
assert_array_equal(a, array([[1, 1, 1, 2, 2, 2],
[3, 3, 3, 4, 4, 4]]))
m1 = array([[1, 2], [3, 4]])
m2 = array([[10], [11]])
a = kron(m1, m2)
expected = array([[10, 20],
[11, 22],
[30, 40],
[33, 44]])
assert_array_equal(a, expected)
class TestHelmert(object):
def test_orthogonality(self):
for n in range(1, 7):
H = helmert(n, full=True)
Id = np.eye(n)
assert_allclose(H.dot(H.T), Id, atol=1e-12)
assert_allclose(H.T.dot(H), Id, atol=1e-12)
def test_subspace(self):
for n in range(2, 7):
H_full = helmert(n, full=True)
H_partial = helmert(n)
for U in H_full[1:, :].T, H_partial.T:
C = np.eye(n) - np.full((n, n), 1 / n)
assert_allclose(U.dot(U.T), C)
assert_allclose(U.T.dot(U), np.eye(n-1), atol=1e-12)
class TestHilbert(object):
def test_basic(self):
h3 = array([[1.0, 1/2., 1/3.],
[1/2., 1/3., 1/4.],
[1/3., 1/4., 1/5.]])
assert_array_almost_equal(hilbert(3), h3)
assert_array_equal(hilbert(1), [[1.0]])
h0 = hilbert(0)
assert_equal(h0.shape, (0, 0))
class TestInvHilbert(object):
def test_basic(self):
invh1 = array([[1]])
assert_array_equal(invhilbert(1, exact=True), invh1)
assert_array_equal(invhilbert(1), invh1)
invh2 = array([[4, -6],
[-6, 12]])
assert_array_equal(invhilbert(2, exact=True), invh2)
assert_array_almost_equal(invhilbert(2), invh2)
invh3 = array([[9, -36, 30],
[-36, 192, -180],
[30, -180, 180]])
assert_array_equal(invhilbert(3, exact=True), invh3)
assert_array_almost_equal(invhilbert(3), invh3)
invh4 = array([[16, -120, 240, -140],
[-120, 1200, -2700, 1680],
[240, -2700, 6480, -4200],
[-140, 1680, -4200, 2800]])
assert_array_equal(invhilbert(4, exact=True), invh4)
assert_array_almost_equal(invhilbert(4), invh4)
invh5 = array([[25, -300, 1050, -1400, 630],
[-300, 4800, -18900, 26880, -12600],
[1050, -18900, 79380, -117600, 56700],
[-1400, 26880, -117600, 179200, -88200],
[630, -12600, 56700, -88200, 44100]])
assert_array_equal(invhilbert(5, exact=True), invh5)
assert_array_almost_equal(invhilbert(5), invh5)
invh17 = array([
[289, -41616, 1976760, -46124400, 629598060, -5540462928,
33374693352, -143034400080, 446982500250, -1033026222800,
1774926873720, -2258997839280, 2099709530100, -1384423866000,
613101997800, -163493866080, 19835652870],
[-41616, 7990272, -426980160, 10627061760, -151103534400,
1367702848512, -8410422724704, 36616806420480, -115857864064800,
270465047424000, -468580694662080, 600545887119360,
-561522320049600, 372133135180800, -165537539406000,
44316454993920, -5395297580640],
[1976760, -426980160, 24337869120, -630981792000, 9228108708000,
-85267724461920, 532660105897920, -2348052711713280,
7504429831470000, -17664748409880000, 30818191841236800,
-39732544853164800, 37341234283298400, -24857330514030000,
11100752642520000, -2982128117299200, 364182586693200],
[-46124400, 10627061760, -630981792000, 16826181120000,
-251209625940000, 2358021022156800, -14914482965141760,
66409571644416000, -214015221119700000, 507295338950400000,
-890303319857952000, 1153715376477081600, -1089119333262870000,
727848632044800000, -326170262829600000, 87894302404608000,
-10763618673376800],
[629598060, -151103534400, 9228108708000,
-251209625940000, 3810012660090000, -36210360321495360,
231343968720664800, -1038687206500944000, 3370739732635275000,
-8037460526495400000, 14178080368737885600, -18454939322943942000,
17489975175339030000, -11728977435138600000, 5272370630081100000,
-1424711708039692800, 174908803442373000],
[-5540462928, 1367702848512, -85267724461920, 2358021022156800,
-36210360321495360, 347619459086355456, -2239409617216035264,
10124803292907663360, -33052510749726468000,
79217210949138662400, -140362995650505067440,
183420385176741672960, -174433352415381259200,
117339159519533952000, -52892422160973595200,
14328529177999196160, -1763080738699119840],
[33374693352, -8410422724704, 532660105897920,
-14914482965141760, 231343968720664800, -2239409617216035264,
14527452132196331328, -66072377044391477760,
216799987176909536400, -521925895055522958000,
928414062734059661760, -1217424500995626443520,
1161358898976091015200, -783401860847777371200,
354015418167362952000, -96120549902411274240,
11851820521255194480],
[-143034400080, 36616806420480, -2348052711713280,
66409571644416000, -1038687206500944000, 10124803292907663360,
-66072377044391477760, 302045152202932469760,
-995510145200094810000, 2405996923185123840000,
-4294704507885446054400, 5649058909023744614400,
-5403874060541811254400, 3654352703663101440000,
-1655137020003255360000, 450325202737117593600,
-55630994283442749600],
[446982500250, -115857864064800, 7504429831470000,
-214015221119700000, 3370739732635275000, -33052510749726468000,
216799987176909536400, -995510145200094810000,
3293967392206196062500, -7988661659013106500000,
14303908928401362270000, -18866974090684772052000,
18093328327706957325000, -12263364009096700500000,
5565847995255512250000, -1517208935002984080000,
187754605706619279900],
[-1033026222800, 270465047424000, -17664748409880000,
507295338950400000, -8037460526495400000, 79217210949138662400,
-521925895055522958000, 2405996923185123840000,
-7988661659013106500000, 19434404971634224000000,
-34894474126569249192000, 46141453390504792320000,
-44349976506971935800000, 30121928988527376000000,
-13697025107665828500000, 3740200989399948902400,
-463591619028689580000],
[1774926873720, -468580694662080,
30818191841236800, -890303319857952000, 14178080368737885600,
-140362995650505067440, 928414062734059661760,
-4294704507885446054400, 14303908928401362270000,
-34894474126569249192000, 62810053427824648545600,
-83243376594051600326400, 80177044485212743068000,
-54558343880470209780000, 24851882355348879230400,
-6797096028813368678400, 843736746632215035600],
[-2258997839280, 600545887119360, -39732544853164800,
1153715376477081600, -18454939322943942000, 183420385176741672960,
-1217424500995626443520, 5649058909023744614400,
-18866974090684772052000, 46141453390504792320000,
-83243376594051600326400, 110552468520163390156800,
-106681852579497947388000, 72720410752415168870400,
-33177973900974346080000, 9087761081682520473600,
-1129631016152221783200],
[2099709530100, -561522320049600, 37341234283298400,
-1089119333262870000, 17489975175339030000,
-174433352415381259200, 1161358898976091015200,
-5403874060541811254400, 18093328327706957325000,
-44349976506971935800000, 80177044485212743068000,
-106681852579497947388000, 103125790826848015808400,
-70409051543137015800000, 32171029219823375700000,
-8824053728865840192000, 1098252376814660067000],
[-1384423866000, 372133135180800,
-24857330514030000, 727848632044800000, -11728977435138600000,
117339159519533952000, -783401860847777371200,
3654352703663101440000, -12263364009096700500000,
30121928988527376000000, -54558343880470209780000,
72720410752415168870400, -70409051543137015800000,
48142941226076592000000, -22027500987368499000000,
6049545098753157120000, -753830033789944188000],
[613101997800, -165537539406000,
11100752642520000, -326170262829600000, 5272370630081100000,
-52892422160973595200, 354015418167362952000,
-1655137020003255360000, 5565847995255512250000,
-13697025107665828500000, 24851882355348879230400,
-33177973900974346080000, 32171029219823375700000,
-22027500987368499000000, 10091416708498869000000,
-2774765838662800128000, 346146444087219270000],
[-163493866080, 44316454993920, -2982128117299200,
87894302404608000, -1424711708039692800,
14328529177999196160, -96120549902411274240,
450325202737117593600, -1517208935002984080000,
3740200989399948902400, -6797096028813368678400,
9087761081682520473600, -8824053728865840192000,
6049545098753157120000, -2774765838662800128000,
763806510427609497600, -95382575704033754400],
[19835652870, -5395297580640, 364182586693200, -10763618673376800,
174908803442373000, -1763080738699119840, 11851820521255194480,
-55630994283442749600, 187754605706619279900,
-463591619028689580000, 843736746632215035600,
-1129631016152221783200, 1098252376814660067000,
-753830033789944188000, 346146444087219270000,
-95382575704033754400, 11922821963004219300]
])
assert_array_equal(invhilbert(17, exact=True), invh17)
assert_allclose(invhilbert(17), invh17.astype(float), rtol=1e-12)
def test_inverse(self):
for n in range(1, 10):
a = hilbert(n)
b = invhilbert(n)
# The Hilbert matrix is increasingly badly conditioned,
# so take that into account in the test
c = cond(a)
assert_allclose(a.dot(b), eye(n), atol=1e-15*c, rtol=1e-15*c)
class TestPascal(object):
cases = [
(1, array([[1]]), array([[1]])),
(2, array([[1, 1],
[1, 2]]),
array([[1, 0],
[1, 1]])),
(3, array([[1, 1, 1],
[1, 2, 3],
[1, 3, 6]]),
array([[1, 0, 0],
[1, 1, 0],
[1, 2, 1]])),
(4, array([[1, 1, 1, 1],
[1, 2, 3, 4],
[1, 3, 6, 10],
[1, 4, 10, 20]]),
array([[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 2, 1, 0],
[1, 3, 3, 1]])),
]
def check_case(self, n, sym, low):
assert_array_equal(pascal(n), sym)
assert_array_equal(pascal(n, kind='lower'), low)
assert_array_equal(pascal(n, kind='upper'), low.T)
assert_array_almost_equal(pascal(n, exact=False), sym)
assert_array_almost_equal(pascal(n, exact=False, kind='lower'), low)
assert_array_almost_equal(pascal(n, exact=False, kind='upper'), low.T)
def test_cases(self):
for n, sym, low in self.cases:
self.check_case(n, sym, low)
def test_big(self):
p = pascal(50)
assert_equal(p[-1, -1], comb(98, 49, exact=True))
def test_threshold(self):
# Regression test. An early version of `pascal` returned an
# array of type np.uint64 for n=35, but that data type is too small
# to hold p[-1, -1]. The second assert_equal below would fail
# because p[-1, -1] overflowed.
p = pascal(34)
assert_equal(2*p.item(-1, -2), p.item(-1, -1), err_msg="n = 34")
p = pascal(35)
assert_equal(2*p.item(-1, -2), p.item(-1, -1), err_msg="n = 35")
def test_invpascal():
def check_invpascal(n, kind, exact):
ip = invpascal(n, kind=kind, exact=exact)
p = pascal(n, kind=kind, exact=exact)
# Matrix-multiply ip and p, and check that we get the identity matrix.
# We can't use the simple expression e = ip.dot(p), because when
# n < 35 and exact is True, p.dtype is np.uint64 and ip.dtype is
# np.int64. The product of those dtypes is np.float64, which loses
# precision when n is greater than 18. Instead we'll cast both to
# object arrays, and then multiply.
e = ip.astype(object).dot(p.astype(object))
assert_array_equal(e, eye(n), err_msg="n=%d kind=%r exact=%r" %
(n, kind, exact))
kinds = ['symmetric', 'lower', 'upper']
ns = [1, 2, 5, 18]
for n in ns:
for kind in kinds:
for exact in [True, False]:
check_invpascal(n, kind, exact)
ns = [19, 34, 35, 50]
for n in ns:
for kind in kinds:
check_invpascal(n, kind, True)
def test_dft():
m = dft(2)
expected = array([[1.0, 1.0], [1.0, -1.0]])
assert_array_almost_equal(m, expected)
m = dft(2, scale='n')
assert_array_almost_equal(m, expected/2.0)
m = dft(2, scale='sqrtn')
assert_array_almost_equal(m, expected/sqrt(2.0))
x = array([0, 1, 2, 3, 4, 5, 0, 1])
m = dft(8)
mx = m.dot(x)
fx = fft(x)
assert_array_almost_equal(mx, fx)
def test_fiedler():
f = fiedler([])
assert_equal(f.size, 0)
f = fiedler([123.])
assert_array_equal(f, np.array([[0.]]))
f = fiedler(np.arange(1, 7))
des = np.array([[0, 1, 2, 3, 4, 5],
[1, 0, 1, 2, 3, 4],
[2, 1, 0, 1, 2, 3],
[3, 2, 1, 0, 1, 2],
[4, 3, 2, 1, 0, 1],
[5, 4, 3, 2, 1, 0]])
assert_array_equal(f, des)
def test_fiedler_companion():
fc = fiedler_companion([])
assert_equal(fc.size, 0)
fc = fiedler_companion([1.])
assert_equal(fc.size, 0)
fc = fiedler_companion([1., 2.])
assert_array_equal(fc, np.array([[-2.]]))
fc = fiedler_companion([1e-12, 2., 3.])
assert_array_almost_equal(fc, companion([1e-12, 2., 3.]))
with assert_raises(ValueError):
fiedler_companion([0, 1, 2])
fc = fiedler_companion([1., -16., 86., -176., 105.])
assert_array_almost_equal(eigvals(fc),
np.array([7., 5., 3., 1.]))
class TestConvolutionMatrix:
"""
Test convolution_matrix vs. numpy.convolve for various parameters.
"""
def create_vector(self, n, cpx):
"""Make a complex or real test vector of length n."""
x = np.linspace(-2.5, 2.2, n)
if cpx:
x = x + 1j*np.linspace(-1.5, 3.1, n)
return x
def test_bad_n(self):
# n must be a positive integer
with pytest.raises(ValueError, match='n must be a positive integer'):
convolution_matrix([1, 2, 3], 0)
def test_bad_first_arg(self):
# first arg must be a 1d array, otherwise ValueError
with pytest.raises(ValueError, match='one-dimensional'):
convolution_matrix(1, 4)
def test_empty_first_arg(self):
# first arg must have at least one value
with pytest.raises(ValueError, match=r'len\(a\)'):
convolution_matrix([], 4)
def test_bad_mode(self):
# mode must be in ('full', 'valid', 'same')
with pytest.raises(ValueError, match='mode.*must be one of'):
convolution_matrix((1, 1), 4, mode='invalid argument')
@pytest.mark.parametrize('cpx', [False, True])
@pytest.mark.parametrize('na', [1, 2, 9])
@pytest.mark.parametrize('nv', [1, 2, 9])
@pytest.mark.parametrize('mode', [None, 'full', 'valid', 'same'])
def test_against_numpy_convolve(self, cpx, na, nv, mode):
a = self.create_vector(na, cpx)
v = self.create_vector(nv, cpx)
if mode is None:
y1 = np.convolve(v, a)
A = convolution_matrix(a, nv)
else:
y1 = np.convolve(v, a, mode)
A = convolution_matrix(a, nv, mode)
y2 = A @ v
assert_array_almost_equal(y1, y2)