Awali
Another Weighted Automata library
product.hh
Go to the documentation of this file.
1 // This file is part of Awali.
2 // Copyright 2016-2022 Sylvain Lombardy, Victor Marsault, Jacques Sakarovitch
3 //
4 // Awali is a free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef AWALI_PRODUCT_HH
18 # define AWALI_PRODUCT_HH
19 
20 # include <iostream>
21 # include <map>
22 # include <utility>
23 
24 # include <vector>
25 # include <memory>
29 #include <awali/sttc/ctx/traits.hh>
32 
33 namespace awali {
34  namespace sttc {
35 
37  template <typename... Auts>
38  auto
39  join_automata(Auts&&... auts)
40  -> decltype(make_mutable_automaton(join(auts->context()...)))
41  {
42  return make_mutable_automaton(join(auts->context()...));
43  }
44 
46  template <typename... Auts>
47  auto
48  meet_automata(Auts&&... auts)
49  -> decltype(make_mutable_automaton(meet(auts->context()...)))
50  {
51  return make_mutable_automaton(meet(auts->context()...));
52  }
53 
54  namespace internal {
55 
56  template<typename WS, typename W1, typename W2>
57  auto variadic_mul(const WS& weightset, const W1& w1, const W2& w2)
58  -> typename WS::value_t
59  {
60  return weightset.mul(w1,w2);
61  }
62 
63  template<typename WS, typename W1, typename... W>
64  auto variadic_mul(const WS& weightset, const W1& w1, const W&... w)
65  -> typename WS::value_t
66  {
67  return weightset.mul(w1, variadic_mul(weightset, w...));
68  }
69 
70  /*----------------------------.
71  | product_algo_impl<Aut...>. |
72  `----------------------------*/
73 
75  template <typename Aut, typename... Auts>
77  {
78  static_assert(all_<labelset_t_of<Auts>::is_free()...>(),
79  "requires free labels");
80 
82  using automaton_t = Aut;
83 
84  public:
85  using tuple_t = typename std::cst_tuple<state_t, std::tuple_size<std::tuple<Auts...>>::value>::type;
86  template <std::size_t... I>
88 
91  static constexpr indices_t indices{};
92 
97 
98  using label_t = typename labelset_t::value_t;
99  using weight_t = typename weightset_t::value_t;
100 
103  template <typename A>
105 
106  public:
108  using automata_t = std::tuple<Auts...>;
109 
111  template <size_t I>
114 
115  product_algo_impl(Aut aut, const Auts&... auts)
116  : transition_maps_{{auts, *aut->weightset()}...}
117  , aut_(aut)
118  , auts_(auts...)
119  {
120  pmap_[pre_()] = aut_->pre();
121  pmap_[post_()] = aut_->post();
122  }
123 
125  tuple_t pre_() const
126  {
127  return pre_(indices);
128  }
129 
130  template <size_t... I>
132  {
133  // clang 3.4 on top of libstdc++ wants this ctor to be
134  // explicitly called.
135  return tuple_t{(std::get<I>(auts_)->pre())...};
136  }
137 
139  tuple_t post_() const
140  {
141  return post_(indices);
142  }
143 
144  template <size_t... I>
146  {
147  // clang 3.4 on top of libstdc++ wants this ctor to be
148  // explicitly called.
149  return tuple_t{(std::get<I>(auts_)->post())...};
150  }
151 
159  {
160  auto lb = pmap_.lower_bound(state);
161  if (lb == pmap_.end() || pmap_.key_comp()(state, lb->first))
162  {
163  lb = pmap_.emplace_hint(lb, state, aut_->add_state());
164  todo_.emplace_back(state);
165  }
166  return lb->second;
167  }
168 
170  void product()
171  {
172  initialize_product();
173 
174  while (!todo_.empty())
175  {
176  tuple_t psrc = todo_.front();
177  todo_.pop_front();
178  state_t src = pmap_[psrc];
179 
180  add_product_transitions(src, psrc);
181  }
182  }
183 
185  void shuffle()
186  {
187  initialize_shuffle();
188 
189  while (!todo_.empty())
190  {
191  tuple_t psrc = todo_.front();
192  todo_.pop_front();
193  state_t src = pmap_[psrc];
194 
195  add_shuffle_transitions(src, psrc);
196  }
197  }
198 
201  {
202  // Infiltrate is a mix of product and shuffle operations, and
203  // the initial states for shuffle are a superset of the
204  // initial states for product:
205  initialize_shuffle();
206 
207  while (!todo_.empty())
208  {
209  tuple_t psrc = todo_.front();
210  todo_.pop_front();
211  state_t src = pmap_[psrc];
212 
213  // Infiltrate is a mix of product and shuffle operations.
214  //
215  // Product transitions must be added before shuffle ones:
216  // this way "product" can use "new_transition" only, which
217  // is faster than "add_transition".
218  add_product_transitions(src, psrc);
219  add_shuffle_transitions(src, psrc);
220  }
221  }
222 
223  void set_history() {
224  auto history = std::make_shared<tuple_history<automata_t>>(auts_);
225  aut_->set_history(history);
226  for (const auto& p: pmap_)
227  if(p.second != aut_->pre() && p.second != aut_->post())
228  history->add_state(p.second, p.first);
229  }
230 
231  private:
234  void initialize_product()
235  {
236  todo_.emplace_back(pre_());
237  }
238 
241  void initialize_shuffle()
242  {
243  // Make the result automaton initial states: same as the
244  // (synchronized) product of pre: synchronized transitions on $.
245  add_product_transitions(aut_->pre(), pre_());
246  }
247 
249  std::tuple<typename transition_map_t<Auts>::map_t&...>
250  out_(const tuple_t& ss)
251  {
252  return out_(ss, indices);
253  }
254 
255  template <size_t... I>
256  std::tuple<typename transition_map_t<Auts>::map_t&...>
257  out_(const tuple_t& ss, seq<I...>)
258  {
259  return std::tie(std::get<I>(transition_maps_)[std::get<I>(ss)]...);
260  }
261 
266  void add_product_transitions(const state_t src,
267  const tuple_t& psrc)
268  {
269  for (auto t: zip_map_tuple(out_(psrc)))
270  // These are always new transitions: first because the
271  // source state is visited for the first time, and second
272  // because the couple (left destination, label) is unique,
273  // and so is (right destination, label).
275  ([&] (const typename transition_map_t<Auts>::transition&... ts)
276  {
277  aut_->new_transition(src, state(std::make_tuple(ts.dst...)),
278  t.first, variadic_mul(*(aut_->weightset()), ts.wgt...));
279  },
280  t.second);
281  }
282 
287  void add_shuffle_transitions(const state_t src,
288  const tuple_t& psrc)
289  {
290  weight_t final = add_shuffle_transitions_(src, psrc, indices);
291  aut_->set_final(src, final);
292  }
293 
298  template <size_t... I>
299  weight_t add_shuffle_transitions_(const state_t src,
300  const tuple_t& psrc,
301  seq<I...>)
302  {
303  weight_t res = aut_->weightset()->one();
304  using swallow = int[];
305  (void) swallow
306  {
307  (res = variadic_mul(*(aut_->weightset()), res,
308  add_shuffle_transitions_<I>(src, psrc)),
309  0)...
310  };
311  return res;
312  }
313 
319  template <size_t I>
320  weight_t
321  add_shuffle_transitions_(const state_t src,
322  const tuple_t& psrc)
323  {
324  // Whether is a final state.
325  weight_t res = aut_->weightset()->zero();
326 
327  auto& ts = std::get<I>(transition_maps_)[std::get<I>(psrc)];
328  for (auto t: ts)
329  if (std::get<I>(auts_)->labelset()->is_special(t.first))
330  res = t.second.front().wgt;
331  else
332  for (auto d: t.second) {
333  auto pdst = psrc;
334  std::get<I>(pdst) = d.dst;
335  aut_->add_transition(src, state(pdst), t.first, d.wgt);
336  }
337  return res;
338  }
339 
341  std::tuple<transition_map_t<Auts>...> transition_maps_;
343  using out_tuple_t = std::pair<size_t, state_t>;
344  using out_map_t = std::map<out_tuple_t, bool>;
345  out_map_t out_map;
346  // Output automaton
347  automaton_t aut_;
349  automata_t auts_;
350 
352  using map = std::map<tuple_t, state_t>;
353  map pmap_;
354 
356  std::deque<tuple_t> todo_;
357  };
358  }
359 
360  /*------------------------.
361  | product(automaton...). |
362  `------------------------*/
363 
365  template <typename... Auts>
366  inline
367  auto
368  k_product_no_history(const Auts&... as)
369  -> decltype(join_automata(as...))
370  {
371  auto res = join_automata(as...);
372  internal::product_algo_impl<decltype(res), Auts...> algo(res, as...);
373  algo.product();
374  return res;
375  }
376 
378  template <typename... Auts>
379  inline
380  auto
381  k_product_with_history(const Auts&... as)
382  -> decltype(join_automata(as...))
383  {
384  auto res = join_automata(as...);
385  internal::product_algo_impl<decltype(res), Auts...> algo(res, as...);
386  algo.product();
387  algo.set_history();
388  return res;
389  }
390 
391  template <typename Lhs, typename Rhs>
392  inline
393  auto
394  product(const Lhs& lhs, const Rhs& rhs, bool keep_history=true)
395  -> decltype(join_automata(lhs, rhs)) {
396  auto res = join_automata(lhs, rhs);
397  internal::product_algo_impl<decltype(res), Lhs, Rhs> algo(res, lhs, rhs);
398  algo.product();
399  if(keep_history)
400  algo.set_history();
401  if(lhs->get_name().empty() || rhs->get_name().empty()) {
402  res->set_name("product");
403  res->set_desc("Automaton obtained by a product");
404  }
405  else {
406  res->set_name("prod-"+lhs->get_name()+"-"+rhs->get_name());
407  res->set_desc("Product of "+lhs->get_name()+" and "+rhs->get_name());
408  }
409  return res;
410  }
411 
412  /*------------------------.
413  | shuffle(automaton...). |
414  `------------------------*/
415 
417  template <typename... Auts>
418  inline
419  auto
420  k_shuffle_no_history(const Auts&... as)
421  -> decltype(join_automata(as...))
422  {
423  auto res = join_automata(as...);
424  internal::product_algo_impl<decltype(res), Auts...> algo(res, as...);
425  algo.shuffle();
426  return res;
427  }
428 
430  template <typename... Auts>
431  inline
432  auto
433  k_shuffle_with_history(const Auts&... as)
434  -> decltype(join_automata(as...))
435  {
436  auto res = join_automata(as...);
437  internal::product_algo_impl<decltype(res), Auts...> algo(res, as...);
438  algo.shuffle();
439  return res;
440  }
441 
442  template <typename Lhs, typename Rhs>
443  inline
444  auto
445  shuffle(const Lhs& lhs, const Rhs& rhs, bool keep_history=true)
446  -> decltype(join_automata(lhs, rhs)) {
447  auto res = join_automata(lhs, rhs);
448  internal::product_algo_impl<decltype(res), Lhs, Rhs> algo(res, lhs, rhs);
449  algo.shuffle();
450  if(keep_history)
451  algo.set_history();
452  if(lhs->get_name().empty() || rhs->get_name().empty()) {
453  res->set_name("shuffle");
454  res->set_desc("Automaton obtained by a shuffle product");
455  }
456  else {
457  res->set_name("shuffle-"+lhs->get_name()+"-"+rhs->get_name());
458  res->set_desc("Shuffle of "+lhs->get_name()+" and "+rhs->get_name());
459  }
460  return res;
461  }
462 
463  /*-------------------------------------.
464  | infiltration(automaton, automaton). |
465  `-------------------------------------*/
466 
468  template <typename Lhs, typename Rhs>
469  inline
470  auto
471  infiltration(const Lhs& lhs, const Rhs& rhs, bool keep_history=true)
472  -> decltype(join_automata(lhs, rhs))
473  {
474  auto res = join_automata(lhs, rhs);
475  internal::product_algo_impl<decltype(res), Lhs, Rhs> algo(res, lhs, rhs);
476  algo.infiltration();
477  if(keep_history)
478  algo.set_history();
479  if(lhs->get_name().empty() || rhs->get_name().empty()) {
480  res->set_name("infiltration");
481  res->set_desc("Automaton obtained by an infiltration product");
482  }
483  else {
484  res->set_name("infilt-"+lhs->get_name()+"-"+rhs->get_name());
485  res->set_desc("Infiltration of "+lhs->get_name()+" and "+rhs->get_name());
486  }
487  return res;
488  }
489 
490  /*----------------------.
491  | power(automaton, n). |
492  `----------------------*/
493 
494  template <typename Aut>
495  auto
496  power(const Aut& aut, unsigned n)
497  -> typename Aut::element_type::automaton_nocv_t
498  {
499  auto res = make_mutable_automaton(aut->context());
500  {
501  // automatonset::one().
502  auto s = res->add_state();
503  res->set_initial(s);
504  res->set_final(s);
505  for (auto l: res->context().labelset()->genset())
506  res->new_transition(s, s, l);
507  }
508 
509  if (n)
510  {
511  // FIXME: for 1, we should return the accessible part only.
512  static bool iterative = getenv("AWALI_ITERATIVE");
513  if (iterative)
514  for (size_t i = 0; i < n; ++i)
515  res = product(res, aut, false);
516  else
517  {
518  auto power = aut;
519  while (true)
520  {
521  if (n % 2)
522  res = product(res, power, false);
523  n /= 2;
524  if (!n)
525  break;
526  power = product(power, power, false);
527  }
528  }
529  }
530 
531  return res;
532  }
533 
534 
535  }
536 }//end of ns awali::stc
537 
538 #endif // !AWALI_ALGOS_PRODUCT_HH
Build the (accessible part of the) product.
Definition: product.hh:77
tuple_t post_() const
The name of the post of the output automaton.
Definition: product.hh:139
labelset_t_of< context_t > labelset_t
Definition: product.hh:95
void set_history()
Definition: product.hh:223
void infiltration()
Compute the (accessible part of the) infiltration product.
Definition: product.hh:200
static constexpr indices_t indices
Definition: product.hh:91
std::tuple< Auts... > automata_t
The type of input automata.
Definition: product.hh:108
product_algo_impl(Aut aut, const Auts &... auts)
Definition: product.hh:115
typename weightset_t::value_t weight_t
Definition: product.hh:99
void product()
Compute the (accessible part of the) product.
Definition: product.hh:170
typename std::cst_tuple< state_t, std::tuple_size< std::tuple< Auts... > >::value >::type tuple_t
Definition: product.hh:85
weightset_t_of< context_t > weightset_t
Definition: product.hh:96
state_t state(tuple_t state)
The state in the product corresponding to a tuple of states of operands.
Definition: product.hh:158
typename labelset_t::value_t label_t
Definition: product.hh:98
base_t< typename std::tuple_element< I, automata_t >::type > input_automaton_t
The type of the Ith input automaton, unqualified.
Definition: product.hh:113
tuple_t pre_() const
The name of the pre of the output automaton.
Definition: product.hh:125
tuple_t post_(seq< I... >) const
Definition: product.hh:145
context_t_of< Aut > context_t
The context of the result.
Definition: product.hh:94
void shuffle()
Compute the (accessible part of the) shuffle product.
Definition: product.hh:185
tuple_t pre_(seq< I... >) const
Definition: product.hh:131
The semiring of Natural numbers.
Definition: n.hh:34
weightset_description weightset(const std::string &k)
any_t weight_t
Type for (transition) weights; it is an alias to any_t since the its precise type depends on the weig...
Definition: typedefs.hh:61
auto map(const std::tuple< Ts... > &ts, Fun f) -> decltype(map_tuple_(f, ts, make_index_sequence< sizeof...(Ts)>()))
Map a function on a tuple, return tuple of the results.
Definition: tuple.hh:134
Definition: tuple.hh:43
static constexpr TOP< void > value
Definition: priority.hh:93
cross_sequences< Sequences... > cross_tuple(const std::tuple< Sequences... > &seqs)
Definition: cross.hh:264
typename std::remove_cv< typename std::remove_reference< T >::type >::type base_t
T without reference or const/volatile qualifiers.
Definition: traits.hh:30
auto variadic_mul(const WS &weightset, const W1 &w1, const W2 &w2) -> typename WS::value_t
Definition: product.hh:57
zipped_maps< Dereference, Maps... > zip_map_tuple(const std::tuple< Maps... > &maps)
Definition: zip_maps.hh:281
auto product(const Lhs &lhs, const Rhs &rhs, bool keep_history=true) -> decltype(join_automata(lhs, rhs))
Definition: product.hh:394
auto power(const Aut &aut, unsigned n) -> typename Aut::element_type::automaton_nocv_t
Definition: product.hh:496
auto join(const ratexpset< Ctx1 > &a, const ratexpset< Ctx2 > &b) -> ratexpset< join_t< Ctx1, Ctx2 >>
The union of two ratexpsets.
Definition: ratexpset.hh:445
auto k_shuffle_no_history(const Auts &... as) -> decltype(join_automata(as...))
Build the (accessible part of the) product.
Definition: product.hh:420
auto infiltration(const Lhs &lhs, const Rhs &rhs, bool keep_history=true) -> decltype(join_automata(lhs, rhs))
Build the (accessible part of the) infiltration.
Definition: product.hh:471
typename internal::context_t_of_impl< internal::base_t< ValueSet > >::type context_t_of
Helper to retrieve the type of the context of a value set.
Definition: traits.hh:66
auto k_product_with_history(const Auts &... as) -> decltype(join_automata(as...))
Build the (accessible part of the) product.
Definition: product.hh:381
auto meet(const ratexpset< Ctx1 > &a, const ratexpset< Ctx2 > &b) -> ratexpset< meet_t< Ctx1, Ctx2 >>
The meet of two ratexpsets.
Definition: ratexpset.hh:434
typename internal::labelset_t_of_impl< internal::base_t< ValueSet > >::type labelset_t_of
Helper to retrieve the type of the labelset of a value set.
Definition: traits.hh:76
auto meet_automata(Auts &&... auts) -> decltype(make_mutable_automaton(meet(auts->context()...)))
Meet between automata.
Definition: product.hh:48
auto k_shuffle_with_history(const Auts &... as) -> decltype(join_automata(as...))
Build the (accessible part of the) product.
Definition: product.hh:433
auto join_automata(Auts &&... auts) -> decltype(make_mutable_automaton(join(auts->context()...)))
Join between automata.
Definition: product.hh:39
mutable_automaton< Context > make_mutable_automaton(const Context &ctx)
Definition: mutable_automaton.hh:915
auto k_product_no_history(const Auts &... as) -> decltype(join_automata(as...))
Build the (accessible part of the) product.
Definition: product.hh:368
typename internal::weightset_t_of_impl< internal::base_t< ValueSet > >::type weightset_t_of
Helper to retrieve the type of the weightset of a value set.
Definition: traits.hh:86
Main namespace of Awali.
Definition: ato.hh:22
unsigned state_t
Definition: types.hh:21
constexpr bool all_()
Definition: tuple.hh:318
Cache the outgoing transitions of an automaton as efficient maps label -> vector<(weight,...
Definition: transition_map.hh:46
Definition: tuple.hh:372