mxlpy 0.23.0__py3-none-any.whl → 0.25.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 +6 -6
- mxlpy/carousel.py +46 -1
- mxlpy/compare.py +13 -0
- mxlpy/experimental/diff.py +14 -0
- mxlpy/fit/__init__.py +9 -0
- mxlpy/fit/common.py +298 -0
- mxlpy/fit/global_.py +534 -0
- mxlpy/{fit.py → fit/local_.py} +105 -308
- mxlpy/identify.py +5 -4
- mxlpy/mc.py +63 -2
- mxlpy/meta/__init__.py +6 -1
- mxlpy/meta/codegen_latex.py +9 -0
- mxlpy/meta/codegen_model.py +53 -11
- mxlpy/meta/codegen_mxlpy.py +21 -0
- mxlpy/meta/source_tools.py +5 -0
- mxlpy/meta/sympy_tools.py +7 -1
- mxlpy/model.py +56 -11
- mxlpy/plot.py +44 -15
- mxlpy/sbml/_data.py +34 -0
- mxlpy/sbml/_export.py +4 -3
- mxlpy/scan.py +125 -7
- mxlpy/simulator.py +5 -0
- mxlpy/types.py +244 -2
- {mxlpy-0.23.0.dist-info → mxlpy-0.25.0.dist-info}/METADATA +12 -2
- {mxlpy-0.23.0.dist-info → mxlpy-0.25.0.dist-info}/RECORD +27 -24
- {mxlpy-0.23.0.dist-info → mxlpy-0.25.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.23.0.dist-info → mxlpy-0.25.0.dist-info}/licenses/LICENSE +0 -0
mxlpy/sbml/_data.py
CHANGED
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import TYPE_CHECKING
|
5
5
|
|
6
|
+
from wadler_lindig import pformat
|
7
|
+
|
6
8
|
if TYPE_CHECKING:
|
7
9
|
from collections.abc import Mapping
|
8
10
|
|
@@ -26,18 +28,30 @@ class AtomicUnit:
|
|
26
28
|
scale: int
|
27
29
|
multiplier: float
|
28
30
|
|
31
|
+
def __repr__(self) -> str:
|
32
|
+
"""Return default representation."""
|
33
|
+
return pformat(self)
|
34
|
+
|
29
35
|
|
30
36
|
@dataclass
|
31
37
|
class CompositeUnit:
|
32
38
|
sbml_id: str
|
33
39
|
units: list
|
34
40
|
|
41
|
+
def __repr__(self) -> str:
|
42
|
+
"""Return default representation."""
|
43
|
+
return pformat(self)
|
44
|
+
|
35
45
|
|
36
46
|
@dataclass
|
37
47
|
class Parameter:
|
38
48
|
value: float
|
39
49
|
is_constant: bool
|
40
50
|
|
51
|
+
def __repr__(self) -> str:
|
52
|
+
"""Return default representation."""
|
53
|
+
return pformat(self)
|
54
|
+
|
41
55
|
|
42
56
|
@dataclass
|
43
57
|
class Compartment:
|
@@ -47,6 +61,10 @@ class Compartment:
|
|
47
61
|
units: str
|
48
62
|
is_constant: bool
|
49
63
|
|
64
|
+
def __repr__(self) -> str:
|
65
|
+
"""Return default representation."""
|
66
|
+
return pformat(self)
|
67
|
+
|
50
68
|
|
51
69
|
@dataclass
|
52
70
|
class Compound:
|
@@ -58,21 +76,37 @@ class Compound:
|
|
58
76
|
is_constant: bool
|
59
77
|
is_concentration: bool
|
60
78
|
|
79
|
+
def __repr__(self) -> str:
|
80
|
+
"""Return default representation."""
|
81
|
+
return pformat(self)
|
82
|
+
|
61
83
|
|
62
84
|
@dataclass
|
63
85
|
class Derived:
|
64
86
|
body: str
|
65
87
|
args: list[str]
|
66
88
|
|
89
|
+
def __repr__(self) -> str:
|
90
|
+
"""Return default representation."""
|
91
|
+
return pformat(self)
|
92
|
+
|
67
93
|
|
68
94
|
@dataclass
|
69
95
|
class Function:
|
70
96
|
body: str
|
71
97
|
args: list[str]
|
72
98
|
|
99
|
+
def __repr__(self) -> str:
|
100
|
+
"""Return default representation."""
|
101
|
+
return pformat(self)
|
102
|
+
|
73
103
|
|
74
104
|
@dataclass
|
75
105
|
class Reaction:
|
76
106
|
body: str
|
77
107
|
stoichiometry: Mapping[str, float | str]
|
78
108
|
args: list[str]
|
109
|
+
|
110
|
+
def __repr__(self) -> str:
|
111
|
+
"""Return default representation."""
|
112
|
+
return pformat(self)
|
mxlpy/sbml/_export.py
CHANGED
@@ -447,6 +447,7 @@ def _create_sbml_variables(
|
|
447
447
|
cpd.setConstant(False)
|
448
448
|
cpd.setBoundaryCondition(False)
|
449
449
|
cpd.setHasOnlySubstanceUnits(False)
|
450
|
+
cpd.setCompartment("compartment")
|
450
451
|
# cpd.setUnit() # FIXME: implement
|
451
452
|
if isinstance((init := variable.initial_value), InitialAssignment):
|
452
453
|
ar = sbml_model.createInitialAssignment()
|
@@ -455,7 +456,7 @@ def _create_sbml_variables(
|
|
455
456
|
ar.setVariable(_convert_id_to_sbml(id_=name, prefix="IA"))
|
456
457
|
ar.setMath(_sbmlify_fn(init.fn, init.args))
|
457
458
|
else:
|
458
|
-
cpd.
|
459
|
+
cpd.setInitialConcentration(float(init))
|
459
460
|
|
460
461
|
|
461
462
|
def _create_sbml_derived_variables(*, model: Model, sbml_model: libsbml.Model) -> None:
|
@@ -591,8 +592,8 @@ def _default_compartments(
|
|
591
592
|
) -> dict[str, Compartment]:
|
592
593
|
if compartments is None:
|
593
594
|
return {
|
594
|
-
"
|
595
|
-
name="
|
595
|
+
"compartment": Compartment(
|
596
|
+
name="compartment",
|
596
597
|
dimensions=3,
|
597
598
|
size=1,
|
598
599
|
units="litre",
|
mxlpy/scan.py
CHANGED
@@ -39,12 +39,14 @@ if TYPE_CHECKING:
|
|
39
39
|
|
40
40
|
|
41
41
|
__all__ = [
|
42
|
+
"ProtocolTimeCourseWorker",
|
42
43
|
"ProtocolWorker",
|
43
44
|
"SteadyStateWorker",
|
44
45
|
"TimeCourseWorker",
|
46
|
+
"protocol",
|
47
|
+
"protocol_time_course",
|
45
48
|
"steady_state",
|
46
49
|
"time_course",
|
47
|
-
"time_course_over_protocol",
|
48
50
|
]
|
49
51
|
|
50
52
|
|
@@ -70,11 +72,6 @@ def _update_parameters_and_initial_conditions[T](
|
|
70
72
|
return fn(model)
|
71
73
|
|
72
74
|
|
73
|
-
###############################################################################
|
74
|
-
# Single returns
|
75
|
-
###############################################################################
|
76
|
-
|
77
|
-
|
78
75
|
###############################################################################
|
79
76
|
# Workers
|
80
77
|
###############################################################################
|
@@ -126,6 +123,22 @@ class ProtocolWorker(Protocol):
|
|
126
123
|
...
|
127
124
|
|
128
125
|
|
126
|
+
class ProtocolTimeCourseWorker(Protocol):
|
127
|
+
"""Worker function for protocol-based simulations."""
|
128
|
+
|
129
|
+
def __call__(
|
130
|
+
self,
|
131
|
+
model: Model,
|
132
|
+
protocol: pd.DataFrame,
|
133
|
+
time_points: Array,
|
134
|
+
*,
|
135
|
+
integrator: IntegratorType | None,
|
136
|
+
y0: dict[str, float] | None,
|
137
|
+
) -> Result:
|
138
|
+
"""Call the worker function."""
|
139
|
+
...
|
140
|
+
|
141
|
+
|
129
142
|
def _steady_state_worker(
|
130
143
|
model: Model,
|
131
144
|
*,
|
@@ -228,6 +241,42 @@ def _protocol_worker(
|
|
228
241
|
return Result.default(model=model, time_points=time_points) if res is None else res
|
229
242
|
|
230
243
|
|
244
|
+
def _protocol_time_course_worker(
|
245
|
+
model: Model,
|
246
|
+
protocol: pd.DataFrame,
|
247
|
+
time_points: Array,
|
248
|
+
*,
|
249
|
+
integrator: IntegratorType | None,
|
250
|
+
y0: dict[str, float] | None,
|
251
|
+
) -> Result:
|
252
|
+
"""Simulate the model over a protocol and return concentrations and fluxes.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
model: Model instance to simulate.
|
256
|
+
y0: Initial conditions as a dictionary {species: value}.
|
257
|
+
protocol: DataFrame containing the protocol steps.
|
258
|
+
time_points: Time points where to return the simulation
|
259
|
+
integrator: Integrator function to use for steady state calculation
|
260
|
+
|
261
|
+
Returns:
|
262
|
+
TimeCourse: Object containing protocol series concentrations and fluxes.
|
263
|
+
|
264
|
+
"""
|
265
|
+
try:
|
266
|
+
res = (
|
267
|
+
Simulator(model, integrator=integrator, y0=y0)
|
268
|
+
.simulate_protocol_time_course(
|
269
|
+
protocol=protocol,
|
270
|
+
time_points=time_points,
|
271
|
+
)
|
272
|
+
.get_result()
|
273
|
+
)
|
274
|
+
except ZeroDivisionError:
|
275
|
+
res = None
|
276
|
+
|
277
|
+
return Result.default(model=model, time_points=time_points) if res is None else res
|
278
|
+
|
279
|
+
|
231
280
|
def steady_state(
|
232
281
|
model: Model,
|
233
282
|
*,
|
@@ -393,7 +442,7 @@ def time_course(
|
|
393
442
|
)
|
394
443
|
|
395
444
|
|
396
|
-
def
|
445
|
+
def protocol(
|
397
446
|
model: Model,
|
398
447
|
*,
|
399
448
|
to_scan: pd.DataFrame,
|
@@ -460,3 +509,72 @@ def time_course_over_protocol(
|
|
460
509
|
protocol=protocol,
|
461
510
|
raw_results=dict(res),
|
462
511
|
)
|
512
|
+
|
513
|
+
|
514
|
+
def protocol_time_course(
|
515
|
+
model: Model,
|
516
|
+
*,
|
517
|
+
to_scan: pd.DataFrame,
|
518
|
+
protocol: pd.DataFrame,
|
519
|
+
time_points: Array,
|
520
|
+
y0: dict[str, float] | None = None,
|
521
|
+
parallel: bool = True,
|
522
|
+
cache: Cache | None = None,
|
523
|
+
worker: ProtocolTimeCourseWorker = _protocol_time_course_worker,
|
524
|
+
integrator: IntegratorType | None = None,
|
525
|
+
) -> ProtocolScan:
|
526
|
+
"""Get protocol series for each supplied parameter.
|
527
|
+
|
528
|
+
Examples:
|
529
|
+
>>> scan.time_course_over_protocol(
|
530
|
+
... model,
|
531
|
+
... parameters=pd.DataFrame({"k2": np.linspace(1, 2, 11)}),
|
532
|
+
... protocol=make_protocol(
|
533
|
+
... {
|
534
|
+
... 1: {"k1": 1},
|
535
|
+
... 2: {"k1": 2},
|
536
|
+
... }
|
537
|
+
... ),
|
538
|
+
... )
|
539
|
+
|
540
|
+
Args:
|
541
|
+
model: Model instance to simulate.
|
542
|
+
to_scan: DataFrame containing parameter or initial values to scan.
|
543
|
+
protocol: Protocol to follow for the simulation.
|
544
|
+
time_points: Time points where to return simulation results
|
545
|
+
y0: Initial conditions as a dictionary {variable: value}.
|
546
|
+
parallel: Whether to execute in parallel (default: True).
|
547
|
+
cache: Optional cache to store and retrieve results.
|
548
|
+
worker: Worker function to use for the simulation.
|
549
|
+
integrator: Integrator function to use for steady state calculation
|
550
|
+
|
551
|
+
Returns:
|
552
|
+
TimeCourseByPars: Protocol series results for each parameter set.
|
553
|
+
|
554
|
+
"""
|
555
|
+
# We update the initial conditions separately here, because `to_scan` might also
|
556
|
+
# contain initial conditions.
|
557
|
+
if y0 is not None:
|
558
|
+
model.update_variables(y0)
|
559
|
+
|
560
|
+
res = parallelise(
|
561
|
+
partial(
|
562
|
+
_update_parameters_and_initial_conditions,
|
563
|
+
fn=partial(
|
564
|
+
worker,
|
565
|
+
protocol=protocol,
|
566
|
+
time_points=time_points,
|
567
|
+
integrator=integrator,
|
568
|
+
y0=None,
|
569
|
+
),
|
570
|
+
model=model,
|
571
|
+
),
|
572
|
+
inputs=list(to_scan.iterrows()),
|
573
|
+
cache=cache,
|
574
|
+
parallel=parallel,
|
575
|
+
)
|
576
|
+
return ProtocolScan(
|
577
|
+
to_scan=to_scan,
|
578
|
+
protocol=protocol,
|
579
|
+
raw_results=dict(res),
|
580
|
+
)
|
mxlpy/simulator.py
CHANGED
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING, Self, cast
|
|
17
17
|
import numpy as np
|
18
18
|
import pandas as pd
|
19
19
|
from sympy import lambdify
|
20
|
+
from wadler_lindig import pformat
|
20
21
|
|
21
22
|
from mxlpy.integrators import DefaultIntegrator
|
22
23
|
from mxlpy.symbolic import to_symbolic_model
|
@@ -63,6 +64,10 @@ class Simulator:
|
|
63
64
|
_integrator_type: IntegratorType
|
64
65
|
_time_shift: float | None
|
65
66
|
|
67
|
+
def __repr__(self) -> str:
|
68
|
+
"""Return default representation."""
|
69
|
+
return pformat(self)
|
70
|
+
|
66
71
|
def __init__(
|
67
72
|
self,
|
68
73
|
model: Model,
|