6 #include <unordered_map>     7 #include <unordered_set>     9 #include <boost/range/algorithm/permutation.hpp>    10 #include <boost/range/algorithm/sort.hpp>    14 #include <vcsn/dyn/fwd.hh>    33   template <Automaton Aut1, Automaton Aut2>
    60     static_assert(std::is_same<weightset1_t, weightset2_t>{},
    61                   "are_isomorphic: contexts must be equal");
    70     template <Automaton Aut>
    74           std::unordered_map<label_t_of<Aut>,
    75                              std::pair<weight_t_of<Aut>, state_t_of<Aut>>,
    84     template <Automaton Aut>
    90               std::unordered_map<weight_t_of<Aut>,
    91                                  std::vector<state_t_of<Aut>>,
    94               vcsn::hash<labelset_t_of<Aut>>,
   102     using pair_t = std::pair<state1_t, state2_t>;
   106     using s1tos2_t = std::unordered_map<state1_t, state2_t>;
   107     using s2tos1_t = std::unordered_map<state2_t, state1_t>;
   133         { automaton1_t::element_type::null_state(),
   134           automaton2_t::element_type::null_state() };
   144     template <Automaton Aut>
   150           const state_t_of<Aut>& src = a->src_of(t);
   151           const label_t_of<Aut>& l = a->label_of(t);
   152           auto& doutsrc = dout[src];
   153           if (doutsrc.find(l) == doutsrc.end())
   154             dout[src][l] = {a->weight_of(t), a->dst_of(t)};
   167         nout1_[a1_->src_of(t1)][a1_->label_of(t1)][a1_->weight_of(t1)]
   168           .emplace_back(a1_->dst_of(t1));
   170         nout2_[a2_->src_of(t2)][a2_->label_of(t2)][a2_->weight_of(t2)]
   171           .emplace_back(a2_->dst_of(t2));
   193       return (a1_->num_states() != a2_->num_states()
   194               || a1_->num_transitions() != a2_->num_transitions());
   213               "are-isomorphic: lhs automaton must be accessible");
   215               "are-isomorphic: rhs automaton must be accessible");
   250     template <Automaton Aut>
   258       const auto& ws = *a->weightset();
   259       const auto& ls = *a->labelset();
   261       using transition_t = std::pair<weight_t_of<Aut>,
   265         [&](
const transition_t& t1, 
const transition_t& t2)
   267           if (ws.less(t1.first, t2.first))
   269           else if (ws.less(t2.first, t1.first))
   272             return ls.less(t1.second, t2.second);
   275 #define HASH_TRANSITIONS(Expression, Endpoint_Getter)                   \   277         auto endpoint_states = std::unordered_set<state_t_of<Aut>>{};   \   279         for (auto& t: Expression)                                       \   281             tt.emplace_back(a->weight_of(t), a->label_of(t));           \   282             endpoint_states.emplace(a->Endpoint_Getter(t));             \   284         boost::sort(tt, less);                                          \   285         for (const auto& t: tt)                                         \   287             hash_combine(res, ws.hash(t.first));                        \   288             hash_combine(res, ls.hash(t.second));                       \   290         hash_combine(res, endpoint_states.size());                      \   300 #undef HASH_TRANSITIONS   309         = std::unordered_map<class_id, std::pair<states1_t, states2_t>>{};
   310       for (
auto s1: a1_->all_states())
   312       for (
auto s2: a2_->all_states())
   319       for (
const auto& c: table)
   320         res.emplace_back(std::move(c.second.first), std::move(c.second.second));
   324                     return c1.first.size() > c2.first.size();
   344                            std::ostream& o = std::cerr)
 const   346       size_t max = 0, 
min = a1_->num_all_states();
   347       long double sum = 0.0;
   348       for (
const auto& c: cs)
   350           max = std::max(max, c.first.size());
   352           sum += c.first.size();
   354       long state_no = a1_->num_all_states();
   355       o << 
"State no: " << state_no << 
'\n'   356         << 
"Class no: " << cs.size() << 
'\n'   357         << 
"* min class size: " << 
min << 
'\n'   358         << 
"* avg class size: " << sum / cs.size() << 
'\n'   359         << 
"* max class size: " << max << 
'\n';
   364                        std::ostream& o = std::cerr)
 const   366       for (
const auto& c: cs)
   369           for (
const auto& s1: c.first)
   372           for (
const auto& s2: c.second)
   384       catch (
const std::out_of_range&)
   391       auto mss1 = std::unordered_set<state1_t>{};
   392       auto mss2 = std::unordered_set<state2_t>{};
   394       worklist.emplace(a1_->pre(), a2_->pre());
   395       worklist.emplace(a1_->post(), a2_->post());
   396       while (! worklist.empty())
   398           const auto p = std::move(worklist.top()); worklist.pop();
   407           const bool m1 = 
has(mss1, s1);
   408           const bool m2 = 
has(mss2, s2);
   419           if ((s1 == a1_->pre()) != (s2 == a2_->pre())
   420               || (s1 == a1_->post()) != (s2 == a2_->post()))
   423           int t1n = 0, t2n = 0;
   424           for (
auto t1: 
all_out(a1_, s1))
   426               auto d1 = a1_->dst_of(t1);
   427               const auto& w1 = a1_->weight_of(t1);
   428               const auto& l1 = a1_->label_of(t1);
   429               const auto& d2s = nout2_.at(s2).at(l1).at(w1);
   433               worklist.emplace(d1, d2);
   436           for (
auto t2: 
all_out(a2_, s2))
   438               auto d2 = a2_->dst_of(t2);
   439               const auto& w2 = a2_->weight_of(t2);
   440               const auto& l2 = a2_->label_of(t2);
   441               const auto& d1s = nout1_.at(s1).at(l2).at(w2);
   445               worklist.emplace(d1, d2);
   456       for (
const auto& c: state_classes_)
   457         for (
size_t i = 0; i < c.first.size(); ++ i)
   469       for (
long i = 1; i <= n; ++ i)
   481       class_permutation_max_.clear();
   482       class_permutation_generated_.clear();
   483       for (
const auto& c: state_classes_)
   485           class_permutation_max_.emplace_back(
factorial(c.second.size()) - 1);
   486           class_permutation_generated_.emplace_back(0);
   503       const int rightmost = int(state_classes_.size()) - 1;
   506       if (boost::next_permutation(state_classes_[rightmost].second))
   509           ++ class_permutation_generated_[rightmost];
   521       assert(class_permutation_generated_[rightmost]
   522              == class_permutation_max_[rightmost]);
   523       class_permutation_generated_[rightmost] = 0;
   525       for (i = rightmost - 1;
   527              && class_permutation_generated_[i] == class_permutation_max_[i];
   530           boost::next_permutation(state_classes_[i].second);
   531           class_permutation_generated_[i] = 0;
   537       boost::next_permutation(state_classes_[i].second);
   538       ++ class_permutation_generated_[i];
   551       for (
const auto& c: state_classes_)
   552         if (c.first.size() != c.second.size())
   581       const auto& ws = *a1_->weightset();
   588       worklist.emplace(a1_->pre(), a2_->pre());
   590       while (! worklist.empty())
   592           const pair_t states = worklist.top(); worklist.pop();
   605           if (dout1_[s1].
size() != dout2_[s2].
size())
   608           for (
const auto& l1_w1dst1 : dout1_[s1]) 
   610               const label1_t& l1 = l1_w1dst1.first;
   611               const weight1_t& w1 = l1_w1dst1.second.first;
   612               const state1_t& dst1 = l1_w1dst1.second.second;
   614               const auto& s2out = dout2_.find(s2);
   615               if (s2out == dout2_.cend())
   617               const auto& s2outl = s2out->second.find(l1);
   618               if (s2outl == s2out->second.cend())
   621               state2_t dst2 = s2outl->second.second;
   623               if (! ws.equal(w1, w2))
   626               const auto& isomorphics_to_dst1 = 
fr_.
s1tos2.find(dst1);
   627               const auto& isomorphics_to_dst2 = 
fr_.
s2tos1.find(dst2);
   629               if (isomorphics_to_dst1 == 
fr_.
s1tos2.cend())
   631                   if (isomorphics_to_dst2 == 
fr_.
s2tos1.cend()) 
   635                       worklist.emplace(dst1, dst2);
   640               else if (isomorphics_to_dst1 == 
fr_.
s1tos2.cend()
   641                        || isomorphics_to_dst1->second != dst2
   642                        || isomorphics_to_dst2->second != dst1)
   666               __func__, 
": isomorphism-check not successfully performed");
   669         res[s2s1.first] = s2s1.second;
   679         << 
"    node [shape = box, style = rounded]\n";
   680       for (
const auto& p : orig)
   683             o << 
"    " << p.first - 2
   684               << 
" [label = \"" << p.second << 
"\"]\n";
   692   template <Automaton Aut1, Automaton Aut2>
   705       template <Automaton Aut1, Automaton Aut2>
   709         const auto& a1 = aut1->
as<Aut1>();
   710         const auto& a2 = aut2->
as<Aut2>();
 
void initialize_next_class_combination_state()
 
auto all_transitions(const Aut &aut)
All the transition indexes between all states (including pre and post). 
 
weightset_t_of< automaton1_t > weightset1_t
 
nout_t< automaton2_t > nout2_
 
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s. 
 
typename detail::weightset_t_of_impl< base_t< ValueSet > >::type weightset_t_of
 
std::stack< pair_t > worklist_t
 
void print_class_stats(const state_classes_t &cs, std::ostream &o=std::cerr) const
Handy debugging method. 
 
std::size_t class_id
Automaton states partitioned into classes. 
 
state_t_of< automaton2_t > state2_t
 
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
 
typename detail::transition_t_of_impl< base_t< ValueSet > >::type transition_t_of
 
std::unordered_map< state_t_of< Aut >, std::unordered_map< label_t_of< Aut >, std::unordered_map< weight_t_of< Aut >, std::vector< state_t_of< Aut > >, vcsn::hash< weightset_t_of< Aut > >, vcsn::equal_to< weightset_t_of< Aut > >>, vcsn::hash< labelset_t_of< Aut > >, vcsn::equal_to< labelset_t_of< Aut > >> > nout_t
For the nonsequential case. 
 
typename detail::state_t_of_impl< base_t< ValueSet > >::type state_t_of
 
nout_t< automaton1_t > nout1_
 
context_t_of< automaton1_t > context1_t
 
std::unordered_map< state_t_of< Aut >, std::unordered_map< label_t_of< Aut >, std::pair< weight_t_of< Aut >, state_t_of< Aut > >, vcsn::hash< labelset_t_of< Aut > >, vcsn::equal_to< labelset_t_of< Aut > >> > dout_t
See the comment for out_ in minimize.hh. 
 
are_isomorphic_impl(const Aut1 &a1, const Aut2 &a2)
 
full_response get_full_response()
 
std::pair< state1_t, state2_t > pair_t
A worklist of pairs of states which are candidate to be isomorphic. 
 
bool next_class_combination()
 
std::unordered_map< state2_t, state1_t > s2tos1_t
 
std::unordered_map< state1_t, state2_t > s1tos2_t
The maps associating the states of a1_ and the states of a2_-> 
 
auto sort(const Aut &a) -> permutation_automaton< Aut >
 
weight_t_of< automaton2_t > weight2_t
 
bool is_isomorphism_valid()
 
void print_classes(const state_classes_t &cs, std::ostream &o=std::cerr) const
Handy debugging method. 
 
std::pair< states1_t, states2_t > class_pair_t
 
label_t_of< automaton1_t > label1_t
 
auto all_in(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions entering state s. 
 
bool are_isomorphic(const Aut1 &a1, const Aut2 &a2)
 
transition_t_of< automaton2_t > transition2_t
 
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
 
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s. 
 
typename detail::context_t_of_impl< base_t< ValueSet > >::type context_t_of
 
Provide a variadic mul on top of a binary mul(), and one(). 
 
origins_t origins()
Only meaningful if operator() returned true. 
 
s1tos2_t s1tos2
Only meaningful if the tag is tag::isomorphic. 
 
weight_t_of< automaton1_t > weight1_t
 
static std::ostream & print(const origins_t &orig, std::ostream &o)
Print origins. 
 
std::vector< state1_t > states1_t
 
bool trivially_different() const
 
dout_t< automaton2_t > dout2_
 
A datum specifying if two given automata are isomorphic, and why if they are not. ...
 
typename detail::labelset_t_of_impl< base_t< ValueSet > >::type labelset_t_of
 
pair_t counterexample
Only meaningful if the tag is tag::counterexample. 
 
struct vcsn::detail::are_isomorphic_impl::full_response fr_
 
std::vector< state2_t > states2_t
 
labelset_t_of< context2_t > labelset2_t
 
typename detail::label_t_of_impl< base_t< ValueSet > >::type label_t_of
 
weightset_t_of< automaton2_t > weightset2_t
 
#define HASH_TRANSITIONS(Expression, Endpoint_Getter)
 
std::map< state2_t, state1_t > origins_t
A map from each a2_ state to the corresponding a1_ state. 
 
std::vector< long > class_permutation_max_
We need to keep some (small) state between a next_class_combination call and the next. 
 
auto & as()
Extract wrapped typed automaton. 
 
std::vector< class_pair_t > state_classes_t
 
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
 
std::vector< long > class_permutation_generated_
 
transition_t_of< automaton1_t > transition1_t
 
label_t_of< automaton2_t > label2_t
 
dout_t< automaton1_t > dout1_
For the simpler, faster sequential case. 
 
bool is_accessible(const Aut &a)
Whether all its states are accessible. 
 
std::vector< std::pair< string_t, string_t > > transitions_t
 
full_response get_full_response_sequential()
 
const state_classes_t make_state_classes()
 
bool is_isomorphism_valid_throwing()
 
void hash_combine(std::size_t &seed, const T &v)
 
Functor to compare Values of ValueSets. 
 
state_t_of< automaton1_t > state1_t
 
void update_result_isomorphism()
 
context_t_of< automaton2_t > context2_t
 
labelset_t_of< context1_t > labelset1_t
 
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message. 
 
bool is_sequential_filling(const Aut &a, dout_t< Aut > &dout)
 
Request the unordered_map implementation. 
 
typename detail::weight_t_of_impl< base_t< ValueSet > >::type weight_t_of
 
class_id state_to_class(state_t_of< Aut > s, Aut &a)
 
state_classes_t state_classes_
 
full_response get_full_response_nonsequential()