1 // -*- coding: utf-8 -*- 2 // Copyright (C) 2015-2021 Laboratoire de Recherche et 3 // Développement 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 <algorithm> 22 #include <deque> 23 #include <stack> 24 #include <utility> 25 #include <unordered_map> 26 #include <set> 27 #include <map> 28 29 #include <spot/misc/bddlt.hh> 30 #include <spot/twaalgos/sccinfo.hh> 31 #include <spot/twaalgos/determinize.hh> 32 #include <spot/twaalgos/degen.hh> 33 #include <spot/twaalgos/sccfilter.hh> 34 #include <spot/twaalgos/simulation.hh> 35 #include <spot/twaalgos/isdet.hh> 36 #include <spot/twaalgos/parity.hh> 37 #include <spot/priv/robin_hood.hh> 38 39 namespace spot 40 { 41 namespace 42 { 43 // forward declaration 44 struct safra_build; 45 class compute_succs; 46 } 47 48 class safra_state final 49 { 50 public: 51 // a helper method to check invariants 52 void check() const53 check() const 54 { 55 // do not refer to braces that do not exist 56 for (const auto& p : nodes_) 57 if (p.second >= 0) 58 if (((unsigned)p.second) >= braces_.size()) 59 assert(false); 60 61 // braces_ describes the parenthood relation, -1 meaning toplevel 62 // so braces_[b] < b always, and -1 is the only negative number allowed 63 for (int b : braces_) 64 { 65 if (b < 0 && b != -1) 66 assert(false); 67 if (b >= 0 && braces_[b] > b) 68 assert(false); 69 } 70 71 // no unused braces 72 std::set<int> used_braces; 73 for (const auto& n : nodes_) 74 { 75 int b = n.second; 76 while (b >= 0) 77 { 78 used_braces.insert(b); 79 b = braces_[b]; 80 } 81 } 82 assert(used_braces.size() == braces_.size()); 83 } 84 85 public: 86 using state_t = unsigned; 87 using safra_node_t = std::pair<state_t, std::vector<int>>; 88 89 bool operator<(const safra_state&) const; 90 bool operator==(const safra_state&) const; 91 size_t hash() const; 92 93 // Print the number of states in each brace 94 // default constructor 95 safra_state(); 96 safra_state(state_t state_number, bool acceptance_scc = false); 97 safra_state(const safra_build& s, const compute_succs& cs, unsigned& color, 98 unsigned topbrace); 99 // Compute successor for transition ap 100 safra_state 101 compute_succ(const compute_succs& cs, const bdd& ap, unsigned& color) const; 102 void 103 merge_redundant_states(const std::vector<std::vector<char>>& implies); 104 unsigned 105 finalize_construction(const std::vector<int>& buildbraces, 106 const compute_succs& cs, unsigned topbrace); 107 108 // each brace points to its parent. 109 // braces_[i] is the parent of i 110 // Note that braces_[i] < i, -1 stands for "no parent" (top-level) 111 std::vector<int> braces_; 112 std::vector<std::pair<state_t, int>> nodes_; 113 }; 114 115 namespace 116 { 117 struct hash_safra 118 { 119 size_t operator ()spot::__anonbe87bc0b0211::hash_safra120 operator()(const safra_state& s) const noexcept 121 { 122 return s.hash(); 123 } 124 }; 125 126 template<class T> 127 struct ref_wrap_equal 128 { 129 bool operator ()spot::__anonbe87bc0b0211::ref_wrap_equal130 operator()(const std::reference_wrapper<T>& x, 131 const std::reference_wrapper<T>& y) const 132 { 133 return std::equal_to<T>()(x.get(), y.get()); 134 } 135 }; 136 137 using power_set = 138 robin_hood::unordered_node_map<safra_state, unsigned, hash_safra>; 139 140 std::string 141 nodes_to_string(const const_twa_graph_ptr& aut, 142 const safra_state& states); 143 144 // Returns true if lhs has a smaller nesting pattern than rhs 145 // If lhs and rhs are the same, return false. 146 // NB the nesting patterns are backwards. nesting_cmp(const std::vector<int> & lhs,const std::vector<int> & rhs)147 bool nesting_cmp(const std::vector<int>& lhs, 148 const std::vector<int>& rhs) 149 { 150 unsigned m = std::min(lhs.size(), rhs.size()); 151 auto lit = lhs.rbegin(); 152 auto rit = rhs.rbegin(); 153 for (unsigned i = 0; i != m; ++i) 154 { 155 if (*lit != *rit) 156 return *lit < *rit; 157 } 158 return lhs.size() > rhs.size(); 159 } 160 161 // a helper class for building the successor of a safra_state 162 struct safra_build final 163 { 164 std::vector<int> braces_; 165 std::map<unsigned, int> nodes_; 166 167 bool compare_bracesspot::__anonbe87bc0b0211::safra_build168 compare_braces(int a, int b) 169 { 170 std::vector<int> a_pattern; 171 std::vector<int> b_pattern; 172 a_pattern.reserve(a+1); 173 b_pattern.reserve(b+1); 174 while (a != b) 175 { 176 if (a > b) 177 { 178 a_pattern.emplace_back(a); 179 a = braces_[a]; 180 } 181 else 182 { 183 b_pattern.emplace_back(b); 184 b = braces_[b]; 185 } 186 } 187 return nesting_cmp(a_pattern, b_pattern); 188 } 189 190 // Used when creating the list of successors 191 // A new intermediate node is created with src's braces and with dst as id 192 // A merge is done if dst already existed in *this 193 void update_succspot::__anonbe87bc0b0211::safra_build194 update_succ(int brace, unsigned dst, const acc_cond::mark_t& acc) 195 { 196 int newb = brace; 197 if (acc) 198 { 199 assert(acc.has(0) && acc.is_singleton() && "Only TBA are accepted"); 200 // Accepting edges generate new braces: step A1 201 newb = braces_.size(); 202 braces_.emplace_back(brace); 203 } 204 auto i = nodes_.emplace(dst, newb); 205 if (!i.second) // dst already exists 206 { 207 // Step A2: Only keep the smallest nesting pattern. 208 // Use nesting_cmp to compare nesting patterns. 209 if (compare_braces(newb, i.first->second)) 210 { 211 i.first->second = newb; 212 } 213 else 214 { 215 if (newb != brace) // new brace was created but is not needed 216 braces_.pop_back(); 217 } 218 } 219 } 220 221 // Same as above, specialized for brace == -1 222 // Acceptance parameter is passed as a template parameter to improve 223 // performance. 224 // If a node for dst already existed, the newly inserted node has smaller 225 // nesting pattern iff is_acc == true AND nodes_[dst] == -1 226 template<bool is_acc> 227 void update_succ_toplevelspot::__anonbe87bc0b0211::safra_build228 update_succ_toplevel(unsigned dst) 229 { 230 if (is_acc) 231 { 232 // Accepting edges generate new braces: step A1 233 int newb = braces_.size(); 234 auto i = nodes_.emplace(dst, newb); 235 if (i.second || i.first->second == -1) 236 { 237 braces_.emplace_back(-1); 238 i.first->second = newb; 239 } 240 } 241 else 242 { 243 nodes_.emplace(dst, -1); 244 } 245 } 246 247 }; 248 249 // Given a certain transition_label, compute all the successors of a 250 // safra_state under that label, and return the new nodes in res. 251 class compute_succs final 252 { 253 friend class spot::safra_state; 254 255 const safra_state* src; 256 const std::vector<bdd>* all_bdds; 257 const const_twa_graph_ptr& aut; 258 const power_set& seen; 259 const scc_info& scc; 260 const std::vector<std::vector<char>>& implies; 261 bool use_scc; 262 bool use_simulation; 263 bool use_stutter; 264 265 // work vectors for safra_state::finalize_construction() 266 mutable std::vector<char> empty_green; 267 mutable std::vector<int> highest_green_ancestor; 268 mutable std::vector<unsigned> decr_by; 269 mutable safra_build ss; 270 271 public: compute_succs(const const_twa_graph_ptr & aut,const power_set & seen,const scc_info & scc,const std::vector<std::vector<char>> & implies,bool use_scc,bool use_simulation,bool use_stutter)272 compute_succs(const const_twa_graph_ptr& aut, 273 const power_set& seen, 274 const scc_info& scc, 275 const std::vector<std::vector<char>>& implies, 276 bool use_scc, 277 bool use_simulation, 278 bool use_stutter) 279 : src(nullptr) 280 , all_bdds(nullptr) 281 , aut(aut) 282 , seen(seen) 283 , scc(scc) 284 , implies(implies) 285 , use_scc(use_scc) 286 , use_simulation(use_simulation) 287 , use_stutter(use_stutter) 288 {} 289 290 void set(const safra_state & s,const std::vector<bdd> & v)291 set(const safra_state& s, const std::vector<bdd>& v) 292 { 293 src = &s; 294 all_bdds = &v; 295 } 296 297 struct iterator 298 { 299 const compute_succs& cs_; 300 std::vector<bdd>::const_iterator bddit; 301 safra_state ss; 302 unsigned color_; 303 iteratorspot::__anonbe87bc0b0211::compute_succs::iterator304 iterator(const compute_succs& c, std::vector<bdd>::const_iterator it) 305 : cs_(c) 306 , bddit(it) 307 { 308 compute_(); 309 } 310 311 bool operator !=spot::__anonbe87bc0b0211::compute_succs::iterator312 operator!=(const iterator& other) const 313 { 314 return bddit != other.bddit; 315 } 316 317 iterator& operator ++spot::__anonbe87bc0b0211::compute_succs::iterator318 operator++() 319 { 320 ++bddit; 321 compute_(); 322 return *this; 323 } 324 // no need to implement postfix increment 325 326 const bdd& condspot::__anonbe87bc0b0211::compute_succs::iterator327 cond() const 328 { 329 return *bddit; 330 } 331 332 const safra_state& operator *spot::__anonbe87bc0b0211::compute_succs::iterator333 operator*() const 334 { 335 return ss; 336 } 337 const safra_state* operator ->spot::__anonbe87bc0b0211::compute_succs::iterator338 operator->() const 339 { 340 return &ss; 341 } 342 343 private: 344 std::vector<safra_state> stutter_path_; 345 346 void compute_spot::__anonbe87bc0b0211::compute_succs::iterator347 compute_() 348 { 349 if (bddit == cs_.all_bdds->end()) 350 return; 351 352 const bdd& ap = *bddit; 353 354 // In stutter-invariant automata, every time we follow a 355 // transition labeled by L, we can actually stutter the L 356 // label and jump further away. The following code performs 357 // this stuttering until a cycle is found, and select one 358 // state of the cycle as the destination to jump to. 359 if (cs_.use_stutter && cs_.aut->prop_stutter_invariant()) 360 { 361 ss = *cs_.src; 362 // The path is usually quite small (3-4 states), so it's 363 // not worth setting up a hash table to detect a cycle. 364 stutter_path_.clear(); 365 std::vector<safra_state>::iterator cycle_seed; 366 unsigned mincolor = -1U; 367 // stutter forward until we cycle 368 for (;;) 369 { 370 // any duplicate value, if any, is usually close to 371 // the end, so search backward. 372 auto it = std::find(stutter_path_.rbegin(), 373 stutter_path_.rend(), ss); 374 if (it != stutter_path_.rend()) 375 { 376 cycle_seed = (it + 1).base(); 377 break; 378 } 379 stutter_path_.emplace_back(std::move(ss)); 380 ss = stutter_path_.back().compute_succ(cs_, ap, color_); 381 mincolor = std::min(color_, mincolor); 382 } 383 bool in_seen = cs_.seen.find(*cycle_seed) != cs_.seen.end(); 384 for (auto it = cycle_seed + 1; it < stutter_path_.end(); ++it) 385 { 386 if (in_seen) 387 { 388 // if *cycle_seed is already in seen, replace 389 // it with a smaller state also in seen. 390 if (cs_.seen.find(*it) != cs_.seen.end() 391 && *it < *cycle_seed) 392 cycle_seed = it; 393 } 394 else 395 { 396 // if *cycle_seed is not in seen, replace it 397 // either with a state in seen or with a smaller 398 // state 399 if (cs_.seen.find(*it) != cs_.seen.end()) 400 { 401 cycle_seed = it; 402 in_seen = true; 403 } 404 else if (*it < *cycle_seed) 405 { 406 cycle_seed = it; 407 } 408 } 409 } 410 ss = std::move(*cycle_seed); 411 color_ = mincolor; 412 } 413 else 414 { 415 ss = cs_.src->compute_succ(cs_, ap, color_); 416 } 417 } 418 }; 419 420 iterator begin() const421 begin() const 422 { 423 return iterator(*this, all_bdds->begin()); 424 } 425 iterator end() const426 end() const 427 { 428 return iterator(*this, all_bdds->end()); 429 } 430 }; 431 432 const char* const sub[10] = 433 { 434 "\u2080", 435 "\u2081", 436 "\u2082", 437 "\u2083", 438 "\u2084", 439 "\u2085", 440 "\u2086", 441 "\u2087", 442 "\u2088", 443 "\u2089", 444 }; 445 subscript(unsigned start)446 std::string subscript(unsigned start) 447 { 448 std::string res; 449 do 450 { 451 res = sub[start % 10] + res; 452 start /= 10; 453 } 454 while (start); 455 return res; 456 } 457 458 struct compare 459 { 460 bool operator ()spot::__anonbe87bc0b0211::compare461 operator() (const safra_state::safra_node_t& lhs, 462 const safra_state::safra_node_t& rhs) const 463 { 464 return lhs.second < rhs.second; 465 } 466 }; 467 468 // Return the nodes sorted in ascending order 469 std::vector<safra_state::safra_node_t> sorted_nodes(const safra_state & s)470 sorted_nodes(const safra_state& s) 471 { 472 std::vector<safra_state::safra_node_t> res; 473 for (const auto& n: s.nodes_) 474 { 475 int brace = n.second; 476 std::vector<int> tmp; 477 while (brace >= 0) 478 { 479 // FIXME is not there a smarter way? 480 tmp.insert(tmp.begin(), brace); 481 brace = s.braces_[brace]; 482 } 483 res.emplace_back(n.first, std::move(tmp)); 484 } 485 std::sort(res.begin(), res.end(), compare()); 486 return res; 487 } 488 489 std::string nodes_to_string(const const_twa_graph_ptr & aut,const safra_state & states)490 nodes_to_string(const const_twa_graph_ptr& aut, 491 const safra_state& states) 492 { 493 auto copy = sorted_nodes(states); 494 std::ostringstream os; 495 std::stack<int> s; 496 bool first = true; 497 for (const auto& n: copy) 498 { 499 auto it = n.second.begin(); 500 // Find brace on top of stack in vector 501 // If brace is not present, then we close it as no other ones of that 502 // type will be found since we ordered our vector 503 while (!s.empty()) 504 { 505 it = std::lower_bound(n.second.begin(), n.second.end(), 506 s.top()); 507 if (it == n.second.end() || *it != s.top()) 508 { 509 os << subscript(s.top()) << '}'; 510 s.pop(); 511 } 512 else 513 { 514 if (*it == s.top()) 515 ++it; 516 break; 517 } 518 } 519 // Add new braces 520 while (it != n.second.end()) 521 { 522 os << '{' << subscript(*it); 523 s.push(*it); 524 ++it; 525 first = true; 526 } 527 if (!first) 528 os << ' '; 529 os << aut->format_state(n.first); 530 first = false; 531 } 532 // Finish unwinding stack to print last braces 533 while (!s.empty()) 534 { 535 os << subscript(s.top()) << '}'; 536 s.pop(); 537 } 538 return os.str(); 539 } 540 541 std::vector<std::string>* print_debug(const const_twa_graph_ptr & aut,const power_set & states)542 print_debug(const const_twa_graph_ptr& aut, 543 const power_set& states) 544 { 545 auto res = new std::vector<std::string>(states.size()); 546 for (const auto& p: states) 547 (*res)[p.second] = nodes_to_string(aut, p.first); 548 return res; 549 } 550 551 class safra_support 552 { 553 const std::vector<bdd>& state_supports; 554 robin_hood::unordered_flat_map<bdd, std::vector<bdd>, bdd_hash> cache; 555 556 public: safra_support(const std::vector<bdd> & s)557 safra_support(const std::vector<bdd>& s): state_supports(s) {} 558 559 const std::vector<bdd>& get(const safra_state & s)560 get(const safra_state& s) 561 { 562 bdd supp = bddtrue; 563 for (const auto& n : s.nodes_) 564 supp &= state_supports[n.first]; 565 auto i = cache.emplace(supp, std::vector<bdd>()); 566 if (i.second) // insertion took place 567 { 568 std::vector<bdd>& res = i.first->second; 569 for (bdd one: minterms_of(bddtrue, supp)) 570 res.emplace_back(one); 571 } 572 return i.first->second; 573 } 574 }; 575 } 576 577 std::vector<char> find_scc_paths(const scc_info& scc); 578 579 safra_state compute_succ(const compute_succs & cs,const bdd & ap,unsigned & color) const580 safra_state::compute_succ(const compute_succs& cs, 581 const bdd& ap, unsigned& color) const 582 { 583 safra_build& ss = cs.ss; 584 ss.braces_ = braces_; // copy 585 ss.nodes_.clear(); 586 unsigned topbrace = braces_.size(); 587 for (const auto& node: nodes_) 588 { 589 for (const auto& t: cs.aut->out(node.first)) 590 { 591 if (!bdd_implies(ap, t.cond)) 592 continue; 593 // Check if we are leaving the SCC, if so we delete all the 594 // braces as no cycles can be found with that node 595 if (cs.use_scc && cs.scc.scc_of(node.first) != cs.scc.scc_of(t.dst)) 596 if (cs.scc.is_accepting_scc(cs.scc.scc_of(t.dst))) 597 // Entering accepting SCC so add brace 598 ss.update_succ_toplevel<true>(t.dst); 599 else 600 // When entering non accepting SCC don't create any braces 601 ss.update_succ_toplevel<false>(t.dst); 602 else 603 ss.update_succ(node.second, t.dst, t.acc); 604 } 605 } 606 return safra_state(ss, cs, color, topbrace); 607 } 608 609 // When a node a implies a node b, remove the node a. 610 void merge_redundant_states(const std::vector<std::vector<char>> & implies)611 safra_state::merge_redundant_states( 612 const std::vector<std::vector<char>>& implies) 613 { 614 auto it1 = nodes_.begin(); 615 while (it1 != nodes_.end()) 616 { 617 const auto& imp1 = implies[it1->first]; 618 auto old_it1 = it1++; 619 if (imp1.empty()) 620 continue; 621 for (auto it2 = nodes_.begin(); it2 != nodes_.end(); ++it2) 622 { 623 if (old_it1 == it2) 624 continue; 625 if (imp1[it2->first]) 626 { 627 it1 = nodes_.erase(old_it1); 628 break; 629 } 630 } 631 } 632 } 633 634 // Return the emitted color, red or green 635 unsigned finalize_construction(const std::vector<int> & buildbraces,const compute_succs & cs,unsigned topbrace)636 safra_state::finalize_construction(const std::vector<int>& buildbraces, 637 const compute_succs& cs, unsigned topbrace) 638 { 639 unsigned red = -1U; 640 unsigned green = -1U; 641 // use std::vector<char> to avoid std::vector<bool> 642 // a char encodes several bools: 643 // * first bit says whether the brace is empty and red 644 // * second bit says whether the brace is green 645 // brackets removed from green pairs can be safely be marked as red, 646 // because their enclosing green has a lower number 647 // beware of pairs marked both as red and green: they are actually empty 648 constexpr char is_empty = 1; 649 constexpr char is_green = 2; 650 cs.empty_green.assign(buildbraces.size(), is_empty | is_green); 651 652 for (const auto& n : nodes_) 653 if (n.second >= 0) 654 { 655 int brace = n.second; 656 // Step A4: For a brace to be green it must not contain states 657 // on its own. 658 cs.empty_green[brace] &= ~is_green; 659 while (brace >= 0 && (cs.empty_green[brace] & is_empty)) 660 { 661 cs.empty_green[brace] &= ~is_empty; 662 brace = buildbraces[brace]; 663 } 664 } 665 666 // Step A4 Remove brackets within green pairs 667 // for each bracket, find its highest green ancestor 668 // 0 cannot be in a green pair, its highest green ancestor is itself 669 // Also find red and green signals to emit 670 // And compute the number of braces to remove for renumbering 671 cs.highest_green_ancestor.assign(buildbraces.size(), 0); 672 cs.decr_by.assign(buildbraces.size(), 0); 673 unsigned decr = 0; 674 for (unsigned b = 0; b != buildbraces.size(); ++b) 675 { 676 cs.highest_green_ancestor[b] = b; 677 const int& ancestor = buildbraces[b]; 678 // Note that ancestor < b 679 if (ancestor >= 0 680 && (cs.highest_green_ancestor[ancestor] != ancestor 681 || (cs.empty_green[ancestor] & is_green))) 682 { 683 cs.highest_green_ancestor[b] = cs.highest_green_ancestor[ancestor]; 684 cs.empty_green[b] |= is_empty; // mark brace for removal 685 } 686 687 if (cs.empty_green[b] & is_empty) 688 { 689 // Step A5 renumber braces 690 ++decr; 691 692 // Any brace above topbrace was added while constructing 693 // this successor, so it should not emit any red. 694 if (b < topbrace) 695 // Step A3 emit red 696 red = std::min(red, 2*b); 697 } 698 else if (cs.empty_green[b] & is_green) 699 { 700 assert(b < topbrace); 701 // Step A4 emit green 702 green = std::min(green, 2*b+1); 703 } 704 705 cs.decr_by[b] = decr; 706 } 707 708 // Update nodes with new braces numbers 709 braces_ = std::vector<int>(buildbraces.size() - decr, -1); 710 for (auto& n : nodes_) 711 { 712 if (n.second >= 0) 713 { 714 unsigned i = cs.highest_green_ancestor[n.second]; 715 int j = buildbraces[i] >=0 716 ? buildbraces[i] - cs.decr_by[buildbraces[i]] 717 : -1; 718 n.second = i - cs.decr_by[i]; 719 braces_[n.second] = j; 720 } 721 } 722 723 return std::min(red, green); 724 } 725 safra_state()726 safra_state::safra_state() 727 : nodes_{std::make_pair(0, -1)} 728 {} 729 730 // Called only to initialize first state safra_state(state_t val,bool accepting_scc)731 safra_state::safra_state(state_t val, bool accepting_scc) 732 : nodes_{std::make_pair(val, -1)} 733 { 734 if (accepting_scc) 735 { 736 braces_.emplace_back(-1); 737 nodes_.back().second = 0; 738 } 739 } 740 safra_state(const safra_build & s,const compute_succs & cs,unsigned & color,unsigned topbrace)741 safra_state::safra_state(const safra_build& s, 742 const compute_succs& cs, 743 unsigned& color, 744 unsigned topbrace) 745 : nodes_(s.nodes_.begin(), s.nodes_.end()) 746 { 747 if (cs.use_simulation) 748 merge_redundant_states(cs.implies); 749 color = finalize_construction(s.braces_, cs, topbrace); 750 } 751 752 bool operator <(const safra_state & other) const753 safra_state::operator<(const safra_state& other) const 754 { 755 // FIXME what is the right, if any, comparison to perform? 756 return braces_ == other.braces_ ? nodes_ < other.nodes_ 757 : braces_ < other.braces_; 758 } 759 size_t hash() const760 safra_state::hash() const 761 { 762 size_t res = 0; 763 //std::cerr << this << " ["; 764 for (const auto& p : nodes_) 765 { 766 res ^= (res << 3) ^ p.first; 767 res ^= (res << 3) ^ p.second; 768 // std::cerr << '(' << p.first << ',' << p.second << ')'; 769 } 770 // std::cerr << "][ "; 771 for (const auto& b : braces_) 772 { 773 res ^= (res << 3) ^ b; 774 // std::cerr << b << ' '; 775 } 776 // std::cerr << "]: " << std::hex << res << std::dec << '\n'; 777 return res; 778 } 779 780 bool operator ==(const safra_state & other) const781 safra_state::operator==(const safra_state& other) const 782 { 783 return nodes_ == other.nodes_ && braces_ == other.braces_; 784 } 785 786 // res[i + scccount*j] = 1 iff SCC i is reachable from SCC j 787 std::vector<char> find_scc_paths(const scc_info & scc)788 find_scc_paths(const scc_info& scc) 789 { 790 unsigned scccount = scc.scc_count(); 791 std::vector<char> res(scccount * scccount, 0); 792 for (unsigned i = 0; i != scccount; ++i) 793 res[i + scccount * i] = 1; 794 for (unsigned i = 0; i != scccount; ++i) 795 { 796 unsigned ibase = i * scccount; 797 for (unsigned d: scc.succ(i)) 798 { 799 // we necessarily have d < i because of the way SCCs are 800 // numbered, so we can build the transitive closure by 801 // just ORing any SCC reachable from d. 802 unsigned dbase = d * scccount; 803 for (unsigned j = 0; j != scccount; ++j) 804 res[ibase + j] |= res[dbase + j]; 805 } 806 } 807 return res; 808 } 809 810 twa_graph_ptr tgba_determinize(const const_twa_graph_ptr & a,bool pretty_print,bool use_scc,bool use_simulation,bool use_stutter,const output_aborter * aborter,int trans_pruning)811 tgba_determinize(const const_twa_graph_ptr& a, 812 bool pretty_print, bool use_scc, 813 bool use_simulation, bool use_stutter, 814 const output_aborter* aborter, 815 int trans_pruning) 816 { 817 if (!a->is_existential()) 818 throw std::runtime_error 819 ("tgba_determinize() does not support alternation"); 820 if (is_universal(a)) 821 return std::const_pointer_cast<twa_graph>(a); 822 823 // Degeneralize 824 const_twa_graph_ptr aut; 825 std::vector<bdd> implications; 826 { 827 twa_graph_ptr aut_tmp = spot::degeneralize_tba(a); 828 if (pretty_print) 829 aut_tmp->copy_state_names_from(a); 830 if (use_simulation) 831 { 832 aut_tmp = spot::scc_filter(aut_tmp); 833 auto aut2 = simulation(aut_tmp, &implications, trans_pruning); 834 if (pretty_print) 835 aut2->copy_state_names_from(aut_tmp); 836 aut_tmp = aut2; 837 } 838 aut = aut_tmp; 839 } 840 scc_info_options scc_opt = scc_info_options::TRACK_SUCCS; 841 // We do need to track states in SCC for stutter invariance (see below how 842 // supports are computed in this case) 843 if (use_stutter && aut->prop_stutter_invariant()) 844 scc_opt = scc_info_options::TRACK_SUCCS | scc_info_options::TRACK_STATES; 845 scc_info scc = scc_info(aut, scc_opt); 846 847 // If use_simulation is false, implications is empty, so nothing is built 848 std::vector<std::vector<char>> implies( 849 implications.size(), 850 std::vector<char>(implications.size(), 0)); 851 { 852 std::vector<char> is_connected = find_scc_paths(scc); 853 unsigned sccs = scc.scc_count(); 854 bool something_implies_something = false; 855 for (unsigned i = 0; i != implications.size(); ++i) 856 { 857 // NB spot::simulation() does not remove unreachable states, as it 858 // would invalidate the contents of 'implications'. 859 // so we need to explicitly test for unreachable states 860 // FIXME based on the scc_info, we could remove the unreachable 861 // states, both in the input automaton and in 'implications' 862 // to reduce the size of 'implies'. 863 if (!scc.reachable_state(i)) 864 continue; 865 unsigned scc_of_i = scc.scc_of(i); 866 bool i_implies_something = false; 867 for (unsigned j = 0; j != implications.size(); ++j) 868 { 869 if (!scc.reachable_state(j)) 870 continue; 871 872 bool i_implies_j = !is_connected[sccs * scc.scc_of(j) + scc_of_i] 873 && bdd_implies(implications[i], implications[j]); 874 implies[i][j] = i_implies_j; 875 i_implies_something |= i_implies_j; 876 } 877 // Clear useless lines. 878 if (!i_implies_something) 879 implies[i].clear(); 880 else 881 something_implies_something = true; 882 } 883 if (!something_implies_something) 884 { 885 implies.clear(); 886 use_simulation = false; 887 } 888 } 889 890 891 // Compute the support of each state 892 std::vector<bdd> support(aut->num_states()); 893 if (use_stutter && aut->prop_stutter_invariant()) 894 { 895 // FIXME this could be improved 896 // supports of states should account for possible stuttering if we plan 897 // to use stuttering invariance 898 for (unsigned c = 0; c != scc.scc_count(); ++c) 899 { 900 bdd c_supp = scc.scc_ap_support(c); 901 for (const auto& su: scc.succ(c)) 902 c_supp &= support[scc.one_state_of(su)]; 903 for (unsigned st: scc.states_of(c)) 904 support[st] = c_supp; 905 } 906 } 907 else 908 { 909 for (unsigned i = 0; i != aut->num_states(); ++i) 910 { 911 bdd res = bddtrue; 912 for (const auto& e : aut->out(i)) 913 res &= bdd_support(e.cond); 914 support[i] = res; 915 } 916 } 917 918 safra_support safra2letters(support); 919 920 auto res = make_twa_graph(aut->get_dict()); 921 res->copy_ap_of(aut); 922 res->prop_copy(aut, 923 { false, // state based 924 false, // inherently_weak 925 false, false, // deterministic 926 false, // complete 927 true // stutter inv 928 }); 929 // completeness can only be improved. 930 if (aut->prop_complete().is_true()) 931 res->prop_complete(true); 932 933 // Given a safra_state get its associated state in output automata. 934 // Required to create new edges from 2 safra-state 935 power_set seen; 936 // As per the standard, references to elements in a std::unordered_set or 937 // std::unordered_map are invalidated by erasure only. 938 std::deque<std::reference_wrapper<power_set::value_type>> todo; 939 auto get_state = [&res, &seen, &todo](const safra_state& s) -> unsigned 940 { 941 auto it = seen.find(s); 942 if (it == seen.end()) 943 { 944 unsigned dst_num = res->new_state(); 945 it = seen.emplace(s, dst_num).first; 946 todo.emplace_back(*it); 947 } 948 return it->second; 949 }; 950 951 { 952 unsigned init_state = aut->get_init_state_number(); 953 bool start_accepting = 954 !use_scc || scc.is_accepting_scc(scc.scc_of(init_state)); 955 safra_state init(init_state, start_accepting); 956 unsigned num = get_state(init); // inserts both in seen and in todo 957 res->set_init_state(num); 958 } 959 unsigned sets = 0; 960 961 compute_succs succs(aut, seen, scc, implies, use_scc, use_simulation, 962 use_stutter); 963 // The main loop 964 while (!todo.empty()) 965 { 966 if (aborter && aborter->too_large(res)) 967 return nullptr; 968 const safra_state& curr = todo.front().get().first; 969 unsigned src_num = todo.front().get().second; 970 todo.pop_front(); 971 succs.set(curr, safra2letters.get(curr)); 972 for (auto s = succs.begin(); s != succs.end(); ++s) 973 { 974 // Don't construct sink state as complete does a better job at this 975 if (s->nodes_.empty()) 976 continue; 977 unsigned dst_num = get_state(*s); 978 if (s.color_ != -1U) 979 { 980 res->new_edge(src_num, dst_num, s.cond(), {s.color_}); 981 sets = std::max(s.color_ + 1, sets); 982 } 983 else 984 res->new_edge(src_num, dst_num, s.cond()); 985 } 986 } 987 // Green and red colors work in pairs, so the number of parity conditions is 988 // necessarily even. 989 sets += sets & 1; 990 // Acceptance is now min(odd) since we can emit Red on paths 0 with new opti 991 res->set_acceptance(sets, acc_cond::acc_code::parity_min_odd(sets)); 992 res->prop_universal(true); 993 res->prop_state_acc(false); 994 995 cleanup_parity_here(res); 996 997 if (pretty_print) 998 res->set_named_prop("state-names", print_debug(aut, seen)); 999 return res; 1000 } 1001 } 1002