{ "cells": [ { "cell_type": "markdown", "id": "7fbe00e3", "metadata": {}, "source": [ "# Correctionlib tutorial\n", "\n", "The purpose of this library is to provide a well-structured JSON data format for a\n", "wide variety of ad-hoc correction factors encountered in a typical HEP analysis and\n", "a companion evaluation tool suitable for use in C++ and python programs.\n", "Here we restrict our definition of correction factors to a class of functions with\n", "scalar inputs that produce a scalar output.\n", "\n", "In python, the function signature is:\n", "\n", "```python\n", "def f(*args: int | float | str) -> float: ...\n", "```\n", "\n", "In C++, the signature is:\n", "```cpp\n", "double Correction::evaluate(const std::vector>& values) const;\n", "```\n", "\n", "The supported function classes include:\n", "\n", " * multi-dimensional binned lookups;\n", " * binned lookups pointing to multi-argument formulas with a restricted\n", " math function set (`exp`, `sqrt`, etc.);\n", " * categorical (string or integer enumeration) maps;\n", " * input transforms (updating one input value in place);\n", " * pseudorandom number generation; and\n", " * compositions of the above.\n", "\n" ] }, { "cell_type": "markdown", "id": "7cf107e9", "metadata": {}, "source": [ "## Basic evaluator usage\n", "We can import a previously defined set of correction objects using the\n", "```python\n", "correctionlib.CorrectionSet.from_file(filename)\n", "```\n", "or from a string with `.from_string(\"...\")`. The `from_file` invocation accepts JSON or gzipped JSON data.\n", "The `CorrectionSet` acts as a dictionary of `Correction` objects." ] }, { "cell_type": "code", "execution_count": 1, "id": "ecf89960", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['gen2_to_gen1', 'phimod', 'ptweight']" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import correctionlib\n", "\n", "ceval = correctionlib.CorrectionSet.from_file(\"mycorrections.json\")\n", "list(ceval.keys())" ] }, { "cell_type": "markdown", "id": "a5e178c6", "metadata": {}, "source": [ "Each correction has a name, description, version, and a input and output specification:" ] }, { "cell_type": "code", "execution_count": 2, "id": "12ec8a54", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Correction gen2_to_gen1 has 2 inputs\n", " Input pt (real): pt\n", " Input eta (real): eta\n", "Correction phimod has 2 inputs\n", " Input phi (real): \n", " Input q (int): Particle charge\n", "Correction ptweight has 2 inputs\n", " Input pt (real): Muon transverse momentum\n", " Input syst (string): Systematic\n" ] } ], "source": [ "for corr in ceval.values():\n", " print(f\"Correction {corr.name} has {len(corr.inputs)} inputs\")\n", " for ix in corr.inputs:\n", " print(f\" Input {ix.name} ({ix.type}): {ix.description}\")" ] }, { "cell_type": "markdown", "id": "1a45a4c6", "metadata": {}, "source": [ "Most important, each correction has a `.evaluate(...)` method that accepts scalars or numpy arrays:" ] }, { "cell_type": "code", "execution_count": 3, "id": "fa574b22", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0278771158865732" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ceval[\"phimod\"].evaluate(1.2, -1)" ] }, { "cell_type": "markdown", "id": "3c511ff1", "metadata": {}, "source": [ "Note that the input types are strict (not coerced) to help catch bugs early, e.g." ] }, { "cell_type": "code", "execution_count": 4, "id": "da7f5ae9", "metadata": {}, "outputs": [], "source": [ "# will not work\n", "# ceval[\"phimod\"].evaluate(1.2, -1.0)" ] }, { "cell_type": "code", "execution_count": 5, "id": "43daa330", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1.08, 1.1 , 1.1 , 1.1 , 1.1 , 1.06, 1.1 , 1.1 , 1.1 , 1.06])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "ptvals = np.random.exponential(15.0, size=10)\n", "\n", "ceval[\"ptweight\"].evaluate(ptvals, \"nominal\")" ] }, { "cell_type": "markdown", "id": "780c7d72", "metadata": {}, "source": [ "Currently, only numpy-compatible awkward arrays are accepted, but a jagged array can be flattened and re-wrapped quite easily:" ] }, { "cell_type": "code", "execution_count": 6, "id": "f84938cf", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
[[1.1, 1.08, 1.06],\n",
       " [1.04, 1.02],\n",
       " [1.02]]\n",
       "-------------------\n",
       "backend: cpu\n",
       "nbytes: 80 B\n",
       "type: 3 * var * float64
" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import awkward as ak\n", "\n", "ptjagged = ak.Array([[10.1, 20.2, 30.3], [40.4, 50.5], [60.6]])\n", "\n", "ptflat, counts = ak.flatten(ptjagged), ak.num(ptjagged)\n", "weight = ak.unflatten(\n", " ceval[\"ptweight\"].evaluate(ptflat, \"nominal\"),\n", " counts=counts,\n", ")\n", "weight" ] }, { "cell_type": "markdown", "id": "f8789ade", "metadata": {}, "source": [ "## Creating new corrections\n", "\n", "Alongside the evaluator we just demonstrated, `correctionlib` also contains a complete JSON Schema defining the expected fields and types found in a correction json object. The complete schema (version 2) is defined in the [documentation](https://cms-nanoaod.github.io/correctionlib/schemav2.html). In the package, we use [pydantic](https://pydantic-docs.helpmanual.io/) to help us validate the data and build correction objects in an easier way. The basic object is the `Correction` which, besides some metadata like a name and version, defines upfront the inputs and output. Below we make our first correction, which will always return `1.1` as the output:" ] }, { "cell_type": "code", "execution_count": 7, "id": "445e56dd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Correction(name='ptweight', description=None, version=1, inputs=[Variable(name='pt', type='real', description='Muon transverse momentum')], output=Variable(name='weight', type='real', description='Multiplicative event weight'), generic_formulas=None, data=1.1)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import correctionlib.schemav2 as cs\n", "\n", "corr = cs.Correction(\n", " name=\"ptweight\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Muon transverse momentum\"),\n", " ],\n", " output=cs.Variable(\n", " name=\"weight\", type=\"real\", description=\"Multiplicative event weight\"\n", " ),\n", " data=1.1,\n", ")\n", "corr" ] }, { "cell_type": "markdown", "id": "7f07d14a", "metadata": {}, "source": [ "The resulting object can be manipulated in-place as needed. Note that this correction object is not an evaluator instance as we saw before. We can convert from the schema to an evaluator with the `.to_evaluator()` function:" ] }, { "cell_type": "code", "execution_count": 8, "id": "70928d03", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.1" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "corr.to_evaluator().evaluate(12.3)" ] }, { "cell_type": "markdown", "id": "99d8e60a", "metadata": {}, "source": [ "A nicer printout is also available through `rich`:" ] }, { "cell_type": "code", "execution_count": 9, "id": "0f4f5fd6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 ptweight (v1)\n",
       "No description\n",
       "Node counts: \n",
       "╭───────── ▶ input ──────────╮\n",
       "│ pt (real)                  │\n",
       "│ Muon transverse momentum   │\n",
       "│ Range: unused, overflow ok │\n",
       "╰────────────────────────────╯\n",
       "╭───────── ◀ output ──────────╮\n",
       "│ weight (real)               │\n",
       "│ Multiplicative event weight │\n",
       "╰─────────────────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mptweight\u001b[0m \u001b[1m(\u001b[0mv1\u001b[1m)\u001b[0m\n", "\u001b[3mNo description\u001b[0m\n", "Node counts: \n", "╭───────── ▶ input ──────────╮\n", "│ \u001b[1mpt\u001b[0m (real) │\n", "│ Muon transverse momentum │\n", "│ Range: \u001b[1;31munused\u001b[0m, overflow ok │\n", "╰────────────────────────────╯\n", "╭───────── ◀ output ──────────╮\n", "│ \u001b[1mweight\u001b[0m (real) │\n", "│ Multiplicative event weight │\n", "╰─────────────────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import rich\n", "\n", "rich.print(corr)" ] }, { "cell_type": "markdown", "id": "237a7a12", "metadata": {}, "source": [ "The `data=` field of the correction is the root node of a tree of objects defining how the correction is to be evaluated. In the first example, it simply terminates at this node, returning always the float value `1.1`. We have many possible node types:\n", "\n", "- Binning: for 1D binned variable, each bin can be any type;\n", "- MultiBinning: an optimization for nested 1D Binnings, this can lookup n-dimensional binned values;\n", "- Category: for discrete dimensions, either integer or string types;\n", "- Formula: arbitrary formulas in up to four (real or integer-valued) input variables;\n", "- FormulaRef: an optimization for repeated use of the same formula with different coefficients;\n", "- Transform: useful to rewrite an input for all downstream nodes;\n", "- HashPRNG: a deterministic pseudorandom number generator; and\n", "- float: a constant value\n", "\n", "Let's create our first binned correction. We'll have to decide how to handle inputs that are out of range with the `flow=` attribute. Try switching `\"clamp\"` with `\"error\"` or another content node." ] }, { "cell_type": "code", "execution_count": 10, "id": "682bb99a", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 ptweight (v1)\n",
       "No description\n",
       "Node counts: Binning: 1\n",
       "╭───────────── ▶ input ─────────────╮\n",
       "│ pt (real)                         │\n",
       "│ Muon transverse momentum          │\n",
       "│ Range: [10.0, 120.0), overflow ok │\n",
       "╰───────────────────────────────────╯\n",
       "╭───────── ◀ output ──────────╮\n",
       "│ weight (real)               │\n",
       "│ Multiplicative event weight │\n",
       "╰─────────────────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mptweight\u001b[0m \u001b[1m(\u001b[0mv1\u001b[1m)\u001b[0m\n", "\u001b[3mNo description\u001b[0m\n", "Node counts: \u001b[1mBinning\u001b[0m: \u001b[1;36m1\u001b[0m\n", "╭───────────── ▶ input ─────────────╮\n", "│ \u001b[1mpt\u001b[0m (real) │\n", "│ Muon transverse momentum │\n", "│ Range: [10.0, 120.0), overflow ok │\n", "╰───────────────────────────────────╯\n", "╭───────── ◀ output ──────────╮\n", "│ \u001b[1mweight\u001b[0m (real) │\n", "│ Multiplicative event weight │\n", "╰─────────────────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ptweight = cs.Correction(\n", " name=\"ptweight\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Muon transverse momentum\")\n", " ],\n", " output=cs.Variable(\n", " name=\"weight\", type=\"real\", description=\"Multiplicative event weight\"\n", " ),\n", " data=cs.Binning(\n", " nodetype=\"binning\",\n", " input=\"pt\",\n", " edges=[10, 20, 30, 40, 50, 80, 120],\n", " content=[1.1, 1.08, 1.06, 1.04, 1.02, 1.0],\n", " flow=\"clamp\",\n", " ),\n", ")\n", "\n", "rich.print(ptweight)" ] }, { "cell_type": "code", "execution_count": 11, "id": "1cc178ba", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ptweight.to_evaluator().evaluate(230.0)" ] }, { "cell_type": "markdown", "id": "736f8216", "metadata": {}, "source": [ "## Formulas\n", "\n", "Formula support currently includes a mostly-complete subset of the ROOT library TFormula class, and is implemented in a threadsafe standalone manner. The parsing grammar is formally defined and parsed through the use of a header-only PEG parser library. This allows for extremely fast parsing of thousands of formulas encountered sometimes in binned formula (spline) correction types. Below we demonstrate a simple formula for a made-up $\\phi$-dependent efficiency correction. This also demonstrates nesting a content node inside another for the first time, with the outer Category node defining which formula to use depending on the particle charge" ] }, { "cell_type": "code", "execution_count": 12, "id": "e86a74de", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 phimod (v1)\n",
       "Phi-dependent tracking efficiency, or something?\n",
       "Node counts: Category: 1, Formula: 2\n",
       "╭───────── ▶ input ──────────╮ ╭──── ▶ input ────╮\n",
       "│ phi (real)                 │ │ q (int)         │\n",
       "│ No description             │ │ Particle charge │\n",
       "│ Range: unused, overflow ok │ │ Values: -1, 1   │\n",
       "╰────────────────────────────╯ ╰─────────────────╯\n",
       "╭───────── ◀ output ──────────╮\n",
       "│ weight (real)               │\n",
       "│ Multiplicative event weight │\n",
       "╰─────────────────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mphimod\u001b[0m \u001b[1m(\u001b[0mv1\u001b[1m)\u001b[0m\n", "Phi-dependent tracking efficiency, or something?\n", "Node counts: \u001b[1mCategory\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[1mFormula\u001b[0m: \u001b[1;36m2\u001b[0m\n", "╭───────── ▶ input ──────────╮ ╭──── ▶ input ────╮\n", "│ \u001b[1mphi\u001b[0m (real) │ │ \u001b[1mq\u001b[0m (int) │\n", "│ \u001b[3mNo description\u001b[0m │ │ Particle charge │\n", "│ Range: \u001b[1;31munused\u001b[0m, overflow ok │ │ Values: -1, 1 │\n", "╰────────────────────────────╯ ╰─────────────────╯\n", "╭───────── ◀ output ──────────╮\n", "│ \u001b[1mweight\u001b[0m (real) │\n", "│ Multiplicative event weight │\n", "╰─────────────────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "phimod = cs.Correction(\n", " name=\"phimod\",\n", " description=\"Phi-dependent tracking efficiency, or something?\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"phi\", type=\"real\"),\n", " cs.Variable(name=\"q\", type=\"int\", description=\"Particle charge\"),\n", " ],\n", " output=cs.Variable(\n", " name=\"weight\", type=\"real\", description=\"Multiplicative event weight\"\n", " ),\n", " data=cs.Category(\n", " nodetype=\"category\",\n", " input=\"q\",\n", " content=[\n", " cs.CategoryItem(\n", " key=1,\n", " value=cs.Formula(\n", " nodetype=\"formula\",\n", " variables=[\"phi\"],\n", " parser=\"TFormula\",\n", " expression=\"(1+0.1*sin(x+0.3))/(1+0.07*sin(x+0.4))\",\n", " ),\n", " ),\n", " cs.CategoryItem(\n", " key=-1,\n", " value=cs.Formula(\n", " nodetype=\"formula\",\n", " variables=[\"phi\"],\n", " parser=\"TFormula\",\n", " expression=\"(1+0.1*sin(x+0.31))/(1+0.07*sin(x+0.39))\",\n", " ),\n", " ),\n", " ],\n", " ),\n", ")\n", "rich.print(phimod)" ] }, { "cell_type": "code", "execution_count": 13, "id": "9c8d375c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0280774577481218" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phimod.to_evaluator().evaluate(1.23, 1)" ] }, { "cell_type": "markdown", "id": "aff9b1ef", "metadata": {}, "source": [ "Try evaluating the correction for a neutral particle (`charge=0`) What can we change to the above definition to allow it to return a reasonable value for neutral particles rather than an error?" ] }, { "cell_type": "markdown", "id": "398610dc", "metadata": {}, "source": [ "## Converting from histograms\n", "\n", "Often one wants to convert a histogram into a correction object. Here we show how to do that using histograms made with the [hist](https://hist.readthedocs.io/en/latest/) package, but any histogram that respects the [Unified Histogram Interface Plottable](https://uhi.readthedocs.io/en/latest/plotting.html#plotting) protocol (including, for example, ROOT TH1s) should work as well.\n", "\n", "Note: some of the conversion utilities require extra packages installed. The simplest way to ensure you have all dependencies is via the `pip install correctionlib[convert]` extras configuration.\n", "\n", "Here we create some mock data for two slightly different pt and eta spectra (say, from two different generators) and derive a correction to reweight one sample to the other." ] }, { "cell_type": "code", "execution_count": 14, "id": "8f8ad2ee", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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aJOPBgftVNuuXTtzTJuMdHiyhovmzZubP/S1M627888/3wxpfvzxx83vRR0arQFIf6dq/xltNho+fLhHv1Olv4u1L6kO0dZOwRpSPvroI/NaDUS6r4WDe+65Ry688EK544475LvvvnP97tVuIFqx0RYVvU4NQ+UtwlmK3zz6Rb/33nsmtRWkWbNm8sc//tGkyaJoU5EGGU2S2gNa+780atRI/v73v7vO0Z7U2pSkHy+++OLffQ5Ng+6JUCswGmIyMjJM2SyUaGBoPn6p2d89IUUc0RW9OjeQirsOALCrkydPmuqD/l7SgSUI7D3V399aiCju93fAfuN8+umnpsnnrbfeKvZc7ZGtTUiaCLWHtJa1tPnJnfW4sH4zGnys4V8AACC0BSzA6Fh3LYvpiKXiaJualrF0XLvStjhte9MJfbTDk1q+fLkJN9rWh5LZMq6LOKIjizxHKzbtJ6787/mdi6nu5En7iSv4dgAAgj/AaDOPdhKyWJ16dCy71atayz/ahpd/imSrg672wNZ2NG2T08faXnfXXXe5wolOnaxtfNrZafTo0aajk842+Nxzz5Xuqw1zGl58aebRc2kWAgCERIDRHsnWWgzK6uncv39/19oI2plIu9bopDr5aTOPHteVLbXPirZ/aYBx7zGtbV/aE3rIkCGmilOrVi0ZP358SA+h1vv122nvhqjl72hb9LnBNewNAIByCTCdOnUqdsSJBo3CwoaON9+wYUOx79OqVSvTjyZcaHgpSWdbq7kHAIBwwmKOAADAdhj3asPOtr50tHVXKaroDrwAANgFAcbmnW3paAsAoTeHF4rH3YMH/R/qwJQe3BUAQFCjDwwAACixXbt2maUGdL0knaF/5syZUhYIMAAAoMSys7OlcePGZu2mwmbLDwQCDAAAIer48ePSt29fiYuLk7p165oJYXU6lGHDhpnjOh/byJEjzSKReo4u7fPJJ5+4Xq/zu+mK1UuXLjXrEOqK2rqo46FDh1zn6OKQunikLgBZlkv6EGAAAPCBzoWmHXJLsllK+npf118eMWKErF27Vj744AOzJI/Or/b555+7jutq1jojvk4wu337drnttttMQNm7d++5a83ONqtiv/baa7JmzRpJT083oae80YnXhuhoCwD2m3jUH5OQ+jJ66fjx4/Lqq6/KwoULpXPnzua5efPmSWJiotnXIKKP9aP1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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import hist\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "dists = (\n", " hist.Hist.new.StrCat([\"gen1\", \"gen2\"], name=\"dataset\", growth=True)\n", " .Reg(20, 0, 100, name=\"pt\")\n", " .Reg(4, -3, 3, name=\"eta\")\n", " .Weight()\n", " .fill(\n", " dataset=\"gen1\",\n", " pt=np.random.exponential(scale=10.0, size=10000)\n", " + np.random.exponential(scale=10.0, size=10000),\n", " eta=np.random.normal(scale=1, size=10000),\n", " )\n", " .fill(\n", " dataset=\"gen2\",\n", " pt=np.random.exponential(scale=10.0, size=10000)\n", " + np.random.exponential(scale=15.0, size=10000),\n", " eta=np.random.normal(scale=1.1, size=10000),\n", " )\n", ")\n", "\n", "fig, ax = plt.subplots()\n", "dists[:, :, sum].plot1d(ax=ax)\n", "ax.legend(title=\"dataset\")" ] }, { "cell_type": "markdown", "id": "e9adbde5", "metadata": {}, "source": [ "Now we derive a correction as a function of $p_T$ and $\\eta$ to `gen2` such that it agrees with `gen1`. We’ll set it to 1 anywhere we run out of statistics for the correction, to avoid divide by zero issues." ] }, { "cell_type": "code", "execution_count": 15, "id": "b07843ef", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ColormeshArtists(pcolormesh=, cbar=, text=[])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "num = dists[\"gen1\", :, :].values()\n", "den = dists[\"gen2\", :, :].values()\n", "sf = np.where(\n", " (num > 0) & (den > 0),\n", " num / np.maximum(den, 1) * den.sum() / num.sum(),\n", " 1.0,\n", ")\n", "\n", "# a quick way to plot the scale factor is to steal the axis definitions from the input histograms:\n", "sfhist = hist.Hist(*dists.axes[1:], data=sf)\n", "sfhist.plot2d()" ] }, { "cell_type": "markdown", "id": "fc40af2c", "metadata": {}, "source": [ "Now we use `correctionlib.convert.from_histogram(...)` to convert the scale factor into a correction object" ] }, { "cell_type": "code", "execution_count": 16, "id": "7d54dc24", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 gen2_to_gen1 (v0)\n",
       "Reweights gen2 to agree with gen1\n",
       "Node counts: MultiBinning: 1\n",
       "╭──────────── ▶ input ─────────────╮ ╭──────────── ▶ input ────────────╮\n",
       "│ pt (real)                        │ │ eta (real)                      │\n",
       "│ pt                               │ │ eta                             │\n",
       "│ Range: [0.0, 100.0), overflow ok │ │ Range: [-3.0, 3.0), overflow ok │\n",
       "╰──────────────────────────────────╯ ╰─────────────────────────────────╯\n",
       "╭─── ◀ output ───╮\n",
       "│ out (real)     │\n",
       "│ No description │\n",
       "╰────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mgen2_to_gen1\u001b[0m \u001b[1m(\u001b[0mv0\u001b[1m)\u001b[0m\n", "Reweights gen2 to agree with gen1\n", "Node counts: \u001b[1mMultiBinning\u001b[0m: \u001b[1;36m1\u001b[0m\n", "╭──────────── ▶ input ─────────────╮ ╭──────────── ▶ input ────────────╮\n", "│ \u001b[1mpt\u001b[0m (real) │ │ \u001b[1meta\u001b[0m (real) │\n", "│ pt │ │ eta │\n", "│ Range: [0.0, 100.0), overflow ok │ │ Range: [-3.0, 3.0), overflow ok │\n", "╰──────────────────────────────────╯ ╰─────────────────────────────────╯\n", "╭─── ◀ output ───╮\n", "│ \u001b[1mout\u001b[0m (real) │\n", "│ \u001b[3mNo description\u001b[0m │\n", "╰────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import correctionlib.convert\n", "\n", "# without a name, the resulting object will fail validation\n", "sfhist.name = \"gen2_to_gen1\"\n", "sfhist.label = \"out\"\n", "gen2_to_gen1 = correctionlib.convert.from_histogram(sfhist)\n", "gen2_to_gen1.description = \"Reweights gen2 to agree with gen1\"\n", "# set overflow bins behavior (default is to raise an error when out of bounds)\n", "gen2_to_gen1.data.flow = \"clamp\"\n", "rich.print(gen2_to_gen1)" ] }, { "cell_type": "markdown", "id": "8437d322", "metadata": {}, "source": [ "Now we generate some new mock data as if it was drawn from `gen2` and reweight it with our correction. Let's see if our correction closes:" ] }, { "cell_type": "code", "execution_count": 17, "id": "bc2bbfc9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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DoyLCSQ+QoknbesrwGTnnwtGAAD7YjdqAABgOwQYAABgOwQYAABgO9TAwP5yz8iBhJ7m0Jl7UCS+WkWfEQAgxOiBge15Fu36KuAFAEQOAgwAALAdAgwAALAd+tsR/nKd/j9eatsz545LWTUaABC+CDAIf1MaBa1toi6Q1zCt6Eaehp2Ucp4cAKAiMIQEAABshx4YhKc4h8hTh/1rq8NGrp6XYftE4h0lNs3OPCay+O4gnSQAoKIQYBCedKjHxwaOJdLw4ut5GowAALbHEBIAALAdAgwAALAdAgwAALAdAgwAALAdinhhf1q0Oyazos8CAHAB0QMDAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAAAiP8CsWbNGunbtKvXq1ZOoqCiZP3++1+OWZcno0aOlbt26kpiYKO3bt5e9e/d6tfn++++lV69ekpKSItWrV5dHHnlETp8+7dVm+/bt0rZtW0lISJC0tDSZPHlyWb9HAABQ2QPMmTNn5JprrpFXXnml2Mc1aLz00ksyY8YM2bhxoyQlJUnHjh3l7Nmz7jYaXnbu3CnLli2TRYsWmVDUv39/9+OnTp2SDh06SIMGDWTLli3ywgsvyJgxY+T1118v6/cJAAAiSGygT+jUqZO5FEd7X6ZNmyajRo2Su+++29z397//XWrXrm16ah544AH54osvZMmSJfLZZ5/J9ddfb9q8/PLL0rlzZ5kyZYrp2Zk9e7bk5ubKm2++KfHx8dK0aVPZtm2bTJ061SvoeMrJyTEXzxAEAAAiU1BrYPbv3y9Hjx41w0Yu1apVk9atW8v69evNbb3WYSNXeFHaPjo62vTYuNrceuutJry4aC/Onj175Icffij2a0+cONF8LddFh50AAEBkCmqA0fCitMfFk952PabXtWrV8no8NjZWatSo4dWmuNfw/BrnGzlypGRmZrovhw4dCuJ3BgAAbD2EFK6qVKliLgAAIPIFtQemTp065vrYsWNe9+tt12N6ffz4ca/H8/PzzcwkzzbFvYbn17ADZ26+NHzyA3PRYwAAEIYB5tJLLzUBY8WKFV7FtFrb0qZNG3Nbr0+ePGlmF7l8/PHHUlhYaGplXG10ZlJeXp67jc5Yuuqqq+Siiy4K5ikDAIDKEGB0vRadEaQXV+GuHh88eNCsCzNkyBB59tlnZeHChZKRkSG9e/c2M4u6detm2jdp0kTuvPNO6devn2zatEk+/fRTGTRokJmhpO1Uz549TQGvrg+j063feecdmT59ugwdOjTY3z9QstwzImOqFV30GABg3xqYzZs3y+233+6+7QoVffr0kVmzZskf/vAHs1aMTnfWnpZbbrnFTJvWBelcdJq0hpZ27dqZ2Ufdu3c3a8e46CyipUuXysCBA6VVq1ZSs2ZNszheSVOoAQBA5RJwgElPTzfrvZREe2HGjRtnLiXRGUdz5szx+XVatGgha9eulXCi33d2XoHf7T3rXkqrgXHm+v+6AABUdhEzC+lC0PBy9eiPyvTc6589VxdUnMSoUxLbuIwnBgBAJUOAQeWV5/Rd25LrLP7YH3EO7Y4s+7kBAHwiwJTR5lHtxREf47ONDhu5el42j2onjviS3+5s53eSvmCCOU6I8/26CI7EV6/TcUH/Gk9pFNiLP3VYJD6pTOcFACgdAaaMNLz4CiQ/bR/ru31+rFcdEQAAKBkBBpWLDu38KHvwbnE4kktseuLkcUn/8BfmeFWnhZJa3XsLjJ/QYaZAe2oAAGVCgEHl4tm7pUM8voZ54hO9jxkSAoCwQYAJoez8bElu8uSPx7eKI77kv/YBAID/CDAhlOhRjOt5jPAJmM68kn8uzryzXseJOmvJl/xsdw9PomUJlUwAEDoEGFRanea18/l4lfxo9/8h9yy8W3JiC0t/0YZp5mpDfrYkVakalPMEAIR4M0cgkpyNji722K/nBrBiMwAgcPTAoFJJiEmQrN0lb3PhLVeSGz9rjk5/qbVM8T5bJ0afktgrpwThLAEApSHABMDsARWV666fkCjfdS2mTTHHpbVF6OhaPLvGdvWr7QlnlnReUBRg1o7oIKk+plyb9iePSecPCTAAcCEQYAJwtuCsJDcebY7T3yu69lf6u+mB/WQQErpIoL8LEGZ7LC5Y6kKE2r6UlZkBAMFDDQwAALAdemDK6MN7VkiNxKqlDgu5el5W3b9KEmM9FkY7n07RfaFoFdfEmISynhYAAJUCAaaMNIw4PJalL3d7ra9xbSzIXkhhQX9mWV9Mch8DAMIHAQYogda8HJjUhfcHAMIQNTAAAMB2CDAAAMB2CDAAAMB2qIEJIS3azeiTEcovAQBApUQPDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDAAAsB0CDFARcs+IjKlWdNFjAEBACDAAAMB2CDAAAMB2gh5gGjZsKFFRUT+5DBw40Dyenp7+k8cGDBjg9RoHDx6ULl26iMPhkFq1asnw4cMlPz8/2KcKhE6es2hoqMSL81zb3NLaelwsi58aAIhIbLDfhc8++0wKCgrct3fs2CH/93//J/fdd5/7vn79+sm4cePctzWouOhzNbzUqVNH1q1bJ0eOHJHevXtLXFycTJgwgR8abCHx1ev8DxtTGvn/wk8dFolPKvN5AUCkCHqAufjii71uT5o0SS6//HK57bbbvAKLBpTiLF26VHbt2iXLly+X2rVrS8uWLWX8+PEyYsQIGTNmjMTHxxf7vJycHHNxOXXqVNC+JwAAEOEBxlNubq784x//kKFDh5qhIpfZs2eb+zXEdO3aVZ5++ml3L8z69eulefPmJry4dOzYUR599FHZuXOnXHvttcV+rYkTJ8rYsWND+e0AvsWd60nMHrxbHI7kktvqsJGr52XYPpF4h39tAQChDzDz58+XkydPSt++fd339ezZUxo0aCD16tWT7du3m56VPXv2yPvvv28eP3r0qFd4Ua7b+lhJRo4caYKSZw9MWlpaCL4roAQeId0M8/g71KPhhWEhAAifAPO3v/1NOnXqZMKKS//+/d3H2tNSt25dadeunXz11VdmqKmsqlSpYi4AACDyhWwa9X//+19Tx/Kb3/zGZ7vWrVub63379plrHVY6duyYVxvX7ZLqZgAAQOUSsgAzc+ZMMwVaZxT5sm3bNnOtPTGqTZs2kpGRIcePH3e3WbZsmaSkpMjVV18dqtMFAACVfQipsLDQBJg+ffpIbOy5L6HDRHPmzJHOnTtLamqqqYF5/PHH5dZbb5UWLVqYNh06dDBB5aGHHpLJkyebupdRo0aZdWQYIkLE0JqXMZkVfRYAYFshCTA6dKSL0T388MNe9+sUaH1s2rRpcubMGVNk2717dxNQXGJiYmTRokVm1pH2xiQlJZkg5LluDAAAqNxCEmC0F8UqZhEvDSyrV68u9fk6S2nx4sWhODUAABAB2AsJAADYDgEGAADYDgEGAADYDgEGAADYDgEmlHLPiIypVnTRYwAAEBQEGAAAYDsEGAAAYDsh3cwx4niubaNDQrGl5L9cZ/HHpbUFAAA+EWACkXcuZCROb+wdaEozpVFAXwoAAJSMISSgAjhz86Xhkx+Yix4DAAJDD0wZZf9uqziq1S59WMjV8zJsn0i8w78Xj/OzHQAAlRQBpqw0ZOiOwv6KD7A9AAAoEQEGCAFnboEkxpY8NOQ5bFTqEFJuvrj65HST1KhgnSQA2BgBBgiBts+vFLHiS24QlSvJTUabw+ufG+ezbaKclS8Sio6z8wrEUSXopwsAtkOACSUdMhqTGdIvAQBAZUSAAYIkwWNdoC1Pt5fE2MQS255wZknnBUXHa0ekS6ojucS2ztOnRF7ixwQAnggwQJBERXlUp0TlikTF+Gic532s7UtqGp0rzh9fW2tgAAAEGCAk0t9N9/m4VRgrUT922HR6v6NERZdSyNswzVytKjgrzGUDABayAwAANsQQEhAkWvOysedGv9pm52e7e2lWP7DcZ73M95nHpdMHXfk5AYAHAgwQxBoYRxlWUdbw4ut52bE/zqH2h24yOqFe0fFTh1k8EUDEYi8kAABgO/TAABVAe1wy+mTw3gNAGRFgADvJcxYNE/naQLS4Y3/oMJbnVHAACGMEGMBGEl+9TheD8a+xayd0f1EzA8BGqIEBAAC2Qw8MEO48ZihlD94tDh/bDphhI1fPy7B9IvGlzIrybA8ANkKAAcKdZ12KbhCqF39oePG3LQDYDENIAADAduiBASKJ9riMyazoswCAkKMHBgAA2A4BBgAA2A4BBgAA2A4BBgAA2A4BBgAA2A4BBgAA2A4BBgAA2A4BBgAA2E7QA8yYMWMkKirK69K4cWP342fPnpWBAwdKamqqVK1aVbp37y7Hjh3zeo2DBw9Kly5dxOFwSK1atWT48OGSn58f7FMFAAA2FZKVeJs2bSrLly8/90Viz32Zxx9/XD744AN57733pFq1ajJo0CC599575dNPPzWPFxQUmPBSp04dWbdunRw5ckR69+4tcXFxMmHChFCcLgAAsJmQBBgNLBpAzpeZmSl/+9vfZM6cOXLHHXeY+2bOnClNmjSRDRs2yE033SRLly6VXbt2mQBUu3ZtadmypYwfP15GjBhhenfi4+OL/Zo5OTnm4nLq1KlQfGsAACBSa2D27t0r9erVk8suu0x69eplhoTUli1bJC8vT9q3b+9uq8NL9evXl/Xr15vbet28eXMTXlw6duxoAsnOnTtL/JoTJ040PTquS1paWii+NQAAEIkBpnXr1jJr1ixZsmSJvPbaa7J//35p27atZGVlydGjR00PSvXq1b2eo2FFH1N67RleXI+7HivJyJEjTQ+P63Lo0KFgf2sAACBSh5A6derkPm7RooUJNA0aNJB3331XEhMTJVSqVKliLgAAIPKFfBq19rZceeWVsm/fPlMXk5ubKydPnvRqo7OQXDUzen3+rCTX7eLqagAAQOUT8gBz+vRp+eqrr6Ru3brSqlUrM5toxYoV7sf37NljamTatGljbut1RkaGHD9+3N1m2bJlkpKSIldffXWoTxcAAFTGIaRhw4ZJ165dzbDR4cOH5ZlnnpGYmBjp0aOHKa595JFHZOjQoVKjRg0TSh577DETWnQGkurQoYMJKg899JBMnjzZ1L2MGjXKrB3DEBEAAAhJgPnf//5nwsqJEyfk4osvlltuucVMkdZj9eKLL0p0dLRZwE6nPesMo1dffdX9fA07ixYtkkcffdQEm6SkJOnTp4+MGzeOnxgAAAhNgJk7d67PxxMSEuSVV14xl5Jo783ixYuDfWoAACBCsBcSAACwHQIMAACwHQIMAP/knhEZU63ooscAUIEIMAAAwHZCspkjABvKdfr/eGltPcU5RKKiyn5eAFAMAgwQQZx5Tmk9p7U53thzozg0PPhrSqPQtH3qsEh8kv/tAcAPBBjARrLzs8WZF1Pi4yecp72PS8sv+dnu3pFEyxL6SQDYBQEGsJFO89r5fNwqjJWoHyvbOr3fUaKi80t/0YZp5mrjfSvFEetjw1UdNnL1vAzbJxLv8K8tAIQAAQaAYWl48XeoR8MLw0IAKhABBghzCTEJkrXb3600ciW58bPm6PSXT2rS8Nk6KjpXql5Z1P5sfqEk+W4OAGGDAAOEuSitUbH8TBZeRSzxpT7PKgzgRLTHZUxmAE8AgNAhwABhLjEuRnaN6+hX2xPOLOm8oOh47Yh0SXUkl9L+tLs9ANgJAQawQQ+MI97f/1WTJeuLSeYotUdyqc/Lzi95RhMAhDNW4gUAALZDDwwQQbTH5cCkLhV9GgAQcvTAAKh4bBQJIEAEGAAAYDsMIQEILX82fmSjSAABIsAACK1AtxRgo0gAfmAICQAA2A49MACCL84h8tRh/9uzUSSAABFgAASfbn9Q1s0e2SgSgB8YQgIAALZDDwyAisdGkQACRA8MAACwHQIMAACwHQIMAL8485zS/K3m5qLHAFCRqIEBYGTnZ4szL8bn48UdlyYxNlGidFYSAAQRAQaA0WleO7/fifR30/1uu7HnRnHoujAAEEQMIQHwi1UYW+wxAFQE/hUCKrGEmATJ2j3OHG95ur0kxpU8hHTCeVo6zy/qpfnw3o8k1VG1xLY6xBRILw0ABIoAA1RipjbFijfHVmG8iFVygBErzvv4x+cV37bg3KFlBedkAcADAQaAcf2zy/14JyaZ/7b9Yp3vZlG5kty46PBsfqEk+cg6AFAW1MAAAADboQcGqMS05mXXuI5+tXXm5sv1z64wx5tHtRNHfKzvepkFQTtNAPgJAgxQyWtgfAWRkuhzfD0vO99HLQ0ABAEBBoBfNLAcmNSFdwtAWKAGBgAA2E7QA8zEiRPlhhtukOTkZKlVq5Z069ZN9uzZ49UmPT3ddF17XgYMGODV5uDBg9KlSxdxOBzmdYYPHy75+fnBPl0AAGBDQR9CWr16tQwcONCEGA0cTz31lHTo0EF27dolSUlJ7nb9+vWTceOKFtBSGlRcCgoKTHipU6eOrFu3To4cOSK9e/eWuLg4mTBhQrBPGQAAVPYAs2TJEq/bs2bNMj0oW7ZskVtvvdUrsGhAKc7SpUtN4Fm+fLnUrl1bWrZsKePHj5cRI0bImDFjJD6eRSUAAKjMQl4Dk5mZaa5r1Kjhdf/s2bOlZs2a0qxZMxk5cqQ4nU73Y+vXr5fmzZub8OLSsWNHOXXqlOzcubPYr5OTk2Me97wAAIDIFNJZSIWFhTJkyBC5+eabTVBx6dmzpzRo0EDq1asn27dvNz0rWifz/vvvm8ePHj3qFV6U67Y+VlLtzdixY0P57QCwm9wzIhPqFR0/dVgk/twwNgB7C2mA0VqYHTt2yCeffOJ1f//+/d3H2tNSt25dadeunXz11Vdy+eWXl+lraS/O0KFD3be1ByYtLa0cZw8g7OU6/X+8tLae4hy6SE7ZzwuAfQPMoEGDZNGiRbJmzRq55JJLfLZt3bq1ud63b58JMFobs2nTJq82x44dM9cl1c1UqVLFXABUIlMahaYtvTVA5auB0Z1nNbzMmzdPPv74Y7n00ktLfc62bdvMtfbEqDZt2khGRoYcP37c3WbZsmWSkpIiV199dbBPGQAAVPYeGB02mjNnjixYsMCsBeOqWalWrZokJiaaYSJ9vHPnzpKammpqYB5//HEzQ6lFixamrU671qDy0EMPyeTJk81rjBo1yrw2vSxA5NF9lq4e/ZE51r2ZfG5voMM72kPiDx02cvW8DNsnEu/wry2AyhdgXnvtNfdidZ5mzpwpffv2NVOgdXr0tGnT5MyZM6ZOpXv37iaguMTExJjhp0cffdT0xuj6MX369PFaNwaAPThzCyQxNr/UAFPcccmquDej1IUw/aLhhSJeIGLEhmIIyRcNLLrYXWl0ltLixYuDeGYAKkLb51eKWP6v3eTa8dofpfbWaGAZU7SUA4DIwl5IAADAdtiNGkDQJcSe+9vok5E3S2Jsos/2PzhPyz0fdDDH87oslYscVX0OSZleHT96fAFELgIMgKDzrEvpNK9dqe2twliJ+jHzdPt3Z4mK9l0Hk9y46PpsQXtJkrhyni0AO2IICUCF8wwspYUXAFD0wAAIOh0y2thzo9/ts/OzJf3dopmLq+5f5XPI6fvs03716gCIbAQYACEZQnLoei1+0rYZfTL8apudV1COMwMQKRhCAgAAtkMPDACcz5+NHwNZ5dcTG0UCQUGAAYDzBbqlABtFAhccQ0gAAMB26IEBgEA3iVRsFAlUKAIMANvS6dfOvJjgveCPC/DpNG6/N4lUbBQJXHAEGAC2Far1YHQNm1KngbNRJFChqIEBgHCSe0ZkTLWiix4DKBY9MABsJSEmQbJ2jzPHW55uL4lxJQ8hnXCels7zi3ppFndbIak+Non0XA24Qqdoez7uz3RuF6Zno5IhwACwFVObYsWbY6swXsQqOcAkxqRI1heTio67p4hYPv7Js86t8BvSXa4DmXLN9GygRAQYALZ1/bPLA2i7wneDqFz3Ltc/nD1TahFvIPs3SX72uQJhy5IAyoMBlIAAAwDlLA72a+ipYZq52njfSnH4CjtlnZ7N6sGoZAgwAGxFa152jevoV1tnbr6752XzqHbiiI/1XS+zQEJPa1V8zXAq6+wmVg9GJUOAAWC/na59BBFP2u7ApC5+tbWsJL+LgwMdQrpgBcJAJUKAAYAAi4NNm4Jod4GwVZAkElMBBcKsHoxKjAADAOUoDg60QPhsfqEkFeWk8tPQpUNO/mLxPUQQFrIDAAC2Qw8MAARYHBy2BcJAJUKAAYAAi4MDLRDOzg/ihpMADAIMAKD82x/4u3bN+dgCAWVEgAGAC0inVDvzgt8jo9O4S1s9OOy2P1BPHQ6sEBn4EQEGAMJ4lV9/beixQZIIAqhECDAAEEaswliJis7/yXFpgjo9O9A1ZgIdQvJsD5QRAQYAQiwhJsG9ym+pzDp3riSSK752foyKzpWqVz4bjFMs3xoz5VlfJlT1NdTWRDwCDACEmM5Y2jW2axmmZ3fyY3p2iALMhRKq+ppAi4kDQTgKCwQYALDp/k1Mz/YhlENUFB6HBQIMAODCCnQPJ39RW1OpEGAAIAJ87zxdapvs3AK5ZdKn5viTJ2+WxHj/pnNflJAk0dFB3Hkm0D2c/EXhcaVCgAGACHDvBx1KbaOzmpKbFM1qunOe/zOcFt+9VlIdVSVUWzgEbf2acCk8LitqawJCgAEA+HTrCx+JVRgflJlT59s55i5JqhJnr59AqOprqK0JCAEGAGxKh3ZW3bcuZENSrl6d0qZql3XtGnW2oL0kic0CTKiEqmcnlCqw14gAAwA2pXUpqY7kkLx2Qqz/NS+egSWQ8OJv7U5Zzz/oWyv84SsJSWiZ3sIcJk5pFEjnVYWyLEvm7c6X9Ol7pEadtAo5h7AOMK+88oq88MILcvToUbnmmmvk5ZdflhtvvLGiTwsAIp4jziEbe24MyWt/n33avaVCabU75endsY2GRQFg1X//J4mWGY8rnmWJa2Ubp/4nmAEtwNfecbxQur+bLckfNpXBgwfL448/LjVq1JALKWwDzDvvvCNDhw6VGTNmSOvWrWXatGnSsWNH2bNnj9SqVauiTw8AIn/tGh0eCIHsvIJAzkSyvphkjqpe9XTQziEcg1F6g0vELrIlW0ROS4cOHWTq1Kkyffr0Cx5koiztBwpDGlpuuOEG+fOf/2xuFxYWSlpamjz22GPy5JNPlvr8U6dOSbVq1SQzM1NSUlKCck4nTh6V9AX/Z45X3b1MUqvXCcrrAkBlov+e/3D2TNBf15lbIG2fX+lf43IUHlfUdhDhFLqyD2TLV2O+ki1btsgll1wiU6ZMMaMmMTEx5Q4y/n5+h2UPTG5urnlTRo4c6TXW2759e1m/fn2xz8nJyTEXF/3GXW9EsGSdypKC7AL3cVx0iJapBoAIF4qy3ZSoKNnwxG1iJ86cfEn/U8X3/kgAoSshOksK4p9339ZRkcmTJ8uwYcNMkHH1yAwYMMB0OmgYCYTrc7vU/hUrDH3zzTd61ta6deu87h8+fLh14403FvucZ555xjyHC+8BvwP8DvA7wO8AvwNyQd6DLVu2/OTzOCMjw7r44ovL/dqHDh3ymRXCsgemLLS3RmtmPLsov//+e0lNTQ1qJbomQx3KOnToUNCGpsB7XZH4neZ9jiT8Pl8Yn376qXTu3NnrvuPHj5uJN6+++qoZSho+fHiZemC05yUrK0vq1avns11YBpiaNWuab/7YsWNe9+vtOnWKrzupUqWKuXiqXr16yM5RwwsB5sLgveZ9jiT8PvM+R4KkpKQSg4t2JpS3mNef0BPEzS2CJz4+Xlq1aiUrVhRtKe/qUdHbbdq0qdBzAwAARSZMmCCXXnqp/OUvfzHB5cCBAzJ+/PgLMhMpLHtglL4Rffr0keuvv96s/aLTqM+cOSO//vWvK/rUAACo1OLiisqwly5dGpQel4gKML/61a/k22+/ldGjR5uF7Fq2bClLliyR2rVrV+h56TDVM88885PhKvBe2xW/07zPkYTf5wvj2muvlfvvv990LtStW1cqQtiuAwMAAGCrGhgAAABfCDAAAMB2CDAAAMB2CDAAAMB2CDAB0s2qGjZsKAkJCWbDyU2bNoXmJ1NJTJw40WzamZycbPbT6Natm9lx3NPZs2dl4MCBZlXlqlWrSvfu3X+yyCECM2nSJLNC9ZAhQ3ifg+ybb76RBx980Py+JiYmSvPmzWXz5s3ux3XehM6u1Jkb+rju8bZ3795gn0ZEKygokKefftqsP6Lv4eWXX27WHvGck8L7XDZr1qyRrl27mlVw9d+I+fPnez3uz/uqq+D36tXLLNqoC8o+8sgjcvr0aQm68u1aVLnMnTvXio+Pt958801r586dVr9+/azq1atbx44dq+hTs62OHTtaM2fOtHbs2GFt27bN6ty5s1W/fn3r9OnT7jYDBgyw0tLSrBUrVlibN2+2brrpJuvnP/95hZ63nW3atMlq2LCh1aJFC2vw4MHu+3mfy+/777+3GjRoYPXt29fauHGj9fXXX1sfffSRtW/fPnebSZMmWdWqVbPmz59v/ec//7F+8YtfWJdeeqmVnZ0dhDOoHJ577jkrNTXVWrRokbV//37rvffes6pWrWpNnz7d3Yb3uWwWL15s/fGPf7Tef/99sx/RvHnzvB7353298847rWuuucbasGGDtXbtWqtRo0ZWjx49rGAjwARAN5IcOHCg+3ZBQYFVr149a+LEiUH/wVRWx48fN//TrF692tw+efKkFRcXZ/6Bcvniiy9Mm/Xr11fgmdpTVlaWdcUVV1jLli2zbrvtNneA4X0OjhEjRli33HJLiY8XFhZaderUsV544QX3ffreV6lSxfrnP/8ZpLOIfF26dLEefvhhr/vuvfdeq1evXuaY9zk4zg8w/ryvu3btMs/77LPP3G0+/PBDKyoqymzUHEwMIfkpNzdXtmzZYrrLXKKjo83t9evXB79rrJLKzMw0164VHfU9z8vL83rfGzduLPXr1+d9LwMdiuvSpYvX+8n7HDwLFy40q4ffd999ZkhUF/t644033I/v37/fLMzp+f7rni86HM2/I/77+c9/braW+fLLL83t//znP/LJJ59Ip06deJ9DyJ/fX73WYSP9/8BF2+vn5caNGyvHSrzh5rvvvjPjruevBKy3d+/eXWHnFUl0vyutybj55pulWbNm5j79n0X3xjp/Y0593/Ux+G/u3LmydetW+eyzz37yGO9zcHz99dfy2muvmaXVn3rqKfNe//73vze/w7o1iut3trh/R/h99t+TTz5pdp3WP2Z080D9t/m5554zdReu32fe5+Dz533Vaw3vnmJjY80fpcH+HSfAIKx6B3bs2GH+kkJwHTp0SAYPHizLli0zBegIXQjXvzx1gzulPTD6Oz1jxgwTYBAc7777rsyePVvmzJkjTZs2lW3btpk/frTwlPe58mAIyU81a9Y0Sf/82S96u06dOqH42VQqgwYNkkWLFsnKlSvlkksucd+v760O3508edKrPe97YHQoTre8v+6668xfQ3pZvXq1vPTSS+ZY/4LifS4/nZlx9dVXe93XpEkTOXjwoDl2/VvBvyPlM3z4cNML88ADD5hZXg899JDZTFBnNfI+h44/v796rf/WeMrPzzczk4L9WUmA8ZN2Abdq1cqMu3r+taW327RpE9QfSmWidWIaXubNmycff/yxmRbpSd9z3fXU833Xadb6gcD77r927dpJRkaG+UvVddGeAu1ydx3zPpefDn+evwyA1mk0aNDAHOvvt/4j7vn7rEMhWhvA77P/nE6nqanwpH9g6r/JvM+h48/vr17rH5z6R5OL/tuuPxutlQmqoJYEV4Jp1FptPWvWLFNp3b9/fzON+ujRoxV9arb16KOPmil5q1atso4cOeK+OJ1Or+m9OrX6448/NtOo27RpYy4oH89ZSLzPwZuiHhsba6b57t2715o9e7blcDisf/zjH17TUPXfjQULFljbt2+37r77bqZRB6hPnz7Wz372M/c0ap3yW7NmTesPf/gD73MQZip+/vnn5qIRYerUqeb4v//9r9+/vzqN+tprrzVLCXzyySdm5iPTqMPAyy+/bD5MdT0YnVat89xRdvo/SHEXXRvGRf/H+N3vfmdddNFF5sPgnnvuMSEHwQ0wvM/B8e9//9tq1qyZ+WOncePG1uuvv+71uE5Fffrpp63atWubNu3atbP27NkTpK9eOZw6dcr87uq/xQkJCdZll11m1i7Jyclxt+F9LpuVK1cW+2+yhkZ/39cTJ06YwKJr86SkpFi//vWvTTAKtij9T3D7dAAAAEKLGhgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAAGA7BBgAttawYUOZNm1aRZ8GgAuMAAMAAGyHvZAAhLX09HRp1qyZOX777bclLi5OHn30URk3bpzcfvvtsnr1aq/2bO8GVA70wAAIe2+99ZbExsbKpk2bZPr06TJ16lT561//Ku+//75ccsklJswcOXLEXABUDrEVfQIAUJq0tDR58cUXJSoqSq666irJyMgwt/v16ycxMTGSnJwsderU4Y0EKhF6YACEvZtuusmEF5c2bdrI3r17paCgoELPC0DFIcAAAADbIcAACHsbN270ur1hwwa54oorzPBRfHw8PTFAJUSAARD2Dh48KEOHDpU9e/bIP//5T3n55Zdl8ODB7nVg1qxZI99884189913FX2qAC4QplEDCPtp1E2bNpXCwkKZM2eO6XXRadTPPvusqYvR3pjf/va3Jtzk5OQwjRqoJAgwAMI+wLRs2ZLVdgF4YQgJAADYDgEGAADYDkNIAADAduiBAQAAtkOAAQAAtkOAAQAAtkOAAQAAtkOAAQAAtkOAAQAAtkOAAQAAtkOAAQAAYjf/H0YyF0KjhNH8AAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ptvals = np.random.exponential(scale=10.0, size=10000) + np.random.exponential(\n", " scale=15.0, size=10000\n", ")\n", "etavals = np.random.normal(scale=1.1, size=10000)\n", "\n", "dists.fill(\n", " dataset=\"gen2rwt\",\n", " pt=ptvals,\n", " eta=etavals,\n", " weight=gen2_to_gen1.to_evaluator().evaluate(ptvals, etavals),\n", ")\n", "\n", "fig, ax = plt.subplots()\n", "dists[:, :, sum].plot1d(ax=ax)\n", "ax.legend(title=\"dataset\")" ] }, { "cell_type": "markdown", "id": "3d8b6a03", "metadata": {}, "source": [ "## Polynomial fits\n", "\n", "It is apparent from the plot of the 2D correction factor that we do not have sufficient sample statistics to derive a smooth correction. One approach to improve the quality is to fit it to a polynomial. A utility method, `correctionlib.convert.ndpolyfit` allows to fit an arbitrary-dimensional polynomial to a set of data points and return a correction object representing the result." ] }, { "cell_type": "code", "execution_count": 18, "id": "646de98e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 gen2_to_gen1_poly (v1)\n",
       "Fit to polynomial of order 2,2\n",
       "Fit status: The unconstrained solution is optimal.\n",
       "chi2 = 3.8810467430391604, P(dof=71) = 1.000\n",
       "Node counts: Formula: 1\n",
       "╭───────── ▶ input ──────────╮ ╭───────── ▶ input ──────────╮\n",
       "│ pt (real)                  │ │ eta (real)                 │\n",
       "│ No description             │ │ No description             │\n",
       "│ Range: unused, overflow ok │ │ Range: unused, overflow ok │\n",
       "╰────────────────────────────╯ ╰────────────────────────────╯\n",
       "╭─── ◀ output ───╮\n",
       "│ output (real)  │\n",
       "│ No description │\n",
       "╰────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mgen2_to_gen1_poly\u001b[0m \u001b[1m(\u001b[0mv1\u001b[1m)\u001b[0m\n", "Fit to polynomial of order \u001b[1;36m2\u001b[0m,\u001b[1;36m2\u001b[0m\n", "Fit status: The unconstrained solution is optimal.\n", "chi2 = \u001b[1;36m3.8810467430391604\u001b[0m, \u001b[1;35mP\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdof\u001b[0m=\u001b[1;36m71\u001b[0m\u001b[1m)\u001b[0m = \u001b[1;36m1.000\u001b[0m\n", "Node counts: \u001b[1mFormula\u001b[0m: \u001b[1;36m1\u001b[0m\n", "╭───────── ▶ input ──────────╮ ╭───────── ▶ input ──────────╮\n", "│ \u001b[1mpt\u001b[0m (real) │ │ \u001b[1meta\u001b[0m (real) │\n", "│ \u001b[3mNo description\u001b[0m │ │ \u001b[3mNo description\u001b[0m │\n", "│ Range: \u001b[1;31munused\u001b[0m, overflow ok │ │ Range: \u001b[1;31munused\u001b[0m, overflow ok │\n", "╰────────────────────────────╯ ╰────────────────────────────╯\n", "╭─── ◀ output ───╮\n", "│ \u001b[1moutput\u001b[0m (real) │\n", "│ \u001b[3mNo description\u001b[0m │\n", "╰────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "centers = np.meshgrid(*[ax.centers for ax in sfhist.axes], indexing=\"ij\")\n", "\n", "gen2_to_gen1_poly, fit = correctionlib.convert.ndpolyfit(\n", " points=[c.flatten() for c in centers],\n", " values=sfhist.values().flatten(),\n", " weights=1 / sfhist.variances().flatten(),\n", " varnames=[ax.name for ax in sfhist.axes],\n", " degree=(2, 2),\n", ")\n", "gen2_to_gen1_poly.name = \"gen2_to_gen1_poly\"\n", "rich.print(gen2_to_gen1_poly)" ] }, { "cell_type": "markdown", "id": "c84435af", "metadata": {}, "source": [ "Let's check the closure of this method to the previous one:" ] }, { "cell_type": "code", "execution_count": 19, "id": "b9fbd038", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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OaatjhrSYl0NYJzrV3Q49pIlS2To0KHSvHlzqVy5sjz//PMMoQ6wIt7g0JxalbTQcElzE8A40+AlLdRN26wLWT0AQAAFMBqEuEu9F5SW19FH33//fYHvo8W93333nbenhzIiKizEsb/52Y5ua1WSEs7LyQU5+2vGtJeY2PLuh1x/5dtzBQCUwXlggKIOo44ODxXRLR/ZTsGOBj6mfT6iw0McQ64BANbFYo4AAMByyMDA8oKjo6Vrz1fN/q7o/Ce8AwCUHQQwsL70JDkY2dfsJqcfEgm3zwoDACir6EKC5TnXvLirfwEAlB0EMAAAwHIIYAAAgOWQb0fpl57s+fEC2zoNomYxRwCwLAIYlH6vXuGztpFZmnT8Y0HQjAKCHQBAqUUXEgAAsBwyMCidwqJFnv7ds7babWTPvIzaLxKe/1wwqUd/Ffmsj49OEgBQUghgUDrpUgJu1j/KlwYv7p4XFlWk0wIAlA50IQEAAMshgAEAAJZDAAMAACyHGhhYn9a8jEso6bMAABQjMjAAAMByCGAAAIDlEMAAAADLIYABAACWQwADAAAshwAGAABYDgEMAACwHAIYAABgOQQwAADAcghgAACA5RDAAAAAyyGAAQAAlkMAAwAALIcABgAAWA4BDAAAsBwCGAAAYDkEMAAAoOwHMKtXr5YePXpIfHy8BAUFybx581yO22w2ef7556V69eoSFRUlHTt2lH379rm0OX36tPTr108qVKggFStWlIEDB0piYqJLm23btsmNN94okZGRUqNGDZkyZUphf0YAABDoAUxSUpJcc8018vbbb+d5XAONN998U2bMmCEbNmyQmJgY6dy5s6SmpjraaPCyc+dOWbp0qSxYsMAERYMHD3YcP3funHTq1Elq1aolmzdvlldeeUXGjRsn7777bmF/TgAAUIaEevuErl27mi0vmn15/fXX5dlnn5Xbb7/dPPZ///d/UrVqVZOpueeee2T37t2yePFi2bhxo1x77bWmzVtvvSXdunWTV1991WR2Zs2aJenp6fLhhx9KeHi4NGzYULZu3SpTp051CXRKu+zkZNnTrLnZr79lswRHR5f0KQEAUCb4tAbmwIEDcvToUdNtZBcbGystWrSQ9evXm/t6q91G9uBFafvg4GCTsbG3adu2rQle7DSLs2fPHjlz5kye752WlmYyN84bAAAom3wawGjwojTj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IiLM5qxixYp+O0cNXghgigfXmutclvB55jqXBTExMfkGLppMKGoxrydBj4+mP/St8PBwad68uSxfvtwlo6L3W7VqVaLnBgAAckycOFHq1Kkj//jHP0zgcvDgQZkwYUKxDKkulRkYpRdCi3avvfZaM/eLDqNOSkqSBx98sKRPDQCAgBYWljPee8mSJT7JuJSpAObuu++WEydOyPPPP28msmvatKksXrxYqlatWqLnpd1UL7zwwkXdVeBaWxWfaa5zWcLnuXj8+c9/lrvuusskF6pXry4loVQOowYAALBcDQwAAIA7BDAAAMByCGAAAIDlEMAAAADLIYDxkq7xULt2bYmMjDQLTv7www/++c0EiEmTJplFO3VJdl1lXNe+0hXHnaWmpsrQoUPNrMrlypUzq5/mnuQQ3pk8ebKZoXr48OFcZx/77bff5L777jOf16ioKGncuLFs2rTJcVzHTejoSh25occ7duwo+/bt8/VplPk1j5577jkz/4hew7p165q5R5zHpHCdC2f16tXSo0cPMwuu/hsxb948l+OeXFedBb9fv35m0kadUHbgwIGSmJgoPle0VYsCy5w5c2zh4eG2Dz/80LZz507boEGDbBUrVrQdO3aspE/Nsjp37mybOXOmbceOHbatW7faunXrZqtZs6YtMTHR0WbIkCG2GjVq2JYvX27btGmTrWXLlrYbbrihRM/byn744Qdb7dq1bU2aNLENGzbM8TjXuehOnz5tq1Wrlu2BBx6wbdiwwfbLL7/YvvnmG9v+/fsdbSZPnmyLjY21zZs3z/bf//7Xdtttt9nq1KljS0lJ8cEZBIaXX37ZFhcXZ1uwYIHtwIEDts8++8xWrlw52xtvvOFow3UunEWLFtmeeeYZ2+eff27WI/riiy9cjntyXbt06WK75pprbN9//73tu+++s11xxRW2e++91+ZrBDBe0IUkhw4d6riflZVli4+Pt02aNMnnv5hAdfz4cfM/zapVq8z9s2fP2sLCwsw/UHa7d+82bdavX1+CZ2pN58+ft1155ZW2pUuX2m666SZHAMN19o0xY8bY2rRpk+/x7OxsW7Vq1WyvvPKK4zG99hEREbZPP/3UR2dR9nXv3t320EMPuTzWu3dvW79+/cw+19k3cgcwnlzXXbt2medt3LjR0ebrr7+2BQUFmYWafYkuJA+lp6ebZcM1XWYXHBxs7q9fv973qbEAlZCQYG7tMzrqNc/IyHC57g0aNJCaNWty3QtBu+K6d+/ucj25zr4zf/58M3v4nXfeabpEdbKv9957z3H8wIEDZmJO5+uva75odzT/jnjuhhtuMEvL7N2719z/73//K2vWrJGuXbtynf3Ik8+v3mq3kf5/YKft9ftyw4YNgTETb2lz8uRJ0++aeyZgvf/TTz+V2HmVJbreldZktG7dWho1amQe0/9ZdG2s3Atz6nXXY/DcnDlzZMuWLbJx48aLjnGdfeOXX36R6dOnm6nVn376aXOtH3/8cfMZ1qVR7J/ZvP4d4fPsuaeeesqsOq1/zOjigfpv88svv2zqLuyfZ66z73lyXfVWg3dnoaGh5o9SX3/GCWBQqrIDO3bsMH9JwbcOHz4sw4YNk6VLl5oCdPgvCNe/PHWBO6UZGP1Mz5gxwwQw8I25c+fKrFmzZPbs2dKwYUPZunWr+eNHC0+5zoGDLiQPVa5c2UT6uUe/6P1q1ar543cTUB599FFZsGCBrFixQi677DLH43pttfvu7NmzLu257t7Rrjhd8r5Zs2bmryHdVq1aJW+++abZ17+guM5FpyMzrr76apfHrrrqKjl06JDZt/9bwb8jRTN69GiThbnnnnvMKK/777/fLCaooxq5zv7jyedXb/XfGmeZmZlmZJKvvysJYDykKeDmzZubflfnv7b0fqtWrXz6SwkkWiemwcsXX3wh3377rRkW6Uyvua566nzddZi1fiFw3T3XoUMH2b59u/lL1b5ppkBT7vZ9rnPRafdn7mkAtE6jVq1aZl8/3/qPuPPnWbtCtDaAz7PnkpOTTU2FM/0DU/9N5jr7jyefX73VPzj1jyY7/bddfzdaK+NTPi0JDoBh1Fpt/dFHH5lK68GDB5th1EePHi3pU7Oshx9+2AzJW7lype3IkSOOLTk52WV4rw6t/vbbb80w6latWpkNReM8Conr7Lsh6qGhoWaY7759+2yzZs2yRUdH2/75z3+6DEPVfze+/PJL27Zt22y33347w6i9NGDAANuf/vQnxzBqHfJbuXJl25NPPsl19sFIxR9//NFsGiJMnTrV7P/6668ef351GPWf//xnM5XAmjVrzMhHhlGXAm+99Zb5MtX5YHRYtY5zR+Hp/yB5bTo3jJ3+j/HII4/YLrnkEvNl0KtXLxPkwLcBDNfZN7766itbo0aNzB87DRo0sL377rsux3Uo6nPPPWerWrWqadOhQwfbnj17fPTugeHcuXPms6v/FkdGRtouv/xyM3dJWlqaow3XuXBWrFiR57/JGjR6el1PnTplAhadm6dChQq2Bx980ARGvhak//FtTgcAAMC/qIEBAACWQwADAAAshwAGAABYDgEMAACwHAIYAABgOQQwAADAcghgAACA5RDAAAAAyyGAAQAAlkMAA8DSateuLa+//npJnwaAYkYAAwAALIe1kACUau3atZNGjRqZ/U8++UTCwsLk4YcflvHjx0v79u1l1apVLu1Z3g0IDGRgAJR6H3/8sYSGhsoPP/wgb7zxhkydOlXef/99+fzzz+Wyyy4zwcyRI0fMBiAwhJb0CQBAQWrUqCHTpk2ToKAgqV+/vmzfvt3cHzRokISEhEj58uWlWrVqXEgggJCBAVDqtWzZ0gQvdq1atZJ9+/ZJVlZWiZ4XgJJDAAMAACyHAAZAqbdhwwaX+99//71ceeWVpvsoPDycTAwQgAhgAJR6hw4dkpEjR8qePXvk008/lbfeekuGDRvmmAdm9erV8ttvv8nJkydL+lQBFBOGUQMo9cOoGzZsKNnZ2TJ79myTddFh1C+99JKpi9FszF//+lcT3KSlpTGMGggQBDAASn0A07RpU2bbBeCCLiQAAGA5BDAAAMBy6EICAACWQwYGAABYDgEMAACwHAIYAABgOQQwAADAcghgAACA5RDAAAAAyyGAAQAAlkMAAwAAxGr+H5pcWM398iEOAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dists.fill(\n", " dataset=\"gen2rwt_poly\",\n", " pt=ptvals,\n", " eta=etavals,\n", " weight=gen2_to_gen1_poly.to_evaluator().evaluate(ptvals, etavals),\n", ")\n", "\n", "\n", "fig, ax = plt.subplots()\n", "dists[:, :, sum].plot1d(ax=ax)\n", "ax.legend(title=\"dataset\")" ] }, { "cell_type": "markdown", "id": "8aae69ba", "metadata": {}, "source": [ "Another important consideration is that evaluating a polynomial in Horner form is $O(k)$ where $k$ is the order of the polynomial, while looking up a value in a non-uniform binning is $O(log(n))$ in $n$ bins. Depending on the situation, an acceptable $k$ may be lower than $log(n)$. In our case, the $(2,2)$ polynomial we derived evaluates slower than the binning, partially because it is not evaluated in Horner form:" ] }, { "cell_type": "code", "execution_count": 20, "id": "01ae867c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.05 ms ± 14.4 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", "2.81 ms ± 3.66 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "corr_bin = gen2_to_gen1.to_evaluator()\n", "corr_pol = gen2_to_gen1_poly.to_evaluator()\n", "\n", "%timeit corr_bin.evaluate(ptvals, etavals)\n", "%timeit corr_pol.evaluate(ptvals, etavals)" ] }, { "cell_type": "markdown", "id": "08564fd8", "metadata": {}, "source": [ "However, in an alternative situation, the same may not hold:" ] }, { "cell_type": "code", "execution_count": 21, "id": "752bdad2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "42.2 μs ± 168 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", "14.9 μs ± 147 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" ] } ], "source": [ "%timeit np.searchsorted([0., 5., 10., 15., 20., 25., 30., 35.], ptvals)\n", "%timeit np.polyval([0.01, 0.1, 1.0], ptvals)" ] }, { "cell_type": "markdown", "id": "18cfc2ed", "metadata": {}, "source": [ "## Resolution models\n", "\n", "In some instances, one might want to smear the value of a variable, e.g. jet energy, to simulate a degradation of resolution with respect to what was expected from simulation. If we can deterministically generate pseudorandom numbers, we can then use them after suitable scaling to correct the jet momentum. To do so in correctionlib, we gather entropy sources such as the kinematics of the jet and event-level quantities, hash them together using the extremely fast [xxhash](https://cyan4973.github.io/xxHash/) algorithm to generate an integer seed, and use that to initialize a [PCG64](https://www.pcg-random.org/) random number generator, which can then be drawn from to build a normal-distributed (or otherwise) value. See [issue #130](https://github.com/cms-nanoAOD/correctionlib/issues/130) for further details." ] }, { "cell_type": "code", "execution_count": 22, "id": "0375e2f5", "metadata": {}, "outputs": [], "source": [ "resrng = cs.Correction(\n", " name=\"resrng\",\n", " description=\"Deterministic smearing value generator\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Unsmeared jet pt\"),\n", " cs.Variable(name=\"eta\", type=\"real\", description=\"Jet pseudorapdity\"),\n", " cs.Variable(name=\"phi\", type=\"real\", description=\"Jet phi (entropy source)\"),\n", " cs.Variable(\n", " name=\"evt\", type=\"int\", description=\"Event number (entropy source)\"\n", " ),\n", " ],\n", " output=cs.Variable(name=\"rng\", type=\"real\"),\n", " data=cs.HashPRNG(\n", " nodetype=\"hashprng\",\n", " inputs=[\"pt\", \"eta\", \"phi\", \"evt\"],\n", " distribution=\"normal\",\n", " ),\n", ")\n", "\n", "resmodel = cs.Correction(\n", " name=\"resmodel\",\n", " description=\"A jet energy resolution smearing model\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Unsmeared jet pt\"),\n", " ],\n", " output=cs.Variable(name=\"scale\", type=\"real\"),\n", " data=cs.Binning(\n", " nodetype=\"binning\",\n", " input=\"pt\",\n", " edges=[10, 20, 30, 40, 50, 80, 120],\n", " content=[0.3, 0.25, 0.20, 0.14, 0.06, 0.02],\n", " flow=\"clamp\",\n", " ),\n", ")" ] }, { "cell_type": "code", "execution_count": 23, "id": "f93ca4d8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0.5, 0, 'Smeared $p_T$ - Unsmeared $p_T$')" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# dummy distributions\n", "phivals = np.random.uniform(-np.pi, np.pi, size=10000)\n", "eventnumber = np.random.randint(123456, 123567, size=10000)\n", "\n", "# apply a pT-dependent smearing by scaling the standard normal draw\n", "smear_val = resrng.to_evaluator().evaluate(\n", " ptvals, etavals, phivals, eventnumber\n", ") * resmodel.to_evaluator().evaluate(ptvals)\n", "pt_smeared = np.maximum(ptvals + smear_val, 0.0)\n", "\n", "\n", "fig, ax = plt.subplots()\n", "ax.hist(pt_smeared - ptvals, bins=50)\n", "ax.set_xlabel(\"Smeared $p_T$ - Unsmeared $p_T$\")" ] }, { "cell_type": "markdown", "id": "4ffea3dd", "metadata": {}, "source": [ "## Chaining with CompoundCorrection\n", "\n", "A CompoundCorrection allows to apply a sequence of corrections in order, referring to other corrections in the same CorrectionSet object. For example, we can merge the smearing and RNG into one:" ] }, { "cell_type": "code", "execution_count": 24, "id": "8dfe7c7f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "One-shot smearing is equivalent to two-step procedure?\n", "True\n" ] } ], "source": [ "cset = cs.CorrectionSet(\n", " schema_version=2,\n", " corrections=[\n", " resmodel,\n", " resrng,\n", " ],\n", " compound_corrections=[\n", " cs.CompoundCorrection(\n", " name=\"resolution_model\",\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Unsmeared jet pt\"),\n", " cs.Variable(name=\"eta\", type=\"real\", description=\"Jet pseudorapdity\"),\n", " cs.Variable(\n", " name=\"phi\", type=\"real\", description=\"Jet phi (entropy source)\"\n", " ),\n", " cs.Variable(\n", " name=\"evt\", type=\"int\", description=\"Event number (entropy source)\"\n", " ),\n", " ],\n", " output=cs.Variable(\n", " name=\"shift\", type=\"real\", description=\"Additive shift to jet pT\"\n", " ),\n", " inputs_update=[],\n", " input_op=\"*\",\n", " output_op=\"*\",\n", " stack=[\"resmodel\", \"resrng\"],\n", " )\n", " ],\n", ")\n", "\n", "oneshot = cset.to_evaluator().compound[\"resolution_model\"]\n", "\n", "print(\"One-shot smearing is equivalent to two-step procedure?\")\n", "print(\n", " np.allclose(\n", " oneshot.evaluate(ptvals, etavals, phivals, eventnumber),\n", " smear_val,\n", " )\n", ")" ] }, { "cell_type": "markdown", "id": "f2807bad", "metadata": {}, "source": [ "Note the `inputs_update` and `input_op` fields can be used to update inputs as they go through the stack, which is useful for chained corrections such as jet energy corrections." ] }, { "cell_type": "markdown", "id": "b533ebae", "metadata": {}, "source": [ "## Systematics\n", "\n", "There are many ways to encode systematic uncertainties within correctionlib, although no nodes are dedicated to the task. See [issue #4](https://github.com/cms-nanoAOD/correctionlib/issues/4) to discuss further the idea of having a dedicated systematic node. The most straightforward option is to use a Category node with string lookup to switch the behavior depending on the active systematic. Below we modify our `ptweight` from before to produce a systematically larger event weight when the `MuonEffUp` systematic is specified, while producing the nominal event weight for any other string key, by taking advantage of the `default=` keyword in the Category node. We also use the `flow=` keyword in the shifted binning to increase the systematic uncertainty for data with muon $p_T$ larger than 120." ] }, { "cell_type": "code", "execution_count": 25, "id": "65c7ee44", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
📈 ptweight (v1)\n",
       "No description\n",
       "Node counts: Category: 1, Binning: 2\n",
       "╭──────────── ▶ input ─────────────╮ ╭───── ▶ input ─────╮\n",
       "│ pt (real)                        │ │ syst (string)     │\n",
       "│ Muon transverse momentum         │ │ Systematic        │\n",
       "│ Range: [0.0, 120.0), overflow ok │ │ Values: MuonEffUp │\n",
       "╰──────────────────────────────────╯ │ has default       │\n",
       "                                     ╰───────────────────╯\n",
       "╭───────── ◀ output ──────────╮\n",
       "│ weight (real)               │\n",
       "│ Multiplicative event weight │\n",
       "╰─────────────────────────────╯\n",
       "
\n" ], "text/plain": [ "📈 \u001b[1mptweight\u001b[0m \u001b[1m(\u001b[0mv1\u001b[1m)\u001b[0m\n", "\u001b[3mNo description\u001b[0m\n", "Node counts: \u001b[1mCategory\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[1mBinning\u001b[0m: \u001b[1;36m2\u001b[0m\n", "╭──────────── ▶ input ─────────────╮ ╭───── ▶ input ─────╮\n", "│ \u001b[1mpt\u001b[0m (real) │ │ \u001b[1msyst\u001b[0m (string) │\n", "│ Muon transverse momentum │ │ Systematic │\n", "│ Range: [0.0, 120.0), overflow ok │ │ Values: MuonEffUp │\n", "╰──────────────────────────────────╯ │ \u001b[1;32mhas default\u001b[0m │\n", " ╰───────────────────╯\n", "╭───────── ◀ output ──────────╮\n", "│ \u001b[1mweight\u001b[0m (real) │\n", "│ Multiplicative event weight │\n", "╰─────────────────────────────╯\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ptweight = cs.Correction(\n", " name=\"ptweight\",\n", " version=1,\n", " inputs=[\n", " cs.Variable(name=\"pt\", type=\"real\", description=\"Muon transverse momentum\"),\n", " cs.Variable(name=\"syst\", type=\"string\", description=\"Systematic\"),\n", " ],\n", " output=cs.Variable(\n", " name=\"weight\", type=\"real\", description=\"Multiplicative event weight\"\n", " ),\n", " data=cs.Category(\n", " nodetype=\"category\",\n", " input=\"syst\",\n", " content=[\n", " cs.CategoryItem(\n", " key=\"MuonEffUp\",\n", " value=cs.Binning(\n", " nodetype=\"binning\",\n", " input=\"pt\",\n", " edges=[0, 10, 20, 30, 40, 50, 80, 120],\n", " content=[1.14, 1.14, 1.09, 1.07, 1.05, 1.03, 1.01],\n", " flow=1.03,\n", " ),\n", " ),\n", " ],\n", " default=cs.Binning(\n", " nodetype=\"binning\",\n", " input=\"pt\",\n", " edges=[10, 20, 30, 40, 50, 80, 120],\n", " content=[1.1, 1.08, 1.06, 1.04, 1.02, 1.0],\n", " flow=\"clamp\",\n", " ),\n", " ),\n", ")\n", "\n", "rich.print(ptweight)" ] }, { "cell_type": "code", "execution_count": 26, "id": "d359ec0e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ptweight.to_evaluator().evaluate(135.0, \"nominal\")" ] }, { "cell_type": "markdown", "id": "37ba6ef2", "metadata": {}, "source": [ "## Writing it all out" ] }, { "cell_type": "code", "execution_count": 27, "id": "398b8b6c", "metadata": {}, "outputs": [], "source": [ "import gzip\n", "\n", "cset = cs.CorrectionSet(\n", " schema_version=2,\n", " description=\"my custom corrections\",\n", " corrections=[\n", " gen2_to_gen1,\n", " ptweight,\n", " phimod,\n", " ],\n", ")\n", "\n", "with open(\"mycorrections.json\", \"w\") as fout:\n", " fout.write(cset.model_dump_json(exclude_unset=True))\n", "\n", "\n", "with gzip.open(\"mycorrections.json.gz\", \"wt\") as fout:\n", " fout.write(cset.model_dump_json(exclude_unset=True))" ] }, { "cell_type": "markdown", "id": "8fb88c12", "metadata": {}, "source": [ "## Command-line utility\n", "\n", "The `correction` utility, bundled with the library, provides useful tools for viewing, combining, and validating correction json sets. It also can provide the necessary compile flags for C++ programs to use correctionlib:" ] }, { "cell_type": "code", "execution_count": 28, "id": "9765cb34", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "usage: correction [-h] [--width WIDTH] [--html HTML]\n", " {validate,summary,merge,config} ...\n", "\n", "Command-line interface to correctionlib.\n", "\n", "positional arguments:\n", " {validate,summary,merge,config}\n", " validate Check if all files are valid\n", " summary Print a summary of the corrections\n", " merge Merge one or more correction files and print to stdout\n", " config Configuration and linking information\n", "\n", "options:\n", " -h, --help show this help message and exit\n", " --width WIDTH Rich output width\n", " --html HTML Save terminal output to an HTML file\n" ] } ], "source": [ "!correction --help" ] }, { "cell_type": "code", "execution_count": 29, "id": "3a8458d4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "usage: correction config [-h] [-v] [--incdir] [--cflags] [--libdir]\n", " [--ldflags] [--rpath] [--cmake]\n", "\n", "options:\n", " -h, --help show this help message and exit\n", " -v, --version Return correctionlib current version\n", " --incdir\n", " --cflags\n", " --libdir\n", " --ldflags\n", " --rpath Include library path hint in linker\n", " --cmake CMake dependency flags\n" ] } ], "source": [ "!correction config -h" ] }, { "cell_type": "code", "execution_count": 30, "id": "b0e5ee3d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing main.cc\n" ] } ], "source": [ "%%file main.cc\n", "#include \n", "#include \"correction.h\"\n", "\n", "int main() {\n", " auto cset = correction::CorrectionSet::from_file(\"mycorrections.json.gz\");\n", " \n", " double val = cset->at(\"ptweight\")->evaluate({15.0, \"nominal\"});\n", " std::cout << val << std::endl;\n", " return 0;\n", "}" ] }, { "cell_type": "code", "execution_count": 31, "id": "0bd8a625", "metadata": {}, "outputs": [], "source": [ "!g++ main.cc -o main $(correction config --cflags --ldflags --rpath)" ] }, { "cell_type": "markdown", "id": "2b9251f4", "metadata": {}, "source": [ "On some platforms, if you see errors such as\n", "```\n", "main.cc:(.text+0x17d): undefined reference to `correction::CorrectionSet::from_file(std::__cxx11::basic_string, std::allocator > const&)'\n", "```\n", "in the above compilation, you may need to add `-D_GLIBCXX_USE_CXX11_ABI=0` to the arguments." ] }, { "cell_type": "code", "execution_count": 32, "id": "b083d0d3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.1\n" ] } ], "source": [ "!./main" ] } ], "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 }