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