Vcsn  2.2
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 <Automaton 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  , prune_(prune)
59  {}
60 
62  {
63  auto proper_ctx = make_proper_context(aut_->context());
64  auto res = make_shared_ptr<aut_proper_t>(proper_ctx);
65 
67 
68  copy_into(aut_, res);
69  return res;
70  }
71 
73  {
74  if (!is_proper(aut_))
76  }
77 
109  private:
112  {
113  if (auto p = profile(s))
114  p->update(in(aut_, s, empty_word_).size(),
115  in(aut_, s).size(),
116  out(aut_, s, empty_word_).size(),
117  all_out(aut_, s).size());
118  }
119 
122  void build_heap_()
123  {
124  for (auto s: aut_->states())
125  // We don't care about states without incoming spontaneous
126  // transitions.
127  if (auto in_sp = in(aut_, s, empty_word_).size())
128  {
129  auto ins = in(aut_, s).size();
130  auto out_sp = out(aut_, s, empty_word_).size();
131  auto out = all_out(aut_, s).size();
132  auto h = todo_.emplace(epsilon_profile<state_t>
133  {s, in_sp, ins, out_sp, out});
134  handles_.emplace(s, h);
135  }
136  }
137 
139  void show_heap_() const
140  {
141  const char* sep = "";
142  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
143  i != end; ++i)
144  {
145  std::cerr << sep << *i;
146  sep = " > ";
147  }
148  }
149 
153  {
154  if (3 < debug_)
155  {
156  std::cerr << "update heap (" << s << " : ";
157  show_heap_();
158  }
159  auto i = handles_.find(s);
160  if (i != handles_.end())
161  todo_.update(i->second);
162  if (3 < debug_)
163  {
164  std::cerr << ") => ";
165  show_heap_();
166  std::cerr << std::endl;
167  }
168  }
169 
170 #ifdef STATS
171  unsigned added_ = 0;
172  unsigned removed_ = 0;
173 #endif
174 
177  {
178  auto i = handles_.find(s);
179  if (i == handles_.end())
180  return nullptr;
181  else
182  return &*i->second;
183  }
184 
197  {
198  // Iterate on a copy, as we remove these transitions in the
199  // loop.
201  // The star of the weight of the loop on 's' (1 if no loop).
202  weight_t star = ws_.one();
203  using state_weight_t = std::pair<state_t, weight_t>;
204  std::vector<state_weight_t> closure;
205  for (auto t : transitions)
206  {
207  weight_t weight = aut_->weight_of(t);
208  state_t src = aut_->src_of(t);
209  if (src == s) //loop
210  star = ws_.star(weight);
211  else
212  closure.emplace_back(src, weight);
213  // Delete incoming epsilon transitions.
214  aut_->del_transition(t);
215  }
216 
217  /*
218  For each transition (t : s -- label|weight --> dst),
219  for each former
220  epsilon transition closure->first -- e|closure->second --> s
221  a transition
222  (closure->first -- label | closure->second*weight --> dst)
223  is added to the automaton (add, not set !!)
224 
225  If (s) is final with weight (weight),
226  for each former
227  epsilon transition closure->first -- e|closure->second --> s
228  pair-second * weight is added to the final weight
229  of closure->first
230  */
231  for (auto t: all_out(aut_, s))
232  {
233  // "Blowing": For each transition (or terminal arrow)
234  // outgoing from (s), the weight is multiplied by
235  // (star).
236  weight_t blow = ws_.mul(star, aut_->weight_of(t));
237  aut_->set_weight(t, blow);
238 
239  label_t label = aut_->label_of(t);
240  state_t dst = aut_->dst_of(t);
241  for (auto pair: closure)
242  {
243  state_t src = pair.first;
244  weight_t w = ws_.mul(pair.second, blow);
245  aut_->add_transition(src, dst, label, w);
246  }
247  }
248 #ifdef STATS
249  unsigned added = all_out(aut_, s).size() * closure.size();
250  unsigned removed = transitions.size();
251 #endif
252  if (prune_ && all_in(aut_, s).empty())
253  {
254 #ifdef STATS
255  removed += all_out(aut_, s).size();
256 #endif
257  aut_->del_state(s);
258  }
259 #ifdef STATS
260  added_ += added;
261  removed_ += removed;
262  if (1 < debug_)
263  std::cerr << " -" << removed << "+" << added
264  << " (-" << removed_ << "+" << added_ << ")";
265 #endif
266  }
267 
281  {
282  build_heap_();
283  /* For each state (s), for each incoming epsilon-transitions
284  (t), if (t) is a loop, the star of its weight is computed,
285  otherwise, (t) is stored into the closure list. Then (t)
286  is removed. */
287 
288  // The neighbors of s: their profiles need to be updated after
289  // s was processed.
290  std::unordered_set<state_t> neighbors;
291  while (!todo_.empty())
292  {
293  if (2 < debug_)
294  {
295  std::cerr << "Before: ";
296  show_heap_();
297  std::cerr << std::endl;
298  }
299  auto p = todo_.top();
300  todo_.pop();
301  if (1 < debug_)
302  std::cerr << "Remove: " << p;
303 
304  auto s = p.state;
305  handles_.erase(s);
306  neighbors.clear();
307  for (auto t: in(aut_, s))
308  {
309  state_t n = aut_->src_of(t);
310  if (n != s)
311  neighbors.emplace(n);
312  }
313  for (auto t: out(aut_, s))
314  {
315  state_t n = aut_->dst_of(t);
316  if (n != s)
317  neighbors.emplace(n);
318  }
319 
320  in_situ_remover_(s);
321 
322  // Update all neighbors and then the heap.
323  for (auto n: neighbors)
324  update_profile_(n);
325  for (auto n: neighbors)
326  update_heap_(n);
327  if (1 < debug_)
328  std::cerr << " #tr: "
330  << "/" << aut_->num_transitions() << std::endl;
331  if (2 < debug_)
332  {
333  std::cerr << "After: ";
334  show_heap_();
335  std::cerr << std::endl;
336  }
337  if (4 < debug_)
338  dot(aut_, std::cerr) << std::endl;
339  if (2 < debug_)
340  std::cerr << std::endl;
341  }
342 #ifdef STATS
343  if (0 < debug_)
344  std::cerr << "Total transitions -" << removed_
345  << "+" << added_ << std::endl;
346 #endif
347  }
348 
354  {
355  size_t res = 0;
356  for (auto t : transitions(aut_))
357  res += aut_->labelset()->is_one(aut_->label_of(t));
358  return res;
359  }
360 
361  private:
363  int debug_;
365  automaton_t aut_;
367  const weightset_t& ws_ = *aut_->weightset();
369  label_t empty_word_ = aut_->labelset()->one();
370 
372  using heap_t = boost::heap::fibonacci_heap<epsilon_profile<state_t>>;
375  std::unordered_map<state_t, typename heap_t::handle_type> handles_;
376 
378  bool prune_;
379  };
380 
381 
382  /*----------------------------------------------.
383  | Specialization when there is no 'one' label. |
384  `----------------------------------------------*/
385 
386  template <Automaton Aut>
387  class epsilon_remover<Aut, false>
388  {
389  using automaton_t = Aut;
391  public:
392  epsilon_remover(automaton_t& aut, bool = true)
393  : aut_(aut)
394  {}
395 
399  {
400  return copy(aut_);
401  }
402 
404  void in_situ_remover() {}
405 
406 
407  private:
408  automaton_t aut_;
409  };
410  }
411 }
bool is_proper(const Aut &aut)
Test whether an automaton is proper.
Definition: is-proper.hh:48
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s.
Definition: automaton.hh:37
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:251
This class contains the core of the proper algorithm.
void update_profile_(state_t s)
The core of the (backward) epsilon-removal.
auto make_proper_context(const context< LabelSet, WeightSet > &ctx) -> proper_context< context< LabelSet, WeightSet >>
From a context, its non-nullable context.
Definition: labelset.hh:226
std::shared_ptr< const detail::weight_base > weight
Definition: fwd.hh:88
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:59
void show_heap_() const
Show the heap, for debugging.
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:260
Definition: a-star.hh:8
auto all_in(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions entering state s.
Definition: automaton.hh:86
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:55
label_t empty_word_
Shorthand to the empty word.
transition_t_of< automaton_t > transition_t
std::unordered_map< state_t, typename heap_t::handle_type > handles_
Map: state -> heap-handle.
void in_situ_remover()
Nothing to do to remove the transitions in place.
label_t_of< automaton_t > label_t
labelset_t_of< automaton_t > labelset_t
epsilon_remover(automaton_t &aut, bool=true)
epsilon_profile< state_t > * profile(state_t s)
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:56
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:61
This is used by some epsilon removal algorithms.
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:56
automaton_t aut_
The automaton we work on.
fresh_automaton_t_of< automaton_t > aut_proper_t
std::vector< typename Cont::value_type > make_vector(const Cont &cont)
The content of cont as a vector.
Definition: vector.hh:20
typename Aut::element_type::template fresh_automaton_t< Context > fresh_automaton_t_of
Given an automaton type, the type of its copies.
Definition: traits.hh:74
aut_proper_t operator()()
Just a copy of the automata in the proper context, since there aren't any transitions to remove...
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:57
auto transitions(const Aut &aut) -> decltype(all_transitions(aut, is_special_t< Aut >
All the transition indexes between visible states.
Definition: automaton.hh:218
void update_heap_(state_t s)
Update the heap for s.
int debug_
Debug level. The higher, the more details are reported.
weightset_t_of< automaton_t > weightset_t
boost::heap::fibonacci_heap< epsilon_profile< state_t >> heap_t
Max-heap to decide the order of state-elimination.
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:54
void build_heap_()
Build the profiles and the heap for states with incoming spontaneous transitions. ...
fresh_automaton_t_of< automaton_t, detail::proper_context< context_t_of< automaton_t >>> aut_proper_t
static int debug_level()
Debug level set in the user's environment.
Definition: debug-level.hh:11
epsilon_remover(automaton_t &aut, bool prune=true)
Get ready to eliminate spontaneous transitions.
typename weightset_t::value_t weight_t
const weightset_t & ws_
Shorthand to the weightset.
#define Automaton
Definition: automaton.hh:24
std::ostream & dot(const Aut &aut, std::ostream &out, format fmt={})
Print an automaton in Graphviz's Dot format.
Definition: dot.hh:377
size_t num_spontaneous_transitions_() const
The number of spontaneous transitions in aut_.
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...
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
state_t_of< automaton_t > state_t
bool prune_
Whether to prune states that become inaccessible.
auto in(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions arriving to state s.
Definition: automaton.hh:105
void in_situ_remover_()
Remove all the states with incoming spontaneous transitions.
auto copy(const AutIn &input, KeepState keep_state, KeepTrans keep_trans) -> decltype(keep_state(input->null_state()), keep_trans(input->null_transition()), make_fresh_automaton< AutIn, AutOut >(input))
A copy of input keeping only its states that are accepted by keep_state, and transitions accepted by ...
Definition: copy.hh:308