STL API
Table of Contents
- STL API
Motivation
STL API is used to evaluate Signal Temporal Logic specification with respect to an arbitrary signal.
Installation
Configure system PATH
export PATH="/path/to/STL-API/bin/directory:$PATH"
Configure PYTHON PATH (for invoking STL API from other modules)
- Temporary Solution:
import sys sys.path.append("/path/to/STL-API/directory/") import stl.api
-
Permanent Solution:
Adding the following line to your shell configuration file (in Unix-based OS). For Mac users, the default shell configuration file is ~/.zshrc
export PYTHONPATH="/path/to/STL-API/directory:$PYTHONPATH"
Configure STL PATH (for unit testing and other repository referential operations)
export STLPATH="/path/to/STL-API/"
Reload .zshrc
file
finally, execute the following command on the command line to reload .zshrc
zsh configuration file
source ~/.zshrc
required Platform
- Python3.9+
required Python packages
- rply (for parser and lexer)
- termcolor (for color printing tools)
Features
- Evaluate simple mathematical expressions and propositional logic
- Calculate the robustness value of a signal with respect to an STL formula
- Syntactically weaken the STL formula
Usage (Python API)
Below we will be showing a simple example demonstrating the usage of the STL API by evaluating a simple STL expression G[0, 1](x > y)
with respect to a signal
To begin, we have to import the essential libraries from the STL API
from stl import Signal, STL
Simple Math Expressions
spec = STL("1 + 1")
# 2
spec = STL("true => false")
# False
signal = Signal(py_dict={"0": {"content": {"x": 1, "y": 2}},
"1": {"content": {"x": 2, "y": 1}}})
spec = STL("x > 0")
# True
Evaluate Signal with respect to STL Formula
Then, we define a (global) start time in int
type, as well as defining an arbitrary signal
time_begin = 0
signal = Signal(py_dict={"0": {"content": {"x": 1, "y": 2}},
"1": {"content": {"x": 2, "y": 1}}})
Next, we define the STL expression we would like to evaluate
stl_spec = STL("G[0, 1](x > y)")
There are often two parameters that we can obtain from the evaluation of STL expressions, namely, the satisfaction value,
in bool
type (True/False), and the robustness value and the robustness value, in float
type. There are two ways
of accessing these values:
The first approach is a fairly straightforward one:
satisfy : bool = stl_spec.satisfy(time_begin, signal) # obtain the satisfaction value
robustness : float = stl_spec.robustness(time_begin, signal) # obtain the robustness value
The second approach is a bit less straightforward, but it allows the evaluation results to be cached for future access
within an object called Eval_Result
stl_eval: Eval_Result = stl_spec.eval(time_begin, signal)
satisfy : bool = stl_eval.satisfy
robustness : float = stl_eval.robustness
STL Formula Weakening
We can also use the Python API to weaken the constraint of an STL formula. For example, suppose we have an STL expression with the following form
stl_spec = STL("G[0, 1](0 < x < 1)")
One way of weakening the formula is by relaxing the bound of the atomic proposition in the STL formula
weakened_stl_spec_1 = stl_spec.weaken("ap-range", 2, 3)
print(weakened_stl_spec_1)
# G[0, 1]((0 - 2) < x < (1 + 3))
weakened_stl_spec_2 = stl_spec.weaken("time-range", 2, 3)
# G[0 - 2, 1 + 3](0 < x < 1)
Usage (REPL)
We can simply the usage procedure above into a simply REPL interface. Users can start the REPL by typing stlinterp
on
the command line
$ stlinterp
Time Start: 0
Signal:
{
"0": {
"content": {
"x": 1,
"y": 2
}
},
"1": {
"content": {
"x": 2,
"y": 1
}
}
}
Please enter STL expressions to be interpreted.
>>>
Once the REPL is started, it will prompt you to enter the STL expression. Users can simply type the expression
G[0, 1](x > y)
after the >>>
, click Enter
to evaluate the expression.
>>> G[0, 1](x > y)
satisfy : False
robustness : -1.0
Structure
Project Structure
- API-level interfaces (high-level)
- They are designed to be simple to use with minimal overhead for training and learning.
- API-level interfaces include
stl.obj
as well as programs instl.api
.
- Low-level interfaces
- Low-level interfaces are designed to support lower-level operations like lexing and parsing of the strings passed in from the API level. It is not intended to interact with the user directly.
- It includes all modules in
stl.parsing
File Structure
bin/
: consists of bash scripts. must be added to system PATH
variable to be executed on the command line.
stllex
: start the REPL for the lexical analyzerstlparse
: start the REPL for the parserstlinterp
: start the REPL for the interpreterstltest
: initiate unit teststlsize
: show the size of the codebase
code-style/
: consists of code-writing guidelines excerpted from Google for code consistency purposes (for developers of the repository)
example/
-> stl/example/
: demonstrate example usage of the internal helper functions, low-level structures (lexer and parser level) as well as high-level objects (api level)
stl/
: main code repository
api.py
: main entry point for importing high-level (api level) objects and functionstool.py
: foundational tools for code repositoryunit_test.py
: handle unit testing of objects and tools, can be invoked usingstltest
command on the command lineobj/
: imported byapi.py
, intended to be used by the users of the API. consists of API level objectsparsing/
: not intended to be used directly by the user. low-level (parser and lexer level objects, i.e. AST (abstract syntax tree), type signatures)error.py
: main entry point for importing errorserr/
: all definition/implementation of errors
FAQ
Common Issues
- Vscode PyLint “Unable to Import” Error
- See Stackoverflow
- create
~/.pylintrc
with the following content[MASTER] init-hook='import sys; sys.path.append("/path/to/STL-API")'
Note that please replace
/path/to/STL-API
with the actual absolute path to the location of the respository
Special Notes
- example folder is in stl folder for unit-test purpose, a symbolic link for example folder is created in the project root directory
Future Work
- Fixed the bug when adding an integer to a floating-point number in stl interpreter
>>> 1 + 1.5 2
possible fix, detect type mismatch, and coerce all int types to float types
- create numeric type instead of using Ints and Floats
- reduce type check effort and automatically convert Ints to Floats
- syntax for absolute value
G[0, 1](|y| > 1)
means that the absolute value of y must be greater than 1 between time 0 and 1 of the signal
- support for nested STL expression
F G[0, 10](y > 1)
means that between time 0 and time 10, the y parameter of the signal will eventually always be greater than 1
- allow passing in external function/objects to evaluation
class Coord: def __init__(self, x, y): ... def distance_between_points(p1, p2) -> float: ... return result stl_spec = STL("G[0, 1](distance_between_points(Coord(a, b), Coord(c, d)) > 3)") stl_spec.add_extern_func(distance_between) stl_spec.add_extern_obj(Coord) stl_spec.eval(...)
- improved the signal so that it can handle more continuous times
- signal = Signal(py_dict={“0”: {“content”: {“timestamp”: 0.1, “x”: 1}}, “1”: {“content”: {“timestamp”: 0.2, “x”: 2}}})
- when the signal has “timestamp” as a quantifiable key, the STL expressions will evaluate based upon the timestamp instead of the timeindex
- backward compatibility