Example Notebook for DFGMark

Statistical Concepts and Definitions

Defining some concepts and vocabulary [1]:

  • model: set of probability density functions (PDFs), which describe distributions of the observables or functions of observables
  • observable: a measurable quantity in an experiment, or a function of measrued quantities
  • model parameters: any variable in your PDF expression which is not an observable
  • parameter of interest (POI) $\left(\theta\right)$: the model parameter(s) that is being studied by the questions or experiments
    • Ex.:the cross section of a signal process
  • nuisance parameter $\left(\nu\right)$: any model parameter which is not a parameter of interest
  • hypothesis: a specific model, with specified observables, POIs, nuisance parameters, and prior PDFs (in case of Bayesian inference)

Examples in HistFactory

  • model: Can be derived from a ROOT.RooStats.HistFactory.Measurement() with MakeModelAndMeasurementFast()
    • Ex:
      meas = ROOT.RooStats.HistFactory.Measurement("meas", "meas")
      # Add lots of things to the measurement
      model_workspace = ROOT.RooStats.HistFactory.MakeModelAndMeasurementFast(meas)
      
  • observable:
  • model parameters:
  • parameter of interest (POI) $\left(\theta\right)$: ROOT.Measurement().AddPOI()
  • nuisance parameter $\left(\nu\right)$: nuisance parameters parametrize the impact of systematic uncertainties. Each systematic uncertainty, $i$, is described with a nuisance parameter, $\nu_{i}$ , that continuously interpolates between the variation and nominal templates. [5]
    • Ex:
      sample = ROOT.RooStats.HistFactory.Sample("sample")
      # A sample uncertainity that affectst the entire sample
      sample.AddOverallSys("sample_uncertainty", uncertainty_down, uncertainty_up)
      # Shape uncertainity
      sample_shape = ROOT.RooStats.HistFactory.HistoSys("sample_shape")
      sample_shape.SetHistoHigh(sample_up_hist)
      sample_shape.SetHistoLow(sample_down_hist)
      sample.AddHistoSys(sample_shape)
      
  • hypothesis:

Components of a Model

Models are built by HistFactory from measurements that consist of channels which hold samples.

The measurement is the object which collects all the information that will then be turned into a model by HistFactory. It is the top level combination of the channels that form the model.

A channel is a representation of observed data. Each channel is required to have a histogram of measured data associated with it. [3] Loosely, channels can be though of as "regions" in an analysis (e.g., signal regions, control regions, validation regions).

Samples are used to describe the channel (data) they are associated with. They can represent signals and backgrounds. Each sample has a histogram describing its shape and a list of systematics describing how that shape transforms based on a number of parameters. [3]

Example: [6]

  • The plot represents a single channel with associated data (the black data points labeled "Data")
  • The samples in the channel represent the individual components of the signal and background simulation stack
  • The channel has:
    • a sample called "signal" (the stacked light blue histogram)
    • a sample called "background1" (the stacked red histogram)
    • a sample called "background2" (the stacked blue histogram)
    • systematic uncertainties assocaited to background1 and background2 (stacked shaded histograms labeled "Syst. Uncert.")

In [1]:
import numpy as np
import ROOT
ROOT.RooMsgService.instance().setGlobalKillBelow(5)
%jsroot on


Welcome to JupyROOT 6.09/03

Per Kyle's suggestion, instead of using data (which is large and needs to be stored and transferred) have information be stored in the shape of a function, and then sample the function to produce the input histograms.

Kyle suggested doing the most trivial thing possible (a line) and then moving on from there


In [2]:
# An example TF1: A line with positive slope
f1 = ROOT.TF1("f1","[0]*x + [1]", 0, 10)
f1.SetParameters(1.5, 0.5)

In [3]:
def sample_func_histogram(shape_function, n_events=1, n_bins=10, range_low=0, range_high=1, hist_name='data_hist'):
    """
    Sample a function. create an pdf from it, then histogram it
    Args:
        shape_function (TF1): The mathematical form of the function to be sampled
        n_events (int): The number of points to sample
        range_low (int, float): The lower bound on the sampling range
        range_high (int, float): The upper bound on the sampling range
        n_bins (int): The number of bins in the generated histogram
        hist_name (str): The name of the histogram object
    Returns:
        hist (TH1): The generated histogram of n_events
    """
    x = ROOT.RooRealVar('x', 'x', range_low, range_high)
    # Get the formula string of the TF1 with the parameters evaluated
    formula = str(shape_function.GetFormula().GetExpFormula("P"))
    pdf = ROOT.RooGenericPdf('mypdf', 'mypdf', formula, ROOT.RooArgList(x))
    data = pdf.generate(ROOT.RooArgSet(x), n_events)
    return super(ROOT.RooDataSet,data).createHistogram(hist_name, x, ROOT.RooFit.Binning(n_bins, range_low, range_high))

In [4]:
c = ROOT.TCanvas()
hist = sample_func_histogram(f1, 10000, 10, 0, 10)
hist.Draw()
c.Draw()



In [5]:
def generate_shape_histogram(shape_function, n_bins=10, range_low=-1, range_high=1):
    """
    Sample a function over a given range and generate a histogram
    Args:
        shape_function (TF1): The mathematical form of the function to be sampled
        range_low (int, float): The lower bound on the sampling range
        range_high (int, float): The upper bound on the sampling range
        n_bins (int): The number of bins in the generated histogram
    Returns:
        hist (TH1): The generated histogram which represents the shape of the function
    """
    hist = ROOT.TH1F("hist", "", n_bins, range_low, range_high)
    for bin in range(0, n_bins+1):
        hist.SetBinContent(bin,shape_function.Eval(hist.GetXaxis().GetBinCenter(bin)))
    return hist

In [6]:
#h1 = generate_shape_histogram(f1, 10, 0, 10)
h1 = sample_func_histogram(f1, 1000, 10, 0, 10, "sample_hist")
c1 = ROOT.TCanvas()
#f1.Draw()
c1.cd()
h1.Draw("SAME")
#f1.Draw("SAME")
c1.Draw()


Comments from Vince: Indeed building the fit model is the first (and crucial) step. It might also be nice to use this model for something. Can you generate some data from this model and maybe perform a simple fit? If you're happy with the way it performs I think it's time to start adding bins/events/channels and building out into external functions to declutter your space.

Overview of Project

Goal: The goal of the project is to be able to benchmark the speed/performance of the current implementation of HistFactory to perform a maximum likelihood fit for a given binned model generated by the user. The user can do this by using the project Python library (which will essentially just be a collection of helper functions).

The binned model is supposed to be parameterized by:

  • number of events
  • number of bins
  • number of channels
  • number of signal components (samples)
  • number of background components (samples)
  • number of parameters of interest
  • number of nuisance parameters

If this is all being made "on the fly" then all components are necessary. However, if the channels are being loaded in from ROOT files, then it would seem that the number of events and number of bins could be inferred(?).

As nuisance parameters are any model parameter which is not a parameter of interest, then if a parameter is not explicitly set as a POI (Measurement.SetPOI("parameter name")) then it is treated as a nuisance parameter.

Building a Simple Model

To make a model, a measurement and least one channel with at least one sample is required. Let's make this concrete by just creating a simple model line by line. [3]


In [7]:
# set inputs
range_low = 0
range_high = 10
n_events = 1000
n_bins = 5
frac_sig = 0.2
frac_bkg = 1 - frac_sig

# Create the measurement
meas = ROOT.RooStats.HistFactory.Measurement("meas", "meas")

meas.SetOutputFilePrefix("./results")
meas.SetPOI("SigXsecOverSM")

meas.AddConstantParam("Lumi")
meas.SetLumi(1.0)
meas.SetLumiRelErr(0.10)

meas.SetExportOnly(False)

# Create a channel

f_channel = ROOT.TF1("f_channel","[0]*x + [1] + [2]",range_low,range_high)
f_channel.SetParameters(1.5, 0.5, 4)

channel_1 = ROOT.RooStats.HistFactory.Channel("channel_1")
channel_1.SetData(sample_func_histogram(f_channel, n_events, n_bins, range_low, range_high))
##channel_1.SetStatErrorConfig(0.05, "Poisson")

# Create the signal sample and set its value
signal = ROOT.RooStats.HistFactory.Sample("signal")
signal.SetNormalizeByTheory(False)

f_sig = ROOT.TF1("f_sig","[0]*x + [1]",-5,10)
f_sig.SetParameters(1.5, 0.5)
#signal.SetHisto(generate_shape_histogram(f_sig, n_bins, range_low, range_high))
signal.SetHisto(sample_func_histogram(f_sig, frac_sig * n_events, n_bins, range_low, range_high, "sig_hist"))
###
# Add the parmaeter of interest and a systematic
# Try to make intelligent choice of upper bound
##import math
##upper_bound = max(3 * math.ceil((data_val - background_val) / signal_val), 3)
#signal.AddNormFactor("SigXsecOverSM", 1, 0, upper_bound)
###
signal.AddNormFactor("SigXsecOverSM", 1, 0, 3)
# Add overall systematic of 5%
signal.AddOverallSys("signal_uncertainty", 1.0 - 0.05, 1.0 + 0.05)
# add signal sample to channel
channel_1.AddSample(signal)

# Create the background sample and set its value
background = ROOT.RooStats.HistFactory.Sample("background")
background.SetNormalizeByTheory(False)

f_bkg = ROOT.TF1("f_bkg","[0]*x + [1]",range_low,range_high)
f_bkg.SetParameters(0, 4)
#background.SetHisto(generate_shape_histogram(f_bkg, n_bins, range_low, range_high))
background.SetHisto(sample_func_histogram(f_bkg, frac_bkg * n_events, n_bins, range_low, range_high, "bkg_hist"))
# Add overall systematic of 2%
background.AddOverallSys("background_uncertainty", 1.0 - 0.02, 1.0 + 0.02)
# add signal sample to channel
channel_1.AddSample(background)

# Add the channel to the measurement
meas.AddChannel(channel_1)


Warning in <TROOT::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).

In [8]:
# Plot a data histogram for a visual check
data_hist = sample_func_histogram(f_channel, n_events, n_bins, range_low, range_high)
c2 = ROOT.TCanvas()
data_hist.Draw()
c2.Draw()


Warning in <TROOT::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TROOT::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).

In [9]:
# If histograms for the channels are stored in ROOT files then go and collect 
# the histograms represented in the channel into the channel object [4]
#meas.CollectHistograms()

# Print the measurement tree
print('\nPrinting measurement tree:\n')
meas.PrintTree()

factory = ROOT.RooStats.HistFactory.HistoToWorkspaceFactoryFast()
print('\n\nPrinting workspace:\n')
workspace = factory.MakeCombinedModel(meas)
print('\n\nPrinting model fit information:\n')
ROOT.RooStats.HistFactory.FitModel(workspace)

workspace.SetName('HistFactoryWorkspace')
workspace.writeToFile("output/HistFactoryWorkspace_linear.root")

# build the model workspace, fit it, create output plots, and save the workspace and plots to files
#hist2workspace = ROOT.RooStats.HistFactory.HistoToWorkspaceFactoryFast(meas)
#workspace = hist2workspace.MakeCombinedModel(meas)
#workspace = hist2workspace.MakeSingleChannelModel(meas, channel_1)
#workspace = ROOT.RooStats.HistFactory.MakeModelAndMeasurementFast(meas)


Out[9]:
False
Printing measurement tree:

Measurement Name: meas	 OutputFilePrefix: ./results	 POI: SigXsecOverSM	 Lumi: 1	 LumiRelErr: 0.1	 BinLow: 0	 BinHigh: 1	 ExportOnly: 0
Constant Params:  Lumi
Channels:
	 Channel Name: channel_1	 InputFile: 
	 Data:
	 	 InputFile: 	 HistoName: data_hist__x	 HistoPath: 	 HistoAddress: 0x6aff180
	 statErrorConfig:
	 	 RelErrorThreshold: 0.05	 ConstraintType: Gaussian
	 Samples: 
	 	 Name: signal	 	 Channel: channel_1	 NormalizeByTheory: False	 StatErrorActivate: False
	 	 	 	 	 InputFile: 	 HistName: sig_hist__x	 HistoPath: 	 HistoAddress: 0x3821920
	 	 Name: background	 	 Channel: channel_1	 NormalizeByTheory: False	 StatErrorActivate: False
	 	 	 	 	 InputFile: 	 HistName: bkg_hist__x	 HistoPath: 	 HistoAddress: 0x63c2a70
	 End of Channel channel_1
End Measurement: meas


Printing workspace:



-------------------
Starting to process channel_1 channel with 1 observables
lumi str = [1,0,10]
lumi Error str = nominalLumi[1,0,2],0.1
making normFactor: SigXsecOverSM
signal_channel_1 has no variation histograms 
processing hist sig_hist__x
background_channel_1 has no variation histograms 
processing hist bkg_hist__x
-----------------------------------------
import model into workspace
RooDataSet::AsimovData[obs_x_channel_1,weight:binWeightAsimov] = 5 entries (1000 weighted)

RooWorkspace(channel_1) channel_1 workspace contents

variables
---------
(Lumi,SigXsecOverSM,alpha_background_uncertainty,alpha_signal_uncertainty,binWidth_obs_x_channel_1_0,binWidth_obs_x_channel_1_1,nom_alpha_background_uncertainty,nom_alpha_signal_uncertainty,nominalLumi,obs_x_channel_1,weightVar)

p.d.f.s
-------
RooGaussian::alpha_background_uncertaintyConstraint[ x=alpha_background_uncertainty mean=nom_alpha_background_uncertainty sigma=1 ] = 1
RooGaussian::alpha_signal_uncertaintyConstraint[ x=alpha_signal_uncertainty mean=nom_alpha_signal_uncertainty sigma=1 ] = 1
RooRealSumPdf::channel_1_model[ binWidth_obs_x_channel_1_0 * L_x_signal_channel_1_overallSyst_x_Exp + binWidth_obs_x_channel_1_1 * L_x_background_channel_1_overallSyst_x_Exp ] = 108/1000
RooGaussian::lumiConstraint[ x=Lumi mean=nominalLumi sigma=0.1 ] = 1
RooProdPdf::model_channel_1[ lumiConstraint * alpha_signal_uncertaintyConstraint * alpha_background_uncertaintyConstraint * channel_1_model(obs_x_channel_1) ] = 108

functions
--------
RooProduct::L_x_background_channel_1_overallSyst_x_Exp[ 1 * background_channel_1_overallSyst_x_Exp ] = 141
RooProduct::L_x_signal_channel_1_overallSyst_x_Exp[ 1 * signal_channel_1_overallSyst_x_Exp ] = 75
RooStats::HistFactory::FlexibleInterpVar::background_channel_1_epsilon[ paramList=(alpha_background_uncertainty) ] = 1
RooHistFunc::background_channel_1_nominal[ depList=(obs_x_channel_1) ] = 141
RooProduct::background_channel_1_overallSyst_x_Exp[ background_channel_1_nominal * background_channel_1_epsilon ] = 141
RooStats::HistFactory::FlexibleInterpVar::signal_channel_1_epsilon[ paramList=(alpha_signal_uncertainty) ] = 1
RooHistFunc::signal_channel_1_nominal[ depList=(obs_x_channel_1) ] = 75
RooProduct::signal_channel_1_overallNorm_x_sigma_epsilon[ SigXsecOverSM * signal_channel_1_epsilon ] = 1
RooProduct::signal_channel_1_overallSyst_x_Exp[ signal_channel_1_nominal * signal_channel_1_overallNorm_x_sigma_epsilon ] = 75

datasets
--------
RooDataSet::asimovData(obs_x_channel_1)
RooDataSet::obsData(obs_x_channel_1)

embedded datasets (in pdfs and functions)
-----------------------------------------
RooDataHist::signal_channel_1nominalDHist(obs_x_channel_1)
RooDataHist::background_channel_1nominalDHist(obs_x_channel_1)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
ModelConfig_Observables:(obs_x_channel_1)
coefList:(binWidth_obs_x_channel_1_0,binWidth_obs_x_channel_1_1)
constraintTerms:(lumiConstraint,alpha_signal_uncertaintyConstraint,alpha_background_uncertaintyConstraint)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
likelihoodTerms:(channel_1_model)
obsAndWeight:(weightVar,obs_x_channel_1)
observables:(obs_x_channel_1)
observablesSet:(obs_x_channel_1)
shapeList:(L_x_signal_channel_1_overallSyst_x_Exp,L_x_background_channel_1_overallSyst_x_Exp)

generic objects
---------------
RooStats::ModelConfig::ModelConfig

Setting Parameter(s) of Interest as: SigXsecOverSM 
full list of observables:
RooArgList:: = (obs_x_channel_1)


------------------
 Entering combination
-----------------------------------------
create toy data for channel_1
RooDataSet::AsimovData0[obs_x_channel_1,channelCat,weight:binWeightAsimov] = 5 entries (1000 weighted)
Merging data for channel channel_1

RooWorkspace(combined) combined contents

variables
---------
(channelCat,nom_alpha_background_uncertainty,nom_alpha_signal_uncertainty,obs_x_channel_1,weightVar)

datasets
--------
RooDataSet::asimovData(obs_x_channel_1,weightVar,channelCat)
RooDataSet::obsData(channelCat,obs_x_channel_1)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
ModelConfig_Observables:(obs_x_channel_1,weightVar,channelCat)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
observables:(obs_x_channel_1,weightVar,channelCat)



----------------
 Importing combined model
setting Lumi constant
Setting Parameter(s) of Interest as: SigXsecOverSM 


Printing model fit information:

In Fit Model
 **********
 **    1 **SET PRINT           1
 **********
 **********
 **    2 **SET NOGRAD
 **********
 PARAMETER DEFINITIONS:
    NO.   NAME         VALUE      STEP SIZE      LIMITS
     1 SigXsecOverSM   1.00000e+00  3.00000e-01    0.00000e+00  3.00000e+00
     2 alpha_background_uncertainty   0.00000e+00  1.00000e+00   -5.00000e+00  5.00000e+00
     3 alpha_signal_uncertainty   0.00000e+00  1.00000e+00   -5.00000e+00  5.00000e+00
 **********
 **    3 **SET ERR         0.5
 **********
 **********
 **    4 **SET PRINT           1
 **********
 **********
 **    5 **SET STR           1
 **********
 NOW USING STRATEGY  1: TRY TO BALANCE SPEED AGAINST RELIABILITY
 **********
 **    6 **MIGRAD        1500           1
 **********
 FIRST CALL TO USER FUNCTION AT NEW START POINT, WITH IFLAG=4.
 START MIGRAD MINIMIZATION.  STRATEGY  1.  CONVERGENCE WHEN EDM .LT. 1.00e-03
 FCN=-3616.3 FROM MIGRAD    STATUS=INITIATE        8 CALLS           9 TOTAL
                     EDM= unknown      STRATEGY= 1      NO ERROR MATRIX       
  EXT PARAMETER               CURRENT GUESS       STEP         FIRST   
  NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE 
   1  SigXsecOverSM   1.00000e+00   3.00000e-01   2.14402e-01  -4.70800e+01
   2  alpha_background_uncertainty   0.00000e+00   1.00000e+00   2.01358e-01   3.32924e+00
   3  alpha_signal_uncertainty   0.00000e+00   1.00000e+00   2.01358e-01  -8.32569e+00
                               ERR DEF= 0.5
 MIGRAD MINIMIZATION HAS CONVERGED.
 MIGRAD WILL VERIFY CONVERGENCE AND ERROR MATRIX.
 COVARIANCE MATRIX CALCULATED SUCCESSFULLY
 FCN=-3628.53 FROM MIGRAD    STATUS=CONVERGED      68 CALLS          69 TOTAL
                     EDM=2.43178e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                                   STEP         FIRST   
  NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE 
   1  SigXsecOverSM   1.72444e+00   1.77393e-01   4.12500e-03   1.33113e-03
   2  alpha_background_uncertainty  -2.21848e+00   9.46353e-01   8.39230e-03   2.49817e-04
   3  alpha_signal_uncertainty  -4.74113e-04   9.94020e-01   7.17654e-03  -1.98588e-03
                               ERR DEF= 0.5
 EXTERNAL ERROR MATRIX.    NDIM=  25    NPAR=  3    ERR DEF=0.5
  3.162e-02 -4.781e-02 -8.656e-02 
 -4.781e-02  9.092e-01  3.378e-04 
 -8.656e-02  3.378e-04  1.001e+00 
 PARAMETER  CORRELATION COEFFICIENTS  
       NO.  GLOBAL      1      2      3
        1  0.56220   1.000 -0.282 -0.486
        2  0.32256  -0.282  1.000  0.000
        3  0.50694  -0.486  0.000  1.000
 **********
 **    7 **SET ERR         0.5
 **********
 **********
 **    8 **SET PRINT           1
 **********
 **********
 **    9 **HESSE        1500
 **********
 COVARIANCE MATRIX CALCULATED SUCCESSFULLY
 FCN=-3628.53 FROM HESSE     STATUS=OK             16 CALLS          85 TOTAL
                     EDM=2.4233e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                                INTERNAL      INTERNAL  
  NO.   NAME      VALUE            ERROR       STEP SIZE       VALUE   
   1  SigXsecOverSM   1.72444e+00   1.77279e-01   1.65000e-04   1.50187e-01
   2  alpha_background_uncertainty  -2.21848e+00   9.46311e-01   3.35692e-04  -4.59718e-01
   3  alpha_signal_uncertainty  -4.74113e-04   9.93392e-01   1.43531e-03  -9.48226e-05
                               ERR DEF= 0.5
 EXTERNAL ERROR MATRIX.    NDIM=  25    NPAR=  3    ERR DEF=0.5
  3.158e-02 -4.777e-02 -8.628e-02 
 -4.777e-02  9.091e-01  6.819e-05 
 -8.628e-02  6.819e-05  1.000e+00 
 PARAMETER  CORRELATION COEFFICIENTS  
       NO.  GLOBAL      1      2      3
        1  0.56141   1.000 -0.282 -0.486
        2  0.32246  -0.282  1.000  0.000
        3  0.50601  -0.486  0.000  1.000
 **********
 **   10 **MINOS        1500           1
 **********
 FCN=-3628.53 FROM MINOS     STATUS=SUCCESSFUL     47 CALLS         132 TOTAL
                     EDM=2.4233e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                  PARABOLIC         MINOS ERRORS        
  NO.   NAME      VALUE            ERROR      NEGATIVE      POSITIVE   
   1  SigXsecOverSM   1.72444e+00   1.77279e-01  -1.72177e-01   1.83823e-01
   2  alpha_background_uncertainty  -2.21848e+00   9.46311e-01                            
   3  alpha_signal_uncertainty  -4.74113e-04   9.93392e-01                            
                               ERR DEF= 0.5
 **********
 **   11 **MINOS        1500           2
 **********
 FCN=-3628.53 FROM MINOS     STATUS=SUCCESSFUL     41 CALLS         173 TOTAL
                     EDM=2.4233e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                  PARABOLIC         MINOS ERRORS        
  NO.   NAME      VALUE            ERROR      NEGATIVE      POSITIVE   
   1  SigXsecOverSM   1.72444e+00   1.77279e-01  -1.72177e-01   1.83823e-01
   2  alpha_background_uncertainty  -2.21848e+00   9.46311e-01  -9.53920e-01   9.53060e-01
   3  alpha_signal_uncertainty  -4.74113e-04   9.93392e-01                            
                               ERR DEF= 0.5
 **********
 **   12 **MINOS        1500           3
 **********
 FCN=-3628.53 FROM MINOS     STATUS=SUCCESSFUL     42 CALLS         215 TOTAL
                     EDM=2.4233e-07    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                  PARABOLIC         MINOS ERRORS        
  NO.   NAME      VALUE            ERROR      NEGATIVE      POSITIVE   
   1  SigXsecOverSM   1.72444e+00   1.77279e-01  -1.72177e-01   1.83823e-01
   2  alpha_background_uncertainty  -2.21848e+00   9.46311e-01  -9.53920e-01   9.53060e-01
   3  alpha_signal_uncertainty  -4.74113e-04   9.93392e-01  -9.99526e-01   1.00047e+00
                               ERR DEF= 0.5

In [10]:
# simPdf and obsData are defualts used by HistFactory
model = workspace.pdf('simPdf')
data = workspace.data('obsData')
model.fitTo(data, ROOT.RooFit.Save(True))


Out[10]:
<ROOT.RooFitResult object ("fitresult_simPdf_obsData") at 0x76dfe10>
 **********
 **   13 **SET PRINT           1
 **********
 **********
 **   14 **SET NOGRAD
 **********
 PARAMETER DEFINITIONS:
    NO.   NAME         VALUE      STEP SIZE      LIMITS
     1 SigXsecOverSM   1.72444e+00  1.77279e-01    0.00000e+00  3.00000e+00
     2 alpha_background_uncertainty  -2.21848e+00  9.46311e-01   -5.00000e+00  5.00000e+00
     3 alpha_signal_uncertainty  -4.74113e-04  9.93392e-01   -5.00000e+00  5.00000e+00
 **********
 **   15 **SET ERR         0.5
 **********
 **********
 **   16 **SET PRINT           1
 **********
 **********
 **   17 **SET STR           1
 **********
 NOW USING STRATEGY  1: TRY TO BALANCE SPEED AGAINST RELIABILITY
 **********
 **   18 **MIGRAD        1500           1
 **********
 FIRST CALL TO USER FUNCTION AT NEW START POINT, WITH IFLAG=4.
 START MIGRAD MINIMIZATION.  STRATEGY  1.  CONVERGENCE WHEN EDM .LT. 1.00e-03
 FCN=-3628.53 FROM MIGRAD    STATUS=INITIATE        6 CALLS           7 TOTAL
                     EDM= unknown      STRATEGY= 1      NO ERROR MATRIX       
  EXT PARAMETER               CURRENT GUESS       STEP         FIRST   
  NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE 
   1  SigXsecOverSM   1.72444e+00   1.77279e-01   1.19838e-01   8.30236e-04
   2  alpha_background_uncertainty  -2.21848e+00   9.46311e-01   2.14053e-01   7.40865e-04
   3  alpha_signal_uncertainty  -4.74113e-04   9.93392e-01   2.00009e-01  -2.06940e-03
                               ERR DEF= 0.5
 MIGRAD MINIMIZATION HAS CONVERGED.
 MIGRAD WILL VERIFY CONVERGENCE AND ERROR MATRIX.
 COVARIANCE MATRIX CALCULATED SUCCESSFULLY
 FCN=-3628.53 FROM MIGRAD    STATUS=CONVERGED      35 CALLS          36 TOTAL
                     EDM=7.86306e-08    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                                   STEP         FIRST   
  NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE 
   1  SigXsecOverSM   1.72442e+00   1.77390e-01   4.12467e-03   1.83728e-03
   2  alpha_background_uncertainty  -2.21861e+00   9.46359e-01   8.37908e-03  -3.33682e-04
   3  alpha_signal_uncertainty  -1.66170e-04   9.94022e-01   7.17642e-03  -2.96540e-04
                               ERR DEF= 0.5
 EXTERNAL ERROR MATRIX.    NDIM=  25    NPAR=  3    ERR DEF=0.5
  3.162e-02 -4.781e-02 -8.656e-02 
 -4.781e-02  9.092e-01  3.388e-04 
 -8.656e-02  3.388e-04  1.001e+00 
 PARAMETER  CORRELATION COEFFICIENTS  
       NO.  GLOBAL      1      2      3
        1  0.56220   1.000 -0.282 -0.486
        2  0.32256  -0.282  1.000  0.000
        3  0.50693  -0.486  0.000  1.000
 **********
 **   19 **SET ERR         0.5
 **********
 **********
 **   20 **SET PRINT           1
 **********
 **********
 **   21 **HESSE        1500
 **********
 COVARIANCE MATRIX CALCULATED SUCCESSFULLY
 FCN=-3628.53 FROM HESSE     STATUS=OK             16 CALLS          52 TOTAL
                     EDM=7.85717e-08    STRATEGY= 1      ERROR MATRIX ACCURATE 
  EXT PARAMETER                                INTERNAL      INTERNAL  
  NO.   NAME      VALUE            ERROR       STEP SIZE       VALUE   
   1  SigXsecOverSM   1.72442e+00   1.77272e-01   1.64987e-04   1.50179e-01
   2  alpha_background_uncertainty  -2.21861e+00   9.46317e-01   3.35163e-04  -4.59748e-01
   3  alpha_signal_uncertainty  -1.66170e-04   9.93381e-01   2.87057e-04  -3.32340e-05
                               ERR DEF= 0.5
 EXTERNAL ERROR MATRIX.    NDIM=  25    NPAR=  3    ERR DEF=0.5
  3.158e-02 -4.777e-02 -8.627e-02 
 -4.777e-02  9.092e-01  1.633e-05 
 -8.627e-02  1.633e-05  1.000e+00 
 PARAMETER  CORRELATION COEFFICIENTS  
       NO.  GLOBAL      1      2      3
        1  0.56138   1.000 -0.282 -0.485
        2  0.32246  -0.282  1.000  0.000
        3  0.50599  -0.485  0.000  1.000

In [11]:
model.graphVizTree("model.dot")

In [12]:
%%bash
dot -Tpng -o Images/model.png model.dot
dot -Tpdf -o Images/model.pdf model.dot

Plot the workspace


In [13]:
# Don't know how to set minimum value properly for a THStack, but can see bkg with unzoom js button
infile = ROOT.TFile.Open("output/HistFactoryWorkspace_linear.root")
w = infile.Get("HistFactoryWorkspace")
mc = w.obj("ModelConfig")
data = w.data("obsData")

hist_factory_nav = ROOT.RooStats.HistFactory.HistFactoryNavigation(mc)

stack = hist_factory_nav.GetChannelStack("channel_1")
channel_data = hist_factory_nav.GetDataHist(data,"channel_1","channel_data")
signal_hist = hist_factory_nav.GetSampleHist("channel_1", "signal", "signal_hist")
background_hist = hist_factory_nav.GetSampleHist("channel_1", "background", "background_hist")

c1 = ROOT.TCanvas()
stack.Draw()
channel_data.Draw("SAME")
leg = ROOT.TLegend(0.65, 0.65, 0.9, 0.9)
for hist in stack.GetHists():
    samplename = hist.GetName().rsplit('_')[0]
    leg.AddEntry(hist, samplename,"f")
stack.SetMinimum(-5)
leg.Draw()
c1.Draw()

c2 = ROOT.TCanvas()
channel_data.Draw()
c2.Draw()

for hist in stack.GetHists():
    print(hist)
print(stack)


<ROOT.TH1F object ("background_tmp__obs_x_channel_1") at 0x781d8b0>
<ROOT.TH1F object ("signal_tmp__obs_x_channel_1") at 0x78240e0>
<ROOT.THStack object at 0x79c0e80>

In [14]:
print(stack.GetNhists())

for hist in stack.GetHists():
    print(hist)


2
<ROOT.TH1F object ("background_tmp__obs_x_channel_1") at 0x781d8b0>
<ROOT.TH1F object ("signal_tmp__obs_x_channel_1") at 0x78240e0>

Constructing a Model

Now let's do that again, but more intelligently by creating functions. As measurement, channel, and sample objects can be created independently of each other, let's build everything up by constructing the sample function

TODO


In [15]:
def make_sample(sample_name="sample", sample_histogram=None,
                uncertainty_down=None, uncertainty_up=None,
                shape_up_hist=None, shape_down_hist=None,
                has_theory_norm=False):
    """
    Construct a HistFactory.Sample()
    
    Args:
        sample_name (str): The name of the signal sample
        sample_histogram: A TH1 object that encodes the shape of the sample
        uncertainty_down (float): The downward normalisation systematic uncertainty (1.0 - uncertainty_down)
        uncertainty_up (float): The upward normalisation systematic uncertainty (1.0 + uncertainty_up)
        shape_up_hist: A TH1 object that encodes the updward sample shape uncertainty
        shape_down_hist: A TH1 object that encodes the downward sample shape uncertainty
        has_theory_norm (bool): Is the sample normalized by theory or not. Default is False.
    Returns:
        sample (HistFactory.Sample()): The constructed sample. Ex: signal sample, background sample
    """
    sample = ROOT.RooStats.HistFactory.Sample(sample_name)
    sample.SetNormalizeByTheory(has_theory_norm)
    sample.SetHisto(sample_histogram)
    #
    sample.AddNormFactor("SigXsecOverSM", 1, 0, 3)
    # Add normalisation systematic uncertainty
    if uncertainty_down and uncertainty_up:
        sample.AddOverallSys(sample_name + "_uncertainty", 1.0 - uncertainty_down, 1.0 + uncertainty_up)
    # Add signal shape systematic uncertainty
    if shape_up_hist and shape_down_hist:
        shape_systematic = ROOT.RooStats.HistFactory.HistoSys(sample_name + "_shape_sys")
        shape_systematic.SetHistoHigh(shape_up_hist)
        shape_systematic.SetHistoLow(shape_down_hist)
        sample.AddHistoSys(shape_systematic)
    return sample

In [16]:
def make_channel(channel_name="channel", channel_data=None, channel_samples=None):
    """
    Make a channel with data and associated samples
    
    Args:
        channel_name (str): The name of the channel.
            Example: If this channel represents a signal region, the name might be "SR"
        channel_data (TH1): The data histogram that describes the channel
        channel_samples (list of HistFactory.Sample()): The samples associated with the channel that describe
                                                        the model for the channel
    
    Returns:
        channel (HistFactor.Channel()): The channel object    
    """
    channel = ROOT.RooStats.HistFactory.Channel(channel_name)
    channel.SetData(channel_data)
    #channel.SetStatErrorConfig(0.05, "Poisson")
    if channel_samples:
        for sample in channel_samples:
            channel.AddSample(sample)
    return channel

In [17]:
def make_model(n_evnets, n_bins, channels, POI, workspace_name=None, workspace_save_path=None):
    """
    Args:
        n_events: The number of events
        n_bins: The numbe of bins
        channels (dictionary of HistFactory.Channel()): The channels in the model with associated samples
        #n_sig_comp: The number of signal components (automatically determined from the channels)
        #n_bkg_comp: The number of background components (automatically determined from the channels)
        POI (list of str): The parameters of interest
        #n_nuisance: The number of nuisance parameters (automatically determined from the samples)
        workspace_name (str): The name of the resulting RooStats.RooWorkspace()
        workspace_save_path (str): The path and filename (sans ".root") to save the workspace
    Returns:
        workspace (RooStats.RooWorkspace): The workspace for the measruement
        model (RooStats.RooSimultaneous): The model generated from the workspace 
    """
    measurement = ROOT.RooStats.HistFactory.Measurement("measurement", "measurement")
    for parameter in POI:
        measurement.SetPOI(parameter)
    # Set luminosity (arbitrary choices)
    measurement.SetLumi(1.0)
    measurement.SetLumiRelErr(0.02)
    measurement.AddConstantParam("Lumi")
    # Add channels to measurement
    for channel in channels:
        measurement.AddChannel(channel)
    # make the factory
    factory = ROOT.RooStats.HistFactory.HistoToWorkspaceFactoryFast()
    workspace = factory.MakeCombinedModel(measurement)
    if workspace_name:
        workspace.SetName(workspace_name)
    else:
        workspace.SetName('HistFactoryWorkspace')
    if workspace_save_path:
        workspace.writeToFile(workspace_save_path + ".root")
    model = workspace.pdf('simPdf') # 'simPdf' is the default name assigned
    return workspace, model

Now let's create the samples (1 signal and 1 background), the channel (1 signal region), and the model:


In [18]:
# See composite model notebook
f_sig = ROOT.TF1("f_sig","[0]*x + [1]", 0, 10)
f_sig.SetParameters(1.5, 0.5)

f_bkg = ROOT.TF1("f_bkg","[0]*x + [1]",range_low,range_high)
f_bkg.SetParameters(0, 4)

samples = []
samples.append(make_sample("signal", sample_func_histogram(f_sig, 20, 10, 0, 10), 0.01, 0.01))
samples.append(make_sample("background", sample_func_histogram(f_bkg, 80, 10, 0, 10), 0.01, 0.01))
print(samples)


[<ROOT.RooStats::HistFactory::Sample object ("signal") at 0x6bc2690>, <ROOT.RooStats::HistFactory::Sample object ("background") at 0x30da300>]
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).

In [19]:
f_channel = ROOT.TF1("f_channel","[0]*x + [1] + [2]", 0, 10)
f_channel.SetParameters(1.5, 0.5, 4)

channels = []
SR = make_channel(channel_name="SR", channel_data=sample_func_histogram(f_channel, 100, 10, 0, 10), channel_samples=samples)
channels.append(SR)
print(channels)


[<ROOT.RooStats::HistFactory::Channel object ("SR") at 0x4db39a0>]
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).

In [20]:
POI = ["SigXsecOverSM"]
ws, model = make_model(100, 10, channels, POI)
data = ws.data('obsData') # 'obsData' is the default name assigned



-------------------
Starting to process SR channel with 1 observables
lumi str = [1,0,10]
lumi Error str = nominalLumi[1,0,1.2],0.02
making normFactor: SigXsecOverSM
signal_SR has no variation histograms 
processing hist data_hist__x
background_SR has no variation histograms 
processing hist data_hist__x
-----------------------------------------
import model into workspace
RooDataSet::AsimovData[obs_x_SR,weight:binWeightAsimov] = 10 entries (100 weighted)

RooWorkspace(SR) SR workspace contents

variables
---------
(Lumi,SigXsecOverSM,alpha_background_uncertainty,alpha_signal_uncertainty,binWidth_obs_x_SR_0,binWidth_obs_x_SR_1,nom_alpha_background_uncertainty,nom_alpha_signal_uncertainty,nominalLumi,obs_x_SR,weightVar)

p.d.f.s
-------
RooRealSumPdf::SR_model[ binWidth_obs_x_SR_0 * L_x_signal_SR_overallSyst_x_Exp + binWidth_obs_x_SR_1 * L_x_background_SR_overallSyst_x_Exp ] = 12/100
RooGaussian::alpha_background_uncertaintyConstraint[ x=alpha_background_uncertainty mean=nom_alpha_background_uncertainty sigma=1 ] = 1
RooGaussian::alpha_signal_uncertaintyConstraint[ x=alpha_signal_uncertainty mean=nom_alpha_signal_uncertainty sigma=1 ] = 1
RooGaussian::lumiConstraint[ x=Lumi mean=nominalLumi sigma=0.02 ] = 1
RooProdPdf::model_SR[ lumiConstraint * alpha_signal_uncertaintyConstraint * alpha_background_uncertaintyConstraint * SR_model(obs_x_SR) ] = 12

functions
--------
RooProduct::L_x_background_SR_overallSyst_x_Exp[ 1 * background_SR_overallSyst_x_Exp ] = 6
RooProduct::L_x_signal_SR_overallSyst_x_Exp[ 1 * signal_SR_overallSyst_x_Exp ] = 6
RooStats::HistFactory::FlexibleInterpVar::background_SR_epsilon[ paramList=(alpha_background_uncertainty) ] = 1
RooHistFunc::background_SR_nominal[ depList=(obs_x_SR) ] = 6
RooProduct::background_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * background_SR_epsilon ] = 1
RooProduct::background_SR_overallSyst_x_Exp[ background_SR_nominal * background_SR_overallNorm_x_sigma_epsilon ] = 6
RooStats::HistFactory::FlexibleInterpVar::signal_SR_epsilon[ paramList=(alpha_signal_uncertainty) ] = 1
RooHistFunc::signal_SR_nominal[ depList=(obs_x_SR) ] = 6
RooProduct::signal_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * signal_SR_epsilon ] = 1
RooProduct::signal_SR_overallSyst_x_Exp[ signal_SR_nominal * signal_SR_overallNorm_x_sigma_epsilon ] = 6

datasets
--------
RooDataSet::asimovData(obs_x_SR)
RooDataSet::obsData(obs_x_SR)

embedded datasets (in pdfs and functions)
-----------------------------------------
RooDataHist::signal_SRnominalDHist(obs_x_SR)
RooDataHist::background_SRnominalDHist(obs_x_SR)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
ModelConfig_Observables:(obs_x_SR)
coefList:(binWidth_obs_x_SR_0,binWidth_obs_x_SR_1)
constraintTerms:(lumiConstraint,alpha_signal_uncertaintyConstraint,alpha_background_uncertaintyConstraint)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
likelihoodTerms:(SR_model)
obsAndWeight:(weightVar,obs_x_SR)
observables:(obs_x_SR)
observablesSet:(obs_x_SR)
shapeList:(L_x_signal_SR_overallSyst_x_Exp,L_x_background_SR_overallSyst_x_Exp)

generic objects
---------------
RooStats::ModelConfig::ModelConfig

Setting Parameter(s) of Interest as: SigXsecOverSM 
full list of observables:
RooArgList:: = (obs_x_SR)


------------------
 Entering combination
-----------------------------------------
create toy data for SR
RooDataSet::AsimovData0[obs_x_SR,channelCat,weight:binWeightAsimov] = 10 entries (100 weighted)
Merging data for channel SR

RooWorkspace(combined) combined contents

variables
---------
(channelCat,nom_alpha_background_uncertainty,nom_alpha_signal_uncertainty,obs_x_SR,weightVar)

datasets
--------
RooDataSet::asimovData(obs_x_SR,weightVar,channelCat)
RooDataSet::obsData(channelCat,obs_x_SR)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
ModelConfig_Observables:(obs_x_SR,weightVar,channelCat)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
observables:(obs_x_SR,weightVar,channelCat)



----------------
 Importing combined model
setting Lumi constant
Setting Parameter(s) of Interest as: SigXsecOverSM 

In [21]:
ws.Print()


RooWorkspace(HistFactoryWorkspace) combined contents

variables
---------
(Lumi,SigXsecOverSM,alpha_background_uncertainty,alpha_signal_uncertainty,binWidth_obs_x_SR_0,binWidth_obs_x_SR_1,channelCat,nom_alpha_background_uncertainty,nom_alpha_signal_uncertainty,nominalLumi,obs_x_SR,weightVar)

p.d.f.s
-------
RooRealSumPdf::SR_model[ binWidth_obs_x_SR_0 * L_x_signal_SR_overallSyst_x_Exp + binWidth_obs_x_SR_1 * L_x_background_SR_overallSyst_x_Exp ] = 12
RooGaussian::alpha_background_uncertaintyConstraint[ x=alpha_background_uncertainty mean=nom_alpha_background_uncertainty sigma=1 ] = 1
RooGaussian::alpha_signal_uncertaintyConstraint[ x=alpha_signal_uncertainty mean=nom_alpha_signal_uncertainty sigma=1 ] = 1
RooGaussian::lumiConstraint[ x=Lumi mean=nominalLumi sigma=0.02 ] = 1
RooProdPdf::model_SR[ lumiConstraint * alpha_signal_uncertaintyConstraint * alpha_background_uncertaintyConstraint * SR_model(obs_x_SR) ] = 12
RooSimultaneous::simPdf[ indexCat=channelCat SR=model_SR ] = 12

functions
--------
RooProduct::L_x_background_SR_overallSyst_x_Exp[ 1 * background_SR_overallSyst_x_Exp ] = 6
RooProduct::L_x_signal_SR_overallSyst_x_Exp[ 1 * signal_SR_overallSyst_x_Exp ] = 6
RooStats::HistFactory::FlexibleInterpVar::background_SR_epsilon[ paramList=(alpha_background_uncertainty) ] = 1
RooHistFunc::background_SR_nominal[ depList=(obs_x_SR) ] = 6
RooProduct::background_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * background_SR_epsilon ] = 1
RooProduct::background_SR_overallSyst_x_Exp[ background_SR_nominal * background_SR_overallNorm_x_sigma_epsilon ] = 6
RooStats::HistFactory::FlexibleInterpVar::signal_SR_epsilon[ paramList=(alpha_signal_uncertainty) ] = 1
RooHistFunc::signal_SR_nominal[ depList=(obs_x_SR) ] = 6
RooProduct::signal_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * signal_SR_epsilon ] = 1
RooProduct::signal_SR_overallSyst_x_Exp[ signal_SR_nominal * signal_SR_overallNorm_x_sigma_epsilon ] = 6

datasets
--------
RooDataSet::asimovData(obs_x_SR,weightVar,channelCat)
RooDataSet::obsData(channelCat,obs_x_SR)

embedded datasets (in pdfs and functions)
-----------------------------------------
RooDataHist::signal_SRnominalDHist(obs_x_SR)
RooDataHist::background_SRnominalDHist(obs_x_SR)

parameter snapshots
-------------------
NominalParamValues = (nom_alpha_signal_uncertainty=0[C],nom_alpha_background_uncertainty=0[C],weightVar=0,obs_x_SR=9.5,Lumi=1[C],nominalLumi=1[C],alpha_signal_uncertainty=0,alpha_background_uncertainty=0,SigXsecOverSM=1,binWidth_obs_x_SR_0=1[C],binWidth_obs_x_SR_1=1[C])

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
ModelConfig_NuisParams:(alpha_background_uncertainty,alpha_signal_uncertainty)
ModelConfig_Observables:(obs_x_SR,weightVar,channelCat)
ModelConfig_POI:(SigXsecOverSM)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background_uncertainty)
observables:(obs_x_SR,weightVar,channelCat)

generic objects
---------------
RooStats::ModelConfig::ModelConfig


In [22]:
model.fitTo(data, ROOT.RooFit.Save(True), ROOT.RooFit.PrintLevel(-1))
#ROOT.RooStats.HistFactory.FitModel(ws) #Using HistFactory built in funciton FitModel()


Out[22]:
<ROOT.RooFitResult object ("fitresult_simPdf_obsData") at 0x7c30700>

Generate some data from the model


In [23]:
model_config = ws.obj("ModelConfig")
##model_config.LoadSnapshot()
x = ws.var("obs_x_SR")
data = model.generate(ROOT.RooArgSet(x),10000)

In [24]:
# Get Info
hist_factory_nav = ROOT.RooStats.HistFactory.HistFactoryNavigation(model_config)
print(hist_factory_nav)

channel_data = hist_factory_nav.GetDataHist(ws.data("obsData"),"SR","channel_data")

# I don't understand the uncertainties on this histogram
c2 = ROOT.TCanvas()
channel_data.Draw()
c2.Draw()


<ROOT.RooStats::HistFactory::HistFactoryNavigation object at 0x76ef5f0>

Model with multiple Gaussians


In [25]:
def get_formula(function, range_low=0, range_high=1, hist_name='data_hist'):
    # Get the formula string of the TF1 with the parameters evaluated
    return str(function.GetFormula().GetExpFormula("P"))

In [26]:
g1 = ROOT.TF1("g1","[0]*exp(-0.5*((x-[1])/[2])**2)",-5,5)
g1.SetParameters(1.25,0,0.5)
g2 = ROOT.TF1("g2","[0]*exp(-0.5*((x-[1])/[2])**2)",-5,5)
g2.SetParameters(1,1,2)
g3 = ROOT.TF1("g3","[0]*exp(-0.5*((x-[1])/[2])**2)",-5,5)
g3.SetParameters(1,-1,2)
g4 = ROOT.TF1("g4","[0]*exp(-0.5*((x-[1])/[2])**2) + [3]*exp(-0.5*((x-[4])/[5])**2) + [6]*exp(-0.5*((x-[7])/[8])**2)", -5, 5)
g4.SetParameters(1.25,0,0.5,1,1,2,1,-1,2)

In [27]:
get_formula(g1) + " + " + get_formula(g2) + " + " + get_formula(g3)


Out[27]:
'1.25*exp(-0.5*pow(((x-0)/0.5),2)) + 1*exp(-0.5*pow(((x-1)/2),2)) + 1*exp(-0.5*pow(((x--1)/2),2))'

In [28]:
c3 = ROOT.TCanvas()
c3.cd()
g1.Draw()
g2.Draw("SAME")
g3.Draw("SAME")
g4.Draw("SAME")
c3.Draw()



In [29]:
n_events = 1000
n_bins = 10
range_low = -5
range_high = 5

samples = []
samples.append(make_sample("signal", sample_func_histogram(g1, 0.1*n_events, n_bins, range_low, range_high),
               0.01, 0.01))
samples.append(make_sample("background1", sample_func_histogram(g2, 0.45*n_events, n_bins, range_low, range_high),
               0.01, 0.01))
samples.append(make_sample("background2", sample_func_histogram(g3, 0.45*n_events, n_bins, range_low, range_high),
               0.01, 0.01))
#print(samples)

channels = []
SR = make_channel(channel_name="SR", channel_data=sample_func_histogram(g4, n_events, n_bins, range_low, range_high), channel_samples=samples)
channels.append(SR)
#print(channels)

POI = ["SigXsecOverSM"]
ws, model = make_model(n_events, n_bins, channels, POI)
data = ws.data('obsData')

model_config = ws.obj("ModelConfig")

hist_factory_nav = ROOT.RooStats.HistFactory.HistFactoryNavigation(model_config)
#print(hist_factory_nav)

stack = hist_factory_nav.GetChannelStack("SR")
channel_data = hist_factory_nav.GetDataHist(ws.data("obsData"),"SR","channel_data")
signal_hist = hist_factory_nav.GetSampleHist("SR", "signal", "signal_hist")
bkg1_hist = hist_factory_nav.GetSampleHist("SR", "background1", "bkg1_hist")
bkg2_hist = hist_factory_nav.GetSampleHist("SR", "background2", "bkg2_hist")

c1 = ROOT.TCanvas()
stack.Draw()
channel_data.Draw("SAME")
leg = ROOT.TLegend(0.65, 0.65, 0.9, 0.9)
for hist in stack.GetHists():
    samplename = hist.GetName().rsplit('_')[0]
    leg.AddEntry(hist, samplename,"f")
stack.SetMinimum(-5)
leg.Draw()
c1.Draw()

c2 = ROOT.TCanvas()
channel_data.Draw()
c2.Draw()



-------------------
Starting to process SR channel with 1 observables
lumi str = [1,0,10]
lumi Error str = nominalLumi[1,0,1.2],0.02
making normFactor: SigXsecOverSM
signal_SR has no variation histograms 
processing hist data_hist__x
background1_SR has no variation histograms 
processing hist data_hist__x
background2_SR has no variation histograms 
processing hist data_hist__x
-----------------------------------------
import model into workspace
RooDataSet::AsimovData[obs_x_SR,weight:binWeightAsimov] = 10 entries (1000 weighted)

RooWorkspace(SR) SR workspace contents

variables
---------
(Lumi,SigXsecOverSM,alpha_background1_uncertainty,alpha_background2_uncertainty,alpha_signal_uncertainty,binWidth_obs_x_SR_0,binWidth_obs_x_SR_1,binWidth_obs_x_SR_2,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty,nom_alpha_signal_uncertainty,nominalLumi,obs_x_SR,weightVar)

p.d.f.s
-------
RooRealSumPdf::SR_model[ binWidth_obs_x_SR_0 * L_x_signal_SR_overallSyst_x_Exp + binWidth_obs_x_SR_1 * L_x_background1_SR_overallSyst_x_Exp + binWidth_obs_x_SR_2 * L_x_background2_SR_overallSyst_x_Exp ] = 20/1000
RooGaussian::alpha_background1_uncertaintyConstraint[ x=alpha_background1_uncertainty mean=nom_alpha_background1_uncertainty sigma=1 ] = 1
RooGaussian::alpha_background2_uncertaintyConstraint[ x=alpha_background2_uncertainty mean=nom_alpha_background2_uncertainty sigma=1 ] = 1
RooGaussian::alpha_signal_uncertaintyConstraint[ x=alpha_signal_uncertainty mean=nom_alpha_signal_uncertainty sigma=1 ] = 1
RooGaussian::lumiConstraint[ x=Lumi mean=nominalLumi sigma=0.02 ] = 1
RooProdPdf::model_SR[ lumiConstraint * alpha_signal_uncertaintyConstraint * alpha_background1_uncertaintyConstraint * alpha_background2_uncertaintyConstraint * SR_model(obs_x_SR) ] = 20

functions
--------
RooProduct::L_x_background1_SR_overallSyst_x_Exp[ 1 * background1_SR_overallSyst_x_Exp ] = 19
RooProduct::L_x_background2_SR_overallSyst_x_Exp[ 1 * background2_SR_overallSyst_x_Exp ] = 1
RooProduct::L_x_signal_SR_overallSyst_x_Exp[ 1 * signal_SR_overallSyst_x_Exp ] = 0
RooStats::HistFactory::FlexibleInterpVar::background1_SR_epsilon[ paramList=(alpha_background1_uncertainty) ] = 1
RooHistFunc::background1_SR_nominal[ depList=(obs_x_SR) ] = 19
RooProduct::background1_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * background1_SR_epsilon ] = 1
RooProduct::background1_SR_overallSyst_x_Exp[ background1_SR_nominal * background1_SR_overallNorm_x_sigma_epsilon ] = 19
RooStats::HistFactory::FlexibleInterpVar::background2_SR_epsilon[ paramList=(alpha_background2_uncertainty) ] = 1
RooHistFunc::background2_SR_nominal[ depList=(obs_x_SR) ] = 1
RooProduct::background2_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * background2_SR_epsilon ] = 1
RooProduct::background2_SR_overallSyst_x_Exp[ background2_SR_nominal * background2_SR_overallNorm_x_sigma_epsilon ] = 1
RooStats::HistFactory::FlexibleInterpVar::signal_SR_epsilon[ paramList=(alpha_signal_uncertainty) ] = 1
RooHistFunc::signal_SR_nominal[ depList=(obs_x_SR) ] = 0
RooProduct::signal_SR_overallNorm_x_sigma_epsilon[ SigXsecOverSM * signal_SR_epsilon ] = 1
RooProduct::signal_SR_overallSyst_x_Exp[ signal_SR_nominal * signal_SR_overallNorm_x_sigma_epsilon ] = 0

datasets
--------
RooDataSet::asimovData(obs_x_SR)
RooDataSet::obsData(obs_x_SR)

embedded datasets (in pdfs and functions)
-----------------------------------------
RooDataHist::signal_SRnominalDHist(obs_x_SR)
RooDataHist::background1_SRnominalDHist(obs_x_SR)
RooDataHist::background2_SRnominalDHist(obs_x_SR)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty)
ModelConfig_Observables:(obs_x_SR)
coefList:(binWidth_obs_x_SR_0,binWidth_obs_x_SR_1,binWidth_obs_x_SR_2)
constraintTerms:(lumiConstraint,alpha_signal_uncertaintyConstraint,alpha_background1_uncertaintyConstraint,alpha_background2_uncertaintyConstraint)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty)
likelihoodTerms:(SR_model)
obsAndWeight:(weightVar,obs_x_SR)
observables:(obs_x_SR)
observablesSet:(obs_x_SR)
shapeList:(L_x_signal_SR_overallSyst_x_Exp,L_x_background1_SR_overallSyst_x_Exp,L_x_background2_SR_overallSyst_x_Exp)

generic objects
---------------
RooStats::ModelConfig::ModelConfig

Setting Parameter(s) of Interest as: SigXsecOverSM 
full list of observables:
RooArgList:: = (obs_x_SR)


------------------
 Entering combination
-----------------------------------------
create toy data for SR
RooDataSet::AsimovData0[obs_x_SR,channelCat,weight:binWeightAsimov] = 10 entries (1000 weighted)
Merging data for channel SR

RooWorkspace(combined) combined contents

variables
---------
(channelCat,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty,nom_alpha_signal_uncertainty,obs_x_SR,weightVar)

datasets
--------
RooDataSet::asimovData(obs_x_SR,weightVar,channelCat)
RooDataSet::obsData(channelCat,obs_x_SR)

named sets
----------
ModelConfig_GlobalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty)
ModelConfig_Observables:(obs_x_SR,weightVar,channelCat)
globalObservables:(nom_alpha_signal_uncertainty,nom_alpha_background1_uncertainty,nom_alpha_background2_uncertainty)
observables:(obs_x_SR,weightVar,channelCat)



----------------
 Importing combined model
setting Lumi constant
Setting Parameter(s) of Interest as: SigXsecOverSM 
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: data_hist__x (Potential memory leak).
Warning in <TFile::Append>: Replacing existing TH1: channel_data__obs_x_SR (Potential memory leak).