forked from informalsystems/modelator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
modelator_api.py.example
387 lines (300 loc) · 10.2 KB
/
modelator_api.py.example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# Modelator API
# (WIP) API documentation, not a real interface
class Model:
"""
Model is the main interface class for working with models
A valid model must have both init and next predicates defined.
"""
@classmethod
def parse_file(cls, file, init="Init", next="Next"):
"""
Attempts to parse the given file, and returns the parsed model on success.
Raises an exception on error.
m = Model.parse_file('HourClock.tla')
"""
pass
def typecheck(self):
""" """
pass
def instantiate(self, constants):
"""
Instantiates model constants, if any. The parameter should be a dict.
m.instantiate({ 'N_PROC': 2, 'USERS': { 'alice', 'bob' } })
"""
pass
def sample(self, examples=None, constants=None):
"""
Sample `examples` from the model;
`examples` can be either a name of a single operator, or a list of operator names.
When omitted, all operators with names prefixed/suffixed with `Ex` or `Example` will be collected.
All model constants should be instantiated, either by
a previous `instantiate()` call, or via the `constants` argument.
Any constant given via `constants` overrides the same constant provided via `instantiate()`.
Returns ModelResult summarizing the sampling; it can be also accessed via `last_sample()`.
result = m.sample(['ExThreeHours', 'ExFullRun'])
for op in result.successful():
print(f"Example for {op}: {result.traces(op)[0]}")
for op in result.unsuccessful():
print(f"Sampling for {op} failed")
"""
pass
def last_sample(self):
"""
returns the result of the last application of `sample()`
(chronological order)
"""
pass
def all_samples(self):
"""
returns the list of results from all applications of `sample()`
"""
pass
def check(
self, invariants=None, constants=None, checker="apalache", checker_params=None
):
"""
Check `invariants` from the model;
`invariants` can be either a name of a single operator, or a list of operator names.
When omitted, all operators with names prefixed/suffixed with `Inv` or `Invariant` will be collected.
All model constants should be instantiated, either by
a previous `instantiate()` call, or via the `constants` argument.
Any constant given via `constants` overrides the same constant provided via `instantiate()`.
Returns ModelResult summarizing the checking; it can be also accessed via `last_check()`.
result = m.check(['InvNoOverflow', 'InvNoUnderflow'])
for op in result.successful():
print(f"Invariant checking for {op} succeeded")
for op in result.unsuccessful():
print(f"Invariant {op} violated; counterexample: {result.traces(op)[0]}")
"""
pass
def last_check(self):
"""
returns the result of the last application of `check()`
"""
pass
def all_checks(self):
"""
returns the list of results from all applications of `check()`
(chronological order)
"""
pass
def add_monitor(self, monitor):
"""
Add a monitor to track actions on this model
"""
pass
def remove_monitor(self, monitor):
"""
Removes a previously added monitor
"""
pass
def start_html_monitor(self, filename):
"""
Start monitoring model actions in HTML file.
"""
def stop_html_monitor(self, filename):
"""
Stop monitoring model actions in HTML file.
"""
def start_markdown_monitor(self, filename):
"""
Start monitoring model actions in Markdown file.
"""
def stop_markdown_monitor(self, filename):
"""
Stop monitoring model actions in Markdown file.
"""
class ModelResult:
"""
A result of running some action on a set of model operators
Different actions can have different outcomes:
- example sampling is successful when a trace satisfying it can be produced.
- invariant checking is successful when a trace violating it can't be produced.
"""
def model(self):
"""
returns the model on which the action was executed
"""
pass
def time(self):
"""
Time when the action was executed
"""
pass
def inprogress(self):
"""
Returns the list of operators for which the action has not completed yet
"""
pass
def successful(self):
"""
Returns the list of operators for which the action was successful
"""
pass
def unsuccessful(self):
"""
Returns the list of operators for which the action was unsuccessful
"""
pass
def traces(self, operator):
"""
Traces associated with the result of applying an action to the operator, if available.
Availability depends on action type, and its success for the operator.
If available, at least one trace is guaranteed to exist.
"""
pass
def progress(self, operator):
"""
returns a progress measure between 0 and 1 (inclusive)
for any operator on which the action is executed.
"""
pass
class Trace:
"""
Trace is a sequence of model states, each state assigning values to state variables.
"""
def as_tla(self):
"""
Returns a list with trace states.
Each state is represented by a dict from variable name to a its value in TLA+ format.
"""
def as_tla_diff(self):
"""
Returns a list, where each element is a diff between trace states.
Thus, it is always 1 element less than the number of states in the trace.
Each diff element is a dict from a changed state component
to the tuple with the old and the new value of this component.
"""
pass
from modelator import *
m = Model.parse_file("HourClock.tla")
m.instantiate({"N_PROC": 2, "USERS": {"alice", "bob"}})
result = m.sample(["ExThreeHours", "ExFullRun"])
for op in result.successful():
print(f"Example for {op}: {result.traces(op)[0].as_tla()}")
for op in result.unsuccessful():
print(f"Sampling for {op} failed")
result = m.check(
["InvNoOverflow", "InvNoUnderflow"], constants={"USERS": {"alice", "bob", "carol"}}
)
for op in result.successful():
print(f"Invariant checking for {op} succeeded")
for op in result.unsuccessful():
print(f"Invariant {op} violated; counterexample: {result.traces(op)[0]}")
class ModelMonitor:
"""
Monitor for actions done with the model
"""
def on_parse_start(self, res: ModelResult):
pass
def on_parse_finish(self, res: ModelResult):
pass
def on_sample_start(self, res: ModelResult):
pass
def on_sample_update(self, res: ModelResult):
pass
def on_sample_finish(self, res: ModelResult):
pass
def on_check_start(self, res: ModelResult):
pass
def on_check_update(self, res: ModelResult):
pass
def on_check_finish(self, res: ModelResult):
pass
class HtmlModelMonitor(ModelMonitor):
"""
A model monitor that stores all model action updates to HTML file
"""
def __init__(self, filename):
super().__init__()
class MarkdownModelMonitor(ModelMonitor):
"""
A model monitor that stores all model action updates to Markdown file
"""
def __init__(self, filename):
super().__init__()
class ModelShell(Model):
"""
Model class from modelator.shell namespace.
Implements all functions from Model, but with the following differences:
- each call is non-blocking, and returns immediately.
- callbacks can be registered for any action
- on (partial) completion of any action, corresponding callbacks will be triggered.
Additionally, `auto` methods are provided, which are auto-triggered on model updates.
"""
def auto_sample(self, examples=None, constants=None):
"""
Same as `sample`, but executed automatically on every model update.
"""
def auto_check(self, invariants=None, constants=None):
"""
Same as `check`, but executed automatically on every model update.
"""
# top-level functions from modelator.shell
def auto_parse_file(file, init="Init", next="Next"):
"""
Same as `parse_file`, but it attempts to parse the file whenever it changes on the disk.
If the parsing is successful, further `auto` actions will be triggered.
"""
pass
def auto_evaluate_file(
file, init="Init", next="Next", examples=None, invariants=None, constants=None
):
"""
Combines in one call `auto_parse_file`, `auto_sample`, `auto_check`.
"""
pass
def stop_auto_file(file):
"""
Stop monitoring started previously by `auto_parse_file` or `auto_evaluate_file`.
"""
pass
def start_shell_monitor():
"""
Takes control of the terminal, and overlays the status information
on any model actions in it.
"""
pass
def stop_shell_monitor():
"""
Releases control of the terminal, and removes all status overlays.
"""
pass
def start_html_monitor(filename):
"""
Starts monitoring status of all model actions in HTML file
"""
pass
def stop_html_monitor(filename):
"""
Stops monitoring status of model actions in HTML file
"""
pass
def start_markdown_monitor(filename):
"""
Starts monitoring status of all model actions in Markdown file
"""
pass
def stop_markdown_monitor(filename):
"""
Stops monitoring status of model actions in Markdown file
"""
pass
from modelator.shell import *
start_modelator_shell()
start_html_monitor("status.html")
auto_evaluate_file("HourClock.tla")
class ModelExperimental(Model):
"""
Optional, experimental methods that might be added later
"""
def modules(self):
"""
returns a dictionary from module names to their content
"""
pass
def main_module(self):
"""
returns the name of the main module
"""
pass