Hello python tutors, I am utilizing a 3rd party numerical minimization routine. This routine requires an input function, which takes as arguments, only the variables with which to solve for. But I don’t want to define all possible input functions, in a giant switch, but rather, if I know I am fitting a polynomial, I would like to just pass a list of parameters and have the code know how to construct this function.
To construct for example, a chisq function, you must pass not only the variables to solve for, but also the data, uncertainties, and perhaps other arguments. So it requires a little hacking to get it to work. With the help of my friends and looking at similar code, I have come up with two ways that work under my simple test cases, and I have a few questions about them. The 3rd party minimizer utilizes the .func_code.co_varnames and .func_code.co_argcount to determine the name and number of variables to minimize. eg. > g = lambda x,c_0,c_1: c_0 + c_1 * x > g.func_code.co_varnames ('x', 'c_0', 'c_1’) > g.func_code.co_argcount 3 so what is needed is a function > def f(c_0,c_1): > …#construct chi_sq(c_0,c_1,x,y,…) ========= METHOD 1: make classes to define functions #create a function_structure which can be used to make the necessary func_code class FunctionStruct: def __init__(self, **kwds): self.__dict__.update(kwds) def __str__(self): return self.__dict__.__str__() def __repr__(self): return self.__str__() def __getitem__(self, s): return self.__dict__[s] # then make a polynomial class which constructs a polynomial of order len(pars) class PolynomialFunc: def __init__(self,x,pars): self.pars = pars self.func_code = FunctionStruct(#create the variables used by minimizer co_argcount = len(pars), co_varnames = tuple(['c_%i'%(i) for i in range(len(pars))]) ) def __call__(self): self.poly = 0. for n in range(self.func_code.co_argcount): self.poly += self.pars[n] * x **n return self.poly # eg create a polynomial of order (1) which can be used in the minimizer f = PolynomialFunc(x,[c_0,c_1]) f.func_code.co_varnames ('c_0', 'c_1’) ========= METHOD 2: use strings, exec and globals to construct function def minimize(pars,x,y,dy): global g_x, g_y, g_dy g_x = x; g_y = y; g_dy = dy argnames = ['c_%i'%(i) for i in range(len(pars))] funcargs = ", ".join(argnames) funcdef = 'def chisq_mn(' + funcargs + '):\n' funcdef += ' global g_x, g_y, g_dy\n' funcdef += ' return chisq(['+funcargs+'],g_x,g_y,g_dy)\n’ #chisq is defined in same file # evaluate string and build function print "funcdef=", funcdef exec funcdef in globals() m = ThirdParty.setup_minimize(chisq_mn) for i in range(len(pars)): m.values[argnames[i]] = pars[i] m.errors[argnames[i]] = 0.1 * pars[i] return m # then we can define f = minimize(pars,x,y,dy,chisq_func) Question 1: Is there a better way to accomplish (my hopefully clear) goals? Question 2: In method 1, is there a way to combine the two classes, or is the FunctionStruct necessary? I want to do something *like* self.func_code.co_varnames = tuple(['c_%i'%(i) for i in range(len(pars))]) self.func_code.co_argcount = len(pars) but I was not sure how to first define ‘self.func_code’. Can you define it as an empty container somehow (is that the right way to describe it)? Question 3: The 2nd method is in some sense simpler - I just hack together a string, but I need to use global variables, which I know can be problematic. Which method, 1 or 2, is in general better? One answer, is whatever works, but I am trying to also improve my programming skills, so I am wondering from a software perspective, which is preferable? Thanks, Andre _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor