Библиотека Boost.Graph: как использовать boost::is_isomorphism с именованными вершинами

Эта проблема похожа на BGL: пример изоморфизма с инвариантами вершин

Я работаю над учебником Boost.Graph, и вызвать boost::is_isomorphism на двух графиках без свойств очень просто . Но я не могу заставить его работать, когда у вершин теперь есть имена.

Этот код показывает:

  • Как я создаю графы путей с именованными вершинами (неважно)
  • Мой тестовый код
  • Моя функция для проверки изоморфизма с именованной вершиной

Вот как я создаю графы путей с именованными вершинами (что довольно неважно, но показано для завершения):

boost::adjacency_list<
  boost::vecS,
  boost::vecS,
  boost::undirectedS,
  boost::property<
    boost::vertex_name_t, std::string
  >
>
create_named_vertices_path_graph(
  const std::vector<std::string>& names
) noexcept
{
  auto g = create_empty_undirected_named_vertices_graph();
  if (names.size() == 0) { return g; }

  auto vertex_name_map
    = get( //not boost::get
      boost::vertex_name,
      g
    );

  auto vd_1 = boost::add_vertex(g);
  vertex_name_map[vd_1] = *names.begin();
  if (names.size() == 1) return g;

  const auto j = std::end(names);
  auto i = std::begin(names);
  for (++i; i!=j; ++i) //Skip first
  {
    auto vd_2 = boost::add_vertex(g);
    vertex_name_map[vd_2] = *i;
    const auto aer = boost::add_edge(vd_1, vd_2, g);
    assert(aer.second);
    vd_1 = vd_2;
  }
  return g;
}

Вот мой тест:

void is_named_vertices_isomorphic_demo() noexcept
{
  const auto g = create_named_vertices_path_graph(
    { "Alpha", "Beta", "Gamma" }
  );
  const auto h = create_named_vertices_path_graph(
    { "Alpha", "Gamma", "Beta" }
  );
  assert( is_named_vertices_isomorphic(g,g));
  assert(!is_named_vertices_isomorphic(g,h));
}

Я хотел бы написать функцию is_named_vertices_isomorphic более или менее как таковую (обратите внимание: она будет скомпилирована, но не пройдет тест, как вдохновлено BGL: Пример изоморфизма с вершинными инвариантами ):

template <typename graph1, typename graph2>
bool is_named_vertices_isomorphic_correct(
  const graph1& g,
  const graph2& h
) noexcept
{
  auto ref_index_map = get(boost::vertex_index, g);
  using vd = typename boost::graph_traits<graph1>::vertex_descriptor;
  std::vector<vd> iso(boost::num_vertices(g));
  return boost::isomorphism(g,h,
    boost::isomorphism_map(
      make_iterator_property_map(iso.begin(), ref_index_map, iso[0])
    )
  );
}

Глядя на вопрос BGL: пример изоморфизма с вершинными инвариантами, я придумать это:

template <typename Graph>
std::string discrete_vertex_invariant(
  const typename boost::graph_traits<Graph>::vertex_descriptor& vd,
  const Graph &g
)
{
  const auto name_map = get(boost::vertex_name,g);
  return name_map[vd];
}

template <typename Graph>
class discrete_vertex_invariant_functor
{
    using vertex_t = typename boost::graph_traits<Graph>::vertex_descriptor;
    const Graph& m_graph;
public:
    using result_type = std::string;
    using argument_type = vertex_t;
    discrete_vertex_invariant_functor(const Graph &g) : m_graph(g) {}
    result_type operator()(const vertex_t& vd) const
    {
        return discrete_vertex_invariant(vd,m_graph);
    }
    result_type max() const
    {
        return "";
    }
};

//helper function to help with argument deduction
template <typename Graph>
discrete_vertex_invariant_functor<Graph> make_discrete_vertex_invariant(
  const Graph &g
)
{
  return discrete_vertex_invariant_functor<Graph>(g);
}

template <typename graph1, typename graph2>
bool is_named_vertices_isomorphic_correct(
  const graph1& g,
  const graph2& h
) noexcept
{
  auto ref_index_map = get(boost::vertex_index, g);
  using vd = typename boost::graph_traits<graph1>::vertex_descriptor;
  std::vector<vd> iso(boost::num_vertices(g));
  return boost::isomorphism(
    g,
    h,
    isomorphism_map(
      make_iterator_property_map(iso.begin(), ref_index_map, iso[0])
    ).vertex_invariant1(make_discrete_vertex_invariant(g))
     .vertex_invariant2(make_discrete_vertex_invariant(h))
  );
}

Оба решения терпят неудачу. Кто может мне помочь?


person richelbilderbeek    schedule 28.12.2015    source источник
comment
Я не знаю, сделано ли это намеренно, но все ваши ссылки указывают на ваш учебник, а не на вопрос, который вы, очевидно, пытались связать.   -  person llonesmiz    schedule 29.12.2015
comment
Поскольку вы не предоставляете полный компилируемый пример, я не могу проверить, но тот факт, что ваш max() не возвращает верхнюю границу возможных значений вашего инварианта, кажется неверным. Кроме того, добавление тега boost-graph может повысить вероятность того, что ваш вопрос дойдет до эксперта по библиотеке.   -  person llonesmiz    schedule 29.12.2015
comment
Почему вы хотите, чтобы второе утверждение было успешным? Графики явно изоморфны. Еще раз прочитать определение изоморфизма? Изоморфизм – это отображение вершин одного графа в соотношении 1 к 1 с вершинами другого графа, при котором сохраняется смежность.   -  person sehe    schedule 30.12.2015


Ответы (2)


Кажется, вам не нужен изоморфизм, по крайней мере, не строго в соответствии с определением.

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

Вот упрощенный способ добиться этого:

template <typename graph1, typename graph2>
bool is_named_vertices_isomorphic/*_correct*/(const graph1 &g, const graph2 &h) noexcept {
    auto ref_index_map = get(boost::vertex_index, g);
    using vd = typename boost::graph_traits<graph1>::vertex_descriptor;
    std::vector<vd> iso(boost::num_vertices(g));

    VertexInvariant::Map shared_names;
    VertexInvariant inv1 { g, shared_names };
    VertexInvariant inv2 { h, shared_names };

    inv1.collect_names();
    inv2.collect_names();

    return boost::isomorphism(g, h,
            boost::isomorphism_map(make_iterator_property_map(iso.begin(), ref_index_map))
            .vertex_invariant1(inv1)
            .vertex_invariant2(inv2)
        );
}

Я еще не сделал вещи общими, потому что это отвлекает.

Для хорошей формы вам нужно использовать boost::property_maps::property_traits<boost::property_map<graph1, boost::vertex_name_t>::const_type>::value_type или аналогичный, и проверить, что результирующий тип действительно совместим для сравнения между graph1 и graph1 и т. д.

Чтобы быть «на самом деле» универсальным, вы хотели бы передать выведенную карту свойств вместо boost::get(boost::vertex_name, g) и так далее.

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

struct VertexInvariant {
    using Map = std::map<std::string, size_t>;
    Graph const& _graph;
    Map&         _mappings;

    using result_type = size_t;
    using argument_type = Graph::vertex_descriptor;

    size_t operator()(argument_type u) const {
        return _mappings.at(boost::get(boost::vertex_name, _graph, u));
    }
    size_t max() const { return _mappings.size(); }

    void collect_names() {
        for (auto vd : boost::make_iterator_range(boost::vertices(_graph))) {
            size_t next_id = _mappings.size();
            auto ins = _mappings.insert({ boost::get(boost::vertex_name, _graph, vd), next_id});
            if (ins.second) {
                //std::cout << "Mapped '" << ins.first->first << "' to " << ins.first->second << "\n";
            }
        }
    }
};

Где помощник VertexInvariant определяется как:

struct VertexInvariant {
    using Map = std::map<std::string, size_t>;
    Graph const& _graph;
    Map&         _mappings;

    using result_type = size_t;
    using argument_type = Graph::vertex_descriptor;

    size_t operator()(argument_type u) const {
        return _mappings.at(boost::get(boost::vertex_name, _graph, u));
    }
    size_t max() const { return _mappings.size(); }

    void collect_names() {
        for (auto vd : boost::make_iterator_range(boost::vertices(_graph))) {
            size_t next_id = _mappings.size();
            auto ins = _mappings.insert({ boost::get(boost::vertex_name, _graph, vd), next_id});
            if (ins.second) {
                //std::cout << "Mapped '" << ins.first->first << "' to " << ins.first->second << "\n";
            }
        }
    }
};

Полная демонстрация

Жить на Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/isomorphism.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/graph_utility.hpp>

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
                                    boost::property<boost::vertex_name_t, std::string> >;

Graph create_empty_undirected_named_vertices_graph() { return {}; }

Graph create_named_vertices_path_graph(const std::vector<std::string> &names) noexcept {
    auto g = create_empty_undirected_named_vertices_graph();
    if (names.size() == 0) {
        return g;
    }

    auto vertex_name_map = get(boost::vertex_name, g); // not boost::get

    auto vd_1 = boost::add_vertex(g);
    vertex_name_map[vd_1] = *names.begin();
    if (names.size() == 1)
        return g;

    const auto j = std::end(names);
    auto name_it = std::begin(names);
    for (++name_it; name_it != j; ++name_it) // Skip first
    {
        auto vd_2 = boost::add_vertex(g);
        vertex_name_map[vd_2] = *name_it;
        const auto aer = boost::add_edge(vd_1, vd_2, g);
        assert(aer.second);
        vd_1 = vd_2;
    }
    return g;
}

//////////////////////////////////////// {{{
namespace {

    struct VertexInvariant {
        using Map = std::map<std::string, size_t>;
        Graph const& _graph;
        Map&         _mappings;

        using result_type = size_t;
        using argument_type = Graph::vertex_descriptor;

        size_t operator()(argument_type u) const {
            return _mappings.at(boost::get(boost::vertex_name, _graph, u));
        }
        size_t max() const { return _mappings.size(); }

        void collect_names() {
            for (auto vd : boost::make_iterator_range(boost::vertices(_graph))) {
                size_t next_id = _mappings.size();
                auto ins = _mappings.insert({ boost::get(boost::vertex_name, _graph, vd), next_id});
                if (ins.second) {
                    //std::cout << "Mapped '" << ins.first->first << "' to " << ins.first->second << "\n";
                }
            }
        }
    };


}
//////////////////////////////////////// }}}

template <typename graph1, typename graph2>
bool is_named_vertices_isomorphic/*_correct*/(const graph1 &g, const graph2 &h) noexcept {
    auto ref_index_map = get(boost::vertex_index, g);
    using vd = typename boost::graph_traits<graph1>::vertex_descriptor;
    std::vector<vd> iso(boost::num_vertices(g));

    VertexInvariant::Map shared_names;
    VertexInvariant inv1 { g, shared_names };
    VertexInvariant inv2 { h, shared_names };

    inv1.collect_names();
    inv2.collect_names();

    return boost::isomorphism(g, h,
            boost::isomorphism_map(make_iterator_property_map(iso.begin(), ref_index_map))
            .vertex_invariant1(inv1)
            .vertex_invariant2(inv2)
        );
}

void is_named_vertices_isomorphic_demo() noexcept {
    const auto g = create_named_vertices_path_graph({ "Alpha", "Beta", "Gamma" });
    std::cout << "\n==== g:\n"; boost::print_graph(g, boost::get(boost::vertex_name, g));
    const auto h = create_named_vertices_path_graph({ "Alpha", "Gamma", "Beta" });
    std::cout << "\n==== h:\n"; boost::print_graph(h, boost::get(boost::vertex_name, h));

    assert(is_named_vertices_isomorphic(g, g));
    assert(!is_named_vertices_isomorphic(g, h));
}

int main() { is_named_vertices_isomorphic_demo(); }

Отпечатки:

==== g:
Alpha --> Beta 
Beta --> Gamma 
Gamma --> 

==== h:
Alpha --> Gamma 
Gamma --> Beta 
Beta --> 
person sehe    schedule 29.12.2015

Функция без класса named_vertex_invariant:

#include "named_vertex_invariant.h"

#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/graph_utility.hpp>

template <typename graph>
bool is_named_vertices_isomorphic(
  const graph &g,
  const graph &h
) noexcept {
  using vd = typename boost::graph_traits<graph>::vertex_descriptor;

  auto vertex_index_map = get(boost::vertex_index, g);
  std::vector<vd> iso(boost::num_vertices(g));

  typename named_vertex_invariant<graph>::str_to_int_map shared_names;
  named_vertex_invariant<graph> inv1{g, shared_names};
  named_vertex_invariant<graph> inv2{h, shared_names};
  inv1.collect_names();
  inv2.collect_names();

  return boost::isomorphism(g, h,
    boost::isomorphism_map(
      make_iterator_property_map(
        iso.begin(),
        vertex_index_map
      )
    )
    .vertex_invariant1(inv1)
    .vertex_invariant2(inv2)
  );
}

Класс named_vertex_invariant:

#include <map>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/isomorphism.hpp>

template <class graph>
struct named_vertex_invariant {
  using str_to_int_map = std::map<std::string, size_t>;
  using result_type = size_t;
  using argument_type
    = typename boost::graph_traits<graph>::vertex_descriptor;

  const graph& m_graph;
  str_to_int_map& m_mappings;

  size_t operator()(argument_type u) const {
      return m_mappings.at(boost::get(boost::vertex_name, m_graph, u));
  }
  size_t max() const noexcept { return m_mappings.size(); }

  void collect_names() noexcept {
    for (auto vd : boost::make_iterator_range(boost::vertices(m_graph))) {
      size_t next_id = m_mappings.size();
      auto ins = m_mappings.insert(
        { boost::get(boost::vertex_name, m_graph, vd), next_id}
      );
      if (ins.second) {
      //  std::cout << "Mapped '" << ins.first->first << "' to " << ins.first->second << "\n";
      }
    }
  }
};
person richelbilderbeek    schedule 31.12.2015