#![deny(missing_docs)]
pub mod utils;
#[derive(Copy, Clone, Debug)]
pub struct TrainingSettings {
pub accuracy_error: f64,
pub step: f64,
pub max_iterations: u64,
pub error_predictions: usize,
pub reset_interval: u64,
pub elasticity: f64,
pub debug: bool,
}
#[derive(Clone, Debug)]
pub struct Fit {
pub error: f64,
pub weights: Vec<f64>,
pub error_predictions: Vec<f64>,
pub iterations: u64,
}
pub fn train<F: Fn(&[f64]) -> f64>(
settings: TrainingSettings,
weights: &[f64],
f: F
) -> Result<Fit, Fit> {
let mut ws = vec![0.0; settings.error_predictions + weights.len()];
for i in 0..weights.len() {
ws[i + settings.error_predictions] = weights[i];
}
if settings.error_predictions > 0 {
ws[0] = settings.step;
}
let eval = |ws: &[f64]| {
let mut score = f(&ws[settings.error_predictions..]);
for i in 0..settings.error_predictions {
score += (score - ws[i]).abs();
}
score
};
let step = |ws: &[f64], i: usize| {
settings.elasticity * if i + 1 < settings.error_predictions {
ws[i + 1]
} else if i + 1 == settings.error_predictions {
settings.step
} else if settings.error_predictions > 0 {
ws[0]
} else {
settings.step
}
};
let check = |w: &mut f64, i: usize| {
if i < settings.error_predictions {
if *w <= settings.step {*w = settings.step}
}
};
let mut iterations = 0;
let mut last_score: Option<f64> = None;
let mut last_score_iterations = 0;
loop {
let score = f(&ws[settings.error_predictions..]);
if score <= settings.accuracy_error {
return Ok(Fit {
error: score,
weights: ws[settings.error_predictions..].into(),
error_predictions: ws[0..settings.error_predictions].into(),
iterations,
})
} else if iterations >= settings.max_iterations ||
last_score_iterations >= 2 * settings.reset_interval {
return Err(Fit {
error: score,
weights: ws[settings.error_predictions..].into(),
error_predictions: ws[0..settings.error_predictions].into(),
iterations,
})
}
if last_score == Some(score) {
last_score_iterations += 1;
} else {
last_score_iterations = 0;
}
last_score = Some(score);
if iterations % settings.reset_interval == 0 {
if settings.debug {
println!("{:?}", Fit {
error: score,
weights: ws[settings.error_predictions..].into(),
error_predictions: ws[0..settings.error_predictions].into(),
iterations,
});
}
for i in 0..settings.error_predictions {
ws[i] = 0.0;
}
ws[0] = settings.step;
}
for i in 0..ws.len() {
let score = eval(&ws);
let old = ws[i];
let step = step(&ws, i);
ws[i] += step;
check(&mut ws[i], i);
let score_up = eval(&ws);
ws[i] -= 2.0 * step;
check(&mut ws[i], i);
let score_down = eval(&ws);
if score <= score_up && score <= score_down {
ws[i] = old;
} else if score_up < score_down {
ws[i] += 2.0 * step;
}
}
iterations += 1;
}
}