def test_invalid_method(self): with pytest.raises(ValueError, match='Unknown solver "xfail"'): find_roots( self.f, lower_bound=self.lower_bound, upper_bound=self.upper_bound, method="xfail", )
def containment_radius(self, containment_fraction): """Containment angle for a given containment fraction. Parameters ---------- containment_fraction : `~numpy.ndarray` Containment fraction Returns ------- containment_radius : `~numpy.ndarray` Containment radius """ rad_max = 1e3 def f(rad): # positive if theta too large return self.containment_fraction(rad * u.deg) - containment_fraction roots, res = find_roots(f, lower_bound=0, upper_bound=rad_max, nbin=1) if np.isnan(roots[0]) or np.allclose(roots[0], rad_max): rad = np.inf else: rad = roots[0] return rad * u.deg
def _confidence(self, dataset, n_sigma, result, positive): stat_best = result["stat"] norm = result["norm"] norm_err = result["norm_err"] def ts_diff(x): return (stat_best + n_sigma**2) - dataset.stat_sum(x) if positive: min_norm = norm max_norm = norm + 1e2 * norm_err factor = 1 else: min_norm = norm - 1e2 * norm_err max_norm = norm factor = -1 with warnings.catch_warnings(): warnings.simplefilter("ignore") roots, res = find_roots( ts_diff, [min_norm], [max_norm], nbin=1, maxiter=self.max_niter, rtol=self.rtol, ) # Where the root finding fails NaN is set as norm return (roots[0] - norm) * factor
def compute_upper_limit(self, n_sigma=3): """Compute upper limit on the signal. Searches the signal value for which the test statistics is n_sigma**2 away from the maximum or from 0 if the measured excess is negative. Parameters ---------- n_sigma : float Confidence level of the upper limit expressed in number of sigma. Default is 3. """ ul = np.zeros_like(self.n_on, dtype="float") min_range = self.n_sig max_range = self.n_sig + 2 * n_sigma * (self.error + 1) it = np.nditer(ul, flags=["multi_index"]) while not it.finished: ts_ref = self._stat_fcn(min_range[it.multi_index], 0.0, it.multi_index) roots, res = find_roots( self._stat_fcn, min_range[it.multi_index], max_range[it.multi_index], nbin=1, args=(ts_ref + n_sigma**2, it.multi_index), ) ul[it.multi_index] = roots[0] it.iternext() return ul
def compute_errp(self, n_sigma=1): """Compute upward excess uncertainties. Searches the signal value for which the test statistics is n_sigma**2 away from the maximum. Parameters ---------- n_sigma : float Confidence level of the uncertainty expressed in number of sigma. Default is 1. """ errp = np.zeros_like(self.n_on, dtype="float") max_range = self.n_sig + 2 * n_sigma * (self.error + 1) it = np.nditer(errp, flags=["multi_index"]) while not it.finished: roots, res = find_roots( self._stat_fcn, self.n_sig[it.multi_index], max_range[it.multi_index], nbin=1, args=(self.stat_max[it.multi_index] + n_sigma**2, it.multi_index), ) errp[it.multi_index] = roots[0] it.iternext() return errp - self.n_sig
def test_methods(self): methods = ["brentq", "secant"] for method in methods: roots, res = find_roots( self.f, lower_bound=self.lower_bound, upper_bound=self.upper_bound, method=method, ) assert roots.unit == u.rad assert_allclose(2 * roots.value / np.pi, np.array([-5.0, -3.0, -1.0])) assert np.all([sol.converged for sol in res]) roots, res = find_roots( self.h, lower_bound=self.lower_bound, upper_bound=self.upper_bound, method=method, ) assert np.isnan(roots[0]) assert res[0].iterations == 0
def _confidence_scipy_brentq(parameters, parameter, function, sigma, reoptimize, upper=True, **kwargs): ts_diff = TSDifference(function, parameters, parameter, reoptimize, ts_diff=sigma**2) lower_bound = parameter.factor upper_bound = parameter.factor_max if upper else parameter.factor_min if np.isnan(upper_bound): # TODO: remove hard coded limits here... upper_bound = parameter.factor if upper: upper_bound += 1e2 * parameter.error / parameter.scale else: upper_bound -= 1e2 * parameter.error / parameter.scale message, success = "Confidence terminated successfully.", True kwargs.setdefault("nbin", 1) roots, res = find_roots(ts_diff.fcn, lower_bound=lower_bound, upper_bound=upper_bound, **kwargs) result = (roots[0], res[0]) if np.isnan(roots[0]): message = ( "Confidence estimation failed. Try to set the parameter.min/max by hand." ) success = False suffix = "errp" if upper else "errn" return { "nfev_" + suffix: result[1].iterations, suffix: np.abs(result[0] - lower_bound), "success_" + suffix: success, "message_" + suffix: message, "stat_null": ts_diff.stat_null, }
def stat_profile_ul_scipy(value_scan, stat_scan, delta_ts=4, interp_scale="sqrt", **kwargs): """Compute upper limit of a parameter from a likelihood profile. Parameters ---------- value_scan : `~numpy.ndarray` Array of parameter values. stat_scan : `~numpy.ndarray` Array of delta fit statistic values, with respect to the minimum. delta_ts : float Difference in test statistics for the upper limit. interp_scale : {"sqrt", "lin"} Interpolation scale applied to the fit statistic profile. If the profile is of parabolic shape, a "sqrt" scaling is recommended. In other cases or for fine sampled profiles a "lin" can also be used. **kwargs : dict Keyword arguments passed to `~scipy.optimize.brentq`. Returns ------- ul : float Upper limit value. """ interp = interpolate_profile(value_scan, stat_scan, interp_scale=interp_scale) def f(x): return interp((x, )) - delta_ts idx = np.argmin(stat_scan) norm_best_fit = value_scan[idx] roots, res = find_roots(f, lower_bound=norm_best_fit, upper_bound=value_scan[-1], nbin=1, **kwargs) return roots[0]
def n_sig_matching_significance(self, significance): """Compute excess matching a given significance. This function is the inverse of `significance`. Parameters ---------- significance : float Significance Returns ------- n_sig : `numpy.ndarray` Excess """ n_sig = np.zeros_like(self.n_bkg, dtype="float") it = np.nditer(n_sig, flags=["multi_index"]) while not it.finished: lower_bound = np.sqrt(self.n_bkg[it.multi_index]) * significance # find upper bounds for secant method as in scipy eps = 1e-4 upper_bound = lower_bound * (1 + eps) upper_bound += eps if upper_bound >= 0 else -eps roots, res = find_roots( self._n_sig_matching_significance_fcn, lower_bound=lower_bound, upper_bound=upper_bound, args=(significance, it.multi_index), nbin=1, method="secant", ) n_sig[it.multi_index] = roots[0] # return NaN if fail it.iternext() return n_sig