def _generate_fun(self, name, specstr, resname, specnames, docodeinserts=False): """Generate string with Python code for function `name`""" fdef = self._fn_template.format( name=name, start=self._format_user_code(self.opts["start"]) if docodeinserts else "", spec=specstr, end=self._format_user_code(self.opts["end"]) if docodeinserts else "", result=makeParList(range(len(specnames)), resname), ) return "\n".join([s for s in fdef.split("\n") if s]) + "\n"
def _generate_fun(self, name, specstr, resname, specnames, docodeinserts=False): """Generate string with Python code for function `name`""" fdef = self._fn_template.format( name=name, start=self._format_user_code(self.opts['start']) if docodeinserts else '', spec=specstr, end=self._format_user_code(self.opts['end']) if docodeinserts else '', result=makeParList(range(len(specnames)), resname) ) return '\n'.join([s for s in fdef.split('\n') if s]) + '\n'
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()) # 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