bigraph-schema 0.0.32__tar.gz → 0.0.37__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.
- {bigraph-schema-0.0.32/bigraph_schema.egg-info → bigraph-schema-0.0.37}/PKG-INFO +5 -1
- bigraph-schema-0.0.37/bigraph_schema/data.py +1 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/registry.py +119 -4
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/type_system.py +476 -174
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37/bigraph_schema.egg-info}/PKG-INFO +5 -1
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema.egg-info/SOURCES.txt +1 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/setup.py +1 -1
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/AUTHORS.md +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/LICENSE +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/README.md +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/__init__.py +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/parse.py +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/protocols.py +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/react.py +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema/units.py +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema.egg-info/dependency_links.txt +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema.egg-info/requires.txt +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema.egg-info/top_level.txt +0 -0
- {bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/setup.cfg +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bigraph-schema
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.37
|
|
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
|
|
7
7
|
Author-email: agmon.eran@gmail.com, ryan.spangler@gmail.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
8
10
|
Classifier: Development Status :: 3 - Alpha
|
|
9
11
|
Classifier: Intended Audience :: Developers
|
|
10
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -52,3 +54,5 @@ This resource will guide you through the core concepts and methods, helping you
|
|
|
52
54
|
## License
|
|
53
55
|
|
|
54
56
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/bigraph-schema/blob/main/LICENSE).
|
|
57
|
+
|
|
58
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -13,9 +13,12 @@ import traceback
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
15
|
from pprint import pformat as pf
|
|
16
|
+
from typing import Any, Tuple, Union, Optional, Mapping
|
|
17
|
+
from dataclasses import field, make_dataclass
|
|
16
18
|
|
|
17
19
|
from bigraph_schema.parse import parse_expression
|
|
18
20
|
from bigraph_schema.protocols import local_lookup_module, function_module
|
|
21
|
+
import bigraph_schema.data
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
NONE_SYMBOL = '!nil'
|
|
@@ -525,6 +528,34 @@ def fold_union(schema, state, method, values, core):
|
|
|
525
528
|
return result
|
|
526
529
|
|
|
527
530
|
|
|
531
|
+
def type_parameters_for(schema):
|
|
532
|
+
parameters = []
|
|
533
|
+
for key in schema['_type_parameters']:
|
|
534
|
+
subschema = schema.get(f'_{key}', 'any')
|
|
535
|
+
parameters.append(subschema)
|
|
536
|
+
|
|
537
|
+
return parameters
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def dataclass_union(schema, path, core):
|
|
541
|
+
parameters = type_parameters_for(schema)
|
|
542
|
+
subtypes = []
|
|
543
|
+
for parameter in parameters:
|
|
544
|
+
dataclass = core.dataclass(
|
|
545
|
+
parameter,
|
|
546
|
+
path)
|
|
547
|
+
|
|
548
|
+
if isinstance(dataclass, str):
|
|
549
|
+
subtypes.append(dataclass)
|
|
550
|
+
elif isinstance(dataclass, type):
|
|
551
|
+
subtypes.append(dataclass.__name__)
|
|
552
|
+
else:
|
|
553
|
+
subtypes.append(str(dataclass))
|
|
554
|
+
|
|
555
|
+
parameter_block = ', '.join(subtypes)
|
|
556
|
+
return eval(f'Union[{parameter_block}]')
|
|
557
|
+
|
|
558
|
+
|
|
528
559
|
def divide_any(schema, state, values, core):
|
|
529
560
|
divisions = values.get('divisions', 2)
|
|
530
561
|
|
|
@@ -551,6 +582,83 @@ def divide_any(schema, state, values, core):
|
|
|
551
582
|
for _ in range(divisions)]
|
|
552
583
|
|
|
553
584
|
|
|
585
|
+
def is_schema_key(schema, key):
|
|
586
|
+
return key.strip('_') not in schema.get('_type_parameters', []) and key.startswith('_')
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def resolve_any(schema, update, core):
|
|
590
|
+
outcome = schema.copy()
|
|
591
|
+
|
|
592
|
+
for key, subschema in update.items():
|
|
593
|
+
if not key in outcome or is_schema_key(update, key):
|
|
594
|
+
if subschema:
|
|
595
|
+
outcome[key] = subschema
|
|
596
|
+
else:
|
|
597
|
+
outcome[key] = core.resolve_schemas(
|
|
598
|
+
outcome.get(key),
|
|
599
|
+
update[key])
|
|
600
|
+
|
|
601
|
+
return outcome
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def dataclass_any(schema, path, core):
|
|
605
|
+
parts = path
|
|
606
|
+
if not parts:
|
|
607
|
+
parts = ['top']
|
|
608
|
+
dataclass_name = '_'.join(parts)
|
|
609
|
+
|
|
610
|
+
if isinstance(schema, dict):
|
|
611
|
+
type_name = schema.get('_type', 'any')
|
|
612
|
+
|
|
613
|
+
branches = {}
|
|
614
|
+
for key, subschema in schema.items():
|
|
615
|
+
if not key.startswith('_'):
|
|
616
|
+
branch = core.dataclass(
|
|
617
|
+
subschema,
|
|
618
|
+
path + [key])
|
|
619
|
+
|
|
620
|
+
def default(subschema=subschema):
|
|
621
|
+
return core.default(subschema)
|
|
622
|
+
|
|
623
|
+
branches[key] = (
|
|
624
|
+
key,
|
|
625
|
+
branch,
|
|
626
|
+
field(default_factory=default))
|
|
627
|
+
|
|
628
|
+
dataclass = make_dataclass(
|
|
629
|
+
dataclass_name,
|
|
630
|
+
branches.values(),
|
|
631
|
+
namespace={
|
|
632
|
+
'__module__': 'bigraph_schema.data'})
|
|
633
|
+
|
|
634
|
+
setattr(
|
|
635
|
+
bigraph_schema.data,
|
|
636
|
+
dataclass_name,
|
|
637
|
+
dataclass)
|
|
638
|
+
|
|
639
|
+
else:
|
|
640
|
+
schema = core.access(schema)
|
|
641
|
+
dataclass = core.dataclass(schema, path)
|
|
642
|
+
|
|
643
|
+
return dataclass
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def dataclass_tuple(schema, path, core):
|
|
647
|
+
parameters = type_parameters_for(schema)
|
|
648
|
+
subtypes = []
|
|
649
|
+
|
|
650
|
+
for index, key in enumerate(schema['type_parameters']):
|
|
651
|
+
subschema = schema.get(key, 'any')
|
|
652
|
+
subtype = core.dataclass(
|
|
653
|
+
subschema,
|
|
654
|
+
path + [index])
|
|
655
|
+
|
|
656
|
+
subtypes.append(subtype)
|
|
657
|
+
|
|
658
|
+
parameter_block = ', '.join(subtypes)
|
|
659
|
+
return eval(f'tuple[{parameter_block}]')
|
|
660
|
+
|
|
661
|
+
|
|
554
662
|
def divide_tuple(schema, state, values, core):
|
|
555
663
|
divisions = values.get('divisions', 2)
|
|
556
664
|
|
|
@@ -740,10 +848,13 @@ def merge_any(schema, current_state, new_state, core):
|
|
|
740
848
|
elif isinstance(new_state, dict):
|
|
741
849
|
if isinstance(current_state, dict):
|
|
742
850
|
for key, value in new_state.items():
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
851
|
+
if key.startswith('_'):
|
|
852
|
+
current_state[key] = value
|
|
853
|
+
else:
|
|
854
|
+
current_state[key] = core.merge(
|
|
855
|
+
schema.get(key),
|
|
856
|
+
current_state.get(key),
|
|
857
|
+
value)
|
|
747
858
|
return current_state
|
|
748
859
|
else:
|
|
749
860
|
return new_state
|
|
@@ -954,6 +1065,8 @@ registry_types = {
|
|
|
954
1065
|
'_check': check_any,
|
|
955
1066
|
'_serialize': serialize_any,
|
|
956
1067
|
'_deserialize': deserialize_any,
|
|
1068
|
+
'_dataclass': dataclass_any,
|
|
1069
|
+
'_resolve': resolve_any,
|
|
957
1070
|
'_fold': fold_any,
|
|
958
1071
|
'_merge': merge_any,
|
|
959
1072
|
'_bind': bind_any,
|
|
@@ -967,6 +1080,7 @@ registry_types = {
|
|
|
967
1080
|
'_slice': slice_tuple,
|
|
968
1081
|
'_serialize': serialize_tuple,
|
|
969
1082
|
'_deserialize': deserialize_tuple,
|
|
1083
|
+
'_dataclass': dataclass_tuple,
|
|
970
1084
|
'_fold': fold_tuple,
|
|
971
1085
|
'_divide': divide_tuple,
|
|
972
1086
|
'_bind': bind_tuple,
|
|
@@ -980,6 +1094,7 @@ registry_types = {
|
|
|
980
1094
|
'_slice': slice_union,
|
|
981
1095
|
'_serialize': serialize_union,
|
|
982
1096
|
'_deserialize': deserialize_union,
|
|
1097
|
+
'_dataclass': dataclass_union,
|
|
983
1098
|
'_fold': fold_union,
|
|
984
1099
|
'_description': 'union of a set of possible types'}}
|
|
985
1100
|
|
|
@@ -9,35 +9,35 @@ import pint
|
|
|
9
9
|
import pprint
|
|
10
10
|
import pytest
|
|
11
11
|
import random
|
|
12
|
+
import typing
|
|
12
13
|
import inspect
|
|
13
14
|
import numbers
|
|
14
15
|
import numpy as np
|
|
15
16
|
|
|
16
17
|
from pint import Quantity
|
|
17
18
|
from pprint import pformat as pf
|
|
19
|
+
from typing import Any, Tuple, Union, Optional, Mapping, Callable, NewType, get_origin, get_args
|
|
20
|
+
from dataclasses import asdict
|
|
18
21
|
|
|
19
22
|
from bigraph_schema.units import units, render_units_type
|
|
20
23
|
from bigraph_schema.react import react_divide_counts
|
|
21
24
|
from bigraph_schema.registry import (
|
|
22
25
|
NONE_SYMBOL,
|
|
23
26
|
Registry, TypeRegistry,
|
|
24
|
-
type_schema_keys, non_schema_keys,
|
|
27
|
+
type_schema_keys, non_schema_keys, is_schema_key,
|
|
25
28
|
apply_tree, visit_method,
|
|
26
29
|
type_merge, deep_merge,
|
|
27
30
|
get_path, establish_path, set_path, transform_path, remove_path,
|
|
28
31
|
remove_omitted
|
|
29
32
|
)
|
|
30
33
|
|
|
34
|
+
import bigraph_schema.data as data
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
TYPE_SCHEMAS = {
|
|
34
38
|
'float': 'float'}
|
|
35
39
|
|
|
36
40
|
|
|
37
|
-
def is_schema_key(schema, key):
|
|
38
|
-
return key.strip('_') not in schema.get('_type_parameters', []) and key.startswith('_')
|
|
39
|
-
|
|
40
|
-
|
|
41
41
|
def resolve_path(path):
|
|
42
42
|
resolve = []
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@ def resolve_path(path):
|
|
|
50
50
|
else:
|
|
51
51
|
resolve.append(step)
|
|
52
52
|
|
|
53
|
-
return resolve
|
|
53
|
+
return tuple(resolve)
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def apply_schema(schema, current, update, core):
|
|
@@ -428,6 +428,8 @@ class TypeSystem:
|
|
|
428
428
|
# TODO: after the reaction, fill in the state with missing values
|
|
429
429
|
# from the schema
|
|
430
430
|
|
|
431
|
+
# TODO: add schema to redex and reactum
|
|
432
|
+
|
|
431
433
|
if 'redex' in reaction or 'reactum' in reaction or 'calls' in reaction:
|
|
432
434
|
redex = reaction.get('redex', {})
|
|
433
435
|
reactum = reaction.get('reactum', {})
|
|
@@ -438,8 +440,10 @@ class TypeSystem:
|
|
|
438
440
|
make_reaction = self.react_registry.access(
|
|
439
441
|
reaction_key)
|
|
440
442
|
react = make_reaction(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
+
schema,
|
|
444
|
+
state,
|
|
445
|
+
reaction.get(reaction_key, {}),
|
|
446
|
+
self)
|
|
443
447
|
|
|
444
448
|
redex = react.get('redex', {})
|
|
445
449
|
reactum = react.get('reactum', {})
|
|
@@ -451,16 +455,24 @@ class TypeSystem:
|
|
|
451
455
|
redex,
|
|
452
456
|
mode=mode)
|
|
453
457
|
|
|
458
|
+
# for path in paths:
|
|
459
|
+
# path_schema, path_state = self.slice(
|
|
460
|
+
# schema,
|
|
461
|
+
# state,
|
|
462
|
+
# path)
|
|
463
|
+
|
|
454
464
|
def merge_state(before):
|
|
455
465
|
remaining = remove_omitted(
|
|
456
466
|
redex,
|
|
457
467
|
reactum,
|
|
458
468
|
before)
|
|
459
469
|
|
|
460
|
-
|
|
470
|
+
merged = deep_merge(
|
|
461
471
|
remaining,
|
|
462
472
|
reactum)
|
|
463
473
|
|
|
474
|
+
return merged
|
|
475
|
+
|
|
464
476
|
for path in paths:
|
|
465
477
|
state = transform_path(
|
|
466
478
|
state,
|
|
@@ -470,6 +482,36 @@ class TypeSystem:
|
|
|
470
482
|
return state
|
|
471
483
|
|
|
472
484
|
|
|
485
|
+
# TODO: maybe all fields are optional?
|
|
486
|
+
def dataclass(self, schema, path=None):
|
|
487
|
+
path = path or []
|
|
488
|
+
|
|
489
|
+
dataclass_function = self.choose_method(
|
|
490
|
+
schema,
|
|
491
|
+
{},
|
|
492
|
+
'dataclass')
|
|
493
|
+
|
|
494
|
+
return dataclass_function(
|
|
495
|
+
schema,
|
|
496
|
+
path,
|
|
497
|
+
self)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def resolve(self, schema, update):
|
|
501
|
+
if update is None:
|
|
502
|
+
return schema
|
|
503
|
+
else:
|
|
504
|
+
resolve_function = self.choose_method(
|
|
505
|
+
schema,
|
|
506
|
+
{},
|
|
507
|
+
'resolve')
|
|
508
|
+
|
|
509
|
+
return resolve_function(
|
|
510
|
+
schema,
|
|
511
|
+
update,
|
|
512
|
+
self)
|
|
513
|
+
|
|
514
|
+
|
|
473
515
|
def check_state(self, schema, state):
|
|
474
516
|
schema = self.access(schema)
|
|
475
517
|
|
|
@@ -524,11 +566,13 @@ class TypeSystem:
|
|
|
524
566
|
|
|
525
567
|
def apply_update(self, schema, state, update):
|
|
526
568
|
if isinstance(update, dict) and '_react' in update:
|
|
527
|
-
|
|
569
|
+
new_state = self.react(
|
|
528
570
|
schema,
|
|
529
571
|
state,
|
|
530
572
|
update['_react'])
|
|
531
573
|
|
|
574
|
+
state = self.deserialize(schema, new_state)
|
|
575
|
+
|
|
532
576
|
elif isinstance(update, dict) and '_fold' in update:
|
|
533
577
|
fold = update['_fold']
|
|
534
578
|
|
|
@@ -879,13 +923,12 @@ class TypeSystem:
|
|
|
879
923
|
def ports_schema(self, schema, instance, edge_path, ports_key='inputs'):
|
|
880
924
|
found = self.access(schema)
|
|
881
925
|
|
|
882
|
-
|
|
883
|
-
|
|
926
|
+
edge_schema, edge_state = self.slice(
|
|
927
|
+
schema,
|
|
928
|
+
instance,
|
|
929
|
+
edge_path)
|
|
884
930
|
|
|
885
|
-
edge_schema = get_path(found, edge_path)
|
|
886
931
|
ports_schema = edge_schema.get(f'_{ports_key}')
|
|
887
|
-
|
|
888
|
-
edge_state = get_path(instance, edge_path)
|
|
889
932
|
ports = edge_state.get(ports_key)
|
|
890
933
|
|
|
891
934
|
return ports_schema, ports
|
|
@@ -954,33 +997,38 @@ class TypeSystem:
|
|
|
954
997
|
wires = [wires]
|
|
955
998
|
|
|
956
999
|
if isinstance(wires, (list, tuple)):
|
|
957
|
-
destination = list(path) + list(wires)
|
|
1000
|
+
destination = resolve_path(list(path) + list(wires))
|
|
958
1001
|
result = set_path(
|
|
959
1002
|
result,
|
|
960
1003
|
destination,
|
|
961
1004
|
states)
|
|
962
1005
|
|
|
963
1006
|
elif isinstance(wires, dict):
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
substates)
|
|
1007
|
+
if isinstance(states, list):
|
|
1008
|
+
result = [
|
|
1009
|
+
self.project(ports, wires, path, state)
|
|
1010
|
+
for state in states]
|
|
1011
|
+
else:
|
|
1012
|
+
branches = []
|
|
1013
|
+
for key in wires.keys():
|
|
1014
|
+
subports, substates = self.slice(ports, states, key)
|
|
1015
|
+
projection = self.project(
|
|
1016
|
+
subports,
|
|
1017
|
+
wires[key],
|
|
1018
|
+
path,
|
|
1019
|
+
substates)
|
|
972
1020
|
|
|
973
|
-
|
|
974
|
-
|
|
1021
|
+
if projection is not None:
|
|
1022
|
+
branches.append(projection)
|
|
975
1023
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1024
|
+
branches = [
|
|
1025
|
+
branch
|
|
1026
|
+
for branch in branches
|
|
1027
|
+
if branch is not None] # and list(branch)[0][1] is not None]
|
|
980
1028
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1029
|
+
result = {}
|
|
1030
|
+
for branch in branches:
|
|
1031
|
+
deep_merge(result, branch)
|
|
984
1032
|
else:
|
|
985
1033
|
raise Exception(
|
|
986
1034
|
f'inverting state\n {states}\naccording to ports schema\n {ports}\nbut wires are not recognized\n {wires}')
|
|
@@ -991,8 +1039,8 @@ class TypeSystem:
|
|
|
991
1039
|
def project_edge(self, schema, instance, edge_path, states, ports_key='outputs'):
|
|
992
1040
|
"""
|
|
993
1041
|
Given states from the perspective of an edge (through its ports), produce states aligned to the tree
|
|
994
|
-
|
|
995
|
-
|
|
1042
|
+
the wires point to.
|
|
1043
|
+
(inverse of view)
|
|
996
1044
|
"""
|
|
997
1045
|
|
|
998
1046
|
if schema is None:
|
|
@@ -1111,13 +1159,13 @@ class TypeSystem:
|
|
|
1111
1159
|
update = self.access(initial_update)
|
|
1112
1160
|
|
|
1113
1161
|
if self.equivalent(current, update):
|
|
1114
|
-
|
|
1162
|
+
outcome = current
|
|
1115
1163
|
|
|
1116
1164
|
elif self.inherits_from(current, update):
|
|
1117
|
-
|
|
1165
|
+
outcome = current
|
|
1118
1166
|
|
|
1119
1167
|
elif self.inherits_from(update, current):
|
|
1120
|
-
|
|
1168
|
+
outcome = update
|
|
1121
1169
|
|
|
1122
1170
|
elif '_type' in current and '_type' in update and current['_type'] == update['_type']:
|
|
1123
1171
|
outcome = current.copy()
|
|
@@ -1141,22 +1189,33 @@ class TypeSystem:
|
|
|
1141
1189
|
outcome.get(key),
|
|
1142
1190
|
update[key])
|
|
1143
1191
|
|
|
1144
|
-
|
|
1192
|
+
elif '_type' in update and '_type' not in current:
|
|
1193
|
+
outcome = self.resolve(update, current)
|
|
1145
1194
|
|
|
1146
1195
|
else:
|
|
1147
|
-
outcome =
|
|
1196
|
+
outcome = self.resolve(current, update)
|
|
1148
1197
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1198
|
+
# elif '_type' in current:
|
|
1199
|
+
# outcome = self.resolve(current, update)
|
|
1200
|
+
|
|
1201
|
+
# elif '_type' in update:
|
|
1202
|
+
# outcome = self.resolve(update, current)
|
|
1203
|
+
|
|
1204
|
+
# else:
|
|
1205
|
+
# outcome = self.resolve(current, update)
|
|
1206
|
+
# outcome = current.copy()
|
|
1207
|
+
|
|
1208
|
+
# for key in update:
|
|
1209
|
+
# if not key in outcome or is_schema_key(update, key):
|
|
1210
|
+
# key_update = update[key]
|
|
1211
|
+
# if key_update:
|
|
1212
|
+
# outcome[key] = key_update
|
|
1213
|
+
# else:
|
|
1214
|
+
# outcome[key] = self.resolve_schemas(
|
|
1215
|
+
# outcome.get(key),
|
|
1216
|
+
# update[key])
|
|
1158
1217
|
|
|
1159
|
-
|
|
1218
|
+
return outcome
|
|
1160
1219
|
|
|
1161
1220
|
|
|
1162
1221
|
def infer_wires(self, ports, state, wires, top_schema=None, path=None):
|
|
@@ -1203,27 +1262,19 @@ class TypeSystem:
|
|
|
1203
1262
|
raise Exception(f'no wires at port "{port_key}" in ports {ports} with state {state}')
|
|
1204
1263
|
|
|
1205
1264
|
else:
|
|
1206
|
-
|
|
1207
|
-
top_schema,
|
|
1208
|
-
path[:-1])
|
|
1209
|
-
|
|
1265
|
+
compound_path = resolve_path(path[:-1] + tuple(port_wires))
|
|
1210
1266
|
destination = establish_path(
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
top=top_schema,
|
|
1214
|
-
cursor=path[:-1])
|
|
1267
|
+
top_schema,
|
|
1268
|
+
compound_path[:-1])
|
|
1215
1269
|
|
|
1216
1270
|
# TODO: validate the schema/state
|
|
1217
|
-
destination_key =
|
|
1271
|
+
destination_key = compound_path[-1]
|
|
1218
1272
|
if destination_key in destination:
|
|
1219
1273
|
current = destination[destination_key]
|
|
1220
1274
|
port_schema = self.resolve_schemas(
|
|
1221
1275
|
current,
|
|
1222
1276
|
port_schema)
|
|
1223
1277
|
|
|
1224
|
-
if isinstance(destination, tuple):
|
|
1225
|
-
import ipdb; ipdb.set_trace()
|
|
1226
|
-
|
|
1227
1278
|
destination[destination_key] = self.access(
|
|
1228
1279
|
port_schema)
|
|
1229
1280
|
|
|
@@ -1341,21 +1392,16 @@ class TypeSystem:
|
|
|
1341
1392
|
pass
|
|
1342
1393
|
|
|
1343
1394
|
else:
|
|
1344
|
-
type_schema = TYPE_SCHEMAS.get(
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
if path_key in destination:
|
|
1355
|
-
# TODO: validate
|
|
1356
|
-
pass
|
|
1357
|
-
else:
|
|
1358
|
-
destination[path_key] = type_schema
|
|
1395
|
+
type_schema = TYPE_SCHEMAS.get(
|
|
1396
|
+
type(state).__name__,
|
|
1397
|
+
'any')
|
|
1398
|
+
|
|
1399
|
+
schema, top_state = self.set_slice(
|
|
1400
|
+
schema,
|
|
1401
|
+
top_state,
|
|
1402
|
+
path,
|
|
1403
|
+
type_schema,
|
|
1404
|
+
state)
|
|
1359
1405
|
|
|
1360
1406
|
return schema, top_state
|
|
1361
1407
|
|
|
@@ -1529,6 +1575,14 @@ def concatenate(schema, current, update, core=None):
|
|
|
1529
1575
|
return current + update
|
|
1530
1576
|
|
|
1531
1577
|
|
|
1578
|
+
def dataclass_float(schema, path, core):
|
|
1579
|
+
return float
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
def dataclass_integer(schema, path, core):
|
|
1583
|
+
return int
|
|
1584
|
+
|
|
1585
|
+
|
|
1532
1586
|
def divide_float(schema, state, values, core):
|
|
1533
1587
|
divisions = values.get('divisions', 2)
|
|
1534
1588
|
portion = float(state) / divisions
|
|
@@ -1659,6 +1713,18 @@ def check_list(schema, state, core):
|
|
|
1659
1713
|
return False
|
|
1660
1714
|
|
|
1661
1715
|
|
|
1716
|
+
def dataclass_list(schema, path, core):
|
|
1717
|
+
element_type = core.find_parameter(
|
|
1718
|
+
schema,
|
|
1719
|
+
'element')
|
|
1720
|
+
|
|
1721
|
+
dataclass = core.dataclass(
|
|
1722
|
+
element_type,
|
|
1723
|
+
path + ['element'])
|
|
1724
|
+
|
|
1725
|
+
return list[dataclass]
|
|
1726
|
+
|
|
1727
|
+
|
|
1662
1728
|
def slice_list(schema, state, path, core):
|
|
1663
1729
|
element_type = core.find_parameter(
|
|
1664
1730
|
schema,
|
|
@@ -1724,6 +1790,25 @@ def check_tree(schema, state, core):
|
|
|
1724
1790
|
return core.check(leaf_type, state)
|
|
1725
1791
|
|
|
1726
1792
|
|
|
1793
|
+
def dataclass_tree(schema, path, core):
|
|
1794
|
+
leaf_type = core.find_parameter(
|
|
1795
|
+
schema,
|
|
1796
|
+
'leaf')
|
|
1797
|
+
|
|
1798
|
+
leaf_dataclass = core.dataclass(
|
|
1799
|
+
leaf_type,
|
|
1800
|
+
path + ['leaf'])
|
|
1801
|
+
|
|
1802
|
+
dataclass_name = '_'.join(path)
|
|
1803
|
+
# block = f"{dataclass_name} = NewType('{dataclass_name}', Union[{leaf_dataclass}, Mapping[str, '{dataclass_name}']])"
|
|
1804
|
+
block = f"NewType('{dataclass_name}', Union[{leaf_dataclass}, Mapping[str, '{dataclass_name}']])"
|
|
1805
|
+
|
|
1806
|
+
dataclass = eval(block)
|
|
1807
|
+
setattr(data, dataclass_name, dataclass)
|
|
1808
|
+
|
|
1809
|
+
return dataclass
|
|
1810
|
+
|
|
1811
|
+
|
|
1727
1812
|
def slice_tree(schema, state, path, core):
|
|
1728
1813
|
leaf_type = core.find_parameter(
|
|
1729
1814
|
schema,
|
|
@@ -1817,6 +1902,30 @@ def apply_map(schema, current, update, core=None):
|
|
|
1817
1902
|
return result
|
|
1818
1903
|
|
|
1819
1904
|
|
|
1905
|
+
def resolve_map(schema, update, core):
|
|
1906
|
+
if isinstance(update, dict):
|
|
1907
|
+
value_schema = schema.get('_value', {})
|
|
1908
|
+
for key, subschema in update.items():
|
|
1909
|
+
value_schema = core.resolve_schemas(
|
|
1910
|
+
value_schema,
|
|
1911
|
+
subschema)
|
|
1912
|
+
schema['_value'] = value_schema
|
|
1913
|
+
|
|
1914
|
+
return schema
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
def dataclass_map(schema, path, core):
|
|
1918
|
+
value_type = core.find_parameter(
|
|
1919
|
+
schema,
|
|
1920
|
+
'value')
|
|
1921
|
+
|
|
1922
|
+
dataclass = core.dataclass(
|
|
1923
|
+
value_type,
|
|
1924
|
+
path + ['value'])
|
|
1925
|
+
|
|
1926
|
+
return Mapping[str, dataclass]
|
|
1927
|
+
|
|
1928
|
+
|
|
1820
1929
|
def check_map(schema, state, core=None):
|
|
1821
1930
|
value_type = core.find_parameter(
|
|
1822
1931
|
schema,
|
|
@@ -1893,6 +2002,18 @@ def apply_maybe(schema, current, update, core):
|
|
|
1893
2002
|
update)
|
|
1894
2003
|
|
|
1895
2004
|
|
|
2005
|
+
def dataclass_maybe(schema, path, core):
|
|
2006
|
+
value_type = core.find_parameter(
|
|
2007
|
+
schema,
|
|
2008
|
+
'value')
|
|
2009
|
+
|
|
2010
|
+
dataclass = core.dataclass(
|
|
2011
|
+
value_type,
|
|
2012
|
+
path + ['value'])
|
|
2013
|
+
|
|
2014
|
+
return Optional[dataclass]
|
|
2015
|
+
|
|
2016
|
+
|
|
1896
2017
|
def check_maybe(schema, state, core):
|
|
1897
2018
|
if state is None:
|
|
1898
2019
|
return True
|
|
@@ -1984,6 +2105,20 @@ def apply_edge(schema, current, update, core):
|
|
|
1984
2105
|
return result
|
|
1985
2106
|
|
|
1986
2107
|
|
|
2108
|
+
def dataclass_edge(schema, path, core):
|
|
2109
|
+
inputs = schema.get('_inputs', {})
|
|
2110
|
+
inputs_dataclass = core.dataclass(
|
|
2111
|
+
inputs,
|
|
2112
|
+
path + ['inputs'])
|
|
2113
|
+
|
|
2114
|
+
outputs = schema.get('_outputs', {})
|
|
2115
|
+
outputs_dataclass = core.dataclass(
|
|
2116
|
+
outputs,
|
|
2117
|
+
path + ['outputs'])
|
|
2118
|
+
|
|
2119
|
+
return Callable[[inputs_dataclass], outputs_dataclass]
|
|
2120
|
+
|
|
2121
|
+
|
|
1987
2122
|
def check_ports(state, core, key):
|
|
1988
2123
|
return key in state and core.check(
|
|
1989
2124
|
'wires',
|
|
@@ -2021,6 +2156,10 @@ def check_array(schema, state, core):
|
|
|
2021
2156
|
|
|
2022
2157
|
|
|
2023
2158
|
|
|
2159
|
+
def dataclass_array(schema, path, core):
|
|
2160
|
+
return np.ndarray
|
|
2161
|
+
|
|
2162
|
+
|
|
2024
2163
|
def slice_array(schema, state, path, core):
|
|
2025
2164
|
if len(path) > 0:
|
|
2026
2165
|
head = path[0]
|
|
@@ -2058,16 +2197,16 @@ def serialize_array(schema, value, core):
|
|
|
2058
2197
|
if isinstance(value, dict):
|
|
2059
2198
|
return value
|
|
2060
2199
|
else:
|
|
2061
|
-
|
|
2200
|
+
array_data = 'string'
|
|
2062
2201
|
dtype = value.dtype.name
|
|
2063
2202
|
if dtype.startswith('int'):
|
|
2064
|
-
|
|
2203
|
+
array_data = 'integer'
|
|
2065
2204
|
elif dtype.startswith('float'):
|
|
2066
|
-
|
|
2205
|
+
array_data = 'float'
|
|
2067
2206
|
|
|
2068
2207
|
return {
|
|
2069
2208
|
'bytes': value.tobytes(),
|
|
2070
|
-
'data':
|
|
2209
|
+
'data': array_data,
|
|
2071
2210
|
'shape': value.shape}
|
|
2072
2211
|
|
|
2073
2212
|
|
|
@@ -2339,6 +2478,14 @@ def register_units(core, units):
|
|
|
2339
2478
|
|
|
2340
2479
|
|
|
2341
2480
|
|
|
2481
|
+
def dataclass_boolean(schema, path, core):
|
|
2482
|
+
return bool
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
def dataclass_string(schema, path, core):
|
|
2486
|
+
return str
|
|
2487
|
+
|
|
2488
|
+
|
|
2342
2489
|
base_type_library = {
|
|
2343
2490
|
'boolean': {
|
|
2344
2491
|
'_type': 'boolean',
|
|
@@ -2347,7 +2494,7 @@ base_type_library = {
|
|
|
2347
2494
|
'_apply': apply_boolean,
|
|
2348
2495
|
'_serialize': serialize_boolean,
|
|
2349
2496
|
'_deserialize': deserialize_boolean,
|
|
2350
|
-
|
|
2497
|
+
'_dataclass': dataclass_boolean},
|
|
2351
2498
|
|
|
2352
2499
|
# abstract number type
|
|
2353
2500
|
'number': {
|
|
@@ -2363,6 +2510,7 @@ base_type_library = {
|
|
|
2363
2510
|
# inherit _apply and _serialize from number type
|
|
2364
2511
|
'_check': check_integer,
|
|
2365
2512
|
'_deserialize': deserialize_integer,
|
|
2513
|
+
'_dataclass': dataclass_integer,
|
|
2366
2514
|
'_description': '64-bit integer',
|
|
2367
2515
|
'_inherit': 'number'},
|
|
2368
2516
|
|
|
@@ -2372,6 +2520,7 @@ base_type_library = {
|
|
|
2372
2520
|
'_check': check_float,
|
|
2373
2521
|
'_deserialize': deserialize_float,
|
|
2374
2522
|
'_divide': divide_float,
|
|
2523
|
+
'_dataclass': dataclass_float,
|
|
2375
2524
|
'_description': '64-bit floating point precision number',
|
|
2376
2525
|
'_inherit': 'number'},
|
|
2377
2526
|
|
|
@@ -2382,6 +2531,7 @@ base_type_library = {
|
|
|
2382
2531
|
'_apply': replace,
|
|
2383
2532
|
'_serialize': serialize_string,
|
|
2384
2533
|
'_deserialize': deserialize_string,
|
|
2534
|
+
'_dataclass': dataclass_string,
|
|
2385
2535
|
'_description': '64-bit integer'},
|
|
2386
2536
|
|
|
2387
2537
|
'list': {
|
|
@@ -2392,12 +2542,12 @@ base_type_library = {
|
|
|
2392
2542
|
'_apply': apply_list,
|
|
2393
2543
|
'_serialize': serialize_list,
|
|
2394
2544
|
'_deserialize': deserialize_list,
|
|
2545
|
+
'_dataclass': dataclass_list,
|
|
2395
2546
|
'_fold': fold_list,
|
|
2396
2547
|
'_divide': divide_list,
|
|
2397
2548
|
'_type_parameters': ['element'],
|
|
2398
2549
|
'_description': 'general list type (or sublists)'},
|
|
2399
2550
|
|
|
2400
|
-
# TODO: tree should behave as if the leaf type is a valid tree
|
|
2401
2551
|
'tree': {
|
|
2402
2552
|
'_type': 'tree',
|
|
2403
2553
|
'_default': {},
|
|
@@ -2406,6 +2556,7 @@ base_type_library = {
|
|
|
2406
2556
|
'_apply': apply_tree,
|
|
2407
2557
|
'_serialize': serialize_tree,
|
|
2408
2558
|
'_deserialize': deserialize_tree,
|
|
2559
|
+
'_dataclass': dataclass_tree,
|
|
2409
2560
|
'_fold': fold_tree,
|
|
2410
2561
|
'_divide': divide_tree,
|
|
2411
2562
|
'_type_parameters': ['leaf'],
|
|
@@ -2417,6 +2568,8 @@ base_type_library = {
|
|
|
2417
2568
|
'_apply': apply_map,
|
|
2418
2569
|
'_serialize': serialize_map,
|
|
2419
2570
|
'_deserialize': deserialize_map,
|
|
2571
|
+
'_resolve': resolve_map,
|
|
2572
|
+
'_dataclass': dataclass_map,
|
|
2420
2573
|
'_check': check_map,
|
|
2421
2574
|
'_slice': slice_map,
|
|
2422
2575
|
'_fold': fold_map,
|
|
@@ -2433,6 +2586,7 @@ base_type_library = {
|
|
|
2433
2586
|
'_apply': apply_array,
|
|
2434
2587
|
'_serialize': serialize_array,
|
|
2435
2588
|
'_deserialize': deserialize_array,
|
|
2589
|
+
'_dataclass': dataclass_array,
|
|
2436
2590
|
'_type_parameters': [
|
|
2437
2591
|
'shape',
|
|
2438
2592
|
'data'],
|
|
@@ -2446,6 +2600,7 @@ base_type_library = {
|
|
|
2446
2600
|
'_slice': slice_maybe,
|
|
2447
2601
|
'_serialize': serialize_maybe,
|
|
2448
2602
|
'_deserialize': deserialize_maybe,
|
|
2603
|
+
'_dataclass': dataclass_maybe,
|
|
2449
2604
|
'_fold': fold_maybe,
|
|
2450
2605
|
'_type_parameters': ['value'],
|
|
2451
2606
|
'_description': 'type to represent values that could be empty'},
|
|
@@ -2465,7 +2620,6 @@ base_type_library = {
|
|
|
2465
2620
|
'_apply': apply_schema},
|
|
2466
2621
|
|
|
2467
2622
|
'edge': {
|
|
2468
|
-
# TODO: do we need to have defaults informed by type parameters?
|
|
2469
2623
|
'_type': 'edge',
|
|
2470
2624
|
'_default': {
|
|
2471
2625
|
'inputs': {},
|
|
@@ -2473,6 +2627,7 @@ base_type_library = {
|
|
|
2473
2627
|
'_apply': apply_edge,
|
|
2474
2628
|
'_serialize': serialize_edge,
|
|
2475
2629
|
'_deserialize': deserialize_edge,
|
|
2630
|
+
'_dataclass': dataclass_edge,
|
|
2476
2631
|
'_check': check_edge,
|
|
2477
2632
|
'_type_parameters': ['inputs', 'outputs'],
|
|
2478
2633
|
'_description': 'hyperedges in the bigraph, with inputs and outputs as type parameters',
|
|
@@ -2480,10 +2635,109 @@ base_type_library = {
|
|
|
2480
2635
|
'outputs': 'wires'}}
|
|
2481
2636
|
|
|
2482
2637
|
|
|
2638
|
+
def add_reaction(schema, state, reaction, core):
|
|
2639
|
+
path = reaction.get('path')
|
|
2640
|
+
|
|
2641
|
+
redex = {}
|
|
2642
|
+
establish_path(
|
|
2643
|
+
redex,
|
|
2644
|
+
path)
|
|
2645
|
+
|
|
2646
|
+
reactum = {}
|
|
2647
|
+
node = establish_path(
|
|
2648
|
+
reactum,
|
|
2649
|
+
path)
|
|
2650
|
+
|
|
2651
|
+
deep_merge(
|
|
2652
|
+
node,
|
|
2653
|
+
reaction.get('add', {}))
|
|
2654
|
+
|
|
2655
|
+
return {
|
|
2656
|
+
'redex': redex,
|
|
2657
|
+
'reactum': reactum}
|
|
2658
|
+
|
|
2659
|
+
|
|
2660
|
+
def remove_reaction(schema, state, reaction, core):
|
|
2661
|
+
path = reaction.get('path', ())
|
|
2662
|
+
redex = {}
|
|
2663
|
+
node = establish_path(
|
|
2664
|
+
redex,
|
|
2665
|
+
path)
|
|
2666
|
+
|
|
2667
|
+
for remove in reaction.get('remove', []):
|
|
2668
|
+
node[remove] = {}
|
|
2669
|
+
|
|
2670
|
+
reactum = {}
|
|
2671
|
+
establish_path(
|
|
2672
|
+
reactum,
|
|
2673
|
+
path)
|
|
2674
|
+
|
|
2675
|
+
return {
|
|
2676
|
+
'redex': redex,
|
|
2677
|
+
'reactum': reactum}
|
|
2678
|
+
|
|
2679
|
+
|
|
2680
|
+
def replace_reaction(schema, state, reaction, core):
|
|
2681
|
+
path = reaction.get('path', ())
|
|
2682
|
+
|
|
2683
|
+
redex = {}
|
|
2684
|
+
node = establish_path(
|
|
2685
|
+
redex,
|
|
2686
|
+
path)
|
|
2687
|
+
|
|
2688
|
+
for before_key, before_state in reaction.get('before', {}).items():
|
|
2689
|
+
node[before_key] = before_state
|
|
2690
|
+
|
|
2691
|
+
reactum = {}
|
|
2692
|
+
node = establish_path(
|
|
2693
|
+
reactum,
|
|
2694
|
+
path)
|
|
2695
|
+
|
|
2696
|
+
for after_key, after_state in reaction.get('after', {}).items():
|
|
2697
|
+
node[after_key] = after_state
|
|
2698
|
+
|
|
2699
|
+
return {
|
|
2700
|
+
'redex': redex,
|
|
2701
|
+
'reactum': reactum}
|
|
2702
|
+
|
|
2703
|
+
|
|
2704
|
+
def divide_reaction(schema, state, reaction, core):
|
|
2705
|
+
mother = reaction['mother']
|
|
2706
|
+
daughters = reaction['daughters']
|
|
2707
|
+
|
|
2708
|
+
mother_schema, mother_state = core.slice(
|
|
2709
|
+
schema,
|
|
2710
|
+
state,
|
|
2711
|
+
mother)
|
|
2712
|
+
|
|
2713
|
+
division = core.fold(
|
|
2714
|
+
mother_schema,
|
|
2715
|
+
mother_state,
|
|
2716
|
+
'divide', {
|
|
2717
|
+
'divisions': len(daughters),
|
|
2718
|
+
'daughter_configs': [daughter[1] for daughter in daughters]})
|
|
2719
|
+
|
|
2720
|
+
after = {
|
|
2721
|
+
daughter[0]: daughter_state
|
|
2722
|
+
for daughter, daughter_state in zip(daughters, division)}
|
|
2723
|
+
|
|
2724
|
+
replace = {
|
|
2725
|
+
'before': {
|
|
2726
|
+
mother: {}},
|
|
2727
|
+
'after': after}
|
|
2728
|
+
|
|
2729
|
+
return replace_reaction(
|
|
2730
|
+
schema,
|
|
2731
|
+
state,
|
|
2732
|
+
replace,
|
|
2733
|
+
core)
|
|
2734
|
+
|
|
2735
|
+
|
|
2483
2736
|
def register_base_reactions(core):
|
|
2484
|
-
core.register_reaction(
|
|
2485
|
-
|
|
2486
|
-
|
|
2737
|
+
core.register_reaction('add', add_reaction)
|
|
2738
|
+
core.register_reaction('remove', remove_reaction)
|
|
2739
|
+
core.register_reaction('replace', replace_reaction)
|
|
2740
|
+
core.register_reaction('divide', divide_reaction)
|
|
2487
2741
|
|
|
2488
2742
|
|
|
2489
2743
|
def register_cube(core):
|
|
@@ -3442,31 +3696,6 @@ def test_add_reaction(compartment_types):
|
|
|
3442
3696
|
'counts': {'A': 13},
|
|
3443
3697
|
'inner': {}}}}}
|
|
3444
3698
|
|
|
3445
|
-
def add_reaction(config):
|
|
3446
|
-
path = config.get('path')
|
|
3447
|
-
|
|
3448
|
-
redex = {}
|
|
3449
|
-
establish_path(
|
|
3450
|
-
redex,
|
|
3451
|
-
path)
|
|
3452
|
-
|
|
3453
|
-
reactum = {}
|
|
3454
|
-
node = establish_path(
|
|
3455
|
-
reactum,
|
|
3456
|
-
path)
|
|
3457
|
-
|
|
3458
|
-
deep_merge(
|
|
3459
|
-
node,
|
|
3460
|
-
config.get('add', {}))
|
|
3461
|
-
|
|
3462
|
-
return {
|
|
3463
|
-
'redex': redex,
|
|
3464
|
-
'reactum': reactum}
|
|
3465
|
-
|
|
3466
|
-
compartment_types.react_registry.register(
|
|
3467
|
-
'add',
|
|
3468
|
-
add_reaction)
|
|
3469
|
-
|
|
3470
3699
|
add_config = {
|
|
3471
3700
|
'path': ['environment', 'inner'],
|
|
3472
3701
|
'add': {
|
|
@@ -3508,30 +3737,6 @@ def test_remove_reaction(compartment_types):
|
|
|
3508
3737
|
'counts': {'A': 13},
|
|
3509
3738
|
'inner': {}}}}}
|
|
3510
3739
|
|
|
3511
|
-
# TODO: register these for general access
|
|
3512
|
-
def remove_reaction(config):
|
|
3513
|
-
path = config.get('path', ())
|
|
3514
|
-
redex = {}
|
|
3515
|
-
node = establish_path(
|
|
3516
|
-
redex,
|
|
3517
|
-
path)
|
|
3518
|
-
|
|
3519
|
-
for remove in config.get('remove', []):
|
|
3520
|
-
node[remove] = {}
|
|
3521
|
-
|
|
3522
|
-
reactum = {}
|
|
3523
|
-
establish_path(
|
|
3524
|
-
reactum,
|
|
3525
|
-
path)
|
|
3526
|
-
|
|
3527
|
-
return {
|
|
3528
|
-
'redex': redex,
|
|
3529
|
-
'reactum': reactum}
|
|
3530
|
-
|
|
3531
|
-
compartment_types.react_registry.register(
|
|
3532
|
-
'remove',
|
|
3533
|
-
remove_reaction)
|
|
3534
|
-
|
|
3535
3740
|
remove_config = {
|
|
3536
3741
|
'path': ['environment', 'inner'],
|
|
3537
3742
|
'remove': ['0']}
|
|
@@ -3566,43 +3771,16 @@ def test_replace_reaction(compartment_types):
|
|
|
3566
3771
|
'counts': {'A': 13},
|
|
3567
3772
|
'inner': {}}}}}
|
|
3568
3773
|
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
reactum = {}
|
|
3581
|
-
node = establish_path(
|
|
3582
|
-
reactum,
|
|
3583
|
-
path)
|
|
3584
|
-
|
|
3585
|
-
for after_key, after_state in config.get('after', {}).items():
|
|
3586
|
-
node[after_key] = after_state
|
|
3587
|
-
|
|
3588
|
-
return {
|
|
3589
|
-
'redex': redex,
|
|
3590
|
-
'reactum': reactum}
|
|
3591
|
-
|
|
3592
|
-
compartment_types.react_registry.register(
|
|
3593
|
-
'replace',
|
|
3594
|
-
replace_reaction)
|
|
3595
|
-
|
|
3596
|
-
replace_config = {
|
|
3597
|
-
'path': ['environment', 'inner'],
|
|
3598
|
-
'before': {'0': {'A': '?1'}},
|
|
3599
|
-
'after': {
|
|
3600
|
-
'2': {
|
|
3601
|
-
'counts': {
|
|
3602
|
-
'A': {'function': 'divide', 'arguments': ['?1', 0.5], }}},
|
|
3603
|
-
'3': {
|
|
3604
|
-
'counts': {
|
|
3605
|
-
'A': '@1'}}}}
|
|
3774
|
+
# replace_config = {
|
|
3775
|
+
# 'path': ['environment', 'inner'],
|
|
3776
|
+
# 'before': {'0': {'A': '?1'}},
|
|
3777
|
+
# 'after': {
|
|
3778
|
+
# '2': {
|
|
3779
|
+
# 'counts': {
|
|
3780
|
+
# 'A': {'function': 'divide', 'arguments': ['?1', 0.5], }}},
|
|
3781
|
+
# '3': {
|
|
3782
|
+
# 'counts': {
|
|
3783
|
+
# 'A': '@1'}}}}
|
|
3606
3784
|
|
|
3607
3785
|
replace_config = {
|
|
3608
3786
|
'path': ['environment', 'inner'],
|
|
@@ -4380,6 +4558,130 @@ def test_set_slice(core):
|
|
|
4380
4558
|
1])[1] == 33
|
|
4381
4559
|
|
|
4382
4560
|
|
|
4561
|
+
def from_state(dataclass, state):
|
|
4562
|
+
if hasattr(dataclass, '__dataclass_fields__'):
|
|
4563
|
+
fields = dataclass.__dataclass_fields__
|
|
4564
|
+
state = state or {}
|
|
4565
|
+
|
|
4566
|
+
init = {}
|
|
4567
|
+
for key, field in fields.items():
|
|
4568
|
+
substate = from_state(
|
|
4569
|
+
field.type,
|
|
4570
|
+
state.get(key))
|
|
4571
|
+
init[key] = substate
|
|
4572
|
+
instance = dataclass(**init)
|
|
4573
|
+
# elif get_origin(dataclass) in [typing.Union, typing.Mapping]:
|
|
4574
|
+
# instance = state
|
|
4575
|
+
else:
|
|
4576
|
+
instance = state
|
|
4577
|
+
# instance = dataclass(state)
|
|
4578
|
+
|
|
4579
|
+
return instance
|
|
4580
|
+
|
|
4581
|
+
|
|
4582
|
+
def test_dataclass(core):
|
|
4583
|
+
simple_schema = {
|
|
4584
|
+
'a': 'float',
|
|
4585
|
+
'b': 'integer',
|
|
4586
|
+
'c': 'boolean',
|
|
4587
|
+
'x': 'string'}
|
|
4588
|
+
|
|
4589
|
+
# TODO: accept just a string instead of only a path
|
|
4590
|
+
simple_dataclass = core.dataclass(
|
|
4591
|
+
simple_schema,
|
|
4592
|
+
['simple'])
|
|
4593
|
+
|
|
4594
|
+
simple_state = {
|
|
4595
|
+
'a': 88.888,
|
|
4596
|
+
'b': 11111,
|
|
4597
|
+
'c': False,
|
|
4598
|
+
'x': 'not a string'}
|
|
4599
|
+
|
|
4600
|
+
simple_new = simple_dataclass(
|
|
4601
|
+
a=1.11,
|
|
4602
|
+
b=33,
|
|
4603
|
+
c=True,
|
|
4604
|
+
x='what')
|
|
4605
|
+
|
|
4606
|
+
simple_from = from_state(
|
|
4607
|
+
simple_dataclass,
|
|
4608
|
+
simple_state)
|
|
4609
|
+
|
|
4610
|
+
nested_schema = {
|
|
4611
|
+
'a': {
|
|
4612
|
+
'a': {
|
|
4613
|
+
'a': 'float',
|
|
4614
|
+
'b': 'float'},
|
|
4615
|
+
'x': 'float'}}
|
|
4616
|
+
|
|
4617
|
+
nested_dataclass = core.dataclass(
|
|
4618
|
+
nested_schema,
|
|
4619
|
+
['nested'])
|
|
4620
|
+
|
|
4621
|
+
nested_state = {
|
|
4622
|
+
'a': {
|
|
4623
|
+
'a': {
|
|
4624
|
+
'a': 13.4444,
|
|
4625
|
+
'b': 888.88},
|
|
4626
|
+
'x': 111.11111}}
|
|
4627
|
+
|
|
4628
|
+
nested_new = data.nested(
|
|
4629
|
+
data.nested_a(
|
|
4630
|
+
data.nested_a_a(
|
|
4631
|
+
a=222.22,
|
|
4632
|
+
b=3.3333),
|
|
4633
|
+
5555.55))
|
|
4634
|
+
|
|
4635
|
+
nested_from = from_state(
|
|
4636
|
+
nested_dataclass,
|
|
4637
|
+
nested_state)
|
|
4638
|
+
|
|
4639
|
+
complex_schema = {
|
|
4640
|
+
'a': 'tree[maybe[float]]',
|
|
4641
|
+
'b': 'float~list[string]',
|
|
4642
|
+
'c': {
|
|
4643
|
+
'd': 'map[edge[GGG:float,OOO:float]]',
|
|
4644
|
+
'e': 'array[(3|4|10),float]'}}
|
|
4645
|
+
|
|
4646
|
+
complex_dataclass = core.dataclass(
|
|
4647
|
+
complex_schema,
|
|
4648
|
+
['complex'])
|
|
4649
|
+
|
|
4650
|
+
complex_state = {
|
|
4651
|
+
'a': {
|
|
4652
|
+
'x': {
|
|
4653
|
+
'oooo': None,
|
|
4654
|
+
'y': 1.1,
|
|
4655
|
+
'z': 33.33},
|
|
4656
|
+
'w': 44.444},
|
|
4657
|
+
'b': ['1', '11', '111', '1111'],
|
|
4658
|
+
'c': {
|
|
4659
|
+
'd': {
|
|
4660
|
+
'A': {
|
|
4661
|
+
'inputs': {
|
|
4662
|
+
'GGG': ['..', '..', 'a', 'w']},
|
|
4663
|
+
'outputs': {
|
|
4664
|
+
'OOO': ['..', '..', 'a', 'x', 'y']}},
|
|
4665
|
+
'B': {
|
|
4666
|
+
'inputs': {
|
|
4667
|
+
'GGG': ['..', '..', 'a', 'x', 'y']},
|
|
4668
|
+
'outputs': {
|
|
4669
|
+
'OOO': ['..', '..', 'a', 'w']}}},
|
|
4670
|
+
'e': np.zeros((3, 4, 10))}}
|
|
4671
|
+
|
|
4672
|
+
complex_from = from_state(
|
|
4673
|
+
complex_dataclass,
|
|
4674
|
+
complex_state)
|
|
4675
|
+
|
|
4676
|
+
complex_dict = asdict(complex_from)
|
|
4677
|
+
|
|
4678
|
+
# assert complex_dict == complex_state ?
|
|
4679
|
+
|
|
4680
|
+
assert complex_from.a['x']['oooo'] is None
|
|
4681
|
+
assert len(complex_from.c.d['A']['inputs']['GGG'])
|
|
4682
|
+
assert isinstance(complex_from.c.e, np.ndarray)
|
|
4683
|
+
|
|
4684
|
+
|
|
4383
4685
|
if __name__ == '__main__':
|
|
4384
4686
|
core = TypeSystem()
|
|
4385
4687
|
|
|
@@ -4422,4 +4724,4 @@ if __name__ == '__main__':
|
|
|
4422
4724
|
test_bind(core)
|
|
4423
4725
|
test_slice(core)
|
|
4424
4726
|
test_set_slice(core)
|
|
4425
|
-
|
|
4727
|
+
test_dataclass(core)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bigraph-schema
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.37
|
|
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
|
|
7
7
|
Author-email: agmon.eran@gmail.com, ryan.spangler@gmail.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
8
10
|
Classifier: Development Status :: 3 - Alpha
|
|
9
11
|
Classifier: Intended Audience :: Developers
|
|
10
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -52,3 +54,5 @@ This resource will guide you through the core concepts and methods, helping you
|
|
|
52
54
|
## License
|
|
53
55
|
|
|
54
56
|
Bigraph-schema is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/bigraph-schema/blob/main/LICENSE).
|
|
57
|
+
|
|
58
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bigraph-schema-0.0.32 → bigraph-schema-0.0.37}/bigraph_schema.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|