Package libSBtab :: Module Value
[hide private]
[frames] | no frames]

Source Code for Module libSBtab.Value

  1  #!/usr/bin/env python 
  2   
  3  import re 
  4  import copy 
  5  import decimal 
  6   
  7  annotate_imported = None 
  8   
  9  import Parser 
 10  import Exception 
 11  import Warning 
 12   
 13  ALLOWED_DIGITS = 2 
 14  FACTOR = decimal.Decimal(str(10 ** ALLOWED_DIGITS)) 
 15   
16 -def formatID(use_database = False, use_qualifier = False, use_compartment = False):
17 arr = [] 18 if use_compartment: 19 arr.append("[%(compartment)s] ") 20 if use_qualifier: 21 arr.append("%(qualifier)s ") 22 if use_database: 23 arr.append("%(database)s:") 24 arr.append("%(id)s") 25 return "".join(arr)
26
27 -def multigcd(args = []):
28 a = args.pop() 29 if not args: 30 return a 31 b = args.pop() 32 while b: 33 a, b = b, a % b 34 args.append(a) 35 return multigcd(args)
36
37 -class Value:
38 - def __len__(self):
39 return len(str(self))
40
41 - def __eq__(self, other):
42 return str(self) == str(other)
43
44 - def __lt__(self, other):
45 return str(self) < str(other)
46
47 - def plainValue(self):
48 return self
49
50 - def additionalValue(self):
51 return ""
52
53 -class ID(Value):
54 - def __init__(self, id, database, qualifier, qualifier_was_default = False):
55 self.id = id 56 self.database = database 57 self.qualifier = qualifier 58 self.qualifier_was_default = qualifier_was_default 59 if self.id: 60 self.id = self.id.strip() 61 if self.database: 62 self.database = self.database.strip() 63 self.annotation = None
64
65 - def plainValue(self):
66 return self.id
67
68 - def additionalValue(self):
69 return self.database
70 71 # lazy evaluation
72 - def semanticSBML(self):
73 if not self.annotation: 74 try: 75 if annotate_imported is None: 76 import semanticSBML.annotate 77 annotate_imported = True 78 except ImportError: 79 annotate_imported = False 80 except: # while unstable rebuilding of semanticsbml 81 annotate_imported = False 82 83 if annotate_imported: 84 self.annotation = semanticSBML.annotate.Annotation(self.database, self.id) 85 else: 86 self.annotation = NotImplemented 87 return self.annotation
88
89 - def __str__(self):
90 if self.id: 91 return formatID(self.database, not self.qualifier_was_default) % self.__dict__ 92 else: 93 return ""
94
95 - def __repr__(self):
96 return str(self)
97
98 - def __nonzero__(self):
99 return len(str(self))
100
101 -class ReactionID(Value):
102 - def __init__(self, id_value, compartment, compartment_was_default = False):
103 self.id = id_value.id 104 self.database = id_value.database 105 self.qualifier = id_value.qualifier 106 self.qualifier_was_default = id_value.qualifier_was_default 107 self.compartment = compartment 108 self.compartment_was_default = compartment_was_default
109
110 - def __str__(self):
111 if self.id: 112 return formatID(self.database, not self.qualifier_was_default, not self.compartment_was_default) % self.__dict__ 113 else: 114 return ""
115
116 - def __repr__(self):
117 return str(self)
118
119 -class ReactionFormula(Value):
120 - def __init__(self, compartment, original_formula_string, parts):
121 self.direction = None 122 self.right = [] 123 self.left = [] 124 self.compartment = compartment 125 self.original_formula_string = original_formula_string 126 lefts, rights = self.set_direction_and_divide_parts(parts) 127 self.find_parts(self.left, lefts) 128 self.find_parts(self.right, rights) 129 self.normalize()
130
131 - def find_parts(self, side, strings):
132 strings = ["+"] + strings 133 while strings: 134 index = None 135 i = 2 136 end = len(strings) - 1 137 while i < end: 138 string = strings[i] 139 if string in ("+", "-"): 140 index = i 141 break 142 i += 1 143 if not index: 144 parts = strings 145 strings = [] 146 else: 147 parts = strings[:index] 148 strings = strings[index:] 149 side.append(self.Reactant(self, parts))
150
151 - def set_direction_and_divide_parts(self, strings):
152 index = 0 153 for i, string in enumerate(strings): 154 index = i 155 string = strings[i] 156 if string in ("<=>", "=>", "<="): 157 if string == "<=": 158 self.direction = -1 159 elif string == "=>": 160 self.direction = 1 161 else: 162 self.direction = 0 # <=> 163 break 164 if self.direction is None: 165 raise Exception.SimpleError("Direction is not set") 166 else: 167 return strings[:index], strings[index+1:]
168 169 # does not normalize coefficients!!
170 - def normalize(self):
171 for reactant1 in self.left: 172 for reactant2 in self.left: 173 if not reactant1 is reactant2: 174 if reactant1.metabolite == reactant2.metabolite: 175 reactant1.coefficient += reactant2.coefficient 176 self.left.remove(reactant2) 177 for reactant2 in self.right: 178 if reactant1.metabolite == reactant2.metabolite: 179 reactant1.coefficient -= reactant2.coefficient 180 self.right.remove(reactant2) 181 for reactant1 in self.right: 182 for reactant2 in self.left: 183 if reactant1.metabolite == reactant2.metabolite: 184 reactant1.coefficient -= reactant2.coefficient 185 self.left.remove(reactant2) 186 for reactant2 in self.right: 187 if not reactant1 is reactant2: 188 if reactant1.metabolite == reactant2.metabolite: 189 reactant1.coefficient += reactant2.coefficient 190 self.right.remove(reactant2) 191 for reactant in self.left: 192 if reactant.coefficient < 0: 193 self.left.remove(reactant) 194 reactant.coefficient = -reactant.coefficient 195 self.right.append(reactant) 196 for reactant in self.right: 197 if reactant.coefficient < 0: 198 self.right.remove(reactant) 199 reactant.coefficient = -reactant.coefficient 200 self.left.append(reactant) 201 if self.direction is None: 202 raise Exception.SimpleError("Direction is not set") 203 if not self.left: 204 raise Exception.SimpleError("No left hand side") 205 if not self.right: 206 raise Exception.SimpleError("No right hand side") 207 self.left.sort() 208 self.right.sort() 209 if self.direction == -1: 210 tmp = self.left 211 self.left = self.right 212 self.right = tmp 213 self.direction = 1 214 elif self.direction == 0 and self.left[0] > self.right[0]: 215 tmp = self.left 216 self.left = self.right 217 self.right = tmp
218
219 - def __str__(self):
220 ret = [] 221 if self.compartment: 222 ret.append("[" + self.compartment + "]") 223 lefts = [] 224 for reactant in self.left: 225 lefts.extend(reactant) 226 ret.extend(lefts[1:]) 227 if self.direction == -1: 228 ret.append("<=") 229 elif self.direction == 1: 230 ret.append("=>") 231 else: 232 ret.append("<=>") 233 rights = [] 234 for reactant in self.right: 235 rights.extend(reactant) 236 ret.extend(rights[1:]) 237 return " ".join(ret)
238
240 ret = copy.deepcopy(self) 241 ret.normalize() 242 coefficients = [] 243 for reactant in ret: 244 coefficients.append(FACTOR * reactant.coefficient) 245 gcd = multigcd(coefficients) 246 for reactant in ret: 247 reactant.coefficient = int(FACTOR * reactant.coefficient / gcd) 248 return ret
249
250 - def __eq__(self, other):
251 if isinstance(other, basestring): 252 other = Parser.parse_reaction_formula({"str" : other}) 253 if isinstance(other, self.__class__): 254 other = other.normalized_copy_with_coefficients() 255 return str(self.normalized_copy_with_coefficients()) == str(other)
256
257 - def __iter__(self):
258 return (self.left + self.right).__iter__()
259
260 - def __neg__(self):
261 c = copy.deepcopy(self) 262 c.direction = -c.direction 263 c.normalize() 264 return c
265
266 - def __repr__(self):
267 return str(self)
268
269 - class Reactant:
270 reactant_re = re.compile("^(.*)(\\[[^\\]]*\\])?$") 271
272 - def __init__(self, reaction_formula, parts):
273 self.reaction_formula = reaction_formula 274 self.coefficient = decimal.Decimal("1.0") 275 self.metabolite = None 276 self.sign_set = False 277 self.coefficient_set = False 278 self.metabolite_set = False 279 self.compartment = reaction_formula.compartment 280 if len(parts) < 2: 281 raise Exception.SimpleError("Not enough parts given") 282 part = parts.pop(0) 283 if self.set_sign(part): 284 part = parts.pop(0) 285 if not self.set_coefficient(part): 286 parts = [part] + parts 287 self.set_metabolite(parts)
288
289 - def set_sign_possible(self, string):
290 return self.start_reactant or string in ("+", "-")
291
292 - def set_sign(self, string):
293 if string in ("+", "-"): 294 self._check_already_sets(self.sign_set, self.coefficient_set, self.metabolite_set) 295 self.sign = string == "+" 296 if self.sign is False: 297 Warning.SimpleWarning("Found a minus (-)") 298 self.coefficient = -self.coefficient 299 self.sign_set = True 300 return True 301 else: 302 return False
303
304 - def set_coefficient(self, string):
305 try: 306 coefficient = decimal.Decimal(string) 307 except decimal.InvalidOperation: 308 return False 309 else: 310 self._check_already_sets(self.coefficient_set, self.metabolite_set) 311 self._check_not_yet_sets(self.sign_set) 312 if FACTOR * coefficient != int(FACTOR * coefficient): 313 raise Exception.SimpleError("Use at most %d digits after the decimal" % ALLOWED_DIGITS) 314 self.coefficient = coefficient 315 if self.sign is False: 316 self.coefficient = -self.coefficient 317 w = None 318 if coefficient < 0: 319 w = Warning.SimpleWarning("Found negative coefficient") 320 if coefficient == 0: 321 w = Warning.SimpleWarning("Found a zero (0) as coefficient") 322 if coefficient == 1: 323 w = Warning.SimpleWarning("Found a one (1) as coefficient") 324 if w: 325 Warning.warn(w) 326 self.coefficient_set = True 327 return True
328
329 - def set_metabolite(self, strings):
330 self._check_already_sets(self.metabolite_set) 331 self._check_not_yet_sets(self.sign_set) 332 if not strings: 333 raise Exception.SimpleError("No metabolite given") 334 metabolite_strings = [] 335 for string in strings: 336 if string in ("+", "-"): 337 raise Exception.SimpleError("Wrong order. Check formula") 338 elif string in ("=>", "<=>", "<="): 339 raise Exception.SimpleError("Direction is set twice") 340 else: 341 metabolite_strings.append(string) 342 last_string = metabolite_strings.pop() 343 m = self.reactant_re.match(string) 344 if m: 345 metabolite_strings.append(m.group(1)) 346 if m.group(2): 347 self.compartment = m.group(2) 348 self.metabolite_set = True 349 try: 350 int(metabolite_strings[0]) 351 except ValueError: 352 self.metabolite = " ".join(metabolite_strings) 353 else: 354 raise Exception.SimpleError("Wrong order. Check formula") 355 return True 356 else: 357 return False
358
359 - def different_compartment(self):
360 return self.compartment != self.reaction_formula.compartment
361
362 - def species(self):
363 if self.different_compartment(): 364 return "%s[%s]" % (self.metabolite, self.compartment) 365 else: 366 return self.metabolite
367
368 - def _check_already_sets(self, *sets):
369 if [v for v in sets if v]: 370 raise Exception.SimpleError("Wrong order. Check formula")
371
372 - def _check_not_yet_sets(self, *sets):
373 self._check_already_sets(*[not v for v in sets])
374
375 - def __iter__(self):
376 if self.coefficient == 0: 377 return [].__iter__() 378 ret = [] 379 output_coefficient = self.coefficient 380 if output_coefficient == int(output_coefficient): 381 output_coefficient = int(output_coefficient) 382 if output_coefficient < 0: 383 ret.append("-") 384 if output_coefficient != -1: 385 ret.append(str(-output_coefficient)) 386 else: 387 ret.append("+") 388 if output_coefficient != 1: 389 ret.append(str(output_coefficient)) 390 ret.append(self.species()) 391 return ret.__iter__()
392
393 - def __cmp__(self, other):
394 return cmp(self.metabolite, other.metabolite) or cmp(self.coefficient, other.coefficient)
395