rusty_machine/learning/toolkit/kernel.rs
1//! Module for kernels
2//!
3//! Currently used within Gaussian Processes and SVMs.
4
5use std::ops::{Add, Mul};
6
7use linalg::Vector;
8use linalg::Metric;
9use rulinalg::utils;
10
11/// The Kernel trait
12///
13/// Requires a function mapping two vectors to a scalar.
14pub trait Kernel {
15 /// The kernel function.
16 ///
17 /// Takes two equal length slices and returns a scalar.
18 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64;
19}
20
21/// The sum of two kernels
22///
23/// This struct should not be directly instantiated but instead
24/// is created when we add two kernels together.
25///
26/// Note that it will be more efficient to implement the final kernel
27/// manually yourself. However this provides an easy mechanism to test
28/// different combinations.
29///
30/// # Examples
31///
32/// ```
33/// use rusty_machine::learning::toolkit::kernel::{Kernel, Polynomial, HyperTan, KernelArith};
34///
35/// let poly_ker = Polynomial::new(1f64,2f64,3f64);
36/// let hypert_ker = HyperTan::new(1f64,2.5);
37///
38/// let poly_plus_hypert_ker = KernelArith(poly_ker) + KernelArith(hypert_ker);
39///
40/// println!("{0}", poly_plus_hypert_ker.kernel(&[1f64,2f64,3f64],
41/// &[3f64,1f64,2f64]));
42/// ```
43#[derive(Debug)]
44pub struct KernelSum<T, U>
45 where T: Kernel,
46 U: Kernel
47{
48 k1: T,
49 k2: U,
50}
51
52/// Computes the sum of the two associated kernels.
53impl<T, U> Kernel for KernelSum<T, U>
54 where T: Kernel,
55 U: Kernel
56{
57 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
58 self.k1.kernel(x1, x2) + self.k2.kernel(x1, x2)
59 }
60}
61
62/// The pointwise product of two kernels
63///
64/// This struct should not be directly instantiated but instead
65/// is created when we multiply two kernels together.
66///
67/// Note that it will be more efficient to implement the final kernel
68/// manually yourself. However this provides an easy mechanism to test
69/// different combinations.
70///
71/// # Examples
72///
73/// ```
74/// use rusty_machine::learning::toolkit::kernel::{Kernel, Polynomial, HyperTan, KernelArith};
75///
76/// let poly_ker = Polynomial::new(1f64,2f64,3f64);
77/// let hypert_ker = HyperTan::new(1f64,2.5);
78///
79/// let poly_plus_hypert_ker = KernelArith(poly_ker) * KernelArith(hypert_ker);
80///
81/// println!("{0}", poly_plus_hypert_ker.kernel(&[1f64,2f64,3f64],
82/// &[3f64,1f64,2f64]));
83/// ```
84#[derive(Debug)]
85pub struct KernelProd<T, U>
86 where T: Kernel,
87 U: Kernel
88{
89 k1: T,
90 k2: U,
91}
92
93/// Computes the product of the two associated kernels.
94impl<T, U> Kernel for KernelProd<T, U>
95 where T: Kernel,
96 U: Kernel
97{
98 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
99 self.k1.kernel(x1, x2) * self.k2.kernel(x1, x2)
100 }
101}
102
103/// A wrapper tuple struct used for kernel arithmetic
104#[derive(Debug)]
105pub struct KernelArith<K: Kernel>(pub K);
106
107impl<T: Kernel, U: Kernel> Add<KernelArith<T>> for KernelArith<U> {
108 type Output = KernelSum<U, T>;
109
110 fn add(self, ker: KernelArith<T>) -> KernelSum<U, T> {
111 KernelSum {
112 k1: self.0,
113 k2: ker.0,
114 }
115 }
116}
117
118impl<T: Kernel, U: Kernel> Mul<KernelArith<T>> for KernelArith<U> {
119 type Output = KernelProd<U, T>;
120
121 fn mul(self, ker: KernelArith<T>) -> KernelProd<U, T> {
122 KernelProd {
123 k1: self.0,
124 k2: ker.0,
125 }
126 }
127}
128
129/// The Linear Kernel
130///
131/// k(x,y) = x<sup>T</sup>y + c
132#[derive(Clone, Copy, Debug)]
133pub struct Linear {
134 /// Constant term added to inner product.
135 pub c: f64,
136}
137
138impl Linear {
139 /// Constructs a new Linear Kernel.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// use rusty_machine::learning::toolkit::kernel;
145 /// use rusty_machine::learning::toolkit::kernel::Kernel;
146 ///
147 /// let ker = kernel::Linear::new(5.0);
148 ///
149 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
150 /// ```
151 pub fn new(c: f64) -> Linear {
152 Linear { c: c }
153 }
154}
155
156/// Constructs the default Linear Kernel
157///
158/// The defaults are:
159///
160/// - c = 0
161impl Default for Linear {
162 fn default() -> Linear {
163 Linear { c: 0f64 }
164 }
165}
166
167impl Kernel for Linear {
168 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
169 utils::dot(x1, x2) + self.c
170 }
171}
172
173/// The Polynomial Kernel
174///
175/// k(x,y) = (αx<sup>T</sup>y + c)<sup>d</sup>
176#[derive(Clone, Copy, Debug)]
177pub struct Polynomial {
178 /// Scaling of the inner product.
179 pub alpha: f64,
180 /// Constant added to inner product.
181 pub c: f64,
182 /// The power to raise the sum to.
183 pub d: f64,
184}
185
186impl Polynomial {
187 /// Constructs a new Polynomial Kernel.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// use rusty_machine::learning::toolkit::kernel;
193 /// use rusty_machine::learning::toolkit::kernel::Kernel;
194 ///
195 /// // Constructs a new polynomial with alpha = 1, c = 0, d = 2.
196 /// let ker = kernel::Polynomial::new(1.0, 0.0, 2.0);
197 ///
198 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
199 /// ```
200 pub fn new(alpha: f64, c: f64, d: f64) -> Polynomial {
201 Polynomial {
202 alpha: alpha,
203 c: c,
204 d: d,
205 }
206 }
207}
208
209/// Construct a new polynomial kernel.
210///
211/// The defaults are:
212///
213/// - alpha = 1
214/// - c = 0
215/// - d = 1
216impl Default for Polynomial {
217 fn default() -> Polynomial {
218 Polynomial {
219 alpha: 1f64,
220 c: 0f64,
221 d: 1f64,
222 }
223 }
224}
225
226impl Kernel for Polynomial {
227 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
228 (self.alpha * utils::dot(x1, x2) + self.c).powf(self.d)
229 }
230}
231
232/// Squared exponential kernel
233///
234/// Equivalently a gaussian function.
235///
236/// k(x,y) = A _exp_(-||x-y||<sup>2</sup> / 2l<sup>2</sup>)
237///
238/// Where A is the amplitude and l the length scale.
239#[derive(Clone, Copy, Debug)]
240pub struct SquaredExp {
241 /// The length scale of the kernel.
242 pub ls: f64,
243 /// The amplitude of the kernel.
244 pub ampl: f64,
245}
246
247impl SquaredExp {
248 /// Construct a new squared exponential kernel.
249 ///
250 /// # Examples
251 ///
252 /// ```
253 /// use rusty_machine::learning::toolkit::kernel;
254 /// use rusty_machine::learning::toolkit::kernel::Kernel;
255 ///
256 /// // Construct a kernel with lengthscale 2 and amplitude 1.
257 /// let ker = kernel::SquaredExp::new(2f64, 1f64);
258 ///
259 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
260 /// ```
261 pub fn new(ls: f64, ampl: f64) -> SquaredExp {
262 SquaredExp {
263 ls: ls,
264 ampl: ampl,
265 }
266 }
267}
268
269/// Constructs the default Squared Exp kernel.
270///
271/// The defaults are:
272///
273/// - ls = 1
274/// - ampl = 1
275impl Default for SquaredExp {
276 fn default() -> SquaredExp {
277 SquaredExp {
278 ls: 1f64,
279 ampl: 1f64,
280 }
281 }
282}
283
284impl Kernel for SquaredExp {
285 /// The squared exponential kernel function.
286 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
287 assert_eq!(x1.len(), x2.len());
288
289 let diff = Vector::new(x1.to_vec()) - Vector::new(x2.to_vec());
290
291 let x = -diff.dot(&diff) / (2f64 * self.ls * self.ls);
292 (self.ampl * x.exp())
293 }
294}
295
296/// The Exponential Kernel
297///
298/// k(x,y) = A _exp_(-||x-y|| / 2l<sup>2</sup>)
299///
300/// Where A is the amplitude and l is the length scale.
301#[derive(Clone, Copy, Debug)]
302pub struct Exponential {
303 /// The length scale of the kernel.
304 pub ls: f64,
305 /// The amplitude of the kernel.
306 pub ampl: f64,
307}
308
309impl Exponential {
310 /// Construct a new squared exponential kernel.
311 ///
312 /// # Examples
313 ///
314 /// ```
315 /// use rusty_machine::learning::toolkit::kernel;
316 /// use rusty_machine::learning::toolkit::kernel::Kernel;
317 ///
318 /// // Construct a kernel with lengthscale 2 and amplitude 1.
319 /// let ker = kernel::Exponential::new(2f64, 1f64);
320 ///
321 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
322 /// ```
323 pub fn new(ls: f64, ampl: f64) -> Exponential {
324 Exponential {
325 ls: ls,
326 ampl: ampl,
327 }
328 }
329}
330
331/// Constructs the default Exponential kernel.
332///
333/// The defaults are:
334///
335/// - ls = 1
336/// - amplitude = 1
337impl Default for Exponential {
338 fn default() -> Exponential {
339 Exponential {
340 ls: 1f64,
341 ampl: 1f64,
342 }
343 }
344}
345
346impl Kernel for Exponential {
347 /// The squared exponential kernel function.
348 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
349 assert_eq!(x1.len(), x2.len());
350
351 let diff = Vector::new(x1.to_vec()) - Vector::new(x2.to_vec());
352
353 let x = -diff.norm() / (2f64 * self.ls * self.ls);
354 (self.ampl * x.exp())
355 }
356}
357
358/// The Hyperbolic Tangent Kernel.
359///
360/// ker(x,y) = _tanh_(αx<sup>T</sup>y + c)
361#[derive(Clone, Copy, Debug)]
362pub struct HyperTan {
363 /// The scaling of the inner product.
364 pub alpha: f64,
365 /// The constant to add to the inner product.
366 pub c: f64,
367}
368
369impl HyperTan {
370 /// Constructs a new Hyperbolic Tangent Kernel.
371 ///
372 /// # Examples
373 ///
374 /// ```
375 /// use rusty_machine::learning::toolkit::kernel;
376 /// use rusty_machine::learning::toolkit::kernel::Kernel;
377 ///
378 /// // Construct a kernel with alpha = 1, c = 2.
379 /// let ker = kernel::HyperTan::new(1.0, 2.0);
380 ///
381 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
382 /// ```
383 pub fn new(alpha: f64, c: f64) -> HyperTan {
384 HyperTan {
385 alpha: alpha,
386 c: c,
387 }
388 }
389}
390
391/// Constructs a default Hyperbolic Tangent Kernel.
392///
393/// The defaults are:
394///
395/// - alpha = 1
396/// - c = 0
397impl Default for HyperTan {
398 fn default() -> HyperTan {
399 HyperTan {
400 alpha: 1f64,
401 c: 0f64,
402 }
403 }
404}
405
406impl Kernel for HyperTan {
407 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
408 (self.alpha * utils::dot(x1, x2) + self.c).tanh()
409 }
410}
411
412/// The Multiquadric Kernel.
413///
414/// k(x,y) = _sqrt_(||x-y||<sup>2</sup> + c<sup>2</sup>)
415#[derive(Clone, Copy, Debug)]
416pub struct Multiquadric {
417 /// Constant added to square of difference.
418 pub c: f64,
419}
420
421impl Multiquadric {
422 /// Constructs a new Multiquadric Kernel.
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use rusty_machine::learning::toolkit::kernel;
428 /// use rusty_machine::learning::toolkit::kernel::Kernel;
429 ///
430 /// // Construct a kernel with c = 2.
431 /// let ker = kernel::Multiquadric::new(2.0);
432 ///
433 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
434 /// ```
435 pub fn new(c: f64) -> Multiquadric {
436 Multiquadric { c: c }
437 }
438}
439
440/// Constructs a default Multiquadric Kernel.
441///
442/// The defaults are:
443///
444/// - c = 0
445impl Default for Multiquadric {
446 fn default() -> Multiquadric {
447 Multiquadric { c: 0f64 }
448 }
449}
450
451impl Kernel for Multiquadric {
452 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
453 assert_eq!(x1.len(), x2.len());
454
455 let diff = Vector::new(x1.to_vec()) - Vector::new(x2.to_vec());
456
457 diff.norm().hypot(self.c)
458 }
459}
460
461/// The Rational Quadratic Kernel.
462///
463/// k(x,y) = (1 + ||x-y||<sup>2</sup> / (2αl<sup>2</sup>))<sup>-α</sup>
464#[derive(Clone, Copy, Debug)]
465pub struct RationalQuadratic {
466 /// Controls inverse power and difference scale.
467 pub alpha: f64,
468 /// Length scale controls scale of difference.
469 pub ls: f64,
470}
471
472impl RationalQuadratic {
473 /// Constructs a new Rational Quadratic Kernel.
474 ///
475 /// # Examples
476 ///
477 /// ```
478 /// use rusty_machine::learning::toolkit::kernel;
479 /// use rusty_machine::learning::toolkit::kernel::Kernel;
480 ///
481 /// // Construct a kernel with alpha = 2, ls = 2.
482 /// let ker = kernel::RationalQuadratic::new(2.0, 2.0);
483 ///
484 /// println!("{0}", ker.kernel(&[1.,2.,3.], &[3.,4.,5.]));
485 /// ```
486 pub fn new(alpha: f64, ls: f64) -> RationalQuadratic {
487 RationalQuadratic {
488 alpha: alpha,
489 ls: ls,
490 }
491 }
492}
493
494/// The default Rational Qaudratic Kernel.
495///
496/// The defaults are:
497///
498/// - alpha = 1
499/// - ls = 1
500impl Default for RationalQuadratic {
501 fn default() -> RationalQuadratic {
502 RationalQuadratic {
503 alpha: 1f64,
504 ls: 1f64,
505 }
506 }
507}
508
509impl Kernel for RationalQuadratic {
510 fn kernel(&self, x1: &[f64], x2: &[f64]) -> f64 {
511 let diff = Vector::new(x1.to_vec()) - Vector::new(x2.to_vec());
512
513 (1f64 + diff.dot(&diff) / (2f64 * self.alpha * self.ls * self.ls)).powf(-self.alpha)
514 }
515}