Source code for src.ACBICI.kernels

###############################################################################
#
#                                ACBICI   v2.1.1
#
#
#     Copyright (C) 2025 Fundación IMDEA Materiales (IMDEA Materials Institute), Getafe, Madrid, Spain and
#                        Universidad Politécnica de Madrid (UPM), Madrid, Spain
#     Contact: christina.schenk@imdea.org, ignacio.romero@imdea.org
#     Author: Christina Schenk, Ignacio Romero (christina.schenk@imdea.org, ignacio.romero@imdea.org)
#
#     This file is part of ACBICI.
#
#     All rights reserved.
#
# ACBICI is licensed under the BSD 3-Clause License.
# You may use, redistribute, and modify this file under the terms of that license.
# See the LICENSE file in the repository root for full details.
#
# THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
#
###############################################################################

"""
Created on Tue Jan 28

@author: Christina Schenk, Ignacio Romero
"""

import numpy as np
import math
from abc import ABC, abstractmethod
import sys

# -----------------------------------------------------------------------------
#                           Abstract kernel class
# -----------------------------------------------------------------------------

[docs] class kernel(ABC): def __new__(cls, *args, **kwargs): instance = super().__new__(cls) instance.label = "" return instance
[docs] @abstractmethod def evalkernel(self, dist, lamb, beta, x1=None, x2=None): """ Evaluates the kernel for a distance 'dist', given the two hyperparameters lambda and beta. All the kernels must be of the form k = lambda * f(distance/beta) Parameters ---------- dist: distance lamb: hyperparameter lambda beta: hyperparameter beta Returns ------- kernel evaluation """ pass
[docs] @abstractmethod def print(self, file=sys.stdout): """ Print information about the kernel on a file Parameters ---------- file: filename Returns ------- None """ pass
[docs] class SqExpo(kernel): """ Squared exponential kernel (RBF or Gaussian Kernel). """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "SqExpo" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta, x1, x2): lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta dist2 = np.square(dist) beta2 = beta*beta kernel = np.exp(-0.5*dist2/beta2) # for multiple betas here /betai2 return lamb * kernel
[docs] def print(self, file=sys.stdout): print(" Squared Exponential Kernel", file=file)
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta, x1, x2)
[docs] class Matern32(kernel): """ Once differentiable Matern kernel. """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "Matern32" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta): lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta sq3 = 1.732050807568877193 kernel = (1.0 + sq3 * dist / beta) * np.exp(-sq3 * dist / beta) return lamb * kernel
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta)
[docs] def print(self, file=sys.stdout): print(" Matern32 Kernel", file=file)
[docs] class Matern52(kernel): """ Twice differentiable Matern kernel. """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "Matern52" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta): lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta sq5 = 2.236067977499789805 dist2 = dist*dist beta2 = beta*beta kernel = (1.0 + sq5 * dist / beta + 5.0 * dist2 / (3.0 * beta2)) * np.exp(-sq5 * dist / beta) return lamb * kernel
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta)
[docs] def print(self, file=sys.stdout): print(" Matern52 Kernel", file=file)
[docs] class Expo(kernel): """ Exponential kernel. Exponential growth. Rate increases as input increases. """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "Expo" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta): lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta kernel = np.exp(-dist/beta) return lamb * kernel
[docs] def print(self, file=sys.stdout): print(" Exponential Kernel", file=file)
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta)
[docs] class RatQuad(kernel): """ Rational quadratic kernel with alpha=1. """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "RatQuad" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta): alpha = 1 lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta dist2 = dist*dist beta2 = beta*beta kernel = (1.0 + dist2 * 0.5 / (alpha*beta2)) ** (-alpha) return lamb * kernel
[docs] def print(self, file=sys.stdout): print(" Rational Quadratic Kernel with alpha=1", file=file)
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta)
[docs] class MultiTask(kernel): """ Multi Task kernel assuming that tasks independent, just comparing for same task dimension 0 for different tasks or matern3/2 kernel if same task """ def __init__(self, *, dist=None, lamb=None, beta=None, x1=None, x2=None): self.label = "MultiTask" self.dist = dist self.lamb = lamb self.beta = beta return
[docs] def evalkernel(self, dist, lamb, beta, x1=None, x2=None): lamb = lamb if lamb is not None else self.lamb beta = beta if beta is not None else self.beta sq3 = 1.732050807568877193 matern32kernel = lamb * (1.0 + sq3 * dist / beta) * np.exp(-sq3 * dist / beta) kernel = matern32kernel if x1 is None or x2 is None: similkernel = kernel else: l1 = x1[:, -1] l2 = x2[:, -1] # Initialize the similarity kernel matrix as all zeros (or ones) similkernel = np.zeros((len(l1), len(l2))) # Loop through all the last elements of x1 and x2 for i in range(len(l1)): for j in range(len(l2)): if l1[i] == l2[j]: # If tasks are the same, apply normal kernel, sqexpo or matern similkernel[i, j] = kernel[i, j] else: similkernel[i, j] = 0 # Otherwise, set similarity to 0 return similkernel
[docs] def print(self, file=sys.stdout): print(" Nonstationary Multi Task Kernel", file=file)
def __call__(self, dist, lamb, beta, x1=None, x2=None): return self.evalkernel(dist, lamb, beta, x1, x2)