mirror of
https://github.com/boostorg/graph.git
synced 2026-01-19 04:12:11 +00:00
Invariant contiguous range requirement removal
Invariant contiguous range requirement removal - Vertex invariants for use in isomorphism algorithm must no longer have low upper bounds due to a hidden allocation linear in the maximum encountered vertex invariant. - Vertex invariants must no longer be convertible to `size_t`, but can be any comparable and hashable types - Build `unordered_map`-backed invariant multiplicity map efficiently from sorted vertex invariants
This commit is contained in:
@@ -114,9 +114,8 @@ equal) or to impose extra conditions on the result. The
|
||||
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.
|
||||
|
||||
@@ -30,6 +30,22 @@ namespace boost
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct HashableConcept {
|
||||
BOOST_CONCEPT_USAGE(HashableConcept)
|
||||
{
|
||||
hash<T> hasher;
|
||||
typedef typename hash<T>::result_type hash_result;
|
||||
hash_result val = hasher(t);
|
||||
boost::ignore_unused_variable_warning(val);
|
||||
}
|
||||
|
||||
T t;
|
||||
};
|
||||
|
||||
template < typename Invariant >
|
||||
struct InvariantConcept : LessThanComparable<Invariant>, EqualityComparable<Invariant>, HashableConcept<Invariant> {};
|
||||
|
||||
template < typename Graph1, typename Graph2, typename IsoMapping,
|
||||
typename Invariant1, typename Invariant2, typename IndexMap1,
|
||||
typename IndexMap2 >
|
||||
@@ -39,8 +55,8 @@ namespace detail
|
||||
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;
|
||||
typedef unordered_map< invariant_t, size_type > multiplicity_map;
|
||||
|
||||
const Graph1& G1;
|
||||
const Graph2& G2;
|
||||
@@ -81,17 +97,17 @@ namespace detail
|
||||
friend struct compare_multiplicity;
|
||||
struct compare_multiplicity
|
||||
{
|
||||
compare_multiplicity(Invariant1 invariant1, size_type* multiplicity)
|
||||
compare_multiplicity(Invariant1 invariant1, const multiplicity_map& multiplicity)
|
||||
: invariant1(invariant1), multiplicity(multiplicity)
|
||||
{
|
||||
}
|
||||
bool operator()(const vertex1_t& x, const vertex1_t& y) const
|
||||
{
|
||||
return multiplicity[invariant1(x)]
|
||||
< multiplicity[invariant1(y)];
|
||||
return multiplicity.at(invariant1(x))
|
||||
< multiplicity.at(invariant1(y));
|
||||
}
|
||||
Invariant1 invariant1;
|
||||
size_type* multiplicity;
|
||||
const multiplicity_map& multiplicity;
|
||||
};
|
||||
|
||||
struct record_dfs_order : default_dfs_visitor
|
||||
@@ -158,46 +174,65 @@ namespace detail
|
||||
);
|
||||
}
|
||||
|
||||
// Generates map of invariant multiplicity from sorted invariants
|
||||
multiplicity_map multiplicities(const std::vector< invariant_t >& invariants) {
|
||||
// Assumes invariants are sorted
|
||||
multiplicity_map invar_multiplicity;
|
||||
|
||||
typedef typename std::vector< invariant_t >::const_iterator invar_iter;
|
||||
typedef typename multiplicity_map::iterator invar_map_iter;
|
||||
invar_iter it = invariants.begin();
|
||||
const invar_iter end = invariants.end();
|
||||
|
||||
if(it == end) {
|
||||
return invar_multiplicity;
|
||||
}
|
||||
|
||||
invariant_t invar = *it;
|
||||
invar_map_iter inserted = invar_multiplicity.emplace(invar, 1).first;
|
||||
++it;
|
||||
for(; it != end; ++it)
|
||||
{
|
||||
if(*it == invar)
|
||||
{
|
||||
inserted->second += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
invar = *it;
|
||||
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);
|
||||
// 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< 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;
|
||||
}
|
||||
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;
|
||||
|
||||
// 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)));
|
||||
|
||||
std::vector< default_color_type > color_vec(num_vertices(G1));
|
||||
safe_iterator_property_map<
|
||||
@@ -437,7 +472,7 @@ namespace detail
|
||||
}
|
||||
|
||||
if(!unmatched_g1_vertices.empty()) {
|
||||
typedef unordered_multimap< invar2_value, vertex2_t > g2_invariant_vertex_multimap;
|
||||
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)
|
||||
@@ -451,7 +486,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;
|
||||
@@ -546,11 +581,17 @@ 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);
|
||||
BOOST_CONCEPT_ASSERT((detail::InvariantConcept<invariant1_t>));
|
||||
|
||||
// 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(
|
||||
|
||||
Reference in New Issue
Block a user