Vcsn  2.1
Be Rational
shortest.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <algorithm>
4 #include <deque>
5 #include <fstream>
6 
7 #include <boost/heap/binomial_heap.hpp>
8 #include <boost/optional.hpp>
9 
10 #include <vcsn/ctx/context.hh>
11 #include <vcsn/dyn/automaton.hh>
12 #include <vcsn/dyn/fwd.hh>
13 #include <vcsn/dyn/polynomial.hh>
15 
16 namespace vcsn
17 {
18 
19  /*----------------------.
20  | shortest(automaton). |
21  `----------------------*/
22 
23  namespace detail
24  {
25  template <typename Aut>
26  class enumerater
27  {
28  public:
29  using automaton_t = Aut;
31 
35 
39 
43 
45  using datum_t = std::tuple<state_t, word_t, weight_t>;
46  struct datum_less
47  {
49  bool operator()(const datum_t& r, const datum_t& l) const
50  {
51  if (labelset_t::less(std::get<1>(l), std::get<1>(r)))
52  return true;
53  else if (labelset_t::less(std::get<1>(r), std::get<1>(l)))
54  return false;
55  else
56  return std::get<0>(l) < std::get<0>(r);
57  }
58  };
59 
63  enumerater(const automaton_t& aut)
64  : aut_(aut)
65  {}
66 
70  polynomial_t operator()(boost::optional<unsigned> num,
71  boost::optional<unsigned> len)
72  {
73  if (!num)
74  num = !len ? 1 : std::numeric_limits<unsigned>::max();
75  if (!len)
76  len = std::numeric_limits<unsigned>::max();
77 
78  return shortest_(*num, *len);
79  }
80 
81  private:
91  template <typename LabelSet = labelset_t_of<context_t>>
92  auto shortest_(unsigned num, unsigned len)
94  {
95  // Each step of propagation contributes a letter. We need to
96  // take the initial and final special characters into account.
97  if (len != std::numeric_limits<unsigned>::max())
98  len += 2;
99 
100  using queue_t = std::deque<datum_t>;
101  auto queue = queue_t{datum_t{aut_->pre(), ls_.one(), ws_.one()}};
102 
103  // The approximated behavior: the first orders to post's past.
104  polynomial_t output;
105  for (unsigned i = 0;
106  !queue.empty() && i < len && output.size() < num;
107  ++i)
108  {
109  queue_t q2;
110  for (const auto& sm: queue)
111  {
112  state_t s; word_t l; weight_t w;
113  std::tie(s, l, w) = sm;
114  for (const auto t: aut_->all_out(s))
115  {
116  auto dst = aut_->dst_of(t);
117  auto nw = ws_.mul(w, aut_->weight_of(t));
118  if (aut_->src_of(t) == aut_->pre())
119  q2.emplace_back(dst, l, std::move(nw));
120  else if (dst == aut_->post())
121  ps_.add_here(output, l, std::move(nw));
122  else
123  q2.emplace_back(dst,
124  ls_.mul(l, aut_->label_of(t)),
125  std::move(nw));
126  }
127  }
128  queue.swap(q2);
129  }
130 
131  polynomial_t res;
132  for (const auto& m: output)
133  {
134  ps_.add_here(res, m);
135  if (--num == 0)
136  break;
137  }
138  return res;
139  }
140 
145  template <typename LabelSet = labelset_t_of<context_t>>
146  auto shortest_(unsigned num, unsigned len)
148  {
149  // Benched as better than Fibonacci, Pairing and Skew Heaps.
150  // D-ary heaps and Priority Queue fail to compile.
151  using queue_t =
152  boost::heap::binomial_heap<datum_t,
153  boost::heap::compare<datum_less>>;
154 
155  auto queue = queue_t{};
156  queue.emplace(aut_->pre(), ls_.one(), ws_.one());
157 
158  // The approximated behavior: the first orders to post's past.
159  polynomial_t res;
160  while (!queue.empty())
161  {
162  state_t s; word_t l; weight_t w;
163  std::tie(s, l, w) = queue.top();
164 
165  // Take all the top of the queue if they have the same
166  // label and state: sum the weights.
167  //
168  // Benches show that this is way more efficient than
169  // trying to update matching datum_t in the queue, even if
170  // we try to take advantage of the ordering in the heap.
171  // Here, we benefit from the fact that the queue provides
172  // matching datum_t in a row.
173  queue.pop();
174 
175  while (!queue.empty()
176  && std::get<0>(queue.top()) == s
177  && ls_.equal(std::get<1>(queue.top()), l))
178  {
179  w = ws_.add(w, std::get<2>(queue.top()));
180  queue.pop();
181  }
182 
183  for (const auto t: aut_->all_out(s))
184  {
185  auto dst = aut_->dst_of(t);
186  auto nw = ws_.mul(w, aut_->weight_of(t));
187  if (aut_->src_of(t) == aut_->pre())
188  queue.emplace(dst, l, std::move(nw));
189  else if (dst == aut_->post())
190  ps_.add_here(res, l, std::move(nw));
191  else
192  {
193  auto nl = ls_.mul(l, aut_->label_of(t));
194  // Discard candidates that are too long.
195  if (ls_.size(nl) <= len)
196  queue.emplace(dst, std::move(nl), std::move(nw));
197  }
198  }
199 
200  // If we found enough words *and* we have completely
201  // treated the current word (there are no other copies in
202  // other states), we're done.
203  if (queue.empty()
204  || (num == res.size()
205  && !ls_.equal(std::get<1>(queue.top()), l)))
206  break;
207  }
208 
209  return res;
210  }
211 
212  private:
214  template <typename Queue>
215  void show_heap_(const Queue& q, std::ostream& os = std::cerr) const
216  {
217  const char* sep = "";
218  for (auto i = q.ordered_begin(), end = q.ordered_end();
219  i != end; ++i)
220  {
221  os << sep;
222  sep = ", ";
223  aut_->print_state_name(std::get<0>(*i), os) << ":<";
224  ws_.print(std::get<2>(*i), os) << '>';
225  ls_.print(std::get<1>(*i), os);
226  }
227  os << '\n';
228  }
229 
232  const weightset_t& ws_ = *aut_->weightset();
233  const polynomialset_t ps_ = make_word_polynomialset(aut_->context());
234  const labelset_t& ls_ = *ps_.labelset();
235  };
236  }
237 
243  template <typename Automaton>
244  inline
246  shortest(const Automaton& aut,
247  boost::optional<unsigned> num = {},
248  boost::optional<unsigned> len = {})
249  {
250  detail::enumerater<Automaton> enumerater(aut);
251  return enumerater(num, len);
252  }
253 
254 
259  template <typename Automaton>
260  inline
261  typename detail::enumerater<Automaton>::polynomial_t
262  enumerate(const Automaton& aut, unsigned len)
263  {
264  return shortest(aut, boost::none, len);
265  }
266 
267 
268  namespace dyn
269  {
270  namespace detail
271  {
273  template <typename Aut, typename Num, typename Len>
274  polynomial
275  shortest(const automaton& aut,
276  boost::optional<unsigned> num,
277  boost::optional<unsigned> len)
278  {
279  const auto& a = aut->as<Aut>();
280  auto ps = vcsn::detail::make_word_polynomialset(a->context());
281  return make_polynomial(ps, shortest(a, num, len));
282  }
283  }
284  }
285 }
const labelset_t & ls_
Definition: shortest.hh:234
bool operator()(const datum_t &r, const datum_t &l) const
Whether l < r (as this is a max heap).
Definition: shortest.hh:49
state_t_of< automaton_t > state_t
Definition: shortest.hh:42
polynomial shortest(const automaton &aut, boost::optional< unsigned > num, boost::optional< unsigned > len)
Bridge.
Definition: shortest.hh:275
labelset_t_of< polynomialset_t > labelset_t
Wordset.
Definition: shortest.hh:37
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:48
typename detail::context_t_of_impl< base_t< ValueSet >>::type context_t_of
Definition: traits.hh:45
auto shortest_(unsigned num, unsigned len) -> enable_if_t< LabelSet::is_free(), polynomial_t >
Case of free labelsets (e.g., lal or lal x lal).
Definition: shortest.hh:92
detail::enumerater< Automaton >::polynomial_t shortest(const Automaton &aut, boost::optional< unsigned > num={}, boost::optional< unsigned > len={})
The approximated behavior of an automaton.
Definition: shortest.hh:246
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:51
typename std::enable_if< Cond, T >::type enable_if_t
Definition: type_traits.hh:16
automaton_t aut_
The automaton whose behavior to approximate.
Definition: shortest.hh:231
typename polynomialset_t::value_t polynomial_t
Definition: shortest.hh:34
word_t_of< automaton_t > word_t
Definition: shortest.hh:38
std::tuple< state_t, word_t, weight_t > datum_t
Used in the case of non-free labelsets.
Definition: shortest.hh:45
typename labelset_t_of< base_t< ValueSet >>::word_t word_t_of
Definition: traits.hh:65
polynomial make_polynomial(const PolynomialSet &ps, const typename PolynomialSet::value_t &p)
Definition: polynomial.hh:88
weight_t_of< automaton_t > weight_t
Definition: shortest.hh:41
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
auto make_word_polynomialset(const Ctx &ctx) -> word_polynomialset_t< Ctx >
The polynomialset of words of a labelset (not necessarily on words itself).
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:69
weightset_t_of< automaton_t > weightset_t
Definition: shortest.hh:40
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:47
std::ostringstream os
The output stream: the corresponding C++ snippet to compile.
Definition: translate.cc:374
enumerater(const automaton_t &aut)
Prepare to compute an approximation of the behavior.
Definition: shortest.hh:63
const polynomialset_t ps_
Definition: shortest.hh:233
detail::enumerater< Automaton >::polynomial_t enumerate(const Automaton &aut, unsigned len)
The approximated behavior of an automaton.
Definition: shortest.hh:262
context_t_of< Aut > context_t
Definition: shortest.hh:30
std::shared_ptr< const detail::polynomial_base > polynomial
Definition: fwd.hh:68
auto shortest_(unsigned num, unsigned len) -> enable_if_t<!LabelSet::is_free(), polynomial_t >
Case of non free labelsets (e.g., law, lan x lan).
Definition: shortest.hh:146
void show_heap_(const Queue &q, std::ostream &os=std::cerr) const
Show the heap, for debugging.
Definition: shortest.hh:215
polynomial_t operator()(boost::optional< unsigned > num, boost::optional< unsigned > len)
The approximated behavior of the automaton.
Definition: shortest.hh:70
const weightset_t & ws_
Definition: shortest.hh:232
typename detail::weight_t_of_impl< base_t< ValueSet >>::type weight_t_of
Definition: traits.hh:50