ucrdtw 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +39 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/ext/Rakefile +3 -0
- data/ext/ucrdtw.c +1055 -0
- data/ext/ucrdtw.h +4 -0
- data/lib/ucrdtw.rb +36 -0
- data/lib/ucrdtw/version.rb +3 -0
- data/ucrdtw.gemspec +25 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 768c8ff98e2715907cd54634adecd0e339c49eac
|
4
|
+
data.tar.gz: f9693c53387ab2e9b3103bff5b1220e98b13027a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8e5721af3c15237f5d7dc03b93d05c45abb33da631144685ab46f82903dba14d200167e02e4f52571d684aae5fb405ac1be28d1028a5e42480c1d98c95594898
|
7
|
+
data.tar.gz: e9438ed7a11dd15d2cb53c6fa9590fa6388d75fea7d16b00e2874e33a48a69e4179c9b4551a1c0678879e6a4c59c5adc594e72090b5024efc5fc86c9673ed442
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Ucrdtw
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ucrdtw`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'ucrdtw'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install ucrdtw
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it ( https://github.com/[my-github-username]/ucrdtw/fork )
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ucrdtw"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/ext/Rakefile
ADDED
data/ext/ucrdtw.c
ADDED
@@ -0,0 +1,1055 @@
|
|
1
|
+
/***********************************************************************/
|
2
|
+
/************************* DISCLAIMER **********************************/
|
3
|
+
/***********************************************************************/
|
4
|
+
/** This UCR Suite software is copyright protected (c) 2012 by **/
|
5
|
+
/** Thanawin Rakthanmanon, Bilson Campana, Abdullah Mueen, **/
|
6
|
+
/** Gustavo Batista and Eamonn Keogh. **/
|
7
|
+
/** **/
|
8
|
+
/** Unless stated otherwise, all software is provided free of charge. **/
|
9
|
+
/** As well, all software is provided on an "as is" basis without **/
|
10
|
+
/** warranty of any kind, express or implied. Under no circumstances **/
|
11
|
+
/** and under no legal theory, whether in tort, contract,or otherwise,**/
|
12
|
+
/** shall Thanawin Rakthanmanon, Bilson Campana, Abdullah Mueen, **/
|
13
|
+
/** Gustavo Batista, or Eamonn Keogh be liable to you or to any other **/
|
14
|
+
/** person for any indirect, special, incidental, or consequential **/
|
15
|
+
/** damages of any character including, without limitation, damages **/
|
16
|
+
/** for loss of goodwill, work stoppage, computer failure or **/
|
17
|
+
/** malfunction, or for any and all other damages or losses. **/
|
18
|
+
/** **/
|
19
|
+
/** If you do not agree with these terms, then you you are advised to **/
|
20
|
+
/** not use this software. **/
|
21
|
+
/***********************************************************************/
|
22
|
+
/***********************************************************************/
|
23
|
+
#include <stdio.h>
|
24
|
+
#include <stdlib.h>
|
25
|
+
#include <math.h>
|
26
|
+
#include <string.h>
|
27
|
+
#include <time.h>
|
28
|
+
|
29
|
+
#include "ucrdtw.h"
|
30
|
+
|
31
|
+
#define min(x,y) ((x)<(y)?(x):(y))
|
32
|
+
#define max(x,y) ((x)>(y)?(x):(y))
|
33
|
+
#define dist(x,y) ((x-y)*(x-y))
|
34
|
+
|
35
|
+
#define INF 1e20 // Pseudo-infinite number for this code
|
36
|
+
|
37
|
+
/// Data structure for sorting the query
|
38
|
+
typedef struct index {
|
39
|
+
double value;
|
40
|
+
int index;
|
41
|
+
} index_t;
|
42
|
+
|
43
|
+
/// Sorting function for the query, sort by abs(z_norm(q[i])) from high to low
|
44
|
+
int index_comp(const void* a, const void* b) {
|
45
|
+
index_t* x = (index_t*) a;
|
46
|
+
index_t* y = (index_t*) b;
|
47
|
+
return fabs(y->value) - fabs(x->value); // high to low
|
48
|
+
}
|
49
|
+
|
50
|
+
/// Data structure (circular array) for finding minimum and maximum for LB_Keogh envolop
|
51
|
+
typedef struct deque {
|
52
|
+
int* dq;
|
53
|
+
int size, capacity;
|
54
|
+
int f, r;
|
55
|
+
} deque_t;
|
56
|
+
|
57
|
+
/// Initial the queue at the beginning step of envelope calculation
|
58
|
+
void deque_init(deque_t* d, int capacity) {
|
59
|
+
d->capacity = capacity;
|
60
|
+
d->size = 0;
|
61
|
+
d->dq = (int*) calloc(d->capacity, sizeof(int));
|
62
|
+
d->f = 0;
|
63
|
+
d->r = d->capacity - 1;
|
64
|
+
}
|
65
|
+
|
66
|
+
/// Destroy the queue
|
67
|
+
void deque_free(deque_t* d) {
|
68
|
+
free(d->dq);
|
69
|
+
}
|
70
|
+
|
71
|
+
/// Insert to the queue at the back
|
72
|
+
void deque_push_back(deque_t* d, int v) {
|
73
|
+
d->dq[d->r] = v;
|
74
|
+
d->r--;
|
75
|
+
if (d->r < 0)
|
76
|
+
d->r = d->capacity - 1;
|
77
|
+
d->size++;
|
78
|
+
}
|
79
|
+
|
80
|
+
/// Delete the current (front) element from queue
|
81
|
+
void deque_pop_front(deque_t* d) {
|
82
|
+
d->f--;
|
83
|
+
if (d->f < 0)
|
84
|
+
d->f = d->capacity - 1;
|
85
|
+
d->size--;
|
86
|
+
}
|
87
|
+
|
88
|
+
/// Delete the last element from queue
|
89
|
+
void deque_pop_back(deque_t* d) {
|
90
|
+
d->r = (d->r + 1) % d->capacity;
|
91
|
+
d->size--;
|
92
|
+
}
|
93
|
+
|
94
|
+
/// Get the value at the current position of the circular queue
|
95
|
+
int deque_front(deque_t* d) {
|
96
|
+
int aux = d->f - 1;
|
97
|
+
|
98
|
+
if (aux < 0)
|
99
|
+
aux = d->capacity - 1;
|
100
|
+
return d->dq[aux];
|
101
|
+
}
|
102
|
+
|
103
|
+
/// Get the value at the last position of the circular queue
|
104
|
+
int deque_back(deque_t* d) {
|
105
|
+
int aux = (d->r + 1) % d->capacity;
|
106
|
+
return d->dq[aux];
|
107
|
+
}
|
108
|
+
|
109
|
+
/// Check whether or not the queue is empty
|
110
|
+
int deque_empty(deque_t* d) {
|
111
|
+
return d->size == 0;
|
112
|
+
}
|
113
|
+
|
114
|
+
/// Finding the envelope of min and max value for LB_Keogh
|
115
|
+
/// Implementation idea is introduced by Danial Lemire in his paper
|
116
|
+
/// "Faster Retrieval with a Two-Pass Dynamic-Time-Warping Lower Bound", Pattern Recognition 42(9), 2009.
|
117
|
+
void lower_upper_lemire(double *t, int len, int r, double *l, double *u) {
|
118
|
+
struct deque du, dl;
|
119
|
+
int i;
|
120
|
+
|
121
|
+
deque_init(&du, 2 * r + 2);
|
122
|
+
deque_init(&dl, 2 * r + 2);
|
123
|
+
|
124
|
+
deque_push_back(&du, 0);
|
125
|
+
deque_push_back(&dl, 0);
|
126
|
+
|
127
|
+
for (i = 1; i < len; i++) {
|
128
|
+
if (i > r) {
|
129
|
+
u[i - r - 1] = t[deque_front(&du)];
|
130
|
+
l[i - r - 1] = t[deque_front(&dl)];
|
131
|
+
}
|
132
|
+
if (t[i] > t[i - 1]) {
|
133
|
+
deque_pop_back(&du);
|
134
|
+
while (!deque_empty(&du) && t[i] > t[deque_back(&du)]) {
|
135
|
+
deque_pop_back(&du);
|
136
|
+
}
|
137
|
+
} else {
|
138
|
+
deque_pop_back(&dl);
|
139
|
+
while (!deque_empty(&dl) && t[i] < t[deque_back(&dl)]) {
|
140
|
+
deque_pop_back(&dl);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
deque_push_back(&du, i);
|
144
|
+
deque_push_back(&dl, i);
|
145
|
+
if (i == 2 * r + 1 + deque_front(&du)) {
|
146
|
+
deque_pop_front(&du);
|
147
|
+
} else if (i == 2 * r + 1 + deque_front(&dl)) {
|
148
|
+
deque_pop_front(&dl);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
for (i = len; i < len + r + 1; i++) {
|
152
|
+
u[i - r - 1] = t[deque_front(&du)];
|
153
|
+
l[i - r - 1] = t[deque_front(&dl)];
|
154
|
+
if (i - deque_front(&du) >= 2 * r + 1) {
|
155
|
+
deque_pop_front(&du);
|
156
|
+
}
|
157
|
+
if (i - deque_front(&dl) >= 2 * r + 1) {
|
158
|
+
deque_pop_front(&dl);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
deque_free(&du);
|
162
|
+
deque_free(&dl);
|
163
|
+
}
|
164
|
+
|
165
|
+
/// Calculate quick lower bound
|
166
|
+
/// Usually, LB_Kim take time O(m) for finding top,bottom,fist and last.
|
167
|
+
/// However, because of z-normalization the top and bottom cannot give significant benefits.
|
168
|
+
/// And using the first and last points can be computed in constant time.
|
169
|
+
/// The pruning power of LB_Kim is non-trivial, especially when the query is not long, say in length 128.
|
170
|
+
double lb_kim_hierarchy(double *t, double *q, int j, int len, double mean, double std, double best_so_far) {
|
171
|
+
/// 1 point at front and back
|
172
|
+
double d, lb;
|
173
|
+
double x0 = (t[j] - mean) / std;
|
174
|
+
double y0 = (t[(len - 1 + j)] - mean) / std;
|
175
|
+
lb = dist(x0,q[0]) + dist(y0, q[len - 1]);
|
176
|
+
if (lb >= best_so_far)
|
177
|
+
return lb;
|
178
|
+
|
179
|
+
/// 2 points at front
|
180
|
+
double x1 = (t[(j + 1)] - mean) / std;
|
181
|
+
d = min(dist(x1,q[0]), dist(x0,q[1]));
|
182
|
+
d = min(d, dist(x1,q[1]));
|
183
|
+
lb += d;
|
184
|
+
if (lb >= best_so_far)
|
185
|
+
return lb;
|
186
|
+
|
187
|
+
/// 2 points at back
|
188
|
+
double y1 = (t[(len - 2 + j)] - mean) / std;
|
189
|
+
d = min(dist(y1,q[len-1]), dist(y0, q[len-2]));
|
190
|
+
d = min(d, dist(y1,q[len-2]));
|
191
|
+
lb += d;
|
192
|
+
if (lb >= best_so_far)
|
193
|
+
return lb;
|
194
|
+
|
195
|
+
/// 3 points at front
|
196
|
+
double x2 = (t[(j + 2)] - mean) / std;
|
197
|
+
d = min(dist(x0,q[2]), dist(x1, q[2]));
|
198
|
+
d = min(d, dist(x2,q[2]));
|
199
|
+
d = min(d, dist(x2,q[1]));
|
200
|
+
d = min(d, dist(x2,q[0]));
|
201
|
+
lb += d;
|
202
|
+
if (lb >= best_so_far)
|
203
|
+
return lb;
|
204
|
+
|
205
|
+
/// 3 points at back
|
206
|
+
double y2 = (t[(len - 3 + j)] - mean) / std;
|
207
|
+
d = min(dist(y0,q[len-3]), dist(y1, q[len-3]));
|
208
|
+
d = min(d, dist(y2,q[len-3]));
|
209
|
+
d = min(d, dist(y2,q[len-2]));
|
210
|
+
d = min(d, dist(y2,q[len-1]));
|
211
|
+
lb += d;
|
212
|
+
|
213
|
+
return lb;
|
214
|
+
}
|
215
|
+
|
216
|
+
/// LB_Keogh 1: Create Envelope for the query
|
217
|
+
/// Note that because the query is known, envelope can be created once at the beginning.
|
218
|
+
///
|
219
|
+
/// Variable Explanation,
|
220
|
+
/// order : sorted indices for the query.
|
221
|
+
/// uo, lo: upper and lower envelops for the query, which already sorted.
|
222
|
+
/// t : a circular array keeping the current data.
|
223
|
+
/// j : index of the starting location in t
|
224
|
+
/// cb : (output) current bound at each position. It will be used later for early abandoning in DTW.
|
225
|
+
double lb_keogh_cumulative(int* order, double *t, double *uo, double *lo, double *cb, int j, int len, double mean, double std, double best_so_far) {
|
226
|
+
double lb = 0;
|
227
|
+
double x, d;
|
228
|
+
int i;
|
229
|
+
|
230
|
+
for (i = 0; i < len && lb < best_so_far; i++) {
|
231
|
+
x = (t[(order[i] + j)] - mean) / std;
|
232
|
+
d = 0;
|
233
|
+
if (x > uo[i]) {
|
234
|
+
d = dist(x, uo[i]);
|
235
|
+
} else if (x < lo[i]) {
|
236
|
+
d = dist(x, lo[i]);
|
237
|
+
}
|
238
|
+
lb += d;
|
239
|
+
cb[order[i]] = d;
|
240
|
+
}
|
241
|
+
return lb;
|
242
|
+
}
|
243
|
+
|
244
|
+
/// LB_Keogh 2: Create Envelop for the data
|
245
|
+
/// Note that the envelops have been created (in main function) when each data point has been read.
|
246
|
+
///
|
247
|
+
/// Variable Explanation,
|
248
|
+
/// tz: Z-normalized data
|
249
|
+
/// qo: sorted query
|
250
|
+
/// cb: (output) current bound at each position. Used later for early abandoning in DTW.
|
251
|
+
/// l,u: lower and upper envelope of the current data
|
252
|
+
double lb_keogh_data_cumulative(int* order, double *tz, double *qo, double *cb, double *l, double *u, int len, double mean, double std, double best_so_far) {
|
253
|
+
double lb = 0;
|
254
|
+
double uu, ll, d;
|
255
|
+
int i;
|
256
|
+
|
257
|
+
for (i = 0; i < len && lb < best_so_far; i++) {
|
258
|
+
uu = (u[order[i]] - mean) / std;
|
259
|
+
ll = (l[order[i]] - mean) / std;
|
260
|
+
d = 0;
|
261
|
+
if (qo[i] > uu) {
|
262
|
+
d = dist(qo[i], uu);
|
263
|
+
} else {
|
264
|
+
if (qo[i] < ll) {
|
265
|
+
d = dist(qo[i], ll);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
lb += d;
|
269
|
+
cb[order[i]] = d;
|
270
|
+
}
|
271
|
+
return lb;
|
272
|
+
}
|
273
|
+
|
274
|
+
/// Calculate Dynamic Time Wrapping distance
|
275
|
+
/// A,B: data and query, respectively
|
276
|
+
/// cb : cummulative bound used for early abandoning
|
277
|
+
/// r : size of Sakoe-Chiba warpping band
|
278
|
+
double dtw(double* A, double* B, double *cb, int m, int r, double best_so_far) {
|
279
|
+
double *cost;
|
280
|
+
double *cost_prev;
|
281
|
+
double *cost_tmp;
|
282
|
+
int i, j, k;
|
283
|
+
double x, y, z, min_cost;
|
284
|
+
|
285
|
+
/// Instead of using matrix of size O(m^2) or O(mr), we will reuse two arrays of size O(r).
|
286
|
+
cost = (double*) calloc(2 * r + 1, sizeof(double));
|
287
|
+
cost_prev = (double*) calloc(2 * r + 1, sizeof(double));
|
288
|
+
for (k = 0; k < 2 * r + 1; k++) {
|
289
|
+
cost[k] = INF;
|
290
|
+
cost_prev[k] = INF;
|
291
|
+
}
|
292
|
+
|
293
|
+
for (i = 0; i < m; i++) {
|
294
|
+
k = max(0, r - i);
|
295
|
+
min_cost = INF;
|
296
|
+
|
297
|
+
for (j = max(0, i - r); j <= min(m - 1, i + r); j++, k++) {
|
298
|
+
/// Initialize all row and column
|
299
|
+
if ((i == 0) && (j == 0)) {
|
300
|
+
cost[k] = dist(A[0], B[0]);
|
301
|
+
min_cost = cost[k];
|
302
|
+
continue;
|
303
|
+
}
|
304
|
+
|
305
|
+
if ((j - 1 < 0) || (k - 1 < 0)) {
|
306
|
+
y = INF;
|
307
|
+
} else {
|
308
|
+
y = cost[k - 1];
|
309
|
+
}
|
310
|
+
if ((i - 1 < 0) || (k + 1 > 2 * r)) {
|
311
|
+
x = INF;
|
312
|
+
} else {
|
313
|
+
x = cost_prev[k + 1];
|
314
|
+
}
|
315
|
+
if ((i - 1 < 0) || (j - 1 < 0)) {
|
316
|
+
z = INF;
|
317
|
+
} else {
|
318
|
+
z = cost_prev[k];
|
319
|
+
}
|
320
|
+
|
321
|
+
/// Classic DTW calculation
|
322
|
+
cost[k] = min( min( x, y) , z) + dist(A[i], B[j]);
|
323
|
+
|
324
|
+
/// Find minimum cost in row for early abandoning (possibly to use column instead of row).
|
325
|
+
if (cost[k] < min_cost) {
|
326
|
+
min_cost = cost[k];
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
/// We can abandon early if the current cummulative distance with lower bound together are larger than best_so_far
|
331
|
+
if (i + r < m - 1 && min_cost + cb[i + r + 1] >= best_so_far) {
|
332
|
+
free(cost);
|
333
|
+
free(cost_prev);
|
334
|
+
return min_cost + cb[i + r + 1];
|
335
|
+
}
|
336
|
+
|
337
|
+
/// Move current array to previous array.
|
338
|
+
cost_tmp = cost;
|
339
|
+
cost = cost_prev;
|
340
|
+
cost_prev = cost_tmp;
|
341
|
+
}
|
342
|
+
k--;
|
343
|
+
|
344
|
+
/// the DTW distance is in the last cell in the matrix of size O(m^2) or at the middle of our array.
|
345
|
+
double final_dtw = cost_prev[k];
|
346
|
+
free(cost);
|
347
|
+
free(cost_prev);
|
348
|
+
return final_dtw;
|
349
|
+
}
|
350
|
+
|
351
|
+
|
352
|
+
/// Calculate the nearest neighbor of a times series in a larger time series expressed as location and distance,
|
353
|
+
/// using the UCR suite optimizations.
|
354
|
+
int ucrdtw(double* data, long long data_size, double* query, long query_size, double warp_width, int verbose, long long* location, double* distance) {
|
355
|
+
long m = query_size;
|
356
|
+
int r = warp_width <= 1 ? floor(warp_width * m) : floor(warp_width);
|
357
|
+
|
358
|
+
double best_so_far; /// best-so-far
|
359
|
+
double *q, *t; /// data array
|
360
|
+
int *order; ///new order of the query
|
361
|
+
double *u, *l, *qo, *uo, *lo, *tz, *cb, *cb1, *cb2, *u_d, *l_d;
|
362
|
+
|
363
|
+
double d = 0.0;
|
364
|
+
long long i, j;
|
365
|
+
double ex, ex2, mean, std;
|
366
|
+
|
367
|
+
long long loc = 0;
|
368
|
+
double t1, t2;
|
369
|
+
int kim = 0, keogh = 0, keogh2 = 0;
|
370
|
+
double dist = 0, lb_kim = 0, lb_k = 0, lb_k2 = 0;
|
371
|
+
double *buffer, *u_buff, *l_buff;
|
372
|
+
index_t *q_tmp;
|
373
|
+
|
374
|
+
/// For every EPOCH points, all cumulative values, such as ex (sum), ex2 (sum square), will be restarted for reducing the floating point error.
|
375
|
+
int EPOCH = 100000;
|
376
|
+
|
377
|
+
if (verbose) {
|
378
|
+
t1 = clock();
|
379
|
+
}
|
380
|
+
|
381
|
+
/// calloc everything here
|
382
|
+
q = (double*) calloc(m, sizeof(double));
|
383
|
+
if (q == NULL) {
|
384
|
+
printf("ERROR: Memory can't be allocated!\n");
|
385
|
+
return -1;
|
386
|
+
}
|
387
|
+
memcpy((void*)q, (void*)query, m * sizeof(double));
|
388
|
+
|
389
|
+
qo = (double*) calloc(m, sizeof(double));
|
390
|
+
if (qo == NULL) {
|
391
|
+
printf("ERROR: Memory can't be allocated!\n");
|
392
|
+
return -1;
|
393
|
+
}
|
394
|
+
|
395
|
+
uo = (double*) calloc(m, sizeof(double));
|
396
|
+
if (uo == NULL) {
|
397
|
+
printf("ERROR: Memory can't be allocated!\n");
|
398
|
+
return -1;
|
399
|
+
}
|
400
|
+
|
401
|
+
lo = (double*) calloc(m, sizeof(double));
|
402
|
+
if (lo == NULL) {
|
403
|
+
printf("ERROR: Memory can't be allocated!\n");
|
404
|
+
return -1;
|
405
|
+
}
|
406
|
+
|
407
|
+
order = (int *) calloc(m, sizeof(int));
|
408
|
+
if (order == NULL) {
|
409
|
+
printf("ERROR: Memory can't be allocated!\n");
|
410
|
+
return -1;
|
411
|
+
}
|
412
|
+
|
413
|
+
q_tmp = (index_t *) calloc(m, sizeof(index_t));
|
414
|
+
if (q_tmp == NULL) {
|
415
|
+
printf("ERROR: Memory can't be allocated!\n");
|
416
|
+
return -1;
|
417
|
+
}
|
418
|
+
|
419
|
+
u = (double*) calloc(m, sizeof(double));
|
420
|
+
if (u == NULL) {
|
421
|
+
printf("ERROR: Memory can't be allocated!\n");
|
422
|
+
return -1;
|
423
|
+
}
|
424
|
+
|
425
|
+
l = (double*) calloc(m, sizeof(double));
|
426
|
+
if (l == NULL) {
|
427
|
+
printf("ERROR: Memory can't be allocated!\n");
|
428
|
+
return -1;
|
429
|
+
}
|
430
|
+
|
431
|
+
cb = (double*) calloc(m, sizeof(double));
|
432
|
+
if (cb == NULL) {
|
433
|
+
printf("ERROR: Memory can't be allocated!\n");
|
434
|
+
}
|
435
|
+
|
436
|
+
cb1 = (double*) calloc(m, sizeof(double));
|
437
|
+
if (cb1 == NULL) {
|
438
|
+
printf("ERROR: Memory can't be allocated!\n");
|
439
|
+
return -1;
|
440
|
+
}
|
441
|
+
|
442
|
+
cb2 = (double*) calloc(m, sizeof(double));
|
443
|
+
if (cb2 == NULL) {
|
444
|
+
printf("ERROR: Memory can't be allocated!\n");
|
445
|
+
return -1;
|
446
|
+
}
|
447
|
+
|
448
|
+
u_d = (double*) calloc(m, sizeof(double));
|
449
|
+
if (u == NULL) {
|
450
|
+
printf("ERROR: Memory can't be allocated!\n");
|
451
|
+
return -1;
|
452
|
+
}
|
453
|
+
|
454
|
+
l_d = (double*) calloc(m, sizeof(double));
|
455
|
+
if (l == NULL) {
|
456
|
+
printf("ERROR: Memory can't be allocated!\n");
|
457
|
+
return -1;
|
458
|
+
}
|
459
|
+
|
460
|
+
t = (double*) calloc(m, sizeof(double) * 2);
|
461
|
+
if (t == NULL) {
|
462
|
+
printf("ERROR: Memory can't be allocated!\n");
|
463
|
+
return -1;
|
464
|
+
}
|
465
|
+
|
466
|
+
tz = (double*) calloc(m, sizeof(double));
|
467
|
+
if (tz == NULL) {
|
468
|
+
printf("ERROR: Memory can't be allocated!\n");
|
469
|
+
return -1;
|
470
|
+
}
|
471
|
+
|
472
|
+
buffer = (double*) calloc(EPOCH, sizeof(double));
|
473
|
+
if (buffer == NULL) {
|
474
|
+
printf("ERROR: Memory can't be allocated!\n");
|
475
|
+
return -1;
|
476
|
+
}
|
477
|
+
|
478
|
+
u_buff = (double*) calloc(EPOCH, sizeof(double));
|
479
|
+
if (u_buff == NULL) {
|
480
|
+
printf("ERROR: Memory can't be allocated!\n");
|
481
|
+
return -1;
|
482
|
+
}
|
483
|
+
|
484
|
+
l_buff = (double*) calloc(EPOCH, sizeof(double));
|
485
|
+
if (l_buff == NULL) {
|
486
|
+
printf("ERROR: Memory can't be allocated!\n");
|
487
|
+
return -1;
|
488
|
+
}
|
489
|
+
|
490
|
+
/// Read query
|
491
|
+
best_so_far = INF;
|
492
|
+
ex = ex2 = 0;
|
493
|
+
for (i = 0; i < m; i++) {
|
494
|
+
d = q[i];
|
495
|
+
ex += d;
|
496
|
+
ex2 += d * d;
|
497
|
+
}
|
498
|
+
/// Do z-normalize the query, keep in same array, q
|
499
|
+
mean = ex / m;
|
500
|
+
std = ex2 / m;
|
501
|
+
std = sqrt(std - mean * mean);
|
502
|
+
for (i = 0; i < m; i++)
|
503
|
+
q[i] = (q[i] - mean) / std;
|
504
|
+
|
505
|
+
/// Create envelope of the query: lower envelope, l, and upper envelope, u
|
506
|
+
lower_upper_lemire(q, m, r, l, u);
|
507
|
+
|
508
|
+
/// Sort the query one time by abs(z-norm(q[i]))
|
509
|
+
for (i = 0; i < m; i++) {
|
510
|
+
q_tmp[i].value = q[i];
|
511
|
+
q_tmp[i].index = i;
|
512
|
+
}
|
513
|
+
qsort(q_tmp, m, sizeof(index_t), index_comp);
|
514
|
+
|
515
|
+
/// also create another arrays for keeping sorted envelope
|
516
|
+
for (i = 0; i < m; i++) {
|
517
|
+
int o = q_tmp[i].index;
|
518
|
+
order[i] = o;
|
519
|
+
qo[i] = q[o];
|
520
|
+
uo[i] = u[o];
|
521
|
+
lo[i] = l[o];
|
522
|
+
}
|
523
|
+
|
524
|
+
/// Initial the cummulative lower bound
|
525
|
+
for (i = 0; i < m; i++) {
|
526
|
+
cb[i] = 0;
|
527
|
+
cb1[i] = 0;
|
528
|
+
cb2[i] = 0;
|
529
|
+
}
|
530
|
+
|
531
|
+
i = 0; /// current index of the data in current chunk of size EPOCH
|
532
|
+
j = 0; /// the starting index of the data in the circular array, t
|
533
|
+
ex = ex2 = 0;
|
534
|
+
int done = 0;
|
535
|
+
int it = 0, ep = 0, k = 0;
|
536
|
+
long long I; /// the starting index of the data in current chunk of size EPOCH
|
537
|
+
long long data_index = 0;
|
538
|
+
while (!done) {
|
539
|
+
/// Read first m-1 points
|
540
|
+
ep = 0;
|
541
|
+
if (it == 0) {
|
542
|
+
for (k = 0; k < m - 1 && data_index < data_size; k++) {
|
543
|
+
buffer[k] = data[data_index++];
|
544
|
+
}
|
545
|
+
} else {
|
546
|
+
for (k = 0; k < m - 1; k++)
|
547
|
+
buffer[k] = buffer[EPOCH - m + 1 + k];
|
548
|
+
}
|
549
|
+
|
550
|
+
/// Read buffer of size EPOCH or when all data has been read.
|
551
|
+
ep = m - 1;
|
552
|
+
while (ep < EPOCH && data_index < data_size) {
|
553
|
+
buffer[ep] = data[data_index++];
|
554
|
+
ep++;
|
555
|
+
}
|
556
|
+
|
557
|
+
/// Data are read in chunk of size EPOCH.
|
558
|
+
/// When there is nothing to read, the loop is end.
|
559
|
+
if (ep <= m - 1) {
|
560
|
+
done = 1;
|
561
|
+
} else {
|
562
|
+
lower_upper_lemire(buffer, ep, r, l_buff, u_buff);
|
563
|
+
/// Do main task here..
|
564
|
+
ex = 0;
|
565
|
+
ex2 = 0;
|
566
|
+
for (i = 0; i < ep; i++) {
|
567
|
+
/// A bunch of data has been read and pick one of them at a time to use
|
568
|
+
d = buffer[i];
|
569
|
+
|
570
|
+
/// Calcualte sum and sum square
|
571
|
+
ex += d;
|
572
|
+
ex2 += d * d;
|
573
|
+
|
574
|
+
/// t is a circular array for keeping current data
|
575
|
+
t[i % m] = d;
|
576
|
+
|
577
|
+
/// Double the size for avoiding using modulo "%" operator
|
578
|
+
t[(i % m) + m] = d;
|
579
|
+
|
580
|
+
/// Start the task when there are more than m-1 points in the current chunk
|
581
|
+
if (i >= m - 1) {
|
582
|
+
mean = ex / m;
|
583
|
+
std = ex2 / m;
|
584
|
+
std = sqrt(std - mean * mean);
|
585
|
+
|
586
|
+
/// compute the start location of the data in the current circular array, t
|
587
|
+
j = (i + 1) % m;
|
588
|
+
/// the start location of the data in the current chunk
|
589
|
+
I = i - (m - 1);
|
590
|
+
|
591
|
+
/// Use a constant lower bound to prune the obvious subsequence
|
592
|
+
lb_kim = lb_kim_hierarchy(t, q, j, m, mean, std, best_so_far);
|
593
|
+
|
594
|
+
if (lb_kim < best_so_far) {
|
595
|
+
/// Use a linear time lower bound to prune; z_normalization of t will be computed on the fly.
|
596
|
+
/// uo, lo are envelope of the query.
|
597
|
+
lb_k = lb_keogh_cumulative(order, t, uo, lo, cb1, j, m, mean, std, best_so_far);
|
598
|
+
if (lb_k < best_so_far) {
|
599
|
+
/// Take another linear time to compute z_normalization of t.
|
600
|
+
/// Note that for better optimization, this can merge to the previous function.
|
601
|
+
for (k = 0; k < m; k++) {
|
602
|
+
tz[k] = (t[(k + j)] - mean) / std;
|
603
|
+
}
|
604
|
+
|
605
|
+
/// Use another lb_keogh to prune
|
606
|
+
/// qo is the sorted query. tz is unsorted z_normalized data.
|
607
|
+
/// l_buff, u_buff are big envelope for all data in this chunk
|
608
|
+
lb_k2 = lb_keogh_data_cumulative(order, tz, qo, cb2, l_buff + I, u_buff + I, m, mean, std, best_so_far);
|
609
|
+
if (lb_k2 < best_so_far) {
|
610
|
+
/// Choose better lower bound between lb_keogh and lb_keogh2 to be used in early abandoning DTW
|
611
|
+
/// Note that cb and cb2 will be cumulative summed here.
|
612
|
+
if (lb_k > lb_k2) {
|
613
|
+
cb[m - 1] = cb1[m - 1];
|
614
|
+
for (k = m - 2; k >= 0; k--)
|
615
|
+
cb[k] = cb[k + 1] + cb1[k];
|
616
|
+
} else {
|
617
|
+
cb[m - 1] = cb2[m - 1];
|
618
|
+
for (k = m - 2; k >= 0; k--)
|
619
|
+
cb[k] = cb[k + 1] + cb2[k];
|
620
|
+
}
|
621
|
+
|
622
|
+
/// Compute DTW and early abandoning if possible
|
623
|
+
dist = dtw(tz, q, cb, m, r, best_so_far);
|
624
|
+
|
625
|
+
if (dist < best_so_far) { /// Update best_so_far
|
626
|
+
/// loc is the real starting location of the nearest neighbor in the file
|
627
|
+
best_so_far = dist;
|
628
|
+
loc = (it) * (EPOCH - m + 1) + i - m + 1;
|
629
|
+
}
|
630
|
+
} else
|
631
|
+
keogh2++;
|
632
|
+
} else
|
633
|
+
keogh++;
|
634
|
+
} else
|
635
|
+
kim++;
|
636
|
+
|
637
|
+
/// Reduce absolute points from sum and sum square
|
638
|
+
ex -= t[j];
|
639
|
+
ex2 -= t[j] * t[j];
|
640
|
+
}
|
641
|
+
}
|
642
|
+
|
643
|
+
/// If the size of last chunk is less then EPOCH, then no more data and terminate.
|
644
|
+
if (ep < EPOCH)
|
645
|
+
done = 1;
|
646
|
+
else
|
647
|
+
it++;
|
648
|
+
}
|
649
|
+
}
|
650
|
+
|
651
|
+
i = (it) * (EPOCH - m + 1) + ep;
|
652
|
+
|
653
|
+
free(q);
|
654
|
+
free(qo);
|
655
|
+
free(uo);
|
656
|
+
free(lo);
|
657
|
+
free(order);
|
658
|
+
free(q_tmp);
|
659
|
+
free(u);
|
660
|
+
free(l);
|
661
|
+
free(cb);
|
662
|
+
free(cb1);
|
663
|
+
free(cb2);
|
664
|
+
free(u_d);
|
665
|
+
free(l_d);
|
666
|
+
free(t);
|
667
|
+
free(tz);
|
668
|
+
free(buffer);
|
669
|
+
free(u_buff);
|
670
|
+
free(l_buff);
|
671
|
+
|
672
|
+
if (verbose) {
|
673
|
+
t2 = clock();
|
674
|
+
printf("Location : %lld\n", loc);
|
675
|
+
printf("Distance : %.6f\n", sqrt(best_so_far));
|
676
|
+
printf("Data Scanned : %lld\n", i);
|
677
|
+
printf("Total Execution Time : %.4f secs\n", (t2 - t1) / CLOCKS_PER_SEC);
|
678
|
+
printf("\n");
|
679
|
+
printf("Pruned by LB_Kim : %6.2f%%\n", ((double) kim / i) * 100);
|
680
|
+
printf("Pruned by LB_Keogh : %6.2f%%\n", ((double) keogh / i) * 100);
|
681
|
+
printf("Pruned by LB_Keogh2 : %6.2f%%\n", ((double) keogh2 / i) * 100);
|
682
|
+
printf("DTW Calculation : %6.2f%%\n", 100 - (((double) kim + keogh + keogh2) / i * 100));
|
683
|
+
}
|
684
|
+
*location = loc;
|
685
|
+
*distance = sqrt(best_so_far);
|
686
|
+
return 0;
|
687
|
+
}
|
688
|
+
|
689
|
+
/// Calculate the nearest neighbor of a times series in a larger time series expressed as location and distance,
|
690
|
+
/// using the UCR suite optimizations. This function supports streaming the data and the query to search.
|
691
|
+
int ucrdtwf(FILE* data_file, FILE* query_file, long query_length, double warp_width, int verbose, long long* location, double* distance) {
|
692
|
+
FILE* fp = data_file;
|
693
|
+
FILE* qp = query_file;
|
694
|
+
long m = query_length;
|
695
|
+
int r = warp_width <= 1 ? floor(warp_width * m) : floor(warp_width);
|
696
|
+
|
697
|
+
double best_so_far; /// best-so-far
|
698
|
+
double *t, *q; /// data array and query array
|
699
|
+
int *order; ///new order of the query
|
700
|
+
double *u, *l, *qo, *uo, *lo, *tz, *cb, *cb1, *cb2, *u_d, *l_d;
|
701
|
+
|
702
|
+
double d;
|
703
|
+
long long i, j;
|
704
|
+
double ex, ex2, mean, std;
|
705
|
+
|
706
|
+
long long loc = 0;
|
707
|
+
double t1, t2;
|
708
|
+
int kim = 0, keogh = 0, keogh2 = 0;
|
709
|
+
double dist = 0, lb_kim = 0, lb_k = 0, lb_k2 = 0;
|
710
|
+
double *buffer, *u_buff, *l_buff;
|
711
|
+
index_t *q_tmp;
|
712
|
+
|
713
|
+
/// For every EPOCH points, all cumulative values, such as ex (sum), ex2 (sum square), will be restarted for reducing the floating point error.
|
714
|
+
int EPOCH = 100000;
|
715
|
+
|
716
|
+
if (verbose) {
|
717
|
+
t1 = clock();
|
718
|
+
}
|
719
|
+
|
720
|
+
/// calloc everything here
|
721
|
+
q = (double*) calloc(m, sizeof(double));
|
722
|
+
if (q == NULL) {
|
723
|
+
printf("ERROR: Memory can't be allocated!\n");
|
724
|
+
return -1;
|
725
|
+
}
|
726
|
+
|
727
|
+
qo = (double*) calloc(m, sizeof(double));
|
728
|
+
if (qo == NULL) {
|
729
|
+
printf("ERROR: Memory can't be allocated!\n");
|
730
|
+
return -1;
|
731
|
+
}
|
732
|
+
|
733
|
+
uo = (double*) calloc(m, sizeof(double));
|
734
|
+
if (uo == NULL) {
|
735
|
+
printf("ERROR: Memory can't be allocated!\n");
|
736
|
+
return -1;
|
737
|
+
}
|
738
|
+
|
739
|
+
lo = (double*) calloc(m, sizeof(double));
|
740
|
+
if (lo == NULL) {
|
741
|
+
printf("ERROR: Memory can't be allocated!\n");
|
742
|
+
return -1;
|
743
|
+
}
|
744
|
+
|
745
|
+
order = (int*) calloc(m, sizeof(int));
|
746
|
+
if (order == NULL) {
|
747
|
+
printf("ERROR: Memory can't be allocated!\n");
|
748
|
+
return -1;
|
749
|
+
}
|
750
|
+
|
751
|
+
q_tmp = (index_t*) calloc(m, sizeof(index_t));
|
752
|
+
if (q_tmp == NULL) {
|
753
|
+
printf("ERROR: Memory can't be allocated!\n");
|
754
|
+
return -1;
|
755
|
+
}
|
756
|
+
|
757
|
+
u = (double*) calloc(m, sizeof(double));
|
758
|
+
if (u == NULL) {
|
759
|
+
printf("ERROR: Memory can't be allocated!\n");
|
760
|
+
return -1;
|
761
|
+
}
|
762
|
+
|
763
|
+
l = (double*) calloc(m, sizeof(double));
|
764
|
+
if (l == NULL) {
|
765
|
+
printf("ERROR: Memory can't be allocated!\n");
|
766
|
+
return -1;
|
767
|
+
}
|
768
|
+
|
769
|
+
cb = (double*) calloc(m, sizeof(double));
|
770
|
+
if (cb == NULL) {
|
771
|
+
printf("ERROR: Memory can't be allocated!\n");
|
772
|
+
}
|
773
|
+
|
774
|
+
cb1 = (double*) calloc(m, sizeof(double));
|
775
|
+
if (cb1 == NULL) {
|
776
|
+
printf("ERROR: Memory can't be allocated!\n");
|
777
|
+
return -1;
|
778
|
+
}
|
779
|
+
|
780
|
+
cb2 = (double*) calloc(m, sizeof(double));
|
781
|
+
if (cb2 == NULL) {
|
782
|
+
printf("ERROR: Memory can't be allocated!\n");
|
783
|
+
return -1;
|
784
|
+
}
|
785
|
+
|
786
|
+
u_d = (double*) calloc(m, sizeof(double));
|
787
|
+
if (u == NULL) {
|
788
|
+
printf("ERROR: Memory can't be allocated!\n");
|
789
|
+
return -1;
|
790
|
+
}
|
791
|
+
|
792
|
+
l_d = (double*) calloc(m, sizeof(double));
|
793
|
+
if (l == NULL) {
|
794
|
+
printf("ERROR: Memory can't be allocated!\n");
|
795
|
+
return -1;
|
796
|
+
}
|
797
|
+
|
798
|
+
t = (double*) calloc(m, sizeof(double) * 2);
|
799
|
+
if (t == NULL) {
|
800
|
+
printf("ERROR: Memory can't be allocated!\n");
|
801
|
+
return -1;
|
802
|
+
}
|
803
|
+
|
804
|
+
tz = (double*) calloc(m, sizeof(double));
|
805
|
+
if (tz == NULL) {
|
806
|
+
printf("ERROR: Memory can't be allocated!\n");
|
807
|
+
return -1;
|
808
|
+
}
|
809
|
+
|
810
|
+
buffer = (double*) calloc(EPOCH, sizeof(double));
|
811
|
+
if (buffer == NULL) {
|
812
|
+
printf("ERROR: Memory can't be allocated!\n");
|
813
|
+
return -1;
|
814
|
+
}
|
815
|
+
|
816
|
+
u_buff = (double*) calloc(EPOCH, sizeof(double));
|
817
|
+
if (u_buff == NULL) {
|
818
|
+
printf("ERROR: Memory can't be allocated!\n");
|
819
|
+
return -1;
|
820
|
+
}
|
821
|
+
|
822
|
+
l_buff = (double*) calloc(EPOCH, sizeof(double));
|
823
|
+
if (l_buff == NULL) {
|
824
|
+
printf("ERROR: Memory can't be allocated!\n");
|
825
|
+
return -1;
|
826
|
+
}
|
827
|
+
|
828
|
+
/// Read query file
|
829
|
+
best_so_far = INF;
|
830
|
+
i = 0;
|
831
|
+
j = 0;
|
832
|
+
ex = ex2 = 0;
|
833
|
+
|
834
|
+
while (fscanf(qp, "%lf", &d) != EOF && i < m) {
|
835
|
+
ex += d;
|
836
|
+
ex2 += d * d;
|
837
|
+
q[i] = d;
|
838
|
+
i++;
|
839
|
+
}
|
840
|
+
|
841
|
+
/// Do z-normalize the query, keep in same array, q
|
842
|
+
mean = ex / m;
|
843
|
+
std = ex2 / m;
|
844
|
+
std = sqrt(std - mean * mean);
|
845
|
+
for (i = 0; i < m; i++)
|
846
|
+
q[i] = (q[i] - mean) / std;
|
847
|
+
|
848
|
+
/// Create envelope of the query: lower envelope, l, and upper envelope, u
|
849
|
+
lower_upper_lemire(q, m, r, l, u);
|
850
|
+
|
851
|
+
/// Sort the query one time by abs(z-norm(q[i]))
|
852
|
+
for (i = 0; i < m; i++) {
|
853
|
+
q_tmp[i].value = q[i];
|
854
|
+
q_tmp[i].index = i;
|
855
|
+
}
|
856
|
+
qsort(q_tmp, m, sizeof(index_t), index_comp);
|
857
|
+
|
858
|
+
/// also create another arrays for keeping sorted envelope
|
859
|
+
for (i = 0; i < m; i++) {
|
860
|
+
int o = q_tmp[i].index;
|
861
|
+
order[i] = o;
|
862
|
+
qo[i] = q[o];
|
863
|
+
uo[i] = u[o];
|
864
|
+
lo[i] = l[o];
|
865
|
+
}
|
866
|
+
|
867
|
+
/// Initial the cummulative lower bound
|
868
|
+
for (i = 0; i < m; i++) {
|
869
|
+
cb[i] = 0;
|
870
|
+
cb1[i] = 0;
|
871
|
+
cb2[i] = 0;
|
872
|
+
}
|
873
|
+
|
874
|
+
i = 0; /// current index of the data in current chunk of size EPOCH
|
875
|
+
j = 0; /// the starting index of the data in the circular array, t
|
876
|
+
ex = ex2 = 0;
|
877
|
+
int done = 0;
|
878
|
+
int it = 0, ep = 0, k = 0;
|
879
|
+
long long I; /// the starting index of the data in current chunk of size EPOCH
|
880
|
+
|
881
|
+
while (!done) {
|
882
|
+
/// Read first m-1 points
|
883
|
+
ep = 0;
|
884
|
+
if (it == 0) {
|
885
|
+
for (k = 0; k < m - 1; k++)
|
886
|
+
if (fscanf(fp, "%lf", &d) != EOF)
|
887
|
+
buffer[k] = d;
|
888
|
+
} else {
|
889
|
+
for (k = 0; k < m - 1; k++)
|
890
|
+
buffer[k] = buffer[EPOCH - m + 1 + k];
|
891
|
+
}
|
892
|
+
|
893
|
+
/// Read buffer of size EPOCH or when all data has been read.
|
894
|
+
ep = m - 1;
|
895
|
+
while (ep < EPOCH) {
|
896
|
+
if (fscanf(fp, "%lf", &d) == EOF)
|
897
|
+
break;
|
898
|
+
buffer[ep] = d;
|
899
|
+
ep++;
|
900
|
+
}
|
901
|
+
|
902
|
+
/// Data are read in chunk of size EPOCH.
|
903
|
+
/// When there is nothing to read, the loop is end.
|
904
|
+
if (ep <= m - 1) {
|
905
|
+
done = 1;
|
906
|
+
} else {
|
907
|
+
lower_upper_lemire(buffer, ep, r, l_buff, u_buff);
|
908
|
+
/// Do main task here..
|
909
|
+
ex = 0;
|
910
|
+
ex2 = 0;
|
911
|
+
for (i = 0; i < ep; i++) {
|
912
|
+
/// A bunch of data has been read and pick one of them at a time to use
|
913
|
+
d = buffer[i];
|
914
|
+
|
915
|
+
/// Calcualte sum and sum square
|
916
|
+
ex += d;
|
917
|
+
ex2 += d * d;
|
918
|
+
|
919
|
+
/// t is a circular array for keeping current data
|
920
|
+
t[i % m] = d;
|
921
|
+
|
922
|
+
/// Double the size for avoiding using modulo "%" operator
|
923
|
+
t[(i % m) + m] = d;
|
924
|
+
|
925
|
+
/// Start the task when there are more than m-1 points in the current chunk
|
926
|
+
if (i >= m - 1) {
|
927
|
+
mean = ex / m;
|
928
|
+
std = ex2 / m;
|
929
|
+
std = sqrt(std - mean * mean);
|
930
|
+
|
931
|
+
/// compute the start location of the data in the current circular array, t
|
932
|
+
j = (i + 1) % m;
|
933
|
+
/// the start location of the data in the current chunk
|
934
|
+
I = i - (m - 1);
|
935
|
+
|
936
|
+
/// Use a constant lower bound to prune the obvious subsequence
|
937
|
+
lb_kim = lb_kim_hierarchy(t, q, j, m, mean, std, best_so_far);
|
938
|
+
|
939
|
+
if (lb_kim < best_so_far) {
|
940
|
+
/// Use a linear time lower bound to prune; z_normalization of t will be computed on the fly.
|
941
|
+
/// uo, lo are envelope of the query.
|
942
|
+
lb_k = lb_keogh_cumulative(order, t, uo, lo, cb1, j, m, mean, std, best_so_far);
|
943
|
+
if (lb_k < best_so_far) {
|
944
|
+
/// Take another linear time to compute z_normalization of t.
|
945
|
+
/// Note that for better optimization, this can merge to the previous function.
|
946
|
+
for (k = 0; k < m; k++) {
|
947
|
+
tz[k] = (t[(k + j)] - mean) / std;
|
948
|
+
}
|
949
|
+
|
950
|
+
/// Use another lb_keogh to prune
|
951
|
+
/// qo is the sorted query. tz is unsorted z_normalized data.
|
952
|
+
/// l_buff, u_buff are big envelope for all data in this chunk
|
953
|
+
lb_k2 = lb_keogh_data_cumulative(order, tz, qo, cb2, l_buff + I, u_buff + I, m, mean, std, best_so_far);
|
954
|
+
if (lb_k2 < best_so_far) {
|
955
|
+
/// Choose better lower bound between lb_keogh and lb_keogh2 to be used in early abandoning DTW
|
956
|
+
/// Note that cb and cb2 will be cumulative summed here.
|
957
|
+
if (lb_k > lb_k2) {
|
958
|
+
cb[m - 1] = cb1[m - 1];
|
959
|
+
for (k = m - 2; k >= 0; k--)
|
960
|
+
cb[k] = cb[k + 1] + cb1[k];
|
961
|
+
} else {
|
962
|
+
cb[m - 1] = cb2[m - 1];
|
963
|
+
for (k = m - 2; k >= 0; k--)
|
964
|
+
cb[k] = cb[k + 1] + cb2[k];
|
965
|
+
}
|
966
|
+
|
967
|
+
/// Compute DTW and early abandoning if possible
|
968
|
+
dist = dtw(tz, q, cb, m, r, best_so_far);
|
969
|
+
|
970
|
+
if (dist < best_so_far) { /// Update best_so_far
|
971
|
+
/// loc is the real starting location of the nearest neighbor in the file
|
972
|
+
best_so_far = dist;
|
973
|
+
loc = (it) * (EPOCH - m + 1) + i - m + 1;
|
974
|
+
}
|
975
|
+
} else
|
976
|
+
keogh2++;
|
977
|
+
} else
|
978
|
+
keogh++;
|
979
|
+
} else
|
980
|
+
kim++;
|
981
|
+
|
982
|
+
/// Reduce absolute points from sum and sum square
|
983
|
+
ex -= t[j];
|
984
|
+
ex2 -= t[j] * t[j];
|
985
|
+
}
|
986
|
+
}
|
987
|
+
|
988
|
+
/// If the size of last chunk is less then EPOCH, then no more data and terminate.
|
989
|
+
if (ep < EPOCH)
|
990
|
+
done = 1;
|
991
|
+
else
|
992
|
+
it++;
|
993
|
+
}
|
994
|
+
}
|
995
|
+
|
996
|
+
i = (it) * (EPOCH - m + 1) + ep;
|
997
|
+
|
998
|
+
free(q);
|
999
|
+
free(qo);
|
1000
|
+
free(uo);
|
1001
|
+
free(lo);
|
1002
|
+
free(order);
|
1003
|
+
free(q_tmp);
|
1004
|
+
free(u);
|
1005
|
+
free(l);
|
1006
|
+
free(cb);
|
1007
|
+
free(cb1);
|
1008
|
+
free(cb2);
|
1009
|
+
free(u_d);
|
1010
|
+
free(l_d);
|
1011
|
+
free(t);
|
1012
|
+
free(tz);
|
1013
|
+
free(buffer);
|
1014
|
+
free(u_buff);
|
1015
|
+
free(l_buff);
|
1016
|
+
|
1017
|
+
if (verbose) {
|
1018
|
+
t2 = clock();
|
1019
|
+
printf("Data Scanned : %lld\n", i);
|
1020
|
+
printf("Total Execution Time : %.4f secs\n", (t2 - t1) / CLOCKS_PER_SEC);
|
1021
|
+
printf("\n");
|
1022
|
+
printf("Pruned by LB_Kim : %6.2f%%\n", ((double) kim / i) * 100);
|
1023
|
+
printf("Pruned by LB_Keogh : %6.2f%%\n", ((double) keogh / i) * 100);
|
1024
|
+
printf("Pruned by LB_Keogh2 : %6.2f%%\n", ((double) keogh2 / i) * 100);
|
1025
|
+
printf("DTW Calculation : %6.2f%%\n", 100 - (((double) kim + keogh + keogh2) / i * 100));
|
1026
|
+
}
|
1027
|
+
*location = loc;
|
1028
|
+
*distance = sqrt(best_so_far);
|
1029
|
+
return 0;
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
int main(int argc, char** argv) {
|
1033
|
+
if (argc < 5) {
|
1034
|
+
printf("Error: Invalid arguments\n");
|
1035
|
+
printf("Command usage: dtwfind <data-file> <query-file> <query-length> <warp-width>\n\n");
|
1036
|
+
printf("For example: dtwfind data.txt query.txt 128 0.05\n");
|
1037
|
+
return -2;
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
FILE* data_file = fopen(argv[1], "r");
|
1041
|
+
if (data_file == NULL) {
|
1042
|
+
printf("ERROR: File not found: %s\n", argv[1]);
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
FILE* query_file = fopen(argv[2], "r");
|
1046
|
+
if (query_file == NULL) {
|
1047
|
+
printf("ERROR: File not found: %s\n", argv[2]);
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
long long location = -1;
|
1051
|
+
double distance = -1;
|
1052
|
+
int ret = ucrdtwf(data_file, query_file, atoll(argv[3]), atof(argv[4]), 1, &location, &distance);
|
1053
|
+
printf("Location: %lld\nDistance: %.6f\n", location, distance);
|
1054
|
+
return ret;
|
1055
|
+
}
|
data/ext/ucrdtw.h
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
|
2
|
+
int ucrdtw(double* data, long long data_size, double* query, long query_size, double warp_width, int verbose, long long* location, double* distance);
|
3
|
+
|
4
|
+
int ucrdtwf(FILE* data_stream, FILE* query_stream, long query_length, double warp_width, int verbose, long long* location, double* distance);
|
data/lib/ucrdtw.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "ucrdtw/version"
|
2
|
+
require 'ffi'
|
3
|
+
require 'ffi-compiler/loader'
|
4
|
+
|
5
|
+
module Ucrdtw
|
6
|
+
extend FFI::Library
|
7
|
+
ffi_lib FFI::Compiler::Loader.find('ucrdtw')
|
8
|
+
|
9
|
+
attach_function :ucrdtw, [:pointer, :long_long, :pointer, :long, :double, :int, :pointer, :pointer], :int
|
10
|
+
|
11
|
+
class <<self
|
12
|
+
def verbose=(verbose)
|
13
|
+
@verbose = verbose
|
14
|
+
end
|
15
|
+
|
16
|
+
def verbose
|
17
|
+
@verbose ||= 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.dtw(data, query, warp_width = 0.05)
|
22
|
+
dataBuf = FFI::MemoryPointer.new(:double, data.size)
|
23
|
+
dataBuf.write_array_of_double(data)
|
24
|
+
|
25
|
+
queryBuf = FFI::MemoryPointer.new(:double, query.size)
|
26
|
+
queryBuf.write_array_of_double(query)
|
27
|
+
|
28
|
+
location = FFI::MemoryPointer.new(:long_long, 1)
|
29
|
+
distance = FFI::MemoryPointer.new(:double, 1)
|
30
|
+
|
31
|
+
ucrdtw(dataBuf, data.length, queryBuf, query.length, warp_width, verbose, location, distance)
|
32
|
+
|
33
|
+
[location.read_long_long, distance.read_double]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/ucrdtw.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ucrdtw/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ucrdtw"
|
8
|
+
spec.version = Ucrdtw::VERSION
|
9
|
+
spec.authors = ["Chris Beer"]
|
10
|
+
spec.email = ["chris@cbeer.info"]
|
11
|
+
|
12
|
+
spec.summary = %q{UCR DTW FFI wrapper}
|
13
|
+
spec.homepage = "https://github.com/cbeer/ucrdtw"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.extensions << 'ext/Rakefile'
|
21
|
+
spec.add_dependency 'ffi-compiler'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ucrdtw
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Beer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- chris@cbeer.info
|
58
|
+
executables: []
|
59
|
+
extensions:
|
60
|
+
- ext/Rakefile
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/console
|
70
|
+
- bin/setup
|
71
|
+
- ext/Rakefile
|
72
|
+
- ext/ucrdtw.c
|
73
|
+
- ext/ucrdtw.h
|
74
|
+
- lib/ucrdtw.rb
|
75
|
+
- lib/ucrdtw/version.rb
|
76
|
+
- ucrdtw.gemspec
|
77
|
+
homepage: https://github.com/cbeer/ucrdtw
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.5
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: UCR DTW FFI wrapper
|
100
|
+
test_files: []
|