aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
incrementalGraphLearner_tpl.h
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#pragma once
42
43
51// =======================================================
52#include <queue>
53// =======================================================
56
59// =======================================================
62// =======================================================
64
65// =======================================================
66
67namespace gum {
68
69 // ============================================================================
71 // ============================================================================
72
73 // ############################################################################
84 // ############################################################################
85 template < TESTNAME AttributeSelection, bool isScalar >
88 gum::VariableSet varList,
89 const DiscreteVariable* value) : target_(target), setOfVars_(varList), value_(value) {
90 GUM_CONSTRUCTOR(IncrementalGraphLearner);
91
92 for (auto varIter = setOfVars_.cbeginSafe(); varIter != setOfVars_.cendSafe(); ++varIter)
93 var2Node_.insert(*varIter, new LinkedList< NodeId >());
95
96 model_.addNode();
99 value_,
101 }
102
103 // ############################################################################
104 /// Default destructor
105 // ############################################################################
106 template < TESTNAME AttributeSelection, bool isScalar >
108 for (auto nodeIter = nodeId2Database_.beginSafe(); nodeIter != nodeId2Database_.endSafe();
109 ++nodeIter)
110 delete nodeIter.val();
112 for (auto nodeIter = nodeSonsMap_.beginSafe(); nodeIter != nodeSonsMap_.endSafe(); ++nodeIter)
113 SOA_DEALLOCATE(nodeIter.val(), sizeof(NodeId) * nodeVarMap_[nodeIter.key()]->domainSize());
114
115 for (auto varIter = var2Node_.beginSafe(); varIter != var2Node_.endSafe(); ++varIter)
116 delete varIter.val();
118 for (auto nodeIter = leafDatabase_.beginSafe(); nodeIter != leafDatabase_.endSafe(); ++nodeIter)
119 delete nodeIter.val();
120
121 _clearValue_();
122
123 GUM_DESTRUCTOR(IncrementalGraphLearner);
124 }
125
126 // ============================================================================
128 // ============================================================================
129
130 // ############################################################################
135 // ############################################################################
136 template < TESTNAME AttributeSelection, bool isScalar >
138 const Observation* newObs) {
139 _assumeValue_(newObs);
140
141 // The we go across the tree
142 NodeId currentNodeId = root_;
143
144 while (nodeSonsMap_.exists(currentNodeId)) {
145 // On each encountered node, we update the database
146 updateNodeWithObservation_(newObs, currentNodeId);
147
148 // The we select the next to go throught
149 currentNodeId = nodeSonsMap_[currentNodeId][_branchObs_(newObs, nodeVarMap_[currentNodeId])];
150 }
151
152 // On final insertion into the leave we reach
153 updateNodeWithObservation_(newObs, currentNodeId);
154 leafDatabase_[currentNodeId]->insert(newObs);
155 }
156
157 // ============================================================================
159 // ============================================================================
160
161 // ############################################################################
164 /// Graph has then indeed to be revised
165 // ############################################################################
166 template < TESTNAME AttributeSelection, bool isScalar >
168 const DiscreteVariable* var) {
169 Link< NodeId >* nodIter = var2Node_[var]->list();
170 Link< NodeId >* nni = nullptr;
171 while (nodIter) {
172 nni = nodIter->nextLink();
173 convertNode2Leaf_(nodIter->element());
174 nodIter = nni;
175 }
176 }
177
178 // ############################################################################
179 /**
180 * From the given sets of node, selects randomly one and installs it
181 * on given node. Chechks of course if node's current variable is not in that
182 * set first.
183 * @param nody : the node we update
184 * @param bestVar : the set of interessting vars to be installed here
185 */
186 // ############################################################################
187 template < TESTNAME AttributeSelection, bool isScalar >
189 NodeId updatedNode,
190 gum::VariableSet& varsOfInterest) {
191 // If this node has no interesting variable, we turn it into a leaf
192 if (varsOfInterest.empty()) {
193 convertNode2Leaf_(updatedNode);
194 return;
196
197 // If this node has already one of the best variable intalled as test, we
198 // move on
199 if (nodeVarMap_.exists(updatedNode) && varsOfInterest.exists(nodeVarMap_[updatedNode])) {
200 return;
201 }
202
203 // In any other case we have to install variable as best test
204 Idx randy = randomValue(varsOfInterest.size()), basc = 0;
205 SetConstIteratorSafe< const DiscreteVariable* > varIter;
206 for (varIter = varsOfInterest.cbeginSafe(), basc = 0;
207 varIter != varsOfInterest.cendSafe() && basc < randy;
208 ++varIter, basc++)
209 ;
210
211 transpose_(updatedNode, *varIter);
212 }
214 // ############################################################################
216 // ############################################################################
217 template < TESTNAME AttributeSelection, bool isScalar >
219 NodeId currentNodeId) {
220 if (nodeVarMap_[currentNodeId] != value_) {
221 leafDatabase_.insert(currentNodeId, new Set< const Observation* >());
222
223 // Resolving tensor sons issue
224 for (Idx modality = 0; modality < nodeVarMap_[currentNodeId]->domainSize(); ++modality) {
225 NodeId sonId = nodeSonsMap_[currentNodeId][modality];
226 convertNode2Leaf_(sonId);
227 (*leafDatabase_[currentNodeId]) = (*leafDatabase_[currentNodeId]) + *(leafDatabase_[sonId]);
228 removeNode_(sonId);
229 }
230
231 SOA_DEALLOCATE(nodeSonsMap_[currentNodeId],
232 sizeof(NodeId) * nodeVarMap_[currentNodeId]->domainSize());
233 nodeSonsMap_.erase(currentNodeId);
235 chgNodeBoundVar_(currentNodeId, value_);
236 }
237 }
238
239 // ############################################################################
242 // ############################################################################
243 template < TESTNAME AttributeSelection, bool isScalar >
245 NodeId currentNodeId,
246 const DiscreteVariable* desiredVar) {
247 // **************************************************************************************
248 // Si le noeud courant contient déjà la variable qu'on souhaite lui amener
249 // Il n'y a rien à faire
250 if (nodeVarMap_[currentNodeId] == desiredVar) { return; }
251
252 // **************************************************************************************
253 // Si le noeud courant est terminal,
254 // Il faut artificiellement insérer un noeud liant à la variable
255 if (nodeVarMap_[currentNodeId] == value_) {
256 // We turned this leaf into an internal node.
257 // This mean that we'll need to install children leaves for each value of
258 // desiredVar
260 // First We must prepare these new leaves NodeDatabases and Sets<const
261 // Observation*>
265 Set< const Observation* >** obsetMap = static_cast< Set< const Observation* >** >(
266 SOA_ALLOCATE(sizeof(Set< const Observation* >*) * desiredVar->domainSize()));
267 for (Idx modality = 0; modality < desiredVar->domainSize(); ++modality) {
269 obsetMap[modality] = new Set< const Observation* >();
272 = leafDatabase_[currentNodeId]->beginSafe();
273 leafDatabase_[currentNodeId]->endSafe() != obsIter;
274 ++obsIter) {
275 dbMap[_branchObs_(*obsIter, desiredVar)]->addObservation(*obsIter);
276 obsetMap[_branchObs_(*obsIter, desiredVar)]->insert(*obsIter);
277 }
279 // Then we can install each new leaves (and put in place the sonsMap)
280 NodeId* sonsMap
281 = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * desiredVar->domainSize()));
282 for (Idx modality = 0; modality < desiredVar->domainSize(); ++modality)
283 sonsMap[modality] = insertLeafNode_(dbMap[modality], value_, obsetMap[modality]);
284
285 // Some necessary clean up
286 SOA_DEALLOCATE(dbMap,
288 * desiredVar->domainSize());
289 SOA_DEALLOCATE(obsetMap, sizeof(Set< const Observation* >*) * desiredVar->domainSize());
290
291 // And finally we can turn the node into an internal node associated to
292 // desiredVar
293 chgNodeBoundVar_(currentNodeId, desiredVar);
294 nodeSonsMap_.insert(currentNodeId, sonsMap);
295
296 return;
297 }
298
299 // *************************************************************************************
300 // Remains the general case where currentNodeId is an internal node.
301
302 // First we ensure that children node use desiredVar as variable
303 for (Idx modality = 0; modality < nodeVarMap_[currentNodeId]->domainSize(); ++modality)
304 transpose_(nodeSonsMap_[currentNodeId][modality], desiredVar);
306 // Sequence<NodeDatabase<AttributeSelection, isScalar>*>
307 // sonsNodeDatabase =
308 // nodeId2Database_[currentNodeId]->splitOnVar(desiredVar);
309 NodeId* sonsMap
310 = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * desiredVar->domainSize()));
312 // Then we create the new mapping
313 for (Idx desiredVarModality = 0; desiredVarModality < desiredVar->domainSize();
314 ++desiredVarModality) {
315 NodeId* grandSonsMap = static_cast< NodeId* >(
316 SOA_ALLOCATE(sizeof(NodeId) * nodeVarMap_[currentNodeId]->domainSize()));
319 for (Idx currentVarModality = 0;
320 currentVarModality < nodeVarMap_[currentNodeId]->domainSize();
321 ++currentVarModality) {
322 grandSonsMap[currentVarModality]
323 = nodeSonsMap_[nodeSonsMap_[currentNodeId][currentVarModality]][desiredVarModality];
324 sonDB->operator+=((*nodeId2Database_[grandSonsMap[currentVarModality]]));
325 }
326
327 sonsMap[desiredVarModality]
328 = insertInternalNode_(sonDB, nodeVarMap_[currentNodeId], grandSonsMap);
329 }
330
331 // Finally we clean the old remaining nodes
332 for (Idx currentVarModality = 0; currentVarModality < nodeVarMap_[currentNodeId]->domainSize();
333 ++currentVarModality) {
334 removeNode_(nodeSonsMap_[currentNodeId][currentVarModality]);
335 }
336
337 // We suppress the old sons map and remap to the new one
338 SOA_DEALLOCATE(nodeSonsMap_[currentNodeId],
339 sizeof(NodeId) * nodeVarMap_[currentNodeId]->domainSize());
340 nodeSonsMap_[currentNodeId] = sonsMap;
341
342 chgNodeBoundVar_(currentNodeId, desiredVar);
343 }
344
345 // ############################################################################
352 // ############################################################################
353 template < TESTNAME AttributeSelection, bool isScalar >
356 const DiscreteVariable* boundVar) {
357 NodeId newNodeId = model_.addNode();
358 nodeVarMap_.insert(newNodeId, boundVar);
359 nodeId2Database_.insert(newNodeId, nDB);
360 var2Node_[boundVar]->addLink(newNodeId);
361
362 needUpdate_ = true;
363
364 return newNodeId;
365 }
366
367 // ############################################################################
375 // ############################################################################
376 template < TESTNAME AttributeSelection, bool isScalar >
379 const DiscreteVariable* boundVar,
380 NodeId* sonsMap) {
381 NodeId newNodeId = this->insertNode_(nDB, boundVar);
382 nodeSonsMap_.insert(newNodeId, sonsMap);
383 return newNodeId;
384 }
385
386 // ############################################################################
394 // ############################################################################
395 template < TESTNAME AttributeSelection, bool isScalar >
398 const DiscreteVariable* boundVar,
400 NodeId newNodeId = this->insertNode_(nDB, boundVar);
401 leafDatabase_.insert(newNodeId, obsSet);
402 return newNodeId;
403 }
404
405 // ############################################################################
411 // ############################################################################
412 template < TESTNAME AttributeSelection, bool isScalar >
414 NodeId currentNodeId,
415 const DiscreteVariable* desiredVar) {
416 if (nodeVarMap_[currentNodeId] == desiredVar) return;
417
418 var2Node_[nodeVarMap_[currentNodeId]]->searchAndRemoveLink(currentNodeId);
419 var2Node_[desiredVar]->addLink(currentNodeId);
420 nodeVarMap_[currentNodeId] = desiredVar;
421
422 if (nodeVarMap_[currentNodeId] != value_ && leafDatabase_.exists(currentNodeId)) {
423 delete leafDatabase_[currentNodeId];
424 leafDatabase_.erase(currentNodeId);
425 }
426
427 if (nodeVarMap_[currentNodeId] == value_ && !leafDatabase_.exists(currentNodeId)) {
428 leafDatabase_.insert(currentNodeId, new Set< const Observation* >());
429 }
430
431 needUpdate_ = true;
432 }
433
434 // ############################################################################
439 // ############################################################################
440 template < TESTNAME AttributeSelection, bool isScalar >
442 // Retriat de l'id
443 model_.eraseNode(currentNodeId);
444
445 // Retrait du vecteur fils
446 if (nodeSonsMap_.exists(currentNodeId)) {
447 SOA_DEALLOCATE(nodeSonsMap_[currentNodeId],
448 sizeof(NodeId) * nodeVarMap_[currentNodeId]->domainSize());
449 nodeSonsMap_.erase(currentNodeId);
450 }
451
452 if (leafDatabase_.exists(currentNodeId)) {
453 delete leafDatabase_[currentNodeId];
454 leafDatabase_.erase(currentNodeId);
455 }
456
457 // Retrait de la variable
458 var2Node_[nodeVarMap_[currentNodeId]]->searchAndRemoveLink(currentNodeId);
459 nodeVarMap_.erase(currentNodeId);
460
461 // Retrait du NodeDatabase
462 delete nodeId2Database_[currentNodeId];
463 nodeId2Database_.erase(currentNodeId);
464
465 needUpdate_ = true;
466 }
467
468 template < TESTNAME AttributeSelection, bool isScalar >
472
473 template < TESTNAME AttributeSelection, bool isScalar >
477
478 template < TESTNAME AttributeSelection, bool isScalar >
480
481 template < TESTNAME AttributeSelection, bool isScalar >
486
487 template < TESTNAME AttributeSelection, bool isScalar >
488 void
493
494 template < TESTNAME AttributeSelection, bool isScalar >
495 void
500
501 template < TESTNAME AttributeSelection, bool isScalar >
507
508 template < TESTNAME AttributeSelection, bool isScalar >
515
516 template < TESTNAME AttributeSelection, bool isScalar >
523
524 template < TESTNAME AttributeSelection, bool isScalar >
528
529 template < TESTNAME AttributeSelection, bool isScalar >
533
534 template < TESTNAME AttributeSelection, bool isScalar >
538
539 template < TESTNAME AttributeSelection, bool isScalar >
540 const DiscreteVariable*
544
545 template < TESTNAME AttributeSelection, bool isScalar >
547 Idx modality) const {
548 return this->nodeSonsMap_[ni][modality];
549 }
550
551 template < TESTNAME AttributeSelection, bool isScalar >
555
556 template < TESTNAME AttributeSelection, bool isScalar >
558 const Observation* newObs,
559 NodeId currentNodeId) {
560 nodeId2Database_[currentNodeId]->addObservation(newObs);
561 }
562
563 template < TESTNAME AttributeSelection, bool isScalar >
567 varIter != setOfVars_.endSafe();
568 ++varIter)
569 ret->add(**varIter);
570 }
571
572} // namespace gum
Headers of the ChiSquare class.
Base class for discrete random variable.
virtual Size domainSize() const =0
virtual void updateNodeWithObservation_(const Observation *newObs, NodeId currentNodeId)
NodeId nodeSon(NodeId ni, Idx modality) const override
virtual void transpose_(NodeId, const DiscreteVariable *)
Installs given variable to the given node, ensuring that the variable is not present in its subtree.
virtual void updateVar(const DiscreteVariable *)
If a new modality appears to exists for given variable, call this method to turn every associated nod...
virtual NodeId insertLeafNode_(NodeDatabase< AttributeSelection, isScalar > *nDB, const DiscreteVariable *boundVar, Set< const Observation * > *obsSet)
inserts a new leaf node in internal graohs
HashTable< const DiscreteVariable *, LinkedList< NodeId > * > var2Node_
Associates to any variable the list of all nodes associated to this variable.
NodeGraphPart model_
The source of nodeId.
Idx _branchObs_(const Observation *obs, const DiscreteVariable *var)
NodeId root_
The root of the ordered tree.
HashTable< NodeId, NodeDatabase< AttributeSelection, isScalar > * > nodeId2Database_
This hashtable binds every node to an associated NodeDatabase which handles every observation that co...
virtual void addObservation(const Observation *obs)
Inserts a new observation.
void updateNode_(NodeId nody, gum::VariableSet &bestVars)
From the given sets of node, selects randomly one and installs it on given node.
virtual void convertNode2Leaf_(NodeId)
Turns the given node into a leaf if not already so.
virtual NodeId insertNode_(NodeDatabase< AttributeSelection, isScalar > *nDB, const DiscreteVariable *boundVar)
inserts a new node in internal graph
void _assumeValue_(const Observation *obs)
Get value assumed by studied variable for current observation.
const DiscreteVariable * nodeVar(NodeId ni) const override
MultiDimFunctionGraph< double > * target_
The final diagram we're building.
~IncrementalGraphLearner() override
Default destructor.
HashTable< NodeId, Set< const Observation * > * > leafDatabase_
Idx nodeNbObservation(NodeId ni) const override
HashTable< NodeId, const DiscreteVariable * > nodeVarMap_
void insertSetOfVars(MultiDimFunctionGraph< double > *ret) const override
IncrementalGraphLearner(MultiDimFunctionGraph< double > *target, gum::VariableSet attributesSet, const DiscreteVariable *learnVariable)
Default constructor.
virtual void chgNodeBoundVar_(NodeId chgedNodeId, const DiscreteVariable *desiredVar)
bool isTerminal(NodeId ni) const override
virtual NodeId insertInternalNode_(NodeDatabase< AttributeSelection, isScalar > *nDB, const DiscreteVariable *boundVar, NodeId *sonsMap)
inserts a new internal node in internal graph
void add(const DiscreteVariable &v) override
Adds a new var to the variables of the multidimensional matrix.
<agrum/FMDP/learning/datastructure/nodeDatabase.h>
void addObservation(const Observation *)
Nb observation taken into account by this instance.
Idx modality(const DiscreteVariable *var) const
Returns the modality assumed by the given variable in this observation.
double reward() const
Returns the modality assumed by the given variable in this observation.
Idx rModality(const DiscreteVariable *var) const
Returns the modality assumed by the given variable in this observation.
Safe iterators for the Set class.
Definition set.h:592
Representation of a set.
Definition set.h:129
static const const_iterator_safe & cendSafe() noexcept
The usual safe end iterator to parse the set.
Definition set_tpl.h:403
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:613
const_iterator_safe cbeginSafe() const
The usual safe begin iterator to parse the set.
Definition set_tpl.h:391
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
Base class for discrete random variable.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
Idx randomValue(const Size max=2)
Returns a random Idx between 0 and max-1 included.
Headers of the interface specifying functions to be implemented by any incremental learner.
Useful macros for maths.
Priority queues in which the same element can appear several times.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Set< const DiscreteVariable * > VariableSet
#define SOA_DEALLOCATE(x, y)
#define SOA_ALLOCATE(x)
Provides basic types used in aGrUM.
Contains useful methods for random stuff.