├── FastDTW-x.podspec ├── FastDTW-x └── Classes │ ├── BinaryDistance.h │ ├── ColMajorCell.cpp │ ├── ColMajorCell.h │ ├── CostMatrix.h │ ├── DTW.h │ ├── EuclideanDistance.h │ ├── ExpandedResWindow.h │ ├── FDAssert.h │ ├── FDMath.h │ ├── FastDTW.cpp │ ├── FastDTW.h │ ├── Foundation.h │ ├── FullWindow.h │ ├── JavaTypes.h │ ├── LinearWindow.h │ ├── ManhattanDistance.h │ ├── MemoryResidentMatrix.h │ ├── PAA.h │ ├── PartialWindowMatrix.h │ ├── SearchWindow.cpp │ ├── SearchWindow.h │ ├── TimeSeries.h │ ├── TimeSeriesPoint.h │ ├── TimeWarpInfo.h │ ├── WarpPath.cpp │ └── WarpPath.h ├── LICENSE └── README.md /FastDTW-x.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "FastDTW-x" 3 | s.version = "1.1.0" 4 | s.summary = "C++ porting of Stan Salvador's FastDTW." 5 | s.homepage = "https://github.com/melode11/FastDTW-x" 6 | s.license = {:type => 'MIT'} 7 | s.author = {"Melo Yao" => "melode11@gmail.com" } 8 | s.source = {:git => "https://github.com/melode11/FastDTW-x.git", :tag => "#{s.version}" } 9 | s.source_files = 'FastDTW-x/Classes/**/*.{h,cpp}' 10 | s.requires_arc = false 11 | s.xcconfig = { 12 | 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++11', 13 | 'CLANG_CXX_LIBRARY' => 'libstdc++' 14 | } 15 | 16 | end -------------------------------------------------------------------------------- /FastDTW-x/Classes/BinaryDistance.h: -------------------------------------------------------------------------------- 1 | // 2 | // BinaryDistance.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/2/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__BinaryDistance__ 10 | #define __FastDTW_x__BinaryDistance__ 11 | 12 | #include "Foundation.h" 13 | #include 14 | #include "FDAssert.h" 15 | #include "TimeSeriesPoint.h" 16 | FD_NS_START 17 | class BinaryDistance 18 | { 19 | public: 20 | BinaryDistance() 21 | { 22 | 23 | } 24 | 25 | template 26 | ValueType calcDistance(const MeasurementVector& v1, const MeasurementVector& v2) const 27 | { 28 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 29 | if (v1 == v2) { 30 | return (ValueType)0.0; 31 | } 32 | else 33 | { 34 | return (ValueType)1.0; 35 | } 36 | } 37 | 38 | template 39 | ValueType calcDistance(const std::vector& v1, const std::vector& v2) const 40 | { 41 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 42 | if (v1 == v2) { 43 | return (ValueType)0.0; 44 | } 45 | else 46 | { 47 | return (ValueType)1.0; 48 | } 49 | } 50 | }; 51 | 52 | FD_NS_END 53 | #endif /* defined(__FastDTW_x__BinaryDistance__) */ 54 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/ColMajorCell.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ColMajorCell.cpp 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/4/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #include "ColMajorCell.h" 10 | FD_NS_START 11 | 12 | ColMajorCell::ColMajorCell():_col(0),_row(0) 13 | { 14 | } 15 | 16 | ColMajorCell::ColMajorCell(JInt col, JInt row):_col(col),_row(row) 17 | { 18 | 19 | } 20 | 21 | ColMajorCell::ColMajorCell(const ColMajorCell& cell):_col(cell._col),_row(cell._row) 22 | { 23 | } 24 | 25 | JInt ColMajorCell::getCol() const 26 | { 27 | return _col; 28 | } 29 | 30 | JInt ColMajorCell::getRow() const 31 | { 32 | return _row; 33 | } 34 | 35 | bool ColMajorCell::operator== (ColMajorCell const& cell) const 36 | { 37 | return _col == cell.getCol()&&_row == cell.getRow(); 38 | } 39 | 40 | bool ColMajorCell::operator< (ColMajorCell const& cell) const 41 | { 42 | return (getCol()*1024 + getRow()) < (cell.getCol()*1024 + cell.getRow()); 43 | } 44 | 45 | FD_NS_END -------------------------------------------------------------------------------- /FastDTW-x/Classes/ColMajorCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColMajorCell.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/4/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__ColMajorCell__ 10 | #define __FastDTW_x__ColMajorCell__ 11 | 12 | #include "Foundation.h" 13 | 14 | FD_NS_START 15 | 16 | class ColMajorCell 17 | { 18 | JInt _col; 19 | JInt _row; 20 | 21 | public: 22 | ColMajorCell(); 23 | 24 | ColMajorCell(JInt col, JInt row); 25 | 26 | ColMajorCell(const ColMajorCell& cell); 27 | 28 | JInt getCol() const; 29 | 30 | JInt getRow() const; 31 | 32 | bool operator== (ColMajorCell const& cell) const; 33 | 34 | bool operator< (ColMajorCell const& cell) const; 35 | }; 36 | 37 | FD_NS_END 38 | #endif /* defined(__FastDTW_x__ColMajorCell__) */ 39 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/CostMatrix.h: -------------------------------------------------------------------------------- 1 | // 2 | // CostMatrix.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/4/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__CostMatrix__ 10 | #define __FastDTW_x__CostMatrix__ 11 | 12 | #include "Foundation.h" 13 | FD_NS_START 14 | 15 | template 16 | class CostMatrix { 17 | 18 | public: 19 | virtual void put(JInt col,JInt row,ValueType value) = 0; 20 | 21 | virtual ValueType get(JInt col, JInt row) const = 0; 22 | 23 | virtual JInt size() const = 0; 24 | }; 25 | 26 | FD_NS_END 27 | #endif /* defined(__FastDTW_x__CostMatrix__) */ 28 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/DTW.h: -------------------------------------------------------------------------------- 1 | // 2 | // DTW.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/6/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__DTW__ 10 | #define __FastDTW_x__DTW__ 11 | 12 | #include "Foundation.h" 13 | #include "WarpPath.h" 14 | #include "TimeSeries.h" 15 | #include "ColMajorCell.h" 16 | #include "FDMath.h" 17 | #include "TimeWarpInfo.h" 18 | #include "SearchWindow.h" 19 | #include "PartialWindowMatrix.h" 20 | #include "MemoryResidentMatrix.h" 21 | #include 22 | #include 23 | FD_NS_START 24 | 25 | namespace STRI { 26 | using namespace std; 27 | 28 | template 29 | ValueType calcWarpCost(const WarpPath& path,const TimeSeries& tsI, const TimeSeries& tsJ, const DistanceFunction& distFn) 30 | { 31 | ValueType totalCost = 0.0; 32 | for (JInt p =0; p 42 | ValueType getWarpDistBetween(TimeSeries const& tsI, TimeSeries const& tsJ, DistanceFunction const& distFn) 43 | { 44 | // The space complexity is 2*tsJ.size(). Dynamic time warping is symmetric so switching the two time series 45 | // parameters does not effect the final warp cost but can reduce the space complexity by allowing tsJ to 46 | // be set as the shorter time series and only requiring 2 columns of size |tsJ| rather than 2 larger columns of 47 | // size |tsI|. 48 | if (tsI.size() < tsJ.size()) { 49 | return getWarpDistBetween(tsJ, tsI, distFn); 50 | } 51 | vector lastColumn(tsJ.size()); 52 | vector currColumn(tsJ.size()); 53 | JInt maxI = tsI.size() - 1; 54 | JInt maxJ = tsJ.size() - 1; 55 | // Calculate the values for the first column, from the bottom up. 56 | currColumn[0] = distFn.calcDistance(*tsI.getMeasurementVector(0), *tsJ.getMeasurementVector(0)); 57 | for (JInt j = 1; j* lastCol = &lastColumn; 61 | vector* currCol = &currColumn; 62 | for (JInt i = 1; i* temp = lastCol; 65 | lastCol = currCol; 66 | currCol = temp; 67 | // Calculate the value for the bottom row of the current column 68 | // (i,0) = LocalCost(i,0) + GlobalCost(i-1,0) 69 | (*currCol)[0] = (*lastCol)[0] + distFn.calcDistance(*tsI.getMeasurementVector(i), *tsJ.getMeasurementVector(0)); 70 | 71 | for (int j=1; j<=maxJ; j++) // j = rows 72 | { 73 | // (i,j) = LocalCost(i,j) + minGlobalCost{(i-1,j),(i-1,j-1),(i,j-1)} 74 | ValueType minGlobalCost = fd_min(lastCol->at(j), fd_min(lastCol->at(j-1), currCol->at(j-1))); 75 | (*currCol)[j] = minGlobalCost + distFn.calcDistance(*tsI.getMeasurementVector(i), *tsJ.getMeasurementVector(j)); 76 | } // end for loop 77 | } 78 | return currCol->at(maxJ); 79 | } 80 | 81 | template 82 | const TimeWarpInfo getWarpInfoBetween(TimeSeries const& tsI, TimeSeries const& tsJ, DistanceFunction const& distFn) 83 | { 84 | // COST MATRIX: 85 | // 5|_|_|_|_|_|_|E| E = min Global Cost 86 | // 4|_|_|_|_|_|_|_| S = Start point 87 | // 3|_|_|_|_|_|_|_| each cell = min global cost to get to that point 88 | // j 2|_|_|_|_|_|_|_| 89 | // 1|_|_|_|_|_|_|_| 90 | // 0|S|_|_|_|_|_|_| 91 | // 0 1 2 3 4 5 6 92 | // i 93 | // access is M(i,j)... column-row 94 | vector > costMatrix; 95 | costMatrix.reserve(tsI.size()); 96 | JInt jSize = tsJ.size(); 97 | for (JInt i = 0; i(jSize)); 99 | } 100 | JInt maxI = tsI.size() - 1; 101 | JInt maxJ = tsJ.size() - 1; 102 | costMatrix[0][0] = distFn.calcDistance(*tsI.getMeasurementVector(0), 103 | *tsJ.getMeasurementVector(0)); 104 | for (int j=1; j<=maxJ; j++) 105 | costMatrix[0][j] = costMatrix[0][j-1] + distFn.calcDistance(*tsI.getMeasurementVector(0), 106 | *tsJ.getMeasurementVector(j)); 107 | for (int i=1; i<=maxI; i++) // i = columns 108 | { 109 | // Calculate the value for the bottom row of the current column 110 | // (i,0) = LocalCost(i,0) + GlobalCost(i-1,0) 111 | costMatrix[i][0] = costMatrix[i-1][0] + distFn.calcDistance(*tsI.getMeasurementVector(i), 112 | *tsJ.getMeasurementVector(0)); 113 | 114 | for (int j=1; j<=maxJ; j++) // j = rows 115 | { 116 | // (i,j) = LocalCost(i,j) + minGlobalCost{(i-1,j),(i-1,j-1),(i,j-1)} 117 | ValueType minGlobalCost = fd_min(costMatrix[i-1][j], 118 | fd_min(costMatrix[i-1][j-1], 119 | costMatrix[i][j-1])); 120 | costMatrix[i][j] = minGlobalCost + distFn.calcDistance(*tsI.getMeasurementVector(i), 121 | *tsJ.getMeasurementVector(j)); 122 | } 123 | } 124 | ValueType minimumCost = costMatrix[maxI][maxJ]; 125 | WarpPath minCostPath(maxI + maxJ - 1); 126 | JInt i = maxI; 127 | JInt j = maxJ; 128 | minCostPath.addFirst(i,j); 129 | while (i>0 || j>0) { 130 | ValueType diagCost; 131 | ValueType leftCost; 132 | ValueType downCost; 133 | if ((i>0) && (j>0)) 134 | { 135 | diagCost = costMatrix[i-1][j-1]; 136 | } 137 | else 138 | { 139 | diagCost = numeric_limits::max(); 140 | } 141 | if (i > 0) 142 | { 143 | leftCost = costMatrix[i-1][j]; 144 | } 145 | else 146 | { 147 | leftCost = numeric_limits::max(); 148 | } 149 | if (j > 0) 150 | { 151 | downCost = costMatrix[i][j-1]; 152 | } 153 | else 154 | { 155 | downCost = numeric_limits::max(); 156 | } 157 | 158 | // Determine which direction to move in. Prefer moving diagonally and 159 | // moving towards the i==j axis of the matrix if there are ties. 160 | if ((diagCost<=leftCost) && (diagCost<=downCost)) 161 | { 162 | i--; 163 | j--; 164 | } 165 | else if ((leftCost diagCost 174 | { 175 | j--; 176 | } 177 | else // leftCost==rightCost > diagCost 178 | { 179 | i--; 180 | } 181 | minCostPath.addFirst(i, j); 182 | } 183 | //Let Return Value Optimization do its job. 184 | return TimeWarpInfo(minimumCost, minCostPath); 185 | } 186 | 187 | template 188 | ValueType getWarpDistBetween(TimeSeries const& tsI,TimeSeries const& tsJ,SearchWindow const& window, DistanceFunction const& distFn) 189 | { 190 | // COST MATRIX: 191 | // 5|_|_|_|_|_|_|E| E = min Global Cost 192 | // 4|_|_|_|_|_|_|_| S = Start point 193 | // 3|_|_|_|_|_|_|_| each cell = min global cost to get to that point 194 | // j 2|_|_|_|_|_|_|_| 195 | // 1|_|_|_|_|_|_|_| 196 | // 0|S|_|_|_|_|_|_| 197 | // 0 1 2 3 4 5 6 198 | // i 199 | // access is M(i,j)... column-row 200 | PartialWindowMatrix costMatrix(window); 201 | JInt maxI = tsI.size()-1; 202 | JInt maxJ = tsI.size() -1; 203 | // Get an iterator that traverses the window cells in the order that the cost matrix is filled. 204 | // (first to last row (1..maxI), bottom to top (1..MaxJ) 205 | SearchWindowIterator matrixIterator = window.iterator(); 206 | while (matrixIterator.hasNext()) { 207 | ColMajorCell currentCell = matrixIterator.next(); 208 | JInt i = currentCell.getCol(); 209 | JInt j = currentCell.getRow(); 210 | if (i == 0 && j==0) { // bottom left cell (first row AND first column) 211 | costMatrix.put(i,j,distFn.calcDistance(*tsI.getMeasurementVector(0),*tsJ.getMeasurementVector(0))); 212 | } 213 | else if (i == 0) // first column 214 | { 215 | costMatrix.put(i, j, distFn.calcDistance(*tsI.getMeasurementVector(0), *tsJ.getMeasurementVector(j)) + 216 | costMatrix.get(i, j-1)); 217 | } 218 | else if (j == 0) // first row 219 | { 220 | costMatrix.put(i, j, distFn.calcDistance(*tsI.getMeasurementVector(i), *tsJ.getMeasurementVector(0)) + 221 | costMatrix.get(i-1, j)); 222 | } 223 | else // not first column or first row 224 | { 225 | ValueType minGlobalCost = fd_min(costMatrix.get(i-1, j), 226 | fd_min(costMatrix.get(i-1, j-1), 227 | costMatrix.get(i, j-1))); 228 | costMatrix.put(i, j, minGlobalCost + distFn.calcDistance(*tsI.getMeasurementVector(i), 229 | *tsJ.getMeasurementVector(j))); 230 | } 231 | } 232 | return costMatrix.get(maxI,maxJ); 233 | } 234 | 235 | template 236 | TimeWarpInfo getWarpInfoBetween(TimeSeries const& tsI, TimeSeries const& tsJ,SearchWindow const& window, DistanceFunction const& distFn) 237 | { 238 | // COST MATRIX: 239 | // 5|_|_|_|_|_|_|E| E = min Global Cost 240 | // 4|_|_|_|_|_|_|_| S = Start point 241 | // 3|_|_|_|_|_|_|_| each cell = min global cost to get to that point 242 | // j 2|_|_|_|_|_|_|_| 243 | // 1|_|_|_|_|_|_|_| 244 | // 0|S|_|_|_|_|_|_| 245 | // 0 1 2 3 4 5 6 246 | // i 247 | // access is M(i,j)... column-row 248 | MemoryResidentMatrix costMatrix(&window); 249 | JInt maxI = tsI.size()-1; 250 | JInt maxJ = tsJ.size()-1; 251 | 252 | // Get an iterator that traverses the window cells in the order that the cost matrix is filled. 253 | // (first to last row (1..maxI), bottom to top (1..MaxJ) 254 | SearchWindowIterator matrixIterator = window.iterator(); 255 | 256 | while (matrixIterator.hasNext()) 257 | { 258 | ColMajorCell currentCell = matrixIterator.next(); // current cell being filled 259 | JInt i = currentCell.getCol(); 260 | JInt j = currentCell.getRow(); 261 | 262 | if ( (i==0) && (j==0) ) // bottom left cell (first row AND first column) 263 | costMatrix.put(i, j, distFn.calcDistance(*tsI.getMeasurementVector(0), *tsJ.getMeasurementVector(0))); 264 | else if (i == 0) // first column 265 | { 266 | costMatrix.put(i, j, distFn.calcDistance(*tsI.getMeasurementVector(0), *tsJ.getMeasurementVector(j)) + 267 | costMatrix.get(i, j-1)); 268 | } 269 | else if (j == 0) // first row 270 | { 271 | costMatrix.put(i, j, distFn.calcDistance(*tsI.getMeasurementVector(i), *tsJ.getMeasurementVector(0)) + 272 | costMatrix.get(i-1, j)); 273 | } 274 | else // not first column or first row 275 | { 276 | ValueType minGlobalCost = fd_min(costMatrix.get(i-1, j), 277 | fd_min(costMatrix.get(i-1, j-1), 278 | costMatrix.get(i, j-1))); 279 | costMatrix.put(i, j, minGlobalCost + distFn.calcDistance(*tsI.getMeasurementVector(i), 280 | *tsJ.getMeasurementVector(j))); 281 | } 282 | } 283 | 284 | // Minimum Cost is at (maxI, maxJ) 285 | ValueType minimumCost = costMatrix.get(maxI, maxJ); 286 | 287 | WarpPath minCostPath(maxI+maxJ-1); 288 | JInt i = maxI; 289 | JInt j = maxJ; 290 | minCostPath.addFirst(i, j); 291 | while ((i>0) || (j>0)) 292 | { 293 | // Find the costs of moving in all three possible directions (left, 294 | // down, and diagonal (down and left at the same time). 295 | ValueType diagCost; 296 | ValueType leftCost; 297 | ValueType downCost; 298 | 299 | if ((i>0) && (j>0)) 300 | diagCost = costMatrix.get(i-1, j-1); 301 | else 302 | diagCost = numeric_limits::max(); 303 | 304 | if (i > 0) 305 | leftCost = costMatrix.get(i-1, j); 306 | else 307 | leftCost = numeric_limits::max(); 308 | 309 | if (j > 0) 310 | downCost = costMatrix.get(i, j-1); 311 | else 312 | downCost = numeric_limits::max(); 313 | 314 | // Determine which direction to move in. Prefer moving diagonally and 315 | // moving towards the i==j axis of the matrix if there are ties. 316 | if ((diagCost<=leftCost) && (diagCost<=downCost)) 317 | { 318 | i--; 319 | j--; 320 | } 321 | else if ((leftCost diagCost 326 | j--; 327 | else // leftCost==rightCost > diagCost 328 | i--; 329 | 330 | // Add the current step to the warp path. 331 | minCostPath.addFirst(i, j); 332 | } 333 | 334 | return TimeWarpInfo(minimumCost, minCostPath); 335 | } 336 | } 337 | 338 | FD_NS_END 339 | #endif /* defined(__FastDTW_x__DTW__) */ 340 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/EuclideanDistance.h: -------------------------------------------------------------------------------- 1 | // 2 | // EuclideanDistance.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/2/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__EuclideanDistance__ 10 | #define __FastDTW_x__EuclideanDistance__ 11 | 12 | #include "FDAssert.h" 13 | #include "FDMath.h" 14 | #include 15 | #include "TimeSeriesPoint.h" 16 | 17 | FD_NS_START 18 | class EuclideanDistance 19 | { 20 | public: 21 | EuclideanDistance() 22 | { 23 | 24 | } 25 | 26 | template 27 | ValueType calcDistance(const MeasurementVector& v1, const MeasurementVector& v2) const 28 | { 29 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 30 | double sqSum = 0.0; 31 | size_t size = v1.size(); 32 | for (JInt i = 0; i 39 | ValueType calcDistance(const std::vector& v1, const std::vector& v2) const 40 | { 41 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 42 | double sqSum = 0.0; 43 | size_t size = v1.size(); 44 | for (size_t i = 0; i 18 | 19 | FD_NS_START 20 | class ExpandedResWindow : public SearchWindow { 21 | 22 | 23 | public: 24 | template 25 | ExpandedResWindow(TimeSeries const& tsI,TimeSeries const& tsJ, 26 | PAA const& shrunkI,PAA const& shrunkJ,WarpPath const& shrunkWarpPath, JInt searchRadius):SearchWindow(tsI.size(),tsJ.size()) 27 | { 28 | // Variables to keep track of the current location of the higher resolution projected path. 29 | JInt currentI = shrunkWarpPath.minI(); 30 | JInt currentJ = shrunkWarpPath.minJ(); 31 | 32 | // Variables to keep track of the last part of the low-resolution warp path that was evaluated 33 | // (to determine direction). 34 | JInt lastWarpedI = numeric_limits::max(); 35 | JInt lastWarpedJ = numeric_limits::max(); 36 | 37 | // For each part of the low-resolution warp path, project that path to the higher resolution by filling in the 38 | // path's corresponding cells at the higher resolution. 39 | for (JInt w=0; w lastWarpedJ) 50 | currentJ += shrunkJ.aggregatePtSize(lastWarpedJ); 51 | 52 | // If the path moved up or diagonally, then the next cell's values on the J axis will be larger. 53 | if (warpedI > lastWarpedI) 54 | currentI += shrunkI.aggregatePtSize(lastWarpedI); 55 | 56 | // If a diagonal move was performed, add 2 cells to the edges of the 2 blocks in the projected path to create 57 | // a continuous path (path with even width...avoid a path of boxes connected only at their corners). 58 | // |_|_|x|x| then mark |_|_|x|x| 59 | // ex: projected path: |_|_|x|x| --2 more cells-> |_|X|x|x| 60 | // |x|x|_|_| (X's) |x|x|X|_| 61 | // |x|x|_|_| |x|x|_|_| 62 | if ((warpedJ>lastWarpedJ) && (warpedI>lastWarpedI)) 63 | { 64 | SearchWindow::markVisited(currentI-1, currentJ); 65 | SearchWindow::markVisited(currentI, currentJ-1); 66 | } // end if 67 | 68 | // Fill in the cells that are created by a projection from the cell in the low-resolution warp path to a 69 | // higher resolution. 70 | for (int x=0; x 12 | #include 13 | 14 | #define FDASSERT0(__assertion__,__msg__) \ 15 | do {if(!(__assertion__)){printf(__msg__);assert(false);}}while(0) 16 | 17 | #define FDASSERT(__assertion__,__msg__,...) \ 18 | do {if(!(__assertion__)){printf(__msg__,__VA_ARGS__);assert(false);}}while(0) 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/FDMath.h: -------------------------------------------------------------------------------- 1 | // 2 | // Math.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/2/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef FastDTW_x_Math_h 10 | #define FastDTW_x_Math_h 11 | #include "Foundation.h" 12 | #include 13 | #define fd_max std::max 14 | #define fd_min std::min 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/FastDTW.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FastDTW.cpp 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/9/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #include "FastDTW.h" 10 | FD_NS_START 11 | namespace FAST { 12 | const JInt DEFAULT_SEARCH_RADIUS = 1; 13 | } 14 | FD_NS_END -------------------------------------------------------------------------------- /FastDTW-x/Classes/FastDTW.h: -------------------------------------------------------------------------------- 1 | // 2 | // FastDTW.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/9/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__FastDTW__ 10 | #define __FastDTW_x__FastDTW__ 11 | 12 | #include "Foundation.h" 13 | #include "DTW.h" 14 | #include "PAA.h" 15 | #include "ExpandedResWindow.h" 16 | 17 | //#include "TimeWarpInfo.h" 18 | //#include "TimeSeries.h" 19 | 20 | FD_NS_START 21 | 22 | namespace FAST { 23 | 24 | extern const JInt DEFAULT_SEARCH_RADIUS; 25 | 26 | template 27 | TimeWarpInfo getWarpInfoBetween(TimeSeries const& tsI, TimeSeries const& tsJ, JInt searchRadius, DistanceFunction const& distFn) 28 | { 29 | if (searchRadius < 0) { 30 | searchRadius = 0; 31 | } 32 | JInt minTSsize = searchRadius + 2; 33 | if (tsI.size() <= minTSsize || tsJ.size()<=minTSsize) { 34 | return STRI::getWarpInfoBetween(tsI, tsJ, distFn); 35 | } 36 | else 37 | { 38 | JDouble resolutionFactor = 2.0; 39 | PAA shrunkI(tsI,(JInt)(tsI.size()/resolutionFactor)); 40 | PAA shrunkJ(tsJ,(JInt)(tsJ.size()/resolutionFactor)); 41 | // Determine the search window that constrains the area of the cost matrix that will be evaluated based on 42 | // the warp path found at the previous resolution (smaller time series). 43 | TimeWarpInfo warpInfo = getWarpInfoBetween(shrunkI, shrunkJ, searchRadius, distFn); 44 | ExpandedResWindow window(tsI, tsJ, shrunkI, shrunkJ, 45 | *(warpInfo.getPath()), 46 | searchRadius); 47 | return STRI::getWarpInfoBetween(tsI, tsJ, window, distFn); 48 | } 49 | 50 | } 51 | 52 | template 53 | inline ValueType getWarpDistBetween(TimeSeries const& tsI,TimeSeries const& tsJ, DistanceFunction const& distFn) 54 | { 55 | return getWarpInfoBetween(tsI, tsJ, DEFAULT_SEARCH_RADIUS, distFn).getDistance(); 56 | } 57 | 58 | template 59 | inline TimeWarpInfo getWarpInfoBetween(TimeSeries const& tsI,TimeSeries const& tsJ,DistanceFunction const& distFn) 60 | { 61 | return getWarpInfoBetween(tsI, tsJ, DEFAULT_SEARCH_RADIUS, distFn); 62 | } 63 | 64 | template 65 | inline ValueType getWarpDistBetween(TimeSeries const& tsI,TimeSeries const& tsJ, 66 | JInt searchRadius,DistanceFunction const& distFn) 67 | { 68 | return getWarpInfoBetween(tsI, tsJ, searchRadius, distFn).getDistance(); 69 | } 70 | 71 | 72 | 73 | } 74 | FD_NS_END 75 | #endif /* defined(__FastDTW_x__FastDTW__) */ 76 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/Foundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Foundation.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/2/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef FastDTW_x_Foundation_h 10 | #define FastDTW_x_Foundation_h 11 | #include "JavaTypes.h" 12 | 13 | #define FD_NS_START namespace fastdtw { 14 | 15 | #define FD_NS_END } 16 | #endif 17 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/FullWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // FullWindow.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/5/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__FullWindow__ 10 | #define __FastDTW_x__FullWindow__ 11 | 12 | #include "Foundation.h" 13 | #include "SearchWindow.h" 14 | #include "TimeSeries.h" 15 | FD_NS_START 16 | 17 | class FullWindow : public SearchWindow 18 | { 19 | public: 20 | template 21 | FullWindow(const TimeSeries& tsI, const TimeSeries& tsJ):SearchWindow(tsI.size(),tsJ.size()) 22 | { 23 | for (JInt i = 0; i 16 | #include "FDMath.h" 17 | 18 | FD_NS_START 19 | class LinearWindow : public SearchWindow 20 | { 21 | public: 22 | template 23 | LinearWindow(const TimeSeries& tsI, const TimeSeries& tsJ, JInt searchRadius):SearchWindow(tsI.size(),tsJ.size()) 24 | { 25 | JDouble ijRatio = tsI.size()/(JDouble)tsJ.size(); 26 | JBool isIlargest = tsI.size() >= tsJ.size(); 27 | for (JInt i = 0; i 14 | #include "FDAssert.h" 15 | #include "TimeSeriesPoint.h" 16 | 17 | FD_NS_START 18 | class ManhattanDistance 19 | { 20 | public: 21 | ManhattanDistance() 22 | { 23 | 24 | } 25 | 26 | template 27 | ValueType calcDistance(const MeasurementVector& v1, const MeasurementVector& v2) const 28 | { 29 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 30 | ValueType diffSum = 0; 31 | size_t size = v1.size(); 32 | for (size_t i = 0; i 40 | ValueType calcDistance(const std::vector& v1, const std::vector& v2) const 41 | { 42 | FDASSERT0(v1.size()==v2.size(),"ERROR: cannot calculate the distance between vectors of different sizes."); 43 | ValueType diffSum = 0; 44 | size_t size = v1.size(); 45 | for (size_t i = 0; i 16 | #include 17 | FD_NS_START 18 | 19 | using namespace std; 20 | 21 | template 22 | class MemoryResidentMatrix : public CostMatrix 23 | { 24 | vector _cellValues; 25 | vector _colOffsets; 26 | const SearchWindow* _window; 27 | public: 28 | MemoryResidentMatrix(const SearchWindow* searchWindow):_window(searchWindow),_cellValues(searchWindow->size()),_colOffsets(searchWindow->maxI()+1) 29 | { 30 | JInt currentOffset = 0; 31 | for (JInt i = searchWindow->minI(); i<=searchWindow->maxI(); ++i) { 32 | _colOffsets[i] = currentOffset; 33 | currentOffset += searchWindow->maxJForI(i) - searchWindow->minJForI(i) + 1; 34 | } 35 | } 36 | 37 | void put(JInt col, JInt row, ValueType value) 38 | { 39 | FDASSERT(row>=_window->minJForI(col) && row <= _window->maxJForI(col), "CostMatrix is filled in a cell (col=%ld, row=%ld) that is not in the search window",col,row); 40 | _cellValues[_colOffsets[col] + row - _window->minJForI(col)] = value; 41 | } 42 | 43 | ValueType get(JInt col, JInt row) const 44 | { 45 | if (row < _window->minJForI(col) || row > _window->maxJForI(col)) { 46 | return numeric_limits::max(); 47 | } 48 | else 49 | { 50 | return _cellValues[_colOffsets[col] + row - _window->minJForI(col)]; 51 | } 52 | } 53 | 54 | JInt size() const 55 | { 56 | return _cellValues.size(); 57 | } 58 | }; 59 | FD_NS_END 60 | #endif /* defined(__FastDTW_x__MemoryResidentMatrix__) */ 61 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/PAA.h: -------------------------------------------------------------------------------- 1 | // 2 | // PAA.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/5/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__PAA__ 10 | #define __FastDTW_x__PAA__ 11 | 12 | #include "Foundation.h" 13 | #include 14 | #include "TimeSeries.h" 15 | #include "FDAssert.h" 16 | #include 17 | FD_NS_START 18 | template 19 | class PAA : public TimeSeries 20 | { 21 | vector _aggPtSize; 22 | JInt _originalLength; 23 | public: 24 | 25 | PAA(const TimeSeries& ts, JInt shrunkSize):TimeSeries(), _originalLength(ts.size()),_aggPtSize(shrunkSize) 26 | { 27 | FDASSERT(shrunkSize>0 && shrunkSize <= ts.size(),"ERROR: The size of an aggregate representation must be greater than zero and \nno larger than the original time series. (shrunkSize=%ld , origSize=%ld).",shrunkSize,ts.size()); 28 | // Ensures that the data structure storing the time series will not need 29 | // to be expanded more than once. (not necessary, for optimization) 30 | TimeSeries::setMaxCapacity(shrunkSize); 31 | TimeSeries::setLabels(*ts.getLabels()); 32 | JDouble reducedPtSize = ts.size()/(JDouble)shrunkSize; 33 | JInt ptToReadFrom(0); 34 | JInt ptToReadTo; 35 | while (ptToReadFrom < ts.size()) { 36 | ptToReadTo = (JInt)round(reducedPtSize*(TimeSeries::size()+1)) -1; 37 | 38 | JInt ptsToRead = ptToReadTo - ptToReadFrom + 1; 39 | JDouble timeSum(0.0); 40 | ValueType measurementSums[nDimension]; 41 | fill(measurementSums, measurementSums+nDimension, 0); 42 | for (JInt pt = ptToReadFrom; pt<=ptToReadTo; ++pt) { 43 | const MeasurementVector *currentPoint = ts.getMeasurementVector(pt); 44 | timeSum += ts.getTimeAtNthPoint(pt); 45 | for (JInt dim = 0; dim::size()] = ptsToRead; 55 | TimeSeries::addLast(timeSum,TimeSeriesPoint(measurementSums)); 56 | ptToReadFrom = ptToReadTo + 1; 57 | } 58 | } 59 | 60 | JInt originalSize() const 61 | { 62 | return _originalLength; 63 | } 64 | 65 | JInt aggregatePtSize(JInt ptIndex) const 66 | { 67 | return _aggPtSize[ptIndex]; 68 | } 69 | 70 | void print(ostream& stream) const 71 | { 72 | TimeSeries::print(stream); 73 | stream<<"original len:"<<_originalLength<<"\n"; 74 | for(JInt i = 0;i<_aggPtSize.size();++i) 75 | { 76 | stream<<_aggPtSize[i] << ","; 77 | } 78 | stream<<"\n"; 79 | 80 | } 81 | }; 82 | 83 | FD_NS_END 84 | #endif /* defined(__FastDTW_x__PAA__) */ 85 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/PartialWindowMatrix.h: -------------------------------------------------------------------------------- 1 | // 2 | // PartialWindowMatrix.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/8/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__PartialWindowMatrix__ 10 | #define __FastDTW_x__PartialWindowMatrix__ 11 | 12 | #include "Foundation.h" 13 | #include 14 | #include 15 | #include "CostMatrix.h" 16 | #include "SearchWindow.h" 17 | #include "FDAssert.h" 18 | 19 | using namespace std; 20 | FD_NS_START 21 | template 22 | class PartialWindowMatrix : public CostMatrix 23 | { 24 | vector _lastCol; 25 | vector _currCol; 26 | JInt _currColIndex; 27 | JInt _minLastRow; 28 | JInt _minCurrRow; 29 | const SearchWindow *_window; 30 | public: 31 | PartialWindowMatrix(const SearchWindow* searchWindow):_window(searchWindow),_lastCol(),_currCol() 32 | { 33 | if (searchWindow->maxI() > 0) { 34 | _currCol.resize(searchWindow->maxJForI(1) - searchWindow->minJForI(1) + 1); 35 | _currColIndex = 1; 36 | _minLastRow = searchWindow->minJForI(_currColIndex - 1); 37 | } 38 | else 39 | { 40 | _currColIndex = 0; 41 | _minLastRow = 0; 42 | } 43 | _minCurrRow = searchWindow->minJForI(_currColIndex); 44 | _lastCol.resize(searchWindow->maxJForI(0) - searchWindow->minJForI(0) + 1); 45 | } 46 | 47 | 48 | void put(JInt col, JInt row, ValueType value) 49 | { 50 | FDASSERT(row>=_window->minJForI(col)&&row<=_window->maxJForI(col), "CostMatrix is filled in a cell (col=%d, row=%d) that is not in the search window",col, row); 51 | if (col == _currColIndex) { 52 | _currCol[row - _minCurrRow] = value; 53 | } 54 | else if(col == _currColIndex - 1) 55 | { 56 | _lastCol[row - _minLastRow] = value; 57 | } 58 | else if(col == _currColIndex + 1) 59 | { 60 | _lastCol = _currCol; 61 | _minLastRow = _minCurrRow; 62 | _currColIndex ++; 63 | _currCol.clear(); 64 | _currCol.resize(_window->maxJForI(col) - _window->minJForI(col) + 1); 65 | _minCurrRow = _window->minJForI(col); 66 | _currCol[row - _minCurrRow] = value; 67 | } 68 | } 69 | 70 | ValueType get(JInt col, JInt row) const 71 | { 72 | if(row>=_window->minJForI(col) && row<=_window->maxJForI(col)) 73 | { 74 | if (col == _currColIndex) { 75 | return _currCol[row - _minCurrRow]; 76 | } 77 | else if (col == _currColIndex - 1) 78 | { 79 | return _lastCol[row - _minLastRow]; 80 | } 81 | } 82 | return numeric_limits::max(); 83 | } 84 | 85 | JInt size() const 86 | { 87 | return _lastCol.size() + _currCol.size(); 88 | } 89 | 90 | JInt windowSize() const 91 | { 92 | return _window->size(); 93 | } 94 | }; 95 | FD_NS_END 96 | #endif /* defined(__FastDTW_x__PartialWindowMatrix__) */ 97 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/SearchWindow.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SearchWindow.cpp 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/4/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #include "SearchWindow.h" 10 | #include "FDAssert.h" 11 | #include "FDMath.h" 12 | 13 | FD_NS_START 14 | 15 | SearchWindowIterator::SearchWindowIterator(const SearchWindow* w):_window(w),_hasMoreElements(w->size()>0),_currentI(w->minI()),_currentJ(w->minJ()),_expectedModCount(w->getModCount()) 16 | { 17 | 18 | } 19 | 20 | SearchWindowIterator::~SearchWindowIterator() 21 | { 22 | 23 | } 24 | 25 | JBool SearchWindowIterator::hasNext() const 26 | { 27 | return _hasMoreElements; 28 | } 29 | 30 | ColMajorCell SearchWindowIterator::next() 31 | { 32 | FDASSERT0(_window->getModCount() == _expectedModCount, "ConcurrentModificationException"); 33 | FDASSERT0(_hasMoreElements, "NoSuchElementException"); 34 | ColMajorCell cell(_currentI,_currentJ); 35 | if(++_currentJ > _window->maxJForI(_currentI)) 36 | { 37 | if (++_currentI<=_window->maxI()) { 38 | _currentJ = _window->minJForI(_currentI); 39 | } 40 | else 41 | { 42 | _hasMoreElements = false; 43 | } 44 | } 45 | return cell; 46 | } 47 | 48 | SearchWindow::SearchWindow(JInt tsIsize, JInt tsJsize):_minValues(tsIsize, -1),_maxValues(tsIsize),_maxJ(tsJsize -1),_size(0),_modCount(0) 49 | { 50 | // fill(_minValues.begin(), _minValues.end(), -1); 51 | // fill(_maxValues.begin(), _maxValues.end(), 0); 52 | } 53 | 54 | SearchWindow::~SearchWindow() 55 | { 56 | 57 | } 58 | 59 | JBool SearchWindow::isInWindow(JInt i, JInt j) const 60 | { 61 | return (i>=minI())&&(i<=maxI())&&(_minValues[i]<=j)&&(_maxValues[i]>=j); 62 | } 63 | 64 | JInt SearchWindow::minI() const 65 | { 66 | return 0; 67 | } 68 | 69 | JInt SearchWindow::maxI() const 70 | { 71 | return _minValues.size() - 1; 72 | } 73 | 74 | JInt SearchWindow::minJ() const 75 | { 76 | return 0; 77 | } 78 | 79 | JInt SearchWindow::maxJ() const 80 | { 81 | return _maxJ; 82 | } 83 | 84 | JInt SearchWindow::minJForI(JInt i) const 85 | { 86 | return _minValues[i]; 87 | } 88 | 89 | JInt SearchWindow::maxJForI(JInt i) const 90 | { 91 | return _maxValues[i]; 92 | } 93 | 94 | JInt SearchWindow::size() const 95 | { 96 | return _size; 97 | } 98 | 99 | JInt SearchWindow::getModCount() const 100 | { 101 | return _modCount; 102 | } 103 | 104 | SearchWindowIterator SearchWindow::iterator() const 105 | { 106 | return SearchWindowIterator(this); 107 | } 108 | 109 | void SearchWindow::expandSearchWindow(JInt radius) 110 | { 111 | if (radius >0) { 112 | // Add all cells in the current Window to an array, iterating through the window and expanding the window 113 | // at the same time is not possible because the window can't be changed during iteration through the cells. 114 | vector windowCells; 115 | windowCells.reserve(size()); 116 | SearchWindowIterator it = iterator(); 117 | for (; it.hasNext();) { 118 | windowCells.push_back(it.next()); 119 | } 120 | for (JInt cell = 0; cell=minI()&&targetRow<=maxJ()) { 127 | markVisited(targetCol, targetRow); 128 | } 129 | else 130 | { 131 | JInt cellsPastEdge = fd_max(minI() - targetCol, targetRow - maxJ()); 132 | markVisited(targetCol+cellsPastEdge, targetRow - cellsPastEdge); 133 | } 134 | } 135 | 136 | if (currentCell.getRow() != maxJ()) {// move up if possible 137 | JInt targetCol = currentCell.getCol(); 138 | JInt targetRow = currentCell.getRow()+radius; 139 | if (targetRow <= maxJ()) { 140 | markVisited(targetCol, targetRow); 141 | } 142 | else 143 | { 144 | JInt cellsPastEdge = targetRow - maxJ(); 145 | markVisited(targetCol, targetRow - cellsPastEdge); 146 | } 147 | } 148 | 149 | if (currentCell.getCol()!=maxI()&¤tCell.getRow()!=maxJ()) {// move to upper-right if possible 150 | JInt targetCol = currentCell.getCol() + radius; 151 | JInt targetRow = currentCell.getRow() + radius; 152 | if (targetCol<=maxI()&&targetRow<=maxJ()) { 153 | markVisited(targetCol, targetRow); 154 | } 155 | else 156 | { 157 | JInt cellsPastEdge = fd_max(targetCol - maxI() , targetRow - maxJ()); 158 | markVisited(targetCol-cellsPastEdge, targetRow-cellsPastEdge); 159 | } 160 | } 161 | if (currentCell.getCol()!=minI()) {// move left if possible 162 | JInt targetCol = currentCell.getCol() - radius; 163 | JInt targetRow = currentCell.getRow(); 164 | if (targetCol >= minI()) { 165 | markVisited(targetCol, targetRow); 166 | } 167 | else 168 | { 169 | JInt cellsPastEdge = minI() - targetCol; 170 | markVisited(targetCol+cellsPastEdge, targetRow); 171 | } 172 | } 173 | 174 | if (currentCell.getCol()!= maxI()) {// move right if possible 175 | JInt targetCol = currentCell.getCol() + radius; 176 | JInt targetRow = currentCell.getRow(); 177 | if (targetCol<=maxI()) { 178 | markVisited(targetCol, targetRow); 179 | } 180 | else 181 | { 182 | JInt cellsPastEdge = targetCol - maxI(); 183 | markVisited(targetCol - cellsPastEdge, targetRow); 184 | } 185 | } 186 | 187 | if (currentCell.getCol()!=minI() && currentCell.getRow()!=minJ()) { // move to lower-left if possible 188 | JInt targetCol = currentCell.getCol() - radius; 189 | JInt targetRow = currentCell.getRow() - radius; 190 | if (targetCol>=minI() && targetRow>=minJ()) { 191 | markVisited(targetCol, targetRow); 192 | } 193 | else 194 | { 195 | JInt cellsPastEdge = fd_max(minI() - targetCol, minJ() - targetRow); 196 | markVisited(targetCol+cellsPastEdge,targetRow+cellsPastEdge); 197 | } 198 | } 199 | 200 | if (currentCell.getRow()!=minJ()) {// move down if possible 201 | JInt targetCol = currentCell.getCol(); 202 | JInt targetRow = currentCell.getRow() - radius; 203 | if (targetRow>=minJ()) { 204 | markVisited(targetCol, targetRow); 205 | } 206 | else 207 | { 208 | JInt cellsPastEdge = minJ() - targetRow; 209 | markVisited(targetCol, targetRow+cellsPastEdge); 210 | } 211 | } 212 | 213 | if (currentCell.getCol()!=maxI() && currentCell.getRow() != minJ()) {// move to lower-right if possible 214 | JInt targetCol = currentCell.getCol() + radius; 215 | JInt targetRow = currentCell.getRow() - radius; 216 | if (targetCol<=maxI() && targetRow>=minJ()) { 217 | markVisited(targetCol, targetRow); 218 | } 219 | else 220 | { 221 | JInt cellsPastEdge = fd_max(targetCol-maxI(), minJ() - targetRow); 222 | markVisited(targetCol-cellsPastEdge, targetRow+cellsPastEdge); 223 | } 224 | } 225 | } 226 | } 227 | 228 | } 229 | 230 | void SearchWindow::expandWindow(JInt radius) 231 | { 232 | if (radius>0) { 233 | expandSearchWindow(1); 234 | expandSearchWindow(radius - 1); 235 | } 236 | } 237 | 238 | void SearchWindow::markVisited(JInt col, JInt row) 239 | { 240 | if (_minValues[col] == -1) { 241 | _minValues[col] = row; 242 | _maxValues[col] = row; 243 | _size++; 244 | _modCount++; 245 | } 246 | else if(_minValues[col]>row) 247 | { 248 | _size+=_minValues[col] - row; 249 | _minValues[col] = row; 250 | _modCount++; 251 | } 252 | else if(_maxValues[col] 14 | #include 15 | #include "ColMajorCell.h" 16 | 17 | FD_NS_START 18 | class SearchWindow; 19 | 20 | class SearchWindowIterator 21 | { 22 | JInt _currentI; 23 | JInt _currentJ; 24 | const SearchWindow* _window; 25 | JBool _hasMoreElements; 26 | JInt _expectedModCount; 27 | 28 | protected: 29 | SearchWindowIterator(const SearchWindow* w); 30 | public: 31 | ~SearchWindowIterator(); 32 | JBool hasNext() const; 33 | ColMajorCell next(); 34 | 35 | friend class SearchWindow; 36 | }; 37 | 38 | using namespace std; 39 | class SearchWindow 40 | { 41 | vector _minValues; 42 | vector _maxValues; 43 | JInt _maxJ; 44 | JInt _size; 45 | JInt _modCount; 46 | 47 | void expandSearchWindow(JInt radius); 48 | public: 49 | SearchWindow(JInt tsIsize, JInt tsJsize); 50 | 51 | virtual ~SearchWindow(); 52 | 53 | JBool isInWindow(JInt i,JInt j) const; 54 | 55 | JInt minI() const; 56 | 57 | JInt maxI() const; 58 | 59 | JInt minJForI(JInt i) const; 60 | 61 | JInt maxJForI(JInt i) const; 62 | 63 | JInt minJ() const; 64 | 65 | JInt maxJ() const; 66 | 67 | JInt size() const; 68 | 69 | JInt getModCount() const; 70 | 71 | SearchWindowIterator iterator() const; 72 | 73 | protected: 74 | void expandWindow(JInt radius); 75 | 76 | void markVisited(JInt col, JInt row); 77 | 78 | }; 79 | 80 | FD_NS_END 81 | #endif /* defined(__FastDTW_x__SearchWindow__) */ 82 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/TimeSeries.h: -------------------------------------------------------------------------------- 1 | // 2 | // TimeSeries.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/3/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__TimeSeries__ 10 | #define __FastDTW_x__TimeSeries__ 11 | 12 | #include "Foundation.h" 13 | #include 14 | #include 15 | #include "TimeSeriesPoint.h" 16 | #include "FDAssert.h" 17 | #include 18 | #include 19 | #include 20 | 21 | FD_NS_START 22 | using namespace std; 23 | template 24 | class TimeSeries 25 | { 26 | private: 27 | static const JInt ZERO_ARRAY[0]; 28 | static const JBool DEFAULT_IS_TIME_1ST_COL = true; 29 | static const JChar DEFAULT_DELIMITER = ','; 30 | static const JBool DEFAULT_IS_LABELED = true; 31 | 32 | 33 | protected: 34 | vector _labels; 35 | vector _timeReadings; 36 | vector > _tsArray; 37 | 38 | void setMaxCapacity(JInt capacity) 39 | { 40 | _timeReadings.reserve(capacity); 41 | _tsArray.reserve(capacity); 42 | } 43 | public: 44 | 45 | 46 | TimeSeries():_labels(),_timeReadings(),_tsArray() 47 | { 48 | _labels.push_back(string("time")); 49 | stringstream ss; 50 | 51 | for (JInt i = 0; isize(); 79 | } 80 | 81 | JInt numOfDimensions() const 82 | { 83 | return _labels.size() - 1;//potential bug when use default constructor 84 | } 85 | 86 | JDouble getTimeAtNthPoint(JInt n) const 87 | { 88 | return _timeReadings[n]; 89 | } 90 | 91 | string& getLabel(JInt n) const 92 | { 93 | return _labels[n]; 94 | } 95 | // 96 | // JInt getLabelsArr(string* strArr,JInt maxNum) 97 | // { 98 | // JInt len = maxNum>_labels.size()?_labels.size():maxNum; 99 | // memcpy(strArr, _labels.data(), len*sizeof(string)); 100 | // return len; 101 | // } 102 | 103 | void setLabels(const vector& lbs) 104 | { 105 | _labels = lbs; 106 | } 107 | 108 | void setLabels(const string* strArr,JInt num) 109 | { 110 | _labels.clear(); 111 | _labels.reserve(num); 112 | copy(strArr, strArr+num, _labels.begin()); 113 | } 114 | 115 | const vector* getLabels() const 116 | { 117 | return &_labels; 118 | } 119 | 120 | ValueType getMeasurement(JInt pointIndex, string& valueLabel) const 121 | { 122 | JInt idx = find(_labels.begin(), _labels.end(), valueLabel) - _labels.begin(); 123 | FDASSERT(idx>0, "ERROR: the label %s was not one of labels",valueLabel.data()); 124 | return _tsArray[pointIndex].get(idx - 1); 125 | } 126 | 127 | ValueType getMeasurement(JInt pointIndex, JInt valueIndex) const 128 | { 129 | return _tsArray[pointIndex].get(valueIndex); 130 | } 131 | 132 | const MeasurementVector* getMeasurementVector(JInt pointIndex) const 133 | { 134 | return _tsArray[pointIndex].toArray(); 135 | } 136 | 137 | void setMeasurement(JInt pointIndex,JInt valueIndex,ValueType value) 138 | { 139 | _tsArray[pointIndex].set(valueIndex,value); 140 | } 141 | 142 | void addFirst(JDouble time, TimeSeriesPoint const& values) 143 | { 144 | FDASSERT(values.size()+1 == _labels.size(), "ERROR: The TimeSeriesPoint contains the wrong number of values. expected:%ld,found:%ld",_labels.size()-1,values.size()); 145 | FDASSERT0(time<_timeReadings[0], "ERROR: The point being inserted into the beginning of the time series does not have the correct time sequence."); 146 | _timeReadings.insert(_timeReadings.begin(), time); 147 | _tsArray.insert(_tsArray.begin(),values); 148 | } 149 | 150 | void addLast(JDouble time, TimeSeriesPoint const& values) 151 | { 152 | FDASSERT(values.size()+1 == _labels.size(), "ERROR: The TimeSeriesPoint contains the wrong number of values. expected:%ld,found:%ld",_labels.size()-1,values.size()); 153 | FDASSERT0(_timeReadings.size()==0 || time>_timeReadings[_timeReadings.size() - 1], "ERROR: The point being inserted into the beginning of the time series does not have the correct time sequence."); 154 | _timeReadings.push_back(time); 155 | _tsArray.push_back(values); 156 | } 157 | 158 | virtual void print(ostream& stream) const 159 | { 160 | stream<<"time readings ["<<_timeReadings.size()<<"]:"; 161 | for (JInt i = 0; i<_timeReadings.size(); ++i) { 162 | stream<<_timeReadings[i] << ","; 163 | } 164 | stream<<"\n"; 165 | stream<<"time series ["<<_tsArray.size()<<"]:"; 166 | for(JInt i = 0;i<_tsArray.size();++i) 167 | { 168 | _tsArray[i].print(stream); 169 | stream << ","; 170 | } 171 | stream<<"\n"; 172 | } 173 | }; 174 | 175 | FD_NS_END 176 | #endif /* defined(__FastDTW_x__TimeSeries__) */ 177 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/TimeSeriesPoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // TimeSeriesPoint.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/3/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__TimeSeriesPoint__ 10 | #define __FastDTW_x__TimeSeriesPoint__ 11 | #include "Foundation.h" 12 | #include "FDAssert.h" 13 | #include 14 | #include 15 | #include 16 | FD_NS_START 17 | using namespace std; 18 | 19 | //Fixed dimension TimeSeriesPoint template 20 | template 21 | class MeasurementVector 22 | { 23 | vector value; 24 | 25 | public: 26 | MeasurementVector():value(nDimension) 27 | { 28 | } 29 | 30 | MeasurementVector(const ValueType* meas):value(nDimension) 31 | { 32 | copy(meas, meas+nDimension, value.begin()); 33 | } 34 | 35 | void setDynamicMeasurements(const ValueType* meas, JInt nDim) 36 | { 37 | FDASSERT0(false, "Invalid method for dynamic TimeSeriesPoint only(set dimension template parameter to 0 to use dynamic TimeSeriesPoint)."); 38 | } 39 | 40 | JInt size() const 41 | { 42 | return value.size(); 43 | } 44 | 45 | ValueType operator[](JInt index) const 46 | { 47 | return value[index]; 48 | } 49 | 50 | ValueType& operator[](JInt index) 51 | { 52 | return value[index]; 53 | } 54 | 55 | bool operator==(const MeasurementVector& mv) const 56 | { 57 | return value == mv.value; 58 | } 59 | 60 | bool operator<(const MeasurementVector& mv) const 61 | { 62 | return value < mv.value; 63 | } 64 | 65 | void print(ostream& stream) const 66 | { 67 | stream<<"p("; 68 | for (JInt i = 0; i 77 | class MeasurementVector { 78 | 79 | ValueType value; 80 | 81 | public: 82 | MeasurementVector():value(0) 83 | { 84 | } 85 | 86 | MeasurementVector(const ValueType* meas):value(*meas) 87 | { 88 | } 89 | 90 | void setDynamicMeasurements(const ValueType* meas, JInt nDim) 91 | { 92 | FDASSERT0(false, "Invalid method for dynamic TimeSeriesPoint only(set dimension template parameter to 0 to use dynamic TimeSeriesPoint)."); 93 | } 94 | 95 | JInt size() const 96 | { 97 | return 1; 98 | } 99 | 100 | ValueType operator[](JInt index) const 101 | { 102 | return value; 103 | } 104 | 105 | 106 | ValueType& operator[](JInt index) 107 | { 108 | return value; 109 | } 110 | 111 | bool operator==(const MeasurementVector& mv) const 112 | { 113 | return value == mv.value; 114 | } 115 | 116 | bool operator<(const MeasurementVector& mv) const 117 | { 118 | return value < mv.value; 119 | } 120 | 121 | void print(ostream& stream) const 122 | { 123 | stream<<"p("< 130 | class MeasurementVector { 131 | 132 | vector value; 133 | 134 | public: 135 | MeasurementVector():value() 136 | { 137 | } 138 | 139 | MeasurementVector(const ValueType* meas):value() 140 | { 141 | } 142 | 143 | void setDynamicMeasurements(const ValueType* meas, JInt nDim) 144 | { 145 | value.resize(nDim); 146 | for (int i = 0; i& mv) const 167 | { 168 | return value == mv.value; 169 | } 170 | 171 | bool operator<(const MeasurementVector& mv) const 172 | { 173 | return value < mv.value; 174 | } 175 | 176 | void print(ostream& stream) const 177 | { 178 | stream<<"p("; 179 | for (JInt i = 0; i 187 | class TimeSeriesPoint { 188 | MeasurementVector _measurements; 189 | 190 | public: 191 | TimeSeriesPoint(const ValueType* meas):_measurements(meas) 192 | { 193 | } 194 | 195 | ValueType get(JInt dimension) const 196 | { 197 | return _measurements[dimension]; 198 | } 199 | 200 | void setDynamicMeasurements(const ValueType* meas, JInt nDim) 201 | { 202 | _measurements.setDynamicMeasurements(meas,nDim); 203 | } 204 | 205 | void set(JInt dimension,ValueType newValue) 206 | { 207 | _measurements[dimension] = newValue; 208 | } 209 | 210 | JInt size() const 211 | { 212 | return _measurements.size(); 213 | } 214 | 215 | const MeasurementVector* toArray() const 216 | { 217 | return &_measurements; 218 | } 219 | 220 | bool operator==(const TimeSeriesPoint& p) const 221 | { 222 | return _measurements == p._measurements; 223 | } 224 | 225 | bool operator<(TimeSeriesPoint& p) 226 | { 227 | return _measurements < p._measurements; 228 | } 229 | 230 | ~TimeSeriesPoint() 231 | { 232 | } 233 | 234 | void print(ostream& stream) const 235 | { 236 | _measurements.print(stream); 237 | } 238 | }; 239 | 240 | FD_NS_END 241 | #endif /* defined(__FastDTW_x__TimeSeriesPoint__) */ 242 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/TimeWarpInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // TimeWarpInfo.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/5/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__TimeWarpInfo__ 10 | #define __FastDTW_x__TimeWarpInfo__ 11 | 12 | #include "Foundation.h" 13 | #include "WarpPath.h" 14 | FD_NS_START 15 | template 16 | class TimeWarpInfo 17 | { 18 | ValueType _distance; 19 | WarpPath _path; 20 | 21 | public: 22 | TimeWarpInfo(ValueType dist, const WarpPath& wp):_distance(dist), _path(wp) 23 | { 24 | } 25 | 26 | ValueType getDistance() const 27 | { 28 | return _distance; 29 | } 30 | 31 | const WarpPath* getPath() const //Unsafe pointer,For reducing the copy cost 32 | { 33 | return &_path; 34 | } 35 | }; 36 | 37 | FD_NS_END 38 | #endif /* defined(__FastDTW_x__TimeWarpInfo__) */ 39 | -------------------------------------------------------------------------------- /FastDTW-x/Classes/WarpPath.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // WrapPath.cpp 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/5/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #include "WarpPath.h" 10 | #include 11 | #include "FDAssert.h" 12 | 13 | FD_NS_START 14 | 15 | WarpPath::WarpPath(JInt initialCapacity) : _tsIindexes(),_tsJindexes() 16 | { 17 | _tsIindexes.reserve(initialCapacity); 18 | _tsJindexes.reserve(initialCapacity); 19 | } 20 | 21 | 22 | JInt WarpPath::size() const 23 | { 24 | return _tsIindexes.size(); 25 | } 26 | 27 | JInt WarpPath::minI() const 28 | { 29 | return _tsIindexes[0]; 30 | } 31 | 32 | JInt WarpPath::minJ() const 33 | { 34 | return _tsJindexes[0]; 35 | } 36 | 37 | JInt WarpPath::maxI() const 38 | { 39 | return _tsIindexes[_tsIindexes.size() - 1]; 40 | } 41 | 42 | JInt WarpPath::maxJ() const 43 | { 44 | return _tsJindexes[_tsJindexes.size() - 1]; 45 | } 46 | 47 | void WarpPath::addFirst(JInt i, JInt j) 48 | { 49 | _tsIindexes.insert(_tsIindexes.begin(), i); 50 | _tsJindexes.insert(_tsJindexes.begin(), j); 51 | } 52 | 53 | void WarpPath::addLast(JInt i, JInt j) 54 | { 55 | _tsIindexes.push_back(i); 56 | _tsJindexes.push_back(j); 57 | } 58 | 59 | void WarpPath::getMatchingIndexesForI(JInt i,vector& outVec) const 60 | { 61 | //find first time i appears 62 | vector::const_iterator it = find(_tsIindexes.begin(), _tsIindexes.end(), i); 63 | //find continuous indices of i. 64 | while (it != _tsIindexes.end() && *it == i) 65 | { 66 | outVec.push_back(_tsJindexes[it-_tsIindexes.begin()]); 67 | ++it; 68 | } 69 | } 70 | 71 | void WarpPath::getMatchingIndexesForJ(JInt j,vector& outVec) const 72 | { 73 | vector::const_iterator it = find(_tsJindexes.begin(), _tsJindexes.end(), j); 74 | while (it!=_tsJindexes.end() && *it == j) { 75 | outVec.push_back(_tsIindexes[it-_tsJindexes.begin()]); 76 | ++it; 77 | } 78 | } 79 | 80 | void WarpPath::invertedCopy(WarpPath& path) const 81 | { 82 | path._tsIindexes = _tsJindexes; 83 | path._tsJindexes = _tsIindexes; 84 | } 85 | 86 | void WarpPath::invert() 87 | { 88 | vector tmp = _tsIindexes; 89 | _tsIindexes = _tsJindexes; 90 | _tsJindexes = tmp; 91 | } 92 | 93 | ColMajorCell WarpPath::get(JInt index) const 94 | { 95 | //Original Java code have boundary check bug here. 96 | //if ( (index>this.size()) || (index<0) ) 97 | FDASSERT0(index>=0 && index<_tsIindexes.size(), "NoSuchElementException"); 98 | return ColMajorCell(_tsIindexes[index],_tsJindexes[index]); 99 | 100 | } 101 | 102 | bool WarpPath::operator==(const WarpPath& path) const 103 | { 104 | return _tsIindexes == path._tsIindexes && _tsJindexes == path._tsJindexes; 105 | } 106 | 107 | bool WarpPath::operator<(const WarpPath& path) const //For containers, so arbitery judgement works here. 108 | { 109 | if (_tsIindexes==path._tsIindexes) 110 | { 111 | return _tsJindexes < path._tsJindexes; 112 | } 113 | else 114 | { 115 | return _tsIindexes < path._tsIindexes; 116 | } 117 | } 118 | 119 | void WarpPath::print(ostream& stream) const 120 | { 121 | for (JInt i = 0; i<_tsIindexes.size(); ++i) { 122 | stream<<"("<<_tsIindexes[i]<<"," << _tsJindexes[i] << ") "; 123 | } 124 | stream<<"\n"; 125 | } 126 | 127 | FD_NS_END -------------------------------------------------------------------------------- /FastDTW-x/Classes/WarpPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // WrapPath.h 3 | // FastDTW-x 4 | // 5 | // Created by Melo Yao on 12/5/13. 6 | // Copyright (c) 2013 melo.yao. All rights reserved. 7 | // 8 | 9 | #ifndef __FastDTW_x__WrapPath__ 10 | #define __FastDTW_x__WrapPath__ 11 | 12 | #include "Foundation.h" 13 | #include 14 | #include "ColMajorCell.h" 15 | #include 16 | 17 | FD_NS_START 18 | using namespace std; 19 | class WarpPath 20 | { 21 | vector _tsIindexes; 22 | vector _tsJindexes; 23 | 24 | public: 25 | WarpPath(JInt initialCapacity); 26 | 27 | JInt size() const; 28 | 29 | JInt minI() const; 30 | 31 | JInt minJ() const; 32 | 33 | JInt maxI() const; 34 | 35 | JInt maxJ() const; 36 | 37 | void addFirst(JInt i, JInt j); 38 | 39 | void addLast(JInt i, JInt j); 40 | 41 | void getMatchingIndexesForI(JInt i,vector& outVec) const; 42 | 43 | void getMatchingIndexesForJ(JInt j,vector& outVec) const; 44 | 45 | void invertedCopy(WarpPath& path) const; 46 | 47 | void invert(); 48 | 49 | ColMajorCell get(JInt index) const; 50 | 51 | bool operator==(const WarpPath& path) const; 52 | 53 | bool operator<(const WarpPath& path) const; 54 | 55 | void print(ostream& stream) const; 56 | 57 | }; 58 | 59 | FD_NS_END 60 | #endif /* defined(__FastDTW_x__WrapPath__) */ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 melode11 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FastDTW-x 2 | ========= 3 | 4 | C++ porting of Stan Salvador's FastDTW 5 | 6 | Usage: 7 | iOS,use CocoaPods,add line in podfile: 8 | pod 'FastDTW-x' 9 | Other platform,Import all source files in Classes folder. 10 | 11 | Sample code: 12 | 13 | #include "DTW.h" 14 | #include "FastDTW.h" 15 | #include "EuclideanDistance.h" 16 | #include 17 | 18 | using namespace fastdtw; 19 | 20 | extern double *sample1; 21 | extern double *sample2; 22 | extern int sampleLength; 23 | 24 | void testDTW() 25 | { 26 | TimeSeries tsI; 27 | for (int i = 0; i(sample1+i)); 29 | } 30 | 31 | TimeSeries tsJ; 32 | for (int i = 0;i(sample2+i)); 35 | } 36 | 37 | TimeWarpInfo info = STRI::getWarpInfoBetween(tsI,tsJ,EuclideanDistance()); 38 | printf("Warp Distance by DTW:%lf\n",info.getDistance()); 39 | info.getPath()->print(std::cout); 40 | } 41 | 42 | void testFastDTW() 43 | { 44 | TimeSeries tsI; 45 | for (int i = 0; i(sample1+i)); 47 | } 48 | 49 | TimeSeries tsJ; 50 | for (int i = 0;i(sample2+i)); 53 | } 54 | 55 | TimeWarpInfo info = FAST::getWarpInfoBetween(tsI,tsJ,EuclideanDistance()); 56 | printf("Warp Distance by DTW:%lf\n",info.getDistance()); 57 | info.getPath()->print(std::cout); 58 | } 59 | 60 | --------------------------------------------------------------------------------