Commit 1b9b952d authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Include support for vertex invariants in isomorphism()

This also properly handles self-loops and thus fixes #180.
parent 8d7af911
...@@ -25,14 +25,46 @@ using namespace boost; ...@@ -25,14 +25,46 @@ using namespace boost;
struct check_iso struct check_iso
{ {
template <class Graph1, class Graph2, class IsoMap, class VertexIndexMap>
void operator()(Graph1& g1, Graph2* g2, IsoMap map, VertexIndexMap index1, template <class Graph1, class Graph2, class IsoMap, class InvMap,
class VertexIndexMap>
void operator()(Graph1& g1, Graph2* g2, InvMap cinv_map1, InvMap cinv_map2,
int64_t max_inv, IsoMap map, VertexIndexMap index1,
VertexIndexMap index2, bool& result) const VertexIndexMap index2, bool& result) const
{ {
result = isomorphism(g1, *g2, isomorphism_map(map). auto inv_map1 = cinv_map1.get_unchecked(num_vertices(g1));
auto inv_map2 = cinv_map2.get_unchecked(num_vertices(*g2));
vinv_t<decltype(inv_map1)> vinv1(inv_map1, max_inv);
vinv_t<decltype(inv_map2)> vinv2(inv_map2, max_inv);
result = isomorphism(g1, *g2,
isomorphism_map(map.get_unchecked(num_vertices(g1))).
vertex_invariant1(vinv1).
vertex_invariant2(vinv2).
vertex_index1_map(index1). vertex_index1_map(index1).
vertex_index2_map(index2)); vertex_index2_map(index2));
} }
template <class Prop>
struct vinv_t
{
vinv_t(Prop& prop, int64_t max)
: _prop(prop), _max(max) {}
Prop& _prop;
int64_t _max;
template <class Vertex>
int64_t operator()(Vertex v) const
{
return _prop[v];
};
int64_t max() const { return _max; }
typedef int64_t result_type;
typedef size_t argument_type;
};
}; };
struct directed_graph_view_pointers: struct directed_graph_view_pointers:
...@@ -49,10 +81,22 @@ typedef property_map_types::apply<integer_types, ...@@ -49,10 +81,22 @@ typedef property_map_types::apply<integer_types,
vertex_props_t; vertex_props_t;
bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2, bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
boost::any iso_map) boost::any ainv_map1, boost::any ainv_map2,
int64_t max_inv, boost::any aiso_map)
{ {
bool result; bool result;
typedef property_map_type::apply<int32_t,
GraphInterface::vertex_index_map_t>::type
iso_map_t;
auto iso_map = any_cast<iso_map_t>(aiso_map);
typedef property_map_type::apply<int64_t,
GraphInterface::vertex_index_map_t>::type
inv_map_t;
auto inv_map1 = any_cast<inv_map_t>(ainv_map1);
auto inv_map2 = any_cast<inv_map_t>(ainv_map2);
if (gi1.GetDirected() != gi2.GetDirected()) if (gi1.GetDirected() != gi2.GetDirected())
return false; return false;
if (gi1.GetDirected()) if (gi1.GetDirected())
...@@ -60,20 +104,22 @@ bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2, ...@@ -60,20 +104,22 @@ bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
run_action<graph_tool::detail::always_directed>() run_action<graph_tool::detail::always_directed>()
(gi1, std::bind(check_iso(), (gi1, std::bind(check_iso(),
placeholders::_1, placeholders::_2, placeholders::_1, placeholders::_2,
placeholders::_3, gi1.GetVertexIndex(), inv_map1, inv_map2, max_inv, iso_map,
gi1.GetVertexIndex(),
gi2.GetVertexIndex(), std::ref(result)), gi2.GetVertexIndex(), std::ref(result)),
directed_graph_view_pointers(), vertex_props_t()) directed_graph_view_pointers())
(gi2.GetGraphView(), iso_map); (gi2.GetGraphView());
} }
else else
{ {
run_action<graph_tool::detail::never_directed>() run_action<graph_tool::detail::never_directed>()
(gi1, std::bind(check_iso(), (gi1, std::bind(check_iso(),
placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_1, placeholders::_2,
inv_map1, inv_map2, max_inv, iso_map,
gi1.GetVertexIndex(), gi1.GetVertexIndex(),
gi2.GetVertexIndex(), std::ref(result)), gi2.GetVertexIndex(), std::ref(result)),
undirected_graph_view_pointers(), vertex_props_t()) undirected_graph_view_pointers())
(gi2.GetGraphView(), iso_map); (gi2.GetGraphView());
} }
return result; return result;
......
...@@ -24,7 +24,8 @@ using namespace boost::python; ...@@ -24,7 +24,8 @@ using namespace boost::python;
using namespace graph_tool; using namespace graph_tool;
bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2, bool check_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
boost::any iso_map); boost::any ainv_map1, boost::any ainv_map2,
int64_t max_inv, boost::any aiso_map);
void get_kruskal_spanning_tree(GraphInterface& gi, boost::any weight_map, void get_kruskal_spanning_tree(GraphInterface& gi, boost::any weight_map,
boost::any tree_map); boost::any tree_map);
void get_prim_spanning_tree(GraphInterface& gi, size_t root, void get_prim_spanning_tree(GraphInterface& gi, size_t root,
......
...@@ -68,7 +68,9 @@ dl_import("from . import libgraph_tool_topology") ...@@ -68,7 +68,9 @@ dl_import("from . import libgraph_tool_topology")
from .. import _prop, Vector_int32_t, _check_prop_writable, \ from .. import _prop, Vector_int32_t, _check_prop_writable, \
_check_prop_scalar, _check_prop_vector, Graph, PropertyMap, GraphView,\ _check_prop_scalar, _check_prop_vector, Graph, PropertyMap, GraphView,\
libcore, _get_rng, _degree, perfect_prop_hash libcore, _get_rng, _degree, perfect_prop_hash
from .. stats import label_self_loops
import random, sys, numpy import random, sys, numpy
__all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph", __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"max_cardinality_matching", "max_independent_vertex_set", "max_cardinality_matching", "max_independent_vertex_set",
"min_spanning_tree", "random_spanning_tree", "dominator_tree", "min_spanning_tree", "random_spanning_tree", "dominator_tree",
...@@ -156,11 +158,33 @@ def similarity(g1, g2, label1=None, label2=None, norm=True): ...@@ -156,11 +158,33 @@ def similarity(g1, g2, label1=None, label2=None, norm=True):
return s return s
def isomorphism(g1, g2, isomap=False): def isomorphism(g1, g2, vertex_inv1=None, vertex_inv2=None, isomap=False):
r"""Check whether two graphs are isomorphic. r"""Check whether two graphs are isomorphic.
If `isomap` is True, a vertex :class:`~graph_tool.PropertyMap` with the Parameters
isomorphism mapping is returned as well. ----------
g1 : :class:`~graph_tool.Graph`
First graph.
g2 : :class:`~graph_tool.Graph`
Second graph.
vertex_inv1 : :class:`~graph_tool.PropertyMap` (optional, default: `None`)
Vertex invariant of the first graph. Only vertices with with the same
invariants are considered in the isomorphism.
vertex_inv2 : :class:`~graph_tool.PropertyMap` (optional, default: `None`)
Vertex invariant of the second graph. Only vertices with with the same
invariants are considered in the isomorphism.
isomap : ``bool`` (optional, default: ``False``)
If ``True``, a vertex :class:`~graph_tool.PropertyMap` with the
isomorphism mapping is returned as well.
Returns
-------
is_isomorphism : ``bool``
``True`` if both graphs are isomorphic, otherwise ``False``.
isomap : :class:`~graph_tool.PropertyMap`
Isomorphism mapping corresponding to a property map belonging to the
first graph which maps its vertices to their corresponding vertices of
the second graph.
Examples Examples
-------- --------
...@@ -182,8 +206,34 @@ def isomorphism(g1, g2, isomap=False): ...@@ -182,8 +206,34 @@ def isomorphism(g1, g2, isomap=False):
""" """
imap = g1.new_vertex_property("int32_t") imap = g1.new_vertex_property("int32_t")
if vertex_inv1 is None:
vertex_inv1 = g1.degree_property_map("total").copy("int64_t")
else:
vertex_inv1 = vertex_inv1.copy("int64_t")
d = g1.degree_property_map("total")
vertex_inv1.fa += (vertex_inv1.fa.max() + 1) * d.a
if vertex_inv2 is None:
vertex_inv2 = g2.degree_property_map("total").copy("int64_t")
else:
vertex_inv2 = vertex_inv2.copy("int64_t")
d = g2.degree_property_map("total")
vertex_inv2.fa += (vertex_inv2.fa.max() + 1) * d.a
inv_max = max(vertex_inv1.fa.max(),vertex_inv2.fa.max()) + 1
l1 = label_self_loops(g1, mark_only=True)
if l1.fa.max() > 0:
g1 = GraphView(g1, efilt=1 - l1.fa)
l2 = label_self_loops(g2, mark_only=True)
if l2.fa.max() > 0:
g2 = GraphView(g2, efilt=1 - l2.fa)
iso = libgraph_tool_topology.\ iso = libgraph_tool_topology.\
check_isomorphism(g1._Graph__graph, g2._Graph__graph, check_isomorphism(g1._Graph__graph, g2._Graph__graph,
_prop("v", g1, vertex_inv1),
_prop("v", g2, vertex_inv2),
inv_max,
_prop("v", g1, imap)) _prop("v", g1, imap))
if isomap: if isomap:
return iso, imap return iso, imap
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment