mxlpy 0.22.0__py3-none-any.whl → 0.24.0__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.
- mxlpy/__init__.py +13 -6
- mxlpy/carousel.py +52 -5
- mxlpy/compare.py +15 -2
- mxlpy/experimental/diff.py +14 -0
- mxlpy/fit.py +20 -1
- mxlpy/integrators/__init__.py +4 -0
- mxlpy/integrators/int_assimulo.py +3 -3
- mxlpy/integrators/int_diffrax.py +119 -0
- mxlpy/integrators/int_scipy.py +12 -6
- mxlpy/label_map.py +1 -2
- mxlpy/mc.py +85 -24
- mxlpy/mca.py +8 -4
- mxlpy/meta/__init__.py +6 -1
- mxlpy/meta/codegen_latex.py +9 -0
- mxlpy/meta/codegen_model.py +55 -12
- mxlpy/meta/codegen_mxlpy.py +215 -58
- mxlpy/meta/source_tools.py +129 -80
- mxlpy/meta/sympy_tools.py +12 -6
- mxlpy/model.py +314 -96
- mxlpy/plot.py +60 -29
- mxlpy/sbml/_data.py +34 -0
- mxlpy/sbml/_export.py +17 -8
- mxlpy/sbml/_import.py +68 -547
- mxlpy/scan.py +163 -249
- mxlpy/simulator.py +9 -359
- mxlpy/types.py +723 -80
- mxlpy/units.py +5 -0
- {mxlpy-0.22.0.dist-info → mxlpy-0.24.0.dist-info}/METADATA +7 -1
- mxlpy-0.24.0.dist-info/RECORD +57 -0
- mxlpy/sbml/_mathml.py +0 -692
- mxlpy/sbml/_unit_conversion.py +0 -74
- mxlpy-0.22.0.dist-info/RECORD +0 -58
- {mxlpy-0.22.0.dist-info → mxlpy-0.24.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.22.0.dist-info → mxlpy-0.24.0.dist-info}/licenses/LICENSE +0 -0
mxlpy/mc.py
CHANGED
@@ -24,9 +24,11 @@ import pandas as pd
|
|
24
24
|
from mxlpy import mca, scan
|
25
25
|
from mxlpy.parallel import Cache, parallelise
|
26
26
|
from mxlpy.scan import (
|
27
|
+
ProtocolTimeCourseWorker,
|
27
28
|
ProtocolWorker,
|
28
29
|
SteadyStateWorker,
|
29
30
|
TimeCourseWorker,
|
31
|
+
_protocol_time_course_worker,
|
30
32
|
_protocol_worker,
|
31
33
|
_steady_state_worker,
|
32
34
|
_time_course_worker,
|
@@ -35,10 +37,10 @@ from mxlpy.scan import (
|
|
35
37
|
from mxlpy.types import (
|
36
38
|
IntegratorType,
|
37
39
|
McSteadyStates,
|
38
|
-
|
40
|
+
ProtocolScan,
|
39
41
|
ResponseCoefficientsByPars,
|
40
|
-
|
41
|
-
|
42
|
+
SteadyStateScan,
|
43
|
+
TimeCourseScan,
|
42
44
|
)
|
43
45
|
|
44
46
|
if TYPE_CHECKING:
|
@@ -49,11 +51,12 @@ if TYPE_CHECKING:
|
|
49
51
|
__all__ = [
|
50
52
|
"ParameterScanWorker",
|
51
53
|
"parameter_elasticities",
|
54
|
+
"protocol",
|
55
|
+
"protocol_time_course",
|
52
56
|
"response_coefficients",
|
53
57
|
"scan_steady_state",
|
54
58
|
"steady_state",
|
55
59
|
"time_course",
|
56
|
-
"time_course_over_protocol",
|
57
60
|
"variable_elasticities",
|
58
61
|
]
|
59
62
|
|
@@ -69,7 +72,7 @@ class ParameterScanWorker(Protocol):
|
|
69
72
|
y0: dict[str, float] | None,
|
70
73
|
rel_norm: bool,
|
71
74
|
integrator: IntegratorType,
|
72
|
-
) ->
|
75
|
+
) -> SteadyStateScan:
|
73
76
|
"""Call the worker function."""
|
74
77
|
...
|
75
78
|
|
@@ -81,7 +84,7 @@ def _parameter_scan_worker(
|
|
81
84
|
y0: dict[str, float] | None,
|
82
85
|
rel_norm: bool,
|
83
86
|
integrator: IntegratorType,
|
84
|
-
) ->
|
87
|
+
) -> SteadyStateScan:
|
85
88
|
"""Worker function for parallel steady state scanning across parameter sets.
|
86
89
|
|
87
90
|
This function executes a parameter scan for steady state solutions for a
|
@@ -125,7 +128,7 @@ def steady_state(
|
|
125
128
|
rel_norm: bool = False,
|
126
129
|
worker: SteadyStateWorker = _steady_state_worker,
|
127
130
|
integrator: IntegratorType | None = None,
|
128
|
-
) ->
|
131
|
+
) -> SteadyStateScan:
|
129
132
|
"""Monte-carlo scan of steady states.
|
130
133
|
|
131
134
|
Examples:
|
@@ -163,10 +166,14 @@ def steady_state(
|
|
163
166
|
max_workers=max_workers,
|
164
167
|
cache=cache,
|
165
168
|
)
|
166
|
-
return
|
167
|
-
|
168
|
-
|
169
|
-
|
169
|
+
return SteadyStateScan(
|
170
|
+
raw_index=(
|
171
|
+
pd.Index(mc_to_scan.iloc[:, 0])
|
172
|
+
if mc_to_scan.shape[1] == 1
|
173
|
+
else pd.MultiIndex.from_frame(mc_to_scan)
|
174
|
+
),
|
175
|
+
raw_results=[i[1] for i in res],
|
176
|
+
to_scan=mc_to_scan,
|
170
177
|
)
|
171
178
|
|
172
179
|
|
@@ -180,7 +187,7 @@ def time_course(
|
|
180
187
|
cache: Cache | None = None,
|
181
188
|
worker: TimeCourseWorker = _time_course_worker,
|
182
189
|
integrator: IntegratorType | None = None,
|
183
|
-
) ->
|
190
|
+
) -> TimeCourseScan:
|
184
191
|
"""MC time course.
|
185
192
|
|
186
193
|
Examples:
|
@@ -219,14 +226,13 @@ def time_course(
|
|
219
226
|
cache=cache,
|
220
227
|
)
|
221
228
|
|
222
|
-
return
|
223
|
-
|
224
|
-
|
225
|
-
fluxes=pd.concat({k: v.fluxes.T for k, v in res}, axis=1).T,
|
229
|
+
return TimeCourseScan(
|
230
|
+
to_scan=mc_to_scan,
|
231
|
+
raw_results=dict(res),
|
226
232
|
)
|
227
233
|
|
228
234
|
|
229
|
-
def
|
235
|
+
def protocol(
|
230
236
|
model: Model,
|
231
237
|
*,
|
232
238
|
protocol: pd.DataFrame,
|
@@ -237,7 +243,7 @@ def time_course_over_protocol(
|
|
237
243
|
cache: Cache | None = None,
|
238
244
|
worker: ProtocolWorker = _protocol_worker,
|
239
245
|
integrator: IntegratorType | None = None,
|
240
|
-
) ->
|
246
|
+
) -> ProtocolScan:
|
241
247
|
"""MC time course.
|
242
248
|
|
243
249
|
Examples:
|
@@ -277,13 +283,68 @@ def time_course_over_protocol(
|
|
277
283
|
max_workers=max_workers,
|
278
284
|
cache=cache,
|
279
285
|
)
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
+
return ProtocolScan(
|
287
|
+
to_scan=mc_to_scan,
|
288
|
+
protocol=protocol,
|
289
|
+
raw_results=dict(res),
|
290
|
+
)
|
291
|
+
|
292
|
+
|
293
|
+
def protocol_time_course(
|
294
|
+
model: Model,
|
295
|
+
*,
|
296
|
+
protocol: pd.DataFrame,
|
297
|
+
time_points: Array,
|
298
|
+
mc_to_scan: pd.DataFrame,
|
299
|
+
y0: dict[str, float] | None = None,
|
300
|
+
max_workers: int | None = None,
|
301
|
+
cache: Cache | None = None,
|
302
|
+
worker: ProtocolTimeCourseWorker = _protocol_time_course_worker,
|
303
|
+
integrator: IntegratorType | None = None,
|
304
|
+
) -> ProtocolScan:
|
305
|
+
"""MC time course.
|
306
|
+
|
307
|
+
Examples:
|
308
|
+
>>> protocol_time_course(model, protocol, time_points, mc_to_scan)
|
309
|
+
p t x y
|
310
|
+
0 0.0 0.1 0.00
|
311
|
+
1.0 0.2 0.01
|
312
|
+
2.0 0.3 0.02
|
313
|
+
3.0 0.4 0.03
|
314
|
+
... ... ...
|
315
|
+
1 0.0 0.1 0.00
|
316
|
+
1.0 0.2 0.01
|
317
|
+
2.0 0.3 0.02
|
318
|
+
3.0 0.4 0.03
|
319
|
+
|
320
|
+
Returns:
|
321
|
+
tuple[concentrations, fluxes] using pandas multiindex
|
322
|
+
Both dataframes are of shape (#time_points * #mc_to_scan, #variables)
|
323
|
+
|
324
|
+
"""
|
325
|
+
if y0 is not None:
|
326
|
+
model.update_variables(y0)
|
327
|
+
|
328
|
+
res = parallelise(
|
329
|
+
partial(
|
330
|
+
_update_parameters_and_initial_conditions,
|
331
|
+
fn=partial(
|
332
|
+
worker,
|
333
|
+
protocol=protocol,
|
334
|
+
time_points=time_points,
|
335
|
+
integrator=integrator,
|
336
|
+
y0=None,
|
337
|
+
),
|
338
|
+
model=model,
|
339
|
+
),
|
340
|
+
inputs=list(mc_to_scan.iterrows()),
|
341
|
+
max_workers=max_workers,
|
342
|
+
cache=cache,
|
343
|
+
)
|
344
|
+
return ProtocolScan(
|
345
|
+
to_scan=mc_to_scan,
|
286
346
|
protocol=protocol,
|
347
|
+
raw_results=dict(res),
|
287
348
|
)
|
288
349
|
|
289
350
|
|
mxlpy/mca.py
CHANGED
@@ -91,8 +91,12 @@ def _response_coefficient_worker(
|
|
91
91
|
y0=None,
|
92
92
|
)
|
93
93
|
|
94
|
-
conc_resp = (upper.variables - lower.variables) / (
|
95
|
-
|
94
|
+
conc_resp = (upper.variables.iloc[-1] - lower.variables.iloc[-1]) / (
|
95
|
+
2 * displacement * old
|
96
|
+
)
|
97
|
+
flux_resp = (upper.fluxes.iloc[-1] - lower.fluxes.iloc[-1]) / (
|
98
|
+
2 * displacement * old
|
99
|
+
)
|
96
100
|
# Reset
|
97
101
|
model.update_parameters({parameter: old})
|
98
102
|
if normalized:
|
@@ -102,8 +106,8 @@ def _response_coefficient_worker(
|
|
102
106
|
integrator=integrator,
|
103
107
|
y0=None,
|
104
108
|
)
|
105
|
-
conc_resp *= old / norm.variables
|
106
|
-
flux_resp *= old / norm.fluxes
|
109
|
+
conc_resp *= old / norm.variables.iloc[-1]
|
110
|
+
flux_resp *= old / norm.fluxes.iloc[-1]
|
107
111
|
return conc_resp, flux_resp
|
108
112
|
|
109
113
|
|
mxlpy/meta/__init__.py
CHANGED
@@ -3,13 +3,18 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
from .codegen_latex import generate_latex_code, to_tex_export
|
6
|
-
from .codegen_model import
|
6
|
+
from .codegen_model import (
|
7
|
+
generate_model_code_py,
|
8
|
+
generate_model_code_rs,
|
9
|
+
generate_model_code_ts,
|
10
|
+
)
|
7
11
|
from .codegen_mxlpy import generate_mxlpy_code
|
8
12
|
|
9
13
|
__all__ = [
|
10
14
|
"generate_latex_code",
|
11
15
|
"generate_model_code_py",
|
12
16
|
"generate_model_code_rs",
|
17
|
+
"generate_model_code_ts",
|
13
18
|
"generate_mxlpy_code",
|
14
19
|
"to_tex_export",
|
15
20
|
]
|
mxlpy/meta/codegen_latex.py
CHANGED
@@ -6,6 +6,7 @@ from dataclasses import dataclass
|
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
8
|
import sympy
|
9
|
+
from wadler_lindig import pformat
|
9
10
|
|
10
11
|
from mxlpy.meta.sympy_tools import fn_to_sympy, list_of_symbols
|
11
12
|
from mxlpy.types import Derived, RateFn
|
@@ -358,6 +359,10 @@ class TexReaction:
|
|
358
359
|
fn: RateFn
|
359
360
|
args: list[str]
|
360
361
|
|
362
|
+
def __repr__(self) -> str:
|
363
|
+
"""Return default representation."""
|
364
|
+
return pformat(self)
|
365
|
+
|
361
366
|
|
362
367
|
@dataclass
|
363
368
|
class TexExport:
|
@@ -397,6 +402,10 @@ class TexExport:
|
|
397
402
|
reactions: dict[str, TexReaction]
|
398
403
|
diff_eqs: dict[str, Mapping[str, float | Derived]]
|
399
404
|
|
405
|
+
def __repr__(self) -> str:
|
406
|
+
"""Return default representation."""
|
407
|
+
return pformat(self)
|
408
|
+
|
400
409
|
@staticmethod
|
401
410
|
def _diff_parameters(
|
402
411
|
p1: dict[str, float],
|
mxlpy/meta/codegen_model.py
CHANGED
@@ -9,6 +9,7 @@ from mxlpy.meta.sympy_tools import (
|
|
9
9
|
fn_to_sympy,
|
10
10
|
list_of_symbols,
|
11
11
|
stoichiometries_to_sympy,
|
12
|
+
sympy_to_inline_js,
|
12
13
|
sympy_to_inline_py,
|
13
14
|
sympy_to_inline_rust,
|
14
15
|
)
|
@@ -23,6 +24,7 @@ if TYPE_CHECKING:
|
|
23
24
|
__all__ = [
|
24
25
|
"generate_model_code_py",
|
25
26
|
"generate_model_code_rs",
|
27
|
+
"generate_model_code_ts",
|
26
28
|
]
|
27
29
|
|
28
30
|
_LOGGER = logging.getLogger(__name__)
|
@@ -37,6 +39,7 @@ def _generate_model_code(
|
|
37
39
|
assignment_template: str,
|
38
40
|
sympy_inline_fn: Callable[[sympy.Expr], str],
|
39
41
|
return_template: str,
|
42
|
+
custom_fns: dict[str, sympy.Expr],
|
40
43
|
imports: list[str] | None = None,
|
41
44
|
end: str | None = None,
|
42
45
|
free_parameters: list[str] | None = None,
|
@@ -70,11 +73,13 @@ def _generate_model_code(
|
|
70
73
|
|
71
74
|
# Derived
|
72
75
|
for name, derived in model.get_raw_derived().items():
|
73
|
-
expr =
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
expr = custom_fns.get(name)
|
77
|
+
if expr is None:
|
78
|
+
expr = fn_to_sympy(
|
79
|
+
derived.fn,
|
80
|
+
origin=name,
|
81
|
+
model_args=list_of_symbols(derived.args),
|
82
|
+
)
|
78
83
|
if expr is None:
|
79
84
|
msg = f"Unable to parse fn for derived value '{name}'"
|
80
85
|
raise ValueError(msg)
|
@@ -82,11 +87,16 @@ def _generate_model_code(
|
|
82
87
|
|
83
88
|
# Reactions
|
84
89
|
for name, rxn in model.get_raw_reactions().items():
|
85
|
-
expr =
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
+
expr = custom_fns.get(name)
|
91
|
+
if expr is None:
|
92
|
+
try:
|
93
|
+
expr = fn_to_sympy(
|
94
|
+
rxn.fn,
|
95
|
+
origin=name,
|
96
|
+
model_args=list_of_symbols(rxn.args),
|
97
|
+
)
|
98
|
+
except KeyError:
|
99
|
+
_LOGGER.warning("Failed to parse %s", name)
|
90
100
|
if expr is None:
|
91
101
|
msg = f"Unable to parse fn for reaction value '{name}'"
|
92
102
|
raise ValueError(msg)
|
@@ -110,7 +120,8 @@ def _generate_model_code(
|
|
110
120
|
_LOGGER.warning(msg)
|
111
121
|
|
112
122
|
# Return
|
113
|
-
|
123
|
+
ret_order = [i for i in variables if i in diff_eqs]
|
124
|
+
ret = ", ".join(f"d{i}dt" for i in ret_order) if len(diff_eqs) > 0 else "()"
|
114
125
|
source.append(return_template.format(ret))
|
115
126
|
|
116
127
|
if end is not None:
|
@@ -122,6 +133,7 @@ def _generate_model_code(
|
|
122
133
|
|
123
134
|
def generate_model_code_py(
|
124
135
|
model: Model,
|
136
|
+
custom_fns: dict[str, sympy.Expr] | None = None,
|
125
137
|
free_parameters: list[str] | None = None,
|
126
138
|
) -> str:
|
127
139
|
"""Transform the model into a python function, inlining the function calls."""
|
@@ -136,21 +148,51 @@ def generate_model_code_py(
|
|
136
148
|
return _generate_model_code(
|
137
149
|
model,
|
138
150
|
imports=[
|
151
|
+
"import math\n",
|
139
152
|
"from collections.abc import Iterable\n",
|
140
153
|
],
|
141
154
|
sized=False,
|
142
155
|
model_fn=model_fn,
|
143
156
|
variables_template=" {} = variables",
|
144
|
-
assignment_template=" {k} = {v}",
|
157
|
+
assignment_template=" {k}: float = {v}",
|
145
158
|
sympy_inline_fn=sympy_to_inline_py,
|
146
159
|
return_template=" return {}",
|
147
160
|
end=None,
|
148
161
|
free_parameters=free_parameters,
|
162
|
+
custom_fns={} if custom_fns is None else custom_fns,
|
163
|
+
)
|
164
|
+
|
165
|
+
|
166
|
+
def generate_model_code_ts(
|
167
|
+
model: Model,
|
168
|
+
custom_fns: dict[str, sympy.Expr] | None = None,
|
169
|
+
free_parameters: list[str] | None = None,
|
170
|
+
) -> str:
|
171
|
+
"""Transform the model into a typescript function, inlining the function calls."""
|
172
|
+
if free_parameters is None:
|
173
|
+
model_fn = "function model(time: number, variables: number[]) {"
|
174
|
+
else:
|
175
|
+
args = ", ".join(f"{k}: number" for k in free_parameters)
|
176
|
+
model_fn = f"function model(time: number, variables: number[], {args}) {{"
|
177
|
+
|
178
|
+
return _generate_model_code(
|
179
|
+
model,
|
180
|
+
imports=[],
|
181
|
+
sized=False,
|
182
|
+
model_fn=model_fn,
|
183
|
+
variables_template=" let [{}] = variables;",
|
184
|
+
assignment_template=" let {k}: number = {v};",
|
185
|
+
sympy_inline_fn=sympy_to_inline_js,
|
186
|
+
return_template=" return [{}];",
|
187
|
+
end="};",
|
188
|
+
free_parameters=free_parameters,
|
189
|
+
custom_fns={} if custom_fns is None else custom_fns,
|
149
190
|
)
|
150
191
|
|
151
192
|
|
152
193
|
def generate_model_code_rs(
|
153
194
|
model: Model,
|
195
|
+
custom_fns: dict[str, sympy.Expr] | None = None,
|
154
196
|
free_parameters: list[str] | None = None,
|
155
197
|
) -> str:
|
156
198
|
"""Transform the model into a rust function, inlining the function calls."""
|
@@ -171,4 +213,5 @@ def generate_model_code_rs(
|
|
171
213
|
return_template=" return [{}]",
|
172
214
|
end="}",
|
173
215
|
free_parameters=free_parameters,
|
216
|
+
custom_fns={} if custom_fns is None else custom_fns,
|
174
217
|
)
|