aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
incrementalTriangulation.cpp
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 /** @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
48  const UndiGraph* theGraph,
49  const NodeProperty< Size >* domsizes) :
53 
54  // reset the triangulation algorithm => it starts with an empty graph
56 
57  // copy the graph passed in argument and update the structures
58  // containing the informations useful for the triangulation
59 
60  for (const auto node: *theGraph)
61  addNode(node, (*domsizes)[node]);
62 
63  // insert all the edges of the graph into the structure. This will
64  // implicitly update the "require_update" field
65  for (const auto& edge: theGraph->edges())
67  }
68 
69  /// default constructor
74 
75  // reset the triangulation algorithm => it starts with an empty graph
77  }
78 
79  /// copy operator
92 
94  }
95 
96  /// destructor
99 
100  // remove things that were allocated within the current class
101  delete _triangulation_;
102  }
103 
104  /// virtual clone constructor
107  }
108 
109  /// virtual copy constructor
111  return new IncrementalTriangulation(*this);
112  }
113 
114  /// copy operator
117  // avoid self assignment
118  if (this != &from) {
120 
121  // copy all the structures stored in "from"
122  _graph_ = from._graph_;
125  _T_mpd_ = from._T_mpd_;
136 
137  // just in case we changed the triangulation algorithm, we remove it
138  // and create it again
139  delete _triangulation_;
141  }
142 
143  return *this;
144  }
145 
146  /// adds a new node to the graph (and update the internal structures)
148  // check if the node already exists
149  if (_graph_.existsNode(node)) return;
150 
151  // add the new node to the graph
154 
155  // add a new clique to T_mpd and the junction tree
158 
161 
162  // indicate in which MPS node belongs
165 
166  // indicate in which MPS the clique added to the junction tree belongs
169 
172 
173  // indicate that the new MPS should not be affected by a triangulation
174  _mps_affected_.insert(MPS, false);
175 
176  // insert the node into the elimination order sequence
178 
181 
183  }
184 
185  /// mark the mps affected by the deletion of a given edge
186 
188  const NodeId Mz,
189  const Edge& edge) {
190  // mark the MPS so that it will be retriangulated
191  _mps_affected_[My] = true;
192 
193  // mark all the neighbour MPS that contain edge
194  for (const auto nei: _T_mpd_.neighbours(My))
195  if (nei != Mz) {
196  const NodeSet& Syk = _T_mpd_.separator(Edge(nei, My));
197 
198  if (Syk.contains(edge.first()) && Syk.contains(edge.second()))
200  }
201  }
202 
203  /// removes an edge from the graph (the join tree may need a retriangulation)
204 
206  // check that the edge exist
207  if (!_graph_.existsEdge(edge)) return;
208 
209  // find a MPS containing the edge (X,Y)
210  const NodeId X = edge.first();
211  const NodeId Y = edge.second();
212 
213  const List< NodeId >& mps1 = _mps_of_node_[X];
214  const List< NodeId >& mps2 = _mps_of_node_[Y];
215 
216  NodeId Mx = mps1[0];
217 
218  if (mps1.size() <= mps2.size()) {
219  for (const auto node: mps1)
220  if (_T_mpd_.clique(node).contains(Y)) {
221  Mx = node;
222  break;
223  }
224  } else {
225  for (const auto node: mps2)
226  if (_T_mpd_.clique(node).contains(X)) {
227  Mx = node;
228  break;
229  }
230  }
231 
232  // mark the MPS that need be updated
234 
235  _require_update_ = true;
236 
238 
240 
241  // remove the edge (X,Y) from the graph
243  }
244 
245  /// removes a node from the graph (the join tree may need a triangulation
246  /// update)
247 
249  // check if the node exists
250  if (!_graph_.existsNode(X)) return;
251 
252  // remove all the edges adjacent to the node
253  {
255 
256  for (auto neighbour_edge = neighbours.beginSafe(); // safe iterator needed here
258  ++neighbour_edge) {
260  }
261  }
262 
263  auto& MPS_of_X = _mps_of_node_[X];
264 
265  // remove X from the MPS containing X
266  for (const auto node: MPS_of_X) {
268 
269  // if the intersection between *iter and one of its neighbour is empty,
270  // remove the edge linking them
272 
274  ++it_neighbour) { // safe iterator needed here
276 
278  }
279  }
280 
281  // remove X from the cliques containing X
282  for (const auto clique: MPS_of_X) {
284 
285  for (unsigned int i = 0; i < cliques_of_X.size(); ++i) {
287 
288  // if the intersection between clique and one of its neighbour is empty,
289  // remove the edge linking them only if, in addition, there is no
290  // edge in _graph_ between a node of clique and a node in the neighbour
292 
294  ++it_neighbour) { // safe iterator needed here
296 
297  if (_junction_tree_.separator(neigh).size() == 0) {
298  // try to see if there is an edge between the nodes of one extremity
299  // of *neighbour and those of the other extremity
300  bool hasCommonEdge = false;
301 
302  for (const auto node1: _junction_tree_.clique(neigh.first()))
303  for (const auto node2: _junction_tree_.clique(neigh.second()))
304  if (_graph_.existsEdge(node1, node2)) {
305  hasCommonEdge = true;
306  break;
307  }
308 
310  }
311  }
312  }
313  }
314 
315  // if the MPS containing X is empty, then remove it, as well as the
316  // corresponding clique in the junction tree (which also only contains X)
317  if ((MPS_of_X.size() == 1) && (_T_mpd_.clique(MPS_of_X[0]).size() == 0)) {
323  }
324 
326 
327  // update the elimination orders
328 
329  if (!_require_update_) {
332 
334 
336 
338  }
339 
340  // remove X completely from the graph
342 
344  }
345 
346  /// add a new edge to the graph
347 
349  const NodeId Mz,
350  const NodeId X,
351  const NodeId Y) {
352  // check if Mx contains Y. In this case, mark Mx and return 1 to indicate
353  // that
354  // Y has been found or 2 to indicate that Y has been found and that the
355  // nearest
356  // MPS containing X has been marked
357  const NodeSet& cliqueMX = _T_mpd_.clique(Mx);
358 
359  if (cliqueMX.contains(Y)) {
360  _mps_affected_[Mx] = true;
361 
362  if (cliqueMX.contains(X)) return 2;
363 
364  return 1;
365  }
366 
367  // parse Mx's neighbours until we find Y
368  for (const auto other_node: _T_mpd_.neighbours(Mx))
369  if (other_node != Mz) {
371 
372  if (neighbourStatus == 2)
373  return 2;
374  else if (neighbourStatus == 1) {
375  _mps_affected_[Mx] = true;
376 
377  if (cliqueMX.contains(X)) return 2;
378 
379  return 1;
380  }
381  }
382 
383  // indicate that X was not found
384  return 0;
385  }
386 
387  /// adds a new edge to the graph (the join tree may need a triangulation
388  /// update)
389  void IncrementalTriangulation::addEdge(const NodeId X, const NodeId Y) {
390  // check that the edge exist
391  if ((X == Y) || !_graph_.existsNode(X) || !_graph_.existsNode(Y)
392  || _graph_.existsEdge(Edge(X, Y)))
393  return;
394 
395  // add the edge to the graph
396  _graph_.addEdge(X, Y);
397 
398  // take any MPS containing X and search its tree to find Y
399  NodeId& mps_X = _mps_of_node_[X][0];
400 
402 
403  if (found == 0) {
404  // the mps containing X do not belong to the same tree as those containing
405  // Y
406  NodeId& mps_Y = _mps_of_node_[Y][0];
407 
408  // find a clique in mps_X containing X and another in mps_Y containing Y
409  // and add a clique XY to the junction tree linked to the cliques found
410  // in mps_X and mps_Y
413  NodeId c_X = 0, c_Y = 0;
414 
415  for (unsigned int i = 0; i < cliques_X.size(); ++i) {
417  c_X = cliques_X[i];
418  break;
419  }
420  }
421 
422  for (unsigned int i = 0; i < cliques_Y.size(); ++i) {
424  c_Y = cliques_Y[i];
425  break;
426  }
427  }
428 
429  // link c_Y and c_X through a new node containing XY
430  NodeSet nodes(2);
431 
432  nodes.insert(X);
433 
434  nodes.insert(Y);
435 
437 
440 
442 
445 
446  // check that the maximal prime subgraph containing X is not X alone
447  // in this case, remove this max prime subgraph, as well as the
448  // corresponding
449  // clique in the junction tree
450  if (_T_mpd_.clique(mps_X).size() == 1) {
457  mps_X = newMPS;
458  } else
460 
461  // do the same thing as above for node Y
462  if (_T_mpd_.clique(mps_Y).size() == 1) {
469  mps_Y = newMPS;
470  } else
472 
474 
476 
478 
479  _mps_affected_.insert(newMPS, false);
480  } else {
481  _require_update_ = true;
483  }
484 
485  // in all cases, recompute the elimination ordering
487  }
488 
489  /// checks that the incremental triangulation works properly
490 
492  // just in case, update everything
494 
495  bool OK = true;
496 
497  // check that all the nodes of the graph belong to the junction tree
498  {
499  NodeProperty< bool > nodesProp = _graph_.nodesProperty< bool >(false);
500 
501  for (const auto cliq: _junction_tree_.nodes())
502  for (const auto node: _junction_tree_.clique(cliq))
503  nodesProp[node] = true;
504 
505  for (const auto& elt: nodesProp)
506  if (!elt.second) {
507  std::cerr << "check nodes" << std::endl
508  << _graph_ << std::endl
509  << _junction_tree_ << std::endl;
510  OK = false;
511  }
512 
513  if (!OK) return false;
514  }
515 
516  // check that the edgs belong to the junction tree
517  {
519  EdgeProperty< bool > edgesProp = _graph_.edgesProperty(false);
520 
521  for (const auto cliq: _junction_tree_.nodes()) {
523 
524  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
525  auto iter3 = iter2;
526 
527  for (++iter3; iter3 != clique.end(); ++iter3) {
528  thePair.first = std::min(*iter2, *iter3);
529  thePair.second = std::max(*iter2, *iter3);
530 
533  }
534  }
535  }
536 
537  for (const auto& elt: edgesProp)
538  if (!elt.second) {
539  std::cerr << "check edges" << std::endl
540  << _graph_ << std::endl
541  << _junction_tree_ << std::endl;
542  OK = false;
543  }
544 
545  if (!OK) return false;
546  }
547 
548  // check that all the nodes of the graph belong to the MPS tree
549  {
550  NodeProperty< bool > nodesProp = _graph_.nodesProperty< bool >(false);
551 
552  for (const auto cliq: _T_mpd_.nodes())
553  for (const auto node: _T_mpd_.clique(cliq))
554  nodesProp[node] = true;
555 
556  for (const auto& elt: nodesProp)
557  if (!elt.second) {
558  std::cerr << "check nodes" << std::endl << _graph_ << std::endl << _T_mpd_ << std::endl;
559  OK = false;
560  }
561 
562  if (!OK) return false;
563  }
564 
565  // check that the arcs of the graph belong to the MPS tree
566  {
568  EdgeProperty< bool > edgesProp = _graph_.edgesProperty(false);
569 
570  for (const auto cliq: _T_mpd_.nodes()) {
571  const NodeSet& clique = _T_mpd_.clique(cliq);
572 
573  for (auto iter2 = clique.begin(); iter2 != clique.end(); ++iter2) {
574  auto iter3 = iter2;
575 
576  for (++iter3; iter3 != clique.end(); ++iter3) {
577  thePair.first = std::min(*iter2, *iter3);
578  thePair.second = std::max(*iter2, *iter3);
579 
582  }
583  }
584  }
585 
586  for (const auto& elt: edgesProp)
587  if (!elt.second) {
588  std::cerr << "check edges" << std::endl << _graph_ << std::endl << _T_mpd_ << std::endl;
589  OK = false;
590  }
591 
592  if (!OK) return false;
593  }
594 
595  // check the MPS of node
596  {
597  NodeProperty< NodeProperty< bool > > chk;
598 
599  for (const auto node: _graph_.nodes())
600  chk.insert(node, HashTable< NodeId, bool >());
601 
602  for (const auto cliq: _T_mpd_.nodes())
603  for (auto node: _T_mpd_.clique(cliq))
604  chk[node].insert(cliq, false);
605 
606  for (const auto& elt: _mps_of_node_) {
607  HashTable< NodeId, bool >& hash = chk[elt.first];
608 
609  for (const auto cell: elt.second) {
610  if (!hash.exists(cell)) {
611  std::cerr << "check mps of nodes" << std::endl
612  << _T_mpd_ << std::endl
613  << _mps_of_node_ << std::endl;
614  OK = false;
615  }
616 
617  hash[cell] = true;
618  }
619  }
620 
621  for (const auto& elt: chk)
622  for (const auto& elt2: elt.second)
623  if (!elt2.second) {
624  std::cerr << "check mps of nodes2" << std::endl
625  << _T_mpd_ << std::endl
626  << _mps_of_node_ << std::endl;
627  OK = false;
628  }
629 
630  if (!OK) return false;
631  }
632 
633  // check that the junction tree and the T_mpd are junction trees
634  {
635  if (!_junction_tree_.isJoinTree()) {
636  std::cerr << "check join tree _junction_tree_" << std::endl
637  << _junction_tree_ << std::endl;
638  return false;
639  }
640 
641  if (!_T_mpd_.isJoinTree()) {
642  std::cerr << "check join tree _T_mpd_" << std::endl << _T_mpd_ << std::endl;
643  return false;
644  }
645  }
646 
647  // check elimination sequences
648  {
650 
651  if (_elimination_order_.size() != _graph_.size()) {
652  std::cerr << "check elimination order" << std::endl << _elimination_order_ << std::endl;
653  return false;
654  }
655 
656  NodeSet nodes;
657 
658  for (const auto node: _graph_.nodes()) {
659  if (nodes.exists(node)) {
660  std::cerr << "check elimination order" << std::endl << _elimination_order_ << std::endl;
661  return false;
662  } else
663  nodes.insert(node);
664  }
665 
666  if (nodes.size() != _graph_.size()) {
667  std::cerr << "check elimination order" << std::endl << _elimination_order_ << std::endl;
668  return false;
669  }
670 
672  std::cerr << "check reverse elimination order" << std::endl
674  return false;
675  }
676 
677  for (const auto node: _graph_.nodes()) {
679  std::cerr << "check reverse elimination order" << std::endl
681  return false;
682  }
683  }
684  }
685 
686  // check created junction tree cliques
687  {
689 
690  if (_created_JT_cliques_.size() != _graph_.size()) {
691  std::cerr << "check creating JT cliques" << std::endl << _created_JT_cliques_ << std::endl;
692  return false;
693  }
694 
695  for (const auto node: _graph_.nodes()) {
698  std::cerr << "check created JT cliques" << std::endl << _created_JT_cliques_ << std::endl;
699  return false;
700  }
701  }
702  }
703 
704  return true;
705  }
706 
707  /// set up a connected subgraph to be triangulated
708 
710  NodeId Mx,
711  NodeId Mfrom,
714  HashTable< NodeId, bool >& cliques_affected) {
715  // mark the clique so that we won't try to update it several times
716  cliques_affected[Mx] = false;
717 
718  // get the nodes that are concerned by the triangulation update
719  for (const auto node: _junction_tree_.clique(Mx))
721 
722  // go on with the neighbour cliques in the junction tree
723  for (const auto othernode: _junction_tree_.neighbours(Mx))
724  if (othernode != Mfrom) {
727  Mx,
728  theGraph,
731  } else {
732  // indicate that we have a clique not affected that is adjacent
733  // to an affected one
735  }
736  }
737  }
738 
739  /// update the junction tree
740 
743  // a temporary subgraph in which we actually perform the triangulation
745 
746  // for each triangulation, we will keep track of the cliques of the
747  // junction tree that are not affected by the triangulation but that are
748  // adjacent to cliques affected. This will enable us to connect easily the
749  // newly created cliques with the old ones.
751 
752  // parse all the affected MPS and get the corresponding cliques
753 
754  for (const auto& elt: _mps_affected_)
755  if (elt.second) {
756  // get the cliques contained in this MPS
758 
759  for (unsigned int i = 0; i < cliques.size(); ++i)
761  }
762 
763  // for each connected set of cliques involved in the triangulations
764  // perform a new triangulation and update the max prime subgraph tree
765  for (const auto& elt: all_cliques_affected) {
766  if (elt.second) {
767  // set up the connected subgraph that need be retriangulated and the
768  // cliques that are affected by this triangulation
769  tmp_graph.clear();
772  elt.first,
773  tmp_graph,
776 
777  // insert the edges in tmp_graph
778  for (auto edge: _graph_.edges()) {
779  try {
781  } catch (Exception&) {} // both extremities must be in tmp_graph
782  }
783 
784  // remove from the mps_of_node table the affected mps containing the
785  // node
786  // for ( UndiGraph::NodeIterator iter_node =
787  // tmp_graph.beginNodes();iter_node
788  // != tmp_graph.endNodes(); ++iter_node ) {
789  for (const auto node: tmp_graph.nodes()) {
791 
793  = _mps_affected_.beginSafe(); // safe iterator needed here
795  ++iter_mps) {
797  }
798  }
799 
800  // now tmp_graph contains the graph that should be triangulated.
801  // so triangulate it and get its junction tree
803 
805 
806  // now, update the global junction tree
807  // first add the nodes of tmp_junction_tree to _junction_tree_
808  // to do so, store the translations of the node ids of tmp_junction_tree
809  // into the node ids of _junction_tree_
811 
812  for (const auto cliq: tmp_junction_tree.nodes()) {
813  // get new ids for the nodes of tmp_junction_tree. These should be
814  // greater than or equal to _junction_tree_.bound () so that we can
815  // use the max_old_id defined at the beginning of the method.
817  // translate the id of the temprary JT into an id of the global JT
820  }
821 
822  // and add the edges of tmp_junction_tree to _junction_tree_
823  for (const auto& edge: tmp_junction_tree.edges())
826 
827  // second get the edges in _junction_tree_ that have an extremal clique
828  // R
829  // in the affected clique set and the other one S not in the affected
830  // set
831  // and see which new node V in the _junction_tree_ should be connected
832  // to S. The running intersection property guarrantees that the clique
833  // in
834  // the tmp_junction_tree that results from the elimination (during the
835  // triangulation process) of the first eliminated node in the separator
836  // between R and S is an admissible candidate
837  for (unsigned int i = 0; i < notAffectedneighbourCliques.size(); ++i) {
838  // check that the separator is not empty. If this is the case, do not
839  // link the new junction tree to the old one
841 
842  if (sep.size() != 0) {
843  // now find the first eliminated node in the separator
845  NodeId elim_node = 0;
846 
847  for (const auto id: sep) {
849 
850  if (new_order < _elim_order_) {
852  elim_node = id;
853  }
854  }
855 
856  // find the _junction_tree_ clique corresponding to the elimination
857  // of
858  // elim_node and insert an edge between this clique and that which
859  // was
860  // not affected
863 
868 
870 
873  }
874 
875  // check that the separator created by the new edge is not equal to
876  // to_connect. If this is the case, then to_connect is included in
877  // not_affected and, hence, should be removed from the graph
881 
882  for (const auto neighbour: _junction_tree_.neighbours(to_connect)) {
884 
887  }
888 
890 
892  }
893  }
894  }
895  }
896  }
897 
898  // remove the mps that were affected and update the cliques_of_mps table
899  for (const auto& elt: all_cliques_affected) {
902  }
903 
904  for (const auto& elt: _mps_affected_)
905  if (elt.second) {
908  }
909  }
910 
911  /// used for computing the junction tree of the maximal prime subgraphs
912 
914  const NodeId node,
915  const NodeId from,
917  HashTable< NodeId, bool >& mark,
918  const NodeSet& new_nodes_in_junction_tree) const {
919  mark[node] = true;
920 
921  // check the separators on all the adjacent edges of Mx
922  for (const auto other_node: _junction_tree_.neighbours(node))
923  if (other_node != from) {
925 
926  // check that the separator between node and other_node is complete
927  bool complete = true;
928 
929  for (auto iter_sep1 = separator.begin(); iter_sep1 != separator.end() && complete;
930  ++iter_sep1) {
931  auto iter_sep2 = iter_sep1;
932 
933  for (++iter_sep2; iter_sep2 != separator.end(); ++iter_sep2) {
935  complete = false;
936  break;
937  }
938  }
939  }
940 
941  // here complete indicates whether the separator is complete or not
943 
946  node,
948  mark,
950  }
951  }
952 
953  /// update the max prime subgraph
954 
955  void
958  // the maximal prime subgraph join tree is created by aggregation of some
959  // cliques. More precisely, when the separator between 2 cliques is not
960  // complete in the original graph, then the two cliques must be merged.
961 
962  // Create a hashtable indicating which clique has been absorbed by some
963  // other
964  // clique. Keys are the cliques absorbed, and values are the cliques that
965  // absorb them.
967 
968  for (const auto clik: _junction_tree_.nodes())
970 
971  // parse all the separators of the junction tree and test those that are not
972  // complete in the original graph
974 
975  HashTable< NodeId, bool > mark = T_mpd_cliques.map(false);
976 
977  for (const auto& elt: mark)
978  if (!elt.second)
980  elt.first,
982  mark,
984 
985  // compute the transitive closure of merged_cliques. This one will contain
986  // pairs (X,Y) indicating that clique X must be merged with clique Y.
987  // Actually clique X will be inserted into clique Y.
988  for (unsigned int i = 0; i < merged_cliques.size(); ++i) {
991  else
993  }
994 
995  // now we can create the max prime junction tree.
996 
997  // create a map translating the cliques' ids in the junction tree into
998  // cliques' id in the T_mpd_ tree
1000 
1001  // First, create the new cliques and create the corresponding
1002  // cliques_of_mps entries
1003  for (const auto& elt: T_mpd_cliques)
1004  if (elt.first == elt.second) {
1010  }
1011 
1012  // add to the cliques previously created the nodes of the cliques that were
1013  // merged into them and update the cliques_of_mps
1014  for (const auto& elt: T_mpd_cliques)
1016  const NodeId idMPS = clique2MPS[elt.second];
1017 
1018  for (const auto node: _junction_tree_.clique(elt.first)) {
1019  try {
1021  } catch (DuplicateElement&) {}
1022  }
1023 
1025  }
1026 
1027  // update the mps_of_node and the mps_of_clique
1028  for (const auto& elt: T_mpd_cliques) {
1029  const NodeId idMPS = clique2MPS[elt.second];
1031 
1032  if (elt.first == elt.second)
1033  for (const auto node: _T_mpd_.clique(idMPS))
1035  }
1036 
1037  // add the edges to the max prime subgraph tree
1038  for (const auto& elt: T_mpd_cliques) {
1040 
1041  for (const auto othernode: _junction_tree_.neighbours(elt.first))
1043  // here iter is linked to another node that has been created during
1044  // the triangulation
1046 
1047  // avoid adding the same edge several times
1049  } else {
1051  }
1052  }
1053  }
1054 
1055  /// updates the triangulated graph using the modif list
1056 
1058  if (!_require_update_) return;
1059  // the set of all the cliques that should be affected by the different
1060  // triangulations we will perform (one by connected component)
1062 
1063  // we need to keep track of the new node ids that will be inserted
1064  // into _junction_tree_. A priori, these should be equal to the ids
1065  // inserted into tmp2global_junction_tree. But, sometimes, some new nodes
1066  // are included into old nodes and, in this case, the translation in
1067  // tmp2global_junction_tree indicates the the new node inserted corresponds
1068  // to an old node. Here we wish to know this additional information
1070 
1072 
1073  // now update the T_mpd so that it be coherent with the junction tree
1075 
1076  // reset the MPS that are affected
1078 
1079  for (const auto node: _T_mpd_.nodes())
1080  _mps_affected_.insert(node, false);
1081 
1082  // remove all the structures used by the triangulation algorithm
1084 
1085  _require_update_ = false;
1086  }
1087 
1088  /// sets the graph to the empty graph
1089 
1091  _graph_.clear();
1094  _T_mpd_.clear();
1095  _mps_of_node_.clear();
1100  _require_update_ = false;
1106  }
1107 
1108  /// a collect algorithm to compute, for each node, one container JT's clique
1109 
1111  const NodeId from,
1112  NodeProperty< bool >& examined) {
1113  // apply collect to all the neighbours except from
1114  for (const auto otherclique: _junction_tree_.neighbours(clique))
1116 
1117  // get the nodes that belong to clique and not to from
1118  examined[clique] = true;
1119 
1121 
1122  if (from != clique) {
1124 
1125  for (const auto cli: cliquenodes)
1127  } else {
1128  for (const auto cli: cliquenodes)
1130  }
1131  }
1132 
1133  /** @brief returns the Ids of the cliques of the junction tree created by the
1134  * elimination of the nodes */
1135 
1137  // check if we already computed the containing cliques
1139 
1140  // we first we compute the junction tree
1142 
1144 
1146 
1147  if (_junction_tree_.size() == 0) { return _created_JT_cliques_; }
1148 
1149  // now we can use a collect algorithm to get the containing cliques
1150  NodeProperty< bool > examined = _junction_tree_.nodesProperty< bool >(false);
1151 
1152  for (const auto& elt: examined)
1154 
1155  return _created_JT_cliques_;
1156  }
1157 
1158  /// returns a clique containing a given node of the triangulated graph
1159 
1162  return _created_JT_cliques_[id];
1163  }
1164 
1165  /** @brief returns the Id of the maximal prime subgraph created by the
1166  * elimination of a given node during the triangulation process */
1167 
1169  // get the created junction tree clique and get its MPS
1171  }
1172 
1173  /// changes the current graph
1174 
1176  const NodeProperty< Size >* dom_sizes) {
1177  // check that both the graph and the domain sizes are different from nullptr
1178  // or else that both are equal to nullptr
1179  if (((graph != nullptr) && (dom_sizes == nullptr))
1180  || ((graph == nullptr) && (dom_sizes != nullptr))) {
1182  "one of the graph or the set of domain sizes "
1183  "is a null pointer.");
1184  }
1185 
1186  // remove the current graph
1187  clear();
1188 
1189  // copy the graph passed in arent and update the structures
1190  // containing the informations useful for the triangulation
1191  if (graph != nullptr) {
1192  for (const auto node: *graph)
1193  addNode(node, (*dom_sizes)[node]);
1194 
1195  for (const auto& edge: graph->edges())
1196  addEdge(edge.first(), edge.second());
1197  }
1198  }
1199 
1200  /// a collect algorithm to compute elimination orderings
1201 
1203  const NodeId from,
1204  NodeProperty< bool >& examined,
1205  Idx& index) {
1206  // apply collect to all the neighbours except from
1207  for (const auto othernode: _junction_tree_.neighbours(node))
1209 
1210  // get the nodes that belong to node and not to from
1211  examined[node] = true;
1212 
1214 
1215  if (from != node) {
1217 
1218  for (const auto cli: clique) {
1219  if (!separator.contains(cli)) {
1222  ++index;
1223  }
1224  }
1225  } else {
1226  for (const auto cli: clique) {
1229  ++index;
1230  }
1231  }
1232  }
1233 
1234  /// get a possible elimination ordering
1235 
1237  // check if we already computed the elimination order
1239 
1240  // to compute the elimination order, we first we compute the junction tree
1242 
1244 
1246 
1248 
1249  if (_junction_tree_.size() == Size(0)) { return _elimination_order_; }
1250 
1251  // now we can use a collect algorithm to get the elimination order
1252  Idx index = Idx(0);
1253 
1254  NodeProperty< bool > examined = _junction_tree_.nodesProperty< bool >(false);
1255 
1256  for (const auto& elt: examined)
1258 
1259  return _elimination_order_;
1260  }
1261 
1262  /** @brief returns the number of a given node in the elimination order
1263  * (0 = first node eliminated) */
1264 
1266  if (!_graph_.existsNode(node)) { GUM_ERROR(NotFound, "the node " << node << " does not exist") }
1267 
1268  // compute the elimination order
1269  eliminationOrder();
1270 
1272  }
1273 
1274 } /* namespace gum */
1275 
1276 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643