seed2lp 2.0.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.
- seed2lp/__init__.py +12 -0
- seed2lp/__main__.py +837 -0
- seed2lp/_version.py +2 -0
- seed2lp/argument.py +717 -0
- seed2lp/asp/atom_for_transfers.lp +7 -0
- seed2lp/asp/community_heuristic.lp +3 -0
- seed2lp/asp/community_search.lp +14 -0
- seed2lp/asp/constraints_targets.lp +15 -0
- seed2lp/asp/definition_atoms.lp +87 -0
- seed2lp/asp/enum-cc.lp +50 -0
- seed2lp/asp/flux.lp +70 -0
- seed2lp/asp/limit_transfers.lp +9 -0
- seed2lp/asp/maximize_flux.lp +2 -0
- seed2lp/asp/maximize_produced_target.lp +7 -0
- seed2lp/asp/minimize.lp +8 -0
- seed2lp/asp/seed-solving.lp +116 -0
- seed2lp/asp/seed_external.lp +1 -0
- seed2lp/asp/show_seeds.lp +2 -0
- seed2lp/asp/show_tranfers.lp +1 -0
- seed2lp/asp/test.lp +61 -0
- seed2lp/clingo_lpx.py +236 -0
- seed2lp/color.py +34 -0
- seed2lp/config.yaml +56 -0
- seed2lp/description.py +424 -0
- seed2lp/file.py +151 -0
- seed2lp/flux.py +365 -0
- seed2lp/linear.py +431 -0
- seed2lp/log_conf.yaml +25 -0
- seed2lp/logger.py +112 -0
- seed2lp/metabolite.py +46 -0
- seed2lp/network.py +1921 -0
- seed2lp/reaction.py +207 -0
- seed2lp/reasoning.py +459 -0
- seed2lp/reasoningcom.py +753 -0
- seed2lp/reasoninghybrid.py +791 -0
- seed2lp/resmod.py +74 -0
- seed2lp/sbml.py +307 -0
- seed2lp/scope.py +124 -0
- seed2lp/solver.py +333 -0
- seed2lp/temp_flux_com.py +74 -0
- seed2lp/utils.py +237 -0
- seed2lp-2.0.0.dist-info/METADATA +404 -0
- seed2lp-2.0.0.dist-info/RECORD +53 -0
- seed2lp-2.0.0.dist-info/WHEEL +5 -0
- seed2lp-2.0.0.dist-info/entry_points.txt +2 -0
- seed2lp-2.0.0.dist-info/licenses/LICENCE.txt +145 -0
- seed2lp-2.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/fba.py +147 -0
- tests/full_network.py +166 -0
- tests/normalization.py +188 -0
- tests/target.py +286 -0
- tests/utils.py +181 -0
seed2lp/flux.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import cobra
|
|
2
|
+
from re import sub
|
|
3
|
+
from cobra.core import Model
|
|
4
|
+
import warnings
|
|
5
|
+
from . import logger, color
|
|
6
|
+
|
|
7
|
+
def get_model(model_file:str):
|
|
8
|
+
"""Get cobra model
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
model_file (str): Sbml file path of the network
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
Model: The model generated from cobra
|
|
15
|
+
"""
|
|
16
|
+
model = cobra.io.read_sbml_model(model_file)
|
|
17
|
+
return model
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_list_fluxes(model:Model, list_objective:list, show_messages:bool=True):
|
|
21
|
+
"""Get the objective reactions fluxe.
|
|
22
|
+
Can generate the flux for multiple objective reactions
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
model (Model): Cobra model
|
|
26
|
+
list_objective (list): List of objective reaction names
|
|
27
|
+
show_messages (bool, optional): Write messages into console if True. Default True.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
dict: Dictionnary of objective reaction and their respective fluxes
|
|
31
|
+
"""
|
|
32
|
+
warnings.filterwarnings("error")
|
|
33
|
+
fluxes_dict = dict()
|
|
34
|
+
for objective_reaction in list_objective:
|
|
35
|
+
objective_reaction = remove_prefix_reaction(objective_reaction)
|
|
36
|
+
# get flux of objective reaction
|
|
37
|
+
model.objective = objective_reaction
|
|
38
|
+
try:
|
|
39
|
+
objective_flux = model.optimize().fluxes[objective_reaction]
|
|
40
|
+
except UserWarning:
|
|
41
|
+
objective_flux = 0.0
|
|
42
|
+
fluxes_dict[objective_reaction]=objective_flux
|
|
43
|
+
if show_messages:
|
|
44
|
+
print(fluxes_dict)
|
|
45
|
+
print('\n')
|
|
46
|
+
return fluxes_dict
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def set_objective(model:Model, objective:str):
|
|
50
|
+
# get flux of objective reaction
|
|
51
|
+
objective_reaction = remove_prefix_reaction(objective)
|
|
52
|
+
model.objective = objective_reaction
|
|
53
|
+
return objective_reaction
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def remove_prefix_reaction(reaction):
|
|
57
|
+
return sub("^R_","",reaction)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_reaction(model:Model, objective_name:str):
|
|
61
|
+
id = remove_prefix_reaction(objective_name)
|
|
62
|
+
return model.reactions.get_by_id(id)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_flux(model:Model, objective_reaction:str, list_objective:list,
|
|
66
|
+
is_community:bool=False, transferred_list:list=None,
|
|
67
|
+
equality_flux:bool=False):
|
|
68
|
+
"""Calculate the flux of the objective reaction chosen using cobra
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
model (Model): Cobra model
|
|
72
|
+
objective_reaction (str): Objective reaction chosen
|
|
73
|
+
list_objective (list): List of all objectives (specially for community mode)
|
|
74
|
+
is_community (bool, optional): If it is a community mode. Defaults to False.
|
|
75
|
+
transferred_list (list, optional): For community mode, we have transferred metavolites list. Defaults to None.
|
|
76
|
+
equality_flux (bool, optional): Force the flux of biomass to be equal between species, else just force to have
|
|
77
|
+
a minimum value of flux=0.1 . Defaults to False.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
float, bool: The value of the objective flux, if the model is infeasible or not
|
|
81
|
+
"""
|
|
82
|
+
warnings.filterwarnings("error")
|
|
83
|
+
objective_flux=dict()
|
|
84
|
+
try:
|
|
85
|
+
###############################################################################
|
|
86
|
+
############################### COMMUNITY MODE ################################
|
|
87
|
+
###############################################################################
|
|
88
|
+
if is_community:
|
|
89
|
+
created_transfers=list()
|
|
90
|
+
created_transfers_id=list()
|
|
91
|
+
dict_objective = dict()
|
|
92
|
+
# Force equality of flux between species' objective reactions
|
|
93
|
+
if equality_flux:
|
|
94
|
+
# First species has its objective set to be compared with
|
|
95
|
+
# for community model, so we loop on the other objectives
|
|
96
|
+
# to add contraints: The flux on the other objectives
|
|
97
|
+
# has to be the same as the objective of community model
|
|
98
|
+
for obj in list_objective[1:]:
|
|
99
|
+
reaction = get_reaction(model, obj)
|
|
100
|
+
dict_objective[reaction]=1
|
|
101
|
+
same_flux = model.problem.Constraint(
|
|
102
|
+
model.reactions.get_by_id(objective_reaction).flux_expression - reaction.flux_expression,
|
|
103
|
+
lb=0, ub=0)
|
|
104
|
+
model.add_cons_vars(same_flux)
|
|
105
|
+
# Force to have flux in all species
|
|
106
|
+
else:
|
|
107
|
+
# We loop on all objective to add the constraint of minimial flux
|
|
108
|
+
# to all objective reactions
|
|
109
|
+
for obj in list_objective:
|
|
110
|
+
reaction = reaction = get_reaction(model, obj)
|
|
111
|
+
dict_objective[reaction]=1
|
|
112
|
+
flux_positiv = model.problem.Constraint(
|
|
113
|
+
reaction.flux_expression,
|
|
114
|
+
lb=0.1, ub=1000)
|
|
115
|
+
model.add_cons_vars(flux_positiv)
|
|
116
|
+
|
|
117
|
+
model.objective = dict_objective
|
|
118
|
+
|
|
119
|
+
# Create Transfer reaction between 2 species where reactant is metabolite "From"
|
|
120
|
+
# and product is metabolite "To"
|
|
121
|
+
for transf_meta in transferred_list:
|
|
122
|
+
name_meta=transf_meta["Metabolite"].rsplit('_',1)[0]
|
|
123
|
+
name_meta=sub("^M_","",name_meta)
|
|
124
|
+
id_reaction = f'R_TRANSF_{name_meta}_{transf_meta["From"]}_{transf_meta["To"]}'
|
|
125
|
+
# Create the transfer reaction
|
|
126
|
+
reaction = cobra.Reaction(id_reaction)
|
|
127
|
+
reaction.lower_bound = 0.
|
|
128
|
+
reaction.upper_bound = 1000.
|
|
129
|
+
reactant = model.metabolites.get_by_id(sub("^M_","",transf_meta["ID from"]))
|
|
130
|
+
procuct = model.metabolites.get_by_id(sub("^M_","",transf_meta["ID to"]))
|
|
131
|
+
reaction.add_metabolites({
|
|
132
|
+
reactant: -1.0,
|
|
133
|
+
procuct: 1.0
|
|
134
|
+
})
|
|
135
|
+
# For some reason sometimes the transfers already exists
|
|
136
|
+
# This part check if it exists and if it does not then the transfers is created
|
|
137
|
+
try:
|
|
138
|
+
model.reactions.get_by_id(id_reaction)
|
|
139
|
+
except:
|
|
140
|
+
if id_reaction not in created_transfers_id:
|
|
141
|
+
created_transfers.append(reaction)
|
|
142
|
+
created_transfers_id.append(id_reaction)
|
|
143
|
+
model.add_reactions(created_transfers)
|
|
144
|
+
###############################################################################
|
|
145
|
+
###############################################################################
|
|
146
|
+
###############################################################################
|
|
147
|
+
|
|
148
|
+
objectives_flux = model.optimize()
|
|
149
|
+
|
|
150
|
+
for obj in list_objective:
|
|
151
|
+
objective = remove_prefix_reaction(obj)
|
|
152
|
+
objective_flux[objective]=round(objectives_flux.fluxes[objective],2)
|
|
153
|
+
|
|
154
|
+
infeasible = False
|
|
155
|
+
except UserWarning:
|
|
156
|
+
for obj in list_objective:
|
|
157
|
+
objective = remove_prefix_reaction(obj)
|
|
158
|
+
objective_flux[objective]=0
|
|
159
|
+
infeasible = True
|
|
160
|
+
logger.log.info("Model infeasible")
|
|
161
|
+
|
|
162
|
+
return objective_flux, infeasible
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_init(model:Model, list_objective:list, show_messages:bool=True):
|
|
166
|
+
"""Get initial flux of all objective reactions using cobra
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
model (Model): Cobra model
|
|
170
|
+
list_objective (list): List of objective reaction names
|
|
171
|
+
show_messages (bool, optional): Write messages into console if True. Default True.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
dic: Dictionnary of objective reaction and their respective fluxes
|
|
175
|
+
"""
|
|
176
|
+
if show_messages:
|
|
177
|
+
title_mess = "\n############################################\n" \
|
|
178
|
+
"############################################\n" \
|
|
179
|
+
f" {color.bold}CHECK FLUX{color.cyan_light}\n"\
|
|
180
|
+
"############################################\n" \
|
|
181
|
+
"############################################\n"
|
|
182
|
+
logger.print_log(title_mess, "info", color.cyan_light)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
print("---------------- FLUX INIT -----------------\n")
|
|
186
|
+
fluxes_init = get_list_fluxes(model, list_objective, show_messages)
|
|
187
|
+
|
|
188
|
+
if show_messages:
|
|
189
|
+
print("--------------- MEDIUM INIT ----------------\n")
|
|
190
|
+
for reaction in model.medium:
|
|
191
|
+
print(reaction, model.reactions.get_by_id(reaction).lower_bound, model.reactions.get_by_id(reaction).upper_bound)
|
|
192
|
+
print(f"\n")
|
|
193
|
+
return fluxes_init
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def stop_flux(model:Model, list_objective:list=None, show_messages:bool=True):
|
|
197
|
+
"""Stop the import reaction flux
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
model (Model): Cobra model
|
|
201
|
+
list_objective (list): List of objective reaction names
|
|
202
|
+
show_messages (bool, optional): Write messages into console if True. Default True.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
dic: Dictionnary of objective reaction and their respective fluxes
|
|
206
|
+
"""
|
|
207
|
+
if show_messages:
|
|
208
|
+
print("---------- STOP IMPORT FLUX -------------\n")
|
|
209
|
+
logger.log.info("Shutting down import flux ...")
|
|
210
|
+
|
|
211
|
+
for elem in model.boundary:
|
|
212
|
+
if not elem.reactants and elem.upper_bound > 0:
|
|
213
|
+
if elem.lower_bound > 0:
|
|
214
|
+
elem.lower_bound = 0
|
|
215
|
+
elem.upper_bound = 0.0
|
|
216
|
+
if not elem.products and elem.lower_bound < 0:
|
|
217
|
+
if elem.upper_bound < 0:
|
|
218
|
+
elem.upper_bound = 0
|
|
219
|
+
elem.lower_bound = 0.0
|
|
220
|
+
|
|
221
|
+
logger.log.info("... DONE")
|
|
222
|
+
|
|
223
|
+
if list_objective is not None:
|
|
224
|
+
fluxes_no_import = get_list_fluxes(model, list_objective, show_messages)
|
|
225
|
+
return fluxes_no_import
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def calculate(model:Model, list_objective:list, list_seeds:list,
|
|
229
|
+
fluxes_lp=dict, try_demands:bool=True, is_community:bool=False,
|
|
230
|
+
transferred_list:list=None, equality_flux:bool=False):
|
|
231
|
+
"""Calculate the flux by adding seeds and import for them using Cobra.
|
|
232
|
+
Calculate on the objective list, the first having flux stop the calculation
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
model (Model): Cobra model
|
|
236
|
+
list_objective (list): List of objective reaction names
|
|
237
|
+
list_seeds (list): One result set of seed
|
|
238
|
+
fluxes_lp (dict): Dictionnary of all reaction and their associated LP flux
|
|
239
|
+
try_demands (bool, optional): Option to try to add demands if seeds failed.
|
|
240
|
+
Defaults to True. False for hybrid with cobra.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
dict, str: result (containing the data), objective_reaction (chosen, the first having flux)
|
|
244
|
+
"""
|
|
245
|
+
warnings.filterwarnings("error")
|
|
246
|
+
logger.log.info("Starting calculate Flux...")
|
|
247
|
+
if not list_objective:
|
|
248
|
+
logger.log.error("No objective found, abort")
|
|
249
|
+
return None, None
|
|
250
|
+
|
|
251
|
+
#cobra.flux_analysis.add_loopless(model)
|
|
252
|
+
#model.solver = 'cplex'
|
|
253
|
+
|
|
254
|
+
species = model.id
|
|
255
|
+
objective_flux_seeds=None
|
|
256
|
+
objective_flux_demands=None
|
|
257
|
+
ok_result=False
|
|
258
|
+
ok_seeds=None
|
|
259
|
+
ok_demands=None
|
|
260
|
+
objective_reaction=None
|
|
261
|
+
|
|
262
|
+
meta_exchange_list=dict()
|
|
263
|
+
|
|
264
|
+
for reaction in model.boundary:
|
|
265
|
+
for key in reaction.metabolites.keys():
|
|
266
|
+
meta_exchange_list[str(key)]=reaction.id
|
|
267
|
+
|
|
268
|
+
created_sinks = []
|
|
269
|
+
logger.log.info("Opening Import flux from seeds (Exchange) or add Sinks ...")
|
|
270
|
+
objective_reaction = set_objective(model, list_objective[0])
|
|
271
|
+
|
|
272
|
+
for seed in list_seeds:
|
|
273
|
+
seed = sub("^M_","",seed)
|
|
274
|
+
#compartment = model.metabolites.get_by_id(seed).compartment
|
|
275
|
+
#if compartment == 'e':
|
|
276
|
+
if seed in meta_exchange_list.keys():
|
|
277
|
+
reaction_exchange = model.reactions.get_by_id(meta_exchange_list[seed])
|
|
278
|
+
# For a seed in the exchange metabolites list, we allow both import and export
|
|
279
|
+
# on the "maximum" flux (1000)
|
|
280
|
+
|
|
281
|
+
if not reaction_exchange.reactants:
|
|
282
|
+
reaction_exchange.upper_bound = float(1000)
|
|
283
|
+
# Do not change the lower bound when there is an export
|
|
284
|
+
# Because in ASP we can not change the value of an already
|
|
285
|
+
# exisiting bounds atom
|
|
286
|
+
if reaction_exchange.lower_bound >= 0:
|
|
287
|
+
reaction_exchange.lower_bound = float(-1000)
|
|
288
|
+
if not reaction_exchange.products:
|
|
289
|
+
reaction_exchange.lower_bound = float(-1000)
|
|
290
|
+
# Do not change the lower bound when there is an export
|
|
291
|
+
# Because in ASP we can not change the value of an already
|
|
292
|
+
# exisiting bounds atom
|
|
293
|
+
if reaction_exchange.upper_bound <= 0:
|
|
294
|
+
reaction_exchange.upper_bound = float(1000)
|
|
295
|
+
else:
|
|
296
|
+
if not f"SK_{seed}" in created_sinks:
|
|
297
|
+
model.add_boundary(metabolite=model.metabolites.get_by_id(seed),
|
|
298
|
+
type='sink',
|
|
299
|
+
ub=float(1000),
|
|
300
|
+
lb=float(-1000))
|
|
301
|
+
created_sinks.append(f"SK_{seed}")
|
|
302
|
+
|
|
303
|
+
logger.log.info("Opening Import flux: Done")
|
|
304
|
+
|
|
305
|
+
logger.log.info("Checking objective flux on seeds ...")
|
|
306
|
+
|
|
307
|
+
lp_flux=None
|
|
308
|
+
if not is_community:
|
|
309
|
+
if fluxes_lp:
|
|
310
|
+
lp_flux = fluxes_lp[list_objective[0]]
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
objective_flux_seeds, infeasible_seeds = get_flux(model, objective_reaction, list_objective, is_community, transferred_list, equality_flux)
|
|
314
|
+
if objective_flux_seeds:
|
|
315
|
+
ok_seeds = True
|
|
316
|
+
for _, value in objective_flux_seeds.items():
|
|
317
|
+
ok_seeds = ok_seeds and value > 10e-5
|
|
318
|
+
else:
|
|
319
|
+
ok_seeds = False
|
|
320
|
+
|
|
321
|
+
objective_flux_demands=None
|
|
322
|
+
infeasible_demands=None
|
|
323
|
+
if ok_seeds:
|
|
324
|
+
ok_result = True
|
|
325
|
+
logger.log.info("... OK")
|
|
326
|
+
elif try_demands:
|
|
327
|
+
logger.log.info("... KO - Checking objective flux on demands ...")
|
|
328
|
+
# create a demand reaction for all products of the biomass reaction
|
|
329
|
+
products = [m.id for m in model.reactions.get_by_id(objective_reaction).products]
|
|
330
|
+
for m in products:
|
|
331
|
+
try:
|
|
332
|
+
model.add_boundary(model.metabolites.get_by_id(m), type="demand")
|
|
333
|
+
except:
|
|
334
|
+
low = model.reactions.get_by_id(f"DM_{m}").lower_bound
|
|
335
|
+
up = model.reactions.get_by_id(f"DM_{m}").upper_bound
|
|
336
|
+
if up <= 0:
|
|
337
|
+
model.reactions.get_by_id(f"DM_{m}").upper_bound = float(1000)
|
|
338
|
+
if low < 0 :
|
|
339
|
+
model.reactions.get_by_id(f"DM_{m}").lower_bound = float(0)
|
|
340
|
+
|
|
341
|
+
objective_flux_demands, infeasible_demands = get_flux(model, objective_reaction, list_objective, is_community, transferred_list, equality_flux)
|
|
342
|
+
|
|
343
|
+
if objective_flux_demands:
|
|
344
|
+
ok_demands = True
|
|
345
|
+
for _, value in objective_flux_seeds.items():
|
|
346
|
+
ok_demands = ok_demands and value > 10e-5
|
|
347
|
+
else:
|
|
348
|
+
ok_demands = False
|
|
349
|
+
|
|
350
|
+
if ok_demands:
|
|
351
|
+
ok_result = True
|
|
352
|
+
logger.log.info("... OK")
|
|
353
|
+
else:
|
|
354
|
+
logger.log.info("... KO")
|
|
355
|
+
|
|
356
|
+
result = {'id' : species,
|
|
357
|
+
'objective_flux_seeds': objective_flux_seeds,
|
|
358
|
+
'objective_flux_demands': objective_flux_demands,
|
|
359
|
+
'OK_seeds': ok_seeds,
|
|
360
|
+
'OK_demands': ok_demands,
|
|
361
|
+
'OK': ok_result,
|
|
362
|
+
'infeasible_seeds': infeasible_seeds,
|
|
363
|
+
'infeasible_demands': infeasible_demands}
|
|
364
|
+
|
|
365
|
+
return result, objective_reaction, lp_flux
|