diff --git a/pymatsolver/__init__.py b/pymatsolver/__init__.py index 1d6e937..1110d13 100644 --- a/pymatsolver/__init__.py +++ b/pymatsolver/__init__.py @@ -79,8 +79,24 @@ "Mumps": _mumps_available, } -BicgJacobi = BiCGJacobi # backwards compatibility -PardisoSolver = Pardiso # backwards compatibility +_DEPRECATED_ALIASES = { + "BicgJacobi": ("BiCGJacobi", BiCGJacobi), + "PardisoSolver": ("Pardiso", Pardiso), +} + + +def __getattr__(name): + if name in _DEPRECATED_ALIASES: + new_name, obj = _DEPRECATED_ALIASES[name] + import warnings + warnings.warn( + f"pymatsolver.{name} is deprecated and will be removed in v0.6.0, " + f"use pymatsolver.{new_name} instead.", + FutureWarning, + stacklevel=2, + ) + return obj + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") if not AvailableSolvers["Pardiso"]: SolverHelp['Pardiso'] = """Pardiso is not working diff --git a/pymatsolver/iterative.py b/pymatsolver/iterative.py index 2c79e2e..8199940 100644 --- a/pymatsolver/iterative.py +++ b/pymatsolver/iterative.py @@ -7,9 +7,9 @@ from .wrappers import WrapIterative from .solvers import Base -# The tol kwarg was removed from bicgstab in scipy 1.14.0. +# The rtol kwarg was added to bicgstab in scipy 1.12.0 (deprecating tol), and tol was removed in 1.14.0. # See https://docs.scipy.org/doc/scipy-1.12.0/reference/generated/scipy.sparse.linalg.bicgstab.html -RTOL_ARG_NAME = "rtol" if Version(scipy.__version__) >= Version("1.14.0") else "tol" +RTOL_ARG_NAME = "rtol" if Version(scipy.__version__) >= Version("1.12.0") else "tol" SolverCG = WrapIterative(cg, name="SolverCG") SolverBiCG = WrapIterative(bicgstab, name="SolverBiCG") @@ -38,8 +38,6 @@ class BiCGJacobi(Base): """ def __init__(self, A, maxiter=1000, rtol=1E-6, atol=0.0, check_accuracy=False, check_rtol=1e-6, check_atol=0, **kwargs): - if "symmetric" in kwargs: - raise TypeError("The symmetric keyword argument was been removed in pymatsolver 0.4.0.") super().__init__(A, check_accuracy=check_accuracy, check_rtol=check_rtol, check_atol=check_atol, **kwargs) self._factored = False self.maxiter = maxiter diff --git a/pymatsolver/solvers.py b/pymatsolver/solvers.py index a671a08..bc5b5f7 100644 --- a/pymatsolver/solvers.py +++ b/pymatsolver/solvers.py @@ -40,7 +40,6 @@ class Base(ABC): Extra keyword arguments. If there are any left here a warning will be raised. """ - __numpy_ufunc__ = True __array_ufunc__ = None _is_conjugate = False @@ -57,9 +56,6 @@ def __init__( self._A = A self._dtype = np.dtype(A.dtype) - if 'accuracy_tol' in kwargs: - raise TypeError("'accuracy_tol' was removed in v0.4.0, use 'check_rtol' and 'check_atol'.") - self.check_accuracy = check_accuracy self.check_rtol = check_rtol self.check_atol = check_atol diff --git a/tests/test_Basic.py b/tests/test_Basic.py index cb7210d..34d7b2f 100644 --- a/tests/test_Basic.py +++ b/tests/test_Basic.py @@ -88,9 +88,6 @@ def test_errors_and_warnings(): with pytest.raises(ValueError, match="A is not a square matrix."): IdentitySolver(np.full((3, 5), 1)) - with pytest.raises(TypeError, match=r"'accuracy_tol' was removed.*"): - IdentitySolver(np.full((4, 4), 1), accuracy_tol=0.41) - with pytest.warns(UnusedArgumentWarning, match="Unused keyword arguments.*"): IdentitySolver(np.full((4, 4), 1), not_an_argument=4) @@ -228,3 +225,10 @@ def test_diagonal_inferance(): assert Ainv.is_symmetric assert not Ainv.is_hermitian assert not Ainv.is_positive_definite + + +@pytest.mark.parametrize("old_name, new_name", [("BicgJacobi", "BiCGJacobi"), ("PardisoSolver", "Pardiso")]) +def test_deprecated_aliases(old_name, new_name): + with pytest.warns(FutureWarning, match=f"pymatsolver.{old_name} is deprecated"): + obj = getattr(pymatsolver, old_name) + assert obj is getattr(pymatsolver, new_name) diff --git a/tests/test_BicgJacobi.py b/tests/test_BicgJacobi.py index a4bc265..07b0b62 100644 --- a/tests/test_BicgJacobi.py +++ b/tests/test_BicgJacobi.py @@ -1,4 +1,4 @@ -from pymatsolver import BicgJacobi +from pymatsolver import BiCGJacobi import numpy as np import numpy.testing as npt import scipy.sparse as sp @@ -30,29 +30,26 @@ def test_solve(test_mat_data, dtype, transpose, symmetric): A = D @ A rhs = A @ sol if transpose: - Ainv = BicgJacobi(A.T, is_symmetric=symmetric).T + Ainv = BiCGJacobi(A.T, is_symmetric=symmetric).T else: - Ainv = BicgJacobi(A, is_symmetric=symmetric) + Ainv = BiCGJacobi(A, is_symmetric=symmetric) Ainv.maxiter = 2000 solb = Ainv * rhs npt.assert_allclose(rhs, A @ solb, rtol=RTOL) def test_errors_and_warnings(test_mat_data): A, sol = test_mat_data - with pytest.raises(TypeError, match="The symmetric keyword.*"): - Ainv = BicgJacobi(A, symmetric=True) - with pytest.raises(ValueError): - Ainv = BicgJacobi(A, rtol=0.0) + Ainv = BiCGJacobi(A, rtol=0.0) with pytest.raises(ValueError): - Ainv = BicgJacobi(A, atol=-1.0) + Ainv = BiCGJacobi(A, atol=-1.0) def test_shallow_copy(test_mat_data): A, sol = test_mat_data - Ainv = BicgJacobi(A, maxiter=100, rtol=1.0E-3, atol=1.0E-16) + Ainv = BiCGJacobi(A, maxiter=100, rtol=1.0E-3, atol=1.0E-16) attrs = Ainv.get_attributes() - new_Ainv = BicgJacobi(A, **attrs) + new_Ainv = BiCGJacobi(A, **attrs) assert attrs == new_Ainv.get_attributes() \ No newline at end of file diff --git a/tests/test_Pardiso.py b/tests/test_Pardiso.py index 7c12e7f..b54be80 100644 --- a/tests/test_Pardiso.py +++ b/tests/test_Pardiso.py @@ -142,6 +142,11 @@ def test_pardiso_fdem(): A = sp.csr_matrix((data, indices, indptr), shape=(13872, 13872)) rhs = np.load(os.path.join(base_path, 'RHS.npy')) + # The rhs has many exact zeros, which would compare round-off against zero in the + # element-wise check below. Offset every entry by a small fraction of its column's largest + # magnitude, along the entry's own phase (angle(0) == 0), so no entry is shifted towards zero. + offset = 1e-3 * np.abs(rhs).max(axis=0) + rhs = rhs + offset * np.exp(1j * np.angle(rhs)) Ainv = pymatsolver.Pardiso(A, check_accuracy=True) print(Ainv.is_symmetric)