saracandu commited on
Commit
142a1a8
·
verified ·
1 Parent(s): 7a77717

Delete stl.py

Browse files
Files changed (1) hide show
  1. stl.py +0 -522
stl.py DELETED
@@ -1,522 +0,0 @@
1
- # For custom type-hints
2
- from custom_typing import realnum
3
-
4
- # For tensor functions
5
- import torch
6
- from torch import Tensor
7
- import torch.nn.functional as F
8
-
9
-
10
- def eventually(x: Tensor, time_span: int) -> Tensor:
11
- """
12
- STL operator 'eventually' in 1D.
13
-
14
- Parameters
15
- ----------
16
- x: torch.Tensor
17
- Signal
18
- time_span: any numeric type
19
- Timespan duration
20
-
21
- Returns
22
- -------
23
- torch.Tensor
24
- A tensor containing the result of the operation.
25
- """
26
- return F.max_pool1d(x, kernel_size=time_span, stride=1)
27
-
28
-
29
- class Node:
30
- """Abstract node class for STL semantics tree."""
31
-
32
- def __init__(self) -> None:
33
- # Must be overloaded.
34
- pass
35
-
36
- def __str__(self) -> str:
37
- # Must be overloaded.
38
- pass
39
-
40
- def boolean(self, x: Tensor, evaluate_at_all_times: bool = False) -> Tensor:
41
- """
42
- Evaluates the boolean semantics at the node.
43
-
44
- Parameters
45
- ----------
46
- x : torch.Tensor, of size N_samples x N_vars x N_sampling_points
47
- The input signals, stored as a batch tensor with trhee dimensions.
48
- evaluate_at_all_times: bool
49
- Whether to evaluate the semantics at all times (True) or
50
- just at t=0 (False).
51
-
52
- Returns
53
- -------
54
- torch.Tensor
55
- A tensor with the boolean semantics for the node.
56
- """
57
- z: Tensor = self._boolean(x)
58
- if evaluate_at_all_times:
59
- return z
60
- else:
61
- return self._extract_semantics_at_time_zero(z)
62
-
63
- def quantitative(
64
- self,
65
- x: Tensor,
66
- normalize: bool = False,
67
- evaluate_at_all_times: bool = False,
68
- ) -> Tensor:
69
- """
70
- Evaluates the quantitative semantics at the node.
71
-
72
- Parameters
73
- ----------
74
- x : torch.Tensor, of size N_samples x N_vars x N_sampling_points
75
- The input signals, stored as a batch tensor with three dimensions.
76
- normalize: bool
77
- Whether the measure of robustness if normalized (True) or
78
- not (False). Currently not in use.
79
- evaluate_at_all_times: bool
80
- Whether to evaluate the semantics at all times (True) or
81
- just at t=0 (False).
82
-
83
- Returns
84
- -------
85
- torch.Tensor
86
- A tensor with the quantitative semantics for the node.
87
- """
88
- z: Tensor = self._quantitative(x, normalize)
89
- if evaluate_at_all_times:
90
- return z
91
- else:
92
- return self._extract_semantics_at_time_zero(z)
93
-
94
- def set_normalizing_flag(self, value: bool = True) -> None:
95
- """
96
- Setter for the 'normalization of robustness of the formula' flag.
97
- Currently not in use.
98
- """
99
-
100
- def time_depth(self) -> int:
101
- """Returns time depth of bounded temporal operators only."""
102
- # Must be overloaded.
103
-
104
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
105
- """Private method equivalent to public one for inner call."""
106
- # Must be overloaded.
107
-
108
- def _boolean(self, x: Tensor) -> Tensor:
109
- """Private method equivalent to public one for inner call."""
110
- # Must be overloaded.
111
-
112
- @staticmethod
113
- def _extract_semantics_at_time_zero(x: Tensor) -> Tensor:
114
- """Extrapolates the vector of truth values at time zero"""
115
- return torch.reshape(x[:, 0, 0], (-1,))
116
-
117
-
118
- class Atom(Node):
119
- """Atomic formula node; for now of the form X<=t or X>=t"""
120
-
121
- def __init__(self, var_index: int, threshold: realnum, lte: bool = False) -> None:
122
- super().__init__()
123
- self.var_index: int = var_index
124
- self.threshold: realnum = threshold
125
- self.lte: bool = lte
126
-
127
- def __str__(self) -> str:
128
- s: str = (
129
- "x_"
130
- + str(self.var_index)
131
- + (" <= " if self.lte else " >= ")
132
- + str(round(self.threshold, 4))
133
- )
134
- return s
135
-
136
- def time_depth(self) -> int:
137
- return 0
138
-
139
- def _boolean(self, x: Tensor) -> Tensor:
140
- # extract tensor of the same dimension as data, but with only one variable
141
- xj: Tensor = x[:, self.var_index, :]
142
- xj: Tensor = xj.view(xj.size()[0], 1, -1)
143
- if self.lte:
144
- z: Tensor = torch.le(xj, self.threshold)
145
- else:
146
- z: Tensor = torch.ge(xj, self.threshold)
147
- return z
148
-
149
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
150
- # extract tensor of the same dimension as data, but with only one variable
151
- xj: Tensor = x[:, self.var_index, :]
152
- xj: Tensor = xj.view(xj.size()[0], 1, -1)
153
- if self.lte:
154
- z: Tensor = -xj + self.threshold
155
- else:
156
- z: Tensor = xj - self.threshold
157
- if normalize:
158
- z: Tensor = torch.tanh(z)
159
- return z
160
-
161
-
162
- class Not(Node):
163
- """Negation node."""
164
-
165
- def __init__(self, child: Node) -> None:
166
- super().__init__()
167
- self.child: Node = child
168
-
169
- def __str__(self) -> str:
170
- s: str = "not ( " + self.child.__str__() + " )"
171
- return s
172
-
173
- def time_depth(self) -> int:
174
- return self.child.time_depth()
175
-
176
- def _boolean(self, x: Tensor) -> Tensor:
177
- z: Tensor = ~self.child._boolean(x)
178
- return z
179
-
180
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
181
- z: Tensor = -self.child._quantitative(x, normalize)
182
- return z
183
-
184
-
185
- class And(Node):
186
- """Conjunction node."""
187
-
188
- def __init__(self, left_child: Node, right_child: Node) -> None:
189
- super().__init__()
190
- self.left_child: Node = left_child
191
- self.right_child: Node = right_child
192
-
193
- def __str__(self) -> str:
194
- s: str = (
195
- "( "
196
- + self.left_child.__str__()
197
- + " and "
198
- + self.right_child.__str__()
199
- + " )"
200
- )
201
- return s
202
-
203
- def time_depth(self) -> int:
204
- return max(self.left_child.time_depth(), self.right_child.time_depth())
205
-
206
- def _boolean(self, x: Tensor) -> Tensor:
207
- z1: Tensor = self.left_child._boolean(x)
208
- z2: Tensor = self.right_child._boolean(x)
209
- size: int = min(z1.size()[2], z2.size()[2])
210
- z1: Tensor = z1[:, :, :size]
211
- z2: Tensor = z2[:, :, :size]
212
- z: Tensor = torch.logical_and(z1, z2)
213
- return z
214
-
215
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
216
- z1: Tensor = self.left_child._quantitative(x, normalize)
217
- z2: Tensor = self.right_child._quantitative(x, normalize)
218
- size: int = min(z1.size()[2], z2.size()[2])
219
- z1: Tensor = z1[:, :, :size]
220
- z2: Tensor = z2[:, :, :size]
221
- z: Tensor = torch.min(z1, z2)
222
- return z
223
-
224
-
225
- class Or(Node):
226
- """Disjunction node."""
227
-
228
- def __init__(self, left_child: Node, right_child: Node) -> None:
229
- super().__init__()
230
- self.left_child: Node = left_child
231
- self.right_child: Node = right_child
232
-
233
- def __str__(self) -> str:
234
- s: str = (
235
- "( "
236
- + self.left_child.__str__()
237
- + " or "
238
- + self.right_child.__str__()
239
- + " )"
240
- )
241
- return s
242
-
243
- def time_depth(self) -> int:
244
- return max(self.left_child.time_depth(), self.right_child.time_depth())
245
-
246
- def _boolean(self, x: Tensor) -> Tensor:
247
- z1: Tensor = self.left_child._boolean(x)
248
- z2: Tensor = self.right_child._boolean(x)
249
- size: int = min(z1.size()[2], z2.size()[2])
250
- z1: Tensor = z1[:, :, :size]
251
- z2: Tensor = z2[:, :, :size]
252
- z: Tensor = torch.logical_or(z1, z2)
253
- return z
254
-
255
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
256
- z1: Tensor = self.left_child._quantitative(x, normalize)
257
- z2: Tensor = self.right_child._quantitative(x, normalize)
258
- size: int = min(z1.size()[2], z2.size()[2])
259
- z1: Tensor = z1[:, :, :size]
260
- z2: Tensor = z2[:, :, :size]
261
- z: Tensor = torch.max(z1, z2)
262
- return z
263
-
264
-
265
- class Globally(Node):
266
- """Globally node."""
267
- def __init__(
268
- self,
269
- child: Node,
270
- unbound: bool = False,
271
- right_unbound: bool = False,
272
- left_time_bound: int = 0,
273
- right_time_bound: int = 1,
274
- adapt_unbound: bool = True,
275
- ) -> None:
276
- super().__init__()
277
- self.child: Node = child
278
- self.unbound: bool = unbound
279
- self.right_unbound: bool = right_unbound
280
- self.left_time_bound: int = left_time_bound
281
- self.right_time_bound: int = right_time_bound + 1
282
- self.adapt_unbound: bool = adapt_unbound
283
-
284
- def __str__(self) -> str:
285
- s_left = "[" + str(self.left_time_bound) + ","
286
- s_right = str(self.right_time_bound) if not self.right_unbound else "inf"
287
- s0: str = s_left + s_right + "]" if not self.unbound else ""
288
- s: str = "always" + s0 + " ( " + self.child.__str__() + " )"
289
- return s
290
-
291
- def time_depth(self) -> int:
292
- if self.unbound:
293
- return self.child.time_depth()
294
- elif self.right_unbound:
295
- return self.child.time_depth() + self.left_time_bound
296
- else:
297
- # diff = torch.le(torch.tensor([self.left_time_bound]), 0).float()
298
- return self.child.time_depth() + self.right_time_bound - 1
299
- # (self.right_time_bound - self.left_time_bound + 1) - diff
300
-
301
- def _boolean(self, x: Tensor) -> Tensor:
302
- z1: Tensor = self.child._boolean(x[:, :, self.left_time_bound:]) # nested temporal parameters
303
- # z1 = z1[:, :, self.left_time_bound:]
304
- if self.unbound or self.right_unbound:
305
- if self.adapt_unbound:
306
- z: Tensor
307
- _: Tensor
308
- z, _ = torch.cummin(torch.flip(z1, [2]), dim=2)
309
- z: Tensor = torch.flip(z, [2])
310
- else:
311
- z: Tensor
312
- _: Tensor
313
- z, _ = torch.min(z1, 2, keepdim=True)
314
- else:
315
- z: Tensor = torch.ge(1.0 - eventually((~z1).double(), self.right_time_bound - self.left_time_bound), 0.5)
316
- return z
317
-
318
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
319
- z1: Tensor = self.child._quantitative(x[:, :, self.left_time_bound:], normalize)
320
- # z1 = z1[:, :, self.left_time_bound:]
321
- if self.unbound or self.right_unbound:
322
- if self.adapt_unbound:
323
- z: Tensor
324
- _: Tensor
325
- z, _ = torch.cummin(torch.flip(z1, [2]), dim=2)
326
- z: Tensor = torch.flip(z, [2])
327
- else:
328
- z: Tensor
329
- _: Tensor
330
- z, _ = torch.min(z1, 2, keepdim=True)
331
- else:
332
- z: Tensor = -eventually(-z1, self.right_time_bound - self.left_time_bound)
333
- return z
334
-
335
-
336
- class Eventually(Node):
337
- """Eventually node."""
338
-
339
- def __init__(
340
- self,
341
- child: Node,
342
- unbound: bool = False,
343
- right_unbound: bool = False,
344
- left_time_bound: int = 0,
345
- right_time_bound: int = 1,
346
- adapt_unbound: bool = True,
347
- ) -> None:
348
- super().__init__()
349
- self.child: Node = child
350
- self.unbound: bool = unbound
351
- self.right_unbound: bool = right_unbound
352
- self.left_time_bound: int = left_time_bound
353
- self.right_time_bound: int = right_time_bound + 1
354
- self.adapt_unbound: bool = adapt_unbound
355
-
356
- if (self.unbound is False) and (self.right_unbound is False) and \
357
- (self.right_time_bound <= self.left_time_bound):
358
- raise ValueError("Temporal thresholds are incorrect: right parameter is higher than left parameter")
359
-
360
- def __str__(self) -> str:
361
- s_left = "[" + str(self.left_time_bound) + ","
362
- s_right = str(self.right_time_bound) if not self.right_unbound else "inf"
363
- s0: str = s_left + s_right + "]" if not self.unbound else ""
364
- s: str = "eventually" + s0 + " ( " + self.child.__str__() + " )"
365
- return s
366
-
367
- def time_depth(self) -> int:
368
- if self.unbound:
369
- return self.child.time_depth()
370
- elif self.right_unbound:
371
- return self.child.time_depth() + self.left_time_bound
372
- else:
373
- # diff = torch.le(torch.tensor([self.left_time_bound]), 0).float()
374
- return self.child.time_depth() + self.right_time_bound - 1
375
- # (self.right_time_bound - self.left_time_bound + 1) - diff
376
-
377
- def _boolean(self, x: Tensor) -> Tensor:
378
- z1: Tensor = self.child._boolean(x[:, :, self.left_time_bound:])
379
- if self.unbound or self.right_unbound:
380
- if self.adapt_unbound:
381
- z: Tensor
382
- _: Tensor
383
- z, _ = torch.cummax(torch.flip(z1, [2]), dim=2)
384
- z: Tensor = torch.flip(z, [2])
385
- else:
386
- z: Tensor
387
- _: Tensor
388
- z, _ = torch.max(z1, 2, keepdim=True)
389
- else:
390
- z: Tensor = torch.ge(eventually(z1.double(), self.right_time_bound - self.left_time_bound), 0.5)
391
- return z
392
-
393
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
394
- z1: Tensor = self.child._quantitative(x[:, :, self.left_time_bound:], normalize)
395
- if self.unbound or self.right_unbound:
396
- if self.adapt_unbound:
397
- z: Tensor
398
- _: Tensor
399
- z, _ = torch.cummax(torch.flip(z1, [2]), dim=2)
400
- z: Tensor = torch.flip(z, [2])
401
- else:
402
- z: Tensor
403
- _: Tensor
404
- z, _ = torch.max(z1, 2, keepdim=True)
405
- else:
406
- z: Tensor = eventually(z1, self.right_time_bound - self.left_time_bound)
407
- return z
408
-
409
-
410
- class Until(Node):
411
- """Until node."""
412
-
413
- def __init__(
414
- self,
415
- left_child: Node,
416
- right_child: Node,
417
- unbound: bool = False,
418
- right_unbound: bool = False,
419
- left_time_bound: int = 0,
420
- right_time_bound: int = 1,
421
- ) -> None:
422
- super().__init__()
423
- self.left_child: Node = left_child
424
- self.right_child: Node = right_child
425
- self.unbound: bool = unbound
426
- self.right_unbound: bool = right_unbound
427
- self.left_time_bound: int = left_time_bound
428
- self.right_time_bound: int = right_time_bound + 1
429
-
430
- if (self.unbound is False) and (self.right_unbound is False) and \
431
- (self.right_time_bound <= self.left_time_bound):
432
- raise ValueError("Temporal thresholds are incorrect: right parameter is higher than left parameter")
433
-
434
- def __str__(self) -> str:
435
- s_left = "[" + str(self.left_time_bound) + ","
436
- s_right = str(self.right_time_bound) if not self.right_unbound else "inf"
437
- s0: str = s_left + s_right + "]" if not self.unbound else ""
438
- s: str = "( " + self.left_child.__str__() + " until" + s0 + " " + self.right_child.__str__() + " )"
439
- return s
440
-
441
- def time_depth(self) -> int:
442
- sum_children_depth: int = self.left_child.time_depth() + self.right_child.time_depth()
443
- if self.unbound:
444
- return sum_children_depth
445
- elif self.right_unbound:
446
- return sum_children_depth + self.left_time_bound
447
- else:
448
- # diff = torch.le(torch.tensor([self.left_time_bound]), 0).float()
449
- return sum_children_depth + self.right_time_bound - 1
450
- # (self.right_time_bound - self.left_time_bound + 1) - diff
451
-
452
- def _boolean(self, x: Tensor) -> Tensor:
453
- if self.unbound:
454
- z1: Tensor = self.left_child._boolean(x)
455
- z2: Tensor = self.right_child._boolean(x)
456
- size: int = min(z1.size()[2], z2.size()[2])
457
- z1: Tensor = z1[:, :, :size]
458
- z2: Tensor = z2[:, :, :size]
459
- z1_rep = torch.repeat_interleave(z1.unsqueeze(2), z1.unsqueeze(2).shape[-1], 2)
460
- z1_tril = torch.tril(z1_rep.transpose(2, 3), diagonal=-1)
461
- z1_triu = torch.triu(z1_rep)
462
- z1_def = torch.cummin(z1_tril + z1_triu, dim=3)[0]
463
-
464
- z2_rep = torch.repeat_interleave(z2.unsqueeze(2), z2.unsqueeze(2).shape[-1], 2)
465
- z2_tril = torch.tril(z2_rep.transpose(2, 3), diagonal=-1)
466
- z2_triu = torch.triu(z2_rep)
467
- z2_def = z2_tril + z2_triu
468
- z: Tensor = torch.max(torch.min(torch.cat([z1_def.unsqueeze(-1), z2_def.unsqueeze(-1)], dim=-1), dim=-1)[0],
469
- dim=-1)[0]
470
- elif self.right_unbound:
471
- timed_until: Node = And(Globally(self.left_child, left_time_bound=0, right_time_bound=self.left_time_bound),
472
- And(Eventually(self.right_child, right_unbound=True,
473
- left_time_bound=self.left_time_bound),
474
- Eventually(Until(self.left_child, self.right_child, unbound=True),
475
- left_time_bound=self.left_time_bound, right_unbound=True)))
476
- z: Tensor = timed_until._boolean(x)
477
- else:
478
- timed_until: Node = And(Globally(self.left_child, left_time_bound=0, right_time_bound=self.left_time_bound),
479
- And(Eventually(self.right_child, left_time_bound=self.left_time_bound,
480
- right_time_bound=self.right_time_bound - 1),
481
- Eventually(Until(self.left_child, self.right_child, unbound=True),
482
- left_time_bound=self.left_time_bound, right_unbound=True)))
483
- z: Tensor = timed_until._boolean(x)
484
- return z
485
-
486
- def _quantitative(self, x: Tensor, normalize: bool = False) -> Tensor:
487
- if self.unbound:
488
- z1: Tensor = self.left_child._quantitative(x, normalize)
489
- z2: Tensor = self.right_child._quantitative(x, normalize)
490
- size: int = min(z1.size()[2], z2.size()[2])
491
- z1: Tensor = z1[:, :, :size]
492
- z2: Tensor = z2[:, :, :size]
493
-
494
- # z1_rep = torch.repeat_interleave(z1.unsqueeze(2), z1.unsqueeze(2).shape[-1], 2)
495
- # z1_tril = torch.tril(z1_rep.transpose(2, 3), diagonal=-1)
496
- # z1_triu = torch.triu(z1_rep)
497
- # z1_def = torch.cummin(z1_tril + z1_triu, dim=3)[0]
498
-
499
- # z2_rep = torch.repeat_interleave(z2.unsqueeze(2), z2.unsqueeze(2).shape[-1], 2)
500
- # z2_tril = torch.tril(z2_rep.transpose(2, 3), diagonal=-1)
501
- # z2_triu = torch.triu(z2_rep)
502
- # z2_def = z2_tril + z2_triu
503
- # z: Tensor = torch.max(torch.min(torch.cat([z1_def.unsqueeze(-1), z2_def.unsqueeze(-1)], dim=-1), dim=-1)[0],
504
- # dim=-1)[0]
505
- z: Tensor = torch.cat([torch.max(torch.min(
506
- torch.cat([torch.cummin(z1[:, :, t:].unsqueeze(-1), dim=2)[0], z2[:, :, t:].unsqueeze(-1)], dim=-1),
507
- dim=-1)[0], dim=2, keepdim=True)[0] for t in range(size)], dim=2)
508
- elif self.right_unbound:
509
- timed_until: Node = And(Globally(self.left_child, left_time_bound=0, right_time_bound=self.left_time_bound),
510
- And(Eventually(self.right_child, right_unbound=True,
511
- left_time_bound=self.left_time_bound),
512
- Eventually(Until(self.left_child, self.right_child, unbound=True),
513
- left_time_bound=self.left_time_bound, right_unbound=True)))
514
- z: Tensor = timed_until._quantitative(x, normalize=normalize)
515
- else:
516
- timed_until: Node = And(Globally(self.left_child, left_time_bound=0, right_time_bound=self.left_time_bound),
517
- And(Eventually(self.right_child, left_time_bound=self.left_time_bound,
518
- right_time_bound=self.right_time_bound-1),
519
- Eventually(Until(self.left_child, self.right_child, unbound=True),
520
- left_time_bound=self.left_time_bound, right_unbound=True)))
521
- z: Tensor = timed_until._quantitative(x, normalize=normalize)
522
- return z