Search code examples
python-2.7curve-fittinglmfit

Python lmfit custom models: set fixed parameter and assigning prefix


I have 2 questions about parameters in the lmfit package.

1.

Is there a way to pre-set the value for parameters for a custom model?

eg.

def my_cust(x,A,b):
    return A*x + b

def gaussian(x, amp, cen, wid):
    return (1.2345*amp/(sqrt(2*pi)*wid)) * exp(-(x-cen)**2 /wid)

mod = Model(my_cust) + Model(gaussian)
pars = mod.make_params(A=11.78,b=25,amp=2000,cen=109.5,wid=17) #initialize all the parameters
results = mod.fit(y,pars,x=x)

In the second last line, for example, amp=2000 initializes the parameter amp. If I wanted to fix this parameter in a built-in model (eg. this one):

params = model.make_params()
params['g1_amplitude'].set(2000, vary=False)

Question 1

Is it possible to fix the value of the parameter amp at 2000 in the mod.fit() line or elsewhere of a custom model?

2.

I am trying to assign prefixes to custom composite models like below:

cust_combination_mod = Model(my_cust, prefix='lin_') + Model(gaussian, prefix='g1_')

When I tried the above line, I got:

  File "build\bdist.win-amd64\egg\lmfit\model.py", line 541, in fit
  File "build\bdist.win-amd64\egg\lmfit\model.py", line 747, in fit
  File "build\bdist.win-amd64\egg\lmfit\minimizer.py", line 1242, in minimize
  File "build\bdist.win-amd64\egg\lmfit\minimizer.py", line 1072, in leastsq
  File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 377, in leastsq
    shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
  File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 26, in _check_func
    res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
  File "build\bdist.win-amd64\egg\lmfit\minimizer.py", line 371, in __residual
  File "build\bdist.win-amd64\egg\lmfit\minimizer.py", line 1432, in _nan_policy
ValueError: The input contains nan values

If there are many custom models, then initializing all the parameters in mod.make_params() (like I showed in 1. above) can be tedious. The issue seems to be discussed here (1,2) but they don't really indicate how to actually assign a prefix to separate components of composite models.

Question 2

Is it possible to assign prefixes to composite custom models in lmfit?


Solution

  • Q1: You can set default values for custom models when defining the model:

    >>> def my_cust(x, a=1.0, b=2.0):
    ...    return a*x + b
    
    >>> lmodel = Model(my_cust)
    >>> params = lmodel.make_params()
    >>> params 
    Parameters([('a', <Parameter 'a', 1.0, bounds=[-inf:inf]>), ('b', <Parameter 'b', 2.0, bounds=[-inf:inf]>)])
    

    and you can fix a parameter after it is created:

    >>> params['a].vary = False
    

    You can also set a "parameter hint" on a model to tell attributes to assign when creating parameters:

    >>> lmodel.set_param_hint('a', vary=False)
    >>> lmodel.set_param_hint('b', min=0)
    >>> params = lmodel.make_params()
    >>> params
    Parameters([('a', <Parameter 'a', value=1.0 (fixed), bounds=[-inf:inf]>), ('b', <Parameter 'b', 2.0, bounds=[0:inf]>)])
    

    Q2: Doing

    >>> comp_model = Model(my_cust, prefix='lin_') + Model(gaussian, prefix='g1_')
    

    should work (and, it does for me). The parameters generated for this will have the proper prefixes, and should be referenced with prefix in Model.make_params:

    >>> params = comp_model.make_params(g1_amp=9, g1_cen=2.0, g1_wid=0.5)
    >>> for name, par in params.items():
    ...    print(name, par)
    ...
    ('lin_a', <Parameter 'lin_a', 1.0, bounds=[-inf:inf]>)
    ('lin_b', <Parameter 'lin_b', 2.0, bounds=[-inf:inf]>)
    ('g1_amp', <Parameter 'g1_amp', 9, bounds=[-inf:inf]>)
    ('g1_cen', <Parameter 'g1_cen', 2.0, bounds=[-inf:inf]>)
    ('g1_wid', <Parameter 'g1_wid', 0.5, bounds=[-inf:inf]>)
    

    If you wanted to preserve the parameter hints set above, you should make the custom model with prefix, set the parameter hints, then make the custom model:

    >>> lmodel = Model(my_cust, prefix='lin_')
    >>> lmodel.set_param_hint('a', vary=False)
    >>> lmodel.set_param_hint('b', min=0)
    >>> comp = lmodel + Model(gauss, prefix='g1_')
    >>> params = comp.make_params(g1_amp=9.0, g1_cen=2.0, g1_wid=0.5)
    >>> for name, par in params.items(): 
    ...    print(name, par)
    ... 
    ('lin_a', <Parameter 'lin_a', value=1.0 (fixed), bounds=[-inf:inf]>)
    ('lin_b', <Parameter 'lin_b', 2.0, bounds=[0:inf]>)
    ('g1_amp', <Parameter 'g1_amp', 9.0, bounds=[-inf:inf]>)
    ('g1_cen', <Parameter 'g1_cen', 2.0, bounds=[-inf:inf]>)
    ('g1_wid', <Parameter 'g1_wid', 0.5, bounds=[-inf:inf]>)
    

    I'm not sure where the exceptions with NaNs was coming from. Perhaps from not giving initial values for parameters (which might cause then to default to -inf) or because there are NaNs in the data being modeled?