def testCast(self): for instance in (afwMath.Chebyshev1Function1F(2), afwMath.GaussianFunction1F(1.0), afwMath.LanczosFunction1F(3), afwMath.NullFunction1F(), afwMath.PolynomialFunction1F(2)): Class = type(instance) base = instance.clone() self.assertEqual(type(base), afwMath.Function1F) derived = Class.cast(base) self.assertEqual(type(derived), Class) for instance in (afwMath.Chebyshev1Function1D(2), afwMath.GaussianFunction1D(1.0), afwMath.LanczosFunction1D(3), afwMath.NullFunction1D(), afwMath.PolynomialFunction1D(2)): Class = type(instance) base = instance.clone() self.assertEqual(type(base), afwMath.Function1D) derived = Class.cast(base) self.assertEqual(type(derived), Class) for instance in (afwMath.Chebyshev1Function2F(2), afwMath.GaussianFunction2F(1.0, 1.0), afwMath.DoubleGaussianFunction2F(1.0), afwMath.LanczosFunction2F(3), afwMath.NullFunction2F(), afwMath.PolynomialFunction2F(2)): Class = type(instance) base = instance.clone() self.assertEqual(type(base), afwMath.Function2F) derived = Class.cast(base) self.assertEqual(type(derived), Class) for instance in (afwMath.Chebyshev1Function2D(2), afwMath.GaussianFunction2D(1.0, 1.0), afwMath.DoubleGaussianFunction2D(1.0), afwMath.LanczosFunction2D(3), afwMath.NullFunction2D(), afwMath.PolynomialFunction2D(2)): Class = type(instance) base = instance.clone() self.assertEqual(type(base), afwMath.Function2D) derived = Class.cast(base) self.assertEqual(type(derived), Class)
def testChebyshev1Function2DTruncate(self): """A test for Chebyshev1Function2D.truncate""" maxOrder = 6 deltaParam = 0.3 ranges = ((-1, 1), (-17, -2), (-65.3, 2.132)) xRangeIter = itertools.cycle(ranges) yRangeIter = itertools.cycle(ranges) yRangeIter.next() # make x and y ranges off from each other nPoints = 7 # number of points in x and y at which to test the functions for order in range(maxOrder + 1): xMin, xMax = xRangeIter.next() xMean = (xMin + xMax) / 2.0 xDelta = (xMax - xMin) / float(nPoints - 1) yMin, yMax = yRangeIter.next() yMean = (yMin + yMax) / 2.0 yDelta = (yMax - yMin) / float(nPoints - 1) xyRange = afwGeom.Box2D(afwGeom.Point2D(xMin, yMin), afwGeom.Point2D(xMax, yMax)) fullNParams = afwMath.Chebyshev1Function2D.nParametersFromOrder( order) fullParams = nrange(fullNParams, deltaParam, deltaParam) fullPoly = afwMath.Chebyshev1Function2D(fullParams, xyRange) for tooBigTruncOrder in range(order + 1, order + 3): self.assertRaises(Exception, fullPoly.truncate, tooBigTruncOrder) for truncOrder in range(order + 1): truncNParams = fullPoly.nParametersFromOrder(truncOrder) f = fullPoly.truncate(truncOrder) self.assertEqual(f.getNParameters(), truncNParams) g = afwMath.Chebyshev1Function2D(fullParams[0:truncNParams], xyRange) self.assertEqual(f.getNParameters(), g.getNParameters()) self.assertEqual(f.getOrder(), truncOrder) self.assertEqual(f.getXYRange(), xyRange) self.assertEqual(g.getOrder(), truncOrder) self.assertEqual(g.getXYRange(), xyRange) minXNorm = None maxXNorm = None for x in numpy.arange(xMin, xMax + xDelta / 2.0, xDelta): xNorm = 2.0 * (x - xMean) / float(xMax - xMin) if minXNorm == None or xNorm < minXNorm: minXNorm = xNorm if maxXNorm == None or xNorm > maxXNorm: maxXNorm = xNorm minYNorm = None maxYNorm = None for y in numpy.arange(yMin, yMax + yDelta / 2.0, yDelta): yNorm = 2.0 * (y - yMean) / float(yMax - yMin) if minYNorm == None or yNorm < minYNorm: minYNorm = yNorm if maxYNorm == None or yNorm > maxYNorm: maxYNorm = yNorm if not numpy.allclose(f(x, y), g(x, y)): self.fail( "%s = %s != %s = %s for x=%s, xMin=%s, xMax=%s, xNorm=%s, yMin=%s, yMax=%s, yNorm=%s, params=%s; order constructor" % \ (type(f).__name__, f(x, y), g(x, y), type(g).__name__, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params)) if not numpy.allclose((minYNorm, maxYNorm), (-1.0, 1.0)): raise RuntimeError( "Invalid y normalization: yMin=%s, yMax=%s, min/max yNorm=(%s, %s) != (-1, 1)" % (yMin, yMax, minYNorm, maxYNorm)) if not numpy.allclose((minXNorm, maxXNorm), (-1.0, 1.0)): raise RuntimeError( "Invalid x normalization: xMin=%s, xMax=%s, min/max xNorm=(%s, %s) != (-1, 1)" % (xMin, xMax, minXNorm, maxXNorm))
def testChebyshev1Function2D(self): """A test for Chebyshev1Function2D""" maxOrder = 6 deltaParam = 0.3 ranges = ((-1, 1), (-1, 0), (0, 1), (-17, -2), (-65.3, 2.132)) xRangeIter = itertools.cycle(ranges) yRangeIter = itertools.cycle(ranges) yRangeIter.next() # make x and y ranges off from each other nPoints = 7 # number of points in x and y at which to test the functions for order in range(maxOrder + 1): xMin, xMax = xRangeIter.next() xMean = (xMin + xMax) / 2.0 xDelta = (xMax - xMin) / float(nPoints - 1) yMin, yMax = yRangeIter.next() yMean = (yMin + yMax) / 2.0 yDelta = (yMax - yMin) / float(nPoints - 1) xyRange = afwGeom.Box2D(afwGeom.Point2D(xMin, yMin), afwGeom.Point2D(xMax, yMax)) f = afwMath.Chebyshev1Function2D(order, xyRange) numParams = f.getNParameters() params = nrange(numParams, deltaParam, deltaParam) f.setParameters(params) g = afwMath.Chebyshev1Function2D(params, xyRange) h = f.clone() self.assertEqual(f.getNParameters(), g.getNParameters()) self.assertEqual(f.getNParameters(), h.getNParameters()) self.assertEqual(f.getXYRange(), xyRange) self.assertEqual(f.getOrder(), order) self.assertEqual(g.getXYRange(), xyRange) self.assertEqual(g.getOrder(), order) # self.assertEqual(h.getXYRange(), xyRange) # self.assertEqual(h.getOrder(), order) # vary x in the inner loop to exercise the caching minYNorm = None maxYNorm = None for y in numpy.arange(yMin, yMax + yDelta / 2.0, yDelta): yNorm = 2.0 * (y - yMean) / float(yMax - yMin) if minYNorm == None or yNorm < minYNorm: minYNorm = yNorm if maxYNorm == None or yNorm > maxYNorm: maxYNorm = yNorm minXNorm = None maxXNorm = None for x in numpy.arange(xMin, xMax + xDelta / 2.0, xDelta): xNorm = 2.0 * (x - xMean) / float(xMax - xMin) if minXNorm == None or xNorm < minXNorm: minXNorm = xNorm if maxXNorm == None or xNorm > maxXNorm: maxXNorm = xNorm predVal = referenceChebyshev1Polynomial2( xNorm, yNorm, params) if not numpy.allclose(predVal, f(x, y)): self.fail( "%s = %s != %s for x=%s, xMin=%s, xMax=%s, xNorm=%s, yMin=%s, yMax=%s, yNorm=%s, params=%s; order constructor" % \ (type(f).__name__, f(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params)) if not numpy.allclose(predVal, g(x, y)): self.fail( "%s = %s != %s for x=%s, xMin=%s, xMax=%s, xNorm=%s, yMin=%s, yMax=%s, yNorm=%s, params=%s; params constructor" % \ (type(g).__name__, g(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params)) if not numpy.allclose(predVal, h(x, y)): self.fail( "%s = %s != %s for x=%s, xMin=%s, xMax=%s, xNorm=%s, yMin=%s, yMax=%s, yNorm=%s, params=%s; clone" % \ (type(h).__name__, h(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params)) if not numpy.allclose((minXNorm, maxXNorm), (-1.0, 1.0)): raise RuntimeError( "Invalid x normalization: xMin=%s, xMax=%s, min/max xNorm=(%s, %s) != (-1, 1)" % (xMin, xMax, minXNorm, maxXNorm)) if not numpy.allclose((minYNorm, maxYNorm), (-1.0, 1.0)): raise RuntimeError( "Invalid y normalization: yMin=%s, yMax=%s, min/max yNorm=(%s, %s) != (-1, 1)" % (yMin, yMax, minYNorm, maxYNorm)) # test that the number of parameters is correct for the given order def numParamsFromOrder(order): return (order + 1) * (order + 2) / 2 MaxOrder = 13 for order in range(MaxOrder + 1): f = afwMath.Chebyshev1Function2D(order) predNParams = numParamsFromOrder(order) self.assertEqual(f.getNParameters(), predNParams) afwMath.Chebyshev1Function2D(numpy.zeros(predNParams, dtype=float)) # test that the wrong number of parameters raises an exception validNumParams = set() for order in range(MaxOrder + 1): validNumParams.add(numParamsFromOrder(order)) for numParams in range(numParamsFromOrder(MaxOrder)): if numParams in validNumParams: continue self.assertRaises(Exception, afwMath.Chebyshev1Function2D, numpy.zeros(numParams, dtype=float)) # test that changing parameters clears the cache # for simplicity use the xyRange that requires no normalization order = 3 numParams = numParamsFromOrder(order) f = afwMath.Chebyshev1Function2D(order) xyRange = afwGeom.Box2D(afwGeom.Point2D(-1.0, -1.0), afwGeom.Point2D(1.0, 1.0)) x = 0.5 y = -0.24 for addValue in (0.0, 0.2): params = nrange(numParams, deltaParam + addValue, deltaParam) f.setParameters(params) predVal = referenceChebyshev1Polynomial2(x, y, params) if not numpy.allclose(predVal, f(x, y)): self.fail("%s = %s != %s for x=%s, y=%s, params=%s" % \ (type(f).__name__, f(x, y), predVal, x, y, params))
def testChebyshev1Function2DTruncate(self): errMsg = ( "{} != {} = {} for x={}, xMin={}, xMax={}, xNorm={}," " yMin={}, yMax={}, yNorm={}, truncParams={}; order constructor") maxOrder = 6 deltaParam = 0.3 ranges = ((-1, 1), (-17, -2), (-65.3, 2.132)) xRangeIter = itertools.cycle(ranges) yRangeIter = itertools.cycle(ranges) next(yRangeIter) # make x and y ranges off from each other nPoints = 7 # number of points in x and y at which to test the functions for order in range(maxOrder + 1): xMin, xMax = next(xRangeIter) xMean = (xMin + xMax) / 2.0 xDelta = (xMax - xMin) / float(nPoints - 1) yMin, yMax = next(yRangeIter) yMean = (yMin + yMax) / 2.0 yDelta = (yMax - yMin) / float(nPoints - 1) xyRange = lsst.geom.Box2D(lsst.geom.Point2D(xMin, yMin), lsst.geom.Point2D(xMax, yMax)) fullNParams = afwMath.Chebyshev1Function2D.nParametersFromOrder( order) fullParams = nrange(fullNParams, deltaParam, deltaParam) fullPoly = afwMath.Chebyshev1Function2D(fullParams, xyRange) for tooBigTruncOrder in range(order + 1, order + 3): with self.assertRaises(pexExceptions.InvalidParameterError): fullPoly.truncate(tooBigTruncOrder) for truncOrder in range(order + 1): truncNParams = fullPoly.nParametersFromOrder(truncOrder) truncParams = fullParams[0:truncNParams] f = fullPoly.truncate(truncOrder) self.assertEqual(f.getNParameters(), truncNParams) g = afwMath.Chebyshev1Function2D(fullParams[0:truncNParams], xyRange) self.assertEqual(f.getNParameters(), g.getNParameters()) self.assertEqual(f.getOrder(), truncOrder) self.assertEqual(f.getXYRange(), xyRange) self.assertEqual(g.getOrder(), truncOrder) self.assertEqual(g.getXYRange(), xyRange) minXNorm = None maxXNorm = None for x in np.arange(xMin, xMax + xDelta / 2.0, xDelta): xNorm = 2.0 * (x - xMean) / float(xMax - xMin) if minXNorm is None or xNorm < minXNorm: minXNorm = xNorm if maxXNorm is None or xNorm > maxXNorm: maxXNorm = xNorm minYNorm = None maxYNorm = None for y in np.arange(yMin, yMax + yDelta / 2.0, yDelta): yNorm = 2.0 * (y - yMean) / float(yMax - yMin) if minYNorm is None or yNorm < minYNorm: minYNorm = yNorm if maxYNorm is None or yNorm > maxYNorm: maxYNorm = yNorm msg = errMsg.format( type(f).__name__, f(x, y), g(x, y), type(g).__name__, x, xMin, xMax, xNorm, yMin, yMax, yNorm, truncParams) self.assertFloatsAlmostEqual(f(x, y), g(x, y), msg=msg) msg = self.normErr.format("y", yMin, yMax, minYNorm, maxYNorm) self.assertFloatsAlmostEqual(minYNorm, -1.0, msg=msg, atol=self.atol, rtol=None) self.assertFloatsAlmostEqual(maxYNorm, 1.0, msg=msg, atol=self.atol, rtol=None) msg = self.normErr.format("x", xMin, xMax, minXNorm, maxXNorm) self.assertFloatsAlmostEqual(minXNorm, -1.0, msg=msg, atol=self.atol, rtol=None) self.assertFloatsAlmostEqual(maxXNorm, 1.0, msg=msg, atol=self.atol, rtol=None)
def testChebyshev1Function2D(self): errMsg = ("{}: {} != {} for x={}, xMin={}, xMax={}, xNorm={}, " "yMin={}, yMax={}, yNorm={}, params={}; {}") maxOrder = 6 deltaParam = 0.3 ranges = ((-1, 1), (-1, 0), (0, 1), (-17, -2), (-65.3, 2.132)) xRangeIter = itertools.cycle(ranges) yRangeIter = itertools.cycle(ranges) next(yRangeIter) # make x and y ranges off from each other nPoints = 7 # number of points in x and y at which to test the functions for order in range(maxOrder + 1): xMin, xMax = next(xRangeIter) xMean = (xMin + xMax) / 2.0 xDelta = (xMax - xMin) / float(nPoints - 1) yMin, yMax = next(yRangeIter) yMean = (yMin + yMax) / 2.0 yDelta = (yMax - yMin) / float(nPoints - 1) xyRange = lsst.geom.Box2D(lsst.geom.Point2D(xMin, yMin), lsst.geom.Point2D(xMax, yMax)) f = afwMath.Chebyshev1Function2D(order, xyRange) numParams = f.getNParameters() params = nrange(numParams, deltaParam, deltaParam) f.setParameters(params) g = afwMath.Chebyshev1Function2D(params, xyRange) h = f.clone() self.assertEqual(f.getNParameters(), g.getNParameters()) self.assertEqual(f.getNParameters(), h.getNParameters()) self.assertEqual(f.getXYRange(), xyRange) self.assertEqual(f.getOrder(), order) self.assertEqual(g.getXYRange(), xyRange) self.assertEqual(g.getOrder(), order) # vary x in the inner loop to exercise the caching minYNorm = None maxYNorm = None for y in np.arange(yMin, yMax + yDelta / 2.0, yDelta): yNorm = 2.0 * (y - yMean) / float(yMax - yMin) if minYNorm is None or yNorm < minYNorm: minYNorm = yNorm if maxYNorm is None or yNorm > maxYNorm: maxYNorm = yNorm minXNorm = None maxXNorm = None for x in np.arange(xMin, xMax + xDelta / 2.0, xDelta): xNorm = 2.0 * (x - xMean) / float(xMax - xMin) if minXNorm is None or xNorm < minXNorm: minXNorm = xNorm if maxXNorm is None or xNorm > maxXNorm: maxXNorm = xNorm predVal = referenceChebyshev1Polynomial2( xNorm, yNorm, params) msg = errMsg.format( type(f).__name__, f(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params, "order constructor") self.assertFloatsAlmostEqual(f(x, y), predVal, msg=msg, atol=self.atol, rtol=None) msg = errMsg.format( type(g).__name__, g(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params, "params constructor") self.assertFloatsAlmostEqual(g(x, y), predVal, msg=msg, atol=self.atol, rtol=None) msg = errMsg.format( type(h).__name__, h(x, y), predVal, x, xMin, xMax, xNorm, yMin, yMax, yNorm, params, "order") self.assertFloatsAlmostEqual(h(x, y), predVal, msg=msg, atol=self.atol, rtol=None) msg = self.normErr.format("x", xMin, xMax, minXNorm, maxXNorm) self.assertFloatsAlmostEqual(minXNorm, -1., msg=msg, atol=self.atol) self.assertFloatsAlmostEqual(maxXNorm, 1., msg=msg, atol=self.atol) msg = self.normErr.format("y", yMin, yMax, minYNorm, maxYNorm) self.assertFloatsAlmostEqual(minYNorm, -1., msg=msg, atol=self.atol) self.assertFloatsAlmostEqual(maxYNorm, 1., msg=msg, atol=self.atol) # test that the number of parameters is correct for the given order def numParamsFromOrder(order): return (order + 1) * (order + 2) // 2 MaxOrder = 13 for order in range(MaxOrder + 1): f = afwMath.Chebyshev1Function2D(order) predNParams = numParamsFromOrder(order) self.assertEqual(f.getNParameters(), predNParams) afwMath.Chebyshev1Function2D(np.zeros(predNParams, dtype=float)) # test that the wrong number of parameters raises an exception validNumParams = set() for order in range(MaxOrder + 1): validNumParams.add(numParamsFromOrder(order)) for numParams in range(numParamsFromOrder(MaxOrder)): if numParams in validNumParams: continue with self.assertRaises(pexExceptions.InvalidParameterError): afwMath.Chebyshev1Function2D(np.zeros(numParams, dtype=float)) # test that changing parameters clears the cache # for simplicity use the xyRange that requires no normalization order = 3 numParams = numParamsFromOrder(order) f = afwMath.Chebyshev1Function2D(order) xyRange = lsst.geom.Box2D(lsst.geom.Point2D(-1.0, -1.0), lsst.geom.Point2D(1.0, 1.0)) x = 0.5 y = -0.24 for addValue in (0.0, 0.2): params = nrange(numParams, deltaParam + addValue, deltaParam) f.setParameters(params) predVal = referenceChebyshev1Polynomial2(x, y, params) msg = f"{f(x, y)} != {predVal} for x={x}, y={y}, params={params}" self.assertFloatsAlmostEqual(f(x, y), predVal, msg=msg, atol=self.atol, rtol=None)
def test_background_subtraction(self): """Check that we can recover the background, and that it is subtracted correctly in the difference image. """ noiseLevel = 1. xSize = 512 ySize = 512 x0 = 123 y0 = 456 template, _ = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=7, templateBorderSize=20, xSize=xSize, ySize=ySize, x0=x0, y0=y0, doApplyCalibration=True) params = [2.2, 2.1, 2.0, 1.2, 1.1, 1.0] bbox2D = lsst.geom.Box2D(lsst.geom.Point2D(x0, y0), lsst.geom.Extent2D(xSize, ySize)) background_model = afwMath.Chebyshev1Function2D(params, bbox2D) science, sources = makeTestImage(psfSize=2.0, noiseLevel=noiseLevel, noiseSeed=6, background=background_model, xSize=xSize, ySize=ySize, x0=x0, y0=y0) config = subtractImages.AlardLuptonSubtractTask.ConfigClass() config.doSubtractBackground = True config.makeKernel.kernel.name = "AL" config.makeKernel.kernel.active.fitForBackground = True config.makeKernel.kernel.active.spatialKernelOrder = 1 config.makeKernel.kernel.active.spatialBgOrder = 2 statsCtrl = _makeStats() def _run_and_check_images(config, statsCtrl, mode): """Check that the fit background matches the input model. """ config.mode = mode task = subtractImages.AlardLuptonSubtractTask(config=config) output = task.run(template.clone(), science.clone(), sources) # We should be fitting the same number of parameters as were in the input model self.assertEqual(output.backgroundModel.getNParameters(), background_model.getNParameters()) # The parameters of the background fit should be close to the input model self.assertFloatsAlmostEqual(np.array( output.backgroundModel.getParameters()), np.array(params), rtol=0.3) # stddev of difference image should be close to expected value. # This will fail if we have mis-subtracted the background. stdVal = _computeRobustStatistics(output.difference.image, output.difference.mask, statsCtrl, statistic=afwMath.STDEV) self.assertFloatsAlmostEqual(stdVal, np.sqrt(2) * noiseLevel, rtol=0.1) _run_and_check_images(config, statsCtrl, "convolveTemplate") _run_and_check_images(config, statsCtrl, "convolveScience")