Эта проблема похожа на 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))
);
}
Оба решения терпят неудачу. Кто может мне помочь?
Похоже, вы не после изоморфизма, по крайней мере, не в строгом соответствии с определением.
Если вы действительно хотите сравнивать графы независимо от порядка индекса вершин, но строго сравнивать имена вершин, вам придется сопоставить имена с целочисленными типами (потому что 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";
}
}
}
};
#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 -->
Функция без класса 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";
}
}
}
};