Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Isomorphism: Remove invariant contiguous range requirement #349

Merged
5 changes: 2 additions & 3 deletions doc/isomorphism.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,8 @@ <h3>Named Parameters</h3>
href="http://www.boost.org/sgi/stl/AdaptableUnaryFunction.html">AdaptableUnaryFunction</a>,
with the argument type of <tt>vertex_invariant1</tt> being <tt>Graph1</tt>'s vertex
descriptor type, the argument type of <tt>vertex_invariant2</tt> being
<tt>Graph2</tt>'s vertex descriptor type, and both functions having integral
result types. The values returned by these two functions must have a low upper
bound, as memory linear in the maximal invariant value is allocated.
<tt>Graph2</tt>'s vertex descriptor type, and both functions sharing a
result type that is totally ordered and hashable, such as an integer.
<br>
<b>Default:</b> <tt>degree_vertex_invariant</tt> for both arguments<br>
<b>Python</b>: Unsupported parameter.
Expand Down
117 changes: 72 additions & 45 deletions include/boost/graph/isomorphism.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/graph/depth_first_search.hpp>
#include <boost/detail/algorithm.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/pending/indirect_cmp.hpp> // for make_indirect_pmap
#include <boost/concept/assert.hpp>

Expand All @@ -32,15 +33,14 @@ namespace detail

template < typename Graph1, typename Graph2, typename IsoMapping,
typename Invariant1, typename Invariant2, typename IndexMap1,
typename IndexMap2 >
typename IndexMap2, typename InvariantCountMap = boost::unordered_flat_map<typename Invariant1::result_type, typename graph_traits< Graph1 >::vertices_size_type > >
class isomorphism_algo
{
typedef typename graph_traits< Graph1 >::vertex_descriptor vertex1_t;
typedef typename graph_traits< Graph2 >::vertex_descriptor vertex2_t;
typedef typename graph_traits< Graph1 >::edge_descriptor edge1_t;
typedef typename graph_traits< Graph1 >::vertices_size_type size_type;
typedef typename Invariant1::result_type invar1_value;
typedef typename Invariant2::result_type invar2_value;
typedef typename Invariant1::result_type invariant_t;

const Graph1& G1;
const Graph2& G2;
Expand Down Expand Up @@ -81,17 +81,20 @@ namespace detail
friend struct compare_multiplicity;
struct compare_multiplicity
{
compare_multiplicity(Invariant1 invariant1, size_type* multiplicity)
: invariant1(invariant1), multiplicity(multiplicity)
compare_multiplicity(Invariant1 invariant1, const InvariantCountMap& multiplicity)
: invariant1(invariant1), multiplicity(&multiplicity)
{
}
bool operator()(const vertex1_t& x, const vertex1_t& y) const
{
return multiplicity[invariant1(x)]
< multiplicity[invariant1(y)];
auto x_multiplicity_iter = multiplicity->find(invariant1(x));
assert(x_multiplicity_iter != multiplicity->end());
auto y_multiplicity_iter = multiplicity->find(invariant1(y));
assert(y_multiplicity_iter != multiplicity->end());
return *x_multiplicity_iter < *y_multiplicity_iter;
jeremy-murphy marked this conversation as resolved.
Show resolved Hide resolved
}
Invariant1 invariant1;
size_type* multiplicity;
const InvariantCountMap* multiplicity;
};

struct record_dfs_order : default_dfs_visitor
Expand Down Expand Up @@ -158,46 +161,63 @@ namespace detail
);
}

// Generates map of invariant multiplicity from sorted invariants
template<typename ForwardIterator>
InvariantCountMap multiplicities(ForwardIterator first, const ForwardIterator last)
{
typedef typename InvariantCountMap::iterator invar_map_iter;

assert(std::is_sorted(first, last));
InvariantCountMap invar_multiplicity;

if(first == last)
return invar_multiplicity;

invariant_t invar = *first;
invar_map_iter inserted = invar_multiplicity.emplace(invar, 1).first;
++first;
for(; first != last; ++first)
{
if(*first == invar)
{
inserted->second += 1;
}
else
{
invar = *first;
inserted = invar_multiplicity.emplace(invar, 1).first;
}
}

return invar_multiplicity;
}

bool test_isomorphism()
{
// reset isomapping
BGL_FORALL_VERTICES_T(v, G1, Graph1)
f[v] = graph_traits< Graph2 >::null_vertex();

std::size_t max_invariant = 0;
{
std::vector< invar1_value > invar1_array;
invar1_array.reserve(num_vertices(G1));
BGL_FORALL_VERTICES_T(v, G1, Graph1)
invar1_array.push_back(invariant1(v));
sort(invar1_array);

std::vector< invar2_value > invar2_array;
invar2_array.reserve(num_vertices(G2));
BGL_FORALL_VERTICES_T(v, G2, Graph2)
invar2_array.push_back(invariant2(v));
sort(invar2_array);
if (!equal(invar1_array, invar2_array))
return false;

// Empty graphs case is covered before test_isomorphism is
// called, so the invar?_arrays cannot be empty, so back() is
// safe. Also the two invariant arrays are equal:
max_invariant = invar1_array.back();
assert(max_invariant != std::numeric_limits<std::size_t>::max());
max_invariant += 1;
}

// Calculate all invariants of G1 and G2, sort and compare
std::vector< invariant_t > invar1_array;
invar1_array.reserve(num_vertices(G1));
BGL_FORALL_VERTICES_T(v, G1, Graph1)
invar1_array.push_back(invariant1(v));
sort(invar1_array);

std::vector< invariant_t > invar2_array;
invar2_array.reserve(num_vertices(G2));
BGL_FORALL_VERTICES_T(v, G2, Graph2)
invar2_array.push_back(invariant2(v));
sort(invar2_array);
if (!equal(invar1_array, invar2_array))
return false;
jeremy-murphy marked this conversation as resolved.
Show resolved Hide resolved

// Sort vertices by the multiplicity of their invariants
std::vector< vertex1_t > V_mult;
BGL_FORALL_VERTICES_T(v, G1, Graph1)
V_mult.push_back(v);
{
std::vector< size_type > multiplicity(max_invariant, 0);
BGL_FORALL_VERTICES_T(v, G1, Graph1)
++multiplicity.at(invariant1(v));
sort(
V_mult, compare_multiplicity(invariant1, &multiplicity[0]));
}
sort(V_mult, compare_multiplicity(invariant1, multiplicities(invar1_array.begin(), invar1_array.end())));
jeremy-murphy marked this conversation as resolved.
Show resolved Hide resolved

std::vector< default_color_type > color_vec(num_vertices(G1));
safe_iterator_property_map<
Expand Down Expand Up @@ -436,13 +456,15 @@ namespace detail
}
}

if(!unmatched_g1_vertices.empty()) {
typedef unordered_multimap< invar2_value, vertex2_t > g2_invariant_vertex_multimap;
if(!unmatched_g1_vertices.empty())
{
typedef unordered_multimap< invariant_t, vertex2_t > g2_invariant_vertex_multimap;
typedef typename g2_invariant_vertex_multimap::iterator multimap_iter;
g2_invariant_vertex_multimap unmatched_invariants;
BGL_FORALL_VERTICES_T(v, G2, Graph2)
{
if(!in_S[v]) {
if(!in_S[v])
{
unmatched_invariants.emplace(invariant2(v), v);
}
}
Expand All @@ -451,7 +473,7 @@ namespace detail
const v1_iter end = unmatched_g1_vertices.end();
for(v1_iter iter = unmatched_g1_vertices.begin(); iter != end; ++iter)
{
invar1_value unmatched_g1_vertex_invariant = invariant1(*iter);
invariant_t unmatched_g1_vertex_invariant = invariant1(*iter);
multimap_iter matching_invariant = unmatched_invariants.find(unmatched_g1_vertex_invariant);
BOOST_ASSERT(matching_invariant != unmatched_invariants.end());
f[*iter] = matching_invariant->second;
Expand Down Expand Up @@ -546,11 +568,16 @@ bool isomorphism(const Graph1& G1, const Graph2& G2, IsoMapping f,
typedef typename graph_traits< Graph2 >::vertex_descriptor vertex2_t;
typedef typename graph_traits< Graph1 >::vertices_size_type size_type;

typedef typename Invariant1::result_type invariant1_t;
typedef typename Invariant2::result_type invariant2_t;

BOOST_STATIC_ASSERT(is_same<invariant1_t, invariant2_t>::value);

// Vertex invariant requirement
BOOST_CONCEPT_ASSERT(
(AdaptableUnaryFunctionConcept< Invariant1, size_type, vertex1_t >));
(AdaptableUnaryFunctionConcept< Invariant1, invariant1_t, vertex1_t >));
BOOST_CONCEPT_ASSERT(
(AdaptableUnaryFunctionConcept< Invariant2, size_type, vertex2_t >));
(AdaptableUnaryFunctionConcept< Invariant2, invariant2_t, vertex2_t >));

// Property map requirements
BOOST_CONCEPT_ASSERT(
Expand Down
Loading