aGrUM  0.16.0
incrementalTriangulation.cpp
Go to the documentation of this file.
1 
29 #include <limits>
30 #include <utility>
31 
32 #include <agrum/agrum.h>
33 #include <agrum/core/math/math.h>
34 
35 #include <agrum/core/list.h>
37 #include <agrum/graphs/undiGraph.h>
38 
39 #ifdef GUM_NO_INLINE
41 #endif // GUM_NO_INLINE
42 
43 #ifndef DOXYGEN_SHOULD_SKIP_THIS
44 
45 namespace gum {
46 
49  const UnconstrainedTriangulation& triang_algo,
50  const UndiGraph* theGraph,
51  const NodeProperty< Size >* domsizes) :
52  Triangulation(domsizes),
53  __triangulation(triang_algo.newFactory()) {
54  // for debugging purposes
55  GUM_CONSTRUCTOR(IncrementalTriangulation);
56 
57  // reset the triangulation algorithm => it starts with an empty graph
58  __triangulation->clear();
59 
60  // copy the graph passed in argument and update the structures
61  // containing the informations useful for the triangulation
62 
63  for (const auto node : *theGraph)
64  addNode(node, (*domsizes)[node]);
65 
66  // insert all the edges of the graph into the structure. This will
67  // implicitly update the "require_update" field
68  for (const auto& edge : theGraph->edges())
69  addEdge(edge.first(), edge.second());
70  }
71 
74  const UnconstrainedTriangulation& triang_algo) :
75  __triangulation(triang_algo.newFactory()) {
76  // for debugging purposes
77  GUM_CONSTRUCTOR(IncrementalTriangulation);
78 
79  // reset the triangulation algorithm => it starts with an empty graph
80  __triangulation->clear();
81  }
82 
85  const IncrementalTriangulation& from) :
86  Triangulation(from),
87  __graph(from.__graph), __junction_tree(from.__junction_tree),
88  __T_mpd(from.__T_mpd), __mps_of_node(from.__mps_of_node),
89  __cliques_of_mps(from.__cliques_of_mps),
90  __mps_of_clique(from.__mps_of_clique), __mps_affected(from.__mps_affected),
91  __triangulation(from.__triangulation->newFactory()),
92  __require_update(from.__require_update),
93  __require_elimination_order(from.__require_elimination_order),
94  __elimination_order(from.__elimination_order),
95  __reverse_elimination_order(from.__reverse_elimination_order),
96  __require_created_JT_cliques(from.__require_created_JT_cliques),
97  __created_JT_cliques(from.__created_JT_cliques) {
98  // for debugging purposes
99  GUM_CONS_CPY(IncrementalTriangulation);
100 
101  __domain_sizes = from.__domain_sizes;
102  }
103 
106  // for debugging purposes
107  GUM_DESTRUCTOR(IncrementalTriangulation);
108 
109  // remove things that were allocated within the current class
110  delete __triangulation;
111  }
112 
114  IncrementalTriangulation* IncrementalTriangulation::newFactory() const {
115  return new IncrementalTriangulation(*__triangulation);
116  }
117 
119  IncrementalTriangulation* IncrementalTriangulation::copyFactory() const {
120  return new IncrementalTriangulation(*this);
121  }
122 
124  IncrementalTriangulation& IncrementalTriangulation::
125  operator=(const IncrementalTriangulation& from) {
126  // avoid self assignment
127  if (this != &from) {
128  // for debugging purposes
129  GUM_OP_CPY(IncrementalTriangulation);
130 
131  // copy all the structures stored in "from"
132  __graph = from.__graph;
133  __domain_sizes = from.__domain_sizes;
134  __junction_tree = from.__junction_tree;
135  __T_mpd = from.__T_mpd;
136  __mps_of_node = from.__mps_of_node;
137  __cliques_of_mps = from.__cliques_of_mps;
138  __mps_of_clique = from.__mps_of_clique;
139  __mps_affected = from.__mps_affected;
140  __require_update = from.__require_update;
141  __require_elimination_order = from.__require_elimination_order;
142  __elimination_order = from.__elimination_order;
143  __reverse_elimination_order = from.__reverse_elimination_order;
144  __require_created_JT_cliques = from.__require_created_JT_cliques;
145  __created_JT_cliques = from.__created_JT_cliques;
146 
147  // just in case we changed the triangulation algorithm, we remove it
148  // and create it again
149  delete __triangulation;
150  __triangulation = from.__triangulation->newFactory();
151  }
152 
153  return *this;
154  }
155 
157  void IncrementalTriangulation::addNode(const NodeId node, Size modal) {
158  // check if the node already exists
159  if (__graph.existsNode(node)) return;
160 
161  // add the new node to the graph
162  __graph.addNodeWithId(node);
163  __domain_sizes.insert(node, modal);
164 
165  // add a new clique to T_mpd and the junction tree
166  NodeSet clique_nodes(2);
167  clique_nodes.insert(node);
168 
169  NodeId MPS = __T_mpd.addNode(clique_nodes);
170  NodeId new_clique = __junction_tree.addNode(clique_nodes);
171 
172  // indicate in which MPS node belongs
173  List< NodeId >& list_of_mps =
174  __mps_of_node.insert(node, List< NodeId >()).second;
175  list_of_mps.insert(MPS);
176 
177  // indicate in which MPS the clique added to the junction tree belongs
178  std::vector< NodeId >& cliquesMPS =
179  __cliques_of_mps.insert(MPS, std::vector< NodeId >()).second;
180 
181  cliquesMPS.push_back(new_clique);
182  __mps_of_clique.insert(new_clique, MPS);
183 
184  // indicate that the new MPS should not be affected by a triangulation
185  __mps_affected.insert(MPS, false);
186 
187  // insert the node into the elimination order sequence
188  __elimination_order.push_back(node);
189 
190  if (!__reverse_elimination_order.exists(node))
191  __reverse_elimination_order.insert(node, Size(__elimination_order.size()));
192 
193  if (!__created_JT_cliques.exists(node))
194  __created_JT_cliques.insert(node, new_clique);
195  }
196 
198 
200  const NodeId Mz,
201  const Edge& edge) {
202  // mark the MPS so that it will be retriangulated
203  __mps_affected[My] = true;
204 
205  // mark all the neighbour MPS that contain edge
206  for (const auto nei : __T_mpd.neighbours(My))
207  if (nei != Mz) {
208  const NodeSet& Syk = __T_mpd.separator(Edge(nei, My));
209 
210  if (Syk.contains(edge.first()) && Syk.contains(edge.second()))
211  __markAffectedMPSsByRemoveLink(nei, My, edge);
212  }
213  }
214 
216 
217  void IncrementalTriangulation::eraseEdge(const Edge& edge) {
218  // check that the edge exist
219  if (!__graph.existsEdge(edge)) return;
220 
221  // find a MPS containing the edge (X,Y)
222  const NodeId X = edge.first();
223  const NodeId Y = edge.second();
224 
225  const List< NodeId >& mps1 = __mps_of_node[X];
226  const List< NodeId >& mps2 = __mps_of_node[Y];
227 
228  NodeId Mx = mps1[0];
229 
230  if (mps1.size() <= mps2.size()) {
231  for (const auto node : mps1)
232  if (__T_mpd.clique(node).contains(Y)) {
233  Mx = node;
234  break;
235  }
236  } else {
237  for (const auto node : mps2)
238  if (__T_mpd.clique(node).contains(X)) {
239  Mx = node;
240  break;
241  }
242  }
243 
244  // mark the MPS that need be updated
245  __markAffectedMPSsByRemoveLink(Mx, Mx, edge);
246 
247  __require_update = true;
248 
249  __require_elimination_order = true;
250 
251  __require_created_JT_cliques = true;
252 
253  // remove the edge (X,Y) from the graph
254  __graph.eraseEdge(edge);
255  }
256 
259 
261  // check if the node exists
262  if (!__graph.existsNode(X)) return;
263 
264  // remove all the edges adjacent to the node
265  {
266  const NodeSet& neighbours = __graph.neighbours(X);
267 
268  for (auto neighbour_edge =
269  neighbours.beginSafe(); // safe iterator needed here
270  neighbour_edge != neighbours.endSafe();
271  ++neighbour_edge) {
272  eraseEdge(Edge(*neighbour_edge, X));
273  }
274  }
275 
276  auto& MPS_of_X = __mps_of_node[X];
277 
278  // remove X from the MPS containing X
279  for (const auto node : MPS_of_X) {
280  __T_mpd.eraseFromClique(node, X);
281 
282  // if the intersection between *iter and one of its neighbour is empty,
283  // remove the edge linking them
284  auto& neighbours = __T_mpd.neighbours(node);
285 
286  for (auto it_neighbour = neighbours.beginSafe();
287  it_neighbour != neighbours.endSafe();
288  ++it_neighbour) { // safe iterator needed here
289  Edge neigh(*it_neighbour, node);
290 
291  if (__T_mpd.separator(neigh).size() == 0) __T_mpd.eraseEdge(neigh);
292  }
293  }
294 
295  // remove X from the cliques containing X
296  for (const auto clique : MPS_of_X) {
297  const std::vector< NodeId >& cliques_of_X = __cliques_of_mps[clique];
298 
299  for (unsigned int i = 0; i < cliques_of_X.size(); ++i) {
300  __junction_tree.eraseFromClique(cliques_of_X[i], X);
301 
302  // if the intersection between clique and one of its neighbour is empty,
303  // remove the edge linking them only if, in addition, there is no
304  // edge in __graph between a node of clique and a node in the neighbour
305  auto& neighbours = __junction_tree.neighbours(cliques_of_X[i]);
306 
307  for (auto it_neighbour = neighbours.beginSafe();
308  it_neighbour != neighbours.endSafe();
309  ++it_neighbour) { // safe iterator needed here
310  Edge neigh(*it_neighbour, cliques_of_X[i]);
311 
312  if (__junction_tree.separator(neigh).size() == 0) {
313  // try to see if there is an edge between the nodes of one extremity
314  // of *neighbour and those of the other extremity
315  bool hasCommonEdge = false;
316 
317  for (const auto node1 : __junction_tree.clique(neigh.first()))
318  for (const auto node2 : __junction_tree.clique(neigh.second()))
319  if (__graph.existsEdge(node1, node2)) {
320  hasCommonEdge = true;
321  break;
322  }
323 
324  if (!hasCommonEdge) { __junction_tree.eraseEdge(neigh); }
325  }
326  }
327  }
328  }
329 
330  // if the MPS containing X is empty, then remove it, as well as the
331  // corresponding clique in the junction tree (which also only contains X)
332  if ((MPS_of_X.size() == 1) && (__T_mpd.clique(MPS_of_X[0]).size() == 0)) {
333  __junction_tree.eraseNode(__cliques_of_mps[MPS_of_X[0]][0]);
334  __T_mpd.eraseNode(MPS_of_X[0]);
335  __mps_of_clique.erase(__cliques_of_mps[MPS_of_X[0]][0]);
336  __cliques_of_mps.erase(MPS_of_X[0]);
337  __mps_affected.erase(MPS_of_X[0]);
338  }
339 
340  __mps_of_node.erase(X);
341 
342  // update the elimination orders
343 
344  if (!__require_update) {
345  for (Idx i = __reverse_elimination_order[X] + 1;
346  i < __reverse_elimination_order.size();
347  ++i)
348  __elimination_order[i - 1] = __elimination_order[i];
349 
350  __elimination_order.pop_back();
351 
352  __reverse_elimination_order.erase(X);
353 
354  __created_JT_cliques.erase(X);
355  }
356 
357  // remove X completely from the graph
358  __graph.eraseNode(X);
359 
360  __domain_sizes.erase(X);
361  }
362 
364 
366  const NodeId Mz,
367  const NodeId X,
368  const NodeId Y) {
369  // check if Mx contains Y. In this case, mark Mx and return 1 to indicate
370  // that
371  // Y has been found or 2 to indicate that Y has been found and that the
372  // nearest
373  // MPS containing X has been marked
374  const NodeSet& cliqueMX = __T_mpd.clique(Mx);
375 
376  if (cliqueMX.contains(Y)) {
377  __mps_affected[Mx] = true;
378 
379  if (cliqueMX.contains(X)) return 2;
380 
381  return 1;
382  }
383 
384  // parse Mx's neighbours until we find Y
385  for (const auto other_node : __T_mpd.neighbours(Mx))
386  if (other_node != Mz) {
387  int neighbourStatus = __markAffectedMPSsByAddLink(other_node, Mx, X, Y);
388 
389  if (neighbourStatus == 2)
390  return 2;
391  else if (neighbourStatus == 1) {
392  __mps_affected[Mx] = true;
393 
394  if (cliqueMX.contains(X)) return 2;
395 
396  return 1;
397  }
398  }
399 
400  // indicate that X was not found
401  return 0;
402  }
403 
406  void IncrementalTriangulation::addEdge(const NodeId X, const NodeId Y) {
407  // check that the edge exist
408  if ((X == Y) || !__graph.existsNode(X) || !__graph.existsNode(Y)
409  || __graph.existsEdge(Edge(X, Y)))
410  return;
411 
412  // add the edge to the graph
413  __graph.addEdge(X, Y);
414 
415  // take any MPS containing X and search its tree to find Y
416  NodeId& mps_X = __mps_of_node[X][0];
417 
418  int found = __markAffectedMPSsByAddLink(mps_X, mps_X, X, Y);
419 
420  if (found == 0) {
421  // the mps containing X do not belong to the same tree as those containing
422  // Y
423  NodeId& mps_Y = __mps_of_node[Y][0];
424 
425  // find a clique in mps_X containing X and another in mps_Y containing Y
426  // and add a clique XY to the junction tree linked to the cliques found
427  // in mps_X and mps_Y
428  const std::vector< NodeId >& cliques_X = __cliques_of_mps[mps_X];
429  const std::vector< NodeId >& cliques_Y = __cliques_of_mps[mps_Y];
430  NodeId c_X = 0, c_Y = 0;
431 
432  for (unsigned int i = 0; i < cliques_X.size(); ++i) {
433  if (__junction_tree.clique(cliques_X[i]).contains(X)) {
434  c_X = cliques_X[i];
435  break;
436  }
437  }
438 
439  for (unsigned int i = 0; i < cliques_Y.size(); ++i) {
440  if (__junction_tree.clique(cliques_Y[i]).contains(Y)) {
441  c_Y = cliques_Y[i];
442  break;
443  }
444  }
445 
446  // link c_Y and c_X through a new node containing XY
447  NodeSet nodes(2);
448 
449  nodes.insert(X);
450 
451  nodes.insert(Y);
452 
453  NodeId newNode = __junction_tree.addNode(nodes);
454 
455  __junction_tree.addEdge(newNode, c_X);
456  __junction_tree.addEdge(newNode, c_Y);
457 
458  NodeId newMPS = __T_mpd.addNode(nodes);
459 
460  __T_mpd.addEdge(newMPS, mps_X);
461  __T_mpd.addEdge(newMPS, mps_Y);
462 
463  // check that the maximal prime subgraph containing X is not X alone
464  // in this case, remove this max prime subgraph, as well as the
465  // corresponding
466  // clique in the junction tree
467  if (__T_mpd.clique(mps_X).size() == 1) {
468  __junction_tree.eraseNode(c_X);
469  __T_mpd.eraseNode(mps_X);
470  __mps_affected.erase(mps_X);
471  __mps_of_clique.erase(c_X);
472  __cliques_of_mps.erase(mps_X);
473  __created_JT_cliques[X] = newNode;
474  mps_X = newMPS;
475  } else
476  __mps_of_node[X].insert(newMPS);
477 
478  // do the same thing as above for node Y
479  if (__T_mpd.clique(mps_Y).size() == 1) {
480  __junction_tree.eraseNode(c_Y);
481  __T_mpd.eraseNode(mps_Y);
482  __mps_affected.erase(mps_Y);
483  __mps_of_clique.erase(c_Y);
484  __cliques_of_mps.erase(mps_Y);
485  __created_JT_cliques[Y] = newNode;
486  mps_Y = newMPS;
487  } else
488  __mps_of_node[Y].insert(newMPS);
489 
490  std::vector< NodeId >& cl =
491  __cliques_of_mps.insert(newMPS, std::vector< NodeId >()).second;
492 
493  cl.push_back(newNode);
494 
495  __mps_of_clique.insert(newNode, newMPS);
496 
497  __mps_affected.insert(newMPS, false);
498  } else {
499  __require_update = true;
500  __require_created_JT_cliques = true;
501  }
502 
503  // in all cases, recompute the elimination ordering
504  __require_elimination_order = true;
505  }
506 
508 
510  // just in case, update everything
511  updateTriangulation();
512 
513  bool OK = true;
514 
515  // check that all the nodes of the graph belong to the junction tree
516  {
517  NodeProperty< bool > nodesProp = __graph.nodesProperty< bool >(false);
518 
519  for (const auto cliq : __junction_tree.nodes())
520  for (const auto node : __junction_tree.clique(cliq))
521  nodesProp[node] = true;
522 
523  for (const auto& elt : nodesProp)
524  if (!elt.second) {
525  std::cerr << "check nodes" << std::endl
526  << __graph << std::endl
527  << __junction_tree << std::endl;
528  OK = false;
529  }
530 
531  if (!OK) return false;
532  }
533 
534  // check that the edgs belong to the junction tree
535  {
536  std::pair< NodeId, NodeId > thePair;
537  EdgeProperty< bool > edgesProp = __graph.edgesProperty(false);
538 
539  for (const auto cliq : __junction_tree.nodes()) {
540  const NodeSet& clique = __junction_tree.clique(cliq);
541 
542  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
543  auto iter3 = iter2;
544 
545  for (++iter3; iter3 != clique.end(); ++iter3) {
546  thePair.first = std::min(*iter2, *iter3);
547  thePair.second = std::max(*iter2, *iter3);
548 
549  if (__graph.existsEdge(thePair.first, thePair.second))
550  edgesProp[Edge(thePair.first, thePair.second)] = true;
551  }
552  }
553  }
554 
555  for (const auto& elt : edgesProp)
556  if (!elt.second) {
557  std::cerr << "check edges" << std::endl
558  << __graph << std::endl
559  << __junction_tree << std::endl;
560  OK = false;
561  }
562 
563  if (!OK) return false;
564  }
565 
566  // check that all the nodes of the graph belong to the MPS tree
567  {
568  NodeProperty< bool > nodesProp = __graph.nodesProperty< bool >(false);
569 
570  for (const auto cliq : __T_mpd.nodes())
571  for (const auto node : __T_mpd.clique(cliq))
572  nodesProp[node] = true;
573 
574  for (const auto& elt : nodesProp)
575  if (!elt.second) {
576  std::cerr << "check nodes" << std::endl
577  << __graph << std::endl
578  << __T_mpd << std::endl;
579  OK = false;
580  }
581 
582  if (!OK) return false;
583  }
584 
585  // check that the arcs of the graph belong to the MPS tree
586  {
587  std::pair< NodeId, NodeId > thePair;
588  EdgeProperty< bool > edgesProp = __graph.edgesProperty(false);
589 
590  for (const auto cliq : __T_mpd.nodes()) {
591  const NodeSet& clique = __T_mpd.clique(cliq);
592 
593  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
594  auto iter3 = iter2;
595 
596  for (++iter3; iter3 != clique.end(); ++iter3) {
597  thePair.first = std::min(*iter2, *iter3);
598  thePair.second = std::max(*iter2, *iter3);
599 
600  if (__graph.existsEdge(thePair.first, thePair.second))
601  edgesProp[Edge(thePair.first, thePair.second)] = true;
602  }
603  }
604  }
605 
606  for (const auto& elt : edgesProp)
607  if (!elt.second) {
608  std::cerr << "check edges" << std::endl
609  << __graph << std::endl
610  << __T_mpd << std::endl;
611  OK = false;
612  }
613 
614  if (!OK) return false;
615  }
616 
617  // check the MPS of node
618  {
619  NodeProperty< NodeProperty< bool > > chk;
620 
621  for (const auto node : __graph.nodes())
622  chk.insert(node, HashTable< NodeId, bool >());
623 
624  for (const auto cliq : __T_mpd.nodes())
625  for (auto node : __T_mpd.clique(cliq))
626  chk[node].insert(cliq, false);
627 
628  for (const auto& elt : __mps_of_node) {
629  HashTable< NodeId, bool >& hash = chk[elt.first];
630 
631  for (const auto cell : elt.second) {
632  if (!hash.exists(cell)) {
633  std::cerr << "check mps of nodes" << std::endl
634  << __T_mpd << std::endl
635  << __mps_of_node << std::endl;
636  OK = false;
637  }
638 
639  hash[cell] = true;
640  }
641  }
642 
643  for (const auto& elt : chk)
644  for (const auto& elt2 : elt.second)
645  if (!elt2.second) {
646  std::cerr << "check mps of nodes2" << std::endl
647  << __T_mpd << std::endl
648  << __mps_of_node << std::endl;
649  OK = false;
650  }
651 
652  if (!OK) return false;
653  }
654 
655  // check that the junction tree and the T_mpd are junction trees
656  {
657  if (!__junction_tree.isJoinTree()) {
658  std::cerr << "check join tree __junction_tree" << std::endl
659  << __junction_tree << std::endl;
660  return false;
661  }
662 
663  if (!__T_mpd.isJoinTree()) {
664  std::cerr << "check join tree __T_mpd" << std::endl
665  << __T_mpd << std::endl;
666  return false;
667  }
668  }
669 
670  // check elimination sequences
671  {
672  eliminationOrder();
673 
674  if (__elimination_order.size() != __graph.size()) {
675  std::cerr << "check elimination order" << std::endl
676  << __elimination_order << std::endl;
677  return false;
678  }
679 
680  NodeSet nodes;
681 
682  for (const auto node : __graph.nodes()) {
683  if (nodes.exists(node)) {
684  std::cerr << "check elimination order" << std::endl
685  << __elimination_order << std::endl;
686  return false;
687  } else
688  nodes.insert(node);
689  }
690 
691  if (nodes.size() != __graph.size()) {
692  std::cerr << "check elimination order" << std::endl
693  << __elimination_order << std::endl;
694  return false;
695  }
696 
697  if (__reverse_elimination_order.size() != __graph.size()) {
698  std::cerr << "check reverse elimination order" << std::endl
699  << __reverse_elimination_order << std::endl;
700  return false;
701  }
702 
703  for (const auto node : __graph.nodes()) {
704  if (!__reverse_elimination_order.exists(node)) {
705  std::cerr << "check reverse elimination order" << std::endl
706  << __reverse_elimination_order << std::endl;
707  return false;
708  }
709  }
710  }
711 
712  // check created junction tree cliques
713  {
714  createdJunctionTreeCliques();
715 
716  if (__created_JT_cliques.size() != __graph.size()) {
717  std::cerr << "check creating JT cliques" << std::endl
718  << __created_JT_cliques << std::endl;
719  return false;
720  }
721 
722  for (const auto node : __graph.nodes()) {
723  if (!__created_JT_cliques.exists(node)
724  || !__junction_tree.existsNode(__created_JT_cliques[node])) {
725  std::cerr << "check created JT cliques" << std::endl
726  << __created_JT_cliques << std::endl;
727  return false;
728  }
729  }
730  }
731 
732  return true;
733  }
734 
736 
738  NodeId Mx,
739  NodeId Mfrom,
740  UndiGraph& theGraph,
741  std::vector< Edge >& notAffectedneighbourCliques,
742  HashTable< NodeId, bool >& cliques_affected) {
743  // mark the clique so that we won't try to update it several times
744  cliques_affected[Mx] = false;
745 
746  // get the nodes that are concerned by the triangulation update
747  for (const auto node : __junction_tree.clique(Mx))
748  if (!theGraph.exists(node)) theGraph.addNodeWithId(node);
749 
750  // go on with the neighbour cliques in the junction tree
751  for (const auto othernode : __junction_tree.neighbours(Mx))
752  if (othernode != Mfrom) {
753  if (cliques_affected.exists(othernode)) {
754  __setUpConnectedTriangulation(othernode,
755  Mx,
756  theGraph,
757  notAffectedneighbourCliques,
758  cliques_affected);
759  } else {
760  // indicate that we have a clique not affected that is adjacent
761  // to an affected one
762  notAffectedneighbourCliques.push_back(Edge(othernode, Mx));
763  }
764  }
765  }
766 
768 
770  NodeProperty< bool >& all_cliques_affected,
771  NodeSet& new_nodes_in_junction_tree) {
772  // a temporary subgraph in which we actually perform the triangulation
773  UndiGraph tmp_graph;
774 
775  // for each triangulation, we will keep track of the cliques of the
776  // junction tree that are not affected by the triangulation but that are
777  // adjacent to cliques affected. This will enable us to connect easily the
778  // newly created cliques with the old ones.
779  std::vector< Edge > notAffectedneighbourCliques;
780 
781  // parse all the affected MPS and get the corresponding cliques
782 
783  for (const auto& elt : __mps_affected)
784  if (elt.second) {
785  // get the cliques contained in this MPS
786  const std::vector< NodeId >& cliques = __cliques_of_mps[elt.first];
787 
788  for (unsigned int i = 0; i < cliques.size(); ++i)
789  all_cliques_affected.insert(cliques[i], true);
790  }
791 
792  // for each connected set of cliques involved in the triangulations
793  // perform a new triangulation and update the max prime subgraph tree
794  for (const auto& elt : all_cliques_affected) {
795  if (elt.second) {
796  // set up the connected subgraph that need be retriangulated and the
797  // cliques that are affected by this triangulation
798  tmp_graph.clear();
799  notAffectedneighbourCliques.clear();
800  __setUpConnectedTriangulation(elt.first,
801  elt.first,
802  tmp_graph,
803  notAffectedneighbourCliques,
804  all_cliques_affected);
805 
806  // insert the edges in tmp_graph
807  for (auto edge : __graph.edges()) {
808  try {
809  tmp_graph.addEdge(edge.first(), edge.second());
810  } catch (Exception&) {} // both extremities must be in tmp_graph
811  }
812 
813  // remove from the mps_of_node table the affected mps containing the
814  // node
815  // for ( UndiGraph::NodeIterator iter_node =
816  // tmp_graph.beginNodes();iter_node
817  // != tmp_graph.endNodes(); ++iter_node ) {
818  for (const auto node : tmp_graph.nodes()) {
819  List< NodeId >& mps = __mps_of_node[node];
820 
822  __mps_affected.beginSafe(); // safe iterator needed here
823  iter_mps != __mps_affected.endSafe();
824  ++iter_mps) {
825  if (iter_mps.val()) mps.eraseByVal(iter_mps.key());
826  }
827  }
828 
829  // now tmp_graph contains the graph that should be triangulated.
830  // so triangulate it and get its junction tree
831  __triangulation->setGraph(&tmp_graph, &__domain_sizes);
832 
833  const CliqueGraph& tmp_junction_tree = __triangulation->junctionTree();
834 
835  // now, update the global junction tree
836  // first add the nodes of tmp_junction_tree to __junction_tree
837  // to do so, store the translations of the node ids of tmp_junction_tree
838  // into the node ids of __junction_tree
839  NodeProperty< NodeId > tmp2global_junction_tree(tmp_junction_tree.size());
840 
841  for (const auto cliq : tmp_junction_tree.nodes()) {
842  // get new ids for the nodes of tmp_junction_tree. These should be
843  // greater than or equal to __junction_tree.bound () so that we can
844  // use the max_old_id defined at the beginning of the method.
845  NodeId new_id = __junction_tree.addNode(tmp_junction_tree.clique(cliq));
846  // translate the id of the temprary JT into an id of the global JT
847  tmp2global_junction_tree.insert(cliq, new_id);
848  new_nodes_in_junction_tree.insert(new_id);
849  }
850 
851  // and add the edges of tmp_junction_tree to __junction_tree
852  for (const auto edge : tmp_junction_tree.edges())
853  __junction_tree.addEdge(tmp2global_junction_tree[edge.first()],
854  tmp2global_junction_tree[edge.second()]);
855 
856  // second get the edges in __junction_tree that have an extremal clique
857  // R
858  // in the affected clique set and the other one S not in the affected
859  // set
860  // and see which new node V in the __junction_tree should be connected
861  // to S. The running intersection property guarrantees that the clique
862  // in
863  // the tmp_junction_tree that results from the elimination (during the
864  // triangulation process) of the first eliminated node in the separator
865  // between R and S is an admissible candidate
866  for (unsigned int i = 0; i < notAffectedneighbourCliques.size(); ++i) {
867  // check that the separator is not empty. If this is the case, do not
868  // link the new junction tree to the old one
869  const NodeSet& sep =
870  __junction_tree.separator(notAffectedneighbourCliques[i]);
871 
872  if (sep.size() != 0) {
873  // now find the first eliminated node in the separator
874  Size __elim_order = tmp_graph.bound() + 1;
875  NodeId elim_node = 0;
876 
877  for (const auto id : sep) {
878  Size new_order = __triangulation->eliminationOrder(id);
879 
880  if (new_order < __elim_order) {
881  __elim_order = new_order;
882  elim_node = id;
883  }
884  }
885 
886  // find the __junction_tree clique corresponding to the elimination
887  // of
888  // elim_node and insert an edge between this clique and that which
889  // was
890  // not affected
891  NodeId& to_connect =
892  tmp2global_junction_tree[__triangulation->createdJunctionTreeClique(
893  elim_node)];
894 
895  NodeId not_affected =
896  all_cliques_affected.exists(notAffectedneighbourCliques[i].first())
897  ? notAffectedneighbourCliques[i].second()
898  : notAffectedneighbourCliques[i].first();
899 
900  __junction_tree.addEdge(not_affected, to_connect);
901 
902  if (!new_nodes_in_junction_tree.contains(to_connect)) {
903  __T_mpd.addEdge(__mps_of_clique[to_connect],
904  __mps_of_clique[not_affected]);
905  }
906 
907  // check that the separator created by the new edge is not equal to
908  // to_connect. If this is the case, then to_connect is included in
909  // not_affected and, hence, should be removed from the graph
910  if (__junction_tree.separator(not_affected, to_connect).size()
911  == __junction_tree.clique(to_connect).size()) {
912  __junction_tree.eraseEdge(Edge(not_affected, to_connect));
913 
914  for (const auto neighbour : __junction_tree.neighbours(to_connect)) {
915  __junction_tree.addEdge(neighbour, not_affected);
916 
917  if (!new_nodes_in_junction_tree.contains(neighbour))
918  __T_mpd.addEdge(__mps_of_clique[neighbour],
919  __mps_of_clique[not_affected]);
920  }
921 
922  __junction_tree.eraseNode(to_connect);
923 
924  to_connect = not_affected;
925  }
926  }
927  }
928  }
929  }
930 
931  // remove the mps that were affected and update the cliques_of_mps table
932  for (const auto& elt : all_cliques_affected) {
933  __mps_of_clique.erase(elt.first);
934  __junction_tree.eraseNode(elt.first);
935  }
936 
937  for (const auto& elt : __mps_affected)
938  if (elt.second) {
939  __cliques_of_mps.erase(elt.first);
940  __T_mpd.eraseNode(elt.first);
941  }
942  }
943 
945 
947  const NodeId node,
948  const NodeId from,
949  std::vector< std::pair< NodeId, NodeId > >& merged_cliques,
951  const NodeSet& new_nodes_in_junction_tree) const {
952  mark[node] = true;
953 
954  // check the separators on all the adjacent edges of Mx
955  for (const auto other_node : __junction_tree.neighbours(node))
956  if (other_node != from) {
957  const NodeSet& separator =
958  __junction_tree.separator(Edge(other_node, node));
959 
960  // check that the separator between node and other_node is complete
961  bool complete = true;
962 
963  for (auto iter_sep1 = separator.begin();
964  iter_sep1 != separator.end() && complete;
965  ++iter_sep1) {
966  auto iter_sep2 = iter_sep1;
967 
968  for (++iter_sep2; iter_sep2 != separator.end(); ++iter_sep2) {
969  if (!__graph.existsEdge(*iter_sep1, *iter_sep2)) {
970  complete = false;
971  break;
972  }
973  }
974  }
975 
976  // here complete indicates whether the separator is complete or not
977  if (!complete)
978  merged_cliques.push_back(std::pair< NodeId, NodeId >(other_node, node));
979 
980  if (new_nodes_in_junction_tree.contains(other_node))
981  __computeMaxPrimeMergings(
982  other_node, node, merged_cliques, mark, new_nodes_in_junction_tree);
983  }
984  }
985 
987 
989  NodeProperty< bool >& all_cliques_affected,
990  const NodeSet& new_nodes_in_junction_tree) {
991  // the maximal prime subgraph join tree is created by aggregation of some
992  // cliques. More precisely, when the separator between 2 cliques is not
993  // complete in the original graph, then the two cliques must be merged.
994 
995  // Create a hashtable indicating which clique has been absorbed by some
996  // other
997  // clique. Keys are the cliques absorbed, and values are the cliques that
998  // absorb them.
999  HashTable< NodeId, NodeId > T_mpd_cliques(all_cliques_affected.size());
1000 
1001  for (const auto clik : __junction_tree.nodes())
1002  if (new_nodes_in_junction_tree.contains(clik))
1003  T_mpd_cliques.insert(clik, clik);
1004 
1005  // parse all the separators of the junction tree and test those that are not
1006  // complete in the original graph
1007  std::vector< std::pair< NodeId, NodeId > > merged_cliques;
1008 
1009  HashTable< NodeId, bool > mark = T_mpd_cliques.map(false);
1010 
1011  for (const auto& elt : mark)
1012  if (!elt.second)
1013  __computeMaxPrimeMergings(
1014  elt.first, elt.first, merged_cliques, mark, new_nodes_in_junction_tree);
1015 
1016  // compute the transitive closure of merged_cliques. This one will contain
1017  // pairs (X,Y) indicating that clique X must be merged with clique Y.
1018  // Actually clique X will be inserted into clique Y.
1019  for (unsigned int i = 0; i < merged_cliques.size(); ++i) {
1020  if (T_mpd_cliques.exists(merged_cliques[i].second))
1021  T_mpd_cliques[merged_cliques[i].first] =
1022  T_mpd_cliques[merged_cliques[i].second];
1023  else
1024  T_mpd_cliques[merged_cliques[i].first] =
1025  __mps_of_clique[merged_cliques[i].second];
1026  }
1027 
1028  // now we can create the max prime junction tree.
1029 
1030  // create a map translating the cliques' ids in the junction tree into
1031  // cliques' id in the _T_mpd tree
1032  NodeProperty< NodeId > clique2MPS(T_mpd_cliques.size());
1033 
1034  // First, create the new cliques and create the corresponding
1035  // cliques_of_mps entries
1036  for (const auto& elt : T_mpd_cliques)
1037  if (elt.first == elt.second) {
1038  NodeId newId = __T_mpd.addNode(__junction_tree.clique(elt.second));
1039  clique2MPS.insert(elt.second, newId);
1040  std::vector< NodeId >& vect_of_cliques =
1041  __cliques_of_mps.insert(newId, std::vector< NodeId >()).second;
1042  vect_of_cliques.push_back(elt.second);
1043  }
1044 
1045  // add to the cliques previously created the nodes of the cliques that were
1046  // merged into them and update the cliques_of_mps
1047  for (const auto& elt : T_mpd_cliques)
1048  if ((elt.first != elt.second)
1049  && (new_nodes_in_junction_tree.contains(elt.second))) {
1050  const NodeId idMPS = clique2MPS[elt.second];
1051 
1052  for (const auto node : __junction_tree.clique(elt.first)) {
1053  try {
1054  __T_mpd.addToClique(idMPS, node);
1055  } catch (DuplicateElement&) {}
1056  }
1057 
1058  __cliques_of_mps[idMPS].push_back(elt.first);
1059  }
1060 
1061  // update the mps_of_node and the mps_of_clique
1062  for (const auto& elt : T_mpd_cliques) {
1063  const NodeId idMPS = clique2MPS[elt.second];
1064  __mps_of_clique.insert(elt.first, idMPS);
1065 
1066  if (elt.first == elt.second)
1067  for (const auto node : __T_mpd.clique(idMPS))
1068  __mps_of_node[node].insert(idMPS);
1069  }
1070 
1071  // add the edges to the max prime subgraph tree
1072  for (const auto& elt : T_mpd_cliques) {
1073  NodeId clique = clique2MPS[elt.second];
1074 
1075  for (const auto othernode : __junction_tree.neighbours(elt.first))
1076  if (T_mpd_cliques.exists(othernode)) {
1077  // here iter is linked to another node that has been created during
1078  // the triangulation
1079  NodeId otherClique = clique2MPS[T_mpd_cliques[othernode]];
1080 
1081  // avoid adding the same edge several times
1082  if (clique > otherClique) { __T_mpd.addEdge(clique, otherClique); }
1083  } else {
1084  __T_mpd.addEdge(clique, __mps_of_clique[othernode]);
1085  }
1086  }
1087  }
1088 
1090 
1092  if (!__require_update) return;
1093  // the set of all the cliques that should be affected by the different
1094  // triangulations we will perform (one by connected component)
1095  NodeProperty< bool > all_cliques_affected(__junction_tree.size());
1096 
1097  // we need to keep track of the new node ids that will be inserted
1098  // into __junction_tree. A priori, these should be equal to the ids
1099  // inserted into tmp2global_junction_tree. But, sometimes, some new nodes
1100  // are included into old nodes and, in this case, the translation in
1101  // tmp2global_junction_tree indicates the the new node inserted corresponds
1102  // to an old node. Here we wish to know this additional information
1103  NodeSet new_nodes_in_junction_tree;
1104 
1105  __updateJunctionTree(all_cliques_affected, new_nodes_in_junction_tree);
1106 
1107  // now update the T_mpd so that it be coherent with the junction tree
1108  __updateMaxPrimeSubgraph(all_cliques_affected, new_nodes_in_junction_tree);
1109 
1110  // reset the MPS that are affected
1111  __mps_affected.clear();
1112 
1113  for (const auto node : __T_mpd.nodes())
1114  __mps_affected.insert(node, false);
1115 
1116  // remove all the structures used by the triangulation algorithm
1117  __triangulation->clear();
1118 
1119  __require_update = false;
1120  }
1121 
1123 
1125  __graph.clear();
1126  __domain_sizes.clear();
1127  __junction_tree.clear();
1128  __T_mpd.clear();
1129  __mps_of_node.clear();
1130  __cliques_of_mps.clear();
1131  __mps_of_clique.clear();
1132  __mps_affected.clear();
1133  __triangulation->clear();
1134  __require_update = false;
1135  __require_elimination_order = false;
1136  __elimination_order.clear();
1137  __reverse_elimination_order.clear();
1138  __require_created_JT_cliques = false;
1139  __created_JT_cliques.clear();
1140  }
1141 
1143 
1145  const NodeId clique, const NodeId from, NodeProperty< bool >& examined) {
1146  // apply collect to all the neighbours except from
1147  for (const auto otherclique : __junction_tree.neighbours(clique))
1148  if (otherclique != from) __collectJTCliques(otherclique, clique, examined);
1149 
1150  // get the nodes that belong to clique and not to from
1151  examined[clique] = true;
1152 
1153  const NodeSet& cliquenodes = __junction_tree.clique(clique);
1154 
1155  if (from != clique) {
1156  const NodeSet& separator = __junction_tree.separator(clique, from);
1157 
1158  for (const auto cli : cliquenodes)
1159  if (!separator.contains(cli)) __created_JT_cliques.insert(cli, clique);
1160  } else {
1161  for (const auto cli : cliquenodes)
1162  __created_JT_cliques.insert(cli, clique);
1163  }
1164  }
1165 
1169  const NodeProperty< NodeId >&
1171  // check if we already computed the containing cliques
1172  if (!__require_created_JT_cliques) return __created_JT_cliques;
1173 
1174  // we first we compute the junction tree
1175  updateTriangulation();
1176 
1177  __created_JT_cliques.clear();
1178 
1179  __require_created_JT_cliques = false;
1180 
1181  if (__junction_tree.size() == 0) { return __created_JT_cliques; }
1182 
1183  // now we can use a collect algorithm to get the containing cliques
1184  NodeProperty< bool > examined = __junction_tree.nodesProperty< bool >(false);
1185 
1186  for (const auto& elt : examined)
1187  if (!elt.second) __collectJTCliques(elt.first, elt.first, examined);
1188 
1189  return __created_JT_cliques;
1190  }
1191 
1193 
1195  createdJunctionTreeCliques();
1196  return __created_JT_cliques[id];
1197  }
1198 
1203  // get the created junction tree clique and get its MPS
1204  return __mps_of_clique[createdJunctionTreeClique(id)];
1205  }
1206 
1208 
1209  void IncrementalTriangulation::setGraph(const UndiGraph* graph,
1210  const NodeProperty< Size >* dom_sizes) {
1211  // check that both the graph and the domain sizes are different from nullptr
1212  // or else that both are equal to nullptr
1213  if (((graph != nullptr) && (dom_sizes == nullptr))
1214  || ((graph == nullptr) && (dom_sizes != nullptr))) {
1215  GUM_ERROR(GraphError,
1216  "one of the graph or the set of domain sizes "
1217  "is a null pointer.");
1218  }
1219 
1220  // remove the current graph
1221  clear();
1222 
1223  // copy the graph passed in arent and update the structures
1224  // containing the informations useful for the triangulation
1225  if (graph != nullptr) {
1226  for (const auto node : *graph)
1227  addNode(node, (*dom_sizes)[node]);
1228 
1229  for (const auto& edge : graph->edges())
1230  addEdge(edge.first(), edge.second());
1231  }
1232  }
1233 
1235 
1237  const NodeId node,
1238  const NodeId from,
1239  NodeProperty< bool >& examined,
1240  Idx& index) {
1241  // apply collect to all the neighbours except from
1242  for (const auto othernode : __junction_tree.neighbours(node))
1243  if (othernode != from)
1244  __collectEliminationOrder(othernode, node, examined, index);
1245 
1246  // get the nodes that belong to node and not to from
1247  examined[node] = true;
1248 
1249  const NodeSet& clique = __junction_tree.clique(node);
1250 
1251  if (from != node) {
1252  const NodeSet& separator = __junction_tree.separator(node, from);
1253 
1254  for (const auto cli : clique) {
1255  if (!separator.contains(cli)) {
1256  __elimination_order[index] = cli;
1257  __reverse_elimination_order.insert(cli, index);
1258  ++index;
1259  }
1260  }
1261  } else {
1262  for (const auto cli : clique) {
1263  __elimination_order[index] = cli;
1264  __reverse_elimination_order.insert(cli, index);
1265  ++index;
1266  }
1267  }
1268  }
1269 
1271 
1272  const std::vector< NodeId >& IncrementalTriangulation::eliminationOrder() {
1273  // check if we already computed the elimination order
1274  if (!__require_elimination_order) return __elimination_order;
1275 
1276  // to compute the elimination order, we first we compute the junction tree
1277  updateTriangulation();
1278 
1279  __elimination_order.resize(__graph.size());
1280 
1281  __reverse_elimination_order.clear();
1282 
1283  __require_elimination_order = false;
1284 
1285  if (__junction_tree.size() == Size(0)) { return __elimination_order; }
1286 
1287  // now we can use a collect algorithm to get the elimination order
1288  Idx index = Idx(0);
1289 
1290  NodeProperty< bool > examined = __junction_tree.nodesProperty< bool >(false);
1291 
1292  for (const auto& elt : examined)
1293  if (!elt.second)
1294  __collectEliminationOrder(elt.first, elt.first, examined, index);
1295 
1296  return __elimination_order;
1297  }
1298 
1303  if (!__graph.existsNode(node)) {
1304  GUM_ERROR(NotFound, "the node " << node << " does not exist");
1305  }
1306 
1307  // compute the elimination order
1308  eliminationOrder();
1309 
1310  return __reverse_elimination_order[node];
1311  }
1312 
1313 } /* namespace gum */
1314 
1315 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
void __collectJTCliques(const NodeId clique, const NodeId from, NodeProperty< bool > &examined)
a collect algorithm to compute, for each node, one container JT&#39;s clique
void eraseEdge(const Edge &edge)
removes an edge from the graph (the join tree may need a retriangulation)
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
virtual void clear()
clears the sequence (to prepare, for instance, a new elimination sequence)
~IncrementalTriangulation()
destructor
void __markAffectedMPSsByRemoveLink(const NodeId My, const NodeId Mz, const Edge &edge)
mark the mps affected by the deletion of a given edge
void setGraph(const UndiGraph *theGraph, const NodeProperty< Size > *domain_sizes)
changes the current graph
void eraseNode(const NodeId node)
removes a node from the graph (the join tree may need a triangulation update)
void __updateMaxPrimeSubgraph(NodeProperty< bool > &cliques_affected, const NodeSet &new_nodes_in_junction_tree)
update the max prime subgraph
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
virtual IncrementalTriangulation * newFactory() const final
virtual clone constructor
const NodeProperty< NodeId > & createdJunctionTreeCliques()
returns the Ids of the cliques of the junction tree created by the elimination of the nodes ...
IncrementalTriangulation(const UnconstrainedTriangulation &triang_algo, const UndiGraph *theGraph, const NodeProperty< Size > *modal)
constructor
int __markAffectedMPSsByAddLink(const NodeId My, const NodeId Mz, const NodeId X, const NodeId Y)
mark the mps affected by the insertion of a new edge
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
IncrementalTriangulation & operator=(const IncrementalTriangulation &from)
copy operator
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Definition: agrum.h:25
UndiGraph * graph() const noexcept
returns the current graph
bool __check()
checks that the incremental triangulation works properly
void updateTriangulation()
updates the triangulated graph using the modif list
NodeId createdMaxPrimeSubgraph(const NodeId id)
returns the Id of the maximal prime subgraph created by the elimination of a given node during the tr...
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
void __setUpConnectedTriangulation(NodeId Mx, NodeId Mfrom, UndiGraph &theGraph, std::vector< Edge > &notAffectedneighborClique, HashTable< NodeId, bool > &cliques_affected)
set-up the connected subgraph that needs be retriangulated
const std::vector< NodeId > & eliminationOrder()
returns an elimination ordering compatible with the triangulated graph
void addNode(const NodeId node, Size modal)
adds a new node to the graph
NodeId createdJunctionTreeClique(const NodeId id)
returns the Id of the clique created by the elimination of a given node during the triangulation proc...
void __updateJunctionTree(NodeProperty< bool > &all_cliques_affected, NodeSet &new_nodes_in_junction_tree)
update the junction tree
void addEdge(const NodeId X, const NodeId Y)
adds a new edge to the graph (the join tree may need a triangulation update)
virtual UnconstrainedEliminationSequenceStrategy * newFactory() const =0
creates a new elimination sequence of the same type as the current object, but this sequence contains...
void __collectEliminationOrder(const NodeId node, const NodeId from, NodeProperty< bool > &examined, Idx &index)
a collect algorithm to compute elimination orderings
Copyright 2005-2019 Pierre-Henri WUILLEMIN et Christophe GONZALES (LIP6) {prenom.nom}_at_lip6.fr.
Size Idx
Type for indexes.
Definition: types.h:53
void clear()
sets the graph to the empty graph
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:48
void __computeMaxPrimeMergings(const NodeId node, const NodeId from, std::vector< std::pair< NodeId, NodeId > > &merged_cliques, NodeProperty< bool > &mark, const NodeSet &new_nodes_in_junction_tree) const
used for computing the junction tree of the maximal prime subgraphs
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Size NodeId
Type for node ids.
Definition: graphElements.h:98
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:613
#define GUM_ERROR(type, msg)
Definition: exceptions.h:55
HashTable< Key, Mount, OtherAlloc > map(Mount(*f)(Val), Size size=Size(0), bool resize_pol=HashTableConst::default_resize_policy, bool key_uniqueness_pol=HashTableConst::default_uniqueness_policy) const
Transforms a hashtable of vals into a hashtable of mountains.
virtual IncrementalTriangulation * copyFactory() const final
virtual copy constructor