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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mxlpy/simulator.py CHANGED
@@ -11,383 +11,29 @@ 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
19
19
  from sympy import lambdify
20
+ from wadler_lindig import pformat
20
21
 
21
22
  from mxlpy.integrators import DefaultIntegrator
22
23
  from mxlpy.symbolic import to_symbolic_model
24
+ from mxlpy.types import Result
23
25
 
24
26
  if TYPE_CHECKING:
25
- from collections.abc import Iterator
26
-
27
27
  from mxlpy.model import Model
28
28
  from mxlpy.types import Array, ArrayLike, IntegratorProtocol, IntegratorType
29
29
 
30
30
  _LOGGER = logging.getLogger(__name__)
31
31
 
32
32
  __all__ = [
33
- "Result",
34
33
  "Simulator",
35
34
  ]
36
35
 
37
36
 
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
37
  @dataclass(
392
38
  init=False,
393
39
  slots=True,
@@ -418,6 +64,10 @@ class Simulator:
418
64
  _integrator_type: IntegratorType
419
65
  _time_shift: float | None
420
66
 
67
+ def __repr__(self) -> str:
68
+ """Return default representation."""
69
+ return pformat(self)
70
+
421
71
  def __init__(
422
72
  self,
423
73
  model: Model,
@@ -478,7 +128,7 @@ class Simulator:
478
128
  y0 = self.y0
479
129
  self.integrator = self._integrator_type(
480
130
  self.model,
481
- [y0[k] for k in self.model.get_variable_names()],
131
+ tuple(y0[k] for k in self.model.get_variable_names()),
482
132
  jac_fn,
483
133
  )
484
134