Coverage for tests/test_truncatedGaussian.py: 20%
142 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-04 03:41 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-04 03:41 -0700
1#
2# LSST Data Management System
3#
4# Copyright 2008-2016 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <https://www.lsstcorp.org/LegalNotices/>.
22#
23import unittest
24import numpy
25try:
26 import scipy.integrate
27 import scipy.stats
28 import scipy.special
29except ImportError:
30 scipy = None
32import lsst.log
33import lsst.utils.logging
34import lsst.utils.tests
35import lsst.meas.modelfit
38if False:
39 lsst.utils.logging.trace_set_at("lsst.meas.modelfit.integrals", 5)
40 lsst.utils.logging.trace_set_at("lsst.meas.modelfit.TruncatedGaussian", 5)
43class TruncatedGaussianTestCase(lsst.utils.tests.TestCase):
45 def setUp(self):
46 numpy.random.seed(500)
48 def check1d(self, mu, hessian, tg):
49 evaluator = tg.evaluate()
50 logEvaluator = tg.evaluateLog()
51 dist = scipy.stats.norm(loc=mu[0], scale=hessian[0, 0]**-0.5)
52 self.assertFloatsAlmostEqual(1.0 - dist.cdf(0.0), tg.getUntruncatedFraction())
53 eps = 1E-7
54 if numpy.all(mu >= 0.0):
55 self.assertFloatsAlmostEqual(logEvaluator(mu), tg.getLogPeakAmplitude())
56 self.assertGreater(logEvaluator(mu+eps), tg.getLogPeakAmplitude())
57 self.assertGreater(logEvaluator(mu-eps), tg.getLogPeakAmplitude())
58 peak = tg.maximize()
59 self.assertGreater(evaluator(peak), 0.0)
60 self.assertLess(evaluator(peak+eps), evaluator(peak))
61 self.assertLess(evaluator(peak-eps), evaluator(peak))
63 def altLogEval(x):
64 if numpy.any(x < 0):
65 return float("inf")
66 return tg.getLogPeakAmplitude() + 0.5*hessian[0, 0]*(x-mu[0])**2
67 for alpha in (numpy.random.randn(10, 1) * hessian[0, 0]**-0.5 + mu[0]):
68 x1 = logEvaluator(alpha)
69 x2 = altLogEval(alpha[0])
70 if numpy.isfinite(x1) and numpy.isfinite(x2):
71 self.assertFloatsAlmostEqual(x1, x2, rtol=1E-14)
72 else:
73 self.assertEqual(x1, x2)
74 integral, check = self.integrate1d(tg)
75 self.assertLess(check, 1E-7)
76 self.assertFloatsAlmostEqual(integral, numpy.exp(-tg.getLogIntegral()), atol=check)
78 def check2d(self, mu, hessian, tg, isDegenerate=False):
79 evaluator = tg.evaluate()
80 logEvaluator = tg.evaluateLog()
81 unit1 = numpy.array([1.0, 0.0])
82 unit2 = numpy.array([0.0, 1.0])
83 eps = 1E-7
84 if numpy.all(mu >= 0.0):
85 self.assertFloatsAlmostEqual(logEvaluator(mu), tg.getLogPeakAmplitude())
86 self.assertGreater(logEvaluator(mu + unit1*eps), tg.getLogPeakAmplitude())
87 self.assertGreater(logEvaluator(mu - unit1*eps), tg.getLogPeakAmplitude())
88 self.assertGreater(logEvaluator(mu + unit2*eps), tg.getLogPeakAmplitude())
89 self.assertGreater(logEvaluator(mu - unit2*eps), tg.getLogPeakAmplitude())
90 peak = tg.maximize()
91 self.assertGreater(evaluator(peak), 0.0)
92 self.assertLess(evaluator(peak + unit1*eps) / evaluator(peak), 1.0)
93 self.assertLess(evaluator(peak - unit1*eps) / evaluator(peak), 1.0)
94 self.assertLess(evaluator(peak + unit2*eps) / evaluator(peak), 1.0)
95 self.assertLess(evaluator(peak - unit2*eps) / evaluator(peak), 1.0)
97 def altLogEval(a):
98 if numpy.any(a < 0):
99 return float("inf")
100 return tg.getLogPeakAmplitude() + 0.5*numpy.dot(numpy.dot(hessian, a - mu).transpose(), a - mu)
101 for alpha in (numpy.random.randn(10, 2) * hessian.diagonal()**-0.5 + mu):
102 x1 = logEvaluator(alpha)
103 x2 = altLogEval(alpha)
104 if numpy.isfinite(x1) and numpy.isfinite(x2):
105 self.assertFloatsAlmostEqual(x1, x2, rtol=1E-14)
106 else:
107 self.assertEqual(x1, x2)
108 integral, check = self.integrate2d(tg)
109 self.assertLess(check, 1E-7)
110 self.assertFloatsAlmostEqual(integral, numpy.exp(-tg.getLogIntegral()), atol=check)
112 def integrate1d(self, tg):
113 evaluator = tg.evaluate()
115 def func(x):
116 return evaluator(numpy.array([x]))
117 return scipy.integrate.quad(func, 0.0, numpy.Inf)
119 def integrate2d(self, tg):
120 evaluator = tg.evaluate()
122 def func(x, y):
123 return evaluator(numpy.array([x, y]))
124 return scipy.integrate.dblquad(func, 0.0, numpy.Inf, lambda x: 0.0, lambda x: numpy.Inf)
126 @unittest.skipIf(scipy is None, "Test requires SciPy")
127 def test1d(self):
128 for i in range(5):
129 sigma = (numpy.random.randn(1, 1)**2 + 1)*5
130 mu = (numpy.random.randn(1))*3
131 q0 = float(numpy.random.randn())
132 hessian = numpy.linalg.inv(sigma)
133 gradient = -numpy.dot(hessian, mu)
134 tg1 = lsst.meas.modelfit.TruncatedGaussian.fromStandardParameters(mu, sigma)
135 tg2 = lsst.meas.modelfit.TruncatedGaussian.fromSeriesParameters(q0, gradient, hessian)
136 self.assertEqual(tg1.getLogIntegral(), 0.0)
137 self.assertFloatsAlmostEqual(tg1.getLogPeakAmplitude(),
138 (0.5*numpy.log(numpy.linalg.det(2.0*numpy.pi*sigma))
139 + numpy.log(tg1.getUntruncatedFraction())),
140 rtol=1E-13)
141 self.assertFloatsAlmostEqual(tg2.getLogPeakAmplitude(),
142 q0 + 0.5*numpy.dot(mu, gradient),
143 rtol=1E-13)
144 self.check1d(mu, hessian, tg1)
145 self.check1d(mu, hessian, tg2)
147 @unittest.skipIf(scipy is None, "Test requires SciPy")
148 def test2d(self):
149 for i in range(5):
150 x = numpy.linspace(-1, 1, 5)
151 model = numpy.zeros((x.size, 2), dtype=float)
152 model[:, 0] = x
153 model[:, 1] = x**2 + x
154 data = numpy.random.randn(x.size) + model[:, 0]*0.9 + model[:, 1]*1.1
155 q0 = 0.5*float(numpy.dot(data, data))
156 gradient = -numpy.dot(model.transpose(), data)
157 hessian = numpy.dot(model.transpose(), model)
158 sigma = numpy.linalg.inv(hessian)
159 self.assertFloatsAlmostEqual(numpy.linalg.inv(sigma), hessian, rtol=1E-15, atol=1E-15)
160 mu = -numpy.dot(sigma, gradient)
161 tg1 = lsst.meas.modelfit.TruncatedGaussian.fromStandardParameters(mu, sigma)
162 self.assertFloatsAlmostEqual(tg1.getLogPeakAmplitude(),
163 (numpy.log(tg1.getUntruncatedFraction())
164 + 0.5*numpy.log(numpy.linalg.det(2.0*numpy.pi*sigma))),
165 rtol=1E-13)
166 self.assertEqual(tg1.getLogIntegral(), 0.0)
167 self.check2d(mu, hessian, tg1)
168 tg2 = lsst.meas.modelfit.TruncatedGaussian.fromSeriesParameters(q0, gradient, hessian)
169 self.assertFloatsAlmostEqual(tg2.getLogPeakAmplitude(),
170 q0+0.5*numpy.dot(mu, gradient),
171 rtol=1E-13)
172 self.check2d(mu, hessian, tg2)
174 @unittest.skipIf(scipy is None, "Test requires SciPy")
175 def testDegenerate(self):
176 for i in range(5):
177 x = numpy.linspace(-1, 1, 5)
178 model = numpy.zeros((x.size, 2), dtype=float)
179 model[:, 0] = x
180 model[:, 1] = 2*x
181 data = numpy.random.randn(x.size) + model[:, 0]*0.9 + model[:, 1]*1.1
182 q0 = 0.5*float(numpy.dot(data, data))
183 gradient = -numpy.dot(model.transpose(), data)
184 hessian = numpy.dot(model.transpose(), model)
185 mu, _, _, _ = numpy.linalg.lstsq(model, data, rcond=None)
186 tg = lsst.meas.modelfit.TruncatedGaussian.fromSeriesParameters(q0, gradient, hessian)
187 self.assertFloatsAlmostEqual(tg.getLogPeakAmplitude(),
188 q0+0.5*numpy.dot(mu, gradient),
189 rtol=1E-13)
190 self.check2d(mu, hessian, tg, isDegenerate=True)
193class TestMemory(lsst.utils.tests.MemoryTestCase):
194 pass
197def setup_module(module):
198 lsst.utils.tests.init()
201if __name__ == "__main__": 201 ↛ 202line 201 didn't jump to line 202, because the condition on line 201 was never true
202 lsst.utils.tests.init()
203 unittest.main()