Skip to content

Commit c021ee1

Browse files
committed
Normalize the sky marginalization weights over the series, not the draws
The sky and time marginalized likelihood grew with the number of points it was told to use: over a range of sixty four in the count it climbed by four and a half in the log likelihood ratio, many times its own scatter, so no count gave the same answer as any other. The times are drawn from each detector's signal to noise series in proportion to the likelihood along it, and what each carries is the probability it was drawn with. That was being worked out over the drawn times rather than over the series they came from, which makes it one in however many were drawn, and leaves the answer growing by the log of the count. The steps matched: 1.39, 1.69 and 1.41 against log 4 of 1.386. Normalize over the series each detector drew from, as the time-only marginalization already does. The same range of counts now moves the answer by 0.36, within the scatter of the counts themselves, while the error on it still falls with the count as it should.
1 parent a9806a9 commit c021ee1

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

pycbc/inference/models/tools.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,13 @@ def make_init():
526526

527527
w = snr.squared_norm().numpy() / 2.0
528528
i = draw_sample(w, size=vsamples)
529+
# the times were drawn from this detector's series in
530+
# proportion to its likelihood, so normalizing over that
531+
# series is what turns it into the probability they were
532+
# drawn with. Normalizing over the drawn times instead would
533+
# make it a probability of one in however many were drawn,
534+
# and the answer would grow with the number of them.
535+
w -= logsumexp(w)
529536

530537
if sref is not None:
531538
mcweight += w[i]
@@ -538,7 +545,6 @@ def make_init():
538545
mcweight = w[i]
539546

540547
idx.append(i)
541-
mcweight -= logsumexp(mcweight)
542548

543549
# check if delay is in dict, if not, throw out
544550
ti = []

test/test_marg_normalization.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@
2929
from utils import simple_exit
3030

3131
from pycbc.detector import Detector
32-
from pycbc.distributions import JointDistribution, SinAngle, Uniform
32+
from pycbc.distributions import (
33+
CosAngle,
34+
JointDistribution,
35+
SinAngle,
36+
Uniform,
37+
UniformAngle,
38+
)
3339
from pycbc.inference import models
3440
from pycbc.noise import noise_from_psd
3541
from pycbc.psd import aLIGOZeroDetHighPower
@@ -81,6 +87,55 @@ def prior(self, halfwidth):
8187
Uniform(distance=(10, 200)),
8288
Uniform(tc=(TC - halfwidth, TC + halfwidth)))
8389

90+
def sky_marginalized(self, npoint, nseed=6):
91+
"""Mean and spread of the sky and time marginalized likelihood."""
92+
variable = ['distance', 'inclination', 'tc', 'polarization',
93+
'ra', 'dec']
94+
static = {k: v for k, v in self.static.items()
95+
if k not in ('ra', 'dec', 'polarization')}
96+
dists = [SinAngle(inclination=None), Uniform(distance=(10, 200)),
97+
Uniform(tc=(TC - 0.1, TC + 0.1)),
98+
UniformAngle(polarization=None), UniformAngle(ra=None),
99+
CosAngle(dec=None)]
100+
values = []
101+
for s in range(nseed):
102+
numpy.random.seed(700 + s)
103+
model = models.MarginalizedTime(
104+
list(variable), copy.deepcopy(self.data),
105+
low_frequency_cutoff=self.flow, psds=self.psds,
106+
static_params=static,
107+
prior=JointDistribution(list(variable), *dists),
108+
marginalize_phase=True,
109+
marginalize_vector_params='tc,ra,dec,polarization',
110+
marginalize_vector_samples=npoint, sample_rate=4096,
111+
marginalize_sky_initial_samples=1e6)
112+
model.update(**self.point)
113+
values.append(model.loglr)
114+
return numpy.mean(values), numpy.std(values) / nseed ** 0.5
115+
116+
def test_sky_marginalization_does_not_depend_on_the_point_count(self):
117+
"""The number of points must buy precision, not a bigger answer.
118+
119+
The times are drawn from each detector's signal to noise series,
120+
so what they carry is their probability under that series. Taking
121+
it over the drawn times rather than over the series made it one in
122+
however many were drawn, and the answer grew by the log of that:
123+
over a range of sixty four in the count it climbed by four and a
124+
half, far outside its own scatter.
125+
"""
126+
seen = [(n,) + self.sky_marginalized(n) for n in (128, 2048)]
127+
(_, coarse, coarse_err), (_, fine, fine_err) = seen
128+
self.assertLess(abs(coarse - fine),
129+
4 * (coarse_err ** 2 + fine_err ** 2) ** 0.5,
130+
"%s" % seen)
131+
132+
def test_more_sky_points_are_more_precise(self):
133+
"""What the count does buy is precision."""
134+
_, coarse = self.sky_marginalized(128)
135+
_, fine = self.sky_marginalized(2048)
136+
self.assertLess(fine, coarse,
137+
"error on the mean went %.4f to %.4f" % (coarse, fine))
138+
84139
def integrated(self, halfwidth, npoint=4001):
85140
"""The marginal computed by summing the likelihood over the prior."""
86141
variable, prior = self.prior(halfwidth)

0 commit comments

Comments
 (0)