[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