1use core::f64;
2
3pub trait Basis<const N: usize>: Send + Sync {
7 fn num_features(&self) -> usize;
9
10 fn evaluate(&self, state: &[f64; N], buffer: &mut [f64]);
13
14 fn evaluate_buffers(&self, state: &[f64; N], buffer: &mut Vec<f64>) {
17 let start_len = buffer.len();
18 let n = self.num_features();
19 buffer.reserve(n);
21
22 buffer.resize(start_len + n, 0.0);
28 self.evaluate(state, &mut buffer[start_len..]);
29 }
30
31 fn eval_dot(&self, state: &[f64; N], weights: &[f64]) -> f64 {
34 let n = self.num_features();
35 let mut buffer = vec![0.0; n]; self.evaluate(state, &mut buffer);
37
38 let mut sum = 0.0;
39 for i in 0..n {
40 sum += buffer[i] * weights[i];
41 }
42 sum
43 }
44}
45
46#[derive(Clone, Debug)]
52pub struct PolynomialBasis {
53 pub degree: usize,
54 pub poly_type: PolynomialType,
55 pub clamp_range: Option<(f64, f64)>,
56}
57
58#[derive(Clone, Copy, Debug)]
59pub enum PolynomialType {
60 Power,
61 Hermite,
62 Chebyshev,
63 Laguerre,
64}
65
66impl PolynomialBasis {
67 pub fn new(degree: usize, poly_type: PolynomialType) -> Self {
68 Self {
69 degree,
70 poly_type,
71 clamp_range: None,
72 }
73 }
74
75 pub fn with_clamp(mut self, min: f64, max: f64) -> Self {
76 self.clamp_range = Some((min, max));
77 self
78 }
79}
80
81#[derive(Clone, Debug)]
86pub struct ScaledBasis<const N: usize, B> {
87 pub inner: B,
88 pub scales: Vec<f64>,
89 pub center: [f64; N],
90}
91
92impl<const N: usize, B> ScaledBasis<N, B> {
93 pub fn new(inner: B, scales: Vec<f64>) -> Self {
94 Self {
95 inner,
96 scales,
97 center: [0.0; N],
98 }
99 }
100
101 pub fn with_center(mut self, center: [f64; N]) -> Self {
102 self.center = center;
103 self
104 }
105}
106
107impl<const N: usize, B: Basis<N>> Basis<N> for ScaledBasis<N, B> {
108 fn num_features(&self) -> usize {
109 self.inner.num_features()
110 }
111
112 fn evaluate(&self, state: &[f64; N], buffer: &mut [f64]) {
113 let mut scaled = *state;
114 for (i, val) in scaled.iter_mut().enumerate() {
115 let s = if i < self.scales.len() {
116 self.scales[i]
117 } else {
118 *self.scales.first().unwrap_or(&1.0)
119 };
120 *val = (*val - self.center[i]) * s;
121 }
122 self.inner.evaluate(&scaled, buffer);
123 }
124
125 fn evaluate_buffers(&self, state: &[f64; N], buffer: &mut Vec<f64>) {
126 let mut scaled = *state;
127 for (i, val) in scaled.iter_mut().enumerate() {
128 let s = if i < self.scales.len() {
129 self.scales[i]
130 } else {
131 *self.scales.first().unwrap_or(&1.0)
132 };
133 *val = (*val - self.center[i]) * s;
134 }
135 self.inner.evaluate_buffers(&scaled, buffer);
136 }
137
138 fn eval_dot(&self, state: &[f64; N], weights: &[f64]) -> f64 {
139 let mut scaled = *state;
140 for (i, val) in scaled.iter_mut().enumerate() {
141 let s = if i < self.scales.len() {
142 self.scales[i]
143 } else {
144 *self.scales.first().unwrap_or(&1.0)
145 };
146 *val = (*val - self.center[i]) * s;
147 }
148 self.inner.eval_dot(&scaled, weights)
149 }
150}
151
152impl<const N: usize> Basis<N> for PolynomialBasis {
153 fn num_features(&self) -> usize {
154 if self.degree == 0 {
155 return 1;
156 } else if self.degree == 1 {
157 return 1 + N;
158 }
159
160 let mut base_count = 1 + N + N * (N + 1) / 2;
162
163 if self.degree > 2 {
165 base_count += (self.degree - 2) * N;
166 }
167 base_count
168 }
169
170 fn evaluate(&self, state: &[f64; N], buffer: &mut [f64]) {
171 const MAX_DEG: usize = 6;
173 let effective_deg = self.degree.min(MAX_DEG);
174
175 let mut uni = [[0.0; MAX_DEG + 1]; N];
177 for i in 0..N {
178 let x = if let Some((min, max)) = self.clamp_range {
179 state[i].clamp(min, max)
180 } else {
181 state[i]
182 };
183 compute_univariate(x, effective_deg, self.poly_type, &mut uni[i][..]);
184 }
185
186 let mut idx = 0;
187
188 buffer[idx] = 1.0;
190 idx += 1;
191
192 if self.degree < 1 {
193 return;
194 }
195
196 for u_row in &uni {
198 buffer[idx] = u_row[1];
199 idx += 1;
200 }
201
202 if self.degree < 2 {
203 return;
204 }
205
206 for (i, u_row) in uni.iter().enumerate() {
209 let u_i = u_row[1];
210
211 buffer[idx] = u_row[2]; idx += 1;
214
215 for u_j in uni.iter().skip(i + 1) {
217 buffer[idx] = u_i * u_j[1];
218 idx += 1;
219 }
220 }
221
222 for k in 3..=self.degree {
224 if k > MAX_DEG {
225 break;
226 }
227 for u_row in &uni {
228 buffer[idx] = u_row[k];
229 idx += 1;
230 }
231 }
232 }
233
234 fn evaluate_buffers(&self, state: &[f64; N], buffer: &mut Vec<f64>) {
236 let n = <Self as Basis<N>>::num_features(self);
237 let start_len = buffer.len();
238
239 buffer.reserve(n);
241
242 unsafe {
246 let ptr = buffer.as_mut_ptr().add(start_len);
247 let slice = std::slice::from_raw_parts_mut(ptr, n);
248 self.evaluate(state, slice);
249 buffer.set_len(start_len + n);
250 }
251 }
252
253 fn eval_dot(&self, state: &[f64; N], weights: &[f64]) -> f64 {
254 const MAX_DEG: usize = 6;
255 let effective_deg = self.degree.min(MAX_DEG);
256
257 let mut uni = [[0.0; MAX_DEG + 1]; N];
259 for i in 0..N {
260 let x = if let Some((min, max)) = self.clamp_range {
261 state[i].clamp(min, max)
262 } else {
263 state[i]
264 };
265 compute_univariate(x, effective_deg, self.poly_type, &mut uni[i][..]);
266 }
267
268 let mut sum = weights[0]; let mut idx = 1;
270
271 if self.degree < 1 {
272 return sum;
273 }
274
275 for u_row in &uni {
277 sum += weights[idx] * u_row[1];
278 idx += 1;
279 }
280
281 if self.degree < 2 {
282 return sum;
283 }
284
285 for i in 0..N {
287 for j in i..N {
288 let val = if i == j {
289 uni[i][2]
290 } else {
291 uni[i][1] * uni[j][1]
292 };
293 sum += weights[idx] * val;
294 idx += 1;
295 }
296 }
297
298 for k in 3..=self.degree {
300 if k > MAX_DEG {
301 break;
302 }
303 for u_row in &uni {
304 sum += weights[idx] * u_row[k];
305 idx += 1;
306 }
307 }
308
309 sum
310 }
311}
312
313#[inline(always)]
314fn compute_univariate(x: f64, k: usize, ptype: PolynomialType, out: &mut [f64]) {
315 out[0] = 1.0;
316 if k >= 1 {
317 out[1] = match ptype {
318 PolynomialType::Laguerre => 1.0 - x,
319 _ => x,
320 };
321 }
322
323 match ptype {
325 PolynomialType::Power => {
326 for i in 2..=k {
327 out[i] = out[i - 1] * x;
328 }
329 }
330 PolynomialType::Hermite => {
331 for i in 2..=k {
333 let n = (i - 1) as f64;
334 out[i] = x * out[i - 1] - n * out[i - 2];
335 }
336 }
337 PolynomialType::Chebyshev => {
338 for i in 2..=k {
340 out[i] = 2.0 * x * out[i - 1] - out[i - 2];
341 }
342 }
343 PolynomialType::Laguerre => {
344 for i in 2..=k {
347 let n_prev = (i - 1) as f64; let n_curr = i as f64; out[i] = ((2.0 * n_prev + 1.0 - x) * out[i - 1] - n_prev * out[i - 2]) / n_curr;
350 }
351 }
352 }
353}