aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
multiDimFunctionGraphOperator_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by 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_(), _DG1InstantiationNeeded_(DG1->realSize(), true, false),
46  _DG2InstantiationNeeded_(DG2->realSize(), true, false) {
47  GUM_CONSTRUCTOR(MultiDimFunctionGraphOperator);
48  _rd_ = MultiDimFunctionGraph< GUM_SCALAR, TerminalNodePolicy >::getReducedAndOrderedInstance();
49  _nbVar_ = 0;
50  _default_ = nullptr;
51 
52  _nbCall_ = 0;
53  _nbVar_ = 0;
54  _sizeVarRetro_ = 1;
55  }
56 
57  template < typename GUM_SCALAR,
58  template < typename >
59  class FUNCTOR,
60  template < typename >
61  class TerminalNodePolicy >
65 
66 
69  ++instIter)
70  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * _nbVar_);
71 
74  ++instIter)
75  SOA_DEALLOCATE(instIter.val(), sizeof(short int) * _nbVar_);
76 
77  if (_nbVar_ != 0) SOA_DEALLOCATE(_default_, sizeof(short int) * _nbVar_);
78  }
79 
80 
81  // This function is the main function. To be call every time an operation
82  // between the two given Function Graphs is required
83  template < typename GUM_SCALAR,
84  template < typename >
85  class FUNCTOR,
86  template < typename >
87  class TerminalNodePolicy >
93 
94  Idx* varInst = nullptr;
95  if (_nbVar_ != 0) {
96  varInst = static_cast< Idx* >(SOA_ALLOCATE(sizeof(Idx) * _nbVar_));
97  for (Idx i = 0; i < _nbVar_; i++)
98  varInst[i] = (Idx)0;
99  }
100 
104 
105  NodeId root = _compute_(conti, (Idx)0 - 1);
107 
108  if (_nbVar_ != 0) SOA_DEALLOCATE(varInst, sizeof(Idx) * _nbVar_);
109 
110  return _rd_;
111  }
112 
113  // This function computes an efficient order for the final decision diagrams.
114  // Its main criterion to do so is the number of re-exploration to be done.
115  template < typename GUM_SCALAR,
116  template < typename >
117  class FUNCTOR,
118  template < typename >
119  class TerminalNodePolicy >
124 
125  while (fite != _DG1_->variablesSequence().endSafe()
126  && site != _DG2_->variablesSequence().endSafe()) {
127  // Test : if var from first order is already in final order
128  // we move onto the next one
129  if (_rd_->variablesSequence().exists(*fite)) {
130  ++fite;
131  continue;
132  }
133 
134  // Test : if var from second order is already in final order
135  // we move onto the next one
136  if (_rd_->variablesSequence().exists(*site)) {
137  ++site;
138  continue;
139  }
140 
141  // Test : is current var of the first order present in the second order.
142  // if not we add it to final order
143  if (!_DG2_->variablesSequence().exists(*fite)) {
144  _rd_->add(**fite);
145  ++fite;
146  continue;
147  }
148 
149  // Test : is current var of the second order present in the first order.
150  // if not we add it to final order
151  if (!_DG1_->variablesSequence().exists(*site)) {
152  _rd_->add(**site);
153  ++site;
154  continue;
155  }
156 
157  // Test : is current var of the second order present in the first order.
158  // if not we add it to final order
159  if (*fite == *site) {
160  _rd_->add(**fite);
161  ++fite;
162  ++site;
163  continue;
164  }
165 
166  // Test : the current tested situation is when two retrograde variables
167  // are detected.
168  // Chosen solution here is to find compute domainSize in between
169  // and chose the one with the smallest
170  _nbVarRetro_++;
171  if (_distance_(_DG1_, *fite, *site) < _distance_(_DG2_, *site, *fite)) {
172  _rd_->add(**fite);
173  _sizeVarRetro_ *= (*fite)->domainSize();
174  ++fite;
175  continue;
176  } else {
177  _rd_->add(**site);
178  _sizeVarRetro_ *= (*site)->domainSize();
179  ++site;
180  continue;
181  }
182  }
183 
184  // Whenever an iterator has finished its sequence,
185  // the other may still be in the middle of its one.
186  // Hence, this part ensures that any variables remaining
187  // will be added to the final sequence if needed.
188  if (fite == _DG1_->variablesSequence().endSafe()) {
189  for (; site != _DG2_->variablesSequence().endSafe(); ++site)
190  if (!_rd_->variablesSequence().exists(*site)) _rd_->add(**site);
191  } else {
192  for (; fite != _DG1_->variablesSequence().endSafe(); ++fite)
193  if (!_rd_->variablesSequence().exists(*fite)) _rd_->add(**fite);
194  }
195 
196 
197  // Various initialization needed now that we have a bigger picture
199 
200  if (_nbVar_ != 0) {
201  _default_ = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * _nbVar_));
202  for (Idx i = 0; i < _nbVar_; i++)
203  _default_[i] = (short int)0;
204  }
205  }
206 
207  // This function computes the number of re-exploration needed whenever to
208  // retrograde variables collides
209  template < typename GUM_SCALAR,
210  template < typename >
211  class FUNCTOR,
212  template < typename >
213  class TerminalNodePolicy >
216  const DiscreteVariable* from,
217  const DiscreteVariable* to) {
219  Idx dist = 1;
220 
221  while (d->variablesSequence().atPos(posi) != to) {
223  posi++;
224  }
225 
226  return dist;
227  }
228 
229 
230  // This function computes for every nodes if any retrograde variable is
231  // present below
232  template < typename GUM_SCALAR,
233  template < typename >
234  class FUNCTOR,
235  template < typename >
236  class TerminalNodePolicy >
239  HashTable< NodeId, short int* >& dgInstNeed) {
240  HashTable< NodeId, short int* > nodesVarDescendant;
241  Size tableSize = Size(_nbVar_ * sizeof(short int));
242 
243  for (auto varIter = dg->variablesSequence().rbeginSafe();
245  --varIter) {
247  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
248  while (nodeIter != nullptr) {
249  short int* instantiationNeeded = static_cast< short int* >(SOA_ALLOCATE(tableSize));
251 
252  short int* varDescendant = static_cast< short int* >(SOA_ALLOCATE(tableSize));
254  for (Idx j = 0; j < _nbVar_; j++) {
255  instantiationNeeded[j] = (short int)0;
256  varDescendant[j] = (short int)0;
257  }
258 
259  varDescendant[varPos] = (short int)1;
260  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons(); ++modality) {
262  short int* sonVarDescendant
264  for (Idx varIdx = 0; varIdx < _nbVar_; varIdx++) {
266  if (varDescendant[varIdx] && varIdx < varPos)
267  instantiationNeeded[varIdx] = (short int)1;
268  }
269  }
270  }
272  }
273  }
274 
275  for (auto varIter = dg->variablesSequence().beginSafe();
277  ++varIter) {
278  const Link< NodeId >* nodeIter = dg->varNodeListe(*varIter)->list();
279  while (nodeIter != nullptr) {
280  for (Idx modality = 0; modality < dg->node(nodeIter->element())->nbSons(); ++modality) {
282  if (!dg->isTerminalNode(sonId)) {
283  for (Idx varIdx = 0; varIdx < _nbVar_; ++varIdx) {
285  dgInstNeed[sonId][varIdx] = (short int)1;
286  }
287  }
288  }
289  }
291  }
292  }
293 
294  for (HashTableIterator< NodeId, short int* > it = nodesVarDescendant.begin();
296  ++it) {
298  }
300  }
301 
302 
303  /// Main recursion function, called every time we move on a node to determine
304  /// what we have to do
305 
306  // A key is used for prunning uneccesary operations since once a node has been
307  // visited in a given context, there's no use to revisit him,
308  // the result will be the same node, so we just have to do an association
309  // context - node.
310  // The context consists in :
311  // _ Leader node we are visiting.
312  // _ Follower node we are visiting.
313  // _ For all retrograde variables, if it has been instanciated
314  // before, current modality instanciated, meaning :
315  // _ 0 means the variable hasn't be instanciated yet,
316  // _ From 1 to domainSize + 1 means that current modality
317  // index of variable is value - 1,
318  // _ domainSize + 2 means variable is on default mode.
319  // A key - node association is made each time we create a node in resulting
320  // diagram.
321  // Since GUM_MULTI_DIM_DECISION_DIAGRAM_RECUR_FUNCTION is a corner step in
322  // algorithm ( meaning each time we explore a node we go trought
323  // this function ), check only have to be at the beginning of that function.
324  template < typename GUM_SCALAR,
325  template < typename >
326  class FUNCTOR,
327  template < typename >
328  class TerminalNodePolicy >
332  _nbCall_ += 1;
333 
334  NodeId newNode = 0;
335 
336 
337  // If both current nodes are terminal,
338  // we only have to compute the resulting value
341  // We have to compute new valueand we insert a new node in diagram with
342  // this value, ...
343  return _rd_->manager()->addTerminalNode(
346  }
347 
348  // If not,
349  // we'll have to do some exploration
350 
351  // First we ensure that we hadn't already visit this pair of node under hte
352  // same circumstances
353 
356  : _default_;
359  ? _nbVar_
363  : _default_;
366  ? _nbVar_
368 
369  short int* instNeeded = static_cast< short int* >(SOA_ALLOCATE(sizeof(short int) * _nbVar_));
370  for (Idx i = 0; i < _nbVar_; i++)
372 
374 
376  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
378  }
379 
380  // ====================================================
381 
383 
385  NodeId leadNodeId = 0;
387  typedef void (O4DGContext::*SetNodeFunction)(const NodeId&);
388  SetNodeFunction leadFunction = nullptr;
389 
390  bool sameVar = false;
391 
396 
401 
402  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
403 
404  return newNode;
405  }
406 
407  leaddg = _DG1_;
411  }
412 
417 
422 
423  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
424 
425  return newNode;
426  }
427 
428  if (leadVarPos == dg2CurrentVarPos) { sameVar = true; }
429 
431  leaddg = _DG2_;
435  }
436  }
437 
438  // ====================================================
439 
440  // Before exploring nodes, we have to ensure that every anticipated
441  // exploration is done
442  for (Idx varPos = lastInstVarPos + 1; varPos < leadVarPos; ++varPos) {
443  if (instNeeded[varPos]) {
445  NodeId* sonsIds
446  = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
447 
448  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
450 
452  }
453 
455 
460 
461  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
462 
463  return newNode;
464  }
465  }
466 
467  // ====================================================
468 
469  // If only one of the current node is terminal,
470  // we have to pursue deeper on the other diagram
471  if (sameVar) {
472  // If so - meaning it's the same variable - we have to go
473  // down on both
476 
479 
480  NodeId* sonsIds = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
481 
482  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
486 
488  }
489 
491 
496 
497  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
498 
499  return newNode;
500  }
501  // ====================================================
502  else {
504 
506  NodeId* sonsIds = static_cast< NodeId* >(SOA_ALLOCATE(sizeof(NodeId) * curVar->domainSize()));
507 
508  for (Idx modality = 0; modality < curVar->domainSize(); modality++) {
511 
513  }
514 
516 
521 
522  SOA_DEALLOCATE(instNeeded, sizeof(short int) * _nbVar_);
523 
524  return newNode;
525  }
526  }
527 
528  template < typename GUM_SCALAR,
529  template < typename >
530  class FUNCTOR,
531  template < typename >
532  class TerminalNodePolicy >
534  return _nbCall_;
535  }
536 
537  template < typename GUM_SCALAR,
538  template < typename >
539  class FUNCTOR,
540  template < typename >
541  class TerminalNodePolicy >
542  INLINE Idx
544  return _nbVarRetro_;
545  }
546 
547  template < typename GUM_SCALAR,
548  template < typename >
549  class FUNCTOR,
550  template < typename >
551  class TerminalNodePolicy >
554  return _sizeVarRetro_;
555  }
556 
557 } // namespace gum
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643