SPlot does not work with fixCoefRange

After doing a fit, I want to extract s-weights. I’ve noticed that when I apply the SPlot class it changes my yields; usually, the yields change very slightly and all is well. However, if I call model.fixCoefRange('fitrange'), the yields change dramatically and the s-weights no longer sum to 1. This is true even if the range boundaries equal that of the fit parameter.

Is there a workaround? Details below.


Here is the relevant output of a fit to 10,000 entries:

Without calling fixCoefRange:

before SPlot: 
    nsig 9366.58311447 
    nbkg 633.425339991
after SPlot:
    nsig 9366.60780824
    nbkg 633.41932735
sum of s-weights for first ten entries:
    1.00000294826
    1.00000869212
    1.00000924234
    1.0000043644
    1.00001065619
    0.999890137275
    1.00000837543
    1.0000104364
    1.00001060384
    1.00001060553

With calling fixCoefRange:

before SPlot:
    nsig 9366.58311447
    nbkg 633.425339991
after SPlot:
    nsig 9899.999999
    nbkg 200.00000003
sum of s-weights for first ten entries:
    1.02608764932
    1.03625250977
    1.03716515952
    1.0289045259
    1.03948865788
    0.585594274615
    1.03572505786
    1.03912948497
    1.03940316514
    1.03940593361

The relevant code snippets, modeled on rs301_splot.C:

# declare fit variable
ws.factory('RooRealVar::m(5000, 6000, "MeV")')
fitvar = ws.var('m')
fitvar.setRange('fitrange', 5000, 6000)
# declare other observables
for b in coordbranches:
    ws.factory('RooRealVar::{0}({1}, {2}, {3})'.format(
        b.uniquenm, b.loBin, b.hiBin, b.units)
    )
# get dataset (created and saved separately)
wsdata = ROOT.TFile.Open('filename').Get('myWS')
data = wsdata.data('data')
# add fit model
ws.factory('SUM::model(nsig[]..., nbkg[]...)'
ws.pdf('model').fixCoefRange('fitrange')  # THIS IS THE LINE THAT MAKES THE CHANGE
# do fit
model = ws.pdf('model')
r = model.fitTo(data, RooFit.Extended(), RooFit.NumCPU(8), RooFit.Save(), RooFit.SumW2Error(True), RooFit.Range('fitrange'))
# do SPlot
nsig = ws.var('nsig')
nbkg = ws.var('nbkg')
yields = ROOT.RooArgList(nsig, nbkg)
ROOT.RooMsgService.instance().setSilentMode(True)
sData = ROOT.RooStats.SPlot('sData', 'An SPlot', data, model, yields)
ROOT.RooMsgService.instance().setSilentMode(False)

Update:

Looking at lines 350, 363, 368, and 618 in SPlot.cxx, this appears to be solely the result of running the fit without specifying the range. Indeed, I can confirm this by calling fitTo myself using the same call as in SPlot.AddSWeight:

print 'nsig', nsig.getVal()
model.fitTo(data, RooFit.Extended(True), RooFit.SumW2Error(True), RooFit.PrintLevel(-1), RooFit.PrintEvalErrors(-1))
print 'nsig', nsig.getVal()

yields:

nsig 9366.583114471146
[#1] INFO:Minization -- createNLL picked up cached consraints from workspace with 0 entries
[#1] INFO:Minization -- RooMinimizer::optimizeConst: activating const optimization
[#1] INFO:Minization --  The following expressions will be evaluated in cache-and-track mode: (g1,g2,bkg)
[#1] INFO:Fitting -- RooAbsPdf::fitTo(model) Calculating sum-of-weights-squared correction matrix for covariance matrix
[#1] INFO:Minization -- RooMinimizer::optimizeConst: deactivating const optimization
nsig 9899.999985002501

which is the same behavior exhibited above by SPlot.

If there were a way to specify fit options, or at least a range, when calling SPlot, it looks like that would, at least partially, solve the issue.

Final update:

I changed the SPlot source code to include RooFit::Range("fitrange") in its call to fitTo and rebuilt ROOT. It now works as expected. This is, obviously, a hacky workaround, but it confirms the diagnosis:

SPlot does not work for fits in a range.

@moneta should we at least add a diagnostic message that SPlot doesn’t support ranges?

Or maybe you make a pull request for the workaround? It sounds like this could be useful for others.

I have created a pull request with a fix.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.