Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Remove nosetest and convert tests to pytest #47

Merged
merged 9 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:

- name: Run Tests
run: |
pytest --cov-config=.coveragerc --cov-report=xml --cov=pymatsolver -s -v
make coverage

- name: Test Documentation
if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python-version == '3.11') }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.coverage
coverage.xml
*.pyc
*.so
build/
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ build:
python setup.py build_ext --inplace

coverage:
nosetests --logging-level=INFO --with-coverage --cover-package=pymatsolver --cover-html
open cover/index.html
pytest --cov-config=.coveragerc --cov-report=xml --cov=pymatsolver -s -v

lint:
pylint --output-format=html pymatsolver > pylint.html
Expand All @@ -14,7 +13,7 @@ graphs:
pyreverse -my -A -o pdf -p pymatsolver pymatsolver/**.py pymatsolver/**/**.py

tests:
nosetests --logging-level=INFO
pytest

docs:
cd docs;make html
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ From a clean install on Ubuntu:
./miniconda.sh -b
export PATH=/root/anaconda/bin:/root/miniconda/bin:$PATH
conda update --yes conda
conda install --yes numpy scipy matplotlib cython ipython nose
conda install --yes numpy scipy matplotlib cython ipython pytest coverage

git clone https://github.com/rowanc1/pymatsolver.git
cd pymatsolver
Expand Down
2 changes: 2 additions & 0 deletions pymatsolver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
from .direct import Solver
from .direct import SolverLU

from .solvers import PymatsolverAccuracyError

BicgJacobi = BiCGJacobi # backwards compatibility

try:
Expand Down
1 change: 1 addition & 0 deletions pymatsolver/direct/pardiso.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def factor(self, A=None):
if not self._factored:
self.solver.refactor(self.A)
self._factored = True

def _solveM(self, rhs):
self.factor()
sol = self.solver.solve(rhs)
Expand Down
8 changes: 6 additions & 2 deletions pymatsolver/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import warnings


class PymatsolverAccuracyError(Exception):
pass


class Base():
_accuracy_tol = 1e-6
_check_accuracy = False
Expand Down Expand Up @@ -57,7 +61,7 @@
def T(self):
"The transpose operator for this class"
if self._transposeClass is None:
raise Exception(
raise NotImplementedError(

Check warning on line 64 in pymatsolver/solvers.py

View check run for this annotation

Codecov / codecov/patch

pymatsolver/solvers.py#L64

Added line #L64 was not covered by tests
'The transpose for the {} class is not possible.'.format(
self.__name__
)
Expand All @@ -74,7 +78,7 @@
msg = 'Accuracy on solve is above tolerance: {0:e} > {1:e}'.format(
nrm, self.accuracy_tol
)
raise Exception(msg)
raise PymatsolverAccuracyError(msg)

def _solve(self, rhs):

Expand Down
33 changes: 14 additions & 19 deletions tests/test_Basic.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import unittest
import numpy as np
import numpy.testing as npt
import pytest
import scipy.sparse as sp
from pymatsolver import Diagonal

TOL = 1e-12


class TestBasic(unittest.TestCase):
def test_DiagonalSolver():

def test_DiagonalSolver(self):
A = sp.identity(5)*2.0
rhs = np.c_[np.arange(1, 6), np.arange(2, 11, 2)]
X = Diagonal(A) * rhs
x = Diagonal(A) * rhs[:, 0]

A = sp.identity(5)*2.0
rhs = np.c_[np.arange(1, 6), np.arange(2, 11, 2)]
X = Diagonal(A) * rhs
x = Diagonal(A) * rhs[:, 0]
sol = rhs/2.0

sol = rhs/2.0
with pytest.raises(TypeError):
Diagonal(A, check_accuracy=np.array([1, 2, 3]))
with pytest.raises(TypeError):
Diagonal(A, accuracy_tol=0)

with self.assertRaises(TypeError):
Diagonal(A, check_accuracy=np.array([1, 2, 3]))
with self.assertRaises(TypeError):
Diagonal(A, accuracy_tol=0)

self.assertLess(np.linalg.norm(sol-X, np.inf), TOL)
self.assertLess(np.linalg.norm(sol[:, 0]-x, np.inf), TOL)


if __name__ == '__main__':
unittest.main()
npt.assert_allclose(sol, X, atol=TOL)
npt.assert_allclose(sol[:, 0], x, atol=TOL)
120 changes: 33 additions & 87 deletions tests/test_BicgJacobi.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,36 @@
import unittest
from pymatsolver import BicgJacobi
import numpy as np
import numpy.testing as npt
import scipy.sparse as sp

TOL = 1e-6


class TestBicgJacobi(unittest.TestCase):

def setUp(self):

nSize = 100
A = sp.rand(nSize, nSize, 0.05, format='csr', random_state=100)
A = A + sp.spdiags(np.ones(nSize), 0, nSize, nSize)
A = A.T*A
A = A.tocsr()
np.random.seed(1)
sol = np.random.rand(nSize, 4)
rhs = A.dot(sol)

self.A = A
self.rhs = rhs
self.sol = sol

def test(self):
rhs = self.rhs
Ainv = BicgJacobi(self.A, symmetric=True)
solb = Ainv*rhs
for i in range(3):
err = np.linalg.norm(
self.A*solb[:, i] - rhs[:, i]) / np.linalg.norm(rhs[:, i])
self.assertLess(err, TOL)
Ainv.clean()

def test_T(self):
rhs = self.rhs
Ainv = BicgJacobi(self.A, symmetric=True)
Ainv.maxIter = 2000
AinvT = Ainv.T
solb = AinvT*rhs
for i in range(3):
err = np.linalg.norm(
self.A.T*solb[:, i] - rhs[:, i]) / np.linalg.norm(rhs[:, i])
self.assertLess(err, TOL)
Ainv.clean()


class TestBicgJacobiComplex(unittest.TestCase):

def setUp(self):
nSize = 100
A = sp.rand(nSize, nSize, 0.05, format='csr', random_state=100)
A.data = A.data + 1j*np.random.rand(A.nnz)
A = A.T.dot(A) + sp.spdiags(np.ones(nSize), 0, nSize, nSize)
A = A.tocsr()

np.random.seed(1)
sol = np.random.rand(nSize, 5) + 1j*np.random.rand(nSize, 5)
rhs = A.dot(sol)

self.A = A
self.rhs = rhs
self.sol = sol

def test(self):
rhs = self.rhs
Ainv = BicgJacobi(self.A, symmetric=True)
solb = Ainv*rhs
for i in range(3):
err = np.linalg.norm(
self.A*solb[:, i] - rhs[:, i]) / np.linalg.norm(rhs[:, i])
self.assertLess(err, TOL)
Ainv.clean()

def test_T(self):
rhs = self.rhs
Ainv = BicgJacobi(self.A, symmetric=True)
Ainv.maxIter = 2000
AinvT = Ainv.T
solb = AinvT*rhs
for i in range(3):
err = np.linalg.norm(
self.A.T*solb[:, i] - rhs[:, i]) / np.linalg.norm(rhs[:, i])
self.assertLess(err, TOL)
Ainv.clean()


if __name__ == '__main__':
unittest.main()
import pytest

TOL = 1e-5

@pytest.fixture()
def test_mat_data():
nSize = 100
A = sp.rand(nSize, nSize, 0.05, format='csr', random_state=100)
A = A + sp.spdiags(np.ones(nSize), 0, nSize, nSize)
A = A.T*A
A = A.tocsr()
np.random.seed(1)
sol = np.random.rand(nSize, 4)
rhs = A.dot(sol)
return A, sol, rhs


@pytest.mark.parametrize('transpose', [True, False])
@pytest.mark.parametrize('dtype', [np.float64, np.complex128])
def test_solve(test_mat_data, dtype, transpose):
A, rhs, sol = test_mat_data
A = A.astype(dtype)
rhs = rhs.astype(dtype)
sol = sol.astype(dtype)
if transpose:
A = A.T
Ainv = BicgJacobi(A, symmetric=True).T
else:
Ainv = BicgJacobi(A, symmetric=True)
Ainv.maxiter = 2000
solb = Ainv * rhs
npt.assert_allclose(rhs, A @ solb, atol=TOL)
132 changes: 55 additions & 77 deletions tests/test_Mumps.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,61 @@
import unittest
import numpy as np
import scipy.sparse as sp
import pymatsolver
import pytest
import numpy.testing as npt

TOL = 1e-11

if pymatsolver.AvailableSolvers['Mumps']:
class TestMumps(unittest.TestCase):

def setUp(self):
n = 5
irn = np.r_[0, 1, 3, 4, 1, 0, 4, 2, 1, 2, 0, 2]
jcn = np.r_[1, 2, 2, 4, 0, 0, 1, 3, 4, 1, 2, 2]
a = np.r_[
3.0, -3.0, 2.0, 1.0, 3.0, 2.0,
4.0, 2.0, 6.0, -1.0, 4.0, 1.0
]
rhs = np.r_[20.0, 24.0, 9.0, 6.0, 13.0]
rhs = np.c_[rhs, 10*rhs, 100*rhs]
sol = np.r_[1., 2., 3., 4., 5.]
sol = np.c_[sol, 10*sol, 100*sol]
A = sp.coo_matrix((a, (irn, jcn)), shape=(n, n))
self.A = A
self.rhs = rhs
self.sol = sol

def test_1to5(self):
rhs = self.rhs
sol = self.sol
Ainv = pymatsolver.Mumps(self.A)
for i in range(3):
self.assertLess(
np.linalg.norm(Ainv * rhs[:, i] - sol[:, i]), TOL
)
self.assertLess(np.linalg.norm(Ainv * rhs - sol, np.inf), TOL)

def test_1to5_cmplx(self):
rhs = self.rhs.astype(complex)
sol = self.sol.astype(complex)
self.A = self.A.astype(complex)
Ainv = pymatsolver.Mumps(self.A)
for i in range(3):
self.assertLess(
np.linalg.norm(Ainv * rhs[:, i] - sol[:, i]), TOL
)
self.assertLess(np.linalg.norm(Ainv * rhs - sol, np.inf), TOL)
if not pymatsolver.AvailableSolvers['Mumps']:
pytest.skip(reason="MUMPS solver is not installed", allow_module_level=True)

def test_1to5_T(self):
rhs = self.rhs
sol = self.sol
Ainv = pymatsolver.Mumps(self.A)
AinvT = Ainv.T
for i in range(3):
self.assertLess(
np.linalg.norm(AinvT.T * rhs[:, i] - sol[:, i]), TOL
)
self.assertLess(np.linalg.norm(AinvT.T * rhs - sol, np.inf), TOL)

# def test_singular(self):
# A = sp.identity(5).tocsr()
# A[-1, -1] = 0
# self.assertRaises(Exception, pymatsolver.Mumps, A)

def test_multiFactorsInMem(self):
n = 100
A = sp.rand(n, n, 0.7)+sp.identity(n)
x = np.ones((n, 10))
rhs = A * x
solvers = [pymatsolver.Mumps(A) for _ in range(20)]

for Ainv in solvers:
self.assertLess(
np.linalg.norm(Ainv * rhs - x)/np.linalg.norm(rhs), TOL)
Ainv.clean()

for Ainv in solvers:
self.assertLess(
np.linalg.norm(Ainv * rhs - x)/np.linalg.norm(rhs), TOL
)
TOL = 1e-11

if __name__ == '__main__':
unittest.main()
@pytest.fixture()
def test_mat_data():
n = 5
irn = np.r_[0, 1, 3, 4, 1, 0, 4, 2, 1, 2, 0, 2]
jcn = np.r_[1, 2, 2, 4, 0, 0, 1, 3, 4, 1, 2, 2]
a = np.r_[
3.0, -3.0, 2.0, 1.0, 3.0, 2.0,
4.0, 2.0, 6.0, -1.0, 4.0, 1.0
]
rhs = np.r_[20.0, 24.0, 9.0, 6.0, 13.0]
rhs = np.c_[rhs, 10 * rhs, 100 * rhs]
sol = np.r_[1., 2., 3., 4., 5.]
sol = np.c_[sol, 10 * sol, 100 * sol]
A = sp.coo_matrix((a, (irn, jcn)), shape=(n, n))
return A, rhs, sol

@pytest.mark.parametrize('transpose', [True, False])
@pytest.mark.parametrize('dtype', [np.float64, np.complex128])
def test_solve(test_mat_data, dtype, transpose):
A, rhs, sol = test_mat_data
sol = sol.astype(dtype)
rhs = rhs.astype(dtype)
A = A.astype(dtype)
if transpose:
Ainv = pymatsolver.Mumps(A.T).T
else:
Ainv = pymatsolver.Mumps(A)
for i in range(3):
npt.assert_allclose(Ainv * rhs[:, i], sol[:, i], atol=TOL)
npt.assert_allclose(Ainv * rhs, sol, atol=TOL)


# def test_singular(self):
# A = sp.identity(5).tocsr()
# A[-1, -1] = 0
# self.assertRaises(Exception, pymatsolver.Mumps, A)

def test_multiFactorsInMem():
n = 100
A = sp.rand(n, n, 0.7)+sp.identity(n)
x = np.ones((n, 10))
rhs = A * x
solvers = [pymatsolver.Mumps(A) for _ in range(20)]

for Ainv in solvers:
npt.assert_allclose(Ainv * rhs, x, rtol=TOL)
Ainv.clean()

for Ainv in solvers:
npt.assert_allclose(Ainv * rhs, x, rtol=TOL)
Loading
Loading