Skip to content

Commit 11a0ba0

Browse files
author
Hiroaki Imoto
authored
Merge pull request #278 from biomass-dev/dev
Release v0.14.0
2 parents 5bee97f + c74f7a2 commit 11a0ba0

40 files changed

Lines changed: 1221 additions & 72 deletions

biomass/construction/reaction_rules.py

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,13 +1811,17 @@ def user_defined(self, line_num: int, line: str) -> None:
18111811
Examples
18121812
--------
18131813
>>> '@rxn Reactant --> Product: define rate equation here'
1814+
>>> '@rxn A --> 0: p[Vdeg] * u[A] * u[B] / (p[Kdeg] + u[A])'
1815+
>>> '@rxn E + S --> E + P: p[Vmax] * u[E] * u[S] / (p[Km] + u[S])'
18141816
18151817
Notes
18161818
-----
18171819
* Use p[xxx] and u[xxx] for describing parameters and species, respectively.
18181820
18191821
* Use '0' or '∅' for degradation/creation to/from nothing.
18201822
1823+
* Multiple reactants and products can be described by using '+'.
1824+
18211825
* Differential equation
18221826
.. math::
18231827
@@ -1833,52 +1837,79 @@ def user_defined(self, line_num: int, line: str) -> None:
18331837
rate_equation = description[1].strip()
18341838
for arrow in self.fwd_arrows:
18351839
if arrow in balance:
1836-
reactant, product = balance.split(arrow)
1837-
if reactant in self.nothing:
1838-
self._set_species(product.strip())
1839-
elif product in self.nothing:
1840-
self._set_species(product.strip())
1840+
two_species = balance.split(arrow)
1841+
# reactant, product = balance.split(arrow)
1842+
if len(two_species[0].split(" ")) > 1 and "+" not in two_species[0]:
1843+
reactants = None
1844+
else:
1845+
reactants = [
1846+
reactant
1847+
for reactant in two_species[0].replace(" ", "").split("+")
1848+
if reactant not in self.nothing
1849+
]
1850+
if len(two_species[1].split(" ")) > 1 and "+" not in two_species[1]:
1851+
products = None
18411852
else:
1842-
self._set_species(reactant.strip(), product.strip())
1853+
products = [
1854+
product
1855+
for product in two_species[1].replace(" ", "").split("+")
1856+
if product not in self.nothing
1857+
]
18431858
break
18441859
else:
18451860
raise ArrowError(f"line{line_num:d}: Use one of {', '.join(self.fwd_arrows)}.")
1861+
if reactants is None or products is None:
1862+
raise DetectionError(f"Unregistered words in line{line_num:d}: {line}")
1863+
if len(reactants) == 0:
1864+
self._set_species(*products)
1865+
elif len(products) == 0:
1866+
self._set_species(*reactants)
1867+
else:
1868+
self._set_species(*reactants, *products)
18461869
rate_equation = (
18471870
rate_equation.replace("p[", "x[C.").replace("u[", "y[V.").replace("^", "**")
18481871
)
18491872
self.reactions.append(f"v[{line_num:d}] = " + rate_equation.strip())
1873+
modulators = set(reactants) & set(products)
1874+
for modulator in modulators:
1875+
reactants.remove(modulator)
1876+
products.remove(modulator)
18501877
modulators = (
18511878
*list(
18521879
set(
18531880
[
18541881
ent
18551882
for ent in re.findall(r"(?<=\[V.)(.+?)(?=\])", rate_equation)
1856-
if ent not in [reactant, product]
1883+
if ent not in [*reactants, *products]
18571884
]
18581885
)
1886+
| modulators
18591887
),
18601888
)
18611889
self.kinetics.append(
18621890
KineticInfo(
1863-
() if reactant in self.nothing else (reactant,),
1864-
() if product in self.nothing else (product,),
1865-
() if modulators is None else (modulators),
1891+
tuple(reactants),
1892+
tuple(products),
1893+
tuple(modulators),
18661894
rate_equation.replace("x[C.", "").replace("y[V.", "").replace("]", ""),
18671895
)
18681896
)
1869-
counter_reactant = 0
1870-
counter_product = 0
1871-
for i, eq in enumerate(self.differential_equations):
1872-
if f"dydt[V.{reactant}]" in eq and reactant not in self.nothing:
1873-
counter_reactant += 1
1874-
self.differential_equations[i] = eq + f" - v[{line_num:d}]"
1875-
elif f"dydt[V.{product}]" in eq and product not in self.nothing:
1876-
counter_product += 1
1877-
self.differential_equations[i] = eq + f" + v[{line_num:d}]"
1878-
if counter_reactant == 0 and reactant not in self.nothing:
1879-
self.differential_equations.append(f"dydt[V.{reactant}] = - v[{line_num:d}]")
1880-
if counter_product == 0 and product not in self.nothing:
1881-
self.differential_equations.append(f"dydt[V.{product}] = + v[{line_num:d}]")
1897+
for reactant in reactants:
1898+
counter_reactant = 0
1899+
for i, eq in enumerate(self.differential_equations):
1900+
if f"dydt[V.{reactant}]" in eq:
1901+
counter_reactant += 1
1902+
self.differential_equations[i] = eq + f" - v[{line_num:d}]"
1903+
if counter_reactant == 0:
1904+
self.differential_equations.append(f"dydt[V.{reactant}] = - v[{line_num:d}]")
1905+
for product in products:
1906+
counter_product = 0
1907+
for i, eq in enumerate(self.differential_equations):
1908+
if f"dydt[V.{product}]" in eq:
1909+
counter_product += 1
1910+
self.differential_equations[i] = eq + f" + v[{line_num:d}]"
1911+
if counter_product == 0:
1912+
self.differential_equations.append(f"dydt[V.{product}] = + v[{line_num:d}]")
18821913

18831914
def _extract_event(self, line_num: int, line: str):
18841915
# About biochemical event

biomass/construction/template/observable.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import Dict, List
22

33
import numpy as np
44

@@ -71,7 +71,7 @@ def simulate(self, x: list, y0: list, _perturbation: dict = {}):
7171
def set_data(self) -> None:
7272
pass
7373

74-
def get_timepoint(self, obs_name: str) -> List[int]:
74+
def get_timepoint(self, obs_name: str) -> Dict[str, List[int]]:
7575
if obs_name in self.obs_names:
76-
return []
76+
return {condition: [] for condition in self.conditions}
7777
assert False

biomass/construction/template/problem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _diff_sim_and_exp(sim_matrix, exp_dict, exp_timepoint, conditions, sim_norm_
3636

3737
for idx, condition in enumerate(conditions):
3838
if condition in exp_dict.keys():
39-
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint))])
39+
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint[condition]))])
4040
exp_val.extend(exp_dict[condition])
4141

4242
return np.array(sim_val) / sim_norm_max, np.array(exp_val)

biomass/dynamics/temporal_dynamics.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from dataclasses import dataclass
33
from math import isnan
4-
from typing import List, Optional
4+
from typing import Dict, List, Optional
55

66
import numpy as np
77
from matplotlib import pyplot as plt
@@ -93,14 +93,14 @@ def plot_timecourse(
9393
if (
9494
viz_type == "experiment" or singleplotting[i].exp_data
9595
) and self.model.problem.experiments[i] is not None:
96-
exp_t = self.model.problem.get_timepoint(obs_name)
96+
exp_t_dict = self.model.problem.get_timepoint(obs_name)
9797
if self.model.problem.error_bars[i] is not None:
9898
self._plot_experimental_data_with_error_bars(
99-
viz_type, exp_t, obs_name, mode, singleplotting, multiplotting
99+
viz_type, exp_t_dict, obs_name, mode, singleplotting, multiplotting
100100
)
101101
else:
102102
self._plot_experimental_data_without_error_bars(
103-
viz_type, exp_t, obs_name, mode, singleplotting, multiplotting
103+
viz_type, exp_t_dict, obs_name, mode, singleplotting, multiplotting
104104
)
105105
if mode == 0:
106106
self._save_mode_0(obs_name, singleplotting, viz_type)
@@ -360,7 +360,7 @@ def _plot_simulations(
360360
def _plot_experimental_data_with_error_bars(
361361
self,
362362
viz_type: str,
363-
exp_t: Optional[List[int]],
363+
exp_t_dict: Optional[Dict[str, List[int]]],
364364
obs_name: str,
365365
mode: int,
366366
singleplotting: List[SingleObservable],
@@ -377,6 +377,7 @@ def _plot_experimental_data_with_error_bars(
377377
or (mode == 1 and condition == multiplotting.condition)
378378
):
379379
try:
380+
exp_t = exp_t_dict[condition]
380381
exp_data = plt.errorbar(
381382
np.array(exp_t) / singleplotting[i].divided_by,
382383
self.model.problem.experiments[i][condition],
@@ -421,7 +422,7 @@ def _plot_experimental_data_with_error_bars(
421422
def _plot_experimental_data_without_error_bars(
422423
self,
423424
viz_type: str,
424-
exp_t: Optional[List[int]],
425+
exp_t_dict: Optional[Dict[str, List[int]]],
425426
obs_name: str,
426427
mode: int,
427428
singleplotting: List[SingleObservable],
@@ -438,6 +439,7 @@ def _plot_experimental_data_without_error_bars(
438439
or (mode == 1 and condition == multiplotting.condition)
439440
):
440441
try:
442+
exp_t = exp_t_dict[condition]
441443
plt.plot(
442444
np.array(exp_t) / singleplotting[i].divided_by,
443445
self.model.problem.experiments[i][condition],

biomass/models/Nakakuki_Cell_2010/observable.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import Dict, List
22

33
import numpy as np
44

@@ -201,19 +201,23 @@ def set_data(self):
201201
"HRG": [sd / np.sqrt(3) for sd in [0.027, 0.059, 0.094, 0.124, 0.113, 0.108]],
202202
}
203203

204-
@staticmethod
205-
def get_timepoint(obs_name) -> List[int]:
204+
def get_timepoint(self, obs_name) -> Dict[str, List[int]]:
206205
if obs_name in [
207206
"Phosphorylated_MEKc",
208207
"Phosphorylated_ERKc",
209208
"Phosphorylated_RSKw",
210209
"Phosphorylated_cFos",
211210
]:
212-
return [0, 300, 600, 900, 1800, 2700, 3600, 5400] # (Unit: sec.)
211+
return {
212+
condition: [0, 300, 600, 900, 1800, 2700, 3600, 5400] # (Unit: sec.)
213+
for condition in self.conditions
214+
}
213215
elif obs_name == "Phosphorylated_CREBw":
214-
return [0, 600, 1800, 3600, 5400]
216+
return {condition: [0, 600, 1800, 3600, 5400] for condition in self.conditions}
215217
elif obs_name == "cfos_mRNA":
216-
return [0, 600, 1200, 1800, 2700, 3600, 5400]
218+
return {
219+
condition: [0, 600, 1200, 1800, 2700, 3600, 5400] for condition in self.conditions
220+
}
217221
elif obs_name in ["cFos_Protein", "dusp_mRNA"]:
218-
return [0, 900, 1800, 2700, 3600, 5400]
222+
return {condition: [0, 900, 1800, 2700, 3600, 5400] for condition in self.conditions}
219223
assert False

biomass/models/Nakakuki_Cell_2010/problem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _diff_sim_and_exp(sim_matrix, exp_dict, exp_timepoint, conditions, sim_norm_
3636

3737
for idx, condition in enumerate(conditions):
3838
if condition in exp_dict.keys():
39-
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint))])
39+
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint[condition]))])
4040
exp_val.extend(exp_dict[condition])
4141

4242
return np.array(sim_val) / sim_norm_max, np.array(exp_val)

biomass/models/_copy.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def copy_to_current(
1919
"tgfb_smad",
2020
"g1s_transition",
2121
"prolif_quies",
22+
"jak_stat_pathway",
2223
]
2324
) -> None:
2425
"""Copy an example model to the current working directory.
@@ -46,6 +47,7 @@ def copy_to_current(
4647
"tgfb_smad",
4748
"g1s_transition",
4849
"prolif_quies",
50+
"jak_stat_pathway",
4951
]
5052
):
5153
raise ValueError(f"model_name must be one of [{', '.join(available_models)}].")

biomass/models/circadian_clock/observable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@ def set_data(self):
7979

8080
def get_timepoint(self, obs_name):
8181
if obs_name in self.obs_names:
82-
return []
82+
return {}
8383
assert False

biomass/models/circadian_clock/problem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _diff_sim_and_exp(sim_matrix, exp_dict, exp_timepoint, conditions, sim_norm_
3636

3737
for idx, condition in enumerate(conditions):
3838
if condition in exp_dict.keys():
39-
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint))])
39+
sim_val.extend(sim_matrix[idx, list(map(int, exp_timepoint[condition]))])
4040
exp_val.extend(exp_dict[condition])
4141

4242
return np.array(sim_val) / sim_norm_max, np.array(exp_val)

biomass/models/g1s_transition/observable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,5 @@ def set_data(self):
8282

8383
def get_timepoint(self, obs_name):
8484
if obs_name in self.obs_names:
85-
return []
85+
return {}
8686
assert False

0 commit comments

Comments
 (0)