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