{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1e432a3d",
   "metadata": {},
   "source": [
    "# rf615_simulation_based_inference\n",
    "Use Simulation Based Inference (SBI) in RooFit.\n",
    "\n",
    "This tutorial shows how to use SBI in ROOT. As reference distribution we\n",
    "choose a simple uniform distribution. The target distribution is chosen to\n",
    "be gaussian with different mean values.\n",
    "The classifier is trained to discriminate between the reference and target\n",
    "distribution.\n",
    "We see how the neural networks generalize to unknown mean values.\n",
    "\n",
    "We compare the approach of using the likelihood ratio trick to morphing.\n",
    "\n",
    "An introduction of SBI can be found in https://arxiv.org/pdf/2010.06439.\n",
    "\n",
    "A short recap:\n",
    "The idea of SBI is to fit a surrogate model to the data, in order to really\n",
    "learn the likelihood function instead of calculating it. Therefore, a classifier is trained to discriminate between\n",
    "samples from a target distribution (here the Gaussian) $$x\\sim p(x|\\theta)$$ and a reference distribution (here the Uniform)\n",
    "$$x\\sim p_{ref}(x|\\theta)$$.\n",
    "\n",
    "The output of the classifier $$\\hat{s}(\\theta)$$ is a monotonic function of the likelihood ration and can be turned into an estimate of the likelihood ratio\n",
    "via $$\\hat{r}(\\theta)=\\frac{1-\\hat{s}(\\theta)}{\\hat{s}(\\theta)}.$$\n",
    "This is called the likelihood ratio trick.\n",
    "\n",
    "In the end we compare the negative logarithmic likelihoods of the learned, morphed and analytical likelihood with minuit and as a plot.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "**Author:** Robin Syring  \n",
    "<i><small>This notebook tutorial was automatically generated with <a href= \"https://github.com/root-project/root/blob/master/documentation/doxygen/converttonotebook.py\">ROOTBOOK-izer</a> from the macro found in the ROOT repository  on Tuesday, May 19, 2026 at 08:33 PM.</small></i>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6e448a01",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:54.242976Z",
     "iopub.status.busy": "2026-05-19T20:33:54.242856Z",
     "iopub.status.idle": "2026-05-19T20:33:56.132374Z",
     "shell.execute_reply": "2026-05-19T20:33:56.131805Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import ROOT\n",
    "from sklearn.neural_network import MLPClassifier"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b868dac8",
   "metadata": {},
   "source": [
    "The samples used for training the classifier in this tutorial / rescale for more accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3db31b0a",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.134419Z",
     "iopub.status.busy": "2026-05-19T20:33:56.134207Z",
     "iopub.status.idle": "2026-05-19T20:33:56.250855Z",
     "shell.execute_reply": "2026-05-19T20:33:56.250457Z"
    }
   },
   "outputs": [],
   "source": [
    "n_samples = 10000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "059441ef",
   "metadata": {},
   "source": [
    "Kills warning messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "df463700",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.253132Z",
     "iopub.status.busy": "2026-05-19T20:33:56.253006Z",
     "iopub.status.idle": "2026-05-19T20:33:56.403139Z",
     "shell.execute_reply": "2026-05-19T20:33:56.402372Z"
    }
   },
   "outputs": [],
   "source": [
    "ROOT.RooMsgService.instance().setGlobalKillBelow(ROOT.RooFit.WARNING)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73a30364",
   "metadata": {},
   "source": [
    "Morphing as a baseline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8f8709c8",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.424629Z",
     "iopub.status.busy": "2026-05-19T20:33:56.424459Z",
     "iopub.status.idle": "2026-05-19T20:33:56.529514Z",
     "shell.execute_reply": "2026-05-19T20:33:56.528932Z"
    }
   },
   "outputs": [],
   "source": [
    "def morphing(setting, workspace):\n",
    "    # Define binning for morphing\n",
    "    grid = ROOT.RooMomentMorphFuncND.Grid(ROOT.RooBinning(4, 0.0, 4.0))\n",
    "    x_var.setBins(50)\n",
    "\n",
    "    # Number of 'sampled' gaussians, if you change it, adjust the binning properly\n",
    "    n_grid = 5\n",
    "\n",
    "    for i in range(n_grid):\n",
    "        # Define the sampled gausians\n",
    "        mu_help = ROOT.RooRealVar(f\"mu{i}\", f\"mu{i}\", i)\n",
    "        help = ROOT.RooGaussian(f\"g{i}\", f\"g{i}\", x_var, mu_help, sigma)\n",
    "        workspace.Import(help, Silence=True)\n",
    "\n",
    "        # Fill the histograms\n",
    "        hist = workspace[f\"g{i}\"].generateBinned([x_var], n_samples)\n",
    "\n",
    "        # Make sure that every bin is filled and we don't get zero probability\n",
    "        for i_bin in range(hist.numEntries()):\n",
    "            hist.add(hist.get(i_bin), 1.0)\n",
    "\n",
    "        # Add the pdf to the workspace\n",
    "        workspace.Import(ROOT.RooHistPdf(f\"histpdf{i}\", f\"histpdf{i}\", [x_var], hist, 1), Silence=True)\n",
    "\n",
    "        # Add the pdf to the grid\n",
    "        grid.addPdf(workspace[f\"histpdf{i}\"], i)\n",
    "\n",
    "    # Create the morphing and add it to the workspace\n",
    "    morph_func = ROOT.RooMomentMorphFuncND(\"morph_func\", \"morph_func\", [mu_var], [x_var], grid, setting)\n",
    "    morph = ROOT.RooWrapperPdf(\"morph\", \"morph\", morph_func, True)\n",
    "    workspace.Import(morph, Silence=True)\n",
    "\n",
    "    # Uncomment to see input plots for the first dimension (you might need to increase the morphed samples)\n",
    "    # f1 = x_var.frame(Title=\"linear morphing;x;pdf\", Range=(-4, 8))\n",
    "    # for i in range(n_grid):\n",
    "    # workspace[f\"histpdf{i}\"].plotOn(f1)\n",
    "    # workspace[\"morph\"].plotOn(f1, LineColor=\"r\")\n",
    "    # c0 = ROOT.TCanvas()\n",
    "    # f1.Draw()\n",
    "    # input() # Wait for user input to proceed"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5570c523",
   "metadata": {},
   "source": [
    "Class used in this case to demonstrate the use of SBI in Root"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "5e5c565e",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.531632Z",
     "iopub.status.busy": "2026-05-19T20:33:56.531494Z",
     "iopub.status.idle": "2026-05-19T20:33:56.637006Z",
     "shell.execute_reply": "2026-05-19T20:33:56.636429Z"
    }
   },
   "outputs": [],
   "source": [
    "class SBI:\n",
    "    # Initializing the class SBI\n",
    "    def __init__(self, workspace):\n",
    "        # Choose the hyperparameters for training the neural network\n",
    "        self.classifier = MLPClassifier(hidden_layer_sizes=(20, 20), max_iter=1000, random_state=42)\n",
    "        self.data_model = None\n",
    "        self.data_ref = None\n",
    "        self.X_train = None\n",
    "        self.y_train = None\n",
    "        self.workspace = workspace\n",
    "\n",
    "    # Defining the target / training data for different values of mean value mu\n",
    "    def model_data(self, model, x, mu, n_samples):\n",
    "        ws = self.workspace\n",
    "        data_test_model = []\n",
    "        samples_gaussian = ws[model].generate([ws[x], ws[mu]], n_samples).to_numpy()\n",
    "        self._training_mus = samples_gaussian[mu]\n",
    "        data_test_model.extend(samples_gaussian[x])\n",
    "\n",
    "        self.data_model = np.array(data_test_model).reshape(-1, 1)\n",
    "\n",
    "    # Generating samples for the reference distribution\n",
    "    def reference_data(self, model, x, n_samples):\n",
    "        ws = self.workspace\n",
    "        # Ensuring the normalization with generating as many reference data as target data\n",
    "        samples_uniform = ws[model].generate(ws[x], n_samples)\n",
    "        data_reference_model = np.array(\n",
    "            [samples_uniform.get(i).getRealValue(\"x\") for i in range(samples_uniform.numEntries())]\n",
    "        )\n",
    "        self.data_ref = data_reference_model.reshape(-1, 1)\n",
    "\n",
    "    # Bringing the data in the right format for training\n",
    "    def preprocessing(self):\n",
    "        thetas_model = self._training_mus.reshape(-1, 1)\n",
    "        thetas_reference = self._training_mus.reshape(-1, 1)\n",
    "        thetas = np.concatenate((thetas_model, thetas_reference), axis=0)\n",
    "        X = np.concatenate([self.data_model, self.data_ref])\n",
    "        self.y_train = np.concatenate([np.ones(len(self.data_model)), np.zeros(len(self.data_ref))])\n",
    "        self.X_train = np.concatenate([X, thetas], axis=1)\n",
    "\n",
    "    # Train the classifier\n",
    "    def train_classifier(self):\n",
    "        self.classifier.fit(self.X_train, self.y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c645dfe",
   "metadata": {},
   "source": [
    "Setting the training and toy data samples; the factor 5 to enable a fair comparison to morphing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4a6436e0",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.639082Z",
     "iopub.status.busy": "2026-05-19T20:33:56.638954Z",
     "iopub.status.idle": "2026-05-19T20:33:56.742274Z",
     "shell.execute_reply": "2026-05-19T20:33:56.741623Z"
    }
   },
   "outputs": [],
   "source": [
    "n_samples_train = n_samples * 5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "591777ee",
   "metadata": {},
   "source": [
    "Define the \"observed\" data in a workspace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a70a8b86",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.744266Z",
     "iopub.status.busy": "2026-05-19T20:33:56.744142Z",
     "iopub.status.idle": "2026-05-19T20:33:56.847797Z",
     "shell.execute_reply": "2026-05-19T20:33:56.847368Z"
    }
   },
   "outputs": [],
   "source": [
    "def build_ws(mu_observed, sigma):\n",
    "    # using a workspace for easier processing inside the class\n",
    "    ws = ROOT.RooWorkspace()\n",
    "    ws.factory(f\"Gaussian::gauss(x[-5,15], mu[0,4], {sigma})\")\n",
    "    ws.factory(\"Uniform::uniform(x)\")\n",
    "    ws[\"mu\"].setVal(mu_observed)\n",
    "    ws.Print(\"v\")\n",
    "    obs_data = ws[\"gauss\"].generate(ws[\"x\"], 1000)\n",
    "    obs_data.SetName(\"obs_data\")\n",
    "    ws.Import(obs_data, Silence=True)\n",
    "\n",
    "    return ws"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7128cc32",
   "metadata": {},
   "source": [
    "The \"observed\" data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e5a27222",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:56.850057Z",
     "iopub.status.busy": "2026-05-19T20:33:56.849931Z",
     "iopub.status.idle": "2026-05-19T20:33:57.117978Z",
     "shell.execute_reply": "2026-05-19T20:33:57.117258Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "RooWorkspace()  contents\n",
      "\n",
      "variables\n",
      "---------\n",
      "(mu,x)\n",
      "\n",
      "p.d.f.s\n",
      "-------\n",
      "RooGaussian::gauss[ x=x mean=mu sigma=1.5 ] = 0.249352\n",
      "RooUniform::uniform[ x=(x) ] = 1\n",
      "\n"
     ]
    }
   ],
   "source": [
    "mu_observed = 2.5\n",
    "sigma = 1.5\n",
    "workspace = build_ws(mu_observed, sigma)\n",
    "x_var = workspace[\"x\"]\n",
    "mu_var = workspace[\"mu\"]\n",
    "gauss = workspace[\"gauss\"]\n",
    "uniform = workspace[\"uniform\"]\n",
    "obs_data = workspace[\"obs_data\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "352f2660",
   "metadata": {},
   "source": [
    "Training the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f680a0a9",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:57.119653Z",
     "iopub.status.busy": "2026-05-19T20:33:57.119515Z",
     "iopub.status.idle": "2026-05-19T20:33:59.648847Z",
     "shell.execute_reply": "2026-05-19T20:33:59.648254Z"
    }
   },
   "outputs": [],
   "source": [
    "model = SBI(workspace)\n",
    "model.model_data(\"gauss\", \"x\", \"mu\", n_samples_train)\n",
    "model.reference_data(\"uniform\", \"x\", n_samples_train)\n",
    "model.preprocessing()\n",
    "model.train_classifier()\n",
    "sbi_model = model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27e16e11",
   "metadata": {},
   "source": [
    "Compute the likelihood ratio of the classifier for analysis purposes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "92f80fcc",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:59.650876Z",
     "iopub.status.busy": "2026-05-19T20:33:59.650747Z",
     "iopub.status.idle": "2026-05-19T20:33:59.754984Z",
     "shell.execute_reply": "2026-05-19T20:33:59.753936Z"
    }
   },
   "outputs": [],
   "source": [
    "def learned_likelihood_ratio(x, mu):\n",
    "    n = max(len(x), len(mu))\n",
    "    X = np.zeros((n, 2))\n",
    "    X[:, 0] = x\n",
    "    X[:, 1] = mu\n",
    "    prob = sbi_model.classifier.predict_proba(X)[:, 1]\n",
    "    return prob / (1 - prob)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "778ae9a8",
   "metadata": {},
   "source": [
    "Compute the learned likelihood ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "40114a8a",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:33:59.756613Z",
     "iopub.status.busy": "2026-05-19T20:33:59.756474Z",
     "iopub.status.idle": "2026-05-19T20:34:00.195212Z",
     "shell.execute_reply": "2026-05-19T20:34:00.194127Z"
    }
   },
   "outputs": [],
   "source": [
    "llhr_learned = ROOT.RooFit.bindFunction(\"MyBinFunc\", learned_likelihood_ratio, x_var, mu_var)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e8de44b",
   "metadata": {},
   "source": [
    "Compute the real likelihood ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "49ba30e9",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:00.196879Z",
     "iopub.status.busy": "2026-05-19T20:34:00.196751Z",
     "iopub.status.idle": "2026-05-19T20:34:00.315172Z",
     "shell.execute_reply": "2026-05-19T20:34:00.314131Z"
    }
   },
   "outputs": [],
   "source": [
    "llhr_calc = ROOT.RooFormulaVar(\"llhr_calc\", \"x[0] / x[1]\", [gauss, uniform])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f364ae1",
   "metadata": {},
   "source": [
    "Create the exact negative log likelihood functions for Gaussian model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f20f4653",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:00.316838Z",
     "iopub.status.busy": "2026-05-19T20:34:00.316708Z",
     "iopub.status.idle": "2026-05-19T20:34:00.459912Z",
     "shell.execute_reply": "2026-05-19T20:34:00.458812Z"
    }
   },
   "outputs": [],
   "source": [
    "nll_gauss = gauss.createNLL(obs_data)\n",
    "ROOT.SetOwnership(nll_gauss, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae5f2d9b",
   "metadata": {},
   "source": [
    "Create the learned pdf and NLL sum based on the learned likelihood ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "be2cd2bd",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:00.461529Z",
     "iopub.status.busy": "2026-05-19T20:34:00.461403Z",
     "iopub.status.idle": "2026-05-19T20:34:00.590474Z",
     "shell.execute_reply": "2026-05-19T20:34:00.589374Z"
    }
   },
   "outputs": [],
   "source": [
    "pdf_learned = ROOT.RooWrapperPdf(\"learned_pdf\", \"learned_pdf\", llhr_learned, True)\n",
    "\n",
    "nllr_learned = pdf_learned.createNLL(obs_data)\n",
    "ROOT.SetOwnership(nllr_learned, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2aa2b2cc",
   "metadata": {},
   "source": [
    "Compute the morphed nll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "c9b97339",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:00.592110Z",
     "iopub.status.busy": "2026-05-19T20:34:00.591983Z",
     "iopub.status.idle": "2026-05-19T20:34:00.865669Z",
     "shell.execute_reply": "2026-05-19T20:34:00.864639Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[#0] ERROR:Eval -- RooAbsReal::logEvalError(morph_func_offset_0_0) evaluation error, \n",
      " origin       : RooFormulaVar::morph_func_offset_0_0[ actualVars=(histpdf0_MOMENT_1_x,morph_func_pos_0,morph_func_slope_0_0) formula=\"x[0]-(x[1]*x[2])\" ]\n",
      " message      : function value is NAN\n",
      " server values: actualVars=(histpdf0_MOMENT_1_x = 0.0504296,morph_func_pos_0 = 0,morph_func_slope_0_0 = inf)\n",
      "[#0] ERROR:Eval -- RooAbsReal::logEvalError(morph_func_offset_1_0) evaluation error, \n",
      " origin       : RooFormulaVar::morph_func_offset_1_0[ actualVars=(histpdf1_MOMENT_1_x,morph_func_pos_0,morph_func_slope_1_0) formula=\"x[0]-(x[1]*x[2])\" ]\n",
      " message      : function value is NAN\n",
      " server values: actualVars=(histpdf1_MOMENT_1_x = 0.997298,morph_func_pos_0 = 0,morph_func_slope_1_0 = inf)\n",
      "[#0] ERROR:Eval -- RooAbsReal::logEvalError(morph_func_offset_2_0) evaluation error, \n",
      " origin       : RooFormulaVar::morph_func_offset_2_0[ actualVars=(histpdf2_MOMENT_1_x,morph_func_pos_0,morph_func_slope_2_0) formula=\"x[0]-(x[1]*x[2])\" ]\n",
      " message      : function value is NAN\n",
      " server values: actualVars=(histpdf2_MOMENT_1_x = 2.01481,morph_func_pos_0 = 0,morph_func_slope_2_0 = inf)\n",
      "[#0] ERROR:Eval -- RooAbsReal::logEvalError(morph_func_offset_3_0) evaluation error, \n",
      " origin       : RooFormulaVar::morph_func_offset_3_0[ actualVars=(histpdf3_MOMENT_1_x,morph_func_pos_0,morph_func_slope_3_0) formula=\"x[0]-(x[1]*x[2])\" ]\n",
      " message      : function value is NAN\n",
      " server values: actualVars=(histpdf3_MOMENT_1_x = 3.04583,morph_func_pos_0 = 0,morph_func_slope_3_0 = inf)\n",
      "[#0] ERROR:Eval -- RooAbsReal::logEvalError(morph_func_offset_4_0) evaluation error, \n",
      " origin       : RooFormulaVar::morph_func_offset_4_0[ actualVars=(histpdf4_MOMENT_1_x,morph_func_pos_0,morph_func_slope_4_0) formula=\"x[0]-(x[1]*x[2])\" ]\n",
      " message      : function value is NAN\n",
      " server values: actualVars=(histpdf4_MOMENT_1_x = 4.00616,morph_func_pos_0 = 0,morph_func_slope_4_0 = inf)\n"
     ]
    }
   ],
   "source": [
    "morphing(ROOT.RooMomentMorphFuncND.Linear, workspace)\n",
    "nll_morph = workspace[\"morph\"].createNLL(obs_data)\n",
    "ROOT.SetOwnership(nll_morph, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ab80398",
   "metadata": {},
   "source": [
    "Plot the negative logarithmic summed likelihood"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "32423534",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:00.867331Z",
     "iopub.status.busy": "2026-05-19T20:34:00.867201Z",
     "iopub.status.idle": "2026-05-19T20:34:01.152890Z",
     "shell.execute_reply": "2026-05-19T20:34:01.151822Z"
    }
   },
   "outputs": [],
   "source": [
    "frame1 = mu_var.frame(Title=\"NLL of SBI vs. Morphing;mu;NLL\", Range=(2.2, 2.8))\n",
    "nllr_learned.plotOn(frame1, LineColor=\"kP6Blue\", ShiftToZero=True, Name=\"learned\")\n",
    "nll_gauss.plotOn(frame1, LineColor=\"kP6Yellow\", ShiftToZero=True, Name=\"gauss\")\n",
    "ROOT.RooAbsReal.setEvalErrorLoggingMode(\"Ignore\")  # Silence some warnings\n",
    "nll_morph.plotOn(frame1, LineColor=\"kP6Red\", ShiftToZero=True, Name=\"morphed\")\n",
    "ROOT.RooAbsReal.setEvalErrorLoggingMode(\"PrintErrors\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ef8f5f0",
   "metadata": {},
   "source": [
    "Plot the likelihood functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "d61299b0",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:01.154566Z",
     "iopub.status.busy": "2026-05-19T20:34:01.154439Z",
     "iopub.status.idle": "2026-05-19T20:34:01.280705Z",
     "shell.execute_reply": "2026-05-19T20:34:01.279643Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<cppyy.gbl.RooPlot object at 0x5573fcf03b10>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "frame2 = x_var.frame(Title=\"Likelihood ratio r(x|#mu=2.5);x;p_{gauss}/p_{uniform}\")\n",
    "llhr_learned.plotOn(frame2, LineColor=\"kP6Blue\", Name=\"learned_ratio\")\n",
    "llhr_calc.plotOn(frame2, LineColor=\"kP6Yellow\", Name=\"exact\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b097a021",
   "metadata": {},
   "source": [
    "Write the plots into one canvas to show, or into separate canvases for saving."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "acf50206",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:01.282367Z",
     "iopub.status.busy": "2026-05-19T20:34:01.282239Z",
     "iopub.status.idle": "2026-05-19T20:34:01.457001Z",
     "shell.execute_reply": "2026-05-19T20:34:01.455930Z"
    }
   },
   "outputs": [],
   "source": [
    "single_canvas = True\n",
    "\n",
    "c = ROOT.TCanvas(\"\", \"\", 1200 if single_canvas else 600, 600)\n",
    "if single_canvas:\n",
    "    c.Divide(2)\n",
    "    c.cd(1)\n",
    "    ROOT.gPad.SetLeftMargin(0.15)\n",
    "    frame1.GetYaxis().SetTitleOffset(1.8)\n",
    "frame1.Draw()\n",
    "\n",
    "legend1 = ROOT.TLegend(0.43, 0.63, 0.8, 0.87)\n",
    "legend1.SetFillColor(\"kWhite\")\n",
    "legend1.SetLineColor(\"kWhite\")\n",
    "legend1.SetTextSize(0.04)\n",
    "legend1.AddEntry(\"learned\", \"learned (SBI)\", \"L\")\n",
    "legend1.AddEntry(\"gauss\", \"true NLL\", \"L\")\n",
    "legend1.AddEntry(\"morphed\", \"moment morphing\", \"L\")\n",
    "legend1.Draw()\n",
    "\n",
    "if single_canvas:\n",
    "    c.cd(2)\n",
    "    ROOT.gPad.SetLeftMargin(0.15)\n",
    "    frame2.GetYaxis().SetTitleOffset(1.8)\n",
    "else:\n",
    "    c.SaveAs(\"rf615_plot_1.png\")\n",
    "    c = ROOT.TCanvas(\"\", \"\", 600, 600)\n",
    "\n",
    "frame2.Draw()\n",
    "\n",
    "legend2 = ROOT.TLegend(0.53, 0.73, 0.87, 0.87)\n",
    "legend2.SetFillColor(\"kWhite\")\n",
    "legend2.SetLineColor(\"kWhite\")\n",
    "legend2.SetTextSize(0.04)\n",
    "legend2.AddEntry(\"learned_ratio\", \"learned (SBI)\", \"L\")\n",
    "legend2.AddEntry(\"exact\", \"true ratio\", \"L\")\n",
    "legend2.Draw()\n",
    "\n",
    "if not single_canvas:\n",
    "    c.SaveAs(\"rf615_plot_2.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "849a771f",
   "metadata": {},
   "source": [
    "Compute the minimum via minuit and display the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "4f7814b2",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:01.458622Z",
     "iopub.status.busy": "2026-05-19T20:34:01.458482Z",
     "iopub.status.idle": "2026-05-19T20:34:01.632808Z",
     "shell.execute_reply": "2026-05-19T20:34:01.632148Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "  RooFitResult: minimized FCN value: 1862.97, estimated distance to minimum: 2.32702e-05\n",
      "                covariance matrix quality: Full, accurate covariance matrix\n",
      "                Status : MINIMIZE=0 \n",
      "\n",
      "    Floating Parameter    FinalValue +/-  Error   \n",
      "  --------------------  --------------------------\n",
      "                    mu    2.5399e+00 +/-  4.74e-02\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "  RooFitResult: minimized FCN value: -1126.14, estimated distance to minimum: 4.23342e-05\n",
      "                covariance matrix quality: Full, accurate covariance matrix\n",
      "                Status : MINIMIZE=0 \n",
      "\n",
      "    Floating Parameter    FinalValue +/-  Error   \n",
      "  --------------------  --------------------------\n",
      "                    mu    2.5511e+00 +/-  7.15e-02\n",
      "\n",
      "\n",
      "  RooFitResult: minimized FCN value: -7350.75, estimated distance to minimum: 0.000125595\n",
      "                covariance matrix quality: Full, accurate covariance matrix\n",
      "                Status : MINIMIZE=0 \n",
      "\n",
      "    Floating Parameter    FinalValue +/-  Error   \n",
      "  --------------------  --------------------------\n",
      "                    mu    2.5103e+00 +/-  4.08e-02\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for nll in [nll_gauss, nllr_learned, nll_morph]:\n",
    "    minimizer = ROOT.RooMinimizer(nll)\n",
    "    minimizer.setErrorLevel(0.5)  # Adjust the error level in the minimization to work with likelihoods\n",
    "    minimizer.setPrintLevel(-1)\n",
    "    minimizer.minimize(\"Minuit2\")\n",
    "    result = minimizer.save()\n",
    "    ROOT.SetOwnership(result, True)\n",
    "    result.Print()\n",
    "\n",
    "del nll_morph\n",
    "del nllr_learned\n",
    "del nll_gauss\n",
    "del workspace"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6427f652",
   "metadata": {},
   "source": [
    "Draw all canvases "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "515858d1",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:34:01.634502Z",
     "iopub.status.busy": "2026-05-19T20:34:01.634377Z",
     "iopub.status.idle": "2026-05-19T20:34:01.828990Z",
     "shell.execute_reply": "2026-05-19T20:34:01.828080Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "\n",
       "<div id=\"root_plot_1779222841819\" style=\"width: 1200px; height: 600px; position: relative\">\n",
       "</div>\n",
       "\n",
       "</div>\n",
       "<script>\n",
       "   function process_root_plot_1779222841819() {\n",
       "      function execCode(Core) {\n",
       "         Core.settings.HandleKeys = false;\n",
       "         \n",
       "Core.unzipJSON(45142,'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').then(json => {\n",
       "   const obj = Core.parse(json);\n",
       "   Core.draw('root_plot_1779222841819', obj, '');\n",
       "});\n",
       "\n",
       "      }\n",
       "      const servers = ['/static/', 'https://root.cern/js/7.11.0/', 'https://jsroot.gsi.de/7.11.0/'],\n",
       "            path = 'build/jsroot';\n",
       "      if (typeof JSROOT !== 'undefined')\n",
       "         execCode(JSROOT);\n",
       "      else if (typeof requirejs !== 'undefined') {\n",
       "         servers.forEach((s,i) => { servers[i] = s + path; });\n",
       "         requirejs.config({ paths: { 'jsroot' : servers } })(['jsroot'],  execCode);\n",
       "      } else {\n",
       "         const config = document.getElementById('jupyter-config-data');\n",
       "         if (config)\n",
       "            servers[0] = (JSON.parse(config.innerHTML || '{}')?.baseUrl || '/') + 'static/';\n",
       "         else\n",
       "            servers.shift();\n",
       "         function loadJsroot() {\n",
       "            return !servers.length ? 0 : import(servers.shift() + path + '.js').catch(loadJsroot).then(() => execCode(JSROOT));\n",
       "         }\n",
       "         loadJsroot();\n",
       "      }\n",
       "   }\n",
       "   process_root_plot_1779222841819();\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from ROOT import gROOT \n",
    "gROOT.GetListOfCanvases().Draw()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
