extern crate num;
extern crate rand;
use self::rand::{thread_rng, Rng};
use ops::*;
use regression::*;
use matrix::Matrix;
use opencv::{Window, RgbImage};
use octave::builder;
#[derive(Copy, Clone)]
pub struct OptParams<T: Clone> {
pub alpha: Option<T>,
pub iter: Option<usize>,
pub eps: Option<T>,
}
impl <T: Clone> OptParams<T> {
pub fn new() -> OptParams<T> {
OptParams {
alpha: None,
iter: None,
eps: None,
}
}
pub fn alpha(&self, val: T) -> OptParams<T> {
OptParams {
alpha: Some(val),
iter: self.iter.clone(),
eps: self.eps.clone(),
}
}
pub fn iter(&self, val: usize) -> OptParams<T> {
OptParams {
alpha: self.alpha.clone(),
iter: Some(val),
eps: self.eps.clone(),
}
}
pub fn eps(&self, val: T) -> OptParams<T> {
OptParams {
alpha: self.alpha.clone(),
iter: self.iter.clone(),
eps: Some(val),
}
}
}
pub fn empty_opts() -> OptParams<f64> {
OptParams::new()
}
pub struct OptResult<T> {
pub fvals: Vec<(Vec<T>, T)>,
pub params: Vec<T>,
pub stopped: bool
}
impl <T: Clone + Copy> OptResult<T> {
pub fn matrix(&self) -> Matrix<T> {
if self.fvals.len() == 0 {
return Matrix::new();
}
let mut m: Matrix<T> = Matrix::new();
for &(ref v, f) in self.fvals.iter() {
let mut x = v.clone();
x.push(f);
m.add_row(&x);
}
m
}
}
pub fn opt<O, D>(f: &O, fd: &D, init: &[f64], opts: OptParams<f64>) -> OptResult<f64>
where O: Fn(&[f64]) -> f64, D: Fn(&[f64]) -> Vec<f64> {
let alpha = opts.alpha.unwrap_or(0.1);
let iter = opts.iter.unwrap_or(1000);
let eps = opts.eps;
let mut r = vec![];
let mut p = init.to_vec();
let mut stopped = false;
for _ in 0..iter {
let i = p.sub(&fd(&p).mul_scalar(alpha));
r.push((i.clone(), f(&i)));
stopped = eps.is_some() && i.iter().zip(p.iter()).all(|(&x, &y)| num::abs(x - y) <= eps.unwrap());
p = i;
if stopped {
break;
}
}
OptResult {
params: p.to_vec(),
fvals: r,
stopped: stopped
}
}
pub fn opt_hypothesis(h: &Hypothesis, x: &Matrix<f64>, y: &[f64], opts: OptParams<f64>) -> OptResult<f64> {
let alpha = opts.alpha.unwrap_or(0.1);
let iter = opts.iter.unwrap_or(1000);
let eps = opts.eps;
let mut r = vec![];
let mut p = h.params();
let mut stopped = false;
let mut hx = Hypothesis::from_params(&p);
for _ in 0..iter {
let d = hx.derivatives(x, y);
let i = p.sub(&d.mul_scalar(alpha));
hx = Hypothesis::from_params(&i);
r.push((i.clone(), hx.error(&x, &y)));
stopped = eps.is_some() && i.iter().zip(p.iter()).all(|(&x, &y)| num::abs(x - y) <= eps.unwrap());
p = i;
if stopped {
break;
}
}
OptResult {
params: p.to_vec(),
fvals: r,
stopped: stopped
}
}
pub fn plot_learning_curve(r: &OptResult<f64>, w: &Window) -> Result<(String, String), &'static str> {
let errors = r.fvals.iter().map(|&(_, ref y)| y).cloned().collect::<Vec<f64>>();
let mut prfx = "/tmp/".to_string();
prfx.extend(thread_rng().gen_ascii_chars().take(16));
let script_file = prfx.clone() + ".m";
let image_file = prfx + ".png";
let r = builder()
.add_vector("y = $$", &errors)
.add("x = 1:size(y, 2)")
.add("plot(x, y, 'linewidth', 2)")
.add("grid on")
.add("title('learning curve')")
.add("xlabel('iteration')")
.add("ylabel('error')")
.add(&("print -r100 -dpng '".to_string() + &image_file + "'"))
.run(&script_file);
match r {
Ok(_) => {
let img = RgbImage::from_file(&image_file);
match img {
Some(i) => {
w.show_image(&i);
Ok((script_file, image_file))
},
_ => Err("Could not load image.")
}
},
_ => Err("Could not run octave.")
}
}