17 #ifndef AWALI_ALGOS_ARE_ISOMORPHIC_HH
18 # define AWALI_ALGOS_ARE_ISOMORPHIC_HH
23 # include <unordered_map>
24 # include <unordered_set>
44 template <
typename Aut1,
typename Aut2>
48 using automaton1_t = Aut1;
55 using automaton2_t = Aut2;
64 using weightset_t = weightset1_t;
65 using labelset_t = labelset1_t;
67 using states_t = std::pair<state_t, state_t>;
73 template<
typename Automaton>
77 std::unordered_map<label_t_of<Automaton>,
78 std::pair<weight_t_of<Automaton>,
84 dout_t<automaton1_t> dout1_;
85 dout_t<automaton2_t> dout2_;
88 template<
typename Automaton>
94 std::unordered_map<weight_t_of<Automaton>,
100 nout_t<automaton1_t> nout1_;
101 nout_t<automaton2_t> nout2_;
106 using pair_t = std::pair<state_t, state_t>;
107 using worklist_t = std::stack<pair_t>;
108 worklist_t worklist_;
111 using s1tos2_t = std::unordered_map<state_t, state_t>;
112 using s2tos1_t = std::unordered_map<state_t, state_t>;
128 nocounterexample = 2,
130 trivially_different = 3,
135 pair_t counterexample;
142 : response(tag::never_computed)
149 template <
typename Automaton>
150 bool is_sequential_filling(
const Automaton& a,
151 dout_t<Automaton>& dout)
153 for (
auto t : a->all_transitions())
155 const state_t& src = a->src_of(t);
157 auto& doutsrc = dout[src];
158 if (doutsrc.find(l) == doutsrc.end())
159 dout[src][l] = {a->weight_of(t), a->dst_of(t)};
171 for (
auto t1 : a1_->all_transitions())
172 nout1_[a1_->src_of(t1)][a1_->label_of(t1)][a1_->weight_of(t1)]
173 .emplace_back(a1_->dst_of(t1));
174 for (
auto t2 : a2_->all_transitions())
175 nout2_[a2_->src_of(t2)][a2_->label_of(t2)][a2_->weight_of(t2)]
176 .emplace_back(a2_->dst_of(t2));
187 bool trivially_different()
190 if (a1_->num_states() != a2_->num_states())
192 if (a1_->num_transitions() != a2_->num_transitions())
219 fr_.response = full_response::tag::trivially_different;
222 "are-isomorphic: lhs automaton must be accessible");
224 "are-isomorphic: rhs automaton must be accessible");
229 if (trivially_different())
232 if (is_sequential_filling(a1_, dout1_))
233 if (is_sequential_filling(a2_, dout2_))
238 if (is_sequential_filling(a2_, dout2_))
259 template<
typename Automaton>
264 std::hash_combine(res, s == a->pre());
265 std::hash_combine(res, s == a->post());
267 const auto ws = * a->weightset();
268 const auto ls = * a->labelset();
272 using transitions_t = std::vector<transition_t>;
276 if (ws.less_than(t1.first, t2.first))
278 else if (ws.less_than(t2.first, t1.first))
281 return ls.less_than(t1.second, t2.second);
284 #define HASH_TRANSITIONS(expression, endpoint_getter) \
286 std::unordered_set<state_t> endpoint_states; \
288 for (auto& t: expression) \
290 tt.emplace_back(transition_t{a->weight_of(t), a->label_of(t)}); \
291 endpoint_states.emplace(a->endpoint_getter(t)); \
293 std::sort(tt.begin(), tt.end(), less_than); \
294 for (const auto& t: tt) \
296 std::hash_combine(res, ws.hash(t.first)); \
297 std::hash_combine(res, ls.hash(t.second)); \
299 std::hash_combine(res, endpoint_states.size()); \
309 #undef HASH_TRANSITIONS
317 std::unordered_map<class_id, std::pair<states1_t, states2_t>> table;
318 for (
auto s1: a1_->all_states())
320 for (
auto s2: a2_->all_states())
327 for (
const auto&
c: table)
328 res.emplace_back(std::move(
c.second.first), std::move(
c.second.second));
332 return c1.first.size() > c2.first.size();
353 size_t max = 0,
min = a1_->num_all_states();
354 long double sum = 0.0;
355 for (
const auto&
c: cs)
359 sum +=
c.first.size();
361 long state_no = a1_->num_all_states();
362 std::cerr <<
"State no: " << state_no <<
"\n";
363 std::cerr <<
"Class no: " << cs.size() <<
"\n";
364 std::cerr <<
"* min class size: " <<
min <<
"\n";
365 std::cerr <<
"* avg class size: " << sum / cs.size() <<
"\n";
366 std::cerr <<
"* max class size: " <<
max <<
"\n";
372 for (
const auto&
c: cs)
375 for (
const auto& s1:
c.first)
376 std::cerr << s1 <<
" ";
378 for (
const auto& s2:
c.second)
379 std::cerr << s2 <<
" ";
390 catch (
const std::out_of_range&)
397 std::unordered_set<state_t> mss1;
398 std::unordered_set<state_t> mss2;
399 std::stack<pair_t> worklist;
400 worklist.push({a1_->pre(), a2_->pre()});
401 worklist.push({a1_->post(), a2_->post()});
402 while (! worklist.empty())
404 const auto p = std::move(worklist.top()); worklist.pop();
411 if (fr_.s1tos2_.at(s1) != s2 || fr_.s2tos1_.at(s2) != s1)
413 const bool m1 = (mss1.find(s1) != mss1.end());
414 const bool m2 = (mss2.find(s2) != mss2.end());
425 if ((s1 == a1_->pre()) != (s2 == a2_->pre())
426 || (s1 == a1_->post()) != (s2 == a2_->post()))
429 int t1n = 0, t2n = 0;
430 for (
auto t1: a1_->all_out(s1))
432 auto d1 = a1_->dst_of(t1);
433 const auto& w1 = a1_->weight_of(t1);
434 const auto& l1 = a1_->label_of(t1);
435 const auto& d2s = nout2_.at(s2).at(l1).at(w1);
436 auto d2 = fr_.s1tos2_.at(d1);
439 worklist.push({d1, d2});
442 for (
auto t2: a2_->all_out(s2))
444 auto d2 = a2_->dst_of(t2);
445 const auto& w2 = a2_->weight_of(t2);
446 const auto& l2 = a2_->label_of(t2);
447 const auto& d1s = nout1_.at(s1).at(l2).at(w2);
448 auto d1 = fr_.s2tos1_.at(d2);
451 worklist.push({d1, d2});
463 for (
int i = 0; i < int(
c.first.size()); ++ i)
467 fr_.s1tos2_[s1] = s2;
468 fr_.s2tos1_[s2] = s1;
475 for (
long i = 1; i <=
n; ++ i)
511 if (std::next_permutation(
state_classes_[rightmost].second.begin(),
531 for (i = rightmost - 1;
560 if (
c.first.size() !=
c.second.size())
562 fr_.response = full_response::tag::nocounterexample;
574 fr_.response = full_response::tag::isomorphic;
581 fr_.response = full_response::tag::nocounterexample;
590 fr_.response = full_response::tag::counterexample;
592 worklist_.push({a1_->pre(), a2_->pre()});
594 while (! worklist_.empty())
596 const states_t
states = worklist_.top(); worklist_.pop();
602 fr_.counterexample = {s1, s2};
609 if (dout1_[s1].size() != dout2_[s2].size())
612 for (
const auto& l1_w1dst1 : dout1_[s1])
614 const label1_t& l1 = l1_w1dst1.first;
615 const weight1_t& w1 = l1_w1dst1.second.first;
616 const state_t& dst1 = l1_w1dst1.second.second;
618 const auto& s2out = dout2_.find(s2);
619 if (s2out == dout2_.cend())
621 const auto& s2outl = s2out->second.find(l1);
622 if (s2outl == s2out->second.cend())
624 weight2_t w2 = s2outl->second.first;
625 state_t dst2 = s2outl->second.second;
630 const auto& isomorphics_to_dst1 = fr_.s1tos2_.find(dst1);
631 const auto& isomorphics_to_dst2 = fr_.s2tos1_.find(dst2);
633 if (isomorphics_to_dst1 == fr_.s1tos2_.cend())
635 if (isomorphics_to_dst2 == fr_.s2tos1_.cend())
637 fr_.s1tos2_[dst1] = dst2;
638 fr_.s2tos1_[dst2] = dst1;
639 worklist_.push({dst1, dst2});
644 else if (isomorphics_to_dst1 == fr_.s1tos2_.cend()
645 || isomorphics_to_dst1->second != dst2
646 || isomorphics_to_dst2->second != dst1)
650 fr_.response = full_response::tag::isomorphic;
657 return r.response == full_response::tag::isomorphic;
669 require(fr_.reponse == full_response::tag::isomorphic,
670 __func__,
": isomorphism-check not successfully performed");
672 for (
const auto& s2s1: fr_.s2tos1_)
673 res[s2s1.first] = s2s1.second;
682 o <<
"/* Origins." << std::endl
683 <<
" node [shape = box, style = rounded]" << std::endl;
687 o <<
" " << p.first - 2
688 <<
" [label = \"" << p.second <<
"\"]" << std::endl;
690 o <<
"*/" << std::endl;
704 template <
typename Aut1,
typename Aut2>
#define HASH_TRANSITIONS(expression, endpoint_getter)
The semiring of complex numbers.
Definition: c.hh:44
Definition: are_isomorphic.hh:46
class_id state_to_class(state_t s, Automaton &a)
Definition: are_isomorphic.hh:260
void print_classes(const state_classes_t &cs)
Handy debugging method.
Definition: are_isomorphic.hh:370
bool is_isomorphism_valid()
Definition: are_isomorphic.hh:384
bool next_class_combination()
Definition: are_isomorphic.hh:499
origins_t origins()
Only meaningful if operator() returned true.
Definition: are_isomorphic.hh:667
std::vector< class_pair_t > state_classes_t
Definition: are_isomorphic.hh:256
void initialize_next_class_combination_state()
Definition: are_isomorphic.hh:485
void print_class_stats(const state_classes_t &cs)
Handy debugging method.
Definition: are_isomorphic.hh:351
std::pair< states1_t, states2_t > class_pair_t
Definition: are_isomorphic.hh:255
std::vector< long > class_permutation_max_
We need to keep some (small) state between a next_class_combination call and the next.
Definition: are_isomorphic.hh:482
const state_classes_t make_state_classes()
Definition: are_isomorphic.hh:314
long factorial(long n)
Definition: are_isomorphic.hh:472
state_classes_t state_classes_
Definition: are_isomorphic.hh:257
std::vector< state_t > states2_t
Definition: are_isomorphic.hh:248
bool is_isomorphism_valid_throwing()
Definition: are_isomorphic.hh:395
const full_response get_full_response_nonsequential()
Definition: are_isomorphic.hh:551
void update_result_isomorphism()
Definition: are_isomorphic.hh:460
std::map< state_t, state_t > origins_t
A map from each a2_ state to the corresponding a1_ state.
Definition: are_isomorphic.hh:663
bool operator()()
Definition: are_isomorphic.hh:654
const full_response get_full_response()
Definition: are_isomorphic.hh:213
std::size_t class_id
Automaton states partitioned into classes.
Definition: are_isomorphic.hh:254
std::vector< state_t > states1_t
Definition: are_isomorphic.hh:247
are_isomorphicer(const Aut1 &a1, const Aut2 &a2)
Definition: are_isomorphic.hh:207
static std::ostream & print(const origins_t &orig, std::ostream &o)
Print origins.
Definition: are_isomorphic.hh:680
const full_response get_full_response_sequential()
Definition: are_isomorphic.hh:586
std::vector< long > class_permutation_generated_
Definition: are_isomorphic.hh:483
The semiring of Natural numbers.
Definition: n.hh:34
The semiring of floating Numbers.
Definition: r.hh:35
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
Definition: hash.hh:42
This is useful to make hashes with labels or weights as keys without using non-default constructors; ...
Definition: hash.hh:58
std::vector< state_t > states(abstract_automaton_t const *aut, bool all)
bool has(const std::map< Key, Value, Compare, Alloc > &s, const Key &e)
Definition: map.hh:53
RatExpSet::ratexp_t less_than(const RatExpSet &rs, const typename RatExpSet::ratexp_t &v)
Definition: less_than.hh:166
typename internal::label_t_of_impl< internal::base_t< ValueSet > >::type label_t_of
Helper to retrieve the type of the labels of a value set.
Definition: traits.hh:71
RatExpSet::ratexp_t equals(const RatExpSet &rs, const typename RatExpSet::ratexp_t &v)
Definition: equal_visit.hh:153
bool is_accessible(const Aut &aut)
Test whether every state of the automaton is accessible.
Definition: accessible.hh:334
void sort(Aut a, Compare p)
Definition: sort.hh:28
typename internal::weight_t_of_impl< internal::base_t< ValueSet > >::type weight_t_of
Helper to retrieve the type of the weights of a value set.
Definition: traits.hh:81
typename internal::context_t_of_impl< internal::base_t< ValueSet > >::type context_t_of
Helper to retrieve the type of the context of a value set.
Definition: traits.hh:66
typename internal::labelset_t_of_impl< internal::base_t< ValueSet > >::type labelset_t_of
Helper to retrieve the type of the labelset of a value set.
Definition: traits.hh:76
bool are_isomorphic(const Aut1 &a1, const Aut2 &a2)
tests whether two automata are isomorphic
Definition: are_isomorphic.hh:706
void require(bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:55
typename internal::weightset_t_of_impl< internal::base_t< ValueSet > >::type weightset_t_of
Helper to retrieve the type of the weightset of a value set.
Definition: traits.hh:86
ATTRIBUTE_CONST int max(int a, int b)
Definition: arith.hh:54
ATTRIBUTE_CONST int min(int a, int b)
Definition: arith.hh:48
Main namespace of Awali.
Definition: ato.hh:22
unsigned state_t
Definition: types.hh:21
unsigned transition_t
Definition: types.hh:22