Added GraphAL unit tests, fixed 6 bugs in GraphAL and added functionality:
[libdai.git] / src / graph.cpp
1 /* This file is part of libDAI - http://www.libdai.org/
2 *
3 * libDAI is licensed under the terms of the GNU General Public License version
4 * 2, or (at your option) any later version. libDAI is distributed without any
5 * warranty. See the file COPYING for more details.
6 *
7 * Copyright (C) 2006-2010 Joris Mooij [joris dot mooij at libdai dot org]
8 * Copyright (C) 2006-2007 Radboud University Nijmegen, The Netherlands
9 */
10
11
12 #include <dai/graph.h>
13 #include <dai/weightedgraph.h>
14 #include <boost/graph/adjacency_list.hpp>
15 #include <boost/graph/connected_components.hpp>
16
17
18 namespace dai {
19
20
21 using namespace std;
22
23
24 void GraphAL::addEdge( size_t n1, size_t n2, bool check ) {
25 DAI_ASSERT( n1 < nrNodes() );
26 DAI_ASSERT( n2 < nrNodes() );
27 bool exists = false;
28 if( check ) {
29 // Check whether the edge already exists
30 foreach( const Neighbor &n, nb(n1) )
31 if( n == n2 ) {
32 exists = true;
33 break;
34 }
35 }
36 if( !exists && n1 != n2 ) { // Add edge
37 Neighbor nb_1( nb(n1).size(), n2, nb(n2).size() );
38 Neighbor nb_2( nb_1.dual, n1, nb_1.iter );
39 nb(n1).push_back( nb_1 );
40 nb(n2).push_back( nb_2 );
41 }
42 }
43
44
45 void GraphAL::eraseNode( size_t n ) {
46 DAI_ASSERT( n < nrNodes() );
47 // Erase neighbor entry of node n
48 _nb.erase( _nb.begin() + n );
49 // Adjust neighbor entries of nodes
50 for( size_t n2 = 0; n2 < nrNodes(); n2++ ) {
51 for( size_t iter = 0; iter < nb(n2).size(); ) {
52 Neighbor &m = nb(n2, iter);
53 if( m.node == n ) {
54 // delete this entry, because it points to the deleted node
55 nb(n2).erase( nb(n2).begin() + iter );
56 } else if( m.node > n ) {
57 // update this entry and the corresponding dual of the neighboring node
58 m.iter = iter;
59 m.node--;
60 nb( m.node, m.dual ).dual = iter;
61 iter++;
62 } else {
63 // skip
64 iter++;
65 }
66 }
67 }
68 }
69
70
71 void GraphAL::eraseEdge( size_t n1, size_t n2 ) {
72 DAI_ASSERT( n1 < nrNodes() );
73 DAI_ASSERT( n2 < nrNodes() );
74 size_t iter;
75 // Search for edge among neighbors of n1
76 for( iter = 0; iter < nb(n1).size(); iter++ )
77 if( nb(n1, iter).node == n2 ) {
78 // Remove it
79 nb(n1).erase( nb(n1).begin() + iter );
80 break;
81 }
82 // Change the iter and dual values of the subsequent neighbors
83 for( ; iter < nb(n1).size(); iter++ ) {
84 Neighbor &m = nb( n1, iter );
85 m.iter = iter;
86 nb( m.node, m.dual ).dual = iter;
87 }
88 // Search for edge among neighbors of n2
89 for( iter = 0; iter < nb(n2).size(); iter++ )
90 if( nb(n2, iter).node == n1 ) {
91 // Remove it
92 nb(n2).erase( nb(n2).begin() + iter );
93 break;
94 }
95 // Change the iter and node values of the subsequent neighbors
96 for( ; iter < nb(n2).size(); iter++ ) {
97 Neighbor &m = nb( n2, iter );
98 m.iter = iter;
99 nb( m.node, m.dual ).dual = iter;
100 }
101 }
102
103
104 bool GraphAL::isConnected() const {
105 if( nrNodes() == 0 ) {
106 return true;
107 } else {
108 std::vector<bool> incomponent( nrNodes(), false );
109
110 incomponent[0] = true;
111 bool found_new_nodes;
112 do {
113 found_new_nodes = false;
114
115 // For all nodes, check if they are connected with the (growing) component
116 for( size_t n1 = 0; n1 < nrNodes(); n1++ )
117 if( !incomponent[n1] ) {
118 foreach( const Neighbor &n2, nb(n1) ) {
119 if( incomponent[n2] ) {
120 found_new_nodes = true;
121 incomponent[n1] = true;
122 break;
123 }
124 }
125 }
126 } while( found_new_nodes );
127
128 // Check if there are remaining nodes (not in the component)
129 bool all_connected = true;
130 for( size_t n1 = 0; (n1 < nrNodes()) && all_connected; n1++ )
131 if( !incomponent[n1] )
132 all_connected = false;
133
134 return all_connected;
135
136 // BGL implementation is slower...
137 /* using namespace boost;
138 typedef adjacency_list< vecS, vecS, undirectedS, property<vertex_distance_t, int> > boostGraphAL;
139 typedef pair<size_t, size_t> E;
140
141 // Copy graph structure into boostGraphAL object
142 vector<E> edges;
143 edges.reserve( nrEdges() );
144 for( size_t n1 = 0; n1 < nrNodes(); n1++ )
145 foreach( const Neighbor &n2, nb(n1) )
146 if( n1 < n2 )
147 edges.push_back( E( n1, n2 ) );
148 boostGraphAL g( edges.begin(), edges.end(), nrNodes() );
149
150 // Construct connected components using Boost GraphAL Library
151 std::vector<int> component( num_vertices( g ) );
152 int num_comp = connected_components( g, make_iterator_property_map(component.begin(), get(vertex_index, g)) );
153
154 return (num_comp == 1);
155 */
156 }
157 }
158
159
160 bool GraphAL::isTree() const {
161 typedef vector<Edge> levelType; // first is node, second is its parent
162 vector<levelType> levels;
163
164 if( nrNodes() == 0 )
165 return true;
166 else {
167 // start with root node 0
168 levels.push_back( levelType( 1, Edge( 0, 0 ) ) );
169 size_t treeSize = 1;
170 bool foundCycle = false;
171 do {
172 levels.push_back( levelType() );
173 const levelType &prevLevel = levels[levels.size() - 2];
174 // build new level: add all neighbors of nodes in the previous level
175 // (without backtracking), aborting if a cycle is detected
176 for( size_t e = 0; e < prevLevel.size(); e++ ) {
177 size_t n2 = prevLevel[e].first; // for all nodes n2 in the previous level
178 foreach( const Neighbor &n1, nb(n2) ) { // for all neighbors n1 of n2
179 if( n1 != prevLevel[e].second ) { // no backtracking allowed
180 for( size_t l = 0; l < levels.size() && !foundCycle; l++ )
181 for( size_t f = 0; f < levels[l].size() && !foundCycle; f++ )
182 if( levels[l][f].first == n1 )
183 // n1 has been visited before -> found a cycle
184 foundCycle = true;
185 if( !foundCycle )
186 // add n1 (and its parent n2) to current level
187 levels.back().push_back( Edge( n1, n2 ) );
188 }
189 if( foundCycle )
190 break;
191 }
192 if( foundCycle )
193 break;
194 }
195 treeSize += levels.back().size();
196 } while( (levels.back().size() != 0) && !foundCycle );
197 if( treeSize == nrNodes() && !foundCycle )
198 return true;
199 else
200 return false;
201 }
202 }
203
204
205 void GraphAL::printDot( std::ostream& os ) const {
206 os << "graph G {" << endl;
207 os << "node[shape=circle,width=0.4,fixedsize=true];" << endl;
208 for( size_t n = 0; n < nrNodes(); n++ )
209 os << "\tx" << n << ";" << endl;
210 for( size_t n1 = 0; n1 < nrNodes(); n1++ )
211 foreach( const Neighbor &n2, nb(n1) )
212 if( n1 < n2 )
213 os << "\tx" << n1 << " -- x" << n2 << ";" << endl;
214 os << "}" << endl;
215 }
216
217
218 void GraphAL::checkConsistency() const {
219 size_t N = nrNodes();
220 for( size_t n1 = 0; n1 < N; n1++ ) {
221 size_t iter = 0;
222 foreach( const Neighbor &n2, nb(n1) ) {
223 DAI_ASSERT( n2.iter == iter );
224 DAI_ASSERT( n2.node < N );
225 DAI_ASSERT( n2.dual < nb(n2).size() );
226 DAI_ASSERT( nb(n2, n2.dual) == n1 );
227 iter++;
228 }
229 }
230 for( size_t n2 = 0; n2 < N; n2++ ) {
231 size_t iter = 0;
232 foreach( const Neighbor &n1, nb(n2) ) {
233 DAI_ASSERT( n1.iter == iter );
234 DAI_ASSERT( n1.node < N );
235 DAI_ASSERT( n1.dual < nb(n1).size() );
236 DAI_ASSERT( nb(n1, n1.dual) == n2 );
237 iter++;
238 }
239 }
240 }
241
242
243 GraphAL createGraphFull( size_t N ) {
244 GraphAL result( N );
245 for( size_t i = 0; i < N; i++ )
246 for( size_t j = i+1; j < N; j++ )
247 result.addEdge( i, j, false );
248 return result;
249 }
250
251
252 GraphAL createGraphGrid( size_t N1, size_t N2, bool periodic ) {
253 GraphAL result( N1*N2 );
254 if( N1 == 1 && N2 == 1 )
255 return result;
256 for( size_t i1 = 0; i1 < N1; i1++ )
257 for( size_t i2 = 0; i2 < N2; i2++ ) {
258 if( i1+1 < N1 || periodic )
259 result.addEdge( i1*N2 + i2, ((i1+1)%N1)*N2 + i2, N1 <= 2 );
260 if( i2+1 < N2 || periodic )
261 result.addEdge( i1*N2 + i2, i1*N2 + ((i2+1)%N2), N2 <= 2 );
262 }
263 return result;
264 }
265
266
267 GraphAL createGraphGrid3D( size_t N1, size_t N2, size_t N3, bool periodic ) {
268 GraphAL result( N1*N2*N3 );
269 for( size_t i1 = 0; i1 < N1; i1++ )
270 for( size_t i2 = 0; i2 < N2; i2++ )
271 for( size_t i3 = 0; i3 < N3; i3++ ) {
272 if( i1+1 < N1 || periodic )
273 result.addEdge( i1*N2*N3 + i2*N3 + i3, ((i1+1)%N1)*N2*N3 + i2*N3 + i3, N1 <= 2 );
274 if( i2+1 < N2 || periodic )
275 result.addEdge( i1*N2*N3 + i2*N3 + i3, i1*N2*N3 + ((i2+1)%N2)*N3 + i3, N2 <= 2 );
276 if( i3+1 < N3 || periodic )
277 result.addEdge( i1*N2*N3 + i2*N3 + i3, i1*N2*N3 + i2*N3 + ((i3+1)%N3), N3 <= 2 );
278 }
279 return result;
280 }
281
282
283 GraphAL createGraphLoop( size_t N ) {
284 GraphAL result( N );
285 for( size_t i = 0; i < N; i++ )
286 result.addEdge( i, (i+1)%N, N <= 2 );
287 return result;
288 }
289
290
291 GraphAL createGraphTree( size_t N ) {
292 GraphAL result( N );
293 for( size_t i = 1; i < N; i++ ) {
294 size_t j = rnd_int( 0, i-1 );
295 result.addEdge( i, j, false );
296 }
297 return result;
298 }
299
300
301 GraphAL createGraphRegular( size_t N, size_t d ) {
302 DAI_ASSERT( (N * d) % 2 == 0 );
303 DAI_ASSERT( d < N );
304
305 GraphAL G( N );
306 if( d > 0 ) {
307 bool ready = false;
308 size_t tries = 0;
309 while( !ready ) {
310 tries++;
311
312 // Start with N*d points {0,1,...,N*d-1} (N*d even) in N groups.
313 // Put U = {0,1,...,N*d-1}. (U denotes the set of unpaired points.)
314 vector<size_t> U;
315 U.reserve( N * d );
316 for( size_t i = 0; i < N * d; i++ )
317 U.push_back( i );
318
319 // Repeat the following until no suitable pair can be found: Choose
320 // two random points i and j in U, and if they are suitable, pair
321 // i with j and delete i and j from U.
322 G = GraphAL( N );
323 bool finished = false;
324 while( !finished ) {
325 random_shuffle( U.begin(), U.end() );
326 size_t i1, i2;
327 bool suit_pair_found = false;
328 for( i1 = 0; i1 < U.size()-1 && !suit_pair_found; i1++ )
329 for( i2 = i1+1; i2 < U.size() && !suit_pair_found; i2++ )
330 if( ((U[i1] / d) != (U[i2] / d)) && !G.hasEdge( U[i1] / d, U[i2] / d ) ) {
331 // they are suitable
332 suit_pair_found = true;
333 G.addEdge( U[i1] / d, U[i2] / d, false );
334 U.erase( U.begin() + i2 ); // first remove largest
335 U.erase( U.begin() + i1 ); // remove smallest
336 }
337 if( !suit_pair_found || U.empty() )
338 finished = true;
339 }
340
341 if( U.empty() ) {
342 // G is a graph with edge from vertex r to vertex s if and only if
343 // there is a pair containing points in the r'th and s'th groups.
344 // If G is d-regular, output, otherwise return to Step 1.
345 ready = true;
346 for( size_t n = 0; n < N; n++ )
347 if( G.nb(n).size() != d ) {
348 ready = false;
349 break;
350 }
351 } else
352 ready = false;
353 }
354 }
355
356 return G;
357 }
358
359
360 } // end of namespace dai