minimize.h

00001 /*
00002  * ASTL - the Automaton Standard Template Library.
00003  * C++ generic components for Finite State Automata handling.
00004  * Copyright (C) 2000-2003 Vincent Le Maout (vincent.lemaout@chello.fr).
00005  * 
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  * 
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #ifndef ASTL_MINIMIZE_H
00023 #define ASTL_MINIMIZE_H
00024 
00025 #include <astl.h>
00026 #include <determinize.h>
00027 
00028 #include <iostream>
00029 #ifndef __DFA_GLIMPSE_H_
00030 #include <vector>
00031 #endif
00032 #include <iterator>
00033 #include <algorithm>
00034 #include <map>
00035 #include <set>
00036 
00037 using namespace std;
00038 
00039 namespace astl {
00040 
00041 struct minimization_tag 
00042 {
00043   unsigned long _degree_in, _equivalent_to;
00044   unsigned char _height;
00045 
00046   minimization_tag() : _degree_in(0), _equivalent_to(0), _height(0) { }
00047   unsigned char height() const { return (_height); }
00048   void          height(unsigned char x) { _height = x; }
00049   unsigned long degree_in() const { return (_degree_in); }
00050   void          degree_in(unsigned long x) { _degree_in = x; }
00051   unsigned long equivalent_to() const { return (_equivalent_to); }
00052   void          equivalent_to(unsigned long x) { _equivalent_to = x; }
00053   friend bool operator == (const minimization_tag &x, const minimization_tag &y) {
00054     return (x.degree_in() == y.degree_in() && x.equivalent_to() == y.equivalent_to());
00055   }
00056   minimization_tag& operator=(const empty&) { return *this; }
00057 };
00058 
00059 ostream& operator << (ostream &out, const struct minimization_tag &t)
00060 {
00061   out << t.degree_in() << " " << t.equivalent_to();
00062   return (out);
00063 }
00064 
00065 // Computes the height of each state in A and
00066 // stores it in a matrix (height x state)
00067 // yields a partition of Q 
00068 // if q is in Q, height(q) = Max {height(p) such that (q, a, p) is in T}
00069 // and height(q) = 0 if q has no outgoing edges
00070 // Uses method Tag::degree_in(long) to store the number of ingoing edges of each state
00071 // Needs template functions swap and max 
00072 
00073 template <class DFA, class Dstate>
00074 unsigned char 
00075 height(DFA& A, Dstate q, vector<vector < Dstate> *> &partition)
00076 {
00077   const typename DFA::Edges edg = A.delta2(q);
00078   unsigned char max_height = 0;
00079   typename DFA::Edges::const_iterator q_trans, edg_end = edg.end();
00080   Dstate aim;
00081 
00082   for (q_trans = edg.begin(); q_trans != edg_end; ++q_trans)
00083     {
00084       aim = (*q_trans).second;
00085       A.tag(aim).degree_in(A.tag(aim).degree_in() + 1);
00086       if (A.tag(aim).degree_in()>1) //la hauteur est deja calculée
00087         max_height = max__(max_height,(unsigned char)(A.tag(aim).height()+1));       
00088       else 
00089         max_height = max__(max_height,(unsigned char)(height(A,aim,partition)+1));
00090     }
00091   if (partition.size() <= max_height)
00092     partition.push_back(new vector<Dstate>(1, q));
00093   else
00094     partition[max_height]->push_back(q);
00095   A.tag(q).height(max_height); 
00096   return (max_height);
00097 }
00098 
00099 // Set default transition of each state in [start, finish) toward
00100 // start state (start is the reference state of the partition)
00101 
00102 template <class DFA, class iterator>
00103 void 
00104 fusion(DFA &A, iterator start, iterator finish)
00105 {
00106   typedef typename DFA::state_type Dstate;
00107   typename vector<Dstate>::iterator where;
00108   Dstate reference = *start;
00109   
00110   for (where = start + 1; where != finish; ++where)
00111     A.tag(*where).equivalent_to((unsigned long) reference);  
00112 }
00113 
00114 // Redirect the outgoing edges of a partition based on height
00115 // to the partition reference state and decrease the number of
00116 // ingoing edges of aim. (delete aim if it has no ingoing edges any more) 
00117 template <class DFA, class State>
00118 void
00119 redirect_edges(DFA &A, vector<State> &v)
00120 {
00121   typename vector<State>::iterator where;
00122   for (where = v.begin(); where != v.end(); ++where)
00123     {
00124       const typename DFA::Edges edg = A.delta2(*where);
00125       typename DFA::Edges::const_iterator trans, edg_end = edg.end();
00126       typename DFA::state_type new_aim, aim;
00127       for (trans = edg.begin(); trans != edg_end; ++trans)
00128         {
00129           aim = (*trans).second;
00130           new_aim = (typename DFA::state_type) A.tag(aim).equivalent_to();
00131           if (new_aim != A.null_state)
00132             {
00133               A.change_trans(*where, (*trans).first, new_aim);
00134               A.tag(aim).degree_in(A.tag(aim).degree_in() - 1);
00135               if (A.tag(aim).degree_in() == 0)  // no more ingoing edges to the aim
00136                 A.del_state(aim);
00137             }
00138         }
00139     }
00140 }
00141 
00142 // Compares two states and returns int <0 or ==0 or >0
00143 template <class DFA, class State>
00144 int
00145 compare_states(DFA &A, State q, State p)
00146 {
00147   bool fq = A.final(q);
00148   bool fp = A.final(p);
00149   if (fq != fp)
00150     return ((int) fq - (int) fp);
00151 
00152   typename DFA::Edges::size_type q_trans_count = A.delta2(q).size();
00153   typename DFA::Edges::size_type p_trans_count = A.delta2(p).size();
00154   if (q_trans_count - p_trans_count != 0) 
00155     return (q_trans_count - p_trans_count);
00156   if (q_trans_count == 0) return (0);
00157 
00158   // Both states have the same finality and the same number of transitions
00159   // We apply a kind of lexicographical comparison
00160   typename DFA::Edges::const_iterator left_edg_end = A.delta2(q).end();
00161   typename DFA::Edges::const_iterator left = A.delta2(q).begin();
00162   typename DFA::Edges::const_iterator right = A.delta2(p).begin();
00163 
00164   for (; left != left_edg_end; ++left, ++right)
00165     {
00166       typedef typename DFA::char_type Dalph;
00167       typedef typename DFA::state_type Dstate;
00168       const pair<const Dalph, Dstate>& q_trans = *left;
00169       const pair<const Dalph, Dstate>& p_trans = *right;
00170     
00171       if (q_trans.first == p_trans.first) // Same letter
00172         {
00173           if (q_trans.second < p_trans.second)
00174             return (-1);
00175           else
00176             if (p_trans.second < q_trans.second)
00177               return (1);
00178         }
00179       else
00180         if (q_trans.first < p_trans.first)
00181           return (-1);
00182         else
00183           return (1);
00184     }
00185   return (0);
00186 }
00187   
00188 
00189 // Groups equivalent states and returns the range they are in
00190 template <class DFA, class Size_type, class State>
00191 void
00192 group_equivalent_states(DFA &A, vector<State> &v, Size_type start, 
00193                         Size_type finish, pair<Size_type, Size_type> &rang)
00194 {
00195   // Result is placed in 'range' argument
00196   Size_type g, d, pg;
00197   pg = g = start + 1;
00198   d = finish;
00199   State e = v[start];  // Reference state
00200   int res;
00201   
00202   while (g <= d) {
00203     res = compare_states(A, v[g], e);
00204     if (res == 0) {
00205       swap(v[pg],v[g]);
00206       pg++;
00207       g++;
00208     }
00209     else
00210       if (res < 0) g++;
00211       else {
00212         res = compare_states(A, v[d], e);
00213         if (res == 0)
00214           if(g == pg) {
00215             swap(v[pg],v[d]);
00216             pg++;
00217             g++;
00218           }
00219           else {  
00220             swap(v[pg],v[g]);
00221             swap(v[pg],v[d]);
00222             pg++;
00223           }
00224         else
00225           if(res > 0) d--;
00226           else {   
00227             swap(v[g],v[d]);
00228             g++;
00229           }
00230       }
00231   }
00232   
00233   rang.second = d;
00234   rang.first  = pg;
00235 }   
00236 
00237 // Refine the partition based on state height
00238 // All equivalent states are placed together in the vector then merged
00239 template <class DFA, class Size_type, class State>
00240 void 
00241 refine_partition(DFA &A, vector<State> &v,
00242                  Size_type start, Size_type finish)
00243 {
00244   if (start < finish)
00245     {
00246       pair<typename vector<State>::size_type, typename vector<State>::size_type> range;
00247   
00248       group_equivalent_states(A, v, start, finish, range);
00249       if (start < range.first - 1) fusion(A, v.begin() + start, v.begin() + range.first);
00250       if (range.first < range.second) refine_partition(A, v, range.first, range.second);
00251       if (range.second + 1 < finish) refine_partition(A, v, range.second + 1, finish);
00252     }
00253 }
00254 
00255 
00256 // Main minimization function
00257 template <class DFA>
00258 void 
00259 acyclic_minimization(DFA &dfa)
00260 {
00261   if (dfa.state_count() > 0) {
00262     typedef typename DFA::state_type Dstate;
00263     typedef vector<vector < Dstate> *> matrix;
00264     matrix partition(1, new vector< Dstate>); 
00265     height(dfa,dfa.initial(),partition);
00266     
00267     if (partition[0]->empty() == false) {
00268       typename matrix::iterator where;
00269       for (where = partition.begin(); where != partition.end(); ++where) {
00270         redirect_edges(dfa, **where);
00271         refine_partition(dfa, **where, (typename matrix::size_type) 0, 
00272                          (**where).size() - 1);
00273         delete (*where);
00274       }
00275     }
00276   }
00277 }         
00278 
00279 template <typename DFA, typename NFA>
00280 void
00281 reverse(const DFA &a1, NFA &a2)
00282 {
00283   map<typename DFA::state_type, 
00284     safe<typename NFA::state_type, NFA::null_state> > m;
00285 
00286   // for each state:
00287   for(typename DFA::const_iterator src = a1.begin();
00288       src != a1.end(); ++src) {
00289     // create a new state if needed:
00290     safe<typename NFA::state_type, NFA::null_state> &new_aim = m[*src];
00291     if (new_aim == NFA::null_state) {
00292       new_aim = a2.new_state();
00293       a2.final(new_aim) = (a1.initial() == *src);
00294       if (a1.final(*src))
00295         a2.initial().insert(new_aim);
00296     }
00297 
00298     // for each transition:
00299     typename DFA::edges_type t = a1.delta2(*src);
00300     for(typename DFA::edges_type::const_iterator i = t.begin();
00301         i != t.end(); ++i) {
00302       // create a new state if needed:
00303       safe<typename NFA::state_type, NFA::null_state> &new_src = m[(*i).second];
00304       if (new_src == NFA::null_state) {
00305         new_src = a2.new_state();
00306         a2.final(new_src) = a1.initial() == new_src;
00307         if (a1.final((*i).second))
00308           a2.initial().insert(new_src);
00309       }
00310       a2.set_trans(new_src, (*i).first, new_aim);
00311     }
00312   }
00313 }
00314 
00315 // Requirements: DFA1::char_type convertible to DFA2::char_type
00316 template <typename DFA1, typename DFA2>
00317 void
00318 brzozowski(const DFA1 &a1, DFA2 &a2)
00319 {
00320   typedef NFA_mmap<typename DFA1::char_traits> nfa_type;
00321 
00322   if (a1.state_count() > 0) {
00323     nfa_type tmp2;
00324     {
00325       DFA1 tmp3;
00326       {
00327         nfa_type tmp1;
00328         reverse(a1, tmp1);
00329         tmp3.initial(clone(tmp3, dfirst_markc(forwarddc(tmp1))));
00330       } 
00331       reverse(tmp3, tmp2);
00332     }
00333     a2.initial(clone(a2, dfirst_markc(forwarddc(tmp2))));
00334   }
00335 }
00336     
00337 } // namespace astl
00338 
00339 #endif // ASTL_MINIMIZE_H

Generated on Sun Mar 8 02:41:34 2009 for ASTL by  doxygen 1.5.7.1