diff options
Diffstat (limited to 'src/silx/math/fit/test/test_bgtheories.py')
-rw-r--r-- | src/silx/math/fit/test/test_bgtheories.py | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/src/silx/math/fit/test/test_bgtheories.py b/src/silx/math/fit/test/test_bgtheories.py new file mode 100644 index 0000000..6620d38 --- /dev/null +++ b/src/silx/math/fit/test/test_bgtheories.py @@ -0,0 +1,154 @@ +# coding: utf-8 +# /*########################################################################## +# Copyright (C) 2016 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################*/ +import copy +import unittest +import numpy +import random + +from silx.math.fit import bgtheories +from silx.math.fit.functions import sum_gauss + + +class TestBgTheories(unittest.TestCase): + """ + """ + def setUp(self): + self.x = numpy.arange(100) + self.y = 10 + 0.05 * self.x + sum_gauss(self.x, 10., 45., 15.) + # add a very narrow high amplitude peak to test strip and snip + self.y += sum_gauss(self.x, 100., 75., 2.) + self.narrow_peak_index = list(self.x).index(75) + random.seed() + + def tearDown(self): + pass + + def testTheoriesAttrs(self): + for theory_name in bgtheories.THEORY: + self.assertIsInstance(theory_name, str) + self.assertTrue(hasattr(bgtheories.THEORY[theory_name], + "function")) + self.assertTrue(hasattr(bgtheories.THEORY[theory_name].function, + "__call__")) + # Ensure legacy functions are not renamed accidentally + self.assertTrue( + {"No Background", "Constant", "Linear", "Strip", "Snip"}.issubset( + set(bgtheories.THEORY))) + + def testNoBg(self): + nobgfun = bgtheories.THEORY["No Background"].function + self.assertTrue(numpy.array_equal(nobgfun(self.x, self.y), + numpy.zeros_like(self.x))) + # default estimate + self.assertEqual(bgtheories.THEORY["No Background"].estimate(self.x, self.y), + ([], [])) + + def testConstant(self): + consfun = bgtheories.THEORY["Constant"].function + c = random.random() * 100 + self.assertTrue(numpy.array_equal(consfun(self.x, self.y, c), + c * numpy.ones_like(self.x))) + # default estimate + esti_par, cons = bgtheories.THEORY["Constant"].estimate(self.x, self.y) + self.assertEqual(cons, + [[0, 0, 0]]) + self.assertAlmostEqual(esti_par, + min(self.y)) + + def testLinear(self): + linfun = bgtheories.THEORY["Linear"].function + a = random.random() * 100 + b = random.random() * 100 + self.assertTrue(numpy.array_equal(linfun(self.x, self.y, a, b), + a + b * self.x)) + # default estimate + esti_par, cons = bgtheories.THEORY["Linear"].estimate(self.x, self.y) + + self.assertEqual(cons, + [[0, 0, 0], [0, 0, 0]]) + self.assertAlmostEqual(esti_par[0], 10, places=3) + self.assertAlmostEqual(esti_par[1], 0.05, places=3) + + def testStrip(self): + stripfun = bgtheories.THEORY["Strip"].function + anchors = sorted(random.sample(list(self.x), 4)) + anchors_indices = [list(self.x).index(a) for a in anchors] + + # we really want to strip away the narrow peak + anchors_indices_copy = copy.deepcopy(anchors_indices) + for idx in anchors_indices_copy: + if abs(idx - self.narrow_peak_index) < 5: + anchors_indices.remove(idx) + anchors.remove(self.x[idx]) + + width = 2 + niter = 1000 + bgtheories.THEORY["Strip"].configure(AnchorsList=anchors, AnchorsFlag=True) + + bg = stripfun(self.x, self.y, width, niter) + + # assert peak amplitude has been decreased + self.assertLess(bg[self.narrow_peak_index], + self.y[self.narrow_peak_index]) + + # default estimate + for i in anchors_indices: + self.assertEqual(bg[i], self.y[i]) + + # estimated parameters are equal to the default ones in the config dict + bgtheories.THEORY["Strip"].configure(StripWidth=7, StripIterations=8) + esti_par, cons = bgtheories.THEORY["Strip"].estimate(self.x, self.y) + self.assertTrue(numpy.array_equal(cons, [[3, 0, 0], [3, 0, 0]])) + self.assertEqual(esti_par, [7, 8]) + + def testSnip(self): + snipfun = bgtheories.THEORY["Snip"].function + anchors = sorted(random.sample(list(self.x), 4)) + anchors_indices = [list(self.x).index(a) for a in anchors] + + # we want to strip away the narrow peak, so remove nearby anchors + anchors_indices_copy = copy.deepcopy(anchors_indices) + for idx in anchors_indices_copy: + if abs(idx - self.narrow_peak_index) < 5: + anchors_indices.remove(idx) + anchors.remove(self.x[idx]) + + width = 16 + bgtheories.THEORY["Snip"].configure(AnchorsList=anchors, AnchorsFlag=True) + bg = snipfun(self.x, self.y, width) + + # assert peak amplitude has been decreased + self.assertLess(bg[self.narrow_peak_index], + self.y[self.narrow_peak_index], + "Snip didn't decrease the peak amplitude.") + + # anchored data must remain fixed + for i in anchors_indices: + self.assertEqual(bg[i], self.y[i]) + + # estimated parameters are equal to the default ones in the config dict + bgtheories.THEORY["Snip"].configure(SnipWidth=7) + esti_par, cons = bgtheories.THEORY["Snip"].estimate(self.x, self.y) + self.assertTrue(numpy.array_equal(cons, [[3, 0, 0]])) + self.assertEqual(esti_par, [7]) |