aGrUM 3.1.1
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-2026 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-2026 *
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#ifdef GUM_NO_INLINE
56#endif // GUM_NO_INLINE
57
58// =======================================================
59
60namespace gum {
61
62 // ############################################################################
63 // Constructors / Destructors
64 // ############################################################################
65
66 // ============================================================================
67 // Default constructor.
68 // ============================================================================
69 LeafAggregator::LeafAggregator(NodeGraphPart* idSource, double similarityThreshold) :
70 _leavesCpt_(idSource), _similarityThreshold_(similarityThreshold) {
71 GUM_CONSTRUCTOR(LeafAggregator);
73 _needsUpdate_ = false;
74 }
75
76 // ============================================================================
77 // Default constructor.
78 // ============================================================================
81
82 delete _initialContext_;
83
85 = _leaf2Pair_.beginSafe();
86 leafIter != _leaf2Pair_.endSafe();
87 ++leafIter) {
88 for (SetIteratorSafe< LeafPair* > pairIter = leafIter.val()->beginSafe();
89 pairIter != leafIter.val()->endSafe();
90 ++pairIter) {
91 LeafPair* curPair = *pairIter;
92 _leaf2Pair_[curPair->otherLeaf(leafIter.key())]->erase(*pairIter);
93 leafIter.val()->erase(curPair);
94 delete curPair;
95 }
96 delete leafIter.val();
97 }
98
99
100 GUM_DESTRUCTOR(LeafAggregator);
101 }
102
103 // ############################################################################
104 //
105 // ############################################################################
106
107 // ============================================================================
108 //
109 // ============================================================================
111 Set< LeafPair* >* leafPairSet = new Set< LeafPair* >();
113
114 // ****************************************************************************************
115 // Création et ajout des pairs de base (Feuille de base + nouvelle Feuille)
117 = _leaf2Pair_.cbeginSafe();
118 leafIter != _leaf2Pair_.cendSafe();
119 ++leafIter) {
120 // Création de la pair et ajout dans les listes de pair des feuilles de
121 // base
122 LeafPair* p = new LeafPair(l, leafIter.key());
123 p->updateLikelyhood();
124 leafPairSet->insert(p);
125 (leafIter.val())->insert(p);
126
127 // Ajout de la nouvelle pair au tas initial
129
130 bag.insert(p);
131 }
132
133 // ****************************************************************************************
134 // Enregistrement de la nouvelle Feuille en tant que feuille de base
135 _leaf2Pair_.insert(l, leafPairSet);
136
137 // ****************************************************************************************
138 // Ajout de la feuille aux FusionContext
139
140 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
141 fusIter != _fusionSeq_.endSafe();
142 ++fusIter) {
143 // Ajout de la nouvelle pair composée de la feuille de FusIter et de la
144 // nouvelle feuille aux FusionContext suivant
145 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
146 ++pairIter) {
147 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())) {
148 bag >> *pairIter;
149 continue;
150 }
151
152 if ((*fusIter)->addPair(*pairIter)) _removeContext_(fusIter.pos() + 1);
153 }
154
155 if ((*fusIter)->associateLeaf(l)) _removeContext_(fusIter.pos() + 1);
156
157 bag << (*fusIter)->leafAssociatedPair(l);
158 }
159
160 _needsUpdate_ = true;
161 }
162
163 // ============================================================================
164 //
165 // ============================================================================
167 // ***********************************************************************************
168 // First we update every base pair linked to that leaf
169 Set< LeafPair* > bag(*(_leaf2Pair_[l]));
170 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
171 ++pairIter) {
172 (*pairIter)->updateLikelyhood();
173 _updateInitialPair_(*pairIter);
174 }
175
176 // **********************************************************************************
177 // The we have top update FusionContext pairs associated to that leaf
178 AbstractLeaf* curLeaf = l;
179 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
180 fusIter != _fusionSeq_.endSafe();
181 ++fusIter) {
182 if ((*fusIter)->leaf()->contains(curLeaf->id())) {
183 bag.clear();
184 if ((*fusIter)->updateAllAssociatedLeaves()) _removeContext_(fusIter.pos() + 1);
185 bag = (*fusIter)->associatedPairs();
186 curLeaf = (*fusIter)->leaf();
187 continue;
188 }
189
190 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
191 ++pairIter) {
192 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())
193 || (*fusIter)->leaf()->contains((*pairIter)->firstLeaf()->id())) {
194 bag >> *pairIter;
195 continue;
196 }
197
198 if ((*fusIter)->updatePair(*pairIter)) _removeContext_(fusIter.pos() + 1);
199 }
200 if ((*fusIter)->updateAssociatedLeaf(curLeaf)) _removeContext_(fusIter.pos() + 1);
201 bag << (*fusIter)->leafAssociatedPair(curLeaf);
202 }
203
204 return _needsUpdate_;
205 }
206
207 // ============================================================================
208 //
209 // ============================================================================
211 // ***********************************************************************************
212 // First we update every base pair linked to that leaf
213 Set< LeafPair* > bag(*(_leaf2Pair_[l]));
214 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
215 ++pairIter) {
216 _removeInitialPair_(*pairIter);
217 (*_leaf2Pair_[(*pairIter)->otherLeaf(l)]) >> *pairIter;
218 }
219
220 // **********************************************************************************
221 // The we have top update FusionContext pairs associated to that leaf
222 Set< LeafPair* > toBeDeleted;
223 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.beginSafe();
224 fusIter != _fusionSeq_.endSafe();
225 ++fusIter) {
226 for (SetIteratorSafe< LeafPair* > pairIter = bag.beginSafe(); pairIter != bag.endSafe();
227 ++pairIter) {
228 if ((*fusIter)->leaf()->contains((*pairIter)->secondLeaf()->id())
229 || (*fusIter)->leaf()->contains((*pairIter)->firstLeaf()->id())) {
230 bag >> *pairIter;
231 continue;
232 }
233
234 if ((*fusIter)->removePair(*pairIter)) { _removeContext_(fusIter.pos() + 1); }
235 }
236
237 bag << (*fusIter)->leafAssociatedPair(l);
238 toBeDeleted << (*fusIter)->leafAssociatedPair(l);
239
240 if ((*fusIter)->deassociateLeaf(l)) { _removeContext_(fusIter.pos() + 1); }
241 }
242
243 for (SetIteratorSafe< LeafPair* > pairIter = toBeDeleted.beginSafe();
244 pairIter != toBeDeleted.endSafe();
245 ++pairIter)
246 delete *pairIter;
247
248 for (SetIteratorSafe< LeafPair* > pairIter = _leaf2Pair_[l]->beginSafe();
249 pairIter != _leaf2Pair_[l]->endSafe();
250 ++pairIter)
251 delete *pairIter;
252 delete _leaf2Pair_[l];
253 _leaf2Pair_.erase(l);
254
255 _needsUpdate_ = true;
256 }
257
258 // ============================================================================
259 //
260 // ============================================================================
262 LeafPair* nextPair = _initialContext_->top();
263 pair_iterator pb = _initialContext_->beginPairs();
264 pair_iterator pe = _initialContext_->endPairs();
265 if (!_fusionSeq_.empty()) {
266 nextPair = _fusionSeq_.back()->top();
267 pb = _fusionSeq_.back()->beginPairs();
268 pe = _fusionSeq_.back()->endPairs();
269 }
270
271
272 while (nextPair && nextPair->likelyhood() < _similarityThreshold_) {
273 AbstractLeaf* newLeaf = nextPair->convert2Leaf(_leavesCpt_->addNode());
274 FusionContext< false >* newContext = new FusionContext< false >(newLeaf);
275
276 for (pair_iterator pairIter = pb; pairIter != pe; ++pairIter) {
277 if (!newLeaf->contains(pairIter.key()->firstLeaf()->id())
278 && !newLeaf->contains(pairIter.key()->secondLeaf()->id()))
279 newContext->addPair(pairIter.key());
280 if (!newLeaf->contains(pairIter.key()->firstLeaf()->id())
281 && !newContext->containsAssociatedLeaf(pairIter.key()->firstLeaf()))
282 newContext->associateLeaf(pairIter.key()->firstLeaf());
283 if (!newLeaf->contains(pairIter.key()->secondLeaf()->id())
284 && !newContext->containsAssociatedLeaf(pairIter.key()->secondLeaf()))
285 newContext->associateLeaf(pairIter.key()->secondLeaf());
286 }
287
288 _fusionSeq_.insert(newContext);
289 nextPair = _fusionSeq_.back()->top();
290 pb = _fusionSeq_.back()->beginPairs();
291 pe = _fusionSeq_.back()->endPairs();
292 }
293 _needsUpdate_ = false;
294 }
295
298 for (SequenceIteratorSafe< FusionContext< false >* > fusIter = _fusionSeq_.rbeginSafe();
299 fusIter != _fusionSeq_.rendSafe();
300 --fusIter) {
301 bool alreadyIn = false;
303 mapIter != retMap.endSafe();
304 ++mapIter)
305 if (mapIter.val()->contains((*fusIter)->leaf()->id())) {
306 alreadyIn = true;
307 break;
308 }
309 if (!alreadyIn) retMap.insert((*fusIter)->leaf()->id(), (*fusIter)->leaf());
310 }
311
313 = _leaf2Pair_.beginSafe();
314 leafIter != _leaf2Pair_.endSafe();
315 ++leafIter) {
317 mapIter != retMap.endSafe();
318 ++mapIter)
319 if (mapIter.val()->contains(leafIter.key()->id())) {
320 retMap.insert(leafIter.key()->id(), mapIter.val());
321 break;
322 }
323 if (!retMap.exists(leafIter.key()->id())) retMap.insert(leafIter.key()->id(), leafIter.key());
324 }
325
326 return retMap;
327 }
328
330 std::string ss
331 = std::format("################\nTas Initial : \n{}\n", _initialContext_->toString());
332 for (auto fusIter = _fusionSeq_.beginSafe(); fusIter != _fusionSeq_.endSafe(); ++fusIter)
333 ss += std::format("################\nTas {} : \n{}", fusIter.pos(), (*fusIter)->toString());
334 return ss;
335 }
336
337 // ############################################################################
338 //
339 // ############################################################################
340
341 // ============================================================================
342 //
343 // ============================================================================
345 for (Idx i = _fusionSeq_.size() - 1; !_fusionSeq_.empty() && i >= startingPos; --i) {
346 _leavesCpt_->eraseNode(_fusionSeq_.atPos(i)->leaf()->id());
347 delete _fusionSeq_.atPos(i);
348 _fusionSeq_.erase(_fusionSeq_.atPos(i));
349 }
350
351 _needsUpdate_ = true;
352 }
353
354 // ============================================================================
355 //
356 // ============================================================================
358 bool res = _initialContext_->addPair(p);
359 if (res) _removeContext_(0);
360 }
361
362 // ============================================================================
363 //
364 // ============================================================================
366 bool res = _initialContext_->updatePair(p);
367 if (res) _removeContext_(0);
368 }
369
370 // ============================================================================
371 //
372 // ============================================================================
374 bool res = _initialContext_->removePair(p);
375 if (res) _removeContext_(0);
376 }
377
378} // 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:1662
The class for generic Hash Tables.
Definition hashTable.h:640
iterator_safe beginSafe()
Returns the safe iterator pointing to the beginning of 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.
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.
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 * otherLeaf(AbstractLeaf *l) const
void updateLikelyhood()
Updates GStatistic.
Definition leafPair.cpp:68
double likelyhood()
Updates GStatistic.
Definition leafPair.cpp:99
AbstractLeaf * convert2Leaf(NodeId leafId) const
Returns a leaf matching data and having given id as id.
Class for node sets in graph.
Safe iterators for Sequence.
Definition sequence.h:1148
Safe iterators for the Set class.
Definition set.h:592
Representation of a set.
Definition set.h:129
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:315
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
iterator_safe beginSafe() const
The usual safe begin iterator to parse the set.
Definition set_tpl.h:385
static const iterator_safe & endSafe() noexcept
The usual safe end iterator to parse the set.
Definition set_tpl.h:397
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