Newer
Older
Master_thesis / raremodel-nb.ipynb
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Import"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\sa_li\\.conda\\envs\\rmd\\lib\\site-packages\\zfit\\util\\execution.py:53: UserWarning: Not running on Linux. Determining available cpus for thread can failand be overestimated. Workaround (only if too many cpus are used):`zfit.run.set_n_cpu(your_cpu_number)`\n",
      "  warnings.warn(\"Not running on Linux. Determining available cpus for thread can fail\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.\n",
      "For more information, please see:\n",
      "  * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n",
      "  * https://github.com/tensorflow/addons\n",
      "If you depend on functionality not listed there, please file an issue.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from pdg_const import pdg\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "import pickle as pkl\n",
    "import sys\n",
    "import time\n",
    "from helperfunctions import display_time, prepare_plot\n",
    "import cmath as c\n",
    "import scipy.integrate as integrate\n",
    "from scipy.optimize import fminbound\n",
    "from array import array as arr\n",
    "import collections\n",
    "from itertools import compress\n",
    "import tensorflow as tf\n",
    "import zfit\n",
    "from zfit import ztf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Build model and graphs\n",
    "## Create graphs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def formfactor( q2, subscript): #returns real value\n",
    "    #check if subscript is viable\n",
    "\n",
    "    if subscript != \"0\" and subscript != \"+\" and subscript != \"T\":\n",
    "        raise ValueError('Wrong subscript entered, choose either 0, + or T')\n",
    "\n",
    "    #get constants\n",
    "\n",
    "    mK = ztf.constant(pdg['Ks_M'])\n",
    "    mbstar0 = ztf.constant(pdg[\"mbstar0\"])\n",
    "    mbstar = ztf.constant(pdg[\"mbstar\"])\n",
    "    b0 = ztf.constant(pdg[\"b0\"])\n",
    "    bplus = ztf.constant(pdg[\"bplus\"])\n",
    "    bT = ztf.constant(pdg[\"bT\"])\n",
    "\n",
    "    mmu = ztf.constant(pdg['muon_M'])\n",
    "    mb = ztf.constant(pdg['bquark_M'])\n",
    "    ms = ztf.constant(pdg['squark_M'])\n",
    "    mB = ztf.constant(pdg['Bplus_M'])\n",
    "\n",
    "    #N comes from derivation in paper\n",
    "\n",
    "    N = 3\n",
    "\n",
    "    #some helperfunctions\n",
    "\n",
    "    tpos = (mB - mK)**2\n",
    "    tzero = (mB + mK)*(ztf.sqrt(mB)-ztf.sqrt(mK))**2\n",
    "\n",
    "    z_oben = ztf.sqrt(tpos - q2) - ztf.sqrt(tpos - tzero)\n",
    "    z_unten = ztf.sqrt(tpos - q2) + ztf.sqrt(tpos - tzero)\n",
    "    z = tf.divide(z_oben, z_unten)\n",
    "\n",
    "    #calculate f0\n",
    "\n",
    "    if subscript == \"0\":\n",
    "        prefactor = 1/(1 - q2/(mbstar0**2))\n",
    "        _sum = 0\n",
    "\n",
    "        for i in range(N):\n",
    "            _sum += b0[i]*(tf.pow(z,i))\n",
    "\n",
    "        return tf.complex(prefactor * _sum, ztf.constant(0.0))\n",
    "\n",
    "    #calculate f+ or fT\n",
    "\n",
    "    else:\n",
    "        prefactor = 1/(1 - q2/(mbstar**2))\n",
    "        _sum = 0\n",
    "\n",
    "        if subscript == \"T\":\n",
    "            b = bT\n",
    "        else:\n",
    "            b = bplus\n",
    "\n",
    "        for i in range(N):\n",
    "            _sum += b[i] * (tf.pow(z, i) - ((-1)**(i-N)) * (i/N) * tf.pow(z, N))\n",
    "\n",
    "        return tf.complex(prefactor * _sum, ztf.constant(0.0))\n",
    "\n",
    "def resonance(q, _mass, width, phase, scale):\n",
    "\n",
    "    q2 = tf.pow(q, 2)\n",
    "\n",
    "    mmu = ztf.constant(pdg['muon_M'])\n",
    "\n",
    "    p = 0.5 * ztf.sqrt(q2 - 4*(mmu**2))\n",
    "\n",
    "    p0 =  0.5 * ztf.sqrt(_mass**2 - 4*mmu**2)\n",
    "\n",
    "    gamma_j = tf.divide(p, q2) * _mass * width / p0\n",
    "\n",
    "    #Calculate the resonance\n",
    "\n",
    "    _top = tf.complex(_mass * width, ztf.constant(0.0))\n",
    "\n",
    "    _bottom = tf.complex(_mass**2 - q2, -_mass*gamma_j)\n",
    "\n",
    "    com = _top/_bottom\n",
    "\n",
    "    #Rotate by the phase\n",
    "\n",
    "    r = tf.abs(com)\n",
    "\n",
    "    _phase = tf.angle(com)\n",
    "\n",
    "    _phase += phase\n",
    "\n",
    "    x = tf.cos(phase)*r\n",
    "    y = tf.sin(phase)*r\n",
    "\n",
    "    com = tf.complex(scale* x, scale * y)\n",
    "\n",
    "    return com\n",
    "\n",
    "def bifur_gauss(q, mean, sigma_L, sigma_R, scale):\n",
    "\n",
    "    _exp = tf.where(q < mean, ztf.exp(- tf.pow((q-mean),2) / (2 * sigma_L**2)), ztf.exp(- tf.pow((q-mean),2) / (2 * sigma_R**2)))\n",
    "\n",
    "    #Scale so the total area under curve is 1 and the top of the cusp is continuous\n",
    "\n",
    "    dgamma = scale*_exp/(ztf.sqrt(2*np.pi))*2*(sigma_L*sigma_R)/(sigma_L+sigma_R)\n",
    "\n",
    "    com = ztf.complex(dgamma, ztf.constant(0.0))\n",
    "\n",
    "    return com\n",
    "\n",
    "def axiv_nonres(q):\n",
    "\n",
    "    GF = ztf.constant(pdg['GF'])\n",
    "    alpha_ew = ztf.constant(pdg['alpha_ew'])\n",
    "    Vtb = ztf.constant(pdg['Vtb'])\n",
    "    Vts = ztf.constant(pdg['Vts'])\n",
    "    C10eff = ztf.constant(pdg['C10eff'])\n",
    "\n",
    "    mmu = ztf.constant(pdg['muon_M'])\n",
    "    mb = ztf.constant(pdg['bquark_M'])\n",
    "    ms = ztf.constant(pdg['squark_M'])\n",
    "    mK = ztf.constant(pdg['Ks_M'])\n",
    "    mB = ztf.constant(pdg['Bplus_M'])\n",
    "\n",
    "    q2 = tf.pow(q, 2)\n",
    "\n",
    "    #Some helperfunctions\n",
    "\n",
    "    beta = ztf.sqrt(tf.abs(1. - 4. * mmu**2. / q2))\n",
    "\n",
    "    kabs = ztf.sqrt(mB**2. +tf.pow(q2, 2)/mB**2. + mK**4./mB**2. - 2. * (mB**2. * mK**2. + mK**2. * q2 + mB**2. * q2) / mB**2.)\n",
    "\n",
    "    #prefactor in front of whole bracket\n",
    "\n",
    "    prefactor1 = GF**2. *alpha_ew**2. * (tf.abs(Vtb*Vts))**2. * kabs * beta / (128. * np.pi**5.)\n",
    "\n",
    "    #left term in bracket\n",
    "\n",
    "    bracket_left = 2./3. * kabs**2. * beta**2. *tf.abs(tf.complex(C10eff, ztf.constant(0.0))*formfactor(q2, \"+\"))**2.\n",
    "\n",
    "    #middle term in bracket\n",
    "\n",
    "    _top = 4. * mmu**2. * (mB**2. - mK**2.) * (mB**2. - mK**2.)\n",
    "\n",
    "    _under = q2 * mB**2.\n",
    "\n",
    "    bracket_middle = _top/_under *tf.pow(tf.abs(tf.complex(C10eff, ztf.constant(0.0)) * formfactor(q2, \"0\")), 2)\n",
    "\n",
    "    #Note sqrt(q2) comes from derivation as we use q2 and plot q\n",
    "\n",
    "    return prefactor1 * (bracket_left + bracket_middle) * 2 *ztf.sqrt(q2)\n",
    "\n",
    "def vec(q, funcs):\n",
    "    \n",
    "    q2 = tf.pow(q, 2)\n",
    "\n",
    "    GF = ztf.constant(pdg['GF'])\n",
    "    alpha_ew = ztf.constant(pdg['alpha_ew'])\n",
    "    Vtb = ztf.constant(pdg['Vtb'])\n",
    "    Vts = ztf.constant(pdg['Vts'])\n",
    "    C7eff = ztf.constant(pdg['C7eff'])\n",
    "\n",
    "    mmu = ztf.constant(pdg['muon_M'])\n",
    "    mb = ztf.constant(pdg['bquark_M'])\n",
    "    ms = ztf.constant(pdg['squark_M'])\n",
    "    mK = ztf.constant(pdg['Ks_M'])\n",
    "    mB = ztf.constant(pdg['Bplus_M'])\n",
    "\n",
    "    #Some helperfunctions\n",
    "\n",
    "    beta = ztf.sqrt(tf.abs(1. - 4. * mmu**2. / q2))\n",
    "\n",
    "    kabs = ztf.sqrt(mB**2. + tf.pow(q2, 2)/mB**2. + mK**4./mB**2. - 2 * (mB**2 * mK**2 + mK**2 * q2 + mB**2 * q2) / mB**2)\n",
    "\n",
    "    #prefactor in front of whole bracket\n",
    "\n",
    "    prefactor1 = GF**2. *alpha_ew**2. * (tf.abs(Vtb*Vts))**2 * kabs * beta / (128. * np.pi**5.)\n",
    "\n",
    "    #right term in bracket\n",
    "\n",
    "    prefactor2 = kabs**2 * (1. - 1./3. * beta**2)\n",
    "\n",
    "    abs_bracket = tf.abs(c9eff(q, funcs) * formfactor(q2, \"+\") + tf.complex(2.0 * C7eff * (mb + ms)/(mB + mK), ztf.constant(0.0)) * formfactor(q2, \"T\"))**2\n",
    "\n",
    "    bracket_right = prefactor2 * abs_bracket\n",
    "\n",
    "    #Note sqrt(q2) comes from derivation as we use q2 and plot q\n",
    "\n",
    "    return prefactor1 * bracket_right * 2 * ztf.sqrt(q2)\n",
    "\n",
    "def c9eff(q, funcs):\n",
    "\n",
    "    C9eff_nr = tf.complex(ztf.constant(pdg['C9eff']), ztf.constant(0.0))\n",
    "\n",
    "    c9 = C9eff_nr\n",
    "\n",
    "    c9 = c9 + funcs\n",
    "\n",
    "    return c9"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def G(y):\n",
    "    \n",
    "    def inner_rect_bracket(q):\n",
    "        return tf.log(ztf.to_complex((1+tf.sqrt(q))/(1-tf.sqrt(q)))-tf.complex(ztf.constant(0), -1*ztf.constant(np.pi)))    \n",
    "    \n",
    "    def inner_right(q):\n",
    "        return ztf.to_complex(2 * tf.atan(1/tf.sqrt(-q)))\n",
    "    \n",
    "    big_bracket = tf.where(y > ztf.const(0.0), inner_rect_bracket(y), inner_right(y))\n",
    "    \n",
    "    return ztf.to_complex(tf.sqrt(tf.abs(y))) * big_bracket\n",
    "\n",
    "def h_S(m, q):\n",
    "    \n",
    "    return tf.constant(2) - G(tf.constant(1) - 4*tf.pow(m, 2) / tf.pow(q, 2))\n",
    "\n",
    "def h_P(m,q):\n",
    "    \n",
    "    return 2/3 + (1 - (tf.constant(1) - 4*tf.pow(m, 2) / tf.pow(q, 2))) * h_S(m,q)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Build pdf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class total_pdf(zfit.pdf.ZPDF):\n",
    "    _N_OBS = 1  # dimension, can be omitted\n",
    "    _PARAMS = ['jpsi_mass', 'jpsi_scale', 'jpsi_phase', 'jpsi_width',\n",
    "                'psi2s_mass', 'psi2s_scale', 'psi2s_phase', 'psi2s_width',\n",
    "                'cusp_mass', 'sigma_L', 'sigma_R', 'cusp_scale'\n",
    "                ]  # the name of the parameters\n",
    "\n",
    "    def _unnormalized_pdf(self, x):\n",
    "        \n",
    "        x = x.unstack_x()\n",
    "\n",
    "        def jpsi_res(q):\n",
    "            return resonance(q, _mass = self.params['jpsi_mass'], scale = self.params['jpsi_scale'], phase = self.params['jpsi_phase'], width = self.params['jpsi_width'])\n",
    "\n",
    "        def psi2s_res(q):\n",
    "            return resonance(q, _mass = self.params['psi2s_mass'], scale = self.params['psi2s_scale'], phase = self.params['psi2s_phase'], width = self.params['psi2s_width'])\n",
    "\n",
    "        def cusp(q):\n",
    "            return bifur_gauss(q, mean = self.params['cusp_mass'], sigma_L = self.params['sigma_L'], sigma_R = self.params['sigma_R'], scale = self.params['cusp_scale'])\n",
    "\n",
    "        funcs = jpsi_res(x) + psi2s_res(x) + cusp(x)\n",
    "\n",
    "        vec_f = vec(x, funcs)\n",
    "\n",
    "        axiv_nr = axiv_nonres(x)\n",
    "\n",
    "        tot = vec_f + axiv_nr\n",
    "\n",
    "        return tot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_min = 2*pdg['muon_M']\n",
    "x_max = (pdg[\"Bplus_M\"]-pdg[\"Ks_M\"]-0.1)\n",
    "\n",
    "obs = zfit.Space('q', limits = (x_min, x_max))\n",
    "\n",
    "with open(r\"./data/slim_points/slim_points_toy_0_range({0}-{1}).pkl\".format(int(x_min), int(x_max)), \"rb\") as input_file:\n",
    "    part_set = pkl.load(input_file)\n",
    "\n",
    "x_part = part_set['x_part']\n",
    "\n",
    "x_part = x_part.astype('float64')\n",
    "\n",
    "data = zfit.data.Data.from_numpy(array=x_part, obs=obs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From c:\\users\\sa_li\\.conda\\envs\\rmd\\lib\\site-packages\\tensorflow\\python\\ops\\resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Colocations handled automatically by placer.\n"
     ]
    }
   ],
   "source": [
    "#jpsi\n",
    "\n",
    "jpsi_mass, jpsi_width, jpsi_phase, jpsi_scale = pdg[\"jpsi\"]\n",
    "jpsi_scale *= pdg[\"factor_jpsi\"]\n",
    "\n",
    "jpsi_m = zfit.Parameter(\"jpsi_m\", ztf.constant(jpsi_mass), floating = False)\n",
    "jpsi_w = zfit.Parameter(\"jpsi_w\", ztf.constant(jpsi_width), floating = False)\n",
    "jpsi_p = zfit.Parameter(\"jpsi_p\", ztf.constant(jpsi_phase), floating = False)\n",
    "jpsi_s = zfit.Parameter(\"jpsi_s\", ztf.constant(jpsi_scale), floating = False)\n",
    "\n",
    "#psi2s\n",
    "\n",
    "psi2s_mass, psi2s_width, psi2s_phase, psi2s_scale = pdg[\"psi2s\"]\n",
    "psi2s_scale *= pdg[\"factor_psi2s\"]\n",
    "\n",
    "psi2s_m = zfit.Parameter(\"psi2s_m\", ztf.constant(psi2s_mass), floating = False)\n",
    "psi2s_w = zfit.Parameter(\"psi2s_w\", ztf.constant(psi2s_width), floating = False)\n",
    "psi2s_p = zfit.Parameter(\"psi2s_p\", ztf.constant(psi2s_phase), floating = False)\n",
    "psi2s_s = zfit.Parameter(\"psi2s_s\", ztf.constant(psi2s_scale), floating = False)\n",
    "\n",
    "#cusp\n",
    "\n",
    "cusp_mass, sigma_R, sigma_L, cusp_scale = 3550, 3e-7, 200, 0\n",
    "\n",
    "cusp_m = zfit.Parameter(\"cusp_m\", ztf.constant(cusp_mass))\n",
    "sig_L = zfit.Parameter(\"sig_L\", ztf.constant(sigma_L))\n",
    "sig_R = zfit.Parameter(\"sig_R\", ztf.constant(sigma_R))\n",
    "cusp_s = zfit.Parameter(\"cusp_s\", ztf.constant(cusp_scale))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup pdf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "total_f = total_pdf(obs=obs, jpsi_mass = jpsi_m, jpsi_scale = jpsi_s, jpsi_phase = jpsi_p, jpsi_width = jpsi_w,\n",
    "            psi2s_mass = psi2s_m, psi2s_scale = psi2s_s, psi2s_phase = psi2s_p, psi2s_width = psi2s_w,\n",
    "            cusp_mass = cusp_m, sigma_L = sig_L, sigma_R = sig_R, cusp_scale = cusp_s)\n",
    "\n",
    "# print(total_pdf.obs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test if graphs actually work and compute values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def total_test_tf(xq):\n",
    "\n",
    "    def jpsi_res(q):\n",
    "        return resonance(q, jpsi_m, jpsi_s, jpsi_p, jpsi_w)\n",
    "\n",
    "    def psi2s_res(q):\n",
    "        return resonance(q, psi2s_m, psi2s_s, psi2s_p, psi2s_w)\n",
    "\n",
    "    def cusp(q):\n",
    "        return bifur_gauss(q, cusp_m, sig_L, sig_R, cusp_s)\n",
    "\n",
    "    funcs = jpsi_res(xq) + psi2s_res(xq) + cusp(xq)\n",
    "\n",
    "    vec_f = vec(xq, funcs)\n",
    "\n",
    "    axiv_nr = axiv_nonres(xq)\n",
    "\n",
    "    tot = vec_f + axiv_nr\n",
    "    \n",
    "    return tot\n",
    "\n",
    "def jpsi_res(q):\n",
    "    return resonance(q, jpsi_m, jpsi_s, jpsi_p, jpsi_w)\n",
    "\n",
    "# calcs = zfit.run(total_test_tf(x_part))\n",
    "\n",
    "test_q = np.linspace(x_min, x_max, 2000000)\n",
    "\n",
    "probs = total_f.pdf(test_q)\n",
    "\n",
    "calcs_test = zfit.run(probs)\n",
    "res_y = zfit.run(jpsi_res(test_q))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.09\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.clf()\n",
    "# plt.plot(x_part, calcs, '.')\n",
    "plt.plot(test_q, calcs_test, label = 'pdf')\n",
    "# plt.plot(test_q, res_y, label = 'res')\n",
    "plt.legend()\n",
    "plt.ylim(0.0, 4e-4)\n",
    "# plt.yscale('log')\n",
    "# plt.xlim(3080, 3110)\n",
    "plt.savefig('test.png')\n",
    "print(jpsi_width)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Adjust scaling of different parts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# total_f.update_integration_options(draws_per_dim=10000000, mc_sampler=None)\n",
    "# inte = total_f.integrate(limits = (3000, 3200), norm_range=False)\n",
    "# print(zfit.run(inte))\n",
    "# print(pdg[\"jpsi_BR\"]/pdg[\"NR_BR\"], zfit.run(inte)/pdg[\"NR_auc\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# factor_jpsi = pdg[\"NR_auc\"]*pdg[\"jpsi_BR\"]/(pdg[\"NR_BR\"]*pdg[\"jpsi_auc\"])\n",
    "# print(np.sqrt(factor_jpsi))\n",
    "# factor_psi2s = pdg[\"NR_auc\"]*pdg[\"psi2s_BR\"]/(pdg[\"NR_BR\"]*pdg[\"psi2s_auc\"])\n",
    "# print(np.sqrt(factor_psi2s))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# def _t_f(xq):\n",
    "\n",
    "#     def jpsi_res(q):\n",
    "#         return resonance(q, jpsi_m, jpsi_s, jpsi_p, jpsi_w)\n",
    "\n",
    "#     def psi2s_res(q):\n",
    "#         return resonance(q, psi2s_m, psi2s_s, psi2s_p, psi2s_w)\n",
    "\n",
    "#     def cusp(q):\n",
    "#         return bifur_gauss(q, cusp_m, sig_L, sig_R, cusp_s)\n",
    "\n",
    "#     funcs = psi2s_res(xq) + jpsi_res(xq) + cusp(xq)\n",
    "\n",
    "#     vec_f = vec(xq, funcs)\n",
    "\n",
    "#     axiv_nr = axiv_nonres(xq)\n",
    "\n",
    "#     tot = vec_f + axiv_nr\n",
    "    \n",
    "#     return tot\n",
    "\n",
    "# def t_f(x):\n",
    "#     probs = zfit.run(_t_f(ztf.constant(x)))\n",
    "#     return probs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5404695.652173913\n"
     ]
    }
   ],
   "source": [
    "print(36000*(1+ pdg[\"jpsi_BR\"]/pdg[\"NR_BR\"] + pdg[\"psi2s_BR\"]/pdg[\"NR_BR\"]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# start = time.time()\n",
    "\n",
    "# result, err = integrate.quad(lambda x: t_f(x), x_min, x_max, limit = 50)\n",
    "# print(result, \"{0:.2f} %\".format(err/result))\n",
    "# print(\"Time:\", time.time()-start)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sampling\n",
    "## One sample"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From c:\\users\\sa_li\\.conda\\envs\\rmd\\lib\\site-packages\\zfit\\core\\sample.py:98: to_int64 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.cast instead.\n"
     ]
    }
   ],
   "source": [
    "nevents = 5404696\n",
    "\n",
    "samp = total_f.sample(n=nevents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sam = samp.unstack_x()\n",
    "\n",
    "sam = zfit.run(sam)\n",
    "\n",
    "bins = int((x_max-x_min)/7)\n",
    "\n",
    "calcs = zfit.run(total_test_tf(samp))\n",
    "\n",
    "plt.hist(sam, bins = bins, range = (x_min,x_max))\n",
    "\n",
    "# plt.plot(sam, calcs, '.')\n",
    "# plt.plot(test_q, calcs_test)\n",
    "# plt.ylim(0, 0.0000007)\n",
    "# plt.xlim(3000, 3750)\n",
    "\n",
    "plt.savefig('test.png')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Toys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sampler = total_f.create_sampler(n=nevents)\n",
    "nll = zfit.loss.UnbinnedNLL(model=total_f, data=sampler, fit_range = (x_min, x_max))\n",
    "\n",
    "# for param in pdf.get_dependents():\n",
    "#     param.set_value(initial_value)\n",
    "\n",
    "sampler.resample(n=nevents)\n",
    "\n",
    "# Randomise initial values\n",
    "# for param in pdf.get_dependents():\n",
    "#     param.set_value(random value here)\n",
    "\n",
    "# Minimise the NLL\n",
    "minimizer = zfit.minimize.MinuitMinimizer(verbosity = 10)\n",
    "minimum = minimizer.minimize(nll)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Fitting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nll = zfit.loss.UnbinnedNLL(model=total_f, data=data, fit_range = (x_min, x_max))\n",
    "\n",
    "minimizer = zfit.minimize.MinuitMinimizer()\n",
    "minimizer._use_tfgrad = False\n",
    "result = minimizer.minimize(nll)\n",
    "\n",
    "param_errors = result.error()\n",
    "\n",
    "for var, errors in param_errors.items():\n",
    "    print('{}: ^{{+{}}}_{{{}}}'.format(var.name, errors['upper'], errors['lower']))\n",
    "\n",
    "print(\"Function minimum:\", result.fmin)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "samp = total_f.sample(n=nevents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sam = samp.unstack_x()\n",
    "\n",
    "sam = zfit.run(sam)\n",
    "\n",
    "bins = int((x_max-x_min)/7)\n",
    "\n",
    "calcs = zfit.run(total_test_tf(samp))\n",
    "\n",
    "plt.clf()\n",
    "\n",
    "plt.hist(sam, bins = bins, range = (x_min,x_max))\n",
    "\n",
    "# plt.plot(sam, calcs, '.')\n",
    "# plt.plot(test_q, calcs_test)\n",
    "# plt.ylim(0, 0.0000007)\n",
    "# plt.xlim(3000, 3750)\n",
    "\n",
    "plt.ylim(0,1000)\n",
    "\n",
    "plt.savefig('test.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}