use crate::error::{Error, Result};
use crate::frame::{Dataset, Frame};
#[derive(Clone, Debug, PartialEq)]
pub enum ParamValue {
Int(i64),
Float(f64),
Bool(bool),
}
impl From<i64> for ParamValue {
fn from(v: i64) -> Self {
ParamValue::Int(v)
}
}
impl From<i32> for ParamValue {
fn from(v: i32) -> Self {
ParamValue::Int(v as i64)
}
}
impl From<usize> for ParamValue {
fn from(v: usize) -> Self {
ParamValue::Int(v as i64)
}
}
impl From<f64> for ParamValue {
fn from(v: f64) -> Self {
ParamValue::Float(v)
}
}
impl From<bool> for ParamValue {
fn from(v: bool) -> Self {
ParamValue::Bool(v)
}
}
impl ParamValue {
pub fn as_i64(&self) -> Result<i64> {
match self {
ParamValue::Int(i) => Ok(*i),
ParamValue::Float(f) if f.fract() == 0.0 => Ok(*f as i64),
other => Err(Error::Param(format!("expected an integer, got {other:?}"))),
}
}
pub fn as_f64(&self) -> Result<f64> {
match self {
ParamValue::Float(f) => Ok(*f),
ParamValue::Int(i) => Ok(*i as f64),
other => Err(Error::Param(format!("expected a float, got {other:?}"))),
}
}
pub fn as_bool(&self) -> Result<bool> {
match self {
ParamValue::Bool(b) => Ok(*b),
other => Err(Error::Param(format!("expected a bool, got {other:?}"))),
}
}
}
pub trait Transformer: TransformerClone + Send + Sync {
fn name(&self) -> &'static str;
fn fit(&mut self, frame: &Frame) -> Result<()>;
fn transform(&self, frame: &Frame) -> Result<Frame>;
fn fit_transform(&mut self, frame: &Frame) -> Result<Frame> {
self.fit(frame)?;
self.transform(frame)
}
fn as_affine(&self) -> Option<(Vec<f64>, Vec<f64>)> {
None
}
#[cfg(feature = "onnx")]
fn onnx_prefix(&self) -> Option<crate::onnx::Prefix> {
self.as_affine()
.map(|(shift, scale)| crate::onnx::Prefix::Affine { shift, scale })
}
fn set_param(&mut self, name: &str, _value: ParamValue) -> Result<()> {
Err(Error::Param(format!(
"{} has no parameter '{name}'",
self.name()
)))
}
}
pub trait TransformerClone {
fn clone_box(&self) -> Box<dyn Transformer>;
}
impl<T> TransformerClone for T
where
T: Transformer + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn Transformer> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Transformer> {
fn clone(&self) -> Self {
self.clone_box()
}
}
pub trait Estimator: Send + Sync {
fn name(&self) -> &'static str;
fn fit(&mut self, dataset: &Dataset) -> Result<()>;
fn set_param(&mut self, name: &str, _value: ParamValue) -> Result<()> {
Err(Error::Param(format!(
"{} has no parameter '{name}'",
self.name()
)))
}
#[cfg(feature = "onnx")]
fn to_onnx_proto(&self) -> Result<onnx_export_rs::proto::ModelProto> {
Err(Error::Backend(format!(
"{} is not ONNX-exportable",
self.name()
)))
}
}
pub trait Predictor: Send + Sync {
fn predict(&self, frame: &Frame) -> Result<Vec<f64>>;
}
pub trait ProbaPredictor: Predictor {
fn predict_proba(&self, frame: &Frame) -> Result<Frame>;
}
pub trait Forecaster {
fn name(&self) -> &'static str;
fn fit(&mut self, series: &[f64]) -> Result<()>;
fn forecast(&self, steps: usize) -> Result<Vec<f64>>;
}
pub trait PartialFit {
fn name(&self) -> &'static str;
fn partial_fit(&mut self, batch: &Dataset) -> Result<()>;
}
pub trait Clusterer {
fn name(&self) -> &'static str;
fn fit(&mut self, frame: &Frame) -> Result<()>;
fn predict(&self, frame: &Frame) -> Result<Vec<f64>>;
}
pub trait Model: Estimator + Predictor + ModelClone {}
impl<T: Estimator + Predictor + Clone + 'static> Model for T {}
pub trait ModelClone {
fn clone_box(&self) -> Box<dyn Model>;
}
impl<T> ModelClone for T
where
T: Model + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn Model> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Model> {
fn clone(&self) -> Self {
self.clone_box()
}
}
pub trait Balancer: BalancerClone + Send + Sync {
fn name(&self) -> &'static str;
fn fit_resample(&self, features: &Frame, target: &[f64]) -> Result<(Frame, Vec<f64>)>;
}
pub trait BalancerClone {
fn clone_box(&self) -> Box<dyn Balancer>;
}
impl<T> BalancerClone for T
where
T: Balancer + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn Balancer> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Balancer> {
fn clone(&self) -> Self {
self.clone_box()
}
}