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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mxlpy/types.py CHANGED
@@ -36,6 +36,7 @@ __all__ = [
36
36
  "McSteadyStates",
37
37
  "MockSurrogate",
38
38
  "Param",
39
+ "Parameter",
39
40
  "ProtocolByPars",
40
41
  "RateFn",
41
42
  "Reaction",
@@ -45,13 +46,14 @@ __all__ = [
45
46
  "RetType",
46
47
  "SteadyStates",
47
48
  "TimeCourseByPars",
49
+ "Variable",
48
50
  "unwrap",
49
51
  "unwrap2",
50
52
  ]
51
53
 
52
54
  type RateFn = Callable[..., float]
53
55
  type Array = NDArray[np.floating[Any]]
54
- type ArrayLike = NDArray[np.floating[Any]] | list[float]
56
+ type ArrayLike = NDArray[np.floating[Any]] | pd.Index | list[float]
55
57
 
56
58
 
57
59
  Param = ParamSpec("Param")
@@ -59,6 +61,8 @@ RetType = TypeVar("RetType")
59
61
 
60
62
 
61
63
  if TYPE_CHECKING:
64
+ import sympy
65
+
62
66
  from mxlpy.model import Model
63
67
 
64
68
 
@@ -147,34 +151,53 @@ type IntegratorType = Callable[
147
151
  ]
148
152
 
149
153
 
154
+ @dataclass
155
+ class Parameter:
156
+ """Container for parameter meta information."""
157
+
158
+ value: float
159
+ unit: sympy.Expr | None = None
160
+ source: str | None = None
161
+
162
+
163
+ @dataclass
164
+ class Variable:
165
+ """Container for variable meta information."""
166
+
167
+ initial_value: float | Derived
168
+ unit: sympy.Expr | None = None
169
+ source: str | None = None
170
+
171
+
150
172
  @dataclass(kw_only=True, slots=True)
151
173
  class Derived:
152
174
  """Container for a derived value."""
153
175
 
154
176
  fn: RateFn
155
177
  args: list[str]
178
+ unit: sympy.Expr | None = None
156
179
 
157
- def calculate(self, dependent: dict[str, Any]) -> float:
180
+ def calculate(self, args: dict[str, Any]) -> float:
158
181
  """Calculate the derived value.
159
182
 
160
183
  Args:
161
- dependent: Dictionary of dependent variables.
184
+ args: Dictionary of args variables.
162
185
 
163
186
  Returns:
164
187
  The calculated derived value.
165
188
 
166
189
  """
167
- return cast(float, self.fn(*(dependent[arg] for arg in self.args)))
190
+ return cast(float, self.fn(*(args[arg] for arg in self.args)))
168
191
 
169
- def calculate_inpl(self, name: str, dependent: dict[str, Any]) -> None:
192
+ def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
170
193
  """Calculate the derived value in place.
171
194
 
172
195
  Args:
173
196
  name: Name of the derived variable.
174
- dependent: Dictionary of dependent variables.
197
+ args: Dictionary of args variables.
175
198
 
176
199
  """
177
- dependent[name] = cast(float, self.fn(*(dependent[arg] for arg in self.args)))
200
+ args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
178
201
 
179
202
 
180
203
  @dataclass(kw_only=True, slots=True)
@@ -183,28 +206,29 @@ class Readout:
183
206
 
184
207
  fn: RateFn
185
208
  args: list[str]
209
+ unit: sympy.Expr | None = None
186
210
 
187
- def calculate(self, dependent: dict[str, Any]) -> float:
211
+ def calculate(self, args: dict[str, Any]) -> float:
188
212
  """Calculate the derived value.
189
213
 
190
214
  Args:
191
- dependent: Dictionary of dependent variables.
215
+ args: Dictionary of args variables.
192
216
 
193
217
  Returns:
194
218
  The calculated derived value.
195
219
 
196
220
  """
197
- return cast(float, self.fn(*(dependent[arg] for arg in self.args)))
221
+ return cast(float, self.fn(*(args[arg] for arg in self.args)))
198
222
 
199
- def calculate_inpl(self, name: str, dependent: dict[str, Any]) -> None:
223
+ def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
200
224
  """Calculate the reaction in place.
201
225
 
202
226
  Args:
203
227
  name: Name of the derived variable.
204
- dependent: Dictionary of dependent variables.
228
+ args: Dictionary of args variables.
205
229
 
206
230
  """
207
- dependent[name] = cast(float, self.fn(*(dependent[arg] for arg in self.args)))
231
+ args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
208
232
 
209
233
 
210
234
  @dataclass(kw_only=True, slots=True)
@@ -214,35 +238,36 @@ class Reaction:
214
238
  fn: RateFn
215
239
  stoichiometry: Mapping[str, float | Derived]
216
240
  args: list[str]
241
+ unit: sympy.Expr | None = None
217
242
 
218
243
  def get_modifiers(self, model: Model) -> list[str]:
219
244
  """Get the modifiers of the reaction."""
220
- include = set(model.variables)
245
+ include = set(model.get_variable_names())
221
246
  exclude = set(self.stoichiometry)
222
247
 
223
248
  return [k for k in self.args if k in include and k not in exclude]
224
249
 
225
- def calculate(self, dependent: dict[str, Any]) -> float:
250
+ def calculate(self, args: dict[str, Any]) -> float:
226
251
  """Calculate the derived value.
227
252
 
228
253
  Args:
229
- dependent: Dictionary of dependent variables.
254
+ args: Dictionary of args variables.
230
255
 
231
256
  Returns:
232
257
  The calculated derived value.
233
258
 
234
259
  """
235
- return cast(float, self.fn(*(dependent[arg] for arg in self.args)))
260
+ return cast(float, self.fn(*(args[arg] for arg in self.args)))
236
261
 
237
- def calculate_inpl(self, name: str, dependent: dict[str, Any]) -> None:
262
+ def calculate_inpl(self, name: str, args: dict[str, Any]) -> None:
238
263
  """Calculate the reaction in place.
239
264
 
240
265
  Args:
241
266
  name: Name of the derived variable.
242
- dependent: Dictionary of dependent variables.
267
+ args: Dictionary of args variables.
243
268
 
244
269
  """
245
- dependent[name] = cast(float, self.fn(*(dependent[arg] for arg in self.args)))
270
+ args[name] = cast(float, self.fn(*(args[arg] for arg in self.args)))
246
271
 
247
272
 
248
273
  @dataclass(kw_only=True)
mxlpy/units.py ADDED
@@ -0,0 +1,128 @@
1
+ """Unit definitions for MxlPy."""
2
+
3
+ from sympy.physics.units import (
4
+ ampere,
5
+ becquerel,
6
+ candela,
7
+ coulomb,
8
+ farad,
9
+ gram,
10
+ gray,
11
+ henry,
12
+ hertz,
13
+ hour,
14
+ joule,
15
+ katal,
16
+ kelvin,
17
+ kilogram,
18
+ liter,
19
+ lux,
20
+ meter,
21
+ micro,
22
+ milli,
23
+ minute,
24
+ mol,
25
+ nano,
26
+ newton,
27
+ ohm,
28
+ pascal,
29
+ pico,
30
+ radian,
31
+ second,
32
+ siemens,
33
+ steradian,
34
+ tesla,
35
+ volt,
36
+ watt,
37
+ weber,
38
+ )
39
+
40
+ __all__ = [
41
+ "ampere",
42
+ "becquerel",
43
+ "coulomb",
44
+ "dimensionless",
45
+ "farad",
46
+ "gram",
47
+ "gray",
48
+ "henry",
49
+ "hertz",
50
+ "hour",
51
+ "item",
52
+ "joule",
53
+ "katal",
54
+ "kelvin",
55
+ "liter",
56
+ "lumen",
57
+ "lux",
58
+ "micro",
59
+ "milli",
60
+ "minute",
61
+ "mmol",
62
+ "mmol_g",
63
+ "mmol_s",
64
+ "mol",
65
+ "nano",
66
+ "newton",
67
+ "nmol",
68
+ "ohm",
69
+ "pascal",
70
+ "ppfd",
71
+ "radian",
72
+ "second",
73
+ "siemens",
74
+ "sievert",
75
+ "sqm",
76
+ "tesla",
77
+ "volt",
78
+ "watt",
79
+ "weber",
80
+ ]
81
+
82
+ # time unit
83
+ per_second = 1 / second # type: ignore
84
+ per_minute = 1 / minute # type: ignore
85
+ per_hour = 1 / hour # type: ignore
86
+
87
+
88
+ sqm = meter**2
89
+ cbm = meter**3
90
+
91
+ mol_s = mol / second # type: ignore
92
+ mol_m = mol / minute # type: ignore
93
+ mol_h = mol / hour # type: ignore
94
+ mol_g = mol / gram # type: ignore
95
+
96
+ mmol = mol * milli
97
+ mmol_s = mmol / second
98
+ mmol_m = mmol / minute
99
+ mmol_h = mmol / hour
100
+ mmol_g = mmol / gram
101
+
102
+ mumol = mol * micro
103
+ mumol_s = mumol / second
104
+ mumol_m = mumol / minute
105
+ mumol_h = mumol / hour
106
+ mumol_g = mumol / gram
107
+
108
+ nmol = mol * nano
109
+ nmol_s = nmol / second
110
+ nmol_m = nmol / minute
111
+ nmol_h = nmol / hour
112
+ nmol_g = nmol / gram
113
+
114
+ pmol = mol * pico
115
+ pmol_s = pmol / second
116
+ pmol_m = pmol / minute
117
+ pmol_h = pmol / hour
118
+ pmol_g = pmol / gram
119
+
120
+ ppfd = mumol / sqm / second
121
+
122
+
123
+ # SBML units
124
+ avogadro = 6.02214076e23
125
+ sievert = joule / kilogram # type: ignore
126
+ lumen = candela * steradian # type: ignore
127
+ dimensionless = None
128
+ item = 1 # pseudounit for one thing
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mxlpy
3
- Version: 0.21.0
3
+ Version: 0.22.0
4
4
  Summary: A package to build metabolic models
5
5
  Author-email: Marvin van Aalst <marvin.vanaalst@gmail.com>
6
6
  Maintainer-email: Marvin van Aalst <marvin.vanaalst@gmail.com>
@@ -1,34 +1,36 @@
1
- mxlpy/__init__.py,sha256=4pbDeyLhQjfL76h2oXdodARzKkrkX5wESV7kEjwC3K8,4399
1
+ mxlpy/__init__.py,sha256=GlBZ-WmSoMgVIeAyVqtEJiRe_jLar84bjbg_FlAr2vw,4477
2
2
  mxlpy/carousel.py,sha256=o72YKzfPCDhT5oHhow4oNvIShG-i3-Z0UMEQLt2iE5A,4699
3
- mxlpy/compare.py,sha256=6iIl6yKXP9guSVLgqqnaqILP_BF_oqyx7DTGbdpwAjM,7800
3
+ mxlpy/compare.py,sha256=PJbb_R9GTGrkcEpGNpUfwZpZbUmZHNjsMQ_5qPAxVNo,7746
4
4
  mxlpy/distributions.py,sha256=ce6RTqn19YzMMec-u09fSIUA8A92M6rehCuHuXWcX7A,8734
5
- mxlpy/fit.py,sha256=vJGd_kl-MqyI7db96uM9STeBnvPVu1VGLCiGuH8mEKc,19009
5
+ mxlpy/fit.py,sha256=3hGUqJ2tOOToZLMMaJw5M9b6_UlUJwT_MhUvfPmRBd8,22355
6
6
  mxlpy/fns.py,sha256=NLxYwa3ylS7SkISBjw_TgQSKEm7WnkZF9wPigX_ZCAM,13915
7
- mxlpy/identify.py,sha256=yU6ccd0yDJhtyo5gkemMuecZALzjR1KzT0vKPmlL4kg,2107
8
- mxlpy/label_map.py,sha256=Zla9tey-7_POTE57XNEuCSeTqdAbMWZdj_j_OwokngY,17823
9
- mxlpy/linear_label_map.py,sha256=5FyD0MMdSGsC3eKeBnpd1LBHyVBqIDWCDjgR8_q6XZo,10289
10
- mxlpy/mc.py,sha256=bt2DrMaovWO_LM3PfkVr0cvK6k_ZSjLRnudasgC5SRM,17132
11
- mxlpy/mca.py,sha256=B1bRb_EHim3uJ90KJkTZ5HXrVOBjrctcRCsQG4PXU-U,9351
12
- mxlpy/model.py,sha256=14gncyYft39rwoiJPb5AynL3whXnZrJY3N7SLExH0Qk,62056
7
+ mxlpy/identify.py,sha256=G-Zyr_l-K2mDtIV1xGrQ52QJxoBYqRoNA5RW6GpbNjs,2213
8
+ mxlpy/label_map.py,sha256=kNzqDVp5X6T4uod-y79d6cItOd7_9jmpojDA1TSPRoE,17872
9
+ mxlpy/linear_label_map.py,sha256=6Ye6IziWGKkYD_5z3FmTVwnJ2T9SvVGN3U4CfXjXseo,10320
10
+ mxlpy/mc.py,sha256=AvvnyNIEvaB9gJyd0RtaFs_uVhZ7Xh2FpljHH04YRkc,17217
11
+ mxlpy/mca.py,sha256=BRNbisYijT2SUT6VdIpIh3Id3VgL3NTtycn0VARDWlE,9375
12
+ mxlpy/model.py,sha256=GD1FGppxDJwvEx4ehcQeOx3qkqr8PxDTQ0TwoixBvKI,71617
13
13
  mxlpy/parallel.py,sha256=yLQLw5O4vnPVp_Zmtw1UhPWtB3483foimxQB-TwFKPg,5016
14
14
  mxlpy/parameterise.py,sha256=IgbvfEnkmaqVq_5GgFjHqApGUN9CJrsVD3Fr7pg9iFA,758
15
15
  mxlpy/paths.py,sha256=TK2wO4N9lG-UV1JGfeB64q48JVDbwqIUj63rl55MKuQ,1022
16
16
  mxlpy/plot.py,sha256=PA7tAmy2XXACxBLtdnfpxKUFRzi-lnCQjr7gw_nzxKU,32544
17
17
  mxlpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- mxlpy/report.py,sha256=6V_kH5usFtar2lUGLjG5k7WIJjUi1TD5qIO7V_6V3Gc,8773
19
- mxlpy/scan.py,sha256=_MwwTiL-IQPlUU9e2L1Wc-V95sElcaPw-DkxoXYanzc,19421
20
- mxlpy/simulator.py,sha256=tdxTUbsWo3SdYi7a1lsMah9D_Dz_FqnMjTfK-3oT2Gk,21091
21
- mxlpy/types.py,sha256=FXBkwHgQ3v_k4ER49hDqyFMIA6i1BQf8isPma97LJdg,12605
18
+ mxlpy/report.py,sha256=v597yzKecgtoNmlNZ_nVhBCa3czNw0tksOK5mLtAQvE,10631
19
+ mxlpy/scan.py,sha256=WARFQgFwOsO2PzC1d2rGDfgDjYV8cYwc196IB2rzrzY,19416
20
+ mxlpy/simulator.py,sha256=so7Dax42EKppeBUTxWWKxYhdAFKS2jAgWzS04fXuFpY,27751
21
+ mxlpy/types.py,sha256=Z697aAiJ3Dp2nvMvdm60Z8rEhQjKKgrYHa1riZpY-QE,12994
22
+ mxlpy/units.py,sha256=WHtP9s90VR8rUpPLsQ_eQeiW3wMPEryMvLpsDfu0tEo,1946
22
23
  mxlpy/experimental/__init__.py,sha256=kZTE-92OErpHzNRqmgSQYH4CGXrogGJ5EL35XGZQ81M,206
23
- mxlpy/experimental/diff.py,sha256=MoM15rbMAHa7p9Zva8NxIc7K585kHJYKFaD1LnN5e10,9088
24
+ mxlpy/experimental/diff.py,sha256=g5hKvFsEUdEk5OGQ_aQuCxLAnenD_jG4G__EcVfKsx4,9104
24
25
  mxlpy/integrators/__init__.py,sha256=OLdcNCDIOiD1Z2LO143YtD47cMadNJt0app41nLAx5o,456
25
26
  mxlpy/integrators/int_assimulo.py,sha256=8gLR1D4zJ-TnJ9DTkfkqA2uVE0H2w_npZhZ8RoWZOX8,5013
26
- mxlpy/integrators/int_scipy.py,sha256=MEwhTNhMMVQE2UFWxv5fifN6TKVjRsyDmyibuuNNiHI,4649
27
- mxlpy/meta/__init__.py,sha256=Z3LnN3a9qDAJTukHZs_nF_G6DrjKXOqBvOb47rSsAsM,314
28
- mxlpy/meta/codegen_latex.py,sha256=ocdn_mrjPXllYBwImOBQcFzjFR6LONnBs3fhRIA0yzs,22875
29
- mxlpy/meta/codegen_modebase.py,sha256=ziUuwod1F10ak7WTj5gcuVL7MLtK65kUhqKGCxgn3mY,3131
30
- mxlpy/meta/codegen_py.py,sha256=bpwXrGUaf8lO81VVcIh0cbtf4cd84CYDZrL3ngf1FHo,3587
31
- mxlpy/meta/source_tools.py,sha256=8kZD0_FnO2t8MTG9FvEFOhhU52uXKNpQJW6xDOGWGck,13540
27
+ mxlpy/integrators/int_scipy.py,sha256=82nU6cN4PjPoTEXSK5GuFxG89EnnKBHbjG8A9YDPKnc,4749
28
+ mxlpy/meta/__init__.py,sha256=_Bec5aPJ6YyAkxUXlsQtAy_2VzX0tPGVSj-eGACqrXc,404
29
+ mxlpy/meta/codegen_latex.py,sha256=i4tPvk2-toAYqtf4TynuE9sfUSHUp21AMUgjgFEB0uo,23215
30
+ mxlpy/meta/codegen_model.py,sha256=LT767mzniKgAAtMTbejGcl6YmivNa3J1wZjEsH6iX9M,5067
31
+ mxlpy/meta/codegen_mxlpy.py,sha256=Yx6GtsHUup4bIrEyMw9FYE60Y35xAK81IC-r0EdZDEM,3576
32
+ mxlpy/meta/source_tools.py,sha256=imdNbGfDjmEEI7gLWm3BFUfB1xwhW4Lt6mhKvE0PhLw,20735
33
+ mxlpy/meta/sympy_tools.py,sha256=TJSCFK9yuxwNrUHbZrcayo_g1UQWmiUiw4Z5V_pUye4,2853
32
34
  mxlpy/nn/__init__.py,sha256=Qjr-ERsY2lbD75sFBOhCUwEasQDSJKcpBn_kReLZ6oA,633
33
35
  mxlpy/nn/_keras.py,sha256=-5zjHRu8OjSiZeuBSIZFyB63uBsNNH5o9y4kBcPnhx8,2263
34
36
  mxlpy/nn/_torch.py,sha256=GUJmLU282VU4O-fs3Sz90SEaAnfuXN2MPlBr_tHmvn4,5775
@@ -37,8 +39,8 @@ mxlpy/npe/_keras.py,sha256=ytvXMPK9KUCGOzTQm08_SgafiMb-MOIUdZQV7JjAO40,9721
37
39
  mxlpy/npe/_torch.py,sha256=v3joh6lFJJxvYJj--wzmKXL9UMTaIN3h6hPNq0uX9NU,11250
38
40
  mxlpy/sbml/__init__.py,sha256=Mt97CddpLi3wIrA1b_5cagLmDdNxAVF_S7QV57Pzw8s,226
39
41
  mxlpy/sbml/_data.py,sha256=yYli7ZQ1_pnH9kt5EmcuHM0moQoa43rrFVdrseXlG0o,1136
40
- mxlpy/sbml/_export.py,sha256=4tU3SVxfEvl0E1urZWHyphkiAeH5HeRO1cODvvrczAQ,20342
41
- mxlpy/sbml/_import.py,sha256=5odQBdpD93mQJp2bVIabmPo6NK60nxqrdSVB8fEsF_A,22099
42
+ mxlpy/sbml/_export.py,sha256=DibzxWLsHQeI4rmvhpV0AfbDmud3lIcfcdm5BV51-PE,20538
43
+ mxlpy/sbml/_import.py,sha256=aCxNKEO6qaZRc7XWFwzOLE1PV0hXFstJbD-SkLjef2k,22082
42
44
  mxlpy/sbml/_mathml.py,sha256=oaU9q5yb9jvDGxDJrqOkbOiurCB1Vv_P99oUwJ7v1VE,24437
43
45
  mxlpy/sbml/_name_conversion.py,sha256=93muW47M7qJoE227HKHmThWpPeWsXDN9eM8cRH2pqPs,1340
44
46
  mxlpy/sbml/_unit_conversion.py,sha256=dW_I6_Ou09ccwnp6LIdrPriIQnQUK5lJcjzM2Fawm6U,1927
@@ -49,8 +51,8 @@ mxlpy/surrogates/_qss.py,sha256=9w-hPPhdcwybkyaSX0sIfYfvcKu1U5j4HHj4SlgZcYQ,723
49
51
  mxlpy/surrogates/_torch.py,sha256=gU0secuRBYgewhNqZmSo6_Xf804dSzwWwIYmdKA7y60,6389
50
52
  mxlpy/symbolic/__init__.py,sha256=_vM5YM5I6OH0QDbFt9uGYKO8Z5Vly0wbGuvUScVrPRU,258
51
53
  mxlpy/symbolic/strikepy.py,sha256=tzo3uvPpXLDex09hWTuitVzuTNwbgl7jWGjD8g6a8iI,20033
52
- mxlpy/symbolic/symbolic_model.py,sha256=JFzcIdyfJihvKjef748DMXU6WI8nHjgjIk5BwUuB4HQ,2543
53
- mxlpy-0.21.0.dist-info/METADATA,sha256=ZKxvZTNcrNCaadkRmKSU_6bQlSsJdTtuz_idNVEVbVs,4601
54
- mxlpy-0.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- mxlpy-0.21.0.dist-info/licenses/LICENSE,sha256=lHX9Eu70g3Iv1aOxXTWNHa3vq9vaVYSPQx4jOLYmDpw,1096
56
- mxlpy-0.21.0.dist-info/RECORD,,
54
+ mxlpy/symbolic/symbolic_model.py,sha256=cKfWoktvFmXjuo8egE7gXKrKBq2iBUiy_BcIKIvvz8A,3026
55
+ mxlpy-0.22.0.dist-info/METADATA,sha256=LsEH4wwslk1cTqQzL_JvkHvPlk5zdfnNebzUzPPhHeY,4601
56
+ mxlpy-0.22.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
+ mxlpy-0.22.0.dist-info/licenses/LICENSE,sha256=lHX9Eu70g3Iv1aOxXTWNHa3vq9vaVYSPQx4jOLYmDpw,1096
58
+ mxlpy-0.22.0.dist-info/RECORD,,
mxlpy/meta/codegen_py.py DELETED
@@ -1,115 +0,0 @@
1
- """Module to export models as code."""
2
-
3
- from __future__ import annotations
4
-
5
- import warnings
6
- from typing import TYPE_CHECKING
7
-
8
- import sympy
9
-
10
- from mxlpy.meta.source_tools import fn_to_sympy, sympy_to_inline
11
- from mxlpy.types import Derived
12
-
13
- if TYPE_CHECKING:
14
- from collections.abc import Callable, Generator, Iterable, Iterator
15
-
16
- from mxlpy.model import Model
17
-
18
- __all__ = [
19
- "generate_model_code_py",
20
- ]
21
-
22
-
23
- def _conditional_join[T](
24
- iterable: Iterable[T],
25
- question: Callable[[T], bool],
26
- true_pat: str,
27
- false_pat: str,
28
- ) -> str:
29
- """Join an iterable, applying a pattern to each element based on a condition."""
30
-
31
- def inner(it: Iterator[T]) -> Generator[str, None, None]:
32
- yield str(next(it))
33
- while True:
34
- try:
35
- el = next(it)
36
- if question(el):
37
- yield f"{true_pat}{el}"
38
- else:
39
- yield f"{false_pat}{el}"
40
- except StopIteration:
41
- break
42
-
43
- return "".join(inner(iter(iterable)))
44
-
45
-
46
- def _list_of_symbols(args: list[str]) -> list[sympy.Symbol | sympy.Expr]:
47
- return [sympy.Symbol(arg) for arg in args]
48
-
49
-
50
- # FIXME: generate from SymbolicModel, should be easier?
51
- def generate_model_code_py(model: Model) -> str:
52
- """Transform the model into a single function, inlining the function calls."""
53
- source = [
54
- "from collections.abc import Iterable\n",
55
- "from mxlpy.types import Float\n",
56
- "def model(t: Float, variables: Float) -> Iterable[Float]:",
57
- ]
58
-
59
- # Variables
60
- variables = model.variables
61
- if len(variables) > 0:
62
- source.append(" {} = variables".format(", ".join(variables)))
63
-
64
- # Parameters
65
- parameters = model.parameters
66
- if len(parameters) > 0:
67
- source.append("\n".join(f" {k} = {v}" for k, v in model.parameters.items()))
68
-
69
- # Derived
70
- for name, derived in model.derived.items():
71
- expr = fn_to_sympy(derived.fn, model_args=_list_of_symbols(derived.args))
72
- source.append(f" {name} = {sympy_to_inline(expr)}")
73
-
74
- # Reactions
75
- for name, rxn in model.reactions.items():
76
- expr = fn_to_sympy(rxn.fn, model_args=_list_of_symbols(rxn.args))
77
- source.append(f" {name} = {sympy_to_inline(expr)}")
78
-
79
- # Stoichiometries; FIXME: do this with sympy instead as well?
80
- stoich_srcs = {}
81
- for rxn_name, rxn in model.reactions.items():
82
- for i, (cpd_name, factor) in enumerate(rxn.stoichiometry.items()):
83
- if isinstance(factor, Derived):
84
- expr = fn_to_sympy(factor.fn, model_args=_list_of_symbols(factor.args))
85
- src = f"{sympy_to_inline(expr)} * {rxn_name}"
86
- elif factor == 1:
87
- src = rxn_name
88
- elif factor == -1:
89
- src = f"-{rxn_name}" if i == 0 else f"- {rxn_name}"
90
- else:
91
- src = f"{factor} * {rxn_name}"
92
- stoich_srcs.setdefault(cpd_name, []).append(src)
93
- for variable, stoich in stoich_srcs.items():
94
- source.append(
95
- f" d{variable}dt = {_conditional_join(stoich, lambda x: x.startswith('-'), ' ', ' + ')}"
96
- )
97
-
98
- # Surrogates
99
- if len(model._surrogates) > 0: # noqa: SLF001
100
- warnings.warn(
101
- "Generating code for Surrogates not yet supported.",
102
- stacklevel=1,
103
- )
104
-
105
- # Return
106
- if len(variables) > 0:
107
- source.append(
108
- " return {}".format(
109
- ", ".join(f"d{i}dt" for i in variables),
110
- ),
111
- )
112
- else:
113
- source.append(" return ()")
114
-
115
- return "\n".join(source)
File without changes