aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
staticTriangulation.cpp
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2025 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-2025 *
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
52
53#ifdef GUM_NO_INLINE
55#endif // GUM_NO_INLINE
56
57namespace gum {
58
59 // default constructor
61 const NodeProperty< Size >* domsizes,
62 const EliminationSequenceStrategy& elimSeq,
63 const JunctionTreeStrategy& JTStrategy,
64 bool minimality) :
67 _minimality_required_(minimality) {
68 // for debugging purposes
69 GUM_CONSTRUCTOR(StaticTriangulation);
70
71 // if the graph is not empty, resize several structures in order to speed-up
72 // their fillings.
73 if (theGraph != nullptr) {
74 _elim_order_.resize(theGraph->size());
75 _reverse_elim_order_.resize(theGraph->size());
76 _elim_cliques_.resize(theGraph->size());
77 _node_2_max_prime_clique_.resize(theGraph->size());
78 _added_fill_ins_.resize(theGraph->size());
79 }
80
81 // register the triangulation to its junction tree strategy
82 junction_tree_strategy_->setTriangulation(this);
83 }
84
85 // default constructor
87 const JunctionTreeStrategy& JTStrategy,
88 bool minimality) :
91 // for debugging purposes
92 GUM_CONSTRUCTOR(StaticTriangulation);
93
94 // register the triangulation to its junction tree strategy
95 junction_tree_strategy_->setTriangulation(this);
96 }
97
98 // copy constructor
125
126 // move constructor
128 Triangulation(std::move(from)),
133 _fill_ins_(std::move(from._fill_ins_)), _elim_order_(std::move(from._elim_order_)),
135 _elim_cliques_(std::move(from._elim_cliques_)), _elim_tree_(std::move(from._elim_tree_)),
146 // move the factories contained in from
147 from.elimination_sequence_strategy_ = new DefaultEliminationSequenceStrategy;
148 from.junction_tree_strategy_ = new DefaultJunctionTreeStrategy;
149 junction_tree_strategy_->moveTriangulation(this);
150
151 // if from has a junction tree, copy it
152 if (from._junction_tree_ != nullptr) {
153 _junction_tree_ = &(junction_tree_strategy_->junctionTree());
154 }
155
156 // for debugging purposes
157 GUM_CONS_MOV(StaticTriangulation);
158 }
159
162 // for debugging purposes
163 GUM_DESTRUCTOR(StaticTriangulation);
164
167
168 // no need to deallocate _original_graph_ nor _junction_tree_ because
169 // they are not owned by the static triangulation class
170 }
171
174 // clear the factories
177
178 // remove the current graphs
179 _original_graph_ = nullptr;
180 _junction_tree_ = nullptr;
181 _triangulated_graph_.clear();
182 _elim_tree_.clear();
184 _elim_cliques_.clear();
186
187 // remove all fill-ins and orderings
188 _fill_ins_.clear();
189 _added_fill_ins_.clear();
190 _elim_order_.clear();
191 _reverse_elim_order_.clear();
192
193 // indicates that the junction tree, the max prime tree, etc, are updated
194 _has_triangulation_ = true;
197 _has_junction_tree_ = true;
199 _has_fill_ins_ = true;
200 }
201
202 // removes redondant fill-ins and compute proper elimination cliques and order
204 // apply recursive thinning (fmint, see Kjaerulff (90), "triangulation of
205 // graphs - algorithms giving small total state space")
206 NodeId node1, node2;
207 bool incomplete;
208 std::vector< NodeId > adj;
209 EdgeSet T_prime;
211
212 for (const auto node: _triangulated_graph_)
213 R.insert(node, 0);
214
215 // the FMINT loop
216 if (_added_fill_ins_.size() > 0) {
217 for (auto iter = _added_fill_ins_.rbegin(); iter != _added_fill_ins_.rend(); ++iter) {
218 if (iter->size()) {
219 // here apply MINT to T_i = _added_fill_ins_[i]
220 EdgeSet& T = *iter;
221
222 // compute R: by default, R is equal to all the nodes in the graph
223 // when R[...] >= j (see the j in the loop below), this means that the
224 // node belongs to an extremal edge in R
225 for (std::size_t k = 0; k < _elim_order_.size(); ++k)
226 R[_elim_order_[k]] = 0; // WARNING: do not replace R[ _elim_order_[k]] by
227
228 // R[k] because the node ids may not be
229 // consecutive numbers
230
231 // apply MINT while some edges can possibly be deleted
232 bool require_mint = true;
233
234 for (unsigned int j = 0; require_mint; ++j) {
235 // find T' (it is equal to the edges (v,w) of T such that
236 // the intersection of adj(v,G) and adj(w,G) is complete and such
237 // that
238 // v and/or w belongs to an extremal node in R
239 for (const auto& edge: T) {
240 node1 = edge.first();
241 node2 = edge.second();
242
243 // check if at least one extremal node belongs to R
244 if ((R[node1] < j) && (R[node2] < j)) continue;
245
246 // check if the intersection of adj(v,G) and adj(w,G) is a
247 // complete subgraph
248 if (_triangulated_graph_.neighbours(node2).size()
249 < _triangulated_graph_.neighbours(node1).size()) {
250 NodeId tmp = node1;
251 node1 = node2;
252 node2 = tmp;
253 }
254
255 incomplete = false;
256
257 // find the nodes belonging to the intersection of adj(v,G)
258 // and adj(w,G)
259 for (const auto adjnode: _triangulated_graph_.neighbours(node1))
260 if (_triangulated_graph_.existsEdge(node2, adjnode)) adj.push_back(adjnode);
261
262 // check if the intersection is complete
263 for (unsigned int k = 0; k < adj.size() && !incomplete; ++k) {
264 for (unsigned int m = k + 1; m < adj.size(); ++m) {
265 if (!_triangulated_graph_.existsEdge(adj[k], adj[m])) {
266 incomplete = true;
267 break;
268 }
269 }
270 }
271
272 adj.clear();
273
274 if (!incomplete) {
275 T_prime.insert(edge);
276 R[node1] = j + 1;
277 R[node2] = j + 1;
278 }
279 }
280
281 // compute the new value of T (i.e. T\T') and update the
282 // triangulated graph
283 for (const auto& edge: T_prime) {
284 T.erase(edge);
285 _triangulated_graph_.eraseEdge(edge);
286
287 if (_has_fill_ins_) _fill_ins_.erase(edge);
288 }
289
290 if (T_prime.size() == 0) require_mint = false;
291
292 T_prime.clear();
293 }
294 }
295 }
296 }
297
298 // here, the recursive thinning has removed all the superfluous edges.
299 // Hence some cliques have been split and, as a result, the elimination
300 // order has changed. We should thus compute a new perfect ordering. To
301 // do so, we use a Maximal Cardinality Search: it has indeed the nice
302 // property that, when the graph is already triangulated, it finds a
303 // compatible order of elimination that does not require any edge addition
304
305 // a structure storing the number of neighbours previously processed
307 std::greater< unsigned int >(),
308 _triangulated_graph_.size());
309
310 for (Size i = 0; i < _elim_order_.size(); ++i)
311 numbered_neighbours.insert(_elim_order_[i], 0);
312
313 // perform the maximum cardinality search
314 if (_elim_order_.size() > 0) {
315 for (Size i = Size(_elim_order_.size()); i >= 1; --i) {
316 NodeId ni = NodeId(i - 1);
317 NodeId node = numbered_neighbours.pop();
318 _elim_order_[ni] = node;
319 _reverse_elim_order_[node] = ni;
320
321 for (const auto neighbour: _triangulated_graph_.neighbours(node)) {
322 try {
323 numbered_neighbours.setPriority(neighbour, 1 + numbered_neighbours.priority(neighbour));
324 } catch (NotFound const&) {}
325 }
326 }
327 }
328
329 // here the elimination order is ok. We now need to update the
330 // _elim_cliques_
331 for (Size i = 0; i < _elim_order_.size(); ++i) {
332 NodeSet& cliques = _elim_cliques_.insert(_elim_order_[i], NodeSet()).second;
333 cliques << _elim_order_[i];
334
335 for (const auto neighbour: _triangulated_graph_.neighbours(_elim_order_[i]))
336 if (_reverse_elim_order_[neighbour] > i) cliques << neighbour;
337 }
338 }
339
342 // if there already exists an elimination tree, no need to compute it again
343 if (_has_elimination_tree_) return;
344
345 // if the graph is not triangulated, triangulate it
347
348 // create the nodes of the elimination tree
349 _elim_tree_.clear();
350
351 auto size = Size(_elim_order_.size());
352 for (auto i = NodeId(0); i < size; ++i)
353 _elim_tree_.addNodeWithId(i, _elim_cliques_[_elim_order_[i]]);
354
355 // create the edges of the elimination tree: join a node to the one in
356 // its clique that is eliminated first
357 for (auto i = NodeId(0); i < size; ++i) {
358 NodeId clique_i_creator = _elim_order_[i];
359 const NodeSet& list_of_nodes = _elim_cliques_[clique_i_creator];
360 Idx child = _original_graph_->bound() + 1;
361
362 for (const auto node: list_of_nodes) {
363 auto it_elim_step = _reverse_elim_order_[node];
364
365 if ((node != clique_i_creator) && (child > it_elim_step)) child = it_elim_step;
366 }
367
368 if (child <= _original_graph_->bound()) {
369 // WARNING: here, we assume that the nodes of the elimination tree are
370 // indexed from 0 to n-1
371 _elim_tree_.addEdge(i, child);
372 }
373 }
374
376 }
377
380 const NodeId from,
381 std::vector< Arc >& merged_cliques,
382 NodeSet& mark) const {
383 mark << node;
384
385 for (const auto other_node: _junction_tree_->neighbours(node))
386 if (other_node != from) {
387 const NodeSet& separator = _junction_tree_->separator(node, other_node);
388 // check that the separator between node and other_node is complete
389 bool complete = true;
390
391 for (auto iter_sep1 = separator.begin(); iter_sep1 != separator.end() && complete;
392 ++iter_sep1) {
393 auto iter_sep2 = iter_sep1;
394
395 for (++iter_sep2; iter_sep2 != separator.end(); ++iter_sep2) {
396 if (!_original_graph_->existsEdge(*iter_sep1, *iter_sep2)) {
397 complete = false;
398 break;
399 }
400 }
401 }
402
403 // here complete indicates whether the separator is complete or not
404 if (!complete) merged_cliques.push_back(Arc(other_node, node));
405
406 _computeMaxPrimeMergings_(other_node, node, merged_cliques, mark);
407 }
408 }
409
412 // if there already exists a max prime junction tree, no need
413 // to recompute it
415
416 // if there is no junction tree, create it
418
419 // the maximal prime subgraph join tree is created by aggregation of some
420 // cliques. More precisely, when the separator between 2 cliques is not
421 // complete in the original graph, then the two cliques must be merged.
422 // Create a hashtable indicating which clique has been absorbed by some
423 // other
424 // clique.
425 NodeProperty< NodeId > T_mpd_cliques(_junction_tree_->size());
426
427 for (const auto clik: _junction_tree_->nodes())
428 T_mpd_cliques.insert(clik, clik);
429
430 // parse all the separators of the junction tree and test those that are not
431 // complete in the orginal graph
432 std::vector< Arc > merged_cliques;
433
434 NodeSet mark;
435
436 for (const auto clik: _junction_tree_->nodes())
437 if (!mark.contains(clik)) _computeMaxPrimeMergings_(clik, clik, merged_cliques, mark);
438
439 // compute the transitive closure of merged_cliques. This one will contain
440 // pairs (X,Y) indicating that clique X must be merged with clique Y.
441 // Actually clique X will be inserted into clique Y.
442 for (unsigned int i = 0; i < merged_cliques.size(); ++i) {
443 T_mpd_cliques[merged_cliques[i].tail()] = T_mpd_cliques[merged_cliques[i].head()];
444 }
445
446 // now we can create the max prime junction tree. First, create the cliques
447 for (const auto& elt: T_mpd_cliques)
448 if (elt.first == elt.second)
449 _max_prime_junction_tree_.addNodeWithId(elt.second, _junction_tree_->clique(elt.second));
450
451 // add to the cliques previously created the nodes of the cliques that were
452 // merged into them
453 for (const auto& elt: T_mpd_cliques)
454 if (elt.first != elt.second)
455 for (const auto node: _junction_tree_->clique(elt.first)) {
456 try {
457 _max_prime_junction_tree_.addToClique(elt.second, node);
458 } catch (DuplicateElement const&) {}
459 }
460
461 // add the edges to the graph
462 for (const auto& edge: _junction_tree_->edges()) {
463 NodeId node1 = T_mpd_cliques[edge.first()];
464 NodeId node2 = T_mpd_cliques[edge.second()];
465
466 if (node1 != node2) {
467 try {
468 _max_prime_junction_tree_.addEdge(node1, node2);
469 } catch (DuplicateElement const&) {}
470 }
471 }
472
473 // compute for each node which clique of the max prime junction tree was
474 // created by the elimination of the node
475 const NodeProperty< NodeId >& node_2_junction_clique
476 = junction_tree_strategy_->createdCliques();
477
478 for (const auto& elt: node_2_junction_clique)
479 _node_2_max_prime_clique_.insert(elt.first, T_mpd_cliques[elt.second]);
480
482 }
483
487
488 // if we did not construct the triangulated graph during the triangulation,
489 // that is, we just constructed the junction tree, we shall construct it now
491 // just in case, be sure that the junction tree has been constructed
493
494 // copy the original graph
496
497 for (const auto clik_id: *_junction_tree_) {
498 // for each clique, add the edges necessary to make it complete
499 const NodeSet& clique = _junction_tree_->clique(clik_id);
500 std::vector< NodeId > clique_nodes(clique.size());
501 unsigned int i = 0;
502
503 for (const auto node: clique) {
504 clique_nodes[i] = node;
505 i += 1;
506 }
507
508 for (i = 0; i < clique_nodes.size(); ++i) {
509 for (unsigned int j = i + 1; j < clique_nodes.size(); ++j) {
510 try {
511 _triangulated_graph_.addEdge(clique_nodes[i], clique_nodes[j]);
512 } catch (DuplicateElement const&) {}
513 }
514 }
515 }
516
518 }
519
521 }
522
523 // initialize the triangulation algorithm for a new graph
525 // remove the old graph
526 clear();
527
528 if (graph != nullptr) {
529 // prepare the size of the data for the new graph
530 _elim_order_.resize(graph->size());
531 _reverse_elim_order_.resize(graph->size());
532 _elim_cliques_.resize(graph->size());
533 _added_fill_ins_.resize(graph->size());
534 _node_2_max_prime_clique_.resize(graph->size());
535 }
536
537 // keep track of the variables passed in argument
538 _original_graph_ = graph;
539 domain_sizes_ = domsizes;
540
541 // indicate that no triangulation was performed for this graph
542 _has_triangulation_ = false;
545 _has_junction_tree_ = false;
547 _has_fill_ins_ = false;
548 }
549
552 // if the graph is already triangulated, no need to triangulate it again
553 if (_has_triangulation_) return;
554
555 // copy the graph to be triangulated, so that we can modify it
556 UndiGraph tmp_graph = *_original_graph_;
557
558 initTriangulation_(tmp_graph);
560
561 // if we are to do recursive thinning, we will have to add fill-ins to the
562 // triangulated graph each time we eliminate a node. Hence, we shall
563 // initialize the triangulated graph by a copy of the original graph
567 }
568
569 // perform the triangulation
570 NodeId removable_node = 0;
571
572 for (unsigned int nb_elim = 0; tmp_graph.size() != 0; ++nb_elim) {
573 removable_node = elimination_sequence_strategy_->nextNodeToEliminate();
574
575 // when minimality is not required, i.e., we won't apply recursive
576 // thinning, the cliques sets can be computed
578 NodeSet& cliques = _elim_cliques_.insert(removable_node, NodeSet()).second;
579 cliques.resize(tmp_graph.neighbours(removable_node).size() / 2);
580 cliques << removable_node;
581 for (const auto clik: tmp_graph.neighbours(removable_node))
582 cliques << clik;
583 } else {
584 // here recursive thinning will be applied, hence we need store the
585 // fill-ins added by the current node removal
586 EdgeSet& current_fill = _added_fill_ins_[nb_elim];
587 NodeId node1, node2;
588
589 const NodeSet& nei = tmp_graph.neighbours(removable_node);
590
591 for (auto iter_edges1 = nei.begin(); iter_edges1 != nei.end(); ++iter_edges1) {
592 node1 = *iter_edges1;
593 auto iter_edges2 = iter_edges1;
594
595 for (++iter_edges2; iter_edges2 != nei.end(); ++iter_edges2) {
596 node2 = *iter_edges2;
597 Edge edge(node1, node2);
598
599 if (!tmp_graph.existsEdge(edge)) {
600 current_fill.insert(edge);
601 _triangulated_graph_.addEdge(node1, node2);
602 }
603 }
604 }
605 }
606
607 // if we want fill-ins but the elimination sequence strategy does not
608 // compute them, we shall compute them here
609 if (_we_want_fill_ins_ && !elimination_sequence_strategy_->providesFillIns()) {
610 NodeId node1, node2;
611
612 // add edges between removable_node's neighbours in order to create
613 // a clique
614 const NodeSet& nei = tmp_graph.neighbours(removable_node);
615
616 for (auto iter_edges1 = nei.begin(); iter_edges1 != nei.end(); ++iter_edges1) {
617 node1 = *iter_edges1;
618 auto iter_edges2 = iter_edges1;
619
620 for (++iter_edges2; iter_edges2 != nei.end(); ++iter_edges2) {
621 node2 = *iter_edges2;
622 Edge edge(node1, node2);
623
624 if (!tmp_graph.existsEdge(edge)) { _fill_ins_.insert(edge); }
625 }
626 }
627
628 // delete removable_node and its adjacent edges
629 tmp_graph.eraseNode(removable_node);
630 }
631
632 // inform the elimination sequence that we are actually removing
633 // removable_node (this enables, for instance, to update the weights of
634 // the nodes in the graph)
635 elimination_sequence_strategy_->eliminationUpdate(removable_node);
636
637 if (!elimination_sequence_strategy_->providesGraphUpdate()) {
638 NodeId node1, node2;
639
640 const NodeSet& nei = tmp_graph.neighbours(removable_node);
641
642 for (auto iter_edges1 = nei.begin(); iter_edges1 != nei.end(); ++iter_edges1) {
643 node1 = *iter_edges1;
644 auto iter_edges2 = iter_edges1;
645
646 for (++iter_edges2; iter_edges2 != nei.end(); ++iter_edges2) {
647 node2 = *iter_edges2;
648 Edge edge(node1, node2);
649
650 if (!tmp_graph.existsEdge(edge)) { tmp_graph.addEdge(node1, node2); }
651 }
652 }
653
654 tmp_graph.eraseNode(removable_node);
655 }
656
657 // update the elimination order
658 _elim_order_[nb_elim] = removable_node;
659 _reverse_elim_order_.insert(removable_node, nb_elim);
660 }
661
662 // indicate whether we actually computed fill-ins
664
665 // if minimality is required, remove the redondant edges
667
668 _has_triangulation_ = true;
669 }
670
673 // if we did not compute the triangulation yet, do it and commpute
674 // the fill-ins at the same time
675 if (!_has_triangulation_) {
676 bool want_fill_ins = _we_want_fill_ins_;
677 _we_want_fill_ins_ = true;
679 _we_want_fill_ins_ = want_fill_ins;
680 _has_fill_ins_ = true;
681 }
682
683 // here, we already computed a triangulation and we already computed
684 // the fill-ins, so return them
685 if (_has_fill_ins_) {
686 if (elimination_sequence_strategy_->providesFillIns())
687 return elimination_sequence_strategy_->fillIns();
688 else return _fill_ins_;
689 } else {
690 // ok, here, we shall compute the fill-ins as they were not precomputed
691 if (!_original_graph_) return _fill_ins_;
692
693 // just in case, be sure that the junction tree has been constructed
695
696 for (const auto clik_id: _junction_tree_->nodes()) {
697 // for each clique, add the edges necessary to make it complete
698 const NodeSet& clique = _junction_tree_->clique(clik_id);
699 std::vector< NodeId > clique_nodes(clique.size());
700 unsigned int i = 0;
701
702 for (const auto node: clique) {
703 clique_nodes[i] = node;
704 i += 1;
705 }
706
707 for (i = 0; i < clique_nodes.size(); ++i) {
708 for (unsigned int j = i + 1; j < clique_nodes.size(); ++j) {
709 Edge edge(clique_nodes[i], clique_nodes[j]);
710
711 if (!_original_graph_->existsEdge(edge)) {
712 try {
713 _fill_ins_.insert(edge);
714 } catch (DuplicateElement const&) {}
715 }
716 }
717 }
718 }
719
720 return _fill_ins_;
721 }
722 }
723
728
729} /* namespace gum */
The base class for all directed edges.
An efficient unconstrained elimination sequence algorithm.
An algorithm producing a junction given the elimination tree produced by a triangulation algorithm.
Exception : a similar element already exists.
bool existsEdge(const Edge &edge) const
indicates whether a given edge exists
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
The base class for all undirected edges.
The base class for all elimination sequence algorithms used by triangulation algorithms.
virtual EliminationSequenceStrategy * copyFactory() const =0
virtual copy constructor
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Base Class for all the algorithms producing a junction given a set of cliques/subcliques resulting fr...
virtual JunctionTreeStrategy * copyFactory(StaticTriangulation *triangulation=nullptr) const =0
virtual copy constructor
Size size() const
alias for sizeNodes
Exception : the element we looked for cannot be found.
void setPriority(const Val &elt, const Priority &new_priority)
Modifies the priority of each instance of a given element.
Val pop()
Removes the top element from the priority queue and return it.
const Priority & priority(const Val &elt) const
Returns the priority of an instance of the value passed in argument.
Size insert(const Val &val, const Priority &priority)
Inserts a new (a copy) element in the priority queue.
A priorityQueue is a heap in which each element has a mutable priority.
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:438
const iterator & end() const noexcept
The usual unsafe end iterator to parse the set.
Definition set_tpl.h:450
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:636
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:497
void resize(Size new_capacity)
Changes the size of the underlying hash table containing the set.
Definition set_tpl.h:468
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool _has_junction_tree_
a boolean indicating whether the junction tree has been constructed
NodeProperty< NodeId > _reverse_elim_order_
the elimination order (access by NodeId)
JunctionTreeStrategy * junction_tree_strategy_
the junction tree strategy used by the triangulation
bool _has_fill_ins_
indicates whether we actually computed fill-ins
void _computeMaxPrimeMergings_(const NodeId node, const NodeId from, std::vector< Arc > &merged_cliques, NodeSet &mark) const
used for computing the junction tree of the maximal prime subgraphs
void _computeMaxPrimeJunctionTree_()
computes the junction tree of the maximal prime subgraphs
const EdgeSet & fillIns()
returns the fill-ins added by the triangulation algorithm
void _computeEliminationTree_()
returns an elimination tree from a triangulated graph
StaticTriangulation(const EliminationSequenceStrategy &elimSeq, const JunctionTreeStrategy &JTStrategy, bool minimality=false)
default constructor: without any graph
const CliqueGraph & junctionTree()
returns a compatible junction tree
bool _has_triangulation_
a boolean indicating whether we have parformed a triangulation
virtual void initTriangulation_(UndiGraph &graph)
the function called to initialize the triangulation process
NodeProperty< NodeSet > _elim_cliques_
the cliques formed by the elimination of the nodes
bool _we_want_fill_ins_
a boolean indicating if we want fill-ins list with the standard triangulation method
EliminationSequenceStrategy * elimination_sequence_strategy_
the elimination sequence strategy used by the triangulation
NodeProperty< NodeId > _node_2_max_prime_clique_
indicates which clique of the max prime junction tree was created by the elmination of a given node (...
CliqueGraph _max_prime_junction_tree_
the maximal prime subgraph junction tree computed from the junction tree
const UndiGraph * _original_graph_
a pointer to the (external) original graph (which will be triangulated)
void clear()
reinitialize the graph to be triangulated to an empty graph
UndiGraph _triangulated_graph_
the triangulated graph
void _computeRecursiveThinning_()
removes redondant fill-ins and compute proper elimination cliques and order
std::vector< EdgeSet > _added_fill_ins_
a vector containing the set of fill-ins added after each node elimination (used by recursive thinning...
bool _has_elimination_tree_
a boolean indicating whether the elimination tree has been computed
bool _minimality_required_
indicates whether the triangulation must be minimal
virtual StaticTriangulation * newFactory() const =0
returns a fresh triangulation of the same type as the current object but over an empty graph
virtual void setGraph(const UndiGraph *graph, const NodeProperty< Size > *domsizes)
initialize the triangulation data structures for a new graph
EdgeSet _fill_ins_
the fill-ins added during the whole triangulation process
virtual ~StaticTriangulation()
destructor
void _triangulate_()
the function that performs the triangulation
const UndiGraph & triangulatedGraph()
returns the triangulated graph
std::vector< NodeId > _elim_order_
the order in which nodes are eliminated by the algorithm
CliqueGraph _elim_tree_
the elimination tree computed by the algorithm
bool _has_max_prime_junction_tree_
indicates whether a maximal prime subgraph junction tree has been constructed
const CliqueGraph * _junction_tree_
the junction tree computed by the algorithm
bool _has_triangulated_graph_
a boolean indicating whether we have constructed the triangulated graph
const NodeProperty< Size > * domain_sizes_
the domain sizes of the variables/nodes of the graph
Triangulation()
default constructor
Base class for undirected graphs.
Definition undiGraph.h:128
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
void eraseNode(NodeId id) override
remove a node and its adjacent edges from the graph
An efficient unconstrained elimination sequence algorithm.
An algorithms producing a junction given the elimination tree produced by the triangulation algorithm...
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Set< Edge > EdgeSet
Some typdefs and define for shortcuts ...
Size NodeId
Type for node ids.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
STL namespace.
priority queues (in which an element cannot appear more than once)
base class for all non-incremental triangulations.
Inline implementations for computing default triangulations of graphs.