Vcsn  2.8
Be Rational
conjunction.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <iostream>
4 #include <map>
5 #include <utility>
6 
7 #include <vcsn/algos/copy.hh>
8 #include <vcsn/algos/insplit.hh>
10 #include <vcsn/algos/strip.hh>
11 #include <vcsn/algos/tags.hh>
12 #include <vcsn/algos/transpose.hh>
17 #include <vcsn/ctx/context.hh>
18 #include <vcsn/ctx/traits.hh>
19 #include <vcsn/dyn/automaton.hh> // dyn::make_automaton
20 #include <vcsn/misc/set.hh> // has
21 #include <vcsn/misc/static-if.hh>
22 #include <vcsn/misc/to.hh>
23 #include <vcsn/misc/tuple.hh> // tuple_element_t, cross_tuple
24 #include <vcsn/misc/zip-maps.hh>
25 
26 namespace vcsn
27 {
28  namespace detail
29  {
30  /*---------------------------------.
31  | product_automaton_impl<Aut...>. |
32  `---------------------------------*/
33 
35  template <bool Lazy, Automaton Aut, Automaton... Auts>
37  : public lazy_tuple_automaton<product_automaton_impl<Lazy, Aut, Auts...>,
38  false, Lazy, Aut, Auts...>
39  {
40  static_assert(all_<labelset_t_of<Auts>::is_letterized()...>(),
41  "product: requires letterized labels");
42 
44  using automaton_t = Aut;
46  using super_t = lazy_tuple_automaton<self_t, false, Lazy, Aut, Auts...>;
47 
48  public:
50  using state_t = typename super_t::state_t;
51 
52  template <Automaton A>
53  using transition_map_t = typename super_t::template transition_map_t<A>;
54 
55  template <size_t... I>
56  using seq = typename super_t::template seq<I...>;
57 
58  using super_t::ws_;
60 
61  static symbol sname()
62  {
63  static symbol res("product_automaton"
64  + super_t::sname_(std::string{Lazy ? "true" : "false"}));
65  return res;
66  }
67 
68  std::ostream& print_set(std::ostream& o, format fmt = {}) const
69  {
70  o << "product_automaton";
71  return aut_->print_set_(o, fmt);
72  }
73 
78 
79  using label_t = typename labelset_t::value_t;
80  using weight_t = typename weightset_t::value_t;
81 
83  using automata_t = std::tuple<Auts...>;
84 
86  template <size_t I>
88 
89  using super_t::aut_;
90 
94  product_automaton_impl(Aut aut, const Auts&... auts)
95  : super_t{aut, auts...}
96  {}
97 
99  void conjunction()
100  {
102 
103  if (!Lazy)
104  while (!aut_->todo_.empty())
105  {
106  const auto& p = aut_->todo_.front();
107  add_conjunction_transitions(std::get<1>(p), std::get<0>(p));
108  aut_->todo_.pop_front();
109  }
110  }
111 
113  template <bool L = Lazy>
114  std::enable_if_t<sizeof...(Auts) == 2 && !L> ldivide()
115  {
116  static_assert(labelset_t::has_one(),
117  "ldivide: labelset must have a neutral");
119 
120  while (!aut_->todo_.empty())
121  {
122  const auto& p = aut_->todo_.front();
123  add_ldivide_transitions(std::get<1>(p), std::get<0>(p));
124  aut_->todo_.pop_front();
125  }
126  }
127 
129  template <bool L = Lazy>
130  std::enable_if_t<sizeof...(Auts) == 2 && !L> add()
131  {
132  using lhs_t = input_automaton_t<0>;
133  using rhs_t = input_automaton_t<1>;
134  constexpr bool bb = (std::is_same<weightset_t_of<lhs_t>, b>::value
135  && std::is_same<weightset_t_of<rhs_t>, b>::value);
136  static_assert(bb, "add: requires Boolean weightset");
138 
139  while (!aut_->todo_.empty())
140  {
141  const auto& p = aut_->todo_.front();
142  add_add_transitions(std::get<1>(p), std::get<0>(p));
143  aut_->todo_.pop_front();
144  }
145  }
146 
148  template <bool L = Lazy>
149  std::enable_if_t<sizeof...(Auts) == 2 && !L> ldivide_here()
150  {
152 
153  using rhs_t = input_automaton_t<1>;
154  auto new_initials = std::vector<state_t_of<rhs_t>>();
155 
156  const auto& lhs = std::get<0>(aut_->auts_);
157  auto& rhs = std::get<1>(aut_->auts_);
158 
159  while (!aut_->todo_.empty())
160  {
161  const auto& p = aut_->todo_.front();
162  const auto& state_name = std::get<0>(p);
163  add_conjunction_transitions(std::get<1>(p), state_name);
164 
165  // If lhs's state is final, rhs's corresponding state is initial.
166  if (lhs->is_final(std::get<0>(state_name)))
167  new_initials.push_back(std::get<1>(state_name));
168  aut_->todo_.pop_front();
169  }
170 
171  for (auto t: initial_transitions(rhs))
172  rhs->unset_initial(rhs->dst_of(t));
173  for (auto s: new_initials)
174  rhs->set_initial(s);
175  }
176 
178  void shuffle()
179  {
180  // Issue #86.
181  if (!std::is_same<weightset_t, b>{})
182  {
183  require(is_proper(std::get<0>(aut_->auts_)),
184  "shuffle: invalid lhs:"
185  " weighted automata with spontaneous"
186  " transitions are not supported");
187  require(is_proper(std::get<1>(aut_->auts_)),
188  "shuffle: invalid rhs:"
189  " weighted automata with spontaneous"
190  " transitions are not supported");
191  }
192 
194 
195  while (!aut_->todo_.empty())
196  {
197  const auto& p = aut_->todo_.front();
198  add_shuffle_transitions<false>(std::get<1>(p), std::get<0>(p));
199  aut_->todo_.pop_front();
200  }
201  }
202 
204  void infiltrate()
205  {
206  // Variadic infiltrate is not trivial to implement, it's not
207  // just conjunction and shuffle in series. For instance, consider
208  // three automata:
209  //
210  // <x>a
211  // x = -> 0 ------> 1 ->
212  //
213  // and likewise for y and z. Let's use `&:` to denote
214  // infiltrate. In (x &: y) there is a transition ((0,0),
215  // <xy>a, (1,1)) coming from the conjunction-like transitions.
216  //
217  // Therefore in (x &: y) &: z there is a transition ((0,0),0),
218  // <xy>a, (1,1), 0) by virtue of the shuffle-like transitions.
219  //
220  // This kind of transition that mixes conjunction and shuffle
221  // would never appear in a naive implementation with only
222  // conjunction and shuffle transitions, but no combinations.
223  require(sizeof...(Auts) == 2,
224  "infiltrate: variadic product does not work");
225 
226  // Issue #86.
227  if (!std::is_same<weightset_t, b>{})
228  {
229  require(is_proper(std::get<0>(aut_->auts_)),
230  "infiltrate: invalid lhs:"
231  " weighted automata with spontaneous"
232  " transitions are not supported");
233  require(is_proper(std::get<1>(aut_->auts_)),
234  "infiltrate: invalid rhs:"
235  " weighted automata with spontaneous"
236  " transitions are not supported");
237  }
238 
239  // Infiltrate is a mix of conjunction and shuffle operations, and
240  // the initial states for shuffle are a superset of the
241  // initial states for conjunction:
243 
244  while (!aut_->todo_.empty())
245  {
246  const auto& p = aut_->todo_.front();
247  // Infiltrate is a mix of conjunction and shuffle operations.
248  //
249  // Conjunction transitions must be added before shuffle ones:
250  // this way "conjunction" can use "new_transition" only, which
251  // is faster than "add_transition".
252  add_conjunction_transitions(std::get<1>(p), std::get<0>(p));
253  add_shuffle_transitions<true>(std::get<1>(p), std::get<0>(p));
254 
255  aut_->todo_.pop_front();
256  }
257  }
258 
260  void add_transitions(const state_t src,
261  const state_name_t& psrc)
262  {
263  add_conjunction_transitions(src, psrc);
264  }
265 
266  private:
270  {
271  aut_->todo_.emplace_back(aut_->pre_(), aut_->pre());
272  }
273 
277  {
278  // Make the result automaton initial states: same as the
279  // conjunction of pre: synchronized transitions on $.
280  add_conjunction_transitions(aut_->pre(), aut_->pre_());
281  }
282 
283  using super_t::out_;
284  using super_t::state;
290  const state_name_t& psrc)
291  {
292  for (const auto& t: zip_map_tuple(out_(psrc)))
293  // These are always new transitions: first because the
294  // source state is visited for the first time, and second
295  // because the couple (left destination, label) is unique,
296  // and so is (right destination, label).
297  if (!aut_->labelset()->is_one(t.first))
299  ([this,src,&t]
300  (const typename transition_map_t<Auts>::transition&... ts)
301  {
302  this->new_transition(src, state(ts.dst...),
303  t.first,
304  ws_.mul(ts.weight()...));
305  },
306  t.second);
307  add_one_transitions_(src, psrc, aut_->indices);
308  }
309 
316  template <bool L = Lazy>
317  std::enable_if_t<sizeof...(Auts) == 2 && !L>
319  {
320  const auto& ls = *aut_->labelset();
321  const auto& lhs = std::get<0>(aut_->auts_);
322  const auto& rhs = std::get<1>(aut_->auts_);
323  const auto& lstate = std::get<0>(psrc);
324  const auto& rstate = std::get<1>(psrc);
325 
326  if (lhs->is_final(lstate) || lstate == lhs->post())
327  {
328  for (auto ts: all_out(rhs, rstate))
329  {
330  const auto& lweight = lhs->is_final(lstate)
331  ? lhs->get_final_weight(lstate) : ws_.one();
332  this->new_transition(src,
333  state(lhs->post(), rhs->dst_of(ts)),
334  rhs->label_of(ts),
335  ws_.mul(lweight, rhs->weight_of(ts)));
336  }
337  }
338  for (const auto& t: zip_map_tuple(out_(psrc)))
339  if (!ls.is_one(t.first)
340  && (!ls.is_special(t.first) || src == aut_->pre()))
342  ([this,ls,src,t]
343  (const typename transition_map_t<Auts>::transition&... ts)
344  {
345  this->add_transition(src, state(ts.dst...),
346  ls.is_special(t.first)
347  ? t.first : ls.one(),
348  ws_.mul(ts.weight()...));
349  },
350  t.second);
351  }
352 
356  template <bool L = Lazy>
357  std::enable_if_t<sizeof...(Auts) == 2 && !L>
358  add_add_transitions(const state_t src, const state_name_t& psrc)
359  {
360  const auto& lhs = std::get<0>(aut_->auts_);
361  const auto& rhs = std::get<1>(aut_->auts_);
362  const auto& lstate = std::get<0>(psrc);
363  const auto& rstate = std::get<1>(psrc);
364 
365  auto common_labels = std::set<label_t_of<Aut>>{};
366  for (auto t: zip_map_tuple(out_(psrc)))
367  {
368  common_labels.insert(t.first);
370  ([this,src,t]
371  (const typename transition_map_t<Auts>::transition&... ts)
372  {
373  this->add_transition(src, state(ts.dst...), t.first);
374  },
375  t.second);
376  }
377  for (auto t: all_out(lhs, lstate))
378  if (!has(common_labels, lhs->label_of(t)))
379  this->new_transition(src, state(lhs->dst_of(t), rhs->post()),
380  lhs->label_of(t));
381  for (auto t: all_out(rhs, rstate))
382  if (!has(common_labels, rhs->label_of(t)))
383  this->new_transition(src, state(lhs->post(), rhs->dst_of(t)),
384  rhs->label_of(t));
385  }
386 
389  template <std::size_t... I>
390  void
391  add_one_transitions_(const state_t src, const state_name_t& psrc,
392  seq<I...>)
393  {
394  using swallow = int[];
395  (void) swallow
396  {
397  (add_one_transitions_<I>(*(std::get<I>(aut_->auts_)->labelset()),
398  src, psrc), 0)...
399  };
400  }
401 
403  template <std::size_t I, typename LS>
404  std::enable_if_t<!LS::has_one(), void>
405  add_one_transitions_(const LS&, const state_t, const state_name_t&)
406  {}
407 
410  template <std::size_t I, typename LS>
411  std::enable_if_t<LS::has_one(), void>
412  add_one_transitions_(const LS& ls, const state_t src,
413  const state_name_t& psrc)
414  {
415  // The first condition prevents the creation of redundant
416  // paths that would lead to incorrect valuations (in the
417  // weighted case), while the second is purely an optimization,
418  // avoiding the creation of non-coaccessible states.
419  if (are_proper_in(psrc, make_index_range<I + 1, sizeof...(Auts)>{})
421  {
422  // one is guaranteed to be first.
423  const auto& tmap = std::get<I>(transition_maps_)[std::get<I>(psrc)];
424  if (!tmap.empty() && ls.is_one(tmap.begin()->first))
425  for (auto t : tmap.begin()->second)
426  {
427  auto pdst = psrc;
428  std::get<I>(pdst) = t.dst;
429  this->new_transition(src, state(pdst), ls.one(), t.weight());
430  }
431  }
432  }
433 
436  template <std::size_t... I>
437  bool are_proper_in(const state_name_t& psrc, seq<I...>) const
438  {
439  return all(is_proper_in<I>(psrc)...);
440  }
441 
444  template <std::size_t... I>
446  {
447  return all(has_proper_out<I>(psrc)...);
448  }
449 
452  template <Automaton Aut_>
453  std::enable_if_t<labelset_t_of<Aut_>::has_one(), bool>
454  is_one(const Aut_& aut, transition_t_of<Aut_> tr) const
455  {
456  return aut->labelset()->is_one(aut->label_of(tr));
457  }
458 
461  template <Automaton Aut_>
462  constexpr std::enable_if_t<!labelset_t_of<Aut_>::has_one(), bool>
463  is_one(const Aut_&, transition_t_of<Aut_>) const
464  {
465  return false;
466  }
467 
469  template <size_t I>
470  constexpr auto
472  -> std::enable_if_t<!labelset_t_of<input_automaton_t<I>>::has_one(),
473  bool>
474  {
475  return true;
476  }
477 
482  template <size_t I>
483  auto
484  is_proper_in(const state_name_t& sn) const
485  -> std::enable_if_t<labelset_t_of<input_automaton_t<I>>::has_one(),
486  bool>
487  {
488  // Amusingly enough, it is faster to check the incoming
489  // transitions rather than recovering the decoration of the
490  // insplit state, which tells whether the state is proper-in.
491  const auto& aut = std::get<I>(aut_->auts_);
492  auto s = std::get<I>(sn);
493  auto rin = all_in(aut, s);
494  auto rtr = rin.begin();
495  // Insplit state, so checking the first transition suffices.
496  // There can be no incoming transitions in the case of pre.
497  return rtr == rin.end() || !is_one(aut, *rtr);
498  }
499 
505  template <size_t I>
506  bool
508  {
509  const auto& tmap = std::get<I>(transition_maps_)[std::get<I>(psrc)];
510  auto s = tmap.size();
511  if (s == 0)
512  return false;
513  else if (2 <= s)
514  return true;
515  else
516  return !std::get<I>(aut_->auts_)->labelset()->is_one(tmap.begin()->first);
517  }
518 
523  template <bool Infiltrate = false>
525  const state_name_t& psrc)
526  {
527  weight_t final
528  = add_shuffle_transitions_<Infiltrate>(src, psrc, aut_->indices);
529  aut_->set_final(src, final);
530  }
531 
536  template <bool Infiltrate, size_t... I>
538  const state_name_t& psrc,
539  seq<I...>)
540  {
541  weight_t res = ws_.one();
542  using swallow = int[];
543  (void) swallow
544  {
545  (res = ws_.mul(res,
546  add_shuffle_transitions_<Infiltrate, I>(src, psrc)),
547  0)...
548  };
549  return res;
550  }
551 
562  template <bool Infiltrate, size_t I>
563  weight_t
565  const state_name_t& psrc)
566  {
567  // Whether is a final state.
568  weight_t res = ws_.zero();
569 
570  auto& ts = std::get<I>(transition_maps_)[std::get<I>(psrc)];
571  for (auto t: ts)
572  if (std::get<I>(aut_->auts_)->labelset()->is_special(t.first))
573  res = t.second.front().weight();
574  else
575  // The src state is visited for the first time, so all
576  // these transitions are new. *Except* in the case where
577  // we have a loop on some tapes.
578  //
579  // If add_conjunction_transitions was called before (in the
580  // case of infiltrate), there may even exist such a
581  // transition in the first loop.
582  //
583  // To trigger the later case, try the self-infiltrate on
584  // derived_term('a*a').
585  for (auto d: t.second)
586  {
587  auto pdst = psrc;
588  std::get<I>(pdst) = d.dst;
589  if (Infiltrate
590  || std::get<I>(psrc) == d.dst)
591  this->add_transition(src, state(pdst), t.first, d.weight());
592  else
593  this->new_transition(src, state(pdst), t.first, d.weight());
594  }
595  return res;
596  }
597  };
598 
600  template <bool Lazy, Automaton Aut, Automaton... Auts>
601  using product_automaton
602  = std::shared_ptr<detail::product_automaton_impl<Lazy, Aut, Auts...>>;
603 
604  template <bool Lazy, Automaton Aut, Automaton... Auts>
605  auto
606  make_product_automaton(Aut aut, const Auts&... auts)
607  -> product_automaton<Lazy, Aut, Auts...>
608  {
609  using res_t = product_automaton<Lazy, Aut, Auts...>;
610  return make_shared_ptr<res_t>(aut, auts...);
611  }
612 
613 
614  /*-----------------------------.
615  | conjunction(automaton...). |
616  `-----------------------------*/
617 
619  template <Automaton Aut, Automaton... Auts>
620  auto
621  conjunction(const Aut& a, const Auts&... as)
622  {
623  auto res = make_product_automaton<false>(meet_automata(a, as...),
624  a, insplit(as)...);
625  res->conjunction();
626  return res->strip();
627  }
628 
630  template <Automaton Aut, Automaton... Auts>
631  auto
632  conjunction_lazy(const Aut& a, const Auts&... as)
633  {
634  auto res = make_product_automaton<true>(meet_automata(a, as...),
635  a, insplit(as)...);
636  res->conjunction();
637  return res;
638  }
639  }
640 
641  using detail::conjunction;
643 
644  namespace dyn
645  {
646  namespace detail
647  {
649  template <typename Auts, size_t... I>
650  automaton
651  conjunction_(const std::vector<automaton>& as, bool lazy,
653  {
654  if (lazy)
655  return conjunction_lazy(as[I]->as<tuple_element_t<I, Auts>>()...);
656  else
657  return conjunction(as[I]->as<tuple_element_t<I, Auts>>()...);
658  }
659 
661  template <typename Auts, typename Bool>
662  automaton
663  conjunction(const std::vector<automaton>& as, bool lazy)
664  {
665  auto indices
667  return conjunction_<Auts>(as, lazy, indices);
668  }
669  }
670  }
671 
672 
673  /*---------------------------------.
674  | ldivide(automaton, automaton). |
675  `---------------------------------*/
676 
681  template <Automaton Aut1, Automaton Aut2>
682  auto
683  ldivide(const Aut1& lhs, const Aut2& rhs, auto_tag = {})
684  {
685  return detail::static_if<std::is_same<weightset_t_of<Aut1>, b>::value
686  && std::is_same<weightset_t_of<Aut2>, b>::value>
687  ([] (const auto& lhs, const auto& rhs)
688  {
689  return ldivide(lhs, rhs, boolean_tag{});
690  },
691  [] (const auto& lhs, const auto& rhs)
692  {
693  return ldivide(lhs, rhs, weighted_tag{});
694  }
695  )(lhs, rhs);
696  }
697 
698  template <Automaton Aut1, Automaton Aut2>
699  auto
700  ldivide(const Aut1& lhs, const Aut2& rhs, boolean_tag)
701  {
702  auto res =
703  detail::static_if<labelset_t_of<Aut2>::has_one()>
704  ([](const auto& rhs){ return insplit(rhs); },
705  [](const auto& rhs){ return copy(rhs); })
706  (rhs);
707  auto prod =
708  detail::make_product_automaton<false>(join_automata(lhs, res), lhs, res);
709  prod->ldivide_here();
710  return res;
711  }
712 
713  template <Automaton Aut1, Automaton Aut2>
714  auto
715  ldivide(const Aut1& lhs, const Aut2& rhs, weighted_tag)
716  {
717  auto prod =
718  detail::make_product_automaton<false>(join_automata(lhs, rhs), lhs, rhs);
719  prod->ldivide();
720  return prod->strip();
721  }
722 
723 
724  namespace dyn
725  {
726  namespace detail
727  {
729  template <Automaton Aut1, Automaton Aut2>
730  automaton
731  ldivide(const automaton& aut1, const automaton& aut2)
732  {
733  const auto& a1 = aut1->as<Aut1>();
734  const auto& a2 = aut2->as<Aut2>();
735  return vcsn::ldivide<Aut1, Aut2>(a1, a2);
736  }
737  }
738  }
739 
740 
741 
742  /*---------------------------------.
743  | rdivide(automaton, automaton). |
744  `---------------------------------*/
745 
750  template <Automaton Aut1, Automaton Aut2>
751  auto
752  rdivide(const Aut1& a1, const Aut2& a2)
753  {
754  auto a1t = transpose(a1);
755  auto a2t = transpose(a2);
756  return transpose(ldivide(a2t, a1t));
757  }
758 
759  namespace dyn
760  {
761  namespace detail
762  {
764  template <Automaton Aut1, Automaton Aut2>
765  automaton
766  rdivide(const automaton& aut1, const automaton& aut2)
767  {
768  const auto& a1 = aut1->as<Aut1>();
769  const auto& a2 = aut2->as<Aut2>();
770  return vcsn::rdivide(a1, a2);
771  }
772  }
773  }
774 
775 
776  /*------------------------.
777  | shuffle(automaton...). |
778  `------------------------*/
779 
781  template <Automaton... Auts>
782  auto
783  shuffle(const Auts&... as)
784  // SFINAE
785  -> tuple_automaton<decltype(join_automata(as...)),
786  Auts...>
787  {
788  auto res =
789  detail::make_product_automaton<false>(join_automata(as...), as...);
790  res->shuffle();
791  return res->strip();
792  }
793 
794  namespace dyn
795  {
796  namespace detail
797  {
799  template <typename Auts, size_t... I>
800  automaton
801  shuffle_(const std::vector<automaton>& as,
803  {
804  return vcsn::shuffle(as[I]->as<tuple_element_t<I, Auts>>()...);
805  }
806 
808  template <typename Auts>
809  automaton
810  shuffle(const std::vector<automaton>& as)
811  {
812  auto indices
814  return shuffle_<Auts>(as, indices);
815  }
816  }
817  }
818 
819 
820  /*----------------------------.
821  | infiltrate(automaton...). |
822  `----------------------------*/
823 
825  template <Automaton A1, Automaton A2>
826  auto
827  infiltrate(const A1& a1, const A2& a2)
828  -> tuple_automaton<decltype(join_automata(a1, a2)),
829  A1, A2>
830  {
831  auto res =
832  detail::make_product_automaton<false>(join_automata(a1, a2), a1, a2);
833  res->infiltrate();
834  return res->strip();
835  }
836 
838  template <Automaton A1, Automaton A2, Automaton A3, Automaton... Auts>
839  auto
840  infiltrate(const A1& a1, const A2& a2, const A3& a3, const Auts&... as)
841  // SFINAE
842  -> decltype(infiltrate(infiltrate(a1, a2), a3, as...))
843  {
844  return infiltrate(infiltrate(a1, a2), a3, as...);
845  }
846 
847  namespace dyn
848  {
849  namespace detail
850  {
852  template <typename Auts, size_t... I>
853  automaton
854  infiltrate_(const std::vector<automaton>& as,
856  {
857  return vcsn::infiltrate(as[I]->as<tuple_element_t<I, Auts>>()...);
858  }
859 
861  template <typename Auts>
862  automaton
863  infiltrate(const std::vector<automaton>& as)
864  {
865  auto indices
867  return infiltrate_<Auts>(as, indices);
868  }
869  }
870  }
871 
872  /*-----------------------------.
873  | conjunction(automaton, n). |
874  `-----------------------------*/
875 
882  template <Automaton Aut>
883  auto
884  conjunction(const Aut& aut, to exp)
886  {
887  // We used to compute `a & n` as `([^]* & a) & a)...`. The code
888  // was simpler, but the additional conjunction (with `[^]*`) made
889  // it noticeably slower. Concrete `a & 2` was twice slower than
890  // `a & a`.
891  auto res = make_fresh_automaton(aut);
892  require(exp.single(),
893  "conjunction: exponent must be single, ", exp);
894  require(exp.finite(),
895  "conjunction: exponent must be finite, ", exp);
896  unsigned n = exp.min;
897  if (n < 2)
898  {
899  // automatonset::universal().
900  auto s = res->new_state();
901  res->set_initial(s);
902  res->set_final(s);
903  for (auto l: res->context().labelset()->generators())
904  res->new_transition(s, s, l);
905  if (n == 1)
906  // Don't return aut: we need the accessible part. However,
907  // `copy_into(accessible(aut), res)` seems more costly than
908  // a plain conjunction!
909  res = strip(conjunction(res, aut));
910  }
911  else
912  {
913  res = strip(conjunction(aut, aut));
914  n -= 2;
915  static bool iterative = getenv("VCSN_ITERATIVE");
916  if (iterative)
917  for (size_t i = 0; i < n; ++i)
918  res = strip(conjunction(res, aut));
919  else
920  {
921  auto power = strip(aut);
922  while (true)
923  {
924  if (n % 2)
925  res = strip(conjunction(res, power));
926  n /= 2;
927  if (!n)
928  break;
929  power = strip(conjunction(power, power));
930  }
931  }
932  }
933 
934  return res;
935  }
936 
937 
938  namespace dyn
939  {
940  namespace detail
941  {
943  template <Automaton Aut, typename Unsigned>
944  automaton
945  conjunction_repeated(const automaton& aut, unsigned n)
946  {
947  const auto& a = aut->as<Aut>();
949  }
950  }
951  }
952 }
A dyn automaton.
Definition: automaton.hh:17
Aut transpose(const transpose_automaton< Aut > &aut)
The transpose of a transpose automaton is the original automaton.
Definition: transpose.hh:253
auto initial_transitions(const Aut &aut) -> decltype(aut->all_out(aut->pre()))
Indexes of transitions to (visible) initial states.
Definition: automaton.hh:167
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
Definition: setalpha.hh:26
typename super_t::state_t state_t
Definition: conjunction.hh:50
auto all_in(Args &&... args) const -> decltype(aut_-> all_in(std::forward< Args >(args)...))
typename detail::weightset_t_of_impl< base_t< ValueSet > >::type weightset_t_of
Definition: traits.hh:67
weight_t add_shuffle_transitions_(const state_t src, const state_name_t &psrc, seq< I... >)
Let all automata advance one after the other, and add the corresponding transitions in the output...
Definition: conjunction.hh:537
std::enable_if_t< sizeof...(Auts)==2 &&!L > add_ldivide_transitions(const state_t src, const state_name_t &psrc)
Behave similarly to add_conjunction_transitions, with three main differences: the algorithm continues...
Definition: conjunction.hh:318
auto conjunction_lazy(const Aut &a, const Auts &... as)
Build the (accessible part of the) conjunction, on-the-fly.
Definition: conjunction.hh:632
labelset_t_of< context_t > labelset_t
Definition: conjunction.hh:76
typename detail::transition_t_of_impl< base_t< ValueSet > >::type transition_t_of
Definition: traits.hh:65
A static range.
Definition: tuple.hh:103
auto join_automata(Auts &&... auts) -> decltype(pass(auts->null_state()...), make_mutable_automaton(join(auts->context()...)))
An automaton whose type is the join between those of auts.
void cross_tuple(Fun f, const std::tuple< Ts... > &ts)
Definition: tuple.hh:347
void add_one_transitions_(const state_t src, const state_name_t &psrc, seq< I... >)
Add the spontaneous transitions leaving state src, if it is relevant (i.e.
Definition: conjunction.hh:391
bool is_proper(const Aut &aut)
Test whether an automaton is proper.
Definition: is-proper.hh:47
void add_conjunction_transitions(const state_t src, const state_name_t &psrc)
Add transitions to the result automaton, starting from the given result input state, which must correspond to the given pair of input state automata.
Definition: conjunction.hh:289
void add_shuffle_transitions(const state_t src, const state_name_t &psrc)
Add transitions to the given result automaton, starting from the given result input state...
Definition: conjunction.hh:524
typename tuple_automaton_impl::state_name_t state_name_t
Decorator implementing the laziness for an algorithm.
Build the (accessible part of the) product.
Definition: conjunction.hh:36
std::enable_if_t< LS::has_one(), void > add_one_transitions_(const LS &ls, const state_t src, const state_name_t &psrc)
If the I-th labelset has one, add the relevant spontaneous transitions leaving the state...
Definition: conjunction.hh:412
void add_transitions(const state_t src, const state_name_t &psrc)
Tell lazy_tuple_automaton how to add the transitions to a state.
Definition: conjunction.hh:260
typename weightset_t::value_t weight_t
Definition: conjunction.hh:80
context_t_of< Aut > context_t
The context of the result.
Definition: conjunction.hh:75
product_automaton_impl(Aut aut, const Auts &... auts)
Build a product automaton.
Definition: conjunction.hh:94
typename super_t::state_name_t state_name_t
Definition: conjunction.hh:49
auto shuffle(const Auts &... as) -> tuple_automaton< decltype(join_automata(as...)), Auts... >
The (accessible part of the) shuffle product.
Definition: conjunction.hh:783
product_automaton_impl self_t
Definition: conjunction.hh:45
auto infiltrate(const A1 &a1, const A2 &a2) -> tuple_automaton< decltype(join_automata(a1, a2)), A1, A2 >
The (accessible part of the) infiltration product.
Definition: conjunction.hh:827
auto meet_automata(Auts &&... auts) -> decltype(pass(auts->null_state()...), make_mutable_automaton(meet(auts->context()...)))
An automaton whose type is the meet between those of auts.
Request for the weighted version of an algorithm.
Definition: tags.hh:149
state_t state(Args &&... args)
Conversion from state name to state number.
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:21
automaton_t aut_
The wrapped automaton, possibly const.
auto insplit(Aut aut, bool lazy=false) -> std::enable_if_t< labelset_t_of< Aut >::has_one(), decltype(make_insplit_automaton(aut))>
Insplit an automaton with possible spontaneous transitions.
Definition: insplit.hh:247
auto conjunction(const Aut &aut, to exp) -> fresh_automaton_t_of< Aut >
Repeated conjunction of a automaton.
Definition: conjunction.hh:884
auto rdivide(const Aut1 &a1, const Aut2 &a2)
Compute the right quotient.
Definition: conjunction.hh:752
static symbol sname_(const T &... t)
std::ostream & print_set(std::ostream &o, format fmt={}) const
Definition: conjunction.hh:68
auto tuple(const Auts &... as)
Build the (accessible part of the) tuple.
base_t< tuple_element_t< I, automata_t > > input_automaton_t
The type of the Ith input automaton, unqualified.
Definition: conjunction.hh:87
std::enable_if_t< labelset_t_of< Aut_ >::has_one(), bool > is_one(const Aut_ &aut, transition_t_of< Aut_ > tr) const
Check if the transition is spontaneous (in the case of a labelset with one).
Definition: conjunction.hh:454
bool are_proper_in(const state_name_t &psrc, seq< I... >) const
Whether no tapes in the sequence have spontaneous incoming transitions.
Definition: conjunction.hh:437
automaton conjunction_(const std::vector< automaton > &as, bool lazy, vcsn::detail::index_sequence< I... >)
Bridge helper.
Definition: conjunction.hh:651
if(exp.max==-1)
Definition: multiply.hh:382
typename detail::context_t_of_impl< base_t< ValueSet > >::type context_t_of
Definition: traits.hh:61
An input/output format for valuesets.
Definition: format.hh:13
typename std::tuple_element< I, T >::type tuple_element_t
C++14.
Definition: tuple.hh:14
constexpr bool all_()
Definition: tuple.hh:485
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
void infiltrate()
Compute the (accessible part of the) infiltration product.
Definition: conjunction.hh:204
auto make_product_automaton(Aut aut, const Auts &... auts) -> product_automaton< Lazy, Aut, Auts... >
Definition: conjunction.hh:606
std::enable_if_t<!LS::has_one(), void > add_one_transitions_(const LS &, const state_t, const state_name_t &)
In the case where the labelset doesn&#39;t have one, do nothing.
Definition: conjunction.hh:405
std::enable_if_t< sizeof...(Auts)==2 &&!L > add_add_transitions(const state_t src, const state_name_t &psrc)
Behaves similarly to add_conjunction_transitions on a Boolean weightset, but use post() as a special ...
Definition: conjunction.hh:358
auto is_proper_in(const state_name_t &sn) const -> std::enable_if_t< labelset_t_of< input_automaton_t< I >>::has_one(), bool >
Whether the state has only proper incoming transitions.
Definition: conjunction.hh:484
std::enable_if_t< sizeof...(Auts)==2 &&!L > ldivide()
Compute the left quotient.
Definition: conjunction.hh:114
auto add_transition(Args &&... args) -> decltype(aut_-> add_transition(std::forward< Args >(args)...))
Tag to request the most appropriate version of an algorithm.
Definition: tags.hh:16
void initialize_shuffle()
Fill the worklist with the initial source-state pairs, as needed for the shuffle algorithm.
Definition: conjunction.hh:276
auto labelset(Args &&... args) const -> decltype(aut_-> labelset(std::forward< Args >(args)...))
auto copy(const AutIn &input, KeepState keep_state, KeepTrans keep_trans) -> decltype(keep_state(input->null_state()), keep_trans(input->null_transition()), make_fresh_automaton< AutIn, AutOut >(input))
A copy of input keeping only its states that are accepted by keep_state, and transitions accepted by ...
Definition: copy.hh:322
typename detail::labelset_t_of_impl< base_t< ValueSet > >::type labelset_t_of
Definition: traits.hh:63
typename labelset_t::value_t label_t
Definition: conjunction.hh:79
Aut automaton_t
The type of the resulting automaton.
Definition: conjunction.hh:44
void shuffle()
Compute the (accessible part of the) shuffle product.
Definition: conjunction.hh:178
Build the static sequence of size_t [0, N[.
Definition: tuple.hh:56
weightset_t_of< context_t > weightset_t
Definition: conjunction.hh:77
std::remove_cv_t< std::remove_reference_t< T > > base_t
T without reference or const/volatile qualifiers.
Definition: traits.hh:13
Definition: a-star.hh:8
std::enable_if_t< sizeof...(Auts)==2 &&!L > ldivide_here()
Compute the left quotient in-place.
Definition: conjunction.hh:149
auto new_transition(Args &&... args) -> decltype(aut_-> new_transition(std::forward< Args >(args)...))
bool has_proper_out(const state_name_t &psrc)
Whether the Ith state of psrc in the Ith input automaton has proper outgoing transitions (but possibl...
Definition: conjunction.hh:507
std::enable_if_t< sizeof...(Auts)==2 &&!L > add()
Compute the deterministic sum of two deterministic automata.
Definition: conjunction.hh:130
auto conjunction(const Aut &a, const Auts &... as)
Build the (accessible part of the) conjunction.
Definition: conjunction.hh:621
auto lweight(Args &&... args) -> decltype(aut_-> lweight(std::forward< Args >(args)...))
constexpr auto is_proper_in(const state_name_t &) const -> std::enable_if_t<!labelset_t_of< input_automaton_t< I >>::has_one(), bool >
Whether the state has only proper incoming transitions.
Definition: conjunction.hh:471
std::shared_ptr< detail::tuple_automaton_impl< Auts... > > tuple_automaton
A tuple automaton as a shared pointer.
typename super_t::template seq< I... > seq
Definition: conjunction.hh:56
auto & as()
Extract wrapped typed automaton.
Definition: automaton.hh:37
A static list of size_t.
Definition: tuple.hh:32
typename super_t::template transition_map_t< A > transition_map_t
Definition: conjunction.hh:53
AutOut make_fresh_automaton(const AutIn &model)
Create an empty, mutable, automaton, based on another one.
Definition: copy.hh:91
Request the Boolean specialization for determinization (B and F2).
Definition: tags.hh:132
typename tuple_automaton_impl::state_t state_t
std::tuple< typename transition_map_t< Auts >::map_t &... > out_(const state_name_t &ss)
The outgoing tuple of transitions from state tuple ss.
automaton infiltrate_(const std::vector< automaton > &as, vcsn::detail::index_sequence< I... >)
Variadic bridge helper.
Definition: conjunction.hh:854
void conjunction()
Compute the (accessible part of the) conjunction.
Definition: conjunction.hh:99
void initialize_conjunction()
Fill the worklist with the initial source-state pairs, as needed for the conjunction algorithm...
Definition: conjunction.hh:269
std::tuple< transition_map_t< Auts >... > transition_maps_
Transition caches.
An exponent, or range of exponents.
Definition: to.hh:25
#define Automaton
Definition: automaton.hh:23
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:87
std::tuple< Auts... > automata_t
The type of input automata.
Definition: conjunction.hh:83
automaton shuffle_(const std::vector< automaton > &as, vcsn::detail::index_sequence< I... >)
Variadic bridge helper.
Definition: conjunction.hh:801
auto all_out(state_t s) const -> decltype(all_out(aut_, s))
All the outgoing transitions.
weight_t add_shuffle_transitions_(const state_t src, const state_name_t &psrc)
Let Ith automaton advance, and add the corresponding transitions in the output.
Definition: conjunction.hh:564
const weightset_t & ws_
The resulting weightset.
typename Aut::element_type::template fresh_automaton_t< Context > fresh_automaton_t_of
Given an automaton type, the type of its copies.
Definition: traits.hh:82
zipped_maps< Dereference, Maps... > zip_map_tuple(const std::tuple< Maps... > &maps)
Definition: zip-maps.hh:257
automaton conjunction_repeated(const automaton &aut, unsigned n)
Bridge (conjunction).
Definition: conjunction.hh:945
return res
Definition: multiply.hh:399
bool all(Bool &&... values)
Whether all the values evaluate as true.
Definition: tuple.hh:492
constexpr std::enable_if_t<!labelset_t_of< Aut_ >::has_one(), bool > is_one(const Aut_ &, transition_t_of< Aut_ >) const
Same as above, but for labelsets without one, so it&#39;s always false.
Definition: conjunction.hh:463
std::shared_ptr< detail::product_automaton_impl< Lazy, Aut, Auts... > > product_automaton
A product automaton as a shared pointer.
Definition: conjunction.hh:602
bool have_proper_out(const state_name_t &psrc, seq< I... >)
Whether all the tapes in the sequence have proper outgoing transitions (but possibly spontaneous too)...
Definition: conjunction.hh:445