aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
gum::DAGCycleDetector Class Reference

A class for detecting directed cycles in DAGs when trying to apply many changes to the graph. More...

#include <DAGCycleDetector.h>

+ Collaboration diagram for gum::DAGCycleDetector:

Public Member Functions

Constructors / Destructors
 DAGCycleDetector () noexcept
 default constructor More...
 
 DAGCycleDetector (const DAGCycleDetector &from)
 copy constructor More...
 
 DAGCycleDetector (DAGCycleDetector &&from)
 move constructor More...
 
 ~DAGCycleDetector ()
 destructor More...
 
Operators
DAGCycleDetectoroperator= (const DAGCycleDetector &from)
 copy operator More...
 
DAGCycleDetectoroperator= (DAGCycleDetector &&from)
 move operator More...
 
bool operator== (const DAGCycleDetector &from) const
 check the equality between two DAGCycleDetectors More...
 
bool operator!= (const DAGCycleDetector &from) const
 check the inequality between two DAGCycleDetectors More...
 
Accessors/Modifiers
void setDAG (const DAG &dag)
 sets the initial DAG from which changes shall be applied More...
 
void addArc (NodeId x, NodeId y)
 adds a new arc to the current DAG More...
 
void eraseArc (NodeId x, NodeId y)
 removes an arc from the current DAG More...
 
void reverseArc (NodeId x, NodeId y)
 reverses an arc from the DAG More...
 
bool hasCycleFromAddition (NodeId x, NodeId y) const noexcept
 indicates whether an arc addition would create a cycle More...
 
bool hasCycleFromReversal (NodeId x, NodeId y) const noexcept
 indicates wether an arc reversal would create a cycle More...
 
bool hasCycleFromDeletion (NodeId x, NodeId y) const noexcept
 indicates whether an arc deletion would create a cycle More...
 
bool hasCycleFromModifications (const std::vector< Change > &modifs) const
 indicates whether a set of modifications would create a cycle More...
 

Public Types

enum  ChangeType { ChangeType::ARC_ADDITION, ChangeType::ARC_DELETION, ChangeType::ARC_REVERSAL }
 

Classes

class  ArcAdd
 the class to indicate that we wish to add a new arc More...
 
class  ArcDel
 the class to indicate that we wish to remove an arc More...
 
class  ArcReverse
 the class to indicate that we wish to reverse an arc More...
 
class  Change
 the base class indicating the possible changes More...
 

Detailed Description

A class for detecting directed cycles in DAGs when trying to apply many changes to the graph.

When trying to assess whether multiple changes applied to a given DAG would induce cycles, use class DAGCycleDetector instead of trying to apply the modifications to the DAG itself and check whether and exception is raised: the class is designed to be fast for such modifications. However, the number of modifications checked should be higher than at least 3 for this class to be competitive.

Definition at line 61 of file DAGCycleDetector.h.

Member Enumeration Documentation

◆ ChangeType

Enumerator
ARC_ADDITION 
ARC_DELETION 
ARC_REVERSAL 

Definition at line 64 of file DAGCycleDetector.h.

Constructor & Destructor Documentation

◆ DAGCycleDetector() [1/3]

INLINE gum::DAGCycleDetector::DAGCycleDetector ( )
noexcept

default constructor

Definition at line 250 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

250  {
251  GUM_CONSTRUCTOR(DAGCycleDetector);
252  }
DAGCycleDetector() noexcept
default constructor
+ Here is the call graph for this function:

◆ DAGCycleDetector() [2/3]

INLINE gum::DAGCycleDetector::DAGCycleDetector ( const DAGCycleDetector from)

copy constructor

Definition at line 255 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

255  :
256  dag__(from.dag__), ancestors__(from.ancestors__),
257  descendants__(from.descendants__) {
258  GUM_CONS_CPY(DAGCycleDetector);
259  }
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
DAGCycleDetector() noexcept
default constructor
+ Here is the call graph for this function:

◆ DAGCycleDetector() [3/3]

INLINE gum::DAGCycleDetector::DAGCycleDetector ( DAGCycleDetector &&  from)

move constructor

Definition at line 262 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

262  :
263  dag__(std::move(from.dag__)), ancestors__(std::move(from.ancestors__)),
264  descendants__(std::move(from.descendants__)) {
265  GUM_CONS_MOV(DAGCycleDetector);
266  }
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
DAGCycleDetector() noexcept
default constructor
+ Here is the call graph for this function:

◆ ~DAGCycleDetector()

INLINE gum::DAGCycleDetector::~DAGCycleDetector ( )

destructor

Definition at line 269 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

269  {
270  GUM_DESTRUCTOR(DAGCycleDetector);
271  }
DAGCycleDetector() noexcept
default constructor
+ Here is the call graph for this function:

Member Function Documentation

◆ addArc()

void gum::DAGCycleDetector::addArc ( NodeId  x,
NodeId  y 
)

adds a new arc to the current DAG

worst case complexity: O(h^2) where h is the height of the DAG

Exceptions
InvalidDirectedCycleif the arc would create a cycle in the dag

Definition at line 310 of file DAGCycleDetector.cpp.

References gum::Set< Key, Alloc >::emplace().

310  {
311  // check that the arc does not already exist
312  if (dag__.existsArc(tail, head)) return;
313 
314  // check that the arc would not create a cycle
315  if (hasCycleFromAddition(tail, head)) {
316  GUM_ERROR(InvalidDirectedCycle,
317  "the arc would create a directed into a DAG");
318  }
319 
320  dag__.addArc(tail, head);
321 
322  // now we apply the addition of the arc as done in method
323  // hasCycleFromModifications
324  const NodeProperty< Size >& anc_tail = ancestors__[tail];
325  const NodeProperty< Size >& desc_head = descendants__[head];
326 
327  // update the set of ancestors
328  NodeProperty< Size > set_to_add = anc_tail;
329  set_to_add.insert(tail, 1);
330  addWeightedSet__(ancestors__[head], set_to_add, 1);
331 
332  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
333  addWeightedSet__(ancestors__[iter.key()],
334  set_to_add,
335  ancestors__[iter.key()][head]);
336  }
337 
338  // update the set of descendants
339  set_to_add = desc_head;
340  set_to_add.insert(head, 1);
341  addWeightedSet__(descendants__[tail], set_to_add, 1);
342 
343  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
344  addWeightedSet__(descendants__[iter.key()],
345  set_to_add,
346  descendants__[iter.key()][tail]);
347  }
348  }
virtual void addArc(const NodeId tail, const NodeId head)
insert a new arc into the directed graph
Definition: diGraph_inl.h:34
void addWeightedSet__(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
bool hasCycleFromAddition(NodeId x, NodeId y) const noexcept
indicates whether an arc addition would create a cycle
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
bool existsArc(const Arc &arc) const
indicates whether a given arc exists
#define GUM_ERROR(type, msg)
Definition: exceptions.h:54
+ Here is the call graph for this function:

◆ addWeightedSet__()

INLINE void gum::DAGCycleDetector::addWeightedSet__ ( NodeProperty< Size > &  nodeset,
const NodeProperty< Size > &  set_to_add,
Size  multiplier 
) const
private

adds a weighted nodeset to another (weights are added)

adds a nodeset to another (nodes are weighted, so weights are added)

Definition at line 316 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

318  {
319  for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
320  if (nodeset.exists(iter.key())) {
321  nodeset[iter.key()] += iter.val() * multiplier;
322  } else {
323  nodeset.insert(iter.key(), iter.val() * multiplier);
324  }
325  }
326  }
+ Here is the call graph for this function:

◆ delWeightedSet__()

INLINE void gum::DAGCycleDetector::delWeightedSet__ ( NodeProperty< Size > &  nodeset,
const NodeProperty< Size > &  set_to_del,
Size  multiplier 
) const
private

removes a weighted nodeset from another (weights are subtracted)

Definition at line 330 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

332  {
333  for (auto iter = set_to_del.cbegin(); iter != set_to_del.cend(); ++iter) {
334  if (nodeset.exists(iter.key())) {
335  Size& weight = nodeset[iter.key()];
336  weight -= iter.val() * multiplier;
337 
338  if (!weight) { nodeset.erase(iter.key()); }
339  }
340  }
341  }
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:47
+ Here is the call graph for this function:

◆ eraseArc()

void gum::DAGCycleDetector::eraseArc ( NodeId  x,
NodeId  y 
)

removes an arc from the current DAG

worst case complexity: O(h^2) where h is the height of the DAG

Definition at line 351 of file DAGCycleDetector.cpp.

References gum::Set< Key, Alloc >::emplace().

351  {
352  // check that the arc exists
353  if (!dag__.existsArc(tail, head)) return;
354 
355  dag__.eraseArc(Arc(tail, head));
356 
357  // we apply the deletion of the arc as done in method
358  // hasCycleFromModifications
359  const NodeProperty< Size >& anc_tail = ancestors__[tail];
360  const NodeProperty< Size >& desc_head = descendants__[head];
361 
362  // update the set of descendants
363  NodeProperty< Size > set_to_del = desc_head;
364  set_to_del.insert(head, 1);
365  delWeightedSet__(descendants__[tail], set_to_del, 1);
366 
367  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
368  delWeightedSet__(descendants__[iter.key()],
369  set_to_del,
370  descendants__[iter.key()][tail]);
371  }
372 
373  // update the set of ancestors
374  set_to_del = anc_tail;
375  set_to_del.insert(tail, 1);
376  delWeightedSet__(ancestors__[head], set_to_del, 1);
377 
378  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
379  delWeightedSet__(ancestors__[iter.key()],
380  set_to_del,
381  ancestors__[iter.key()][head]);
382  }
383  }
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
void delWeightedSet__(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_del, Size multiplier) const
removes a weighted nodeset from another (weights are subtracted)
bool existsArc(const Arc &arc) const
indicates whether a given arc exists
+ Here is the call graph for this function:

◆ hasCycleFromAddition()

INLINE bool gum::DAGCycleDetector::hasCycleFromAddition ( NodeId  x,
NodeId  y 
) const
noexcept

indicates whether an arc addition would create a cycle

worst case complexity: O(1)

Definition at line 297 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

298  {
299  return descendants__[y].exists(x);
300  }
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
+ Here is the call graph for this function:

◆ hasCycleFromDeletion()

INLINE bool gum::DAGCycleDetector::hasCycleFromDeletion ( NodeId  x,
NodeId  y 
) const
noexcept

indicates whether an arc deletion would create a cycle

worst case complexity: O(1)

Definition at line 309 of file DAGCycleDetector_inl.h.

310  {
311  return false;
312  }

◆ hasCycleFromModifications()

bool gum::DAGCycleDetector::hasCycleFromModifications ( const std::vector< Change > &  modifs) const

indicates whether a set of modifications would create a cycle

the Boolean returned corresponds to the existence (or not) of a cycle on the graph resulting from the whole sequence of modifications. This means, for instance, that if, among modifs, there exists an arc (X,Y) addition involving a cycle but also the same arc (X,Y) deletion, the final graph obtained does not contains a cycle (due to the deletion) and the method will thus return false.

Definition at line 114 of file DAGCycleDetector.cpp.

References gum::Set< Key, Alloc >::emplace().

115  {
116  // first, we separate deletions and insertions (reversals are cut into
117  // a deletion + an insertion) and we check that no insertion also exists
118  // as a deletion (if so, we remove both operations). In addition, if
119  // we try to add an arc (X,X) we return that it induces a cycle
120  HashTable< Arc, Size > deletions(Size(modifs.size()));
121  HashTable< Arc, Size > additions(Size(modifs.size()));
122 
123  for (const auto& modif: modifs) {
124  Arc arc(modif.tail(), modif.head());
125 
126  switch (modif.type()) {
128  if (deletions.exists(arc))
129  ++deletions[arc];
130  else
131  deletions.insert(arc, 1);
132 
133  break;
134 
136  if (modif.tail() == modif.head()) return true;
137 
138  if (additions.exists(arc))
139  ++additions[arc];
140  else
141  additions.insert(arc, 1);
142 
143  break;
144 
146  if (deletions.exists(arc))
147  ++deletions[arc];
148  else
149  deletions.insert(arc, 1);
150 
151  arc = Arc(modif.head(), modif.tail());
152 
153  if (additions.exists(arc))
154  ++additions[arc];
155  else
156  additions.insert(arc, 1);
157 
158  break;
159 
160  default:
161  GUM_ERROR(OperationNotAllowed, "undefined change type");
162  }
163  }
164 
165  for (auto iter = additions.beginSafe(); // safe iterator needed here
166  iter != additions.endSafe();
167  ++iter) {
168  if (deletions.exists(iter.key())) {
169  Size& nb_del = deletions[iter.key()];
170  Size& nb_add = iter.val();
171 
172  if (nb_del > nb_add) {
173  additions.erase(iter);
174  nb_del -= nb_add;
175  } else if (nb_del < nb_add) {
176  deletions.erase(iter.key());
177  nb_add -= nb_del;
178  } else {
179  deletions.erase(iter.key());
180  additions.erase(iter);
181  }
182  }
183  }
184 
185  // get the set of nodes involved in the modifications
186  NodeSet extremities;
187 
188  for (const auto& modif: modifs) {
189  extremities.insert(modif.tail());
190  extremities.insert(modif.head());
191  }
192 
193  // we now retrieve the set of ancestors and descendants of all the
194  // extremities of the arcs involved in the modifications. We also
195  // keep track of all the children and parents of these nodes
196  NodeProperty< NodeProperty< Size > > ancestors, descendants;
197 
198  for (const auto& modif: modifs) {
199  if (!ancestors.exists(modif.tail())) {
200  NodeProperty< Size >& anc
201  = ancestors.insert(modif.tail(), NodeProperty< Size >()).second;
202  restrictWeightedSet__(anc, ancestors__[modif.tail()], extremities);
203 
204  NodeProperty< Size >& desc
205  = descendants.insert(modif.tail(), NodeProperty< Size >()).second;
206  restrictWeightedSet__(desc, descendants__[modif.tail()], extremities);
207  }
208 
209  if (!ancestors.exists(modif.head())) {
210  NodeProperty< Size >& anc
211  = ancestors.insert(modif.head(), NodeProperty< Size >()).second;
212  restrictWeightedSet__(anc, ancestors__[modif.head()], extremities);
213 
214  NodeProperty< Size >& desc
215  = descendants.insert(modif.head(), NodeProperty< Size >()).second;
216  restrictWeightedSet__(desc, descendants__[modif.head()], extremities);
217  }
218  }
219 
220  // we apply all the suppressions:
221  // assume that the modif concerns arc (X,Y). Then, after removing
222  // arc (X,Y), the sets of descendants of the nodes T in
223  // ( X + ancestors (X) ) are updated by subtracting the number of paths
224  // leading to Y and its set of descendants. These numbers are equal to
225  // the numbers found in descendants[X] * the numbers of paths leading
226  // to X, i.e., descendants[T][X].
227  // By symmetry, the set of ancestors of nodes T in ( Y + descendants (Y) )
228  // are
229  // updated by subtracting the number of paths leading to X and its
230  // ancestors.
231  for (auto iter = deletions.cbegin(); iter != deletions.cend(); ++iter) {
232  const Arc& arc = iter.key();
233  const NodeId tail = arc.tail();
234  const NodeProperty< Size >& anc_tail = ancestors[tail];
235  const NodeId head = arc.head();
236  const NodeProperty< Size >& desc_head = descendants[head];
237 
238  // update the set of descendants
239  NodeProperty< Size > set_to_del = desc_head;
240  set_to_del.insert(head, 1);
241  delWeightedSet__(descendants[tail], set_to_del, 1);
242 
243  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
244  delWeightedSet__(descendants[iter.key()],
245  set_to_del,
246  descendants[iter.key()][tail]);
247  }
248 
249  // update the set of ancestors
250  set_to_del = anc_tail;
251  set_to_del.insert(tail, 1);
252  delWeightedSet__(ancestors[head], set_to_del, 1);
253 
254  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
255  delWeightedSet__(ancestors[iter.key()],
256  set_to_del,
257  ancestors[iter.key()][head]);
258  }
259  }
260 
261  // now we apply all the additions of arcs (including the additions after
262  // arc reversals). After adding arc (X,Y), the set of ancestors T of Y and
263  // its
264  // descendants shall be updated by adding the number of paths leading to
265  // X and its ancestors, i.e., ancestors[X] * the number of paths leading
266  // to Y, i.e., ancestors[T][Y]. Similarly, the set of descendants of X and
267  // its
268  // ancestors should be updated by adding the number of paths leading to Y
269  // and
270  // its descendants. Finally, an arc (X,Y) can be added if and
271  // only if Y does not belong to the ancestor set of X
272  for (auto iter = additions.cbegin(); iter != additions.cend(); ++iter) {
273  const Arc& arc = iter.key();
274  NodeId tail = arc.tail();
275  NodeId head = arc.head();
276 
277  const NodeProperty< Size >& anc_tail = ancestors[tail];
278 
279  if (anc_tail.exists(head)) { return true; }
280 
281  const NodeProperty< Size >& desc_head = descendants[head];
282 
283  // update the set of ancestors
284  NodeProperty< Size > set_to_add = anc_tail;
285  set_to_add.insert(tail, 1);
286  addWeightedSet__(ancestors[head], set_to_add, 1);
287 
288  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
289  addWeightedSet__(ancestors[iter.key()],
290  set_to_add,
291  ancestors[iter.key()][head]);
292  }
293 
294  // update the set of descendants
295  set_to_add = desc_head;
296  set_to_add.insert(head, 1);
297  addWeightedSet__(descendants[tail], set_to_add, 1);
298 
299  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
300  addWeightedSet__(descendants[iter.key()],
301  set_to_add,
302  descendants[iter.key()][tail]);
303  }
304  }
305 
306  return false;
307  }
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
void restrictWeightedSet__(NodeProperty< Size > &result_set, const NodeProperty< Size > &set_to_restrict, const NodeSet &extrmities) const
put into a weighted nodeset the nodes of another weighted set that belong to a set of arc extremities...
void addWeightedSet__(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:47
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
void delWeightedSet__(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_del, Size multiplier) const
removes a weighted nodeset from another (weights are subtracted)
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:632
#define GUM_ERROR(type, msg)
Definition: exceptions.h:54
+ Here is the call graph for this function:

◆ hasCycleFromReversal()

INLINE bool gum::DAGCycleDetector::hasCycleFromReversal ( NodeId  x,
NodeId  y 
) const
noexcept

indicates wether an arc reversal would create a cycle

worst case complexity: O(1)

Definition at line 303 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

304  {
305  return (ancestors__[y][x] > 1);
306  }
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
+ Here is the call graph for this function:

◆ operator!=()

INLINE bool gum::DAGCycleDetector::operator!= ( const DAGCycleDetector from) const

check the inequality between two DAGCycleDetectors

used essentally for debugging purposes

Definition at line 376 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

376  {
377  return !operator==(from);
378  }
bool operator==(const DAGCycleDetector &from) const
check the equality between two DAGCycleDetectors
+ Here is the call graph for this function:

◆ operator=() [1/2]

INLINE DAGCycleDetector & gum::DAGCycleDetector::operator= ( const DAGCycleDetector from)

copy operator

Definition at line 275 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

275  {
276  if (this != &from) {
277  dag__ = from.dag__;
278  ancestors__ = from.ancestors__;
279  descendants__ = from.descendants__;
280  }
281 
282  return *this;
283  }
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
+ Here is the call graph for this function:

◆ operator=() [2/2]

INLINE DAGCycleDetector & gum::DAGCycleDetector::operator= ( DAGCycleDetector &&  from)

move operator

Definition at line 286 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

286  {
287  if (this != &from) {
288  dag__ = std::move(from.dag__);
289  ancestors__ = std::move(from.ancestors__);
290  descendants__ = std::move(from.descendants__);
291  }
292 
293  return *this;
294  }
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
+ Here is the call graph for this function:

◆ operator==()

INLINE bool gum::DAGCycleDetector::operator== ( const DAGCycleDetector from) const

check the equality between two DAGCycleDetectors

used essentally for debugging purposes

Definition at line 370 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

370  {
371  return ( //( dagmodel__ == from.dagmodel__ ) &&
372  (ancestors__ == from.ancestors__) && (descendants__ == from.descendants__));
373  }
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
+ Here is the call graph for this function:

◆ restrictWeightedSet__()

INLINE void gum::DAGCycleDetector::restrictWeightedSet__ ( NodeProperty< Size > &  result_set,
const NodeProperty< Size > &  set_to_restrict,
const NodeSet extrmities 
) const
private

put into a weighted nodeset the nodes of another weighted set that belong to a set of arc extremities

Definition at line 346 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

349  {
350  for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend();
351  ++iter) {
352  if (extremities.exists(iter.key())) {
353  result_set.insert(iter.key(), iter.val());
354  }
355  }
356  }
+ Here is the call graph for this function:

◆ reverseArc()

INLINE void gum::DAGCycleDetector::reverseArc ( NodeId  x,
NodeId  y 
)

reverses an arc from the DAG

worst case complexity: O(h^2) where h is the height of the DAG

Exceptions
InvalidDirectedCycleif the arc reversal would create a cycle in the dag

Definition at line 359 of file DAGCycleDetector_inl.h.

References gum::Set< Key, Alloc >::emplace().

359  {
360  if (hasCycleFromReversal(tail, head)) {
361  GUM_ERROR(InvalidDirectedCycle,
362  "the arc would create a directed into a DAG");
363  }
364 
365  eraseArc(tail, head);
366  addArc(head, tail);
367  }
void eraseArc(NodeId x, NodeId y)
removes an arc from the current DAG
void addArc(NodeId x, NodeId y)
adds a new arc to the current DAG
bool hasCycleFromReversal(NodeId x, NodeId y) const noexcept
indicates wether an arc reversal would create a cycle
#define GUM_ERROR(type, msg)
Definition: exceptions.h:54
+ Here is the call graph for this function:

◆ setDAG()

void gum::DAGCycleDetector::setDAG ( const DAG dag)

sets the initial DAG from which changes shall be applied

Definition at line 45 of file DAGCycleDetector.cpp.

References gum::Set< Key, Alloc >::emplace().

45  {
46  // sets the dag
47  dag__ = dag;
48 
49  // get the set of roots and leaves of the dag
50  List< NodeId > roots, leaves;
51  NodeProperty< Size > nb_parents, nb_children;
52 
53  for (const auto node: dag) {
54  Size nb_ch = dag.children(node).size();
55  nb_children.insert(node, nb_ch);
56 
57  if (!nb_ch) leaves.insert(node);
58 
59  Size nb_pa = dag.parents(node).size();
60  nb_parents.insert(node, nb_pa);
61 
62  if (!nb_pa) roots.insert(node);
63  }
64 
65  // recompute the set of ancestors
66  NodeProperty< Size > empty_set;
67  ancestors__.clear();
68 
69  for (NodeId node: dag) {
70  ancestors__.insert(node, empty_set);
71  }
72 
73  while (!roots.empty()) {
74  // get a node and update the ancestors of its children
75  NodeId node = roots.front();
76  NodeProperty< Size > node_ancestors = ancestors__[node];
77  node_ancestors.insert(node, 1);
78  const NodeSet& node_children = dag.children(node);
79  roots.popFront();
80 
81  for (const auto ch: node_children) {
82  addWeightedSet__(ancestors__[ch], node_ancestors, 1);
83  --nb_parents[ch];
84 
85  if (!nb_parents[ch]) { roots.insert(ch); }
86  }
87  }
88 
89  // recompute the set of descendants
90  descendants__.clear();
91 
92  for (const auto node: dag) {
93  descendants__.insert(node, empty_set);
94  }
95 
96  while (!leaves.empty()) {
97  // get a node and update the descendants of its parents
98  NodeId node = leaves.front();
99  NodeProperty< Size > node_descendants = descendants__[node];
100  node_descendants.insert(node, 1);
101  const NodeSet& node_parents = dag.parents(node);
102  leaves.popFront();
103 
104  for (const auto pa: node_parents) {
105  addWeightedSet__(descendants__[pa], node_descendants, 1);
106  --nb_children[pa];
107 
108  if (!nb_children[pa]) { leaves.insert(pa); }
109  }
110  }
111  }
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
void addWeightedSet__(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
DiGraph dag__
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > ancestors__
the set of ancestors of each node in the dag
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:47
NodeProperty< NodeProperty< Size > > descendants__
the set of descendants of each node in the dag
Size NodeId
Type for node ids.
Definition: graphElements.h:97
+ Here is the call graph for this function:

Member Data Documentation

◆ ancestors__

NodeProperty< NodeProperty< Size > > gum::DAGCycleDetector::ancestors__
private

the set of ancestors of each node in the dag

for each ancestor, we keep track of the number of paths leading to it

Definition at line 324 of file DAGCycleDetector.h.

◆ dag__

DiGraph gum::DAGCycleDetector::dag__
private

the initial dag from which modifications are applied

Definition at line 320 of file DAGCycleDetector.h.

◆ descendants__

NodeProperty< NodeProperty< Size > > gum::DAGCycleDetector::descendants__
private

the set of descendants of each node in the dag

for each ancestor, we keep track of the number of paths leading to it

Definition at line 328 of file DAGCycleDetector.h.


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