From bddbd23ca0b4d19b1a463eaa1226d997e95d34ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20Pf=C3=B6rtner?= Date: Fri, 17 Feb 2023 22:08:03 +0100 Subject: [PATCH] Rename `Kernel` to `CovarianceFunction` (#789) * Rename `Kernel` -> `CovarianceFunction` * Renaming in `test_randprocs` * Renaming in benchmarks * Pylint fixes * Bugfix * Update CODEOWNERS --- .github/CODEOWNERS | 4 +- benchmarks/covfuncs.py | 70 ++++++++++++++ benchmarks/kernels.py | 70 -------------- docs/source/api/randprocs.rst | 2 +- docs/source/api/randprocs/covfuncs.rst | 7 ++ docs/source/api/randprocs/kernels.rst | 7 -- src/probnum/randprocs/__init__.py | 3 +- src/probnum/randprocs/_gaussian_process.py | 12 +-- src/probnum/randprocs/_random_process.py | 13 +-- src/probnum/randprocs/covfuncs/__init__.py | 43 +++++++++ src/probnum/randprocs/covfuncs/_arithmetic.py | 17 ++++ .../_arithmetic_fallbacks.py | 95 ++++++++++--------- .../_covariance_function.py} | 52 +++++----- .../_exponentiated_quadratic.py | 24 ++--- .../{kernels => covfuncs}/_linear.py | 10 +- .../{kernels => covfuncs}/_matern.py | 47 ++++----- .../{kernels => covfuncs}/_polynomial.py | 12 +-- .../{kernels => covfuncs}/_product_matern.py | 44 +++++---- .../_rational_quadratic.py | 22 ++--- .../{kernels => covfuncs}/_white_noise.py | 12 +-- src/probnum/randprocs/kernels.py | 10 ++ src/probnum/randprocs/kernels/__init__.py | 43 --------- src/probnum/randprocs/kernels/_arithmetic.py | 17 ---- tests/test_randprocs/conftest.py | 14 +-- .../__init__.py | 0 .../conftest.py | 66 ++++++------- .../test_covfuncs/test_arithmetic.py | 39 ++++++++ .../test_arithmetic_fallbacks.py | 77 +++++++++++++++ .../test_call.py | 52 +++++----- .../test_covfuncs/test_covariance_function.py | 13 +++ .../test_exponentiated_quadratic.py | 6 +- .../test_linop_matrix.py | 69 ++++++++------ .../test_matern.py | 28 +++--- .../test_covfuncs/test_product_matern.py | 43 +++++++++ .../test_rational_quadratic.py | 6 +- tests/test_randprocs/test_gaussian_process.py | 20 ++-- .../test_kernels/test_arithmetic.py | 39 -------- .../test_kernels/test_arithmetic_fallbacks.py | 73 -------------- .../test_kernels/test_kernel.py | 13 --- .../test_kernels/test_product_matern.py | 43 --------- tests/test_randprocs/test_random_process.py | 4 +- 41 files changed, 643 insertions(+), 598 deletions(-) create mode 100644 benchmarks/covfuncs.py delete mode 100644 benchmarks/kernels.py create mode 100644 docs/source/api/randprocs/covfuncs.rst delete mode 100644 docs/source/api/randprocs/kernels.rst create mode 100644 src/probnum/randprocs/covfuncs/__init__.py create mode 100644 src/probnum/randprocs/covfuncs/_arithmetic.py rename src/probnum/randprocs/{kernels => covfuncs}/_arithmetic_fallbacks.py (61%) rename src/probnum/randprocs/{kernels/_kernel.py => covfuncs/_covariance_function.py} (93%) rename src/probnum/randprocs/{kernels => covfuncs}/_exponentiated_quadratic.py (77%) rename src/probnum/randprocs/{kernels => covfuncs}/_linear.py (81%) rename src/probnum/randprocs/{kernels => covfuncs}/_matern.py (82%) rename src/probnum/randprocs/{kernels => covfuncs}/_polynomial.py (80%) rename src/probnum/randprocs/{kernels => covfuncs}/_product_matern.py (71%) rename src/probnum/randprocs/{kernels => covfuncs}/_rational_quadratic.py (76%) rename src/probnum/randprocs/{kernels => covfuncs}/_white_noise.py (77%) create mode 100644 src/probnum/randprocs/kernels.py delete mode 100644 src/probnum/randprocs/kernels/__init__.py delete mode 100644 src/probnum/randprocs/kernels/_arithmetic.py rename tests/test_randprocs/{test_kernels => test_covfuncs}/__init__.py (100%) rename tests/test_randprocs/{test_kernels => test_covfuncs}/conftest.py (53%) create mode 100644 tests/test_randprocs/test_covfuncs/test_arithmetic.py create mode 100644 tests/test_randprocs/test_covfuncs/test_arithmetic_fallbacks.py rename tests/test_randprocs/{test_kernels => test_covfuncs}/test_call.py (71%) create mode 100644 tests/test_randprocs/test_covfuncs/test_covariance_function.py rename tests/test_randprocs/{test_kernels => test_covfuncs}/test_exponentiated_quadratic.py (63%) rename tests/test_randprocs/{test_kernels => test_covfuncs}/test_linop_matrix.py (53%) rename tests/test_randprocs/{test_kernels => test_covfuncs}/test_matern.py (68%) create mode 100644 tests/test_randprocs/test_covfuncs/test_product_matern.py rename tests/test_randprocs/{test_kernels => test_covfuncs}/test_rational_quadratic.py (59%) delete mode 100644 tests/test_randprocs/test_kernels/test_arithmetic.py delete mode 100644 tests/test_randprocs/test_kernels/test_arithmetic_fallbacks.py delete mode 100644 tests/test_randprocs/test_kernels/test_kernel.py delete mode 100644 tests/test_randprocs/test_kernels/test_product_matern.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 86e75a7ae..4d7d829be 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -40,12 +40,12 @@ /tests/test_quad/ @mmahsereci @tskarvone /tests/test_problems/test_zoo/test_quad/ @mmahsereci @tskarvone -# Random Processes & Kernels +# Random Processes & Covariance Functions /src/probnum/randprocs/ @marvinpfoertner @JonathanWenger /tests/test_randprocs/ @marvinpfoertner @JonathanWenger /benchmarks/randprocs.py @marvinpfoertner @JonathanWenger -/benchmarks/kernels.py @marvinpfoertner @JonathanWenger +/benchmarks/covfuncs.py @marvinpfoertner @JonathanWenger /src/probnum/randprocs/markov/ @pnkraemer @schmidtjonathan /tests/test_randprocs/test_markov/ @pnkraemer @schmidtjonathan diff --git a/benchmarks/covfuncs.py b/benchmarks/covfuncs.py new file mode 100644 index 000000000..42f1af29d --- /dev/null +++ b/benchmarks/covfuncs.py @@ -0,0 +1,70 @@ +"""Benchmarks for covariance functions.""" + +import numpy as np + +from probnum.randprocs import covfuncs + +# Module level variables +COVFUNC_NAMES = [ + "white_noise", + "linear", + "polynomial", + "exp_quad", + "rat_quad", + "matern12", + "matern32", + "matern52", + "matern72", +] + +N_DATAPOINTS = [10, 100, 1000] + + +def get_covfunc(covfunc_name, input_shape): + """Return a covariance function for a given name.""" + if covfunc_name == "white_noise": + k = covfuncs.WhiteNoise(input_shape=input_shape) + elif covfunc_name == "linear": + k = covfuncs.Linear(input_shape=input_shape) + elif covfunc_name == "polynomial": + k = covfuncs.Polynomial(input_shape=input_shape) + elif covfunc_name == "exp_quad": + k = covfuncs.ExpQuad(input_shape=input_shape) + elif covfunc_name == "rat_quad": + k = covfuncs.RatQuad(input_shape=input_shape) + elif covfunc_name == "matern12": + k = covfuncs.Matern(input_shape=input_shape, nu=0.5) + elif covfunc_name == "matern32": + k = covfuncs.Matern(input_shape=input_shape, nu=1.5) + elif covfunc_name == "matern52": + k = covfuncs.Matern(input_shape=input_shape, nu=2.5) + elif covfunc_name == "matern72": + k = covfuncs.Matern(input_shape=input_shape, nu=3.5) + else: + raise ValueError(f"Covariance function '{covfunc_name}' not recognized.") + + return k + + +class CovarianceFunctions: + """Benchmark evaluation of a covariance function at a set of inputs.""" + + param_names = ["covfunc", "n_datapoints"] + params = [COVFUNC_NAMES, N_DATAPOINTS] + + def setup(self, covfunc, n_datapoints): + rng = np.random.default_rng(42) + self.input_dim = 100 + self.data = rng.normal(size=(n_datapoints, self.input_dim)) + self.covfunc = get_covfunc(covfunc_name=covfunc, input_shape=self.input_dim) + + def time_covfunc_call(self, covfunc, n_datapoints): + self.covfunc(self.data, None) + + def time_covfunc_matrix(self, covfunc, n_datapoints): + """Times sampling from this distribution.""" + self.covfunc.matrix(self.data) + + def peakmem_covfunc_matrix(self, covfunc, n_datapoints): + """Peak memory of sampling process.""" + self.covfunc.matrix(self.data) diff --git a/benchmarks/kernels.py b/benchmarks/kernels.py deleted file mode 100644 index 270e34aff..000000000 --- a/benchmarks/kernels.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Benchmarks for random variables.""" - -import numpy as np - -from probnum.randprocs import kernels - -# Module level variables -KERNEL_NAMES = [ - "white_noise", - "linear", - "polynomial", - "exp_quad", - "rat_quad", - "matern12", - "matern32", - "matern52", - "matern72", -] - -N_DATAPOINTS = [10, 100, 1000] - - -def get_kernel(kernel_name, input_shape): - """Return a kernel for a given name.""" - if kernel_name == "white_noise": - kernel = kernels.WhiteNoise(input_shape=input_shape) - elif kernel_name == "linear": - kernel = kernels.Linear(input_shape=input_shape) - elif kernel_name == "polynomial": - kernel = kernels.Polynomial(input_shape=input_shape) - elif kernel_name == "exp_quad": - kernel = kernels.ExpQuad(input_shape=input_shape) - elif kernel_name == "rat_quad": - kernel = kernels.RatQuad(input_shape=input_shape) - elif kernel_name == "matern12": - kernel = kernels.Matern(input_shape=input_shape, nu=0.5) - elif kernel_name == "matern32": - kernel = kernels.Matern(input_shape=input_shape, nu=1.5) - elif kernel_name == "matern52": - kernel = kernels.Matern(input_shape=input_shape, nu=2.5) - elif kernel_name == "matern72": - kernel = kernels.Matern(input_shape=input_shape, nu=3.5) - else: - raise ValueError(f"Kernel name '{kernel_name}' not recognized.") - - return kernel - - -class Kernels: - """Benchmark evaluation of a kernel at a set of inputs.""" - - param_names = ["kernel", "n_datapoints"] - params = [KERNEL_NAMES, N_DATAPOINTS] - - def setup(self, kernel, n_datapoints): - rng = np.random.default_rng(42) - self.input_dim = 100 - self.data = rng.normal(size=(n_datapoints, self.input_dim)) - self.kernel = get_kernel(kernel_name=kernel, input_shape=self.input_dim) - - def time_kernel_call(self, kernel, n_datapoints): - self.kernel(self.data, None) - - def time_kernel_matrix(self, kernel, n_datapoints): - """Times sampling from this distribution.""" - self.kernel.matrix(self.data) - - def peakmem_kernel_matrix(self, kernel, n_datapoints): - """Peak memory of sampling process.""" - self.kernel.matrix(self.data) diff --git a/docs/source/api/randprocs.rst b/docs/source/api/randprocs.rst index bb3bc7c2e..9e6d141b3 100644 --- a/docs/source/api/randprocs.rst +++ b/docs/source/api/randprocs.rst @@ -9,5 +9,5 @@ probnum.randprocs .. toctree:: :hidden: + randprocs/covfuncs randprocs/markov - randprocs/kernels diff --git a/docs/source/api/randprocs/covfuncs.rst b/docs/source/api/randprocs/covfuncs.rst new file mode 100644 index 000000000..08780a283 --- /dev/null +++ b/docs/source/api/randprocs/covfuncs.rst @@ -0,0 +1,7 @@ +************************** +probnum.randprocs.covfuncs +************************** + +.. automodapi:: probnum.randprocs.covfuncs + :no-heading: + :headings: "=" diff --git a/docs/source/api/randprocs/kernels.rst b/docs/source/api/randprocs/kernels.rst deleted file mode 100644 index 030ec66a7..000000000 --- a/docs/source/api/randprocs/kernels.rst +++ /dev/null @@ -1,7 +0,0 @@ -************************* -probnum.randprocs.kernels -************************* - -.. automodapi:: probnum.randprocs.kernels - :no-heading: - :headings: "=" diff --git a/src/probnum/randprocs/__init__.py b/src/probnum/randprocs/__init__.py index 99c66126a..71f2983d5 100644 --- a/src/probnum/randprocs/__init__.py +++ b/src/probnum/randprocs/__init__.py @@ -6,7 +6,7 @@ functions with stochastic output. """ -from . import kernels +from . import covfuncs, kernels, markov from ._gaussian_process import GaussianProcess from ._random_process import RandomProcess @@ -15,7 +15,6 @@ "RandomProcess", "GaussianProcess", ] -from . import markov # Set correct module paths. Corrects links and module paths in documentation. RandomProcess.__module__ = "probnum.randprocs" diff --git a/src/probnum/randprocs/_gaussian_process.py b/src/probnum/randprocs/_gaussian_process.py index 4844e2cb3..aea560d4f 100644 --- a/src/probnum/randprocs/_gaussian_process.py +++ b/src/probnum/randprocs/_gaussian_process.py @@ -7,7 +7,7 @@ from probnum import randvars from probnum.typing import ArrayLike -from . import _random_process, kernels +from . import _random_process, covfuncs from .. import functions @@ -23,7 +23,7 @@ class GaussianProcess(_random_process.RandomProcess[ArrayLike, np.ndarray]): mean Mean function. cov - Covariance function or kernel. + Covariance function. See Also -------- @@ -32,14 +32,14 @@ class GaussianProcess(_random_process.RandomProcess[ArrayLike, np.ndarray]): Examples -------- - Define a Gaussian process with a zero mean function and RBF kernel. + Define a Gaussian process with a zero mean function and RBF covariance function. >>> import numpy as np >>> from probnum.functions import Zero - >>> from probnum.randprocs.kernels import ExpQuad + >>> from probnum.randprocs.covfuncs import ExpQuad >>> from probnum.randprocs import GaussianProcess >>> mu = Zero(input_shape=()) # zero-mean function - >>> k = ExpQuad(input_shape=()) # RBF kernel + >>> k = ExpQuad(input_shape=()) # RBF covariance function >>> gp = GaussianProcess(mu, k) Sample from the Gaussian process. @@ -59,7 +59,7 @@ class GaussianProcess(_random_process.RandomProcess[ArrayLike, np.ndarray]): def __init__( self, mean: functions.Function, - cov: kernels.Kernel, + cov: covfuncs.CovarianceFunction, ): if not isinstance(mean, functions.Function): raise TypeError("The mean function must have type `probnum.Function`.") diff --git a/src/probnum/randprocs/_random_process.py b/src/probnum/randprocs/_random_process.py index 061a1f8db..30861372f 100644 --- a/src/probnum/randprocs/_random_process.py +++ b/src/probnum/randprocs/_random_process.py @@ -8,7 +8,7 @@ import numpy as np from probnum import functions, randvars, utils as _utils -from probnum.randprocs import kernels +from probnum.randprocs import covfuncs from probnum.typing import DTypeLike, ShapeLike, ShapeType InputType = TypeVar("InputType") @@ -57,7 +57,7 @@ def __init__( output_shape: ShapeLike, dtype: DTypeLike, mean: Optional[functions.Function] = None, - cov: Optional[kernels.Kernel] = None, + cov: Optional[covfuncs.CovarianceFunction] = None, ): self._input_shape = _utils.as_shape(input_shape) self._input_ndim = len(self._input_shape) @@ -94,9 +94,10 @@ def __init__( # Covariance function if cov is not None: - if not isinstance(cov, kernels.Kernel): + if not isinstance(cov, covfuncs.CovarianceFunction): raise TypeError( - "The covariance functions must be implemented as a `Kernel`." + "The covariance functions must be implemented as a " + "`CovarianceFunction`." ) if cov.input_shape != self._input_shape: @@ -196,7 +197,7 @@ def mean(self) -> functions.Function: return self._mean @property - def cov(self) -> kernels.Kernel: + def cov(self) -> covfuncs.CovarianceFunction: r"""Covariance function :math:`k(x_0, x_1)` of the random process. .. math:: @@ -222,7 +223,7 @@ def cov(self) -> kernels.Kernel: def var(self, args: InputType) -> OutputType: """Variance function. - Returns the variance function which is the value of the covariance or kernel + Returns the variance function which is the value of the covariance function evaluated elementwise at ``args`` for each output dimension separately. Parameters diff --git a/src/probnum/randprocs/covfuncs/__init__.py b/src/probnum/randprocs/covfuncs/__init__.py new file mode 100644 index 000000000..38a067594 --- /dev/null +++ b/src/probnum/randprocs/covfuncs/__init__.py @@ -0,0 +1,43 @@ +"""Covariance functions. + +Covariance functions describe the spatial or temporal variation of a random process. +If evaluated at two sets of points, a covariance function computes the covariance of the +values of the random process at these locations. + +Covariance functions support basic algebraic operations, including scaling, addition +and multiplication. +""" + +from ._covariance_function import CovarianceFunction, IsotropicMixin +from ._exponentiated_quadratic import ExpQuad +from ._linear import Linear +from ._matern import Matern +from ._polynomial import Polynomial +from ._product_matern import ProductMatern +from ._rational_quadratic import RatQuad +from ._white_noise import WhiteNoise + +# Public classes and functions. Order is reflected in documentation. +__all__ = [ + "CovarianceFunction", + "IsotropicMixin", + "WhiteNoise", + "Linear", + "Polynomial", + "ExpQuad", + "RatQuad", + "Matern", + "ProductMatern", +] + +# Set correct module paths. Corrects links and module paths in documentation. +CovarianceFunction.__module__ = "probnum.randprocs.covfuncs" +IsotropicMixin.__module__ = "probnum.randprocs.covfuncs" + +WhiteNoise.__module__ = "probnum.randprocs.covfuncs" +Linear.__module__ = "probnum.randprocs.covfuncs" +Polynomial.__module__ = "probnum.randprocs.covfuncs" +ExpQuad.__module__ = "probnum.randprocs.covfuncs" +RatQuad.__module__ = "probnum.randprocs.covfuncs" +Matern.__module__ = "probnum.randprocs.covfuncs" +ProductMatern.__module__ = "probnum.randprocs.covfuncs" diff --git a/src/probnum/randprocs/covfuncs/_arithmetic.py b/src/probnum/randprocs/covfuncs/_arithmetic.py new file mode 100644 index 000000000..01a06e6fb --- /dev/null +++ b/src/probnum/randprocs/covfuncs/_arithmetic.py @@ -0,0 +1,17 @@ +"""Covariance function arithmetic.""" +from ._arithmetic_fallbacks import SumCovarianceFunction, _mul_fallback +from ._covariance_function import BinaryOperandType, CovarianceFunction + + +# pylint: disable=missing-param-doc +def add(op1: BinaryOperandType, op2: BinaryOperandType) -> CovarianceFunction: + """Covariance function summation.""" + return SumCovarianceFunction(op1, op2) + + +def mul(op1: BinaryOperandType, op2: BinaryOperandType) -> CovarianceFunction: + """Covariance function multiplication.""" + return _mul_fallback(op1, op2) + + +# pylint: enable=missing-param-doc diff --git a/src/probnum/randprocs/kernels/_arithmetic_fallbacks.py b/src/probnum/randprocs/covfuncs/_arithmetic_fallbacks.py similarity index 61% rename from src/probnum/randprocs/kernels/_arithmetic_fallbacks.py rename to src/probnum/randprocs/covfuncs/_arithmetic_fallbacks.py index c6db31dae..e1e03c225 100644 --- a/src/probnum/randprocs/kernels/_arithmetic_fallbacks.py +++ b/src/probnum/randprocs/covfuncs/_arithmetic_fallbacks.py @@ -1,4 +1,4 @@ -"""Fallback-implementations of Kernel arithmetic.""" +"""Fallback-implementations of covariance function arithmetic.""" from __future__ import annotations @@ -11,17 +11,17 @@ from probnum import linops, utils from probnum.typing import NotImplementedType, ScalarLike -from ._kernel import BinaryOperandType, Kernel +from ._covariance_function import BinaryOperandType, CovarianceFunction ######################################################################################## # Generic Linear Operator Arithmetic (Fallbacks) ######################################################################################## -class ScaledKernel(Kernel): - r"""Kernel scaled with a (positive) scalar. +class ScaledCovarianceFunction(CovarianceFunction): + r"""Covariance function scaled with a (positive) scalar. - Define a new kernel + Define a new covariance function .. math :: k(x_0, x_1) = o k'(x_0, x_1) @@ -30,59 +30,58 @@ class ScaledKernel(Kernel): Parameters ---------- - kernel - Kernel. + covfunc + Covariance function. scalar Scalar to multiply with. """ - def __init__(self, kernel: Kernel, scalar: ScalarLike): - - if not isinstance(kernel, Kernel): - raise TypeError("`kernel` must be a `Kernel`") + def __init__(self, covfunc: CovarianceFunction, scalar: ScalarLike): + if not isinstance(covfunc, CovarianceFunction): + raise TypeError("`covfunc` must be a `CovarianceFunction`") if np.ndim(scalar) != 0: raise TypeError("`scalar` must be a scalar.") - self._kernel = kernel + self._covfunc = covfunc self._scalar = utils.as_numpy_scalar(scalar) super().__init__( - input_shape=kernel.input_shape, - output_shape_0=kernel.output_shape_0, - output_shape_1=kernel.output_shape_1, + input_shape=covfunc.input_shape, + output_shape_0=covfunc.output_shape_0, + output_shape_1=covfunc.output_shape_1, ) def _evaluate(self, x0: np.ndarray, x1: Optional[np.ndarray] = None) -> np.ndarray: - return self._scalar * self._kernel(x0, x1) + return self._scalar * self._covfunc(x0, x1) def _evaluate_linop( self, x0: np.ndarray, x1: Optional[np.ndarray] ) -> linops.LinearOperator: - return self._scalar * self._kernel.linop(x0, x1) + return self._scalar * self._covfunc.linop(x0, x1) def __repr__(self) -> str: - return f"{self._scalar} * {self._kernel}" + return f"{self._scalar} * {self._covfunc}" -class SumKernel(Kernel): - r"""Sum of kernels. +class SumCovarianceFunction(CovarianceFunction): + r"""Sum of covariance functions. - Define a new kernel + Define a new covariance function .. math :: k(x_0, x_1) = \sum_{i=1}^m k_i(x_0, x_1) - from a set of kernels :math:`k_i` via summation. + from a set of covariance functions :math:`k_i` via summation. Parameters ---------- summands - Kernels to sum together. Must have the same ``input_shape`` and + Covariance functions to sum together. Must have the same ``input_shape`` and ``output_shape``. """ - def __init__(self, *summands: Kernel): + def __init__(self, *summands: CovarianceFunction): if not all( (summand.input_shape == summands[0].input_shape) @@ -92,7 +91,7 @@ def __init__(self, *summands: Kernel): ): raise ValueError("All summands must have the same in- and output shape.") - self._summands = SumKernel._expand_sum_kernels(*summands) + self._summands = SumCovarianceFunction._expand_sum_covfuncs(*summands) super().__init__( input_shape=summands[0].input_shape, @@ -113,17 +112,19 @@ def _evaluate_linop( ) def __repr__(self): - res = "SumKernel [\n\t" + res = "SumCovarianceFunction [\n\t" res += ",\n\t".join(repr(summand) for summand in self._summands) res += "\n]" return res @staticmethod - def _expand_sum_kernels(*summands: Kernel) -> Tuple[Kernel, ...]: + def _expand_sum_covfuncs( + *summands: CovarianceFunction, + ) -> Tuple[CovarianceFunction, ...]: expanded_summands = [] for summand in summands: - if isinstance(summand, SumKernel): + if isinstance(summand, SumCovarianceFunction): # pylint: disable="protected-access" expanded_summands.extend(summand._summands) else: @@ -132,24 +133,24 @@ def _expand_sum_kernels(*summands: Kernel) -> Tuple[Kernel, ...]: return tuple(expanded_summands) -class ProductKernel(Kernel): - r"""(Element-wise) Product of kernels. +class ProductCovarianceFunction(CovarianceFunction): + r"""(Element-wise) Product of covariance functions. - Define a new kernel + Define a new covariance function .. math :: k(x_0, x_1) = \prod_{i=1}^m k_i(x_0, x_1) - from a set of kernels :math:`k_i` via multiplication. + from a set of covariance functions :math:`k_i` via multiplication. Parameters ---------- factors - Kernels to multiply together. Must have the same ``input_shape`` and - ``output_shape``. + Covariance functions to multiply together. Must have the same ``input_shape`` + and ``output_shape``. """ - def __init__(self, *factors: Kernel): + def __init__(self, *factors: CovarianceFunction): if not all( (factor.input_shape == factors[0].input_shape) @@ -159,7 +160,7 @@ def __init__(self, *factors: Kernel): ): raise ValueError("All factors must have the same in- and output shape.") - self._factors = ProductKernel._expand_prod_kernels(*factors) + self._factors = ProductCovarianceFunction._expand_prod_covfuncs(*factors) super().__init__( input_shape=factors[0].input_shape, @@ -173,17 +174,19 @@ def _evaluate(self, x0: np.ndarray, x1: Optional[np.ndarray]) -> np.ndarray: ) def __repr__(self): - res = "ProductKernel [\n\t" + res = "ProductCovarianceFunction [\n\t" res += ",\n\t".join(repr(factor) for factor in self._factors) res += "\n]" return res @staticmethod - def _expand_prod_kernels(*factors: Kernel) -> Tuple[Kernel, ...]: + def _expand_prod_covfuncs( + *factors: CovarianceFunction, + ) -> Tuple[CovarianceFunction, ...]: expanded_factors = [] for factor in factors: - if isinstance(factor, ProductKernel): + if isinstance(factor, ProductCovarianceFunction): # pylint: disable="protected-access" expanded_factors.extend(factor._factors) else: @@ -194,15 +197,15 @@ def _expand_prod_kernels(*factors: Kernel) -> Tuple[Kernel, ...]: def _mul_fallback( op1: BinaryOperandType, op2: BinaryOperandType -) -> Union[Kernel, NotImplementedType]: +) -> Union[CovarianceFunction, NotImplementedType]: res = NotImplemented - if isinstance(op1, Kernel): - if isinstance(op2, Kernel): - res = ProductKernel(op1, op2) + if isinstance(op1, CovarianceFunction): + if isinstance(op2, CovarianceFunction): + res = ProductCovarianceFunction(op1, op2) elif np.ndim(op2) == 0: - res = ScaledKernel(kernel=op1, scalar=op2) - elif isinstance(op2, Kernel): + res = ScaledCovarianceFunction(op1, scalar=op2) + elif isinstance(op2, CovarianceFunction): if np.ndim(op1) == 0: - res = ScaledKernel(kernel=op2, scalar=op1) + res = ScaledCovarianceFunction(op2, scalar=op1) return res diff --git a/src/probnum/randprocs/kernels/_kernel.py b/src/probnum/randprocs/covfuncs/_covariance_function.py similarity index 93% rename from src/probnum/randprocs/kernels/_kernel.py rename to src/probnum/randprocs/covfuncs/_covariance_function.py index 7ebf8db6b..aec176d4f 100644 --- a/src/probnum/randprocs/kernels/_kernel.py +++ b/src/probnum/randprocs/covfuncs/_covariance_function.py @@ -12,10 +12,10 @@ from probnum import linops, utils as _pn_utils from probnum.typing import ArrayLike, ScalarLike, ShapeLike, ShapeType -BinaryOperandType = Union["Kernel", ScalarLike] +BinaryOperandType = Union["CovarianceFunction", ScalarLike] -class Kernel(abc.ABC): +class CovarianceFunction(abc.ABC): r"""(Cross-)covariance function. A cross-covariance function @@ -45,11 +45,13 @@ class Kernel(abc.ABC): input_shape_0 :attr:`~probnum.randprocs.RandomProcess.input_shape` of the :class:`~probnum.\ randprocs.RandomProcess` :math:`f_0`. - This defines the shape of the :class:`Kernel`\ 's first input :math:`x_0`. + This defines the shape of the :class:`CovarianceFunction`\ 's first input + :math:`x_0`. input_shape_1 :attr:`~probnum.randprocs.RandomProcess.input_shape` of the :class:`~probnum.\ randprocs.RandomProcess` :math:`f_1`. - This defines the shape of the :class:`Kernel`\ 's second input :math:`x_1`. + This defines the shape of the :class:`CovarianceFunction`\ 's second input + :math:`x_1`. input_shape Convenience argument, which can be used to set ``input_shape_0 == input_shape_1 == input_shape``. @@ -65,7 +67,7 @@ class Kernel(abc.ABC): Examples -------- - >>> from probnum.randprocs.kernels import Linear + >>> from probnum.randprocs.covfuncs import Linear >>> D = 3 >>> k = Linear(input_shape=D) >>> k.input_shape_0 @@ -114,10 +116,10 @@ class Kernel(abc.ABC): >>> k(xs, None) # x1 = None is an efficient way to set x1 == x0 array([0.04132231, 0.41322314, 1.23140496, 2.49586777]) - :class:`Kernel`\ s support basic arithmetic operations. For example, we can model - independent measurement noise as follows: + :class:`CovarianceFunction`\ s support basic arithmetic operations. For example, we + can model independent measurement noise as follows: - >>> from probnum.randprocs.kernels import WhiteNoise + >>> from probnum.randprocs.covfuncs import WhiteNoise >>> k_noise = k + 0.1 * WhiteNoise(input_shape=D) >>> k_noise.matrix(xs) array([[0.14132231, 0.11570248, 0.19008264, 0.26446281], @@ -161,7 +163,7 @@ def input_shape_0(self) -> ShapeType: r""":attr:`~probnum.randprocs.RandomProcess.input_shape` of the :class:`~probnum.randprocs.RandomProcess` :math:`f_0`. This defines the shape of a single, i.e. non-batched, first argument :math:`x_0` - of the :class:`Kernel`.""" + of the :class:`CovarianceFunction`.""" return self._input_shape_0 @property @@ -179,7 +181,7 @@ def input_shape_1(self) -> ShapeType: r""":attr:`~probnum.randprocs.RandomProcess.input_shape` of the :class:`~probnum.randprocs.RandomProcess` :math:`f_1`. This defines the shape of a single, i.e. non-batched, second argument - :math:`x_1` of the :class:`Kernel`.""" + :math:`x_1` of the :class:`CovarianceFunction`.""" return self._input_shape_1 @property @@ -200,10 +202,12 @@ def input_shape(self) -> ShapeType: Raises ------ ValueError - If the input shapes of the :class:`Kernel` are not equal. + If the input shapes of the :class:`CovarianceFunction` are not equal. """ if self.input_shape_0 != self.input_shape_1: - raise ValueError("The input shapes of the `Kernel` are not equal.") + raise ValueError( + "The input shapes of the `CovarianceFunction` are not equal." + ) return self.input_shape_0 @@ -280,10 +284,10 @@ def __call__( ---------- x0 *shape=* ``batch_shape_0 +`` :attr:`input_shape_0` -- (Batch of) input(s) - for the first argument of the :class:`Kernel`. + for the first argument of the :class:`CovarianceFunction`. x1 *shape=* ``batch_shape_1 +`` :attr:`input_shape_1` -- (Batch of) input(s) - for the second argument of the :class:`Kernel`. + for the second argument of the :class:`CovarianceFunction`. Can also be set to ``None``, in which case the function will behave as if ``x1 = x0`` (but it is implemented more efficiently). @@ -322,7 +326,7 @@ def __call__( Examples -------- - See documentation of class :class:`Kernel`. + See documentation of class :class:`CovarianceFunction`. """ x0 = np.asarray(x0) @@ -357,10 +361,10 @@ def matrix( ---------- x0 *shape=* ``batch_shape_0 +`` :attr:`input_shape_0` -- (Batch of) input(s) - for the first argument of the :class:`Kernel`. + for the first argument of the :class:`CovarianceFunction`. x1 *shape=* ``batch_shape_1 +`` :attr:`input_shape_1` -- (Batch of) input(s) - for the second argument of the :class:`Kernel`. + for the second argument of the :class:`CovarianceFunction`. Can also be set to :data:`None`, in which case the function will behave as if ``x1 == x0`` (potentially using a more efficient implementation for this particular case). @@ -427,10 +431,10 @@ def linop( ---------- x0 *shape=* ``batch_shape_0 +`` :attr:`input_shape_0` -- (Batch of) input(s) - for the first argument of the :class:`Kernel`. + for the first argument of the :class:`CovarianceFunction`. x1 *shape=* ``batch_shape_1 +`` :attr:`input_shape_1` -- (Batch of) input(s) - for the second argument of the :class:`Kernel`. + for the second argument of the :class:`CovarianceFunction`. Can also be set to :data:`None`, in which case the function will behave as if ``x1 == x0`` (potentially using a more efficient implementation for this particular case). @@ -650,28 +654,28 @@ def _euclidean_inner_products( __array_ufunc__ = None """ This prevents numpy from calling elementwise arithmetic operations instead of - the arithmetic operations defined by `Kernel`. + the arithmetic operations defined by `CovarianceFunction`. """ - def __add__(self, other: BinaryOperandType) -> Kernel: + def __add__(self, other: BinaryOperandType) -> CovarianceFunction: # pylint: disable=import-outside-toplevel,cyclic-import from ._arithmetic import add return add(self, other) - def __radd__(self, other: BinaryOperandType) -> Kernel: + def __radd__(self, other: BinaryOperandType) -> CovarianceFunction: # pylint: disable=import-outside-toplevel,cyclic-import from ._arithmetic import add return add(other, self) - def __mul__(self, other: BinaryOperandType) -> Kernel: + def __mul__(self, other: BinaryOperandType) -> CovarianceFunction: # pylint: disable=import-outside-toplevel,cyclic-import from ._arithmetic import mul return mul(self, other) - def __rmul__(self, other: BinaryOperandType) -> Kernel: + def __rmul__(self, other: BinaryOperandType) -> CovarianceFunction: # pylint: disable=import-outside-toplevel,cyclic-import from ._arithmetic import mul diff --git a/src/probnum/randprocs/kernels/_exponentiated_quadratic.py b/src/probnum/randprocs/covfuncs/_exponentiated_quadratic.py similarity index 77% rename from src/probnum/randprocs/kernels/_exponentiated_quadratic.py rename to src/probnum/randprocs/covfuncs/_exponentiated_quadratic.py index a42c0eb55..79efbe24a 100644 --- a/src/probnum/randprocs/kernels/_exponentiated_quadratic.py +++ b/src/probnum/randprocs/covfuncs/_exponentiated_quadratic.py @@ -1,4 +1,4 @@ -"""Exponentiated quadratic kernel.""" +"""Exponentiated quadratic covariance function.""" import functools from typing import Optional @@ -7,37 +7,37 @@ from probnum.typing import ArrayLike, ShapeLike -from ._kernel import IsotropicMixin, Kernel +from ._covariance_function import CovarianceFunction, IsotropicMixin -class ExpQuad(Kernel, IsotropicMixin): - r"""Exponentiated quadratic / RBF kernel. +class ExpQuad(CovarianceFunction, IsotropicMixin): + r"""Exponentiated quadratic covariance function. Covariance function defined by .. math :: k(x_0, x_1) = \exp \left( -\frac{\lVert x_0 - x_1 \rVert_2^2}{2 l^2} \right). - This kernel is also known as the squared - exponential or radial basis function kernel. + This covariance function is also known as the squared exponential (SE) or radial + basis function (RBF) kernel. Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. lengthscale - Lengthscale :math:`l` of the kernel. Describes the input scale on which the - process varies. + Lengthscale :math:`l` of the covariance function. Describes the input scale on + which the process varies. See Also -------- - RatQuad : Rational quadratic kernel. - Matern : Matern kernel. + RatQuad : Rational quadratic covariance function. + Matern : Matern covariance function. Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import ExpQuad + >>> from probnum.randprocs.covfuncs import ExpQuad >>> K = ExpQuad((), lengthscales=0.1) >>> xs = np.linspace(0, 1, 3) >>> K.matrix(xs) diff --git a/src/probnum/randprocs/kernels/_linear.py b/src/probnum/randprocs/covfuncs/_linear.py similarity index 81% rename from src/probnum/randprocs/kernels/_linear.py rename to src/probnum/randprocs/covfuncs/_linear.py index 4cbd82993..ca177a42c 100644 --- a/src/probnum/randprocs/kernels/_linear.py +++ b/src/probnum/randprocs/covfuncs/_linear.py @@ -9,11 +9,11 @@ from probnum.typing import ScalarLike, ShapeLike import probnum.utils as _utils -from ._kernel import Kernel +from ._covariance_function import CovarianceFunction -class Linear(Kernel): - r"""Linear kernel. +class Linear(CovarianceFunction): + r"""Linear covariance function. Linear covariance function defined by @@ -23,7 +23,7 @@ class Linear(Kernel): Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. constant Constant offset :math:`c`. @@ -34,7 +34,7 @@ class Linear(Kernel): Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import Linear + >>> from probnum.randprocs.covfuncs import Linear >>> K = Linear(input_shape=2) >>> xs = np.array([[1, 2], [2, 3]]) >>> K.matrix(xs) diff --git a/src/probnum/randprocs/kernels/_matern.py b/src/probnum/randprocs/covfuncs/_matern.py similarity index 82% rename from src/probnum/randprocs/kernels/_matern.py rename to src/probnum/randprocs/covfuncs/_matern.py index 186a15848..eb5ac234a 100644 --- a/src/probnum/randprocs/kernels/_matern.py +++ b/src/probnum/randprocs/covfuncs/_matern.py @@ -1,4 +1,4 @@ -"""Matérn kernel.""" +"""Matérn covariance function.""" import fractions import functools @@ -10,11 +10,11 @@ from probnum.typing import ArrayLike, ScalarLike, ScalarType, ShapeLike import probnum.utils as _utils -from ._kernel import IsotropicMixin, Kernel +from ._covariance_function import CovarianceFunction, IsotropicMixin -class Matern(Kernel, IsotropicMixin): - r"""Matérn kernel. +class Matern(CovarianceFunction, IsotropicMixin): + r"""Matérn covariance function. Covariance function defined by @@ -36,15 +36,16 @@ class Matern(Kernel, IsotropicMixin): := \sum_{i = 1}^d \frac{(x_{0,i} - x_{1,i})^2}{l_i}. - The Matérn kernel generalizes the :class:`~probnum.randprocs.kernels.ExpQuad` kernel - via its additional parameter :math:`\nu` controlling the smoothness of the functions - in the associated RKHS. For :math:`\nu \rightarrow \infty`, the Matérn kernel - converges to the :class:`~probnum.randprocs.kernels.ExpQuad` kernel. A Gaussian - process with Matérn covariance function is :math:`\lceil \nu \rceil - 1` times - differentiable. + The Matérn covariance function generalizes the :class:`~probnum.randprocs.covfuncs.\ + ExpQuad` covariance function via its additional parameter :math:`\nu` controlling + the smoothness of the functions in the associated RKHS. + For :math:`\nu \rightarrow \infty`, the Matérn covariance function converges to the + :class:`~probnum.randprocs.covfuncs.ExpQuad` covariance function. + A Gaussian process with Matérn covariance function is :math:`\lceil \nu \rceil - 1` + times differentiable. If :math:`\nu` is a half-integer, i.e. :math:`\nu = p + \frac{1}{2}` for some - nonnegative integer :math:`p`, then the expression for the kernel function + nonnegative integer :math:`p`, then the expression for the covariance function simplifies to a product of an exponential and a polynomial .. math:: @@ -62,23 +63,25 @@ class Matern(Kernel, IsotropicMixin): Parameters ---------- input_shape - Shape of the kernel's inputs. + Shape of the covariance function's inputs. nu Hyperparameter :math:`\nu` controlling differentiability. lengthscales - Lengthscales :math:`l_i` along the different input dimensions of the kernel. + Lengthscales :math:`l_i` along the different input dimensions of the covariance + function. Describes the input scales on which the process varies. - The lengthscales will be broadcast to the input shape of the kernel. + The lengthscales will be broadcast to the input shape of the covariance + function. See Also -------- - ExpQuad : Exponentiated Quadratic / RBF kernel. - ProductMatern : Product Matern kernel. + ExpQuad : Exponentiated Quadratic covariance function. + ProductMatern : Tensor product of 1D Matérn covariance functions. Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import Matern + >>> from probnum.randprocs.covfuncs import Matern >>> K = Matern((), nu=2.5, lengthscales=0.1) >>> xs = np.linspace(0, 1, 3) >>> K.matrix(xs) @@ -126,9 +129,9 @@ def nu(self) -> ScalarType: @functools.cached_property def p(self) -> Optional[int]: - r"""Degree :math:`p` of the polynomial part of a Matérn kernel with half-integer - smoothness parameter :math:`\nu = p + \frac{1}{2}`. If :math:`\nu` is not a - half-integer, this is set to :data:`None`. + r"""Degree :math:`p` of the polynomial part of a Matérn covariance function with + half-integer smoothness parameter :math:`\nu = p + \frac{1}{2}`. If :math:`\nu` + is not a half-integer, this is set to :data:`None`. Sample paths of a Gaussian process with this covariance function are :math:`p`-times continuously differentiable.""" @@ -189,8 +192,8 @@ def _evaluate(self, x0: np.ndarray, x1: Optional[np.ndarray]) -> np.ndarray: @functools.lru_cache(maxsize=None) def half_integer_coefficients(p: int) -> Tuple[fractions.Fraction]: r"""Computes the rational coefficients :math:`c_i` of the polynomial part of a - Matérn kernel with half-integer smoothness parameter :math:`\nu = p + - \frac{1}{2}`. + Matérn covariance function with half-integer smoothness parameter :math:`\nu = \ + p + \frac{1}{2}`. We leverage the recursion diff --git a/src/probnum/randprocs/kernels/_polynomial.py b/src/probnum/randprocs/covfuncs/_polynomial.py similarity index 80% rename from src/probnum/randprocs/kernels/_polynomial.py rename to src/probnum/randprocs/covfuncs/_polynomial.py index 6828dc583..411c91862 100644 --- a/src/probnum/randprocs/kernels/_polynomial.py +++ b/src/probnum/randprocs/covfuncs/_polynomial.py @@ -1,4 +1,4 @@ -"""Polynomial kernel.""" +"""Polynomial covariance function.""" from typing import Optional @@ -7,11 +7,11 @@ from probnum.typing import IntLike, ScalarLike, ShapeLike import probnum.utils as _utils -from ._kernel import Kernel +from ._covariance_function import CovarianceFunction -class Polynomial(Kernel): - r"""Polynomial kernel. +class Polynomial(CovarianceFunction): + r"""Polynomial covariance function. Covariance function defined by @@ -21,7 +21,7 @@ class Polynomial(Kernel): Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. constant Constant offset :math:`c`. exponent @@ -34,7 +34,7 @@ class Polynomial(Kernel): Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import Polynomial + >>> from probnum.randprocs.covfuncs import Polynomial >>> K = Polynomial(input_shape=2, constant=1.0, exponent=3) >>> xs = np.array([[1, -1], [-1, 0]]) >>> K.matrix(xs) diff --git a/src/probnum/randprocs/kernels/_product_matern.py b/src/probnum/randprocs/covfuncs/_product_matern.py similarity index 71% rename from src/probnum/randprocs/kernels/_product_matern.py rename to src/probnum/randprocs/covfuncs/_product_matern.py index 74fcc9cf6..d36f83e76 100644 --- a/src/probnum/randprocs/kernels/_product_matern.py +++ b/src/probnum/randprocs/covfuncs/_product_matern.py @@ -1,4 +1,4 @@ -"""Product Matern kernel.""" +"""Tensor product of 1D Matérn covariance functions.""" from typing import Optional, Union @@ -7,39 +7,40 @@ from probnum import utils as _utils from probnum.typing import ScalarLike, ShapeLike -from ._kernel import Kernel +from ._covariance_function import CovarianceFunction from ._matern import Matern -class ProductMatern(Kernel): - r"""Product Matern kernel. +class ProductMatern(CovarianceFunction): + r"""Tensor product of one-dimensional Matérn covariance functions. - Covariance function defined as a product of one-dimensional Matern - kernels: :math:`k(x_0, x_1) = \prod_{i=1}^d k_i(x_{0,i}, x_{1,i})`, + Covariance function defined as a product of one-dimensional Matérn covariance + functions: :math:`k(x_0, x_1) = \prod_{i=1}^d k_i(x_{0,i}, x_{1,i})`, where :math:`x_0 = (x_{0,i}, \ldots, x_{0,d})` and :math:`x_0 = (x_{0,i}, \ldots, - x_{0,d})` and :math:`k_i` are one-dimensional Matern kernels. + x_{0,d})` and :math:`k_i` are one-dimensional Matérn covariance functions. Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. lengthscales - Lengthscales of the one-dimensional Matern kernels. Describes the input scale on - which the process varies. If a scalar, the same lengthscale is used in each - dimension. + Lengthscales of the one-dimensional Matérn covariance functions. Describes the + input scale on which the process varies. If a scalar, the same lengthscale is + used in each dimension. nus - Hyperparameters controlling differentiability of the one-dimensional Matern - kernels. If a scalar, the same smoothness is used in each dimension. + Hyperparameters controlling differentiability of the one-dimensional Matérn + covariance functions. If a scalar, the same smoothness is used in each + dimension. See Also -------- - Matern : Stationary Matern kernel. - ExpQuad : Exponentiated Quadratic / RBF kernel. + Matern : Stationary Matérn covariance function. + ExpQuad : Exponentiated Quadratic covariance function. Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import ProductMatern + >>> from probnum.randprocs.covfuncs import ProductMatern >>> lengthscales = np.array([0.1, 1.2]) >>> nus = np.array([0.5, 3.5]) >>> K = ProductMatern(input_shape=(2,), lengthscales=lengthscales, nus=nus) @@ -52,7 +53,8 @@ class ProductMatern(Kernel): Raises ------ ValueError - If kernel input is scalar, but ``lengthscales`` or ``nus`` are not. + If covariance function input is scalar, but ``lengthscales`` or ``nus`` are + not. """ def __init__( @@ -109,12 +111,12 @@ def _evaluate(self, x0: np.ndarray, x1: Optional[np.ndarray] = None) -> np.ndarr # product case (input_dim,) = self.input_shape - kernel_eval = 1.0 + k_x0_x1 = 1.0 if x1 is None: for dim in range(input_dim): - kernel_eval *= self.univariate_materns[dim](x0[..., dim], None) + k_x0_x1 *= self.univariate_materns[dim](x0[..., dim], None) else: for dim in range(input_dim): - kernel_eval *= self.univariate_materns[dim](x0[..., dim], x1[..., dim]) + k_x0_x1 *= self.univariate_materns[dim](x0[..., dim], x1[..., dim]) - return kernel_eval + return k_x0_x1 diff --git a/src/probnum/randprocs/kernels/_rational_quadratic.py b/src/probnum/randprocs/covfuncs/_rational_quadratic.py similarity index 76% rename from src/probnum/randprocs/kernels/_rational_quadratic.py rename to src/probnum/randprocs/covfuncs/_rational_quadratic.py index ad970a0b1..b0cf025fe 100644 --- a/src/probnum/randprocs/kernels/_rational_quadratic.py +++ b/src/probnum/randprocs/covfuncs/_rational_quadratic.py @@ -1,4 +1,4 @@ -"""Rational quadratic kernel.""" +"""Rational quadratic covariance function.""" from typing import Optional @@ -7,11 +7,11 @@ from probnum.typing import ScalarLike, ShapeLike import probnum.utils as _utils -from ._kernel import IsotropicMixin, Kernel +from ._covariance_function import CovarianceFunction, IsotropicMixin -class RatQuad(Kernel, IsotropicMixin): - r"""Rational quadratic kernel. +class RatQuad(CovarianceFunction, IsotropicMixin): + r"""Rational quadratic covariance function. Covariance function defined by @@ -26,28 +26,28 @@ class RatQuad(Kernel, IsotropicMixin): \end{equation} where :math:`\alpha > 0`. For :math:`\alpha \rightarrow \infty` the rational - quadratic kernel converges to the :class:`~probnum.randprocs.kernels.ExpQuad` - kernel. + quadratic covariance function converges to the :class:`~probnum.randprocs.covfuncs.\ + ExpQuad` covariance function. Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. lengthscale - Lengthscale :math:`l` of the kernel. Describes the input scale on which the - process varies. + Lengthscale :math:`l` of the covariance function. Describes the input scale on + which the process varies. alpha Scale mixture :math:`\alpha`. Positive constant determining the weighting between different lengthscales. See Also -------- - ExpQuad : Exponentiated Quadratic / RBF kernel. + ExpQuad : Exponentiated Quadratic / RBF covariance function. Examples -------- >>> import numpy as np - >>> from probnum.randprocs.kernels import RatQuad + >>> from probnum.randprocs.covfuncs import RatQuad >>> K = RatQuad(input_shape=1, lengthscale=0.1, alpha=3) >>> xs = np.linspace(0, 1, 3)[:, None] >>> K(xs[:, None, :], xs[None, :, :]) diff --git a/src/probnum/randprocs/kernels/_white_noise.py b/src/probnum/randprocs/covfuncs/_white_noise.py similarity index 77% rename from src/probnum/randprocs/kernels/_white_noise.py rename to src/probnum/randprocs/covfuncs/_white_noise.py index f66659334..ae5377ccc 100644 --- a/src/probnum/randprocs/kernels/_white_noise.py +++ b/src/probnum/randprocs/covfuncs/_white_noise.py @@ -1,4 +1,4 @@ -"""White noise kernel.""" +"""White noise covariance function.""" from typing import Optional @@ -7,13 +7,13 @@ from probnum import utils as _utils from probnum.typing import ScalarLike, ShapeLike -from ._kernel import Kernel +from ._covariance_function import CovarianceFunction -class WhiteNoise(Kernel): - r"""White noise kernel. +class WhiteNoise(CovarianceFunction): + r"""White noise covariance function. - Kernel representing independent and identically distributed white noise + Covariance function representing independent and identically distributed white noise .. math :: k(x_0, x_1) = \sigma^2 \delta(x_0, x_1). @@ -21,7 +21,7 @@ class WhiteNoise(Kernel): Parameters ---------- input_shape - Shape of the kernel's input. + Shape of the covariance function's input. sigma_sq Noise level :math:`\sigma^2 \geq 0`. """ diff --git a/src/probnum/randprocs/kernels.py b/src/probnum/randprocs/kernels.py new file mode 100644 index 000000000..8ac9d2ca3 --- /dev/null +++ b/src/probnum/randprocs/kernels.py @@ -0,0 +1,10 @@ +"""This is an alias for the :mod:`probnum.randprocs.covfuncs` module. We mainly include +it and the :class:`Kernel` alias for backwards compatibility. + +.. deprecated:: 0.1.23 + The module is deprecated and should not be used in new code. + Use :mod:`probnum.randprocs.covfuncs` instead. +""" +from .covfuncs import * # pylint: disable=wildcard-import,unused-wildcard-import + +Kernel = CovarianceFunction diff --git a/src/probnum/randprocs/kernels/__init__.py b/src/probnum/randprocs/kernels/__init__.py deleted file mode 100644 index 526a0afe3..000000000 --- a/src/probnum/randprocs/kernels/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Kernels or covariance functions. - -Kernels describe the spatial or temporal variation of a random process. -If evaluated at two sets of points a kernel is defined as the covariance -of the values of the random process at these locations. - -Kernels support basic algebraic operations, including scaling, addition -and multiplication. -""" - -from ._exponentiated_quadratic import ExpQuad -from ._kernel import IsotropicMixin, Kernel -from ._linear import Linear -from ._matern import Matern -from ._polynomial import Polynomial -from ._product_matern import ProductMatern -from ._rational_quadratic import RatQuad -from ._white_noise import WhiteNoise - -# Public classes and functions. Order is reflected in documentation. -__all__ = [ - "Kernel", - "IsotropicMixin", - "WhiteNoise", - "Linear", - "Polynomial", - "ExpQuad", - "RatQuad", - "Matern", - "ProductMatern", -] - -# Set correct module paths. Corrects links and module paths in documentation. -Kernel.__module__ = "probnum.randprocs.kernels" -IsotropicMixin.__module__ = "probnum.randprocs.kernels" - -WhiteNoise.__module__ = "probnum.randprocs.kernels" -Linear.__module__ = "probnum.randprocs.kernels" -Polynomial.__module__ = "probnum.randprocs.kernels" -ExpQuad.__module__ = "probnum.randprocs.kernels" -RatQuad.__module__ = "probnum.randprocs.kernels" -Matern.__module__ = "probnum.randprocs.kernels" -ProductMatern.__module__ = "probnum.randprocs.kernels" diff --git a/src/probnum/randprocs/kernels/_arithmetic.py b/src/probnum/randprocs/kernels/_arithmetic.py deleted file mode 100644 index 51592d8ab..000000000 --- a/src/probnum/randprocs/kernels/_arithmetic.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Kernel arithmetic.""" -from ._arithmetic_fallbacks import SumKernel, _mul_fallback -from ._kernel import BinaryOperandType, Kernel - - -# pylint: disable=missing-param-doc -def add(op1: BinaryOperandType, op2: BinaryOperandType) -> Kernel: - """Kernel summation.""" - return SumKernel(op1, op2) - - -def mul(op1: BinaryOperandType, op2: BinaryOperandType) -> Kernel: - """Kernel multiplication.""" - return _mul_fallback(op1, op2) - - -# pylint: enable=missing-param-doc diff --git a/tests/test_randprocs/conftest.py b/tests/test_randprocs/conftest.py index d754c1c50..0fa3fd464 100644 --- a/tests/test_randprocs/conftest.py +++ b/tests/test_randprocs/conftest.py @@ -7,7 +7,7 @@ import pytest from probnum import functions, randprocs -from probnum.randprocs import kernels +from probnum.randprocs import covfuncs @pytest.fixture( @@ -64,15 +64,15 @@ def fixture_mean(request, input_dim: int) -> Callable: params=[ pytest.param(kerndef, id=kerndef[0].__name__) for kerndef in [ - (kernels.Polynomial, {"constant": 1.0, "exponent": 3}), - (kernels.ExpQuad, {"lengthscales": 1.5}), - (kernels.RatQuad, {"lengthscale": 0.5, "alpha": 2.0}), - (kernels.Matern, {"lengthscales": 0.5, "nu": 1.5}), + (covfuncs.Polynomial, {"constant": 1.0, "exponent": 3}), + (covfuncs.ExpQuad, {"lengthscales": 1.5}), + (covfuncs.RatQuad, {"lengthscale": 0.5, "alpha": 2.0}), + (covfuncs.Matern, {"lengthscales": 0.5, "nu": 1.5}), ] ], name="cov", ) -def fixture_cov(request, input_dim: int) -> kernels.Kernel: +def fixture_cov(request, input_dim: int) -> covfuncs.CovarianceFunction: """Covariance function.""" return request.param[0](**request.param[1], input_shape=(input_dim,)) @@ -85,7 +85,7 @@ def fixture_cov(request, input_dim: int) -> kernels.Kernel: "gp", randprocs.GaussianProcess( mean=functions.Zero(input_shape=(1,)), - cov=kernels.Matern(input_shape=(1,)), + cov=covfuncs.Matern(input_shape=(1,)), ), ), ] diff --git a/tests/test_randprocs/test_kernels/__init__.py b/tests/test_randprocs/test_covfuncs/__init__.py similarity index 100% rename from tests/test_randprocs/test_kernels/__init__.py rename to tests/test_randprocs/test_covfuncs/__init__.py diff --git a/tests/test_randprocs/test_kernels/conftest.py b/tests/test_randprocs/test_covfuncs/conftest.py similarity index 53% rename from tests/test_randprocs/test_kernels/conftest.py rename to tests/test_randprocs/test_covfuncs/conftest.py index dbd8656f7..308a4ae62 100644 --- a/tests/test_randprocs/test_kernels/conftest.py +++ b/tests/test_randprocs/test_covfuncs/conftest.py @@ -1,4 +1,4 @@ -"""Test fixtures for kernels.""" +"""Test fixtures for covariance functions.""" from typing import Callable, Optional @@ -18,7 +18,7 @@ def fixture_rng(request): return np.random.default_rng(seed=request.param) -# Kernel objects +# `CovarianceFunction` objects @pytest.fixture( params=[ pytest.param(input_shape, id=f"inshape{input_shape}") @@ -35,48 +35,50 @@ def fixture_input_shape(request) -> ShapeType: params=[ pytest.param(kerndef, id=kerndef[0].__name__) for kerndef in [ - (pn.randprocs.kernels.Linear, {"constant": 1.0}), - (pn.randprocs.kernels.WhiteNoise, {"sigma_sq": 1.0}), - (pn.randprocs.kernels.Polynomial, {"constant": 1.0, "exponent": 3}), - (pn.randprocs.kernels.ExpQuad, {"lengthscales": 1.5}), - (pn.randprocs.kernels.RatQuad, {"lengthscale": 0.5, "alpha": 2.0}), - (pn.randprocs.kernels.Matern, {"lengthscales": 0.5, "nu": 0.5}), - (pn.randprocs.kernels.Matern, {"lengthscales": 0.5, "nu": 1.5}), - (pn.randprocs.kernels.Matern, {"lengthscales": 1.5, "nu": 2.5}), - (pn.randprocs.kernels.Matern, {"lengthscales": 2.5, "nu": 7.0}), - (pn.randprocs.kernels.ProductMatern, {"lengthscales": 0.5, "nus": 0.5}), + (pn.randprocs.covfuncs.Linear, {"constant": 1.0}), + (pn.randprocs.covfuncs.WhiteNoise, {"sigma_sq": 1.0}), + (pn.randprocs.covfuncs.Polynomial, {"constant": 1.0, "exponent": 3}), + (pn.randprocs.covfuncs.ExpQuad, {"lengthscales": 1.5}), + (pn.randprocs.covfuncs.RatQuad, {"lengthscale": 0.5, "alpha": 2.0}), + (pn.randprocs.covfuncs.Matern, {"lengthscales": 0.5, "nu": 0.5}), + (pn.randprocs.covfuncs.Matern, {"lengthscales": 0.5, "nu": 1.5}), + (pn.randprocs.covfuncs.Matern, {"lengthscales": 1.5, "nu": 2.5}), + (pn.randprocs.covfuncs.Matern, {"lengthscales": 2.5, "nu": 7.0}), + (pn.randprocs.covfuncs.ProductMatern, {"lengthscales": 0.5, "nus": 0.5}), ] ], - name="kernel", + name="k", ) -def fixture_kernel(request, input_shape: ShapeType) -> pn.randprocs.kernels.Kernel: - """Kernel / covariance function.""" +def fixture_k( + request, input_shape: ShapeType +) -> pn.randprocs.covfuncs.CovarianceFunction: + """Covariance function.""" return request.param[0](input_shape=input_shape, **request.param[1]) -@pytest.fixture(name="kernel_call_naive") -def fixture_kernel_call_naive( - kernel: pn.randprocs.kernels.Kernel, +@pytest.fixture(name="k_call_naive") +def fixture_k_call_naive( + k: pn.randprocs.covfuncs.CovarianceFunction, ) -> Callable[[np.ndarray, Optional[np.ndarray]], np.ndarray]: - """Naive implementation of kernel broadcasting which applies the kernel function to - scalar arguments while looping over the first dimensions of the inputs explicitly. + """Naive implementation of covariance function vectorization which applies the + covariance function to scalar arguments while looping over the first dimensions of + the inputs explicitly. - Can be used as a reference implementation of `Kernel.__call__` vectorization. + Can be used as a reference implementation of `CovarianceFunction.__call__` + vectorization. """ - if kernel.input_ndim == 0: - kernel_vectorized = np.vectorize(kernel, signature="(),()->()") + if k.input_ndim == 0: + k_vectorized = np.vectorize(k, signature="(),()->()") else: - assert kernel.input_ndim == 1 + assert k.input_ndim == 1 - kernel_vectorized = np.vectorize(kernel, signature="(d),(d)->()") + k_vectorized = np.vectorize(k, signature="(d),(d)->()") - return lambda x0, x1: ( - kernel_vectorized(x0, x0) if x1 is None else kernel_vectorized(x0, x1) - ) + return lambda x0, x1: (k_vectorized(x0, x0) if x1 is None else k_vectorized(x0, x1)) -# Test data for `Kernel.matrix` +# Test data for `CovarianceFunction.matrix` @pytest.fixture( params=[ pytest.param(shape, id=f"x0{shape}") @@ -91,7 +93,7 @@ def fixture_kernel_call_naive( name="x0_batch_shape", ) def fixture_x0_batch_shape(request) -> ShapeType: - """Batch shape of the first argument of ``Kernel.matrix``.""" + """Batch shape of the first argument of ``CovarianceFunction.matrix``.""" return request.param @@ -109,8 +111,8 @@ def fixture_x0_batch_shape(request) -> ShapeType: name="x1_batch_shape", ) def fixture_x1_batch_shape(request) -> Optional[ShapeType]: - """Batch shape of the second argument of ``Kernel.matrix`` or ``None`` if the second - argument is ``None``.""" + """Batch shape of the second argument of ``CovarianceFunction.matrix`` or ``None`` + if the second argument is ``None``.""" return request.param diff --git a/tests/test_randprocs/test_covfuncs/test_arithmetic.py b/tests/test_randprocs/test_covfuncs/test_arithmetic.py new file mode 100644 index 000000000..13f6a2c93 --- /dev/null +++ b/tests/test_randprocs/test_covfuncs/test_arithmetic.py @@ -0,0 +1,39 @@ +"""Tests for covariance function arithmetic.""" +from probnum.randprocs import covfuncs +from probnum.randprocs.covfuncs._arithmetic_fallbacks import ( + ProductCovarianceFunction, + ScaledCovarianceFunction, + SumCovarianceFunction, +) + + +def test_scalar_mul(k: covfuncs.CovarianceFunction): + scalar = 3.14 + k_mul = k * scalar + k_rmul = scalar * k + assert isinstance(k_mul, ScaledCovarianceFunction) + assert isinstance(k_rmul, ScaledCovarianceFunction) + assert k_mul.input_shape == k.input_shape + assert k_rmul.input_shape == k.input_shape + assert k_mul.output_shape_0 == k.output_shape_0 + assert k_mul.output_shape_1 == k.output_shape_1 + assert k_rmul.output_shape_0 == k.output_shape_0 + assert k_rmul.output_shape_1 == k.output_shape_1 + + +def test_add(k: covfuncs.CovarianceFunction): + k_whitenoise = covfuncs.WhiteNoise(input_shape=k.input_shape) + k_sum = k + k_whitenoise + assert isinstance(k_sum, SumCovarianceFunction) + assert k_sum.input_shape == k.input_shape + assert k_sum.output_shape_0 == k.output_shape_0 + assert k_sum.output_shape_1 == k.output_shape_1 + + +def test_mul(k: covfuncs.CovarianceFunction): + k_poly = covfuncs.Polynomial(input_shape=k.input_shape) + k_prod = k * k_poly + assert isinstance(k_prod, ProductCovarianceFunction) + assert k_prod.input_shape == k.input_shape + assert k_prod.output_shape_0 == k.output_shape_0 + assert k_prod.output_shape_1 == k.output_shape_1 diff --git a/tests/test_randprocs/test_covfuncs/test_arithmetic_fallbacks.py b/tests/test_randprocs/test_covfuncs/test_arithmetic_fallbacks.py new file mode 100644 index 000000000..c2f36ce23 --- /dev/null +++ b/tests/test_randprocs/test_covfuncs/test_arithmetic_fallbacks.py @@ -0,0 +1,77 @@ +"""Tests for fall-back implementations of covariance function arithmetic.""" + +import numpy as np +import pytest +from pytest_cases import parametrize + +from probnum.randprocs import covfuncs +from probnum.randprocs.covfuncs._arithmetic_fallbacks import ( + ProductCovarianceFunction, + ScaledCovarianceFunction, + SumCovarianceFunction, +) +from probnum.typing import ScalarType + + +@parametrize("scalar", [1.0, 3, 1000.0]) +def test_scaled_covfunc_evaluation( + k: covfuncs.CovarianceFunction, scalar: ScalarType, x0: np.ndarray +): + k_scaled = ScaledCovarianceFunction(k, scalar=scalar) + np.testing.assert_allclose(k_scaled.matrix(x0), scalar * k.matrix(x0)) + + +def test_non_scalar_raises_error(): + with pytest.raises(TypeError): + ScaledCovarianceFunction( + covfuncs.WhiteNoise(input_shape=()), scalar=np.array([0, 1]) + ) + + +def test_non_covfunc_raises_error(): + with pytest.raises(TypeError): + ScaledCovarianceFunction(np.eye(5), scalar=1.0) + + +def test_sum_covfunc_evaluation(k: covfuncs.CovarianceFunction, x0: np.ndarray): + k_whitenoise = covfuncs.WhiteNoise(input_shape=k.input_shape) + k_sum = SumCovarianceFunction(k, k_whitenoise) + np.testing.assert_allclose(k_sum.matrix(x0), k.matrix(x0) + k_whitenoise.matrix(x0)) + + +def test_sum_covfunc_shape_mismatch_raises_error(): + with pytest.raises(ValueError): + SumCovarianceFunction( + covfuncs.WhiteNoise(input_shape=()), covfuncs.WhiteNoise(input_shape=(1,)) + ) + + +def test_sum_covfunc_contracts(): + input_shape = () + k = covfuncs.ExpQuad(input_shape=input_shape) + k_sum = SumCovarianceFunction(k, SumCovarianceFunction(k, k)) + assert all( + not isinstance(summand, SumCovarianceFunction) for summand in k_sum._summands + ) + + +def test_product_covfunc_evaluation(k: covfuncs.CovarianceFunction, x0: np.ndarray): + k_poly = covfuncs.Polynomial(input_shape=k.input_shape) + k_sum = ProductCovarianceFunction(k, k_poly) + np.testing.assert_allclose(k_sum.matrix(x0), k.matrix(x0) * k_poly.matrix(x0)) + + +def test_product_covfunc_shape_mismatch_raises_error(): + with pytest.raises(ValueError): + ProductCovarianceFunction( + covfuncs.WhiteNoise(input_shape=()), covfuncs.WhiteNoise(input_shape=(1,)) + ) + + +def test_product_covfunc_contracts(): + input_shape = () + k = covfuncs.ExpQuad(input_shape=input_shape) + k_prod = ProductCovarianceFunction(k, ProductCovarianceFunction(k, k)) + assert all( + not isinstance(factor, ProductCovarianceFunction) for factor in k_prod._factors + ) diff --git a/tests/test_randprocs/test_kernels/test_call.py b/tests/test_randprocs/test_covfuncs/test_call.py similarity index 71% rename from tests/test_randprocs/test_kernels/test_call.py rename to tests/test_randprocs/test_covfuncs/test_call.py index c0159af48..d430b6335 100644 --- a/tests/test_randprocs/test_kernels/test_call.py +++ b/tests/test_randprocs/test_covfuncs/test_call.py @@ -1,4 +1,4 @@ -"""Test cases for `Kernel.__call__`.""" +"""Test cases for `CovarianceFunction.__call__`.""" from typing import Callable, Optional, Tuple, Union @@ -82,28 +82,30 @@ def fixture_x1( @pytest.fixture(name="call_result") def fixture_call_result( - kernel: pn.randprocs.kernels.Kernel, x0: np.ndarray, x1: Optional[np.ndarray] + k: pn.randprocs.covfuncs.CovarianceFunction, + x0: np.ndarray, + x1: Optional[np.ndarray], ) -> Union[np.ndarray, np.floating]: - """Result of ``Kernel.__call__`` when given ``x0`` and ``x1``.""" + """Result of ``CovarianceFunction.__call__`` when given ``x0`` and ``x1``.""" - return kernel(x0, x1) + return k(x0, x1) @pytest.fixture(name="call_result_naive") def fixture_call_result_naive( - kernel_call_naive: Callable[[np.ndarray, Optional[np.ndarray]], np.ndarray], + k_call_naive: Callable[[np.ndarray, Optional[np.ndarray]], np.ndarray], x0: np.ndarray, x1: Optional[np.ndarray], ) -> Union[np.ndarray, np.floating]: - """Result of ``Kernel.__call__`` when applied to the entries of ``x0`` and ``x1`` in - a loop.""" + """Result of ``CovarianceFunction.__call__`` when applied to the entries of ``x0`` + and ``x1`` in a loop.""" - return kernel_call_naive(x0, x1) + return k_call_naive(x0, x1) def test_type(call_result: Union[np.ndarray, np.floating]): - """Test whether the type of the output of ``Kernel.__call__`` is a NumPy type, i.e. - an ``np.ndarray`` or a ``np.floating``.""" + """Test whether the type of the output of ``CovarianceFunction.__call__`` is a NumPy + type, i.e. an ``np.ndarray`` or a ``np.floating``.""" assert isinstance(call_result, (np.ndarray, np.floating)) @@ -112,8 +114,8 @@ def test_shape( call_result: Union[np.ndarray, np.floating], call_result_naive: Union[np.ndarray, np.floating], ): - """Test whether the shape of the output of ``Kernel.__call__`` matches the shape of - the naive reference implementation.""" + """Test whether the shape of the output of ``CovarianceFunction.__call__`` matches + the shape of the naive reference implementation.""" assert call_result.shape == call_result_naive.shape @@ -122,8 +124,8 @@ def test_values( call_result: Union[np.ndarray, np.floating], call_result_naive: Union[np.ndarray, np.floating], ): - """Test whether the entries of the output of ``Kernel.__call__`` match the entries - generated by the naive reference implementation.""" + """Test whether the entries of the output of ``CovarianceFunction.__call__`` match + the entries generated by the naive reference implementation.""" np.testing.assert_allclose( call_result, @@ -143,20 +145,22 @@ def test_values( (4, 25), ], ) -def test_wrong_input_dimension(kernel: pn.randprocs.kernels.Kernel, shape: ShapeType): +def test_wrong_input_dimension( + k: pn.randprocs.covfuncs.CovarianceFunction, shape: ShapeType +): """Test whether passing an input with the wrong input dimension raises an error.""" - if kernel.input_ndim > 0: - input_shape = shape + tuple(dim + 1 for dim in kernel.input_shape) + if k.input_ndim > 0: + input_shape = shape + tuple(dim + 1 for dim in k.input_shape) with pytest.raises(ValueError): - kernel(np.zeros(input_shape), None) + k(np.zeros(input_shape), None) with pytest.raises(ValueError): - kernel(np.ones(input_shape), np.zeros(shape + kernel.input_shape)) + k(np.ones(input_shape), np.zeros(shape + k.input_shape)) with pytest.raises(ValueError): - kernel(np.ones(shape + kernel.input_shape), np.zeros(input_shape)) + k(np.ones(shape + k.input_shape), np.zeros(input_shape)) @pytest.mark.parametrize( @@ -168,7 +172,7 @@ def test_wrong_input_dimension(kernel: pn.randprocs.kernels.Kernel, shape: Shape ], ) def test_broadcasting_error( - kernel: pn.randprocs.kernels.Kernel, + k: pn.randprocs.covfuncs.CovarianceFunction, x0_shape: np.ndarray, x1_shape: np.ndarray, ): @@ -176,7 +180,7 @@ def test_broadcasting_error( shape.""" with pytest.raises(ValueError): - kernel( - np.zeros(x0_shape + kernel.input_shape), - np.ones(x1_shape + kernel.input_shape), + k( + np.zeros(x0_shape + k.input_shape), + np.ones(x1_shape + k.input_shape), ) diff --git a/tests/test_randprocs/test_covfuncs/test_covariance_function.py b/tests/test_randprocs/test_covfuncs/test_covariance_function.py new file mode 100644 index 000000000..5f00b1954 --- /dev/null +++ b/tests/test_randprocs/test_covfuncs/test_covariance_function.py @@ -0,0 +1,13 @@ +"""Test cases for `CovarianceFunction`""" + +import numpy as np + +from probnum.randprocs import covfuncs + + +def test_input_ndim(k: covfuncs.CovarianceFunction): + assert k.input_ndim == np.empty(k.input_shape).ndim + + +def test_input_size(k: covfuncs.CovarianceFunction): + assert k.input_size == np.empty(k.input_shape).size diff --git a/tests/test_randprocs/test_kernels/test_exponentiated_quadratic.py b/tests/test_randprocs/test_covfuncs/test_exponentiated_quadratic.py similarity index 63% rename from tests/test_randprocs/test_kernels/test_exponentiated_quadratic.py rename to tests/test_randprocs/test_covfuncs/test_exponentiated_quadratic.py index 88f35c58f..fbf2a8f03 100644 --- a/tests/test_randprocs/test_kernels/test_exponentiated_quadratic.py +++ b/tests/test_randprocs/test_covfuncs/test_exponentiated_quadratic.py @@ -1,13 +1,13 @@ -"""Test cases for the `ExpQuad` kernel.""" +"""Test cases for the `ExpQuad` covariance function.""" import numpy as np import pytest -from probnum.randprocs import kernels +from probnum.randprocs import covfuncs @pytest.mark.parametrize("lengthscales", (0.0, -1.0, (0.0, 1.0), (-0.2, 2.0))) def test_nonpositive_lengthscales_raises_exception(lengthscales): """Check whether a non-positive `lengthscales` parameter raises a ValueError.""" with pytest.raises(ValueError): - kernels.ExpQuad(np.shape(lengthscales), lengthscales=lengthscales) + covfuncs.ExpQuad(np.shape(lengthscales), lengthscales=lengthscales) diff --git a/tests/test_randprocs/test_kernels/test_linop_matrix.py b/tests/test_randprocs/test_covfuncs/test_linop_matrix.py similarity index 53% rename from tests/test_randprocs/test_kernels/test_linop_matrix.py rename to tests/test_randprocs/test_covfuncs/test_linop_matrix.py index 165ae8b90..56f314bcc 100644 --- a/tests/test_randprocs/test_kernels/test_linop_matrix.py +++ b/tests/test_randprocs/test_covfuncs/test_linop_matrix.py @@ -1,4 +1,4 @@ -"""Test cases for ``Kernel.matrix`` and ``Kernel.linop``""" +"""Test cases for ``CovarianceFunction.matrix`` and ``CovarianceFunction.linop``""" from typing import Callable, Optional @@ -11,31 +11,35 @@ @pytest.fixture def linop( - kernel: pn.randprocs.kernels.Kernel, x0: np.ndarray, x1: Optional[np.ndarray] + k: pn.randprocs.covfuncs.CovarianceFunction, + x0: np.ndarray, + x1: Optional[np.ndarray], ) -> pn.linops.LinearOperator: """`LinearOperator` representation of the covariance matrix.""" if x1 is None and np.prod(x0.shape[:-1]) >= 100: pytest.skip("Runs too long") - return kernel.linop(x0, x1) + return k.linop(x0, x1) @pytest.fixture def matrix( - kernel: pn.randprocs.kernels.Kernel, x0: np.ndarray, x1: Optional[np.ndarray] + k: pn.randprocs.covfuncs.CovarianceFunction, + x0: np.ndarray, + x1: Optional[np.ndarray], ) -> np.ndarray: """Covariance matrix.""" if x1 is None and np.prod(x0.shape[:-1]) >= 100: pytest.skip("Runs too long") - return kernel.matrix(x0, x1) + return k.matrix(x0, x1) @pytest.fixture def matrix_naive( - kernel: pn.randprocs.kernels.Kernel, - kernel_call_naive: Callable[[np.ndarray, Optional[np.ndarray]], np.ndarray], + k: pn.randprocs.covfuncs.CovarianceFunction, + k_call_naive: Callable[[np.ndarray, Optional[np.ndarray]], np.ndarray], x0: np.ndarray, x1: Optional[np.ndarray], ) -> np.ndarray: @@ -46,38 +50,40 @@ def matrix_naive( x1 = x0 - return kernel_call_naive( - x0=x0.reshape((-1, 1) + kernel.input_shape, order="C"), - x1=x1.reshape((1, -1) + kernel.input_shape, order="C"), + return k_call_naive( + x0=x0.reshape((-1, 1) + k.input_shape, order="C"), + x1=x1.reshape((1, -1) + k.input_shape, order="C"), ) def test_linop_type(linop: pn.linops.LinearOperator): - """Check whether a `Kernel.linop` evaluates to a `pn.linops.LinearOperator`.""" + """Check whether a `CovarianceFunction.linop` evaluates to a + `pn.linops.LinearOperator`.""" assert isinstance(linop, pn.linops.LinearOperator) def test_matrix_type(matrix: np.ndarray): - """Check whether a `Kernel.matrix` evaluates to a numpy scalar or array.""" + """Check whether a `CovarianceFunction.matrix` evaluates to a numpy scalar or + array.""" assert isinstance(matrix, (np.ndarray, np.number)) def test_shape( - kernel: pn.randprocs.kernels.Kernel, + k: pn.randprocs.covfuncs.CovarianceFunction, x0: np.ndarray, x1: Optional[np.ndarray], linop: pn.linops.LinearOperator, matrix: np.ndarray, matrix_naive: np.ndarray, ): - """Test the shape of `Kernel.{linop,matrix}`.""" + """Test the shape of `CovarianceFunction.{linop,matrix}`.""" assert linop.shape == matrix_naive.shape assert matrix.shape == matrix_naive.shape, ( - f"Kernel {type(kernel)} does not have the right shape if evaluated at inputs " - f"with x0.shape={x0.shape}" + f"Covariance function {type(k)} does not have the right shape if evaluated at " + f"inputs with x0.shape={x0.shape}" + ("" if x1 is None else f"and x1.shape={x1.shape}.") ) @@ -86,8 +92,8 @@ def test_linop_equals_matrix_naive( linop: pn.linops.LinearOperator, matrix_naive: np.ndarray, ): - """Test whether the values of `Kernel.linop(...).todense()` match the reference - implementation.""" + """Test whether the values of `CovarianceFunction.linop(...).todense()` match the + reference implementation.""" np.testing.assert_allclose( linop.todense(), @@ -101,7 +107,8 @@ def test_matrix_equals_matrix_naive( matrix: np.ndarray, matrix_naive: np.ndarray, ): - """Test whether the values of `Kernel.matrix` match the reference implementation.""" + """Test whether the values of `CovarianceFunction.matrix` match the reference + implementation.""" np.testing.assert_allclose( matrix, @@ -119,28 +126,30 @@ def test_matrix_equals_matrix_naive( (10,), ], ) -def test_input_shape_mismatch(kernel: pn.randprocs.kernels.Kernel, shape: ShapeType): +def test_input_shape_mismatch( + k: pn.randprocs.covfuncs.CovarianceFunction, shape: ShapeType +): """Test whether passing an input with the wrong input shape raises an error.""" - if kernel.input_ndim > 0: - input_shape = shape + tuple(dim + 1 for dim in kernel.input_shape) + if k.input_ndim > 0: + input_shape = shape + tuple(dim + 1 for dim in k.input_shape) - # `Kernel.linop` + # `CovarianceFunction.linop` with pytest.raises(ValueError): - kernel.linop(np.zeros(input_shape)) + k.linop(np.zeros(input_shape)) with pytest.raises(ValueError): - kernel.linop(np.ones(input_shape), np.zeros(shape + kernel.input_shape)) + k.linop(np.ones(input_shape), np.zeros(shape + k.input_shape)) with pytest.raises(ValueError): - kernel.linop(np.ones(shape + kernel.input_shape), np.zeros(input_shape)) + k.linop(np.ones(shape + k.input_shape), np.zeros(input_shape)) - # `Kernel.matrix` + # `CovarianceFunction.matrix` with pytest.raises(ValueError): - kernel.matrix(np.zeros(input_shape)) + k.matrix(np.zeros(input_shape)) with pytest.raises(ValueError): - kernel.matrix(np.ones(input_shape), np.zeros(shape + kernel.input_shape)) + k.matrix(np.ones(input_shape), np.zeros(shape + k.input_shape)) with pytest.raises(ValueError): - kernel.matrix(np.ones(shape + kernel.input_shape), np.zeros(input_shape)) + k.matrix(np.ones(shape + k.input_shape), np.zeros(input_shape)) diff --git a/tests/test_randprocs/test_kernels/test_matern.py b/tests/test_randprocs/test_covfuncs/test_matern.py similarity index 68% rename from tests/test_randprocs/test_kernels/test_matern.py rename to tests/test_randprocs/test_covfuncs/test_matern.py index 8d94548f1..54a5904d1 100644 --- a/tests/test_randprocs/test_kernels/test_matern.py +++ b/tests/test_randprocs/test_covfuncs/test_matern.py @@ -1,10 +1,10 @@ -"""Test cases for the Matern kernel.""" +"""Test cases for the Matérn covariance function.""" import numpy as np import pytest -from probnum.randprocs import kernels -from probnum.randprocs.kernels._matern import _matern_bessel +from probnum.randprocs import covfuncs +from probnum.randprocs.covfuncs._matern import _matern_bessel from probnum.typing import ShapeType @@ -12,14 +12,14 @@ def test_nonpositive_nu_raises_exception(nu): """Check whether a non-positive nu parameter raises a ValueError.""" with pytest.raises(ValueError): - kernels.Matern(input_shape=(), nu=nu) + covfuncs.Matern(input_shape=(), nu=nu) @pytest.mark.parametrize("lengthscales", (0.0, -1.0, (0.0, 1.0), (-0.2, 2.0))) def test_nonpositive_lengthscales_raises_exception(lengthscales): """Check whether a non-positive `lengthscales` parameter raises a ValueError.""" with pytest.raises(ValueError): - kernels.Matern(np.shape(lengthscales), lengthscales=lengthscales) + covfuncs.Matern(np.shape(lengthscales), lengthscales=lengthscales) @pytest.mark.parametrize("p", range(4)) @@ -33,7 +33,7 @@ def test_half_integer_impl_equals_naive_impl( nu = p + 0.5 lengthscales = rng.gamma(1.0, size=input_shape) + 0.5 - k = kernels.Matern(input_shape, nu=nu, lengthscales=lengthscales) + k = covfuncs.Matern(input_shape, nu=nu, lengthscales=lengthscales) assert k.is_half_integer assert k.p == p @@ -46,24 +46,28 @@ def test_half_integer_impl_equals_naive_impl( np.testing.assert_allclose(k.matrix(x0, x1), k_naive.matrix(x0, x1)) -def test_nu_large_recovers_rbf_kernel( +def test_nu_large_recovers_rbf_covfunc( x0: np.ndarray, x1: np.ndarray, input_shape: ShapeType ): - """Test whether a Matern kernel with nu large is close to an RBF kernel.""" + """Test whether a Matern covariance function with nu large is close to an RBF + covariance function.""" lengthscale = 1.25 - rbf = kernels.ExpQuad(input_shape=input_shape, lengthscales=lengthscale) - matern = kernels.Matern(input_shape=input_shape, lengthscales=lengthscale, nu=15) + rbf = covfuncs.ExpQuad(input_shape=input_shape, lengthscales=lengthscale) + matern = covfuncs.Matern(input_shape=input_shape, lengthscales=lengthscale, nu=15) np.testing.assert_allclose( rbf.matrix(x0, x1), matern.matrix(x0, x1), - err_msg="RBF and Matern kernel are not sufficiently close for nu->infty.", + err_msg=( + "RBF and Matern covariance function are not sufficiently close for " + "nu->infty." + ), rtol=0.05, atol=0.01, ) -class NaiveMatern(kernels.IsotropicMixin, kernels.Kernel): +class NaiveMatern(covfuncs.IsotropicMixin, covfuncs.CovarianceFunction): def __init__(self, input_shape, *, nu, lengthscales): super().__init__(input_shape=input_shape) diff --git a/tests/test_randprocs/test_covfuncs/test_product_matern.py b/tests/test_randprocs/test_covfuncs/test_product_matern.py new file mode 100644 index 000000000..015314928 --- /dev/null +++ b/tests/test_randprocs/test_covfuncs/test_product_matern.py @@ -0,0 +1,43 @@ +"""Test cases for the product Matern covariance function.""" + +import numpy as np +import pytest + +from probnum.randprocs import covfuncs +import probnum.utils as _utils + + +@pytest.mark.parametrize("nu", [0.5, 1.5, 2.5, 3.0]) +def test_covfun_matrix(input_dim, nu): + """Check that the product Matérn covariance function matrix is an elementwise + product of 1D Matérn covariance function matrices.""" + lengthscale = 1.25 + matern = covfuncs.Matern(input_shape=(1,), lengthscales=lengthscale, nu=nu) + product_matern = covfuncs.ProductMatern( + input_shape=(input_dim,), lengthscales=lengthscale, nus=nu + ) + rng = np.random.default_rng(42) + num_xs = 15 + xs = rng.random(size=(num_xs, input_dim)) + k_matrix1 = product_matern.matrix(xs) + k_matrix2 = np.ones(shape=(num_xs, num_xs)) + for dim in range(input_dim): + k_matrix2 *= matern.matrix(_utils.as_colvec(xs[:, dim])) + np.testing.assert_allclose( + k_matrix1, + k_matrix2, + ) + + +@pytest.mark.parametrize( + "ell,nu", + [ + (np.array([3.0]), 0.5), + (3.0, np.array([0.5])), + (np.array([3.0]), np.array([0.5])), + ], +) +def test_wrong_initialization_raises_exception(ell, nu): + """Parameters must be scalars if covariance function input is scalar.""" + with pytest.raises(ValueError): + covfuncs.ProductMatern(input_shape=(), lengthscales=ell, nus=nu) diff --git a/tests/test_randprocs/test_kernels/test_rational_quadratic.py b/tests/test_randprocs/test_covfuncs/test_rational_quadratic.py similarity index 59% rename from tests/test_randprocs/test_kernels/test_rational_quadratic.py rename to tests/test_randprocs/test_covfuncs/test_rational_quadratic.py index 6c2263f55..b8febc4fb 100644 --- a/tests/test_randprocs/test_kernels/test_rational_quadratic.py +++ b/tests/test_randprocs/test_covfuncs/test_rational_quadratic.py @@ -1,12 +1,12 @@ -"""Test cases for the rational quadratic kernel.""" +"""Test cases for the rational quadratic covariance function.""" import pytest -from probnum.randprocs import kernels +from probnum.randprocs import covfuncs @pytest.mark.parametrize("alpha", [-1, -1.0, 0.0, 0]) def test_nonpositive_alpha_raises_exception(alpha): """Check whether a non-positive alpha parameter raises a ValueError.""" with pytest.raises(ValueError): - kernels.RatQuad(input_shape=(), alpha=alpha) + covfuncs.RatQuad(input_shape=(), alpha=alpha) diff --git a/tests/test_randprocs/test_gaussian_process.py b/tests/test_randprocs/test_gaussian_process.py index 885457047..781048631 100644 --- a/tests/test_randprocs/test_gaussian_process.py +++ b/tests/test_randprocs/test_gaussian_process.py @@ -4,37 +4,37 @@ import pytest from probnum import functions, randprocs, randvars -from probnum.randprocs import kernels +from probnum.randprocs import covfuncs def test_mean_not_function_raises_error(): with pytest.raises(TypeError): randprocs.GaussianProcess( mean=np.zeros_like, - cov=kernels.ExpQuad(input_shape=(1,)), + cov=covfuncs.ExpQuad(input_shape=(1,)), ) -def test_cov_not_kernel_raises_error(): - """Initializing a GP with a covariance function which is not a kernel raises a - TypeError.""" +def test_cov_not_covfunc_raises_error(): + """Initializing a GP with a covariance function which is not a covariance function + raises a TypeError.""" with pytest.raises(TypeError): randprocs.GaussianProcess( mean=functions.Zero(input_shape=(1,), output_shape=(1,)), cov=np.dot ) -def test_mean_kernel_shape_mismatch_raises_error(): +def test_mean_covfunc_shape_mismatch_raises_error(): with pytest.raises(ValueError): randprocs.GaussianProcess( mean=functions.Zero(input_shape=(2,), output_shape=(1,)), - cov=kernels.ExpQuad(input_shape=(3,)), + cov=covfuncs.ExpQuad(input_shape=(3,)), ) with pytest.raises(ValueError): randprocs.GaussianProcess( mean=functions.Zero(input_shape=(2,), output_shape=(2,)), - cov=kernels.ExpQuad(input_shape=(2,)), + cov=covfuncs.ExpQuad(input_shape=(2,)), ) @@ -42,13 +42,13 @@ def test_mean_wrong_input_shape_raises_error(): with pytest.raises(ValueError): randprocs.GaussianProcess( mean=functions.Zero(input_shape=(2, 2), output_shape=(1,)), - cov=kernels.ExpQuad(input_shape=(2,)), + cov=covfuncs.ExpQuad(input_shape=(2,)), ) with pytest.raises(ValueError): randprocs.GaussianProcess( mean=functions.Zero(input_shape=(2,), output_shape=(2, 1)), - cov=kernels.ExpQuad(input_shape=(2,)), + cov=covfuncs.ExpQuad(input_shape=(2,)), ) diff --git a/tests/test_randprocs/test_kernels/test_arithmetic.py b/tests/test_randprocs/test_kernels/test_arithmetic.py deleted file mode 100644 index 57038e955..000000000 --- a/tests/test_randprocs/test_kernels/test_arithmetic.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for kernel arithmetic.""" -from probnum.randprocs import kernels -from probnum.randprocs.kernels._arithmetic_fallbacks import ( - ProductKernel, - ScaledKernel, - SumKernel, -) - - -def test_scalar_mul(kernel: kernels.Kernel): - scalar = 3.14 - kernel_mul = kernel * scalar - kernel_rmul = scalar * kernel - assert isinstance(kernel_mul, ScaledKernel) - assert isinstance(kernel_rmul, ScaledKernel) - assert kernel_mul.input_shape == kernel.input_shape - assert kernel_rmul.input_shape == kernel.input_shape - assert kernel_mul.output_shape_0 == kernel.output_shape_0 - assert kernel_mul.output_shape_1 == kernel.output_shape_1 - assert kernel_rmul.output_shape_0 == kernel.output_shape_0 - assert kernel_rmul.output_shape_1 == kernel.output_shape_1 - - -def test_add(kernel: kernels.Kernel): - k_whitenoise = kernels.WhiteNoise(input_shape=kernel.input_shape) - kernel_sum = kernel + k_whitenoise - assert isinstance(kernel_sum, SumKernel) - assert kernel_sum.input_shape == kernel.input_shape - assert kernel_sum.output_shape_0 == kernel.output_shape_0 - assert kernel_sum.output_shape_1 == kernel.output_shape_1 - - -def test_mul(kernel: kernels.Kernel): - k_poly = kernels.Polynomial(input_shape=kernel.input_shape) - kernel_prod = kernel * k_poly - assert isinstance(kernel_prod, ProductKernel) - assert kernel_prod.input_shape == kernel.input_shape - assert kernel_prod.output_shape_0 == kernel.output_shape_0 - assert kernel_prod.output_shape_1 == kernel.output_shape_1 diff --git a/tests/test_randprocs/test_kernels/test_arithmetic_fallbacks.py b/tests/test_randprocs/test_kernels/test_arithmetic_fallbacks.py deleted file mode 100644 index 28f387ecb..000000000 --- a/tests/test_randprocs/test_kernels/test_arithmetic_fallbacks.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Tests for fall-back implementations of kernel arithmetic.""" - -import numpy as np -import pytest -from pytest_cases import parametrize - -from probnum.randprocs import kernels -from probnum.randprocs.kernels._arithmetic_fallbacks import ( - ProductKernel, - ScaledKernel, - SumKernel, -) -from probnum.typing import ScalarType - - -@parametrize("scalar", [1.0, 3, 1000.0]) -def test_scaled_kernel_evaluation( - kernel: kernels.Kernel, scalar: ScalarType, x0: np.ndarray -): - k_scaled = ScaledKernel(kernel=kernel, scalar=scalar) - np.testing.assert_allclose(k_scaled.matrix(x0), scalar * kernel.matrix(x0)) - - -def test_non_scalar_raises_error(): - with pytest.raises(TypeError): - ScaledKernel(kernel=kernels.WhiteNoise(input_shape=()), scalar=np.array([0, 1])) - - -def test_non_kernel_raises_error(): - with pytest.raises(TypeError): - ScaledKernel(kernel=np.eye(5), scalar=1.0) - - -def test_sum_kernel_evaluation(kernel: kernels.Kernel, x0: np.ndarray): - k_whitenoise = kernels.WhiteNoise(input_shape=kernel.input_shape) - k_sum = SumKernel(kernel, k_whitenoise) - np.testing.assert_allclose( - k_sum.matrix(x0), kernel.matrix(x0) + k_whitenoise.matrix(x0) - ) - - -def test_sum_kernel_shape_mismatch_raises_error(): - with pytest.raises(ValueError): - SumKernel( - kernels.WhiteNoise(input_shape=()), kernels.WhiteNoise(input_shape=(1,)) - ) - - -def test_sum_kernel_contracts(): - input_shape = () - k = kernels.ExpQuad(input_shape=input_shape) - k_sum = SumKernel(k, SumKernel(k, k)) - assert all(not isinstance(summand, SumKernel) for summand in k_sum._summands) - - -def test_product_kernel_evaluation(kernel: kernels.Kernel, x0: np.ndarray): - k_poly = kernels.Polynomial(input_shape=kernel.input_shape) - k_sum = ProductKernel(kernel, k_poly) - np.testing.assert_allclose(k_sum.matrix(x0), kernel.matrix(x0) * k_poly.matrix(x0)) - - -def test_product_kernel_shape_mismatch_raises_error(): - with pytest.raises(ValueError): - ProductKernel( - kernels.WhiteNoise(input_shape=()), kernels.WhiteNoise(input_shape=(1,)) - ) - - -def test_product_kernel_contracts(): - input_shape = () - k = kernels.ExpQuad(input_shape=input_shape) - k_prod = ProductKernel(k, ProductKernel(k, k)) - assert all(not isinstance(factor, ProductKernel) for factor in k_prod._factors) diff --git a/tests/test_randprocs/test_kernels/test_kernel.py b/tests/test_randprocs/test_kernels/test_kernel.py deleted file mode 100644 index b5837cf87..000000000 --- a/tests/test_randprocs/test_kernels/test_kernel.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Test cases for `Kernel`""" - -import numpy as np - -from probnum.randprocs import kernels - - -def test_input_ndim(kernel: kernels.Kernel): - assert kernel.input_ndim == np.empty(kernel.input_shape).ndim - - -def test_input_size(kernel: kernels.Kernel): - assert kernel.input_size == np.empty(kernel.input_shape).size diff --git a/tests/test_randprocs/test_kernels/test_product_matern.py b/tests/test_randprocs/test_kernels/test_product_matern.py deleted file mode 100644 index 2bcf9ef97..000000000 --- a/tests/test_randprocs/test_kernels/test_product_matern.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Test cases for the product Matern kernel.""" - -import numpy as np -import pytest - -from probnum.randprocs import kernels -import probnum.utils as _utils - - -@pytest.mark.parametrize("nu", [0.5, 1.5, 2.5, 3.0]) -def test_kernel_matrix(input_dim, nu): - """Check that the product Matérn kernel matrix is an elementwise product of 1D - Matérn kernel matrices.""" - lengthscale = 1.25 - matern = kernels.Matern(input_shape=(1,), lengthscales=lengthscale, nu=nu) - product_matern = kernels.ProductMatern( - input_shape=(input_dim,), lengthscales=lengthscale, nus=nu - ) - rng = np.random.default_rng(42) - num_xs = 15 - xs = rng.random(size=(num_xs, input_dim)) - kernel_matrix1 = product_matern.matrix(xs) - kernel_matrix2 = np.ones(shape=(num_xs, num_xs)) - for dim in range(input_dim): - kernel_matrix2 *= matern.matrix(_utils.as_colvec(xs[:, dim])) - np.testing.assert_allclose( - kernel_matrix1, - kernel_matrix2, - ) - - -@pytest.mark.parametrize( - "ell,nu", - [ - (np.array([3.0]), 0.5), - (3.0, np.array([0.5])), - (np.array([3.0]), np.array([0.5])), - ], -) -def test_wrong_initialization_raises_exception(ell, nu): - """Parameters must be scalars if kernel input is scalar.""" - with pytest.raises(ValueError): - kernels.ProductMatern(input_shape=(), lengthscales=ell, nus=nu) diff --git a/tests/test_randprocs/test_random_process.py b/tests/test_randprocs/test_random_process.py index f7d76a2d2..d239a5757 100644 --- a/tests/test_randprocs/test_random_process.py +++ b/tests/test_randprocs/test_random_process.py @@ -153,7 +153,7 @@ def test_inconsistent_cov_shape_errors(): input_shape=(42,), output_shape=(), dtype=np.double, - cov=randprocs.kernels.ExpQuad( + cov=randprocs.covfuncs.ExpQuad( input_shape=(3,), ), ) @@ -163,7 +163,7 @@ def test_inconsistent_cov_shape_errors(): input_shape=(), output_shape=(1,), dtype=np.double, - cov=randprocs.kernels.ExpQuad( + cov=randprocs.covfuncs.ExpQuad( input_shape=(), ), )