简体   繁体   English

Boost.Graph库:如何在命名顶点上使用boost :: is_isomorphism

[英]Boost.Graph library: how to use boost::is_isomorphism with named vertices

This Issue is similar to BGL: Example of isomorphism with vertex invariants 此问题与BGL相似:顶点不变的同构示例

I am working on a Boost.Graph tutorial and calling boost::is_isomorphism on two graphs without properties is easy. 我正在研究Boost.Graph教程,并且在没有属性的两个图上调用boost :: is_isomorphism很容易。 But I cannot get it working when the vertices now have names. 但是当顶点具有名称时,我无法使其正常工作。

This code shows: 此代码显示:

  • How I create path graphs with named vertices (unimportant) 如何使用命名顶点创建路径图(不重要)
  • My test code 我的测试代码
  • My function to test for isomorphism with named vertice 我的功能是使用命名的顶点测试同构

Here is how I create path graphs with named vertices (which is rather unimportant, but shown for completion): 这是我如何创建具有命名顶点的路径图(这虽然不重要,但显示为完整):

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;
}

Here is my test: 这是我的测试:

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));
}

I would like to write the function is_named_vertices_isomorphic more or less as such (note: this will compile, but fail the test, as inspired by BGL: Example of isomorphism with vertex invariants ): 我想或多或少这样编写函数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])
    )
  );
}

Looking at the question BGL: Example of isomorphism with vertex invariants made me come up with this: 看问题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))
  );
}

Both solutions fail. 两种解决方案均失败。 Who can help me out? 谁能帮助我?

It does seem you're not after iso-morphism, at least not strictly according to the definition. 看来您不是不是在追求同构,至少不是严格按照定义。

If you actually wished to compare graphs regardless of the Vertex Index ordering, but strictly comparing vertex names, you will have to map the names onto integral types (because the max_vertex_invariant value is expected to be unsigned integral). 如果您实际上希望比较图而不管顶点索引的顺序如何,但要严格比较顶点名称,则必须将名称映射到整数类型(因为max_vertex_invariant值应为无符号整数)。

Here's a simplistic way to achieve it: 这是一种简单的实现方法:

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)
        );
}

I've not made things generic yet, because it distracts. 我还没有使事情变得通用,因为它分散了人们的注意力。

For good form, you'd need to use boost::property_maps::property_traits<boost::property_map<graph1, boost::vertex_name_t>::const_type>::value_type or similar, and check that the resultant type is actually compatible for comparison between graph1 and graph1 etc. 为了获得良好的格式,您需要使用boost::property_maps::property_traits<boost::property_map<graph1, boost::vertex_name_t>::const_type>::value_type或类似的boost::property_maps::property_traits<boost::property_map<graph1, boost::vertex_name_t>::const_type>::value_type ,并检查生成的类型是否 graph1graph1等之间的比较

To be "actually" generic you'd want to pass in a deduced property map instead of boost::get(boost::vertex_name, g) and so on. 要成为“实际上”的泛型,您需要传递一个推导的属性映射,而不是boost::get(boost::vertex_name, g)等。

To be honest, I feel that this doesn't make much sense for such semantic-laden things as the iso-morphism invariants. 老实说,我觉得这对于像等态不变式这样充满语义的事物没有多大意义。 You can obviously make things as generic as you need them to be for your application. 显然,您可以根据需要将它们做成通用的。

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";
            }
        }
    }
};

Where the helper VertexInvariant is defined as: 助手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";
            }
        }
    }
};

Full Demo 完整演示

Live On Coliru 生活在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(); }

Prints: 打印:

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

==== h:
Alpha --> Gamma 
Gamma --> Beta 
Beta --> 

The function, without the named_vertex_invariant class: 没有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)
  );
}

The named_vertex_invariant class: 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";
      }
    }
  }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM