use std::path::{Path, PathBuf};
use ndarray::prelude::*;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use smartcore::{api::SupervisedEstimator, linalg::basic::matrix::DenseMatrix};
pub trait Classical<M, P>: Sized + Send + Sync + Serialize + for<'de> Deserialize<'de>
where
M: SupervisedEstimator<DenseMatrix<f32>, Vec<f32>, P> + Send + Sync,
P: Clone + Send + Sync,
{
fn new(models: Vec<M>, window_size: usize) -> Self;
fn name(&self) -> String;
fn models(&self) -> &[M];
fn window_size(&self) -> usize;
fn train(samples: &ndarray::Array2<f32>, window_size: usize, stride: usize, parameters: P) -> Result<Self, String> {
let (windows, _) = crate::data::create_windows(samples, window_size, stride);
let inner_models = (0..window_size)
.into_par_iter()
.map(|i| {
let (train_x, train_y) = crate::data::windows_to_train(&windows, i);
let train_x = DenseMatrix::from_2d_vec(&train_x).map_err(|e| e.to_string())?;
M::fit(&train_x, &train_y, parameters.clone()).map_err(|e| e.to_string())
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self::new(inner_models, window_size))
}
fn denoise(&self, samples: &ndarray::Array2<f32>, stride: usize) -> Result<ndarray::Array2<f32>, String> {
let (windows, starts) = crate::data::create_windows(samples, self.window_size(), stride);
let predicted = (0..self.window_size())
.into_par_iter()
.map(|i| {
let (test_x, _) = crate::data::windows_to_train(&windows, i);
let test_x = DenseMatrix::from_2d_vec(&test_x).map_err(|e| e.to_string())?;
let model = &self.models()[i];
M::predict(model, &test_x).map_err(|e| e.to_string())
})
.collect::<Result<Vec<_>, _>>()?;
let predicted = predicted.into_iter().map(Array1::from_vec).collect::<Vec<_>>();
let predicted = predicted.iter().map(ArrayBase::view).collect::<Vec<_>>();
let predicted = ndarray::stack(Axis(1), &predicted).map_err(|e| e.to_string())?;
let out_shape = (samples.len_of(Axis(0)), samples.len_of(Axis(1)));
Ok(crate::data::reassemble(predicted, out_shape, &starts))
}
fn save(&self, path_to_dir: &Path) -> Result<PathBuf, String> {
if !path_to_dir.exists() {
return Err(format!("The directory '{}' does not exist.", path_to_dir.display()));
}
if !path_to_dir.is_dir() {
return Err(format!("The path '{}' is not a directory.", path_to_dir.display()));
}
if !path_to_dir
.metadata()
.map(|m| m.permissions().readonly())
.unwrap_or(false)
{
return Err(format!("The directory '{}' is not writable.", path_to_dir.display()));
}
let path = path_to_dir.join(self.name());
let mut file = std::fs::File::create(&path).map_err(|e| e.to_string())?;
bincode::serde::encode_into_std_write(self, &mut file, bincode::config::standard())
.map_err(|e| e.to_string())?;
Ok(path)
}
fn load(path: &Path) -> Result<Self, String> {
if !path.exists() {
return Err(format!("The file '{}' does not exist.", path.display()));
}
if !path.is_file() {
return Err(format!("The path '{}' is not a file.", path.display()));
}
if !path.metadata().map(|m| m.permissions().readonly()).unwrap_or(false) {
return Err(format!("The file '{}' is not readable.", path.display()));
}
let mut file = std::fs::File::open(path).map_err(|e| e.to_string())?;
bincode::serde::decode_from_std_read(&mut file, bincode::config::standard()).map_err(|e| e.to_string())
}
}