1 #ifndef VCSN_ALGOS_ARE_ISOMORPHIC_HH
2 # define VCSN_ALGOS_ARE_ISOMORPHIC_HH
7 # include <unordered_map>
8 # include <unordered_set>
29 template <
typename Aut1,
typename Aut2>
56 using states_t = std::pair<state1_t, state2_t>;
62 template<
typename Automaton>
66 std::unordered_map<label_t_of<Automaton>,
67 std::pair<weight_t_of<Automaton>,
77 template<
typename Automaton>
83 std::unordered_map<weight_t_of<Automaton>,
84 std::vector<state_t_of<Automaton>>,
95 using pair_t = std::pair<state1_t, state2_t>;
100 using s1tos2_t = std::unordered_map<state1_t, state2_t>;
101 using s2tos1_t = std::unordered_map<state2_t, state1_t>;
138 template <
typename Automaton>
142 for (
auto t : a->all_transitions())
146 auto& doutsrc = dout[src];
147 if (doutsrc.find(l) == doutsrc.end())
148 dout[src][l] = {a->weight_of(t), a->dst_of(t)};
160 for (
auto t1 :
a1_->all_transitions())
162 .emplace_back(
a1_->dst_of(t1));
163 for (
auto t2 :
a2_->all_transitions())
165 .emplace_back(
a2_->dst_of(t2));
179 if (
a1_->num_states() !=
a2_->num_states())
181 if (
a1_->num_transitions() !=
a2_->num_transitions())
211 "are-isomorphic: lhs automaton must be accessible");
213 "are-isomorphic: rhs automaton must be accessible");
248 template<
typename Automaton>
253 std::hash_combine(res, s == a->pre());
254 std::hash_combine(res, s == a->post());
256 const auto ws = * a->weightset();
257 const auto ls = * a->labelset();
259 using transition_t = std::pair<weight_t_of<Automaton>,
263 [&](
const transition_t& t1,
const transition_t& t2)
265 if (ws.less_than(t1.first, t2.first))
267 else if (ws.less_than(t2.first, t1.first))
270 return ls.less_than(t1.second, t2.second);
273 #define HASH_TRANSITIONS(expression, endpoint_getter) \
275 std::unordered_set<state_t_of<Automaton>> endpoint_states; \
277 for (auto& t: expression) \
279 tt.emplace_back(transition_t{a->weight_of(t), a->label_of(t)}); \
280 endpoint_states.emplace(a->endpoint_getter(t)); \
282 std::sort(tt.begin(), tt.end(), less_than); \
283 for (const auto& t: tt) \
285 std::hash_combine(res, ws.hash(t.first)); \
286 std::hash_combine(res, ls.hash(t.second)); \
288 std::hash_combine(res, endpoint_states.size()); \
298 #undef HASH_TRANSITIONS
306 std::unordered_map<class_id, std::pair<states1_t, states2_t>> table;
307 for (
auto s1:
a1_->all_states())
309 for (
auto s2:
a2_->all_states())
316 for (
const auto& c: table)
317 res.emplace_back(std::move(c.second.first), std::move(c.second.second));
321 return c1.first.size() > c2.first.size();
327 std::sort(c.first.begin(), c.first.end());
332 std::sort(c.second.begin(), c.second.end());
342 size_t max = 0, min =
a1_->num_all_states();
343 long double sum = 0.0;
344 for (
const auto& c: cs)
346 max = std::max(max, c.first.size());
347 min = std::min(min, c.first.size());
348 sum += c.first.size();
350 long state_no =
a1_->num_all_states();
351 std::cerr <<
"State no: " << state_no <<
"\n";
352 std::cerr <<
"Class no: " << cs.size() <<
"\n";
353 std::cerr <<
"* min class size: " << min <<
"\n";
354 std::cerr <<
"* avg class size: " << sum / cs.size() <<
"\n";
355 std::cerr <<
"* max class size: " << max <<
"\n";
361 for (
const auto& c: cs)
364 for (
const auto& s1: c.first)
365 std::cerr << s1 <<
" ";
367 for (
const auto& s2: c.second)
368 std::cerr << s2 <<
" ";
379 catch (
const std::out_of_range&)
386 std::unordered_set<state1_t> mss1;
387 std::unordered_set<state2_t> mss2;
388 std::stack<pair_t> worklist;
389 worklist.push({
a1_->pre(),
a2_->pre()});
390 worklist.push({
a1_->post(),
a2_->post()});
391 while (! worklist.empty())
393 const auto p = std::move(worklist.top()); worklist.pop();
402 const bool m1 = (mss1.find(s1) != mss1.end());
403 const bool m2 = (mss2.find(s2) != mss2.end());
414 if ((s1 ==
a1_->pre()) != (s2 ==
a2_->pre())
415 || (s1 ==
a1_->post()) != (s2 ==
a2_->post()))
418 int t1n = 0, t2n = 0;
419 for (
auto t1:
a1_->all_out(s1))
421 auto d1 =
a1_->dst_of(t1);
422 const auto& w1 =
a1_->weight_of(t1);
423 const auto& l1 =
a1_->label_of(t1);
424 const auto& d2s =
nout2_.at(s2).at(l1).at(w1);
428 worklist.push({d1, d2});
431 for (
auto t2:
a2_->all_out(s2))
433 auto d2 =
a2_->dst_of(t2);
434 const auto& w2 =
a2_->weight_of(t2);
435 const auto& l2 =
a2_->label_of(t2);
436 const auto& d1s =
nout1_.at(s1).at(l2).at(w2);
440 worklist.push({d1, d2});
452 for (
int i = 0; i < int(c.first.size()); ++ i)
464 for (
long i = 1; i <= n; ++ i)
500 if (std::next_permutation(
state_classes_[rightmost].second.begin(),
520 for (i = rightmost - 1;
549 if (c.first.size() != c.second.size())
601 for (
const auto& l1_w1dst1 :
dout1_[s1])
603 const label1_t& l1 = l1_w1dst1.first;
604 const weight1_t& w1 = l1_w1dst1.second.first;
605 const state1_t& dst1 = l1_w1dst1.second.second;
607 const auto& s2out =
dout2_.find(s2);
608 if (s2out ==
dout2_.cend())
610 const auto& s2outl = s2out->second.find(l1);
611 if (s2outl == s2out->second.cend())
614 state2_t dst2 = s2outl->second.second;
616 if (! weightset_t::equals(w1, w2))
619 const auto& isomorphics_to_dst1 =
fr_.
s1tos2_.find(dst1);
620 const auto& isomorphics_to_dst2 =
fr_.
s2tos1_.find(dst2);
622 if (isomorphics_to_dst1 ==
fr_.
s1tos2_.cend())
624 if (isomorphics_to_dst2 ==
fr_.
s2tos1_.cend())
633 else if (isomorphics_to_dst1 ==
fr_.
s1tos2_.cend()
634 || isomorphics_to_dst1->second != dst2
635 || isomorphics_to_dst2->second != dst1)
659 __func__,
": isomorphism-check not successfully performed");
662 res[s2s1.first] = s2s1.second;
671 o <<
"/* Origins." << std::endl
672 <<
" node [shape = box, style = rounded]" << std::endl;
676 o <<
" " << p.first - 2
677 <<
" [label = \"" << p.second <<
"\"]" << std::endl;
679 o <<
"*/" << std::endl;
685 template <
typename Aut1,
typename Aut2>
700 template <
typename Aut1,
typename Aut2>
704 const auto& a1 = aut1->as<Aut1>();
705 const auto& a2 = aut2->as<Aut2>();
715 #endif // !VCSN_ALGOS_ARE_ISOMORPHIC_HH
void print_classes(const state_classes_t &cs)
Handy debugging method.
std::vector< long > class_permutation_generated_
struct vcsn::are_isomorphicer::full_response fr_
void print_class_stats(const state_classes_t &cs)
Handy debugging method.
REGISTER_DECLARE(accessible,(const automaton &) -> automaton)
std::shared_ptr< detail::automaton_base > automaton
dout_t< automaton1_t > dout1_
For the simpler, faster sequential case.
labelset_t_of< context1_t > labelset1_t
bool next_class_combination()
std::pair< state1_t, state2_t > pair_t
A worklist of pairs of states which are candidate to be isomorphic.
std::stack< pair_t > worklist_t
std::unordered_map< state_t_of< Automaton >, std::unordered_map< label_t_of< Automaton >, std::pair< weight_t_of< Automaton >, state_t_of< Automaton >>, vcsn::hash< labelset_t_of< Automaton >>, vcsn::equal_to< labelset_t_of< Automaton >>>> dout_t
See the comment for out_ in minimize.hh.
std::pair< states1_t, states2_t > class_pair_t
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
#define HASH_TRANSITIONS(expression, endpoint_getter)
transition_t_of< automaton2_t > transition2_t
context_t_of< automaton2_t > context2_t
std::map< state2_t, state1_t > origins_t
A map from each a2_ state to the corresponding a1_ state.
void initialize_next_class_combination_state()
transition_t_of< automaton1_t > transition1_t
class_id state_to_class(state_t_of< Automaton > s, Automaton &a)
void update_result_isomorphism()
bool are_isomorphic(const Aut1 &a1, const Aut2 &a2)
static std::ostream & print(const origins_t &orig, std::ostream &o)
Print origins.
weightset_t_of< automaton1_t > weightset1_t
const full_response get_full_response_sequential()
std::vector< state1_t > states1_t
typename detail::context_t_of_impl< base_t< ValueSet >>::type context_t_of
weight_t_of< automaton2_t > weight2_t
std::vector< state2_t > states2_t
bool trivially_different()
label_t_of< automaton1_t > label1_t
const full_response get_full_response_nonsequential()
auto sort(const Aut &a) -> permutation_automaton< Aut >
state_t_of< automaton1_t > state1_t
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
bool is_isomorphism_valid_throwing()
state_t_of< automaton2_t > state2_t
bool is_sequential_filling(const Automaton &a, dout_t< Automaton > &dout)
weightset_t_of< automaton1_t > weightset2_t
bool are_isomorphic(const automaton &aut1, const automaton &aut2)
Bridge.
std::unordered_map< state_t_of< Automaton >, std::unordered_map< label_t_of< Automaton >, std::unordered_map< weight_t_of< Automaton >, std::vector< state_t_of< Automaton >>, vcsn::hash< weightset_t_of< Automaton >>, vcsn::equal_to< weightset_t_of< Automaton >>>, vcsn::hash< labelset_t_of< Automaton >>, vcsn::equal_to< labelset_t_of< Automaton >>>> nout_t
For the nonsequential case.
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
std::vector< long > class_permutation_max_
We need to keep some (small) state between a next_class_combination call and the next.
dout_t< automaton2_t > dout2_
enum vcsn::are_isomorphicer::full_response::tag response
Provide a variadic mul on top of a binary mul(), and one().
std::vector< std::pair< string_t, string_t >> transitions_t
auto sum(const A &lhs, const B &rhs) -> decltype(join_automata(lhs, rhs))
are_isomorphicer(const Aut1 &a1, const Aut2 &a2)
label_t_of< automaton2_t > label2_t
typename detail::weight_t_of_impl< base_t< ValueSet >>::type weight_t_of
origins_t origins()
Only meaningful if operator() returned true.
context_t_of< automaton1_t > context1_t
std::size_t class_id
Automaton states partitioned into classes.
state_classes_t state_classes_
bool is_accessible(const Aut &a)
nout_t< automaton2_t > nout2_
s1tos2_t s1tos2_
Only meaningful if the tag is tag::isomorphic.
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
const state_classes_t make_state_classes()
RatExpSet::value_t less_than(const RatExpSet &rs, const typename RatExpSet::value_t &v)
std::vector< class_pair_t > state_classes_t
labelset_t_of< context2_t > labelset2_t
std::pair< state1_t, state2_t > states_t
nout_t< automaton1_t > nout1_
A datum specifying if two given automata are isomorphic, and why if they are not. ...
std::unordered_map< state1_t, state2_t > s1tos2_t
The maps associating the states of a1_ and the states of a2_->
weight_t_of< automaton1_t > weight1_t
pair_t counterexample
Only meaningful if the tag is tag::counterexample.
const full_response get_full_response()
bool has(const std::map< Key, Value, Compare, Alloc > &s, const Key &e)
bool is_isomorphism_valid()
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
std::unordered_map< state2_t, state1_t > s2tos1_t