All Downloads are FREE. Search and download functionalities are using the official Maven repository.

smile.math.distance.DynamicTimeWarping Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2010-2020 Haifeng Li. All rights reserved.
 *
 * Smile is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * Smile is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Smile.  If not, see .
 ******************************************************************************/

package smile.math.distance;

/**
 * Dynamic time warping is an algorithm for measuring similarity between two
 * sequences which may vary in time or speed. DTW has been applied to video,
 * audio, and graphics - indeed, any data which can be turned into a linear
 * representation can be analyzed with DTW. A well known application has been
 * automatic speech recognition, to cope with different speaking speeds.
 * 

* In general, DTW is a method that allows a computer to find an optimal * match between two given sequences (e.g. time series) with certain * restrictions. The sequences are "warped" non-linearly in the time dimension * to determine a measure of their similarity independent of certain non-linear * variations in the time dimension. This sequence alignment method is often * used in the context of hidden Markov models. *

* One example of the restrictions imposed on the matching of the sequences * is on the monotonicity of the mapping in the time dimension. Continuity * is less important in DTW than in other pattern matching algorithms; * DTW is an algorithm particularly suited to matching sequences with * missing information, provided there are long enough segments for matching * to occur. *

* The optimization process is performed using dynamic programming, hence the name. *

* The extension of the problem for two-dimensional "series" like images * (planar warping) is NP-complete, while the problem for one-dimensional * signals like time series can be solved in polynomial time. * * @author Haifeng Li */ public class DynamicTimeWarping implements Distance { private static final long serialVersionUID = 1L; private Distance distance; private double width = 1; /** * Constructor. Dynamic time warping without path constraints. */ public DynamicTimeWarping(Distance distance) { this.distance = distance; } /** * Dynamic time warping with Sakoe-Chiba band, which primarily to prevent * unreasonable warping and also improve computational cost. * @param radius the window width of Sakoe-Chiba band in terms of percentage of sequence length. */ public DynamicTimeWarping(Distance distance, double radius) { if (radius < 0 || radius > 1) { throw new IllegalArgumentException("radius = " + radius); } this.distance = distance; this.width = radius; } @Override public String toString() { return "Dynamic Time Warping"; } @Override public double d(T[] x1, T[] x2) { int n1 = x1.length; int n2 = x2.length; int radius = (int) Math.round(width * Math.max(n1, n2)); double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { int start = 1; int end = n2; if (radius > 0) { start = Math.max(1, i - radius); end = i + radius; if (end < n2) table[1][end+1] = Double.POSITIVE_INFINITY; else end = n2; } table[1][start - 1] = Double.POSITIVE_INFINITY; for (int j = start; j <= end; j++) { double cost = distance.d(x1[i-1], x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping without path constraints. */ public static double d(int[] x1, int[] x2) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { table[1][0] = Double.POSITIVE_INFINITY; for (int j = 1; j <= n2; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping with Sakoe-Chiba band, which primarily to prevent * unreasonable warping and also improve computational cost. * @param radius the window width of Sakoe-Chiba band. */ public static double d(int[] x1, int[] x2, int radius) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { int start = Math.max(1, i - radius); int end = Math.min(n2, i + radius); table[1][start-1] = Double.POSITIVE_INFINITY; if (end < n2) table[1][end+1] = Double.POSITIVE_INFINITY; for (int j = start; j <= end; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping without path constraints. */ public static double d(float[] x1, float[] x2) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { table[1][0] = Double.POSITIVE_INFINITY; for (int j = 1; j <= n2; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping with Sakoe-Chiba band, which primarily to prevent * unreasonable warping and also improve computational cost. * @param radius the window width of Sakoe-Chiba band. */ public static double d(float[] x1, float[] x2, int radius) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { int start = Math.max(1, i - radius); int end = Math.min(n2, i + radius); table[1][start-1] = Double.POSITIVE_INFINITY; if (end < n2) table[1][end+1] = Double.POSITIVE_INFINITY; for (int j = start; j <= end; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping without path constraints. */ public static double d(double[] x1, double[] x2) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { table[1][0] = Double.POSITIVE_INFINITY; for (int j = 1; j <= n2; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } /** * Dynamic time warping with Sakoe-Chiba band, which primarily to prevent * unreasonable warping and also improve computational cost. * @param radius the window width of Sakoe-Chiba band. */ public static double d(double[] x1, double[] x2, int radius) { int n1 = x1.length; int n2 = x2.length; double[][] table = new double[2][n2 + 1]; table[0][0] = 0; for (int i = 1; i <= n2; i++) { table[0][i] = Double.POSITIVE_INFINITY; } for (int i = 1; i <= n1; i++) { int start = Math.max(1, i - radius); int end = Math.min(n2, i + radius); table[1][start-1] = Double.POSITIVE_INFINITY; if (end < n2) table[1][end+1] = Double.POSITIVE_INFINITY; for (int j = start; j <= end; j++) { double cost = Math.abs(x1[i-1] - x2[j-1]); double min = table[0][j - 1]; if (min > table[0][j]) { min = table[0][j]; } if (min > table[1][j - 1]) { min = table[1][j - 1]; } table[1][j] = cost + min; } double[] swap = table[0]; table[0] = table[1]; table[1] = swap; } return table[0][n2]; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy