From b2947748b89c366745343a5bee471fdc44dbf64b Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Sat, 21 Oct 2023 16:21:06 -0700 Subject: [PATCH 1/6] assembled test for mutual dependencies --- process_bigraph/tests.py | 125 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index 1ef5dc5..b317e39 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -2,7 +2,7 @@ Tests for Process Bigraph """ -from process_bigraph.composite import Step, Process, Composite +from process_bigraph.composite import Process, Step, Composite from process_bigraph.composite import merge_collections from process_bigraph.type_system import types @@ -134,17 +134,13 @@ class OperatorStep(Step): 'operator': 'string'} - def __init__(self, config): - super().__init__(config) - - def schema(self): return { 'inputs': { - 'a': 'int', - 'b': 'int'}, + 'a': 'float', + 'b': 'float'}, 'outputs': { - 'c': 'int'}} + 'c': 'float'}} def update(self, inputs): @@ -190,11 +186,121 @@ def test_step_initialization(): 'outputs': { 'c': ['D']}}}}}) - import ipdb; ipdb.set_trace() assert composite.state['D'] == (13 + 21) * 21 +# class AddStep(Step): +# config_schema = { +# 'offset': 'float'} + + +# def schema(self): +# return { +# 'inputs': { +# 'a': 'float', +# 'b': 'float'}, +# 'outputs': { +# 'c': 'float'}} + + +# def update(self, inputs): +# output = self.config['offset'] + inputs['a'] + inputs['b'] +# outputs = {'c': output} + +# return outputs + + +# class SubtractStep(Step): +# config_schema = { +# 'offset': 'float'} + + +# def schema(self): +# return { +# 'inputs': { +# 'a': 'float', +# 'b': 'float'}, +# 'outputs': { +# 'c': 'float'}} + + +# def update(self, inputs): +# output = self.config['offset'] + inputs['a'] - inputs['b'] +# outputs = {'c': output} + +# return outputs + + +# class MultiplyStep(Step): +# config_schema = { +# 'scale': { +# '_type': 'float', +# '_default': 1.0}} + + +# def schema(self): +# return { +# 'inputs': { +# 'a': { +# '_type': 'float', +# '_default': 1.0}, +# 'b': { +# '_type': 'float', +# '_default': 1.0}}, +# 'outputs': { +# 'c': 'float'}} + + +# def update(self, inputs): +# output = self.config['scale'] * (inputs['a'] * inputs['b']) +# outputs = {'c': output} + +# return outputs + + +def test_dependencies(): + operation = { + 'x': 11.111, + 'y': 22.2, + 'z': 555.555, + 'add': { + '_type': 'step', + 'address': 'local:!process_bigraph.tests.OperatorStep', + 'config': { + 'operator': '+'}, + 'wires': { + 'inputs': { + 'a': ['x'], + 'b': ['y']}, + 'outputs': { + 'c': ['w']}}}, + 'subtract': { + '_type': 'step', + 'address': 'local:!process_bigraph.tests.OperatorStep', + 'config': { + 'operator': '-'}, + 'wires': { + 'inputs': { + 'a': ['z'], + 'b': ['w']}, + 'outputs': { + 'c': ['j']}}}, + 'multiply': { + '_type': 'step', + 'address': 'local:!process_bigraph.tests.OperatorStep', + 'config': { + 'operator': '*'}, + 'wires': { + 'inputs': { + 'a': ['j'], + 'b': ['w']}, + 'outputs': { + 'c': ['k']}}}} + + import ipdb; ipdb.set_trace() + + composite = Composite({'state': operation}) if __name__ == '__main__': @@ -204,3 +310,4 @@ def test_step_initialization(): test_composite() test_infer() test_step_initialization() + test_dependencies() From 12930659c2cb6065027cefdd6e1c4bb55123fec5 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Tue, 24 Oct 2023 13:56:11 -0700 Subject: [PATCH 2/6] working out corner cases for dependency triggering --- process_bigraph/composite.py | 67 ++++++++++++++--- process_bigraph/tests.py | 138 ++++++++++++----------------------- 2 files changed, 104 insertions(+), 101 deletions(-) diff --git a/process_bigraph/composite.py b/process_bigraph/composite.py index 03ace2d..520cc14 100644 --- a/process_bigraph/composite.py +++ b/process_bigraph/composite.py @@ -316,6 +316,8 @@ def build_step_network(steps): 'output_paths': None} for step_key in steps} + nodes = {} + for step_key, step in steps.items(): for other_key, other_step in steps.items(): if step_key == other_key: @@ -336,14 +338,35 @@ def build_step_network(steps): other_wires['outputs']) output_paths = ancestors[other_key]['output_paths'] - if any(item in output_paths for item in input_paths): - ancestors[step_key]['ancestors'].append(other_key) + for item in input_paths: + if item in output_paths: + ancestors[step_key]['ancestors'].append(other_key) + + path = tuple(item) + if not path in nodes: + nodes[path] = { + 'before': set([]), + 'after': set([])} + nodes[path]['before'].add(other_key) + nodes[path]['after'].add(step_key) + + return ancestors, nodes + - return ancestors +def build_trigger_state(steps): + ancestors, nodes = build_step_network(steps) + + trigger_state = { + 'steps': ancestors, + 'states': { + key: value['before'] + for key, value in nodes.items()}} + + return trigger_state def find_starting_steps(steps): - ancestors = build_step_network(steps) + ancestors, nodes = build_step_network(steps) starting = [] for step_key, before in ancestors.items(): if len(before['ancestors']) == 0: @@ -353,7 +376,7 @@ def find_starting_steps(steps): def find_starting_paths(steps): - ancestors = build_step_network(steps) + ancestors, nodes = build_step_network(steps) starting = [] for before in ancestors.values(): if len(before['ancestors']) == 0: @@ -471,12 +494,17 @@ def __init__(self, config=None, local_types=None): self.bridge_updates = [] - # this will work for dags but not for cycles - self.starting_steps = find_starting_steps( +# # this will work for dags but not for cycles +# self.starting_steps = find_starting_steps( +# self.step_paths) + + self.trigger_state = build_trigger_state( self.step_paths) - self.run_steps( - self.starting_steps) + to_run, self.trigger_state = self.determine_steps( + self.trigger_state) + + self.run_steps(to_run) def schema(self): @@ -687,6 +715,25 @@ def run(self, interval, force_complete=False): force_complete = False + def determine_steps(self, trigger_state): + to_run = [] + for step_key, wires in trigger_state['steps']: + fulfilled = True + for input in wires['input_paths']: + if len(trigger_state['states'][tuple(input)]) > 0: + fulfilled = False + break + if fulfilled: + to_run.append(step_key) + + for step_key in to_run: + wires = trigger_state['steps'][step_key] + for output in wires['output_paths']: + trigger_state['states'][tuple(output)].remove(step_key) + + return to_run, trigger_state + + def run_steps(self, step_paths): if len(step_paths) > 0: updates = [] @@ -727,6 +774,8 @@ def trigger_steps(self, update_paths): steps_to_run.append(step_path) self.steps_run.add(step_path) + + self.run_steps(steps_to_run) diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index b317e39..976f775 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -190,124 +190,78 @@ def test_step_initialization(): assert composite.state['D'] == (13 + 21) * 21 -# class AddStep(Step): -# config_schema = { -# 'offset': 'float'} - - -# def schema(self): -# return { -# 'inputs': { -# 'a': 'float', -# 'b': 'float'}, -# 'outputs': { -# 'c': 'float'}} - - -# def update(self, inputs): -# output = self.config['offset'] + inputs['a'] + inputs['b'] -# outputs = {'c': output} - -# return outputs - - -# class SubtractStep(Step): -# config_schema = { -# 'offset': 'float'} - - -# def schema(self): -# return { -# 'inputs': { -# 'a': 'float', -# 'b': 'float'}, -# 'outputs': { -# 'c': 'float'}} - - -# def update(self, inputs): -# output = self.config['offset'] + inputs['a'] - inputs['b'] -# outputs = {'c': output} - -# return outputs - - -# class MultiplyStep(Step): -# config_schema = { -# 'scale': { -# '_type': 'float', -# '_default': 1.0}} - - -# def schema(self): -# return { -# 'inputs': { -# 'a': { -# '_type': 'float', -# '_default': 1.0}, -# 'b': { -# '_type': 'float', -# '_default': 1.0}}, -# 'outputs': { -# 'c': 'float'}} - - -# def update(self, inputs): -# output = self.config['scale'] * (inputs['a'] * inputs['b']) -# outputs = {'c': output} - -# return outputs - - def test_dependencies(): operation = { - 'x': 11.111, - 'y': 22.2, - 'z': 555.555, - 'add': { + 'a': 11.111, + 'b': 22.2, + 'c': 555.555, + + '1': { '_type': 'step', 'address': 'local:!process_bigraph.tests.OperatorStep', 'config': { 'operator': '+'}, 'wires': { 'inputs': { - 'a': ['x'], - 'b': ['y']}, + 'a': ['a'], + 'b': ['b']}, + 'outputs': { + 'c': ['e']}}}, + '2.1': { + '_type': 'step', + 'address': 'local:!process_bigraph.tests.OperatorStep', + 'config': { + 'operator': '-'}, + 'wires': { + 'inputs': { + 'a': ['c'], + 'b': ['e']}, 'outputs': { - 'c': ['w']}}}, - 'subtract': { + 'c': ['f']}}}, + '2.2': { '_type': 'step', 'address': 'local:!process_bigraph.tests.OperatorStep', 'config': { 'operator': '-'}, 'wires': { 'inputs': { - 'a': ['z'], - 'b': ['w']}, + 'a': ['d'], + 'b': ['e']}, 'outputs': { - 'c': ['j']}}}, - 'multiply': { + 'c': ['g']}}}, + '3': { '_type': 'step', 'address': 'local:!process_bigraph.tests.OperatorStep', 'config': { 'operator': '*'}, 'wires': { 'inputs': { - 'a': ['j'], - 'b': ['w']}, + 'a': ['f'], + 'b': ['g']}, 'outputs': { - 'c': ['k']}}}} - - import ipdb; ipdb.set_trace() + 'c': ['h']}}}, + '4': { + '_type': 'step', + 'address': 'local:!process_bigraph.tests.OperatorStep', + 'config': { + 'operator': '+'}, + 'wires': { + 'inputs': { + 'a': ['e'], + 'b': ['h']}, + 'outputs': { + 'c': ['i']}}}} composite = Composite({'state': operation}) + import ipdb; ipdb.set_trace() + if __name__ == '__main__': - test_default_config() - test_merge_collections() - test_process() - test_composite() - test_infer() - test_step_initialization() + # test_default_config() + # test_merge_collections() + # test_process() + # test_composite() + # test_infer() + # test_step_initialization() test_dependencies() From 76d9a488d1784c2c85f2d5d492f2a2e548aa0851 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Fri, 27 Oct 2023 15:37:00 -0700 Subject: [PATCH 3/6] working out test for division reaction --- process_bigraph/tests.py | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index 976f775..6a19f84 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -2,6 +2,8 @@ Tests for Process Bigraph """ +import random + from process_bigraph.composite import Process, Step, Composite from process_bigraph.composite import merge_collections from process_bigraph.type_system import types @@ -257,6 +259,124 @@ def test_dependencies(): import ipdb; ipdb.set_trace() +class SimpleCompartment(Process): + config_schema = { + 'id': 'string'} + + + def schema(self): + return { + 'outer': 'tree[process]', + 'inner': 'tree[process]'} + + + def update(self, state, interval): + choice = random.random() + update = {} + + outer = state['outer'] + inner = state['inner'] + + # TODO: implement divide_state(_) + divisions = self.types.divide_state( + self.schema(), + inner) + + if choice < 0.2: + # update = { + # 'outer': { + # '_divide': { + # 'mother': self.config['id'], + # 'daughters': [ + # {'id': self.config['id'] + '0'}, + # {'id': self.config['id'] + '1'}]}}} + + # daughter_ids = [self.config['id'] + str(i) + # for i in range(2)] + + # update = { + # 'outer': { + # '_react': { + # 'redex': { + # 'inner': { + # self.config['id']: {}}}, + # 'reactum': { + # 'inner': { + # daughter_config['id']: { + # '_type': 'process', + # 'address': 'local:!process_bigraph.tests.SimpleCompartment', + # 'config': daughter_config, + # 'inner': daughter_inner, + # 'wires': { + # 'outer': ['..']}} + # for daughter_config, daughter_inner in zip(daughter_configs, divisions)}}}}} + + update = { + 'outer': { + 'inner': { + '_react': { + 'reaction': 'divide', + 'config': { + 'id': self.config['id'], + 'daughters': [{ + 'id': daughter_id, + 'state': daughter_state} + for daughter_id, daughter_state in zip( + daughter_ids, + divisions)] + + return update + + +# TODO: create reaction registry, register this under "divide" +def divide_reaction(config): + return { + 'redex': { + config['id']: {}}, + 'reactum': { + daughter_config['id']: daughter_config['state'] + for daughter_config in config['daughters']}} + + +def engulf_reaction(config): + return { + 'redex': {}, + 'reactum': {}} + + +def burst_reaction(config): + return { + 'redex': {}, + 'reactum': {}} + + +def test_reaction(): + composite = { + 'state': { + 'environment': { + 'concentrations': {}, + 'inner': { + 'agent1': { + '_type': 'process', + 'address': 'local:!process_bigraph.tests.SimpleCompartment', + 'config': {'id': '0'}, + 'concentrations': {}, + 'inner': { + 'agent2': { + '_type': 'process', + 'address': 'local:!process_bigraph.tests.SimpleCompartment', + 'config': {'id': '0'}, + 'inner': {}, + 'wires': { + 'outer': ['..', '..'], + 'inner': ['inner']}}}, + 'wires': { + 'outer': ['..', '..'], + 'inner': ['inner']}}}}}} + + + + if __name__ == '__main__': # test_default_config() # test_merge_collections() From 524aa2c9daf55a10af18b8f55cbe1c76284689c4 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Mon, 30 Oct 2023 20:34:56 -0700 Subject: [PATCH 4/6] steps are being triggered in parallel according to their dependency network --- process_bigraph/composite.py | 158 +++++++++++++++++++++++------------ process_bigraph/tests.py | 17 ++-- 2 files changed, 113 insertions(+), 62 deletions(-) diff --git a/process_bigraph/composite.py b/process_bigraph/composite.py index 520cc14..93403a4 100644 --- a/process_bigraph/composite.py +++ b/process_bigraph/composite.py @@ -296,14 +296,14 @@ def empty_front(time): def find_leaves(d, path=None): leaves = [] - path = [] + path = () for key, value in d.items(): if isinstance(value, dict): - subleaves = find_leaves(value, path + [key]) + subleaves = find_leaves(value, path + (key,)) leaves.extend(subleaves) else: - leaves.append(path + value) + leaves.append(path + tuple(value)) return leaves @@ -311,7 +311,7 @@ def find_leaves(d, path=None): def build_step_network(steps): ancestors = { step_key: { - 'ancestors': [], + # 'ancestors': [], 'input_paths': None, 'output_paths': None} for step_key in steps} @@ -333,56 +333,84 @@ def build_step_network(steps): wires['inputs']) input_paths = ancestors[step_key]['input_paths'] - if ancestors[other_key]['output_paths'] is None: - ancestors[other_key]['output_paths'] = find_leaves( - other_wires['outputs']) - output_paths = ancestors[other_key]['output_paths'] - - for item in input_paths: - if item in output_paths: - ancestors[step_key]['ancestors'].append(other_key) - - path = tuple(item) - if not path in nodes: - nodes[path] = { - 'before': set([]), - 'after': set([])} - nodes[path]['before'].add(other_key) - nodes[path]['after'].add(step_key) + if ancestors[step_key]['output_paths'] is None: + ancestors[step_key]['output_paths'] = find_leaves( + wires['outputs']) + output_paths = ancestors[step_key]['output_paths'] + + for input in input_paths: + path = tuple(input) + if not path in nodes: + nodes[path] = { + 'before': set([]), + 'after': set([])} + nodes[path]['after'].add(step_key) + + for output in output_paths: + path = tuple(output) + if not path in nodes: + nodes[path] = { + 'before': set([]), + 'after': set([])} + nodes[path]['before'].add(step_key) return ancestors, nodes -def build_trigger_state(steps): - ancestors, nodes = build_step_network(steps) +def combined_step_network(steps): + steps, nodes = build_step_network(steps) trigger_state = { - 'steps': ancestors, - 'states': { - key: value['before'] - for key, value in nodes.items()}} + 'steps': steps, + 'nodes': nodes} return trigger_state -def find_starting_steps(steps): - ancestors, nodes = build_step_network(steps) - starting = [] - for step_key, before in ancestors.items(): - if len(before['ancestors']) == 0: - starting.append(step_key) +def build_trigger_state(nodes): + return { + key: value['before'].copy() + for key, value in nodes.items()} + + +def find_downstream(steps, nodes, upstream): + downstream = set(upstream) + visited = set([]) + previous_len = -1 + + while len(downstream) > len(visited) and len(visited) > previous_len: + previous_len = len(visited) + down = set([]) + for step_path in downstream: + if step_path not in visited: + for output in steps[step_path]['output_paths']: + for dependent in nodes[output]['after']: + down.add(dependent) + visited.add(step_path) + downstream ^= down - return starting + return downstream -def find_starting_paths(steps): - ancestors, nodes = build_step_network(steps) - starting = [] - for before in ancestors.values(): - if len(before['ancestors']) == 0: - starting.extend(before['input_paths']) +def determine_steps(steps, remaining, fulfilled): + to_run = [] - return starting + for step_path in remaining: + step_inputs = steps[step_path]['input_paths'] + all_fulfilled = True + for input in step_inputs: + if len(fulfilled[input]) > 0: + all_fulfilled = False + if all_fulfilled: + to_run.append(step_path) + + for step_path in to_run: + remaining.remove(step_path) + step_outputs = steps[step_path]['output_paths'] + for output in step_outputs: + fulfilled[output].remove(step_path) + + return to_run, remaining, fulfilled class Composite(Process): @@ -494,17 +522,29 @@ def __init__(self, config=None, local_types=None): self.bridge_updates = [] -# # this will work for dags but not for cycles -# self.starting_steps = find_starting_steps( -# self.step_paths) + self.step_dependencies, self.node_dependencies = build_step_network( + self.step_paths) + self.reset_step_state(self.step_paths) + to_run = self.cycle_step_state() + + self.run_steps(to_run) + + + def reset_step_state(self, step_paths): self.trigger_state = build_trigger_state( - self.step_paths) + self.node_dependencies) + + self.steps_remaining = set(step_paths) + - to_run, self.trigger_state = self.determine_steps( + def cycle_step_state(self): + to_run, self.steps_remaining, self.trigger_state = determine_steps( + self.step_dependencies, + self.steps_remaining, self.trigger_state) - self.run_steps(to_run) + return to_run def schema(self): @@ -542,8 +582,6 @@ def process_update( def defer_project(update, args): schema, state, path = args - # if 'c' in update: - # import ipdb; ipdb.set_trace() return types.project_edge( schema, state, @@ -560,6 +598,7 @@ def defer_project(update, args): return absolute + def run_process(self, path, process, end_time, full_step, force_complete): if path not in self.front: self.front[path] = empty_front(self.state['global_time']) @@ -649,7 +688,7 @@ def apply_updates(self, updates): if bridge_update: self.bridge_updates.append(bridge_update) - self.trigger_steps(update_paths) + return update_paths # view_expire_update = self.apply_update(up, store) # view_expire = view_expire or view_expire_update @@ -657,6 +696,7 @@ def apply_updates(self, updates): # if view_expire: # self.state.build_topology_views() + def run(self, interval, force_complete=False): end_time = self.state['global_time'] + interval while self.state['global_time'] < end_time or force_complete: @@ -695,7 +735,10 @@ def run(self, interval, force_complete=False): advance['update'] = {} paths.append(path) - self.apply_updates(updates) + # get all update paths, then trigger steps that + # depend on those paths + update_paths = self.apply_updates(updates) + self.trigger_steps(update_paths) # # display and emit # if self.progress_bar: @@ -715,7 +758,7 @@ def run(self, interval, force_complete=False): force_complete = False - def determine_steps(self, trigger_state): + def determine_steps(self): to_run = [] for step_key, wires in trigger_state['steps']: fulfilled = True @@ -735,6 +778,8 @@ def determine_steps(self, trigger_state): def run_steps(self, step_paths): + print(f'running steps: {step_paths}') + if len(step_paths) > 0: updates = [] for step_path in step_paths: @@ -757,7 +802,10 @@ def run_steps(self, step_paths): updates.append(step_update) - self.apply_updates(updates) + update_paths = self.apply_updates(updates) + to_run = self.cycle_step_state() + if len(to_run) > 0: + self.run_steps(to_run) else: self.steps_run = set([]) @@ -774,8 +822,12 @@ def trigger_steps(self, update_paths): steps_to_run.append(step_path) self.steps_run.add(step_path) - + steps_to_run = find_downstream( + self.step_dependencies, + self.node_dependencies, + steps_to_run) + self.reset_step_state(steps_to_run) self.run_steps(steps_to_run) diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index 6a19f84..98f2015 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -256,8 +256,6 @@ def test_dependencies(): composite = Composite({'state': operation}) - import ipdb; ipdb.set_trace() - class SimpleCompartment(Process): config_schema = { @@ -323,7 +321,7 @@ def update(self, state, interval): 'state': daughter_state} for daughter_id, daughter_state in zip( daughter_ids, - divisions)] + divisions)]}}}}} return update @@ -378,10 +376,11 @@ def test_reaction(): if __name__ == '__main__': - # test_default_config() - # test_merge_collections() - # test_process() - # test_composite() - # test_infer() - # test_step_initialization() + test_default_config() + test_merge_collections() + test_process() + test_composite() + test_infer() + test_step_initialization() test_dependencies() + # test_reaction() From 223cdf582fae9f40a416712cb7647fac21c986fe Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Mon, 30 Oct 2023 20:41:06 -0700 Subject: [PATCH 5/6] only triggering paths that have all dependencies fulfilled --- process_bigraph/composite.py | 4 +++- process_bigraph/tests.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/process_bigraph/composite.py b/process_bigraph/composite.py index 93403a4..afbcf5f 100644 --- a/process_bigraph/composite.py +++ b/process_bigraph/composite.py @@ -828,7 +828,9 @@ def trigger_steps(self, update_paths): steps_to_run) self.reset_step_state(steps_to_run) - self.run_steps(steps_to_run) + to_run = self.cycle_step_state() + + self.run_steps(to_run) def gather_results(self, queries=None): diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index 98f2015..29142f6 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -256,6 +256,8 @@ def test_dependencies(): composite = Composite({'state': operation}) + assert composite.state['h'] == -17396.469884 + class SimpleCompartment(Process): config_schema = { From 9c89e6d44546969a1668a9a5feac94ece6481bd2 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Wed, 1 Nov 2023 00:52:56 -0700 Subject: [PATCH 6/6] composite type initialization --- process_bigraph/__init__.py | 1 + process_bigraph/composite.py | 16 ++++++------- process_bigraph/registry.py | 1 + process_bigraph/tests.py | 44 ++++------------------------------ process_bigraph/type_system.py | 2 +- pytest.ini | 2 +- 6 files changed, 16 insertions(+), 50 deletions(-) diff --git a/process_bigraph/__init__.py b/process_bigraph/__init__.py index 3fa2b2f..421e4b0 100644 --- a/process_bigraph/__init__.py +++ b/process_bigraph/__init__.py @@ -12,3 +12,4 @@ # TODO process_registry.register('console-emitter', ConsoleEmitter) process_registry.register('ram-emitter', RAMEmitter) + diff --git a/process_bigraph/composite.py b/process_bigraph/composite.py index afbcf5f..d20403d 100644 --- a/process_bigraph/composite.py +++ b/process_bigraph/composite.py @@ -335,7 +335,7 @@ def build_step_network(steps): if ancestors[step_key]['output_paths'] is None: ancestors[step_key]['output_paths'] = find_leaves( - wires['outputs']) + wires.get('outputs', {})) output_paths = ancestors[step_key]['output_paths'] for input in input_paths: @@ -414,17 +414,19 @@ def determine_steps(steps, remaining, fulfilled): class Composite(Process): - """Composite parent class. - """ + Composite parent class. + """ + + config_schema = { # TODO: add schema type 'composition': 'tree[any]', 'state': 'tree[any]', 'schema': 'tree[any]', 'bridge': 'wires', - 'global_time_precision': 'maybe[float]', - } + 'global_time_precision': 'maybe[float]'} + # TODO: if processes are serialized, deserialize them first def __init__(self, config=None, local_types=None): @@ -669,8 +671,6 @@ def apply_updates(self, updates): series = [series] for update in series: - # print(update) - paths = hierarchy_depth(update) update_paths.extend(paths.keys()) @@ -778,8 +778,6 @@ def determine_steps(self): def run_steps(self, step_paths): - print(f'running steps: {step_paths}') - if len(step_paths) > 0: updates = [] for step_path in step_paths: diff --git a/process_bigraph/registry.py b/process_bigraph/registry.py index 4f4c93c..cae1691 100644 --- a/process_bigraph/registry.py +++ b/process_bigraph/registry.py @@ -24,3 +24,4 @@ #: Maps process names to :term:`protocol methods` protocol_registry = Registry() + diff --git a/process_bigraph/tests.py b/process_bigraph/tests.py index 29142f6..be3d945 100644 --- a/process_bigraph/tests.py +++ b/process_bigraph/tests.py @@ -27,36 +27,6 @@ def update(self, state, interval): 'level': state['level'] * self.config['rate']} -def test_serialized_composite(): - # This should specify the same thing as above - composite_schema = { - '_type': 'process[exchange:float]', - 'address': 'local:!process_bigraph.composite.Composite', - 'config': { - 'state': { - 'increase': { - '_type': 'process[level:float]', - 'address': 'local:!process_bigraph.tests.IncreaseProcess', - 'config': {'rate': '0.3'}, - 'wires': {'level': ['value']} - }, - 'value': '11.11', - }, - 'schema': { - 'increase': 'process[level:float]', - # 'increase': 'process[{"level":"float","down":{"a":"int"}}]', - 'value': 'float', - }, - 'bridge': { - 'exchange': 'value' - }, - } - } - - composite_instance = types.deserialize(composite_schema, {}) - composite_instance.update() - - def test_default_config(): process = IncreaseProcess() assert process.config['rate'] == 0.1 @@ -259,6 +229,11 @@ def test_dependencies(): assert composite.state['h'] == -17396.469884 +def test_dependency_cycle(): + # test a step network with cycles in a few ways + pass + + class SimpleCompartment(Process): config_schema = { 'id': 'string'} @@ -329,13 +304,6 @@ def update(self, state, interval): # TODO: create reaction registry, register this under "divide" -def divide_reaction(config): - return { - 'redex': { - config['id']: {}}, - 'reactum': { - daughter_config['id']: daughter_config['state'] - for daughter_config in config['daughters']}} def engulf_reaction(config): @@ -375,8 +343,6 @@ def test_reaction(): 'inner': ['inner']}}}}}} - - if __name__ == '__main__': test_default_config() test_merge_collections() diff --git a/process_bigraph/type_system.py b/process_bigraph/type_system.py index e7b9b12..91c7f0d 100644 --- a/process_bigraph/type_system.py +++ b/process_bigraph/type_system.py @@ -278,7 +278,7 @@ def infer_schema(self, schema, state, top_state=None, path=None): else: for key, value in state.items(): inner_path = path + (key,) - if get_path(schema, inner_path) is None or get_path(state, inner_path) is None: + if get_path(schema, inner_path) is None or get_path(state, inner_path) is None or (isinstance(value, dict) and '_type' in value): schema, top_state = self.infer_schema( schema, value, diff --git a/pytest.ini b/pytest.ini index 9b39300..2864ba6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] python_files = *.py addopts = --doctest-modules --strict-markers -testpaths = vivarium +testpaths = process_bigraph markers = slow: indicates slow tests (deselect with '-m "not slow"') \ No newline at end of file