Vcsn  2.1
Be Rational
to-expression.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <boost/heap/fibonacci_heap.hpp>
4 
5 #include <vcsn/algos/copy.hh>
6 #include <vcsn/algos/lift.hh>
8 #include <vcsn/core/rat/info.hh>
9 #include <vcsn/core/rat/size.hh>
10 #include <vcsn/dyn/label.hh>
11 #include <vcsn/misc/builtins.hh>
12 #include <vcsn/misc/getargs.hh>
13 #include <vcsn/misc/vector.hh>
14 
15 namespace vcsn
16 {
17  namespace detail
18  {
19  /*----------------.
20  | Naive profiler. |
21  `----------------*/
22 
23  template <typename Aut>
25  {
26  using automaton_t = Aut;
29 
31  : aut_(aut)
32  {}
33 
35  {
37  : state_(state)
38  {}
39 
40  bool operator<(const state_profile& rhs) const
41  {
42  return std::make_tuple(rhs.size_, rhs.has_loop_, rhs.state_)
43  < std::make_tuple(size_, has_loop_, state_);
44  }
45 
46  friend std::ostream& operator<<(std::ostream& o, const state_profile& p)
47  {
48  return o << p.state_
49  << 's' << p.size_
50  << 'l' << p.has_loop_;
51  }
52 
53  size_t size_;
55  bool has_loop_ = false;
56  };
57 
60  {
61  state_profile p = state_profile(state);
62  update(p);
63  return p;
64  }
65 
67  {}
68 
70  {
71  size_t out = 0;
72  // Since we are in LAO, there can be at most one such loop.
73  // Don't count the loops as out-degree.
74  for (auto t: aut_->all_out(p.state_))
75  if (aut_->dst_of(t) == p.state_)
76  p.has_loop_ = true;
77  else
78  ++out;
79  size_t in = aut_->all_in(p.state_).size();
80  p.size_ = in * out;
81  }
82 
84  };
85 
86  /*-------------------.
87  | Delgado profiler. |
88  `-------------------*/
89 
90  template <typename Aut>
92  {
93  using automaton_t = Aut;
96 
97  delgado_profiler(const automaton_t& aut, bool count_labels = false)
98  : aut_(aut)
99  , count_labels_(count_labels)
100  , transition_cache_(aut_->all_transitions().back() + 1, 0)
101  {}
102 
104  {
106  : state_(state)
107  {}
108 
109  bool operator<(const state_profile& rhs) const
110  {
111  return std::make_tuple(rhs.size_, rhs.state_)
112  < std::make_tuple(size_, state_);
113  }
114 
115  friend std::ostream& operator<<(std::ostream& o, const state_profile& p)
116  {
117  return o << p.state_
118  << 's' << p.size_;
119  }
120 
121  size_t size_;
123  };
124 
127  {
128  state_profile p = state_profile(state);
129  update(p);
130  return p;
131  }
132 
137  {
138  auto& res = transition_cache_[t];
139  if (res == 0)
140  {
141  using expset_t = weightset_t_of<automaton_t>;
142  if (count_labels_)
143  res = rat::make_info<expset_t>(aut_->weight_of(t)).atom;
144  else
145  res = rat::size<expset_t>(aut_->weight_of(t));
146  }
147  return res;
148  }
149 
155  {
156  if (transition_cache_.size() <= t)
157  transition_cache_.resize(t + 1, 0);
158  transition_cache_[t] = 0;
159  }
160 
166  {
167  // The cumulated size of the incoming transitions excluding loops.
168  size_t size_in = 0;
169  // The number of incoming transitions excluding loops.
170  size_t ins = 0;
171  // The size of the loop, if there is one.
172  size_t size_loop = 0;
173  for (auto t: aut_->all_in(p.state_))
174  if (aut_->src_of(t) == p.state_)
175  size_loop += size_of_transition(t);
176  else
177  {
178  ++ins;
179  size_in += size_of_transition(t);
180  }
181 
182  // The cumulated size of the outgoing transitions excluding loops.
183  size_t size_out = 0;
184  // The number of outgoing transitions excluding loops.
185  size_t outs = 0;
186  for (auto t: aut_->all_out(p.state_))
187  if (aut_->dst_of(t) != p.state_)
188  {
189  ++outs;
190  size_out += size_of_transition(t);
191  }
192 
193  p.size_ = (size_in * (outs - 1)
194  + size_out * (ins - 1)
195  + size_loop * (ins * outs - 1));
196  }
197 
200  std::vector<size_t> transition_cache_;
201  };
202  }
203 
204  /*------------------.
205  | eliminate_state. |
206  `------------------*/
207 
208  namespace detail
209  {
211  template <typename Aut, typename Profiler>
213  {
214  using profiler_t = Profiler;
215  using profile_t = typename profiler_t::state_profile;
218 
220  : aut_(aut)
221  , profiler_(profiler)
222  , handles_(aut_->all_states().back() + 1)
223  {
224  for (auto s: aut_->states())
225  handles_[s] = todo_.emplace(profiler_.make_state_profile(s));
226  }
227 
230  {
231  if (s == aut_->null_state())
232  {
233  require(!todo_.empty(), "not a valid state: ", s);
234  auto p = todo_.top();
235  todo_.pop();
236  s = p.state_;
237  }
238  else
239  require(aut_->has_state(s), "not a valid state: ", s);
240  eliminate_state_(s);
241  }
242 
244  void operator()()
245  {
246  while (!todo_.empty())
247  {
248  auto p = todo_.top();
249  todo_.pop();
250 
251 #ifdef DEBUG_LOOP
252  std::cerr << "Remove: " << p << std::endl;
253 #endif
254  eliminate_state_(p.state_);
255  }
256  }
257 
258  private:
260  {
261 #ifdef DEBUG_UPDATE
262  std::cerr << "update heap: (";
263  show_heap_();
264 #endif
265  for (auto s: neighbors_)
266  if (s != aut_->pre() && s != aut_->post())
267  {
268  auto& h = handles_[s];
269  profiler_.update(*h);
270  todo_.update(h);
271  }
272 #ifdef DEBUG_UPDATE
273  std::cerr << ") => ";
274  show_heap_();
275  std::cerr << std::endl;
276 #endif
277  }
278 
280  void show_heap_() const
281  {
282  const char* sep = "";
283  for (auto i = todo_.ordered_begin(), end = todo_.ordered_end();
284  i != end; ++i)
285  {
286  std::cerr << sep << *i;
287  sep = " > ";
288  }
289  }
290 
292  template <typename Kind = typename context_t_of<automaton_t>::kind_t>
295  void>
296  {
297  neighbors_.clear();
298 
299  // Shorthand to the weightset.
300  const auto& ws_ = *aut_->weightset();
301 
302  // The loop's weight.
303  auto loop = ws_.zero();
304  assert(aut_->outin(s, s).size() <= 1);
305  // There is a single possible loop labeled by \e, but it's
306  // easier and symmetrical with LAR to use a for-loop.
307  for (auto t: make_vector(aut_->outin(s, s)))
308  {
309  loop = ws_.add(loop, aut_->weight_of(t));
310  aut_->del_transition(t);
311  }
312  loop = ws_.star(loop);
313 
314  // Get all the predecessors, and successors, except itself.
315  auto outs = aut_->all_out(s);
316  for (auto in: aut_->all_in(s))
317  for (auto out: outs)
318  {
319  auto src = aut_->src_of(in);
320  auto dst = aut_->dst_of(out);
321  auto t = aut_->add_transition
322  (src, dst,
323  aut_->label_of(in),
324  ws_.mul(aut_->weight_of(in), loop, aut_->weight_of(out)));
325  profiler_.invalidate_cache(t);
326  neighbors_.emplace(src);
327  neighbors_.emplace(dst);
328  }
329 
330  aut_->del_state(s);
331  update_heap_();
332  }
333 
335  //
336  // FIXME: expressionset<lal_char(a-c), z>, q for instance cannot
337  // work, because we need to move the q weights inside the
338  // lal_char(a-c), z expressions, which obviously not possible.
339  // So we need to require that there is a subtype relationship
340  // between the weightset and the weightset of the expression.
341  //
342  // Yet as of 2014-07, there is no means to check that subtype
343  // relationship in Vcsn.
344  template <typename Kind>
347  void>
348  {
349  neighbors_.clear();
350 
351  // Shorthand to the labelset, which is an expressionset.
352  const auto& rs_ = *aut_->labelset();
353 
354  // The loops' expression.
355  auto loop = rs_.zero();
356  for (auto t: make_vector(aut_->outin(s, s)))
357  {
358  loop = rs_.add(loop,
359  rs_.lmul(aut_->weight_of(t), aut_->label_of(t)));
360  aut_->del_transition(t);
361  }
362  loop = rs_.star(loop);
363 
364  // Get all the predecessors, and successors, except itself.
365  auto outs = aut_->all_out(s);
366  for (auto in: aut_->all_in(s))
367  for (auto out: outs)
368  {
369  auto src = aut_->src_of(in);
370  auto dst = aut_->dst_of(out);
371  auto t = aut_->add_transition
372  (src, dst,
373  rs_.mul(rs_.lmul(aut_->weight_of(in), aut_->label_of(in)),
374  loop,
375  rs_.lmul(aut_->weight_of(out), aut_->label_of(out))));
376  profiler_.invalidate_cache(t);
377  neighbors_.emplace(src);
378  neighbors_.emplace(dst);
379  }
380 
381  aut_->del_state(s);
382  update_heap_();
383  }
384 
389 
391  using heap_t = boost::heap::fibonacci_heap<profile_t>;
394  std::vector<typename heap_t::handle_type> handles_;
395 
396  std::unordered_set<state_t> neighbors_;
397  };
398 
399  template <typename Aut, typename Profiler>
401  make_state_eliminator(Aut& a, Profiler& profiler)
402  {
403  return state_eliminator<Aut, Profiler>(a, profiler);
404  }
405  }
406 
407 
409  template <typename Aut>
410  Aut&
412  state_t_of<Aut> s = Aut::element_type::null_state())
413  {
415  auto profiler = detail::naive_profiler<Aut>(res);
416  auto eliminate_state = detail::make_state_eliminator(res, profiler);
417  eliminate_state(s);
418  return res;
419  }
420 
422  template <typename Aut>
423  auto
424  eliminate_state(const Aut& aut,
425  state_t_of<Aut> s = Aut::element_type::null_state())
427  {
428  // Get a copy, but be sure to keep the correspondance bw original
429  // state numbers and the new ones.
430  auto res = make_fresh_automaton<Aut>(aut);
431  auto copy = make_copier(aut, res);
432  copy();
433  if (s != aut->null_state())
434  {
435  require(aut->has_state(s), "not a valid state: ", s);
436  s = copy.state_map().at(s);
437  }
438  return eliminate_state_here(res, s);
439  }
440 
441 
442  /*-----------------------.
443  | dyn::eliminate_state. |
444  `-----------------------*/
445 
446  namespace dyn
447  {
448  namespace detail
449  {
451  template <typename Aut, typename Int>
452  automaton
453  eliminate_state(const automaton& aut, int state)
454  {
455  const auto& a = aut->as<Aut>();
456  auto s = 0 <= state ? state + 2 : a->null_state();
458  }
459  }
460  }
461 
462 
463  /*----------------------------.
464  | to_expression(automaton). |
465  `----------------------------*/
466 
467  template <typename Aut,
468  typename Profiler,
469  typename ExpSet = expressionset<context_t_of<Aut>>>
470  typename ExpSet::value_t
471  to_expression(Aut& a, Profiler& profiler)
472  {
473  auto eliminate_states = detail::make_state_eliminator(a, profiler);
474  eliminate_states();
475  return a->get_initial_weight(a->post());
476  }
477 
479  {
480  best,
481  delgado,
483  naive,
484  };
485 
486  template <typename Aut,
487  typename ExpSet = expressionset<context_t_of<Aut>>>
488  typename ExpSet::value_t
491  {
492  // State elimination is performed on the lifted automaton.
493  auto a = lift(aut, ids);
494  using delgado_t = detail::delgado_profiler<decltype(a)>;
495  using naive_t = detail::naive_profiler<decltype(a)>;
496  switch (algo)
497  {
499  raise("next_state: invalid algorithm: best");
500 
502  {
503  auto profiler = delgado_t(a);
504  return to_expression<decltype(a), delgado_t, ExpSet>(a, profiler);
505  }
506 
508  {
509  auto profiler = delgado_t(a, true);
510  return to_expression<decltype(a), delgado_t, ExpSet>(a, profiler);
511  }
512 
514  {
515  auto profiler = naive_t(a);
516  return to_expression<decltype(a), naive_t, ExpSet>(a, profiler);
517  }
518  }
520  }
521 
522  template <typename Aut,
523  typename ExpSet = expressionset<context_t_of<Aut>>>
524  typename ExpSet::value_t
527  {
529  {
530  typename ExpSet::value_t best;
531  auto best_size = std::numeric_limits<size_t>::max();
535  {
536  auto r = to_expression_heuristic<Aut, ExpSet>(aut, ids, a);
537  auto s = rat::size<ExpSet>(r);
538  if (s < best_size)
539  {
540  best = r;
541  best_size = s;
542  }
543  }
544  return best;
545  }
546  else
547  {
548  return to_expression_heuristic<Aut, ExpSet>(aut, ids, algo);
549  }
550  }
551 
552  template <typename Aut,
553  typename ExpSet = expressionset<context_t_of<Aut>>>
554  typename ExpSet::value_t
556  const std::string& algo)
557  {
558  static const auto map = std::map<std::string, to_expression_heuristic_t>
559  {
563  {"delgado_label", to_expression_heuristic_t::delgado_label},
565  };
566  return to_expression<Aut, ExpSet>(a, ids, getargs("algorithm", map, algo));
567  }
568 
569  /*----------------------------.
570  | to_expression(automaton). |
571  `----------------------------*/
572 
573  namespace dyn
574  {
575  namespace detail
576  {
578  template <typename Aut, typename Identities, typename String>
579  expression
581  const std::string& algo)
582  {
583  const auto& a = aut->as<Aut>();
584  using context_t = context_t_of<Aut>;
585  using expressionset_t = vcsn::expressionset<context_t>;
586  auto rs = expressionset_t{a->context(), ids};
587  auto res = ::vcsn::to_expression(a, ids, algo);
588  return make_expression(rs, res);
589  }
590  }
591  }
592 
593  /*------------------------.
594  | to_expression(label). |
595  `------------------------*/
596 
597  namespace dyn
598  {
599  namespace detail
600  {
602  template <typename Context, typename Identities, typename Label>
603  expression
605  const label& lbl)
606  {
607  const auto& c = ctx->as<Context>();
608  const auto& l = lbl->as<Label>();
609  auto rs = vcsn::make_expressionset(c, ids);
610  return make_expression(rs, rs.atom(l.label()));
611  }
612  }
613  }
614 
615 
616  /*-------------------------------.
617  | to_expression(letter_class). |
618  `-------------------------------*/
619 
620  namespace detail
621  {
622  template <typename ExpSet>
623  auto letter_class_impl(const ExpSet&,
624  const letter_class_t&, bool,
625  std::false_type, std::true_type)
626  -> typename ExpSet::value_t
627  {
628  raise("letter_class: not implemented (is_expressionset)");
629  }
630 
631  template <typename ExpSet>
632  auto letter_class_impl(const ExpSet& rs,
633  const letter_class_t& chars, bool accept,
634  std::false_type, std::false_type)
635  -> typename ExpSet::value_t
636  {
637  auto ls = *rs.labelset();
638 
639  using labelset_t = decltype(ls);
640  using letter_t = typename labelset_t::letter_t;
641 
642  auto ccs = std::set<std::pair<letter_t, letter_t>>{};
643  for (const auto& cc: chars)
644  {
645  std::istringstream i1{cc.first};
646  std::istringstream i2{cc.second};
647  letter_t l1 = ls.get_letter(i1, false);
648  letter_t l2 = ls.get_letter(i2, false);
649  ccs.emplace(l1, l2);
650  }
651  return rs.letter_class(ccs, accept);
652  }
653 
654  template <typename ExpSet>
655  auto letter_class_impl(const ExpSet& rs,
656  const letter_class_t&, bool,
657  std::true_type, std::false_type)
658  -> typename ExpSet::value_t
659  {
660  return rs.one();
661  }
662  }
663 
673  template <typename ExpressionSet>
674  typename ExpressionSet::value_t
675  to_expression(const ExpressionSet& rs, const letter_class_t& letters,
676  bool accept = true)
677  {
678  using labelset_t = labelset_t_of<ExpressionSet>;
679  using is_one_t = std::is_same<labelset_t, vcsn::oneset>;
681  return detail::letter_class_impl(rs, letters, accept,
682  is_one_t{}, is_expset_t{});
683  }
684 
685  namespace dyn
686  {
687  namespace detail
688  {
690  template <typename Context, typename Identities,
691  typename Letters, typename Bool>
692  expression
694  const letter_class_t& letters, bool accept)
695  {
696  const auto& c = ctx->as<Context>();
697  auto rs = vcsn::make_expressionset(c, ids);
698  return make_expression(rs, to_expression(rs, letters, accept));
699  }
700  }
701  }
702 } // vcsn::
std::vector< typename Cont::value_type > make_vector(const Cont &cont)
The content of cont as a vector.
Definition: vector.hh:20
state_t_of< automaton_t > state_t
size_t size_of_transition(transition_t t)
The "weight" of a transition.
expression to_expression(const automaton &aut, vcsn::rat::identities ids, const std::string &algo)
Bridge.
detail::lifted_automaton_t< Aut, Tapes...> lift(const Aut &a, vcsn::rat::identities ids={})
Lift some tapes of the transducer.
Definition: lift.hh:215
void operator()(state_t s)
Eliminate state s.
void invalidate_cache(transition_t)
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:59
void show_heap_() const
Show the heap, for debugging.
weightset_mixin< detail::r_impl > r
Definition: fwd.hh:54
ExpressionSet::value_t to_expression(const ExpressionSet &rs, const letter_class_t &letters, bool accept=true)
An expression matching one letter in a letter class.
std::shared_ptr< const detail::context_base > context
A dyn::context.
Definition: fwd.hh:41
std::vector< size_t > transition_cache_
void operator()()
Eliminate all the states, in the order specified by next_state.
Container::value_type back(const Container &container)
The last member of this Container.
Definition: algorithm.hh:26
automaton_t aut_
The automaton we work on.
detail::copier< AutIn, AutOut > make_copier(const AutIn &in, AutOut &out)
Build an automaton copier.
Definition: copy.hh:116
expression to_expression_class(const context &ctx, rat::identities ids, const letter_class_t &letters, bool accept)
Bridge (to_expression).
std::string type(const automaton &a)
The implementation type of a.
Definition: others.cc:197
C::mapped_type getargs(const std::string &kind, const C &map, const std::string &key)
Find a correspondance in a map.
Definition: getargs.hh:21
state_profile make_state_profile(state_t state)
typename detail::context_t_of_impl< base_t< ValueSet >>::type context_t_of
Definition: traits.hh:45
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
std::set< std::pair< std::string, std::string >> letter_class_t
A set of letter ranges.
Definition: fwd.hh:103
typename std::enable_if< Cond, T >::type enable_if_t
Definition: type_traits.hh:16
Aut & eliminate_state_here(Aut &res, state_t_of< Aut > s=Aut::element_type::null_state())
In place removal of state s from automaton res.
void invalidate_cache(transition_t t)
Updating transitions' size in the cache during the profiler's construction would be clearer but appea...
Eliminate states in an automaton whose labelset is oneset.
state_eliminator(automaton_t &aut, profiler_t &profiler)
naive_profiler(const automaton_t &aut)
expression to_expression_label(const context &ctx, rat::identities ids, const label &lbl)
Bridge (to_expression).
automaton eliminate_state(const automaton &aut, int state)
Bridge.
automaton make_automaton(const Aut &aut)
Build a dyn::automaton.
Definition: automaton.hh:75
static dyn::context ctx(const driver &d)
Get the context of the driver.
Definition: parse.cc:80
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
auto eliminate_state_(state_t s) -> enable_if_t< std::is_same< Kind, labels_are_one >::value, void >
Eliminate state s in the case of labels are one.
bool operator<(const state_profile &rhs) const
ExpSet::value_t to_expression_heuristic(const Aut &aut, vcsn::rat::identities ids, to_expression_heuristic_t algo)
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
boost::heap::fibonacci_heap< profile_t > heap_t
Max-heap to decide the order of state-elimination.
auto letter_class_impl(const ExpSet &, const letter_class_t &, bool, std::false_type, std::true_type) -> typename ExpSet::value_t
friend std::ostream & operator<<(std::ostream &o, const state_profile &p)
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:69
static identities ids(const driver &d)
Get the identities of the driver.
Definition: parse.cc:87
void update(state_profile &p)
The "weight" of a state, as defined by Delgado/Morais.
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:47
auto eliminate_state(const Aut &aut, state_t_of< Aut > s=Aut::element_type::null_state()) -> fresh_automaton_t_of< Aut >
A copy of automaton res without the state s.
transition_t_of< automaton_t > transition_t
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:75
typename profiler_t::state_profile profile_t
void update(state_profile &p)
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:49
expressionset< Context > make_expressionset(const Context &ctx, rat::identities identities={})
Shorthand to expressionset constructor.
auto rs
Definition: lift.hh:151
An expressionset can implement several different sets of identities on expressions.
Definition: identities.hh:21
expression make_expression(const ExpSet &rs, const typename ExpSet::value_t &r)
Definition: expression.hh:83
typename std::remove_cv< Aut >::type automaton_t
ExpSet::value_t to_expression(Aut &a, Profiler &profiler)
state_profile make_state_profile(state_t state)
std::shared_ptr< detail::expression_base > expression
Definition: expression.hh:78
state_t_of< automaton_t > state_t
state_t_of< automaton_t > state_t
std::integral_constant< bool, B > bool_constant
Definition: type_traits.hh:35
auto eliminate_state_impl_(state_t s) -> enable_if_t< std::is_same< Kind, labels_are_expressions >::value, void >
Eliminate state s in the case of labels are expressions.
state_eliminator< Aut, Profiler > make_state_eliminator(Aut &a, Profiler &profiler)
std::vector< typename heap_t::handle_type > handles_
Map: state -> heap-handle.
#define BUILTIN_UNREACHABLE()
Definition: builtins.hh:13
std::unordered_set< state_t > neighbors_
bool operator<(const state_profile &rhs) const
transition_t_of< automaton_t > transition_t
delgado_profiler(const automaton_t &aut, bool count_labels=false)
friend std::ostream & operator<<(std::ostream &o, const state_profile &p)
to_expression_heuristic_t
profiler_t & profiler_
The profiler we work with. Corresponding to a specific heuristic.