use scirs2_core::ndarray::{Array2, ArrayView1};
use sklears_core::error::Result as SklResult;
#[allow(non_snake_case)]
pub trait Kernel: std::fmt::Debug + Send + Sync {
fn compute_kernel_matrix(
&self,
X1: &Array2<f64>,
X2: Option<&Array2<f64>>,
) -> SklResult<Array2<f64>>;
fn kernel(&self, x1: &ArrayView1<f64>, x2: &ArrayView1<f64>) -> f64;
fn get_params(&self) -> Vec<f64>;
fn set_params(&mut self, params: &[f64]) -> SklResult<()>;
fn clone_box(&self) -> Box<dyn Kernel>;
fn compute_kernel_gradient(
&self,
X1: &Array2<f64>,
X2: Option<&Array2<f64>>,
) -> SklResult<Vec<Array2<f64>>> {
let X2 = X2.unwrap_or(X1);
let params = self.get_params();
let n_params = params.len();
let mut gradients = Vec::with_capacity(n_params);
let epsilon = 1e-8;
let base_matrix = self.compute_kernel_matrix(X1, Some(X2))?;
for i in 0..n_params {
let mut kernel_copy = self.clone_box();
let mut params_plus = params.clone();
params_plus[i] += epsilon;
kernel_copy.set_params(¶ms_plus)?;
let matrix_plus = kernel_copy.compute_kernel_matrix(X1, Some(X2))?;
let gradient = (&matrix_plus - &base_matrix) / epsilon;
gradients.push(gradient);
}
Ok(gradients)
}
fn kernel_gradient(&self, x1: &ArrayView1<f64>, x2: &ArrayView1<f64>) -> SklResult<Vec<f64>> {
let params = self.get_params();
let n_params = params.len();
let mut gradients = Vec::with_capacity(n_params);
let epsilon = 1e-8;
let base_value = self.kernel(x1, x2);
for i in 0..n_params {
let mut kernel_copy = self.clone_box();
let mut params_plus = params.clone();
params_plus[i] += epsilon;
kernel_copy.set_params(¶ms_plus)?;
let value_plus = kernel_copy.kernel(x1, x2);
let gradient = (value_plus - base_value) / epsilon;
gradients.push(gradient);
}
Ok(gradients)
}
}
impl Clone for Box<dyn Kernel> {
fn clone(&self) -> Self {
self.clone_box()
}
}