modelbase2 0.1.79__py3-none-any.whl → 0.3.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.
Files changed (59) hide show
  1. modelbase2/__init__.py +148 -25
  2. modelbase2/distributions.py +336 -0
  3. modelbase2/experimental/__init__.py +17 -0
  4. modelbase2/experimental/codegen.py +239 -0
  5. modelbase2/experimental/diff.py +227 -0
  6. modelbase2/experimental/notes.md +4 -0
  7. modelbase2/experimental/tex.py +521 -0
  8. modelbase2/fit.py +284 -0
  9. modelbase2/fns.py +185 -0
  10. modelbase2/integrators/__init__.py +19 -0
  11. modelbase2/integrators/int_assimulo.py +146 -0
  12. modelbase2/integrators/int_scipy.py +147 -0
  13. modelbase2/label_map.py +610 -0
  14. modelbase2/linear_label_map.py +301 -0
  15. modelbase2/mc.py +548 -0
  16. modelbase2/mca.py +280 -0
  17. modelbase2/model.py +1621 -0
  18. modelbase2/nnarchitectures.py +128 -0
  19. modelbase2/npe.py +271 -0
  20. modelbase2/parallel.py +171 -0
  21. modelbase2/parameterise.py +28 -0
  22. modelbase2/paths.py +36 -0
  23. modelbase2/plot.py +832 -0
  24. modelbase2/sbml/__init__.py +14 -0
  25. modelbase2/sbml/_data.py +77 -0
  26. modelbase2/sbml/_export.py +656 -0
  27. modelbase2/sbml/_import.py +585 -0
  28. modelbase2/sbml/_mathml.py +691 -0
  29. modelbase2/sbml/_name_conversion.py +52 -0
  30. modelbase2/sbml/_unit_conversion.py +74 -0
  31. modelbase2/scan.py +616 -0
  32. modelbase2/scope.py +96 -0
  33. modelbase2/simulator.py +635 -0
  34. modelbase2/surrogates/__init__.py +31 -0
  35. modelbase2/surrogates/_poly.py +91 -0
  36. modelbase2/surrogates/_torch.py +191 -0
  37. modelbase2/surrogates.py +316 -0
  38. modelbase2/types.py +352 -11
  39. modelbase2-0.3.0.dist-info/METADATA +93 -0
  40. modelbase2-0.3.0.dist-info/RECORD +43 -0
  41. {modelbase2-0.1.79.dist-info → modelbase2-0.3.0.dist-info}/WHEEL +1 -1
  42. modelbase2/core/__init__.py +0 -29
  43. modelbase2/core/algebraic_module_container.py +0 -130
  44. modelbase2/core/constant_container.py +0 -113
  45. modelbase2/core/data.py +0 -109
  46. modelbase2/core/name_container.py +0 -29
  47. modelbase2/core/reaction_container.py +0 -115
  48. modelbase2/core/utils.py +0 -28
  49. modelbase2/core/variable_container.py +0 -24
  50. modelbase2/ode/__init__.py +0 -13
  51. modelbase2/ode/integrator.py +0 -80
  52. modelbase2/ode/mca.py +0 -270
  53. modelbase2/ode/model.py +0 -470
  54. modelbase2/ode/simulator.py +0 -153
  55. modelbase2/utils/__init__.py +0 -0
  56. modelbase2/utils/plotting.py +0 -372
  57. modelbase2-0.1.79.dist-info/METADATA +0 -44
  58. modelbase2-0.1.79.dist-info/RECORD +0 -22
  59. {modelbase2-0.1.79.dist-info → modelbase2-0.3.0.dist-info/licenses}/LICENSE +0 -0
modelbase2/__init__.py CHANGED
@@ -1,36 +1,159 @@
1
+ """modelbase2: A Python Package for Metabolic Modeling and Analysis.
2
+
3
+ This package provides tools for creating, simulating and analyzing metabolic models
4
+ with features including:
5
+
6
+ Key Features:
7
+ - Model creation and manipulation
8
+ - Steady state and time-series simulations
9
+ - Parameter fitting and optimization
10
+ - Monte Carlo analysis
11
+ - Metabolic Control Analysis (MCA)
12
+ - SBML import/export support
13
+ - Visualization tools
14
+
15
+ Core Components:
16
+ Model: Core class for metabolic model representation
17
+ Simulator: Handles model simulation and integration
18
+ DefaultIntegrator: Standard ODE solver implementation
19
+ LabelMapper: Maps between model components and labels
20
+ Cache: Performance optimization through result caching
21
+
22
+ Simulation Features:
23
+ - Steady state calculations
24
+ - Time course simulations
25
+ - Parameter scanning
26
+ - Surrogate modeling with PyTorch
27
+
28
+ Analysis Tools:
29
+ - Parameter fitting to experimental data
30
+ - Monte Carlo methods for uncertainty analysis
31
+ - Metabolic Control Analysis
32
+ - Custom visualization functions
33
+
34
+ """
35
+
1
36
  from __future__ import annotations
2
37
 
3
38
  __all__ = [
4
- "AlgebraicModule",
5
39
  "Assimulo",
6
- "Constant",
7
- "DerivedConstant",
8
- "DerivedStoichiometry",
40
+ "Cache",
41
+ "DefaultIntegrator",
42
+ "Derived",
43
+ "IntegratorProtocol",
44
+ "LabelMapper",
45
+ "LinearLabelMapper",
9
46
  "Model",
10
- "Reaction",
47
+ "Scipy",
11
48
  "Simulator",
12
- "Variable",
49
+ "cartesian_product",
50
+ "distributions",
51
+ "experimental",
52
+ "fit",
53
+ "make_protocol",
54
+ "mc",
13
55
  "mca",
56
+ "nnarchitectures",
57
+ "plot",
58
+ "sbml",
59
+ "steady_state",
60
+ "surrogates",
61
+ "time_course",
62
+ "time_course_over_protocol",
14
63
  ]
15
64
 
16
- import logging
17
- from .core.data import (
18
- AlgebraicModule,
19
- Constant,
20
- DerivedConstant,
21
- DerivedStoichiometry,
22
- Reaction,
23
- Variable,
65
+ import contextlib
66
+ import itertools as it
67
+ from typing import TYPE_CHECKING
68
+
69
+ import pandas as pd
70
+
71
+ if TYPE_CHECKING:
72
+ from modelbase2.types import ArrayLike
73
+
74
+ from . import (
75
+ distributions,
76
+ experimental,
77
+ fit,
78
+ mc,
79
+ mca,
80
+ nnarchitectures,
81
+ plot,
82
+ sbml,
83
+ surrogates,
24
84
  )
25
- from .ode import Assimulo, Model, Simulator, mca
26
-
27
- logger = logging.getLogger(__name__)
28
- logger.setLevel(logging.WARNING)
29
- formatter = logging.Formatter(
30
- fmt="{asctime} - {levelname} - {message}",
31
- datefmt="%Y-%m-%d %H:%M:%S",
32
- style="{",
85
+ from .integrators import DefaultIntegrator, Scipy
86
+ from .label_map import LabelMapper
87
+ from .linear_label_map import LinearLabelMapper
88
+ from .mc import Cache
89
+ from .model import Model
90
+ from .scan import (
91
+ steady_state,
92
+ time_course,
93
+ time_course_over_protocol,
33
94
  )
34
- handler = logging.StreamHandler()
35
- handler.setFormatter(formatter)
36
- logger.addHandler(handler)
95
+ from .simulator import Simulator
96
+ from .types import Derived, IntegratorProtocol
97
+
98
+ with contextlib.suppress(ImportError):
99
+ from .integrators import Assimulo
100
+
101
+
102
+ def cartesian_product(parameters: dict[str, ArrayLike]) -> pd.DataFrame:
103
+ """Generate a cartesian product of the parameter values.
104
+
105
+ Args:
106
+ parameters: Dictionary containing parameter names and values.
107
+
108
+ Returns:
109
+ pd.DataFrame: DataFrame containing the cartesian product of the parameter values.
110
+
111
+ """
112
+ return pd.DataFrame(
113
+ it.product(*parameters.values()),
114
+ columns=list(parameters),
115
+ )
116
+
117
+
118
+ def make_protocol(steps: list[tuple[float, dict[str, float]]]) -> pd.DataFrame:
119
+ """Create protocol DataFrame from a dictionary of steps.
120
+
121
+ Arguments:
122
+ steps: dictionary of steps, where each key is the duration of
123
+ the step in seconds and the value is a dictionary of all
124
+ parameter values during that step.
125
+
126
+ Examples:
127
+ >>> make_protocol([
128
+ ... (1, {"k1": 1.0}),
129
+ ... (2, {"k1": 2.0}),
130
+ ... (3, {"k1": 1.0}),
131
+ ... ])
132
+
133
+ | Timedelta | k1 |
134
+ |:----------------|-----:|
135
+ | 0 days 00:00:01 | 1.0 |
136
+ | 0 days 00:00:03 | 2.0 |
137
+ | 0 days 00:00:06 | 1.0 |
138
+
139
+ >>> make_protocol([
140
+ ... (1, {"k1": 1.0, "k2": 2.0}),
141
+ ... (2, {"k1": 2.0, "k2": 3.0}),
142
+ ... (3, {"k1": 1.0, "k2": 2.0}),
143
+ ... ])
144
+
145
+ | Timedelta | k1 | k2 |
146
+ |:----------------|-----:|-----:|
147
+ | 0 days 00:00:01 | 1 | 2 |
148
+ | 0 days 00:00:03 | 2 | 3 |
149
+ | 0 days 00:00:06 | 1 | 2 |
150
+
151
+ """
152
+ data = {}
153
+ t0 = pd.Timedelta(0)
154
+ for step, pars in steps:
155
+ t0 += pd.Timedelta(seconds=step)
156
+ data[t0] = pars
157
+ protocol = pd.DataFrame(data).T
158
+ protocol.index.name = "Timedelta"
159
+ return protocol
@@ -0,0 +1,336 @@
1
+ """Probability Distribution Classes for Parameter Sampling.
2
+
3
+ This module provides a collection of probability distributions used for parameter sampling
4
+ in metabolic modeling and Monte Carlo simulations.
5
+
6
+ Classes:
7
+ Distribution (Protocol): Base protocol for all distribution classes
8
+ Beta: Beta distribution for parameters bounded between 0 and 1
9
+ Uniform: Uniform distribution for parameters with simple bounds
10
+ LogUniform: LogUniform distribution for parameters with simple bounds
11
+ Normal: Normal (Gaussian) distribution for unbounded parameters
12
+ LogNormal: Log-normal distribution for strictly positive parameters
13
+ Skewnorm: Skewed normal distribution for asymmetric parameter distributions
14
+
15
+ Each distribution class provides:
16
+ - Consistent interface through the sample() method
17
+ - Optional random number generator (RNG) control
18
+ - Reproducible results via seed parameter
19
+
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from dataclasses import dataclass
25
+ from typing import Protocol, cast
26
+
27
+ import numpy as np
28
+ import pandas as pd
29
+ from scipy import stats
30
+
31
+ from modelbase2.plot import Axes, FigAx, _default_fig_ax
32
+ from modelbase2.types import Array
33
+
34
+ __all__ = [
35
+ "Beta",
36
+ "Distribution",
37
+ "GaussianKde",
38
+ "LogNormal",
39
+ "LogUniform",
40
+ "Normal",
41
+ "RNG",
42
+ "Skewnorm",
43
+ "Uniform",
44
+ "sample",
45
+ ]
46
+
47
+ RNG = np.random.default_rng(seed=42)
48
+
49
+
50
+ class Distribution(Protocol):
51
+ """Protocol defining interface for distribution classes.
52
+
53
+ All distribution classes must implement the sample() method.
54
+ """
55
+
56
+ def sample(
57
+ self,
58
+ num: int,
59
+ rng: np.random.Generator | None = None,
60
+ ) -> Array:
61
+ """Generate random samples from the distribution.
62
+
63
+ Args:
64
+ num: Number of samples to generate
65
+ rng: Random number generator
66
+
67
+ Returns:
68
+ Array of random samples
69
+
70
+ """
71
+ ...
72
+
73
+
74
+ @dataclass
75
+ class Beta:
76
+ """Beta distribution for parameters bounded between 0 and 1.
77
+
78
+ Args:
79
+ a: Alpha shape parameter (>0)
80
+ b: Beta shape parameter (>0)
81
+
82
+ """
83
+
84
+ a: float
85
+ b: float
86
+
87
+ def sample(self, num: int, rng: np.random.Generator | None = None) -> Array:
88
+ """Generate random samples from the beta distribution.
89
+
90
+ Args:
91
+ num: Number of samples to generate
92
+ rng: Random number generator
93
+
94
+ """
95
+ if rng is None:
96
+ rng = RNG
97
+ return rng.beta(self.a, self.b, num)
98
+
99
+
100
+ @dataclass
101
+ class Uniform:
102
+ """Uniform distribution for parameters with simple bounds.
103
+
104
+ Args:
105
+ lower_bound: Minimum value
106
+ upper_bound: Maximum value
107
+
108
+ """
109
+
110
+ lower_bound: float
111
+ upper_bound: float
112
+
113
+ def sample(self, num: int, rng: np.random.Generator | None = None) -> Array:
114
+ """Generate random samples from the uniform distribution.
115
+
116
+ Args:
117
+ num: Number of samples to generate
118
+ rng: Random number generator
119
+
120
+ """
121
+ if rng is None:
122
+ rng = RNG
123
+ return rng.uniform(self.lower_bound, self.upper_bound, num)
124
+
125
+
126
+ @dataclass
127
+ class LogUniform:
128
+ """LogUniform distribution for parameters with simple bounds.
129
+
130
+ Args:
131
+ lower_bound: Minimum value
132
+ upper_bound: Maximum value
133
+
134
+ """
135
+
136
+ lower_bound: float
137
+ upper_bound: float
138
+
139
+ def sample(self, num: int, rng: np.random.Generator | None = None) -> Array:
140
+ """Generate random samples from the loguniform distribution.
141
+
142
+ Args:
143
+ num: Number of samples to generate
144
+ rng: Random number generator
145
+
146
+ """
147
+ if rng is None:
148
+ rng = RNG
149
+ return stats.loguniform.rvs(
150
+ self.lower_bound, self.upper_bound, size=num, random_state=rng
151
+ )
152
+
153
+
154
+ @dataclass
155
+ class Normal:
156
+ """Normal (Gaussian) distribution for unbounded parameters.
157
+
158
+ Args:
159
+ loc: Mean of the distribution
160
+ scale: Standard deviation
161
+
162
+ """
163
+
164
+ loc: float
165
+ scale: float
166
+
167
+ def sample(self, num: int, rng: np.random.Generator | None = None) -> Array:
168
+ """Generate random samples from the normal distribution.
169
+
170
+ Args:
171
+ num: Number of samples to generate
172
+ rng: Random number generator
173
+
174
+ """
175
+ if rng is None:
176
+ rng = RNG
177
+ return rng.normal(self.loc, self.scale, num)
178
+
179
+
180
+ @dataclass
181
+ class LogNormal:
182
+ """Log-normal distribution for strictly positive parameters.
183
+
184
+ Args:
185
+ mean: Mean of the underlying normal distribution
186
+ sigma: Standard deviation of the underlying normal distribution
187
+ seed: Random seed for reproducibility
188
+
189
+ """
190
+
191
+ mean: float
192
+ sigma: float
193
+
194
+ def sample(self, num: int, rng: np.random.Generator | None = None) -> Array:
195
+ """Generate random samples from the log-normal distribution.
196
+
197
+ Args:
198
+ num: Number of samples to generate
199
+ rng: Random number generator
200
+
201
+ """
202
+ if rng is None:
203
+ rng = RNG
204
+ return rng.lognormal(self.mean, self.sigma, num)
205
+
206
+
207
+ @dataclass
208
+ class Skewnorm:
209
+ """Skewed normal distribution for asymmetric parameter distributions.
210
+
211
+ Args:
212
+ loc: Mean of the distribution
213
+ scale: Standard deviation
214
+ a: Skewness parameter
215
+
216
+ """
217
+
218
+ loc: float
219
+ scale: float
220
+ a: float
221
+
222
+ def sample(
223
+ self,
224
+ num: int,
225
+ rng: np.random.Generator | None = None, # noqa: ARG002
226
+ ) -> Array:
227
+ """Generate random samples from the skewed normal distribution.
228
+
229
+ Args:
230
+ num: Number of samples to generate
231
+ rng: The random generator argument is unused but required for API compatibility
232
+
233
+ """
234
+ return cast(
235
+ Array, stats.skewnorm(self.a, loc=self.loc, scale=self.scale).rvs(num)
236
+ )
237
+
238
+
239
+ @dataclass
240
+ class GaussianKde:
241
+ """Representation of a kernel-density estimate using Gaussian kernels.
242
+
243
+ Args:
244
+ mean: Mean of the underlying normal distribution
245
+ sigma: Standard deviation of the underlying normal distribution
246
+ seed: Random seed for reproducibility
247
+
248
+ """
249
+
250
+ kde: stats.gaussian_kde
251
+
252
+ @classmethod
253
+ def from_data(cls, data: Array | pd.Series) -> GaussianKde:
254
+ """Create a GaussianKde object from a data array.
255
+
256
+ Args:
257
+ data: Array of data points
258
+
259
+ """
260
+ return cls(stats.gaussian_kde(data))
261
+
262
+ def plot(
263
+ self,
264
+ xmin: float,
265
+ xmax: float,
266
+ n: int = 1000,
267
+ ax: Axes | None = None,
268
+ ) -> FigAx:
269
+ """Plot the kernel-density estimate."""
270
+ fig, ax = _default_fig_ax(ax=ax, grid=True, figsize=(5, 3))
271
+
272
+ x = np.geomspace(xmin, xmax, n)
273
+ y = self.kde(x)
274
+ ax.set_xlim(xmin, xmax)
275
+ ax.set_xscale("log")
276
+ ax.fill_between(x, y, alpha=0.2)
277
+ ax.plot(x, y)
278
+ ax.grid(visible=True)
279
+ ax.set_frame_on(False)
280
+ return fig, ax
281
+
282
+ def sample(
283
+ self,
284
+ num: int,
285
+ rng: np.random.Generator | None = None, # noqa: ARG002
286
+ ) -> Array:
287
+ """Generate random samples from the kde.
288
+
289
+ Args:
290
+ num: Number of samples to generate
291
+ rng: Random number generator. Unused but required for API compatibility
292
+
293
+ """
294
+ return cast(Array, self.kde.resample(num)[0])
295
+
296
+
297
+ def sample(
298
+ parameters: dict[str, Distribution],
299
+ n: int,
300
+ rng: np.random.Generator | None = None,
301
+ ) -> pd.DataFrame:
302
+ """Generate samples from the specified distributions.
303
+
304
+ Examples:
305
+ >>> sample({"beta": Beta(a=1.0, b=1.0),
306
+ ... "uniform": Uniform(lower_bound=0.0, upper_bound=1.0),
307
+ ... "normal": Normal(loc=1.0, scale=0.1),
308
+ ... "log_normal": LogNormal(mean=1.0, sigma=0.1),
309
+ ... "skewnorm": Skewnorm(loc=1.0, scale=0.1, a=5.0),},
310
+ ... n=2,)
311
+ beta uniform normal log_normal skewnorm
312
+ 0 0.253043 0.682496 1.067891 2.798020 1.216259
313
+ 1 0.573357 0.139752 1.006758 2.895416 1.129373
314
+
315
+ Args:
316
+ parameters: Dictionary mapping parameter names to distribution objects.
317
+ n: Number of samples to generate.
318
+ rng: Random number generator.
319
+
320
+ Returns: DataFrame containing the generated samples.
321
+
322
+ """
323
+ return pd.DataFrame({k: v.sample(n, rng) for k, v in parameters.items()})
324
+
325
+
326
+ if __name__ == "__main__":
327
+ _ = sample(
328
+ {
329
+ "beta": Beta(a=1.0, b=1.0),
330
+ "uniform": Uniform(lower_bound=0.0, upper_bound=1.0),
331
+ "normal": Normal(loc=1.0, scale=0.1),
332
+ "log_normal": LogNormal(mean=1.0, sigma=0.1),
333
+ "skewnorm": Skewnorm(loc=1.0, scale=0.1, a=5.0),
334
+ },
335
+ n=1,
336
+ )
@@ -0,0 +1,17 @@
1
+ """Experimental features for modelbase2.
2
+
3
+ APIs should be considered unstable and may change without notice.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from .codegen import generate_model_code_py, generate_modelbase_code
9
+ from .diff import model_diff
10
+ from .tex import to_tex
11
+
12
+ __all__ = [
13
+ "generate_model_code_py",
14
+ "generate_modelbase_code",
15
+ "model_diff",
16
+ "to_tex",
17
+ ]