Kernel Composite#

composite #

CLASS DESCRIPTION
CompositeKernel

Abstract base class for composite kernels that combine multiple kernels.

SumKernel

Sum of multiple kernels.

ProductKernel

Product of multiple kernels.

ScaledKernel

Scaled version of a kernel.

FUNCTION DESCRIPTION
sum_kernels

Create a sum of kernels (functional interface).

product_kernels

Create a product of kernels (functional interface).

scale_kernel

Create a scaled kernel (functional interface).

Classes#

CompositeKernel(kernels: list[Kernel], learnable: bool = False, learnable_log: bool = True, normalization=None, dtype: torch.dtype = torch.float32, **kwargs) #

Bases: Kernel, ABC

Abstract base class for composite kernels that combine multiple kernels.

Provides a framework for kernel arithmetic operations such as addition, multiplication, and weighted combinations of kernel functions.

PARAMETER DESCRIPTION
kernels

List of kernel functions to combine.

TYPE: list[Kernel]

learnable

Whether composite parameters should be learnable.

TYPE: bool, by default False DEFAULT: False

learnable_log

Whether to parameterize learnable parameters in log space.

TYPE: bool, by default True DEFAULT: True

normalization

Kernel normalization instance for the final combined kernel.

TYPE: KernelNormalizer | None, by default None DEFAULT: None

dtype

Data type for computations.

TYPE: torch.dtype, by default torch.float32 DEFAULT: float32

**kwargs

Additional arguments passed to Kernel base class.

DEFAULT: {}

ATTRIBUTE DESCRIPTION
kernels

List of kernel functions.

TYPE: ModuleList

METHOD DESCRIPTION
forward_step

Compute composite kernel matrix from distance matrix.

Source code in spectre/kernel/composite.py
def __init__(
    self,
    kernels: list[Kernel],
    learnable: bool = False,
    learnable_log: bool = True,
    normalization=None,
    dtype: torch.dtype = torch.float32,
    **kwargs,
):
    super().__init__(
        learnable=learnable,
        learnable_log=learnable_log,
        normalization=normalization,
        dtype=dtype,
        **kwargs,
    )

    # Validate kernels
    if not isinstance(kernels, list) or len(kernels) == 0:
        raise ValueError("Kernels must be a non-empty list of Kernel objects")

    for i, kernel in enumerate(kernels):
        if not isinstance(kernel, Kernel):
            raise TypeError(
                f"All kernels must be Kernel objects, got {type(kernel).__name__} "
                f"at index {i}."
            )

    self.kernels = torch.nn.ModuleList(kernels)
Functions#
forward_step(D: torch.Tensor, weights: torch.Tensor | None = None, **kwargs) -> torch.Tensor #

Compute composite kernel matrix from distance matrix.

PARAMETER DESCRIPTION
D

Pairwise distance matrix of shape (n_samples, n_samples).

TYPE: Tensor

weights

Sample weights for normalization.

TYPE: torch.Tensor | None, by default None DEFAULT: None

RETURNS DESCRIPTION
Tensor

Combined kernel matrix.

Source code in spectre/kernel/composite.py
def forward_step(
    self, D: torch.Tensor, weights: torch.Tensor | None = None, **kwargs
) -> torch.Tensor:
    """
    Compute composite kernel matrix from distance matrix.

    Parameters
    ----------
    D : torch.Tensor
        Pairwise distance matrix of shape (n_samples, n_samples).

    weights : torch.Tensor | None, by default None
        Sample weights for normalization.

    Returns
    -------
    torch.Tensor
        Combined kernel matrix.
    """
    # Compute all individual kernel matrices
    kernel_matrices = []
    for kernel in self.kernels:
        # Disable normalization for individual kernels (apply only to final result)
        original_normalization = kernel.normalization
        kernel.normalization = None

        K = kernel.forward_step(D, weights)
        kernel_matrices.append(K)

        # Restore original normalization setting
        kernel.normalization = original_normalization

    # Combine kernel matrices using subclass-specific logic
    combined_kernel = self._combine_kernels(kernel_matrices)

    return combined_kernel

SumKernel(kernels: list[Kernel], weights: list[float] | None = None, learnable: bool = False, learnable_log: bool = True, **kwargs) #

Bases: CompositeKernel

Sum of multiple kernels.

Combines kernels by element-wise addition, optionally with learnable weights.

PARAMETER DESCRIPTION
kernels

List of kernel functions to sum.

TYPE: list[Kernel]

weights

Weights for each kernel. If None, uses uniform weights.

TYPE: list[float] | None, by default None DEFAULT: None

learnable

Whether weights should be learnable.

TYPE: bool, by default False DEFAULT: False

learnable_log

Whether to parameterize weights in log space.

TYPE: bool, by default True DEFAULT: True

normalize

Whether to apply normalization.

TYPE: bool, by default True

**kwargs

Additional arguments passed to CompositeKernel.

DEFAULT: {}

Examples:

>>> from spectre.kernel import GaussianKernel, PolynomialKernel
>>> k1 = GaussianKernel(eps=1.0)
>>> k2 = PolynomialKernel(degree=2)
>>> sum_kernel = SumKernel([k1, k2], weights=[0.7, 0.3])
>>> X = torch.randn(50, 3)
>>> K = sum_kernel(X)
METHOD DESCRIPTION
get_kernel_params

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def __init__(
    self,
    kernels: list[Kernel],
    weights: list[float] | None = None,
    learnable: bool = False,
    learnable_log: bool = True,
    **kwargs,
):
    super().__init__(
        kernels=kernels, learnable=learnable, learnable_log=learnable_log, **kwargs
    )

    n_kernels = len(kernels)
    self.n_kernels = n_kernels

    if weights is None:
        weights = [1.0] * n_kernels
    elif len(weights) != n_kernels:
        raise ValueError(
            f"Number of weights ({len(weights)}) must match number of kernels ({n_kernels})"
        )

    for i, w in enumerate(weights):
        if not isinstance(w, (int, float)):
            raise TypeError(f"Weight {i} must be numeric, got {type(w).__name__}")
        check_in_interval(float(w), "[0, inf)")

    # Initialize learnable or fixed weights
    if learnable:
        if learnable_log:
            # Log-parameterization for positivity
            log_weights = torch.log(torch.tensor(weights, dtype=self.dtype))
            self.log_weights = torch.nn.Parameter(log_weights)
        else:
            self.weight_params = torch.nn.Parameter(
                torch.tensor(weights, dtype=self.dtype)
            )
    else:
        self.register_buffer(
            "fixed_weights", torch.tensor(weights, dtype=self.dtype)
        )
Functions#
get_kernel_params() -> dict[str, torch.Tensor] #

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def get_kernel_params(self) -> dict[str, torch.Tensor]:
    """Return current kernel parameters."""
    return {"weights": self._get_weights()}

ProductKernel(kernels: list[Kernel], exponents: list[float] | None = None, learnable: bool = False, learnable_log: bool = True, **kwargs) #

Bases: CompositeKernel

Product of multiple kernels.

Combines kernels by element-wise multiplication, optionally with learnable exponents.

PARAMETER DESCRIPTION
kernels

List of kernel functions to multiply.

TYPE: list[Kernel]

exponents

Exponents for each kernel. If None, uses exponents of 1.0.

TYPE: list[float] | None, by default None DEFAULT: None

learnable

Whether exponents should be learnable.

TYPE: bool, by default False DEFAULT: False

learnable_log

Whether to parameterize exponents in log space.

TYPE: bool, by default True DEFAULT: True

normalize

Whether to apply normalization.

TYPE: bool, by default True

**kwargs

Additional arguments passed to CompositeKernel.

DEFAULT: {}

Examples:

>>> k1 = GaussianKernel(eps=1.0)
>>> k2 = PolynomialKernel(degree=2)
>>> prod_kernel = ProductKernel([k1, k2], exponents=[1.0, 0.5])
>>> X = torch.randn(50, 3)
>>> K = prod_kernel(X)
METHOD DESCRIPTION
get_kernel_params

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def __init__(
    self,
    kernels: list[Kernel],
    exponents: list[float] | None = None,
    learnable: bool = False,
    learnable_log: bool = True,
    **kwargs,
):
    super().__init__(
        kernels=kernels, learnable=learnable, learnable_log=learnable_log, **kwargs
    )

    n_kernels = len(kernels)

    # Set up exponents
    if exponents is None:
        exponents = [1.0] * n_kernels
    elif len(exponents) != n_kernels:
        raise ValueError(
            f"Number of exponents ({len(exponents)}) must match number of kernels ({n_kernels})"
        )

    # Validate exponents
    for i, exp in enumerate(exponents):
        if not isinstance(exp, (int, float)):
            raise TypeError(
                f"Exponent {i} must be numeric, got {type(exp).__name__}"
            )
        check_in_interval(float(exp), "(0, inf)")

    self.n_kernels = n_kernels

    # Initialize learnable or fixed exponents
    if learnable:
        if learnable_log:
            log_exponents = torch.log(torch.tensor(exponents, dtype=self.dtype))
            self.log_exponents = torch.nn.Parameter(log_exponents)
        else:
            self.exponent_params = torch.nn.Parameter(
                torch.tensor(exponents, dtype=self.dtype)
            )
    else:
        self.register_buffer(
            "fixed_exponents", torch.tensor(exponents, dtype=self.dtype)
        )
Functions#
get_kernel_params() -> dict[str, torch.Tensor] #

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def get_kernel_params(self) -> dict[str, torch.Tensor]:
    """Return current kernel parameters."""
    return {"exponents": self._get_exponents()}

ScaledKernel(kernel: Kernel, scale: float = 1.0, learnable: bool = False, learnable_log: bool = True, **kwargs) #

Bases: Kernel

Scaled version of a kernel.

Applies a learnable or fixed scaling factor to a kernel.

PARAMETER DESCRIPTION
kernel

Base kernel to scale.

TYPE: Kernel

scale

Scaling factor.

TYPE: float, by default 1.0 DEFAULT: 1.0

learnable

Whether scale should be learnable.

TYPE: bool, by default False DEFAULT: False

learnable_log

Whether to parameterize scale in log space.

TYPE: bool, by default True DEFAULT: True

normalize

Whether to apply normalization.

TYPE: bool, by default True

**kwargs

Additional arguments passed to Kernel.

DEFAULT: {}

Examples:

>>> base_kernel = GaussianKernel(eps=1.0)
>>> scaled_kernel = ScaledKernel(base_kernel, scale=2.0, learnable=True)
>>> X = torch.randn(50, 3)
>>> K = scaled_kernel(X)
METHOD DESCRIPTION
forward_step

Compute scaled kernel matrix from distance matrix.

get_kernel_params

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def __init__(
    self,
    kernel: Kernel,
    scale: float = 1.0,
    learnable: bool = False,
    learnable_log: bool = True,
    **kwargs,
):
    super().__init__(learnable=learnable, learnable_log=learnable_log, **kwargs)

    if not isinstance(kernel, Kernel):
        raise TypeError(
            f"Kernel must be a Kernel object, got {type(kernel).__name__}"
        )

    if not isinstance(scale, (int, float)):
        raise TypeError(f"Scale must be numeric, got {type(scale).__name__}")
    check_in_interval(float(scale), "(0, inf)")

    self.kernel = kernel

    # Initialize learnable or fixed scale
    if learnable:
        if learnable_log:
            self.log_scale = torch.nn.Parameter(
                torch.log(torch.tensor(scale, dtype=self.dtype))
            )
        else:
            self.scale_param = torch.nn.Parameter(
                torch.tensor(scale, dtype=self.dtype)
            )
    else:
        self.register_buffer("fixed_scale", torch.tensor(scale, dtype=self.dtype))
Functions#
forward_step(D: torch.Tensor, weights: torch.Tensor | None = None, **kwargs) -> torch.Tensor #

Compute scaled kernel matrix from distance matrix.

PARAMETER DESCRIPTION
D

Pairwise distance matrix of shape (n_samples, n_samples).

TYPE: Tensor

weights

Sample weights for normalization.

TYPE: torch.Tensor | None, by default None DEFAULT: None

**kwargs

Additional keyword arguments passed to the kernel.

TYPE: dict DEFAULT: {}

RETURNS DESCRIPTION
Tensor

Scaled kernel matrix.

Source code in spectre/kernel/composite.py
def forward_step(
    self, D: torch.Tensor, weights: torch.Tensor | None = None, **kwargs
) -> torch.Tensor:
    """
    Compute scaled kernel matrix from distance matrix.

    Parameters
    ----------
    D : torch.Tensor
        Pairwise distance matrix of shape (n_samples, n_samples).

    weights : torch.Tensor | None, by default None
        Sample weights for normalization.

    **kwargs : dict
        Additional keyword arguments passed to the kernel.

    Returns
    -------
    torch.Tensor
        Scaled kernel matrix.
    """
    # Disable normalization for base kernel (apply only to final result)
    original_normalization = self.kernel.normalization
    self.kernel.normalization = None

    # Compute base kernel
    K = self.kernel.forward_step(D, weights)

    # Restore original normalization setting
    self.kernel.normalization = original_normalization

    # Apply scaling
    scale = self._get_scale()
    scaled_kernel = scale * K

    return scaled_kernel
get_kernel_params() -> dict[str, torch.Tensor] #

Return current kernel parameters.

Source code in spectre/kernel/composite.py
def get_kernel_params(self) -> dict[str, torch.Tensor]:
    """Return current kernel parameters."""
    return {"scale": self._get_scale()}

Functions#

sum_kernels(kernels: list[Kernel], weights: list[float] | None = None) -> SumKernel #

Create a sum of kernels (functional interface).

PARAMETER DESCRIPTION
kernels

List of kernels to sum.

TYPE: list[Kernel]

weights

Weights for each kernel.

TYPE: list[float] | None, by default None DEFAULT: None

RETURNS DESCRIPTION
SumKernel

Sum kernel object.

Examples:

>>> k1 = GaussianKernel(bw_method=torch.tensor(1.0))
>>> k2 = PolynomialKernel(degree=torch.tensor(2))
>>> sum_k = sum_kernels([k1, k2], weights=[0.6, 0.4])
Source code in spectre/kernel/composite.py
def sum_kernels(kernels: list[Kernel], weights: list[float] | None = None) -> SumKernel:
    """
    Create a sum of kernels (functional interface).

    Parameters
    ----------
    kernels : list[Kernel]
        List of kernels to sum.

    weights : list[float] | None, by default None
        Weights for each kernel.

    Returns
    -------
    SumKernel
        Sum kernel object.

    Examples
    --------
    >>> k1 = GaussianKernel(bw_method=torch.tensor(1.0))
    >>> k2 = PolynomialKernel(degree=torch.tensor(2))
    >>> sum_k = sum_kernels([k1, k2], weights=[0.6, 0.4])
    """
    return SumKernel(kernels=kernels, weights=weights, learnable=False)

product_kernels(kernels: list[Kernel], exponents: list[float] | None = None) -> ProductKernel #

Create a product of kernels (functional interface).

PARAMETER DESCRIPTION
kernels

List of kernels to multiply.

TYPE: list[Kernel]

exponents

Exponents for each kernel.

TYPE: list[float] | None, by default None DEFAULT: None

RETURNS DESCRIPTION
ProductKernel

Product kernel object.

Examples:

>>> k1 = GaussianKernel(bw_method=torch.tensor(1.0))
>>> k2 = PolynomialKernel(degree=torch.tensor(2))
>>> prod_k = product_kernels([k1, k2], exponents=[1.0, 0.5])
Source code in spectre/kernel/composite.py
def product_kernels(
    kernels: list[Kernel], exponents: list[float] | None = None
) -> ProductKernel:
    """
    Create a product of kernels (functional interface).

    Parameters
    ----------
    kernels : list[Kernel]
        List of kernels to multiply.

    exponents : list[float] | None, by default None
        Exponents for each kernel.

    Returns
    -------
    ProductKernel
        Product kernel object.

    Examples
    --------
    >>> k1 = GaussianKernel(bw_method=torch.tensor(1.0))
    >>> k2 = PolynomialKernel(degree=torch.tensor(2))
    >>> prod_k = product_kernels([k1, k2], exponents=[1.0, 0.5])
    """
    return ProductKernel(kernels=kernels, exponents=exponents, learnable=False)

scale_kernel(kernel: Kernel, scale: float = 1.0) -> ScaledKernel #

Create a scaled kernel (functional interface).

PARAMETER DESCRIPTION
kernel

Base kernel to scale.

TYPE: Kernel

scale

Scaling factor.

TYPE: float, by default 1.0 DEFAULT: 1.0

RETURNS DESCRIPTION
ScaledKernel

Scaled kernel object.

Examples:

>>> base_k = GaussianKernel(bw_method=torch.tensor(1.0))
>>> scaled_k = scale_kernel(base_k, scale=2.0)
Source code in spectre/kernel/composite.py
def scale_kernel(kernel: Kernel, scale: float = 1.0) -> ScaledKernel:
    """
    Create a scaled kernel (functional interface).

    Parameters
    ----------
    kernel : Kernel
        Base kernel to scale.

    scale : float, by default 1.0
        Scaling factor.

    Returns
    -------
    ScaledKernel
        Scaled kernel object.

    Examples
    --------
    >>> base_k = GaussianKernel(bw_method=torch.tensor(1.0))
    >>> scaled_k = scale_kernel(base_k, scale=2.0)
    """
    return ScaledKernel(kernel=kernel, scale=scale, learnable=False)