aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
gum::MeekRules Class Reference

#include <MeekRules.h>

Public Member Functions

MixedGraph propagate (const MixedGraph &mg)
 Propagates the orientation of a MixedGraph (no double-headed arcs) and return a PDAG.
PDAG propagateToCPDAG (const MixedGraph &mg)
 Propagates the orientation of a MixedGraph (no double-headed arcs) and return a PDAG.
DAG propagateToDAG (const MixedGraph &mg)
 Propagates the orientation of a MixedGraph and completes it as a DAG.
std::vector< Arcchoices () const
 Get the Choices object : the list of arcs that the algorithm has to choose from a double arc or a double non-arc.
Constructors / Destructors
 MeekRules ()
 default constructor
virtual ~MeekRules ()
 destructor

Private Member Functions

MixedGraph _propagates_ (const MixedGraph &graph)
bool _applyMeekRules_ (MixedGraph &graph, NodeId xj)
 Propagates the orientation from a node to its neighbours.
void _propagatesOrientationInChainOfRemainingEdges_ (gum::MixedGraph &graph)
 heuristic for remaining edges when everything else has been tried
void _orientDoubleHeadedArcs_ (MixedGraph &mg)
 Tells us if we can orient the edge xi - xj to xi -> xj.
bool _isOrientable_ (const gum::MixedGraph &graph, gum::NodeId xi, gum::NodeId xj) const
void _complete_ (MixedGraph &graph)
gum::Arc _critereMinParents_ (const gum::MixedGraph &graph, gum::NodeId x, gum::NodeId y)
 When resolving double-headed arcs, prioritize selecting the option that minimizes the number of parent nodes in the graph.

Static Private Member Functions

static bool _existsDirectedPath_ (const MixedGraph &graph, NodeId n1, NodeId n2)

Private Attributes

std::vector< Arc_choices_

Detailed Description

Definition at line 56 of file MeekRules.h.

Constructor & Destructor Documentation

◆ MeekRules()

gum::MeekRules::MeekRules ( )

default constructor

Definition at line 54 of file MeekRules.cpp.

54{ GUM_CONSTRUCTOR(MeekRules); }
MeekRules()
default constructor
Definition MeekRules.cpp:54

References MeekRules().

Referenced by MeekRules(), and ~MeekRules().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ~MeekRules()

gum::MeekRules::~MeekRules ( )
virtual

destructor

Definition at line 57 of file MeekRules.cpp.

57{ GUM_DESTRUCTOR(MeekRules); }

References MeekRules().

Here is the call graph for this function:

Member Function Documentation

◆ _applyMeekRules_()

bool gum::MeekRules::_applyMeekRules_ ( MixedGraph & graph,
NodeId xj )
private

Propagates the orientation from a node to its neighbours.

Parameters
graphgraph in which to which to propagate arcs
nodenode on which neighbours to propagate th orientation
force: true if an orientation has always to be found.

Definition at line 206 of file MeekRules.cpp.

206 {
207 bool res = false;
208 const auto neighbours = graph.neighbours(xj);
209 for (auto& xi: neighbours) {
210 bool i_j = _isOrientable_(graph, xi, xj);
211 bool j_i = _isOrientable_(graph, xj, xi);
212 if (i_j || j_i) {
213 // GUM_SL_EMIT(xi, xj, "Removing Edge", "line 660")
214 graph.eraseEdge(Edge(xi, xj));
215 res = true;
216 }
217 if (i_j) {
218 graph.eraseEdge(Edge(xi, xj));
219 graph.addArc(xi, xj);
220 _applyMeekRules_(graph, xj);
221 }
222 if (j_i) {
223 graph.eraseEdge(Edge(xi, xj));
224 graph.addArc(xj, xi);
225 _applyMeekRules_(graph, xi);
226 }
227 if (i_j && j_i) {
228 // GUM_TRACE(" + add arc (" << xi << "," << xj << ")")
229 _choices_.emplace_back(xi, xj);
230 }
231 }
232 return res;
233 }
std::vector< Arc > _choices_
Definition MeekRules.h:100
bool _applyMeekRules_(MixedGraph &graph, NodeId xj)
Propagates the orientation from a node to its neighbours.
bool _isOrientable_(const gum::MixedGraph &graph, gum::NodeId xi, gum::NodeId xj) const

References _applyMeekRules_(), _choices_, _isOrientable_(), gum::DiGraph::addArc(), gum::EdgeGraphPart::eraseEdge(), and gum::EdgeGraphPart::neighbours().

Referenced by _applyMeekRules_(), _complete_(), _propagates_(), and _propagatesOrientationInChainOfRemainingEdges_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _complete_()

void gum::MeekRules::_complete_ ( MixedGraph & graph)
private

Definition at line 190 of file MeekRules.cpp.

190 {
191 // Propagates existing orientations thanks to rules
192 bool newOrientation = true;
193 while (newOrientation) {
194 newOrientation = false;
195 for (NodeId x: graph.nodes()) {
196 if (!graph.parents(x).empty()) { newOrientation |= _applyMeekRules_(graph, x); }
197 }
198 }
199
200 // GUM_TRACE(graph.toDot())
201 // Orient remaining edges
203 }
void _propagatesOrientationInChainOfRemainingEdges_(gum::MixedGraph &graph)
heuristic for remaining edges when everything else has been tried
Size NodeId
Type for node ids.

References _applyMeekRules_(), _propagatesOrientationInChainOfRemainingEdges_(), gum::Set< Key >::empty(), gum::NodeGraphPart::nodes(), and gum::ArcGraphPart::parents().

Referenced by propagateToDAG().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _critereMinParents_()

gum::Arc gum::MeekRules::_critereMinParents_ ( const gum::MixedGraph & graph,
gum::NodeId x,
gum::NodeId y )
private

When resolving double-headed arcs, prioritize selecting the option that minimizes the number of parent nodes in the graph.

Returns
gum::Arc the arc that should be earased.

Definition at line 236 of file MeekRules.cpp.

236 {
237 // If the number of parents of x is less than the number of parents of y.
238 if (graph.parents(x).size() < graph.parents(y).size()) {
239 // If the number of parents of x is less than the number of parents of y.
240 // We want to keep y->x, so we return x->y for erasure.
241 return Arc(x, y);
242 } else if (graph.parents(x).size() > graph.parents(y).size()) {
243 return Arc(y, x);
244 } else { // If they have the same number of parents, we choose the one with less neighbours.
245 if (graph.neighbours(x).size() < graph.neighbours(y).size()) {
246 return Arc(x, y);
247 } else {
248 return Arc(y, x);
249 }
250 }
251 }
const NodeSet & parents(NodeId id) const
returns the set of nodes with arc ingoing to a given node
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:636

References gum::EdgeGraphPart::neighbours(), gum::ArcGraphPart::parents(), and gum::Set< Key >::size().

Referenced by _orientDoubleHeadedArcs_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _existsDirectedPath_()

bool gum::MeekRules::_existsDirectedPath_ ( const MixedGraph & graph,
NodeId n1,
NodeId n2 )
staticprivate

Definition at line 340 of file MeekRules.cpp.

340 {
341 // not recursive version => use a FIFO for simulating the recursion
342 List< NodeId > nodeFIFO;
343 // mark[node] = successor if visited, else mark[node] does not exist
344 Set< NodeId > mark;
345
346 mark.insert(n2);
347 nodeFIFO.pushBack(n2);
348
349 NodeId current;
350
351 while (!nodeFIFO.empty()) {
352 current = nodeFIFO.front();
353 nodeFIFO.popFront();
354
355 // check the parents
356 for (const auto new_one: graph.parents(current)) {
357 if (graph.existsArc(current,
358 new_one)) // if there is a double arc, pass
359 continue;
360
361 if (new_one == n1) { return true; }
362
363 if (mark.exists(new_one)) // if this node is already marked, do not
364 continue; // check it again
365
366 mark.insert(new_one);
367 nodeFIFO.pushBack(new_one);
368 }
369 }
370
371 return false;
372 }

References gum::List< Val >::empty(), gum::Set< Key >::exists(), gum::ArcGraphPart::existsArc(), gum::List< Val >::front(), gum::Set< Key >::insert(), gum::ArcGraphPart::parents(), gum::List< Val >::popFront(), and gum::List< Val >::pushBack().

Referenced by _isOrientable_(), and _orientDoubleHeadedArcs_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _isOrientable_()

bool gum::MeekRules::_isOrientable_ ( const gum::MixedGraph & graph,
gum::NodeId xi,
gum::NodeId xj ) const
private

Definition at line 253 of file MeekRules.cpp.

253 {
254 // no cycle
255 if (_existsDirectedPath_(graph, xj, xi)) {
256 // GUM_TRACE("cycle(" << xi << "-" << xj << ")")
257 return false;
258 }
259 // R1
260 if (!(graph.parents(xi) - graph.boundary(xj)).empty()) {
261 // GUM_TRACE("R1(" << xi << "-" << xj << ")")
262 return true;
263 }
264 // R2
265 if (_existsDirectedPath_(graph, xi, xj)) {
266 // GUM_TRACE("R2(" << xi << "-" << xj << ")")
267 return true;
268 }
269 // R3
270 int nbr = 0;
271 for (const auto p: graph.parents(xj)) {
272 if (!graph.mixedOrientedPath(xi, p).empty()) {
273 nbr += 1;
274 if (nbr == 2) {
275 // GUM_TRACE("R3(" << xi << "-" << xj << ")")
276 return true;
277 }
278 }
279 }
280 return false;
281 }
static bool _existsDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
NodeSet boundary(NodeId node) const
returns the set of node adjacent to a given node
std::vector< NodeId > mixedOrientedPath(NodeId node1, NodeId node2) const
returns a mixed edge/directed arc path from node1 to node2 in the arc/edge set

References _existsDirectedPath_(), gum::MixedGraph::boundary(), gum::MixedGraph::mixedOrientedPath(), and gum::ArcGraphPart::parents().

Referenced by _applyMeekRules_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _orientDoubleHeadedArcs_()

void gum::MeekRules::_orientDoubleHeadedArcs_ ( MixedGraph & mg)
private

Tells us if we can orient the edge xi - xj to xi -> xj.

Orient double-headed arcs while avoiding cycles.

Parameters
graphthe graph
xithe tail of the arc
xjthe head of the arc
Returns
true if we can orient xi to xj Gets the orientation probabilities like MIIC for the orientation phase
Parameters
mgthe MixedGraph the graph from which the double headed arcs will be oriented.

Definition at line 112 of file MeekRules.cpp.

112 {
113 gum::ArcSet L; // create a set of all double-headed arcs
114 for (gum::NodeId x: mg.nodes())
115 for (NodeId y: mg.parents(x))
116 // If there is a mutual parent-child relationship, add the arc to the set
117 if (mg.parents(y).contains(x)) {
118 if (x > y) {
119 continue; // Avoid duplicate arcs by considering only one direction
120 } else {
121 L.insert(gum::Arc(x, y));
122 }
123 }
124
125 // If there are double-headed arcs
126 if (not L.empty()) {
127 while (true) {
128 bool withdrawFlag_L = false;
129 for (auto arc: ArcSet(L)) {
130 bool tail_head = _existsDirectedPath_(mg, arc.tail(), arc.head());
131 bool head_tail = _existsDirectedPath_(mg, arc.head(), arc.tail());
132 bool withdrawFlag_arc = false;
133
134 // Case 1: There is already a path from tail to head and no path from head to tail
135 if (tail_head && !head_tail) {
136 // Erase the arc from head to tail to avoid cycles
137 mg.eraseArc(Arc(arc.head(), arc.tail()));
138 withdrawFlag_arc = true;
139
140 // Case 2: There is already a path from head to tail and no path from tail to head
141 } else if (!tail_head && head_tail) {
142 // Erase the arc from tail to head to avoid cycles
143 mg.eraseArc(Arc(arc.tail(), arc.head()));
144 withdrawFlag_arc = true;
145
146 // Case 3: There is no path between tail and head
147 } else if (!tail_head && !head_tail) {
148 // Choose an arbitrary orientation and erase the corresponding arc
149 // mg.eraseArc(Arc(arc.head(), arc.tail()));
150 mg.eraseArc(_critereMinParents_(mg, arc.tail(), arc.head()));
151 withdrawFlag_arc = true;
152 }
153
154 // Remove the arc from the set if it was processed
155 if (withdrawFlag_arc) {
156 L.erase(arc);
157 withdrawFlag_L = true;
158 }
159 }
160 // If all double-headed arcs are processed, exit the loop
161 if (L.empty()) { break; }
162
163 // If no arcs were withdrawn, erase an arbitrary double arc in the graph (the first one in
164 // L). Hoping the situation will improve. ┐( ̄ヘ ̄)┌ If we arrive here, it's because the
165 // double-headed arc creates cycles in both directions
166 if (!withdrawFlag_L) {
167 auto it = L.begin();
168 auto arc = *it;
169 mg.eraseArc(Arc(arc.head(), arc.tail()));
170 mg.eraseArc(Arc(arc.tail(), arc.head()));
171 L.erase(arc);
172 }
173 }
174 }
175 }
gum::Arc _critereMinParents_(const gum::MixedGraph &graph, gum::NodeId x, gum::NodeId y)
When resolving double-headed arcs, prioritize selecting the option that minimizes the number of paren...
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:438
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:539
bool empty() const noexcept
Indicates whether the set is the empty set.
Definition set_tpl.h:642
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:582
Set< Arc > ArcSet
Some typdefs and define for shortcuts ...

References _critereMinParents_(), _existsDirectedPath_(), gum::Set< Key >::begin(), gum::Set< Key >::contains(), gum::Set< Key >::empty(), gum::Set< Key >::erase(), gum::ArcGraphPart::eraseArc(), gum::Set< Key >::insert(), gum::NodeGraphPart::nodes(), and gum::ArcGraphPart::parents().

Referenced by propagateToCPDAG(), and propagateToDAG().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _propagates_()

MixedGraph gum::MeekRules::_propagates_ ( const MixedGraph & graph)
private

Definition at line 177 of file MeekRules.cpp.

177 {
178 MixedGraph graph(mg);
179 // Propagates existing orientations thanks to Meek rules
180 bool newOrientation = true;
181 while (newOrientation) {
182 newOrientation = false;
183 for (NodeId x: graph.nodes()) {
184 if (!graph.parents(x).empty()) { newOrientation |= _applyMeekRules_(graph, x); }
185 }
186 }
187 return graph;
188 }

References _applyMeekRules_(), gum::Set< Key >::empty(), gum::NodeGraphPart::nodes(), and gum::ArcGraphPart::parents().

Referenced by propagate(), propagateToCPDAG(), and propagateToDAG().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _propagatesOrientationInChainOfRemainingEdges_()

void gum::MeekRules::_propagatesOrientationInChainOfRemainingEdges_ ( gum::MixedGraph & graph)
private

heuristic for remaining edges when everything else has been tried

Arbitrary propagation if we can't propagate thanks to MeekRules.

Parameters
graphgraph in which to which to propagate arcs
_latentCouples_

Definition at line 284 of file MeekRules.cpp.

284 {
285 // then decide the orientation for remaining edges
286 while (!essentialGraph.edges().empty()) {
287 const auto& edge = *(essentialGraph.edges().begin());
288 NodeId root = edge.first();
289 Size size_children_root = essentialGraph.children(root).size();
290 NodeSet visited;
291 NodeSet stack{root};
292
293 // check the best root for the set of neighbours
294 while (!stack.empty()) {
295 NodeId next = *(stack.begin());
296 stack.erase(next);
297 if (visited.contains(next)) continue;
298 if (essentialGraph.children(next).size() > size_children_root) {
299 size_children_root = essentialGraph.children(next).size();
300 root = next;
301 }
302 for (const auto n: essentialGraph.neighbours(next))
303 if (!stack.contains(n) && !visited.contains(n)) stack.insert(n);
304 visited.insert(next);
305 }
306
307 // orientation now
308 visited.clear();
309 stack.clear();
310 stack.insert(root);
311 while (!stack.empty()) {
312 // GUM_TRACE("stack : " << stack)
313 NodeId next = *(stack.begin());
314 stack.erase(next);
315 // GUM_TRACE("next : " << next)
316 if (visited.contains(next)) continue;
317 const auto nei = essentialGraph.neighbours(next);
318 for (const auto n: nei) {
319 // GUM_TRACE("n : "<< n)
320 if (!stack.contains(n) && !visited.contains(n)) stack.insert(n);
321 // GUM_TRACE(" + amap reasonably orientation for " << n << "->" << next);
322 if (_applyMeekRules_(essentialGraph, next)) {
323 continue;
324 } else {
325 if (!essentialGraph.existsArc(next,
326 n)) { // Checking that we're not creating a
327 // doubly-oriented arc by adding a "random" arc.
328 essentialGraph.eraseEdge(Edge(n, next));
329 essentialGraph.addArc(n, next);
330 _choices_.emplace_back(n, next);
331 // GUM_TRACE(" + add arc (" << n << "->" << next << ")")
332 }
333 }
334 }
335 visited.insert(next);
336 }
337 }
338 }
void clear()
Removes all the elements, if any, from the set.
Definition set_tpl.h:338
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...

References _applyMeekRules_(), _choices_, gum::DiGraph::addArc(), gum::Set< Key >::begin(), gum::ArcGraphPart::children(), gum::Set< Key >::clear(), gum::Set< Key >::contains(), gum::EdgeGraphPart::edges(), gum::Set< Key >::empty(), gum::Set< Key >::erase(), gum::EdgeGraphPart::eraseEdge(), gum::ArcGraphPart::existsArc(), gum::Set< Key >::insert(), gum::EdgeGraphPart::neighbours(), and gum::Set< Key >::size().

Referenced by _complete_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ choices()

std::vector< Arc > gum::MeekRules::choices ( ) const
inline

Get the Choices object : the list of arcs that the algorithm has to choose from a double arc or a double non-arc.

Returns
std::vector< Arc >

Definition at line 95 of file MeekRules.h.

95{ return _choices_; };

References _choices_.

◆ propagate()

MixedGraph gum::MeekRules::propagate ( const MixedGraph & mg)

Propagates the orientation of a MixedGraph (no double-headed arcs) and return a PDAG.

Propagates MeekRules in a MixedGraph.

Parameters
graphthe graph in which to which to propagate arcs
Warning
propagates may create double arcs

Definition at line 60 of file MeekRules.cpp.

60 {
61 _choices_.clear();
62 return _propagates_(graph);
63 }
MixedGraph _propagates_(const MixedGraph &graph)

References _choices_, and _propagates_().

Here is the call graph for this function:

◆ propagateToCPDAG()

PDAG gum::MeekRules::propagateToCPDAG ( const MixedGraph & mg)

Propagates the orientation of a MixedGraph (no double-headed arcs) and return a PDAG.

Parameters
graphthe graph in which to which to propagate arcs
Warning
propagateToCPDAG may have to select between double arcs created by propagates

Definition at line 66 of file MeekRules.cpp.

66 {
67 _choices_.clear();
68
69 MixedGraph graph = _propagates_(mg);
70
71 // Resolve double-headed arc while avoiding cycle creation.
73
74 // cycle have been resolved above, so we can safely convert to PDAG
75 PDAG pdag;
76 for (auto node: graph) {
77 pdag.addNodeWithId(node);
78 }
79 for (const Edge& edge: graph.edges()) {
80 pdag.addEdge(edge.first(), edge.second());
81 }
82 for (const Arc& arc: graph.arcs()) {
83 pdag.addArc(arc.tail(), arc.head());
84 }
85 return pdag;
86 }
void _orientDoubleHeadedArcs_(MixedGraph &mg)
Tells us if we can orient the edge xi - xj to xi -> xj.

References _choices_, _orientDoubleHeadedArcs_(), _propagates_(), gum::PDAG::addArc(), gum::PDAG::addEdge(), gum::NodeGraphPart::addNodeWithId(), gum::ArcGraphPart::arcs(), and gum::EdgeGraphPart::edges().

Here is the call graph for this function:

◆ propagateToDAG()

DAG gum::MeekRules::propagateToDAG ( const MixedGraph & mg)

Propagates the orientation of a MixedGraph and completes it as a DAG.

Propagates the orientation of a MixedGraph and return a DAG.

Parameters
graphthe graph in which to which to propagate arcs
Warning
propagateToDAG may have to select between double arcs created by propagates

Definition at line 89 of file MeekRules.cpp.

89 {
90 _choices_.clear();
91 // Orient all remaining edges into arcs
92 MixedGraph graph = _propagates_(mg);
93
94 // complete remaining edges
95 _complete_(graph);
96
97 // Resolve double-headed arc while avoiding cycle creation.
99
100
101 DAG dag;
102 for (auto node: graph.nodes()) {
103 dag.addNodeWithId(node);
104 }
105 for (const Arc& arc: graph.arcs()) {
106 dag.addArc(arc.tail(), arc.head());
107 }
108 return dag;
109 }
void _complete_(MixedGraph &graph)

References _choices_, _complete_(), _orientDoubleHeadedArcs_(), _propagates_(), gum::DAG::addArc(), gum::NodeGraphPart::addNodeWithId(), gum::ArcGraphPart::arcs(), and gum::NodeGraphPart::nodes().

Here is the call graph for this function:

Member Data Documentation

◆ _choices_

std::vector< Arc > gum::MeekRules::_choices_
private

The documentation for this class was generated from the following files: