diff --git a/src/ta/ta.cc b/src/ta/ta.cc index 8121921e7..e7b506a02 100644 --- a/src/ta/ta.cc +++ b/src/ta/ta.cc @@ -24,11 +24,6 @@ namespace spot { - spot::state* - ta::get_artificial_initial_state() const - { - return 0; - } scc_stack_ta::connected_component::connected_component(int i) { diff --git a/src/ta/ta.hh b/src/ta/ta.hh index 2561b72da..a589a13a7 100644 --- a/src/ta/ta.hh +++ b/src/ta/ta.hh @@ -31,10 +31,51 @@ namespace spot { + // Forward declarations. See below. class ta_succ_iterator; - /// ta representation of a Testing Automata + /// \defgroup ta TA (Testing Automata) + /// + /// This type and its cousins are listed \ref ta_essentials "here". + /// This is an abstract interface. Its implementations are \ref + /// ta_representation "concrete representations". The + /// algorithms that work on spot::ta are \ref tgba_algorithms + /// "listed separately". + + /// \addtogroup ta_essentials Essential TA types + /// \ingroup ta + + /// \brief A Testing Automaton. + /// \ingroup ta_essentials + /// + /// The Testing Automata (TA) were introduced by + /// Henri Hansen, Wojciech Penczek and Antti Valmari + /// in "Stuttering-insensitive automata for on-the-fly de- tection of livelock + /// properties" In Proc. of FMICSÕ02, vol. 66(2) of Electronic Notes in + /// Theoretical Computer Science.Elsevier. + /// + /// While a TGBA automaton observes the value of the atomic propositions, the + /// basic idea of TA is to detect the changes in these values; if a valuation + /// does not change between two consecutive valuations of an execution, + /// the TA stay in the same state. A TA transition \c (s,k,d) is labeled by a + /// "changeset" \c k: i.e. the set of atomic propositions that change between + /// states \c s and \c d, if the changeset is empty then the transition is + /// called stuttering transition. + /// To detect execution that ends by stuttering in the same TA state, a + /// new kind of acceptance states is introduced: "livelock-acceptance states" + /// (in addition to the standard Buchi-acceptance states). + /// + /// Browsing such automaton can be achieved using two functions: + /// \c get_initial_states_set or \c get_artificial_initial_state, and \c + /// succ_iter. The former returns the initial state(s) while the latter lists + /// the successor states of any state (filtred by transition "changeset"). + /// + /// Note that although this is a transition-based automata, + /// we never represent transitions! Transition informations are + /// obtained by querying the iterator over the successors of + /// a state. + class ta { @@ -46,52 +87,103 @@ namespace spot typedef std::set states_set_t; + /// \brief Get the initial states set of the automaton. virtual const states_set_t get_initial_states_set() const = 0; - virtual spot::state* - get_artificial_initial_state() const = 0; + /// \brief Get the artificial initial state set of the automaton. + /// Return 0 if this artificial state is not implemented + /// (in this case, use \c get_initial_states_set) + /// The aim of adding this state is to have an unique initial state. This + /// artificial initial state have one transition to each real initial state, + /// and this transition is labeled by the corresponding initial condition. + /// (For more details, see the paper cited above) + spot::state* + get_artificial_initial_state() const + { + return 0; + } + /// \brief Get an iterator over the successors of \a state. + /// + /// The iterator has been allocated with \c new. It is the + /// responsability of the caller to \c delete it when no + /// longer needed. + /// virtual ta_succ_iterator* - succ_iter(const spot::state* s) const = 0; + succ_iter(const spot::state* state) const = 0; + /// \brief Get an iterator over the successors of \a state + /// filtred by the changeset labeling the transitions + /// + /// The iterator has been allocated with \c new. It is the + /// responsability of the caller to \c delete it when no + /// longer needed. + /// virtual ta_succ_iterator* - succ_iter(const spot::state* s, bdd condition) const = 0; + succ_iter(const spot::state* state, bdd changeset) const = 0; + /// \brief Get the dictionary associated to the automaton. + /// + /// State are represented as BDDs. The dictionary allows + /// to map BDD variables back to formulae, and vice versa. + /// This is useful when dealing with several automata (which + /// may use the same BDD variable for different formula), + /// or simply when printing. virtual bdd_dict* get_dict() const = 0; + /// \brief Format the state as a string for printing. + /// + /// This formating is the responsability of the automata + /// that owns the state. virtual std::string format_state(const spot::state* s) const = 0; + /// \brief Return true if \a s is a Buchi-accepting state, otherwise false virtual bool is_accepting_state(const spot::state* s) const = 0; + /// \brief Return true if \a s is a livelock-accepting state + /// , otherwise false virtual bool is_livelock_accepting_state(const spot::state* s) const = 0; + /// \brief Return true if \a s is an initial state, otherwise false virtual bool is_initial_state(const spot::state* s) const = 0; + /// \brief Return a BDD condition that represents the valuation + /// of atomic propositions in the state \a s virtual bdd get_state_condition(const spot::state* s) const = 0; + /// \brief Release a state \a s virtual void free_state(const spot::state* s) const = 0; /// \brief Return the set of all acceptance conditions used - /// by this automaton. + /// by this automaton + /// (for Generalized form: Transition-based Generalized Testing Automata). /// /// The goal of the emptiness check is to ensure that /// a strongly connected component walks through each /// of these acceptiong conditions. I.e., the union /// of the acceptiong conditions of all transition in /// the SCC should be equal to the result of this function. - virtual bdd all_acceptance_conditions() const = 0; + virtual bdd + all_acceptance_conditions() const = 0; }; - /// Successor iterators used by spot::ta. + /// \brief Iterate over the successors of a state. + /// \ingroup ta_essentials + /// + /// This class provides the basic functionalities required to + /// iterate over the successors of a state, as well as querying + /// transition labels. Because transitions are never explicitely + /// encoded, labels (conditions and acceptance conditions) can only + /// be queried while iterating over the successors. class ta_succ_iterator : public tgba_succ_iterator { public: @@ -109,12 +201,13 @@ namespace spot virtual state* current_state() const = 0; + + /// \brief Get the changeset on the transition leading to current successor. + /// + /// This is a boolean function of atomic propositions. virtual bdd current_condition() const = 0; - virtual bool - is_stuttering_transition() const = 0; - bdd current_acceptance_conditions() const = 0; @@ -173,6 +266,29 @@ namespace spot stack_type s; }; +/// \addtogroup ta_representation TA representations +/// \ingroup ta + +/// \addtogroup ta_algorithms TA algorithms +/// \ingroup ta + +/// \addtogroup ta_io Input/Output of TA +/// \ingroup ta_algorithms + +/// \addtogroup tgba_ta Transforming TGBA into TA +/// \ingroup ta_algorithms + + +/// \addtogroup ta_generic Algorithm patterns +/// \ingroup ta_algorithms + +/// \addtogroup ta_reduction TA simplifications +/// \ingroup ta_algorithms + +/// \addtogroup ta_misc Miscellaneous algorithms on TA +/// \ingroup ta_algorithms + + } #endif // SPOT_TA_TA_HH diff --git a/src/ta/taexplicit.cc b/src/ta/taexplicit.cc index 9132c4c0a..cd1e24d1b 100644 --- a/src/ta/taexplicit.cc +++ b/src/ta/taexplicit.cc @@ -80,9 +80,13 @@ namespace spot state* ta_explicit_succ_iterator::current_state() const { - trace << "***ta_explicit_succ_iterator::current_state() if(done()) =***" << done() << std::endl; + trace + << "***ta_explicit_succ_iterator::current_state() if(done()) =***" + << done() << std::endl; assert(!done()); - trace << "***ta_explicit_succ_iterator::current_state() (*i_)->condition =***" << (*i_)->condition << std::endl; + trace + << "***ta_explicit_succ_iterator::current_state() (*i_)->condition =***" + << (*i_)->condition << std::endl; state_ta_explicit* s = (*i_)->dest; return s; } @@ -101,11 +105,6 @@ namespace spot return (*i_)->acceptance_conditions; } - bool - ta_explicit_succ_iterator::is_stuttering_transition() const - { - return source_->get_tgba_condition() == ((*i_)->dest)->get_tgba_condition(); - } //////////////////////////////////////// // state_ta_explicit @@ -414,7 +413,6 @@ namespace spot } - void ta_explicit::create_transition(state_ta_explicit* source, bdd condition, bdd acceptance_conditions, state_ta_explicit* dest, bool add_at_beginning) diff --git a/src/ta/taexplicit.hh b/src/ta/taexplicit.hh index 93c3a956d..1d81c2247 100644 --- a/src/ta/taexplicit.hh +++ b/src/ta/taexplicit.hh @@ -37,7 +37,8 @@ namespace spot class ta_explicit_succ_iterator; class ta_explicit; - /// ta_explicit explicit representa_explicittion of a Testing Automata_explicit + /// Explicit representation of a spot::ta. + /// \ingroup ta_representation class ta_explicit : public ta { public: @@ -53,7 +54,6 @@ namespace spot void add_to_initial_states_set(state* s, bdd condition = bddfalse); - void create_transition(state_ta_explicit* source, bdd condition, bdd acceptance_conditions, state_ta_explicit* dest, @@ -145,11 +145,12 @@ namespace spot }; /// states used by spot::ta_explicit. - /// \ingroup ta_ + /// \ingroup ta_representation class state_ta_explicit : public spot::state { public: + /// Explicit transitions. struct transition { bdd condition; @@ -176,11 +177,11 @@ namespace spot virtual state_ta_explicit* clone() const; - virtual void destroy() const + virtual void + destroy() const { } - virtual ~state_ta_explicit() { @@ -214,9 +215,12 @@ namespace spot void set_initial_state(bool is_initial_state); + /// \brief Return true if the state has no successors bool is_hole_state() const; + /// \brief Remove stuttering transitions + /// and transitions leading to states having no successors void delete_stuttering_and_hole_successors(); @@ -257,9 +261,6 @@ namespace spot virtual bdd current_acceptance_conditions() const; - virtual bool - is_stuttering_transition() const; - private: state_ta_explicit::transitions* transitions_; state_ta_explicit::transitions::const_iterator i_; diff --git a/src/ta/taproduct.cc b/src/ta/taproduct.cc index cdf79b56c..bca321a60 100644 --- a/src/ta/taproduct.cc +++ b/src/ta/taproduct.cc @@ -348,11 +348,6 @@ namespace spot return ta_->is_livelock_accepting_state(stp->get_ta_state()); } - spot::state* - ta_product::get_artificial_initial_state() const - { - return 0; - } bool ta_product::is_initial_state(const spot::state* s) const diff --git a/src/ta/taproduct.hh b/src/ta/taproduct.hh index 93cf9d4fc..8a41fd1b3 100644 --- a/src/ta/taproduct.hh +++ b/src/ta/taproduct.hh @@ -28,8 +28,9 @@ namespace spot { /// \brief A state for spot::ta_product. + /// \ingroup emptiness_check /// - /// This state is in fact a pair of state: the state from the ta + /// This state is in fact a pair of state: the state from the TA /// automaton and that of Kripke structure. class state_ta_product : public state { @@ -37,7 +38,6 @@ namespace spot /// \brief Constructor /// \param ta_state The state from the ta automaton. /// \param kripke_state_ The state from Kripke structure. - state_ta_product(state* ta_state, state* kripke_state) : ta_state_(ta_state), kripke_state_(kripke_state) { @@ -100,6 +100,7 @@ namespace spot bdd current_acceptance_conditions() const; + /// \brief Return true if the changeset of the current transition is empty bool is_stuttering_transition() const; @@ -111,6 +112,7 @@ namespace spot void next_non_stuttering_(); + /// \brief Move to the next successor in the kripke structure void next_kripke_dest(); @@ -131,31 +133,26 @@ namespace spot }; - /// \brief A lazy product. (States are computed on the fly.) + /// \brief A lazy product between a Testing automaton and a Kripke structure. + /// (States are computed on the fly.) + /// \ingroup emptiness_check class ta_product : public ta { public: - ta_product(const ta* testing_automata, const kripke* kripke_structure); + /// \brief Constructor. + /// \param testing_automaton The TA component in the product. + /// \param kripke_structure The Kripke component in the product. + ta_product(const ta* testing_automaton, const kripke* kripke_structure); virtual ~ta_product(); - virtual const states_set_t + virtual const std::set get_initial_states_set() const; virtual ta_succ_iterator_product* succ_iter(const spot::state* s) const; - virtual ta_succ_iterator_product* - succ_iter(const spot::state* s, bdd condition) const - { - - if (condition == bddtrue) - return succ_iter(s); - //TODO - return 0; - } - virtual bdd_dict* get_dict() const; @@ -168,12 +165,11 @@ namespace spot virtual bool is_livelock_accepting_state(const spot::state* s) const; - virtual spot::state* - get_artificial_initial_state() const; - virtual bool is_initial_state(const spot::state* s) const; + /// \brief Return true if the state \a s has no succeseurs + /// in the ta automaton (the TA component of the product automaton) virtual bool is_hole_state_in_ta_component(const spot::state* s) const; diff --git a/src/ta/tgbtaexplicit.cc b/src/ta/tgbtaexplicit.cc index 9f55c35a4..8f94317c2 100644 --- a/src/ta/tgbtaexplicit.cc +++ b/src/ta/tgbtaexplicit.cc @@ -46,8 +46,8 @@ namespace spot tgba_succ_iterator* tgbta_explicit::succ_iter(const spot::state* state, - const spot::state* global_state, - const tgba* global_automaton) const + const spot::state*, + const tgba*) const { return ta_explicit::succ_iter(state); } @@ -55,13 +55,13 @@ namespace spot bdd tgbta_explicit::compute_support_conditions(const spot::state* in) const { - return get_tgba()->support_conditions(((state_ta_explicit*) in)->get_tgba_state()); + return get_tgba()->support_conditions(((const state_ta_explicit*) in)->get_tgba_state()); } bdd tgbta_explicit::compute_support_variables(const spot::state* in) const { - return get_tgba()->support_variables(((state_ta_explicit*) in)->get_tgba_state()); + return get_tgba()->support_variables(((const state_ta_explicit*) in)->get_tgba_state()); } bdd_dict* diff --git a/src/ta/tgbtaproduct.cc b/src/ta/tgbtaproduct.cc index 1ea7c3174..878478504 100644 --- a/src/ta/tgbtaproduct.cc +++ b/src/ta/tgbtaproduct.cc @@ -58,16 +58,16 @@ namespace spot } tgba_succ_iterator* - tgbta_product::succ_iter(const state* local_state, const state* global_state, - const tgba* global_automaton) const + tgbta_product::succ_iter(const state* local_state, const state*, + const tgba*) const { const state_product* s = down_cast (local_state); assert(s); fixed_size_pool* p = const_cast (&pool_); - return new tgbta_succ_iterator_product(s, (kripke*) left_, - (tgbta *) right_, p); + return new tgbta_succ_iterator_product(s, (const kripke*) left_, + (const tgbta *) right_, p); } //////////////////////////////////////////////////////////// diff --git a/src/taalgos/emptinessta.cc b/src/taalgos/emptinessta.cc index df3665264..14869af89 100644 --- a/src/taalgos/emptinessta.cc +++ b/src/taalgos/emptinessta.cc @@ -43,11 +43,11 @@ namespace spot ta_check::~ta_check() { - } bool - ta_check::check(bool disable_second_pass) + ta_check::check(bool disable_second_pass, + disable_heuristic_for_livelock_detection) { // We use five main data in this algorithm: @@ -67,14 +67,12 @@ namespace spot int num = 1; // * todo: the depth-first search stack. This holds pairs of the - // form (STATE, ITERATOR) where ITERATOR is a ta_succ_iterator + // form (STATE, ITERATOR) where ITERATOR is a ta_succ_iterator_product // over the successors of STATE. In our use, ITERATOR should // always be freed when TODO is popped, but STATE should not because // it is also used as a key in H. std::stack todo; - - Sgi::hash_map colour; @@ -88,7 +86,8 @@ namespace spot bool livelock_acceptance_states_not_found = true; - bool activate_heuristic = (is_full_2_pass_ == disable_second_pass); + bool activate_heuristic = !disable_heuristic_for_livelock_detection + && (is_full_2_pass_ == disable_second_pass); // Setup depth-first search from initial states. const ta* ta_ = a_->get_ta(); @@ -117,7 +116,7 @@ namespace spot scc.push(num); arc.push(bddfalse); - ta_succ_iterator* iter = a_->succ_iter(init); + ta_succ_iterator_product* iter = a_->succ_iter(init); iter->first(); todo.push(pair_state_iter(init, iter)); @@ -133,7 +132,7 @@ namespace spot state* curr = todo.top().first; // We are looking at the next successor in SUCC. - ta_succ_iterator* succ = todo.top().second; + ta_succ_iterator_product* succ = todo.top().second; // If there is no more successor, backtrack. if (succ->done()) @@ -147,7 +146,8 @@ namespace spot trace << "PASS 1 : backtrack" << std::endl; - if (a_->is_livelock_accepting_state(curr)) + if (a_->is_livelock_accepting_state(curr) + && !a_->is_accepting_state(curr)) { livelock_acceptance_states_not_found = false; trace @@ -241,7 +241,7 @@ namespace spot scc.push(num); arc.push(acc_cond); - ta_succ_iterator* iter = a_->succ_iter(dest); + ta_succ_iterator_product* iter = a_->succ_iter(dest); iter->first(); todo.push(pair_state_iter(dest, iter)); //colour[dest] = GREY; @@ -315,10 +315,11 @@ namespace spot a_->get_dict(), scc.top().condition) << std::endl; trace << "PASS 1: a_->all_acceptance_conditions() : " - << ( a_->all_acceptance_conditions()) << std::endl; + << (a_->all_acceptance_conditions()) << std::endl; trace - << "PASS 1 CYCLE and (scc.top().condition == a_->all_acceptance_conditions()) : " - << (scc.top().condition == a_->all_acceptance_conditions()) << std::endl; + << "PASS 1 CYCLE and (scc.top().condition == a_->all_acceptance_conditions()) : " + << (scc.top().condition + == a_->all_acceptance_conditions()) << std::endl; trace << "PASS 1: bddtrue : " << (a_->all_acceptance_conditions() @@ -407,7 +408,7 @@ namespace spot } bool - ta_check::livelock_detection(const ta* t) + ta_check::livelock_detection(const ta_product* t) { // We use five main data in this algorithm: @@ -460,7 +461,7 @@ namespace spot h->insert(init, ++num); sscc.push(num); sscc.top().is_accepting = t->is_livelock_accepting_state(init); - ta_succ_iterator* iter = t->succ_iter(init); + ta_succ_iterator_product* iter = t->succ_iter(init); iter->first(); todo.push(pair_state_iter(init, iter)); inc_depth(); @@ -473,7 +474,7 @@ namespace spot state* curr = todo.top().first; // We are looking at the next successor in SUCC. - ta_succ_iterator* succ = todo.top().second; + ta_succ_iterator_product* succ = todo.top().second; // If there is no more successor, backtrack. if (succ->done()) @@ -554,7 +555,7 @@ namespace spot sscc.push(num); sscc.top().is_accepting = t->is_livelock_accepting_state(dest); - ta_succ_iterator* iter = t->succ_iter(dest); + ta_succ_iterator_product* iter = t->succ_iter(dest); iter->first(); todo.push(pair_state_iter(dest, iter)); inc_depth(); @@ -651,26 +652,24 @@ namespace spot delete h; } - void - ta_check::clear(numbered_state_heap* h, std::stack todo, - spot::ta_succ_iterator* init_states_it) - { + ta_check::clear(numbered_state_heap* h, std::stack todo, + spot::ta_succ_iterator* init_states_it) + { - set_states(states() + h->size()); + set_states(states() + h->size()); delete init_states_it; - // Release all iterators in TODO. - while (!todo.empty()) - { - delete todo.top().second; - todo.pop(); - dec_depth(); - } - delete h; - } - + // Release all iterators in TODO. + while (!todo.empty()) + { + delete todo.top().second; + todo.pop(); + dec_depth(); + } + delete h; + } std::ostream& ta_check::print_stats(std::ostream& os) const diff --git a/src/taalgos/emptinessta.hh b/src/taalgos/emptinessta.hh index 60a8d4421..8055ce735 100644 --- a/src/taalgos/emptinessta.hh +++ b/src/taalgos/emptinessta.hh @@ -36,11 +36,54 @@ namespace spot namespace { - typedef std::pair pair_state_iter; + typedef std::pair pair_state_iter; } - /// \brief An implementation of the ta emptiness-check algorithm. + + /// \addtogroup emptiness_check Emptiness-checks + /// \ingroup ta_algorithms /// - /// See the documentation for spot::ta. + /// \brief Check whether the language of a product between a Kripke structure + /// and a TA is empty. It works for both standard and generalized form of TA. + /// + /// you should call \c check to check the product automaton. + /// If \c check() returns false, then the product automaton + /// was found empty. Otherwise the automaton accepts some run. + /// + /// This is based on the following paper. + /// \verbatim + /// @InProceedings{ geldenhuys.06.spin, + /// author = {Jaco Geldenhuys and Henri Hansen}, + /// title = {Larger Automata and Less Work for {LTL} Model Checking}, + /// booktitle = {Proceedings of the 13th International SPIN Workshop + /// (SPIN'06)}, + /// year = {2006}, + /// pages = {53--70}, + /// series = {Lecture Notes in Computer Science}, + /// volume = {3925}, + /// publisher = {Springer} + /// } + /// \endverbatim + /// + /// the implementation of \c check is inspired from the two-pass algorithm + /// of the paper above: + /// - the fist-pass detect all Buchi-accepting cycles and includes + // the heuristic proposed in the paper to detect some + /// livelock-accepting cycles. + /// - the second-pass detect all livelock-accepting cycles. + /// In addition, we add some optimizations to the fist pass: + /// 1- Detection of all (livelock-accepting) cycles containing a least + /// one state that is both livelock and accepting states + /// 2- Detection of all livelock-accepting cycles containing a least + /// one state (k,t) such as its "TA component" t is a livelock-accepting + /// state that has no successors in the TA automaton. + /// + /// The implementation of each pass is a SCC-based algorithm inspired + /// from spot::gtec.hh. + + /// \brief An implementation of the emptiness-check algorithm for a product + /// between a TA and a Kripke structure + /// + /// See the paper cited above. class ta_check : public ec_statistics { public: @@ -48,26 +91,38 @@ namespace spot virtual ~ta_check(); - /// Check whether the automaton's language is empty. + /// \brief Check whether the TA product automaton contains an accepting run: + /// it detects the two kinds of accepting runs: Buchi-accepting runs + /// and livelock-accepting runs. This emptiness check algorithm can also + /// check a product using the generalized form of TA. + /// + /// Return false if the product automaton accepts no run, otherwise true + /// + /// \param disable_second_pass: is used to disable the second pass when + /// when it is not necessary, for example when all the livelock-accepting + /// states of the TA automaton have no successors, we call this kind of + /// TA as STA (Single-pass Testing Automata) + /// (see spot::tgba2ta::add_artificial_livelock_accepting_state() for an + /// automatic transformation of any TA automaton into STA automaton + /// + /// \param disable_heuristic_for_livelock_detection: disable the heuristic + /// used in the first pass to detect livelock-accepting runs, + /// this heuristic is described in the paper cited above virtual bool - check(bool disable_second_pass = false); + check(bool disable_second_pass = false, + bool disable_heuristic_for_livelock_detection = false); + /// \brief Check whether the product automaton contains + /// a livelock-accepting run + /// Return false if the product automaton accepts no livelock-accepting run, + /// otherwise true virtual bool - livelock_detection(const ta* t); + livelock_detection(const ta_product* t); + /// Print statistics, if any. virtual std::ostream& print_stats(std::ostream& os) const; - /// \brief Return the status of the emptiness-check. - /// - /// When check() succeed, the status should be passed along - /// to spot::counter_example. - /// - /// This status should not be deleted, it is a pointer - /// to a member of this class that will be deleted when - /// the ta object is deleted. - // const tgba_check_status* result() const; - protected: void clear(numbered_state_heap* h, std::stack todo, std::queue< @@ -77,19 +132,23 @@ namespace spot clear(numbered_state_heap* h, std::stack todo, spot::ta_succ_iterator* init_states_it); + /// the heuristic for livelock-accepting runs detection, it's described + /// in the paper cited above bool heuristic_livelock_detection(const state * stuttering_succ, numbered_state_heap* h, int h_livelock_root, std::set liveset_curr); + const ta_product* a_; ///< The automaton. option_map o_; ///< The options + // Force the second pass bool is_full_2_pass_; - // * scc: a stack of strongly connected components (SCC) + // scc: a stack of strongly connected components (SCC) scc_stack_ta scc; - // * sscc: a stack of strongly stuttering-connected components (SSCC) + // sscc: a stack of strongly stuttering-connected components (SSCC) scc_stack_ta sscc; }; diff --git a/src/taalgos/tgba2ta.cc b/src/taalgos/tgba2ta.cc index ec1c0beb0..30782d923 100644 --- a/src/taalgos/tgba2ta.cc +++ b/src/taalgos/tgba2ta.cc @@ -48,7 +48,6 @@ namespace spot ta_explicit* build_ta(ta_explicit* ta, bdd atomic_propositions_set_, - bool artificial_initial_state_mode, bool artificial_livelock_accepting_state_mode, bool degeneralized) { @@ -70,7 +69,8 @@ namespace spot { init_state = new state_ta_explicit(tgba_init_state->clone(), satone_tgba_condition, true, - ((tgba_sba_proxy*) tgba_)->state_is_accepting(tgba_init_state)); + ((const tgba_sba_proxy*) tgba_)->state_is_accepting( + tgba_init_state)); } else { @@ -117,10 +117,13 @@ namespace spot if (degeneralized) { - new_dest = new state_ta_explicit(tgba_state->clone(), - dest_condition, false, - ((tgba_sba_proxy*) tgba_)->state_is_accepting( - tgba_state)); + new_dest + = new state_ta_explicit( + tgba_state->clone(), + dest_condition, + false, + ((const tgba_sba_proxy*) tgba_)->state_is_accepting( + tgba_state)); } else @@ -133,7 +136,7 @@ namespace spot if (dest != new_dest) { - // the state dest already exists in the testing automata + // the state dest already exists in the testing automata new_dest->get_tgba_state()->destroy(); delete new_dest; } @@ -158,22 +161,21 @@ namespace spot state_ta_explicit* artificial_livelock_accepting_state = 0; - trace << "*** build_ta: artificial_livelock_accepting_state_mode = ***" - << artificial_livelock_accepting_state_mode << std::endl; + trace + << "*** build_ta: artificial_livelock_accepting_state_mode = ***" + << artificial_livelock_accepting_state_mode << std::endl; if (artificial_livelock_accepting_state_mode) { - artificial_livelock_accepting_state = - new state_ta_explicit(ta->get_tgba()->get_init_state(), bddtrue, - false, false, true, 0); - trace << "*** build_ta: artificial_livelock_accepting_state = ***" - << artificial_livelock_accepting_state << std::endl; + artificial_livelock_accepting_state = new state_ta_explicit( + ta->get_tgba()->get_init_state(), bddtrue, false, false, true, 0); + trace + << "*** build_ta: artificial_livelock_accepting_state = ***" + << artificial_livelock_accepting_state << std::endl; } - - compute_livelock_acceptance_states(ta, artificial_livelock_accepting_state); return ta; @@ -203,7 +205,7 @@ namespace spot tgba_init_state->destroy(); // build ta automata: - build_ta(ta, atomic_propositions_set_, artificial_initial_state_mode, + build_ta(ta, atomic_propositions_set_, artificial_livelock_accepting_state_mode, degeneralized); return ta; } @@ -221,9 +223,11 @@ namespace spot == artificial_livelock_accepting_state); trace - << "*** add_artificial_livelock_accepting_state: assert(artificial_livelock_accepting_state_added == artificial_livelock_accepting_state) = ***" - << (artificial_livelock_accepting_state_added - == artificial_livelock_accepting_state) << std::endl; + << "*** add_artificial_livelock_accepting_state: " + << "assert(artificial_livelock_accepting_state_added == " + << "artificial_livelock_accepting_state) = ***" + << (artificial_livelock_accepting_state_added + == artificial_livelock_accepting_state) << std::endl; ta::states_set_t states_set = testing_automata->get_states_set(); ta::states_set_t::iterator it; @@ -250,7 +254,7 @@ namespace spot (dest)->get_transitions(); bool dest_trans_empty = dest_trans == 0 || dest_trans->empty(); - //TODO TA++ + //TA++ if (dest->is_livelock_accepting_state() && (!dest->is_accepting_state() || dest_trans_empty)) { @@ -260,7 +264,6 @@ namespace spot } //remove hole successors states - if (dest_trans_empty) { source->get_transitions((*it_trans)->condition)->remove( @@ -325,7 +328,8 @@ namespace spot // * h: a hash of all visited nodes, with their order, // (it is called "Hash" in Couvreur's paper) numbered_state_heap* h = - numbered_state_heap_hash_map_factory::instance()->build(); ///< Heap of visited states. + numbered_state_heap_hash_map_factory::instance()->build(); + ///< Heap of visited states. // * num: the number of visited nodes. Used to set the order of each // visited node, @@ -424,14 +428,17 @@ namespace spot assert(*spi.second != -1); *spi.second = -1; if (is_livelock_accepting_sscc) - {//if it is an accepting sscc - //add the state to G (=the livelock-accepting states set) + {//if it is an accepting sscc add the state to + //G (=the livelock-accepting states set) state_ta_explicit * livelock_accepting_state = down_cast (*i); - livelock_accepting_state->set_livelock_accepting_state( + livelock_accepting_state->set_livelock_accepting_state( true); + + //case STA (Single-pass Testing Automata) or case + //STGTA (Single-pass Transition-based Generalised Testing Automata) if (artificial_livelock_accepting_state != 0) livelock_accepting_state->set_accepting_state( true); @@ -498,8 +505,8 @@ namespace spot if (*spi.second == -1) continue; - trace << "***compute_livelock_acceptance_states: CYCLE***" - << std::endl; + trace + << "***compute_livelock_acceptance_states: CYCLE***" << std::endl; if (!curr->compare(dest)) { @@ -512,13 +519,14 @@ namespace spot == testing_automata->all_acceptance_conditions())) { self_loop_state->set_livelock_accepting_state(true); - if (artificial_livelock_accepting_state != 0) self_loop_state->set_accepting_state(true); + if (artificial_livelock_accepting_state != 0) + self_loop_state->set_accepting_state(true); } trace - << "***compute_livelock_acceptance_states: CYCLE: self_loop_state***" - << std::endl; + << "***compute_livelock_acceptance_states: CYCLE: self_loop_state***" + << std::endl; } @@ -566,16 +574,16 @@ namespace spot delete h; trace - << "*** compute_livelock_acceptance_states: PRE call add_artificial_livelock_accepting_state() method ... (artificial_livelock_accepting_state != 0) :***" - << (artificial_livelock_accepting_state != 0) << std::endl; + << "*** compute_livelock_acceptance_states: PRE call add_artificial_livelock_accepting_state() method ... (artificial_livelock_accepting_state != 0) :***" + << (artificial_livelock_accepting_state != 0) << std::endl; if (artificial_livelock_accepting_state != 0) add_artificial_livelock_accepting_state(testing_automata, artificial_livelock_accepting_state); trace - << "*** compute_livelock_acceptance_states: POST call add_artificial_livelock_accepting_state() method ***" - << std::endl; + << "*** compute_livelock_acceptance_states: POST call add_artificial_livelock_accepting_state() method ***" + << std::endl; } tgbta_explicit* @@ -591,9 +599,10 @@ namespace spot tgba_->all_acceptance_conditions(), ta_init_state); // build ta automata: - build_ta(tgbta, atomic_propositions_set_, true, true, false); + build_ta(tgbta, atomic_propositions_set_, true, false); - trace << "***tgba_to_tgbta: POST build_ta***" << std::endl; + trace + << "***tgba_to_tgbta: POST build_ta***" << std::endl; // adapt a ta automata to build tgbta automata : ta::states_set_t states_set = tgbta->get_states_set(); @@ -622,13 +631,13 @@ namespace spot if (trans_empty || state->is_accepting_state()) { trace - << "***tgba_to_tgbta: PRE if (state->is_livelock_accepting_state()) ... create_transition ***" - << std::endl; + << "***tgba_to_tgbta: PRE if (state->is_livelock_accepting_state()) ... create_transition ***" + << std::endl; tgbta->create_transition(state, bdd_stutering_transition, tgbta->all_acceptance_conditions(), state); trace - << "***tgba_to_tgbta: POST if (state->is_livelock_accepting_state()) ... create_transition ***" - << std::endl; + << "***tgba_to_tgbta: POST if (state->is_livelock_accepting_state()) ... create_transition ***" + << std::endl; } @@ -640,7 +649,8 @@ namespace spot state->set_livelock_accepting_state(false); state->set_accepting_state(false); - trace << "***tgba_to_tgbta: POST create_transition ***" << std::endl; + trace + << "***tgba_to_tgbta: POST create_transition ***" << std::endl; } diff --git a/src/taalgos/tgba2ta.hh b/src/taalgos/tgba2ta.hh index a035fde66..c0cf96ebf 100644 --- a/src/taalgos/tgba2ta.hh +++ b/src/taalgos/tgba2ta.hh @@ -34,22 +34,73 @@ namespace spot { + /// \brief Build a spot::tgba_explicit* from an LTL formula. + /// \ingroup tgba_ta + /// + /// This is based on the following paper. + /// \verbatim + /// @InProceedings{ geldenhuys.06.spin, + /// author = {Jaco Geldenhuys and Henri Hansen}, + /// title = {Larger Automata and Less Work for {LTL} Model Checking}, + /// booktitle = {Proceedings of the 13th International SPIN Workshop + /// (SPIN'06)}, + /// year = {2006}, + /// pages = {53--70}, + /// series = {Lecture Notes in Computer Science}, + /// volume = {3925}, + /// publisher = {Springer} + /// } + /// \endverbatim + /// + /// \param tgba_to_convert The TGBA automaton to convert into a TA automaton + /// + /// \param atomic_propositions_set The set of atomic propositions used in the + /// input TGBA \a tgba_to_convert + /// + /// \param artificial_initial_state_mode When set, the algorithm will build + /// a TA automaton with an unique initial state. This + /// artificial initial state have one transition to each real initial state, + /// and this transition is labeled by the corresponding initial condition. + /// (see spot::ta::get_artificial_initial_state()) + /// + /// \param STA_mode When set, the returned TA + /// automaton is a STA (Single-pass Testing Automata): a STA automaton is a TA + /// where: for every livelock-accepting state s, if s is not also a + /// Buchi-accepting state, then s has no successors. A STA product requires + /// only one-pass emptiness check algorithm (see spot::ta_check::check) + /// + /// \param degeneralized When false, the returned automaton is a generalized + /// form of TA, called TGTA (Transition-based Generalized Testing Automaton). + /// Like TGBA, TGTA use Generalized Büchi acceptance + /// conditions intead of Büchi-accepting states: there are several acceptance + /// sets (of transitions), and a path is accepted if it traverses + /// at least one transition of each set infinitely often or if it contains a + /// livelock-accepting cycle. + /// + /// \return A spot::ta_explicit that recognizes the same language as the + /// TGBA \a tgba_to_convert. ta_explicit* tgba_to_ta(const tgba* tgba_to_convert, bdd atomic_propositions_set, - bool artificial_initial_state_mode = true, - bool artificial_livelock_accepting_state_mode = false, + bool artificial_initial_state_mode = true, bool STA_mode = false, bool degeneralized = true); - //artificial_livelock_accepting_state is used in the case of TA+ automata - void - compute_livelock_acceptance_states(ta_explicit* testing_automata, state_ta_explicit* artificial_livelock_accepting_state = 0); + stgta_explicit* + tgba_to_stgta(const tgba* tgba_to_convert, bdd atomic_propositions_set); - void - add_artificial_livelock_accepting_state(ta_explicit* testing_automata, - state_ta_explicit* artificial_livelock_accepting_state); - tgbta_explicit* - tgba_to_tgbta(const tgba* tgba_to_convert, bdd atomic_propositions_set); + //artificial_livelock_accepting_state is used in the case of + //STA (Single-pass Testing Automata) or in the case + //STGTA (Single-pass Transition-based Generalised Testing Automata) + void + compute_livelock_acceptance_states(ta_explicit* testing_automata, + state_ta_explicit* artificial_livelock_accepting_state = 0); + + //artificial_livelock_accepting_state is added to transform TA into + //STA (Single-pass Testing Automata) or to transform TGTA into + //STGTA (Single-pass Transition-based Generalised Testing Automata) + void + add_artificial_livelock_accepting_state(ta_explicit* testing_automata, + state_ta_explicit* artificial_livelock_accepting_state); } diff --git a/src/tgbatest/ltl2ta.test b/src/tgbatest/ltl2ta.test index f4182146f..26a604ecc 100755 --- a/src/tgbatest/ltl2ta.test +++ b/src/tgbatest/ltl2ta.test @@ -62,7 +62,7 @@ done for opt in -TM; do - ../ltl2tgba -ks $opt -in 'a U (b U c)' > stdout + ../ltl2tgba -ks $opt -in -DS 'a U (b U c)' > stdout grep 'transitions: 69$' stdout grep 'states: 10$' stdout done @@ -70,7 +70,7 @@ done for opt in -TM; do - ../ltl2tgba -ks $opt '!(Ga U b)' > stdout + ../ltl2tgba -ks $opt -DS '!(Ga U b)' > stdout grep 'transitions: 15$' stdout grep 'states: 5$' stdout done @@ -78,7 +78,7 @@ done # Make sure 'Ga U b' has 6 states and 12 transitions, # before and after degeneralization. for opt in -TM; do - ../ltl2tgba -ks $opt 'Ga U b' > stdout + ../ltl2tgba -ks $opt -DS 'Ga U b' > stdout grep 'transitions: 13$' stdout grep 'states: 6$' stdout done @@ -88,13 +88,13 @@ done # has 21 states and 96 transitions, before minimization. f='(G (p -> F q)) && ((X (p) U q) || ! X (p U (p && q)))' -../ltl2tgba -ks -TA "$f" > stdout +../ltl2tgba -ks -TA -DS "$f" > stdout grep 'transitions: 96$' stdout grep 'states: 21$' stdout # Note: after minimization with -TM. # has 20 states and 89 transitions, after minimization. -../ltl2tgba -ks -TM "$f" > stdout +../ltl2tgba -ks -TM -DS "$f" > stdout grep 'transitions: 89$' stdout grep 'states: 20$' stdout @@ -102,7 +102,7 @@ grep 'states: 20$' stdout # Make sure 'GFa & GFb & GFc & GFd & GFe & GFf' # has 448 states and 28224 transitions. f='GFa & GFb & GFc & GFd & GFe & GFg' -../ltl2tgba -ks -TA -x "$f" > stdout +../ltl2tgba -ks -TA -DS -x "$f" > stdout grep 'transitions: 28351$' stdout grep 'states: 449$' stdout @@ -111,71 +111,71 @@ grep 'states: 449$' stdout # has 290 states and 18527 transitions with artificial livelock state. f='GFa & GFb & GFc & GFd & GFe & GFg' -../ltl2tgba -ks -TM -x -lv "$f" > stdout -grep 'transitions: 18527$' stdout +../ltl2tgba -ks -TM -x -lv -DS "$f" > stdout +grep 'transitions: 18496$' stdout grep 'states: 290$' stdout #tests with artificial livelock state: -run 0 ../ltl2tgba -ks -TA -lv "Gq|Gr|(G(q|FGp)&G(r|FG!p))" >stdout -grep 'transitions: 920$' stdout +run 0 ../ltl2tgba -ks -TA -lv -DS "Gq|Gr|(G(q|FGp)&G(r|FG!p))" >stdout +grep 'transitions: 882$' stdout grep 'states: 78$' stdout -run 0 ../ltl2tgba -TM -ks -lv "Gq|Gr|(G(q|FGp)&G(r|FG!p))" >stdout -grep 'transitions: 458$' stdout +run 0 ../ltl2tgba -TM -ks -lv -DS "Gq|Gr|(G(q|FGp)&G(r|FG!p))" >stdout +grep 'transitions: 440$' stdout grep 'states: 28$' stdout -run 0 ../ltl2tgba -TM -ks -in -R3f -x "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +run 0 ../ltl2tgba -TM -ks -in -R3f -x -DS "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout grep 'transitions: 521$' stdout grep 'states: 43$' stdout -run 0 ../ltl2tgba -TM -ks -lv -R3f -x "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 645$' stdout +run 0 ../ltl2tgba -TM -ks -lv -R3f -x -DS "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +grep 'transitions: 636$' stdout grep 'states: 45$' stdout -run 0 ../ltl2tgba -TM -ks "G(F(GWaitLeft7 U Idle4) U (WaitLeft2 M IsEating2))" >stdout +run 0 ../ltl2tgba -TM -ks -DS "G(F(GWaitLeft7 U Idle4) U (WaitLeft2 M IsEating2))" >stdout grep 'transitions: 2779$' stdout grep 'states: 127$' stdout -run 0 ../ltl2tgba -TM -ks -lv "G(F(GWaitLeft7 U Idle4) U (WaitLeft2 M IsEating2))" >stdout -grep 'transitions: 3105$' stdout +run 0 ../ltl2tgba -TM -ks -lv -DS "G(F(GWaitLeft7 U Idle4) U (WaitLeft2 M IsEating2))" >stdout +grep 'transitions: 2831$' stdout grep 'states: 128$' stdout run 0 ../ltl2tgba -TM -ks "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 536$' stdout -grep 'states: 37$' stdout +grep 'transitions: 498$' stdout +grep 'states: 34$' stdout run 0 ../ltl2tgba -TM -ks -lv -in "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 612$' stdout -grep 'states: 37$' stdout +grep 'transitions: 566$' stdout +grep 'states: 35$' stdout -run 0 ../ltl2tgba -TM -ks -in -R3 -x "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +run 0 ../ltl2tgba -TM -ks -in -R3 -x -DS "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout grep 'transitions: 464$' stdout grep 'states: 36$' stdout -run 0 ../ltl2tgba -TM -ks -lv -R3 -x "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 575$' stdout +run 0 ../ltl2tgba -TM -ks -lv -R3 -x -DS "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +grep 'transitions: 565$' stdout grep 'states: 38$' stdout -run 0 ../ltl2tgba -TA -ks -lv "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 876$' stdout +run 0 ../ltl2tgba -TA -ks -lv -DS "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +grep 'transitions: 831$' stdout grep 'states: 56$' stdout -run 0 ../ltl2tgba -TM -ks -lv "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout -grep 'transitions: 631$' stdout -grep 'states: 38$' stdout +run 0 ../ltl2tgba -TM -ks -lv "FG((WaitRight4 M (HasRight1 W GWaitLeft0)) M HasLeft4)" >stdout +grep 'transitions: 585$' stdout +grep 'states: 36$' stdout -echo '.................. OK' +echo '.................. TESTs: OK' diff --git a/src/tgbatest/ltl2tgba.cc b/src/tgbatest/ltl2tgba.cc index c6543d344..061c530a4 100644 --- a/src/tgbatest/ltl2tgba.cc +++ b/src/tgbatest/ltl2tgba.cc @@ -288,13 +288,13 @@ syntax(char* prog) << " -TM Translate an LTL formula into a minimal Testing automata" << std::endl << std::endl - << " -lv Translate an LTL formula into a Testing automata with an artificial livelock accepting state" + << " -lv Translate an LTL formula into a Testing automata with an artificial livelock accepting state (Single-pass Testing Automata)" << std::endl << std::endl << " -in Translate an LTL formula into a Testing automata without artificial initial state" << std::endl << std::endl - << " -TGBTA Translate an LTL formula into a TGBTA" + << " -STGTA Translate an LTL formula into a STGTA (Single-pass Transition-based Generalised Testing Automata)" << std::endl; @@ -697,7 +697,7 @@ main(int argc, char** argv) ta_opt = true; opt_minimize = true; } - else if (!strcmp(argv[formula_index], "-TGBTA")) + else if (!strcmp(argv[formula_index], "-STGTA")) { tgbta_opt = true; } @@ -1154,12 +1154,15 @@ main(int argc, char** argv) tm.stop("producing output"); } - delete testing_automata_nm; - //delete testing_automata; + + delete testing_automata_nm; + delete testing_automata; a = 0; degeneralized = 0; - output = -1; + if (degeneralize_opt != DegenSBA) to_free = 0; + aut_red = 0; + output = -1; } else if (tgbta_opt) { a = tgba_to_tgbta(a, atomic_props_set_bdd);