isomorph.hxx

00001 // isomorph.hxx: this file is part of the Vaucanson project.
00002 //
00003 // Vaucanson, a generic library for finite state machines.
00004 //
00005 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The Vaucanson Group.
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
00011 //
00012 // The complete GNU General Public Licence Notice can be found as the
00013 // `COPYING' file in the root directory.
00014 //
00015 // The Vaucanson Group consists of people listed in the `AUTHORS' file.
00016 //
00017 #ifndef VCSN_ALGORITHMS_ISOMORPH_HXX
00018 # define VCSN_ALGORITHMS_ISOMORPH_HXX
00019 
00020 # include <vaucanson/algorithms/isomorph.hh>
00021 # include <vaucanson/algorithms/determinize.hh>
00022 
00023 # include <vaucanson/automata/concept/automata_base.hh>
00024 # include <vaucanson/misc/usual_macros.hh>
00025 
00026 # include <vaucanson/algorithms/internal/skeleton.hh>
00027 
00028 # include <queue>
00029 # include <set>
00030 # include <list>
00031 # include <utility>
00032 
00033 namespace vcsn
00034 {
00035 
00036   /*--------------------------------------.
00037   | Helper for are_isomorphic algorithm.  |
00038   `--------------------------------------*/
00039 
00040   class Trie
00041   {
00042     public:
00043       Trie();
00045       Trie(const Trie& t);
00046 
00047       Trie* insert(std::vector<int>&);
00048 
00049     public:
00050       std::list<int> A, B;
00051 
00052       // Node in list C pointing to this node
00053       std::list<Trie*>::iterator L;
00054 
00055     private:
00057       bool L_is_valid;
00058       Trie* insert_suffix(std::vector<int>&, unsigned int);
00059       std::map<int, Trie> children;
00060   };
00061 
00062   inline
00063   Trie::Trie ()
00064     : A(), B(), L(), L_is_valid(false), children()
00065   {
00066   }
00067 
00068   inline
00069   Trie::Trie (const Trie& t)
00070     : A(t.A), B(t.B),
00071       L(), L_is_valid(t.L_is_valid), children(t.children)
00072   {
00073     if (L_is_valid)
00074       L = t.L;
00075   }
00076 
00077 
00078   // Find node associated to the insertion of a vector of integers.
00079   inline Trie* Trie::insert(std::vector<int>& S)
00080   {
00081     return insert_suffix(S, 0);
00082   }
00083 
00084   inline Trie* Trie::insert_suffix(std::vector<int>& S, unsigned int p)
00085   {
00086     if (p == S.capacity() - 1)
00087       return this;
00088 
00089     return children[S[p]].insert_suffix(S, p + 1);
00090   }
00091 
00092 
00093 
00094   /*--------------------------------------.
00095   | Functor for are_isomorphic algorithm. |
00096   `--------------------------------------*/
00097 
00098   template<typename A, typename T>
00099   class Isomorpher
00100   {
00101     public:
00102 
00103       typedef Element<A, T> auto_t;
00104       AUTOMATON_TYPES(auto_t);
00105 
00106       typedef std::vector<int> trans_t;
00107       typedef std::list<int> state_list_t;
00108       typedef std::vector<state_list_t> delta_t;
00109 
00110       struct automaton_vars
00111       {
00112           // We have to build the vector T_L using a valid iterator (i.e., not a
00113           // singular one).  We use a temp int list to this end.
00114           automaton_vars(const automaton_t& aut)
00115             : s(aut),
00116               aut(aut),
00117               perm(aut.states().size(), -1),
00118               T_L(aut.states().size(), state_list_t().end()),
00119               delta_det_in(aut.states().size()),
00120               delta_det_out(aut.states().size()),
00121               class_state(aut.states().size())
00122           {
00123           }
00124 
00125           void
00126           shrink_unused()
00127           {
00128             T_L.clear();
00129             delta_det_in.clear();
00130             delta_det_out.clear();
00131             class_state.clear();
00132             class_state.clear();
00133           }
00134 
00135 
00136           Skeleton<A, T> s;
00137           const automaton_t aut;
00138 
00139           // perm[i] = state of another automaton corresponding to state i in
00140           // the isomorphism, or -1 when the association
00141           // is yet undefined.
00142           trans_t perm;
00143 
00144           // Vector indexed by states that points to the corresponding node
00145           // of the list of states of each class of states stored in the
00146           // Tries.
00147           std::vector<state_list_t::iterator> T_L;
00148 
00149           // Lists of deterministic ingoing and outgoing transitions
00150           // for each state (delta_det_in[i] = list of deterministic ingoing
00151           // transitions for state i, idem for delta_det_out[i])
00152           delta_t delta_det_in;
00153           delta_t delta_det_out;
00154 
00155           // class_state_A[i] = class (node of a Trie) of state i for automaton A
00156           std::vector<Trie*> class_state;
00157       };
00158 
00159       Isomorpher(const automaton_t& a, const automaton_t& b)
00160         : a_(a), b_(b)
00161       {
00162       }
00163 
00164       bool
00165       operator()()
00166       {
00167         if (fails_on_quick_tests())
00168           return false;
00169 
00170         list_in_out_det_trans(a_);
00171         list_in_out_det_trans(b_);
00172 
00173         if (!construct_tries())
00174           return false;
00175 
00176         // Analyzes co-deterministic ingoing and outgoing transitions
00177         //(obs: if the automata are deterministic, the isomorphism test
00178         // ends here)
00179         if (!analyse_det_trans())
00180           return false;
00181 
00182         a_.shrink_unused();
00183         b_.shrink_unused();
00184 
00185         return backtracking();
00186       }
00187 
00188     private:
00194       bool
00195       fails_on_quick_tests()
00196       {
00197         return a_.aut.states().size() != b_.aut.states().size()
00198           || a_.aut.transitions().size() != b_.aut.transitions().size()
00199           || a_.aut.initial().size() != b_.aut.initial().size()
00200           || a_.aut.final().size() != b_.aut.final().size();
00201       }
00202 
00203 
00209       void
00210       list_in_out_det_trans(automaton_vars& av)
00211       {
00212         for (int i = 0; i < static_cast<int>(av.aut.states().size()); i++)
00213         {
00214           if (av.s.delta_in[i].size() > 0)
00215             list_det_trans(av, av.delta_det_in, av.s.delta_in, i);
00216 
00217           if (av.s.delta_out[i].size() > 0)
00218             list_det_trans(av, av.delta_det_out, av.s.delta_out, i);
00219         }
00220       }
00221 
00222 
00230       bool
00231       construct_tries()
00232       {
00233         // Constructs Tries for Automaton A.
00234         for (int i = 0; i < static_cast<int>(a_.aut.states().size()); i++)
00235         {
00236           Trie *T_aux = add_all_transitions_to_trie(a_, i);
00237 
00238           // New class?
00239           if (T_aux->A.size() == 0)
00240           {
00241             // Inserts in list C of classes (nodes of Tries)
00242             C_.push_front(T_aux);
00243 
00244             // Each Trie node points to its node in list C
00245             T_aux->L = C_.begin();
00246           }
00247 
00248           // Inserts the state in the list A of its corresponding class
00249           T_aux->A.push_front(i);
00250           // Defines class of state i
00251           a_.class_state[i] = T_aux;
00252           // T_L_A[i] = node of the list of states of class of state i
00253           a_.T_L[i] = T_aux->A.begin();
00254         }
00255 
00256 
00257         // Constructs Tries for automaton B.
00258         for (int i = 0; i < static_cast<int>(b_.aut.states().size()); i++)
00259         {
00260           Trie *T_aux = add_all_transitions_to_trie(b_, i);
00261 
00262           // Does the class of state i have more states for automaton B
00263           // than those for automaton A ?
00264           if (T_aux->A.size() == T_aux->B.size())
00265             return false;
00266 
00267           // Inserts the state in the list B of its corresponding class
00268           T_aux->B.push_front(i);
00269           // Defines class of state i
00270           b_.class_state[i] = T_aux;
00271           // T_L_B[i] = node of the list of states of class of state i
00272           b_.T_L[i] = T_aux->B.begin();
00273         }
00274 
00275         return true;
00276       }
00277 
00278 
00289       bool
00290       construct_list_of_classes_with_one_state(std::list<int>& U)
00291       {
00292         std::list<Trie*>::iterator itr_C = C_.begin();
00293 
00294         while (itr_C != C_.end())
00295         {
00296           // Do automata A and B have the same number of states in the
00297           // current class?
00298           if ((*itr_C)->A.size() != (*itr_C)->B.size())
00299             return false;
00300 
00301           // Class *itr_C contains only one state of each automata?
00302           if ((*itr_C)->A.size() == 1)
00303           {
00304             // States *((*itr_C).A.begin()) and
00305             // *((*itr_C).B.begin()) have to be identified.
00306             int k = *((*itr_C)->A.begin());
00307             U.push_front(k);
00308             int l = *((*itr_C)->B.begin());
00309             a_.perm[k] = l;
00310             b_.perm[l] = k;
00311 
00312             // Just for coherence, lists A and B of class *itr_C are voided
00313             ((*itr_C)->A).erase((*itr_C)->A.begin());
00314             ((*itr_C)->B).erase((*itr_C)->B.begin());
00315             // Deletes current node and points to the next.
00316             itr_C = C_.erase(itr_C);
00317           }
00318           else
00319             itr_C++;
00320         }
00321         return true;
00322       }
00323 
00333       void
00334       list_det_trans(automaton_vars& av,
00335                      delta_t& delta_det,
00336                      const delta_t& delta_in_or_out,
00337                      int i)
00338       {
00339         state_list_t::const_iterator it = delta_in_or_out[i].begin();
00340         // Number of transitions with the same label
00341         int j = 1;
00342 
00343         for (it++; it != delta_in_or_out[i].end(); it++)
00344         {
00345           // It seems there is no iterator arithmetics in C++:
00346           // *(it_int - 1) doesn't compile.
00347           int k = *(--it);
00348           it++;
00349           // New label?
00350           if (av.s.transitions_labels[*it] != av.s.transitions_labels[k])
00351             // The last transition is deterministic?
00352             if (j == 1)
00353               delta_det[i].push_back(k);
00354           // Several transitions with the same label?
00355             else
00356               // Does nothing. A series of transitions with a new label begins.
00357               j = 1;
00358           // Same label?
00359           else
00360             j++;
00361         }
00362         // The last label visited is deterministic?
00363         if (j == 1)
00364         {
00365           delta_det[i].push_back(*(--it));
00366           it++;
00367         }
00368       }
00369 
00377       Trie *
00378       add_all_transitions_to_trie(const automaton_vars& av,
00379                                   int i)
00380       {
00381         // Vector all_transitions_lex contains the sequence of labels of
00382         // ingoing and outgoing transitions of state i in lex. order,
00383         // separated by a mark (-1)
00384         trans_t all_transitions_lex(av.s.delta_in[i].size() +
00385                                     av.s.delta_out[i].size() + 1);
00386 
00387         // First stores the sequence of ingoing transitions
00388         for (state_list_t::const_iterator it = av.s.delta_in[i].begin();
00389              it != av.s.delta_in[i].end(); ++it)
00390           all_transitions_lex.push_back(av.s.transitions_labels[*it]);
00391 
00392         all_transitions_lex.push_back(-1);
00393 
00394         // Next, outgoing transitions
00395         for (state_list_t::const_iterator it = av.s.delta_out[i].begin();
00396              it != av.s.delta_out[i].end(); ++it)
00397           all_transitions_lex.push_back(av.s.transitions_labels[*it]);
00398 
00399         // Gets the node of the correct Trie (Trie of initial, final or normal
00400         // states) for the sequence of transitions of state i
00401         if (av.aut.is_initial(av.s.states[i]))
00402           return T_I_.insert(all_transitions_lex);
00403         if (av.aut.is_final(av.s.states[i]))
00404           return T_T_.insert(all_transitions_lex);
00405 
00406         return T_Q_IT_.insert(all_transitions_lex);
00407       }
00408 
00419       bool
00420       analyse_det_trans()
00421       {
00422         state_list_t U;
00423 
00424         if (!construct_list_of_classes_with_one_state(U))
00425           return false;
00426 
00427         for (; !U.empty(); U.pop_front())
00428           if (!do_analyse_det_trans(U,
00429                                     a_.delta_det_in,
00430                                     b_.delta_det_in,
00431                                     a_.s.src_transitions,
00432                                     b_.s.src_transitions)
00433               || !do_analyse_det_trans(U,
00434                                        a_.delta_det_out,
00435                                        b_.delta_det_out,
00436                                        a_.s.dst_transitions,
00437                                        b_.s.dst_transitions))
00438             return false;
00439 
00440         return true;
00441       }
00442 
00443 
00444       bool
00445       do_analyse_det_trans(std::list<int>& U,
00446                            const delta_t& delta_det_A,
00447                            const delta_t& delta_det_B,
00448                            const trans_t& transitions_A,
00449                            const trans_t& transitions_B)
00450       {
00451         // Each state in U has already an image (perm_A and perm_B are
00452         // defined for this state)
00453 
00454         // i = current state in automaton A, j = its image in automaton B
00455         int i = U.front();
00456         int j = a_.perm[i];
00457 
00458         // As states i and j are already associated, they belong to
00459         // the same class, then have the same sequence of
00460         // deterministic transitions.
00461         state_list_t::const_iterator it = delta_det_A[i].begin();
00462         state_list_t::const_iterator it_aux = delta_det_B[j].begin();
00463         for (; it != delta_det_A[i].end(); ++it, ++it_aux)
00464         {
00465 
00466           // The states being considered are the sources of current
00467           // co-deterministic transitions (k for automaton A, l for B)
00468           int k = transitions_A[*it];
00469           int l = transitions_B[*it_aux];
00470 
00471           // Has state k already been visited?
00472           if (a_.perm[k] >= 0)
00473           {
00474             // If it has already been visited, does the current image
00475             // matches state l?
00476             if (a_.perm[k] != l)
00477               return false;
00478           }
00479           else
00480           {
00481             // State k has not already been visited. The same must be
00482             // true for state l.
00483             if (b_.perm[l] != -1)
00484               return false;
00485             // Tries to associate states k and l
00486 
00487             // Does k and l belongs to different classes?
00488             if (a_.class_state[k] != b_.class_state[l])
00489               return false;
00490             // The states k and l belong to the same class and can be
00491             // associated.
00492             a_.perm[b_.perm[l] = k] = l;
00493             // Removes k and l from theirs lists of states in theirs
00494             // classes (O(1))
00495             a_.class_state[k]->A.erase(a_.T_L[k]);
00496             b_.class_state[l]->B.erase(b_.T_L[l]);
00497             U.push_front(k);
00498             if (a_.class_state[k]->A.size() == 1)
00499             {
00500               // If it remains only one state of each
00501               // automaton in the class of the current states,
00502               // they must be associated. Then they are
00503               // putted in list U, and the class are removed
00504               // from C.
00505               // From now on k and l represent these states.
00506               Trie* T_aux = a_.class_state[k];
00507               k = T_aux->A.front();
00508               l = T_aux->B.front();
00509               a_.perm[b_.perm[l] = k] = l;
00510 
00511               U.push_front(k);
00512 
00513               // Removes class T_aux from C
00514               C_.erase(T_aux->L);
00515             }
00516           }
00517         }
00518         return true;
00519       }
00520 
00521 
00526       bool
00527       backtracking()
00528       {
00529         // Stores in l the number of non-fixed states
00530         int l = 0;
00531         for (std::list<Trie*>::iterator it = C_.begin();
00532              it != C_.end(); ++it)
00533           l += (*it)->A.size();
00534 
00535         // Vectors of classes of remaining states.      States in the same
00536         // class are stored contiguously, and in the case of vector for
00537         // automaton B, classes are separated by two enTries: one with -1,
00538         // denoting the end of the class, and the following with the
00539         // position where the class begins in this vector.
00540         trans_t C_A(l);
00541         trans_t C_B(l + 2 * C_.size());
00542         // current[i] = position in C_B of image of state C_A[i] during
00543         // backtracking.
00544         trans_t current(l);
00545 
00546 
00547         // Vector for test of correspondence of transitions of states already
00548         // attributed
00549         trans_t correspondence_transitions(a_.aut.states().size(), 0);
00550 
00551         list_remaining(C_A, C_B, current);
00552 
00553         // Tries to associate states of C_A and C_B
00554 
00555         int i = 0;
00556         bool rvalue = true;
00557 
00558         // Backtracking loop. Each iteration Tries to associate state
00559         // C_A[i]. If i = C_A.size(), the automata are isomorphic.
00560         while (i < static_cast<int>(C_A.size()))
00561         {
00562           int j;
00563           // Searches for the first free state in the class of C_A[i]
00564           for (j = current[i]; C_B[j] != -1 && b_.perm[C_B[j]] >= 0; j++)
00565             continue;
00566 
00567           // There is no possibility for state C_A[i]
00568           if (C_B[j] == -1)
00569           {
00570             // If there is no possibility for C_A[0], the automata are not
00571             // isomorphic
00572             if (i == 0)
00573               return false;
00574             else
00575             {
00576               // Prepares for backtracking in next iteration.
00577               // Image of C_A[i] is set to first state of its class
00578               current[i] = C_B[j + 1];
00579               // Next iteration will try to associate state i - 1
00580               // to next possible position
00581               i--;
00582               b_.perm[a_.perm[C_A[i]]] = -1;
00583               a_.perm[C_A[i]] = -1;
00584               current[i]++;
00585             }
00586           }
00587           // Tries to associate state C_A[i] to state C_B[j]
00588           else
00589           {
00590             current[i] = j;
00591             a_.perm[C_A[i]] = C_B[j];
00592             b_.perm[C_B[j]] = C_A[i];
00593 
00594             rvalue = (test_correspondence(a_.s.delta_in, b_.s.delta_in,
00595                                           a_.s.src_transitions,
00596                                           b_.s.src_transitions,
00597                                           C_A, C_B, i, j,
00598                                           correspondence_transitions)
00599                       && test_correspondence(a_.s.delta_out, b_.s.delta_out,
00600                                              a_.s.dst_transitions,
00601                                              b_.s.dst_transitions,
00602                                              C_A, C_B, i, j,
00603                                              correspondence_transitions));
00604 
00605             // States C_A[i] and C_B[j] can be associated.
00606             if (rvalue)
00607               // Tries to associate state in C_A[i + 1] in next iteration
00608               i++;
00609             // Impossible to associate C_A[i] to C_B[j]
00610             else
00611             {
00612               // Tries C_A[i] to C_B[j + 1] in next iteration
00613               b_.perm[a_.perm[C_A[i]]] = -1;
00614               a_.perm[C_A[i]] = -1;
00615               current[i]++;
00616             }
00617           }
00618         }
00619         return rvalue;
00620       }
00621 
00627       void
00628       list_remaining(trans_t& C_A,
00629                      trans_t& C_B,
00630                      trans_t& current)
00631       {
00632         int i = 0, j = 0;
00633         for (std::list<Trie*>::iterator it = C_.begin(); it != C_.end();
00634              it++)
00635         {
00636           state_list_t::iterator it_A = (*it)->A.begin();
00637           state_list_t::iterator it_B = (*it)->B.begin();
00638           C_A[i] = *it_A;
00639           C_B[j] = *it_B;
00640           current[i++] = j++;
00641           for (it_A++, it_B++; it_A != (*it)->A.end();
00642                it_A++, it_B++)
00643           {
00644             C_A[i] = *it_A;
00645             C_B[j++] = *it_B;
00646             current[i] = current[i - 1];
00647             i++;
00648           }
00649           // End of class
00650           C_B[j++] = -1;
00651           // Position where the class begins
00652           C_B[j++] = current[i - 1];
00653         }
00654       }
00655 
00660       bool
00661       test_correspondence(const delta_t& delta_A,
00662                           const delta_t& delta_B,
00663                           const trans_t& transitions_A,
00664                           const trans_t& transitions_B,
00665                           trans_t& C_A,
00666                           trans_t& C_B,
00667                           int i,
00668                           int j,
00669                           trans_t& correspondence_transitions)
00670       {
00671         state_list_t::const_iterator it_A = delta_A[C_A[i]].begin();
00672         state_list_t::const_iterator it_B = delta_B[C_B[j]].begin();
00673 
00674         // Each iteration considers a sequence of ingoing labels
00675         // with the same label. The sequence begins at it_int
00676         while (it_A != delta_A[C_A[i]].end())
00677         {
00678           // Searches for sources of ingoing transitions for current
00679           // label that have already been associated. For each state
00680           // visited, its position in correspondence_transitions is
00681           // incremented.
00682           int k = 0;
00683           state_list_t::const_iterator it_aux;
00684           for (it_aux = it_A;
00685                (it_aux != delta_A[C_A[i]].end()) &&
00686                  (a_.s.transitions_labels[*it_aux] ==
00687                   a_.s.transitions_labels[*it_A]);
00688                it_aux++, k++)
00689             // Is the source of current transition associated?
00690             if (a_.perm[transitions_A[*it_aux]] >= 0)
00691               correspondence_transitions[transitions_A[*it_aux]]++;
00692 
00693           // Here, k = number of ingoing transitions for current label
00694 
00695           // Idem for ingoing transitions of state C_B[j], but positions in
00696           // correspondence_transitions are decremented.
00697           for (; (k > 0); it_B++, k--)
00698             // Has the source of current transition already been visited?
00699             if (b_.perm[transitions_B[*it_B]] >= 0)
00700               // Trying to decrement a position with 0 means that the
00701               // corresponding state in A is not correct.
00702               if (correspondence_transitions[b_.perm[transitions_B[*it_B]]] == 0)
00703                 // The association of C_A[i] and C_B[j] is impossible
00704                 return false;
00705               else
00706                 correspondence_transitions[b_.perm[transitions_B[*it_B]]]--;
00707 
00708           // Verifies correspondence_transitions. The correspondence for
00709           // current label is correct iff correspondence_transitions[l] = 0
00710           // for all src l of ingoing transitions of C_A[i] labelled by
00711           // the current label.
00712           // For this, int_itr visits all transitions until int_itr_aux.
00713 
00714           for (; it_A != it_aux; it_A++)
00715           {
00716             if (a_.perm[transitions_A[*it_A]] >= 0)
00717               if (correspondence_transitions[transitions_A[*it_A]] != 0)
00718                 return false;
00719             // All positions must be 0 for next iteration
00720             correspondence_transitions[transitions_A[*it_A]] = 0;
00721           }
00722 
00723         }
00724         return true;
00725       }
00726 
00727 
00728 
00729       //Private Attributes
00730       automaton_vars a_;
00731       automaton_vars b_;
00732       std::list<Trie*> C_;
00733       // Tries of classes of normal, initial and final states.
00734       Trie T_Q_IT_;
00735       Trie T_I_;
00736       Trie T_T_;
00737   };
00738 
00739 
00740   /*---------------.
00741   | are_isomorphic |
00742   `---------------*/
00743 
00744   template<typename A, typename T>
00745   bool
00746   are_isomorphic(const Element<A, T>& a, const Element<A, T>& b)
00747   {
00748     TIMER_SCOPED("are_isomorphic");
00749 
00750     Isomorpher<A, T> isomorpher(a, b);
00751 
00752     return isomorpher();
00753   }
00754 
00755 } // vcsn
00756 
00757 #endif // ! VCSN_ALGORITHMS_ISOMORPH_HXX

Generated on Wed Jun 13 17:00:22 2007 for Vaucanson by  doxygen 1.5.1