Vcsn  2.2
Be Rational
epsilon-remover-separate.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/heap/fibonacci_heap.hpp>
11 
12 #include <vcsn/algos/copy.hh>
13 #include <vcsn/algos/dot.hh>
14 #include <vcsn/algos/fwd.hh>
16 #include <vcsn/algos/is-proper.hh>
17 #include <vcsn/algos/is-valid.hh>
18 #include <vcsn/core/kind.hh>
19 #include <vcsn/misc/attributes.hh>
20 #include <vcsn/misc/debug-level.hh>
21 #include <vcsn/misc/direction.hh>
23 #include <vcsn/misc/vector.hh> // make_vector
24 
25 #define STATS
26 
27 namespace vcsn
28 {
29  namespace detail
30  {
35  template <Automaton Aut,
36  bool has_one = labelset_t_of<Aut>::has_one()>
38  {
39  using automaton_t = std::remove_cv_t<Aut>;
41  using weight_t = typename weightset_t::value_t;
43 
50 
57 
58  public:
62  epsilon_remover_separate(const automaton_t& aut, bool prune = true)
63  : debug_(debug_level())
64  , ws_(*aut->weightset())
65  , prune_(prune)
66  , d2p_(aut->all_states().back() + 1,
67  aut_proper_t::element_type::null_state())
68  , p2d_(aut->all_states().back() + 1,
69  aut_dirty_t::element_type::null_state())
70  {
71  auto dirty_ctx = dirty_ctx_t{{}, ws_};
72  auto proper_ctx = make_proper_context(aut->context());
73  aut_dirty_ = make_shared_ptr<aut_dirty_t>(dirty_ctx);
74  aut_proper_ = make_shared_ptr<aut_proper_t>(proper_ctx);
75 
76  auto pcopier = make_copier(aut, aut_proper_);
77  auto dcopier = make_copier(aut, aut_dirty_);
78 
79  using state_t = state_t_of<automaton_t>;
80  using transition_t = transition_t_of<automaton_t>;
81  pcopier([](state_t) { return true; },
82  [&aut](transition_t t) {
83  return !aut->labelset()->is_one(aut->label_of(t));
84  }
85  );
86  dcopier([&aut](state_t s) {
87  return (!in(aut, s, aut->labelset()->one()).empty()
88  || !out(aut, s, aut->labelset()->one()).empty());
89  },
90  [&aut](transition_t t) {
91  return aut->labelset()->is_one(aut->label_of(t));
92  }
93  );
94 
95  const auto& dorigins = dcopier.state_map();
96  const auto& porigins = pcopier.state_map();
97  for (const auto& dp : dorigins)
98  {
99  auto pp = porigins.find(dp.first);
100  assert(pp != porigins.end());
101  d2p_[dp.second] = pp->second;
102  p2d_[pp->second] = dp.second;
103  }
104  }
105 
106  private:
109 
112  {
113  auto dirty_s = p2d_[proper_s];
114  if (auto p = profile_(proper_s))
115  {
116  auto in_dirty = in(aut_dirty_, dirty_s).size();
117  auto in_proper = in(aut_proper_, proper_s).size();
118  auto out_dirty = out(aut_dirty_, dirty_s).size();
119  auto out_proper = all_out(aut_proper_, proper_s).size();
120 
121  p->update(in_dirty, in_proper + in_dirty,
122  out_dirty, out_proper + out_dirty);
123  }
124  }
125 
128  void build_heap_()
129  {
130  for (auto s: aut_dirty_->states())
131  // We don't care about states without incoming spontaneous
132  // transitions.
133  if (in(aut_dirty_, s).size())
134  {
135  auto proper_s = d2p_[s];
136  auto h = todo_.emplace(profile_t{proper_s, 0, 0, 0, 0});
137  handles_.emplace(proper_s, h);
138  update_profile_(proper_s);
139  }
140  }
141 
143  void show_heap_() const
144  {
145  const char* sep = "";
146  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
147  i != end; ++i)
148  {
149  std::cerr << sep << *i;
150  sep = " > ";
151  }
152  }
153 
157  {
158  if (3 < debug_)
159  {
160  std::cerr << "update heap (" << s << " : ";
161  show_heap_();
162  }
163  auto i = handles_.find(s);
164  if (i != handles_.end())
165  todo_.update(i->second);
166  if (3 < debug_)
167  {
168  std::cerr << ") => ";
169  show_heap_();
170  std::cerr << std::endl;
171  }
172  }
173 
174 #ifdef STATS
175  unsigned added_ = 0;
176  unsigned removed_ = 0;
177 #endif
178 
182  {
183  auto i = handles_.find(s);
184  if (i == handles_.end())
185  return nullptr;
186  else
187  return &*i->second;
188  }
189 
201  void remover_on(state_proper_t proper_s, state_dirty_t dirty_s)
202  {
203  // The star of the weight of the loop on 's' (1 if no loop).
204  weight_t star = ws_.one();
205  using state_weight_t = std::pair<state_dirty_t, weight_t>;
206  auto closure = std::vector<state_weight_t>{};
207 
208 #ifdef STATS
209  // Number of transitions that will be removed.
210  auto removed = in(aut_dirty_, dirty_s).size();
211 #endif
212  // Iterate on a copy: we remove these transitions in the loop.
213  for (auto t : make_vector(in(aut_dirty_, dirty_s)))
214  {
215  weight_t weight = aut_dirty_->weight_of(t);
216  auto src = aut_dirty_->src_of(t);
217  if (src == dirty_s) //loop
218  star = ws_.star(weight);
219  else
220  closure.emplace_back(src, weight);
221  // Delete incoming epsilon transitions.
222  aut_dirty_->del_transition(t);
223  }
224 
225  /*
226  For each transition (t : s -- label|weight --> dst),
227  for each former
228  epsilon transition closure->first -- e|closure->second --> s
229  a transition
230  (closure->first -- label | closure->second*weight --> dst)
231  is added to the automaton (add, not set !!)
232 
233  If (s) is final with weight (weight),
234  for each former
235  epsilon transition closure->first -- e|closure->second --> s
236  pair-second * weight is added to the final weight
237  of closure->first
238  */
239 
240 
241  // TODO: factoring with lambda
242  for (auto t: out(aut_dirty_, dirty_s))
243  {
244  weight_t blow = ws_.mul(star, aut_dirty_->weight_of(t));
245  aut_dirty_->set_weight(t, blow);
246 
247  auto dst = aut_dirty_->dst_of(t);
248  for (auto pair: closure)
249  {
250  auto src = pair.first;
251  weight_t w = ws_.mul(pair.second, blow);
252  aut_dirty_->add_transition(src, dst, {}, w);
253  }
254  }
255 
256  for (auto t: all_out(aut_proper_, proper_s))
257  {
258  weight_t blow = ws_.mul(star, aut_proper_->weight_of(t));
259  aut_proper_->set_weight(t, blow);
260 
261  label_proper_t label = aut_proper_->label_of(t);
262  auto dst = aut_proper_->dst_of(t);
263  for (auto pair: closure)
264  {
265  auto src = pair.first;
266  weight_t w = ws_.mul(pair.second, blow);
267  aut_proper_->add_transition(d2p_[src], dst, label, w);
268  }
269  }
270 #ifdef STATS
271  // Number of transition that have been added.
272  auto added = (all_out(aut_proper_, proper_s).size()
273  + out(aut_dirty_, dirty_s).size()) * closure.size();
274 #endif
275  if (prune_
276  && in(aut_dirty_, dirty_s).empty()
277  && all_in(aut_proper_, proper_s).empty())
278  {
279 #ifdef STATS
280  removed += (all_out(aut_proper_, proper_s).size()
281  + out(aut_dirty_, dirty_s).size());
282 #endif
283  aut_proper_->del_state(proper_s);
284  aut_dirty_->del_state(dirty_s);
285  }
286 #ifdef STATS
287  added_ += added;
288  removed_ += removed;
289  if (1 < debug_)
290  std::cerr << " -" << removed << "+" << added
291  << " (-" << removed_ << "+" << added_ << ")";
292 #endif
293  }
294 
295  public:
298  {
299  state_dirty_t dirty_s = p2d_[proper_s];
300  if (dirty_s == aut_dirty_->null_state())
301  return false;
302  else
303  return !in(aut_dirty_, dirty_s).empty();
304  }
305 
307  void remover_on(state_proper_t proper_s)
308  {
309  state_dirty_t dirty_s = p2d_[proper_s];
310  if (dirty_s != aut_dirty_->null_state())
311  remover_on(proper_s, dirty_s);
312  }
313 
327  {
328  if (4 < debug_)
329  {
330  dot(aut_proper_, std::cerr) << std::endl;
331  dot(aut_dirty_, std::cerr) << std::endl;
332  }
333  build_heap_();
334  /* For each state (s), for each incoming epsilon-transitions
335  (t), if (t) is a loop, the star of its weight is computed,
336  otherwise, (t) is stored into the closure list. Then (t)
337  is removed. */
338 
339  // The neighbors of s: their profiles need to be updated after
340  // s was processed.
341  auto neighbors = std::unordered_set<state_proper_t>{};
342  while (!todo_.empty())
343  {
344  if (2 < debug_)
345  {
346  std::cerr << "Before: ";
347  show_heap_();
348  std::cerr << std::endl;
349  }
350  auto p = todo_.top();
351  todo_.pop();
352  if (1 < debug_)
353  std::cerr << "Remove: " << p;
354 
355  state_proper_t proper_s = p.state;
356  auto dirty_s = p2d_[proper_s];
357  handles_.erase(proper_s);
358 
359  // Adjacent states.
360  neighbors.clear();
361  for (auto t: in(aut_proper_, proper_s))
362  if (aut_proper_->src_of(t) != proper_s)
363  neighbors.emplace(aut_proper_->src_of(t));
364  for (auto t: out(aut_proper_, proper_s))
365  if (aut_proper_->dst_of(t) != proper_s)
366  neighbors.emplace(aut_proper_->dst_of(t));
367 
368  if (dirty_s != aut_dirty_->null_state())
369  {
370  state_dirty_t n;
371  for (auto t: in(aut_dirty_, dirty_s))
372  if ((n = aut_dirty_->src_of(t)) != dirty_s)
373  neighbors.emplace(d2p_[n]);
374  for (auto t: out(aut_dirty_, dirty_s))
375  if ((n = aut_dirty_->dst_of(t)) != dirty_s)
376  neighbors.emplace(d2p_[n]);
377 
378  remover_on(proper_s, dirty_s);
379  }
380 
381  // Update all neighbors and then the heap.
382  for (auto n: neighbors)
383  update_profile_(n);
384  for (auto n: neighbors)
385  update_heap_(n);
386  if (1 < debug_)
387  std::cerr << " #tr: "
388  << transitions(aut_dirty_).size()
389  << "/" << (transitions(aut_dirty_).size()
390  + transitions(aut_proper_).size())
391  << std::endl;
392  if (2 < debug_)
393  {
394  std::cerr << "After: ";
395  show_heap_();
396  std::cerr << std::endl;
397  }
398  if (4 < debug_)
399  {
400  dot(aut_proper_, std::cerr) << std::endl;
401  dot(aut_dirty_, std::cerr) << std::endl;
402  }
403  if (2 < debug_)
404  std::cerr << std::endl;
405  }
406 #ifdef STATS
407  if (0 < debug_)
408  std::cerr << "Total transitions -" << removed_
409  << "+" << added_ << std::endl;
410 #endif
411 
412  return aut_proper_;
413  }
414 
417  {
418  return aut_proper_;
419  }
420 
421  private:
423  int debug_;
424 
428 
432 
434  const weightset_t& ws_;
435 
437  using heap_t = boost::heap::fibonacci_heap<profile_t>;
440  std::unordered_map<state_proper_t, typename heap_t::handle_type> handles_;
441 
443  bool prune_;
444 
445  std::vector<state_proper_t> d2p_; // dirty states -> proper states
446  std::vector<state_dirty_t> p2d_; // proper states -> dirty states
447  };
448 
449  template <Automaton Aut>
450  class epsilon_remover_separate<Aut, false>
451  {
452  using automaton_t = std::remove_cv_t<Aut>;
454  public:
456  : aut_(aut)
457  {}
458 
460  {
461  return copy(aut_);
462  }
463 
465  {
466  return aut_;
467  }
468 
469  private:
471  };
472  }
473 } // namespace vcsn
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s.
Definition: automaton.hh:37
boost::heap::fibonacci_heap< profile_t > heap_t
Max-heap to decide the order of state-elimination.
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:251
profile_t * profile_(state_proper_t s) const
The profile of state s, or nullptr if it is not to be processed.
Container::value_type back(const Container &container)
The last member of this Container.
Definition: algorithm.hh:27
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
std::shared_ptr< detail::mutable_automaton_impl< Context >> mutable_automaton
Definition: fwd.hh:25
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
fresh_automaton_t_of< Aut, proper_ctx_t > aut_proper_t
Proper automaton.
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:55
void remover_on(state_proper_t proper_s, state_dirty_t dirty_s)
For each state (s), for each incoming epsilon-transitions (t), if (t) is a loop, the star of its weig...
void build_heap_()
Build the profiles and the heap for states with incoming spontaneous transitions. ...
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
std::unordered_map< state_proper_t, typename heap_t::handle_type > handles_
Map: state -> heap-handle.
void update_heap_(state_proper_t s)
Update the heap for s.
This class contains the core of the proper algorithm.
epsilon_remover_separate(const automaton_t &aut, bool prune=true)
Get ready to eliminate spontaneous transitions.
void remover_on(state_proper_t proper_s)
Wrapper around remover_on() which does a lookup of the dirty state.
void show_heap_() const
Show the heap, for debugging.
std::vector< typename Cont::value_type > make_vector(const Cont &cont)
The content of cont as a vector.
Definition: vector.hh:20
int debug_
Debug level. The higher, the more details are reported.
mutable_automaton< dirty_ctx_t > aut_dirty_t
Spontaneous automaton.
const weightset_t & ws_
Shorthand to the weightset.
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
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:57
bool prune_
Whether to prune states that become inaccessible.
auto transitions(const Aut &aut) -> decltype(all_transitions(aut, is_special_t< Aut >
All the transition indexes between visible states.
Definition: automaton.hh:218
aut_proper_t get_proper()
Return the proper part. Needed by lazy algorithm.
bool state_has_spontaneous_in(state_proper_t proper_s) const
Whether the given state has incoming spontaneous transitions.
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:54
void update_profile_(state_proper_t proper_s)
Update the profile of s.
static int debug_level()
Debug level set in the user's environment.
Definition: debug-level.hh:11
detail::copier< AutIn, AutOut > make_copier(const AutIn &in, AutOut &out, bool safe=true)
Build an automaton copier.
Definition: copy.hh:249
aut_proper_t operator()()
Remove all the states with incoming spontaneous transitions.
#define Automaton
Definition: automaton.hh:24
aut_dirty_t aut_dirty_
The sponteanous part.
std::ostream & dot(const Aut &aut, std::ostream &out, format fmt={})
Print an automaton in Graphviz's Dot format.
Definition: dot.hh:377
auto in(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions arriving to state s.
Definition: automaton.hh:105
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