use crate::error::{ForecastError, Result};
use super::signal::Signal;
pub trait Cost: std::fmt::Debug {
fn fit(&mut self, signal: &Signal) -> Result<()>;
fn error(&self, start: usize, end: usize) -> Result<f64>;
fn min_size(&self) -> usize {
1
}
fn name(&self) -> &str;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DetectorResult {
pub bkps: Vec<usize>,
}
impl DetectorResult {
pub fn n_changepoints(&self) -> usize {
self.bkps.len().saturating_sub(1)
}
pub fn segments(&self) -> Vec<(usize, usize)> {
let mut out = Vec::with_capacity(self.bkps.len());
let mut start = 0usize;
for &b in &self.bkps {
out.push((start, b));
start = b;
}
out
}
pub fn changepoints(&self) -> &[usize] {
if self.bkps.is_empty() {
&[]
} else {
&self.bkps[..self.bkps.len() - 1]
}
}
}
pub trait Detector {
fn fit(&mut self, signal: &Signal) -> Result<()>;
fn predict_pen(&self, pen: f64) -> Result<DetectorResult>;
fn predict_n_bkps(&self, _n_bkps: usize) -> Result<DetectorResult> {
Err(ForecastError::InvalidParameter(format!(
"Detector '{}' does not support n_bkps mode",
self.name()
)))
}
fn predict_eps(&self, _epsilon: f64) -> Result<DetectorResult> {
Err(ForecastError::InvalidParameter(format!(
"Detector '{}' does not support epsilon mode",
self.name()
)))
}
fn fit_predict_pen(&mut self, signal: &Signal, pen: f64) -> Result<DetectorResult> {
self.fit(signal)?;
self.predict_pen(pen)
}
fn fit_predict_n_bkps(&mut self, signal: &Signal, n_bkps: usize) -> Result<DetectorResult> {
self.fit(signal)?;
self.predict_n_bkps(n_bkps)
}
fn name(&self) -> &str;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detector_result_segments_round_trip() {
let r = DetectorResult {
bkps: vec![30, 70, 100],
};
assert_eq!(r.n_changepoints(), 2);
assert_eq!(r.segments(), vec![(0, 30), (30, 70), (70, 100)]);
assert_eq!(r.changepoints(), &[30, 70]);
}
#[test]
fn detector_result_no_changepoints() {
let r = DetectorResult { bkps: vec![100] };
assert_eq!(r.n_changepoints(), 0);
assert_eq!(r.segments(), vec![(0, 100)]);
assert_eq!(r.changepoints(), &[] as &[usize]);
}
#[test]
fn detector_result_empty() {
let r = DetectorResult { bkps: vec![] };
assert_eq!(r.n_changepoints(), 0);
assert_eq!(r.segments(), Vec::<(usize, usize)>::new());
}
}