mxlpy 0.20.0__py3-none-any.whl → 0.22.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 +5 -1
- mxlpy/carousel.py +166 -0
- mxlpy/compare.py +2 -6
- mxlpy/experimental/diff.py +1 -1
- mxlpy/fit.py +386 -44
- mxlpy/identify.py +20 -16
- mxlpy/integrators/int_scipy.py +3 -0
- mxlpy/label_map.py +5 -5
- mxlpy/linear_label_map.py +3 -1
- mxlpy/mc.py +24 -20
- mxlpy/mca.py +9 -7
- mxlpy/meta/__init__.py +5 -3
- mxlpy/meta/codegen_latex.py +44 -30
- mxlpy/meta/codegen_model.py +174 -0
- mxlpy/meta/{codegen_modebase.py → codegen_mxlpy.py} +35 -29
- mxlpy/meta/source_tools.py +408 -167
- mxlpy/meta/sympy_tools.py +117 -0
- mxlpy/model.py +528 -224
- mxlpy/parallel.py +7 -6
- mxlpy/report.py +153 -90
- mxlpy/sbml/_export.py +11 -8
- mxlpy/sbml/_import.py +7 -7
- mxlpy/scan.py +32 -20
- mxlpy/simulator.py +240 -59
- mxlpy/symbolic/symbolic_model.py +29 -17
- mxlpy/types.py +45 -20
- mxlpy/units.py +128 -0
- {mxlpy-0.20.0.dist-info → mxlpy-0.22.0.dist-info}/METADATA +3 -1
- mxlpy-0.22.0.dist-info/RECORD +58 -0
- mxlpy/meta/codegen_py.py +0 -115
- mxlpy-0.20.0.dist-info/RECORD +0 -55
- {mxlpy-0.20.0.dist-info → mxlpy-0.22.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.20.0.dist-info → mxlpy-0.22.0.dist-info}/licenses/LICENSE +0 -0
mxlpy/model.py
CHANGED
@@ -15,9 +15,23 @@ from typing import TYPE_CHECKING, Self, cast
|
|
15
15
|
|
16
16
|
import numpy as np
|
17
17
|
import pandas as pd
|
18
|
+
import sympy
|
18
19
|
|
19
20
|
from mxlpy import fns
|
20
|
-
from mxlpy.
|
21
|
+
from mxlpy.meta.source_tools import fn_to_sympy
|
22
|
+
from mxlpy.meta.sympy_tools import (
|
23
|
+
list_of_symbols,
|
24
|
+
stoichiometries_to_sympy,
|
25
|
+
)
|
26
|
+
from mxlpy.types import (
|
27
|
+
AbstractSurrogate,
|
28
|
+
Array,
|
29
|
+
Derived,
|
30
|
+
Parameter,
|
31
|
+
Reaction,
|
32
|
+
Readout,
|
33
|
+
Variable,
|
34
|
+
)
|
21
35
|
|
22
36
|
if TYPE_CHECKING:
|
23
37
|
from collections.abc import Iterable, Mapping
|
@@ -32,9 +46,38 @@ __all__ = [
|
|
32
46
|
"MissingDependenciesError",
|
33
47
|
"Model",
|
34
48
|
"ModelCache",
|
49
|
+
"TableView",
|
35
50
|
]
|
36
51
|
|
37
52
|
|
53
|
+
def _latex_view(expr: sympy.Expr | None) -> str:
|
54
|
+
if expr is None:
|
55
|
+
return "PARSE-ERROR"
|
56
|
+
return f"${sympy.latex(expr)}$"
|
57
|
+
|
58
|
+
|
59
|
+
@dataclass(kw_only=True, slots=True)
|
60
|
+
class TableView:
|
61
|
+
"""Markdown view of pandas Dataframe.
|
62
|
+
|
63
|
+
Mostly used to get nice LaTeX rendering of sympy expressions.
|
64
|
+
"""
|
65
|
+
|
66
|
+
data: pd.DataFrame
|
67
|
+
|
68
|
+
def __repr__(self) -> str:
|
69
|
+
"""Normal Python shell output."""
|
70
|
+
return self.data.to_markdown()
|
71
|
+
|
72
|
+
def _repr_markdown_(self) -> str:
|
73
|
+
"""Fancy IPython shell output.
|
74
|
+
|
75
|
+
Looks the same as __repr__, but is handled by IPython to output
|
76
|
+
`IPython.display.Markdown`, so looks nice
|
77
|
+
"""
|
78
|
+
return self.data.to_markdown()
|
79
|
+
|
80
|
+
|
38
81
|
@dataclass
|
39
82
|
class Dependency:
|
40
83
|
"""Container class for building dependency tree."""
|
@@ -275,8 +318,8 @@ class Model:
|
|
275
318
|
"""
|
276
319
|
|
277
320
|
_ids: dict[str, str] = field(default_factory=dict)
|
278
|
-
_variables: dict[str,
|
279
|
-
_parameters: dict[str,
|
321
|
+
_variables: dict[str, Variable] = field(default_factory=dict)
|
322
|
+
_parameters: dict[str, Parameter] = field(default_factory=dict)
|
280
323
|
_derived: dict[str, Derived] = field(default_factory=dict)
|
281
324
|
_readouts: dict[str, Readout] = field(default_factory=dict)
|
282
325
|
_reactions: dict[str, Reaction] = field(default_factory=dict)
|
@@ -300,7 +343,7 @@ class Model:
|
|
300
343
|
ModelCache: An instance of ModelCache containing the initialized cache data.
|
301
344
|
|
302
345
|
"""
|
303
|
-
all_parameter_values: dict[str, float] = self.
|
346
|
+
all_parameter_values: dict[str, float] = self.get_parameter_values()
|
304
347
|
all_parameter_names: set[str] = set(all_parameter_values)
|
305
348
|
|
306
349
|
# Sanity checks
|
@@ -317,11 +360,19 @@ class Model:
|
|
317
360
|
self._derived
|
318
361
|
| self._reactions
|
319
362
|
| self._surrogates
|
320
|
-
| {
|
363
|
+
| {
|
364
|
+
k: init
|
365
|
+
for k, v in self._variables.items()
|
366
|
+
if isinstance(init := v.initial_value, Derived)
|
367
|
+
}
|
321
368
|
)
|
322
369
|
order = _sort_dependencies(
|
323
370
|
available=all_parameter_names
|
324
|
-
| {
|
371
|
+
| {
|
372
|
+
k
|
373
|
+
for k, v in self._variables.items()
|
374
|
+
if not isinstance(v.initial_value, Derived)
|
375
|
+
}
|
325
376
|
| set(self._data)
|
326
377
|
| {"time"},
|
327
378
|
elements=[
|
@@ -337,7 +388,11 @@ class Model:
|
|
337
388
|
dependent = (
|
338
389
|
all_parameter_values
|
339
390
|
| self._data
|
340
|
-
| {
|
391
|
+
| {
|
392
|
+
k: init
|
393
|
+
for k, v in self._variables.items()
|
394
|
+
if not isinstance(init := v.initial_value, Derived)
|
395
|
+
}
|
341
396
|
| {"time": 0.0}
|
342
397
|
)
|
343
398
|
for name in order:
|
@@ -393,7 +448,9 @@ class Model:
|
|
393
448
|
dxdt = pd.Series(np.zeros(len(var_names), dtype=float), index=var_names)
|
394
449
|
|
395
450
|
initial_conditions: dict[str, float] = {
|
396
|
-
k:
|
451
|
+
k: init
|
452
|
+
for k, v in self._variables.items()
|
453
|
+
if not isinstance(init := v.initial_value, Derived)
|
397
454
|
}
|
398
455
|
for name in static_order:
|
399
456
|
if name in self._variables:
|
@@ -465,29 +522,88 @@ class Model:
|
|
465
522
|
del self._ids[name]
|
466
523
|
|
467
524
|
##########################################################################
|
468
|
-
# Parameters
|
525
|
+
# Parameters - views
|
469
526
|
##########################################################################
|
470
527
|
|
528
|
+
@property
|
529
|
+
def parameters(self) -> TableView:
|
530
|
+
"""Return view of parameters."""
|
531
|
+
index = list(self._parameters.keys())
|
532
|
+
data = [
|
533
|
+
{
|
534
|
+
"value": el.value,
|
535
|
+
"unit": _latex_view(unit) if (unit := el.unit) is not None else "",
|
536
|
+
# "source": ...,
|
537
|
+
}
|
538
|
+
for el in self._parameters.values()
|
539
|
+
]
|
540
|
+
return TableView(data=pd.DataFrame(data, index=index))
|
541
|
+
|
542
|
+
def get_raw_parameters(self, *, as_copy: bool = True) -> dict[str, Parameter]:
|
543
|
+
"""Returns the parameters of the model."""
|
544
|
+
if as_copy:
|
545
|
+
return copy.deepcopy(self._parameters)
|
546
|
+
return self._parameters
|
547
|
+
|
548
|
+
def get_parameter_values(self) -> dict[str, float]:
|
549
|
+
"""Returns the parameters of the model.
|
550
|
+
|
551
|
+
Examples:
|
552
|
+
>>> model.parameters
|
553
|
+
{"k1": 0.1, "k2": 0.2}
|
554
|
+
|
555
|
+
Returns:
|
556
|
+
parameters: A dictionary where the keys are parameter names (as strings)
|
557
|
+
and the values are parameter values (as floats).
|
558
|
+
|
559
|
+
"""
|
560
|
+
return {k: v.value for k, v in self._parameters.items()}
|
561
|
+
|
562
|
+
def get_parameter_names(self) -> list[str]:
|
563
|
+
"""Retrieve the names of the parameters.
|
564
|
+
|
565
|
+
Examples:
|
566
|
+
>>> model.get_parameter_names()
|
567
|
+
['k1', 'k2']
|
568
|
+
|
569
|
+
Returns:
|
570
|
+
parametes: A list containing the names of the parameters.
|
571
|
+
|
572
|
+
"""
|
573
|
+
return list(self._parameters)
|
574
|
+
|
575
|
+
#####################################
|
576
|
+
# Parameters - create
|
577
|
+
#####################################
|
578
|
+
|
471
579
|
@_invalidate_cache
|
472
|
-
def add_parameter(
|
580
|
+
def add_parameter(
|
581
|
+
self,
|
582
|
+
name: str,
|
583
|
+
value: float,
|
584
|
+
unit: sympy.Expr | None = None,
|
585
|
+
source: str | None = None,
|
586
|
+
) -> Self:
|
473
587
|
"""Adds a parameter to the model.
|
474
588
|
|
475
589
|
Examples:
|
476
590
|
>>> model.add_parameter("k1", 0.1)
|
477
591
|
|
478
592
|
Args:
|
479
|
-
name
|
480
|
-
value
|
593
|
+
name: The name of the parameter.
|
594
|
+
value: The value of the parameter.
|
595
|
+
unit: unit of the parameter
|
596
|
+
source: source of the information given
|
481
597
|
|
482
598
|
Returns:
|
483
599
|
Self: The instance of the model with the added parameter.
|
484
600
|
|
485
601
|
"""
|
486
602
|
self._insert_id(name=name, ctx="parameter")
|
487
|
-
self._parameters[name] = value
|
603
|
+
self._parameters[name] = Parameter(value=value, unit=unit, source=source)
|
488
604
|
return self
|
489
605
|
|
490
|
-
def add_parameters(self, parameters:
|
606
|
+
def add_parameters(self, parameters: Mapping[str, float | Parameter]) -> Self:
|
491
607
|
"""Adds multiple parameters to the model.
|
492
608
|
|
493
609
|
Examples:
|
@@ -502,36 +618,15 @@ class Model:
|
|
502
618
|
|
503
619
|
"""
|
504
620
|
for k, v in parameters.items():
|
505
|
-
|
621
|
+
if isinstance(v, Parameter):
|
622
|
+
self.add_parameter(k, v.value, unit=v.unit, source=v.source)
|
623
|
+
else:
|
624
|
+
self.add_parameter(k, v)
|
506
625
|
return self
|
507
626
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
Examples:
|
513
|
-
>>> model.parameters
|
514
|
-
{"k1": 0.1, "k2": 0.2}
|
515
|
-
|
516
|
-
Returns:
|
517
|
-
parameters: A dictionary where the keys are parameter names (as strings)
|
518
|
-
and the values are parameter values (as floats).
|
519
|
-
|
520
|
-
"""
|
521
|
-
return self._parameters.copy()
|
522
|
-
|
523
|
-
def get_parameter_names(self) -> list[str]:
|
524
|
-
"""Retrieve the names of the parameters.
|
525
|
-
|
526
|
-
Examples:
|
527
|
-
>>> model.get_parameter_names()
|
528
|
-
['k1', 'k2']
|
529
|
-
|
530
|
-
Returns:
|
531
|
-
parametes: A list containing the names of the parameters.
|
532
|
-
|
533
|
-
"""
|
534
|
-
return list(self._parameters)
|
627
|
+
#####################################
|
628
|
+
# Parameters - delete
|
629
|
+
#####################################
|
535
630
|
|
536
631
|
@_invalidate_cache
|
537
632
|
def remove_parameter(self, name: str) -> Self:
|
@@ -568,8 +663,19 @@ class Model:
|
|
568
663
|
self.remove_parameter(name)
|
569
664
|
return self
|
570
665
|
|
666
|
+
#####################################
|
667
|
+
# Parameters - update
|
668
|
+
#####################################
|
669
|
+
|
571
670
|
@_invalidate_cache
|
572
|
-
def update_parameter(
|
671
|
+
def update_parameter(
|
672
|
+
self,
|
673
|
+
name: str,
|
674
|
+
value: float | None = None,
|
675
|
+
*,
|
676
|
+
unit: sympy.Expr | None = None,
|
677
|
+
source: str | None = None,
|
678
|
+
) -> Self:
|
573
679
|
"""Update the value of a parameter.
|
574
680
|
|
575
681
|
Examples:
|
@@ -578,6 +684,8 @@ class Model:
|
|
578
684
|
Args:
|
579
685
|
name: The name of the parameter to update.
|
580
686
|
value: The new value for the parameter.
|
687
|
+
unit: Unit of the parameter
|
688
|
+
source: Source of the information
|
581
689
|
|
582
690
|
Returns:
|
583
691
|
Self: The instance of the class with the updated parameter.
|
@@ -589,10 +697,17 @@ class Model:
|
|
589
697
|
if name not in self._parameters:
|
590
698
|
msg = f"'{name}' not found in parameters"
|
591
699
|
raise KeyError(msg)
|
592
|
-
|
700
|
+
|
701
|
+
parameter = self._parameters[name]
|
702
|
+
if value is not None:
|
703
|
+
parameter.value = value
|
704
|
+
if unit is not None:
|
705
|
+
parameter.unit = unit
|
706
|
+
if source is not None:
|
707
|
+
parameter.source = source
|
593
708
|
return self
|
594
709
|
|
595
|
-
def update_parameters(self, parameters:
|
710
|
+
def update_parameters(self, parameters: Mapping[str, float | Parameter]) -> Self:
|
596
711
|
"""Update multiple parameters of the model.
|
597
712
|
|
598
713
|
Examples:
|
@@ -606,7 +721,10 @@ class Model:
|
|
606
721
|
|
607
722
|
"""
|
608
723
|
for k, v in parameters.items():
|
609
|
-
|
724
|
+
if isinstance(v, Parameter):
|
725
|
+
self.update_parameter(k, value=v.value, unit=v.unit, source=v.source)
|
726
|
+
else:
|
727
|
+
self.update_parameter(k, v)
|
610
728
|
return self
|
611
729
|
|
612
730
|
def scale_parameter(self, name: str, factor: float) -> Self:
|
@@ -623,7 +741,7 @@ class Model:
|
|
623
741
|
Self: The instance of the class with the updated parameter.
|
624
742
|
|
625
743
|
"""
|
626
|
-
return self.update_parameter(name, self._parameters[name] * factor)
|
744
|
+
return self.update_parameter(name, self._parameters[name].value * factor)
|
627
745
|
|
628
746
|
def scale_parameters(self, parameters: dict[str, float]) -> Self:
|
629
747
|
"""Scales the parameters of the model.
|
@@ -667,7 +785,7 @@ class Model:
|
|
667
785
|
Self: The instance of the model with the parameter converted to a variable.
|
668
786
|
|
669
787
|
"""
|
670
|
-
value = self._parameters[name] if initial_value is None else initial_value
|
788
|
+
value = self._parameters[name].value if initial_value is None else initial_value
|
671
789
|
self.remove_parameter(name)
|
672
790
|
self.add_variable(name, value)
|
673
791
|
|
@@ -708,7 +826,7 @@ class Model:
|
|
708
826
|
##########################################################################
|
709
827
|
|
710
828
|
@property
|
711
|
-
def variables(self) ->
|
829
|
+
def variables(self) -> TableView:
|
712
830
|
"""Returns a copy of the variables dictionary.
|
713
831
|
|
714
832
|
Examples:
|
@@ -722,10 +840,79 @@ class Model:
|
|
722
840
|
dict[str, float]: A copy of the variables dictionary.
|
723
841
|
|
724
842
|
"""
|
725
|
-
|
843
|
+
index = list(self._variables.keys())
|
844
|
+
data = []
|
845
|
+
for name, el in self._variables.items():
|
846
|
+
if isinstance(init := el.initial_value, Derived):
|
847
|
+
value_str = _latex_view(
|
848
|
+
fn_to_sympy(
|
849
|
+
init.fn,
|
850
|
+
origin=name,
|
851
|
+
model_args=list_of_symbols(init.args),
|
852
|
+
)
|
853
|
+
)
|
854
|
+
else:
|
855
|
+
value_str = str(init)
|
856
|
+
data.append(
|
857
|
+
{
|
858
|
+
"value": value_str,
|
859
|
+
"unit": _latex_view(unit) if (unit := el.unit) is not None else "",
|
860
|
+
# "source"
|
861
|
+
}
|
862
|
+
)
|
863
|
+
return TableView(data=pd.DataFrame(data, index=index))
|
864
|
+
|
865
|
+
def get_raw_variables(self, *, as_copy: bool = True) -> dict[str, Variable]:
|
866
|
+
"""Retrieve the initial conditions of the model.
|
867
|
+
|
868
|
+
Examples:
|
869
|
+
>>> model.get_initial_conditions()
|
870
|
+
{"x1": 1.0, "x2": 2.0}
|
871
|
+
|
872
|
+
Returns:
|
873
|
+
initial_conditions: A dictionary where the keys are variable names and the values are their initial conditions.
|
874
|
+
|
875
|
+
"""
|
876
|
+
if as_copy:
|
877
|
+
return copy.deepcopy(self._variables)
|
878
|
+
return self._variables
|
879
|
+
|
880
|
+
def get_initial_conditions(self) -> dict[str, float]:
|
881
|
+
"""Retrieve the initial conditions of the model.
|
882
|
+
|
883
|
+
Examples:
|
884
|
+
>>> model.get_initial_conditions()
|
885
|
+
{"x1": 1.0, "x2": 2.0}
|
886
|
+
|
887
|
+
Returns:
|
888
|
+
initial_conditions: A dictionary where the keys are variable names and the values are their initial conditions.
|
889
|
+
|
890
|
+
"""
|
891
|
+
if (cache := self._cache) is None:
|
892
|
+
cache = self._create_cache()
|
893
|
+
return cache.initial_conditions
|
894
|
+
|
895
|
+
def get_variable_names(self) -> list[str]:
|
896
|
+
"""Retrieve the names of all variables.
|
897
|
+
|
898
|
+
Examples:
|
899
|
+
>>> model.get_variable_names()
|
900
|
+
["x1", "x2"]
|
901
|
+
|
902
|
+
Returns:
|
903
|
+
variable_names: A list containing the names of all variables.
|
904
|
+
|
905
|
+
"""
|
906
|
+
return list(self._variables)
|
726
907
|
|
727
908
|
@_invalidate_cache
|
728
|
-
def add_variable(
|
909
|
+
def add_variable(
|
910
|
+
self,
|
911
|
+
name: str,
|
912
|
+
initial_value: float | Derived,
|
913
|
+
unit: sympy.Expr | None = None,
|
914
|
+
source: str | None = None,
|
915
|
+
) -> Self:
|
729
916
|
"""Adds a variable to the model with the given name and initial condition.
|
730
917
|
|
731
918
|
Examples:
|
@@ -733,17 +920,23 @@ class Model:
|
|
733
920
|
|
734
921
|
Args:
|
735
922
|
name: The name of the variable to add.
|
736
|
-
|
923
|
+
initial_value: The initial condition value for the variable.
|
924
|
+
unit: unit of the variable
|
925
|
+
source: source of the information given
|
737
926
|
|
738
927
|
Returns:
|
739
928
|
Self: The instance of the model with the added variable.
|
740
929
|
|
741
930
|
"""
|
742
931
|
self._insert_id(name=name, ctx="variable")
|
743
|
-
self._variables[name] =
|
932
|
+
self._variables[name] = Variable(
|
933
|
+
initial_value=initial_value, unit=unit, source=source
|
934
|
+
)
|
744
935
|
return self
|
745
936
|
|
746
|
-
def add_variables(
|
937
|
+
def add_variables(
|
938
|
+
self, variables: Mapping[str, float | Variable | Derived]
|
939
|
+
) -> Self:
|
747
940
|
"""Adds multiple variables to the model with their initial conditions.
|
748
941
|
|
749
942
|
Examples:
|
@@ -757,8 +950,16 @@ class Model:
|
|
757
950
|
Self: The instance of the model with the added variables.
|
758
951
|
|
759
952
|
"""
|
760
|
-
for name,
|
761
|
-
|
953
|
+
for name, v in variables.items():
|
954
|
+
if isinstance(v, Variable):
|
955
|
+
self.add_variable(
|
956
|
+
name=name,
|
957
|
+
initial_value=v.initial_value,
|
958
|
+
unit=v.unit,
|
959
|
+
source=v.source,
|
960
|
+
)
|
961
|
+
else:
|
962
|
+
self.add_variable(name=name, initial_value=v)
|
762
963
|
return self
|
763
964
|
|
764
965
|
@_invalidate_cache
|
@@ -797,7 +998,13 @@ class Model:
|
|
797
998
|
return self
|
798
999
|
|
799
1000
|
@_invalidate_cache
|
800
|
-
def update_variable(
|
1001
|
+
def update_variable(
|
1002
|
+
self,
|
1003
|
+
name: str,
|
1004
|
+
initial_value: float | Derived,
|
1005
|
+
unit: sympy.Expr | None = None,
|
1006
|
+
source: str | None = None,
|
1007
|
+
) -> Self:
|
801
1008
|
"""Updates the value of a variable in the model.
|
802
1009
|
|
803
1010
|
Examples:
|
@@ -805,7 +1012,9 @@ class Model:
|
|
805
1012
|
|
806
1013
|
Args:
|
807
1014
|
name: The name of the variable to update.
|
808
|
-
|
1015
|
+
initial_value: The initial condition or value to set for the variable.
|
1016
|
+
unit: Unit of the variable
|
1017
|
+
source: Source of the information
|
809
1018
|
|
810
1019
|
Returns:
|
811
1020
|
Self: The instance of the model with the updated variable.
|
@@ -814,10 +1023,20 @@ class Model:
|
|
814
1023
|
if name not in self._variables:
|
815
1024
|
msg = f"'{name}' not found in variables"
|
816
1025
|
raise KeyError(msg)
|
817
|
-
|
1026
|
+
|
1027
|
+
variable = self._variables[name]
|
1028
|
+
|
1029
|
+
if initial_value is not None:
|
1030
|
+
variable.initial_value = initial_value
|
1031
|
+
if unit is not None:
|
1032
|
+
variable.unit = unit
|
1033
|
+
if source is not None:
|
1034
|
+
variable.source = source
|
818
1035
|
return self
|
819
1036
|
|
820
|
-
def update_variables(
|
1037
|
+
def update_variables(
|
1038
|
+
self, variables: Mapping[str, float | Derived | Variable]
|
1039
|
+
) -> Self:
|
821
1040
|
"""Updates multiple variables in the model.
|
822
1041
|
|
823
1042
|
Examples:
|
@@ -831,37 +1050,17 @@ class Model:
|
|
831
1050
|
|
832
1051
|
"""
|
833
1052
|
for k, v in variables.items():
|
834
|
-
|
1053
|
+
if isinstance(v, Variable):
|
1054
|
+
self.update_variable(
|
1055
|
+
k,
|
1056
|
+
initial_value=v.initial_value,
|
1057
|
+
unit=v.unit,
|
1058
|
+
source=v.source,
|
1059
|
+
)
|
1060
|
+
else:
|
1061
|
+
self.update_variable(k, v)
|
835
1062
|
return self
|
836
1063
|
|
837
|
-
def get_variable_names(self) -> list[str]:
|
838
|
-
"""Retrieve the names of all variables.
|
839
|
-
|
840
|
-
Examples:
|
841
|
-
>>> model.get_variable_names()
|
842
|
-
["x1", "x2"]
|
843
|
-
|
844
|
-
Returns:
|
845
|
-
variable_names: A list containing the names of all variables.
|
846
|
-
|
847
|
-
"""
|
848
|
-
return list(self._variables)
|
849
|
-
|
850
|
-
def get_initial_conditions(self) -> dict[str, float]:
|
851
|
-
"""Retrieve the initial conditions of the model.
|
852
|
-
|
853
|
-
Examples:
|
854
|
-
>>> model.get_initial_conditions()
|
855
|
-
{"x1": 1.0, "x2": 2.0}
|
856
|
-
|
857
|
-
Returns:
|
858
|
-
initial_conditions: A dictionary where the keys are variable names and the values are their initial conditions.
|
859
|
-
|
860
|
-
"""
|
861
|
-
if (cache := self._cache) is None:
|
862
|
-
cache = self._create_cache()
|
863
|
-
return cache.initial_conditions
|
864
|
-
|
865
1064
|
def make_variable_static(self, name: str, value: float | None = None) -> Self:
|
866
1065
|
"""Converts a variable to a static parameter.
|
867
1066
|
|
@@ -881,8 +1080,12 @@ class Model:
|
|
881
1080
|
Self: The instance of the class for method chaining.
|
882
1081
|
|
883
1082
|
"""
|
884
|
-
value_or_derived =
|
1083
|
+
value_or_derived = (
|
1084
|
+
self._variables[name].initial_value if value is None else value
|
1085
|
+
)
|
885
1086
|
self.remove_variable(name)
|
1087
|
+
|
1088
|
+
# FIXME: better handling of unit
|
886
1089
|
if isinstance(value_or_derived, Derived):
|
887
1090
|
self.add_derived(name, value_or_derived.fn, args=value_or_derived.args)
|
888
1091
|
else:
|
@@ -901,12 +1104,12 @@ class Model:
|
|
901
1104
|
return self
|
902
1105
|
|
903
1106
|
##########################################################################
|
904
|
-
# Derived
|
1107
|
+
# Derived - views
|
905
1108
|
##########################################################################
|
906
1109
|
|
907
1110
|
@property
|
908
|
-
def derived(self) ->
|
909
|
-
"""Returns a
|
1111
|
+
def derived(self) -> TableView:
|
1112
|
+
"""Returns a view of the derived quantities.
|
910
1113
|
|
911
1114
|
Examples:
|
912
1115
|
>>> model.derived
|
@@ -917,10 +1120,30 @@ class Model:
|
|
917
1120
|
dict[str, Derived]: A copy of the derived dictionary.
|
918
1121
|
|
919
1122
|
"""
|
920
|
-
|
1123
|
+
index = list(self._derived.keys())
|
1124
|
+
data = [
|
1125
|
+
{
|
1126
|
+
"value": _latex_view(
|
1127
|
+
fn_to_sympy(
|
1128
|
+
el.fn,
|
1129
|
+
origin=name,
|
1130
|
+
model_args=list_of_symbols(el.args),
|
1131
|
+
)
|
1132
|
+
),
|
1133
|
+
"unit": _latex_view(unit) if (unit := el.unit) is not None else "",
|
1134
|
+
}
|
1135
|
+
for name, el in self._derived.items()
|
1136
|
+
]
|
921
1137
|
|
922
|
-
|
923
|
-
|
1138
|
+
return TableView(data=pd.DataFrame(data, index=index))
|
1139
|
+
|
1140
|
+
def get_raw_derived(self, *, as_copy: bool = True) -> dict[str, Derived]:
|
1141
|
+
"""Get copy of derived values."""
|
1142
|
+
if as_copy:
|
1143
|
+
return copy.deepcopy(self._derived)
|
1144
|
+
return self._derived
|
1145
|
+
|
1146
|
+
def get_derived_variables(self) -> dict[str, Derived]:
|
924
1147
|
"""Returns a dictionary of derived variables.
|
925
1148
|
|
926
1149
|
Examples:
|
@@ -940,8 +1163,7 @@ class Model:
|
|
940
1163
|
|
941
1164
|
return {k: v for k, v in derived.items() if k not in cache.all_parameter_values}
|
942
1165
|
|
943
|
-
|
944
|
-
def derived_parameters(self) -> dict[str, Derived]:
|
1166
|
+
def get_derived_parameters(self) -> dict[str, Derived]:
|
945
1167
|
"""Returns a dictionary of derived parameters.
|
946
1168
|
|
947
1169
|
Examples:
|
@@ -966,6 +1188,7 @@ class Model:
|
|
966
1188
|
fn: RateFn,
|
967
1189
|
*,
|
968
1190
|
args: list[str],
|
1191
|
+
unit: sympy.Expr | None = None,
|
969
1192
|
) -> Self:
|
970
1193
|
"""Adds a derived attribute to the model.
|
971
1194
|
|
@@ -976,13 +1199,14 @@ class Model:
|
|
976
1199
|
name: The name of the derived attribute.
|
977
1200
|
fn: The function used to compute the derived attribute.
|
978
1201
|
args: The list of arguments to be passed to the function.
|
1202
|
+
unit: Unit of the derived value
|
979
1203
|
|
980
1204
|
Returns:
|
981
1205
|
Self: The instance of the model with the added derived attribute.
|
982
1206
|
|
983
1207
|
"""
|
984
1208
|
self._insert_id(name=name, ctx="derived")
|
985
|
-
self._derived[name] = Derived(fn=fn, args=args)
|
1209
|
+
self._derived[name] = Derived(fn=fn, args=args, unit=unit)
|
986
1210
|
return self
|
987
1211
|
|
988
1212
|
def get_derived_parameter_names(self) -> list[str]:
|
@@ -996,7 +1220,7 @@ class Model:
|
|
996
1220
|
A list of names of the derived parameters.
|
997
1221
|
|
998
1222
|
"""
|
999
|
-
return list(self.
|
1223
|
+
return list(self.get_derived_parameters())
|
1000
1224
|
|
1001
1225
|
def get_derived_variable_names(self) -> list[str]:
|
1002
1226
|
"""Retrieve the names of derived variables.
|
@@ -1009,7 +1233,7 @@ class Model:
|
|
1009
1233
|
A list of names of derived variables.
|
1010
1234
|
|
1011
1235
|
"""
|
1012
|
-
return list(self.
|
1236
|
+
return list(self.get_derived_variables())
|
1013
1237
|
|
1014
1238
|
@_invalidate_cache
|
1015
1239
|
def update_derived(
|
@@ -1018,6 +1242,7 @@ class Model:
|
|
1018
1242
|
fn: RateFn | None = None,
|
1019
1243
|
*,
|
1020
1244
|
args: list[str] | None = None,
|
1245
|
+
unit: sympy.Expr | None = None,
|
1021
1246
|
) -> Self:
|
1022
1247
|
"""Updates the derived function and its arguments for a given name.
|
1023
1248
|
|
@@ -1026,16 +1251,21 @@ class Model:
|
|
1026
1251
|
|
1027
1252
|
Args:
|
1028
1253
|
name: The name of the derived function to update.
|
1029
|
-
fn: The new derived function. If None, the existing function is retained.
|
1030
|
-
args: The new arguments for the derived function. If None, the existing arguments are retained.
|
1254
|
+
fn: The new derived function. If None, the existing function is retained.
|
1255
|
+
args: The new arguments for the derived function. If None, the existing arguments are retained.
|
1256
|
+
unit: Unit of the derived value
|
1031
1257
|
|
1032
1258
|
Returns:
|
1033
1259
|
Self: The instance of the class with the updated derived function and arguments.
|
1034
1260
|
|
1035
1261
|
"""
|
1036
1262
|
der = self._derived[name]
|
1037
|
-
|
1038
|
-
|
1263
|
+
if fn is not None:
|
1264
|
+
der.fn = fn
|
1265
|
+
if args is not None:
|
1266
|
+
der.args = args
|
1267
|
+
if unit is not None:
|
1268
|
+
der.unit = unit
|
1039
1269
|
return self
|
1040
1270
|
|
1041
1271
|
@_invalidate_cache
|
@@ -1061,7 +1291,27 @@ class Model:
|
|
1061
1291
|
###########################################################################
|
1062
1292
|
|
1063
1293
|
@property
|
1064
|
-
def reactions(self) ->
|
1294
|
+
def reactions(self) -> TableView:
|
1295
|
+
"""Get view of reactions."""
|
1296
|
+
index = list(self._reactions.keys())
|
1297
|
+
data = [
|
1298
|
+
{
|
1299
|
+
"value": _latex_view(
|
1300
|
+
fn_to_sympy(
|
1301
|
+
rxn.fn,
|
1302
|
+
origin=name,
|
1303
|
+
model_args=list_of_symbols(rxn.args),
|
1304
|
+
)
|
1305
|
+
),
|
1306
|
+
"stoichiometry": stoichiometries_to_sympy(name, rxn.stoichiometry),
|
1307
|
+
"unit": _latex_view(unit) if (unit := rxn.unit) is not None else "",
|
1308
|
+
# "source"
|
1309
|
+
}
|
1310
|
+
for name, rxn in self._reactions.items()
|
1311
|
+
]
|
1312
|
+
return TableView(data=pd.DataFrame(data, index=index))
|
1313
|
+
|
1314
|
+
def get_raw_reactions(self, *, as_copy: bool = True) -> dict[str, Reaction]:
|
1065
1315
|
"""Retrieve the reactions in the model.
|
1066
1316
|
|
1067
1317
|
Examples:
|
@@ -1072,7 +1322,9 @@ class Model:
|
|
1072
1322
|
dict[str, Reaction]: A deep copy of the reactions dictionary.
|
1073
1323
|
|
1074
1324
|
"""
|
1075
|
-
|
1325
|
+
if as_copy:
|
1326
|
+
return copy.deepcopy(self._reactions)
|
1327
|
+
return self._reactions
|
1076
1328
|
|
1077
1329
|
def get_stoichiometries(
|
1078
1330
|
self, variables: dict[str, float] | None = None, time: float = 0.0
|
@@ -1091,7 +1343,7 @@ class Model:
|
|
1091
1343
|
"""
|
1092
1344
|
if (cache := self._cache) is None:
|
1093
1345
|
cache = self._create_cache()
|
1094
|
-
args = self.
|
1346
|
+
args = self.get_args(variables=variables, time=time)
|
1095
1347
|
|
1096
1348
|
stoich_by_cpds = copy.deepcopy(cache.stoich_by_cpds)
|
1097
1349
|
for cpd, stoich in cache.dyn_stoich_by_cpds.items():
|
@@ -1121,7 +1373,7 @@ class Model:
|
|
1121
1373
|
"""
|
1122
1374
|
if (cache := self._cache) is None:
|
1123
1375
|
cache = self._create_cache()
|
1124
|
-
args = self.
|
1376
|
+
args = self.get_args(variables=variables, time=time)
|
1125
1377
|
|
1126
1378
|
stoich = copy.deepcopy(cache.stoich_by_cpds[variable])
|
1127
1379
|
for rxn, derived in cache.dyn_stoich_by_cpds.get(variable, {}).items():
|
@@ -1155,6 +1407,8 @@ class Model:
|
|
1155
1407
|
*,
|
1156
1408
|
args: list[str],
|
1157
1409
|
stoichiometry: Mapping[str, float | str | Derived],
|
1410
|
+
unit: sympy.Expr | None = None,
|
1411
|
+
# source: str | None = None,
|
1158
1412
|
) -> Self:
|
1159
1413
|
"""Adds a reaction to the model.
|
1160
1414
|
|
@@ -1170,6 +1424,7 @@ class Model:
|
|
1170
1424
|
fn: The function representing the reaction.
|
1171
1425
|
args: A list of arguments for the reaction function.
|
1172
1426
|
stoichiometry: The stoichiometry of the reaction, mapping species to their coefficients.
|
1427
|
+
unit: Unit of the rate
|
1173
1428
|
|
1174
1429
|
Returns:
|
1175
1430
|
Self: The instance of the model with the added reaction.
|
@@ -1181,7 +1436,12 @@ class Model:
|
|
1181
1436
|
k: Derived(fn=fns.constant, args=[v]) if isinstance(v, str) else v
|
1182
1437
|
for k, v in stoichiometry.items()
|
1183
1438
|
}
|
1184
|
-
self._reactions[name] = Reaction(
|
1439
|
+
self._reactions[name] = Reaction(
|
1440
|
+
fn=fn,
|
1441
|
+
stoichiometry=stoich,
|
1442
|
+
args=args,
|
1443
|
+
unit=unit,
|
1444
|
+
)
|
1185
1445
|
return self
|
1186
1446
|
|
1187
1447
|
def get_reaction_names(self) -> list[str]:
|
@@ -1205,6 +1465,7 @@ class Model:
|
|
1205
1465
|
*,
|
1206
1466
|
args: list[str] | None = None,
|
1207
1467
|
stoichiometry: Mapping[str, float | Derived | str] | None = None,
|
1468
|
+
unit: sympy.Expr | None = None,
|
1208
1469
|
) -> Self:
|
1209
1470
|
"""Updates the properties of an existing reaction in the model.
|
1210
1471
|
|
@@ -1220,6 +1481,7 @@ class Model:
|
|
1220
1481
|
fn: The new function for the reaction. If None, the existing function is retained.
|
1221
1482
|
args: The new arguments for the reaction. If None, the existing arguments are retained.
|
1222
1483
|
stoichiometry: The new stoichiometry for the reaction. If None, the existing stoichiometry is retained.
|
1484
|
+
unit: Unit of the reaction
|
1223
1485
|
|
1224
1486
|
Returns:
|
1225
1487
|
Self: The instance of the model with the updated reaction.
|
@@ -1235,6 +1497,7 @@ class Model:
|
|
1235
1497
|
}
|
1236
1498
|
rxn.stoichiometry = stoich
|
1237
1499
|
rxn.args = rxn.args if args is None else args
|
1500
|
+
rxn.unit = rxn.unit if unit is None else unit
|
1238
1501
|
return self
|
1239
1502
|
|
1240
1503
|
@_invalidate_cache
|
@@ -1285,7 +1548,14 @@ class Model:
|
|
1285
1548
|
# Think of something like NADPH / (NADP + NADPH) as a proxy for energy state
|
1286
1549
|
##########################################################################
|
1287
1550
|
|
1288
|
-
def add_readout(
|
1551
|
+
def add_readout(
|
1552
|
+
self,
|
1553
|
+
name: str,
|
1554
|
+
fn: RateFn,
|
1555
|
+
*,
|
1556
|
+
args: list[str],
|
1557
|
+
unit: sympy.Expr | None = None,
|
1558
|
+
) -> Self:
|
1289
1559
|
"""Adds a readout to the model.
|
1290
1560
|
|
1291
1561
|
Examples:
|
@@ -1298,13 +1568,14 @@ class Model:
|
|
1298
1568
|
name: The name of the readout.
|
1299
1569
|
fn: The function to be used for the readout.
|
1300
1570
|
args: The list of arguments for the function.
|
1571
|
+
unit: Unit of the readout
|
1301
1572
|
|
1302
1573
|
Returns:
|
1303
1574
|
Self: The instance of the model with the added readout.
|
1304
1575
|
|
1305
1576
|
"""
|
1306
1577
|
self._insert_id(name=name, ctx="readout")
|
1307
|
-
self._readouts[name] = Readout(fn=fn, args=args)
|
1578
|
+
self._readouts[name] = Readout(fn=fn, args=args, unit=unit)
|
1308
1579
|
return self
|
1309
1580
|
|
1310
1581
|
def get_readout_names(self) -> list[str]:
|
@@ -1320,6 +1591,12 @@ class Model:
|
|
1320
1591
|
"""
|
1321
1592
|
return list(self._readouts)
|
1322
1593
|
|
1594
|
+
def get_raw_readouts(self, *, as_copy: bool = True) -> dict[str, Readout]:
|
1595
|
+
"""Get copy of readouts in the model."""
|
1596
|
+
if as_copy:
|
1597
|
+
return copy.deepcopy(self._readouts)
|
1598
|
+
return self._readouts
|
1599
|
+
|
1323
1600
|
def remove_readout(self, name: str) -> Self:
|
1324
1601
|
"""Remove a readout by its name.
|
1325
1602
|
|
@@ -1428,6 +1705,21 @@ class Model:
|
|
1428
1705
|
self._surrogates.pop(name)
|
1429
1706
|
return self
|
1430
1707
|
|
1708
|
+
def get_raw_surrogates(
|
1709
|
+
self, *, as_copy: bool = True
|
1710
|
+
) -> dict[str, AbstractSurrogate]:
|
1711
|
+
"""Get direct copies of model surrogates."""
|
1712
|
+
if as_copy:
|
1713
|
+
return copy.deepcopy(self._surrogates)
|
1714
|
+
return self._surrogates
|
1715
|
+
|
1716
|
+
def get_surrogate_output_names(self) -> list[str]:
|
1717
|
+
"""Return output names by surrogates."""
|
1718
|
+
names = []
|
1719
|
+
for i in self._surrogates.values():
|
1720
|
+
names.extend(i.outputs)
|
1721
|
+
return names
|
1722
|
+
|
1431
1723
|
def get_surrogate_reaction_names(self) -> list[str]:
|
1432
1724
|
"""Return reaction names by surrogates."""
|
1433
1725
|
names = []
|
@@ -1464,7 +1756,39 @@ class Model:
|
|
1464
1756
|
# - readouts
|
1465
1757
|
##########################################################################
|
1466
1758
|
|
1467
|
-
def
|
1759
|
+
def get_arg_names(
|
1760
|
+
self,
|
1761
|
+
*,
|
1762
|
+
include_time: bool,
|
1763
|
+
include_variables: bool,
|
1764
|
+
include_parameters: bool,
|
1765
|
+
include_derived_parameters: bool,
|
1766
|
+
include_derived_variables: bool,
|
1767
|
+
include_reactions: bool,
|
1768
|
+
include_surrogate_outputs: bool,
|
1769
|
+
include_readouts: bool,
|
1770
|
+
) -> list[str]:
|
1771
|
+
"""Get names of all kinds of model components."""
|
1772
|
+
names = []
|
1773
|
+
if include_time:
|
1774
|
+
names.append("time")
|
1775
|
+
if include_variables:
|
1776
|
+
names.extend(self.get_variable_names())
|
1777
|
+
if include_parameters:
|
1778
|
+
names.extend(self.get_parameter_names())
|
1779
|
+
if include_derived_variables:
|
1780
|
+
names.extend(self.get_derived_variable_names())
|
1781
|
+
if include_derived_parameters:
|
1782
|
+
names.extend(self.get_derived_parameter_names())
|
1783
|
+
if include_reactions:
|
1784
|
+
names.extend(self.get_reaction_names())
|
1785
|
+
if include_surrogate_outputs:
|
1786
|
+
names.extend(self.get_surrogate_output_names())
|
1787
|
+
if include_readouts:
|
1788
|
+
names.extend(self.get_readout_names())
|
1789
|
+
return names
|
1790
|
+
|
1791
|
+
def _get_args(
|
1468
1792
|
self,
|
1469
1793
|
variables: dict[str, float],
|
1470
1794
|
time: float = 0.0,
|
@@ -1474,7 +1798,7 @@ class Model:
|
|
1474
1798
|
"""Generate a dictionary of model components dependent on other components.
|
1475
1799
|
|
1476
1800
|
Examples:
|
1477
|
-
>>> model.
|
1801
|
+
>>> model._get_args({"x1": 1.0, "x2": 2.0}, time=0.0)
|
1478
1802
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1479
1803
|
|
1480
1804
|
Args:
|
@@ -1502,11 +1826,18 @@ class Model:
|
|
1502
1826
|
|
1503
1827
|
return cast(dict[str, float], args)
|
1504
1828
|
|
1505
|
-
def
|
1829
|
+
def get_args(
|
1506
1830
|
self,
|
1507
1831
|
variables: dict[str, float] | None = None,
|
1508
1832
|
time: float = 0.0,
|
1509
1833
|
*,
|
1834
|
+
include_time: bool = True,
|
1835
|
+
include_variables: bool = True,
|
1836
|
+
include_parameters: bool = True,
|
1837
|
+
include_derived_parameters: bool = True,
|
1838
|
+
include_derived_variables: bool = True,
|
1839
|
+
include_reactions: bool = True,
|
1840
|
+
include_surrogate_outputs: bool = True,
|
1510
1841
|
include_readouts: bool = False,
|
1511
1842
|
) -> pd.Series:
|
1512
1843
|
"""Generate a pandas Series of arguments for the model.
|
@@ -1514,20 +1845,27 @@ class Model:
|
|
1514
1845
|
Examples:
|
1515
1846
|
# Using initial conditions
|
1516
1847
|
>>> model.get_args()
|
1517
|
-
{"x1": 1.
|
1848
|
+
{"x1": 1.get_args, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1518
1849
|
|
1519
1850
|
# With custom concentrations
|
1520
|
-
>>> model.
|
1851
|
+
>>> model.get_args({"x1": 1.0, "x2": 2.0})
|
1521
1852
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1522
1853
|
|
1523
1854
|
# With custom concentrations and time
|
1524
|
-
>>> model.
|
1855
|
+
>>> model.get_args({"x1": 1.0, "x2": 2.0}, time=1.0)
|
1525
1856
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 1.0}
|
1526
1857
|
|
1527
1858
|
Args:
|
1528
1859
|
variables: A dictionary where keys are the names of the concentrations and values are their respective float values.
|
1529
|
-
time: The time point at which the arguments are generated
|
1530
|
-
|
1860
|
+
time: The time point at which the arguments are generated.
|
1861
|
+
include_time: Whether to include the time as an argument
|
1862
|
+
include_variables: Whether to include variables
|
1863
|
+
include_parameters: Whether to include parameters
|
1864
|
+
include_derived_parameters: Whether to include derived parameters
|
1865
|
+
include_derived_variables: Whether to include derived variables
|
1866
|
+
include_reactions: Whether to include reactions
|
1867
|
+
include_surrogate_outputs: Whether to include surrogate outputs
|
1868
|
+
include_readouts: Whether to include readouts
|
1531
1869
|
|
1532
1870
|
Returns:
|
1533
1871
|
A pandas Series containing the generated arguments with float dtype.
|
@@ -1535,111 +1873,60 @@ class Model:
|
|
1535
1873
|
"""
|
1536
1874
|
if (cache := self._cache) is None:
|
1537
1875
|
cache = self._create_cache()
|
1538
|
-
|
1539
|
-
args = self._get_dependent(
|
1876
|
+
raw = self._get_args(
|
1540
1877
|
variables=self.get_initial_conditions() if variables is None else variables,
|
1541
1878
|
time=time,
|
1542
1879
|
cache=cache,
|
1543
1880
|
)
|
1544
|
-
|
1545
1881
|
if include_readouts:
|
1546
1882
|
for name, ro in self._readouts.items(): # FIXME: order?
|
1547
|
-
ro.calculate_inpl(name,
|
1548
|
-
|
1549
|
-
return
|
1883
|
+
ro.calculate_inpl(name, raw)
|
1884
|
+
args = pd.Series(raw, dtype=float)
|
1885
|
+
return args.loc[
|
1886
|
+
self.get_arg_names(
|
1887
|
+
include_time=include_time,
|
1888
|
+
include_variables=include_variables,
|
1889
|
+
include_parameters=include_parameters,
|
1890
|
+
include_derived_parameters=include_derived_parameters,
|
1891
|
+
include_derived_variables=include_derived_variables,
|
1892
|
+
include_reactions=include_reactions,
|
1893
|
+
include_surrogate_outputs=include_surrogate_outputs,
|
1894
|
+
include_readouts=include_readouts,
|
1895
|
+
)
|
1896
|
+
]
|
1550
1897
|
|
1551
|
-
def
|
1898
|
+
def _get_args_time_course(
|
1552
1899
|
self,
|
1553
|
-
variables: pd.DataFrame,
|
1554
1900
|
*,
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
>>> model.get_dependent_time_course(
|
1561
|
-
... pd.DataFrame({"x1": [1.0, 2.0], "x2": [2.0, 3.0]}
|
1562
|
-
... )
|
1563
|
-
pd.DataFrame({
|
1564
|
-
"x1": [1.0, 2.0],
|
1565
|
-
"x2": [2.0, 3.0],
|
1566
|
-
"k1": [0.1, 0.1],
|
1567
|
-
"time": [0.0, 1.0]},
|
1568
|
-
)
|
1569
|
-
|
1570
|
-
Args:
|
1571
|
-
variables: A DataFrame containing concentration data with time as the index.
|
1572
|
-
include_readouts: If True, include readout variables in the resulting DataFrame.
|
1573
|
-
|
1574
|
-
Returns:
|
1575
|
-
A DataFrame containing the combined concentration data, parameter values,
|
1576
|
-
derived variables, and optionally readout variables, with time as an additional column.
|
1901
|
+
variables: pd.DataFrame,
|
1902
|
+
include_readouts: bool,
|
1903
|
+
) -> dict[float, dict[str, float]]:
|
1904
|
+
if (cache := self._cache) is None:
|
1905
|
+
cache = self._create_cache()
|
1577
1906
|
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1907
|
+
args_by_time = {}
|
1908
|
+
for time, values in variables.iterrows():
|
1909
|
+
args = self._get_args(
|
1581
1910
|
variables=values.to_dict(),
|
1582
1911
|
time=cast(float, time),
|
1583
|
-
|
1912
|
+
cache=cache,
|
1584
1913
|
)
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
##########################################################################
|
1591
|
-
# Get args
|
1592
|
-
##########################################################################
|
1593
|
-
|
1594
|
-
def get_args(
|
1595
|
-
self,
|
1596
|
-
variables: dict[str, float] | None = None,
|
1597
|
-
time: float = 0.0,
|
1598
|
-
*,
|
1599
|
-
include_derived: bool = True,
|
1600
|
-
include_readouts: bool = False,
|
1601
|
-
) -> pd.Series:
|
1602
|
-
"""Generate a pandas Series of arguments for the model.
|
1603
|
-
|
1604
|
-
Examples:
|
1605
|
-
# Using initial conditions
|
1606
|
-
>>> model.get_args()
|
1607
|
-
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1608
|
-
|
1609
|
-
# With custom concentrations
|
1610
|
-
>>> model.get_args({"x1": 1.0, "x2": 2.0})
|
1611
|
-
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1612
|
-
|
1613
|
-
# With custom concentrations and time
|
1614
|
-
>>> model.get_args({"x1": 1.0, "x2": 2.0}, time=1.0)
|
1615
|
-
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 1.0}
|
1616
|
-
|
1617
|
-
Args:
|
1618
|
-
variables: A dictionary where keys are the names of the concentrations and values are their respective float values.
|
1619
|
-
time: The time point at which the arguments are generated.
|
1620
|
-
include_derived: Whether to include derived variables in the arguments.
|
1621
|
-
include_readouts: Whether to include readouts in the arguments.
|
1622
|
-
|
1623
|
-
Returns:
|
1624
|
-
A pandas Series containing the generated arguments with float dtype.
|
1625
|
-
|
1626
|
-
"""
|
1627
|
-
names = self.get_variable_names()
|
1628
|
-
if include_derived:
|
1629
|
-
names.extend(self.get_derived_variable_names())
|
1630
|
-
if include_readouts:
|
1631
|
-
names.extend(self._readouts)
|
1632
|
-
|
1633
|
-
args = self.get_dependent(
|
1634
|
-
variables=variables, time=time, include_readouts=include_readouts
|
1635
|
-
)
|
1636
|
-
return args.loc[names]
|
1914
|
+
if include_readouts:
|
1915
|
+
for name, ro in self._readouts.items(): # FIXME: order?
|
1916
|
+
ro.calculate_inpl(name, args)
|
1917
|
+
args_by_time[time] = args
|
1918
|
+
return args_by_time
|
1637
1919
|
|
1638
1920
|
def get_args_time_course(
|
1639
1921
|
self,
|
1640
1922
|
variables: pd.DataFrame,
|
1641
1923
|
*,
|
1642
|
-
|
1924
|
+
include_variables: bool = True,
|
1925
|
+
include_parameters: bool = True,
|
1926
|
+
include_derived_parameters: bool = True,
|
1927
|
+
include_derived_variables: bool = True,
|
1928
|
+
include_reactions: bool = True,
|
1929
|
+
include_surrogate_outputs: bool = True,
|
1643
1930
|
include_readouts: bool = False,
|
1644
1931
|
) -> pd.DataFrame:
|
1645
1932
|
"""Generate a DataFrame containing time course arguments for model evaluation.
|
@@ -1657,22 +1944,40 @@ class Model:
|
|
1657
1944
|
|
1658
1945
|
Args:
|
1659
1946
|
variables: A DataFrame containing concentration data with time as the index.
|
1660
|
-
|
1661
|
-
|
1947
|
+
include_variables: Whether to include variables
|
1948
|
+
include_parameters: Whether to include parameters
|
1949
|
+
include_derived_parameters: Whether to include derived parameters
|
1950
|
+
include_derived_variables: Whether to include derived variables
|
1951
|
+
include_reactions: Whether to include reactions
|
1952
|
+
include_surrogate_outputs: Whether to include surrogate outputs
|
1953
|
+
include_readouts: Whether to include readouts
|
1662
1954
|
|
1663
1955
|
Returns:
|
1664
1956
|
A DataFrame containing the combined concentration data, parameter values,
|
1665
1957
|
derived variables, and optionally readout variables, with time as an additional column.
|
1666
1958
|
|
1667
1959
|
"""
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
)
|
1675
|
-
|
1960
|
+
args = pd.DataFrame(
|
1961
|
+
self._get_args_time_course(
|
1962
|
+
variables=variables,
|
1963
|
+
include_readouts=include_readouts,
|
1964
|
+
),
|
1965
|
+
dtype=float,
|
1966
|
+
).T
|
1967
|
+
|
1968
|
+
return args.loc[
|
1969
|
+
:,
|
1970
|
+
self.get_arg_names(
|
1971
|
+
include_time=False,
|
1972
|
+
include_variables=include_variables,
|
1973
|
+
include_parameters=include_parameters,
|
1974
|
+
include_derived_parameters=include_derived_parameters,
|
1975
|
+
include_derived_variables=include_derived_variables,
|
1976
|
+
include_reactions=include_reactions,
|
1977
|
+
include_surrogate_outputs=include_surrogate_outputs,
|
1978
|
+
include_readouts=include_readouts,
|
1979
|
+
),
|
1980
|
+
]
|
1676
1981
|
|
1677
1982
|
##########################################################################
|
1678
1983
|
# Get fluxes
|
@@ -1707,10 +2012,9 @@ class Model:
|
|
1707
2012
|
|
1708
2013
|
"""
|
1709
2014
|
names = self.get_reaction_names()
|
1710
|
-
|
1711
|
-
names.extend(surrogate.stoichiometries)
|
2015
|
+
names.extend(self.get_surrogate_reaction_names())
|
1712
2016
|
|
1713
|
-
args = self.
|
2017
|
+
args = self.get_args(
|
1714
2018
|
variables=variables,
|
1715
2019
|
time=time,
|
1716
2020
|
include_readouts=False,
|
@@ -1739,11 +2043,11 @@ class Model:
|
|
1739
2043
|
the index of the input arguments.
|
1740
2044
|
|
1741
2045
|
"""
|
1742
|
-
names = self.get_reaction_names()
|
2046
|
+
names: list[str] = self.get_reaction_names()
|
1743
2047
|
for surrogate in self._surrogates.values():
|
1744
2048
|
names.extend(surrogate.stoichiometries)
|
1745
2049
|
|
1746
|
-
variables = self.
|
2050
|
+
variables = self.get_args_time_course(
|
1747
2051
|
variables=variables,
|
1748
2052
|
include_readouts=False,
|
1749
2053
|
)
|
@@ -1781,7 +2085,7 @@ class Model:
|
|
1781
2085
|
strict=True,
|
1782
2086
|
)
|
1783
2087
|
)
|
1784
|
-
dependent: dict[str, float] = self.
|
2088
|
+
dependent: dict[str, float] = self._get_args(
|
1785
2089
|
variables=vars_d,
|
1786
2090
|
time=time,
|
1787
2091
|
cache=cache,
|
@@ -1829,7 +2133,7 @@ class Model:
|
|
1829
2133
|
if (cache := self._cache) is None:
|
1830
2134
|
cache = self._create_cache()
|
1831
2135
|
var_names = self.get_variable_names()
|
1832
|
-
dependent = self.
|
2136
|
+
dependent = self._get_args(
|
1833
2137
|
variables=self.get_initial_conditions() if variables is None else variables,
|
1834
2138
|
time=time,
|
1835
2139
|
cache=cache,
|