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.
Files changed (53) hide show
  1. seed2lp/__init__.py +12 -0
  2. seed2lp/__main__.py +837 -0
  3. seed2lp/_version.py +2 -0
  4. seed2lp/argument.py +717 -0
  5. seed2lp/asp/atom_for_transfers.lp +7 -0
  6. seed2lp/asp/community_heuristic.lp +3 -0
  7. seed2lp/asp/community_search.lp +14 -0
  8. seed2lp/asp/constraints_targets.lp +15 -0
  9. seed2lp/asp/definition_atoms.lp +87 -0
  10. seed2lp/asp/enum-cc.lp +50 -0
  11. seed2lp/asp/flux.lp +70 -0
  12. seed2lp/asp/limit_transfers.lp +9 -0
  13. seed2lp/asp/maximize_flux.lp +2 -0
  14. seed2lp/asp/maximize_produced_target.lp +7 -0
  15. seed2lp/asp/minimize.lp +8 -0
  16. seed2lp/asp/seed-solving.lp +116 -0
  17. seed2lp/asp/seed_external.lp +1 -0
  18. seed2lp/asp/show_seeds.lp +2 -0
  19. seed2lp/asp/show_tranfers.lp +1 -0
  20. seed2lp/asp/test.lp +61 -0
  21. seed2lp/clingo_lpx.py +236 -0
  22. seed2lp/color.py +34 -0
  23. seed2lp/config.yaml +56 -0
  24. seed2lp/description.py +424 -0
  25. seed2lp/file.py +151 -0
  26. seed2lp/flux.py +365 -0
  27. seed2lp/linear.py +431 -0
  28. seed2lp/log_conf.yaml +25 -0
  29. seed2lp/logger.py +112 -0
  30. seed2lp/metabolite.py +46 -0
  31. seed2lp/network.py +1921 -0
  32. seed2lp/reaction.py +207 -0
  33. seed2lp/reasoning.py +459 -0
  34. seed2lp/reasoningcom.py +753 -0
  35. seed2lp/reasoninghybrid.py +791 -0
  36. seed2lp/resmod.py +74 -0
  37. seed2lp/sbml.py +307 -0
  38. seed2lp/scope.py +124 -0
  39. seed2lp/solver.py +333 -0
  40. seed2lp/temp_flux_com.py +74 -0
  41. seed2lp/utils.py +237 -0
  42. seed2lp-2.0.0.dist-info/METADATA +404 -0
  43. seed2lp-2.0.0.dist-info/RECORD +53 -0
  44. seed2lp-2.0.0.dist-info/WHEEL +5 -0
  45. seed2lp-2.0.0.dist-info/entry_points.txt +2 -0
  46. seed2lp-2.0.0.dist-info/licenses/LICENCE.txt +145 -0
  47. seed2lp-2.0.0.dist-info/top_level.txt +2 -0
  48. tests/__init__.py +0 -0
  49. tests/fba.py +147 -0
  50. tests/full_network.py +166 -0
  51. tests/normalization.py +188 -0
  52. tests/target.py +286 -0
  53. 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