aGrUM  0.20.3
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 226 of file DAGCycleDetector_inl.h.

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

226 { GUM_CONSTRUCTOR(DAGCycleDetector); }
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 229 of file DAGCycleDetector_inl.h.

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

229  :
230  _dag_(from._dag_), _ancestors_(from._ancestors_), _descendants_(from._descendants_) {
231  GUM_CONS_CPY(DAGCycleDetector);
232  }
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 235 of file DAGCycleDetector_inl.h.

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

235  :
236  _dag_(std::move(from._dag_)), _ancestors_(std::move(from._ancestors_)),
237  _descendants_(std::move(from._descendants_)) {
238  GUM_CONS_MOV(DAGCycleDetector);
239  }
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 242 of file DAGCycleDetector_inl.h.

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

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

Member Function Documentation

◆ _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 284 of file DAGCycleDetector_inl.h.

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

286  {
287  for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
288  if (nodeset.exists(iter.key())) {
289  nodeset[iter.key()] += iter.val() * multiplier;
290  } else {
291  nodeset.insert(iter.key(), iter.val() * multiplier);
292  }
293  }
294  }
+ 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 298 of file DAGCycleDetector_inl.h.

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

300  {
301  for (auto iter = set_to_del.cbegin(); iter != set_to_del.cend(); ++iter) {
302  if (nodeset.exists(iter.key())) {
303  Size& weight = nodeset[iter.key()];
304  weight -= iter.val() * multiplier;
305 
306  if (!weight) { nodeset.erase(iter.key()); }
307  }
308  }
309  }
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:47
+ 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 314 of file DAGCycleDetector_inl.h.

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

316  {
317  for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend(); ++iter) {
318  if (extremities.exists(iter.key())) { result_set.insert(iter.key(), iter.val()); }
319  }
320  }
+ Here is the call graph for this function:

◆ 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 299 of file DAGCycleDetector.cpp.

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

299  {
300  // check that the arc does not already exist
301  if (_dag_.existsArc(tail, head)) return;
302 
303  // check that the arc would not create a cycle
304  if (hasCycleFromAddition(tail, head)) {
305  GUM_ERROR(InvalidDirectedCycle, "the arc would create a directed into a DAG")
306  }
307 
308  _dag_.addArc(tail, head);
309 
310  // now we apply the addition of the arc as done in method
311  // hasCycleFromModifications
312  const NodeProperty< Size >& anc_tail = _ancestors_[tail];
313  const NodeProperty< Size >& desc_head = _descendants_[head];
314 
315  // update the set of ancestors
316  NodeProperty< Size > set_to_add = anc_tail;
317  set_to_add.insert(tail, 1);
318  _addWeightedSet_(_ancestors_[head], set_to_add, 1);
319 
320  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
321  _addWeightedSet_(_ancestors_[iter.key()], set_to_add, _ancestors_[iter.key()][head]);
322  }
323 
324  // update the set of descendants
325  set_to_add = desc_head;
326  set_to_add.insert(head, 1);
327  _addWeightedSet_(_descendants_[tail], set_to_add, 1);
328 
329  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
330  _addWeightedSet_(_descendants_[iter.key()], set_to_add, _descendants_[iter.key()][tail]);
331  }
332  }
DiGraph _dag_
the initial dag from which modifications are applied
virtual void addArc(const NodeId tail, const NodeId head)
insert a new arc into the directed graph
Definition: diGraph_inl.h:34
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 _addWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
bool hasCycleFromAddition(NodeId x, NodeId y) const noexcept
indicates whether an arc addition would create a cycle
bool existsArc(const Arc &arc) const
indicates whether a given arc exists
#define GUM_ERROR(type, msg)
Definition: exceptions.h:51
+ 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 335 of file DAGCycleDetector.cpp.

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

335  {
336  // check that the arc exists
337  if (!_dag_.existsArc(tail, head)) return;
338 
339  _dag_.eraseArc(Arc(tail, head));
340 
341  // we apply the deletion of the arc as done in method
342  // hasCycleFromModifications
343  const NodeProperty< Size >& anc_tail = _ancestors_[tail];
344  const NodeProperty< Size >& desc_head = _descendants_[head];
345 
346  // update the set of descendants
347  NodeProperty< Size > set_to_del = desc_head;
348  set_to_del.insert(head, 1);
349  _delWeightedSet_(_descendants_[tail], set_to_del, 1);
350 
351  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
352  _delWeightedSet_(_descendants_[iter.key()], set_to_del, _descendants_[iter.key()][tail]);
353  }
354 
355  // update the set of ancestors
356  set_to_del = anc_tail;
357  set_to_del.insert(tail, 1);
358  _delWeightedSet_(_ancestors_[head], set_to_del, 1);
359 
360  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
361  _delWeightedSet_(_ancestors_[iter.key()], set_to_del, _ancestors_[iter.key()][head]);
362  }
363  }
DiGraph _dag_
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > _ancestors_
the set of ancestors of each node in the dag
virtual void eraseArc(const Arc &arc)
removes an arc from the ArcGraphPart
void _delWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_del, Size multiplier) const
removes a weighted nodeset from another (weights are subtracted)
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
+ 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 268 of file DAGCycleDetector_inl.h.

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

268  {
269  return _descendants_[y].exists(x);
270  }
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 278 of file DAGCycleDetector_inl.h.

278  {
279  return false;
280  }

◆ 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().

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

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

273  {
274  return (_ancestors_[y][x] > 1);
275  }
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 339 of file DAGCycleDetector_inl.h.

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

339  {
340  return !operator==(from);
341  }
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 246 of file DAGCycleDetector_inl.h.

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

246  {
247  if (this != &from) {
248  _dag_ = from._dag_;
249  _ancestors_ = from._ancestors_;
250  _descendants_ = from._descendants_;
251  }
252 
253  return *this;
254  }
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 257 of file DAGCycleDetector_inl.h.

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

257  {
258  if (this != &from) {
259  _dag_ = std::move(from._dag_);
260  _ancestors_ = std::move(from._ancestors_);
261  _descendants_ = std::move(from._descendants_);
262  }
263 
264  return *this;
265  }
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 333 of file DAGCycleDetector_inl.h.

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

333  {
334  return ( //( _dagmodel_ == from. _dagmodel_ ) &&
335  (_ancestors_ == from._ancestors_) && (_descendants_ == from._descendants_));
336  }
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:

◆ 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 323 of file DAGCycleDetector_inl.h.

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

323  {
324  if (hasCycleFromReversal(tail, head)) {
325  GUM_ERROR(InvalidDirectedCycle, "the arc would create a directed into a DAG")
326  }
327 
328  eraseArc(tail, head);
329  addArc(head, tail);
330  }
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:51
+ 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  }
DiGraph _dag_
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > _ancestors_
the set of ancestors of each node in the dag
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
NodeProperty< NodeProperty< Size > > _descendants_
the set of descendants of each node in the dag
void _addWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:47
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: