Как использовать алгоритм повышения графа на именованном графе?

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

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

Код (без шаблона именованных свойств графа) выглядит следующим образом:

Graph g;

// add a few named vertices
auto v1 = add_vertex(1111, g);
auto v2 = add_vertex(2222, g);  
auto v3 = add_vertex(3333, g);

// add 2 edges
add_edge(1111, 2222, g);
add_edge(2222, 3333, g);

simple_bfs_visitor bfs_visitor_instance{};
//boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance)); // fails to compile
//boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance).vertex_index_map(boost::get(&Vertex::id, g))); // compiles but fails with exception

Может ли кто-нибудь привести пример того, как вызвать алгоритм графа на предоставленном графе?

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


Вот полный (не рабочий) код:

struct Vertex
{
    size_t      id;
    std::string other = "default-constructed";
};
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
    struct type {
        using result_type = size_t;
        result_type const& operator()(Vertex const& bundle) const {
            return bundle.id;
        }
    };
};

template <> struct boost::graph::internal_vertex_constructor<Vertex> {
    struct type {
    private:
        using extractor = typename internal_vertex_name<Vertex>::type;
        using name_t = std::decay_t<typename extractor::result_type>;

    public:
        using argument_type = name_t;
        using result_type = Vertex;

        result_type operator()(const name_t& id) const { return { id }; }
    };
};

using Graph = boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;

class simple_bfs_visitor : public boost::default_bfs_visitor
{
public:
    void discover_vertex(const Graph::vertex_descriptor& v, const Graph& g) const
    {
        std::cout << "hi from bfs" << '\n';
    }
};

void Func()
{
    Graph g;

    // add a few named vertices
    auto v1 = add_vertex(1111, g);
    auto v2 = add_vertex(2222, g);  
    auto v3 = add_vertex(3333, g);

    // add 2 edges
    add_edge(1111, 2222, g);
    add_edge(2222, 3333, g);

    simple_bfs_visitor bfs_visitor_instance{};
    //boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance)); // fails to compile
    //boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance).vertex_index_map(boost::get(&Vertex::id, g))); // fails with exception
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
33
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предупреждал вас на днях:

  1. Я выбираю listS, потому что он обеспечивает стабильность ссылок/дескрипторов. Это, однако, подразумевает отсутствие неявного свойства vertex_index_t.
  2. Если вы сделаете это делатьvecS, то у вас будет конфликт с типом идентификатора (size_t) в разрешении перегрузки
  3. В ПС. Я вспомнил, что что vertex_index предполагается (алгоритмами BGL) для сопоставления с непрерывным доменом [0, num_vertices(g)), поэтому данные значения не будут удовлетворять требованиям, что делает небезопасным фактическое использование его в качестве идентификатора вершины.. Это прекрасно объясняет, почему, если вы форсируете это, как в вашем примере (с get(&Vertex::id, g)), это не пойдет хорошо.

Проверка некоторых вещей

В разделе 3. давайте проверим документацию на breadth_first_search, и да, там прямо указано:

IN:vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)).

Итак, не делайте того, что вы пытались! Это приведет к Неопределенное поведение. Но читайте дальше:

This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.

Default: get(vertex_index, g). Note: if you use this default, make sure your graph has an internal vertex_index property. For example, adjacency_list with VertexList=listS does not have an internal vertex_index property.

Печальная новость: все ваши выпуски были точно отозваны.

Хорошая новость: у нас есть 3 варианта исправить это:

  1. пройти свою собственную цветную карту
  2. передать внешний индекс вершины
  3. заставьте adjacency_list снова использовать vecS, но избегайте конфликтов перегрузки

1. Передайте свою собственную цветовую карту

Вы можете посмотреть документацию, чтобы увидеть требования. Самый простой способ удовлетворить это:

std::map<V, boost::default_color_type> colors;
auto color_map = boost::make_assoc_property_map(colors);

Теперь вы называете это:

breadth_first_search(                    //
    g, v1,                               //
    boost::visitor(bfs_visitor_instance) //
        .vertex_color_map(color_map)     //
);

2. Передайте свой собственный индекс вершины

Вместо этого вы можете указать index. Очень похоже:

std::map<V, int> index;
auto index_map = boost::make_assoc_property_map(index);

Но на этот раз вы должны убедиться карта заполняется в соответствии с ожиданиями!

// important: remember to make the data up-to-date!
for (auto v : boost::make_iterator_range(vertices(g)))
    index_map[v] = index.size();

И мы снова вызываем это так:

breadth_first_search(                    //
    g, v1,                               //
    boost::visitor(bfs_visitor_instance) //
        .vertex_index_map(index_map)     //
);

2б. Интермеццо

Конечно, вы можете предоставить оба, хотя это не обязательно. Это может быть оптимизация, если вы много раз вызываете BFS или хотите использовать свою собственную структуру данных (например, flat_map<V, color, small_vector<V, N> >, чтобы вы могли избежать всех распределений там):

breadth_first_search(                    //
    g, v1,                               //
    boost::visitor(bfs_visitor_instance) //
        .vertex_color_map(color_map)     //
        .vertex_index_map(index_map)     //
);

3. Используйте ВекС...

Это влияет на стабильность дескриптора, но давайте хотя бы покажем. Я бы предложил использовать тип оболочки для идентификатора:

struct Id {
    size_t _val;
    Id(size_t v = {}) : _val(v) {}

    // relay some common operations
    friend std::ostream& operator<<(std::ostream& os, Id const& id) { return os << "Id:" << id._val; }
    friend size_t hash_value(Id const& id) { return boost::hash_value(id._val); }
    auto operator<=>(Id const&) const = default;
};

Нам нужен хэш/равенство для поиска имен и operator<< для печати графика. Конечно, реализация тривиальна.

С этим все «просто работает», потому что Graph имеет внутреннюю неявную vertex_index:

boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance));

Живой список всего вышеперечисленного

Жить на Колиру

Скомпилировано дважды, с определением USE_VCES или без него:

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>

#ifndef USE_VECS
    using VertexS = boost::listS;
    using Id      = size_t;
#else
    using VertexS = boost::vecS;
    struct Id {
        size_t _val;
        Id(size_t v = {}) : _val(v) {}

        // relay some common operations
        friend std::ostream& operator<<(std::ostream& os, Id const& id) { return os << "Id:" << id._val; }
        friend size_t hash_value(Id const& id) { return boost::hash_value(id._val); }
        auto operator<=>(Id const&) const = default;
    };
#endif

struct Vertex {
    Id id;
    std::string other = "default-constructed";
};

using Graph =
    boost::adjacency_list<boost::vecS, VertexS, boost::directedS, Vertex>;

// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
    struct type {
        using result_type = decltype(Vertex::id);
        result_type const& operator()(Vertex const& bundle) const {
            return bundle.id;
        }
    };
};

template <> struct boost::graph::internal_vertex_constructor<Vertex> {
    struct type {
      private:
        using extractor = typename internal_vertex_name<Vertex>::type;
        using name_t    = std::decay_t<typename extractor::result_type>;

      public:
        using argument_type = name_t;
        using result_type = Vertex;

        result_type operator()(const name_t& id) const { return {id}; }
    };
};

using V = Graph::vertex_descriptor;

struct simple_bfs_visitor : boost::default_bfs_visitor {
    void discover_vertex(V, const Graph&) const {
        std::cout << "hi from bfs" << '\n';
    }
};

int main() {
    Graph g;

    V v1 = add_vertex(1111, g);
    /*V v2 =*/ add_vertex(2222, g);
    /*V v3 =*/ add_vertex(3333, g);

    add_edge(Id{1111}, Id{2222}, g);
    add_edge(Id{2222}, Id{3333}, g);

    print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
    print_graph(g, get(&Vertex::other, g), std::cout << "---\n");

    simple_bfs_visitor bfs_visitor_instance{};

    // 1. bring your own colors
    std::map<V, boost::default_color_type> colors;
    auto color_map = boost::make_assoc_property_map(colors);

    breadth_first_search(                    //
        g, v1,                               //
        boost::visitor(bfs_visitor_instance) //
            .vertex_color_map(color_map)     //
    );

    // 2. bring your own index
    std::map<V, int> index;
    auto index_map = boost::make_assoc_property_map(index);

    // important: remember to make the data up-to-date!
    for (auto v : boost::make_iterator_range(vertices(g)))
        index_map[v] = index.size();

    breadth_first_search(                    //
        g, v1,                               //
        boost::visitor(bfs_visitor_instance) //
            .vertex_index_map(index_map)     //
    );

    // 2b. bring your own
    breadth_first_search(                    //
        g, v1,                               //
        boost::visitor(bfs_visitor_instance) //
            .vertex_color_map(color_map)     //
            .vertex_index_map(index_map)     //
    );

#ifdef USE_VECS
    // 3. use vecS but not `size_t` as the Id:
    boost::breadth_first_search(g, v1, boost::visitor(bfs_visitor_instance));
#endif
}

Скомпилируйте и запустите:

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -DUSE_VECS -o vecS.exe
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -o listS.exe
set -x
./vecS.exe
./listS.exe

Отпечатки

./vecS.exe
./listS.exe
+ ./vecS.exe
---
Id:1111 --> Id:2222 
Id:2222 --> Id:3333 
Id:3333 --> 
---
default-constructed --> default-constructed 
default-constructed --> default-constructed 
default-constructed --> 
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
+ ./listS.exe
---
1111 --> 2222 
2222 --> 3333 
3333 --> 
---
default-constructed --> default-constructed 
default-constructed --> default-constructed 
default-constructed --> 
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs
hi from bfs

Если вам плевать на здравомыслие/хотите жить на грани, вы мог, конечно же, являетесь id собственностью, которая существенно отличается от Graph::vertex_descriptor, и просто покончите с этим: coliru.stacked-crooked.com/a/0025a1477d2da8e8

sehe 20.03.2022 01:46

Еще одно решение, которое, похоже, работает, — использовать сам идентификатор в качестве цветовой карты: boost::visitor(bfs_visitor_instance).color_map(boost::get(&V‌​ertex::id, g)). Как думаете, получится или я что-то пропустил?

Elad Maimoni 20.03.2022 09:14

Хорошо, я отвечу на свой вопрос: нет. Использование идентификаторов в качестве цветовой карты не имеет смысла. цветовая карта доступна для записи, а это означает, что boost переопределит идентификаторы. возможно повреждение всей структуры данных.

Elad Maimoni 20.03.2022 10:22

Но, с другой стороны, добавление поля цвета к каждой вершине и использование его в качестве цвета работает просто отлично, насколько я вижу.

Elad Maimoni 20.03.2022 10:43

«кажется, работает, чтобы использовать сам идентификатор в качестве цветовой карты» - конечно, нет. Это (очевидно) перезапишет поле id цветом вершины. Это не то, что вам нужно, но, что более важно, нарушает контракт «уникальные имена» из свойства внутреннего имени.

sehe 20.03.2022 16:04

Ах, теперь видите, что вы так поняли (во втором комментарии)

sehe 20.03.2022 16:05

О, у меня завалялся этот, который я забыл опубликовать: вы можете использовать внутренние свойства, чтобы не указывать карты свойств вручную. Обратите внимание, что вам все равно нужно заполнить индекс вершины, если вы не используете неявный (из-за использования vecS): coliru.stacked-crooked.com/a/286667d2e2845bcf

sehe 21.03.2022 00:12

Другие вопросы по теме