【问题标题】:BGL: adjacency list returns wrong edge property for descriptorBGL:邻接列表为描述符返回错误的边缘属性
【发布时间】:2015-05-19 10:52:03
【问题描述】:

我有一个存储为邻接列表的图形。顶点属性保存一个int ID,边缘属性保存一个4x4矩阵和一个权重;

在测试用例中,该图是一个 3 顶点图,其中一条边连接每对顶点(一个完整的图)。

我有一个边描述符向量PathType,代表一条路径,我正在遍历它并访问每个边及其属性RelationshipEdge,如下所示。

for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
        edge_t edge = *pathIterator;
        RelationshipEdge rEdge = m_graph[edge];
        int sourceID = m_graph[boost::source(edge, m_graph)].id;
        int destID = m_graph[boost::target(edge, m_graph)].id;

但有时执行此操作时,返回的RelationshipEdge 包含错误边缘的数据。

例如,检查 edge 显示为 1 的 m_source 和 2 的 m_target。如果我检查图形并找到源 1 和目标 2 的边,权重为 3,矩阵为进入。但是 rEdge 的权重为 1,并且矩阵不同。这些值实际上对应于源 0 和目标 1 的边。

我是否正确访问边缘属性?

我的图表类型的定义是:

typedef boost::adjacency_list< 
boost::vecS, boost::vecS, boost::undirectedS, MarkerVertex, RelationshipEdge>
CorrelationAdjacencyList;

【问题讨论】:

  • 你的图形类型定义在哪里,@Joe(考虑一个 SSCCE,因为你的散文建议你可以用一小组数据重现这个问题)
  • 我现在已经添加了。在同一路径中调用其他边时,甚至在不同路径中调用同一边时,我会返回正确的属性。这是我可以创建的一个尽可能小的示例,它仍在测试所有功能。
  • 对不起,我的水晶球没有想到它失败的原因。尝试发布 SSCCE

标签: c++ graph boost-graph adjacency-list


【解决方案1】:

我相信您的错误来自代码库中的其他地方。

我把这个简单的代码放在一个类似的图表上测试边缘访问和顺序,一切都按预期工作。

罪魁祸首可以手动维护edge_descriptorsvertex_descriptors 如sehe所述。或者你的路径向量初始化或构造。

#include <iostream>
#include <algorithm>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>

using namespace std;

enum edge_t {A,B};

struct MarkerVertex{
    std::string id;
};

struct RelationshipEdge{
    std::string id;
};


typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
    MarkerVertex, RelationshipEdge> CorrelationAdjacencyList;


typedef boost::graph_traits<CorrelationAdjacencyList>::edge_descriptor   Edge;
typedef boost::graph_traits<CorrelationAdjacencyList>::vertex_descriptor Vertex;

typedef vector<Edge> PathType;


void printPath(PathType &p, CorrelationAdjacencyList &g){
    cout << "print path" << endl;

    for(PathType::iterator pi = p.begin(); pi != p.end(); ++pi){
        Edge e = *pi;

        Vertex s = boost::source(e,g);
        Vertex t = boost::target(e,g);

        cout << g[s].id << "\t" 
             << g[e].id << "\t"
             << g[t].id << endl;

        bool isFound;
        Edge eForward;
        boost::tie(eForward,isFound) = boost::edge(s,t,g);
        cout << "forward  " << g[eForward].id << "\t" << isFound << endl;

        Edge eBackward;
        boost::tie(eBackward,isFound) = boost::edge(t,s,g);
        cout << "backward " << g[eBackward].id << "\t" << isFound << endl;
    }
}


int main(int argc, char* argv[]){

    CorrelationAdjacencyList graph;

    Vertex a = boost::add_vertex(graph); graph[a].id = "a";
    Vertex b = boost::add_vertex(graph); graph[b].id = "b";
    Vertex c = boost::add_vertex(graph); graph[c].id = "c";

    Edge e1 = boost::add_edge(a,b,graph).first; graph[e1].id = "e1";
    Edge e2 = boost::add_edge(b,c,graph).first; graph[e2].id = "e2";
    Edge e3 = boost::add_edge(c,a,graph).first; graph[e3].id = "e3";

    PathType path1,path2,path3;

    path1.push_back(e1);
    path1.push_back(e2);
    path1.push_back(e3);

    path2.push_back(e2);
    path2.push_back(e3);
    path2.push_back(e1);

    path3.push_back(e3);
    path3.push_back(e2);
    path3.push_back(e1);

    printPath(path1,graph);
    cout << endl;
    printPath(path2,graph);
    cout << endl;
    printPath(path3,graph);


    cin.get();
}

【讨论】:

  • 这里的工作量太大了。 +1 纯粹的勤奋
【解决方案2】:

如果没有更多信息,我可以有根据地猜测您使用 vecS 容器并且迭代器/引用已失效。

这会在插入/删除边/顶点时发生。

【讨论】:

  • 您使用 vecS 是对的,我已将图形类型定义添加到问题中。我正在经历几条路径,在之前的路径中,我调用了相同的边缘,但返回了正确的属性。在这些迭代之间没有插入或删除边或顶点。此外,在这两次迭代中,我都切换了源顶点和目标顶点,结果没有变化。在一次迭代中,两条边(已切换和未切换)都返回正确的属性,但在本次迭代中,它们都返回错误的属性。邻接列表中边缘的属性仍然正确。
【解决方案3】:

这是我找到的解决方案,虽然我不相信我完全理解为什么原来的方法不能。

我已将上面的代码替换为

for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
    edge_t edge = *pathIterator;

    int sourceID = m_graph[boost::source(edge, m_graph)].id;
    int destID = m_graph[boost::target(edge, m_graph)].id;

    int lowerID, higherID;
    if (sourceID < destID){
        lowerID = sourceID;
        higherID = destID;
    } else {
        lowerID = destID;
        higherID = sourceID;
    }

    edge_t edge2 = m_edgeDescriptorsByEndpoints.at(make_pair(lowerID, higherID));
    RelationshipEdge rEdge = m_graph[edge2];

m_edgeDescriptorsByEndpoints 是顶点 ID 对到边描述符的映射。

所以我正在获取边描述符,获取源顶点和目标顶点的 ID,从地图中找到相应的边描述符,然后获取该边的属性。

不完全是一个令人满意的解决方案,但它确实有效,就我目前能够测试的而言。

编辑

按照@sehe 的建议,我用对boost::edge(u,v,g) 的调用替换了对我的地图的调用,因此代码现在如下:

for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
            edge_t edge = *pathIterator;

            vert_t sourceV = boost::source(edge, m_graph);
            vert_t targetV = boost::target(edge, m_graph);

            pair<edge_t, bool> edgePair = boost::edge(sourceV, targetV, m_graph);
            if (!edgePair.second){
                cerr << "Edge does not exist" << endl;
            }
            RelationshipEdge rEdge = m_graph[edgePair.first];

这段代码仍然导致 rEdge 包含正确的属性,这似乎很奇怪,因为我认为查询图形以获取边的顶点,然后查询图形以获取连接这些顶点的边总是会给你那个完全相同的边缘。

【讨论】:

  • 如果顺序很重要,看起来您正在使用有向图。你确定你使用的是undirectedS
  • 所以边可以在任何方向上遍历,从这个意义上说它是无向的,我们使用undirectedS,但是我们存储的属性(转换)有一个方向,我们需要意识到。我们选择始终从具有 lowerID 的标记的角度存储该转换。
  • “不完全是一个令人满意的解决方案” 我同意这里。它坐得不好。我想你可能有UB。你能分享更多的代码。简单的事情,比如PathType 是什么,edge_t 等等。一个回购链接就可以了
  • 另外请注意,您可以通过使用boost::edge(u,v,g) 来不使用地图: 如果存在从顶点 u 到顶点 v 的边,则返回包含一条这样的边和 true 的对。如果 u 和 v 之间没有边,则返回带有任意边描述符和 false 的对。 如果将 setS 用于 outedge 容器选择器,它甚至会很有效。添加外部映射似乎会导致错误和性能问题。
  • 嗯,您能否发布说明问题的测试用例的完整代码?
猜你喜欢
  • 2021-12-26
  • 1970-01-01
  • 2011-12-18
  • 1970-01-01
  • 2017-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多