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}