def checkInstallation(self, compile=True): """ Checks if installation of tool is correct by looking for executable and executing it. If check is False and compile is True, then try and compile it. :returns: True, if everything is ok """ if not os.path.exists(self.executablePath): if compile: logger.warn( "%s executable not found. Trying to compile it now. This may take a while." % self.name) self.compile() else: logger.warn("%s exectuable not found." % self.name) self.complain() return False if not os.path.exists(self.executablePath): logger.error( "Compilation of %s failed Is a according compiler installed?" % self.name) self.complain() if not os.access(self.executablePath, os.X_OK): logger.warning("%s is not executable Trying to chmod" % self.executable) self.chmod() return True
def writeToFile(self, args): toFile = None if args.tofile: toFile = "highest" if args.alltofile: if toFile == "highest": logger.warn ( "Specified both --tofile and --alltofile. Will use "\ "--alltofile" ) toFile = "all" return toFile
def unlink(self, unlinkdir=True): """ Remove temporary files. :param unlinkdir: remove temp directory completely """ if self.tempdir == None: return if self.keepTempDir: logger.warn("Keeping everything in " + self.tempdir) return logger.debug( "Unlinking " + self.tempdir ) for inputFile in ["fort.61", "fort.68", "log"]: if os.path.exists(self.tempdir + "/" + inputFile): os.unlink(self.tempdir + "/" + inputFile) if unlinkdir: for inputFile in ["temp.cfg"]: os.unlink(self.tempdir + "/" + inputFile) if os.path.exists(self.tempdir): os.rmdir(self.tempdir) self.tempdir = None
def checkInstallation(self, compile=True ): """ Checks if installation of tool is correct by looking for executable and executing it. If check is False and compile is True, then try and compile it. :returns: True, if everything is ok """ if not os.path.exists(self.executablePath): if compile: logger.warn("%s executable not found. Trying to compile it now. This may take a while." % self.name ) self.compile() else: logger.warn("%s exectuable not found." % self.name ) self.complain() return False if not os.path.exists(self.executablePath): logger.error("Compilation of %s failed Is a according compiler installed?" % self.name ) self.complain() if not os.access(self.executablePath, os.X_OK): logger.warning("%s is not executable Trying to chmod" % self.executable) self.chmod() return True
def likelihoodFromLimits(upperLimit, expectedUpperLimit, nsig, nll=False): """ computes the likelihood from an expected and an observed upper limit. :param upperLimit: observed upper limit, as a yield (i.e. unitless) :param expectedUpperLimit: expected upper limit, also as a yield :param nSig: number of signal events :param nll: if True, return negative log likelihood :returns: likelihood (float) """ assert (upperLimit > 0.) def getSigma(ul, muhat=0.): """ get the standard deviation sigma, given an upper limit and a central value. assumes a truncated Gaussian likelihood """ # the expected scale, eq 3.24 in arXiv:1202.3415 return (ul - muhat) / 1.96 def llhd(nsig, mumax, sigma_exp, nll): ## need to account for the truncation! ## first compute how many sigmas left of center is 0. Zprime = mumax / sigma_exp ## now compute the area of the truncated gaussian A = stats.norm.cdf(Zprime) if nll: return np.log(A) - stats.norm.logpdf(nsig, mumax, sigma_exp) return float(stats.norm.pdf(nsig, mumax, sigma_exp) / A) dr = 2. * (upperLimit - expectedUpperLimit) / (expectedUpperLimit + upperLimit) if dr > runtime._drmax: if runtime._cap_likelihoods == False: logger.warn( "asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f)" % (upperLimit, expectedUpperLimit, dr, runtime._drmax)) return None oldUL = upperLimit upperLimit = expectedUpperLimit * (2. + runtime._drmax) / ( 2. - runtime._drmax) logger.warn("asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f). capping to %.2f." % \ ( oldUL, expectedUpperLimit, dr, runtime._drmax, upperLimit ) ) ## we are asked to cap likelihoods, so we set observed UL such that dr == drmax # sigma_exp = expectedUpperLimit / 1.96 # the expected scale, eq 3.24 in arXiv:1202.3415 sigma_exp = getSigma( expectedUpperLimit) # the expected scale, eq 3.24 in arXiv:1202.3415 if upperLimit < expectedUpperLimit: ## underfluctuation. mumax = 0. return llhd(nsig, 0., sigma_exp, nll) def root_func(x): ## we want the root of this one return (erf((upperLimit - x) / denominator) + erf(x / denominator)) / (1. + erf(x / denominator)) - .95 denominator = np.sqrt(2.) * sigma_exp fA = root_func(0.) fB = root_func(max(upperLimit, expectedUpperLimit)) if np.sign(fA * fB) > 0.: ## the have the same sign logger.error("when computing likelihood: fA and fB have same sign") return None mumax = optimize.brentq(root_func, 0., max(upperLimit, expectedUpperLimit), rtol=1e-03, xtol=1e-06) llhdexp = llhd(nsig, mumax, sigma_exp, nll) return llhdexp
def llhdFromLimits_moments(upperLimit, expectedUpperLimit, nll=False, underfluct="norm_0"): """Compute the Expected Value, Variance, Skewness and Mode of normalized Likelihood that was computed with the Expected and observed Upper Limit. :param upperLimit: observed upper limit, as a yield (i.e. unitless) :param expectedUpperLimit: expected upper limit, also as a yield :param nSig: number of signal events :param nll: if True, return negative log likelihood :param underfluct: How to handle Unfderfluctuations, "norm_0" uses a gaussian with maximum at 0, "norm_neg" uses a gaussian with negative maximum, "exp" uses an exponential distribution, defaults to "norm_0" :returns: EV, Var, Skew and Mode of computed Likelihood as dict """ assert (upperLimit > 0.) def getSigma(ul, muhat=0.): """ get the standard deviation sigma, given an upper limit and a central value. assumes a truncated Gaussian likelihood """ # the expected scale, eq 3.24 in arXiv:1202.3415 return (ul - muhat) / 1.96 sigma_exp = getSigma( expectedUpperLimit) # the expected scale, eq 3.24 in arXiv:1202.3415 denominator = np.sqrt(2.) * sigma_exp def root_func(x): ## we want the root of this one return (erf((upperLimit - x) / denominator) + erf(x / denominator)) / (1. + erf(x / denominator)) - .95 def find_neg_mumax(upperLimit, expectedUpperLimit, xa, xb): while root_func(xa) * root_func(xb) > 0: xa = 2 * xa mumax = optimize.brentq(root_func, xa, xb, rtol=1e-03, xtol=1e-06) return mumax def getLam(ul): """ get the scale for the exponential destribution that reproduces the upper limit""" return -np.log(0.05) / ul def llhdexponential(nsig, lam, nll): ## exponential distribution if nll: return float(lam * nsig - np.log(lam)) return float(stats.expon.pdf(nsig, scale=1 / lam)) def trunc_norm_moments(mumax, sigma): rho = np.exp( -mumax**2 / (2 * sigma**2)) / (np.sqrt(2 * np.pi) * (1 - stats.norm.cdf(0, loc=mumax, scale=sigma))) h = -mumax / sigma ev = mumax + (sigma * rho) var = sigma**2 * (1 + rho * h - rho**2) #skew = sigma**3 * rho* ( (rho-h)**2 + rho*(rho-h) - 1 ) skew = rho * (2 * rho**2 - 3 * rho * h + h**2 - 1) / (1 + h * rho - rho**2)**(3 / 2) #skew = sigma*rho*(ev**2 - var) if mumax < 0: mod = 0. else: mod = mumax return ({"ev": ev, "var": var, "skew": skew, "mode": mod}) def exp_moments(lam): ev = 1 / lam var = 1 / (lam)**2 skew = 2 mod = 0. return ({"ev": ev, "var": var, "skew": skew, "mode": mod}) dr = 2. * (upperLimit - expectedUpperLimit) / (expectedUpperLimit + upperLimit) if dr > runtime._drmax: if runtime._cap_likelihoods == False: logger.warn( "asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f)" % (upperLimit, expectedUpperLimit, dr, runtime._drmax)) return None oldUL = upperLimit upperLimit = expectedUpperLimit * (2. + runtime._drmax) / ( 2. - runtime._drmax) logger.warn("asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f). capping to %.2f." % \ ( oldUL, expectedUpperLimit, dr, runtime._drmax, upperLimit ) ) ## we are asked to cap likelihoods, so we set observed UL such that dr == drmax if upperLimit <= expectedUpperLimit: ## underfluctuation. if underfluct == "norm_0": mumax = 0 return trunc_norm_moments(mumax, sigma_exp) elif underfluct == "norm_neg": xa = -expectedUpperLimit xb = 1 mumax = find_neg_mumax(upperLimit, expectedUpperLimit, xa, xb) return trunc_norm_moments(mumax, sigma_exp) elif underfluct == "exp": lam = getLam(upperLimit) return exp_moments(lam) else: logger.warn( "underfluct not defined, choose one of norm_0, norm_neg and exp" ) fA = root_func(0.) fB = root_func(max(upperLimit, expectedUpperLimit)) if np.sign(fA * fB) > 0.: ## the have the same sign logger.error("when computing likelihood: fA and fB have same sign") return None mumax = optimize.brentq(root_func, 0., max(upperLimit, expectedUpperLimit), rtol=1e-03, xtol=1e-06) return trunc_norm_moments(mumax, sigma_exp)
def likelihoodFromLimits(upperLimit, expectedUpperLimit, nsig, nll=False, underfluct="norm_0"): """ computes the likelihood from an expected and an observed upper limit. :param upperLimit: observed upper limit, as a yield (i.e. unitless) :param expectedUpperLimit: expected upper limit, also as a yield :param nSig: number of signal events :param nll: if True, return negative log likelihood :param underfluct: How to handle Unfderfluctuations, "norm_0" uses a gaussian with maximum at 0, "norm_neg" uses a gaussian with negative maximum, "exp" uses an exponential distribution, defaults to "norm_0" :returns: likelihood (float) """ assert (upperLimit > 0.) def getSigma(ul, muhat=0.): """ get the standard deviation sigma, given an upper limit and a central value. assumes a truncated Gaussian likelihood """ # the expected scale, eq 3.24 in arXiv:1202.3415 return (ul - muhat) / 1.96 def llhd(nsig, mumax, sigma_exp, nll): ## need to account for the truncation! ## first compute how many sigmas left of center is 0. Zprime = mumax / sigma_exp ## now compute the area of the truncated gaussian A = stats.norm.cdf(Zprime) if nll: return np.log(A) - stats.norm.logpdf(nsig, mumax, sigma_exp) return float(stats.norm.pdf(nsig, mumax, sigma_exp) / A) # sigma_exp = expectedUpperLimit / 1.96 # the expected scale, eq 3.24 in arXiv:1202.3415 sigma_exp = getSigma( expectedUpperLimit) # the expected scale, eq 3.24 in arXiv:1202.3415 denominator = np.sqrt(2.) * sigma_exp def root_func(x): ## we want the root of this one return (erf((upperLimit - x) / denominator) + erf(x / denominator)) / (1. + erf(x / denominator)) - .95 def find_neg_mumax(upperLimit, expectedUpperLimit, xa, xb): while root_func(xa) * root_func(xb) > 0: xa = 2 * xa #logger.error (xa, root_func(xa), xb, root_func(xb)) mumax = optimize.brentq(root_func, xa, xb, rtol=1e-03, xtol=1e-06) return mumax def getLam(ul): """ get the scale for the exponential destribution that reproduces the upper limit""" return -np.log(0.05) / ul def llhdexponential(nsig, lam, nll): ## exponential distribution if nll: return float(lam * nsig - np.log(lam)) return float(stats.expon.pdf(nsig, scale=1 / lam)) dr = 2. * (upperLimit - expectedUpperLimit) / (expectedUpperLimit + upperLimit) if dr > runtime._drmax: if runtime._cap_likelihoods == False: logger.warn( "asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f)" % (upperLimit, expectedUpperLimit, dr, runtime._drmax)) return None oldUL = upperLimit upperLimit = expectedUpperLimit * (2. + runtime._drmax) / ( 2. - runtime._drmax) logger.warn("asking for likelihood from limit but difference between oUL(%.2f) and eUL(%.2f) is too large (dr=%.2f>%.2f). capping to %.2f." % \ ( oldUL, expectedUpperLimit, dr, runtime._drmax, upperLimit ) ) ## we are asked to cap likelihoods, so we set observed UL such that dr == drmax if upperLimit <= expectedUpperLimit: ## underfluctuation. if underfluct == "norm_0": return llhd(nsig, 0., sigma_exp, nll) elif underfluct == "norm_neg": xa = -expectedUpperLimit xb = 1 mumax = find_neg_mumax(upperLimit, expectedUpperLimit, xa, xb) return llhd(nsig, mumax, sigma_exp, nll) elif underfluct == "exp": lam = getLam(upperLimit) return llhdexponential(nsig, lam, nll) else: logger.warn( "underfluct not defined, choose one of norm_0, norm_neg and exp" ) fA = root_func(0.) fB = root_func(max(upperLimit, expectedUpperLimit)) if np.sign(fA * fB) > 0.: ## the have the same sign logger.error("when computing likelihood: fA and fB have same sign") return None mumax = optimize.brentq(root_func, 0., max(upperLimit, expectedUpperLimit), rtol=1e-03, xtol=1e-06) llhdexp = llhd(nsig, mumax, sigma_exp, nll) return llhdexp
def ulSigma (self, expected=False, workspace_index=None): """ Compute the upper limit on the signal strength modifier with: - by default, the combination of the workspaces contained into self.workspaces - if workspace_index is specified, self.workspace[workspace_index] (useful for computation of the best upper limit) :param expected: - if set to `True`: uses expected SM backgrounds as signals - else: uses `self.nsignals` :param workspace_index: - if different from `None`: index of the workspace to use for upper limit - else: all workspaces are combined :return: the upper limit at `self.cl` level (0.95 by default) """ startUL = time.time() logger.debug("Calling ulSigma") if self.data.errorFlag or self.workspaces == None: # For now, this flag can only be turned on by PyhfData.checkConsistency return None if self.nWS == 1: if self.zeroSignalsFlag[0] == True: logger.warning("There is only one workspace but all signals are zeroes") return None else: if workspace_index == None: logger.error("There are several workspaces but no workspace index was provided") return None elif self.zeroSignalsFlag[workspace_index] == True: logger.debug("Workspace number %d has zero signals" % workspace_index) return None def updateWorkspace(): if self.nWS == 1: return self.workspaces[0] else: return self.workspaces[workspace_index] workspace = updateWorkspace() def root_func(mu): # Same modifiers_settings as those use when running the 'pyhf cls' command line msettings = {'normsys': {'interpcode': 'code4'}, 'histosys': {'interpcode': 'code4p'}} model = workspace.model(modifier_settings=msettings) start = time.time() stat = "qtilde" # by default args = { "return_expected": expected } pver = float ( pyhf.__version__[:3] ) if pver < 0.6: args["qtilde"]=True else: args["test_stat"]=stat with np.testing.suppress_warnings() as sup: if pyhfinfo["backend"] == "numpy": sup.filter ( RuntimeWarning, r'invalid value encountered in log') result = pyhf.infer.hypotest(mu, workspace.data(model), model, **args ) end = time.time() logger.debug("Hypotest elapsed time : %1.4f secs" % (end - start)) if expected: logger.debug("expected = {}, mu = {}, result = {}".format(expected, mu, result)) try: CLs = float(result[1].tolist()) except TypeError: CLs = float(result[1][0]) else: logger.debug("expected = {}, mu = {}, result = {}".format(expected, mu, result)) CLs = float(result) # logger.debug("Call of root_func(%f) -> %f" % (mu, 1.0 - CLs)) return 1.0 - self.cl - CLs # Rescaling singals so that mu is in [0, 10] factor = 10. wereBothLarge = False wereBothTiny = False nattempts = 0 nNan = 0 lo_mu, hi_mu = .2, 5. while "mu is not in [lo_mu,hi_mu]": nattempts += 1 if nNan > 5: logger.warn("encountered NaN 5 times while trying to determine the bounds for brent bracketing. now trying with q instead of qtilde test statistic") stat = "q" nattempts = 0 if nattempts > 10: logger.warn ( "tried 10 times to determine the bounds for brent bracketing. we abort now." ) return None # Computing CL(1) - 0.95 and CL(10) - 0.95 once and for all rt1 = root_func(lo_mu) rt10 = root_func(hi_mu) if rt1 < 0. and 0. < rt10: # Here's the real while condition break if self.alreadyBeenThere: factor = 1 + (factor-1)/2 logger.debug("Diminishing rescaling factor") if np.isnan(rt1): nNan += 1 self.rescale(factor) workspace = updateWorkspace() continue if np.isnan(rt10): nNan += 1 self.rescale(1/factor) workspace = updateWorkspace() continue # Analyzing previous values of wereBoth*** if rt10 < 0 and rt1 < 0 and wereBothLarge: factor = 1 + (factor-1)/2 logger.debug("Diminishing rescaling factor") if rt10 > 0 and rt1 > 0 and wereBothTiny: factor = 1 + (factor-1)/2 logger.debug("Diminishing rescaling factor") # Preparing next values of wereBoth*** wereBothTiny = rt10 < 0 and rt1 < 0 wereBothLarge = rt10 > 0 and rt1 > 0 # Main rescaling code if rt10 < 0.: self.rescale(factor) workspace = updateWorkspace() continue if rt1 > 0.: self.rescale(1/factor) workspace = updateWorkspace() continue # Finding the root (Brent bracketing part) logger.debug("Final scale : %f" % self.scale) logger.debug("Starting brent bracketing") ul = optimize.brentq(root_func, lo_mu, hi_mu, rtol=1e-3, xtol=1e-3) endUL = time.time() logger.debug("ulSigma elpased time : %1.4f secs" % (endUL - startUL)) return ul*self.scale # self.scale has been updated whithin self.rescale() method