1 /* This file is part of libDAI - http://www.libdai.org/
3 * Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
5 * Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
10 /// \brief Defines the DAG class, which represents a directed acyclic graph
13 #ifndef __defined_libdai_dag_h
14 #define __defined_libdai_dag_h
21 #include <dai/exceptions.h>
22 #include <dai/smallset.h>
23 #include <dai/graph.h>
29 /// Represents the neighborhood structure of nodes in a directed cyclic graph.
30 /** A directed cyclic graph has nodes connected by directed edges, such that there is no
31 * directed cycle of edges n1->n2->n3->...->n1. Nodes are indexed by an unsigned integer.
32 * If there are nrNodes() nodes, they are numbered 0,1,2,...,nrNodes()-1. An edge
33 * from node \a n1 to node \a n2 is represented by a Edge(\a n1,\a n2).
35 * DAG is implemented as a sparse adjacency list, i.e., it stores for each node a list of
36 * its parents and a list of its children. Both lists are implemented as a vector of Neighbor
37 * structures (accessible by the pa() and ch() methods). Thus, each node has two associated
38 * variables of type DAG::Neighbors, which are vectors of Neighbor structures, describing their
39 * parent and children nodes.
43 /// Contains for each node a vector of its parent nodes
44 std::vector
<Neighbors
> _pa
;
46 /// Contains for each node a vector of its children nodes
47 std::vector
<Neighbors
> _ch
;
50 /// \name Constructors and destructors
52 /// Default constructor (creates an empty DAG).
53 DAG() : _pa(), _ch() {}
55 /// Constructs DAG with \a nr nodes and no edges.
56 DAG( size_t nr
) : _pa( nr
), _ch( nr
) {}
58 /// Constructs DAG from a range of edges.
59 /** \tparam EdgeInputIterator Iterator that iterates over instances of Edge.
60 * \param nr The number of nodes.
61 * \param begin Points to the first edge.
62 * \param end Points just beyond the last edge.
63 * \param check Whether to only add an edge if it does not exist already and
64 * if it does not introduce a cycle
66 template<typename EdgeInputIterator
>
67 DAG( size_t nr
, EdgeInputIterator begin
, EdgeInputIterator end
, bool check
=true ) : _pa(), _ch() {
68 construct( nr
, begin
, end
, check
);
72 /// \name Accessors and mutators
74 /// Returns constant reference to the \a _p 'th parent of node \a n
75 const Neighbor
& pa( size_t n
, size_t _p
) const {
76 DAI_DEBASSERT( n
< _pa
.size() );
77 DAI_DEBASSERT( _p
< _pa
[n
].size() );
80 /// Returns reference to the \a _p 'th parent of node \a n
81 Neighbor
& pa( size_t n
, size_t _p
) {
82 DAI_DEBASSERT( n
< _pa
.size() );
83 DAI_DEBASSERT( _p
< _pa
[n
].size() );
87 /// Returns constant reference to all parents of node \a n
88 const Neighbors
& pa( size_t n
) const {
89 DAI_DEBASSERT( n
< _pa
.size() );
92 /// Returns reference to all parents of node \a n
93 Neighbors
& pa( size_t n
) {
94 DAI_DEBASSERT( n
< _pa
.size() );
98 /// Returns constant reference to the \a _c 'th child of node \a n
99 const Neighbor
& ch( size_t n
, size_t _c
) const {
100 DAI_DEBASSERT( n
< _ch
.size() );
101 DAI_DEBASSERT( _c
< _ch
[n
].size() );
104 /// Returns reference to the \a _c 'th child of node \a n
105 Neighbor
& ch( size_t n
, size_t _c
) {
106 DAI_DEBASSERT( n
< _ch
.size() );
107 DAI_DEBASSERT( _c
< _ch
[n
].size() );
111 /// Returns constant reference to all children of node \a n
112 const Neighbors
& ch( size_t n
) const {
113 DAI_DEBASSERT( n
< _ch
.size() );
116 /// Returns reference to all children of node \a n
117 Neighbors
& ch( size_t n
) {
118 DAI_DEBASSERT( n
< _ch
.size() );
123 /// \name Adding nodes and edges
125 /// (Re)constructs DAG from a range of edges.
126 /** \tparam EdgeInputIterator Iterator that iterates over instances of Edge.
127 * \param nr The number of nodes.
128 * \param begin Points to the first edge.
129 * \param end Points just beyond the last edge.
130 * \param check Whether to only add an edge if it does not exist already and does not introduce a cycle.
132 template<typename EdgeInputIterator
>
133 void construct( size_t nr
, EdgeInputIterator begin
, EdgeInputIterator end
, bool check
=true );
135 /// Adds a node without parents and children and returns the index of the added node.
137 _pa
.push_back( Neighbors() );
138 _ch
.push_back( Neighbors() );
139 return _pa
.size() - 1;
142 /// Adds a node with parents specified by a range of nodes.
143 /** \tparam NodeInputIterator Iterator that iterates over instances of \c size_t.
144 * \param begin Points to the first index of the nodes that should become parents of the added node.
145 * \param end Points just beyond the last index of the nodes that should become parents of the added node.
146 * \param sizeHint For improved efficiency, the size of the range may be specified by \a sizeHint.
147 * \returns Index of the added node.
149 template <typename NodeInputIterator
>
150 size_t addNode( NodeInputIterator begin
, NodeInputIterator end
, size_t sizeHint
=0 ) {
151 Neighbors newparents
;
152 newparents
.reserve( sizeHint
);
154 for( NodeInputIterator it
= begin
; it
!= end
; ++it
) {
155 DAI_ASSERT( *it
< nrNodes() );
156 Neighbor
newparent( iter
, *it
, ch(*it
).size() );
157 Neighbor
newchild( ch(*it
).size(), nrNodes(), iter
++ );
158 newparents
.push_back( newparent
);
159 ch( *it
).push_back( newchild
);
161 _pa
.push_back( newparents
);
162 _ch
.push_back( Neighbors() );
163 return _pa
.size() - 1;
166 /// Adds an edge from node \a n1 and node \a n2.
167 /** If \a check == \c true, only adds the edge if it does not exist already and it would not introduce a cycle.
169 DAG
& addEdge( size_t n1
, size_t n2
, bool check
=true );
172 /// \name Erasing nodes and edges
174 /// Removes node \a n and all ingoing and outgoing edges; indices of other nodes are changed accordingly.
175 void eraseNode( size_t n
);
177 /// Removes edge from node \a n1 to node \a n2.
178 void eraseEdge( size_t n1
, size_t n2
);
183 /// Returns number of nodes
184 size_t nrNodes() const {
185 DAI_DEBASSERT( _pa
.size() == _ch
.size() );
189 /// Calculates the number of edges, time complexity: O(nrNodes())
190 size_t nrEdges() const {
192 for( size_t i
= 0; i
< _pa
.size(); i
++ )
193 sum
+= _pa
[i
].size();
197 /// Returns true if the DAG contains an edge from node \a n1 and \a n2
198 /** \note The time complexity is linear in the number of children of \a n1 or the number of parents of \a n2, whichever is smaller
200 bool hasEdge( size_t n1
, size_t n2
) const {
201 if( ch(n1
).size() < pa(n2
).size() ) {
202 for( size_t _n2
= 0; _n2
< ch(n1
).size(); _n2
++ )
203 if( ch( n1
, _n2
) == n2
)
206 for( size_t _n1
= 0; _n1
< pa(n2
).size(); _n1
++ )
207 if( pa( n2
, _n1
) == n1
)
213 /// Returns the index of a given node \a p amongst the parents of \a n
214 /** \note The time complexity is linear in the number of parents of \a n
215 * \throw OBJECT_NOT_FOUND if \a p is not a parent of \a n
217 size_t findPa( size_t n
, size_t p
) {
218 for( size_t _p
= 0; _p
< pa(n
).size(); _p
++ )
219 if( pa( n
, _p
) == p
)
221 DAI_THROW(OBJECT_NOT_FOUND
);
225 /// Returns the index of a given node \a c amongst the children of \a n
226 /** \note The time complexity is linear in the number of children of \a n
227 * \throw OBJECT_NOT_FOUND if \a c is not a child \a n
229 size_t findCh( size_t n
, size_t c
) {
230 for( size_t _c
= 0; _c
< ch(n
).size(); _c
++ )
231 if( ch( n
, _c
) == c
)
233 DAI_THROW(OBJECT_NOT_FOUND
);
237 /// Returns parents of node \a n as a SmallSet<size_t>.
238 SmallSet
<size_t> paSet( size_t n
) const;
240 /// Returns children of node \a n as a SmallSet<size_t>.
241 SmallSet
<size_t> chSet( size_t n
) const;
243 /// Returns the set of ancestors of node \a n, i.e., all nodes \a a such that there exists a directed path from \a a to \a n (excluding \a n itself)
244 std::set
<size_t> ancestors( size_t n
) const;
246 /// Returns the set of descendants of node \a n, i.e., all nodes \a d such that there exists a directed path from \a n to \a d (excluding \a n itself)
247 std::set
<size_t> descendants( size_t n
) const;
249 /// Returns whether there exists a directed path from node \a n1 to node \a n2 (which may consist of zero edges)
250 bool existsDirectedPath( size_t n1
, size_t n2
) const;
252 /// Returns true if the DAG is connected
253 bool isConnected() const;
255 /// Asserts internal consistency
256 void checkConsistency() const;
258 /// Comparison operator which returns true if two DAGs are identical
259 /** \note Two DAGs are called identical if they have the same number
260 * of nodes and the same edges (i.e., \a x has an edge from \a n1 to \a n2
261 * if and only if \c *this has an edge from node \a n1 to \a n2).
263 bool operator==( const DAG
& x
) const {
264 if( nrNodes() != x
.nrNodes() )
266 for( size_t n1
= 0; n1
< nrNodes(); n1
++ ) {
267 if( pa(n1
).size() != x
.pa(n1
).size() )
269 foreach( const Neighbor
&n2
, pa(n1
) )
270 if( !x
.hasEdge( n2
, n1
) )
272 foreach( const Neighbor
&n2
, x
.pa(n1
) )
273 if( !hasEdge( n2
, n1
) )
280 /// \name Input and output
282 /// Writes this DAG to an output stream in GraphViz .dot syntax
283 void printDot( std::ostream
& os
) const;
285 /// Writes this DAG to an output stream
286 friend std::ostream
& operator<<( std::ostream
& os
, const DAG
& g
) {
294 template<typename EdgeInputIterator
>
295 void DAG::construct( size_t nr
, EdgeInputIterator begin
, EdgeInputIterator end
, bool check
) {
301 for( EdgeInputIterator e
= begin
; e
!= end
; e
++ )
302 addEdge( e
->first
, e
->second
, check
);
306 } // end of namespace dai