【问题标题】:how is boost::property_map implemented in boost and how to change itboost::property_map 在 boost 中是如何实现的以及如何改变它
【发布时间】:2014-06-08 10:57:57
【问题描述】:

我想知道属性映射是如何在 boost Graph 中实现的。例如,

我有这样定义的顶点和边属性:

 //vertex property:-->
 struct NodeInfo {  int a , b , c; };   //actual bundled property

 struct NodeInfoPropertyTag {               // tag and kind  (as in boost documentation)
      typedef boost::vertex_property_tag kind;
      static  std::size_t const num;
 };

 std::size_t const NodeInfoPropertyTag::num = (std::size_t) &NodeInfoPropertyTag::num;

 //typedef the Vertex Property
 typedef boost::property <NodeInfoPropertyTag, NodeInfo>  NodeProperty;


 //Similar fashion for Edge Property --> some property for each edge of graph.
 typedef boost::property <EdgeInfoPropertyTag, EdgeInfo>  EdgeProperty;

我的图表的类型定义如下:

 typedef boost::adjacency_list <vecS, vecS, undirectedS, NodeProperty, EdgeProperty, no_property, listS>   Graph_t;

现在,在使用上面的 typedef 初始化图 G 时,我可以使用属性为顶点和边分配属性

例如:

  Graph_t  G;
  typedef graph_traits<Graph_t>::vertex_descriptor   vd_t;
  // edge_descriptor   ed_t;

  NodeInfo   ninfo1, ninfo2;   //put some values in ninfo  
  vd_t  v = add_vertex (ninfo1, G)   //add a vertex in G with property ninfo1 
  vd_t  u = add_vertex (ninfo2, G)   //add a vertex in G with property ninfo2


  EdgeInfo  einfo;   //initialize edgeinfo  for edge property
  add_edge (u, v, einfo, G )   edge (u, v) with property einfo is added in G

要访问任何顶点的节点属性,我可以使用以下两种方法中的任何一种:

 //method 1: direct method: using Tags

 // for a vertex "v" -->  get()
 NodInfo   info  =  boost::get (NodeInfoPropertyTag(), G, v)   //get v's property

 //modify  info  

 //put the modified property
  put (NodeInfoPropertyTag(), G, v, info)    // (put in G, key = v, value = info )


 //method 2 : using property maps.

 //Edge Map and Node Map  
 typedef typename boost::property_map <Graph_t, EdgeInfoPropertyTag>::type  EdgeMap;
 typedef typename boost::property_map <Graph_t, NodeInfoPropertyTag>::type  NodeMap;


 //edge e --> get
 EdgeInfo  einfo = boost::get (EdgeMap, e)

 //modify einfo

 //put 
 put (EdgeMap, e, einfo)   //put in the EdgeMap  key = e, value = einfo

现在,这两种操作基本相同:即使用

  //former is translated to the latter --> 
  get(NodeInfoPropertyTag(), G, "key")   is equivalent to  get (NodeMap, "key")

我的问题是:

  1. 这些属性是如何存储在 Graph 对象中的。
  2. 它是否存储在诸如 std::map 之类的映射中?或一些清单?或一些有效的类似地图的数据结构。
  3. 如果是这样,我怎样才能将底层数据结构修改为 std::unordered_map 甚至 concurrent_hashmap 或 boost::vector_property_map ?

注意: 我不确定是否可以使用 vector_prop_map (此处),但我真的很想使用它,以便 顶点 id 成为向量索引并且更高效 --> 它可能会导致边缘出现问题

我的图将包含一百万个顶点和许多边,以这种方式搜索 std::map 仍然是 log(n) ,但我希望可移植性改变 底层数据结构,以便我可以使用 unordered_map / concurrent_hashmap

我需要 concurrent_hashmap(来自英特尔 TBB),因为我将并行化我的算法 因此希望同时访问将要成为的属性映射 由线程修改。

请建议是否可以控制和修改boost图中用于存储边和顶点属性的此类底层数据结构。

【问题讨论】:

    标签: c++ algorithm boost graph boost-graph


    【解决方案1】:

    这些属性是如何存储在 Graph 对象中的。

    属性不会单独存储或类似的存储。

    顶点和边属性存储在图的顶点和边中。没有使用std::map 或其他一些关联容器。无论您作为 VertexProperties 和 EdgeProperties 模板参数提供给 adjacency_list 的什么,都将存储在顶点和边中,即与使用 std::list&lt;T&gt; 时相同,其中 T 将存储在链表的节点中(沿带有必要的 next-prev 指针)。换句话说,adjacency_list 将存储包含 VertexProperties 类型对象的顶点,以及所需的任何边列表(进出)。

    当您使用property_map(通过get/put 函数)时,它只是做一些模板元编程魔术来创建一个薄包装器,该包装器将读取/写入顶点或边的正确单个属性。从概念上讲,这是等价的:

    NodeInfo info = boost::get (NodeInfoPropertyTag(), G, v);
    
    // is conceptual equivalent to:
    
    NodeInfo info = G[v].NodeInfoProperty;
    

    这就是property-map所做的一切,它查找顶点属性(通过给定图形对象中的顶点描述符),并获取对应的顶点属性的数据成员(或子对象)到给定的标签类型。弄清楚如何为正确的属性标签获取正确的数据成员(或子对象)是一个模板元编程魔术,它可以在编译时计算出来(没有运行时开销)。而且,一般来说,从顶点描述符中查找顶点属性是一个常数时间的操作(例如,取消引用指针、按索引查找等)。

    总体而言,获取(用于读取或写入)特定顶点的特定属性是一个恒定时间操作。据我所知,对于您使用 adjacency_list 的模板参数所做的任何选择都是如此。

    如果是这样,我该如何将底层数据结构修改为 std::unordered_map 甚至 concurrent_hashmap 或 boost::vector_property_map ?

    您可以通过 OutEdgeList、VertexList 和 EdgeList 指定您希望如何存储顶点和边。属性本身没有额外的存储方法。在这些情况下,使用映射或哈希映射并没有多大意义。

    我真的很想用它让顶点id变成向量索引

    当您为 VertexList 参数指定 vecS 时,adjacency_list 已经是这种情况。

    我需要 concurrent_hashmap(来自 Intel TBB),因为我将并行化我的算法,因此希望并发访问将由线程修改的属性映射。

    您应该考虑改用 Parallel Graph 库。

    请建议是否可以控制和修改boost图中用于存储边和顶点属性的此类底层数据结构。

    您可以指定用于存储顶点和边列表的数据结构。您也可以(理论上)为这些容器添加新类型的容器。然而,根据我的经验,这真的很难,因为 adjacency_list 的实现很难让你完全理解,而且交换它的底层容器并不像 Boost 网站上宣传的那么容易。

    【讨论】:

    • @Mikael...非常感谢您的回答。我可以在共享内存上使用并行 BGL 来实现我的算法;进程组可以映射到核心。我只是想尝试使用 TBB parallel_for_each() 运行我的算法,因为我的串行算法使用 for_each() 进行工作。我只是关心当两个或多个线程访问相同属性时如何修改属性。也许我可以将它们声明为易失性或原子性。你怎么看 ?有些属性根本没有修改,而有些属性被修改了。
    • @user2404319 可以为属性使用原子,但我不知道它是否能很好地与boost::property 和相应的属性映射一起使用。我的意思是 get-put 函数可能不尊重原子读/写函数。我建议您使用捆绑属性而不是过时且复杂的boost::property 机制。它们处理起来要好得多,而且定义自己的属性映射类要容易得多,这些类可以尊重原子操作(或您可能想要加在它们上的任何锁)。
    • @user2404319 另外,当谈到在并行算法中使用 adjacency_list 时,我不会太担心属性的读/写(你可以用原子或一些锁来解决),但我会更担心对图形结构的并发修改(添加/删除顶点或边)。但是如果你的算法不修改图结构,那应该没问题。我认为当您对图形进行并发结构更改或想要分配存储时,Parallel Graph 更相关。
    • 很好的答案和讨论。只想指出,Parallel Boost Graph 是针对分布式并行(多节点、分布式内存)而不是线程级共享内存并行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    • 2011-05-11
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多