Vaucanson 1.4
reduce.hxx
00001 // reduce.hxx: this file is part of the Vaucanson project.
00002 //
00003 // Vaucanson, a generic library for finite state machines.
00004 //
00005 // Copyright (C) 2008, 2011 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 
00018 #ifndef VCSN_ALGORITHMS_REDUCE_HXX
00019 # define VCSN_ALGORITHMS_REDUCE_HXX
00020 # include <queue>
00021 # include <vector>
00022 # include <map>
00023 # include <vaucanson/misc/usual_macros.hh>
00024 
00025 namespace vcsn {
00026 
00027   template<typename A, typename AI, typename AO>
00028   class reducer
00029   {
00030     typedef Element<A, AI> Auto;
00031     AUTOMATON_TYPES(Auto);
00032     AUTOMATON_FREEMONOID_TYPES(Auto);
00033     typedef std::vector<semiring_elt_t> semiring_vector_t;
00034     typedef std::vector<std::map<std::size_t, semiring_elt_t> > semiring_matrix_t;
00035     typedef std::map<monoid_elt_t, semiring_matrix_t> semiring_matrix_set_t;
00036 
00037   /* output may have an implementation different from the
00038      implementation of the input; this is for instance the case where
00039      input is a transpose view, as in the wrappers.
00040      */
00041 
00042   public:
00043     reducer(const Element<A, AI>& input, Element<A, AO>& output) :
00044       zero(input.series().semiring().wzero_),
00045       one(input.series().semiring().wone_),
00046       empty_word(input.series().monoid().VCSN_EMPTY_),
00047       output(output)
00048     {
00049       std::map<hstate_t, int>   state_to_index;
00050       int i = 0;
00051 
00052       for_all_const_states(s, input)
00053         state_to_index[*s] = i++;
00054       nb_states = i;
00055 
00056       init.resize(i);
00057       final.resize(i);
00058 
00059       // We assume that there are only weights on initial and final
00060       // transitions
00061 
00062       // Conversion of the automaton into linear representation
00063       for_all_const_initial_states(s, input)
00064         init[state_to_index[*s]] = input.get_initial(*s).get(empty_word);
00065 
00066       for_all_const_final_states(s, input)
00067         final[state_to_index[*s]] = input.get_final(*s).get(empty_word);
00068 
00069       for_all_const_transitions(t, input)
00070       {
00071         series_set_elt_t s = input.series_of(*t);
00072         assert(is_support_in_alphabet(s));
00073         for_all_(series_set_elt_t::support_t, l, s.supp())
00074         {
00075           const monoid_elt_t m(input.structure().series().monoid(), *l);
00076           typename semiring_matrix_set_t::iterator it = letter_matrix_set.find(m);
00077           if (it == letter_matrix_set.end())
00078             it = letter_matrix_set.insert(make_pair(m, semiring_matrix_t(nb_states))).first;
00079           it->second[state_to_index[input.src_of(*t)]][state_to_index[input.dst_of(*t)]] = s.get(m);
00080         }
00081       }
00082     }
00083 
00084     //utility class
00085     struct triple_t
00086     {
00087       hstate_t state;
00088       semiring_vector_t vector;
00089       monoid_elt_t letter;
00090 
00091       triple_t(const hstate_t& state, const semiring_vector_t& vector,
00092                const monoid_elt_t& letter) :
00093         state(state), vector(vector), letter(letter) {}
00094 
00095     };
00096 
00097     //utility methods
00098     void product_vector_matrix(const semiring_vector_t& v,
00099                                const semiring_matrix_t& m,
00100                                semiring_vector_t& res)
00101     {
00102       for(int i=0; i<nb_states; i++)
00103         for(typename std::map<std::size_t, semiring_elt_t>::const_iterator it
00104               = m[i].begin(); it != m[i].end(); ++it)
00105           {
00106             int j = it->first;
00107             res[j] += v[i] * it->second;
00108           }
00109     }
00110 
00111     semiring_elt_t scalar_product(const semiring_vector_t& v,
00112                                   const semiring_vector_t& w)
00113     {
00114       semiring_elt_t res(zero);
00115       for(int i = 0; i < nb_states; ++i)
00116         res += v[i] * w[i];
00117       return res;
00118     }
00119 
00120     //core of the algorithm
00121     void
00122     left_reduce()
00123     {
00124       //if the initial vector is null, the reduction is empty
00125       {
00126         int i;
00127         for (i = 0; i < nb_states; ++i)
00128           if (init[i] != zero)
00129             break;
00130         if (i == nb_states)
00131           return;
00132       }
00133       // otherwise...
00134       std::queue<triple_t> queue;
00135       // The base is a list of vectors, each vector is associated with
00136       // a state of the output
00137       std::list<std::pair<hstate_t,semiring_vector_t> > base;
00138       // The initial vector corresponds to the first state and is the
00139       // first element of the new base
00140       hstate_t n_init = output.add_state();
00141       base.push_back(std::pair<hstate_t,semiring_vector_t>(n_init, init));
00142       output.set_initial(n_init);
00143       // Final weight of the initial state
00144       semiring_elt_t t = scalar_product(init, final);
00145       if(t != zero)
00146         {
00147           series_set_elt_t s(output.structure().series());
00148           s.assoc(empty_word, t);
00149           output.set_final(n_init, s);
00150         }
00151       /* for each letter a, I.mu(a) is computed and pushed in the queue
00152          with the letter a and the state corresponding to I; it allows to
00153          build the automaton in the same time.
00154       */
00155       for (typename semiring_matrix_set_t::const_iterator it
00156              = letter_matrix_set.begin(); it != letter_matrix_set.end(); ++it)
00157         {
00158           semiring_vector_t nv(nb_states);
00159           product_vector_matrix(init, it->second, nv);
00160           queue.push(triple_t(n_init, nv, it->first));
00161         }
00162       while (!queue.empty())
00163         {
00164           triple_t& tr = queue.front();
00165           //the triple tr is not yet poped, to keep valid references.
00166           semiring_vector_t& current = tr.vector;
00167           // first non null indices of current and base vectors.
00168           std::size_t curr_fnn = 0;
00169           std::size_t base_fnn = 0;
00170           for(typename std::list<std::pair<hstate_t,semiring_vector_t> >::iterator it
00171                 = base.begin(); it!=base.end(); ++it)
00172           {
00173             while (curr_fnn < nb_states && current[curr_fnn] == zero)
00174               ++curr_fnn;
00175             // first non null index of the base vector
00176             // As the matrix is "in stairs" the base_fnn is larger than the previous one
00177             semiring_vector_t& vbase = it->second;
00178             while (base_fnn < nb_states && vbase[base_fnn] == zero)
00179               ++base_fnn;
00180             // Case A
00181             if (base_fnn < curr_fnn)
00182               // nothing to do in this case
00183               continue;
00184             // Case B
00185             if (base_fnn > curr_fnn)
00186               // current is added to the base before the current base vector
00187               {
00188                 hstate_t n_state= output.add_state();
00189                 base.insert(it,std::pair<hstate_t,semiring_vector_t>(n_state,
00190                                                                      current));
00191                 series_set_elt_t s(output.structure().series());
00192                 s.assoc(tr.letter, one);
00193                 output.add_series_transition(tr.state, n_state, s);
00194                 semiring_elt_t t=scalar_product(current, final);
00195                 if(t != zero)
00196                 {
00197                   series_set_elt_t s(output.structure().series());
00198                   s.assoc(empty_word, t);
00199                   output.set_final(n_state, s);
00200                 }
00201                 // All the vectors current.mu(a) are put in the queue
00202                 for (typename semiring_matrix_set_t::const_iterator itm
00203                        = letter_matrix_set.begin();
00204                      itm != letter_matrix_set.end(); ++itm)
00205                {
00206                   semiring_vector_t nv(nb_states);
00207                   product_vector_matrix(current, itm->second, nv);
00208                   queue.push(triple_t(n_state, nv, itm->first));
00209                }
00210                 // To avoid treatment after exiting the loop:
00211                 curr_fnn = nb_states;
00212                 break;
00213               }
00214             // Case C
00215             // Otherwise, current is reduced w.r.t base
00216             semiring_elt_t ratio = current[curr_fnn] / vbase[curr_fnn];
00217             // This is safer than current[curr_fnn] = current[curr_fnn]-ratio*vbase[curr_fnn];
00218             current[curr_fnn] = zero;
00219             for(int i = curr_fnn+1; i < nb_states; ++i)
00220                 current[i ] =current[i] - ratio*vbase[i];
00221                 series_set_elt_t s(output.structure().series());
00222                 s.assoc(tr.letter, ratio);
00223             output.add_series_transition(tr.state, it->first, s);
00224           }
00225           // If current has not been totally reduced w.r.t the base,
00226           // it has to be put itself in the base
00227           while (curr_fnn < nb_states && current[curr_fnn] == zero)
00228             ++curr_fnn;
00229           if (nb_states> curr_fnn)
00230             // current is added at the end of the base
00231             {
00232               hstate_t n_state= output.add_state();
00233               base.push_back(std::pair<hstate_t,semiring_vector_t>(n_state,
00234                                                                    current));
00235               series_set_elt_t s(output.structure().series());
00236               s.assoc(tr.letter, one);
00237               output.add_series_transition(tr.state, n_state, s);
00238               semiring_elt_t t=scalar_product(current, final);
00239               if(t != zero)
00240                 {
00241                   series_set_elt_t s(output.structure().series());
00242                   s.assoc(empty_word, t);
00243                   output.set_final(n_state, s);
00244                 }
00245               for (typename semiring_matrix_set_t::const_iterator itm
00246                      = letter_matrix_set.begin();
00247                    itm != letter_matrix_set.end(); ++itm)
00248               {
00249                 semiring_vector_t nv(nb_states);
00250                 product_vector_matrix(current, itm->second, nv);
00251                 queue.push(triple_t(n_state, nv, itm->first));
00252               }
00253            }
00254           queue.pop();
00255         }
00256         /* Print the base
00257           for(typename std::list<std::pair<hstate_t,semiring_vector_t> >::iterator it = base.begin(); it!=base.end(); ++it)
00258           {
00259                 std::cerr << it->first << ':';
00260                 for(int i=0; i < nb_states; ++i)
00261                         std::cerr << (it->second)[i] << ',';
00262                 std::cerr << std::endl;
00263           }
00264           */
00265     }
00266 
00267   private:
00268     // zero and identity of used algebraic structure.
00269     semiring_elt_t      zero;
00270     semiring_elt_t      one;
00271     monoid_elt_t        empty_word;
00272 
00273     // Linear representation of the input automaton in matrix form
00274     semiring_vector_t init;
00275     semiring_vector_t final;
00276     semiring_matrix_set_t letter_matrix_set;
00277     size_t nb_states;
00278 
00279     Element<A, AO>& output;
00280   };
00281 
00282   template<typename A, typename AI, typename AO>
00283   void
00284   do_semi_reduce(const Element<A, AI>& a, Element<A, AO>& output)
00285   {
00286     reducer<A, AI, AO> r(a, output);
00287     r.left_reduce();
00288   }
00289 
00290   template<typename A, typename AI>
00291   Element<A, AI>
00292   semi_reduce(const Element<A, AI>& a)
00293   {
00294     precondition(is_realtime(a));
00295     Element<A, AI> output(a.structure());
00296     do_semi_reduce(a, output);
00297     return output;
00298   }
00299 
00300   template<typename A, typename AI>
00301   Element<A, AI>
00302   reduce(const Element<A, AI>& a, misc::direction_type dir)
00303   {
00304     precondition(is_realtime(a));
00305     Element<A, AI> tmp(a.structure());
00306     Element<A, AI> output(a.structure());
00307     if (dir == misc::right_left)
00308     {
00309       do_semi_reduce(transpose_view(a), tmp);
00310       do_semi_reduce(transpose_view(tmp), output);
00311       return output;
00312     }
00313     else
00314     {
00315       do_semi_reduce(a, tmp);
00316       do_semi_reduce(transpose_view(tmp), output);
00317       return transpose(output);
00318     }
00319   }
00320 
00321 
00322 } // vcsn
00323 
00324 #endif // !VCSN_ALGORITHMS_REDUCE_HXX //