erl-CaTL 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
erl_catl-1.0.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Explainable Robotics Lab (ERL)
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: erl-CaTL
3
+ Version: 1.0.0
4
+ Summary: Capability Temporal Logic Tool
5
+ Author-email: Crockett Hensley <crh325@lehigh.edu>, Cristian-Ioan Vasile <crv519@lehigh.edu>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/erl-lehigh/catl
8
+ Project-URL: Repository, https://github.com/erl-lehigh/catl
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pytelo>=1.0.0
13
+ Requires-Dist: networkx>=3.0
14
+ Requires-Dist: shapely>=2.0
15
+ Requires-Dist: pyyaml>=5.4
16
+ Requires-Dist: matplotlib>=3.7
17
+ Dynamic: license-file
18
+
19
+ # Capability Temporal Logic (CaTL)
20
+
21
+ Setup
22
+ -----
23
+
24
+ ```bash
25
+ git clone git@github.com:wasserfeder/catl.git
26
+ mkdir -p catl/lib
27
+ cd catl/lib
28
+ wget 'https://www.antlr.org/download/antlr-4.8-complete.jar'
29
+ pip install antlr4-python2-runtime==4.8
30
+ sudo apt-get install default-jre
31
+ ```
32
+
33
+ For permanent settings use:
34
+
35
+ ```bash
36
+ cd <clone-dir>
37
+ echo "export CLASSPATH=\".:$PWD/lib/antlr-4.8-complete.jar:$CLASSPATH\"" >> ~/.bashrc
38
+ echo "alias antlr4=\"java -jar $PWD/lib/antlr-4.8-complete.jar -visitor\"" >> ~/.bashrc
39
+ echo "alias grun=\"java org.antlr.v4.gui.TestRig\"" >> ~/.bashrc
40
+ ```
41
+
42
+ Otherwise
43
+
44
+ ```bash
45
+ cd <clone-dir>
46
+ export CLASSPATH=".:$PWD/lib/antlr-4.8-complete.jar:$CLASSPATH"
47
+ alias antlr4="java -jar $PWD/lib/antlr-4.8-complete.jar -visitor"'
48
+ alias grun="java org.antlr.v4.gui.TestRig"
49
+ ```
50
+
51
+ where `<clone-dir>` is the directory where you cloned the `catl` repository.
52
+
53
+ Install *Gurobi* with *gurobipy* for python2, and optionally for python3.
54
+
55
+
56
+ Run
57
+ ---
58
+
59
+ ```bash
60
+ cd <clone-dir>/catl
61
+ antlr4 -Dlanguage=Python2 catl.g4
62
+ ```
63
+
64
+ **NOTE:** At the moment the implementation only supports python2. However, you
65
+ can generate lexers, parsers, listeners, and visitors for other target languages,
66
+ such as Java (default), C++, Python3, C#, Go, JavaScript, and Swift.
67
+ See http://www.antlr.org/download.html for more details.
@@ -0,0 +1,49 @@
1
+ # Capability Temporal Logic (CaTL)
2
+
3
+ Setup
4
+ -----
5
+
6
+ ```bash
7
+ git clone git@github.com:wasserfeder/catl.git
8
+ mkdir -p catl/lib
9
+ cd catl/lib
10
+ wget 'https://www.antlr.org/download/antlr-4.8-complete.jar'
11
+ pip install antlr4-python2-runtime==4.8
12
+ sudo apt-get install default-jre
13
+ ```
14
+
15
+ For permanent settings use:
16
+
17
+ ```bash
18
+ cd <clone-dir>
19
+ echo "export CLASSPATH=\".:$PWD/lib/antlr-4.8-complete.jar:$CLASSPATH\"" >> ~/.bashrc
20
+ echo "alias antlr4=\"java -jar $PWD/lib/antlr-4.8-complete.jar -visitor\"" >> ~/.bashrc
21
+ echo "alias grun=\"java org.antlr.v4.gui.TestRig\"" >> ~/.bashrc
22
+ ```
23
+
24
+ Otherwise
25
+
26
+ ```bash
27
+ cd <clone-dir>
28
+ export CLASSPATH=".:$PWD/lib/antlr-4.8-complete.jar:$CLASSPATH"
29
+ alias antlr4="java -jar $PWD/lib/antlr-4.8-complete.jar -visitor"'
30
+ alias grun="java org.antlr.v4.gui.TestRig"
31
+ ```
32
+
33
+ where `<clone-dir>` is the directory where you cloned the `catl` repository.
34
+
35
+ Install *Gurobi* with *gurobipy* for python2, and optionally for python3.
36
+
37
+
38
+ Run
39
+ ---
40
+
41
+ ```bash
42
+ cd <clone-dir>/catl
43
+ antlr4 -Dlanguage=Python2 catl.g4
44
+ ```
45
+
46
+ **NOTE:** At the moment the implementation only supports python2. However, you
47
+ can generate lexers, parsers, listeners, and visitors for other target languages,
48
+ such as Java (default), C++, Python3, C#, Go, JavaScript, and Swift.
49
+ See http://www.antlr.org/download.html for more details.
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "erl-CaTL" # This becomes the pip name
7
+ version = "1.0.0"
8
+ description = "Capability Temporal Logic Tool"
9
+ readme = "README.md"
10
+ license = {text = "BSD-3-Clause"}
11
+ authors = [{name = "Crockett Hensley", email = "crh325@lehigh.edu"}, {name = "Cristian-Ioan Vasile", email = "crv519@lehigh.edu"}]
12
+ requires-python = ">=3.10"
13
+ dependencies = [
14
+ "pytelo>=1.0.0",
15
+ "networkx>=3.0",
16
+ "shapely>=2.0",
17
+ "pyyaml>=5.4",
18
+ "matplotlib>=3.7"
19
+ ]
20
+
21
+
22
+ [project.urls]
23
+ "Homepage" = "https://github.com/erl-lehigh/catl"
24
+ "Repository" = "https://github.com/erl-lehigh/catl"
25
+
26
+ [tool.setuptools]
27
+ package-dir = {"" = "src"}
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ '''
2
+ Copyright (C) 2018-2020 Cristian Ioan Vasile <cvasile@lehigh.edu>
3
+ Explainable Robotics Lab (ERL), Autonomous and Intelligent Robotics (AIR) Lab,
4
+ Lehigh University
5
+ Hybrid and Networked Systems (HyNeSs) Group, BU Robotics Lab, Boston University
6
+ See license.txt file for license information.
7
+ '''
8
+ from .catl import Operation, CATLFormula, CapabilityRequest
9
+ from .catl2stl import catl2stl
10
+
@@ -0,0 +1,316 @@
1
+ '''
2
+ Copyright (C) 2018-2020 Cristian Ioan Vasile <cvasile@lehigh.edu>
3
+ Explainable Robotics Lab (ERL), Autonomous and Intelligent Robotics (AIR) Lab,
4
+ Lehigh University
5
+ Hybrid and Networked Systems (HyNeSs) Group, BU Robotics Lab, Boston University
6
+ See license.txt file for license information.
7
+ '''
8
+
9
+ import itertools as it
10
+ from collections import namedtuple
11
+
12
+ from antlr4 import InputStream, CommonTokenStream, TerminalNode
13
+
14
+ from .catlLexer import catlLexer
15
+ from .catlParser import catlParser
16
+ from .catlVisitor import catlVisitor
17
+
18
+
19
+ class Operation(object):
20
+ '''CATL operations'''
21
+ NOP, NOT, OR, AND, IMPLIES, UNTIL, EVENT, ALWAYS, PRED, LIMIT, BOOL = \
22
+ range(11)
23
+ opnames = [None, '!', '||', '&&', '=>', 'U', 'F', 'G', 'T', 'L', 'bool']
24
+ opcodes = {'!': NOT, '&&': AND, '||' : OR, '=>': IMPLIES,
25
+ 'U': UNTIL, 'F': EVENT, 'G': ALWAYS, 'T': PRED, 'L': LIMIT}
26
+
27
+ @classmethod
28
+ def getCode(cls, text):
29
+ ''' Gets the code corresponding to the string representation.'''
30
+ return cls.opcodes.get(text, cls.NOP)
31
+
32
+ @classmethod
33
+ def getString(cls, op):
34
+ '''Gets custom string representation for each operation.'''
35
+ return cls.opnames[op]
36
+
37
+ CapabilityRequest = namedtuple('CapabilityRequest', ['capability', 'count'])
38
+ ResourceRequest = namedtuple('ResourceRequest', ['resource', 'quantity'])
39
+
40
+ class CATLFormula(object):
41
+ '''Abstract Syntax Tree representation of an CATL formula'''
42
+
43
+ def __init__(self, operation, **kwargs):
44
+ '''Constructor'''
45
+ self.op = operation
46
+
47
+ if self.op == Operation.BOOL:
48
+ self.value = kwargs['value']
49
+ elif self.op == Operation.PRED:
50
+ self.duration = kwargs['duration']
51
+ self.proposition = kwargs['proposition']
52
+ self.capability_requests = kwargs['capabilities']
53
+ self.resource_requests = kwargs['resources']
54
+ elif self.op == Operation.LIMIT:
55
+ self.proposition = kwargs['proposition']
56
+ self.capability_requests = kwargs['capabilities']
57
+ self.resource_requests = kwargs['resources']
58
+ elif self.op in (Operation.AND, Operation.OR):
59
+ self.children = kwargs['children']
60
+ elif self.op == Operation.IMPLIES:
61
+ self.left = kwargs['left']
62
+ self.right = kwargs['right']
63
+ elif self.op == Operation.NOT:
64
+ self.child = kwargs['child']
65
+ elif self.op in(Operation.ALWAYS, Operation.EVENT):
66
+ self.low = kwargs['low']
67
+ self.high = kwargs['high']
68
+ self.child = kwargs['child']
69
+ elif self.op == Operation.UNTIL:
70
+ self.low = kwargs['low']
71
+ self.high = kwargs['high']
72
+ self.left = kwargs['left']
73
+ self.right = kwargs['right']
74
+
75
+ self.__string = None
76
+ self.__hash = None
77
+
78
+ def robustness(self, s, t):
79
+ '''Computes the robustness of the CATL formula.'''
80
+ raise NotImplementedError
81
+
82
+ def bound(self):
83
+ '''Computes the bound of the CATL formula.'''
84
+ if self.op == Operation.BOOL:
85
+ return 0
86
+ elif self.op == Operation.PRED:
87
+ return self.duration
88
+ elif self.op == Operation.LIMIT:
89
+ return 0
90
+ elif self.op in (Operation.AND, Operation.OR):
91
+ return max([ch.bound() for ch in self.children])
92
+ elif self.op == Operation.IMPLIES:
93
+ return max(self.left.bound(), self.right.bound())
94
+ elif self.op == Operation.NOT:
95
+ return self.child.bound()
96
+ elif self.op == Operation.UNTIL:
97
+ return self.high + max(self.left.bound(), self.right.bound())
98
+ elif self.op in (Operation.ALWAYS, Operation.EVENT):
99
+ return self.high + self.child.bound()
100
+
101
+ def propositions(self):
102
+ '''Computes the set of propositions involved in the CATL formula.'''
103
+ if self.op in (Operation.PRED, Operation.LIMIT):
104
+ return {self.proposition}
105
+ elif self.op in (Operation.AND, Operation.OR):
106
+ return set.union(*[child.propositions() for child in self.children])
107
+ elif self.op in (Operation.IMPLIES, Operation.UNTIL):
108
+ return self.left.propositions() | self.right.propositions()
109
+ elif self.op in (Operation.NOT, Operation.ALWAYS, Operation.EVENT):
110
+ return self.child.propositions()
111
+
112
+ def capabilities(self):
113
+ '''Computes the set of capabilities involved in the CATL formula.'''
114
+ if self.op in (Operation.PRED, Operation.LIMIT):
115
+ return {cr.capability for cr in self.capability_requests}
116
+ elif self.op in (Operation.AND, Operation.OR):
117
+ return set.union(*[child.capabilities() for child in self.children])
118
+ elif self.op in (Operation.IMPLIES, Operation.UNTIL):
119
+ return self.left.capabilities() | self.right.capabilities()
120
+ elif self.op in (Operation.NOT, Operation.ALWAYS, Operation.EVENT):
121
+ return self.child.capabilities()
122
+
123
+ def resources(self):
124
+ '''Computes the set of resources involved in the CATL formula.'''
125
+ if self.op in (Operation.PRED, Operation.LIMIT):
126
+ return {cr.resource for cr in self.resource_requests}
127
+ elif self.op in (Operation.AND, Operation.OR):
128
+ return set.union(*[child.resources() for child in self.children])
129
+ elif self.op in (Operation.IMPLIES, Operation.UNTIL):
130
+ return self.left.resources() | self.right.resources()
131
+ elif self.op in (Operation.NOT, Operation.ALWAYS, Operation.EVENT):
132
+ return self.child.resources()
133
+
134
+ def identifier(self):
135
+ '''Computes an integer identifier for the formula based on the object's
136
+ hash value.
137
+ '''
138
+ h = hash(self)
139
+ if h < 0:
140
+ h = hex(ord('-'))[2:] + hex(-h)[1:]
141
+ else:
142
+ h = hex(ord('+'))[2:] + hex(h)[1:]
143
+ return h
144
+
145
+ def __hash__(self):
146
+ if self.__hash is None:
147
+ self.__hash = hash(str(self))
148
+ return self.__hash
149
+
150
+ def __eq__(self, other):
151
+ return str(self) == str(other)
152
+
153
+ def __ne__(self, other):
154
+ return not self.__eq__(other)
155
+
156
+ def __str__(self):
157
+ if self.__string is not None:
158
+ return self.__string
159
+
160
+ opname = Operation.getString(self.op)
161
+ if self.op == Operation.BOOL:
162
+ s = str(self.value)
163
+ elif self.op == Operation.PRED:
164
+ s = '({p} {d} {caps} {res})'.format(p=self.proposition,
165
+ d=self.duration,
166
+ caps=self.capability_requests,
167
+ res=self.resource_requests)
168
+ elif self.op == Operation.LIMIT:
169
+ s = '(! {p} {caps} {res})'.format(p=self.proposition,
170
+ caps=self.capability_requests,
171
+ res=self.resource_requests)
172
+ elif self.op == Operation.IMPLIES:
173
+ s = '({left} {op} {right})'.format(left=self.left, op=opname,
174
+ right=self.right)
175
+ elif self.op in (Operation.AND, Operation.OR):
176
+ children = [str(child) for child in self.children]
177
+ s = '(' + ' {op} '.format(op=opname).join(children) + ')'
178
+ elif self.op == Operation.NOT:
179
+ s = '{op} {child}'.format(op=opname, child=self.child)
180
+ elif self.op == Operation.UNTIL:
181
+ s = '({left} {op}[{low}, {high}] {right})'.format(op=opname,
182
+ left=self.left, right=self.right, low=self.low, high=self.high)
183
+ elif self.op in (Operation.ALWAYS, Operation.EVENT):
184
+ s = '({op}[{low}, {high}] {child})'.format(op=opname,
185
+ low=self.low, high=self.high, child=self.child)
186
+ else:
187
+ raise ValueError('Unknown operation {}!'.format(self.op))
188
+ self.__string = s
189
+ return self.__string
190
+
191
+ @classmethod
192
+ def from_formula(cls, formula):
193
+ '''Creates a CATLFormula object from a formula string.
194
+
195
+ Parameters
196
+ ----------
197
+ formula (str) CATL formula
198
+
199
+ Returns
200
+ -------
201
+ (CATLFormula) an AST of the CATL formula
202
+ '''
203
+ lexer = catlLexer(InputStream(formula))
204
+ tokens = CommonTokenStream(lexer)
205
+ parser = catlParser(tokens)
206
+ t = parser.catlProperty()
207
+ return CATLAbstractSyntaxTreeExtractor().visit(t)
208
+
209
+
210
+ class CATLAbstractSyntaxTreeExtractor(catlVisitor):
211
+ '''Parse Tree visitor that constructs the AST of an CATL formula'''
212
+
213
+ def visitFormula(self, ctx):
214
+ op = Operation.getCode(ctx.op.text)
215
+ ret = None
216
+ low = -1
217
+ high = -1
218
+ if op in (Operation.AND, Operation.OR):
219
+ left = self.visit(ctx.left)
220
+ right = self.visit(ctx.right)
221
+ assert op != right.op
222
+ if left.op == op:
223
+ children = left.children
224
+ else:
225
+ children = [left]
226
+ children.append(right)
227
+ ret = CATLFormula(op, children=children)
228
+ elif op == Operation.IMPLIES:
229
+ ret = CATLFormula(op, left=self.visit(ctx.left),
230
+ right=self.visit(ctx.right))
231
+ elif op == Operation.NOT:
232
+ ret = CATLFormula(op, child=self.visit(ctx.child))
233
+ elif op == Operation.UNTIL:
234
+ low = float(ctx.low.text)
235
+ high = float(ctx.high.text)
236
+ ret = CATLFormula(op, left=self.visit(ctx.left),
237
+ right=self.visit(ctx.right), low=low, high=high)
238
+ elif op in (Operation.ALWAYS, Operation.EVENT):
239
+ low = float(ctx.low.text)
240
+ high = float(ctx.high.text)
241
+ ret = CATLFormula(op, child=self.visit(ctx.child),
242
+ low=low, high=high)
243
+ else:
244
+ print('Error: unknown operation!')
245
+ return ret
246
+
247
+ def visitCatlPredicate(self, ctx):
248
+ return self.visit(ctx.predicate())
249
+
250
+ def visitPredicate(self, ctx):
251
+ if Operation.getCode(ctx.op.text) == Operation.PRED:
252
+ if ctx.resources():
253
+ resources = self.visit(ctx.resources())
254
+ else:
255
+ resources = set()
256
+ return CATLFormula(Operation.PRED, duration=int(ctx.duration.text),
257
+ proposition=ctx.proposition.text,
258
+ capabilities=self.visit(ctx.capabilities()),
259
+ resources=resources)
260
+ return CATLFormula(Operation.BOOL, value=bool(ctx.op.text))
261
+
262
+ def visitCatlLimit(self, ctx):
263
+ return self.visit(ctx.limit())
264
+
265
+ def visitLimit(self, ctx):
266
+ if Operation.getCode(ctx.op.text) == Operation.LIMIT:
267
+ if ctx.capabilities():
268
+ capabilities = self.visit(ctx.capabilities())
269
+ else:
270
+ capabilities = set()
271
+ if ctx.resources():
272
+ resources = self.visit(ctx.resources())
273
+ else:
274
+ resources = set()
275
+ if not capabilities and not resources:
276
+ raise Exception('Limit operator must constrain either agents'
277
+ ' or resources!')
278
+ return CATLFormula(Operation.LIMIT,
279
+ proposition=ctx.proposition.text,
280
+ capabilities=capabilities,
281
+ resources=resources)
282
+ return CATLFormula(Operation.BOOL, value=bool(ctx.op.text))
283
+
284
+ def visitCapabilities(self, ctx):
285
+ return {self.visit(ch) for ch in ctx.children
286
+ if not isinstance(ch, TerminalNode)}
287
+
288
+ def visitCapabilityRequest(self, ctx):
289
+ return CapabilityRequest(capability=ctx.cap.text,
290
+ count=int(ctx.count.text))
291
+
292
+ def visitResources(self, ctx):
293
+ return {self.visit(ch) for ch in ctx.children
294
+ if not isinstance(ch, TerminalNode)}
295
+
296
+ def visitResourceRequest(self, ctx):
297
+ return ResourceRequest(resource=ctx.res.text,
298
+ quantity=float(ctx.quantity.text))
299
+
300
+ def visitParprop(self, ctx):
301
+ return self.visit(ctx.child)
302
+
303
+
304
+ if __name__ == '__main__':
305
+ ast = CATLFormula.from_formula(
306
+ 'F[0, 2] T(4, test, {(a, 2), (b, 3)})'
307
+ '&& G[1, 7] T(2, test, {(a, 1), (c, 4)}, {(h1, 2.3), (h2, 5)})'
308
+ '&& F[3, 5] T(3, test2, {(b, 1), (d, 2)})'
309
+ '&& F[3, 5] L(test2, {(b, 1), (d, 2)})'
310
+ '&& F[3, 5] L(test2, {}, {(h1, 2.3), (h2, 5)})'
311
+ '&& F[3, 5] L(test2, {(b, 1), (d, 2)}, {(h1, 2.3), (h2, 5)})')
312
+ print('AST:', str(ast))
313
+ print('Propositions:', ast.propositions())
314
+ print('Capabilities:', ast.capabilities())
315
+ print('Resources:', ast.resources())
316
+ print('Bound:', ast.bound())
@@ -0,0 +1,110 @@
1
+ '''
2
+ Copyright (C) 2018-2020 Cristian Ioan Vasile <cvasile@lehigh.edu>
3
+ Explainable Robotics Lab (ERL), Autonomous and Intelligent Robotics (AIR) Lab,
4
+ Lehigh University
5
+ Hybrid and Networked Systems (HyNeSs) Group, BU Robotics Lab, Boston University
6
+ See license.txt file for license information.
7
+ '''
8
+
9
+ from antlr4 import InputStream, CommonTokenStream
10
+
11
+ from .catl import Operation as CATLOperation
12
+ from .catl import CATLFormula
13
+ from .catl import CATLAbstractSyntaxTreeExtractor
14
+
15
+ from .catlLexer import catlLexer
16
+ from .catlParser import catlParser
17
+
18
+ from pytelo.stl import Operation as STLOperation
19
+ from pytelo.stl import RelOperation as STLRelOperation
20
+ from pytelo.stl import STLFormula
21
+
22
+
23
+ def catl2stl(catl_ast):
24
+ '''Translates a CATL abstract syntax tree to an STL one.
25
+ The set of variables in the STL formula is the Cartesian product between the
26
+ set of atomic propositions and the set of capabilities in the CATL formula.
27
+ The variable labels are in the form "z_{proposition}_{capability}".
28
+ The STL predicates capture the minimum number ``n''of agents with capability
29
+ ``c'' needed at regions labeled with proposition ``p'', i.e.,
30
+ ``z_{\pi,c} \geq n''. Thus, a CATL task is translated to STL as follows
31
+
32
+ .. math ::
33
+
34
+ T(d, \pi, \{(c_1, n_1), \ldots,(c_m, n_m)\}) \equiv
35
+ \box_{[0, d]} \bigcup_{i=1}^{m} (z_{\pi, c_i} \geq n_i)
36
+ '''
37
+ if catl_ast.op == CATLOperation.BOOL:
38
+ return STLFormula(STLOperation.BOOL, value=catl_ast.value)
39
+ elif catl_ast.op == CATLOperation.PRED:
40
+ var = catl_ast.proposition + '_{cap}'
41
+ conjunction_terms = \
42
+ [STLFormula(STLOperation.PRED, relation=STLRelOperation.GE,
43
+ variable=var.format(cap=cap), threshold=th)
44
+ for cap, th in catl_ast.capability_requests]
45
+ child = STLFormula(STLOperation.AND, children=conjunction_terms)
46
+
47
+ return STLFormula(STLOperation.ALWAYS, low=0, high=catl_ast.duration,
48
+ child=child)
49
+ elif catl_ast.op == CATLOperation.LIMIT:
50
+ var = catl_ast.proposition + '_{cap}'
51
+ conjunction_terms = \
52
+ [STLFormula(STLOperation.PRED, relation=STLRelOperation.LE,
53
+ variable=var.format(cap=cap), threshold=th)
54
+ for cap, th in catl_ast.capability_requests]
55
+ return STLFormula(STLOperation.AND, children=conjunction_terms)
56
+ elif catl_ast.op in (CATLOperation.AND, CATLOperation.OR):
57
+ children = [catl2stl(ch) for ch in catl_ast.children]
58
+ if catl_ast.op == CATLOperation.AND:
59
+ op = STLOperation.AND
60
+ else:
61
+ op = STLOperation.OR
62
+ return STLFormula(op, children=children)
63
+ elif catl_ast.op == CATLOperation.IMPLIES:
64
+ left = catl2stl(catl_ast.left)
65
+ right = catl2stl(catl_ast.right)
66
+ return STLFormula(STLOperation.IMPLIES, left=left, right=right)
67
+ elif catl_ast.op == CATLOperation.NOT:
68
+ child = catl2stl(catl_ast.child)
69
+ return STLFormula(STLOperation.NOT, child=child)
70
+ elif catl_ast.op in (CATLOperation.ALWAYS, CATLOperation.EVENT):
71
+ child = catl2stl(catl_ast.child)
72
+ if catl_ast.op == CATLOperation.ALWAYS:
73
+ op = STLOperation.ALWAYS
74
+ else:
75
+ op = STLOperation.EVENT
76
+ return STLFormula(op, child=child, low=catl_ast.low, high=catl_ast.high)
77
+ elif catl_ast.op == CATLOperation.UNTIL:
78
+ left = catl2stl(catl_ast.left)
79
+ right = catl2stl(catl_ast.right)
80
+ return STLFormula(STLOperation.UNTIL, left=left, right=right,
81
+ low=catl_ast.low, high=catl_ast.high)
82
+
83
+
84
+ if __name__ == '__main__':
85
+ formulae = (
86
+ 'F[0, 2] T(4, test, {(a, 2), (b, 3)})'
87
+ '&& G[1, 7] T(2, test, {(a, 1), (c, 4)})'
88
+ '&& F[3, 5] T(3, test2, {(b, 1), (d, 2)})'
89
+ '&& F[3, 5] L(test2, {(b, 1), (d, 2)})',
90
+ #
91
+ 'F[0,25] T(3,blue,{(C1,1),(C2,1)})'
92
+ '&& F[0,25] T(2,orange,{(C1,1),(C2,1)})'
93
+ '&& (L(orange,{(C1,1),(C2,1)}) U[0,25] T(3,blue,{(C1,1),(C2,1)}))'
94
+ )
95
+
96
+ for formula in formulae:
97
+ print('Formula:', formula)
98
+ lexer = catlLexer(InputStream(formula))
99
+
100
+ tokens = CommonTokenStream(lexer)
101
+
102
+ parser = catlParser(tokens)
103
+ t = parser.catlProperty()
104
+ print(t.toStringTree())
105
+
106
+ ast = CATLAbstractSyntaxTreeExtractor().visit(t)
107
+ print('CATL:', ast)
108
+
109
+ stl = catl2stl(ast)
110
+ print('STL:', stl)