def __call__(self, model, x, measured_raw_cts, measured_bkg_cts, t_raw, t_bkg, x_err=None, **kwargs): if x_err is not None: model = IntModel(model.__class__)(x_err, *model.parameters) model_copy = _validate_model(model, self.supported_constraints) farg = _convert_input(x, measured_raw_cts) farg = (model_copy, measured_bkg_cts, t_raw, t_bkg) + farg p0, _ = _model_to_fit_params(model_copy) # TODO: Honor estimate_jacobian in kwargs, and/or determine if # model supports jacobian, and/or if fitter supports the jac argument. fitparams, self.fit_info = self._opt_method( self.objective_function, p0, farg, jac=self.objective_derivative, **kwargs) _fitter_to_model_params(model_copy, fitparams) return model_copy
def __call__(self, model, in_coords, ref_coords, sigma=5.0, maxsig=4.0, **kwargs): model_copy = _validate_model(model, ['bounds']) x, y = in_coords xref, yref = ref_coords xmax = max(np.max(x), np.max(xref)) ymax = max(np.max(y), np.max(yref)) landscape = self.mklandscape(ref_coords, sigma, maxsig, (int(ymax),int(xmax))) farg = (model_copy,) + _convert_input(x, y, landscape) p0, _ = _model_to_fit_params(model_copy) # TODO: Use the name of the parameter to infer the step size ranges = [] for p in model_copy.param_names: bounds = model_copy.bounds[p] try: diff = np.diff(bounds)[0] except TypeError: pass else: if diff > 0: ranges.append(slice(*(bounds+(min(0.5*sigma, 0.1*diff),)))) continue ranges.append((getattr(model_copy, p).value,) * 2) # Ns=1 limits the fitting along an axis where the range is not a slice # object: this is those were the bounds are equal (i.e. fixed param) fitted_params = self._opt_method(self.objective_function, ranges, farg, Ns=1, finish=None, **kwargs) _fitter_to_model_params(model_copy, fitted_params) return model_copy
def __call__(self, model, in_coords, ref_coords, sigma=5.0, maxsig=4.0, landscape=None, **kwargs): model_copy = _validate_model(model, ['bounds', 'fixed']) # Turn 1D arrays into tuples to allow iteration over axes try: iter(in_coords[0]) except TypeError: in_coords = (in_coords, ) try: iter(ref_coords[0]) except TypeError: ref_coords = (ref_coords, ) # Remember, coords are x-first (reversed python order) if landscape is None: landshape = tuple( int(max(np.max(inco), np.max(refco)) + 10) for inco, refco in zip(in_coords, ref_coords))[::-1] landscape = self.mklandscape(ref_coords, sigma, maxsig, landshape) farg = (model_copy, ) + _convert_input(in_coords, landscape) p0, _ = _model_to_fit_params(model_copy) # TODO: Use the name of the parameter to infer the step size ranges = [] for p in model_copy.param_names: bounds = model_copy.bounds[p] try: diff = np.diff(bounds)[0] except TypeError: pass else: # We don't check that the value of a fixed param is within bounds if diff > 0 and not model_copy.fixed[p]: ranges.append( slice(*(bounds + (min(0.5 * sigma, 0.1 * diff), )))) continue ranges.append((getattr(model_copy, p).value, ) * 2) # Ns=1 limits the fitting along an axis where the range is not a slice # object: this is those were the bounds are equal (i.e. fixed param) fitted_params = self._opt_method(self.objective_function, ranges, farg, Ns=1, finish=None, **kwargs) _fitter_to_model_params(model_copy, fitted_params) return model_copy
def __call__(self, model, x, y, weights=None, **kwargs): """ Fit data to this model. Parameters ---------- model : `~astropy.modeling.FittableModel` model to fit to x, y x : array input coordinates y : array input coordinates weights : array, optional Weights for fitting. For data with Gaussian uncertainties, the weights should be 1/sigma. kwargs : dict optional keyword arguments to be passed to the optimizer or the statistic Returns ------- model_copy : `~astropy.modeling.FittableModel` a copy of the input model with parameters set by the fitter """ model_copy = _validate_model(model, self._opt_method.supported_constraints) farg = _convert_input(x, y) farg = (model_copy, weights) + farg p0, _ = _model_to_fit_params(model_copy) fitparams, self.fit_info = self._opt_method( self.log_probability, p0, farg, self.nsteps, save_samples=self.save_samples, **kwargs) # set the output model parameters to the "best fit" parameters _fitter_to_model_params(model_copy, fitparams) # get and set the symmetric and asymmetric uncertainties on each parameter model_copy = self._set_uncs_and_posterior(model_copy) return model_copy
def __call__(self, model, in_coords, ref_coords, sigma=5.0, maxsig=4.0, **kwargs): model_copy = _validate_model(model, ['bounds']) x, y = in_coords xref, yref = ref_coords xmax = max(np.max(x), np.max(xref)) ymax = max(np.max(y), np.max(yref)) landscape = self.mklandscape(ref_coords, sigma, maxsig, (int(ymax), int(xmax))) farg = (model_copy, ) + _convert_input(x, y, landscape) p0, _ = _model_to_fit_params(model_copy) # TODO: Use the name of the parameter to infer the step size ranges = [] for p in model_copy.param_names: bounds = model_copy.bounds[p] try: diff = np.diff(bounds)[0] except TypeError: pass else: if diff > 0: ranges.append( slice(*(bounds + (min(0.5 * sigma, 0.1 * diff), )))) continue ranges.append((getattr(model_copy, p).value, ) * 2) # Ns=1 limits the fitting along an axis where the range is not a slice # object: this is those were the bounds are equal (i.e. fixed param) fitted_params = self._opt_method(self.objective_function, ranges, farg, Ns=1, finish=None, **kwargs) _fitter_to_model_params(model_copy, fitted_params) return model_copy
def __call__(self, model, x, y, z=None, weights=None, maxiter=DEFAULT_MAXITER, ftol=DEFAULT_ACC, xtol=DEFAULT_ACC, gtol=DEFAULT_ACC, factor=100.0, iterfunct='default', iterkw={}, nocovar=False, rescale=0, autoderivative=True, diag=None, epsfcn=None): """ Fit data to this model. Parameters ---------- model : `~astropy.modeling.FittableModel` model to fit to x, y, z x : array input coordinates y : array input coordinates z : array (optional) input coordinates weights : array (optional) weights maxiter : int maximum number of iterations acc : float Relative error desired in the approximate solution epsilon : float A suitable step length for the forward-difference approximation of the Jacobian (if model.fjac=None). If epsfcn is less than the machine precision, it is assumed that the relative errors in the functions are of the order of the machine precision. estimate_jacobian : bool If False (default) and if the model has a fit_deriv method, it will be used. Otherwise the Jacobian will be estimated. If True, the Jacobian will be estimated in any case. Returns ------- model_copy : `~astropy.modeling.FittableModel` a copy of the input model with parameters set by the fitter """ from .extern.mpfit import mpfit as MPFit model_copy = _validate_model(model, self.supported_constraints) farg = (model_copy, weights, ) keys = ('model', 'weights', ) inputs = _convert_input(x, y, z) farg = farg + inputs if len(inputs) == 3: keys = keys + ('x', 'y', 'err') elif len(inputs) == 2: keys = keys + ('x', 'err') functkw = dict(zip(keys, farg)) if model_copy.fit_deriv is None and not autoderivative: warnings.warn("Can't compute automatic derivatives, " "no model.fit_deriv is defined, using autoderivative=True" " instead", AstropyUserWarning) autoderivative = True if not autoderivative: warnings.warn("Analytic derivatives are currently broken in the mpfit python version...", AstropyUserWarning) autoderivative = True init_values = model.parameters parinfo = self.make_parinfo(model) fitresult = MPFit( self.objective_function, functkw=functkw, maxiter=maxiter, epsfcn=epsfcn, xtol=xtol, gtol=gtol, ftol=ftol, factor=factor, parinfo=parinfo, nprint=self.nprint, iterfunct=iterfunct, iterkw={}, nocovar=int(nocovar), rescale=int(rescale), autoderivative=int(autoderivative), diag=diag, quiet=int(self.quiet)) model_copy.parameters = fitresult.params self.fit_info['status'] = fitresult.status self.fit_info['fnorm'] = fitresult.fnorm self.fit_info['covar'] = fitresult.covar self.fit_info['nfev'] = fitresult.nfev self.fit_info['niter'] = fitresult.niter self.fit_info['perror'] = fitresult.perror self.fit_result = fitresult if fitresult.status not in [1, 2, 3, 4]: warnings.warn("The fit may be unsuccessful; check " "fit_info['message'] for more information.", AstropyUserWarning) self.fit_info['message'] = fitresult.errmsg elif fitresult.status == 1: self.fit_info['message'] = "Both actual and predicted relative reductions in the sum of squares are at most ftol." elif fitresult.status == 2: self.fit_info['message'] = "Relative error between two consecutive iterates is at most xtol." elif fitresult.status == 3: self.fit_info['message'] = "Conditions for status = 1 and status = 2 both hold." elif fitresult.status == 4: self.fit_info['message'] = "The cosine of the angle between fvec and any column of the jacobian is at most gtol in absolute value." # now try to compute the true covariance matrix cov = self.fit_info['covar'] n = len(init_values) pcor = cov * 0.0 for i in range(n): for j in range(n): pcor[i,j] = cov[i,j]/np.sqrt(cov[i,i]*cov[j,j]) self.fit_info['param_cor'] = pcor return model_copy
def _curve_fit(self, model, x, y, z=None, weights=None, yerr=None, maxiter=DEFAULT_MAXITER, acc=DEFAULT_ACC, epsilon=DEFAULT_EPS, estimate_jacobian=False, **kwargs): """ Fit data to this model. Parameters ---------- model : `~astropy.modeling.FittableModel` model to fit to x, y, z x : array input coordinates y : array input coordinates z : array (optional) input coordinates weights : array (optional) Weights for fitting. For data with Gaussian uncertainties, the weights should be 1/sigma. maxiter : int maximum number of iterations acc : float Relative error desired in the approximate solution epsilon : float A suitable step length for the forward-difference approximation of the Jacobian (if model.fjac=None). If epsfcn is less than the machine precision, it is assumed that the relative errors in the functions are of the order of the machine precision. estimate_jacobian : bool If False (default) and if the model has a fit_deriv method, it will be used. Otherwise the Jacobian will be estimated. If True, the Jacobian will be estimated in any case. equivalencies : list or None, optional and keyword-only argument List of *additional* equivalencies that are should be applied in case x, y and/or z have units. Default is None. Returns ------- model_copy : `~astropy.modeling.FittableModel` a copy of the input model with parameters set by the fitter """ model_copy = _validate_model(model, self.supported_constraints) farg = ( model_copy, weights, ) + _convert_input(x, y, z) if model_copy.fit_deriv is None or estimate_jacobian: dfunc = None else: dfunc = self._wrap_deriv init_values, finds = _model_to_fit_params(model_copy) def f(x, *p0, mod=model_copy): _fitter_to_model_params(mod, p0) return mod(x) fitparams, cov_x = opt.curve_fit(f, x, y, p0=init_values, sigma=yerr, epsfcn=epsilon, jac=dfunc, col_deriv=model_copy.col_fit_deriv, maxfev=maxiter, xtol=acc, absolute_sigma=False, **kwargs) error = [] for i in range(len(fitparams)): try: error.append(np.absolute(cov_x[i][i])**0.5) except: error.append(0.00) _fitter_to_model_params(model_copy, fitparams) _output_errors = np.zeros(model.parameters.shape) _output_errors[finds] = np.array(error) self.fit_info['cov_x'] = cov_x self.fit_info['param_names'] = model_copy.param_names self.fit_info['param_err'] = _output_errors self.fit_info['param_fit'] = model_copy.parameters # now try to compute the true covariance matrix if (len(y) > len(init_values)) and cov_x is not None: sum_sqrs = np.sum(self.objective_function(fitparams, *farg)**2) dof = len(y) - len(init_values) self.fit_info['param_cov'] = cov_x * sum_sqrs / dof else: self.fit_info['param_cov'] = None self.fit_info['param_units'] = [ getattr(model_copy, p).unit for p in model_copy.param_names ] return model_copy
def _bootstrap(self, model, x, y, z=None, yerr=0.0, weights=None, **kwargs): model_copy = super().__call__(model, x, y, **kwargs) init_values, _ = _model_to_fit_params(model) pfit, finds = _model_to_fit_params(model_copy) farg = ( model_copy, weights, ) + _convert_input(x, y, z) self._output_errors = np.zeros(model.parameters.shape) # Get the stdev of the residuals residuals = self.objective_function(pfit, *farg) sigma_res = np.std(residuals) sigma_err_total = np.sqrt(sigma_res**2 + yerr**2) # 100 random data sets are generated and fitted ps = [] for i in range(10): rand_delta = np.random.normal(0., sigma_err_total, len(y)) rand_y = y + rand_delta farg = ( model_copy, weights, ) + _convert_input(x, rand_y, z) rand_fit, rand_cov = opt.leastsq(self.objective_function, init_values, args=farg, full_output=False) ps.append(rand_fit) ps = np.array(ps) mean_pfit = np.mean(ps, 0) # You can choose the confidence interval that you want for your # parameter estimates: n_sigma = 1. # 1sigma gets approximately the same as methods above # 1sigma corresponds to 68.3% confidence interval # 2sigma corresponds to 95.44% confidence interval err_pfit = n_sigma * np.std(ps, 0) _fitter_to_model_params(model_copy, mean_pfit) self._output_errors[finds] = np.array(err_pfit) self.fit_info['param_names'] = model_copy.param_names self.fit_info['param_err'] = self._output_errors self.fit_info['param_fit'] = model_copy.parameters self.fit_info['param_units'] = [ getattr(model_copy, p).unit for p in model_copy.param_names ] return model_copy