Esempio n. 1
0
 def test_except_inherit(self):
     """Don't re-use an inherited parameter"""
     src1 = Source([])
     src1.set_expression('a')
     src2 = Source([], shapeof=src1)
     self.assertRaises(ValueError, src2.set_expression, 'a')
     self.assertRaises(ValueError, src2.set_expression, 'a', ['a'], ['1'])
     self.assertRaises(ValueError, src2.set_expression, 'a*b', ['a', 'b'], ['b', 'a'])
Esempio n. 2
0
 def test_except_inherit(self):
     """Don't re-use an inherited parameter"""
     src1 = Source([])
     src1.set_expression('a')
     src2 = Source([], shapeof=src1)
     self.assertRaises(ValueError, src2.set_expression, 'a')
     self.assertRaises(ValueError, src2.set_expression, 'a', ['a'], ['1'])
     self.assertRaises(ValueError, src2.set_expression, 'a*b', ['a', 'b'],
                       ['b', 'a'])
Esempio n. 3
0
    def test_zero(self):
        builder = SpecBuilder('SpectrumZero')

        sig = [10., 11.]
        src_sig = Source(sig)
        src_sig.use_stats(.5 * (2 * np.array(sig))**0.5)
        src_sig.set_expression('lumi*xsec_sig', ['lumi', 'xsec_sig'],
                               ['xsec_sig', 'lumi'])
        builder.add_source(src_sig)
        builder.set_prior('xsec_sig', 1, 0.9, 1.2, 'normal')
        builder.set_prior('lumi', 1, 0.95, 1.05, 'lognormal')

        sig_syst1 = [-5, 0]
        src_sig_syst1_up = Source(sig_syst1, shapeof=src_sig)
        src_sig_syst1_up.set_expression('syst1', polarity='up')
        builder.add_source(src_sig_syst1_up)
        builder.set_prior('syst1', 0, -1, 1, 'normal')

        spec = builder.build()

        pars = list(spec.central)
        data = spec(pars)
        isyst = spec.ipar('syst1')

        pars[isyst] = 2
        # ensure syst made bin go to zero
        self.assertAlmostEqual(spec(pars)[0], 0)
        # ensure not NaN (0 data so bin is ignored)
        self.assertTrue(spec.ll(pars) == spec.ll(pars))

        # try again with negative bin value
        pars[isyst] = 3
        self.assertAlmostEqual(spec(pars)[0], -5)
        self.assertTrue(spec.ll(pars) == spec.ll(pars))

        # now set the data and check that ll goes to -inf
        spec.set_data(data)
        # check also grads, so need memory arrays
        pars = np.array(pars, dtype=np.float64)
        grads = pars * 0

        pars[isyst] = 2
        self.assertEqual(spec.ll(pars), float('-inf'))
        spec._obj.Gradient(pars, grads)
        self.assertEqual(grads[isyst], float('inf'))

        pars[isyst] = 3
        self.assertEqual(spec.ll(pars), float('-inf'))
        spec._obj.Gradient(pars, grads)
        self.assertEqual(grads[isyst], float('inf'))
Esempio n. 4
0
 def test_except_polarity(self):
     """Reject bad polarity values"""
     src = Source([])
     self.assertRaises(ValueError,
                       src.set_expression,
                       'a',
                       polarity='invalid')
Esempio n. 5
0
 def test_expression(self):
     """Set an expression, parameters and gradients"""
     src = Source([])
     src.set_expression('a*b*b', ['a', 'b'], ['b*b', '2*a*b'])
     self.assertEqual(['a', 'b'], src._pars)
     self.assertEqual(['b*b', '2*a*b'], src._grads)
     # Should convert numerical gradients
     src = Source([])
     src.set_expression('a', ['a'], [1])
     self.assertEqual(['1'], src._grads)
Esempio n. 6
0
 def test_except_infer_pars(self):
     """Try to infer bad expression"""
     src = Source([])
     self.assertRaises(RuntimeError, src.set_expression, 'a+a')
     self.assertRaises(RuntimeError, src.set_expression, '2*a')
     self.assertRaises(ValueError, src.set_expression, '2*a', ['a'])
     self.assertRaises(ValueError, src.set_expression, '2*a', grads=['2'])
     self.assertRaises(ValueError, src.set_expression, 'a*b', ['a', 'b'],
                       ['b'])
Esempio n. 7
0
    def test_infer(self):
        """Infer parameters and gradients from expression"""
        src = Source([])
        src.set_expression('a')
        self.assertEqual(['a'], src._pars)
        self.assertEqual(['1'], src._grads)

        src = Source([])
        src.set_expression('a+b')
        self.assertEqual(['a', 'b'], src._pars)
        self.assertEqual(['1', '1'], src._grads)
Esempio n. 8
0
 def test_inherit(self):
     """Test inheriting from parent sources"""
     # Setup a source which inherits from two others
     src1 = Source([])
     src1.set_expression('a+b')
     src2 = Source([], shapeof=src1)
     src2.set_expression('5*c*c', ['c'], ['10*c'])
     src3 = Source([], shapeof=src2)
     src3.set_expression('d+e')
     # Check the correct compound expression
     self.assertEqual('((a+b) * (5*c*c)) * (d+e)', src3._expr)
     # Ensure paramters correctly ammended
     self.assertEqual(['a', 'b', 'c', 'd', 'e'], src3._pars)
     # Check that the gradients are correctly propagated
     self.assertEqual('((1) * (5*c*c)) * (d+e)', src3._grads[0])
     self.assertEqual('((1) * (5*c*c)) * (d+e)', src3._grads[1])
     self.assertEqual('((10*c) * (a+b)) * (d+e)', src3._grads[2])
     self.assertEqual('(1) * ((a+b) * (5*c*c))', src3._grads[3])
     self.assertEqual('(1) * ((a+b) * (5*c*c))', src3._grads[4])
Esempio n. 9
0
 def test_expression(self):
     """Set an expression, parameters and gradients"""
     src = Source([])
     src.set_expression('a*b*b', ['a', 'b'], ['b*b', '2*a*b'])
     self.assertEqual(['a', 'b'], src._pars)
     self.assertEqual(['b*b', '2*a*b'], src._grads)
     # Should convert numerical gradients
     src = Source([])
     src.set_expression('a', ['a'], [1])
     self.assertEqual(['1'], src._grads)
Esempio n. 10
0
    def test_infer(self):
        """Infer parameters and gradients from expression"""
        src = Source([])
        src.set_expression('a')
        self.assertEqual(['a'], src._pars)
        self.assertEqual(['1'], src._grads)

        src = Source([])
        src.set_expression('a+b')
        self.assertEqual(['a', 'b'], src._pars)
        self.assertEqual(['1', '1'], src._grads)
Esempio n. 11
0
    def setUpClass(cls):
        """Setup a single spectrum object for all tests"""

        # Builder accumulates data and builds the spectrum
        builder = SpecBuilder('Spectrum')

        ### Add a signal ###

        # Add a trinagular signal
        sig = [1000., 1100., 1200., 1100., 1000.]
        src_sig = Source(sig)
        # Indicate the bin contents in sig are subject to statistical
        # uncertainty, based on double the count (as if 2x MC was generated
        # then scaled down by 0.5)
        src_sig.use_stats(.5 * (2 * np.array(sig))**0.5)
        # Allow its scale to vary
        src_sig.set_expression(
            'lumi*xsec_sig',  # scale factor
            ['lumi', 'xsec_sig'],  # parameters are lumi and xsec
            ['xsec_sig', 'lumi'])  # dn/dlumi and dn/dxsec
        # Add to builder once configured
        builder.add_source(src_sig)
        # Constrain xsec with an asymmeric prior
        builder.set_prior('xsec_sig', 1, 0.9, 1.2, 'normal')
        # Constrain lumi with 5% uncertainty
        builder.set_prior('lumi', 1, 0.95, 1.05, 'lognormal')

        ### Add two systematic uncertinaties ###

        # Add systematic shape variation (a top hat)
        sig_syst1 = [0, 50, 50, 50, 0]
        # This is a shape which inherits the normalization from the signal
        src_sig_syst1_up = Source(sig_syst1, shapeof=src_sig)
        # Assume 1:1 statistical uncertainty on this shape
        src_sig_syst1_up.use_stats(np.array(sig_syst1)**0.5)
        # Control the amount of this variation with the parameter syst1, and
        # indicate that the shape applies only if syst1 >= 0. Note that
        # parameter list and gradients can be omitted for simple sums
        src_sig_syst1_up.set_expression('syst1', polarity='up')
        # Make syst1 fully asymmetric: it has the same effect on the spectrum
        # when the parameter is positive as negative
        src_sig_syst1_down = Source(sig_syst1, shapeof=src_sig)
        src_sig_syst1_down.set_expression('syst1', polarity='down')
        builder.add_source(src_sig_syst1_up)
        builder.add_source(src_sig_syst1_down)
        # 1 sigma penality when this parameter gets to values +/- 1
        builder.set_prior('syst1', 0, -1, 1, 'normal')

        # Add a linear systematic variant
        sig_syst2 = [-100, -50, 0, 50, 100]
        src_sig_syst2 = Source(sig_syst2, shapeof=src_sig)
        # This one is symmetrized: the value of syst2 simply scales
        src_sig_syst2.set_expression('syst2')
        builder.add_source(src_sig_syst2)
        builder.set_prior('syst2', 0, -1, 1, 'normal')

        ### Add a template (the parameter of interest) ###

        # Add shape to th3 signal, but won't be constrained
        sig_temp1 = [0, 0, 10, 100, 0]
        src_poi = Source(sig_temp1, shapeof=src_sig)
        # The parameter of interest is called p, and scales the template by
        # a factof of 5
        src_poi.set_expression('5*p', ['p'], ['5'])
        builder.add_source(src_poi)

        ### Add a background ###

        bg = [110, 100, 100, 100, 105]
        src_bg = Source(bg)
        src_bg.set_expression('lumi*xsec_bg', ['lumi', 'xsec_bg'],
                              ['xsec_bg', 'lumi'])
        builder.add_source(src_bg)
        builder.set_prior('xsec_bg', 1, 0.9, 1.1, 'normal')

        ### Share one of the systematics with the background ###

        bg_syst2 = [10, 20, 10, 20, 10]
        src_bg_syst2 = Source(bg_syst2, shapeof=src_bg)
        src_bg_syst2.set_expression('syst2')
        builder.add_source(src_bg_syst2)
        # Note that this parameter is already constrained

        ### Add a custom regularization for the free parameter ###

        builder.add_regularization('std::pow(p-syst1, 2)', ['p', 'syst1'],
                                   ['2*(p-syst1)', '-2*(p-syst1)'])

        # Store the builder so that tests can use it or its contents
        cls.builder = builder
        cls.spec = builder.build()
Esempio n. 12
0
 def test_except_reset(self):
     """Don't allow re-setting expression"""
     src = Source([])
     src.set_expression('a')
     self.assertRaises(RuntimeError, src.set_expression, 'a')
Esempio n. 13
0
 def test_except_par_name(self):
     """Reject bad parameter names"""
     src = Source([])
     self.assertRaises(ValueError, src.set_expression, '_a', ['_a'], ['1'])
     self.assertRaises(ValueError, src.set_expression, '1a', ['1a'], ['1'])
Esempio n. 14
0
    def setUpClass(cls):
        """Setup a single spectrum object for all tests"""

        # Builder accumulates data and builds the spectrum
        builder = SpecBuilder('Spectrum')

        ### Add a signal ###
        
        # Add a trinagular signal
        sig = [1000., 1100., 1200., 1100., 1000.]
        src_sig = Source(sig)
        # Indicate the bin contents in sig are subject to statistical
        # uncertainty, based on double the count (as if 2x MC was generated
        # then scaled down by 0.5)
        src_sig.use_stats(.5*(2*np.array(sig))**0.5)
        # Allow its scale to vary
        src_sig.set_expression(
            'lumi*xsec_sig',  # scale factor
            ['lumi', 'xsec_sig'],  # parameters are lumi and xsec
            ['xsec_sig', 'lumi'])  # dn/dlumi and dn/dxsec
        # Add to builder once configured
        builder.add_source(src_sig)
        # Constrain xsec with an asymmeric prior
        builder.set_prior('xsec_sig', 1, 0.9, 1.2, 'normal')
        # Constrain lumi with 5% uncertainty
        builder.set_prior('lumi', 1, 0.95, 1.05, 'lognormal')

        ### Add two systematic uncertinaties ###

        # Add systematic shape variation (a top hat)
        sig_syst1 = [0, 50, 50 , 50, 0]
        # This is a shape which inherits the normalization from the signal
        src_sig_syst1_up = Source(sig_syst1, shapeof=src_sig)
        # Assume 1:1 statistical uncertainty on this shape
        src_sig_syst1_up.use_stats(np.array(sig_syst1)**0.5)
        # Control the amount of this variation with the parameter syst1, and
        # indicate that the shape applies only if syst1 >= 0. Note that 
        # parameter list and gradients can be omitted for simple sums
        src_sig_syst1_up.set_expression('syst1', polarity='up')
        # Make syst1 fully asymmetric: it has the same effect on the spectrum
        # when the parameter is positive as negative
        src_sig_syst1_down = Source(sig_syst1, shapeof=src_sig)
        src_sig_syst1_down.set_expression('syst1', polarity='down')
        builder.add_source(src_sig_syst1_up)
        builder.add_source(src_sig_syst1_down)
        # 1 sigma penality when this parameter gets to values +/- 1
        builder.set_prior('syst1', 0, -1, 1, 'normal')

        # Add a linear systematic variant
        sig_syst2 = [-100, -50, 0 , 50, 100]
        src_sig_syst2 = Source(sig_syst2, shapeof=src_sig)
        # This one is symmetrized: the value of syst2 simply scales
        src_sig_syst2.set_expression('syst2')
        builder.add_source(src_sig_syst2)
        builder.set_prior('syst2', 0, -1, 1, 'normal')

        ### Add a template (the parameter of interest) ###

        # Add shape to th3 signal, but won't be constrained
        sig_temp1 = [0, 0, 10, 100, 0]
        src_poi = Source(sig_temp1, shapeof=src_sig)
        # The parameter of interest is called p, and scales the template by
        # a factof of 5
        src_poi.set_expression('5*p', ['p'], ['5'])
        builder.add_source(src_poi)

        ### Add a background ###

        bg = [110, 100, 100, 100, 105]
        src_bg = Source(bg)
        src_bg.set_expression(
            'lumi*xsec_bg',
            ['lumi', 'xsec_bg'],
            ['xsec_bg', 'lumi'])
        builder.add_source(src_bg)
        builder.set_prior('xsec_bg', 1, 0.9, 1.1, 'normal')

        ### Share one of the systematics with the background ###

        bg_syst2 = [10, 20, 10, 20, 10]
        src_bg_syst2 = Source(bg_syst2, shapeof=src_bg)
        src_bg_syst2.set_expression('syst2')
        builder.add_source(src_bg_syst2)
        # Note that this parameter is already constrained

        ### Add a custom regularization for the free parameter ###

        builder.add_regularization(
            'std::pow(p-syst1, 2)',
            ['p', 'syst1'],
            ['2*(p-syst1)', '-2*(p-syst1)'])

        # Store the builder so that tests can use it or its contents
        cls.builder = builder
        cls.spec = builder.build()
Esempio n. 15
0
 def test_inherit(self):
     """Test inheriting from parent sources"""
     # Setup a source which inherits from two others
     src1 = Source([])
     src1.set_expression('a+b')
     src2 = Source([], shapeof=src1)
     src2.set_expression('5*c*c', ['c'], ['10*c'])
     src3 = Source([], shapeof=src2)
     src3.set_expression('d+e')
     # Check the correct compound expression
     self.assertEqual('((a+b) * (5*c*c)) * (d+e)', src3._expr)
     # Ensure paramters correctly ammended
     self.assertEqual(['a', 'b', 'c', 'd', 'e'], src3._pars)
     # Check that the gradients are correctly propagated
     self.assertEqual('((1) * (5*c*c)) * (d+e)', src3._grads[0])
     self.assertEqual('((1) * (5*c*c)) * (d+e)', src3._grads[1])
     self.assertEqual('((10*c) * (a+b)) * (d+e)', src3._grads[2])
     self.assertEqual('(1) * ((a+b) * (5*c*c))', src3._grads[3])
     self.assertEqual('(1) * ((a+b) * (5*c*c))', src3._grads[4])
Esempio n. 16
0
 def test_except_reset(self):
     """Don't allow re-setting expression"""
     src = Source([])
     src.set_expression('a')
     self.assertRaises(RuntimeError, src.set_expression, 'a')
Esempio n. 17
0
 def test_data(self):
     """Data is correctly propagated"""
     src = Source([1, 2, 3])
     np.testing.assert_array_almost_equal([1, 2, 3], src._data, 15)