Skip to content

Commit 0ce9433

Browse files
committed
Work on bilevel optimization and model utilities module
1 parent 32c8364 commit 0ce9433

File tree

4 files changed

+169
-12
lines changed

4 files changed

+169
-12
lines changed

modelseedpy/core/msmodelutl.py

+126-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import re
33
from cobra import Model, Reaction, Metabolite
4+
from modelseedpy.fbapkg.mspackagemanager import MSPackageManager
45

56
logger = logging.getLogger(__name__)
67

@@ -14,6 +15,7 @@ class MSModelUtil:
1415

1516
def __init__(self,model):
1617
self.model = model
18+
self.pkgmgr = MSPackageManager.get_pkg_mgr(model)
1719
self.metabolite_hash = None
1820
self.search_metabolite_hash = None
1921

@@ -40,6 +42,8 @@ def add_name_to_metabolite_hash(self,name,met):
4042
self.search_metabolite_hash[sname].append(met)
4143

4244
def find_met(self,name):
45+
if self.metabolite_hash == None:
46+
self.build_metabolite_hash()
4347
if name in self.metabolite_hash:
4448
return self.metabolite_hash[name]
4549
sname = search_name(name)
@@ -107,4 +111,125 @@ def add_exchanges_for_metabolites(self,cpds,uptake=0,excretion=0,prefix='EX_', p
107111
return drains
108112

109113
def reaction_scores(self):
110-
return {}
114+
return {}
115+
116+
#Required this function to add gapfilled compounds to a KBase model for saving gapfilled model
117+
def convert_cobra_compound_to_kbcompound(self,cpd,kbmodel,add_to_model = 1):
118+
refid = "cpd00000"
119+
if re.search('cpd\d+_[a-z]+',cpd.id):
120+
refid = cpd.id
121+
refid = re.sub("_[a-z]\d+$","",refid)
122+
cpd_data = {
123+
"aliases": [],
124+
"charge": cpd.charge,
125+
"compound_ref": "~/template/compounds/id/"+refid,
126+
"dblinks": {},
127+
"formula": cpd.formula,
128+
"id": cpd.id,
129+
"modelcompartment_ref": "~/modelcompartments/id/"+cpd.id.split("_").pop(),
130+
"name": cpd.name,
131+
"numerical_attributes": {},
132+
"string_attributes": {}
133+
}
134+
if add_to_model == 1:
135+
kbmodel["modelcompounds"].append(cpd_data)
136+
return cpd_data
137+
138+
#Required this function to add gapfilled reactions to a KBase model for saving gapfilled model
139+
def convert_cobra_reaction_to_kbreaction(self,rxn,kbmodel,cpd_hash,direction = "=",add_to_model = 1,reaction_genes = None):
140+
rxnref = "~/template/reactions/id/rxn00000_c"
141+
if re.search('rxn\d+_[a-z]+',rxn.id):
142+
rxnref = "~/template/reactions/id/"+rxn.id
143+
rxnref = re.sub("\d+$","",rxnref)
144+
rxn_data = {
145+
"id": rxn.id,
146+
"aliases": [],
147+
"dblinks": {},
148+
"direction": direction,
149+
"edits": {},
150+
"gapfill_data": {},
151+
"maxforflux": 1000000,
152+
"maxrevflux": 1000000,
153+
"modelReactionProteins": [],
154+
"modelReactionReagents": [],
155+
"modelcompartment_ref": "~/modelcompartments/id/"+rxn.id.split("_").pop(),
156+
"name": rxn.name,
157+
"numerical_attributes": {},
158+
"probability": 0,
159+
"protons": 0,
160+
"reaction_ref": rxnref,
161+
"string_attributes": {}
162+
}
163+
for cpd in rxn.metabolites:
164+
if cpd.id not in kbmodel["modelcompounds"]:
165+
cpd_hash[cpd.id] = self.convert_cobra_compound_to_kbcompound(cpd,kbmodel,1)
166+
rxn_data["modelReactionReagents"].append({
167+
"coefficient" : rxn.metabolites[cpd],
168+
"modelcompound_ref" : "~/modelcompounds/id/"+cpd.id
169+
})
170+
if reaction_genes != None and rxn.id in reaction_genes:
171+
best_gene = None
172+
for gene in reaction_genes[rxn.id]:
173+
if best_gene == None or reaction_genes[rxn.id][gene] > reaction_genes[rxn.id][best_gene]:
174+
best_gene = gene
175+
rxn_data["modelReactionProteins"] = [{"note":"Added from gapfilling","modelReactionProteinSubunits":[],"source":"Unknown"}]
176+
rxn_data["modelReactionProteins"][0]["modelReactionProteinSubunits"] = [{"note":"Added from gapfilling","optionalSubunit":0,"triggering":1,"feature_refs":["~/genome/features/id/"+best_gene],"role":"Unknown"}]
177+
if add_to_model == 1:
178+
kbmodel["modelreactions"].append(rxn_data)
179+
return rxn_data
180+
181+
def add_gapfilling_solution_to_kbase_model(self,newmodel,gapfilled_reactions,gfid=None,media_ref = None,reaction_genes = None):
182+
rxn_table = []
183+
gapfilling_obj = None
184+
if gfid == None:
185+
largest_index = 0
186+
for gapfilling in newmodel["gapfillings"]:
187+
current_index = int(gapfilling["id"].split(".").pop())
188+
if largest_index == 0 or largest_index < current_index:
189+
largest_index = current_index
190+
largest_index += 1
191+
gfid = "gf."+str(largest_index)
192+
else:
193+
for gapfilling in newmodel["gapfillings"]:
194+
if gapfilling["id"] == gfid:
195+
gapfilling_obj = gapfilling
196+
if gapfilling_obj == None:
197+
gapfilling_obj = {
198+
"gapfill_id": newmodel["id"]+"."+gfid,
199+
"id": gfid,
200+
"integrated": 1,
201+
"integrated_solution": "0",
202+
"media_ref": media_ref
203+
}
204+
newmodel["gapfillings"].append(gapfilling_obj)
205+
cpd_hash = {}
206+
for cpd in newmodel["modelcompounds"]:
207+
cpd_hash[cpd["id"]] = cpd
208+
for rxn in gapfilled_reactions["new"]:
209+
reaction = self.model.reactions.get_by_id(rxn)
210+
kbrxn = self.convert_cobra_reaction_to_kbreaction(reaction,newmodel,cpd_hash,gapfilled_reactions["new"][rxn],1,reaction_genes)
211+
kbrxn["gapfill_data"][gfid] = dict()
212+
kbrxn["gapfill_data"][gfid]["0"] = [gapfilled_reactions["new"][rxn],1,[]]
213+
rxn_table.append({
214+
'id':kbrxn["id"],
215+
'name':kbrxn["name"],
216+
'direction':format_direction(kbrxn["direction"]),
217+
'gene':format_gpr(kbrxn),
218+
'equation':format_equation(kbrxn,cpd_hash),
219+
'newrxn':1
220+
})
221+
for rxn in gapfilled_reactions["reversed"]:
222+
for kbrxn in newmodel["modelreactions"]:
223+
if kbrxn["id"] == rxn:
224+
kbrxn["direction"] = "="
225+
rxn_table.append({
226+
'id':kbrxn["id"],
227+
'name':kbrxn["name"],
228+
'direction':format_direction(kbrxn["direction"]),
229+
'gene':format_gpr(kbrxn),
230+
'equation':format_equation(kbrxn,cpd_hash),
231+
'newrxn':0
232+
})
233+
kbrxn["gapfill_data"][gfid] = dict()
234+
kbrxn["gapfill_data"][gfid]["0"] = [gapfilled_reactions["reversed"][rxn],1,[]]
235+
return rxn_table

modelseedpy/fbapkg/bilevelpkg.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
from __future__ import absolute_import
44

5+
import re
56
from optlang.symbolics import Zero, add
67
from cobra.core import Gene, Metabolite, Model, Reaction
78
from modelseedpy.fbapkg.basefbapkg import BaseFBAPkg
89

910
#Base class for FBA packages
1011
class BilevelPkg(BaseFBAPkg):
1112
def __init__(self,model):
12-
BaseFBAPkg.__init__(self,model,"reaction use",{"dualconst":"string","dualub":"string","duallb":"string"},{"dualvar":"string","objective":"string"})
13+
BaseFBAPkg.__init__(self,model,"reaction use",{"dualconst":"string","dualub":"string","duallb":"string"},{"dualvar":"string","objective":"string","dualbin":"string"})
1314

1415
def build_package(self,filter = None,binary_variable_count = 0):
1516
self.validate_parameters({}, [], {
@@ -38,7 +39,6 @@ def build_package(self,filter = None,binary_variable_count = 0):
3839
if self.parameters["binary_variable_count"] > 0:
3940
for reaction in self.model.reactions:
4041
var = self.build_variable("bflxcmp",reaction,None)
41-
const = self.build_constraint("bflxcmp",reaction,None,None,None)
4242
#Now implementing dual variables and constraints
4343
varhash = {}
4444
for var in variables:
@@ -80,12 +80,20 @@ def build_variable(self,type,object,obj_coef):
8080
var = BaseFBAPkg.build_variable(self,type,lb,ub,"continuous",object.name)
8181
obj_coef[var] = coef
8282
return var
83-
if type == "dualub":
83+
if type == "dualub":#Add a constraint that makes this variable zero when binary variable is zero
8484
var = BaseFBAPkg.build_variable(self,type,0,1000000,"continuous",object.name)
85+
if re.search('(.+)_(fflxcmp\d+)$', object.name) is not None:
86+
m = re.search('(.+)_(fflxcmp\d+)$', object.name)
87+
bvar = self.variables[m[2]][m[1]]
88+
BaseFBAPkg.build_constraint(self,"dualbin",None,0,{var:1,bvar:-1000000},object.name)
8589
obj_coef[var] = object.ub
8690
return var
8791
if type == "duallb":
8892
var = BaseFBAPkg.build_variable(self,type,-1000000,0,"continuous",object.name)
93+
#if re.search('(.+)_(fflxcmp\d+)$', object.name) is not None:
94+
#m = re.search('(.+)_(fflxcmp\d+)$', metabolite.id)
95+
#bvar = self.variables[m[2]][m[1]]
96+
#BaseFBAPkg.build_constraint(self,object.name+"_lbdualbin",None,0,{var:-1,bvar:-1000000},object)
8997
obj_coef[var] = object.lb
9098
return var
9199
if type == "flxcmp" and self.parameters["binary_variable_count"] > 0:

modelseedpy/fbapkg/gapfillingpkg.py

+23
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,29 @@ def run_test_conditions(self,condition_list,solution = None,max_iterations = 10)
502502
return None
503503
return solution
504504

505+
def filter_database_based_on_tests(self,test_conditions):
506+
filetered_list = []
507+
with self.model:
508+
rxnlist = []
509+
for reaction in self.model.reactions:
510+
if reaction.id in self.gapfilling_penalties:
511+
if "reverse" in self.gapfilling_penalties[reaction.id]:
512+
reaction.lower_bound = 0
513+
rxnlist.append([reaction,"<"])
514+
if "forward" in self.gapfilling_penalties[reaction.id]:
515+
reaction.upper_bound = 0
516+
rxnlist.append([reaction,">"])
517+
reaction.update_variable_bounds()
518+
self.pkgmgr.getpkg("ObjConstPkg").constraints["objc"]["1"].lb = 0
519+
filtered_list = FBAHelper.reaction_expansion_test(self.model,rxnlist,test_conditions,self.pkgmgr)
520+
#Now constraining filtered reactions to zero
521+
for item in filtered_list:
522+
print("Filtering:",item[0].id,item[1])
523+
if item[1] == ">":
524+
self.model.reactions.get_by_id(item[0].id).upper_bound = 0
525+
else:
526+
self.model.reactions.get_by_id(item[0].id).lower_bound = 0
527+
505528
def compute_gapfilled_solution(self, flux_values=None):
506529
if flux_values is None:
507530
flux_values = FBAHelper.compute_flux_values_from_variables(self.model)

modelseedpy/multiomics/msexpression.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ def __init__(self, id):
5555

5656
class MSExpressionFeature:
5757

58-
def __init__(self, feature):
58+
def __init__(self,feature,parent):
5959
self.id = feature.id
6060
self.feature = feature
6161
self.values = {}
62+
self.parent = parent
6263

6364
def add_value(self,condition,value):
6465
if condition in self.values:
@@ -72,6 +73,11 @@ def add_value(self,condition,value):
7273
self.values[condition] = value
7374

7475
def get_value(self,condition,normalization = None):
76+
if isinstance(condition,str):
77+
if condition not in self.parent.conditions:
78+
logger.warning("Condition "+condition+" not found in expression object!")
79+
return None
80+
condition = self.parent.conditions.get_by_id(condition)
7581
if condition not in self.values:
7682
logger.info("Condition "+condition.id+" has no value in "+self.feature.id)
7783
return None
@@ -133,11 +139,11 @@ def add_feature(self,id,create_gene_if_missing = False):
133139
if id in self.object.reactions:
134140
feature = self.object.reactions.get_by_id(id)
135141
if feature == None:
136-
logger.warning("Gene referred by expression "+id+" not found in genome object!")
142+
logger.warning("Feature referred by expression "+id+" not found in genome object!")
137143
return None
138144
if feature.id in self.features:
139145
return self.features.get_by_id(feature.id)
140-
protfeature = MSExpressionFeature(feature)
146+
protfeature = MSExpressionFeature(feature,self)
141147
self.features.append(protfeature)
142148
return protfeature
143149

@@ -147,11 +153,6 @@ def get_value(self,feature,condition,normalization = None):
147153
logger.warning("Feature "+feature+" not found in expression object!")
148154
return None
149155
feature = self.features.get_by_id(feature)
150-
if isinstance(condition,str):
151-
if condition not in self.conditions:
152-
logger.warning("Condition "+condition+" not found in expression object!")
153-
return None
154-
condition = self.conditions.get_by_id(condition)
155156
return feature.get_value(condition,normalization)
156157

157158
def build_reaction_expression(self,model,default):

0 commit comments

Comments
 (0)