tBuLi / symfit

Symbolic Fitting; fitting as it should be.
http://symfit.readthedocs.org
MIT License
235 stars 18 forks source link

Global fit problem - Help needed #174

Closed omergra closed 6 years ago

omergra commented 6 years ago

Hey, I'm trying to fit a pretty complex function, with two shared parameters and two non-shared parameters. Using scipy's curve fit, i got good optimal parameters of v = 520.19071225, D = 55.90232709 which are consistent with previously published results on the same data set. The problem is, that when using symfit, the code does not converge to anything close, not even with initial values, bounds etc. The problem is a global fit problem, and the code below uses 2 data sets instead of 16 that will be eventually needed.
Can someone help\point me in the right direction? what am i doing wrong? Code:


        v = sf.parameters('v_',value=500, min=0, max=1000)[0]
        d = sf.parameters('D_', min=0, max=100)[0]
        y0_p = sf.parameters(', '.join('y0_{}'.format(key) for key in self.data.keys()), min=0, max=1)
        b_p = sf.parameters(', '.join('b_{}'.format(key) for key in self.data.keys()), value=50, min=0, max=100)
        # create variables for symfit
        x = sf.variables('x')[0]
        y_var = sf.variables(', '.join('y_{}'.format(key) for key in self.data.keys()))
        # get fixed & shared params
        dx, a, w2, a2, tau, s, wz2 = self.get_params()
        # create model
        model_dict = sf.Model({
            y:  y0+b*sf.exp(-1*(dst*dx-v*(sf.cos(a))*x)**2/(w2+0.5*a2+4*d*x)) *
                sf.exp(-x * x * (v * sf.sin(a) - dx / tau) * (v * sf.sin(a) - dx / tau) / (
                w2 + 0.5 * a2 + a * d * x))
            for y, y0, b, dst in zip(y_var, y0_p, b_p, dist)
            })
        # get kwargs - arrange data as a dictionary with the variable names as keys
        data_dict = self.create_kwargs_dict(tau, model_dict)
        # x data
        # expand named parameters for function using tuple
        fit = sf.Fit(model_dict, x=data_dict['x'], y_20=data_dict['y_20'])
        res = fit.execute()

Thanks
Omer
pckroon commented 6 years ago

Hi!

I'm sorry, but I find I hard to keep track of what's going on here. Could you print model_dict, your variables and your parameters? And can you check once more that your model is in fact correct (a typo is easily made ;)) Also, d doesn't have an initial value (defaults to 0? I think?)

The bottom line is, what you're trying "should just work", but it's hard to find why or what exactly here.

@tBuLi for the initial parameter values, I think that defaults to 0 now, but it might make more sense to default it to the average of the upper and lower bounds if they're given; half the value of the bound if one is given; and 0 if none are given?

tBuLi commented 6 years ago

Indeed, this example is not self contained, so it is hard to follow exactly whats happening. In principle it should work, however, but why don't you use **data_dict? That's how this will become a global fit right? In the current example you only fit to y_20, which might explain the difference with previous results? So

fit = sf.Fit(model_dict, **data_dict)

In reply to @pckroon, the default is always 1, because 0 could lead to singularities for some models. So it's safer to assume 1. In case of bounds I don't think we actually do anything at the moment to ensure it is within the bounds, we leave that bit of sanity checking to the minimizer at the moment. It would indeed be better to set this to a non-zero value in the middle of the range, unless provided.

omergra commented 6 years ago

Hi, Sorry for the delayed response. The problem was a mistake in the algebra. I changed the initial value to be in the middle of the boundary range. @tBuLi, I use **data_dict, the code I posted was just a simplified example. Thanks for the help, I'm closing this issue.