process-bigraph 0.0.51__tar.gz → 1.0.2__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 (39) hide show
  1. {process_bigraph-0.0.51/process_bigraph.egg-info → process_bigraph-1.0.2}/PKG-INFO +1 -3
  2. process_bigraph-1.0.2/process_bigraph/__init__.py +36 -0
  3. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/composite.py +113 -69
  4. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/emitter.py +38 -116
  5. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/experiments/minimal_gillespie.py +1 -2
  6. process_bigraph-1.0.2/process_bigraph/processes/__init__.py +3 -0
  7. process_bigraph-1.0.2/process_bigraph/processes/examples.py +243 -0
  8. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/processes/growth_division.py +31 -14
  9. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/processes/parameter_scan.py +36 -35
  10. process_bigraph-1.0.2/process_bigraph/protocols/__init__.py +22 -0
  11. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/protocols/parallel.py +89 -30
  12. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/protocols/rest.py +57 -32
  13. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/run.py +3 -3
  14. process_bigraph-1.0.2/process_bigraph/types/__init__.py +2 -0
  15. process_bigraph-1.0.2/process_bigraph/types/process.py +76 -0
  16. {process_bigraph-0.0.51 → process_bigraph-1.0.2/process_bigraph.egg-info}/PKG-INFO +1 -3
  17. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph.egg-info/SOURCES.txt +4 -9
  18. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph.egg-info/requires.txt +0 -2
  19. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/pyproject.toml +4 -6
  20. process_bigraph-0.0.51/process_bigraph/__init__.py +0 -58
  21. process_bigraph-0.0.51/process_bigraph/package/__init__.py +0 -0
  22. process_bigraph-0.0.51/process_bigraph/package/discover.py +0 -103
  23. process_bigraph-0.0.51/process_bigraph/process_types.py +0 -471
  24. process_bigraph-0.0.51/process_bigraph/processes/__init__.py +0 -26
  25. process_bigraph-0.0.51/process_bigraph/protocols/__init__.py +0 -17
  26. process_bigraph-0.0.51/process_bigraph/protocols/docker.py +0 -262
  27. process_bigraph-0.0.51/process_bigraph/protocols/local.py +0 -43
  28. process_bigraph-0.0.51/process_bigraph/protocols/protocol.py +0 -3
  29. process_bigraph-0.0.51/process_bigraph/tests.py +0 -1330
  30. process_bigraph-0.0.51/setup.py +0 -10
  31. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/AUTHORS.md +0 -0
  32. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/LICENSE +0 -0
  33. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/README.md +0 -0
  34. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/experiments/__init__.py +0 -0
  35. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/protocols/socket.py +0 -0
  36. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph/units.py +0 -0
  37. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph.egg-info/dependency_links.txt +0 -0
  38. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/process_bigraph.egg-info/top_level.txt +0 -0
  39. {process_bigraph-0.0.51 → process_bigraph-1.0.2}/setup.cfg +0 -0
@@ -1,17 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: process-bigraph
3
- Version: 0.0.51
3
+ Version: 1.0.2
4
4
  Summary: protocol and execution for compositional systems biology
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  License-File: AUTHORS.md
9
9
  Requires-Dist: bigraph-schema
10
- Requires-Dist: docker>=7.1.0
11
10
  Requires-Dist: ipdb>=0.13.13
12
11
  Requires-Dist: matplotlib
13
12
  Requires-Dist: pint>=0.24.4
14
- Requires-Dist: pytest
15
13
  Dynamic: license-file
16
14
 
17
15
  # Process-Bigraph
@@ -0,0 +1,36 @@
1
+ from bigraph_schema import allocate_core
2
+
3
+ from process_bigraph.composite import Process, Step, Composite, interval_time_precision
4
+ from process_bigraph.emitter import Emitter, gather_emitter_results, generate_emitter_state
5
+ from process_bigraph.types import StepLink, ProcessLink, CompositeLink
6
+
7
+
8
+ import pprint
9
+ pretty = pprint.PrettyPrinter(indent=2)
10
+
11
+ def pp(x):
12
+ """Print ``x`` in a pretty format."""
13
+ pretty.pprint(x)
14
+
15
+ def pf(x):
16
+ """Format ``x`` for display."""
17
+ return pretty.pformat(x)
18
+
19
+
20
+ def register_types(core):
21
+ core.register_type('interval', {
22
+ '_inherit': 'float'})
23
+
24
+ core.register_type('default 1', {
25
+ '_inherit': 'float',
26
+ '_default': 1.0})
27
+
28
+ core.register_type('ode_config', {
29
+ 'stoichiometry': {
30
+ '_type': 'array',
31
+ '_data': 'int64'},
32
+ 'rates': 'map[float]',
33
+ 'species': 'map[float]'})
34
+
35
+ return core
36
+
@@ -24,8 +24,8 @@ from typing import (
24
24
  import collections
25
25
 
26
26
  from bigraph_schema import (
27
- Edge, Registry, TypeSystem, visit_method,
28
- get_path, set_path, resolve_path, hierarchy_depth, deep_merge,
27
+ Edge,
28
+ get_path, set_path, resolve_path, hierarchy_depth,
29
29
  is_schema_key, strip_schema_keys)
30
30
 
31
31
  from bigraph_schema.protocols import local_lookup_module
@@ -120,6 +120,8 @@ def find_step_triggers(
120
120
 
121
121
  for wire in wire_paths:
122
122
  trigger_path = resolve_path(prefix + tuple(wire))
123
+ if isinstance(trigger_path, list):
124
+ import ipdb; ipdb.set_trace()
123
125
  triggers.setdefault(trigger_path, []).append(path)
124
126
 
125
127
  return triggers
@@ -404,7 +406,7 @@ class Open(Edge):
404
406
  'initial_state', 'inputs', 'outputs', 'update')
405
407
 
406
408
  ATTRIBUTE_READ_COMMANDS = (
407
- 'config', 'composition', 'state')
409
+ 'config', 'schema', 'state')
408
410
 
409
411
 
410
412
  def __init__(self, config=None, core=None):
@@ -678,7 +680,7 @@ def as_step(inputs, outputs, core=None):
678
680
  FunctionStep.__name__ = step_name + 'Step'
679
681
 
680
682
  if core is not None:
681
- core.register_process(step_name, FunctionStep)
683
+ core.register_link(step_name, FunctionStep)
682
684
 
683
685
  return FunctionStep
684
686
 
@@ -710,7 +712,7 @@ def as_process(inputs, outputs, core=None):
710
712
  FunctionProcess.__name__ = process_name + 'Process'
711
713
 
712
714
  if core is not None:
713
- core.register_process(process_name, FunctionProcess)
715
+ core.register_link(process_name, FunctionProcess)
714
716
 
715
717
  return FunctionProcess
716
718
 
@@ -748,13 +750,13 @@ class ProcessEnsemble(Process):
748
750
  inputs_func = getattr(self, attr_name)
749
751
  if callable(inputs_func):
750
752
  inputs = inputs_func()
751
- union_inputs = self.core.resolve_schemas(union_inputs, inputs)
753
+ union_inputs = self.core.resolve(union_inputs, inputs)
752
754
 
753
755
  if attr_name.startswith('outputs_'):
754
756
  outputs_func = getattr(self, attr_name)
755
757
  if callable(outputs_func):
756
758
  outputs = outputs_func()
757
- union_outputs = self.core.resolve_schemas(union_outputs, outputs)
759
+ union_outputs = self.core.resolve(union_outputs, outputs)
758
760
 
759
761
  return {
760
762
  'inputs': union_inputs,
@@ -836,8 +838,8 @@ class Composite(Process):
836
838
  """
837
839
 
838
840
  config_schema = {
839
- 'composition': 'schema',
840
- 'state': 'tree[any]',
841
+ 'schema': 'schema',
842
+ 'state': 'tree[node]',
841
843
  'interface': {
842
844
  'inputs': 'schema',
843
845
  'outputs': 'schema'
@@ -860,7 +862,7 @@ class Composite(Process):
860
862
 
861
863
  This method:
862
864
  - Adds `global_time` to schema/state if missing
863
- - Generates the full composition/state tree
865
+ - Generates the full schema/state tree
864
866
  - Finds all step/process instances
865
867
  - Resolves the schema bridge
866
868
  - Prepares the step execution network
@@ -870,12 +872,12 @@ class Composite(Process):
870
872
  config: Optional override configuration (usually not needed).
871
873
  """
872
874
 
873
- # Get the initial composition schema from config.
874
- initial_composition = self.config.get('composition', {})
875
+ # Get the initial schema schema from config.
876
+ initial_schema = self.config.get('schema', {})
875
877
 
876
878
  # Ensure 'global_time' is explicitly declared in the schema.
877
- if 'global_time' not in initial_composition:
878
- initial_composition['global_time'] = 'float'
879
+ if 'global_time' not in initial_schema:
880
+ initial_schema['global_time'] = 'float'
879
881
 
880
882
  # Get the initial state from config.
881
883
  initial_state = self.config.get('state', {})
@@ -885,8 +887,9 @@ class Composite(Process):
885
887
  initial_state['global_time'] = 0.0
886
888
 
887
889
  # Generate internal schema and state structures using the core engine.
888
- self.composition, self.state = self.core.generate(
889
- initial_composition,
890
+ # self.schema, self.state = self.core.generate(
891
+ self.schema, self.state = self.core.realize(
892
+ initial_schema,
890
893
  initial_state)
891
894
 
892
895
  # Load the bridge configuration, which defines how inputs/outputs connect to the world.
@@ -902,29 +905,39 @@ class Composite(Process):
902
905
  self.edge_paths = {**self.process_paths, **self.step_paths}
903
906
 
904
907
  # Initialize each process/step's state and accumulate it into a unified state tree.
905
- edge_state: Dict[str, Any] = {}
908
+ edge_schema = {}
909
+ edge_state = {}
906
910
  for path, edge in self.edge_paths.items():
907
911
  # Generate the initial state for this specific edge (process or step).
908
- initial = self.core.initialize_edge_state(
909
- self.composition,
910
- path,
911
- edge)
912
+ initial_schema, initial_state = self.core.link_state(
913
+ edge,
914
+ path)
912
915
 
913
916
  # Merge the new edge state with the global state tree, checking for conflicts.
914
917
  try:
915
- edge_state = deep_merge(edge_state, initial)
916
- except Exception:
918
+ edge_schema, edge_state = self.core.combine(
919
+ edge_schema, edge_state,
920
+ initial_schema, initial_state)
921
+
922
+ except Exception as e:
917
923
  raise Exception(
918
924
  f'initial state from edge does not match initial state from other edges:\n'
919
925
  f'{path}\n{edge}\n{edge_state}'
926
+ f'{e.message}'
920
927
  )
921
928
 
922
929
  # Apply the merged edge_state into the global state and update instance paths.
923
- self.merge(self.composition, edge_state)
930
+ if edge_state:
931
+ self.schema, self.state = self.core.combine(
932
+ edge_schema, edge_state,
933
+ self.schema, self.state)
924
934
 
925
935
  # Wire the input/output schema for the Composite from the bridge config.
926
936
  self.process_schema = {
927
- port: self.core.wire_schema(self.composition, self.bridge[port])
937
+ port: self.core.wire_schema(
938
+ self.schema,
939
+ self.state,
940
+ self.bridge[port])
928
941
  for port in ['inputs', 'outputs']
929
942
  }
930
943
 
@@ -952,7 +965,7 @@ class Composite(Process):
952
965
  Load a Composite from a saved JSON file.
953
966
 
954
967
  Args:
955
- path: Path to the saved composition file.
968
+ path: Path to the saved schema file.
956
969
  core: Optional core context providing deserialization.
957
970
 
958
971
  Returns:
@@ -960,8 +973,6 @@ class Composite(Process):
960
973
  """
961
974
  with open(path) as data:
962
975
  document = json.load(data)
963
- composition = document['composition']
964
- document['composition'] = core.deserialize('schema', composition)
965
976
  return cls(document, core=core)
966
977
 
967
978
  def clean_front(self, state):
@@ -976,7 +987,13 @@ class Composite(Process):
976
987
  - self.step_paths
977
988
  """
978
989
  self.process_paths = find_instance_paths(state, 'process_bigraph.composite.Process')
979
- self.step_paths = find_instance_paths(state, 'process_bigraph.composite.Step')
990
+ if hasattr(self, 'step_paths'):
991
+ previous_step_paths = self.step_paths.keys()
992
+ self.step_paths = find_instance_paths(state, 'process_bigraph.composite.Step')
993
+ if previous_step_paths != self.step_paths.keys():
994
+ self.build_step_network()
995
+ else:
996
+ self.step_paths = find_instance_paths(state, 'process_bigraph.composite.Step')
980
997
 
981
998
  all_paths = set(
982
999
  list(self.process_paths.keys()) +
@@ -999,12 +1016,13 @@ class Composite(Process):
999
1016
  path: Path where merge should occur (default: root).
1000
1017
  """
1001
1018
  path = path or []
1002
- self.composition, self.state = self.core.merge(
1003
- self.composition,
1019
+ # self.schema, self.state = self.core.merge(
1020
+ self.schema, self.state = self.core.combine(
1021
+ self.schema,
1004
1022
  self.state,
1005
- path,
1006
1023
  schema,
1007
1024
  state)
1025
+
1008
1026
  self.find_instance_paths(self.state)
1009
1027
 
1010
1028
  def merge_schema(
@@ -1025,10 +1043,10 @@ class Composite(Process):
1025
1043
  scoped_schema = set_path({}, path, schema)
1026
1044
 
1027
1045
  # Merge it into the existing schema
1028
- self.composition = self.core.merge_schemas(self.composition, scoped_schema)
1046
+ self.schema = self.core.merge(self.schema, scoped_schema)
1029
1047
 
1030
1048
  # Re-generate state based on the new schema structure
1031
- self.composition, self.state = self.core.generate(self.composition, self.state)
1049
+ self.schema, self.state = self.core.generate(self.schema, self.state)
1032
1050
 
1033
1051
  # Re-scan the state tree for processes and steps
1034
1052
  self.find_instance_paths(self.state)
@@ -1043,7 +1061,7 @@ class Composite(Process):
1043
1061
  """
1044
1062
  path = path or []
1045
1063
  scoped_update = set_path({}, path, update)
1046
- self.state = self.core.apply(self.composition, self.state, scoped_update)
1064
+ self.state = self.core.apply(self.schema, self.state, scoped_update)
1047
1065
  self.find_instance_paths(self.state)
1048
1066
 
1049
1067
 
@@ -1058,16 +1076,17 @@ class Composite(Process):
1058
1076
  Returns:
1059
1077
  A serialized representation of the current state.
1060
1078
  """
1061
- return self.core.serialize(self.composition, self.state)
1079
+ return self.core.serialize(self.schema, self.state)
1062
1080
 
1063
1081
  def serialize_schema(self) -> Dict[str, Any]:
1064
1082
  """
1065
- Serialize the composition (schema) using the core serializer.
1083
+ Serialize the schema (schema) using the core serializer.
1066
1084
 
1067
1085
  Returns:
1068
1086
  A serialized schema representation.
1069
1087
  """
1070
- return self.core.serialize('schema', self.composition)
1088
+ return self.core.render(self.schema)
1089
+ # return self.core.serialize('schema', self.schema)
1071
1090
 
1072
1091
  def save(
1073
1092
  self,
@@ -1092,7 +1111,7 @@ class Composite(Process):
1092
1111
  if state:
1093
1112
  document['state'] = self.serialize_state()
1094
1113
  if schema:
1095
- document['composition'] = self.serialize_schema()
1114
+ document['schema'] = self.serialize_schema()
1096
1115
 
1097
1116
  os.makedirs(outdir, exist_ok=True)
1098
1117
  filepath = os.path.join(outdir, filename)
@@ -1128,13 +1147,15 @@ class Composite(Process):
1128
1147
  """
1129
1148
  state = state or self.state
1130
1149
 
1131
- bridge_view = self.core.view(
1132
- self.interface()['outputs'],
1133
- self.bridge['outputs'],
1150
+ # if 'environment' in state and '_add' in state['environment']:
1151
+ # import ipdb; ipdb.set_trace()
1152
+
1153
+ bridge_view = self.core.view_ports(
1154
+ self.schema,
1155
+ state,
1134
1156
  (),
1135
- top_schema=self.composition,
1136
- top_state=state
1137
- )
1157
+ self.interface()['outputs'],
1158
+ self.bridge['outputs'])
1138
1159
 
1139
1160
  return bridge_view
1140
1161
 
@@ -1268,14 +1289,13 @@ class Composite(Process):
1268
1289
 
1269
1290
  for step_path in step_paths:
1270
1291
  step = get_path(self.state, step_path)
1271
- state = self.core.view_edge(
1272
- self.composition, self.state, step_path, 'inputs'
1273
- )
1292
+ state = self.core.view(
1293
+ self.schema, self.state, step_path, 'inputs')
1274
1294
 
1275
1295
  # Steps are always invoked with interval = -1.0
1276
1296
  step_update = self.process_update(
1277
- step_path, step, state, -1.0, 'outputs'
1278
- )
1297
+ step_path, step, state, -1.0, 'outputs')
1298
+
1279
1299
  updates.append(step_update)
1280
1300
 
1281
1301
  update_paths = self.apply_updates(updates)
@@ -1364,7 +1384,7 @@ class Composite(Process):
1364
1384
  and captures its update as a deferred computation.
1365
1385
 
1366
1386
  Args:
1367
- path: The path to the process in the state/composition tree.
1387
+ path: The path to the process in the state/schema tree.
1368
1388
  process: The dictionary representing the process (must contain 'interval').
1369
1389
  end_time: The simulation time to run up to.
1370
1390
  full_step: The current smallest time step among all processes.
@@ -1387,7 +1407,10 @@ class Composite(Process):
1387
1407
  state = future_front['state']
1388
1408
  else:
1389
1409
  # Otherwise, slice the current state for the process
1390
- state = self.core.view_edge(self.composition, self.state, path)
1410
+ state = self.core.view(
1411
+ self.schema,
1412
+ self.state,
1413
+ path)
1391
1414
  process_interval = process['interval']
1392
1415
 
1393
1416
  # Determine the target time for the next update
@@ -1437,7 +1460,7 @@ class Composite(Process):
1437
1460
  into absolute state terms until `.get()` is called on the returned `Defer` object.
1438
1461
 
1439
1462
  Args:
1440
- path: The path to the process in the state/composition tree.
1463
+ path: The path to the process in the state/schema tree.
1441
1464
  process: The dictionary representing the process instance (must include 'instance').
1442
1465
  states: The current state values at the process’s ports.
1443
1466
  interval: The time interval to simulate.
@@ -1451,14 +1474,22 @@ class Composite(Process):
1451
1474
 
1452
1475
  # Invoke the process and retrieve a wrapped SyncUpdate object
1453
1476
  update = process['instance'].invoke(clean_state, interval)
1454
-
1455
1477
  # This nested function projects the update into the global state at the given path
1456
- def defer_project(update_result: Any, args: Tuple[Any, Any, Union[str, Tuple[str, ...]]]) -> Any:
1478
+ def defer_project(update_results: Any, args: Tuple[Any, Any, Union[str, Tuple[str, ...]]]) -> Any:
1457
1479
  schema, state, process_path = args
1458
- return self.core.project_edge(schema, state, process_path, update_result, ports_key)
1480
+
1481
+ if not isinstance(update_results, list):
1482
+ update_results = [update_results]
1483
+
1484
+ return [self.core.project(
1485
+ schema,
1486
+ state,
1487
+ process_path,
1488
+ update_result,
1489
+ ports_key) for update_result in update_results]
1459
1490
 
1460
1491
  # Return a deferred object that will project the update when requested
1461
- return Defer(update, defer_project, (self.composition, self.state, path))
1492
+ return Defer(update, defer_project, (self.schema, self.state, path))
1462
1493
 
1463
1494
  def apply_updates(self, updates: List["Defer"]) -> List[Union[str, Tuple[str, ...]]]:
1464
1495
  """
@@ -1484,22 +1515,33 @@ class Composite(Process):
1484
1515
  if not isinstance(series, list):
1485
1516
  series = [series]
1486
1517
 
1487
- for update in series:
1518
+ for update_schema, update_state in series:
1488
1519
  # if update and isinstance(update, dict) and 'environment' in update and update['environment'] and isinstance(update['environment'], dict) and '_react' in update['environment']:
1489
1520
  # import ipdb; ipdb.set_trace()
1490
1521
 
1491
1522
  # Extract all hierarchical paths touched by this update
1492
- paths = hierarchy_depth(update)
1523
+ paths = hierarchy_depth(update_state)
1493
1524
  update_paths.extend(paths.keys())
1494
1525
 
1495
- # Apply update directly to the internal state
1496
- self.state = self.core.apply_update(self.composition, self.state, update)
1526
+ # Apply update directly to the internal state,
1527
+ # using the schema from the link itself
1528
+ self.state, merges = self.core.apply(
1529
+ update_schema,
1530
+ self.state,
1531
+ update_state)
1532
+
1533
+ self.schema = self.core.resolve_merges(
1534
+ self.schema,
1535
+ merges)
1497
1536
 
1498
1537
  # Read updated bridge outputs, if available
1499
- bridge_update = self.read_bridge(update)
1538
+ bridge_update = self.read_bridge(update_state)
1500
1539
  if bridge_update:
1501
1540
  self.bridge_updates.append(bridge_update)
1502
1541
 
1542
+ self.schema, self.state = self.core.realize(self.schema, self.state)
1543
+
1544
+ # TODO: are we doing this twice?
1503
1545
  # Refresh process and step instance paths
1504
1546
  self.find_instance_paths(self.state)
1505
1547
 
@@ -1549,17 +1591,19 @@ class Composite(Process):
1549
1591
  Returns:
1550
1592
  A list of updates generated for the bridge outputs.
1551
1593
  """
1552
- projection = self.core.project(
1594
+ project_schema, project_state = self.core.project_ports(
1553
1595
  self.interface()['inputs'],
1554
1596
  self.bridge['inputs'],
1555
1597
  [],
1556
- state
1557
- )
1598
+ state)
1599
+ self.merge({}, project_state)
1558
1600
 
1559
- self.merge({}, projection)
1560
- self.run(interval)
1601
+ # first_update = self.read_bridge(
1602
+ # self.state)
1603
+ # self.bridge_updates = [first_update]
1561
1604
 
1562
- updates = self.bridge_updates
1563
1605
  self.bridge_updates = []
1564
1606
 
1565
- return updates
1607
+ self.run(interval)
1608
+
1609
+ return self.bridge_updates