fraug/quality_benchmarking/
dtw.rs

1/// Implementation of Dynamic Time Warping (DTW) algorithm.
2/// This function computes the DTW distance between two sequences and returns the distance
3/// along with the optimal path.
4/// # Arguments
5/// * `a` - First sequence as a slice of f64 values.
6/// * `b` - Second sequence as a slice of f64 values.
7/// # Returns
8/// A tuple containing the DTW distance (f64) and a vector of tuples representing the
9/// optimal path as pairs of indices (usize, usize).
10/// # Examples
11/// ```
12/// use fraug::quality_benchmarking::dtw;
13/// let a = vec![1.0, 2.0, 3.0];
14/// let b = vec![2.0, 3.0, 4.0];
15/// let (distance, path) = dtw(&a, &b);
16/// ```
17
18pub fn dtw(a: &[f64], b: &[f64]) -> (f64, Vec<(usize, usize)>) {
19    let n = a.len();
20    let m = b.len();
21    let mut cost = vec![vec![f64::INFINITY; m + 1]; n + 1];
22    cost[0][0] = 0.0;
23    for i in 1..=n {
24        for j in 1..=m {
25            let diff = (a[i - 1] - b[j - 1]).abs();
26            let min_prev = cost[i - 1][j].min(cost[i][j - 1]).min(cost[i - 1][j - 1]);
27            cost[i][j] = diff + min_prev;
28        }
29    }
30    let distance = cost[n][m];
31    // Backtrack to get the best path
32    let mut path = Vec::new();
33    let (mut i, mut j) = (n, m);
34    while i > 0 || j > 0 {
35        path.push((i - 1, j - 1));
36        if i == 0 {
37            j -= 1;
38        } else if j == 0 {
39            i -= 1;
40        } else {
41            let diag = cost[i - 1][j - 1];
42            let up = cost[i - 1][j];
43            let left = cost[i][j - 1];
44            if diag <= up && diag <= left {
45                i -= 1;
46                j -= 1;
47            } else if up < left {
48                i -= 1;
49            } else {
50                j -= 1;
51            }
52        }
53    }
54    path.reverse();
55    (distance / n as f64, path)
56}