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.
Files changed (23) hide show
  1. {process-bigraph-0.0.5/process_bigraph.egg-info → process-bigraph-0.0.7}/PKG-INFO +1 -6
  2. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/__init__.py +1 -0
  3. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/composite.py +219 -24
  4. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/registry.py +1 -0
  5. process-bigraph-0.0.7/process_bigraph/tests.py +354 -0
  6. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/type_system.py +37 -15
  7. {process-bigraph-0.0.5 → process-bigraph-0.0.7/process_bigraph.egg-info}/PKG-INFO +1 -6
  8. process-bigraph-0.0.7/process_bigraph.egg-info/requires.txt +3 -0
  9. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/setup.py +4 -2
  10. process-bigraph-0.0.5/process_bigraph/tests.py +0 -138
  11. process-bigraph-0.0.5/process_bigraph.egg-info/requires.txt +0 -1
  12. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/AUTHORS.md +0 -0
  13. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/LICENSE +0 -0
  14. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/README.md +0 -0
  15. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/emitter.py +0 -0
  16. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/experiments/__init__.py +0 -0
  17. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/experiments/minimal_gillespie.py +0 -0
  18. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/processes.py +0 -0
  19. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph/protocols.py +0 -0
  20. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/SOURCES.txt +0 -0
  21. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/dependency_links.txt +0 -0
  22. {process-bigraph-0.0.5 → process-bigraph-0.0.7}/process_bigraph.egg-info/top_level.txt +0 -0
  23. {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.5
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
-
@@ -12,3 +12,4 @@ protocol_registry.register('local', local_lookup)
12
12
  # TODO
13
13
  process_registry.register('console-emitter', ConsoleEmitter)
14
14
  process_registry.register('ram-emitter', RAMEmitter)
15
+
@@ -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
- for wire in step['wires']['inputs'].values():
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
- class Composite(Process):
295
- """Composite parent class.
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.run_steps(self.step_triggers.keys())
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
- self.run_steps(update_paths)
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
- self.apply_updates(updates)
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
- for update_path in update_paths:
607
- paths = explode_path(update_path)
608
- for path in paths:
609
- step_paths = self.step_triggers.get(path, [])
610
- for step_path in step_paths:
611
- if step_path is not None and step_path not in self.steps_run:
612
- steps_to_run.append(step_path)
613
- self.steps_run.add(step_path)
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
- if len(steps_to_run) > 0:
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 steps_to_run:
618
- step = get_path(self.state, step_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
@@ -24,3 +24,4 @@ process_registry = Registry()
24
24
 
25
25
  #: Maps process names to :term:`protocol methods`
26
26
  protocol_registry = Registry()
27
+
@@ -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
- subwires = subwires.get('inputs', {})
249
- port_schema = port_schema.get('inputs', {})
250
-
251
- schema = self.infer_wires(
252
- port_schema,
253
- hydrated_state,
254
- subwires,
255
- top_schema=schema,
256
- path=path[:-1])
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 state.items():
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
- result[key] = self.hydrate_state(
336
- subschema,
337
- state.get(key))
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.5
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
-
@@ -0,0 +1,3 @@
1
+ bigraph-schema
2
+ numpy
3
+ pytest>=6.2.5
@@ -2,7 +2,7 @@ import re
2
2
  from setuptools import setup, find_packages
3
3
 
4
4
 
5
- VERSION = '0.0.5'
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