aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
incrementalTriangulation.cpp
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 /** @file
23  * @brief source implementations for computing default triangulations of graphs
24  *
25  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26  */
27 
28 #include <limits>
29 #include <utility>
30 
31 #include <agrum/agrum.h>
32 #include <agrum/tools/core/math/math_utils.h>
33 
34 #include <agrum/tools/core/list.h>
35 #include <agrum/tools/graphs/algorithms/triangulations/incrementalTriangulation.h>
36 #include <agrum/tools/graphs/undiGraph.h>
37 
38 #ifdef GUM_NO_INLINE
39 # include <agrum/tools/graphs/algorithms/triangulations/incrementalTriangulation_inl.h>
40 #endif // GUM_NO_INLINE
41 
42 #ifndef DOXYGEN_SHOULD_SKIP_THIS
43 
44 namespace gum {
45 
46  /// constructor
49  const UndiGraph* theGraph,
50  const NodeProperty< Size >* domsizes) :
53  // for debugging purposes
55 
56  // reset the triangulation algorithm => it starts with an empty graph
58 
59  // copy the graph passed in argument and update the structures
60  // containing the informations useful for the triangulation
61 
62  for (const auto node: *theGraph)
63  addNode(node, (*domsizes)[node]);
64 
65  // insert all the edges of the graph into the structure. This will
66  // implicitly update the "require_update" field
67  for (const auto& edge: theGraph->edges())
69  }
70 
71  /// default constructor
75  // for debugging purposes
77 
78  // reset the triangulation algorithm => it starts with an empty graph
80  }
81 
82  /// copy operator
97  // for debugging purposes
99 
101  }
102 
103  /// destructor
105  // for debugging purposes
107 
108  // remove things that were allocated within the current class
109  delete triangulation__;
110  }
111 
112  /// virtual clone constructor
115  }
116 
117  /// virtual copy constructor
119  return new IncrementalTriangulation(*this);
120  }
121 
122  /// copy operator
125  // avoid self assignment
126  if (this != &from) {
127  // for debugging purposes
129 
130  // copy all the structures stored in "from"
131  graph__ = from.graph__;
134  T_mpd__ = from.T_mpd__;
145 
146  // just in case we changed the triangulation algorithm, we remove it
147  // and create it again
148  delete triangulation__;
150  }
151 
152  return *this;
153  }
154 
155  /// adds a new node to the graph (and update the internal structures)
157  // check if the node already exists
158  if (graph__.existsNode(node)) return;
159 
160  // add the new node to the graph
163 
164  // add a new clique to T_mpd and the junction tree
167 
170 
171  // indicate in which MPS node belongs
175 
176  // indicate in which MPS the clique added to the junction tree belongs
179 
182 
183  // indicate that the new MPS should not be affected by a triangulation
184  mps_affected__.insert(MPS, false);
185 
186  // insert the node into the elimination order sequence
188 
191 
194  }
195 
196  /// mark the mps affected by the deletion of a given edge
197 
199  const NodeId Mz,
200  const Edge& edge) {
201  // mark the MPS so that it will be retriangulated
202  mps_affected__[My] = true;
203 
204  // mark all the neighbour MPS that contain edge
205  for (const auto nei: T_mpd__.neighbours(My))
206  if (nei != Mz) {
207  const NodeSet& Syk = T_mpd__.separator(Edge(nei, My));
208 
209  if (Syk.contains(edge.first()) && Syk.contains(edge.second()))
211  }
212  }
213 
214  /// removes an edge from the graph (the join tree may need a retriangulation)
215 
217  // check that the edge exist
218  if (!graph__.existsEdge(edge)) return;
219 
220  // find a MPS containing the edge (X,Y)
221  const NodeId X = edge.first();
222  const NodeId Y = edge.second();
223 
224  const List< NodeId >& mps1 = mps_of_node__[X];
225  const List< NodeId >& mps2 = mps_of_node__[Y];
226 
227  NodeId Mx = mps1[0];
228 
229  if (mps1.size() <= mps2.size()) {
230  for (const auto node: mps1)
231  if (T_mpd__.clique(node).contains(Y)) {
232  Mx = node;
233  break;
234  }
235  } else {
236  for (const auto node: mps2)
237  if (T_mpd__.clique(node).contains(X)) {
238  Mx = node;
239  break;
240  }
241  }
242 
243  // mark the MPS that need be updated
245 
246  require_update__ = true;
247 
249 
251 
252  // remove the edge (X,Y) from the graph
254  }
255 
256  /// removes a node from the graph (the join tree may need a triangulation
257  /// update)
258 
260  // check if the node exists
261  if (!graph__.existsNode(X)) return;
262 
263  // remove all the edges adjacent to the node
264  {
266 
267  for (auto neighbour_edge
268  = neighbours.beginSafe(); // safe iterator needed here
270  ++neighbour_edge) {
272  }
273  }
274 
275  auto& MPS_of_X = mps_of_node__[X];
276 
277  // remove X from the MPS containing X
278  for (const auto node: MPS_of_X) {
280 
281  // if the intersection between *iter and one of its neighbour is empty,
282  // remove the edge linking them
284 
285  for (auto it_neighbour = neighbours.beginSafe();
287  ++it_neighbour) { // safe iterator needed here
289 
291  }
292  }
293 
294  // remove X from the cliques containing X
295  for (const auto clique: MPS_of_X) {
297 
298  for (unsigned int i = 0; i < cliques_of_X.size(); ++i) {
300 
301  // if the intersection between clique and one of its neighbour is empty,
302  // remove the edge linking them only if, in addition, there is no
303  // edge in graph__ between a node of clique and a node in the neighbour
305 
306  for (auto it_neighbour = neighbours.beginSafe();
308  ++it_neighbour) { // safe iterator needed here
310 
311  if (junction_tree__.separator(neigh).size() == 0) {
312  // try to see if there is an edge between the nodes of one extremity
313  // of *neighbour and those of the other extremity
314  bool hasCommonEdge = false;
315 
316  for (const auto node1: junction_tree__.clique(neigh.first()))
317  for (const auto node2: junction_tree__.clique(neigh.second()))
318  if (graph__.existsEdge(node1, node2)) {
319  hasCommonEdge = true;
320  break;
321  }
322 
324  }
325  }
326  }
327  }
328 
329  // if the MPS containing X is empty, then remove it, as well as the
330  // corresponding clique in the junction tree (which also only contains X)
331  if ((MPS_of_X.size() == 1) && (T_mpd__.clique(MPS_of_X[0]).size() == 0)) {
337  }
338 
340 
341  // update the elimination orders
342 
343  if (!require_update__) {
344  for (Idx i = reverse_elimination_order__[X] + 1;
346  ++i)
348 
350 
352 
354  }
355 
356  // remove X completely from the graph
358 
360  }
361 
362  /// add a new edge to the graph
363 
365  const NodeId Mz,
366  const NodeId X,
367  const NodeId Y) {
368  // check if Mx contains Y. In this case, mark Mx and return 1 to indicate
369  // that
370  // Y has been found or 2 to indicate that Y has been found and that the
371  // nearest
372  // MPS containing X has been marked
373  const NodeSet& cliqueMX = T_mpd__.clique(Mx);
374 
375  if (cliqueMX.contains(Y)) {
376  mps_affected__[Mx] = true;
377 
378  if (cliqueMX.contains(X)) return 2;
379 
380  return 1;
381  }
382 
383  // parse Mx's neighbours until we find Y
384  for (const auto other_node: T_mpd__.neighbours(Mx))
385  if (other_node != Mz) {
387 
388  if (neighbourStatus == 2)
389  return 2;
390  else if (neighbourStatus == 1) {
391  mps_affected__[Mx] = true;
392 
393  if (cliqueMX.contains(X)) return 2;
394 
395  return 1;
396  }
397  }
398 
399  // indicate that X was not found
400  return 0;
401  }
402 
403  /// adds a new edge to the graph (the join tree may need a triangulation
404  /// update)
405  void IncrementalTriangulation::addEdge(const NodeId X, const NodeId Y) {
406  // check that the edge exist
407  if ((X == Y) || !graph__.existsNode(X) || !graph__.existsNode(Y)
408  || graph__.existsEdge(Edge(X, Y)))
409  return;
410 
411  // add the edge to the graph
412  graph__.addEdge(X, Y);
413 
414  // take any MPS containing X and search its tree to find Y
415  NodeId& mps_X = mps_of_node__[X][0];
416 
418 
419  if (found == 0) {
420  // the mps containing X do not belong to the same tree as those containing
421  // Y
422  NodeId& mps_Y = mps_of_node__[Y][0];
423 
424  // find a clique in mps_X containing X and another in mps_Y containing Y
425  // and add a clique XY to the junction tree linked to the cliques found
426  // in mps_X and mps_Y
429  NodeId c_X = 0, c_Y = 0;
430 
431  for (unsigned int i = 0; i < cliques_X.size(); ++i) {
433  c_X = cliques_X[i];
434  break;
435  }
436  }
437 
438  for (unsigned int i = 0; i < cliques_Y.size(); ++i) {
440  c_Y = cliques_Y[i];
441  break;
442  }
443  }
444 
445  // link c_Y and c_X through a new node containing XY
446  NodeSet nodes(2);
447 
448  nodes.insert(X);
449 
450  nodes.insert(Y);
451 
453 
456 
458 
461 
462  // check that the maximal prime subgraph containing X is not X alone
463  // in this case, remove this max prime subgraph, as well as the
464  // corresponding
465  // clique in the junction tree
466  if (T_mpd__.clique(mps_X).size() == 1) {
473  mps_X = newMPS;
474  } else
476 
477  // do the same thing as above for node Y
478  if (T_mpd__.clique(mps_Y).size() == 1) {
485  mps_Y = newMPS;
486  } else
488 
489  std::vector< NodeId >& cl
491 
493 
495 
496  mps_affected__.insert(newMPS, false);
497  } else {
498  require_update__ = true;
500  }
501 
502  // in all cases, recompute the elimination ordering
504  }
505 
506  /// checks that the incremental triangulation works properly
507 
509  // just in case, update everything
511 
512  bool OK = true;
513 
514  // check that all the nodes of the graph belong to the junction tree
515  {
516  NodeProperty< bool > nodesProp = graph__.nodesProperty< bool >(false);
517 
518  for (const auto cliq: junction_tree__.nodes())
519  for (const auto node: junction_tree__.clique(cliq))
520  nodesProp[node] = true;
521 
522  for (const auto& elt: nodesProp)
523  if (!elt.second) {
524  std::cerr << "check nodes" << std::endl
525  << graph__ << std::endl
526  << junction_tree__ << std::endl;
527  OK = false;
528  }
529 
530  if (!OK) return false;
531  }
532 
533  // check that the edgs belong to the junction tree
534  {
536  EdgeProperty< bool > edgesProp = graph__.edgesProperty(false);
537 
538  for (const auto cliq: junction_tree__.nodes()) {
540 
541  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
542  auto iter3 = iter2;
543 
544  for (++iter3; iter3 != clique.end(); ++iter3) {
545  thePair.first = std::min(*iter2, *iter3);
546  thePair.second = std::max(*iter2, *iter3);
547 
550  }
551  }
552  }
553 
554  for (const auto& elt: edgesProp)
555  if (!elt.second) {
556  std::cerr << "check edges" << std::endl
557  << graph__ << std::endl
558  << junction_tree__ << std::endl;
559  OK = false;
560  }
561 
562  if (!OK) return false;
563  }
564 
565  // check that all the nodes of the graph belong to the MPS tree
566  {
567  NodeProperty< bool > nodesProp = graph__.nodesProperty< bool >(false);
568 
569  for (const auto cliq: T_mpd__.nodes())
570  for (const auto node: T_mpd__.clique(cliq))
571  nodesProp[node] = true;
572 
573  for (const auto& elt: nodesProp)
574  if (!elt.second) {
575  std::cerr << "check nodes" << std::endl
576  << graph__ << std::endl
577  << T_mpd__ << std::endl;
578  OK = false;
579  }
580 
581  if (!OK) return false;
582  }
583 
584  // check that the arcs of the graph belong to the MPS tree
585  {
587  EdgeProperty< bool > edgesProp = graph__.edgesProperty(false);
588 
589  for (const auto cliq: T_mpd__.nodes()) {
590  const NodeSet& clique = T_mpd__.clique(cliq);
591 
592  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
593  auto iter3 = iter2;
594 
595  for (++iter3; iter3 != clique.end(); ++iter3) {
596  thePair.first = std::min(*iter2, *iter3);
597  thePair.second = std::max(*iter2, *iter3);
598 
601  }
602  }
603  }
604 
605  for (const auto& elt: edgesProp)
606  if (!elt.second) {
607  std::cerr << "check edges" << std::endl
608  << graph__ << std::endl
609  << T_mpd__ << std::endl;
610  OK = false;
611  }
612 
613  if (!OK) return false;
614  }
615 
616  // check the MPS of node
617  {
618  NodeProperty< NodeProperty< bool > > chk;
619 
620  for (const auto node: graph__.nodes())
621  chk.insert(node, HashTable< NodeId, bool >());
622 
623  for (const auto cliq: T_mpd__.nodes())
624  for (auto node: T_mpd__.clique(cliq))
625  chk[node].insert(cliq, false);
626 
627  for (const auto& elt: mps_of_node__) {
628  HashTable< NodeId, bool >& hash = chk[elt.first];
629 
630  for (const auto cell: elt.second) {
631  if (!hash.exists(cell)) {
632  std::cerr << "check mps of nodes" << std::endl
633  << T_mpd__ << std::endl
634  << mps_of_node__ << std::endl;
635  OK = false;
636  }
637 
638  hash[cell] = true;
639  }
640  }
641 
642  for (const auto& elt: chk)
643  for (const auto& elt2: elt.second)
644  if (!elt2.second) {
645  std::cerr << "check mps of nodes2" << std::endl
646  << T_mpd__ << std::endl
647  << mps_of_node__ << std::endl;
648  OK = false;
649  }
650 
651  if (!OK) return false;
652  }
653 
654  // check that the junction tree and the T_mpd are junction trees
655  {
656  if (!junction_tree__.isJoinTree()) {
657  std::cerr << "check join tree junction_tree__" << std::endl
658  << junction_tree__ << std::endl;
659  return false;
660  }
661 
662  if (!T_mpd__.isJoinTree()) {
663  std::cerr << "check join tree T_mpd__" << std::endl
664  << T_mpd__ << std::endl;
665  return false;
666  }
667  }
668 
669  // check elimination sequences
670  {
672 
673  if (elimination_order__.size() != graph__.size()) {
674  std::cerr << "check elimination order" << std::endl
676  return false;
677  }
678 
679  NodeSet nodes;
680 
681  for (const auto node: graph__.nodes()) {
682  if (nodes.exists(node)) {
683  std::cerr << "check elimination order" << std::endl
685  return false;
686  } else
687  nodes.insert(node);
688  }
689 
690  if (nodes.size() != graph__.size()) {
691  std::cerr << "check elimination order" << std::endl
693  return false;
694  }
695 
697  std::cerr << "check reverse elimination order" << std::endl
699  return false;
700  }
701 
702  for (const auto node: graph__.nodes()) {
704  std::cerr << "check reverse elimination order" << std::endl
706  return false;
707  }
708  }
709  }
710 
711  // check created junction tree cliques
712  {
714 
715  if (created_JT_cliques__.size() != graph__.size()) {
716  std::cerr << "check creating JT cliques" << std::endl
718  return false;
719  }
720 
721  for (const auto node: graph__.nodes()) {
724  std::cerr << "check created JT cliques" << std::endl
726  return false;
727  }
728  }
729  }
730 
731  return true;
732  }
733 
734  /// set up a connected subgraph to be triangulated
735 
737  NodeId Mx,
738  NodeId Mfrom,
741  HashTable< NodeId, bool >& cliques_affected) {
742  // mark the clique so that we won't try to update it several times
743  cliques_affected[Mx] = false;
744 
745  // get the nodes that are concerned by the triangulation update
746  for (const auto node: junction_tree__.clique(Mx))
748 
749  // go on with the neighbour cliques in the junction tree
750  for (const auto othernode: junction_tree__.neighbours(Mx))
751  if (othernode != Mfrom) {
754  Mx,
755  theGraph,
758  } else {
759  // indicate that we have a clique not affected that is adjacent
760  // to an affected one
762  }
763  }
764  }
765 
766  /// update the junction tree
767 
771  // a temporary subgraph in which we actually perform the triangulation
773 
774  // for each triangulation, we will keep track of the cliques of the
775  // junction tree that are not affected by the triangulation but that are
776  // adjacent to cliques affected. This will enable us to connect easily the
777  // newly created cliques with the old ones.
779 
780  // parse all the affected MPS and get the corresponding cliques
781 
782  for (const auto& elt: mps_affected__)
783  if (elt.second) {
784  // get the cliques contained in this MPS
786 
787  for (unsigned int i = 0; i < cliques.size(); ++i)
789  }
790 
791  // for each connected set of cliques involved in the triangulations
792  // perform a new triangulation and update the max prime subgraph tree
793  for (const auto& elt: all_cliques_affected) {
794  if (elt.second) {
795  // set up the connected subgraph that need be retriangulated and the
796  // cliques that are affected by this triangulation
797  tmp_graph.clear();
800  elt.first,
801  tmp_graph,
804 
805  // insert the edges in tmp_graph
806  for (auto edge: graph__.edges()) {
807  try {
809  } catch (Exception&) {} // both extremities must be in tmp_graph
810  }
811 
812  // remove from the mps_of_node table the affected mps containing the
813  // node
814  // for ( UndiGraph::NodeIterator iter_node =
815  // tmp_graph.beginNodes();iter_node
816  // != tmp_graph.endNodes(); ++iter_node ) {
817  for (const auto node: tmp_graph.nodes()) {
819 
821  = mps_affected__.beginSafe(); // safe iterator needed here
823  ++iter_mps) {
825  }
826  }
827 
828  // now tmp_graph contains the graph that should be triangulated.
829  // so triangulate it and get its junction tree
831 
833 
834  // now, update the global junction tree
835  // first add the nodes of tmp_junction_tree to junction_tree__
836  // to do so, store the translations of the node ids of tmp_junction_tree
837  // into the node ids of junction_tree__
839 
840  for (const auto cliq: tmp_junction_tree.nodes()) {
841  // get new ids for the nodes of tmp_junction_tree. These should be
842  // greater than or equal to junction_tree__.bound () so that we can
843  // use the max_old_id defined at the beginning of the method.
845  // translate the id of the temprary JT into an id of the global JT
848  }
849 
850  // and add the edges of tmp_junction_tree to junction_tree__
851  for (const auto& edge: tmp_junction_tree.edges())
854 
855  // second get the edges in junction_tree__ that have an extremal clique
856  // R
857  // in the affected clique set and the other one S not in the affected
858  // set
859  // and see which new node V in the junction_tree__ should be connected
860  // to S. The running intersection property guarrantees that the clique
861  // in
862  // the tmp_junction_tree that results from the elimination (during the
863  // triangulation process) of the first eliminated node in the separator
864  // between R and S is an admissible candidate
865  for (unsigned int i = 0; i < notAffectedneighbourCliques.size(); ++i) {
866  // check that the separator is not empty. If this is the case, do not
867  // link the new junction tree to the old one
868  const NodeSet& sep
870 
871  if (sep.size() != 0) {
872  // now find the first eliminated node in the separator
874  NodeId elim_node = 0;
875 
876  for (const auto id: sep) {
878 
879  if (new_order < elim_order__) {
881  elim_node = id;
882  }
883  }
884 
885  // find the junction_tree__ clique corresponding to the elimination
886  // of
887  // elim_node and insert an edge between this clique and that which
888  // was
889  // not affected
892 
897 
899 
903  }
904 
905  // check that the separator created by the new edge is not equal to
906  // to_connect. If this is the case, then to_connect is included in
907  // not_affected and, hence, should be removed from the graph
911 
912  for (const auto neighbour: junction_tree__.neighbours(to_connect)) {
914 
918  }
919 
921 
923  }
924  }
925  }
926  }
927  }
928 
929  // remove the mps that were affected and update the cliques_of_mps table
930  for (const auto& elt: all_cliques_affected) {
933  }
934 
935  for (const auto& elt: mps_affected__)
936  if (elt.second) {
939  }
940  }
941 
942  /// used for computing the junction tree of the maximal prime subgraphs
943 
945  const NodeId node,
946  const NodeId from,
948  HashTable< NodeId, bool >& mark,
949  const NodeSet& new_nodes_in_junction_tree) const {
950  mark[node] = true;
951 
952  // check the separators on all the adjacent edges of Mx
953  for (const auto other_node: junction_tree__.neighbours(node))
954  if (other_node != from) {
955  const NodeSet& separator
957 
958  // check that the separator between node and other_node is complete
959  bool complete = true;
960 
961  for (auto iter_sep1 = separator.begin();
963  ++iter_sep1) {
964  auto iter_sep2 = iter_sep1;
965 
966  for (++iter_sep2; iter_sep2 != separator.end(); ++iter_sep2) {
968  complete = false;
969  break;
970  }
971  }
972  }
973 
974  // here complete indicates whether the separator is complete or not
975  if (!complete)
977 
980  node,
982  mark,
984  }
985  }
986 
987  /// update the max prime subgraph
988 
992  // the maximal prime subgraph join tree is created by aggregation of some
993  // cliques. More precisely, when the separator between 2 cliques is not
994  // complete in the original graph, then the two cliques must be merged.
995 
996  // Create a hashtable indicating which clique has been absorbed by some
997  // other
998  // clique. Keys are the cliques absorbed, and values are the cliques that
999  // absorb them.
1001 
1002  for (const auto clik: junction_tree__.nodes())
1005 
1006  // parse all the separators of the junction tree and test those that are not
1007  // complete in the original graph
1009 
1010  HashTable< NodeId, bool > mark = T_mpd_cliques.map(false);
1011 
1012  for (const auto& elt: mark)
1013  if (!elt.second)
1015  elt.first,
1017  mark,
1019 
1020  // compute the transitive closure of merged_cliques. This one will contain
1021  // pairs (X,Y) indicating that clique X must be merged with clique Y.
1022  // Actually clique X will be inserted into clique Y.
1023  for (unsigned int i = 0; i < merged_cliques.size(); ++i) {
1027  else
1030  }
1031 
1032  // now we can create the max prime junction tree.
1033 
1034  // create a map translating the cliques' ids in the junction tree into
1035  // cliques' id in the T_mpd_ tree
1037 
1038  // First, create the new cliques and create the corresponding
1039  // cliques_of_mps entries
1040  for (const auto& elt: T_mpd_cliques)
1041  if (elt.first == elt.second) {
1047  }
1048 
1049  // add to the cliques previously created the nodes of the cliques that were
1050  // merged into them and update the cliques_of_mps
1051  for (const auto& elt: T_mpd_cliques)
1052  if ((elt.first != elt.second)
1054  const NodeId idMPS = clique2MPS[elt.second];
1055 
1056  for (const auto node: junction_tree__.clique(elt.first)) {
1057  try {
1059  } catch (DuplicateElement&) {}
1060  }
1061 
1063  }
1064 
1065  // update the mps_of_node and the mps_of_clique
1066  for (const auto& elt: T_mpd_cliques) {
1067  const NodeId idMPS = clique2MPS[elt.second];
1069 
1070  if (elt.first == elt.second)
1071  for (const auto node: T_mpd__.clique(idMPS))
1073  }
1074 
1075  // add the edges to the max prime subgraph tree
1076  for (const auto& elt: T_mpd_cliques) {
1078 
1079  for (const auto othernode: junction_tree__.neighbours(elt.first))
1081  // here iter is linked to another node that has been created during
1082  // the triangulation
1084 
1085  // avoid adding the same edge several times
1087  } else {
1089  }
1090  }
1091  }
1092 
1093  /// updates the triangulated graph using the modif list
1094 
1096  if (!require_update__) return;
1097  // the set of all the cliques that should be affected by the different
1098  // triangulations we will perform (one by connected component)
1100 
1101  // we need to keep track of the new node ids that will be inserted
1102  // into junction_tree__. A priori, these should be equal to the ids
1103  // inserted into tmp2global_junction_tree. But, sometimes, some new nodes
1104  // are included into old nodes and, in this case, the translation in
1105  // tmp2global_junction_tree indicates the the new node inserted corresponds
1106  // to an old node. Here we wish to know this additional information
1108 
1110 
1111  // now update the T_mpd so that it be coherent with the junction tree
1113 
1114  // reset the MPS that are affected
1116 
1117  for (const auto node: T_mpd__.nodes())
1118  mps_affected__.insert(node, false);
1119 
1120  // remove all the structures used by the triangulation algorithm
1122 
1123  require_update__ = false;
1124  }
1125 
1126  /// sets the graph to the empty graph
1127 
1129  graph__.clear();
1132  T_mpd__.clear();
1133  mps_of_node__.clear();
1138  require_update__ = false;
1144  }
1145 
1146  /// a collect algorithm to compute, for each node, one container JT's clique
1147 
1148  void
1150  const NodeId from,
1151  NodeProperty< bool >& examined) {
1152  // apply collect to all the neighbours except from
1153  for (const auto otherclique: junction_tree__.neighbours(clique))
1155 
1156  // get the nodes that belong to clique and not to from
1157  examined[clique] = true;
1158 
1160 
1161  if (from != clique) {
1163 
1164  for (const auto cli: cliquenodes)
1166  } else {
1167  for (const auto cli: cliquenodes)
1169  }
1170  }
1171 
1172  /** @brief returns the Ids of the cliques of the junction tree created by the
1173  * elimination of the nodes */
1174 
1175  const NodeProperty< NodeId >&
1177  // check if we already computed the containing cliques
1179 
1180  // we first we compute the junction tree
1182 
1184 
1186 
1187  if (junction_tree__.size() == 0) { return created_JT_cliques__; }
1188 
1189  // now we can use a collect algorithm to get the containing cliques
1190  NodeProperty< bool > examined = junction_tree__.nodesProperty< bool >(false);
1191 
1192  for (const auto& elt: examined)
1194 
1195  return created_JT_cliques__;
1196  }
1197 
1198  /// returns a clique containing a given node of the triangulated graph
1199 
1202  return created_JT_cliques__[id];
1203  }
1204 
1205  /** @brief returns the Id of the maximal prime subgraph created by the
1206  * elimination of a given node during the triangulation process */
1207 
1209  // get the created junction tree clique and get its MPS
1211  }
1212 
1213  /// changes the current graph
1214 
1216  const NodeProperty< Size >* dom_sizes) {
1217  // check that both the graph and the domain sizes are different from nullptr
1218  // or else that both are equal to nullptr
1219  if (((graph != nullptr) && (dom_sizes == nullptr))
1220  || ((graph == nullptr) && (dom_sizes != nullptr))) {
1222  "one of the graph or the set of domain sizes "
1223  "is a null pointer.");
1224  }
1225 
1226  // remove the current graph
1227  clear();
1228 
1229  // copy the graph passed in arent and update the structures
1230  // containing the informations useful for the triangulation
1231  if (graph != nullptr) {
1232  for (const auto node: *graph)
1233  addNode(node, (*dom_sizes)[node]);
1234 
1235  for (const auto& edge: graph->edges())
1236  addEdge(edge.first(), edge.second());
1237  }
1238  }
1239 
1240  /// a collect algorithm to compute elimination orderings
1241 
1243  const NodeId node,
1244  const NodeId from,
1245  NodeProperty< bool >& examined,
1246  Idx& index) {
1247  // apply collect to all the neighbours except from
1248  for (const auto othernode: junction_tree__.neighbours(node))
1249  if (othernode != from)
1251 
1252  // get the nodes that belong to node and not to from
1253  examined[node] = true;
1254 
1256 
1257  if (from != node) {
1259 
1260  for (const auto cli: clique) {
1261  if (!separator.contains(cli)) {
1264  ++index;
1265  }
1266  }
1267  } else {
1268  for (const auto cli: clique) {
1271  ++index;
1272  }
1273  }
1274  }
1275 
1276  /// get a possible elimination ordering
1277 
1279  // check if we already computed the elimination order
1281 
1282  // to compute the elimination order, we first we compute the junction tree
1284 
1286 
1288 
1290 
1291  if (junction_tree__.size() == Size(0)) { return elimination_order__; }
1292 
1293  // now we can use a collect algorithm to get the elimination order
1294  Idx index = Idx(0);
1295 
1296  NodeProperty< bool > examined = junction_tree__.nodesProperty< bool >(false);
1297 
1298  for (const auto& elt: examined)
1299  if (!elt.second)
1301 
1302  return elimination_order__;
1303  }
1304 
1305  /** @brief returns the number of a given node in the elimination order
1306  * (0 = first node eliminated) */
1307 
1309  if (!graph__.existsNode(node)) {
1310  GUM_ERROR(NotFound, "the node " << node << " does not exist");
1311  }
1312 
1313  // compute the elimination order
1314  eliminationOrder();
1315 
1317  }
1318 
1319 } /* namespace gum */
1320 
1321 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669