[Tutor] constructing semi-arbitrary functions
"André Walker-Loud <walksloud@gmail.com>"
walksloud at gmail.com
Mon Feb 17 20:23:39 CET 2014
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
More information about the Tutor
mailing list