LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
KnnSearcher.cpp
Go to the documentation of this file.
1// Copyright 2002-2019 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6#include "KnnSearcher.h"
7#include "hasheddb.h"
8#include "lshsignature.h"
9#include "toolnumber.h"
12#ifndef WIN32
13#include <sys/mman.h>
14#endif
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <fcntl.h>
18#include <algorithm>
19#include <boost/lexical_cast.hpp>
20
21
22using namespace std;
23
24namespace Lima
25{
26namespace LinguisticProcessing
27{
28namespace WordSenseDisambiguation
29{
30
31 KnnSearcher::KnnSearcher(const map<string, string>& knnsearchConfig) :
32 m_hashedDir(),
33 m_data(),
34 m_hkMap(),
35 m_hkMapSize(0),
36 m_nbVectors(0),
37 m_nbDims(0),
38 m_signatureLength(0),
39 m_totalPermutations(0),
40 m_beam(0),
41 m_k(0)
42 {
43#ifndef WIN32
44 LOGINIT("WordSenseDisambiguator");
45 if (knnsearchConfig.find("hashedDir") != knnsearchConfig.end())
46 {
47 m_hashedDir = knnsearchConfig.find("hashedDir")->second;
48 }
49 else
50 {
51 m_hashedDir = "hasheddb";
52 LWARN << "No hashedDir in knnsearchConfig. Default is set to 'hasheddb'. ";
53 }
54 if (knnsearchConfig.find("totalPermutations") != knnsearchConfig.end())
55 {
56 m_totalPermutations = boost::lexical_cast< int >(knnsearchConfig.find("totalPermutations")->second);
57 }
58 else
59 {
61 LWARN << "No totalPermutations in knnsearchConfig. Default is set to '10'. ";
62 }
63 if (knnsearchConfig.find("beam") != knnsearchConfig.end())
64 {
65 m_beam = boost::lexical_cast< int >(knnsearchConfig.find("beam")->second);
66 }
67 else
68 {
69 m_beam = 20;
70 LWARN << "No beam in knnsearchConfig. Default is set to '5'. ";
71 }
72 if (knnsearchConfig.find("k") != knnsearchConfig.end())
73 {
74 m_k = boost::lexical_cast< int >(knnsearchConfig.find("k")->second);
75 }
76 else
77 {
78 m_k = 50;
79 LWARN << "No k in knnsearchConfig. Default is set to '50'. ";
80 }
81
82 struct stat filestatus;
83 string convFile = m_hashedDir + "/semmapconversion.global.bin";
84 stat(convFile.c_str(), &filestatus );
85 uint64_t mmapSize = filestatus.st_size;
86 int nbVects = mmapSize/sizeof(uint64_t);
87 int ifd = open(convFile.c_str(), O_RDONLY);
88 if (ifd == -1)
89 cerr << "Error on opening file " << convFile.c_str() << endl;
90
91 uint64_t* codemmap = (uint64_t*)mmap(0, mmapSize, PROT_READ, MAP_PRIVATE, ifd, 0);
92 if (codemmap == MAP_FAILED)
93 {
94 close(ifd);
95 perror("Error mmapping the file");
96 exit(EXIT_FAILURE);
97 }
98 else if (codemmap == NULL)
99 {
100 close(ifd);
101 perror("Error mmapping the file - codemmap == NULL");
102 exit(EXIT_FAILURE);
103 }
104 for (int i = 0; i < nbVects; i++)
105 {
106 m_owner2System[codemmap[i]]=i;
107 m_system2Owner[i]=codemmap[i];
108 }
109 if (munmap(codemmap, mmapSize) == -1) {
110 LWARN << "Error un-mmapping the converter file "<< convFile;
111 }
112#endif
113 }
114
115
117 {
118#ifndef WIN32
119 LOGINIT("WordSenseDisambiguator");
120 if (munmap(m_hkMap, m_hkMapSize) == -1) {
121 LWARN << "Error un-mmapping the hk file ";
122 }
123#endif
124 }
125
126int KnnSearcher::getKNN(uint64_t lemmaId,
127 SemanticContext::const_iterator itContext,
128 NNList& knns)
129{
130 LOGINIT("WordSenseDisambiguator");
131
132 HashedDB* hashedDb;
133 if (m_data.find(itContext->first) == m_data.end()
134 || m_data.find(itContext->first)->second == NULL)
135 {
136 string hashedDbFile = m_hashedDir + "/SemMap." + itContext->first + ".16384.hashed";
137 hashedDb = new HashedDB(hashedDbFile);
138 m_nbVectors = hashedDb->getNSignatures();
139 m_data[itContext->first] = hashedDb;
140 }
141 else
142 {
143 hashedDb = m_data.find(itContext->first)->second;
144 }
145 if (m_hkMap == NULL)
146 {
147 mapHashKey(itContext->first);
148 }
149 // hash context :D
151 uint64_t start = time(NULL);
152 //LSHSignature * instanceSignature = buildSignature(itContext);
153 LSHSignature * instanceSignature = new LSHSignature(m_signatureLength);
154 uint64_t end = time(NULL);
155 LDEBUG << "Hash context duration " << end-start << " s ";
156
157 // knn lookup
158 // Extract target vector signature (partial)
159 LSHSignature targetSignature = hashedDb->getSignature ( owner2Sys(lemmaId ) );
160
161 // change with instance Signature
162 hashedDb->insert (owner2Sys(lemmaId), *instanceSignature);
163
164 // XOR the hashed DB with the target signature (partial)
165 LDEBUG << "Xoring hashed db" ;
166 LSHSignature target1 = hashedDb->getSignature ( 0 );
167 LSHSignature target2 = hashedDb->getSignature ( 1 );
168 cerr << owner2Sys(lemmaId) << endl;
169 LSHSignature trueTarget = hashedDb->getSignature ( owner2Sys(lemmaId) );
170 target1.print(0,20);
171 target2.print(0,20);
172 trueTarget.print(0,20);
173 hashedDb->xorX ( *instanceSignature );
174 target1 = hashedDb->getSignature ( 0 );
175 target2 = hashedDb->getSignature ( 1 );
176 trueTarget = hashedDb->getSignature ( owner2Sys(lemmaId) );
177
178 LDEBUG << "Sorting hashed db";
179 // Fast Search
180 /*
181 vectorCount* finalResult = NULL;
182 int resultsCount = fastKNNSort ( *hashedDb, finalResult );
183 */
184 vectorCount* finalResult = (vectorCount*) malloc (50*sizeof(vectorCount));
185 int resultsCount = 50;
186
187 cerr << "test2 : " ;
188 cerr << finalResult[0].index << endl;
189 // Presenting results
190 LDEBUG << "ResultsCount : " << resultsCount;
191 for (int i = 0; i < resultsCount; i++)
192 {
193 cerr << "Reading at : " << i << endl;
194 LDEBUG << sys2Owner(finalResult[i].index) << " : " << finalResult[i].count;
195 knns[sys2Owner(finalResult[i].index)] = finalResult[i].count;
196 }
197
198 // back to original hasheddb
199 hashedDb->insert (owner2Sys(lemmaId), targetSignature);
200 free(finalResult);
201 delete instanceSignature;
202 //delete hashedDb;
203 //return knns[sys2Owner(finalResult[resultsCount].index)];
204 return knns.size();
205}
206
207
208int KnnSearcher::fastKNNSort(HashedDB& xoredDb, vectorCount* semiFinalResult)
209{
210 LOGINIT("WordSenseDisambiguator");
211 // m_totalPermutations : the number of permutations performed by the whole thing
212 // result will contain beam * nPermutations candidates
213 int nbLongsInLSH = m_signatureLength / ( 8*sizeof ( LSHSignature::base ) );
214 vector<int> result;
215 for ( int permutationI = 0; permutationI < m_totalPermutations; permutationI ++ )
216 {
217 // count permutation's bits for this candidate
218 // permutations : actually, a randomization from {0,1,2,...,X}
219 vector<int> & permutation = ToolNumber::makePermutation ( nbLongsInLSH );
220
221 int beam = m_beam > m_nbVectors
223 :m_beam ;
224 cerr << "beam "<< m_beam << "|" << m_nbVectors << " -> " << beam << endl;
225 vector<int> & tmpResult = xoredDb.fastKNNSearch1 ( permutation ,beam ) ;
226 delete &permutation;
227 for ( vector<int>::iterator candidateItr = tmpResult.begin(); candidateItr != tmpResult.end(); candidateItr ++ )
228 {
229 LDEBUG << "tmpResult systemId : "<< *candidateItr << " -> "<< sys2Owner(*candidateItr) << " : " << xoredDb.getSignature ( *candidateItr ).bitCount() ;
230 xoredDb.getSignature ( *candidateItr ).print(0,30);
231 }
232 result.insert ( result.end(), tmpResult.begin(), tmpResult.end() );
233 delete &tmpResult;
234 }
235 // cerr << "results.size : " << result.size() << endl;
236 // count local bits for each candidate
237 set<int> doneSet;
238 // semiFinalResult : (index, semiFinalCount)[maxPermutations*beam]
239 const int maxResults = m_totalPermutations * m_beam;
240 semiFinalResult=(vectorCount*) malloc (maxResults*sizeof(vectorCount));
241 LDEBUG << "Max results : " << maxResults;
242 int resultsCount;
243 int resultIndex = 0;
244 // merge local candidates
245 for ( vector<int>::iterator candidateItr = result.begin(); candidateItr != result.end(); candidateItr ++ )
246 if ( doneSet.find ( *candidateItr ) == doneSet.end() )
247 {
248 semiFinalResult[resultIndex].index = *candidateItr;
249 LDEBUG << "ownerId : "<< *candidateItr;
250 semiFinalResult[resultIndex].count = xoredDb.getSignature ( *candidateItr ).bitCount();
251 LDEBUG << "count : " << semiFinalResult[resultIndex].count;
252 doneSet.insert ( *candidateItr );
253 resultIndex++;
254 cerr << "resultindex : " << resultIndex << endl;
255 }
256 resultsCount = resultIndex;
257
258 for (; resultIndex < maxResults; resultIndex++)
259 {
260 semiFinalResult[resultIndex].index = -1;
261 semiFinalResult[resultIndex].count = m_signatureLength + 1;
262 }
263
264 // Now, every process has a bunch of candidates and their full cosine approximation : we must sort / merge and keep only neighbours best values
265 // Select the n-best candidates in the current (local) list / The n-best are sorted
266
267 if ( m_k > resultsCount)
268 {
269 LDEBUG << "m_k ("
270 << m_k
271 << ")> resultsCount ("
272 << resultsCount
273 << "). Process will potentially fail.";
274 }
275 //LDEBUG << "nbTerms + 1 : " << m_nbVectors + 1;
276 int limitSort = m_k > resultsCount
277 ? resultsCount
278 : m_k ;
279 LDEBUG << "limit sort : " << limitSort;
280 partial_sort ( semiFinalResult, semiFinalResult + limitSort, semiFinalResult + resultsCount, vectorCountCompare );
281
282 cerr << "maxResults : " << maxResults << endl;
283 cerr << "resultsCount : " << resultsCount << endl;
284 cerr << "test : " << semiFinalResult[0].index << endl;
285
286 return resultsCount;
287}
288
289
290LSHSignature* KnnSearcher::buildSignature(SemanticContext::const_iterator itContext)
291{
292 LOGINIT("WordSenseDisambiguator");
293 LSHSignature* instanceSignature = new LSHSignature(m_signatureLength);
294 int nPairs = itContext->second.size();
295 char * data;
296
297 data = (char*) malloc (nPairs*(sizeof(uint)+sizeof(float)));
298 int cntCtx = 0;
299 for (set<uint64_t>::const_iterator itValues = itContext->second.begin();
300 itValues != itContext->second.end();
301 itValues++)
302 {
303 uint tmpI = owner2Sys(*itValues);
304 memcpy(&data[cntCtx], &tmpI, sizeof(uint));
305 float tmpF = (float)1./(float)nPairs;
306 memcpy(&data[cntCtx]+sizeof(int), &tmpF, sizeof(float));
307 cntCtx++;
308 }
309 assert(nPairs==cntCtx);
310
311 LDEBUG << "magiccosine at " << itContext->first << " - m_nbDims = " << m_nbDims << " - nPairs = " << nPairs ;
312 uint64_t start = time(NULL);
313 assert(m_data.find(itContext->first)!=m_data.end());
314 assert(m_hkMap!=NULL);
315
316 for ( int key=0; key < m_signatureLength; key++ )
317 {
318 if ( ToolNumber::magiccosine ( data ,
319 ((float*) (m_hkMap + 2*sizeof(int) +key* m_nbDims*sizeof(float))),
320 m_nbDims,
321 nPairs) > 0 )
322 {
323 instanceSignature->setBit ( key );
324 }
325 else
326 {
327 instanceSignature->unsetBit ( key );
328 }
329 }
330
331 free(data);
332 uint64_t end = time(NULL);
333 LDEBUG << "cosine duration : " << end-start<< " s" ;
334 TimeUtils::logElapsedTime("Hash context");
335 return instanceSignature;
336}
337
338/*
339void KnnSearcher::mapHashedDB(string relation)
340{
341 LOGINIT("WordSenseDisambiguator");
342 //--------initialize mmap ----------
343 string hashedDbFile = m_hashedDir + "SemMap."+ relation + ".16384.A.hasheddb";
344
345 int fd = open(hashedDbFile.c_str(), O_RDONLY);
346 if (fd == -1) {
347 perror("Error opening file for reading");
348 exit(EXIT_FAILURE);
349 }
350 int *premap; // mmapped array of int's
351 premap = (int*)mmap(0, 2*sizeof(int), PROT_READ, MAP_PRIVATE, fd, 0);
352 if (premap == MAP_FAILED) {
353 close(fd);
354 perror("Error mmapping the file");
355 exit(EXIT_FAILURE);
356 }
357
358 // Read the file int-by-int from the mmap
359
360 m_signatureLength = premap[0];
361 m_nbVectors = premap[1];
362 //LDEBUG << lshSignatureLength << "*" << nVectors << "=" << lshSignatureLength * nVectors ;
363 if (munmap(premap, 2*sizeof(int)) == -1) {
364 perror("Error un-mmapping the file");
365 }
366 close(fd);
367 open(hashedDbFile.c_str(), O_RDONLY);
368 // LDEBUG << "lshSignatureLength : " << lshSignatureLength ;
369 // LDEBUG << "nVectors : " << nVectors ;
370 if (m_hashedDbMapSize=0)
371 {
372 m_hashedDbMapSize = (m_signatureLength/8)*m_nbVectors+ 2*sizeof(int);
373 }
374 LDEBUG << m_hashedDbMapSize;
375 LDEBUG << "hashed map n vectors " << m_nbVectors ;
376 // LDEBUG << "HASHEDMAP SIZE : " << hashedMapSize ;
377 // LDEBUG << "getPageSize "<< getpagesize() ;
378 m_data[relation].second = (char*)mmap(0, m_hashedDbMapSize, PROT_READ, MAP_PRIVATE, fd, 0);
379 if (m_data[relation].second == MAP_FAILED) {
380 close(fd);
381 perror("Error mmapping the file");
382 exit(EXIT_FAILURE);
383 }
384
385 close(fd);
386 //-----------------------------
387
388
389}
390*/
391
392
393void KnnSearcher::mapHashKey(string relation)
394{
395#ifndef WIN32
396 LOGINIT("WordSenseDisambiguator");
397 map<uint64_t, int> dists;
398 string hashkeyFile = m_hashedDir + "/SemMap.all.16384.hashkey";
399 int hkfd = open(hashkeyFile.c_str(), O_RDONLY);
400 if (hkfd == -1) {
401 LERROR << "Error opening file "<< hashkeyFile <<" for reading";
402 exit(EXIT_FAILURE);
403 }
404 int *hkpremap; // mmapped array of int's
405 hkpremap = (int*)mmap(0, 2*sizeof(int), PROT_READ, MAP_PRIVATE, hkfd, 0);
406 if (hkpremap == MAP_FAILED) {
407 close(hkfd);
408 LDEBUG << "Error mmapping the file " << hashkeyFile ;
409 exit(EXIT_FAILURE);
410 }
411
412 // Read the file int-by-int from the mmap
413 m_signatureLength = hkpremap[1];
414 m_nbDims = hkpremap[0];
415 //LDEBUG << lshSignatureLength << "*" << nVectors << "=" << lshSignatureLength * nVectors ;
416 if (munmap(hkpremap, 2*sizeof(int)) == -1)
417 {
418 LDEBUG << "Error un-mmapping the file " << hashkeyFile ;
419 }
420 if (m_hkMapSize == 0 )
421 {
422 m_hkMapSize = m_signatureLength*m_nbDims*sizeof(float)+ 2*sizeof(int);
423 }
424 m_hkMap = (char*)mmap(0, m_hkMapSize, PROT_READ, MAP_PRIVATE, hkfd, 0);
425 if (m_hkMap == MAP_FAILED)
426 {
427 close(hkfd);
428 LDEBUG << "Error mmapping the file " << hashkeyFile << " at the second time ." ;
429 exit(EXIT_FAILURE);
430 }
431
432 close(hkfd);
433
434 LDEBUG << "Hashkey mapped for " << relation ;
435#endif
436}
437
438
439
440uint64_t KnnSearcher::owner2Sys(uint64_t ownerId)
441{
442 if (m_owner2System.find(ownerId)!=m_owner2System.end())
443 {
444 return m_owner2System[ownerId];
445 }
446 return 2000000;
447}
448
449
450uint64_t KnnSearcher::sys2Owner(uint64_t systemId)
451{
452 if (m_system2Owner.find(systemId)!=m_system2Owner.end())
453 {
454 return m_system2Owner[systemId];
455 }
456 return 0;
457}
458
459}
460}
461}
#define LWARN
Definition LimaCommon.h:160
#define LOGINIT(X)
Definition LimaCommon.h:187
#define LDEBUG
Definition LimaCommon.h:157
#define LERROR
Definition LimaCommon.h:161
void print(double rank, int limit)
void setBit(uint b)
void unsetBit(uint b)
uint64_t base
std::vector< int > & fastKNNSearch1(std::vector< int > &permutations, int beam)
Definition hasheddb.cpp:117
LSHSignature * buildSignature(SemanticContext::const_iterator itContext)
KnnSearcher(const std::map< std::string, std::string > &knnsearchConfig)
int fastKNNSort(HashedDB &xoredDB, vectorCount *finalResult)
int getKNN(uint64_t lemmaId, SemanticContext::const_iterator itContext, NNList &knns)
static std::vector< int > & makePermutation(int size)
Definition toolnumber.h:30
static float magiccosine(char *a, float *b, int dim, int nPairs)
Definition toolnumber.h:53
static void logElapsedTime(const std::string &mess, const std::string &taskCategory=std::string(""))
log the number of microseconds since last UpdateCurrentTime
static void updateCurrentTime(const std::string &taskCategory=std::string(""))
store current time for new elapsed time computation
bool vectorCountCompare(vectorCount i, vectorCount j)
Definition hasheddb.cpp:38
NAUTITIA.
STL namespace.