Coverage for tests/test_mixture.py: 18%
132 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-16 02:43 -0700
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-16 02:43 -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
27import lsst.utils.tests
28import lsst.meas.modelfit
30try:
31 import scipy.stats
32except ImportError:
33 scipy = None
36class MixtureTestCase(lsst.utils.tests.TestCase):
38 def setUp(self):
39 numpy.random.seed(500)
40 self.rng = lsst.afw.math.Random("MT19937", 500)
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)
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))
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)
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)
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)
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)
135 def testDerivatives(self):
136 epsilon = 1E-7
137 g = self.makeRandomMixture(3, 4)
138 t = self.makeRandomMixture(4, 3, df=4.0)
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)
172 for x in numpy.random.randn(10, g.getDimension()):
173 doTest(g, x)
175 for x in numpy.random.randn(10, t.getDimension()):
176 doTest(t, x)
179class TestMemory(lsst.utils.tests.MemoryTestCase):
180 pass
183def setup_module(module):
184 lsst.utils.tests.init()
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()