unfolding.rs

2.1 kB · rust · 75 lines

1use crate::numerics::mean;2use faer::prelude::SolveLstsq;3use faer::Mat;45pub struct Spacings {6    pub values: Vec<f64>,7    pub negatives: usize,8}910fn spacings(unfolded: &[f64]) -> Spacings {11    let raw: Vec<f64> = unfolded.windows(2).map(|pair| pair[1] - pair[0]).collect();12    let negatives = raw.iter().filter(|s| **s < 0.0).count();13    let clamped: Vec<f64> = raw.iter().map(|s| s.max(0.0)).collect();14    let scale = mean(&clamped);15    Spacings {16        values: clamped.iter().map(|s| s / scale).collect(),17        negatives,18    }19}2021fn chebyshev(x: f64, degree: usize) -> Vec<f64> {22    let mut out = vec![1.0, x];23    for k in 2..=degree {24        out.push(2.0 * x * out[k - 1] - out[k - 2]);25    }26    out.truncate(degree + 1);27    out28}2930pub fn polynomial(values: &[f64], degree: usize) -> Spacings {31    let n = values.len();32    let (low, high) = (values[0], values[n - 1]);33    let scaled: Vec<f64> = values34        .iter()35        .map(|v| 2.0 * (v - low) / (high - low) - 1.0)36        .collect();37    let mut design = Mat::<f64>::zeros(n, degree + 1);38    let mut target = Mat::<f64>::zeros(n, 1);39    for (i, x) in scaled.iter().enumerate() {40        for (k, basis) in chebyshev(*x, degree).iter().enumerate() {41            design[(i, k)] = *basis;42        }43        target[(i, 0)] = i as f64 + 0.5;44    }45    let coefficients = design.qr().solve_lstsq(&target);46    let unfolded: Vec<f64> = scaled47        .iter()48        .map(|x| {49            chebyshev(*x, degree)50                .iter()51                .enumerate()52                .map(|(k, basis)| coefficients[(k, 0)] * basis)53                .sum()54        })55        .collect();56    spacings(&unfolded)57}5859pub fn window(values: &[f64], half: usize) -> Spacings {60    let n = values.len();61    let mut unfolded = vec![0.0; n];62    for i in 1..n {63        let lo = i.saturating_sub(half);64        let hi = (i + half).min(n - 1);65        let width = values[hi] - values[lo];66        let gap = values[i] - values[i - 1];67        let step = if gap > 0.0 {68            gap * (hi - lo) as f64 / width69        } else {70            0.071        };72        unfolded[i] = unfolded[i - 1] + step;73    }74    spacings(&unfolded)75}