1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import sys, os
18 import re
19
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
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
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
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
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
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
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
176
177
178
179
180 return (removelist, macros, kineticList, kineticMacros, useMacro)
181
182
184
185 """
186 Handles the input for alternative kinetics form the commandline or from the
187 annotation of the root model.
188 """
190 self.__notPartOfModel = []
191 self.__invalid = []
192
193
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
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 = {}
228
229
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
238
239
240
241
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
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
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 = []
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
273 print "No alternative kinetics could be found in root model."
274
275 return (alternateKinetics, ReverseKineticMacros)
276
277
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
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
321 return self.__invalid
322
324 return self.__notPartOfModel
325
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):
342
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
371
372
373
374 u = {}
375 try:
376 for x in s:
377 u[x] = 1
378 except TypeError:
379 del u
380 else:
381 return u.keys()
382
383
384
385
386
387
388
389
390 try:
391 t = list(s)
392 t.sort()
393 except TypeError:
394 del t
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
407 u = []
408 for x in s:
409 if x not in u:
410 u.append(x)
411 return u
412
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
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
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
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
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
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
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
533 uids = self.__unique(ids)
534 uids, s = self.__names2Ids(uids, s)
535
536
537 combinations = self.__true_combinations(uids,s)
538
539
540 for c in combinations:
541 if ReverseValue != '':
542 ReverseMacros[str(c)] = ReverseValue
543
544
545
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
567
568
569
570
571 return (comb, ReverseMacros, macros)
572