aGrUM  0.14.2
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 59 of file DAGCycleDetector.h.

Member Enumeration Documentation

◆ ChangeType

Enumerator
ARC_ADDITION 
ARC_DELETION 
ARC_REVERSAL 

Definition at line 62 of file DAGCycleDetector.h.

Constructor & Destructor Documentation

◆ DAGCycleDetector() [1/3]

INLINE gum::DAGCycleDetector::DAGCycleDetector ( )
noexcept

default constructor

Definition at line 242 of file DAGCycleDetector_inl.h.

242  {
243  GUM_CONSTRUCTOR(DAGCycleDetector);
244  }
DAGCycleDetector() noexcept
default constructor

◆ DAGCycleDetector() [2/3]

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

copy constructor

Definition at line 247 of file DAGCycleDetector_inl.h.

247  :
248  __dag(from.__dag), __ancestors(from.__ancestors),
249  __descendants(from.__descendants) {
250  GUM_CONS_CPY(DAGCycleDetector);
251  }
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 254 of file DAGCycleDetector_inl.h.

254  :
255  __dag(std::move(from.__dag)), __ancestors(std::move(from.__ancestors)),
256  __descendants(std::move(from.__descendants)) {
257  GUM_CONS_MOV(DAGCycleDetector);
258  }
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 261 of file DAGCycleDetector_inl.h.

References operator=().

261  {
262  GUM_DESTRUCTOR(DAGCycleDetector);
263  }
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 308 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().

310  {
311  for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
312  if (nodeset.exists(iter.key())) {
313  nodeset[iter.key()] += iter.val() * multiplier;
314  } else {
315  nodeset.insert(iter.key(), iter.val() * multiplier);
316  }
317  }
318  }
+ 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 322 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().

324  {
325  for (auto iter = set_to_del.cbegin(); iter != set_to_del.cend(); ++iter) {
326  if (nodeset.exists(iter.key())) {
327  Size& weight = nodeset[iter.key()];
328  weight -= iter.val() * multiplier;
329 
330  if (!weight) { nodeset.erase(iter.key()); }
331  }
332  }
333  }
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
+ 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 338 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().

341  {
342  for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend();
343  ++iter) {
344  if (extremities.exists(iter.key())) {
345  result_set.insert(iter.key(), iter.val());
346  }
347  }
348  }
+ 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 303 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().

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

342  {
343  // check that the arc exists
344  if (!__dag.existsArc(tail, head)) return;
345 
346  __dag.eraseArc(Arc(tail, head));
347 
348  // we apply the deletion of the arc as done in method
349  // hasCycleFromModifications
350  const NodeProperty< Size >& anc_tail = __ancestors[tail];
351  const NodeProperty< Size >& desc_head = __descendants[head];
352 
353  // update the set of descendants
354  NodeProperty< Size > set_to_del = desc_head;
355  set_to_del.insert(head, 1);
356  __delWeightedSet(__descendants[tail], set_to_del, 1);
357 
358  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
360  __descendants[iter.key()], set_to_del, __descendants[iter.key()][tail]);
361  }
362 
363  // update the set of ancestors
364  set_to_del = anc_tail;
365  set_to_del.insert(tail, 1);
366  __delWeightedSet(__ancestors[head], set_to_del, 1);
367 
368  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
370  __ancestors[iter.key()], set_to_del, __ancestors[iter.key()][head]);
371  }
372  }
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 289 of file DAGCycleDetector_inl.h.

References __descendants.

Referenced by addArc().

290  {
291  return __descendants[y].exists(x);
292  }
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 301 of file DAGCycleDetector_inl.h.

302  {
303  return false;
304  }

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

113  {
114  // first, we separate deletions and insertions (reversals are cut into
115  // a deletion + an insertion) and we check that no insertion also exists
116  // as a deletion (if so, we remove both operations). In addition, if
117  // we try to add an arc (X,X) we return that it induces a cycle
118  HashTable< Arc, Size > deletions(Size(modifs.size()));
119  HashTable< Arc, Size > additions(Size(modifs.size()));
120 
121  for (const auto& modif : modifs) {
122  Arc arc(modif.tail(), modif.head());
123 
124  switch (modif.type()) {
126  if (deletions.exists(arc))
127  ++deletions[arc];
128  else
129  deletions.insert(arc, 1);
130 
131  break;
132 
134  if (modif.tail() == modif.head()) return true;
135 
136  if (additions.exists(arc))
137  ++additions[arc];
138  else
139  additions.insert(arc, 1);
140 
141  break;
142 
144  if (deletions.exists(arc))
145  ++deletions[arc];
146  else
147  deletions.insert(arc, 1);
148 
149  arc = Arc(modif.head(), modif.tail());
150 
151  if (additions.exists(arc))
152  ++additions[arc];
153  else
154  additions.insert(arc, 1);
155 
156  break;
157 
158  default: GUM_ERROR(OperationNotAllowed, "undefined change type");
159  }
160  }
161 
162  for (auto iter = additions.beginSafe(); // safe iterator needed here
163  iter != additions.endSafe();
164  ++iter) {
165  if (deletions.exists(iter.key())) {
166  Size& nb_del = deletions[iter.key()];
167  Size& nb_add = iter.val();
168 
169  if (nb_del > nb_add) {
170  additions.erase(iter);
171  nb_del -= nb_add;
172  } else if (nb_del < nb_add) {
173  deletions.erase(iter.key());
174  nb_add -= nb_del;
175  } else {
176  deletions.erase(iter.key());
177  additions.erase(iter);
178  }
179  }
180  }
181 
182  // get the set of nodes involved in the modifications
183  NodeSet extremities;
184 
185  for (const auto& modif : modifs) {
186  extremities.insert(modif.tail());
187  extremities.insert(modif.head());
188  }
189 
190  // we now retrieve the set of ancestors and descendants of all the
191  // extremities of the arcs involved in the modifications. We also
192  // keep track of all the children and parents of these nodes
193  NodeProperty< NodeProperty< Size > > ancestors, descendants;
194 
195  for (const auto& modif : modifs) {
196  if (!ancestors.exists(modif.tail())) {
197  NodeProperty< Size >& anc =
198  ancestors.insert(modif.tail(), NodeProperty< Size >()).second;
199  __restrictWeightedSet(anc, __ancestors[modif.tail()], extremities);
200 
201  NodeProperty< Size >& desc =
202  descendants.insert(modif.tail(), NodeProperty< Size >()).second;
203  __restrictWeightedSet(desc, __descendants[modif.tail()], extremities);
204  }
205 
206  if (!ancestors.exists(modif.head())) {
207  NodeProperty< Size >& anc =
208  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) {
242  descendants[iter.key()], set_to_del, descendants[iter.key()][tail]);
243  }
244 
245  // update the set of ancestors
246  set_to_del = anc_tail;
247  set_to_del.insert(tail, 1);
248  __delWeightedSet(ancestors[head], set_to_del, 1);
249 
250  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
252  ancestors[iter.key()], set_to_del, ancestors[iter.key()][head]);
253  }
254  }
255 
256  // now we apply all the additions of arcs (including the additions after
257  // arc reversals). After adding arc (X,Y), the set of ancestors T of Y and
258  // its
259  // descendants shall be updated by adding the number of paths leading to
260  // X and its ancestors, i.e., ancestors[X] * the number of paths leading
261  // to Y, i.e., ancestors[T][Y]. Similarly, the set of descendants of X and
262  // its
263  // ancestors should be updated by adding the number of paths leading to Y
264  // and
265  // its descendants. Finally, an arc (X,Y) can be added if and
266  // only if Y does not belong to the ancestor set of X
267  for (auto iter = additions.cbegin(); iter != additions.cend(); ++iter) {
268  const Arc& arc = iter.key();
269  NodeId tail = arc.tail();
270  NodeId head = arc.head();
271 
272  const NodeProperty< Size >& anc_tail = ancestors[tail];
273 
274  if (anc_tail.exists(head)) { return true; }
275 
276  const NodeProperty< Size >& desc_head = descendants[head];
277 
278  // update the set of ancestors
279  NodeProperty< Size > set_to_add = anc_tail;
280  set_to_add.insert(tail, 1);
281  __addWeightedSet(ancestors[head], set_to_add, 1);
282 
283  for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
285  ancestors[iter.key()], set_to_add, ancestors[iter.key()][head]);
286  }
287 
288  // update the set of descendants
289  set_to_add = desc_head;
290  set_to_add.insert(head, 1);
291  __addWeightedSet(descendants[tail], set_to_add, 1);
292 
293  for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
295  descendants[iter.key()], set_to_add, descendants[iter.key()][tail]);
296  }
297  }
298 
299  return false;
300  }
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:45
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:610
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
+ 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 295 of file DAGCycleDetector_inl.h.

References __ancestors.

Referenced by reverseArc().

296  {
297  return (__ancestors[y][x] > 1);
298  }
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 368 of file DAGCycleDetector_inl.h.

References operator==().

368  {
369  return !operator==(from);
370  }
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 267 of file DAGCycleDetector_inl.h.

References __ancestors, __dag, and __descendants.

Referenced by ~DAGCycleDetector().

267  {
268  if (this != &from) {
269  __dag = from.__dag;
270  __ancestors = from.__ancestors;
271  __descendants = from.__descendants;
272  }
273 
274  return *this;
275  }
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 278 of file DAGCycleDetector_inl.h.

References __ancestors, __dag, and __descendants.

278  {
279  if (this != &from) {
280  __dag = std::move(from.__dag);
281  __ancestors = std::move(from.__ancestors);
282  __descendants = std::move(from.__descendants);
283  }
284 
285  return *this;
286  }
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 362 of file DAGCycleDetector_inl.h.

References __ancestors, and __descendants.

Referenced by operator!=().

362  {
363  return ( //( __dagmodel == from.__dagmodel ) &&
364  (__ancestors == from.__ancestors) && (__descendants == from.__descendants));
365  }
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 351 of file DAGCycleDetector_inl.h.

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

351  {
352  if (hasCycleFromReversal(tail, head)) {
353  GUM_ERROR(InvalidDirectedCycle,
354  "the arc would create a directed into a DAG");
355  }
356 
357  eraseArc(tail, head);
358  addArc(head, tail);
359  }
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:52
+ 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 43 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().

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