def generate_aux(self): auxnames = list(self.fspec._auxfnspecs.keys()) # User aux fn interface uafi = {} # protectednames = auxnames + self.fspec._protected_mathnames + \ # self.fspec._protected_randomnames + \ # self.fspec._protected_scipynames + \ # self.fspec._protected_specialfns + \ # ['abs', 'and', 'or', 'not', 'True', 'False'] auxfns = self.builtin_aux # the internal functions may be used by user-defined functions, # so need them to be accessible to processTokens when parsing self.fspec._pyauxfns = auxfns # add the user-defined function names for cross-referencing checks # (without their definitions) for auxname in auxnames: self.fspec._pyauxfns[auxname] = None # don't process the built-in functions -> unique fns because # they are global definitions existing throughout the # namespace self.fspec._protected_auxnames.extend(['Jacobian', 'Jacobian_pars']) # protected names are the names that must not be used for # user-specified auxiliary fn arguments protectednames = self.fspec.pars + self.fspec.inputs \ + ['abs', 'pow', 'and', 'or', 'not', 'True', 'False'] \ + self.fspec._protected_auxnames + auxnames \ + self.fspec._protected_numpynames \ + self.fspec._protected_scipynames \ + self.fspec._protected_specialfns \ + self.fspec._protected_macronames \ + self.fspec._protected_mathnames \ + self.fspec._protected_randomnames \ + self.fspec._protected_reusenames # checks for user-defined auxiliary fns # name map for fixing inter-auxfn references auxfn_namemap = {} specials_base = self.fspec.pars + self.fspec._protected_auxnames \ + ['abs', 'pow', 'and', 'or', 'not', 'True', 'False'] \ + auxnames + self.fspec._protected_scipynames \ + self.fspec._protected_numpynames + self.fspec._protected_specialfns \ + self.fspec._protected_macronames + self.fspec._protected_mathnames \ + self.fspec._protected_randomnames + self.fspec._protected_reusenames for auxname in auxnames: auxinfo = self.fspec._auxfnspecs[auxname] try: if len(auxinfo) != 2: raise ValueError('auxinfo tuple must be of length 2') except TypeError: raise TypeError('fnspecs argument must contain pairs') # auxinfo[0] = tuple or list of parameter names # auxinfo[1] = string containing body of function definition assert isinstance(auxinfo[0], list), ('aux function arguments ' 'must be given as a list') assert isinstance(auxinfo[1], str), ('aux function specification ' 'must be a string ' 'of the function code') # Process Jacobian functions, etc., specially, if present if auxname == 'Jacobian': if not compareList(auxinfo[0], ['t'] + self.fspec.vars): print(['t'] + self.fspec.vars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError( "Invalid argument list given in Jacobian.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ('^', '**')]): auxstr = convertPowers(auxstr, 'pow') specvars = self.fspec.vars specvars.sort() specdict = {}.fromkeys(specvars) if len(specvars) == 1: assert '[' not in auxstr, \ "'[' character invalid in Jacobian for 1D system" assert ']' not in auxstr, \ "']' character invalid in Jacobian for 1D system" specdict[specvars[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedPy( specvars, specdict, specials=specials + specials_base) body_processed = self._specStrParse(specvars, body_processed_dict, 'xjac', specials=specials + specials_base) auxstr_py = self._generate_fun('_auxfn_Jac', reusestr + body_processed, 'xjac', specvars) # check Jacobian m = n = len(specvars) specdict_check = {}.fromkeys(specvars) for specname in specvars: temp = body_processed_dict[specname] specdict_check[specname] = \ count_sep(temp.replace("[", "").replace("]", "")) + 1 body_processed = "" for row in range(m): if specdict_check[specvars[row]] != n: print("Row %i: " % m + specdict[specvars[row]]) print("Found length %i" % specdict_check[specvars[row]]) raise ValueError("Jacobian should be %sx%s" % (m, n)) elif auxname == 'Jacobian_pars': if not compareList(auxinfo[0], ['t'] + self.fspec.pars): print(['t'] + self.fspec.pars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError( "Invalid argument list given in Jacobian.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ('^', '**')]): auxstr = convertPowers(auxstr, 'pow') specvars = self.fspec.pars specvars.sort() specdict = {}.fromkeys(self.fspec.vars) if len(specvars) == len(self.fspec.vars) == 1: assert '[' not in auxstr, \ "'[' character invalid in Jacobian for 1D system" assert ']' not in auxstr, \ "']' character invalid in Jacobian for 1D system" specdict[specvars[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, self.fspec.vars) reusestr, body_processed_dict = self._processReusedPy( self.fspec.vars, specdict, specials=specials + specials_base) body_processed = self._specStrParse(self.fspec.vars, body_processed_dict, 'pjac', specials=specials + specials_base) auxstr_py = self._generate_fun('_auxfn_Jac_p', reusestr + body_processed, 'pjac', self.fspec.vars) # check Jacobian n = len(specvars) m = len(self.fspec.vars) specdict_check = {}.fromkeys(self.fspec.vars) for specname in self.fspec.vars: temp = body_processed_dict[specname] specdict_check[specname] = \ count_sep(temp.replace("[", "").replace("]", "")) + 1 body_processed = "" for row in range(m): try: if specdict_check[self.fspec.vars[row]] != n: print("Row %i: " % m + specdict[self.fspec.vars[row]]) print("Found length %i" % specdict_check[self.fspec.vars[row]]) raise ValueError( "Jacobian w.r.t. pars should be %sx%s" % (m, n)) except IndexError: print("\nFound:\n") info(specdict) raise ValueError( "Jacobian w.r.t. pars should be %sx%s" % (m, n)) elif auxname == 'massMatrix': if not compareList(auxinfo[0], ['t'] + self.fspec.vars): print(['t'] + self.fspec.vars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError( "Invalid argument list given in Mass Matrix.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ('^', '**')]): auxstr = convertPowers(auxstr, 'pow') specvars = self.fspec.vars specvars.sort() specdict = {}.fromkeys(specvars) if len(specvars) == 1: assert '[' not in auxstr, \ "'[' character invalid in mass matrix for 1D system" assert ']' not in auxstr, \ "']' character invalid in mass matrix for 1D system" specdict[list(specvars.values())[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedPy( specvars, specdict, specials=specials + specials_base) body_processed = self._specStrParse(specvars, body_processed_dict, 'xmat', specials=specials + specials_base) auxstr_py = self._generate_fun('_auxfn_massMatrix', reusestr + body_processed, 'xmat', specvars) # check matrix m = n = len(specvars) specdict_check = {}.fromkeys(specvars) for specname in specvars: specdict_check[specname] = 1 + \ count_sep( body_processed_dict[specname].replace("[", "").replace("]", "")) body_processed = "" for row in range(m): if specdict_check[specvars[row]] != n: print("Row %i: " % m + specdict[specvars[row]]) print("Found length %i" % specdict_check[specvars[row]]) raise ValueError("Mass matrix should be %sx%s" % (m, n)) else: user_parstr = makeParList(auxinfo[0]) # `parsinps` is always added to allow reference to own # parameters if user_parstr == '': # no arguments, user calls as fn() auxparstr = 'parsinps' else: auxparstr = 'parsinps, ' + user_parstr auxstr_py = 'def _auxfn_' + auxname + '(ds, ' + auxparstr \ + '):\n' auxparlist = auxparstr.replace(" ", "").split(",") badparnames = intersect(auxparlist, remain(protectednames, auxnames)) if badparnames != []: print("Bad parameter names in auxiliary function %s: %r" % (auxname, badparnames)) # print(str(auxinfo[0])) # print(str(auxparlist)) raise ValueError( "Cannot use protected names (including" " globally visible system parameters for auxiliary " "function arguments") # special symbols to allow in parsing function body specials = auxparlist specials.remove('parsinps') illegalterms = remain(self.fspec.vars + self.fspec.auxvars, specials) auxstr = auxinfo[1] if any([pt in auxstr for pt in ('^', '**')]): auxstr = convertPowers(auxstr, 'pow') reusestr, body_processed_dict = self._processReusedPy( [auxname], {auxname: auxstr}, specials=specials + specials_base, dovars=False, illegal=illegalterms) body_processed = self._specStrParse([auxname], body_processed_dict, specials=specials + specials_base, dovars=False, noreturndefs=True, illegal=illegalterms) auxstr_py += reusestr + _indentstr + 'return ' \ + body_processed # syntax validation done in makeUniqueFn try: auxfns[auxname] = makeUniqueFn(auxstr_py) # Note: this automatically updates self.fspec._pyauxfns too except Exception: print('Error in supplied auxiliary spec dictionary code') raise auxfn_namemap['ds.' + auxname] = 'ds.' + auxfns[auxname][1] # prepare user-interface wrapper function (not method) if specials == [''] or specials == []: fn_args = '' else: fn_args = ',' + ','.join(specials) fn_elts = [ 'def ', auxname, '(self', fn_args, ',__parsinps__=None):\n\t', 'if __parsinps__ is None:\n\t\t', '__parsinps__=self.map_ixs(self.genref)\n\t', 'return self.genref.', auxfns[auxname][1], '(__parsinps__', fn_args, ')\n' ] uafi[auxname] = ''.join(fn_elts) # resolve inter-auxiliary function references for auxname, auxspec in auxfns.items(): dummyQ = QuantSpec('dummy', auxspec[0], preserveSpace=True, treatMultiRefs=False) dummyQ.mapNames(auxfn_namemap) auxfns[auxname] = (dummyQ(), auxspec[1]) self.fspec._user_auxfn_interface = uafi self.fspec._protected_auxnames.extend(auxnames) return auxfns
def generate_aux(self): auxnames = list(self.fspec._auxfnspecs.keys()) # User aux fn interface uafi = {} # protectednames = auxnames + self.fspec._protected_mathnames + \ # self.fspec._protected_randomnames + \ # self.fspec._protected_scipynames + \ # self.fspec._protected_specialfns + \ # ['abs', 'and', 'or', 'not', 'True', 'False'] auxfns = self.builtin_aux # the internal functions may be used by user-defined functions, # so need them to be accessible to processTokens when parsing self.fspec._pyauxfns = auxfns # add the user-defined function names for cross-referencing checks # (without their definitions) for auxname in auxnames: self.fspec._pyauxfns[auxname] = None # don't process the built-in functions -> unique fns because # they are global definitions existing throughout the # namespace self.fspec._protected_auxnames.extend(["Jacobian", "Jacobian_pars"]) # protected names are the names that must not be used for # user-specified auxiliary fn arguments protectednames = ( self.fspec.pars + self.fspec.inputs + ["abs", "pow", "and", "or", "not", "True", "False"] + self.fspec._protected_auxnames + auxnames + self.fspec._protected_numpynames + self.fspec._protected_scipynames + self.fspec._protected_specialfns + self.fspec._protected_macronames + self.fspec._protected_mathnames + self.fspec._protected_randomnames + self.fspec._protected_reusenames ) # checks for user-defined auxiliary fns # name map for fixing inter-auxfn references auxfn_namemap = {} specials_base = ( self.fspec.pars + self.fspec._protected_auxnames + ["abs", "pow", "and", "or", "not", "True", "False"] + auxnames + self.fspec._protected_scipynames + self.fspec._protected_numpynames + self.fspec._protected_specialfns + self.fspec._protected_macronames + self.fspec._protected_mathnames + self.fspec._protected_randomnames + self.fspec._protected_reusenames ) for auxname in auxnames: auxinfo = self.fspec._auxfnspecs[auxname] try: if len(auxinfo) != 2: raise ValueError("auxinfo tuple must be of length 2") except TypeError: raise TypeError("fnspecs argument must contain pairs") # auxinfo[0] = tuple or list of parameter names # auxinfo[1] = string containing body of function definition assert isinstance(auxinfo[0], list), "aux function arguments " "must be given as a list" assert isinstance(auxinfo[1], six.string_types), ( "aux function specification " "must be a string of the function code" ) # Process Jacobian functions, etc., specially, if present if auxname == "Jacobian": if not compareList(auxinfo[0], ["t"] + self.fspec.vars): print(["t"] + self.fspec.vars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError("Invalid argument list given in Jacobian.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ("^", "**")]): auxstr = convertPowers(auxstr, "pow") specvars = self.fspec.vars specvars.sort() specdict = {}.fromkeys(specvars) if len(specvars) == 1: assert "[" not in auxstr, "'[' character invalid in Jacobian for 1D system" assert "]" not in auxstr, "']' character invalid in Jacobian for 1D system" specdict[specvars[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedPy( specvars, specdict, specials=specials + specials_base ) body_processed = self._specStrParse( specvars, body_processed_dict, "xjac", specials=specials + specials_base ) auxstr_py = self._generate_fun("_auxfn_Jac", reusestr + body_processed, "xjac", specvars) # check Jacobian m = n = len(specvars) specdict_check = {}.fromkeys(specvars) for specname in specvars: temp = body_processed_dict[specname] specdict_check[specname] = count_sep(temp.replace("[", "").replace("]", "")) + 1 body_processed = "" for row in range(m): if specdict_check[specvars[row]] != n: print("Row %i: " % m + specdict[specvars[row]]) print("Found length %i" % specdict_check[specvars[row]]) raise ValueError("Jacobian should be %sx%s" % (m, n)) elif auxname == "Jacobian_pars": if not compareList(auxinfo[0], ["t"] + self.fspec.pars): print(["t"] + self.fspec.pars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError("Invalid argument list given in Jacobian.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ("^", "**")]): auxstr = convertPowers(auxstr, "pow") specvars = self.fspec.pars specvars.sort() specdict = {}.fromkeys(self.fspec.vars) if len(specvars) == len(self.fspec.vars) == 1: assert "[" not in auxstr, "'[' character invalid in Jacobian for 1D system" assert "]" not in auxstr, "']' character invalid in Jacobian for 1D system" specdict[specvars[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, self.fspec.vars) reusestr, body_processed_dict = self._processReusedPy( self.fspec.vars, specdict, specials=specials + specials_base ) body_processed = self._specStrParse( self.fspec.vars, body_processed_dict, "pjac", specials=specials + specials_base ) auxstr_py = self._generate_fun("_auxfn_Jac_p", reusestr + body_processed, "pjac", self.fspec.vars) # check Jacobian n = len(specvars) m = len(self.fspec.vars) specdict_check = {}.fromkeys(self.fspec.vars) for specname in self.fspec.vars: temp = body_processed_dict[specname] specdict_check[specname] = count_sep(temp.replace("[", "").replace("]", "")) + 1 body_processed = "" for row in range(m): try: if specdict_check[self.fspec.vars[row]] != n: print("Row %i: " % m + specdict[self.fspec.vars[row]]) print("Found length %i" % specdict_check[self.fspec.vars[row]]) raise ValueError("Jacobian w.r.t. pars should be %sx%s" % (m, n)) except IndexError: print("\nFound:\n") info(specdict) raise ValueError("Jacobian w.r.t. pars should be %sx%s" % (m, n)) elif auxname == "massMatrix": if not compareList(auxinfo[0], ["t"] + self.fspec.vars): print(["t"] + self.fspec.vars) print("Auxinfo =" + str(auxinfo[0])) raise ValueError("Invalid argument list given in Mass Matrix.") auxparlist = ["t", "x", "parsinps"] # special symbols to allow in parsing function body specials = ["t", "x"] auxstr = auxinfo[1] if any([pt in auxstr for pt in ("^", "**")]): auxstr = convertPowers(auxstr, "pow") specvars = self.fspec.vars specvars.sort() specdict = {}.fromkeys(specvars) if len(specvars) == 1: assert "[" not in auxstr, "'[' character invalid in mass matrix for 1D system" assert "]" not in auxstr, "']' character invalid in mass matrix for 1D system" specdict[list(specvars.values())[0]] = auxstr else: specdict = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedPy( specvars, specdict, specials=specials + specials_base ) body_processed = self._specStrParse( specvars, body_processed_dict, "xmat", specials=specials + specials_base ) auxstr_py = self._generate_fun("_auxfn_massMatrix", reusestr + body_processed, "xmat", specvars) # check matrix m = n = len(specvars) specdict_check = {}.fromkeys(specvars) for specname in specvars: specdict_check[specname] = 1 + count_sep( body_processed_dict[specname].replace("[", "").replace("]", "") ) body_processed = "" for row in range(m): if specdict_check[specvars[row]] != n: print("Row %i: " % m + specdict[specvars[row]]) print("Found length %i" % specdict_check[specvars[row]]) raise ValueError("Mass matrix should be %sx%s" % (m, n)) else: user_parstr = makeParList(auxinfo[0]) # `parsinps` is always added to allow reference to own # parameters if user_parstr == "": # no arguments, user calls as fn() auxparstr = "parsinps" else: auxparstr = "parsinps, " + user_parstr auxstr_py = "def _auxfn_" + auxname + "(ds, " + auxparstr + "):\n" auxparlist = auxparstr.replace(" ", "").split(",") badparnames = intersect(auxparlist, remain(protectednames, auxnames)) if badparnames != []: print("Bad parameter names in auxiliary function %s: %r" % (auxname, badparnames)) # print(str(auxinfo[0])) # print(str(auxparlist)) raise ValueError( "Cannot use protected names (including" " globally visible system parameters for auxiliary " "function arguments" ) # special symbols to allow in parsing function body specials = auxparlist specials.remove("parsinps") illegalterms = remain(self.fspec.vars + self.fspec.auxvars, specials) auxstr = auxinfo[1] if any([pt in auxstr for pt in ("^", "**")]): auxstr = convertPowers(auxstr, "pow") reusestr, body_processed_dict = self._processReusedPy( [auxname], {auxname: auxstr}, specials=specials + specials_base, dovars=False, illegal=illegalterms ) body_processed = self._specStrParse( [auxname], body_processed_dict, specials=specials + specials_base, dovars=False, noreturndefs=True, illegal=illegalterms, ) auxstr_py += reusestr + _indentstr + "return " + body_processed # syntax validation done in makeUniqueFn try: auxfns[auxname] = makeUniqueFn(auxstr_py) # Note: this automatically updates self.fspec._pyauxfns too except Exception: print("Error in supplied auxiliary spec dictionary code") raise auxfn_namemap["ds." + auxname] = "ds." + auxfns[auxname][1] # prepare user-interface wrapper function (not method) if specials == [""] or specials == []: fn_args = "" else: fn_args = "," + ",".join(specials) fn_elts = [ "def ", auxname, "(self", fn_args, ",__parsinps__=None):\n\t", "if __parsinps__ is None:\n\t\t", "__parsinps__=self.map_ixs(self.genref)\n\t", "return self.genref.", auxfns[auxname][1], "(__parsinps__", fn_args, ")\n", ] uafi[auxname] = "".join(fn_elts) # resolve inter-auxiliary function references for auxname, auxspec in auxfns.items(): dummyQ = QuantSpec("dummy", auxspec[0], preserveSpace=True, treatMultiRefs=False) dummyQ.mapNames(auxfn_namemap) auxfns[auxname] = (dummyQ(), auxspec[1]) self.fspec._user_auxfn_interface = uafi self.fspec._protected_auxnames.extend(auxnames) return auxfns
def generate_aux(self): auxnames = list(self.fspec._auxfnspecs.keys()) auxfns = {} # parameter and variable definitions # sorted version of var and par names sorted version of par # names (vars not #define'd in aux functions unless Jacobian) vnames = self.fspec.vars pnames = self.fspec.pars vnames.sort() pnames.sort() for auxname in auxnames: assert auxname not in ["auxvars", "vfieldfunc"], ( "auxiliary function name '" + auxname + "' clashes with internal" " names" ) # must add parameter argument so that we can name # parameters inside the functions! this would either # require all calls to include this argument (yuk!) or # else we add these extra parameters automatically to # every call found in the .c code (as is done currently. # this is still an untidy solution, but there you go...) for auxname in auxnames: auxspec = self.fspec._auxfnspecs[auxname] assert len(auxspec) == 2, "auxspec tuple must be of length 2" if not isinstance(auxspec[0], list): print("Found type " + type(auxspec[0])) print("Containing: " + auxspec[0]) raise TypeError("aux function arguments " "must be given as a list") if not isinstance(auxspec[1], str): print("Found type " + type(auxspec[1])) print("Containing: " + auxspec[1]) raise TypeError( "aux function specification " "must be a string of the function code" ) # Process Jacobian functions specially, if present if auxname == "Jacobian": sig = "void jacobian(" if not compareList(auxspec[0], ["t"] + self.fspec.vars): print(["t"] + self.fspec.vars) print("Auxspec =" + auxspec[0]) raise ValueError("Invalid argument list given in Jacobian.") if any([pt in auxspec[1] for pt in ("^", "**")]): auxstr = convertPowers(auxspec[1], "pow") else: auxstr = auxspec[1] parlist = "unsigned n_, unsigned np_, double t, double *Y_," ismat = True sig += ( parlist + " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" ) specvars = self.fspec.vars specvars.sort() n = len(specvars) m = n specdict_temp = {}.fromkeys(specvars) if m == 1: assert ( "[" not in auxstr ), "'[' character invalid in Jacobian for 1D system" assert ( "]" not in auxstr ), "']' character invalid in Jacobian for 1D system" specdict_temp[specvars[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedC( specvars, specdict_temp ) specdict = {}.fromkeys(specvars) for specname in specvars: temp = body_processed_dict[specname] specdict[specname] = splitargs( temp.replace("[", "").replace("]", "") ) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += ( "f_[" + str(col) + "][" + str(row) + "] = " + specdict[specvars[row]][col] + ";\n" ) except IndexError: raise ValueError("Jacobian should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} elif auxname == "Jacobian_pars": sig = "void jacobianParam(" if not compareList(auxspec[0], ["t"] + self.fspec.pars): print(["t"] + self.fspec.pars) print("Auxspec =" + auxspec[0]) raise ValueError("Invalid argument list given in Jacobian.") parlist = "unsigned n_, unsigned np_, double t, double *Y_," if any([pt in auxspec[1] for pt in ("^", "**")]): auxstr = convertPowers(auxspec[1], "pow") else: auxstr = auxspec[1] ismat = True # specials = ["t","Y_","n_","np_","wkn_","wk_"] sig += ( parlist + " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" ) specvars = self.fspec.pars specvars.sort() n = len(specvars) if n == 0: raise ValueError( "Cannot have a Jacobian w.r.t. pars" " because no pars are defined" ) m = len(self.fspec.vars) specdict_temp = {}.fromkeys(self.fspec.vars) if m == n == 1: assert ( "[" not in auxstr ), "'[' character invalid in Jacobian for 1D system" assert ( "]" not in auxstr ), "']' character invalid in Jacobian for 1D system" specdict_temp[list(self.fspec.vars.values())[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr(auxstr, self.fspec.vars, m) reusestr, body_processed_dict = self._processReusedC( self.fspec.vars, specdict_temp ) specdict = {}.fromkeys(self.fspec.vars) for specname in self.fspec.vars: temp = body_processed_dict[specname] specdict[specname] = splitargs( temp.replace("[", "").replace("]", "") ) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += ( "f_[" + str(col) + "][" + str(row) + "] = " + specdict[self.fspec.vars[row]][col] + ";\n" ) except (IndexError, KeyError): print("%d %r" % (n, specvars)) print("\nFound matrix:\n") info(specdict) raise ValueError("Jacobian should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} elif auxname == "massMatrix": sig = "void massMatrix(" if not compareList(auxspec[0], ["t"] + self.fspec.vars): raise ValueError("Invalid argument list given in Mass Matrix.") if any([pt in auxspec[1] for pt in ("^", "**")]): auxstr = convertPowers(auxspec[1], "pow") else: auxstr = auxspec[1] parlist = "unsigned n_, unsigned np_," ismat = True # specials = ["n_","np_","wkn_","wk_"] sig += ( parlist + " double t, double *Y_, double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" ) specvars = self.fspec.vars specvars.sort() n = len(specvars) m = n specdict_temp = {}.fromkeys(specvars) if m == 1: assert ( "[" not in auxstr ), "'[' character invalid in mass matrix for 1D system" assert ( "]" not in auxstr ), "']' character invalid in mass matrix for 1D system" specdict_temp[list(specvars.values())[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr(auxstr, specvars, m) reusestr, body_processed_dict = self._processReusedC( specvars, specdict_temp ) specdict = {}.fromkeys(specvars) for specname in specvars: temp = ( body_processed_dict[specname].replace("[", "").replace("]", "") ) specdict[specname] = splitargs(temp) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += ( "f_[" + str(col) + "][" + str(row) + "] = " + specdict[specvars[row]][col] + ";\n" ) except KeyError: raise ValueError("Mass matrix should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} else: ismat = False sig = "double " + auxname + "(" parlist = "" namemap = {} for parname in auxspec[0]: if parname == "": continue parlist += "double " + "__" + parname + "__, " namemap[parname] = "__" + parname + "__" sig += parlist + "double *p_, double *wk_, double *xv_)" auxstr = auxspec[1] if any([pt in auxspec[1] for pt in ("^", "**")]): auxstr = convertPowers(auxstr, "pow") prep_auxstr = self._processSpecialC(auxstr) prep_auxstr_quant = QuantSpec( "prep_q", prep_auxstr.replace(" ", "").replace("\n", ""), treatMultiRefs=False, preserveSpace=True, ) # have to do name map now in case function's formal arguments # coincide with state variable names, which may get tied up # in reused terms and not properly matched to the formal args. prep_auxstr_quant.mapNames(namemap) auxspec = (auxspec[0], prep_auxstr_quant()) reusestr, auxspec_processedDict = self._processReusedC( [auxname], {auxname: auxspec[1]} ) # addition of parameter done in Generator code # dummyQ = QuantSpec('dummy', auxspec_processedDict[auxname]) # auxspec_processed = "" # add pars argument to inter-aux fn call # auxfn_found = False # then expect a left brace next # for tok in dummyQ: # if auxfn_found: # expect left brace in this tok # if tok == '(': # auxspec_processed += tok + 'p_, ' # auxfn_found = False # else: # raise ValueError("Problem parsing inter-auxiliary" # " function call") # elif tok in self.fspec.auxfns and tok not in \ # ['Jacobian', 'Jacobian_pars']: # auxfn_found = True # auxspec_processed += tok # else: # auxspec_processed += tok # body_processed = "return "+auxspec_processed + ";\n\n" # add underscore to local names, to avoid clash with global # '#define' names dummyQ = QuantSpec( "dummy", auxspec_processedDict[auxname], treatMultiRefs=False, preserveSpace=True, ) body_processed = "return " * (not ismat) + dummyQ() + ";\n\n" # auxspecstr = sig + " {\n\n" + pardefines + vardefines*ismat \ auxspecstr = ( sig + " {\n\n" + "\n" + (len(reusestr) > 0) * "/* reused term definitions */\n" + reusestr + (len(reusestr) > 0) * "\n" + body_processed + "}" ) # + parundefines + varundefines*ismat + "}" # sig as second entry, whereas Python-coded specifications # have the fn name there auxfns[auxname] = (auxspecstr, sig) # Don't apply #define's for built-in functions auxfns["heav"] = ( "int heav(double x_, double *p_, double *wk_, double *xv_) {\n" + " if (x_>0.0) {return 1;} else {return 0;}\n}", "int heav(double x_, double *p_, double *wk_, double *xv_)", ) auxfns["__rhs_if"] = ( "double __rhs_if(int cond_, double e1_, " + "double e2_, double *p_, double *wk_, double *xv_) {\n" + " if (cond_) {return e1_;} else {return e2_;};\n}", "double __rhs_if(int cond_, double e1_, double e2_, double *p_, double *wk_, double *xv_)", ) auxfns["__maxof2"] = ( "double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" + "if (e1_ > e2_) {return e1_;} else {return e2_;};\n}", "double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)", ) auxfns["__minof2"] = ( "double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" + "if (e1_ < e2_) {return e1_;} else {return e2_;};\n}", "double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)", ) auxfns["__maxof3"] = ( "double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ > temp_) {return e3_;} else {return temp_;};\n}", "double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)", ) auxfns["__minof3"] = ( "double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ < temp_) {return e3_;} else {return temp_;};\n}", "double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)", ) auxfns["__maxof4"] = ( "double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ > temp_) {temp_ = e3_;};\nif (e4_ > temp_) {return e4_;} else {return temp_;};\n}", "double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)", ) auxfns["__minof4"] = ( "double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ < temp_) {temp_ = e3_;};\nif (e4_ < temp_) {return e4_;} else {return temp_;};\n}", "double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)", ) # temporary placeholders for these built-ins... cases_ic = "" cases_index = "" for i in range(len(self.fspec.vars)): if i == 0: command = "if" else: command = "else if" vname = self.fspec.vars[i] cases_ic += ( " " + command + " (strcmp(varname, " + '"' + vname + '"' + ")==0)\n\treturn gICs[" + str(i) + "];\n" ) cases_index += ( " " + command + " (strcmp(name, " + '"' + vname + '"' + ")==0)\n\treturn " + str(i) + ";\n" ) # add remaining par names for getindex for i in range(len(self.fspec.pars)): pname = self.fspec.pars[i] cases_index += ( " else if" + " (strcmp(name, " + '"' + pname + '"' + ")==0)\n\treturn " + str(i + len(self.fspec.vars)) + ";\n" ) cases_ic += ( """ else {\n\tfprintf(stderr, "Invalid variable name %s for """ + """initcond call\\n", varname);\n\treturn 0.0/0.0;\n\t}\n""" ) cases_index += ( """ else {\n\tfprintf(stderr, "Invalid name %s for """ + """getindex call\\n", name);\n\treturn 0.0/0.0;\n\t}\n""" ) auxfns["initcond"] = ( "double initcond(char *varname, double *p_, double *wk_, double *xv_) {\n" + "\n" + cases_ic + "}", "double initcond(char *varname, double *p_, double *wk_, double *xv_)", ) auxfns["getindex"] = ( "int getindex(char *name, double *p_, double *wk_, double *xv_) {\n" + "\n" + cases_index + "}", "int getindex(char *name, double *p_, double *wk_, double *xv_)", ) auxfns["globalindepvar"] = ( "double globalindepvar(double t, double *p_, double *wk_, double *xv_)" + " {\n return globalt0+t;\n}", "double globalindepvar(double t, double *p_, double *wk_, double *xv_)", ) auxfns["getbound"] = ( "double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_) {\n" + " return gBds[which_bd][getindex(name)];\n}", "double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_)", ) return auxfns
def _processSpecialC(self, specStr): """Pre-process 'if' statements and names of 'abs' and 'sign' functions, as well as logical operators. """ qspec = QuantSpec("spec", specStr, treatMultiRefs=False) qspec.mapNames( { "abs": "fabs", "sign": "signum", "mod": "fmod", "and": "&&", "or": "||", "not": "!", "True": 1, "False": 0, "max": "__maxof", "min": "__minof", } ) qtoks = qspec.parser.tokenized # default value new_specStr = str(qspec) if "if" in qtoks: new_specStr = "" num_ifs = qtoks.count("if") if_ix = -1 ix_continue = 0 for _ in range(num_ifs): if_ix = qtoks[if_ix + 1 :].index("if") + if_ix + 1 new_specStr += "".join(qtoks[ix_continue:if_ix]) + "__rhs_if(" rbrace_ix = findEndBrace(qtoks[if_ix + 1 :]) + if_ix + 1 ix_continue = rbrace_ix + 1 new_specStr += "".join(qtoks[if_ix + 2 : ix_continue]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec("spec", new_specStr) qtoks = qspec.parser.tokenized if "__minof" in qtoks: new_specStr = "" num = qtoks.count("__minof") n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1 :].index("__minof") + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1 :]) + n_ix + 1 ix_continue = rbrace_ix + 1 # assert qtoks[n_ix+2] == '[', "Error in min() syntax" # assert qtoks[rbrace_ix-1] == ']', "Error in min() syntax" # new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2 : ix_continue].count(",") + 1 if num_args > 4: raise NotImplementedError( "Max of more than 4 arguments not currently supported in C" ) new_specStr += "__minof%s(" % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2 : ix_continue] if q not in ("[", "]")] ) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec("spec", new_specStr) qtoks = qspec.parser.tokenized if "__maxof" in qtoks: new_specStr = "" num = qtoks.count("__maxof") n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1 :].index("__maxof") + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1 :]) + n_ix + 1 ix_continue = rbrace_ix + 1 # assert qtoks[n_ix+2] == '[', "Error in max() syntax" # assert qtoks[rbrace_ix-1] == ']', "Error in max() syntax" # new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2 : ix_continue].count(",") + 1 if num_args > 4: raise NotImplementedError( "Min of more than 4 arguments not currently supported in C" ) new_specStr += "__maxof%s(" % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2 : ix_continue] if q not in ("[", "]")] ) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec("spec", new_specStr) qtoks = qspec.parser.tokenized return new_specStr
def _map_names(spec, namemap): if spec and namemap: q = QuantSpec('__temp__', spec, preserveSpace=True) q.mapNames(namemap) spec = q() return spec
def _processSpecialC(self, specStr): """Pre-process 'if' statements and names of 'abs' and 'sign' functions, as well as logical operators. """ qspec = QuantSpec('spec', specStr, treatMultiRefs=False) qspec.mapNames({'abs': 'fabs', 'sign': 'signum', 'mod': 'fmod', 'and': '&&', 'or': '||', 'not': '!', 'True': 1, 'False': 0, 'if': '__rhs_if', 'max': '__maxof', 'min': '__minof'}) qtoks = qspec.parser.tokenized # default value new_specStr = str(qspec) # NOTE: This simple iterative parsing of the arguments means that # user cannot nest calls to min() or max() with eachother if '__minof' in qtoks: new_specStr = "" num = qtoks.count('__minof') n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1:].index('__minof') + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1:]) + n_ix + 1 ix_continue = rbrace_ix + 1 #assert qtoks[n_ix+2] == '[', "Error in min() syntax" #assert qtoks[rbrace_ix-1] == ']', "Error in min() syntax" #new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2:ix_continue].count(',') + 1 if num_args > 4: raise NotImplementedError( "Max of more than 4 arguments not currently supported in C") new_specStr += '__minof%s(' % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2:ix_continue] if q not in ('[', ']')]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec('spec', new_specStr) qtoks = qspec.parser.tokenized if '__maxof' in qtoks: new_specStr = "" num = qtoks.count('__maxof') n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1:].index('__maxof') + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1:]) + n_ix + 1 ix_continue = rbrace_ix + 1 #assert qtoks[n_ix+2] == '[', "Error in max() syntax" #assert qtoks[rbrace_ix-1] == ']', "Error in max() syntax" #new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2:ix_continue].count(',') + 1 if num_args > 4: raise NotImplementedError( "Min of more than 4 arguments not currently supported in C") new_specStr += '__maxof%s(' % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2:ix_continue] if q not in ('[', ']')]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec('spec', new_specStr) qtoks = qspec.parser.tokenized return new_specStr
def generate_aux(self): auxnames = list(self.fspec._auxfnspecs.keys()) auxfns = {} # parameter and variable definitions # sorted version of var and par names sorted version of par # names (vars not #define'd in aux functions unless Jacobian) vnames = self.fspec.vars pnames = self.fspec.pars vnames.sort() pnames.sort() for auxname in auxnames: assert auxname not in ['auxvars', 'vfieldfunc'], \ ("auxiliary function name '" + auxname + "' clashes with internal" " names") # must add parameter argument so that we can name # parameters inside the functions! this would either # require all calls to include this argument (yuk!) or # else we add these extra parameters automatically to # every call found in the .c code (as is done currently. # this is still an untidy solution, but there you go...) for auxname in auxnames: auxspec = self.fspec._auxfnspecs[auxname] assert len(auxspec) == 2, 'auxspec tuple must be of length 2' if not isinstance(auxspec[0], list): print("Found type " + type(auxspec[0])) print("Containing: " + auxspec[0]) raise TypeError('aux function arguments ' 'must be given as a list') if not isinstance(auxspec[1], str): print("Found type " + type(auxspec[1])) print("Containing: " + auxspec[1]) raise TypeError('aux function specification ' 'must be a string of the function code') # Process Jacobian functions specially, if present if auxname == 'Jacobian': sig = "void jacobian(" if not compareList(auxspec[0], ['t'] + self.fspec.vars): print(['t'] + self.fspec.vars) print("Auxspec =" + auxspec[0]) raise ValueError( "Invalid argument list given in Jacobian.") if any([pt in auxspec[1] for pt in ('^', '**')]): auxstr = convertPowers(auxspec[1], 'pow') else: auxstr = auxspec[1] parlist = "unsigned n_, unsigned np_, double t, double *Y_," ismat = True sig += parlist + \ " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" specvars = self.fspec.vars specvars.sort() n = len(specvars) m = n specdict_temp = {}.fromkeys(specvars) if m == 1: assert '[' not in auxstr, \ "'[' character invalid in Jacobian for 1D system" assert ']' not in auxstr, \ "']' character invalid in Jacobian for 1D system" specdict_temp[specvars[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr(auxstr, specvars) reusestr, body_processed_dict = self._processReusedC( specvars, specdict_temp) specdict = {}.fromkeys(specvars) for specname in specvars: temp = body_processed_dict[specname] specdict[specname] = splitargs( temp.replace("[", "").replace("]", "")) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += "f_[" + str(col) + "][" + str(row) \ + "] = " + specdict[specvars[row]][col] + ";\n" except IndexError: raise ValueError( "Jacobian should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} elif auxname == 'Jacobian_pars': sig = "void jacobianParam(" if not compareList(auxspec[0], ['t'] + self.fspec.pars): print(['t'] + self.fspec.pars) print("Auxspec =" + auxspec[0]) raise ValueError( "Invalid argument list given in Jacobian.") parlist = "unsigned n_, unsigned np_, double t, double *Y_," if any([pt in auxspec[1] for pt in ('^', '**')]): auxstr = convertPowers(auxspec[1], 'pow') else: auxstr = auxspec[1] ismat = True # specials = ["t","Y_","n_","np_","wkn_","wk_"] sig += parlist + \ " double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" specvars = self.fspec.pars specvars.sort() n = len(specvars) if n == 0: raise ValueError("Cannot have a Jacobian w.r.t. pars" " because no pars are defined") m = len(self.fspec.vars) specdict_temp = {}.fromkeys(self.fspec.vars) if m == n == 1: assert '[' not in auxstr, \ "'[' character invalid in Jacobian for 1D system" assert ']' not in auxstr, \ "']' character invalid in Jacobian for 1D system" specdict_temp[list(self.fspec.vars.values())[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr( auxstr, self.fspec.vars, m) reusestr, body_processed_dict = self._processReusedC( self.fspec.vars, specdict_temp) specdict = {}.fromkeys(self.fspec.vars) for specname in self.fspec.vars: temp = body_processed_dict[specname] specdict[specname] = splitargs( temp.replace("[", "").replace("]", "")) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += "f_[" + str(col) + "][" + str(row) \ + "] = " + specdict[ self.fspec.vars[row]][col] + ";\n" except (IndexError, KeyError): print("%d %r" % (n, specvars)) print("\nFound matrix:\n") info(specdict) raise ValueError( "Jacobian should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} elif auxname == 'massMatrix': sig = "void massMatrix(" if not compareList(auxspec[0], ['t'] + self.fspec.vars): raise ValueError( "Invalid argument list given in Mass Matrix.") if any([pt in auxspec[1] for pt in ('^', '**')]): auxstr = convertPowers(auxspec[1], 'pow') else: auxstr = auxspec[1] parlist = "unsigned n_, unsigned np_," ismat = True # specials = ["n_","np_","wkn_","wk_"] sig += parlist + \ " double t, double *Y_, double *p_, double **f_, unsigned wkn_, double *wk_, unsigned xvn_, double *xv_)" specvars = self.fspec.vars specvars.sort() n = len(specvars) m = n specdict_temp = {}.fromkeys(specvars) if m == 1: assert '[' not in auxstr, \ "'[' character invalid in mass matrix for 1D system" assert ']' not in auxstr, \ "']' character invalid in mass matrix for 1D system" specdict_temp[list(specvars.values())[0]] = auxstr else: specdict_temp = parseMatrixStrToDictStr( auxstr, specvars, m) reusestr, body_processed_dict = self._processReusedC( specvars, specdict_temp) specdict = {}.fromkeys(specvars) for specname in specvars: temp = body_processed_dict[ specname].replace("[", "").replace("]", "") specdict[specname] = splitargs(temp) body_processed = "" # C integrators expect column-major matrices for col in range(n): for row in range(m): try: body_processed += "f_[" + str(col) + "][" + str(row) \ + "] = " + specdict[specvars[row]][col] + ";\n" except KeyError: raise ValueError( "Mass matrix should be %sx%s" % (m, n)) body_processed += "\n" auxspec_processedDict = {auxname: body_processed} else: ismat = False sig = "double " + auxname + "(" parlist = "" namemap = {} for parname in auxspec[0]: if parname == '': continue parlist += "double " + "__" + parname + "__, " namemap[parname] = '__' + parname + '__' sig += parlist + "double *p_, double *wk_, double *xv_)" auxstr = auxspec[1] if any([pt in auxspec[1] for pt in ('^', '**')]): auxstr = convertPowers(auxstr, 'pow') prep_auxstr = self._processSpecialC(auxstr) prep_auxstr_quant = QuantSpec('prep_q', prep_auxstr.replace( ' ', '').replace('\n', ''), treatMultiRefs=False, preserveSpace=True) # have to do name map now in case function's formal arguments # coincide with state variable names, which may get tied up # in reused terms and not properly matched to the formal args. prep_auxstr_quant.mapNames(namemap) auxspec = (auxspec[0], prep_auxstr_quant()) reusestr, auxspec_processedDict = self._processReusedC( [auxname], {auxname: auxspec[1]}) # addition of parameter done in Generator code # dummyQ = QuantSpec('dummy', auxspec_processedDict[auxname]) # auxspec_processed = "" # add pars argument to inter-aux fn call # auxfn_found = False # then expect a left brace next # for tok in dummyQ: # if auxfn_found: # expect left brace in this tok # if tok == '(': # auxspec_processed += tok + 'p_, ' # auxfn_found = False # else: # raise ValueError("Problem parsing inter-auxiliary" # " function call") # elif tok in self.fspec.auxfns and tok not in \ # ['Jacobian', 'Jacobian_pars']: # auxfn_found = True # auxspec_processed += tok # else: # auxspec_processed += tok # body_processed = "return "+auxspec_processed + ";\n\n" # add underscore to local names, to avoid clash with global # '#define' names dummyQ = QuantSpec('dummy', auxspec_processedDict[auxname], treatMultiRefs=False, preserveSpace=True) body_processed = "return " * (not ismat) + dummyQ() + ";\n\n" # auxspecstr = sig + " {\n\n" + pardefines + vardefines*ismat \ auxspecstr = sig + " {\n\n" \ + "\n" + (len(reusestr) > 0) * "/* reused term definitions */\n" \ + reusestr + (len(reusestr) > 0) * "\n" + body_processed \ + "}" # + parundefines + varundefines*ismat + "}" # sig as second entry, whereas Python-coded specifications # have the fn name there auxfns[auxname] = (auxspecstr, sig) # Don't apply #define's for built-in functions auxfns['heav'] = ("int heav(double x_, double *p_, double *wk_, double *xv_) {\n" + " if (x_>0.0) {return 1;} else {return 0;}\n}", "int heav(double x_, double *p_, double *wk_, double *xv_)") auxfns['__rhs_if'] = ("double __rhs_if(int cond_, double e1_, " + "double e2_, double *p_, double *wk_, double *xv_) {\n" + " if (cond_) {return e1_;} else {return e2_;};\n}", "double __rhs_if(int cond_, double e1_, double e2_, double *p_, double *wk_, double *xv_)") auxfns['__maxof2'] = ("double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" + "if (e1_ > e2_) {return e1_;} else {return e2_;};\n}", "double __maxof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)") auxfns['__minof2'] = ("double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_) {\n" + "if (e1_ < e2_) {return e1_;} else {return e2_;};\n}", "double __minof2(double e1_, double e2_, double *p_, double *wk_, double *xv_)") auxfns['__maxof3'] = ("double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ > temp_) {return e3_;} else {return temp_;};\n}", "double __maxof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)") auxfns['__minof3'] = ("double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ < temp_) {return e3_;} else {return temp_;};\n}", "double __minof3(double e1_, double e2_, double e3_, double *p_, double *wk_, double *xv_)") auxfns['__maxof4'] = ("double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ > e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ > temp_) {temp_ = e3_;};\nif (e4_ > temp_) {return e4_;} else {return temp_;};\n}", "double __maxof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)") auxfns['__minof4'] = ("double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_) {\n" + "double temp_;\nif (e1_ < e2_) {temp_ = e1_;} else {temp_ = e2_;};\n" + "if (e3_ < temp_) {temp_ = e3_;};\nif (e4_ < temp_) {return e4_;} else {return temp_;};\n}", "double __minof4(double e1_, double e2_, double e3_, double e4_, double *p_, double *wk_, double *xv_)") # temporary placeholders for these built-ins... cases_ic = "" cases_index = "" for i in range(len(self.fspec.vars)): if i == 0: command = 'if' else: command = 'else if' vname = self.fspec.vars[i] cases_ic += " " + command + " (strcmp(varname, " + '"' + vname + '"'\ + ")==0)\n\treturn gICs[" + str(i) + "];\n" cases_index += " " + command + " (strcmp(name, " + '"' + vname + '"'\ + ")==0)\n\treturn " + str(i) + ";\n" # add remaining par names for getindex for i in range(len(self.fspec.pars)): pname = self.fspec.pars[i] cases_index += " else if" + " (strcmp(name, " + '"' + pname + '"'\ + ")==0)\n\treturn " + str( i + len(self.fspec.vars)) + ";\n" cases_ic += """ else {\n\tfprintf(stderr, "Invalid variable name %s for """ \ + """initcond call\\n", varname);\n\treturn 0.0/0.0;\n\t}\n""" cases_index += """ else {\n\tfprintf(stderr, "Invalid name %s for """ \ + """getindex call\\n", name);\n\treturn 0.0/0.0;\n\t}\n""" auxfns['initcond'] = ("double initcond(char *varname, double *p_, double *wk_, double *xv_) {\n" + "\n" + cases_ic + "}", 'double initcond(char *varname, double *p_, double *wk_, double *xv_)') auxfns['getindex'] = ("int getindex(char *name, double *p_, double *wk_, double *xv_) {\n" + "\n" + cases_index + "}", 'int getindex(char *name, double *p_, double *wk_, double *xv_)') auxfns['globalindepvar'] = ("double globalindepvar(double t, double *p_, double *wk_, double *xv_)" + " {\n return globalt0+t;\n}", 'double globalindepvar(double t, double *p_, double *wk_, double *xv_)') auxfns['getbound'] = \ ("double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_) {\n" + " return gBds[which_bd][getindex(name)];\n}", 'double getbound(char *name, int which_bd, double *p_, double *wk_, double *xv_)') return auxfns
def _processSpecialC(self, specStr): """Pre-process 'if' statements and names of 'abs' and 'sign' functions, as well as logical operators. """ qspec = QuantSpec('spec', specStr, treatMultiRefs=False) qspec.mapNames({'abs': 'fabs', 'sign': 'signum', 'mod': 'fmod', 'and': '&&', 'or': '||', 'not': '!', 'True': 1, 'False': 0, 'max': '__maxof', 'min': '__minof'}) qtoks = qspec.parser.tokenized # default value new_specStr = str(qspec) if 'if' in qtoks: new_specStr = "" num_ifs = qtoks.count('if') if_ix = -1 ix_continue = 0 for _ in range(num_ifs): if_ix = qtoks[if_ix + 1:].index('if') + if_ix + 1 new_specStr += "".join(qtoks[ix_continue:if_ix]) + "__rhs_if(" rbrace_ix = findEndBrace(qtoks[if_ix + 1:]) + if_ix + 1 ix_continue = rbrace_ix + 1 new_specStr += "".join(qtoks[if_ix + 2:ix_continue]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec('spec', new_specStr) qtoks = qspec.parser.tokenized if '__minof' in qtoks: new_specStr = "" num = qtoks.count('__minof') n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1:].index('__minof') + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1:]) + n_ix + 1 ix_continue = rbrace_ix + 1 #assert qtoks[n_ix+2] == '[', "Error in min() syntax" #assert qtoks[rbrace_ix-1] == ']', "Error in min() syntax" #new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2:ix_continue].count(',') + 1 if num_args > 4: raise NotImplementedError( "Max of more than 4 arguments not currently supported in C") new_specStr += '__minof%s(' % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2:ix_continue] if q not in ('[', ']')]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec('spec', new_specStr) qtoks = qspec.parser.tokenized if '__maxof' in qtoks: new_specStr = "" num = qtoks.count('__maxof') n_ix = -1 ix_continue = 0 for _ in range(num): n_ix = qtoks[n_ix + 1:].index('__maxof') + n_ix + 1 new_specStr += "".join(qtoks[ix_continue:n_ix]) rbrace_ix = findEndBrace(qtoks[n_ix + 1:]) + n_ix + 1 ix_continue = rbrace_ix + 1 #assert qtoks[n_ix+2] == '[', "Error in max() syntax" #assert qtoks[rbrace_ix-1] == ']', "Error in max() syntax" #new_specStr += "".join(qtoks[n_ix+3:rbrace_ix-1]) + ")" num_args = qtoks[n_ix + 2:ix_continue].count(',') + 1 if num_args > 4: raise NotImplementedError( "Min of more than 4 arguments not currently supported in C") new_specStr += '__maxof%s(' % str(num_args) new_specStr += "".join( [q for q in qtoks[n_ix + 2:ix_continue] if q not in ('[', ']')]) new_specStr += "".join(qtoks[ix_continue:]) qspec = QuantSpec('spec', new_specStr) qtoks = qspec.parser.tokenized return new_specStr