aGrUM  0.16.0
multiDimFunctionGraphOperator_tpl.h
Go to the documentation of this file.
1 
32 
33 namespace gum {
34 
35  template < typename GUM_SCALAR,
36  template < typename >
37  class FUNCTOR,
38  template < typename >
39  class TerminalNodePolicy >
44  __DG1(DG1),
45  __DG2(DG2), __function(),
46  __DG1InstantiationNeeded(DG1->realSize(), true, false),
47  __DG2InstantiationNeeded(DG2->realSize(), true, false) {
48  GUM_CONSTRUCTOR(MultiDimFunctionGraphOperator);
49  __rd =
50  MultiDimFunctionGraph< GUM_SCALAR,
51  TerminalNodePolicy >::getReducedAndOrderedInstance();
52  __nbVar = 0;
53  __default = nullptr;
54 
55  __nbCall = 0;
56  __nbVar = 0;
57  __sizeVarRetro = 1;
58  }
59 
60  template < typename GUM_SCALAR,
61  template < typename >
62  class FUNCTOR,
63  template < typename >
64  class TerminalNodePolicy >
67  GUM_DESTRUCTOR(MultiDimFunctionGraphOperator);
68 
69 
70  for (auto instIter = __DG1InstantiationNeeded.beginSafe();
71  instIter != __DG1InstantiationNeeded.endSafe();
72  ++instIter)
73  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * __nbVar);
74 
75  for (auto instIter = __DG2InstantiationNeeded.beginSafe();
76  instIter != __DG2InstantiationNeeded.endSafe();
77  ++instIter)
78  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * __nbVar);
79 
80  if (__nbVar != 0) SOA_DEALLOCATE(__default, sizeof(short int) * __nbVar);
81  }
82 
83 
84  // This function is the main function. To be call every time an operation
85  // between the two given Function Graphs is required
86  template < typename GUM_SCALAR,
87  template < typename >
88  class FUNCTOR,
89  template < typename >
90  class TerminalNodePolicy >
97 
98  Idx* varInst = nullptr;
99  if (__nbVar != 0) {
100  varInst = static_cast< Idx* >(SOA_ALLOCATE(sizeof(Idx) * __nbVar));
101  for (Idx i = 0; i < __nbVar; i++)
102  varInst[i] = (Idx)0;
103  }
104 
105  O4DGContext conti(varInst, __nbVar);
106  conti.setDG1Node(__DG1->root());
107  conti.setDG2Node(__DG2->root());
108 
109  NodeId root = __compute(conti, (Idx)0 - 1);
110  __rd->manager()->setRootNode(root);
111 
112  if (__nbVar != 0) SOA_DEALLOCATE(varInst, sizeof(Idx) * __nbVar);
113 
114  return __rd;
115  }
116 
117  // This function computes an efficient order for the final decision diagrams.
118  // Its main criterion to do so is the number of re-exploration to be done.
119  template < typename GUM_SCALAR,
120  template < typename >
121  class FUNCTOR,
122  template < typename >
123  class TerminalNodePolicy >
127  __DG1->variablesSequence().beginSafe();
129  __DG2->variablesSequence().beginSafe();
130 
131  while (fite != __DG1->variablesSequence().endSafe()
132  && site != __DG2->variablesSequence().endSafe()) {
133  // Test : if var from first order is already in final order
134  // we move onto the next one
135  if (__rd->variablesSequence().exists(*fite)) {
136  ++fite;
137  continue;
138  }
139 
140  // Test : if var from second order is already in final order
141  // we move onto the next one
142  if (__rd->variablesSequence().exists(*site)) {
143  ++site;
144  continue;
145  }
146 
147  // Test : is current var of the first order present in the second order.
148  // if not we add it to final order
149  if (!__DG2->variablesSequence().exists(*fite)) {
150  __rd->add(**fite);
151  ++fite;
152  continue;
153  }
154 
155  // Test : is current var of the second order present in the first order.
156  // if not we add it to final order
157  if (!__DG1->variablesSequence().exists(*site)) {
158  __rd->add(**site);
159  ++site;
160  continue;
161  }
162 
163  // Test : is current var of the second order present in the first order.
164  // if not we add it to final order
165  if (*fite == *site) {
166  __rd->add(**fite);
167  ++fite;
168  ++site;
169  continue;
170  }
171 
172  // Test : the current tested situation is when two retrograde variables
173  // are detected.
174  // Chosen solution here is to find compute domainSize in between
175  // and chose the one with the smallest
176  __nbVarRetro++;
177  if (__distance(__DG1, *fite, *site) < __distance(__DG2, *site, *fite)) {
178  __rd->add(**fite);
179  __sizeVarRetro *= (*fite)->domainSize();
180  ++fite;
181  continue;
182  } else {
183  __rd->add(**site);
184  __sizeVarRetro *= (*site)->domainSize();
185  ++site;
186  continue;
187  }
188  }
189 
190  // Whenever an iterator has finished its sequence,
191  // the other may still be in the middle of its one.
192  // Hence, this part ensures that any variables remaining
193  // will be added to the final sequence if needed.
194  if (fite == __DG1->variablesSequence().endSafe()) {
195  for (; site != __DG2->variablesSequence().endSafe(); ++site)
196  if (!__rd->variablesSequence().exists(*site)) __rd->add(**site);
197  } else {
198  for (; fite != __DG1->variablesSequence().endSafe(); ++fite)
199  if (!__rd->variablesSequence().exists(*fite)) __rd->add(**fite);
200  }
201 
202 
203  // Various initialization needed now that we have a bigger picture
204  __nbVar = __rd->variablesSequence().size();
205 
206  if (__nbVar != 0) {
207  __default =
208  static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * __nbVar));
209  for (Idx i = 0; i < __nbVar; i++)
210  __default[i] = (short int)0;
211  }
212  }
213 
214  // This function computes the number of re-exploration needed whenever to
215  // retrograde variables collides
216  template < typename GUM_SCALAR,
217  template < typename >
218  class FUNCTOR,
219  template < typename >
220  class TerminalNodePolicy >
221  INLINE Idx
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 
239  // This function computes for every nodes if any retrograde variable is
240  // present below
241  template < typename GUM_SCALAR,
242  template < typename >
243  class FUNCTOR,
244  template < typename >
245  class TerminalNodePolicy >
246  INLINE void
250  HashTable< NodeId, short int* >& dgInstNeed) {
251  HashTable< NodeId, short int* > nodesVarDescendant;
252  Size tableSize = Size(__nbVar * sizeof(short int));
253 
254  for (auto varIter = dg->variablesSequence().rbeginSafe();
255  varIter != dg->variablesSequence().rendSafe();
256  --varIter) {
257  Idx varPos = __rd->variablesSequence().pos(*varIter);
258  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
259  while (nodeIter != nullptr) {
260  short int* instantiationNeeded =
261  static_cast< short int* >(SOA_ALLOCATE(tableSize));
262  dgInstNeed.insert(nodeIter->element(), instantiationNeeded);
263 
264  short int* varDescendant =
265  static_cast< short int* >(SOA_ALLOCATE(tableSize));
266  nodesVarDescendant.insert(nodeIter->element(), varDescendant);
267  for (Idx j = 0; j < __nbVar; j++) {
268  instantiationNeeded[j] = (short int)0;
269  varDescendant[j] = (short int)0;
270  }
271 
272  varDescendant[varPos] = (short int)1;
273  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons();
274  ++modality) {
275  if (!dg->isTerminalNode(dg->node(nodeIter->element())->son(modality))) {
276  short int* sonVarDescendant =
277  nodesVarDescendant[dg->node(nodeIter->element())->son(modality)];
278  for (Idx varIdx = 0; varIdx < __nbVar; varIdx++) {
279  varDescendant[varIdx] += sonVarDescendant[varIdx];
280  if (varDescendant[varIdx] && varIdx < varPos)
281  instantiationNeeded[varIdx] = (short int)1;
282  }
283  }
284  }
285  nodeIter = nodeIter->nextLink();
286  }
287  }
288 
289  for (auto varIter = dg->variablesSequence().beginSafe();
290  varIter != dg->variablesSequence().endSafe();
291  ++varIter) {
292  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
293  while (nodeIter != nullptr) {
294  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons();
295  ++modality) {
296  NodeId sonId = dg->node(nodeIter->element())->son(modality);
297  if (!dg->isTerminalNode(sonId)) {
298  for (Idx varIdx = 0; varIdx < __nbVar; ++varIdx) {
299  if (dgInstNeed[nodeIter->element()][varIdx]
300  && nodesVarDescendant[sonId][varIdx]) {
301  dgInstNeed[sonId][varIdx] = (short int)1;
302  }
303  }
304  }
305  }
306  nodeIter = nodeIter->nextLink();
307  }
308  }
309 
310  for (HashTableIterator< NodeId, short int* > it = nodesVarDescendant.begin();
311  it != nodesVarDescendant.end();
312  ++it) {
313  SOA_DEALLOCATE(it.val(), tableSize);
314  }
315  nodesVarDescendant.clear();
316  }
317 
318 
321 
322  // A key is used for prunning uneccesary operations since once a node has been
323  // visited in a given context, there's no use to revisit him,
324  // the result will be the same node, so we just have to do an association
325  // context - node.
326  // The context consists in :
327  // _ Leader node we are visiting.
328  // _ Follower node we are visiting.
329  // _ For all retrograde variables, if it has been instanciated
330  // before, current modality instanciated, meaning :
331  // _ 0 means the variable hasn't be instanciated yet,
332  // _ From 1 to domainSize + 1 means that current modality
333  // index of variable is value - 1,
334  // _ domainSize + 2 means variable is on default mode.
335  // A key - node association is made each time we create a node in resulting
336  // diagram.
337  // Since GUM_MULTI_DIM_DECISION_DIAGRAM_RECUR_FUNCTION is a corner step in
338  // algorithm ( meaning each time we explore a node we go trought
339  // this function ), check only have to be at the beginning of that function.
340  template < typename GUM_SCALAR,
341  template < typename >
342  class FUNCTOR,
343  template < typename >
344  class TerminalNodePolicy >
346  __compute(O4DGContext& currentSituation, Idx lastInstVarPos) {
347  __nbCall += 1;
348 
349  NodeId newNode = 0;
350 
351 
352  // If both current nodes are terminal,
353  // we only have to compute the resulting value
354  if (__DG1->isTerminalNode(currentSituation.DG1Node())
355  && __DG2->isTerminalNode(currentSituation.DG2Node())) {
356  // We have to compute new valueand we insert a new node in diagram with
357  // this value, ...
358  return __rd->manager()->addTerminalNode(
359  __function(__DG1->terminalNodeValue(currentSituation.DG1Node()),
360  __DG2->terminalNodeValue(currentSituation.DG2Node())));
361  }
362 
363  // If not,
364  // we'll have to do some exploration
365 
366  // First we ensure that we hadn't already visit this pair of node under hte
367  // same circumstances
368 
369  short int* dg1NeededVar =
370  __DG1InstantiationNeeded.exists(currentSituation.DG1Node())
371  ? __DG1InstantiationNeeded[currentSituation.DG1Node()]
372  : __default;
373  Idx dg1CurrentVarPos =
374  __DG1->isTerminalNode(currentSituation.DG1Node())
375  ? __nbVar
376  : __rd->variablesSequence().pos(
377  __DG1->node(currentSituation.DG1Node())->nodeVar());
378  short int* dg2NeededVar =
379  __DG2InstantiationNeeded.exists(currentSituation.DG2Node())
380  ? __DG2InstantiationNeeded[currentSituation.DG2Node()]
381  : __default;
382  Idx dg2CurrentVarPos =
383  __DG2->isTerminalNode(currentSituation.DG2Node())
384  ? __nbVar
385  : __rd->variablesSequence().pos(
386  __DG2->node(currentSituation.DG2Node())->nodeVar());
387 
388  short int* instNeeded =
389  static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * __nbVar));
390  for (Idx i = 0; i < __nbVar; i++)
391  instNeeded[i] = dg1NeededVar[i] + dg2NeededVar[i];
392 
393  double curSitKey = currentSituation.key(instNeeded);
394 
395  if (__explorationTable.exists(curSitKey)) {
396  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
397  return __explorationTable[curSitKey];
398  }
399 
400  // ====================================================
401 
402  NodeId origDG1 = currentSituation.DG1Node(),
403  origDG2 = currentSituation.DG2Node();
404 
406  nullptr;
407  NodeId leadNodeId = 0;
408  Idx leadVarPos = __rd->variablesSequence().size();
409  typedef void (O4DGContext::*SetNodeFunction)(const NodeId&);
410  SetNodeFunction leadFunction = nullptr;
411 
412  bool sameVar = false;
413 
414  if (!__DG1->isTerminalNode(currentSituation.DG1Node())) {
415  if (currentSituation.varModality(dg1CurrentVarPos) != 0) {
416  currentSituation.setDG1Node(
417  __DG1->node(currentSituation.DG1Node())
418  ->son(currentSituation.varModality(dg1CurrentVarPos) - 1));
419 
420  newNode = __compute(currentSituation, lastInstVarPos);
421  __explorationTable.insert(curSitKey, newNode);
422  currentSituation.setDG1Node(origDG1);
423  currentSituation.setDG2Node(origDG2);
424 
425  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
426 
427  return newNode;
428  }
429 
430  leaddg = __DG1;
431  leadNodeId = currentSituation.DG1Node();
432  leadVarPos = dg1CurrentVarPos;
433  leadFunction = &O4DGContext::setDG1Node;
434  }
435 
436  if (!__DG2->isTerminalNode(currentSituation.DG2Node())) {
437  if (currentSituation.varModality(dg2CurrentVarPos) != 0) {
438  currentSituation.setDG2Node(
439  __DG2->node(currentSituation.DG2Node())
440  ->son(currentSituation.varModality(dg2CurrentVarPos) - 1));
441 
442  newNode = __compute(currentSituation, lastInstVarPos);
443  __explorationTable.insert(curSitKey, newNode);
444  currentSituation.setDG1Node(origDG1);
445  currentSituation.setDG2Node(origDG2);
446 
447  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
448 
449  return newNode;
450  }
451 
452  if (leadVarPos == dg2CurrentVarPos) { sameVar = true; }
453 
454  if (leadVarPos > dg2CurrentVarPos) {
455  leaddg = __DG2;
456  leadNodeId = currentSituation.DG2Node();
457  leadVarPos = dg2CurrentVarPos;
458  leadFunction = &O4DGContext::setDG2Node;
459  }
460  }
461 
462  // ====================================================
463 
464  // Before exploring nodes, we have to ensure that every anticipated
465  // exploration is done
466  for (Idx varPos = lastInstVarPos + 1; varPos < leadVarPos; ++varPos) {
467  if (instNeeded[varPos]) {
468  const DiscreteVariable* curVar = __rd->variablesSequence().atPos(varPos);
469  NodeId* sonsIds = static_cast< NodeId* >(
470  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
471 
472  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
473  currentSituation.chgVarModality(varPos, modality + 1);
474 
475  sonsIds[modality] = __compute(currentSituation, varPos);
476  }
477 
478  newNode = __rd->manager()->addInternalNode(curVar, sonsIds);
479 
480  __explorationTable.insert(curSitKey, newNode);
481  currentSituation.chgVarModality(varPos, 0);
482  currentSituation.setDG1Node(origDG1);
483  currentSituation.setDG2Node(origDG2);
484 
485  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
486 
487  return newNode;
488  }
489  }
490 
491  // ====================================================
492 
493  // If only one of the current node is terminal,
494  // we have to pursue deeper on the other diagram
495  if (sameVar) {
496  // If so - meaning it's the same variable - we have to go
497  // down on both
498  const InternalNode* dg1Node = __DG1->node(origDG1);
499  const InternalNode* dg2Node = __DG2->node(origDG2);
500 
501  const DiscreteVariable* curVar = dg1Node->nodeVar();
502  Idx varPos = __rd->variablesSequence().pos(curVar);
503 
504  NodeId* sonsIds = static_cast< NodeId* >(
505  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
506 
507  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
508  currentSituation.chgVarModality(varPos, modality + 1);
509  currentSituation.setDG1Node(dg1Node->son(modality));
510  currentSituation.setDG2Node(dg2Node->son(modality));
511 
512  sonsIds[modality] = __compute(currentSituation, varPos);
513  }
514 
515  newNode = __rd->manager()->addInternalNode(curVar, sonsIds);
516 
517  __explorationTable.insert(curSitKey, newNode);
518  currentSituation.chgVarModality(varPos, 0);
519  currentSituation.setDG1Node(origDG1);
520  currentSituation.setDG2Node(origDG2);
521 
522  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
523 
524  return newNode;
525  }
526  // ====================================================
527  else {
528  const InternalNode* leaddgNode = leaddg->node(leadNodeId);
529 
530  const DiscreteVariable* curVar = leaddgNode->nodeVar();
531  NodeId* sonsIds = static_cast< NodeId* >(
532  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
533 
534  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
535  currentSituation.chgVarModality(leadVarPos, modality + 1);
536  (currentSituation.*leadFunction)(leaddgNode->son(modality));
537 
538  sonsIds[modality] = __compute(currentSituation, leadVarPos);
539  }
540 
541  newNode = __rd->manager()->addInternalNode(curVar, sonsIds);
542 
543  __explorationTable.insert(curSitKey, newNode);
544  currentSituation.chgVarModality(leadVarPos, 0);
545  currentSituation.setDG1Node(origDG1);
546  currentSituation.setDG2Node(origDG2);
547 
548  SOA_DEALLOCATE(instNeeded, sizeof(short int) * __nbVar);
549 
550  return newNode;
551  }
552  }
553 
554  template < typename GUM_SCALAR,
555  template < typename >
556  class FUNCTOR,
557  template < typename >
558  class TerminalNodePolicy >
559  INLINE Idx MultiDimFunctionGraphOperator< GUM_SCALAR,
560  FUNCTOR,
561  TerminalNodePolicy >::nbCall() {
562  return __nbCall;
563  }
564 
565  template < typename GUM_SCALAR,
566  template < typename >
567  class FUNCTOR,
568  template < typename >
569  class TerminalNodePolicy >
570  INLINE Idx MultiDimFunctionGraphOperator< GUM_SCALAR,
571  FUNCTOR,
572  TerminalNodePolicy >::nbVarRetro() {
573  return __nbVarRetro;
574  }
575 
576  template < typename GUM_SCALAR,
577  template < typename >
578  class FUNCTOR,
579  template < typename >
580  class TerminalNodePolicy >
581  INLINE Idx
584  return __sizeVarRetro;
585  }
586 
587 } // namespace gum
iterator begin()
Returns an unsafe iterator pointing to the beginning of the hashtable.
const NodeId & DG2Node() const
Get DG2 diagram current explored Node.
Definition: o4DGContext.h:92
Safe iterators for Sequence.
Definition: sequence.h:1206
const iterator & end() noexcept
Returns the unsafe iterator pointing to the end of the hashtable.
const iterator_safe & endSafe() noexcept
Returns the safe iterator pointing to the end of the hashtable.
const DiscreteVariable * nodeVar() const
Returns the node variable.
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...
Unsafe Iterators for hashtablesHashTableIterator provides a fast but unsafe way to parse HashTables...
Definition: hashTable.h:2750
MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * compute()
Computes and builds the Function Graph that is the result of the operation.
MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * __rd
The resulting function graph.
#define SOA_DEALLOCATE(x, y)
NodeId son(Idx modality) const
Returns the son at a given index.
short int * __default
Just a comptuationnal trick.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
HashTable< NodeId, short int *> __DG1InstantiationNeeded
Table uses to know if a given node of first function graph has retrograde vrariables.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
const double & key(short int *instNeeded)
Returns o4DGContext key.
Idx __nbVar
The total number of variable implied in the operation.
void chgVarModality(Idx, Idx)
Changes given variable modality.
Base class for discrete random variable.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Definition: agrum.h:25
virtual Size domainSize() const =0
Idx __distance(const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *, const DiscreteVariable *, const DiscreteVariable *)
Heuristic methods to decide which of two retrograde variables should come first.
const NodeId & DG1Node() const
Get DG1 diagram current explored Node.
Definition: o4DGContext.h:86
HashTable< double, NodeId > __explorationTable
The hashtable used to know if two pair of nodes have already been visited.
void setDG1Node(const NodeId &)
Set DG1 diagram current explored Node.
HashTable< NodeId, short int *> __DG2InstantiationNeeded
Table uses to know if a given node of second function graph has retrograde vrariables.
Structure used to represent a node internal structure.
Definition: internalNode.h:102
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Class implementingting a function graph.
Class used to perform Function Graph Operations.
const FUNCTOR< GUM_SCALAR > __function
The function to be performed on the leaves.
void setDG2Node(const NodeId &)
Set DG2 diagram current explored Node.
const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * __DG1
One of the two function graphs used for the operation.
void clear()
Removes all the elements in the hash table.
iterator_safe beginSafe()
Returns the safe iterator pointing to the beginning of the hashtable.
MultiDimFunctionGraphOperator(const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *DG1, const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > *DG2)
Default constructor.
Size Idx
Type for indexes.
Definition: types.h:53
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:48
Class used to manipulate context during Function Graph Operations.
Definition: o4DGContext.h:49
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
void __establishVarOrder()
Computes an order for the final Decision graph that will minimize the number of re exploration...
Size NodeId
Type for node ids.
Definition: graphElements.h:98
Idx varModality(Idx)
Changes given variable modality.
NodeId __compute(O4DGContext &currentSituation, Idx lastInstVarPos)
The main recursion function.
const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy > * __DG2
The other one.
#define SOA_ALLOCATE(x)