comparison query.cpp @ 239:2cc06e5b05a5

Merge refactoring branch. Bug fixes: * 64-bit powertable bug; * -inf - -inf bug; * use new times information; * plus short track, O2_MAXFILES and structure padding ABI fixes (already backported) Major code changes: * split source into functional units, known as 'files'; * Reporter class for accumulating and reporting on query results; * much OAOOization, mostly from above: net 800 LOC (25%) shorter.
author mas01cr
date Thu, 13 Dec 2007 14:23:32 +0000
parents
children 4eb4608e28e1
comparison
equal deleted inserted replaced
224:3a81da6fb1d7 239:2cc06e5b05a5
1 #include "audioDB.h"
2
3 #include "reporter.h"
4
5 bool audioDB::powers_acceptable(double p1, double p2) {
6 if (use_absolute_threshold) {
7 if ((p1 < absolute_threshold) || (p2 < absolute_threshold)) {
8 return false;
9 }
10 }
11 if (use_relative_threshold) {
12 if (fabs(p1-p2) > fabs(relative_threshold)) {
13 return false;
14 }
15 }
16 return true;
17 }
18
19 void audioDB::query(const char* dbName, const char* inFile, adb__queryResponse *adbQueryResponse) {
20 initTables(dbName, inFile);
21 Reporter *r = 0;
22 switch (queryType) {
23 case O2_POINT_QUERY:
24 sequenceLength = 1;
25 normalizedDistance = false;
26 r = new pointQueryReporter<std::greater < NNresult > >(pointNN);
27 break;
28 case O2_TRACK_QUERY:
29 sequenceLength = 1;
30 normalizedDistance = false;
31 r = new trackAveragingReporter<std::greater < NNresult > >(pointNN, trackNN, dbH->numFiles);
32 break;
33 case O2_SEQUENCE_QUERY:
34 if(radius == 0) {
35 r = new trackAveragingReporter<std::less < NNresult > >(pointNN, trackNN, dbH->numFiles);
36 } else {
37 r = new trackSequenceQueryRadReporter(trackNN, dbH->numFiles);
38 }
39 break;
40 default:
41 error("unrecognized queryType in query()");
42 }
43 trackSequenceQueryNN(dbName, inFile, r);
44 r->report(fileTable, adbQueryResponse);
45 delete r;
46 }
47
48 // return ordinal position of key in keyTable
49 unsigned audioDB::getKeyPos(char* key){
50 for(unsigned k=0; k<dbH->numFiles; k++)
51 if(strncmp(fileTable + k*O2_FILETABLESIZE, key, strlen(key))==0)
52 return k;
53 error("Key not found",key);
54 return O2_ERR_KEYNOTFOUND;
55 }
56
57 // This is a common pattern in sequence queries: what we are doing is
58 // taking a window of length seqlen over a buffer of length length,
59 // and placing the sum of the elements in that window in the first
60 // element of the window: thus replacing all but the last seqlen
61 // elements in the buffer with the corresponding windowed sum.
62 void audioDB::sequence_sum(double *buffer, int length, int seqlen) {
63 double tmp1, tmp2, *ps;
64 int j, w;
65
66 tmp1 = *buffer;
67 j = 1;
68 w = seqlen - 1;
69 while(w--) {
70 *buffer += buffer[j++];
71 }
72 ps = buffer + 1;
73 w = length - seqlen; // +1 - 1
74 while(w--) {
75 tmp2 = *ps;
76 if(isfinite(tmp1)) {
77 *ps = *(ps - 1) - tmp1 + *(ps + seqlen - 1);
78 } else {
79 for(int i = 1; i < seqlen; i++) {
80 *ps += *(ps + i);
81 }
82 }
83 tmp1 = tmp2;
84 ps++;
85 }
86 }
87
88 // In contrast to sequence_sum() above, sequence_sqrt() and
89 // sequence_average() below are simple mappers across the sequence.
90 void audioDB::sequence_sqrt(double *buffer, int length, int seqlen) {
91 int w = length - seqlen + 1;
92 while(w--) {
93 *buffer = sqrt(*buffer);
94 buffer++;
95 }
96 }
97
98 void audioDB::sequence_average(double *buffer, int length, int seqlen) {
99 int w = length - seqlen + 1;
100 while(w--) {
101 *buffer /= seqlen;
102 buffer++;
103 }
104 }
105
106 void audioDB::initialize_arrays(int track, unsigned int numVectors, double *query, double *data_buffer, double **D, double **DD) {
107 unsigned int j, k, l, w;
108 double *dp, *qp, *sp;
109
110 const unsigned HOP_SIZE = sequenceHop;
111 const unsigned wL = sequenceLength;
112
113 for(j = 0; j < numVectors; j++) {
114 // Sum products matrix
115 D[j] = new double[trackTable[track]];
116 assert(D[j]);
117 // Matched filter matrix
118 DD[j]=new double[trackTable[track]];
119 assert(DD[j]);
120 }
121
122 // Dot product
123 for(j = 0; j < numVectors; j++)
124 for(k = 0; k < trackTable[track]; k++){
125 qp = query + j * dbH->dim;
126 sp = data_buffer + k * dbH->dim;
127 DD[j][k] = 0.0; // Initialize matched filter array
128 dp = &D[j][k]; // point to correlation cell j,k
129 *dp = 0.0; // initialize correlation cell
130 l = dbH->dim; // size of vectors
131 while(l--)
132 *dp += *qp++ * *sp++;
133 }
134
135 // Matched Filter
136 // HOP SIZE == 1
137 double* spd;
138 if(HOP_SIZE == 1) { // HOP_SIZE = shingleHop
139 for(w = 0; w < wL; w++) {
140 for(j = 0; j < numVectors - w; j++) {
141 sp = DD[j];
142 spd = D[j+w] + w;
143 k = trackTable[track] - w;
144 while(k--)
145 *sp++ += *spd++;
146 }
147 }
148 } else { // HOP_SIZE != 1
149 for(w = 0; w < wL; w++) {
150 for(j = 0; j < numVectors - w; j += HOP_SIZE) {
151 sp = DD[j];
152 spd = D[j+w]+w;
153 for(k = 0; k < trackTable[track] - w; k += HOP_SIZE) {
154 *sp += *spd;
155 sp += HOP_SIZE;
156 spd += HOP_SIZE;
157 }
158 }
159 }
160 }
161 }
162
163 void audioDB::delete_arrays(int track, unsigned int numVectors, double **D, double **DD) {
164 if(D != NULL) {
165 for(unsigned int j = 0; j < numVectors; j++) {
166 delete[] D[j];
167 }
168 }
169 if(DD != NULL) {
170 for(unsigned int j = 0; j < numVectors; j++) {
171 delete[] DD[j];
172 }
173 }
174 }
175
176 void audioDB::read_data(int track, double **data_buffer_p, size_t *data_buffer_size_p) {
177 if (trackTable[track] * sizeof(double) * dbH->dim > *data_buffer_size_p) {
178 if(*data_buffer_p) {
179 free(*data_buffer_p);
180 }
181 {
182 *data_buffer_size_p = trackTable[track] * sizeof(double) * dbH->dim;
183 void *tmp = malloc(*data_buffer_size_p);
184 if (tmp == NULL) {
185 error("error allocating data buffer");
186 }
187 *data_buffer_p = (double *) tmp;
188 }
189 }
190
191 read(dbfid, *data_buffer_p, trackTable[track] * sizeof(double) * dbH->dim);
192 }
193
194 // These names deserve some unpicking. The names starting with a "q"
195 // are pointers to the query, norm and power vectors; the names
196 // starting with "v" are things that will end up pointing to the
197 // actual query point's information. -- CSR, 2007-12-05
198 void audioDB::set_up_query(double **qp, double **vqp, double **qnp, double **vqnp, double **qpp, double **vqpp, double *mqdp, unsigned *nvp) {
199 *nvp = (statbuf.st_size - sizeof(int)) / (dbH->dim * sizeof(double));
200
201 if(!(dbH->flags & O2_FLAG_L2NORM)) {
202 error("Database must be L2 normed for sequence query","use -L2NORM");
203 }
204
205 if(*nvp < sequenceLength) {
206 error("Query shorter than requested sequence length", "maybe use -l");
207 }
208
209 VERB_LOG(1, "performing norms... ");
210
211 *qp = new double[*nvp * dbH->dim];
212 memcpy(*qp, indata+sizeof(int), *nvp * dbH->dim * sizeof(double));
213 *qnp = new double[*nvp];
214 unitNorm(*qp, dbH->dim, *nvp, *qnp);
215
216 sequence_sum(*qnp, *nvp, sequenceLength);
217 sequence_sqrt(*qnp, *nvp, sequenceLength);
218
219 if (usingPower) {
220 *qpp = new double[*nvp];
221 if (lseek(powerfd, sizeof(int), SEEK_SET) == (off_t) -1) {
222 error("error seeking to data", powerFileName, "lseek");
223 }
224 int count = read(powerfd, *qpp, *nvp * sizeof(double));
225 if (count == -1) {
226 error("error reading data", powerFileName, "read");
227 }
228 if ((unsigned) count != *nvp * sizeof(double)) {
229 error("short read", powerFileName);
230 }
231
232 sequence_sum(*qpp, *nvp, sequenceLength);
233 sequence_average(*qpp, *nvp, sequenceLength);
234 }
235
236 if (usingTimes) {
237 unsigned int k;
238 *mqdp = 0.0;
239 double *querydurs = new double[*nvp];
240 double *timesdata = new double[*nvp*2];
241 insertTimeStamps(*nvp, timesFile, timesdata);
242 for(k = 0; k < *nvp; k++) {
243 querydurs[k] = timesdata[2*k+1] - timesdata[2*k];
244 *mqdp += querydurs[k];
245 }
246 *mqdp /= k;
247
248 VERB_LOG(1, "mean query file duration: %f\n", *mqdp);
249
250 delete [] querydurs;
251 delete [] timesdata;
252 }
253
254 // Defaults, for exhaustive search (!usingQueryPoint)
255 *vqp = *qp;
256 *vqnp = *qnp;
257 *vqpp = *qpp;
258
259 if(usingQueryPoint) {
260 if(queryPoint > *nvp || queryPoint > *nvp - sequenceLength + 1) {
261 error("queryPoint > numVectors-wL+1 in query");
262 } else {
263 VERB_LOG(1, "query point: %u\n", queryPoint);
264 *vqp = *qp + queryPoint * dbH->dim;
265 *vqnp = *qnp + queryPoint;
266 if (usingPower) {
267 *vqpp = *qpp + queryPoint;
268 }
269 *nvp = sequenceLength;
270 }
271 }
272 }
273
274 // FIXME: this is not the right name; we're not actually setting up
275 // the database, but copying various bits of it out of mmap()ed tables
276 // in order to reduce seeks.
277 void audioDB::set_up_db(double **snp, double **vsnp, double **spp, double **vspp, double **mddp, unsigned int *dvp) {
278 *dvp = dbH->length / (dbH->dim * sizeof(double));
279 *snp = new double[*dvp];
280
281 double *snpp = *snp, *sppp = 0;
282 memcpy(*snp, l2normTable, *dvp * sizeof(double));
283
284 if (usingPower) {
285 if (!(dbH->flags & O2_FLAG_POWER)) {
286 error("database not power-enabled", dbName);
287 }
288 *spp = new double[*dvp];
289 sppp = *spp;
290 memcpy(*spp, powerTable, *dvp * sizeof(double));
291 }
292
293 for(unsigned int i = 0; i < dbH->numFiles; i++){
294 if(trackTable[i] >= sequenceLength) {
295 sequence_sum(snpp, trackTable[i], sequenceLength);
296 sequence_sqrt(snpp, trackTable[i], sequenceLength);
297
298 if (usingPower) {
299 sequence_sum(sppp, trackTable[i], sequenceLength);
300 sequence_average(sppp, trackTable[i], sequenceLength);
301 }
302 }
303 snpp += trackTable[i];
304 if (usingPower) {
305 sppp += trackTable[i];
306 }
307 }
308
309 if (usingTimes) {
310 if(!(dbH->flags & O2_FLAG_TIMES)) {
311 error("query timestamps provided for non-timed database", dbName);
312 }
313
314 *mddp = new double[dbH->numFiles];
315
316 for(unsigned int k = 0; k < dbH->numFiles; k++) {
317 unsigned int j;
318 (*mddp)[k] = 0.0;
319 for(j = 0; j < trackTable[k]; j++) {
320 (*mddp)[k] += timesTable[2*j+1] - timesTable[2*j];
321 }
322 (*mddp)[k] /= j;
323 }
324 }
325
326 *vsnp = *snp;
327 *vspp = *spp;
328 }
329
330 void audioDB::trackSequenceQueryNN(const char* dbName, const char* inFile, Reporter *reporter) {
331
332 unsigned int numVectors;
333 double *query, *query_data;
334 double *qNorm, *qnPtr, *qPower = 0, *qpPtr = 0;
335 double meanQdur;
336
337 set_up_query(&query_data, &query, &qNorm, &qnPtr, &qPower, &qpPtr, &meanQdur, &numVectors);
338
339 unsigned int dbVectors;
340 double *sNorm, *snPtr, *sPower = 0, *spPtr = 0;
341 double *meanDBdur = 0;
342
343 set_up_db(&sNorm, &snPtr, &sPower, &spPtr, &meanDBdur, &dbVectors);
344
345 VERB_LOG(1, "matching tracks...");
346
347 assert(pointNN>0 && pointNN<=O2_MAXNN);
348 assert(trackNN>0 && trackNN<=O2_MAXNN);
349
350 unsigned j,k,track,trackOffset=0, HOP_SIZE=sequenceHop, wL=sequenceLength;
351 double **D = 0; // Differences query and target
352 double **DD = 0; // Matched filter distance
353
354 D = new double*[numVectors];
355 DD = new double*[numVectors];
356
357 gettimeofday(&tv1, NULL);
358 unsigned processedTracks = 0;
359
360 // build track offset table
361 off_t *trackOffsetTable = new off_t[dbH->numFiles];
362 unsigned cumTrack=0;
363 off_t trackIndexOffset;
364 for(k = 0; k < dbH->numFiles; k++){
365 trackOffsetTable[k] = cumTrack;
366 cumTrack += trackTable[k] * dbH->dim;
367 }
368
369 char nextKey[MAXSTR];
370
371 // Track loop
372 size_t data_buffer_size = 0;
373 double *data_buffer = 0;
374 lseek(dbfid, dbH->dataOffset, SEEK_SET);
375
376 for(processedTracks=0, track=0 ; processedTracks < dbH->numFiles ; track++, processedTracks++) {
377
378 trackOffset = trackOffsetTable[track]; // numDoubles offset
379
380 // get trackID from file if using a control file
381 if(trackFile) {
382 trackFile->getline(nextKey,MAXSTR);
383 if(!trackFile->eof()) {
384 track = getKeyPos(nextKey);
385 trackOffset = trackOffsetTable[track];
386 lseek(dbfid, dbH->dataOffset + trackOffset * sizeof(double), SEEK_SET);
387 } else {
388 break;
389 }
390 }
391
392 trackIndexOffset=trackOffset/dbH->dim; // numVectors offset
393
394 read_data(track, &data_buffer, &data_buffer_size);
395 if(sequenceLength <= trackTable[track]) { // test for short sequences
396
397 VERB_LOG(7,"%u.%jd.%u | ", track, (intmax_t) trackIndexOffset, trackTable[track]);
398
399 initialize_arrays(track, numVectors, query, data_buffer, D, DD);
400
401 if(usingTimes) {
402 VERB_LOG(3,"meanQdur=%f meanDBdur=%f\n", meanQdur, meanDBdur[track]);
403 }
404
405 if((!usingTimes) || fabs(meanDBdur[track]-meanQdur) < meanQdur*timesTol) {
406 if(usingTimes) {
407 VERB_LOG(3,"within duration tolerance.\n");
408 }
409
410 // Search for minimum distance by shingles (concatenated vectors)
411 for(j = 0; j <= numVectors - wL; j += HOP_SIZE) {
412 for(k = 0; k <= trackTable[track] - wL; k += HOP_SIZE) {
413 double thisDist;
414 if(normalizedDistance) {
415 thisDist = 2-(2/(qnPtr[j]*sNorm[trackIndexOffset+k]))*DD[j][k];
416 } else {
417 thisDist = DD[j][k];
418 }
419 // Power test
420 if ((!usingPower) || powers_acceptable(qpPtr[j], sPower[trackIndexOffset + k])) {
421 // radius test
422 if((!radius) || thisDist < radius) {
423 reporter->add_point(track, usingQueryPoint ? queryPoint : j, k, thisDist);
424 }
425 }
426 }
427 }
428 } // Duration match
429 delete_arrays(track, numVectors, D, DD);
430 }
431 }
432
433 free(data_buffer);
434
435 gettimeofday(&tv2,NULL);
436 VERB_LOG(1,"elapsed time: %ld msec\n",
437 (tv2.tv_sec*1000 + tv2.tv_usec/1000) -
438 (tv1.tv_sec*1000 + tv1.tv_usec/1000))
439
440 // Clean up
441 if(trackOffsetTable)
442 delete[] trackOffsetTable;
443 if(query_data)
444 delete[] query_data;
445 if(qNorm)
446 delete[] qNorm;
447 if(sNorm)
448 delete[] sNorm;
449 if(qPower)
450 delete[] qPower;
451 if(sPower)
452 delete[] sPower;
453 if(D)
454 delete[] D;
455 if(DD)
456 delete[] DD;
457 if(meanDBdur)
458 delete[] meanDBdur;
459 }
460
461 // Unit norm block of features
462 void audioDB::unitNorm(double* X, unsigned dim, unsigned n, double* qNorm){
463 unsigned d;
464 double L2, *p;
465
466 VERB_LOG(2, "norming %u vectors...", n);
467 while(n--) {
468 p = X;
469 L2 = 0.0;
470 d = dim;
471 while(d--) {
472 L2 += *p * *p;
473 p++;
474 }
475 if(qNorm) {
476 *qNorm++=L2;
477 }
478 X += dim;
479 }
480 VERB_LOG(2, "done.\n");
481 }