1<HTML> 2<!-- 3 Copyright (c) Jeremy Siek 2000 4 5 Distributed under the Boost Software License, Version 1.0. 6 (See accompanying file LICENSE_1_0.txt or copy at 7 http://www.boost.org/LICENSE_1_0.txt) 8 --> 9<Head> 10<Title>Boost Graph Library: Using Property Maps</Title> 11<BODY BGCOLOR="#ffffff" LINK="#0000ee" TEXT="#000000" VLINK="#551a8b" 12 ALINK="#ff0000"> 13<IMG SRC="../../../boost.png" 14 ALT="C++ Boost" width="277" height="86"> 15 16<BR Clear> 17 18 19<H1><A NAME="sec:property-maps"></A> 20Property Maps 21</H1> 22 23<P> 24The main link between the abstract mathematical nature of graphs and 25the concrete problems they are used to solve is the properties that 26are attached to the vertices and edges of a graph, things like 27distance, capacity, weight, color, etc. There are many ways to attach 28properties to graph in terms of data-structure implementation, but 29graph algorithms should not have to deal with the implementation 30details of the properties. The <I>property map</I> interface 31defined in Section <A 32HREF="../../property_map/doc/property_map.html">Property 33Map Concepts</A> provides a generic method for accessing 34properties from graphs. This is the interface used in the BGL 35algorithms to access properties. 36 37<P> 38 39<H2>Property Map Interface</H2> 40 41<P> 42The property map interface specifies that each property is 43accessed using a separate property map object. In the following 44example we show an implementation of the <TT>relax()</TT> function used 45inside of Dijkstra's shortest paths algorithm. In this function, we 46need to access the weight property of an edge, and the distance 47property of a vertex. We write <TT>relax()</TT> as a template function 48so that it can be used in many difference situations. Two of the 49arguments to the function, <TT>weight</TT> and <TT>distance</TT>, are the 50property map objects. In general, BGL algorithms explicitly pass 51property map objects for every property that a function will 52need. The property map interface defines several functions, two 53of which we use here: <TT>get()</TT> and <TT>put()</TT>. The <TT>get()</TT> 54function takes a property map object, such as <TT>distance</TT> and 55a <I>key</I> object. In the case of the distance property we are using 56the vertex objects <TT>u</TT> and <TT>v</TT> as keys. The <TT>get()</TT> 57function then returns the property value for the vertex. 58 59<P> 60<PRE> 61 template <class Edge, class Graph, 62 class WeightPropertyMap, 63 class DistancePropertyMap> 64 bool relax(Edge e, const Graph& g, 65 WeightPropertyMap weight, 66 DistancePropertyMap distance) 67 { 68 typedef typename graph_traits<Graph>::vertex_descriptor Vertex; 69 Vertex u = source(e,g), v = target(e,g); 70 if ( get(distance, u) + get(weight, e) < get(distance, v)) { 71 put(distance, v, get(distance, u) + get(weight, e)); 72 return true; 73 } else 74 return false; 75 } 76</PRE> 77 78The function <TT>get()</TT> returns a copy of the property value. There 79is a third function in the property map interface, <TT>at()</TT>, 80that returns a reference to the property value (a const reference if 81the map is not mutable). 82 83<P> 84Similar to the <TT>iterator_traits</TT> class of the STL, there is a 85<TT>property_traits</TT> class that can be used to deduce the types 86associated with a property map type: the key and value types, and 87the property map category (which is used to tell whether the 88map is readable, writeable, or both). In the <TT>relax()</TT> 89function we could have used <TT>property_traits</TT> to declare local 90variables of the distance property type. 91 92<P> 93<PRE> 94 { 95 typedef typename graph_traits<Graph>::vertex_descriptor Vertex; 96 Vertex u = source(e,g), v = target(e,g); 97 typename property_traits<DistancePropertyMap>::value_type 98 du, dv; // local variables of the distance property type 99 du = get(distance, u); 100 dv = get(distance, v); 101 if (du + get(weight, e) < dv) { 102 put(distance, v, du + get(weight, e)); 103 return true; 104 } else 105 return false; 106 } 107</PRE> 108 109<P> 110There are two kinds of graph properties: interior and exterior. 111 112<P> 113<DL> 114<DT><STRONG>Interior Properties</STRONG></DT> 115<DD>are stored ``inside'' the graph object 116in some way, and the lifetime of the property value objects is the 117same as that of the graph object. 118 119<P> 120</DD> 121<DT><STRONG>Exterior Properties</STRONG></DT> 122<DD>are stored ``outside'' of the graph 123object and the lifetime of the property value objects is independent 124of the graph. This is useful for properties that are only needed 125temporarily, perhaps for the duration of a particular algorithm such 126as the color property used in <TT>breadth_first_search()</TT>. When 127using exterior properties with a BGL algorithm a property map 128object for the exterior property must be passed as an argument to the 129algorithm. 130</DD> 131</DL> 132 133<P> 134 135<H2><A NAME="sec:interior-properties"></A> 136Interior Properties 137</H2> 138 139<P> 140A graph type that supports interior property storage (such as 141<TT>adjacency_list</TT>) provides access to its property map 142objects through the interface defined in <a 143href="./PropertyGraph.html">PropertyGraph</a>. There is a function 144<TT>get(Property, g)</TT> that get property map objects from a 145graph. The first argument is the property type to specify which 146property you want to access and the second argument is the graph 147object. A graph type must document which properties (and therefore 148tags) it provides access to. The type of the property map depends on 149the type of graph and the property being mapped. A trait class is 150defined that provides a generic way to deduce the property map type: 151<TT>property_map</TT>. The following code shows how one can obtain the 152property map for the distance and weight properties of some graph 153type. 154 155<P> 156<PRE> 157 property_map<Graph, vertex_distance_t>::type d 158 = get(vertex_distance, g); 159 160 property_map<Graph, edge_weight_t>::type w 161 = get(edge_weight, g); 162</PRE> 163 164<P> 165In general, the BGL algorithms require all property maps needed 166by the algorithm to be explicitly passed to the algorithm. For 167example, the BGL Dijkstra's shortest paths algorithm requires four 168property maps: distance, weight, color, and vertex ID. 169 170<P> 171Often times some or all of the properties will be interior to the 172graph, so one would call Dijkstra's algorithm in the following way 173(given some graph <TT>g</TT> and source vertex <TT>src</TT>). 174 175<P> 176<PRE> 177 dijkstra_shortest_paths(g, src, 178 distance_map(get(vertex_distance, g)). 179 weight_map(get(edge_weight, g)). 180 color_map(get(vertex_color, g)). 181 vertex_index_map(get(vertex_index, g))); 182</PRE> 183 184<P> 185Since it is somewhat cumbersome to specify all of the property maps, 186BGL provides defaults that assume some of the properties are interior 187and can be accessed via <TT>get(Property, g)</TT> from the graph, or 188if the property map is only used internally, then the algorithm will 189create a property map for itself out of an array and using the graph's 190vertex index map as the offset into the array. Below we show a call to 191<TT>dijkstra_shortest_paths</TT> algorithm using all defaults for the 192named parameters. This call is equivalent to the previous call to 193Dijkstra's algorithm. 194 195<P> 196<PRE> 197 dijkstra_shortest_paths(g, src); 198</PRE> 199 200<P> 201The next question is: how do interior properties become attached to a 202graph object in the first place? This depends on the graph class that 203you are using. The <TT>adjacency_list</TT> graph class of BGL uses a 204property mechanism (see Section <A 205HREF="using_adjacency_list.html#sec:adjacency-list-properties">Internal 206Properties</A>) to allow an arbitrary number of properties to be 207stored on the edges and vertices of the graph. 208 209<P> 210 211<H2><A NAME="sec:exterior-properties"></A> 212Exterior Properties 213</H2> 214 215<P> 216In this section we will describe two methods for constructing exterior 217property maps, however there is an unlimited number of ways that 218one could create exterior properties for a graph. 219 220<P> 221The first method uses the adaptor class 222<TT>iterator_property_map</TT>. This 223class wraps a random access iterator, creating a property map out 224of it. The random access iterator must point to the beginning of a 225range of property values, and the length of the range must be the 226number of vertices or edges in the graph (depending on whether it is a 227vertex or edge property map). The adaptor must also be supplied 228with an ID property map, which will be used to map the vertex or 229edge descriptor to the offset of the property value (offset from the 230random access iterator). The ID property map will typically be an 231interior property map of the graph. The following example shows 232how the <TT>iterator_property_map</TT> 233can be used to create exterior property maps for the capacity and flow properties, which are stored in arrays. The arrays are 234indexed by edge ID. The edge ID is added to the graph using a property, 235and the values of the ID's are given when each edge is added to the 236graph. The complete source code for this example is in 237<TT>example/exterior_edge_properties.cpp</TT>. The 238<TT>print_network()</TT> function prints out the graph with 239the flow and capacity values. 240 241<P> 242<PRE> 243 typedef adjacency_list<vecS, vecS, bidirectionalS, 244 no_property, property<edge_index_t, std::size_t> > Graph; 245 246 const int num_vertices = 9; 247 Graph G(num_vertices); 248 249 int capacity_array[] = { 10, 20, 20, 20, 40, 40, 20, 20, 20, 10 }; 250 int flow_array[] = { 8, 12, 12, 12, 12, 12, 16, 16, 16, 8 }; 251 252 // Add edges to the graph, and assign each edge an ID number. 253 add_edge(0, 1, 0, G); 254 // ... 255 256 typedef graph_traits<Graph>::edge_descriptor Edge; 257 typedef property_map<Graph, edge_index_t>::type EdgeID_Map; 258 EdgeID_Map edge_id = get(edge_index, G); 259 260 iterator_property_map 261 <int*, int, int&, EdgeID_Map> 262 capacity(capacity_array, edge_id), 263 flow(flow_array, edge_id); 264 265 print_network(G, capacity, flow); 266</PRE> 267 268<P> 269The second method uses a pointer type (a pointer to an array of 270property values) as a property map. This requires the key type to 271be an integer so that it can be used as an offset to the pointer. The 272<TT>adjacency_list</TT> class with template parameter 273<TT>VertexList=vecS</TT> uses integers for vertex descriptors (indexed 274from zero to the number of vertices in the graph), so they are 275suitable as the key type for a pointer property map. When the 276<TT>VertexList</TT> is not <TT>vecS</TT>, then the vertex descriptor is 277not an integer, and cannot be used with a pointer property map. 278Instead the method described above of using a 279<TT>iterator_property_map</TT> with an ID 280property must be used. The <TT>edge_list</TT> class may also use an 281integer type for the vertex descriptor, depending on how the adapted 282edge iterator is defined. The example in 283<TT>example/bellman_ford.cpp</TT> shows <TT>edge_list</TT> being used 284with pointers as vertex property maps. 285 286<P> 287The reason that pointers can be used as property maps is that 288there are several overloaded functions and a specialization of 289<TT>property_traits</TT> in the header <a 290href="../../../boost/property_map/property_map.hpp"><TT>boost/property_map/property_map.hpp</TT></a> 291that implement the property map interface in terms of 292pointers. The definition of those functions is listed here. 293 294<P> 295<PRE> 296namespace boost { 297 template <class T> 298 struct property_traits<T*> { 299 typedef T value_type; 300 typedef ptrdiff_t key_type; 301 typedef lvalue_property_map_tag category; 302 }; 303 304 template <class T> 305 void put(T* pa, std::ptrdiff_t key, const T& value) { pa[key] = value; } 306 307 template <class T> 308 const T& get(const T* pa, std::ptrdiff_t key) { return pa[key]; } 309 310 template <class T> 311 const T& at(const T* pa, std::ptrdiff_t key) { return pa[key]; } 312 313 template <class T> 314 T& at(T* pa, std::ptrdiff_t key) { return pa[key]; } 315} 316</PRE> 317 318<P> 319In the following example, we use an array to store names of cities for 320each vertex in the graph, and a <TT>std::vector</TT> to store vertex 321colors which will be needed in a call to 322<TT>breadth_first_search()</TT>. Since the iterator of a 323<TT>std::vector</TT> (obtained with a call to <TT>begin()</TT>) is a 324pointer, the pointer property map method also works for 325<TT>std::vector::iterator</TT>. The complete source code for this 326example is in <a 327href="../example/city_visitor.cpp"><TT>example/city_visitor.cpp</TT></a>. 328 329<P> 330<PRE> 331// Definition of city_visitor omitted... 332 333int main(int,char*[]) 334{ 335 enum { SanJose, SanFran, LA, SanDiego, Fresno, LosVegas, Reno, 336 Sacramento, SaltLake, Pheonix, N }; 337 338 // An array of vertex name properties 339 std::string names[] = { "San Jose", "San Francisco", "San Jose", 340 "San Francisco", "Los Angeles", "San Diego", 341 "Fresno", "Los Vegas", "Reno", "Sacramento", 342 "Salt Lake City", "Pheonix" }; 343 344 // Specify all the connecting roads between cities. 345 typedef std::pair<int,int> E; 346 E edge_array[] = { E(Sacramento, Reno), ... }; 347 348 // Specify the graph type. 349 typedef adjacency_list<vecS, vecS, undirectedS> Graph; 350 // Create the graph object, based on the edges in edge_array. 351 Graph G(N, edge_array, edge_array + sizeof(edge_array)/sizeof(E)); 352 353 // DFS and BFS need to "color" the vertices. 354 // Here we use std::vector as exterior property storage. 355 std::vector<default_color_type> colors(N); 356 357 cout << "*** Depth First ***" << endl; 358 depth_first_search(G, city_visitor(names), colors.begin()); 359 cout << endl; 360 361 // Get the source vertex 362 boost::graph_traits<Graph>::vertex_descriptor 363 s = vertex(SanJose, G); 364 365 cout << "*** Breadth First ***" << endl; 366 breadth_first_search(G, s, city_visitor(names), colors.begin()); 367 368 return 0; 369} 370</PRE> 371 372<P> 373 374<H2><A NAME="sec:custom-exterior-property-maps"></A> 375Constructing an Exterior Property Map 376</H2> 377 378<P> 379Implementing your own exterior property maps is not very 380difficult. You simply need to overload the functions required by the 381<a href="property_map.html">property map concept</a> 382that you want your class to model. At most, this means overloading the 383<TT>put()</TT> and <TT>get()</TT> functions and implementing 384<TT>operator[]</TT>. Also, your property map class will need to have 385nested typedefs for all the types defined in <TT>property_traits</TT>, 386or you can create a specialization of <TT>property_traits</TT> for 387your new property map type. 388 389<P> 390The implementation of the 391<TT>iterator_property_map</TT> class 392serves as a good example for how to build and exterior property 393map. Here we present a simplified implementation of the 394<TT>iterator_property_map</TT> class 395which we will name <TT>iterator_pa</TT>. 396 397<P> 398We start with the definition of the <TT>iterator_map</TT> class 399itself. This adaptor class is templated on the adapted <TT>Iterator</TT> 400type and the ID property map. The job of the ID property map 401is to map the key object (which will typically be a vertex or edge 402descriptor) to an integer offset. The <TT>iterator_map</TT> class will 403need the three necessary typedefs for a property map: 404<TT>key_type</TT>, <TT>value_type</TT>, and <TT>category</TT>. We can use 405<TT>property_traits</TT> to find the key type of <TT>IDMap</TT>, and 406we can use <TT>iterator_traits</TT> to determine the value type of 407<TT>Iterator</TT>. We choose 408<TT>boost::lvalue_property_map_tag</TT> for the category 409since we plan on implementing the <TT>at()</TT> function. 410 411<P> 412<PRE> 413 template <class Iterator, class IDMap> 414 class iterator_map 415 { 416 public: 417 typedef typename boost::property_traits<IDMap>::key_type key_type; 418 typedef typename std::iterator_traits<Iterator>::value_type value_type; 419 typedef boost::lvalue_property_map_tag category; 420 421 iterator_map(Iterator i = Iterator(), 422 const IDMap& id = IDMap()) 423 : m_iter(i), m_id(id) { } 424 Iterator m_iter; 425 IDMap m_id; 426 }; 427</PRE> 428 429<P> 430Next we implement the three property map functions, <TT>get()</TT>, 431<TT>put()</TT>, and <TT>at()</TT>. In each of the functions, the 432<TT>key</TT> object is converted to an integer offset using the 433<TT>m_id</TT> property map, and then that is used as an offset 434to the random access iterator <TT>m_iter</TT>. 435 436<P> 437<PRE> 438 template <class Iter, class ID> 439 typename std::iterator_traits<Iter>::value_type 440 get(const iterator_map<Iter,ID>& i, 441 typename boost::property_traits<ID>::key_type key) 442 { 443 return i.m_iter[i.m_id[key]]; 444 } 445 template <class Iter, class ID> 446 void 447 put(const iterator_map<Iter,ID>& i, 448 typename boost::property_traits<ID>::key_type key, 449 const typename std::iterator_traits<Iter>::value_type& value) 450 { 451 i.m_iter[i.m_id[key]] = value; 452 } 453 template <class Iter, class ID> 454 typename std::iterator_traits<Iter>::reference 455 at(const iterator_map<Iter,ID>& i, 456 typename boost::property_traits<ID>::key_type key) 457 { 458 return i.m_iter[i.m_id[key]]; 459 } 460</PRE> 461 462<P> 463That is it. The <TT>iterator_map</TT> class is complete and could be 464used just like the 465<TT>iterator_property_map</TT> in the 466previous section. 467 468<P> 469 470 471<br> 472<HR> 473<TABLE> 474<TR valign=top> 475<TD nowrap>Copyright © 2000-2001</TD><TD> 476<A HREF="http://www.boost.org/people/jeremy_siek.htm">Jeremy Siek</A>, Indiana University (<A HREF="mailto:jsiek@osl.iu.edu">jsiek@osl.iu.edu</A>) 477</TD></TR></TABLE> 478 479</BODY> 480</HTML> 481