aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
multiDimFunctionGraphOperator_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief Class used to compute the operation between two decision diagrams
25  *
26  * @author Pierre-Henri WUILLEMIN(@LIP6) and Jean-Christophe MAGNAN and Christophe
27  * GONZALES(@AMU)
28  */
29 
30 #include <agrum/tools/multidim/utils/FunctionGraphUtilities/internalNode.h>
31 #include <agrum/tools/multidim/utils/FunctionGraphUtilities/operators/multiDimFunctionGraphOperator.h>
32 
33 namespace gum {
34 
35  template < typename GUM_SCALAR,
36  template < typename >
37  class FUNCTOR,
38  template < typename >
39  class TerminalNodePolicy >
40  MultiDimFunctionGraphOperator< GUM_SCALAR, FUNCTOR, TerminalNodePolicy >::
41  MultiDimFunctionGraphOperator(
42  const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy >* DG1,
43  const MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy >* DG2) :
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__ = MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy >::
50  getReducedAndOrderedInstance();
51  nbVar__ = 0;
52  default__ = nullptr;
53 
54  nbCall__ = 0;
55  nbVar__ = 0;
56  sizeVarRetro__ = 1;
57  }
58 
59  template < typename GUM_SCALAR,
60  template < typename >
61  class FUNCTOR,
62  template < typename >
63  class TerminalNodePolicy >
67 
68 
71  ++instIter)
72  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * nbVar__);
73 
76  ++instIter)
77  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * nbVar__);
78 
79  if (nbVar__ != 0) SOA_DEALLOCATE(default__, sizeof(short int) * nbVar__);
80  }
81 
82 
83  // This function is the main function. To be call every time an operation
84  // between the two given Function Graphs is required
85  template < typename GUM_SCALAR,
86  template < typename >
87  class FUNCTOR,
88  template < typename >
89  class TerminalNodePolicy >
92  compute() {
96 
97  Idx* varInst = nullptr;
98  if (nbVar__ != 0) {
99  varInst = static_cast< Idx* >(SOA_ALLOCATE(sizeof(Idx) * nbVar__));
100  for (Idx i = 0; i < nbVar__; i++)
101  varInst[i] = (Idx)0;
102  }
103 
107 
108  NodeId root = compute__(conti, (Idx)0 - 1);
110 
111  if (nbVar__ != 0) SOA_DEALLOCATE(varInst, sizeof(Idx) * nbVar__);
112 
113  return rd__;
114  }
115 
116  // This function computes an efficient order for the final decision diagrams.
117  // Its main criterion to do so is the number of re-exploration to be done.
118  template < typename GUM_SCALAR,
119  template < typename >
120  class FUNCTOR,
121  template < typename >
122  class TerminalNodePolicy >
129 
130  while (fite != DG1__->variablesSequence().endSafe()
131  && site != DG2__->variablesSequence().endSafe()) {
132  // Test : if var from first order is already in final order
133  // we move onto the next one
134  if (rd__->variablesSequence().exists(*fite)) {
135  ++fite;
136  continue;
137  }
138 
139  // Test : if var from second order is already in final order
140  // we move onto the next one
141  if (rd__->variablesSequence().exists(*site)) {
142  ++site;
143  continue;
144  }
145 
146  // Test : is current var of the first order present in the second order.
147  // if not we add it to final order
148  if (!DG2__->variablesSequence().exists(*fite)) {
149  rd__->add(**fite);
150  ++fite;
151  continue;
152  }
153 
154  // Test : is current var of the second order present in the first order.
155  // if not we add it to final order
156  if (!DG1__->variablesSequence().exists(*site)) {
157  rd__->add(**site);
158  ++site;
159  continue;
160  }
161 
162  // Test : is current var of the second order present in the first order.
163  // if not we add it to final order
164  if (*fite == *site) {
165  rd__->add(**fite);
166  ++fite;
167  ++site;
168  continue;
169  }
170 
171  // Test : the current tested situation is when two retrograde variables
172  // are detected.
173  // Chosen solution here is to find compute domainSize in between
174  // and chose the one with the smallest
175  nbVarRetro__++;
176  if (distance__(DG1__, *fite, *site) < distance__(DG2__, *site, *fite)) {
177  rd__->add(**fite);
178  sizeVarRetro__ *= (*fite)->domainSize();
179  ++fite;
180  continue;
181  } else {
182  rd__->add(**site);
183  sizeVarRetro__ *= (*site)->domainSize();
184  ++site;
185  continue;
186  }
187  }
188 
189  // Whenever an iterator has finished its sequence,
190  // the other may still be in the middle of its one.
191  // Hence, this part ensures that any variables remaining
192  // will be added to the final sequence if needed.
193  if (fite == DG1__->variablesSequence().endSafe()) {
194  for (; site != DG2__->variablesSequence().endSafe(); ++site)
195  if (!rd__->variablesSequence().exists(*site)) rd__->add(**site);
196  } else {
197  for (; fite != DG1__->variablesSequence().endSafe(); ++fite)
198  if (!rd__->variablesSequence().exists(*fite)) rd__->add(**fite);
199  }
200 
201 
202  // Various initialization needed now that we have a bigger picture
204 
205  if (nbVar__ != 0) {
206  default__
207  = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * nbVar__));
208  for (Idx i = 0; i < nbVar__; i++)
209  default__[i] = (short int)0;
210  }
211  }
212 
213  // This function computes the number of re-exploration needed whenever to
214  // retrograde variables collides
215  template < typename GUM_SCALAR,
216  template < typename >
217  class FUNCTOR,
218  template < typename >
219  class TerminalNodePolicy >
220  INLINE Idx
224  const DiscreteVariable* from,
225  const DiscreteVariable* to) {
227  Idx dist = 1;
228 
229  while (d->variablesSequence().atPos(posi) != to) {
231  posi++;
232  }
233 
234  return dist;
235  }
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 >
242  class FUNCTOR,
243  template < typename >
244  class TerminalNodePolicy >
245  INLINE void
249  HashTable< NodeId, short int* >& dgInstNeed) {
250  HashTable< NodeId, short int* > nodesVarDescendant;
251  Size tableSize = Size(nbVar__ * sizeof(short int));
252 
253  for (auto varIter = dg->variablesSequence().rbeginSafe();
255  --varIter) {
257  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
258  while (nodeIter != nullptr) {
259  short int* instantiationNeeded
260  = static_cast< short int* >(SOA_ALLOCATE(tableSize));
262 
263  short int* varDescendant
264  = static_cast< short int* >(SOA_ALLOCATE(tableSize));
266  for (Idx j = 0; j < nbVar__; j++) {
267  instantiationNeeded[j] = (short int)0;
268  varDescendant[j] = (short int)0;
269  }
270 
271  varDescendant[varPos] = (short int)1;
272  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons();
273  ++modality) {
275  short int* sonVarDescendant
277  for (Idx varIdx = 0; varIdx < nbVar__; varIdx++) {
279  if (varDescendant[varIdx] && varIdx < varPos)
280  instantiationNeeded[varIdx] = (short int)1;
281  }
282  }
283  }
285  }
286  }
287 
288  for (auto varIter = dg->variablesSequence().beginSafe();
290  ++varIter) {
291  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
292  while (nodeIter != nullptr) {
293  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons();
294  ++modality) {
296  if (!dg->isTerminalNode(sonId)) {
297  for (Idx varIdx = 0; varIdx < nbVar__; ++varIdx) {
300  dgInstNeed[sonId][varIdx] = (short int)1;
301  }
302  }
303  }
304  }
306  }
307  }
308 
309  for (HashTableIterator< NodeId, short int* > it = nodesVarDescendant.begin();
311  ++it) {
313  }
315  }
316 
317 
318  /// Main recursion function, called every time we move on a node to determine
319  /// what we have to do
320 
321  // A key is used for prunning uneccesary operations since once a node has been
322  // visited in a given context, there's no use to revisit him,
323  // the result will be the same node, so we just have to do an association
324  // context - node.
325  // The context consists in :
326  // _ Leader node we are visiting.
327  // _ Follower node we are visiting.
328  // _ For all retrograde variables, if it has been instanciated
329  // before, current modality instanciated, meaning :
330  // _ 0 means the variable hasn't be instanciated yet,
331  // _ From 1 to domainSize + 1 means that current modality
332  // index of variable is value - 1,
333  // _ domainSize + 2 means variable is on default mode.
334  // A key - node association is made each time we create a node in resulting
335  // diagram.
336  // Since GUM_MULTI_DIM_DECISION_DIAGRAM_RECUR_FUNCTION is a corner step in
337  // algorithm ( meaning each time we explore a node we go trought
338  // this function ), check only have to be at the beginning of that function.
339  template < typename GUM_SCALAR,
340  template < typename >
341  class FUNCTOR,
342  template < typename >
343  class TerminalNodePolicy >
346  nbCall__ += 1;
347 
348  NodeId newNode = 0;
349 
350 
351  // If both current nodes are terminal,
352  // we only have to compute the resulting value
355  // We have to compute new valueand we insert a new node in diagram with
356  // this value, ...
357  return rd__->manager()->addTerminalNode(
360  }
361 
362  // If not,
363  // we'll have to do some exploration
364 
365  // First we ensure that we hadn't already visit this pair of node under hte
366  // same circumstances
367 
368  short int* dg1NeededVar
371  : default__;
373  ? nbVar__
376  short int* dg2NeededVar
379  : default__;
381  ? nbVar__
384 
385  short int* instNeeded
386  = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * nbVar__));
387  for (Idx i = 0; i < nbVar__; i++)
389 
391 
393  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
395  }
396 
397  // ====================================================
398 
401 
403  = nullptr;
404  NodeId leadNodeId = 0;
406  typedef void (O4DGContext::*SetNodeFunction)(const NodeId&);
407  SetNodeFunction leadFunction = nullptr;
408 
409  bool sameVar = false;
410 
416 
421 
422  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
423 
424  return newNode;
425  }
426 
427  leaddg = DG1__;
431  }
432 
438 
443 
444  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
445 
446  return newNode;
447  }
448 
449  if (leadVarPos == dg2CurrentVarPos) { sameVar = true; }
450 
452  leaddg = DG2__;
456  }
457  }
458 
459  // ====================================================
460 
461  // Before exploring nodes, we have to ensure that every anticipated
462  // exploration is done
463  for (Idx varPos = lastInstVarPos + 1; varPos < leadVarPos; ++varPos) {
464  if (instNeeded[varPos]) {
466  NodeId* sonsIds = static_cast< NodeId* >(
467  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
468 
469  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
471 
473  }
474 
476 
481 
482  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
483 
484  return newNode;
485  }
486  }
487 
488  // ====================================================
489 
490  // If only one of the current node is terminal,
491  // we have to pursue deeper on the other diagram
492  if (sameVar) {
493  // If so - meaning it's the same variable - we have to go
494  // down on both
497 
500 
501  NodeId* sonsIds = static_cast< NodeId* >(
502  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
503 
504  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
508 
510  }
511 
513 
518 
519  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
520 
521  return newNode;
522  }
523  // ====================================================
524  else {
526 
528  NodeId* sonsIds = static_cast< NodeId* >(
529  SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
530 
531  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
534 
536  }
537 
539 
544 
545  SOA_DEALLOCATE(instNeeded, sizeof(short int) * nbVar__);
546 
547  return newNode;
548  }
549  }
550 
551  template < typename GUM_SCALAR,
552  template < typename >
553  class FUNCTOR,
554  template < typename >
555  class TerminalNodePolicy >
557  FUNCTOR,
559  return nbCall__;
560  }
561 
562  template < typename GUM_SCALAR,
563  template < typename >
564  class FUNCTOR,
565  template < typename >
566  class TerminalNodePolicy >
568  FUNCTOR,
570  return nbVarRetro__;
571  }
572 
573  template < typename GUM_SCALAR,
574  template < typename >
575  class FUNCTOR,
576  template < typename >
577  class TerminalNodePolicy >
578  INLINE Idx
581  return sizeVarRetro__;
582  }
583 
584 } // namespace gum
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669