bigraph-schema 0.0.11__tar.gz → 0.0.12__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.

Potentially problematic release.


This version of bigraph-schema might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bigraph-schema
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: A serializable type schema for compositional systems biology
5
5
  Home-page: https://github.com/vivarium-collective/bigraph-schema
6
6
  Author: Eran Agmon, Ryan Spangler
@@ -156,6 +156,26 @@ def establish_path(tree, path, top=None, cursor=()):
156
156
  cursor=cursor + (head,))
157
157
 
158
158
 
159
+ def set_path(tree, path, value, top=None, cursor=None):
160
+ if value is None:
161
+ return None
162
+ final = path[-1]
163
+ towards = path[:-1]
164
+ destination = establish_path(tree, towards)
165
+ destination[final] = value
166
+ return tree
167
+
168
+
169
+ def remove_path(tree, path):
170
+ if path is None or len(path) == 0:
171
+ return None
172
+
173
+ upon = get_path(tree, path[:-1])
174
+ if upon is not None:
175
+ del upon[path[-1]]
176
+ return tree
177
+
178
+
159
179
  class Registry(object):
160
180
  def __init__(self):
161
181
  """A Registry holds a collection of functions or objects."""
@@ -3,7 +3,7 @@ import pprint
3
3
  import pytest
4
4
 
5
5
  from bigraph_schema.parse import parse_expression
6
- from bigraph_schema.registry import Registry, TypeRegistry, RegistryRegistry, type_schema_keys, optional_schema_keys, deep_merge, get_path, establish_path, non_schema_keys
6
+ from bigraph_schema.registry import Registry, TypeRegistry, RegistryRegistry, type_schema_keys, optional_schema_keys, deep_merge, get_path, establish_path, set_path, remove_path, non_schema_keys
7
7
  from bigraph_schema.units import units, render_units_type, parse_dimensionality
8
8
 
9
9
 
@@ -151,6 +151,10 @@ class SchemaTypes():
151
151
  schema.get('_bindings'),
152
152
  self)
153
153
 
154
+ elif isinstance(schema, str) or isinstance(schema, list):
155
+ schema = self.access(schema)
156
+ state = self.apply_update(schema, state, update)
157
+
154
158
  elif isinstance(update, dict):
155
159
  for key, branch in update.items():
156
160
  if key not in schema:
@@ -162,10 +166,8 @@ class SchemaTypes():
162
166
  branch)
163
167
 
164
168
  state[key] = subupdate
165
-
166
169
  else:
167
- schema = self.access(schema)
168
- state = self.apply_update(schema, state, update)
170
+ raise Exception(f'trying to apply update\n {update}\nto state\n {state}\nwith schema\n{schema}, but the update is not a dict')
169
171
 
170
172
  return state
171
173
 
@@ -338,6 +340,126 @@ class SchemaTypes():
338
340
  state=state)
339
341
 
340
342
 
343
+ def ports_and_wires(self, schema, instance, edge_path):
344
+ found = self.access(schema)
345
+
346
+ edge_schema = get_path(found, edge_path)
347
+ ports = edge_schema.get('_ports')
348
+ edge_state = get_path(instance, edge_path)
349
+ wires = edge_state.get('wires')
350
+
351
+ return ports, wires
352
+
353
+
354
+ def project_state(self, schema, wires, instance, path):
355
+ result = {}
356
+ if isinstance(wires, str):
357
+ wires = [wires]
358
+ if isinstance(wires, list):
359
+ result = get_path(instance, path + wires)
360
+ elif isinstance(wires, dict):
361
+ result = {
362
+ port_key: self.project_state(
363
+ schema[port_key],
364
+ wires[port_key],
365
+ instance,
366
+ path)
367
+ for port_key in wires}
368
+ else:
369
+ raise Exception(f'trying to project state with these ports:\n{ports}\nbut not sure what these wires are:\n{wires}')
370
+
371
+ return result
372
+
373
+
374
+ def project(self, schema, instance, edge_path=()):
375
+ '''
376
+ project the state of the current instance into a form
377
+ the edge expects, based on its ports
378
+ '''
379
+
380
+ if schema is None:
381
+ return None
382
+ if instance is None:
383
+ instance = self.default(schema)
384
+
385
+ ports, wires = self.ports_and_wires(schema, instance, edge_path=edge_path)
386
+
387
+ if ports is None:
388
+ return None
389
+ if wires is None:
390
+ return None
391
+
392
+ return self.project_state(
393
+ ports,
394
+ wires,
395
+ instance,
396
+ edge_path[:-1])
397
+
398
+
399
+ def invert_state(self, ports, wires, path, states):
400
+ result = {}
401
+
402
+ if isinstance(wires, str):
403
+ wires = [wires]
404
+
405
+ if isinstance(wires, list):
406
+ destination = path + wires
407
+ result = set_path(
408
+ result,
409
+ destination,
410
+ states)
411
+
412
+ elif isinstance(wires, dict):
413
+ branches = [
414
+ self.invert_state(
415
+ ports.get(key),
416
+ wires[key],
417
+ path,
418
+ states.get(key))
419
+ for key in wires.keys()]
420
+
421
+ branches = [
422
+ branch
423
+ for branch in branches
424
+ if branch is not None and list(branch)[0][1] is not None]
425
+
426
+ result = {}
427
+ for branch in branches:
428
+ deep_merge(result, branch)
429
+ else:
430
+ raise Exception(
431
+ f'inverting state\n {state}\naccording to ports schema\n {schema}\nbut wires are not recognized\n {wires}')
432
+
433
+ return result
434
+
435
+
436
+ def invert(self, schema, instance, edge_path, states):
437
+ '''
438
+ given states from the perspective of an edge (through
439
+ it's ports), produce states aligned to the tree
440
+ the wires point to.
441
+ (inverse of project)
442
+ '''
443
+
444
+ if schema is None:
445
+ return None
446
+ if instance is None:
447
+ instance = self.default(schema)
448
+
449
+ ports, wires = self.ports_and_wires(schema, instance, edge_path)
450
+
451
+ if ports is None:
452
+ return None
453
+ if wires is None:
454
+ return None
455
+
456
+ return self.invert_state(
457
+ ports,
458
+ wires,
459
+ edge_path[:-1],
460
+ states)
461
+
462
+
341
463
  def link_place(self, place, link):
342
464
  pass
343
465
 
@@ -355,20 +477,18 @@ class SchemaTypes():
355
477
  return {}
356
478
 
357
479
 
358
- def query(self, schema, state, redex):
480
+ def query(self, schema, instance, redex):
359
481
  subschema = {}
360
482
  return subschema
361
483
 
362
484
 
363
- def substitute(self, schema, state, reactum):
364
- return state
365
-
366
-
367
- def react(self, schema, redex, reactum):
485
+ def react(self, schema, instance, redex, reactum):
368
486
  return {}
369
487
 
370
488
 
371
489
  def accumulate(current, update, bindings, types):
490
+ if update is None:
491
+ import ipdb; ipdb.set_trace()
372
492
  return current + update
373
493
 
374
494
  def concatenate(current, update, bindings, types):
@@ -455,7 +575,28 @@ def evaluate(code, bindings, types):
455
575
 
456
576
  # TODO: make these work
457
577
  def apply_tree(current, update, bindings, types):
458
- pass
578
+ if isinstance(update, dict):
579
+ if current is None:
580
+ current = {}
581
+ for key, branch in update.items():
582
+ if key == '_add':
583
+ current.update(branch)
584
+ elif key == '_remove':
585
+ current = remove_path(current, branch)
586
+ else:
587
+ current[key] = apply_tree(
588
+ current.get(key),
589
+ branch,
590
+ bindings,
591
+ types)
592
+
593
+ return current
594
+ else:
595
+ leaf_type = bindings['leaf']
596
+ if current is None:
597
+ current = types.default(leaf_type)
598
+ return types.apply(leaf_type, current, update)
599
+
459
600
 
460
601
  def divide_tree(tree, bindings, types):
461
602
  result = [{}, {}]
@@ -1012,58 +1153,44 @@ def test_fill_from_parse(base_types):
1012
1153
  'port A': ['a']}}}
1013
1154
 
1014
1155
 
1015
- def test_fill_in_disconnected_port(base_types):
1016
- test_schema = {
1017
- 'edge1': {
1018
- '_type': 'edge',
1019
- '_ports': {
1020
- '1': {'_type': 'float'}}}}
1021
-
1022
- test_state = {}
1023
-
1024
-
1025
- def test_fill_type_mismatch(base_types):
1026
- test_schema = {
1027
- 'a': {'_type': 'int', '_value': 2},
1028
- 'edge1': {
1029
- '_type': 'edge',
1030
- '_ports': {
1031
- '1': {'_type': 'float'},
1032
- '2': {'_type': 'float'}},
1033
- 'wires': {
1034
- '1': ['..', 'a'],
1035
- '2': ['a']},
1036
- 'a': 5}}
1037
-
1038
-
1039
- def test_edge_type_mismatch(base_types):
1040
- test_schema = {
1041
- 'edge1': {
1042
- '_type': 'edge',
1043
- '_ports': {
1044
- '1': {'_type': 'float'}},
1045
- 'wires': {
1046
- '1': ['..', 'a']}},
1047
- 'edge2': {
1048
- '_type': 'edge',
1049
- '_ports': {
1050
- '1': {'_type': 'int'}},
1051
- 'wires': {
1052
- '1': ['..', 'a']}}}
1053
-
1054
-
1055
- def test_fill_nested_store(base_types):
1056
- test_schema = {
1057
- 'edge1': {
1058
- '_type': 'edge',
1059
- '_ports': {
1060
- '1': {'_type': 'float'},
1061
- },
1062
- 'wires': {
1063
- '1': ['somewhere', 'down', 'this', 'path']
1064
- },
1065
- },
1066
- }
1156
+ # def test_fill_in_disconnected_port(base_types):
1157
+ # test_schema = {
1158
+ # 'edge1': {
1159
+ # '_type': 'edge',
1160
+ # '_ports': {
1161
+ # '1': {'_type': 'float'}}}}
1162
+
1163
+ # test_state = {}
1164
+
1165
+
1166
+ # def test_fill_type_mismatch(base_types):
1167
+ # test_schema = {
1168
+ # 'a': {'_type': 'int', '_value': 2},
1169
+ # 'edge1': {
1170
+ # '_type': 'edge',
1171
+ # '_ports': {
1172
+ # '1': {'_type': 'float'},
1173
+ # '2': {'_type': 'float'}},
1174
+ # 'wires': {
1175
+ # '1': ['..', 'a'],
1176
+ # '2': ['a']},
1177
+ # 'a': 5}}
1178
+
1179
+
1180
+ # def test_edge_type_mismatch(base_types):
1181
+ # test_schema = {
1182
+ # 'edge1': {
1183
+ # '_type': 'edge',
1184
+ # '_ports': {
1185
+ # '1': {'_type': 'float'}},
1186
+ # 'wires': {
1187
+ # '1': ['..', 'a']}},
1188
+ # 'edge2': {
1189
+ # '_type': 'edge',
1190
+ # '_ports': {
1191
+ # '1': {'_type': 'int'}},
1192
+ # 'wires': {
1193
+ # '1': ['..', 'a']}}}
1067
1194
 
1068
1195
 
1069
1196
  def test_establish_path(base_types):
@@ -1128,13 +1255,8 @@ def test_expected_schema(base_types):
1128
1255
 
1129
1256
  dual_process_schema = {
1130
1257
  'process1': 'edge[port1:float|port2:int]',
1131
- # 'process1': {
1132
- # '_ports': {
1133
- # 'port1': 'float',
1134
- # 'port2': 'int',
1135
- # },
1136
- # },
1137
1258
  'process2': {
1259
+ '_type': 'edge',
1138
1260
  '_ports': {
1139
1261
  'port1': 'float',
1140
1262
  'port2': 'int',
@@ -1151,12 +1273,6 @@ def test_expected_schema(base_types):
1151
1273
  # 'store1': 'process1:edge[port1:float|port2:int]|process2[port1:float|port2:int]',
1152
1274
  'store1': 'dual_process',
1153
1275
  'process3': 'edge[dual_process]'}
1154
- # 'process3': {
1155
- # '_ports': {
1156
- # 'port1': 'dual_process'
1157
- # }
1158
- # }
1159
- # }
1160
1276
 
1161
1277
  test_state = {
1162
1278
  'store1': {
@@ -1208,56 +1324,36 @@ def test_expected_schema(base_types):
1208
1324
 
1209
1325
 
1210
1326
  def test_link_place(base_types):
1327
+ # TODO: this form is more fundamental than the compressed/inline dict form,
1328
+ # and we should probably derive that from this form
1329
+
1211
1330
  bigraph = {
1212
1331
  'nodes': {
1213
- 'v0': {
1214
- '_type': 'int',
1215
- '_value': 0},
1216
- 'v1': {
1217
- '_type': 'int',
1218
- '_value': 1},
1219
- 'v2': {
1220
- '_type': 'int',
1221
- '_value': 2},
1222
- 'v3': {
1223
- '_type': 'int',
1224
- '_value': 3},
1225
- 'v4': {
1226
- '_type': 'int',
1227
- '_value': 4},
1228
- 'v5': {
1229
- '_type': 'int',
1230
- '_value': 5},
1231
- 'e0': {
1232
- '_type': 'edge[e0-0:int|e0-1:int|e0-2:int]',
1233
- 'wires': {
1234
- 'e0-0': 'v0',
1235
- 'e0-1': 'v1',
1236
- 'e0-2': 'v4'}},
1332
+ 'v0': 'int',
1333
+ 'v1': 'int',
1334
+ 'v2': 'int',
1335
+ 'v3': 'int',
1336
+ 'v4': 'int',
1337
+ 'v5': 'int',
1338
+ 'e0': 'edge[e0-0:int|e0-1:int|e0-2:int]',
1237
1339
  'e1': {
1238
- '_type': 'edge[e1-0:int|e2-0:int]',
1340
+ '_type': 'edge',
1239
1341
  '_ports': {
1240
1342
  'e1-0': 'int',
1241
- 'e2-0': 'int'},
1242
- 'wires': {
1243
- 'e1-0': 'v3',
1244
- 'e1-1': 'v1'}},
1343
+ 'e2-0': 'int'}},
1245
1344
  'e2': {
1246
- '_type': 'edge[e2-0:int|e2-1:int|e2-2:int]',
1247
- 'wires': {
1248
- 'e2-0': 'v3',
1249
- 'e2-1': 'v4',
1250
- 'e2-2': 'v5'}}},
1345
+ '_type': 'edge[e2-0:int|e2-1:int|e2-2:int]'}},
1346
+
1251
1347
  'place': {
1252
- 'v0': {
1253
- 'v1': {},
1254
- 'v2': {
1255
- 'v3': {}}},
1256
- 'v4': {
1257
- 'v5': {}},
1258
- 'e0': {},
1259
- 'e1': {},
1260
- 'e2': {}},
1348
+ 'v0': None,
1349
+ 'v1': 'v0',
1350
+ 'v2': 'v0',
1351
+ 'v3': 'v2',
1352
+ 'v4': None,
1353
+ 'v5': 'v4',
1354
+ 'e0': None,
1355
+ 'e1': None,
1356
+ 'e2': None},
1261
1357
 
1262
1358
  'link': {
1263
1359
  'e0': {
@@ -1267,23 +1363,44 @@ def test_link_place(base_types):
1267
1363
  'e1': {
1268
1364
  'e1-0': 'v3',
1269
1365
  'e1-1': 'v1'},
1366
+ 'e2': {
1367
+ 'e2-0': 'v3',
1368
+ 'e2-1': 'v4',
1369
+ 'e2-2': 'v5'}},
1370
+
1371
+ 'state': {
1372
+ 'v0': '1',
1373
+ 'v1': '1',
1374
+ 'v2': '2',
1375
+ 'v3': '3',
1376
+ 'v4': '5',
1377
+ 'v5': '8',
1378
+ 'e0': {
1379
+ 'wires': {
1380
+ 'e0-0': 'v0',
1381
+ 'e0-1': 'v1',
1382
+ 'e0-2': 'v4'}},
1383
+ 'e1': {
1384
+ 'wires': {
1385
+ 'e1-0': 'v3',
1386
+ 'e1-1': 'v1'}},
1270
1387
  'e2': {
1271
1388
  'e2-0': 'v3',
1272
1389
  'e2-1': 'v4',
1273
1390
  'e2-2': 'v5'}}}
1274
1391
 
1275
- placegraph = {
1392
+ placegraph = { # schema
1276
1393
  'v0': {
1277
- 'v1': {},
1394
+ 'v1': int,
1278
1395
  'v2': {
1279
- 'v3': {}}},
1396
+ 'v3': int}},
1280
1397
  'v4': {
1281
- 'v5': {}},
1282
- 'e0': 'int',
1283
- 'e1': 'int',
1284
- 'e2': 'int'}
1398
+ 'v5': int},
1399
+ 'e0': 'edge',
1400
+ 'e1': 'edge',
1401
+ 'e2': 'edge'}
1285
1402
 
1286
- hypergraph = {
1403
+ hypergraph = { # edges
1287
1404
  'e0': {
1288
1405
  'e0-0': 'v0',
1289
1406
  'e0-1': 'v1',
@@ -1380,6 +1497,8 @@ def test_serialize_deserialize(cube_types):
1380
1497
  def test_project(cube_types):
1381
1498
  schema = {
1382
1499
  'edge1': {
1500
+ # '_type': 'edge[1:int|2:float|3:string|4:tree[int]]',
1501
+ # '_type': 'edge',
1383
1502
  '_type': 'edge',
1384
1503
  '_ports': {
1385
1504
  '1': 'int',
@@ -1403,15 +1522,12 @@ def test_project(cube_types):
1403
1522
  # TODO: support overriding various type methods
1404
1523
  instance = {
1405
1524
  'edge1': {
1406
- # '_type': 'edge[1:int|2:float|3:string|4:tree[int]]',
1407
- # '_type': 'edge',
1408
1525
  'wires': {
1409
1526
  '1': ['a0', 'a0.0'],
1410
1527
  '2': ['a0', 'a0.1'],
1411
1528
  '3': ['a0', 'a0.2', 'a0.2.0'],
1412
1529
  '4': ['a1']}},
1413
1530
  'a1': {
1414
- '_type': 'tree[int]',
1415
1531
  'branch1': {
1416
1532
  'branch2': 11,
1417
1533
  'branch3': 22},
@@ -1419,11 +1535,28 @@ def test_project(cube_types):
1419
1535
 
1420
1536
  instance = cube_types.fill(schema, instance)
1421
1537
 
1422
- # TODO: does project require the schema?
1423
- states = cube_types.project(schema, instance, ['edge1'])
1424
- update = cube_types.inverse(schema, states, ['edge1'])
1538
+ states = cube_types.project(
1539
+ schema,
1540
+ instance,
1541
+ ['edge1'])
1425
1542
 
1426
- assert update == {}
1543
+ update = cube_types.invert(
1544
+ schema,
1545
+ instance,
1546
+ ['edge1'],
1547
+ states)
1548
+
1549
+ assert update == {
1550
+ 'a0': {
1551
+ 'a0.0': 0,
1552
+ 'a0.1': 0.0,
1553
+ 'a0.2': {
1554
+ 'a0.2.0': ''}},
1555
+ 'a1': {
1556
+ 'branch1': {
1557
+ 'branch2': 11,
1558
+ 'branch3': 22},
1559
+ 'branch4': 44}}
1427
1560
 
1428
1561
  updated_instance = cube_types.apply(
1429
1562
  schema,
@@ -1432,18 +1565,44 @@ def test_project(cube_types):
1432
1565
 
1433
1566
  add_update = {
1434
1567
  '4': {
1568
+ 'branch6': 111,
1569
+ 'branch1': {
1570
+ '_add': {
1571
+ 'branch7': 4444},
1572
+ '_remove': ['branch2']},
1435
1573
  '_add': {
1436
- 'branch5': 55}}}
1574
+ 'branch5': 55},
1575
+ '_remove': ['branch4']}}
1437
1576
 
1438
- inverted_update = cube_types.inverse(
1577
+ inverted_update = cube_types.invert(
1439
1578
  schema,
1440
- add_update,
1441
- ['edge1'])
1579
+ instance,
1580
+ ['edge1'],
1581
+ add_update)
1442
1582
 
1443
- added_branch = cube_types.apply(
1583
+ modified_branch = cube_types.apply(
1444
1584
  schema,
1445
- inverted_update,
1446
- ['edge1'])
1585
+ instance,
1586
+ inverted_update)
1587
+
1588
+ assert modified_branch == {
1589
+ 'a0': {
1590
+ 'a0.0': 0,
1591
+ 'a0.1': 0.0,
1592
+ 'a0.2': {
1593
+ 'a0.2.0': ''}},
1594
+ 'a1': {
1595
+ 'branch1': {
1596
+ 'branch7': 4444,
1597
+ 'branch3': 44},
1598
+ 'branch5': 55,
1599
+ 'branch6': 111},
1600
+ 'edge1': {
1601
+ 'wires': {
1602
+ '1': ['a0', 'a0.0'],
1603
+ '2': ['a0', 'a0.1'],
1604
+ '3': ['a0', 'a0.2', 'a0.2.0'],
1605
+ '4': ['a1']}}}
1447
1606
 
1448
1607
 
1449
1608
  if __name__ == '__main__':
@@ -1461,4 +1620,4 @@ if __name__ == '__main__':
1461
1620
  test_fill_in_missing_nodes(types)
1462
1621
  test_fill_from_parse(types)
1463
1622
  test_serialize_deserialize(types)
1464
- # test_project(types)
1623
+ test_project(types)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bigraph-schema
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: A serializable type schema for compositional systems biology
5
5
  Home-page: https://github.com/vivarium-collective/bigraph-schema
6
6
  Author: Eran Agmon, Ryan Spangler
@@ -2,7 +2,7 @@ import re
2
2
  from setuptools import setup, find_packages
3
3
 
4
4
 
5
- VERSION = '0.0.11'
5
+ VERSION = '0.0.12'
6
6
 
7
7
 
8
8
  with open("README.md", "r") as readme:
File without changes