# -*- coding: utf-8 -*- #+TITLE: Working with LTL formulas with finite semantics #+DESCRIPTION: Code example for using Spot to translate LTLf formulas #+INCLUDE: setup.org #+HTML_LINK_UP: tut.html #+PROPERTY: header-args:sh :results verbatim :exports both #+PROPERTY: header-args:python :results output :exports both #+PROPERTY: header-args:C+++ :results verbatim :exports both The LTL operators used by Spot are defined over infinite words, and the various type of automata supported are all \omega-automata, i.e., automata over infinite words. #+name: from_ltlf #+begin_src sh :exports none :var f="bug" ltlfilt --from-ltlf -f "$f" #+end_src However, there is a trick we can use in case we want to use Spot to build a finite automaton that recognize some LTLf (i.e. LTL with finite semantics) property. The plan is as follows: 1. Have Spot read the input LTLf formula as if it was LTL. 2. Rewrite this formula in a way that embeds the semantics of LTLf in LTL. First, introduce a new atomic proposition =alive= that will be true initially, but that will eventually become false forever. Then adjust all original LTL operators so that they have to be satisfied during the =alive= part of the word. For instance the formula =(a U b) & Fc= would be transformed into call_from_ltlf(f="(a U b) & Fc"). 3. Convert the resulting formula into a Büchi automaton: #+name: tut12a #+begin_src sh :exports none ltlfilt --from-ltlf -f "(a U b) & Fc" | ltl2tgba -B -d #+end_src #+BEGIN_SRC dot :file tut12a.svg :var txt=tut12a :exports results $txt #+END_SRC #+RESULTS: [[file:tut12a.svg]] 4. Remove the =alive= property, after marking as accepting all states with an outgoing edge labeled by =!alive=. (Note that since Spot does not actually support state-based acceptance, it needs to keep a false self-loop on any accepting state without a successor in order to mark it as accepting.) #+name: tut12b #+begin_src sh :exports none ltlfilt --from-ltlf -f "(a U b) & Fc" | ltl2tgba -B | autfilt --to-finite -d #+end_src #+BEGIN_SRC dot :file tut12b.svg :var txt=tut12b :exports results $txt #+END_SRC #+RESULTS: [[file:tut12b.svg]] 5. Interpret the above automaton as finite automaton. (This part is out of scope for Spot.) The above sequence of operations was described by De Giacomo & Vardi in their [[https://www.cs.rice.edu/~vardi/papers/ijcai13.pdf][IJCAI'13 paper]] and by Dutta & Vardi in their [[https://www.cs.rice.edu/~vardi/papers/memocode14a.pdf][Memocode'14 paper]]. However, beware that the LTLf to LTL rewriting suggested in theorem 1 of the [[https://www.cs.rice.edu/~vardi/papers/ijcai13.pdf][IJCAI'13 paper]] has a typo (=t(φ₁ U φ₂)= should be equal to =t(φ₁) U t(φ₂ & alive)=) that is fixed in the [[https://www.cs.rice.edu/~vardi/papers/memocode14a.pdf][Memocode'14 paper]], but that second paper forgets to ensure that =alive= holds initially, as required in the first paper... * Shell version The first four steps of the above sequence of operations can be executed as follows. Transforming LTLf to LTL can be done using [[file:ltlfilt.org][=ltlfilt=]]'s =--from-ltlf= option, translating the resulting formula into a Büchi automaton is obviously done with [[file:ltl2tgba.org][=ltl2tgba=]], and removing an atomic proposition while adapting the accepting states can be done with [[file:autfilt.org][=autfilt=]]'s =--to-finite= option. Interpreting the resulting Büchi automaton as a finite automaton is out of scope for Spot. #+begin_src sh ltlfilt --from-ltlf -f "(a U b) & Fc" | ltl2tgba -B | autfilt --to-finite #+end_src #+RESULTS: #+begin_example HOA: v1 States: 4 Start: 2 AP: 3 "b" "a" "c" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels explicit-labels state-acc deterministic properties: very-weak --BODY-- State: 0 [!2] 0 [2] 1 State: 1 {0} [t] 1 State: 2 [0&!2] 0 [0&2] 1 [!0&1&!2] 2 [!0&1&2] 3 State: 3 [0] 1 [!0&1] 3 --END-- #+end_example Use =-B -D= instead of =-B= if you want to ensure that a deterministic automaton is output. * Python version In Python, we use the =from_ltlf()= function to convert from LTLf to LTL and translate the result into a Büchi automaton using =translate()= [[file:tut10.org][as usual]]. Then we need to call the =to_finite()= function. #+begin_src python import spot # Translate LTLf to Büchi. f = spot.from_ltlf('(a U b) & Fc') aut = f.translate('small', 'buchi', 'sbacc') # Remove "alive" atomic propositions and print result. print(spot.to_finite(aut).to_str('hoa')) #+end_src #+RESULTS: #+begin_example HOA: v1 States: 4 Start: 2 AP: 3 "b" "a" "c" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels explicit-labels state-acc deterministic properties: very-weak --BODY-- State: 0 [!2] 0 [2] 1 State: 1 {0} [t] 1 State: 2 [0&!2] 0 [0&2] 1 [!0&1&!2] 2 [!0&1&2] 3 State: 3 [0] 1 [!0&1] 3 --END-- #+end_example If you need to print the automaton in a custom format (some finite automaton format probably), you should check our example of [[file:tut21.org][custom print of an automaton]]. * C++ version The C++ version is straightforward adaptation of the Python version. (The Python function =translate()= is a convenient Python-only wrappers around the =spot::translator= object that we need to use here.) #+begin_src C++ #include #include #include #include #include #include int main() { spot::parsed_formula pf = spot::parse_infix_psl("(a U b) & Fc"); if (pf.format_errors(std::cerr)) return 1; spot::translator trans; trans.set_type(spot::postprocessor::Buchi); trans.set_pref(spot::postprocessor::SBAcc | spot::postprocessor::Small); spot::twa_graph_ptr aut = trans.run(spot::from_ltlf(pf.f)); aut = spot::to_finite(aut); print_hoa(std::cout, aut) << '\n'; return 0; } #+end_src #+RESULTS: #+begin_example HOA: v1 States: 4 Start: 2 AP: 3 "b" "a" "c" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels explicit-labels state-acc deterministic properties: very-weak --BODY-- State: 0 [!2] 0 [2] 1 State: 1 {0} [t] 1 State: 2 [0&!2] 0 [0&2] 1 [!0&1&!2] 2 [!0&1&2] 3 State: 3 [0] 1 [!0&1] 3 --END-- #+end_example Again, please check our example of [[file:tut21.org][custom print of an automaton]] if you need to print in some format for NFA/DFA. * Final notes Spot only deals with infinite behaviors, so if you plan to use Spot to perform some LTLf model checking, you should stop at step 3. Keep the =alive= proposition in your property automaton, and also add it to the Kripke structure representing your system. Alternatively, if your Kripke structure is already equipped with some =dead= property (as introduced by default in our [[https://spot.lrde.epita.fr/ipynb/ltsmin-dve.html][=ltsmin= interface]]), you could replace =alive= by =!dead= by using ~ltlfilt --from-ltl="!dead"~ (from the command-line), a running =from_ltlf(f, "!dead")= in Python or C++. When working with LTLf, there are two different semantics for the next operator: - The weak next: =X a= is true if =a= hold in the next step, or if there is no next step. In particular, =X(0)= is true iff there is no successor. (By the way, you cannot write =X0= because that is an atomic proposition: use =X(0)= or =X 0=.) - The strong next: =X[!] a= is true if =a= hold in the next step *and* there must be a next step. In particular =X[!]1= asserts that there is a successor. To see the difference between =X= and =X[!]=, consider the following translation of =(a & Xa) | (b & X[!]b)=: #+name: tut12c #+begin_src sh :exports none f="(a & Xa) | (b & X[!]b)" ltlfilt --from-ltlf -f "$f" | ltl2tgba -B | autfilt --name="$f" --to-finite -d.nA #+end_src #+BEGIN_SRC dot :file tut12c.svg :var txt=tut12c :exports results $txt #+END_SRC #+RESULTS: [[file:tut12c.svg]] Note that because in reality Spot supports only transitions-based acceptance, and the above automaton is really stored as a Büchi automaton that *we* decide to interpret as a finite automaton, there is a bit of trickery involved to represent an "accepting state" without any successor. While such a state makes sense in finite automata, it makes no sense in ω-automata: we fake it by adding a self-loop labeled by *false* (internally, this extra edge is carrying the acceptance mark that is displayed on the state). For instance: #+name: tut12d #+begin_src sh :exports none f="a | (b & X(0))" ltlfilt --from-ltlf -f "$f" | ltl2tgba -B | autfilt --name="$f" --to-finite -d.nA #+end_src #+BEGIN_SRC dot :file tut12d.svg :var txt=tut12d :exports results $txt #+END_SRC #+RESULTS: [[file:tut12d.svg]] # LocalWords: utf LTLf html args ltlf src ltlfilt Fc tgba svg txt # LocalWords: ap De Giacomo Vardi IJCAI Dutta Memocode acc Buchi ba # LocalWords: postprocess aut det str NFA DFA ltsmin iff LTL Büchi # LocalWords: automata ltl autfilt HOA buchi sbacc iostream hoa Xa # LocalWords: Kripke nA