use super::pairwise::elastic_align_pair;
use super::srsf::reparameterize_curve;
use super::{AlignmentResult, AlignmentSetResult};
use crate::iter_maybe_parallel;
use crate::matrix::FdMatrix;
#[cfg(feature = "parallel")]
use rayon::iter::ParallelIterator;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct DecompositionResult {
pub alignment: AlignmentResult,
pub d_amplitude: f64,
pub d_phase: f64,
}
#[must_use = "expensive computation whose result should not be discarded"]
pub fn align_to_target(
data: &FdMatrix,
target: &[f64],
argvals: &[f64],
lambda: f64,
) -> AlignmentSetResult {
let (n, m) = data.shape();
let results: Vec<AlignmentResult> = iter_maybe_parallel!(0..n)
.map(|i| {
let fi = data.row(i);
elastic_align_pair(target, &fi, argvals, lambda)
})
.collect();
let mut gammas = FdMatrix::zeros(n, m);
let mut aligned_data = FdMatrix::zeros(n, m);
let mut distances = Vec::with_capacity(n);
for (i, r) in results.into_iter().enumerate() {
for j in 0..m {
gammas[(i, j)] = r.gamma[j];
aligned_data[(i, j)] = r.f_aligned[j];
}
distances.push(r.distance);
}
AlignmentSetResult {
gammas,
aligned_data,
distances,
}
}
pub fn elastic_decomposition(
f1: &[f64],
f2: &[f64],
argvals: &[f64],
lambda: f64,
) -> DecompositionResult {
let alignment = elastic_align_pair(f1, f2, argvals, lambda);
let d_amplitude = alignment.distance;
let d_phase = crate::warping::phase_distance(&alignment.gamma, argvals);
DecompositionResult {
alignment,
d_amplitude,
d_phase,
}
}
pub(super) fn apply_stored_warps(data: &FdMatrix, gammas: &FdMatrix, argvals: &[f64]) -> FdMatrix {
let (n, m) = data.shape();
let mut aligned = FdMatrix::zeros(n, m);
for i in 0..n {
let fi = data.row(i);
let gamma: Vec<f64> = (0..m).map(|j| gammas[(i, j)]).collect();
let f_aligned = reparameterize_curve(&fi, argvals, &gamma);
for j in 0..m {
aligned[(i, j)] = f_aligned[j];
}
}
aligned
}