Coverage for tests/test_truncatedGaussian.py: 17%

142 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-19 04: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 

31 

32import lsst.log 

33import lsst.utils.logging 

34import lsst.utils.tests 

35import lsst.meas.modelfit 

36 

37 

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) 

41 

42 

43class TruncatedGaussianTestCase(lsst.utils.tests.TestCase): 

44 

45 def setUp(self): 

46 numpy.random.seed(500) 

47 

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)) 

62 

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) 

77 

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) 

96 

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) 

111 

112 def integrate1d(self, tg): 

113 evaluator = tg.evaluate() 

114 

115 def func(x): 

116 return evaluator(numpy.array([x])) 

117 return scipy.integrate.quad(func, 0.0, numpy.Inf) 

118 

119 def integrate2d(self, tg): 

120 evaluator = tg.evaluate() 

121 

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) 

125 

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) 

146 

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) 

173 

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) 

191 

192 

193class TestMemory(lsst.utils.tests.MemoryTestCase): 

194 pass 

195 

196 

197def setup_module(module): 

198 lsst.utils.tests.init() 

199 

200 

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()