process-bigraph 0.0.17__tar.gz → 0.0.18__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.17/process_bigraph.egg-info → process-bigraph-0.0.18}/PKG-INFO +6 -1
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/composite.py +163 -88
- process-bigraph-0.0.18/process_bigraph/experiments/growth_division.py +207 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/tests.py +1 -1
- {process-bigraph-0.0.17 → process-bigraph-0.0.18/process_bigraph.egg-info}/PKG-INFO +6 -1
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/setup.py +1 -1
- process-bigraph-0.0.17/process_bigraph/experiments/growth_division.py +0 -66
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/AUTHORS.md +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/LICENSE +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/README.md +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/__init__.py +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/experiments/__init__.py +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/experiments/minimal_gillespie.py +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/experiments/parameter_scan.py +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/protocols.py +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph.egg-info/SOURCES.txt +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph.egg-info/dependency_links.txt +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph.egg-info/requires.txt +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph.egg-info/top_level.txt +0 -0
- {process-bigraph-0.0.17 → process-bigraph-0.0.18}/setup.cfg +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: process-bigraph
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.18
|
|
4
|
+
Summary: UNKNOWN
|
|
4
5
|
Home-page: https://github.com/vivarium-collective/process-bigraph
|
|
5
6
|
Author: Ryan Spangler, Eran Agmon
|
|
6
7
|
Author-email: ryan.spangler@gmail.com, agmon.eran@gmail.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
7
10
|
Classifier: Development Status :: 3 - Alpha
|
|
8
11
|
Classifier: Intended Audience :: Developers
|
|
9
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -70,3 +73,5 @@ diagraph of a whole-cell E. coli model.
|
|
|
70
73
|
## License
|
|
71
74
|
|
|
72
75
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
76
|
+
|
|
77
|
+
|
|
@@ -7,12 +7,13 @@ Composite, Process, and Step classes
|
|
|
7
7
|
import abc
|
|
8
8
|
import copy
|
|
9
9
|
import math
|
|
10
|
+
import types
|
|
10
11
|
import collections
|
|
11
12
|
from typing import Dict
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
from bigraph_schema import Edge, TypeSystem, get_path, establish_path, set_path, deep_merge
|
|
15
|
-
from bigraph_schema.registry import Registry, validate_merge
|
|
16
|
+
from bigraph_schema.registry import Registry, validate_merge, visit_method
|
|
16
17
|
|
|
17
18
|
from process_bigraph.protocols import local_lookup, local_lookup_module
|
|
18
19
|
|
|
@@ -33,8 +34,50 @@ def check_process(schema, state, core):
|
|
|
33
34
|
Edge)
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def
|
|
37
|
-
|
|
37
|
+
def fold_visit(schema, state, method, values, core):
|
|
38
|
+
visit = visit_method(
|
|
39
|
+
schema,
|
|
40
|
+
state,
|
|
41
|
+
method,
|
|
42
|
+
values,
|
|
43
|
+
core)
|
|
44
|
+
|
|
45
|
+
return visit
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def divide_process(schema, state, values, core):
|
|
49
|
+
# daughter_configs must have a config per daughter
|
|
50
|
+
|
|
51
|
+
daughter_configs = values.get(
|
|
52
|
+
'daughter_configs',
|
|
53
|
+
[{} for index in range(values['divisions'])])
|
|
54
|
+
|
|
55
|
+
if 'config' not in state:
|
|
56
|
+
return daughter_configs
|
|
57
|
+
|
|
58
|
+
existing_config = state['config']
|
|
59
|
+
|
|
60
|
+
divisions = []
|
|
61
|
+
for index in range(values['divisions']):
|
|
62
|
+
daughter_config = copy.deepcopy(
|
|
63
|
+
existing_config)
|
|
64
|
+
daughter_config = deep_merge(
|
|
65
|
+
daughter_config,
|
|
66
|
+
daughter_configs[index])
|
|
67
|
+
|
|
68
|
+
# TODO: provide a way to override inputs and outputs
|
|
69
|
+
daughter_state = {
|
|
70
|
+
'address': state['address'],
|
|
71
|
+
'config': daughter_config,
|
|
72
|
+
'inputs': copy.deepcopy(state['inputs']),
|
|
73
|
+
'outputs': copy.deepcopy(state['outputs'])}
|
|
74
|
+
|
|
75
|
+
if 'interval' in state:
|
|
76
|
+
daughter_state['interval'] = state['interval']
|
|
77
|
+
|
|
78
|
+
divisions.append(daughter_state)
|
|
79
|
+
|
|
80
|
+
return divisions
|
|
38
81
|
|
|
39
82
|
|
|
40
83
|
def serialize_process(schema, value, core):
|
|
@@ -70,8 +113,8 @@ def deserialize_process(schema, encoded, core):
|
|
|
70
113
|
:returns: The deserialized state with an instantiated process.
|
|
71
114
|
"""
|
|
72
115
|
deserialized = encoded.copy()
|
|
73
|
-
if 'address' not in
|
|
74
|
-
|
|
116
|
+
if 'address' not in deserialized:
|
|
117
|
+
return deserialized
|
|
75
118
|
|
|
76
119
|
protocol, address = encoded['address'].split(':', 1)
|
|
77
120
|
|
|
@@ -154,6 +197,7 @@ process_types = {
|
|
|
154
197
|
'_serialize': serialize_process,
|
|
155
198
|
'_deserialize': deserialize_step,
|
|
156
199
|
'_check': check_process,
|
|
200
|
+
'_fold': fold_visit,
|
|
157
201
|
'_divide': divide_process,
|
|
158
202
|
'_description': '',
|
|
159
203
|
# TODO: support reference to type parameters from other states
|
|
@@ -167,11 +211,13 @@ process_types = {
|
|
|
167
211
|
'_serialize': serialize_process,
|
|
168
212
|
'_deserialize': deserialize_process,
|
|
169
213
|
'_check': check_process,
|
|
214
|
+
'_fold': fold_visit,
|
|
170
215
|
'_divide': divide_process,
|
|
171
216
|
'_description': '',
|
|
172
217
|
# TODO: support reference to type parameters from other states
|
|
173
218
|
'interval': 'interval',
|
|
174
|
-
|
|
219
|
+
'address': 'protocol',
|
|
220
|
+
'config': 'tree[any]'},
|
|
175
221
|
}
|
|
176
222
|
|
|
177
223
|
|
|
@@ -187,8 +233,8 @@ def register_protocols(core):
|
|
|
187
233
|
|
|
188
234
|
|
|
189
235
|
def register_emitters(core):
|
|
190
|
-
core.
|
|
191
|
-
core.
|
|
236
|
+
core.register_process('console-emitter', ConsoleEmitter)
|
|
237
|
+
core.register_process('ram-emitter', RAMEmitter)
|
|
192
238
|
|
|
193
239
|
|
|
194
240
|
class ProcessTypes(TypeSystem):
|
|
@@ -201,8 +247,10 @@ class ProcessTypes(TypeSystem):
|
|
|
201
247
|
register_protocols(self)
|
|
202
248
|
register_emitters(self)
|
|
203
249
|
|
|
250
|
+
self.register_process('composite', Composite)
|
|
204
251
|
|
|
205
|
-
|
|
252
|
+
|
|
253
|
+
def register_process(self, name, process_data):
|
|
206
254
|
self.process_registry.register(name, process_data)
|
|
207
255
|
|
|
208
256
|
|
|
@@ -227,19 +275,20 @@ class ProcessTypes(TypeSystem):
|
|
|
227
275
|
key: value
|
|
228
276
|
for key, value in state.items()
|
|
229
277
|
if key.startswith('_')}
|
|
278
|
+
|
|
230
279
|
state_schema = self.access(
|
|
231
280
|
state_type)
|
|
232
281
|
|
|
233
|
-
hydrated_state = self.deserialize(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
path,
|
|
237
|
-
hydrated_state)
|
|
282
|
+
hydrated_state = self.deserialize(
|
|
283
|
+
state_schema,
|
|
284
|
+
state)
|
|
238
285
|
|
|
239
|
-
schema =
|
|
286
|
+
schema, top_state = self.set_slice(
|
|
240
287
|
schema,
|
|
288
|
+
top_state,
|
|
241
289
|
path,
|
|
242
|
-
state_schema
|
|
290
|
+
state_schema,
|
|
291
|
+
hydrated_state)
|
|
243
292
|
|
|
244
293
|
# TODO: fix is_descendant
|
|
245
294
|
# if core.type_registry.is_descendant('process', state_schema) or core.registry.is_descendant('step', state_schema):
|
|
@@ -252,13 +301,6 @@ class ProcessTypes(TypeSystem):
|
|
|
252
301
|
subschema = port_schema.get(
|
|
253
302
|
port_key, {})
|
|
254
303
|
|
|
255
|
-
# _, schema = self.set_slice(
|
|
256
|
-
# 'schema',
|
|
257
|
-
# schema,
|
|
258
|
-
# path + (f'_{port_key}',),
|
|
259
|
-
# 'schema',
|
|
260
|
-
# subschema)
|
|
261
|
-
|
|
262
304
|
schema = set_path(
|
|
263
305
|
schema,
|
|
264
306
|
path + (f'_{port_key}',),
|
|
@@ -272,7 +314,7 @@ class ProcessTypes(TypeSystem):
|
|
|
272
314
|
hydrated_state,
|
|
273
315
|
ports,
|
|
274
316
|
top_schema=schema,
|
|
275
|
-
path=path
|
|
317
|
+
path=path)
|
|
276
318
|
|
|
277
319
|
elif '_type' in schema:
|
|
278
320
|
hydrated_state = self.deserialize(schema, state)
|
|
@@ -295,21 +337,11 @@ class ProcessTypes(TypeSystem):
|
|
|
295
337
|
pass
|
|
296
338
|
|
|
297
339
|
else:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
path[:-1],
|
|
304
|
-
top=schema,
|
|
305
|
-
cursor=path[:-1])
|
|
306
|
-
|
|
307
|
-
path_key = path[-1]
|
|
308
|
-
if path_key in destination:
|
|
309
|
-
# TODO: validate
|
|
310
|
-
pass
|
|
311
|
-
else:
|
|
312
|
-
destination[path_key] = type_schema
|
|
340
|
+
schema, top_state = super().infer_schema(
|
|
341
|
+
schema,
|
|
342
|
+
state,
|
|
343
|
+
top_state,
|
|
344
|
+
path)
|
|
313
345
|
|
|
314
346
|
return schema, top_state
|
|
315
347
|
|
|
@@ -333,23 +365,32 @@ class ProcessTypes(TypeSystem):
|
|
|
333
365
|
|
|
334
366
|
def initialize_edge_state(self, schema, path, edge):
|
|
335
367
|
initial_state = edge['instance'].initial_state()
|
|
368
|
+
if not initial_state:
|
|
369
|
+
return initial_state
|
|
370
|
+
|
|
336
371
|
input_ports = get_path(schema, path + ('_inputs',))
|
|
337
372
|
output_ports = get_path(schema, path + ('_outputs',))
|
|
338
|
-
ports = {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
373
|
+
ports = {
|
|
374
|
+
'_inputs': input_ports,
|
|
375
|
+
'_outputs': output_ports}
|
|
376
|
+
|
|
377
|
+
input_state = {}
|
|
378
|
+
if input_ports:
|
|
379
|
+
input_state = self.project_edge(
|
|
380
|
+
ports,
|
|
381
|
+
edge,
|
|
382
|
+
path[:-1],
|
|
383
|
+
initial_state,
|
|
384
|
+
ports_key='inputs')
|
|
385
|
+
|
|
386
|
+
output_state = {}
|
|
387
|
+
if output_ports:
|
|
388
|
+
output_state = self.project_edge(
|
|
389
|
+
ports,
|
|
390
|
+
edge,
|
|
391
|
+
path[:-1],
|
|
392
|
+
initial_state,
|
|
393
|
+
ports_key='outputs')
|
|
353
394
|
|
|
354
395
|
state = deep_merge(input_state, output_state)
|
|
355
396
|
|
|
@@ -362,6 +403,7 @@ class ProcessTypes(TypeSystem):
|
|
|
362
403
|
if protocol == 'local':
|
|
363
404
|
self.lookup_local(config)
|
|
364
405
|
|
|
406
|
+
|
|
365
407
|
def hierarchy_depth(hierarchy, path=()):
|
|
366
408
|
"""
|
|
367
409
|
Create a mapping of every path in the hierarchy to the node living at
|
|
@@ -372,7 +414,9 @@ def hierarchy_depth(hierarchy, path=()):
|
|
|
372
414
|
|
|
373
415
|
for key, inner in hierarchy.items():
|
|
374
416
|
down = tuple(path + (key,))
|
|
375
|
-
if
|
|
417
|
+
if key.startswith('_'):
|
|
418
|
+
base[path] = inner
|
|
419
|
+
elif isinstance(inner, dict) and 'instance' not in inner:
|
|
376
420
|
base.update(hierarchy_depth(inner, down))
|
|
377
421
|
else:
|
|
378
422
|
base[down] = inner
|
|
@@ -524,19 +568,19 @@ def find_instances(state, instance_type='process_bigraph.composite.Process'):
|
|
|
524
568
|
found = {}
|
|
525
569
|
|
|
526
570
|
for key, inner in state.items():
|
|
527
|
-
if isinstance(inner, dict)
|
|
528
|
-
|
|
571
|
+
if isinstance(inner, dict):
|
|
572
|
+
if isinstance(inner.get('instance'), process_class):
|
|
573
|
+
found[key] = inner
|
|
574
|
+
elif not key.startswith('_'):
|
|
575
|
+
inner_instances = find_instances(
|
|
576
|
+
inner,
|
|
577
|
+
instance_type=instance_type)
|
|
578
|
+
|
|
579
|
+
if inner_instances:
|
|
580
|
+
found[key] = inner_instances
|
|
529
581
|
return found
|
|
530
582
|
|
|
531
583
|
|
|
532
|
-
def find_processes(state):
|
|
533
|
-
return find_instances(state, 'process_bigraph.composite.Process')
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def find_steps(state):
|
|
537
|
-
return find_instances(state, 'process_bigraph.composite.Step')
|
|
538
|
-
|
|
539
|
-
|
|
540
584
|
def find_instance_paths(state, instance_type='process_bigraph.composite.Process'):
|
|
541
585
|
instances = find_instances(state, instance_type)
|
|
542
586
|
return hierarchy_depth(instances)
|
|
@@ -745,7 +789,7 @@ class Composite(Process):
|
|
|
745
789
|
config_schema = {
|
|
746
790
|
'composition': 'schema',
|
|
747
791
|
'state': 'tree[any]',
|
|
748
|
-
'
|
|
792
|
+
'interface': {
|
|
749
793
|
'inputs': 'schema',
|
|
750
794
|
'outputs': 'schema'},
|
|
751
795
|
'bridge': {
|
|
@@ -775,22 +819,10 @@ class Composite(Process):
|
|
|
775
819
|
self.core.access(composition))
|
|
776
820
|
|
|
777
821
|
# TODO: add flag to self.core.access(copy=True)
|
|
778
|
-
initial_schema = self.core.access(
|
|
779
|
-
self.config.get('schema', {})) or {}
|
|
780
822
|
self.bridge = self.config.get('bridge', {})
|
|
781
823
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
state,
|
|
785
|
-
'process_bigraph.composite.Process')
|
|
786
|
-
|
|
787
|
-
self.step_paths = find_instance_paths(
|
|
788
|
-
state,
|
|
789
|
-
'process_bigraph.composite.Step')
|
|
790
|
-
|
|
791
|
-
self.emitter_paths = find_instance_paths(
|
|
792
|
-
state,
|
|
793
|
-
'process_bigraph.composite.Emitter')
|
|
824
|
+
self.find_instance_paths(
|
|
825
|
+
state)
|
|
794
826
|
|
|
795
827
|
# merge the processes and steps into a single "edges" dict
|
|
796
828
|
self.edge_paths = self.process_paths.copy()
|
|
@@ -808,7 +840,7 @@ class Composite(Process):
|
|
|
808
840
|
edge)
|
|
809
841
|
|
|
810
842
|
try:
|
|
811
|
-
edge_state =
|
|
843
|
+
edge_state = deep_merge(edge_state, initial)
|
|
812
844
|
except:
|
|
813
845
|
raise Exception(
|
|
814
846
|
f'initial state from edge does not match initial state from other edges:\n{path}\n{edge}\n{edge_state}')
|
|
@@ -875,8 +907,27 @@ class Composite(Process):
|
|
|
875
907
|
return to_run
|
|
876
908
|
|
|
877
909
|
|
|
878
|
-
def
|
|
879
|
-
|
|
910
|
+
def find_instance_paths(self, state):
|
|
911
|
+
# find all processes, steps, and emitter in the state
|
|
912
|
+
self.process_paths = find_instance_paths(
|
|
913
|
+
state,
|
|
914
|
+
'process_bigraph.composite.Process')
|
|
915
|
+
|
|
916
|
+
self.step_paths = find_instance_paths(
|
|
917
|
+
state,
|
|
918
|
+
'process_bigraph.composite.Step')
|
|
919
|
+
|
|
920
|
+
self.emitter_paths = find_instance_paths(
|
|
921
|
+
state,
|
|
922
|
+
'process_bigraph.composite.Emitter')
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
def inputs(self):
|
|
926
|
+
return self.process_schema.get('inputs', {})
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def outputs(self):
|
|
930
|
+
return self.process_schema.get('outputs', {})
|
|
880
931
|
|
|
881
932
|
|
|
882
933
|
def merge(self, initial_state):
|
|
@@ -1030,6 +1081,34 @@ class Composite(Process):
|
|
|
1030
1081
|
# self.state.build_topology_views()
|
|
1031
1082
|
|
|
1032
1083
|
|
|
1084
|
+
def expire_process_paths(self, update_paths):
|
|
1085
|
+
for update_path in update_paths:
|
|
1086
|
+
for process_path in self.process_paths.copy():
|
|
1087
|
+
updated = all([
|
|
1088
|
+
update == process
|
|
1089
|
+
for update, process in zip(update_path, process_path)])
|
|
1090
|
+
|
|
1091
|
+
if updated:
|
|
1092
|
+
self.find_instance_paths(
|
|
1093
|
+
self.state)
|
|
1094
|
+
return
|
|
1095
|
+
|
|
1096
|
+
# del self.process_paths[process_path]
|
|
1097
|
+
|
|
1098
|
+
# target_schema, target_state = self.core.slice(
|
|
1099
|
+
# self.composition,
|
|
1100
|
+
# self.state,
|
|
1101
|
+
# update_path)
|
|
1102
|
+
|
|
1103
|
+
# process_subpaths = find_instance_paths(
|
|
1104
|
+
# target_state,
|
|
1105
|
+
# 'process_bigraph.composite.Process')
|
|
1106
|
+
|
|
1107
|
+
# for subpath, process in process_subpaths.items():
|
|
1108
|
+
# process_path = update_path + subpath
|
|
1109
|
+
# self.process_paths[process_path] = process
|
|
1110
|
+
|
|
1111
|
+
|
|
1033
1112
|
def run(self, interval, force_complete=False):
|
|
1034
1113
|
if self.to_run:
|
|
1035
1114
|
self.run_steps(self.to_run)
|
|
@@ -1076,6 +1155,7 @@ class Composite(Process):
|
|
|
1076
1155
|
# get all update paths, then trigger steps that
|
|
1077
1156
|
# depend on those paths
|
|
1078
1157
|
update_paths = self.apply_updates(updates)
|
|
1158
|
+
self.expire_process_paths(update_paths)
|
|
1079
1159
|
self.trigger_steps(update_paths)
|
|
1080
1160
|
|
|
1081
1161
|
# # display and emit
|
|
@@ -1204,11 +1284,6 @@ class Composite(Process):
|
|
|
1204
1284
|
self.merge(
|
|
1205
1285
|
projection)
|
|
1206
1286
|
|
|
1207
|
-
# self.state = self.core.set(
|
|
1208
|
-
# self.composition,
|
|
1209
|
-
# self.state,
|
|
1210
|
-
# projection)
|
|
1211
|
-
|
|
1212
1287
|
self.run(interval)
|
|
1213
1288
|
|
|
1214
1289
|
updates = self.bridge_updates
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from process_bigraph import Step, Process, Composite, ProcessTypes, interval_time_precision, deep_merge
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Grow(Process):
|
|
6
|
+
config_schema = {
|
|
7
|
+
'rate': 'float'}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def __init__(self, config, core=None):
|
|
11
|
+
super().__init__(config, core)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def inputs(self):
|
|
15
|
+
return {
|
|
16
|
+
'mass': 'float'}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def outputs(self):
|
|
20
|
+
return {
|
|
21
|
+
'mass': 'float'}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def update(self, state, interval):
|
|
25
|
+
# this calculates a delta
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
'mass': state['mass'] * self.config['rate'] * interval}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# TODO: build composite and divide within it
|
|
32
|
+
|
|
33
|
+
class Divide(Step):
|
|
34
|
+
# assume the agent_schema has the right divide methods present
|
|
35
|
+
config_schema = {
|
|
36
|
+
'agent_id': 'string',
|
|
37
|
+
'agent_schema': 'schema',
|
|
38
|
+
'threshold': 'float',
|
|
39
|
+
'divisions': {
|
|
40
|
+
'_type': 'integer',
|
|
41
|
+
'_default': 2}}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __init__(self, config, core=None):
|
|
45
|
+
super().__init__(config, core)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def inputs(self):
|
|
49
|
+
return {
|
|
50
|
+
'trigger': 'float'}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def outputs(self):
|
|
54
|
+
return {
|
|
55
|
+
'environment': {
|
|
56
|
+
'_type': 'map',
|
|
57
|
+
'_value': self.config['agent_schema']}}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# this should be generalized to some function that depends on
|
|
61
|
+
# state from the self.config['agent_schema'] (instead of trigger > threshold)
|
|
62
|
+
def update(self, state):
|
|
63
|
+
if state['trigger'] > self.config['threshold']:
|
|
64
|
+
mother = self.config['agent_id']
|
|
65
|
+
daughters = [(
|
|
66
|
+
f'{mother}_{i}', {
|
|
67
|
+
'state': {
|
|
68
|
+
'divide': {
|
|
69
|
+
'config': {
|
|
70
|
+
'agent_id': f'{mother}_{i}'}}}})
|
|
71
|
+
for i in range(self.config['divisions'])]
|
|
72
|
+
|
|
73
|
+
# return divide reaction
|
|
74
|
+
return {
|
|
75
|
+
'environment': {
|
|
76
|
+
'_react': {
|
|
77
|
+
'divide': {
|
|
78
|
+
'mother': mother,
|
|
79
|
+
'daughters': daughters}}}}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def generate_bridge_wires(schema):
|
|
83
|
+
return {
|
|
84
|
+
key: [key]
|
|
85
|
+
for key in schema
|
|
86
|
+
if not key.startswith('_')}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def generate_bridge(schema, state, interval=1.0):
|
|
90
|
+
bridge = {
|
|
91
|
+
port: generate_bridge_wires(schema[port])
|
|
92
|
+
for port in ['inputs', 'outputs']}
|
|
93
|
+
|
|
94
|
+
config = {
|
|
95
|
+
'state': state,
|
|
96
|
+
'bridge': bridge}
|
|
97
|
+
|
|
98
|
+
composite = {
|
|
99
|
+
'_type': 'process',
|
|
100
|
+
'address': 'local:composite',
|
|
101
|
+
'interval': interval,
|
|
102
|
+
'config': config,
|
|
103
|
+
'inputs': generate_bridge_wires(schema['inputs']),
|
|
104
|
+
'outputs': generate_bridge_wires(schema['outputs'])}
|
|
105
|
+
|
|
106
|
+
return composite
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def grow_divide_agent(config=None, state=None, path=None):
|
|
110
|
+
agent_id = path[-1]
|
|
111
|
+
|
|
112
|
+
config = config or {}
|
|
113
|
+
state = state or {}
|
|
114
|
+
path = path or []
|
|
115
|
+
|
|
116
|
+
agent_schema = config.get(
|
|
117
|
+
'agent_schema',
|
|
118
|
+
{'mass': 'float'})
|
|
119
|
+
|
|
120
|
+
grow_config = {
|
|
121
|
+
'rate': 0.1}
|
|
122
|
+
|
|
123
|
+
grow_config = deep_merge(
|
|
124
|
+
grow_config,
|
|
125
|
+
config.get(
|
|
126
|
+
'grow'))
|
|
127
|
+
|
|
128
|
+
divide_config = {
|
|
129
|
+
'agent_id': agent_id,
|
|
130
|
+
'agent_schema': agent_schema,
|
|
131
|
+
'threshold': 2.0,
|
|
132
|
+
'divisions': 2}
|
|
133
|
+
|
|
134
|
+
divide_config = deep_merge(
|
|
135
|
+
divide_config,
|
|
136
|
+
config.get(
|
|
137
|
+
'divide'))
|
|
138
|
+
|
|
139
|
+
grow_divide_state = {
|
|
140
|
+
'grow': {
|
|
141
|
+
'_type': 'process',
|
|
142
|
+
'address': 'local:grow',
|
|
143
|
+
'config': grow_config,
|
|
144
|
+
'inputs': {
|
|
145
|
+
'mass': ['mass']},
|
|
146
|
+
'outputs': {
|
|
147
|
+
'mass': ['mass']}},
|
|
148
|
+
|
|
149
|
+
'divide': {
|
|
150
|
+
'_type': 'process',
|
|
151
|
+
'address': 'local:divide',
|
|
152
|
+
'config': divide_config,
|
|
153
|
+
'inputs': {
|
|
154
|
+
'trigger': ['mass']},
|
|
155
|
+
'outputs': {
|
|
156
|
+
'environment': ['environment']}}}
|
|
157
|
+
|
|
158
|
+
grow_divide_state = deep_merge(
|
|
159
|
+
grow_divide_state,
|
|
160
|
+
state)
|
|
161
|
+
|
|
162
|
+
composite = generate_bridge({
|
|
163
|
+
'inputs': {},
|
|
164
|
+
'outputs': agent_schema},
|
|
165
|
+
grow_divide_state)
|
|
166
|
+
|
|
167
|
+
composite['config']['bridge']['outputs']['environment'] = ['environment']
|
|
168
|
+
composite['outputs']['environment'] = ['..']
|
|
169
|
+
|
|
170
|
+
return composite
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_grow_divide(core):
|
|
174
|
+
initial_mass = 1.0
|
|
175
|
+
|
|
176
|
+
grow_divide = grow_divide_agent(
|
|
177
|
+
{'grow': {'rate': 0.03}},
|
|
178
|
+
{'mass': initial_mass},
|
|
179
|
+
['environment', '0'])
|
|
180
|
+
|
|
181
|
+
environment = {
|
|
182
|
+
'environment': {
|
|
183
|
+
'0': {
|
|
184
|
+
'mass': initial_mass,
|
|
185
|
+
'grow_divide': grow_divide}}}
|
|
186
|
+
|
|
187
|
+
composite = Composite({
|
|
188
|
+
'state': environment},
|
|
189
|
+
core=core)
|
|
190
|
+
|
|
191
|
+
updates = composite.update({}, 100.0)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.fixture
|
|
195
|
+
def core():
|
|
196
|
+
core = ProcessTypes()
|
|
197
|
+
core.register_process('grow', Grow)
|
|
198
|
+
core.register_process('divide', Divide)
|
|
199
|
+
return core
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == '__main__':
|
|
203
|
+
core = ProcessTypes()
|
|
204
|
+
core.register_process('grow', Grow)
|
|
205
|
+
core.register_process('divide', Divide)
|
|
206
|
+
|
|
207
|
+
test_grow_divide(core)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: process-bigraph
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.18
|
|
4
|
+
Summary: UNKNOWN
|
|
4
5
|
Home-page: https://github.com/vivarium-collective/process-bigraph
|
|
5
6
|
Author: Ryan Spangler, Eran Agmon
|
|
6
7
|
Author-email: ryan.spangler@gmail.com, agmon.eran@gmail.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
7
10
|
Classifier: Development Status :: 3 - Alpha
|
|
8
11
|
Classifier: Intended Audience :: Developers
|
|
9
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -70,3 +73,5 @@ diagraph of a whole-cell E. coli model.
|
|
|
70
73
|
## License
|
|
71
74
|
|
|
72
75
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
76
|
+
|
|
77
|
+
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
from process_bigraph import Step, Process, Composite, ProcessTypes, interval_time_precision, deep_merge
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def Growth(Process):
|
|
5
|
-
config_schema = {
|
|
6
|
-
'rate': 'float'}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def __init__(self, config, core=None):
|
|
10
|
-
super().__init__(config, core)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def inputs(self):
|
|
14
|
-
return {
|
|
15
|
-
'mass': 'float'}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def outputs(self):
|
|
19
|
-
return {
|
|
20
|
-
'mass': 'float'}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def update(self, state, interval):
|
|
24
|
-
# this calculates a delta
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
'mass': state['mass'] * self.config['rate'] * interval}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
example_agent_schema = {
|
|
31
|
-
'id': 'string',
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# TODO: build composite and divide within it
|
|
37
|
-
|
|
38
|
-
def Divide(Step):
|
|
39
|
-
# assume the agent_schema has the right divide methods present
|
|
40
|
-
config_schema = {
|
|
41
|
-
'agent_schema': 'schema',
|
|
42
|
-
'threshold': 'float'}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def __init__(self, config, core=None):
|
|
46
|
-
super().__init__(config, core)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def inputs(self):
|
|
50
|
-
return {
|
|
51
|
-
'trigger': 'float'}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def outputs(self):
|
|
55
|
-
return {
|
|
56
|
-
'self': self.config['agent_schema']}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def update(self, state):
|
|
60
|
-
if state['trigger'] > self.config['threshold']:
|
|
61
|
-
# return divide reaction
|
|
62
|
-
return {
|
|
63
|
-
'self': {
|
|
64
|
-
'_fold': {
|
|
65
|
-
'method': 'divide',
|
|
66
|
-
'divisions': 2}}}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/experiments/minimal_gillespie.py
RENAMED
|
File without changes
|
{process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph/experiments/parameter_scan.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{process-bigraph-0.0.17 → process-bigraph-0.0.18}/process_bigraph.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|