Pythonization supporting argument unpacking?

Hello. This is my first forum post, so please forgive any transgressions. :smiley:

I noticed some PyROOT behaviour recently and I don’t know if it’s a bug or if this is the way it’s supposed to be, so I’ll just describe what I’m doing and hope you can help me with this…

I’ve been working on a sort of Python interface to TMinuit for performing simple one-dimensional function fits to data points. To take full advantage of Python’s flexibility I have defined a fit function in Python which always takes the independent variable (x) as its first argument and the parameters as keyword arguments:

def expo(x, amplitude=.8, xcoefficient=.5):
    return amplitude * exp(xcoefficient * x)

I’ve implemented a function (calculate_chi2) to calculate the chi^2 for a given parameter set and defined a FCN for TMinuit using the usual syntax (function_to_minimize), which I passed to TMinuit using SetFCN.

# calculate the chi^2 for the current parameters 
def calculate_chi2(parameters):
    result = 0.0
    for i in range(number_of_points):
        x = xdata[i] 

        # This doesn't work, presumably because the unpacking operator `*'
        # doesn't behave well when used with buffers... (?):
        fit_function_value_at_x = expo(x, *parameters)

        y = ydata[i] 
        result += ((y-fit_function_value_at_x)*(y-fit_function_value_at_x))
    return result
# this is passed to TMinuit.SetFCN
def function_to_minimize(number_of_parameters, derivatives, f, parameters, internal_flag):
    f[0] = calculate_chi2(parameters)
# refer TMinuit to the aforementioned FCN
gMinuit = TMinuit(number_of_parameters)
gMinuit.SetFCN(function_to_minimize)
        

The result was that everything failed with a MemoryError. Debugging on the Python side says the culprit is the line "fit_function_value_at_x = expo(x, parameters)" in calculate_chi2. Also, I expected the parameter vector to be passed as a Python list, or in any case as an iterable, so the unpacking operator () will work on it.

I don’t really understand the mechanics of C++ communicating with Python, but is there any way the pythonization of TMinuit could be improved upon/expanded, so the unpacking operator could be used in the above code without causing a MemoryError?

I’ve found a temporary workaround by putting the parameters in a Python iterable (e.g. a list) and unpacking that when passing to the fit function. What I thought this would do is actually bring C++ data to the Python side, so that Python (and the * operator) can work with it, but I’m afraid this translation doesn’t scale well with large inputs (making fits with a lot of plot points slower than necessary).

# temporary workaround -> put the parameters in a list, which
# can be unpacked by `*'... (slow?)
parameter_list = []
for j in range(number_of_parameters):
    parameter_list.append(parameters[j])

fit_function_value_at_x = expo(x, *parameter_list)

Any thoughts?

Hi,

the C++ function needs to work with an actual array. The problem then is whether the size of the array can be known (so as to make it iterable). It can sometimes (and I originally implemented that), but there were also use cases where the size was unknown (so the length-setting code was removed, leaving the array “unlimited”).

Cheers,
Wim