process-bigraph 0.0.5__tar.gz → 0.0.7__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.5/process_bigraph.egg-info → process-bigraph-0.0.7}/PKG-INFO +1 -6
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/__init__.py +1 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/composite.py +219 -24
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/registry.py +1 -0
- process-bigraph-0.0.7/process_bigraph/tests.py +354 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/type_system.py +37 -15
- {process-bigraph-0.0.5 → process-bigraph-0.0.7/process_bigraph.egg-info}/PKG-INFO +1 -6
- process-bigraph-0.0.7/process_bigraph.egg-info/requires.txt +3 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/setup.py +4 -2
- process-bigraph-0.0.5/process_bigraph/tests.py +0 -138
- process-bigraph-0.0.5/process_bigraph.egg-info/requires.txt +0 -1
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/AUTHORS.md +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/LICENSE +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/README.md +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/emitter.py +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/experiments/__init__.py +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/experiments/minimal_gillespie.py +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/processes.py +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/protocols.py +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/SOURCES.txt +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/dependency_links.txt +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/top_level.txt +0 -0
- {process-bigraph-0.0.5 → process-bigraph-0.0.7}/setup.cfg +0 -0
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: process-bigraph
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: UNKNOWN
|
|
3
|
+
Version: 0.0.7
|
|
5
4
|
Home-page: https://github.com/vivarium-collective/process-bigraph
|
|
6
5
|
Author: Ryan Spangler, Eran Agmon
|
|
7
6
|
Author-email: ryan.spangler@gmail.com, agmon.eran@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
7
|
Classifier: Development Status :: 3 - Alpha
|
|
11
8
|
Classifier: Intended Audience :: Developers
|
|
12
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -73,5 +70,3 @@ diagraph of a whole-cell E. coli model.
|
|
|
73
70
|
## License
|
|
74
71
|
|
|
75
72
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
76
|
-
|
|
77
|
-
|
|
@@ -244,7 +244,10 @@ def find_instance_paths(state, instance_type='process_bigraph.composite.Process'
|
|
|
244
244
|
def find_step_triggers(path, step):
|
|
245
245
|
prefix = tuple(path[:-1])
|
|
246
246
|
triggers = {}
|
|
247
|
-
|
|
247
|
+
wire_paths = find_leaves(
|
|
248
|
+
step['wires']['inputs'])
|
|
249
|
+
|
|
250
|
+
for wire in wire_paths:
|
|
248
251
|
trigger_path = tuple(prefix) + tuple(wire)
|
|
249
252
|
if trigger_path not in triggers:
|
|
250
253
|
triggers[trigger_path] = []
|
|
@@ -291,18 +294,139 @@ def empty_front(time):
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
def find_leaves(d, path=None):
|
|
298
|
+
leaves = []
|
|
299
|
+
path = ()
|
|
300
|
+
|
|
301
|
+
for key, value in d.items():
|
|
302
|
+
if isinstance(value, dict):
|
|
303
|
+
subleaves = find_leaves(value, path + (key,))
|
|
304
|
+
leaves.extend(subleaves)
|
|
305
|
+
else:
|
|
306
|
+
leaves.append(path + tuple(value))
|
|
307
|
+
|
|
308
|
+
return leaves
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def build_step_network(steps):
|
|
312
|
+
ancestors = {
|
|
313
|
+
step_key: {
|
|
314
|
+
# 'ancestors': [],
|
|
315
|
+
'input_paths': None,
|
|
316
|
+
'output_paths': None}
|
|
317
|
+
for step_key in steps}
|
|
318
|
+
|
|
319
|
+
nodes = {}
|
|
320
|
+
|
|
321
|
+
for step_key, step in steps.items():
|
|
322
|
+
for other_key, other_step in steps.items():
|
|
323
|
+
if step_key == other_key:
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
schema = step['instance'].schema()
|
|
327
|
+
wires = step['wires']
|
|
328
|
+
other_schema = other_step['instance'].schema()
|
|
329
|
+
other_wires = other_step['wires']
|
|
330
|
+
|
|
331
|
+
if ancestors[step_key]['input_paths'] is None:
|
|
332
|
+
ancestors[step_key]['input_paths'] = find_leaves(
|
|
333
|
+
wires['inputs'])
|
|
334
|
+
input_paths = ancestors[step_key]['input_paths']
|
|
335
|
+
|
|
336
|
+
if ancestors[step_key]['output_paths'] is None:
|
|
337
|
+
ancestors[step_key]['output_paths'] = find_leaves(
|
|
338
|
+
wires.get('outputs', {}))
|
|
339
|
+
output_paths = ancestors[step_key]['output_paths']
|
|
340
|
+
|
|
341
|
+
for input in input_paths:
|
|
342
|
+
path = tuple(input)
|
|
343
|
+
if not path in nodes:
|
|
344
|
+
nodes[path] = {
|
|
345
|
+
'before': set([]),
|
|
346
|
+
'after': set([])}
|
|
347
|
+
nodes[path]['after'].add(step_key)
|
|
348
|
+
|
|
349
|
+
for output in output_paths:
|
|
350
|
+
path = tuple(output)
|
|
351
|
+
if not path in nodes:
|
|
352
|
+
nodes[path] = {
|
|
353
|
+
'before': set([]),
|
|
354
|
+
'after': set([])}
|
|
355
|
+
nodes[path]['before'].add(step_key)
|
|
356
|
+
|
|
357
|
+
return ancestors, nodes
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def combined_step_network(steps):
|
|
361
|
+
steps, nodes = build_step_network(steps)
|
|
362
|
+
|
|
363
|
+
trigger_state = {
|
|
364
|
+
'steps': steps,
|
|
365
|
+
'nodes': nodes}
|
|
366
|
+
|
|
367
|
+
return trigger_state
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def build_trigger_state(nodes):
|
|
371
|
+
return {
|
|
372
|
+
key: value['before'].copy()
|
|
373
|
+
for key, value in nodes.items()}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def find_downstream(steps, nodes, upstream):
|
|
377
|
+
downstream = set(upstream)
|
|
378
|
+
visited = set([])
|
|
379
|
+
previous_len = -1
|
|
380
|
+
|
|
381
|
+
while len(downstream) > len(visited) and len(visited) > previous_len:
|
|
382
|
+
previous_len = len(visited)
|
|
383
|
+
down = set([])
|
|
384
|
+
for step_path in downstream:
|
|
385
|
+
if step_path not in visited:
|
|
386
|
+
for output in steps[step_path]['output_paths']:
|
|
387
|
+
for dependent in nodes[output]['after']:
|
|
388
|
+
down.add(dependent)
|
|
389
|
+
visited.add(step_path)
|
|
390
|
+
downstream ^= down
|
|
391
|
+
|
|
392
|
+
return downstream
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def determine_steps(steps, remaining, fulfilled):
|
|
396
|
+
to_run = []
|
|
397
|
+
|
|
398
|
+
for step_path in remaining:
|
|
399
|
+
step_inputs = steps[step_path]['input_paths']
|
|
400
|
+
all_fulfilled = True
|
|
401
|
+
for input in step_inputs:
|
|
402
|
+
if len(fulfilled[input]) > 0:
|
|
403
|
+
all_fulfilled = False
|
|
404
|
+
if all_fulfilled:
|
|
405
|
+
to_run.append(step_path)
|
|
406
|
+
|
|
407
|
+
for step_path in to_run:
|
|
408
|
+
remaining.remove(step_path)
|
|
409
|
+
step_outputs = steps[step_path]['output_paths']
|
|
410
|
+
for output in step_outputs:
|
|
411
|
+
fulfilled[output].remove(step_path)
|
|
412
|
+
|
|
413
|
+
return to_run, remaining, fulfilled
|
|
414
|
+
|
|
296
415
|
|
|
416
|
+
class Composite(Process):
|
|
417
|
+
"""
|
|
418
|
+
Composite parent class.
|
|
297
419
|
"""
|
|
420
|
+
|
|
421
|
+
|
|
298
422
|
config_schema = {
|
|
299
423
|
# TODO: add schema type
|
|
300
424
|
'composition': 'tree[any]',
|
|
301
425
|
'state': 'tree[any]',
|
|
302
426
|
'schema': 'tree[any]',
|
|
303
427
|
'bridge': 'wires',
|
|
304
|
-
'global_time_precision': 'maybe[float]'
|
|
305
|
-
|
|
428
|
+
'global_time_precision': 'maybe[float]'}
|
|
429
|
+
|
|
306
430
|
|
|
307
431
|
# TODO: if processes are serialized, deserialize them first
|
|
308
432
|
def __init__(self, config=None, local_types=None):
|
|
@@ -311,11 +435,17 @@ class Composite(Process):
|
|
|
311
435
|
initial_composition = self.config.get('composition', {})
|
|
312
436
|
if 'global_time' not in initial_composition:
|
|
313
437
|
initial_composition['global_time'] = 'float'
|
|
438
|
+
initial_composition = types.access(
|
|
439
|
+
initial_composition)
|
|
314
440
|
|
|
315
441
|
initial_state = self.config.get('state', {})
|
|
316
442
|
if 'global_time' not in initial_state:
|
|
317
443
|
initial_state['global_time'] = 0.0
|
|
318
444
|
|
|
445
|
+
initial_state = types.hydrate(
|
|
446
|
+
initial_composition,
|
|
447
|
+
initial_state)
|
|
448
|
+
|
|
319
449
|
initial_schema = types.access(
|
|
320
450
|
self.config.get('schema', {})) or {}
|
|
321
451
|
self.bridge = self.config.get('bridge', {})
|
|
@@ -394,11 +524,35 @@ class Composite(Process):
|
|
|
394
524
|
|
|
395
525
|
self.bridge_updates = []
|
|
396
526
|
|
|
397
|
-
self.
|
|
527
|
+
self.step_dependencies, self.node_dependencies = build_step_network(
|
|
528
|
+
self.step_paths)
|
|
529
|
+
|
|
530
|
+
self.reset_step_state(self.step_paths)
|
|
531
|
+
to_run = self.cycle_step_state()
|
|
532
|
+
|
|
533
|
+
self.run_steps(to_run)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def reset_step_state(self, step_paths):
|
|
537
|
+
self.trigger_state = build_trigger_state(
|
|
538
|
+
self.node_dependencies)
|
|
539
|
+
|
|
540
|
+
self.steps_remaining = set(step_paths)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def cycle_step_state(self):
|
|
544
|
+
to_run, self.steps_remaining, self.trigger_state = determine_steps(
|
|
545
|
+
self.step_dependencies,
|
|
546
|
+
self.steps_remaining,
|
|
547
|
+
self.trigger_state)
|
|
548
|
+
|
|
549
|
+
return to_run
|
|
550
|
+
|
|
398
551
|
|
|
399
552
|
def schema(self):
|
|
400
553
|
return self.process_schema
|
|
401
554
|
|
|
555
|
+
|
|
402
556
|
def process_update(
|
|
403
557
|
self,
|
|
404
558
|
path,
|
|
@@ -446,6 +600,7 @@ class Composite(Process):
|
|
|
446
600
|
|
|
447
601
|
return absolute
|
|
448
602
|
|
|
603
|
+
|
|
449
604
|
def run_process(self, path, process, end_time, full_step, force_complete):
|
|
450
605
|
if path not in self.front:
|
|
451
606
|
self.front[path] = empty_front(self.state['global_time'])
|
|
@@ -516,8 +671,6 @@ class Composite(Process):
|
|
|
516
671
|
series = [series]
|
|
517
672
|
|
|
518
673
|
for update in series:
|
|
519
|
-
# print(update)
|
|
520
|
-
|
|
521
674
|
paths = hierarchy_depth(update)
|
|
522
675
|
update_paths.extend(paths.keys())
|
|
523
676
|
|
|
@@ -535,7 +688,7 @@ class Composite(Process):
|
|
|
535
688
|
if bridge_update:
|
|
536
689
|
self.bridge_updates.append(bridge_update)
|
|
537
690
|
|
|
538
|
-
|
|
691
|
+
return update_paths
|
|
539
692
|
|
|
540
693
|
# view_expire_update = self.apply_update(up, store)
|
|
541
694
|
# view_expire = view_expire or view_expire_update
|
|
@@ -543,6 +696,7 @@ class Composite(Process):
|
|
|
543
696
|
# if view_expire:
|
|
544
697
|
# self.state.build_topology_views()
|
|
545
698
|
|
|
699
|
+
|
|
546
700
|
def run(self, interval, force_complete=False):
|
|
547
701
|
end_time = self.state['global_time'] + interval
|
|
548
702
|
while self.state['global_time'] < end_time or force_complete:
|
|
@@ -581,7 +735,10 @@ class Composite(Process):
|
|
|
581
735
|
advance['update'] = {}
|
|
582
736
|
paths.append(path)
|
|
583
737
|
|
|
584
|
-
|
|
738
|
+
# get all update paths, then trigger steps that
|
|
739
|
+
# depend on those paths
|
|
740
|
+
update_paths = self.apply_updates(updates)
|
|
741
|
+
self.trigger_steps(update_paths)
|
|
585
742
|
|
|
586
743
|
# # display and emit
|
|
587
744
|
# if self.progress_bar:
|
|
@@ -600,22 +757,34 @@ class Composite(Process):
|
|
|
600
757
|
if force_complete and self.state['global_time'] == end_time:
|
|
601
758
|
force_complete = False
|
|
602
759
|
|
|
603
|
-
def run_steps(self, update_paths):
|
|
604
|
-
steps_to_run = []
|
|
605
760
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
761
|
+
def determine_steps(self):
|
|
762
|
+
to_run = []
|
|
763
|
+
for step_key, wires in trigger_state['steps']:
|
|
764
|
+
fulfilled = True
|
|
765
|
+
for input in wires['input_paths']:
|
|
766
|
+
if len(trigger_state['states'][tuple(input)]) > 0:
|
|
767
|
+
fulfilled = False
|
|
768
|
+
break
|
|
769
|
+
if fulfilled:
|
|
770
|
+
to_run.append(step_key)
|
|
614
771
|
|
|
615
|
-
|
|
772
|
+
for step_key in to_run:
|
|
773
|
+
wires = trigger_state['steps'][step_key]
|
|
774
|
+
for output in wires['output_paths']:
|
|
775
|
+
trigger_state['states'][tuple(output)].remove(step_key)
|
|
776
|
+
|
|
777
|
+
return to_run, trigger_state
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def run_steps(self, step_paths):
|
|
781
|
+
if len(step_paths) > 0:
|
|
616
782
|
updates = []
|
|
617
|
-
for step_path in
|
|
618
|
-
step = get_path(
|
|
783
|
+
for step_path in step_paths:
|
|
784
|
+
step = get_path(
|
|
785
|
+
self.state,
|
|
786
|
+
step_path)
|
|
787
|
+
|
|
619
788
|
state = types.view_edge(
|
|
620
789
|
self.composition,
|
|
621
790
|
self.state,
|
|
@@ -631,11 +800,37 @@ class Composite(Process):
|
|
|
631
800
|
|
|
632
801
|
updates.append(step_update)
|
|
633
802
|
|
|
634
|
-
self.apply_updates(updates)
|
|
803
|
+
update_paths = self.apply_updates(updates)
|
|
804
|
+
to_run = self.cycle_step_state()
|
|
805
|
+
if len(to_run) > 0:
|
|
806
|
+
self.run_steps(to_run)
|
|
635
807
|
else:
|
|
636
808
|
self.steps_run = set([])
|
|
637
809
|
|
|
638
810
|
|
|
811
|
+
def trigger_steps(self, update_paths):
|
|
812
|
+
steps_to_run = []
|
|
813
|
+
|
|
814
|
+
for update_path in update_paths:
|
|
815
|
+
paths = explode_path(update_path)
|
|
816
|
+
for path in paths:
|
|
817
|
+
step_paths = self.step_triggers.get(path, [])
|
|
818
|
+
for step_path in step_paths:
|
|
819
|
+
if step_path is not None and step_path not in self.steps_run:
|
|
820
|
+
steps_to_run.append(step_path)
|
|
821
|
+
self.steps_run.add(step_path)
|
|
822
|
+
|
|
823
|
+
steps_to_run = find_downstream(
|
|
824
|
+
self.step_dependencies,
|
|
825
|
+
self.node_dependencies,
|
|
826
|
+
steps_to_run)
|
|
827
|
+
|
|
828
|
+
self.reset_step_state(steps_to_run)
|
|
829
|
+
to_run = self.cycle_step_state()
|
|
830
|
+
|
|
831
|
+
self.run_steps(to_run)
|
|
832
|
+
|
|
833
|
+
|
|
639
834
|
def gather_results(self, queries=None):
|
|
640
835
|
'''
|
|
641
836
|
a map of paths to emitters --> queries for the emitter at that path
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for Process Bigraph
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from process_bigraph.composite import Process, Step, Composite
|
|
8
|
+
from process_bigraph.composite import merge_collections
|
|
9
|
+
from process_bigraph.type_system import types
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IncreaseProcess(Process):
|
|
13
|
+
config_schema = {
|
|
14
|
+
'rate': {
|
|
15
|
+
'_type': 'float',
|
|
16
|
+
'_default': '0.1'}}
|
|
17
|
+
|
|
18
|
+
def __init__(self, config=None):
|
|
19
|
+
super().__init__(config)
|
|
20
|
+
|
|
21
|
+
def schema(self):
|
|
22
|
+
return {
|
|
23
|
+
'level': 'float'}
|
|
24
|
+
|
|
25
|
+
def update(self, state, interval):
|
|
26
|
+
return {
|
|
27
|
+
'level': state['level'] * self.config['rate']}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_default_config():
|
|
31
|
+
process = IncreaseProcess()
|
|
32
|
+
assert process.config['rate'] == 0.1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_merge_collections():
|
|
36
|
+
a = {('what',): [1, 2, 3]}
|
|
37
|
+
b = {('okay', 'yes'): [3, 3], ('what',): [4, 5, 11]}
|
|
38
|
+
|
|
39
|
+
c = merge_collections(a, b)
|
|
40
|
+
|
|
41
|
+
assert c[('what',)] == [1, 2, 3, 4, 5, 11]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_process():
|
|
45
|
+
process = IncreaseProcess({'rate': 0.2})
|
|
46
|
+
schema = process.schema()
|
|
47
|
+
state = types.fill(schema)
|
|
48
|
+
update = process.update({'level': 5.5}, 1.0)
|
|
49
|
+
new_state = types.apply(schema, state, update)
|
|
50
|
+
|
|
51
|
+
assert new_state['level'] == 1.1
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_composite():
|
|
55
|
+
# TODO: add support for the various vivarium emitters
|
|
56
|
+
|
|
57
|
+
# increase = IncreaseProcess({'rate': 0.3})
|
|
58
|
+
# TODO: This is the config of the composite,
|
|
59
|
+
# we also need a way to serialize the entire composite
|
|
60
|
+
|
|
61
|
+
composite = Composite({
|
|
62
|
+
'composition': {
|
|
63
|
+
'increase': 'process[level:float]',
|
|
64
|
+
'value': 'float'},
|
|
65
|
+
'schema': {
|
|
66
|
+
'exchange': 'float'},
|
|
67
|
+
'bridge': {
|
|
68
|
+
'exchange': ['value']},
|
|
69
|
+
'state': {
|
|
70
|
+
'increase': {
|
|
71
|
+
'address': 'local:!process_bigraph.tests.IncreaseProcess',
|
|
72
|
+
'config': {'rate': '0.3'},
|
|
73
|
+
'interval': '1.0',
|
|
74
|
+
'wires': {'level': ['value']}},
|
|
75
|
+
'value': '11.11'}})
|
|
76
|
+
|
|
77
|
+
initial_state = {'exchange': 3.33}
|
|
78
|
+
|
|
79
|
+
updates = composite.update(initial_state, 10.0)
|
|
80
|
+
|
|
81
|
+
final_exchange = sum([
|
|
82
|
+
update['exchange']
|
|
83
|
+
for update in [initial_state] + updates])
|
|
84
|
+
|
|
85
|
+
assert composite.state['value'] > 45
|
|
86
|
+
assert 'exchange' in updates[0]
|
|
87
|
+
assert updates[0]['exchange'] == 0.999
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_infer():
|
|
91
|
+
composite = Composite({
|
|
92
|
+
'state': {
|
|
93
|
+
'increase': {
|
|
94
|
+
'_type': 'process',
|
|
95
|
+
'address': 'local:!process_bigraph.tests.IncreaseProcess',
|
|
96
|
+
'config': {'rate': '0.3'},
|
|
97
|
+
'wires': {'level': ['value']}},
|
|
98
|
+
'value': '11.11'}})
|
|
99
|
+
|
|
100
|
+
assert composite.composition['value']['_type'] == 'float'
|
|
101
|
+
assert composite.state['value'] == 11.11
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class OperatorStep(Step):
|
|
105
|
+
config_schema = {
|
|
106
|
+
'operator': 'string'}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def schema(self):
|
|
110
|
+
return {
|
|
111
|
+
'inputs': {
|
|
112
|
+
'a': 'float',
|
|
113
|
+
'b': 'float'},
|
|
114
|
+
'outputs': {
|
|
115
|
+
'c': 'float'}}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def update(self, inputs):
|
|
119
|
+
a = inputs['a']
|
|
120
|
+
b = inputs['b']
|
|
121
|
+
|
|
122
|
+
if self.config['operator'] == '+':
|
|
123
|
+
c = a + b
|
|
124
|
+
elif self.config['operator'] == '*':
|
|
125
|
+
c = a * b
|
|
126
|
+
elif self.config['operator'] == '-':
|
|
127
|
+
c = a - b
|
|
128
|
+
|
|
129
|
+
return {'c': c}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_step_initialization():
|
|
133
|
+
composite = Composite({
|
|
134
|
+
'state': {
|
|
135
|
+
'A': 13,
|
|
136
|
+
'B': 21,
|
|
137
|
+
'step1': {
|
|
138
|
+
'_type': 'step',
|
|
139
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
140
|
+
'config': {
|
|
141
|
+
'operator': '+'},
|
|
142
|
+
# TODO: avoid inputs/outputs key in wires?
|
|
143
|
+
'wires': {
|
|
144
|
+
'inputs': {
|
|
145
|
+
'a': ['A'],
|
|
146
|
+
'b': ['B']},
|
|
147
|
+
'outputs': {
|
|
148
|
+
'c': ['C']}}},
|
|
149
|
+
'step2': {
|
|
150
|
+
'_type': 'step',
|
|
151
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
152
|
+
'config': {
|
|
153
|
+
'operator': '*'},
|
|
154
|
+
'wires': {
|
|
155
|
+
'inputs': {
|
|
156
|
+
'a': ['B'],
|
|
157
|
+
'b': ['C']},
|
|
158
|
+
'outputs': {
|
|
159
|
+
'c': ['D']}}}}})
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
assert composite.state['D'] == (13 + 21) * 21
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_dependencies():
|
|
166
|
+
operation = {
|
|
167
|
+
'a': 11.111,
|
|
168
|
+
'b': 22.2,
|
|
169
|
+
'c': 555.555,
|
|
170
|
+
|
|
171
|
+
'1': {
|
|
172
|
+
'_type': 'step',
|
|
173
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
174
|
+
'config': {
|
|
175
|
+
'operator': '+'},
|
|
176
|
+
'wires': {
|
|
177
|
+
'inputs': {
|
|
178
|
+
'a': ['a'],
|
|
179
|
+
'b': ['b']},
|
|
180
|
+
'outputs': {
|
|
181
|
+
'c': ['e']}}},
|
|
182
|
+
'2.1': {
|
|
183
|
+
'_type': 'step',
|
|
184
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
185
|
+
'config': {
|
|
186
|
+
'operator': '-'},
|
|
187
|
+
'wires': {
|
|
188
|
+
'inputs': {
|
|
189
|
+
'a': ['c'],
|
|
190
|
+
'b': ['e']},
|
|
191
|
+
'outputs': {
|
|
192
|
+
'c': ['f']}}},
|
|
193
|
+
'2.2': {
|
|
194
|
+
'_type': 'step',
|
|
195
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
196
|
+
'config': {
|
|
197
|
+
'operator': '-'},
|
|
198
|
+
'wires': {
|
|
199
|
+
'inputs': {
|
|
200
|
+
'a': ['d'],
|
|
201
|
+
'b': ['e']},
|
|
202
|
+
'outputs': {
|
|
203
|
+
'c': ['g']}}},
|
|
204
|
+
'3': {
|
|
205
|
+
'_type': 'step',
|
|
206
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
207
|
+
'config': {
|
|
208
|
+
'operator': '*'},
|
|
209
|
+
'wires': {
|
|
210
|
+
'inputs': {
|
|
211
|
+
'a': ['f'],
|
|
212
|
+
'b': ['g']},
|
|
213
|
+
'outputs': {
|
|
214
|
+
'c': ['h']}}},
|
|
215
|
+
'4': {
|
|
216
|
+
'_type': 'step',
|
|
217
|
+
'address': 'local:!process_bigraph.tests.OperatorStep',
|
|
218
|
+
'config': {
|
|
219
|
+
'operator': '+'},
|
|
220
|
+
'wires': {
|
|
221
|
+
'inputs': {
|
|
222
|
+
'a': ['e'],
|
|
223
|
+
'b': ['h']},
|
|
224
|
+
'outputs': {
|
|
225
|
+
'c': ['i']}}}}
|
|
226
|
+
|
|
227
|
+
composite = Composite({'state': operation})
|
|
228
|
+
|
|
229
|
+
assert composite.state['h'] == -17396.469884
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_dependency_cycle():
|
|
233
|
+
# test a step network with cycles in a few ways
|
|
234
|
+
pass
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class SimpleCompartment(Process):
|
|
238
|
+
config_schema = {
|
|
239
|
+
'id': 'string'}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def schema(self):
|
|
243
|
+
return {
|
|
244
|
+
'outer': 'tree[process]',
|
|
245
|
+
'inner': 'tree[process]'}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def update(self, state, interval):
|
|
249
|
+
choice = random.random()
|
|
250
|
+
update = {}
|
|
251
|
+
|
|
252
|
+
outer = state['outer']
|
|
253
|
+
inner = state['inner']
|
|
254
|
+
|
|
255
|
+
# TODO: implement divide_state(_)
|
|
256
|
+
divisions = self.types.divide_state(
|
|
257
|
+
self.schema(),
|
|
258
|
+
inner)
|
|
259
|
+
|
|
260
|
+
if choice < 0.2:
|
|
261
|
+
# update = {
|
|
262
|
+
# 'outer': {
|
|
263
|
+
# '_divide': {
|
|
264
|
+
# 'mother': self.config['id'],
|
|
265
|
+
# 'daughters': [
|
|
266
|
+
# {'id': self.config['id'] + '0'},
|
|
267
|
+
# {'id': self.config['id'] + '1'}]}}}
|
|
268
|
+
|
|
269
|
+
# daughter_ids = [self.config['id'] + str(i)
|
|
270
|
+
# for i in range(2)]
|
|
271
|
+
|
|
272
|
+
# update = {
|
|
273
|
+
# 'outer': {
|
|
274
|
+
# '_react': {
|
|
275
|
+
# 'redex': {
|
|
276
|
+
# 'inner': {
|
|
277
|
+
# self.config['id']: {}}},
|
|
278
|
+
# 'reactum': {
|
|
279
|
+
# 'inner': {
|
|
280
|
+
# daughter_config['id']: {
|
|
281
|
+
# '_type': 'process',
|
|
282
|
+
# 'address': 'local:!process_bigraph.tests.SimpleCompartment',
|
|
283
|
+
# 'config': daughter_config,
|
|
284
|
+
# 'inner': daughter_inner,
|
|
285
|
+
# 'wires': {
|
|
286
|
+
# 'outer': ['..']}}
|
|
287
|
+
# for daughter_config, daughter_inner in zip(daughter_configs, divisions)}}}}}
|
|
288
|
+
|
|
289
|
+
update = {
|
|
290
|
+
'outer': {
|
|
291
|
+
'inner': {
|
|
292
|
+
'_react': {
|
|
293
|
+
'reaction': 'divide',
|
|
294
|
+
'config': {
|
|
295
|
+
'id': self.config['id'],
|
|
296
|
+
'daughters': [{
|
|
297
|
+
'id': daughter_id,
|
|
298
|
+
'state': daughter_state}
|
|
299
|
+
for daughter_id, daughter_state in zip(
|
|
300
|
+
daughter_ids,
|
|
301
|
+
divisions)]}}}}}
|
|
302
|
+
|
|
303
|
+
return update
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# TODO: create reaction registry, register this under "divide"
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def engulf_reaction(config):
|
|
310
|
+
return {
|
|
311
|
+
'redex': {},
|
|
312
|
+
'reactum': {}}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def burst_reaction(config):
|
|
316
|
+
return {
|
|
317
|
+
'redex': {},
|
|
318
|
+
'reactum': {}}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def test_reaction():
|
|
322
|
+
composite = {
|
|
323
|
+
'state': {
|
|
324
|
+
'environment': {
|
|
325
|
+
'concentrations': {},
|
|
326
|
+
'inner': {
|
|
327
|
+
'agent1': {
|
|
328
|
+
'_type': 'process',
|
|
329
|
+
'address': 'local:!process_bigraph.tests.SimpleCompartment',
|
|
330
|
+
'config': {'id': '0'},
|
|
331
|
+
'concentrations': {},
|
|
332
|
+
'inner': {
|
|
333
|
+
'agent2': {
|
|
334
|
+
'_type': 'process',
|
|
335
|
+
'address': 'local:!process_bigraph.tests.SimpleCompartment',
|
|
336
|
+
'config': {'id': '0'},
|
|
337
|
+
'inner': {},
|
|
338
|
+
'wires': {
|
|
339
|
+
'outer': ['..', '..'],
|
|
340
|
+
'inner': ['inner']}}},
|
|
341
|
+
'wires': {
|
|
342
|
+
'outer': ['..', '..'],
|
|
343
|
+
'inner': ['inner']}}}}}}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
if __name__ == '__main__':
|
|
347
|
+
test_default_config()
|
|
348
|
+
test_merge_collections()
|
|
349
|
+
test_process()
|
|
350
|
+
test_composite()
|
|
351
|
+
test_infer()
|
|
352
|
+
test_step_initialization()
|
|
353
|
+
test_dependencies()
|
|
354
|
+
# test_reaction()
|
|
@@ -245,19 +245,40 @@ class ProcessTypes(TypeSystem):
|
|
|
245
245
|
|
|
246
246
|
subwires = hydrated_state['wires']
|
|
247
247
|
if state_type == 'step':
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
248
|
+
input_subwires = subwires.get('inputs', {})
|
|
249
|
+
input_port_schema = port_schema.get('inputs', {})
|
|
250
|
+
schema = self.infer_wires(
|
|
251
|
+
input_port_schema,
|
|
252
|
+
hydrated_state,
|
|
253
|
+
input_subwires,
|
|
254
|
+
top_schema=schema,
|
|
255
|
+
path=path[:-1])
|
|
256
|
+
|
|
257
|
+
output_subwires = subwires.get('outputs', {})
|
|
258
|
+
output_port_schema = port_schema.get('outputs', {})
|
|
259
|
+
schema = self.infer_wires(
|
|
260
|
+
output_port_schema,
|
|
261
|
+
hydrated_state,
|
|
262
|
+
output_subwires,
|
|
263
|
+
top_schema=schema,
|
|
264
|
+
path=path[:-1])
|
|
265
|
+
else:
|
|
266
|
+
schema = self.infer_wires(
|
|
267
|
+
port_schema,
|
|
268
|
+
hydrated_state,
|
|
269
|
+
subwires,
|
|
270
|
+
top_schema=schema,
|
|
271
|
+
path=path[:-1])
|
|
272
|
+
elif '_type' in schema:
|
|
273
|
+
hydrated_state = self.deserialize(schema, state)
|
|
274
|
+
top_state = set_path(
|
|
275
|
+
top_state,
|
|
276
|
+
path,
|
|
277
|
+
hydrated_state)
|
|
257
278
|
else:
|
|
258
279
|
for key, value in state.items():
|
|
259
280
|
inner_path = path + (key,)
|
|
260
|
-
if get_path(schema, inner_path) is None:
|
|
281
|
+
if get_path(schema, inner_path) is None or get_path(state, inner_path) is None or (isinstance(value, dict) and '_type' in value):
|
|
261
282
|
schema, top_state = self.infer_schema(
|
|
262
283
|
schema,
|
|
263
284
|
value,
|
|
@@ -325,16 +346,17 @@ class ProcessTypes(TypeSystem):
|
|
|
325
346
|
schema = self.access(schema)
|
|
326
347
|
return self.hydrate_state(schema, state)
|
|
327
348
|
else:
|
|
328
|
-
result =
|
|
329
|
-
for key, value in
|
|
349
|
+
result = state.copy()
|
|
350
|
+
for key, value in schema.items():
|
|
330
351
|
if key in schema:
|
|
331
352
|
subschema = schema[key]
|
|
332
353
|
else:
|
|
333
354
|
subschema = schema
|
|
334
355
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
356
|
+
if key in state:
|
|
357
|
+
result[key] = self.hydrate_state(
|
|
358
|
+
subschema,
|
|
359
|
+
state.get(key))
|
|
338
360
|
else:
|
|
339
361
|
result = state
|
|
340
362
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: process-bigraph
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: UNKNOWN
|
|
3
|
+
Version: 0.0.7
|
|
5
4
|
Home-page: https://github.com/vivarium-collective/process-bigraph
|
|
6
5
|
Author: Ryan Spangler, Eran Agmon
|
|
7
6
|
Author-email: ryan.spangler@gmail.com, agmon.eran@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
7
|
Classifier: Development Status :: 3 - Alpha
|
|
11
8
|
Classifier: Intended Audience :: Developers
|
|
12
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -73,5 +70,3 @@ diagraph of a whole-cell E. coli model.
|
|
|
73
70
|
## License
|
|
74
71
|
|
|
75
72
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
76
|
-
|
|
77
|
-
|
|
@@ -2,7 +2,7 @@ import re
|
|
|
2
2
|
from setuptools import setup, find_packages
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
VERSION = '0.0.
|
|
5
|
+
VERSION = '0.0.7'
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
with open("README.md", "r") as readme:
|
|
@@ -44,6 +44,8 @@ setup(
|
|
|
44
44
|
python_requires=">=3.6",
|
|
45
45
|
install_requires=[
|
|
46
46
|
# List your package dependencies here
|
|
47
|
-
"bigraph-schema"
|
|
47
|
+
"bigraph-schema",
|
|
48
|
+
"numpy",
|
|
49
|
+
"pytest>=6.2.5",
|
|
48
50
|
],
|
|
49
51
|
)
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for Process Bigraph
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from process_bigraph.composite import Process, Composite
|
|
6
|
-
from process_bigraph.composite import merge_collections
|
|
7
|
-
from process_bigraph.type_system import types
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class IncreaseProcess(Process):
|
|
11
|
-
config_schema = {
|
|
12
|
-
'rate': {
|
|
13
|
-
'_type': 'float',
|
|
14
|
-
'_default': '0.1'}}
|
|
15
|
-
|
|
16
|
-
def __init__(self, config=None):
|
|
17
|
-
super().__init__(config)
|
|
18
|
-
|
|
19
|
-
def schema(self):
|
|
20
|
-
return {
|
|
21
|
-
'level': 'float'}
|
|
22
|
-
|
|
23
|
-
def update(self, state, interval):
|
|
24
|
-
return {
|
|
25
|
-
'level': state['level'] * self.config['rate']}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_serialized_composite():
|
|
29
|
-
# This should specify the same thing as above
|
|
30
|
-
composite_schema = {
|
|
31
|
-
'_type': 'process[exchange:float]',
|
|
32
|
-
'address': 'local:!process_bigraph.composite.Composite',
|
|
33
|
-
'config': {
|
|
34
|
-
'state': {
|
|
35
|
-
'increase': {
|
|
36
|
-
'_type': 'process[level:float]',
|
|
37
|
-
'address': 'local:!process_bigraph.tests.IncreaseProcess',
|
|
38
|
-
'config': {'rate': '0.3'},
|
|
39
|
-
'wires': {'level': ['value']}
|
|
40
|
-
},
|
|
41
|
-
'value': '11.11',
|
|
42
|
-
},
|
|
43
|
-
'schema': {
|
|
44
|
-
'increase': 'process[level:float]',
|
|
45
|
-
# 'increase': 'process[{"level":"float","down":{"a":"int"}}]',
|
|
46
|
-
'value': 'float',
|
|
47
|
-
},
|
|
48
|
-
'bridge': {
|
|
49
|
-
'exchange': 'value'
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
composite_instance = types.deserialize(composite_schema, {})
|
|
55
|
-
composite_instance.update()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def test_default_config():
|
|
59
|
-
process = IncreaseProcess()
|
|
60
|
-
assert process.config['rate'] == 0.1
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_merge_collections():
|
|
64
|
-
a = {('what',): [1, 2, 3]}
|
|
65
|
-
b = {('okay', 'yes'): [3, 3], ('what',): [4, 5, 11]}
|
|
66
|
-
|
|
67
|
-
c = merge_collections(a, b)
|
|
68
|
-
|
|
69
|
-
assert c[('what',)] == [1, 2, 3, 4, 5, 11]
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_process():
|
|
73
|
-
process = IncreaseProcess({'rate': 0.2})
|
|
74
|
-
schema = process.schema()
|
|
75
|
-
state = types.fill(schema)
|
|
76
|
-
update = process.update({'level': 5.5}, 1.0)
|
|
77
|
-
new_state = types.apply(schema, state, update)
|
|
78
|
-
|
|
79
|
-
assert new_state['level'] == 1.1
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_composite():
|
|
83
|
-
# TODO: add support for the various vivarium emitters
|
|
84
|
-
|
|
85
|
-
# increase = IncreaseProcess({'rate': 0.3})
|
|
86
|
-
# TODO: This is the config of the composite,
|
|
87
|
-
# we also need a way to serialize the entire composite
|
|
88
|
-
|
|
89
|
-
composite = Composite({
|
|
90
|
-
'composition': {
|
|
91
|
-
'increase': 'process[level:float]',
|
|
92
|
-
'value': 'float'},
|
|
93
|
-
'schema': {
|
|
94
|
-
'exchange': 'float'},
|
|
95
|
-
'bridge': {
|
|
96
|
-
'exchange': ['value']},
|
|
97
|
-
'state': {
|
|
98
|
-
'increase': {
|
|
99
|
-
'address': 'local:!process_bigraph.tests.IncreaseProcess',
|
|
100
|
-
'config': {'rate': '0.3'},
|
|
101
|
-
'interval': '1.0',
|
|
102
|
-
'wires': {'level': ['value']}},
|
|
103
|
-
'value': '11.11'}})
|
|
104
|
-
|
|
105
|
-
initial_state = {'exchange': 3.33}
|
|
106
|
-
|
|
107
|
-
updates = composite.update(initial_state, 10.0)
|
|
108
|
-
|
|
109
|
-
final_exchange = sum([
|
|
110
|
-
update['exchange']
|
|
111
|
-
for update in [initial_state] + updates])
|
|
112
|
-
|
|
113
|
-
assert composite.state['value'] > 45
|
|
114
|
-
assert 'exchange' in updates[0]
|
|
115
|
-
assert updates[0]['exchange'] == 0.999
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def test_infer():
|
|
119
|
-
composite = Composite({
|
|
120
|
-
'state': {
|
|
121
|
-
'increase': {
|
|
122
|
-
'_type': 'process',
|
|
123
|
-
'address': 'local:!process_bigraph.tests.IncreaseProcess',
|
|
124
|
-
'config': {'rate': '0.3'},
|
|
125
|
-
'wires': {'level': ['value']}},
|
|
126
|
-
'value': '11.11'}})
|
|
127
|
-
|
|
128
|
-
assert composite.composition['value']['_type'] == 'float'
|
|
129
|
-
assert composite.state['value'] == 11.11
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if __name__ == '__main__':
|
|
134
|
-
test_default_config()
|
|
135
|
-
test_merge_collections()
|
|
136
|
-
test_process()
|
|
137
|
-
test_composite()
|
|
138
|
-
test_infer()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bigraph-schema
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/experiments/minimal_gillespie.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|