aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
DAGCycleDetector_inl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
49
50#include <agrum/base/graphs/algorithms/DAGCycleDetector.h> // to ease IDE parser
51
52namespace gum {
53
54 /* ===========================================================================
55 */
56 // CHANGES
57 /* ===========================================================================
58 */
59
60 // default constructor
62 _type_{type}, _tail_{tail}, _head_{head} {
63 GUM_CONSTRUCTOR(DAGCycleDetector::Change);
64 }
65
66 // copy constructor
68 _type_{from._type_}, _tail_{from._tail_}, _head_{from._head_} {
69 GUM_CONS_CPY(DAGCycleDetector::Change);
70 }
71
72 // move constructor
74 _type_{from._type_}, _tail_{from._tail_}, _head_{from._head_} {
75 GUM_CONS_MOV(DAGCycleDetector::Change);
76 }
77
78 // destructor
79 INLINE DAGCycleDetector::Change::~Change() noexcept { GUM_DESTRUCTOR(DAGCycleDetector::Change); }
80
81 // copy operator
84
85 // move operator
88 _type_ = from._type_;
89 _tail_ = from._tail_;
90 _head_ = from._head_;
91 return *this;
92 }
93
96 return _type_;
97 }
98
100 INLINE NodeId DAGCycleDetector::Change::tail() const noexcept { return _tail_; }
101
103 INLINE NodeId DAGCycleDetector::Change::head() const noexcept { return _head_; }
104
105 /* ===========================================================================
106 */
107 // ArcAdd
108 /* ===========================================================================
109 */
110
116
119 DAGCycleDetector::Change(from.type(), from.tail(), from.head()) {
120 GUM_CONS_CPY(DAGCycleDetector::ArcAdd);
121 }
122
125 DAGCycleDetector::Change(std::move(from.type()),
126 std::move(from.tail()),
127 std::move(from.head())) {
128 GUM_CONS_MOV(DAGCycleDetector::ArcAdd);
129 }
130
132 INLINE DAGCycleDetector::ArcAdd::~ArcAdd() noexcept { GUM_DESTRUCTOR(DAGCycleDetector::ArcAdd); }
133
137
142 return *this;
143 }
144
145 /* ===========================================================================
146 */
147 // ArcDel
148 /* ===========================================================================
149 */
150
156
159 DAGCycleDetector::Change(from.type(), from.tail(), from.head()) {
160 GUM_CONS_CPY(DAGCycleDetector::ArcDel);
161 }
162
165 DAGCycleDetector::Change(std::move(from.type()),
166 std::move(from.tail()),
167 std::move(from.head())) {
168 GUM_CONS_MOV(DAGCycleDetector::ArcDel);
169 }
170
172 INLINE DAGCycleDetector::ArcDel::~ArcDel() noexcept { GUM_DESTRUCTOR(DAGCycleDetector::ArcDel); }
173
177
182 return *this;
183 }
184
185 /* ===========================================================================
186 */
187 // ArcReverse
188 /* ===========================================================================
189 */
190
196
199 : DAGCycleDetector::Change(from.type(), from.tail(), from.head()) {
200 GUM_CONS_CPY(DAGCycleDetector::ArcReverse);
201 }
202
205 DAGCycleDetector::Change(std::move(from.type()),
206 std::move(from.tail()),
207 std::move(from.head())) {
208 GUM_CONS_MOV(DAGCycleDetector::ArcReverse);
209 }
210
213 GUM_DESTRUCTOR(DAGCycleDetector::ArcReverse);
214 }
215
219 = default;
220
227
228 /* ===========================================================================
229 */
230 // DAGCycleDetector
231 /* ===========================================================================
232 */
233
235 INLINE DAGCycleDetector::DAGCycleDetector() noexcept { GUM_CONSTRUCTOR(DAGCycleDetector); }
236
242
245 _dag_(std::move(from._dag_)), _ancestors_(std::move(from._ancestors_)),
246 _descendants_(std::move(from._descendants_)) {
247 GUM_CONS_MOV(DAGCycleDetector);
248 }
249
252
254 INLINE
256 if (this != &from) {
257 _dag_ = from._dag_;
260 }
261
262 return *this;
263 }
264
267 if (this != &from) {
268 _dag_ = std::move(from._dag_);
269 _ancestors_ = std::move(from._ancestors_);
270 _descendants_ = std::move(from._descendants_);
271 }
272
273 return *this;
274 }
275
277 INLINE bool DAGCycleDetector::hasCycleFromAddition(NodeId x, NodeId y) const noexcept {
278 return _descendants_[y].exists(x);
279 }
280
282 INLINE bool DAGCycleDetector::hasCycleFromReversal(NodeId x, NodeId y) const noexcept {
283 return (_ancestors_[y][x] > 1);
284 }
285
287 INLINE bool DAGCycleDetector::hasCycleFromDeletion(NodeId x, NodeId y) const noexcept {
288 return false;
289 }
290
292 INLINE
294 const NodeProperty< Size >& set_to_add,
295 Size multiplier) const {
296 for (auto iter = set_to_add.cbegin(); iter != set_to_add.cend(); ++iter) {
297 if (nodeset.exists(iter.key())) {
298 nodeset[iter.key()] += iter.val() * multiplier;
299 } else {
300 nodeset.insert(iter.key(), iter.val() * multiplier);
301 }
302 }
303 }
304
306
309 INLINE
311 const NodeProperty< Size >& set_to_restrict,
312 const NodeSet& extremities) const {
313 for (auto iter = set_to_restrict.cbegin(); iter != set_to_restrict.cend(); ++iter) {
314 if (extremities.exists(iter.key())) { result_set.insert(iter.key(), iter.val()); }
315 }
316 }
317
320 if (hasCycleFromReversal(tail, head)) {
321 GUM_ERROR(InvalidDirectedCycle, "the arc would create a directed into a DAG")
322 }
323
324 eraseArc(tail, head);
325 addArc(head, tail);
326 }
327
329 INLINE bool DAGCycleDetector::operator==(const DAGCycleDetector& from) const {
330 return ( //( _dagmodel_ == from. _dagmodel_ ) &&
331 (_ancestors_ == from._ancestors_) && (_descendants_ == from._descendants_));
332 }
333
335 INLINE bool DAGCycleDetector::operator!=(const DAGCycleDetector& from) const {
336 return !operator==(from);
337 }
338
339} /* namespace gum */
A class for detecting directed cycles in DAGs when trying to apply many changes to the graph.
the class to indicate that we wish to add a new arc
ArcAdd & operator=(const ArcAdd &from) noexcept
copy operator
~ArcAdd() noexcept override
destructor
ArcAdd(NodeId tail, NodeId head) noexcept
default constructor
the class to indicate that we wish to remove an arc
ArcDel(NodeId tail, NodeId head) noexcept
default constructor
ArcDel & operator=(const ArcDel &from) noexcept
copy operator
~ArcDel() noexcept override
destructor
the class to indicate that we wish to reverse an arc
~ArcReverse() noexcept override
destructor
ArcReverse & operator=(const ArcReverse &from) noexcept
copy operator
ArcReverse(NodeId tail, NodeId head) noexcept
default constructor
the base class indicating the possible changes
ChangeType type() const noexcept
returns the type of the operation
NodeId tail() const noexcept
indicates the tail of the arc involved in the modification
NodeId _head_
the head of the arc to be modified
Change & operator=(const Change &from) noexcept
ChangeType _type_
the type of modification
Change(ChangeType type, NodeId tail, NodeId head) noexcept
NodeId _tail_
the tail of the arc to be modified
NodeId head() const noexcept
indicates the head of the arc involved in the modification
DAGCycleDetector() noexcept
default constructor
bool hasCycleFromDeletion(NodeId x, NodeId y) const noexcept
indicates whether an arc deletion would create a cycle
NodeProperty< NodeProperty< Size > > _descendants_
the set of descendants of each node in the dag
void _addWeightedSet_(NodeProperty< Size > &nodeset, const NodeProperty< Size > &set_to_add, Size multiplier) const
adds a weighted nodeset to another (weights are added)
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
NodeProperty< NodeProperty< Size > > _ancestors_
the set of ancestors of each node in the dag
DAGCycleDetector & operator=(const DAGCycleDetector &from)
copy operator
bool operator!=(const DAGCycleDetector &from) const
check the inequality between two DAGCycleDetectors
void reverseArc(NodeId x, NodeId y)
reverses an arc from the DAG
void eraseArc(NodeId x, NodeId y)
removes an arc from the current DAG
DiGraph _dag_
the initial dag from which modifications are applied
bool operator==(const DAGCycleDetector &from) const
check the equality between two DAGCycleDetectors
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
bool hasCycleFromAddition(NodeId x, NodeId y) const noexcept
indicates whether an arc addition would create a cycle
const const_iterator & cend() const noexcept
Returns the unsafe const_iterator pointing to the end of the hashtable.
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
const_iterator cbegin() const
Returns an unsafe const_iterator pointing to the beginning of the hashtable.
Exception : existence of a directed cycle in a graph.
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size NodeId
Type for node ids.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
STL namespace.