Example #1
0
    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
Example #2
0
    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
Example #3
0
    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
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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
Example #9
0
    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