aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
leafAggregator.cpp
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2025 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41
49// =======================================================
51
53
54// =======================================================
55
56namespace gum {
57
58 // ############################################################################
59 // Constructors / Destructors
60 // ############################################################################
61
62 // ============================================================================
63 // Default constructor.
64 // ============================================================================
65 LeafAggregator::LeafAggregator(NodeGraphPart* idSource, double similarityThreshold) :
66 _leavesCpt_(idSource), _similarityThreshold_(similarityThreshold) {
67 GUM_CONSTRUCTOR(LeafAggregator);
69 _needsUpdate_ = false;
70 }
71
72 // ============================================================================
73 // Default constructor.
74 // ============================================================================
77
78 delete _initialContext_;
79
81 = _leaf2Pair_.beginSafe();
82 leafIter != _leaf2Pair_.endSafe();
83 ++leafIter) {
84 for (SetIteratorSafe< LeafPair* > pairIter = leafIter.val()->beginSafe();
85 pairIter != leafIter.val()->endSafe();
86 ++pairIter) {
87 LeafPair* curPair = *pairIter;
88 _leaf2Pair_[curPair->otherLeaf(leafIter.key())]->erase(*pairIter);
89 leafIter.val()->erase(curPair);
90 delete curPair;
91 }
92 delete leafIter.val();
93 }
94
95
96 GUM_DESTRUCTOR(LeafAggregator);
97 }
98
99 // ############################################################################
100 //
101 // ############################################################################
102
103 // ============================================================================
104 //
105 // ============================================================================
107 Set< LeafPair* >* leafPairSet = new Set< LeafPair* >();
109
110 // ****************************************************************************************
111 // Création et ajout des pairs de base (Feuille de base + nouvelle Feuille)
113 = _leaf2Pair_.cbeginSafe();
114 leafIter != _leaf2Pair_.cendSafe();
115 ++leafIter) {
116 // Création de la pair et ajout dans les listes de pair des feuilles de
117 // base
118 LeafPair* p = new LeafPair(l, leafIter.key());
119 p->updateLikelyhood();
120 leafPairSet->insert(p);
121 (leafIter.val())->insert(p);
122
123 // Ajout de la nouvelle pair au tas initial
125
126 bag.insert(p);
127 }
128
129 // ****************************************************************************************
130 // Enregistrement de la nouvelle Feuille en tant que feuille de base
131 _leaf2Pair_.insert(l, leafPairSet);
132
133 // ****************************************************************************************
134 // Ajout de la feuille aux FusionContext
135
136 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
137 fusIter != _fusionSeq_.endSafe();
138 ++fusIter) {
139 // Ajout de la nouvelle pair composée de la feuille de FusIter et de la
140 // nouvelle feuille aux FusionContext suivant
141 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
142 ++pairIter) {
143 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())) {
144 bag >> *pairIter;
145 continue;
146 }
147
148 if ((*fusIter)->addPair(*pairIter)) _removeContext_(fusIter.pos() + 1);
149 }
150
151 if ((*fusIter)->associateLeaf(l)) _removeContext_(fusIter.pos() + 1);
152
153 bag << (*fusIter)->leafAssociatedPair(l);
154 }
155
156 _needsUpdate_ = true;
157 }
158
159 // ============================================================================
160 //
161 // ============================================================================
163 // ***********************************************************************************
164 // First we update every base pair linked to that leaf
165 Set< LeafPair* > bag(*(_leaf2Pair_[l]));
166 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
167 ++pairIter) {
168 (*pairIter)->updateLikelyhood();
169 _updateInitialPair_(*pairIter);
170 }
171
172 // **********************************************************************************
173 // The we have top update FusionContext pairs associated to that leaf
174 AbstractLeaf* curLeaf = l;
175 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
176 fusIter != _fusionSeq_.endSafe();
177 ++fusIter) {
178 if ((*fusIter)->leaf()->contains(curLeaf->id())) {
179 bag.clear();
180 if ((*fusIter)->updateAllAssociatedLeaves()) _removeContext_(fusIter.pos() + 1);
181 bag = (*fusIter)->associatedPairs();
182 curLeaf = (*fusIter)->leaf();
183 continue;
184 }
185
186 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
187 ++pairIter) {
188 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())
189 || (*fusIter)->leaf()->contains((*pairIter)->firstLeaf()->id())) {
190 bag >> *pairIter;
191 continue;
192 }
193
194 if ((*fusIter)->updatePair(*pairIter)) _removeContext_(fusIter.pos() + 1);
195 }
196 if ((*fusIter)->updateAssociatedLeaf(curLeaf)) _removeContext_(fusIter.pos() + 1);
197 bag << (*fusIter)->leafAssociatedPair(curLeaf);
198 }
199
200 return _needsUpdate_;
201 }
202
203 // ============================================================================
204 //
205 // ============================================================================
207 // ***********************************************************************************
208 // First we update every base pair linked to that leaf
209 Set< LeafPair* > bag(*(_leaf2Pair_[l]));
210 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
211 ++pairIter) {
212 _removeInitialPair_(*pairIter);
213 (*_leaf2Pair_[(*pairIter)->otherLeaf(l)]) >> *pairIter;
214 }
215
216 // **********************************************************************************
217 // The we have top update FusionContext pairs associated to that leaf
218 Set< LeafPair* > toBeDeleted;
219 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
220 fusIter != _fusionSeq_.endSafe();
221 ++fusIter) {
222 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
223 ++pairIter) {
224 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())
225 || (*fusIter)->leaf()->contains((*pairIter)->firstLeaf()->id())) {
226 bag >> *pairIter;
227 continue;
228 }
229
230 if ((*fusIter)->removePair(*pairIter)) { _removeContext_(fusIter.pos() + 1); }
231 }
232
233 bag << (*fusIter)->leafAssociatedPair(l);
234 toBeDeleted << (*fusIter)->leafAssociatedPair(l);
235
236 if ((*fusIter)->deassociateLeaf(l)) { _removeContext_(fusIter.pos() + 1); }
237 }
238
239 for (SetIteratorSafe< LeafPair* > pairIter = toBeDeleted.beginSafe();
240 pairIter != toBeDeleted.endSafe();
241 ++pairIter)
242 delete *pairIter;
243
244 for (SetIteratorSafe< LeafPair* > pairIter = _leaf2Pair_[l]->beginSafe();
245 pairIter != _leaf2Pair_[l]->endSafe();
246 ++pairIter)
247 delete *pairIter;
248 delete _leaf2Pair_[l];
249 _leaf2Pair_.erase(l);
250
251 _needsUpdate_ = true;
252 }
253
254 // ============================================================================
255 //
256 // ============================================================================
258 LeafPair* nextPair = _initialContext_->top();
259 pair_iterator pb = _initialContext_->beginPairs();
260 pair_iterator pe = _initialContext_->endPairs();
261 if (!_fusionSeq_.empty()) {
262 nextPair = _fusionSeq_.back()->top();
263 pb = _fusionSeq_.back()->beginPairs();
264 pe = _fusionSeq_.back()->endPairs();
265 }
266
267
268 while (nextPair && nextPair->likelyhood() < _similarityThreshold_) {
269 AbstractLeaf* newLeaf = nextPair->convert2Leaf(_leavesCpt_->addNode());
270 FusionContext< false >* newContext = new FusionContext< false >(newLeaf);
271
272 for (pair_iterator pairIter = pb; pairIter != pe; ++pairIter) {
273 if (!newLeaf->contains(pairIter.key()->firstLeaf()->id())
274 && !newLeaf->contains(pairIter.key()->secondLeaf()->id()))
275 newContext->addPair(pairIter.key());
276 if (!newLeaf->contains(pairIter.key()->firstLeaf()->id())
277 && !newContext->containsAssociatedLeaf(pairIter.key()->firstLeaf()))
278 newContext->associateLeaf(pairIter.key()->firstLeaf());
279 if (!newLeaf->contains(pairIter.key()->secondLeaf()->id())
280 && !newContext->containsAssociatedLeaf(pairIter.key()->secondLeaf()))
281 newContext->associateLeaf(pairIter.key()->secondLeaf());
282 }
283
284 _fusionSeq_.insert(newContext);
285 nextPair = _fusionSeq_.back()->top();
286 pb = _fusionSeq_.back()->beginPairs();
287 pe = _fusionSeq_.back()->endPairs();
288 }
289 _needsUpdate_ = false;
290 }
291
294 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.rbeginSafe();
295 fusIter != _fusionSeq_.rendSafe();
296 --fusIter) {
297 bool alreadyIn = false;
299 mapIter != retMap.endSafe();
300 ++mapIter)
301 if (mapIter.val()->contains((*fusIter)->leaf()->id())) {
302 alreadyIn = true;
303 break;
304 }
305 if (!alreadyIn) retMap.insert((*fusIter)->leaf()->id(), (*fusIter)->leaf());
306 }
307
309 = _leaf2Pair_.beginSafe();
310 leafIter != _leaf2Pair_.endSafe();
311 ++leafIter) {
313 mapIter != retMap.endSafe();
314 ++mapIter)
315 if (mapIter.val()->contains(leafIter.key()->id())) {
316 retMap.insert(leafIter.key()->id(), mapIter.val());
317 break;
318 }
319 if (!retMap.exists(leafIter.key()->id())) retMap.insert(leafIter.key()->id(), leafIter.key());
320 }
321
322 return retMap;
323 }
324
326 std::stringstream ss;
327 ss << "################\nTas Initial : " << std::endl
328 << _initialContext_->toString() << std::endl;
329
330 for (auto fusIter = _fusionSeq_.beginSafe(); fusIter != _fusionSeq_.endSafe(); ++fusIter) {
331 ss << "################\nTas " << fusIter.pos() << " : " << std::endl
332 << (*fusIter)->toString();
333 }
334
335 return ss.str();
336 }
337
338 // ############################################################################
339 //
340 // ############################################################################
341
342 // ============================================================================
343 //
344 // ============================================================================
346 for (Idx i = _fusionSeq_.size() - 1; !_fusionSeq_.empty() && i >= startingPos; --i) {
347 _leavesCpt_->eraseNode(_fusionSeq_.atPos(i)->leaf()->id());
348 delete _fusionSeq_.atPos(i);
349 _fusionSeq_.erase(_fusionSeq_.atPos(i));
350 }
351
352 _needsUpdate_ = true;
353 }
354
355 // ============================================================================
356 //
357 // ============================================================================
359 bool res = _initialContext_->addPair(p);
360 if (res) _removeContext_(0);
361 }
362
363 // ============================================================================
364 //
365 // ============================================================================
367 bool res = _initialContext_->updatePair(p);
368 if (res) _removeContext_(0);
369 }
370
371 // ============================================================================
372 //
373 // ============================================================================
375 bool res = _initialContext_->removePair(p);
376 if (res) _removeContext_(0);
377 }
378
379} // namespace gum
Safe Iterators for hashtables.
<agrum/FMDP/learning/datastructure/leaves/abstractLeaf.h>
virtual bool contains(NodeId testedId) const
Returns true if abstractleaf has leaf in it.
bool addPair(LeafPair *p)
bool containsAssociatedLeaf(AbstractLeaf *l)
bool associateLeaf(AbstractLeaf *l)
Safe Const Iterators for hashtables.
Definition hashTable.h:1602
The class for generic Hash Tables.
Definition hashTable.h:637
const iterator_safe & endSafe() noexcept
Returns the safe iterator pointing to the end of the hashtable.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
iterator_safe beginSafe()
Returns the safe iterator pointing to the beginning of the hashtable.
Sequence< FusionContext< false > * > _fusionSeq_
void _addInitialPair_(LeafPair *)
void _updateInitialPair_(LeafPair *)
bool updateLeaf(AbstractLeaf *)
LeafAggregator(NodeGraphPart *idSource, double similarityThreshold)
Default constructor.
void removeLeaf(AbstractLeaf *)
HashTable< AbstractLeaf *, Set< LeafPair * > * > _leaf2Pair_
~LeafAggregator()
Default destructor.
NodeGraphPart * _leavesCpt_
FusionContext< true > * _initialContext_
void _removeInitialPair_(LeafPair *)
HashTable< NodeId, AbstractLeaf * > leavesMap()
void addLeaf(AbstractLeaf *)
<agrum/FMDP/learning/datastructure/leaves/leafPair.h>
Definition leafPair.h:69
AbstractLeaf * convert2Leaf(NodeId leafId) const
Returns a leaf matching data and having given id as id.
Definition leafPair.h:132
void updateLikelyhood()
Updates GStatistic.
Definition leafPair.cpp:64
AbstractLeaf * otherLeaf(AbstractLeaf *l) const
Definition leafPair.h:134
double likelyhood()
Updates GStatistic.
Definition leafPair.cpp:95
Class for node sets in graph.
Safe iterators for Sequence.
Definition sequence.h:1134
Safe iterators for the Set class.
Definition set.h:601
Representation of a set.
Definition set.h:131
iterator_safe beginSafe() const
The usual safe begin iterator to parse the set.
Definition set_tpl.h:414
const iterator_safe & endSafe() const noexcept
The usual safe end iterator to parse the set.
Definition set_tpl.h:426
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:338
Size Idx
Type for indexes.
Definition types.h:79
Headers of the Leaf Aggregator class.
Useful macros for maths.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
HashTableConstIteratorSafe< LeafPair *, std::vector< Size > > pair_iterator