def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return (None, None, multiply(coefficient, u[2]), multiply(coefficient, u[3]))
def to_shape(uncertainty, nominal): """Converts an overall uncertainty to a shape uncertainty. If the uncertainty has no overall component, it is returned as-is. If the uncertainty has an overall component and a shape component, the shape component is disregarded and replaced by the newly converted shape component. Args: uncertainty: A tuple of the form (overall_up, overall_down, shape_up, shape_down) nominal: The nominal shape result Returns: A tuple of the form (None, None, shape_up, shape_down) """ # Extract overall uncertainties overall_up, overall_down, _, _ = uncertainty # If we don't have overall components, bail if overall_up is None or overall_down is None: return uncertainty # Compute the result return ( None, None, multiply(overall_up, nominal), multiply(overall_down, nominal) )
def to_shape(uncertainty, nominal): """Converts an overall uncertainty to a shape uncertainty. If the uncertainty has no overall component, it is returned as-is. If the uncertainty has an overall component and a shape component, the shape component is disregarded and replaced by the newly converted shape component. Args: uncertainty: A tuple of the form (overall_up, overall_down, shape_up, shape_down) nominal: The nominal shape result Returns: A tuple of the form (None, None, shape_up, shape_down) """ # Extract overall uncertainties overall_up, overall_down, _, _ = uncertainty # If we don't have overall components, bail if overall_up is None or overall_down is None: return uncertainty # Compute the result return (None, None, multiply(overall_up, nominal), multiply(overall_down, nominal))
def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return ( None, None, multiply(coefficient[1], u[2]), multiply(coefficient[2], u[3]) )
def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply( coefficient[0], self.calculation.calculation(process, region) ) return (None, None, n, n)
def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply(coefficient, self.calculation.calculation(process, region)) return (None, None, n, n)
def __call__(self, process, region, weighted_combination=True): """Executes the background estimation scheme. This method combines components which enter into backgruond estimation. Implementers should NOT override this method. Instead, they should return a tuple of the components entering background estimation by overriding the `components` method. This method is designed to handle the following types of calculations: - Count - Histogram - Uncertainty Args: process: The process to consider region: The region to consider weighted_combination: Whether or not to apply weights from components Returns: The combined background estimation. """ # Get components components = self.components(process, region) # Watch for empty components if len(components) == 0: raise ValueError('must have at least one component for estimation') # If we're not using weights, switch all coefficients to 1 if not weighted_combination: components = [(1.0, c[1], c[2], c[3]) for c in components] # Determine if we're dealing with an uncertainty or not is_uncertainty = isinstance(self.calculation, Uncertainty) # If we're dealing with an uncertainty calculation, then create a # calculation that will compute a faux-uncertainty ntuple with the # nominal value for components that shouldn't have the uncertainty # applied. Also have it support the coefficient. if is_uncertainty: def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply(coefficient, self.calculation.calculation(process, region)) return (None, None, n, n) # If we're dealing with an uncertainty calculation, then create a # calculation that will convert any overall systematics to shape # systematics so that they can be combined with the other components of # the estimation. Also have it support the coefficient. if is_uncertainty: def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return (None, None, multiply(coefficient, u[2]), multiply(coefficient, u[3])) # Compute the first value which we'll use as the basis of the result coefficient, use_nominal, process, region = components[0] if is_uncertainty: if use_nominal: result = nominal(coefficient, process, region) else: result = uncertainty(coefficient, process, region) else: result = multiply(coefficient, self.calculation(process, region)) # Compute the remaining values for coefficient, use_nominal, process, region in components[1:]: if is_uncertainty: if use_nominal: value = nominal(coefficient, process, region) else: value = uncertainty(coefficient, process, region) result = ( None, None, # NOTE: Coefficient already handled above add(1.0, result[2], 1.0, value[2]), add(1.0, result[3], 1.0, value[3])) else: result = add(1.0, result, coefficient, self.calculation(process, region)) # All done return result
def __call__(self, process, region, weighted_combination = True): """Executes the background estimation scheme. This method combines components which enter into backgruond estimation. Implementers should NOT override this method. Instead, they should return a tuple of the components entering background estimation by overriding the `components` method. This method is designed to handle the following types of calculations: - Count - Histogram - Uncertainty Args: process: The process to consider region: The region to consider weighted_combination: Whether or not to apply weights from components Returns: The combined background estimation. """ # Get components components = self.components(process, region) # Watch for empty components if len(components) == 0: raise ValueError('must have at least one component for estimation') # If we're not using weights, switch all coefficients to 1 if not weighted_combination: components = [(1.0, c[1], c[2], c[3]) for c in components] # Determine if we're dealing with an uncertainty or not is_uncertainty = isinstance(self.calculation, Uncertainty) # Uncertainty calculations need 3-valued coefficients, to accomodate # for up/down variations. Convert 1-valued coefficients to 3-tuples # with the same value. def to_tuple(v): return (v, v, v) if isinstance(v, float) else v components = [(to_tuple(c[0]), c[1], c[2], c[3]) for c in components] # If we're dealing with an uncertainty calculation, then create a # calculation that will compute a faux-uncertainty ntuple with the # nominal value for components that shouldn't have the uncertainty # applied. Also have it support the coefficient. if is_uncertainty: def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply( coefficient[0], self.calculation.calculation(process, region) ) return (None, None, n, n) # If we're dealing with an uncertainty calculation, then create a # calculation that will convert any overall systematics to shape # systematics so that they can be combined with the other components of # the estimation. Also have it support the coefficient. if is_uncertainty: def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return ( None, None, multiply(coefficient[1], u[2]), multiply(coefficient[2], u[3]) ) # Compute the first value which we'll use as the basis of the result coefficient, use_nominal, process, region = components[0] if is_uncertainty: if use_nominal: result = nominal(coefficient, process, region) else: result = uncertainty(coefficient, process, region) else: result = multiply(coefficient[0], self.calculation(process, region)) # Compute the remaining values for coefficient, use_nominal, process, region in components[1:]: if is_uncertainty: if use_nominal: value = nominal(coefficient, process, region) else: value = uncertainty(coefficient, process, region) result = ( None, None, # NOTE: Coefficient already handled above add(1.0, result[2], 1.0, value[2]), add(1.0, result[3], 1.0, value[3]) ) else: result = add( 1.0, result, coefficient[0], self.calculation(process, region) ) # Allow the HigherOrderCalculation to polish the result # (e.g. special treatment of overflow bin for Histogram) self.calculation.finalize_result(result) if 'estimation' in process.metadata().get('print_me', []): print('Final estimate: {:.2f}'.format(integral(result, False))) return result
def __call__(self, process, region, weighted_combination = True): """Executes the background estimation scheme. This method combines components which enter into backgruond estimation. Implementers should NOT override this method. Instead, they should return a tuple of the components entering background estimation by overriding the `components` method. This method is designed to handle the following types of calculations: - Count - Histogram - Uncertainty Args: process: The process to consider region: The region to consider weighted_combination: Whether or not to apply weights from components Returns: The combined background estimation. """ # Get components components = self.components(process, region) # Watch for empty components if len(components) == 0: raise ValueError('must have at least one component for estimation') # If we're not using weights, switch all coefficients to 1 if not weighted_combination: components = [(1.0, c[1], c[2], c[3]) for c in components] # Determine if we're dealing with an uncertainty or not is_uncertainty = isinstance(self.calculation, Uncertainty) # If we're dealing with an uncertainty calculation, then create a # calculation that will compute a faux-uncertainty ntuple with the # nominal value for components that shouldn't have the uncertainty # applied. Also have it support the coefficient. if is_uncertainty: def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply( coefficient, self.calculation.calculation(process, region) ) return (None, None, n, n) # If we're dealing with an uncertainty calculation, then create a # calculation that will convert any overall systematics to shape # systematics so that they can be combined with the other components of # the estimation. Also have it support the coefficient. if is_uncertainty: def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return ( None, None, multiply(coefficient, u[2]), multiply(coefficient, u[3]) ) # Compute the first value which we'll use as the basis of the result coefficient, use_nominal, process, region = components[0] if is_uncertainty: if use_nominal: result = nominal(coefficient, process, region) else: result = uncertainty(coefficient, process, region) else: result = multiply(coefficient, self.calculation(process, region)) # Compute the remaining values for coefficient, use_nominal, process, region in components[1:]: if is_uncertainty: if use_nominal: value = nominal(coefficient, process, region) else: value = uncertainty(coefficient, process, region) result = ( None, None, # NOTE: Coefficient already handled above add(1.0, result[2], 1.0, value[2]), add(1.0, result[3], 1.0, value[3]) ) else: result = add( 1.0, result, coefficient, self.calculation(process, region) ) # All done return result
def __call__(self, process, region, weighted_combination=True): """Executes the background estimation scheme. This method combines components which enter into backgruond estimation. Implementers should NOT override this method. Instead, they should return a tuple of the components entering background estimation by overriding the `components` method. This method is designed to handle the following types of calculations: - Count - Histogram - Uncertainty Args: process: The process to consider region: The region to consider weighted_combination: Whether or not to apply weights from components Returns: The combined background estimation. """ # Get components components = self.components(process, region) # Watch for empty components if len(components) == 0: raise ValueError('must have at least one component for estimation') # If we're not using weights, switch all coefficients to 1 if not weighted_combination: components = [(1.0, c[1], c[2], c[3]) for c in components] # Determine if we're dealing with an uncertainty or not is_uncertainty = isinstance(self.calculation, Uncertainty) # Uncertainty calculations need 3-valued coefficients, to accomodate # for up/down variations. Convert 1-valued coefficients to 3-tuples # with the same value. def to_tuple(v): return (v, v, v) if isinstance(v, float) else v components = [(to_tuple(c[0]), c[1], c[2], c[3]) for c in components] # If we're dealing with an uncertainty calculation, then create a # calculation that will compute a faux-uncertainty ntuple with the # nominal value for components that shouldn't have the uncertainty # applied. Also have it support the coefficient. if is_uncertainty: def nominal(coefficient, process, region): # self.calculation.calculation is the nominal calculation n = multiply(coefficient[0], self.calculation.calculation(process, region)) return (None, None, n, n) # If we're dealing with an uncertainty calculation, then create a # calculation that will convert any overall systematics to shape # systematics so that they can be combined with the other components of # the estimation. Also have it support the coefficient. if is_uncertainty: def uncertainty(coefficient, process, region): u = self.calculation(process, region) # self.calculation.calculation is the nominal calculation n = self.calculation.calculation(process, region) u = to_shape(u, n) return (None, None, multiply(coefficient[1], u[2]), multiply(coefficient[2], u[3])) # Compute the first value which we'll use as the basis of the result coefficient, use_nominal, process, region = components[0] if is_uncertainty: if use_nominal: result = nominal(coefficient, process, region) else: result = uncertainty(coefficient, process, region) else: result = multiply(coefficient[0], self.calculation(process, region)) # Compute the remaining values for coefficient, use_nominal, process, region in components[1:]: if is_uncertainty: if use_nominal: value = nominal(coefficient, process, region) else: value = uncertainty(coefficient, process, region) result = ( None, None, # NOTE: Coefficient already handled above add(1.0, result[2], 1.0, value[2]), add(1.0, result[3], 1.0, value[3])) else: result = add(1.0, result, coefficient[0], self.calculation(process, region)) # Allow the HigherOrderCalculation to polish the result # (e.g. special treatment of overflow bin for Histogram) self.calculation.finalize_result(result) if 'estimation' in process.metadata().get('print_me', []): print('Final estimate: {:.2f}'.format(integral(result, False))) return result