modelbase2 0.6.0__py3-none-any.whl → 0.7.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.
- modelbase2/__init__.py +2 -0
- modelbase2/experimental/codegen.py +48 -57
- modelbase2/experimental/diff.py +3 -4
- modelbase2/experimental/source_tools.py +30 -0
- modelbase2/experimental/symbolic.py +102 -61
- modelbase2/experimental/tex.py +9 -6
- modelbase2/fit.py +11 -4
- modelbase2/fns.py +6 -0
- modelbase2/integrators/int_assimulo.py +3 -3
- modelbase2/integrators/int_scipy.py +4 -5
- modelbase2/mca.py +6 -6
- modelbase2/model.py +48 -41
- modelbase2/nnarchitectures.py +9 -8
- modelbase2/npe.py +2 -1
- modelbase2/parameterise.py +1 -2
- modelbase2/sbml/_export.py +2 -14
- modelbase2/scan.py +7 -7
- modelbase2/simulator.py +336 -353
- modelbase2/surrogates/_poly.py +6 -2
- modelbase2/surrogates/_torch.py +1 -1
- modelbase2/types.py +3 -2
- {modelbase2-0.6.0.dist-info → modelbase2-0.7.0.dist-info}/METADATA +11 -1
- modelbase2-0.7.0.dist-info/RECORD +44 -0
- modelbase2/scope.py +0 -96
- modelbase2/surrogates.py +0 -287
- modelbase2-0.6.0.dist-info/RECORD +0 -45
- {modelbase2-0.6.0.dist-info → modelbase2-0.7.0.dist-info}/WHEEL +0 -0
- {modelbase2-0.6.0.dist-info → modelbase2-0.7.0.dist-info}/licenses/LICENSE +0 -0
modelbase2/simulator.py
CHANGED
@@ -10,7 +10,7 @@ Classes:
|
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
-
from dataclasses import dataclass
|
13
|
+
from dataclasses import dataclass, field
|
14
14
|
from typing import TYPE_CHECKING, Literal, Self, cast, overload
|
15
15
|
|
16
16
|
import numpy as np
|
@@ -18,13 +18,13 @@ import pandas as pd
|
|
18
18
|
|
19
19
|
from modelbase2.integrators import DefaultIntegrator
|
20
20
|
|
21
|
-
__all__ = ["Simulator"]
|
21
|
+
__all__ = ["Result", "Simulator"]
|
22
22
|
|
23
23
|
if TYPE_CHECKING:
|
24
|
-
from collections.abc import Callable
|
24
|
+
from collections.abc import Callable, Iterator
|
25
25
|
|
26
26
|
from modelbase2.model import Model
|
27
|
-
from modelbase2.types import ArrayLike, IntegratorProtocol
|
27
|
+
from modelbase2.types import Array, ArrayLike, IntegratorProtocol
|
28
28
|
|
29
29
|
|
30
30
|
def _normalise_split_results(
|
@@ -56,6 +56,240 @@ def _normalise_split_results(
|
|
56
56
|
return results
|
57
57
|
|
58
58
|
|
59
|
+
@dataclass(slots=True)
|
60
|
+
class Result:
|
61
|
+
"""Simulation results."""
|
62
|
+
|
63
|
+
model: Model
|
64
|
+
_raw_variables: list[pd.DataFrame]
|
65
|
+
_parameters: list[dict[str, float]]
|
66
|
+
_dependent: list[pd.DataFrame] = field(default_factory=list)
|
67
|
+
|
68
|
+
@property
|
69
|
+
def variables(self) -> pd.DataFrame:
|
70
|
+
"""Simulation variables."""
|
71
|
+
return self.get_variables(
|
72
|
+
include_derived=True,
|
73
|
+
include_readouts=True,
|
74
|
+
concatenated=True,
|
75
|
+
normalise=None,
|
76
|
+
)
|
77
|
+
|
78
|
+
@property
|
79
|
+
def fluxes(self) -> pd.DataFrame:
|
80
|
+
"""Simulation fluxes."""
|
81
|
+
return self.get_fluxes()
|
82
|
+
|
83
|
+
def __iter__(self) -> Iterator[pd.DataFrame]:
|
84
|
+
"""Iterate over the concentration and flux response coefficients."""
|
85
|
+
return iter((self.variables, self.fluxes))
|
86
|
+
|
87
|
+
def _get_dependent(
|
88
|
+
self,
|
89
|
+
*,
|
90
|
+
include_readouts: bool = True,
|
91
|
+
) -> list[pd.DataFrame]:
|
92
|
+
# Already computed
|
93
|
+
if len(self._dependent) > 0:
|
94
|
+
return self._dependent
|
95
|
+
|
96
|
+
# Compute new otherwise
|
97
|
+
for res, p in zip(self._raw_variables, self._parameters, strict=True):
|
98
|
+
self.model.update_parameters(p)
|
99
|
+
self._dependent.append(
|
100
|
+
self.model.get_dependent_time_course(
|
101
|
+
variables=res,
|
102
|
+
include_readouts=include_readouts,
|
103
|
+
)
|
104
|
+
)
|
105
|
+
return self._dependent
|
106
|
+
|
107
|
+
def _select_variables(
|
108
|
+
self,
|
109
|
+
dependent: list[pd.DataFrame],
|
110
|
+
*,
|
111
|
+
include_derived: bool,
|
112
|
+
include_readouts: bool,
|
113
|
+
) -> list[pd.DataFrame]:
|
114
|
+
names = self.model.get_variable_names()
|
115
|
+
if include_derived:
|
116
|
+
names.extend(self.model.get_derived_variable_names())
|
117
|
+
if include_readouts:
|
118
|
+
names.extend(self.model.get_readout_names())
|
119
|
+
return [i.loc[:, names] for i in dependent]
|
120
|
+
|
121
|
+
def _select_fluxes(
|
122
|
+
self,
|
123
|
+
dependent: list[pd.DataFrame],
|
124
|
+
*,
|
125
|
+
include_surrogates: bool,
|
126
|
+
) -> list[pd.DataFrame]:
|
127
|
+
names = self.model.get_reaction_names()
|
128
|
+
if include_surrogates:
|
129
|
+
names.extend(self.model.get_surrogate_reaction_names())
|
130
|
+
return [i.loc[:, names] for i in dependent]
|
131
|
+
|
132
|
+
def _adjust_data(
|
133
|
+
self,
|
134
|
+
data: list[pd.DataFrame],
|
135
|
+
normalise: float | ArrayLike | None = None,
|
136
|
+
*,
|
137
|
+
concatenated: bool = True,
|
138
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
139
|
+
if normalise is not None:
|
140
|
+
data = _normalise_split_results(data, normalise=normalise)
|
141
|
+
if concatenated:
|
142
|
+
return pd.concat(data, axis=0)
|
143
|
+
return data
|
144
|
+
|
145
|
+
@overload
|
146
|
+
def get_variables( # type: ignore
|
147
|
+
self,
|
148
|
+
*,
|
149
|
+
include_derived: bool = True,
|
150
|
+
include_readouts: bool = True,
|
151
|
+
concatenated: Literal[False],
|
152
|
+
normalise: float | ArrayLike | None = None,
|
153
|
+
) -> list[pd.DataFrame]: ...
|
154
|
+
|
155
|
+
@overload
|
156
|
+
def get_variables(
|
157
|
+
self,
|
158
|
+
*,
|
159
|
+
include_derived: bool = True,
|
160
|
+
include_readouts: bool = True,
|
161
|
+
concatenated: Literal[True],
|
162
|
+
normalise: float | ArrayLike | None = None,
|
163
|
+
) -> pd.DataFrame: ...
|
164
|
+
|
165
|
+
@overload
|
166
|
+
def get_variables(
|
167
|
+
self,
|
168
|
+
*,
|
169
|
+
include_derived: bool = True,
|
170
|
+
include_readouts: bool = True,
|
171
|
+
concatenated: bool = True,
|
172
|
+
normalise: float | ArrayLike | None = None,
|
173
|
+
) -> pd.DataFrame: ...
|
174
|
+
|
175
|
+
def get_variables(
|
176
|
+
self,
|
177
|
+
*,
|
178
|
+
include_derived: bool = True,
|
179
|
+
include_readouts: bool = True,
|
180
|
+
concatenated: bool = True,
|
181
|
+
normalise: float | ArrayLike | None = None,
|
182
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
183
|
+
"""Get the variables over time.
|
184
|
+
|
185
|
+
Examples:
|
186
|
+
>>> Result().get_variables()
|
187
|
+
Time ATP NADPH
|
188
|
+
0.000000 1.000000 1.000000
|
189
|
+
0.000100 0.999900 0.999900
|
190
|
+
0.000200 0.999800 0.999800
|
191
|
+
|
192
|
+
"""
|
193
|
+
if not include_derived and not include_readouts:
|
194
|
+
return self._adjust_data(
|
195
|
+
self._raw_variables,
|
196
|
+
normalise=normalise,
|
197
|
+
concatenated=concatenated,
|
198
|
+
)
|
199
|
+
|
200
|
+
variables = self._select_variables(
|
201
|
+
self._get_dependent(),
|
202
|
+
include_derived=include_derived,
|
203
|
+
include_readouts=include_readouts,
|
204
|
+
)
|
205
|
+
return self._adjust_data(
|
206
|
+
variables, normalise=normalise, concatenated=concatenated
|
207
|
+
)
|
208
|
+
|
209
|
+
@overload
|
210
|
+
def get_fluxes( # type: ignore
|
211
|
+
self,
|
212
|
+
*,
|
213
|
+
include_surrogates: bool = True,
|
214
|
+
normalise: float | ArrayLike | None = None,
|
215
|
+
concatenated: Literal[False],
|
216
|
+
) -> list[pd.DataFrame]: ...
|
217
|
+
|
218
|
+
@overload
|
219
|
+
def get_fluxes(
|
220
|
+
self,
|
221
|
+
*,
|
222
|
+
include_surrogates: bool = True,
|
223
|
+
normalise: float | ArrayLike | None = None,
|
224
|
+
concatenated: Literal[True],
|
225
|
+
) -> pd.DataFrame: ...
|
226
|
+
|
227
|
+
@overload
|
228
|
+
def get_fluxes(
|
229
|
+
self,
|
230
|
+
*,
|
231
|
+
include_surrogates: bool = True,
|
232
|
+
normalise: float | ArrayLike | None = None,
|
233
|
+
concatenated: bool = True,
|
234
|
+
) -> pd.DataFrame: ...
|
235
|
+
|
236
|
+
def get_fluxes(
|
237
|
+
self,
|
238
|
+
*,
|
239
|
+
include_surrogates: bool = True,
|
240
|
+
normalise: float | ArrayLike | None = None,
|
241
|
+
concatenated: bool = True,
|
242
|
+
) -> pd.DataFrame | list[pd.DataFrame]:
|
243
|
+
"""Get the flux results.
|
244
|
+
|
245
|
+
Examples:
|
246
|
+
>>> Result.get_fluxes()
|
247
|
+
Time v1 v2
|
248
|
+
0.000000 1.000000 10.00000
|
249
|
+
0.000100 0.999900 9.999000
|
250
|
+
0.000200 0.999800 9.998000
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
pd.DataFrame: DataFrame of fluxes.
|
254
|
+
|
255
|
+
"""
|
256
|
+
fluxes = self._select_fluxes(
|
257
|
+
self._get_dependent(),
|
258
|
+
include_surrogates=include_surrogates,
|
259
|
+
)
|
260
|
+
return self._adjust_data(
|
261
|
+
fluxes,
|
262
|
+
normalise=normalise,
|
263
|
+
concatenated=concatenated,
|
264
|
+
)
|
265
|
+
|
266
|
+
def get_combined(self) -> pd.DataFrame:
|
267
|
+
"""Get the variables and fluxes as a single pandas.DataFrame.
|
268
|
+
|
269
|
+
Examples:
|
270
|
+
>>> Result.get_combined()
|
271
|
+
Time ATP NADPH v1 v2
|
272
|
+
0.000000 1.000000 1.000000 1.000000 10.00000
|
273
|
+
0.000100 0.999900 0.999900 0.999900 9.999000
|
274
|
+
0.000200 0.999800 0.999800 0.999800 9.998000
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
pd.DataFrame: DataFrame of fluxes.
|
278
|
+
|
279
|
+
"""
|
280
|
+
return pd.concat((self.variables, self.fluxes), axis=1)
|
281
|
+
|
282
|
+
def get_new_y0(self) -> dict[str, float]:
|
283
|
+
"""Get the new initial conditions after the simulation.
|
284
|
+
|
285
|
+
Examples:
|
286
|
+
>>> Simulator(model).simulate_to_steady_state().get_new_y0()
|
287
|
+
{"ATP": 1.0, "NADPH": 1.0}
|
288
|
+
|
289
|
+
"""
|
290
|
+
return dict(self.variables.iloc[-1])
|
291
|
+
|
292
|
+
|
59
293
|
@dataclass(
|
60
294
|
init=False,
|
61
295
|
slots=True,
|
@@ -68,19 +302,23 @@ class Simulator:
|
|
68
302
|
model: Model instance to simulate.
|
69
303
|
y0: Initial conditions for the simulation.
|
70
304
|
integrator: Integrator protocol to use for the simulation.
|
71
|
-
|
305
|
+
variables: List of DataFrames containing concentration results.
|
72
306
|
dependent: List of DataFrames containing argument values.
|
73
307
|
simulation_parameters: List of dictionaries containing simulation parameters.
|
74
308
|
|
75
309
|
"""
|
76
310
|
|
77
311
|
model: Model
|
78
|
-
y0:
|
312
|
+
y0: dict[str, float]
|
79
313
|
integrator: IntegratorProtocol
|
80
|
-
|
314
|
+
variables: list[pd.DataFrame] | None
|
81
315
|
dependent: list[pd.DataFrame] | None
|
82
316
|
simulation_parameters: list[dict[str, float]] | None
|
83
317
|
|
318
|
+
# For resets (e.g. update variable)
|
319
|
+
_integrator_type: Callable[[Callable, ArrayLike], IntegratorProtocol]
|
320
|
+
_time_shift: float | None
|
321
|
+
|
84
322
|
def __init__(
|
85
323
|
self,
|
86
324
|
model: Model,
|
@@ -94,63 +332,44 @@ class Simulator:
|
|
94
332
|
"""Initialize the Simulator.
|
95
333
|
|
96
334
|
Args:
|
97
|
-
model
|
98
|
-
y0
|
99
|
-
If None, the initial conditions are obtained from the model.
|
100
|
-
integrator
|
101
|
-
|
102
|
-
test_run (bool, optional): If True, performs a test run to ensure the model's methods
|
103
|
-
(get_full_concs, get_fluxes, get_right_hand_side) work correctly with the initial conditions.
|
104
|
-
Defaults to True.
|
335
|
+
model: The model to be simulated.
|
336
|
+
y0: Initial conditions for the model variables.
|
337
|
+
If None, the initial conditions are obtained from the model.
|
338
|
+
integrator: The integrator to use for the simulation.
|
339
|
+
test_run (bool, optional): If True, performs a test run for better error messages
|
105
340
|
|
106
341
|
"""
|
107
342
|
self.model = model
|
108
|
-
y0 = model.get_initial_conditions() if y0 is None else y0
|
109
|
-
self.y0 = [y0[k] for k in model.get_variable_names()]
|
343
|
+
self.y0 = model.get_initial_conditions() if y0 is None else y0
|
110
344
|
|
111
|
-
self.
|
112
|
-
self.
|
345
|
+
self._integrator_type = integrator
|
346
|
+
self._time_shift = None
|
347
|
+
self.variables = None
|
113
348
|
self.simulation_parameters = None
|
114
349
|
|
115
350
|
if test_run:
|
116
|
-
|
117
|
-
self.model.get_right_hand_side(y0, 0)
|
118
|
-
|
119
|
-
def _save_simulation_results(
|
120
|
-
self,
|
121
|
-
*,
|
122
|
-
results: pd.DataFrame,
|
123
|
-
skipfirst: bool,
|
124
|
-
) -> None:
|
125
|
-
"""Save simulation results.
|
351
|
+
self.model.get_right_hand_side(self.y0, time=0)
|
126
352
|
|
127
|
-
|
128
|
-
results: DataFrame containing the simulation results.
|
129
|
-
skipfirst: Whether to skip the first row of results.
|
130
|
-
|
131
|
-
"""
|
132
|
-
if self.concs is None:
|
133
|
-
self.concs = [results]
|
134
|
-
elif skipfirst:
|
135
|
-
self.concs.append(results.iloc[1:, :])
|
136
|
-
else:
|
137
|
-
self.concs.append(results)
|
353
|
+
self._initialise_integrator()
|
138
354
|
|
139
|
-
|
140
|
-
|
141
|
-
self.
|
355
|
+
def _initialise_integrator(self) -> None:
|
356
|
+
y0 = self.y0
|
357
|
+
self.integrator = self._integrator_type(
|
358
|
+
self.model,
|
359
|
+
[y0[k] for k in self.model.get_variable_names()],
|
360
|
+
)
|
142
361
|
|
143
362
|
def clear_results(self) -> None:
|
144
363
|
"""Clear simulation results."""
|
145
|
-
self.
|
364
|
+
self.variables = None
|
146
365
|
self.dependent = None
|
147
366
|
self.simulation_parameters = None
|
148
|
-
|
149
|
-
|
367
|
+
self._time_shift = None
|
368
|
+
self._initialise_integrator()
|
150
369
|
|
151
370
|
def _handle_simulation_results(
|
152
371
|
self,
|
153
|
-
time:
|
372
|
+
time: Array | None,
|
154
373
|
results: ArrayLike | None,
|
155
374
|
*,
|
156
375
|
skipfirst: bool,
|
@@ -169,6 +388,9 @@ class Simulator:
|
|
169
388
|
self.clear_results()
|
170
389
|
return
|
171
390
|
|
391
|
+
if self._time_shift is not None:
|
392
|
+
time += self._time_shift
|
393
|
+
|
172
394
|
# NOTE: IMPORTANT!
|
173
395
|
# model._get_rhs sorts the return array by model.get_variable_names()
|
174
396
|
# Do NOT change this ordering
|
@@ -177,7 +399,17 @@ class Simulator:
|
|
177
399
|
index=time,
|
178
400
|
columns=self.model.get_variable_names(),
|
179
401
|
)
|
180
|
-
|
402
|
+
|
403
|
+
if self.variables is None:
|
404
|
+
self.variables = [results_df]
|
405
|
+
elif skipfirst:
|
406
|
+
self.variables.append(results_df.iloc[1:, :])
|
407
|
+
else:
|
408
|
+
self.variables.append(results_df)
|
409
|
+
|
410
|
+
if self.simulation_parameters is None:
|
411
|
+
self.simulation_parameters = []
|
412
|
+
self.simulation_parameters.append(self.model.parameters)
|
181
413
|
|
182
414
|
def simulate(
|
183
415
|
self,
|
@@ -201,7 +433,11 @@ class Simulator:
|
|
201
433
|
Self: The Simulator instance with updated results.
|
202
434
|
|
203
435
|
"""
|
436
|
+
if self._time_shift is not None:
|
437
|
+
t_end -= self._time_shift
|
438
|
+
|
204
439
|
time, results = self.integrator.integrate(t_end=t_end, steps=steps)
|
440
|
+
|
205
441
|
self._handle_simulation_results(time, results, skipfirst=True)
|
206
442
|
return self
|
207
443
|
|
@@ -223,6 +459,10 @@ class Simulator:
|
|
223
459
|
Self: The Simulator instance with updated results.
|
224
460
|
|
225
461
|
"""
|
462
|
+
if self._time_shift is not None:
|
463
|
+
time_points = np.array(time_points, dtype=float)
|
464
|
+
time_points -= self._time_shift
|
465
|
+
|
226
466
|
time, results = self.integrator.integrate_time_course(time_points=time_points)
|
227
467
|
self._handle_simulation_results(time, results, skipfirst=True)
|
228
468
|
return self
|
@@ -256,7 +496,7 @@ class Simulator:
|
|
256
496
|
rel_norm=rel_norm,
|
257
497
|
)
|
258
498
|
self._handle_simulation_results(
|
259
|
-
[time] if time is not None else None,
|
499
|
+
np.array([time], dtype=float) if time is not None else None,
|
260
500
|
[results] if results is not None else None, # type: ignore
|
261
501
|
skipfirst=False,
|
262
502
|
)
|
@@ -287,329 +527,37 @@ class Simulator:
|
|
287
527
|
t_end = cast(pd.Timedelta, t_end)
|
288
528
|
self.model.update_parameters(pars.to_dict())
|
289
529
|
self.simulate(t_end.total_seconds(), steps=time_points_per_step)
|
290
|
-
if self.
|
530
|
+
if self.variables is None:
|
291
531
|
break
|
292
532
|
return self
|
293
533
|
|
294
|
-
def
|
295
|
-
|
296
|
-
concs: list[pd.DataFrame],
|
297
|
-
params: list[dict[str, float]],
|
298
|
-
*,
|
299
|
-
include_readouts: bool = True,
|
300
|
-
) -> list[pd.DataFrame]:
|
301
|
-
dependent: list[pd.DataFrame] = []
|
302
|
-
|
303
|
-
for res, p in zip(concs, params, strict=True):
|
304
|
-
self.model.update_parameters(p)
|
305
|
-
dependent.append(
|
306
|
-
self.model.get_dependent_time_course(
|
307
|
-
concs=res,
|
308
|
-
include_readouts=include_readouts,
|
309
|
-
)
|
310
|
-
)
|
311
|
-
return dependent
|
312
|
-
|
313
|
-
@overload
|
314
|
-
def get_concs( # type: ignore
|
315
|
-
self,
|
316
|
-
*,
|
317
|
-
normalise: float | ArrayLike | None = None,
|
318
|
-
concatenated: Literal[False],
|
319
|
-
) -> None | list[pd.DataFrame]: ...
|
320
|
-
|
321
|
-
@overload
|
322
|
-
def get_concs(
|
323
|
-
self,
|
324
|
-
*,
|
325
|
-
normalise: float | ArrayLike | None = None,
|
326
|
-
concatenated: Literal[True],
|
327
|
-
) -> None | pd.DataFrame: ...
|
328
|
-
|
329
|
-
@overload
|
330
|
-
def get_concs(
|
331
|
-
self,
|
332
|
-
*,
|
333
|
-
normalise: float | ArrayLike | None = None,
|
334
|
-
concatenated: Literal[True] = True,
|
335
|
-
) -> None | pd.DataFrame: ...
|
336
|
-
|
337
|
-
def get_concs(
|
338
|
-
self,
|
339
|
-
*,
|
340
|
-
normalise: float | ArrayLike | None = None,
|
341
|
-
concatenated: bool = True,
|
342
|
-
) -> None | pd.DataFrame | list[pd.DataFrame]:
|
343
|
-
"""Get the concentration results.
|
534
|
+
def get_result(self) -> Result | None:
|
535
|
+
"""Get result of the simulation.
|
344
536
|
|
345
537
|
Examples:
|
346
|
-
>>> Simulator(model).
|
538
|
+
>>> variables, fluxes = Simulator(model).simulate().get_result()
|
539
|
+
>>> variables
|
347
540
|
Time ATP NADPH
|
348
541
|
0.000000 1.000000 1.000000
|
349
542
|
0.000100 0.999900 0.999900
|
350
543
|
0.000200 0.999800 0.999800
|
351
|
-
|
352
|
-
Returns:
|
353
|
-
pd.DataFrame: DataFrame of concentrations.
|
354
|
-
|
355
|
-
"""
|
356
|
-
if self.concs is None:
|
357
|
-
return None
|
358
|
-
|
359
|
-
results = self.concs.copy()
|
360
|
-
if normalise is not None:
|
361
|
-
results = _normalise_split_results(results=results, normalise=normalise)
|
362
|
-
if concatenated:
|
363
|
-
return pd.concat(results, axis=0)
|
364
|
-
|
365
|
-
return results
|
366
|
-
|
367
|
-
# Get results depending on the raw results
|
368
|
-
|
369
|
-
def _get_dependent(self) -> list[pd.DataFrame] | None:
|
370
|
-
if (concs := self.concs) is None:
|
371
|
-
return None
|
372
|
-
if (params := self.simulation_parameters) is None:
|
373
|
-
return None
|
374
|
-
|
375
|
-
return self._get_dependent_vectorised(concs, params)
|
376
|
-
|
377
|
-
@overload
|
378
|
-
def get_full_concs( # type: ignore
|
379
|
-
self,
|
380
|
-
*,
|
381
|
-
normalise: float | ArrayLike | None = None,
|
382
|
-
concatenated: Literal[False],
|
383
|
-
include_readouts: bool = True,
|
384
|
-
dependent: list[pd.DataFrame] | None = None,
|
385
|
-
) -> list[pd.DataFrame] | None: ...
|
386
|
-
|
387
|
-
@overload
|
388
|
-
def get_full_concs(
|
389
|
-
self,
|
390
|
-
*,
|
391
|
-
normalise: float | ArrayLike | None = None,
|
392
|
-
concatenated: Literal[True],
|
393
|
-
include_readouts: bool = True,
|
394
|
-
dependent: list[pd.DataFrame] | None = None,
|
395
|
-
) -> pd.DataFrame | None: ...
|
396
|
-
|
397
|
-
@overload
|
398
|
-
def get_full_concs(
|
399
|
-
self,
|
400
|
-
*,
|
401
|
-
normalise: float | ArrayLike | None = None,
|
402
|
-
concatenated: bool = True,
|
403
|
-
include_readouts: bool = True,
|
404
|
-
dependent: list[pd.DataFrame] | None = None,
|
405
|
-
) -> pd.DataFrame | None: ...
|
406
|
-
|
407
|
-
def get_full_concs(
|
408
|
-
self,
|
409
|
-
*,
|
410
|
-
normalise: float | ArrayLike | None = None,
|
411
|
-
concatenated: bool = True,
|
412
|
-
include_readouts: bool = True,
|
413
|
-
dependent: list[pd.DataFrame] | None = None,
|
414
|
-
) -> pd.DataFrame | list[pd.DataFrame] | None:
|
415
|
-
"""Get the full concentration results, including derived quantities.
|
416
|
-
|
417
|
-
Examples:
|
418
|
-
>>> Simulator(model).get_full_concs()
|
419
|
-
Time ATP NADPH
|
420
|
-
0.000000 1.000000 1.000000
|
421
|
-
0.000100 0.999900 0.999900
|
422
|
-
0.000200 0.999800 0.999800
|
423
|
-
|
424
|
-
Returns: DataFrame of full concentrations.
|
425
|
-
|
426
|
-
"""
|
427
|
-
if (concs := self.concs) is None:
|
428
|
-
return None
|
429
|
-
if (params := self.simulation_parameters) is None:
|
430
|
-
return None
|
431
|
-
if dependent is None:
|
432
|
-
dependent = self._get_dependent_vectorised(concs, params)
|
433
|
-
|
434
|
-
# Filter to full concs
|
435
|
-
names = (
|
436
|
-
self.model.get_variable_names() + self.model.get_derived_variable_names()
|
437
|
-
)
|
438
|
-
if include_readouts:
|
439
|
-
names.extend(self.model.get_readout_names())
|
440
|
-
dependent = [i.loc[:, names] for i in dependent]
|
441
|
-
|
442
|
-
if normalise is not None:
|
443
|
-
dependent = _normalise_split_results(
|
444
|
-
results=dependent,
|
445
|
-
normalise=normalise,
|
446
|
-
)
|
447
|
-
if concatenated:
|
448
|
-
return pd.concat(dependent, axis=0)
|
449
|
-
return dependent
|
450
|
-
|
451
|
-
@overload
|
452
|
-
def get_fluxes( # type: ignore
|
453
|
-
self,
|
454
|
-
*,
|
455
|
-
normalise: float | ArrayLike | None = None,
|
456
|
-
concatenated: Literal[False],
|
457
|
-
dependent: list[pd.DataFrame] | None = None,
|
458
|
-
) -> list[pd.DataFrame] | None: ...
|
459
|
-
|
460
|
-
@overload
|
461
|
-
def get_fluxes(
|
462
|
-
self,
|
463
|
-
*,
|
464
|
-
normalise: float | ArrayLike | None = None,
|
465
|
-
concatenated: Literal[True],
|
466
|
-
dependent: list[pd.DataFrame] | None = None,
|
467
|
-
) -> pd.DataFrame | None: ...
|
468
|
-
|
469
|
-
@overload
|
470
|
-
def get_fluxes(
|
471
|
-
self,
|
472
|
-
*,
|
473
|
-
normalise: float | ArrayLike | None = None,
|
474
|
-
concatenated: bool = True,
|
475
|
-
dependent: list[pd.DataFrame] | None = None,
|
476
|
-
) -> pd.DataFrame | None: ...
|
477
|
-
|
478
|
-
def get_fluxes(
|
479
|
-
self,
|
480
|
-
*,
|
481
|
-
normalise: float | ArrayLike | None = None,
|
482
|
-
concatenated: bool = True,
|
483
|
-
dependent: list[pd.DataFrame] | None = None,
|
484
|
-
) -> pd.DataFrame | list[pd.DataFrame] | None:
|
485
|
-
"""Get the flux results.
|
486
|
-
|
487
|
-
Examples:
|
488
|
-
>>> Simulator(model).get_fluxes()
|
544
|
+
>>> fluxes
|
489
545
|
Time v1 v2
|
490
546
|
0.000000 1.000000 10.00000
|
491
547
|
0.000100 0.999900 9.999000
|
492
548
|
0.000200 0.999800 9.998000
|
493
549
|
|
494
|
-
Returns:
|
495
|
-
pd.DataFrame: DataFrame of fluxes.
|
496
|
-
|
497
550
|
"""
|
498
|
-
if (
|
551
|
+
if (variables := self.variables) is None:
|
499
552
|
return None
|
500
|
-
if (
|
553
|
+
if (parameters := self.simulation_parameters) is None:
|
501
554
|
return None
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
names = self.model.get_reaction_names()
|
507
|
-
dependent = [i.loc[:, names] for i in dependent]
|
508
|
-
if normalise is not None:
|
509
|
-
dependent = _normalise_split_results(
|
510
|
-
results=dependent,
|
511
|
-
normalise=normalise,
|
512
|
-
)
|
513
|
-
if concatenated:
|
514
|
-
return pd.concat(dependent, axis=0)
|
515
|
-
return dependent
|
516
|
-
|
517
|
-
def get_concs_and_fluxes(self) -> tuple[pd.DataFrame | None, pd.DataFrame | None]:
|
518
|
-
"""Get the concentrations and fluxes.
|
519
|
-
|
520
|
-
Examples:
|
521
|
-
>>> Simulator(model).get_concs_and_fluxes()
|
522
|
-
(concs, fluxes)
|
523
|
-
|
524
|
-
|
525
|
-
Returns:
|
526
|
-
tuple[pd.DataFrame, pd.DataFrame]: Tuple of concentrations and fluxes.
|
527
|
-
|
528
|
-
"""
|
529
|
-
if (concs := self.concs) is None:
|
530
|
-
return None, None
|
531
|
-
if (params := self.simulation_parameters) is None:
|
532
|
-
return None, None
|
533
|
-
|
534
|
-
dependent = self._get_dependent_vectorised(concs, params)
|
535
|
-
|
536
|
-
return self.get_concs(), self.get_fluxes(dependent=dependent)
|
537
|
-
|
538
|
-
def get_full_concs_and_fluxes(
|
539
|
-
self,
|
540
|
-
*,
|
541
|
-
include_readouts: bool = True,
|
542
|
-
) -> tuple[pd.DataFrame | None, pd.DataFrame | None]:
|
543
|
-
"""Get the full concentrations and fluxes.
|
544
|
-
|
545
|
-
>>> Simulator(model).get_full_concs_and_fluxes()
|
546
|
-
(full_concs, full_fluxes)
|
547
|
-
|
548
|
-
Args:
|
549
|
-
include_readouts: Whether to include readouts in the results.
|
550
|
-
|
551
|
-
Returns:
|
552
|
-
Full concentrations and fluxes
|
553
|
-
|
554
|
-
"""
|
555
|
-
if (concs := self.concs) is None:
|
556
|
-
return None, None
|
557
|
-
if (params := self.simulation_parameters) is None:
|
558
|
-
return None, None
|
559
|
-
dependent = self._get_dependent_vectorised(concs, params)
|
560
|
-
|
561
|
-
return (
|
562
|
-
self.get_full_concs(include_readouts=include_readouts, dependent=dependent),
|
563
|
-
self.get_fluxes(dependent=dependent),
|
555
|
+
return Result(
|
556
|
+
model=self.model,
|
557
|
+
_raw_variables=variables,
|
558
|
+
_parameters=parameters,
|
564
559
|
)
|
565
560
|
|
566
|
-
def get_results(self) -> pd.DataFrame | None:
|
567
|
-
"""Get the combined results of concentrations and fluxes.
|
568
|
-
|
569
|
-
Examples:
|
570
|
-
>>> Simulator(model).get_results()
|
571
|
-
Time ATP NADPH v1 v2
|
572
|
-
0.000000 1.000000 1.000000 1.000000 1.000000
|
573
|
-
0.000100 0.999900 0.999900 0.999900 0.999900
|
574
|
-
0.000200 0.999800 0.999800 0.999800 0.999800
|
575
|
-
|
576
|
-
Returns:
|
577
|
-
pd.DataFrame: Combined DataFrame of concentrations and fluxes.
|
578
|
-
|
579
|
-
"""
|
580
|
-
c, v = self.get_concs_and_fluxes()
|
581
|
-
if c is None or v is None:
|
582
|
-
return None
|
583
|
-
return pd.concat((c, v), axis=1)
|
584
|
-
|
585
|
-
def get_full_results(self) -> pd.DataFrame | None:
|
586
|
-
"""Get the combined full results of concentrations and fluxes.
|
587
|
-
|
588
|
-
Examples:
|
589
|
-
>>> Simulator(model).get_full_results()
|
590
|
-
Time ATP NADPH v1 v2
|
591
|
-
0.000000 1.000000 1.000000 1.000000 1.000000
|
592
|
-
0.000100 0.999900 0.999900 0.999900 0.999900
|
593
|
-
0.000200 0.999800 0.999800 0.999800 0.999800
|
594
|
-
|
595
|
-
"""
|
596
|
-
c, v = self.get_full_concs_and_fluxes()
|
597
|
-
if c is None or v is None:
|
598
|
-
return None
|
599
|
-
return pd.concat((c, v), axis=1)
|
600
|
-
|
601
|
-
def get_new_y0(self) -> dict[str, float] | None:
|
602
|
-
"""Get the new initial conditions after the simulation.
|
603
|
-
|
604
|
-
Examples:
|
605
|
-
>>> Simulator(model).get_new_y0()
|
606
|
-
{"ATP": 1.0, "NADPH": 1.0}
|
607
|
-
|
608
|
-
"""
|
609
|
-
if (res := self.get_concs()) is None:
|
610
|
-
return None
|
611
|
-
return dict(res.iloc[-1])
|
612
|
-
|
613
561
|
def update_parameter(self, parameter: str, value: float) -> Self:
|
614
562
|
"""Updates the value of a specified parameter in the model.
|
615
563
|
|
@@ -665,3 +613,38 @@ class Simulator:
|
|
665
613
|
"""
|
666
614
|
self.model.scale_parameters(parameters)
|
667
615
|
return self
|
616
|
+
|
617
|
+
def update_variable(self, variable: str, value: float) -> Self:
|
618
|
+
"""Updates the value of a specified value in the simulation.
|
619
|
+
|
620
|
+
Examples:
|
621
|
+
>>> Simulator(model).update_variable("k1", 0.1)
|
622
|
+
|
623
|
+
Args:
|
624
|
+
variable: name of the model variable
|
625
|
+
value: new value
|
626
|
+
|
627
|
+
"""
|
628
|
+
return self.update_variables({variable: value})
|
629
|
+
|
630
|
+
def update_variables(self, variables: dict[str, float]) -> Self:
|
631
|
+
"""Updates the value of a specified value in the simulation.
|
632
|
+
|
633
|
+
Examples:
|
634
|
+
>>> Simulator(model).update_variables({"k1": 0.1})
|
635
|
+
|
636
|
+
Args:
|
637
|
+
variables: {variable: value} pairs
|
638
|
+
|
639
|
+
"""
|
640
|
+
sim_variables = self.variables
|
641
|
+
|
642
|
+
# In case someone calls this before the first simulation
|
643
|
+
if sim_variables is None:
|
644
|
+
self.y0 = self.y0 | variables
|
645
|
+
return self
|
646
|
+
|
647
|
+
self.y0 = sim_variables[-1].iloc[-1, :].to_dict() | variables
|
648
|
+
self._time_shift = float(sim_variables[-1].index[-1])
|
649
|
+
self._initialise_integrator()
|
650
|
+
return self
|