def get_h(self, keep1=True): """ c = h(p) """ hs = [poly.get_h(keep1=keep1) for poly in self] yids = butil.flatten([h_.yids for h_ in hs]) coefstrs = butil.flatten([h_.frepr.tolist() for h_ in hs]) senstrs = [[exprmanip.simplify_expr(exprmanip.diff_expr(coefstr, pid)) for pid in self.pids] for coefstr in coefstrs] hstr = str(coefstrs).replace("'", "") Dhstr = str(senstrs).replace("'", "") subs0 = dict(butil.flatten([poly.convarvals.items() for poly in self], depth=1)) def f(p): subs = subs0.copy() subs.update(dict(zip(self.pids, p))) return np.array(eval(hstr, subs)) def Df(p): subs = subs0.copy() subs.update(dict(zip(self.pids, p))) return np.array(eval(Dhstr, subs)) h = predict.Predict(f=f, Df=Df, pids=self.pids, p0=self.p0, yids=yids, frepr=butil.Series(coefstrs, yids), Dfrepr=butil.DF(senstrs, yids, self.pids)) return h
def _sub_var_names(self, input): mapping_dict = {} for id in ExprManip.extract_vars(input): # convert it back to something key_column will recognize # had to use a form dynVarName__derivWRT__optParamName for the # sensitivity variable because otherwise, # extract_vars gets confused splitId = id.split('__derivWRT__') if len(splitId) == 1: idname = splitId[0] elif len(splitId) == 2: idname = tuple(splitId) else: raise 'Problem with id %s in Trajectory._sub_var_names' % id if idname in self.key_column.keys(): mapping = 'values[start:end, %i]' % self.key_column.get(idname) elif idname in self.const_var_values.keys(): # Don't substitute for constant variable names. Those will # be taken care of earlier in the method. continue elif idname == 'time': mapping = 'times[start:end]' else: raise 'Problem with idname %s in Trajectory._sub_var_names' % id mapping_dict[id] = mapping input = ExprManip.sub_for_vars(input, mapping_dict) return input
def list2predict2(l, pids, uids=None, us=None, yids=None, c=None, p0=None): """ pred = list2predict(['exp(-p1*1)+exp(-p2*1)', 'exp(-p1*2)+exp(-p2*2)', 'exp(-p1*1)-exp(-p2*1)'], pids=['p1','p2'], p0=None) pred = list2predict(['(k1f*C1-k1r*X1)-(k2f*X1-k2r*X2)', '(k2f*X1-k2r*X2)-(k3f*X2-k3r*C2)'], uids=['X1','X2'], us=butil.get_product([1,2,3],[1,2,3]), pids=['k1f','k1r','k2f','k2r','k3f','k3r'], c={'C1':2,'C2':1}) Input: c: a mapping """ if c is not None: l = [exprmanip.sub_for_vars(s, c) for s in l] ystr = str(l).replace("'", "") ycode = compile(ystr, '', 'eval') def f(p): return np.array(eval(ycode, dict(zip(pids, p)), mathsubs)) jaclist = [] for s in l: jacrow = [exprmanip.simplify_expr(exprmanip.diff_expr(s, pid)) for pid in pids] jaclist.append(jacrow) if us is not None: jaclist = [[[exprmanip.sub_for_vars(jacentry, dict(zip(uids, u))) for jacentry in jacrow] for jacrow in jaclist] for u in us] jaclist = butil.flatten(jaclist, depth=1) jacstr = str(jaclist).replace("'", "") jaccode = compile(jacstr, '', 'eval') def Df(p): return np.array(eval(jaccode, dict(zip(pids, p)), mathsubs)) if p0 is None: p0 = [1] * len(pids) if yids is None: yids = ['y%d'%i for i in range(1, len(l)+1)] if us is not None: uids = ['u%d'%i for i in range(1, len(us)+1)] yids = butil.get_product(yids, uids) return Predict(f=f, Df=Df, p0=p0, pids=pids, yids=yids)
def get_Ep_str(net): """ """ Ep = [] for rxnid in net.rxnids: ratelaw = exprmanip.sub_for_vars(net.rxns[rxnid].kineticLaw, net.asgrules.to_od()) Ep_rxn = [] for pid in net.pids: Ep_rxn.append(exprmanip.simplify_expr(exprmanip.diff_expr(ratelaw, pid))) Ep.append(Ep_rxn) Ep_str = str(Ep).replace("'", "") Ep_code = compile(Ep_str, '', 'eval') # compile to code object net.Ep_str, net.Ep_code = Ep_str, Ep_code return Ep_str, Ep_code
def doKwargsSubstitution(self, kwargs): oldStoichiometry = self.stoichiometry self.stoichiometry = {} for base in oldStoichiometry: self.stoichiometry[kwargs[base]] = oldStoichiometry[base] self.kineticLaw = ExprManip.sub_for_vars(self.kineticLaw, kwargs)
def test_diff_expr(self): cases = [('x', 'x'), ('+x', 'x'), ('y', 'x'), ('x+y', 'x'), ('x*y', 'x'), ('x/y', 'x'), ('x**y', 'x'), ('x**x', 'x'), ('y**x', 'x'), ('acos(x)', 'x'), ('asin(x)', 'x'), ('atan(x)', 'x'), ('cos(x)', 'x'), ('cosh(x)', 'x'), ('exp(x)', 'x'), ('log(x)', 'x'), ('log10(x)', 'x'), ('sin(x)', 'x'), ('sinh(x)', 'x'), ('sqrt(x)', 'x'), ('tan(x)', 'x'), ('tanh(x)', 'x'), ('pow(x, 2)', 'x'), ('pow(y, x)', 'x'), ('y**2 - 4*x*z', 'x'), ('(-y + sqrt(y**2 - 4*x*z))/(2*x)', 'x'), ('(-y + sqrt(y**2 - 4*x*z))/(2*x)', 'y'), ('(-y + sqrt(y**2 - 4*x*z))/(2*x)', 'z'), ('sqrt(y - x)', 'x'), ('f(x)', 'x'), ('f(x - y*x + cos(x*z))', 'x'), ('g(x - y*x + cos(x*z), y, x)', 'x'), ('g(x - y*x + cos(x*z), y, x)', 'y'), ('g(x - y*x + cos(x*z), y**z, x - 14*z)', 'y'), ('g(x - y*x + cos(x*z), y, x)', 'y'), ('g(x*y, x*y**2, y)', 'z')] for expr, wrt in cases: d = ExprManip.diff_expr(expr, wrt) ad = eval(d) fd = self._num_diff(expr, wrt, x=x, y=y, z=z) # We test that our numeric and analytic derivatives differ by less # than 0.1% if ad != 0: assert abs(fd - ad) / (0.5 * (ad + fd)) < 1e-3 else: assert ad == fd
def parseTrigger(self, trigger): if '<' in trigger or '>' in trigger or '=' in trigger: raise ValueError('Event triggers must use the functions gt and lt, ' 'rather than the symbols > and <. For example, ' 'to trigger when B becomes less than A, use ' 'lt(B,A).') # Figures out if the event is time-triggered and parses it to niceness. # 'and' is a reserved keyword in python, so the parser will break # unless we substitute the name here. trigger = trigger.replace('and(', 'and_func(') self.trigger = trigger if ExprManip.extract_vars(trigger) == sets.Set(['time']): self.timeTriggered = True ast = ExprManip.AST.strip_parse(trigger) firstArg = ExprManip.AST.ast2str(ast.args[0]) secondArg = ExprManip.AST.ast2str(ast.args[1]) if firstArg == 'time': self.triggeringTime = eval(secondArg) elif secondArg == 'time': self.triggeringTime = eval(firstArg) else: raise 'Problem in time triggered events'
def parseTrigger(self, trigger): if '<' in trigger or '>' in trigger or '=' in trigger: raise ValueError( 'Event triggers must use the functions gt and lt, ' 'rather than the symbols > and <. For example, ' 'to trigger when B becomes less than A, use ' 'lt(B,A).') # Figures out if the event is time-triggered and parses it to niceness. # 'and' is a reserved keyword in python, so the parser will break # unless we substitute the name here. trigger = trigger.replace('and(', 'and_func(') self.trigger = trigger if ExprManip.extract_vars(trigger) == set(['time']): self.timeTriggered = True ast = ExprManip.AST.strip_parse(trigger) firstArg = ExprManip.AST.ast2str(ast.args[0]) secondArg = ExprManip.AST.ast2str(ast.args[1]) if firstArg == 'time': self.triggeringTime = eval(secondArg) elif secondArg == 'time': self.triggeringTime = eval(firstArg) else: raise NameError('Problem in time triggered events')
def get_h(self, keep1=True): """ """ coefs = self.get_coefs() if not keep1: coefs = butil.get_submapping(coefs, f_value=lambda s: s != '1') subcoefids, subcoefstrs = coefs.keys(), coefs.values() yids = ['%s, %s, %s'%((self.polyid,)+subcoefid) for subcoefid in subcoefids] senstrs = [[exprmanip.diff_expr(subcoefstr, pid) for pid in self.pids] for subcoefstr in subcoefstrs] hstr = str(subcoefstrs).replace("'", "") Dhstr = str(senstrs).replace("'", "") subs0 = self.convarvals.to_dict() def h(p): subs = subs0.copy() subs.update(dict(zip(self.pids, p))) return np.array(eval(hstr, subs)) def Dh(p): subs = subs0.copy() subs.update(dict(zip(self.pids, p))) return np.array(eval(Dhstr, subs)) h = predict.Predict(f=h, Df=Dh, pids=self.pids, p0=self.p0, yids=yids, frepr=butil.Series(subcoefstrs, yids), Dfrepr=butil.DF(senstrs, yids, self.pids)) return h
def test_sub_for_vars(self): cases = [ ('x', { 'x': 'y' }, 'y'), ('x + 1', { 'x': 'y' }, 'y + 1'), ('f(x)**2 - y + z', { 'x': 'y*z/(x-2)' }, 'f(y*z/(x-2))**2 - y + z'), ('x**2', { 'x': 'y+2' }, '(y+2)**2'), ('p[0]**x', { 'x': 'y+2' }, 'p[0]**(y+2)'), # In these cases substituting one-by-one will fail ('x*y', { 'x': 'y', 'y': 'z' }, 'y*z'), ('z*y', { 'z': 'y', 'y': 'z' }, 'y*z'), ] for expr, mapping, answer in cases: subbed = ExprManip.sub_for_vars(expr, mapping) assert eval(answer) == eval(subbed)
def get_concentration_elasticity_string(net): """ """ Ex = [] for rxnid in net.rxnids: ratelaw = exprmanip.sub_for_vars(net.rxns[rxnid].kineticLaw, net.asgrules.to_dict()) Ex_rxn = [] for xid in net.xids: Ex_rxn.append( exprmanip.simplify_expr(exprmanip.diff_expr(ratelaw, xid))) Ex.append(Ex_rxn) Ex_str = str(Ex).replace("'", "") Ex_code = compile(Ex_str, '', 'eval') # compile to code object net.Ex_str, net.Ex_code = Ex_str, Ex_code return Ex_str, Ex_code
def test_expr2Tex(self): lines.append(r'\section{Basic expressions}') lines.append(r'\begin{longtable}{rl}') cases = [ 'x', 'x+y', 'x-y', 'x*y', 'x/y', 'x**y', '-x', 'x**-y', 'x**(-y + z)', 'f(x)', 'g(x,y,z)', 'x**(y**z)', '(x**y)**z', 'x**y**z', 'x - (x+y)', '(x+y) - z', 'g(x-0+2, y**2 - 0**0, z*y + x/1)', 'sqrt(x+y-sqrt(z/x))', ] for expr in cases: TeXed = ExprManip.expr2TeX(expr) line = r'{\tt %s} & $ %s $ \\' % (expr, TeXed) lines.append(line) lines.append(r'\end{longtable}')
def test_var_args_subs(self): subbed = ExprManip.sub_for_func('or_func(x,y,z)', 'or_func', '*', 'a or b') x, y, z = True, False, False assert eval(subbed) x, y, z = False, False, False assert not eval(subbed) self.assertRaises(ValueError, ExprManip.sub_for_func, 'or_func(x,y,z)', 'or_func', '*', 'a + b')
def test_extract_comps(self): cases = [('x == 3', ['x == 3']), ('x == 3 and y == 4', ['x == 3', 'y == 4']), ('x < 3 and not y > 4', ['x < 3', 'y > 4']), ('x < 3 + (y > 4)', ['x < (3 + (y > 4))', 'y > 4']), ] for expr, comps in cases: result = ExprManip.extract_comps(expr) assert result == sets.Set(comps)
def __init__(self, id, stoichiometry, kineticLaw = '', name = '', reactant_stoichiometry = None, product_stoichiometry = None): self.id = id self.stoichiometry = stoichiometry # self.reactant_stoichiometry and self.product_stoichiometry # are defined to help repserve the stoichiometry defined in an # SBML model self.reactant_stoichiometry = reactant_stoichiometry self.product_stoichiometry = product_stoichiometry self.kineticLaw = kineticLaw self.name = name variables = ExprManip.extract_vars(kineticLaw) self.parameters = variables.difference(sets.Set(stoichiometry.keys()))
def get_Ex_str(net): """ """ Ex = [] for rxnid in net.rxnids: ratelaw = net.rxns[rxnid].kineticLaw Ex_rxn = [] for xid in net.xids: Ex_rxn.append(expr.diff_expr(ratelaw, xid)) # diff also simplifies Ex.append(Ex_rxn) Ex_str = str(Ex).replace("'", "") Ex_code = compile(Ex_str, '', 'eval') # compile to code object net.Ex_str, net.Ex_code = Ex_str, Ex_code return Ex_str, Ex_code
def get_Ep_str(net): """ """ Ep = [] for rxnid in net.rxnids: ratelaw = net.rxns[rxnid].kineticLaw Ep_rxn = [] for pid in net.pids: Ep_rxn.append(expr.diff_expr(ratelaw, pid)) # diff also simplifies Ep.append(Ep_rxn) Ep_str = str(Ep).replace("'", "") Ep_code = compile(Ep_str, '', 'eval') # compile to code object net.Ep_str, net.Ep_code = Ep_str, Ep_code return Ep_str, Ep_code
def test_sub_for_var(self): cases = [ ('x', 'x', 'y', 'y'), ('x + 1', 'x', 'y', 'y + 1'), ('f(x)**2 - y + z', 'x', 'y*z/(x-2)', 'f(y*z/(x-2))**2 - y + z'), ('x**2', 'x', 'y+2', '(y+2)**2'), ('p[0]**x', 'x', 'y+2', 'p[0]**(y+2)'), ('g(x, y, f(z)) != y+2 and z == y', 'z', 'y', '(g(x, y, f(y)) != (y + 2)) and (y == y)'), ] for expr, out_var, in_expr, answer in cases: subbed = ExprManip.sub_for_var(expr, out_var, in_expr) assert eval(answer) == eval(subbed)
def test_name_dict(self): name_dict = {'alpha': r'\alpha', 'beta': r'\beta', 'betarho': r'\beta\rho', 'chi': r'\lambda', 'tau': r'\tau' } cases = ['alpha', 'alpha**beta', 'chi(betarho, tau)', 'sqrt(4+sqrt(alpha))', ] for expr in cases: TeXed = ExprManip.expr2TeX(expr, name_dict)
def str2predict(s, pids, uids, us, c=None, p0=None, yids=None): """ Input: us: a list of u's where each u has the same length as uids and has the same order c: if given, a mapping from convarid to convarval """ if c is not None: s = exprmanip.sub_for_vars(s, c) ystr = str([exprmanip.sub_for_vars(s, dict(zip(uids, u))) for u in us]).\ replace("'", "") ycode = compile(ystr, '', 'eval') def f(p): return np.array(eval(ycode, dict(zip(pids, p)), mathsubs)) jaclist = [] for u in us: s_u = exprmanip.sub_for_vars(s, dict(zip(uids, u))) jacrow = [exprmanip.simplify_expr(exprmanip.diff_expr(s_u, pid)) for pid in pids] jaclist.append(jacrow) jacstr = str(jaclist).replace("'", "") jaccode = compile(jacstr, '', 'eval') def Df(p): return np.array(eval(jaccode, dict(zip(pids, p)), mathsubs)) if p0 is None: p0 = [1] * len(pids) if yids is None: yids = ['u=%s'%str(list(u)) for u in us] return Predict(f=f, Df=Df, p0=p0, pids=pids, yids=yids)
def test_sub_for_func(self): cases = [ ('f(x)', 'f', 'y', 'y+1', 'x+1'), ('f(x)**2', 'f', 'y', 'y+1', '(x+1)**2'), ('f(g(x, y, z))**2', 'f', 'y', 'y+1', '(g(x,y,z)+1)**2'), ('g(f(x), y, z)**2', 'f', 'y', 'y+1', 'g(x+1,y,z)**2'), ('g(f(x), p[0], z)**2', 'f', 'y', 'y+1', 'g(x+1,p[0],z)**2'), ('g(z, y, f(x)) == y+2 and f(x) + f(y) != f(x)', 'g', ('x', 'y', 'z'), 'f(x + y/z)', '(f(z + y/f(x)) == (y + 2)) and (f(x) + f(y) != f(x))'), ] for expr, func_name, func_vars, func_expr, answer in cases: subbed = ExprManip.sub_for_func(expr, func_name, func_vars, func_expr) assert eval(answer) == eval(subbed)
def test_extract_funcs(self): cases = [('g(x)', [('g', 1)]), ('f(g(x))', [('f', 1), ('g', 1)]), ('f(g(x)/2, a, b(x, y))', [('f', 3), ('g', 1), ('b', 2)]), ('a + g(x)', [('g', 1)]), ('a + g(x) + (a + f(x))', [('f', 1), ('g', 1)]), ('f(g(1 + h(a))/2) + j(q**k(x)) + q.r[1] + s[1]', [('f', 1), ('g', 1), ('h', 1), ('j', 1), ('k', 1)]), ('(g(x) + 2)**f(y)', [('f', 1), ('g', 1)]), ('g(x)**f(y)', [('f', 1), ('g', 1)]), ('g(x) > f(y)', [('f', 1), ('g', 1)]), ('g(x) and f(y)', [('f', 1), ('g', 1)]), ('g(x) and not f(y)', [('f', 1), ('g', 1)])] for expr, funcs in cases: assert ExprManip.extract_funcs(expr) == set(funcs)
def test_simplify_expr(self): cases = [ 'x', 'x+y', 'x-y', 'x*y', 'x/y', 'x**y', '-x', 'x**-y', 'x**(-y + z)', 'f(x)', 'g(x,y,z)', 'x**(y**z)', '(x**y)**z', 'x**y**z', 'x - (x+y)', '(x+y) - z', 'g(x-0+2, y**2 - 0**0, z*y + x/1)', 'x/x', 'x/y', '(x-x)/z', 'x**2 - y/z', 'x+1-1+2-3-x', '0+1*1', 'x-x+y', '(-2)**2', '-2**2', 'x/y == x/y', 'not True', 'x/x + y/y == 2', '3 + 4 > 6', '3 + (4 > 6)', ] cases1 = ['x**(-y + z)'] # cases = ['-y + z'] for expr in cases: simplified = ExprManip.simplify_expr(expr) orig = eval(expr) simp = eval(simplified) if orig != 0: assert old_div(abs(orig - simp), (0.5 * (orig + simp))) < 1e-6 else: assert simp == 0
def __init__(self, id, stoichiometry, kineticLaw='', name='', reactant_stoichiometry=None, product_stoichiometry=None): self.id = id self.stoichiometry = stoichiometry # self.reactant_stoichiometry and self.product_stoichiometry # are defined to help repserve the stoichiometry defined in an # SBML model self.reactant_stoichiometry = reactant_stoichiometry self.product_stoichiometry = product_stoichiometry self.kineticLaw = kineticLaw self.name = name variables = ExprManip.extract_vars(kineticLaw) self.parameters = variables.difference(sets.Set(stoichiometry.keys()))
def test_extract_vars(self): cases = [('x', ['x']), ('x + y', ['x', 'y']), ('x * y', ['x', 'y']), ('x / y', ['x', 'y']), ('x**2', ['x']), ('x**y', ['x', 'y']), ('f(x)', ['x']), ('f(x, y)', ['x', 'y']), ('f(x + z/x, y)', ['x', 'y', 'z']), ('x**2 + f(y)', ['x', 'y']), ('x + y**2 + z**(a + b) + z**f.g.h(a + b + sqrt(c))', ['x', 'y', 'z', 'a', 'b', 'c']), ('x < y', ['x', 'y']), ('(x < y) and (x == 2)', ['x', 'y']), ] for expr, vars in cases: assert ExprManip.extract_vars(expr) == sets.Set(vars)
def solve_path2(s1, s2): """ Input: s1 and s2: strs of ratelaws; variable is 'X' solve_for: 'X' or 'J' #idx_root: 0 or 1, since there can be two roots (if v1 - v2 == 0 # is a quadratic equation) Output: A tuple of (X string, J string) """ if 'inf' in s1 and 'inf' in s2: raise ValueError elif 'inf' in s1: Xstr = str(sympy.solve(s1.replace('inf', '1'), 'X', simplify=True)[0]) Jstr = exprmanip.simplify_expr(exprmanip.sub_for_var(s2, 'X', Xstr)) elif 'inf' in s2: Xstr = str(sympy.solve(s2.replace('inf', '1'), 'X', simplify=True)[0]) Jstr = exprmanip.simplify_expr(exprmanip.sub_for_var(s1, 'X', Xstr)) else: eqn = '(%s) - (%s)' % (s1, s2) roots = sympy.solve(eqn, 'X', simplify=True) if len(roots) == 2: # choose the positive root (can be the 1st or the 2nd of the two) bools = ['+ sqrt' in str(sympy.expand(root)) for root in roots] idx = bools.index(True) Xstr = str(roots[idx]) else: Xstr = str(roots[0]) Jstr = exprmanip.simplify_expr(exprmanip.sub_for_var(s1, 'X', Xstr)) """ try: xsol = str(roots[1]) varids = list(exprmanip.extract_vars(xsol)) tests = [] for i in range(10): subs = dict(zip(varids, np.random.lognormal(size=len(varids)))) subs['sqrt'] = np.sqrt tests.append(eval(xsol, subs) > 0) if not all(tests): raise ValueError except (IndexError, ValueError): xsol = str(roots[0]) """ return Xstr, Jstr
def test_sub_for_comps(self): cases = [ ('not x < 3 and True', { 'x < 3': 'True' }, False), ('not x < 3 and x < 3', { 'x < 3': 'True' }, False), ('x < 3 and x < 3', { 'x < 3': 'True' }, True), ('x < 3 and y > 4', { 'x < 3': 'True', 'y > 4': 'False' }, False), ('x < 3 and not y > 4', { 'x < 3': 'True', 'y > 4': 'False' }, True), ] for expr, mapping, result in cases: subbed = ExprManip.sub_for_comps(expr, mapping) assert eval(subbed) == result
def replace_varid(self, varid_old, varid_new, only_expr=False): """Change id of reaction, species, or parameter. :param only_expr: bool; if True, only replace varid in expressions such as reaction ratelaws or assignment rules but not variable ids; useful when varid_new == 'varid_old * r' """ if only_expr: f = lambda varid: varid else: f = lambda varid: varid_new if varid == varid_old else varid netid_new = f(self.id) net_new = self.__class__(netid_new) for var in self.variables: var_new = copy.deepcopy(var) var_new.id = f(var.id) net_new.variables.set(var_new.id, var_new) for rxn in self.reactions: rxn_new = copy.deepcopy(rxn) rxn_new.id = f(rxn.id) rxn_new.stoichiometry = OD( zip(map(f, rxn.stoichiometry.keys()), rxn.stoichiometry.values())) try: rxn_new.reactant_stoichiometry =\ OD(zip(map(f, rxn.reactant_stoichiometry.keys()), rxn.reactant_stoichiometry.values())) rxn_new.product_stoichiometry =\ OD(zip(map(f, rxn.product_stoichiometry.keys()), rxn.product_stoichiometry.values())) except AttributeError: # some rxns have no reactant/product stoich pass rxn_new.parameters = set(map(f, rxn.parameters)) rxn_new.kineticLaw = exprmanip.sub_for_var(rxn.kineticLaw, varid_old, varid_new) net_new.reactions.set(rxn_new.id, rxn_new) for varid, rule in self.assignmentRules.items(): net_new.assignmentRules.set( f(varid), exprmanip.sub_for_var(rule, varid_old, varid_new)) for varid, rule in self.algebraicRules.items(): net_new.algebraicRules.set( exprmanip.sub_for_var(varid, varid_old, varid_new), exprmanip.sub_for_var(rule, varid_old, varid_new)) for varid, rule in self.rateRules.items(): net_new.rateRules.set( f(varid), exprmanip.sub_for_var(rule, varid_old, varid_new)) for eid, event in self.events.items(): eid_new = f(eid) trigger_new = exprmanip.sub_for_var(event.trigger, varid_old, varid_new) assignments_new = OD( zip(map(f, event.event_assignments.keys()), event.event_assignments.values())) net_new.add_event(eid_new, trigger_new, assignments_new) net_new.functionDefinitions = self.functionDefinitions.copy() for fid, f in net_new.functionDefinitions.items(): fstr = 'lambda %s: %s' % (','.join(f.variables), f.math) net_new.namespace[fid] = eval(fstr, net_new.namespace) # _makeCrossReferences will take care of at least the following # attributes: # assignedVars, constantVars, optimizableVars, dynamicVars, # algebraicVars net_new._makeCrossReferences() return net_new
def fromSBMLString(sbmlStr, id=None, duplicate_rxn_params=False): r = libsbml.SBMLReader() d = r.readSBMLFromString(sbmlStr) if d.getNumErrors(): message = 'libSBML reported errors in SBML file. Try running file '\ 'through the online validator: '\ 'http://www.sbml.org/Facilities/Validator . Specific errors '\ 'noted are: ' errors = [] for ii in range(d.getNumErrors()): pm = d.getError(ii) errors.append(pm.getMessage()) print(message + '; '.join(errors)) m = d.getModel() modelId = m.getId() if (id is None) and (modelId == ''): raise ValueError('Network id not specified in SBML or passed in.') elif id is not None: modelId = id rn = Network_mod.Network(id=modelId, name=m.getName()) for f in m.getListOfFunctionDefinitions(): id, name = f.getId(), f.getName() math = f.getMath() variables = [] for ii in range(math.getNumChildren() - 1): variables.append(formula_to_py(math.getChild(ii))) math = formula_to_py(math.getRightChild()) rn.addFunctionDefinition(id, variables, math) for c in m.getListOfCompartments(): id, name = c.getId(), c.getName() size = c.getSize() isConstant = c.getConstant() rn.addCompartment(id=id, size=size, isConstant=isConstant, name=name) for s in m.getListOfSpecies(): id, name = s.getId(), s.getName() compartment = s.getCompartment() if s.isSetInitialConcentration(): iC = s.getInitialConcentration() elif s.isSetInitialAmount(): iC = s.getInitialAmount() else: iC = 1 isBC, isConstant = s.getBoundaryCondition(), s.getConstant() xml_text = s.toSBML() uniprot_ids = set([ entry[1:].split('"')[0] for entry in xml_text.split('uniprot')[1:] ]) rn.addSpecies(id=id, compartment=compartment, initialConcentration=iC, isConstant=isConstant, is_boundary_condition=isBC, name=name, uniprot_ids=uniprot_ids) for p in m.getListOfParameters(): parameter = createNetworkParameter(p) rn.addVariable(parameter) for rxn in m.getListOfReactions(): id, name = rxn.getId(), rxn.getName() kL = rxn.getKineticLaw() kLFormula = kL.getFormula() substitution_dict = {} # Deal with parameters defined within reactions for p in kL.getListOfParameters(): parameter = createNetworkParameter(p) # If a parameter with this name already exists, **and it has a # different value than this parameter** we rename this parameter # instance by prefixing it with the rxn name so there isn't a # clash. if parameter.id in list(rn.variables.keys()): logger.warn('Parameter %s appears in two different reactions ' 'in SBML file.' % parameter.id) if parameter.value != rn.variables.get(parameter.id).value or\ duplicate_rxn_params: oldId = parameter.id parameter.id = id + '_' + parameter.id substitution_dict[oldId] = parameter.id logger.warn('It has different values in the two positions ' 'so we are creating a new parameter %s.' % (parameter.id)) else: logger.warn('It has the same value in the two positions ' 'so we are only defining one parameter %s. ' 'This behavior can be changed with the option ' 'duplicate_rxn_params = True' % (parameter.id)) if parameter.id not in list(rn.variables.keys()): rn.addVariable(parameter) kLFormula = ExprManip.sub_for_vars(kLFormula, substitution_dict) # Assemble the stoichiometry. SBML has the annoying trait that # species can appear as both products and reactants and 'cancel out' # For each species appearing in the reaction, we build up a string # representing the stoichiometry. Then we'll simplify that string and # see whether we ended up with a float value in the end. stoichiometry = {} reactant_stoichiometry = {} product_stoichiometry = {} for reactant in rxn.getListOfReactants(): species = reactant.getSpecies() stoichiometry.setdefault(species, '0') stoich = reactant.getStoichiometryMath() stoich = stoichToString(reactant, stoich) stoichiometry[species] += '-(%s)' % stoich if species in reactant_stoichiometry: reactant_stoichiometry[species].append(stoich) else: reactant_stoichiometry[species] = [stoich] for product in rxn.getListOfProducts(): species = product.getSpecies() stoichiometry.setdefault(species, '0') stoich = product.getStoichiometryMath() stoich = stoichToString(product, stoich) stoichiometry[species] += '+(%s)' % stoich if species in product_stoichiometry: product_stoichiometry[species].append(stoich) else: product_stoichiometry[species] = [stoich] for species, stoich in list(stoichiometry.items()): stoich = ExprManip.simplify_expr(stoich) try: # Try converting the string to a float. stoich = float(stoich) except ValueError: pass stoichiometry[species] = stoich for modifier in rxn.getListOfModifiers(): stoichiometry.setdefault(modifier.getSpecies(), 0) rn.addReaction(id=id, stoichiometry=stoichiometry, kineticLaw=kLFormula, reactant_stoichiometry=reactant_stoichiometry, product_stoichiometry=product_stoichiometry) for ii, r in enumerate(m.getListOfRules()): if r.getTypeCode() == libsbml.SBML_ALGEBRAIC_RULE: math = formula_to_py(r.getMath()) rn.add_algebraic_rule(math) else: variable = r.getVariable() math = formula_to_py(r.getMath()) if r.getTypeCode() == libsbml.SBML_ASSIGNMENT_RULE: rn.addAssignmentRule(variable, math) elif r.getTypeCode() == libsbml.SBML_RATE_RULE: rn.addRateRule(variable, math) for ii, e in enumerate(m.getListOfEvents()): id, name = e.getId(), e.getName() if id == '': id = 'event%i' % ii try: # For libSBML 3.0 trigger_math = e.getTrigger().getMath() except AttributeError: # For older versions trigger_math = e.getTrigger() trigger = formula_to_py(trigger_math) if e.getDelay() is not None: try: # For libSBML 3.0 delay_math = e.getDelay().getMath() except AttributeError: # For older versions delay_math = e.getDelay() delay = formula_to_py(delay_math) else: delay = 0 timeUnits = e.getTimeUnits() eaDict = KeyedList() for ea in e.getListOfEventAssignments(): ea_formula = formula_to_py(ea.getMath()) ea_formula = ea_formula.replace('or(', 'or_func(') ea_formula = ea_formula.replace('and(', 'and_func(') eaDict.set(ea.getVariable(), ea_formula) rn.addEvent(id=id, trigger=trigger, eventAssignments=eaDict, delay=delay, name=name) for ii, con in enumerate(m.getListOfConstraints()): id, name = con.getId(), con.getName() if id == '': id = 'constraint%i' % ii trigger_math = con.getMath() trigger = formula_to_py(trigger_math) if con.isSetMessage(): message = con.getMessage() else: message = None rn.addConstraint(id=id, trigger=trigger, message=message, name=name) return rn
def toSBMLString(net): metaId = 0 try: m = libsbml.Model(net.id) except NotImplementedError: m = libsbml.Model(sbml_level, sbml_version) m.setId(net.id) m.setName(net.name) m.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 for id, fd in list(net.functionDefinitions.items()): try: sfd = libsbml.FunctionDefinition(id) except: sfd = libsbml.FunctionDefinition(sbml_level, sbml_version) sfd.setId(id) sfd.setName(fd.name) formula = fd.math formula = formula.replace('**', '^') formula = 'lambda(%s, %s)' % (','.join(fd.variables), formula) sfd.setMath(libsbml.parseFormula(formula)) sfd.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addFunctionDefinition(sfd) for id, c in list(net.compartments.items()): try: sc = libsbml.Compartment(id) except NotImplementedError: sc = libsbml.Compartment(sbml_level, sbml_version) sc.setId(id) sc.setName(c.name) sc.setConstant(c.is_constant) sc.setSize(c.initialValue) sc.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addCompartment(sc) for id, s in list(net.species.items()): try: ss = libsbml.Species(id) except NotImplementedError: ss = libsbml.Species(sbml_level, sbml_version) ss.setId(id) ss.setName(s.name) ss.setCompartment(s.compartment) if s.initialValue is not None and not isinstance(s.initialValue, str): ss.setInitialConcentration(s.initialValue) ss.setBoundaryCondition(s.is_boundary_condition) ss.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addSpecies(ss) for id, p in list(net.parameters.items()): try: sp = libsbml.Parameter(id) except NotImplementedError: sp = libsbml.Parameter(sbml_level, sbml_version) sp.setId(id) sp.setName(p.name) if p.initialValue is not None: sp.setValue(p.initialValue) sp.setConstant(p.is_constant) sp.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addParameter(sp) for id, r in list(net.rateRules.items()): try: sr = libsbml.RateRule() except NotImplementedError: sr = libsbml.RateRule(sbml_level, sbml_version) sr.setVariable(id) formula = r.replace('**', '^') sr.setMath(libsbml.parseFormula(formula)) sr.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addRule(sr) for id, r in list(net.assignmentRules.items()): try: sr = libsbml.AssignmentRule() except NotImplementedError: sr = libsbml.AssignmentRule(sbml_level, sbml_version) sr.setVariable(id) formula = r.replace('**', '^') sr.setMath(libsbml.parseFormula(formula)) sr.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addRule(sr) for r, r in list(net.algebraicRules.items()): try: sr = libsbml.AlgebraicRule() except NotImplementedError: sr = libsbml.AlgebraicRule(sbml_level, sbml_version) formula = r.replace('**', '^') sr.setMath(libsbml.parseFormula(formula)) sr.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addRule(sr) for id, rxn in list(net.reactions.items()): # Need to identify modifiers in kinetic law and add them to # stoichiometry kl_vars = ExprManip.extract_vars(rxn.kineticLaw) species_in_kl = kl_vars.intersection(list(net.species.keys())) for s in species_in_kl: if s not in rxn.stoichiometry: rxn.stoichiometry[s] = 0 try: srxn = libsbml.Reaction(id) except NotImplementedError: srxn = libsbml.Reaction(sbml_level, sbml_version) srxn.setId(id) srxn.setName(rxn.name) # Handle the case where the model was originally read in from an # SBML file, so that the reactants and products of the Reaction # object are explicitly set. if rxn.reactant_stoichiometry != None and \ rxn.product_stoichiometry != None: for rid, stoich_list in list(rxn.reactant_stoichiometry.items()): for stoich in stoich_list: rxn_add_stoich(srxn, rid, -float(stoich), is_product=False) for rid, stoich_list in list(rxn.product_stoichiometry.items()): for stoich in stoich_list: rxn_add_stoich(srxn, rid, stoich, is_product=True) # Handle the case where the model was created using the SloppyCell # API, in which case reactants and products are inferred from their # stoichiometries else: for rid, stoich in list(rxn.stoichiometry.items()): rxn_add_stoich(srxn, rid, stoich) formula = rxn.kineticLaw.replace('**', '^') try: kl = libsbml.KineticLaw(formula) except NotImplementedError: kl = libsbml.KineticLaw(sbml_level, sbml_version) kl.setFormula(formula) srxn.setKineticLaw(kl) srxn.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addReaction(srxn) for id, e in list(net.events.items()): try: se = libsbml.Event(id) except NotImplementedError: se = libsbml.Event(sbml_level, sbml_version) se.setId(id) se.setName(e.name) formula = e.trigger.replace('**', '^') formula = formula.replace('and_func(', 'and(') formula = formula.replace('or_func(', 'or(') ast = libsbml.parseFormula(formula) if ast is None: raise ValueError('Problem parsing event trigger: %s. Problem may ' 'be use of relational operators (< and >) rather ' 'than libsbml-friendly functions lt and gt.' % formula) try: se.setTrigger(ast) except TypeError: try: trigger = libsbml.Trigger(ast) except NotImplementedError: trigger = libsbml.Trigger(sbml_level, sbml_version) trigger.setMath(ast) se.setTrigger(trigger) formula = str(e.delay).replace('**', '^') try: se.setDelay(libsbml.parseFormula(formula)) except TypeError: try: se.setDelay(libsbml.Delay(libsbml.parseFormula(formula))) except NotImplementedError: delay = libsbml.Delay(sbml_level, sbml_version) delay.setMath(libsbml.parseFormula(formula)) se.setDelay(delay) for varId, formula in list(e.event_assignments.items()): try: sea = libsbml.EventAssignment() except NotImplementedError: sea = libsbml.EventAssignment(sbml_level, sbml_version) sea.setVariable(varId) formula = str(formula).replace('**', '^') formula = formula.replace('and_func(', 'and(') formula = formula.replace('or_func(', 'or(') ast = libsbml.parseFormula(formula) replaceTime(ast) sea.setMath(ast) se.addEventAssignment(sea) se.setMetaId('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addEvent(se) for id, con in list(net.constraints.items()): try: scon = libsbml.Constraint() except NotImplementedError: scon = libsbml.Constraint(sbml_level, sbml_version) scon.setId(con.id) scon.setName(con.name) formula = con.trigger.replace('**', '^') ast = libsbml.parseFormula(formula) if ast is None: raise ValueError( 'Problem parsing constraint math: %s. Problem may ' 'be use of relational operators (< and >) rather ' 'than libsbml-friendly functions lt and gt.' % formula) scon.setMath(ast) se.setcon('SloppyCell_{0:05d}'.format(metaId)) metaId += 1 m.addConstraint(scon) d = libsbml.SBMLDocument(sbml_level, sbml_version) d.setModel(m) sbmlStr = libsbml.writeSBMLToString(d) return sbmlStr
def to_tex(self, d_tex=None, eqn=True, filepath='', landscape=True, margin=2): """ Input: d_tex: a mapping... """ _repl = exprmanip.sub_for_vars _raisepower = lambda tu: tu[0] ** tu[1] def _2tex_pid(pid): if pid.startswith('Vf_') or pid.startswith('Vb_'): pid = '%s^{%s}' % tuple(pid.split('_')) if pid.count('_') == 2: pid = '%s^{%s}_{%s}' % tuple(pid.split('_')) return pid d_tex = dict(zip(self.pids, [_2tex_pid(pid) for pid in self.pids]) +\ d_tex.items()) _2tex = lambda sympyexpr:\ sympy.latex(sympyexpr, mode='plain', long_frac_ratio=10, mul_symbol='dot', symbol_names=butil.chkeys(d_tex, lambda k: sympy.symbols(k))) _rm1pt0 = lambda expr: re.sub('(?<![0-9])1.0\s*\\\cdot', '', expr) lines = [] lines.append(r'\documentclass{article}') lines.append(r'\usepackage{amsmath,fullpage,longtable,array,calc,mathastext,breqn,xcolor}') if landscape == True: lines.append(r'\usepackage[a4paper,landscape,margin=1in]{geometry}') else: lines.append(r'\usepackage[a4paper,margin=%fin]{geometry}'%margin) lines.append(r'\begin{document}') coefs_r = [] yids = [] for poly in self: termstrs = [] leadingcoef_r = sympy.Poly(poly.coeffs()[0], r).coeffs()[0] for monom_X, coef_X in poly.terms(): coef_X = sympy.simplify(coef_X, ratio=1) poly_r = sympy.Poly(coef_X, r) coefs_r.extend([coef_r/leadingcoef_r for coef_r in poly_r.coeffs()]) monom_X = sympy.prod(map(_raisepower, zip(X, monom_X))) monomstr_X = _2tex(monom_X) if monomstr_X == '1': monomstr_X = '' monomstr_X = '\\textcolor{red}{%s}' % monomstr_X termstrs_r = [] for monom_r, coef_r in poly_r.terms(): coefstr_r = _rm1pt0(_2tex(coef_r)) if coef_r.is_Add: coefstr_r = '\left('+ coefstr_r +'\\right)' monom_r = sympy.prod(map(_raisepower, zip(r, monom_r))) monomstr_r = _2tex(monom_r) if monomstr_r == '1': monomstr_r = '' monomstr_r = '\\textcolor{blue}{%s}' % monomstr_r termstrs_r.append(coefstr_r + '\t' + monomstr_r) coefstr_X = '\\left(' + '+'.join(termstrs_r) + '\\right)' yids.append((ixid, str(monom_X), str(monom_r))) termstrs.append(coefstr_X + '\t' + monomstr_X) linestr = '\\begin{dmath} \n' + '+'.join(termstrs) + '=0\n\end{dmath} \n\n' lines.append(linestr.replace('+-', '-')) lines.append('\\end{document}') if filepath: fh = file(filepath, 'w') fh.write(os.linesep.join(lines)) fh.close() coefs_r = [_rm1pt0(str(coef)) for coef in coefs_r] str_h = str(coefs_r).replace("'", "") str_Dh = str([[exprmanip.diff_expr(coef, pid) for pid in self.pids] for coef in coefs_r]).replace("'", "") def h(p): self.update(p=p) return np.array(eval(str_h, self.varvals.to_dict())) def Dh(p): self.update(p=p) return np.array(eval(str_Dh, self.varvals.to_dict())) coefs = predict.Predict(f=h, Df=Dh, pids=self.pids, p0=self.p0, yids=yids, funcform=coefs_r) return coefs
def fromSBMLString(sbmlStr, id = None, duplicate_rxn_params=False): r = libsbml.SBMLReader() d = r.readSBMLFromString(sbmlStr) if d.getNumErrors(): message = 'libSBML reported errors in SBML file. Try running file '\ 'through the online validator: '\ 'http://www.sbml.org/Facilities/Validator . Specific errors '\ 'noted are: ' errors = [] for ii in range(d.getNumErrors()): pm = d.getError(ii) errors.append(pm.getMessage()) raise ValueError(message + '; '.join(errors)) m = d.getModel() modelId = m.getId() if (id == None) and (modelId == ''): raise ValueError('Network id not specified in SBML or passed in.') elif id is not None: modelId = id rn = Network_mod.Network(id = modelId, name = m.getName()) for f in m.getListOfFunctionDefinitions(): id, name = f.getId(), f.getName() math = f.getMath() variables = [] for ii in range(math.getNumChildren() - 1): variables.append(libsbml.formulaToString(math.getChild(ii))) math = libsbml.formulaToString(math.getRightChild()) rn.addFunctionDefinition(id, variables, math) for c in m.getListOfCompartments(): id, name = c.getId(), c.getName() size = c.getSize() isConstant = c.getConstant() rn.addCompartment(id = id, size = size, isConstant = isConstant, name = name) for s in m.getListOfSpecies(): id, name = s.getId(), s.getName() compartment = s.getCompartment() if s.isSetInitialConcentration(): iC = s.getInitialConcentration() elif s.isSetInitialAmount(): iC = s.getInitialAmount() else: iC = 1 isBC, isConstant = s.getBoundaryCondition(), s.getConstant() xml_text = s.toSBML() uniprot_ids = set([entry[1:].split('"')[0] for entry in xml_text.split('uniprot')[1:]]) rn.addSpecies(id = id, compartment = compartment, initialConcentration = iC, isConstant = isConstant, is_boundary_condition = isBC, name = name, uniprot_ids = uniprot_ids) for p in m.getListOfParameters(): parameter = createNetworkParameter(p) rn.addVariable(parameter) for rxn in m.getListOfReactions(): id, name = rxn.getId(), rxn.getName() kL = rxn.getKineticLaw() kLFormula = kL.getFormula() substitution_dict = {} # Deal with parameters defined within reactions for p in kL.getListOfParameters(): parameter = createNetworkParameter(p) # If a parameter with this name already exists, **and it has a # different value than this parameter** we rename this parameter # instance by prefixing it with the rxn name so there isn't a # clash. if parameter.id in rn.variables.keys(): logger.warn('Parameter %s appears in two different reactions ' 'in SBML file.' % parameter.id) if parameter.value != rn.variables.get(parameter.id).value or\ duplicate_rxn_params: oldId = parameter.id parameter.id = id + '_' + parameter.id substitution_dict[oldId] = parameter.id logger.warn('It has different values in the two positions ' 'so we are creating a new parameter %s.' % (parameter.id)) else: logger.warn('It has the same value in the two positions ' 'so we are only defining one parameter %s. ' 'This behavior can be changed with the option ' 'duplicate_rxn_params = True' % (parameter.id)) if parameter.id not in rn.variables.keys(): rn.addVariable(parameter) kLFormula = ExprManip.sub_for_vars(kLFormula, substitution_dict) # Assemble the stoichiometry. SBML has the annoying trait that # species can appear as both products and reactants and 'cancel out' # For each species appearing in the reaction, we build up a string # representing the stoichiometry. Then we'll simplify that string and # see whether we ended up with a float value in the end. stoichiometry = {} reactant_stoichiometry = {} product_stoichiometry = {} for reactant in rxn.getListOfReactants(): species = reactant.getSpecies() stoichiometry.setdefault(species, '0') stoich = reactant.getStoichiometryMath() stoich = stoichToString(reactant, stoich) stoichiometry[species] += '-(%s)' % stoich if species in reactant_stoichiometry: reactant_stoichiometry[species].append(stoich) else: reactant_stoichiometry[species] = [stoich] for product in rxn.getListOfProducts(): species = product.getSpecies() stoichiometry.setdefault(species, '0') stoich = product.getStoichiometryMath() stoich = stoichToString(product, stoich) stoichiometry[species] += '+(%s)' % stoich if species in product_stoichiometry: product_stoichiometry[species].append(stoich) else: product_stoichiometry[species] = [stoich] for species, stoich in stoichiometry.items(): stoich = ExprManip.simplify_expr(stoich) try: # Try converting the string to a float. stoich = float(stoich) except ValueError: pass stoichiometry[species] = stoich for modifier in rxn.getListOfModifiers(): stoichiometry.setdefault(modifier.getSpecies(), 0) rn.addReaction(id = id, stoichiometry = stoichiometry, kineticLaw = kLFormula, reactant_stoichiometry = reactant_stoichiometry, product_stoichiometry = product_stoichiometry) for ii, r in enumerate(m.getListOfRules()): if r.getTypeCode() == libsbml.SBML_ALGEBRAIC_RULE: math = libsbml.formulaToString(r.getMath()) rn.add_algebraic_rule(math) else: variable = r.getVariable() math = libsbml.formulaToString(r.getMath()) if r.getTypeCode() == libsbml.SBML_ASSIGNMENT_RULE: rn.addAssignmentRule(variable, math) elif r.getTypeCode() == libsbml.SBML_RATE_RULE: rn.addRateRule(variable, math) for ii, e in enumerate(m.getListOfEvents()): id, name = e.getId(), e.getName() if id == '': id = 'event%i' % ii try: # For libSBML 3.0 trigger_math = e.getTrigger().getMath() except AttributeError: # For older versions trigger_math = e.getTrigger() trigger = libsbml.formulaToString(trigger_math) trigger = trigger.replace('or(','or_func(') trigger = trigger.replace('and(','and_func(') if e.getDelay() is not None: try: # For libSBML 3.0 delay_math = e.getDelay().getMath() except AttributeError: # For older versions delay_math = e.getDelay() delay = libsbml.formulaToString(delay_math) else: delay = 0 timeUnits = e.getTimeUnits() eaDict = KeyedList() for ea in e.getListOfEventAssignments(): ea_formula = libsbml.formulaToString(ea.getMath()) ea_formula = ea_formula.replace('or(','or_func(') ea_formula = ea_formula.replace('and(','and_func(') eaDict.set(ea.getVariable(), ea_formula) rn.addEvent(id = id, trigger = trigger, eventAssignments = eaDict, delay = delay, name = name) for ii, con in enumerate(m.getListOfConstraints()): id, name = con.getId(), con.getName() if id == '': id = 'constraint%i' % ii trigger_math = con.getMath() trigger = libsbml.formulaToString(trigger_math) if con.isSetMessage(): message = con.getMessage() else: message = None rn.addConstraint(id = id, trigger = trigger, message = message, name = name) return rn