Coverage for tests/test_mixture.py: 18%

132 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-29 03:16 -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 os 

24import unittest 

25import numpy 

26 

27import lsst.utils.tests 

28import lsst.meas.modelfit 

29 

30try: 

31 import scipy.stats 

32except ImportError: 

33 scipy = None 

34 

35 

36class MixtureTestCase(lsst.utils.tests.TestCase): 

37 

38 def setUp(self): 

39 numpy.random.seed(500) 

40 self.rng = lsst.afw.math.Random("MT19937", 500) 

41 

42 @staticmethod 

43 def makeRandomMixture(nDim, nComponents, df=float("inf")): 

44 componentList = [] 

45 for i in range(nComponents): 

46 mu = numpy.random.randn(nDim)*4 

47 a = numpy.random.randn(nDim+1, nDim) 

48 sigma = numpy.dot(a.transpose(), a) + numpy.identity(nDim) 

49 componentList.append(lsst.meas.modelfit.Mixture.Component(numpy.random.rand(), mu, sigma)) 

50 return lsst.meas.modelfit.Mixture(nDim, componentList, df) 

51 

52 def testWrappers(self): 

53 """Test that we correctly wrapped tricky things. 

54 """ 

55 l1 = [] 

56 l1.append(lsst.meas.modelfit.MixtureComponent(1)) 

57 l1.append(lsst.meas.modelfit.MixtureComponent(1)) 

58 l1.append(lsst.meas.modelfit.MixtureComponent(1)) 

59 l1[0].weight = 1.0 

60 l1[0].setMu(numpy.array([1.0], dtype=float)) 

61 l1[0].setSigma(numpy.array([[4.0]], dtype=float)) 

62 l1[1].weight = 0.5 

63 l1[2].weight = 0.5 

64 m1 = lsst.meas.modelfit.Mixture(1, l1) 

65 self.assertEqual(m1[0].weight, 0.5) 

66 self.assertEqual([0.5, 0.25, 0.25], [c.weight for c in m1]) 

67 self.assertFloatsAlmostEqual(m1[0].getMu(), numpy.array([1.0], dtype=float)) 

68 self.assertFloatsAlmostEqual(m1[0].getSigma(), numpy.array([4.0], dtype=float)) 

69 self.assertFloatsAlmostEqual(m1.evaluate(m1[1], numpy.array([0.0], dtype=float)), 

70 m1[1].weight*(2.0*numpy.pi)**(-0.5)) 

71 self.assertFloatsAlmostEqual(m1.evaluate(numpy.array([0.0], dtype=float)), 

72 (m1[0].weight*numpy.exp(-0.125)/2 + m1[1].weight + m1[2].weight) 

73 * (2.0*numpy.pi)**(-0.5)) 

74 

75 def testGaussian(self): 

76 """Test that our implementations for a single-component Gaussian are correct. 

77 """ 

78 m = self.makeRandomMixture(2, 1) 

79 mu = m[0].getMu() 

80 sigma = m[0].getSigma() 

81 fisher = numpy.linalg.inv(sigma) 

82 x = numpy.random.randn(20, 2) 

83 p = numpy.zeros(20, dtype=float) 

84 m.evaluate(x, p) 

85 z = ((x - mu)[:, numpy.newaxis, :] * fisher[numpy.newaxis, :, :, ] 

86 * (x - mu)[:, :, numpy.newaxis]).sum(axis=2).sum(axis=1) 

87 self.assertFloatsAlmostEqual(p, numpy.exp(-0.5*z) / numpy.linalg.det(2*numpy.pi*sigma)**0.5) 

88 x = numpy.zeros((1000000, 2), dtype=float) 

89 m.draw(self.rng, x) 

90 self.assertFloatsAlmostEqual(x.mean(axis=0), mu, rtol=2E-2) 

91 self.assertFloatsAlmostEqual(numpy.cov(x, rowvar=False), sigma, rtol=3E-2) 

92 

93 @unittest.skipIf(scipy is None, "Test requires SciPy") 

94 def testGaussianSciPy(self): 

95 m = self.makeRandomMixture(2, 1) 

96 x = numpy.zeros((1000000, 2), dtype=float) 

97 m.draw(self.rng, x) 

98 self.assertGreater(scipy.stats.normaltest(x[:, 0])[1], 0.05) 

99 self.assertGreater(scipy.stats.normaltest(x[:, 1])[1], 0.05) 

100 

101 @unittest.skipIf(scipy is None, "Test requires SciPy") 

102 def testStudentsT(self): 

103 """Test that our implementations for a single-component Student's T are correct. 

104 """ 

105 for df in [4, 8]: 

106 m = self.makeRandomMixture(1, 1, df=df) 

107 mu = m[0].getMu() 

108 sigma = m[0].getSigma() 

109 x = numpy.random.randn(20, 1) 

110 p = numpy.zeros(20, dtype=float) 

111 m.evaluate(x, p) 

112 x = x.reshape(20) 

113 z = (x - mu)/(sigma**0.5) 

114 self.assertFloatsAlmostEqual(p, scipy.stats.t.pdf(z, df)/sigma**0.5) 

115 x = numpy.zeros((1000000, 1), dtype=float) 

116 m.draw(self.rng, x) 

117 self.assertFloatsAlmostEqual(x.mean(), mu, rtol=5E-2) 

118 self.assertFloatsAlmostEqual(x.var(), sigma * df / (df - 2), rtol=5E-2) 

119 self.assertLess(scipy.stats.normaltest(x)[1], 0.05) 

120 

121 def testPersistence(self): 

122 """Test table-based persistence of Mixtures""" 

123 filename = "testMixturePersistence.fits" 

124 mix1 = self.makeRandomMixture(3, 4, df=3.5) 

125 mix1.writeFits(filename) 

126 mix2 = lsst.meas.modelfit.Mixture.readFits(filename) 

127 self.assertEqual(mix1.getDegreesOfFreedom(), mix2.getDegreesOfFreedom()) 

128 self.assertEqual(len(mix1), len(mix2)) 

129 for c1, c2 in zip(mix1, mix2): 

130 self.assertFloatsAlmostEqual(c1.weight, c2.weight) 

131 self.assertFloatsAlmostEqual(c1.getMu(), c2.getMu()) 

132 self.assertFloatsAlmostEqual(c1.getSigma(), c2.getSigma()) 

133 os.remove(filename) 

134 

135 def testDerivatives(self): 

136 epsilon = 1E-7 

137 g = self.makeRandomMixture(3, 4) 

138 t = self.makeRandomMixture(4, 3, df=4.0) 

139 

140 def doTest(mixture, point): 

141 n = mixture.getDimension() 

142 # Compute numeric first derivatives 

143 testPoints = numpy.zeros((2*n, n), dtype=float) 

144 testPoints[:, :] = point[numpy.newaxis, :] 

145 for i in range(n): 

146 testPoints[i, i] += epsilon 

147 testPoints[n+i, i] -= epsilon 

148 testValues = numpy.zeros(2*n, dtype=float) 

149 mixture.evaluate(testPoints, testValues) 

150 numericGradient = numpy.zeros(n, dtype=float) 

151 for i in range(n): 

152 numericGradient[i] = (testValues[i] - testValues[n+i]) / (2.0 * epsilon) 

153 # Compute numeric second derivatives from analytic first derivatives 

154 numericHessian = numpy.zeros((n, n), dtype=float) 

155 testGrad1 = numpy.zeros(n, dtype=float) 

156 testGrad2 = numpy.zeros(n, dtype=float) 

157 testHessian = numpy.zeros((n, n), dtype=float) 

158 for i in range(n): 

159 testPoint = point.copy() 

160 testPoint[i] += epsilon 

161 mixture.evaluateDerivatives(testPoint, testGrad1, testHessian) 

162 testPoint[i] -= 2.0*epsilon 

163 mixture.evaluateDerivatives(testPoint, testGrad2, testHessian) 

164 numericHessian[i, :] = (testGrad1 - testGrad2) / (2.0 * epsilon) 

165 # Compute analytic derivatives and compare 

166 analyticGradient = numpy.zeros(n, dtype=float) 

167 analyticHessian = numpy.zeros((n, n), dtype=float) 

168 mixture.evaluateDerivatives(point, analyticGradient, analyticHessian) 

169 self.assertFloatsAlmostEqual(analyticGradient, numericGradient, rtol=1.5E-6) 

170 self.assertFloatsAlmostEqual(analyticHessian, numericHessian, rtol=1E-6) 

171 

172 for x in numpy.random.randn(10, g.getDimension()): 

173 doTest(g, x) 

174 

175 for x in numpy.random.randn(10, t.getDimension()): 

176 doTest(t, x) 

177 

178 

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

180 pass 

181 

182 

183def setup_module(module): 

184 lsst.utils.tests.init() 

185 

186 

187if __name__ == "__main__": 187 ↛ 188line 187 didn't jump to line 188, because the condition on line 187 was never true

188 lsst.utils.tests.init() 

189 unittest.main()