{
"cells": [
{
"cell_type": "markdown",
"id": "05e294a7",
"metadata": {},
"source": [
"# rf204a_extendedLikelihood\n",
"Extended maximum likelihood fit in multiple ranges.\n",
"\n",
" When an extended pdf and multiple ranges are used, the\n",
" RooExtendPdf cannot correctly interpret the coefficients\n",
" used for extension.\n",
" This can be solved by using a RooAddPdf for extending the model.\n",
"\n",
"\n",
"\n",
"\n",
"**Author:** Stephan Hageboeck \n",
"This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:29 PM."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "88f168fb",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:45.782901Z",
"iopub.status.busy": "2026-05-19T20:29:45.782792Z",
"iopub.status.idle": "2026-05-19T20:29:45.791008Z",
"shell.execute_reply": "2026-05-19T20:29:45.790518Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"#include \"RooRealVar.h\"\n",
"#include \"RooDataSet.h\"\n",
"#include \"RooGaussian.h\"\n",
"#include \"RooChebychev.h\"\n",
"#include \"RooAddPdf.h\"\n",
"#include \"RooExtendPdf.h\"\n",
"#include \"RooFitResult.h\"\n",
"#include \"TCanvas.h\"\n",
"#include \"TAxis.h\"\n",
"#include \"RooPlot.h\"\n",
"using namespace RooFit ;"
]
},
{
"cell_type": "markdown",
"id": "470bef6e",
"metadata": {},
"source": [
"Setup component pdfs\n",
"---------------------------------------"
]
},
{
"cell_type": "markdown",
"id": "36548c07",
"metadata": {},
"source": [
"Declare observable x"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "6896c69e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:45.792529Z",
"iopub.status.busy": "2026-05-19T20:29:45.792416Z",
"iopub.status.idle": "2026-05-19T20:29:46.125474Z",
"shell.execute_reply": "2026-05-19T20:29:46.120423Z"
}
},
"outputs": [],
"source": [
"RooRealVar x(\"x\",\"x\",0,11) ;"
]
},
{
"cell_type": "markdown",
"id": "eb191728",
"metadata": {},
"source": [
"Create two Gaussian PDFs g1(x,mean1,sigma) anf g2(x,mean2,sigma) and their parameters"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "00778142",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:46.127787Z",
"iopub.status.busy": "2026-05-19T20:29:46.127655Z",
"iopub.status.idle": "2026-05-19T20:29:46.331107Z",
"shell.execute_reply": "2026-05-19T20:29:46.330359Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[#0] WARNING:InputArguments -- The parameter 'sigma1' with range [-inf, inf] of the RooGaussian 'sig1' exceeds the safe range of (0, inf). Advise to limit its range.\n",
"[#0] WARNING:InputArguments -- The parameter 'sigma2' with range [-inf, inf] of the RooGaussian 'sig2' exceeds the safe range of (0, inf). Advise to limit its range.\n"
]
}
],
"source": [
"RooRealVar mean(\"mean\",\"mean of gaussians\",5) ;\n",
"RooRealVar sigma1(\"sigma1\",\"width of gaussians\",0.5) ;\n",
"RooRealVar sigma2(\"sigma2\",\"width of gaussians\",1) ;\n",
"\n",
"RooGaussian sig1(\"sig1\",\"Signal component 1\",x,mean,sigma1) ;\n",
"RooGaussian sig2(\"sig2\",\"Signal component 2\",x,mean,sigma2) ;"
]
},
{
"cell_type": "markdown",
"id": "e257cc80",
"metadata": {},
"source": [
"Build Chebychev polynomial pdf"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5d5d8d44",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:46.332668Z",
"iopub.status.busy": "2026-05-19T20:29:46.332519Z",
"iopub.status.idle": "2026-05-19T20:29:46.534955Z",
"shell.execute_reply": "2026-05-19T20:29:46.534135Z"
}
},
"outputs": [],
"source": [
"RooRealVar a0(\"a0\",\"a0\",0.5,0.,1.) ;\n",
"RooRealVar a1(\"a1\",\"a1\",0.2,0.,1.) ;\n",
"RooChebychev bkg(\"bkg\",\"Background\",x,RooArgSet(a0,a1)) ;"
]
},
{
"cell_type": "markdown",
"id": "dbf47ac6",
"metadata": {},
"source": [
"Sum the signal components into a composite signal pdf"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "562cbc5e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:46.543332Z",
"iopub.status.busy": "2026-05-19T20:29:46.543193Z",
"iopub.status.idle": "2026-05-19T20:29:46.745489Z",
"shell.execute_reply": "2026-05-19T20:29:46.744888Z"
}
},
"outputs": [],
"source": [
"RooRealVar sig1frac(\"sig1frac\",\"fraction of component 1 in signal\",0.8,0.,1.) ;\n",
"RooAddPdf sig(\"sig\",\"Signal\",RooArgList(sig1,sig2),sig1frac) ;"
]
},
{
"cell_type": "markdown",
"id": "bdc870ef",
"metadata": {},
"source": [
"Extend the pdfs\n",
"-----------------------------"
]
},
{
"cell_type": "markdown",
"id": "25086b53",
"metadata": {},
"source": [
"Define signal range in which events counts are to be defined"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "2d65ae03",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:46.747406Z",
"iopub.status.busy": "2026-05-19T20:29:46.747260Z",
"iopub.status.idle": "2026-05-19T20:29:46.950828Z",
"shell.execute_reply": "2026-05-19T20:29:46.949930Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[#1] INFO:Eval -- RooRealVar::setRange(x) new range named 'signalRange' created with bounds [4,6]\n"
]
}
],
"source": [
"x.setRange(\"signalRange\",4,6) ;"
]
},
{
"cell_type": "markdown",
"id": "a7093c59",
"metadata": {},
"source": [
"Associated nsig/nbkg as expected number of events with sig/bkg _in_the_range_ \"signalRange\""
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "29810433",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:46.952183Z",
"iopub.status.busy": "2026-05-19T20:29:46.952059Z",
"iopub.status.idle": "2026-05-19T20:29:47.154128Z",
"shell.execute_reply": "2026-05-19T20:29:47.153751Z"
}
},
"outputs": [],
"source": [
"RooRealVar nsig(\"nsig\",\"number of signal events in signalRange\",500,0.,10000) ;\n",
"RooRealVar nbkg(\"nbkg\",\"number of background events in signalRange\",500,0,10000) ;"
]
},
{
"cell_type": "markdown",
"id": "35c3c8c6",
"metadata": {},
"source": [
"Use AddPdf to extend the model. Giving as many coefficients as pdfs switches\n",
"on extension."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "d4f3f3f1",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:47.177852Z",
"iopub.status.busy": "2026-05-19T20:29:47.177680Z",
"iopub.status.idle": "2026-05-19T20:29:47.383450Z",
"shell.execute_reply": "2026-05-19T20:29:47.382392Z"
}
},
"outputs": [],
"source": [
"RooAddPdf model(\"model\",\"(g1+g2)+a\", RooArgList(bkg,sig), RooArgList(nbkg,nsig)) ;"
]
},
{
"cell_type": "markdown",
"id": "36e3eb54",
"metadata": {},
"source": [
"Sample data, fit model\n",
"-------------------------------------------"
]
},
{
"cell_type": "markdown",
"id": "1a58e7a5",
"metadata": {},
"source": [
"Generate 1000 events from model so that nsig,nbkg come out to numbers <<500 in fit"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2878ed15",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:47.385145Z",
"iopub.status.busy": "2026-05-19T20:29:47.384984Z",
"iopub.status.idle": "2026-05-19T20:29:47.606476Z",
"shell.execute_reply": "2026-05-19T20:29:47.605801Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_56:2:2: warning: 'data' shadows a declaration with the same name in the 'std' namespace; use '::data' to reference this declaration\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n"
]
}
],
"source": [
"std::unique_ptr data{model.generate(x,1000)};\n",
"\n",
"\n",
"\n",
"auto canv = new TCanvas(\"Canvas\", \"Canvas\", 1500, 600);\n",
"canv->Divide(3,1);"
]
},
{
"cell_type": "markdown",
"id": "2e81631c",
"metadata": {},
"source": [
"Fit full range\n",
"-------------------------------------------"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "761a25f0",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:47.608245Z",
"iopub.status.busy": "2026-05-19T20:29:47.608113Z",
"iopub.status.idle": "2026-05-19T20:29:47.815735Z",
"shell.execute_reply": "2026-05-19T20:29:47.815347Z"
}
},
"outputs": [],
"source": [
"canv->cd(1);"
]
},
{
"cell_type": "markdown",
"id": "be7cfc62",
"metadata": {},
"source": [
"Perform unbinned ML fit to data, full range"
]
},
{
"cell_type": "markdown",
"id": "1ccf4592",
"metadata": {},
"source": [
"IMPORTANT:\n",
"The model needs to be copied when fitting with different ranges because\n",
"the interpretation of the coefficients is tied to the fit range\n",
"that's used in the first fit"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "3e0165ce",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:47.817653Z",
"iopub.status.busy": "2026-05-19T20:29:47.817504Z",
"iopub.status.idle": "2026-05-19T20:29:48.027342Z",
"shell.execute_reply": "2026-05-19T20:29:48.026582Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_60:3:47: error: reference to 'data' is ambiguous\n",
"std::unique_ptr r{model1.fitTo(*data,Save(), PrintLevel(-1))};\n",
" ^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n",
"input_line_60:7:1: error: reference to 'data' is ambiguous\n",
"data->plotOn(frame);\n",
"^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n"
]
}
],
"source": [
"RooAddPdf model1(model);\n",
"std::unique_ptr r{model1.fitTo(*data,Save(), PrintLevel(-1))};\n",
"r->Print() ;\n",
"\n",
"RooPlot * frame = x.frame(Title(\"Full range fitted\"));\n",
"data->plotOn(frame);\n",
"model1.plotOn(frame, VisualizeError(*r));\n",
"model1.plotOn(frame);\n",
"model1.paramOn(frame);\n",
"frame->Draw();"
]
},
{
"cell_type": "markdown",
"id": "567dca77",
"metadata": {},
"source": [
"Fit in two regions\n",
"-------------------------------------------"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "056c625e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:48.028683Z",
"iopub.status.busy": "2026-05-19T20:29:48.028532Z",
"iopub.status.idle": "2026-05-19T20:29:48.238289Z",
"shell.execute_reply": "2026-05-19T20:29:48.237586Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_61:7:48: error: reference to 'data' is ambiguous\n",
"std::unique_ptr r2{model2.fitTo(*data, Range(\"left,right\"), Save(), PrintLevel(-1))};\n",
" ^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n",
"input_line_61:12:1: error: reference to 'data' is ambiguous\n",
"data->plotOn(frame2);\n",
"^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n"
]
}
],
"source": [
"canv->cd(2);\n",
"x.setRange(\"left\", 0., 4.);\n",
"x.setRange(\"right\", 6., 10.);\n",
"\n",
"RooAddPdf model2(model);\n",
"std::unique_ptr r2{model2.fitTo(*data, Range(\"left,right\"), Save(), PrintLevel(-1))};\n",
"r2->Print();\n",
"\n",
"\n",
"RooPlot * frame2 = x.frame(Title(\"Fit in left/right sideband\"));\n",
"data->plotOn(frame2);\n",
"model2.plotOn(frame2, VisualizeError(*r2));\n",
"model2.plotOn(frame2);\n",
"model2.paramOn(frame2);\n",
"frame2->Draw();"
]
},
{
"cell_type": "markdown",
"id": "96cc8724",
"metadata": {},
"source": [
"Fit in one region\n",
"-------------------------------------------\n",
"Note how restricting the region to only the left tail increases\n",
"the fit uncertainty"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "27c6187e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:48.239623Z",
"iopub.status.busy": "2026-05-19T20:29:48.239491Z",
"iopub.status.idle": "2026-05-19T20:29:48.449291Z",
"shell.execute_reply": "2026-05-19T20:29:48.448583Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_62:6:48: error: reference to 'data' is ambiguous\n",
"std::unique_ptr r3{model3.fitTo(*data, Range(\"leftToMiddle\"), Save(), PrintLevel(-1))};\n",
" ^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n",
"input_line_62:11:1: error: reference to 'data' is ambiguous\n",
"data->plotOn(frame3);\n",
"^\n",
"input_line_56:2:30: note: candidate found by name lookup is 'data'\n",
" std::unique_ptr data{model.generate(x,1000)};\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:344:5: note: candidate found by name lookup is 'std::data'\n",
" data(initializer_list<_Tp> __il) noexcept\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:312:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:323:5: note: candidate found by name lookup is 'std::data'\n",
" data(const _Container& __cont) noexcept(noexcept(__cont.data()))\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/range_access.h:334:5: note: candidate found by name lookup is 'std::data'\n",
" data(_Tp (&__array)[_Nm]) noexcept\n",
" ^\n"
]
}
],
"source": [
"canv->cd(3);\n",
"x.setRange(\"leftToMiddle\", 0., 5.);\n",
"\n",
"RooAddPdf model3(model);\n",
"std::unique_ptr r3{model3.fitTo(*data, Range(\"leftToMiddle\"), Save(), PrintLevel(-1))};\n",
"r3->Print();\n",
"\n",
"\n",
"RooPlot * frame3 = x.frame(Title(\"Fit from left to middle\"));\n",
"data->plotOn(frame3);\n",
"model3.plotOn(frame3, VisualizeError(*r3));\n",
"model3.plotOn(frame3);\n",
"model3.paramOn(frame3);\n",
"frame3->Draw();\n",
"\n",
"canv->Draw();"
]
},
{
"cell_type": "markdown",
"id": "e5b04a59",
"metadata": {},
"source": [
"Draw all canvases "
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "ce963d09",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:29:48.450573Z",
"iopub.status.busy": "2026-05-19T20:29:48.450445Z",
"iopub.status.idle": "2026-05-19T20:29:48.679295Z",
"shell.execute_reply": "2026-05-19T20:29:48.678417Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
"
\n",
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%jsroot on\n",
"gROOT->GetListOfCanvases()->Draw()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "ROOT C++",
"language": "c++",
"name": "root"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".C",
"mimetype": " text/x-c++src",
"name": "c++"
}
},
"nbformat": 4,
"nbformat_minor": 5
}