Vaucanson  1.4.1
isomorph.hxx
1 // isomorph.hxx: this file is part of the Vaucanson project.
2 //
3 // Vaucanson, a generic library for finite state machines.
4 //
5 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The Vaucanson Group.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // The complete GNU General Public Licence Notice can be found as the
13 // `COPYING' file in the root directory.
14 //
15 // The Vaucanson Group consists of people listed in the `AUTHORS' file.
16 //
17 #ifndef VCSN_ALGORITHMS_ISOMORPH_HXX
18 # define VCSN_ALGORITHMS_ISOMORPH_HXX
19 
22 
23 # include <vaucanson/automata/concept/automata_base.hh>
24 # include <vaucanson/misc/usual_macros.hh>
25 
27 
28 # include <queue>
29 # include <set>
30 # include <list>
31 # include <utility>
32 
33 namespace vcsn
34 {
35 
36  /*--------------------------------------.
37  | Helper for are_isomorphic algorithm. |
38  `--------------------------------------*/
39 
40  class Trie
41  {
42  public:
43  Trie();
45  Trie(const Trie& t);
46 
47  Trie* insert(std::vector<int>&);
48 
49  public:
50  std::list<int> A, B;
51 
52  // Node in list C pointing to this node
53  std::list<Trie*>::iterator L;
54 
55  private:
57  bool L_is_valid;
58  Trie* insert_suffix(std::vector<int>&, unsigned int);
59  std::map<int, Trie> children;
60  };
61 
62  inline
63  Trie::Trie ()
64  : A(), B(), L(), L_is_valid(false), children()
65  {
66  }
67 
68  inline
69  Trie::Trie (const Trie& t)
70  : A(t.A), B(t.B),
71  L(), L_is_valid(t.L_is_valid), children(t.children)
72  {
73  if (L_is_valid)
74  L = t.L;
75  }
76 
77 
78  // Find node associated to the insertion of a vector of integers.
79  inline Trie* Trie::insert(std::vector<int>& S)
80  {
81  return insert_suffix(S, 0);
82  }
83 
84  inline Trie* Trie::insert_suffix(std::vector<int>& S, unsigned int p)
85  {
86  if (p == S.capacity() - 1)
87  return this;
88 
89  return children[S[p]].insert_suffix(S, p + 1);
90  }
91 
92 
93 
94  /*--------------------------------------.
95  | Functor for are_isomorphic algorithm. |
96  `--------------------------------------*/
97 
98  template<typename A, typename T>
99  class Isomorpher
100  {
101  public:
102 
103  typedef Element<A, T> auto_t;
104  AUTOMATON_TYPES(auto_t);
105 
106  typedef std::vector<int> trans_t;
107  typedef std::list<int> state_list_t;
108  typedef std::vector<state_list_t> delta_t;
109 
110  struct automaton_vars
111  {
112  // We have to build the vector T_L using a valid iterator (i.e., not a
113  // singular one). We use a temp int list to this end.
114  automaton_vars(const automaton_t& aut)
115  : s(aut),
116  aut(aut),
117  perm(aut.states().size(), -1),
118  T_L(aut.states().size(), state_list_t().end()),
119  delta_det_in(aut.states().size()),
120  delta_det_out(aut.states().size()),
121  class_state(aut.states().size())
122  {
123  }
124 
125  void
126  shrink_unused()
127  {
128  T_L.clear();
129  delta_det_in.clear();
130  delta_det_out.clear();
131  class_state.clear();
132  class_state.clear();
133  }
134 
135 
136  Skeleton<A, T> s;
137  const automaton_t aut;
138 
139  // perm[i] = state of another automaton corresponding to state i in
140  // the isomorphism, or -1 when the association
141  // is yet undefined.
142  trans_t perm;
143 
144  // Vector indexed by states that points to the corresponding node
145  // of the list of states of each class of states stored in the
146  // Tries.
147  std::vector<state_list_t::iterator> T_L;
148 
149  // Lists of deterministic ingoing and outgoing transitions
150  // for each state (delta_det_in[i] = list of deterministic ingoing
151  // transitions for state i, idem for delta_det_out[i])
152  delta_t delta_det_in;
153  delta_t delta_det_out;
154 
155  // class_state_A[i] = class (node of a Trie) of state i for automaton A
156  std::vector<Trie*> class_state;
157  };
158 
159  Isomorpher(const automaton_t& a, const automaton_t& b)
160  : a_(a), b_(b)
161  {
162  }
163 
164  bool
165  operator()()
166  {
167  if (fails_on_quick_tests())
168  return false;
169 
170  list_in_out_det_trans(a_);
171  list_in_out_det_trans(b_);
172 
173  if (!construct_tries())
174  return false;
175 
176  // Analyzes co-deterministic ingoing and outgoing transitions
177  //(obs: if the automata are deterministic, the isomorphism test
178  // ends here)
179  if (!analyse_det_trans())
180  return false;
181 
182  a_.shrink_unused();
183  b_.shrink_unused();
184 
185  return backtracking();
186  }
187 
188  private:
194  bool
195  fails_on_quick_tests()
196  {
197  return a_.aut.states().size() != b_.aut.states().size()
198  || a_.aut.transitions().size() != b_.aut.transitions().size()
199  || a_.aut.initial().size() != b_.aut.initial().size()
200  || a_.aut.final().size() != b_.aut.final().size();
201  }
202 
203 
209  void
210  list_in_out_det_trans(automaton_vars& av)
211  {
212  for (int i = 0; i < static_cast<int>(av.aut.states().size()); i++)
213  {
214  if (av.s.delta_in[i].size() > 0)
215  list_det_trans(av, av.delta_det_in, av.s.delta_in, i);
216 
217  if (av.s.delta_out[i].size() > 0)
218  list_det_trans(av, av.delta_det_out, av.s.delta_out, i);
219  }
220  }
221 
222 
230  bool
231  construct_tries()
232  {
233  // Constructs Tries for Automaton A.
234  for (int i = 0; i < static_cast<int>(a_.aut.states().size()); i++)
235  {
236  Trie *T_aux = add_all_transitions_to_trie(a_, i);
237 
238  // New class?
239  if (T_aux->A.size() == 0)
240  {
241  // Inserts in list C of classes (nodes of Tries)
242  C_.push_front(T_aux);
243 
244  // Each Trie node points to its node in list C
245  T_aux->L = C_.begin();
246  }
247 
248  // Inserts the state in the list A of its corresponding class
249  T_aux->A.push_front(i);
250  // Defines class of state i
251  a_.class_state[i] = T_aux;
252  // T_L_A[i] = node of the list of states of class of state i
253  a_.T_L[i] = T_aux->A.begin();
254  }
255 
256 
257  // Constructs Tries for automaton B.
258  for (int i = 0; i < static_cast<int>(b_.aut.states().size()); i++)
259  {
260  Trie *T_aux = add_all_transitions_to_trie(b_, i);
261 
262  // Does the class of state i have more states for automaton B
263  // than those for automaton A ?
264  if (T_aux->A.size() == T_aux->B.size())
265  return false;
266 
267  // Inserts the state in the list B of its corresponding class
268  T_aux->B.push_front(i);
269  // Defines class of state i
270  b_.class_state[i] = T_aux;
271  // T_L_B[i] = node of the list of states of class of state i
272  b_.T_L[i] = T_aux->B.begin();
273  }
274 
275  return true;
276  }
277 
278 
289  bool
290  construct_list_of_classes_with_one_state(std::list<int>& U)
291  {
292  std::list<Trie*>::iterator itr_C = C_.begin();
293 
294  while (itr_C != C_.end())
295  {
296  // Do automata A and B have the same number of states in the
297  // current class?
298  if ((*itr_C)->A.size() != (*itr_C)->B.size())
299  return false;
300 
301  // Class *itr_C contains only one state of each automata?
302  if ((*itr_C)->A.size() == 1)
303  {
304  // States *((*itr_C).A.begin()) and
305  // *((*itr_C).B.begin()) have to be identified.
306  int k = *((*itr_C)->A.begin());
307  U.push_front(k);
308  int l = *((*itr_C)->B.begin());
309  a_.perm[k] = l;
310  b_.perm[l] = k;
311 
312  // Just for coherence, lists A and B of class *itr_C are voided
313  ((*itr_C)->A).erase((*itr_C)->A.begin());
314  ((*itr_C)->B).erase((*itr_C)->B.begin());
315  // Deletes current node and points to the next.
316  itr_C = C_.erase(itr_C);
317  }
318  else
319  itr_C++;
320  }
321  return true;
322  }
323 
333  void
334  list_det_trans(automaton_vars& av,
335  delta_t& delta_det,
336  const delta_t& delta_in_or_out,
337  int i)
338  {
339  state_list_t::const_iterator it = delta_in_or_out[i].begin();
340  // Number of transitions with the same label
341  int j = 1;
342 
343  for (it++; it != delta_in_or_out[i].end(); it++)
344  {
345  // It seems there is no iterator arithmetics in C++:
346  // *(it_int - 1) doesn't compile.
347  int k = *(--it);
348  it++;
349  // New label?
350  if (av.s.transitions_labels[*it] != av.s.transitions_labels[k])
351  // The last transition is deterministic?
352  if (j == 1)
353  delta_det[i].push_back(k);
354  // Several transitions with the same label?
355  else
356  // Does nothing. A series of transitions with a new label begins.
357  j = 1;
358  // Same label?
359  else
360  j++;
361  }
362  // The last label visited is deterministic?
363  if (j == 1)
364  {
365  delta_det[i].push_back(*(--it));
366  it++;
367  }
368  }
369 
377  Trie *
378  add_all_transitions_to_trie(const automaton_vars& av,
379  int i)
380  {
381  // Vector all_transitions_lex contains the sequence of labels of
382  // ingoing and outgoing transitions of state i in lex. order,
383  // separated by a mark (-1)
384  trans_t all_transitions_lex(av.s.delta_in[i].size() +
385  av.s.delta_out[i].size() + 1);
386 
387  // First stores the sequence of ingoing transitions
388  for (state_list_t::const_iterator it = av.s.delta_in[i].begin();
389  it != av.s.delta_in[i].end(); ++it)
390  all_transitions_lex.push_back(av.s.transitions_labels[*it]);
391 
392  all_transitions_lex.push_back(-1);
393 
394  // Next, outgoing transitions
395  for (state_list_t::const_iterator it = av.s.delta_out[i].begin();
396  it != av.s.delta_out[i].end(); ++it)
397  all_transitions_lex.push_back(av.s.transitions_labels[*it]);
398 
399  // Gets the node of the correct Trie (Trie of initial, final or normal
400  // states) for the sequence of transitions of state i
401  if (av.aut.is_initial(av.s.states[i]))
402  return T_I_.insert(all_transitions_lex);
403  if (av.aut.is_final(av.s.states[i]))
404  return T_T_.insert(all_transitions_lex);
405 
406  return T_Q_IT_.insert(all_transitions_lex);
407  }
408 
419  bool
420  analyse_det_trans()
421  {
422  state_list_t U;
423 
424  if (!construct_list_of_classes_with_one_state(U))
425  return false;
426 
427  for (; !U.empty(); U.pop_front())
428  if (!do_analyse_det_trans(U,
429  a_.delta_det_in,
430  b_.delta_det_in,
431  a_.s.src_transitions,
432  b_.s.src_transitions)
433  || !do_analyse_det_trans(U,
434  a_.delta_det_out,
435  b_.delta_det_out,
436  a_.s.dst_transitions,
437  b_.s.dst_transitions))
438  return false;
439 
440  return true;
441  }
442 
443 
444  bool
445  do_analyse_det_trans(std::list<int>& U,
446  const delta_t& delta_det_A,
447  const delta_t& delta_det_B,
448  const trans_t& transitions_A,
449  const trans_t& transitions_B)
450  {
451  // Each state in U has already an image (perm_A and perm_B are
452  // defined for this state)
453 
454  // i = current state in automaton A, j = its image in automaton B
455  int i = U.front();
456  int j = a_.perm[i];
457 
458  // As states i and j are already associated, they belong to
459  // the same class, then have the same sequence of
460  // deterministic transitions.
461  state_list_t::const_iterator it = delta_det_A[i].begin();
462  state_list_t::const_iterator it_aux = delta_det_B[j].begin();
463  for (; it != delta_det_A[i].end(); ++it, ++it_aux)
464  {
465 
466  // The states being considered are the sources of current
467  // co-deterministic transitions (k for automaton A, l for B)
468  int k = transitions_A[*it];
469  int l = transitions_B[*it_aux];
470 
471  // Has state k already been visited?
472  if (a_.perm[k] >= 0)
473  {
474  // If it has already been visited, does the current image
475  // matches state l?
476  if (a_.perm[k] != l)
477  return false;
478  }
479  else
480  {
481  // State k has not already been visited. The same must be
482  // true for state l.
483  if (b_.perm[l] != -1)
484  return false;
485  // Tries to associate states k and l
486 
487  // Does k and l belongs to different classes?
488  if (a_.class_state[k] != b_.class_state[l])
489  return false;
490  // The states k and l belong to the same class and can be
491  // associated.
492  a_.perm[b_.perm[l] = k] = l;
493  // Removes k and l from theirs lists of states in theirs
494  // classes (O(1))
495  a_.class_state[k]->A.erase(a_.T_L[k]);
496  b_.class_state[l]->B.erase(b_.T_L[l]);
497  U.push_front(k);
498  if (a_.class_state[k]->A.size() == 1)
499  {
500  // If it remains only one state of each
501  // automaton in the class of the current states,
502  // they must be associated. Then they are
503  // putted in list U, and the class are removed
504  // from C.
505  // From now on k and l represent these states.
506  Trie* T_aux = a_.class_state[k];
507  k = T_aux->A.front();
508  l = T_aux->B.front();
509  a_.perm[b_.perm[l] = k] = l;
510 
511  U.push_front(k);
512 
513  // Removes class T_aux from C
514  C_.erase(T_aux->L);
515  }
516  }
517  }
518  return true;
519  }
520 
521 
526  bool
527  backtracking()
528  {
529  // Stores in l the number of non-fixed states
530  int l = 0;
531  for (std::list<Trie*>::iterator it = C_.begin();
532  it != C_.end(); ++it)
533  l += (*it)->A.size();
534 
535  // Vectors of classes of remaining states. States in the same
536  // class are stored contiguously, and in the case of vector for
537  // automaton B, classes are separated by two enTries: one with -1,
538  // denoting the end of the class, and the following with the
539  // position where the class begins in this vector.
540  trans_t C_A(l);
541  trans_t C_B(l + 2 * C_.size());
542  // current[i] = position in C_B of image of state C_A[i] during
543  // backtracking.
544  trans_t current(l);
545 
546 
547  // Vector for test of correspondence of transitions of states already
548  // attributed
549  trans_t correspondence_transitions(a_.aut.states().size(), 0);
550 
551  list_remaining(C_A, C_B, current);
552 
553  // Tries to associate states of C_A and C_B
554 
555  int i = 0;
556  bool rvalue = true;
557 
558  // Backtracking loop. Each iteration Tries to associate state
559  // C_A[i]. If i = C_A.size(), the automata are isomorphic.
560  while (i < static_cast<int>(C_A.size()))
561  {
562  int j;
563  // Searches for the first free state in the class of C_A[i]
564  for (j = current[i]; C_B[j] != -1 && b_.perm[C_B[j]] >= 0; j++)
565  continue;
566 
567  // There is no possibility for state C_A[i]
568  if (C_B[j] == -1)
569  {
570  // If there is no possibility for C_A[0], the automata are not
571  // isomorphic
572  if (i == 0)
573  return false;
574  else
575  {
576  // Prepares for backtracking in next iteration.
577  // Image of C_A[i] is set to first state of its class
578  current[i] = C_B[j + 1];
579  // Next iteration will try to associate state i - 1
580  // to next possible position
581  i--;
582  b_.perm[a_.perm[C_A[i]]] = -1;
583  a_.perm[C_A[i]] = -1;
584  current[i]++;
585  }
586  }
587  // Tries to associate state C_A[i] to state C_B[j]
588  else
589  {
590  current[i] = j;
591  a_.perm[C_A[i]] = C_B[j];
592  b_.perm[C_B[j]] = C_A[i];
593 
594  rvalue = (test_correspondence(a_.s.delta_in, b_.s.delta_in,
595  a_.s.src_transitions,
596  b_.s.src_transitions,
597  C_A, C_B, i, j,
598  correspondence_transitions)
599  && test_correspondence(a_.s.delta_out, b_.s.delta_out,
600  a_.s.dst_transitions,
601  b_.s.dst_transitions,
602  C_A, C_B, i, j,
603  correspondence_transitions));
604 
605  // States C_A[i] and C_B[j] can be associated.
606  if (rvalue)
607  // Tries to associate state in C_A[i + 1] in next iteration
608  i++;
609  // Impossible to associate C_A[i] to C_B[j]
610  else
611  {
612  // Tries C_A[i] to C_B[j + 1] in next iteration
613  b_.perm[a_.perm[C_A[i]]] = -1;
614  a_.perm[C_A[i]] = -1;
615  current[i]++;
616  }
617  }
618  }
619  return rvalue;
620  }
621 
627  void
628  list_remaining(trans_t& C_A,
629  trans_t& C_B,
630  trans_t& current)
631  {
632  int i = 0, j = 0;
633  for (std::list<Trie*>::iterator it = C_.begin(); it != C_.end();
634  it++)
635  {
636  state_list_t::iterator it_A = (*it)->A.begin();
637  state_list_t::iterator it_B = (*it)->B.begin();
638  C_A[i] = *it_A;
639  C_B[j] = *it_B;
640  current[i++] = j++;
641  for (it_A++, it_B++; it_A != (*it)->A.end();
642  it_A++, it_B++)
643  {
644  C_A[i] = *it_A;
645  C_B[j++] = *it_B;
646  current[i] = current[i - 1];
647  i++;
648  }
649  // End of class
650  C_B[j++] = -1;
651  // Position where the class begins
652  C_B[j++] = current[i - 1];
653  }
654  }
655 
660  bool
661  test_correspondence(const delta_t& delta_A,
662  const delta_t& delta_B,
663  const trans_t& transitions_A,
664  const trans_t& transitions_B,
665  trans_t& C_A,
666  trans_t& C_B,
667  int i,
668  int j,
669  trans_t& correspondence_transitions)
670  {
671  state_list_t::const_iterator it_A = delta_A[C_A[i]].begin();
672  state_list_t::const_iterator it_B = delta_B[C_B[j]].begin();
673 
674  // Each iteration considers a sequence of ingoing labels
675  // with the same label. The sequence begins at it_int
676  while (it_A != delta_A[C_A[i]].end())
677  {
678  // Searches for sources of ingoing transitions for current
679  // label that have already been associated. For each state
680  // visited, its position in correspondence_transitions is
681  // incremented.
682  int k = 0;
683  state_list_t::const_iterator it_aux;
684  for (it_aux = it_A;
685  (it_aux != delta_A[C_A[i]].end()) &&
686  (a_.s.transitions_labels[*it_aux] ==
687  a_.s.transitions_labels[*it_A]);
688  it_aux++, k++)
689  // Is the source of current transition associated?
690  if (a_.perm[transitions_A[*it_aux]] >= 0)
691  correspondence_transitions[transitions_A[*it_aux]]++;
692 
693  // Here, k = number of ingoing transitions for current label
694 
695  // Idem for ingoing transitions of state C_B[j], but positions in
696  // correspondence_transitions are decremented.
697  for (; (k > 0); it_B++, k--)
698  // Has the source of current transition already been visited?
699  if (b_.perm[transitions_B[*it_B]] >= 0)
700  {
701  // Trying to decrement a position with 0 means that the
702  // corresponding state in A is not correct.
703  if (correspondence_transitions[b_.perm[transitions_B[*it_B]]]
704  == 0)
705  // The association of C_A[i] and C_B[j] is impossible
706  return false;
707  else
708  correspondence_transitions[b_.perm[transitions_B[*it_B]]]--;
709  }
710 
711  // Verifies correspondence_transitions. The correspondence for
712  // current label is correct iff correspondence_transitions[l] = 0
713  // for all src l of ingoing transitions of C_A[i] labelled by
714  // the current label.
715  // For this, int_itr visits all transitions until int_itr_aux.
716 
717  for (; it_A != it_aux; it_A++)
718  {
719  if (a_.perm[transitions_A[*it_A]] >= 0)
720  if (correspondence_transitions[transitions_A[*it_A]] != 0)
721  return false;
722  // All positions must be 0 for next iteration
723  correspondence_transitions[transitions_A[*it_A]] = 0;
724  }
725 
726  }
727  return true;
728  }
729 
730 
731 
732  //Private Attributes
733  automaton_vars a_;
734  automaton_vars b_;
735  std::list<Trie*> C_;
736  // Tries of classes of normal, initial and final states.
737  Trie T_Q_IT_;
738  Trie T_I_;
739  Trie T_T_;
740  };
741 
742 
743  /*---------------.
744  | are_isomorphic |
745  `---------------*/
746 
747  template<typename A, typename T>
748  bool
750  {
751  BENCH_TASK_SCOPED("are_isomorphic");
752 
753  Isomorpher<A, T> isomorpher(a, b);
754 
755  return isomorpher();
756  }
757 
758 } // vcsn
759 
760 #endif // ! VCSN_ALGORITHMS_ISOMORPH_HXX