aGrUM  0.16.0
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 62 of file DAGCycleDetector.h.

Member Enumeration Documentation

◆ ChangeType

Enumerator
ARC_ADDITION 
ARC_DELETION 
ARC_REVERSAL 

Definition at line 65 of file DAGCycleDetector.h.

Constructor & Destructor Documentation

◆ DAGCycleDetector() [1/3]

INLINE gum::DAGCycleDetector::DAGCycleDetector ( )
noexcept

default constructor

Definition at line 245 of file DAGCycleDetector_inl.h.

245  {
246  GUM_CONSTRUCTOR(DAGCycleDetector);
247  }
DAGCycleDetector() noexcept
default constructor

◆ DAGCycleDetector() [2/3]

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

copy constructor

Definition at line 250 of file DAGCycleDetector_inl.h.

250  :
251  __dag(from.__dag), __ancestors(from.__ancestors),
252  __descendants(from.__descendants) {
253  GUM_CONS_CPY(DAGCycleDetector);
254  }
DiGraph __dag
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag
DAGCycleDetector() noexcept
default constructor

◆ DAGCycleDetector() [3/3]

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

move constructor

Definition at line 257 of file DAGCycleDetector_inl.h.

257  :
258  __dag(std::move(from.__dag)), __ancestors(std::move(from.__ancestors)),
259  __descendants(std::move(from.__descendants)) {
260  GUM_CONS_MOV(DAGCycleDetector);
261  }
DiGraph __dag
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag
DAGCycleDetector() noexcept
default constructor

◆ ~DAGCycleDetector()

INLINE gum::DAGCycleDetector::~DAGCycleDetector ( )

destructor

Definition at line 264 of file DAGCycleDetector_inl.h.

References operator=().

264  {
265  GUM_DESTRUCTOR(DAGCycleDetector);
266  }
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 311 of file DAGCycleDetector_inl.h.

References gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::HashTable< Key, Val, Alloc >::exists(), and gum::HashTable< Key, Val, Alloc >::insert().

Referenced by addArc(), hasCycleFromModifications(), and setDAG().

313  {
314  for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
315  if (nodeset.exists(iter.key())) {
316  nodeset[iter.key()] += iter.val() * multiplier;
317  } else {
318  nodeset.insert(iter.key(), iter.val() * multiplier);
319  }
320  }
321  }
+ Here is the call graph for this function:
+ Here is the caller 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 325 of file DAGCycleDetector_inl.h.

References gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::HashTable< Key, Val, Alloc >::erase(), and gum::HashTable< Key, Val, Alloc >::exists().

Referenced by eraseArc(), and hasCycleFromModifications().

327  {
328  for (auto iter = set_to_del.cbegin(); iter != set_to_del.cend(); ++iter) {
329  if (nodeset.exists(iter.key())) {
330  Size& weight = nodeset[iter.key()];
331  weight -= iter.val() * multiplier;
332 
333  if (!weight) { nodeset.erase(iter.key()); }
334  }
335  }
336  }
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:48
+ Here is the call graph for this function:
+ Here is the caller 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 341 of file DAGCycleDetector_inl.h.

References gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::Set< Key, Alloc >::exists(), and gum::HashTable< Key, Val, Alloc >::insert().

Referenced by hasCycleFromModifications().

344  {
345  for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend();
346  ++iter) {
347  if (extremities.exists(iter.key())) {
348  result_set.insert(iter.key(), iter.val());
349  }
350  }
351  }
+ Here is the call graph for this function:
+ Here is the caller 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 306 of file DAGCycleDetector.cpp.

References __addWeightedSet(), __ancestors, __dag, __descendants, gum::DiGraph::addArc(), gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::ArcGraphPart::existsArc(), GUM_ERROR, hasCycleFromAddition(), and gum::HashTable< Key, Val, Alloc >::insert().

Referenced by reverseArc().

306  {
307  // check that the arc does not already exist
308  if (__dag.existsArc(tail, head)) return;
309 
310  // check that the arc would not create a cycle
311  if (hasCycleFromAddition(tail, head)) {
312  GUM_ERROR(InvalidDirectedCycle,
313  "the arc would create a directed into a DAG");
314  }
315 
316  __dag.addArc(tail, head);
317 
318  // now we apply the addition of the arc as done in method
319  // hasCycleFromModifications
320  const NodeProperty< Size >& anc_tail = __ancestors[tail];
321  const NodeProperty< Size >& desc_head = __descendants[head];
322 
323  // update the set of ancestors
324  NodeProperty< Size > set_to_add = anc_tail;
325  set_to_add.insert(tail, 1);
326  __addWeightedSet(__ancestors[head], set_to_add, 1);
327 
328  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
330  __ancestors[iter.key()], set_to_add, __ancestors[iter.key()][head]);
331  }
332 
333  // update the set of descendants
334  set_to_add = desc_head;
335  set_to_add.insert(head, 1);
336  __addWeightedSet(__descendants[tail], set_to_add, 1);
337 
338  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
340  __descendants[iter.key()], set_to_add, __descendants[iter.key()][tail]);
341  }
342  }
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:35
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
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors 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:55
+ Here is the call graph for this function:
+ Here is the caller 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 345 of file DAGCycleDetector.cpp.

References __ancestors, __dag, __delWeightedSet(), __descendants, gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::ArcGraphPart::eraseArc(), gum::ArcGraphPart::existsArc(), and gum::HashTable< Key, Val, Alloc >::insert().

Referenced by reverseArc().

345  {
346  // check that the arc exists
347  if (!__dag.existsArc(tail, head)) return;
348 
349  __dag.eraseArc(Arc(tail, head));
350 
351  // we apply the deletion of the arc as done in method
352  // hasCycleFromModifications
353  const NodeProperty< Size >& anc_tail = __ancestors[tail];
354  const NodeProperty< Size >& desc_head = __descendants[head];
355 
356  // update the set of descendants
357  NodeProperty< Size > set_to_del = desc_head;
358  set_to_del.insert(head, 1);
359  __delWeightedSet(__descendants[tail], set_to_del, 1);
360 
361  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
363  __descendants[iter.key()], set_to_del, __descendants[iter.key()][tail]);
364  }
365 
366  // update the set of ancestors
367  set_to_del = anc_tail;
368  set_to_del.insert(tail, 1);
369  __delWeightedSet(__ancestors[head], set_to_del, 1);
370 
371  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
373  __ancestors[iter.key()], set_to_del, __ancestors[iter.key()][head]);
374  }
375  }
DiGraph __dag
the initial dag from which modifications are applied
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
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors 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:
+ Here is the caller 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 292 of file DAGCycleDetector_inl.h.

References __descendants.

Referenced by addArc().

293  {
294  return __descendants[y].exists(x);
295  }
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
+ Here is the caller 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 304 of file DAGCycleDetector_inl.h.

305  {
306  return false;
307  }

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

References __addWeightedSet(), __ancestors, __delWeightedSet(), __descendants, __restrictWeightedSet(), ARC_ADDITION, ARC_DELETION, ARC_REVERSAL, gum::HashTable< Key, Val, Alloc >::beginSafe(), gum::HashTable< Key, Val, Alloc >::cbegin(), gum::HashTable< Key, Val, Alloc >::cend(), gum::HashTable< Key, Val, Alloc >::endSafe(), gum::HashTable< Key, Val, Alloc >::erase(), gum::HashTable< Key, Val, Alloc >::exists(), GUM_ERROR, gum::Arc::head(), gum::Set< Key, Alloc >::insert(), gum::HashTable< Key, Val, Alloc >::insert(), and gum::Arc::tail().

116  {
117  // first, we separate deletions and insertions (reversals are cut into
118  // a deletion + an insertion) and we check that no insertion also exists
119  // as a deletion (if so, we remove both operations). In addition, if
120  // we try to add an arc (X,X) we return that it induces a cycle
121  HashTable< Arc, Size > deletions(Size(modifs.size()));
122  HashTable< Arc, Size > additions(Size(modifs.size()));
123 
124  for (const auto& modif : modifs) {
125  Arc arc(modif.tail(), modif.head());
126 
127  switch (modif.type()) {
129  if (deletions.exists(arc))
130  ++deletions[arc];
131  else
132  deletions.insert(arc, 1);
133 
134  break;
135 
137  if (modif.tail() == modif.head()) return true;
138 
139  if (additions.exists(arc))
140  ++additions[arc];
141  else
142  additions.insert(arc, 1);
143 
144  break;
145 
147  if (deletions.exists(arc))
148  ++deletions[arc];
149  else
150  deletions.insert(arc, 1);
151 
152  arc = Arc(modif.head(), modif.tail());
153 
154  if (additions.exists(arc))
155  ++additions[arc];
156  else
157  additions.insert(arc, 1);
158 
159  break;
160 
161  default: 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) {
245  descendants[iter.key()], set_to_del, descendants[iter.key()][tail]);
246  }
247 
248  // update the set of ancestors
249  set_to_del = anc_tail;
250  set_to_del.insert(tail, 1);
251  __delWeightedSet(ancestors[head], set_to_del, 1);
252 
253  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
255  ancestors[iter.key()], set_to_del, ancestors[iter.key()][head]);
256  }
257  }
258 
259  // now we apply all the additions of arcs (including the additions after
260  // arc reversals). After adding arc (X,Y), the set of ancestors T of Y and
261  // its
262  // descendants shall be updated by adding the number of paths leading to
263  // X and its ancestors, i.e., ancestors[X] * the number of paths leading
264  // to Y, i.e., ancestors[T][Y]. Similarly, the set of descendants of X and
265  // its
266  // ancestors should be updated by adding the number of paths leading to Y
267  // and
268  // its descendants. Finally, an arc (X,Y) can be added if and
269  // only if Y does not belong to the ancestor set of X
270  for (auto iter = additions.cbegin(); iter != additions.cend(); ++iter) {
271  const Arc& arc = iter.key();
272  NodeId tail = arc.tail();
273  NodeId head = arc.head();
274 
275  const NodeProperty< Size >& anc_tail = ancestors[tail];
276 
277  if (anc_tail.exists(head)) { return true; }
278 
279  const NodeProperty< Size >& desc_head = descendants[head];
280 
281  // update the set of ancestors
282  NodeProperty< Size > set_to_add = anc_tail;
283  set_to_add.insert(tail, 1);
284  __addWeightedSet(ancestors[head], set_to_add, 1);
285 
286  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
288  ancestors[iter.key()], set_to_add, ancestors[iter.key()][head]);
289  }
290 
291  // update the set of descendants
292  set_to_add = desc_head;
293  set_to_add.insert(head, 1);
294  __addWeightedSet(descendants[tail], set_to_add, 1);
295 
296  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
298  descendants[iter.key()], set_to_add, descendants[iter.key()][tail]);
299  }
300  }
301 
302  return false;
303  }
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)
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
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:48
Size NodeId
Type for node ids.
Definition: graphElements.h:98
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:613
#define GUM_ERROR(type, msg)
Definition: exceptions.h:55
+ 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 298 of file DAGCycleDetector_inl.h.

References __ancestors.

Referenced by reverseArc().

299  {
300  return (__ancestors[y][x] > 1);
301  }
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag
+ Here is the caller 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 371 of file DAGCycleDetector_inl.h.

References operator==().

371  {
372  return !operator==(from);
373  }
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 270 of file DAGCycleDetector_inl.h.

References __ancestors, __dag, and __descendants.

Referenced by ~DAGCycleDetector().

270  {
271  if (this != &from) {
272  __dag = from.__dag;
273  __ancestors = from.__ancestors;
274  __descendants = from.__descendants;
275  }
276 
277  return *this;
278  }
DiGraph __dag
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag
+ Here is the caller graph for this function:

◆ operator=() [2/2]

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

move operator

Definition at line 281 of file DAGCycleDetector_inl.h.

References __ancestors, __dag, and __descendants.

281  {
282  if (this != &from) {
283  __dag = std::move(from.__dag);
284  __ancestors = std::move(from.__ancestors);
285  __descendants = std::move(from.__descendants);
286  }
287 
288  return *this;
289  }
DiGraph __dag
the initial dag from which modifications are applied
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag

◆ operator==()

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

check the equality between two DAGCycleDetectors

used essentally for debugging purposes

Definition at line 365 of file DAGCycleDetector_inl.h.

References __ancestors, and __descendants.

Referenced by operator!=().

365  {
366  return ( //( __dagmodel == from.__dagmodel ) &&
367  (__ancestors == from.__ancestors) && (__descendants == from.__descendants));
368  }
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
NodeProperty< NodeProperty< Size > > __ancestors
the set of ancestors of each node in the dag
+ Here is the caller 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 354 of file DAGCycleDetector_inl.h.

References addArc(), eraseArc(), GUM_ERROR, and hasCycleFromReversal().

354  {
355  if (hasCycleFromReversal(tail, head)) {
356  GUM_ERROR(InvalidDirectedCycle,
357  "the arc would create a directed into a DAG");
358  }
359 
360  eraseArc(tail, head);
361  addArc(head, tail);
362  }
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:55
+ 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 46 of file DAGCycleDetector.cpp.

References __addWeightedSet(), __ancestors, __dag, __descendants, gum::List< Val, Alloc >::empty(), gum::List< Val, Alloc >::front(), gum::List< Val, Alloc >::insert(), gum::HashTable< Key, Val, Alloc >::insert(), gum::List< Val, Alloc >::popFront(), and gum::HashTable< Key, Val, Alloc >::size().

Referenced by gum::learning::StructuralConstraintDAG::StructuralConstraintDAG().

46  {
47  // sets the dag
48  __dag = dag;
49 
50  // get the set of roots and leaves of the dag
51  List< NodeId > roots, leaves;
52  NodeProperty< Size > nb_parents, nb_children;
53 
54  for (const auto node : dag) {
55  Size nb_ch = dag.children(node).size();
56  nb_children.insert(node, nb_ch);
57 
58  if (!nb_ch) leaves.insert(node);
59 
60  Size nb_pa = dag.parents(node).size();
61  nb_parents.insert(node, nb_pa);
62 
63  if (!nb_pa) roots.insert(node);
64  }
65 
66  // recompute the set of ancestors
67  NodeProperty< Size > empty_set;
68  __ancestors.clear();
69 
70  for (NodeId node : dag) {
71  __ancestors.insert(node, empty_set);
72  }
73 
74  while (!roots.empty()) {
75  // get a node and update the ancestors of its children
76  NodeId node = roots.front();
77  NodeProperty< Size > node_ancestors = __ancestors[node];
78  node_ancestors.insert(node, 1);
79  const NodeSet& node_children = dag.children(node);
80  roots.popFront();
81 
82  for (const auto ch : node_children) {
83  __addWeightedSet(__ancestors[ch], node_ancestors, 1);
84  --nb_parents[ch];
85 
86  if (!nb_parents[ch]) { roots.insert(ch); }
87  }
88  }
89 
90  // recompute the set of descendants
91  __descendants.clear();
92 
93  for (const auto node : dag) {
94  __descendants.insert(node, empty_set);
95  }
96 
97  while (!leaves.empty()) {
98  // get a node and update the descendants of its parents
99  NodeId node = leaves.front();
100  NodeProperty< Size > node_descendants = __descendants[node];
101  node_descendants.insert(node, 1);
102  const NodeSet& node_parents = dag.parents(node);
103  leaves.popFront();
104 
105  for (const auto pa : node_parents) {
106  __addWeightedSet(__descendants[pa], node_descendants, 1);
107  --nb_children[pa];
108 
109  if (!nb_children[pa]) { leaves.insert(pa); }
110  }
111  }
112  }
DiGraph __dag
the initial dag from which modifications are applied
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)
NodeProperty< NodeProperty< Size > > __descendants
the set of descendants of each node in the dag
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:48
Size NodeId
Type for node ids.
Definition: graphElements.h:98
+ Here is the call graph for this function:
+ Here is the caller 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 320 of file DAGCycleDetector.h.

Referenced by addArc(), eraseArc(), hasCycleFromModifications(), hasCycleFromReversal(), operator=(), operator==(), and setDAG().

◆ __dag

DiGraph gum::DAGCycleDetector::__dag
private

the initial dag from which modifications are applied

Definition at line 316 of file DAGCycleDetector.h.

Referenced by addArc(), eraseArc(), operator=(), and setDAG().

◆ __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 324 of file DAGCycleDetector.h.

Referenced by addArc(), eraseArc(), hasCycleFromAddition(), hasCycleFromModifications(), operator=(), operator==(), and setDAG().


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