Vaucanson  1.4.1
reduce.hxx
1 // reduce.hxx: this file is part of the Vaucanson project.
2 //
3 // Vaucanson, a generic library for finite state machines.
4 //
5 // Copyright (C) 2008, 2011 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 
18 #ifndef VCSN_ALGORITHMS_REDUCE_HXX
19 # define VCSN_ALGORITHMS_REDUCE_HXX
20 # include <queue>
21 # include <vector>
22 # include <map>
23 # include <vaucanson/misc/usual_macros.hh>
24 
25 namespace vcsn {
26 
27  template<typename A, typename AI, typename AO>
28  class reducer
29  {
30  typedef Element<A, AI> Auto;
31  AUTOMATON_TYPES(Auto);
32  AUTOMATON_FREEMONOID_TYPES(Auto);
33  typedef std::vector<semiring_elt_t> semiring_vector_t;
34  typedef std::vector<std::map<std::size_t, semiring_elt_t> > semiring_matrix_t;
35  typedef std::map<monoid_elt_t, semiring_matrix_t> semiring_matrix_set_t;
36 
37  /* output may have an implementation different from the
38  implementation of the input; this is for instance the case where
39  input is a transpose view, as in the wrappers.
40  */
41 
42  public:
43  reducer(const Element<A, AI>& input, Element<A, AO>& output) :
44  zero(input.series().semiring().wzero_),
45  one(input.series().semiring().wone_),
46  empty_word(input.series().monoid().VCSN_EMPTY_),
47  output(output)
48  {
49  std::map<hstate_t, int> state_to_index;
50  int i = 0;
51 
52  for_all_const_states(s, input)
53  state_to_index[*s] = i++;
54  nb_states = i;
55 
56  init.resize(i);
57  final.resize(i);
58 
59  // We assume that there are only weights on initial and final
60  // transitions
61 
62  // Conversion of the automaton into linear representation
63  for_all_const_initial_states(s, input)
64  init[state_to_index[*s]] = input.get_initial(*s).get(empty_word);
65 
66  for_all_const_final_states(s, input)
67  final[state_to_index[*s]] = input.get_final(*s).get(empty_word);
68 
69  for_all_const_transitions(t, input)
70  {
71  series_set_elt_t s = input.series_of(*t);
72  assert(is_support_in_alphabet(s));
73  for_all_(series_set_elt_t::support_t, l, s.supp())
74  {
75  const monoid_elt_t m(input.structure().series().monoid(), *l);
76  typename semiring_matrix_set_t::iterator it = letter_matrix_set.find(m);
77  if (it == letter_matrix_set.end())
78  it = letter_matrix_set.insert(make_pair(m, semiring_matrix_t(nb_states))).first;
79  it->second[state_to_index[input.src_of(*t)]][state_to_index[input.dst_of(*t)]] = s.get(m);
80  }
81  }
82  }
83 
84  //utility class
85  struct triple_t
86  {
87  hstate_t state;
88  semiring_vector_t vector;
89  monoid_elt_t letter;
90 
91  triple_t(const hstate_t& state, const semiring_vector_t& vector,
92  const monoid_elt_t& letter) :
93  state(state), vector(vector), letter(letter) {}
94 
95  };
96 
97  //utility methods
98  void product_vector_matrix(const semiring_vector_t& v,
99  const semiring_matrix_t& m,
100  semiring_vector_t& res)
101  {
102  for(int i=0; i<nb_states; i++)
103  for(typename std::map<std::size_t, semiring_elt_t>::const_iterator it
104  = m[i].begin(); it != m[i].end(); ++it)
105  {
106  int j = it->first;
107  res[j] += v[i] * it->second;
108  }
109  }
110 
111  semiring_elt_t scalar_product(const semiring_vector_t& v,
112  const semiring_vector_t& w)
113  {
114  semiring_elt_t res(zero);
115  for(int i = 0; i < nb_states; ++i)
116  res += v[i] * w[i];
117  return res;
118  }
119 
120  //core of the algorithm
121  void
122  left_reduce()
123  {
124  //if the initial vector is null, the reduction is empty
125  {
126  int i;
127  for (i = 0; i < nb_states; ++i)
128  if (init[i] != zero)
129  break;
130  if (i == nb_states)
131  return;
132  }
133  // otherwise...
134  std::queue<triple_t> queue;
135  // The base is a list of vectors, each vector is associated with
136  // a state of the output
137  std::list<std::pair<hstate_t,semiring_vector_t> > base;
138  // The initial vector corresponds to the first state and is the
139  // first element of the new base
140  hstate_t n_init = output.add_state();
141  base.push_back(std::pair<hstate_t,semiring_vector_t>(n_init, init));
142  output.set_initial(n_init);
143  // Final weight of the initial state
144  semiring_elt_t t = scalar_product(init, final);
145  if(t != zero)
146  {
147  series_set_elt_t s(output.structure().series());
148  s.assoc(empty_word, t);
149  output.set_final(n_init, s);
150  }
151  /* for each letter a, I.mu(a) is computed and pushed in the queue
152  with the letter a and the state corresponding to I; it allows to
153  build the automaton in the same time.
154  */
155  for (typename semiring_matrix_set_t::const_iterator it
156  = letter_matrix_set.begin(); it != letter_matrix_set.end(); ++it)
157  {
158  semiring_vector_t nv(nb_states);
159  product_vector_matrix(init, it->second, nv);
160  queue.push(triple_t(n_init, nv, it->first));
161  }
162  while (!queue.empty())
163  {
164  triple_t& tr = queue.front();
165  //the triple tr is not yet poped, to keep valid references.
166  semiring_vector_t& current = tr.vector;
167  // first non null indices of current and base vectors.
168  std::size_t curr_fnn = 0;
169  std::size_t base_fnn = 0;
170  for(typename std::list<std::pair<hstate_t,semiring_vector_t> >::iterator it
171  = base.begin(); it!=base.end(); ++it)
172  {
173  while (curr_fnn < nb_states && current[curr_fnn] == zero)
174  ++curr_fnn;
175  // first non null index of the base vector
176  // As the matrix is "in stairs" the base_fnn is larger than the previous one
177  semiring_vector_t& vbase = it->second;
178  while (base_fnn < nb_states && vbase[base_fnn] == zero)
179  ++base_fnn;
180  // Case A
181  if (base_fnn < curr_fnn)
182  // nothing to do in this case
183  continue;
184  // Case B
185  if (base_fnn > curr_fnn)
186  // current is added to the base before the current base vector
187  {
188  hstate_t n_state= output.add_state();
189  base.insert(it,std::pair<hstate_t,semiring_vector_t>(n_state,
190  current));
191  series_set_elt_t s(output.structure().series());
192  s.assoc(tr.letter, one);
193  output.add_series_transition(tr.state, n_state, s);
194  semiring_elt_t t=scalar_product(current, final);
195  if(t != zero)
196  {
197  series_set_elt_t s(output.structure().series());
198  s.assoc(empty_word, t);
199  output.set_final(n_state, s);
200  }
201  // All the vectors current.mu(a) are put in the queue
202  for (typename semiring_matrix_set_t::const_iterator itm
203  = letter_matrix_set.begin();
204  itm != letter_matrix_set.end(); ++itm)
205  {
206  semiring_vector_t nv(nb_states);
207  product_vector_matrix(current, itm->second, nv);
208  queue.push(triple_t(n_state, nv, itm->first));
209  }
210  // To avoid treatment after exiting the loop:
211  curr_fnn = nb_states;
212  break;
213  }
214  // Case C
215  // Otherwise, current is reduced w.r.t base
216  semiring_elt_t ratio = current[curr_fnn] / vbase[curr_fnn];
217  // This is safer than current[curr_fnn] = current[curr_fnn]-ratio*vbase[curr_fnn];
218  current[curr_fnn] = zero;
219  for(int i = curr_fnn+1; i < nb_states; ++i)
220  current[i ] =current[i] - ratio*vbase[i];
221  series_set_elt_t s(output.structure().series());
222  s.assoc(tr.letter, ratio);
223  output.add_series_transition(tr.state, it->first, s);
224  }
225  // If current has not been totally reduced w.r.t the base,
226  // it has to be put itself in the base
227  while (curr_fnn < nb_states && current[curr_fnn] == zero)
228  ++curr_fnn;
229  if (nb_states> curr_fnn)
230  // current is added at the end of the base
231  {
232  hstate_t n_state= output.add_state();
233  base.push_back(std::pair<hstate_t,semiring_vector_t>(n_state,
234  current));
235  series_set_elt_t s(output.structure().series());
236  s.assoc(tr.letter, one);
237  output.add_series_transition(tr.state, n_state, s);
238  semiring_elt_t t=scalar_product(current, final);
239  if(t != zero)
240  {
241  series_set_elt_t s(output.structure().series());
242  s.assoc(empty_word, t);
243  output.set_final(n_state, s);
244  }
245  for (typename semiring_matrix_set_t::const_iterator itm
246  = letter_matrix_set.begin();
247  itm != letter_matrix_set.end(); ++itm)
248  {
249  semiring_vector_t nv(nb_states);
250  product_vector_matrix(current, itm->second, nv);
251  queue.push(triple_t(n_state, nv, itm->first));
252  }
253  }
254  queue.pop();
255  }
256  /* Print the base
257  for(typename std::list<std::pair<hstate_t,semiring_vector_t> >::iterator it = base.begin(); it!=base.end(); ++it)
258  {
259  std::cerr << it->first << ':';
260  for(int i=0; i < nb_states; ++i)
261  std::cerr << (it->second)[i] << ',';
262  std::cerr << std::endl;
263  }
264  */
265  }
266 
267  private:
268  // zero and identity of used algebraic structure.
269  semiring_elt_t zero;
270  semiring_elt_t one;
271  monoid_elt_t empty_word;
272 
273  // Linear representation of the input automaton in matrix form
274  semiring_vector_t init;
275  semiring_vector_t final;
276  semiring_matrix_set_t letter_matrix_set;
277  size_t nb_states;
278 
279  Element<A, AO>& output;
280  };
281 
282  template<typename A, typename AI, typename AO>
283  void
284  do_semi_reduce(const Element<A, AI>& a, Element<A, AO>& output)
285  {
286  reducer<A, AI, AO> r(a, output);
287  r.left_reduce();
288  }
289 
290  template<typename A, typename AI>
291  Element<A, AI>
293  {
294  precondition(is_realtime(a));
295  Element<A, AI> output(a.structure());
296  do_semi_reduce(a, output);
297  return output;
298  }
299 
300  template<typename A, typename AI>
301  Element<A, AI>
303  {
304  precondition(is_realtime(a));
305  Element<A, AI> tmp(a.structure());
306  Element<A, AI> output(a.structure());
307  if (dir == misc::right_left)
308  {
309  do_semi_reduce(transpose_view(a), tmp);
310  do_semi_reduce(transpose_view(tmp), output);
311  return output;
312  }
313  else
314  {
315  do_semi_reduce(a, tmp);
316  do_semi_reduce(transpose_view(tmp), output);
317  return transpose(output);
318  }
319  }
320 
321 
322 } // vcsn
323 
324 #endif // !VCSN_ALGORITHMS_REDUCE_HXX //