1use rayon::prelude::*;
24
25#[derive(Clone, Debug)]
30pub struct IrregFdata {
31 pub offsets: Vec<usize>,
34 pub argvals: Vec<f64>,
36 pub values: Vec<f64>,
38 pub rangeval: [f64; 2],
40}
41
42impl IrregFdata {
43 pub fn from_lists(argvals_list: &[Vec<f64>], values_list: &[Vec<f64>]) -> Self {
52 let n = argvals_list.len();
53 assert_eq!(
54 n,
55 values_list.len(),
56 "argvals_list and values_list must have same length"
57 );
58
59 let mut offsets = Vec::with_capacity(n + 1);
60 offsets.push(0);
61
62 let total_points: usize = argvals_list.iter().map(|v| v.len()).sum();
63 let mut argvals = Vec::with_capacity(total_points);
64 let mut values = Vec::with_capacity(total_points);
65
66 let mut range_min = f64::INFINITY;
67 let mut range_max = f64::NEG_INFINITY;
68
69 for i in 0..n {
70 assert_eq!(
71 argvals_list[i].len(),
72 values_list[i].len(),
73 "Observation {} has mismatched argvals/values lengths",
74 i
75 );
76
77 argvals.extend_from_slice(&argvals_list[i]);
78 values.extend_from_slice(&values_list[i]);
79 offsets.push(argvals.len());
80
81 if let (Some(&min), Some(&max)) = (argvals_list[i].first(), argvals_list[i].last()) {
82 range_min = range_min.min(min);
83 range_max = range_max.max(max);
84 }
85 }
86
87 IrregFdata {
88 offsets,
89 argvals,
90 values,
91 rangeval: [range_min, range_max],
92 }
93 }
94
95 pub fn from_flat(
103 offsets: Vec<usize>,
104 argvals: Vec<f64>,
105 values: Vec<f64>,
106 rangeval: [f64; 2],
107 ) -> Self {
108 IrregFdata {
109 offsets,
110 argvals,
111 values,
112 rangeval,
113 }
114 }
115
116 #[inline]
118 pub fn n_obs(&self) -> usize {
119 self.offsets.len().saturating_sub(1)
120 }
121
122 #[inline]
124 pub fn n_points(&self, i: usize) -> usize {
125 self.offsets[i + 1] - self.offsets[i]
126 }
127
128 #[inline]
130 pub fn get_obs(&self, i: usize) -> (&[f64], &[f64]) {
131 let start = self.offsets[i];
132 let end = self.offsets[i + 1];
133 (&self.argvals[start..end], &self.values[start..end])
134 }
135
136 #[inline]
138 pub fn total_points(&self) -> usize {
139 self.argvals.len()
140 }
141
142 pub fn obs_counts(&self) -> Vec<usize> {
144 (0..self.n_obs()).map(|i| self.n_points(i)).collect()
145 }
146
147 pub fn min_obs(&self) -> usize {
149 (0..self.n_obs())
150 .map(|i| self.n_points(i))
151 .min()
152 .unwrap_or(0)
153 }
154
155 pub fn max_obs(&self) -> usize {
157 (0..self.n_obs())
158 .map(|i| self.n_points(i))
159 .max()
160 .unwrap_or(0)
161 }
162}
163
164pub fn integrate_irreg(offsets: &[usize], argvals: &[f64], values: &[f64]) -> Vec<f64> {
181 let n = offsets.len() - 1;
182
183 (0..n)
184 .into_par_iter()
185 .map(|i| {
186 let start = offsets[i];
187 let end = offsets[i + 1];
188 let t = &argvals[start..end];
189 let x = &values[start..end];
190
191 if t.len() < 2 {
192 return 0.0;
193 }
194
195 let mut integral = 0.0;
196 for j in 1..t.len() {
197 let h = t[j] - t[j - 1];
198 integral += 0.5 * h * (x[j] + x[j - 1]);
199 }
200 integral
201 })
202 .collect()
203}
204
205pub fn integrate_irreg_struct(ifd: &IrregFdata) -> Vec<f64> {
207 integrate_irreg(&ifd.offsets, &ifd.argvals, &ifd.values)
208}
209
210pub fn norm_lp_irreg(offsets: &[usize], argvals: &[f64], values: &[f64], p: f64) -> Vec<f64> {
229 let n = offsets.len() - 1;
230
231 (0..n)
232 .into_par_iter()
233 .map(|i| {
234 let start = offsets[i];
235 let end = offsets[i + 1];
236 let t = &argvals[start..end];
237 let x = &values[start..end];
238
239 if t.len() < 2 {
240 return 0.0;
241 }
242
243 let mut integral = 0.0;
244 for j in 1..t.len() {
245 let h = t[j] - t[j - 1];
246 let val_left = x[j - 1].abs().powf(p);
247 let val_right = x[j].abs().powf(p);
248 integral += 0.5 * h * (val_left + val_right);
249 }
250 integral.powf(1.0 / p)
251 })
252 .collect()
253}
254
255#[inline]
261fn kernel_epanechnikov(u: f64) -> f64 {
262 if u.abs() <= 1.0 {
263 0.75 * (1.0 - u * u)
264 } else {
265 0.0
266 }
267}
268
269#[inline]
271fn kernel_gaussian(u: f64) -> f64 {
272 (-0.5 * u * u).exp() / (2.0 * std::f64::consts::PI).sqrt()
273}
274
275pub fn mean_irreg(
291 offsets: &[usize],
292 argvals: &[f64],
293 values: &[f64],
294 target_argvals: &[f64],
295 bandwidth: f64,
296 kernel_type: i32,
297) -> Vec<f64> {
298 let n = offsets.len() - 1;
299 let kernel = match kernel_type {
300 0 => kernel_epanechnikov,
301 _ => kernel_gaussian,
302 };
303
304 target_argvals
305 .par_iter()
306 .map(|&t| {
307 let mut sum_weights = 0.0;
308 let mut sum_values = 0.0;
309
310 for i in 0..n {
311 let start = offsets[i];
312 let end = offsets[i + 1];
313 let obs_t = &argvals[start..end];
314 let obs_x = &values[start..end];
315
316 for (&ti, &xi) in obs_t.iter().zip(obs_x.iter()) {
317 let u = (ti - t) / bandwidth;
318 let w = kernel(u);
319 sum_weights += w;
320 sum_values += w * xi;
321 }
322 }
323
324 if sum_weights > 0.0 {
325 sum_values / sum_weights
326 } else {
327 f64::NAN
328 }
329 })
330 .collect()
331}
332
333pub fn cov_irreg(
351 offsets: &[usize],
352 argvals: &[f64],
353 values: &[f64],
354 s_grid: &[f64],
355 t_grid: &[f64],
356 bandwidth: f64,
357) -> Vec<f64> {
358 let n = offsets.len() - 1;
359 let ns = s_grid.len();
360 let nt = t_grid.len();
361
362 let mean_at_obs = mean_irreg(offsets, argvals, values, argvals, bandwidth, 1);
364
365 let centered: Vec<f64> = values
367 .iter()
368 .zip(mean_at_obs.iter())
369 .map(|(&v, &m)| v - m)
370 .collect();
371
372 let mut cov = vec![0.0; ns * nt];
374
375 for (si, &s) in s_grid.iter().enumerate() {
376 for (ti, &t) in t_grid.iter().enumerate() {
377 let mut sum_weights = 0.0;
378 let mut sum_products = 0.0;
379
380 for i in 0..n {
382 let start = offsets[i];
383 let end = offsets[i + 1];
384 let obs_t = &argvals[start..end];
385 let obs_c = ¢ered[start..end];
386
387 for j1 in 0..obs_t.len() {
388 for j2 in 0..obs_t.len() {
389 let u1 = (obs_t[j1] - s) / bandwidth;
390 let u2 = (obs_t[j2] - t) / bandwidth;
391
392 let w1 = kernel_gaussian(u1);
393 let w2 = kernel_gaussian(u2);
394 let w = w1 * w2;
395
396 sum_weights += w;
397 sum_products += w * obs_c[j1] * obs_c[j2];
398 }
399 }
400 }
401
402 if sum_weights > 0.0 {
403 cov[si + ti * ns] = sum_products / sum_weights;
404 }
405 }
406 }
407
408 cov
409}
410
411fn lp_distance_pair(t1: &[f64], x1: &[f64], t2: &[f64], x2: &[f64], p: f64) -> f64 {
419 let mut all_t: Vec<f64> = t1.iter().chain(t2.iter()).copied().collect();
421 all_t.sort_by(|a, b| a.partial_cmp(b).unwrap());
422 all_t.dedup();
423
424 let t_min = t1.first().unwrap().max(*t2.first().unwrap());
426 let t_max = t1.last().unwrap().min(*t2.last().unwrap());
427
428 let common_t: Vec<f64> = all_t
429 .into_iter()
430 .filter(|&t| t >= t_min && t <= t_max)
431 .collect();
432
433 if common_t.len() < 2 {
434 return f64::NAN;
435 }
436
437 let y1: Vec<f64> = common_t.iter().map(|&t| linear_interp(t1, x1, t)).collect();
439 let y2: Vec<f64> = common_t.iter().map(|&t| linear_interp(t2, x2, t)).collect();
440
441 let mut integral = 0.0;
443 for j in 1..common_t.len() {
444 let h = common_t[j] - common_t[j - 1];
445 let val_left = (y1[j - 1] - y2[j - 1]).abs().powf(p);
446 let val_right = (y1[j] - y2[j]).abs().powf(p);
447 integral += 0.5 * h * (val_left + val_right);
448 }
449
450 integral.powf(1.0 / p)
451}
452
453fn linear_interp(argvals: &[f64], values: &[f64], t: f64) -> f64 {
455 if t <= argvals[0] {
456 return values[0];
457 }
458 if t >= argvals[argvals.len() - 1] {
459 return values[values.len() - 1];
460 }
461
462 let idx = argvals.iter().position(|&x| x > t).unwrap();
464 let t0 = argvals[idx - 1];
465 let t1 = argvals[idx];
466 let x0 = values[idx - 1];
467 let x1 = values[idx];
468
469 x0 + (x1 - x0) * (t - t0) / (t1 - t0)
471}
472
473pub fn metric_lp_irreg(offsets: &[usize], argvals: &[f64], values: &[f64], p: f64) -> Vec<f64> {
484 let n = offsets.len() - 1;
485 let mut dist = vec![0.0; n * n];
486
487 let pairs: Vec<(usize, usize)> = (0..n)
489 .flat_map(|i| (i + 1..n).map(move |j| (i, j)))
490 .collect();
491
492 let distances: Vec<f64> = pairs
493 .par_iter()
494 .map(|&(i, j)| {
495 let start_i = offsets[i];
496 let end_i = offsets[i + 1];
497 let start_j = offsets[j];
498 let end_j = offsets[j + 1];
499
500 lp_distance_pair(
501 &argvals[start_i..end_i],
502 &values[start_i..end_i],
503 &argvals[start_j..end_j],
504 &values[start_j..end_j],
505 p,
506 )
507 })
508 .collect();
509
510 for (k, &(i, j)) in pairs.iter().enumerate() {
512 dist[i + j * n] = distances[k];
513 dist[j + i * n] = distances[k];
514 }
515
516 dist
517}
518
519pub fn to_regular_grid(
536 offsets: &[usize],
537 argvals: &[f64],
538 values: &[f64],
539 target_grid: &[f64],
540) -> Vec<f64> {
541 let n = offsets.len() - 1;
542 let m = target_grid.len();
543
544 (0..n)
545 .into_par_iter()
546 .flat_map(|i| {
547 let start = offsets[i];
548 let end = offsets[i + 1];
549 let obs_t = &argvals[start..end];
550 let obs_x = &values[start..end];
551
552 target_grid
553 .iter()
554 .map(|&t| {
555 if obs_t.is_empty() || t < obs_t[0] || t > obs_t[obs_t.len() - 1] {
556 f64::NAN
557 } else {
558 linear_interp(obs_t, obs_x, t)
559 }
560 })
561 .collect::<Vec<f64>>()
562 })
563 .collect::<Vec<f64>>()
564 .chunks(m)
566 .enumerate()
567 .fold(vec![0.0; n * m], |mut acc, (i, row)| {
568 for (j, &val) in row.iter().enumerate() {
569 acc[i + j * n] = val;
570 }
571 acc
572 })
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 #[test]
580 fn test_from_lists() {
581 let argvals_list = vec![vec![0.0, 0.5, 1.0], vec![0.0, 1.0]];
582 let values_list = vec![vec![1.0, 2.0, 3.0], vec![1.0, 3.0]];
583
584 let ifd = IrregFdata::from_lists(&argvals_list, &values_list);
585
586 assert_eq!(ifd.n_obs(), 2);
587 assert_eq!(ifd.n_points(0), 3);
588 assert_eq!(ifd.n_points(1), 2);
589 assert_eq!(ifd.total_points(), 5);
590 }
591
592 #[test]
593 fn test_get_obs() {
594 let argvals_list = vec![vec![0.0, 0.5, 1.0], vec![0.0, 1.0]];
595 let values_list = vec![vec![1.0, 2.0, 3.0], vec![1.0, 3.0]];
596
597 let ifd = IrregFdata::from_lists(&argvals_list, &values_list);
598
599 let (t0, x0) = ifd.get_obs(0);
600 assert_eq!(t0, &[0.0, 0.5, 1.0]);
601 assert_eq!(x0, &[1.0, 2.0, 3.0]);
602
603 let (t1, x1) = ifd.get_obs(1);
604 assert_eq!(t1, &[0.0, 1.0]);
605 assert_eq!(x1, &[1.0, 3.0]);
606 }
607
608 #[test]
609 fn test_integrate_irreg() {
610 let offsets = vec![0, 3, 6];
612 let argvals = vec![0.0, 0.5, 1.0, 0.0, 0.5, 1.0];
613 let values = vec![1.0, 1.0, 1.0, 2.0, 2.0, 2.0];
614
615 let integrals = integrate_irreg(&offsets, &argvals, &values);
616
617 assert!((integrals[0] - 1.0).abs() < 1e-10);
618 assert!((integrals[1] - 2.0).abs() < 1e-10);
619 }
620
621 #[test]
622 fn test_norm_lp_irreg() {
623 let offsets = vec![0, 3];
625 let argvals = vec![0.0, 0.5, 1.0];
626 let values = vec![2.0, 2.0, 2.0];
627
628 let norms = norm_lp_irreg(&offsets, &argvals, &values, 2.0);
629
630 assert!((norms[0] - 2.0).abs() < 1e-10);
631 }
632
633 #[test]
634 fn test_linear_interp() {
635 let t = vec![0.0, 1.0, 2.0];
636 let x = vec![0.0, 2.0, 4.0];
637
638 assert!((linear_interp(&t, &x, 0.5) - 1.0).abs() < 1e-10);
639 assert!((linear_interp(&t, &x, 1.5) - 3.0).abs() < 1e-10);
640 }
641
642 #[test]
643 fn test_mean_irreg() {
644 let offsets = vec![0, 3, 6];
646 let argvals = vec![0.0, 0.5, 1.0, 0.0, 0.5, 1.0];
647 let values = vec![0.0, 1.0, 2.0, 0.0, 1.0, 2.0];
648
649 let target = vec![0.0, 0.5, 1.0];
650 let mean = mean_irreg(&offsets, &argvals, &values, &target, 0.5, 1);
651
652 assert!((mean[1] - 1.0).abs() < 0.3);
654 }
655}