Commit 4f9e4785 authored by Tiago Peixoto's avatar Tiago Peixoto

similarity(): Implement 'asymmetric' parameter

parent f466b829
......@@ -47,7 +47,7 @@ typedef boost::mpl::push_back<edge_scalar_properties, ecmap_t>::type
python::object similarity(GraphInterface& gi1, GraphInterface& gi2,
boost::any weight1, boost::any weight2,
boost::any label1, boost::any label2)
boost::any label1, boost::any label2, bool asym)
{
if (weight1.empty())
weight1 = ecmap_t();
......@@ -59,7 +59,7 @@ python::object similarity(GraphInterface& gi1, GraphInterface& gi2,
{
auto l2 = uncheck(l1, label2);
auto ew2 = uncheck(ew1, weight2);
auto ret = get_similarity(g1, g2, ew1, ew2, l1, l2);
auto ret = get_similarity(g1, g2, ew1, ew2, l1, l2, asym);
s = python::object(ret);
},
all_graph_views(),
......@@ -72,7 +72,7 @@ python::object similarity(GraphInterface& gi1, GraphInterface& gi2,
python::object similarity_fast(GraphInterface& gi1, GraphInterface& gi2,
boost::any weight1, boost::any weight2,
boost::any label1, boost::any label2);
boost::any label1, boost::any label2, bool asym);
void export_similarity()
{
......
......@@ -25,15 +25,28 @@ namespace graph_tool
using namespace std;
using namespace boost;
template <class Map, class K>
typename Map::value_type::second_type get_map(Map& m, K&& k)
{
auto iter = m.find(k);
if (iter == m.end())
return typename Map::value_type::second_type(0);
return iter->second;
}
template <class Keys, class Set1, class Set2>
auto set_difference(Keys& ks, Set1& s1, Set2& s2)
auto set_difference(Keys& ks, Set1& s1, Set2& s2, bool asym)
{
typename Set1::value_type::second_type s = 0;
for (auto& k : ks)
{
auto x1 = s1[k];
auto x2 = s2[k];
s += std::max(x1, x2) - std::min(x1, x2);
auto x1 = get_map(s1, k);
auto x2 = get_map(s2, k);
if (x1 > x2)
s += x1 - x2;
else if (!asym)
s += x2 - x1;
}
return s;
}
......@@ -41,7 +54,7 @@ auto set_difference(Keys& ks, Set1& s1, Set2& s2)
template <class Vertex, class WeightMap, class LabelMap, class Graph1, class Graph2>
auto vertex_difference(Vertex v1, Vertex v2, WeightMap& ew1, WeightMap& ew2,
LabelMap& l1, LabelMap& l2, const Graph1& g1,
const Graph2& g2)
const Graph2& g2, bool asym)
{
typedef typename property_traits<LabelMap>::value_type label_t;
typedef typename property_traits<WeightMap>::value_type val_t;
......@@ -72,12 +85,12 @@ auto vertex_difference(Vertex v1, Vertex v2, WeightMap& ew1, WeightMap& ew2,
}
}
return set_difference(keys, adj1, adj2);
return set_difference(keys, adj1, adj2, asym);
}
template <class Graph1, class Graph2, class WeightMap, class LabelMap>
auto get_similarity(const Graph1& g1, const Graph2& g2, WeightMap ew1,
WeightMap ew2, LabelMap l1, LabelMap l2)
WeightMap ew2, LabelMap l1, LabelMap l2, bool asym)
{
typedef typename property_traits<LabelMap>::value_type label_t;
typedef typename property_traits<WeightMap>::value_type val_t;
......@@ -103,28 +116,31 @@ auto get_similarity(const Graph1& g1, const Graph2& g2, WeightMap ew1,
else
v2 = li2->second;
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2);
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2, asym);
}
for (auto& lv2 : lmap2)
if (!asym)
{
vertex_t v2 = lv2.second;
vertex_t v1;
for (auto& lv2 : lmap2)
{
vertex_t v2 = lv2.second;
vertex_t v1;
auto li1 = lmap1.find(lv2.first);
if (li1 == lmap1.end())
v1 = graph_traits<Graph2>::null_vertex();
else
continue;
auto li1 = lmap1.find(lv2.first);
if (li1 == lmap1.end())
v1 = graph_traits<Graph2>::null_vertex();
else
continue;
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2);
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2, false);
}
}
return s;
}
template <class Graph1, class Graph2, class WeightMap, class LabelMap>
auto get_similarity_fast(const Graph1& g1, const Graph2& g2, WeightMap ew1,
WeightMap ew2, LabelMap l1, LabelMap l2)
WeightMap ew2, LabelMap l1, LabelMap l2, bool asym)
{
typedef typename property_traits<WeightMap>::value_type val_t;
typedef typename graph_traits<Graph1>::vertex_descriptor vertex_t;
......@@ -160,23 +176,26 @@ auto get_similarity_fast(const Graph1& g1, const Graph2& g2, WeightMap ew1,
{
auto v2 = lmap2[i];
if (v1 == graph_traits<Graph1>::null_vertex() &&
v2 == graph_traits<Graph1>::null_vertex())
v2 == graph_traits<Graph2>::null_vertex())
return;
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2);
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2, asym);
});
#pragma omp parallel if (num_vertices(g2) > OPENMP_MIN_THRESH) \
reduction(+:s)
parallel_loop_no_spawn
(lmap2,
[&](size_t i, auto v2)
{
auto v1 = lmap1[i];
if (v1 != graph_traits<Graph1>::null_vertex() ||
v2 == graph_traits<Graph1>::null_vertex())
return;
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2);
});
if (!asym)
{
#pragma omp parallel if (num_vertices(g2) > OPENMP_MIN_THRESH) \
reduction(+:s)
parallel_loop_no_spawn
(lmap2,
[&](size_t i, auto v2)
{
auto v1 = lmap1[i];
if (v1 != graph_traits<Graph1>::null_vertex() ||
v2 == graph_traits<Graph2>::null_vertex())
return;
s += vertex_difference(v1, v2, ew1, ew2, l1, l2, g1, g2, false);
});
}
return s;
}
......
......@@ -47,7 +47,7 @@ typedef boost::mpl::push_back<edge_scalar_properties, ecmap_t>::type
python::object similarity_fast(GraphInterface& gi1, GraphInterface& gi2,
boost::any weight1, boost::any weight2,
boost::any label1, boost::any label2)
boost::any label1, boost::any label2, bool asym)
{
if (weight1.empty())
weight1 = ecmap_t();
......@@ -59,7 +59,7 @@ python::object similarity_fast(GraphInterface& gi1, GraphInterface& gi2,
{
auto l2 = uncheck(l1, label2);
auto ew2 = uncheck(ew1, weight2);
auto ret = get_similarity_fast(g1, g2, ew1, ew2, l1, l2);
auto ret = get_similarity_fast(g1, g2, ew1, ew2, l1, l2, asym);
s = python::object(ret);
},
all_graph_views(),
......
......@@ -92,7 +92,7 @@ __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"edge_reciprocity"]
def similarity(g1, g2, eweight1=None, eweight2=None, label1=None, label2=None,
norm=True, distance=False):
norm=True, distance=False, asymmetric=False):
r"""Return the adjacency similarity between the two graphs.
Parameters
......@@ -117,6 +117,9 @@ def similarity(g1, g2, eweight1=None, eweight2=None, label1=None, label2=None,
distance : bool (optional, default: ``False``)
If ``True``, the complementary value is returned, i.e. the distance
between the two graphs.
asymmetric : bool (optional, default: ``False``)
If ``True``, the asymmetric similarity of ``g1`` to ``g2`` will be
computed.
Returns
-------
......@@ -150,6 +153,16 @@ def similarity(g1, g2, eweight1=None, eweight2=None, label1=None, label2=None,
If ``norm == True`` the value returned is
:math:`S(\boldsymbol A_1, \boldsymbol A_2) / E`.
If ``asymmetric == True``, the above is changed so that the comparison is
made only for entries in :math:`\boldsymbol A_1` that are larger than in :math:`\boldsymbol A_2`, i.e.
.. math::
d(\boldsymbol A_1, \boldsymbol A_2) = \sum_{i<j} (A_{ij}^{(1)} - A_{ij}^{(2)}) H((A_{ij}^{(1)} - A_{ij}^{(2)})),
where :math:`H(x)` is the unit step function, and the total sum is changed
accordingly to :math:`E=\sum_{i<j}|A_{ij}^{(1)}|`.
The algorithm runs with complexity :math:`O(E_1 + V_1 + E_2 + V_2)`.
If enabled during compilation, and the vertex labels are integers, this
......@@ -213,18 +226,24 @@ def similarity(g1, g2, eweight1=None, eweight2=None, label1=None, label2=None,
s = libgraph_tool_topology.\
similarity(g1._Graph__graph, g2._Graph__graph,
ew1, ew2, _prop("v", g1, label1),
_prop("v", g2, label2))
_prop("v", g2, label2), asymmetric)
else:
s = libgraph_tool_topology.\
similarity_fast(g1._Graph__graph, g2._Graph__graph,
ew1, ew2, _prop("v", g1, label1),
_prop("v", g2, label2))
_prop("v", g2, label2), asymmetric)
if not g1.is_directed() or not g2.is_directed():
s //= 2
if eweight1 is None and eweight1 is None:
E = g1.num_edges() + g2.num_edges()
if asymmetric:
E = g1.num_edges()
else:
E = g1.num_edges() + g2.num_edges()
else:
E = float(abs(eweight1.fa).sum() + abs(eweight2.fa).sum())
if asymmetric:
E = float(abs(eweight1.fa).sum())
else:
E = float(abs(eweight1.fa).sum() + abs(eweight2.fa).sum())
if not distance:
s = E - s
if norm:
......
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