diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd9518a..5fa9e55 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,16 +16,16 @@ jobs: source_check: name: source check runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v4 + with: + fetch-depth: "0" - - name: Set up Python 3.9 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.11 - name: Install dependencies run: | @@ -34,7 +34,7 @@ jobs: - name: black check run: | - python -m black --check --diff --color . + python -m black --check --diff --color . - name: isort check run: | @@ -50,49 +50,48 @@ jobs: strategy: fail-fast: false matrix: - # macos-13 is an intel runner, macos-14 is apple silicon - os: [ubuntu-latest, windows-latest, macos-13, macos-14] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest] # windows-11-arm (no scipy) steps: - uses: actions/checkout@v4 with: - fetch-depth: '0' + fetch-depth: "0" - name: Build wheels - uses: pypa/cibuildwheel@v2.18.0 + uses: pypa/cibuildwheel@v3.2.1 with: - output-dir: dist + output-dir: dist-wheel-${{ matrix.os }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - path: ./dist/*.whl + name: dist-wheel-${{ matrix.os }} + path: ./dist-wheel-${{ matrix.os }}/*.whl build_sdist: - name: sdist on ${{ matrix.os }} with py ${{ matrix.ver.py }} numpy${{ matrix.ver.np }} scipy${{ matrix.ver.sp }} + name: sdist on ${{ matrix.os }} with py==${{ matrix.ver.py }} numpy${{ matrix.ver.np }} scipy${{ matrix.ver.sp }} sklearn${{ matrix.ver.sk }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-13, macos-14] - # https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg + os: + [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-14] ver: - - {py: '3.8', np: '==1.20.0', sp: '==1.5.4'} - - {py: '3.9', np: '==1.20.0', sp: '==1.5.4'} - - {py: '3.10', np: '==1.21.6', sp: '==1.7.2'} - - {py: '3.11', np: '==1.23.2', sp: '==1.9.2'} - - {py: '3.12', np: '==1.26.2', sp: '==1.11.2'} - - {py: '3.12', np: '>=2.0.0rc1', sp: '>=1.13.0'} + - { py: "3.9", np: "==1.20.0", sp: "==1.5.4", sk: "==0.24.0" } + - { py: "3.10", np: "==1.21.6", sp: "==1.7.2", sk: "==1.0.2" } + - { py: "3.11", np: "==1.23.2", sp: "==1.9.2", sk: "==1.1.3" } + - { py: "3.12", np: "==1.26.2", sp: "==1.11.2", sk: "==1.3.1" } + - { py: "3.13", np: "==2.1.0", sp: "==1.14.1", sk: "==1.5.2" } + - { py: "3.14", np: "==2.3.2", sp: "==1.16.1", sk: "==1.7.2" } + - { py: "3.14", np: ">=2.3.2", sp: ">=1.16.1", sk: ">=1.7.2" } exclude: - os: macos-14 - ver: {py: '3.8', np: '==1.20.0', sp: '==1.5.4'} - - os: macos-14 - ver: {py: '3.9', np: '==1.20.0', sp: '==1.5.4'} + ver: { py: "3.9", np: "==1.20.0", sp: "==1.5.4", sk: "==0.24.0" } - os: macos-14 - ver: {py: '3.10', np: '==1.21.6', sp: '==1.7.2'} + ver: { py: "3.10", np: "==1.21.6", sp: "==1.7.2", sk: "==1.0.2" } steps: - uses: actions/checkout@v4 with: - fetch-depth: '0' + fetch-depth: "0" - name: Set up Python ${{ matrix.ver.py }} uses: actions/setup-python@v5 @@ -102,41 +101,74 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install build "coveralls>=3.0.0" + pip install build - - name: Install PyKrige + - name: Install pykrige run: | pip install -v --editable .[test] + - name: Install pinned dependencies + run: | + pip install "numpy${{ matrix.ver.np }}" "scipy${{ matrix.ver.sp }}" "scikit-learn${{ matrix.ver.sk }}" + - name: Run tests - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - pip install "numpy${{ matrix.ver.np }}" "scipy${{ matrix.ver.sp }}" - python -m pytest --cov pykrige --cov-report term-missing -v tests/ - python -m coveralls --service=github + python -m pytest -v tests/ - name: Build sdist run: | # PEP 517 package builder from pypa - python -m build --sdist --outdir dist . + python -m build --sdist --outdir dist-sdist . - - uses: actions/upload-artifact@v3 - if: matrix.os == 'ubuntu-latest' && matrix.ver.py == '3.9' + - uses: actions/upload-artifact@v4 + if: matrix.os == 'ubuntu-latest' && matrix.ver.py == '3.11' with: - path: dist/*.tar.gz + name: dist-sdist + path: dist-sdist/*.tar.gz + + coverage: + name: coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install "coveralls>=3.0.0" + + - name: Install pykrige + run: | + pip install -v --editable .[test] + + - name: Run tests + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pip install "numpy${{ matrix.ver.np }}" + python -m pytest --cov pykrige --cov-report term-missing -v tests/ + python -m coveralls --service=github upload_to_pypi: needs: [build_wheels, build_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: - name: artifact + pattern: dist-* + merge-multiple: true path: dist - name: Publish to Test PyPI + # only if working on main if: github.ref == 'refs/heads/main' uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index b0fd898..e22bdc0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ Changelog ========= +Version 1.7.3 +------------- +*October 16, 2025* + +See [#308](https://github.com/GeoStat-Framework/PyKrige/pull/308) + +**Changes** + +* add wheels for Python 3.13 and 3.14 as well as for Linux aarch64 +* pin the earliest supported scikit-learn release alongside NumPy/SciPy in the sdist CI +* allow the housing-based classification/regression tests to attempt loading the California housing dataset and skip cleanly when the download fails + +**Bug fixes** + +* prevent `ValueError` from NumPy when pseudo inverse kriging statistics encounter duplicate coordinates by keeping distance vectors 1-D + + Version 1.7.2 ------------- *May 27, 2024* diff --git a/examples/05_kriging_1D.py b/examples/05_kriging_1D.py index 0f083f5..b5b1c97 100644 --- a/examples/05_kriging_1D.py +++ b/examples/05_kriging_1D.py @@ -4,6 +4,7 @@ An example of 1D kriging with PyKrige """ + import matplotlib.pyplot as plt import numpy as np diff --git a/examples/06_exact_values_example_1D.py b/examples/06_exact_values_example_1D.py index 404b07d..5a9b32f 100644 --- a/examples/06_exact_values_example_1D.py +++ b/examples/06_exact_values_example_1D.py @@ -4,7 +4,7 @@ ============ PyKrige demonstration and usage -as a non-exact interpolator in 1D. +as a non-exact interpolator in 1D. """ import matplotlib.pyplot as plt diff --git a/pyproject.toml b/pyproject.toml index 8a59fe4..d504766 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,17 @@ [build-system] requires = [ - "setuptools>=64", - "setuptools_scm>=7", - "numpy>=2.0.0rc1,<2.3; python_version >= '3.9'", - "scipy>=1.13.0,<2; python_version >= '3.9'", - "oldest-supported-numpy; python_version < '3.9'", - "scipy>=1.3.2,<2; python_version < '3.9'", - "Cython>=3.0.10,<3.1.0", + "setuptools>=77", + "wheel", + "Cython>=3", + "numpy>=2", + "scipy>=1.5.4", + "pentapy>=1.1.0,<2", + "setuptools_scm[toml]>=7", ] build-backend = "setuptools.build_meta" [project] -requires-python = ">=3.8" +requires-python = ">=3.9" name = "PyKrige" description = "Kriging Toolkit for Python." authors = [ @@ -21,14 +21,14 @@ maintainers = [ {name = "Sebastian Müller, Roman Yurchak", email = "info@geostat-framework.org"}, ] readme = "README.md" -license = {text = "BSD-3-Clause"} +license = "BSD-3-Clause" +license-files = ["LICENSE"] dynamic = ["version"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Intended Audience :: Science/Research", - "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: Unix", "Operating System :: Microsoft", @@ -36,11 +36,12 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: GIS", "Topic :: Scientific/Engineering :: Mathematics", @@ -49,7 +50,7 @@ classifiers = [ ] dependencies = [ "numpy>=1.20.0", - "scipy>=1.1.0,<2", + "scipy>=1.5.4,<2", ] [project.optional-dependencies] @@ -86,9 +87,6 @@ Homepage = "https://github.com/GeoStat-Framework/PyKrige" Source = "https://github.com/GeoStat-Framework/PyKrige" Tracker = "https://github.com/GeoStat-Framework/PyKrige/issues" -[tool.setuptools] -license-files = ["LICENSE"] - [tool.setuptools_scm] write_to = "src/pykrige/_version.py" write_to_template = "__version__ = '{version}'" @@ -155,8 +153,11 @@ max-line-length = 100 [tool.cibuildwheel] # Switch to using build build-frontend = "build" -# Disable building PyPy wheels on all platforms, 32bit and musllinux builds, py3.6/7 -skip = ["cp36-*", "cp37-*", "pp*", "*-win32", "*-manylinux_i686", "*-musllinux_*"] +# Disable building py3.6/7/8, pp3.8, 32bit linux +skip = ["*-win32", "*_i686", "*-musllinux_*", "cp31?t-*"] # Run the package tests using `pytest` test-extras = "test" test-command = "pytest -v {package}/tests" + +environment.PIP_ONLY_BINARY = "numpy,scipy" +environment.PIP_PREFER_BINARY = "1" diff --git a/src/pykrige/core.py b/src/pykrige/core.py index 25ad3e4..30ae911 100755 --- a/src/pykrige/core.py +++ b/src/pykrige/core.py @@ -700,7 +700,7 @@ def _krige( # measurement points (X) and the kriging point (coords) if coordinates_type == "euclidean": d = squareform(pdist(X, metric="euclidean")) - bd = np.squeeze(cdist(X, coords[None, :], metric="euclidean")) + bd = np.asarray(cdist(X, coords[None, :], metric="euclidean")).ravel() # geographic coordinate distances still calculated in the old way... # assume X[:, 0] ('x') => lon, X[:, 1] ('y') => lat @@ -709,12 +709,14 @@ def _krige( x1, x2 = np.meshgrid(X[:, 0], X[:, 0], sparse=True) y1, y2 = np.meshgrid(X[:, 1], X[:, 1], sparse=True) d = great_circle_distance(x1, y1, x2, y2) - bd = great_circle_distance( - X[:, 0], - X[:, 1], - coords[0] * np.ones(X.shape[0]), - coords[1] * np.ones(X.shape[0]), - ) + bd = np.asarray( + great_circle_distance( + X[:, 0], + X[:, 1], + coords[0] * np.ones(X.shape[0]), + coords[1] * np.ones(X.shape[0]), + ) + ).ravel() # this check is done when initializing variogram, but kept here anyways... else: @@ -725,7 +727,7 @@ def _krige( # check if kriging point overlaps with measurement point if np.any(np.absolute(bd) <= 1e-10): zero_value = True - zero_index = np.where(bd <= 1e-10)[0][0] + zero_index = int(np.flatnonzero(bd <= 1e-10)[0]) # set up kriging matrix n = X.shape[0] diff --git a/tests/test_classification_krige.py b/tests/test_classification_krige.py index c5c9975..9110a97 100644 --- a/tests/test_classification_krige.py +++ b/tests/test_classification_krige.py @@ -69,14 +69,13 @@ def test_krige_classification_housing(): try: housing = fetch_california_housing() - except (ssl.SSLError, urllib.error.URLError): - ssl._create_default_https_context = ssl._create_unverified_context - try: - housing = fetch_california_housing() - except PermissionError: - # This can raise permission error on Appveyor - pytest.skip("Failed to load california housing dataset") - ssl._create_default_https_context = ssl.create_default_context + except ( + ssl.SSLError, + urllib.error.URLError, + urllib.error.HTTPError, + PermissionError, + ): + pytest.skip("Failed to load california housing dataset") # take only first 1000 p = housing["data"][:1000, :-2] diff --git a/tests/test_core.py b/tests/test_core.py index fbd4fd6..20e6fc6 100755 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2,6 +2,7 @@ Testing code. Updated BSM February 2017 """ + import os import sys diff --git a/tests/test_regression_krige.py b/tests/test_regression_krige.py index ca5da2a..e353a2c 100644 --- a/tests/test_regression_krige.py +++ b/tests/test_regression_krige.py @@ -69,14 +69,13 @@ def test_krige_housing(): try: housing = fetch_california_housing() - except (ssl.SSLError, urllib.error.URLError): - ssl._create_default_https_context = ssl._create_unverified_context - try: - housing = fetch_california_housing() - except PermissionError: - # This can raise permission error on Appveyor - pytest.skip("Failed to load california housing dataset") - ssl._create_default_https_context = ssl.create_default_context + except ( + ssl.SSLError, + urllib.error.URLError, + urllib.error.HTTPError, + PermissionError, + ): + pytest.skip("Failed to load california housing dataset") # take only first 1000 p = housing["data"][:1000, :-2]