1 // Copyright 2005 The Trustees of Indiana University. 2 3 // Use, modification and distribution is subject to the Boost Software 4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 // Authors: Douglas Gregor 8 // Andrew Lumsdaine 9 #ifndef BOOST_GRAPH_METIS_HPP 10 #define BOOST_GRAPH_METIS_HPP 11 12 #ifdef BOOST_GRAPH_METIS_NO_INLINE 13 #define BOOST_GRAPH_METIS_INLINE_KEYWORD 14 #else 15 #define BOOST_GRAPH_METIS_INLINE_KEYWORD inline 16 #endif 17 18 #include <string> 19 #include <iostream> 20 #include <iterator> 21 #include <utility> 22 #include <sstream> 23 #include <exception> 24 #include <vector> 25 #include <algorithm> 26 27 namespace boost 28 { 29 namespace graph 30 { 31 32 class BOOST_SYMBOL_VISIBLE metis_exception : public std::exception 33 { 34 }; 35 class BOOST_SYMBOL_VISIBLE metis_input_exception : public metis_exception 36 { 37 }; 38 39 class metis_reader 40 { 41 public: 42 typedef std::size_t vertices_size_type; 43 typedef std::size_t edges_size_type; 44 typedef double vertex_weight_type; 45 typedef double edge_weight_type; 46 47 class edge_iterator 48 { 49 public: 50 typedef std::input_iterator_tag iterator_category; 51 typedef std::pair< vertices_size_type, vertices_size_type > 52 value_type; 53 typedef const value_type& reference; 54 typedef const value_type* pointer; 55 typedef std::ptrdiff_t difference_type; 56 57 private: 58 class postincrement_proxy 59 { postincrement_proxy(const value_type & value)60 postincrement_proxy(const value_type& value) : value(value) {} 61 62 public: operator *() const63 reference operator*() const { return value; } 64 65 private: 66 value_type value; 67 friend class edge_iterator; 68 }; 69 70 public: 71 edge_iterator& operator++(); 72 postincrement_proxy operator++(int); 73 operator *() const74 reference operator*() const { return self->edge; } operator ->() const75 pointer operator->() const { return &self->edge; } 76 77 // Default copy constructor and assignment operator are okay 78 79 private: 80 edge_iterator(metis_reader* self); 81 void advance(bool skip_initial_read); 82 83 metis_reader* self; 84 85 friend class metis_reader; 86 friend bool operator==(edge_iterator, edge_iterator); 87 friend bool operator!=(edge_iterator, edge_iterator); 88 }; 89 friend class edge_iterator; 90 91 class edge_weight_iterator 92 { 93 public: 94 typedef std::input_iterator_tag iterator_category; 95 typedef edge_weight_type value_type; 96 typedef const value_type& reference; 97 typedef const value_type* pointer; 98 99 // Default copy constructor and assignment operator are okay 100 operator *() const101 reference operator*() const { return self->edge_weight; } operator ->() const102 pointer operator->() const { return &self->edge_weight; } 103 operator ++()104 edge_weight_iterator& operator++() { return *this; } operator ++(int)105 edge_weight_iterator operator++(int) { return *this; } 106 107 private: edge_weight_iterator(metis_reader * self)108 edge_weight_iterator(metis_reader* self) : self(self) {} 109 110 metis_reader* self; 111 112 friend class metis_reader; 113 }; 114 metis_reader(std::istream & in)115 metis_reader(std::istream& in) : in(in), edge_weight(1.0) { start(); } 116 117 edge_iterator begin(); 118 edge_iterator end(); 119 edge_weight_iterator weight_begin(); 120 num_vertices() const121 vertices_size_type num_vertices() const { return n_vertices; } num_edges() const122 edges_size_type num_edges() const { return n_edges; } 123 num_vertex_weights() const124 std::size_t num_vertex_weights() const { return n_vertex_weights; } 125 vertex_weight(vertices_size_type v,std::size_t n)126 vertex_weight_type vertex_weight(vertices_size_type v, std::size_t n) 127 { 128 return vertex_weights[v * num_vertex_weights() + n]; 129 } 130 has_edge_weights() const131 bool has_edge_weights() const { return edge_weights; } 132 133 private: 134 void start(); 135 136 // Configuration information 137 std::istream& in; 138 139 // Information about the current METIS file 140 vertices_size_type n_vertices; 141 edges_size_type n_edges; 142 std::size_t n_vertex_weights; 143 bool edge_weights; 144 145 // Information about the current edge/vertex 146 std::istringstream line_in; 147 std::pair< vertices_size_type, vertices_size_type > edge; 148 std::vector< vertex_weight_type > vertex_weights; 149 edge_weight_type edge_weight; 150 151 friend bool operator==(edge_iterator, edge_iterator); 152 friend bool operator!=(edge_iterator, edge_iterator); 153 }; 154 155 class metis_distribution 156 { 157 public: 158 typedef int process_id_type; 159 typedef std::size_t size_type; 160 typedef std::vector< process_id_type >::iterator iterator; 161 162 metis_distribution(std::istream& in, process_id_type my_id); 163 164 size_type block_size(process_id_type id, size_type) const; operator ()(size_type n) const165 process_id_type operator()(size_type n) const { return vertices[n]; } 166 size_type local(size_type n) const; global(size_type n) const167 size_type global(size_type n) const { return global(my_id, n); } 168 size_type global(process_id_type id, size_type n) const; 169 begin()170 iterator begin() { return vertices.begin(); } end()171 iterator end() { return vertices.end(); } 172 173 private: 174 process_id_type my_id; 175 std::vector< process_id_type > vertices; 176 }; 177 178 #if !defined(BOOST_GRAPH_METIS_NO_INLINE) || defined(BOOST_GRAPH_METIS_SOURCE) 179 BOOST_GRAPH_METIS_INLINE_KEYWORD operator ==(metis_reader::edge_iterator x,metis_reader::edge_iterator y)180 bool operator==( 181 metis_reader::edge_iterator x, metis_reader::edge_iterator y) 182 { 183 return (x.self == y.self 184 || (x.self && x.self->edge.first == x.self->num_vertices()) 185 || (y.self && y.self->edge.first == y.self->num_vertices())); 186 } 187 188 BOOST_GRAPH_METIS_INLINE_KEYWORD operator !=(metis_reader::edge_iterator x,metis_reader::edge_iterator y)189 bool operator!=( 190 metis_reader::edge_iterator x, metis_reader::edge_iterator y) 191 { 192 return !(x == y); 193 } 194 195 BOOST_GRAPH_METIS_INLINE_KEYWORD edge_iterator(metis_reader * self)196 metis_reader::edge_iterator::edge_iterator(metis_reader* self) : self(self) 197 { 198 if (self) 199 advance(true); 200 } 201 202 BOOST_GRAPH_METIS_INLINE_KEYWORD operator ++()203 metis_reader::edge_iterator& metis_reader::edge_iterator::operator++() 204 { 205 advance(false); 206 return *this; 207 } 208 209 BOOST_GRAPH_METIS_INLINE_KEYWORD advance(bool skip_initial_read)210 void metis_reader::edge_iterator::advance(bool skip_initial_read) 211 { 212 do 213 { 214 215 if (!skip_initial_read) 216 { 217 // Try to read the next edge 218 if (self->line_in >> std::ws >> self->edge.second) 219 { 220 --self->edge.second; 221 if (self->has_edge_weights()) 222 { 223 if (!(self->line_in >> self->edge_weight)) 224 boost::throw_exception(metis_input_exception()); 225 } 226 return; 227 } 228 229 // Check if we're done 230 ++self->edge.first; 231 if (self->edge.first == self->num_vertices()) 232 return; 233 } 234 235 // Find the next line 236 std::string line; 237 while (getline(self->in, line) && !line.empty() && line[0] == '%') 238 { 239 /* Keep reading lines in the loop header... */ 240 } 241 if (!self->in) 242 boost::throw_exception(metis_input_exception()); 243 self->line_in.str(line); 244 self->line_in.clear(); 245 246 // Read the next line 247 std::size_t weights_left = self->n_vertex_weights; 248 vertex_weight_type weight; 249 while (weights_left > 0) 250 { 251 if (self->line_in >> weight) 252 self->vertex_weights.push_back(weight); 253 else 254 boost::throw_exception(metis_input_exception()); 255 --weights_left; 256 } 257 258 // Successive iterations will pick up edges for this vertex. 259 skip_initial_read = false; 260 } while (true); 261 } 262 263 BOOST_GRAPH_METIS_INLINE_KEYWORD 264 metis_reader::edge_iterator::postincrement_proxy operator ++(int)265 metis_reader::edge_iterator::operator++(int) 266 { 267 postincrement_proxy result(**this); 268 ++(*this); 269 return result; 270 } 271 272 BOOST_GRAPH_METIS_INLINE_KEYWORD begin()273 metis_reader::edge_iterator metis_reader::begin() 274 { 275 if (edge.first != 0) 276 start(); 277 return edge_iterator(this); 278 } 279 280 BOOST_GRAPH_METIS_INLINE_KEYWORD end()281 metis_reader::edge_iterator metis_reader::end() { return edge_iterator(0); } 282 283 BOOST_GRAPH_METIS_INLINE_KEYWORD weight_begin()284 metis_reader::edge_weight_iterator metis_reader::weight_begin() 285 { 286 return edge_weight_iterator(this); 287 } 288 289 BOOST_GRAPH_METIS_INLINE_KEYWORD start()290 void metis_reader::start() 291 { 292 in.seekg(0, std::ios::beg); 293 std::string line; 294 while (getline(in, line) && !line.empty() && line[0] == '%') 295 { 296 /* Keep getting lines in loop header. */ 297 } 298 299 if (!in || line.empty()) 300 boost::throw_exception(metis_input_exception()); 301 302 // Determine number of vertices and edges in the graph 303 line_in.str(line); 304 if (!(line_in >> n_vertices >> n_edges)) 305 boost::throw_exception(metis_input_exception()); 306 307 // Determine whether vertex or edge weights are included in the graph 308 int fmt = 0; 309 line_in >> fmt; 310 n_vertex_weights = fmt / 10; 311 edge_weights = (fmt % 10 == 1); 312 313 // Determine how many (if any!) vertex weights are included 314 if (n_vertex_weights) 315 line_in >> n_vertex_weights; 316 317 // Setup the iteration data structures 318 edge_weight = 1.0; 319 edge.first = 0; 320 edge.second = 0; 321 vertex_weights.reserve(n_vertex_weights * num_vertices()); 322 } 323 metis_distribution(std::istream & in,process_id_type my_id)324 metis_distribution::metis_distribution( 325 std::istream& in, process_id_type my_id) 326 : my_id(my_id) 327 , vertices(std::istream_iterator< process_id_type >(in), 328 std::istream_iterator< process_id_type >()) 329 { 330 } 331 block_size(process_id_type id,size_type) const332 metis_distribution::size_type metis_distribution::block_size( 333 process_id_type id, size_type) const 334 { 335 return std::count(vertices.begin(), vertices.end(), id); 336 } 337 local(size_type n) const338 metis_distribution::size_type metis_distribution::local(size_type n) const 339 { 340 return std::count(vertices.begin(), vertices.begin() + n, vertices[n]); 341 } 342 global(process_id_type id,size_type n) const343 metis_distribution::size_type metis_distribution::global( 344 process_id_type id, size_type n) const 345 { 346 std::vector< process_id_type >::const_iterator i = vertices.begin(); 347 while (*i != id) 348 ++i; 349 350 while (n > 0) 351 { 352 do 353 { 354 ++i; 355 } while (*i != id); 356 --n; 357 } 358 359 return i - vertices.begin(); 360 } 361 362 #endif 363 364 } 365 } // end namespace boost::graph 366 367 #endif // BOOST_GRAPH_METIS_HPP 368