Невозможно вызвать boost :: clear_vertex при использовании listS для списков вершин и ребер - PullRequest
0 голосов
/ 14 декабря 2018

Я пишу программу, в которой используется библиотека графов надстроек для решения задачи коммивояжера с использованием поиска A * с минимальной эвристикой связующего дерева.Я довольно новичок в boost :: graph. В моем эвристическом классе я вычисляю минимальное остовное дерево всех вершин, которые еще не посещались.Я отслеживаю, какие вершины были посещены, сохраняя копию исходного графа, из которого я удаляю текущую вершину и все ее ребра при каждом вызове эвристики.Однако, когда я иду на вызов boost::clear_vertex(u, subGraph), где u - это vertex_descriptor, а subGraph - это копия исходного графа, из которого я вычитаю вершины, я получаю ошибку отладочного утверждения, в которой говорится:

Список итераторов стирания вне диапазона.

После некоторой отладки я обнаружил, что в конечном итоге ошибка генерируется в строке 1383 STL <list>, где по какой-то причине следующее условие ложно:

_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 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.

1 Ответ

0 голосов
/ 14 декабря 2018

Вы сталкиваетесь с проверками отладки итератора из 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.Однако вы также передаете параметр 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 здесь не жалуются, что является хорошим признаком.Скорее всего, итераторы отладки вашего компилятора также не будут жаловаться.

Теперь обратите внимание:

  • listS делает не недействительными любые другиеитераторы на remove (это также в правилах аннулирования итераторов ).Очевидно, только удаленный.

  • m_goal страдает той же проблемой, что и u: вряд ли он может быть из правого графа, поскольку вы копируете весь граф

  • Несмотря на то, что remove делает недействительным только этот конкретный дескриптор вершины, похоже, вы пытаетесь сделать это внутри обратного вызова для поиска A *.Это вполне может сломать инварианты, принятые этим алгоритмом (я не проверял документацию, но вы должны это сделать! Опять же, это потому, что вы не показываете код, связанный с A *).

  • Ваш код все еще кажется разорванным, является ли Weight или нет, double.(Почему static_cast?)


Конечный результат

Это то, что я получил в итоге, включая различные очистки.

Live On 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 
...