From df21bbbd8981a836729de70694542d3e694851c3 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 14:43:10 -0600 Subject: [PATCH 01/17] Switch to pyproject.toml based install --- .bumpversion.cfg | 3 -- .coveragerc | 9 ---- .gitignore | 1 + Makefile | 20 +-------- pymatsolver/__init__.py | 17 +++++++- pyproject.toml | 97 +++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 - requirements_docs.txt | 7 --- setup.py | 48 -------------------- 9 files changed, 115 insertions(+), 88 deletions(-) delete mode 100644 .bumpversion.cfg delete mode 100644 .coveragerc create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 requirements_docs.txt delete mode 100644 setup.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 6453a5b..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[bumpversion] -current_version = 0.2.0 -files = setup.py pymatsolver/__init__.py docs/conf.py diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 251a420..0000000 --- a/.coveragerc +++ /dev/null @@ -1,9 +0,0 @@ -[run] -source = pymatsolver -omit = - */python?.?/* - */lib-python/?.?/*.py - */lib_pypy/_*.py - */site-packages/ordereddict.py - */site-packages/nose/* - */unittest2/* diff --git a/.gitignore b/.gitignore index 2d81f6b..93d17ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .coverage coverage.xml +coverage_html_report/ *.pyc *.so build/ diff --git a/Makefile b/Makefile index 88a3dd5..ab8c376 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,10 @@ .PHONY: build coverage lint graphs tests docs mumps mumps_mac mumps_install_mac -build: - python setup.py build_ext --inplace - coverage: - pytest --cov-config=.coveragerc --cov-report=xml --cov=pymatsolver -s -v - -lint: - pylint --output-format=html pymatsolver > pylint.html - -graphs: - pyreverse -my -A -o pdf -p pymatsolver pymatsolver/**.py pymatsolver/**/**.py + pytest --cov-config=pyproject.toml -s -v tests: pytest docs: cd docs;make html - -mumps: - cd pymatsolver/mumps;make build - -mumps_mac: - cd pymatsolver/mumps;make build_mac - -mumps_install_mac: - brew install mumps --with-scotch5 --without-mpi diff --git a/pymatsolver/__init__.py b/pymatsolver/__init__.py index 70b6d60..94e50a5 100644 --- a/pymatsolver/__init__.py +++ b/pymatsolver/__init__.py @@ -99,7 +99,22 @@ to be installed through conda. """ -__version__ = '0.2.0' __author__ = 'SimPEG Team' __license__ = 'MIT' __copyright__ = '2013 - 2024, SimPEG Team, https://simpeg.xyz' + +from importlib.metadata import version, PackageNotFoundError + +# Version +try: + # - Released versions just tags: 0.8.0 + # - GitHub commits add .dev#+hash: 0.8.1.dev4+g2785721 + # - Uncommitted changes add timestamp: 0.8.1.dev4+g2785721.d20191022 + __version__ = version("pymatsolver") +except PackageNotFoundError: + # If it was not installed, then we don't know the version. We could throw a + # warning here, but this case *should* be rare. discretize should be + # installed properly! + from datetime import datetime + + __version__ = "unknown-" + datetime.today().strftime("%Y%m%d") \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..48f18f1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,97 @@ +[build-system] +requires = ["setuptools>=64", "setuptools_scm>=8"] +build-backend = "setuptools.build_meta" + +[project] +name = 'pymatsolver' +description = "pymatsolver: Matrix Solvers for Python" +readme = 'README.rst' +requires-python = '>=3.10' +authors = [ + {name = 'SimPEG developers', email = 'rowanc1@gmail.com'}, +] +keywords = [ + 'matrix solver', +] +dependencies = [ + "numpy>=1.21", + "scipy>=1.8", +] +classifiers = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Mathematics', + 'Topic :: Scientific/Engineering :: Physics', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Operating System :: MacOS', + 'Natural Language :: English', +] +dynamic = ["version"] + +[project.license] +file = 'LICENSE' + +[project.urls] +Homepage = 'https://simpeg.xyz' +Documentation = 'https://simpeg.xyz/pymatsolver/' +Repository = 'https://github.com/simpeg/pymatsolver' + +[project.optional-dependencies] +pardiso = ["pydiso"] +mumps = ["python-mumps"] +docs = [ + "sphinx", + "numpydoc", + "pydata-sphinx-theme" +] + +tests = [ + "pytest", + "pytest-cov", +] + +build = [ + "setuptools_scm>=8", + "setuptools>=64", +] + +[tool.setuptools.packages.find] +include = ["pymatsolver*"] + +[tool.setuptools_scm] + +[tool.coverage.run] +branch = true +source = ["pymatsolver", "tests"] + +[tool.coverage.report] +ignore_errors = false +show_missing = true +# Regexes for lines to exclude from consideration +exclude_also = [ + # Don't complain about missing debug-only code: + "def __repr__", + "if self\\.debug", + + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + "AbstractMethodError", + + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", + + # Don't complain about abstract methods, they aren't run: + "@(abc\\.)?abstractmethod", +] + +[tool.coverage.html] +directory = "coverage_html_report" + diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d6e1198..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ --e . diff --git a/requirements_docs.txt b/requirements_docs.txt deleted file mode 100644 index 80baa8d..0000000 --- a/requirements_docs.txt +++ /dev/null @@ -1,7 +0,0 @@ -sphinx -numpydoc - -# Restrict pydata-sphinx-theme on older versions of Python on Windows. -# Otherwise we get doc build failures. -pydata-sphinx-theme~=0.14.4,<0.15 ; python_version <= '3.9' and platform_system == "Windows" -pydata-sphinx-theme diff --git a/setup.py b/setup.py deleted file mode 100644 index de98ca1..0000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -"""pymatsolver: Matrix Solvers for Python - -pymatsolver is a python package for easy to use matrix solvers. - -""" - -from setuptools import setup, find_packages - -CLASSIFIERS = [ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Mathematics', - 'Topic :: Scientific/Engineering :: Physics', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS', - 'Natural Language :: English', -] - -with open("README.rst") as f: - LONG_DESCRIPTION = ''.join(f.readlines()) - -setup( - name="pymatsolver", - version="0.2.0", - packages=find_packages(exclude=["tests"]), - install_requires=[ - 'numpy>=1.7', - 'scipy>=1.8', - ], - author="SimPEG Developers", - author_email="rowanc1@gmail.com", - description="pymatsolver: Matrix Solvers for Python", - long_description=LONG_DESCRIPTION, - license="MIT", - keywords="matrix solver", - url="http://simpeg.xyz/", - download_url="http://github.com/simpeg/pymatsolver", - classifiers=CLASSIFIERS, - platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], - use_2to3=False -) From 1146362c2bbe1285f379af158d7dd006736f3b9f Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 14:46:15 -0600 Subject: [PATCH 02/17] Don't complain about package not found errors. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 48f18f1..53aec39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ exclude_also = [ # Don't complain if non-runnable code isn't run: "if 0:", "if __name__ == .__main__.:", + "except PackageNotFoundError:", # Don't complain about abstract methods, they aren't run: "@(abc\\.)?abstractmethod", From de85567f958f5b1fbc94f7674f3e6252bf3dcd80 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 22:54:00 -0600 Subject: [PATCH 03/17] install doc info from pyproject file --- .github/workflows/python-package-conda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index daa4408..38fc38d 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -65,7 +65,7 @@ jobs: - name: Test Documentation if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python-version == '3.11') }} run: | - pip install -r requirements_docs.txt + pip install .[docs] cd docs make html cd .. From 9b209e06b604f08ec39fc24e07765ab4dec7104f Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:08:54 -0600 Subject: [PATCH 04/17] enable mumps and pardiso solvers to be defined even when not available. --- pymatsolver/__init__.py | 39 ++++++++++++++++------------------ pymatsolver/direct/__init__.py | 18 +++++----------- pymatsolver/direct/mumps.py | 11 +++++++++- pymatsolver/direct/pardiso.py | 10 +++++++-- tests/test_uninstalled.py | 13 ++++++++++++ 5 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 tests/test_uninstalled.py diff --git a/pymatsolver/__init__.py b/pymatsolver/__init__.py index 94e50a5..6dd5dbc 100644 --- a/pymatsolver/__init__.py +++ b/pymatsolver/__init__.py @@ -49,17 +49,6 @@ Mumps """ -SolverHelp = {} -AvailableSolvers = { - "Diagonal": True, - "Solver": True, - "SolverLU": True, - "SolverCG": True, - "Triangle": True, - "Pardiso": False, - "Mumps": False -} - # Simple solvers from .solvers import Diagonal, Triangle, Forward, Backward from .wrappers import wrap_direct, WrapDirect @@ -71,28 +60,36 @@ from .iterative import BiCGJacobi # Scipy direct solvers -from .direct import Solver +from .direct import Solver, pardiso from .direct import SolverLU from .solvers import PymatsolverAccuracyError +from .direct import Pardiso, Mumps +from .direct.pardiso import _available as _pardiso_available +from .direct.mumps import _available as _mumps_available + +SolverHelp = {} +AvailableSolvers = { + "Diagonal": True, + "Solver": True, + "SolverLU": True, + "SolverCG": True, + "Triangle": True, + "Pardiso": _pardiso_available, + "Mumps": _mumps_available, +} BicgJacobi = BiCGJacobi # backwards compatibility +PardisoSolver = Pardiso # backwards compatibility -try: - from .direct import Pardiso - AvailableSolvers['Pardiso'] = True - PardisoSolver = Pardiso # backwards compatibility -except ImportError: +if not AvailableSolvers["Pardiso"]: SolverHelp['Pardiso'] = """Pardiso is not working Ensure that you have pydiso installed, which may also require Python to be installed through conda. """ -try: - from .direct import Mumps - AvailableSolvers['Mumps'] = True -except ImportError: +if not AvailableSolvers["Mumps"]: SolverHelp['Mumps'] = """Mumps is not working. Ensure that you have python-mumps installed, which may also require Python diff --git a/pymatsolver/direct/__init__.py b/pymatsolver/direct/__init__.py index 498b9a0..e245779 100644 --- a/pymatsolver/direct/__init__.py +++ b/pymatsolver/direct/__init__.py @@ -1,18 +1,10 @@ -from ..wrappers import WrapDirect from scipy.sparse.linalg import spsolve, splu +from ..wrappers import WrapDirect +from .pardiso import Pardiso +from .mumps import Mumps + Solver = WrapDirect(spsolve, factorize=False, name="Solver") SolverLU = WrapDirect(splu, factorize=True, name="SolverLU") -__all__ = ["Solver", "SolverLU"] -try: - from .pardiso import Pardiso - __all__ += ["Pardiso"] -except ImportError: - pass - -try: - from .mumps import Mumps - __all__ += ["Mumps"] -except ImportError: - pass +__all__ = ["Solver", "SolverLU", "Pardiso", "Mumps"] diff --git a/pymatsolver/direct/mumps.py b/pymatsolver/direct/mumps.py index baccc40..42036eb 100644 --- a/pymatsolver/direct/mumps.py +++ b/pymatsolver/direct/mumps.py @@ -1,5 +1,10 @@ from pymatsolver.solvers import Base -from mumps import Context +try: + from mumps import Context + _available = True +except ImportError: + Context = None + _available = False class Mumps(Base): """The MUMPS direct solver. @@ -33,6 +38,10 @@ class Mumps(Base): _transposed = False def __init__(self, A, ordering=None, is_symmetric=None, is_positive_definite=False, check_accuracy=False, check_rtol=1e-6, check_atol=0, accuracy_tol=None, **kwargs): + if not _available: + raise ImportError( + "The Mumps solver requires the python-mumps package to be installed." + ) is_hermitian = kwargs.pop('is_hermitian', False) super().__init__(A, is_symmetric=is_symmetric, is_positive_definite=is_positive_definite, is_hermitian=is_hermitian, check_accuracy=check_accuracy, check_rtol=check_rtol, check_atol=check_atol, accuracy_tol=accuracy_tol, **kwargs) if ordering is None: diff --git a/pymatsolver/direct/pardiso.py b/pymatsolver/direct/pardiso.py index 3405c37..f5a923f 100644 --- a/pymatsolver/direct/pardiso.py +++ b/pymatsolver/direct/pardiso.py @@ -1,6 +1,10 @@ from pymatsolver.solvers import Base -from pydiso.mkl_solver import MKLPardisoSolver -from pydiso.mkl_solver import set_mkl_pardiso_threads, get_mkl_pardiso_max_threads +try: + from pydiso.mkl_solver import MKLPardisoSolver + from pydiso.mkl_solver import set_mkl_pardiso_threads, get_mkl_pardiso_max_threads + _available = True +except ImportError: + _available = False class Pardiso(Base): """The Pardiso direct solver. @@ -39,6 +43,8 @@ class Pardiso(Base): _transposed = False def __init__(self, A, n_threads=None, is_symmetric=None, is_positive_definite=False, is_hermitian=None, check_accuracy=False, check_rtol=1e-6, check_atol=0, accuracy_tol=None, **kwargs): + if not _available: + raise ImportError("Pardiso solver requires the pydiso package to be installed.") super().__init__(A, is_symmetric=is_symmetric, is_positive_definite=is_positive_definite, is_hermitian=is_hermitian, check_accuracy=check_accuracy, check_rtol=check_rtol, check_atol=check_atol, accuracy_tol=accuracy_tol, **kwargs) self.solver = MKLPardisoSolver( self.A, diff --git a/tests/test_uninstalled.py b/tests/test_uninstalled.py new file mode 100644 index 0000000..e66195a --- /dev/null +++ b/tests/test_uninstalled.py @@ -0,0 +1,13 @@ +import pymatsolver +import pytest +import scipy.sparse as sp + +@pytest.mark.skipif(pymatsolver.AvailableSolvers["Mumps"], reason="Mumps is installed.") +def test_mumps_uninstalled(): + with pytest.raises(ImportError): + pymatsolver.Mumps(sp.eye(4)) + +@pytest.mark.skipif(pymatsolver.AvailableSolvers["Pardiso"], reason="Pardiso is installed.") +def test_pydiso_uninstalled(): + with pytest.raises(ImportError): + pymatsolver.Pardiso(sp.eye(4)) From be79a65c639b8fa902fb6f3f42e10dc80001e900 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:10:42 -0600 Subject: [PATCH 05/17] generate xml report --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ab8c376..6b9a5e3 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: build coverage lint graphs tests docs mumps mumps_mac mumps_install_mac coverage: - pytest --cov-config=pyproject.toml -s -v + pytest --cov-config=pyproject.toml --cov-report=xml -s -v tests: pytest From bf2aa781e4a0d37f41c4bcb567fdcc4e604e3e26 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:16:56 -0600 Subject: [PATCH 06/17] tell pytest to do coverage test --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6b9a5e3..bde9a3d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ .PHONY: build coverage lint graphs tests docs mumps mumps_mac mumps_install_mac coverage: - pytest --cov-config=pyproject.toml --cov-report=xml -s -v + pytest --cov --cov-config=pyproject.toml -s -v + coverage xml tests: pytest From acd080f3ed4d0ed5247c7558f991cf703960adde Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:18:09 -0600 Subject: [PATCH 07/17] makefile cleanup --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bde9a3d..5649c99 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build coverage lint graphs tests docs mumps mumps_mac mumps_install_mac +.PHONY: coverage tests docs coverage: pytest --cov --cov-config=pyproject.toml -s -v From 96905cd3e6b7e848d5cc0a3c183ad4b56fb3ec41 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:23:07 -0600 Subject: [PATCH 08/17] don't do branch coverage --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 53aec39..466828e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,6 @@ include = ["pymatsolver*"] [tool.setuptools_scm] [tool.coverage.run] -branch = true source = ["pymatsolver", "tests"] [tool.coverage.report] From fc9e3028df911a44d774a0669f13804c2bc571b9 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:55:49 -0600 Subject: [PATCH 09/17] use lambda function --- tests/test_Wrappers.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_Wrappers.py b/tests/test_Wrappers.py index c84ce83..8159755 100644 --- a/tests/test_Wrappers.py +++ b/tests/test_Wrappers.py @@ -63,14 +63,12 @@ def clean(self): assert Ainv.solver.A is None def test_iterative_deprecations(): - def iterative_solver(A, x): - return x with pytest.warns(FutureWarning, match="check_accuracy and accuracy_tol were unused.*"): - wrap_iterative(iterative_solver, check_accuracy=True) + wrap_iterative(lambda a, x: x, check_accuracy=True) with pytest.warns(FutureWarning, match="check_accuracy and accuracy_tol were unused.*"): - wrap_iterative(iterative_solver, accuracy_tol=1E-3) + wrap_iterative(lambda a, x: x, accuracy_tol=1E-3) def test_non_scipy_iterative(): def iterative_solver(A, x): From ae5f7a4aa327f23f06f9b56703a819fb59fc776a Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 10 Oct 2024 23:59:53 -0600 Subject: [PATCH 10/17] add configuration for setuptools_scm --- .git_archival.txt | 4 ++++ .gitattributes | 1 + MANIFEST.in | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 .git_archival.txt create mode 100644 .gitattributes create mode 100644 MANIFEST.in diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000..b1a286b --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ +ref-names: $Format:%D$ \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..82bf71c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.git_archival.txt export-subst \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..19a5b12 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +prune .github +prune docs +prune tests +exclude .gitignore MANIFEST.in .pre-commit-config.yaml +exclude .git_archival.txt .gitattributes Makefile \ No newline at end of file From 8b59cc502ce3a4f3493e1a4014fa9722cf2b6b1a Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 00:11:44 -0600 Subject: [PATCH 11/17] test source and doc build --- .github/workflows/python-package-conda.yml | 50 +++++++++++----------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 38fc38d..7360bf4 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -79,13 +79,15 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} distribute: - name: Distributing from 3.8 - needs: build-and-test - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + name: Distributing from ${{ env.PYTHON_VERSION }} + # needs: build-and-test + # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') runs-on: ubuntu-latest defaults: run: shell: bash -l {0} + env: + PYTHON_VERSION: 3.11 steps: - uses: actions/checkout@v4 @@ -93,24 +95,24 @@ jobs: uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true - activate-environment: dev - python-version: 3.8 + channels: conda-forge + python-version: ${{ env.PYTHON_VERSION }} - - name: Install Env + - name: Install Base Env run: | conda info conda list conda config --show - conda install --quiet --yes -c conda-forge pip numpy scipy pydiso - pip install -r requirements_docs.txt + conda install --quiet --yes pip numpy scipy - name: Install Our Package run: | - pip install -v -e . + pip install -v -e .[docs] - name: Generate Source Distribution run: | - python setup.py sdist + pip install build + python -m build --sdist . - name: Build Documentation run: | @@ -118,17 +120,17 @@ jobs: make html cd .. - - name: GitHub Pages - uses: crazy-max/ghaction-github-pages@v2.5.0 - with: - build_dir: docs/_build/html - jekyll: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: pypi-publish - uses: pypa/gh-action-pypi-publish@v1.4.2 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} - skip_existing: true +# - name: GitHub Pages +# uses: crazy-max/ghaction-github-pages@v2.5.0 +# with: +# build_dir: docs/_build/html +# jekyll: false +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# +# - name: pypi-publish +# uses: pypa/gh-action-pypi-publish@v1.4.2 +# with: +# user: __token__ +# password: ${{ secrets.PYPI_API_TOKEN }} +# skip_existing: true From 5d8d1e2918ae1852b0b5073e82b83c6cd36dab16 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 00:13:40 -0600 Subject: [PATCH 12/17] try here --- .github/workflows/python-package-conda.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 7360bf4..c62de93 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -79,7 +79,7 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} distribute: - name: Distributing from ${{ env.PYTHON_VERSION }} + name: Distributing # needs: build-and-test # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') runs-on: ubuntu-latest @@ -87,7 +87,7 @@ jobs: run: shell: bash -l {0} env: - PYTHON_VERSION: 3.11 + PYTHON_VERSION: '3.11' steps: - uses: actions/checkout@v4 From 500703e224601b25358cad59c33f8e9910fbf84f Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 00:16:36 -0600 Subject: [PATCH 13/17] uncomment out portions --- .github/workflows/python-package-conda.yml | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index c62de93..096da79 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -80,8 +80,8 @@ jobs: distribute: name: Distributing - # needs: build-and-test - # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + needs: build-and-test + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') runs-on: ubuntu-latest defaults: run: @@ -120,17 +120,17 @@ jobs: make html cd .. -# - name: GitHub Pages -# uses: crazy-max/ghaction-github-pages@v2.5.0 -# with: -# build_dir: docs/_build/html -# jekyll: false -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# -# - name: pypi-publish -# uses: pypa/gh-action-pypi-publish@v1.4.2 -# with: -# user: __token__ -# password: ${{ secrets.PYPI_API_TOKEN }} -# skip_existing: true + - name: GitHub Pages + uses: crazy-max/ghaction-github-pages@v2.5.0 + with: + build_dir: docs/_build/html + jekyll: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: pypi-publish + uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + skip_existing: true From e895b22ef03fb66f2e3397b326c3452a6640d5a3 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 00:23:11 -0600 Subject: [PATCH 14/17] last bit of coverage --- tests/test_Wrappers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_Wrappers.py b/tests/test_Wrappers.py index 8159755..8a6b427 100644 --- a/tests/test_Wrappers.py +++ b/tests/test_Wrappers.py @@ -59,6 +59,10 @@ def clean(self): Ainv = WrappedClass(A) assert Ainv.A is A assert Ainv.solver.A is A + + rhs = np.array([0.9, 1.0]) + npt.assert_equal(Ainv @ rhs, rhs) + Ainv.clean() assert Ainv.solver.A is None From 99dad48e159c26d4afb167a40badbbe522614ce3 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 09:37:51 -0600 Subject: [PATCH 15/17] simplify warning and error names --- pymatsolver/__init__.py | 2 +- pymatsolver/solvers.py | 10 +++++++--- pymatsolver/wrappers.py | 4 ++-- tests/test_Basic.py | 3 ++- tests/test_Pardiso.py | 2 +- tests/test_Wrappers.py | 3 ++- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pymatsolver/__init__.py b/pymatsolver/__init__.py index 6dd5dbc..1d6e937 100644 --- a/pymatsolver/__init__.py +++ b/pymatsolver/__init__.py @@ -63,7 +63,7 @@ from .direct import Solver, pardiso from .direct import SolverLU -from .solvers import PymatsolverAccuracyError +from .solvers import SolverAccuracyError from .direct import Pardiso, Mumps from .direct.pardiso import _available as _pardiso_available from .direct.mumps import _available as _mumps_available diff --git a/pymatsolver/solvers.py b/pymatsolver/solvers.py index 72accd8..f8d79a4 100644 --- a/pymatsolver/solvers.py +++ b/pymatsolver/solvers.py @@ -7,7 +7,11 @@ import copy -class PymatsolverAccuracyError(Exception): +class SolverAccuracyError(Exception): + pass + + +class UnusedArgumentWarning(UserWarning): pass @@ -93,7 +97,7 @@ def __init__( if kwargs: warnings.warn( f"Unused keyword arguments for {self.__class__.__name__}: {kwargs.keys()}", - UserWarning, + UnusedArgumentWarning, stacklevel=3 ) @@ -275,7 +279,7 @@ def _compute_accuracy(self, rhs, x): rhs_norm = np.linalg.norm(rhs) tolerance = max(self.check_rtol * rhs_norm, self.check_atol) if resid_norm > tolerance: - raise PymatsolverAccuracyError( + raise SolverAccuracyError( f'Accuracy on solve is above tolerance: {resid_norm} > {tolerance}' ) diff --git a/pymatsolver/wrappers.py b/pymatsolver/wrappers.py index 97a38d5..6982fc2 100644 --- a/pymatsolver/wrappers.py +++ b/pymatsolver/wrappers.py @@ -2,7 +2,7 @@ from inspect import signature import numpy as np -from pymatsolver.solvers import Base +from pymatsolver.solvers import Base, UnusedArgumentWarning def _valid_kwargs_for_func(func, **kwargs): """Validates keyword arguments for a function by inspecting its signature. @@ -28,7 +28,7 @@ def _valid_kwargs_for_func(func, **kwargs): sig.bind_partial(**{key: value}) valid_kwargs[key] = value except TypeError: - warnings.warn(f'Unused keyword argument "{key}" for {func.__name__}.', UserWarning, stacklevel=3) + warnings.warn(f'Unused keyword argument "{key}" for {func.__name__}.', UnusedArgumentWarning, stacklevel=3) # stack level of three because we want the warning issued at the call # to the wrapped solver's `__init__` method. return valid_kwargs diff --git a/tests/test_Basic.py b/tests/test_Basic.py index 2a074ab..cc3c826 100644 --- a/tests/test_Basic.py +++ b/tests/test_Basic.py @@ -4,6 +4,7 @@ import scipy.sparse as sp import pymatsolver from pymatsolver import Diagonal +from pymatsolver.solvers import UnusedArgumentWarning TOL = 1e-12 @@ -84,7 +85,7 @@ def test_errors_and_warnings(): with pytest.warns(FutureWarning, match="accuracy_tol is deprecated.*"): IdentitySolver(np.full((4, 4), 1), accuracy_tol=0.41) - with pytest.warns(UserWarning, match="Unused keyword arguments.*"): + with pytest.warns(UnusedArgumentWarning, match="Unused keyword arguments.*"): IdentitySolver(np.full((4, 4), 1), not_an_argument=4) with pytest.raises(TypeError, match="is_symmetric must be a boolean."): diff --git a/tests/test_Pardiso.py b/tests/test_Pardiso.py index d7e9a19..7c12e7f 100644 --- a/tests/test_Pardiso.py +++ b/tests/test_Pardiso.py @@ -128,7 +128,7 @@ def test_inacurrate_symmetry(test_mat_data): D = sp.diags(np.linspace(2, 3, A.shape[0])) A = A @ D Ainv = pymatsolver.Pardiso(A, is_symmetric=True, check_accuracy=True) - with pytest.raises(pymatsolver.PymatsolverAccuracyError): + with pytest.raises(pymatsolver.SolverAccuracyError): Ainv * rhs diff --git a/tests/test_Wrappers.py b/tests/test_Wrappers.py index 8a6b427..10b0dab 100644 --- a/tests/test_Wrappers.py +++ b/tests/test_Wrappers.py @@ -1,4 +1,5 @@ from pymatsolver import SolverCG, SolverLU, wrap_direct, wrap_iterative +from pymatsolver.solvers import UnusedArgumentWarning import pytest import scipy.sparse as sp import warnings @@ -10,7 +11,7 @@ def test_wrapper_unused_kwargs(solver_class): A = sp.eye(10) - with pytest.warns(UserWarning, match="Unused keyword argument.*"): + with pytest.warns(UnusedArgumentWarning, match="Unused keyword argument.*"): solver_class(A, not_a_keyword_arg=True) def test_good_arg_iterative(): From 8cc077958d3e261c61697e617bea2a80463e1fb7 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 09:45:14 -0600 Subject: [PATCH 16/17] update action versions --- .github/workflows/python-package-conda.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 096da79..29602d1 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -62,6 +62,13 @@ jobs: run: | make coverage + - name: Generate Source Distribution + if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python-version == '3.11') }} + run: | + pip install build twine + python -m build --sdist . + twine check dist/* + - name: Test Documentation if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python-version == '3.11') }} run: | @@ -111,8 +118,9 @@ jobs: - name: Generate Source Distribution run: | - pip install build + pip install build twine python -m build --sdist . + twine check dist/* - name: Build Documentation run: | @@ -121,15 +129,16 @@ jobs: cd .. - name: GitHub Pages - uses: crazy-max/ghaction-github-pages@v2.5.0 + uses: crazy-max/ghaction-github-pages@v4 with: build_dir: docs/_build/html jekyll: false + keep_history: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: pypi-publish - uses: pypa/gh-action-pypi-publish@v1.4.2 + uses: pypa/gh-action-pypi-publish@v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} From 9d9b60f06421ce8db3a491714d21cb462c494a75 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 11 Oct 2024 09:57:59 -0600 Subject: [PATCH 17/17] update readme --- README.rst | 68 +++++++++++++++++------------------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/README.rst b/README.rst index 450e634..767a77f 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ pymatsolver :target: https://github.com/simpeg/pymatsolver/blob/master/LICENSE :alt: MIT license. -.. image:: https://codecov.io/gh/simpeg/pymatsolver/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/simpeg/pymatsolver/branch/main/graph/badge.svg?token=8uQoxzxf3r :target: https://codecov.io/gh/simpeg/pymatsolver :alt: Coverage status @@ -32,60 +32,34 @@ All solvers work with :code:`scipy.sparse` matricies, and a single or multiple r * L/U Triangular Solves * Wrapping of SciPy matrix solvers (direct and indirect) -* Pardiso solvers now that MKL comes with conda! -* Mumps solver with nice error messages +* Pardiso solvers +* Mumps solvers -Installing Mumps -================ +Installing Solvers +================== +Often, there are faster solvers available for your system than the default scipy factorizations available. +pymatsolver provides a consistent interface to both MKL's ``Pardiso`` routines and the ``MUMPS`` solver package. To +make use of these we use intermediate wrappers for the libraries that must be installed separately. -We have not been able to get the pip install to work because of multiple dependencies on fortran libraries. -However, the linux and mac installs are relatively easy. Note that you must have mumps pre-installed, -currently we have only got this working for the sequential version, so when you are installing, -you will need to point to that one. You can also look at the `.travis.yml` file for how to get it working on TravisCI. +Pardiso +------- +The Pardiso interface is recommended for Intel processor based systems. The interface is enabled by +the ``pydiso`` python package, which can be installed through conda-forge as: -Linux ------ - -From a clean install on Ubuntu: - -.. code-block:: bash - - apt-get update - apt-get -y install gcc gfortran git libopenmpi-dev libmumps-seq-dev libblas-dev liblapack-dev - - # Install all the python you need! - wget http://repo.continuum.io/miniconda/Miniconda-3.8.3-Linux-x86_64.sh -O miniconda.sh; - chmod +x miniconda.sh - ./miniconda.sh -b - export PATH=/root/anaconda/bin:/root/miniconda/bin:$PATH - conda update --yes conda - conda install --yes numpy scipy matplotlib cython ipython pytest coverage - - git clone https://github.com/rowanc1/pymatsolver.git - cd pymatsolver - make mumps +.. code:: -Mac ---- + conda install -c conda-forge pydiso -This assumes that you have Brew and some python installed (numpy, scipy): - -.. code-block:: bash - - brew install mumps --with-scotch5 --without-mpi - - git clone https://github.com/rowanc1/pymatsolver.git - cd pymatsolver - make mumps_mac - -If you have problems you may have to go into the Makefile and update the pointers to Lib and Include for the various libraries. +Mumps +----- +Mumps is available for all platforms. The mumps interface is enabled by installing the ``python-mumps`` +wrapper package. This can easily be installed through conda-forge with: -This command is helpful for finding dependencies. You should also take note of have happens when brew installs mumps. +.. code:: -.. code-block:: bash + conda install -c conda-forge python-mumps - mpicc --showme Code: @@ -93,7 +67,7 @@ https://github.com/simpeg/pymatsolver Tests: -https://travis-ci.org/simpeg/pymatsolver +https://github.com/simpeg/pymatsolver/actions Bugs & Issues: