aGrUM 2.3.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:

Classes

class  Change
 the base class indicating the possible changes More...
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...

Public Types

enum class  ChangeType { ARC_ADDITION , ARC_DELETION , ARC_REVERSAL }

Public Member Functions

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

Private Member Functions

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

Private Attributes

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

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 81 of file DAGCycleDetector.h.

Member Enumeration Documentation

◆ ChangeType

Enumerator
ARC_ADDITION 
ARC_DELETION 
ARC_REVERSAL 

Definition at line 84 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{ GUM_CONSTRUCTOR(DAGCycleDetector); }
DAGCycleDetector() noexcept
default constructor

References DAGCycleDetector().

Referenced by DAGCycleDetector(), DAGCycleDetector(), DAGCycleDetector(), ~DAGCycleDetector(), operator!=(), operator=(), operator=(), and operator==().

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

◆ DAGCycleDetector() [2/3]

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

copy constructor

Definition at line 248 of file DAGCycleDetector_inl.h.

248 :
249 _dag_(from._dag_), _ancestors_(from._ancestors_), _descendants_(from._descendants_) {
250 GUM_CONS_CPY(DAGCycleDetector);
251 }
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
DiGraph _dag_
the initial dag from which modifications are applied

References DAGCycleDetector(), _ancestors_, _dag_, and _descendants_.

Here is the call graph for this function:

◆ 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 }

References DAGCycleDetector(), _ancestors_, _dag_, and _descendants_.

Here is the call graph for this function:

◆ ~DAGCycleDetector()

INLINE gum::DAGCycleDetector::~DAGCycleDetector ( )

destructor

Definition at line 261 of file DAGCycleDetector_inl.h.

261{ GUM_DESTRUCTOR(DAGCycleDetector); }

References DAGCycleDetector().

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

305 {
306 for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
307 if (nodeset.exists(iter.key())) {
308 nodeset[iter.key()] += iter.val() * multiplier;
309 } else {
310 nodeset.insert(iter.key(), iter.val() * multiplier);
311 }
312 }
313 }

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

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

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

319 {
320 for (auto iter = set_to_del.cbegin(); iter != set_to_del.cend(); ++iter) {
321 if (nodeset.exists(iter.key())) {
322 Size& weight = nodeset[iter.key()];
323 weight -= iter.val() * multiplier;
324
325 if (!weight) { nodeset.erase(iter.key()); }
326 }
327 }
328 }
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74

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

Referenced by eraseArc(), and hasCycleFromModifications().

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

335 {
336 for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend(); ++iter) {
337 if (extremities.exists(iter.key())) { result_set.insert(iter.key(), iter.val()); }
338 }
339 }

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

Referenced by hasCycleFromModifications().

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

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, "the arc would create a directed into a DAG")
317 }
318
319 _dag_.addArc(tail, head);
320
321 // now we apply the addition of the arc as done in method
322 // hasCycleFromModifications
323 const NodeProperty< Size >& anc_tail = _ancestors_[tail];
324 const NodeProperty< Size >& desc_head = _descendants_[head];
325
326 // update the set of ancestors
327 NodeProperty< Size > set_to_add = anc_tail;
328 set_to_add.insert(tail, 1);
329 _addWeightedSet_(_ancestors_[head], set_to_add, 1);
330
331 for (auto iter = desc_head.cbegin(); iter != desc_head.cend(); ++iter) {
332 _addWeightedSet_(_ancestors_[iter.key()], set_to_add, _ancestors_[iter.key()][head]);
333 }
334
335 // update the set of descendants
336 set_to_add = desc_head;
337 set_to_add.insert(head, 1);
338 _addWeightedSet_(_descendants_[tail], set_to_add, 1);
339
340 for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
341 _addWeightedSet_(_descendants_[iter.key()], set_to_add, _descendants_[iter.key()][tail]);
342 }
343 }
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
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
#define GUM_ERROR(type, msg)
Definition exceptions.h:72
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.

References _addWeightedSet_(), _ancestors_, _dag_, _descendants_, gum::HashTable< Key, Val >::cbegin(), gum::HashTable< Key, Val >::cend(), GUM_ERROR, hasCycleFromAddition(), and gum::HashTable< Key, Val >::insert().

Referenced by reverseArc().

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

346 {
347 // check that the arc exists
348 if (!_dag_.existsArc(tail, head)) return;
349
350 _dag_.eraseArc(Arc(tail, head));
351
352 // we apply the deletion of the arc as done in method
353 // hasCycleFromModifications
354 const NodeProperty< Size >& anc_tail = _ancestors_[tail];
355 const NodeProperty< Size >& desc_head = _descendants_[head];
356
357 // update the set of descendants
358 NodeProperty< Size > set_to_del = desc_head;
359 set_to_del.insert(head, 1);
360 _delWeightedSet_(_descendants_[tail], set_to_del, 1);
361
362 for (auto iter = anc_tail.cbegin(); iter != anc_tail.cend(); ++iter) {
363 _delWeightedSet_(_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) {
372 _delWeightedSet_(_ancestors_[iter.key()], set_to_del, _ancestors_[iter.key()][head]);
373 }
374 }
void _delWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_del, Size multiplier) const
removes a weighted nodeset from another (weights are subtracted)

References _ancestors_, _dag_, _delWeightedSet_(), _descendants_, gum::HashTable< Key, Val >::cbegin(), gum::HashTable< Key, Val >::cend(), and gum::HashTable< Key, Val >::insert().

Referenced by reverseArc().

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

287 {
288 return _descendants_[y].exists(x);
289 }

References _descendants_.

Referenced by addArc().

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

297 {
298 return false;
299 }

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

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

References _addWeightedSet_(), _ancestors_, _delWeightedSet_(), _descendants_, _restrictWeightedSet_(), ARC_ADDITION, ARC_DELETION, ARC_REVERSAL, gum::HashTable< Key, Val >::beginSafe(), gum::HashTable< Key, Val >::cbegin(), gum::HashTable< Key, Val >::cend(), gum::HashTable< Key, Val >::endSafe(), gum::HashTable< Key, Val >::erase(), gum::HashTable< Key, Val >::exists(), GUM_ERROR, gum::Arc::head(), gum::HashTable< Key, Val >::insert(), gum::Set< Key >::insert(), and gum::Arc::tail().

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

292 {
293 return (_ancestors_[y][x] > 1);
294 }

References _ancestors_.

Referenced by reverseArc().

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

358 {
359 return !operator==(from);
360 }
bool operator==(const DAGCycleDetector &from) const
check the equality between two DAGCycleDetectors

References DAGCycleDetector(), and operator==().

Here is the call graph for this function:

◆ operator=() [1/2]

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

copy operator

Definition at line 265 of file DAGCycleDetector_inl.h.

265 {
266 if (this != &from) {
267 _dag_ = from._dag_;
268 _ancestors_ = from._ancestors_;
269 _descendants_ = from._descendants_;
270 }
271
272 return *this;
273 }

References DAGCycleDetector(), _ancestors_, _dag_, and _descendants_.

Here is the call graph for this function:

◆ operator=() [2/2]

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

move operator

Definition at line 276 of file DAGCycleDetector_inl.h.

276 {
277 if (this != &from) {
278 _dag_ = std::move(from._dag_);
279 _ancestors_ = std::move(from._ancestors_);
280 _descendants_ = std::move(from._descendants_);
281 }
282
283 return *this;
284 }

References DAGCycleDetector(), _ancestors_, _dag_, and _descendants_.

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

352 {
353 return ( //( _dagmodel_ == from. _dagmodel_ ) &&
354 (_ancestors_ == from._ancestors_) && (_descendants_ == from._descendants_));
355 }

References DAGCycleDetector(), _ancestors_, and _descendants_.

Referenced by operator!=().

Here is the call graph for this function:
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 342 of file DAGCycleDetector_inl.h.

342 {
343 if (hasCycleFromReversal(tail, head)) {
344 GUM_ERROR(InvalidDirectedCycle, "the arc would create a directed into a DAG")
345 }
346
347 eraseArc(tail, head);
348 addArc(head, tail);
349 }
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
void eraseArc(NodeId x, NodeId y)
removes an arc from the current DAG

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

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

65 {
66 // sets the dag
67 _dag_ = dag;
68
69 // get the set of roots and leaves of the dag
70 List< NodeId > roots, leaves;
71 NodeProperty< Size > nb_parents, nb_children;
72
73 for (const auto node: dag) {
74 Size nb_ch = dag.children(node).size();
75 nb_children.insert(node, nb_ch);
76
77 if (!nb_ch) leaves.insert(node);
78
79 Size nb_pa = dag.parents(node).size();
80 nb_parents.insert(node, nb_pa);
81
82 if (!nb_pa) roots.insert(node);
83 }
84
85 // recompute the set of ancestors
86 NodeProperty< Size > empty_set;
87 _ancestors_.clear();
88
89 for (NodeId node: dag) {
90 _ancestors_.insert(node, empty_set);
91 }
92
93 while (!roots.empty()) {
94 // get a node and update the ancestors of its children
95 NodeId node = roots.front();
96 NodeProperty< Size > node_ancestors = _ancestors_[node];
97 node_ancestors.insert(node, 1);
98 const NodeSet& node_children = dag.children(node);
99 roots.popFront();
100
101 for (const auto ch: node_children) {
102 _addWeightedSet_(_ancestors_[ch], node_ancestors, 1);
103 --nb_parents[ch];
104
105 if (!nb_parents[ch]) { roots.insert(ch); }
106 }
107 }
108
109 // recompute the set of descendants
110 _descendants_.clear();
111
112 for (const auto node: dag) {
113 _descendants_.insert(node, empty_set);
114 }
115
116 while (!leaves.empty()) {
117 // get a node and update the descendants of its parents
118 NodeId node = leaves.front();
119 NodeProperty< Size > node_descendants = _descendants_[node];
120 node_descendants.insert(node, 1);
121 const NodeSet& node_parents = dag.parents(node);
122 leaves.popFront();
123
124 for (const auto pa: node_parents) {
125 _addWeightedSet_(_descendants_[pa], node_descendants, 1);
126 --nb_children[pa];
127
128 if (!nb_children[pa]) { leaves.insert(pa); }
129 }
130 }
131 }

References _addWeightedSet_(), _ancestors_, _dag_, _descendants_, gum::ArcGraphPart::children(), gum::List< Val >::empty(), gum::List< Val >::front(), gum::HashTable< Key, Val >::insert(), gum::List< Val >::insert(), gum::ArcGraphPart::parents(), gum::List< Val >::popFront(), and gum::Set< Key >::size().

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 339 of file DAGCycleDetector.h.

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

◆ _dag_

DiGraph gum::DAGCycleDetector::_dag_
private

the initial dag from which modifications are applied

Definition at line 335 of file DAGCycleDetector.h.

Referenced by DAGCycleDetector(), DAGCycleDetector(), addArc(), eraseArc(), operator=(), 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 343 of file DAGCycleDetector.h.

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


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