deimos/calc/
polynomial.rs1#[cfg(feature = "python")]
4use pyo3::prelude::*;
5
6use super::*;
7use crate::{
8 calc_config, calc_input_names, calc_output_names,
9 math::{polyfit, polyval},
10 py_json_methods,
11};
12
13#[cfg_attr(feature = "python", pyclass)]
18#[derive(Serialize, Deserialize, Debug, Default)]
19pub struct Polynomial {
20 input_name: String,
22 coefficients: Vec<f64>,
23 note: String,
24 save_outputs: bool,
25
26 #[serde(skip)]
28 input_index: usize,
29
30 #[serde(skip)]
31 output_index: usize,
32}
33
34impl Polynomial {
35 pub fn new(
36 input_name: String,
37 coefficients: Vec<f64>,
38 note: String,
39 save_outputs: bool,
40 ) -> Box<Self> {
41 Box::new(Self {
42 input_name,
43 coefficients,
44 note,
45 save_outputs,
46 input_index: usize::MAX,
47 output_index: usize::MAX,
48 })
49 }
50
51 pub fn fit_from_points(
52 input_name: &str,
53 points: &[(f64, f64)],
54 order: usize,
55 note: &str,
56 save_outputs: bool,
57 ) -> Result<Box<Self>, String> {
58 let coefficients = polyfit(points, order)?;
59
60 Ok(Self::new(
61 input_name.into(),
62 coefficients,
63 note.into(),
64 save_outputs,
65 ))
66 }
67}
68
69py_json_methods!(
70 Polynomial,
71 Calc,
72 #[new]
73 fn py_new(
74 input_name: String,
75 coefficients: Vec<f64>,
76 note: String,
77 save_outputs: bool,
78 ) -> Self {
79 *Self::new(input_name, coefficients, note, save_outputs)
80 }
81);
82
83#[typetag::serde]
84impl Calc for Polynomial {
85 fn init(
86 &mut self,
87 _: ControllerCtx,
88 input_indices: Vec<usize>,
89 output_range: Range<usize>,
90 ) -> Result<(), String> {
91 if self.coefficients.is_empty() {
92 return Err("Polynomial coefficients cannot be empty".to_string());
93 }
94 self.input_index = input_indices
95 .first()
96 .copied()
97 .ok_or_else(|| "Polynomial calc missing input index".to_string())?;
98 self.output_index = output_range
99 .clone()
100 .next()
101 .ok_or_else(|| "Polynomial calc missing output index".to_string())?;
102 Ok(())
103 }
104
105 fn terminate(&mut self) -> Result<(), String> {
106 self.input_index = usize::MAX;
107 self.output_index = usize::MAX;
108 Ok(())
109 }
110
111 fn eval(&mut self, tape: &mut [f64]) -> Result<(), String> {
112 let x = tape[self.input_index];
113 let y = polyval(x, &self.coefficients);
114 tape[self.output_index] = y;
115 Ok(())
116 }
117
118 fn get_input_map(&self) -> BTreeMap<CalcInputName, FieldName> {
119 let mut map = BTreeMap::new();
120 map.insert("x".to_owned(), self.input_name.clone());
121 map
122 }
123
124 fn update_input_map(&mut self, field: &str, source: &str) -> Result<(), String> {
125 if field == "x" {
126 self.input_name = source.to_owned();
127 Ok(())
128 } else {
129 Err(format!("Unrecognized field {field}"))
130 }
131 }
132
133 calc_config!();
134 calc_input_names!(x);
135 calc_output_names!(y);
136}