Vcsn  2.8
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/algos/is-acyclic.hh>
12 #include <vcsn/ctx/context.hh>
13 #include <vcsn/dyn/automaton.hh>
14 #include <vcsn/dyn/fwd.hh>
15 #include <vcsn/dyn/value.hh>
17 
18 namespace vcsn
19 {
20 
21  /*----------------------.
22  | shortest(automaton). |
23  `----------------------*/
24 
25  namespace detail
26  {
28  template <Automaton Aut>
29  class enumerater
30  {
31  public:
32  using automaton_t = Aut;
34 
38 
42 
46 
48  using profile_t = std::tuple<state_t, word_t, weight_t>;
49  struct profile_less
50  {
52  bool operator()(const profile_t& r, const profile_t& l) const
53  {
54  if (labelset_t::less(std::get<1>(l), std::get<1>(r)))
55  return true;
56  else if (labelset_t::less(std::get<1>(r), std::get<1>(l)))
57  return false;
58  else
59  return std::get<0>(l) < std::get<0>(r);
60  }
61  };
62 
66  enumerater(const automaton_t& aut)
67  : aut_(aut)
68  {}
69 
74  polynomial_t operator()(boost::optional<unsigned> num,
75  boost::optional<unsigned> len)
76  {
77  if (!num)
78  num = !len ? 1 : std::numeric_limits<unsigned>::max();
79  if (!len)
80  len = std::numeric_limits<unsigned>::max();
81 
82  return shortest_(*num, *len);
83  }
84 
91  {
92  return shortest_(1, std::numeric_limits<unsigned>::max(), src, dst);
93  }
94 
95  private:
105  template <typename LabelSet = labelset_t_of<context_t>>
106  auto shortest_(unsigned num, unsigned len,
107  state_t_of<Aut> src = Aut::element_type::pre(),
108  state_t_of<Aut> dst = Aut::element_type::post())
109  -> std::enable_if_t<LabelSet::is_free(), polynomial_t>
110  {
111  // Each step of propagation contributes a letter. We need to
112  // take the initial and final special characters into account.
113  if (len != std::numeric_limits<unsigned>::max())
114  len += 2;
115 
116  using queue_t = std::deque<profile_t>;
117  auto queue = queue_t{profile_t{src, ls_.one(), ws_.one()}};
118 
119  // The approximated behavior: the first orders to post's past.
120  auto output = ps_.zero();
121  for (unsigned i = 0;
122  !queue.empty() && i < len && output.size() < num;
123  ++i)
124  {
125  queue_t q2;
126  for (const auto& sm: queue)
127  {
128  state_t s; word_t l; weight_t w;
129  std::tie(s, l, w) = sm;
130  for (const auto t: all_out(aut_, s))
131  {
132  auto t_dst = aut_->dst_of(t);
133  auto nw = ws_.mul(w, aut_->weight_of(t));
134  if (t_dst == aut_->post() && t_dst == dst)
135  ps_.add_here(output, l, std::move(nw));
136  else if (aut_->src_of(t) == aut_->pre()
137  || t_dst == aut_->post())
138  q2.emplace_back(t_dst, l, std::move(nw));
139  else if (t_dst == dst)
140  ps_.add_here(output, ls_.mul(l, aut_->label_of(t)),
141  std::move(nw));
142  else
143  q2.emplace_back(t_dst,
144  ls_.mul(l, aut_->label_of(t)),
145  std::move(nw));
146  }
147  }
148  queue.swap(q2);
149  }
150 
151  // Extract the num first terms in `output`.
152  auto res = ps_.zero();
153  for (const auto& m: output)
154  {
155  ps_.add_here(res, m);
156  if (--num == 0)
157  break;
158  }
159  return res;
160  }
161 
166  template <typename LabelSet = labelset_t_of<context_t>>
167  auto shortest_(unsigned num, unsigned len,
168  state_t_of<Aut> src = Aut::element_type::pre(),
169  state_t_of<Aut> dst = Aut::element_type::post())
170  -> std::enable_if_t<!LabelSet::is_free(), polynomial_t>
171  {
172  // Benched as better than Fibonacci, Pairing and Skew Heaps.
173  // D-ary heaps and Priority Queue fail to compile.
174  using queue_t =
175  boost::heap::binomial_heap<profile_t,
176  boost::heap::compare<profile_less>>;
177 
178  auto queue = queue_t{};
179  queue.emplace(src, ls_.one(), ws_.one());
180 
181  // The approximated behavior: the first orders to post's past.
182  auto res = ps_.zero();
183  while (!queue.empty())
184  {
185  state_t s; word_t l; weight_t w;
186  std::tie(s, l, w) = queue.top();
187 
188  // Take all the top of the queue if they have the same
189  // label and state: sum the weights.
190  //
191  // Benches show that this is way more efficient than
192  // trying to update matching profile_t in the queue, even if
193  // we try to take advantage of the ordering in the heap.
194  // Here, we benefit from the fact that the queue provides
195  // matching profile_t in a row.
196  queue.pop();
197 
198  while (!queue.empty()
199  && std::get<0>(queue.top()) == s
200  && ls_.equal(std::get<1>(queue.top()), l))
201  {
202  w = ws_.add(w, std::get<2>(queue.top()));
203  queue.pop();
204  }
205 
206  for (const auto t: all_out(aut_, s))
207  {
208  auto t_dst = aut_->dst_of(t);
209  auto nw = ws_.mul(w, aut_->weight_of(t));
210  if (t_dst == aut_->post() && t_dst == dst)
211  ps_.add_here(res, l, std::move(nw));
212  else if (aut_->src_of(t) == aut_->pre()
213  || t_dst == aut_->post())
214  queue.emplace(t_dst, l, std::move(nw));
215  else if (t_dst == dst)
216  {
217  auto nl = ls_.mul(l, aut_->label_of(t));
218  // Discard candidates that are too long.
219  if (ls_.size(nl) <= len)
220  ps_.add_here(res, std::move(nl), std::move(nw));
221  }
222  else
223  {
224  auto nl = ls_.mul(l, aut_->label_of(t));
225  // Discard candidates that are too long.
226  if (ls_.size(nl) <= len)
227  queue.emplace(t_dst, std::move(nl), std::move(nw));
228  }
229  }
230 
231  // If we found enough words *and* we have completely
232  // treated the current word (there are no other copies in
233  // other states), we're done.
234  if (queue.empty()
235  || (num == res.size()
236  && !ls_.equal(std::get<1>(queue.top()), l)))
237  break;
238  }
239 
240  return res;
241  }
242 
243  private:
245  template <typename Queue>
246  void show_heap_(const Queue& q, std::ostream& os = std::cerr) const
247  {
248  const char* sep = "";
249  for (auto i = q.ordered_begin(), end = q.ordered_end();
250  i != end; ++i)
251  {
252  os << sep;
253  sep = ", ";
254  aut_->print_state_name(std::get<0>(*i), os) << ":<";
255  ws_.print(std::get<2>(*i), os) << '>';
256  ls_.print(std::get<1>(*i), os);
257  }
258  os << '\n';
259  }
260 
264  const weightset_t& ws_ = *aut_->weightset();
266  const polynomialset_t ps_ = make_word_polynomialset(aut_->context());
268  const labelset_t& ls_ = *ps_.labelset();
269  };
270  }
271 
277  template <Automaton Aut>
279  shortest(const Aut& aut,
280  boost::optional<unsigned> num = {},
281  boost::optional<unsigned> len = {})
282  {
284  return enumerater(num, len);
285  }
286 
293  template <Automaton Aut>
295  shortest(const Aut& aut, state_t_of<Aut> src, state_t_of<Aut> dst)
296  {
298  return enumerater(src, dst);
299  }
300 
301 
306  template <Automaton Aut>
308  enumerate(const Aut& aut, unsigned len)
309  {
310  return shortest(aut, boost::none, len);
311  }
312 
313 
314  namespace dyn
315  {
316  namespace detail
317  {
319  template <Automaton Aut, typename Num, typename Len>
320  polynomial
321  shortest(const automaton& aut,
322  boost::optional<unsigned> num,
323  boost::optional<unsigned> len)
324  {
325  const auto& a = aut->as<Aut>();
326  auto ps = vcsn::detail::make_word_polynomialset(a->context());
327  return {ps, shortest(a, num, len)};
328  }
329  }
330  }
331 }
A dyn automaton.
Definition: automaton.hh:17
typename detail::weightset_t_of_impl< base_t< ValueSet > >::type weightset_t_of
Definition: traits.hh:67
void show_heap_(const Queue &q, std::ostream &os=std::cerr) const
Show the heap, for debugging.
Definition: shortest.hh:246
auto shortest_(unsigned num, unsigned len, state_t_of< Aut > src=Aut::element_type::pre(), state_t_of< Aut > dst=Aut::element_type::post()) -> std::enable_if_t<!LabelSet::is_free(), polynomial_t >
Case of non free labelsets (e.g., law, lan x lan).
Definition: shortest.hh:167
typename detail::state_t_of_impl< base_t< ValueSet > >::type state_t_of
Definition: traits.hh:64
bool operator()(const profile_t &r, const profile_t &l) const
Whether l < r (as this is a max heap).
Definition: shortest.hh:52
state_t_of< automaton_t > state_t
Definition: shortest.hh:45
labelset_t_of< polynomialset_t > labelset_t
Wordset.
Definition: shortest.hh:40
polynomial_t operator()(state_t src, state_t dst)
The approximated behavior of a part of the automaton (looks for one word of unspecified length)...
Definition: shortest.hh:90
polynomial_t operator()(boost::optional< unsigned > num, boost::optional< unsigned > len)
The approximated behavior of the automaton.
Definition: shortest.hh:74
value_impl< detail::polynomial_tag > polynomial
Definition: fwd.hh:33
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s.
Definition: automaton.hh:67
typename detail::context_t_of_impl< base_t< ValueSet > >::type context_t_of
Definition: traits.hh:61
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
word_t_of< automaton_t > word_t
Definition: shortest.hh:41
const labelset_t & ls_
Shorthand to the (word) labelset.
Definition: shortest.hh:268
std::ostringstream os
The output stream: the corresponding C++ snippet to compile.
Definition: translate.cc:404
typename detail::labelset_t_of_impl< base_t< ValueSet > >::type labelset_t_of
Definition: traits.hh:63
Definition: a-star.hh:8
std::tuple< state_t, word_t, weight_t > profile_t
Used in the case of non-free labelsets.
Definition: shortest.hh:48
enumerater(const automaton_t &aut)
Prepare to compute an approximation of the behavior.
Definition: shortest.hh:66
const polynomialset_t ps_
Shorthand to the polynomialset of words.
Definition: shortest.hh:266
detail::enumerater< Aut >::polynomial_t enumerate(const Aut &aut, unsigned len)
The approximated behavior of an automaton.
Definition: shortest.hh:308
detail::enumerater< Aut >::polynomial_t shortest(const Aut &aut, boost::optional< unsigned > num={}, boost::optional< unsigned > len={})
The approximated behavior of an automaton.
Definition: shortest.hh:279
weightset_t_of< automaton_t > weightset_t
Definition: shortest.hh:43
Compute the shortest words accepted by an automaton.
Definition: shortest.hh:29
context_t_of< Aut > context_t
Definition: shortest.hh:33
auto & as()
Extract wrapped typed automaton.
Definition: automaton.hh:37
auto make_word_polynomialset(const Ctx &ctx) -> word_polynomialset_t< Ctx >
The polynomialset of words of a labelset (not necessarily on words itself).
const weightset_t & ws_
Shorthand to the weightset.
Definition: shortest.hh:264
weight_t_of< automaton_t > weight_t
Definition: shortest.hh:44
automaton_t aut_
The automaton whose behavior to approximate.
Definition: shortest.hh:262
typename polynomialset_t::value_t polynomial_t
Definition: shortest.hh:37
auto shortest_(unsigned num, unsigned len, state_t_of< Aut > src=Aut::element_type::pre(), state_t_of< Aut > dst=Aut::element_type::post()) -> std::enable_if_t< LabelSet::is_free(), polynomial_t >
Case of free labelsets (e.g., lal or lal x lal).
Definition: shortest.hh:106
typename labelset_t_of< base_t< ValueSet > >::word_t word_t_of
Definition: traits.hh:90
typename detail::weight_t_of_impl< base_t< ValueSet > >::type weight_t_of
Definition: traits.hh:66
return res
Definition: multiply.hh:399