How to define a chi2 value function for arbitrary function?
Question: I am doing some data fitting using the pyminuit Python bindings for the minuit
minimisation code (http://code.google.com/p/pyminuit/). The minimiser accepts
a function and uses introspection to extract the parameters to be minimised.
In general, I want to minimise the chi squared value for a dataset given a
particular function to describe the dataset.

My question: Is there a way to define a chi squared function which, given an
arbitrary function with varying numbers of parameters, returns a function
which gives the chi squared value for that function _and_ only contains the
parameters to be minimised in the function argument specification?

Example:

    
    
    from scipy import *
    import minuit
    # Generate some data to fit
    data_x = arange(50)
    noise = 0.3
    data_y = data_x**3 + normal(0.0, noise)
    # Fit function, e.g. a cubic
    fit_func = lambda x, a1, a2, a3, a4: a1 + a2*x + a3*x**2 + a4*x**3
    
    # Minimisation function e.g. chi squared
    # Note this has only the parameters to be minimised in the definition (eg not data_x)
    min_func = lambda a1, a2, a3, a4: sum( (fit_func(data_x, a1, a2, a3, a4) - data_y)**2 / noise**2 )
    

THIS is where I'd like to write something like `min_func =
make_chi2(fit_func)`. I don't know what to do as `data_x` and `data_y` are
only defined outside of the function. The rest of the minimisation routine,
for completeness, looks like:

    
    
    # Initialise minimiser object with initial values
    m = minuit.Minuit(min_func, {'a1': 1.0, 'a2': 1.0, 'a3': 1.0, 'a4': 1.0})
    # Run minimiser
    m.migrad()
    # Print minimised values - example output
    print m.values
    >>> {'a1': 0.000, 'a2': 0.000, 'a3': 0.000, 'a4': 1.000}
    

Thanks for your help in advance!


Answer: Since PyMinuit uses introspection, you have to use introspection, too.
`make_chi_squared()` could be implemented like this:

    
    
    import inspect
    
    chi_squared_template = """
    def chi_squared(%(params)s):
        return (((f(data_x, %(params)s) - data_y) / errors) ** 2).sum()
    """
    
    def make_chi_squared(f, data_x, data_y, errors):
        params = ", ".join(inspect.getargspec(f).args[1:])
        exec chi_squared_template % {"params": params}
        return chi_squared
    

Example usage:

    
    
    import numpy
    
    def f(x, a1, a2, a3, a4):
        return a1 + a2*x + a3*x**2 + a4*x**3
    
    data_x = numpy.arange(50)
    errors = numpy.random.randn(50) * 0.3
    data_y = data_x**3 + errors
    
    chi_squared = make_chi_squared(f, data_x, data_y, errors)
    print inspect.getargspec(chi_squared).args
    

printing

    
    
    ['a1', 'a2', 'a3', 'a4']
    

