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/resmod.py ADDED
@@ -0,0 +1,74 @@
1
+ from . import flux
2
+ from cobra.core import Model
3
+
4
+ class Resmod:
5
+ def __init__(self, name:str, objectives:list, solver_type:str, search_mode:str, search_type:str,
6
+ size:int, seeds_list:list, flux_lp:dict, flux_cobra:dict=None, run_mode:str=None,
7
+ accu:bool=False, is_community:bool=False, transferred_list:list=None):
8
+ """Initialize Object Resmod
9
+
10
+ Args:
11
+ name (str): Name of the solution
12
+ objectives (list): List of objective reaction names
13
+ solver_type (str): Type of solver (Reasoning / FBA / Hybrid)
14
+ search_mode (str): search mode type (Minimize / Submin)
15
+ search_type (str): search type (enumeration / union /intersection)
16
+ size (int): Size of set of seeds
17
+ seeds_list (list): List of seeds
18
+ flux_lp (dict): Dictionnary of all reaction with their LP flux
19
+ flux_cobra (float, optional): Cobra flux calculated (mode Filter, Guess Check). Defaults to None.
20
+ run_mode (str, optional): Running command used (full or target). Defaults to None.
21
+ accu (bool, optional): Is accumulation allowed. Defaults to False.
22
+ """
23
+ self.name = name
24
+ self.objectives = objectives
25
+ self.solver_type = solver_type
26
+ self.search_mode = search_mode
27
+ self.search_type = search_type
28
+ self.size = size
29
+ self.seeds_list = seeds_list
30
+ self.flux_lp = flux_lp
31
+ self.chosen_lp = None
32
+ self.tested_objective = None
33
+ self.objective_flux_seeds = None
34
+ self.objective_flux_demands = None
35
+ self.OK_seeds = False
36
+ self.OK_demands = False
37
+ self.OK = False
38
+ self.run_mode = run_mode
39
+ self.accu = accu
40
+ self.flux_cobra = flux_cobra
41
+ self.is_community = is_community
42
+ self.transferred_list = transferred_list
43
+
44
+
45
+
46
+ ######################## METHODS ########################
47
+ def check_flux(self, model_cobra:Model, try_demands:bool=True, equality_flux:bool=False):
48
+ """Execute flux calculation usng cobra and store data
49
+
50
+ Args:
51
+ model_cobra (Model): Cobra model
52
+ show_messages (bool, optional): Option to show the messages on console.
53
+ Defaults to True. False for hybrid with cobra.
54
+ try_demands (bool, optional): Option to try to add demands if seeds failed.
55
+ Defaults to True. False for hybrid with cobra.
56
+ equality_flux (bool, optional): Community mode otpion to force equality of flux
57
+ between species' biomass
58
+ """
59
+ with model_cobra as m:
60
+ flux_output, objective, lp_flux = \
61
+ flux.calculate(m, self.objectives, self.seeds_list, self.flux_lp, try_demands,
62
+ self.is_community, self.transferred_list, equality_flux)
63
+ if flux_output:
64
+ self.tested_objective = objective
65
+ self.chosen_lp = lp_flux
66
+ self.objective_flux_seeds = flux_output['objective_flux_seeds']
67
+ self.objective_flux_demands = flux_output['objective_flux_demands']
68
+ self.OK_seeds = flux_output['OK_seeds']
69
+ self.OK_demands = flux_output['OK_demands']
70
+ self.OK = flux_output['OK']
71
+ self.infeasible_seeds = flux_output['infeasible_seeds']
72
+ self.infeasible_demands = flux_output['infeasible_demands']
73
+
74
+ ########################################################
seed2lp/sbml.py ADDED
@@ -0,0 +1,307 @@
1
+ """Routines to extract information from SBML files.
2
+
3
+ """
4
+ import xml.etree.ElementTree as ET
5
+ from re import sub, match, search
6
+ from . import logger
7
+
8
+ def register_all_namespaces(file:str):
9
+ """Get namespaces for rewriting SBML file
10
+
11
+ Args:
12
+ file (str): SBML file path
13
+ """
14
+ namespaces = dict([node for _, node in ET.iterparse(file, events=['start-ns'])])
15
+ for ns in namespaces:
16
+ #print(ns, namespaces[ns])
17
+ ET.register_namespace(ns, namespaces[ns])
18
+
19
+ def get_root(file:str):
20
+ """Get etree root
21
+
22
+ Args:
23
+ file (str): SBML file path
24
+
25
+ Returns:
26
+ sbml (etree Element), first_line (str) : Return an etree elemnt of the network, and the first line of the sbml file
27
+ """
28
+ register_all_namespaces(file)
29
+ with open(file) as f:
30
+ first_line = f.readline()
31
+ xmlstring = f.read()
32
+ f.close()
33
+
34
+ # Remove the default namespace definition (xmlns="http://some/namespace")
35
+ default_namespace = search(r'\sxmlns="[^"]+"', xmlstring).group()
36
+ xmlstring = sub(default_namespace, '', xmlstring, count=1)
37
+
38
+ sbml = ET.fromstring(xmlstring)
39
+ #tree = ET.parse(file)
40
+ #sbml = tree.getroot()
41
+ return sbml, first_line, default_namespace
42
+
43
+ def get_sbml_tag(element:ET.Element) -> str:
44
+ "Return tag associated with given SBML element"
45
+ if element.tag[0] == "{":
46
+ _, tag = element.tag[1:].split("}") # uri is not used
47
+ else:
48
+ tag = element.tag
49
+ return tag
50
+
51
+
52
+ def get_model(sbml:ET.Element) -> ET.Element:
53
+ """
54
+ return the model of a SBML
55
+ """
56
+ model_element = None
57
+ for e in sbml:
58
+ tag = get_sbml_tag(e)
59
+ if tag == "model":
60
+ model_element = e
61
+ break
62
+ return model_element
63
+
64
+ def get_listOfSpecies(model:ET.Element) -> list[ET.Element]:
65
+ """
66
+ return list of species of a SBML model
67
+ """
68
+ listOfSpecies = None
69
+ for e in model:
70
+ tag = get_sbml_tag(e)
71
+ if tag == "listOfSpecies":
72
+ listOfSpecies = e
73
+ break
74
+ return listOfSpecies
75
+
76
+
77
+ def get_listOfReactions(model:ET.Element) -> list[ET.Element]:
78
+ """return list of reactions of a SBML model"""
79
+ listOfReactions = []
80
+ for e in model:
81
+ tag = get_sbml_tag(e)
82
+ if tag == "listOfReactions":
83
+ listOfReactions = e
84
+ break
85
+ return listOfReactions
86
+
87
+
88
+ def get_listOfReactants(reaction:ET.Element, species:str=None, is_community:bool=False) -> list:
89
+ """return list of reactants of a reaction"""
90
+ listOfReactants = list()
91
+ listOfReactantsNames = list()
92
+ for e in reaction:
93
+ tag = get_sbml_tag(e)
94
+ if tag == "listOfReactants":
95
+ for meta in e:
96
+ meta_id = meta_name = meta.attrib.get('species')
97
+ if is_community:
98
+ meta_id = sub("^M_", f"M_{species}_",meta_id)
99
+ listOfReactants.append([meta_id, meta.attrib.get('stoichiometry'), meta_name])
100
+
101
+ # Create a list of only of meta to determine if a reaction will be transport reaction
102
+ # both list must be the same (same element in reactnats and products)
103
+ listOfReactantsNames.append(meta_name.rsplit('_', 1)[0])
104
+ break
105
+ return listOfReactants, listOfReactantsNames
106
+
107
+
108
+ def get_listOfReactants_from_name(model:ET.Element, reaction_name) -> list[ET.Element]:
109
+ """return list of reactants of a reaction"""
110
+ reactions_list = get_listOfReactions(model)
111
+ for reaction in reactions_list:
112
+ if reaction_name == reaction.attrib['id']:
113
+ for e in reaction:
114
+ tag = get_sbml_tag(e)
115
+ if tag == "listOfReactants":
116
+ listOfReactants = e
117
+ break
118
+ return listOfReactants
119
+
120
+
121
+ def get_listOfProducts(reaction:ET.Element, species:str=None, is_community:bool=False) -> list:
122
+ """return list of products of a reaction"""
123
+ listOfProducts = list()
124
+ listOfProductsNames = list()
125
+ for e in reaction:
126
+ tag = get_sbml_tag(e)
127
+ if tag == "listOfProducts":
128
+ for meta in e:
129
+ meta_id = meta_name = meta.attrib.get('species')
130
+ if is_community:
131
+ meta_id = sub("^M_", f"M_{species}_",meta_id)
132
+ listOfProducts.append([meta_id, meta.attrib.get('stoichiometry'), meta_name])
133
+
134
+ # Create a list of only of meta to determine if a reaction will be transport reaction
135
+ # both list must be the same (same element in reactnats and products)
136
+ listOfProductsNames.append(meta_name.rsplit('_', 1)[0])
137
+ break
138
+ return listOfProducts, listOfProductsNames
139
+
140
+
141
+ def get_reaction_from_name(model:ET.Element, reaction_name) -> list[ET.Element]:
142
+ """return list of reactants of a reaction"""
143
+ reactions_list = get_listOfReactions(model)
144
+ for reaction in reactions_list:
145
+ if reaction_name == reaction.attrib['id']:
146
+ return reaction
147
+ raise ValueError(f"No reaction {reaction_name} found in list of reactions \n")
148
+
149
+
150
+ def get_fbc(sbml:ET.Element):
151
+ """
152
+ return the fbc namespace of a SBML
153
+ """
154
+ fbc = None
155
+ for nss in ET._namespaces(sbml):
156
+ for key in nss:
157
+ if key is not None and 'fbc' in key:
158
+ fbc=key
159
+ break
160
+ return fbc
161
+
162
+
163
+ def get_listOfParameters(model:ET.Element)-> dict:
164
+ """return list of reactions of a SBML model"""
165
+ listOfParameters = dict()
166
+ for e in model:
167
+ tag = get_sbml_tag(e)
168
+ if tag == "listOfParameters":
169
+ for param in e:
170
+ listOfParameters[param.get('id')]=param.get('value')
171
+ break
172
+ return listOfParameters
173
+
174
+
175
+ def get_parameters(model:ET.Element) -> list[ET.Element]:
176
+ """
177
+ return list of parameters of a SBML model
178
+ """
179
+ listOfParameters = None
180
+ for e in model:
181
+ tag = get_sbml_tag(e)
182
+ if tag == "listOfParameters":
183
+ listOfParameters = e
184
+ break
185
+ return listOfParameters
186
+
187
+
188
+ def get_objectives(model:ET.Element) -> list[ET.Element]:
189
+ """
190
+ return list of parameters of a SBML model
191
+ """
192
+ listOfObjective = None
193
+ for e in model:
194
+ tag = get_sbml_tag(e)
195
+ if tag == "listOfObjectives":
196
+ listOfObjective = e
197
+ break
198
+ return listOfObjective
199
+
200
+
201
+ def get_listOfFluxObjectives(model,fbc)-> list:
202
+ """return list of objective reactions of a SBML model"""
203
+ listOfFluxObjectives = list()
204
+
205
+ for e in model:
206
+ tag = get_sbml_tag(e)
207
+ if tag == "listOfObjectives":
208
+ for lo in e[0]:
209
+ if lo:
210
+ for o in lo:
211
+ name = o.attrib.get('{'+fbc+'}reaction')
212
+ coef = o.attrib.get('{'+fbc+'}coefficient')
213
+ reaction = get_reaction_from_name(model, name)
214
+ listOfFluxObjectives.append([name,coef,reaction])
215
+ break
216
+ return listOfFluxObjectives
217
+
218
+
219
+ def read_SBML_species(filename)-> dict:
220
+ """Yield names of species listed in given SBML file"""
221
+ model_dict = dict()
222
+ tree = ET.parse(filename)
223
+ sbml = tree.getroot()
224
+ model = get_model(sbml)
225
+ species_list = list()
226
+ reactions_list = list()
227
+
228
+ for species in get_listOfSpecies(model):
229
+ species_list.append(species.attrib['id'])
230
+ for reaction in get_listOfReactions(model):
231
+ reactions_list.append(f"{reaction.attrib['id']}")
232
+
233
+ model_dict['Metabolites'] = species_list
234
+ model_dict['Reactions'] = reactions_list
235
+ return model_dict
236
+
237
+
238
+ def get_used_metabolites(filename, call_log=False)-> set:
239
+ """Determine from source file the truly used metabolite (and not the list of species)
240
+ Necessary for scripts 10_1_scope_analyse.py and 10_3_iCN718_metabolite_analyses.py
241
+
242
+ Args:
243
+ filename (str): Path to thie SBML file
244
+
245
+ Returns:
246
+ used_metabolites (set): Set of used metabolites
247
+ """
248
+ tree = ET.parse(filename)
249
+ sbml = tree.getroot()
250
+ model = get_model(sbml)
251
+ # SBML has species that are never used on any reactions
252
+ # but are present into species list
253
+ # Also, there is some reaction that involves species but
254
+ # having boundaries to [0,0], so we are not taken into account
255
+ # the species of the species of these reactions into the used metabolites
256
+ used_metabolites = set()
257
+
258
+ fbc = get_fbc(get_root(filename)[0])
259
+ parameters = get_listOfParameters(model)
260
+
261
+ for reaction in get_listOfReactions(model):
262
+ ubound = parameters[reaction.attrib.get('{'+fbc+'}upperFluxBound')]
263
+ lbound = parameters[reaction.attrib.get('{'+fbc+'}lowerFluxBound')]
264
+ if float(ubound) == 0 and float(lbound) == 0 and call_log:
265
+ logger.log.warning(f"Reaction {reaction.attrib['id']} deleted, boudaries [0,0]")
266
+ continue
267
+ else:
268
+ reactants,_ = get_listOfReactants(reaction,"",False)
269
+ products,_ = get_listOfProducts(reaction,"",False)
270
+ for reactant in reactants:
271
+ used_metabolites.add(reactant[0])
272
+ for product in products:
273
+ used_metabolites.add(product[0])
274
+
275
+ return used_metabolites
276
+
277
+
278
+
279
+ def etree_to_string(model) -> str:
280
+ return str(ET.tostring(model, encoding='utf-8', method='xml'),'UTF-8')
281
+
282
+ def create_sub_element(element:ET.Element, sub_element:str):
283
+ ET.SubElement(element,sub_element)
284
+
285
+ def remove_sub_elements(element:ET.Element):
286
+ for metabolite in list(element):
287
+ element.remove(metabolite)
288
+
289
+ def add_metabolites(element:ET.Element, metabolites_list):
290
+ for metabolite in metabolites_list:
291
+ element.append(metabolite)
292
+
293
+ def remove_reaction(model:ET.Element, element:ET.Element):
294
+ for e in model:
295
+ tag = get_sbml_tag(e)
296
+ if tag == "listOfReactions":
297
+ e.remove(element)
298
+
299
+ def check_remove_objective(model:ET.Element, reaction:ET.Element, fbc):
300
+ for e in model:
301
+ tag = get_sbml_tag(e)
302
+ if tag == "listOfObjectives":
303
+ for lo in e[0]:
304
+ if lo:
305
+ for o in lo:
306
+ if o.attrib.get('{'+fbc+'}reaction') == reaction.attrib['id']:
307
+ lo.remove(o)
seed2lp/scope.py ADDED
@@ -0,0 +1,124 @@
1
+ from .network import Network
2
+ from menetools import run_menescope
3
+ from .file import is_valid_dir, save
4
+ from os.path import join
5
+ #from .sbml import get_used_metabolites
6
+ import libsbml
7
+ from padmet.utils.sbmlPlugin import convert_from_coded_id
8
+ from padmet.utils.connection import sbmlGenerator
9
+ import sys
10
+ from . import logger
11
+
12
+
13
+ class Scope:
14
+ def __init__(self, file:str, network:Network, output_dir:str):
15
+ """Initialize Object Scope
16
+
17
+ Args:
18
+ file (str): SBML File (Needed to detect source used metabolites on the network)
19
+ network (Network): Corrected Network
20
+ """
21
+ self.file = file
22
+ self.network = network
23
+ self.output_dir = output_dir
24
+ self.dir_seeds_sbml = is_valid_dir(join(output_dir,'sbml'))
25
+ self.dir_scope = is_valid_dir(join(output_dir,'scope'))
26
+
27
+ ######################## METHODS ########################
28
+ def execute(self):
29
+ """Execute the scope from seeds solution for each solution and save it into file.
30
+ Creates an intermediate seed sbml file.
31
+ """
32
+ # Get global data on the network
33
+ #set_used_metabolites = get_used_metabolites(self.file, True)
34
+ set_used_metabolites = set(self.network.used_meta.keys())
35
+
36
+
37
+ # run the scope for each solutions of the result
38
+ for result in self.network.result_seeds:
39
+ #TODO : print better in TABLE
40
+ print(result.run_mode.upper())
41
+ print(result.solver_type)
42
+ print(result.search_mode)
43
+ print(result.name)
44
+ print("Accumulation:", result.accu)
45
+
46
+ if result.accu == True:
47
+ accu="accu"
48
+ else:
49
+ accu="no_accu"
50
+
51
+ run_mode = result.run_mode.lower().replace(" ","_").replace("-", "_")
52
+ solver = result.solver_type.lower().replace(" ","_").replace("-", "_")
53
+ search_mode = result.search_mode.lower().replace(" ","_").replace("-", "_")
54
+ seeds=set(result.seeds_list)
55
+
56
+ seeds_sbml_complete_dir_path = is_valid_dir(join(self.dir_seeds_sbml, run_mode, solver, search_mode, accu))
57
+ seeds_sbml_path=join(seeds_sbml_complete_dir_path, f'{result.name}.sbml')
58
+ scope_dir_path = is_valid_dir(join(self.dir_scope, run_mode, solver, search_mode, accu))
59
+
60
+ # Write the seed into sbl format for each solutions
61
+ create_species_sbml(seeds, seeds_sbml_path)
62
+ logger.log.info(f"Seeds sbml file created: {seeds_sbml_path}")
63
+
64
+ # Run menescope from seed to get the scope
65
+ logger.log.info(f"Scope running for {seeds_sbml_path}...")
66
+ scope_model = run_menescope(self.file, seeds_sbml_path)
67
+ logger.log.info(f"Scope terminated.")
68
+ scope_model["size_scope"] = len(scope_model["scope"])
69
+ scope_model["size_all_metabolites"] = len(set_used_metabolites)
70
+
71
+
72
+
73
+ print("size of scope", scope_model["size_scope"])
74
+ print("size of all metabolites", scope_model["size_all_metabolites"],"\n\n")
75
+ save(f'{result.name}', scope_dir_path, scope_model, "json")
76
+ logger.log.info(f"Scope saved in: {scope_dir_path}/{result.name}.json.")
77
+
78
+
79
+
80
+ def create_species_sbml(metabolites, outputfile):
81
+ """Create a SBML files with a list of species containing metabolites of the input set.
82
+ Check if there are forbidden SBML characters in the metabolite IDs/ If yes, exit.
83
+
84
+ Args:
85
+ metabolites (set): set of metabolites
86
+ outputfile (str): SBML file to be written
87
+ """
88
+ document = libsbml.SBMLDocument(2, 1)
89
+ model = document.createModel("metabolites")
90
+ forbidden_charlist = ['-', '|', '/', '(', ')',
91
+ "'", '=', '#', '*', '.', ':', '!', '+', '[',
92
+ ']', ',', ' ']
93
+ forbidden_character_in_metabolites = None
94
+ issue_trying_to_add_species = None
95
+ for compound in metabolites:
96
+ compound = compound.strip('"')
97
+ _, _, comp = convert_from_coded_id(compound)
98
+ s = model.createSpecies()
99
+ sbmlGenerator.check(s, 'create species')
100
+ forbidden_characters_detacted = [char for char in forbidden_charlist if char in compound]
101
+ if len(forbidden_characters_detacted) > 0:
102
+ logger.log.warning("Forbidden character ({0}) in {1}. SBML creation will failed.".format(' '.join(forbidden_characters_detacted), compound))
103
+ forbidden_character_in_metabolites = True
104
+ try:
105
+ sbmlGenerator.check(s.setId(compound), 'set species id')
106
+ except:
107
+ issue_trying_to_add_species = True
108
+ logger.log.warning("Issue when trying to add compound {0}.".format(compound))
109
+
110
+ if comp is not None:
111
+ sbmlGenerator.check(s.setCompartment(comp), 'set species compartment')
112
+ elif comp is None:
113
+ logger.log.warning("No compartment for " + compound)
114
+
115
+ if issue_trying_to_add_species is True and forbidden_character_in_metabolites is True:
116
+ logger.log.warning("Forbidden character in compound ID, SBML creation will failed.")
117
+ logger.log.warning("Modify the metabolic networks SBMl file by renaming these metabolites and removing the forbidden character.")
118
+ sys.exit(1)
119
+ if issue_trying_to_add_species is True and forbidden_character_in_metabolites is None:
120
+ logger.log.warning("Issue when trying to add metabolite into SBML file, potential issue with SBML format.")
121
+ logger.log.warning("Modify the metabolic networks SBMl file by renaming these metabolites and removing the forbidden character.")
122
+ sys.exit(1)
123
+
124
+ libsbml.writeSBMLToFile(document, outputfile)