Exemplo n.º 1
0
    def fit(self, current):
        self._fit.model.thawedpars = current

        # nm = NelderMead()
        # nm.config['iquad'] = 0
        # nm.config['finalsimplex'] = 1

        #lm = LevMar()
        #lm.config['maxfev'] = 5

        cv = Covariance()

        # Use the fit method defined before called get_draws().  This way the user
        # does not have to pass in the fitting method and method options.
        fit = Fit(self._fit.data, self._fit.model, self._fit.stat, self._fit.method,
                  cv)

        fit_result = fit.fit()
        covar_result = fit.est_errors()
        sigma = np.array(covar_result.extra_output)

        if np.isnan(sigma).any():
            raise CovarError("NaNs found in covariance matrix")

        # cache the fitting scales
        self._sigma = sigma
Exemplo n.º 2
0
def setup():
    data = Data1D('fake', _x, _y, _err)

    g1 = Gauss1D('g1')
    g1.fwhm.set(1.0, _tiny, _max, frozen=False)
    g1.pos.set(1.0, -_max, _max, frozen=False)
    g1.ampl.set(1.0, -_max, _max, frozen=False)
    p1 = PowLaw1D('p1')
    p1.gamma.set(1.0, -10, 10, frozen=False)
    p1.ampl.set(1.0, 0.0, _max, frozen=False)
    p1.ref.set(1.0, -_max, _max, frozen=True)
    model = p1 + g1

    method = LevMar()
    method.config['maxfev'] = 10000
    method.config['ftol'] = float(_eps)
    method.config['epsfcn'] = float(_eps)
    method.config['gtol'] = float(_eps)
    method.config['xtol'] = float(_eps)
    method.config['factor'] = float(100)

    fit = Fit(data, model, Chi2DataVar(), method, Covariance())
    results = fit.fit()

    for key in ["succeeded", "numpoints", "nfev"]:
        assert _fit_results_bench[key] == int(getattr(results, key))

    for key in ["rstat", "qval", "statval", "dof"]:
        # used rel and abs tol of 1e-7 with numpy allclose
        assert float(getattr(results,
                             key)) == pytest.approx(_fit_results_bench[key])

    for key in ["parvals"]:
        try:
            # used rel and abs tol of 1e-4 with numpy allclose
            assert getattr(results,
                           key) == pytest.approx(_fit_results_bench[key])
        except AssertionError:
            print('parvals bench: ', _fit_results_bench[key])
            print('parvals fit:   ', getattr(results, key))
            print('results', results)
            raise

    fields = [
        'data', 'model', 'method', 'fit', 'results', 'covresults', 'dof', 'mu',
        'num'
    ]
    out = namedtuple('Results', fields)

    out.data = data
    out.model = model
    out.method = method
    out.fit = fit
    out.results = results
    out.covresults = fit.est_errors()
    out.dof = results.dof
    out.mu = numpy.array(results.parvals)
    out.cov = numpy.array(out.covresults.extra_output)
    out.num = 10
    return out
Exemplo n.º 3
0
    def fit(self, current):
        self._fit.model.thawedpars = current

        # nm = NelderMead()
        # nm.config['iquad'] = 0
        # nm.config['finalsimplex'] = 1

        #lm = LevMar()
        #lm.config['maxfev'] = 5

        cv = Covariance()

        # Use the fit method defined before called get_draws().  This way the user
        # does not have to pass in the fitting method and method options.
        fit = Fit(self._fit.data, self._fit.model, self._fit.stat,
                  self._fit.method, cv)

        fit_result = fit.fit()
        covar_result = fit.est_errors()
        sigma = np.array(covar_result.extra_output)

        if np.isnan(sigma).any():
            raise CovarError("NaNs found in covariance matrix")

        # cache the fitting scales
        self._sigma = sigma
Exemplo n.º 4
0
    def fit(self):
        """Fit spectrum"""
        from sherpa.fit import Fit
        from sherpa.models import ArithmeticModel, SimulFitModel
        from sherpa.astro.instrument import Response1D
        from sherpa.data import DataSimulFit

        # Translate model to sherpa model if necessary
        if isinstance(self.model, models.SpectralModel):
            model = self.model.to_sherpa()
        else:
            model = self.model

        if not isinstance(model, ArithmeticModel):
            raise ValueError('Model not understood: {}'.format(model))

        # Make model amplitude O(1e0)
        val = model.ampl.val * self.FLUX_FACTOR ** (-1)
        model.ampl = val

        if self.fit_range is not None:
            log.info('Restricting fit range to {}'.format(self.fit_range))
            fitmin = self.fit_range[0].to('keV').value
            fitmax = self.fit_range[1].to('keV').value

        # Loop over observations
        pha = list()
        folded_model = list()
        nobs = len(self.obs_list)
        for ii in range(nobs):
            temp = self.obs_list[ii].to_sherpa()
            if self.fit_range is not None:
                temp.notice(fitmin, fitmax)
                if temp.get_background() is not None:
                    temp.get_background().notice(fitmin, fitmax)
            temp.ignore_bad()
            if temp.get_background() is not None:
                temp.get_background().ignore_bad()
            pha.append(temp)
            # Forward folding
            resp = Response1D(pha[ii])
            folded_model.append(resp(model) * self.FLUX_FACTOR)

        data = DataSimulFit('simul fit data', pha)
        fitmodel = SimulFitModel('simul fit model', folded_model)

        log.debug(fitmodel)
        fit = Fit(data, fitmodel, self.statistic)
        fitresult = fit.fit()
        log.debug(fitresult)
        # The model instance passed to the Fit now holds the best fit values
        covar = fit.est_errors()
        log.debug(covar)

        for ii in range(nobs):
            efilter = pha[ii].get_filter()
            shmodel = fitmodel.parts[ii]
            self.result[ii].fit = _sherpa_to_fitresult(shmodel, covar, efilter, fitresult)
Exemplo n.º 5
0
 def tst_low_level(self, thaw_c1):
     if thaw_c1:
         self.mdl.c1.thaw()
     self.mdl.c2.thaw()
     f = Fit(self.data, self.mdl, estmethod=Confidence())
     self.mdl.c2 = 1
     f.fit()
     if not thaw_c1:
         self.mdl.c1.thaw()
         f.fit()
     result = f.est_errors()
     self.cmp_results(result)
Exemplo n.º 6
0
def test_low_level(thaw_c1, setUp):
    data, mdl = setUp
    if thaw_c1:
        mdl.c1.thaw()

    mdl.c2.thaw()
    f = Fit(data, mdl, estmethod=Confidence())
    mdl.c2 = 1
    f.fit()

    if not thaw_c1:
        mdl.c1.thaw()
        f.fit()

    result = f.est_errors()
    cmp_results(result)
Exemplo n.º 7
0
class SpectrumFit(object):
    """Orchestrate a 1D counts spectrum fit.

    After running the :func:`~gammapy.spectrum.SpectrumFit.fit` and
    :func:`~gammapy.spectrum.SpectrumFit.est_errors` methods, the fit results
    are available in :func:`~gammapy.spectrum.SpectrumFit.result`. For usage
    examples see :ref:`spectral_fitting`

    Parameters
    ----------
    obs_list : `~gammapy.spectrum.SpectrumObservationList`, `~gammapy.spectrum.SpectrumObservation`
        Observation(s) to fit
    model : `~gammapy.spectrum.models.SpectralModel`
        Source model. Should return counts if ``forward_folded`` is False and a flux otherwise
    stat : {'wstat', 'cash'}
        Fit statistic
    forward_folded : bool, default: True
        Fold ``model`` with the IRFs given in ``obs_list``
    fit_range : tuple of `~astropy.units.Quantity`
        Fit range, will be convolved with observation thresholds. If you want to
        control which bins are taken into account in the fit for each
        observation, use :func:`~gammapy.spectrum.PHACountsSpectrum.quality`
    background_model : `~gammapy.spectrum.models.SpectralModel`, optional
        Background model to be used in cash fits
    method : {'sherpa', 'iminuit'}
        Optimization backend for the fit
    err_method : {'sherpa'}
        Optimization backend for error estimation
    """
    def __init__(self,
                 obs_list,
                 model,
                 stat='wstat',
                 forward_folded=True,
                 fit_range=None,
                 background_model=None,
                 method='sherpa',
                 err_method='sherpa'):
        self.obs_list = obs_list
        self._model = model
        self.stat = stat
        self.forward_folded = forward_folded
        self.fit_range = fit_range
        self._background_model = background_model
        self.method = method
        self.err_method = err_method

        self._predicted_counts = None
        self._statval = None

        self.covar_axis = None
        self.covariance = None
        self._result = None

        self._check_valid_fit()
        self._apply_fit_range()

    def __str__(self):
        ss = self.__class__.__name__
        ss += '\nSource model {}'.format(self.model)
        ss += '\nStat {}'.format(self.stat)
        ss += '\nForward Folded {}'.format(self.forward_folded)
        ss += '\nFit range {}'.format(self.fit_range)
        if self.background_model is not None:
            ss += '\nBackground model {}'.format(self.background_model)
        ss += '\nBackend {}'.format(self.method)
        ss += '\nError Backend {}'.format(self.err_method)

        return ss

    @property
    def result(self):
        """Fit result

        The result is a list of length ``n``, where ``n`` ist the number of
        observations that participated in the fit. The best fit model is
        usually the same for all observations but the results differ in the
        fitted energy range, predicted counts, final fit statistic value
        etc.
        """
        return self._result

    @property
    def model(self):
        """Source model

        The model parameters change every time the likelihood is evaluated. In
        order to access the best-fit model parameters, use
        :func:`gammapy.spectrum.SpectrumFit.result`
        """
        return self._model

    @model.setter
    def model(self, model):
        self._model = model

    @property
    def background_model(self):
        """Background model

        The model parameters change every time the likelihood is evaluated. In
        order to access the best-fit model parameters, use
        :func:`gammapy.spectrum.SpectrumFit.result`
        """
        return self._background_model

    @background_model.setter
    def background_model(self, model):
        self._background_model = model

    @property
    def obs_list(self):
        """Observations participating in the fit"""
        return self._obs_list

    @obs_list.setter
    def obs_list(self, obs_list):
        if isinstance(obs_list, SpectrumObservation):
            obs_list = SpectrumObservationList([obs_list])

        self._obs_list = SpectrumObservationList(obs_list)

    @property
    def bins_in_fit_range(self):
        """Bins participating in the fit for each observation."""
        return self._bins_in_fit_range

    @property
    def predicted_counts(self):
        """Current value of predicted counts.

        For each observation a tuple to counts for the on and off region is
        returned.
        """
        return self._predicted_counts

    @property
    def statval(self):
        """Current value of statval.

        For each observation the statval per bin is returned.
        """
        return self._statval

    @property
    def fit_range(self):
        """Fit range."""
        return self._fit_range

    @fit_range.setter
    def fit_range(self, fit_range):
        self._fit_range = fit_range
        self._apply_fit_range()

    @property
    def true_fit_range(self):
        """True fit range for each observation.

        True fit range is the fit range set in the
        `~gammapy.spectrum.SpectrumFit` with observation threshold taken into
        account.
        """
        true_range = []
        for binrange, obs in zip(self.bins_in_fit_range, self.obs_list):
            idx = np.where(binrange)[0]
            if len(idx) == 0:
                true_range.append(None)
                continue
            e_min = obs.e_reco[idx[0]]
            e_max = obs.e_reco[idx[-1] + 1]
            fit_range = u.Quantity((e_min, e_max))
            true_range.append(fit_range)
        return true_range

    def _apply_fit_range(self):
        """Mark bins within desired fit range for each observation."""
        self._bins_in_fit_range = []
        for obs in self.obs_list:
            # Take into account fit range
            energy = obs.e_reco
            valid_range = np.zeros(energy.nbins)

            if self.fit_range is not None:

                precision = 1e-3  # to avoid floating round precision
                idx_lo = np.where(energy *
                                  (1 + precision) < self.fit_range[0])[0]
                valid_range[idx_lo] = 1

                idx_hi = np.where(energy[:-1] *
                                  (1 - precision) > self.fit_range[1])[0]
                if len(idx_hi) != 0:
                    idx_hi = np.insert(idx_hi, 0, idx_hi[0] - 1)
                valid_range[idx_hi] = 1

            # Take into account thresholds
            try:
                quality = obs.on_vector.quality
            except AttributeError:
                quality = np.zeros(obs.e_reco.nbins)

            convolved = np.logical_and(1 - quality, 1 - valid_range)

            self._bins_in_fit_range.append(convolved)

    def predict_counts(self):
        """Predict counts for all observations.

        The result is stored as ``predicted_counts`` attribute.
        """
        predicted_counts = []
        for obs in self.obs_list:
            mu_sig = self._predict_counts_helper(obs, self.model,
                                                 self.forward_folded)
            mu_bkg = None
            if self.background_model is not None:
                # For now, never fold background model with IRFs
                mu_bkg = self._predict_counts_helper(obs,
                                                     self.background_model,
                                                     False)
            counts = [mu_sig, mu_bkg]
            predicted_counts.append(counts)
        self._predicted_counts = predicted_counts

    def _predict_counts_helper(self, obs, model, forward_folded=True):
        """Predict counts for one observation.

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Response functions
        model : `~gammapy.spectrum.SpectralModel`
            Source or background model
        forward_folded : bool, default: True
            Fold model with IRFs

        Returns
        ------
        predicted_counts: `np.array`
            Predicted counts for one observation
        """
        predictor = CountsPredictor(model=model)
        if forward_folded:
            predictor.aeff = obs.aeff
            predictor.edisp = obs.edisp
        else:
            predictor.e_true = obs.e_reco

        predictor.livetime = obs.livetime

        predictor.run()
        counts = predictor.npred.data.data

        # Check count unit (~unit of model amplitude)
        if counts.unit.is_equivalent(''):
            counts = counts.value
        else:
            raise ValueError('Predicted counts {}'.format(counts))

        # Apply AREASCAL column
        counts *= obs.on_vector.areascal

        return counts

    def calc_statval(self):
        """Calc statistic for all observations.

        The result is stored as attribute ``statval``, bin outside the fit
        range are set to 0.
        """
        statval = []
        for obs, npred in zip(self.obs_list, self.predicted_counts):
            on_stat, off_stat = self._calc_statval_helper(obs, npred)
            statvals = (on_stat, off_stat)
            statval.append(statvals)
        self._statval = statval
        self._restrict_statval()

    def _calc_statval_helper(self, obs, prediction):
        """Calculate ``statval`` for one observation.

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Measured counts
        prediction : tuple of `~numpy.ndarray`
            Predicted (on counts, off counts)

        Returns
        ------
        statsval : tuple of `~numpy.ndarray`
            Statval for (on, off)
        """
        stats_func = getattr(stats, self.stat)

        # Off stat = 0 by default
        off_stat = np.zeros(obs.e_reco.nbins)
        if self.stat == 'cash' or self.stat == 'cstat':
            if self.background_model is not None:
                mu_on = prediction[0] + prediction[1]
                on_stat = stats_func(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                mu_off = prediction[1] / obs.alpha
                off_stat = stats_func(n_on=obs.off_vector.data.data.value,
                                      mu_on=mu_off)
            else:
                mu_on = prediction[0]
                on_stat = stats_func(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                off_stat = np.zeros_like(on_stat)

        elif self.stat == 'wstat':
            kwargs = dict(n_on=obs.on_vector.data.data.value,
                          n_off=obs.off_vector.data.data.value,
                          alpha=obs.alpha,
                          mu_sig=prediction[0])
            # Store the result of the profile likelihood as bkg prediction
            mu_bkg = stats.get_wstat_mu_bkg(**kwargs)
            prediction[1] = mu_bkg * obs.alpha
            on_stat_ = stats_func(**kwargs)
            # The on_stat sometime contains nan values
            # TODO: Handle properly
            on_stat = np.nan_to_num(on_stat_)
            off_stat = np.zeros_like(on_stat)
        else:
            raise NotImplementedError('{}'.format(self.stat))

        return on_stat, off_stat

    def total_stat(self, parameters):
        """Statistic summed over all bins and all observations.

        This is the likelihood function that is passed to the optimizers

        Parameters
        ----------
        parameters : `~gammapy.utils.fitting.ParameterList`
            Model parameters
        """
        self.model.parameters = parameters
        self.predict_counts()
        self.calc_statval()
        total_stat = np.sum([np.sum(v) for v in self.statval],
                            dtype=np.float64)
        return total_stat

    def _restrict_statval(self):
        """Apply valid fit range to statval.
        """
        for statval, valid_range in zip(self.statval, self.bins_in_fit_range):
            # Find bins outside safe range
            idx = np.where(np.invert(valid_range))[0]
            statval[0][idx] = 0
            statval[1][idx] = 0

    def _check_valid_fit(self):
        """Helper function to give useful error messages."""
        # Assume that settings are the same for all observations
        test_obs = self.obs_list[0]
        irfs_exist = test_obs.aeff is not None or test_obs.edisp is not None
        if self.forward_folded and not irfs_exist:
            raise ValueError('IRFs required for forward folded fit')
        if self.stat == 'wstat' and self.obs_list[0].off_vector is None:
            raise ValueError('Off vector required for WStat fit')
        try:
            test_obs.livetime
        except KeyError:
            raise ValueError('No observation livetime given')

    def likelihood_1d(self, model, parname, parvals):
        """Compute likelihood profile.

        TODO: Replace by something more generic

        Parameters
        ----------
        model : `~gammapy.spectrum.models.SpectralModel`
            Model to draw likelihood profile for
        parname : str
            Parameter to calculate profile for
        parvals : `~astropy.units.Quantity`
            Parameter values
        """
        likelihood = []
        self._model = model
        for val in parvals:
            self.model.parameters[parname].value = val
            stat = self.total_stat(self.model.parameters)
            likelihood.append(stat)
        return np.array(likelihood)

    def plot_likelihood_1d(self, ax=None, **kwargs):
        """Plot 1-dim likelihood profile.

        See :func:`~gammapy.spectrum.SpectrumFit.likelihood_1d`
        """
        import matplotlib.pyplot as plt
        ax = plt.gca() if ax is None else ax

        yy = self.likelihood_1d(**kwargs)
        ax.plot(kwargs['parvals'], yy)
        ax.set_xlabel(kwargs['parname'])

        return ax

    def fit(self):
        """Run the fit."""
        if self.method == 'sherpa':
            self._fit_sherpa()
        elif self.method == 'iminuit':
            self._fit_iminuit()
        else:
            raise NotImplementedError('method: {}'.format(self.method))

    def _fit_sherpa(self):
        """Wrapper around sherpa minimizer."""
        from sherpa.fit import Fit
        from sherpa.data import Data1DInt
        from sherpa.optmethods import NelderMead
        from .sherpa_utils import SherpaModel, SherpaStat

        binning = self.obs_list[0].e_reco
        # The sherpa data object is not usued in the fit. It is set to the
        # first observation for debugging purposes, see below
        data = self.obs_list[0].on_vector.data.data.value
        data = Data1DInt('Dummy data', binning[:-1].value, binning[1:].value,
                         data)
        # DEBUG
        # from sherpa.models import PowLaw1D
        # from sherpa.stats import Cash
        # model = PowLaw1D('sherpa')
        # model.ref = 0.1
        # fit = Fit(data, model, Cash(), NelderMead())

        # NOTE: We cannot use the Levenbergr-Marquart optimizer in Sherpa
        # because it relies on the fvec return value of the fit statistic (we
        # return None). The computation of fvec is not straightforwad, not just
        # stats per bin. E.g. for a cash fit the sherpa stat computes it
        # according to cstat
        # see https://github.com/sherpa/sherpa/blob/master/sherpa/include/sherpa/stats.hh#L122

        self._sherpa_fit = Fit(data, SherpaModel(self), SherpaStat(self),
                               NelderMead())
        fitresult = self._sherpa_fit.fit()
        log.debug(fitresult)
        self._make_fit_result(self.model.parameters)

    def _fit_iminuit(self):
        """Iminuit minimization"""
        parameters, minuit = fit_minuit(parameters=self.model.parameters,
                                        function=self.total_stat)
        log.debug(minuit)
        self._make_fit_result(parameters)

    def _make_fit_result(self, parameters):
        """Bundle fit results into `~gammapy.spectrum.SpectrumFitResult`.

        Parameters
        ----------
        parameters : `~gammapy.utils.modeling.ParameterList`
            Best fit parameters
        """
        from . import SpectrumFitResult

        # run again with best fit parameters
        self.total_stat(parameters)
        model = self.model.copy()

        if self.background_model is not None:
            bkg_model = self.background_model.copy()
        else:
            bkg_model = None

        statname = self.stat

        results = []
        for idx, obs in enumerate(self.obs_list):
            fit_range = self.true_fit_range[idx]
            statval = np.sum(self.statval[idx])
            stat_per_bin = self.statval[idx]
            npred_src = copy.deepcopy(self.predicted_counts[idx][0])
            npred_bkg = copy.deepcopy(self.predicted_counts[idx][1])

            results.append(
                SpectrumFitResult(model=model,
                                  fit_range=fit_range,
                                  statname=statname,
                                  statval=statval,
                                  stat_per_bin=stat_per_bin,
                                  npred_src=npred_src,
                                  npred_bkg=npred_bkg,
                                  background_model=bkg_model,
                                  obs=obs))

        self._result = results

    def est_errors(self):
        """Estimate parameter errors."""
        if self.err_method == 'sherpa':
            self._est_errors_sherpa()
        else:
            raise NotImplementedError('{}'.format(self.err_method))

        for res in self.result:
            res.covar_axis = self.covar_axis
            res.covariance = self.covariance
            res.model.parameters.set_parameter_covariance(
                self.covariance, self.covar_axis)

    def _est_errors_sherpa(self):
        """Wrapper around Sherpa error estimator."""
        covar = self._sherpa_fit.est_errors()
        self.covar_axis = [par.split('.')[-1] for par in covar.parnames]
        self.covariance = copy.deepcopy(covar.extra_output)

    def run(self, outdir=None):
        """Run all steps and write result to disk.

        Parameters
        ----------
        outdir : Path, str
            directory to write results files to (if given)
        """
        log.info('Running {}'.format(self))

        self.fit()
        self.est_errors()

        if outdir is not None:
            self._write_result(outdir)

    def _write_result(self, outdir):
        outdir = make_path(outdir)
        outdir.mkdir(exist_ok=True, parents=True)

        # Assume only one model is fit to all data
        modelname = self.result[0].model.__class__.__name__
        filename = outdir / 'fit_result_{}.yaml'.format(modelname)
        log.info('Writing {}'.format(filename))
        self.result[0].to_yaml(filename)
Exemplo n.º 8
0
class test_sim(SherpaTestCase):
    def setUp(self):
        # self.startdir = os.getcwd()
        self.old_level = logger.getEffectiveLevel()
        logger.setLevel(logging.CRITICAL)

        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        pha = os.path.join(datadir, "refake_0934_1_21_1e4.fak")
        rmf = os.path.join(datadir, "ccdid7_default.rmf")
        arf = os.path.join(datadir, "quiet_0934.arf")

        self.simarf = os.path.join(datadir, "aref_sample.fits")
        self.pcaarf = os.path.join(datadir, "aref_Cedge.fits")

        data = read_pha(pha)
        data.ignore(None, 0.3)
        data.ignore(7.0, None)

        rsp = Response1D(data)
        self.abs1 = XSwabs("abs1")
        self.p1 = XSpowerlaw("p1")
        model = rsp(self.abs1 * self.p1)

        self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())

    def tearDown(self):
        # os.chdir(self.startdir)
        logger.setLevel(self.old_level)

    @needs_data
    def test_pragbayes_simarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("PragBayes")
        mcmc.set_sampler_opt("simarf", self.simarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 7)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
        # try:
        #     assert (covar_results.parmaxes < params.std(1)).all()
        # except AssertionError:
        #     print 'covar: ', str(covar_results.parmaxes)
        #     print 'param: ', str(params.std(1))
        #     raise

    @needs_data
    def test_pragbayes_pcaarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("pragBayes")
        mcmc.set_sampler_opt("simarf", self.pcaarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 5)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
Exemplo n.º 9
0
class SpectrumFit(object):
    """
    Spectral Fit

    For usage examples see :ref:`spectral_fitting`

    Parameters
    ----------
    obs_list : `~gammapy.spectrum.SpectrumObservationList`, `~gammapy.spectrum.SpectrumObservation`
        Observation(s) to fit
    model : `~gammapy.spectrum.models.SpectralModel`
        Source model. Should return counts if ``forward_folded`` is False and a flux otherwise
    stat : {'wstat', 'cash'}
        Fit statistic
    forward_folded : bool, default: True
        Fold ``model`` with the IRFs given in ``obs_list``
    fit_range : tuple of `~astropy.units.Quantity``, optional
        Fit range
    background_model : `~gammapy.spectrum.model.SpectralModel`, optional
        Background model to be used in cash fits
    method : {'sherpa'}
        Optimization backend for the fit
    err_method : {'sherpa'}
        Optimization backend for error estimation
    """
    def __init__(self,
                 obs_list,
                 model,
                 stat='wstat',
                 forward_folded=True,
                 fit_range=None,
                 background_model=None,
                 method='sherpa',
                 err_method='sherpa'):
        # TODO: add fancy converters to accept also e.g. CountsSpectrum
        if isinstance(obs_list, SpectrumObservation):
            obs_list = SpectrumObservationList([obs_list])
        if not isinstance(obs_list, SpectrumObservationList):
            raise ValueError('Invalid input {} for parameter obs_list'.format(
                type(obs_list)))

        self.obs_list = obs_list
        self.model = model
        self.stat = stat
        self.forward_folded = forward_folded
        self.fit_range = fit_range
        self.background_model = background_model
        self.method = method
        self.err_method = method

        # TODO: Reexpose as properties to improve docs
        self.predicted_counts = None
        self.statval = None
        # TODO: Remove once there is a Parameter class
        self.covar_axis = None
        self.covariance = None
        self.result = list()

    def __str__(self):
        """String repr"""
        ss = self.__class__.__name__
        ss += '\nData {}'.format(self.obs_list)
        ss += '\nSource model {}'.format(self.model)
        ss += '\nStat {}'.format(self.stat)
        ss += '\nForward Folded {}'.format(self.forward_folded)
        ss += '\nFit range {}'.format(self.fit_range)
        if self.background_model is not None:
            ss += '\nBackground model {}'.format(self.background_model)
        ss += '\nBackend {}'.format(self.method)
        ss += '\nError Backend {}'.format(self.err_method)

        return ss

    @property
    def fit_range(self):
        """Fit range"""
        return self._fit_range

    @fit_range.setter
    def fit_range(self, fit_range):
        self._fit_range = fit_range
        self._apply_fit_range()

    def _apply_fit_range(self):
        """Mark bins within desired fit range for each observation

        TODO: Split into smaller functions
        TODO: Could reuse code from PHACountsSpectrum
        TODO: Use True (not 0) to mark good bins
        TODO: Add to EnergyBounds
        """
        self._bins_in_fit_range = list()
        for obs in self.obs_list:
            # Take into account fit range
            energy = obs.e_reco
            valid_range = np.zeros(energy.nbins)

            if self.fit_range is not None:
                idx_lo = np.where(energy < self.fit_range[0])[0]
                valid_range[idx_lo] = 1

                idx_hi = np.where(energy[:-1] > self.fit_range[1])[0]
                if len(idx_hi) != 0:
                    idx_hi = np.insert(idx_hi, 0, idx_hi[0] - 1)
                valid_range[idx_hi] = 1

            # Take into account thresholds
            try:
                quality = obs.on_vector.quality
            except AttributeError:
                quality = np.zeros(obs.e_reco.nbins)

            # Convolve (see TODO above)
            convolved = np.logical_and(1 - quality, 1 - valid_range)

            self._bins_in_fit_range.append(convolved)

    @property
    def true_fit_range(self):
        """True fit range for each observation

        True fit range is the fit range set in the
        `~gammapy.spectrum.SpectrumFit` with observation threshold taken into
        account.
        """
        true_range = list()
        for binrange, obs in zip(self._bins_in_fit_range, self.obs_list):
            idx = np.where(binrange)[0]
            e_min = obs.e_reco[idx[0]]
            e_max = obs.e_reco[idx[-1] + 1]
            fit_range = u.Quantity((e_min, e_max))
            true_range.append(fit_range)
        return true_range

    def predict_counts(self, **kwargs):
        """Predict counts for all observations

        The result is stored as ``predicted_counts`` attribute
        """
        predicted_counts = list()
        for obs in self.obs_list:
            mu_sig = self._predict_counts_helper(obs, self.model,
                                                 self.forward_folded)
            mu_bkg = None
            if self.background_model is not None:
                # For now, never fold background model with IRFs
                mu_bkg = self._predict_counts_helper(obs,
                                                     self.background_model,
                                                     False)
            counts = [mu_sig, mu_bkg]
            predicted_counts.append(counts)
        self.predicted_counts = predicted_counts

    def _predict_counts_helper(self, obs, model, forward_folded=True):
        """Predict counts for one observation

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Response functions
        model : `~gammapy.spectrum.SpectralModel`
            Source or background model
        forward_folded : bool, default: True
            Fold model with IRFs

        Returns
        ------
        predicted_counts: `np.array`
            Predicted counts for one observation
        """
        binning = obs.e_reco
        if forward_folded:
            temp = calculate_predicted_counts(model=model,
                                              livetime=obs.livetime,
                                              aeff=obs.aeff,
                                              edisp=obs.edisp,
                                              e_reco=binning)
            counts = temp.data.data
        else:
            # TODO: This could also be part of calculate predicted counts
            counts = model.integral(binning[:-1], binning[1:], intervals=True)

        # Check count unit (~unit of model amplitude)
        cond = counts.unit.is_equivalent('ct') or counts.unit.is_equivalent('')
        if cond:
            counts = counts.value
        else:
            raise ValueError('Predicted counts {}'.format(counts))

        return counts

    def calc_statval(self):
        """Calc statistic for all observations

        The result is stored as attribute ``statval``, bin outside the fit
        range are set to 0.
        """
        statval = list()
        for obs, npred in zip(self.obs_list, self.predicted_counts):
            on_stat, off_stat = self._calc_statval_helper(obs, npred)
            stats = (on_stat, off_stat)
            statval.append(stats)
        self.statval = statval
        self._restrict_statval()

    def _calc_statval_helper(self, obs, prediction):
        """Calculate statval one observation

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Measured counts
        prediction : tuple of `~np.array`
            Predicted (on counts, off counts)

        Returns
        ------
        statsval : tuple or `~np.array`
            Statval for (on, off)
        """
        # Off stat = 0 by default
        off_stat = np.zeros(obs.e_reco.nbins)
        if self.stat == 'cash':
            if self.background_model is not None:
                mu_on = prediction[0] + prediction[1]
                on_stat = stats.cash(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                mu_off = prediction[1] / obs.alpha
                off_stat = stats.cash(n_on=obs.off_vector.data.data.value,
                                      mu_on=mu_off)
            else:
                mu_on = prediction[0]
                on_stat = stats.cash(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                off_stat = np.zeros_like(on_stat)

        elif self.stat == 'wstat':
            kwargs = dict(n_on=obs.on_vector.data.data.value,
                          n_off=obs.off_vector.data.data.value,
                          alpha=obs.alpha,
                          mu_sig=prediction[0])
            # Store the result of the profile likelihood as bkg prediction
            mu_bkg = stats.get_wstat_mu_bkg(**kwargs)
            prediction[1] = mu_bkg * obs.alpha
            on_stat = stats.wstat(**kwargs)
            off_stat = np.zeros_like(on_stat)
        else:
            raise NotImplementedError('{}'.format(self.stat))

        return on_stat, off_stat

    @property
    def total_stat(self):
        """Statistic summed over all bins and all observations

        This is what is used for the fit
        """
        total_stat = np.sum(self.statval, dtype=np.float64)
        return total_stat

    def _restrict_statval(self):
        """Apply valid fit range to statval
        """
        restricted_statval = list()
        for statval, valid_range in zip(self.statval, self._bins_in_fit_range):
            # Find bins outside safe range
            idx = np.where(np.invert(valid_range))[0]
            statval[0][idx] = 0
            statval[1][idx] = 0

    def _check_valid_fit(self):
        """Helper function to give usefull error messages"""
        # TODO: Check if IRFs are given for forward folding
        if self.stat == 'wstat' and self.obs_list[0].off_vector is None:
            raise ValueError('Off vector required for WStat fit')

    def likelihood_1d(self, model, parname, parvals):
        """Compute likelihood profile

        Parameters
        ----------
        model : `~gammapy.spectrum.models.SpectralModel`
            Model to draw likelihood profile for
        parname : str
            Parameter to calculate profile for
        parvals : `~astropy.units.Quantity`
            Parameter values
        """
        likelihood = list()
        self.model = model
        for val in parvals:
            self.model.parameters[parname].value = val
            self.predict_counts()
            self.calc_statval()
            likelihood.append(self.total_stat)
        return np.array(likelihood)

    def plot_likelihood_1d(self, ax=None, **kwargs):
        """Plot 1D likelihood profile

        see :func:`~gammapy.spectrum.SpectrumFit.likelihood_1d`
        """
        import matplotlib.pyplot as plt
        ax = plt.gca() if ax is None else ax

        yy = self.likelihood_1d(**kwargs)
        ax.plot(kwargs['parvals'], yy)
        ax.set_xlabel(kwargs['parname'])

        return ax

    def fit(self):
        """Run the fit"""
        self._check_valid_fit()
        if self.method == 'sherpa':
            self._fit_sherpa()
        else:
            raise NotImplementedError('{}'.format(self.method))

    def _fit_sherpa(self):
        """Wrapper around sherpa minimizer
        """
        from sherpa.fit import Fit
        from sherpa.data import Data1DInt
        from sherpa.optmethods import NelderMead

        binning = self.obs_list[0].e_reco
        # The sherpa data object is not usued in the fit. It is set to the
        # first observation for debugging purposes, see below
        data = self.obs_list[0].on_vector.data.data.value
        data = Data1DInt('Dummy data', binning[:-1].value, binning[1:].value,
                         data)
        # DEBUG
        #from sherpa.models import PowLaw1D
        #from sherpa.stats import Cash
        #model = PowLaw1D('sherpa')
        #model.ref = 0.1
        #fit = Fit(data, model, Cash(), NelderMead())

        # NOTE: We cannot use the Levenbergr-Marquart optimizer in Sherpa
        # because it relies on the fvec return value of the fit statistic (we
        # return None). The computation of fvec is not straightforwad, not just
        # stats per bin. E.g. for a cash fit the sherpa stat computes it
        # according to cstat
        # see https://github.com/sherpa/sherpa/blob/master/sherpa/include/sherpa/stats.hh#L122

        self._sherpa_fit = Fit(data, SherpaModel(self), SherpaStat(self),
                               NelderMead())
        fitresult = self._sherpa_fit.fit()
        log.debug(fitresult)
        self._make_fit_result()

    def _make_fit_result(self):
        """Bunde fit results into `~gammapy.spectrum.SpectrumFitResult`

        It is important to copy best fit values, because the error estimation
        will change the model parameters and statval again
        """
        from . import SpectrumFitResult

        model = self.model.copy()
        if self.background_model is not None:
            bkg_model = self.background_model.copy()
        else:
            bkg_model = None

        covariance = None
        covar_axis = None
        statname = self.stat

        for idx, obs in enumerate(self.obs_list):
            fit_range = self.true_fit_range[idx]
            statval = np.sum(self.statval[idx])
            npred_src = copy.deepcopy(self.predicted_counts[idx][0])
            npred_bkg = copy.deepcopy(self.predicted_counts[idx][1])
            self.result.append(
                SpectrumFitResult(model=model,
                                  covariance=covariance,
                                  covar_axis=covar_axis,
                                  fit_range=fit_range,
                                  statname=statname,
                                  statval=statval,
                                  npred_src=npred_src,
                                  npred_bkg=npred_bkg,
                                  background_model=bkg_model,
                                  obs=obs))

    def est_errors(self):
        """Estimate errors"""
        if self.err_method == 'sherpa':
            self._est_errors_sherpa()
        else:
            raise NotImplementedError('{}'.format(self.err_method))
        for res in self.result:
            res.covar_axis = self.covar_axis
            res.covariance = self.covariance
            res.model.parameters.set_parameter_covariance(
                self.covariance, self.covar_axis)

    def _est_errors_sherpa(self):
        """Wrapper around Sherpa error estimator"""
        covar = self._sherpa_fit.est_errors()
        covar_axis = list()
        for idx, par in enumerate(covar.parnames):
            name = par.split('.')[-1]
            covar_axis.append(name)
        self.covar_axis = covar_axis
        self.covariance = copy.deepcopy(covar.extra_output)

    def compute_fluxpoints(self, binning):
        """Compute `~DifferentialFluxPoints` for best fit model

        TODO: Implement

        Parameters
        ----------
        binning : `~astropy.units.Quantity`
            Energy binning, see
            :func:`~gammapy.spectrum.utils.calculate_flux_point_binning` for a
            method to get flux points with a minimum significance.

        Returns
        -------
        result : `~gammapy.spectrum.SpectrumResult`
        """
        raise NotImplementedError()

    def run(self, outdir=None):
        """Run all steps and write result to disk

        Parameters
        ----------
        outdir : Path, str
            directory to write results files to
        """
        cwd = Path.cwd()
        outdir = cwd if outdir is None else make_path(outdir)
        outdir.mkdir(exist_ok=True)
        os.chdir(str(outdir))

        self.fit()
        self.est_errors()

        # Assume only one model is fit to all data
        modelname = self.result[0].model.__class__.__name__
        filename = 'fit_result_{}.yaml'.format(modelname)
        log.info('Writing {}'.format(filename))
        self.result[0].to_yaml(filename)
        os.chdir(str(cwd))
Exemplo n.º 10
0
    def fit(self):
        """Fit spectrum"""
        from sherpa.fit import Fit
        from sherpa.models import ArithmeticModel, SimulFitModel
        from sherpa.astro.instrument import Response1D
        from sherpa.data import DataSimulFit

        # Reset results
        self._result = list()

        # Translate model to sherpa model if necessary
        if isinstance(self.model, models.SpectralModel):
            model = self.model.to_sherpa()
        else:
            model = self.model

        if not isinstance(model, ArithmeticModel):
            raise ValueError('Model not understood: {}'.format(model))

        # Make model amplitude O(1e0)
        val = model.ampl.val * self.FLUX_FACTOR ** (-1)
        model.ampl = val

        if self.fit_range is not None:
            log.info('Restricting fit range to {}'.format(self.fit_range))
            fitmin = self.fit_range[0].to('keV').value
            fitmax = self.fit_range[1].to('keV').value

        # Loop over observations
        pha = list()
        folded_model = list()
        nobs = len(self.obs_list)
        for ii in range(nobs):
            temp = self.obs_list[ii].to_sherpa()
            if self.fit_range is not None:
                temp.notice(fitmin, fitmax)
                if temp.get_background() is not None:
                    temp.get_background().notice(fitmin, fitmax)
            temp.ignore_bad()
            if temp.get_background() is not None:
                temp.get_background().ignore_bad()
            pha.append(temp)
            log.debug('Noticed channels obs {}: {}'.format(
                ii, temp.get_noticed_channels()))
            # Forward folding
            resp = Response1D(pha[ii])
            folded_model.append(resp(model) * self.FLUX_FACTOR)

        if (len(pha) == 1 and len(pha[0].get_noticed_channels()) == 1):
            raise ValueError('You are trying to fit one observation in only '
                             'one bin, error estimation will fail')

        data = DataSimulFit('simul fit data', pha)
        log.debug(data)
        fitmodel = SimulFitModel('simul fit model', folded_model)
        log.debug(fitmodel)

        fit = Fit(data, fitmodel, self.statistic, method=self.method_fit)

        fitresult = fit.fit()
        log.debug(fitresult)
        # The model instance passed to the Fit now holds the best fit values
        covar = fit.est_errors()
        log.debug(covar)

        for ii in range(nobs):
            efilter = pha[ii].get_filter()
            # Skip observations not participating in the fit
            if efilter != '':
                shmodel = fitmodel.parts[ii]
                result = _sherpa_to_fitresult(shmodel, covar, efilter, fitresult)
                result.obs = self.obs_list[ii]
            else:
                result = None
            self._result.append(result)

        valid_result = np.nonzero(self.result)[0][0]
        global_result = copy.deepcopy(self.result[valid_result])
        global_result.npred = None
        global_result.obs = None
        all_fitranges = [_.fit_range for _ in self._result if _ is not None]
        fit_range_min = min([_[0] for _ in all_fitranges])
        fit_range_max = max([_[1] for _ in all_fitranges])
        global_result.fit_range = u.Quantity((fit_range_min, fit_range_max))
        self._global_result = global_result
Exemplo n.º 11
0
    def fit(self):
        """Fit spectrum"""
        from sherpa.fit import Fit
        from sherpa.models import ArithmeticModel, SimulFitModel
        from sherpa.astro.instrument import Response1D
        from sherpa.data import DataSimulFit

        # Reset results
        self._result = list()

        # Translate model to sherpa model if necessary
        if isinstance(self.model, models.SpectralModel):
            model = self.model.to_sherpa()
        else:
            model = self.model

        if not isinstance(model, ArithmeticModel):
            raise ValueError('Model not understood: {}'.format(model))

        # Make model amplitude O(1e0)
        val = model.ampl.val * self.FLUX_FACTOR**(-1)
        model.ampl = val

        if self.fit_range is not None:
            log.info('Restricting fit range to {}'.format(self.fit_range))
            fitmin = self.fit_range[0].to('keV').value
            fitmax = self.fit_range[1].to('keV').value

        # Loop over observations
        pha = list()
        folded_model = list()
        nobs = len(self.obs_list)
        for ii in range(nobs):
            temp = self.obs_list[ii].to_sherpa()
            if self.fit_range is not None:
                temp.notice(fitmin, fitmax)
                if temp.get_background() is not None:
                    temp.get_background().notice(fitmin, fitmax)
            temp.ignore_bad()
            if temp.get_background() is not None:
                temp.get_background().ignore_bad()
            pha.append(temp)
            log.debug('Noticed channels obs {}: {}'.format(
                ii, temp.get_noticed_channels()))
            # Forward folding
            resp = Response1D(pha[ii])
            folded_model.append(resp(model) * self.FLUX_FACTOR)

        if (len(pha) == 1 and len(pha[0].get_noticed_channels()) == 1):
            raise ValueError('You are trying to fit one observation in only '
                             'one bin, error estimation will fail')

        data = DataSimulFit('simul fit data', pha)
        log.debug(data)
        fitmodel = SimulFitModel('simul fit model', folded_model)
        log.debug(fitmodel)

        fit = Fit(data, fitmodel, self.statistic)

        fitresult = fit.fit()
        log.debug(fitresult)
        # The model instance passed to the Fit now holds the best fit values
        covar = fit.est_errors()
        log.debug(covar)

        for ii in range(nobs):
            efilter = pha[ii].get_filter()
            # Skip observations not participating in the fit
            if efilter != '':
                shmodel = fitmodel.parts[ii]
                result = _sherpa_to_fitresult(shmodel, covar, efilter,
                                              fitresult)
                result.obs = self.obs_list[ii]
            else:
                result = None
            self._result.append(result)

        valid_result = np.nonzero(self.result)[0][0]
        global_result = copy.deepcopy(self.result[valid_result])
        global_result.npred = None
        global_result.obs = None
        all_fitranges = [_.fit_range for _ in self._result if _ is not None]
        fit_range_min = min([_[0] for _ in all_fitranges])
        fit_range_max = max([_[1] for _ in all_fitranges])
        global_result.fit_range = u.Quantity((fit_range_min, fit_range_max))
        self._global_result = global_result
Exemplo n.º 12
0
class test_sim(SherpaTestCase):

    @requires_fits
    @requires_xspec
    def setUp(self):
        from sherpa.astro.io import read_pha
        from sherpa.astro.xspec import XSwabs, XSpowerlaw
        # self.startdir = os.getcwd()
        self.old_level = logger.getEffectiveLevel()
        logger.setLevel(logging.CRITICAL)

        pha = self.make_path("refake_0934_1_21_1e4.fak")
        # rmf = self.make_path("ccdid7_default.rmf")
        # arf = self.make_path("quiet_0934.arf")

        self.simarf = self.make_path("aref_sample.fits")
        self.pcaarf = self.make_path("aref_Cedge.fits")

        data = read_pha(pha)
        data.ignore(None, 0.3)
        data.ignore(7.0, None)

        rsp = Response1D(data)
        self.abs1 = XSwabs('abs1')
        self.p1 = XSpowerlaw('p1')
        model = rsp(self.abs1 * self.p1)

        self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())

    def tearDown(self):
        # os.chdir(self.startdir)
        if hasattr(self, 'old_level'):
            logger.setLevel(self.old_level)

    @requires_xspec
    @requires_data
    def test_pragbayes_simarf(self):
        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("PragBayes")
        mcmc.set_sampler_opt("simarf", self.simarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 7)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
        # try:
        #     assert (covar_results.parmaxes < params.std(1)).all()
        # except AssertionError:
        #     print 'covar: ', str(covar_results.parmaxes)
        #     print 'param: ', str(params.std(1))
        #     raise

    @requires_xspec
    @requires_data
    def test_pragbayes_pcaarf(self):
        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("pragBayes")
        mcmc.set_sampler_opt("simarf", self.pcaarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 5)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
Exemplo n.º 13
0
class test_sim(SherpaTestCase):

    @unittest.skipIf(not has_fits_support(),
                     'need pycrates, pyfits or astropy.io.fits')
    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    def setUp(self):
        try:
            from sherpa.astro.io import read_pha
            from sherpa.astro.xspec import XSwabs, XSpowerlaw
        except:
            return
        #self.startdir = os.getcwd()
        self.old_level = logger.getEffectiveLevel()
        logger.setLevel(logging.CRITICAL)

        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        pha = os.path.join(datadir, "refake_0934_1_21_1e4.fak")
        rmf = os.path.join(datadir, "ccdid7_default.rmf")
        arf = os.path.join(datadir, "quiet_0934.arf")

        self.simarf = os.path.join(datadir, "aref_sample.fits")
        self.pcaarf = os.path.join(datadir, "aref_Cedge.fits")

        data = read_pha(pha)
        data.ignore(None,0.3)
        data.ignore(7.0,None)

        rsp = Response1D(data)
        self.abs1 = XSwabs('abs1')
        self.p1 = XSpowerlaw('p1')
        model = rsp(self.abs1*self.p1)

        self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())


    def tearDown(self):
        #os.chdir(self.startdir)
        if hasattr(self,'old_level'):
            logger.setLevel(self.old_level)

    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    @unittest.skipIf(test_data_missing(), "required test data missing")
    def test_pragbayes_simarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("PragBayes")
        mcmc.set_sampler_opt("simarf", self.simarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 7)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
        # try:
        #     assert (covar_results.parmaxes < params.std(1)).all()
        # except AssertionError:
        #     print 'covar: ', str(covar_results.parmaxes)
        #     print 'param: ', str(params.std(1))
        #     raise

    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    @unittest.skipIf(test_data_missing(), "required test data missing")
    def test_pragbayes_pcaarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("pragBayes")
        mcmc.set_sampler_opt("simarf", self.pcaarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 5)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
Exemplo n.º 14
0
report("res3.statval == res2.statval")
report("res3.statval - res2.statval")

report("res2.parvals")
report("res3.parvals")

for p2, p3 in zip(res2.parvals, res3.parvals):
    print("{:+.2e}".format(p3 - p2))

from sherpa.utils import calc_mlr
report("calc_mlr(res.dof - res2.dof, res.statval - res2.statval)")

report("f.estmethod.name")

coverrs = f.est_errors()
report("coverrs.format()")

report("coverrs")

dump("f.estmethod.sigma")
f.estmethod.sigma = 1.6
coverrs90 = f.est_errors()
report("coverrs90.format()")

dump("coverrs90.percent")

report("coverrs.extra_output")

print([p.split('.')[1] for p in coverrs.parnames])
Exemplo n.º 15
0
    def prepare_spectra(nH: float,
                        group: int = 1,
                        add_gal: bool = False,
                        redshift: Optional[float] = None,
                        **kwargs) -> float:
        """
        Fit the spectra using an absorbed powerlaw model using the Wstat statistic. The function also returns a p-value for the gof.
        :param nH: The galactic absorption column density in units of 10^22 /cm3
        :param group: The number of counts per energy bin
        :param add_gal: Setting this to True would add an intrinsic abrosption column density along side the galactic one
        :param redshift: The redshift to use in the fit. Only takes effect if add_gal is set to True
        ...
        :return: Returns the p-value of the gof. The null hypothesis states that the model and the observation differ while alternate says that the model explains the data
        """

        pha = read_pha("core_spectrum.pi")
        pha.set_analysis("energy")
        pha.notice(0.5, 7.0)
        tabs = ~pha.mask
        pha.group_counts(group, tabStops=tabs)
        x = pha.get_x()
        x = pha.apply_filter(x, pha._middle)
        y = pha.get_y(filter=True)
        pha.set_analysis("energy")

        model = xsphabs.abs1 * powlaw1d.srcp1
        print("Fitting the spectrum")

        zFlag = False
        if (nH is not None) and (nH > 0.0):
            if add_gal == 1:
                model = xsphabs.gal * xszphabs.abs1 * powlaw1d.srcp
                gal.nH = nH
                freeze(gal.nH)
                zFlag = True

            else:
                model = xsphabs.abs1 * powlaw1d.srcp1
                abs1.nH = nH
                freeze(abs1.nH)
        else:
            model = xszphabs.abs1 * powlaw1d.srcp1
            zFlag = True

        if zFlag is True and add_gal == 1:
            # print('REDSHIFT',redshift)
            abs1.redshift = redshift
            freeze(abs1.redshift)

        full_model = RSPModelPHA(pha.get_arf(), pha.get_rmf(), pha,
                                 pha.exposure * model)

        print(full_model)

        fit = Fit(pha, full_model, method=MonCar(), stat=WStat())
        res = fit.fit()

        print(res.format())
        print(fit.est_errors())

        # calculate the p-value for wstat
        mplot2 = ModelPlot()
        mplot2.prepare(pha, full_model)

        miu = mplot2.y * pha.exposure * 0.0146
        obs = y * pha.exposure * 0.0146

        c, ce, cv = SpecUtils.estimate_gof_cstat(miu, obs)

        #print(f"C0={c},C_e={ce},C_v={cv}")

        zval = (fit.calc_stat() - ce) / np.sqrt(cv)

        if zval > 0:
            pval = special.erfc(zval / np.sqrt(2))
        else:
            pval = special.erf(abs(zval) / np.sqrt(2))

        print(f"p-value for wstat = {pval}")

        set_data(pha)
        set_model(model)
        save_chart_spectrum("core_flux_chart.dat", elow=0.5, ehigh=7.0)
        # save_chart_spectrum("core_flux_chart.rdb",format='text/tsv', elow=0.5, ehigh=7.0)
        SAOTraceUtils.save_spectrum_rdb("core_flux_chart.dat")

        return pval
Exemplo n.º 16
0
class test_sim(SherpaTestCase):

    _fit_results_bench = {
        'rstat': 89.29503933428586,
        'qval': 0.0,
        'succeeded': 1,
        'numpoints': 100,
        'dof': 95,
        'nfev': 93,
        'statval': 8483.0287367571564,
        'parnames': ['p1.gamma', 'p1.ampl', 'g1.fwhm',
                     'g1.pos', 'g1.ampl'], 
        'parvals': numpy.array(
            [1.0701938169914813,
             9.1826254677279469,
             2.5862083052721028,
             2.601619746022207,
             47.262657692418749])
        }

    _x = numpy.arange(0.1, 10.1, 0.1)
    _y = numpy.array(
        [ 114, 47, 35, 30, 40, 27, 30, 26, 24, 20, 26, 35,
          29, 28, 34, 36, 43, 39, 33, 47, 44, 46, 53, 56,
          52, 53, 49, 57, 49, 36, 33, 42, 49, 45, 42, 32,
          31, 34, 18, 24, 25, 11, 17, 17, 11,  9,  8,  5,
           4, 10,  3,  4,  6,  3,  0,  2,  4,  4,  0,  1,
           2,  0,  3,  3,  0,  2,  1,  2,  3,  0,  1,  0,
           1,  0,  0,  1,  3,  3,  0,  2,  0,  0,  1,  2,
           0,  1,  0,  1,  1,  0,  1,  1,  1,  1,  1,  1,
           1,  0,  1,  0
          ]
        )
    _err = numpy.ones(100)*0.4


    def setUp(self):
        data = Data1D('fake', self._x, self._y, self._err)

        g1 = Gauss1D('g1')
        g1.fwhm.set(1.0, _tiny, _max, frozen=False)
        g1.pos.set(1.0, -_max, _max, frozen=False)
        g1.ampl.set(1.0, -_max, _max, frozen=False)
        p1 = PowLaw1D('p1')
        p1.gamma.set(1.0, -10, 10, frozen=False)
        p1.ampl.set(1.0, 0.0, _max, frozen=False)
        p1.ref.set(1.0, -_max, _max, frozen=True)      
        model = p1 + g1

        method = LevMar()
        method.config['maxfev'] = 10000
        method.config['ftol'] = float(_eps)
        method.config['epsfcn'] = float(_eps)
        method.config['gtol'] = float(_eps)
        method.config['xtol'] = float(_eps)
        method.config['factor'] = float(100)

        self.fit = Fit(data, model, Chi2DataVar(), method, Covariance())
        results = self.fit.fit()

        for key in ["succeeded", "numpoints", "nfev"]:
            assert self._fit_results_bench[key] == int(getattr(results, key))

        for key in ["rstat", "qval", "statval", "dof"]:
            assert numpy.allclose(float(self._fit_results_bench[key]),
                                  float(getattr(results, key)),
                                  1.e-7, 1.e-7)

        for key in ["parvals"]:
            try:
                assert numpy.allclose(self._fit_results_bench[key],
                                      getattr(results, key),
                                      1.e-4, 1.e-4)
            except AssertionError:
                print 'parvals bench: ', self._fit_results_bench[key]
                print 'parvals fit:   ', getattr(results, key)
                print 'results', results
                raise

        covresults = self.fit.est_errors()
        self.dof = results.dof
        self.mu = numpy.array(results.parvals)
        self.cov = numpy.array(covresults.extra_output)
        self.num = 10


    def test_student_t(self):
        multivariate_t(self.mu, self.cov, self.dof, self.num)
        
    def test_cauchy(self):
        multivariate_cauchy(self.mu, self.cov, self.num)       

    def test_parameter_scale_vector(self):
        ps = ParameterScaleVector()
        ps.get_scales(self.fit)

    def test_parameter_scale_matrix(self):
        ps = ParameterScaleMatrix()
        ps.get_scales(self.fit)

    def test_uniform_parameter_sample(self):
        up = UniformParameterSampleFromScaleVector()
        up.get_sample(self.fit, num=self.num)
        
    def test_normal_parameter_sample_vector(self):
        np = NormalParameterSampleFromScaleVector()
        np.get_sample(self.fit, num=self.num)

    def test_normal_parameter_sample_matrix(self):
        np = NormalParameterSampleFromScaleMatrix()
        np.get_sample(self.fit, num=self.num)

    def test_t_parameter_sample_matrix(self):
        np = StudentTParameterSampleFromScaleMatrix()
        np.get_sample(self.fit, self.dof, num=self.num)

    def test_uniform_sample(self):
        up = UniformSampleFromScaleVector()
        up.get_sample(self.fit, num=self.num)
        
    def test_normal_sample_vector(self):
        np = NormalSampleFromScaleVector()
        np.get_sample(self.fit, num=self.num)

    def test_normal_sample_matrix(self):
        np = NormalSampleFromScaleMatrix()
        np.get_sample(self.fit, num=self.num)

    def test_t_sample_matrix(self):
        np = StudentTSampleFromScaleMatrix()
        np.get_sample(self.fit, self.num, self.dof)

    def test_uniform_sample(self):
        uniform_sample(self.fit, num=self.num)

    def test_normal_sample(self):
        normal_sample(self.fit, num=self.num, correlate=False)

    def test_normal_sample_correlated(self):
        normal_sample(self.fit, num=self.num, correlate=True)

    def test_t_sample(self):
        t_sample(self.fit, self.num, self.dof)

    def test_lrt(self):
        results = LikelihoodRatioTest.run(self.fit, self.fit.model.lhs,
                                          self.fit.model, niter=25)


    def test_mh(self):

        self.fit.method = NelderMead()
        self.fit.stat = Cash()
        results = self.fit.fit()
        results = self.fit.est_errors()
        cov = results.extra_output

        mcmc = MCMC()
        
        samplers = mcmc.list_samplers()
        priors = mcmc.list_priors()
        for par in self.fit.model.pars:
            mcmc.set_prior(par, flat)
            prior = mcmc.get_prior(par)

        sampler = mcmc.get_sampler()
        name = mcmc.get_sampler_name()

        mcmc.set_sampler('MH')

        opt = mcmc.get_sampler_opt('defaultprior')
        mcmc.set_sampler_opt('defaultprior', opt)
        #mcmc.set_sampler_opt('verbose', True)

        log = logging.getLogger("sherpa")
        level = log.level
        log.setLevel(logging.ERROR)
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=1e2)
        log.setLevel(level)


    def test_metropolisMH(self):

        self.fit.method = NelderMead()
        self.fit.stat = CStat()
        results = self.fit.fit()
        results = self.fit.est_errors()
        cov = results.extra_output

        mcmc = MCMC()
        mcmc.set_sampler('MetropolisMH')
        #mcmc.set_sampler_opt('verbose', True)
        
        log = logging.getLogger("sherpa")
        level = log.level
        log.setLevel(logging.ERROR)
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=1e2)
        log.setLevel(level)
 

    def tearDown(self):
        pass
Exemplo n.º 17
0
def prepare_spectra(group, nH, add_gal, redshift):
    pha = read_pha("core_spectrum.pi")
    pha.set_analysis("energy")
    pha.notice(0.5, 7.0)
    tabs = ~pha.mask
    pha.group_counts(group, tabStops=tabs)
    x = pha.get_x()
    x = pha.apply_filter(x, pha._middle)
    y = pha.get_y(filter=True)
    pha.set_analysis("energy")

    model = xsphabs.abs1 * powlaw1d.srcp1
    print("Fitting the spectrum")

    zFlag = False
    if (nH is not None) and (nH > 0.0):
        if add_gal == 1:
            model = xsphabs.gal * xszphabs.abs1 * powlaw1d.srcp
            gal.nH = nH
            freeze(gal.nH)
            zFlag = True

        else:
            model = xsphabs.abs1 * powlaw1d.srcp1
            abs1.nH = nH
            freeze(abs1.nH)
    else:
        model = xszphabs.abs1 * powlaw1d.srcp1
        zFlag = True

    if zFlag is True and add_gal == 1:
        # print('REDSHIFT',redshift)
        abs1.redshift = redshift
        freeze(abs1.redshift)

    full_model = RSPModelPHA(pha.get_arf(), pha.get_rmf(), pha, pha.exposure * model)

    print(full_model)

    fit = Fit(pha, full_model, method=MonCar(), stat=WStat())
    res = fit.fit()

    print(res.format())
    print(fit.est_errors())

    # calculate the p-value for wstat
    mplot2 = ModelPlot()
    mplot2.prepare(pha, full_model)

    miu = mplot2.y * pha.exposure * 0.0146
    obs = y * pha.exposure * 0.0146

    c, ce, cv = gof_cstat(miu, obs)

    print(f"C0={c},C_e={ce},C_v={cv}")

    zval = (fit.calc_stat() - ce) / np.sqrt(cv)

    if zval > 0:
        pval = special.erfc(zval / np.sqrt(2))
    else:
        pval = special.erf(abs(zval) / np.sqrt(2))

    print(f"p-value for wstat = {pval}")

    set_data(pha)
    set_model(model)
    save_chart_spectrum("core_flux_chart.dat", elow=0.5, ehigh=7.0)
    # save_chart_spectrum("core_flux_chart.rdb",format='text/tsv', elow=0.5, ehigh=7.0)
    save_spectrum_rdb("core_flux_chart.dat")
Exemplo n.º 18
0
Arquivo: main.py Projeto: pllim/saba
class SherpaFitter(Fitter):
    __doc__ = """
    Sherpa Fitter for astropy models.

    Parameters
    ----------
    optimizer : string
        the name of a sherpa optimizer.
        posible options include
        {opt}
    statistic : string
        the name of a sherpa statistic.
        posible options include
        {stat}
    estmethod : string
        the name of a sherpa estmethod.
        possible options include
        {est}

    """.format(opt=", ".join(OptMethod._sherpa_values.keys()),
               stat=", ".join(Stat._sherpa_values.keys()),
               est=", ".join(EstMethod._sherpa_values.keys()))  # is this evil?

    supported_constraints = ['bounds', 'fixed','tied']

    def __init__(self, optimizer="levmar", statistic="leastsq", estmethod="covariance"):
        try:
            optimizer = optimizer.value
        except AttributeError:
            optimizer = OptMethod(optimizer).value

        try:
            statistic = statistic.value
        except AttributeError:
            statistic = Stat(statistic).value

        super(SherpaFitter, self).__init__(optimizer=optimizer, statistic=statistic)

        try:
            self._est_method = estmethod.value()
        except AttributeError:
            self._est_method = EstMethod(estmethod).value()

        self.fit_info = {}
        self._fitter = None  # a handle for sherpa fit function
        self._fitmodel = None  # a handle for sherpa fit model
        self._data = None  # a handle for sherpa dataset
        self.error_info = {}

        setattr(self.__class__, 'opt_config', property(lambda s: s._opt_config, doc=self._opt_method.__doc__))
        # sherpa doesn't currently have a docstring for est_method but maybe the future
        setattr(self.__class__, 'est_config', property(lambda s: s._est_config, doc=self._est_method.__doc__))


    def __call__(self, models, x, y, z=None, xbinsize=None, ybinsize=None, err=None, bkg=None, bkg_scale=1, **kwargs):
        """
        Fit the astropy model with a the sherpa fit routines.


        Parameters
        ----------
        models : `astropy.modeling.FittableModel` or list of `astropy.modeling.FittableModel`
            model to fit to x, y, z
        x : array or list of arrays
            input coordinates (independent for 1D & 2D fits)
        y : array or list of arrays
            input coordinates (dependent for 1D fits or independent for 2D fits)
        z : array or list of arrays (optional)
            input coordinates (dependent for 2D fits)
        xbinsize : array or list of arrays (optional)
            an array of xbinsizes in x  - this will be x -/+ (binsize  / 2.0)
        ybinsize : array or list of arrays (optional)
            an array of xbinsizes in y  - this will be y -/+ (ybinsize / 2.0)
        err : array or list of arrays (optional)
            an array of errors in dependent variable
        bkg : array or list of arrays (optional)
            this will act as background data
        bkg_sale : float or list of floats (optional)
            the scaling factor for the dataset if a single value
            is supplied it will be copied for each dataset
        **kwargs :
            keyword arguments will be passed on to sherpa fit routine

        Returns
        -------
        model_copy : `astropy.modeling.FittableModel` or a list of models.
            a copy of the input model with parameters set by the fitter
        """

        tie_list = []
        try:
            n_inputs = models[0].n_inputs
        except TypeError:
            n_inputs = models.n_inputs

        self._data = Dataset(n_inputs, x, y, z, xbinsize, ybinsize, err, bkg, bkg_scale)

        if self._data.ndata > 1:

            if len(models) == 1:
                self._fitmodel = ConvertedModel([models.copy() for _ in range(self._data.ndata)], tie_list)
                # Copy the model so each data set has the same model!
            elif len(models) == self._data.ndata:
                self._fitmodel = ConvertedModel(models, tie_list)
            else:
                raise Exception("Don't know how to handle multiple models "
                                "unless there is one foreach dataset")
        else:
            if len(models) > 1:
                self._data.make_simfit(len(models))
                self._fitmodel = ConvertedModel(models, tie_list)
            else:
                self._fitmodel = ConvertedModel(models)

        self._fitter = Fit(self._data.data, self._fitmodel.sherpa_model, self._stat_method, self._opt_method, self._est_method, **kwargs)
        self.fit_info = self._fitter.fit()

        return self._fitmodel.get_astropy_model()

    def est_errors(self, sigma=None, maxiters=None, numcores=1, methoddict=None, parlist=None):
        """
        Use sherpa error estimators based on the last fit.

        Parameters
        ----------
        sigma: float
            this will be set as the confidance interval for which the errors are found too.
        maxiters: int
            the maximum number of iterations the error estimator will run before giving up.
        methoddict: dict
            !not quite sure couldn't figure this one out yet!
        parlist: list
            a list of parameters to find the confidance interval of if none are provided all free
            parameters will be estimated.
        """
        if self._fitter is None:
            ValueError("Must complete a valid fit before errors can be calculated")
        if sigma is not None:
            self._fitter.estmethod.config['sigma'] = sigma
        if maxiters is not None:
            self._fitter.estmethod.config['maxiters'] = maxiters
        if 'numcores' in self._fitter.estmethod.config:
            if not numcores == self._fitter.estmethod.config['numcores']:
                self._fitter.estmethod.config['numcores'] = numcores

        self.error_info = self._fitter.est_errors(methoddict=methoddict, parlist=parlist)
        pnames = [p.split(".", 1)[-1] for p in self.error_info.parnames]  # this is to remove the model name
        return pnames, self.error_info.parvals, self.error_info.parmins, self.error_info.parmaxes

    @property
    def _est_config(self):
        """This returns the est.config property"""
        return self._est_method.config

    @property
    def _opt_config(self):
        """This returns the opt.config property"""
        return self._opt_method.config

    # Here is the MCMC wrapper!
    @format_doc("{__doc__}\n{mcmc_doc}",mcmc_doc=SherpaMCMC.__doc__)
    def get_sampler(self, *args, **kwargs):
        """
        This returns and instance of `SherpaMCMC` with it's self as the fitter
        """
        return SherpaMCMC(self, *args, **kwargs)
Exemplo n.º 19
0
class SherpaFitter(Fitter):
    __doc__ = """
    Sherpa Fitter for astropy models.

    Parameters
    ----------
    optimizer : string
        the name of a sherpa optimizer.
        posible options include
        {opt}
    statistic : string
        the name of a sherpa statistic.
        posible options include
        {stat}
    estmethod : string
        the name of a sherpa estmethod.
        possible options include
        {est}

    """.format(opt=", ".join(OptMethod._sherpa_values.keys()),
               stat=", ".join(Stat._sherpa_values.keys()),
               est=", ".join(EstMethod._sherpa_values.keys()))  # is this evil?


    def __init__(self, optimizer="levmar", statistic="leastsq", estmethod="covariance"):
        try:
            optimizer = optimizer.value
        except AttributeError:
            optimizer = OptMethod(optimizer).value

        try:
            statistic = statistic.value
        except AttributeError:
            statistic = Stat(statistic).value

        super(SherpaFitter, self).__init__(optimizer=optimizer, statistic=statistic)

        try:
            self._est_method = estmethod.value()
        except AttributeError:
            self._est_method = EstMethod(estmethod).value()

        self.fit_info = {}
        self._fitter = None  # a handle for sherpa fit function
        self._fitmodel = None  # a handle for sherpa fit model
        self._data = None  # a handle for sherpa dataset
        self.error_info = {}

        setattr(self.__class__, 'opt_config', property(lambda s: s._opt_config, doc=self._opt_method.__doc__))
        # sherpa doesn't currently have a docstring for est_method but maybe the future
        setattr(self.__class__, 'est_config', property(lambda s: s._est_config, doc=self._est_method.__doc__))  


    def __call__(self, models, x, y, z=None, xbinsize=None, ybinsize=None, err=None, bkg=None, bkg_scale=1, **kwargs):
        """
        Fit the astropy model with a the sherpa fit routines.


        Parameters
        ----------
        models : `astropy.modeling.FittableModel` or list of `astropy.modeling.FittableModel`
            model to fit to x, y, z
        x : array or list of arrays
            input coordinates (independent for 1D & 2D fits)
        y : array or list of arrays
            input coordinates (dependent for 1D fits or independent for 2D fits)
        z : array or list of arrays (optional)
            input coordinates (dependent for 2D fits)
        xbinsize : array or list of arrays (optional)
            an array of xbinsizes in x  - this will be x -/+ (binsize  / 2.0)
        ybinsize : array or list of arrays (optional)
            an array of xbinsizes in y  - this will be y -/+ (ybinsize / 2.0)
        err : array or list of arrays (optional)
            an array of errors in dependent variable
        bkg : array or list of arrays (optional)
            this will act as background data
        bkg_sale : float or list of floats (optional)
            the scaling factor for the dataset if a single value
            is supplied it will be copied for each dataset
        **kwargs :
            keyword arguments will be passed on to sherpa fit routine

        Returns
        -------
        model_copy : `astropy.modeling.FittableModel` or a list of models.
            a copy of the input model with parameters set by the fitter
        """

        tie_list = []
        try:
            n_inputs = models[0].n_inputs
        except TypeError:
            n_inputs = models.n_inputs

        self._data = Dataset(n_inputs, x, y, z, xbinsize, ybinsize, err, bkg, bkg_scale)

        if self._data.ndata > 1:

            if len(models) == 1:
                self._fitmodel = ConvertedModel([models.copy() for _ in xrange(self._data.ndata)], tie_list)
                # Copy the model so each data set has the same model!
            elif len(models) == self._data.ndata:
                self._fitmodel = ConvertedModel(models, tie_list)
            else:
                raise Exception("Don't know how to handle multiple models "
                                "unless there is one foreach dataset")
        else:
            if len(models) > 1:
                self._data.make_simfit(len(models))
                self._fitmodel = ConvertedModel(models, tie_list)
            else:
                self._fitmodel = ConvertedModel(models)

        self._fitter = Fit(self._data.data, self._fitmodel.sherpa_model, self._stat_method, self._opt_method, self._est_method, **kwargs)
        self.fit_info = self._fitter.fit()

        return self._fitmodel.get_astropy_model()

    def est_errors(self, sigma=None, maxiters=None, numcores=1, methoddict=None, parlist=None):
        """
        Use sherpa error estimators based on the last fit.

        Parameters
        ----------
        sigma: float
            this will be set as the confidance interval for which the errors are found too.
        maxiters: int
            the maximum number of iterations the error estimator will run before giving up.
        methoddict: dict
            !not quite sure couldn't figure this one out yet!
        parlist: list
            a list of parameters to find the confidance interval of if none are provided all free
            parameters will be estimated.
        """
        if self._fitter is None:
            ValueError("Must complete a valid fit before errors can be calculated")
        if sigma is not None:
            self._fitter.estmethod.config['sigma'] = sigma
        if maxiters is not None:
            self._fitter.estmethod.config['maxiters'] = maxiters
        if 'numcores' in self._fitter.estmethod.config:
            if not numcores == self._fitter.estmethod.config['numcores']:
                self._fitter.estmethod.config['numcores'] = numcores

        self.error_info = self._fitter.est_errors(methoddict=methoddict, parlist=parlist)
        pnames = [p.split(".", 1)[-1] for p in self.error_info.parnames]  # this is to remove the model name
        return pnames, self.error_info.parvals, self.error_info.parmins, self.error_info.parmaxes

    @property
    def _est_config(self):
        """This returns the est.config property"""
        return self._est_method.config

    @property
    def _opt_config(self):
        """This returns the opt.config property"""
        return self._opt_method.config

    # Here is the MCMC wrapper!
    @format_doc("{__doc__}\n{mcmc_doc}",mcmc_doc=SherpaMCMC.__doc__)
    def get_sampler(self, *args, **kwargs):
        """
        This returns and instance of `SherpaMCMC` with it's self as the fitter
        """
        return SherpaMCMC(self, *args, **kwargs)
Exemplo n.º 20
0
class test_sim(SherpaTestCase):

    _fit_results_bench = {
        'rstat':
        89.29503933428586,
        'qval':
        0.0,
        'succeeded':
        1,
        'numpoints':
        100,
        'dof':
        95,
        'nfev':
        93,
        'statval':
        8483.0287367571564,
        'parnames': ['p1.gamma', 'p1.ampl', 'g1.fwhm', 'g1.pos', 'g1.ampl'],
        'parvals':
        numpy.array([
            1.0701938169914813, 9.1826254677279469, 2.5862083052721028,
            2.601619746022207, 47.262657692418749
        ])
    }

    _x = numpy.arange(0.1, 10.1, 0.1)
    _y = numpy.array([
        114, 47, 35, 30, 40, 27, 30, 26, 24, 20, 26, 35, 29, 28, 34, 36, 43,
        39, 33, 47, 44, 46, 53, 56, 52, 53, 49, 57, 49, 36, 33, 42, 49, 45, 42,
        32, 31, 34, 18, 24, 25, 11, 17, 17, 11, 9, 8, 5, 4, 10, 3, 4, 6, 3, 0,
        2, 4, 4, 0, 1, 2, 0, 3, 3, 0, 2, 1, 2, 3, 0, 1, 0, 1, 0, 0, 1, 3, 3, 0,
        2, 0, 0, 1, 2, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0
    ])
    _err = numpy.ones(100) * 0.4

    def setUp(self):
        data = Data1D('fake', self._x, self._y, self._err)

        g1 = Gauss1D('g1')
        g1.fwhm.set(1.0, _tiny, _max, frozen=False)
        g1.pos.set(1.0, -_max, _max, frozen=False)
        g1.ampl.set(1.0, -_max, _max, frozen=False)
        p1 = PowLaw1D('p1')
        p1.gamma.set(1.0, -10, 10, frozen=False)
        p1.ampl.set(1.0, 0.0, _max, frozen=False)
        p1.ref.set(1.0, -_max, _max, frozen=True)
        model = p1 + g1

        method = LevMar()
        method.config['maxfev'] = 10000
        method.config['ftol'] = float(_eps)
        method.config['epsfcn'] = float(_eps)
        method.config['gtol'] = float(_eps)
        method.config['xtol'] = float(_eps)
        method.config['factor'] = float(100)

        self.fit = Fit(data, model, Chi2DataVar(), method, Covariance())
        results = self.fit.fit()

        for key in ["succeeded", "numpoints", "nfev"]:
            assert self._fit_results_bench[key] == int(getattr(results, key))

        for key in ["rstat", "qval", "statval", "dof"]:
            assert numpy.allclose(float(self._fit_results_bench[key]),
                                  float(getattr(results, key)), 1.e-7, 1.e-7)

        for key in ["parvals"]:
            try:
                assert numpy.allclose(self._fit_results_bench[key],
                                      getattr(results, key), 1.e-4, 1.e-4)
            except AssertionError:
                print 'parvals bench: ', self._fit_results_bench[key]
                print 'parvals fit:   ', getattr(results, key)
                print 'results', results
                raise

        covresults = self.fit.est_errors()
        self.dof = results.dof
        self.mu = numpy.array(results.parvals)
        self.cov = numpy.array(covresults.extra_output)
        self.num = 10

    def test_student_t(self):
        multivariate_t(self.mu, self.cov, self.dof, self.num)

    def test_cauchy(self):
        multivariate_cauchy(self.mu, self.cov, self.num)

    def test_parameter_scale_vector(self):
        ps = ParameterScaleVector()
        ps.get_scales(self.fit)

    def test_parameter_scale_matrix(self):
        ps = ParameterScaleMatrix()
        ps.get_scales(self.fit)

    def test_uniform_parameter_sample(self):
        up = UniformParameterSampleFromScaleVector()
        up.get_sample(self.fit, num=self.num)

    def test_normal_parameter_sample_vector(self):
        np = NormalParameterSampleFromScaleVector()
        np.get_sample(self.fit, num=self.num)

    def test_normal_parameter_sample_matrix(self):
        np = NormalParameterSampleFromScaleMatrix()
        np.get_sample(self.fit, num=self.num)

    def test_t_parameter_sample_matrix(self):
        np = StudentTParameterSampleFromScaleMatrix()
        np.get_sample(self.fit, self.dof, num=self.num)

    def test_uniform_sample(self):
        up = UniformSampleFromScaleVector()
        up.get_sample(self.fit, num=self.num)

    def test_normal_sample_vector(self):
        np = NormalSampleFromScaleVector()
        np.get_sample(self.fit, num=self.num)

    def test_normal_sample_matrix(self):
        np = NormalSampleFromScaleMatrix()
        np.get_sample(self.fit, num=self.num)

    def test_t_sample_matrix(self):
        np = StudentTSampleFromScaleMatrix()
        np.get_sample(self.fit, self.num, self.dof)

    def test_uniform_sample(self):
        uniform_sample(self.fit, num=self.num)

    def test_normal_sample(self):
        normal_sample(self.fit, num=self.num, correlate=False)

    def test_normal_sample_correlated(self):
        normal_sample(self.fit, num=self.num, correlate=True)

    def test_t_sample(self):
        t_sample(self.fit, self.num, self.dof)

    def test_lrt(self):
        results = LikelihoodRatioTest.run(self.fit,
                                          self.fit.model.lhs,
                                          self.fit.model,
                                          niter=25)

    def test_mh(self):

        self.fit.method = NelderMead()
        self.fit.stat = Cash()
        results = self.fit.fit()
        results = self.fit.est_errors()
        cov = results.extra_output

        mcmc = MCMC()

        samplers = mcmc.list_samplers()
        priors = mcmc.list_priors()
        for par in self.fit.model.pars:
            mcmc.set_prior(par, flat)
            prior = mcmc.get_prior(par)

        sampler = mcmc.get_sampler()
        name = mcmc.get_sampler_name()

        mcmc.set_sampler('MH')

        opt = mcmc.get_sampler_opt('defaultprior')
        mcmc.set_sampler_opt('defaultprior', opt)
        #mcmc.set_sampler_opt('verbose', True)

        log = logging.getLogger("sherpa")
        level = log.level
        log.setLevel(logging.ERROR)
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=1e2)
        log.setLevel(level)

    def test_metropolisMH(self):

        self.fit.method = NelderMead()
        self.fit.stat = CStat()
        results = self.fit.fit()
        results = self.fit.est_errors()
        cov = results.extra_output

        mcmc = MCMC()
        mcmc.set_sampler('MetropolisMH')
        #mcmc.set_sampler_opt('verbose', True)

        log = logging.getLogger("sherpa")
        level = log.level
        log.setLevel(logging.ERROR)
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=1e2)
        log.setLevel(level)

    def tearDown(self):
        pass
Exemplo n.º 21
0
class test_sim(SherpaTestCase):

    @unittest.skipIf(not has_fits_support(),
                     'need pycrates, pyfits')
    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    def setUp(self):
        try:
            from sherpa.astro.io import read_pha
            from sherpa.astro.xspec import XSwabs, XSpowerlaw
        except:
            return
        #self.startdir = os.getcwd()
        self.old_level = logger.getEffectiveLevel()
        logger.setLevel(logging.CRITICAL)

        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        pha = os.path.join(datadir, "refake_0934_1_21_1e4.fak")
        rmf = os.path.join(datadir, "ccdid7_default.rmf")
        arf = os.path.join(datadir, "quiet_0934.arf")
        
        self.simarf = os.path.join(datadir, "aref_sample.fits")
        self.pcaarf = os.path.join(datadir, "aref_Cedge.fits")

        data = read_pha(pha)
        data.ignore(None,0.3)
        data.ignore(7.0,None)

        rsp = Response1D(data)
        self.abs1 = XSwabs('abs1')
        self.p1 = XSpowerlaw('p1')
        model = rsp(self.abs1*self.p1)
        
        self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())


    def tearDown(self):
        #os.chdir(self.startdir)
        if hasattr(self,'old_level'):
            logger.setLevel(self.old_level)

    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    @unittest.skipIf(test_data_missing(), "required test data missing")
    def test_pragbayes_simarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("PragBayes")
        mcmc.set_sampler_opt("simarf", self.simarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 7)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
        # try:
        #     assert (covar_results.parmaxes < params.std(1)).all()
        # except AssertionError:
        #     print 'covar: ', str(covar_results.parmaxes)
        #     print 'param: ', str(params.std(1))
        #     raise

    @unittest.skipIf(not has_package_from_list('sherpa.astro.xspec'),
                     "required sherpa.astro.xspec module missing")
    @unittest.skipIf(test_data_missing(), "required test data missing")
    def test_pragbayes_pcaarf(self):
        datadir = SherpaTestCase.datadir
        if datadir is None:
            return

        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("pragBayes")
        mcmc.set_sampler_opt("simarf", self.pcaarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 5)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
Exemplo n.º 22
0
#source_model.ypos.freeze()
source_model.fwhm = 0.12
source_model.ampl = 1.0
#source_model.fwhm.freeze()
# Adding this constant background components the fit works with cash statistics as well
#spatial_model_bkg = Const2D('spatial-model-bkg')
#spectral_model_bkg = PowLaw1D('spectral-model-bkg')
#bkg_model = CombinedModel3D(spatial_model=spatial_model_bkg, spectral_model=spectral_model_bkg)

bkg = TableModel('bkg')
bkg.load(None, bkg_data.ravel())
# Freeze bkg amplitude
bkg.ampl = 1
bkg.ampl.freeze()

model = bkg + 1E-11 * (source_model)

# Fit
# For now only Chi2 statistics seems to work, using Cash, the optimizer doesn't run at all,
# maybe because of missing background model?
fit = Fit(data=cube,
          model=model,
          stat=Cash(),
          method=NelderMead(),
          estmethod=Covariance())
result = fit.fit()
err = fit.est_errors()
print(err)
fin = time.time()
print fin - debut
Exemplo n.º 23
0
class test_sim(SherpaTestCase):

    @requires_fits
    @requires_xspec
    def setUp(self):
        from sherpa.astro.io import read_pha
        from sherpa.astro.xspec import XSwabs, XSpowerlaw
        # self.startdir = os.getcwd()
        self.old_level = logger.getEffectiveLevel()
        logger.setLevel(logging.CRITICAL)

        pha = self.make_path("refake_0934_1_21_1e4.fak")
        # rmf = self.make_path("ccdid7_default.rmf")
        # arf = self.make_path("quiet_0934.arf")

        self.simarf = self.make_path("aref_sample.fits")
        self.pcaarf = self.make_path("aref_Cedge.fits")

        data = read_pha(pha)
        data.ignore(None,0.3)
        data.ignore(7.0,None)

        rsp = Response1D(data)
        self.abs1 = XSwabs('abs1')
        self.p1 = XSpowerlaw('p1')
        model = rsp(self.abs1*self.p1)

        self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())

    def tearDown(self):
        # os.chdir(self.startdir)
        if hasattr(self, 'old_level'):
            logger.setLevel(self.old_level)

    @requires_xspec
    @requires_data
    def test_pragbayes_simarf(self):
        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("PragBayes")
        mcmc.set_sampler_opt("simarf", self.simarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 7)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
        # try:
        #     assert (covar_results.parmaxes < params.std(1)).all()
        # except AssertionError:
        #     print 'covar: ', str(covar_results.parmaxes)
        #     print 'param: ', str(params.std(1))
        #     raise

    @requires_xspec
    @requires_data
    def test_pragbayes_pcaarf(self):
        mcmc = sim.MCMC()

        self.abs1.nh = 0.092886
        self.p1.phoindex = 0.994544
        self.p1.norm = 9.26369

        mcmc.set_sampler("pragBayes")
        mcmc.set_sampler_opt("simarf", self.pcaarf)
        mcmc.set_sampler_opt("p_M", 0.5)
        mcmc.set_sampler_opt("nsubiter", 5)

        covar_results = self.fit.est_errors()
        cov = covar_results.extra_output

        niter = 10
        stats, accept, params = mcmc.get_draws(self.fit, cov, niter=niter)
Exemplo n.º 24
0
class SpectrumFit(object):
    """Orchestrate a 1D counts spectrum fit.

    For usage examples see :ref:`spectral_fitting`

    Parameters
    ----------
    obs_list : `~gammapy.spectrum.SpectrumObservationList`, `~gammapy.spectrum.SpectrumObservation`
        Observation(s) to fit
    model : `~gammapy.spectrum.models.SpectralModel`
        Source model. Should return counts if ``forward_folded`` is False and a flux otherwise
    stat : {'wstat', 'cash'}
        Fit statistic
    forward_folded : bool, default: True
        Fold ``model`` with the IRFs given in ``obs_list``
    fit_range : tuple of `~astropy.units.Quantity`
        Fit range, will be convolved with observation thresholds. If you want to
        control which bins are taken into account in the fit for each
        observations, use :func:`~gammapy.spectrum.SpectrumObservation.qualitiy`
    background_model : `~gammapy.spectrum.models.SpectralModel`, optional
        Background model to be used in cash fits
    method : {'sherpa'}
        Optimization backend for the fit
    err_method : {'sherpa'}
        Optimization backend for error estimation
    """

    def __init__(self, obs_list, model, stat='wstat', forward_folded=True,
                 fit_range=None, background_model=None,
                 method='sherpa', err_method='sherpa'):
        self.obs_list = self._convert_obs_list(obs_list)
        self.model = model
        self.stat = stat
        self.forward_folded = forward_folded
        self.fit_range = fit_range
        self.background_model = background_model
        self.method = method
        self.err_method = err_method

        self._predicted_counts = None
        self._statval = None

        self.covar_axis = None
        self.covariance = None
        self.result = None

        self._check_valid_fit()
        self._apply_fit_range()

    def __str__(self):
        ss = self.__class__.__name__
        ss += '\nSource model {}'.format(self.model)
        ss += '\nStat {}'.format(self.stat)
        ss += '\nForward Folded {}'.format(self.forward_folded)
        ss += '\nFit range {}'.format(self.fit_range)
        if self.background_model is not None:
            ss += '\nBackground model {}'.format(self.background_model)
        ss += '\nBackend {}'.format(self.method)
        ss += '\nError Backend {}'.format(self.err_method)

        return ss

    @staticmethod
    def _convert_obs_list(obs_list):
        """Helper function to accept different inputs for obs_list."""
        if isinstance(obs_list, SpectrumObservation):
            obs_list = SpectrumObservationList([obs_list])
        if not isinstance(obs_list, SpectrumObservationList):
            raise ValueError('Invalid input {} for parameter obs_list'.format(
                type(obs_list)))
        return obs_list

    @property
    def bins_in_fit_range(self):
        """Bins participating in the fit for each observation."""
        return self._bins_in_fit_range

    @property
    def predicted_counts(self):
        """Current value of predicted counts.

        For each observation a tuple to counts for the on and off region is
        returned.
        """
        return self._predicted_counts

    @property
    def statval(self):
        """Current value of statval.

        For each observation the statval per bin is returned.
        """
        return self._statval

    @property
    def fit_range(self):
        """Fit range."""
        return self._fit_range

    @fit_range.setter
    def fit_range(self, fit_range):
        self._fit_range = fit_range
        self._apply_fit_range()

    @property
    def true_fit_range(self):
        """True fit range for each observation.

        True fit range is the fit range set in the
        `~gammapy.spectrum.SpectrumFit` with observation threshold taken into
        account.
        """
        true_range = []
        for binrange, obs in zip(self.bins_in_fit_range, self.obs_list):
            idx = np.where(binrange)[0]
            e_min = obs.e_reco[idx[0]]
            e_max = obs.e_reco[idx[-1] + 1]
            fit_range = u.Quantity((e_min, e_max))
            true_range.append(fit_range)
        return true_range

    def _apply_fit_range(self):
        """Mark bins within desired fit range for each observation."""
        self._bins_in_fit_range = []
        for obs in self.obs_list:
            # Take into account fit range
            energy = obs.e_reco
            valid_range = np.zeros(energy.nbins)

            if self.fit_range is not None:

                precision = 1e-3  # to avoid floating round precision
                idx_lo = np.where(energy * (1 + precision) < self.fit_range[0])[0]
                valid_range[idx_lo] = 1

                
                idx_hi = np.where(energy[:-1] * (1 - precision) > self.fit_range[1])[0]
                if len(idx_hi) != 0:
                    idx_hi = np.insert(idx_hi, 0, idx_hi[0] - 1)
                valid_range[idx_hi] = 1

            # Take into account thresholds
            try:
                quality = obs.on_vector.quality
            except AttributeError:
                quality = np.zeros(obs.e_reco.nbins)

            convolved = np.logical_and(1 - quality, 1 - valid_range)

            self._bins_in_fit_range.append(convolved)

    def predict_counts(self):
        """Predict counts for all observations.

        The result is stored as ``predicted_counts`` attribute.
        """
        predicted_counts = []
        for obs in self.obs_list:
            mu_sig = self._predict_counts_helper(obs,
                                                 self.model,
                                                 self.forward_folded)
            mu_bkg = None
            if self.background_model is not None:
                # For now, never fold background model with IRFs
                mu_bkg = self._predict_counts_helper(obs,
                                                     self.background_model,
                                                     False)
            counts = [mu_sig, mu_bkg]
            predicted_counts.append(counts)
        self._predicted_counts = predicted_counts

    def _predict_counts_helper(self, obs, model, forward_folded=True):
        """Predict counts for one observation.

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Response functions
        model : `~gammapy.spectrum.SpectralModel`
            Source or background model
        forward_folded : bool, default: True
            Fold model with IRFs

        Returns
        ------
        predicted_counts: `np.array`
            Predicted counts for one observation
        """
        predictor = CountsPredictor(model=model)
        if forward_folded:
            predictor.livetime = obs.livetime
            predictor.aeff = obs.aeff
            predictor.edisp = obs.edisp
        else:
            predictor.e_true = obs.e_reco

        predictor.run()
        counts = predictor.npred.data.data

        # Check count unit (~unit of model amplitude)
        cond = counts.unit.is_equivalent('ct') or counts.unit.is_equivalent('')
        if cond:
            counts = counts.value
        else:
            raise ValueError('Predicted counts {}'.format(counts))

        # Apply AREASCAL column
        counts *= obs.on_vector.areascal

        return counts

    def calc_statval(self):
        """Calc statistic for all observations.

        The result is stored as attribute ``statval``, bin outside the fit
        range are set to 0.
        """
        statval = []
        for obs, npred in zip(self.obs_list, self.predicted_counts):
            on_stat, off_stat = self._calc_statval_helper(obs, npred)
            statvals = (on_stat, off_stat)
            statval.append(statvals)
        self._statval = statval
        self._restrict_statval()

    def _calc_statval_helper(self, obs, prediction):
        """Calculate ``statval`` for one observation.

        Parameters
        ----------
        obs : `~gammapy.spectrum.SpectrumObservation`
            Measured counts
        prediction : tuple of `~numpy.ndarray`
            Predicted (on counts, off counts)

        Returns
        ------
        statsval : tuple of `~numpy.ndarray`
            Statval for (on, off)
        """
        stats_func = getattr(stats, self.stat)

        # Off stat = 0 by default
        off_stat = np.zeros(obs.e_reco.nbins)
        if self.stat == 'cash' or self.stat == 'cstat':
            if self.background_model is not None:
                mu_on = prediction[0] + prediction[1]
                on_stat = stats_func(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                mu_off = prediction[1] / obs.alpha
                off_stat = stats_func(n_on=obs.off_vector.data.data.value,
                                      mu_on=mu_off)
            else:
                mu_on = prediction[0]
                on_stat = stats_func(n_on=obs.on_vector.data.data.value,
                                     mu_on=mu_on)
                off_stat = np.zeros_like(on_stat)

        elif self.stat == 'wstat':
            kwargs = dict(n_on=obs.on_vector.data.data.value,
                          n_off=obs.off_vector.data.data.value,
                          alpha=obs.alpha,
                          mu_sig=prediction[0])
            # Store the result of the profile likelihood as bkg prediction
            mu_bkg = stats.get_wstat_mu_bkg(**kwargs)
            prediction[1] = mu_bkg * obs.alpha
            on_stat_ = stats_func(**kwargs)
            # The on_stat sometime contains nan values
            # TODO: Handle properly
            on_stat = np.nan_to_num(on_stat_)
            off_stat = np.zeros_like(on_stat)
        else:
            raise NotImplementedError('{}'.format(self.stat))

        return on_stat, off_stat

    @property
    def total_stat(self):
        """Statistic summed over all bins and all observations.

        This is what is used for the fit.
        """
        total_stat = np.sum(self.statval, dtype=np.float64)
        return total_stat

    def _restrict_statval(self):
        """Apply valid fit range to statval.
        """
        for statval, valid_range in zip(self.statval, self.bins_in_fit_range):
            # Find bins outside safe range
            idx = np.where(np.invert(valid_range))[0]
            statval[0][idx] = 0
            statval[1][idx] = 0

    def _check_valid_fit(self):
        """Helper function to give useful error messages."""
        # Assume that settings are the same for all observations
        test_obs = self.obs_list[0]
        irfs_exist = test_obs.aeff is not None or test_obs.edisp is not None
        if self.forward_folded and not irfs_exist:
            raise ValueError('IRFs required for forward folded fit')
        if self.stat == 'wstat' and self.obs_list[0].off_vector is None:
            raise ValueError('Off vector required for WStat fit')

    def likelihood_1d(self, model, parname, parvals):
        """Compute likelihood profile.

        Parameters
        ----------
        model : `~gammapy.spectrum.models.SpectralModel`
            Model to draw likelihood profile for
        parname : str
            Parameter to calculate profile for
        parvals : `~astropy.units.Quantity`
            Parameter values
        """
        likelihood = []
        self.model = model
        for val in parvals:
            self.model.parameters[parname].value = val
            self.predict_counts()
            self.calc_statval()
            likelihood.append(self.total_stat)
        return np.array(likelihood)

    def plot_likelihood_1d(self, ax=None, **kwargs):
        """Plot 1-dim likelihood profile.

        See :func:`~gammapy.spectrum.SpectrumFit.likelihood_1d`
        """
        import matplotlib.pyplot as plt
        ax = plt.gca() if ax is None else ax

        yy = self.likelihood_1d(**kwargs)
        ax.plot(kwargs['parvals'], yy)
        ax.set_xlabel(kwargs['parname'])

        return ax

    def fit(self):
        """Run the fit."""
        if self.method == 'sherpa':
            self._fit_sherpa()
        else:
            raise NotImplementedError('method: {}'.format(self.method))

    def _fit_sherpa(self):
        """Wrapper around sherpa minimizer."""
        from sherpa.fit import Fit
        from sherpa.data import Data1DInt
        from sherpa.optmethods import NelderMead
        from .sherpa_utils import SherpaModel, SherpaStat

        binning = self.obs_list[0].e_reco
        # The sherpa data object is not usued in the fit. It is set to the
        # first observation for debugging purposes, see below
        data = self.obs_list[0].on_vector.data.data.value
        data = Data1DInt('Dummy data', binning[:-1].value,
                         binning[1:].value, data)
        # DEBUG
        # from sherpa.models import PowLaw1D
        # from sherpa.stats import Cash
        # model = PowLaw1D('sherpa')
        # model.ref = 0.1
        # fit = Fit(data, model, Cash(), NelderMead())

        # NOTE: We cannot use the Levenbergr-Marquart optimizer in Sherpa
        # because it relies on the fvec return value of the fit statistic (we
        # return None). The computation of fvec is not straightforwad, not just
        # stats per bin. E.g. for a cash fit the sherpa stat computes it
        # according to cstat
        # see https://github.com/sherpa/sherpa/blob/master/sherpa/include/sherpa/stats.hh#L122

        self._sherpa_fit = Fit(data,
                               SherpaModel(self),
                               SherpaStat(self),
                               NelderMead())
        fitresult = self._sherpa_fit.fit()
        log.debug(fitresult)
        self._make_fit_result()

    def _make_fit_result(self):
        """Bundle fit results into `~gammapy.spectrum.SpectrumFitResult`.

        It is important to copy best fit values, because the error estimation
        will change the model parameters and statval again
        """
        from . import SpectrumFitResult

        model = self.model.copy()
        if self.background_model is not None:

            bkg_model = self.background_model.copy()
        else:
            bkg_model = None

        statname = self.stat

        results = []
        for idx, obs in enumerate(self.obs_list):
            fit_range = self.true_fit_range[idx]
            statval = np.sum(self.statval[idx])
            stat_per_bin = self.statval[idx]
            npred_src = copy.deepcopy(self.predicted_counts[idx][0])
            npred_bkg = copy.deepcopy(self.predicted_counts[idx][1])

            results.append(SpectrumFitResult(
                model=model,
                fit_range=fit_range,
                statname=statname,
                statval=statval,
                stat_per_bin=stat_per_bin,
                npred_src=npred_src,
                npred_bkg=npred_bkg,
                background_model=bkg_model,
                obs=obs
            ))

        self.result = results

    def est_errors(self):
        """Estimate parameter errors."""
        if self.err_method == 'sherpa':
            self._est_errors_sherpa()
        else:
            raise NotImplementedError('{}'.format(self.err_method))

        for res in self.result:
            res.covar_axis = self.covar_axis
            res.covariance = self.covariance
            res.model.parameters.set_parameter_covariance(self.covariance, self.covar_axis)

    def _est_errors_sherpa(self):
        """Wrapper around Sherpa error estimator."""
        covar = self._sherpa_fit.est_errors()
        self.covar_axis = [par.split('.')[-1] for par in covar.parnames]
        self.covariance = copy.deepcopy(covar.extra_output)

    def run(self, outdir=None):
        """Run all steps and write result to disk.

        Parameters
        ----------
        outdir : Path, str
            directory to write results files to (if given)
        """
        log.info('Running {}'.format(self))

        self.fit()
        self.est_errors()

        if outdir is not None:
            self._write_result(outdir)

    def _write_result(self, outdir):
        outdir = make_path(outdir)
        outdir.mkdir(exist_ok=True, parents=True)

        # Assume only one model is fit to all data
        modelname = self.result[0].model.__class__.__name__
        filename = outdir / 'fit_result_{}.yaml'.format(modelname)
        log.info('Writing {}'.format(filename))
        self.result[0].to_yaml(filename)
Exemplo n.º 25
0
    estmethod=Covariance(),
)
fit_results = fit.fit()
print(fit_results.format())
fit = Fit(
    data=cube,
    model=model,
    stat=Cash(),
    method=NelderMead(),
    estmethod=Covariance(),
)
fit_results = fit.fit()
fit_results
fit_results.format()
print(fit_results.format())
err_est_results = fit.est_errors()
print(err_est_results.format())
exclusion_region = CircleSkyRegion(
    center=SkyCoord(83.60, 21.88, unit='deg'),
    radius=Angle(0.1, "deg"),
)

sky_mask_cube = counts_3d.region_mask(exclusion_region)
sky_mask_cube.data = sky_mask_cube.data.astype(int)
index_region_selected_3d = np.where(sky_mask_cube.data.value == 1)
counts_3d = counts
exclusion_region = CircleSkyRegion(
    center=SkyCoord(83.60, 21.88, unit='deg'),
    radius=Angle(0.1, "deg"),
)
Exemplo n.º 26
0
  File "example.py", line 125, in <module>
    residplot.prepare(d, mdl, f.stat)
  File "/home/djburke/miniconda2/envs/sherpa410-py35/lib/python3.5/site-packages/sherpa-4.10.0-py3.5-linux-x86_64.egg/sherpa/plot/__init__.py", line 1140, in prepare
    self.yerr = staterr / staterr
TypeError: unsupported operand type(s) for /: 'list' and 'list'
"""

d.ignore(None, 1)

statinfo = f.calc_stat_info()
report("statinfo")

dump("statinfo.rstat == fitres3.rstat")
dump("f.estmethod.name")

coverrs = f.est_errors()
report("coverrs.format()")

dump("f.estmethod.sigma")

f.estmethod.sigma = 1.6
coverrs90 = f.est_errors()
report("coverrs90.format()")

from sherpa.estmethods import Confidence
f.estmethod = Confidence()
print("*** start confidence errors")
conferrs = f.est_errors()
print("*** end   confidence errors")

report("conferrs.format()")