umaudemc 0.16.0__py3-none-any.whl → 0.17.1__py3-none-any.whl
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.
- umaudemc/__init__.py +1 -1
- umaudemc/__main__.py +1 -0
- umaudemc/command/scheck.py +5 -5
- umaudemc/command/sworker.py +18 -2
- umaudemc/distributed.py +54 -13
- umaudemc/quatex.py +162 -60
- umaudemc/simulators.py +46 -8
- umaudemc/statistical.py +50 -25
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/METADATA +1 -1
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/RECORD +14 -14
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/WHEEL +1 -1
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/entry_points.txt +0 -0
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/licenses/LICENSE +0 -0
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.1.dist-info}/top_level.txt +0 -0
umaudemc/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.
|
|
1
|
+
__version__ = '0.17.0'
|
umaudemc/__main__.py
CHANGED
umaudemc/command/scheck.py
CHANGED
|
@@ -19,16 +19,16 @@ def show_results(program, nsims, qdata):
|
|
|
19
19
|
qdata_it = iter(qdata)
|
|
20
20
|
q = next(qdata_it, None)
|
|
21
21
|
|
|
22
|
-
for k,
|
|
22
|
+
for k, query in enumerate(program.queries):
|
|
23
23
|
# Print the query name and location only if there are many
|
|
24
24
|
if program.nqueries > 1:
|
|
25
25
|
# If the number of simulation is lower for this query
|
|
26
26
|
sim_detail = f' ({q.n} simulations)' if q.n != nsims else ''
|
|
27
27
|
|
|
28
|
-
print(f'Query {k + 1} ({
|
|
28
|
+
print(f'Query {k + 1} ({query.filename}:{query.line}:{query.column}){sim_detail}')
|
|
29
29
|
|
|
30
30
|
# For parametric queries, we show the result for every value
|
|
31
|
-
var =
|
|
31
|
+
var = query.parameters[0] if query.parameters else None
|
|
32
32
|
|
|
33
33
|
while q and q.query == k:
|
|
34
34
|
if var:
|
|
@@ -93,10 +93,10 @@ def plot_results(program, qdata):
|
|
|
93
93
|
return
|
|
94
94
|
|
|
95
95
|
for k, xs, ys, rs in results:
|
|
96
|
-
|
|
96
|
+
query = program.queries[k]
|
|
97
97
|
|
|
98
98
|
# Plot the mean
|
|
99
|
-
p = plt.plot(xs, ys, label=f'{line}:{column}')
|
|
99
|
+
p = plt.plot(xs, ys, label=f'{query.line}:{query.column}')
|
|
100
100
|
# Plot the confidence interval
|
|
101
101
|
plt.fill_between(xs, [y - r for y, r in zip(ys, rs)],
|
|
102
102
|
[y + r for y, r in zip(ys, rs)],
|
umaudemc/command/sworker.py
CHANGED
|
@@ -96,13 +96,16 @@ class Worker:
|
|
|
96
96
|
# (delta, its second argument, does not matter because
|
|
97
97
|
# convergence is not evaluated by the worker)
|
|
98
98
|
qdata = [QueryData(k, 1.0, idict)
|
|
99
|
-
for k, qinfo in enumerate(program.
|
|
100
|
-
for idict in make_parameter_dicts(qinfo
|
|
99
|
+
for k, qinfo in enumerate(program.queries)
|
|
100
|
+
for idict, _ in make_parameter_dicts(qinfo, 1.0)]
|
|
101
101
|
|
|
102
|
+
# Compact arrays are used so that they can be sent through the socket
|
|
102
103
|
sums = array('d', [0.0] * len(qdata))
|
|
103
104
|
sum_sq = array('d', [0.0] * len(qdata))
|
|
104
105
|
counts = array('i', [0] * len(qdata))
|
|
105
106
|
|
|
107
|
+
converged = array('i') # to store indices of converged queries
|
|
108
|
+
|
|
106
109
|
while True:
|
|
107
110
|
|
|
108
111
|
for _ in range(block):
|
|
@@ -120,14 +123,27 @@ class Worker:
|
|
|
120
123
|
# Check whether to continue
|
|
121
124
|
answer = conn.recv(1)
|
|
122
125
|
|
|
126
|
+
# Stop command
|
|
123
127
|
if answer == b's':
|
|
124
128
|
print('Done')
|
|
125
129
|
return
|
|
126
130
|
|
|
131
|
+
# Partial convergence
|
|
132
|
+
elif answer == b'p':
|
|
133
|
+
mcount = int.from_bytes(conn.recv(4), 'big')
|
|
134
|
+
# Safety check
|
|
135
|
+
if mcount <= len(qdata):
|
|
136
|
+
converged.frombytes(conn.recv(4 * mcount))
|
|
137
|
+
# Set the converged queries to avoid recomputing them
|
|
138
|
+
for index in converged:
|
|
139
|
+
qdata[index].converged = True
|
|
140
|
+
del converged[:]
|
|
141
|
+
|
|
127
142
|
elif answer != b'c':
|
|
128
143
|
usermsgs.print_error(f'Unknown command {answer.decode()}. Stopping.')
|
|
129
144
|
return
|
|
130
145
|
|
|
146
|
+
# Reset the accumulators for the next round
|
|
131
147
|
for k in range(len(qdata)):
|
|
132
148
|
sums[k] = 0
|
|
133
149
|
sum_sq[k] = 0
|
umaudemc/distributed.py
CHANGED
|
@@ -225,7 +225,7 @@ def setup_workers(args, initial_data, dspec, constants, seen_files, stack):
|
|
|
225
225
|
input_data['block'] = block_size # if specified
|
|
226
226
|
|
|
227
227
|
input_data = json.dumps(input_data).encode()
|
|
228
|
-
sock.sendall(len(input_data).to_bytes(4) + input_data)
|
|
228
|
+
sock.sendall(len(input_data).to_bytes(4, 'big') + input_data)
|
|
229
229
|
|
|
230
230
|
# Send the relevant files
|
|
231
231
|
with sock.makefile('wb', buffering=0) as fobj:
|
|
@@ -249,6 +249,17 @@ def setup_workers(args, initial_data, dspec, constants, seen_files, stack):
|
|
|
249
249
|
return sockets
|
|
250
250
|
|
|
251
251
|
|
|
252
|
+
class WorkerData:
|
|
253
|
+
"""Relevant data for the worker"""
|
|
254
|
+
|
|
255
|
+
__attrs__ = ('index', 'block', 'name')
|
|
256
|
+
|
|
257
|
+
def __init__(self, index, block, data):
|
|
258
|
+
self.index = index
|
|
259
|
+
self.block = data.get('block', block)
|
|
260
|
+
self.name = data.get('name')
|
|
261
|
+
|
|
262
|
+
|
|
252
263
|
def distributed_check(args, initial_data, min_sim, max_sim, program, constants, seen_files):
|
|
253
264
|
"""Distributed statistical model checking"""
|
|
254
265
|
|
|
@@ -269,20 +280,27 @@ def distributed_check(args, initial_data, min_sim, max_sim, program, constants,
|
|
|
269
280
|
# Use a selector to wait for updates from any worker
|
|
270
281
|
selector = selectors.DefaultSelector()
|
|
271
282
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
sock.
|
|
283
|
+
# Use a selector to be notified of the worker answers
|
|
284
|
+
for k, (sock, data) in enumerate(zip(sockets, dspec['workers'])):
|
|
285
|
+
selector.register(sock, selectors.EVENT_READ, data=WorkerData(k, args.block, data))
|
|
286
|
+
sock.send(b'c') # continue command
|
|
275
287
|
|
|
276
288
|
buffer = array('d')
|
|
277
289
|
ibuffer = array('i')
|
|
278
290
|
|
|
279
291
|
# Query data
|
|
280
|
-
qdata = [QueryData(k,
|
|
281
|
-
for k, qinfo in enumerate(program.
|
|
282
|
-
for idict in make_parameter_dicts(qinfo
|
|
292
|
+
qdata = [QueryData(k, delta, idict)
|
|
293
|
+
for k, qinfo in enumerate(program.queries)
|
|
294
|
+
for idict, delta in make_parameter_dicts(qinfo, args.delta)]
|
|
283
295
|
nqueries = len(qdata)
|
|
284
296
|
num_sims = 0
|
|
285
297
|
|
|
298
|
+
# In order to tell the workers which queries have converged, we keep
|
|
299
|
+
# a list of indices of converged queries in cronological order and
|
|
300
|
+
# a list with the next index to be transmitted to each worker
|
|
301
|
+
converged_queries = array('i')
|
|
302
|
+
next_to_tell = [0] * len(sockets)
|
|
303
|
+
|
|
286
304
|
quantile = get_quantile_func()
|
|
287
305
|
|
|
288
306
|
while sockets:
|
|
@@ -294,35 +312,58 @@ def distributed_check(args, initial_data, min_sim, max_sim, program, constants,
|
|
|
294
312
|
|
|
295
313
|
answer = sock.recv(1)
|
|
296
314
|
|
|
315
|
+
# Block finished
|
|
297
316
|
if answer == b'b':
|
|
317
|
+
# The message is the concatenation of three arrays
|
|
318
|
+
# (sum, sum_sq, and counts) of nqueries elements each.
|
|
319
|
+
# sum and sum_sq contain 64-bit floating-point numbers
|
|
320
|
+
# and counts contains 32-bit integer numbers
|
|
298
321
|
data = sock.recv(24 * nqueries)
|
|
299
322
|
buffer.frombytes(data[:16 * nqueries])
|
|
300
323
|
ibuffer.frombytes(data[16 * nqueries:])
|
|
301
324
|
|
|
325
|
+
# Some queries may have not been evaluated because
|
|
326
|
+
# they converged, but it is easier to sum their zeros
|
|
302
327
|
for k in range(nqueries):
|
|
303
328
|
qdata[k].sum += buffer[k]
|
|
304
329
|
qdata[k].sum_sq += buffer[nqueries + k]
|
|
305
330
|
qdata[k].n += ibuffer[k]
|
|
306
331
|
|
|
307
|
-
num_sims += key.data
|
|
332
|
+
num_sims += key.data.block
|
|
308
333
|
|
|
309
334
|
del buffer[:]
|
|
310
335
|
del ibuffer[:]
|
|
311
|
-
finished.append(key
|
|
336
|
+
finished.append(key)
|
|
312
337
|
|
|
313
338
|
else:
|
|
314
|
-
usermsgs.print_error(f'Server {key.data
|
|
339
|
+
usermsgs.print_error(f'Server {key.data.name} disconnected or misbehaving')
|
|
315
340
|
selector.unregister(key.fileobj)
|
|
316
341
|
sockets.remove(key.fileobj)
|
|
317
342
|
|
|
318
343
|
# Check whether the simulation has converged
|
|
319
|
-
converged = check_interval(qdata, num_sims, min_sim, args.alpha, quantile, args.verbose)
|
|
344
|
+
converged, which = check_interval(qdata, num_sims, min_sim, args.alpha, quantile, args.verbose)
|
|
345
|
+
|
|
346
|
+
# More converged queries
|
|
347
|
+
if which:
|
|
348
|
+
converged_queries.extend(which)
|
|
320
349
|
|
|
321
350
|
if converged or max_sim and num_sims >= max_sim:
|
|
322
351
|
break
|
|
323
352
|
|
|
324
|
-
for
|
|
325
|
-
|
|
353
|
+
for key in finished:
|
|
354
|
+
index = key.data.index
|
|
355
|
+
|
|
356
|
+
# Transmit which queries have converged
|
|
357
|
+
if next_to_tell[index] != len(converged_queries):
|
|
358
|
+
# We send p followed by the number of indices and then the indices
|
|
359
|
+
key.fileobj.send(b'p'
|
|
360
|
+
+ (len(converged_queries) - next_to_tell[index]).to_bytes(4, 'big')
|
|
361
|
+
+ converged_queries[next_to_tell[index]:].tobytes())
|
|
362
|
+
next_to_tell[index] = len(converged_queries)
|
|
363
|
+
|
|
364
|
+
# Continue without change
|
|
365
|
+
else:
|
|
366
|
+
key.fileobj.send(b'c')
|
|
326
367
|
|
|
327
368
|
finished.clear()
|
|
328
369
|
|
umaudemc/quatex.py
CHANGED
|
@@ -8,6 +8,19 @@ import os
|
|
|
8
8
|
from . import usermsgs
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
class QuaTExQuery:
|
|
12
|
+
"""QuaTeX query information"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, fname, line, column, expr, parameters, delta):
|
|
15
|
+
self.filename = fname # file cointing the query
|
|
16
|
+
self.line = line # query location
|
|
17
|
+
self.column = column
|
|
18
|
+
self.parameters = parameters # parameters as (variable, start, step, end)
|
|
19
|
+
|
|
20
|
+
self.expr = expr # query expression (only needed for compilation)
|
|
21
|
+
self.delta = delta # delta value
|
|
22
|
+
|
|
23
|
+
|
|
11
24
|
class QuaTExProgram:
|
|
12
25
|
"""Compiled QuaTEx program"""
|
|
13
26
|
|
|
@@ -23,7 +36,7 @@ class QuaTExProgram:
|
|
|
23
36
|
self.nqueries = len(slots) - ndefs
|
|
24
37
|
|
|
25
38
|
# Query information (file name, line, column, and parameters)
|
|
26
|
-
self.
|
|
39
|
+
self.queries = qinfo
|
|
27
40
|
|
|
28
41
|
|
|
29
42
|
class QuaTExLexer:
|
|
@@ -171,6 +184,7 @@ class QuaTExLexer:
|
|
|
171
184
|
self.ltype = self.LT_STRING
|
|
172
185
|
self._capture_string()
|
|
173
186
|
|
|
187
|
+
# Names cannot start with a number
|
|
174
188
|
elif c.isalpha() or c == '$':
|
|
175
189
|
self.ltype = self.LT_NAME
|
|
176
190
|
self._capture(self._is_name)
|
|
@@ -211,9 +225,9 @@ class QuaTExLexer:
|
|
|
211
225
|
class QuaTExParser:
|
|
212
226
|
"""Parser for QuaTEx"""
|
|
213
227
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
PS_IF_COND = 0 # condition
|
|
229
|
+
PS_IF_THEN = 1 # true branch
|
|
230
|
+
PS_IF_ELSE = 2 # negative branch
|
|
217
231
|
PS_PAREN = 3 # parenthesis
|
|
218
232
|
PS_ARITH = 4 # completing an arithmetic expression
|
|
219
233
|
PS_CALLARGS = 5 # call arguments
|
|
@@ -223,6 +237,7 @@ class QuaTExParser:
|
|
|
223
237
|
BINOPS_PREC = (4, 4, 3, 3, 3, 11, 12, 7, 7, 6, 6, 6, 6)
|
|
224
238
|
BINOPS_AST = (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod, ast.And, ast.Or, ast.Eq,
|
|
225
239
|
ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)
|
|
240
|
+
# Kind of binary operator (0 = BinOp, 1 = BoolOp, 2 = Compare)
|
|
226
241
|
BINOPS_CMP = (0, ) * 5 + (1, ) * 2 + (2, ) * 6
|
|
227
242
|
# Unary operator and its precedence (as in C)
|
|
228
243
|
UNARY_OPS = ('!', )
|
|
@@ -238,13 +253,14 @@ class QuaTExParser:
|
|
|
238
253
|
self.pending_lexers = []
|
|
239
254
|
self.seen_files = set() if filename.startswith('<') else {os.path.realpath(filename)}
|
|
240
255
|
|
|
241
|
-
#
|
|
242
|
-
self.fvars = []
|
|
243
|
-
# Whether the variables that may occur
|
|
244
|
-
# in an expression are known
|
|
256
|
+
# Whether the variables that may occur in an expression are known
|
|
245
257
|
self.known_vars = True
|
|
258
|
+
# Parameters of the current function if self.known_vars
|
|
259
|
+
# or variables found in the last processed expression otherwise
|
|
260
|
+
self.fvars = []
|
|
246
261
|
# Constants defined outside
|
|
247
262
|
self.constants = {} if constants is None else constants
|
|
263
|
+
self.pending_constants = []
|
|
248
264
|
|
|
249
265
|
# State stack for parsing expressions
|
|
250
266
|
self.stack = []
|
|
@@ -253,7 +269,7 @@ class QuaTExParser:
|
|
|
253
269
|
# Whether parsing errors have been encountered
|
|
254
270
|
self.ok = True
|
|
255
271
|
|
|
256
|
-
# Compilation slot indices for each
|
|
272
|
+
# Compilation slot indices for each element
|
|
257
273
|
self.fslots = {}
|
|
258
274
|
self.calls = []
|
|
259
275
|
self.observations = []
|
|
@@ -284,13 +300,29 @@ class QuaTExParser:
|
|
|
284
300
|
|
|
285
301
|
return True
|
|
286
302
|
|
|
303
|
+
def _expect_any(self, *options):
|
|
304
|
+
"""Check whether any of the given strings is the next token"""
|
|
305
|
+
|
|
306
|
+
atoken = self.lexer.get_token()
|
|
307
|
+
options_str = ', '.join(f'"{opt}"' for opt in options)
|
|
308
|
+
|
|
309
|
+
if atoken is None:
|
|
310
|
+
self._eprint(f'unexpected end of file where any of {options_str} is required.')
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
if atoken not in options:
|
|
314
|
+
self._eprint(f'unexpected token "{atoken}" where any of {options_str} is required.')
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
return atoken
|
|
318
|
+
|
|
287
319
|
def _in_state(self, state):
|
|
288
320
|
"""Check whether the parser is in the given state"""
|
|
289
321
|
|
|
290
322
|
return self.stack and self.stack[-1] == state
|
|
291
323
|
|
|
292
324
|
def _next_token(self):
|
|
293
|
-
"""Get the next token
|
|
325
|
+
"""Get the next token dealing with file exhaustion"""
|
|
294
326
|
|
|
295
327
|
token = self.lexer.get_token()
|
|
296
328
|
|
|
@@ -332,40 +364,89 @@ class QuaTExParser:
|
|
|
332
364
|
self._eprint(f'unexpected token "{var_name}" where a variable name is required.')
|
|
333
365
|
return (None, ) * 3
|
|
334
366
|
|
|
367
|
+
if not self._expect(','):
|
|
368
|
+
return (None, ) * 3
|
|
369
|
+
|
|
335
370
|
# Parameter specification (name, initial value, step, last value)
|
|
336
371
|
spec = [var_name]
|
|
337
372
|
|
|
338
|
-
#
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
373
|
+
# Make a copy of the expression variables, since we continue in
|
|
374
|
+
# known variables mode with no variables allowed
|
|
375
|
+
expr_vars = tuple(self.fvars)
|
|
376
|
+
self.fvars.clear()
|
|
342
377
|
|
|
343
|
-
|
|
378
|
+
# Parse the initial value, step, and last value
|
|
379
|
+
for k in range(3):
|
|
380
|
+
expr = self._parse_constexpr(',' if k < 2 else ')')
|
|
344
381
|
|
|
345
|
-
if
|
|
346
|
-
self._eprint(f'unexpected token "{token}" where a number is required.')
|
|
382
|
+
if expr is None:
|
|
347
383
|
return (None, ) * 3
|
|
348
384
|
|
|
349
|
-
spec.append(float(
|
|
350
|
-
|
|
351
|
-
# Check the closing parenthesis
|
|
352
|
-
if not self._expect(')'):
|
|
353
|
-
return (None, ) * 3
|
|
385
|
+
spec.append(float(expr))
|
|
354
386
|
|
|
355
387
|
# Check whether the variables in the expressions are the parameter
|
|
356
|
-
for var, line, column in
|
|
388
|
+
for var, line, column in expr_vars:
|
|
357
389
|
if var != var_name:
|
|
358
390
|
self._eprint(f'unknown variable "{var}".', line=line, column=column)
|
|
359
391
|
self.ok = False
|
|
360
392
|
|
|
393
|
+
return tuple(spec)
|
|
394
|
+
|
|
395
|
+
def _parse_delta(self, parameter):
|
|
396
|
+
"""Parse the delta annotation"""
|
|
397
|
+
|
|
398
|
+
if not self._expect('delta', '='):
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
# Only the parameter can appear as free variable in the expression
|
|
361
402
|
self.fvars.clear()
|
|
362
403
|
|
|
363
|
-
|
|
404
|
+
if parameter:
|
|
405
|
+
self.fvars.append(parameter[0])
|
|
406
|
+
|
|
407
|
+
if (delta := self._parse_expr(';', constexpr=True)) is None:
|
|
408
|
+
return None
|
|
364
409
|
|
|
365
|
-
|
|
410
|
+
# Prepare delta as a function on the parameter (if any)
|
|
411
|
+
args = [ast.arg(parameter[0])] if parameter else []
|
|
412
|
+
|
|
413
|
+
delta_fn = ast.Expression(ast.Lambda(ast.arguments(args=args), delta))
|
|
414
|
+
ast.fix_missing_locations(delta_fn)
|
|
415
|
+
|
|
416
|
+
return eval(compile(delta_fn, '<delta>', 'eval'), {}, {})
|
|
417
|
+
|
|
418
|
+
def _parse_constexpr(self, end_token):
|
|
419
|
+
"""Parse a constant expression as a number"""
|
|
420
|
+
|
|
421
|
+
# Soft errors, like unbound variables, are hard here because
|
|
422
|
+
# we run the code to obtain a value for the expression
|
|
423
|
+
old_ok, self.ok = self.ok, True
|
|
424
|
+
|
|
425
|
+
if (expr := self._parse_expr(end_token, constexpr=True)) is None:
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
elif not self.ok:
|
|
429
|
+
return 0.0 # dummy value to keep running
|
|
430
|
+
|
|
431
|
+
self.ok = old_ok
|
|
432
|
+
|
|
433
|
+
# Evaluate the expression
|
|
434
|
+
expr = ast.Expression(expr)
|
|
435
|
+
ast.fix_missing_locations(expr)
|
|
436
|
+
|
|
437
|
+
result = eval(compile(expr, self.lexer.filename, 'eval'), {}, {})
|
|
438
|
+
|
|
439
|
+
# Check whether it is a number
|
|
440
|
+
if not isinstance(result, (int, float)):
|
|
441
|
+
self._eprint(f'"{result}" obtained while a number is expected.')
|
|
442
|
+
return None
|
|
443
|
+
|
|
444
|
+
return result
|
|
445
|
+
|
|
446
|
+
def _parse_expr(self, end_token, constexpr=False):
|
|
366
447
|
"""Parse an expression"""
|
|
367
448
|
|
|
368
|
-
# Current expression
|
|
449
|
+
# Current expression (as a Python AST node)
|
|
369
450
|
current = None
|
|
370
451
|
# Number of nested conditions in the current position
|
|
371
452
|
inside_cond = 0
|
|
@@ -400,13 +481,13 @@ class QuaTExParser:
|
|
|
400
481
|
self._eprint('misplaced "if" keyword.')
|
|
401
482
|
return None
|
|
402
483
|
|
|
403
|
-
self.stack.append(self.
|
|
484
|
+
self.stack.append(self.PS_IF_COND)
|
|
404
485
|
arg_stack.append([])
|
|
405
486
|
inside_cond += 1
|
|
406
487
|
|
|
407
488
|
elif token == 'then':
|
|
408
|
-
if current and self._in_state(self.
|
|
409
|
-
self.stack[-1] = self.
|
|
489
|
+
if current and self._in_state(self.PS_IF_COND):
|
|
490
|
+
self.stack[-1] = self.PS_IF_THEN
|
|
410
491
|
arg_stack[-1].append(current)
|
|
411
492
|
current = None
|
|
412
493
|
inside_cond -= 1
|
|
@@ -415,8 +496,8 @@ class QuaTExParser:
|
|
|
415
496
|
return None
|
|
416
497
|
|
|
417
498
|
elif token == 'else':
|
|
418
|
-
if current and self._in_state(self.
|
|
419
|
-
self.stack[-1] = self.
|
|
499
|
+
if current and self._in_state(self.PS_IF_THEN):
|
|
500
|
+
self.stack[-1] = self.PS_IF_ELSE
|
|
420
501
|
arg_stack[-1].append(current)
|
|
421
502
|
current = None
|
|
422
503
|
else:
|
|
@@ -424,7 +505,7 @@ class QuaTExParser:
|
|
|
424
505
|
return None
|
|
425
506
|
|
|
426
507
|
elif token == 'fi':
|
|
427
|
-
if current and self._in_state(self.
|
|
508
|
+
if current and self._in_state(self.PS_IF_ELSE):
|
|
428
509
|
arg_stack[-1].append(current)
|
|
429
510
|
current = ast.IfExp(*arg_stack[-1])
|
|
430
511
|
arg_stack.pop()
|
|
@@ -442,6 +523,10 @@ class QuaTExParser:
|
|
|
442
523
|
self._eprint('the next operator # cannot be used in conditions or call arguments.')
|
|
443
524
|
self.ok = False
|
|
444
525
|
|
|
526
|
+
elif constexpr:
|
|
527
|
+
self._eprint('the next operator # cannot be used in constant contexts.')
|
|
528
|
+
self.ok = False
|
|
529
|
+
|
|
445
530
|
# A function call should follow
|
|
446
531
|
token = self.lexer.get_token()
|
|
447
532
|
line, column = self.lexer.sline, self.lexer.scolumn
|
|
@@ -455,7 +540,8 @@ class QuaTExParser:
|
|
|
455
540
|
inside_next = True
|
|
456
541
|
call_name, call_line, call_column = token, line, column
|
|
457
542
|
|
|
458
|
-
|
|
543
|
+
# discard is a soft keyword
|
|
544
|
+
elif token == 'discard' and not (inside_cond or call_name or constexpr):
|
|
459
545
|
if current:
|
|
460
546
|
self._eprint('misplaced discard keyword.')
|
|
461
547
|
return None
|
|
@@ -491,10 +577,15 @@ class QuaTExParser:
|
|
|
491
577
|
self._eprint(f'argument missing after a comma in a call to "{call_name}".')
|
|
492
578
|
self.ok = False
|
|
493
579
|
|
|
580
|
+
# Call are internally represented as tuples that indicate whether
|
|
581
|
+
# the call is preceded by next (i.e. to be evaluated after a step)
|
|
582
|
+
# and which code slot contains the callee definition
|
|
494
583
|
slot = self.fslots.setdefault(call_name, len(self.fslots))
|
|
495
584
|
current = ast.Tuple([ast.Constant(inside_next), ast.Constant(slot), *args], ast.Load())
|
|
496
|
-
|
|
497
|
-
|
|
585
|
+
# We keep the source location for error reporting (using the attributes
|
|
586
|
+
# lineno and col_offset of the AST node only here is not possible)
|
|
587
|
+
current.custom_loc = dict(fname=self.lexer.filename, line=call_line, column=call_column)
|
|
588
|
+
self.calls.append((call_name, self.lexer.filename, call_line, call_column, len(args)))
|
|
498
589
|
inside_next = False
|
|
499
590
|
call_name = None
|
|
500
591
|
|
|
@@ -549,6 +640,10 @@ class QuaTExParser:
|
|
|
549
640
|
self._eprint(f'"{token}" is called in a condition or call argument, but this is not allowed.')
|
|
550
641
|
return None
|
|
551
642
|
|
|
643
|
+
if constexpr:
|
|
644
|
+
self._eprint(f'"{token}" is called in a constant context, but calls are not allowed.')
|
|
645
|
+
return None
|
|
646
|
+
|
|
552
647
|
self.stack.append(self.PS_CALLARGS)
|
|
553
648
|
arg_stack.append([])
|
|
554
649
|
call_name = token
|
|
@@ -558,17 +653,16 @@ class QuaTExParser:
|
|
|
558
653
|
else:
|
|
559
654
|
current = ast.Name(token, ast.Load())
|
|
560
655
|
|
|
561
|
-
|
|
656
|
+
# Constants (from the command line) start with $, but variables can also
|
|
657
|
+
if token.startswith('$') and (value := self.constants.get(token[1:])) is not None:
|
|
658
|
+
current = ast.Constant(value)
|
|
659
|
+
|
|
660
|
+
elif not self.known_vars:
|
|
562
661
|
self.fvars.append((token, line, column))
|
|
563
662
|
|
|
564
663
|
elif token not in self.fvars:
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
current = ast.Constant(value)
|
|
568
|
-
|
|
569
|
-
else:
|
|
570
|
-
self._eprint(f'unknown variable "{token}".', line=line, column=column)
|
|
571
|
-
self.ok = False
|
|
664
|
+
self._eprint(f'unknown variable "{token}".', line=line, column=column)
|
|
665
|
+
self.ok = False
|
|
572
666
|
|
|
573
667
|
|
|
574
668
|
# We continue with the peeked token
|
|
@@ -694,22 +788,29 @@ class QuaTExParser:
|
|
|
694
788
|
if parameter:
|
|
695
789
|
self.known_vars = False
|
|
696
790
|
|
|
697
|
-
expr
|
|
791
|
+
if not (expr := self._parse_expr(']')):
|
|
792
|
+
return False
|
|
698
793
|
|
|
699
794
|
self.known_vars = True
|
|
700
795
|
|
|
701
796
|
# Parse parameter specification in parametric queries
|
|
702
797
|
parameter = self._parse_parameter() if parameter else None
|
|
798
|
+
delta = None
|
|
703
799
|
|
|
704
|
-
|
|
800
|
+
# Check whether there is a "with delta = <number>" prefix
|
|
801
|
+
if (token := self._expect_any('with', ';')) is None:
|
|
705
802
|
return False
|
|
706
803
|
|
|
804
|
+
if token == 'with':
|
|
805
|
+
if (delta := self._parse_delta(parameter)) is None:
|
|
806
|
+
return False
|
|
807
|
+
|
|
707
808
|
# Ignore parameterized expressions with empty range
|
|
708
809
|
if parameter and parameter[1] > parameter[3]:
|
|
709
810
|
usermsgs.print_warning_loc(self.lexer.filename, line, column,
|
|
710
811
|
'ignoring parametric query with empty range.')
|
|
711
812
|
else:
|
|
712
|
-
self.queries.append((self.lexer.filename, line, column, expr, parameter))
|
|
813
|
+
self.queries.append(QuaTExQuery(self.lexer.filename, line, column, expr, parameter, delta))
|
|
713
814
|
|
|
714
815
|
# Function definition -- <name> ( <args> ) = <expr> ;
|
|
715
816
|
else:
|
|
@@ -744,7 +845,7 @@ class QuaTExParser:
|
|
|
744
845
|
if not self._expect('='):
|
|
745
846
|
return False
|
|
746
847
|
|
|
747
|
-
expr = self._parse_expr(';'
|
|
848
|
+
expr = self._parse_expr(';')
|
|
748
849
|
|
|
749
850
|
if not expr:
|
|
750
851
|
return False
|
|
@@ -763,8 +864,7 @@ class QuaTExParser:
|
|
|
763
864
|
expr, tail_pos = pending.pop()
|
|
764
865
|
|
|
765
866
|
if isinstance(expr, ast.Tuple) and not tail_pos:
|
|
766
|
-
self._eprint('non-tail calls are not allowed.',
|
|
767
|
-
line=expr.custom_loc[0], column=expr.custom_loc[1])
|
|
867
|
+
self._eprint('non-tail calls are not allowed.', **expr.custom_loc)
|
|
768
868
|
return False
|
|
769
869
|
|
|
770
870
|
elif isinstance(expr, ast.UnaryOp):
|
|
@@ -799,17 +899,17 @@ class QuaTExParser:
|
|
|
799
899
|
arities[name] = len(args)
|
|
800
900
|
|
|
801
901
|
# Check whether all calls are well-defined
|
|
802
|
-
for name, line, column, arity in self.calls:
|
|
902
|
+
for name, fname, line, column, arity in self.calls:
|
|
803
903
|
def_arity = arities.get(name)
|
|
804
904
|
|
|
805
905
|
if def_arity is None:
|
|
806
906
|
self._eprint(f'call to undefined function "{name}".',
|
|
807
|
-
line=line, column=column)
|
|
907
|
+
line=line, column=column, fname=fname)
|
|
808
908
|
ok = False
|
|
809
909
|
|
|
810
910
|
elif arity != def_arity:
|
|
811
911
|
self._eprint(f'wrong number of arguments in a call to "{name}" ({arity} given, but {def_arity} expected).',
|
|
812
|
-
line=line, column=column)
|
|
912
|
+
line=line, column=column, fname=fname)
|
|
813
913
|
ok = False
|
|
814
914
|
|
|
815
915
|
# Check all calls are tail in expression
|
|
@@ -817,8 +917,8 @@ class QuaTExParser:
|
|
|
817
917
|
if not self._check_tail(expr):
|
|
818
918
|
ok = False
|
|
819
919
|
|
|
820
|
-
for
|
|
821
|
-
if not self._check_tail(expr):
|
|
920
|
+
for query in self.queries:
|
|
921
|
+
if not self._check_tail(query.expr):
|
|
822
922
|
ok = False
|
|
823
923
|
|
|
824
924
|
if not ok:
|
|
@@ -848,22 +948,24 @@ class QuaTExParser:
|
|
|
848
948
|
line=line, column=column)
|
|
849
949
|
ok = False
|
|
850
950
|
|
|
851
|
-
for k,
|
|
951
|
+
for k, query in enumerate(self.queries):
|
|
852
952
|
try:
|
|
853
|
-
expr = ast.Expression(expr)
|
|
953
|
+
expr = ast.Expression(query.expr)
|
|
854
954
|
ast.fix_missing_locations(expr)
|
|
855
|
-
slots[used_defs + k] = compile(expr, filename=f'query{
|
|
955
|
+
slots[used_defs + k] = compile(expr, filename=f'query{query.filename}:{query.line}:{query.column}', mode='eval')
|
|
856
956
|
|
|
857
957
|
except TypeError:
|
|
858
958
|
self._eprint('this query cannot cannot be compiled.',
|
|
859
|
-
line=line, column=column, fname=fname)
|
|
959
|
+
line=query.line, column=query.column, fname=query.fname)
|
|
860
960
|
ok = False
|
|
861
961
|
|
|
962
|
+
# No longer needed
|
|
963
|
+
query.expr = None
|
|
964
|
+
|
|
862
965
|
if not ok:
|
|
863
966
|
return None
|
|
864
967
|
|
|
865
|
-
return QuaTExProgram(slots, varnames, len(self.fslots),
|
|
866
|
-
tuple((fname, line, column, params) for fname, line, column, _, params in self.queries))
|
|
968
|
+
return QuaTExProgram(slots, varnames, len(self.fslots), self.queries)
|
|
867
969
|
|
|
868
970
|
|
|
869
971
|
def parse_quatex(input_file, filename='<string>', legacy=False, constants=None):
|
umaudemc/simulators.py
CHANGED
|
@@ -109,6 +109,42 @@ class StrategyStepSimulator(BaseSimulator):
|
|
|
109
109
|
self.step += 1
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
class RuleStepSimulator(BaseSimulator):
|
|
113
|
+
"""Simulator where rule application (potentially using random) is the step"""
|
|
114
|
+
|
|
115
|
+
def __init__(self, initial):
|
|
116
|
+
super().__init__(initial)
|
|
117
|
+
|
|
118
|
+
# See the PMaude simulator to an explanation for why we need that
|
|
119
|
+
self.random = None
|
|
120
|
+
|
|
121
|
+
if nat_kind := self.module.findSort('Nat').kind():
|
|
122
|
+
if random := self.module.findSymbol('random', (nat_kind,), nat_kind):
|
|
123
|
+
self.random = random(self.module.parseTerm('1', nat_kind))
|
|
124
|
+
|
|
125
|
+
def restart(self):
|
|
126
|
+
"""Restart simulator"""
|
|
127
|
+
|
|
128
|
+
super().restart()
|
|
129
|
+
|
|
130
|
+
# PMaude uses Maude's random symbol, which is memoryless
|
|
131
|
+
# and deterministic for a fixed seed, so we need a new seed
|
|
132
|
+
if self.random:
|
|
133
|
+
self.random.copy().reduce()
|
|
134
|
+
maude.setRandomSeed(random.getrandbits(31))
|
|
135
|
+
|
|
136
|
+
def next_step(self):
|
|
137
|
+
"""Perform a step of the simulation"""
|
|
138
|
+
|
|
139
|
+
# Application of any executable rule
|
|
140
|
+
# (assumming it is deterministic)
|
|
141
|
+
next_state, *_ = next(self.state.apply(None), (None,))
|
|
142
|
+
|
|
143
|
+
if next_state is not None:
|
|
144
|
+
self.state = next_state
|
|
145
|
+
self.step += 1
|
|
146
|
+
|
|
147
|
+
|
|
112
148
|
def all_children(graph, state):
|
|
113
149
|
"""All children of a state in a graph"""
|
|
114
150
|
|
|
@@ -128,9 +164,9 @@ class UmaudemcSimulator(BaseSimulator):
|
|
|
128
164
|
def __init__(self, initial, graph, assigner):
|
|
129
165
|
super().__init__(initial)
|
|
130
166
|
|
|
131
|
-
self.state_nr = 0
|
|
132
167
|
self.graph = graph
|
|
133
168
|
self.assigner = assigner
|
|
169
|
+
self.state_nr = 0
|
|
134
170
|
self.time = 0.0
|
|
135
171
|
|
|
136
172
|
def restart(self):
|
|
@@ -274,13 +310,12 @@ class PMaudeSimulator(BaseSimulator):
|
|
|
274
310
|
self.nat_kind = nat_kind
|
|
275
311
|
self.val = val
|
|
276
312
|
|
|
277
|
-
#
|
|
278
|
-
self.state =
|
|
279
|
-
self.state.rewrite()
|
|
313
|
+
# restart must be called before running
|
|
314
|
+
self.state = None
|
|
280
315
|
|
|
281
316
|
# Try to find Maude's random symbol for calculating random(1). If the
|
|
282
|
-
# PMaude specification only reduces random(0), even after resetting
|
|
283
|
-
#
|
|
317
|
+
# PMaude specification only reduces random(0), even after resetting the
|
|
318
|
+
# random seed for a new simulation, it will take the same cached value
|
|
284
319
|
self.random = self.module.findSymbol('random', (nat_kind,), nat_kind)
|
|
285
320
|
|
|
286
321
|
if self.random:
|
|
@@ -473,12 +508,15 @@ def get_simulator(method, data):
|
|
|
473
508
|
method = 'step' if data.strategy else 'uniform'
|
|
474
509
|
|
|
475
510
|
# Check whether a strategy is provided for methods that require it
|
|
476
|
-
if not data.strategy and method in ('
|
|
511
|
+
if not data.strategy and method in ('strategy-fast', 'strategy'):
|
|
477
512
|
usermsgs.print_error(f'No strategy is provided for the {method} assignment method.')
|
|
478
513
|
return None
|
|
479
514
|
|
|
480
515
|
if method == 'step':
|
|
481
|
-
|
|
516
|
+
if data.strategy:
|
|
517
|
+
return StrategyStepSimulator(data.term, data.strategy)
|
|
518
|
+
else:
|
|
519
|
+
return RuleStepSimulator(data.term)
|
|
482
520
|
|
|
483
521
|
if method == 'strategy-fast':
|
|
484
522
|
return StrategyPathSimulator(data.module, data.term, data.strategy)
|
umaudemc/statistical.py
CHANGED
|
@@ -120,17 +120,17 @@ class QueryData:
|
|
|
120
120
|
self.discarded = 0
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
def make_parameter_dicts(qinfo):
|
|
123
|
+
def make_parameter_dicts(qinfo, delta):
|
|
124
124
|
"""Make the initial variable mapping for the parameters of a query"""
|
|
125
125
|
|
|
126
|
-
if qinfo is None:
|
|
127
|
-
yield {}
|
|
126
|
+
if qinfo.parameters is None:
|
|
127
|
+
yield {}, (qinfo.delta() if qinfo.delta else delta)
|
|
128
128
|
|
|
129
129
|
else:
|
|
130
|
-
var, x, step, end = qinfo
|
|
130
|
+
var, x, step, end = qinfo.parameters
|
|
131
131
|
|
|
132
132
|
while x <= end:
|
|
133
|
-
yield {var: x}
|
|
133
|
+
yield {var: x}, (qinfo.delta(x) if qinfo.delta else delta)
|
|
134
134
|
x += step
|
|
135
135
|
|
|
136
136
|
|
|
@@ -139,8 +139,10 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
|
|
|
139
139
|
|
|
140
140
|
# Whether the size of the confidence interval for all queries have converged
|
|
141
141
|
converged = True
|
|
142
|
+
# Which queries have converged
|
|
143
|
+
which = []
|
|
142
144
|
|
|
143
|
-
for query in qdata:
|
|
145
|
+
for k, query in enumerate(qdata):
|
|
144
146
|
# This query has already converged
|
|
145
147
|
if query.converged:
|
|
146
148
|
continue
|
|
@@ -148,18 +150,23 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
|
|
|
148
150
|
elif query.n == 0:
|
|
149
151
|
converged = False
|
|
150
152
|
continue
|
|
153
|
+
# A single execution
|
|
154
|
+
elif query.n == 1:
|
|
155
|
+
query.mu, query.s, query.h = query.sum, 0.0, 0.0
|
|
156
|
+
# General case
|
|
157
|
+
else:
|
|
158
|
+
# The radius encloses the confidence level in the reference
|
|
159
|
+
# distribution for calculating confidence intervals
|
|
160
|
+
tinv = quantile(query.n - 1, 1 - alpha / 2) / math.sqrt(query.n)
|
|
151
161
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
query.mu = query.sum / query.n
|
|
157
|
-
query.s = math.sqrt(max(query.sum_sq - query.sum * query.mu, 0.0) / (query.n - 1))
|
|
158
|
-
query.h = query.s * tinv
|
|
162
|
+
query.mu = query.sum / query.n
|
|
163
|
+
query.s = math.sqrt(max(query.sum_sq - query.sum * query.mu, 0.0) / (query.n - 1))
|
|
164
|
+
query.h = query.s * tinv
|
|
159
165
|
|
|
160
166
|
if query.h <= query.delta and query.n >= min_sim:
|
|
161
167
|
query.converged = True
|
|
162
168
|
query.discarded = num_sims - query.n
|
|
169
|
+
which.append(k)
|
|
163
170
|
else:
|
|
164
171
|
converged = False
|
|
165
172
|
|
|
@@ -170,7 +177,10 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
|
|
|
170
177
|
f' σ={" ".join(str(q.s) for q in qdata)}'
|
|
171
178
|
f' r={" ".join(str(q.h) for q in qdata)}')
|
|
172
179
|
|
|
173
|
-
|
|
180
|
+
for k in which:
|
|
181
|
+
print(f' Query {qdata[k].query + 1} has converged')
|
|
182
|
+
|
|
183
|
+
return converged, which
|
|
174
184
|
|
|
175
185
|
|
|
176
186
|
def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, block_size,
|
|
@@ -200,7 +210,7 @@ def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, blo
|
|
|
200
210
|
query.sum_sq += value * value
|
|
201
211
|
query.n += 1
|
|
202
212
|
|
|
203
|
-
converged = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
|
|
213
|
+
converged, _ = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
|
|
204
214
|
|
|
205
215
|
if converged or max_sim and num_sims >= max_sim:
|
|
206
216
|
break
|
|
@@ -211,7 +221,7 @@ def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, blo
|
|
|
211
221
|
return num_sims, qdata
|
|
212
222
|
|
|
213
223
|
|
|
214
|
-
def thread_main(program, qdata, simulator, num_sims, block_size, seed, queue, barrier, more, dump=None):
|
|
224
|
+
def thread_main(program, qdata, simulator, num_sims, block_size, seed, queue, barrier, more, convergents, dump=None):
|
|
215
225
|
"""Entry point of a calculating thread"""
|
|
216
226
|
|
|
217
227
|
maude.setRandomSeed(seed)
|
|
@@ -254,6 +264,12 @@ def thread_main(program, qdata, simulator, num_sims, block_size, seed, queue, ba
|
|
|
254
264
|
if not more.value:
|
|
255
265
|
break
|
|
256
266
|
|
|
267
|
+
# Disable queries that have converged
|
|
268
|
+
for k in range(len(qdata)):
|
|
269
|
+
if convergents[k] == -1:
|
|
270
|
+
break
|
|
271
|
+
qdata[convergents[k]].converged = True
|
|
272
|
+
|
|
257
273
|
# Continue for a next block
|
|
258
274
|
block = block_size
|
|
259
275
|
|
|
@@ -280,12 +296,14 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
|
|
|
280
296
|
queue = mp.Queue()
|
|
281
297
|
barrier = mp.Barrier(jobs + 1)
|
|
282
298
|
more = mp.Value('b', False, lock=False)
|
|
299
|
+
convergents = mp.Array('i', [-1] * len(qdata), lock=False)
|
|
283
300
|
|
|
284
301
|
rest, rest_block = num_sims % jobs, block_size % jobs
|
|
285
302
|
processes = [mp.Process(target=thread_main,
|
|
286
303
|
args=(program, qdata, simulator, num_sims // jobs + (k < rest),
|
|
287
304
|
block_size // jobs + (k < rest_block),
|
|
288
|
-
seeds[k], queue, barrier, more, dumps[k]))
|
|
305
|
+
seeds[k], queue, barrier, more, convergents, dumps[k]))
|
|
306
|
+
for k in range(jobs)]
|
|
289
307
|
|
|
290
308
|
# Start all processes
|
|
291
309
|
for p in processes:
|
|
@@ -301,7 +319,7 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
|
|
|
301
319
|
query.sum_sq += sum_sq[k]
|
|
302
320
|
query.n += counts[k]
|
|
303
321
|
|
|
304
|
-
converged = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
|
|
322
|
+
converged, which = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
|
|
305
323
|
|
|
306
324
|
if converged or max_sim and num_sims >= max_sim:
|
|
307
325
|
break
|
|
@@ -309,6 +327,11 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
|
|
|
309
327
|
num_sims += block_size
|
|
310
328
|
|
|
311
329
|
more.value = True
|
|
330
|
+
# Inform which queries have converged
|
|
331
|
+
for k, qindex in enumerate(which):
|
|
332
|
+
convergents[k] = qindex
|
|
333
|
+
convergents[len(which)] = -1
|
|
334
|
+
|
|
312
335
|
barrier.wait()
|
|
313
336
|
|
|
314
337
|
more.value = False
|
|
@@ -328,9 +351,9 @@ def qdata_to_dict(num_sims, qdata, program):
|
|
|
328
351
|
qdata_it = iter(qdata)
|
|
329
352
|
q = next(qdata_it, None)
|
|
330
353
|
|
|
331
|
-
for k,
|
|
354
|
+
for k, query in enumerate(program.queries):
|
|
332
355
|
# For parametric queries, we return an array of values
|
|
333
|
-
if
|
|
356
|
+
if query.parameters:
|
|
334
357
|
mean, std, radius, count, discarded = [], [], [], [], []
|
|
335
358
|
|
|
336
359
|
while q and q.query == k:
|
|
@@ -342,13 +365,15 @@ def qdata_to_dict(num_sims, qdata, program):
|
|
|
342
365
|
q = next(qdata_it, None)
|
|
343
366
|
|
|
344
367
|
# We also write information about the parameter
|
|
345
|
-
param_info = {'params': [dict(name
|
|
368
|
+
param_info = {'params': [dict(zip(('name', 'start', 'step', 'stop'), query.parameters))]}
|
|
346
369
|
|
|
347
370
|
else:
|
|
348
371
|
mean, std, radius, count, discarded = q.mu, q.s, q.h, q.n, q.discarded
|
|
372
|
+
q = next(qdata_it, None)
|
|
349
373
|
param_info = {}
|
|
350
374
|
|
|
351
|
-
queries.append(dict(mean=mean, std=std, radius=radius,
|
|
375
|
+
queries.append(dict(mean=mean, std=std, radius=radius,
|
|
376
|
+
file=query.filename, line=query.line, column=query.column,
|
|
352
377
|
nsims=count, discarded=discarded, **param_info))
|
|
353
378
|
|
|
354
379
|
return dict(nsims=num_sims, queries=queries)
|
|
@@ -370,9 +395,9 @@ def check(program, simulator, seed, alpha, delta, block, min_sim, max_sim, jobs,
|
|
|
370
395
|
|
|
371
396
|
# Each query maintains some data like the sum of the outcomes
|
|
372
397
|
# and the sum of their squares
|
|
373
|
-
qdata = [QueryData(k,
|
|
374
|
-
for k, qinfo in enumerate(program.
|
|
375
|
-
for idict in make_parameter_dicts(qinfo
|
|
398
|
+
qdata = [QueryData(k, dt, idict)
|
|
399
|
+
for k, qinfo in enumerate(program.queries)
|
|
400
|
+
for idict, dt in make_parameter_dicts(qinfo, delta)]
|
|
376
401
|
|
|
377
402
|
# Run the simulations
|
|
378
403
|
if jobs == 1 and num_sims != 1:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
umaudemc/__init__.py,sha256=
|
|
2
|
-
umaudemc/__main__.py,sha256=
|
|
1
|
+
umaudemc/__init__.py,sha256=ctD9pjqBvASXR0DHHzalDZFaQsnMJWDpTalYrvY3e_Y,23
|
|
2
|
+
umaudemc/__main__.py,sha256=LgKeZWi1JRrEclPS3asyZziErEJyldQvlZTtTt_tcyc,15063
|
|
3
3
|
umaudemc/api.py,sha256=naZ5edEbvx-S-NU29yAAJtqglfYnSAYVS2RJNyxJMQQ,19893
|
|
4
4
|
umaudemc/backends.py,sha256=mzJkALYwcKPInT0lBiRsCxJSewKvx5j_akQsqWN1Ezo,4590
|
|
5
5
|
umaudemc/common.py,sha256=UcIf7hTpP2qjcT9u_9-UcYR0nNeosx1xRZW7wsuT2bE,7305
|
|
6
6
|
umaudemc/counterprint.py,sha256=vVqM_UjGRk_xeftFxBGI5m6cQXV7mf8KvbQ_fvAvSQk,9226
|
|
7
|
-
umaudemc/distributed.py,sha256=
|
|
7
|
+
umaudemc/distributed.py,sha256=jUxVUHtPRefwxNYZmU0ErePre6FTmH5S8RX6ADBL1vA,10611
|
|
8
8
|
umaudemc/formatter.py,sha256=nbQlIsR5Xv18OEcpJdnTDGqO9xGL_amvBGFMU2OmheU,6026
|
|
9
9
|
umaudemc/formulae.py,sha256=jZPPDhjgsb7cs5rWvitiQoO0fd8JIlK98at2SN-LzVE,12156
|
|
10
10
|
umaudemc/grapher.py,sha256=K1chKNNlEzQvfOsiFmRPJmd9OpxRIrg6OyiMW6gqOCU,4348
|
|
@@ -15,10 +15,10 @@ umaudemc/mproc.py,sha256=9X5pTb3Z3XHcdOo8ynH7I5RZQpjzm9xr4IBbEtaglUE,11766
|
|
|
15
15
|
umaudemc/opsem.py,sha256=Xfdi9QGy-vcpmQ9ni8lBDAlKNw-fCRzYr6wnPbv6m1s,9448
|
|
16
16
|
umaudemc/probabilistic.py,sha256=MNvFeEd84-OYedSnyksZB87UckPfwizVNJepCItgRy8,29306
|
|
17
17
|
umaudemc/pyslang.py,sha256=ABSXYUQO2TmDq8EZ3EpVZV8NecZ0p0gERlSvLUIVAm8,87970
|
|
18
|
-
umaudemc/quatex.py,sha256=
|
|
18
|
+
umaudemc/quatex.py,sha256=7n7ugusjFUyO5jqKeb6-O8hdH4vj2fgnc-V_Z41w-40,27271
|
|
19
19
|
umaudemc/resources.py,sha256=qKqvgLYTJVtsQHQMXFObyCLTo6-fssQeu_mG7tvVyD0,932
|
|
20
|
-
umaudemc/simulators.py,sha256=
|
|
21
|
-
umaudemc/statistical.py,sha256=
|
|
20
|
+
umaudemc/simulators.py,sha256=1_DKaO2JYxSQHcbTE2wbB_lgd_kFBi40PPjOMvqOgaE,14341
|
|
21
|
+
umaudemc/statistical.py,sha256=I_C1ymz-ue-XaX0zF7lyQUce57-BdEWhctKg6yG2c_A,11371
|
|
22
22
|
umaudemc/terminal.py,sha256=B4GWLyW4Sdymgoavj418y4TI4MnWqNu3JS4BBoSYeTc,1037
|
|
23
23
|
umaudemc/usermsgs.py,sha256=h4VPxljyKidEI8vpPcToKJA6mcLu9PtMkIh6vH3rDuA,719
|
|
24
24
|
umaudemc/webui.py,sha256=XlDV87tOOdcclHp2_oerrvHwRmCZdqAR4PppqeZm47A,11072
|
|
@@ -40,8 +40,8 @@ umaudemc/command/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
40
40
|
umaudemc/command/check.py,sha256=PyaPDMw5OnPxSIZ10U4we0b5tTrjnYKAtAeQkJh2uLE,12031
|
|
41
41
|
umaudemc/command/graph.py,sha256=JqGzESC2sn-LBh2sqctrij03ItzwDO808s2qkNKUle0,6112
|
|
42
42
|
umaudemc/command/pcheck.py,sha256=eV4e4GcOHanP4hcIhMKd5Js22_ONac6kYj70FXun3mY,7274
|
|
43
|
-
umaudemc/command/scheck.py,sha256=
|
|
44
|
-
umaudemc/command/sworker.py,sha256=
|
|
43
|
+
umaudemc/command/scheck.py,sha256=mmAzCdGdIHgKbsyEi0uW-jcj9X0MSc-EEkEPR-EiB8U,6208
|
|
44
|
+
umaudemc/command/sworker.py,sha256=uxw8gmacghUMvM0MUqM-AXX-w9xOA-10Mj-ExPWoNUI,5196
|
|
45
45
|
umaudemc/command/test.py,sha256=Ru21JXNF61F5N5jayjwxp8okIjOAvuZuAlV_5ltQ-GU,37088
|
|
46
46
|
umaudemc/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
47
|
umaudemc/data/opsem.maude,sha256=geDP3_RMgtS1rRmYOybJDCXn_-dyHHxg0JxfYg1ftv0,27929
|
|
@@ -52,9 +52,9 @@ umaudemc/data/smcgraph.js,sha256=iCNQNmsuGdL_GLnqVhGDisediFtedxw3C24rxSiQwx8,667
|
|
|
52
52
|
umaudemc/data/smcview.css,sha256=ExFqrMkSeaf8VxFrJXflyCsRW3FTwbv78q0Hoo2UVrM,3833
|
|
53
53
|
umaudemc/data/smcview.js,sha256=_fHum1DRU1mhco-9-c6KqTLgiC5u_cCUf61jIK7wcIQ,14509
|
|
54
54
|
umaudemc/data/templog.maude,sha256=TZ-66hVWoG6gp7gJpS6FsQn7dpBTLrr76bKo-UfHGcA,9161
|
|
55
|
-
umaudemc-0.
|
|
56
|
-
umaudemc-0.
|
|
57
|
-
umaudemc-0.
|
|
58
|
-
umaudemc-0.
|
|
59
|
-
umaudemc-0.
|
|
60
|
-
umaudemc-0.
|
|
55
|
+
umaudemc-0.17.1.dist-info/licenses/LICENSE,sha256=MrEGL32oSWfnAZ0Bq4BZNcqnq3Mhp87Q4w6-deXfFnA,17992
|
|
56
|
+
umaudemc-0.17.1.dist-info/METADATA,sha256=bW8k2ctMQZPpFsXIHJ9wiBc2HoDwqw3VZHNyPgx75qk,1654
|
|
57
|
+
umaudemc-0.17.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
58
|
+
umaudemc-0.17.1.dist-info/entry_points.txt,sha256=8rYRlLkn4orZtAoujDSeol1t_UFBrK0bfjmLTNv9B44,52
|
|
59
|
+
umaudemc-0.17.1.dist-info/top_level.txt,sha256=Yo_CF78HLGBSblk3890qLcx6XZ17zHCbGcT9iG8sfMw,9
|
|
60
|
+
umaudemc-0.17.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|