1use super::srsf::{reparameterize_curve, srsf_single, srsf_transform};
4use super::{dp_alignment_core, AlignmentResult};
5use crate::helpers::{l2_distance, simpsons_weights};
6use crate::iter_maybe_parallel;
7use crate::matrix::FdMatrix;
8#[cfg(feature = "parallel")]
9use rayon::iter::ParallelIterator;
10
11#[must_use = "expensive computation whose result should not be discarded"]
27pub fn elastic_align_pair(f1: &[f64], f2: &[f64], argvals: &[f64], lambda: f64) -> AlignmentResult {
28 let q1 = srsf_single(f1, argvals);
29 let q2 = srsf_single(f2, argvals);
30 elastic_align_pair_from_srsf(f2, &q1, &q2, argvals, lambda)
31}
32
33#[must_use = "expensive computation whose result should not be discarded"]
43pub fn elastic_distance(f1: &[f64], f2: &[f64], argvals: &[f64], lambda: f64) -> f64 {
44 elastic_align_pair(f1, f2, argvals, lambda).distance
45}
46
47fn elastic_align_pair_from_srsf(
55 f2: &[f64],
56 q1: &[f64],
57 q2: &[f64],
58 argvals: &[f64],
59 lambda: f64,
60) -> AlignmentResult {
61 let gamma = dp_alignment_core(q1, q2, argvals, lambda);
63
64 let f_aligned = reparameterize_curve(f2, argvals, &gamma);
66
67 let q_aligned = srsf_single(&f_aligned, argvals);
69
70 let weights = simpsons_weights(argvals);
71 let distance = l2_distance(q1, &q_aligned, &weights);
72
73 AlignmentResult {
74 gamma,
75 f_aligned,
76 distance,
77 }
78}
79
80fn elastic_distance_from_srsf(
84 f2: &[f64],
85 q1: &[f64],
86 q2: &[f64],
87 argvals: &[f64],
88 lambda: f64,
89) -> f64 {
90 let gamma = dp_alignment_core(q1, q2, argvals, lambda);
91 let f_aligned = reparameterize_curve(f2, argvals, &gamma);
92 let q_aligned = srsf_single(&f_aligned, argvals);
93 let weights = simpsons_weights(argvals);
94 l2_distance(q1, &q_aligned, &weights)
95}
96
97pub fn elastic_self_distance_matrix(data: &FdMatrix, argvals: &[f64], lambda: f64) -> FdMatrix {
115 let n = data.nrows();
116
117 let srsfs = srsf_transform(data, argvals);
119
120 let upper_vals: Vec<f64> = iter_maybe_parallel!(0..n)
121 .flat_map(|i| {
122 let qi = srsfs.row(i);
123 ((i + 1)..n)
124 .map(|j| {
125 let fj = data.row(j);
126 let qj = srsfs.row(j);
127 elastic_distance_from_srsf(&fj, &qi, &qj, argvals, lambda)
128 })
129 .collect::<Vec<_>>()
130 })
131 .collect();
132
133 let mut dist = FdMatrix::zeros(n, n);
134 let mut idx = 0;
135 for i in 0..n {
136 for j in (i + 1)..n {
137 let d = upper_vals[idx];
138 dist[(i, j)] = d;
139 dist[(j, i)] = d;
140 idx += 1;
141 }
142 }
143 dist
144}
145
146pub fn elastic_cross_distance_matrix(
160 data1: &FdMatrix,
161 data2: &FdMatrix,
162 argvals: &[f64],
163 lambda: f64,
164) -> FdMatrix {
165 let n1 = data1.nrows();
166 let n2 = data2.nrows();
167
168 let srsfs1 = srsf_transform(data1, argvals);
170 let srsfs2 = srsf_transform(data2, argvals);
171
172 let vals: Vec<f64> = iter_maybe_parallel!(0..n1)
173 .flat_map(|i| {
174 let qi = srsfs1.row(i);
175 (0..n2)
176 .map(|j| {
177 let fj = data2.row(j);
178 let qj = srsfs2.row(j);
179 elastic_distance_from_srsf(&fj, &qi, &qj, argvals, lambda)
180 })
181 .collect::<Vec<_>>()
182 })
183 .collect();
184
185 let mut dist = FdMatrix::zeros(n1, n2);
186 for i in 0..n1 {
187 for j in 0..n2 {
188 dist[(i, j)] = vals[i * n2 + j];
189 }
190 }
191 dist
192}
193
194pub fn amplitude_distance(f1: &[f64], f2: &[f64], argvals: &[f64], lambda: f64) -> f64 {
196 elastic_distance(f1, f2, argvals, lambda)
197}
198
199pub fn phase_distance_pair(f1: &[f64], f2: &[f64], argvals: &[f64], lambda: f64) -> f64 {
201 let alignment = elastic_align_pair(f1, f2, argvals, lambda);
202 crate::warping::phase_distance(&alignment.gamma, argvals)
203}
204
205pub fn phase_self_distance_matrix(data: &FdMatrix, argvals: &[f64], lambda: f64) -> FdMatrix {
207 let n = data.nrows();
208
209 let upper_vals: Vec<f64> = iter_maybe_parallel!(0..n)
210 .flat_map(|i| {
211 let fi = data.row(i);
212 ((i + 1)..n)
213 .map(|j| {
214 let fj = data.row(j);
215 phase_distance_pair(&fi, &fj, argvals, lambda)
216 })
217 .collect::<Vec<_>>()
218 })
219 .collect();
220
221 let mut dist = FdMatrix::zeros(n, n);
222 let mut idx = 0;
223 for i in 0..n {
224 for j in (i + 1)..n {
225 let d = upper_vals[idx];
226 dist[(i, j)] = d;
227 dist[(j, i)] = d;
228 idx += 1;
229 }
230 }
231 dist
232}
233
234pub fn amplitude_self_distance_matrix(data: &FdMatrix, argvals: &[f64], lambda: f64) -> FdMatrix {
236 elastic_self_distance_matrix(data, argvals, lambda)
237}