123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- """
- Copyright (C) 2010 David Fong and Michael Saunders
- Distributed under the same license as SciPy
- Testing Code for LSMR.
- 03 Jun 2010: First version release with lsmr.py
- David Chin-lung Fong clfong@stanford.edu
- Institute for Computational and Mathematical Engineering
- Stanford University
- Michael Saunders saunders@stanford.edu
- Systems Optimization Laboratory
- Dept of MS&E, Stanford University.
- """
- from numpy import array, arange, eye, zeros, ones, sqrt, transpose, hstack
- from numpy.linalg import norm
- from numpy.testing import assert_allclose
- import pytest
- from scipy.sparse import coo_matrix
- from scipy.sparse.linalg._interface import aslinearoperator
- from scipy.sparse.linalg import lsmr
- from .test_lsqr import G, b
- class TestLSMR:
- def setup_method(self):
- self.n = 10
- self.m = 10
- def assertCompatibleSystem(self, A, xtrue):
- Afun = aslinearoperator(A)
- b = Afun.matvec(xtrue)
- x = lsmr(A, b)[0]
- assert norm(x - xtrue) == pytest.approx(0, abs=1e-5)
- def testIdentityACase1(self):
- A = eye(self.n)
- xtrue = zeros((self.n, 1))
- self.assertCompatibleSystem(A, xtrue)
- def testIdentityACase2(self):
- A = eye(self.n)
- xtrue = ones((self.n,1))
- self.assertCompatibleSystem(A, xtrue)
- def testIdentityACase3(self):
- A = eye(self.n)
- xtrue = transpose(arange(self.n,0,-1))
- self.assertCompatibleSystem(A, xtrue)
- def testBidiagonalA(self):
- A = lowerBidiagonalMatrix(20,self.n)
- xtrue = transpose(arange(self.n,0,-1))
- self.assertCompatibleSystem(A,xtrue)
- def testScalarB(self):
- A = array([[1.0, 2.0]])
- b = 3.0
- x = lsmr(A, b)[0]
- assert norm(A.dot(x) - b) == pytest.approx(0)
- def testComplexX(self):
- A = eye(self.n)
- xtrue = transpose(arange(self.n, 0, -1) * (1 + 1j))
- self.assertCompatibleSystem(A, xtrue)
- def testComplexX0(self):
- A = 4 * eye(self.n) + ones((self.n, self.n))
- xtrue = transpose(arange(self.n, 0, -1))
- b = aslinearoperator(A).matvec(xtrue)
- x0 = zeros(self.n, dtype=complex)
- x = lsmr(A, b, x0=x0)[0]
- assert norm(x - xtrue) == pytest.approx(0, abs=1e-5)
- def testComplexA(self):
- A = 4 * eye(self.n) + 1j * ones((self.n, self.n))
- xtrue = transpose(arange(self.n, 0, -1).astype(complex))
- self.assertCompatibleSystem(A, xtrue)
- def testComplexB(self):
- A = 4 * eye(self.n) + ones((self.n, self.n))
- xtrue = transpose(arange(self.n, 0, -1) * (1 + 1j))
- b = aslinearoperator(A).matvec(xtrue)
- x = lsmr(A, b)[0]
- assert norm(x - xtrue) == pytest.approx(0, abs=1e-5)
- def testColumnB(self):
- A = eye(self.n)
- b = ones((self.n, 1))
- x = lsmr(A, b)[0]
- assert norm(A.dot(x) - b.ravel()) == pytest.approx(0)
- def testInitialization(self):
- # Test that the default setting is not modified
- x_ref, _, itn_ref, normr_ref, *_ = lsmr(G, b)
- assert_allclose(norm(b - G@x_ref), normr_ref, atol=1e-6)
- # Test passing zeros yields similiar result
- x0 = zeros(b.shape)
- x = lsmr(G, b, x0=x0)[0]
- assert_allclose(x, x_ref)
- # Test warm-start with single iteration
- x0 = lsmr(G, b, maxiter=1)[0]
- x, _, itn, normr, *_ = lsmr(G, b, x0=x0)
- assert_allclose(norm(b - G@x), normr, atol=1e-6)
- # NOTE(gh-12139): This doesn't always converge to the same value as
- # ref because error estimates will be slightly different when calculated
- # from zeros vs x0 as a result only compare norm and itn (not x).
- # x generally converges 1 iteration faster because it started at x0.
- # itn == itn_ref means that lsmr(x0) took an extra iteration see above.
- # -1 is technically possible but is rare (1 in 100000) so it's more
- # likely to be an error elsewhere.
- assert itn - itn_ref in (0, 1)
- # If an extra iteration is performed normr may be 0, while normr_ref
- # may be much larger.
- assert normr < normr_ref * (1 + 1e-6)
- class TestLSMRReturns:
- def setup_method(self):
- self.n = 10
- self.A = lowerBidiagonalMatrix(20, self.n)
- self.xtrue = transpose(arange(self.n, 0, -1))
- self.Afun = aslinearoperator(self.A)
- self.b = self.Afun.matvec(self.xtrue)
- self.x0 = ones(self.n)
- self.x00 = self.x0.copy()
- self.returnValues = lsmr(self.A, self.b)
- self.returnValuesX0 = lsmr(self.A, self.b, x0=self.x0)
- def test_unchanged_x0(self):
- x, istop, itn, normr, normar, normA, condA, normx = self.returnValuesX0
- assert_allclose(self.x00, self.x0)
- def testNormr(self):
- x, istop, itn, normr, normar, normA, condA, normx = self.returnValues
- assert norm(self.b - self.Afun.matvec(x)) == pytest.approx(normr)
- def testNormar(self):
- x, istop, itn, normr, normar, normA, condA, normx = self.returnValues
- assert (norm(self.Afun.rmatvec(self.b - self.Afun.matvec(x)))
- == pytest.approx(normar))
- def testNormx(self):
- x, istop, itn, normr, normar, normA, condA, normx = self.returnValues
- assert norm(x) == pytest.approx(normx)
- def lowerBidiagonalMatrix(m, n):
- # This is a simple example for testing LSMR.
- # It uses the leading m*n submatrix from
- # A = [ 1
- # 1 2
- # 2 3
- # 3 4
- # ...
- # n ]
- # suitably padded by zeros.
- #
- # 04 Jun 2010: First version for distribution with lsmr.py
- if m <= n:
- row = hstack((arange(m, dtype=int),
- arange(1, m, dtype=int)))
- col = hstack((arange(m, dtype=int),
- arange(m-1, dtype=int)))
- data = hstack((arange(1, m+1, dtype=float),
- arange(1,m, dtype=float)))
- return coo_matrix((data, (row, col)), shape=(m,n))
- else:
- row = hstack((arange(n, dtype=int),
- arange(1, n+1, dtype=int)))
- col = hstack((arange(n, dtype=int),
- arange(n, dtype=int)))
- data = hstack((arange(1, n+1, dtype=float),
- arange(1,n+1, dtype=float)))
- return coo_matrix((data,(row, col)), shape=(m,n))
- def lsmrtest(m, n, damp):
- """Verbose testing of lsmr"""
- A = lowerBidiagonalMatrix(m,n)
- xtrue = arange(n,0,-1, dtype=float)
- Afun = aslinearoperator(A)
- b = Afun.matvec(xtrue)
- atol = 1.0e-7
- btol = 1.0e-7
- conlim = 1.0e+10
- itnlim = 10*n
- show = 1
- x, istop, itn, normr, normar, norma, conda, normx \
- = lsmr(A, b, damp, atol, btol, conlim, itnlim, show)
- j1 = min(n,5)
- j2 = max(n-4,1)
- print(' ')
- print('First elements of x:')
- str = ['%10.4f' % (xi) for xi in x[0:j1]]
- print(''.join(str))
- print(' ')
- print('Last elements of x:')
- str = ['%10.4f' % (xi) for xi in x[j2-1:]]
- print(''.join(str))
- r = b - Afun.matvec(x)
- r2 = sqrt(norm(r)**2 + (damp*norm(x))**2)
- print(' ')
- str = 'normr (est.) %17.10e' % (normr)
- str2 = 'normr (true) %17.10e' % (r2)
- print(str)
- print(str2)
- print(' ')
- if __name__ == "__main__":
- lsmrtest(20,10,0)
|