process-bigraph 0.0.27__tar.gz → 0.0.28__tar.gz
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.
- {process-bigraph-0.0.27/process_bigraph.egg-info → process-bigraph-0.0.28}/PKG-INFO +1 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/__init__.py +2 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/composite.py +12 -518
- process-bigraph-0.0.28/process_bigraph/emitter.py +406 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/process_types.py +139 -21
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/growth_division.py +2 -2
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/parameter_scan.py +2 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/protocols.py +0 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/tests.py +78 -17
- {process-bigraph-0.0.27 → process-bigraph-0.0.28/process_bigraph.egg-info}/PKG-INFO +1 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/SOURCES.txt +1 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/setup.py +1 -1
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/AUTHORS.md +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/LICENSE +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/README.md +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/experiments/__init__.py +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/experiments/minimal_gillespie.py +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/__init__.py +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/dependency_links.txt +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/requires.txt +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/top_level.txt +0 -0
- {process-bigraph-0.0.27 → process-bigraph-0.0.28}/setup.cfg +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import pprint
|
|
2
2
|
from bigraph_schema.registry import deep_merge, default
|
|
3
3
|
from process_bigraph.processes import register_processes
|
|
4
|
-
from process_bigraph.composite import Process, Step, Composite,
|
|
4
|
+
from process_bigraph.composite import Process, Step, Composite, interval_time_precision
|
|
5
|
+
from process_bigraph.process_types import ProcessTypes
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
pretty = pprint.PrettyPrinter(indent=2)
|
|
@@ -10,12 +10,14 @@ import math
|
|
|
10
10
|
import collections
|
|
11
11
|
from typing import Dict
|
|
12
12
|
|
|
13
|
-
from bigraph_schema import
|
|
13
|
+
from bigraph_schema import (
|
|
14
|
+
Edge, Registry,
|
|
15
|
+
get_path, resolve_path, hierarchy_depth, deep_merge,
|
|
16
|
+
is_schema_key, strip_schema_keys)
|
|
14
17
|
|
|
15
18
|
from process_bigraph.protocols import local_lookup, local_lookup_module
|
|
16
19
|
|
|
17
20
|
|
|
18
|
-
|
|
19
21
|
# =========================
|
|
20
22
|
# Process Utility Functions
|
|
21
23
|
# =========================
|
|
@@ -24,7 +26,8 @@ def assert_interface(interface: Dict):
|
|
|
24
26
|
"""Ensure that an interface dict has the required keys"""
|
|
25
27
|
required_keys = ['inputs', 'outputs']
|
|
26
28
|
existing_keys = set(interface.keys())
|
|
27
|
-
assert existing_keys == set(required_keys),
|
|
29
|
+
assert existing_keys == set(required_keys), \
|
|
30
|
+
f"every interface requires an inputs schema and an outputs schema, not {existing_keys}"
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
def find_instances(state, instance_type='process_bigraph.composite.Process'):
|
|
@@ -57,7 +60,7 @@ def find_step_triggers(path, step):
|
|
|
57
60
|
step['inputs'])
|
|
58
61
|
|
|
59
62
|
for wire in wire_paths:
|
|
60
|
-
trigger_path = tuple(prefix) + tuple(wire)
|
|
63
|
+
trigger_path = resolve_path(tuple(prefix) + tuple(wire))
|
|
61
64
|
if trigger_path not in triggers:
|
|
62
65
|
triggers[trigger_path] = []
|
|
63
66
|
triggers[trigger_path].append(path)
|
|
@@ -241,324 +244,6 @@ def interval_time_precision(timestep):
|
|
|
241
244
|
return global_time_precision
|
|
242
245
|
|
|
243
246
|
|
|
244
|
-
# ======================
|
|
245
|
-
# Process Type Functions
|
|
246
|
-
# ======================
|
|
247
|
-
|
|
248
|
-
def apply_process(schema, current, update, core):
|
|
249
|
-
"""Apply an update to a process."""
|
|
250
|
-
process_schema = schema.copy()
|
|
251
|
-
process_schema.pop('_apply')
|
|
252
|
-
return core.apply(
|
|
253
|
-
process_schema,
|
|
254
|
-
current,
|
|
255
|
-
update)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def check_process(schema, state, core):
|
|
259
|
-
"""Check if this is a process."""
|
|
260
|
-
return 'instance' in state and isinstance(
|
|
261
|
-
state['instance'],
|
|
262
|
-
Edge)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
def fold_visit(schema, state, method, values, core):
|
|
266
|
-
visit = visit_method(
|
|
267
|
-
schema,
|
|
268
|
-
state,
|
|
269
|
-
method,
|
|
270
|
-
values,
|
|
271
|
-
core)
|
|
272
|
-
|
|
273
|
-
return visit
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def divide_process(schema, state, values, core):
|
|
277
|
-
# daughter_configs must have a config per daughter
|
|
278
|
-
|
|
279
|
-
daughter_configs = values.get(
|
|
280
|
-
'daughter_configs',
|
|
281
|
-
[{} for index in range(values['divisions'])])
|
|
282
|
-
|
|
283
|
-
if 'config' not in state:
|
|
284
|
-
return daughter_configs
|
|
285
|
-
|
|
286
|
-
existing_config = state['config']
|
|
287
|
-
|
|
288
|
-
divisions = []
|
|
289
|
-
for index in range(values['divisions']):
|
|
290
|
-
daughter_config = copy.deepcopy(
|
|
291
|
-
existing_config)
|
|
292
|
-
daughter_config = deep_merge(
|
|
293
|
-
daughter_config,
|
|
294
|
-
daughter_configs[index])
|
|
295
|
-
|
|
296
|
-
# TODO: provide a way to override inputs and outputs
|
|
297
|
-
daughter_state = {
|
|
298
|
-
'address': state['address'],
|
|
299
|
-
'config': daughter_config,
|
|
300
|
-
'inputs': copy.deepcopy(state['inputs']),
|
|
301
|
-
'outputs': copy.deepcopy(state['outputs'])}
|
|
302
|
-
|
|
303
|
-
if 'interval' in state:
|
|
304
|
-
daughter_state['interval'] = state['interval']
|
|
305
|
-
|
|
306
|
-
divisions.append(daughter_state)
|
|
307
|
-
|
|
308
|
-
return divisions
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def serialize_process(schema, value, core):
|
|
312
|
-
"""Serialize a process to a JSON-safe representation."""
|
|
313
|
-
# TODO -- need to get back the protocol: address and the config
|
|
314
|
-
process = value.copy()
|
|
315
|
-
process['config'] = core.serialize(
|
|
316
|
-
process['instance'].config_schema,
|
|
317
|
-
process['config'])
|
|
318
|
-
del process['instance']
|
|
319
|
-
return process
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
def deserialize_process(schema, encoded, core):
|
|
323
|
-
"""Deserialize a process from a serialized state.
|
|
324
|
-
|
|
325
|
-
This function is used by the type system to deserialize a process.
|
|
326
|
-
|
|
327
|
-
:param encoded: A JSON-safe representation of the process.
|
|
328
|
-
:param bindings: The bindings to use for deserialization.
|
|
329
|
-
:param core: The type system to use for deserialization.
|
|
330
|
-
|
|
331
|
-
:returns: The deserialized state with an instantiated process.
|
|
332
|
-
"""
|
|
333
|
-
encoded = encoded or {}
|
|
334
|
-
schema = schema or {}
|
|
335
|
-
|
|
336
|
-
if not encoded:
|
|
337
|
-
deserialized = core.default(schema)
|
|
338
|
-
else:
|
|
339
|
-
deserialized = encoded
|
|
340
|
-
|
|
341
|
-
if not deserialized.get('address'):
|
|
342
|
-
return deserialized
|
|
343
|
-
|
|
344
|
-
protocol, address = deserialized['address'].split(':', 1)
|
|
345
|
-
|
|
346
|
-
if 'instance' in deserialized:
|
|
347
|
-
instantiate = type(deserialized['instance'])
|
|
348
|
-
else:
|
|
349
|
-
process_lookup = core.protocol_registry.access(protocol)
|
|
350
|
-
if not process_lookup:
|
|
351
|
-
raise Exception(f'protocol "{protocol}" not implemented')
|
|
352
|
-
|
|
353
|
-
instantiate = process_lookup(core, address)
|
|
354
|
-
if not instantiate:
|
|
355
|
-
raise Exception(f'process "{address}" not found')
|
|
356
|
-
|
|
357
|
-
config = core.deserialize(
|
|
358
|
-
instantiate.config_schema,
|
|
359
|
-
deserialized.get('config', {}))
|
|
360
|
-
|
|
361
|
-
interval = core.deserialize(
|
|
362
|
-
'interval',
|
|
363
|
-
deserialized.get('interval'))
|
|
364
|
-
|
|
365
|
-
if interval is None:
|
|
366
|
-
interval = core.default(
|
|
367
|
-
schema.get(
|
|
368
|
-
'interval',
|
|
369
|
-
'interval'))
|
|
370
|
-
|
|
371
|
-
if not 'instance' in deserialized:
|
|
372
|
-
process = instantiate(
|
|
373
|
-
config,
|
|
374
|
-
core=core)
|
|
375
|
-
|
|
376
|
-
deserialized['instance'] = process
|
|
377
|
-
|
|
378
|
-
deserialized['config'] = config
|
|
379
|
-
deserialized['interval'] = interval
|
|
380
|
-
deserialized['_inputs'] = copy.deepcopy(
|
|
381
|
-
deserialized['instance'].inputs())
|
|
382
|
-
deserialized['_outputs'] = copy.deepcopy(
|
|
383
|
-
deserialized['instance'].outputs())
|
|
384
|
-
|
|
385
|
-
return deserialized
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
def deserialize_step(schema, encoded, core):
|
|
389
|
-
if not encoded:
|
|
390
|
-
deserialized = core.default(schema)
|
|
391
|
-
else:
|
|
392
|
-
deserialized = copy.deepcopy(encoded)
|
|
393
|
-
|
|
394
|
-
if not deserialized['address']:
|
|
395
|
-
return deserialized
|
|
396
|
-
|
|
397
|
-
protocol, address = deserialized['address'].split(':', 1)
|
|
398
|
-
|
|
399
|
-
if 'instance' in deserialized:
|
|
400
|
-
instantiate = type(deserialized['instance'])
|
|
401
|
-
else:
|
|
402
|
-
process_lookup = core.protocol_registry.access(protocol)
|
|
403
|
-
if not process_lookup:
|
|
404
|
-
raise Exception(f'protocol "{protocol}" not implemented')
|
|
405
|
-
|
|
406
|
-
instantiate = process_lookup(core, address)
|
|
407
|
-
if not instantiate:
|
|
408
|
-
raise Exception(f'process "{address}" not found')
|
|
409
|
-
|
|
410
|
-
config = core.deserialize(
|
|
411
|
-
instantiate.config_schema,
|
|
412
|
-
deserialized.get('config', {}))
|
|
413
|
-
|
|
414
|
-
if not 'instance' in deserialized:
|
|
415
|
-
process = instantiate(config, core=core)
|
|
416
|
-
deserialized['instance'] = process
|
|
417
|
-
|
|
418
|
-
deserialized['config'] = config
|
|
419
|
-
deserialized['_inputs'] = copy.deepcopy(
|
|
420
|
-
deserialized['instance'].inputs())
|
|
421
|
-
deserialized['_outputs'] = copy.deepcopy(
|
|
422
|
-
deserialized['instance'].outputs())
|
|
423
|
-
|
|
424
|
-
return deserialized
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
PROCESS_TYPES = {
|
|
428
|
-
'protocol': {
|
|
429
|
-
'_type': 'protocol',
|
|
430
|
-
'_inherit': 'string'},
|
|
431
|
-
|
|
432
|
-
'emitter_mode': 'enum[none,all,stores,bridge,paths,ports]',
|
|
433
|
-
|
|
434
|
-
'interval': {
|
|
435
|
-
'_type': 'interval',
|
|
436
|
-
'_inherit': 'float',
|
|
437
|
-
'_apply': 'set',
|
|
438
|
-
'_default': '1.0'},
|
|
439
|
-
|
|
440
|
-
'step': {
|
|
441
|
-
'_type': 'step',
|
|
442
|
-
'_inherit': 'edge',
|
|
443
|
-
'_apply': apply_process,
|
|
444
|
-
'_serialize': serialize_process,
|
|
445
|
-
'_deserialize': deserialize_step,
|
|
446
|
-
'_check': check_process,
|
|
447
|
-
'_fold': fold_visit,
|
|
448
|
-
'_divide': divide_process,
|
|
449
|
-
'_description': '',
|
|
450
|
-
# TODO: support reference to type parameters from other states
|
|
451
|
-
'address': 'protocol',
|
|
452
|
-
'config': 'quote'},
|
|
453
|
-
|
|
454
|
-
# TODO: slice process to allow for navigating through a port
|
|
455
|
-
'process': {
|
|
456
|
-
'_type': 'process',
|
|
457
|
-
'_inherit': 'edge',
|
|
458
|
-
'_apply': apply_process,
|
|
459
|
-
'_serialize': serialize_process,
|
|
460
|
-
'_deserialize': deserialize_process,
|
|
461
|
-
'_check': check_process,
|
|
462
|
-
'_fold': fold_visit,
|
|
463
|
-
'_divide': divide_process,
|
|
464
|
-
'_description': '',
|
|
465
|
-
# TODO: support reference to type parameters from other states
|
|
466
|
-
'interval': 'interval',
|
|
467
|
-
'address': 'protocol',
|
|
468
|
-
'config': 'quote'}}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
BASE_PROTOCOLS = {
|
|
472
|
-
'local': local_lookup}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
# ===================
|
|
476
|
-
# Process Type System
|
|
477
|
-
# ===================
|
|
478
|
-
|
|
479
|
-
class ProcessTypes(TypeSystem):
|
|
480
|
-
"""
|
|
481
|
-
ProcessTypes class extends the TypeSystem class to include process types.
|
|
482
|
-
It maintains a registry of process types and provides methods to register
|
|
483
|
-
new process types, protocols, and emitters.
|
|
484
|
-
"""
|
|
485
|
-
|
|
486
|
-
def __init__(self):
|
|
487
|
-
super().__init__()
|
|
488
|
-
self.process_registry = Registry()
|
|
489
|
-
self.protocol_registry = Registry()
|
|
490
|
-
|
|
491
|
-
self.update_types(PROCESS_TYPES)
|
|
492
|
-
self.register_protocols(BASE_PROTOCOLS)
|
|
493
|
-
self.register_processes(BASE_EMITTERS)
|
|
494
|
-
|
|
495
|
-
self.register_process('composite', Composite)
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
def register_protocols(self, protocols):
|
|
499
|
-
"""Register protocols with the core"""
|
|
500
|
-
self.protocol_registry.register_multiple(protocols)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
def register_process(
|
|
504
|
-
self,
|
|
505
|
-
name,
|
|
506
|
-
process_data
|
|
507
|
-
):
|
|
508
|
-
"""
|
|
509
|
-
Registers a new process type in the process registry.
|
|
510
|
-
|
|
511
|
-
Args:
|
|
512
|
-
name (str): The name of the process type.
|
|
513
|
-
process_data: The data associated with the process type.
|
|
514
|
-
"""
|
|
515
|
-
self.process_registry.register(name, process_data)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
def register_processes(self, processes):
|
|
519
|
-
for process_key, process_data in processes.items():
|
|
520
|
-
self.register_process(
|
|
521
|
-
process_key,
|
|
522
|
-
process_data)
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
def initialize_edge_state(self, schema, path, edge):
|
|
526
|
-
"""
|
|
527
|
-
Initialize the state for an edge based on the schema and the edge.
|
|
528
|
-
"""
|
|
529
|
-
initial_state = edge['instance'].initial_state()
|
|
530
|
-
if not initial_state:
|
|
531
|
-
return initial_state
|
|
532
|
-
|
|
533
|
-
input_ports = copy.deepcopy(get_path(schema, path + ('_inputs',)))
|
|
534
|
-
output_ports = copy.deepcopy(get_path(schema, path + ('_outputs',)))
|
|
535
|
-
ports = {
|
|
536
|
-
'_inputs': input_ports,
|
|
537
|
-
'_outputs': output_ports}
|
|
538
|
-
|
|
539
|
-
input_state = {}
|
|
540
|
-
if input_ports:
|
|
541
|
-
input_state = self.project_edge(
|
|
542
|
-
ports,
|
|
543
|
-
edge,
|
|
544
|
-
path[:-1],
|
|
545
|
-
initial_state,
|
|
546
|
-
ports_key='inputs')
|
|
547
|
-
|
|
548
|
-
output_state = {}
|
|
549
|
-
if output_ports:
|
|
550
|
-
output_state = self.project_edge(
|
|
551
|
-
ports,
|
|
552
|
-
edge,
|
|
553
|
-
path[:-1],
|
|
554
|
-
initial_state,
|
|
555
|
-
ports_key='outputs')
|
|
556
|
-
|
|
557
|
-
state = deep_merge(input_state, output_state)
|
|
558
|
-
|
|
559
|
-
return state
|
|
560
|
-
|
|
561
|
-
|
|
562
247
|
# ===============
|
|
563
248
|
# Process Classes
|
|
564
249
|
# ===============
|
|
@@ -586,6 +271,10 @@ class Step(Edge):
|
|
|
586
271
|
return sync
|
|
587
272
|
|
|
588
273
|
|
|
274
|
+
def register_shared(self, instance):
|
|
275
|
+
self.instance = instance
|
|
276
|
+
|
|
277
|
+
|
|
589
278
|
def update(self, state):
|
|
590
279
|
return {}
|
|
591
280
|
|
|
@@ -703,16 +392,6 @@ class Composite(Process):
|
|
|
703
392
|
'bridge': {
|
|
704
393
|
'inputs': 'wires',
|
|
705
394
|
'outputs': 'wires'},
|
|
706
|
-
'emitter': {
|
|
707
|
-
'path': {
|
|
708
|
-
'_type': 'path',
|
|
709
|
-
'_default': ['emitter']},
|
|
710
|
-
'address': {
|
|
711
|
-
'_type': 'string',
|
|
712
|
-
'_default': 'local:ram-emitter'},
|
|
713
|
-
'config': 'tree[any]',
|
|
714
|
-
'mode': 'emitter_mode',
|
|
715
|
-
'emit': 'wires'},
|
|
716
395
|
'global_time_precision': 'maybe[float]'}
|
|
717
396
|
|
|
718
397
|
|
|
@@ -744,9 +423,6 @@ class Composite(Process):
|
|
|
744
423
|
initial_composition,
|
|
745
424
|
initial_state)
|
|
746
425
|
|
|
747
|
-
# self.composition = copy.deepcopy(
|
|
748
|
-
# self.core.access(composition))
|
|
749
|
-
|
|
750
426
|
# TODO: add flag to self.core.access(copy=True)
|
|
751
427
|
self.bridge = self.config.get('bridge', {})
|
|
752
428
|
|
|
@@ -778,10 +454,6 @@ class Composite(Process):
|
|
|
778
454
|
self.state,
|
|
779
455
|
edge_state)
|
|
780
456
|
|
|
781
|
-
# self.state = self.core.deserialize(
|
|
782
|
-
# self.composition,
|
|
783
|
-
# self.state)
|
|
784
|
-
|
|
785
457
|
# TODO: call validate on this composite, not just check
|
|
786
458
|
# assert self.core.validate(
|
|
787
459
|
# self.composition,
|
|
@@ -797,11 +469,6 @@ class Composite(Process):
|
|
|
797
469
|
self.global_time_precision = self.config[
|
|
798
470
|
'global_time_precision']
|
|
799
471
|
|
|
800
|
-
emitter_config = self.config.get('emitter')
|
|
801
|
-
if emitter_config and not emitter_config.get('mode', 'none') == 'none':
|
|
802
|
-
self.add_emitter(
|
|
803
|
-
emitter_config)
|
|
804
|
-
|
|
805
472
|
self.front: Dict = {
|
|
806
473
|
path: empty_front(self.state['global_time'])
|
|
807
474
|
for path in self.process_paths}
|
|
@@ -813,6 +480,7 @@ class Composite(Process):
|
|
|
813
480
|
|
|
814
481
|
# self.run_steps(self.to_run)
|
|
815
482
|
|
|
483
|
+
|
|
816
484
|
def build_step_network(self):
|
|
817
485
|
self.step_triggers = {}
|
|
818
486
|
for step_path, step in self.step_paths.items():
|
|
@@ -872,10 +540,6 @@ class Composite(Process):
|
|
|
872
540
|
print(f"Created new file: {filename}")
|
|
873
541
|
|
|
874
542
|
|
|
875
|
-
# def __repr__(self):
|
|
876
|
-
# return self.core.representation(self.composition)
|
|
877
|
-
|
|
878
|
-
|
|
879
543
|
def reset_step_state(self, step_paths):
|
|
880
544
|
self.trigger_state = build_trigger_state(
|
|
881
545
|
self.node_dependencies)
|
|
@@ -900,10 +564,6 @@ class Composite(Process):
|
|
|
900
564
|
state,
|
|
901
565
|
'process_bigraph.composite.Step')
|
|
902
566
|
|
|
903
|
-
self.emitter_paths = find_instance_paths(
|
|
904
|
-
state,
|
|
905
|
-
'process_bigraph.composite.Emitter')
|
|
906
|
-
|
|
907
567
|
|
|
908
568
|
def inputs(self):
|
|
909
569
|
return self.process_schema.get('inputs', {})
|
|
@@ -913,63 +573,6 @@ class Composite(Process):
|
|
|
913
573
|
return self.process_schema.get('outputs', {})
|
|
914
574
|
|
|
915
575
|
|
|
916
|
-
def read_emitter_config(self, emitter_config):
|
|
917
|
-
|
|
918
|
-
# upcoming deprecation warning
|
|
919
|
-
print("Warning: read_emitter_config() is deprecated and will be removed in a future version. "
|
|
920
|
-
"Use use Vivarium for managing simulations and emitters instead of Composite.")
|
|
921
|
-
|
|
922
|
-
address = emitter_config.get('address', 'local:ram-emitter')
|
|
923
|
-
config = emitter_config.get('config', {})
|
|
924
|
-
mode = emitter_config.get('mode', 'none')
|
|
925
|
-
|
|
926
|
-
if mode == 'all':
|
|
927
|
-
inputs = {
|
|
928
|
-
key: [emitter_config.get('inputs', {}).get(key, key)]
|
|
929
|
-
for key in self.state.keys()
|
|
930
|
-
if not is_schema_key(key)}
|
|
931
|
-
|
|
932
|
-
elif mode == 'none':
|
|
933
|
-
inputs = emitter_config.get('emit', {})
|
|
934
|
-
|
|
935
|
-
elif mode == 'bridge':
|
|
936
|
-
inputs = {}
|
|
937
|
-
|
|
938
|
-
elif mode == 'ports':
|
|
939
|
-
inputs = {}
|
|
940
|
-
|
|
941
|
-
if not 'emit' in config:
|
|
942
|
-
config['emit'] = {
|
|
943
|
-
input: 'any'
|
|
944
|
-
for input in inputs}
|
|
945
|
-
|
|
946
|
-
return {
|
|
947
|
-
'_type': 'step',
|
|
948
|
-
'address': address,
|
|
949
|
-
'config': config,
|
|
950
|
-
'inputs': inputs}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
def add_emitter(self, emitter_config):
|
|
954
|
-
path = tuple(emitter_config['path'])
|
|
955
|
-
|
|
956
|
-
step_config = self.read_emitter_config(emitter_config)
|
|
957
|
-
emitter = set_path(
|
|
958
|
-
{}, path, step_config)
|
|
959
|
-
|
|
960
|
-
self.merge(
|
|
961
|
-
{},
|
|
962
|
-
emitter)
|
|
963
|
-
|
|
964
|
-
_, instance = self.core.slice(
|
|
965
|
-
self.composition,
|
|
966
|
-
self.state,
|
|
967
|
-
path)
|
|
968
|
-
|
|
969
|
-
self.emitter_paths[path] = instance
|
|
970
|
-
self.step_paths[path] = instance
|
|
971
|
-
|
|
972
|
-
|
|
973
576
|
def merge(self, schema, state, path=None):
|
|
974
577
|
path = path or []
|
|
975
578
|
self.composition, self.state = self.core.merge(
|
|
@@ -1209,16 +812,6 @@ class Composite(Process):
|
|
|
1209
812
|
self.expire_process_paths(update_paths)
|
|
1210
813
|
self.trigger_steps(update_paths)
|
|
1211
814
|
|
|
1212
|
-
# # display and emit
|
|
1213
|
-
# if self.progress_bar:
|
|
1214
|
-
# print_progress_bar(self.global_time, end_time)
|
|
1215
|
-
# if self.emit_step == 1:
|
|
1216
|
-
# self._emit_store_data()
|
|
1217
|
-
# elif emit_time <= self.global_time:
|
|
1218
|
-
# while emit_time <= self.global_time:
|
|
1219
|
-
# self._emit_store_data()
|
|
1220
|
-
# emit_time += self.emit_step
|
|
1221
|
-
|
|
1222
815
|
else:
|
|
1223
816
|
# all processes have run past the interval
|
|
1224
817
|
self.state['global_time'] = end_time
|
|
@@ -1283,27 +876,6 @@ class Composite(Process):
|
|
|
1283
876
|
self.run_steps(to_run)
|
|
1284
877
|
|
|
1285
878
|
|
|
1286
|
-
def gather_results(self, queries=None):
|
|
1287
|
-
'''
|
|
1288
|
-
a map of paths to emitter --> queries for the emitter at that path
|
|
1289
|
-
'''
|
|
1290
|
-
|
|
1291
|
-
if queries is None:
|
|
1292
|
-
queries = {
|
|
1293
|
-
path: None
|
|
1294
|
-
for path in self.emitter_paths.keys()}
|
|
1295
|
-
|
|
1296
|
-
results = {}
|
|
1297
|
-
for path, query in queries.items():
|
|
1298
|
-
emitter = get_path(self.state, path)
|
|
1299
|
-
results[path] = emitter['instance'].query(query)
|
|
1300
|
-
|
|
1301
|
-
# TODO: unnest the results?
|
|
1302
|
-
# TODO: allow the results to be transposed
|
|
1303
|
-
|
|
1304
|
-
return results
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
879
|
def update(self, state, interval):
|
|
1308
880
|
# do everything
|
|
1309
881
|
|
|
@@ -1325,81 +897,3 @@ class Composite(Process):
|
|
|
1325
897
|
return updates
|
|
1326
898
|
|
|
1327
899
|
|
|
1328
|
-
# ========
|
|
1329
|
-
# Emitters
|
|
1330
|
-
# ========
|
|
1331
|
-
# Emitters are steps that observe the state of the system and emit it to an external source.
|
|
1332
|
-
# This could be to a database, to a file, or to the console.
|
|
1333
|
-
|
|
1334
|
-
class Emitter(Step):
|
|
1335
|
-
"""Base emitter class.
|
|
1336
|
-
|
|
1337
|
-
An `Emitter` implementation instance diverts all querying of data to
|
|
1338
|
-
the primary historical collection whose type pertains to Emitter child, i.e:
|
|
1339
|
-
database-emitter=>`pymongo.Collection`, ram-emitter=>`.RamEmitter.history`(`List`)
|
|
1340
|
-
"""
|
|
1341
|
-
config_schema = {
|
|
1342
|
-
'emit': 'schema'}
|
|
1343
|
-
|
|
1344
|
-
def inputs(self) -> Dict:
|
|
1345
|
-
return self.config['emit']
|
|
1346
|
-
|
|
1347
|
-
def query(self, query=None):
|
|
1348
|
-
return {}
|
|
1349
|
-
|
|
1350
|
-
def update(self, state) -> Dict:
|
|
1351
|
-
return {}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
class ConsoleEmitter(Emitter):
|
|
1355
|
-
"""Console emitter class.
|
|
1356
|
-
|
|
1357
|
-
This emitter logs the state to the console.
|
|
1358
|
-
"""
|
|
1359
|
-
|
|
1360
|
-
def update(self, state) -> Dict:
|
|
1361
|
-
print(state)
|
|
1362
|
-
return {}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
class RAMEmitter(Emitter):
|
|
1366
|
-
"""RAM emitter class.
|
|
1367
|
-
|
|
1368
|
-
This emitter logs the state to a list in memory.
|
|
1369
|
-
"""
|
|
1370
|
-
|
|
1371
|
-
def __init__(self, config, core):
|
|
1372
|
-
super().__init__(config, core)
|
|
1373
|
-
self.history = []
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
def update(self, state) -> Dict:
|
|
1377
|
-
self.history.append(copy.deepcopy(state))
|
|
1378
|
-
return {}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
def query(self, query=None):
|
|
1382
|
-
"""
|
|
1383
|
-
Query the history of the emitter.
|
|
1384
|
-
:param query: a list of paths to query from the history. If None, the entire history is returned.
|
|
1385
|
-
:return: results of the query in a list
|
|
1386
|
-
"""
|
|
1387
|
-
if isinstance(query, list):
|
|
1388
|
-
results = []
|
|
1389
|
-
for t in self.history:
|
|
1390
|
-
result = {}
|
|
1391
|
-
for path in query:
|
|
1392
|
-
element = get_path(t, path)
|
|
1393
|
-
result = set_path(result, path, element)
|
|
1394
|
-
results.append(result)
|
|
1395
|
-
# element = get_path(self.history, path)
|
|
1396
|
-
# result = set_path(result, path, element)
|
|
1397
|
-
else:
|
|
1398
|
-
results = self.history
|
|
1399
|
-
|
|
1400
|
-
return results
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
BASE_EMITTERS = {
|
|
1404
|
-
'console-emitter': ConsoleEmitter,
|
|
1405
|
-
'ram-emitter': RAMEmitter}
|