umaudemc 0.16.0__py3-none-any.whl → 0.17.0__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 +2 -2
- umaudemc/distributed.py +3 -3
- umaudemc/quatex.py +162 -60
- umaudemc/simulators.py +43 -4
- umaudemc/statistical.py +25 -19
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.dist-info}/METADATA +1 -1
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.dist-info}/RECORD +14 -14
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.dist-info}/WHEEL +1 -1
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.dist-info}/entry_points.txt +0 -0
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.dist-info}/licenses/LICENSE +0 -0
- {umaudemc-0.16.0.dist-info → umaudemc-0.17.0.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,8 +96,8 @@ 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
102
|
sums = array('d', [0.0] * len(qdata))
|
|
103
103
|
sum_sq = array('d', [0.0] * len(qdata))
|
umaudemc/distributed.py
CHANGED
|
@@ -277,9 +277,9 @@ def distributed_check(args, initial_data, min_sim, max_sim, program, constants,
|
|
|
277
277
|
ibuffer = array('i')
|
|
278
278
|
|
|
279
279
|
# Query data
|
|
280
|
-
qdata = [QueryData(k,
|
|
281
|
-
for k, qinfo in enumerate(program.
|
|
282
|
-
for idict in make_parameter_dicts(qinfo
|
|
280
|
+
qdata = [QueryData(k, delta, idict)
|
|
281
|
+
for k, qinfo in enumerate(program.queries)
|
|
282
|
+
for idict, delta in make_parameter_dicts(qinfo, args.delta)]
|
|
283
283
|
nqueries = len(qdata)
|
|
284
284
|
num_sims = 0
|
|
285
285
|
|
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
|
|
|
@@ -279,8 +315,8 @@ class PMaudeSimulator(BaseSimulator):
|
|
|
279
315
|
self.state.rewrite()
|
|
280
316
|
|
|
281
317
|
# Try to find Maude's random symbol for calculating random(1). If the
|
|
282
|
-
# PMaude specification only reduces random(0), even after resetting
|
|
283
|
-
#
|
|
318
|
+
# PMaude specification only reduces random(0), even after resetting the
|
|
319
|
+
# random seed for a new simulation, it will take the same cached value
|
|
284
320
|
self.random = self.module.findSymbol('random', (nat_kind,), nat_kind)
|
|
285
321
|
|
|
286
322
|
if self.random:
|
|
@@ -473,12 +509,15 @@ def get_simulator(method, data):
|
|
|
473
509
|
method = 'step' if data.strategy else 'uniform'
|
|
474
510
|
|
|
475
511
|
# Check whether a strategy is provided for methods that require it
|
|
476
|
-
if not data.strategy and method in ('
|
|
512
|
+
if not data.strategy and method in ('strategy-fast', 'strategy'):
|
|
477
513
|
usermsgs.print_error(f'No strategy is provided for the {method} assignment method.')
|
|
478
514
|
return None
|
|
479
515
|
|
|
480
516
|
if method == 'step':
|
|
481
|
-
|
|
517
|
+
if data.strategy:
|
|
518
|
+
return StrategyStepSimulator(data.term, data.strategy)
|
|
519
|
+
else:
|
|
520
|
+
return RuleStepSimulator(data.term)
|
|
482
521
|
|
|
483
522
|
if method == 'strategy-fast':
|
|
484
523
|
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
|
|
|
@@ -148,14 +148,18 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
|
|
|
148
148
|
elif query.n == 0:
|
|
149
149
|
converged = False
|
|
150
150
|
continue
|
|
151
|
+
# A single execution
|
|
152
|
+
elif query.n == 1:
|
|
153
|
+
query.mu, query.s, query.h = query.sum, 0.0, 0.0
|
|
154
|
+
# General case
|
|
155
|
+
else:
|
|
156
|
+
# The radius encloses the confidence level in the reference
|
|
157
|
+
# distribution for calculating confidence intervals
|
|
158
|
+
tinv = quantile(query.n - 1, 1 - alpha / 2) / math.sqrt(query.n)
|
|
151
159
|
|
|
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
|
|
160
|
+
query.mu = query.sum / query.n
|
|
161
|
+
query.s = math.sqrt(max(query.sum_sq - query.sum * query.mu, 0.0) / (query.n - 1))
|
|
162
|
+
query.h = query.s * tinv
|
|
159
163
|
|
|
160
164
|
if query.h <= query.delta and query.n >= min_sim:
|
|
161
165
|
query.converged = True
|
|
@@ -328,9 +332,9 @@ def qdata_to_dict(num_sims, qdata, program):
|
|
|
328
332
|
qdata_it = iter(qdata)
|
|
329
333
|
q = next(qdata_it, None)
|
|
330
334
|
|
|
331
|
-
for k,
|
|
335
|
+
for k, query in enumerate(program.queries):
|
|
332
336
|
# For parametric queries, we return an array of values
|
|
333
|
-
if
|
|
337
|
+
if query.parameters:
|
|
334
338
|
mean, std, radius, count, discarded = [], [], [], [], []
|
|
335
339
|
|
|
336
340
|
while q and q.query == k:
|
|
@@ -342,13 +346,15 @@ def qdata_to_dict(num_sims, qdata, program):
|
|
|
342
346
|
q = next(qdata_it, None)
|
|
343
347
|
|
|
344
348
|
# We also write information about the parameter
|
|
345
|
-
param_info = {'params': [dict(name
|
|
349
|
+
param_info = {'params': [dict(zip(('name', 'start', 'step', 'stop'), query.parameters))]}
|
|
346
350
|
|
|
347
351
|
else:
|
|
348
352
|
mean, std, radius, count, discarded = q.mu, q.s, q.h, q.n, q.discarded
|
|
353
|
+
q = next(qdata_it, None)
|
|
349
354
|
param_info = {}
|
|
350
355
|
|
|
351
|
-
queries.append(dict(mean=mean, std=std, radius=radius,
|
|
356
|
+
queries.append(dict(mean=mean, std=std, radius=radius,
|
|
357
|
+
file=query.filename, line=query.line, column=query.column,
|
|
352
358
|
nsims=count, discarded=discarded, **param_info))
|
|
353
359
|
|
|
354
360
|
return dict(nsims=num_sims, queries=queries)
|
|
@@ -370,9 +376,9 @@ def check(program, simulator, seed, alpha, delta, block, min_sim, max_sim, jobs,
|
|
|
370
376
|
|
|
371
377
|
# Each query maintains some data like the sum of the outcomes
|
|
372
378
|
# 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
|
|
379
|
+
qdata = [QueryData(k, dt, idict)
|
|
380
|
+
for k, qinfo in enumerate(program.queries)
|
|
381
|
+
for idict, dt in make_parameter_dicts(qinfo, delta)]
|
|
376
382
|
|
|
377
383
|
# Run the simulations
|
|
378
384
|
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=aCvU1TaM5SZhAtWahclyANc1I0YXj8SuPXO_VjaqDdo,9115
|
|
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=K8NFgwvvTgj1lueksnWOSc-7bo6VxS1_CA9g-6l5umc,14367
|
|
21
|
+
umaudemc/statistical.py,sha256=aogqx2JCur9Lw9ZfOI6jaggwLcVTflDSs_Mn7yCmOGE,10815
|
|
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=1icakFDO_qSr4ga-5Cu6eDfLirjLPJLNtO7OkbwTUQQ,4651
|
|
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.0.dist-info/licenses/LICENSE,sha256=MrEGL32oSWfnAZ0Bq4BZNcqnq3Mhp87Q4w6-deXfFnA,17992
|
|
56
|
+
umaudemc-0.17.0.dist-info/METADATA,sha256=HMhQv0StUz5ODf2H8vanIt_FnkK6p7BDZd-nkc8o0Fg,1654
|
|
57
|
+
umaudemc-0.17.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
58
|
+
umaudemc-0.17.0.dist-info/entry_points.txt,sha256=8rYRlLkn4orZtAoujDSeol1t_UFBrK0bfjmLTNv9B44,52
|
|
59
|
+
umaudemc-0.17.0.dist-info/top_level.txt,sha256=Yo_CF78HLGBSblk3890qLcx6XZ17zHCbGcT9iG8sfMw,9
|
|
60
|
+
umaudemc-0.17.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|