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/mca.py CHANGED
@@ -83,15 +83,15 @@ def variable_elasticities(
83
83
  old = concs[var]
84
84
 
85
85
  upper = model.get_fluxes(
86
- concs=concs | {var: old * (1 + displacement)}, time=time
86
+ variables=concs | {var: old * (1 + displacement)}, time=time
87
87
  )
88
88
  lower = model.get_fluxes(
89
- concs=concs | {var: old * (1 - displacement)}, time=time
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(concs=concs, time=time)
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(concs=concs, time=time)
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(concs=concs, time=time)
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(concs=concs, time=time)
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
- derived_order = _sort_dependencies(
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 self._derived.items()],
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 derived_order:
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, concs: dict[str, float] | None = None, time: float = 0.0
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.get_args(concs=concs, time=time)
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(fn=fn, stoichiometry=stoich, args=args)
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]) if isinstance(v, str) else 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 args
1302
+ # Get dependent values. This includes
1303
+ # - derived parameters
1304
+ # - derived variables
1305
+ # - fluxes
1306
+ # - readouts
1287
1307
  ##########################################################################
1288
1308
 
1289
- def _get_args(
1309
+ def _get_dependent(
1290
1310
  self,
1291
- concs: dict[str, float],
1311
+ variables: dict[str, float],
1292
1312
  time: float = 0.0,
1293
1313
  *,
1294
- include_readouts: bool,
1314
+ cache: ModelCache,
1295
1315
  ) -> dict[str, float]:
1296
- """Generate a dictionary of arguments for model calculations.
1316
+ """Generate a dictionary of model components dependent on other components.
1297
1317
 
1298
1318
  Examples:
1299
- >>> model._get_args({"x1": 1.0, "x2": 2.0}, time=0.0)
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
- concs: A dictionary of concentrations with keys as the names of the substances
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
- if (cache := self._cache) is None:
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
- derived = self._derived
1321
- for name in cache.derived_variable_names:
1322
- dv = derived[name]
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 get_args(
1344
+ def get_dependent(
1331
1345
  self,
1332
- concs: dict[str, float] | None = None,
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.0, "x2": 2.0, "k1": 0.1, "time": 0.0}
1356
+ {"x1": 1.get_dependent, "x2": 2.0, "k1": 0.1, "time": 0.0}
1343
1357
 
1344
1358
  # With custom concentrations
1345
- >>> model.get_args({"x1": 1.0, "x2": 2.0})
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.get_args({"x1": 1.0, "x2": 2.0}, time=1.0)
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
- concs: A dictionary where keys are the names of the concentrations and values are their respective float values.
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
- return pd.Series(
1362
- self._get_args(
1363
- concs=self.get_initial_conditions() if concs is None else concs,
1364
- time=time,
1365
- include_readouts=include_readouts,
1366
- ),
1367
- dtype=float,
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
- def get_args_time_course(
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
- concs: pd.DataFrame,
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.get_args_time_course(
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
- concs: A DataFrame containing concentration data with time as the index.
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(concs), len(cache.all_parameter_values)),
1423
+ (len(variables), len(cache.all_parameter_values)),
1404
1424
  np.fromiter(cache.all_parameter_values.values(), dtype=float),
1405
1425
  ),
1406
- index=concs.index,
1426
+ index=variables.index,
1407
1427
  columns=list(cache.all_parameter_values),
1408
1428
  )
1409
1429
 
1410
- args = pd.concat((concs, pars_df), axis=1)
1430
+ args = pd.concat((variables, pars_df), axis=1)
1411
1431
  args["time"] = args.index
1412
1432
 
1413
- derived = self._derived
1414
- for name in cache.derived_variable_names:
1415
- dv = derived[name]
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 full concs
1443
+ # Get args
1425
1444
  ##########################################################################
1426
1445
 
1427
- def get_full_concs(
1446
+ def get_args(
1428
1447
  self,
1429
- concs: dict[str, float] | None = None,
1448
+ variables: dict[str, float] | None = None,
1430
1449
  time: float = 0.0,
1431
1450
  *,
1432
- include_readouts: bool = True,
1451
+ include_derived: bool = True,
1452
+ include_readouts: bool = False,
1433
1453
  ) -> pd.Series:
1434
- """Get the full concentrations as a pandas Series.
1454
+ """Generate a pandas Series of arguments for the model.
1435
1455
 
1436
1456
  Examples:
1437
- >>> model.get_full_concs({"x1": 1.0, "x2": 2.0}, time=0.0)
1438
- pd.Series({
1439
- "x1": 1.0,
1440
- "x2": 2.0,
1441
- "d1": 3.0,
1442
- "d2": 4.0,
1443
- "r1": 0.1,
1444
- "r2": 0.2,
1445
- "energy_state": 0.5,
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
- concs (dict[str, float]): A dictionary of concentrations with variable names as keys and their corresponding values as floats.
1450
- time (float, optional): The time point at which to get the concentrations. Default is 0.0.
1451
- include_readouts (bool, optional): Whether to include readout variables in the result. Default is True.
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
- pd.Series: A pandas Series containing the full concentrations for the specified variables.
1476
+ A pandas Series containing the generated arguments with float dtype.
1455
1477
 
1456
1478
  """
1457
- names = self.get_variable_names() + self.get_derived_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.get_readout_names())
1483
+ names.extend(self._readouts)
1460
1484
 
1461
- return self.get_args(
1462
- concs=concs,
1463
- time=time,
1464
- include_readouts=include_readouts,
1465
- ).loc[names]
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
- concs: dict[str, float] | None = None,
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
- concs: A dictionary where keys are species names and values are their concentrations.
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
- args = self.get_args(
1522
- concs=concs,
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
- fluxes: dict[str, float] = {}
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
- args (pd.DataFrame): A DataFrame containing the input arguments for the reactions
1548
- and surrogates. Each column corresponds to a specific input
1549
- variable, and each row represents a different time point.
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
- fluxes: dict[str, Float] = {}
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
- outputs = pd.DataFrame(
1566
- [surrogate.predict(y) for y in args.loc[:, surrogate.args].to_numpy()],
1567
- index=args.index,
1568
- )
1569
- flux_df = pd.concat((flux_df, outputs), axis=1)
1570
- return flux_df
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, concs: Array) -> Array:
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
- concs: Array of concentrations
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
- concsd: dict[str, float] = dict(
1651
+ vars_d: dict[str, float] = dict(
1598
1652
  zip(
1599
1653
  cache.var_names,
1600
- concs,
1654
+ variables,
1601
1655
  strict=True,
1602
1656
  )
1603
1657
  )
1604
- args: dict[str, float] = self._get_args(
1605
- concs=concsd,
1658
+ dependent: dict[str, float] = self._get_dependent(
1659
+ variables=vars_d,
1606
1660
  time=time,
1607
- include_readouts=False,
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 * fluxes[flux]
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.fn(*(args[i] for i in dv.args))
1619
- dxdt[k] += n * fluxes[flux]
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
- concs: dict[str, float] | None = None,
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
- concs: A dictionary mapping compound names to their concentrations.
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
- args = self._get_args(
1654
- concs=self.get_initial_conditions() if concs is None else concs,
1706
+ dependent = self._get_dependent(
1707
+ variables=self.get_initial_conditions() if variables is None else variables,
1655
1708
  time=time,
1656
- include_readouts=False,
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 * fluxes[flux]
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(*(args[i] for i in dv.args))
1667
- dxdt[k] += n * fluxes[flux]
1718
+ n = dv.fn(*(dependent[i] for i in dv.args))
1719
+ dxdt[k] += n * dependent[flux]
1668
1720
  return dxdt
@@ -33,17 +33,18 @@ class MLP(nn.Module):
33
33
  def __init__(
34
34
  self,
35
35
  n_inputs: int,
36
- layers: list[int],
37
- activation: Callable | None = nn.ReLU(),
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 (int): The number of input features.
44
- n_outputs list(int): A list containing the number of neurons in hidden and output layer.
45
- activation Callable | None (default nn.ReLU()): The activation function to be applied after each hidden layer
46
- activation Callable | None (default None): The activation function to be applied after the final (output) layer
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 = 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 = []