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> |
---|
20 | Property Maps |
---|
21 | </H1> |
---|
22 | |
---|
23 | <P> |
---|
24 | The main link between the abstract mathematical nature of graphs and |
---|
25 | the concrete problems they are used to solve is the properties that |
---|
26 | are attached to the vertices and edges of a graph, things like |
---|
27 | distance, capacity, weight, color, etc. There are many ways to attach |
---|
28 | properties to graph in terms of data-structure implementation, but |
---|
29 | graph algorithms should not have to deal with the implementation |
---|
30 | details of the properties. The <I>property map</I> interface |
---|
31 | defined in Section <A |
---|
32 | HREF="../../property_map/property_map.html">Property |
---|
33 | Map Concepts</A> provides a generic method for accessing |
---|
34 | properties from graphs. This is the interface used in the BGL |
---|
35 | algorithms to access properties. |
---|
36 | |
---|
37 | <P> |
---|
38 | |
---|
39 | <H2>Property Map Interface</H2> |
---|
40 | |
---|
41 | <P> |
---|
42 | The property map interface specifies that each property is |
---|
43 | accessed using a separate property map object. In the following |
---|
44 | example we show an implementation of the <TT>relax()</TT> function used |
---|
45 | inside of Dijkstra's shortest paths algorithm. In this function, we |
---|
46 | need to access the weight property of an edge, and the distance |
---|
47 | property of a vertex. We write <TT>relax()</TT> as a template function |
---|
48 | so that it can be used in many difference situations. Two of the |
---|
49 | arguments to the function, <TT>weight</TT> and <TT>distance</TT>, are the |
---|
50 | property map objects. In general, BGL algorithms explicitly pass |
---|
51 | property map objects for every property that a function will |
---|
52 | need. The property map interface defines several functions, two |
---|
53 | of which we use here: <TT>get()</TT> and <TT>put()</TT>. The <TT>get()</TT> |
---|
54 | function takes a property map object, such as <TT>distance</TT> and |
---|
55 | a <I>key</I> object. In the case of the distance property we are using |
---|
56 | the vertex objects <TT>u</TT> and <TT>v</TT> as keys. The <TT>get()</TT> |
---|
57 | function 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 | |
---|
78 | The function <TT>get()</TT> returns a copy of the property value. There |
---|
79 | is a third function in the property map interface, <TT>at()</TT>, |
---|
80 | that returns a reference to the property value (a const reference if |
---|
81 | the map is not mutable). |
---|
82 | |
---|
83 | <P> |
---|
84 | Similar 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 |
---|
86 | associated with a property map type: the key and value types, and |
---|
87 | the property map category (which is used to tell whether the |
---|
88 | map is readable, writeable, or both). In the <TT>relax()</TT> |
---|
89 | function we could have used <TT>property_traits</TT> to declare local |
---|
90 | variables 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> |
---|
110 | There 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 |
---|
116 | in some way, and the lifetime of the property value objects is the |
---|
117 | same 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 |
---|
123 | object and the lifetime of the property value objects is independent |
---|
124 | of the graph. This is useful for properties that are only needed |
---|
125 | temporarily, perhaps for the duration of a particular algorithm such |
---|
126 | as the color property used in <TT>breadth_first_search()</TT>. When |
---|
127 | using exterior properties with a BGL algorithm a property map |
---|
128 | object for the exterior property must be passed as an argument to the |
---|
129 | algorithm. |
---|
130 | </DD> |
---|
131 | </DL> |
---|
132 | |
---|
133 | <P> |
---|
134 | |
---|
135 | <H2><A NAME="sec:interior-properties"></A> |
---|
136 | Interior Properties |
---|
137 | </H2> |
---|
138 | |
---|
139 | <P> |
---|
140 | A graph type that supports interior property storage (such as |
---|
141 | <TT>adjacency_list</TT>) provides access to its property map |
---|
142 | objects through the interface defined in <a |
---|
143 | href="./PropertyGraph.html">PropertyGraph</a>. There is a function |
---|
144 | <TT>get(Property, g)</TT> that get property map objects from a |
---|
145 | graph. The first argument is the property type to specify which |
---|
146 | property you want to access and the second argument is the graph |
---|
147 | object. A graph type must document which properties (and therefore |
---|
148 | tags) it provides access to. The type of the property map depends on |
---|
149 | the type of graph and the property being mapped. A trait class is |
---|
150 | defined 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 |
---|
152 | property map for the distance and weight properties of some graph |
---|
153 | type. |
---|
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> |
---|
165 | In general, the BGL algorithms require all property maps needed |
---|
166 | by the algorithm to be explicitly passed to the algorithm. For |
---|
167 | example, the BGL Dijkstra's shortest paths algorithm requires four |
---|
168 | property maps: distance, weight, color, and vertex ID. |
---|
169 | |
---|
170 | <P> |
---|
171 | Often times some or all of the properties will be interior to the |
---|
172 | graph, 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> |
---|
185 | Since it is somewhat cumbersome to specify all of the property maps, |
---|
186 | BGL provides defaults that assume some of the properties are interior |
---|
187 | and can be accessed via <TT>get(Property, g)</TT> from the graph, or |
---|
188 | if the property map is only used internally, then the algorithm will |
---|
189 | create a property map for itself out of an array and using the graph's |
---|
190 | vertex 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 |
---|
192 | named parameters. This call is equivalent to the previous call to |
---|
193 | Dijkstra's algorithm. |
---|
194 | |
---|
195 | <P> |
---|
196 | <PRE> |
---|
197 | dijkstra_shortest_paths(g, src); |
---|
198 | </PRE> |
---|
199 | |
---|
200 | <P> |
---|
201 | The next question is: how do interior properties become attached to a |
---|
202 | graph object in the first place? This depends on the graph class that |
---|
203 | you are using. The <TT>adjacency_list</TT> graph class of BGL uses a |
---|
204 | property mechanism (see Section <A |
---|
205 | HREF="using_adjacency_list.html#sec:adjacency-list-properties">Internal |
---|
206 | Properties</A>) to allow an arbitrary number of properties to be |
---|
207 | stored on the edges and vertices of the graph. |
---|
208 | |
---|
209 | <P> |
---|
210 | |
---|
211 | <H2><A NAME="sec:exterior-properties"></A> |
---|
212 | Exterior Properties |
---|
213 | </H2> |
---|
214 | |
---|
215 | <P> |
---|
216 | In this section we will describe two methods for constructing exterior |
---|
217 | property maps, however there is an unlimited number of ways that |
---|
218 | one could create exterior properties for a graph. |
---|
219 | |
---|
220 | <P> |
---|
221 | The first method uses the adaptor class |
---|
222 | <TT>random_access_iterator_property_map</TT>. This |
---|
223 | class wraps a random access iterator, creating a property map out |
---|
224 | of it. The random access iterator must point to the beginning of a |
---|
225 | range of property values, and the length of the range must be the |
---|
226 | number of vertices or edges in the graph (depending on whether it is a |
---|
227 | vertex or edge property map). The adaptor must also be supplied |
---|
228 | with an ID property map, which will be used to map the vertex or |
---|
229 | edge descriptor to the offset of the property value (offset from the |
---|
230 | random access iterator). The ID property map will typically be an |
---|
231 | interior property map of the graph. The following example shows |
---|
232 | how the <TT>random_access_iterator_property_map</TT> |
---|
233 | can be used to create exterior property maps for the capacity and flow properties, which are stored in arrays. The arrays are |
---|
234 | indexed by edge ID. The edge ID is added to the graph using a property, |
---|
235 | and the values of the ID's are given when each edge is added to the |
---|
236 | graph. 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 |
---|
239 | the 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 | random_access_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> |
---|
269 | The second method uses a pointer type (a pointer to an array of |
---|
270 | property values) as a property map. This requires the key type to |
---|
271 | be 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 |
---|
274 | from zero to the number of vertices in the graph), so they are |
---|
275 | suitable 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 |
---|
277 | not an integer, and cannot be used with a pointer property map. |
---|
278 | Instead the method described above of using a |
---|
279 | <TT>random_access_iterator_property_map</TT> with an ID |
---|
280 | property must be used. The <TT>edge_list</TT> class may also use an |
---|
281 | integer type for the vertex descriptor, depending on how the adapted |
---|
282 | edge iterator is defined. The example in |
---|
283 | <TT>example/bellman_ford.cpp</TT> shows <TT>edge_list</TT> being used |
---|
284 | with pointers as vertex property maps. |
---|
285 | |
---|
286 | <P> |
---|
287 | The reason that pointers can be used as property maps is that |
---|
288 | there are several overloaded functions and a specialization of |
---|
289 | <TT>property_traits</TT> in the header <a |
---|
290 | href="../../../boost/property_map.hpp"><TT>boost/property_map.hpp</TT></a> |
---|
291 | that implement the property map interface in terms of |
---|
292 | pointers. The definition of those functions is listed here. |
---|
293 | |
---|
294 | <P> |
---|
295 | <PRE> |
---|
296 | namespace 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> |
---|
319 | In the following example, we use an array to store names of cities for |
---|
320 | each vertex in the graph, and a <TT>std::vector</TT> to store vertex |
---|
321 | colors 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 |
---|
324 | pointer, the pointer property map method also works for |
---|
325 | <TT>std::vector::iterator</TT>. The complete source code for this |
---|
326 | example is in <a |
---|
327 | href="../example/city_visitor.cpp"><TT>example/city_visitor.cpp</TT></a>. |
---|
328 | |
---|
329 | <P> |
---|
330 | <PRE> |
---|
331 | // Definition of city_visitor omitted... |
---|
332 | |
---|
333 | int 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> |
---|
375 | Constructing an Exterior Property Map |
---|
376 | </H2> |
---|
377 | |
---|
378 | <P> |
---|
379 | Implementing your own exterior property maps is not very |
---|
380 | difficult. You simply need to overload the functions required by the |
---|
381 | <a href="property_map.html">property map concept</a> |
---|
382 | that 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 |
---|
385 | nested typedefs for all the types defined in <TT>property_traits</TT>, |
---|
386 | or you can create a specialization of <TT>property_traits</TT> for |
---|
387 | your new property map type. |
---|
388 | |
---|
389 | <P> |
---|
390 | The implementation of the |
---|
391 | <TT>random_access_iterator_property_map</TT> class |
---|
392 | serves as a good example for how to build and exterior property |
---|
393 | map. Here we present a simplified implementation of the |
---|
394 | <TT>random_access_iterator_property_map</TT> class |
---|
395 | which we will name <TT>iterator_pa</TT>. |
---|
396 | |
---|
397 | <P> |
---|
398 | We start with the definition of the <TT>iterator_map</TT> class |
---|
399 | itself. This adaptor class is templated on the adapted <TT>Iterator</TT> |
---|
400 | type and the ID property map. The job of the ID property map |
---|
401 | is to map the key object (which will typically be a vertex or edge |
---|
402 | descriptor) to an integer offset. The <TT>iterator_map</TT> class will |
---|
403 | need 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 |
---|
406 | we 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 |
---|
409 | since 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> |
---|
430 | Next 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 |
---|
434 | to 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> |
---|
463 | That is it. The <TT>iterator_map</TT> class is complete and could be |
---|
464 | used just like the |
---|
465 | <TT>random_access_iterator_property_map</TT> in the |
---|
466 | previous 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="../../../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> |
---|