1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2018-2020 Laboratoire de Recherche et Développement
3 // de l'Epita.
4 //
5 // This file is part of Spot, a model checking library.
6 //
7 // Spot is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // Spot is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 #include "config.h"
21 #include <spot/twaalgos/toparity.hh>
22 #include <spot/twaalgos/sccinfo.hh>
23
24 #include <deque>
25 #include <utility>
26 #include <spot/twa/twa.hh>
27 #include <spot/twaalgos/cleanacc.hh>
28 #include <spot/twaalgos/degen.hh>
29 #include <spot/twaalgos/toparity.hh>
30 #include <spot/twaalgos/dualize.hh>
31 #include <spot/twaalgos/isdet.hh>
32 #include <spot/twaalgos/parity.hh>
33 #include <spot/twaalgos/remfin.hh>
34 #include <spot/twaalgos/sccinfo.hh>
35 #include <spot/twaalgos/totgba.hh>
36 #include <unordered_map>
37
38 #include <unistd.h>
39 namespace spot
40 {
41
42 // Old version of IAR.
43 namespace
44 {
45
46 using perm_t = std::vector<unsigned>;
47 struct iar_state
48 {
49 unsigned state;
50 perm_t perm;
51
52 bool
operator <spot::__anonca2c3ba70111::iar_state53 operator<(const iar_state& other) const
54 {
55 return state == other.state ? perm < other.perm : state < other.state;
56 }
57 };
58
59 template<bool is_rabin>
60 class iar_generator
61 {
62 // helper functions: access fin and inf parts of the pairs
63 // these functions negate the Streett condition to see it as a Rabin one
64 const acc_cond::mark_t&
fin(unsigned k) const65 fin(unsigned k) const
66 {
67 if (is_rabin)
68 return pairs_[k].fin;
69 else
70 return pairs_[k].inf;
71 }
72 acc_cond::mark_t
inf(unsigned k) const73 inf(unsigned k) const
74 {
75 if (is_rabin)
76 return pairs_[k].inf;
77 else
78 return pairs_[k].fin;
79 }
80 public:
iar_generator(const const_twa_graph_ptr & a,const std::vector<acc_cond::rs_pair> & p,const bool pretty_print)81 explicit iar_generator(const const_twa_graph_ptr& a,
82 const std::vector<acc_cond::rs_pair>& p,
83 const bool pretty_print)
84 : aut_(a)
85 , pairs_(p)
86 , scc_(scc_info(a))
87 , pretty_print_(pretty_print)
88 , state2pos_iar_states(aut_->num_states(), -1U)
89 {}
90
91 twa_graph_ptr
run()92 run()
93 {
94 res_ = make_twa_graph(aut_->get_dict());
95 res_->copy_ap_of(aut_);
96
97 build_iar_scc(scc_.initial());
98
99 {
100 // resulting automaton has acceptance condition: parity max odd
101 // for Rabin-like input and parity max even for Streett-like input.
102 // with priorities ranging from 0 to 2*(nb pairs)
103 // /!\ priorities are shifted by -1 compared to the original paper
104 unsigned sets = 2 * pairs_.size() + 1;
105 res_->set_acceptance(sets, acc_cond::acc_code::parity_max(is_rabin,
106 sets));
107 }
108 {
109 unsigned s = iar2num.at(state2iar.at(aut_->get_init_state_number()));
110 res_->set_init_state(s);
111 }
112
113 // there could be quite a number of unreachable states, prune them
114 res_->purge_unreachable_states();
115
116 if (pretty_print_)
117 {
118 unsigned nstates = res_->num_states();
119 auto names = new std::vector<std::string>(nstates);
120 for (auto e : res_->edges())
121 {
122 unsigned s = e.src;
123 iar_state iar = num2iar.at(s);
124 std::ostringstream st;
125 st << iar.state << ' ';
126 if (iar.perm.empty())
127 st << '[';
128 char sep = '[';
129 for (unsigned h: iar.perm)
130 {
131 st << sep << h;
132 sep = ',';
133 }
134 st << ']';
135 (*names)[s] = st.str();
136 }
137 res_->set_named_prop("state-names", names);
138 }
139
140 return res_;
141 }
142
143 void
build_iar_scc(unsigned scc_num)144 build_iar_scc(unsigned scc_num)
145 {
146 // we are working on an SCC: the state we start from does not matter
147 unsigned init = scc_.one_state_of(scc_num);
148
149 std::deque<iar_state> todo;
150 auto get_state = [&](const iar_state& s)
151 {
152 auto it = iar2num.find(s);
153 if (it == iar2num.end())
154 {
155 unsigned nb = res_->new_state();
156 iar2num[s] = nb;
157 num2iar[nb] = s;
158 unsigned iar_pos = iar_states.size();
159 unsigned old_newest_pos = state2pos_iar_states[s.state];
160 state2pos_iar_states[s.state] = iar_pos;
161 iar_states.push_back({s, old_newest_pos});
162 todo.push_back(s);
163 return nb;
164 }
165 return it->second;
166 };
167
168 auto get_other_scc = [this](unsigned state)
169 {
170 auto it = state2iar.find(state);
171 // recursively build the destination SCC if we detect that it has
172 // not been already built.
173 if (it == state2iar.end())
174 build_iar_scc(scc_.scc_of(state));
175 return iar2num.at(state2iar.at(state));
176 };
177
178 if (scc_.is_trivial(scc_num))
179 {
180 iar_state iar_s{init, perm_t()};
181 state2iar[init] = iar_s;
182 unsigned src_num = get_state(iar_s);
183 // Do not forget to connect to subsequent SCCs
184 for (const auto& e : aut_->out(init))
185 res_->new_edge(src_num, get_other_scc(e.dst), e.cond);
186 return;
187 }
188
189 // determine the pairs that appear in the SCC
190 auto colors = scc_.acc_sets_of(scc_num);
191 std::set<unsigned> scc_pairs;
192 for (unsigned k = 0; k != pairs_.size(); ++k)
193 if (!inf(k) || (colors & (pairs_[k].fin | pairs_[k].inf)))
194 scc_pairs.insert(k);
195
196 perm_t p0;
197 for (unsigned k : scc_pairs)
198 p0.push_back(k);
199
200 iar_state s0{init, p0};
201 get_state(s0); // put s0 in todo
202
203 // the main loop
204 while (!todo.empty())
205 {
206 iar_state current = todo.front();
207 todo.pop_front();
208
209 unsigned src_num = get_state(current);
210
211 for (const auto& e : aut_->out(current.state))
212 {
213 // connect to the appropriate state
214 if (scc_.scc_of(e.dst) != scc_num)
215 res_->new_edge(src_num, get_other_scc(e.dst), e.cond);
216 else
217 {
218 // find the new permutation
219 perm_t new_perm = current.perm;
220 // Count pairs whose fin-part is seen on this transition
221 unsigned seen_nb = 0;
222 // consider the pairs for this SCC only
223 for (unsigned k : scc_pairs)
224 if (e.acc & fin(k))
225 {
226 ++seen_nb;
227 auto it = std::find(new_perm.begin(),
228 new_perm.end(),
229 k);
230 // move the pair in front of the permutation
231 std::rotate(new_perm.begin(), it, it+1);
232 }
233
234 iar_state dst;
235 unsigned dst_num = -1U;
236
237 // Optimization: when several indices are seen in the
238 // transition, they move at the front of new_perm in any
239 // order. Check whether there already exists an iar_state
240 // that matches this condition.
241
242 auto iar_pos = state2pos_iar_states[e.dst];
243 while (iar_pos != -1U)
244 {
245 iar_state& tmp = iar_states[iar_pos].first;
246 iar_pos = iar_states[iar_pos].second;
247 if (std::equal(new_perm.begin() + seen_nb,
248 new_perm.end(),
249 tmp.perm.begin() + seen_nb))
250 {
251 dst = tmp;
252 dst_num = iar2num[dst];
253 break;
254 }
255 }
256 // if such a state was not found, build it
257 if (dst_num == -1U)
258 {
259 dst = iar_state{e.dst, new_perm};
260 dst_num = get_state(dst);
261 }
262
263 // find the maximal index encountered by this transition
264 unsigned maxint = -1U;
265 for (int k = current.perm.size() - 1; k >= 0; --k)
266 {
267 unsigned pk = current.perm[k];
268 if (!inf(pk) ||
269 (e.acc & (pairs_[pk].fin | pairs_[pk].inf))) {
270 maxint = k;
271 break;
272 }
273 }
274
275 acc_cond::mark_t acc = {};
276 if (maxint == -1U)
277 acc = {0};
278 else if (e.acc & fin(current.perm[maxint]))
279 acc = {2*maxint+2};
280 else
281 acc = {2*maxint+1};
282
283 res_->new_edge(src_num, dst_num, e.cond, acc);
284 }
285 }
286 }
287
288 // Optimization: find the bottom SCC of the sub-automaton we have just
289 // built. To that end, we have to ignore edges going out of scc_num.
290 auto leaving_edge = [&](unsigned d)
291 {
292 return scc_.scc_of(num2iar.at(d).state) != scc_num;
293 };
294 auto filter_edge = [](const twa_graph::edge_storage_t&,
295 unsigned dst,
296 void* filter_data)
297 {
298 decltype(leaving_edge)* data =
299 static_cast<decltype(leaving_edge)*>(filter_data);
300
301 if ((*data)(dst))
302 return scc_info::edge_filter_choice::ignore;
303 return scc_info::edge_filter_choice::keep;
304 };
305 scc_info sub_scc(res_, get_state(s0), filter_edge, &leaving_edge);
306 // SCCs are numbered in reverse topological order, so the bottom SCC has
307 // index 0.
308 const unsigned bscc = 0;
309 assert(sub_scc.succ(0).empty());
310 assert(
311 [&]()
312 {
313 for (unsigned s = 1; s != sub_scc.scc_count(); ++s)
314 if (sub_scc.succ(s).empty())
315 return false;
316 return true;
317 } ());
318
319 assert(sub_scc.states_of(bscc).size()
320 >= scc_.states_of(scc_num).size());
321
322 // update state2iar
323 for (unsigned scc_state : sub_scc.states_of(bscc))
324 {
325 iar_state& iar = num2iar.at(scc_state);
326 if (state2iar.find(iar.state) == state2iar.end())
327 state2iar[iar.state] = iar;
328 }
329 }
330
331 private:
332 const const_twa_graph_ptr& aut_;
333 const std::vector<acc_cond::rs_pair>& pairs_;
334 const scc_info scc_;
335 twa_graph_ptr res_;
336 bool pretty_print_;
337
338 // to be used when entering a new SCC
339 // maps a state of aut_ onto an iar_state with the appropriate perm
340 std::map<unsigned, iar_state> state2iar;
341
342 std::map<iar_state, unsigned> iar2num;
343 std::map<unsigned, iar_state> num2iar;
344
345 std::vector<unsigned> state2pos_iar_states;
346 std::vector<std::pair<iar_state, unsigned>> iar_states;
347 };
348
349 // Make this a function different from iar_maybe(), so that
350 // iar() does not have to call a deprecated function.
351 static twa_graph_ptr
iar_maybe_(const const_twa_graph_ptr & aut,bool pretty_print)352 iar_maybe_(const const_twa_graph_ptr& aut, bool pretty_print)
353 {
354 std::vector<acc_cond::rs_pair> pairs;
355 if (!aut->acc().is_rabin_like(pairs))
356 if (!aut->acc().is_streett_like(pairs))
357 return nullptr;
358 else
359 {
360 iar_generator<false> gen(aut, pairs, pretty_print);
361 return gen.run();
362 }
363 else
364 {
365 iar_generator<true> gen(aut, pairs, pretty_print);
366 return gen.run();
367 }
368 }
369 }
370
371 twa_graph_ptr
iar_maybe(const const_twa_graph_ptr & aut,bool pretty_print)372 iar_maybe(const const_twa_graph_ptr& aut, bool pretty_print)
373 {
374 return iar_maybe_(aut, pretty_print);
375 }
376
377 twa_graph_ptr
iar(const const_twa_graph_ptr & aut,bool pretty_print)378 iar(const const_twa_graph_ptr& aut, bool pretty_print)
379 {
380 if (auto res = iar_maybe_(aut, pretty_print))
381 return res;
382 throw std::runtime_error("iar() expects Rabin-like or Streett-like input");
383 }
384
385 // New version for paritizing
386 namespace
387 {
388 struct node
389 {
390 // A color of the permutation or a state.
391 unsigned label;
392 std::vector<node*> children;
393 // is_leaf is true if the label is a state of res_.
394 bool is_leaf;
395
nodespot::__anonca2c3ba70711::node396 node()
397 : node(0, 0){
398 }
399
nodespot::__anonca2c3ba70711::node400 node(int label_, bool is_leaf_)
401 : label(label_)
402 , children(0)
403 , is_leaf(is_leaf_){
404 }
405
~nodespot::__anonca2c3ba70711::node406 ~node()
407 {
408 for (auto c : children)
409 delete c;
410 }
411
412 // Add a permutation to the tree.
413 void
add_new_permspot::__anonca2c3ba70711::node414 add_new_perm(const std::vector<unsigned>& permu, int pos, unsigned state)
415 {
416 if (pos == -1)
417 children.push_back(new node(state, true));
418 else
419 {
420 auto lab = permu[pos];
421 auto child = std::find_if(children.begin(), children.end(),
422 [lab](node* n){
423 return n->label == lab;
424 });
425 if (child == children.end())
426 {
427 node* new_child = new node(lab, false);
428 children.push_back(new_child);
429 new_child->add_new_perm(permu, pos - 1, state);
430 }
431 else
432 (*child)->add_new_perm(permu, pos - 1, state);
433 }
434 }
435
436 node*
get_sub_treespot::__anonca2c3ba70711::node437 get_sub_tree(const std::vector<unsigned>& elements, int pos)
438 {
439 if (pos < 0)
440 return this;
441 unsigned lab = elements[pos];
442 auto child = std::find_if(children.begin(), children.end(),
443 [lab](node* n){
444 return n->label == lab;
445 });
446 assert(child != children.end());
447 return (*child)->get_sub_tree(elements, pos - 1);
448 }
449
450 // Gives a state of res_ (if it exists) reachable from this node.
451 // If use_last is true, we take the most recent, otherwise we take
452 // the oldest.
453 unsigned
get_endspot::__anonca2c3ba70711::node454 get_end(bool use_last)
455 {
456 if (children.empty())
457 {
458 if (!is_leaf)
459 return -1U;
460 return label;
461 }
462 if (use_last)
463 return children[children.size() - 1]->get_end(use_last);
464 return children[0]->get_end(use_last);
465 }
466
467 // Try to find a state compatible with the permu when seen_nb colors are
468 // moved.
469 unsigned
get_existingspot::__anonca2c3ba70711::node470 get_existing(const std::vector<unsigned>& permu, unsigned seen_nb, int pos,
471 bool use_last)
472 {
473 if (pos < (int) seen_nb)
474 return get_end(use_last);
475 else
476 {
477 auto lab = permu[pos];
478 auto child = std::find_if(children.begin(), children.end(),
479 [lab](node* n){
480 return n->label == lab;
481 });
482 if (child == children.end())
483 return -1U;
484 return (*child)->get_existing(permu, seen_nb, pos - 1, use_last);
485 }
486 }
487 };
488
489 class state_2_car_scc
490 {
491 std::vector<node> nodes;
492
493 public:
state_2_car_scc(unsigned nb_states)494 state_2_car_scc(unsigned nb_states)
495 : nodes(nb_states, node()){
496 }
497
498 // Try to find a state compatible with the permu when seen_nb colors are
499 // moved. If use_last is true, it return the last created compatible state.
500 // If it is false, it returns the oldest.
501 unsigned
get_res_state(unsigned state,const std::vector<unsigned> & permu,unsigned seen_nb,bool use_last)502 get_res_state(unsigned state, const std::vector<unsigned>& permu,
503 unsigned seen_nb, bool use_last)
504 {
505 return nodes[state].get_existing(permu, seen_nb,
506 permu.size() - 1, use_last);
507 }
508
509 void
add_res_state(unsigned initial,unsigned state,const std::vector<unsigned> & permu)510 add_res_state(unsigned initial, unsigned state,
511 const std::vector<unsigned>& permu)
512 {
513 nodes[initial].add_new_perm(permu, ((int) permu.size()) - 1, state);
514 }
515
516 node*
get_sub_tree(const std::vector<unsigned> & elements,unsigned state)517 get_sub_tree(const std::vector<unsigned>& elements, unsigned state)
518 {
519 return nodes[state].get_sub_tree(elements, elements.size() - 1);
520 }
521 };
522
523 class car_generator
524 {
525 enum algorithm {
526 // Try to have a Büchi condition if we have Rabin.
527 Rabin_to_Buchi,
528 Streett_to_Buchi,
529 // IAR
530 IAR_Streett,
531 IAR_Rabin,
532 // CAR
533 CAR,
534 // Changing colors transforms acceptance to max even/odd copy.
535 Copy_even,
536 Copy_odd,
537 // If a condition is "t" or "f", we just have to copy the automaton.
538 False_clean,
539 True_clean,
540 None
541 };
542
543
544 static std::string
algorithm_to_str(algorithm algo)545 algorithm_to_str(algorithm algo)
546 {
547 std::string algo_str;
548 switch (algo)
549 {
550 case IAR_Streett:
551 algo_str = "IAR (Streett)";
552 break;
553 case IAR_Rabin:
554 algo_str = "IAR (Rabin)";
555 break;
556 case CAR:
557 algo_str = "CAR";
558 break;
559 case Copy_even:
560 algo_str = "Copy even";
561 break;
562 case Copy_odd:
563 algo_str = "Copy odd";
564 break;
565 case False_clean:
566 algo_str = "False clean";
567 break;
568 case True_clean:
569 algo_str = "True clean";
570 break;
571 case Streett_to_Buchi:
572 algo_str = "Streett to Büchi";
573 break;
574 case Rabin_to_Buchi:
575 algo_str = "Rabin to Büchi";
576 break;
577 default:
578 algo_str = "None";
579 break;
580 }
581 return algo_str;
582 }
583
584 using perm_t = std::vector<unsigned>;
585
586 struct car_state
587 {
588 // State of the original automaton
589 unsigned state;
590 // We create a new automaton for each SCC of the original automaton
591 // so we keep a link between a car_state and the state of the
592 // subautomaton.
593 unsigned state_scc;
594 // Permutation used by IAR and CAR.
595 perm_t perm;
596
597 bool
operator <spot::__anonca2c3ba70711::car_generator::car_state598 operator<(const car_state &other) const
599 {
600 if (state < other.state)
601 return true;
602 if (state > other.state)
603 return false;
604 if (perm < other.perm)
605 return true;
606 if (perm > other.perm)
607 return false;
608 return state_scc < other.state_scc;
609 }
610
611 std::string
to_stringspot::__anonca2c3ba70711::car_generator::car_state612 to_string(algorithm algo) const
613 {
614 std::stringstream s;
615 s << state;
616 unsigned ps = perm.size();
617 if (ps > 0)
618 {
619 s << " [";
620 for (unsigned i = 0; i != ps; ++i)
621 {
622 if (i > 0)
623 s << ',';
624 s << perm[i];
625 }
626 s << ']';
627 }
628 s << ", ";
629 s << algorithm_to_str(algo);
630 return s.str();
631 }
632 };
633
634 const acc_cond::mark_t &
fin(const std::vector<acc_cond::rs_pair> & pairs,unsigned k,algorithm algo) const635 fin(const std::vector<acc_cond::rs_pair>& pairs, unsigned k, algorithm algo)
636 const
637 {
638 if (algo == IAR_Rabin)
639 return pairs[k].fin;
640 else
641 return pairs[k].inf;
642 }
643
644 acc_cond::mark_t
inf(const std::vector<acc_cond::rs_pair> & pairs,unsigned k,algorithm algo) const645 inf(const std::vector<acc_cond::rs_pair>& pairs, unsigned k, algorithm algo)
646 const
647 {
648 if (algo == IAR_Rabin)
649 return pairs[k].inf;
650 else
651 return pairs[k].fin;
652 }
653
654 // Gives for each state a set of marks incoming to this state.
655 std::vector<std::set<acc_cond::mark_t>>
get_inputs_states(const twa_graph_ptr & aut)656 get_inputs_states(const twa_graph_ptr& aut)
657 {
658 auto used = aut->acc().get_acceptance().used_sets();
659 std::vector<std::set<acc_cond::mark_t>> inputs(aut->num_states());
660 for (auto e : aut->edges())
661 {
662 auto elements = e.acc & used;
663 if (elements.has_many())
664 inputs[e.dst].insert(elements);
665 }
666 return inputs;
667 }
668
669 // Gives for each state a set of pairs incoming to this state.
670 std::vector<std::set<std::vector<unsigned>>>
get_inputs_iar(const twa_graph_ptr & aut,algorithm algo,const std::set<unsigned> & perm_elem,const std::vector<acc_cond::rs_pair> & pairs)671 get_inputs_iar(const twa_graph_ptr& aut, algorithm algo,
672 const std::set<unsigned>& perm_elem,
673 const std::vector<acc_cond::rs_pair>& pairs)
674 {
675 std::vector<std::set<std::vector<unsigned>>> inputs(aut->num_states());
676 for (auto e : aut->edges())
677 {
678 auto acc = e.acc;
679 std::vector<unsigned> new_vect;
680 for (unsigned k : perm_elem)
681 if (acc & fin(pairs, k, algo))
682 new_vect.push_back(k);
683 std::sort(std::begin(new_vect), std::end(new_vect));
684 inputs[e.dst].insert(new_vect);
685 }
686 return inputs;
687 }
688 // Give an order from the set of marks
689 std::vector<unsigned>
group_to_vector(const std::set<acc_cond::mark_t> & group)690 group_to_vector(const std::set<acc_cond::mark_t>& group)
691 {
692 // In this function, we have for example the marks {1, 2}, {1, 2, 3}, {2}
693 // A compatible order is [2, 1, 3]
694 std::vector<acc_cond::mark_t> group_vect(group.begin(), group.end());
695
696 // We sort the elements by inclusion. This function is called on a
697 // set of marks such that each mark is included or includes the others.
698 std::sort(group_vect.begin(), group_vect.end(),
699 [](const acc_cond::mark_t left, const acc_cond::mark_t right)
700 {
701 return (left != right) && ((left & right) == left);
702 });
703 // At this moment, we have the vector [{2}, {1, 2}, {1, 2, 3}].
704 // In order to create the order, we add the elements of the first element.
705 // Then we add the elements of the second mark (without duplication), etc.
706 std::vector<unsigned> result;
707 for (auto mark : group_vect)
708 {
709 for (unsigned col : mark.sets())
710 if (std::find(result.begin(), result.end(), col) == result.end())
711 result.push_back(col);
712 }
713 return result;
714 }
715
716 // Give an order from the set of indices of pairs
717 std::vector<unsigned>
group_to_vector_iar(const std::set<std::vector<unsigned>> & group)718 group_to_vector_iar(const std::set<std::vector<unsigned>>& group)
719 {
720 std::vector<std::vector<unsigned>> group_vect(group.begin(), group.end());
721 for (auto& vec : group_vect)
722 std::sort(std::begin(vec), std::end(vec));
723 std::sort(group_vect.begin(), group_vect.end(),
724 [](const std::vector<unsigned> left,
725 const std::vector<unsigned> right)
726 {
727 return (right != left)
728 && std::includes(right.begin(), right.end(),
729 left.begin(), left.end());
730 });
731 std::vector<unsigned> result;
732 for (auto vec : group_vect)
733 for (unsigned col : vec)
734 if (std::find(result.begin(), result.end(), col) == result.end())
735 result.push_back(col);
736 return result;
737 }
738
739 // Give a correspondance between a mark and an order for CAR
740 std::map<acc_cond::mark_t, std::vector<unsigned>>
get_groups(const std::set<acc_cond::mark_t> & marks_input)741 get_groups(const std::set<acc_cond::mark_t>& marks_input)
742 {
743 std::map<acc_cond::mark_t, std::vector<unsigned>> result;
744
745 std::vector<std::set<acc_cond::mark_t>> groups;
746 for (acc_cond::mark_t mark : marks_input)
747 {
748 bool added = false;
749 for (unsigned group = 0; group < groups.size(); ++group)
750 {
751 if (std::all_of(groups[group].begin(), groups[group].end(),
752 [mark](acc_cond::mark_t element)
753 {
754 return ((element | mark) == mark)
755 || ((element | mark) == element);
756 }))
757 {
758 groups[group].insert(mark);
759 added = true;
760 break;
761 }
762 }
763 if (!added)
764 groups.push_back({mark});
765 }
766 for (auto& group : groups)
767 {
768 auto new_vector = group_to_vector(group);
769 for (auto mark : group)
770 result.insert({mark, new_vector});
771 }
772 return result;
773 }
774
775 // Give a correspondance between a mark and an order for IAR
776 std::map<std::vector<unsigned>, std::vector<unsigned>>
get_groups_iar(const std::set<std::vector<unsigned>> & marks_input)777 get_groups_iar(const std::set<std::vector<unsigned>>& marks_input)
778 {
779 std::map<std::vector<unsigned>, std::vector<unsigned>> result;
780
781 std::vector<std::set<std::vector<unsigned>>> groups;
782 for (auto vect : marks_input)
783 {
784 bool added = false;
785 for (unsigned group = 0; group < groups.size(); ++group)
786 if (std::all_of(groups[group].begin(), groups[group].end(),
787 [vect](std::vector<unsigned> element)
788 {
789 return std::includes(vect.begin(), vect.end(),
790 element.begin(), element.end())
791 || std::includes(element.begin(), element.end(),
792 vect.begin(), vect.end());
793 }))
794 {
795 groups[group].insert(vect);
796 added = true;
797 break;
798 }
799 if (!added)
800 groups.push_back({vect});
801 }
802 for (auto& group : groups)
803 {
804 auto new_vector = group_to_vector_iar(group);
805 for (auto vect : group)
806 result.insert({vect, new_vector});
807 }
808 return result;
809 }
810
811 // Give for each state the correspondance between a mark and an order (CAR)
812 std::vector<std::map<acc_cond::mark_t, std::vector<unsigned>>>
get_mark_to_vector(const twa_graph_ptr & aut)813 get_mark_to_vector(const twa_graph_ptr& aut)
814 {
815 std::vector<std::map<acc_cond::mark_t, std::vector<unsigned>>> result;
816 auto inputs = get_inputs_states(aut);
817 for (unsigned state = 0; state < inputs.size(); ++state)
818 result.push_back(get_groups(inputs[state]));
819 return result;
820 }
821
822 // Give for each state the correspondance between a mark and an order (IAR)
823 std::vector<std::map<std::vector<unsigned>, std::vector<unsigned>>>
get_iar_to_vector(const twa_graph_ptr & aut,algorithm algo,const std::set<unsigned> & perm_elem,const std::vector<acc_cond::rs_pair> & pairs)824 get_iar_to_vector(const twa_graph_ptr& aut, algorithm algo,
825 const std::set<unsigned>& perm_elem,
826 const std::vector<acc_cond::rs_pair>& pairs)
827 {
828 std::vector<std::map<std::vector<unsigned>, std::vector<unsigned>>> result;
829 auto inputs = get_inputs_iar(aut, algo, perm_elem, pairs);
830 for (unsigned state = 0; state < inputs.size(); ++state)
831 result.push_back(get_groups_iar(inputs[state]));
832 return result;
833 }
834
835 public:
car_generator(const const_twa_graph_ptr & a,to_parity_options options)836 explicit car_generator(const const_twa_graph_ptr &a, to_parity_options options)
837 : aut_(a)
838 , scc_(scc_info(a))
839 , is_odd(false)
840 , options(options)
841 {
842 if (options.pretty_print)
843 names = new std::vector<std::string>();
844 else
845 names = nullptr;
846 }
847
848 // During the creation of the states, we had to choose between a set of
849 // compatible states. But it is possible to create another compatible state
850 // after. This function checks if a compatible state was created after and
851 // use it.
852 void
change_transitions_destination(twa_graph_ptr & aut,const std::vector<unsigned> & states,std::map<unsigned,std::vector<unsigned>> & partial_history,state_2_car_scc & state_2_car)853 change_transitions_destination(twa_graph_ptr& aut,
854 const std::vector<unsigned>& states,
855 std::map<unsigned, std::vector<unsigned>>& partial_history,
856 state_2_car_scc& state_2_car)
857 {
858 for (auto s : states)
859 for (auto& edge : aut->out(s))
860 {
861 unsigned
862 src = edge.src,
863 dst = edge.dst;
864 // We don't change loops
865 if (src == dst)
866 continue;
867 unsigned dst_scc = num2car[dst].state_scc;
868 auto cant_change = partial_history[aut->edge_number(edge)];
869 edge.dst = state_2_car.get_sub_tree(cant_change, dst_scc)
870 ->get_end(true);
871 }
872 }
873
874 unsigned
apply_false_true_clean(const twa_graph_ptr & sub_automaton,bool is_true,const std::vector<int> & inf_fin_prefix,unsigned max_free_color,std::map<unsigned,car_state> & state2car_local,std::map<car_state,unsigned> & car2num_local)875 apply_false_true_clean(const twa_graph_ptr &sub_automaton, bool is_true,
876 const std::vector<int>& inf_fin_prefix,
877 unsigned max_free_color,
878 std::map<unsigned, car_state>& state2car_local,
879 std::map<car_state, unsigned>& car2num_local)
880 {
881 std::vector<unsigned>* init_states = sub_automaton->
882 get_named_prop<std::vector<unsigned>>("original-states");
883
884 for (unsigned state = 0; state < sub_automaton->num_states(); ++state)
885 {
886 unsigned s_aut = (*init_states)[state];
887
888 car_state new_car = { s_aut, state, perm_t() };
889 auto new_state = res_->new_state();
890 car2num_local[new_car] = new_state;
891 num2car.insert(num2car.begin() + new_state, new_car);
892 if (options.pretty_print)
893 names->push_back(
894 new_car.to_string(is_true ? True_clean : False_clean));
895 state2car_local[s_aut] = new_car;
896 }
897 for (unsigned state = 0; state < sub_automaton->num_states(); ++state)
898 {
899 unsigned s_aut = (*init_states)[state];
900 car_state src = { s_aut, state, perm_t() };
901 unsigned src_state = car2num_local[src];
902 for (auto e : aut_->out(s_aut))
903 {
904 auto col = is_true ^ !is_odd;
905 if (((unsigned)col) > max_free_color)
906 throw std::runtime_error("CAR needs more sets");
907 if (scc_.scc_of(s_aut) == scc_.scc_of(e.dst))
908 {
909 for (auto c : e.acc.sets())
910 if (inf_fin_prefix[c] + is_odd > col)
911 col = inf_fin_prefix[c] + is_odd;
912 acc_cond::mark_t cond = { (unsigned) col };
913 res_->new_edge(
914 src_state, car2num_local[state2car_local[e.dst]],
915 e.cond, cond);
916 }
917 }
918 }
919 return sub_automaton->num_states();
920 }
921
922 unsigned
apply_copy(const twa_graph_ptr & sub_automaton,const std::vector<unsigned> & permut,bool copy_odd,const std::vector<int> & inf_fin_prefix,std::map<unsigned,car_state> & state2car_local,std::map<car_state,unsigned> & car2num_local)923 apply_copy(const twa_graph_ptr &sub_automaton,
924 const std::vector<unsigned> &permut,
925 bool copy_odd,
926 const std::vector<int>& inf_fin_prefix,
927 std::map<unsigned, car_state>& state2car_local,
928 std::map<car_state, unsigned>& car2num_local)
929 {
930 std::vector<unsigned>* init_states = sub_automaton
931 ->get_named_prop<std::vector<unsigned>>("original-states");
932 for (unsigned state = 0; state < sub_automaton->num_states(); ++state)
933 {
934 car_state new_car = { (*init_states)[state], state, perm_t() };
935 auto new_state = res_->new_state();
936 car2num_local[new_car] = new_state;
937 num2car.insert(num2car.begin() + new_state, new_car);
938 state2car_local[(*init_states)[state]] = new_car;
939 if (options.pretty_print)
940 names->push_back(
941 new_car.to_string(copy_odd ? Copy_odd : Copy_even));
942 }
943 auto cond_col = sub_automaton->acc().get_acceptance().used_sets();
944 for (unsigned s = 0; s < sub_automaton->num_states(); ++s)
945 {
946 for (auto e : sub_automaton->out(s))
947 {
948 acc_cond::mark_t mark = { };
949 int max_edge = -1;
950 for (auto col : e.acc.sets())
951 {
952 if (cond_col.has(col))
953 max_edge = std::max(max_edge, (int) permut[col]);
954 if (inf_fin_prefix[col] + (is_odd || copy_odd) > max_edge)
955 max_edge = inf_fin_prefix[col] + (is_odd || copy_odd);
956 }
957 if (max_edge != -1)
958 mark.set((unsigned) max_edge);
959 car_state src = { (*init_states)[s], s, perm_t() },
960 dst = { (*init_states)[e.dst], e.dst, perm_t() };
961 unsigned src_state = car2num_local[src],
962 dst_state = car2num_local[dst];
963 res_->new_edge(src_state, dst_state, e.cond, mark);
964 }
965 }
966 return sub_automaton->num_states();
967 }
968
969 unsigned
apply_to_Buchi(const twa_graph_ptr & sub_automaton,const twa_graph_ptr & buchi,bool is_streett_to_buchi,const std::vector<int> & inf_fin_prefix,unsigned max_free_color,std::map<unsigned,car_state> & state2car_local,std::map<car_state,unsigned> & car2num_local)970 apply_to_Buchi(const twa_graph_ptr& sub_automaton,
971 const twa_graph_ptr& buchi,
972 bool is_streett_to_buchi,
973 const std::vector<int>& inf_fin_prefix,
974 unsigned max_free_color,
975 std::map<unsigned, car_state>& state2car_local,
976 std::map<car_state, unsigned>& car2num_local)
977 {
978 std::vector<unsigned>* init_states = sub_automaton
979 ->get_named_prop<std::vector<unsigned>>("original-states");
980
981 for (unsigned state = 0; state < buchi->num_states(); ++state)
982 {
983 car_state new_car = { (*init_states)[state], state, perm_t() };
984 auto new_state = res_->new_state();
985 car2num_local[new_car] = new_state;
986 num2car.insert(num2car.begin() + new_state, new_car);
987 state2car_local[(*init_states)[state]] = new_car;
988 if (options.pretty_print)
989 names->push_back(new_car.to_string(
990 is_streett_to_buchi ? Streett_to_Buchi : Rabin_to_Buchi));
991 }
992 auto g = buchi->get_graph();
993 for (unsigned s = 0; s < buchi->num_states(); ++s)
994 {
995 unsigned b = g.state_storage(s).succ;
996 while (b)
997 {
998 auto& e = g.edge_storage(b);
999 auto acc = e.acc;
1000 acc <<= (is_odd + is_streett_to_buchi);
1001 if ((is_odd || is_streett_to_buchi) && acc == acc_cond::mark_t{ })
1002 acc = { (unsigned) (is_streett_to_buchi && is_odd) };
1003 car_state src = { (*init_states)[s], s, perm_t() },
1004 dst = { (*init_states)[e.dst], e.dst, perm_t() };
1005 unsigned src_state = car2num_local[src],
1006 dst_state = car2num_local[dst];
1007 int col = ((int) acc.max_set()) - 1;
1008 if (col > (int) max_free_color)
1009 throw std::runtime_error("CAR needs more sets");
1010 auto& e2 = sub_automaton->get_graph().edge_storage(b);
1011 for (auto c : e2.acc.sets())
1012 {
1013 if (inf_fin_prefix[c] + is_odd > col)
1014 col = inf_fin_prefix[c] + is_odd;
1015 }
1016 if (col != -1)
1017 acc = { (unsigned) col };
1018 else
1019 acc = {};
1020 res_->new_edge(src_state, dst_state, e.cond, acc);
1021 b = e.next_succ;
1022 }
1023 }
1024 return buchi->num_states();
1025 }
1026
1027 // Create a permutation for the first state of a SCC (IAR)
1028 void
initial_perm_iar(std::set<unsigned> & perm_elem,perm_t & p0,algorithm algo,const acc_cond::mark_t & colors,const std::vector<acc_cond::rs_pair> & pairs)1029 initial_perm_iar(std::set<unsigned> &perm_elem, perm_t &p0,
1030 algorithm algo, const acc_cond::mark_t &colors,
1031 const std::vector<acc_cond::rs_pair> &pairs)
1032 {
1033 for (unsigned k = 0; k != pairs.size(); ++k)
1034 if (!inf(pairs, k, algo) || (colors & (pairs[k].fin | pairs[k].inf)))
1035 {
1036 perm_elem.insert(k);
1037 p0.push_back(k);
1038 }
1039 }
1040
1041 // Create a permutation for the first state of a SCC (CAR)
1042 void
initial_perm_car(perm_t & p0,const acc_cond::mark_t & colors)1043 initial_perm_car(perm_t &p0, const acc_cond::mark_t &colors)
1044 {
1045 auto cont = colors.sets();
1046 p0.assign(cont.begin(), cont.end());
1047 }
1048
1049 void
find_new_perm_iar(perm_t & new_perm,const std::vector<acc_cond::rs_pair> & pairs,const acc_cond::mark_t & acc,algorithm algo,const std::set<unsigned> & perm_elem,unsigned & seen_nb)1050 find_new_perm_iar(perm_t &new_perm,
1051 const std::vector<acc_cond::rs_pair> &pairs,
1052 const acc_cond::mark_t &acc,
1053 algorithm algo, const std::set<unsigned> &perm_elem,
1054 unsigned &seen_nb)
1055 {
1056 for (unsigned k : perm_elem)
1057 if (acc & fin(pairs, k, algo))
1058 {
1059 ++seen_nb;
1060 auto it = std::find(new_perm.begin(), new_perm.end(), k);
1061
1062 // move the pair in front of the permutation
1063 std::rotate(new_perm.begin(), it, it + 1);
1064 }
1065 }
1066
1067 // Given the set acc of colors appearing on an edge, create a new
1068 // permutation new_perm, and give the number seen_nb of colors moved to
1069 // the head of the permutation.
1070 void
find_new_perm_car(perm_t & new_perm,const acc_cond::mark_t & acc,unsigned & seen_nb,unsigned & h)1071 find_new_perm_car(perm_t &new_perm, const acc_cond::mark_t &acc,
1072 unsigned &seen_nb, unsigned &h)
1073 {
1074 for (unsigned k : acc.sets())
1075 {
1076 auto it = std::find(new_perm.begin(), new_perm.end(), k);
1077 if (it != new_perm.end())
1078 {
1079 h = std::max(h, unsigned(it - new_perm.begin()) + 1);
1080 std::rotate(new_perm.begin(), it, it + 1);
1081 ++seen_nb;
1082 }
1083 }
1084 }
1085
1086 void
get_acceptance_iar(algorithm algo,const perm_t & current_perm,const std::vector<acc_cond::rs_pair> & pairs,const acc_cond::mark_t & e_acc,acc_cond::mark_t & acc)1087 get_acceptance_iar(algorithm algo, const perm_t ¤t_perm,
1088 const std::vector<acc_cond::rs_pair> &pairs,
1089 const acc_cond::mark_t &e_acc, acc_cond::mark_t &acc)
1090 {
1091 unsigned delta_acc = (algo == IAR_Streett) && is_odd;
1092
1093 // find the maximal index encountered by this transition
1094 unsigned maxint = -1U;
1095
1096 for (int k = current_perm.size() - 1; k >= 0; --k)
1097 {
1098 unsigned pk = current_perm[k];
1099
1100 if (!inf(pairs, pk,
1101 algo)
1102 || (e_acc & (pairs[pk].fin | pairs[pk].inf)))
1103 {
1104 maxint = k;
1105 break;
1106 }
1107 }
1108 unsigned value;
1109
1110 if (maxint == -1U)
1111 value = delta_acc;
1112 else if (e_acc & fin(pairs, current_perm[maxint], algo))
1113 value = 2 * maxint + 2 + delta_acc;
1114 else
1115 value = 2 * maxint + 1 + delta_acc;
1116 acc = { value };
1117 }
1118
1119 void
get_acceptance_car(const acc_cond & sub_aut_cond,const perm_t & new_perm,unsigned h,acc_cond::mark_t & acc)1120 get_acceptance_car(const acc_cond &sub_aut_cond, const perm_t &new_perm,
1121 unsigned h, acc_cond::mark_t &acc)
1122 {
1123 acc_cond::mark_t m(new_perm.begin(), new_perm.begin() + h);
1124 bool rej = !sub_aut_cond.accepting(m);
1125 unsigned value = 2 * h + rej + is_odd;
1126 acc = { value };
1127 }
1128
1129 unsigned
apply_lar(const twa_graph_ptr & sub_automaton,unsigned init,std::vector<acc_cond::rs_pair> & pairs,algorithm algo,unsigned scc_num,const std::vector<int> & inf_fin_prefix,unsigned max_free_color,std::map<unsigned,car_state> & state2car_local,std::map<car_state,unsigned> & car2num_local,unsigned max_states)1130 apply_lar(const twa_graph_ptr &sub_automaton,
1131 unsigned init, std::vector<acc_cond::rs_pair> &pairs,
1132 algorithm algo, unsigned scc_num,
1133 const std::vector<int>& inf_fin_prefix,
1134 unsigned max_free_color,
1135 std::map<unsigned, car_state>& state2car_local,
1136 std::map<car_state, unsigned>& car2num_local,
1137 unsigned max_states)
1138 {
1139 auto maps = get_mark_to_vector(sub_automaton);
1140 // For each edge e of res_, we store the elements of the permutation
1141 // that are not moved, and we respect the order.
1142 std::map<unsigned, std::vector<unsigned>> edge_to_colors;
1143 unsigned nb_created_states = 0;
1144 auto state_2_car = state_2_car_scc(sub_automaton->num_states());
1145 std::vector<unsigned>* init_states = sub_automaton->
1146 get_named_prop<std::vector<unsigned>>("original-states");
1147 std::deque<car_state> todo;
1148 auto get_state =
1149 [&](const car_state &s){
1150 auto it = car2num_local.find(s);
1151
1152 if (it == car2num_local.end())
1153 {
1154 ++nb_created_states;
1155 unsigned nb = res_->new_state();
1156 if (options.search_ex)
1157 state_2_car.add_res_state(s.state_scc, nb, s.perm);
1158 car2num_local[s] = nb;
1159 num2car.insert(num2car.begin() + nb, s);
1160
1161 todo.push_back(s);
1162 if (options.pretty_print)
1163 names->push_back(s.to_string(algo));
1164 return nb;
1165 }
1166 return it->second;
1167 };
1168
1169 auto colors = sub_automaton->acc().get_acceptance().used_sets();
1170 std::set<unsigned> perm_elem;
1171
1172 perm_t p0 = { };
1173 switch (algo)
1174 {
1175 case IAR_Streett:
1176 case IAR_Rabin:
1177 initial_perm_iar(perm_elem, p0, algo, colors, pairs);
1178 break;
1179 case CAR:
1180 initial_perm_car(p0, colors);
1181 break;
1182 default:
1183 assert(false);
1184 break;
1185 }
1186
1187 std::vector<std::map<std::vector<unsigned>, std::vector<unsigned>>>
1188 iar_maps;
1189 if (algo == IAR_Streett || algo == IAR_Rabin)
1190 iar_maps = get_iar_to_vector(sub_automaton, algo, perm_elem, pairs);
1191
1192 car_state s0{ (*init_states)[init], init, p0 };
1193 get_state(s0); // put s0 in todo
1194
1195 // the main loop
1196 while (!todo.empty())
1197 {
1198 car_state current = todo.front();
1199 todo.pop_front();
1200
1201 unsigned src_num = get_state(current);
1202 for (const auto &e : sub_automaton->out(current.state_scc))
1203 {
1204 perm_t new_perm = current.perm;
1205
1206 // Count pairs whose fin-part is seen on this transition
1207 unsigned seen_nb = 0;
1208
1209 // consider the pairs for this SCC only
1210 unsigned h = 0;
1211
1212 switch (algo)
1213 {
1214 case IAR_Rabin:
1215 case IAR_Streett:
1216 find_new_perm_iar(new_perm, pairs, e.acc, algo,
1217 perm_elem, seen_nb);
1218 break;
1219 case CAR:
1220 find_new_perm_car(new_perm, e.acc, seen_nb, h);
1221 break;
1222 default:
1223 assert(false);
1224 }
1225
1226 std::vector<unsigned> not_moved(new_perm.begin() + seen_nb,
1227 new_perm.end());
1228
1229 if (options.force_order)
1230 {
1231 if (algo == CAR && seen_nb > 1)
1232 {
1233 auto map = maps[e.dst];
1234 acc_cond::mark_t first_vals(
1235 new_perm.begin(), new_perm.begin() + seen_nb);
1236 auto new_start = map.find(first_vals);
1237 assert(new_start->second.size() >= seen_nb);
1238 assert(new_start != map.end());
1239 for (unsigned i = 0; i < seen_nb; ++i)
1240 new_perm[i] = new_start->second[i];
1241 }
1242 else if ((algo == IAR_Streett || algo == IAR_Rabin)
1243 && seen_nb > 1)
1244 {
1245 auto map = iar_maps[e.dst];
1246 std::vector<unsigned> first_vals(
1247 new_perm.begin(), new_perm.begin() + seen_nb);
1248 std::sort(std::begin(first_vals), std::end(first_vals));
1249 auto new_start = map.find(first_vals);
1250 assert(new_start->second.size() >= seen_nb);
1251 assert(new_start != map.end());
1252 for (unsigned i = 0; i < seen_nb; ++i)
1253 new_perm[i] = new_start->second[i];
1254 }
1255 }
1256
1257 // Optimization: when several indices are seen in the
1258 // transition, they move at the front of new_perm in any
1259 // order. Check whether there already exists an car_state
1260 // that matches this condition.
1261 car_state dst;
1262 unsigned dst_num = -1U;
1263
1264 if (options.search_ex)
1265 dst_num = state_2_car.get_res_state(e.dst, new_perm, seen_nb,
1266 options.use_last);
1267
1268 if (dst_num == -1U)
1269 {
1270 auto dst = car_state{ (*init_states)[e.dst], e.dst, new_perm };
1271 dst_num = get_state(dst);
1272 if (nb_created_states > max_states)
1273 return -1U;
1274 }
1275
1276 acc_cond::mark_t acc = { };
1277
1278 switch (algo)
1279 {
1280 case IAR_Rabin:
1281 case IAR_Streett:
1282 get_acceptance_iar(algo, current.perm, pairs, e.acc, acc);
1283 break;
1284 case CAR:
1285 get_acceptance_car(sub_automaton->acc(), new_perm, h, acc);
1286 break;
1287 default:
1288 assert(false);
1289 }
1290
1291 unsigned acc_col = acc.min_set() - 1;
1292 if (options.parity_prefix)
1293 {
1294 if (acc_col > max_free_color)
1295 throw std::runtime_error("CAR needs more sets");
1296 // parity prefix
1297 for (auto col : e.acc.sets())
1298 {
1299 if (inf_fin_prefix[col] + is_odd > (int) acc_col)
1300 acc_col = (unsigned) inf_fin_prefix[col] + is_odd;
1301 }
1302 }
1303 auto new_e = res_->new_edge(src_num, dst_num, e.cond, { acc_col });
1304 edge_to_colors.insert({new_e, not_moved});
1305 }
1306 }
1307 if (options.search_ex && options.use_last)
1308 {
1309 std::vector<unsigned> added_states;
1310 std::transform(car2num_local.begin(), car2num_local.end(),
1311 std::back_inserter(added_states),
1312 [](std::pair<const car_state, unsigned> pair) {
1313 return pair.second;
1314 });
1315 change_transitions_destination(
1316 res_, added_states, edge_to_colors, state_2_car);
1317 }
1318 auto leaving_edge =
1319 [&](unsigned d){
1320 return scc_.scc_of(num2car.at(d).state) != scc_num;
1321 };
1322 auto filter_edge =
1323 [](const twa_graph::edge_storage_t &,
1324 unsigned dst,
1325 void* filter_data){
1326 decltype(leaving_edge) *data =
1327 static_cast<decltype(leaving_edge)*>(filter_data);
1328
1329 if ((*data)(dst))
1330 return scc_info::edge_filter_choice::ignore;
1331
1332 return scc_info::edge_filter_choice::keep;
1333 };
1334 scc_info sub_scc(res_, get_state(s0), filter_edge, &leaving_edge);
1335
1336 // SCCs are numbered in reverse topological order, so the bottom SCC has
1337 // index 0.
1338 const unsigned bscc = 0;
1339 assert(sub_scc.scc_count() != 0);
1340 assert(sub_scc.succ(0).empty());
1341 assert(
1342 [&](){
1343 for (unsigned s = 1; s != sub_scc.scc_count(); ++s)
1344 if (sub_scc.succ(s).empty())
1345 return false;
1346
1347 return true;
1348 } ());
1349
1350 assert(sub_scc.states_of(bscc).size() >= sub_automaton->num_states());
1351
1352 // update state2car
1353 for (unsigned scc_state : sub_scc.states_of(bscc))
1354 {
1355 car_state &car = num2car.at(scc_state);
1356
1357 if (state2car_local.find(car.state) == state2car_local.end())
1358 state2car_local[car.state] = car;
1359 }
1360 return sub_scc.states_of(bscc).size();
1361 }
1362
1363 algorithm
chooseAlgo(twa_graph_ptr & sub_automaton,twa_graph_ptr & rabin_aut,std::vector<acc_cond::rs_pair> & pairs,std::vector<unsigned> & permut)1364 chooseAlgo(twa_graph_ptr &sub_automaton,
1365 twa_graph_ptr &rabin_aut,
1366 std::vector<acc_cond::rs_pair> &pairs,
1367 std::vector<unsigned> &permut)
1368 {
1369 auto scc_condition = sub_automaton->acc();
1370 if (options.parity_equiv)
1371 {
1372 if (scc_condition.is_f())
1373 return False_clean;
1374 if (scc_condition.is_t())
1375 return True_clean;
1376 std::vector<int> permut_tmp(scc_condition.all_sets().max_set(), -1);
1377
1378 if (!is_odd && scc_condition.is_parity_max_equiv(permut_tmp, true))
1379 {
1380 for (auto c : permut_tmp)
1381 permut.push_back((unsigned) c);
1382
1383 scc_condition.apply_permutation(permut);
1384 sub_automaton->apply_permutation(permut);
1385 return Copy_even;
1386 }
1387 std::fill(permut_tmp.begin(), permut_tmp.end(), -1);
1388 if (scc_condition.is_parity_max_equiv(permut_tmp, false))
1389 {
1390 for (auto c : permut_tmp)
1391 permut.push_back((unsigned) c);
1392 scc_condition.apply_permutation(permut);
1393 sub_automaton->apply_permutation(permut);
1394 return Copy_odd;
1395 }
1396 }
1397
1398 if (options.rabin_to_buchi)
1399 {
1400 auto ra = rabin_to_buchi_if_realizable(sub_automaton);
1401 if (ra != nullptr)
1402 {
1403 rabin_aut = ra;
1404 return Rabin_to_Buchi;
1405 }
1406 else
1407 {
1408 bool streett_buchi = false;
1409 auto sub_cond = sub_automaton->get_acceptance();
1410 sub_automaton->set_acceptance(sub_cond.complement());
1411 auto ra = rabin_to_buchi_if_realizable(sub_automaton);
1412 streett_buchi = (ra != nullptr);
1413 sub_automaton->set_acceptance(sub_cond);
1414 if (streett_buchi)
1415 {
1416 rabin_aut = ra;
1417 return Streett_to_Buchi;
1418 }
1419 }
1420 }
1421
1422 auto pairs1 = std::vector<acc_cond::rs_pair>();
1423 auto pairs2 = std::vector<acc_cond::rs_pair>();
1424 std::sort(pairs1.begin(), pairs1.end());
1425 pairs1.erase(std::unique(pairs1.begin(), pairs1.end()), pairs1.end());
1426 std::sort(pairs2.begin(), pairs2.end());
1427 pairs2.erase(std::unique(pairs2.begin(), pairs2.end()), pairs2.end());
1428 bool is_r_like = scc_condition.is_rabin_like(pairs1);
1429 bool is_s_like = scc_condition.is_streett_like(pairs2);
1430 unsigned num_cols = scc_condition.get_acceptance().used_sets().count();
1431 if (is_r_like)
1432 {
1433 if ((is_s_like && pairs1.size() < pairs2.size()) || !is_s_like)
1434 {
1435 if (pairs1.size() > num_cols)
1436 return CAR;
1437 pairs = pairs1;
1438 return IAR_Rabin;
1439 }
1440 else if (is_s_like)
1441 {
1442 if (pairs2.size() > num_cols)
1443 return CAR;
1444 pairs = pairs2;
1445 return IAR_Streett;
1446 }
1447 }
1448 else
1449 {
1450 if (is_s_like)
1451 {
1452 if (pairs2.size() > num_cols)
1453 return CAR;
1454 pairs = pairs2;
1455 return IAR_Streett;
1456 }
1457 }
1458 return CAR;
1459 }
1460
1461 unsigned
build_scc(twa_graph_ptr & sub_automaton,unsigned scc_num,std::map<unsigned,car_state> & state2car_local,std::map<car_state,unsigned> & car2num_local,algorithm & algo,unsigned max_states=-1U)1462 build_scc(twa_graph_ptr &sub_automaton,
1463 unsigned scc_num,
1464 std::map<unsigned, car_state>& state2car_local,
1465 std::map<car_state, unsigned>&car2num_local,
1466 algorithm& algo,
1467 unsigned max_states = -1U)
1468 {
1469
1470 std::vector<int> parity_prefix_colors (SPOT_MAX_ACCSETS,
1471 - SPOT_MAX_ACCSETS - 2);
1472 unsigned min_prefix_color = SPOT_MAX_ACCSETS + 1;
1473 if (options.parity_prefix)
1474 {
1475 auto new_acc = sub_automaton->acc();
1476 auto colors = std::vector<unsigned>();
1477 bool inf_start =
1478 sub_automaton->acc().has_parity_prefix(new_acc, colors);
1479 sub_automaton->set_acceptance(new_acc);
1480 for (unsigned i = 0; i < colors.size(); ++i)
1481 parity_prefix_colors[colors[i]] =
1482 SPOT_MAX_ACCSETS - 4 - i - !inf_start;
1483 if (colors.size() > 0)
1484 min_prefix_color =
1485 SPOT_MAX_ACCSETS - 4 - colors.size() - 1 - !inf_start;
1486 }
1487 --min_prefix_color;
1488
1489 unsigned init = 0;
1490
1491 std::vector<acc_cond::rs_pair> pairs = { };
1492 auto permut = std::vector<unsigned>();
1493 twa_graph_ptr rabin_aut = nullptr;
1494 algo = chooseAlgo(sub_automaton, rabin_aut, pairs, permut);
1495 switch (algo)
1496 {
1497 case False_clean:
1498 case True_clean:
1499 return apply_false_true_clean(sub_automaton, (algo == True_clean),
1500 parity_prefix_colors, min_prefix_color,
1501 state2car_local, car2num_local);
1502 break;
1503 case IAR_Streett:
1504 case IAR_Rabin:
1505 case CAR:
1506 return apply_lar(sub_automaton, init, pairs, algo, scc_num,
1507 parity_prefix_colors, min_prefix_color,
1508 state2car_local, car2num_local, max_states);
1509 break;
1510 case Copy_odd:
1511 case Copy_even:
1512 return apply_copy(sub_automaton, permut, algo == Copy_odd,
1513 parity_prefix_colors, state2car_local,
1514 car2num_local);
1515 break;
1516 case Rabin_to_Buchi:
1517 case Streett_to_Buchi:
1518 return apply_to_Buchi(sub_automaton, rabin_aut,
1519 (algo == Streett_to_Buchi),
1520 parity_prefix_colors, min_prefix_color,
1521 state2car_local, car2num_local);
1522 break;
1523 default:
1524 break;
1525 }
1526 return -1U;
1527 }
1528
1529 public:
1530 twa_graph_ptr
run()1531 run()
1532 {
1533 res_ = make_twa_graph(aut_->get_dict());
1534 res_->copy_ap_of(aut_);
1535 for (unsigned scc = 0; scc < scc_.scc_count(); ++scc)
1536 {
1537 if (!scc_.is_useful_scc(scc))
1538 continue;
1539 auto sub_automata = scc_.split_on_sets(scc, { }, true);
1540 if (sub_automata.empty())
1541 {
1542 for (auto state : scc_.states_of(scc))
1543 {
1544 auto new_state = res_->new_state();
1545 car_state new_car = { state, state, perm_t() };
1546 car2num[new_car] = new_state;
1547 num2car.insert(num2car.begin() + new_state, new_car);
1548 if (options.pretty_print)
1549 names->push_back(new_car.to_string(None));
1550 state2car[state] = new_car;
1551 }
1552 continue;
1553 }
1554
1555 auto sub_automaton = sub_automata[0];
1556 auto deg = sub_automaton;
1557 if (options.acc_clean)
1558 simplify_acceptance_here(sub_automaton);
1559 bool has_degeneralized = false;
1560 if (options.partial_degen)
1561 {
1562 std::vector<acc_cond::mark_t> forbid;
1563 auto m =
1564 is_partially_degeneralizable(sub_automaton, true,
1565 true, forbid);
1566 while (m != acc_cond::mark_t {})
1567 {
1568 auto tmp = partial_degeneralize(deg, m);
1569 simplify_acceptance_here(tmp);
1570 if (tmp->get_acceptance().used_sets().count()
1571 < deg->get_acceptance().used_sets().count() ||
1572 !(options.reduce_col_deg))
1573 {
1574 deg = tmp;
1575 has_degeneralized = true;
1576 }
1577 else
1578 forbid.push_back(m);
1579 m = is_partially_degeneralizable(deg, true, true, forbid);
1580 }
1581 }
1582
1583 if (options.propagate_col)
1584 {
1585 propagate_marks_here(sub_automaton);
1586 if (deg != sub_automaton)
1587 propagate_marks_here(deg);
1588 }
1589
1590 std::map<unsigned, car_state> state2car_sub, state2car_deg;
1591 std::map<car_state, unsigned> car2num_sub, car2num_deg;
1592
1593 unsigned nb_states_deg = -1U,
1594 nb_states_sub = -1U;
1595
1596 algorithm algo_sub, algo_deg;
1597 unsigned max_states_sub_car = -1U;
1598 // We try with and without degeneralization and we keep the best.
1599 if (has_degeneralized)
1600 {
1601 nb_states_deg =
1602 build_scc(deg, scc, state2car_deg, car2num_deg, algo_deg);
1603 // We suppose that if we see nb_states_deg + 1000 states when
1604 // when construct the version without degeneralization during the
1605 // construction, we will not be able to have nb_states_deg after
1606 // removing useless states. So we will stop the execution.
1607 max_states_sub_car =
1608 10000 + nb_states_deg - 1;
1609 }
1610 if (!options.force_degen || !has_degeneralized)
1611 nb_states_sub =
1612 build_scc(sub_automaton, scc, state2car_sub, car2num_sub,
1613 algo_sub, max_states_sub_car);
1614 if (nb_states_deg < nb_states_sub)
1615 {
1616 state2car.insert(state2car_deg.begin(), state2car_deg.end());
1617 car2num.insert(car2num_deg.begin(), car2num_deg.end());
1618 algo_sub = algo_deg;
1619 }
1620 else
1621 {
1622 state2car.insert(state2car_sub.begin(), state2car_sub.end());
1623 car2num.insert(car2num_sub.begin(), car2num_sub.end());
1624 }
1625 if ((algo_sub == IAR_Rabin || algo_sub == Copy_odd) && !is_odd)
1626 {
1627 is_odd = true;
1628 for (auto &edge : res_->edges())
1629 {
1630 if (scc_.scc_of(num2car[edge.src].state) != scc
1631 && scc_.scc_of(num2car[edge.dst].state) != scc)
1632 {
1633 if (edge.acc == acc_cond::mark_t{})
1634 edge.acc = { 0 };
1635 else
1636 edge.acc <<= 1;
1637 }
1638 }
1639 }
1640 }
1641
1642 for (unsigned state = 0; state < res_->num_states(); ++state)
1643 {
1644 unsigned original_state = num2car.at(state).state;
1645 auto state_scc = scc_.scc_of(original_state);
1646 for (auto edge : aut_->out(original_state))
1647 {
1648 if (scc_.scc_of(edge.dst) != state_scc)
1649 {
1650 auto car = state2car.find(edge.dst);
1651 if (car != state2car.end())
1652 {
1653 unsigned res_dst = car2num.at(car->second);
1654 res_->new_edge(state, res_dst, edge.cond, { });
1655 }
1656 }
1657 }
1658 }
1659 unsigned initial_state = aut_->get_init_state_number();
1660 auto initial_car_ptr = state2car.find(initial_state);
1661 car_state initial_car;
1662 // If we take an automaton with one state and without transition,
1663 // the SCC was useless so state2car doesn't have initial_state
1664 if (initial_car_ptr == state2car.end())
1665 {
1666 assert(res_->num_states() == 0);
1667 auto new_state = res_->new_state();
1668 car_state new_car = {initial_state, 0, perm_t()};
1669 state2car[initial_state] = new_car;
1670 if (options.pretty_print)
1671 names->push_back(new_car.to_string(None));
1672 num2car.insert(num2car.begin() + new_state, new_car);
1673 car2num[new_car] = new_state;
1674 initial_car = new_car;
1675 }
1676 else
1677 initial_car = initial_car_ptr->second;
1678 auto initial_state_res = car2num.find(initial_car);
1679 if (initial_state_res != car2num.end())
1680 res_->set_init_state(initial_state_res->second);
1681 else
1682 res_->new_state();
1683 if (options.pretty_print)
1684 res_->set_named_prop("state-names", names);
1685
1686 res_->purge_unreachable_states();
1687 // If parity_prefix is used, we use all available colors by
1688 // default: The IAR/CAR are using lower indices, and the prefix is
1689 // using the upper indices. So we use reduce_parity() to clear
1690 // the mess. If parity_prefix is not used,
1691 unsigned max_color = SPOT_MAX_ACCSETS;
1692 if (!options.parity_prefix)
1693 {
1694 acc_cond::mark_t all = {};
1695 for (auto& e: res_->edges())
1696 all |= e.acc;
1697 max_color = all.max_set();
1698 }
1699 res_->set_acceptance(acc_cond::acc_code::parity_max(is_odd, max_color));
1700 if (options.parity_prefix)
1701 reduce_parity_here(res_);
1702 return res_;
1703 }
1704
1705 private:
1706 const const_twa_graph_ptr &aut_;
1707 const scc_info scc_;
1708 twa_graph_ptr res_;
1709 // Says if we constructing an odd or even max
1710 bool is_odd;
1711
1712 std::vector<car_state> num2car;
1713 std::map<unsigned, car_state> state2car;
1714 std::map<car_state, unsigned> car2num;
1715
1716 to_parity_options options;
1717
1718 std::vector<std::string>* names;
1719 }; // car_generator
1720
1721 }// namespace
1722
1723
1724 twa_graph_ptr
to_parity(const const_twa_graph_ptr & aut,const to_parity_options options)1725 to_parity(const const_twa_graph_ptr &aut, const to_parity_options options)
1726 {
1727 return car_generator(aut, options).run();
1728 }
1729
1730 // Old version of CAR.
1731 namespace
1732 {
1733 struct lar_state
1734 {
1735 unsigned state;
1736 std::vector<unsigned> perm;
1737
operator <spot::__anonca2c3ba71411::lar_state1738 bool operator<(const lar_state& s) const
1739 {
1740 return state == s.state ? perm < s.perm : state < s.state;
1741 }
1742
to_stringspot::__anonca2c3ba71411::lar_state1743 std::string to_string() const
1744 {
1745 std::stringstream s;
1746 s << state << " [";
1747 unsigned ps = perm.size();
1748 for (unsigned i = 0; i != ps; ++i)
1749 {
1750 if (i > 0)
1751 s << ',';
1752 s << perm[i];
1753 }
1754 s << ']';
1755 return s.str();
1756 }
1757 };
1758
1759 class lar_generator
1760 {
1761 const const_twa_graph_ptr& aut_;
1762 twa_graph_ptr res_;
1763 const bool pretty_print;
1764
1765 std::map<lar_state, unsigned> lar2num;
1766 public:
lar_generator(const const_twa_graph_ptr & a,bool pretty_print)1767 explicit lar_generator(const const_twa_graph_ptr& a, bool pretty_print)
1768 : aut_(a)
1769 , res_(nullptr)
1770 , pretty_print(pretty_print)
1771 {}
1772
run()1773 twa_graph_ptr run()
1774 {
1775 res_ = make_twa_graph(aut_->get_dict());
1776 res_->copy_ap_of(aut_);
1777
1778 std::deque<lar_state> todo;
1779 auto get_state = [this, &todo](const lar_state& s)
1780 {
1781 auto it = lar2num.emplace(s, -1U);
1782 if (it.second) // insertion took place
1783 {
1784 unsigned nb = res_->new_state();
1785 it.first->second = nb;
1786 todo.push_back(s);
1787 }
1788 return it.first->second;
1789 };
1790
1791 std::vector<unsigned> initial_perm(aut_->num_sets());
1792 std::iota(initial_perm.begin(), initial_perm.end(), 0);
1793 {
1794 lar_state s0{aut_->get_init_state_number(), initial_perm};
1795 res_->set_init_state(get_state(s0));
1796 }
1797
1798 scc_info si(aut_, scc_info_options::NONE);
1799 // main loop
1800 while (!todo.empty())
1801 {
1802 lar_state current = todo.front();
1803 todo.pop_front();
1804
1805 // TODO: todo could store this number to avoid one lookup
1806 unsigned src_num = get_state(current);
1807
1808 unsigned source_scc = si.scc_of(current.state);
1809 for (const auto& e : aut_->out(current.state))
1810 {
1811 // find the new permutation
1812 std::vector<unsigned> new_perm = current.perm;
1813 unsigned h = 0;
1814 for (unsigned k : e.acc.sets())
1815 {
1816 auto it = std::find(new_perm.begin(), new_perm.end(), k);
1817 h = std::max(h, unsigned(new_perm.end() - it));
1818 std::rotate(it, it+1, new_perm.end());
1819 }
1820
1821 if (source_scc != si.scc_of(e.dst))
1822 {
1823 new_perm = initial_perm;
1824 h = 0;
1825 }
1826
1827 lar_state dst{e.dst, new_perm};
1828 unsigned dst_num = get_state(dst);
1829
1830 // Do the h last elements satisfy the acceptance condition?
1831 // If they do, emit 2h, if they don't emit 2h+1.
1832 acc_cond::mark_t m(new_perm.end() - h, new_perm.end());
1833 bool rej = !aut_->acc().accepting(m);
1834 res_->new_edge(src_num, dst_num, e.cond, {2*h + rej});
1835 }
1836 }
1837
1838 // parity max even
1839 unsigned sets = 2*aut_->num_sets() + 2;
1840 res_->set_acceptance(sets, acc_cond::acc_code::parity_max_even(sets));
1841
1842 if (pretty_print)
1843 {
1844 auto names = new std::vector<std::string>(res_->num_states());
1845 for (const auto& p : lar2num)
1846 (*names)[p.second] = p.first.to_string();
1847 res_->set_named_prop("state-names", names);
1848 }
1849
1850 return res_;
1851 }
1852 };
1853 }
1854
1855 twa_graph_ptr
to_parity_old(const const_twa_graph_ptr & aut,bool pretty_print)1856 to_parity_old(const const_twa_graph_ptr& aut, bool pretty_print)
1857 {
1858 if (!aut->is_existential())
1859 throw std::runtime_error("LAR does not handle alternation");
1860 // if aut is already parity return it as is
1861 if (aut->acc().is_parity())
1862 return std::const_pointer_cast<twa_graph>(aut);
1863
1864 lar_generator gen(aut, pretty_print);
1865 return gen.run();
1866 }
1867
1868 }
1869