SimpleSEDML 0.0.1__py3-none-any.whl
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.
- __init__.py +1 -0
- constants.py +4 -0
- model.py +188 -0
- plot.py +41 -0
- report.py +26 -0
- simple_sedml.py +390 -0
- simplesedml-0.0.1.dist-info/METADATA +92 -0
- simplesedml-0.0.1.dist-info/RECORD +13 -0
- simplesedml-0.0.1.dist-info/WHEEL +5 -0
- simplesedml-0.0.1.dist-info/licenses/LICENSE +21 -0
- simplesedml-0.0.1.dist-info/top_level.txt +8 -0
- simulation.py +88 -0
- task.py +57 -0
__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.1"
|
constants.py
ADDED
model.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'''Class that handles model definitions and their parameters.'''
|
|
2
|
+
|
|
3
|
+
import codecs
|
|
4
|
+
import urllib3
|
|
5
|
+
import os
|
|
6
|
+
import tellurium as te # type: ignore
|
|
7
|
+
from typing import Optional, List
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
SBML_STR = "sbml_str"
|
|
11
|
+
ANT_STR = "ant_str"
|
|
12
|
+
SBML_FILE = "sbml_file"
|
|
13
|
+
ANT_FILE = "ant_file"
|
|
14
|
+
SBML_URL = "sbml_url"
|
|
15
|
+
MODEL_ID = "model_id"
|
|
16
|
+
MODEL_REF_TYPES = [SBML_STR, ANT_STR, SBML_FILE, ANT_FILE, SBML_URL, MODEL_ID]
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
Issues
|
|
20
|
+
1. To eliminate the warning for urllib3, need to create a virtual environment with a higher version of python.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
class Model:
|
|
24
|
+
def __init__(self, id:str, model_ref:Optional[str]=None, ref_type:Optional[str]=None,
|
|
25
|
+
model_source:Optional[str]=None,
|
|
26
|
+
is_overwrite:bool=False,
|
|
27
|
+
**kwargs):
|
|
28
|
+
"""Provide information about the model and a model identifier.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
id (str): identifier for the model
|
|
32
|
+
model_ref (str): reference to the file; reference type is specified separately
|
|
33
|
+
ref_type (str):
|
|
34
|
+
- "sbml_str": string representation of the SBML model
|
|
35
|
+
- "ant_str": string representation of the SBML model
|
|
36
|
+
- "sbml_file": file path to the SBML model
|
|
37
|
+
- "ant_file": file path to the Antimony model
|
|
38
|
+
- "sbml_url": URL to the SBML model
|
|
39
|
+
- "model_id": ID of a previously defined model
|
|
40
|
+
is_overwrite (bool): if True, overwrite the model if it already exists
|
|
41
|
+
model_source (str): source for the SBML model. If None, the source is a file with the same name as the model ID
|
|
42
|
+
in the current directory.
|
|
43
|
+
"""
|
|
44
|
+
# Handle defaults
|
|
45
|
+
if model_ref is None:
|
|
46
|
+
# id is a file path to an SBML model
|
|
47
|
+
model_ref = id
|
|
48
|
+
_, filename = os.path.split(id)
|
|
49
|
+
splits = filename.split(".")
|
|
50
|
+
id = splits[0]
|
|
51
|
+
ref_type = SBML_FILE
|
|
52
|
+
elif ref_type is None:
|
|
53
|
+
ref_type = SBML_STR
|
|
54
|
+
# id, model_ref, ref_type should all be assigned
|
|
55
|
+
self.id = id
|
|
56
|
+
self.model_ref = model_ref
|
|
57
|
+
self.ref_type = ref_type
|
|
58
|
+
self.param_change_dct = kwargs
|
|
59
|
+
self.is_overwrite = is_overwrite
|
|
60
|
+
#
|
|
61
|
+
self.sbml_str = self._getSBMLFromReference()
|
|
62
|
+
self.model_source_path = self._makeModelSource(model_source)
|
|
63
|
+
|
|
64
|
+
def _makeModelSource(self, source:Optional[str])->str:
|
|
65
|
+
"""Saves the model to a file. The file name is the model ID.
|
|
66
|
+
"""
|
|
67
|
+
if self.ref_type == MODEL_ID:
|
|
68
|
+
# model_ref is the ID of a previously defined model
|
|
69
|
+
return self.model_ref
|
|
70
|
+
# FIXME: Model source should be the filename in the path
|
|
71
|
+
if source is None:
|
|
72
|
+
# Use the current directory
|
|
73
|
+
source = os.getcwd()
|
|
74
|
+
source = os.path.join(source, self.id)
|
|
75
|
+
source = str(source)
|
|
76
|
+
if self.is_overwrite or not os.path.exists(source):
|
|
77
|
+
with open(source, "wb") as f:
|
|
78
|
+
f.write(self.sbml_str.encode('utf-8'))
|
|
79
|
+
f.flush()
|
|
80
|
+
print(f"**Model saved to {source}")
|
|
81
|
+
if (not self.is_overwrite and os.path.exists(source)):
|
|
82
|
+
msg = "*** File {model_source_path} already exists and will be used as model source."
|
|
83
|
+
msg += "\n Use is_overwrite=True to overwrite."
|
|
84
|
+
warnings.warn(msg)
|
|
85
|
+
return source
|
|
86
|
+
|
|
87
|
+
def _getSBMLFromReference(self)->str:
|
|
88
|
+
"""Extracts an SBML strong from the model reference
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
self.model_ref (str): reference to the file; reference type is specified separately
|
|
92
|
+
self.ref_type (str): One of self.MODEL_self.REF_TYPES
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
SBML string
|
|
96
|
+
"""
|
|
97
|
+
if self.ref_type in [SBML_FILE, ANT_FILE]:
|
|
98
|
+
with open(self.model_ref, "r") as f:
|
|
99
|
+
lines = f.read()
|
|
100
|
+
if self.ref_type == SBML_FILE:
|
|
101
|
+
sbml_str = lines
|
|
102
|
+
else:
|
|
103
|
+
sbml_str = te.antimonyToSBML(lines)
|
|
104
|
+
elif self.ref_type == SBML_STR:
|
|
105
|
+
sbml_str = self.model_ref
|
|
106
|
+
elif self.ref_type == ANT_STR:
|
|
107
|
+
sbml_str = te.antimonyToSBML(self.model_ref)
|
|
108
|
+
elif self.ref_type == MODEL_ID:
|
|
109
|
+
# self.model_ref is the ID of a previously defined model
|
|
110
|
+
sbml_str = ""
|
|
111
|
+
else:
|
|
112
|
+
# self.ref_type == SBML_URL
|
|
113
|
+
response = urllib3.request("GET", self.model_ref)
|
|
114
|
+
if response.status == 200:
|
|
115
|
+
sbml_str = codecs.decode(response.data, 'utf-8')
|
|
116
|
+
else:
|
|
117
|
+
raise ValueError(f"Failed to fetch SBML from URL: {self.model_ref}")
|
|
118
|
+
return sbml_str
|
|
119
|
+
|
|
120
|
+
def __str__(self):
|
|
121
|
+
params = ", ".join(f"{param} = {val}" for param, val in self.param_change_dct.items())
|
|
122
|
+
if len(params) > 0:
|
|
123
|
+
params = f" with {params}"
|
|
124
|
+
if self.ref_type == MODEL_ID:
|
|
125
|
+
source = self.id
|
|
126
|
+
else:
|
|
127
|
+
source = f'"{self.model_source_path}"'
|
|
128
|
+
return f'{self.id} = model {source} {params}'
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def findReferenceType(model_ref:str, model_ids:List[str], ref_type:Optional[str]=None)->str:
|
|
132
|
+
"""Infers the reference type from the model reference.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
model_ref (str): reference to the file; reference type is specified separately
|
|
136
|
+
model_ids (List[str]): List of known model IDs
|
|
137
|
+
refer_type (str): One of self.MODEL_REF_TYPES
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
str: reference type
|
|
141
|
+
"""
|
|
142
|
+
# Use the ref_type if it is specified
|
|
143
|
+
if ref_type is not None:
|
|
144
|
+
return ref_type
|
|
145
|
+
# Check if this is a model ID
|
|
146
|
+
if model_ref in model_ids:
|
|
147
|
+
try:
|
|
148
|
+
is_file = os.path.exists(model_ref)
|
|
149
|
+
except:
|
|
150
|
+
is_file = False
|
|
151
|
+
if is_file:
|
|
152
|
+
warnings.warn(f"Model ID {model_ref} is also a file path. Using model ID.")
|
|
153
|
+
return MODEL_ID
|
|
154
|
+
# Check for Antimony string
|
|
155
|
+
try:
|
|
156
|
+
_ = te.loada(model_ref)
|
|
157
|
+
return ANT_STR
|
|
158
|
+
except:
|
|
159
|
+
pass
|
|
160
|
+
# Check for SBML string
|
|
161
|
+
try:
|
|
162
|
+
_ = te.loadSBMLModel(model_ref)
|
|
163
|
+
if "sbml" in model_ref:
|
|
164
|
+
return SBML_STR
|
|
165
|
+
except:
|
|
166
|
+
pass
|
|
167
|
+
# Check if this is a URL
|
|
168
|
+
if ("http://" in model_ref) or ("https://" in model_ref):
|
|
169
|
+
return SBML_URL
|
|
170
|
+
# Check if this is a file path
|
|
171
|
+
try:
|
|
172
|
+
is_file = os.path.exists(model_ref)
|
|
173
|
+
except:
|
|
174
|
+
is_file = False
|
|
175
|
+
if is_file:
|
|
176
|
+
try:
|
|
177
|
+
with open(model_ref, "r") as f:
|
|
178
|
+
lines = f.read()
|
|
179
|
+
if lines.startswith("<"):
|
|
180
|
+
return SBML_FILE
|
|
181
|
+
else:
|
|
182
|
+
return ANT_FILE
|
|
183
|
+
except:
|
|
184
|
+
pass
|
|
185
|
+
# Report error
|
|
186
|
+
msg = f"Unidentifiable model reference: {model_ref}. "
|
|
187
|
+
msg += f"\nFix the reference and/or specify the reference type.\nMust be one of {MODEL_REF_TYPES}."
|
|
188
|
+
raise ValueError(msg)
|
plot.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Optional, List, Union
|
|
2
|
+
|
|
3
|
+
class Plot:
|
|
4
|
+
def __init__(self, x_var:str, y_var:Union[str, List[str]], z_var:Optional[str]=None, title:Optional[str]=None,
|
|
5
|
+
is_plot:bool=True)->None:
|
|
6
|
+
"""
|
|
7
|
+
Plot class to represent a plot in the script. The following cases are supported:
|
|
8
|
+
plot x vs y (z is None, y is str)
|
|
9
|
+
plot x vs y1, y2, y3 (z is None, y is a list of str)
|
|
10
|
+
plot x vs y vs z (z is a str, y is str)
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
x_var (str): x variable
|
|
14
|
+
y_var (str): y variable
|
|
15
|
+
z_var (str, optional): z variable. Defaults to None.
|
|
16
|
+
title (str, optional): title of the plot. Defaults to None.
|
|
17
|
+
"""
|
|
18
|
+
self.x_var = x_var
|
|
19
|
+
self.y_var = y_var
|
|
20
|
+
self.z_var = z_var
|
|
21
|
+
self.title = title
|
|
22
|
+
self.is_plot = is_plot
|
|
23
|
+
|
|
24
|
+
# FIXME: Support the above use cases
|
|
25
|
+
def __str__(self)->str:
|
|
26
|
+
if not self.is_plot:
|
|
27
|
+
return ""
|
|
28
|
+
if self.z_var is None:
|
|
29
|
+
if isinstance(self.y_var, str):
|
|
30
|
+
y_vars = [self.y_var]
|
|
31
|
+
else:
|
|
32
|
+
y_vars = self.y_var
|
|
33
|
+
var_clause = f'{self.x_var} vs {", ".join(y_vars)}'
|
|
34
|
+
else:
|
|
35
|
+
if not isinstance(self.y_var, str):
|
|
36
|
+
raise ValueError("y_var must be a string when z_var is provided")
|
|
37
|
+
var_clause = f"{self.x_var} vs {self.y_var} vs {self.z_var}"
|
|
38
|
+
if self.title is None:
|
|
39
|
+
return f"plot {var_clause}"
|
|
40
|
+
else:
|
|
41
|
+
return f"plot \"{self.title}\" {var_clause}"
|
report.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
class Report:
|
|
4
|
+
def __init__(self, metadata:Optional[dict]=None, title:str=""):
|
|
5
|
+
"""Reports data after a simulation.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
metadata (Optional[dict], optional): A dictionary of values saved in the
|
|
9
|
+
'attrs' attribute of the DataFrame generated.
|
|
10
|
+
title (str, optional): Saved in the SEDML
|
|
11
|
+
"""
|
|
12
|
+
self.metadata = metadata
|
|
13
|
+
self.title = title
|
|
14
|
+
self.variables:list = []
|
|
15
|
+
|
|
16
|
+
def addVariables(self, *args):
|
|
17
|
+
"""
|
|
18
|
+
List of data to report
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
*args: list of report variables
|
|
22
|
+
"""
|
|
23
|
+
self.variables.extend(args)
|
|
24
|
+
|
|
25
|
+
def __str__(self)->str:
|
|
26
|
+
return "\n".join([f'report "{self.title}" {", ".join(self.variables)}'])
|
simple_sedml.py
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
from src.model import Model, ANT_STR
|
|
2
|
+
from src.simulation import Simulation
|
|
3
|
+
from src.task import Task, RepeatedTask
|
|
4
|
+
from plot import Plot
|
|
5
|
+
from src.report import Report
|
|
6
|
+
|
|
7
|
+
from collections import namedtuple
|
|
8
|
+
import pandas as pd # type: ignore
|
|
9
|
+
import phrasedml # type: ignore
|
|
10
|
+
import tellurium as te # type: ignore
|
|
11
|
+
import warnings
|
|
12
|
+
from typing import Optional, List, Tuple, Union
|
|
13
|
+
|
|
14
|
+
REPORT = "report"
|
|
15
|
+
MODEL = "model"
|
|
16
|
+
SIMULATION = "simulation"
|
|
17
|
+
TASK = "task"
|
|
18
|
+
REPEATED_TASK = "repeated_task"
|
|
19
|
+
PLOT2D = "plot2d"
|
|
20
|
+
TIME_COURSE = "time_course"
|
|
21
|
+
# Simulation parameters
|
|
22
|
+
ST_UNIFORM = "uniform"
|
|
23
|
+
ST_STEADY_STATE = "steady_state"
|
|
24
|
+
ST_UNIFORM_STOCHASTIC = "uniform_stochastic"
|
|
25
|
+
# Default values
|
|
26
|
+
D_START = 0
|
|
27
|
+
D_END = 5
|
|
28
|
+
D_NUM_STEP = 10
|
|
29
|
+
D_NUM_POINT = D_NUM_STEP + 1
|
|
30
|
+
D_REF_TYPE = ANT_STR
|
|
31
|
+
D_SIM_TYPE = ST_UNIFORM
|
|
32
|
+
D_SIM_UNIFORM_ALGORITHM = "CVODE"
|
|
33
|
+
D_SIM_UNIFORM_STOCHASTIC_ALGORITHM = "gillespie"
|
|
34
|
+
|
|
35
|
+
ModelInfo = namedtuple("ModelInfo", ["model_id", "parameters", "floating_species"])
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
PhraSED-ML is strctured as a series of sections, each of which specifies a Model, Simulation, Task or repeated task.
|
|
39
|
+
|
|
40
|
+
A model section contains one or more references to models. Some of these may be indirect in that they reference a reference.
|
|
41
|
+
A model section may contain changes to parameters, initial conditions or other model components.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Restrictions:
|
|
45
|
+
- No local variables (because this is an API)
|
|
46
|
+
- No formulas (because this is an API and python can do this)
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SimpleSEDML(object):
|
|
51
|
+
"""A directive can consist of many sections each of which species a Model, Simulation, Task or repeated task,
|
|
52
|
+
and an action (plot or report).
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
object (_type_): _description_
|
|
56
|
+
"""
|
|
57
|
+
def __init__(self)->None:
|
|
58
|
+
# Dictionary of script elements, indexed by their section ID
|
|
59
|
+
self.model_dct:dict = {}
|
|
60
|
+
self.simulation_dct:dict = {}
|
|
61
|
+
self.task_dct:dict = {}
|
|
62
|
+
self.repeated_task_dct:dict = {}
|
|
63
|
+
self.report_dct:dict = {}
|
|
64
|
+
self.plot_dct:dict = {}
|
|
65
|
+
#
|
|
66
|
+
self.report_id = 0
|
|
67
|
+
self.plot_id = 0
|
|
68
|
+
self.time_course_id = 0
|
|
69
|
+
|
|
70
|
+
def __str__(self)->str:
|
|
71
|
+
"""Creates phrasedml string from composition of sections
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
str: SED-ML string
|
|
75
|
+
"""
|
|
76
|
+
sections = [
|
|
77
|
+
*[str(m) for m in self.model_dct.values()],
|
|
78
|
+
*[str(s) for s in self.simulation_dct.values()],
|
|
79
|
+
*[str(t) for t in self.task_dct.values()],
|
|
80
|
+
*[str(rt) for rt in self.repeated_task_dct.values()],
|
|
81
|
+
*[str(r) for r in self.report_dct.values()],
|
|
82
|
+
*[str(p) for p in self.plot_dct.values()],
|
|
83
|
+
]
|
|
84
|
+
return "\n".join(sections)
|
|
85
|
+
|
|
86
|
+
def antimonyToSBML(antimony_str)->str:
|
|
87
|
+
"""Converts an Antimony string to SBML
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
antimony_str: Antimony string
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
str: SBML string
|
|
94
|
+
"""
|
|
95
|
+
return te.antimonyToSBML(antimony_str)
|
|
96
|
+
|
|
97
|
+
def getSEDML(self)->str:
|
|
98
|
+
"""Converts the script to a SED-ML string
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
str: SED-ML string
|
|
102
|
+
Raises:
|
|
103
|
+
ValueError: if the conversion failsk
|
|
104
|
+
"""
|
|
105
|
+
sedml_str = phrasedml.convertString(str(self))
|
|
106
|
+
if sedml_str is None:
|
|
107
|
+
raise ValueError(phrasedml.getLastError())
|
|
108
|
+
return sedml_str
|
|
109
|
+
|
|
110
|
+
def _checkDuplicate(self, id:str, dict_type:str):
|
|
111
|
+
"""Checks if the ID already exists in the dictionary
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
id: ID of the model
|
|
115
|
+
dict_type: type of the dictionary (model, simulation, task, repeated_task)
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ValueError: if the ID already exists in the dictionary
|
|
119
|
+
"""
|
|
120
|
+
TYPE_DCT = {
|
|
121
|
+
MODEL: self.model_dct,
|
|
122
|
+
SIMULATION: self.simulation_dct,
|
|
123
|
+
TASK: self.task_dct,
|
|
124
|
+
REPEATED_TASK: self.repeated_task_dct,
|
|
125
|
+
REPORT: self.report_dct,
|
|
126
|
+
PLOT2D: self.plot_dct,
|
|
127
|
+
}
|
|
128
|
+
if id in TYPE_DCT[dict_type]:
|
|
129
|
+
raise ValueError(f"Duplicate {dict_type} ID: {id}")
|
|
130
|
+
|
|
131
|
+
def addModel(self, id:str, model_ref:str, ref_type:Optional[str]=None,
|
|
132
|
+
model_source_path:Optional[str]=None, is_overwrite:bool=False, **parameters):
|
|
133
|
+
"""Adds a model to the script
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
id: ID of the model
|
|
137
|
+
model_ref: reference to the model
|
|
138
|
+
ref_type: type of the reference (e.g. "sbml_str", "ant_str", "sbml_file", "ant_file", "sbml_url")
|
|
139
|
+
model_source_path: path to the model source file
|
|
140
|
+
is_overwrite: if True, overwrite the model if it already exists
|
|
141
|
+
"""
|
|
142
|
+
self._checkDuplicate(id, MODEL)
|
|
143
|
+
model_ids = list(self.model_dct.keys())
|
|
144
|
+
ref_type = Model.findReferenceType(model_ref, model_ids, ref_type=ref_type)
|
|
145
|
+
model = Model(id, model_ref, ref_type=ref_type,
|
|
146
|
+
model_source=model_source_path, is_overwrite=is_overwrite, **parameters)
|
|
147
|
+
self.model_dct[id] = model
|
|
148
|
+
|
|
149
|
+
def addSimulation(self,
|
|
150
|
+
id:str,
|
|
151
|
+
simulation_type:str=ST_UNIFORM,
|
|
152
|
+
start:float=D_START,
|
|
153
|
+
end:float=D_END,
|
|
154
|
+
num_step:Optional[int]=None,
|
|
155
|
+
num_point:Optional[int]=None,
|
|
156
|
+
algorithm:Optional[str]=None):
|
|
157
|
+
"""Adds a simulation to the script
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
id (str): Simulation identifier
|
|
161
|
+
start (float): start time for simulation
|
|
162
|
+
end (float): end time for simulation
|
|
163
|
+
"""
|
|
164
|
+
if (num_step is None) and (num_point is None):
|
|
165
|
+
num_step = D_NUM_STEP
|
|
166
|
+
elif (num_step is None) and (num_point is not None):
|
|
167
|
+
num_step = num_point - 1
|
|
168
|
+
if algorithm is None:
|
|
169
|
+
if simulation_type == ST_UNIFORM:
|
|
170
|
+
algorithm = D_SIM_UNIFORM_ALGORITHM
|
|
171
|
+
elif simulation_type == ST_UNIFORM_STOCHASTIC:
|
|
172
|
+
algorithm = D_SIM_UNIFORM_STOCHASTIC_ALGORITHM
|
|
173
|
+
self._checkDuplicate(id, SIMULATION)
|
|
174
|
+
self.simulation_dct[id] = Simulation(id, simulation_type, start, end,
|
|
175
|
+
num_step, algorithm=algorithm) # type: ignore
|
|
176
|
+
|
|
177
|
+
def addTask(self, id, model_id:str, simulation_id:str):
|
|
178
|
+
"""Adds a task to the script
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
id: ID of the task
|
|
182
|
+
model: Model object
|
|
183
|
+
simulation: Simulation object
|
|
184
|
+
"""
|
|
185
|
+
self._checkDuplicate(id, TASK)
|
|
186
|
+
task = Task(id, model_id, simulation_id)
|
|
187
|
+
self.task_dct[id] = task
|
|
188
|
+
|
|
189
|
+
def addRepeatedTask(self, id:str, subtask_id:str, parameter_df:pd.DataFrame, reset:bool=True):
|
|
190
|
+
"""Adds a repeated task to the script
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
repeated_task: RepeatedTask object
|
|
194
|
+
"""
|
|
195
|
+
self._checkDuplicate(id, REPEATED_TASK)
|
|
196
|
+
task = RepeatedTask(id, subtask_id, parameter_df, reset=reset)
|
|
197
|
+
self.repeated_task_dct[id] = task
|
|
198
|
+
|
|
199
|
+
def addPlot(self, x_var:str, y_var:Union[str, List[str]], z_var:Optional[str]=None, title:Optional[str]=None,
|
|
200
|
+
id:Optional[str]=None,
|
|
201
|
+
is_plot:bool=True)->None:
|
|
202
|
+
"""
|
|
203
|
+
Plot class to represent a plot in the script.
|
|
204
|
+
Args:
|
|
205
|
+
x_var (str): x variable
|
|
206
|
+
y_var (str): y variable or list of y variables
|
|
207
|
+
z_var (str, optional): z variable. Defaults to None.
|
|
208
|
+
title (str, optional): title of the plot. Defaults to None.
|
|
209
|
+
id (str, optional): ID of the plot. Defaults to None.
|
|
210
|
+
is_plot (bool, optional): if True, plot the data. Defaults to True.
|
|
211
|
+
"""
|
|
212
|
+
if id is None:
|
|
213
|
+
id = str(self.plot_id)
|
|
214
|
+
self.plot_id += 1
|
|
215
|
+
plot = Plot(x_var, y_var, z_var=z_var, title=title, is_plot=is_plot)
|
|
216
|
+
self.plot_dct[id] = plot
|
|
217
|
+
|
|
218
|
+
def addReport(self, *report_variables, id:Optional[str]=None,
|
|
219
|
+
metadata:Optional[dict]=None, title:str=""):
|
|
220
|
+
"""Adds data to the report
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
id: ID of the report
|
|
224
|
+
report_variable: variable to be added to the report
|
|
225
|
+
metadata: metadata for the report variable
|
|
226
|
+
title: title for the report variable
|
|
227
|
+
"""
|
|
228
|
+
if id is None:
|
|
229
|
+
id = str(self.report_id)
|
|
230
|
+
self.report_id += 1
|
|
231
|
+
if not id in self.report_dct.keys():
|
|
232
|
+
if len(title) == 0:
|
|
233
|
+
title = f"Report {id}"
|
|
234
|
+
self.report_dct[id] = Report(metadata=metadata, title=title)
|
|
235
|
+
if metadata is not None:
|
|
236
|
+
self.report_dct[id].metadata = metadata
|
|
237
|
+
if title is not None:
|
|
238
|
+
self.report_dct[id].title = title
|
|
239
|
+
self.report_dct[id].addVariables(*report_variables)
|
|
240
|
+
|
|
241
|
+
def _getModelInfo(self, model_id)->ModelInfo:
|
|
242
|
+
"""Returns information about model ID, parameters, floating species and fixed species.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
model_id (str):
|
|
246
|
+
|
|
247
|
+
Returns: ModelInfo
|
|
248
|
+
"""
|
|
249
|
+
if model_id not in self.model_dct:
|
|
250
|
+
raise ValueError(f"Model ID {model_id} not found.")
|
|
251
|
+
model = self.model_dct[model_id]
|
|
252
|
+
rr = te.loadSBMLModel(model.sbml_str)
|
|
253
|
+
model_info = ModelInfo(
|
|
254
|
+
model_id=model.id,
|
|
255
|
+
parameters=rr.getGlobalParameterIds(),
|
|
256
|
+
floating_species=rr.getFloatingSpeciesIds(),
|
|
257
|
+
)
|
|
258
|
+
return model_info
|
|
259
|
+
|
|
260
|
+
def execute(self)->pd.DataFrame:
|
|
261
|
+
"""Executes the script and returns the results as a DataFrame
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
pd.DataFrame: DataFrame with the results
|
|
265
|
+
"""
|
|
266
|
+
if (len(self.repeated_task_dct) > 0):
|
|
267
|
+
is_repeated_task = True
|
|
268
|
+
else:
|
|
269
|
+
is_repeated_task = False
|
|
270
|
+
if len(self.task_dct) > 1:
|
|
271
|
+
is_more_than_one_task = True
|
|
272
|
+
else:
|
|
273
|
+
is_more_than_one_task = False
|
|
274
|
+
if len(self.report_dct) > 0:
|
|
275
|
+
if is_repeated_task:
|
|
276
|
+
warnings.warn("Reports only generate data for the last repeated task.")
|
|
277
|
+
if is_more_than_one_task:
|
|
278
|
+
warnings.warn("Reports only generate data for the last task.")
|
|
279
|
+
te.executeSEDML(self.getSEDML())
|
|
280
|
+
return te.getLastReport()
|
|
281
|
+
|
|
282
|
+
def getModelInfo(self, model_id:Optional[str]=None)->List[dict]:
|
|
283
|
+
"""Returns a dictionary with the model information
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
model_id: ID of the model. If None, returns information for all models
|
|
287
|
+
|
|
288
|
+
Returns: List of dictionaries structured as follows:
|
|
289
|
+
"task_id": str
|
|
290
|
+
"parameters": list of parameters
|
|
291
|
+
"species": list of species
|
|
292
|
+
"""
|
|
293
|
+
info_dcts:list = []
|
|
294
|
+
for model in self.model_dct.values():
|
|
295
|
+
if (model_id is not None) and (model.id != model_id):
|
|
296
|
+
continue
|
|
297
|
+
model_info = self._getModelInfo(model.id)
|
|
298
|
+
info_dct = dict(
|
|
299
|
+
model_id=model_info.model_id,
|
|
300
|
+
parameters=model_info.parameters,
|
|
301
|
+
floating_species=model_info.floating_species
|
|
302
|
+
)
|
|
303
|
+
info_dcts.append(info_dct)
|
|
304
|
+
return info_dcts
|
|
305
|
+
|
|
306
|
+
@classmethod
|
|
307
|
+
def makeTimeCourse(cls,
|
|
308
|
+
model_ref:str,
|
|
309
|
+
ref_type:Optional[str]=None,
|
|
310
|
+
plot_variables:Optional[str]=None,
|
|
311
|
+
start:float=0, end:float=5, num_step:int=50,
|
|
312
|
+
time_course_id:Optional[str]=None,
|
|
313
|
+
title:Optional[str]=None,
|
|
314
|
+
algorithm:Optional[str]=None,
|
|
315
|
+
**parameters)->str:
|
|
316
|
+
"""Creates a time course simulation
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
model_ref: reference to the model
|
|
320
|
+
ref_type: type of the reference (e.g. "sbml_str", "ant_str", "sbml_file", "ant_file", "sbml_url")
|
|
321
|
+
plot_variables: variables to be plotted
|
|
322
|
+
start: start time
|
|
323
|
+
end: end time
|
|
324
|
+
num_step: number of steps
|
|
325
|
+
time_course_id: ID of the time course simulation
|
|
326
|
+
algorithm: algorithm to use for the simulation
|
|
327
|
+
title: title of the plot
|
|
328
|
+
parameters: parameters to be passed to the model
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
str: SEDML
|
|
332
|
+
"""
|
|
333
|
+
if time_course_id is None:
|
|
334
|
+
time_course_id = TIME_COURSE
|
|
335
|
+
model_id = f"{time_course_id}_model"
|
|
336
|
+
sim_id = f"{time_course_id}_sim"
|
|
337
|
+
task_id = f"{time_course_id}_task"
|
|
338
|
+
if title is None:
|
|
339
|
+
title = ""
|
|
340
|
+
#
|
|
341
|
+
simple = cls()
|
|
342
|
+
simple.addModel(model_id, model_ref, ref_type=ref_type, is_overwrite=True, **parameters)
|
|
343
|
+
if plot_variables is None:
|
|
344
|
+
variable_dct = simple.getModelInfo(model_id)[0]
|
|
345
|
+
plot_variables = variable_dct['floating_species']
|
|
346
|
+
plot_variables.insert(0, "time") # type: ignore
|
|
347
|
+
simple.addSimulation(sim_id, "uniform", start, end, num_step, algorithm=algorithm)
|
|
348
|
+
simple.addTask(task_id, model_id, sim_id)
|
|
349
|
+
x1_var = plot_variables[0]
|
|
350
|
+
y_vars = plot_variables[1:]
|
|
351
|
+
simple.addPlot(x1_var, y_vars, title=title)
|
|
352
|
+
return simple.getSEDML()
|
|
353
|
+
|
|
354
|
+
def validate(self):
|
|
355
|
+
"""
|
|
356
|
+
Validates the script and returns a list of errors.
|
|
357
|
+
Checks for:
|
|
358
|
+
1. At least one model is defined.
|
|
359
|
+
2. At least one simulation is defined.
|
|
360
|
+
3. At least one task is defined.
|
|
361
|
+
4. At least one output is defined.
|
|
362
|
+
5. All referenced task IDs are defined.
|
|
363
|
+
"""
|
|
364
|
+
raise NotImplementedError("Validation is not implemented yet.")
|
|
365
|
+
|
|
366
|
+
@classmethod
|
|
367
|
+
def executeSEDML(cls, sedml_str:str)->Union[None, pd.DataFrame]:
|
|
368
|
+
"""Executes the SED-ML string and returns the results as a DataFrame
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
sedml_str: SED-ML string
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
pd.DataFrame: DataFrame with the results
|
|
375
|
+
"""
|
|
376
|
+
try:
|
|
377
|
+
te.executeSEDML(sedml_str)
|
|
378
|
+
except Exception as e:
|
|
379
|
+
raise RuntimeError(f"SED-ML execution failed: {e}")
|
|
380
|
+
# Return a DataFrame if there is a report
|
|
381
|
+
num_report = sedml_str.count("<report id=")
|
|
382
|
+
if num_report > 1:
|
|
383
|
+
warnings.warn("Only generate data for the last report.")
|
|
384
|
+
if num_report >= 1:
|
|
385
|
+
df = te.getLastReport()
|
|
386
|
+
if df is None:
|
|
387
|
+
raise ValueError("No report found.")
|
|
388
|
+
return df
|
|
389
|
+
else:
|
|
390
|
+
return None
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: SimpleSEDML
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python Subnet Discovery for Systems Biology
|
|
5
|
+
Author-email: Joseph Hellerstein <joseph.hellerstein@gmail.com>, Herbert S Sauro <hsauro@uw.edu>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 UW Sauro Lab
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/sys-bio/SimpleSEDML
|
|
29
|
+
Requires-Python: >=3.6
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: numpy
|
|
33
|
+
Requires-Dist: build
|
|
34
|
+
Requires-Dist: coverage
|
|
35
|
+
Requires-Dist: jupyterlab
|
|
36
|
+
Requires-Dist: matplotlib
|
|
37
|
+
Requires-Dist: nose2
|
|
38
|
+
Requires-Dist: pandas
|
|
39
|
+
Requires-Dist: phrasedml
|
|
40
|
+
Requires-Dist: pip
|
|
41
|
+
Requires-Dist: requests
|
|
42
|
+
Requires-Dist: tabulate
|
|
43
|
+
Requires-Dist: tellurium
|
|
44
|
+
Requires-Dist: twine
|
|
45
|
+
Requires-Dist: urllib3
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
[](https://github.com/sys-bio/SimpleSBML/actions/workflows/github-actions.yml)
|
|
49
|
+
|
|
50
|
+
# SimpleSEDML
|
|
51
|
+
A simple API for using the [Simulation Experiment Description Markup Language (SED-ML)](https://sed-ml.org/), a community standard for describing simulation experiments.
|
|
52
|
+
|
|
53
|
+
The project provides a python interface to generating SED-ML based on the abstractions provided by [phraSED-ML](https://pmc.ncbi.nlm.nih.gov/articles/PMC5313123/pdf/nihms846540.pdf) to describe simulation experiments. These absractions are: (a) models (including changes in values of model parameters);
|
|
54
|
+
(b) simulations (including deterministic, stochastic, and steady state);
|
|
55
|
+
(c) tasks (which specify simulations to run on tasks and repetitions for changes in parameter values);
|
|
56
|
+
and (d) output for data reports and plots.
|
|
57
|
+
|
|
58
|
+
``SimpleSEDML`` generalizes the capabilities of ``PhraSEDML`` and simplifies its usage by exploiting the Python environment:
|
|
59
|
+
|
|
60
|
+
* A model source can be a file path or URL and may be in the Antimony language as well as SBML;
|
|
61
|
+
* Repeated tasks are defined more simply by the use of a ``pandas`` ``DataFrame``.
|
|
62
|
+
* Convenience methods are provided to simplify the API.
|
|
63
|
+
|
|
64
|
+
# Example
|
|
65
|
+
|
|
66
|
+
See this [Jupyter notebook](https://github.com/sys-bio/SimpleSEDML/blob/main/examples/usage_examples.ipynb) for a detailed example.
|
|
67
|
+
|
|
68
|
+
Consider the model below in the Antimony language.
|
|
69
|
+
|
|
70
|
+
mymodel = """
|
|
71
|
+
model myModel
|
|
72
|
+
J1: S1 -> S2; k1*S1;
|
|
73
|
+
k1 = 0.5;
|
|
74
|
+
end
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
We want to simulate this model and do a time course plot of all floating species in the model.
|
|
78
|
+
|
|
79
|
+
from simple_sedml import SimpleSEDML
|
|
80
|
+
|
|
81
|
+
sedml_str = SimpleSEDML.makeTimeCourse(mymodel)
|
|
82
|
+
|
|
83
|
+
We can print, save, or execute ``sedml_str``. To execute it,
|
|
84
|
+
|
|
85
|
+
SimpleSEDML.executeSEDML(sedml_str)
|
|
86
|
+
<img src="docs/images/phrasedml_example.png" style="width:300px;height:300px;">
|
|
87
|
+
|
|
88
|
+
# Restrictions
|
|
89
|
+
1. If there are multiple task directives and/or there is a repeated task directive AND there is a report directive, SimpleSEDML.execute only returns the results of the last simulation. You can circumvent this by iterating in python to obtain the desired reports.
|
|
90
|
+
|
|
91
|
+
# Plans
|
|
92
|
+
1. First implementation of ``SimpleSEDML`` with methods for ``addModel``, ``addSimulation``, ``addTask``, ``addReport``, ``execute``, and ``to_sedml``.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
__init__.py,sha256=sXLh7g3KC4QCFxcZGBTpG2scR7hmmBsMjq6LqRptkRg,22
|
|
2
|
+
constants.py,sha256=Gko24Uw_l3rFDYmL9qEjOWZHyVul14dycoIV_BDUA-k,113
|
|
3
|
+
model.py,sha256=VYKRcIYTpdfV2KpHGScp3xmAKqPu_BvtWgq1aTF4n2c,7058
|
|
4
|
+
plot.py,sha256=uhVbfd7RDcjAH1_mIxIYYHXkPA8NZLxAAKnnpbDDCH0,1562
|
|
5
|
+
report.py,sha256=K4VNeQE5RfJncyvjgSbZw9qU1ww0vGM1sjt3ndGZho4,771
|
|
6
|
+
simple_sedml.py,sha256=YHaroiHa_vYG2MH7p6F7nIV_CD0JOtTdKaM1rcQox_o,14077
|
|
7
|
+
simulation.py,sha256=0_82AVeAPGzY9vOLysrl6khHwt-abV1ofcP4nZzUCQk,3654
|
|
8
|
+
task.py,sha256=XA5CLfrbbXuXumfbYcHCvecl0PtYxnYG38uOLPmc1lY,2064
|
|
9
|
+
simplesedml-0.0.1.dist-info/licenses/LICENSE,sha256=vyn7B-UTdbg7Tu5NPD5FChKtwroX0n_ILQwKp3RpXGY,1069
|
|
10
|
+
simplesedml-0.0.1.dist-info/METADATA,sha256=ZsCoH1XRog6Hu0dIeImskzx4_CcNYlZ-bRoc-iUv5os,4300
|
|
11
|
+
simplesedml-0.0.1.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
|
12
|
+
simplesedml-0.0.1.dist-info/top_level.txt,sha256=7Yk_B53o9eR6joDkOmHrrrMr8zgi25-LP7XMmOWwELg,66
|
|
13
|
+
simplesedml-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 UW Sauro Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
simulation.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'''Simulation directives for PhraSEDML'''
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
# Simulation types
|
|
6
|
+
ST_UNIFORM = "uniform"
|
|
7
|
+
ST_STOCHASTIC = "stochastic"
|
|
8
|
+
ST_ONESTEP = "onestep"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Simulation:
|
|
12
|
+
def __init__(self, id:str, simulation_type:str,
|
|
13
|
+
start:float=0,
|
|
14
|
+
end:float=5,
|
|
15
|
+
num_step:int=50,
|
|
16
|
+
time_interval:float=0.5, # required for onestep
|
|
17
|
+
absolute_tolerance:Optional[float]=None,
|
|
18
|
+
algorithm:Optional[str]=None,
|
|
19
|
+
initial_time_step:Optional[float]=None,
|
|
20
|
+
maximum_adams_order:Optional[int]=None,
|
|
21
|
+
maximum_bdf_order:Optional[int]=None,
|
|
22
|
+
maximum_iterations:Optional[int]=None,
|
|
23
|
+
maximum_num_steps:Optional[int]=None,
|
|
24
|
+
maximum_time_step:Optional[float]=None,
|
|
25
|
+
minimum_damping:Optional[float]=None,
|
|
26
|
+
minimum_time_step:Optional[float]=None,
|
|
27
|
+
relative_tolerance:Optional[float]=None,
|
|
28
|
+
seed:Optional[int]=None,
|
|
29
|
+
variable_step_size:Optional[bool]=None):
|
|
30
|
+
"""Simulation class for SED-ML
|
|
31
|
+
Args:
|
|
32
|
+
id (str): identifier for the simulation
|
|
33
|
+
simulation_type (str): type of simulation
|
|
34
|
+
- "uniform": uniform simulation
|
|
35
|
+
- "stochastic": stochastic simulation
|
|
36
|
+
- "onestep": one-step simulation
|
|
37
|
+
start (float): start time for the simulation
|
|
38
|
+
end (float): end time for the simulation
|
|
39
|
+
num_step (int): number of steps for the simulation
|
|
40
|
+
time_interval (float): time interval for the simulation
|
|
41
|
+
algorithm (str): algorithm to use for the simulation. Defaults are:
|
|
42
|
+
- "CVODE": CVODE algorithm
|
|
43
|
+
- "gillespie": Gillespie algorithm
|
|
44
|
+
"""
|
|
45
|
+
self.id = id
|
|
46
|
+
self.simulation_type = simulation_type
|
|
47
|
+
self.start = start
|
|
48
|
+
self.end = end
|
|
49
|
+
self.num_step = num_step
|
|
50
|
+
self.time_interval = time_interval
|
|
51
|
+
if algorithm is None:
|
|
52
|
+
if simulation_type == ST_UNIFORM:
|
|
53
|
+
algorithm = "CVODE"
|
|
54
|
+
elif simulation_type == ST_STOCHASTIC:
|
|
55
|
+
algorithm = "gillespie"
|
|
56
|
+
self.algorithm = algorithm
|
|
57
|
+
# Setup the options
|
|
58
|
+
self.option_dct = dict(
|
|
59
|
+
absolute_tolerance=absolute_tolerance,
|
|
60
|
+
algorithm=algorithm,
|
|
61
|
+
initial_time_step=initial_time_step,
|
|
62
|
+
maximum_adams_order=maximum_adams_order,
|
|
63
|
+
maximum_bdf_order=maximum_bdf_order,
|
|
64
|
+
maximum_iterations=maximum_iterations,
|
|
65
|
+
maximum_num_steps=maximum_num_steps,
|
|
66
|
+
maximum_time_step=maximum_time_step,
|
|
67
|
+
minimum_damping=minimum_damping,
|
|
68
|
+
minimum_time_step=minimum_time_step,
|
|
69
|
+
relative_tolerance=relative_tolerance,
|
|
70
|
+
seed=seed,
|
|
71
|
+
variable_step_size=variable_step_size
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def __str__(self)->str:
|
|
75
|
+
if self.simulation_type == ST_UNIFORM:
|
|
76
|
+
simulate_arg = "simulate uniform"
|
|
77
|
+
elif self.simulation_type == ST_STOCHASTIC:
|
|
78
|
+
simulate_arg = "simulate uniform_stochastic"
|
|
79
|
+
if self.simulation_type == ST_ONESTEP:
|
|
80
|
+
line = f'{self.id} = simulate onestep({self.time_interval})'
|
|
81
|
+
else:
|
|
82
|
+
line = f'{self.id} = {simulate_arg}({self.start}, {self.end}, {self.num_step})'
|
|
83
|
+
# Include the options
|
|
84
|
+
option_lines = [f"{self.id}.algorithm.{k} = {str(v)} " for k, v in self.option_dct.items()
|
|
85
|
+
if (v is not None) and (k != "algorithm")]
|
|
86
|
+
option_lines.append(f"{self.id}.algorithm = {self.algorithm}")
|
|
87
|
+
section = line + "\n" + "\n".join(option_lines)
|
|
88
|
+
return section
|
task.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from src.model import Model
|
|
2
|
+
from src.simulation import Simulation
|
|
3
|
+
|
|
4
|
+
import pandas as pd; # type: ignore
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Task:
|
|
9
|
+
def __init__(self, id:str, model_id:str, simulation_id:str):
|
|
10
|
+
self.id = id
|
|
11
|
+
self.model_id = model_id
|
|
12
|
+
self.simulation_id = simulation_id
|
|
13
|
+
|
|
14
|
+
def __str__(self)->str:
|
|
15
|
+
result = f'{self.id} = run {self.simulation_id} on {self.model_id}'
|
|
16
|
+
return result
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RepeatedTask:
|
|
20
|
+
# A RepeatedTask executes a task with changes in the values of global parameters.
|
|
21
|
+
# Ex: repeat1 = repeat nested_task for S1 in [1, 3, 5], S2 in [0, 10, 3], reset=true
|
|
22
|
+
# Note that it is not necessary to specify functions as in the original phraSED-ML since python provides this.
|
|
23
|
+
|
|
24
|
+
def __init__(self, id:str, subtask_id:str, parameter_df:pd.DataFrame, reset:bool=True):
|
|
25
|
+
"""Repeats a single task with changes in global parameters.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
id (str): Identity of repeated task
|
|
29
|
+
subtask_id (Task): Task to be repeated
|
|
30
|
+
parameter_df (pd.DataFrame): DataFrame with the parameters to be changed. Column names must be global parameters.
|
|
31
|
+
reset (bool, optional): _description_. Defaults to True.
|
|
32
|
+
"""
|
|
33
|
+
#
|
|
34
|
+
self.id = id
|
|
35
|
+
self.subtask_id = subtask_id
|
|
36
|
+
self.parameter_df = parameter_df
|
|
37
|
+
self.reset = reset
|
|
38
|
+
|
|
39
|
+
def _makeChangeValues(self)->str:
|
|
40
|
+
"""Creates a string with the changes in the values of global parameters.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
ser (pd.Series): Series with the changes in the values of global parameters.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
str: String with the changes in the values of global parameters.
|
|
47
|
+
"""
|
|
48
|
+
results = []
|
|
49
|
+
for col in self.parameter_df.columns:
|
|
50
|
+
results.append(f'{col} in {str(list(self.parameter_df[col].values))}')
|
|
51
|
+
return ', '.join(results)
|
|
52
|
+
|
|
53
|
+
def __str__(self)->str:
|
|
54
|
+
line = f'{self.id} = repeat {self.subtask_id} for '
|
|
55
|
+
line += self._makeChangeValues()
|
|
56
|
+
line += f', reset={self.reset}'
|
|
57
|
+
return line
|