Что необходимо для использования алгоритмов BGL в существующих структурах данных (ребра и вершины в качестве вектора <Object *>)? - PullRequest
2 голосов
/ 18 мая 2019

У меня есть пользовательские структуры данных, такие как:

vector<myVertex *> my_vertices;
vector<myEdge *> my_edges;

Мой класс myEdge имеет методы source () и target (), возвращающие myVertex *, поэтому он должен быть полностью готов, как есть, верно?

Что внешняя адаптация мне нужно сделать, чтобы использовать график BGL с моими контейнерами? Мне известно о примерах адаптеров в документе , однако некоторая помощь будет принята с благодарностью!

Меня интересует простой тип базового графа adjacency_list, пока я не уверен в нужных мне концепциях обхода графа.

Что я понял до сих пор о параметрах adjacency_list:

adjacency_list<OutEdgeListS, VertexListS, DirectedS,
             VertexProperty, EdgeProperty, GraphProperty, EdgeListS>
  • OutEdgeListS и VertexListS являются селекторами для контейнеров, используемых для представления (1) списка ребер для каждой из вершин и (2) списка вершин. Эти контейнеры содержат элементы vertex_descriptor и edge_descriptor соответственно. Мой тип контейнера - простой std :: vector, поэтому я не думаю, что мне нужно создавать новый тип контейнера, как в example / container_gen.cpp. я нужно просто точно указать, возможно, с graph_traits, что тип моих элементов контейнера - указатель на объект.
  • VertexProperty и EdgeProperty предназначены для использования в качестве внутреннего объемного хранилища для дополнительной информации, например, цветовых меток, веса ребер ... и предлагают уже несколько лет функцию связанного свойства.

Я бы хотел, чтобы дескрипторы вершин и ребер по умолчанию не были целыми числами, а были указателями на мои объекты. В документации BGL прямо говорится, что это возможно в 2002-версии книги , 12.1.2:

Реализация объектно-ориентированного графа может использовать указатели для кучи выделенные объекты вершин. С классом черт графа эти различия скрыты типом, связанным с дескриптором вершины.

Хотя, похоже, он исчез из текущего онлайн-документа по версии 1.70.

В идеале я хотел бы инициализировать так:

MyGraph g(const& my_edges,const& my_vertices,
  undirected_tag, some_color, someweights, allow_parallel_edges_tag);

P.S. Я не заинтересован в заполнении указателей объектов в property_map. Я не хочу использовать 'vecS по умолчанию', std :: vector, где дескриптор является целым числом. Я готов использовать «пользовательский vecS» в качестве std :: vector указателей на объекты; для OutEdgeList и VertexList.

Редактировать: это тот же самый точный вопрос, что и «1». это . За исключением того, что он так и не получил ответа ... и предлагаемое решение было для "2.", с property_map и дорогим двойным отображением :). Похоже, что после того как сотни часов копались в SO, большинство людей рекомендуют использовать property_maps, а не работать с пользовательскими контейнерами. Люди склонны использовать property_maps для хранения фактических узлов и ребер - своих указателей на объекты, и позволяют vertex & edge_descriptors содержать целочисленные индексы по умолчанию. Тем не менее, из того, что я прочитал здесь , есть "внизу" vertex_descriptor - внутренний слой реального индекса для повышения.

В качестве контекста: я планирую использовать dijkstra / johnson_all_pairs_shortest_paths (с картой предшественника и посетителем?) И, далее, оптимальный-dreyfus-wagner для деревьев Штейнера с http://paal.mimuw.edu.pl/, библиотекой поверх bgl. Создание SQL-соединения для инструмента dbms-erd pgmodeler https://github.com/pgmodeler/pgmodeler/pull/1232.

20/05/19: Ответ на ответ sehe

Потрясающая информация, которая склеивает все части вместе и заставила меня догнать некоторые ключевые моменты, такие как концепции графиков. Я пришел спросить, как использовать список смежности с пользовательскими структурами данных, а вы пошли, чтобы объяснить, как определить полностью пользовательский граф.

Я собираюсь изучить компромиссы между подходами:

  1. сохранить мои структуры данных как есть, и ваше решение для пользовательского графа. я будет тратить совсем немного времени на инициализацию, но, вероятно, много больше времени на выяснение границ. Низкая сложность, но время сложность.
  2. Тот же подход, но рефакторинг моей библиотеки, добавление выделенного хранилища, с вектор падающих ребер на вершину (как атрибут класса мойVertex?).Внешний поиск с постоянным временем, а не O (log (n)) с (1) std :: equal_range?Вероятно, лучший вариант.
  3. Использовать adjacency_list, но иметь гарантии сложности времени bgl.
    • Создание списка смежности по умолчанию, настройка двустороннего сопоставления с контейнерами моей библиотеки / использование связанных / внутренних свойств.Высокая космическая сложность;низкая сложность по времени, но только для алгоритмов bgl, инициализация будет долгой.
    • Не хотите ли вы также уточнить, если наличие надлежащего OutEdgeList и VertexList делает использование класса adjacency-list с пользовательскими контейнерами опцией?Хранить ссылки на эти последние?На данный момент я подозреваю, что реализация adjacency_list может быть не такой гибкой.

1 Ответ

2 голосов
/ 19 мая 2019

Документация для концепций Graph удобно здесь: https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/graph_concepts.html

Итак, вы никогда не говорили нам, какие алгоритмы вы намереваетесь использовать.

Итак, позвольте мне выбрать примеры: BFS. Документы говорят, что для этого требуется:

Ориентированный или неориентированный граф.Тип графика должен быть моделью График списка вершин и График инцидентов .

Глядя на ваши уже существующие структуры данных, выглядит только вылегко охватить случай использования списка вершин.

Края реализованы больше как список краев.Невозможно эмулировать граф инцидентности из пограничного списка без времени выполнения или затрат на хранение (это математика, не имеет ничего общего с библиотекой или качеством кода).

В действительности, вполне вероятно, что вы пропустили части вашегоуже существующие структуры данных, которые имеют отношение к проблеме, так как большинство алгоритмов будут весьма неоптимальными только для списков Vertex + Edge.

На практике я предполагаю, что ваш список Edge может быть организован как классический список смежности.(например, упорядочение по исходной вершине, поэтому вы МОЖЕТЕ выполнить поиск O (log (n)) по исходной вершине).

Для примера ниже Я предполагаю, что это случай .Имейте в виду, что мы только приближаемся к гарантиям сложности из концепции графов инцидентов:

Гарантии сложности

source(), target() и out_edges() Функции должны быть все время.Функция out_degree() должна быть линейной по количеству выходных ребер.

Чтобы фактически выполнить эти требования, вам потребуется специальное хранилище выходных ребер для каждой вершины

Итак, давайте попробуем:

Mocking YourLibrary

namespace YourLibrary {
    struct myVertex {
    };

    struct myEdge {
        myVertex* _s = nullptr;
        myVertex* _t = nullptr;

        myVertex* source() const { return _s; }
        myVertex* target() const { return _t; }
    };

    using Vertices = std::vector<myVertex *>;
    using Edges = std::vector<myEdge *>;
}

Выполнение концепций графика

Вы хотели сохранить ссылкик уже существующим структурам данных:

namespace Glue {

    struct MyGraph {
        struct EdgeOrder {
            template <typename A, typename B>
                bool operator()(A const* a, B const* b) const { return source(a) < source(b); }
            private:
            static auto source(YourLibrary::myVertex const* v) { return v; }
            static auto source(YourLibrary::myEdge const* e) { return e->source(); }
        };

        using Vertices = YourLibrary::Vertices;
        using Edges = YourLibrary::Edges;

        Vertices& _vertices;
        Edges& _edges;

        MyGraph(Vertices& vv, Edges& ee) : _vertices(vv), _edges(ee)  { }
    };
}

Теперь я просто просмотрю список требуемых типов характеристик по концепции:

namespace boost {

    template <> struct graph_traits<Glue::MyGraph> {
        // Due to Graph concept
        using vertex_descriptor      = YourLibrary::myVertex*;
        using edge_descriptor        = YourLibrary::myEdge*;
        using directed_category      = directed_tag;
        using edge_parallel_category = allow_parallel_edge_tag;
        static vertex_descriptor null_vertex() { return nullptr; }

        // Due to Vertex List concept
        struct traversal_category : vertex_list_graph_tag, incidence_graph_tag { };
        using vertex_iterator        = Glue::MyGraph::Vertices::const_iterator;
        using vertices_size_type     = std::size_t;

        // Due to Incidence Graph concept
        using out_edge_iterator = Glue::MyGraph::Edges::const_iterator;
        using degree_size_type = std::size_t;
    };

}

И, наконец, заново открою пространство именпоэтому ADL может найти эти функции, которые необходимы для соответствия критериям «допустимых выражений»:

namespace Glue {
    // Due to Vertex List concept
    auto vertices(MyGraph const& g) {
        return std::make_pair(g._vertices.begin(), g._vertices.end());
    }

    std::size_t num_vertices(MyGraph const& g) {
        return g._vertices.size();
    }

    // Due to Incidence Graph concept
    auto source(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->source();
    }
    auto target(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->target();
    }

    auto out_edges(YourLibrary::myVertex const* v, MyGraph const& g) {
        return std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});;
    }
    std::size_t out_degree(YourLibrary::myVertex const* v, MyGraph const& g) {
        auto oee = std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});
        return std::distance(oee.first, oee.second);
    }
}

Это будет примерно функционально эквивалентно списку adjacency_list сsetS для контейнера вершин.

См. Это Live On Coliru

Запуск BFS

Все, что требуется дополнительно дляАргументы алгоритма.Вам понадобится и карта цветов, и карта индексов вершин.Это совершенно нормально и также потребуется, например, если у вас есть, например, adjacency_list<vecS, listS, directedS>.

. Я скрою карту индекса внутри оболочки MyGraph и открою карту цветов, чтобы вы могли выбрать свои предпочтения:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/container/flat_map.hpp>
#include <algorithm>

namespace YourLibrary {
    struct myVertex {
    };

    struct myEdge {
        myVertex* _s = nullptr;
        myVertex* _t = nullptr;

        myVertex* source() const { return _s; }
        myVertex* target() const { return _t; }
    };

    using Vertices = std::vector<myVertex *>;
    using Edges = std::vector<myEdge *>;
}

namespace Glue {

    struct MyGraph {
        struct EdgeOrder {
            template <typename A, typename B>
                bool operator()(A const* a, B const* b) const { return source(a) < source(b); }
            private:
            static auto source(YourLibrary::myVertex const* v) { return v; }
            static auto source(YourLibrary::myEdge const* e) { return e->source(); }
        };

        using Vertices = YourLibrary::Vertices;
        using Edges = YourLibrary::Edges;

        using Index = boost::container::flat_map<Vertices::value_type, std::size_t>;

        Vertices& _vertices;
        Edges& _edges;
        Index _index;

        MyGraph(Vertices& vv, Edges& ee) : _vertices(vv), _edges(ee)  {
            _index.reserve(vv.size());
            std::size_t i = 0;
            for(auto v : vv) { _index[v] = i++; }
        }
    };
}

namespace boost {

    template <> struct graph_traits<Glue::MyGraph> {
        // Due to Graph concept
        using vertex_descriptor      = YourLibrary::myVertex*;
        using edge_descriptor        = YourLibrary::myEdge*;
        using directed_category      = directed_tag;
        using edge_parallel_category = allow_parallel_edge_tag;
        static vertex_descriptor null_vertex() { return nullptr; }

        // Due to Vertex List concept
        struct traversal_category : vertex_list_graph_tag, incidence_graph_tag { };
        using vertex_iterator        = Glue::MyGraph::Vertices::const_iterator;
        using vertices_size_type     = std::size_t;

        // Due to Incidence Graph concept
        using out_edge_iterator = Glue::MyGraph::Edges::const_iterator;
        using degree_size_type = std::size_t;
    };

}

namespace Glue {
    // Due to Vertex List concept
    auto vertices(MyGraph const& g) {
        return std::make_pair(g._vertices.begin(), g._vertices.end());
    }

    std::size_t num_vertices(MyGraph const& g) {
        return g._vertices.size();
    }

    // Due to Incidence Graph concept
    auto source(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->source();
    }
    auto target(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->target();
    }

    auto out_edges(YourLibrary::myVertex const* v, MyGraph const& g) {
        return std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});;
    }
    std::size_t out_degree(YourLibrary::myVertex const* v, MyGraph const& g) {
        auto oee = std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});
        return std::distance(oee.first, oee.second);
    }

    // Due to BFD requiring the index_map
    auto get(boost::vertex_index_t, MyGraph const& g) {
        return boost::make_assoc_property_map(g._index);
    }
}

int main() {
    // I hate manual memory management, so let's own some objects
    auto a = std::make_unique<YourLibrary::myVertex>();
    auto b = std::make_unique<YourLibrary::myVertex>();
    auto c = std::make_unique<YourLibrary::myVertex>();
    auto ab = std::make_unique<YourLibrary::myEdge>(YourLibrary::myEdge{a.get(), b.get()});
    auto bc = std::make_unique<YourLibrary::myEdge>(YourLibrary::myEdge{b.get(), c.get()});

    // These were given in your question:
    YourLibrary::Vertices vv { a.get(), b.get(), c.get() };
    YourLibrary::Edges ee { ab.get(), bc.get() };

    // this is the glue required to fulfill the BGL concepts:
    Glue::MyGraph g(vv, ee);

    // this is showing that you can now BFS on it
    using V = boost::graph_traits<Glue::MyGraph>::vertex_descriptor;
    V start_vertex = a.get();
    std::map<V, boost::default_color_type> color_data;

    boost::breadth_first_search(g, start_vertex,
            boost::visitor(boost::default_bfs_visitor{})
            .color_map(boost::make_assoc_property_map(color_data)));
}

Заключение

Алгоритмы имеют требования, и пока выудовлетворяя их, вы можете использовать любую структуру данных, какую захотите.

В этом случае вы МОЖЕТЕ быть действительно уверены в сделанных предположениях и добавить это в конструктор MyGraph:

assert(std::is_sorted(_edges.begin(), _edges.end(), EdgeOrder{}));
...