process-bigraph 1.0.4__tar.gz → 1.0.6__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-1.0.6/PKG-INFO +146 -0
- process_bigraph-1.0.6/README.md +126 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/composite.py +106 -50
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/processes/growth_division.py +1 -1
- process_bigraph-1.0.6/process_bigraph/processes/math_expression.py +525 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/processes/parameter_scan.py +0 -1
- process_bigraph-1.0.6/process_bigraph/types/process.py +147 -0
- process_bigraph-1.0.6/process_bigraph.egg-info/PKG-INFO +146 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph.egg-info/SOURCES.txt +1 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph.egg-info/requires.txt +6 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/pyproject.toml +7 -1
- process_bigraph-1.0.4/PKG-INFO +0 -63
- process_bigraph-1.0.4/README.md +0 -49
- process_bigraph-1.0.4/process_bigraph/types/process.py +0 -76
- process_bigraph-1.0.4/process_bigraph.egg-info/PKG-INFO +0 -63
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/AUTHORS.md +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/LICENSE +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/__init__.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/emitter.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/experiments/__init__.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/experiments/minimal_gillespie.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/processes/__init__.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/processes/examples.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/protocols/__init__.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/protocols/parallel.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/protocols/rest.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/protocols/socket.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/run.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/types/__init__.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph/units.py +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph.egg-info/dependency_links.txt +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/process_bigraph.egg-info/top_level.txt +0 -0
- {process_bigraph-1.0.4 → process_bigraph-1.0.6}/setup.cfg +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: process-bigraph
|
|
3
|
+
Version: 1.0.6
|
|
4
|
+
Summary: protocol and execution for compositional systems biology
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
License-File: AUTHORS.md
|
|
9
|
+
Requires-Dist: bigraph-schema
|
|
10
|
+
Requires-Dist: ipdb>=0.13.13
|
|
11
|
+
Requires-Dist: matplotlib
|
|
12
|
+
Requires-Dist: pint>=0.24.4
|
|
13
|
+
Requires-Dist: scipy>=1.8
|
|
14
|
+
Requires-Dist: pandas
|
|
15
|
+
Requires-Dist: sympy
|
|
16
|
+
Requires-Dist: ipykernel
|
|
17
|
+
Requires-Dist: jupyter
|
|
18
|
+
Requires-Dist: notebook
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Process-Bigraph
|
|
22
|
+
|
|
23
|
+
[](https://pypi.org/project/process-bigraph/)
|
|
24
|
+
[](https://vivarium-collective.github.io/process-bigraph/notebooks/index.html)
|
|
25
|
+
|
|
26
|
+
**Process-Bigraph** is a compositional runtime and protocol for building and executing
|
|
27
|
+
**multiscale biological models from interoperable processes**.
|
|
28
|
+
|
|
29
|
+
It provides a shared architectural layer for:
|
|
30
|
+
- declaring **process interfaces**
|
|
31
|
+
- wiring processes through **typed shared state**
|
|
32
|
+
- orchestrating execution across **heterogeneous timescales**
|
|
33
|
+
- supporting **dynamic structure** (workflows, division, graph rewrites)
|
|
34
|
+
|
|
35
|
+
Process-Bigraph is the execution core of **Vivarium 2.0**, designed to integrate models
|
|
36
|
+
built with different formalisms—including ODEs, FBA, agent-based models, spatial solvers,
|
|
37
|
+
and machine-learning components—into a single coherent simulation.
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="https://github.com/vivarium-collective/process-bigraph/blob/main/doc/_static/composition_framework.png?raw=true"
|
|
41
|
+
width="800"
|
|
42
|
+
alt="Process Bigraph composition framework">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🧩 What is a Process Bigraph?
|
|
48
|
+
|
|
49
|
+
A **process bigraph** combines:
|
|
50
|
+
|
|
51
|
+
- **Typed stores** — hierarchical, schema-validated state defined with
|
|
52
|
+
[**bigraph-schema**](https://github.com/vivarium-collective/bigraph-schema)
|
|
53
|
+
- **Processes** — executable components with explicit input/output ports
|
|
54
|
+
- **Composites** — encapsulated sub-simulations with their own internal structure
|
|
55
|
+
- **Orchestration patterns** — multi-timestepping, directed workflows, and event-driven rewrites
|
|
56
|
+
|
|
57
|
+
Processes do **not** mutate state directly.
|
|
58
|
+
Instead, they emit **typed deltas** that are merged by the runtime.
|
|
59
|
+
|
|
60
|
+
This allows:
|
|
61
|
+
- numerical updates
|
|
62
|
+
- structural rewrites
|
|
63
|
+
- scheduling and orchestration
|
|
64
|
+
|
|
65
|
+
to coexist under a single execution semantics.
|
|
66
|
+
|
|
67
|
+
In this sense, Process-Bigraph is a **composition protocol**, not a domain-specific simulator.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 📄 Paper reference
|
|
72
|
+
|
|
73
|
+
The conceptual framework and formal semantics of process bigraphs are introduced in:
|
|
74
|
+
|
|
75
|
+
> **Agmon, E. & Spangler, R. K.**
|
|
76
|
+
> *Process Bigraphs and the Architecture of Compositional Systems Biology*
|
|
77
|
+
> https://arxiv.org/abs/2512.23754
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🚀 Getting Started
|
|
82
|
+
|
|
83
|
+
### Installation
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
pip install process-bigraph
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 📘 Tutorials
|
|
90
|
+
|
|
91
|
+
The Process-Bigraph tutorials are executable Jupyter notebooks,
|
|
92
|
+
rendered to HTML and published automatically on GitHub Pages.
|
|
93
|
+
|
|
94
|
+
- 📚 **Tutorial Index (all tutorials)**
|
|
95
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/index.html
|
|
96
|
+
|
|
97
|
+
### Learning Path (Featured Tutorials)
|
|
98
|
+
|
|
99
|
+
- **Tutorial 1 — Process-Bigraph Basics**
|
|
100
|
+
*Processes, Steps, ports, Composites, workflows, and emitters*
|
|
101
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_1.html
|
|
102
|
+
|
|
103
|
+
- **Tutorial 2 — Wrapping an ODE Solver (`odeint`)**
|
|
104
|
+
*How to expose an existing scientific API as a Process*
|
|
105
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_2.html
|
|
106
|
+
|
|
107
|
+
- **Tutorial 3 — Declarative Math**
|
|
108
|
+
*Defining mathematical relationships, signal pipelines, and events using `MathExpressionStep`*
|
|
109
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_3.html
|
|
110
|
+
|
|
111
|
+
More tutorials are added continuously and appear automatically in the index.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🧪 Reference Implementation: spatio-flux
|
|
116
|
+
|
|
117
|
+
Process-Bigraph is exercised end-to-end in **spatio-flux**, a multiscale reference
|
|
118
|
+
model built entirely using the process-bigraph protocol.
|
|
119
|
+
|
|
120
|
+
spatio-flux composes spatial fields, particle dynamics, and metabolic processes
|
|
121
|
+
using typed shared state and declarative orchestration.
|
|
122
|
+
|
|
123
|
+
GitHub: https://github.com/vivarium-collective/spatio-flux
|
|
124
|
+
Live test report: https://vivarium-collective.github.io/spatio-flux/report/index.html
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 🔗 Related Resources
|
|
129
|
+
|
|
130
|
+
- **Bigraph Schema Basics**
|
|
131
|
+
https://vivarium-collective.github.io/bigraph-viz/notebooks/basics.html
|
|
132
|
+
*Introduction to the schema language underlying Process-Bigraph*
|
|
133
|
+
|
|
134
|
+
- **Visualization of Bigraph Document** — diagramming and rendering with
|
|
135
|
+
[**bigraph-viz**](https://github.com/vivarium-collective/bigraph-viz)
|
|
136
|
+
https://vivarium-collective.github.io/bigraph-viz/notebooks/format.html
|
|
137
|
+
|
|
138
|
+
- **E. coli Whole-Cell Wiring Diagram**
|
|
139
|
+
https://raw.githubusercontent.com/vivarium-collective/bigraph-viz/main/doc/_static/ecoli.png
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 📜 License
|
|
144
|
+
|
|
145
|
+
Process-Bigraph is open-source software released under the
|
|
146
|
+
[Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Process-Bigraph
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/process-bigraph/)
|
|
4
|
+
[](https://vivarium-collective.github.io/process-bigraph/notebooks/index.html)
|
|
5
|
+
|
|
6
|
+
**Process-Bigraph** is a compositional runtime and protocol for building and executing
|
|
7
|
+
**multiscale biological models from interoperable processes**.
|
|
8
|
+
|
|
9
|
+
It provides a shared architectural layer for:
|
|
10
|
+
- declaring **process interfaces**
|
|
11
|
+
- wiring processes through **typed shared state**
|
|
12
|
+
- orchestrating execution across **heterogeneous timescales**
|
|
13
|
+
- supporting **dynamic structure** (workflows, division, graph rewrites)
|
|
14
|
+
|
|
15
|
+
Process-Bigraph is the execution core of **Vivarium 2.0**, designed to integrate models
|
|
16
|
+
built with different formalisms—including ODEs, FBA, agent-based models, spatial solvers,
|
|
17
|
+
and machine-learning components—into a single coherent simulation.
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="https://github.com/vivarium-collective/process-bigraph/blob/main/doc/_static/composition_framework.png?raw=true"
|
|
21
|
+
width="800"
|
|
22
|
+
alt="Process Bigraph composition framework">
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 🧩 What is a Process Bigraph?
|
|
28
|
+
|
|
29
|
+
A **process bigraph** combines:
|
|
30
|
+
|
|
31
|
+
- **Typed stores** — hierarchical, schema-validated state defined with
|
|
32
|
+
[**bigraph-schema**](https://github.com/vivarium-collective/bigraph-schema)
|
|
33
|
+
- **Processes** — executable components with explicit input/output ports
|
|
34
|
+
- **Composites** — encapsulated sub-simulations with their own internal structure
|
|
35
|
+
- **Orchestration patterns** — multi-timestepping, directed workflows, and event-driven rewrites
|
|
36
|
+
|
|
37
|
+
Processes do **not** mutate state directly.
|
|
38
|
+
Instead, they emit **typed deltas** that are merged by the runtime.
|
|
39
|
+
|
|
40
|
+
This allows:
|
|
41
|
+
- numerical updates
|
|
42
|
+
- structural rewrites
|
|
43
|
+
- scheduling and orchestration
|
|
44
|
+
|
|
45
|
+
to coexist under a single execution semantics.
|
|
46
|
+
|
|
47
|
+
In this sense, Process-Bigraph is a **composition protocol**, not a domain-specific simulator.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 📄 Paper reference
|
|
52
|
+
|
|
53
|
+
The conceptual framework and formal semantics of process bigraphs are introduced in:
|
|
54
|
+
|
|
55
|
+
> **Agmon, E. & Spangler, R. K.**
|
|
56
|
+
> *Process Bigraphs and the Architecture of Compositional Systems Biology*
|
|
57
|
+
> https://arxiv.org/abs/2512.23754
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🚀 Getting Started
|
|
62
|
+
|
|
63
|
+
### Installation
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
pip install process-bigraph
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 📘 Tutorials
|
|
70
|
+
|
|
71
|
+
The Process-Bigraph tutorials are executable Jupyter notebooks,
|
|
72
|
+
rendered to HTML and published automatically on GitHub Pages.
|
|
73
|
+
|
|
74
|
+
- 📚 **Tutorial Index (all tutorials)**
|
|
75
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/index.html
|
|
76
|
+
|
|
77
|
+
### Learning Path (Featured Tutorials)
|
|
78
|
+
|
|
79
|
+
- **Tutorial 1 — Process-Bigraph Basics**
|
|
80
|
+
*Processes, Steps, ports, Composites, workflows, and emitters*
|
|
81
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_1.html
|
|
82
|
+
|
|
83
|
+
- **Tutorial 2 — Wrapping an ODE Solver (`odeint`)**
|
|
84
|
+
*How to expose an existing scientific API as a Process*
|
|
85
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_2.html
|
|
86
|
+
|
|
87
|
+
- **Tutorial 3 — Declarative Math**
|
|
88
|
+
*Defining mathematical relationships, signal pipelines, and events using `MathExpressionStep`*
|
|
89
|
+
https://vivarium-collective.github.io/process-bigraph/notebooks/tutorial_3.html
|
|
90
|
+
|
|
91
|
+
More tutorials are added continuously and appear automatically in the index.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 🧪 Reference Implementation: spatio-flux
|
|
96
|
+
|
|
97
|
+
Process-Bigraph is exercised end-to-end in **spatio-flux**, a multiscale reference
|
|
98
|
+
model built entirely using the process-bigraph protocol.
|
|
99
|
+
|
|
100
|
+
spatio-flux composes spatial fields, particle dynamics, and metabolic processes
|
|
101
|
+
using typed shared state and declarative orchestration.
|
|
102
|
+
|
|
103
|
+
GitHub: https://github.com/vivarium-collective/spatio-flux
|
|
104
|
+
Live test report: https://vivarium-collective.github.io/spatio-flux/report/index.html
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 🔗 Related Resources
|
|
109
|
+
|
|
110
|
+
- **Bigraph Schema Basics**
|
|
111
|
+
https://vivarium-collective.github.io/bigraph-viz/notebooks/basics.html
|
|
112
|
+
*Introduction to the schema language underlying Process-Bigraph*
|
|
113
|
+
|
|
114
|
+
- **Visualization of Bigraph Document** — diagramming and rendering with
|
|
115
|
+
[**bigraph-viz**](https://github.com/vivarium-collective/bigraph-viz)
|
|
116
|
+
https://vivarium-collective.github.io/bigraph-viz/notebooks/format.html
|
|
117
|
+
|
|
118
|
+
- **E. coli Whole-Cell Wiring Diagram**
|
|
119
|
+
https://raw.githubusercontent.com/vivarium-collective/bigraph-viz/main/doc/_static/ecoli.png
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📜 License
|
|
124
|
+
|
|
125
|
+
Process-Bigraph is open-source software released under the
|
|
126
|
+
[Apache 2 License](https://github.com/vivarium-collective/process-bigraph/blob/main/LICENSE).
|
|
@@ -72,8 +72,11 @@ def find_instances(
|
|
|
72
72
|
|
|
73
73
|
for key, inner in state.items():
|
|
74
74
|
if isinstance(inner, dict):
|
|
75
|
-
|
|
75
|
+
instance = inner.get('instance')
|
|
76
|
+
|
|
77
|
+
if isinstance(instance, process_class):
|
|
76
78
|
found[key] = inner
|
|
79
|
+
|
|
77
80
|
elif not is_schema_key(key):
|
|
78
81
|
sub_instances = find_instances(inner, instance_type)
|
|
79
82
|
if sub_instances:
|
|
@@ -116,10 +119,11 @@ def find_step_triggers(
|
|
|
116
119
|
"""
|
|
117
120
|
prefix = tuple(path[:-1])
|
|
118
121
|
triggers: Dict[Tuple[str, ...], List[Union[List[str], Tuple[str, ...]]]] = {}
|
|
119
|
-
wire_paths = find_leaves(step['inputs'])
|
|
122
|
+
wire_paths = find_leaves(step['inputs'], path=prefix)
|
|
120
123
|
|
|
121
124
|
for wire in wire_paths:
|
|
122
|
-
trigger_path = resolve_path(
|
|
125
|
+
trigger_path = resolve_path(tuple(wire))
|
|
126
|
+
# trigger_path = resolve_path(prefix + tuple(wire))
|
|
123
127
|
if isinstance(trigger_path, list):
|
|
124
128
|
import ipdb; ipdb.set_trace()
|
|
125
129
|
triggers.setdefault(trigger_path, []).append(path)
|
|
@@ -210,7 +214,7 @@ def find_leaves(tree_structure, path=None):
|
|
|
210
214
|
list: List of leaf paths as tuples.
|
|
211
215
|
"""
|
|
212
216
|
leaves = []
|
|
213
|
-
path = ()
|
|
217
|
+
path = path or ()
|
|
214
218
|
|
|
215
219
|
if tree_structure is None:
|
|
216
220
|
pass
|
|
@@ -221,7 +225,7 @@ def find_leaves(tree_structure, path=None):
|
|
|
221
225
|
else:
|
|
222
226
|
for key, value in tree_structure.items():
|
|
223
227
|
if isinstance(value, dict):
|
|
224
|
-
subleaves = find_leaves(value, path
|
|
228
|
+
subleaves = find_leaves(value, path=path)
|
|
225
229
|
leaves.extend(subleaves)
|
|
226
230
|
else:
|
|
227
231
|
leaves.append(path + tuple(value))
|
|
@@ -241,7 +245,7 @@ def build_step_network(steps):
|
|
|
241
245
|
- nodes: A mapping from paths to sets of steps that are dependent on them.
|
|
242
246
|
"""
|
|
243
247
|
ancestors = {
|
|
244
|
-
step_key: {'input_paths': None, 'output_paths': None}
|
|
248
|
+
step_key: {'input_paths': None, 'output_paths': None, 'priority': None}
|
|
245
249
|
for step_key in steps
|
|
246
250
|
}
|
|
247
251
|
nodes = {}
|
|
@@ -252,11 +256,15 @@ def build_step_network(steps):
|
|
|
252
256
|
|
|
253
257
|
# Compute input paths once per step
|
|
254
258
|
if ancestors[step_key]['input_paths'] is None:
|
|
255
|
-
ancestors[step_key]['input_paths'] = find_leaves(step['inputs'])
|
|
259
|
+
ancestors[step_key]['input_paths'] = find_leaves(step['inputs'], path=step_key[:-1])
|
|
256
260
|
|
|
257
261
|
# Compute output paths once per step
|
|
258
262
|
if ancestors[step_key]['output_paths'] is None:
|
|
259
|
-
ancestors[step_key]['output_paths'] = find_leaves(step.get('outputs', {}))
|
|
263
|
+
ancestors[step_key]['output_paths'] = find_leaves(step.get('outputs', {}), path=step_key[:-1])
|
|
264
|
+
|
|
265
|
+
# Assign the priority
|
|
266
|
+
if ancestors[step_key]['priority'] is None:
|
|
267
|
+
ancestors[step_key]['priority'] = step.get('priority', 0.0)
|
|
260
268
|
|
|
261
269
|
input_paths = ancestors[step_key]['input_paths'] or []
|
|
262
270
|
output_paths = ancestors[step_key]['output_paths'] or []
|
|
@@ -278,7 +286,7 @@ def build_step_network(steps):
|
|
|
278
286
|
return ancestors, nodes
|
|
279
287
|
|
|
280
288
|
|
|
281
|
-
def build_trigger_state(nodes):
|
|
289
|
+
def build_trigger_state(nodes, paths):
|
|
282
290
|
"""
|
|
283
291
|
Initialize the trigger state from dependency nodes.
|
|
284
292
|
|
|
@@ -288,8 +296,10 @@ def build_trigger_state(nodes):
|
|
|
288
296
|
Returns:
|
|
289
297
|
A mapping of paths to the set of steps waiting on those paths.
|
|
290
298
|
"""
|
|
299
|
+
path_set = set(paths)
|
|
300
|
+
|
|
291
301
|
return {
|
|
292
|
-
key: value['before'].
|
|
302
|
+
key: set(value['before']).intersection(path_set)
|
|
293
303
|
for key, value in nodes.items()}
|
|
294
304
|
|
|
295
305
|
|
|
@@ -344,13 +354,51 @@ def determine_steps(steps, remaining, fulfilled):
|
|
|
344
354
|
"""
|
|
345
355
|
to_run = []
|
|
346
356
|
|
|
357
|
+
if not remaining:
|
|
358
|
+
return to_run, remaining, fulfilled
|
|
359
|
+
|
|
347
360
|
for step_path in list(remaining):
|
|
348
361
|
step_inputs = steps[step_path].get('input_paths', []) or []
|
|
349
362
|
if all(len(fulfilled[input]) == 0 for input in step_inputs):
|
|
350
363
|
to_run.append(step_path)
|
|
351
364
|
|
|
365
|
+
if not to_run:
|
|
366
|
+
# cycles
|
|
367
|
+
visited = set([])
|
|
368
|
+
cycle = set([])
|
|
369
|
+
look = list(remaining)
|
|
370
|
+
|
|
371
|
+
while look:
|
|
372
|
+
step_path = look[0]
|
|
373
|
+
look = look[1:]
|
|
374
|
+
|
|
375
|
+
if step_path in visited:
|
|
376
|
+
if step_path in remaining:
|
|
377
|
+
cycle.add(step_path)
|
|
378
|
+
|
|
379
|
+
else:
|
|
380
|
+
visited.add(step_path)
|
|
381
|
+
|
|
382
|
+
inputs = steps[step_path]['input_paths']
|
|
383
|
+
for input_path in inputs:
|
|
384
|
+
if input_path in fulfilled:
|
|
385
|
+
unfulfilled = fulfilled[input_path]
|
|
386
|
+
look += unfulfilled
|
|
387
|
+
|
|
388
|
+
if not cycle:
|
|
389
|
+
return to_run, remaining, fulfilled
|
|
390
|
+
|
|
391
|
+
order = sorted(
|
|
392
|
+
cycle,
|
|
393
|
+
key=lambda path: steps[path]['priority'])
|
|
394
|
+
|
|
395
|
+
priority = order[-1]
|
|
396
|
+
to_run = [priority]
|
|
397
|
+
|
|
352
398
|
for step_path in to_run:
|
|
353
|
-
remaining
|
|
399
|
+
if step_path in remaining:
|
|
400
|
+
remaining.remove(step_path)
|
|
401
|
+
|
|
354
402
|
step_outputs = steps[step_path].get('output_paths', []) or []
|
|
355
403
|
for output in step_outputs:
|
|
356
404
|
exploded_path = explode_path(output)[1:]
|
|
@@ -658,64 +706,74 @@ class Process(Open):
|
|
|
658
706
|
return {}
|
|
659
707
|
|
|
660
708
|
|
|
661
|
-
def as_step(inputs, outputs,
|
|
709
|
+
def as_step(inputs, outputs, name=None, aliases=None):
|
|
662
710
|
"""
|
|
663
|
-
Decorator
|
|
664
|
-
|
|
711
|
+
Decorator: convert an `update_*` pure function into a Step subclass.
|
|
712
|
+
|
|
713
|
+
- Does NOT register into any core.
|
|
714
|
+
- Adds metadata so discover_packages can register nice aliases (e.g. "add").
|
|
665
715
|
"""
|
|
666
716
|
def decorator(func):
|
|
667
|
-
|
|
668
|
-
|
|
717
|
+
if not func.__name__.startswith("update_"):
|
|
718
|
+
raise AssertionError("Function name must be of the form update_*")
|
|
669
719
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
720
|
+
step_name = name or func.__name__[len("update_"):]
|
|
721
|
+
step_aliases = list(aliases or [])
|
|
722
|
+
# default alias: the function-derived name, e.g. update_add -> "add"
|
|
723
|
+
if step_name not in step_aliases:
|
|
724
|
+
step_aliases.insert(0, step_name)
|
|
673
725
|
|
|
674
|
-
|
|
675
|
-
|
|
726
|
+
class FunctionStep(Step):
|
|
727
|
+
def inputs(self): return inputs
|
|
728
|
+
def outputs(self): return outputs
|
|
729
|
+
def update(self, state): return func(state)
|
|
676
730
|
|
|
677
|
-
|
|
678
|
-
return func(state)
|
|
731
|
+
FunctionStep.__name__ = f"{step_name}Step"
|
|
679
732
|
|
|
680
|
-
|
|
733
|
+
# IMPORTANT: make this class look like it belongs to the user's module
|
|
734
|
+
FunctionStep.__module__ = func.__module__
|
|
681
735
|
|
|
682
|
-
|
|
683
|
-
|
|
736
|
+
# Discovery metadata
|
|
737
|
+
FunctionStep.__pb_kind__ = "step"
|
|
738
|
+
FunctionStep.__pb_aliases__ = step_aliases
|
|
739
|
+
FunctionStep.__pb_wrapped__ = func
|
|
684
740
|
|
|
685
741
|
return FunctionStep
|
|
686
|
-
|
|
687
742
|
return decorator
|
|
688
743
|
|
|
689
744
|
|
|
690
|
-
def as_process(inputs, outputs,
|
|
745
|
+
def as_process(inputs, outputs, name=None, aliases=None):
|
|
691
746
|
"""
|
|
692
|
-
Decorator
|
|
693
|
-
|
|
747
|
+
Decorator: convert an `update_*` function into a Process subclass.
|
|
748
|
+
|
|
749
|
+
- Does NOT register into any core.
|
|
750
|
+
- Adds metadata so discover_packages can register nice aliases (e.g. "odeint").
|
|
694
751
|
"""
|
|
695
752
|
def decorator(func):
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
class FunctionProcess(Process):
|
|
700
|
-
def __init__(self, config=None, core=None):
|
|
701
|
-
super().__init__(config=config, core=core)
|
|
753
|
+
if not func.__name__.startswith("update_"):
|
|
754
|
+
raise AssertionError("Function name must be of the form update_*")
|
|
702
755
|
|
|
703
|
-
|
|
704
|
-
|
|
756
|
+
process_name = name or func.__name__[len("update_"):]
|
|
757
|
+
process_aliases = list(aliases or [])
|
|
758
|
+
if process_name not in process_aliases:
|
|
759
|
+
process_aliases.insert(0, process_name)
|
|
705
760
|
|
|
706
|
-
|
|
707
|
-
|
|
761
|
+
class FunctionProcess(Process):
|
|
762
|
+
def inputs(self): return inputs
|
|
763
|
+
def outputs(self): return outputs
|
|
764
|
+
def update(self, state, interval): return func(state, interval)
|
|
708
765
|
|
|
709
|
-
|
|
710
|
-
return func(state, interval)
|
|
766
|
+
FunctionProcess.__name__ = f"{process_name}Process"
|
|
711
767
|
|
|
712
|
-
|
|
768
|
+
# IMPORTANT: make this class look like it belongs to the user's module
|
|
769
|
+
FunctionProcess.__module__ = func.__module__
|
|
713
770
|
|
|
714
|
-
|
|
715
|
-
|
|
771
|
+
# Discovery metadata
|
|
772
|
+
FunctionProcess.__pb_kind__ = "process"
|
|
773
|
+
FunctionProcess.__pb_aliases__ = process_aliases
|
|
774
|
+
FunctionProcess.__pb_wrapped__ = func
|
|
716
775
|
|
|
717
776
|
return FunctionProcess
|
|
718
|
-
|
|
719
777
|
return decorator
|
|
720
778
|
|
|
721
779
|
|
|
@@ -1147,9 +1205,6 @@ class Composite(Process):
|
|
|
1147
1205
|
"""
|
|
1148
1206
|
state = state or self.state
|
|
1149
1207
|
|
|
1150
|
-
# if 'environment' in state and '_add' in state['environment']:
|
|
1151
|
-
# import ipdb; ipdb.set_trace()
|
|
1152
|
-
|
|
1153
1208
|
bridge_view = self.core.view_ports(
|
|
1154
1209
|
self.schema,
|
|
1155
1210
|
state,
|
|
@@ -1229,7 +1284,7 @@ class Composite(Process):
|
|
|
1229
1284
|
step_paths: A dictionary of step paths (as keys).
|
|
1230
1285
|
"""
|
|
1231
1286
|
# Start with a fresh trigger state from the dependency graph
|
|
1232
|
-
self.trigger_state = build_trigger_state(self.node_dependencies)
|
|
1287
|
+
self.trigger_state = build_trigger_state(self.node_dependencies, step_paths)
|
|
1233
1288
|
|
|
1234
1289
|
# Track steps still waiting to be executed in this cycle
|
|
1235
1290
|
self.steps_remaining: Set[Union[str, Tuple[str, ...]]] = set(step_paths)
|
|
@@ -1359,6 +1414,7 @@ class Composite(Process):
|
|
|
1359
1414
|
paths.append(path)
|
|
1360
1415
|
|
|
1361
1416
|
update_paths = self.apply_updates(updates)
|
|
1417
|
+
update_paths.append(('global_time',)) # updated global time can trigger steps
|
|
1362
1418
|
self.expire_process_paths(update_paths)
|
|
1363
1419
|
self.trigger_steps(update_paths)
|
|
1364
1420
|
|