Repository navigation
Closed
Description
I am using NonLinearLSQFitter and SLSQPFitter to fit some data to a parametric model I implemented. I set some boundaries in the parameters using the bounds={...} argument in the model __init__ method. It seems that the bouns are set correctly, I ran the code in debug mode and the NonLinearLSQFitter._set_bounds() method is reached, for example.
Using NonLinearLSQFitter the bounds are ignored, and using SLSQPFitter the fit returns the initial guess, while reporting success in the fit_info property.
Here's the code I used to trigger the bug:
from astropy.modeling import ParametricModel, Parameter, fitting
import numpy as np
from astropy.modeling.core import _convert_input, _convert_output
import matplotlib.pyplot as plt
class LineModel(ParametricModel):
param_names = ['A', 'B']
def __init__(self, A, B, **cons):
try:
param_dim = len(A)
except TypeError:
param_dim = 1
self._A = Parameter(name='A', val=A, mclass=self, param_dim=param_dim)
self._B = Parameter(name='B', val=B, mclass=self, param_dim=param_dim)
ParametricModel.__init__(self, self.param_names, n_inputs=1, n_outputs=1, param_dim=param_dim, **cons)
self.linear = False
def eval(self, x, params):
A, B = params
return A * x + B
def deriv(self, params, x, dummy=None):
A, B = params
deriv_dict = {}
deriv_dict['A'] = x
deriv_dict['B'] = np.ones_like(x)
derivval = [deriv_dict[par] for par in self.param_names]
return np.array(derivval).T
def __call__(self, x):
x, fmt = _convert_input(x, self.param_dim)
result = self.eval(x, self.param_sets)
return _convert_output(result, fmt)
# These values should be recovered by the fitting procedure.
A = -2.0
B = 0.5
x = np.linspace(-1.0, 1.0, 100)
y = A * x + B + np.random.normal(scale=0.1, size=100)
print 'Original A:', A
print 'Original B:', B
############################################################
# Case 1: NonLinearLSQFitter without bounds, OK.
############################################################
guess_A = 1.1
guess_B = 0.0
line_model = LineModel(guess_A, guess_B)
fitter = fitting.NonLinearLSQFitter(line_model)
fitter(x, y)
print 'NonLinearLSQFitter without bounds:'
print 'A =', line_model.A
print 'B =', line_model.B
plt.ioff()
plt.plot(x, y)
plt.plot(x, line_model(x))
plt.show()
############################################################
# Case 2: NonLinearLSQFitter with bounds.
# A should be out of bounds, but fits anyway.
############################################################
guess_A = 1.1
guess_B = 0.0
bounds={'A': (-1.5, 5.0), 'B': (-1.0, 1.0)}
line_model = LineModel(guess_A, guess_B, bounds=bounds)
fitter = fitting.NonLinearLSQFitter(line_model)
fitter(x, y)
print 'NonLinearLSQFitter with bounds:'
print 'A =', line_model.A
print 'B =', line_model.B
plt.ioff()
plt.plot(x, y)
plt.plot(x, line_model(x))
plt.show()
############################################################
# Case 3: SLSQPFitter without bounds, OK
############################################################
guess_A = 1.1
guess_B = 0.0
line_model = LineModel(guess_A, guess_B)
fitter = fitting.SLSQPFitter(line_model)
fitter(x, y)
print 'SLSQPFitter without bounds:'
print 'A =', line_model.A
print 'B =', line_model.B
plt.ioff()
plt.plot(x, y)
plt.plot(x, line_model(x))
plt.show()
############################################################
# Case 4: SLSQPFitter with bounds.
# Does not fit even if within bounds,
# and reports a successful fit.
############################################################
guess_A = 1.1
guess_B = 0.0
bounds={'A': (-5.0, 5.0), 'B': (-1.0, 1.0)}
line_model = LineModel(guess_A, guess_B, bounds=bounds)
fitter = fitting.SLSQPFitter(line_model)
fitter(x, y)
print 'SLSQPFitter with bounds:'
print 'A =', line_model.A
print 'B =', line_model.B
plt.ioff()
plt.plot(x, y)
plt.plot(x, line_model(x))
plt.show()
Activity
nden commentedon Jul 1, 2013
@streeto It's not an obvious fix but I the bug is in the way
parameters.Parametersandcore._ParameterPropertyupdate model.parameters and individual parameters. I'll take a look today.streeto commentedon Jul 1, 2013
I just fixed it for NonLinearLSQFitter, there is a bug in _set_bounds():
This is wrong because if we slip below b[0], the second setattr will set par again anyway. The fix is simple:
nden commentedon Jul 1, 2013
@streeto Excellent, thanks! Do you want to file a PR or do you want me to do it?
I have a fix for the SLSQP fitter which wasn't passing the parameters to the model.
streeto commentedon Jul 2, 2013
Sorry, my workspace is a mess, I am unable to prepare a pull request right now. Please proceed with the fix. :-)
cdeil commentedon Aug 8, 2013
@streeto Can you try to update your test example and see if everything works as expected with the current
astropy.modeling?Currently your script gives an error (see below) because the API for creating user-defined models has changed a bit. See Gaussian1DModel as an example how to do it now.
streeto commentedon Aug 8, 2013
@nden, I updated the test, it works correctly. Below's the updated version. Thanks for the effort! Now to try it with my real world unstable models. :-)
cdeil commentedon Aug 8, 2013
@streeto Thanks! Please close this issue.