mxlpy 0.19.0__py3-none-any.whl → 0.21.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/__init__.py +4 -10
- mxlpy/carousel.py +166 -0
- mxlpy/compare.py +240 -0
- mxlpy/experimental/diff.py +15 -3
- mxlpy/fit.py +279 -38
- mxlpy/fns.py +37 -42
- mxlpy/identify.py +16 -10
- mxlpy/integrators/__init__.py +4 -3
- mxlpy/integrators/int_assimulo.py +6 -6
- mxlpy/integrators/int_scipy.py +6 -6
- mxlpy/label_map.py +4 -2
- mxlpy/linear_label_map.py +4 -2
- mxlpy/mc.py +26 -34
- mxlpy/mca.py +11 -9
- mxlpy/meta/__init__.py +6 -4
- mxlpy/meta/codegen_latex.py +179 -86
- mxlpy/meta/codegen_modebase.py +3 -1
- mxlpy/meta/codegen_py.py +11 -3
- mxlpy/meta/source_tools.py +8 -4
- mxlpy/model.py +42 -14
- mxlpy/nn/_keras.py +10 -3
- mxlpy/nn/_torch.py +7 -1
- mxlpy/parallel.py +12 -8
- mxlpy/parameterise.py +11 -3
- mxlpy/plot.py +203 -50
- mxlpy/report.py +33 -8
- mxlpy/sbml/__init__.py +3 -3
- mxlpy/sbml/_data.py +7 -6
- mxlpy/sbml/_mathml.py +8 -7
- mxlpy/sbml/_name_conversion.py +5 -1
- mxlpy/scan.py +44 -37
- mxlpy/simulator.py +36 -33
- mxlpy/surrogates/_keras.py +2 -0
- mxlpy/surrogates/_poly.py +6 -2
- mxlpy/surrogates/_qss.py +4 -1
- mxlpy/surrogates/_torch.py +6 -2
- mxlpy/symbolic/__init__.py +5 -3
- mxlpy/symbolic/strikepy.py +5 -2
- mxlpy/symbolic/symbolic_model.py +12 -3
- mxlpy/types.py +5 -10
- {mxlpy-0.19.0.dist-info → mxlpy-0.21.0.dist-info}/METADATA +8 -4
- mxlpy-0.21.0.dist-info/RECORD +56 -0
- mxlpy-0.19.0.dist-info/RECORD +0 -54
- {mxlpy-0.19.0.dist-info → mxlpy-0.21.0.dist-info}/WHEEL +0 -0
- {mxlpy-0.19.0.dist-info → mxlpy-0.21.0.dist-info}/licenses/LICENSE +0 -0
mxlpy/meta/codegen_latex.py
CHANGED
@@ -10,6 +10,11 @@ import sympy
|
|
10
10
|
from mxlpy.meta.source_tools import fn_to_sympy
|
11
11
|
from mxlpy.types import Derived, RateFn
|
12
12
|
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from collections.abc import Callable, Mapping
|
15
|
+
|
16
|
+
from mxlpy import Model
|
17
|
+
|
13
18
|
__all__ = [
|
14
19
|
"TexExport",
|
15
20
|
"TexReaction",
|
@@ -18,12 +23,6 @@ __all__ = [
|
|
18
23
|
"get_model_tex_diff",
|
19
24
|
]
|
20
25
|
|
21
|
-
if TYPE_CHECKING:
|
22
|
-
from collections.abc import Callable, Mapping
|
23
|
-
|
24
|
-
from mxlpy import Model
|
25
|
-
|
26
|
-
|
27
26
|
cdot = r"\cdot"
|
28
27
|
empty_set = r"\emptyset"
|
29
28
|
left_right_arrows = r"\xleftrightharpoons{}"
|
@@ -94,8 +93,38 @@ def _escape_non_math(s: str) -> str:
|
|
94
93
|
return s.replace("_", r"\_")
|
95
94
|
|
96
95
|
|
97
|
-
def
|
98
|
-
return
|
96
|
+
def _name_to_latex(s: str) -> str:
|
97
|
+
return _escape_non_math(_rename_latex(s))
|
98
|
+
|
99
|
+
|
100
|
+
def _sympy_to_latex(expr: sympy.Expr) -> str:
|
101
|
+
return sympy.latex(
|
102
|
+
expr,
|
103
|
+
fold_frac_powers=True,
|
104
|
+
fold_func_brackets=True,
|
105
|
+
fold_short_frac=True,
|
106
|
+
mul_symbol="dot",
|
107
|
+
)
|
108
|
+
|
109
|
+
|
110
|
+
def _fn_to_latex(
|
111
|
+
fn: Callable,
|
112
|
+
arg_names: list[str],
|
113
|
+
long_name_cutoff: int,
|
114
|
+
) -> tuple[str, dict[str, str]]:
|
115
|
+
tex_names = [_mathrm(_name_to_latex(i)) for i in arg_names]
|
116
|
+
long_names = (
|
117
|
+
k
|
118
|
+
for k, k_orig in zip(tex_names, arg_names, strict=True)
|
119
|
+
if len(k_orig) >= long_name_cutoff
|
120
|
+
)
|
121
|
+
replacements = {k: _name_to_latex(f"_x{i}") for i, k in enumerate(long_names)}
|
122
|
+
|
123
|
+
expr = fn_to_sympy(
|
124
|
+
fn, _list_of_symbols([replacements.get(k, k) for k in tex_names])
|
125
|
+
)
|
126
|
+
fn_str = _sympy_to_latex(expr)
|
127
|
+
return fn_str, replacements
|
99
128
|
|
100
129
|
|
101
130
|
def _table(
|
@@ -213,6 +242,12 @@ def _latex_list(rows: list[str]) -> str:
|
|
213
242
|
return "\n\n".join(rows)
|
214
243
|
|
215
244
|
|
245
|
+
def _latex_align(items: list[str]) -> str:
|
246
|
+
return rf"""\begin{{align*}}
|
247
|
+
{"\n".join(items)}
|
248
|
+
\end{{align*}}"""
|
249
|
+
|
250
|
+
|
216
251
|
def _latex_list_as_sections(
|
217
252
|
rows: list[tuple[str, str]], sec_fn: Callable[[str], str]
|
218
253
|
) -> str:
|
@@ -220,7 +255,7 @@ def _latex_list_as_sections(
|
|
220
255
|
[
|
221
256
|
"\n".join(
|
222
257
|
(
|
223
|
-
sec_fn(
|
258
|
+
sec_fn(_name_to_latex(name)),
|
224
259
|
content,
|
225
260
|
)
|
226
261
|
)
|
@@ -234,7 +269,7 @@ def _latex_list_as_bold(rows: list[tuple[str, str]]) -> str:
|
|
234
269
|
[
|
235
270
|
"\n".join(
|
236
271
|
(
|
237
|
-
_bold(
|
272
|
+
_bold(_name_to_latex(name)) + r"\\",
|
238
273
|
content,
|
239
274
|
r"\vspace{20pt}",
|
240
275
|
)
|
@@ -244,33 +279,57 @@ def _latex_list_as_bold(rows: list[tuple[str, str]]) -> str:
|
|
244
279
|
)
|
245
280
|
|
246
281
|
|
247
|
-
def
|
248
|
-
|
249
|
-
if v == 1:
|
250
|
-
return _mathrm(k)
|
251
|
-
if v == -1:
|
252
|
-
return f"-{_mathrm(k)}"
|
253
|
-
return f"{v} {cdot} {_mathrm(k)}"
|
282
|
+
def _replacements_in_align(replacements: dict[str, str]) -> str:
|
283
|
+
reps = "\n".join(rf"&\qquad {v} :: {k} \\" for k, v in replacements.items())
|
254
284
|
|
255
|
-
|
256
|
-
|
257
|
-
return empty_set
|
258
|
-
return s
|
285
|
+
return rf"""&\quad \mathrm{{with}}\\
|
286
|
+
{reps}\\"""
|
259
287
|
|
260
|
-
line = []
|
261
|
-
for k, v in stoich.items():
|
262
|
-
if isinstance(v, int | float):
|
263
|
-
line.append(optional_factor(k, v))
|
264
|
-
else:
|
265
|
-
line.append(_fn_to_latex(v.fn, [_rename_latex(i) for i in v.args]))
|
266
288
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
289
|
+
def _diff_eq(name: str) -> str:
|
290
|
+
return rf"\frac{{d\left({name}\right)}}{{dt}}"
|
291
|
+
|
292
|
+
|
293
|
+
def _optional_factor(k: str, v: float) -> str:
|
294
|
+
if v == 1:
|
295
|
+
return k
|
296
|
+
if v == -1:
|
297
|
+
return f"-{k}"
|
298
|
+
return f"{v} {cdot} {k}"
|
299
|
+
|
300
|
+
|
301
|
+
def _stoichs_to_latex(
|
302
|
+
stoichs: Mapping[str, float | Derived],
|
303
|
+
long_name_cutoff: int,
|
304
|
+
) -> tuple[str, dict[str, str]]:
|
305
|
+
replacements = {}
|
306
|
+
expr = sympy.Integer(0)
|
307
|
+
|
308
|
+
for rxn_name, rxn_stoich in stoichs.items():
|
309
|
+
rxn_name = _name_to_latex(rxn_name) # noqa: PLW2901
|
310
|
+
|
311
|
+
if isinstance(rxn_stoich, Derived):
|
312
|
+
arg_names = [_mathrm(_name_to_latex(i)) for i in rxn_stoich.args]
|
313
|
+
long_names = (
|
314
|
+
k
|
315
|
+
for k, k_orig in zip(arg_names, rxn_stoich.args, strict=True)
|
316
|
+
if len(k_orig) >= long_name_cutoff
|
317
|
+
)
|
318
|
+
replacements.update(
|
319
|
+
{
|
320
|
+
k: _name_to_latex(f"_x{i}")
|
321
|
+
for i, k in enumerate(long_names, len(replacements))
|
322
|
+
}
|
323
|
+
)
|
324
|
+
sympy_fn = fn_to_sympy(
|
325
|
+
rxn_stoich.fn,
|
326
|
+
_list_of_symbols([replacements.get(k, k) for k in arg_names]),
|
327
|
+
)
|
328
|
+
expr = expr + sympy_fn * sympy.Symbol(rxn_name) # type: ignore
|
271
329
|
else:
|
272
|
-
|
273
|
-
|
330
|
+
expr = expr + rxn_stoich * sympy.Symbol(rxn_name) # type: ignore
|
331
|
+
|
332
|
+
return _sympy_to_latex(expr.subs(1.0, 1).simplify()), replacements
|
274
333
|
|
275
334
|
|
276
335
|
@dataclass
|
@@ -333,7 +392,7 @@ class TexExport:
|
|
333
392
|
variables: dict[str, float]
|
334
393
|
derived: dict[str, Derived]
|
335
394
|
reactions: dict[str, TexReaction]
|
336
|
-
|
395
|
+
diff_eqs: dict[str, Mapping[str, float | Derived]]
|
337
396
|
|
338
397
|
@staticmethod
|
339
398
|
def _diff_parameters(
|
@@ -390,10 +449,8 @@ class TexExport:
|
|
390
449
|
variables=self._diff_variables(self.variables, other.variables),
|
391
450
|
derived=self._diff_derived(self.derived, other.derived),
|
392
451
|
reactions=self._diff_reactions(self.reactions, other.reactions),
|
393
|
-
|
394
|
-
k: v
|
395
|
-
for k, v in other.stoichiometries.items()
|
396
|
-
if self.stoichiometries.get(k, {}) != v
|
452
|
+
diff_eqs={
|
453
|
+
k: v for k, v in other.diff_eqs.items() if self.diff_eqs.get(k, {}) != v
|
397
454
|
},
|
398
455
|
)
|
399
456
|
|
@@ -440,9 +497,9 @@ class TexExport:
|
|
440
497
|
)
|
441
498
|
for k, v in self.reactions.items()
|
442
499
|
},
|
443
|
-
|
500
|
+
diff_eqs={
|
444
501
|
_add_gls_if_found(k): {gls.get(k2, k2): v2 for k2, v2 in v.items()}
|
445
|
-
for k, v in self.
|
502
|
+
for k, v in self.diff_eqs.items()
|
446
503
|
},
|
447
504
|
)
|
448
505
|
|
@@ -466,7 +523,7 @@ class TexExport:
|
|
466
523
|
headers=["Model name", "Initial concentration"],
|
467
524
|
rows=[
|
468
525
|
[
|
469
|
-
k,
|
526
|
+
_name_to_latex(k),
|
470
527
|
f"{v:.2e}",
|
471
528
|
]
|
472
529
|
for k, v in self.variables.items()
|
@@ -496,7 +553,7 @@ class TexExport:
|
|
496
553
|
return _table(
|
497
554
|
headers=["Parameter name", "Parameter value"],
|
498
555
|
rows=[
|
499
|
-
[
|
556
|
+
[_name_to_latex(k), f"{v:.2e}"]
|
500
557
|
for k, v in sorted(self.parameters.items())
|
501
558
|
],
|
502
559
|
n_columns=2,
|
@@ -505,7 +562,10 @@ class TexExport:
|
|
505
562
|
long_desc="Model parameters",
|
506
563
|
)
|
507
564
|
|
508
|
-
def export_derived(
|
565
|
+
def export_derived(
|
566
|
+
self,
|
567
|
+
long_name_cutoff: int,
|
568
|
+
) -> str:
|
509
569
|
"""Export derived quantities as LaTeX equations.
|
510
570
|
|
511
571
|
Returns
|
@@ -523,16 +583,20 @@ class TexExport:
|
|
523
583
|
True
|
524
584
|
|
525
585
|
"""
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
586
|
+
rows = []
|
587
|
+
for k, v in sorted(self.derived.items()):
|
588
|
+
fn_str, repls = _fn_to_latex(
|
589
|
+
v.fn,
|
590
|
+
arg_names=v.args,
|
591
|
+
long_name_cutoff=long_name_cutoff,
|
592
|
+
)
|
593
|
+
rows.append(f"{_mathrm(_name_to_latex(k))} &= {fn_str} \\\\")
|
594
|
+
if repls:
|
595
|
+
rows.append(_replacements_in_align(repls))
|
534
596
|
|
535
|
-
|
597
|
+
return _latex_align(rows)
|
598
|
+
|
599
|
+
def export_reactions(self, long_name_cutoff: int) -> str:
|
536
600
|
"""Export reactions as LaTeX equations.
|
537
601
|
|
538
602
|
Returns
|
@@ -550,16 +614,22 @@ class TexExport:
|
|
550
614
|
True
|
551
615
|
|
552
616
|
"""
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
617
|
+
rows = []
|
618
|
+
for k, v in sorted(self.reactions.items()):
|
619
|
+
fn_str, repls = _fn_to_latex(
|
620
|
+
v.fn,
|
621
|
+
arg_names=v.args,
|
622
|
+
long_name_cutoff=long_name_cutoff,
|
623
|
+
)
|
624
|
+
rows.append(f"{_mathrm(_name_to_latex(k))} &= {fn_str} \\\\")
|
625
|
+
if repls:
|
626
|
+
rows.append(_replacements_in_align(repls))
|
627
|
+
return _latex_align(rows)
|
561
628
|
|
562
|
-
def
|
629
|
+
def export_diff_eqs(
|
630
|
+
self,
|
631
|
+
long_name_cutoff: int,
|
632
|
+
) -> str:
|
563
633
|
"""Export stoichiometries as LaTeX table.
|
564
634
|
|
565
635
|
Returns
|
@@ -576,22 +646,20 @@ class TexExport:
|
|
576
646
|
True
|
577
647
|
|
578
648
|
"""
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
long_desc="Model stoichiometries.",
|
592
|
-
)
|
649
|
+
rows = []
|
650
|
+
for var_name, stoich in sorted(self.diff_eqs.items()):
|
651
|
+
dxdt = _diff_eq(_mathrm(_name_to_latex(var_name)))
|
652
|
+
stoich_str, repls = _stoichs_to_latex(
|
653
|
+
stoich,
|
654
|
+
long_name_cutoff=long_name_cutoff,
|
655
|
+
)
|
656
|
+
|
657
|
+
rows.append(f"{dxdt} &= {stoich_str} \\\\")
|
658
|
+
if repls:
|
659
|
+
rows.append(_replacements_in_align(repls))
|
660
|
+
return _latex_align(rows)
|
593
661
|
|
594
|
-
def export_all(self) -> str:
|
662
|
+
def export_all(self, long_name_cutoff: int = 10) -> str:
|
595
663
|
"""Export all model parts as a complete LaTeX document section.
|
596
664
|
|
597
665
|
Returns
|
@@ -626,28 +694,35 @@ class TexExport:
|
|
626
694
|
sections.append(
|
627
695
|
(
|
628
696
|
"Derived",
|
629
|
-
self.export_derived(
|
697
|
+
self.export_derived(
|
698
|
+
long_name_cutoff=long_name_cutoff,
|
699
|
+
),
|
630
700
|
)
|
631
701
|
)
|
632
702
|
if len(self.reactions) > 0:
|
633
703
|
sections.append(
|
634
704
|
(
|
635
705
|
"Reactions",
|
636
|
-
self.export_reactions(
|
706
|
+
self.export_reactions(
|
707
|
+
long_name_cutoff=long_name_cutoff,
|
708
|
+
),
|
637
709
|
)
|
638
710
|
)
|
639
711
|
sections.append(
|
640
712
|
(
|
641
|
-
"
|
642
|
-
self.
|
713
|
+
"Differential equations",
|
714
|
+
self.export_diff_eqs(
|
715
|
+
long_name_cutoff=long_name_cutoff,
|
716
|
+
),
|
643
717
|
)
|
644
718
|
)
|
645
|
-
return _latex_list_as_sections(sections,
|
719
|
+
return _latex_list_as_sections(sections, _subsection_)
|
646
720
|
|
647
721
|
def export_document(
|
648
722
|
self,
|
649
723
|
author: str = "mxlpy",
|
650
724
|
title: str = "Model construction",
|
725
|
+
long_name_cutoff: int = 10,
|
651
726
|
) -> str:
|
652
727
|
r"""Export complete LaTeX document with all model components.
|
653
728
|
|
@@ -657,6 +732,8 @@ class TexExport:
|
|
657
732
|
Name of the author for the document
|
658
733
|
title
|
659
734
|
Title for the document
|
735
|
+
long_name_cutoff
|
736
|
+
length of function argument names before they are shortened
|
660
737
|
|
661
738
|
Returns
|
662
739
|
-------
|
@@ -671,8 +748,8 @@ class TexExport:
|
|
671
748
|
True
|
672
749
|
|
673
750
|
"""
|
674
|
-
content = self.export_all()
|
675
|
-
return rf"""\documentclass{{article}}
|
751
|
+
content = self.export_all(long_name_cutoff=long_name_cutoff)
|
752
|
+
return rf"""\documentclass[fleqn]{{article}}
|
676
753
|
\usepackage[english]{{babel}}
|
677
754
|
\usepackage[a4paper,top=2cm,bottom=2cm,left=2cm,right=2cm,marginparwidth=1.75cm]{{geometry}}
|
678
755
|
\usepackage{{amsmath, amssymb, array, booktabs,
|
@@ -680,6 +757,7 @@ class TexExport:
|
|
680
757
|
ragged2e, tabularx, titlesec, titling}}
|
681
758
|
\newcommand{{\sectionbreak}}{{\clearpage}}
|
682
759
|
\setlength{{\parindent}}{{0pt}}
|
760
|
+
\allowdisplaybreaks
|
683
761
|
|
684
762
|
\title{{{title}}}
|
685
763
|
\date{{}} % clear date
|
@@ -692,18 +770,24 @@ class TexExport:
|
|
692
770
|
|
693
771
|
|
694
772
|
def _to_tex_export(self: Model) -> TexExport:
|
773
|
+
diff_eqs = {}
|
774
|
+
for rxn_name, rxn in self.reactions.items():
|
775
|
+
for var_name, factor in rxn.stoichiometry.items():
|
776
|
+
diff_eqs.setdefault(var_name, {})[rxn_name] = factor
|
777
|
+
|
695
778
|
return TexExport(
|
696
779
|
parameters=self.parameters,
|
697
780
|
variables=self.get_initial_conditions(), # FIXME: think about this later
|
698
781
|
derived=self.derived,
|
699
782
|
reactions={k: TexReaction(v.fn, v.args) for k, v in self.reactions.items()},
|
700
|
-
|
783
|
+
diff_eqs=diff_eqs,
|
701
784
|
)
|
702
785
|
|
703
786
|
|
704
787
|
def generate_latex_code(
|
705
788
|
model: Model,
|
706
789
|
gls: dict[str, str] | None = None,
|
790
|
+
long_name_cutoff: int = 10,
|
707
791
|
) -> str:
|
708
792
|
"""Export model as LaTeX document.
|
709
793
|
|
@@ -713,6 +797,8 @@ def generate_latex_code(
|
|
713
797
|
The model to export
|
714
798
|
gls
|
715
799
|
Optional glossary mapping for renaming model components
|
800
|
+
long_name_cutoff
|
801
|
+
length of function argument names before they are shortened
|
716
802
|
|
717
803
|
Returns
|
718
804
|
-------
|
@@ -733,13 +819,18 @@ def generate_latex_code(
|
|
733
819
|
|
734
820
|
"""
|
735
821
|
gls = default_init(gls)
|
736
|
-
return
|
822
|
+
return (
|
823
|
+
_to_tex_export(model)
|
824
|
+
.rename_with_glossary(gls)
|
825
|
+
.export_document(long_name_cutoff=long_name_cutoff)
|
826
|
+
)
|
737
827
|
|
738
828
|
|
739
829
|
def get_model_tex_diff(
|
740
830
|
m1: Model,
|
741
831
|
m2: Model,
|
742
832
|
gls: dict[str, str] | None = None,
|
833
|
+
long_name_cutoff: int = 10,
|
743
834
|
) -> str:
|
744
835
|
"""Create LaTeX diff showing changes between two models.
|
745
836
|
|
@@ -751,6 +842,8 @@ def get_model_tex_diff(
|
|
751
842
|
Second model (compared against the base)
|
752
843
|
gls
|
753
844
|
Optional glossary mapping for renaming model components
|
845
|
+
long_name_cutoff
|
846
|
+
length of function argument names before they are shortened
|
754
847
|
|
755
848
|
Returns
|
756
849
|
-------
|
@@ -773,7 +866,7 @@ def get_model_tex_diff(
|
|
773
866
|
return f"""{" start autogenerated ":%^60}
|
774
867
|
{_clearpage()}
|
775
868
|
{_subsubsection("Model changes")}{_label(section_label)}
|
776
|
-
{((_to_tex_export(m1) - _to_tex_export(m2)).rename_with_glossary(gls).export_all())}
|
869
|
+
{((_to_tex_export(m1) - _to_tex_export(m2)).rename_with_glossary(gls).export_all(long_name_cutoff=long_name_cutoff))}
|
777
870
|
{_clearpage()}
|
778
871
|
{" end autogenerated ":%^60}
|
779
872
|
"""
|
mxlpy/meta/codegen_modebase.py
CHANGED
mxlpy/meta/codegen_py.py
CHANGED
@@ -1,15 +1,23 @@
|
|
1
1
|
"""Module to export models as code."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
import warnings
|
4
|
-
from
|
6
|
+
from typing import TYPE_CHECKING
|
5
7
|
|
6
8
|
import sympy
|
7
9
|
|
8
10
|
from mxlpy.meta.source_tools import fn_to_sympy, sympy_to_inline
|
9
|
-
from mxlpy.model import Model
|
10
11
|
from mxlpy.types import Derived
|
11
12
|
|
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
|
+
]
|
13
21
|
|
14
22
|
|
15
23
|
def _conditional_join[T](
|
mxlpy/meta/source_tools.py
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
"""Tools for working with python source files."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
import ast
|
4
6
|
import inspect
|
5
7
|
import textwrap
|
6
|
-
from collections.abc import Callable
|
7
8
|
from dataclasses import dataclass
|
8
|
-
from
|
9
|
-
from typing import Any, cast
|
9
|
+
from typing import TYPE_CHECKING, Any, cast
|
10
10
|
|
11
11
|
import dill
|
12
12
|
import sympy
|
13
13
|
from sympy.printing.pycode import pycode
|
14
14
|
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from collections.abc import Callable
|
17
|
+
from types import ModuleType
|
18
|
+
|
15
19
|
__all__ = [
|
16
20
|
"Context",
|
17
21
|
"fn_to_sympy",
|
@@ -35,7 +39,7 @@ class Context:
|
|
35
39
|
symbols: dict[str, sympy.Symbol | sympy.Expr] | None = None,
|
36
40
|
caller: Callable | None = None,
|
37
41
|
parent_module: ModuleType | None = None,
|
38
|
-
) ->
|
42
|
+
) -> Context:
|
39
43
|
"""Update the context with new values."""
|
40
44
|
return Context(
|
41
45
|
symbols=self.symbols if symbols is None else symbols,
|
mxlpy/model.py
CHANGED
@@ -17,13 +17,13 @@ import numpy as np
|
|
17
17
|
import pandas as pd
|
18
18
|
|
19
19
|
from mxlpy import fns
|
20
|
-
from mxlpy.types import
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
from mxlpy.types import AbstractSurrogate, Array, Derived, Reaction, Readout
|
21
|
+
|
22
|
+
if TYPE_CHECKING:
|
23
|
+
from collections.abc import Iterable, Mapping
|
24
|
+
from inspect import FullArgSpec
|
25
|
+
|
26
|
+
from mxlpy.types import Callable, Param, RateFn, RetType
|
27
27
|
|
28
28
|
__all__ = [
|
29
29
|
"ArityMismatchError",
|
@@ -34,12 +34,6 @@ __all__ = [
|
|
34
34
|
"ModelCache",
|
35
35
|
]
|
36
36
|
|
37
|
-
if TYPE_CHECKING:
|
38
|
-
from collections.abc import Iterable, Mapping
|
39
|
-
from inspect import FullArgSpec
|
40
|
-
|
41
|
-
from mxlpy.types import Callable, Param, RateFn, RetType
|
42
|
-
|
43
37
|
|
44
38
|
@dataclass
|
45
39
|
class Dependency:
|
@@ -694,6 +688,21 @@ class Model:
|
|
694
688
|
|
695
689
|
return self
|
696
690
|
|
691
|
+
def get_unused_parameters(self) -> set[str]:
|
692
|
+
"""Get parameters which aren't used in the model."""
|
693
|
+
args = set()
|
694
|
+
for variable in self._variables.values():
|
695
|
+
if isinstance(variable, Derived):
|
696
|
+
args.update(variable.args)
|
697
|
+
for derived in self._derived.values():
|
698
|
+
args.update(derived.args)
|
699
|
+
for reaction in self._reactions.values():
|
700
|
+
args.update(reaction.args)
|
701
|
+
for surrogate in self._surrogates.values():
|
702
|
+
args.update(surrogate.args)
|
703
|
+
|
704
|
+
return set(self._parameters).difference(args)
|
705
|
+
|
697
706
|
##########################################################################
|
698
707
|
# Variables
|
699
708
|
##########################################################################
|
@@ -1115,10 +1124,29 @@ class Model:
|
|
1115
1124
|
args = self.get_dependent(variables=variables, time=time)
|
1116
1125
|
|
1117
1126
|
stoich = copy.deepcopy(cache.stoich_by_cpds[variable])
|
1118
|
-
for rxn, derived in cache.dyn_stoich_by_cpds
|
1127
|
+
for rxn, derived in cache.dyn_stoich_by_cpds.get(variable, {}).items():
|
1119
1128
|
stoich[rxn] = float(derived.fn(*(args[i] for i in derived.args)))
|
1120
1129
|
return stoich
|
1121
1130
|
|
1131
|
+
def get_raw_stoichiometries_of_variable(
|
1132
|
+
self, variable: str
|
1133
|
+
) -> dict[str, float | Derived]:
|
1134
|
+
"""Retrieve the raw stoichiometry of a specific variable.
|
1135
|
+
|
1136
|
+
Examples:
|
1137
|
+
>>> model.get_stoichiometries_of_variable("x1")
|
1138
|
+
{"v1": -1, "v2": Derived(...)}
|
1139
|
+
|
1140
|
+
Args:
|
1141
|
+
variable: The name of the variable for which to retrieve the stoichiometry.
|
1142
|
+
|
1143
|
+
"""
|
1144
|
+
stoichs: dict[str, dict[str, float | Derived]] = {}
|
1145
|
+
for rxn_name, rxn in self._reactions.items():
|
1146
|
+
for cpd_name, factor in rxn.stoichiometry.items():
|
1147
|
+
stoichs.setdefault(cpd_name, {})[rxn_name] = factor
|
1148
|
+
return stoichs[variable]
|
1149
|
+
|
1122
1150
|
@_invalidate_cache
|
1123
1151
|
def add_reaction(
|
1124
1152
|
self,
|
mxlpy/nn/_keras.py
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, cast
|
2
4
|
|
3
5
|
import keras
|
4
6
|
import pandas as pd
|
5
7
|
from tqdm.keras import TqdmCallback
|
6
8
|
|
7
|
-
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from mxlpy.types import Array
|
8
11
|
|
9
|
-
__all__ = [
|
12
|
+
__all__ = [
|
13
|
+
"LSTM",
|
14
|
+
"MLP",
|
15
|
+
"train",
|
16
|
+
]
|
10
17
|
|
11
18
|
|
12
19
|
def train(
|
mxlpy/nn/_torch.py
CHANGED
@@ -24,7 +24,13 @@ if TYPE_CHECKING:
|
|
24
24
|
|
25
25
|
from mxlpy.types import Array
|
26
26
|
|
27
|
-
__all__ = [
|
27
|
+
__all__ = [
|
28
|
+
"DefaultDevice",
|
29
|
+
"LSTM",
|
30
|
+
"LossFn",
|
31
|
+
"MLP",
|
32
|
+
"train",
|
33
|
+
]
|
28
34
|
|
29
35
|
DefaultDevice = torch.device("cpu")
|
30
36
|
|