Vaucanson 1.4
|
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 //