예제 #1
0
def test_cash(test_data, reference_values):
    statsvec = stats.cash(n_on=test_data["n_on"], mu_on=test_data["mu_sig"])
    assert_allclose(statsvec, reference_values["cash"])
예제 #2
0
def test_cash_sum_cython(test_data):
    counts = np.array(test_data["n_on"], dtype=float)
    npred = np.array(test_data["mu_sig"], dtype=float)
    stat = stats.cash_sum_cython(counts=counts, npred=npred)
    ref = stats.cash(counts, npred).sum()
    assert_allclose(stat, ref)
예제 #3
0
    def run(self, dataset, steps="all"):
        """
        Run TS map estimation.

        Requires a MapDataset with counts, exposure and background_model
        properly set to run.

        Parameters
        ----------
        dataset : `~gammapy.datasets.MapDataset`
            Input MapDataset.
        steps : list of str or 'all'
            Which maps to compute. Available options are:

                * "ts": estimate delta TS and significance (sqrt_ts)
                * "flux-err": estimate symmetric error on flux.
                * "flux-ul": estimate upper limits on flux.

            By default all steps are executed.

        Returns
        -------
        maps : dict
             Dictionary containing result maps. Keys are:

                * ts : delta TS map
                * sqrt_ts : sqrt(delta TS), or significance map
                * flux : flux map
                * flux_err : symmetric error map
                * flux_ul : upper limit map

        """
        p = self.parameters

        # First create 2D map arrays
        counts = dataset.counts.sum_over_axes(keepdims=False)
        background = dataset.npred().sum_over_axes(keepdims=False)
        exposure = dataset.exposure.sum_over_axes(keepdims=False)

        kernel = self.get_kernel(dataset)

        if dataset.mask is not None:
            mask = counts.copy(
                data=(dataset.mask.sum(axis=0) > 0).astype("int"))
        else:
            mask = counts.copy(data=np.ones_like(counts).astype("int"))

        if self.downsampling_factor:
            shape = counts.data.shape
            pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0]

            counts = counts.pad(pad_width).downsample(self.downsampling_factor,
                                                      preserve_counts=True)
            background = background.pad(pad_width).downsample(
                self.downsampling_factor, preserve_counts=True)
            exposure = exposure.pad(pad_width).downsample(
                self.downsampling_factor, preserve_counts=False)
            mask = mask.pad(pad_width).downsample(self.downsampling_factor,
                                                  preserve_counts=False)
            mask.data = mask.data.astype("int")

        mask.data &= self.mask_default(exposure, background, kernel.data).data

        if steps == "all":
            steps = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"]

        result = {}
        for name in steps:
            data = np.nan * np.ones_like(counts.data)
            result[name] = counts.copy(data=data)

        flux_map = self.flux_default(dataset, kernel.data)

        if p["threshold"] or p["method"] == "root newton":
            flux = flux_map.data
        else:
            flux = None

        # prepare dtype for cython methods
        counts_array = counts.data.astype(float)
        background_array = background.data.astype(float)
        exposure_array = exposure.data.astype(float)

        # Compute null statistics per pixel for the whole image
        c_0 = cash(counts_array, background_array)

        error_method = p["error_method"] if "flux_err" in steps else "none"
        ul_method = p["ul_method"] if "flux_ul" in steps else "none"

        wrap = functools.partial(
            _ts_value,
            counts=counts_array,
            exposure=exposure_array,
            background=background_array,
            c_0=c_0,
            kernel=kernel.data,
            flux=flux,
            method=p["method"],
            error_method=error_method,
            threshold=p["threshold"],
            error_sigma=p["error_sigma"],
            ul_method=ul_method,
            ul_sigma=p["ul_sigma"],
            rtol=p["rtol"],
        )

        x, y = np.where(np.squeeze(mask.data))
        positions = list(zip(x, y))
        results = list(map(wrap, positions))

        # Set TS values at given positions
        j, i = zip(*positions)
        for name in ["ts", "flux", "niter"]:
            result[name].data[j, i] = [_[name] for _ in results]

        if "flux_err" in steps:
            result["flux_err"].data[j, i] = [_["flux_err"] for _ in results]

        if "flux_ul" in steps:
            result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results]

        # Compute sqrt(TS) values
        if "sqrt_ts" in steps:
            result["sqrt_ts"] = self.sqrt_ts(result["ts"])

        if self.downsampling_factor:
            for name in steps:
                order = 0 if name == "niter" else 1
                result[name] = result[name].upsample(
                    factor=self.downsampling_factor,
                    preserve_counts=False,
                    order=order)
                result[name] = result[name].crop(crop_width=pad_width)

        # Set correct units
        if "flux" in steps:
            result["flux"].unit = flux_map.unit
        if "flux_err" in steps:
            result["flux_err"].unit = flux_map.unit
        if "flux_ul" in steps:
            result["flux_ul"].unit = flux_map.unit

        return result
예제 #4
0
 def stat_array(self):
     """Likelihood per bin given the current model parameters"""
     return cash(n_on=self.counts.data, mu_on=self.npred().data)
예제 #5
0
def test_cash_bad_truncation():
    with pytest.raises(ValueError):
        stats.cash(10, 10, 0.0)
예제 #6
0
    def run(self, dataset, kernel, which="all", downsampling_factor=None):
        """
        Run TS map estimation.

        Requires a MapDataset with counts, exposure and background_model
        properly set to run.

        Parameters
        ----------
        kernel : `astropy.convolution.Kernel2D` or 2D `~numpy.ndarray`
            Source model kernel.
        which : list of str or 'all'
            Which maps to compute.
        downsampling_factor : int
            Sample down the input maps to speed up the computation. Only integer
            values that are a multiple of 2 are allowed. Note that the kernel is
            not sampled down, but must be provided with the downsampled bin size.

        Returns
        -------
        maps : dict
            Result maps.
        """
        p = self.parameters

        if (np.array(kernel.shape) > np.array(dataset.counts.data.shape[1:])).any():
            raise ValueError(
                "Kernel shape larger than map shape, please adjust"
                " size of the kernel"
            )

        # First create 2D map arrays
        counts = dataset.counts.sum_over_axes(keepdims=False)
        background = dataset.npred().sum_over_axes(keepdims=False)
        exposure = dataset.exposure.sum_over_axes(keepdims=False)
        if dataset.mask is not None:
            mask = counts.copy(data=(dataset.mask.sum(axis=0) > 0).astype("int"))
        else:
            mask = counts.copy(data=np.ones_like(counts).astype("int"))

        if downsampling_factor:
            shape = counts.data.shape
            pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0]

            counts = counts.pad(pad_width).downsample(
                downsampling_factor, preserve_counts=True
            )
            background = background.pad(pad_width).downsample(
                downsampling_factor, preserve_counts=True
            )
            exposure = exposure.pad(pad_width).downsample(
                downsampling_factor, preserve_counts=False
            )
            mask = mask.pad(pad_width).downsample(
                downsampling_factor, preserve_counts=False
            )
            mask.data = mask.data.astype("int")

        mask.data &= self.mask_default(exposure, background, kernel).data

        if not isinstance(kernel, Kernel2D):
            kernel = CustomKernel(kernel)

        if which == "all":
            which = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"]

        result = {}
        for name in which:
            data = np.nan * np.ones_like(counts.data)
            result[name] = counts.copy(data=data)

        flux_map = self.flux_default(dataset, kernel)

        if p["threshold"] or p["method"] == "root newton":
            flux = flux_map.data
        else:
            flux = None

        # prepare dtype for cython methods
        counts_array = counts.data.astype(float)
        background_array = background.data.astype(float)
        exposure_array = exposure.data.astype(float)

        # Compute null statistics per pixel for the whole image
        c_0 = cash(counts_array, background_array)

        error_method = p["error_method"] if "flux_err" in which else "none"
        ul_method = p["ul_method"] if "flux_ul" in which else "none"

        wrap = functools.partial(
            _ts_value,
            counts=counts_array,
            exposure=exposure_array,
            background=background_array,
            c_0=c_0,
            kernel=kernel,
            flux=flux,
            method=p["method"],
            error_method=error_method,
            threshold=p["threshold"],
            error_sigma=p["error_sigma"],
            ul_method=ul_method,
            ul_sigma=p["ul_sigma"],
            rtol=p["rtol"],
        )

        x, y = np.where(np.squeeze(mask.data))
        positions = list(zip(x, y))
        results = list(map(wrap, positions))

        # Set TS values at given positions
        j, i = zip(*positions)
        for name in ["ts", "flux", "niter"]:
            result[name].data[j, i] = [_[name] for _ in results]

        if "flux_err" in which:
            result["flux_err"].data[j, i] = [_["flux_err"] for _ in results]

        if "flux_ul" in which:
            result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results]

        # Compute sqrt(TS) values
        if "sqrt_ts" in which:
            result["sqrt_ts"] = self.sqrt_ts(result["ts"])

        if downsampling_factor:
            for name in which:
                order = 0 if name == "niter" else 1
                result[name] = result[name].upsample(
                    factor=downsampling_factor, preserve_counts=False, order=order
                )
                result[name] = result[name].crop(crop_width=pad_width)

        # Set correct units
        if "flux" in which:
            result["flux"].unit = flux_map.unit
        if "flux_err" in which:
            result["flux_err"].unit = flux_map.unit
        if "flux_ul" in which:
            result["flux_ul"].unit = flux_map.unit

        return result
예제 #7
0
 def _stat_fcn(self, mu, delta=0, index=None):
     return cash(self.n_on[index], self.mu_bkg[index] + mu) - delta
예제 #8
0
 def TS_max(self):
     """Stat value for best fit hypothesis, i.e. expected signal mu = n_on - mu_bkg"""
     return cash(self.n_on, self.n_on)
예제 #9
0
 def TS_null(self):
     """Stat value for null hypothesis, i.e. 0 expected signal counts"""
     return cash(self.n_on, self.mu_bkg + 0)
예제 #10
0
    def run(self, maps, kernel, which="all", downsampling_factor=None):
        """
        Run TS map estimation.

        Requires "counts", "exposure" and "background" map to run.

        Parameters
        ----------
        maps : dict
            Input sky maps.
        kernel : `astropy.convolution.Kernel2D` or 2D `~numpy.ndarray`
            Source model kernel.
        which : list of str or 'all'
            Which maps to compute.
        downsampling_factor : int
            Sample down the input maps to speed up the computation. Only integer
            values that are a multiple of 2 are allowed. Note that the kernel is
            not sampled down, but must be provided with the downsampled bin size.

        Returns
        -------
        maps : dict
            Result maps.
        """
        p = self.parameters

        if (np.array(kernel.shape) > np.array(maps["counts"].data.shape)).any():
            raise ValueError(
                "Kernel shape larger than map shape, please adjust"
                " size of the kernel"
            )

        if downsampling_factor:
            maps_downsampled = {}

            shape = maps["counts"].data.shape
            pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0]

            for name, map_ in maps.items():
                preserve_counts = name in ["counts", "background", "exclusion"]
                maps_downsampled[name] = map_.pad(pad_width).downsample(
                    downsampling_factor, preserve_counts=preserve_counts
                )
            maps = maps_downsampled

        if not isinstance(kernel, Kernel2D):
            kernel = CustomKernel(kernel)

        if which == "all":
            which = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"]

        result = {}
        for name in which:
            data = np.nan * np.ones_like(maps["counts"].data)
            result[name] = maps["counts"].copy(data=data)

        mask = self.mask_default(maps, kernel)

        if "mask" in maps:
            mask.data &= maps["mask"].data

        if p["threshold"] or p["method"] == "root newton":
            flux = self.flux_default(maps, kernel).data
        else:
            flux = None

        # prepare dtype for cython methods
        counts = maps["counts"].data.astype(float)
        background = maps["background"].data.astype(float)
        exposure = maps["exposure"].data.astype(float)

        # Compute null statistics per pixel for the whole image
        c_0 = cash(counts, background)

        error_method = p["error_method"] if "flux_err" in which else "none"
        ul_method = p["ul_method"] if "flux_ul" in which else "none"

        wrap = functools.partial(
            _ts_value,
            counts=counts,
            exposure=exposure,
            background=background,
            c_0=c_0,
            kernel=kernel,
            flux=flux,
            method=p["method"],
            error_method=error_method,
            threshold=p["threshold"],
            error_sigma=p["error_sigma"],
            ul_method=ul_method,
            ul_sigma=p["ul_sigma"],
            rtol=p["rtol"],
        )

        x, y = np.where(mask.data)
        positions = list(zip(x, y))

        with contextlib.closing(multiprocessing.Pool(processes=p["n_jobs"])) as pool:
            log.info("Using {} jobs to compute TS map.".format(p["n_jobs"]))
            results = pool.map(wrap, positions)

        pool.join()

        # Set TS values at given positions
        j, i = zip(*positions)
        for name in ["ts", "flux", "niter"]:
            result[name].data[j, i] = [_[name] for _ in results]

        if "flux_err" in which:
            result["flux_err"].data[j, i] = [_["flux_err"] for _ in results]

        if "flux_ul" in which:
            result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results]

        # Compute sqrt(TS) values
        if "sqrt_ts" in which:
            result["sqrt_ts"] = self.sqrt_ts(result["ts"])

        if downsampling_factor:
            for name in which:
                order = 0 if name == "niter" else 1
                result[name] = result[name].upsample(
                    factor=downsampling_factor, preserve_counts=False, order=order
                )
                result[name] = result[name].crop(crop_width=pad_width)

        return result
예제 #11
0
 def _excess_matching_significance_fcn(self, excess, significance, index):
     TS0 = cash(excess + self.background[index], self.mu_bkg[index])
     TS1 = cash(excess + self.background[index],
                self.mu_bkg[index] + excess)
     return np.sign(excess) * np.sqrt(np.clip(TS0 - TS1, 0,
                                              None)) - significance