Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does logic.past work? #11

Open
thisiscam opened this issue May 16, 2020 · 3 comments
Open

How does logic.past work? #11

thisiscam opened this issue May 16, 2020 · 3 comments
Assignees
Labels
documentation Adding or editing any form of documentation (e.g., documentation files, code comments).

Comments

@thisiscam
Copy link

Hi, I was studying about past LTL operators, and ran into this repo. Could someone explain how the translate routine omega.logic.past works? Specifically, I wonder what is the returned tuple value and what does "initial condition" mean?

@johnyf
Copy link
Member

johnyf commented May 17, 2020

About past LTL, please refer to the literature cited in the docstring of
the module omega.logic.past:

https://github.com/tulip-control/omega/blob/v0.3.1/omega/logic/past.py#L4-L27

A usage example of this module is the function openpromela.logic.map_to_future:

https://github.com/johnyf/openpromela/blob/8a74b37b2b481cb9b1914fd9b2b4b38bb8162a73/openpromela/logic.py#L2272
(requires a previous version of omega
https://github.com/tulip-control/omega/blob/v0.0.7/omega/logic/past.py)

of the package openpromela,
which is described in a paper
and a technical report.

The function omega.logic.past.translate

https://github.com/tulip-control/omega/blob/v0.3.1/omega/logic/past.py#L245

takes a formula that may contain past LTL operators and returns a translated
formula with operators of future LTL, together with additional variables
that work as memory for representing the past LTL operators, and formulas about
these additional variables. These additional variables and formulas are called
temporal testers.

The past operators are listed in the documentation

https://github.com/tulip-control/omega/blob/v0.3.1/doc/doc.md#temporal-logic-syntax

  • '-X' expr # weak previous
  • '--X' expr # strong previous
  • '-[]' expr # historically (dual of [])
  • '-<>' expr # once (dual of <>)
  • expr 'S' expr # since

For example, assuming that u is a variable, translating the operator
"weak previous":

>>> from omega.logic import past

>>> past.translate('-X u')

({'u_prev1': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 'u_prev1',
 'u_prev1',
 '((X u_prev1) <=> u)',
 [])

The function omega.logic.past.translate returns a tuple with 5 items:

dvars, r, init, trans, win
  • dvars: dictionary of variable declarations. These are the additional
    memory variables added to represent the past LTL operators. In the example,
    u_prev1 is the additional variable, and is Boolean-valued.

  • r: translated formula in future LTL. In the example, -X u has been
    translated to u_prev1.

  • init: the initial condition applicable to the additional variable.
    Weak previous is TRUE at the initial state, so the initial condition is
    u_prev1. This initial condition together with the transition formula and
    the liveness formula describe the temporal tester.

  • trans: transition formula applicable to the additional variable.
    In the example, ((X u_prev1) <=> u). The operator X means "next" and
    can also be written using a prime u_prev1' <=> u. This formula requires
    that the next value of the memory variable u_prev1 be equal to the current
    value of the variable u. In other words, the variable u_prev1 remembers
    the previous value of variable u, and thus implements the operator
    "weak previous".

    This formula is understood to be placed within the operator
    []. So what trans means is []((X u_prev1) <=> u). The operator "always"
    has been omitted because the returned formulas are intended to be stored in
    an automaton instance, in the attributes aut.init, aut.action, and
    aut.win. The automaton represents a formula with initial condition,
    []action formulas, and []<>win formulas for each item in win.

    The automaton class is omega.symbolic.temporal.Automaton:

    https://github.com/tulip-control/omega/blob/v0.3.1/omega/symbolic/temporal.py#L36

  • win: recurrence formula, to be placed within []<>. The example of
    translating the operator "until" (below) includes a liveness formula
    (see also d7e8d1e).

Translating the operator "strong previous":

>>> past.translate('--X u')

({'u_prev1': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 'u_prev1',
 '(~ u_prev1)',
 '((X u_prev1) <=> u)',
 [])

Compared to the operator "weak previous", the operator "strong previous" is
represented by a variable with initial value FALSE, as described by (~ u_prev1).
The reason is that:

  • The operator "strong previous" (--X) is TRUE if there exists a previous state
    and the argument is TRUE in the previous state.

  • The operator "weak previous" (-X) is TRUE if there does not exist
    a previous state or there exists a previous state and the argument is TRUE
    in the previous state.

Translating the operator "historically":

>>> past.translate('-[] u')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '(~ _aux0)',
 '(_aux0 <=> ( ~ u ))',
 '((X _aux0) <=> (    (X ( ~ u )) \\/ ((X True) /\\ _aux0)))',
 [])

The translation of the formula -[] u is ~ _aux0.
The auxiliary variable _aux0 starts equal to ~ u (negated u) and becomes
TRUE if ~ u becomes TRUE. After becoming TRUE, variable _aux0 remains TRUE.
So variable _aux0 remembers whether u has become FALSE, and ~ _aux0 is
TRUE if u has been uninterruptedly TRUE in the past.

Translating the operator "once":

>>> past.translate('-<> u')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '_aux0',
 '(_aux0 <=> u)',
 '((X _aux0) <=> (    (X u) \\/ ((X True) /\\ _aux0)))',
 [])

The translation of the formula -<> u is _aux0.
The auxiliary variable _aux0 starts equal to u and becomes TRUE if u
becomes TRUE. After becoming TRUE, variable _aux0 remains TRUE.
So variable _aux0 remembers whether u has become TRUE, and _aux0 is TRUE
if u has been TRUE at least once in the past.

Translating the operator "since":

>>> past.translate('a S b')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '_aux0',
 '(_aux0 <=> b)',
 '((X _aux0) <=> (    (X b) \\/ ((X a) /\\ _aux0)))',
 [])

The translation of the formula a S b is _aux0. The variable _aux0 becomes
TRUE when b becomes TRUE. Afterwards, _aux0 remains TRUE as long as a is
TRUE, until b become TRUE again. So variable _aux0 remembers if a has been
uninterruptedly TRUE since the last state when b was TRUE.

The operator "until" can also be translated (for a closed system):

>>> past.translate('a U b', until=True)

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '_aux0',
 'True',
 '(_aux0 <=> (    (b) \\/ ((a) /\\ (X _aux0))))',
 ['((b) \\/ ~ _aux0)'])

An example with more than one temporal operator of past LTL:

>>> past.translate('-[] (a S b)')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'},
  '_aux1': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '(~ _aux1)',
 '((_aux0 <=> b)) /\\ ((_aux1 <=> ( ~ _aux0 )))',
 '(((X _aux0) <=> (    (X b) \\/ ((X a) /\\ _aux0)))) /\\ (((X _aux1) <=> (    (X ( ~ _aux0 )) \\/ ((X True) /\\ _aux1))))',
 [])

@thisiscam
Copy link
Author

thisiscam commented May 17, 2020

@johnyf Thanks for the detailed reply!

I think I understand now. Just to confirm, is the following correct:

>>> past.translate('a S b')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '_aux0',
 '(_aux0 <=> b)',
 '((X _aux0) <=> (    (X b) \\/ ((X a) /\\ _aux0)))',
 [])

This is really saying that a S b is equivalent to _aux0, given that (_aux0 <=> b) /\\ []((X _aux0) <=> ( (X b) \\/ ((X a) /\\ _aux0))) (initial condition conjuncted with "always transition relation".

In other words, I think another way to say this is that a S b is equivalent to

exists _aux0, (_aux0 <=> b) /\\ []((X _aux0) <=> (    (X b) \\/ ((X a) /\\ _aux0)))

(where I existentially quantified _aux0 to project it out)

@johnyf
Copy link
Member

johnyf commented May 18, 2020

This is correct provided that the translated formula a S b occur within the scope of the temporal existential quantifier.

Stutter-invariant temporal existential quantification in the temporal logic of actions, TLA+ is denoted by \EE. Please refer to this book about TLA+ and this paper about TLA.

If we denote stutter-sensitive temporal existential quantification in raw TLA+ by \ee, then the translation of the formula a S b could be written as follows (in TLA+ prime ' denotes the operator "next"):

\ee _aux0:  /\ _aux0
            /\ (_aux0 <=> b)
            /\ []((_aux0') <=> ((b') \/ ((a') /\ _aux0)))

The translation of past temporal operators is based on Section 5 of this paper. Quantified propositional temporal logic (QPTL) is described in this paper.

Another example would be translating the formula [](a S b).

>>> past.translate('[] (a S b)')

({'_aux0': {'type': 'bool', 'dom': None, 'owner': 'sys'}},
 '( [] _aux0 )',
 '(_aux0 <=> b)',
 '((X _aux0) <=> (    (X b) \\/ ((X a) /\\ _aux0)))',
 [])

The translated formula in raw TLA+ would be (== in TLA+ denotes a definition)

TranslatedFormula ==
    \ee _aux0:
        /\ [] _aux0
        /\ _aux0 <=> b
        /\ [](_aux0' <=> (b' \/ (a' /\ _aux0)))

The formula [](a S b) is equivalent to the translated formula, i.e.,

OriginalFormula == [](a S b)

THEOREM
    OriginalFormula <=> TranslatedFormula

The TLA+ proof language is described in this document.

The variable _aux0 could be considered a history-determined variable in raw TLA+.
History-determined variables in TLA are discussed in Section 2.4 of this paper.

Realizability of properties with
quantified history-determined variables is preserved when "unhiding" the history-determined variables by removing the temporal quantifier, as discussed in Section 9.7 on pages 154--157 of this dissertation (Table 3.2 on page 18 lists temporal and rigid quantifiers).

So GR(1) synthesis can be used when past temporal operators occur, after applying the translation of the past temporal operators and the addition of the auxiliary, history-determined variables.

The preservation of realizability when unhiding history-determined variables is described in detail in the module HistoryIsRealizable in the supplemental material, in particular the theorem RealizingHistory.

A definition of stutter-sensitive temporal quantification is included on page 4 of the module TemporalLogic in the supplemental material.

@johnyf johnyf added the documentation Adding or editing any form of documentation (e.g., documentation files, code comments). label Jun 8, 2021
@johnyf johnyf self-assigned this Jun 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Adding or editing any form of documentation (e.g., documentation files, code comments).
Projects
None yet
Development

No branches or pull requests

2 participants