Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

# 

# LSST Data Management System 

# 

# Copyright 2008-2016 AURA/LSST. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <https://www.lsstcorp.org/LegalNotices/>. 

# 

import unittest 

import numpy 

 

import lsst.utils.tests 

import lsst.log 

import lsst.log.utils 

import lsst.meas.modelfit 

 

try: 

import scipy.integrate 

except ImportError: 

scipy = None 

 

lsst.log.utils.traceSetAt("meas.modelfit.SoftenedLinearPrior", 5) 

 

 

class SoftenedLinearPriorTestCase(lsst.utils.tests.TestCase): 

 

NUM_DIFF_STEP = 1E-3 

 

def setUp(self): 

# a prior with broad ramps and non-zero slope; broad ramps makes evaluating numerical 

# derivatives easier, and we want to do that to check the analytic ones 

numpy.random.seed(500) 

ctrl = lsst.meas.modelfit.SoftenedLinearPrior.Control() 

ctrl.logRadiusMinOuter = ctrl.logRadiusMinInner - 2.0 

ctrl.logRadiusMaxOuter = ctrl.logRadiusMaxInner + 2.0 

ctrl.ellipticityMaxOuter = ctrl.ellipticityMaxInner + 2.0 

ctrl.logRadiusMinMaxRatio = 2.0 

self.prior = lsst.meas.modelfit.SoftenedLinearPrior(ctrl) 

self.amplitudes = numpy.array([1.0], dtype=lsst.meas.modelfit.Scalar) 

 

def tearDown(self): 

del self.prior 

del self.amplitudes 

 

def evaluatePrior(self, e1, e2, r): 

b = numpy.broadcast(e1, e2, r) 

p = numpy.zeros(b.shape, dtype=lsst.meas.modelfit.Scalar) 

for i, (e1i, e2i, ri) in enumerate(b): 

p.flat[i] = self.prior.evaluate(numpy.array([e1i, e2i, ri]), self.amplitudes) 

return p 

 

def checkDerivatives(self, e1, e2, r): 

nonlinear = numpy.array([e1, e2, r], dtype=lsst.meas.modelfit.Scalar) 

amplitudeGradient = numpy.zeros(1, dtype=lsst.meas.modelfit.Scalar) 

amplitudeHessian = numpy.zeros((1, 1), dtype=lsst.meas.modelfit.Scalar) 

crossHessian = numpy.zeros((3, 1), dtype=lsst.meas.modelfit.Scalar) 

nonlinearGradient = numpy.zeros(3, dtype=lsst.meas.modelfit.Scalar) 

nonlinearHessian = numpy.zeros((3, 3), dtype=lsst.meas.modelfit.Scalar) 

self.prior.evaluateDerivatives(nonlinear, self.amplitudes, 

nonlinearGradient, amplitudeGradient, 

nonlinearHessian, amplitudeHessian, 

crossHessian) 

p = self.prior.evaluate(nonlinear, self.amplitudes) 

for i in range(3): 

nonlinearA = nonlinear.copy() 

nonlinearB = nonlinear.copy() 

nonlinearA[i] -= self.NUM_DIFF_STEP 

nonlinearB[i] += self.NUM_DIFF_STEP 

pA = self.prior.evaluate(nonlinearA, self.amplitudes) 

pB = self.prior.evaluate(nonlinearB, self.amplitudes) 

dp = (pB - pA) / (2*self.NUM_DIFF_STEP) 

self.assertFloatsAlmostEqual(nonlinearGradient[i], dp, rtol=1E-3, atol=1E-8) 

d2p = (pA + pB - 2*p) / self.NUM_DIFF_STEP**2 

self.assertFloatsAlmostEqual(nonlinearHessian[i, i], d2p, rtol=1E-3, atol=1E-8) 

for j in range(i+1, 3): 

nonlinearAA = nonlinearA.copy() 

nonlinearAB = nonlinearA.copy() 

nonlinearBA = nonlinearB.copy() 

nonlinearBB = nonlinearB.copy() 

nonlinearAA[j] -= self.NUM_DIFF_STEP 

nonlinearAB[j] += self.NUM_DIFF_STEP 

nonlinearBA[j] -= self.NUM_DIFF_STEP 

nonlinearBB[j] += self.NUM_DIFF_STEP 

pAA = self.prior.evaluate(nonlinearAA, self.amplitudes) 

pAB = self.prior.evaluate(nonlinearAB, self.amplitudes) 

pBA = self.prior.evaluate(nonlinearBA, self.amplitudes) 

pBB = self.prior.evaluate(nonlinearBB, self.amplitudes) 

d2p = (pBB - pAB - pBA + pAA) / (4*self.NUM_DIFF_STEP**2) 

self.assertFloatsAlmostEqual(nonlinearHessian[i, j], d2p, rtol=1E-3, atol=1E-8) 

 

def testDerivatives(self): 

"""Test that evaluateDerivatives() returns results similar to finite-differences 

on evaluate(). 

""" 

ctrl = self.prior.getControl() 

# a single |e| value for each ellipticity zone 

ellipticityPoints = numpy.array([0.5*ctrl.ellipticityMaxInner, 

0.5*(ctrl.ellipticityMaxInner + ctrl.ellipticityMaxOuter)]) 

# a single ln(radius) value for each logRadius zone 

logRadiusPoints = numpy.array([0.5*(ctrl.logRadiusMinOuter + ctrl.logRadiusMinInner), 

0.5*(ctrl.logRadiusMinInner + ctrl.logRadiusMaxInner), 

0.5*(ctrl.logRadiusMaxInner + ctrl.logRadiusMaxOuter)]) 

# a range of position angles 

thetaPoints = numpy.linspace(0.0, numpy.pi, 5) 

for theta in thetaPoints: 

for e in ellipticityPoints: 

e1 = e*numpy.cos(2.0*theta) 

e2 = e*numpy.sin(2.0*theta) 

for r in logRadiusPoints: 

self.checkDerivatives(e1, e2, r) 

 

@unittest.skipIf(scipy is None, "could not import scipy") 

def testIntegral(self): 

"""Test that the prior is properly normalized. 

 

Normally, this test has a very low bar, because it's expensive to compute a high-quality 

numerical integral to compare with. Even so, the scipy integrator does better than it 

thinks it does, and we use that smaller tolerance for the test. That means this test 

could fail if something about the scipy integrator changes, because we're not telling it 

that it has to get as close as it currently is (because doing so would take way too long). 

 

If this class is ever changed, we should do at least one of this test with the tolerances 

tightened. 

""" 

ctrl = self.prior.getControl() 

integral, absErr = scipy.integrate.tplquad( 

self.evaluatePrior, 

ctrl.logRadiusMinOuter, ctrl.logRadiusMaxOuter, 

lambda logR: -ctrl.ellipticityMaxOuter, 

lambda logR: ctrl.ellipticityMaxOuter, 

lambda logR, e2: -(ctrl.ellipticityMaxOuter**2 - e2**2)**0.5, 

lambda logR, e2: (ctrl.ellipticityMaxOuter**2 - e2**2)**0.5, 

epsabs=1.0, 

epsrel=1.0, 

) 

self.assertFloatsAlmostEqual(integral, 1.0, atol=0.01) 

 

def testEllipticityDistribution(self): 

"""Test that the ellipticity distribution is constant in the inner region, 

mononotically decreasing in the ramp, and zero in the outer region, according 

to evaluate(). 

""" 

ctrl = self.prior.getControl() 

# a range of |e| values in each ellipticity zone 

eInnerPoints = numpy.linspace(0.0, ctrl.ellipticityMaxInner, 5) 

eRampPoints = numpy.linspace(ctrl.ellipticityMaxInner, ctrl.ellipticityMaxOuter, 5) 

eOuterPoints = numpy.linspace(ctrl.ellipticityMaxOuter, ctrl.ellipticityMaxOuter + 5.0, 5) 

# a range of position angles 

thetaPoints = numpy.linspace(0.0, numpy.pi, 5) 

# a single ln(radius) value for each logRadius zone 

logRadiusPoints = numpy.array([0.5*(ctrl.logRadiusMinOuter + ctrl.logRadiusMinInner), 

0.5*(ctrl.logRadiusMinInner + ctrl.logRadiusMaxInner), 

0.5*(ctrl.logRadiusMaxInner + ctrl.logRadiusMaxOuter)]) 

for logRadius in logRadiusPoints: 

for theta in thetaPoints: 

# All inner points should have the same value 

pInner = self.evaluatePrior(eInnerPoints*numpy.cos(2*theta), 

eInnerPoints*numpy.sin(2*theta), 

logRadius) 

self.assertFloatsAlmostEqual(pInner.mean(), pInner) 

 

# Each ramp point should be greater than the next one 

pRamp = self.evaluatePrior(eRampPoints*numpy.cos(2*theta), 

eRampPoints*numpy.sin(2*theta), 

logRadius) 

numpy.testing.assert_array_less(pRamp[1:], pRamp[:-1]) 

 

# Each outer point should be zero 

pOuter = self.evaluatePrior(eOuterPoints*numpy.cos(2*theta), 

eOuterPoints*numpy.sin(2*theta), 

logRadius) 

self.assertFloatsAlmostEqual(pOuter, 0.0) 

 

def testLogRadiusDistribution(self): 

"""Test that the ln(radius) distribution is constant in the inner region, 

mononotically decreasing in the ramps, and zero in the outer regions, according 

to evaluate(). 

""" 

ctrl = self.prior.getControl() 

# a range of ln(radius) values in each logRadius zone 

rLowerOuterPoints = numpy.linspace(ctrl.logRadiusMinOuter - 2.0, ctrl.logRadiusMinOuter, 5) 

rLowerRampPoints = numpy.linspace(ctrl.logRadiusMinOuter, ctrl.logRadiusMinInner, 5) 

rInnerPoints = numpy.linspace(ctrl.logRadiusMinInner, ctrl.logRadiusMaxInner, 5) 

rUpperRampPoints = numpy.linspace(ctrl.logRadiusMaxInner, ctrl.logRadiusMaxOuter, 5) 

rUpperOuterPoints = numpy.linspace(ctrl.logRadiusMaxOuter, ctrl.logRadiusMaxOuter + 2.0, 5) 

 

# a range of position angles 

thetaPoints = numpy.linspace(0.0, numpy.pi, 5) 

# a single |e| value for each ellipticity zone 

ellipticityPoints = numpy.array([0.5*ctrl.ellipticityMaxInner, 

0.5*(ctrl.ellipticityMaxInner + ctrl.ellipticityMaxOuter)]) 

for ellipticity in ellipticityPoints: 

for theta in thetaPoints: 

e1 = ellipticity*numpy.cos(2*theta) 

e2 = ellipticity*numpy.sin(2*theta) 

# Outer points should be zero 

pLowerOuter = self.evaluatePrior(e1, e2, rLowerOuterPoints) 

self.assertFloatsAlmostEqual(pLowerOuter, 0.0) 

# Each ramp point should be less than the next one 

pLowerRamp = self.evaluatePrior(e1, e2, rLowerRampPoints) 

numpy.testing.assert_array_less(pLowerRamp[:-1], pLowerRamp[1:]) 

# All adjacent inner points should have the same distance between them (constant slope) 

pInner = self.evaluatePrior(e1, e2, rInnerPoints) 

diffs = pInner[1:] - pInner[:-1] 

self.assertFloatsAlmostEqual(diffs.mean(), diffs) 

# Each ramp point should be greater than the next one 

pUpperRamp = self.evaluatePrior(e1, e2, rUpperRampPoints) 

numpy.testing.assert_array_less(pUpperRamp[1:], pUpperRamp[:-1]) 

# Outer points should be zero 

pUpperOuter = self.evaluatePrior(e1, e2, rUpperOuterPoints) 

self.assertFloatsAlmostEqual(pUpperOuter, 0.0) 

 

 

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

pass 

 

 

def setup_module(module): 

lsst.utils.tests.init() 

 

 

236 ↛ 237line 236 didn't jump to line 237, because the condition on line 236 was never trueif __name__ == "__main__": 

lsst.utils.tests.init() 

unittest.main()