Hi,
I'm trying to speed up fitting by using numba. I havn't got much experience in using numba, however, it seems that it gives a speed up of roughly a factor of four for simple functions:
from lmfit.models import moffat
from numba import jit
import numpy as np
@jit
def moffat_numba(x, amplitude=1., center=0., sigma=1., beta=1.):
return amplitude / (((x - center)/sigma)**2 + 1)**beta
if __name__ == '__main__':
import time
xs = np.linspace(-4, 4, 100)
ys = moffat(xs, 2.5, 0, 0.5, 1.)
np.random.seed(1)
moffat_numba(xs) # call it once becaues it's getting compiled at first call I guess?!
t = time.time()
for i in range(100000):
moffat(xs)
t2 = time.time()
for i in range(100000):
moffat_numba(xs)
t3 = time.time()
print 'without numba', t2-t
print 'numba', t3-t2
gives me
without numba 1.10714912415
numba 0.231529951096
However, when I try to build a lmfit Model that uses the numba functions I get an error:
from lmfit.models import *
from lmfit.lineshapes import *
from numba import jit
@jit
def moffat_numba(x, amplitude=1., center=0., sigma=1., beta=1.):
return amplitude / (((x - center)/sigma)**2 + 1)**beta
class MoffatModelnumba(Model):
__doc__ = moffat.__doc__ + COMMON_DOC if moffat.__doc__ else ""
def __init__(self, *args, **kwargs):
super(MoffatModelnumba, self).__init__(moffat_numba, *args, **kwargs)
self.set_param_hint('fwhm', expr="2*%ssigma*sqrt(2**(1.0/%sbeta)-1)" % (self.prefix, self.prefix))
def guess(self, data, x=None, negative=False, **kwargs):
pars = guess_from_peak(self, data, x, negative, ampscale=0.5, sigscale=1.)
return update_param_vals(pars, self.prefix, **kwargs)
if __name__ == '__main__':
import time
xs = np.linspace(-4, 4, 100)
ys = moffat(xs, 2.5, 0, 0.5, 1.)
np.random.seed(1)
moffat_numba(xs)
t = time.time()
mod = MoffatModel()
for i in range(100):
yn = ys + 0.1*np.random.normal(size=len(xs))
pars = mod.guess(yn, xs)
out = mod.fit(yn,pars, x=xs)
t2 = time.time()
mod = MoffatModelnumba()
for i in range(100):
yn = ys + 0.1*np.random.normal(size=len(xs))
pars = mod.guess(yn, xs)
out = mod.fit(yn,pars, x=xs)
t3 = time.time()
print 'without numba', t2-t
print 'numba', t3-t2
Gives me:
File "xxxxxx/.PyCharm50/config/scratches/scratch_35", line 12, in __init__
super(MoffatModelnumba, self).__init__(moffat_numba, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/lmfit/model.py", line 113, in __init__
self._parse_params()
File "/usr/local/lib/python2.7/dist-packages/lmfit/model.py", line 159, in _parse_params
argspec = inspect.getargspec(self.func)
File "/usr/lib/python2.7/inspect.py", line 816, in getargspec
raise TypeError('{!r} is not a Python function'.format(func))
TypeError: CPUDispatcher(<function moffat_numba at 0x7f0d5203d230>) is not a Python function
I don't know enough about lmfit to judge if there is an easy way around that issue and how much work it is to make the Model class accept numba functions.
However, I think it could be a great enhancement ?
Best,
Julian