Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
proper.hh
Go to the documentation of this file.
1 #ifndef VCSN_ALGOS_PROPER_HH
2 # define VCSN_ALGOS_PROPER_HH
3 
4 # include <stdexcept>
5 # include <type_traits>
6 # include <unordered_map>
7 # include <unordered_set>
8 # include <utility>
9 # include <vector>
10 
11 # include <boost/lexical_cast.hpp>
12 # include <boost/heap/fibonacci_heap.hpp>
13 
14 # include <vcsn/algos/copy.hh>
15 # include <vcsn/algos/dot.hh>
16 # include <vcsn/algos/fwd.hh>
18 # include <vcsn/algos/info.hh>
19 # include <vcsn/algos/is-proper.hh>
20 # include <vcsn/algos/is-valid.hh>
21 # include <vcsn/core/kind.hh>
22 # include <vcsn/misc/attributes.hh>
23 # include <vcsn/misc/direction.hh>
24 # include <vcsn/misc/star_status.hh>
25 
26 #define STATS
27 
28 namespace vcsn
29 {
30 
31  namespace detail
32  {
34  static inline
36  {
37  if (auto cp = getenv("VCSN_DEBUG"))
38  return *cp ? boost::lexical_cast<int>(cp) : 1;
39  else
40  return 0;
41  }
42 
47  template <typename Aut,
48  bool has_one = labelset_t_of<Aut>::has_one()>
49  class properer
50  {
51  using automaton_t = typename std::remove_cv<Aut>::type;
54  using weight_t = typename weightset_t::value_t;
57  using transitions_t = std::vector<transition_t>;
58 
59  public:
63  properer(automaton_t& aut, bool prune = true)
64  : debug_(debug_level())
65  , aut_(aut)
66  , ws_(*aut->weightset())
67  , empty_word_(aut->labelset()->one())
68  , prune_(prune)
69  {}
70 
85  static void proper_here(automaton_t& aut, bool prune = true)
86  {
87  if (!is_proper(aut))
88  proper_here_<weightset_t::star_status()>(aut, prune);
89  }
90 
122  static bool in_situ_remover(automaton_t& aut, bool prune = true)
123  {
124  try
125  {
126  properer p(aut, prune);
127  p.in_situ_remover_();
128  return true;
129  }
130  catch (const std::runtime_error&)
131  {
132  return false;
133  }
134  }
135 
136  private:
144  {
148  size_t in_sp;
150  size_t in_nsp;
152  size_t out_sp;
154  size_t out_nsp;
155 
164  size_t insp, size_t in,
165  size_t outsp, size_t out)
166  : state(s)
167  , in_sp(insp), in_nsp(in - insp)
168  , out_sp(outsp), out_nsp(out - outsp)
169  {}
170 
171  void update(size_t insp, size_t in,
172  size_t outsp, size_t out)
173  {
174  in_sp = insp;
175  in_nsp = in - insp;
176  out_sp = outsp;
177  out_nsp = out - outsp;
178  }
179 
184  bool operator<(const state_profile& r) const
185  {
186  // (i) First, work on those with fewer outgoing spontaneous
187  // transitions.
188  // (ii) Prefer fewer outgoing transitions.
189  // (iii) Prefer fewer incoming spontaneous transitions.
190  // (iv) Then, ensure total order using state handle.
191  //
192  // Note the inversions between lhs and rhs.
193  return (std::tie (r.out_sp, r.out_nsp, r.in_sp, state)
194  < std::tie(out_sp, out_nsp, in_sp, r.state));
195  }
196 
197  friend std::ostream& operator<<(std::ostream& o, const state_profile& p)
198  {
199  return o << p.state
200  << 'o' << p.out_sp
201  << 'O' << p.out_nsp
202  << 'i' << p.in_sp
203  << 'I' << p.in_nsp;
204  }
205  };
206 
209  {
210  if (auto p = profile(s))
211  p->update(aut_->in(s, empty_word_).size(),
212  aut_->in(s).size(),
213  aut_->out(s, empty_word_).size(),
214  aut_->all_out(s).size());
215  }
216 
219  void build_heap_()
220  {
221  for (auto s: aut_->states())
222  // We don't care about states without incoming spontaneous
223  // transitions.
224  if (auto in_sp = aut_->in(s, empty_word_).size())
225  {
226  auto in = aut_->in(s).size();
227  auto out_sp = aut_->out(s, empty_word_).size();
228  auto out = aut_->all_out(s).size();
229  auto h = todo_.emplace(state_profile
230  {s, in_sp, in, out_sp, out});
231  handles_.emplace(s, h);
232  }
233  }
234 
236  void show_heap_() const
237  {
238  const char* sep = "";
239  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
240  i != end; ++i)
241  {
242  std::cerr << sep << *i;
243  sep = " > ";
244  }
245  }
246 
250  {
251  if (3 < debug_)
252  {
253  std::cerr << "update heap (" << s << " : ";
254  show_heap_();
255  }
256  auto i = handles_.find(s);
257  if (i != handles_.end())
258  todo_.update(i->second);
259  if (3 < debug_)
260  {
261  std::cerr << ") => ";
262  show_heap_();
263  std::cerr << std::endl;
264  }
265  }
266 
267 #ifdef STATS
268  unsigned added_ = 0;
269  unsigned removed_ = 0;
270 #endif
271 
274  {
275  auto i = handles_.find(s);
276  if (i == handles_.end())
277  return nullptr;
278  else
279  {
280  // std::cerr << "Found: " << s << std::endl;
281  return &*i->second;
282  }
283  }
284 
297  {
298  const auto& tr = aut_->in(s, empty_word_);
299  // Iterate on a copy, as we remove these transitions in the
300  // loop.
301  transitions_t transitions{tr.begin(), tr.end()};
302  // The star of the weight of the loop on 's' (1 if no loop).
303  weight_t star = ws_.one();
304  using state_weight_t = std::pair<state_t, weight_t>;
305  std::vector<state_weight_t> closure;
306  for (auto t : transitions)
307  {
308  weight_t weight = aut_->weight_of(t);
309  state_t src = aut_->src_of(t);
310  if (src == s) //loop
311  star = ws_.star(weight);
312  else
313  closure.emplace_back(src, weight);
314  // Delete incoming epsilon transitions.
315  aut_->del_transition(t);
316  }
317 
318  /*
319  For each transition (t : s -- label|weight --> dst),
320  for each former
321  epsilon transition closure->first -- e|closure->second --> s
322  a transition
323  (closure->first -- label | closure->second*weight --> dst)
324  is added to the automaton (add, not set !!)
325 
326  If (s) is final with weight (weight),
327  for each former
328  epsilon transition closure->first -- e|closure->second --> s
329  pair-second * weight is added to the final weight
330  of closure->first
331  */
332  for (auto t: aut_->all_out(s))
333  {
334  // "Blowing": For each transition (or terminal arrow)
335  // outgoing from (s), the weight is multiplied by
336  // (star).
337  weight_t blow = ws_.mul(star, aut_->weight_of(t));
338  aut_->set_weight(t, blow);
339 
340  label_t label = aut_->label_of(t);
341  state_t dst = aut_->dst_of(t);
342  for (auto pair: closure)
343  {
344  state_t src = pair.first;
345  weight_t w = ws_.mul(pair.second, blow);
346  aut_->add_transition(src, dst, label, w);
347  }
348  }
349 #ifdef STATS
350  unsigned added = aut_->all_out(s).size() * closure.size();
351  unsigned removed = transitions.size();
352 #endif
353  if (prune_ && aut_->all_in(s).empty())
354  {
355 #ifdef STATS
356  removed += aut_->all_out(s).size();
357 #endif
358  aut_->del_state(s);
359  }
360 #ifdef STATS
361  added_ += added;
362  removed_ += removed;
363  if (1 < debug_)
364  std::cerr << " -" << removed << "+" << added
365  << " (-" << removed_ << "+" << added_ << ")";
366 #endif
367  }
368 
382  {
383  build_heap_();
384  /* For each state (s), for each incoming epsilon-transitions
385  (t), if (t) is a loop, the star of its weight is computed,
386  otherwise, (t) is stored into the closure list. Then (t)
387  is removed. */
388 
389  // The neighbors of s: their profiles need to be updated after
390  // s was processed.
391  std::unordered_set<state_t> neighbors;
392  while (!todo_.empty())
393  {
394  if (2 < debug_)
395  {
396  std::cerr << "Before: ";
397  show_heap_();
398  std::cerr << std::endl;
399  }
400  auto p = todo_.top();
401  todo_.pop();
402  if (1 < debug_)
403  std::cerr << "Remove: " << p;
404 
405  auto s = p.state;
406  handles_.erase(s);
407  neighbors.clear();
408  for (auto t: aut_->in(s))
409  {
410  state_t n = aut_->src_of(t);
411  if (n != s)
412  neighbors.emplace(n);
413  }
414  for (auto t: aut_->out(s))
415  {
416  state_t n = aut_->dst_of(t);
417  if (n != s)
418  neighbors.emplace(n);
419  }
420 
421  in_situ_remover_(s);
422 
423  // Update all neighbors and then the heap.
424  for (auto n: neighbors)
425  update_profile_(n);
426  for (auto n: neighbors)
427  update_heap_(n);
428  if (1 < debug_)
429  std::cerr << " #tr: "
431  << "/" << aut_->num_transitions() << std::endl;
432  if (2 < debug_)
433  {
434  std::cerr << "After: ";
435  show_heap_();
436  std::cerr << std::endl;
437  }
438  if (4 < debug_)
439  dot(aut_, std::cerr) << std::endl;
440  if (2 < debug_)
441  std::cerr << std::endl;
442  }
443 #ifdef STATS
444  if (0 < debug_)
445  std::cerr << "Total transitions -" << removed_
446  << "+" << added_ << std::endl;
447 #endif
448  }
449 
451  template <star_status_t Status>
452  static
453  typename std::enable_if<Status == star_status_t::TOPS>::type
454  proper_here_(automaton_t& aut, bool = true)
455  {
456  if (!in_situ_remover(aut))
457  raise("invalid automaton");
458  }
459 
462  template <star_status_t Status>
463  static
464  typename std::enable_if<Status == star_status_t::ABSVAL>::type
465  proper_here_(automaton_t& aut, bool prune = true)
466  {
467  require(is_valid(aut), "invalid automaton");
468  in_situ_remover(aut, prune);
469  }
470 
472  template <star_status_t Status>
473  static
474  typename std::enable_if<Status == star_status_t::STARRABLE>::type
475  proper_here_(automaton_t& aut, bool prune = true)
476  {
477  in_situ_remover(aut, prune);
478  }
479 
484  template <star_status_t Status>
485  static
486  typename std::enable_if<Status == star_status_t::NON_STARRABLE>::type
487  proper_here_(automaton_t& aut, bool prune = true)
488  {
489  require(is_valid(aut), "invalid automaton");
490  in_situ_remover(aut, prune);
491  }
492 
493  private:
495  int debug_;
499  const weightset_t& ws_;
502 
504  using heap_t = boost::heap::fibonacci_heap<state_profile>;
507  std::unordered_map<state_t, typename heap_t::handle_type> handles_;
508 
510  bool prune_;
511  };
512 
513 
514  /*----------------------------------------------.
515  | Specialization when there is no 'one' label. |
516  `----------------------------------------------*/
517 
518  template <typename Aut>
519  class properer<Aut, false>
520  {
521  using automaton_t = typename std::remove_cv<Aut>::type;
522  public:
523  static
524 #ifndef __clang__
525  constexpr
526 #endif
527  void proper_here(automaton_t&, bool = true)
528  {}
529  };
530 
531  }
532 
533 
534  /*---------.
535  | proper. |
536  `---------*/
537 
540  template <typename Aut>
541  inline
542  bool in_situ_remover(Aut& aut, bool prune)
543  {
544  return detail::properer<Aut>::in_situ_remover(aut, prune);
545  }
546 
549  template <typename Aut>
550  inline
552  bool prune = true)
553  {
554  switch (dir)
555  {
556  case direction::backward:
558  return;
559  case direction::forward:
560  auto tr_aut = transpose(aut);
561  using tr_aut_t = decltype(tr_aut);
563  return;
564  }
565  }
566 
567 
574  template <typename LabelSet>
576  {
577  using type = LabelSet;
578  static std::shared_ptr<const type>
579  value(const std::shared_ptr<const LabelSet>& ls)
580  {
581  return ls;
582  }
583  };
584 
585  template <typename LabelSet>
587  {
588  using type = LabelSet;
589  static std::shared_ptr<const type>
590  value(const std::shared_ptr<const nullableset<LabelSet>>& ls)
591  {
592  return ls->labelset();
593  }
594  };
595 
597  template <typename LabelSet, typename WeightSet>
598  auto
601  {
603  std::shared_ptr<const typename proper_labelset::type> ls
604  = proper_labelset::value(ctx.labelset());
605  std::shared_ptr<const WeightSet> ws = ctx.weightset();
606  return {ls, ws};
607  }
608 
611  template <typename Aut>
612  auto
613  proper(const Aut& aut, direction dir = direction::backward,
614  bool prune = true)
615  -> mutable_automaton<decltype(proper_context(copy(aut)->context()))>
616  {
617  // FIXME: We could avoid copying if the automaton is already
618  // proper.
619  auto a = copy(aut);
620  proper_here(a, dir, prune);
621  auto ctx = proper_context(a->context());
622  auto res = make_mutable_automaton(ctx);
623  copy_into(a, res);
624  return res;
625  }
626 
627  namespace dyn
628  {
629  namespace detail
630  {
632  template <typename Aut, typename Dir, typename Bool>
633  automaton proper(const automaton& aut, direction dir, bool prune)
634  {
635  const auto& a = aut->as<Aut>();
636  return make_automaton(::vcsn::proper(a, dir, prune));
637  }
638 
640  (const automaton& aut, direction dir, bool prune)
641  -> automaton);
642  }
643 
644  }
645 
646 } // namespace vcsn
647 
648 #endif // !VCSN_ALGOS_PROPER_HH
typename weightset_t::value_t weight_t
Definition: proper.hh:54
static constexpr void proper_here(automaton_t &, bool=true)
Definition: proper.hh:527
static std::enable_if< Status==star_status_t::ABSVAL >::type proper_here_(automaton_t &aut, bool prune=true)
ABSVAL: valid iff proper succeeds on the "absolute value" of the automaton.
Definition: proper.hh:465
static std::enable_if< Status==star_status_t::TOPS >::type proper_here_(automaton_t &aut, bool=true)
TOPS: valid iff proper succeeds.
Definition: proper.hh:454
void show_heap_() const
Show the heap, for debugging.
Definition: proper.hh:236
mutable_automaton< Context > make_mutable_automaton(const Context &ctx)
size_t out_sp
Number of outgoing spontaneous transitions.
Definition: proper.hh:152
automaton proper(const automaton &aut, direction dir, bool prune)
Bridge.
Definition: proper.hh:633
REGISTER_DECLARE(accessible,(const automaton &) -> automaton)
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:71
static int debug_level()
Debug level set in the user's environment.
Definition: proper.hh:35
transition_t_of< automaton_t > transition_t
Definition: proper.hh:56
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:34
typename std::remove_cv< Aut >::type automaton_t
Definition: proper.hh:521
From a labelset, its non-nullable labelset.
Definition: proper.hh:575
size_t out_nsp
Number of outgoing non-spontaneous transitions.
Definition: proper.hh:154
auto proper(const Aut &aut, direction dir=direction::backward, bool prune=true) -> mutable_automaton< decltype(proper_context(copy(aut) ->context()))>
Eliminate spontaneous transitions.
Definition: proper.hh:613
properer(automaton_t &aut, bool prune=true)
Get ready to eliminate spontaneous transitions.
Definition: proper.hh:63
int debug_
Debug level. The higher, the more details are reported.
Definition: proper.hh:495
auto proper_context(const context< LabelSet, WeightSet > &ctx) -> context< typename proper_labelset< LabelSet >::type, WeightSet >
From a context, its non-nullable context.
Definition: proper.hh:599
void in_situ_remover_()
Remove all the states with incoming spontaneous transitions.
Definition: proper.hh:381
Implementation of labels are nullables (letter or empty).
Definition: fwd.hh:13
std::shared_ptr< const detail::weight_base > weight
Definition: fwd.hh:82
Looking downstream.
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:214
automaton make_automaton(const Aut &aut)
Build a dyn::automaton.
Definition: automaton.hh:77
std::shared_ptr< detail::mutable_automaton_impl< Context >> mutable_automaton
Definition: fwd.hh:25
bool is_valid(const Aut &aut)
Definition: is-valid.hh:138
state_profile * profile(state_t s)
Definition: proper.hh:273
void build_heap_()
Build the profiles and the heap for states with incoming spontaneous transitions. ...
Definition: proper.hh:219
bool in_situ_remover(Aut &aut, bool prune=true)
Blindly eliminate epsilon transitions without checking for the validity of the automaton.
Definition: proper.hh:542
friend std::ostream & operator<<(std::ostream &o, const state_profile &p)
Definition: proper.hh:197
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...
Definition: proper.hh:296
void update_heap_(state_t s)
Update the heap for s.
Definition: proper.hh:249
bool is_proper(const Aut &aut)
Test whether an automaton is proper.
Definition: is-proper.hh:49
AutOut copy(const AutIn &input, Pred keep_state)
A copy of input keeping only its states that are accepted by keep_state.
Definition: copy.hh:171
std::vector< transition_t > transitions_t
Definition: proper.hh:57
void proper_here(Aut &aut, direction dir=direction::backward, bool prune=true)
Eliminate spontaneous transitions in place.
Definition: proper.hh:551
automaton_t & aut_
The automaton we work on.
Definition: proper.hh:497
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:33
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:38
label_t_of< automaton_t > label_t
Definition: proper.hh:55
static std::enable_if< Status==star_status_t::NON_STARRABLE >::type proper_here_(automaton_t &aut, bool prune=true)
NON_STARRABLE: valid iff there is no epsilon-circuit with weight zero.
Definition: proper.hh:487
static std::shared_ptr< const type > value(const std::shared_ptr< const nullableset< LabelSet >> &ls)
Definition: proper.hh:590
This class contains the core of the proper algorithm.
Definition: proper.hh:49
state_t_of< automaton_t > state_t
Definition: proper.hh:52
weightset_t_of< automaton_t > weightset_t
Definition: proper.hh:53
size_t num_eps_transitions(const Aut &)
Definition: info.hh:205
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:36
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:46
size_t in_nsp
Number of incoming non-spontaneous transitions.
Definition: proper.hh:150
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:36
std::shared_ptr< const detail::context_base > context
Definition: context.hh:71
state_profile(state_t s, size_t insp, size_t in, size_t outsp, size_t out)
Generate a state profile.
Definition: proper.hh:163
const weightset_t & ws_
Shorthand to the weightset.
Definition: proper.hh:499
state_t state
From the heap's top, recover state to eliminate.
Definition: proper.hh:146
static void proper_here(automaton_t &aut, bool prune=true)
Remove the epsilon-transitions of the input.
Definition: proper.hh:85
Aut transpose(const transpose_automaton< Aut > &aut)
Definition: transpose.hh:230
static std::enable_if< Status==star_status_t::STARRABLE >::type proper_here_(automaton_t &aut, bool prune=true)
STARRABLE: always valid.
Definition: proper.hh:475
bool prune_
Whether to prune states that become inaccessible.
Definition: proper.hh:510
void copy_into(const AutIn &in, AutOut &out, Pred keep_state)
Copy an automaton.
Definition: copy.hh:101
direction
Orientation.
Definition: direction.hh:10
std::unordered_map< state_t, typename heap_t::handle_type > handles_
Map: state -> heap-handle.
Definition: proper.hh:507
bool operator<(const state_profile &r) const
Whether l < r for the max-heap.
Definition: proper.hh:184
typename std::remove_cv< Aut >::type automaton_t
Definition: proper.hh:51
Looking upstream.
static std::shared_ptr< const type > value(const std::shared_ptr< const LabelSet > &ls)
Definition: proper.hh:579
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:35
boost::heap::fibonacci_heap< state_profile > heap_t
Max-heap to decide the order of state-elimination.
Definition: proper.hh:504
void update(size_t insp, size_t in, size_t outsp, size_t out)
Definition: proper.hh:171
size_t in_sp
Number of incoming spontaneous transitions.
Definition: proper.hh:148
label_t empty_word_
Shorthand to the empty word.
Definition: proper.hh:501
Data needed to compare the elimination order between states.
Definition: proper.hh:143
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:344
static bool in_situ_remover(automaton_t &aut, bool prune=true)
The core of the (backward) epsilon-removal.
Definition: proper.hh:122
void update_profile_(state_t s)
Update the profile of s.
Definition: proper.hh:208
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:39