Skip to content

Kernels

Physics-aware scikit-learn kernels for fingerprint, composition, and structure similarity.

matgpr.kernels

ElementFractionKernel

Bases: Kernel

Kernel for elemental composition fraction or count vectors.

The kernel first projects non-negative element vectors onto the composition simplex by normalizing each row to sum to one. It then compares normalized compositions with either an L1 Laplacian kernel or an L2 RBF kernel.

The default L1 form is:

k(x, y) = exp(-sum_i |c_i - c'_i| / length_scale)

where c and c' are element-fraction vectors. This is useful when the physical idea of similarity is the amount of composition substitution needed to move from one material to another.

Source code in matgpr/kernels.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
class ElementFractionKernel(Kernel):
    """Kernel for elemental composition fraction or count vectors.

    The kernel first projects non-negative element vectors onto the composition
    simplex by normalizing each row to sum to one. It then compares normalized
    compositions with either an L1 Laplacian kernel or an L2 RBF kernel.

    The default L1 form is:

    ``k(x, y) = exp(-sum_i |c_i - c'_i| / length_scale)``

    where ``c`` and ``c'`` are element-fraction vectors. This is useful when
    the physical idea of similarity is the amount of composition substitution
    needed to move from one material to another.
    """

    def __init__(
        self,
        *,
        length_scale: float = 1.0,
        length_scale_bounds: tuple[float, float] | str = (1e-5, 1e5),
        metric: str = "l1",
        normalize: bool = True,
        eps: float = 1e-12,
    ):
        _validate_distance_metric(metric)
        self.length_scale = length_scale
        self.length_scale_bounds = length_scale_bounds
        self.metric = metric
        self.normalize = normalize
        self.eps = eps

    @property
    def hyperparameter_length_scale(self):
        return Hyperparameter("length_scale", "numeric", self.length_scale_bounds)

    def __call__(self, X, Y=None, eval_gradient: bool = False):
        """Return the element-fraction composition kernel matrix."""
        if eval_gradient and Y is not None:
            raise ValueError("Gradient can only be evaluated when Y is None")

        length_scale = _validate_length_scale(self.length_scale)
        distance = pairwise_composition_distance(
            X,
            Y,
            metric=self.metric,
            normalize=self.normalize,
            eps=self.eps,
        )

        if self.metric == "l1":
            kernel = np.exp(-distance / length_scale)
            log_gradient = kernel * distance / length_scale
        elif self.metric == "l2":
            kernel = np.exp(-0.5 * distance / length_scale**2)
            log_gradient = kernel * distance / length_scale**2
        if eval_gradient:
            if self.hyperparameter_length_scale.fixed:
                return kernel, np.empty((*kernel.shape, 0))
            return kernel, log_gradient[:, :, None]
        return kernel

    def diag(self, X) -> np.ndarray:
        """Return the diagonal of the kernel matrix."""
        X = _normalize_composition_vectors(X, normalize=self.normalize, eps=self.eps, name="X")
        return np.ones(X.shape[0])

    def is_stationary(self) -> bool:
        """Return whether the kernel is stationary."""
        return False

    def __repr__(self) -> str:
        return (
            "ElementFractionKernel("
            f"length_scale={self.length_scale}, "
            f"metric={self.metric!r}, "
            f"normalize={self.normalize})"
        )

__call__(X, Y=None, eval_gradient=False)

Return the element-fraction composition kernel matrix.

Source code in matgpr/kernels.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def __call__(self, X, Y=None, eval_gradient: bool = False):
    """Return the element-fraction composition kernel matrix."""
    if eval_gradient and Y is not None:
        raise ValueError("Gradient can only be evaluated when Y is None")

    length_scale = _validate_length_scale(self.length_scale)
    distance = pairwise_composition_distance(
        X,
        Y,
        metric=self.metric,
        normalize=self.normalize,
        eps=self.eps,
    )

    if self.metric == "l1":
        kernel = np.exp(-distance / length_scale)
        log_gradient = kernel * distance / length_scale
    elif self.metric == "l2":
        kernel = np.exp(-0.5 * distance / length_scale**2)
        log_gradient = kernel * distance / length_scale**2
    if eval_gradient:
        if self.hyperparameter_length_scale.fixed:
            return kernel, np.empty((*kernel.shape, 0))
        return kernel, log_gradient[:, :, None]
    return kernel

diag(X)

Return the diagonal of the kernel matrix.

Source code in matgpr/kernels.py
89
90
91
92
def diag(self, X) -> np.ndarray:
    """Return the diagonal of the kernel matrix."""
    X = _normalize_composition_vectors(X, normalize=self.normalize, eps=self.eps, name="X")
    return np.ones(X.shape[0])

is_stationary()

Return whether the kernel is stationary.

Source code in matgpr/kernels.py
94
95
96
def is_stationary(self) -> bool:
    """Return whether the kernel is stationary."""
    return False

StructureFeatureKernel

Bases: Kernel

Kernel for continuous structure descriptors.

This kernel is intended for global crystal-structure descriptors such as lattice-shape, volume-per-atom, and density features from matgpr.structure_fingerprint. It compares descriptor vectors after optional fixed feature scaling using either an L2 RBF kernel or an L1 Laplacian kernel.

Source code in matgpr/kernels.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class StructureFeatureKernel(Kernel):
    """Kernel for continuous structure descriptors.

    This kernel is intended for global crystal-structure descriptors such as
    lattice-shape, volume-per-atom, and density features from
    ``matgpr.structure_fingerprint``. It compares descriptor vectors after
    optional fixed feature scaling using either an L2 RBF kernel or an L1
    Laplacian kernel.
    """

    def __init__(
        self,
        *,
        length_scale: float = 1.0,
        length_scale_bounds: tuple[float, float] | str = (1e-5, 1e5),
        metric: str = "l2",
        feature_scales=None,
    ):
        _validate_distance_metric(metric)
        self.length_scale = length_scale
        self.length_scale_bounds = length_scale_bounds
        self.metric = metric
        self.feature_scales = feature_scales

    @property
    def hyperparameter_length_scale(self):
        return Hyperparameter("length_scale", "numeric", self.length_scale_bounds)

    def __call__(self, X, Y=None, eval_gradient: bool = False):
        """Return the structure-feature kernel matrix."""
        if eval_gradient and Y is not None:
            raise ValueError("Gradient can only be evaluated when Y is None")

        length_scale = _validate_length_scale(self.length_scale)
        distance = pairwise_structure_distance(
            X,
            Y,
            metric=self.metric,
            feature_scales=self.feature_scales,
        )

        if self.metric == "l2":
            kernel = np.exp(-0.5 * distance / length_scale**2)
            log_gradient = kernel * distance / length_scale**2
        elif self.metric == "l1":
            kernel = np.exp(-distance / length_scale)
            log_gradient = kernel * distance / length_scale
        if eval_gradient:
            if self.hyperparameter_length_scale.fixed:
                return kernel, np.empty((*kernel.shape, 0))
            return kernel, log_gradient[:, :, None]
        return kernel

    def diag(self, X) -> np.ndarray:
        """Return the diagonal of the kernel matrix."""
        X = _scale_structure_features(X, feature_scales=self.feature_scales, name="X")
        return np.ones(X.shape[0])

    def is_stationary(self) -> bool:
        """Return whether the kernel is stationary."""
        return True

    def __repr__(self) -> str:
        return f"StructureFeatureKernel(length_scale={self.length_scale}, metric={self.metric!r})"

__call__(X, Y=None, eval_gradient=False)

Return the structure-feature kernel matrix.

Source code in matgpr/kernels.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def __call__(self, X, Y=None, eval_gradient: bool = False):
    """Return the structure-feature kernel matrix."""
    if eval_gradient and Y is not None:
        raise ValueError("Gradient can only be evaluated when Y is None")

    length_scale = _validate_length_scale(self.length_scale)
    distance = pairwise_structure_distance(
        X,
        Y,
        metric=self.metric,
        feature_scales=self.feature_scales,
    )

    if self.metric == "l2":
        kernel = np.exp(-0.5 * distance / length_scale**2)
        log_gradient = kernel * distance / length_scale**2
    elif self.metric == "l1":
        kernel = np.exp(-distance / length_scale)
        log_gradient = kernel * distance / length_scale
    if eval_gradient:
        if self.hyperparameter_length_scale.fixed:
            return kernel, np.empty((*kernel.shape, 0))
        return kernel, log_gradient[:, :, None]
    return kernel

diag(X)

Return the diagonal of the kernel matrix.

Source code in matgpr/kernels.py
160
161
162
163
def diag(self, X) -> np.ndarray:
    """Return the diagonal of the kernel matrix."""
    X = _scale_structure_features(X, feature_scales=self.feature_scales, name="X")
    return np.ones(X.shape[0])

is_stationary()

Return whether the kernel is stationary.

Source code in matgpr/kernels.py
165
166
167
def is_stationary(self) -> bool:
    """Return whether the kernel is stationary."""
    return True

TanimotoKernel

Bases: Kernel

Tanimoto kernel for binary or non-negative count fingerprints.

For vectors x and y, the generalized Tanimoto similarity is:

k(x, y) = dot(x, y) / (||x||^2 + ||y||^2 - dot(x, y)).

This is commonly used for molecular fingerprints and polymer fingerprints. Inputs must be binary or non-negative count descriptors.

Source code in matgpr/kernels.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
class TanimotoKernel(Kernel):
    """Tanimoto kernel for binary or non-negative count fingerprints.

    For vectors ``x`` and ``y``, the generalized Tanimoto similarity is:

    ``k(x, y) = dot(x, y) / (||x||^2 + ||y||^2 - dot(x, y))``.

    This is commonly used for molecular fingerprints and polymer fingerprints.
    Inputs must be binary or non-negative count descriptors.
    """

    def __init__(self, *, eps: float = 1e-12, validate_nonnegative: bool = True):
        self.eps = eps
        self.validate_nonnegative = validate_nonnegative

    def __call__(self, X, Y=None, eval_gradient: bool = False):
        """Return the Tanimoto kernel matrix."""
        if eval_gradient and Y is not None:
            raise ValueError("Gradient can only be evaluated when Y is None")

        kernel = pairwise_tanimoto_similarity(
            X,
            Y,
            eps=self.eps,
            validate_nonnegative=self.validate_nonnegative,
        )
        if eval_gradient:
            return kernel, np.empty((*kernel.shape, 0))
        return kernel

    def diag(self, X) -> np.ndarray:
        """Return the diagonal of the kernel matrix."""
        X = _to_2d_float_array(X, "X")
        if self.validate_nonnegative:
            _validate_nonnegative(X, "X")
        return np.ones(X.shape[0])

    def is_stationary(self) -> bool:
        """Return whether the kernel is stationary."""
        return False

    def __repr__(self) -> str:
        return f"TanimotoKernel(eps={self.eps}, validate_nonnegative={self.validate_nonnegative})"

__call__(X, Y=None, eval_gradient=False)

Return the Tanimoto kernel matrix.

Source code in matgpr/kernels.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def __call__(self, X, Y=None, eval_gradient: bool = False):
    """Return the Tanimoto kernel matrix."""
    if eval_gradient and Y is not None:
        raise ValueError("Gradient can only be evaluated when Y is None")

    kernel = pairwise_tanimoto_similarity(
        X,
        Y,
        eps=self.eps,
        validate_nonnegative=self.validate_nonnegative,
    )
    if eval_gradient:
        return kernel, np.empty((*kernel.shape, 0))
    return kernel

diag(X)

Return the diagonal of the kernel matrix.

Source code in matgpr/kernels.py
203
204
205
206
207
208
def diag(self, X) -> np.ndarray:
    """Return the diagonal of the kernel matrix."""
    X = _to_2d_float_array(X, "X")
    if self.validate_nonnegative:
        _validate_nonnegative(X, "X")
    return np.ones(X.shape[0])

is_stationary()

Return whether the kernel is stationary.

Source code in matgpr/kernels.py
210
211
212
def is_stationary(self) -> bool:
    """Return whether the kernel is stationary."""
    return False

FeatureSubsetKernel

Bases: Kernel

Apply a scikit-learn kernel to selected feature columns.

This wrapper is useful for mixed materials descriptors. For example, a Tanimoto kernel can act on fingerprint columns while an RBF or Matern kernel acts on continuous physics descriptors, then the kernels can be added or multiplied.

Source code in matgpr/kernels.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
class FeatureSubsetKernel(Kernel):
    """Apply a scikit-learn kernel to selected feature columns.

    This wrapper is useful for mixed materials descriptors. For example, a
    Tanimoto kernel can act on fingerprint columns while an RBF or Matern kernel
    acts on continuous physics descriptors, then the kernels can be added or
    multiplied.
    """

    def __init__(self, kernel: Kernel, columns):
        self.kernel = kernel
        self.columns = columns

    @property
    def hyperparameters(self):
        return self.kernel.hyperparameters

    @property
    def theta(self):
        return self.kernel.theta

    @theta.setter
    def theta(self, theta):
        self.kernel.theta = theta

    @property
    def bounds(self):
        return self.kernel.bounds

    def clone_with_theta(self, theta):
        """Return a clone with updated wrapped-kernel hyperparameters."""
        cloned = clone(self)
        cloned.kernel = self.kernel.clone_with_theta(theta)
        return cloned

    def __call__(self, X, Y=None, eval_gradient: bool = False):
        """Return the wrapped-kernel matrix on selected columns."""
        X_subset = _select_columns(X, self.columns, "X")
        Y_subset = None if Y is None else _select_columns(Y, self.columns, "Y")
        return self.kernel(X_subset, Y_subset, eval_gradient=eval_gradient)

    def diag(self, X) -> np.ndarray:
        """Return the wrapped-kernel diagonal on selected columns."""
        return self.kernel.diag(_select_columns(X, self.columns, "X"))

    def is_stationary(self) -> bool:
        """Return whether the wrapped kernel is stationary."""
        return self.kernel.is_stationary()

    def __repr__(self) -> str:
        return f"FeatureSubsetKernel(kernel={self.kernel!r}, columns={self.columns!r})"

clone_with_theta(theta)

Return a clone with updated wrapped-kernel hyperparameters.

Source code in matgpr/kernels.py
247
248
249
250
251
def clone_with_theta(self, theta):
    """Return a clone with updated wrapped-kernel hyperparameters."""
    cloned = clone(self)
    cloned.kernel = self.kernel.clone_with_theta(theta)
    return cloned

__call__(X, Y=None, eval_gradient=False)

Return the wrapped-kernel matrix on selected columns.

Source code in matgpr/kernels.py
253
254
255
256
257
def __call__(self, X, Y=None, eval_gradient: bool = False):
    """Return the wrapped-kernel matrix on selected columns."""
    X_subset = _select_columns(X, self.columns, "X")
    Y_subset = None if Y is None else _select_columns(Y, self.columns, "Y")
    return self.kernel(X_subset, Y_subset, eval_gradient=eval_gradient)

diag(X)

Return the wrapped-kernel diagonal on selected columns.

Source code in matgpr/kernels.py
259
260
261
def diag(self, X) -> np.ndarray:
    """Return the wrapped-kernel diagonal on selected columns."""
    return self.kernel.diag(_select_columns(X, self.columns, "X"))

is_stationary()

Return whether the wrapped kernel is stationary.

Source code in matgpr/kernels.py
263
264
265
def is_stationary(self) -> bool:
    """Return whether the wrapped kernel is stationary."""
    return self.kernel.is_stationary()

pairwise_tanimoto_similarity(X, Y=None, *, eps=1e-12, validate_nonnegative=True)

Return pairwise generalized Tanimoto similarities.

Parameters:

Name Type Description Default
X

Two-dimensional arrays of binary or non-negative count fingerprints. If Y is omitted, similarities are calculated between rows of X.

required
Y

Two-dimensional arrays of binary or non-negative count fingerprints. If Y is omitted, similarities are calculated between rows of X.

required
eps float

Small denominator threshold used to identify all-zero fingerprints.

1e-12
validate_nonnegative bool

If True, reject negative feature values.

True
Source code in matgpr/kernels.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
def pairwise_tanimoto_similarity(
    X,
    Y=None,
    *,
    eps: float = 1e-12,
    validate_nonnegative: bool = True,
) -> np.ndarray:
    """Return pairwise generalized Tanimoto similarities.

    Parameters
    ----------
    X, Y
        Two-dimensional arrays of binary or non-negative count fingerprints. If
        ``Y`` is omitted, similarities are calculated between rows of ``X``.
    eps
        Small denominator threshold used to identify all-zero fingerprints.
    validate_nonnegative
        If ``True``, reject negative feature values.
    """
    X = _to_2d_float_array(X, "X")
    Y = X if Y is None else _to_2d_float_array(Y, "Y")
    if X.shape[1] != Y.shape[1]:
        raise ValueError("X and Y must have the same number of features")
    if eps <= 0:
        raise ValueError("eps must be positive")
    if validate_nonnegative:
        _validate_nonnegative(X, "X")
        _validate_nonnegative(Y, "Y")

    dot = X @ Y.T
    x_norm = np.sum(X * X, axis=1)[:, None]
    y_norm = np.sum(Y * Y, axis=1)[None, :]
    denominator = x_norm + y_norm - dot

    similarity = np.divide(
        dot,
        denominator,
        out=np.zeros_like(dot, dtype=float),
        where=denominator > eps,
    )
    both_zero = denominator <= eps
    similarity[both_zero] = 1.0
    return similarity

pairwise_composition_distance(X, Y=None, *, metric='l1', normalize=True, eps=1e-12)

Return pairwise distances between non-negative composition vectors.

Parameters:

Name Type Description Default
X

Two-dimensional arrays of elemental amount, count, or fraction vectors. If Y is omitted, distances are calculated between rows of X.

required
Y

Two-dimensional arrays of elemental amount, count, or fraction vectors. If Y is omitted, distances are calculated between rows of X.

required
metric str

"l1" returns the sum of absolute element-fraction differences. "l2" returns squared Euclidean distance.

'l1'
normalize bool

If True, each row is normalized to sum to one before distances are computed.

True
eps float

Small threshold used to reject all-zero composition rows.

1e-12
Source code in matgpr/kernels.py
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
def pairwise_composition_distance(
    X,
    Y=None,
    *,
    metric: str = "l1",
    normalize: bool = True,
    eps: float = 1e-12,
) -> np.ndarray:
    """Return pairwise distances between non-negative composition vectors.

    Parameters
    ----------
    X, Y
        Two-dimensional arrays of elemental amount, count, or fraction vectors.
        If ``Y`` is omitted, distances are calculated between rows of ``X``.
    metric
        ``"l1"`` returns the sum of absolute element-fraction differences.
        ``"l2"`` returns squared Euclidean distance.
    normalize
        If ``True``, each row is normalized to sum to one before distances are
        computed.
    eps
        Small threshold used to reject all-zero composition rows.
    """
    X = _normalize_composition_vectors(X, normalize=normalize, eps=eps, name="X")
    Y = (
        X
        if Y is None
        else _normalize_composition_vectors(Y, normalize=normalize, eps=eps, name="Y")
    )
    if X.shape[1] != Y.shape[1]:
        raise ValueError("X and Y must have the same number of features")

    if metric == "l1":
        return cdist(X, Y, metric="cityblock")
    if metric == "l2":
        return cdist(X, Y, metric="sqeuclidean")
    raise ValueError("metric must be either 'l1' or 'l2'")

pairwise_structure_distance(X, Y=None, *, metric='l2', feature_scales=None)

Return pairwise distances between continuous structure descriptor rows.

Parameters:

Name Type Description Default
X

Two-dimensional arrays of structure descriptors.

required
Y

Two-dimensional arrays of structure descriptors.

required
metric str

"l2" returns squared Euclidean distance. "l1" returns summed absolute feature differences.

'l2'
feature_scales

Optional positive per-feature scales. When supplied, descriptor columns are divided by these scales before distances are computed.

None
Source code in matgpr/kernels.py
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
def pairwise_structure_distance(
    X,
    Y=None,
    *,
    metric: str = "l2",
    feature_scales=None,
) -> np.ndarray:
    """Return pairwise distances between continuous structure descriptor rows.

    Parameters
    ----------
    X, Y
        Two-dimensional arrays of structure descriptors.
    metric
        ``"l2"`` returns squared Euclidean distance. ``"l1"`` returns summed
        absolute feature differences.
    feature_scales
        Optional positive per-feature scales. When supplied, descriptor columns
        are divided by these scales before distances are computed.
    """
    X = _scale_structure_features(X, feature_scales=feature_scales, name="X")
    Y = X if Y is None else _scale_structure_features(Y, feature_scales=feature_scales, name="Y")
    if X.shape[1] != Y.shape[1]:
        raise ValueError("X and Y must have the same number of features")

    if metric == "l2":
        return cdist(X, Y, metric="sqeuclidean")
    if metric == "l1":
        return cdist(X, Y, metric="cityblock")
    raise ValueError("metric must be either 'l1' or 'l2'")

build_element_fraction_gpr_kernel(*, constant_value=1.0, constant_value_bounds=(0.001, 1000.0), length_scale=1.0, length_scale_bounds=(1e-05, 100000.0), noise_level=1e-06, noise_level_bounds=(1e-10, 0.1), metric='l1', normalize=True)

Build a scikit-learn GPR kernel for element-fraction composition vectors.

Source code in matgpr/kernels.py
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
def build_element_fraction_gpr_kernel(
    *,
    constant_value: float = 1.0,
    constant_value_bounds: tuple[float, float] | str = (1e-3, 1e3),
    length_scale: float = 1.0,
    length_scale_bounds: tuple[float, float] | str = (1e-5, 1e5),
    noise_level: float = 1e-6,
    noise_level_bounds: tuple[float, float] | str = (1e-10, 1e-1),
    metric: str = "l1",
    normalize: bool = True,
):
    """Build a scikit-learn GPR kernel for element-fraction composition vectors."""
    return ConstantKernel(
        constant_value, constant_value_bounds=constant_value_bounds
    ) * ElementFractionKernel(
        length_scale=length_scale,
        length_scale_bounds=length_scale_bounds,
        metric=metric,
        normalize=normalize,
    ) + WhiteKernel(noise_level=noise_level, noise_level_bounds=noise_level_bounds)

build_structure_gpr_kernel(*, constant_value=1.0, constant_value_bounds=(0.001, 1000.0), length_scale=1.0, length_scale_bounds=(1e-05, 100000.0), noise_level=1e-06, noise_level_bounds=(1e-10, 0.1), metric='l2', feature_scales=None)

Build a scikit-learn GPR kernel for continuous structure descriptors.

Source code in matgpr/kernels.py
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
def build_structure_gpr_kernel(
    *,
    constant_value: float = 1.0,
    constant_value_bounds: tuple[float, float] | str = (1e-3, 1e3),
    length_scale: float = 1.0,
    length_scale_bounds: tuple[float, float] | str = (1e-5, 1e5),
    noise_level: float = 1e-6,
    noise_level_bounds: tuple[float, float] | str = (1e-10, 1e-1),
    metric: str = "l2",
    feature_scales=None,
):
    """Build a scikit-learn GPR kernel for continuous structure descriptors."""
    return ConstantKernel(
        constant_value, constant_value_bounds=constant_value_bounds
    ) * StructureFeatureKernel(
        length_scale=length_scale,
        length_scale_bounds=length_scale_bounds,
        metric=metric,
        feature_scales=feature_scales,
    ) + WhiteKernel(noise_level=noise_level, noise_level_bounds=noise_level_bounds)

build_tanimoto_gpr_kernel(*, constant_value=1.0, constant_value_bounds=(0.001, 1000.0), noise_level=1e-06, noise_level_bounds=(1e-10, 0.1), validate_nonnegative=True)

Build a scikit-learn GPR kernel using Tanimoto fingerprint similarity.

Source code in matgpr/kernels.py
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def build_tanimoto_gpr_kernel(
    *,
    constant_value: float = 1.0,
    constant_value_bounds: tuple[float, float] | str = (1e-3, 1e3),
    noise_level: float = 1e-6,
    noise_level_bounds: tuple[float, float] | str = (1e-10, 1e-1),
    validate_nonnegative: bool = True,
):
    """Build a scikit-learn GPR kernel using Tanimoto fingerprint similarity."""
    return ConstantKernel(
        constant_value, constant_value_bounds=constant_value_bounds
    ) * TanimotoKernel(validate_nonnegative=validate_nonnegative) + WhiteKernel(
        noise_level=noise_level, noise_level_bounds=noise_level_bounds
    )

build_additive_kernel(*kernels)

Add two or more scikit-learn kernels.

Source code in matgpr/kernels.py
448
449
450
451
def build_additive_kernel(*kernels: Kernel) -> Kernel:
    """Add two or more scikit-learn kernels."""
    _validate_kernel_sequence(kernels)
    return reduce(add, kernels)

build_product_kernel(*kernels)

Multiply two or more scikit-learn kernels.

Source code in matgpr/kernels.py
454
455
456
457
def build_product_kernel(*kernels: Kernel) -> Kernel:
    """Multiply two or more scikit-learn kernels."""
    _validate_kernel_sequence(kernels)
    return reduce(mul, kernels)