sklears_gaussian_process/
kernel_trait.rs

1//! Core kernel trait for Gaussian Process models
2//!
3//! This module defines the fundamental Kernel trait that all kernel implementations must follow.
4//! All implementations comply with SciRS2 Policy (never use rand/ndarray directly).
5
6use scirs2_core::ndarray::{Array2, ArrayView1};
7use sklears_core::error::Result as SklResult;
8
9/// Kernel trait for Gaussian Process models
10///
11/// All kernel functions implement this trait to provide consistent interface
12/// for computing kernel matrices, gradients, and parameter management.
13pub trait Kernel: std::fmt::Debug + Send + Sync {
14    /// Compute the kernel matrix between X1 and X2
15    fn compute_kernel_matrix(
16        &self,
17        X1: &Array2<f64>,
18        X2: Option<&Array2<f64>>,
19    ) -> SklResult<Array2<f64>>;
20
21    /// Compute the kernel between two points
22    fn kernel(&self, x1: &ArrayView1<f64>, x2: &ArrayView1<f64>) -> f64;
23
24    /// Get hyperparameters
25    fn get_params(&self) -> Vec<f64>;
26
27    /// Set hyperparameters
28    fn set_params(&mut self, params: &[f64]) -> SklResult<()>;
29
30    /// Clone the kernel
31    fn clone_box(&self) -> Box<dyn Kernel>;
32
33    /// Compute the gradient of the kernel matrix with respect to hyperparameters
34    /// Returns a vector of gradient matrices, one for each hyperparameter
35    fn compute_kernel_gradient(
36        &self,
37        X1: &Array2<f64>,
38        X2: Option<&Array2<f64>>,
39    ) -> SklResult<Vec<Array2<f64>>> {
40        // Default implementation using finite differences
41        let X2 = X2.unwrap_or(X1);
42        let params = self.get_params();
43        let n_params = params.len();
44        let mut gradients = Vec::with_capacity(n_params);
45
46        let epsilon = 1e-8;
47        let base_matrix = self.compute_kernel_matrix(X1, Some(X2))?;
48
49        for i in 0..n_params {
50            let mut kernel_copy = self.clone_box();
51            let mut params_plus = params.clone();
52            params_plus[i] += epsilon;
53            kernel_copy.set_params(&params_plus)?;
54
55            let matrix_plus = kernel_copy.compute_kernel_matrix(X1, Some(X2))?;
56            let gradient = (&matrix_plus - &base_matrix) / epsilon;
57            gradients.push(gradient);
58        }
59
60        Ok(gradients)
61    }
62
63    /// Compute the gradient of the kernel function with respect to hyperparameters
64    /// for a single pair of points
65    fn kernel_gradient(&self, x1: &ArrayView1<f64>, x2: &ArrayView1<f64>) -> SklResult<Vec<f64>> {
66        // Default implementation using finite differences
67        let params = self.get_params();
68        let n_params = params.len();
69        let mut gradients = Vec::with_capacity(n_params);
70
71        let epsilon = 1e-8;
72        let base_value = self.kernel(x1, x2);
73
74        for i in 0..n_params {
75            let mut kernel_copy = self.clone_box();
76            let mut params_plus = params.clone();
77            params_plus[i] += epsilon;
78            kernel_copy.set_params(&params_plus)?;
79
80            let value_plus = kernel_copy.kernel(x1, x2);
81            let gradient = (value_plus - base_value) / epsilon;
82            gradients.push(gradient);
83        }
84
85        Ok(gradients)
86    }
87}
88
89impl Clone for Box<dyn Kernel> {
90    fn clone(&self) -> Self {
91        self.clone_box()
92    }
93}