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