LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
indexElementIterator.cpp
Go to the documentation of this file.
1// Copyright 2002-2020 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6/************************************************************************
7 * @author Besancon Romaric (romaric.besancon@cea.fr)
8 * @date Tue Feb 7 2006
9 ***********************************************************************/
10
11#include "bowNamedEntity.h"
12#include "bowText.h"
13#include "indexElement.h"
14
20#include "defaultIdGenerator.h"
24#include <limits.h>
25#include <algorithm>
26
27using namespace std;
28using namespace Lima::Common::Misc;
29
30namespace Lima {
31namespace Common {
32namespace BagOfWords {
33
34
36{
38
40 AbstractLexiconIdGenerator* idAccessor=0,
41 const uint64_t maxCompoundSize=0,
42 const uint64_t nbMaxPartialCompounds=1000);
45
46
48 void getPositionLengthList(const std::vector<uint64_t>& structure,
49 Misc::PositionLengthList& poslenlist) const;
50
56 bool addInPartQueue(const IndexElement& newElement);
57
60 void storePartsInQueue(boost::shared_ptr< BoWToken > token);
61
62 bool addPartElementsInQueue(boost::shared_ptr< BoWToken > token,
63 std::pair<std::vector<uint64_t>, uint64_t> & ids_rels,
64 const uint64_t rel);
65
79 bool addCombinedPartsInQueue(const Lima::Common::BagOfWords::BoWType type,
80 const std::vector<std::pair<std::vector<uint64_t>, uint64_t> >& partIds_Rels,
81 const uint64_t head,
83 std::pair<std::vector<uint64_t>, uint64_t>& ids_rels,
84 std::vector<uint64_t>& structure,
85 std::vector<uint64_t>& relations,
86 const uint64_t i);
87
88 typedef std::deque<IndexElement> IndexElementQueue;
89
90 // members
91 BoWText::const_iterator m_iterator;
92 BoWText::const_iterator m_iteratorEnd;
93 IndexElementQueue m_partQueue;
94 uint64_t m_maxSizeQueue;
95 uint64_t m_maxCompoundSize;
96 AbstractLexiconIdGenerator* m_idGenerator;
97 QMap<QString,IndexElement> m_alreadyFoundElements;
98};
99
100IndexElementIteratorPrivate::IndexElementIteratorPrivate(const BoWText& bowText,
101 AbstractLexiconIdGenerator* idGenerator,
102 const uint64_t maxCompoundSize,
103 const uint64_t nbMaxPartialCompounds):
104m_iterator(bowText.begin()),
105m_iteratorEnd(bowText.end()),
106m_partQueue(),
107m_maxSizeQueue(nbMaxPartialCompounds),
108m_maxCompoundSize(maxCompoundSize),
109m_idGenerator(idGenerator)
110{
111 if (m_idGenerator==0) {
112 m_idGenerator=new DefaultIdGenerator(
114 }
115 if (m_maxCompoundSize==0) {
116 // if 0, no limitation => set it to UINT_MAX to avoid testing 0
117 // each time
118 m_maxCompoundSize=UINT_MAX;
119 }
120}
121
122IndexElementIteratorPrivate::IndexElementIteratorPrivate(const IndexElementIteratorPrivate& ieip):
123m_iterator(ieip.m_iterator),
124m_iteratorEnd(ieip.m_iteratorEnd),
125m_partQueue(ieip.m_partQueue),
126m_maxSizeQueue(ieip.m_maxSizeQueue),
127m_maxCompoundSize(ieip.m_maxCompoundSize)
128{
129 m_idGenerator=new DefaultIdGenerator(
131 *m_idGenerator = *ieip.m_idGenerator;
132}
133
134IndexElementIteratorPrivate::~IndexElementIteratorPrivate()
135{
136 delete m_idGenerator;
137}
138
139
140//***********************************************************************
141// constructors and destructors
143 AbstractLexiconIdGenerator* idGenerator,
144 const uint64_t maxCompoundSize,
145 const uint64_t nbMaxPartialCompounds):
146 m_d(new IndexElementIteratorPrivate(bowText, idGenerator, maxCompoundSize, nbMaxPartialCompounds))
147{
148}
149
151 m_d(new IndexElementIteratorPrivate(*iei.m_d))
152{
153}
154
159
160//***********************************************************************
161
163{
164 return (m_d->m_iterator == m_d->m_iteratorEnd);
165}
166
167//**********************************************************************
168// get current element ("dereference" iterator)
169//**********************************************************************
170// getting parts is done in this function (rather than in ++ function):
171// which means that if a ++ is done before calling a getElement on
172// a complex token, no parts will be explored
174{
175#ifdef DEBUG_CD
177 LDEBUG << "IndexElementIterator::getElement empty:" << m_d->m_partQueue.empty();
178#endif
179 // If queue is empty
180 // - for simple tokens: a new index element is returned
181 // - for complex tokens : it is filled and then its front is returned
182 if (m_d->m_partQueue.empty())
183 {
184 if (m_d->m_iterator==m_d->m_iteratorEnd)
185 { // at end
186#ifdef DEBUG_CD
187 LDEBUG << "IndexElementIterator::getElement at end: return empty element";
188#endif
189 return IndexElement(); // empty element has id 0
190 }
191 else
192 {
193 boost::shared_ptr< BoWToken> token = boost::dynamic_pointer_cast<BoWToken>((*m_d->m_iterator));
194 boost::shared_ptr< BoWPredicate > predicate;
195
196 switch ((*m_d->m_iterator)->getType())
197 {
199 {
200#ifdef DEBUG_CD
201 LDEBUG << "IndexElementIterator::getElement simple token:" << token->getIdUTF8String();
202#endif
203 if (!m_d->m_alreadyFoundElements.contains(QString::fromUtf8(token->getIdUTF8String().c_str())))
204 {
205 m_d->m_alreadyFoundElements.insert(QString::fromUtf8(token->getIdUTF8String().c_str()),
206 IndexElement(m_d->m_idGenerator->getId(token->getString()),
207 token->getType(),
208 token->getLemma(),
209 token->getCategory(),
210 token->getPosition(),
211 token->getLength()
212 ));
213 }
214 return m_d->m_alreadyFoundElements[QString::fromUtf8(token->getIdUTF8String().c_str())];
215 }
217#ifdef DEBUG_CD
218 LDEBUG << "IndexElementIterator::getElement term:" << token->getIdUTF8String();
219#endif
220 m_d->storePartsInQueue(token);
221 if (m_d->m_partQueue.empty())
222 {
223#ifdef DEBUG_CD
224 LDEBUG << "IndexElementIterator::getElement term: part queue is empty" ;
225#endif
226 (*this)++;
227 return getElement();
228 }
229#ifdef DEBUG_CD
230 LDEBUG << "IndexElementIterator::getElement term after storePartsInQueue front is:" << m_d->m_partQueue.front();
231#endif
232 m_d->m_alreadyFoundElements.insert(QString::fromUtf8(token->getIdUTF8String().c_str()),m_d->m_partQueue.front());
233 return m_d->m_partQueue.front();
234
236#ifdef DEBUG_CD
237 LDEBUG << "IndexElementIterator::getElement named entity:" << boost::dynamic_pointer_cast<BoWNamedEntity>(*m_d->m_iterator)->getIdUTF8String() ;//<< Lima::Common::MediaticData::MediaticData::single().getEntityName(static_cast<BoWNamedEntity*>((*m_d->m_iterator))->getNamedEntityType());
238 // element itself will be stored in queue as part
239#endif
240 m_d->storePartsInQueue(token);
241#ifdef DEBUG_CD
242 LDEBUG << "IndexElementIterator::getElement ne after storePartsInQueue front is:" << m_d->m_partQueue.front();
243#endif
244 if (m_d->m_partQueue.empty())
245 {
246#ifdef DEBUG_CD
247 LDEBUG << "IndexElementIterator::getElement ne: part queue is empty" ;
248#endif
249 (*this)++;
250 return getElement();
251 }
252 m_d->m_alreadyFoundElements.insert(QString::fromUtf8(token->getIdUTF8String().c_str()),m_d->m_partQueue.front());
253 return m_d->m_partQueue.front();
254
255 // FIXME Change the handling of predicates to take into account their complex structure nature
257 {
258 predicate = boost::dynamic_pointer_cast<BoWPredicate>((*m_d->m_iterator));
259 uint64_t id=m_d->m_idGenerator->getId(predicate->getString());
260 return IndexElement(id,
261 predicate->getType(),
262 predicate->getString(),
263 L_NONE,
264 predicate->getPosition(),
265 predicate->getLength(),
266 predicate->getPredicateType()
267 );
268 }
270 return IndexElement();
271 }
272 }
273 }
274 // Queue was not empty, returning its front
275 else {
276#ifdef DEBUG_CD
277 LDEBUG << "IndexElementIterator::getElement empty:" << m_d->m_partQueue.empty() << "return part queue front" << m_d->m_partQueue.front();
278#endif
279 return m_d->m_partQueue.front();
280 }
281
282 // Unreachable
283 return IndexElement(); // empty element has id 0
284}
285
286//**********************************************************************
287// operator ++
288//**********************************************************************
290{
291#ifdef DEBUG_CD
293#endif
294 // If queue is empty, try to advance the text iterator to the next BoWToken
295 // Otherwose, pop the front element and advance the text iterator if the queue is now empty
296 if (m_d->m_partQueue.empty()) {
297#ifdef DEBUG_CD
298 LDEBUG << "IndexElementIterator::operator++ part queue is empty";
299#endif
300 if (m_d->m_iterator!=m_d->m_iteratorEnd) {
301 m_d->m_iterator++;
302 // Jump already found elements
303#ifdef DEBUG_CD
304 LDEBUG << "IndexElementIterator::operator++ Jump if necessary";
305#endif
306 while (m_d->m_iterator != m_d->m_iteratorEnd &&
307 boost::dynamic_pointer_cast<BoWToken>((*m_d->m_iterator)) &&
308 m_d->m_alreadyFoundElements.contains( QString::fromUtf8(boost::dynamic_pointer_cast<BoWToken>((*m_d->m_iterator))->getIdUTF8String().c_str()) ) ) {
309 m_d->m_iterator++;
310 }
311 }
312 }
313 else {
314#ifdef DEBUG_CD
315 LDEBUG << "IndexElementIterator::operator++ part queue not empty";
316#endif
317 m_d->m_partQueue.pop_front();
318 if (m_d->m_partQueue.empty()) { // finished for the parts of this token
319 m_d->m_iterator++;
320 // Jump already found elements
321 while (m_d->m_iterator != m_d->m_iteratorEnd &&
322 boost::dynamic_pointer_cast<BoWToken>((*m_d->m_iterator)) &&
323 m_d->m_alreadyFoundElements.contains( QString::fromUtf8(boost::dynamic_pointer_cast<BoWToken>((*m_d->m_iterator))->getIdUTF8String().c_str()) ) ) {
324 m_d->m_iterator++;
325 }
326 }
327 }
328 return *this;
329}
330
331// postfix ++ operator
333 IndexElementIterator it = *this;
334 ++(*this);
335 return it;
336}
337
338//**********************************************************************
339// helper functions for iterator
340//**********************************************************************
341void IndexElementIteratorPrivate::getPositionLengthList(const std::vector<uint64_t>& structure,
342 PositionLengthList& poslenlist) const
343{
344 // update position/length list for structure
345 // use previous elements in queue
346 for (std::vector<uint64_t>::const_iterator it = structure.begin(); it != structure.end(); ++it) {
347
348 QMap<QString,IndexElement>::const_iterator found = m_alreadyFoundElements.begin();
349 while (found != m_alreadyFoundElements.end() && *it != found.value().getId()) {
350 ++found;
351 }
352
353 if (found != m_alreadyFoundElements.end()) {
354 const PositionLengthList& p = found.value().getPositionLengthList();
355 poslenlist.insert(poslenlist.end(), p.begin(), p.end());
356 }
357 else {
359 LERROR << "getPositionLengthList failure: element id " << *it << " not found";
360 }
361 }
362
363 // sort positions
364 std::sort(poslenlist.begin(),poslenlist.end());
365}
366
367
368bool IndexElementIteratorPrivate::addInPartQueue(const IndexElement& newElement)
369{
370#ifdef DEBUG_CD
372 LDEBUG << "IndexElementIteratorPrivate::addInPartQueue" << newElement;
373#endif
374 if (m_partQueue.size() >= m_maxSizeQueue) {
376 LWARN << "size of queue exceeded";
377 return false;
378 }
379
380 m_partQueue.push_back(newElement);
381// BOWLOGINIT;
382// if (logger.isDebugEnabled()) {
383// ostringstream oss;
384// for (vector<uint64_t>::const_iterator it=structure.begin(),
385// it_end=structure.end(); it!=it_end; it++) {
386// oss << *it << ";";
387// }
388// LDEBUG << "add in part queue " << id << ":"
389// << oss.str()
390// << ";size of queue=" << m_partQueue.size()
391// ;
392// }
393 return true;
394}
395
396
397void IndexElementIteratorPrivate::storePartsInQueue(boost::shared_ptr< Lima::Common::BagOfWords::BoWToken > token)
398{
399#ifdef DEBUG_CD
401 LDEBUG << "IndexElementIteratorPrivate::storePartsInQueue" << token->getIdUTF8String();
402#endif
403 pair<vector<uint64_t>, uint64_t> tokenIds;
404 if (!addPartElementsInQueue(token,tokenIds,0)) {
406 LWARN << "Token contain too many subparts (some are ignored): " << token->getLemma();
407 }
408}
409
410bool IndexElementIteratorPrivate::addPartElementsInQueue(boost::shared_ptr< BoWToken > token,
411 pair<vector<uint64_t>, uint64_t>& ids_rel,
412 uint64_t rel)
413{
414#ifdef DEBUG_CD
416 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue" << token->getIdUTF8String() << rel;
417#endif
418
420 bool result = false;
421 switch (token->getType())
422 {
424 {
425#ifdef DEBUG_CD
426 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue simple token:" << token->getIdUTF8String();
427#endif
428 if (!m_alreadyFoundElements.contains(QString::fromUtf8(token->getIdUTF8String().c_str())))
429 {
430 LimaString lemma=token->getLemma();
431 if (lemma.size()==0) {
432 lemma=token->getInflectedForm();
433 }
434 // simple token : get Id and push in parts
435 uint64_t id=m_idGenerator->getId(token->getString());
436
437 m_alreadyFoundElements.insert(QString::fromUtf8(token->getIdUTF8String().c_str()),IndexElement(id,
438 token->getType(),
439 lemma,
440 token->getCategory(),
441 token->getPosition(),
442 token->getLength(),
443 neType));
444 result = addInPartQueue(m_alreadyFoundElements[QString::fromUtf8(token->getIdUTF8String().c_str())]);
445 } else {
446 result = true;
447 }
448 ids_rel=make_pair(vector<uint64_t>(1,m_alreadyFoundElements[QString::fromUtf8(token->getIdUTF8String().c_str())].getId()),rel);
449 return result;
450 }
452 neType=boost::dynamic_pointer_cast<BoWNamedEntity>(token)->getNamedEntityType();
453 break;
457 default:;
458 }
459
460 // is a complex token
461 boost::shared_ptr< BoWComplexToken > complexToken=
462 boost::dynamic_pointer_cast<BoWComplexToken>(token);
463
464 if (complexToken==0) {
466 LERROR << "failed to convert BoWText element in complex token";
467 return false;
468 }
469
470 if (complexToken->size() == 1) {
471#ifdef DEBUG_CD
472 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue complex token of size one";
473#endif
474 // only one part, do not get into it
475 // (for instance, named entity with one element)
476 // push simple token in parts
477 if (!m_alreadyFoundElements.contains(QString::fromUtf8(token->getIdUTF8String().c_str())))
478 {
479 uint64_t id=m_idGenerator->getId(token->getString());
480 ids_rel=make_pair(vector<uint64_t>(1,id),rel);
481
482 LimaString lemma=token->getLemma();
483 if (lemma.size()==0) {
484 lemma=token->getInflectedForm();
485 }
486 m_alreadyFoundElements.insert(QString::fromUtf8(token->getIdUTF8String().c_str()), IndexElement(id,
487 token->getType(),
488 lemma,
489 token->getCategory(),
490 token->getPosition(),
491 token->getLength(),
492 neType));
493 result = addInPartQueue(m_alreadyFoundElements[QString::fromUtf8(token->getIdUTF8String().c_str())]);
494 } else {
495 return result = true;
496 }
497 return result;
498 }
499
500#ifdef DEBUG_CD
501 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue complex token of size" << complexToken->size();
502#endif
503 ids_rel=make_pair(vector<uint64_t>(),rel);
504 uint64_t nbParts=complexToken->getParts().size();
505 uint64_t head=complexToken->getHead();
506 vector<pair<vector<uint64_t>, uint64_t> > partIdsRels(nbParts);
507 for (uint64_t i=0; i<nbParts; i++) {
508#ifdef DEBUG_CD
509 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue on part" << i << "of complex token" << *complexToken;
510#endif
511 pair<vector<uint64_t>, uint64_t>& thisPartIdsRels=partIdsRels[i];
512 uint64_t relType;
513 boost::shared_ptr< BoWRelation > relation=(complexToken->getParts()[i]).getBoWRelation();
514 if (relation !=0 ) relType=relation->getSynType(); else relType=0;
515 if (!addPartElementsInQueue(complexToken->getParts()[i].getBoWToken(),thisPartIdsRels,relType)) {
516 return false;
517 }
518
519 if (i==head) {
520 // add ids of the head
521 ids_rel.first.insert(ids_rel.first.end(),thisPartIdsRels.first.begin(),thisPartIdsRels.first.end());
522 }
523 }
524#ifdef DEBUG_CD
525 LDEBUG << "IndexElementIteratorPrivate::addPartElementsInQueue parts added; combining them";
526#endif
527 // add ids for combined parts
528 vector<uint64_t> structure; //current structure in recursive function
529 vector<uint64_t> relations; //current relations in recursive function
530 if (!addCombinedPartsInQueue(token->getType(),partIdsRels,head,neType,ids_rel,structure,relations,0)) {
531 return false;
532 }
533 return true;
534}
535
536bool IndexElementIteratorPrivate::addCombinedPartsInQueue(
538 const std::vector<std::pair<std::vector<uint64_t>, uint64_t> >& partIdsRels,
539 const uint64_t head,
541 std::pair<std::vector<uint64_t>, uint64_t>& ids_rel,
542 std::vector<uint64_t>& structure,
543 std::vector<uint64_t>& relations,
544 const uint64_t current)
545{
546#ifdef DEBUG_CD
548#endif
549 QStringList structureKey;
550 for (auto it = structure.begin(); it != structure.end(); ++it) {
551 structureKey << QString::number(*it);
552 }
553#ifdef DEBUG_CD
554 LDEBUG << "addCombinedPartsInQueue: nb parts=" << partIdsRels.size()
555 << ", head=" << head << ", current=" << current << ", structure=" << structureKey.join(";");
556#endif
557 bool result = false;
558 if (current>=partIdsRels.size()) {
559 if (structure.size() == 1) {
560 //just the head: is already in queue
561#ifdef DEBUG_CD
562 LDEBUG << "addCombinedPartsInQueue: just the head: is already in queue";
563#endif
564 return true;
565 }
566 // build indexElement before getting the id : allow to have the
567 // true size of compound (trick: use PositionLengthList to have
568 // the size: number of leaves of the structure), and to avoid
569 // compute the id if size is more than maxCompoundSize
570 if (!m_alreadyFoundElements.contains(structureKey.join(";")))
571 {
572 IndexElement compoundElement(0,type,structure,relations,neType);
573 getPositionLengthList(structure,compoundElement.getPositionLengthList());
574 if (compoundElement.getPositionLengthList().size() > m_maxCompoundSize) {
575 // compound larger than allowed, do not add it in parts, but
576 // return true anyway (false is reserved for queue overflow)
577#ifdef DEBUG_CD
578 LDEBUG << "addCombinedPartsInQueue: just the head: max compound size exceeded";
579#endif
580 return true;
581 }
582 // at end of parts => add current structure
583
584 uint64_t id=m_idGenerator->getId(structure);
585#ifdef DEBUG_CD
586 LDEBUG << "IndexElementIterator: got id from generator " << id;
587#endif
588 compoundElement.setId(id);
589 m_alreadyFoundElements.insert(structureKey.join(";"),compoundElement);
590 if (!addInPartQueue(m_alreadyFoundElements[structureKey.join(";")])) {
591#ifdef DEBUG_CD
592 LDEBUG << "addCombinedPartsInQueue: queue overflow";
593#endif
594 return false;
595 } else {
596 result = true;
597 }
598 } else {
599 result = true;
600 }
601 ids_rel.first.push_back(m_alreadyFoundElements[structureKey.join(";")].getId());
602#ifdef DEBUG_CD
603 LDEBUG << "addCombinedPartsInQueue: added to ids_rel.first: " << m_alreadyFoundElements[structureKey.join(";")].getId() << "; return" << result;
604#endif
605 return result;
606 }
607
608 // add possible at end of structure and recursive call
609 for (auto it = partIdsRels[current].first.begin(); it != partIdsRels[current].first.end(); ++it) {
610 structure.push_back(*it);
611 relations.push_back(partIdsRels[current].second);
612 if (!addCombinedPartsInQueue(type, partIdsRels,head,neType,ids_rel,structure,relations,current+1)) {
613#ifdef DEBUG_CD
614 LDEBUG << "addCombinedPartsInQueue: recursive call returned false";
615#endif
616 return false;
617 }
618 structure.pop_back();
619 relations.pop_back();
620 }
621 // if head, stop here: current iterator is head, hence always added
622 // otherwise, recursive call without current iterator (that is an
623 // extension)
624 if (current!=head) {
625 if (!addCombinedPartsInQueue(type, partIdsRels,head,neType,ids_rel,structure,relations,current+1)) {
626#ifdef DEBUG_CD
627 LDEBUG << "addCombinedPartsInQueue: second recursive call returned false";
628#endif
629 return false;
630 }
631 }
632 return true;
633}
634
635} // end namespace
636} // end namespace
637} // end namespace
#define LWARN
Definition LimaCommon.h:160
#define LDEBUG
Definition LimaCommon.h:157
#define LERROR
Definition LimaCommon.h:161
#define BOWLOGINIT
Definition LimaCommon.h:201
#define L_NONE
Definition StdBitset.h:338
static AbstractLexiconIdGeneratorInformer * getInstance()
virtual uint64_t getId(const LimaString &word) override=0
This class represents a list of elements, that are pointers on polymmorphic tokens that can be simple...
Definition bowText.h:48
An iterator on the bowText that returns IndexElements.
const IndexElement & getElement() const
IndexElementIterator(const BoWText &bowText, AbstractLexiconIdGenerator *idAccessor=0, const uint64_t maxCompoundSize=0, const uint64_t nbMaxPartialCompounds=1000)
constructor
Represent an element of an index If it is a predicate, its simple term is "PredicateElement" and its ...
BoWType
enum to characterize the type of the AbstractBoWElement
@ BOW_NOTYPE
the AbstractBoWElement is an abstract one that should not be instanciated
@ BOW_TERM
the AbstractBoWElement is a multi-term
@ BOW_NAMEDENTITY
the AbstractBoWElement is a named entity
@ BOW_TOKEN
the AbstractBoWElement is a simple token
@ BOW_PREDICATE
the AbstractBoWElement is a predicate (n-ary relation, template or semantic frame
std::vector< std::pair< Position, Length > > PositionLengthList
NAUTITIA.
QString LimaString
Definition LimaString.h:33
STL namespace.