Копирование из grid_graph в adjacency_list с помощью boost :: copy_graph - PullRequest
7 голосов
/ 23 сентября 2011

Я использую библиотеку графов буста и пытаюсь инициализировать MutableGraph, чтобы начать жизнь в виде сетки.Края будут добавлены и удалены позже, поэтому я думаю, что adjacency_list<vecS,listS,undirectedS> - правильный выбор.

Мое чтение о BGL показало, что разумным способом инициализации его этими краями было бы использование * 1005.* используя boost::copy_graph для копирования с boost::grid_graph, который может сделать все начальные ребра для меня бесплатно.Я думал, что это имеет смысл - copy_graph копирует из модели VertexListGraph в модель MutableGraph, что и есть то, что у меня есть.

Сначала я попытался использовать версию * с двумя аргументами copy_graph, со смутной надеждой, что что-то разумное случится со значениями по умолчанию для остальных.Оказалось, что это не так, у grid_graph (по причинам, которые я не совсем понял), похоже, нет возможности использовать PropertyMap s с ребрами или вершинами, поэтому по умолчанию vertex_copy иedge_copy не удалось (с ошибкой компилятора) копировать свойства.

Поскольку версия с двумя аргументами явно не показалась мне подходящей, я перешел и попытался реализовать свой собственный двоичный оператор для копирования вершин и ребер.Даже с копией «no-op» это не работает так, как я надеюсь (то есть не компилируется).

Я собрал минимальный рабочий пример, иллюстрирующий проблему:

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/graph/copy.hpp>

struct Position {
  int x, y;
};

struct VertexProperties {
  Position pos;
};

typedef boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, 
                      VertexProperties> Graph;

struct MyCopy {
  template <typename S, typename D>
  void operator()(const S& /*src*/, D& /*dest*/) {
    // Nothing for now, deduced types to try and just make it compile
    // TODO: set values for pos to reflect position on grid.
  }
};

int main() {
    boost::array<std::size_t, 2> lengths = { { 3, 3 } };
    boost::grid_graph<2> grid(lengths);

    Graph graph;
    MyCopy copier;
    // Using 3-Arg version of copy_graph so we can specify a custom way of copying to create the properties
    boost::copy_graph(grid,graph,boost::bgl_named_params<MyCopy,boost::vertex_copy_t,
                                 boost::bgl_named_params<MyCopy,boost::edge_copy_t> >(copier));
}

Этот пример не компилируется:

g++ -Wextra -Wall -O2 -g -o copytest.o -c copytest.cc
In file included from /usr/include/boost/graph/grid_graph.hpp:24:0,
                 from copytest.cc:2:
/usr/include/boost/iterator/transform_iterator.hpp: In constructor ‘boost::transform_iterator<UnaryFunction, Iterator, Reference, Value>::transform_iterator() [with UnaryFunc = boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >, Iterator = boost::counting_iterator<unsigned int, boost::use_default, boost::use_default>, Reference = boost::use_default, Value = boost::use_default]’:
/usr/include/boost/graph/copy.hpp:115:55:   instantiated from ‘static void boost::detail::copy_graph_impl<0>::apply(const Graph&, MutableGraph&, CopyVertex, CopyEdge, Orig2CopyVertexIndexMap, IndexMap) [with Graph = boost::grid_graph<2u>, MutableGraph = boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties>, CopyVertex = MyCopy, CopyEdge = MyCopy, IndexMap = boost::grid_graph_index_map<boost::grid_graph<2u>, boost::array<unsigned int, 2u>, unsigned int>, Orig2CopyVertexIndexMap = boost::iterator_property_map<__gnu_cxx::__normal_iterator<void**, std::vector<void*, std::allocator<void*> > >, boost::grid_graph_index_map<boost::grid_graph<2u>, boost::array<unsigned int, 2u>, unsigned int>, void*, void*&>]’
/usr/include/boost/graph/copy.hpp:327:5:   instantiated from ‘void boost::copy_graph(const VertexListGraph&, MutableGraph&, const boost::bgl_named_params<P, T, R>&) [with VertexListGraph = boost::grid_graph<2u>, MutableGraph = boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties>, P = MyCopy, T = boost::vertex_copy_t, R = boost::bgl_named_params<MyCopy, boost::edge_copy_t>]’
/mnt/home/ajw/code/hpcwales/copytest.cc:31:66:   instantiated from here
/usr/include/boost/iterator/transform_iterator.hpp:100:26: error: no matching function for call to ‘boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >::grid_graph_vertex_at()’
/usr/include/boost/graph/grid_graph.hpp:104:7: note: candidates are: boost::detail::grid_graph_vertex_at<Graph>::grid_graph_vertex_at(const Graph*) [with Graph = boost::grid_graph<2u>]
/usr/include/boost/graph/grid_graph.hpp:100:33: note:                 boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >::grid_graph_vertex_at(const boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >&)

Мой анализ этой ошибки состоит в том, что она, похоже, пытается по умолчанию создать часть внутренних компонентов grid_graph, чтоне может быть построен по умолчанию, по какой-то причине, которая мне не очень понятна.(Clang на самом деле не говорит мне ничего, чего я не вижу здесь от g ++).

Вопросы:

  1. Это правильный путь для инициализации изменяемого графа для началакак обычная сетка?Сначала я думал, что сделать это самому гораздо проще, чем написать функцию, но теперь я не уверен в этом!
  2. Почему значение по умолчанию orig_to_copy и / или vertex_index здесь не подходит?Я предполагаю, что эти два являются причиной ошибки.(Что, если таковые имеются, на самом деле вызывает проблему? Я не могу понять, какова основная причина текущей ошибки).
  3. Каков «правильный» способ исправить это?

1 Ответ

10 голосов
/ 24 сентября 2011

Вы на правильном пути, но есть две вещи, которые необходимо изменить в вашем коде. Во-первых, существует специальный метод определения пользовательских свойств вершин. Во-вторых, существует другой синтаксис (более предпочтительный и, вероятно, единственный, который является правильным) для именованных параметров BGL.

По первому пункту, пожалуйста, обратитесь к разделу документации под названием Пользовательские свойства вершин . По сути, для определения пользовательского свойства вершины сначала необходимо определить «тип тега» (struct с именем, оканчивающимся на _t):

struct vertex_position_t {
    typedef boost::vertex_property_tag kind;
};

Затем вы включаете тип тега где-то в шаблон boost::property, который определяет внутренне хранимые свойства вершин:

typedef boost::property<boost::vertex_index_t, std::size_t,
        boost::property<vertex_position_t, Position> > VertexProperties;

Вышеприведенное typedef определяет два хранимых внутри свойства: индекс и пользовательскую "позицию".

В отношении второго элемента, предпочтительный способ использовать именованные параметры - это синтаксис, похожий на цепочку методов. Например, если функция принимает два именованных параметра, named_param1 и named_param2, в пространстве имен boost с именами named_param1 и named_param2 есть две функции, соответственно. Функция boost::named_param1 принимает значение параметра named_param1 и возвращает объект, имеющий метод named_param2 (аналогично, функция boost::named_param2 принимает значение параметра named_param2 и возвращает объект, имеющий named_param1 метод ). Вы вызываете метод для установки значения этого именованного параметра (который, в свою очередь, возвращает другой объект, имеющий методы для других поддерживаемых именованных параметров).

Чтобы передать значения val1 и val2 для именованных параметров named_param1 и named_param2, вы можете использовать:

boost::named_parameter1(val1).named_param2(val2)

или

boost::named_parameter2(val2).named_param1(val1)

Для справки, вот полная программа, которая копирует сетку в объект типа Graph:

#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/property_map/property_map.hpp>

struct vertex_position_t {
    typedef boost::vertex_property_tag kind;
};

struct Position {
    std::size_t x, y;

    Position()
        : x(0), y(0)
    {
    }
};

typedef boost::property<boost::vertex_index_t, std::size_t, boost::property<vertex_position_t, Position> > VertexProperties;
typedef boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties> Graph;
typedef boost::graph_traits<Graph> GraphTraits;

namespace detail {
typedef boost::grid_graph<2> Grid;
typedef boost::graph_traits<Grid> GridTraits;

struct grid_to_graph_vertex_copier {
    typedef boost::property_map< Grid, boost::vertex_index_t>::type grid_vertex_index_map;
    typedef boost::property_map< ::Graph, boost::vertex_index_t>::type graph_vertex_index_map;
    typedef boost::property_map< ::Graph, ::vertex_position_t>::type graph_vertex_position_map;

    const Grid& grid;
    grid_vertex_index_map grid_vertex_index;
    graph_vertex_index_map graph_vertex_index;
    graph_vertex_position_map graph_vertex_position;

    grid_to_graph_vertex_copier(const Grid& grid_, Graph& graph)
        : grid(grid_), grid_vertex_index(get(boost::vertex_index_t(), grid_)),
        graph_vertex_index(get(boost::vertex_index_t(), graph)),
        graph_vertex_position(get(::vertex_position_t(), graph))
    {
    }

private:
    Position grid_vertex_index_to_position(std::size_t idx) const {
        unsigned num_dims = grid.dimensions();
        assert(grid.dimensions() == 2);

        idx %= grid.length(0) * grid.length(1);

        Position ret;
        ret.x = idx % grid.length(0);
        ret.y = idx / grid.length(0);

        return ret;
    }

public:
    void operator()(GridTraits::vertex_descriptor grid_vertex, ::GraphTraits::vertex_descriptor graph_vertex) const {
        std::size_t idx = get(grid_vertex_index, grid_vertex);
        put(graph_vertex_index, graph_vertex, idx);
        Position pos = grid_vertex_index_to_position(idx);
        std::cout << "grid_vertex = " << idx << ", pos.x = " << pos.x << ", pos.y = " << pos.y << std::endl;
        put(graph_vertex_position, graph_vertex, pos);
    }
};

struct grid_to_graph_edge_copier {
    void operator()(GridTraits::edge_descriptor grid_edge, ::GraphTraits::edge_descriptor graph_edge) const {
    }
};
}

int main()
{
    boost::array<std::size_t, 2> lengths = { { 3, 5 } };
    detail::Grid grid(lengths);

    Graph graph;

    boost::copy_graph(grid, graph, boost::vertex_copy(detail::grid_to_graph_vertex_copier(grid, graph))
            .edge_copy(detail::grid_to_graph_edge_copier()));

    std::cout << std::endl;
    boost::write_graphviz(std::cout, graph);

    return EXIT_SUCCESS;
}

Когда я запустил это, я получил следующий вывод:

grid_vertex = 0, pos.x = 0, pos.y = 0
grid_vertex = 1, pos.x = 1, pos.y = 0
grid_vertex = 2, pos.x = 2, pos.y = 0
grid_vertex = 3, pos.x = 0, pos.y = 1
grid_vertex = 4, pos.x = 1, pos.y = 1
grid_vertex = 5, pos.x = 2, pos.y = 1
grid_vertex = 6, pos.x = 0, pos.y = 2
grid_vertex = 7, pos.x = 1, pos.y = 2
grid_vertex = 8, pos.x = 2, pos.y = 2
grid_vertex = 9, pos.x = 0, pos.y = 3
grid_vertex = 10, pos.x = 1, pos.y = 3
grid_vertex = 11, pos.x = 2, pos.y = 3
grid_vertex = 12, pos.x = 0, pos.y = 4
grid_vertex = 13, pos.x = 1, pos.y = 4
grid_vertex = 14, pos.x = 2, pos.y = 4

graph G {
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;
12;
13;
14;
0--1 ;
1--2 ;
3--4 ;
4--5 ;
6--7 ;
7--8 ;
9--10 ;
10--11 ;
12--13 ;
13--14 ;
1--0 ;
2--1 ;
4--3 ;
5--4 ;
7--6 ;
8--7 ;
10--9 ;
11--10 ;
13--12 ;
14--13 ;
0--3 ;
1--4 ;
2--5 ;
3--6 ;
4--7 ;
5--8 ;
6--9 ;
7--10 ;
8--11 ;
9--12 ;
10--13 ;
11--14 ;
3--0 ;
4--1 ;
5--2 ;
6--3 ;
7--4 ;
8--5 ;
9--6 ;
10--7 ;
11--8 ;
12--9 ;
13--10 ;
14--11 ;
}
...