Description
Description
When using a single model, whether the return type of the model function is a numpy.ndarray
or a list
does not affect the result. However, when using a CompositeModel that combines two models (e.g. by +
operation), fitting fails if both model functions return a list
. In contrast, if at least one of the model functions returns a numpy.ndarray
, the fit succeeds.
The issue arises due to the _residual()
method of the model class, which throws an error message while calculating diff = model - data
. Here, model is the output of eval()
. The problem appears to be that the length of model
is twice that of data
and the output of each model function when using two lists as the return type. It is likely that the bug is caused by appending the lists instead of adding them element-wise during Model evaluation.
Using two lists as return:
import numpy as np
from lmfit.model import Model
def lin1(x, k):
rtrn = [k*x1 for x1 in x]
return rtrn
def lin2(x, k):
rtrn = [k * x1 for x1 in x]
return rtrn
y=np.linspace(0, 100, 100)
x=np.linspace(0, 100, 100)
Model1 = Model(lin1, independent_vars=["x"], prefix="m1_")
Model2 = Model(lin2, independent_vars=["x"], prefix="m2_")
ModelSum = Model1 + Model2
pars = ModelSum.make_params()
pars.add('m1_k', value=0.5)
pars.add('m2_k', value=0.5)
result = ModelSum.fit(y, params=pars, x=x)
print(result.fit_report())
Error message:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-8-ad2e6908430e> in <module>
27 pars.add('m1_k', value=0.5)
28 pars.add('m2_k', value=0.5)
---> 29 result = ModelSum.fit(y, params=pars, x=x)
30 print(result.fit_report())
~/anaconda3/lib/python3.8/site-packages/lmfit/model.py in fit(self, data, params, weights, method, iter_cb, scale_covar, verbose, fit_kws, nan_policy, calc_covar, max_nfev, **kwargs)
1088 nan_policy=self.nan_policy, calc_covar=calc_covar,
1089 max_nfev=max_nfev, **fit_kws)
-> 1090 output.fit(data=data, weights=weights)
1091 output.components = self.components
1092 return output
~/anaconda3/lib/python3.8/site-packages/lmfit/model.py in fit(self, data, params, weights, method, nan_policy, **kwargs)
1445 self.userkws.update(kwargs)
1446 self.init_fit = self.model.eval(params=self.params, **self.userkws)
-> 1447 _ret = self.minimize(method=self.method)
1448
1449 for attr in dir(_ret):
~/anaconda3/lib/python3.8/site-packages/lmfit/minimizer.py in minimize(self, method, params, **kws)
2377 val.lower().startswith(user_method)):
2378 kwargs['method'] = val
-> 2379 return function(**kwargs)
2380
2381
~/anaconda3/lib/python3.8/site-packages/lmfit/minimizer.py in leastsq(self, params, max_nfev, **kws)
1701 result.call_kws = lskws
1702 try:
-> 1703 lsout = scipy_leastsq(self.__residual, variables, **lskws)
1704 except AbortFitException:
1705 pass
~/anaconda3/lib/python3.8/site-packages/scipy/optimize/_minpack_py.py in leastsq(func, x0, args, Dfun, full_output, col_deriv, ftol, xtol, gtol, maxfev, epsfcn, factor, diag)
411 if not isinstance(args, tuple):
412 args = (args,)
--> 413 shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
414 m = shape[0]
415
~/anaconda3/lib/python3.8/site-packages/scipy/optimize/_minpack_py.py in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
24 def _check_func(checker, argname, thefunc, x0, args, numinputs,
25 output_shape=None):
---> 26 res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
27 if (output_shape is not None) and (shape(res) != output_shape):
28 if (output_shape[0] != 1):
~/anaconda3/lib/python3.8/site-packages/lmfit/minimizer.py in __residual(self, fvars, apply_bounds_transformation)
588 raise AbortFitException(f"fit aborted: too many function evaluations {self.max_nfev}")
589
--> 590 out = self.userfcn(params, *self.userargs, **self.userkws)
591
592 if callable(self.iter_cb):
~/anaconda3/lib/python3.8/site-packages/lmfit/model.py in _residual(self, params, data, weights, **kwargs)
798 raise ValueError(msg)
799
--> 800 diff = model - data
801
802 if diff.dtype == complex:
ValueError: operands could not be broadcast together with shapes (200,) (100,)
Using at least one numpy.ndarray
as return:
import numpy as np
from lmfit.model import Model
def lin1(x, k):
rtrn = k*x
return rtrn
def lin2(x, k):
rtrn = [k * x1 for x1 in x]
return rtrn
y=np.linspace(0, 100, 100)
x=np.linspace(0, 100, 100)
Model1 = Model(lin1, independent_vars=["x"], prefix="m1_")
Model2 = Model(lin2, independent_vars=["x"], prefix="m2_")
ModelSum = Model1 + Model2
pars = ModelSum.make_params()
pars.add('m1_k', value=0.5)
pars.add('m2_k', value=0.5)
result = ModelSum.fit(y, params=pars, x=x)
print(result.fit_report())
Fit succeeds!
Version information
Python: 3.8.3 (default, Jul 2 2020, 16:21:59)
[GCC 7.3.0]
lmfit: 1.2.1, scipy: 1.10.1, numpy: 1.23.4,asteval: 0.9.29, uncertainties: 3.1.6
If you need any further information, please let me know,
Kind regards,
Julian