mxlpy 0.8.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 +165 -0
- mxlpy/distributions.py +339 -0
- mxlpy/experimental/__init__.py +12 -0
- mxlpy/experimental/diff.py +226 -0
- mxlpy/fit.py +291 -0
- mxlpy/fns.py +191 -0
- mxlpy/integrators/__init__.py +19 -0
- mxlpy/integrators/int_assimulo.py +146 -0
- mxlpy/integrators/int_scipy.py +146 -0
- mxlpy/label_map.py +610 -0
- mxlpy/linear_label_map.py +303 -0
- mxlpy/mc.py +548 -0
- mxlpy/mca.py +280 -0
- mxlpy/meta/__init__.py +11 -0
- mxlpy/meta/codegen_latex.py +516 -0
- mxlpy/meta/codegen_modebase.py +110 -0
- mxlpy/meta/codegen_py.py +107 -0
- mxlpy/meta/source_tools.py +320 -0
- mxlpy/model.py +1737 -0
- mxlpy/nn/__init__.py +10 -0
- mxlpy/nn/_tensorflow.py +0 -0
- mxlpy/nn/_torch.py +129 -0
- mxlpy/npe.py +277 -0
- mxlpy/parallel.py +171 -0
- mxlpy/parameterise.py +27 -0
- mxlpy/paths.py +36 -0
- mxlpy/plot.py +875 -0
- mxlpy/py.typed +0 -0
- mxlpy/sbml/__init__.py +14 -0
- mxlpy/sbml/_data.py +77 -0
- mxlpy/sbml/_export.py +644 -0
- mxlpy/sbml/_import.py +599 -0
- mxlpy/sbml/_mathml.py +691 -0
- mxlpy/sbml/_name_conversion.py +52 -0
- mxlpy/sbml/_unit_conversion.py +74 -0
- mxlpy/scan.py +629 -0
- mxlpy/simulator.py +655 -0
- mxlpy/surrogates/__init__.py +31 -0
- mxlpy/surrogates/_poly.py +97 -0
- mxlpy/surrogates/_torch.py +196 -0
- mxlpy/symbolic/__init__.py +10 -0
- mxlpy/symbolic/strikepy.py +582 -0
- mxlpy/symbolic/symbolic_model.py +75 -0
- mxlpy/types.py +474 -0
- mxlpy-0.8.0.dist-info/METADATA +106 -0
- mxlpy-0.8.0.dist-info/RECORD +48 -0
- mxlpy-0.8.0.dist-info/WHEEL +4 -0
- mxlpy-0.8.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
"""Integrator Package.
|
2
|
+
|
3
|
+
This package provides integrators for solving ordinary differential equations (ODEs).
|
4
|
+
It includes support for both Assimulo and Scipy integrators, with Assimulo being the default if available.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from __future__ import annotations
|
8
|
+
|
9
|
+
__all__ = ["DefaultIntegrator"]
|
10
|
+
|
11
|
+
|
12
|
+
from .int_scipy import Scipy
|
13
|
+
|
14
|
+
try:
|
15
|
+
from .int_assimulo import Assimulo
|
16
|
+
|
17
|
+
DefaultIntegrator = Assimulo
|
18
|
+
except ImportError:
|
19
|
+
DefaultIntegrator = Scipy
|
@@ -0,0 +1,146 @@
|
|
1
|
+
"""Assimulo integrator for solving ODEs."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"Assimulo",
|
9
|
+
]
|
10
|
+
|
11
|
+
import contextlib
|
12
|
+
import os
|
13
|
+
from typing import TYPE_CHECKING, Literal
|
14
|
+
|
15
|
+
import numpy as np
|
16
|
+
|
17
|
+
with contextlib.redirect_stderr(open(os.devnull, "w")): # noqa: PTH123
|
18
|
+
from assimulo.problem import Explicit_Problem # type: ignore
|
19
|
+
from assimulo.solvers import CVode # type: ignore
|
20
|
+
from assimulo.solvers.sundials import CVodeError # type: ignore
|
21
|
+
|
22
|
+
if TYPE_CHECKING:
|
23
|
+
from collections.abc import Callable
|
24
|
+
|
25
|
+
from mxlpy.types import Array, ArrayLike
|
26
|
+
|
27
|
+
|
28
|
+
@dataclass
|
29
|
+
class Assimulo:
|
30
|
+
"""Assimulo integrator for solving ODEs.
|
31
|
+
|
32
|
+
Attributes:
|
33
|
+
rhs: Right-hand side function of the ODE.
|
34
|
+
y0: Initial conditions.
|
35
|
+
atol: Absolute tolerance for the solver.
|
36
|
+
rtol: Relative tolerance for the solver.
|
37
|
+
maxnef: Maximum number of error failures.
|
38
|
+
maxncf: Maximum number of convergence failures.
|
39
|
+
verbosity: Verbosity level of the solver.
|
40
|
+
|
41
|
+
Methods:
|
42
|
+
integrate: Integrate the ODE system.
|
43
|
+
|
44
|
+
"""
|
45
|
+
|
46
|
+
rhs: Callable
|
47
|
+
y0: ArrayLike
|
48
|
+
atol: float = 1e-8
|
49
|
+
rtol: float = 1e-8
|
50
|
+
maxnef: int = 4 # max error failures
|
51
|
+
maxncf: int = 1 # max convergence failures
|
52
|
+
verbosity: Literal[50, 40, 30, 20, 10] = 50
|
53
|
+
|
54
|
+
def __post_init__(self) -> None:
|
55
|
+
"""Post-initialization method for setting up the CVode integrator with the provided parameters.
|
56
|
+
|
57
|
+
This method initializes the CVode integrator with an explicit problem defined by the
|
58
|
+
right-hand side function (`self.rhs`) and the initial conditions (`self.y0`). It also
|
59
|
+
sets various integrator options such as absolute tolerance (`self.atol`), relative
|
60
|
+
tolerance (`self.rtol`), maximum number of error test failures (`self.maxnef`), maximum
|
61
|
+
number of convergence failures (`self.maxncf`), and verbosity level (`self.verbosity`).
|
62
|
+
|
63
|
+
"""
|
64
|
+
self.integrator = CVode(Explicit_Problem(self.rhs, self.y0))
|
65
|
+
self.integrator.atol = self.atol
|
66
|
+
self.integrator.rtol = self.rtol
|
67
|
+
self.integrator.maxnef = self.maxnef
|
68
|
+
self.integrator.maxncf = self.maxncf
|
69
|
+
self.integrator.verbosity = self.verbosity
|
70
|
+
|
71
|
+
def reset(self) -> None:
|
72
|
+
"""Reset the integrator."""
|
73
|
+
self.integrator.reset()
|
74
|
+
|
75
|
+
def integrate(
|
76
|
+
self,
|
77
|
+
*,
|
78
|
+
t_end: float,
|
79
|
+
steps: int | None = None,
|
80
|
+
) -> tuple[Array | None, ArrayLike | None]:
|
81
|
+
"""Integrate the ODE system.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
t_end: Terminal time point for the integration.
|
85
|
+
steps: Number of steps for the integration.
|
86
|
+
time_points: Time points for the integration.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
np.ndarray: Array of integrated values.
|
90
|
+
|
91
|
+
"""
|
92
|
+
if steps is None:
|
93
|
+
steps = 0
|
94
|
+
try:
|
95
|
+
return self.integrator.simulate(t_end, steps) # type: ignore
|
96
|
+
except CVodeError:
|
97
|
+
return None, None
|
98
|
+
|
99
|
+
def integrate_time_course(
|
100
|
+
self,
|
101
|
+
*,
|
102
|
+
time_points: ArrayLike,
|
103
|
+
) -> tuple[Array | None, ArrayLike | None]:
|
104
|
+
"""Integrate the ODE system over a time course.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
time_points: Time points for the integration.
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
tuple[ArrayLike | None, ArrayLike | None]: Tuple containing the time points and the integrated values.
|
111
|
+
|
112
|
+
"""
|
113
|
+
try:
|
114
|
+
return self.integrator.simulate(time_points[-1], 0, time_points) # type: ignore
|
115
|
+
except CVodeError:
|
116
|
+
return None, None
|
117
|
+
|
118
|
+
def integrate_to_steady_state(
|
119
|
+
self,
|
120
|
+
*,
|
121
|
+
tolerance: float,
|
122
|
+
rel_norm: bool,
|
123
|
+
t_max: float = 1_000_000_000,
|
124
|
+
) -> tuple[float | None, ArrayLike | None]:
|
125
|
+
"""Integrate the ODE system to steady state.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
tolerance: Tolerance for determining steady state.
|
129
|
+
rel_norm: Whether to use relative normalization.
|
130
|
+
t_max: Maximum time point for the integration (default: 1,000,000,000).
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
tuple[float | None, ArrayLike | None]: Tuple containing the final time point and the integrated values at steady state.
|
134
|
+
|
135
|
+
"""
|
136
|
+
self.reset()
|
137
|
+
|
138
|
+
try:
|
139
|
+
for t_end in np.geomspace(1000, t_max, 3):
|
140
|
+
t, y = self.integrator.simulate(t_end)
|
141
|
+
diff = (y[-1] - y[-2]) / y[-1] if rel_norm else y[-1] - y[-2]
|
142
|
+
if np.linalg.norm(diff, ord=2) < tolerance:
|
143
|
+
return t[-1], y[-1]
|
144
|
+
except CVodeError:
|
145
|
+
return None, None
|
146
|
+
return None, None
|
@@ -0,0 +1,146 @@
|
|
1
|
+
"""Scipy integrator for solving ODEs."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from dataclasses import dataclass, field
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"Scipy",
|
9
|
+
]
|
10
|
+
|
11
|
+
import copy
|
12
|
+
from typing import TYPE_CHECKING, cast
|
13
|
+
|
14
|
+
import numpy as np
|
15
|
+
import scipy.integrate as spi
|
16
|
+
|
17
|
+
from mxlpy.types import Array, ArrayLike
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from collections.abc import Callable
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class Scipy:
|
25
|
+
"""Scipy integrator for solving ODEs.
|
26
|
+
|
27
|
+
Attributes:
|
28
|
+
rhs: Right-hand side function of the ODE.
|
29
|
+
y0: Initial conditions.
|
30
|
+
atol: Absolute tolerance for the solver.
|
31
|
+
rtol: Relative tolerance for the solver.
|
32
|
+
t0: Initial time point.
|
33
|
+
_y0_orig: Original initial conditions.
|
34
|
+
|
35
|
+
Methods:
|
36
|
+
__post_init__: Initialize the Scipy integrator.
|
37
|
+
reset: Reset the integrator.
|
38
|
+
integrate: Integrate the ODE system.
|
39
|
+
integrate_to_steady_state: Integrate the ODE system to steady state.
|
40
|
+
|
41
|
+
"""
|
42
|
+
|
43
|
+
rhs: Callable
|
44
|
+
y0: ArrayLike
|
45
|
+
atol: float = 1e-8
|
46
|
+
rtol: float = 1e-8
|
47
|
+
t0: float = 0.0
|
48
|
+
_y0_orig: ArrayLike = field(default_factory=list)
|
49
|
+
|
50
|
+
def __post_init__(self) -> None:
|
51
|
+
"""Create copy of initial state.
|
52
|
+
|
53
|
+
This method creates a copy of the initial state `y0` and stores it in the `_y0_orig` attribute.
|
54
|
+
This is useful for preserving the original initial state for future reference or reset operations.
|
55
|
+
|
56
|
+
"""
|
57
|
+
self._y0_orig = self.y0.copy()
|
58
|
+
|
59
|
+
def reset(self) -> None:
|
60
|
+
"""Reset the integrator."""
|
61
|
+
self.t0 = 0
|
62
|
+
self.y0 = self._y0_orig.copy()
|
63
|
+
|
64
|
+
def integrate(
|
65
|
+
self,
|
66
|
+
*,
|
67
|
+
t_end: float,
|
68
|
+
steps: int | None = None,
|
69
|
+
) -> tuple[Array | None, ArrayLike | None]:
|
70
|
+
"""Integrate the ODE system.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
t_end: Terminal time point for the integration.
|
74
|
+
steps: Number of steps for the integration.
|
75
|
+
time_points: Array of time points for the integration.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
tuple[ArrayLike | None, ArrayLike | None]: Tuple containing the time points and the integrated values.
|
79
|
+
|
80
|
+
"""
|
81
|
+
# Scipy counts the total amount of return points rather than steps as assimulo
|
82
|
+
steps = 100 if steps is None else steps + 1
|
83
|
+
|
84
|
+
return self.integrate_time_course(
|
85
|
+
time_points=np.linspace(self.t0, t_end, steps)
|
86
|
+
)
|
87
|
+
|
88
|
+
def integrate_time_course(
|
89
|
+
self, *, time_points: ArrayLike
|
90
|
+
) -> tuple[Array | None, ArrayLike | None]:
|
91
|
+
"""Integrate the ODE system over a time course.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
time_points: Time points for the integration.
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
tuple[ArrayLike, ArrayLike]: Tuple containing the time points and the integrated values.
|
98
|
+
|
99
|
+
"""
|
100
|
+
y = spi.odeint(
|
101
|
+
func=self.rhs,
|
102
|
+
y0=self.y0,
|
103
|
+
t=time_points,
|
104
|
+
tfirst=True,
|
105
|
+
atol=self.atol,
|
106
|
+
rtol=self.rtol,
|
107
|
+
)
|
108
|
+
self.t0 = time_points[-1]
|
109
|
+
self.y0 = y[-1, :]
|
110
|
+
return np.array(time_points, dtype=float), y
|
111
|
+
|
112
|
+
def integrate_to_steady_state(
|
113
|
+
self,
|
114
|
+
*,
|
115
|
+
tolerance: float,
|
116
|
+
rel_norm: bool,
|
117
|
+
step_size: int = 100,
|
118
|
+
max_steps: int = 1000,
|
119
|
+
) -> tuple[float | None, ArrayLike | None]:
|
120
|
+
"""Integrate the ODE system to steady state.
|
121
|
+
|
122
|
+
Args:
|
123
|
+
tolerance: Tolerance for determining steady state.
|
124
|
+
rel_norm: Whether to use relative normalization.
|
125
|
+
step_size: Step size for the integration (default: 100).
|
126
|
+
max_steps: Maximum number of steps for the integration (default: 1,000).
|
127
|
+
integrator: Name of the integrator to use (default: "lsoda").
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
tuple[float | None, ArrayLike | None]: Tuple containing the final time point and the integrated values at steady state.
|
131
|
+
|
132
|
+
"""
|
133
|
+
self.reset()
|
134
|
+
integ = spi.ode(self.rhs)
|
135
|
+
integ.set_integrator(name="lsoda")
|
136
|
+
integ.set_initial_value(self.y0)
|
137
|
+
t = self.t0 + step_size
|
138
|
+
y1 = copy.deepcopy(self.y0)
|
139
|
+
for _ in range(max_steps):
|
140
|
+
y2 = integ.integrate(t)
|
141
|
+
diff = (y2 - y1) / y1 if rel_norm else y2 - y1
|
142
|
+
if np.linalg.norm(diff, ord=2) < tolerance:
|
143
|
+
return t, cast(ArrayLike, y2)
|
144
|
+
y1 = y2
|
145
|
+
t += step_size
|
146
|
+
return None, None
|