modelbase2 0.5.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 +12 -7
- 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/linear_label_map.py +4 -2
- modelbase2/mca.py +6 -6
- modelbase2/model.py +182 -130
- modelbase2/nnarchitectures.py +9 -8
- modelbase2/npe.py +2 -1
- modelbase2/parameterise.py +1 -2
- modelbase2/sbml/_export.py +2 -14
- modelbase2/sbml/_import.py +11 -1
- modelbase2/scan.py +8 -8
- modelbase2/simulator.py +340 -329
- modelbase2/surrogates/_poly.py +6 -2
- modelbase2/surrogates/_torch.py +1 -1
- modelbase2/types.py +129 -16
- {modelbase2-0.5.0.dist-info → modelbase2-0.7.0.dist-info}/METADATA +11 -1
- modelbase2-0.7.0.dist-info/RECORD +44 -0
- modelbase2/experimental/_backup.py +0 -1017
- modelbase2/scope.py +0 -96
- modelbase2/surrogates.py +0 -322
- modelbase2-0.5.0.dist-info/RECORD +0 -46
- {modelbase2-0.5.0.dist-info → modelbase2-0.7.0.dist-info}/WHEEL +0 -0
- {modelbase2-0.5.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
|
-
|
72
|
-
|
305
|
+
variables: List of DataFrames containing concentration results.
|
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
|
-
|
81
|
-
|
314
|
+
variables: list[pd.DataFrame] | None
|
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,66 +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.
|
113
|
-
self.
|
345
|
+
self._integrator_type = integrator
|
346
|
+
self._time_shift = None
|
347
|
+
self.variables = None
|
114
348
|
self.simulation_parameters = None
|
115
349
|
|
116
350
|
if test_run:
|
117
|
-
|
118
|
-
self.model.get_full_concs(y0, 0)
|
119
|
-
self.model.get_fluxes(y0, 0)
|
120
|
-
self.model.get_right_hand_side(y0, 0)
|
121
|
-
|
122
|
-
def _save_simulation_results(
|
123
|
-
self,
|
124
|
-
*,
|
125
|
-
results: pd.DataFrame,
|
126
|
-
skipfirst: bool,
|
127
|
-
) -> None:
|
128
|
-
"""Save simulation results.
|
129
|
-
|
130
|
-
Args:
|
131
|
-
results: DataFrame containing the simulation results.
|
132
|
-
skipfirst: Whether to skip the first row of results.
|
351
|
+
self.model.get_right_hand_side(self.y0, time=0)
|
133
352
|
|
134
|
-
|
135
|
-
if self.concs is None:
|
136
|
-
self.concs = [results]
|
137
|
-
elif skipfirst:
|
138
|
-
self.concs.append(results.iloc[1:, :])
|
139
|
-
else:
|
140
|
-
self.concs.append(results)
|
353
|
+
self._initialise_integrator()
|
141
354
|
|
142
|
-
|
143
|
-
|
144
|
-
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
|
+
)
|
145
361
|
|
146
362
|
def clear_results(self) -> None:
|
147
363
|
"""Clear simulation results."""
|
148
|
-
self.
|
149
|
-
self.
|
364
|
+
self.variables = None
|
365
|
+
self.dependent = None
|
150
366
|
self.simulation_parameters = None
|
151
|
-
|
152
|
-
|
367
|
+
self._time_shift = None
|
368
|
+
self._initialise_integrator()
|
153
369
|
|
154
370
|
def _handle_simulation_results(
|
155
371
|
self,
|
156
|
-
time:
|
372
|
+
time: Array | None,
|
157
373
|
results: ArrayLike | None,
|
158
374
|
*,
|
159
375
|
skipfirst: bool,
|
@@ -172,15 +388,28 @@ class Simulator:
|
|
172
388
|
self.clear_results()
|
173
389
|
return
|
174
390
|
|
391
|
+
if self._time_shift is not None:
|
392
|
+
time += self._time_shift
|
393
|
+
|
175
394
|
# NOTE: IMPORTANT!
|
176
|
-
# model._get_rhs sorts the return array by model.
|
395
|
+
# model._get_rhs sorts the return array by model.get_variable_names()
|
177
396
|
# Do NOT change this ordering
|
178
397
|
results_df = pd.DataFrame(
|
179
398
|
results,
|
180
399
|
index=time,
|
181
400
|
columns=self.model.get_variable_names(),
|
182
401
|
)
|
183
|
-
|
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)
|
184
413
|
|
185
414
|
def simulate(
|
186
415
|
self,
|
@@ -204,7 +433,11 @@ class Simulator:
|
|
204
433
|
Self: The Simulator instance with updated results.
|
205
434
|
|
206
435
|
"""
|
436
|
+
if self._time_shift is not None:
|
437
|
+
t_end -= self._time_shift
|
438
|
+
|
207
439
|
time, results = self.integrator.integrate(t_end=t_end, steps=steps)
|
440
|
+
|
208
441
|
self._handle_simulation_results(time, results, skipfirst=True)
|
209
442
|
return self
|
210
443
|
|
@@ -226,6 +459,10 @@ class Simulator:
|
|
226
459
|
Self: The Simulator instance with updated results.
|
227
460
|
|
228
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
|
+
|
229
466
|
time, results = self.integrator.integrate_time_course(time_points=time_points)
|
230
467
|
self._handle_simulation_results(time, results, skipfirst=True)
|
231
468
|
return self
|
@@ -259,7 +496,7 @@ class Simulator:
|
|
259
496
|
rel_norm=rel_norm,
|
260
497
|
)
|
261
498
|
self._handle_simulation_results(
|
262
|
-
[time] if time is not None else None,
|
499
|
+
np.array([time], dtype=float) if time is not None else None,
|
263
500
|
[results] if results is not None else None, # type: ignore
|
264
501
|
skipfirst=False,
|
265
502
|
)
|
@@ -290,298 +527,37 @@ class Simulator:
|
|
290
527
|
t_end = cast(pd.Timedelta, t_end)
|
291
528
|
self.model.update_parameters(pars.to_dict())
|
292
529
|
self.simulate(t_end.total_seconds(), steps=time_points_per_step)
|
293
|
-
if self.
|
530
|
+
if self.variables is None:
|
294
531
|
break
|
295
532
|
return self
|
296
533
|
|
297
|
-
def
|
298
|
-
|
299
|
-
concs: list[pd.DataFrame],
|
300
|
-
params: list[dict[str, float]],
|
301
|
-
*,
|
302
|
-
include_readouts: bool = True,
|
303
|
-
) -> list[pd.DataFrame]:
|
304
|
-
args: list[pd.DataFrame] = []
|
305
|
-
|
306
|
-
for res, p in zip(concs, params, strict=True):
|
307
|
-
self.model.update_parameters(p)
|
308
|
-
args.append(
|
309
|
-
self.model.get_args_time_course(
|
310
|
-
concs=res,
|
311
|
-
include_readouts=include_readouts,
|
312
|
-
)
|
313
|
-
)
|
314
|
-
return args
|
315
|
-
|
316
|
-
@overload
|
317
|
-
def get_concs( # type: ignore
|
318
|
-
self,
|
319
|
-
*,
|
320
|
-
normalise: float | ArrayLike | None = None,
|
321
|
-
concatenated: Literal[False],
|
322
|
-
) -> None | list[pd.DataFrame]: ...
|
323
|
-
|
324
|
-
@overload
|
325
|
-
def get_concs(
|
326
|
-
self,
|
327
|
-
*,
|
328
|
-
normalise: float | ArrayLike | None = None,
|
329
|
-
concatenated: Literal[True],
|
330
|
-
) -> None | pd.DataFrame: ...
|
331
|
-
|
332
|
-
@overload
|
333
|
-
def get_concs(
|
334
|
-
self,
|
335
|
-
*,
|
336
|
-
normalise: float | ArrayLike | None = None,
|
337
|
-
concatenated: Literal[True] = True,
|
338
|
-
) -> None | pd.DataFrame: ...
|
339
|
-
|
340
|
-
def get_concs(
|
341
|
-
self,
|
342
|
-
*,
|
343
|
-
normalise: float | ArrayLike | None = None,
|
344
|
-
concatenated: bool = True,
|
345
|
-
) -> None | pd.DataFrame | list[pd.DataFrame]:
|
346
|
-
"""Get the concentration results.
|
347
|
-
|
348
|
-
Examples:
|
349
|
-
>>> Simulator(model).get_concs()
|
350
|
-
Time ATP NADPH
|
351
|
-
0.000000 1.000000 1.000000
|
352
|
-
0.000100 0.999900 0.999900
|
353
|
-
0.000200 0.999800 0.999800
|
354
|
-
|
355
|
-
Returns:
|
356
|
-
pd.DataFrame: DataFrame of concentrations.
|
357
|
-
|
358
|
-
"""
|
359
|
-
if self.concs is None:
|
360
|
-
return None
|
361
|
-
|
362
|
-
results = self.concs.copy()
|
363
|
-
if normalise is not None:
|
364
|
-
results = _normalise_split_results(results=results, normalise=normalise)
|
365
|
-
if concatenated:
|
366
|
-
return pd.concat(results, axis=0)
|
367
|
-
|
368
|
-
return results
|
369
|
-
|
370
|
-
@overload
|
371
|
-
def get_full_concs( # type: ignore
|
372
|
-
self,
|
373
|
-
*,
|
374
|
-
normalise: float | ArrayLike | None = None,
|
375
|
-
concatenated: Literal[False],
|
376
|
-
include_readouts: bool = True,
|
377
|
-
) -> list[pd.DataFrame] | None: ...
|
378
|
-
|
379
|
-
@overload
|
380
|
-
def get_full_concs(
|
381
|
-
self,
|
382
|
-
*,
|
383
|
-
normalise: float | ArrayLike | None = None,
|
384
|
-
concatenated: Literal[True],
|
385
|
-
include_readouts: bool = True,
|
386
|
-
) -> pd.DataFrame | None: ...
|
387
|
-
|
388
|
-
@overload
|
389
|
-
def get_full_concs(
|
390
|
-
self,
|
391
|
-
*,
|
392
|
-
normalise: float | ArrayLike | None = None,
|
393
|
-
concatenated: bool = True,
|
394
|
-
include_readouts: bool = True,
|
395
|
-
) -> pd.DataFrame | None: ...
|
396
|
-
|
397
|
-
def get_full_concs(
|
398
|
-
self,
|
399
|
-
*,
|
400
|
-
normalise: float | ArrayLike | None = None,
|
401
|
-
concatenated: bool = True,
|
402
|
-
include_readouts: bool = True,
|
403
|
-
) -> pd.DataFrame | list[pd.DataFrame] | None:
|
404
|
-
"""Get the full concentration results, including derived quantities.
|
534
|
+
def get_result(self) -> Result | None:
|
535
|
+
"""Get result of the simulation.
|
405
536
|
|
406
537
|
Examples:
|
407
|
-
>>> Simulator(model).
|
538
|
+
>>> variables, fluxes = Simulator(model).simulate().get_result()
|
539
|
+
>>> variables
|
408
540
|
Time ATP NADPH
|
409
541
|
0.000000 1.000000 1.000000
|
410
542
|
0.000100 0.999900 0.999900
|
411
543
|
0.000200 0.999800 0.999800
|
412
|
-
|
413
|
-
Returns: DataFrame of full concentrations.
|
414
|
-
|
415
|
-
"""
|
416
|
-
if (concs := self.concs) is None:
|
417
|
-
return None
|
418
|
-
if (params := self.simulation_parameters) is None:
|
419
|
-
return None
|
420
|
-
if (args := self.args) is None:
|
421
|
-
args = self._get_args_vectorised(concs, params)
|
422
|
-
|
423
|
-
names = (
|
424
|
-
self.model.get_variable_names() + self.model.get_derived_variable_names()
|
425
|
-
)
|
426
|
-
if include_readouts:
|
427
|
-
names.extend(self.model.get_readout_names())
|
428
|
-
full_concs = [i.loc[:, names] for i in args]
|
429
|
-
if normalise is not None:
|
430
|
-
full_concs = _normalise_split_results(
|
431
|
-
results=full_concs,
|
432
|
-
normalise=normalise,
|
433
|
-
)
|
434
|
-
if concatenated:
|
435
|
-
return pd.concat(full_concs, axis=0)
|
436
|
-
return full_concs
|
437
|
-
|
438
|
-
@overload
|
439
|
-
def get_fluxes( # type: ignore
|
440
|
-
self,
|
441
|
-
*,
|
442
|
-
normalise: float | ArrayLike | None = None,
|
443
|
-
concatenated: Literal[False],
|
444
|
-
) -> list[pd.DataFrame] | None: ...
|
445
|
-
|
446
|
-
@overload
|
447
|
-
def get_fluxes(
|
448
|
-
self,
|
449
|
-
*,
|
450
|
-
normalise: float | ArrayLike | None = None,
|
451
|
-
concatenated: Literal[True],
|
452
|
-
) -> pd.DataFrame | None: ...
|
453
|
-
|
454
|
-
@overload
|
455
|
-
def get_fluxes(
|
456
|
-
self,
|
457
|
-
*,
|
458
|
-
normalise: float | ArrayLike | None = None,
|
459
|
-
concatenated: bool = True,
|
460
|
-
) -> pd.DataFrame | None: ...
|
461
|
-
|
462
|
-
def get_fluxes(
|
463
|
-
self,
|
464
|
-
*,
|
465
|
-
normalise: float | ArrayLike | None = None,
|
466
|
-
concatenated: bool = True,
|
467
|
-
) -> pd.DataFrame | list[pd.DataFrame] | None:
|
468
|
-
"""Get the flux results.
|
469
|
-
|
470
|
-
Examples:
|
471
|
-
>>> Simulator(model).get_fluxes()
|
544
|
+
>>> fluxes
|
472
545
|
Time v1 v2
|
473
546
|
0.000000 1.000000 10.00000
|
474
547
|
0.000100 0.999900 9.999000
|
475
548
|
0.000200 0.999800 9.998000
|
476
549
|
|
477
|
-
Returns:
|
478
|
-
pd.DataFrame: DataFrame of fluxes.
|
479
|
-
|
480
550
|
"""
|
481
|
-
if (
|
551
|
+
if (variables := self.variables) is None:
|
482
552
|
return None
|
483
|
-
if (
|
553
|
+
if (parameters := self.simulation_parameters) is None:
|
484
554
|
return None
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
for y, p in zip(args, params, strict=True):
|
490
|
-
self.model.update_parameters(p)
|
491
|
-
fluxes.append(self.model.get_fluxes_time_course(args=y))
|
492
|
-
|
493
|
-
if normalise is not None:
|
494
|
-
fluxes = _normalise_split_results(
|
495
|
-
results=fluxes,
|
496
|
-
normalise=normalise,
|
497
|
-
)
|
498
|
-
if concatenated:
|
499
|
-
return pd.concat(fluxes, axis=0)
|
500
|
-
return fluxes
|
501
|
-
|
502
|
-
def get_concs_and_fluxes(self) -> tuple[pd.DataFrame | None, pd.DataFrame | None]:
|
503
|
-
"""Get the concentrations and fluxes.
|
504
|
-
|
505
|
-
Examples:
|
506
|
-
>>> Simulator(model).get_concs_and_fluxes()
|
507
|
-
(concs, fluxes)
|
508
|
-
|
509
|
-
|
510
|
-
Returns:
|
511
|
-
tuple[pd.DataFrame, pd.DataFrame]: Tuple of concentrations and fluxes.
|
512
|
-
|
513
|
-
"""
|
514
|
-
return self.get_concs(), self.get_fluxes()
|
515
|
-
|
516
|
-
def get_full_concs_and_fluxes(
|
517
|
-
self,
|
518
|
-
*,
|
519
|
-
include_readouts: bool = True,
|
520
|
-
) -> tuple[pd.DataFrame | None, pd.DataFrame | None]:
|
521
|
-
"""Get the full concentrations and fluxes.
|
522
|
-
|
523
|
-
>>> Simulator(model).get_full_concs_and_fluxes()
|
524
|
-
(full_concs, full_fluxes)
|
525
|
-
|
526
|
-
Args:
|
527
|
-
include_readouts: Whether to include readouts in the results.
|
528
|
-
|
529
|
-
Returns:
|
530
|
-
Full concentrations and fluxes
|
531
|
-
|
532
|
-
"""
|
533
|
-
return (
|
534
|
-
self.get_full_concs(include_readouts=include_readouts),
|
535
|
-
self.get_fluxes(),
|
555
|
+
return Result(
|
556
|
+
model=self.model,
|
557
|
+
_raw_variables=variables,
|
558
|
+
_parameters=parameters,
|
536
559
|
)
|
537
560
|
|
538
|
-
def get_results(self) -> pd.DataFrame | None:
|
539
|
-
"""Get the combined results of concentrations and fluxes.
|
540
|
-
|
541
|
-
Examples:
|
542
|
-
>>> Simulator(model).get_results()
|
543
|
-
Time ATP NADPH v1 v2
|
544
|
-
0.000000 1.000000 1.000000 1.000000 1.000000
|
545
|
-
0.000100 0.999900 0.999900 0.999900 0.999900
|
546
|
-
0.000200 0.999800 0.999800 0.999800 0.999800
|
547
|
-
|
548
|
-
Returns:
|
549
|
-
pd.DataFrame: Combined DataFrame of concentrations and fluxes.
|
550
|
-
|
551
|
-
"""
|
552
|
-
c, v = self.get_concs_and_fluxes()
|
553
|
-
if c is None or v is None:
|
554
|
-
return None
|
555
|
-
return pd.concat((c, v), axis=1)
|
556
|
-
|
557
|
-
def get_full_results(self) -> pd.DataFrame | None:
|
558
|
-
"""Get the combined full results of concentrations and fluxes.
|
559
|
-
|
560
|
-
Examples:
|
561
|
-
>>> Simulator(model).get_full_results()
|
562
|
-
Time ATP NADPH v1 v2
|
563
|
-
0.000000 1.000000 1.000000 1.000000 1.000000
|
564
|
-
0.000100 0.999900 0.999900 0.999900 0.999900
|
565
|
-
0.000200 0.999800 0.999800 0.999800 0.999800
|
566
|
-
|
567
|
-
"""
|
568
|
-
c, v = self.get_full_concs_and_fluxes()
|
569
|
-
if c is None or v is None:
|
570
|
-
return None
|
571
|
-
return pd.concat((c, v), axis=1)
|
572
|
-
|
573
|
-
def get_new_y0(self) -> dict[str, float] | None:
|
574
|
-
"""Get the new initial conditions after the simulation.
|
575
|
-
|
576
|
-
Examples:
|
577
|
-
>>> Simulator(model).get_new_y0()
|
578
|
-
{"ATP": 1.0, "NADPH": 1.0}
|
579
|
-
|
580
|
-
"""
|
581
|
-
if (res := self.get_concs()) is None:
|
582
|
-
return None
|
583
|
-
return dict(res.iloc[-1])
|
584
|
-
|
585
561
|
def update_parameter(self, parameter: str, value: float) -> Self:
|
586
562
|
"""Updates the value of a specified parameter in the model.
|
587
563
|
|
@@ -637,3 +613,38 @@ class Simulator:
|
|
637
613
|
"""
|
638
614
|
self.model.scale_parameters(parameters)
|
639
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
|