mxlpy 0.21.0__py3-none-any.whl → 0.23.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 +15 -2
- mxlpy/carousel.py +6 -4
- mxlpy/compare.py +4 -8
- mxlpy/experimental/diff.py +1 -1
- mxlpy/fit.py +195 -99
- mxlpy/identify.py +14 -9
- 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 +15 -6
- mxlpy/label_map.py +6 -7
- mxlpy/linear_label_map.py +3 -1
- mxlpy/mc.py +25 -22
- mxlpy/mca.py +10 -6
- mxlpy/meta/__init__.py +5 -3
- mxlpy/meta/codegen_latex.py +44 -30
- mxlpy/meta/codegen_model.py +175 -0
- mxlpy/meta/codegen_mxlpy.py +254 -0
- mxlpy/meta/source_tools.py +506 -221
- mxlpy/meta/sympy_tools.py +117 -0
- mxlpy/model.py +758 -257
- mxlpy/plot.py +16 -14
- mxlpy/report.py +153 -90
- mxlpy/sbml/_export.py +22 -11
- mxlpy/sbml/_import.py +68 -547
- mxlpy/scan.py +39 -243
- mxlpy/simulator.py +109 -283
- mxlpy/symbolic/symbolic_model.py +29 -17
- mxlpy/types.py +694 -97
- mxlpy/units.py +133 -0
- {mxlpy-0.21.0.dist-info → mxlpy-0.23.0.dist-info}/METADATA +4 -1
- mxlpy-0.23.0.dist-info/RECORD +57 -0
- mxlpy/meta/codegen_modebase.py +0 -112
- mxlpy/meta/codegen_py.py +0 -115
- mxlpy/sbml/_mathml.py +0 -692
- mxlpy/sbml/_unit_conversion.py +0 -74
- mxlpy-0.21.0.dist-info/RECORD +0 -56
- {mxlpy-0.21.0.dist-info → mxlpy-0.23.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.21.0.dist-info → mxlpy-0.23.0.dist-info}/licenses/LICENSE +0 -0
mxlpy/types.py
CHANGED
@@ -17,9 +17,18 @@ Classes:
|
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
19
|
from abc import abstractmethod
|
20
|
-
from collections.abc import Callable, Iterable, Iterator, Mapping
|
20
|
+
from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping
|
21
21
|
from dataclasses import dataclass, field
|
22
|
-
from typing import
|
22
|
+
from typing import (
|
23
|
+
TYPE_CHECKING,
|
24
|
+
Any,
|
25
|
+
Literal,
|
26
|
+
ParamSpec,
|
27
|
+
Protocol,
|
28
|
+
TypeVar,
|
29
|
+
cast,
|
30
|
+
overload,
|
31
|
+
)
|
23
32
|
|
24
33
|
import numpy as np
|
25
34
|
import pandas as pd
|
@@ -31,34 +40,47 @@ __all__ = [
|
|
31
40
|
"Array",
|
32
41
|
"ArrayLike",
|
33
42
|
"Derived",
|
43
|
+
"InitialAssignment",
|
34
44
|
"IntegratorProtocol",
|
35
45
|
"IntegratorType",
|
36
46
|
"McSteadyStates",
|
37
47
|
"MockSurrogate",
|
38
48
|
"Param",
|
39
|
-
"
|
49
|
+
"Parameter",
|
50
|
+
"ProtocolScan",
|
40
51
|
"RateFn",
|
41
52
|
"Reaction",
|
42
53
|
"Readout",
|
43
54
|
"ResponseCoefficients",
|
44
55
|
"ResponseCoefficientsByPars",
|
56
|
+
"Result",
|
45
57
|
"RetType",
|
46
|
-
"
|
47
|
-
"
|
58
|
+
"Rhs",
|
59
|
+
"SteadyStateScan",
|
60
|
+
"TimeCourseScan",
|
61
|
+
"Variable",
|
48
62
|
"unwrap",
|
49
63
|
"unwrap2",
|
50
64
|
]
|
51
65
|
|
52
66
|
type RateFn = Callable[..., float]
|
53
67
|
type Array = NDArray[np.floating[Any]]
|
54
|
-
type ArrayLike = NDArray[np.floating[Any]] | list[float]
|
55
|
-
|
68
|
+
type ArrayLike = NDArray[np.floating[Any]] | pd.Index | list[float]
|
69
|
+
type Rhs = Callable[
|
70
|
+
[
|
71
|
+
float, # t
|
72
|
+
Iterable[float], # y
|
73
|
+
],
|
74
|
+
tuple[float, ...],
|
75
|
+
]
|
56
76
|
|
57
77
|
Param = ParamSpec("Param")
|
58
78
|
RetType = TypeVar("RetType")
|
59
79
|
|
60
80
|
|
61
81
|
if TYPE_CHECKING:
|
82
|
+
import sympy
|
83
|
+
|
62
84
|
from mxlpy.model import Model
|
63
85
|
|
64
86
|
|
@@ -106,8 +128,8 @@ class IntegratorProtocol(Protocol):
|
|
106
128
|
|
107
129
|
def __init__(
|
108
130
|
self,
|
109
|
-
rhs:
|
110
|
-
y0:
|
131
|
+
rhs: Rhs,
|
132
|
+
y0: tuple[float, ...],
|
111
133
|
jacobian: Callable | None = None,
|
112
134
|
) -> None:
|
113
135
|
"""Initialise the integrator."""
|
@@ -143,38 +165,93 @@ class IntegratorProtocol(Protocol):
|
|
143
165
|
|
144
166
|
|
145
167
|
type IntegratorType = Callable[
|
146
|
-
[
|
168
|
+
[
|
169
|
+
Rhs, # model
|
170
|
+
tuple[float, ...], # y0
|
171
|
+
Callable | None, # jacobian
|
172
|
+
],
|
173
|
+
IntegratorProtocol,
|
147
174
|
]
|
148
175
|
|
149
176
|
|
177
|
+
@dataclass
|
178
|
+
class Variable:
|
179
|
+
"""Container for variable meta information."""
|
180
|
+
|
181
|
+
initial_value: float | InitialAssignment
|
182
|
+
unit: sympy.Expr | None = None
|
183
|
+
source: str | None = None
|
184
|
+
|
185
|
+
|
186
|
+
@dataclass
|
187
|
+
class Parameter:
|
188
|
+
"""Container for parameter meta information."""
|
189
|
+
|
190
|
+
value: float | InitialAssignment
|
191
|
+
unit: sympy.Expr | None = None
|
192
|
+
source: str | None = None
|
193
|
+
|
194
|
+
|
150
195
|
@dataclass(kw_only=True, slots=True)
|
151
196
|
class Derived:
|
152
197
|
"""Container for a derived value."""
|
153
198
|
|
154
199
|
fn: RateFn
|
155
200
|
args: list[str]
|
201
|
+
unit: sympy.Expr | None = None
|
202
|
+
|
203
|
+
def calculate(self, args: dict[str, Any]) -> float:
|
204
|
+
"""Calculate the derived value.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
args: Dictionary of args variables.
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
The calculated derived value.
|
211
|
+
|
212
|
+
"""
|
213
|
+
return cast(float, self.fn(*(args[arg] for arg in self.args)))
|
214
|
+
|
215
|
+
def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
|
216
|
+
"""Calculate the derived value in place.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
name: Name of the derived variable.
|
220
|
+
args: Dictionary of args variables.
|
221
|
+
|
222
|
+
"""
|
223
|
+
args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
|
224
|
+
|
225
|
+
|
226
|
+
@dataclass(kw_only=True, slots=True)
|
227
|
+
class InitialAssignment:
|
228
|
+
"""Container for a derived value."""
|
229
|
+
|
230
|
+
fn: RateFn
|
231
|
+
args: list[str]
|
232
|
+
unit: sympy.Expr | None = None
|
156
233
|
|
157
|
-
def calculate(self,
|
234
|
+
def calculate(self, args: dict[str, Any]) -> float:
|
158
235
|
"""Calculate the derived value.
|
159
236
|
|
160
237
|
Args:
|
161
|
-
|
238
|
+
args: Dictionary of args variables.
|
162
239
|
|
163
240
|
Returns:
|
164
241
|
The calculated derived value.
|
165
242
|
|
166
243
|
"""
|
167
|
-
return cast(float, self.fn(*(
|
244
|
+
return cast(float, self.fn(*(args[arg] for arg in self.args)))
|
168
245
|
|
169
|
-
def calculate_inpl(self, name: str,
|
246
|
+
def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
|
170
247
|
"""Calculate the derived value in place.
|
171
248
|
|
172
249
|
Args:
|
173
250
|
name: Name of the derived variable.
|
174
|
-
|
251
|
+
args: Dictionary of args variables.
|
175
252
|
|
176
253
|
"""
|
177
|
-
|
254
|
+
args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
|
178
255
|
|
179
256
|
|
180
257
|
@dataclass(kw_only=True, slots=True)
|
@@ -183,28 +260,29 @@ class Readout:
|
|
183
260
|
|
184
261
|
fn: RateFn
|
185
262
|
args: list[str]
|
263
|
+
unit: sympy.Expr | None = None
|
186
264
|
|
187
|
-
def calculate(self,
|
265
|
+
def calculate(self, args: dict[str, Any]) -> float:
|
188
266
|
"""Calculate the derived value.
|
189
267
|
|
190
268
|
Args:
|
191
|
-
|
269
|
+
args: Dictionary of args variables.
|
192
270
|
|
193
271
|
Returns:
|
194
272
|
The calculated derived value.
|
195
273
|
|
196
274
|
"""
|
197
|
-
return cast(float, self.fn(*(
|
275
|
+
return cast(float, self.fn(*(args[arg] for arg in self.args)))
|
198
276
|
|
199
|
-
def calculate_inpl(self, name: str,
|
277
|
+
def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
|
200
278
|
"""Calculate the reaction in place.
|
201
279
|
|
202
280
|
Args:
|
203
281
|
name: Name of the derived variable.
|
204
|
-
|
282
|
+
args: Dictionary of args variables.
|
205
283
|
|
206
284
|
"""
|
207
|
-
|
285
|
+
args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
|
208
286
|
|
209
287
|
|
210
288
|
@dataclass(kw_only=True, slots=True)
|
@@ -214,35 +292,36 @@ class Reaction:
|
|
214
292
|
fn: RateFn
|
215
293
|
stoichiometry: Mapping[str, float | Derived]
|
216
294
|
args: list[str]
|
295
|
+
unit: sympy.Expr | None = None
|
217
296
|
|
218
297
|
def get_modifiers(self, model: Model) -> list[str]:
|
219
298
|
"""Get the modifiers of the reaction."""
|
220
|
-
include = set(model.
|
299
|
+
include = set(model.get_variable_names())
|
221
300
|
exclude = set(self.stoichiometry)
|
222
301
|
|
223
302
|
return [k for k in self.args if k in include and k not in exclude]
|
224
303
|
|
225
|
-
def calculate(self,
|
304
|
+
def calculate(self, args: dict[str, Any]) -> float:
|
226
305
|
"""Calculate the derived value.
|
227
306
|
|
228
307
|
Args:
|
229
|
-
|
308
|
+
args: Dictionary of args variables.
|
230
309
|
|
231
310
|
Returns:
|
232
311
|
The calculated derived value.
|
233
312
|
|
234
313
|
"""
|
235
|
-
return cast(float, self.fn(*(
|
314
|
+
return cast(float, self.fn(*(args[arg] for arg in self.args)))
|
236
315
|
|
237
|
-
def calculate_inpl(self, name: str,
|
316
|
+
def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
|
238
317
|
"""Calculate the reaction in place.
|
239
318
|
|
240
319
|
Args:
|
241
320
|
name: Name of the derived variable.
|
242
|
-
|
321
|
+
args: Dictionary of args variables.
|
243
322
|
|
244
323
|
"""
|
245
|
-
|
324
|
+
args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
|
246
325
|
|
247
326
|
|
248
327
|
@dataclass(kw_only=True)
|
@@ -297,22 +376,417 @@ class MockSurrogate(AbstractSurrogate):
|
|
297
376
|
) # type: ignore
|
298
377
|
|
299
378
|
|
379
|
+
@dataclass(kw_only=True)
|
380
|
+
class AbstractEstimator:
|
381
|
+
"""Abstract class for parameter estimation using neural networks."""
|
382
|
+
|
383
|
+
parameter_names: list[str]
|
384
|
+
|
385
|
+
@abstractmethod
|
386
|
+
def predict(self, features: pd.Series | pd.DataFrame) -> pd.DataFrame:
|
387
|
+
"""Predict the target values for the given features."""
|
388
|
+
|
389
|
+
|
390
|
+
###############################################################################
|
391
|
+
# Simulation results
|
392
|
+
###############################################################################
|
393
|
+
|
394
|
+
|
395
|
+
def _normalise_split_results(
|
396
|
+
results: list[pd.DataFrame],
|
397
|
+
normalise: float | ArrayLike,
|
398
|
+
) -> list[pd.DataFrame]:
|
399
|
+
"""Normalize split results by a given factor or array.
|
400
|
+
|
401
|
+
Args:
|
402
|
+
results: List of DataFrames containing the results to normalize.
|
403
|
+
normalise: Normalization factor or array.
|
404
|
+
|
405
|
+
Returns:
|
406
|
+
list[pd.DataFrame]: List of normalized DataFrames.
|
407
|
+
|
408
|
+
"""
|
409
|
+
if isinstance(normalise, int | float):
|
410
|
+
return [i / normalise for i in results]
|
411
|
+
if len(normalise) == len(results):
|
412
|
+
return [(i.T / j).T for i, j in zip(results, normalise, strict=True)]
|
413
|
+
|
414
|
+
results = []
|
415
|
+
start = 0
|
416
|
+
end = 0
|
417
|
+
for i in results:
|
418
|
+
end += len(i)
|
419
|
+
results.append(i / np.reshape(normalise[start:end], (len(i), 1))) # type: ignore
|
420
|
+
start += end
|
421
|
+
return results
|
422
|
+
|
423
|
+
|
300
424
|
@dataclass(kw_only=True, slots=True)
|
301
|
-
class
|
302
|
-
"""
|
425
|
+
class Result:
|
426
|
+
"""Simulation results."""
|
427
|
+
|
428
|
+
model: Model
|
429
|
+
raw_variables: list[pd.DataFrame]
|
430
|
+
raw_parameters: list[dict[str, float]]
|
431
|
+
raw_args: list[pd.DataFrame] = field(default_factory=list)
|
432
|
+
|
433
|
+
@classmethod
|
434
|
+
def default(cls, model: Model, time_points: Array) -> Result:
|
435
|
+
"""Get result filled with NaNs."""
|
436
|
+
return Result(
|
437
|
+
model=model,
|
438
|
+
raw_variables=[
|
439
|
+
pd.DataFrame(
|
440
|
+
data=np.full(
|
441
|
+
shape=(len(time_points), len(model.get_variable_names())),
|
442
|
+
fill_value=np.nan,
|
443
|
+
),
|
444
|
+
index=time_points,
|
445
|
+
columns=model.get_variable_names(),
|
446
|
+
)
|
447
|
+
],
|
448
|
+
raw_parameters=[model.get_parameter_values()],
|
449
|
+
)
|
303
450
|
|
304
|
-
|
305
|
-
|
451
|
+
@property
|
452
|
+
def variables(self) -> pd.DataFrame:
|
453
|
+
"""Simulation variables."""
|
454
|
+
return self.get_variables(
|
455
|
+
include_derived_variables=True,
|
456
|
+
include_readouts=True,
|
457
|
+
concatenated=True,
|
458
|
+
normalise=None,
|
459
|
+
)
|
460
|
+
|
461
|
+
@property
|
462
|
+
def fluxes(self) -> pd.DataFrame:
|
463
|
+
"""Simulation fluxes."""
|
464
|
+
return self.get_fluxes()
|
465
|
+
|
466
|
+
def _compute_args(self) -> list[pd.DataFrame]:
|
467
|
+
# Already computed
|
468
|
+
if len(self.raw_args) > 0:
|
469
|
+
return self.raw_args
|
470
|
+
|
471
|
+
# Compute new otherwise
|
472
|
+
for res, p in zip(self.raw_variables, self.raw_parameters, strict=True):
|
473
|
+
self.model.update_parameters(p)
|
474
|
+
self.raw_args.append(
|
475
|
+
self.model.get_args_time_course(
|
476
|
+
variables=res,
|
477
|
+
include_variables=True,
|
478
|
+
include_parameters=True,
|
479
|
+
include_derived_parameters=True,
|
480
|
+
include_derived_variables=True,
|
481
|
+
include_reactions=True,
|
482
|
+
include_surrogate_variables=True,
|
483
|
+
include_surrogate_fluxes=True,
|
484
|
+
include_readouts=True,
|
485
|
+
)
|
486
|
+
)
|
487
|
+
return self.raw_args
|
488
|
+
|
489
|
+
def _select_data(
|
490
|
+
self,
|
491
|
+
dependent: list[pd.DataFrame],
|
492
|
+
*,
|
493
|
+
include_variables: bool = False,
|
494
|
+
include_parameters: bool = False,
|
495
|
+
include_derived_parameters: bool = False,
|
496
|
+
include_derived_variables: bool = False,
|
497
|
+
include_reactions: bool = False,
|
498
|
+
include_surrogate_variables: bool = False,
|
499
|
+
include_surrogate_fluxes: bool = False,
|
500
|
+
include_readouts: bool = False,
|
501
|
+
) -> list[pd.DataFrame]:
|
502
|
+
names = self.model.get_arg_names(
|
503
|
+
include_time=False,
|
504
|
+
include_variables=include_variables,
|
505
|
+
include_parameters=include_parameters,
|
506
|
+
include_derived_parameters=include_derived_parameters,
|
507
|
+
include_derived_variables=include_derived_variables,
|
508
|
+
include_reactions=include_reactions,
|
509
|
+
include_surrogate_variables=include_surrogate_variables,
|
510
|
+
include_surrogate_fluxes=include_surrogate_fluxes,
|
511
|
+
include_readouts=include_readouts,
|
512
|
+
)
|
513
|
+
return [i.loc[:, names] for i in dependent]
|
514
|
+
|
515
|
+
def _adjust_data(
|
516
|
+
self,
|
517
|
+
data: list[pd.DataFrame],
|
518
|
+
normalise: float | ArrayLike | None = None,
|
519
|
+
*,
|
520
|
+
concatenated: bool = True,
|
521
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
522
|
+
if normalise is not None:
|
523
|
+
data = _normalise_split_results(data, normalise=normalise)
|
524
|
+
if concatenated:
|
525
|
+
return pd.concat(data, axis=0)
|
526
|
+
return data
|
527
|
+
|
528
|
+
@overload
|
529
|
+
def get_args( # type: ignore
|
530
|
+
self,
|
531
|
+
*,
|
532
|
+
include_variables: bool = True,
|
533
|
+
include_parameters: bool = False,
|
534
|
+
include_derived_parameters: bool = False,
|
535
|
+
include_derived_variables: bool = True,
|
536
|
+
include_reactions: bool = True,
|
537
|
+
include_surrogate_variables: bool = False,
|
538
|
+
include_surrogate_fluxes: bool = False,
|
539
|
+
include_readouts: bool = False,
|
540
|
+
concatenated: Literal[False],
|
541
|
+
normalise: float | ArrayLike | None = None,
|
542
|
+
) -> list[pd.DataFrame]: ...
|
543
|
+
|
544
|
+
@overload
|
545
|
+
def get_args(
|
546
|
+
self,
|
547
|
+
*,
|
548
|
+
include_variables: bool = True,
|
549
|
+
include_parameters: bool = False,
|
550
|
+
include_derived_parameters: bool = False,
|
551
|
+
include_derived_variables: bool = True,
|
552
|
+
include_reactions: bool = True,
|
553
|
+
include_surrogate_variables: bool = False,
|
554
|
+
include_surrogate_fluxes: bool = False,
|
555
|
+
include_readouts: bool = False,
|
556
|
+
concatenated: Literal[True],
|
557
|
+
normalise: float | ArrayLike | None = None,
|
558
|
+
) -> pd.DataFrame: ...
|
559
|
+
|
560
|
+
@overload
|
561
|
+
def get_args(
|
562
|
+
self,
|
563
|
+
*,
|
564
|
+
include_variables: bool = True,
|
565
|
+
include_parameters: bool = False,
|
566
|
+
include_derived_parameters: bool = False,
|
567
|
+
include_derived_variables: bool = True,
|
568
|
+
include_reactions: bool = True,
|
569
|
+
include_surrogate_variables: bool = False,
|
570
|
+
include_surrogate_fluxes: bool = False,
|
571
|
+
include_readouts: bool = False,
|
572
|
+
concatenated: bool = True,
|
573
|
+
normalise: float | ArrayLike | None = None,
|
574
|
+
) -> pd.DataFrame: ...
|
575
|
+
|
576
|
+
def get_args(
|
577
|
+
self,
|
578
|
+
*,
|
579
|
+
include_variables: bool = True,
|
580
|
+
include_parameters: bool = False,
|
581
|
+
include_derived_parameters: bool = False,
|
582
|
+
include_derived_variables: bool = True,
|
583
|
+
include_reactions: bool = True,
|
584
|
+
include_surrogate_variables: bool = False,
|
585
|
+
include_surrogate_fluxes: bool = False,
|
586
|
+
include_readouts: bool = False,
|
587
|
+
concatenated: bool = True,
|
588
|
+
normalise: float | ArrayLike | None = None,
|
589
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
590
|
+
"""Get the variables over time.
|
591
|
+
|
592
|
+
Examples:
|
593
|
+
>>> Result().get_variables()
|
594
|
+
Time ATP NADPH
|
595
|
+
0.000000 1.000000 1.000000
|
596
|
+
0.000100 0.999900 0.999900
|
597
|
+
0.000200 0.999800 0.999800
|
598
|
+
|
599
|
+
"""
|
600
|
+
variables = self._select_data(
|
601
|
+
self._compute_args(),
|
602
|
+
include_variables=include_variables,
|
603
|
+
include_parameters=include_parameters,
|
604
|
+
include_derived_parameters=include_derived_parameters,
|
605
|
+
include_derived_variables=include_derived_variables,
|
606
|
+
include_reactions=include_reactions,
|
607
|
+
include_surrogate_variables=include_surrogate_variables,
|
608
|
+
include_surrogate_fluxes=include_surrogate_fluxes,
|
609
|
+
include_readouts=include_readouts,
|
610
|
+
)
|
611
|
+
return self._adjust_data(
|
612
|
+
variables, normalise=normalise, concatenated=concatenated
|
613
|
+
)
|
614
|
+
|
615
|
+
@overload
|
616
|
+
def get_variables( # type: ignore
|
617
|
+
self,
|
618
|
+
*,
|
619
|
+
include_derived_variables: bool = True,
|
620
|
+
include_readouts: bool = True,
|
621
|
+
concatenated: Literal[False],
|
622
|
+
normalise: float | ArrayLike | None = None,
|
623
|
+
) -> list[pd.DataFrame]: ...
|
624
|
+
|
625
|
+
@overload
|
626
|
+
def get_variables(
|
627
|
+
self,
|
628
|
+
*,
|
629
|
+
include_derived_variables: bool = True,
|
630
|
+
include_readouts: bool = True,
|
631
|
+
concatenated: Literal[True],
|
632
|
+
normalise: float | ArrayLike | None = None,
|
633
|
+
) -> pd.DataFrame: ...
|
634
|
+
|
635
|
+
@overload
|
636
|
+
def get_variables(
|
637
|
+
self,
|
638
|
+
*,
|
639
|
+
include_derived_variables: bool = True,
|
640
|
+
include_readouts: bool = True,
|
641
|
+
concatenated: bool = True,
|
642
|
+
normalise: float | ArrayLike | None = None,
|
643
|
+
) -> pd.DataFrame: ...
|
644
|
+
|
645
|
+
def get_variables(
|
646
|
+
self,
|
647
|
+
*,
|
648
|
+
include_derived_variables: bool = True,
|
649
|
+
include_readouts: bool = True,
|
650
|
+
concatenated: bool = True,
|
651
|
+
normalise: float | ArrayLike | None = None,
|
652
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
653
|
+
"""Get the variables over time.
|
654
|
+
|
655
|
+
Examples:
|
656
|
+
>>> Result().get_variables()
|
657
|
+
Time ATP NADPH
|
658
|
+
0.000000 1.000000 1.000000
|
659
|
+
0.000100 0.999900 0.999900
|
660
|
+
0.000200 0.999800 0.999800
|
661
|
+
|
662
|
+
"""
|
663
|
+
if not include_derived_variables and not include_readouts:
|
664
|
+
return self._adjust_data(
|
665
|
+
self.raw_variables,
|
666
|
+
normalise=normalise,
|
667
|
+
concatenated=concatenated,
|
668
|
+
)
|
669
|
+
|
670
|
+
variables = self._select_data(
|
671
|
+
self._compute_args(),
|
672
|
+
include_variables=True,
|
673
|
+
include_derived_variables=include_derived_variables,
|
674
|
+
include_readouts=include_readouts,
|
675
|
+
)
|
676
|
+
return self._adjust_data(
|
677
|
+
variables, normalise=normalise, concatenated=concatenated
|
678
|
+
)
|
679
|
+
|
680
|
+
@overload
|
681
|
+
def get_fluxes( # type: ignore
|
682
|
+
self,
|
683
|
+
*,
|
684
|
+
include_surrogates: bool = True,
|
685
|
+
normalise: float | ArrayLike | None = None,
|
686
|
+
concatenated: Literal[False],
|
687
|
+
) -> list[pd.DataFrame]: ...
|
688
|
+
|
689
|
+
@overload
|
690
|
+
def get_fluxes(
|
691
|
+
self,
|
692
|
+
*,
|
693
|
+
include_surrogates: bool = True,
|
694
|
+
normalise: float | ArrayLike | None = None,
|
695
|
+
concatenated: Literal[True],
|
696
|
+
) -> pd.DataFrame: ...
|
697
|
+
|
698
|
+
@overload
|
699
|
+
def get_fluxes(
|
700
|
+
self,
|
701
|
+
*,
|
702
|
+
include_surrogates: bool = True,
|
703
|
+
normalise: float | ArrayLike | None = None,
|
704
|
+
concatenated: bool = True,
|
705
|
+
) -> pd.DataFrame: ...
|
706
|
+
|
707
|
+
def get_fluxes(
|
708
|
+
self,
|
709
|
+
*,
|
710
|
+
include_surrogates: bool = True,
|
711
|
+
normalise: float | ArrayLike | None = None,
|
712
|
+
concatenated: bool = True,
|
713
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
714
|
+
"""Get the flux results.
|
715
|
+
|
716
|
+
Examples:
|
717
|
+
>>> Result.get_fluxes()
|
718
|
+
Time v1 v2
|
719
|
+
0.000000 1.000000 10.00000
|
720
|
+
0.000100 0.999900 9.999000
|
721
|
+
0.000200 0.999800 9.998000
|
722
|
+
|
723
|
+
Returns:
|
724
|
+
pd.DataFrame: DataFrame of fluxes.
|
725
|
+
|
726
|
+
"""
|
727
|
+
fluxes = self._select_data(
|
728
|
+
self._compute_args(),
|
729
|
+
include_reactions=True,
|
730
|
+
include_surrogate_fluxes=include_surrogates,
|
731
|
+
)
|
732
|
+
return self._adjust_data(
|
733
|
+
fluxes,
|
734
|
+
normalise=normalise,
|
735
|
+
concatenated=concatenated,
|
736
|
+
)
|
737
|
+
|
738
|
+
def get_combined(self) -> pd.DataFrame:
|
739
|
+
"""Get the variables and fluxes as a single pandas.DataFrame.
|
740
|
+
|
741
|
+
Examples:
|
742
|
+
>>> Result.get_combined()
|
743
|
+
Time ATP NADPH v1 v2
|
744
|
+
0.000000 1.000000 1.000000 1.000000 10.00000
|
745
|
+
0.000100 0.999900 0.999900 0.999900 9.999000
|
746
|
+
0.000200 0.999800 0.999800 0.999800 9.998000
|
747
|
+
|
748
|
+
Returns:
|
749
|
+
pd.DataFrame: DataFrame of fluxes.
|
750
|
+
|
751
|
+
"""
|
752
|
+
return pd.concat((self.variables, self.fluxes), axis=1)
|
753
|
+
|
754
|
+
def get_new_y0(self) -> dict[str, float]:
|
755
|
+
"""Get the new initial conditions after the simulation.
|
756
|
+
|
757
|
+
Examples:
|
758
|
+
>>> Simulator(model).simulate_to_steady_state().get_new_y0()
|
759
|
+
{"ATP": 1.0, "NADPH": 1.0}
|
760
|
+
|
761
|
+
"""
|
762
|
+
return dict(
|
763
|
+
self.get_variables(
|
764
|
+
include_derived_variables=False,
|
765
|
+
include_readouts=False,
|
766
|
+
).iloc[-1]
|
767
|
+
)
|
306
768
|
|
307
769
|
def __iter__(self) -> Iterator[pd.DataFrame]:
|
308
770
|
"""Iterate over the concentration and flux response coefficients."""
|
309
771
|
return iter((self.variables, self.fluxes))
|
310
772
|
|
773
|
+
|
774
|
+
@dataclass(kw_only=True, slots=True)
|
775
|
+
class ResponseCoefficients:
|
776
|
+
"""Container for response coefficients."""
|
777
|
+
|
778
|
+
variables: pd.DataFrame
|
779
|
+
fluxes: pd.DataFrame
|
780
|
+
|
311
781
|
@property
|
312
|
-
def
|
782
|
+
def combined(self) -> pd.DataFrame:
|
313
783
|
"""Return the response coefficients as a DataFrame."""
|
314
784
|
return pd.concat((self.variables, self.fluxes), axis=1)
|
315
785
|
|
786
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
787
|
+
"""Iterate over the concentration and flux response coefficients."""
|
788
|
+
return iter((self.variables, self.fluxes))
|
789
|
+
|
316
790
|
|
317
791
|
@dataclass(kw_only=True, slots=True)
|
318
792
|
class ResponseCoefficientsByPars:
|
@@ -322,124 +796,247 @@ class ResponseCoefficientsByPars:
|
|
322
796
|
fluxes: pd.DataFrame
|
323
797
|
parameters: pd.DataFrame
|
324
798
|
|
325
|
-
def __iter__(self) -> Iterator[pd.DataFrame]:
|
326
|
-
"""Iterate over the concentration and flux response coefficients."""
|
327
|
-
return iter((self.variables, self.fluxes))
|
328
|
-
|
329
799
|
@property
|
330
|
-
def
|
800
|
+
def combined(self) -> pd.DataFrame:
|
331
801
|
"""Return the response coefficients as a DataFrame."""
|
332
802
|
return pd.concat((self.variables, self.fluxes), axis=1)
|
333
803
|
|
804
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
805
|
+
"""Iterate over the concentration and flux response coefficients."""
|
806
|
+
return iter((self.variables, self.fluxes))
|
334
807
|
|
335
|
-
@dataclass(kw_only=True, slots=True)
|
336
|
-
class SteadyStates:
|
337
|
-
"""Container for steady states."""
|
338
808
|
|
339
|
-
|
340
|
-
|
341
|
-
|
809
|
+
@dataclass(kw_only=True, slots=True)
|
810
|
+
class SteadyStateScan:
|
811
|
+
"""Container for steady states by scanned values."""
|
342
812
|
|
343
|
-
|
344
|
-
|
345
|
-
|
813
|
+
to_scan: pd.DataFrame
|
814
|
+
raw_index: pd.Index | pd.MultiIndex
|
815
|
+
raw_results: list[Result]
|
346
816
|
|
347
817
|
@property
|
348
|
-
def
|
349
|
-
"""Return
|
350
|
-
return pd.
|
818
|
+
def variables(self) -> pd.DataFrame:
|
819
|
+
"""Return steady-state variables by scan."""
|
820
|
+
return pd.DataFrame(
|
821
|
+
[i.variables.iloc[-1].T for i in self.raw_results], index=self.raw_index
|
822
|
+
)
|
351
823
|
|
824
|
+
@property
|
825
|
+
def fluxes(self) -> pd.DataFrame:
|
826
|
+
"""Return steady-state fluxes by scan."""
|
827
|
+
return pd.DataFrame(
|
828
|
+
[i.fluxes.iloc[-1].T for i in self.raw_results], index=self.raw_index
|
829
|
+
)
|
352
830
|
|
353
|
-
@
|
354
|
-
|
355
|
-
|
831
|
+
@property
|
832
|
+
def combined(self) -> pd.DataFrame:
|
833
|
+
"""Return steady-state args by scan."""
|
834
|
+
return self.get_args()
|
356
835
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
836
|
+
def get_args(
|
837
|
+
self,
|
838
|
+
*,
|
839
|
+
include_variables: bool = True,
|
840
|
+
include_parameters: bool = False,
|
841
|
+
include_derived_parameters: bool = False,
|
842
|
+
include_derived_variables: bool = True,
|
843
|
+
include_reactions: bool = True,
|
844
|
+
include_surrogate_variables: bool = False,
|
845
|
+
include_surrogate_fluxes: bool = False,
|
846
|
+
include_readouts: bool = False,
|
847
|
+
) -> pd.DataFrame:
|
848
|
+
"""Return steady-state args by scan."""
|
849
|
+
return pd.DataFrame(
|
850
|
+
[
|
851
|
+
i.get_args(
|
852
|
+
include_variables=include_variables,
|
853
|
+
include_parameters=include_parameters,
|
854
|
+
include_derived_parameters=include_derived_parameters,
|
855
|
+
include_derived_variables=include_derived_variables,
|
856
|
+
include_reactions=include_reactions,
|
857
|
+
include_surrogate_variables=include_surrogate_variables,
|
858
|
+
include_surrogate_fluxes=include_surrogate_fluxes,
|
859
|
+
include_readouts=include_readouts,
|
860
|
+
)
|
861
|
+
.iloc[-1]
|
862
|
+
.T
|
863
|
+
for i in self.raw_results
|
864
|
+
],
|
865
|
+
index=self.raw_index,
|
866
|
+
)
|
361
867
|
|
362
868
|
def __iter__(self) -> Iterator[pd.DataFrame]:
|
363
869
|
"""Iterate over the concentration and flux steady states."""
|
364
870
|
return iter((self.variables, self.fluxes))
|
365
871
|
|
366
|
-
@property
|
367
|
-
def results(self) -> pd.DataFrame:
|
368
|
-
"""Return the steady states as a DataFrame."""
|
369
|
-
return pd.concat((self.variables, self.fluxes), axis=1)
|
370
|
-
|
371
872
|
|
372
873
|
@dataclass(kw_only=True, slots=True)
|
373
|
-
class
|
374
|
-
"""Container for time courses by
|
874
|
+
class TimeCourseScan:
|
875
|
+
"""Container for time courses by scanned values."""
|
375
876
|
|
376
|
-
|
377
|
-
|
378
|
-
parameters: pd.DataFrame
|
877
|
+
to_scan: pd.DataFrame
|
878
|
+
raw_results: dict[Hashable, Result]
|
379
879
|
|
380
|
-
|
381
|
-
|
382
|
-
|
880
|
+
@property
|
881
|
+
def variables(self) -> pd.DataFrame:
|
882
|
+
"""Return all args of the time courses."""
|
883
|
+
return pd.concat(
|
884
|
+
{k: i.variables for k, i in self.raw_results.items()}, names=["n", "time"]
|
885
|
+
)
|
383
886
|
|
384
887
|
@property
|
385
|
-
def
|
888
|
+
def fluxes(self) -> pd.DataFrame:
|
889
|
+
"""Return all args of the time courses."""
|
890
|
+
return pd.concat(
|
891
|
+
{k: i.fluxes for k, i in self.raw_results.items()}, names=["n", "time"]
|
892
|
+
)
|
893
|
+
|
894
|
+
@property
|
895
|
+
def combined(self) -> pd.DataFrame:
|
386
896
|
"""Return the time courses as a DataFrame."""
|
387
|
-
return
|
897
|
+
return self.get_args()
|
898
|
+
|
899
|
+
def get_args(
|
900
|
+
self,
|
901
|
+
*,
|
902
|
+
include_variables: bool = True,
|
903
|
+
include_parameters: bool = False,
|
904
|
+
include_derived_parameters: bool = False,
|
905
|
+
include_derived_variables: bool = True,
|
906
|
+
include_reactions: bool = True,
|
907
|
+
include_surrogate_variables: bool = False,
|
908
|
+
include_surrogate_fluxes: bool = False,
|
909
|
+
include_readouts: bool = False,
|
910
|
+
) -> pd.DataFrame:
|
911
|
+
"""Return all args of the time courses."""
|
912
|
+
return pd.concat(
|
913
|
+
{
|
914
|
+
k: i.get_args(
|
915
|
+
include_variables=include_variables,
|
916
|
+
include_parameters=include_parameters,
|
917
|
+
include_derived_parameters=include_derived_parameters,
|
918
|
+
include_derived_variables=include_derived_variables,
|
919
|
+
include_reactions=include_reactions,
|
920
|
+
include_surrogate_variables=include_surrogate_variables,
|
921
|
+
include_surrogate_fluxes=include_surrogate_fluxes,
|
922
|
+
include_readouts=include_readouts,
|
923
|
+
)
|
924
|
+
for k, i in self.raw_results.items()
|
925
|
+
},
|
926
|
+
names=["n", "time"],
|
927
|
+
)
|
388
928
|
|
389
929
|
def get_by_name(self, name: str) -> pd.DataFrame:
|
390
930
|
"""Get time courses by name."""
|
391
|
-
return self.
|
931
|
+
return self.combined[name].unstack().T
|
392
932
|
|
393
933
|
def get_agg_per_time(self, agg: str | Callable) -> pd.DataFrame:
|
394
934
|
"""Get aggregated time courses."""
|
395
|
-
mean = cast(pd.DataFrame, self.
|
935
|
+
mean = cast(pd.DataFrame, self.combined.unstack(level=1).agg(agg, axis=0))
|
396
936
|
return cast(pd.DataFrame, mean.unstack().T)
|
397
937
|
|
398
938
|
def get_agg_per_run(self, agg: str | Callable) -> pd.DataFrame:
|
399
939
|
"""Get aggregated time courses."""
|
400
|
-
mean = cast(pd.DataFrame, self.
|
940
|
+
mean = cast(pd.DataFrame, self.combined.unstack(level=0).agg(agg, axis=0))
|
401
941
|
return cast(pd.DataFrame, mean.unstack().T)
|
402
942
|
|
943
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
944
|
+
"""Iterate over the concentration and flux time courses."""
|
945
|
+
return iter((self.variables, self.fluxes))
|
946
|
+
|
403
947
|
|
404
948
|
@dataclass(kw_only=True, slots=True)
|
405
|
-
class
|
406
|
-
"""Container for protocols by
|
949
|
+
class ProtocolScan:
|
950
|
+
"""Container for protocols by scanned values."""
|
407
951
|
|
408
|
-
|
409
|
-
fluxes: pd.DataFrame
|
410
|
-
parameters: pd.DataFrame
|
952
|
+
to_scan: pd.DataFrame
|
411
953
|
protocol: pd.DataFrame
|
954
|
+
raw_results: dict[Hashable, Result]
|
412
955
|
|
413
|
-
|
414
|
-
|
415
|
-
|
956
|
+
@property
|
957
|
+
def variables(self) -> pd.DataFrame:
|
958
|
+
"""Return all args of the time courses."""
|
959
|
+
return pd.concat(
|
960
|
+
{k: i.variables for k, i in self.raw_results.items()},
|
961
|
+
names=["n", "time"],
|
962
|
+
)
|
416
963
|
|
417
964
|
@property
|
418
|
-
def
|
419
|
-
"""Return
|
420
|
-
return pd.concat(
|
965
|
+
def fluxes(self) -> pd.DataFrame:
|
966
|
+
"""Return all args of the time courses."""
|
967
|
+
return pd.concat(
|
968
|
+
{k: i.fluxes for k, i in self.raw_results.items()},
|
969
|
+
names=["n", "time"],
|
970
|
+
)
|
971
|
+
|
972
|
+
@property
|
973
|
+
def combined(self) -> pd.DataFrame:
|
974
|
+
"""Return the time courses as a DataFrame."""
|
975
|
+
return self.get_args()
|
976
|
+
|
977
|
+
def get_args(
|
978
|
+
self,
|
979
|
+
*,
|
980
|
+
include_variables: bool = True,
|
981
|
+
include_parameters: bool = False,
|
982
|
+
include_derived_parameters: bool = False,
|
983
|
+
include_derived_variables: bool = True,
|
984
|
+
include_reactions: bool = True,
|
985
|
+
include_surrogate_variables: bool = False,
|
986
|
+
include_surrogate_fluxes: bool = False,
|
987
|
+
include_readouts: bool = False,
|
988
|
+
) -> pd.DataFrame:
|
989
|
+
"""Return all args of the time courses."""
|
990
|
+
return pd.concat(
|
991
|
+
{
|
992
|
+
k: i.get_args(
|
993
|
+
include_variables=include_variables,
|
994
|
+
include_parameters=include_parameters,
|
995
|
+
include_derived_parameters=include_derived_parameters,
|
996
|
+
include_derived_variables=include_derived_variables,
|
997
|
+
include_reactions=include_reactions,
|
998
|
+
include_surrogate_variables=include_surrogate_variables,
|
999
|
+
include_surrogate_fluxes=include_surrogate_fluxes,
|
1000
|
+
include_readouts=include_readouts,
|
1001
|
+
)
|
1002
|
+
for k, i in self.raw_results.items()
|
1003
|
+
},
|
1004
|
+
names=["n", "time"],
|
1005
|
+
)
|
421
1006
|
|
422
1007
|
def get_by_name(self, name: str) -> pd.DataFrame:
|
423
1008
|
"""Get concentration or flux by name."""
|
424
|
-
return self.
|
1009
|
+
return self.combined[name].unstack().T
|
425
1010
|
|
426
1011
|
def get_agg_per_time(self, agg: str | Callable) -> pd.DataFrame:
|
427
1012
|
"""Get aggregated concentration or flux."""
|
428
|
-
mean = cast(pd.DataFrame, self.
|
1013
|
+
mean = cast(pd.DataFrame, self.combined.unstack(level=1).agg(agg, axis=0))
|
429
1014
|
return cast(pd.DataFrame, mean.unstack().T)
|
430
1015
|
|
431
1016
|
def get_agg_per_run(self, agg: str | Callable) -> pd.DataFrame:
|
432
1017
|
"""Get aggregated concentration or flux."""
|
433
|
-
mean = cast(pd.DataFrame, self.
|
1018
|
+
mean = cast(pd.DataFrame, self.combined.unstack(level=0).agg(agg, axis=0))
|
434
1019
|
return cast(pd.DataFrame, mean.unstack().T)
|
435
1020
|
|
1021
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
1022
|
+
"""Iterate over the concentration and flux protocols."""
|
1023
|
+
return iter((self.variables, self.fluxes))
|
436
1024
|
|
437
|
-
@dataclass(kw_only=True)
|
438
|
-
class AbstractEstimator:
|
439
|
-
"""Abstract class for parameter estimation using neural networks."""
|
440
1025
|
|
441
|
-
|
1026
|
+
@dataclass(kw_only=True, slots=True)
|
1027
|
+
class McSteadyStates:
|
1028
|
+
"""Container for Monte Carlo steady states."""
|
442
1029
|
|
443
|
-
|
444
|
-
|
445
|
-
|
1030
|
+
variables: pd.DataFrame
|
1031
|
+
fluxes: pd.DataFrame
|
1032
|
+
parameters: pd.DataFrame
|
1033
|
+
mc_to_scan: pd.DataFrame
|
1034
|
+
|
1035
|
+
@property
|
1036
|
+
def combined(self) -> pd.DataFrame:
|
1037
|
+
"""Return the steady states as a DataFrame."""
|
1038
|
+
return pd.concat((self.variables, self.fluxes), axis=1)
|
1039
|
+
|
1040
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
1041
|
+
"""Iterate over the concentration and flux steady states."""
|
1042
|
+
return iter((self.variables, self.fluxes))
|