Module modelmage :: Module preparator
[hide private]
[frames] | no frames]

Source Code for Module modelmage.preparator

  1  #-----------------------------------------------# 
  2  #                  modelMaGe                    # 
  3  #                                               # 
  4  #                 v1.0beta September 2009           # 
  5  #       Joerg Schaber, Max Floettmann and Jian Li   # 
  6  #                                               # 
  7  #                http://modelmage.org               # 
  8  #-----------------------------------------------# 
  9  ## @file    preparator.py 
 10  ## @brief   Determines the models to be generated given the command line directives. 
 11  ## @author  Max Floettmann, Joerg Schaber and Jian Li 
 12  ##  
 13  ## This file is part of modelMaGe.  Please visit http://modelMaGe.org for more 
 14  ## information about modelMaGe, and the latest version of modelMaGe. 
 15  ## 
 16   
 17  import sys, os 
 18  import re 
 19  #from mmexceptions import * 
 20  import libsbml as lSBML 
 21  from xml.dom import minidom 
 22   
 23   
 24  """ 
 25  This module provides simple input and output functions and a parser for 
 26  logical combinations of nodes that shall be removed. 
 27  """ 
 28   
29 -class Initializer():
30 """ 31 Provides methods to extract information from a given .ini file. 32 """ 33
34 - def __init__(self, init = None, remove = None, kinetics = None):
35 self.init = init 36 self.kinetics = None 37 self.remove = None 38 if init: 39 if os.path.isfile(init): 40 self.init = file(init).read() 41 else: 42 print "Given init file is not a valid file!" 43 exit(1) 44 else: 45 if remove: 46 self.remove = remove 47 if kinetics: 48 self.kinetics = kinetics
49
50 - def getArgs(self):
51 """ 52 Returns the commands of the remove section in the init file as a tuple of 53 remove commands and macros in a dict. The macros are not complete when returned 54 by this method, they have to be updated by the removeHandler.getTrueCombinations 55 which generates the correct logical combinations which can then be inserted in the macros. 56 57 @rtype: tuple 58 @return: [0] remove command [1] defined macros 59 """ 60 #parse remove 61 if self.init: 62 try: 63 remove = self.init.split('[Begin Remove]')[1] 64 remove = remove.split('[End Remove]')[0] 65 except: 66 remove = ' ' 67 lines = remove.splitlines() 68 remove = '' 69 for line in lines: 70 if '#' in line: 71 line = line.split('#')[0] 72 elif line != '': 73 remove += line 74 if ';' in remove: 75 remove = remove.split(';') 76 else: 77 remove = [remove] 78 if not remove: 79 remove = ' ' 80 81 elif self.remove: 82 try: 83 remove = self.remove.split(";") 84 except: 85 remove = self.remove 86 else: 87 remove = ' ' 88 89 macros = {} 90 removelist = [] 91 useMacro = False 92 #for line in remove: 93 for k, line in enumerate(remove): 94 line = line.strip() 95 if '=' not in line and useMacro: 96 print "Error: Macros must be specified for all or for no model structure." 97 exit(1) 98 if '=' in line: 99 useMacro = True 100 macro = line.split('=') 101 macros[macro[0].strip()] = macro[1].strip() 102 removelist.append(macro[0].strip()) 103 else: 104 removelist.append(line) 105 macros[line] = line 106 107 #find kinetics: 108 if self.init: 109 try: 110 kinetics = self.init.split('[Begin Kinetics]')[1] 111 kinetics = kinetics.split('[End Kinetics]')[0] 112 except: 113 print 'No kinetics found in .ini file.' 114 # return (removelist, macros,[],{},useMacro) 115 lines = kinetics.splitlines() 116 kinetics = '' 117 for line in lines: 118 if "#" in line: 119 line = line.split('#')[0] 120 elif line != '': 121 kinetics += line 122 if ';' in kinetics: 123 kinetics = kinetics.split(';') 124 else: 125 kinetics = [kinetics] 126 127 if not kinetics: 128 kinetics = ' ' 129 130 elif self.kinetics: 131 try: 132 kinetics = self.kinetics.split(";") 133 except: 134 kinetics = self.kinetics 135 else: 136 kinetics = ' ' 137 138 kineticMacros = {} 139 kineticList = [] 140 141 for i, line in enumerate(kinetics): 142 line = line.strip() 143 if '#' in line: 144 line = line.split('#')[0] 145 if '=' not in line and useMacro and line != '': 146 print "Error: Macros must be specified for all or for no model structure." 147 exit(1) 148 if '=' in line: 149 useMacro = True 150 kineticMacro = line.split('=') 151 kineticMacros[kineticMacro[0].strip()] = kineticMacro[1].strip() 152 kineticList.append(kineticMacro[0].strip()) 153 else: 154 kineticList.append(line) 155 if remove: 156 try: 157 kineticMacros[removelist[i]] = line 158 except IndexError: 159 print "Error: Please specify the same number of ';' separated kinetic and remove arguments." 160 exit(1) 161 else: 162 kineticMacros[''] = line 163 164 if removelist and kineticList and kineticList[0] != '': 165 if useMacro: 166 removelist.sort() 167 kineticList.sort() 168 if removelist != kineticList: 169 print "Error: The aliases for kinetics and remove option must be equal." 170 exit(1) 171 elif len(removelist) != len(kineticList): 172 print "Error: Please specify the same number of ';' separated kinetic and remove arguments." 173 exit(1) 174 175 # print "RemMA: %s" % macros 176 # print "RemLi: %s" % removelist 177 # print "KinLi: %s" % kineticList 178 # print "KinMA: %s" % kineticMacros 179 # print useMacro 180 return (removelist, macros, kineticList, kineticMacros, useMacro)
181 182
183 -class kineticHandler():
184 185 """ 186 Handles the input for alternative kinetics form the commandline or from the 187 annotation of the root model. 188 """
189 - def __init__(self):
190 self.__notPartOfModel = [] 191 self.__invalid = []
192 193
194 - def getKinetics(self, string, document, macros):
195 """ 196 Reads and processes \'string\' for alternative kinetics from commandline. 197 Or if \'string\' is empty reads the kinetics from the annotation of the root 198 model. 199 The string must follow the conventions of the parameter -k: 200 \"reaction(kinetic)\" 201 202 @type string:str 203 @param string: string of the form \"reaction(kinetic)\" 204 205 @type document: sbml.SBMLDocument 206 @param document: the SBML document containing the root model 207 208 @rtype: {str:[str]} 209 @return: alternate kinetics for each reaction in the root model 210 """ 211 kinetics = {} 212 ReverseKineticMacros = {} 213 if string: 214 kinetics, ReverseKineticMacros = self.__getKineticsFromString(string, macros) 215 else: 216 kinetics, ReverseKineticMacros = self.__getKineticsFromModel(document) 217 return (kinetics, ReverseKineticMacros)
218 219
220 - def __getKineticsFromString(self, string, macros):
221 """ 222 Reads different kinetics from the commandline argument -k/--kinetics 223 224 @rtype: {str:[str]} 225 @return: alternate kinetics for each reaction in the root model 226 """ 227 kinetics = {} # Dic of reaction that get alternative kinetics, 228 # to check if specified reaction are actually in the model(s). 229 # Should be a list. 230 ReverseKineticMacros = {} 231 reactions = re.findall('([a-zA-Z_][a-zA-Z0-9_]*)(\(.*?\))',string) 232 233 for reaction in reactions: 234 reactionValue = reaction[1].replace('(','').replace(')','').split(',') 235 kinetics[reaction[0]] = reactionValue 236 for key, value in macros[1].items(): 237 #print "macro key: %s\tvalue: %s"%(key, value) 238 #print "reaction[0]: %s\treaction[1]:%s"%(reaction[0], reaction[1]) 239 #print "reactionValue[0]: %s"%reactionValue[0] 240 241 # ReverseKineticMacros: {'reaction.kinetic.model': 'model'} 242 if reaction[0] + reaction[1] in value: 243 reverseKey = str(reaction[0]) + "." + reactionValue[0] + "." + key 244 ReverseKineticMacros[reverseKey] = key 245 246 return (kinetics, ReverseKineticMacros)
247
248 - def __getKineticsFromModel(self, document):
249 """ 250 Reads different kinetics from the root models annotation and writes 251 them into a dictionary 252 253 @rtype: {str:[str]} 254 @return: alternate kinetics for each reaction in the root model 255 """ 256 writer = lSBML.SBMLWriter() 257 document = minidom.parseString(writer.writeToString(document)) 258 alternateKinetics = {} 259 # dummy variable 260 ReverseKineticMacros = {} 261 try: 262 modelMaGe = document.getElementsByTagName('modelMaGe')[0] 263 reactions = modelMaGe.getElementsByTagName('reaction') 264 for reaction in reactions: 265 reactionId = reaction.attributes['id'].value 266 altlaws = [] # list of alternate laws for each reaction 267 laws = reaction.getElementsByTagName('law') 268 for law in laws: 269 altlaws.append(str(law.attributes['id'].value)) 270 alternateKinetics[str(reactionId)] = altlaws 271 except Exception, e: 272 # raise generatorError(e, 'ModelMage annotation in rootfile is missing or damaged.') 273 print "No alternative kinetics could be found in root model." 274 275 return (alternateKinetics, ReverseKineticMacros)
276 277
278 - def checkReactionIds(self, ids):
279 """ 280 Checks if all given ids have the correct form as it is given in the 281 SBML definition. 282 283 @type ids: list 284 @param ids: list of ids 285 286 @rtype: bool 287 @return: true if all ids match the right pattern, false otherwise 288 """ 289 pattern = re.compile('[a-zA-Z_][a-zA-Z0-9_]*') 290 self.__invalid = [id for id in ids if not re.match(pattern, id)] 291 if self.__invalid: 292 return 0 293 else: 294 return 1
295
296 - def checkPartOfModel(self, model, ids):
297 """ 298 Checks if reaction ids are part of a given model. 299 300 @type model: sbml.SBMLModel 301 @param model: the root model 302 303 @type ids: list 304 @param ids: list of ids 305 306 @rtype: list 307 @return: list of reactions that are in the model and in \'ids\' 308 """ 309 if not self.checkReactionIds(ids): 310 print "'--kinetics', Input error: %s is an invalid id." % self.getInvalidIds() 311 exit(1) 312 reactions = [] 313 listOfReactions = model.getListOfReactions() 314 for reaction in listOfReactions: 315 reactions.append(reaction.getId()) 316 intersection = [x for x in reactions if x in ids] 317 self.__notPartOfModel = [x for x in ids if x not in reactions] 318 return intersection
319
320 - def getInvalidIds(self):
321 return self.__invalid
322
323 - def getNonModelIds(self):
324 return self.__notPartOfModel
325
326 -class removeHandler():
327 """ 328 Handles the the commandline option for reactions or species that should be 329 removed. The method getTrueCombinations(self, string) can be called from 330 other objects and returns a list of all combinations of ids for which the 331 string is true. 332 e.g. 333 -r "(s_1 & re_1)^re_2 334 returns: 335 [['re_2'], ['s_1', 're_2'], ['s_1', 're_1'], ['re_2', 're_1']] 336 337 The results can then be passed to the generator. 338 """
339 - def __init__(self, idToName, nameToId):
340 self.__idNameMap = idToName 341 self.__nameIdMap = nameToId
342
343 - def __unique(self, s):
344 """Return a list of the elements in s, but without duplicates. 345 346 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 347 unique("abcabc") some permutation of ["a", "b", "c"], and 348 unique(([1, 2], [2, 3], [1, 2])) some permutation of 349 [[2, 3], [1, 2]]. 350 351 For best speed, all sequence elements should be hashable. Then 352 unique() will usually work in linear time. 353 354 If not possible, the sequence elements should enjoy a total 355 ordering, and if list(s).sort() doesn't raise TypeError it's 356 assumed that they do enjoy a total ordering. Then unique() will 357 usually work in O(N*log2(N)) time. 358 359 If that's not possible either, the sequence elements must support 360 equality-testing. Then unique() will usually work in quadratic 361 time. 362 363 from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 364 """ 365 366 n = len(s) 367 if n == 0: 368 return [] 369 370 # Try using a dict first, as that's the fastest and will usually 371 # work. If it doesn't work, it will usually fail quickly, so it 372 # usually doesn't cost much to *try* it. It requires that all the 373 # sequence elements be hashable, and support equality comparison. 374 u = {} 375 try: 376 for x in s: 377 u[x] = 1 378 except TypeError: 379 del u # move on to the next method 380 else: 381 return u.keys() 382 383 # We can't hash all the elements. Second fastest is to sort, 384 # which brings the equal elements together; then duplicates are 385 # easy to weed out in a single pass. 386 # NOTE: Python's list.sort() was designed to be efficient in the 387 # presence of many duplicate elements. This isn't true of all 388 # sort functions in all languages or libraries, so this approach 389 # is more effective in Python than it may be elsewhere. 390 try: 391 t = list(s) 392 t.sort() 393 except TypeError: 394 del t # move on to the next method 395 else: 396 assert n > 0 397 last = t[0] 398 lasti = i = 1 399 while i < n: 400 if t[i] != last: 401 t[lasti] = last = t[i] 402 lasti += 1 403 i += 1 404 return t[:lasti] 405 406 # Brute force is all that's left. 407 u = [] 408 for x in s: 409 if x not in u: 410 u.append(x) 411 return u
412
413 - def __all_combinations(self, l):
414 """ 415 Returns a 2**l x l matrix 416 of the 2**l possible combinations 417 of choosing l times 2 elements (0 and 1) 418 with odering and repitition 419 """ 420 comb = [] 421 for i in range(2**l): 422 comb.append(range(l)) 423 for j in range(l): 424 comb[i][j]=int((float(i)%2**(j+1))/2**j) 425 return comb
426
427 - def __true_combinations(self, list, str):
428 """ 429 Finds the list of argument combinations 430 given in list that hold TRUE given 431 the logical string str 432 """ 433 result = [] 434 l = len(list) 435 # get all possible cominations of (unique) arguments 436 comb = self.__all_combinations(l) 437 for i in range(2**l): 438 new = str 439 for j in range(l): 440 if list[j].find(':') == -1: 441 new = new.replace(list[j],repr(comb[i][j])) 442 else: 443 new = new.replace(list[j],repr(comb[i][j])) 444 445 try: 446 eval(new) 447 except: 448 print "Remove argument contains invalid elements, please check: \n %s" % str 449 sys.exit(1) 450 if bool(eval(new)): 451 t = [] 452 for j in range(l): 453 if comb[i][j] == 1: 454 t.append(list[j]) 455 result.append(t) 456 return result
457 458
459 - def __names2Ids(self, names, string):
460 """ 461 Exchanges given names with ids and enables modelMaGe to remove nodes by names 462 """ 463 for name in names: 464 if ':' in name: 465 part = name.split(':') 466 new = name 467 if part[0] in self.__nameIdMap.keys(): 468 new = new.replace(part[0],self.__nameIdMap[part[0]]) 469 if part[1] in self.__nameIdMap.keys(): 470 new = new.replace(part[1],self.__nameIdMap[part[1]]) 471 names.insert(names.index(name),new) 472 names.remove(name) 473 string = string.replace(name, new) 474 if name in self.__nameIdMap.keys(): 475 names.remove(name) 476 names.append(self.__nameIdMap[name]) 477 string = string.replace(name, self.__nameIdMap[name]) 478 479 return (names, string)
480
481 - def getTrueCombinations(self, string, macros, useMacros):
482 """ 483 Generates a list of all combinations of tokens (which have to match the 484 form of SBML ids or the id of a reaction and a species combined with a ':') 485 that let the logical expression become "\True\". 486 Also updates the dictionaries macros to map ids of removed entities to the kinetics 487 and fills the ReverseMacros dictionary with the correct values. 488 """ 489 ReverseMacros = {} 490 remMacros = macros[0] 491 kinMacros = macros[1] 492 # devide the input into logical substrings 493 try: 494 substrings = string.split(',') 495 except: 496 substrings = [''] 497 comb = [] 498 for s in substrings: 499 s = s.strip() 500 ReverseValue = '' 501 if s == '': 502 if not useMacros: 503 ReverseMacros[''] = 'master' 504 if '' in remMacros.keys(): 505 remMacros['master'] = remMacros[''] 506 remMacros.pop('') 507 else: 508 remMacros['master'] = '' 509 if '' in kinMacros.keys(): 510 kinMacros['master'] = kinMacros[''] 511 kinMacros.pop('') 512 else: 513 kinMacros['master'] = '' 514 else: 515 for key, value in macros[0].items(): 516 if value == s: 517 ReverseMacros[''] = key 518 break 519 comb.append(s) 520 else: 521 for key, value in macros[0].items(): 522 if value == s and value != '': 523 ReverseValue = key 524 break 525 # get identifiers in logical argument s 526 ids = re.findall('[a-zA-Z]+_+[a-zA-Z0-9]+\:?[a-zA-Z_]?[a-zA-Z0-9_]*',s) 527 ids += re.findall('[^\^\&\|\)\(\s]+',s) 528 ids = [x.strip() for x in ids] 529 if not ids: 530 print 'No id found in remove string!' 531 exit(1) 532 # get unique identifiers in logical argument string 533 uids = self.__unique(ids) 534 uids, s = self.__names2Ids(uids, s) 535 # test which argument combination prove TRUE 536 # according to the logical argument s 537 combinations = self.__true_combinations(uids,s) 538 # print combinations 539 # print "Value: %s" % ReverseValue 540 for c in combinations: 541 if ReverseValue != '': 542 ReverseMacros[str(c)] = ReverseValue 543 #if there are no macros specified update 544 #old macros so that the remove combinations string is key for 545 #remove and kinetics and kinetic string is value 546 if not useMacros: 547 combstr = '' 548 for x in c: 549 combstr += x 550 ReverseMacros[str(c)] = combstr 551 remMacros[combstr] = str(c) 552 if s in remMacros.keys(): 553 remMacros.pop(s) 554 if s in kinMacros.keys(): 555 kinMacros[combstr] = kinMacros[s] 556 elif s in self.__idNameMap.keys(): 557 if self.__idNameMap[s] in kinMacros.keys(): 558 kinMacros[combstr] = kinMacros[self.__idNameMap[s]] 559 if c in kinMacros.keys(): 560 kinMacros.pop(s) 561 562 comb.extend(combinations) 563 if [] in comb: 564 comb.remove([]) 565 comb = self.__unique(comb) 566 # print "RemMA: %s" % remMacros 567 # print "KinMA: %s" % kinMacros 568 # print "Rev: %s" % ReverseMacros 569 # print "comb: %s" % comb 570 571 return (comb, ReverseMacros, macros)
572