簡體   English   中英

在將listS用於頂點和邊列表時無法調用boost :: clear_vertex

[英]Unable to call boost::clear_vertex while using listS for the vertex and edge lists

我正在編寫一個程序,該程序使用升壓圖庫來解決A *搜索和最小生成樹啟發式問題的Traveling Salesman問題。 我對boost :: graph很陌生,在我的啟發式課程中,我計算了所有尚未訪問的頂點的最小生成樹。 通過維護原始圖形的副本來跟蹤訪問過哪些頂點,在每次調用啟發式方法時,都會從中刪除當前頂點及其所有邊。 但是,當我去調用boost::clear_vertex(u, subGraph) ,其中uvertex_descriptorsubGraph是要從中減去頂點的原始圖的副本,我得到了一個調試斷言失敗的陳述:

列表擦除迭代器超出范圍。

經過一些調試后,我發現最終錯誤是在STL <list> 1383行上生成的,出於某種原因,以下條件為false:

_Where._Getcont() != _STD addressof(this->_Get_data())

這是我的啟發式課程:

class MST_Heuristic : public astar_heuristic<MyGraphType, double>
{
public:
    MST_Heuristic(vertex_descriptor goal, MyGraphType g)
        : m_goal(goal), subGraph(g), firstRun(true) {}
    double operator () (vertex_descriptor u)
    {
        double MSTDist = 0.0;
        double startDist = numeric_limits<double>::infinity();
        int minEdgeWeight = subGraph[*out_edges(u, subGraph).first].weight;         // initialize minEdgeWeight to weight of first out edge

        if (firstRun)
        {
            IndexMap mapIndex;
            associative_property_map<IndexMap> vertexIndices(mapIndex);
            int j = 0;
            for (auto v = vertices(subGraph).first; v != vertices(subGraph).second; v++)
            {
                put(vertexIndices, *v, j++);
            }

            dijkstra_shortest_paths(subGraph, u, get(&VertexData::pred, subGraph),  // calculate the shortest path from the start for each vertex
                get(&VertexData::dist2, subGraph), get(&EdgeData::weight, subGraph),
                vertexIndices, less<double>(), plus<double>(),
                numeric_limits<double>::infinity(), 0, do_nothing_dijkstra_visitor(),
                get(&VertexData::color, subGraph));
        }
        for (auto ed : make_iterator_range(out_edges(u, subGraph)))
        {
            minEdgeWeight = min(subGraph[ed].weight, minEdgeWeight);                // find distance from nearest unvisited vertex to the current vertex
        }
        clear_vertex(u, subGraph);
        remove_vertex(u, subGraph);
        // Problem here; The problem has to do with removing vertices/edges and destabilizing the graph, thereby making it impossible to iterate through the graph

        IndexMap mapIndex;
        associative_property_map<IndexMap> vertexIndices(mapIndex);
        int j = 0;
        for (auto v = vertices(subGraph).first; v != vertices(subGraph).second; v++)
        {
            put(vertexIndices, *v, j++);
        }

        prim_minimum_spanning_tree(subGraph, *vertices(subGraph).first,             // calculate the minimum spanning tree
            get(&VertexData::pred, subGraph), get(&VertexData::dist, subGraph),
            get(&EdgeData::weight, subGraph), vertexIndices,
            do_nothing_dijkstra_visitor());

        for (auto vd : make_iterator_range(vertices(subGraph)))                     // estimate distance to travel all the unvisited vertices
        {
            MSTDist += subGraph[vd].dist;
            startDist = min(startDist, subGraph[vd].dist2);
        }

        firstRun = false;
        return static_cast<double>(minEdgeWeight) + MSTDist + startDist;            // return the result of the heuristic function
    }
private:
    vertex_descriptor m_goal;
    MyGraphType subGraph;
    bool firstRun;
};

以下是一些相關的typedef:

typedef adjacency_list_traits<listS, listS, undirectedS> GraphTraits;               // to simplify the next definition

typedef GraphTraits::vertex_descriptor vertex_descriptor;                           // vertex descriptor for the graph

typedef GraphTraits::edge_descriptor edge_descriptor;                               // edge descriptor for the graph

typedef std::map<vertex_descriptor, size_t>IndexMap;                                // type used for the vertex index property map

typedef adjacency_list<listS, listS, undirectedS,VertexData, EdgeData> MyGraphType; // graph type

我真的很感謝有人為我弄清楚為什么會這樣。 另外,可能我對啟發式課程的想法完全是愚蠢的,因此,如果您認為我應該嘗試使用其他方法來生成最小生成樹啟發式而不是繼續對此進行弄亂,那么我肯定對潛在客戶持開放態度。 如果我的啟發式方法很愚蠢,那么我真的很樂意就其他方法提出一些建議。 我的增強版為boost_1_67_0,我正在使用MS Visual Studio 2017。

您正在通過MSVC進行迭代器調試檢查。 這很好,因為否則您可能不知道它,並且您的程序會(無聲) 未定義行為

現在讓我看一下代碼。

這看起來很可疑:

double minEdgeWeight =
    subGraph[*out_edges(u, subGraph).first].weight; // initialize minEdgeWeight to weight of first out edge

這隱含了一個假設,即u具有至少一個輸出邊緣。 這可能是正確的,但您應該檢查一下。

用UbSan進行進一步檢查

/home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:82:30: runtime error: load of value 3200171710, which is not a valid value for type 'boost::default_color_type'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:82:30 in 
/home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:83:13: runtime error: load of value 3200171710, which is not a valid value for type 'ColorValue' (aka 'boost::default_color_type')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:83:13 in 
/home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:87:15: runtime error: load of value 3200171710, which is not a valid value for type 'ColorValue' (aka 'boost::default_color_type')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/sehe/custom/boost_1_67_0/boost/graph/breadth_first_search.hpp:87:15 in 
sotest: /home/sehe/custom/boost_1_67_0/boost/graph/two_bit_color_map.hpp:86: void boost::put(const two_bit_color_map<IndexMap> &, typename property_traits<IndexMap>::key_type, boost::two_bit_color_type) [IndexMap = boost::associative_property_map<std::map<void *, unsigned long, std::less<void *>, std::allocator<std::pair<void *const, unsigned long> > > >]: Assertion `(std::size_t)i < pm.n' failed.

初始化顏色圖可能是個好主意。 我不知道這是否適用於您的代碼,因為您沒有再次包含相關代碼。

所以我改變了:

struct VertexData {
    vertex_descriptor pred;
    double dist = 0, dist2 = 0;
    boost::default_color_type color = {};
};

不,還是一樣的錯誤。 現在閱讀代碼。

... 20分鍾以后。 啊哈。 subGraph圖形復制到subGraph 但是,您還傳遞了參數u 那怎么可能是正確的? 頂點u很可能不是來自subGraph 這可能是另一個錯誤來源。

讓我們也修復此問題:

msth(msth.vertex(2));

使用新的成員訪問器:

vertex_descriptor vertex(std::size_t n) const {
    return boost::vertex(n, subGraph);
}

發表您的評論

    // Problem here; The problem has to do with removing vertices/edges and destabilizing the graph, thereby making
    // it impossible to iterate through the graph

很明顯,您從圖形外部獲得了一個頂點u 沒什么關於“破壞穩定”的(這不是它的工作方式。迭代器有時會失效,但是沒有任何事情會因此而變得不穩定:如果不小心,您可能會調用未定義的行為)。

至少,當通過有效的u UbSan和ASan不在這里抱怨,這是一個好兆頭。 您的編譯器的Debug Iterators很可能也不會抱怨。

現在,請注意:

  • listS不能否定任何其他迭代器remove (這也是在迭代器失效規則 )。 顯然,只刪除了一個。

  • m_goal遇到與u相同的問題:由於要復制整個圖,因此它幾乎不可能來自正確的圖

  • 即使remove僅會使該特定的頂點描述符無效,但您似乎仍在嘗試在A *搜索的回調中執行此操作。 這很可能會破壞該算法假定的不變式(我沒有檢查過文檔,但是您應該!同樣,這是因為您沒有顯示與A *相關的​​代碼)。

  • 無論權Weight是否為double ,您的代碼似乎仍然殘缺不全。 (為什么要使用static_cast ?)


最終結果

這是我最終得到的結果,其中包括各種清理工作。

生活在Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iomanip>
#include <numeric>

typedef boost::adjacency_list_traits<boost::listS, boost::listS, boost::undirectedS>
    GraphTraits;                                          // to simplify the next definition
typedef GraphTraits::vertex_descriptor vertex_descriptor; // vertex descriptor for the graph
typedef GraphTraits::edge_descriptor edge_descriptor;     // edge descriptor for the graph
typedef double Weight;

struct VertexData {
    std::string name;
    VertexData(std::string name = "") : name(std::move(name)) {}
    //
    vertex_descriptor pred {};
    Weight dist = 0, dist2 = 0;
    boost::default_color_type color = {};

    friend std::ostream& operator<<(std::ostream &os, VertexData const &vd) {
        return os << "{name:" << std::quoted(vd.name) << "}";
    }
};

struct EdgeData {
    Weight weight = 1;
};

typedef boost::adjacency_list<boost::listS, boost::listS, boost::undirectedS, VertexData, EdgeData>
    MyGraphType; // graph type

class MST_Heuristic : public boost::astar_heuristic<MyGraphType, Weight> {
    struct do_nothing_dijkstra_visitor : boost::default_dijkstra_visitor {};

    auto make_index() const {
        std::map<vertex_descriptor, size_t> m;
        size_t n=0;
        for (auto vd : boost::make_iterator_range(vertices(subGraph)))
            m[vd] = n++;
        return m;
    }
  public:
    MST_Heuristic(MyGraphType g) : subGraph(g), firstRun(true) {}

    Weight operator()(vertex_descriptor u) {

        if (firstRun) {
            auto idx = make_index();
            dijkstra_shortest_paths(
                subGraph, u,
                get(&VertexData::pred, subGraph), // calculate the shortest path from the start for each vertex
                get(&VertexData::dist2, subGraph),
                get(&EdgeData::weight, subGraph),
                boost::make_assoc_property_map(idx), std::less<Weight>(),
                std::plus<Weight>(), std::numeric_limits<Weight>::infinity(), 0, do_nothing_dijkstra_visitor(),
                get(&VertexData::color, subGraph));
        }

        Weight minEdgeWeight = std::numeric_limits<Weight>::max(); // initialize minEdgeWeight to weight of first out edge
        for (auto ed : make_iterator_range(out_edges(u, subGraph))) {
            minEdgeWeight = std::min(subGraph[ed].weight, minEdgeWeight); // find distance from nearest unvisited vertex to the current vertex
        }

        clear_vertex(u, subGraph);
        remove_vertex(u, subGraph);

        {
            auto idx = make_index();
            prim_minimum_spanning_tree(subGraph, vertex(0), // calculate the minimum spanning tree
                                       get(&VertexData::pred, subGraph), get(&VertexData::dist, subGraph),
                                       get(&EdgeData::weight, subGraph), boost::make_assoc_property_map(idx),
                                       do_nothing_dijkstra_visitor());
        }

        //// combine
        Weight MSTDist = 0.0;
        Weight startDist = std::numeric_limits<Weight>::infinity();

        for (auto vd : boost::make_iterator_range(vertices(subGraph))) // estimate distance to travel all the unvisited vertices
        {
            MSTDist += subGraph[vd].dist;
            startDist = std::min(startDist, subGraph[vd].dist2);
        }

        firstRun = false;
        return minEdgeWeight + MSTDist + startDist; // return the result of the heuristic function
    }

    vertex_descriptor vertex(std::size_t n) const {
        return boost::vertex(n, subGraph);
    }

  private:

    MyGraphType subGraph;
    bool firstRun;
};

int main() {
    MyGraphType g;

    auto v1 = add_vertex({"one"}, g);
    auto v2 = add_vertex({"two"}, g);
    auto v3 = add_vertex({"three"}, g);
    auto v4 = add_vertex({"four"}, g);
    auto v5 = add_vertex({"five"}, g);

    add_edge(v1, v2, g);
    add_edge(v2, v3, g);
    add_edge(v3, v4, g);
    add_edge(v4, v5, g);

    print_graph(g, get(&VertexData::name, g));

    MST_Heuristic msth(g);
    msth(msth.vertex(2));
}

打印

one <--> two 
two <--> one three 
three <--> two four 
four <--> three five 
five <--> four 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM