Skip to content
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/quality-checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]
fail-fast: false

runs-on: ${{ matrix.os }}
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

## v0.4.2 - 11.02.2026
Maintance release (https://github.com/geometric-kernels/GeometricKernels/pull/173).
* Fix compatibility with modern `plum-dispatch`/`numpy`.
* Replace legacy `plum` typing aliases with modern Python type syntax.
* Drop Python 3.9 support.
* Discontinue testing for `gpflow` due to incompatibility with the latest `setuptools`.

## v0.4.1 - 15.12.2025
* HammingGraph space by @colmont in https://github.com/geometric-kernels/GeometricKernels/pull/170
* Added a link to a pull request example in README by @vabor112 in https://github.com/geometric-kernels/GeometricKernels/pull/171
Expand Down
8 changes: 2 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ lint: sync

test: sync ## Run the tests, start with the failing ones and break on first fail.
@$(UV_RUN) pytest -v -x --ff -rN -Wignore -s --tb=short --durations=0 --cov --cov-report=xml --cov-report=html:coverage_html tests
@$(UV_RUN) pytest --nbmake --nbmake-kernel=python3 --durations=0 --nbmake-timeout=1000 --ignore=notebooks/frontends/GPJax.ipynb notebooks/
@if [ "$(UV_PYTHON)" = "python3.9" ]; then \
echo "Skipping GPJax notebook on python3.9"; \
else \
$(UV_RUN) pytest --nbmake --nbmake-kernel=python3 --durations=0 --nbmake-timeout=1000 notebooks/frontends/GPJax.ipynb; \
fi;
# gpflow is ignored due to incompatibility with the recent setuptools
@$(UV_RUN) pytest --nbmake --nbmake-kernel=python3 --durations=0 --nbmake-timeout=1000 --ignore=notebooks/frontends/GPflow.ipynb notebooks/
@echo -e "$(SUCCESS)Tests done$(RESET)"
5 changes: 2 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Before doing anything, you might want to create and activate a new virtual envir

uv venv --python python[version] [venv_dir]

where [env_dir] is the directory (default is `.venv`) of the environment and [version] is the version of Python you want to use, we currently support 3.9, 3.10, 3.11, 3.12.
where [env_dir] is the directory (default is `.venv`) of the environment and [version] is the version of Python you want to use, we currently support 3.10, 3.11, 3.12.

[Optional] activate the environment. However, this is not strictly necessary for `uv`. Instead, use tools like `uv run python` to run Python inside the environment. See `uv documentation <https://docs.astral.sh/uv/>` for more details.

Expand Down Expand Up @@ -65,7 +65,7 @@ where [env_dir] is the directory (default is `.venv`) of the environment and [ve
conda create -n [env_name] python=[version]
conda activate [env_name]

where [env_name] is the name of the environment and [version] is the version of Python you want to use, we currently support 3.9, 3.10, 3.11, 3.12.
where [env_name] is the name of the environment and [version] is the version of Python you want to use, we currently support 3.10, 3.11, 3.12.

.. raw:: html

Expand Down Expand Up @@ -449,4 +449,3 @@ Please also consider citing the theoretical papers the library is based on. You
API reference <autoapi/geometric_kernels/index>
Bibliography <bibliography>
GitHub <https://github.com/geometric-kernels/GeometricKernels>

35 changes: 31 additions & 4 deletions geometric_kernels/frontends/gpflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,40 @@
:doc:`frontends/GPflow.ipynb </examples/frontends/GPflow>` notebook.
"""

import gpflow
import numpy as np
import tensorflow as tf
from beartype.typing import List, Optional, Union
from gpflow.base import TensorType
from gpflow.kernels.base import ActiveDims
from gpflow.utilities import positive

try:
import gpflow
from gpflow.base import TensorType
from gpflow.kernels.base import ActiveDims
from gpflow.utilities import positive
except ImportError as error:
seen = set()
current: BaseException | None = error
missing_pkg_resources = False
while current is not None and id(current) not in seen:
seen.add(id(current))
if getattr(current, "name", None) == "pkg_resources":
missing_pkg_resources = True
break
text = str(current)
if "pkg_resources" in text and (
"No module named" in text or "cannot import name" in text
):
missing_pkg_resources = True
break
current = current.__cause__ or current.__context__

if missing_pkg_resources:
raise ImportError(
"Importing `gpflow` failed because it depends on `pkg_resources`. "
"`pkg_resources` was removed from `setuptools==82.0.0`. "
"You can try pinning an older `setuptools` version, but this setup "
"is no longer tested by GeometricKernels."
) from error
raise

from geometric_kernels.kernels import BaseGeometricKernel
from geometric_kernels.spaces import Space
Expand Down
10 changes: 3 additions & 7 deletions geometric_kernels/lab_extras/extras.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import lab as B
from beartype.typing import List
from lab import dispatch
from lab.util import abstract
from plum import Union
from scipy.sparse import spmatrix


Expand All @@ -23,7 +21,7 @@ def take_along_axis(a: B.Numeric, index: B.Numeric, axis: int = 0):

@dispatch
@abstract()
def from_numpy(_: B.Numeric, b: Union[List, B.Numeric]):
def from_numpy(_: B.Numeric, b: list | B.Numeric):
"""
Converts the array `b` to a tensor of the same backend as `_`.

Expand Down Expand Up @@ -290,7 +288,7 @@ def eigvalsh(x: B.Numeric):

@dispatch
@abstract()
def reciprocal_no_nan(x: Union[B.Numeric, spmatrix]):
def reciprocal_no_nan(x: B.Numeric | spmatrix):
"""
Return element-wise reciprocal (1/x). Whenever x = 0 puts 1/x = 0.

Expand Down Expand Up @@ -357,9 +355,7 @@ def bool_like(reference: B.Numeric):
"""


def smart_cast(
dtype: Union[B.Bool, B.Int, B.Float, B.Complex, B.Numeric], x: B.Numeric
):
def smart_cast(dtype: B.Bool | B.Int | B.Float | B.Complex | B.Numeric, x: B.Numeric):
"""
Return `x` cast to the `dtype` abstract data type.

Expand Down
11 changes: 6 additions & 5 deletions geometric_kernels/lab_extras/jax/extras.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from typing import TypeAlias

import jax.numpy as jnp
import lab as B
from beartype.typing import List
from lab import dispatch
from plum import Union, convert
from plum import convert

_Numeric = Union[B.Number, B.JAXNumeric]
_Numeric: TypeAlias = B.Number | B.JAXNumeric


@dispatch
def take_along_axis(a: Union[_Numeric, B.Numeric], index: _Numeric, axis: int = 0) -> _Numeric: # type: ignore
def take_along_axis(a: _Numeric | B.Numeric, index: _Numeric, axis: int = 0) -> _Numeric: # type: ignore
"""
Gathers elements of `a` along `axis` at `index` locations.
"""
Expand All @@ -18,7 +19,7 @@ def take_along_axis(a: Union[_Numeric, B.Numeric], index: _Numeric, axis: int =


@dispatch
def from_numpy(_: B.JAXNumeric, b: Union[List, B.NPNumeric, B.Number, B.JAXNumeric]): # type: ignore
def from_numpy(_: B.JAXNumeric, b: list | B.NPNumeric | B.Number | B.JAXNumeric): # type: ignore
"""
Converts the array `b` to a tensor of the same backend as `a`
"""
Expand Down
11 changes: 6 additions & 5 deletions geometric_kernels/lab_extras/numpy/extras.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from typing import TypeAlias

import lab as B
import numpy as np
from beartype.typing import Any, List, Optional
from beartype.typing import Any
from lab import dispatch
from plum import Union
from scipy.sparse import spmatrix

_Numeric = Union[B.Number, B.NPNumeric]
_Numeric: TypeAlias = B.Number | B.NPNumeric


@dispatch
Expand All @@ -17,7 +18,7 @@ def take_along_axis(a: _Numeric, index: _Numeric, axis: int = 0) -> _Numeric: #


@dispatch
def from_numpy(_: B.NPNumeric, b: Union[List, B.NPNumeric, B.Number]): # type: ignore
def from_numpy(_: B.NPNumeric, b: list | B.NPNumeric | B.Number): # type: ignore
"""
Converts the array `b` to a tensor of the same backend as `a`
"""
Expand All @@ -33,7 +34,7 @@ def trapz(y: _Numeric, x: _Numeric, dx: _Numeric = 1.0, axis: int = -1): # type


@dispatch
def norm(x: _Numeric, ord: Optional[Any] = None, axis: Optional[int] = None): # type: ignore
def norm(x: _Numeric, ord: Any | None = None, axis: int | None = None): # type: ignore
"""
Matrix or vector norm.
"""
Expand Down
17 changes: 6 additions & 11 deletions geometric_kernels/lab_extras/tensorflow/extras.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
import sys
from typing import TypeAlias

import lab as B
import tensorflow as tf
import tensorflow_probability as tfp
from beartype.typing import Any, List, Optional
from beartype.typing import Any
from lab import dispatch
from plum import Union

_Numeric = Union[B.Number, B.TFNumeric, B.NPNumeric]
_Numeric: TypeAlias = B.Number | B.TFNumeric | B.NPNumeric


@dispatch
def take_along_axis(a: _Numeric, index: _Numeric, axis: int = 0) -> _Numeric: # type: ignore
"""
Gathers elements of `a` along `axis` at `index` locations.
"""
if sys.version_info[:2] <= (3, 9):
index = tf.cast(index, tf.int32)
return tf.experimental.numpy.take_along_axis(
a, index, axis=axis
) # the absence of explicit cast to int64 causes an error for Python 3.9 and below
return tf.experimental.numpy.take_along_axis(a, index, axis=axis)


@dispatch
def from_numpy(_: B.TFNumeric, b: Union[List, B.Numeric, B.NPNumeric, B.TFNumeric]): # type: ignore
def from_numpy(_: B.TFNumeric, b: list | B.Numeric | B.NPNumeric | B.TFNumeric): # type: ignore
"""
Converts the array `b` to a tensor of the same backend as `a`
"""
Expand All @@ -39,7 +34,7 @@ def trapz(y: _Numeric, x: _Numeric, dx=None, axis=-1): # type: ignore


@dispatch
def norm(x: _Numeric, ord: Optional[Any] = None, axis: Optional[int] = None): # type: ignore
def norm(x: _Numeric, ord: Any | None = None, axis: int | None = None): # type: ignore
"""
Matrix or vector norm.
"""
Expand Down
13 changes: 7 additions & 6 deletions geometric_kernels/lab_extras/torch/extras.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from typing import TypeAlias

import lab as B
import torch
from beartype.typing import Any, List, Optional
from beartype.typing import Any
from lab import dispatch
from plum import Union

_Numeric = Union[B.Number, B.TorchNumeric]
_Numeric: TypeAlias = B.Number | B.TorchNumeric


@dispatch
def take_along_axis(a: Union[_Numeric, B.Numeric], index: _Numeric, axis: int = 0) -> _Numeric: # type: ignore
def take_along_axis(a: _Numeric | B.Numeric, index: B.TorchNumeric, axis: int = 0) -> _Numeric: # type: ignore
"""
Gathers elements of `a` along `axis` at `index` locations.
"""
Expand All @@ -19,7 +20,7 @@ def take_along_axis(a: Union[_Numeric, B.Numeric], index: _Numeric, axis: int =

@dispatch
def from_numpy(
a: B.TorchNumeric, b: Union[List, B.Number, B.NPNumeric, B.TorchNumeric]
a: B.TorchNumeric, b: list | B.Number | B.NPNumeric | B.TorchNumeric
): # type: ignore
"""
Converts the array `b` to a tensor of the same backend as `a`
Expand All @@ -38,7 +39,7 @@ def trapz(y: B.TorchNumeric, x: _Numeric, dx: _Numeric = 1.0, axis: int = -1):


@dispatch
def norm(x: _Numeric, ord: Optional[Any] = None, axis: Optional[int] = None): # type: ignore
def norm(x: _Numeric, ord: Any | None = None, axis: int | None = None): # type: ignore
"""
Matrix or vector norm.
"""
Expand Down
15 changes: 7 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ description="A Python Package offering geometric kernels in NumPy, TensorFlow, P
readme = "README.md"
classifiers = [
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand All @@ -21,21 +20,21 @@ classifiers = [
keywords=[
"geometric-kernels",
]
requires-python = ">=3.9"
requires-python = ">=3.10"
dependencies = [
"backends", # >=1.7",
"backends>=1.8.0",
"einops",
"geomstats",
"numpy>=2.0",
"numpy>=2.0,<2.4", # constraining from above due to geomstats==2.8.0 incompatibility
"opt-einsum",
"plum-dispatch",
"plum-dispatch>=2.6.0",
"potpourri3d",
"robust_laplacian",
"scipy>=1.3",
"spherical-harmonics-basis",
"sympy~=1.13",
]
version="0.4.1"
version="0.4.2"

[project.urls]
Documentation = "https://geometric-kernels.github.io/"
Expand All @@ -58,7 +57,7 @@ allow_redefinition = true

[tool.black]
line-length = 88
target-version = ['py39', 'py310', 'py311', 'py312']
target-version = ['py310', 'py311', 'py312']


[tool.uv]
Expand Down Expand Up @@ -106,5 +105,5 @@ dev = [
'jaxlib',
'jaxtyping',
'optax',
'gpjax>=0.12.2; python_version >= "3.10"', # gpjax is not supported on python-3.9 or older.
'gpjax>=0.12.2; python_version >= "3.10"', # gpjax requires python >= 3.10.
]
3 changes: 1 addition & 2 deletions tests/spaces/test_hamming_graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import lab as B
import numpy as np
import pytest
from plum import Tuple

from geometric_kernels.kernels import MaternGeometricKernel
from geometric_kernels.spaces import HammingGraph, HypercubeGraph
Expand All @@ -11,7 +10,7 @@


@pytest.fixture(params=[(1, 2), (2, 2), (5, 2), (10, 2), (10, 4)])
def inputs(request) -> Tuple[B.Numeric]:
def inputs(request) -> tuple[B.Numeric]:
"""
Returns a tuple (space, eigenfunctions, X, X2, weights) where:
- space is a HammingGraph object with (dim, n_cat) equal to request.param,
Expand Down
3 changes: 1 addition & 2 deletions tests/spaces/test_hypercube_graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import lab as B
import numpy as np
import pytest
from plum import Tuple

from geometric_kernels.kernels import MaternGeometricKernel
from geometric_kernels.spaces import HypercubeGraph
Expand All @@ -11,7 +10,7 @@


@pytest.fixture(params=[1, 2, 3, 5, 10])
def inputs(request) -> Tuple[B.Numeric]:
def inputs(request) -> tuple[B.Numeric]:
"""
Returns a tuple (space, eigenfunctions, X, X2) where:
- space is a HypercubeGraph object with dimension equal to request.param,
Expand Down