use crate::cache::{Cache, Qfloat};
use crate::kernel::Kernel;
use crate::types::{SvmNode, SvmParameter};
pub trait QMatrix {
fn get_q(&mut self, i: usize, len: usize) -> &[Qfloat];
fn get_qd(&self) -> &[f64];
fn swap_index(&mut self, i: usize, j: usize);
}
pub struct SvcQ<'a> {
kernel: Kernel<'a>,
cache: Cache,
y: Vec<i8>,
qd: Vec<f64>,
}
impl<'a> SvcQ<'a> {
pub fn new(x: &'a [Vec<SvmNode>], param: &SvmParameter, y: &[i8]) -> Self {
let l = x.len();
let kernel = Kernel::new(x, param);
let cache = Cache::new(l, (param.cache_size * 1048576.0) as usize);
let qd: Vec<f64> = (0..l).map(|i| kernel.evaluate(i, i)).collect();
let y = y.to_vec();
Self { kernel, cache, y, qd }
}
}
impl<'a> QMatrix for SvcQ<'a> {
fn get_q(&mut self, i: usize, len: usize) -> &[Qfloat] {
let (data, start) = self.cache.get_data(i, len);
if start < len {
let yi = self.y[i] as f64;
for j in start..len {
let kval = self.kernel.evaluate(i, j);
data[j] = (yi * self.y[j] as f64 * kval) as Qfloat;
}
}
&data[..len]
}
fn get_qd(&self) -> &[f64] {
&self.qd
}
fn swap_index(&mut self, i: usize, j: usize) {
self.cache.swap_index(i, j);
self.kernel.swap_index(i, j);
self.y.swap(i, j);
self.qd.swap(i, j);
}
}
pub struct OneClassQ<'a> {
kernel: Kernel<'a>,
cache: Cache,
qd: Vec<f64>,
}
impl<'a> OneClassQ<'a> {
pub fn new(x: &'a [Vec<SvmNode>], param: &SvmParameter) -> Self {
let l = x.len();
let kernel = Kernel::new(x, param);
let cache = Cache::new(l, (param.cache_size * 1048576.0) as usize);
let qd: Vec<f64> = (0..l).map(|i| kernel.evaluate(i, i)).collect();
Self { kernel, cache, qd }
}
}
impl<'a> QMatrix for OneClassQ<'a> {
fn get_q(&mut self, i: usize, len: usize) -> &[Qfloat] {
let (data, start) = self.cache.get_data(i, len);
if start < len {
for j in start..len {
data[j] = self.kernel.evaluate(i, j) as Qfloat;
}
}
&data[..len]
}
fn get_qd(&self) -> &[f64] {
&self.qd
}
fn swap_index(&mut self, i: usize, j: usize) {
self.cache.swap_index(i, j);
self.kernel.swap_index(i, j);
self.qd.swap(i, j);
}
}
pub struct SvrQ<'a> {
kernel: Kernel<'a>,
cache: Cache,
l: usize,
sign: Vec<i8>,
index: Vec<usize>,
qd: Vec<f64>,
buffer: [Vec<Qfloat>; 2],
next_buffer: usize,
}
impl<'a> SvrQ<'a> {
pub fn new(x: &'a [Vec<SvmNode>], param: &SvmParameter) -> Self {
let l = x.len();
let kernel = Kernel::new(x, param);
let cache = Cache::new(l, (param.cache_size * 1048576.0) as usize);
let mut sign = vec![0i8; 2 * l];
let mut index = vec![0usize; 2 * l];
let mut qd = vec![0.0f64; 2 * l];
for k in 0..l {
sign[k] = 1;
sign[k + l] = -1;
index[k] = k;
index[k + l] = k;
let kk = kernel.evaluate(k, k);
qd[k] = kk;
qd[k + l] = kk;
}
let buffer = [vec![0.0 as Qfloat; 2 * l], vec![0.0 as Qfloat; 2 * l]];
Self {
kernel,
cache,
l,
sign,
index,
qd,
buffer,
next_buffer: 0,
}
}
}
impl<'a> QMatrix for SvrQ<'a> {
fn get_q(&mut self, i: usize, len: usize) -> &[Qfloat] {
let real_i = self.index[i];
let l = self.l;
let (data, start) = self.cache.get_data(real_i, l);
if start < l {
for j in start..l {
data[j] = self.kernel.evaluate(real_i, j) as Qfloat;
}
}
let buf_idx = self.next_buffer;
self.next_buffer = 1 - self.next_buffer;
let si = self.sign[i] as f32;
let buf = &mut self.buffer[buf_idx];
for j in 0..len {
buf[j] = si * (self.sign[j] as f32) * data[self.index[j]];
}
&self.buffer[buf_idx][..len]
}
fn get_qd(&self) -> &[f64] {
&self.qd
}
fn swap_index(&mut self, i: usize, j: usize) {
self.sign.swap(i, j);
self.index.swap(i, j);
self.qd.swap(i, j);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{KernelType, SvmNode, SvmParameter};
fn make_nodes(pairs: &[(i32, f64)]) -> Vec<SvmNode> {
pairs.iter().map(|&(i, v)| SvmNode { index: i, value: v }).collect()
}
fn default_rbf_param() -> SvmParameter {
SvmParameter {
kernel_type: KernelType::Rbf,
gamma: 0.5,
cache_size: 1.0,
..Default::default()
}
}
#[test]
fn svc_q_diagonal_equals_one_for_rbf() {
let data = vec![
make_nodes(&[(1, 1.0), (2, 0.0)]),
make_nodes(&[(1, 0.0), (2, 1.0)]),
];
let y = vec![1i8, -1i8];
let param = default_rbf_param();
let q = SvcQ::new(&data, ¶m, &y);
for &d in q.get_qd() {
assert!((d - 1.0).abs() < 1e-15);
}
}
#[test]
fn svc_q_symmetry_and_sign() {
let data = vec![
make_nodes(&[(1, 1.0)]),
make_nodes(&[(1, 2.0)]),
make_nodes(&[(1, 3.0)]),
];
let y = vec![1i8, -1i8, 1i8];
let param = default_rbf_param();
let mut q = SvcQ::new(&data, ¶m, &y);
let l = data.len();
let mut matrix = vec![vec![0.0f32; l]; l];
for i in 0..l {
let row = q.get_q(i, l).to_vec();
for j in 0..l {
matrix[i][j] = row[j];
}
}
for i in 0..l {
for j in 0..l {
assert!(
(matrix[i][j] - matrix[j][i]).abs() < 1e-6,
"Q[{},{}]={} != Q[{},{}]={}",
i, j, matrix[i][j], j, i, matrix[j][i]
);
}
}
assert!(matrix[0][1] < 0.0);
assert!(matrix[0][2] > 0.0);
}
#[test]
fn one_class_q_no_sign_scaling() {
let data = vec![
make_nodes(&[(1, 1.0)]),
make_nodes(&[(1, 2.0)]),
];
let param = default_rbf_param();
let mut q = OneClassQ::new(&data, ¶m);
let row = q.get_q(0, 2);
assert!(row[0] > 0.0);
assert!(row[1] > 0.0);
assert!((row[0] - 1.0).abs() < 1e-6);
}
#[test]
fn svr_q_double_buffer() {
let data = vec![
make_nodes(&[(1, 1.0)]),
make_nodes(&[(1, 2.0)]),
];
let param = default_rbf_param();
let mut q = SvrQ::new(&data, ¶m);
let l2 = 2 * data.len();
let row0 = q.get_q(0, l2).to_vec();
let row1 = q.get_q(1, l2).to_vec();
assert!(row0.iter().any(|&v| v != 0.0));
assert!(row1.iter().any(|&v| v != 0.0));
assert!((row0[2] - (-1.0)).abs() < 1e-6, "Q[0][2] = {}", row0[2]);
}
}