传送门:Boost Graph Library 快速入门 html
原文:Boost Property Map ios
图的抽象数学性质与它们被用来解决具体问题之间的主要联系就是被附加在图的顶点和边上的属性(property),好比距离(distance)、容量(capacity)、权重(weight)、颜色(color)等。根据不一样的数据结构,有许多方法用来将各类 property 添加到图中,可是做用在图上的算法不须要去关心这些具体的细节。定义在章节 Property Map Concepts 中的“属性映射接口(property map interface)”为访问图中的 property 提供了一个通用方法。这里讲的是在BGL算法中用来访问各类 property 的接口。算法
Property map interface 明确指出每一个属性都要用单独的属性映射对象(property map object)来访问。在下面的例子中,咱们将展现函数 relax() 的一个实现,这个函数被用在 Dijkstra 最短路径算法中。在这个函数中,咱们须要访问一条边的 weight property 和一个顶点的 distance property。咱们把 relax() 写成一个模板函数,这样它就能够用在许多不一样的场景中。这个函数的参数,weight 和 distance 是 property object。通常来讲,BGL算法会给一个函数所须要的每一个 property 传递一个 property map object。Property map interface 定义了一些函数,其中咱们要用到的两个是:get() 和 put()。get() 函数接受一个 property map object,好比 distance, 和一个关键字对象(key object)做为参数。对于 distance property 咱们用顶点对象 u 和 v 做为关键字。而后,get() 会返回对应顶点的 property value。Trait 类是被定义为用来提供一个通用的方法来推导特定的 property map 类型:property_map。数组
1 template <class Edge, class Graph, 2 class WeightPropertyMap, 3 class DistancePropertyMap> 4 bool relax(Edge e, const Graph& g, 5 WeightPropertyMap weight, 6 DistancePropertyMap distance) 7 { 8 typedef typename graph_traits<Graph>::vertex_descriptor Vertex; 9 Vertex u = source(e,g), v = target(e,g); 10 if ( get(distance, u) + get(weight, e) < get(distance, v)) { 11 put(distance, v, get(distance, u) + get(weight, e)); 12 return true; 13 } else 14 return false; 15 }
函数 get() 返回的是 property value 的一份拷贝。Property map interface 中的第三个函数,at(),它返回一个 property value 的引用(若是 map 不是 mutable 类型的,则返回类型是常量引用)。数据结构
和 STL 中的 iterator_traits 类类似,这里有一个 property_traits 类可以用来推导与 property map 相关的类型:key 和 value 的类型,还有 property map 的种类(category)(用来讲明 map 是否可读、可写或者二者均可以)。在 relax() 函数中,咱们能够用 property_traits 来推导 distance property 的局部变量的类型。app
1 { 2 typedef typename graph_traits<Graph>::vertex_descriptor Vertex; 3 Vertex u = source(e,g), v = target(e,g); 4 typename property_traits<DistancePropertyMap>::value_type 5 du, dv; // local variables of the distance property type 6 du = get(distance, u); 7 dv = get(distance, v); 8 if (du + get(weight, e) < dv) { 9 put(distance, v, du + get(weight, e)); 10 return true; 11 } else 12 return false; 13 }
图的属性能够分为两类:内部的(interior)和外部的(interior)。函数
Interior Properties
以某些方式存储在图对象的“内部”,而且 property value 对象的生命周期和图对象的相同。ui
Exterior Properties
存储在图的“外部”而且 property value 对象的生命周期和图的相互独立。这对那些仅仅临时须要的 property 来讲是颇有用的,perhaps for the duration of a particular algorithm such as the color property used in breadth_first_search(). 当在一个 BGL 算法中使用 exterior properties 时,一个做为 exterior properties 的 property map object 必须做为参数传递给这个算法。spa
一个支持内部属性(interior property)存储的图(如 adjacency_list)经过定义在 PropertyGraph 中的接口来访问它的 property map objects。其中有一个从图中得到 property map objects 的函数 get(Property, g)。第一个参数是 property 的类型,用来指明你想访问哪种 property,第二个参数是一个图对象。一个图类型(graph type)必须用文档说明它支持访问哪种 property (and therefore tags)。Property map 的类型依赖于 graph 的类型和当前所映射的属性。Trait 类型的做用是提供一个通用的方法来推导property map 的类型:property_map。下面的代码展现了如何为某些图 的 distance 和 weight 属性获取 property map。.net
property_map<Graph, vertex_distance_t>::type d = get(vertex_distance, g); property_map<Graph, edge_weight_t>::type w = get(edge_weight, g);
通常来讲,BGL算法须要将它所须要的全部 property map 显式地传递给它。例如,BGL Dijkstra 最短路径算法须要四个 property map:distance,weight,color 和 vertex ID。
一般某些或者全部的 property 都会在图的内部,因此能够以下调用 Dijkstra 算法(给了图 g 和顶点 src)。
dijkstra_shortest_paths(g, src, distance_map(get(vertex_distance, g)). weight_map(get(edge_weight, g)). color_map(get(vertex_color, g)). vertex_index_map(get(vertex_index, g)));
由于指定全部的 property map 多少有点繁琐,BGL提供了一些默认行为,假设部分 property 是内部的而且能够经过 get(Property, g)来从图中访问,或者 property仅仅是内部使用,那么算法将用数组来为它本身建立 property map而且用图的 vertex index map作为数组的偏移量。下面咱们展现了使用全部命名参数的默认值来调用 dijkstra_shortest_paths。这个调用和前面的Dijkstra算法的调用是相等的。
dijkstra_shortest_paths(g, src);
下一个问题是:内部 property 如何在一开始添加到图对象中?这取决于你所使用的图的类型。BGL的 adjacency_list 图使用一个属性机制(见章节 Internal Properties)来容许任意数量的 property被添加到图的边和顶点中。
这一节咱们将描述两个构造外部 property 的方法,虽然那里有无数种方法来构造外部 property。
第一种方法是使用适配器类 iterator_property_map。这个类包装了一个随机访问迭代器,用它建立一个 property map。随机访问迭代器必须指向一个 property values 范围(range)的开始,而且这个 range 的长度必须是图中顶点或边的的数目(取决于它是一个顶点仍是边 property map)。这个适配器还须要一个ID property map,用来映射顶点或者边描述符到 property value 的偏移量(从随机访问迭代器的偏移量)。这个 ID property map 是一个典型的图内部 property map。下面的例子展现了如何用 iterator_property_map 来给存储在数组中的 capacity 和 flow 属性建立外部 property map。这些数组是按照边的ID来索引的。边的ID经过一个 property 被加入到图中,而且ID的值是在边被加入到图中时给出的。这个例子的完整的源代码在example/exterior_properties.cpp中。其中 print_network() 函数打印出图和它的属性 flow 和 capacity 的值。
typedef adjacency_list<vecS, vecS, bidirectionalS, no_property, property<edge_index_t, std::size_t> > Graph; const int num_vertices = 9; Graph G(num_vertices); int capacity_array[] = { 10, 20, 20, 20, 40, 40, 20, 20, 20, 10 }; int flow_array[] = { 8, 12, 12, 12, 12, 12, 16, 16, 16, 8 }; // Add edges to the graph, and assign each edge an ID number. add_edge(0, 1, 0, G); // ... typedef graph_traits<Graph>::edge_descriptor Edge; typedef property_map<Graph, edge_index_t>::type EdgeID_Map; EdgeID_Map edge_id = get(edge_index, G); iterator_property_map <int*, int, int&, EdgeID_Map> capacity(capacity_array, edge_id), flow(flow_array, edge_id); print_network(G, capacity, flow);
第二种方法是用指针类型(指向property values数组的指针)做为property map。这种方法要求关键字类型必须是整型,以便关键字能够做为指针的偏移量。带有模板参数 VertexList=vecS 的 adjacency_list 使用整型做为顶点描述符(索引从0到图中顶点的数目),因此对于指针 property map 它们(顶点描述符)做为关键字是可行的。当 VertexList 不是 vecS时,那么顶点描述符就不是整型,因此就不能配合指针 property map 使用。相反的就必须使用上面所描述的使用 iterator_property_map 和 ID property map 的方法。edge_list 类也能够用整型做为顶点描述符,这取决于适配的边迭代器是如何定义的。在 example/bellman_ford.cpp 中的例子展现了经过把指针做为顶点 property map 来使用 edge_list。
指针能够作为 property map 是由于有数个重载的函数和一个特殊化的 property_traits,在头文件 boost/property_map/property_map.hpp 中, 用指针实现了 property map interface。这些函数的定义以下所列。
namespace boost { template <class T> struct property_traits<T*> { typedef T value_type; typedef ptrdiff_t key_type; typedef lvalue_property_map_tag category; }; template <class T> void put(T* pa, std::ptrdiff_t key, const T& value) { pa[key] = value; } template <class T> const T& get(const T* pa, std::ptrdiff_t key) { return pa[key]; } template <class T> const T& at(const T* pa, std::ptrdiff_t key) { return pa[key]; } template <class T> T& at(T* pa, std::ptrdiff_t key) { return pa[key]; } }
在下面的例子中,咱们用数组存储图中每一个顶点所表明的城市的名字,用 std::vector 存储在调用 breadth_first_search() 时所须要的顶点的颜色。由于 std::vector 的迭代器(经过调用 begin() 得到)是一个指针,指针 property map 方法也能够工做在 std::vector::iterator 上。这个例子的完整代码在 example/city_visitor.cpp 中。
1 // Definition of city_visitor omitted... 2 3 int main(int,char*[]) 4 { 5 enum { SanJose, SanFran, LA, SanDiego, Fresno, LosVegas, Reno, 6 Sacramento, SaltLake, Pheonix, N }; 7 8 // An array of vertex name properties 9 std::string names[] = { "San Jose", "San Francisco", "San Jose", 10 "San Francisco", "Los Angeles", "San Diego", 11 "Fresno", "Los Vegas", "Reno", "Sacramento", 12 "Salt Lake City", "Pheonix" }; 13 14 // Specify all the connecting roads between cities. 15 typedef std::pair<int,int> E; 16 E edge_array[] = { E(Sacramento, Reno), ... }; 17 18 // Specify the graph type. 19 typedef adjacency_list<vecS, vecS, undirectedS> Graph; 20 // Create the graph object, based on the edges in edge_array. 21 Graph G(N, edge_array, edge_array + sizeof(edge_array)/sizeof(E)); 22 23 // DFS and BFS need to "color" the vertices. 24 // Here we use std::vector as exterior property storage. 25 std::vector<default_color_type> colors(N); 26 27 cout << "*** Depth First ***" << endl; 28 depth_first_search(G, city_visitor(names), colors.begin()); 29 cout << endl; 30 31 // Get the source vertex 32 boost::graph_traits<Graph>::vertex_descriptor 33 s = vertex(SanJose, G); 34 35 cout << "*** Breadth First ***" << endl; 36 breadth_first_search(G, s, city_visitor(names), colors.begin()); 37 38 return 0; 39 }
实现你本身的外部 property map 不是十分困难。你只须要重载 property map concept 中所要求的那些函数中你所须要的部分。这意味着最多重载 put() 和 get() 函数并实现 operator[] 。固然,你的 property map 也须要为定义在 property_traits 中的全部的类型作嵌套定义,或者你能够为你的新 property map 建立一个 property_traits 特例。
类 iterator_property_map 的实现能够做为一个建立外部 property map 的好例子。这里咱们展现一个 iterator_property_map 的简化版本,命名为 iterator_pa。
咱们从定义 iterator_map 自己开始。这个适配器类使用适配的迭代器类型和 ID property map 做为模板参数。ID property map 的做用是把关键字(通常来讲是顶点或边的描述符)映射为一个整型偏移量。iterator_map 须要一个 property map 所必须的三个类型定义:key_type, value_type 和 capacity。咱们能够用 property_traits 获得 IDMap 的关键字类型,而且咱们可以用 iterator_traits 来肯定 Iterator 的值的类型。由于计划实现 at() 函数,因此咱们选择 boost::lvalue_property_map_tag 做为 capacity。
template <class Iterator, class IDMap> class iterator_map { public: typedef typename boost::property_traits<IDMap>::key_type key_type; typedef typename std::iterator_traits<Iterator>::value_type value_type; typedef boost::lvalue_property_map_tag category; iterator_map(Iterator i = Iterator(), const IDMap& id = IDMap()) : m_iter(i), m_id(id) { } Iterator m_iter; IDMap m_id; };
下面咱们实现三个 property map 函数,get(), put() 和 at()。在每一个函数中,使用 m_id property map 将关键字对象转换为一个整型偏移量,用来做为随机访问迭代器 m_iter 的偏移量。
template <class Iter, class ID> typename std::iterator_traits<Iter>::value_type get(const iterator_map<Iter,ID>& i, typename boost::property_traits<ID>::key_type key) { return i.m_iter[i.m_id[key]]; } template <class Iter, class ID> void put(const iterator_map<Iter,ID>& i, typename boost::property_traits<ID>::key_type key, const typename std::iterator_traits<Iter>::value_type& value) { i.m_iter[i.m_id[key]] = value; } template <class Iter, class ID> typename std::iterator_traits<Iter>::reference at(const iterator_map<Iter,ID>& i, typename boost::property_traits<ID>::key_type key) { return i.m_iter[i.m_id[key]]; }
这就对了。iterator_map 类已经完成而且能够向前面使用 iterator_property_map 同样来使用。
1 #include <boost/config.hpp> 2 #include <iostream> 3 #include <boost/graph/adjacency_list.hpp> 4 #include <boost/property_map/property_map.hpp> 5 6 template <class Graph, class Capacity, class Flow> 7 void print_network(Graph& G, Capacity capacity, Flow flow) 8 { 9 typedef typename boost::graph_traits<Graph>::vertex_iterator Viter; 10 typedef typename boost::graph_traits<Graph>::out_edge_iterator OutEdgeIter; 11 typedef typename boost::graph_traits<Graph>::in_edge_iterator InEdgeIter; 12 13 Viter ui, uiend; 14 for (boost::tie(ui, uiend) = boost::vertices(G); ui != uiend; ++ui) { 15 OutEdgeIter out, out_end; 16 std::cout << *ui << "\t"; 17 18 for(boost::tie(out, out_end) = boost::out_edges(*ui, G); out != out_end; ++out) 19 std::cout << "--(" << boost::get(capacity, *out) << ", " 20 << boost::get(flow, *out) << ")--> " << boost::target(*out,G) << "\t"; 21 std::cout << std::endl << "\t"; 22 23 InEdgeIter in, in_end; 24 for(boost::tie(in, in_end) = boost::in_edges(*ui, G); in != in_end; ++in) 25 std::cout << "<--(" << boost::get(capacity, *in) << "," << boost::get(flow, *in) << ")-- " 26 << boost::source(*in, G) << "\t"; 27 std::cout << std::endl; 28 } 29 } 30 31 32 int main(int , char* []) { 33 34 typedef boost::adjacency_list<boost::vecS, boost::vecS, 35 boost::bidirectionalS, boost::no_property, 36 boost::property<boost::edge_index_t, std::size_t> > Graph; 37 38 const int num_vertices = 9; 39 Graph G(num_vertices); 40 41 /* 2<----5 42 / ^ 43 / \ 44 V \ 45 0 ---->1---->3----->6--->8 46 \ ^ 47 \ / 48 V / 49 4----->7 50 */ 51 52 int capacity[] = { 10, 20, 20, 20, 40, 40, 20, 20, 20, 10 }; 53 int flow[] = { 8, 12, 12, 12, 12, 12, 16, 16, 16, 8 }; 54 55 // insert edges into the graph, and assign each edge an ID number 56 // to index into the property arrays 57 boost::add_edge(0, 1, 0, G); 58 59 boost::add_edge(1, 4, 1, G); 60 boost::add_edge(4, 7, 2, G); 61 boost::add_edge(7, 6, 3, G); 62 63 boost::add_edge(1, 3, 4, G); 64 boost::add_edge(3, 6, 5, G); 65 66 boost::add_edge(6, 5, 6, G); 67 boost::add_edge(5, 2, 7, G); 68 boost::add_edge(2, 1, 8, G); 69 70 boost::add_edge(6, 8, 9, G); 71 72 typedef boost::property_map<Graph, boost::edge_index_t>::type EdgeIndexMap; 73 EdgeIndexMap edge_id = boost::get(boost::edge_index, G); 74 75 typedef boost::iterator_property_map<int*, EdgeIndexMap, int, int&> IterMap; 76 77 print_network(G, IterMap(capacity, edge_id), IterMap(flow, edge_id)); 78 79 return 0; 80 }
1 // 2 //======================================================================= 3 // Copyright 1997, 1998, 1999, 2000 University of Notre Dame. 4 // Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek 5 // 6 // Distributed under the Boost Software License, Version 1.0. (See 7 // accompanying file LICENSE_1_0.txt or copy at 8 // http://www.boost.org/LICENSE_1_0.txt) 9 //======================================================================= 10 // 11 12 #include <boost/config.hpp> 13 #include <iostream> 14 #include <vector> 15 #include <string> 16 #include <boost/graph/adjacency_list.hpp> 17 #include <boost/graph/depth_first_search.hpp> 18 #include <boost/graph/breadth_first_search.hpp> 19 #include <boost/property_map/property_map.hpp> 20 #include <boost/graph/graph_utility.hpp> // for boost::make_list 21 22 23 /* 24 Example of using a visitor with the depth first search 25 and breadth first search algorithm 26 27 Sacramento ---- Reno ---- Salt Lake City 28 | 29 San Francisco 30 | 31 San Jose ---- Fresno 32 | 33 Los Angeles ---- Las Vegas ---- Phoenix 34 | 35 San Diego 36 37 38 The visitor has three main functions: 39 40 discover_vertex(u,g) is invoked when the algorithm first arrives at the 41 vertex u. This will happen in the depth first or breadth first 42 order depending on which algorithm you use. 43 44 examine_edge(e,g) is invoked when the algorithm first checks an edge to see 45 whether it has already been there. Whether using BFS or DFS, all 46 the edges of vertex u are examined immediately after the call to 47 visit(u). 48 49 finish_vertex(u,g) is called when after all the vertices reachable from vertex 50 u have already been visited. 51 52 */ 53 54 using namespace std; 55 using namespace boost; 56 57 58 struct city_arrival : public base_visitor<city_arrival> 59 { 60 city_arrival(string* n) : names(n) { } 61 typedef on_discover_vertex event_filter; 62 template <class Vertex, class Graph> 63 inline void operator()(Vertex u, Graph&) { 64 cout << endl << "arriving at " << names[u] << endl 65 << " neighboring cities are: "; 66 } 67 string* names; 68 }; 69 70 struct neighbor_cities : public base_visitor<neighbor_cities> 71 { 72 neighbor_cities(string* n) : names(n) { } 73 typedef on_examine_edge event_filter; 74 template <class Edge, class Graph> 75 inline void operator()(Edge e, Graph& g) { 76 cout << names[ target(e, g) ] << ", "; 77 } 78 string* names; 79 }; 80 81 struct finish_city : public base_visitor<finish_city> 82 { 83 finish_city(string* n) : names(n) { } 84 typedef on_finish_vertex event_filter; 85 template <class Vertex, class Graph> 86 inline void operator()(Vertex u, Graph&) { 87 cout << endl << "finished with " << names[u] << endl; 88 } 89 string* names; 90 }; 91 92 int main(int, char*[]) 93 { 94 95 enum { SanJose, SanFran, LA, SanDiego, Fresno, LasVegas, Reno, 96 Sacramento, SaltLake, Phoenix, N }; 97 98 string names[] = { "San Jose", "San Francisco", "Los Angeles", "San Diego", 99 "Fresno", "Las Vegas", "Reno", "Sacramento", 100 "Salt Lake City", "Phoenix" }; 101 102 typedef std::pair<int,int> E; 103 E edge_array[] = { E(Sacramento, Reno), E(Sacramento, SanFran), 104 E(Reno, SaltLake), 105 E(SanFran, SanJose), 106 E(SanJose, Fresno), E(SanJose, LA), 107 E(LA, LasVegas), E(LA, SanDiego), 108 E(LasVegas, Phoenix) }; 109 110 /* Create the graph type we want. */ 111 typedef adjacency_list<vecS, vecS, undirectedS> Graph; 112 #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 113 // VC++ has trouble with the edge iterator constructor 114 Graph G(N); 115 for (std::size_t j = 0; j < sizeof(edge_array)/sizeof(E); ++j) 116 add_edge(edge_array[j].first, edge_array[j].second, G); 117 #else 118 Graph G(edge_array, edge_array + sizeof(edge_array)/sizeof(E), N); 119 #endif 120 121 cout << "*** Depth First ***" << endl; 122 depth_first_search 123 (G, 124 visitor(make_dfs_visitor(boost::make_list(city_arrival(names), 125 neighbor_cities(names), 126 finish_city(names))))); 127 cout << endl; 128 129 /* Get the source vertex */ 130 boost::graph_traits<Graph>::vertex_descriptor 131 s = vertex(SanJose,G); 132 133 cout << "*** Breadth First ***" << endl; 134 breadth_first_search 135 (G, s, visitor(make_bfs_visitor(boost::make_list(city_arrival(names), 136 neighbor_cities(names), 137 finish_city(names))))); 138 139 return 0; 140 }