Vcsn  2.1
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 
24 #define STATS
25 
26 namespace vcsn
27 {
28  namespace detail
29  {
34  template <typename Aut,
35  bool has_one = labelset_t_of<Aut>::has_one()>
37  {
41  using weight_t = typename weightset_t::value_t;
44  using transitions_t = std::vector<transition_t>;
45 
48 
51 
53 
54  public:
58  epsilon_remover_separate(const automaton_t& aut, bool prune = true)
59  : debug_(debug_level())
60  , ws_(*aut->weightset())
61  , prune_(prune)
62  , d2p_(aut->all_states().back() + 1, aut->null_state())
63  , p2d_(aut->all_states().back() + 1, aut->null_state())
64  {
65  auto dirty_ctx = dirty_ctx_t{{}, ws_};
66  auto proper_ctx = make_proper_context(aut->context());
67  aut_dirty_ = make_shared_ptr<aut_dirty_t>(dirty_ctx);
68  aut_proper_ = make_shared_ptr<aut_proper_t>(proper_ctx);
69 
70  auto pcopier = make_copier(aut, aut_proper_);
71  auto dcopier = make_copier(aut, aut_dirty_);
72 
73  pcopier([](state_t) { return true; },
74  [&aut](transition_t t) {
75  return !aut->labelset()->is_one(aut->label_of(t));
76  }
77  );
78  dcopier([&aut](state_t s) {
79  return (!aut->in(s, aut->labelset()->one()).empty()
80  || !aut->out(s, aut->labelset()->one()).empty());
81  },
82  [&aut](transition_t t) {
83  return aut->labelset()->is_one(aut->label_of(t));
84  }
85  );
86 
87  const auto& dorigins = dcopier.state_map();
88  const auto& porigins = pcopier.state_map();
89  for (const auto& dp : dorigins)
90  {
91  auto pp = porigins.find(dp.first);
92  assert(pp != porigins.end());
93  d2p_[dp.second] = pp->second;
94  p2d_[pp->second] = dp.second;
95  }
96  }
97 
98  private:
99 
101  void update_profile_(state_t proper_s)
102  {
103  state_t dirty_s = p2d_[proper_s];
104  if (auto p = profile(proper_s))
105  {
106  auto in_dirty = aut_dirty_->in(dirty_s).size();
107  auto in_proper = aut_proper_->in(proper_s).size();
108  auto out_dirty = aut_dirty_->out(dirty_s).size();
109  auto out_proper = aut_proper_->all_out(proper_s).size();
110 
111  p->update(in_dirty, in_proper + in_dirty,
112  out_dirty, out_proper + out_dirty);
113  }
114  }
115 
118  void build_heap_()
119  {
120  for (auto s: aut_dirty_->states())
121  // We don't care about states without incoming spontaneous
122  // transitions.
123  if (aut_dirty_->in(s).size())
124  {
125  auto proper_s = d2p_[s];
126  auto h = todo_.emplace(epsilon_profile<state_t>
127  {proper_s, 0, 0, 0, 0});
128  handles_.emplace(proper_s, h);
129  update_profile_(proper_s);
130  }
131  }
132 
134  void show_heap_() const
135  {
136  const char* sep = "";
137  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
138  i != end; ++i)
139  {
140  std::cerr << sep << *i;
141  sep = " > ";
142  }
143  }
144 
148  {
149  if (3 < debug_)
150  {
151  std::cerr << "update heap (" << s << " : ";
152  show_heap_();
153  }
154  auto i = handles_.find(s);
155  if (i != handles_.end())
156  todo_.update(i->second);
157  if (3 < debug_)
158  {
159  std::cerr << ") => ";
160  show_heap_();
161  std::cerr << std::endl;
162  }
163  }
164 
165 #ifdef STATS
166  unsigned added_ = 0;
167  unsigned removed_ = 0;
168 #endif
169 
172  {
173  auto i = handles_.find(s);
174  if (i == handles_.end())
175  return nullptr;
176  else
177  return &*i->second;
178  }
179 
193  void remover_on(state_t dirty_s, state_t proper_s)
194  {
195  const auto& tr = aut_dirty_->in(dirty_s);
196  // Iterate on a copy, as we remove these transitions in the
197  // loop.
198  transitions_t transitions{tr.begin(), tr.end()};
199  // The star of the weight of the loop on 's' (1 if no loop).
200  weight_t star = ws_.one();
201  using state_weight_t = std::pair<state_t, weight_t>;
202  std::vector<state_weight_t> closure;
203  for (auto t : transitions)
204  {
205  weight_t weight = aut_dirty_->weight_of(t);
206  state_t src = aut_dirty_->src_of(t);
207  if (src == dirty_s) //loop
208  star = ws_.star(weight);
209  else
210  closure.emplace_back(src, weight);
211  // Delete incoming epsilon transitions.
212  aut_dirty_->del_transition(t);
213  }
214 
215  /*
216  For each transition (t : s -- label|weight --> dst),
217  for each former
218  epsilon transition closure->first -- e|closure->second --> s
219  a transition
220  (closure->first -- label | closure->second*weight --> dst)
221  is added to the automaton (add, not set !!)
222 
223  If (s) is final with weight (weight),
224  for each former
225  epsilon transition closure->first -- e|closure->second --> s
226  pair-second * weight is added to the final weight
227  of closure->first
228  */
229 
230 
231  // TODO: factoring with lambda
232  for (auto t: aut_dirty_->out(dirty_s))
233  {
234  weight_t blow = ws_.mul(star, aut_dirty_->weight_of(t));
235  aut_dirty_->set_weight(t, blow);
236 
237  state_t dst = aut_dirty_->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_dirty_->add_transition(src, dst, {}, w);
243  }
244  }
245 
246  for (auto t: aut_proper_->all_out(proper_s))
247  {
248  weight_t blow = ws_.mul(star, aut_proper_->weight_of(t));
249  aut_proper_->set_weight(t, blow);
250 
251  label_proper_t label = aut_proper_->label_of(t);
252  state_t dst = aut_proper_->dst_of(t);
253  for (auto pair: closure)
254  {
255  state_t src = pair.first;
256  weight_t w = ws_.mul(pair.second, blow);
257  aut_proper_->add_transition(d2p_[src], dst, label, w);
258  }
259  }
260 #ifdef STATS
261  unsigned added = (aut_proper_->all_out(proper_s).size()
262  + aut_dirty_->out(dirty_s).size()) * closure.size();
263  unsigned removed = transitions.size();
264 #endif
265  if (prune_ &&
266  aut_dirty_->in(dirty_s).empty() &&
267  aut_proper_->all_in(proper_s).empty())
268  {
269 #ifdef STATS
270  removed += (aut_proper_->all_out(proper_s).size()
271  + aut_dirty_->out(dirty_s).size());
272 #endif
273  aut_proper_->del_state(proper_s);
274  aut_dirty_->del_state(dirty_s);
275  }
276 #ifdef STATS
277  added_ += added;
278  removed_ += removed;
279  if (1 < debug_)
280  std::cerr << " -" << removed << "+" << added
281  << " (-" << removed_ << "+" << added_ << ")";
282 #endif
283  }
284 
285  public:
286 
300  {
301  if (4 < debug_)
302  {
303  dot(aut_proper_, std::cerr) << std::endl;
304  dot(aut_dirty_, std::cerr) << std::endl;
305  }
306  build_heap_();
307  /* For each state (s), for each incoming epsilon-transitions
308  (t), if (t) is a loop, the star of its weight is computed,
309  otherwise, (t) is stored into the closure list. Then (t)
310  is removed. */
311 
312  // The neighbors of s: their profiles need to be updated after
313  // s was processed.
314  std::unordered_set<state_t> neighbors;
315  while (!todo_.empty())
316  {
317  if (2 < debug_)
318  {
319  std::cerr << "Before: ";
320  show_heap_();
321  std::cerr << std::endl;
322  }
323  auto p = todo_.top();
324  todo_.pop();
325  if (1 < debug_)
326  std::cerr << "Remove: " << p;
327 
328  auto proper_s = p.state;
329  auto dirty_s = p2d_[proper_s];
330  handles_.erase(proper_s);
331  neighbors.clear();
332  state_t n;
333  for (auto t: aut_proper_->in(proper_s))
334  if ((n = aut_proper_->src_of(t)) != proper_s)
335  neighbors.emplace(n);
336  for (auto t: aut_proper_->out(proper_s))
337  if ((n = aut_proper_->dst_of(t)) != proper_s)
338  neighbors.emplace(n);
339 
340  if (dirty_s != aut_dirty_->null_state())
341  {
342  for (auto t: aut_dirty_->in(dirty_s))
343  if ((n = aut_dirty_->src_of(t)) != dirty_s)
344  neighbors.emplace(d2p_[n]);
345  for (auto t: aut_dirty_->out(dirty_s))
346  if ((n = aut_dirty_->dst_of(t)) != dirty_s)
347  neighbors.emplace(d2p_[n]);
348 
349  remover_on(dirty_s, proper_s);
350  }
351 
352  // Update all neighbors and then the heap.
353  for (auto n: neighbors)
354  update_profile_(n);
355  for (auto n: neighbors)
356  update_heap_(n);
357  if (1 < debug_)
358  std::cerr << " #tr: "
359  << aut_dirty_->transitions().size()
360  << "/" << (aut_dirty_->transitions().size()
361  + aut_proper_->transitions().size())
362  << std::endl;
363  if (2 < debug_)
364  {
365  std::cerr << "After: ";
366  show_heap_();
367  std::cerr << std::endl;
368  }
369  if (4 < debug_)
370  {
371  dot(aut_proper_, std::cerr) << std::endl;
372  dot(aut_dirty_, std::cerr) << std::endl;
373  }
374  if (2 < debug_)
375  std::cerr << std::endl;
376  }
377 #ifdef STATS
378  if (0 < debug_)
379  std::cerr << "Total transitions -" << removed_
380  << "+" << added_ << std::endl;
381 #endif
382 
383  return aut_proper_;
384  }
385 
386  private:
388  int debug_;
389 
393 
395  const weightset_t& ws_;
396 
398  using heap_t = boost::heap::fibonacci_heap<epsilon_profile<state_t>>;
401  std::unordered_map<state_t, typename heap_t::handle_type> handles_;
402 
404  bool prune_;
405 
406  std::vector<state_t> d2p_; // dirty states -> proper states
407  std::vector<state_t> p2d_; // proper states -> dirty states
408  };
409 
410  template <typename Aut>
411  class epsilon_remover_separate<Aut, false>
412  {
415  public:
417  : aut_(aut)
418  {}
419 
421  {
422  return copy(aut_);
423  }
424 
425  private:
427  };
428  }
429 } // namespace vcsn
std::unordered_map< state_t, typename heap_t::handle_type > handles_
Map: state -> heap-handle.
void update_heap_(state_t s)
Update the heap for s.
static int debug_level()
Debug level set in the user's environment.
Definition: debug-level.hh:11
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
aut_proper_t aut_proper_
The automata we work on.
transition_t_of< automaton_t > transition_t
Container::value_type back(const Container &container)
The last member of this Container.
Definition: algorithm.hh:26
void remover_on(state_t dirty_s, state_t proper_s)
For each state (s), for each incoming epsilon-transitions (t), if (t) is a loop, the star of its weig...
detail::copier< AutIn, AutOut > make_copier(const AutIn &in, AutOut &out)
Build an automaton copier.
Definition: copy.hh:116
void update_profile_(state_t proper_s)
Update the profile of s.
std::string type(const automaton &a)
The implementation type of a.
Definition: others.cc:197
epsilon_profile< state_t > * profile(state_t s)
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:48
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.
typename Aut::element_type::template fresh_automaton_t< Context > fresh_automaton_t_of
Definition: traits.hh:57
int debug_
Debug level. The higher, the more details are reported.
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
std::shared_ptr< const detail::weight_base > weight
Definition: fwd.hh:86
void show_heap_() const
Show the heap, for debugging.
aut_proper_t operator()()
Remove all the states with incoming spontaneous transitions.
boost::heap::fibonacci_heap< epsilon_profile< state_t >> heap_t
Max-heap to decide the order of state-elimination.
mutable_automaton< dirty_ctx_t > aut_dirty_t
const weightset_t & ws_
Shorthand to the weightset.
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:47
fresh_automaton_t_of< Aut, proper_ctx_t > aut_proper_t
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:49
bool prune_
Whether to prune states that become inaccessible.
typename std::remove_cv< Aut >::type automaton_t
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:243
void build_heap_()
Build the profiles and the heap for states with incoming spontaneous transitions. ...
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:371
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.
std::shared_ptr< detail::mutable_automaton_impl< Context >> mutable_automaton
Definition: fwd.hh:24