cobrak 0.0.1__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.
- cobrak/__init__.py +100 -0
- cobrak/_efms.py +189 -0
- cobrak/bigg_metabolites_functionality.py +76 -0
- cobrak/brenda_functionality.py +833 -0
- cobrak/cobrapy_model_functionality.py +153 -0
- cobrak/constants.py +162 -0
- cobrak/dataclasses.py +319 -0
- cobrak/equilibrator_functionality.py +249 -0
- cobrak/evolution.py +996 -0
- cobrak/example_models.py +171 -0
- cobrak/expasy_functionality.py +30 -0
- cobrak/genetic.py +216 -0
- cobrak/io.py +1002 -0
- cobrak/lps.py +1857 -0
- cobrak/model_instantiation.py +574 -0
- cobrak/ncbi_taxonomy_functionality.py +202 -0
- cobrak/nlps.py +1090 -0
- cobrak/plotting.py +814 -0
- cobrak/printing.py +799 -0
- cobrak/pso_parallel.py +262 -0
- cobrak/pyomo_functionality.py +274 -0
- cobrak/sabio_rk_functionality.py +779 -0
- cobrak/spreadsheet_functionality.py +1363 -0
- cobrak/standard_solvers.py +104 -0
- cobrak/tellurium_functionality.py +299 -0
- cobrak/uniprot_functionality.py +150 -0
- cobrak/utilities.py +2284 -0
- cobrak-0.0.1.dist-info/METADATA +283 -0
- cobrak-0.0.1.dist-info/RECORD +32 -0
- cobrak-0.0.1.dist-info/WHEEL +5 -0
- cobrak-0.0.1.dist-info/licenses/LICENSE +201 -0
- cobrak-0.0.1.dist-info/top_level.txt +1 -0
cobrak/__init__.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
COBRAk is a general COBRA/COBRA-k suite written as Python package. For more about it, visit its repository:
|
|
3
|
+
https://github.com/klamt-lab/COBRAk
|
|
4
|
+
|
|
5
|
+
The __init__ file of COBRAk initializes the rich text output & tracebacks as well as its logger. Furthermore, graceful
|
|
6
|
+
shutdown of user-induced shutdowns is enabled.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import signal
|
|
11
|
+
import sys
|
|
12
|
+
from types import FrameType
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from rich.logging import RichHandler
|
|
17
|
+
from rich.traceback import install
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# PUBLIC FUNCTIONS SECTION #
|
|
21
|
+
def exit_signal_handler(sig: int, frame: FrameType | None) -> None:
|
|
22
|
+
"""Handles the exit signal by printing a shutdown message and exiting the program.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
sig (int): The signal number.
|
|
26
|
+
frame (Optional[FrameType]): The current frame.
|
|
27
|
+
"""
|
|
28
|
+
print(
|
|
29
|
+
"COBRAk received user signal to terminate (this message may appear multiple times in parallelized contexts). Shutting down..."
|
|
30
|
+
)
|
|
31
|
+
sys.exit(0)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def setup_rich_tracebacks(show_locals: bool) -> None:
|
|
35
|
+
"""Sets up rich tracebacks with the given options.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
show_locals (bool): Whether to show local variables in the traceback.
|
|
39
|
+
"""
|
|
40
|
+
install(show_locals=show_locals)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def set_logging_level(level: int) -> None:
|
|
44
|
+
"""Sets the logging level.
|
|
45
|
+
|
|
46
|
+
E.g. INFO, ERROR, WARNING and CRITICAL from Python's logging module.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
level (int): The logging level.
|
|
50
|
+
"""
|
|
51
|
+
logger.setLevel(level)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def set_logging_handler(
|
|
55
|
+
show_path: bool = False,
|
|
56
|
+
show_time: bool = False,
|
|
57
|
+
show_level: bool = True,
|
|
58
|
+
keywords: list[str] = [
|
|
59
|
+
"info",
|
|
60
|
+
"warning",
|
|
61
|
+
"error",
|
|
62
|
+
"critical",
|
|
63
|
+
],
|
|
64
|
+
**args: Any,
|
|
65
|
+
) -> RichHandler:
|
|
66
|
+
"""
|
|
67
|
+
Sets up the logging handler with the given options.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
show_path (bool, optional): Whether to show the path. Defaults to False.
|
|
71
|
+
show_time (bool, optional): Whether to show the time. Defaults to False.
|
|
72
|
+
show_level (bool, optional): Whether to show the level. Defaults to True.
|
|
73
|
+
keywords (Dict[str, str], optional): The keywords to highlight. Defaults to ["info", "warning", "error", "critical"]
|
|
74
|
+
**args (Any, optional): Additional Rich handler arguments.
|
|
75
|
+
"""
|
|
76
|
+
return RichHandler(
|
|
77
|
+
show_path=show_path,
|
|
78
|
+
show_time=show_time,
|
|
79
|
+
show_level=show_level,
|
|
80
|
+
keywords=keywords,
|
|
81
|
+
**args,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ACTIVE SCRIPT SECTION #
|
|
86
|
+
signal.signal(signal.SIGINT, exit_signal_handler)
|
|
87
|
+
signal.signal(signal.SIGTERM, exit_signal_handler)
|
|
88
|
+
|
|
89
|
+
setup_rich_tracebacks(False)
|
|
90
|
+
|
|
91
|
+
# Create a logger
|
|
92
|
+
logger = logging.getLogger(__name__)
|
|
93
|
+
|
|
94
|
+
# Set the logging level
|
|
95
|
+
set_logging_level(logging.INFO)
|
|
96
|
+
|
|
97
|
+
# Add the handler to the logger
|
|
98
|
+
logger.addHandler(set_logging_handler())
|
|
99
|
+
|
|
100
|
+
console = Console()
|
cobrak/_efms.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from random import choice
|
|
3
|
+
|
|
4
|
+
from joblib import Parallel, cpu_count, delayed
|
|
5
|
+
from pyomo.environ import (
|
|
6
|
+
Binary,
|
|
7
|
+
ConcreteModel,
|
|
8
|
+
Constraint,
|
|
9
|
+
Objective,
|
|
10
|
+
Reals,
|
|
11
|
+
SolverFactoryClass,
|
|
12
|
+
Var,
|
|
13
|
+
maximize,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .constants import BIG_M, STANDARD_MIN_MDF
|
|
17
|
+
from .dataclasses import Model, Solver
|
|
18
|
+
from .lps import (
|
|
19
|
+
get_lp_from_cobrak_model,
|
|
20
|
+
get_objective,
|
|
21
|
+
)
|
|
22
|
+
from .pyomo_functionality import get_solver
|
|
23
|
+
from .standard_solvers import SCIP
|
|
24
|
+
from .utilities import (
|
|
25
|
+
delete_orphaned_metabolites_and_enzymes,
|
|
26
|
+
delete_unused_reactions_in_variability_dict,
|
|
27
|
+
get_stoichiometrically_coupled_reactions,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
STANDARD_EFM_OBJ_NAME = "EFM_STANDARD_OBJECTIVE"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _efm_batch_routine(
|
|
34
|
+
pyomo_model: ConcreteModel,
|
|
35
|
+
pyomo_solver: SolverFactoryClass,
|
|
36
|
+
objective_name: str,
|
|
37
|
+
cobrak_solver: Solver,
|
|
38
|
+
objective_sense: int,
|
|
39
|
+
) -> None:
|
|
40
|
+
getattr(pyomo_model, objective_name).activate()
|
|
41
|
+
|
|
42
|
+
# OBJ ACTIVATE
|
|
43
|
+
pyomo_solver.solve(
|
|
44
|
+
pyomo_model, tee=True, warmstart=True, **cobrak_solver.solve_extra_options
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if results.solver.termination_condition == TerminationCondition.maxTimeLimit:
|
|
48
|
+
result = None
|
|
49
|
+
else:
|
|
50
|
+
result = value(getattr(model, target_id))
|
|
51
|
+
|
|
52
|
+
getattr(pyomo_model, objective_name).deactivate()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def calculate_efms(
|
|
56
|
+
cobrak_model: Model,
|
|
57
|
+
with_inhomogenous_constraints: bool = False,
|
|
58
|
+
with_enzyme_constraints: bool = False,
|
|
59
|
+
with_thermodynamic_constraints: bool = False,
|
|
60
|
+
solver: Solver = SCIP,
|
|
61
|
+
variability_dict: dict[str, tuple[float, float]] = {},
|
|
62
|
+
max_num_efms: int = 1_000_000_000,
|
|
63
|
+
min_mdf: float = STANDARD_MIN_MDF,
|
|
64
|
+
min_efm_flux_bound: float = 1e-5,
|
|
65
|
+
) -> set[tuple[str, ...]]:
|
|
66
|
+
enforced_reacs = [
|
|
67
|
+
reac_id
|
|
68
|
+
for reac_id in variability_dict
|
|
69
|
+
if variability_dict[reac_id][0] > 0.0 and reac_id in cobrak_model.reactions
|
|
70
|
+
]
|
|
71
|
+
if not with_inhomogenous_constraints:
|
|
72
|
+
for enforced_reac in enforced_reacs:
|
|
73
|
+
cobrak_model.reactions[enforced_reac].min_flux = 0.0
|
|
74
|
+
variability_dict[enforced_reac] = (0.0, variability_dict[enforced_reac][1])
|
|
75
|
+
|
|
76
|
+
reduced_model = deepcopy(cobrak_model)
|
|
77
|
+
if variability_dict != {}:
|
|
78
|
+
reduced_model = delete_unused_reactions_in_variability_dict(
|
|
79
|
+
reduced_model, variability_dict
|
|
80
|
+
)
|
|
81
|
+
reduced_model = delete_orphaned_metabolites_and_enzymes(reduced_model)
|
|
82
|
+
|
|
83
|
+
all_couples = get_stoichiometrically_coupled_reactions(
|
|
84
|
+
reduced_model,
|
|
85
|
+
)
|
|
86
|
+
eligible_couples: list[list[str]] = []
|
|
87
|
+
for couple in all_couples:
|
|
88
|
+
eligible = True
|
|
89
|
+
for enforced_reac in enforced_reacs:
|
|
90
|
+
if enforced_reac in couple:
|
|
91
|
+
eligible = False
|
|
92
|
+
break
|
|
93
|
+
if eligible:
|
|
94
|
+
eligible_couples.append(couple)
|
|
95
|
+
|
|
96
|
+
pyomo_model = get_lp_from_cobrak_model(
|
|
97
|
+
cobrak_model=reduced_model,
|
|
98
|
+
with_enzyme_constraints=with_enzyme_constraints,
|
|
99
|
+
with_thermodynamic_constraints=with_thermodynamic_constraints,
|
|
100
|
+
with_loop_constraints=True,
|
|
101
|
+
min_mdf=min_mdf,
|
|
102
|
+
)
|
|
103
|
+
zc_var_sum = 0.0
|
|
104
|
+
zc_var_ids: list[str] = []
|
|
105
|
+
objective_names: list[str] = []
|
|
106
|
+
for eligible_couple in eligible_couples:
|
|
107
|
+
zc_var_id = "zC_var_" + "_".join(eligible_couple)
|
|
108
|
+
zc_var_ids.append(zc_var_id)
|
|
109
|
+
|
|
110
|
+
setattr(pyomo_model, zc_var_id, Var(within=Binary))
|
|
111
|
+
setattr(
|
|
112
|
+
pyomo_model,
|
|
113
|
+
zc_var_id + "_constraint",
|
|
114
|
+
Constraint(
|
|
115
|
+
rule=getattr(pyomo_model, eligible_couple[0])
|
|
116
|
+
<= BIG_M * getattr(pyomo_model, zc_var_id)
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
setattr(
|
|
120
|
+
pyomo_model,
|
|
121
|
+
zc_var_id + "_constraint_2",
|
|
122
|
+
Constraint(
|
|
123
|
+
rule=getattr(pyomo_model, eligible_couple[0])
|
|
124
|
+
>= min_efm_flux_bound * getattr(pyomo_model, zc_var_id)
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
zc_var_sum += getattr(pyomo_model, zc_var_id)
|
|
128
|
+
|
|
129
|
+
objective_names.append(f"objective_{eligible_couple[0]}")
|
|
130
|
+
setattr(
|
|
131
|
+
pyomo_model,
|
|
132
|
+
objective_names[-1],
|
|
133
|
+
Objective(expr=getattr(pyomo_model, eligible_couple[0]), sense=maximize),
|
|
134
|
+
)
|
|
135
|
+
getattr(pyomo_model, objective_names[-1]).deactivate()
|
|
136
|
+
|
|
137
|
+
objective_names.append(f"objective_{eligible_couple[0]}")
|
|
138
|
+
setattr(
|
|
139
|
+
pyomo_model,
|
|
140
|
+
STANDARD_EFM_OBJ_NAME,
|
|
141
|
+
Objective(expr=0.0, sense=maximize),
|
|
142
|
+
)
|
|
143
|
+
getattr(pyomo_model, STANDARD_EFM_OBJ_NAME).deactivate()
|
|
144
|
+
|
|
145
|
+
setattr(pyomo_model, "zc_var_sum", Var(within=Reals, bounds=(1.0, 10_000.0)))
|
|
146
|
+
setattr(
|
|
147
|
+
pyomo_model,
|
|
148
|
+
"zc_var_sum_constraint_1",
|
|
149
|
+
Constraint(rule=getattr(pyomo_model, "zc_var_sum") == zc_var_sum),
|
|
150
|
+
)
|
|
151
|
+
pyomo_model.obj = get_objective(pyomo_model, "zc_var_sum", -1)
|
|
152
|
+
|
|
153
|
+
integer_cut_i = 0
|
|
154
|
+
pyomo_solver = get_solver(solver.name, solver.solver_options, solver.solver_attrs)
|
|
155
|
+
efms: set[tuple[str, ...]] = set()
|
|
156
|
+
for i in range(max_num_efms):
|
|
157
|
+
opt_targets = [STANDARD_EFM_OBJ_NAME] + [
|
|
158
|
+
choice(objective_names) for _ in range(cpu_count() - 1)
|
|
159
|
+
]
|
|
160
|
+
min_z_results = Parallel(n_jobs=-1, verbose=10)(
|
|
161
|
+
delayed(_efm_batch_routine)(
|
|
162
|
+
pyomo_model,
|
|
163
|
+
pyomo_solver,
|
|
164
|
+
opt_target,
|
|
165
|
+
+1,
|
|
166
|
+
)
|
|
167
|
+
for opt_target in opt_targets
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
new_efms = min_z_results.difference(efms)
|
|
171
|
+
if len(new_efms) == 0:
|
|
172
|
+
break
|
|
173
|
+
for new_efm in new_efms:
|
|
174
|
+
new_integer_cut = 0.0
|
|
175
|
+
new_integer_cut_sum = 0.0
|
|
176
|
+
for zc_var_id in zc_var_ids:
|
|
177
|
+
if getattr(pyomo_model, zc_var_id).value <= 0.1:
|
|
178
|
+
continue
|
|
179
|
+
new_integer_cut += getattr(pyomo_model, zc_var_id)
|
|
180
|
+
new_integer_cut_sum += 1
|
|
181
|
+
setattr(
|
|
182
|
+
pyomo_model,
|
|
183
|
+
f"integer_cut_{integer_cut_i}",
|
|
184
|
+
Constraint(expr=new_integer_cut <= new_integer_cut_sum - 1),
|
|
185
|
+
)
|
|
186
|
+
integer_cut_i += 1
|
|
187
|
+
efms = efms.union(new_efms)
|
|
188
|
+
|
|
189
|
+
return efms
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""bigg_parse_metabolites_file.py
|
|
2
|
+
|
|
3
|
+
This module contains a function which transforms a BIGG metabolites .txt list
|
|
4
|
+
into an machine-readable JSON.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# IMPORTS SECTION #
|
|
8
|
+
from .io import json_write
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# PUBLIC FUNCTIONS SECTION #
|
|
12
|
+
def bigg_parse_metabolites_file(
|
|
13
|
+
bigg_metabolites_txt_path: str,
|
|
14
|
+
bigg_metabolites_json_path: str,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Parses a BIGG metabolites text file and returns a dictionary for this file.
|
|
17
|
+
|
|
18
|
+
As of Sep 14 2024, a BIGG metabolites list of all BIGG-included metabolites
|
|
19
|
+
is retrievable under http://bigg.ucsd.edu/data_access
|
|
20
|
+
|
|
21
|
+
Arguments
|
|
22
|
+
----------
|
|
23
|
+
* bigg_metabolites_file_path: str ~ The file path to the BIGG metabolites file.
|
|
24
|
+
The usual file name (which has to be included too in this argument) is
|
|
25
|
+
bigg_models_metabolites.txt
|
|
26
|
+
* output_folder: str ~ The folder in which the JSON including the parsed BIGG
|
|
27
|
+
metabolites file data is stored with the name 'bigg_id_name_mapping.json'
|
|
28
|
+
|
|
29
|
+
Output
|
|
30
|
+
----------
|
|
31
|
+
* A JSON file with the name 'bigg_id_name_mapping.json' in the given output folder,
|
|
32
|
+
with the following structure:
|
|
33
|
+
<pre>
|
|
34
|
+
{
|
|
35
|
+
"$BIGG_ID": "$CHEMICAL_OR_USUAL_NAME",
|
|
36
|
+
(...),
|
|
37
|
+
"$BIGG_ID": "$BIGG_ID",
|
|
38
|
+
(...),
|
|
39
|
+
}
|
|
40
|
+
</pre>
|
|
41
|
+
The BIGG ID <-> BIGG ID mapping is done for models which already use the BIGG IDs.
|
|
42
|
+
"""
|
|
43
|
+
# Open the BIGG metabolites file as string list, and remove all newlines
|
|
44
|
+
with open(bigg_metabolites_txt_path, encoding="utf-8") as f:
|
|
45
|
+
lines = f.readlines()
|
|
46
|
+
lines = [x.replace("\n", "") for x in lines if len(x) > 0]
|
|
47
|
+
|
|
48
|
+
# Mapping variable which will store the BIGG ID<->
|
|
49
|
+
bigg_id_name_mapping = {}
|
|
50
|
+
# Go through each BIGG metabolites file line (which is a tab-separated file)
|
|
51
|
+
# and retrieve the BIGG ID and the name (if there is a name for the given BIGG
|
|
52
|
+
# ID)
|
|
53
|
+
for line in lines:
|
|
54
|
+
bigg_id = line.split("\t")[1]
|
|
55
|
+
bigg_id_name_mapping[bigg_id] = bigg_id
|
|
56
|
+
|
|
57
|
+
# Exception to check if there is no name :O
|
|
58
|
+
try:
|
|
59
|
+
name = line.split("\t")[2].lower()
|
|
60
|
+
except Exception:
|
|
61
|
+
continue
|
|
62
|
+
bigg_id_name_mapping[name] = bigg_id
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
database_links = line.split("\t")[4]
|
|
66
|
+
except Exception:
|
|
67
|
+
continue
|
|
68
|
+
for database_link_part in database_links.split(": "):
|
|
69
|
+
if "CHEBI:" not in database_link_part:
|
|
70
|
+
continue
|
|
71
|
+
subpart = database_link_part.split("CHEBI:")[1].strip()
|
|
72
|
+
chebi_id = subpart.split("; ")[0] if "; " in subpart else subpart
|
|
73
|
+
bigg_id_name_mapping[chebi_id] = bigg_id
|
|
74
|
+
|
|
75
|
+
# Write the JSON in the given folder :D
|
|
76
|
+
json_write(bigg_metabolites_json_path, bigg_id_name_mapping)
|