mxlpy 0.20.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/__init__.py CHANGED
@@ -52,6 +52,7 @@ from . import (
52
52
  plot,
53
53
  report,
54
54
  sbml,
55
+ units,
55
56
  )
56
57
  from .integrators import DefaultIntegrator, Scipy
57
58
  from .label_map import LabelMapper
@@ -61,7 +62,7 @@ from .model import Model
61
62
  from .scan import steady_state, time_course, time_course_over_protocol
62
63
  from .simulator import Simulator
63
64
  from .symbolic import SymbolicModel, to_symbolic_model
64
- from .types import Derived, IntegratorProtocol, unwrap
65
+ from .types import Derived, IntegratorProtocol, Parameter, Variable, unwrap
65
66
 
66
67
  with contextlib.suppress(ImportError):
67
68
  from .integrators import Assimulo
@@ -87,9 +88,11 @@ __all__ = [
87
88
  "LabelMapper",
88
89
  "LinearLabelMapper",
89
90
  "Model",
91
+ "Parameter",
90
92
  "Scipy",
91
93
  "Simulator",
92
94
  "SymbolicModel",
95
+ "Variable",
93
96
  "cartesian_product",
94
97
  "compare",
95
98
  "distributions",
@@ -110,6 +113,7 @@ __all__ = [
110
113
  "time_course",
111
114
  "time_course_over_protocol",
112
115
  "to_symbolic_model",
116
+ "units",
113
117
  "unwrap",
114
118
  ]
115
119
 
mxlpy/carousel.py ADDED
@@ -0,0 +1,166 @@
1
+ """Reaction carousel."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import itertools as it
6
+ from copy import deepcopy
7
+ from dataclasses import dataclass, field
8
+ from functools import partial
9
+ from typing import TYPE_CHECKING
10
+
11
+ import pandas as pd
12
+
13
+ from mxlpy import parallel, scan
14
+
15
+ __all__ = ["Carousel", "CarouselSteadyState", "CarouselTimeCourse", "ReactionTemplate"]
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import Iterable, Mapping
19
+
20
+ from mxlpy import Model
21
+ from mxlpy.types import Array, IntegratorType, RateFn
22
+
23
+
24
+ @dataclass
25
+ class ReactionTemplate:
26
+ """Template for a reaction in a model."""
27
+
28
+ fn: RateFn
29
+ args: list[str]
30
+ additional_parameters: dict[str, float] = field(default_factory=dict)
31
+
32
+
33
+ @dataclass
34
+ class CarouselSteadyState:
35
+ """Time course of a carousel simulation."""
36
+
37
+ carousel: list[Model]
38
+ results: list[scan.TimePoint]
39
+
40
+ def get_variables_by_model(self) -> pd.DataFrame:
41
+ """Get the variables of the time course results, indexed by model."""
42
+ return pd.DataFrame({i: r.variables for i, r in enumerate(self.results)}).T
43
+
44
+
45
+ @dataclass
46
+ class CarouselTimeCourse:
47
+ """Time course of a carousel simulation."""
48
+
49
+ carousel: list[Model]
50
+ results: list[scan.TimeCourse]
51
+
52
+ def get_variables_by_model(self) -> pd.DataFrame:
53
+ """Get the variables of the time course results, indexed by model."""
54
+ return pd.concat({i: r.variables for i, r in enumerate(self.results)})
55
+
56
+
57
+ def _dict_product[T1, T2](d: Mapping[T1, Iterable[T2]]) -> Iterable[dict[T1, T2]]:
58
+ yield from (dict(zip(d.keys(), x, strict=True)) for x in it.product(*d.values()))
59
+
60
+
61
+ def _make_reaction_carousel(
62
+ model: Model, rxns: dict[str, list[ReactionTemplate]]
63
+ ) -> Iterable[Model]:
64
+ for d in _dict_product(rxns):
65
+ new = deepcopy(model)
66
+ for rxn, template in d.items():
67
+ new.add_parameters(template.additional_parameters)
68
+ new.update_reaction(name=rxn, fn=template.fn, args=template.args)
69
+ yield new
70
+
71
+
72
+ class Carousel:
73
+ """A carousel of models with different reaction templates."""
74
+
75
+ variants: list[Model]
76
+
77
+ def __init__(
78
+ self,
79
+ model: Model,
80
+ variants: dict[str, list[ReactionTemplate]],
81
+ ) -> None:
82
+ """Initialize the carousel with a model and reaction templates."""
83
+ self.variants = list(
84
+ _make_reaction_carousel(
85
+ model=model,
86
+ rxns=variants,
87
+ )
88
+ )
89
+
90
+ def time_course(
91
+ self,
92
+ time_points: Array,
93
+ *,
94
+ y0: dict[str, float] | None = None,
95
+ integrator: IntegratorType | None = None,
96
+ ) -> CarouselTimeCourse:
97
+ """Simulate the carousel of models over a time course."""
98
+ results = [
99
+ i[1]
100
+ for i in parallel.parallelise(
101
+ partial(
102
+ scan._time_course_worker, # noqa: SLF001
103
+ time_points=time_points,
104
+ integrator=integrator,
105
+ y0=y0,
106
+ ),
107
+ list(enumerate(self.variants)),
108
+ )
109
+ ]
110
+
111
+ return CarouselTimeCourse(
112
+ carousel=self.variants,
113
+ results=results,
114
+ )
115
+
116
+ def protocol_time_course(
117
+ self,
118
+ protocol: pd.DataFrame,
119
+ *,
120
+ y0: dict[str, float] | None = None,
121
+ integrator: IntegratorType | None = None,
122
+ ) -> CarouselTimeCourse:
123
+ """Simulate the carousel of models over a protocol time course."""
124
+ results = [
125
+ i[1]
126
+ for i in parallel.parallelise(
127
+ partial(
128
+ scan._protocol_worker, # noqa: SLF001
129
+ protocol=protocol,
130
+ integrator=integrator,
131
+ y0=y0,
132
+ ),
133
+ list(enumerate(self.variants)),
134
+ )
135
+ ]
136
+
137
+ return CarouselTimeCourse(
138
+ carousel=self.variants,
139
+ results=results,
140
+ )
141
+
142
+ def steady_state(
143
+ self,
144
+ *,
145
+ y0: dict[str, float] | None = None,
146
+ integrator: IntegratorType | None = None,
147
+ rel_norm: bool = False,
148
+ ) -> CarouselSteadyState:
149
+ """Simulate the carousel of models over a time course."""
150
+ results = [
151
+ i[1]
152
+ for i in parallel.parallelise(
153
+ partial(
154
+ scan._steady_state_worker, # noqa: SLF001
155
+ integrator=integrator,
156
+ rel_norm=rel_norm,
157
+ y0=y0,
158
+ ),
159
+ list(enumerate(self.variants)),
160
+ )
161
+ ]
162
+
163
+ return CarouselSteadyState(
164
+ carousel=self.variants,
165
+ results=results,
166
+ )
mxlpy/compare.py CHANGED
@@ -230,11 +230,7 @@ def protocol_time_courses(
230
230
  ) -> ProtocolComparison:
231
231
  """Compare the time courses of two models."""
232
232
  return ProtocolComparison(
233
- res1=unwrap(
234
- Simulator(m1).simulate_over_protocol(protocol=protocol).get_result()
235
- ),
236
- res2=unwrap(
237
- Simulator(m2).simulate_over_protocol(protocol=protocol).get_result()
238
- ),
233
+ res1=unwrap(Simulator(m1).simulate_protocol(protocol=protocol).get_result()),
234
+ res2=unwrap(Simulator(m2).simulate_protocol(protocol=protocol).get_result()),
239
235
  protocol=protocol,
240
236
  )
@@ -187,7 +187,7 @@ def model_diff(m1: Model, m2: Model) -> ModelDiff:
187
187
  if (v2 := m2._parameters.get(k)) is None: # noqa: SLF001
188
188
  diff.missing_parameters.add(k)
189
189
  elif v1 != v2:
190
- diff.different_parameters[k] = (v1, v2)
190
+ diff.different_parameters[k] = (v1, v2) # type: ignore
191
191
 
192
192
  for k, v1 in m1._variables.items(): # noqa: SLF001
193
193
  if (v2 := m2._variables.get(k)) is None: # noqa: SLF001