-
Notifications
You must be signed in to change notification settings - Fork 6
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
[RFC] Implement DexVerify and LTD #34
base: main
Are you sure you want to change the base?
Conversation
Add LTL notes outlining existing ideas and findings.
This commit contains the inital LTL implementation. It has a lot of dev-cruft such as dev prints, ugly comments, poor programming practices etc.
This commit just aims to tidy up the arg parsing. It is a step in the right direction but is still far from a final implementation.
These need fleshing out but for now they serve as regressions tests while developing the LTD system.
Model holds = 0.0 (no penalty) Model does not hold = 1.0 (full penalty)
...to 'proposition' for clarity.
Composite operators were previously printing "{name} is {underlying-formula}({ args})" which was confusing for complex models. They now print only "{name}({ args})". The full trace (when implemented) will show how the operators are composed under the hood. Improve LTD error messages If the wrong number of args are given to any class inheriting from any of the classes in OperatorTypes.py the derived class name is printed with the error. name is printed with the error.
Next(p): p must hold at the next time step.
This patch adds class Composite which all composite operators should inherit from. The composite operators then only need to define the underlying proposition which removes some boilerplate code.
After(p, q): p must hold at some point after q. Both must hold but cannot hold simultaneously.
This allows additional globals to be passed to eval() without allowing the user to call the subcommands globally in the test file.
The LTD commands are now no longer callable from the test files outside of the DexVerify command.
Add checks for DextStepIter validity before dereferencing Fix LTD After() docstring The docstring had switched p and q when describing After's behaviour. This will probably be a common error because of the prefix notation. This has been raised as an issue and will be discussed in the future. Add missing newline to CompositeOperators.py
Release operator LTL definition was using U instead of W operator. The LTL definitions of Weak and Until are now more solid. I've done a little rewording here and there.
Also update an incorrect TODO comment.
Ordered(p, q, ...): p comes before q [comes before ...]. The propositions may be separated by any number of steps but they all must eventually hold.
At the moment the LTL_work document is around 200 lines, there are at least 100 lines of license comments, and there are around 150 lines of "example" test files. So, this PR is not actually as large as it looks. |
I've noticed that my implementation of Next() is inconsistent. An easy fix: Next() should return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, I would say something about adding unittests but...
dex/command/commands/DexVerify.py
Outdated
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
# THE SOFTWARE. | ||
"""[TODO] Add words |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Words should be added before merging
Thanks @Melamoto for taking a look! |
Next(p) should return False if there is no next time step. Without this patch Next(True) would have always been evaluated to True. Henceforth(q) == Release(False, q) == Weak(q, And(q, False)) And(q, False) is always False, so: Weak(q, And(q, False)) == Weak(q, False) Fixed some typos and removed an unused import.
Latest commit (afbd9ff) fixes |
Add and improve LTD in-code documentation. External documentation will be required for the operators but the format is yet to be decided.
BinaryOperatorTree.proposition_template() was fairly un-pythonic. Improve DexVerify docstring.
EDIT Here's the full RFC walk through.
DexVerify and LTD
Background
Combined with #26, this patch allows us to use DExTer to verify any and all
expected program states are encountered while debugging.
The work is all based on Linear Temporal Logic (LTL). Using LTL you can verify
that a set of propositions hold over time. LTL uses a small set of base
temporal operators and boolean connectives to build up complex propositions.
While not strictly true in LTL theory, consider the following to be our base
set of operators:
Andour atomic propositions as:
LTL_work.md covers all the definitions of the operators, but I'll give a quick
overview here.
Until:
p U q
means p must hold at least until q holds, which it must do at some point.Weak (weak Until):
p W q
means p must hold until q holds, or forever if q does not hold.Next:
X p
means p must hold in the next time frame.Propositions can be combined with these operators to create new propositions:
Where
a
is an atomic proposition as described above.It becomes useful to define common formulae patterns as composite operators.
One such example is
Eventually
orF
for future.Taking the definition I provided earlier "True (p) must hold at least until
q (a) holds, which it must do at some point". That is to say,
a
mustEventually hold.
LTL to LTD
The LTL operators are exposed as subcommands (referred to as LTD) to a new
dexter command
DexVerify
.Time is discrete, where each step represents a debugger step.
Next(p)
meansthat
p
must hold at the next debugger step.Use the LTD command
ExpectState
to make an assertion about the program state.ExpectState
behaves very similarly to the standaloneDexExpectProgramState
command.
To verify an assertion that some program state is eventually encountered,
you'd write:
Implementation
The base operators are defined in BasicOperators.py. These are derived from
an operator type (binary, unary) from OperatorTypes.py, which are all in turn
derived from Proposition from Proposition.py.
Propositions and boolean connectives can only work on the program state as
described by the debugger at a specific point in time. Temporal operators,
however, need to look into the future.
To accomodate this a program state iterator DextStepIter is defined in
DextIR.py. Operators can dereference and increment the iterator before passing
it on to their operands. Any operator which must increment the iterator first
copies it so that the "future looking" is limited to only its operands.
See
Until.eval(...)
andNext.eval(...)
for concrete examples.Using the basic operator types as a foundation with which to build all
composite operators makes for some really compact defintions.
Composite operators, defined in CompositeOperators.py, inherit from Composite.
They must call set_proposition to define the forumla they represent. For
example, The sum of the class Eventually is as follows:
Remember that the definition for Eventually (F) is
True U a
.This RFC defines more composite operators but hopefully you're now equipped to
look at these in source.
The examples/LTD/ directory contains some simple examples which can be run
through DExTer with this patch (xfail prefix denotes an expected failure).
Shortcomings
This patch doesn't provide any nice output for --verbose. We need to implement
a nice error reporter which can concisely explain why a given model doesn't
hold.
Exposing operators as function calls means all formulae read in prefix notation.
This turns
p Until q
intoUntil(p, q)
. I personally read this left to rightas "Until p holds, q must hold". This is incorrect. Until(p, q) means "Until q
holds, p must hold". I find that swapping the operands makes it more readable,
but then of course makes it confusing to people who read prefix notation naturally.
I think this at least warrants a discussion.
Prefix notation also makes some operators read strangely. I think
Both(p, q)
andEither(p, q)
instead ofAnd(p, q)
andOr(p, q)
respectively are more natural.The implementation doesn't do anything to prevent someone adding an operator
with circular dependencies. Hopefully whoever implemented the new operator will
have run tests with it and then seen a max stack depth exception.