譯:Boost Property Maps

傳送門: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 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 Properties

 一個支持內部屬性(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被添加到圖的邊和頂點中。

Exterior Properties

這一節咱們將描述兩個構造外部 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 }

Constructing an Exterior Property Map

實現你本身的外部 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 同樣來使用。

附example代碼:

example/exterior_properties.cpp

 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 }

example/city_visitor.cpp

  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 }
相關文章
相關標籤/搜索