mxlpy 0.24.0__py3-none-any.whl → 0.26.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 +2 -0
- mxlpy/fit.py +960 -359
- mxlpy/fuzzy.py +139 -0
- mxlpy/identify.py +1 -0
- mxlpy/integrators/int_scipy.py +4 -3
- mxlpy/meta/codegen_latex.py +1 -0
- mxlpy/meta/source_tools.py +1 -1
- mxlpy/model.py +74 -33
- mxlpy/nn/__init__.py +5 -0
- mxlpy/nn/_equinox.py +293 -0
- mxlpy/nn/_torch.py +59 -2
- mxlpy/npe/__init__.py +5 -0
- mxlpy/npe/_equinox.py +344 -0
- mxlpy/npe/_torch.py +6 -22
- mxlpy/parallel.py +73 -4
- mxlpy/surrogates/__init__.py +5 -0
- mxlpy/surrogates/_equinox.py +195 -0
- mxlpy/surrogates/_torch.py +5 -20
- mxlpy/symbolic/symbolic_model.py +30 -3
- mxlpy/types.py +172 -0
- {mxlpy-0.24.0.dist-info → mxlpy-0.26.0.dist-info}/METADATA +11 -1
- {mxlpy-0.24.0.dist-info → mxlpy-0.26.0.dist-info}/RECORD +24 -20
- {mxlpy-0.24.0.dist-info → mxlpy-0.26.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.24.0.dist-info → mxlpy-0.26.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import TYPE_CHECKING, Self
|
5
|
+
|
6
|
+
import jax
|
7
|
+
import jax.numpy as jnp
|
8
|
+
import numpy as np
|
9
|
+
import optax
|
10
|
+
import pandas as pd
|
11
|
+
|
12
|
+
from mxlpy.nn._equinox import MLP, LossFn, mean_abs_error
|
13
|
+
from mxlpy.nn._equinox import train as _train
|
14
|
+
from mxlpy.types import AbstractSurrogate, Derived
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
import equinox as eqx
|
18
|
+
|
19
|
+
__all__ = ["Surrogate", "Trainer", "train"]
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass(kw_only=True)
|
23
|
+
class Surrogate(AbstractSurrogate):
|
24
|
+
"""Surrogate model using PyTorch.
|
25
|
+
|
26
|
+
Attributes:
|
27
|
+
model: PyTorch neural network model.
|
28
|
+
|
29
|
+
Methods:
|
30
|
+
predict: Predict outputs based on input data using the PyTorch model.
|
31
|
+
|
32
|
+
"""
|
33
|
+
|
34
|
+
model: eqx.Module
|
35
|
+
|
36
|
+
def predict_raw(self, y: np.ndarray) -> np.ndarray:
|
37
|
+
"""Predict outputs based on input data using the PyTorch model.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
y: Input data as a numpy array.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
dict[str, float]: Dictionary mapping output variable names to predicted values.
|
44
|
+
|
45
|
+
"""
|
46
|
+
# One has to implement __call__ on eqx.Module, so this should
|
47
|
+
# always exist. Should really be abstract on eqx.Module
|
48
|
+
return self.model(y).numpy() # type: ignore
|
49
|
+
|
50
|
+
def predict(
|
51
|
+
self,
|
52
|
+
args: dict[str, float | pd.Series | pd.DataFrame],
|
53
|
+
) -> dict[str, float]:
|
54
|
+
"""Predict outputs based on input data."""
|
55
|
+
return dict(
|
56
|
+
zip(
|
57
|
+
self.outputs,
|
58
|
+
self.predict_raw(np.array([args[arg] for arg in self.args])),
|
59
|
+
strict=True,
|
60
|
+
)
|
61
|
+
)
|
62
|
+
|
63
|
+
|
64
|
+
@dataclass(init=False)
|
65
|
+
class Trainer:
|
66
|
+
features: pd.DataFrame
|
67
|
+
targets: pd.DataFrame
|
68
|
+
model: eqx.Module
|
69
|
+
optimizer: optax.GradientTransformation
|
70
|
+
losses: list[pd.Series]
|
71
|
+
loss_fn: LossFn
|
72
|
+
seed: int
|
73
|
+
|
74
|
+
def __init__(
|
75
|
+
self,
|
76
|
+
features: pd.DataFrame,
|
77
|
+
targets: pd.DataFrame,
|
78
|
+
model: eqx.Module | None = None,
|
79
|
+
optimizer: optax.GradientTransformation | None = None,
|
80
|
+
loss_fn: LossFn = mean_abs_error,
|
81
|
+
seed: int = 0,
|
82
|
+
) -> None:
|
83
|
+
self.features = features
|
84
|
+
self.targets = targets
|
85
|
+
|
86
|
+
if model is None:
|
87
|
+
model = MLP(
|
88
|
+
n_inputs=len(features.columns),
|
89
|
+
neurons_per_layer=[50, 50, len(targets.columns)],
|
90
|
+
key=jax.random.PRNGKey(seed),
|
91
|
+
)
|
92
|
+
self.model = model
|
93
|
+
|
94
|
+
self.optimizer = (
|
95
|
+
optax.adamw(learning_rate=0.001) if optimizer is None else optimizer
|
96
|
+
)
|
97
|
+
self.loss_fn = loss_fn
|
98
|
+
self.losses = []
|
99
|
+
self.seed = seed
|
100
|
+
|
101
|
+
def train(
|
102
|
+
self,
|
103
|
+
epochs: int,
|
104
|
+
batch_size: int | None = None,
|
105
|
+
) -> Self:
|
106
|
+
losses = _train(
|
107
|
+
model=self.model,
|
108
|
+
features=jnp.array(self.features),
|
109
|
+
targets=jnp.array(self.targets),
|
110
|
+
epochs=epochs,
|
111
|
+
optimizer=self.optimizer,
|
112
|
+
batch_size=batch_size,
|
113
|
+
loss_fn=self.loss_fn,
|
114
|
+
)
|
115
|
+
if len(self.losses) > 0:
|
116
|
+
losses.index += self.losses[-1].index[-1]
|
117
|
+
self.losses.append(losses)
|
118
|
+
return self
|
119
|
+
|
120
|
+
def get_loss(self) -> pd.Series:
|
121
|
+
return pd.concat(self.losses)
|
122
|
+
|
123
|
+
def get_surrogate(
|
124
|
+
self,
|
125
|
+
surrogate_args: list[str] | None = None,
|
126
|
+
surrogate_outputs: list[str] | None = None,
|
127
|
+
surrogate_stoichiometries: dict[str, dict[str, float | Derived]] | None = None,
|
128
|
+
) -> Surrogate:
|
129
|
+
return Surrogate(
|
130
|
+
model=self.model,
|
131
|
+
args=surrogate_args if surrogate_args is not None else [],
|
132
|
+
outputs=surrogate_outputs if surrogate_outputs is not None else [],
|
133
|
+
stoichiometries=surrogate_stoichiometries
|
134
|
+
if surrogate_stoichiometries is not None
|
135
|
+
else {},
|
136
|
+
)
|
137
|
+
|
138
|
+
|
139
|
+
def train(
|
140
|
+
features: pd.DataFrame,
|
141
|
+
targets: pd.DataFrame,
|
142
|
+
epochs: int,
|
143
|
+
surrogate_args: list[str] | None = None,
|
144
|
+
surrogate_outputs: list[str] | None = None,
|
145
|
+
surrogate_stoichiometries: dict[str, dict[str, float | Derived]] | None = None,
|
146
|
+
batch_size: int | None = None,
|
147
|
+
model: eqx.Module | None = None,
|
148
|
+
optimizer: optax.GradientTransformation | None = None,
|
149
|
+
loss_fn: LossFn = mean_abs_error,
|
150
|
+
) -> tuple[Surrogate, pd.Series]:
|
151
|
+
"""Train a PyTorch surrogate model.
|
152
|
+
|
153
|
+
Examples:
|
154
|
+
>>> train_torch_surrogate(
|
155
|
+
... features,
|
156
|
+
... targets,
|
157
|
+
... epochs=100,
|
158
|
+
... surrogate_inputs=["x1", "x2"],
|
159
|
+
... surrogate_stoichiometries={
|
160
|
+
... "v1": {"x1": -1, "x2": 1, "ATP": -1},
|
161
|
+
... },
|
162
|
+
...)surrogate_stoichiometries
|
163
|
+
|
164
|
+
Args:
|
165
|
+
features: DataFrame containing the input features for training.
|
166
|
+
targets: DataFrame containing the target values for training.
|
167
|
+
epochs: Number of training epochs.
|
168
|
+
surrogate_args: Names of inputs arguments for the surrogate model.
|
169
|
+
surrogate_outputs: Names of output arguments from the surrogate.
|
170
|
+
surrogate_stoichiometries: Mapping of variables to their stoichiometries
|
171
|
+
batch_size: Size of mini-batches for training (None for full-batch).
|
172
|
+
model: Predefined neural network model (None to use default MLP features-50-50-output).
|
173
|
+
optimizer: Optimizer class to use for training (default: optax.GradientTransformation).
|
174
|
+
device: Device to run the training on (default: DefaultDevice).
|
175
|
+
loss_fn: Custom loss function or instance of torch loss object
|
176
|
+
|
177
|
+
Returns:
|
178
|
+
tuple[TorchSurrogate, pd.Series]: Trained surrogate model and loss history.
|
179
|
+
|
180
|
+
"""
|
181
|
+
trainer = Trainer(
|
182
|
+
features=features,
|
183
|
+
targets=targets,
|
184
|
+
model=model,
|
185
|
+
optimizer=optimizer,
|
186
|
+
loss_fn=loss_fn,
|
187
|
+
).train(
|
188
|
+
epochs=epochs,
|
189
|
+
batch_size=batch_size,
|
190
|
+
)
|
191
|
+
return trainer.get_surrogate(
|
192
|
+
surrogate_args=surrogate_args,
|
193
|
+
surrogate_outputs=surrogate_outputs,
|
194
|
+
surrogate_stoichiometries=surrogate_stoichiometries,
|
195
|
+
), trainer.get_loss()
|
mxlpy/surrogates/_torch.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections.abc import Callable
|
4
3
|
from dataclasses import dataclass
|
5
4
|
from typing import TYPE_CHECKING, Self
|
6
5
|
|
@@ -10,37 +9,23 @@ import torch
|
|
10
9
|
from torch import nn
|
11
10
|
from torch.optim.adam import Adam
|
12
11
|
|
13
|
-
from mxlpy.nn._torch import MLP, DefaultDevice
|
12
|
+
from mxlpy.nn._torch import MLP, DefaultDevice, LossFn, mean_abs_error
|
14
13
|
from mxlpy.nn._torch import train as _train
|
15
14
|
from mxlpy.types import AbstractSurrogate, Derived
|
16
15
|
|
17
16
|
if TYPE_CHECKING:
|
17
|
+
from collections.abc import Callable
|
18
|
+
|
18
19
|
from torch.optim.optimizer import ParamsT
|
19
20
|
|
20
|
-
type LossFn = Callable[[torch.Tensor, torch.Tensor], torch.Tensor]
|
21
21
|
|
22
22
|
__all__ = [
|
23
|
-
"LossFn",
|
24
23
|
"Surrogate",
|
25
24
|
"Trainer",
|
26
25
|
"train",
|
27
26
|
]
|
28
27
|
|
29
28
|
|
30
|
-
def _mean_abs(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
|
31
|
-
"""Standard loss for surrogates.
|
32
|
-
|
33
|
-
Args:
|
34
|
-
x: Predictions of a model.
|
35
|
-
y: Targets.
|
36
|
-
|
37
|
-
Returns:
|
38
|
-
torch.Tensor: loss.
|
39
|
-
|
40
|
-
"""
|
41
|
-
return torch.mean(torch.abs(x - y))
|
42
|
-
|
43
|
-
|
44
29
|
@dataclass(kw_only=True)
|
45
30
|
class Surrogate(AbstractSurrogate):
|
46
31
|
"""Surrogate model using PyTorch.
|
@@ -101,7 +86,7 @@ class Trainer:
|
|
101
86
|
model: nn.Module | None = None,
|
102
87
|
optimizer_cls: Callable[[ParamsT], Adam] = Adam,
|
103
88
|
device: torch.device = DefaultDevice,
|
104
|
-
loss_fn: LossFn =
|
89
|
+
loss_fn: LossFn = mean_abs_error,
|
105
90
|
) -> None:
|
106
91
|
self.features = features
|
107
92
|
self.targets = targets
|
@@ -168,7 +153,7 @@ def train(
|
|
168
153
|
model: nn.Module | None = None,
|
169
154
|
optimizer_cls: Callable[[ParamsT], Adam] = Adam,
|
170
155
|
device: torch.device = DefaultDevice,
|
171
|
-
loss_fn: LossFn =
|
156
|
+
loss_fn: LossFn = mean_abs_error,
|
172
157
|
) -> tuple[Surrogate, pd.Series]:
|
173
158
|
"""Train a PyTorch surrogate model.
|
174
159
|
|
mxlpy/symbolic/symbolic_model.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
"""Conversions into symbolic representation."""
|
3
2
|
|
4
3
|
from __future__ import annotations
|
5
4
|
|
@@ -7,6 +6,7 @@ from dataclasses import dataclass
|
|
7
6
|
from typing import TYPE_CHECKING, cast
|
8
7
|
|
9
8
|
import sympy
|
9
|
+
from wadler_lindig import pformat
|
10
10
|
|
11
11
|
from mxlpy.meta.sympy_tools import fn_to_sympy, list_of_symbols
|
12
12
|
|
@@ -21,13 +21,20 @@ __all__ = [
|
|
21
21
|
|
22
22
|
@dataclass
|
23
23
|
class SymbolicModel:
|
24
|
+
"""Symbolic model."""
|
25
|
+
|
24
26
|
variables: dict[str, sympy.Symbol]
|
25
27
|
parameters: dict[str, sympy.Symbol]
|
28
|
+
external: dict[str, sympy.Symbol]
|
26
29
|
eqs: list[sympy.Expr]
|
27
30
|
initial_conditions: dict[str, float]
|
28
31
|
parameter_values: dict[str, float]
|
29
32
|
|
33
|
+
def __repr__(self) -> str:
|
34
|
+
return pformat(self)
|
35
|
+
|
30
36
|
def jacobian(self) -> sympy.Matrix:
|
37
|
+
"""Get jacobian of symbolic model."""
|
31
38
|
# FIXME: don't rely on ordering of variables
|
32
39
|
return sympy.Matrix(self.eqs).jacobian(
|
33
40
|
sympy.Matrix(list(self.variables.values()))
|
@@ -35,6 +42,7 @@ class SymbolicModel:
|
|
35
42
|
|
36
43
|
|
37
44
|
def to_symbolic_model(model: Model) -> SymbolicModel:
|
45
|
+
"""Convert model into symbolic representation."""
|
38
46
|
cache = model._create_cache() # noqa: SLF001
|
39
47
|
initial_conditions = model.get_initial_conditions()
|
40
48
|
|
@@ -52,7 +60,25 @@ def to_symbolic_model(model: Model) -> SymbolicModel:
|
|
52
60
|
strict=True,
|
53
61
|
)
|
54
62
|
)
|
55
|
-
|
63
|
+
# data
|
64
|
+
data = dict(
|
65
|
+
zip(
|
66
|
+
model._data, # noqa: SLF001
|
67
|
+
cast(list[sympy.Symbol], list_of_symbols(model._data)), # noqa: SLF001
|
68
|
+
strict=True,
|
69
|
+
)
|
70
|
+
)
|
71
|
+
# Insert surrogates?
|
72
|
+
_surr = model.get_surrogate_output_names(include_fluxes=True)
|
73
|
+
surrogates: dict[str, sympy.Symbol] = dict(
|
74
|
+
zip(
|
75
|
+
_surr,
|
76
|
+
cast(list[sympy.Symbol], list_of_symbols(_surr)),
|
77
|
+
strict=True,
|
78
|
+
)
|
79
|
+
)
|
80
|
+
|
81
|
+
symbols: dict[str, sympy.Symbol | sympy.Expr] = variables | parameters | data # type: ignore
|
56
82
|
|
57
83
|
# Insert derived into symbols
|
58
84
|
for k, v in model.get_raw_derived().items():
|
@@ -93,4 +119,5 @@ def to_symbolic_model(model: Model) -> SymbolicModel:
|
|
93
119
|
eqs=[eqs[i] for i in cache.var_names],
|
94
120
|
initial_conditions=model.get_initial_conditions(),
|
95
121
|
parameter_values=model.get_parameter_values(),
|
122
|
+
external=data | surrogates,
|
96
123
|
)
|
mxlpy/types.py
CHANGED
@@ -798,6 +798,177 @@ class Result:
|
|
798
798
|
"""
|
799
799
|
return pd.concat((self.variables, self.fluxes), axis=1)
|
800
800
|
|
801
|
+
@overload
|
802
|
+
def get_right_hand_side( # type: ignore
|
803
|
+
self,
|
804
|
+
*,
|
805
|
+
normalise: float | ArrayLike | None = None,
|
806
|
+
concatenated: Literal[False],
|
807
|
+
) -> list[pd.DataFrame]: ...
|
808
|
+
|
809
|
+
@overload
|
810
|
+
def get_right_hand_side(
|
811
|
+
self,
|
812
|
+
*,
|
813
|
+
normalise: float | ArrayLike | None = None,
|
814
|
+
concatenated: Literal[True],
|
815
|
+
) -> pd.DataFrame: ...
|
816
|
+
|
817
|
+
@overload
|
818
|
+
def get_right_hand_side(
|
819
|
+
self,
|
820
|
+
*,
|
821
|
+
normalise: float | ArrayLike | None = None,
|
822
|
+
concatenated: bool = True,
|
823
|
+
) -> pd.DataFrame: ...
|
824
|
+
|
825
|
+
def get_right_hand_side(
|
826
|
+
self,
|
827
|
+
*,
|
828
|
+
normalise: float | ArrayLike | None = None,
|
829
|
+
concatenated: bool = True,
|
830
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
831
|
+
"""Get right hand side over time."""
|
832
|
+
args_by_simulation = self._compute_args()
|
833
|
+
return self._adjust_data(
|
834
|
+
[
|
835
|
+
self.model.update_parameters(p).get_right_hand_side_time_course(
|
836
|
+
args=args
|
837
|
+
)
|
838
|
+
for args, p in zip(args_by_simulation, self.raw_parameters, strict=True)
|
839
|
+
],
|
840
|
+
normalise=normalise,
|
841
|
+
concatenated=concatenated,
|
842
|
+
)
|
843
|
+
|
844
|
+
@overload
|
845
|
+
def get_producers( # type: ignore
|
846
|
+
self,
|
847
|
+
variable: str,
|
848
|
+
*,
|
849
|
+
scaled: bool = False,
|
850
|
+
normalise: float | ArrayLike | None = None,
|
851
|
+
concatenated: Literal[False],
|
852
|
+
) -> list[pd.DataFrame]: ...
|
853
|
+
|
854
|
+
@overload
|
855
|
+
def get_producers(
|
856
|
+
self,
|
857
|
+
variable: str,
|
858
|
+
*,
|
859
|
+
scaled: bool = False,
|
860
|
+
normalise: float | ArrayLike | None = None,
|
861
|
+
concatenated: Literal[True],
|
862
|
+
) -> pd.DataFrame: ...
|
863
|
+
|
864
|
+
@overload
|
865
|
+
def get_producers(
|
866
|
+
self,
|
867
|
+
variable: str,
|
868
|
+
*,
|
869
|
+
scaled: bool = False,
|
870
|
+
normalise: float | ArrayLike | None = None,
|
871
|
+
concatenated: bool = True,
|
872
|
+
) -> pd.DataFrame: ...
|
873
|
+
|
874
|
+
def get_producers(
|
875
|
+
self,
|
876
|
+
variable: str,
|
877
|
+
*,
|
878
|
+
scaled: bool = False,
|
879
|
+
normalise: float | ArrayLike | None = None,
|
880
|
+
concatenated: bool = True,
|
881
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
882
|
+
"""Get fluxes of variable with positive stoichiometry."""
|
883
|
+
self.model.update_parameters(self.raw_parameters[0])
|
884
|
+
names = [
|
885
|
+
k
|
886
|
+
for k, v in self.model.get_stoichiometries_of_variable(variable).items()
|
887
|
+
if v > 0
|
888
|
+
]
|
889
|
+
|
890
|
+
fluxes: list[pd.DataFrame] = [
|
891
|
+
i.loc[:, names]
|
892
|
+
for i in self.get_fluxes(normalise=normalise, concatenated=False)
|
893
|
+
]
|
894
|
+
|
895
|
+
if scaled:
|
896
|
+
fluxes = [i.copy() for i in fluxes]
|
897
|
+
for v, p in zip(fluxes, self.raw_parameters, strict=True):
|
898
|
+
self.model.update_parameters(p)
|
899
|
+
stoichs = self.model.get_stoichiometries_of_variable(variable)
|
900
|
+
for k in names:
|
901
|
+
v.loc[:, k] *= stoichs[k]
|
902
|
+
|
903
|
+
self.model.update_parameters(self.raw_parameters[-1])
|
904
|
+
if concatenated:
|
905
|
+
return pd.concat(fluxes, axis=0)
|
906
|
+
return fluxes
|
907
|
+
|
908
|
+
@overload
|
909
|
+
def get_consumers( # type: ignore
|
910
|
+
self,
|
911
|
+
variable: str,
|
912
|
+
*,
|
913
|
+
scaled: bool = False,
|
914
|
+
normalise: float | ArrayLike | None = None,
|
915
|
+
concatenated: Literal[False],
|
916
|
+
) -> list[pd.DataFrame]: ...
|
917
|
+
|
918
|
+
@overload
|
919
|
+
def get_consumers(
|
920
|
+
self,
|
921
|
+
variable: str,
|
922
|
+
*,
|
923
|
+
scaled: bool = False,
|
924
|
+
normalise: float | ArrayLike | None = None,
|
925
|
+
concatenated: Literal[True],
|
926
|
+
) -> pd.DataFrame: ...
|
927
|
+
|
928
|
+
@overload
|
929
|
+
def get_consumers(
|
930
|
+
self,
|
931
|
+
variable: str,
|
932
|
+
*,
|
933
|
+
scaled: bool = False,
|
934
|
+
normalise: float | ArrayLike | None = None,
|
935
|
+
concatenated: bool = True,
|
936
|
+
) -> pd.DataFrame: ...
|
937
|
+
|
938
|
+
def get_consumers(
|
939
|
+
self,
|
940
|
+
variable: str,
|
941
|
+
*,
|
942
|
+
scaled: bool = False,
|
943
|
+
normalise: float | ArrayLike | None = None,
|
944
|
+
concatenated: bool = True,
|
945
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
946
|
+
"""Get fluxes of variable with negative stoichiometry."""
|
947
|
+
self.model.update_parameters(self.raw_parameters[0])
|
948
|
+
names = [
|
949
|
+
k
|
950
|
+
for k, v in self.model.get_stoichiometries_of_variable(variable).items()
|
951
|
+
if v < 0
|
952
|
+
]
|
953
|
+
|
954
|
+
fluxes: list[pd.DataFrame] = [
|
955
|
+
i.loc[:, names]
|
956
|
+
for i in self.get_fluxes(normalise=normalise, concatenated=False)
|
957
|
+
]
|
958
|
+
|
959
|
+
if scaled:
|
960
|
+
fluxes = [i.copy() for i in fluxes]
|
961
|
+
for v, p in zip(fluxes, self.raw_parameters, strict=True):
|
962
|
+
self.model.update_parameters(p)
|
963
|
+
stoichs = self.model.get_stoichiometries_of_variable(variable)
|
964
|
+
for k in names:
|
965
|
+
v.loc[:, k] *= -stoichs[k]
|
966
|
+
|
967
|
+
self.model.update_parameters(self.raw_parameters[-1])
|
968
|
+
if concatenated:
|
969
|
+
return pd.concat(fluxes, axis=0)
|
970
|
+
return fluxes
|
971
|
+
|
801
972
|
def get_new_y0(self) -> dict[str, float]:
|
802
973
|
"""Get the new initial conditions after the simulation.
|
803
974
|
|
@@ -810,6 +981,7 @@ class Result:
|
|
810
981
|
self.get_variables(
|
811
982
|
include_derived_variables=False,
|
812
983
|
include_readouts=False,
|
984
|
+
include_surrogate_variables=False,
|
813
985
|
).iloc[-1]
|
814
986
|
)
|
815
987
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mxlpy
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.26.0
|
4
4
|
Summary: A package to build metabolic models
|
5
5
|
Author-email: Marvin van Aalst <marvin.vanaalst@gmail.com>
|
6
6
|
Maintainer-email: Marvin van Aalst <marvin.vanaalst@gmail.com>
|
@@ -45,6 +45,9 @@ Requires-Dist: toml>=0.10.2
|
|
45
45
|
Requires-Dist: tqdm>=4.66.6
|
46
46
|
Requires-Dist: typing-extensions>=4.12.2
|
47
47
|
Requires-Dist: wadler-lindig>=0.1.7
|
48
|
+
Provides-Extra: equinox
|
49
|
+
Requires-Dist: equinox>=0.13; extra == 'equinox'
|
50
|
+
Requires-Dist: optax>=0.2.5; extra == 'equinox'
|
48
51
|
Provides-Extra: keras
|
49
52
|
Requires-Dist: keras>=3.9.2; extra == 'keras'
|
50
53
|
Provides-Extra: tensorflow
|
@@ -124,3 +127,10 @@ You have two choices here, using `uv` (pypi-only) or using `pixi` (conda-forge,
|
|
124
127
|
## LLMs
|
125
128
|
|
126
129
|
We support the [llms.txt](https://llmstxt.org/) convention for making documentation available to large language models and the applications that make use of them. It is located at [docs/llms.txt](https://github.com/Computational-Biology-Aachen/MxlPy/tree/main/docs/llms.txt)
|
130
|
+
|
131
|
+
## Tool family 🏠
|
132
|
+
|
133
|
+
`MxlPy` is part of a larger family of tools that are designed with a similar set of abstractions. Check them out!
|
134
|
+
|
135
|
+
- [MxlBricks](https://github.com/Computational-Biology-Aachen/mxl-bricks) is built on top of `MxlPy` to build mechanistic models composed of pre-defined reactions (bricks)
|
136
|
+
- [MxlWeb](https://github.com/Computational-Biology-Aachen/mxl-web) brings simulation of mechanistic models to the browser!
|
@@ -1,16 +1,17 @@
|
|
1
|
-
mxlpy/__init__.py,sha256=
|
1
|
+
mxlpy/__init__.py,sha256=BbX_NokTtsiJgVqCcEeBlt3KJJbjl-8dFCQu9xkPZao,4477
|
2
2
|
mxlpy/carousel.py,sha256=3M2rqi2bx87y8D-oqEKTKZ6Q_clDQHbdLNdVjLJeO7c,6013
|
3
3
|
mxlpy/compare.py,sha256=rJPOXc-aX6I1EC3ERAAW5Jn04kMwrlqUqdBgbZa6LA4,8098
|
4
4
|
mxlpy/distributions.py,sha256=ce6RTqn19YzMMec-u09fSIUA8A92M6rehCuHuXWcX7A,8734
|
5
|
-
mxlpy/fit.py,sha256=
|
5
|
+
mxlpy/fit.py,sha256=Kxf5NTHS47YGa9jD_35T_ddEZLsI_lfSJcpqYDW5vWA,40746
|
6
6
|
mxlpy/fns.py,sha256=NLxYwa3ylS7SkISBjw_TgQSKEm7WnkZF9wPigX_ZCAM,13915
|
7
|
-
mxlpy/
|
7
|
+
mxlpy/fuzzy.py,sha256=T72nr_IHCF4KH5daLxWEgui9SyBw8TPlvPzmGHhaTWQ,4161
|
8
|
+
mxlpy/identify.py,sha256=gB66R9u8-SJGHxEoTfl9QC2seDoreaIo3e6zMQOMGho,2258
|
8
9
|
mxlpy/label_map.py,sha256=PwYanfg07hC0ayyOOP72RFlCQNvhCTbpOhW6kZZ2GUU,17826
|
9
10
|
mxlpy/linear_label_map.py,sha256=6Ye6IziWGKkYD_5z3FmTVwnJ2T9SvVGN3U4CfXjXseo,10320
|
10
11
|
mxlpy/mc.py,sha256=uMHknTNHmK5b4REM3PtCpCjEr9LoSIBsGsCNU4_yHpg,18639
|
11
12
|
mxlpy/mca.py,sha256=IoOHJbjPnAEDqKk64OjFjgRPX5K_aE9D4RrCJ1xFIkw,9457
|
12
|
-
mxlpy/model.py,sha256=
|
13
|
-
mxlpy/parallel.py,sha256=
|
13
|
+
mxlpy/model.py,sha256=1LzmtolJgQi1NefFIBZWajDaScNTANXBqJkfcejia8Q,80928
|
14
|
+
mxlpy/parallel.py,sha256=UIswqVcdycZt-SNGZqTl0g0ErONUkrEpVDQn7TI19Z0,7373
|
14
15
|
mxlpy/parameterise.py,sha256=IgbvfEnkmaqVq_5GgFjHqApGUN9CJrsVD3Fr7pg9iFA,758
|
15
16
|
mxlpy/paths.py,sha256=TK2wO4N9lG-UV1JGfeB64q48JVDbwqIUj63rl55MKuQ,1022
|
16
17
|
mxlpy/plot.py,sha256=Dng9tyga8xagINQO_OZvjLRoaYGFR8crMljDHrzwEF8,33603
|
@@ -18,40 +19,43 @@ mxlpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
19
|
mxlpy/report.py,sha256=v597yzKecgtoNmlNZ_nVhBCa3czNw0tksOK5mLtAQvE,10631
|
19
20
|
mxlpy/scan.py,sha256=KXFdeWwgUBqJBCiOGDdMAGU5nS3PRVn7Cg0ISpzzK1U,17160
|
20
21
|
mxlpy/simulator.py,sha256=-jsSP1fEnJnHj1hcj37cAPUyyP57mP_ow2cL-xd9iw8,16699
|
21
|
-
mxlpy/types.py,sha256=
|
22
|
+
mxlpy/types.py,sha256=JaJFE_7aiQPWU8j70Sg0tzNX64uratFEEu6MBqMOgTI,38507
|
22
23
|
mxlpy/units.py,sha256=4bKXkCYsONUVWRdzV7aVcWFQSA6sxilebgXXFUEKw_c,2091
|
23
24
|
mxlpy/experimental/__init__.py,sha256=kZTE-92OErpHzNRqmgSQYH4CGXrogGJ5EL35XGZQ81M,206
|
24
25
|
mxlpy/experimental/diff.py,sha256=fmsM53lH86TD2iglQILK-XeZnIAtwHAcGC3Afs8V-tc,9457
|
25
26
|
mxlpy/integrators/__init__.py,sha256=TKo2dkJqdW3_n7YrmF6k3kEjr8_kr3-7MDaLu-zFWRg,533
|
26
27
|
mxlpy/integrators/int_assimulo.py,sha256=1cvR8cOBdrl_DQs9v0o7wItSG5iyYqwZVh7EO0fg3ro,5021
|
27
28
|
mxlpy/integrators/int_diffrax.py,sha256=q_8NZgIZ6T-SRRcI8kSjEb6l-DbXqPv6rjj9KApkQac,3326
|
28
|
-
mxlpy/integrators/int_scipy.py,sha256=
|
29
|
+
mxlpy/integrators/int_scipy.py,sha256=ncfrdYCTU4sDVe6oX_1Kpv9aMjHmKtgUMS8KAmCW4-c,5020
|
29
30
|
mxlpy/meta/__init__.py,sha256=8-UPZan2pT6RSuN65KC4vV9WPJiAzm2ZsXz3Zu_rmww,475
|
30
|
-
mxlpy/meta/codegen_latex.py,sha256=
|
31
|
+
mxlpy/meta/codegen_latex.py,sha256=Br6xAP7LigvFUEtvnyzbk3bG8Q_GGxZSPS4pYggi5WY,23502
|
31
32
|
mxlpy/meta/codegen_model.py,sha256=FkVrAS7HOyTQPNTItgyo8jjDEylkbETGnxJgRTBDAP4,6722
|
32
33
|
mxlpy/meta/codegen_mxlpy.py,sha256=FjBw37IqziB4dv5VBAvKenFODfxjJXdW6rQqx9CqYUk,8433
|
33
|
-
mxlpy/meta/source_tools.py,sha256=
|
34
|
+
mxlpy/meta/source_tools.py,sha256=CBrDqKzeqyClP5rokBw8KsaXkY1tZS0qHvTkes8y870,22440
|
34
35
|
mxlpy/meta/sympy_tools.py,sha256=mWVn63OMeiTmVlV586pXxDa3l-_Pc1VcP1G39vYRGS4,3203
|
35
|
-
mxlpy/nn/__init__.py,sha256=
|
36
|
+
mxlpy/nn/__init__.py,sha256=MHzt_lpQlOstEXh_qIP0NR-WYd8xzzuFWXWcI2u3URA,816
|
37
|
+
mxlpy/nn/_equinox.py,sha256=YF3MGzAgW6pFDL8uQcPe86bfGsYfmr9ojK2neC_cF20,8727
|
36
38
|
mxlpy/nn/_keras.py,sha256=-5zjHRu8OjSiZeuBSIZFyB63uBsNNH5o9y4kBcPnhx8,2263
|
37
|
-
mxlpy/nn/_torch.py,sha256=
|
38
|
-
mxlpy/npe/__init__.py,sha256=
|
39
|
+
mxlpy/nn/_torch.py,sha256=gv3fIC_byDF-KQtUlkYk0k7Kn6tyzBDEe_U_Wy8v69s,7788
|
40
|
+
mxlpy/npe/__init__.py,sha256=G550CnHCI1glqjAB3cxdObaRjfe8qTGho1wAF2C_8r8,1454
|
41
|
+
mxlpy/npe/_equinox.py,sha256=zHaaYmdwhIByKSYhz53h3SeT3HpsoFwBeiNRNorfO9I,11086
|
39
42
|
mxlpy/npe/_keras.py,sha256=ytvXMPK9KUCGOzTQm08_SgafiMb-MOIUdZQV7JjAO40,9721
|
40
|
-
mxlpy/npe/_torch.py,sha256=
|
43
|
+
mxlpy/npe/_torch.py,sha256=4g9RBSHjmgdBYb0xotrjHKM1j0mmPhNAVhNt8fMaKtg,10980
|
41
44
|
mxlpy/sbml/__init__.py,sha256=Mt97CddpLi3wIrA1b_5cagLmDdNxAVF_S7QV57Pzw8s,226
|
42
45
|
mxlpy/sbml/_data.py,sha256=98w5vyhdOHilD5zjy21XFzam0FlvcW_cb1XRTsIgD_M,2019
|
43
46
|
mxlpy/sbml/_export.py,sha256=leKf7dFqpoYxO3xFu4j_mM5eAgo-lq__ywADXsmamRU,21008
|
44
47
|
mxlpy/sbml/_import.py,sha256=_4MR54YyVkIh9eVAiSMd7yijhHC_ds-3v7M_C4Zn8BY,3565
|
45
48
|
mxlpy/sbml/_name_conversion.py,sha256=93muW47M7qJoE227HKHmThWpPeWsXDN9eM8cRH2pqPs,1340
|
46
|
-
mxlpy/surrogates/__init__.py,sha256=
|
49
|
+
mxlpy/surrogates/__init__.py,sha256=nrjfvS9ZnaQkKT2ybFxP33LhkwUs9D2EKmrMkgczaFw,1107
|
50
|
+
mxlpy/surrogates/_equinox.py,sha256=hF-c67RYzcV_Z-wfDyWsFSrKvvWgryDIrAsDQiStSrA,6032
|
47
51
|
mxlpy/surrogates/_keras.py,sha256=r2pR3iTJOaMqtATbsCm5CF94TYG9b-9cKljc8kMOplQ,3852
|
48
52
|
mxlpy/surrogates/_poly.py,sha256=z2g3JTdVyQJ8dIiXP4BOun_yMZOrlYpPNvQ0wmFYDTk,3672
|
49
53
|
mxlpy/surrogates/_qss.py,sha256=9w-hPPhdcwybkyaSX0sIfYfvcKu1U5j4HHj4SlgZcYQ,723
|
50
|
-
mxlpy/surrogates/_torch.py,sha256=
|
54
|
+
mxlpy/surrogates/_torch.py,sha256=sgSc9z6s4ry2ZYhl7C6DuQaj7rXcd-TNckmvB342aGk,6086
|
51
55
|
mxlpy/symbolic/__init__.py,sha256=_vM5YM5I6OH0QDbFt9uGYKO8Z5Vly0wbGuvUScVrPRU,258
|
52
56
|
mxlpy/symbolic/strikepy.py,sha256=tzo3uvPpXLDex09hWTuitVzuTNwbgl7jWGjD8g6a8iI,20033
|
53
|
-
mxlpy/symbolic/symbolic_model.py,sha256=
|
54
|
-
mxlpy-0.
|
55
|
-
mxlpy-0.
|
56
|
-
mxlpy-0.
|
57
|
-
mxlpy-0.
|
57
|
+
mxlpy/symbolic/symbolic_model.py,sha256=tJriUs6C_A3y2i89FBNGBYDL3dLKS_MHBm2nQuBRmvs,3773
|
58
|
+
mxlpy-0.26.0.dist-info/METADATA,sha256=dOHcVtFJHYDSoWxdIrDTsyjzoTm79rj4qyPaE3jSdCM,5534
|
59
|
+
mxlpy-0.26.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
60
|
+
mxlpy-0.26.0.dist-info/licenses/LICENSE,sha256=lHX9Eu70g3Iv1aOxXTWNHa3vq9vaVYSPQx4jOLYmDpw,1096
|
61
|
+
mxlpy-0.26.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|