diff --git a/gstlal-burst/python/string/string_lr_far.py b/gstlal-burst/python/string/string_lr_far.py index c4d7cb679c7465ec188aa99c7b431a5f99662cf3..9fd154f79f294b8d5c2b7284c457424388193a8a 100644 --- a/gstlal-burst/python/string/string_lr_far.py +++ b/gstlal-burst/python/string/string_lr_far.py @@ -15,18 +15,19 @@ import numpy import random +import sys from lal import rate from lalburst import snglcoinc from ligo.lw import ligolw +from ligo.lw import lsctables from ligo.lw import param as ligolw_param from ligo.lw import utils as ligolw_utils - +from gstlal import stats as gstlalstats from gstlal import string_extrinsics - # # ============================================================================= # @@ -42,22 +43,22 @@ from gstlal import string_extrinsics class LnLRDensity(snglcoinc.LnLRDensity): + # SNR threshold FIXME don't hardcode + snr_threshold = 3.75 # SNR, chi^2 binning definition snr2_chi2_binning = rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801))) - def __init__(self, instruments, snr_threshold, min_instruments=2): + def __init__(self, instruments, min_instruments=2): # check input if min_instruments < 2: raise ValueError("min_instruments=%d must be >=2" % min_instruments) if min_instruments > len(instruments): raise ValueError("not enough instruments (%s) to satisfy min_instruments=%d" % (", ".join(sorted(instruments)), min_instruments)) - if snr_threshold <= 0.: - raise ValueError("SNR threshold = %g must be >0" % snr_threshold) self.min_instruments = min_instruments - self.snr_threshold = snr_threshold self.densities = {} - for instrument in instruments: + self.instruments = frozenset(instruments) + for instrument in self.instruments: self.densities["%s_snr2_chi2" % instrument] = rate.BinnedLnPDF(self.snr2_chi2_binning) def __call__(self, **params): @@ -80,9 +81,8 @@ class LnLRDensity(snglcoinc.LnLRDensity): return self def increment(self, weight = 1.0, **params): - instruments = params["snrs"].items() - for instrument in instrumets: - self.densities["%s_snr2_chi2" % instrument].count[params["snrs"][instrument], params["chi2s_over_snr2s"][instrument]] += weight + for instrument in self.instruments: + self.densities["%s_snr2_chi2" % instrument].count[params['snrs'][instrument], params['chi2s_over_snr2s'][instrument]] += weight def copy(self): new = type(self)([]) @@ -108,7 +108,6 @@ class LnLRDensity(snglcoinc.LnLRDensity): instruments = set(key.split("_", 1)[0] for key in self.densities if key.endswith("_snr2_chi2")) xml.appendChild(ligolw_param.Param.from_pyvalue(u"instruments", lsctables.instrumentsproperty.set(instruments))) xml.appendChild(ligolw_param.Param.from_pyvalue(u"min_instruments", self.min_instruments)) - xml.appendChild(ligolw_param.Param.from_pyvalue(u"snr_threshold", self.snr_threshold)) for key, lnpdf in self.densities.items(): xml.appendChild(lnpdf.to_xml(key)) return xml @@ -119,7 +118,6 @@ class LnLRDensity(snglcoinc.LnLRDensity): self = cls( instruments = lsctables.instrumentsproperty.get(ligolw_param.get_pyvalue(xml, u"instruments")), min_instruments = ligolw_param.get_pyvalue(xml, u"min_instruments"), - snr_threshold = ligolw_param.get_pyvalue(xml, u"snr_threshold") ) for key in self.densities: self.densities[key] = rate.BinnedLnPDF.from_xml(xml, key) @@ -132,11 +130,20 @@ class LnLRDensity(snglcoinc.LnLRDensity): class LnSignalDensity(LnLRDensity): + def __call__(self, snrs, chi2s_over_snr2s): + if len(snrs) < self.min_instruments: + return NegInf + lnP = 0.0 + # evalute the (snr, \chi^2 | snr) PDFs + interps = self.interps + return lnP + sum(interps["%s_snr2_chi2" % instrument](snrs[instrument], chi2_over_snr2) for instrument, chi2_over_snr2 in chi2s_over_snr2s.items()) + def add_signal_model(self, prefactors_range = (0.001, 0.30), inv_snr_pow = 4.): # normalize to 10 *mi*llion signals. This count makes the # density estimation code choose a suitable kernel size - string_extrinsics.NumeratorSNRCHIPDF.add_signal_model(self.densities["snr2_chi2"], 10000000., prefactors_range, inv_snr_pow = inv_snr_pow, snr_min = self.snr_threshold) - self.densities["snr2_chi2"].normalize() + for instrument in self.instruments: + string_extrinsics.NumeratorSNRCHIPDF.add_signal_model(self.densities["%s_snr2_chi2" % instrument], 10000000., prefactors_range, inv_snr_pow = inv_snr_pow, snr_min = self.snr_threshold) + self.densities["%s_snr2_chi2" % instrument].normalize() def to_xml(self, name): xml = super(LnSignalDensity, self).to_xml(name) @@ -206,15 +213,12 @@ class LnNoiseDensity(LnLRDensity): def to_xml(self, name): xml = super(LnNoiseDensity, self).to_xml(name) - xml.appendChild(self.triggerrates.to_xml(u"triggerrates")) return xml @classmethod def from_xml(cls, xml, name): xml = cls.get_xml_root(xml, name) self = super(LnNoiseDensity, cls).from_xml(xml, name) - self.triggerrates = trigger_rate.triggerrates.from_xml(xml, u"triggerrates") - self.triggerrates.coalesce() # just in case return self diff --git a/gstlal-burst/python/string/stringutils.py b/gstlal-burst/python/string/stringutils.py index a74f4117f64871a40d41fc17153d03a1c1b57306..8b6e577f68de2b318174e0c3324c885d5dbf5aeb 100644 --- a/gstlal-burst/python/string/stringutils.py +++ b/gstlal-burst/python/string/stringutils.py @@ -188,24 +188,20 @@ class StringCoincParamsDistributions(snglcoinc.LnLikelihoodRatioMixin): class LIGOLWContentHandler(ligolw.LIGOLWContentHandler): pass - def __init__(self, instruments, snr_threshold, min_instruments=2): + def __init__(self, instruments=frozenset(("H1", "L1")), min_instruments=2): self.triangulators = triangulators(dict.fromkeys(instruments, 8e-5)) - self.numerator = string_lr_far.LnSignalDensity(instruments = instruments, min_instruments = min_instruments, snr_threshold = snr_threshold) - self.denominator = string_lr_far.LnNoiseDensity(instruments = instruments, min_instruments = min_instruments, snr_threshold = snr_threshold) - self.candidates = string_lr_far.LnLRDensity(instruments = instruments, min_instruments = min_instruments, snr_threshold = snr_threshold) + self.numerator = string_lr_far.LnSignalDensity(instruments = instruments, min_instruments = min_instruments) + self.denominator = string_lr_far.LnNoiseDensity(instruments = instruments, min_instruments = min_instruments) + self.candidates = string_lr_far.LnLRDensity(instruments = instruments, min_instruments = min_instruments) + + @property + def instruments(self): + return self.denominator.instruments @property def min_instruments(self): return self.denominator.min_instruments - def __call__(self, **kwargs): - """ - Evaluate the ranking statistic. - """ - # Full lnL ranking stat, defined to be the largest lnL from - # all allowed subsets of trigges. Maximizes over 2+ IFO combos. - return max(super(StringCoincParamsDistribution, self).__call__(**kwargs) for kwargs in kwarggen(min_instruments = self.min_instruments, **kwargs)) - def __iadd__(self, other): if type(self) != type(other): raise TypeError(other) @@ -247,7 +243,7 @@ class StringCoincParamsDistributions(snglcoinc.LnLikelihoodRatioMixin): return dict( snrs = dict((event.ifo, event.snr) for event in events), - chi2s_over_snr2s = dict((event.ifo, event.chisq / event.chisq_dof / event.snr**2.) for event in events), + chi2s_over_snr2s = dict((event.ifo, event.chisq / event.chisq_dof / event.snr**2.) for event in events) ) def ln_lr_from_triggers(self, events, offsetvector): @@ -270,7 +266,7 @@ class StringCoincParamsDistributions(snglcoinc.LnLikelihoodRatioMixin): @classmethod def from_xml(cls, xml, name): xml = cls.get_xml_root(xml, name) - self = cls([]) + self = cls.__new__(cls) self.numerator = string_lr_far.LnSignalDensity.from_xml(xml, "numerator") self.denominator = string_lr_far.LnNoiseDensity.from_xml(xml, "denominator") self.candidates = string_lr_far.LnLRDensity.from_xml(xml, "candidates")