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/mca.py
CHANGED
@@ -83,15 +83,15 @@ def variable_elasticities(
|
|
83
83
|
old = concs[var]
|
84
84
|
|
85
85
|
upper = model.get_fluxes(
|
86
|
-
|
86
|
+
variables=concs | {var: old * (1 + displacement)}, time=time
|
87
87
|
)
|
88
88
|
lower = model.get_fluxes(
|
89
|
-
|
89
|
+
variables=concs | {var: old * (1 - displacement)}, time=time
|
90
90
|
)
|
91
91
|
|
92
92
|
elasticity_coef = (upper - lower) / (2 * displacement * old)
|
93
93
|
if normalized:
|
94
|
-
elasticity_coef *= old / model.get_fluxes(
|
94
|
+
elasticity_coef *= old / model.get_fluxes(variables=concs, time=time)
|
95
95
|
elasticities[var] = elasticity_coef
|
96
96
|
|
97
97
|
return pd.DataFrame(data=elasticities)
|
@@ -137,16 +137,16 @@ def parameter_elasticities(
|
|
137
137
|
old = model.parameters[par]
|
138
138
|
|
139
139
|
model.update_parameters({par: old * (1 + displacement)})
|
140
|
-
upper = model.get_fluxes(
|
140
|
+
upper = model.get_fluxes(variables=concs, time=time)
|
141
141
|
|
142
142
|
model.update_parameters({par: old * (1 - displacement)})
|
143
|
-
lower = model.get_fluxes(
|
143
|
+
lower = model.get_fluxes(variables=concs, time=time)
|
144
144
|
|
145
145
|
# Reset
|
146
146
|
model.update_parameters({par: old})
|
147
147
|
elasticity_coef = (upper - lower) / (2 * displacement * old)
|
148
148
|
if normalized:
|
149
|
-
elasticity_coef *= old / model.get_fluxes(
|
149
|
+
elasticity_coef *= old / model.get_fluxes(variables=concs, time=time)
|
150
150
|
elasticities[par] = elasticity_coef
|
151
151
|
|
152
152
|
return pd.DataFrame(data=elasticities)
|
modelbase2/model.py
CHANGED
@@ -20,7 +20,6 @@ from modelbase2 import fns
|
|
20
20
|
from modelbase2.types import (
|
21
21
|
Array,
|
22
22
|
Derived,
|
23
|
-
Float,
|
24
23
|
Reaction,
|
25
24
|
Readout,
|
26
25
|
)
|
@@ -238,6 +237,7 @@ class ModelCache:
|
|
238
237
|
"""
|
239
238
|
|
240
239
|
var_names: list[str]
|
240
|
+
order: list[str]
|
241
241
|
all_parameter_values: dict[str, float]
|
242
242
|
derived_parameter_names: list[str]
|
243
243
|
derived_variable_names: list[str]
|
@@ -293,22 +293,26 @@ class Model:
|
|
293
293
|
# Sanity checks
|
294
294
|
for name, el in it.chain(
|
295
295
|
self._derived.items(),
|
296
|
-
self._readouts.items(),
|
297
296
|
self._reactions.items(),
|
297
|
+
self._readouts.items(),
|
298
298
|
):
|
299
299
|
if not _check_function_arity(el.fn, len(el.args)):
|
300
300
|
raise ArityMismatchError(name, el.fn, el.args)
|
301
301
|
|
302
|
-
# Sort derived
|
303
|
-
|
302
|
+
# Sort derived & reactions
|
303
|
+
to_sort = self._derived | self._reactions | self._surrogates
|
304
|
+
order = _sort_dependencies(
|
304
305
|
available=set(self._parameters) | set(self._variables) | {"time"},
|
305
|
-
elements=[(k, set(v.args)) for k, v in
|
306
|
+
elements=[(k, set(v.args)) for k, v in to_sort.items()],
|
306
307
|
)
|
307
308
|
|
308
309
|
# Split derived into parameters and variables
|
310
|
+
# for user convenience
|
309
311
|
derived_variable_names: list[str] = []
|
310
312
|
derived_parameter_names: list[str] = []
|
311
|
-
for name in
|
313
|
+
for name in order:
|
314
|
+
if name in self._reactions or name in self._surrogates:
|
315
|
+
continue
|
312
316
|
derived = self._derived[name]
|
313
317
|
if all(i in all_parameter_names for i in derived.args):
|
314
318
|
all_parameter_names.add(name)
|
@@ -348,6 +352,7 @@ class Model:
|
|
348
352
|
|
349
353
|
self._cache = ModelCache(
|
350
354
|
var_names=var_names,
|
355
|
+
order=order,
|
351
356
|
all_parameter_values=all_parameter_values,
|
352
357
|
stoich_by_cpds=stoich_by_compounds,
|
353
358
|
dyn_stoich_by_cpds=dyn_stoich_by_compounds,
|
@@ -886,7 +891,7 @@ class Model:
|
|
886
891
|
|
887
892
|
"""
|
888
893
|
self._insert_id(name=name, ctx="derived")
|
889
|
-
self._derived[name] = Derived(fn, args)
|
894
|
+
self._derived[name] = Derived(name=name, fn=fn, args=args)
|
890
895
|
return self
|
891
896
|
|
892
897
|
def get_derived_parameter_names(self) -> list[str]:
|
@@ -979,7 +984,7 @@ class Model:
|
|
979
984
|
return copy.deepcopy(self._reactions)
|
980
985
|
|
981
986
|
def get_stoichiometries(
|
982
|
-
self,
|
987
|
+
self, variables: dict[str, float] | None = None, time: float = 0.0
|
983
988
|
) -> pd.DataFrame:
|
984
989
|
"""Retrieve the stoichiometries of the model.
|
985
990
|
|
@@ -995,7 +1000,7 @@ class Model:
|
|
995
1000
|
"""
|
996
1001
|
if (cache := self._cache) is None:
|
997
1002
|
cache = self._create_cache()
|
998
|
-
args = self.
|
1003
|
+
args = self.get_dependent(variables=variables, time=time)
|
999
1004
|
|
1000
1005
|
stoich_by_cpds = copy.deepcopy(cache.stoich_by_cpds)
|
1001
1006
|
for cpd, stoich in cache.dyn_stoich_by_cpds.items():
|
@@ -1036,10 +1041,12 @@ class Model:
|
|
1036
1041
|
self._insert_id(name=name, ctx="reaction")
|
1037
1042
|
|
1038
1043
|
stoich: dict[str, Derived | float] = {
|
1039
|
-
k: Derived(fns.constant, [v]) if isinstance(v, str) else v
|
1044
|
+
k: Derived(name=k, fn=fns.constant, args=[v]) if isinstance(v, str) else v
|
1040
1045
|
for k, v in stoichiometry.items()
|
1041
1046
|
}
|
1042
|
-
self._reactions[name] = Reaction(
|
1047
|
+
self._reactions[name] = Reaction(
|
1048
|
+
name=name, fn=fn, stoichiometry=stoich, args=args
|
1049
|
+
)
|
1043
1050
|
return self
|
1044
1051
|
|
1045
1052
|
def get_reaction_names(self) -> list[str]:
|
@@ -1088,7 +1095,9 @@ class Model:
|
|
1088
1095
|
|
1089
1096
|
if stoichiometry is not None:
|
1090
1097
|
stoich = {
|
1091
|
-
k: Derived(fns.constant, [v])
|
1098
|
+
k: Derived(name=k, fn=fns.constant, args=[v])
|
1099
|
+
if isinstance(v, str)
|
1100
|
+
else v
|
1092
1101
|
for k, v in stoichiometry.items()
|
1093
1102
|
}
|
1094
1103
|
rxn.stoichiometry = stoich
|
@@ -1162,7 +1171,7 @@ class Model:
|
|
1162
1171
|
|
1163
1172
|
"""
|
1164
1173
|
self._insert_id(name=name, ctx="readout")
|
1165
|
-
self._readouts[name] = Readout(fn, args)
|
1174
|
+
self._readouts[name] = Readout(name=name, fn=fn, args=args)
|
1166
1175
|
return self
|
1167
1176
|
|
1168
1177
|
def get_readout_names(self) -> list[str]:
|
@@ -1282,27 +1291,39 @@ class Model:
|
|
1282
1291
|
self._surrogates.pop(name)
|
1283
1292
|
return self
|
1284
1293
|
|
1294
|
+
def get_surrogate_reaction_names(self) -> list[str]:
|
1295
|
+
"""Return reaction names by surrogates."""
|
1296
|
+
names = []
|
1297
|
+
for i in self._surrogates.values():
|
1298
|
+
names.extend(i.stoichiometries)
|
1299
|
+
return names
|
1300
|
+
|
1285
1301
|
##########################################################################
|
1286
|
-
# Get
|
1302
|
+
# Get dependent values. This includes
|
1303
|
+
# - derived parameters
|
1304
|
+
# - derived variables
|
1305
|
+
# - fluxes
|
1306
|
+
# - readouts
|
1287
1307
|
##########################################################################
|
1288
1308
|
|
1289
|
-
def
|
1309
|
+
def _get_dependent(
|
1290
1310
|
self,
|
1291
|
-
|
1311
|
+
variables: dict[str, float],
|
1292
1312
|
time: float = 0.0,
|
1293
1313
|
*,
|
1294
|
-
|
1314
|
+
cache: ModelCache,
|
1295
1315
|
) -> dict[str, float]:
|
1296
|
-
"""Generate a dictionary of
|
1316
|
+
"""Generate a dictionary of model components dependent on other components.
|
1297
1317
|
|
1298
1318
|
Examples:
|
1299
|
-
>>> model.
|
1319
|
+
>>> model._get_dependent({"x1": 1.0, "x2": 2.0}, time=0.0)
|
1300
1320
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1301
1321
|
|
1302
1322
|
Args:
|
1303
|
-
|
1323
|
+
variables: A dictionary of concentrations with keys as the names of the substances
|
1304
1324
|
and values as their respective concentrations.
|
1305
1325
|
time: The time point for the calculation
|
1326
|
+
cache: A ModelCache object containing precomputed values and dependencies.
|
1306
1327
|
include_readouts: A flag indicating whether to include readout values in the returned dictionary.
|
1307
1328
|
|
1308
1329
|
Returns:
|
@@ -1311,25 +1332,18 @@ class Model:
|
|
1311
1332
|
with their respective names as keys and their calculated values as values.
|
1312
1333
|
|
1313
1334
|
"""
|
1314
|
-
|
1315
|
-
cache = self._create_cache()
|
1316
|
-
|
1317
|
-
args: dict[str, float] = cache.all_parameter_values | concs
|
1335
|
+
args: dict[str, float] = cache.all_parameter_values | variables
|
1318
1336
|
args["time"] = time
|
1319
1337
|
|
1320
|
-
|
1321
|
-
for name in cache.
|
1322
|
-
|
1323
|
-
args[name] = cast(float, dv.fn(*(args[arg] for arg in dv.args)))
|
1338
|
+
containers = self._derived | self._reactions | self._surrogates
|
1339
|
+
for name in cache.order:
|
1340
|
+
containers[name].calculate_inpl(args)
|
1324
1341
|
|
1325
|
-
if include_readouts:
|
1326
|
-
for name, ro in self._readouts.items():
|
1327
|
-
args[name] = cast(float, ro.fn(*(args[arg] for arg in ro.args)))
|
1328
1342
|
return args
|
1329
1343
|
|
1330
|
-
def
|
1344
|
+
def get_dependent(
|
1331
1345
|
self,
|
1332
|
-
|
1346
|
+
variables: dict[str, float] | None = None,
|
1333
1347
|
time: float = 0.0,
|
1334
1348
|
*,
|
1335
1349
|
include_readouts: bool = False,
|
@@ -1339,18 +1353,18 @@ class Model:
|
|
1339
1353
|
Examples:
|
1340
1354
|
# Using initial conditions
|
1341
1355
|
>>> model.get_args()
|
1342
|
-
{"x1": 1.
|
1356
|
+
{"x1": 1.get_dependent, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1343
1357
|
|
1344
1358
|
# With custom concentrations
|
1345
|
-
>>> model.
|
1359
|
+
>>> model.get_dependent({"x1": 1.0, "x2": 2.0})
|
1346
1360
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1347
1361
|
|
1348
1362
|
# With custom concentrations and time
|
1349
|
-
>>> model.
|
1363
|
+
>>> model.get_dependent({"x1": 1.0, "x2": 2.0}, time=1.0)
|
1350
1364
|
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 1.0}
|
1351
1365
|
|
1352
1366
|
Args:
|
1353
|
-
|
1367
|
+
variables: A dictionary where keys are the names of the concentrations and values are their respective float values.
|
1354
1368
|
time: The time point at which the arguments are generated (default is 0.0).
|
1355
1369
|
include_readouts: Whether to include readouts in the arguments (default is False).
|
1356
1370
|
|
@@ -1358,25 +1372,31 @@ class Model:
|
|
1358
1372
|
A pandas Series containing the generated arguments with float dtype.
|
1359
1373
|
|
1360
1374
|
"""
|
1361
|
-
|
1362
|
-
self.
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1375
|
+
if (cache := self._cache) is None:
|
1376
|
+
cache = self._create_cache()
|
1377
|
+
|
1378
|
+
args = self._get_dependent(
|
1379
|
+
variables=self.get_initial_conditions() if variables is None else variables,
|
1380
|
+
time=time,
|
1381
|
+
cache=cache,
|
1368
1382
|
)
|
1369
1383
|
|
1370
|
-
|
1384
|
+
if include_readouts:
|
1385
|
+
for ro in self._readouts.values(): # FIXME: order?
|
1386
|
+
ro.calculate_inpl(args)
|
1387
|
+
|
1388
|
+
return pd.Series(args, dtype=float)
|
1389
|
+
|
1390
|
+
def get_dependent_time_course(
|
1371
1391
|
self,
|
1372
|
-
|
1392
|
+
variables: pd.DataFrame,
|
1373
1393
|
*,
|
1374
1394
|
include_readouts: bool = False,
|
1375
1395
|
) -> pd.DataFrame:
|
1376
1396
|
"""Generate a DataFrame containing time course arguments for model evaluation.
|
1377
1397
|
|
1378
1398
|
Examples:
|
1379
|
-
>>> model.
|
1399
|
+
>>> model.get_dependent_time_course(
|
1380
1400
|
... pd.DataFrame({"x1": [1.0, 2.0], "x2": [2.0, 3.0]}
|
1381
1401
|
... )
|
1382
1402
|
pd.DataFrame({
|
@@ -1387,7 +1407,7 @@ class Model:
|
|
1387
1407
|
)
|
1388
1408
|
|
1389
1409
|
Args:
|
1390
|
-
|
1410
|
+
variables: A DataFrame containing concentration data with time as the index.
|
1391
1411
|
include_readouts: If True, include readout variables in the resulting DataFrame.
|
1392
1412
|
|
1393
1413
|
Returns:
|
@@ -1400,20 +1420,19 @@ class Model:
|
|
1400
1420
|
|
1401
1421
|
pars_df = pd.DataFrame(
|
1402
1422
|
np.full(
|
1403
|
-
(len(
|
1423
|
+
(len(variables), len(cache.all_parameter_values)),
|
1404
1424
|
np.fromiter(cache.all_parameter_values.values(), dtype=float),
|
1405
1425
|
),
|
1406
|
-
index=
|
1426
|
+
index=variables.index,
|
1407
1427
|
columns=list(cache.all_parameter_values),
|
1408
1428
|
)
|
1409
1429
|
|
1410
|
-
args = pd.concat((
|
1430
|
+
args = pd.concat((variables, pars_df), axis=1)
|
1411
1431
|
args["time"] = args.index
|
1412
1432
|
|
1413
|
-
|
1414
|
-
for name in cache.
|
1415
|
-
|
1416
|
-
args[name] = dv.fn(*args.loc[:, dv.args].to_numpy().T)
|
1433
|
+
containers = self._derived | self._reactions | self._surrogates
|
1434
|
+
for name in cache.order:
|
1435
|
+
containers[name].calculate_inpl_time_course(args)
|
1417
1436
|
|
1418
1437
|
if include_readouts:
|
1419
1438
|
for name, ro in self._readouts.items():
|
@@ -1421,48 +1440,91 @@ class Model:
|
|
1421
1440
|
return args
|
1422
1441
|
|
1423
1442
|
##########################################################################
|
1424
|
-
# Get
|
1443
|
+
# Get args
|
1425
1444
|
##########################################################################
|
1426
1445
|
|
1427
|
-
def
|
1446
|
+
def get_args(
|
1428
1447
|
self,
|
1429
|
-
|
1448
|
+
variables: dict[str, float] | None = None,
|
1430
1449
|
time: float = 0.0,
|
1431
1450
|
*,
|
1432
|
-
|
1451
|
+
include_derived: bool = True,
|
1452
|
+
include_readouts: bool = False,
|
1433
1453
|
) -> pd.Series:
|
1434
|
-
"""
|
1454
|
+
"""Generate a pandas Series of arguments for the model.
|
1435
1455
|
|
1436
1456
|
Examples:
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1457
|
+
# Using initial conditions
|
1458
|
+
>>> model.get_args()
|
1459
|
+
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1460
|
+
|
1461
|
+
# With custom concentrations
|
1462
|
+
>>> model.get_args({"x1": 1.0, "x2": 2.0})
|
1463
|
+
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
|
1464
|
+
|
1465
|
+
# With custom concentrations and time
|
1466
|
+
>>> model.get_args({"x1": 1.0, "x2": 2.0}, time=1.0)
|
1467
|
+
{"x1": 1.0, "x2": 2.0, "k1": 0.1, "time": 1.0}
|
1447
1468
|
|
1448
1469
|
Args:
|
1449
|
-
|
1450
|
-
time
|
1451
|
-
|
1470
|
+
variables: A dictionary where keys are the names of the concentrations and values are their respective float values.
|
1471
|
+
time: The time point at which the arguments are generated.
|
1472
|
+
include_derived: Whether to include derived variables in the arguments.
|
1473
|
+
include_readouts: Whether to include readouts in the arguments.
|
1452
1474
|
|
1453
1475
|
Returns:
|
1454
|
-
|
1476
|
+
A pandas Series containing the generated arguments with float dtype.
|
1455
1477
|
|
1456
1478
|
"""
|
1457
|
-
names = self.get_variable_names()
|
1479
|
+
names = self.get_variable_names()
|
1480
|
+
if include_derived:
|
1481
|
+
names.extend(self.get_derived_variable_names())
|
1458
1482
|
if include_readouts:
|
1459
|
-
names.extend(self.
|
1483
|
+
names.extend(self._readouts)
|
1460
1484
|
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1485
|
+
args = self.get_dependent(
|
1486
|
+
variables=variables, time=time, include_readouts=include_readouts
|
1487
|
+
)
|
1488
|
+
return args.loc[names]
|
1489
|
+
|
1490
|
+
def get_args_time_course(
|
1491
|
+
self,
|
1492
|
+
variables: pd.DataFrame,
|
1493
|
+
*,
|
1494
|
+
include_derived: bool = True,
|
1495
|
+
include_readouts: bool = False,
|
1496
|
+
) -> pd.DataFrame:
|
1497
|
+
"""Generate a DataFrame containing time course arguments for model evaluation.
|
1498
|
+
|
1499
|
+
Examples:
|
1500
|
+
>>> model.get_args_time_course(
|
1501
|
+
... pd.DataFrame({"x1": [1.0, 2.0], "x2": [2.0, 3.0]}
|
1502
|
+
... )
|
1503
|
+
pd.DataFrame({
|
1504
|
+
"x1": [1.0, 2.0],
|
1505
|
+
"x2": [2.0, 3.0],
|
1506
|
+
"k1": [0.1, 0.1],
|
1507
|
+
"time": [0.0, 1.0]},
|
1508
|
+
)
|
1509
|
+
|
1510
|
+
Args:
|
1511
|
+
variables: A DataFrame containing concentration data with time as the index.
|
1512
|
+
include_derived: Whether to include derived variables in the arguments.
|
1513
|
+
include_readouts: If True, include readout variables in the resulting DataFrame.
|
1514
|
+
|
1515
|
+
Returns:
|
1516
|
+
A DataFrame containing the combined concentration data, parameter values,
|
1517
|
+
derived variables, and optionally readout variables, with time as an additional column.
|
1518
|
+
|
1519
|
+
"""
|
1520
|
+
names = self.get_variable_names()
|
1521
|
+
if include_derived:
|
1522
|
+
names.extend(self.get_derived_variable_names())
|
1523
|
+
|
1524
|
+
args = self.get_dependent_time_course(
|
1525
|
+
variables=variables, include_readouts=include_readouts
|
1526
|
+
)
|
1527
|
+
return args.loc[:, names]
|
1466
1528
|
|
1467
1529
|
##########################################################################
|
1468
1530
|
# Get fluxes
|
@@ -1492,7 +1554,7 @@ class Model:
|
|
1492
1554
|
|
1493
1555
|
def get_fluxes(
|
1494
1556
|
self,
|
1495
|
-
|
1557
|
+
variables: dict[str, float] | None = None,
|
1496
1558
|
time: float = 0.0,
|
1497
1559
|
) -> pd.Series:
|
1498
1560
|
"""Calculate the fluxes for the given concentrations and time.
|
@@ -1511,28 +1573,25 @@ class Model:
|
|
1511
1573
|
pd.Series({"r1": 0.1, "r2": 0.2})
|
1512
1574
|
|
1513
1575
|
Args:
|
1514
|
-
|
1576
|
+
variables: A dictionary where keys are species names and values are their concentrations.
|
1515
1577
|
time: The time at which to calculate the fluxes. Defaults to 0.0.
|
1516
1578
|
|
1517
1579
|
Returns:
|
1518
1580
|
Fluxes: A pandas Series containing the fluxes for each reaction.
|
1519
1581
|
|
1520
1582
|
"""
|
1521
|
-
|
1522
|
-
|
1583
|
+
names = self.get_reaction_names()
|
1584
|
+
for surrogate in self._surrogates.values():
|
1585
|
+
names.extend(surrogate.stoichiometries)
|
1586
|
+
|
1587
|
+
args = self.get_dependent(
|
1588
|
+
variables=variables,
|
1523
1589
|
time=time,
|
1524
1590
|
include_readouts=False,
|
1525
1591
|
)
|
1592
|
+
return args.loc[names]
|
1526
1593
|
|
1527
|
-
|
1528
|
-
for name, rxn in self._reactions.items():
|
1529
|
-
fluxes[name] = cast(float, rxn.fn(*args.loc[rxn.args]))
|
1530
|
-
|
1531
|
-
for surrogate in self._surrogates.values():
|
1532
|
-
fluxes |= surrogate.predict(args.loc[surrogate.args].to_numpy())
|
1533
|
-
return pd.Series(fluxes, dtype=float)
|
1534
|
-
|
1535
|
-
def get_fluxes_time_course(self, args: pd.DataFrame) -> pd.DataFrame:
|
1594
|
+
def get_fluxes_time_course(self, variables: pd.DataFrame) -> pd.DataFrame:
|
1536
1595
|
"""Generate a time course of fluxes for the given reactions and surrogates.
|
1537
1596
|
|
1538
1597
|
Examples:
|
@@ -1544,9 +1603,9 @@ class Model:
|
|
1544
1603
|
time course of fluxes.
|
1545
1604
|
|
1546
1605
|
Args:
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1606
|
+
variables: A DataFrame containing the input arguments for the reactions
|
1607
|
+
and surrogates. Each column corresponds to a specific input
|
1608
|
+
variable, and each row represents a different time point.
|
1550
1609
|
|
1551
1610
|
Returns:
|
1552
1611
|
pd.DataFrame: A DataFrame containing the calculated fluxes for each reaction and
|
@@ -1554,26 +1613,21 @@ class Model:
|
|
1554
1613
|
the index of the input arguments.
|
1555
1614
|
|
1556
1615
|
"""
|
1557
|
-
|
1558
|
-
for name, rate in self._reactions.items():
|
1559
|
-
fluxes[name] = rate.fn(*args.loc[:, rate.args].to_numpy().T)
|
1560
|
-
|
1561
|
-
# Create df here already to avoid having to play around with
|
1562
|
-
# shape of surrogate outputs
|
1563
|
-
flux_df = pd.DataFrame(fluxes, index=args.index)
|
1616
|
+
names = self.get_reaction_names()
|
1564
1617
|
for surrogate in self._surrogates.values():
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1618
|
+
names.extend(surrogate.stoichiometries)
|
1619
|
+
|
1620
|
+
variables = self.get_dependent_time_course(
|
1621
|
+
variables=variables,
|
1622
|
+
include_readouts=False,
|
1623
|
+
)
|
1624
|
+
return variables.loc[:, names]
|
1571
1625
|
|
1572
1626
|
##########################################################################
|
1573
1627
|
# Get rhs
|
1574
1628
|
##########################################################################
|
1575
1629
|
|
1576
|
-
def __call__(self, /, time: float,
|
1630
|
+
def __call__(self, /, time: float, variables: Array) -> Array:
|
1577
1631
|
"""Simulation version of get_right_hand_side.
|
1578
1632
|
|
1579
1633
|
Examples:
|
@@ -1585,7 +1639,7 @@ class Model:
|
|
1585
1639
|
|
1586
1640
|
Args:
|
1587
1641
|
time: The current time point.
|
1588
|
-
|
1642
|
+
variables: Array of concentrations
|
1589
1643
|
|
1590
1644
|
|
1591
1645
|
Returns:
|
@@ -1594,34 +1648,33 @@ class Model:
|
|
1594
1648
|
"""
|
1595
1649
|
if (cache := self._cache) is None:
|
1596
1650
|
cache = self._create_cache()
|
1597
|
-
|
1651
|
+
vars_d: dict[str, float] = dict(
|
1598
1652
|
zip(
|
1599
1653
|
cache.var_names,
|
1600
|
-
|
1654
|
+
variables,
|
1601
1655
|
strict=True,
|
1602
1656
|
)
|
1603
1657
|
)
|
1604
|
-
|
1605
|
-
|
1658
|
+
dependent: dict[str, float] = self._get_dependent(
|
1659
|
+
variables=vars_d,
|
1606
1660
|
time=time,
|
1607
|
-
|
1661
|
+
cache=cache,
|
1608
1662
|
)
|
1609
|
-
fluxes: dict[str, float] = self._get_fluxes(args)
|
1610
1663
|
|
1611
1664
|
dxdt = cache.dxdt
|
1612
1665
|
dxdt[:] = 0
|
1613
1666
|
for k, stoc in cache.stoich_by_cpds.items():
|
1614
1667
|
for flux, n in stoc.items():
|
1615
|
-
dxdt[k] += n *
|
1668
|
+
dxdt[k] += n * dependent[flux]
|
1616
1669
|
for k, sd in cache.dyn_stoich_by_cpds.items():
|
1617
1670
|
for flux, dv in sd.items():
|
1618
|
-
n = dv.
|
1619
|
-
dxdt[k] += n *
|
1671
|
+
n = dv.calculate(dependent)
|
1672
|
+
dxdt[k] += n * dependent[flux]
|
1620
1673
|
return cast(Array, dxdt.to_numpy())
|
1621
1674
|
|
1622
1675
|
def get_right_hand_side(
|
1623
1676
|
self,
|
1624
|
-
|
1677
|
+
variables: dict[str, float] | None = None,
|
1625
1678
|
time: float = 0.0,
|
1626
1679
|
) -> pd.Series:
|
1627
1680
|
"""Calculate the right-hand side of the differential equations for the model.
|
@@ -1640,7 +1693,7 @@ class Model:
|
|
1640
1693
|
pd.Series({"x1": 0.1, "x2": 0.2})
|
1641
1694
|
|
1642
1695
|
Args:
|
1643
|
-
|
1696
|
+
variables: A dictionary mapping compound names to their concentrations.
|
1644
1697
|
time: The current time point. Defaults to 0.0.
|
1645
1698
|
|
1646
1699
|
Returns:
|
@@ -1650,19 +1703,18 @@ class Model:
|
|
1650
1703
|
if (cache := self._cache) is None:
|
1651
1704
|
cache = self._create_cache()
|
1652
1705
|
var_names = self.get_variable_names()
|
1653
|
-
|
1654
|
-
|
1706
|
+
dependent = self._get_dependent(
|
1707
|
+
variables=self.get_initial_conditions() if variables is None else variables,
|
1655
1708
|
time=time,
|
1656
|
-
|
1709
|
+
cache=cache,
|
1657
1710
|
)
|
1658
|
-
fluxes = self._get_fluxes(args)
|
1659
1711
|
dxdt = pd.Series(np.zeros(len(var_names), dtype=float), index=var_names)
|
1660
1712
|
for k, stoc in cache.stoich_by_cpds.items():
|
1661
1713
|
for flux, n in stoc.items():
|
1662
|
-
dxdt[k] += n *
|
1714
|
+
dxdt[k] += n * dependent[flux]
|
1663
1715
|
|
1664
1716
|
for k, sd in cache.dyn_stoich_by_cpds.items():
|
1665
1717
|
for flux, dv in sd.items():
|
1666
|
-
n = dv.fn(*(
|
1667
|
-
dxdt[k] += n *
|
1718
|
+
n = dv.fn(*(dependent[i] for i in dv.args))
|
1719
|
+
dxdt[k] += n * dependent[flux]
|
1668
1720
|
return dxdt
|
modelbase2/nnarchitectures.py
CHANGED
@@ -33,17 +33,18 @@ class MLP(nn.Module):
|
|
33
33
|
def __init__(
|
34
34
|
self,
|
35
35
|
n_inputs: int,
|
36
|
-
|
37
|
-
activation: Callable | None =
|
36
|
+
neurons_per_layer: list[int],
|
37
|
+
activation: Callable | None = None,
|
38
38
|
output_activation: Callable | None = None,
|
39
39
|
) -> None:
|
40
40
|
"""Initializes the MLP with the given number of inputs and list of (hidden) layers.
|
41
41
|
|
42
42
|
Args:
|
43
|
-
n_inputs
|
44
|
-
|
45
|
-
|
46
|
-
activation
|
43
|
+
n_inputs: The number of input features.
|
44
|
+
neurons_per_layer: Number of neurons per layer
|
45
|
+
n_outputs: A list containing the number of neurons in hidden and output layer.
|
46
|
+
activation: The activation function to be applied after each hidden layer (default nn.ReLU)
|
47
|
+
output_activation: The activation function to be applied after the final (output) layer
|
47
48
|
|
48
49
|
For instance, MLP(10, layers = [50, 50, 10]) initializes a neural network with the following architecture:
|
49
50
|
- Linear layer with `n_inputs` inputs and 50 outputs
|
@@ -57,8 +58,8 @@ class MLP(nn.Module):
|
|
57
58
|
|
58
59
|
"""
|
59
60
|
super().__init__()
|
60
|
-
self.layers =
|
61
|
-
self.activation = activation
|
61
|
+
self.layers = neurons_per_layer
|
62
|
+
self.activation = nn.ReLU() if activation is None else activation
|
62
63
|
self.output_activation = output_activation
|
63
64
|
|
64
65
|
levels = []
|