[RooFit] Why does 2D RooAddPdf use Numerical Integration?

Hi,

I am trying to understand the following behaviour in RooFit. I construct a RooAddPdf from 2D RooHistPdfs (signal and background). I want to plot the model and data projected onto 1D.

The original RooHistPdf getAnalyticalIntegral returns 1 (as I expect as I am doing no interpolation). When I plot it produces the message:

[#1] INFO:Plotting -- RooAbsReal::plotOn(histPdf_signal) plot on x integrates over variables (y)

However, the RooAddPdf model getAnalyticalIntegral returns 0 and when I plot informs me that it is using numerical integration.

[#1] INFO:Plotting -- RooAbsReal::plotOn(model) plot on x integrates over variables (y)
[#1] INFO:NumericIntegration -- RooRealIntegral::init(model_Int[y]_Norm[x,y]) using numeric integrator RooIntegrator1D to calculate Int(y)

However, if I understand correctly the RooAddPdf should calculate the integral as c1int(pdf1)+c2int(pdf2) which (since pdf1 and pdf2 can be integrated analytically) should be exact.

How can I make the projection calculation be performed analytically (i.e. summing over bins rather than applying some generic numerical integration algorithm)?

My full code demonstrating the problem follows.

Cheers,
Dave.

from ROOT import TH2F, TRandom3, RooArgSet, RooArgList, RooRealVar, RooDataHist, RooHistPdf, RooAddPdf, TCanvas

#set signal and backgroun event rate
nSignal = 1000
nBackground = 1000
#define binning
binsX, minX, maxX = 2, -3., 3.
binsY, minY, maxY = 2, -3., 3.

#declare histograms
rawSignalHist = TH2F("signal","signal",binsX, minY, maxY, binsY, minY, maxY)
rawBackgroundHist = TH2F("background","background",binsX, minY, maxY, binsY, minY, maxY)
#fill with some fake data
rand = TRandom3(21343)
for i in xrange(nSignal):
    x = rand.Gaus(1.,0.5)
    y = rand.Gaus(1.,0.5)
    rawSignalHist.Fill(x,y)
for i in xrange(nBackground):
    x = rand.Gaus(-1.,1.0)
    y = rand.Gaus(-1.,1.0)
    rawBackgroundHist.Fill(x,y)

#the observables
x = RooRealVar("x","x", minX, maxX)
y = RooRealVar("y","y", minY, maxY)
#make the RooHistPdfs
dataHistSignal = RooDataHist("dataHist_signal","data hist for signal",RooArgList(x,y),rawSignalHist)
histPdfSignal = RooHistPdf("histPdf_signal","histPdf for signal", RooArgSet(x,y), dataHistSignal)
dataHistBackground = RooDataHist("dataHist_background","data hist for background", RooArgList(x,y),rawBackgroundHist)
histPdfBackground = RooHistPdf("histPdf_background","histPdf for background", RooArgSet(x,y), dataHistBackground)
#the parameters
weightSignal = RooRealVar("weightSignal","weightSignal",nSignal,0.,nSignal*2.)
weightBackground = RooRealVar("weightBackground","weightBackground",nBackground,0.,nBackground*2.)
model = RooAddPdf("model","model", RooArgList(histPdfSignal,histPdfBackground), RooArgList(weightSignal, weightBackground))
#make fake data
data = model.generate(RooArgSet(x,y), nSignal+nBackground)

#plot the components of the model
frame = x.frame()
data.plotOn(frame)
print "can integrate RooHistPdf:",histPdfSignal.getAnalyticalIntegral(RooArgSet(x), RooArgSet())#returns 1, can integrate analytically
histPdfSignal.plotOn(frame) # no numeric integration
print "can integrate RooAddPdf:",model.getAnalyticalIntegral(RooArgSet(x), RooArgSet())#returns 0, cannot integrate analytically
model.plotOn(frame)
#plot the model
frame.Draw()

#produces the following output:
# can integrate RooHistPdf: 1
# [#1] INFO:Plotting -- RooAbsReal::plotOn(histPdf_signal) plot on x integrates over variables (y)
# can integrate RooAddPdf: 0
# [#1] INFO:Plotting -- RooAbsReal::plotOn(model) plot on x integrates over variables (y)
# [#1] INFO:NumericIntegration -- RooRealIntegral::init(model_Int[y]_Norm[x,y]) using numeric integrator RooIntegrator1D to calculate Int(y)

integrationQuestion.py (2.46 KB)