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