aGrUM 3.2.0
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();
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.
pair_iterator endPairs()
bool addPair(LeafPair *p)
bool removePair(LeafPair *p)
bool updatePair(LeafPair *p)
bool containsAssociatedLeaf(AbstractLeaf *l)
pair_iterator beginPairs()
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
LeafPair(AbstractLeaf *l1, AbstractLeaf *l2)
Default constructor.
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.
virtual void eraseNode(const NodeId id)
erase the node with the given id
virtual NodeId addNode()
insert a new node and return its id
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
Set(Size capacity=HashTableConst::default_size, bool resize_policy=true)
Default constructor.
Definition set_tpl.h:277
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