[Tutor] constructing semi-arbitrary functions
Peter Otten
__peter__ at web.de
Mon Feb 17 21:13:14 CET 2014
André Walker-Loud <walksloud at gmail.com> wrote:
> 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?
I think you are looking for closures:
def make_poly(coeff):
def poly(x):
return sum(c * x ** n for n, c in enumerate(coeff))
return poly
def minimize(x, y, dy):
def chisq_mn(*args):
return chisq(args, x, y, dy)
return chisq_mn
>>> def make_poly(coeff):
... def poly(x):
... return sum(c * x ** n for n, c in enumerate(coeff))
... return poly
...
>>> p = make_poly([1, 0, 1])
>>> for i in range(-4, 5):
... x = i*.25
... print x, "->", p(x)
...
-1.0 -> 2.0
-0.75 -> 1.5625
-0.5 -> 1.25
-0.25 -> 1.0625
0.0 -> 1.0
0.25 -> 1.0625
0.5 -> 1.25
0.75 -> 1.5625
1.0 -> 2.0
More information about the Tutor
mailing list