{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy import stats\n",
    "from mpl_toolkits.mplot3d import Axes3D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 197232 characters]\n",
      "text/plain": [
       "<Figure size 720x720 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "cov = np.array([[1, -0.6],\n",
    "                [-0.6, 1]])\n",
    "n = 100\n",
    "x = np.linspace(-2, 2, n)\n",
    "x, y = np.meshgrid(x, x)\n",
    "pos = np.zeros([n, n, 2])\n",
    "pos[:, :, 0] = x\n",
    "pos[:, :, 1] = y\n",
    "\n",
    "norm = stats.multivariate_normal(np.zeros(2), cov)\n",
    "\n",
    "fig = plt.figure(figsize=(10, 10))\n",
    "ax = fig.gca(projection='3d')\n",
    "ax.plot_surface(x, y, norm.pdf(pos),cmap='viridis',linewidth=0)\n",
    "ax.set_zlim(0, 0.7)\n",
    "ax.set_xlabel('X axis')\n",
    "ax.set_ylabel('Y axis')\n",
    "ax.set_zlabel('Z axis')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5c51fd0>]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 33784 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "n = 50\n",
    "x = np.linspace(0, 10, n)\n",
    "\n",
    "# Define the gaussian with mu = sin(x) and negligible covariance matrix \n",
    "norm = stats.multivariate_normal(mean=np.sin(x), cov=np.eye(n) * 1e-6)\n",
    "plt.figure(figsize=(16, 6))\n",
    "\n",
    "# Taking a sample from the distribution and plotting it.\n",
    "plt.plot(x, norm.rvs())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[<matplotlib.lines.Line2D at 0x7fdac5e3b320>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac5e7c208>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac5e3ba58>]]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 117052 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "np.random.seed(18)\n",
    "\n",
    "# Define the gaussian with mu = 0 and a covariance matrix w/o any correlation, \n",
    "# but w/ uncertainty\n",
    "norm = stats.multivariate_normal(mean=np.zeros(n), cov=np.eye(n))\n",
    "plt.figure(figsize=(16, 6))\n",
    "\n",
    "# Taking 3 sample from the distribution and plotting it.\n",
    "[plt.plot(x, norm.rvs()) for _ in range(3)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[<matplotlib.lines.Line2D at 0x7fdac411bef0>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac5cbcc18>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac41236a0>]]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 98380 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def kernel(m1, m2, l=0.8):\n",
    "    return np.exp(- 1 / (2 * l**2) * (m1[:, None] - m2)**2)\n",
    "\n",
    "n = 100\n",
    "x = np.linspace(0, 10, n)\n",
    "cov = kernel(x, x, 0.215)\n",
    "\n",
    "norm = stats.multivariate_normal(mean=np.zeros(n), cov=cov)\n",
    "plt.figure(figsize=(16, 6))\n",
    "[plt.plot(x, norm.rvs()) for _ in range(3)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{equation}\n",
       "\\begin{pmatrix}\n",
       "f \\\\\n",
       "f_*\n",
       "\\end{pmatrix}\n",
       "= \\mathcal{N} \\begin{pmatrix}\n",
       "\\begin{bmatrix}\n",
       "\\mu \\\\\n",
       "\\mu_* \\\\  \n",
       "\\end{bmatrix},\n",
       "\\begin{bmatrix}\n",
       "K & K_* \\\\\n",
       "K_*^T & K_{**}\n",
       "\\end{bmatrix}\n",
       "\\end{pmatrix}\n",
       "\\end{equation}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{equation}\n",
    "\\begin{pmatrix}\n",
    "f \\\\\n",
    "f_*\n",
    "\\end{pmatrix}\n",
    "= \\mathcal{N} \\begin{pmatrix}\n",
    "\\begin{bmatrix}\n",
    "\\mu \\\\\n",
    "\\mu_* \\\\  \n",
    "\\end{bmatrix},\n",
    "\\begin{bmatrix}\n",
    "K & K_* \\\\\n",
    "K_*^T & K_{**}\n",
    "\\end{bmatrix}\n",
    "\\end{pmatrix}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.        , 0.89801781, 0.65033901, 0.3798084 ],\n",
       "       [0.89801781, 1.        , 0.89801781, 0.65033901],\n",
       "       [0.65033901, 0.89801781, 1.        , 0.89801781],\n",
       "       [0.3798084 , 0.65033901, 0.89801781, 1.        ]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# squared exponential kernel.\n",
    "# Gives more correleation to values of x close by.\n",
    "# https://www.cs.toronto.edu/~duvenaud/cookbook/\n",
    "def kernel(m1, m2, l=1):\n",
    "    return np.exp(- 1 / (2 * l**2) * (m1[:, None] - m2)**2)\n",
    "\n",
    "n = 50\n",
    "x = np.linspace(0, 10, n)\n",
    "cov = kernel(x, x, 0.44)\n",
    "# Note that x_ij close by are strongly correlated and x_ij+n less\n",
    "cov[:4, :4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[<matplotlib.lines.Line2D at 0x7fdac6e54390>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac4208198>],\n",
       " [<matplotlib.lines.Line2D at 0x7fdac6e54b38>]]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 74332 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# We can plot a function defined by a gaussian. \n",
    "# The mean dictates the mean of you distribution of functions\n",
    "# The covariance matrix determines the variance at a certain point x_i, but als determines how x_j and x_i are correlated.\n",
    "norm = stats.multivariate_normal(mean=np.zeros(n), cov=cov)\n",
    "plt.figure(figsize=(16, 6))\n",
    "[plt.plot(x, norm.rvs()) for _ in range(3)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5e1ad68>]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 18948 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(np.arange(50), cov @ np.random.normal(size=50))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5fdf278>]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 18944 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# We can also force the function to only show the mean by downscaling the covariance matrix\n",
    "norm = stats.multivariate_normal(mean=np.sin(x), cov=cov * 1e-6).rvs()\n",
    "\n",
    "plt.plot(x, norm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5d294e0>]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 20040 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "norm = stats.multivariate_normal(mean=np.ones(x.shape), cov=cov).rvs()\n",
    "\n",
    "plt.plot(x, norm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5f3e668>]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 19128 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "f = lambda x: np.sin(x)\n",
    "x = np.linspace(0, 10)\n",
    "\n",
    "plt.plot(x, f(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 10)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 8372 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "np.random.seed(512)\n",
    "# True function\n",
    "f = lambda x: np.sin(x)\n",
    "\n",
    "# Known domain of true function f\n",
    "x = np.random.uniform(0, 10, 5)\n",
    "\n",
    "plt.figure(figsize=(16, 6))\n",
    "plt.scatter(x, f(x))\n",
    "plt.xlim(0, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fdac5f7b3c8>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7b518>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7b668>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7b7b8>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7b908>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7ba58>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7bba8>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7bcf8>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7be48>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f7bf98>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac40f4dd8>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f59240>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f59390>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f594e0>,\n",
       " <matplotlib.lines.Line2D at 0x7fdac5f59630>]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 191452 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "n = 200\n",
    "# Domain of f* we want to infer \n",
    "x_s = np.linspace(0, 10, n)\n",
    "\n",
    "# covariance matrix\n",
    "K_ss = kernel(x_s, x_s, l=0.9)\n",
    " \n",
    "# Is square root of matrix (standard deviation)\n",
    "L_ss = np.linalg.cholesky(K_ss + 1e-6 * np.eye(n))\n",
    "\n",
    "# N~(0, I) * L\n",
    "f_prior = np.dot(L_ss, np.random.normal(size=(n, 15)))\n",
    "\n",
    "plt.figure(figsize=(16, 6))\n",
    "plt.plot(x_s, f_prior)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Algorithm for GP\n",
    "\n",
    "### Mean\n",
    "\n",
    "\n",
    "$\\text{L:= cholesky}(K + \\sigma^2_n I)$\n",
    "\n",
    "where $\\sigma^2_n$ is the noise level (can be close to zero if no noise), $I$ is the identity matrix, $K$ is the covariance matrix, and cholesky can be seen as the square root of the matrix.\n",
    "\n",
    "$\\alpha$:= $L^{T^I} \\cdot L^I y$\n",
    "\n",
    "$f_*$ := $k_*^T \\alpha$\n",
    "\n",
    "### variance\n",
    "v:=$L^I k_*$ \n",
    "\n",
    "The variance of the function $f_*$:\n",
    "\n",
    "$\\mathbb{V}[f_*]$:= $k_{**} - \\text{v}^T\\text{v}$\n",
    "\n",
    "So the standard deviation is:\n",
    "\n",
    "B:= $\\text{cholesky}(\\mathbb{V}[f_*])$\n",
    "\n",
    "### posterior\n",
    "$p(f|D) = \\mu + \\sigma[f_*]  \\mathcal{N}(0, 1)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# find mean mu*\n",
    "K = kernel(x, x, l=0.9)\n",
    "K_s = kernel(x, x_s, l=0.9)\n",
    "L = np.linalg.cholesky(K + 1e-6 * np.eye(len(x)))\n",
    "alpha = np.linalg.solve(L.T, np.linalg.solve(L, f(x)))\n",
    "mu = K_s.T @ alpha"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "v = np.linalg.solve(L, K_s)\n",
    "variance = K_ss - v.T @ v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 125876 characters]\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "np.random.seed(18)\n",
    "\n",
    "# Sample 10 functions from the posterior\n",
    "B = np.linalg.cholesky(variance + 1e-6 * np.eye(variance.shape[0]))\n",
    "f_post = mu[:, None] + B @ np.random.normal(size=(n, 10))\n",
    "\n",
    "# Find standard deviation for plotting uncertainty values.\n",
    "s2 = np.ones(n) - np.sum(v**2, axis=0)\n",
    "std = np.sqrt(s2)\n",
    "\n",
    "plt.figure(figsize=(16, 6))\n",
    "plt.plot(x, f(x), 'bs', ms=8, color='red')\n",
    "plt.plot(x_s, f_post)\n",
    "plt.fill_between(x_s, mu - 2 * std, mu + 2 * std, color=\"#dddddd\")\n",
    "plt.plot(x_s, mu, 'r--', lw=2)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{equation}\n",
       "p(x|y) = \\mathcal{N}(\\underbrace{\\mu_x + \\Sigma_{xy} \\Sigma_y^{-1}(y - \\mu_y)}_\\textrm{conditional mean} ,  \n",
       "                     \\underbrace{\\Sigma_x - \\Sigma_{xy}\\Sigma_y^{-1}\\Sigma_{xy}^T}_\\textrm{conditional covariance})\n",
       "\\end{equation}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{equation}\n",
    "p(x|y) = \\mathcal{N}(\\underbrace{\\mu_x + \\Sigma_{xy} \\Sigma_y^{-1}(y - \\mu_y)}_\\textrm{conditional mean} ,  \n",
    "                     \\underbrace{\\Sigma_x - \\Sigma_{xy}\\Sigma_y^{-1}\\Sigma_{xy}^T}_\\textrm{conditional covariance})\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{equation}\n",
       "p(x, y) = \\mathcal{N} \\begin{pmatrix}\n",
       "\\begin{bmatrix}\n",
       "\\mu_x \\\\\n",
       "\\mu_y \\\\  \n",
       "\\end{bmatrix},\n",
       "\\begin{bmatrix}\n",
       "\\Sigma_x & \\Sigma_{xy} \\\\\n",
       "\\Sigma_{xy}^T & \\Sigma_y\n",
       "\\end{bmatrix}\n",
       "\\end{pmatrix}\n",
       "\\end{equation}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{equation}\n",
    "p(x, y) = \\mathcal{N} \\begin{pmatrix}\n",
    "\\begin{bmatrix}\n",
    "\\mu_x \\\\\n",
    "\\mu_y \\\\  \n",
    "\\end{bmatrix},\n",
    "\\begin{bmatrix}\n",
    "\\Sigma_x & \\Sigma_{xy} \\\\\n",
    "\\Sigma_{xy}^T & \\Sigma_y\n",
    "\\end{bmatrix}\n",
    "\\end{pmatrix}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\n",
       "\\begin{equation}\n",
       "\n",
       "\\begin{pmatrix}\n",
       "    f(x_1) \\\\\n",
       "    f(x_2)  \\\\\n",
       "    \\vdots \\\\\n",
       "    f(x_n)\n",
       "\\end{pmatrix} \\sim \\mathcal{N}\n",
       "\n",
       "\\begin{pmatrix}\n",
       "    \\begin{pmatrix}\n",
       "        \\mu_1 \\\\\n",
       "        \\mu_2  \\\\\n",
       "        \\vdots \\\\\n",
       "        \\mu_n\n",
       "    \\end{pmatrix},\n",
       "    \n",
       "    \\begin{pmatrix}\n",
       "        \\sigma_{11} & \\sigma_{12} & \\cdots & \\sigma_{1n} \\\\\n",
       "        \\sigma_{21} & \\sigma_{22} & \\cdots & \\sigma_{2n}  \\\\\n",
       "        \\vdots      & \\vdots        & \\ddots \\\\\n",
       "        \\sigma_n    & \\sigma_{n2} &        & \\sigma_{nn}\n",
       "    \\end{pmatrix}\n",
       "\\end{pmatrix}\n",
       "\n",
       "\\end{equation}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\n",
    "\\begin{equation}\n",
    "\n",
    "\\begin{pmatrix}\n",
    "    f(x_1) \\\\\n",
    "    f(x_2)  \\\\\n",
    "    \\vdots \\\\\n",
    "    f(x_n)\n",
    "\\end{pmatrix} \\sim \\mathcal{N}\n",
    "\n",
    "\\begin{pmatrix}\n",
    "    \\begin{pmatrix}\n",
    "        \\mu_1 \\\\\n",
    "        \\mu_2  \\\\\n",
    "        \\vdots \\\\\n",
    "        \\mu_n\n",
    "    \\end{pmatrix},\n",
    "    \n",
    "    \\begin{pmatrix}\n",
    "        \\sigma_{11} & \\sigma_{12} & \\cdots & \\sigma_{1n} \\\\\n",
    "        \\sigma_{21} & \\sigma_{22} & \\cdots & \\sigma_{2n}  \\\\\n",
    "        \\vdots      & \\vdots        & \\ddots \\\\\n",
    "        \\sigma_n    & \\sigma_{n2} &        & \\sigma_{nn}\n",
    "    \\end{pmatrix}\n",
    "\\end{pmatrix}\n",
    "\n",
    "\\end{equation}"
   ]
  }
 ],
 "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.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
