{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## KF Basics - Part I\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Introduction\n",
    "#### What is the need to describe belief in terms of PDF's?\n",
    "This is because robot environments are stochastic. A robot environment may have cows with Tesla by side. That is a robot and it's environment cannot be deterministically modelled(e.g as a function of something like time t). In the real world sensors are also error prone, and hence there'll be a set of values with a mean and variance that it can take. Hence, we always have to model around some mean and variances associated."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### What is Expectation of a Random Variables?\n",
    " Expectation is nothing but an average of the probabilites\n",
    " \n",
    "$$\\mathbb E[X] = \\sum_{i=1}^n p_ix_i$$\n",
    "\n",
    "In the continous form,\n",
    "\n",
    "$$\\mathbb E[X] = \\int_{-\\infty}^\\infty x\\, f(x) \\,dx$$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.4000000000000001\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "x=[3,1,2]\n",
    "p=[0.1,0.3,0.4]\n",
    "E_x=np.sum(np.multiply(x,p))\n",
    "print(E_x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### What is the advantage of representing the belief as a unimodal as opposed to multimodal?\n",
    "Obviously, it makes sense because we can't multiple probabilities to a car moving for two locations. This would be too confusing and the information will not be useful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variance, Covariance and Correlation\n",
    "\n",
    "#### Variance\n",
    "Variance is the spread of the data. The mean does'nt tell much **about** the data. Therefore the variance tells us about the **story** about the data meaning the spread of the data.\n",
    "\n",
    "$$\\mathit{VAR}(X) = \\frac{1}{n}\\sum_{i=1}^n (x_i - \\mu)^2$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0224618077401504"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x=np.random.randn(10)\n",
    "np.var(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Covariance\n",
    "\n",
    "This is for a multivariate distribution. For example, a robot in 2-D space can take values in both x and y. To describe them, a normal distribution with mean in both x and y is needed.\n",
    "\n",
    "For a multivariate distribution, mean $\\mu$ can be represented as a matrix, \n",
    "\n",
    "$$\n",
    "\\mu = \\begin{bmatrix}\\mu_1\\\\\\mu_2\\\\ \\vdots \\\\\\mu_n\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "\n",
    "Similarly, variance can also be represented.\n",
    "\n",
    "But an important concept is that in the same way as every variable or dimension has a variation in its values, it is also possible that there will be values on how they **together vary**. This is also a measure of how two datasets are related to each other or **correlation**.\n",
    "\n",
    "For example, as height increases weight also generally increases. These variables are correlated. They are positively correlated because as one variable gets larger so does the other.\n",
    "\n",
    "We use a **covariance matrix** to denote covariances of a multivariate normal distribution:\n",
    "$$\n",
    "\\Sigma = \\begin{bmatrix}\n",
    "  \\sigma_1^2 & \\sigma_{12} & \\cdots & \\sigma_{1n} \\\\\n",
    "  \\sigma_{21} &\\sigma_2^2 & \\cdots & \\sigma_{2n} \\\\\n",
    "  \\vdots  & \\vdots  & \\ddots & \\vdots  \\\\\n",
    "  \\sigma_{n1} & \\sigma_{n2} & \\cdots & \\sigma_n^2\n",
    " \\end{bmatrix}\n",
    "$$\n",
    "\n",
    "**Diagonal** - Variance of each variable associated. \n",
    "\n",
    "**Off-Diagonal** - covariance between ith and jth variables.\n",
    "\n",
    "$$\\begin{aligned}VAR(X) = \\sigma_x^2 &=  \\frac{1}{n}\\sum_{i=1}^n(X - \\mu)^2\\\\\n",
    "COV(X, Y) = \\sigma_{xy} &= \\frac{1}{n}\\sum_{i=1}^n[(X-\\mu_x)(Y-\\mu_y)\\big]\\end{aligned}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.08868895, 0.05064471, 0.08855629],\n",
       "       [0.05064471, 0.06219243, 0.11555291],\n",
       "       [0.08855629, 0.11555291, 0.21534324]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x=np.random.random((3,3))\n",
    "np.cov(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Covariance taking the data as **sample** with $\\frac{1}{N-1}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.1571437 , -0.00766623],\n",
       "       [-0.00766623,  0.13957621]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_cor=np.random.rand(1,10)\n",
    "y_cor=np.random.rand(1,10)\n",
    "np.cov(x_cor,y_cor)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Covariance taking the data as **population** with $\\frac{1}{N}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.14142933, -0.0068996 ],\n",
       "       [-0.0068996 ,  0.12561859]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.cov(x_cor,y_cor,bias=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Gaussians \n",
    "\n",
    "#### Central Limit Theorem\n",
    "\n",
    "According to this theorem, the average of n samples of random and independent variables tends to follow a normal distribution as we increase the sample size.(Generally, for n>=30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([ 1.,  4.,  9., 12., 12., 20., 16., 16.,  4.,  6.]),\n",
       " array([5.30943011, 5.34638597, 5.38334183, 5.42029769, 5.45725355,\n",
       "        5.49420941, 5.53116527, 5.56812114, 5.605077  , 5.64203286,\n",
       "        5.67898872]),\n",
       " <a list of 10 Patch objects>)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 7708 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import random\n",
    "a=np.zeros((100,))\n",
    "for i in range(100):\n",
    "    x=[random.uniform(1,10) for _ in range(1000)]\n",
    "    a[i]=np.sum(x,axis=0)/1000\n",
    "plt.hist(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Gaussian Distribution\n",
    "A Gaussian is a *continuous probability distribution* that is completely described with two parameters, the mean ($\\mu$) and the variance ($\\sigma^2$). It is defined as:\n",
    "\n",
    "$$ \n",
    "f(x, \\mu, \\sigma) = \\frac{1}{\\sigma\\sqrt{2\\pi}} \\exp\\big [{-\\frac{(x-\\mu)^2}{2\\sigma^2} }\\big ]\n",
    "$$\n",
    "Range is $$[-\\inf,\\inf] $$\n",
    "\n",
    "\n",
    "This is just a function of mean($\\mu$) and standard deviation ($\\sigma$) and what gives the normal distribution the charecteristic **bell curve**. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 16264 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.mlab as mlab\n",
    "import math\n",
    "import scipy.stats\n",
    "\n",
    "mu = 0\n",
    "variance = 5\n",
    "sigma = math.sqrt(variance)\n",
    "x = np.linspace(mu - 5*sigma, mu + 5*sigma, 100)\n",
    "plt.plot(x,scipy.stats.norm.pdf(x, mu, sigma))\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Why do we need Gaussian distributions?\n",
    "Since it becomes really difficult in the real world to deal with multimodal distribution as we cannot put the belief in two seperate location of the robots. This becomes really confusing and in practice impossible to comprehend. \n",
    "Gaussian probability distribution allows us to drive the robots using only one mode with peak at the mean with some variance."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Gaussian Properties\n",
    "\n",
    "**Multiplication**\n",
    "\n",
    "\n",
    "For the measurement update in a Bayes Filter, the algorithm tells us to multiply the Prior P(X_t) and measurement P(Z_t|X_t) to calculate the posterior:\n",
    "\n",
    "$$P(X \\mid Z) = \\frac{P(Z \\mid X)P(X)}{P(Z)}$$\n",
    "\n",
    "Here for the numerator,  $P(Z \\mid X),P(X)$ both are gaussian.\n",
    "\n",
    "$N(\\mu_1, \\sigma_1^2)$ and $N(\\mu_2, \\sigma_2^2)$ are their mean and variances.\n",
    "\n",
    "New mean is \n",
    "\n",
    "$$\\mu_\\mathtt{new} = \\frac{\\mu_1 \\sigma_2^2 + \\mu_2 \\sigma_1^2}{\\sigma_1^2+\\sigma_2^2}$$\n",
    "New variance is\n",
    "$$\\sigma_\\mathtt{new} = \\frac{\\sigma_1^2\\sigma_2^2}{\\sigma_1^2+\\sigma_2^2}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "New mean is at:  5.0\n",
      "New variance is:  1.0\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 24852 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.mlab as mlab\n",
    "import math\n",
    "mu1 = 0\n",
    "variance1 = 2\n",
    "sigma = math.sqrt(variance1)\n",
    "x1 = np.linspace(mu1 - 3*sigma, mu1 + 3*sigma, 100)\n",
    "plt.plot(x1,scipy.stats.norm.pdf(x1, mu1, sigma),label='prior')\n",
    "\n",
    "mu2 = 10\n",
    "variance2 = 2\n",
    "sigma = math.sqrt(variance2)\n",
    "x2 = np.linspace(mu2 - 3*sigma, mu2 + 3*sigma, 100)\n",
    "plt.plot(x2,scipy.stats.norm.pdf(x2, mu2, sigma),\"g-\",label='measurement')\n",
    "\n",
    "\n",
    "mu_new=(mu1*variance2+mu2*variance1)/(variance1+variance2)\n",
    "print(\"New mean is at: \",mu_new)\n",
    "var_new=(variance1*variance2)/(variance1+variance2)\n",
    "print(\"New variance is: \",var_new)\n",
    "sigma = math.sqrt(var_new)\n",
    "x3 = np.linspace(mu_new - 3*sigma, mu_new + 3*sigma, 100)\n",
    "plt.plot(x3,scipy.stats.norm.pdf(x3, mu_new, var_new),label=\"posterior\")\n",
    "plt.legend(loc='upper left')\n",
    "plt.xlim(-10,20)\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Addition**\n",
    "\n",
    "The motion step involves a case of adding up probability (Since it has to abide the Law of Total Probability). This means their beliefs are to be added and hence two gaussians. They are simply arithmetic additions of the two.\n",
    "\n",
    "$$\\begin{gathered}\\mu_x = \\mu_p + \\mu_z \\\\\n",
    "\\sigma_x^2 = \\sigma_z^2+\\sigma_p^2\\, \\square\\end{gathered}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "New mean is at:  15\n",
      "New variance is:  2\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 24268 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.mlab as mlab\n",
    "import math\n",
    "mu1 = 5\n",
    "variance1 = 1\n",
    "sigma = math.sqrt(variance1)\n",
    "x1 = np.linspace(mu1 - 3*sigma, mu1 + 3*sigma, 100)\n",
    "plt.plot(x1,scipy.stats.norm.pdf(x1, mu1, sigma),label='prior')\n",
    "\n",
    "mu2 = 10\n",
    "variance2 = 1\n",
    "sigma = math.sqrt(variance2)\n",
    "x2 = np.linspace(mu2 - 3*sigma, mu2 + 3*sigma, 100)\n",
    "plt.plot(x2,scipy.stats.norm.pdf(x2, mu2, sigma),\"g-\",label='measurement')\n",
    "\n",
    "\n",
    "mu_new=mu1+mu2\n",
    "print(\"New mean is at: \",mu_new)\n",
    "var_new=(variance1+variance2)\n",
    "print(\"New variance is: \",var_new)\n",
    "sigma = math.sqrt(var_new)\n",
    "x3 = np.linspace(mu_new - 3*sigma, mu_new + 3*sigma, 100)\n",
    "plt.plot(x3,scipy.stats.norm.pdf(x3, mu_new, var_new),label=\"posterior\")\n",
    "plt.legend(loc='upper left')\n",
    "plt.xlim(-10,20)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 51544 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Example from:\n",
    "#https://scipython.com/blog/visualizing-the-bivariate-gaussian-distribution/\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import cm\n",
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "# Our 2-dimensional distribution will be over variables X and Y\n",
    "N = 60\n",
    "X = np.linspace(-3, 3, N)\n",
    "Y = np.linspace(-3, 4, N)\n",
    "X, Y = np.meshgrid(X, Y)\n",
    "\n",
    "# Mean vector and covariance matrix\n",
    "mu = np.array([0., 1.])\n",
    "Sigma = np.array([[ 1. , -0.5], [-0.5,  1.5]])\n",
    "\n",
    "# Pack X and Y into a single 3-dimensional array\n",
    "pos = np.empty(X.shape + (2,))\n",
    "pos[:, :, 0] = X\n",
    "pos[:, :, 1] = Y\n",
    "\n",
    "def multivariate_gaussian(pos, mu, Sigma):\n",
    "    \"\"\"Return the multivariate Gaussian distribution on array pos.\n",
    "\n",
    "    pos is an array constructed by packing the meshed arrays of variables\n",
    "    x_1, x_2, x_3, ..., x_k into its _last_ dimension.\n",
    "\n",
    "    \"\"\"\n",
    "\n",
    "    n = mu.shape[0]\n",
    "    Sigma_det = np.linalg.det(Sigma)\n",
    "    Sigma_inv = np.linalg.inv(Sigma)\n",
    "    N = np.sqrt((2*np.pi)**n * Sigma_det)\n",
    "    # This einsum call calculates (x-mu)T.Sigma-1.(x-mu) in a vectorized\n",
    "    # way across all the input variables.\n",
    "    fac = np.einsum('...k,kl,...l->...', pos-mu, Sigma_inv, pos-mu)\n",
    "\n",
    "    return np.exp(-fac / 2) / N\n",
    "\n",
    "# The distribution on the variables X, Y packed into pos.\n",
    "Z = multivariate_gaussian(pos, mu, Sigma)\n",
    "\n",
    "# Create a surface plot and projected filled contour plot under it.\n",
    "fig = plt.figure()\n",
    "ax = fig.gca(projection='3d')\n",
    "ax.plot_surface(X, Y, Z, rstride=3, cstride=3, linewidth=1, antialiased=True,\n",
    "                cmap=cm.viridis)\n",
    "\n",
    "cset = ax.contourf(X, Y, Z, zdir='z', offset=-0.15, cmap=cm.viridis)\n",
    "\n",
    "# Adjust the limits, ticks and view angle\n",
    "ax.set_zlim(-0.15,0.2)\n",
    "ax.set_zticks(np.linspace(0,0.2,5))\n",
    "ax.view_init(27, -21)\n",
    "\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a 3D projection of the gaussians involved with the lower surface showing the 2D projection of the 3D projection above. The innermost ellipse represents the highest peak, that is the maximum probability for a given (X,Y) value.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "** numpy einsum examples **"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 175,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0  1  2  3  4]\n",
      " [ 5  6  7  8  9]\n",
      " [10 11 12 13 14]\n",
      " [15 16 17 18 19]\n",
      " [20 21 22 23 24]]\n",
      "[0 1 2 3 4]\n",
      "[[0 1 2]\n",
      " [3 4 5]]\n"
     ]
    }
   ],
   "source": [
    "a = np.arange(25).reshape(5,5)\n",
    "b = np.arange(5)\n",
    "c = np.arange(6).reshape(2,3)\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 204,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 30,  80, 130, 180, 230])"
      ]
     },
     "execution_count": 204,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#this is the diagonal sum, i repeated means the diagonal\n",
    "np.einsum('ij', a)\n",
    "#this takes the output ii which is the diagonal and outputs to a\n",
    "np.einsum('ii->i',a)\n",
    "#this takes in the array A represented by their axes 'ij' and  B by its only axes'j' \n",
    "#and multiples them element wise\n",
    "np.einsum('ij,j',a, b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 199,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0, 22, 76])"
      ]
     },
     "execution_count": 199,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.arange(3).reshape(3,1)\n",
    "B = np.array([[ 0,  1,  2,  3],\n",
    "              [ 4,  5,  6,  7],\n",
    "              [ 8,  9, 10, 11]])\n",
    "C=np.multiply(A,B)\n",
    "np.sum(C,axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0, 22, 76])"
      ]
     },
     "execution_count": 197,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "D = np.array([0,1,2])\n",
    "E = np.array([[ 0,  1,  2,  3],\n",
    "              [ 4,  5,  6,  7],\n",
    "              [ 8,  9, 10, 11]])\n",
    "\n",
    "np.einsum('i,ij->i',D,E)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 238,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.contour.QuadContourSet at 0x139196438>"
      ]
     },
     "execution_count": 238,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 4808 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from scipy.stats import multivariate_normal\n",
    "x, y = np.mgrid[-5:5:.1, -5:5:.1]\n",
    "pos = np.empty(x.shape + (2,))\n",
    "pos[:, :, 0] = x; pos[:, :, 1] = y\n",
    "rv = multivariate_normal([0.5, -0.2], [[2.0, 0.9], [0.9, 0.5]])\n",
    "plt.contourf(x, y, rv.pdf(pos))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### References:\n",
    "\n",
    "1. Roger Labbe's [repo](https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python) on Kalman Filters. (Majority of the examples in the notes are from this)\n",
    "\n",
    "\n",
    "\n",
    "2. Probabilistic Robotics by Sebastian Thrun, Wolfram Burgard and Dieter Fox, MIT Press.\n",
    "\n",
    "\n",
    "\n",
    "3. Scipy [Documentation](https://scipython.com/blog/visualizing-the-bivariate-gaussian-distribution/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "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.6.6"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "source": [],
    "metadata": {
     "collapsed": false
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}