aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
multiDimFunctionGraphOperator_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-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#pragma once
41
42
50
53
54namespace gum {
55
56 template < typename GUM_SCALAR,
57 template < typename > class FUNCTOR,
58 template < typename > class TerminalNodePolicy >
74
75 template < typename GUM_SCALAR,
76 template < typename > class FUNCTOR,
77 template < typename > class TerminalNodePolicy >
80 GUM_DESTRUCTOR(MultiDimFunctionGraphOperator);
81
82
83 for (auto instIter = _DG1InstantiationNeeded_.beginSafe();
84 instIter != _DG1InstantiationNeeded_.endSafe();
85 ++instIter)
86 SOA_DEALLOCATE(instIter.val(), sizeof(short int) * _nbVar_);
87
88 for (auto instIter = _DG2InstantiationNeeded_.beginSafe();
89 instIter != _DG2InstantiationNeeded_.endSafe();
90 ++instIter)
91 SOA_DEALLOCATE(instIter.val(), sizeof(short int) * _nbVar_);
92
93 if (_nbVar_ != 0) SOA_DEALLOCATE(_default_, sizeof(short int) * _nbVar_);
94 }
95
96 // This function is the main function. To be call every time an operation
97 // between the two given Function Graphs is required
98 template < typename GUM_SCALAR,
99 template < typename > class FUNCTOR,
100 template < typename > class TerminalNodePolicy >
106
107 Idx* varInst = nullptr;
108 if (_nbVar_ != 0) {
109 varInst = static_cast< Idx* >(SOA_ALLOCATE(sizeof(Idx) * _nbVar_));
110 for (Idx i = 0; i < _nbVar_; i++)
111 varInst[i] = (Idx)0;
112 }
113
114 O4DGContext conti(varInst, _nbVar_);
115 conti.setDG1Node(_DG1_->root());
116 conti.setDG2Node(_DG2_->root());
117
118 NodeId root = _compute_(conti, (Idx)0 - 1);
119 _rd_->manager()->setRootNode(root);
120
121 if (_nbVar_ != 0) SOA_DEALLOCATE(varInst, sizeof(Idx) * _nbVar_);
122
123 return _rd_;
124 }
125
126 // This function computes an efficient order for the final decision diagrams.
127 // Its main criterion to do so is the number of re-exploration to be done.
128 template < typename GUM_SCALAR,
129 template < typename > class FUNCTOR,
130 template < typename > class TerminalNodePolicy >
133 SequenceIteratorSafe< const DiscreteVariable* > fite = _DG1_->variablesSequence().beginSafe();
134 SequenceIteratorSafe< const DiscreteVariable* > site = _DG2_->variablesSequence().beginSafe();
135
136 while (fite != _DG1_->variablesSequence().endSafe()
137 && site != _DG2_->variablesSequence().endSafe()) {
138 // Test : if var from first order is already in final order
139 // we move onto the next one
140 if (_rd_->variablesSequence().exists(*fite)) {
141 ++fite;
142 continue;
143 }
144
145 // Test : if var from second order is already in final order
146 // we move onto the next one
147 if (_rd_->variablesSequence().exists(*site)) {
148 ++site;
149 continue;
150 }
151
152 // Test : is current var of the first order present in the second order.
153 // if not we add it to final order
154 if (!_DG2_->variablesSequence().exists(*fite)) {
155 _rd_->add(**fite);
156 ++fite;
157 continue;
158 }
159
160 // Test : is current var of the second order present in the first order.
161 // if not we add it to final order
162 if (!_DG1_->variablesSequence().exists(*site)) {
163 _rd_->add(**site);
164 ++site;
165 continue;
166 }
167
168 // Test : is current var of the second order present in the first order.
169 // if not we add it to final order
170 if (*fite == *site) {
171 _rd_->add(**fite);
172 ++fite;
173 ++site;
174 continue;
175 }
176
177 // Test : the current tested situation is when two retrograde variables
178 // are detected.
179 // Chosen solution here is to find compute domainSize in between
180 // and chose the one with the smallest
181 _nbVarRetro_++;
182 if (_distance_(_DG1_, *fite, *site) < _distance_(_DG2_, *site, *fite)) {
183 _rd_->add(**fite);
184 _sizeVarRetro_ *= (*fite)->domainSize();
185 ++fite;
186 continue;
187 } else {
188 _rd_->add(**site);
189 _sizeVarRetro_ *= (*site)->domainSize();
190 ++site;
191 continue;
192 }
193 }
194
195 // Whenever an iterator has finished its sequence,
196 // the other may still be in the middle of its one.
197 // Hence, this part ensures that any variables remaining
198 // will be added to the final sequence if needed.
199 if (fite == _DG1_->variablesSequence().endSafe()) {
200 for (; site != _DG2_->variablesSequence().endSafe(); ++site)
201 if (!_rd_->variablesSequence().exists(*site)) _rd_->add(**site);
202 } else {
203 for (; fite != _DG1_->variablesSequence().endSafe(); ++fite)
204 if (!_rd_->variablesSequence().exists(*fite)) _rd_->add(**fite);
205 }
206
207
208 // Various initialization needed now that we have a bigger picture
209 _nbVar_ = _rd_->variablesSequence().size();
210
211 if (_nbVar_ != 0) {
212 _default_ = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * _nbVar_));
213 for (Idx i = 0; i < _nbVar_; i++)
214 _default_[i] = (short int)0;
215 }
216 }
217
218 // This function computes the number of re-exploration needed whenever to
219 // retrograde variables collides
220 template < typename GUM_SCALAR,
221 template < typename > class FUNCTOR,
222 template < typename > class TerminalNodePolicy >
225 const DiscreteVariable* from,
226 const DiscreteVariable* to) {
227 Idx posi = d->variablesSequence().pos(from);
228 Idx dist = 1;
229
230 while (d->variablesSequence().atPos(posi) != to) {
231 dist *= (*(d->variablesSequence().atPos(posi))).domainSize();
232 posi++;
233 }
234
235 return dist;
236 }
237
238 // This function computes for every nodes if any retrograde variable is
239 // present below
240 template < typename GUM_SCALAR,
241 template < typename > class FUNCTOR,
242 template < typename > class TerminalNodePolicy >
246 HashTable< NodeId, short int* > nodesVarDescendant;
247 Size tableSize = Size(_nbVar_ * sizeof(short int));
248
249 for (auto varIter = dg->variablesSequence().rbeginSafe();
250 varIter != dg->variablesSequence().rendSafe();
251 --varIter) {
252 Idx varPos = _rd_->variablesSequence().pos(*varIter);
253 const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
254 while (nodeIter != nullptr) {
255 short int* instantiationNeeded = static_cast< short int* >(SOA_ALLOCATE(tableSize));
256 dgInstNeed.insert(nodeIter->element(), instantiationNeeded);
257
258 short int* varDescendant = static_cast< short int* >(SOA_ALLOCATE(tableSize));
259 nodesVarDescendant.insert(nodeIter->element(), varDescendant);
260 for (Idx j = 0; j < _nbVar_; j++) {
261 instantiationNeeded[j] = (short int)0;
262 varDescendant[j] = (short int)0;
263 }
264
265 varDescendant[varPos] = (short int)1;
266 for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons(); ++modality) {
267 if (!dg->isTerminalNode(dg->node(nodeIter->element())->son(modality))) {
268 short int* sonVarDescendant
269 = nodesVarDescendant[dg->node(nodeIter->element())->son(modality)];
270 for (Idx varIdx = 0; varIdx < _nbVar_; varIdx++) {
271 varDescendant[varIdx] += sonVarDescendant[varIdx];
272 if (varDescendant[varIdx] && varIdx < varPos)
273 instantiationNeeded[varIdx] = (short int)1;
274 }
275 }
276 }
277 nodeIter = nodeIter->nextLink();
278 }
279 }
280
281 for (auto varIter = dg->variablesSequence().beginSafe();
282 varIter != dg->variablesSequence().endSafe();
283 ++varIter) {
284 const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
285 while (nodeIter != nullptr) {
286 for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons(); ++modality) {
287 NodeId sonId = dg->node(nodeIter->element())->son(modality);
288 if (!dg->isTerminalNode(sonId)) {
289 for (Idx varIdx = 0; varIdx < _nbVar_; ++varIdx) {
290 if (dgInstNeed[nodeIter->element()][varIdx] && nodesVarDescendant[sonId][varIdx]) {
291 dgInstNeed[sonId][varIdx] = (short int)1;
292 }
293 }
294 }
295 }
296 nodeIter = nodeIter->nextLink();
297 }
298 }
299
300 for (HashTableIterator< NodeId, short int* > it = nodesVarDescendant.begin();
301 it != nodesVarDescendant.end();
302 ++it) {
303 SOA_DEALLOCATE(it.val(), tableSize);
304 }
305 nodesVarDescendant.clear();
306 }
307
310
311 // A key is used for prunning uneccesary operations since once a node has been
312 // visited in a given context, there's no use to revisit him,
313 // the result will be the same node, so we just have to do an association
314 // context - node.
315 // The context consists in :
316 // _ Leader node we are visiting.
317 // _ Follower node we are visiting.
318 // _ For all retrograde variables, if it has been instanciated
319 // before, current modality instanciated, meaning :
320 // _ 0 means the variable hasn't be instanciated yet,
321 // _ From 1 to domainSize + 1 means that current modality
322 // index of variable is value - 1,
323 // _ domainSize + 2 means variable is on default mode.
324 // A key - node association is made each time we create a node in resulting
325 // diagram.
326 // Since GUM_MULTI_DIM_DECISION_DIAGRAM_RECUR_FUNCTION is a corner step in
327 // algorithm ( meaning each time we explore a node we go trought
328 // this function ), check only have to be at the beginning of that function.
329 template < typename GUM_SCALAR,
330 template < typename > class FUNCTOR,
331 template < typename > class TerminalNodePolicy >
333 O4DGContext& currentSituation,
334 Idx lastInstVarPos) {
335 _nbCall_ += 1;
336
337 NodeId newNode = 0;
338
339
340 // If both current nodes are terminal,
341 // we only have to compute the resulting value
342 if (_DG1_->isTerminalNode(currentSituation.DG1Node())
343 && _DG2_->isTerminalNode(currentSituation.DG2Node())) {
344 // We have to compute new valueand we insert a new node in diagram with
345 // this value, ...
346 return _rd_->manager()->addTerminalNode(
347 _function_(_DG1_->terminalNodeValue(currentSituation.DG1Node()),
348 _DG2_->terminalNodeValue(currentSituation.DG2Node())));
349 }
350
351 // If not,
352 // we'll have to do some exploration
353
354 // First we ensure that we hadn't already visit this pair of node under hte
355 // same circumstances
356
357 short int* dg1NeededVar = _DG1InstantiationNeeded_.exists(currentSituation.DG1Node())
358 ? _DG1InstantiationNeeded_[currentSituation.DG1Node()]
359 : _default_;
360 Idx dg1CurrentVarPos
361 = _DG1_->isTerminalNode(currentSituation.DG1Node())
362 ? _nbVar_
363 : _rd_->variablesSequence().pos(_DG1_->node(currentSituation.DG1Node())->nodeVar());
364 short int* dg2NeededVar = _DG2InstantiationNeeded_.exists(currentSituation.DG2Node())
365 ? _DG2InstantiationNeeded_[currentSituation.DG2Node()]
366 : _default_;
367 Idx dg2CurrentVarPos
368 = _DG2_->isTerminalNode(currentSituation.DG2Node())
369 ? _nbVar_
370 : _rd_->variablesSequence().pos(_DG2_->node(currentSituation.DG2Node())->nodeVar());
371
372 short int* instNeeded = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * _nbVar_));
373 for (Idx i = 0; i < _nbVar_; i++)
374 instNeeded[i] = dg1NeededVar[i] + dg2NeededVar[i];
375
376 double curSitKey = currentSituation.key(instNeeded);
377
378 if (_explorationTable_.exists(curSitKey)) {
379 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
380 return _explorationTable_[curSitKey];
381 }
382
383 // ====================================================
384
385 NodeId origDG1 = currentSituation.DG1Node(), origDG2 = currentSituation.DG2Node();
386
388 NodeId leadNodeId = 0;
389 Idx leadVarPos = _rd_->variablesSequence().size();
390 using SetNodeFunction = void (O4DGContext::*)(const NodeId&);
391
392 SetNodeFunction leadFunction = nullptr;
393
394 bool sameVar = false;
395
396 if (!_DG1_->isTerminalNode(currentSituation.DG1Node())) {
397 if (currentSituation.varModality(dg1CurrentVarPos) != 0) {
398 currentSituation.setDG1Node(_DG1_->node(currentSituation.DG1Node())
399 ->son(currentSituation.varModality(dg1CurrentVarPos) - 1));
400
401 newNode = _compute_(currentSituation, lastInstVarPos);
402 _explorationTable_.insert(curSitKey, newNode);
403 currentSituation.setDG1Node(origDG1);
404 currentSituation.setDG2Node(origDG2);
405
406 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
407
408 return newNode;
409 }
410
411 leaddg = _DG1_;
412 leadNodeId = currentSituation.DG1Node();
413 leadVarPos = dg1CurrentVarPos;
414 leadFunction = &O4DGContext::setDG1Node;
415 }
416
417 if (!_DG2_->isTerminalNode(currentSituation.DG2Node())) {
418 if (currentSituation.varModality(dg2CurrentVarPos) != 0) {
419 currentSituation.setDG2Node(_DG2_->node(currentSituation.DG2Node())
420 ->son(currentSituation.varModality(dg2CurrentVarPos) - 1));
421
422 newNode = _compute_(currentSituation, lastInstVarPos);
423 _explorationTable_.insert(curSitKey, newNode);
424 currentSituation.setDG1Node(origDG1);
425 currentSituation.setDG2Node(origDG2);
426
427 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
428
429 return newNode;
430 }
431
432 if (leadVarPos == dg2CurrentVarPos) { sameVar = true; }
433
434 if (leadVarPos > dg2CurrentVarPos) {
435 leaddg = _DG2_;
436 leadNodeId = currentSituation.DG2Node();
437 leadVarPos = dg2CurrentVarPos;
438 leadFunction = &O4DGContext::setDG2Node;
439 }
440 }
441
442 // ====================================================
443
444 // Before exploring nodes, we have to ensure that every anticipated
445 // exploration is done
446 for (Idx varPos = lastInstVarPos + 1; varPos < leadVarPos; ++varPos) {
447 if (instNeeded[varPos]) {
448 const DiscreteVariable* curVar = _rd_->variablesSequence().atPos(varPos);
449 NodeId* sonsIds
450 = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
451
452 for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
453 currentSituation.chgVarModality(varPos, modality + 1);
454
455 sonsIds[modality] = _compute_(currentSituation, varPos);
456 }
457
458 newNode = _rd_->manager()->addInternalNode(curVar, sonsIds);
459
460 _explorationTable_.insert(curSitKey, newNode);
461 currentSituation.chgVarModality(varPos, 0);
462 currentSituation.setDG1Node(origDG1);
463 currentSituation.setDG2Node(origDG2);
464
465 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
466
467 return newNode;
468 }
469 }
470
471 // ====================================================
472
473 // If only one of the current node is terminal,
474 // we have to pursue deeper on the other diagram
475 if (sameVar) {
476 // If so - meaning it's the same variable - we have to go
477 // down on both
478 const InternalNode* dg1Node = _DG1_->node(origDG1);
479 const InternalNode* dg2Node = _DG2_->node(origDG2);
480
481 const DiscreteVariable* curVar = dg1Node->nodeVar();
482 Idx varPos = _rd_->variablesSequence().pos(curVar);
483
484 NodeId* sonsIds = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
485
486 for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
487 currentSituation.chgVarModality(varPos, modality + 1);
488 currentSituation.setDG1Node(dg1Node->son(modality));
489 currentSituation.setDG2Node(dg2Node->son(modality));
490
491 sonsIds[modality] = _compute_(currentSituation, varPos);
492 }
493
494 newNode = _rd_->manager()->addInternalNode(curVar, sonsIds);
495
496 _explorationTable_.insert(curSitKey, newNode);
497 currentSituation.chgVarModality(varPos, 0);
498 currentSituation.setDG1Node(origDG1);
499 currentSituation.setDG2Node(origDG2);
500
501 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
502
503 return newNode;
504 }
505 // ====================================================
506 else {
507 const InternalNode* leaddgNode = leaddg->node(leadNodeId);
508
509 const DiscreteVariable* curVar = leaddgNode->nodeVar();
510 NodeId* sonsIds = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
511
512 for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
513 currentSituation.chgVarModality(leadVarPos, modality + 1);
514 (currentSituation.*leadFunction)(leaddgNode->son(modality));
515
516 sonsIds[modality] = _compute_(currentSituation, leadVarPos);
517 }
518
519 newNode = _rd_->manager()->addInternalNode(curVar, sonsIds);
520
521 _explorationTable_.insert(curSitKey, newNode);
522 currentSituation.chgVarModality(leadVarPos, 0);
523 currentSituation.setDG1Node(origDG1);
524 currentSituation.setDG2Node(origDG2);
525
526 SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
527
528 return newNode;
529 }
530 }
531
532 template < typename GUM_SCALAR,
533 template < typename > class FUNCTOR,
534 template < typename > class TerminalNodePolicy >
538
539 template < typename GUM_SCALAR,
540 template < typename > class FUNCTOR,
541 template < typename > class TerminalNodePolicy >
542 INLINE Idx
546
547 template < typename GUM_SCALAR,
548 template < typename > class FUNCTOR,
549 template < typename > class TerminalNodePolicy >
554
555} // namespace gum
Unsafe Iterators for hashtables.
Definition hashTable.h:2428
Base class for discrete random variable.
virtual Size domainSize() const =0
The class for generic Hash Tables.
Definition hashTable.h:637
iterator begin()
Returns an unsafe 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 & end() noexcept
Returns the unsafe iterator pointing to the end of the hashtable.
void clear()
Removes all the elements in the hash table.
Structure used to represent a node internal structure.
const DiscreteVariable * nodeVar() const
Returns the node variable.
NodeId son(Idx modality) const
Returns the son at a given index.
Idx _nbVar_
The total number of variable implied in the operation.
Idx _distance_(const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *, const DiscreteVariable *, const DiscreteVariable *)
Heuristic methods to decide which of two retrograde variables should come first.
const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * _DG2_
The other one.
HashTable< double, NodeId > _explorationTable_
The hashtable used to know if two pair of nodes have already been visited.
void _establishVarOrder_()
Computes an order for the final Decision graph that will minimize the number of re exploration.
MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * _rd_
The resulting function graph.
const FUNCTOR< GUM_SCALAR > _function_
The function to be performed on the leaves.
MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * compute()
Computes and builds the Function Graph that is the result of the operation.
HashTable< NodeId, short int * > _DG2InstantiationNeeded_
Table uses to know if a given node of second function graph has retrograde vrariables.
void _findRetrogradeVariables_(const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *dg, HashTable< NodeId, short int * > &dgInstNeed)
Establish for each node in both function graph if it has retrograde variables beneath it.
MultiDimFunctionGraphOperator(const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *DG1, const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *DG2)
Default constructor.
HashTable< NodeId, short int * > _DG1InstantiationNeeded_
Table uses to know if a given node of first function graph has retrograde vrariables.
NodeId _compute_(O4DGContext &currentSituation, Idx lastInstVarPos)
The main recursion function.
const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * _DG1_
One of the two function graphs used for the operation.
short int * _default_
Just a comptuationnal trick.
static MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * getReducedAndOrderedInstance()
Returns a reduced and ordered instance.
virtual const Sequence< const DiscreteVariable * > & variablesSequence() const override
Returns a const ref to the sequence of DiscreteVariable*.
Class used to manipulate context during Function Graph Operations.
Definition o4DGContext.h:70
const NodeId & DG2Node() const
Get DG2 diagram current explored Node.
void setDG2Node(const NodeId &)
Set DG2 diagram current explored Node.
const NodeId & DG1Node() const
Get DG1 diagram current explored Node.
void chgVarModality(Idx, Idx)
Changes given variable modality.
void setDG1Node(const NodeId &)
Set DG1 diagram current explored Node.
const double & key(short int *instNeeded)
Returns o4DGContext key.
Idx varModality(Idx)
Changes given variable modality.
Safe iterators for Sequence.
Definition sequence.h:1134
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.
Headers of the InternalNode class.
Class used to compute the operation between two decision diagrams.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
#define SOA_DEALLOCATE(x, y)
#define SOA_ALLOCATE(x)