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