Vcsn  2.1
Be Rational
epsilon-remover.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <stdexcept>
4 #include <type_traits>
5 #include <unordered_map>
6 #include <unordered_set>
7 #include <utility>
8 #include <vector>
9 
10 #include <boost/lexical_cast.hpp>
11 #include <boost/heap/fibonacci_heap.hpp>
12 
13 #include <vcsn/algos/copy.hh>
14 #include <vcsn/algos/dot.hh>
15 #include <vcsn/algos/fwd.hh>
17 #include <vcsn/algos/is-proper.hh>
18 #include <vcsn/algos/is-valid.hh>
19 #include <vcsn/core/kind.hh>
20 #include <vcsn/misc/attributes.hh>
21 #include <vcsn/misc/debug-level.hh>
22 #include <vcsn/misc/direction.hh>
24 #include <vcsn/misc/vector.hh> // make_vector
25 
26 #define STATS
27 
28 namespace vcsn
29 {
30  namespace detail
31  {
36  template <typename Aut,
37  bool has_one = labelset_t_of<Aut>::has_one()>
39  {
40  using automaton_t = Aut;
43  using weight_t = typename weightset_t::value_t;
47 
50 
51  public:
55  epsilon_remover(automaton_t& aut, bool prune = true)
56  : debug_(debug_level())
57  , aut_(aut)
58  , ws_(*aut->weightset())
59  , empty_word_(aut->labelset()->one())
60  , prune_(prune)
61  {}
62 
64  {
65  auto proper_ctx = make_proper_context(aut_->context());
66  auto res = make_shared_ptr<aut_proper_t>(proper_ctx);
67 
68  if (!is_proper(aut_))
70 
71  copy_into(aut_, res);
72  return res;
73  }
74 
106  private:
109  {
110  if (auto p = profile(s))
111  p->update(aut_->in(s, empty_word_).size(),
112  aut_->in(s).size(),
113  aut_->out(s, empty_word_).size(),
114  aut_->all_out(s).size());
115  }
116 
119  void build_heap_()
120  {
121  for (auto s: aut_->states())
122  // We don't care about states without incoming spontaneous
123  // transitions.
124  if (auto in_sp = aut_->in(s, empty_word_).size())
125  {
126  auto in = aut_->in(s).size();
127  auto out_sp = aut_->out(s, empty_word_).size();
128  auto out = aut_->all_out(s).size();
129  auto h = todo_.emplace(epsilon_profile<state_t>
130  {s, in_sp, in, out_sp, out});
131  handles_.emplace(s, h);
132  }
133  }
134 
136  void show_heap_() const
137  {
138  const char* sep = "";
139  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
140  i != end; ++i)
141  {
142  std::cerr << sep << *i;
143  sep = " > ";
144  }
145  }
146 
150  {
151  if (3 < debug_)
152  {
153  std::cerr << "update heap (" << s << " : ";
154  show_heap_();
155  }
156  auto i = handles_.find(s);
157  if (i != handles_.end())
158  todo_.update(i->second);
159  if (3 < debug_)
160  {
161  std::cerr << ") => ";
162  show_heap_();
163  std::cerr << std::endl;
164  }
165  }
166 
167 #ifdef STATS
168  unsigned added_ = 0;
169  unsigned removed_ = 0;
170 #endif
171 
174  {
175  auto i = handles_.find(s);
176  if (i == handles_.end())
177  return nullptr;
178  else
179  return &*i->second;
180  }
181 
194  {
195  // Iterate on a copy, as we remove these transitions in the
196  // loop.
197  auto transitions = make_vector(aut_->in(s, empty_word_));
198  // The star of the weight of the loop on 's' (1 if no loop).
199  weight_t star = ws_.one();
200  using state_weight_t = std::pair<state_t, weight_t>;
201  std::vector<state_weight_t> closure;
202  for (auto t : transitions)
203  {
204  weight_t weight = aut_->weight_of(t);
205  state_t src = aut_->src_of(t);
206  if (src == s) //loop
207  star = ws_.star(weight);
208  else
209  closure.emplace_back(src, weight);
210  // Delete incoming epsilon transitions.
211  aut_->del_transition(t);
212  }
213 
214  /*
215  For each transition (t : s -- label|weight --> dst),
216  for each former
217  epsilon transition closure->first -- e|closure->second --> s
218  a transition
219  (closure->first -- label | closure->second*weight --> dst)
220  is added to the automaton (add, not set !!)
221 
222  If (s) is final with weight (weight),
223  for each former
224  epsilon transition closure->first -- e|closure->second --> s
225  pair-second * weight is added to the final weight
226  of closure->first
227  */
228  for (auto t: aut_->all_out(s))
229  {
230  // "Blowing": For each transition (or terminal arrow)
231  // outgoing from (s), the weight is multiplied by
232  // (star).
233  weight_t blow = ws_.mul(star, aut_->weight_of(t));
234  aut_->set_weight(t, blow);
235 
236  label_t label = aut_->label_of(t);
237  state_t dst = aut_->dst_of(t);
238  for (auto pair: closure)
239  {
240  state_t src = pair.first;
241  weight_t w = ws_.mul(pair.second, blow);
242  aut_->add_transition(src, dst, label, w);
243  }
244  }
245 #ifdef STATS
246  unsigned added = aut_->all_out(s).size() * closure.size();
247  unsigned removed = transitions.size();
248 #endif
249  if (prune_ && aut_->all_in(s).empty())
250  {
251 #ifdef STATS
252  removed += aut_->all_out(s).size();
253 #endif
254  aut_->del_state(s);
255  }
256 #ifdef STATS
257  added_ += added;
258  removed_ += removed;
259  if (1 < debug_)
260  std::cerr << " -" << removed << "+" << added
261  << " (-" << removed_ << "+" << added_ << ")";
262 #endif
263  }
264 
278  {
279  build_heap_();
280  /* For each state (s), for each incoming epsilon-transitions
281  (t), if (t) is a loop, the star of its weight is computed,
282  otherwise, (t) is stored into the closure list. Then (t)
283  is removed. */
284 
285  // The neighbors of s: their profiles need to be updated after
286  // s was processed.
287  std::unordered_set<state_t> neighbors;
288  while (!todo_.empty())
289  {
290  if (2 < debug_)
291  {
292  std::cerr << "Before: ";
293  show_heap_();
294  std::cerr << std::endl;
295  }
296  auto p = todo_.top();
297  todo_.pop();
298  if (1 < debug_)
299  std::cerr << "Remove: " << p;
300 
301  auto s = p.state;
302  handles_.erase(s);
303  neighbors.clear();
304  for (auto t: aut_->in(s))
305  {
306  state_t n = aut_->src_of(t);
307  if (n != s)
308  neighbors.emplace(n);
309  }
310  for (auto t: aut_->out(s))
311  {
312  state_t n = aut_->dst_of(t);
313  if (n != s)
314  neighbors.emplace(n);
315  }
316 
317  in_situ_remover_(s);
318 
319  // Update all neighbors and then the heap.
320  for (auto n: neighbors)
321  update_profile_(n);
322  for (auto n: neighbors)
323  update_heap_(n);
324  if (1 < debug_)
325  std::cerr << " #tr: "
327  << "/" << aut_->num_transitions() << std::endl;
328  if (2 < debug_)
329  {
330  std::cerr << "After: ";
331  show_heap_();
332  std::cerr << std::endl;
333  }
334  if (4 < debug_)
335  dot(aut_, std::cerr) << std::endl;
336  if (2 < debug_)
337  std::cerr << std::endl;
338  }
339 #ifdef STATS
340  if (0 < debug_)
341  std::cerr << "Total transitions -" << removed_
342  << "+" << added_ << std::endl;
343 #endif
344  }
345 
351  {
352  size_t res = 0;
353  for (auto t : aut_->transitions())
354  res += aut_->labelset()->is_one(aut_->label_of(t));
355  return res;
356  }
357 
358  private:
360  int debug_;
362  automaton_t aut_;
364  const weightset_t& ws_;
367 
369  using heap_t = boost::heap::fibonacci_heap<epsilon_profile<state_t>>;
372  std::unordered_map<state_t, typename heap_t::handle_type> handles_;
373 
375  bool prune_;
376  };
377 
378 
379  /*----------------------------------------------.
380  | Specialization when there is no 'one' label. |
381  `----------------------------------------------*/
382 
383  template <typename Aut>
384  class epsilon_remover<Aut, false>
385  {
386  using automaton_t = Aut;
388  public:
389  epsilon_remover(automaton_t& aut, bool = true)
390  : aut_(aut)
391  {}
392 
394  {
395  return copy(aut_);
396  }
397 
398  private:
400  };
401  }
402 }
void show_heap_() const
Show the heap, for debugging.
boost::heap::fibonacci_heap< epsilon_profile< state_t >> heap_t
Max-heap to decide the order of state-elimination.
epsilon_remover(automaton_t &aut, bool=true)
std::vector< typename Cont::value_type > make_vector(const Cont &cont)
The content of cont as a vector.
Definition: vector.hh:20
static int debug_level()
Debug level set in the user's environment.
Definition: debug-level.hh:11
void update_heap_(state_t s)
Update the heap for s.
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:46
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:59
labelset_t_of< automaton_t > labelset_t
fresh_automaton_t_of< automaton_t, detail::proper_context< context_t_of< automaton_t >>> aut_proper_t
state_t_of< automaton_t > state_t
typename weightset_t::value_t weight_t
std::unordered_map< state_t, typename heap_t::handle_type > handles_
Map: state -> heap-handle.
void in_situ_remover_()
Remove all the states with incoming spontaneous transitions.
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:48
size_t num_spontaneous_transitions_() const
The number of spontaneous transitions in aut_.
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:51
This is used by some epsilon removal algorithms.
epsilon_profile< state_t > * profile(state_t s)
typename Aut::element_type::template fresh_automaton_t< Context > fresh_automaton_t_of
Definition: traits.hh:57
auto make_proper_context(const context< LabelSet, WeightSet > &ctx) -> proper_context< context< LabelSet, WeightSet >>
From a context, its non-nullable context.
Definition: labelset.hh:218
AutOut copy(const AutIn &input, KeepState keep_state, KeepTrans keep_trans)
A copy of input keeping only its states that are accepted by keep_state.
Definition: copy.hh:254
bool prune_
Whether to prune states that become inaccessible.
std::shared_ptr< const detail::weight_base > weight
Definition: fwd.hh:86
void build_heap_()
Build the profiles and the heap for states with incoming spontaneous transitions. ...
void copy_into(const AutIn &in, AutOut &out, KeepState keep_state, KeepTrans keep_trans)
Copy selected states and transitions of an automaton.
Definition: copy.hh:126
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:47
label_t empty_word_
Shorthand to the empty word.
label_t_of< automaton_t > label_t
int debug_
Debug level. The higher, the more details are reported.
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:49
transition_t_of< automaton_t > transition_t
const weightset_t & ws_
Shorthand to the weightset.
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:243
bool is_proper(const Aut &aut)
Test whether an automaton is proper.
Definition: is-proper.hh:48
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:371
void update_profile_(state_t s)
The core of the (backward) epsilon-removal.
epsilon_remover(automaton_t &aut, bool prune=true)
Get ready to eliminate spontaneous transitions.
automaton_t aut_
The automaton we work on.
fresh_automaton_t_of< automaton_t > aut_proper_t
This class contains the core of the proper algorithm.
weightset_t_of< automaton_t > weightset_t
void in_situ_remover_(state_t s)
For each state (s), for each incoming epsilon-transitions (t), if (t) is a loop, the star of its weig...