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.
@@ -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()
@@ -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 = _mean_abs,
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 = _mean_abs,
156
+ loss_fn: LossFn = mean_abs_error,
172
157
  ) -> tuple[Surrogate, pd.Series]:
173
158
  """Train a PyTorch surrogate model.
174
159
 
@@ -1,5 +1,4 @@
1
- # ruff: noqa: D100, D101, D102, D103, D104, D105, D106, D107, D200, D203, D400, D401
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
- symbols: dict[str, sympy.Symbol | sympy.Expr] = variables | parameters # type: ignore
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.24.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=MzR_lZLel95RyWISsF16mDLscGoaw79oD976MFo5_TI,4453
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=UTNvbUcL9mg7NuH5qq3syqpXXqynT_lXmKQudxwWcsE,22880
5
+ mxlpy/fit.py,sha256=Kxf5NTHS47YGa9jD_35T_ddEZLsI_lfSJcpqYDW5vWA,40746
6
6
  mxlpy/fns.py,sha256=NLxYwa3ylS7SkISBjw_TgQSKEm7WnkZF9wPigX_ZCAM,13915
7
- mxlpy/identify.py,sha256=G-Zyr_l-K2mDtIV1xGrQ52QJxoBYqRoNA5RW6GpbNjs,2213
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=kjZTQo8yM62JX2GedwTCfxtTQpdprg8vJzgJvp_5XCI,79643
13
- mxlpy/parallel.py,sha256=yLQLw5O4vnPVp_Zmtw1UhPWtB3483foimxQB-TwFKPg,5016
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=Vg3ekCkhbH8HjqdDTA7vaAdFp9Diney3ARZZfbq0MoA,33475
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=xKyisVN1jW5hxmVD2K_RpoQ2MwNrMxSGODsAEgEt6_I,4922
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=Owj5GKDFkiqks2mIDQIXV3M_5TysGwAZgKSq8C3KBIg,23461
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=e6ufIsBjgde3QuQ9OsAYES0Lib4WALw4E-0wH_2cF6Y,22442
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=Qjr-ERsY2lbD75sFBOhCUwEasQDSJKcpBn_kReLZ6oA,633
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=GUJmLU282VU4O-fs3Sz90SEaAnfuXN2MPlBr_tHmvn4,5775
38
- mxlpy/npe/__init__.py,sha256=hBHCUD2JYDBBGS2kTY8mTCfWB3u1R7m5l--wUupZt6o,1270
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=v3joh6lFJJxvYJj--wzmKXL9UMTaIN3h6hPNq0uX9NU,11250
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=cz9qr0ToYSutIK45IvKrMe1mPP7Lj0I_V0HYGixfpZU,916
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=gU0secuRBYgewhNqZmSo6_Xf804dSzwWwIYmdKA7y60,6389
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=cKfWoktvFmXjuo8egE7gXKrKBq2iBUiy_BcIKIvvz8A,3026
54
- mxlpy-0.24.0.dist-info/METADATA,sha256=Hqld-ntG-lfZey_b-aHgJo-T8KYylrC-iSf9CxMs-XA,4980
55
- mxlpy-0.24.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
- mxlpy-0.24.0.dist-info/licenses/LICENSE,sha256=lHX9Eu70g3Iv1aOxXTWNHa3vq9vaVYSPQx4jOLYmDpw,1096
57
- mxlpy-0.24.0.dist-info/RECORD,,
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