diff --git a/src/dstarparse/dra2ba.cc b/src/dstarparse/dra2ba.cc index 7431e097d..83ba45898 100644 --- a/src/dstarparse/dra2ba.cc +++ b/src/dstarparse/dra2ba.cc @@ -231,10 +231,11 @@ namespace spot realizable_(realizable) { out_->copy_ap_of(a->aut); - out_->set_bprop(tgba_digraph::StateBasedAcc); + out_->prop_state_based_acc(); acc_ = out_->set_single_acceptance_set(); out_->new_states(num_states_ * (a->accpair_count + 1)); out_->set_init_state(a->aut->get_init_state_number()); + } tgba_digraph_ptr diff --git a/src/dstarparse/nra2nba.cc b/src/dstarparse/nra2nba.cc index c599674cd..89e9e936c 100644 --- a/src/dstarparse/nra2nba.cc +++ b/src/dstarparse/nra2nba.cc @@ -46,7 +46,7 @@ namespace spot { out_->copy_ap_of(aut); out_->set_single_acceptance_set(); - out_->set_bprop(tgba_digraph::StateBasedAcc); + out_->prop_state_based_acc(); out_->new_states(num_states_ * (d_->accpair_count + 1)); auto i = aut->get_init_state(); diff --git a/src/neverparse/neverclaimparse.yy b/src/neverparse/neverclaimparse.yy index 31f6d93b9..40caef278 100644 --- a/src/neverparse/neverclaimparse.yy +++ b/src/neverparse/neverclaimparse.yy @@ -303,7 +303,7 @@ namespace spot tgba_digraph_ptr result = make_tgba_digraph(dict); auto namer = result->create_namer(); result->set_single_acceptance_set(); - result->set_bprop(tgba_digraph::SBA); + result->prop_state_based_acc(); neverclaimyy::parser parser(error_list, env, result, namer, fcache); parser.set_debug_level(debug); diff --git a/src/saba/sabacomplementtgba.cc b/src/saba/sabacomplementtgba.cc index 7065497f9..d6ba021a3 100644 --- a/src/saba/sabacomplementtgba.cc +++ b/src/saba/sabacomplementtgba.cc @@ -186,7 +186,7 @@ namespace spot : automaton_(automaton), the_acceptance_cond_(the_acceptance_cond), origin_(origin) { - assert(automaton->get_bprop(tgba_digraph::SBA)); + assert(automaton->is_sba()); // If state not accepting or rank is even if (((origin_->get_rank() & 1) == 0) || !automaton_->state_is_accepting(origin_->get_state())) diff --git a/src/tgba/tgba.cc b/src/tgba/tgba.cc index 056f7082c..6f957edee 100644 --- a/src/tgba/tgba.cc +++ b/src/tgba/tgba.cc @@ -29,6 +29,7 @@ namespace spot last_support_conditions_input_(0), num_acc_(-1) { + props = 0U; } tgba::~tgba() diff --git a/src/tgba/tgba.hh b/src/tgba/tgba.hh index bc9e0c9cf..83212c143 100644 --- a/src/tgba/tgba.hh +++ b/src/tgba/tgba.hh @@ -251,6 +251,89 @@ namespace spot private: mutable bdd last_support_conditions_output_; mutable int num_acc_; + + protected: + + // Boolean properties. Beware: true means that the property + // holds, but false means the property is unknown. + struct bprop + { + bool single_acc_set:1; // A single acceptance set. + bool state_based_acc:1; // State-based acceptance. + bool inherently_weak:1; // Weak automaton. + bool deterministic:1; // Deterministic automaton. + }; + union + { + unsigned props; + bprop is; + }; + + public: + + bool has_single_acc_set() const + { + return is.single_acc_set; + } + + void prop_single_acc_set(bool val = true) + { + is.single_acc_set = val; + } + + bool has_state_based_acc() const + { + return is.state_based_acc; + } + + void prop_state_based_acc(bool val = true) + { + is.state_based_acc = val; + } + + bool is_sba() const + { + return has_state_based_acc() && has_single_acc_set(); + } + + bool is_inherently_weak() const + { + return is.inherently_weak; + } + + void prop_inherently_weak(bool val = true) + { + is.inherently_weak = val; + } + + bool is_deterministic() const + { + return is.deterministic; + } + + void prop_deterministic(bool val = true) + { + is.deterministic = val; + } + + // This is no default value here on purpose. This way any time we + // add a new property we cannot to update every call to prop_copy(). + void prop_copy(const const_tgba_ptr& other, + bool state_based, + bool single_acc, + bool inherently_weak, + bool deterministic) + { + if (state_based) + prop_state_based_acc(other->has_state_based_acc()); + if (single_acc) + prop_single_acc_set(other->has_single_acc_set()); + if (inherently_weak) + prop_inherently_weak(other->is_inherently_weak()); + if (deterministic) + prop_deterministic(other->is_deterministic()); + } + }; /// \addtogroup tgba_representation TGBA representations diff --git a/src/tgba/tgbagraph.cc b/src/tgba/tgbagraph.cc index 570612577..4e95bad6e 100644 --- a/src/tgba/tgbagraph.cc +++ b/src/tgba/tgbagraph.cc @@ -39,10 +39,7 @@ namespace spot all_acceptance_conditions_ = compute_all_acceptance_conditions(neg_acceptance_conditions_); - if (number_of_acceptance_conditions() == 1) - set_bprop(tgba_digraph::SingleAccSet); - else - clear_bprop(tgba_digraph::SingleAccSet); + prop_single_acc_set(number_of_acceptance_conditions() == 1); } bdd tgba_digraph::set_single_acceptance_set() @@ -50,7 +47,8 @@ namespace spot if (all_acceptance_conditions_ != bddfalse) dict_->unregister_all_typed_variables(bdd_dict::acc, this); - set_bprop(tgba_digraph::SingleAccSet); + prop_single_acc_set(); + int accvar = dict_->register_acceptance_variable(ltl::constant::true_instance(), this); diff --git a/src/tgba/tgbagraph.hh b/src/tgba/tgbagraph.hh index c6a2ad91d..70831478e 100644 --- a/src/tgba/tgbagraph.hh +++ b/src/tgba/tgbagraph.hh @@ -390,34 +390,9 @@ namespace spot /// extremities and acceptance. void merge_transitions(); - protected: - unsigned bprops_ = 0; - - public: - enum bprop { - StateBasedAcc = 1, - SingleAccSet = 2, - SBA = StateBasedAcc | SingleAccSet, - }; - - bool get_bprop(bprop p) const - { - return (bprops_ & p) == p; - } - - void set_bprop(bprop p) - { - bprops_ |= p; - } - - void clear_bprop(bprop p) - { - bprops_ &= ~p; - } - bool state_is_accepting(unsigned s) const { - assert(get_bprop(StateBasedAcc)); + assert(has_state_based_acc()); for (auto& t: g_.out(s)) // Stop at the first transition, since the remaining should be // labeled identically. diff --git a/src/tgbaalgos/degen.cc b/src/tgbaalgos/degen.cc index 11e2127e1..1ee5be133 100644 --- a/src/tgbaalgos/degen.cc +++ b/src/tgbaalgos/degen.cc @@ -264,7 +264,9 @@ namespace spot res->copy_ap_of(a); res->set_single_acceptance_set(); if (want_sba) - res->set_bprop(tgba_digraph::StateBasedAcc); + res->prop_state_based_acc(); + // Preserve determinism and weakness + res->prop_copy(a, false, false, true, true); // Create an order of acceptance conditions. Each entry in this // vector correspond to an acceptance set. Each index can @@ -624,8 +626,8 @@ namespace spot { // If this already a degeneralized digraph, there is nothing we // can improve. - if (auto d = std::dynamic_pointer_cast(a)) - if (d->get_bprop(tgba_digraph::SBA)) + if (a->is_sba()) + if (auto d = std::dynamic_pointer_cast(a)) return std::const_pointer_cast(d); return degeneralize_aux(a, use_z_lvl, use_cust_acc_orders, @@ -639,8 +641,8 @@ namespace spot { // If this already a degeneralized digraph, there is nothing we // can improve. - if (auto d = std::dynamic_pointer_cast(a)) - if (d->get_bprop(tgba_digraph::SingleAccSet)) + if (a->has_single_acc_set()) + if (auto d = std::dynamic_pointer_cast(a)) return std::const_pointer_cast(d); return degeneralize_aux(a, use_z_lvl, use_cust_acc_orders, diff --git a/src/tgbaalgos/dotty.cc b/src/tgbaalgos/dotty.cc index db6f42fbb..8ef1151d8 100644 --- a/src/tgbaalgos/dotty.cc +++ b/src/tgbaalgos/dotty.cc @@ -129,10 +129,7 @@ namespace spot { if (!dd) dd = dotty_decorator::instance(); - if (auto gd = dynamic_cast(g.get())) - assume_sba |= gd->get_bprop(tgba_digraph::StateBasedAcc); - - dotty_bfs d(os, g, assume_sba, dd); + dotty_bfs d(os, g, assume_sba || g->has_state_based_acc(), dd); d.run(); return os; } diff --git a/src/tgbaalgos/isdet.cc b/src/tgbaalgos/isdet.cc index 94aef3812..2c33082e9 100644 --- a/src/tgbaalgos/isdet.cc +++ b/src/tgbaalgos/isdet.cc @@ -25,9 +25,10 @@ namespace spot { namespace { + template static unsigned - count_nondet_states_aux(const const_tgba_ptr& aut, bool count = true) + count_nondet_states_aux(const const_tgba_ptr& aut) { unsigned res = 0; typedef std::deque todo_list; @@ -82,13 +83,15 @@ namespace spot unsigned count_nondet_states(const const_tgba_ptr& aut) { - return count_nondet_states_aux(aut); + return count_nondet_states_aux(aut); } bool is_deterministic(const const_tgba_ptr& aut) { - return !count_nondet_states_aux(aut, false); + if (aut->is_deterministic()) + return true; + return !count_nondet_states_aux(aut); } bool diff --git a/src/tgbaalgos/lbtt.cc b/src/tgbaalgos/lbtt.cc index 5396d5794..c4dcd3e28 100644 --- a/src/tgbaalgos/lbtt.cc +++ b/src/tgbaalgos/lbtt.cc @@ -79,13 +79,13 @@ namespace spot all_acc_conds_(a->all_acceptance_conditions()), acs_(all_acc_conds_), sba_format_(sba_format), - // Check if the automaton can be converted into a - // tgba_digraph. This makes the state_is_accepting() - // function more efficient. - sba_(std::dynamic_pointer_cast(a)) + sba_(nullptr) { - if (sba_ && !sba_->get_bprop(tgba_digraph::SBA)) - sba_ = nullptr; + // Check if the automaton can be converted into a + // tgba_digraph. This makes the state_is_accepting() function + // more efficient. + if (a->is_sba()) + sba_ = std::dynamic_pointer_cast(a); } bool @@ -243,7 +243,7 @@ namespace spot auto aut = make_tgba_digraph(dict); acc_mapper_int acc_b(aut, num_acc, envacc); aut->new_states(num_states); - aut->set_bprop(tgba_digraph::StateBasedAcc); + aut->prop_state_based_acc(); for (unsigned n = 0; n < num_states; ++n) { diff --git a/src/tgbaalgos/minimize.cc b/src/tgbaalgos/minimize.cc index 6309fbca2..9b6c82c9c 100644 --- a/src/tgbaalgos/minimize.cc +++ b/src/tgbaalgos/minimize.cc @@ -122,7 +122,7 @@ namespace spot auto dict = a->get_dict(); auto res = make_tgba_digraph(dict); res->copy_ap_of(a); - res->set_bprop(tgba_digraph::StateBasedAcc); + res->prop_state_based_acc(); // For each set, create a state in the resulting automaton. // For a state s, state_num[s] is the number of the state in the minimal @@ -499,8 +499,10 @@ namespace spot // non_final contain all states. // final is empty: there is no acceptance condition build_state_set(det_a, non_final); - - return minimize_dfa(det_a, final, non_final); + auto res = minimize_dfa(det_a, final, non_final); + res->prop_deterministic(); + res->prop_inherently_weak(); + return res; } tgba_digraph_ptr minimize_wdba(const const_tgba_ptr& a) @@ -599,7 +601,10 @@ namespace spot } } - return minimize_dfa(det_a, final, non_final); + auto res = minimize_dfa(det_a, final, non_final); + res->prop_deterministic(); + res->prop_inherently_weak(); + return res; } tgba_digraph_ptr @@ -618,6 +623,11 @@ namespace spot return std::const_pointer_cast(aut_f); } + // If the input automaton was already weak and deterministic, the + // output is necessary correct. + if (aut_f->is_inherently_weak() && aut_f->is_deterministic()) + return min_aut_f; + // if f is a syntactic obligation formula, the WDBA minimization // must be correct. if (f && f->is_syntactic_obligation()) diff --git a/src/tgbaalgos/neverclaim.cc b/src/tgbaalgos/neverclaim.cc index d749634dd..2345e2011 100644 --- a/src/tgbaalgos/neverclaim.cc +++ b/src/tgbaalgos/neverclaim.cc @@ -44,7 +44,7 @@ namespace spot comments_(comments), all_acc_conds_(a->all_acceptance_conditions()), sba_(std::dynamic_pointer_cast(a)) { - assert(!sba_ || sba_->get_bprop(tgba_digraph::StateBasedAcc)); + assert(!sba_ || sba_->has_state_based_acc()); } void diff --git a/src/tgbaalgos/postproc.cc b/src/tgbaalgos/postproc.cc index d5673b7c0..48da6b796 100644 --- a/src/tgbaalgos/postproc.cc +++ b/src/tgbaalgos/postproc.cc @@ -195,10 +195,11 @@ namespace spot { bool reject_bigger = (PREF_ == Small) && (level_ == Medium); dba = minimize_obligation(a, f, 0, reject_bigger); - if (dba == a || !dba) // Minimization failed. - dba = nullptr; - else + if (dba && dba->is_inherently_weak() && dba->is_deterministic()) dba_is_minimal = dba_is_wdba = true; + else + // Minimization failed. + dba = nullptr; // The WDBA is a BA, so no degeneralization is required. } diff --git a/src/tgbaalgos/sccfilter.cc b/src/tgbaalgos/sccfilter.cc index c8e4ed9c0..a7d20fc7d 100644 --- a/src/tgbaalgos/sccfilter.cc +++ b/src/tgbaalgos/sccfilter.cc @@ -498,10 +498,8 @@ namespace spot tgba_digraph_ptr scc_filter_states(const const_tgba_digraph_ptr& aut, scc_info* given_si) { - bool sb = aut->get_bprop(tgba_digraph::StateBasedAcc); auto res = scc_filter_apply>(aut, given_si); - if (sb) - res->set_bprop(tgba_digraph::StateBasedAcc); + res->prop_copy(aut, true, true, true, true); return res; } @@ -519,6 +517,11 @@ namespace spot >>>(aut, given_si); res->merge_transitions(); + res->prop_copy(aut, + false, // state-based acceptance is not preserved + true, + true, + true); return res; } @@ -545,6 +548,11 @@ namespace spot ignoredvars, early_susp); res->merge_transitions(); + res->prop_copy(aut, + false, // state-based acceptance is not preserved + true, + true, + false); // determinism may not be preserved return res; } diff --git a/src/tgbaalgos/simulation.cc b/src/tgbaalgos/simulation.cc index c00084fe2..8d1231160 100644 --- a/src/tgbaalgos/simulation.cc +++ b/src/tgbaalgos/simulation.cc @@ -526,15 +526,6 @@ namespace spot } } - bool result_is_deterministic() const - { - assert(stat.states != 0); - - return res_is_deterministic; - } - - - // Build the minimal resulting automaton. tgba_digraph_ptr build_result() { @@ -550,7 +541,7 @@ namespace spot res->copy_ap_of(a_); res->set_acceptance_conditions(all_acceptance_conditions_); if (Sba) - res->set_bprop(tgba_digraph::StateBasedAcc); + res->prop_state_based_acc(); bdd sup_all_acc = bdd_support(all_acceptance_conditions_); // Non atomic propositions variables (= acc and class) @@ -691,7 +682,15 @@ namespace spot } delete gb; - res_is_deterministic = nb_minato == nb_satoneset; + res->prop_copy(original_, + false, // state-based acc forced below + false, // single acc is set by set_acceptance_conditions + true, // weakness preserved, + false); // determinism checked and set below + if (nb_minato == nb_satoneset) + res->prop_deterministic(); + if (Sba) + res->prop_state_based_acc(); return res; } @@ -779,8 +778,6 @@ namespace spot const_tgba_ptr original_; bdd all_acceptance_conditions_; - - bool res_is_deterministic; }; // For now, we don't try to handle cosimulation. @@ -1267,7 +1264,6 @@ namespace spot { min = tmp; min_size_ = cur_size; - res_is_deterministic = dir_sim.result_is_deterministic(); } } @@ -1342,7 +1338,7 @@ namespace spot prev = next; direct_simulation simul(res ? res : t); res = simul.run(); - if (simul.result_is_deterministic()) + if (res->is_deterministic()) break; direct_simulation cosimul(res); diff --git a/src/tgbatest/degenlskip.test b/src/tgbatest/degenlskip.test index 3836d2cb5..d6b6a65db 100755 --- a/src/tgbatest/degenlskip.test +++ b/src/tgbatest/degenlskip.test @@ -57,10 +57,10 @@ digraph G { 0 -> 1 1 [label="0", peripheries=2] 1 -> 2 [label="1\n{Acc[1]}"] - 2 [label="1"] + 2 [label="2"] 2 -> 3 [label="b\n"] 2 -> 2 [label="!b\n"] - 3 [label="2"] + 3 [label="1"] 3 -> 1 [label="a\n"] 3 -> 3 [label="!a\n"] } diff --git a/src/tgbatest/ltl2tgba.cc b/src/tgbatest/ltl2tgba.cc index 0fd2214dd..7dc71c178 100644 --- a/src/tgbatest/ltl2tgba.cc +++ b/src/tgbatest/ltl2tgba.cc @@ -1072,13 +1072,11 @@ checked_main(int argc, char** argv) { if (daut->type == spot::Rabin) { - spot::tgba_digraph_ptr res; if (dra2dba) - res = spot::dstar_to_tgba(daut); + a = spot::dstar_to_tgba(daut); else - res = spot::nra_to_nba(daut); - a = res; - assert(res->get_bprop(spot::tgba_digraph::SBA)); + a = spot::nra_to_nba(daut); + assert(a->is_sba()); assume_sba = true; } else