aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
DAGCycleDetector.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /** @file
23  * @brief A class for detecting directed cycles in DAGs when trying to apply
24  * many changes to the graph
25  *
26  * When trying to assess whether multiple changes applied to a given DAG would
27  * induce cycles, use class DAGCycleDetector instead of trying to apply the
28  * modifications to the DAG itself and check whether and exception is raised:
29  * the class is designed to be fast for such modifications. However, the number
30  * of modifications checked should be higher than at least 3 for this class to
31  * be competitive.
32  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
33  */
34 #ifndef GUM_DAG_CYCLE_DETECTOR_H
35 #define GUM_DAG_CYCLE_DETECTOR_H
36 
37 #include <vector>
38 
39 #include <agrum/tools/graphs/DAG.h>
40 
41 namespace gum {
42 
43  /* ===========================================================================
44  */
45  // DAG CYCLE DETECTOR
46  /* ===========================================================================
47  */
48  /** @class DAGCycleDetector
49  * @ingroup graph_group
50  * @brief A class for detecting directed cycles in DAGs when trying to apply
51  * many changes to the graph
52  *
53  * When trying to assess whether multiple changes applied to a given DAG would
54  * induce cycles, use class DAGCycleDetector instead of trying to apply the
55  * modifications to the DAG itself and check whether and exception is raised:
56  * the class is designed to be fast for such modifications. However, the
57  *number
58  * of modifications checked should be higher than at least 3 for this class to
59  * be competitive.
60  */
62  public:
63  // the type of modification that can be applied to the graph
64  enum class ChangeType
65  {
69  };
70 
71  /// the base class indicating the possible changes
72  class Change {
73  public:
74  Change(ChangeType type, NodeId tail, NodeId head) noexcept;
75  Change(const Change& from) noexcept;
76  Change(Change&& from) noexcept;
77  virtual ~Change() noexcept;
78 
79  protected:
80  Change& operator=(const Change& from) noexcept;
81  Change& operator=(Change&& from) noexcept;
82 
83  public:
84  // ##########################################################################
85  /// @name Accessors/Modifiers
86  // ##########################################################################
87  /// @{
88 
89  /// returns the type of the operation
90  ChangeType type() const noexcept;
91 
92  /// indicates the tail of the arc involved in the modification
93  NodeId tail() const noexcept;
94 
95  /// indicates the head of the arc involved in the modification
96  NodeId head() const noexcept;
97 
98  /// @}
99 
100  private:
101  /// the type of modification
103 
104  /// the tail of the arc to be modified
106 
107  /// the head of the arc to be modified
109  };
110 
111  /**
112  * @class ArcAdd
113  * @headerfile DAGCycleDetector.h <agrum/tools/graphs/DAGCycleDetector.h>
114  * @brief the class to indicate that we wish to add a new arc
115  * @ingroup graph_group
116  */
117  class ArcAdd: public Change {
118  public:
119  // ##########################################################################
120  /// @name Constructors / Destructors
121  // ##########################################################################
122  /// @{
123  /// default constructor
124  ArcAdd(NodeId tail, NodeId head) noexcept;
125 
126  /// copy constructor
127  ArcAdd(const ArcAdd& from) noexcept;
128 
129  /// move constructor
130  ArcAdd(ArcAdd&& from) noexcept;
131 
132  /// destructor
133  ~ArcAdd() noexcept;
134 
135  /// @}
136 
137  // ##########################################################################
138  /// @name Operators
139  // ##########################################################################
140  /// @{
141 
142  /// copy operator
143  ArcAdd& operator=(const ArcAdd& from) noexcept;
144 
145  /// move operator
146  ArcAdd& operator=(ArcAdd&& from) noexcept;
147 
148  /// @}
149  };
150 
151  /**
152  * @class ArcDel
153  * @headerfile DAGCycleDetector.h <agrum/tools/core/DAGCycleDetector.h>
154  * @brief the class to indicate that we wish to remove an arc
155  * @ingroup graph_group
156  */
157  class ArcDel: public Change {
158  public:
159  // ##########################################################################
160  /// @name Constructors / Destructors
161  // ##########################################################################
162  /// @{
163  /// default constructor
164  ArcDel(NodeId tail, NodeId head) noexcept;
165 
166  /// copy constructor
167  ArcDel(const ArcDel& from) noexcept;
168 
169  /// move constructor
170  ArcDel(ArcDel&& from) noexcept;
171 
172  /// destructor
173  ~ArcDel() noexcept;
174 
175  /// @}
176 
177  // ##########################################################################
178  /// @name Operators
179  // ##########################################################################
180  /// @{
181 
182  /// copy operator
183  ArcDel& operator=(const ArcDel& from) noexcept;
184 
185  /// move operator
186  ArcDel& operator=(ArcDel&& from) noexcept;
187 
188  /// @}
189  };
190 
191  /** @class ArcReverse
192  * @headerfile DAGCycleDetector.h <agrum/tools/graphs/DAGCycleDetector.h>
193  * @brief the class to indicate that we wish to reverse an arc
194  * @ingroup graph_group
195  */
196  class ArcReverse: public Change {
197  public:
198  // ##########################################################################
199  /// @name Constructors / Destructors
200  // ##########################################################################
201  /// @{
202  /// default constructor
203  ArcReverse(NodeId tail, NodeId head) noexcept;
204 
205  /// copy constructor
206  ArcReverse(const ArcReverse& from) noexcept;
207 
208  /// move constructor
209  ArcReverse(ArcReverse&& from) noexcept;
210 
211  /// destructor
212  ~ArcReverse() noexcept;
213 
214  /// @}
215 
216  // ##########################################################################
217  /// @name Operators
218  // ##########################################################################
219  /// @{
220 
221  /// copy operator
222  ArcReverse& operator=(const ArcReverse& from) noexcept;
223 
224  /// move operator
225  ArcReverse& operator=(ArcReverse&& from) noexcept;
226 
227  /// @}
228  };
229 
230  // ############################################################################
231  /// @name Constructors / Destructors
232  // ############################################################################
233  /// @{
234 
235  /// default constructor
236  DAGCycleDetector() noexcept;
237 
238  /// copy constructor
239  DAGCycleDetector(const DAGCycleDetector& from);
240 
241  /// move constructor
243 
244  /// destructor
245  ~DAGCycleDetector();
246 
247  /// @}
248 
249  // ############################################################################
250  /// @name Operators
251  // ############################################################################
252  /// @{
253 
254  /// copy operator
256 
257  /// move operator
259 
260  /// check the equality between two DAGCycleDetectors
261  /** used essentally for debugging purposes */
262  bool operator==(const DAGCycleDetector& from) const;
263 
264  /// check the inequality between two DAGCycleDetectors
265  /** used essentally for debugging purposes */
266  bool operator!=(const DAGCycleDetector& from) const;
267 
268  /// @}
269 
270  // ############################################################################
271  /// @name Accessors/Modifiers
272  // ############################################################################
273  /// @{
274 
275  /// sets the initial DAG from which changes shall be applied
276  void setDAG(const DAG& dag);
277 
278  /// adds a new arc to the current DAG
279  /** worst case complexity: O(h^2) where h is the height of the DAG
280  * @throws InvalidDirectedCycle if the arc would create a cycle in the dag
281  */
282  void addArc(NodeId x, NodeId y);
283 
284  /// removes an arc from the current DAG
285  /** worst case complexity: O(h^2) where h is the height of the DAG */
286  void eraseArc(NodeId x, NodeId y);
287 
288  /// reverses an arc from the DAG
289  /** worst case complexity: O(h^2) where h is the height of the DAG
290  * @throws InvalidDirectedCycle if the arc reversal would create a cycle
291  * in the dag */
292  void reverseArc(NodeId x, NodeId y);
293 
294  /// indicates whether an arc addition would create a cycle
295  /** worst case complexity: O(1) */
296  bool hasCycleFromAddition(NodeId x, NodeId y) const noexcept;
297 
298  /// indicates wether an arc reversal would create a cycle
299  /** worst case complexity: O(1) */
300  bool hasCycleFromReversal(NodeId x, NodeId y) const noexcept;
301 
302  /// indicates whether an arc deletion would create a cycle
303  /** worst case complexity: O(1) */
304  bool hasCycleFromDeletion(NodeId x, NodeId y) const noexcept;
305 
306  /// indicates whether a set of modifications would create a cycle
307  /** the Boolean returned corresponds to the existence (or not) of a cycle
308  * on the graph resulting from the whole sequence of modifications. This
309  * means, for instance, that if, among modifs, there exists an arc (X,Y)
310  * addition involving a cycle but also the same arc (X,Y) deletion, the
311  * final
312  * graph obtained does not contains a cycle (due to the deletion) and
313  * the method will thus return false. */
314  bool hasCycleFromModifications(const std::vector< Change >& modifs) const;
315 
316  /// @}
317 
318  private:
319  /// the initial dag from which modifications are applied
321 
322  /// the set of ancestors of each node in the dag
323  /** for each ancestor, we keep track of the number of paths leading to it */
325 
326  /// the set of descendants of each node in the dag
327  /** for each ancestor, we keep track of the number of paths leading to it */
329 
330  /// adds a weighted nodeset to another (weights are added)
331  void _addWeightedSet_(NodeProperty< Size >& nodeset,
332  const NodeProperty< Size >& set_to_add,
333  Size multiplier) const;
334 
335  /// removes a weighted nodeset from another (weights are subtracted)
336  void _delWeightedSet_(NodeProperty< Size >& nodeset,
337  const NodeProperty< Size >& set_to_del,
338  Size multiplier) const;
339 
340  /** @brief put into a weighted nodeset the nodes of another weighted set
341  * that
342  * belong to a set of arc extremities */
343  void _restrictWeightedSet_(NodeProperty< Size >& result_set,
344  const NodeProperty< Size >& set_to_restrict,
345  const NodeSet& extrmities) const;
346  };
347 
348 } /* namespace gum */
349 
350 #ifndef GUM_NO_INLINE
351 # include <agrum/tools/graphs/algorithms/DAGCycleDetector_inl.h>
352 #endif // GUM_NOINLINE
353 
354 #endif // GUM_DAG_CYCLE_DETECTOR_H
DiGraph _dag_
the initial dag from which modifications are applied
the base class indicating the possible changes
NodeId tail() const noexcept
indicates the tail of the arc involved in the modification
ArcAdd(ArcAdd &&from) noexcept
move constructor
ArcAdd(const ArcAdd &from) noexcept
copy constructor
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643
ArcAdd & operator=(const ArcAdd &from) noexcept
copy operator
Change(Change &&from) noexcept
void eraseArc(NodeId x, NodeId y)
removes an arc from the current DAG
ArcDel(NodeId tail, NodeId head) noexcept
default constructor
void _delWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_del, Size multiplier) const
removes a weighted nodeset from another (weights are subtracted)
void addArc(NodeId x, NodeId y)
adds a new arc to the current DAG
ChangeType type() const noexcept
returns the type of the operation
ArcReverse(NodeId tail, NodeId head) noexcept
default constructor
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...
the class to indicate that we wish to reverse an arc
Change & operator=(const Change &from) noexcept
bool hasCycleFromModifications(const std::vector< Change > &modifs) const
indicates whether a set of modifications would create a cycle
DAGCycleDetector & operator=(const DAGCycleDetector &from)
copy operator
ChangeType _type_
the type of modification
Change & operator=(Change &&from) noexcept
void setDAG(const DAG &dag)
sets the initial DAG from which changes shall be applied
NodeId head() const noexcept
indicates the head of the arc involved in the modification
the class to indicate that we wish to add a new arc
ArcDel(ArcDel &&from) noexcept
move constructor
ArcDel & operator=(ArcDel &&from) noexcept
move operator
DAGCycleDetector(const DAGCycleDetector &from)
copy constructor
ArcReverse(const ArcReverse &from) noexcept
copy constructor
NodeId _head_
the head of the arc to be modified
Change(ChangeType type, NodeId tail, NodeId head) noexcept
DAGCycleDetector(DAGCycleDetector &&from)
move constructor
NodeProperty< NodeProperty< Size > > _descendants_
the set of descendants of each node in the dag
ArcDel(const ArcDel &from) noexcept
copy constructor
NodeId _tail_
the tail of the arc to be modified
void _addWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
ArcReverse & operator=(const ArcReverse &from) noexcept
copy operator
bool operator!=(const DAGCycleDetector &from) const
check the inequality between two DAGCycleDetectors
bool hasCycleFromAddition(NodeId x, NodeId y) const noexcept
indicates whether an arc addition would create a cycle
ArcReverse(ArcReverse &&from) noexcept
move constructor
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
DAGCycleDetector & operator=(DAGCycleDetector &&from)
move operator
ArcDel & operator=(const ArcDel &from) noexcept
copy operator
DAGCycleDetector() noexcept
default constructor
the class to indicate that we wish to remove an arc
ArcAdd & operator=(ArcAdd &&from) noexcept
move operator
bool operator==(const DAGCycleDetector &from) const
check the equality between two DAGCycleDetectors
ArcReverse & operator=(ArcReverse &&from) noexcept
move operator
ArcAdd(NodeId tail, NodeId head) noexcept
default constructor
void reverseArc(NodeId x, NodeId y)
reverses an arc from the DAG
Change(const Change &from) noexcept