are-isomorphic: small simplifications
* src/tgbaalgos/are_isomorphic.cc (is_isomorphism): Do not copy trans_storage_t elements as they contain more than what we use. Remap the states of a1 before the sort, not during the sort. (are_trivially_different): Also catch the case where the number of acceptance sets are different. * src/tgbaalgos/are_isomorphic.hh: Improve doxygen.
This commit is contained in:
parent
ff4dca48a5
commit
be57ec290a
2 changed files with 60 additions and 63 deletions
|
|
@ -33,10 +33,28 @@ namespace
|
||||||
typedef std::vector<unsigned> states_t;
|
typedef std::vector<unsigned> states_t;
|
||||||
typedef std::unordered_map<class_t, states_t> class2states_t;
|
typedef std::unordered_map<class_t, states_t> class2states_t;
|
||||||
typedef std::vector<class_t> state2class_t;
|
typedef std::vector<class_t> state2class_t;
|
||||||
typedef spot::tgba_digraph::graph_t::trans_storage_t trans_storage_t;
|
|
||||||
|
|
||||||
bool
|
struct transition
|
||||||
trans_lessthan(trans_storage_t ts1, trans_storage_t ts2)
|
{
|
||||||
|
unsigned src;
|
||||||
|
unsigned dst;
|
||||||
|
int cond;
|
||||||
|
spot::acc_cond::mark_t acc;
|
||||||
|
|
||||||
|
|
||||||
|
transition(unsigned src, unsigned dst, int cond, spot::acc_cond::mark_t acc)
|
||||||
|
: src(src), dst(dst), cond(cond), acc(acc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const transition& o) const
|
||||||
|
{
|
||||||
|
return src == o.src && dst == o.dst && cond == o.cond && acc == o.acc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
trans_lessthan(const transition& ts1, const transition& ts2)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
ts1.src != ts2.src ?
|
ts1.src != ts2.src ?
|
||||||
|
|
@ -45,27 +63,10 @@ namespace
|
||||||
ts1.dst < ts2.dst :
|
ts1.dst < ts2.dst :
|
||||||
ts1.acc != ts2.acc ?
|
ts1.acc != ts2.acc ?
|
||||||
ts1.acc < ts2.acc :
|
ts1.acc < ts2.acc :
|
||||||
ts1.cond.id() < ts2.cond.id();
|
ts1.cond < ts2.cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<bool(trans_storage_t ts1, trans_storage_t ts2)>
|
static state2class_t
|
||||||
trans_lessthan_mapped(const std::vector<unsigned>& mapping)
|
|
||||||
{
|
|
||||||
return [mapping](trans_storage_t ts1,
|
|
||||||
trans_storage_t ts2)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
mapping[ts1.src] != mapping[ts2.src] ?
|
|
||||||
mapping[ts1.src] < mapping[ts2.src] :
|
|
||||||
mapping[ts1.dst] != mapping[ts2.dst] ?
|
|
||||||
mapping[ts1.dst] < mapping[ts2.dst] :
|
|
||||||
ts1.acc != ts2.acc ?
|
|
||||||
ts1.acc < ts2.acc :
|
|
||||||
ts1.cond.id() < ts2.cond.id();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
state2class_t
|
|
||||||
map_state_class(const spot::const_tgba_digraph_ptr a)
|
map_state_class(const spot::const_tgba_digraph_ptr a)
|
||||||
{
|
{
|
||||||
state2class_t hashin(a->num_states(), 0);
|
state2class_t hashin(a->num_states(), 0);
|
||||||
|
|
@ -92,7 +93,7 @@ namespace
|
||||||
return state2class;
|
return state2class;
|
||||||
}
|
}
|
||||||
|
|
||||||
class2states_t
|
static class2states_t
|
||||||
map_class_states(const spot::const_tgba_digraph_ptr a)
|
map_class_states(const spot::const_tgba_digraph_ptr a)
|
||||||
{
|
{
|
||||||
unsigned n = a->num_states();
|
unsigned n = a->num_states();
|
||||||
|
|
@ -111,7 +112,7 @@ namespace
|
||||||
return class2states;
|
return class2states;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
mapping_from_classes(std::vector<unsigned>& mapping,
|
mapping_from_classes(std::vector<unsigned>& mapping,
|
||||||
class2states_t classes1,
|
class2states_t classes1,
|
||||||
class2states_t classes2)
|
class2states_t classes2)
|
||||||
|
|
@ -120,15 +121,17 @@ namespace
|
||||||
return false;
|
return false;
|
||||||
for (auto& p : classes1)
|
for (auto& p : classes1)
|
||||||
{
|
{
|
||||||
if (p.second.size() != classes2[p.first].size())
|
auto& c2 = classes2[p.first];
|
||||||
|
if (p.second.size() != c2.size())
|
||||||
return false;
|
return false;
|
||||||
for (unsigned j = 0; j < p.second.size(); ++j)
|
auto ps = p.second.size();
|
||||||
mapping[p.second[j]] = classes2[p.first][j];
|
for (unsigned j = 0; j < ps; ++j)
|
||||||
|
mapping[p.second[j]] = c2[j];
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
next_class_permutation(class2states_t& classes)
|
next_class_permutation(class2states_t& classes)
|
||||||
{
|
{
|
||||||
for (auto& p : classes)
|
for (auto& p : classes)
|
||||||
|
|
@ -137,50 +140,40 @@ namespace
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
is_isomorphism(const std::vector<unsigned>& mapping,
|
is_isomorphism(const std::vector<unsigned>& mapping,
|
||||||
const spot::const_tgba_digraph_ptr a1,
|
const spot::const_tgba_digraph_ptr a1,
|
||||||
const spot::const_tgba_digraph_ptr a2)
|
const spot::const_tgba_digraph_ptr a2)
|
||||||
{
|
{
|
||||||
unsigned n = a1->num_states();
|
unsigned n = a1->num_states();
|
||||||
assert(n == a2->num_states());
|
assert(n == a2->num_states());
|
||||||
std::vector<trans_storage_t> trans1;
|
unsigned tend = a1->num_transitions();
|
||||||
std::vector<trans_storage_t> trans2;
|
assert(tend == a2->num_transitions());
|
||||||
|
|
||||||
|
std::vector<transition> trans1;
|
||||||
|
trans1.reserve(tend);
|
||||||
|
std::vector<transition> trans2;
|
||||||
|
trans2.reserve(tend);
|
||||||
|
|
||||||
for (auto& t: a1->transitions())
|
for (auto& t: a1->transitions())
|
||||||
trans1.push_back(t);
|
trans1.emplace_back(mapping[t.src], mapping[t.dst], t.acc, t.cond.id());
|
||||||
|
|
||||||
for (auto& t: a2->transitions())
|
for (auto& t: a2->transitions())
|
||||||
trans2.push_back(t);
|
trans2.emplace_back(t.src, t.dst, t.acc, t.cond.id());
|
||||||
|
|
||||||
// Sort the vectors of transitions so that they can be compared.
|
// Sort the vectors of transitions so that they can be compared.
|
||||||
// To use the same metric, the transitions of a1 have to be mapped to
|
std::sort(trans1.begin(), trans1.end(), trans_lessthan);
|
||||||
// a2.
|
|
||||||
std::sort(trans1.begin(), trans1.end(), trans_lessthan_mapped(mapping));
|
|
||||||
std::sort(trans2.begin(), trans2.end(), trans_lessthan);
|
std::sort(trans2.begin(), trans2.end(), trans_lessthan);
|
||||||
|
return trans1 == trans2;
|
||||||
for (unsigned i = 0; i < trans1.size(); ++i)
|
|
||||||
{
|
|
||||||
auto ts1 = trans1[i];
|
|
||||||
auto ts2 = trans2[i];
|
|
||||||
if (!(ts2.src == mapping[ts1.src] &&
|
|
||||||
ts2.dst == mapping[ts1.dst] &&
|
|
||||||
ts1.acc == ts2.acc &&
|
|
||||||
ts1.cond.id() == ts2.cond.id()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
are_trivially_different(const spot::const_tgba_digraph_ptr a1,
|
are_trivially_different(const spot::const_tgba_digraph_ptr a1,
|
||||||
const spot::const_tgba_digraph_ptr a2)
|
const spot::const_tgba_digraph_ptr a2)
|
||||||
{
|
{
|
||||||
return (a1->num_states() != a2->num_states() ||
|
return (a1->num_states() != a2->num_states()
|
||||||
a1->num_transitions() != a2->num_transitions());
|
|| a1->num_transitions() != a2->num_transitions()
|
||||||
|
|| a1->acc().num_sets() != a2->acc().num_sets());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,7 +184,7 @@ namespace spot
|
||||||
const const_tgba_digraph_ptr a2)
|
const const_tgba_digraph_ptr a2)
|
||||||
{
|
{
|
||||||
if (are_trivially_different(a1, a2))
|
if (are_trivially_different(a1, a2))
|
||||||
return std::vector<unsigned>();
|
return {};
|
||||||
unsigned n = a1->num_states();
|
unsigned n = a1->num_states();
|
||||||
assert(n == a2->num_states());
|
assert(n == a2->num_states());
|
||||||
class2states_t a1_classes = map_class_states(a1);
|
class2states_t a1_classes = map_class_states(a1);
|
||||||
|
|
@ -201,12 +194,12 @@ namespace spot
|
||||||
// Get the first possible mapping between a1 and a2, or return false if
|
// Get the first possible mapping between a1 and a2, or return false if
|
||||||
// the number of class or the size of the classes do not match.
|
// the number of class or the size of the classes do not match.
|
||||||
if (!(mapping_from_classes(mapping, a1_classes, a2_classes)))
|
if (!(mapping_from_classes(mapping, a1_classes, a2_classes)))
|
||||||
return std::vector<unsigned>();
|
return {};
|
||||||
|
|
||||||
while (!is_isomorphism(mapping, a1, a2))
|
while (!is_isomorphism(mapping, a1, a2))
|
||||||
{
|
{
|
||||||
if (!next_class_permutation(a2_classes))
|
if (!next_class_permutation(a2_classes))
|
||||||
return std::vector<unsigned>();
|
return {};
|
||||||
mapping_from_classes(mapping, a1_classes, a2_classes);
|
mapping_from_classes(mapping, a1_classes, a2_classes);
|
||||||
}
|
}
|
||||||
return mapping;
|
return mapping;
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,16 @@
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
/// \ingroup tgba_misc
|
/// \ingroup tgba_misc
|
||||||
/// \brief Return an isomorphism between a1 and a2 if such an isomorphism
|
/// \brief Check whether two automate are isormorphic.
|
||||||
/// exists. Otherwise, return an empty vector.
|
///
|
||||||
/// The return value is a vector indexed by states of a1, and containing
|
/// Return an isomorphism between a1 and a2 if such an isomorphism
|
||||||
/// states of a2.
|
/// exists. Otherwise, return an empty vector.
|
||||||
SPOT_API std::vector<unsigned> are_isomorphic(const const_tgba_digraph_ptr a1,
|
///
|
||||||
const const_tgba_digraph_ptr a2);
|
/// \return a vector indexed by states of \a a1, and containing
|
||||||
|
/// states of \a a2.
|
||||||
|
SPOT_API std::vector<unsigned>
|
||||||
|
are_isomorphic(const const_tgba_digraph_ptr a1,
|
||||||
|
const const_tgba_digraph_ptr a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue