{ "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 }