mxlpy 0.22.0__py3-none-any.whl → 0.23.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mxlpy/simulator.py CHANGED
@@ -11,8 +11,8 @@ Classes:
11
11
  from __future__ import annotations
12
12
 
13
13
  import logging
14
- from dataclasses import dataclass, field
15
- from typing import TYPE_CHECKING, Literal, Self, cast, overload
14
+ from dataclasses import dataclass
15
+ from typing import TYPE_CHECKING, Self, cast
16
16
 
17
17
  import numpy as np
18
18
  import pandas as pd
@@ -20,374 +20,19 @@ from sympy import lambdify
20
20
 
21
21
  from mxlpy.integrators import DefaultIntegrator
22
22
  from mxlpy.symbolic import to_symbolic_model
23
+ from mxlpy.types import Result
23
24
 
24
25
  if TYPE_CHECKING:
25
- from collections.abc import Iterator
26
-
27
26
  from mxlpy.model import Model
28
27
  from mxlpy.types import Array, ArrayLike, IntegratorProtocol, IntegratorType
29
28
 
30
29
  _LOGGER = logging.getLogger(__name__)
31
30
 
32
31
  __all__ = [
33
- "Result",
34
32
  "Simulator",
35
33
  ]
36
34
 
37
35
 
38
- def _normalise_split_results(
39
- results: list[pd.DataFrame],
40
- normalise: float | ArrayLike,
41
- ) -> list[pd.DataFrame]:
42
- """Normalize split results by a given factor or array.
43
-
44
- Args:
45
- results: List of DataFrames containing the results to normalize.
46
- normalise: Normalization factor or array.
47
-
48
- Returns:
49
- list[pd.DataFrame]: List of normalized DataFrames.
50
-
51
- """
52
- if isinstance(normalise, int | float):
53
- return [i / normalise for i in results]
54
- if len(normalise) == len(results):
55
- return [(i.T / j).T for i, j in zip(results, normalise, strict=True)]
56
-
57
- results = []
58
- start = 0
59
- end = 0
60
- for i in results:
61
- end += len(i)
62
- results.append(i / np.reshape(normalise[start:end], (len(i), 1))) # type: ignore
63
- start += end
64
- return results
65
-
66
-
67
- @dataclass(kw_only=True, slots=True)
68
- class Result:
69
- """Simulation results."""
70
-
71
- model: Model
72
- raw_variables: list[pd.DataFrame]
73
- raw_parameters: list[dict[str, float]]
74
- raw_args: list[pd.DataFrame] = field(default_factory=list)
75
-
76
- @property
77
- def variables(self) -> pd.DataFrame:
78
- """Simulation variables."""
79
- return self.get_variables(
80
- include_derived_variables=True,
81
- include_readouts=True,
82
- concatenated=True,
83
- normalise=None,
84
- )
85
-
86
- @property
87
- def fluxes(self) -> pd.DataFrame:
88
- """Simulation fluxes."""
89
- return self.get_fluxes()
90
-
91
- def __iter__(self) -> Iterator[pd.DataFrame]:
92
- """Iterate over the concentration and flux response coefficients."""
93
- return iter((self.variables, self.fluxes))
94
-
95
- def _compute_args(self) -> list[pd.DataFrame]:
96
- # Already computed
97
- if len(self.raw_args) > 0:
98
- return self.raw_args
99
-
100
- # Compute new otherwise
101
- for res, p in zip(self.raw_variables, self.raw_parameters, strict=True):
102
- self.model.update_parameters(p)
103
- self.raw_args.append(
104
- self.model.get_args_time_course(
105
- variables=res,
106
- include_variables=True,
107
- include_parameters=True,
108
- include_derived_parameters=True,
109
- include_derived_variables=True,
110
- include_reactions=True,
111
- include_surrogate_outputs=True,
112
- include_readouts=True,
113
- )
114
- )
115
- return self.raw_args
116
-
117
- def _select_data(
118
- self,
119
- dependent: list[pd.DataFrame],
120
- *,
121
- include_variables: bool = False,
122
- include_parameters: bool = False,
123
- include_derived_parameters: bool = False,
124
- include_derived_variables: bool = False,
125
- include_reactions: bool = False,
126
- include_surrogate_outputs: bool = False,
127
- include_readouts: bool = False,
128
- ) -> list[pd.DataFrame]:
129
- names = self.model.get_arg_names(
130
- include_time=False,
131
- include_variables=include_variables,
132
- include_parameters=include_parameters,
133
- include_derived_parameters=include_derived_parameters,
134
- include_derived_variables=include_derived_variables,
135
- include_reactions=include_reactions,
136
- include_surrogate_outputs=include_surrogate_outputs,
137
- include_readouts=include_readouts,
138
- )
139
- return [i.loc[:, names] for i in dependent]
140
-
141
- def _adjust_data(
142
- self,
143
- data: list[pd.DataFrame],
144
- normalise: float | ArrayLike | None = None,
145
- *,
146
- concatenated: bool = True,
147
- ) -> pd.DataFrame | list[pd.DataFrame]:
148
- if normalise is not None:
149
- data = _normalise_split_results(data, normalise=normalise)
150
- if concatenated:
151
- return pd.concat(data, axis=0)
152
- return data
153
-
154
- @overload
155
- def get_args( # type: ignore
156
- self,
157
- *,
158
- include_variables: bool = True,
159
- include_parameters: bool = False,
160
- include_derived_parameters: bool = False,
161
- include_derived_variables: bool = True,
162
- include_reactions: bool = True,
163
- include_surrogate_outputs: bool = False,
164
- include_readouts: bool = False,
165
- concatenated: Literal[False],
166
- normalise: float | ArrayLike | None = None,
167
- ) -> list[pd.DataFrame]: ...
168
-
169
- @overload
170
- def get_args(
171
- self,
172
- *,
173
- include_variables: bool = True,
174
- include_parameters: bool = False,
175
- include_derived_parameters: bool = False,
176
- include_derived_variables: bool = True,
177
- include_reactions: bool = True,
178
- include_surrogate_outputs: bool = False,
179
- include_readouts: bool = False,
180
- concatenated: Literal[True],
181
- normalise: float | ArrayLike | None = None,
182
- ) -> pd.DataFrame: ...
183
-
184
- @overload
185
- def get_args(
186
- self,
187
- *,
188
- include_variables: bool = True,
189
- include_parameters: bool = False,
190
- include_derived_parameters: bool = False,
191
- include_derived_variables: bool = True,
192
- include_reactions: bool = True,
193
- include_surrogate_outputs: bool = False,
194
- include_readouts: bool = False,
195
- concatenated: bool = True,
196
- normalise: float | ArrayLike | None = None,
197
- ) -> pd.DataFrame: ...
198
-
199
- def get_args(
200
- self,
201
- *,
202
- include_variables: bool = True,
203
- include_parameters: bool = False,
204
- include_derived_parameters: bool = False,
205
- include_derived_variables: bool = True,
206
- include_reactions: bool = True,
207
- include_surrogate_outputs: bool = False,
208
- include_readouts: bool = False,
209
- concatenated: bool = True,
210
- normalise: float | ArrayLike | None = None,
211
- ) -> pd.DataFrame | list[pd.DataFrame]:
212
- """Get the variables over time.
213
-
214
- Examples:
215
- >>> Result().get_variables()
216
- Time ATP NADPH
217
- 0.000000 1.000000 1.000000
218
- 0.000100 0.999900 0.999900
219
- 0.000200 0.999800 0.999800
220
-
221
- """
222
- variables = self._select_data(
223
- self._compute_args(),
224
- include_variables=include_variables,
225
- include_parameters=include_parameters,
226
- include_derived_parameters=include_derived_parameters,
227
- include_derived_variables=include_derived_variables,
228
- include_reactions=include_reactions,
229
- include_surrogate_outputs=include_surrogate_outputs,
230
- include_readouts=include_readouts,
231
- )
232
- return self._adjust_data(
233
- variables, normalise=normalise, concatenated=concatenated
234
- )
235
-
236
- @overload
237
- def get_variables( # type: ignore
238
- self,
239
- *,
240
- include_derived_variables: bool = True,
241
- include_readouts: bool = True,
242
- concatenated: Literal[False],
243
- normalise: float | ArrayLike | None = None,
244
- ) -> list[pd.DataFrame]: ...
245
-
246
- @overload
247
- def get_variables(
248
- self,
249
- *,
250
- include_derived_variables: bool = True,
251
- include_readouts: bool = True,
252
- concatenated: Literal[True],
253
- normalise: float | ArrayLike | None = None,
254
- ) -> pd.DataFrame: ...
255
-
256
- @overload
257
- def get_variables(
258
- self,
259
- *,
260
- include_derived_variables: bool = True,
261
- include_readouts: bool = True,
262
- concatenated: bool = True,
263
- normalise: float | ArrayLike | None = None,
264
- ) -> pd.DataFrame: ...
265
-
266
- def get_variables(
267
- self,
268
- *,
269
- include_derived_variables: bool = True,
270
- include_readouts: bool = True,
271
- concatenated: bool = True,
272
- normalise: float | ArrayLike | None = None,
273
- ) -> pd.DataFrame | list[pd.DataFrame]:
274
- """Get the variables over time.
275
-
276
- Examples:
277
- >>> Result().get_variables()
278
- Time ATP NADPH
279
- 0.000000 1.000000 1.000000
280
- 0.000100 0.999900 0.999900
281
- 0.000200 0.999800 0.999800
282
-
283
- """
284
- if not include_derived_variables and not include_readouts:
285
- return self._adjust_data(
286
- self.raw_variables,
287
- normalise=normalise,
288
- concatenated=concatenated,
289
- )
290
-
291
- variables = self._select_data(
292
- self._compute_args(),
293
- include_variables=True,
294
- include_derived_variables=include_derived_variables,
295
- include_readouts=include_readouts,
296
- )
297
- return self._adjust_data(
298
- variables, normalise=normalise, concatenated=concatenated
299
- )
300
-
301
- @overload
302
- def get_fluxes( # type: ignore
303
- self,
304
- *,
305
- include_surrogates: bool = True,
306
- normalise: float | ArrayLike | None = None,
307
- concatenated: Literal[False],
308
- ) -> list[pd.DataFrame]: ...
309
-
310
- @overload
311
- def get_fluxes(
312
- self,
313
- *,
314
- include_surrogates: bool = True,
315
- normalise: float | ArrayLike | None = None,
316
- concatenated: Literal[True],
317
- ) -> pd.DataFrame: ...
318
-
319
- @overload
320
- def get_fluxes(
321
- self,
322
- *,
323
- include_surrogates: bool = True,
324
- normalise: float | ArrayLike | None = None,
325
- concatenated: bool = True,
326
- ) -> pd.DataFrame: ...
327
-
328
- def get_fluxes(
329
- self,
330
- *,
331
- include_surrogates: bool = True,
332
- normalise: float | ArrayLike | None = None,
333
- concatenated: bool = True,
334
- ) -> pd.DataFrame | list[pd.DataFrame]:
335
- """Get the flux results.
336
-
337
- Examples:
338
- >>> Result.get_fluxes()
339
- Time v1 v2
340
- 0.000000 1.000000 10.00000
341
- 0.000100 0.999900 9.999000
342
- 0.000200 0.999800 9.998000
343
-
344
- Returns:
345
- pd.DataFrame: DataFrame of fluxes.
346
-
347
- """
348
- fluxes = self._select_data(
349
- self._compute_args(),
350
- include_reactions=True,
351
- include_surrogate_outputs=include_surrogates,
352
- )
353
- return self._adjust_data(
354
- fluxes,
355
- normalise=normalise,
356
- concatenated=concatenated,
357
- )
358
-
359
- def get_combined(self) -> pd.DataFrame:
360
- """Get the variables and fluxes as a single pandas.DataFrame.
361
-
362
- Examples:
363
- >>> Result.get_combined()
364
- Time ATP NADPH v1 v2
365
- 0.000000 1.000000 1.000000 1.000000 10.00000
366
- 0.000100 0.999900 0.999900 0.999900 9.999000
367
- 0.000200 0.999800 0.999800 0.999800 9.998000
368
-
369
- Returns:
370
- pd.DataFrame: DataFrame of fluxes.
371
-
372
- """
373
- return pd.concat((self.variables, self.fluxes), axis=1)
374
-
375
- def get_new_y0(self) -> dict[str, float]:
376
- """Get the new initial conditions after the simulation.
377
-
378
- Examples:
379
- >>> Simulator(model).simulate_to_steady_state().get_new_y0()
380
- {"ATP": 1.0, "NADPH": 1.0}
381
-
382
- """
383
- return dict(
384
- self.get_variables(
385
- include_derived_variables=False,
386
- include_readouts=False,
387
- ).iloc[-1]
388
- )
389
-
390
-
391
36
  @dataclass(
392
37
  init=False,
393
38
  slots=True,
@@ -478,7 +123,7 @@ class Simulator:
478
123
  y0 = self.y0
479
124
  self.integrator = self._integrator_type(
480
125
  self.model,
481
- [y0[k] for k in self.model.get_variable_names()],
126
+ tuple(y0[k] for k in self.model.get_variable_names()),
482
127
  jac_fn,
483
128
  )
484
129