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/solver.py
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Object Solver constitued of
|
|
2
|
+
# - run_mode (str): Running command used (full or target)
|
|
3
|
+
# - network (Network): Object Network
|
|
4
|
+
# - time_limit_minute (float): Time limit given by user in minutes
|
|
5
|
+
# - time_limit (int): Time limit converted in seconds for resolutions
|
|
6
|
+
# - number_solution (int): Limit number of solutions to find
|
|
7
|
+
# - clingo_configuration (str): Configuration for clingo resolution (jumpy or not, or other)
|
|
8
|
+
# - clingo_strategy (str): Strategy for clingo resolution (usc,oll or not, other)
|
|
9
|
+
# - enumeration (bool): Enumerate the solutions limited by the number of solutions
|
|
10
|
+
# - intersection (bool): Find the intersection of all solutions without limitation (give one solution)
|
|
11
|
+
# - union (bool): Find the union of all solutions without limitation (give one solution)
|
|
12
|
+
# - minimize (bool): Search the minimal carinality of solutions
|
|
13
|
+
# - subset_minimal (bool): Search the subset minimal solutions
|
|
14
|
+
# - clingo_constant (str): Set the value of constant in lp file for search
|
|
15
|
+
# - one_model_unsat (bool): Set if the minimze solution is unsatisfiable
|
|
16
|
+
# - optimum (int): Value of minimal cardinality if not unsatisfiable
|
|
17
|
+
# - output (dict): List of all solutions
|
|
18
|
+
# - timer_list (dict): List of all timers to find solution
|
|
19
|
+
# - verbose (bool): Set debug mode
|
|
20
|
+
|
|
21
|
+
from os import path
|
|
22
|
+
from .network import Network
|
|
23
|
+
from .file import write_instance_file
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from . import logger
|
|
26
|
+
from . import color
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ASP_CLINGO:
|
|
32
|
+
SRC_DIR = path.dirname(path.abspath(__file__))
|
|
33
|
+
ASP_SRC_SEED_SOLVING = path.join(SRC_DIR, 'asp/seed-solving.lp')
|
|
34
|
+
ASP_SRC_CONSTRAINTS_TARGET = path.join(SRC_DIR, 'asp/constraints_targets.lp')
|
|
35
|
+
ASP_SRC_MINIMIZE = path.join(SRC_DIR, 'asp/minimize.lp')
|
|
36
|
+
ASP_SRC_FLUX = path.join(SRC_DIR, 'asp/flux.lp')
|
|
37
|
+
ASP_SRC_MAXIMIZE_FLUX = path.join(SRC_DIR, 'asp/maximize_flux.lp')
|
|
38
|
+
ASP_SRC_MAXIMIZE_PRODUCED_TARGET = path.join(SRC_DIR, 'asp/maximize_produced_target.lp')
|
|
39
|
+
ASP_SRC_COMMUNITY = path.join(SRC_DIR, 'asp/community_search.lp')
|
|
40
|
+
ASP_SRC_SHOW_SEEDS = path.join(SRC_DIR, 'asp/show_seeds.lp')
|
|
41
|
+
ASP_SRC_SHOW_TRANSFERS = path.join(SRC_DIR, 'asp/show_tranfers.lp')
|
|
42
|
+
ASP_SRC_LIMIT_TRANSFERS = path.join(SRC_DIR, 'asp/limit_transfers.lp')
|
|
43
|
+
ASP_SRC_ATOM_TRANSF = path.join(SRC_DIR, 'asp/atom_for_transfers.lp')
|
|
44
|
+
ASP_SRC_ATOMS = path.join(SRC_DIR, 'asp/definition_atoms.lp')
|
|
45
|
+
ASP_SRC_COM_HEURISTIC = path.join(SRC_DIR, 'asp/community_heuristic.lp')
|
|
46
|
+
ASP_SRC_SEED_EXTERNAL = path.join(SRC_DIR, 'asp/seed_external.lp')
|
|
47
|
+
CLINGO_CONFIGURATION = {
|
|
48
|
+
'minimize-enumeration': ['--project=show', '--opt-mode=enum'],
|
|
49
|
+
'minimize-union': ['--enum-mode=brave', '--opt-mode=enum'],
|
|
50
|
+
'minimize-intersection': ['--enum-mode=cautious', '--opt-mode=enum'],
|
|
51
|
+
'minimize-one-model': None,
|
|
52
|
+
'submin-enumeration': ['--heuristic=Domain', '--enum-mode=domRec', '--dom-mod=5,16'], # modifier 5: false / pick 16 : Atoms that are shown
|
|
53
|
+
'submin-intersection': ['--heuristic=Domain', '--enum-mode=cautious', '--dom-mod=5,16'], # modifier 5: false / pick 16 : Atoms that are shown
|
|
54
|
+
}
|
|
55
|
+
ASW_FLAG, OPT_FLAG, OPT_FOUND = 'Answer: ', 'Optimization: ', 'OPTIMUM FOUND'
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class GROUNDING:
|
|
59
|
+
#TODO: CLEAN GROUND MODE IF NOT USED
|
|
60
|
+
GROUND = False
|
|
61
|
+
|
|
62
|
+
###################################################################
|
|
63
|
+
########################## Class Solver ##########################
|
|
64
|
+
###################################################################
|
|
65
|
+
class Solver:
|
|
66
|
+
def __init__(self, run_mode:str, network:Network,
|
|
67
|
+
time_limit_minute:float=None, number_solution:int=None,
|
|
68
|
+
clingo_configuration:str=None, clingo_strategy:str=None,
|
|
69
|
+
intersection:bool=False, union:bool=False,
|
|
70
|
+
minimize:bool=False, subset_minimal:bool=False,
|
|
71
|
+
temp_dir:str=None, short_option:str=None, run_solve:str=None,
|
|
72
|
+
verbose:bool=False, community_mode:str=None):
|
|
73
|
+
""""Initialize Object Solver
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
run_mode (str): Running command used (full or target)
|
|
77
|
+
network (Network): Network constructed
|
|
78
|
+
time_limit_minute (float, optional): Time limit given by user in minutes . Defaults to None.
|
|
79
|
+
number_solution (int, optional): Limit number of solutions to find. Defaults to 100. if -1, no enumeration
|
|
80
|
+
clingo_configuration (str, optional): Configuration for clingo resolution . Defaults to None.
|
|
81
|
+
clingo_strategy (str, optional): Strategy for clingo resolution. Defaults to None.
|
|
82
|
+
intersection (bool, optional): Find the intersection of all solutions without limitation (give one solution). Defaults to False.
|
|
83
|
+
union (bool, optional): Find the union of all solutions without limitation (give one solution). Defaults to False.
|
|
84
|
+
minimize (bool, optional): Search the minimal carinality of solutions. Defaults to False.
|
|
85
|
+
subset_minimal (bool, optional): Search the subset minimal solutions. Defaults to False.
|
|
86
|
+
temp_dir (str, optional): Temporary directory for saving instance file and clingo outputs. Defaults to None.
|
|
87
|
+
short_option (str, optional): Short way to write option on filename. Defaults to None.
|
|
88
|
+
run_solve (str, optional): Solving run used (reasoning, filter, guess-check, guess-check-div)
|
|
89
|
+
verbose (bool, optional): Set debug mode. Defaults to False.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
self.is_linear:bool
|
|
93
|
+
self.asp = ASP_CLINGO()
|
|
94
|
+
self.ground = GROUNDING().GROUND
|
|
95
|
+
self.run_mode = run_mode
|
|
96
|
+
self.network = network
|
|
97
|
+
self.time_limit_minute = time_limit_minute
|
|
98
|
+
self.set_time_limit()
|
|
99
|
+
self.number_solution = number_solution
|
|
100
|
+
self._set_clingo_configuration(clingo_configuration)
|
|
101
|
+
self._set_clingo_strategy(clingo_strategy)
|
|
102
|
+
self.enumeration = True
|
|
103
|
+
if(number_solution == -1):
|
|
104
|
+
self.enumeration = False
|
|
105
|
+
self.intersection = intersection
|
|
106
|
+
self.union = union
|
|
107
|
+
self.minimize = minimize
|
|
108
|
+
self.subset_minimal = subset_minimal
|
|
109
|
+
self.clingo_constant = list()
|
|
110
|
+
self.one_model_unsat = True
|
|
111
|
+
self.optimum = tuple()
|
|
112
|
+
self.optimum_found = False
|
|
113
|
+
self.opt_prod_tgt = None
|
|
114
|
+
self.opt_size = None
|
|
115
|
+
self.output = dict()
|
|
116
|
+
self.timer_list = dict()
|
|
117
|
+
self.verbose = verbose
|
|
118
|
+
self.messages = list()
|
|
119
|
+
self.temp_dir = temp_dir
|
|
120
|
+
self.short_option = short_option
|
|
121
|
+
self.run_solve = run_solve
|
|
122
|
+
if self.run_solve == "guess_check_div":
|
|
123
|
+
self.diversity = True
|
|
124
|
+
else:
|
|
125
|
+
self.diversity = False
|
|
126
|
+
self.grounded = str()
|
|
127
|
+
self.temp_result_file = str()
|
|
128
|
+
self.community_mode = community_mode
|
|
129
|
+
|
|
130
|
+
self.asp_files = [self.asp.ASP_SRC_SEED_SOLVING, self.asp.ASP_SRC_CONSTRAINTS_TARGET, self.asp.ASP_SRC_ATOMS]
|
|
131
|
+
self._set_instance_file()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
######################## SETTER ########################
|
|
135
|
+
def set_time_limit(self):
|
|
136
|
+
"""Convert time limit minute into seconds for resolutions
|
|
137
|
+
"""
|
|
138
|
+
if self.time_limit_minute != 0:
|
|
139
|
+
self.time_limit=self.time_limit_minute*60
|
|
140
|
+
else:
|
|
141
|
+
self.time_limit=None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _set_clingo_configuration(self, clingo_configuration:str):
|
|
145
|
+
"""Prepare configuration command option for resolution
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
clingo_configuration (str): configuration mode
|
|
149
|
+
"""
|
|
150
|
+
if clingo_configuration != "none":
|
|
151
|
+
self.clingo_configuration = f"--configuration={clingo_configuration}"
|
|
152
|
+
else:
|
|
153
|
+
self.clingo_configuration = ""
|
|
154
|
+
|
|
155
|
+
def _set_clingo_strategy(self, clingo_strategy:str):
|
|
156
|
+
"""Prepare strategy command option for resolution
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
clingo_strategy (str): strategy mode
|
|
160
|
+
"""
|
|
161
|
+
if clingo_strategy != "none":
|
|
162
|
+
self.clingo_strategy = f"--opt-strategy={clingo_strategy}"
|
|
163
|
+
else:
|
|
164
|
+
self.clingo_strategy = ""
|
|
165
|
+
|
|
166
|
+
def _set_instance_file(self):
|
|
167
|
+
"""Prepare ASP instance filename for saving into temporary directory file
|
|
168
|
+
"""
|
|
169
|
+
filename = f'instance_{self.network.name}_{self.short_option}'
|
|
170
|
+
self.network.instance_file = path.join(self.temp_dir,f'{filename}.lp')
|
|
171
|
+
write_instance_file(self.network.instance_file, self.network.facts)
|
|
172
|
+
self.asp_files.append(self.network.instance_file)
|
|
173
|
+
logger.log.info(f"Instance file written: {self.network.instance_file}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _set_temp_result_file(self):
|
|
177
|
+
self.temp_result_file = f'temp_{self.network.name}_{self.short_option}'
|
|
178
|
+
|
|
179
|
+
########################################################
|
|
180
|
+
|
|
181
|
+
######################## METHODS ########################
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_solutions_infos(self, search_mode:str=""):
|
|
185
|
+
"""Get infos of solving mode for messsages and outputs.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
search_mode (str, optional): Describe the mode of resolution. Defaults to "".
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
str, str, str: mode_message, model_type, output_type
|
|
192
|
+
"""
|
|
193
|
+
mode_message = ""
|
|
194
|
+
model_type = ""
|
|
195
|
+
output_type = ""
|
|
196
|
+
match search_mode:
|
|
197
|
+
case "minimize-one-model":
|
|
198
|
+
mode_message="Minimize optimum"
|
|
199
|
+
model_type="model_one_solution"
|
|
200
|
+
output_type = 'MINIMIZE OPTIMUM'
|
|
201
|
+
case "minimize-intersection":
|
|
202
|
+
mode_message="Minimize intersection"
|
|
203
|
+
model_type="model_intersection"
|
|
204
|
+
output_type = 'MINIMIZE INTERSECTION'
|
|
205
|
+
case "minimize-union":
|
|
206
|
+
mode_message="Minimize union"
|
|
207
|
+
model_type="model_union"
|
|
208
|
+
output_type = 'MINIMIZE UNION'
|
|
209
|
+
case "minimize-enumeration":
|
|
210
|
+
output_type = 'MINIMIZE ENUMERATION'
|
|
211
|
+
case "submin-enumeration":
|
|
212
|
+
output_type = 'SUBSET MINIMAL ENUMERATION'
|
|
213
|
+
case "submin-intersection":
|
|
214
|
+
mode_message="Subset Minimal intersection"
|
|
215
|
+
model_type="model_intersection"
|
|
216
|
+
output_type = 'SUBSET MINIMAL INTERSECTION'
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
full_option=[self.clingo_configuration, self.clingo_strategy]
|
|
220
|
+
greedy_clingo_option=self.asp.CLINGO_CONFIGURATION[search_mode]
|
|
221
|
+
if greedy_clingo_option:
|
|
222
|
+
full_option = [*full_option, *greedy_clingo_option]
|
|
223
|
+
full_option = list(filter(None, full_option))
|
|
224
|
+
return full_option, mode_message, model_type, output_type
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def init_const(self):
|
|
228
|
+
""" Inititate ASP constants
|
|
229
|
+
"""
|
|
230
|
+
self.clingo_constant = ['-c']
|
|
231
|
+
match self.run_mode:
|
|
232
|
+
case 'target':
|
|
233
|
+
logger.print_log("Mode : TARGET", "info")
|
|
234
|
+
if not self.network.targets_as_seeds:
|
|
235
|
+
logger.print_log('Option: TARGETS ARE FORBIDDEN SEEDS', "info")
|
|
236
|
+
logger.print_log(f'Search seeds validating the {len(self.network.targets)} targets…','debug')
|
|
237
|
+
self.clingo_constant.append('run_mode=target')
|
|
238
|
+
case 'full':
|
|
239
|
+
logger.print_log("Mode : FULL NETWORK", "info")
|
|
240
|
+
logger.print_log("Search seeds validating all metabolites as targets…",'debug')
|
|
241
|
+
self.clingo_constant.append('run_mode=full')
|
|
242
|
+
case 'fba':
|
|
243
|
+
logger.print_log("Mode : FBA", "info")
|
|
244
|
+
if not self.network.targets_as_seeds:
|
|
245
|
+
logger.print_log('Option: TARGETS ARE FORBIDDEN SEEDS', "info")
|
|
246
|
+
logger.print_log('Info: Targets are reactant of objective', "info")
|
|
247
|
+
logger.print_log("Search seeds aleatory…",'debug')
|
|
248
|
+
self.clingo_constant.append('run_mode=fba')
|
|
249
|
+
case 'community':
|
|
250
|
+
match self.community_mode:
|
|
251
|
+
case "global":
|
|
252
|
+
logger.print_log("Community Mode : Global Subset Minimal", "info")
|
|
253
|
+
case "bisteps":
|
|
254
|
+
logger.print_log("Community Mode : Bisteps Subset Minimal", "info")
|
|
255
|
+
case "delsupset":
|
|
256
|
+
logger.print_log("Community Mode : Delete superset of seeds", "info")
|
|
257
|
+
if not self.network.targets_as_seeds:
|
|
258
|
+
logger.print_log('Option: TARGETS ARE FORBIDDEN SEEDS', "info")
|
|
259
|
+
logger.print_log(f'Search seeds validating the {len(self.network.targets)} targets…','debug')
|
|
260
|
+
self.clingo_constant.append('run_mode=target')
|
|
261
|
+
|
|
262
|
+
if self.network.accumulation:
|
|
263
|
+
logger.print_log('ACCUMULATION: Authorized', "info")
|
|
264
|
+
self.clingo_constant.append('-c')
|
|
265
|
+
self.clingo_constant.append('accu=1')
|
|
266
|
+
else:
|
|
267
|
+
logger.print_log('ACCUMULATION: Forbidden', "info")
|
|
268
|
+
self.clingo_constant.append('-c')
|
|
269
|
+
self.clingo_constant.append('accu=0')
|
|
270
|
+
|
|
271
|
+
def get_separate_optimum(self):
|
|
272
|
+
"""This function separate the optimisations if possible:
|
|
273
|
+
Maximization of produced target (rank 2)
|
|
274
|
+
Minimization of set of seeds (rank 1)
|
|
275
|
+
When using multiple optimization with clingo, the solver gives back
|
|
276
|
+
a list of optimality found in order of importance in the asp files .
|
|
277
|
+
A higher rank means a higher importance.
|
|
278
|
+
"""
|
|
279
|
+
if len(self.optimum)==1:
|
|
280
|
+
self.opt_prod_tgt = None
|
|
281
|
+
self.opt_size = self.optimum[0]
|
|
282
|
+
else:
|
|
283
|
+
self.opt_prod_tgt = self.optimum[0]
|
|
284
|
+
self.opt_size = self.optimum[1]
|
|
285
|
+
|
|
286
|
+
def get_message(self, mode:str=None):
|
|
287
|
+
"""Get messages to put on terminal
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
mode (str, optional): witch kind of messages to chose. Defaults to None.
|
|
291
|
+
"""
|
|
292
|
+
match mode:
|
|
293
|
+
case 'subsetmin':
|
|
294
|
+
logger.print_log("\n____________________________________________","info",color.purple)
|
|
295
|
+
logger.print_log("____________________________________________\n", "info",color.purple)
|
|
296
|
+
logger.print_log(f"Sub Mode: {color.bold}SUBSET MINIMAL{color.reset}".center(55), "info")
|
|
297
|
+
logger.print_log("____________________________________________", "info",color.purple)
|
|
298
|
+
logger.print_log("____________________________________________\n", "info",color.purple)
|
|
299
|
+
case "minimize":
|
|
300
|
+
logger.print_log("\n____________________________________________","info",color.purple)
|
|
301
|
+
logger.print_log("____________________________________________\n", "info",color.purple)
|
|
302
|
+
logger.print_log(f"Sub Mode: {color.bold}MINIMIZE{color.reset}".center(55), "info")
|
|
303
|
+
logger.print_log("____________________________________________", "info",color.purple)
|
|
304
|
+
logger.print_log("____________________________________________\n", "info",color.purple)
|
|
305
|
+
case "one solution":
|
|
306
|
+
logger.print_log(f"\n~~~~~~~~~~~~~~~ {color.bold}One solution{color.reset} ~~~~~~~~~~~~~~~", "info")
|
|
307
|
+
case "intersection":
|
|
308
|
+
logger.print_log(f"\n~~~~~~~~~~~~~~~ {color.bold}Intersection{color.reset} ~~~~~~~~~~~~~~~", "info")
|
|
309
|
+
case "enumeration":
|
|
310
|
+
logger.print_log(f"\n~~~~~~~~~~~~~~~~ {color.bold}Enumeration{color.reset} ~~~~~~~~~~~~~~~", "info")
|
|
311
|
+
case "union":
|
|
312
|
+
logger.print_log(f"\n~~~~~~~~~~~~~~~~~~~ {color.bold}Union{color.reset} ~~~~~~~~~~~~~~~~~~", "info")
|
|
313
|
+
case "end":
|
|
314
|
+
logger.print_log('############################################\n\n', "info", color.cyan_light)
|
|
315
|
+
case "optimum error":
|
|
316
|
+
logger.print_log("\n____________________________________________","info")
|
|
317
|
+
logger.print_log('ABORTED: No objective funcion found \
|
|
318
|
+
\nPlease correct the SBML file to contain either \
|
|
319
|
+
\n - a function with "BIOMASS" (not case sensiive) in the name \
|
|
320
|
+
\n - a function in the objective list', "error")
|
|
321
|
+
case "command":
|
|
322
|
+
logger.print_log(" Command", "debug")
|
|
323
|
+
case "classic":
|
|
324
|
+
logger.print_log(f"\n················ {color.bold}Classic mode{color.reset} ···············", "info")
|
|
325
|
+
case "filter":
|
|
326
|
+
logger.print_log(f"\n················ {color.bold}Filter mode{color.reset} ···············", "info")
|
|
327
|
+
case "guess_check":
|
|
328
|
+
logger.print_log(f"\n·············· {color.bold}Guess-Check mode{color.reset} ············", "info")
|
|
329
|
+
case "guess_check_div":
|
|
330
|
+
logger.print_log(f"\n····· {color.bold}Guess-Check with diversity mode{color.reset} ······", "info")
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
seed2lp/temp_flux_com.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import cobra
|
|
2
|
+
model=cobra.io.read_sbml_model("networks/toys_communities/communities/comex.sbml")
|
|
3
|
+
model=cobra.io.read_sbml_model("networks/toys_communities/sbml/B1.sbml")
|
|
4
|
+
|
|
5
|
+
for elem in model.boundary:
|
|
6
|
+
if not elem.reactants and elem.upper_bound > 0:
|
|
7
|
+
if elem.lower_bound > 0:
|
|
8
|
+
elem.lower_bound = 0
|
|
9
|
+
elem.upper_bound = 0.0
|
|
10
|
+
if not elem.products and elem.lower_bound < 0:
|
|
11
|
+
if elem.upper_bound < 0:
|
|
12
|
+
elem.upper_bound = 0
|
|
13
|
+
elem.lower_bound = 0.0
|
|
14
|
+
|
|
15
|
+
model.reactions.EX_A.upper_bound = 0
|
|
16
|
+
model.reactions.EX_B.upper_bound = 0
|
|
17
|
+
model.reactions.EX_C.upper_bound = 0
|
|
18
|
+
model.reactions.EX_E.upper_bound = 0
|
|
19
|
+
#model.reactions.EX_A.lower_bound = -1000
|
|
20
|
+
model.reactions.B1_EX_A.lower_bound = -1000
|
|
21
|
+
model.reactions.B2_EX_A.lower_bound = -1000
|
|
22
|
+
model.reactions.B2_EX_B.lower_bound = -1000
|
|
23
|
+
#model.reactions.EX_B.lower_bound = -1000
|
|
24
|
+
model.reactions.EX_C.lower_bound = 0
|
|
25
|
+
model.reactions.EX_E.lower_bound = 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
same_flux = model.problem.Constraint(
|
|
29
|
+
model.reactions.B1_Biom1.flux_expression - model.reactions.B2_Biom2.flux_expression,
|
|
30
|
+
lb=0, ub=0)
|
|
31
|
+
model.add_cons_vars(same_flux)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
#biom_com = cobra.Reaction("Biom_com")
|
|
35
|
+
#biom_com.name = "Biom_com"
|
|
36
|
+
#biom_com.lower_bound = 0
|
|
37
|
+
#biom_com.upper_bound = 1000
|
|
38
|
+
#biom_com.add_metabolites({
|
|
39
|
+
# model.metabolites.get_by_id("BM1_e"): -1.0,
|
|
40
|
+
# model.metabolites.get_by_id("BM2_e"): -1.0})
|
|
41
|
+
#
|
|
42
|
+
#model.reactions.add(biom_com)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
model.objective = "B1_Biom1"
|
|
46
|
+
solution = model.optimize()
|
|
47
|
+
print(solution.fluxes['B1_Biom1'], solution.fluxes['B2_Biom2'], solution.objective_value)
|
|
48
|
+
|
|
49
|
+
for r in model.reactions:
|
|
50
|
+
print(f"{r} -- {r.lower_bound} -- {r.upper_bound}")
|
|
51
|
+
|
|
52
|
+
#######################################################################################################################################################
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
import cobra
|
|
56
|
+
model=cobra.io.read_sbml_model("networks/toys_communities/communities/comex_2.sbml")
|
|
57
|
+
|
|
58
|
+
for elem in model.boundary:
|
|
59
|
+
if not elem.reactants and elem.upper_bound > 0:
|
|
60
|
+
if elem.lower_bound > 0:
|
|
61
|
+
elem.lower_bound = 0
|
|
62
|
+
elem.upper_bound = 0.0
|
|
63
|
+
if not elem.products and elem.lower_bound < 0:
|
|
64
|
+
if elem.upper_bound < 0:
|
|
65
|
+
elem.upper_bound = 0
|
|
66
|
+
elem.lower_bound = 0.0
|
|
67
|
+
|
|
68
|
+
model.reactions.EX_A.upper_bound = 0
|
|
69
|
+
model.reactions.EX_B.upper_bound = 0
|
|
70
|
+
model.reactions.EX_A.lower_bound = -1000
|
|
71
|
+
model.reactions.EX_B.lower_bound = 0
|
|
72
|
+
|
|
73
|
+
#same_flux = model.problem.Constraint(model.reactions.Biom1_B1.flux_expression - model.reactions.Biom2_B2.flux_expression, lb=0, ub=0)
|
|
74
|
+
#model.add_cons_vars(same_flux)
|
seed2lp/utils.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Utilitaries"""
|
|
2
|
+
import os
|
|
3
|
+
import clyngor
|
|
4
|
+
import re
|
|
5
|
+
from re import findall
|
|
6
|
+
from . import logger
|
|
7
|
+
from csv import reader
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def solve(*args, **kwargs):
|
|
11
|
+
"Wrapper around clyngor.solve"
|
|
12
|
+
kwargs.setdefault('use_clingo_module', False)
|
|
13
|
+
try:
|
|
14
|
+
return clyngor.solve(*args, **kwargs)
|
|
15
|
+
except FileNotFoundError as err:
|
|
16
|
+
if 'clingo' in err.filename:
|
|
17
|
+
logger.log.error('Binary file clingo is not accessible in the PATH.')
|
|
18
|
+
exit(1)
|
|
19
|
+
else: raise err
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_ids_from_file(fname:str, asp_atome_type:str=None) -> [str]:
|
|
23
|
+
"""Get metabolites id from seeds file, forbidden seeds file or possible seeds file
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
fname (str): file path
|
|
27
|
+
asp_atome_type (str, optional): Type of atome for facts. Defaults to None.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
NotImplementedError: Target file of extension [ext] not implemented
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
[str]: List of metabolite
|
|
34
|
+
"""
|
|
35
|
+
"Yield identifiers of seeds/targets/metabolites found in given sbml or text or lp file"
|
|
36
|
+
metabolit_list = list()
|
|
37
|
+
ext = os.path.splitext(fname)[1]
|
|
38
|
+
#if ext in {'.sbml', '.xml'}: # sbml data
|
|
39
|
+
# from .sbml import read_SBML_species
|
|
40
|
+
# yield from read_SBML_species(fname)
|
|
41
|
+
if ext in {'.lp'}: # ASP data
|
|
42
|
+
for model in solve(fname).by_arity:
|
|
43
|
+
for line, in model.get(f'{asp_atome_type}/1', ()):
|
|
44
|
+
line = unquoted(line)
|
|
45
|
+
if re.search("^M_*",line):
|
|
46
|
+
metabolit_list.append(line)
|
|
47
|
+
else:
|
|
48
|
+
metabolit_list.append(f'M_{line}')
|
|
49
|
+
elif ext in {'.txt', ''}: # file, one line per metabolite
|
|
50
|
+
with open(fname) as fd:
|
|
51
|
+
for line in map(str.strip, fd):
|
|
52
|
+
if line:
|
|
53
|
+
if re.search("^M_*",line):
|
|
54
|
+
metabolit_list.append(line)
|
|
55
|
+
else:
|
|
56
|
+
metabolit_list.append(f'M_{line}')
|
|
57
|
+
else:
|
|
58
|
+
raise NotImplementedError(f"Target file of ext {ext}: {fname}")
|
|
59
|
+
return metabolit_list
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_targets_from_file(fname:str, is_community:bool):
|
|
63
|
+
"""Get metabolites id or reactions id from target file
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
fname (str): Target file path
|
|
67
|
+
is_community (bool): Community mode
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ValueError: The element [element] misses prefix M_ or R_"
|
|
71
|
+
NotImplementedError: The [file name] extension has to be ".txt".
|
|
72
|
+
ValueError: Multiple objective reaction found
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
[str],[str]: List of target and list of objective reaction
|
|
76
|
+
"""
|
|
77
|
+
target_list=dict()
|
|
78
|
+
objective_reaction_list = list()
|
|
79
|
+
ext = os.path.splitext(fname)[1]
|
|
80
|
+
if ext in {'.txt', ".csv",''}: # file, one line per metabolite
|
|
81
|
+
# file = open(fname)
|
|
82
|
+
# tgts = reader(file, delimiter='\t')
|
|
83
|
+
with open(fname, "r") as f:
|
|
84
|
+
for line in f:
|
|
85
|
+
line = line.strip()
|
|
86
|
+
if not line:
|
|
87
|
+
continue # ignore empty line
|
|
88
|
+
data = re.split(r"[ \t]+", line)
|
|
89
|
+
|
|
90
|
+
obj_id=data[0]
|
|
91
|
+
if is_community:
|
|
92
|
+
if len(data)==2:
|
|
93
|
+
species=data[1]
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(f"invalid data, needs 2 elements (metabolite or reaction / species):\n {line}")
|
|
96
|
+
else:
|
|
97
|
+
if len(data)==1:
|
|
98
|
+
species=""
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(f"invalid data, needs 1 element (metabolite or reaction):\n {line}")
|
|
101
|
+
|
|
102
|
+
if re.search("^M_*",line):
|
|
103
|
+
prefixed_id=prefix_id_network(is_community, obj_id, species, "metabolite")
|
|
104
|
+
if obj_id in target_list:
|
|
105
|
+
target_list[obj_id].append(prefixed_id)
|
|
106
|
+
else:
|
|
107
|
+
target_list[obj_id]=[prefixed_id]
|
|
108
|
+
#target_list.append(line)
|
|
109
|
+
elif re.search("^R_*",line):
|
|
110
|
+
objective_reaction_list.append([species, obj_id])
|
|
111
|
+
else:
|
|
112
|
+
raise ValueError(f"\n{fname} : The element {line} misses prefix M_ or R_")
|
|
113
|
+
else:
|
|
114
|
+
raise NotImplementedError(f'\nThe {fname} extension has to be ".txt". Given: {ext}')
|
|
115
|
+
|
|
116
|
+
if len(objective_reaction_list) >1 and not is_community:
|
|
117
|
+
raise ValueError(f"\nMultiple objective reaction found in {fname}\n")
|
|
118
|
+
elif is_community:
|
|
119
|
+
no_duplicates = len({x[0] for x in objective_reaction_list}) == len(objective_reaction_list)
|
|
120
|
+
if not no_duplicates:
|
|
121
|
+
raise ValueError(f"\nMultiple objective reaction found in {fname} for same species\n")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
return target_list, objective_reaction_list
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def quoted(string:str) -> str:
|
|
129
|
+
r"""Return string, double quoted
|
|
130
|
+
|
|
131
|
+
>>> quoted('"a').replace('\\', '$')
|
|
132
|
+
'"$"a"'
|
|
133
|
+
>>> quoted('"a b"').replace('\\', '$')
|
|
134
|
+
'"a b"'
|
|
135
|
+
>>> quoted('a b').replace('\\', '$')
|
|
136
|
+
'"a b"'
|
|
137
|
+
>>> quoted('a\\"').replace('\\', '$')
|
|
138
|
+
'"a$""'
|
|
139
|
+
>>> quoted('a"').replace('\\', '$')
|
|
140
|
+
'"a$""'
|
|
141
|
+
>>> quoted('\\"a"').replace('\\', '$')
|
|
142
|
+
'"$"a$""'
|
|
143
|
+
>>> quoted('"').replace('\\', '$')
|
|
144
|
+
'"$""'
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
if len(string) > 1 and string[0] == '"' and string[-2] != '\\' and string[-1] == '"':
|
|
148
|
+
return string
|
|
149
|
+
else:
|
|
150
|
+
return '"' + string.replace('\\"', '"').replace('"', '\\"') + '"'
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def unquoted(string:str) -> str:
|
|
154
|
+
r"""Remove surrounding double quotes if they are acting as such
|
|
155
|
+
|
|
156
|
+
>>> unquoted('"a').replace('\\', '$')
|
|
157
|
+
'$"a'
|
|
158
|
+
>>> unquoted('"a b"')
|
|
159
|
+
'a b'
|
|
160
|
+
>>> unquoted('b"').replace('\\', '$')
|
|
161
|
+
'b$"'
|
|
162
|
+
>>> unquoted('"b\\"').replace('\\', '$')
|
|
163
|
+
'$"b$"'
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
if string[0] == '"' and string[-2] != '\\' and string[-1] == '"':
|
|
167
|
+
return string[1:-1]
|
|
168
|
+
else:
|
|
169
|
+
return string.replace('\\"', '"').replace('"', '\\"')
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def quoted_data(asp:str) -> str:
|
|
173
|
+
"Return the same atoms as found in given asp code, but with all arguments quoted"
|
|
174
|
+
def gen():
|
|
175
|
+
for model in clyngor.solve(inline=asp):
|
|
176
|
+
for pred, args in model:
|
|
177
|
+
yield f'{pred}(' + ','.join(quoted(str(arg)) for arg in args) + ').'
|
|
178
|
+
return ' '.join(gen())
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def repair_json(json_str:str, is_clingo_lpx:bool=False):
|
|
182
|
+
"""Function to add closing ] or } to the json after the process has been killed
|
|
183
|
+
delete also the last element of the json which can be not finished
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
proc_output (str): process output
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
str: complete output on json format
|
|
190
|
+
"""
|
|
191
|
+
close = {'{': '}',
|
|
192
|
+
'[': ']'}
|
|
193
|
+
if is_clingo_lpx:
|
|
194
|
+
output = json_str.rsplit('{', 1)[0]
|
|
195
|
+
output = output.rsplit(',', 1)[0]
|
|
196
|
+
else:
|
|
197
|
+
output = json_str.rsplit('\"model', 1)[0]
|
|
198
|
+
# get the list of caracter "{" "[" "]" "}" in the order of apparition
|
|
199
|
+
list_open_close=findall("{|\[|\]|}", output)
|
|
200
|
+
missing_list=list()
|
|
201
|
+
for car in list_open_close:
|
|
202
|
+
size=len(missing_list)
|
|
203
|
+
# delete the opening element when the closing element appear right after
|
|
204
|
+
if size!= 0 and ((missing_list[size -1] == "{" and car == "}")
|
|
205
|
+
or (missing_list[size -1] == "[" and car == "]")):
|
|
206
|
+
missing_list.pop(size -1)
|
|
207
|
+
else:
|
|
208
|
+
missing_list.append(car)
|
|
209
|
+
close_str=""
|
|
210
|
+
for i, open in reversed(list(enumerate(missing_list))):
|
|
211
|
+
close_str += "\n" + i * "\t" + close[open]
|
|
212
|
+
logger.log.warning("Output not totally recovered. Json has been repaired but might miss results")
|
|
213
|
+
return output+close_str
|
|
214
|
+
|
|
215
|
+
def prefix_id_network(is_community:bool, name:str, species:str="", type_element:str=""):
|
|
216
|
+
"""Prefix Reaction or Metbolite by the network name (filename) if the tool is used for community.
|
|
217
|
+
For single network, nothing is prefixed.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
name (str): ID of the element
|
|
221
|
+
species (str, optional): Network name (from filename). Defaults to "".
|
|
222
|
+
type_element: (str, optional): "reaction" or "metabolite" or no type. Defaults to "".
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
str: The name prfixed by the network if needed
|
|
226
|
+
"""
|
|
227
|
+
match is_community, type_element:
|
|
228
|
+
case True,"reaction":
|
|
229
|
+
return re.sub("^R_", f"R_{species}_",name)
|
|
230
|
+
case True,"metabolite":
|
|
231
|
+
return re.sub("^M_", f"M_{species}_",name)
|
|
232
|
+
case True,"metaid":
|
|
233
|
+
return re.sub("^meta_R_", f"meta_R_{species}_",name)
|
|
234
|
+
case True,_:
|
|
235
|
+
return f"{species}_{name}"
|
|
236
|
+
case _,_:
|
|
237
|
+
return name
|