light_curve_feature/
evaluator.rs

1pub use crate::error::EvaluatorError;
2pub use crate::float_trait::Float;
3pub use crate::time_series::TimeSeries;
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)]
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}
23
24#[derive(Clone, Debug)]
25pub struct EvaluatorProperties {
26    pub info: EvaluatorInfo,
27    pub names: Vec<String>,
28    pub descriptions: Vec<String>,
29}
30
31// pub trait EvaluatorPropertiesTrait {
32//     fn get_properties(&self) -> &EvaluatorProperties;
33// }
34
35#[enum_dispatch]
36pub trait EvaluatorInfoTrait {
37    /// Get feature evaluator meta-information
38    fn get_info(&self) -> &EvaluatorInfo;
39
40    /// Size of vectors returned by [eval()](FeatureEvaluator::eval),
41    /// [get_names()](FeatureEvaluator::get_names) and
42    /// [get_descriptions()](FeatureEvaluator::get_descriptions)
43    fn size_hint(&self) -> usize {
44        self.get_info().size
45    }
46
47    /// Minimum time series length required to successfully evaluate feature
48    fn min_ts_length(&self) -> usize {
49        self.get_info().min_ts_length
50    }
51
52    /// If time array used by the feature
53    fn is_t_required(&self) -> bool {
54        self.get_info().t_required
55    }
56
57    /// If magnitude array is used by the feature
58    fn is_m_required(&self) -> bool {
59        self.get_info().m_required
60    }
61
62    /// If weight array is used by the feature
63    fn is_w_required(&self) -> bool {
64        self.get_info().w_required
65    }
66
67    /// If feature requires time-sorting on the input [TimeSeries]
68    fn is_sorting_required(&self) -> bool {
69        self.get_info().sorting_required
70    }
71}
72
73// impl<P> EvaluatorInfoTrait for P
74// where
75//     P: EvaluatorPropertiesTrait,
76// {
77//     fn get_info(&self) -> &EvaluatorInfo {
78//         &self.get_properties().info
79//     }
80// }
81
82#[enum_dispatch]
83pub trait FeatureNamesDescriptionsTrait {
84    /// Vector of feature names. The length and feature order corresponds to
85    /// [eval()](FeatureEvaluator::eval) output
86    fn get_names(&self) -> Vec<&str>;
87
88    /// Vector of feature descriptions. The length and feature order corresponds to
89    /// [eval()](FeatureEvaluator::eval) output
90    fn get_descriptions(&self) -> Vec<&str>;
91}
92
93// impl<P> FeatureNamesDescriptionsTrait for P
94// where
95//     P: EvaluatorPropertiesTrait,
96// {
97//     fn get_names(&self) -> Vec<&str> {
98//         self.get_properties()
99//             .names
100//             .iter()
101//             .map(|name| name.as_str())
102//             .collect()
103//     }
104//
105//     fn get_descriptions(&self) -> Vec<&str> {
106//         self.get_properties()
107//             .descriptions
108//             .iter()
109//             .map(|descr| descr.as_str())
110//             .collect()
111//     }
112// }
113
114/// The trait each feature should implement
115#[enum_dispatch]
116pub trait FeatureEvaluator<T: Float>:
117    FeatureNamesDescriptionsTrait
118    + EvaluatorInfoTrait
119    + Send
120    + Clone
121    + Debug
122    + Serialize
123    + DeserializeOwned
124    + JsonSchema
125{
126    /// Vector of feature values or `EvaluatorError`
127    fn eval(&self, ts: &mut TimeSeries<T>) -> Result<Vec<T>, EvaluatorError>;
128
129    /// Returns vector of feature values and fill invalid components with given value
130    fn eval_or_fill(&self, ts: &mut TimeSeries<T>, fill_value: T) -> Vec<T> {
131        match self.eval(ts) {
132            Ok(v) => v,
133            Err(_) => vec![fill_value; self.size_hint()],
134        }
135    }
136
137    /// Checks if [TimeSeries] has enough points to evaluate the feature
138    fn check_ts_length(&self, ts: &TimeSeries<T>) -> Result<usize, EvaluatorError> {
139        let length = ts.lenu();
140        if length < self.min_ts_length() {
141            Err(EvaluatorError::ShortTimeSeries {
142                actual: length,
143                minimum: self.min_ts_length(),
144            })
145        } else {
146            Ok(length)
147        }
148    }
149}
150
151pub fn get_nonzero_m_std<T: Float>(ts: &mut TimeSeries<T>) -> Result<T, EvaluatorError> {
152    let std = ts.m.get_std();
153    if std.is_zero() || ts.is_plateau() {
154        Err(EvaluatorError::FlatTimeSeries)
155    } else {
156        Ok(std)
157    }
158}
159
160pub fn get_nonzero_m_std2<T: Float>(ts: &mut TimeSeries<T>) -> Result<T, EvaluatorError> {
161    let std2 = ts.m.get_std2();
162    if std2.is_zero() || ts.is_plateau() {
163        Err(EvaluatorError::FlatTimeSeries)
164    } else {
165        Ok(std2)
166    }
167}
168
169pub fn get_nonzero_reduced_chi2<T: Float>(ts: &mut TimeSeries<T>) -> Result<T, EvaluatorError> {
170    let reduced_chi2 = ts.get_m_reduced_chi2();
171    if reduced_chi2.is_zero() || ts.is_plateau() {
172        Err(EvaluatorError::FlatTimeSeries)
173    } else {
174        Ok(reduced_chi2)
175    }
176}
177
178pub trait OwnedArrays<T>
179where
180    T: Float,
181{
182    fn ts(self) -> TimeSeries<'static, T>;
183}
184
185pub struct TmArrays<T> {
186    pub t: Array1<T>,
187    pub m: Array1<T>,
188}
189
190impl<T> OwnedArrays<T> for TmArrays<T>
191where
192    T: Float,
193{
194    fn ts(self) -> TimeSeries<'static, T> {
195        TimeSeries::new_without_weight(self.t, self.m)
196    }
197}
198
199pub struct TmwArrays<T> {
200    pub t: Array1<T>,
201    pub m: Array1<T>,
202    pub w: Array1<T>,
203}
204
205impl<T> OwnedArrays<T> for TmwArrays<T>
206where
207    T: Float,
208{
209    fn ts(self) -> TimeSeries<'static, T> {
210        TimeSeries::new(self.t, self.m, self.w)
211    }
212}