Skip to main content

light_curve_feature/
evaluator.rs

1pub use crate::data::TimeSeries;
2pub use crate::error::EvaluatorError;
3pub use crate::float_trait::Float;
4
5use enum_dispatch::enum_dispatch;
6pub use lazy_static::lazy_static;
7pub use macro_const::macro_const;
8use ndarray::Array1;
9pub use schemars::JsonSchema;
10use serde::de::DeserializeOwned;
11pub use serde::{Deserialize, Serialize};
12pub use std::fmt::Debug;
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
15pub struct EvaluatorInfo {
16    pub size: usize,
17    pub min_ts_length: usize,
18    pub t_required: bool,
19    pub m_required: bool,
20    pub w_required: bool,
21    pub sorting_required: bool,
22    pub variability_required: bool,
23}
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
26pub struct EvaluatorProperties {
27    pub info: EvaluatorInfo,
28    pub names: Vec<String>,
29    pub descriptions: Vec<String>,
30}
31
32#[enum_dispatch]
33pub trait EvaluatorInfoTrait {
34    /// Get feature evaluator meta-information
35    fn get_info(&self) -> &EvaluatorInfo;
36
37    /// Size of vectors returned by [eval()](FeatureEvaluator::eval),
38    /// [get_names()](FeatureEvaluator::get_names) and
39    /// [get_descriptions()](FeatureEvaluator::get_descriptions)
40    fn size_hint(&self) -> usize {
41        self.get_info().size
42    }
43
44    /// Minimum time series length required to successfully evaluate feature
45    fn min_ts_length(&self) -> usize {
46        self.get_info().min_ts_length
47    }
48
49    /// If time array used by the feature
50    fn is_t_required(&self) -> bool {
51        self.get_info().t_required
52    }
53
54    /// If magnitude array is used by the feature
55    fn is_m_required(&self) -> bool {
56        self.get_info().m_required
57    }
58
59    /// If weight array is used by the feature
60    fn is_w_required(&self) -> bool {
61        self.get_info().w_required
62    }
63
64    /// If feature requires time-sorting on the input [TimeSeries]
65    fn is_sorting_required(&self) -> bool {
66        self.get_info().sorting_required
67    }
68
69    /// If feature requires magnitude array elements to be different
70    fn is_variability_required(&self) -> bool {
71        self.get_info().variability_required
72    }
73
74    fn check_ts<F>(&self, ts: &mut TimeSeries<F>) -> Result<(), EvaluatorError>
75    where
76        F: Float,
77    {
78        self.check_ts_length(ts)?;
79        self.check_ts_variability(ts)
80    }
81
82    /// Checks if [TimeSeries] has enough points to evaluate the feature
83    fn check_ts_length<F>(&self, ts: &TimeSeries<F>) -> Result<(), EvaluatorError>
84    where
85        F: Float,
86    {
87        let length = ts.lenu();
88        if length < self.min_ts_length() {
89            Err(EvaluatorError::ShortTimeSeries {
90                actual: length,
91                minimum: self.min_ts_length(),
92            })
93        } else {
94            Ok(())
95        }
96    }
97
98    /// Checks if [TimeSeries] meets variability requirement
99    fn check_ts_variability<F>(&self, ts: &mut TimeSeries<F>) -> Result<(), EvaluatorError>
100    where
101        F: Float,
102    {
103        if self.is_variability_required() && ts.is_plateau() {
104            Err(EvaluatorError::FlatTimeSeries)
105        } else {
106            Ok(())
107        }
108    }
109}
110
111#[enum_dispatch]
112pub trait FeatureNamesDescriptionsTrait {
113    /// Vector of feature names. The length and feature order corresponds to
114    /// [eval()](FeatureEvaluator::eval) output
115    fn get_names(&self) -> Vec<&str>;
116
117    /// Vector of feature descriptions. The length and feature order corresponds to
118    /// [eval()](FeatureEvaluator::eval) output
119    fn get_descriptions(&self) -> Vec<&str>;
120}
121
122/// The trait each feature should implement
123#[enum_dispatch]
124pub trait FeatureEvaluator<T: Float>:
125    FeatureNamesDescriptionsTrait
126    + EvaluatorInfoTrait
127    + Send
128    + Clone
129    + Debug
130    + Serialize
131    + DeserializeOwned
132    + JsonSchema
133{
134    /// Version of [FeatureEvaluator::eval] which can panic for incorrect input
135    fn eval_no_ts_check(&self, ts: &mut TimeSeries<T>) -> Result<Vec<T>, EvaluatorError>;
136
137    /// Vector of feature values or `EvaluatorError`
138    fn eval(&self, ts: &mut TimeSeries<T>) -> Result<Vec<T>, EvaluatorError> {
139        self.check_ts(ts)?;
140        self.eval_no_ts_check(ts)
141    }
142
143    /// Returns vector of feature values and fill invalid components with given value
144    fn eval_or_fill(&self, ts: &mut TimeSeries<T>, fill_value: T) -> Vec<T> {
145        match self.eval(ts) {
146            Ok(v) => v,
147            Err(_) => vec![fill_value; self.size_hint()],
148        }
149    }
150}
151
152pub trait OwnedArrays<T>
153where
154    T: Float,
155{
156    fn ts(self) -> TimeSeries<'static, T>;
157}
158
159pub struct TmArrays<T> {
160    pub t: Array1<T>,
161    pub m: Array1<T>,
162}
163
164impl<T> OwnedArrays<T> for TmArrays<T>
165where
166    T: Float,
167{
168    fn ts(self) -> TimeSeries<'static, T> {
169        TimeSeries::new_without_weight(self.t, self.m)
170    }
171}
172
173pub struct TmwArrays<T> {
174    pub t: Array1<T>,
175    pub m: Array1<T>,
176    pub w: Array1<T>,
177}
178
179impl<T> OwnedArrays<T> for TmwArrays<T>
180where
181    T: Float,
182{
183    fn ts(self) -> TimeSeries<'static, T> {
184        TimeSeries::new(self.t, self.m, self.w)
185    }
186}