Coverage for tests/feature_maps/test_feature_maps.py: 100%
31 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-16 21:43 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-16 21:43 +0000
1import lab as B
2import numpy as np
3import pytest
5from geometric_kernels.feature_maps import RandomPhaseFeatureMapCompact
6from geometric_kernels.kernels import MaternGeometricKernel, default_feature_map
7from geometric_kernels.kernels.matern_kernel import default_num
8from geometric_kernels.spaces import NoncompactSymmetricSpace
9from geometric_kernels.utils.utils import make_deterministic
11from ..helper import check_function_with_backend, create_random_state, spaces
14@pytest.fixture(
15 params=spaces(),
16 ids=str,
17)
18def feature_map_and_friends(request, backend):
19 """
20 Returns a tuple (feature_map, kernel, space) where:
21 - feature_map is the `default_feature_map` of the `kernel`,
22 - kernel is the `MaternGeometricKernel` on the `space`, with a reasonably
23 small value of `num`,
24 - space = request.param,
26 `backend` parameter is required to create a random state for the feature
27 map, if it requires one.
28 """
29 space = request.param
31 if isinstance(space, NoncompactSymmetricSpace):
32 kernel = MaternGeometricKernel(
33 space, key=create_random_state(backend), num=min(default_num(space), 100)
34 )
35 else:
36 kernel = MaternGeometricKernel(space, num=min(default_num(space), 3))
38 feature_map = default_feature_map(kernel=kernel)
39 if isinstance(feature_map, RandomPhaseFeatureMapCompact):
40 # RandomPhaseFeatureMapCompact requires a key. Note: normally,
41 # RandomPhaseFeatureMapNoncompact, RejectionSamplingFeatureMapHyperbolic,
42 # and RejectionSamplingFeatureMapSPD also require a key, but when they
43 # are obtained from an already constructed kernel's feature map, the key
44 # is already provided and fixed in the similar way as we do just below.
45 feature_map = make_deterministic(feature_map, key=create_random_state(backend))
47 return feature_map, kernel, space
50@pytest.mark.parametrize("backend", ["numpy", "tensorflow", "torch", "jax"])
51def test_feature_map_approximates_kernel(backend, feature_map_and_friends):
52 feature_map, kernel, space = feature_map_and_friends
54 params = kernel.init_params()
56 key = np.random.RandomState(0)
57 key, X = space.random(key, 50)
59 def diff_kern_mats(params, X):
60 _, embedding = feature_map(X, params)
62 kernel_mat = kernel.K(params, X, X)
63 kernel_mat_alt = B.matmul(embedding, B.T(embedding))
65 return kernel_mat - kernel_mat_alt
67 # Check that, approximately, k(X, X) = <phi(X), phi(X)>, where k is the
68 # kernel and phi is the feature map.
69 check_function_with_backend(
70 backend,
71 np.zeros((X.shape[0], X.shape[0])),
72 diff_kern_mats,
73 params,
74 X,
75 atol=0.1,
76 )