umaudemc 0.17.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.
@@ -99,10 +99,13 @@ class Worker:
99
99
  for k, qinfo in enumerate(program.queries)
100
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,9 +280,10 @@ 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
- for sock, data in zip(sockets, dspec['workers']):
273
- selector.register(sock, selectors.EVENT_READ, data={'block': args.block} | data)
274
- sock.send(b'c')
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')
@@ -283,6 +295,12 @@ def distributed_check(args, initial_data, min_sim, max_sim, program, constants,
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['block']
332
+ num_sims += key.data.block
308
333
 
309
334
  del buffer[:]
310
335
  del ibuffer[:]
311
- finished.append(key.fileobj)
336
+ finished.append(key)
312
337
 
313
338
  else:
314
- usermsgs.print_error(f'Server {key.data["name"]} disconnected or misbehaving')
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 sock in finished:
325
- sock.send(b'c')
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/simulators.py CHANGED
@@ -164,9 +164,9 @@ class UmaudemcSimulator(BaseSimulator):
164
164
  def __init__(self, initial, graph, assigner):
165
165
  super().__init__(initial)
166
166
 
167
- self.state_nr = 0
168
167
  self.graph = graph
169
168
  self.assigner = assigner
169
+ self.state_nr = 0
170
170
  self.time = 0.0
171
171
 
172
172
  def restart(self):
@@ -310,9 +310,8 @@ class PMaudeSimulator(BaseSimulator):
310
310
  self.nat_kind = nat_kind
311
311
  self.val = val
312
312
 
313
- # Prepares the initial term
314
- self.state = self.initial.copy()
315
- self.state.rewrite()
313
+ # restart must be called before running
314
+ self.state = None
316
315
 
317
316
  # Try to find Maude's random symbol for calculating random(1). If the
318
317
  # PMaude specification only reduces random(0), even after resetting the
umaudemc/statistical.py CHANGED
@@ -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
@@ -164,6 +166,7 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
164
166
  if query.h <= query.delta and query.n >= min_sim:
165
167
  query.converged = True
166
168
  query.discarded = num_sims - query.n
169
+ which.append(k)
167
170
  else:
168
171
  converged = False
169
172
 
@@ -174,7 +177,10 @@ def check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose):
174
177
  f' σ={" ".join(str(q.s) for q in qdata)}'
175
178
  f' r={" ".join(str(q.h) for q in qdata)}')
176
179
 
177
- return converged
180
+ for k in which:
181
+ print(f' Query {qdata[k].query + 1} has converged')
182
+
183
+ return converged, which
178
184
 
179
185
 
180
186
  def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, block_size,
@@ -204,7 +210,7 @@ def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, blo
204
210
  query.sum_sq += value * value
205
211
  query.n += 1
206
212
 
207
- converged = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
213
+ converged, _ = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
208
214
 
209
215
  if converged or max_sim and num_sims >= max_sim:
210
216
  break
@@ -215,7 +221,7 @@ def run_single(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, blo
215
221
  return num_sims, qdata
216
222
 
217
223
 
218
- 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):
219
225
  """Entry point of a calculating thread"""
220
226
 
221
227
  maude.setRandomSeed(seed)
@@ -258,6 +264,12 @@ def thread_main(program, qdata, simulator, num_sims, block_size, seed, queue, ba
258
264
  if not more.value:
259
265
  break
260
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
+
261
273
  # Continue for a next block
262
274
  block = block_size
263
275
 
@@ -284,12 +296,14 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
284
296
  queue = mp.Queue()
285
297
  barrier = mp.Barrier(jobs + 1)
286
298
  more = mp.Value('b', False, lock=False)
299
+ convergents = mp.Array('i', [-1] * len(qdata), lock=False)
287
300
 
288
301
  rest, rest_block = num_sims % jobs, block_size % jobs
289
302
  processes = [mp.Process(target=thread_main,
290
303
  args=(program, qdata, simulator, num_sims // jobs + (k < rest),
291
304
  block_size // jobs + (k < rest_block),
292
- seeds[k], queue, barrier, more, dumps[k])) for k in range(jobs)]
305
+ seeds[k], queue, barrier, more, convergents, dumps[k]))
306
+ for k in range(jobs)]
293
307
 
294
308
  # Start all processes
295
309
  for p in processes:
@@ -305,7 +319,7 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
305
319
  query.sum_sq += sum_sq[k]
306
320
  query.n += counts[k]
307
321
 
308
- converged = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
322
+ converged, which = check_interval(qdata, num_sims, min_sim, alpha, quantile, verbose)
309
323
 
310
324
  if converged or max_sim and num_sims >= max_sim:
311
325
  break
@@ -313,6 +327,11 @@ def run_parallel(program, qdata, num_sims, min_sim, max_sim, simulator, alpha, b
313
327
  num_sims += block_size
314
328
 
315
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
+
316
335
  barrier.wait()
317
336
 
318
337
  more.value = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: umaudemc
3
- Version: 0.17.0
3
+ Version: 0.17.1
4
4
  Summary: Unified Maude model-checking utility
5
5
  Author-email: ningit <ningit@users.noreply.github.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -4,7 +4,7 @@ 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=aCvU1TaM5SZhAtWahclyANc1I0YXj8SuPXO_VjaqDdo,9115
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
@@ -17,8 +17,8 @@ umaudemc/probabilistic.py,sha256=MNvFeEd84-OYedSnyksZB87UckPfwizVNJepCItgRy8,293
17
17
  umaudemc/pyslang.py,sha256=ABSXYUQO2TmDq8EZ3EpVZV8NecZ0p0gERlSvLUIVAm8,87970
18
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=K8NFgwvvTgj1lueksnWOSc-7bo6VxS1_CA9g-6l5umc,14367
21
- umaudemc/statistical.py,sha256=aogqx2JCur9Lw9ZfOI6jaggwLcVTflDSs_Mn7yCmOGE,10815
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
@@ -41,7 +41,7 @@ umaudemc/command/check.py,sha256=PyaPDMw5OnPxSIZ10U4we0b5tTrjnYKAtAeQkJh2uLE,120
41
41
  umaudemc/command/graph.py,sha256=JqGzESC2sn-LBh2sqctrij03ItzwDO808s2qkNKUle0,6112
42
42
  umaudemc/command/pcheck.py,sha256=eV4e4GcOHanP4hcIhMKd5Js22_ONac6kYj70FXun3mY,7274
43
43
  umaudemc/command/scheck.py,sha256=mmAzCdGdIHgKbsyEi0uW-jcj9X0MSc-EEkEPR-EiB8U,6208
44
- umaudemc/command/sworker.py,sha256=1icakFDO_qSr4ga-5Cu6eDfLirjLPJLNtO7OkbwTUQQ,4651
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.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,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5