Search code examples
pythonsympyattributeerrorsymfit

AttributeError when evaluating symfit model with substraction in exponent


I've set up a model to fit some data with a substraction in the exponent. It works fine for fits, but when I explicitly evaluate the model I get strange results and an AttributeError: 'Mul' object has no attribute 'exp'.

The problem seems to stem from the way lambdify works and occurs when evaluating a model containing a non-trivial function such as potentiation or exponentiation.

import symfit as sf

a = sf.Parameter('a',1,0,2) #name, initial value, minimum, maximum
b = sf.Parameter('b',1,0,2)
c=sf.Parameter('c',1,0,2)
x, y = sf.variables('x, y')

model=sf.Model({y: a * (c - sf.exp((x) / b))})
model(1,a,b,c)
Out: Ans(y=a*(c + 1))

model=sf.Model({y: a * (1 - sf.exp((x-c) / b))})
model(1,a,b,c)
Traceback (most recent call last):

  File "<ipython-input-9-faba3b52b923>", line 1, in <module>
    model(1,a,b,c)

  File "C:\PortablePrograms\Python\WPy-3670\python-3.6.7.amd64\lib\site-packages\symfit\core\fit.py", line 334, in __call__
    return Ans(*self.eval_components(**bound_arguments.arguments))

  File "C:\PortablePrograms\Python\WPy-3670\python-3.6.7.amd64\lib\site-packages\symfit\core\fit.py", line 296, in eval_components
    return [expr(*args, **kwargs) for expr in self.numerical_components]

  File "C:\PortablePrograms\Python\WPy-3670\python-3.6.7.amd64\lib\site-packages\symfit\core\fit.py", line 296, in <listcomp>
    return [expr(*args, **kwargs) for expr in self.numerical_components]

  File "C:\PortablePrograms\Python\WPy-3670\python-3.6.7.amd64\lib\site-packages\sympy\utilities\lambdify.py", line 444, in wrapper
    return funcarg(*newargs, **kwargsx)

  File "<string>", line 1, in <lambda>

AttributeError: 'Mul' object has no attribute 'exp'

I would expect either the initial value of the parameters to be used or to get a symbolic answer, but get neither.

For the first model the result is therefore both wrong and inconsistant, I would expect

Ans(y=a*(c-exp(1/b)) 

or

Ans(y=a*(c-1)) 

or just simply

Ans(y=0)

For the second model lambdify seems to be unable to parse the expression.

This is also the case when using sf.sqrt(), or sympy.exp(). I'm working in python 3.6.7 and IPython 7.1.1, using Symfit 0.4.6 and Sympy 1.1.1 (as Symfit doesn't work with higher versions accoring to pip).

I can use either model to fit data and evaluate the model with the best-fit parameters as shown in the example here (https://pypi.org/project/symfit/). The line

yfit = model(x=xdata, **fit_result.params)[y] 

doesn't work either (tried on python 2.7, 3.5 and 3.6) unless changed to

yfit = [model(x=x, **fit_result.params) for x in xdata]

Solution

  • Calling a Model in symfit means the symbolic expression is converted into a lambda function, i.e. a normal python function which is no longer symbolic.

    In your example, calling

    sf.Model({y: a * (c - sf.exp((x) / b))})
    

    is equivalent to writing

    lambda x, a, b, c: a * (c - np.exp((x) / b))
    

    Notice that the symbolic exponential has now been changed into a numpy one. From this example you can see that a model is not meant to be called with symbolic expressions, it is meant to be called with numbers or arrays.

    So your call should be changed to

    model(x=1, a=1, b=1, c=1)
    

    If you need to work on a symbolic level, keep working with the expressions in Model directly, the call is there purely for numerical use.

    Lastly, the example in the docs assumes xdata is an array, but in the example it is a list. That should be updated (by me).

    As a final remark, learn to love keyword arguments in python ;). The following are equivalent:

    a = sf.Parameter('a',1,0,2) #name, initial value, minimum, maximum
    a = sf.Parameter('a', value=1, min=0, max=2)
    

    but only one is self-documenting :).