3636EPS = np .sqrt (np .finfo (float ).eps )
3737
3838
39- # supported constraints
40- # TODO: Register these automatically through a metaclass
41- constraintsdef = {
42- 'NonLinearLSQFitter' : ['fixed' , 'tied' , 'bounds' ],
43- 'SLSQPFitter' : ['bounds' , 'eqcons' , 'ineqcons' , 'fixed' , 'tied' ],
44- 'LinearLSQFitter' : ['fixed' ]}
45-
46-
4739def _convert_input (x , y , z = None ):
4840 x = np .asarray (x )
4941 y = np .asarray (y )
@@ -69,7 +61,7 @@ class ModelLinearityError(ModelsError):
6961 """
7062
7163
72- class UnsupportedConstraintError (ModelsError ):
64+ class UnsupportedConstraintError (ModelsError , ValueError ):
7365 """
7466 Raised when a fitter does not support a type of constraint.
7567 """
@@ -84,6 +76,12 @@ class Fitter(object):
8476
8577 __metaclass__ = abc .ABCMeta
8678
79+ # The base Fitter does not support any constraints by default; individual
80+ # fitters should explicitly set this list to the specific constraints
81+ # it supports
82+ # May contain any of 'bounds', 'eqcons', 'ineqcons', 'fixed', or 'tied'
83+ supported_constraints = []
84+
8785 def __init__ (self , model ):
8886 if not model .fittable :
8987 raise ValueError ("Model must be a subclass of ParametricModel" )
@@ -257,28 +255,28 @@ def _wrap_deriv(self, p, x, y, z=None):
257255 return [np .ravel (_ ) for _ in self .model .deriv (x , y , * params )]
258256
259257 def _validate_constraints (self ):
260- fname = self .__class__ .__name__
261- try :
262- c = constraintsdef [fname ]
263- except KeyError :
264- raise UnsupportedConstraintError ("{0} does not support fitting" ,
265- "with constraints" .format (fname ))
266- if any (self .fixed ) and 'fixed' not in c :
267- raise ValueError ("{0} cannot handle fixed parameter" ,
268- "constraints ." .format (fname ))
269- if any (self .tied ) and 'tied' not in c :
270- raise ValueError ("{0} cannot handle tied parameter" ,
271- "constraints " .format (fname ))
258+ message = '{0} cannot handle {{0}} constraints.' .format (
259+ self .__class__ .__name__ )
260+
261+ if any (self .fixed ) and 'fixed' not in self .supported_constraints :
262+ raise UnsupportedConstraintError (
263+ message .format ('fixed parameter' ))
264+
265+ if any (self .tied ) and 'tied' not in self .supported_constraints :
266+ raise UnsupportedConstraintError (
267+ message .format ('tied parameter' ))
268+
272269 if (any ([tuple (b ) != (- 1E12 , 1E12 ) for b in self .bounds ]) and
273- 'bounds' not in c ):
274- raise ValueError ("{0} cannot handle bound parameter" ,
275- "constraints" .format (fname ))
276- if self .model .eqcons and 'eqcons' not in c :
277- raise ValueError ("{0} cannot handle equality constraints but " ,
278- "eqcons given" .format (fname ))
279- if self .model .ineqcons and 'ineqcons' not in c :
280- raise ValueError ("{0} cannot handle inequality constraints but " ,
281- "ineqcons given" .format (fname ))
270+ 'bounds' not in self .supported_constraints ):
271+ raise UnsupportedConstraintError (
272+ message .format ('bound parameter' ))
273+
274+ if self .model .eqcons and 'eqcons' not in self .supported_constraints :
275+ raise UnsupportedConstraintError (message .format ('equality' ))
276+
277+ if (self .model .ineqcons and
278+ 'ineqcons' not in self .supported_constraints ):
279+ raise UnsupportedConstraintError (message .format ('inequality' ))
282280
283281 @property
284282 def covar (self ):
@@ -330,6 +328,8 @@ class LinearLSQFitter(Fitter):
330328 A nonlinear model is passed to a linear fitter
331329 """
332330
331+ supported_constraints = ['fixed' ]
332+
333333 def __init__ (self , model ):
334334 super (LinearLSQFitter , self ).__init__ (model )
335335 if not self .model .linear :
@@ -502,6 +502,8 @@ class NonLinearLSQFitter(Fitter):
502502 A linear model is passed to a nonlinear fitter
503503 """
504504
505+ supported_constraints = ['fixed' , 'tied' , 'bounds' ]
506+
505507 def __init__ (self , model ):
506508
507509 self .fit_info = {'nfev' : None ,
@@ -636,6 +638,8 @@ class SLSQPFitter(Fitter):
636638 .. [1] http://www.netlib.org/toms/733
637639 """
638640
641+ supported_constraints = ['bounds' , 'eqcons' , 'ineqcons' , 'fixed' , 'tied' ]
642+
639643 def __init__ (self , model ):
640644 super (SLSQPFitter , self ).__init__ (model )
641645 if self .model .linear :
0 commit comments