{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
    "<script>\n",
    "  window.dataLayer = window.dataLayer || [];\n",
    "  function gtag(){dataLayer.push(arguments);}\n",
    "  gtag('js', new Date());\n",
    "\n",
    "  gtag('config', 'UA-59152712-8');\n",
    "</script>\n",
    "\n",
    "# Tutorial-IllinoisGRMHD: eigen.C\n",
    "\n",
    "## Authors: Leo Werneck & Zach Etienne\n",
    "\n",
    "<font color='red'>**This module is currently under development**</font>\n",
    "\n",
    "## This tutorial module demonstrates how to obtain the eigenvalues of a $3\\times3$ matrix. \n",
    "\n",
    "### Note: This module will likely be absorbed by another one once we finish documenting the code.\n",
    "\n",
    "### Required and recommended citations:\n",
    "\n",
    "* **(Required)** Etienne, Z. B., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L. IllinoisGRMHD: an open-source, user-friendly GRMHD code for dynamical spacetimes. Class. Quantum Grav. 32 (2015) 175009. ([arxiv:1501.07276](http://arxiv.org/abs/1501.07276)).\n",
    "* **(Required)** Noble, S. C., Gammie, C. F., McKinney, J. C., Del Zanna, L. Primitive Variable Solvers for Conservative General Relativistic Magnetohydrodynamics. Astrophysical Journal, 641, 626 (2006) ([astro-ph/0512420](https://arxiv.org/abs/astro-ph/0512420)).\n",
    "* **(Recommended)** Del Zanna, L., Bucciantini N., Londrillo, P. An efficient shock-capturing central-type scheme for multidimensional relativistic flows - II. Magnetohydrodynamics. A&A 400 (2) 397-413 (2003). DOI: 10.1051/0004-6361:20021641 ([astro-ph/0210618](https://arxiv.org/abs/astro-ph/0210618))."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='toc'></a>\n",
    "\n",
    "# Table of Contents\n",
    "$$\\label{toc}$$\n",
    "\n",
    "This module is organized as follows\n",
    "\n",
    "0. [Step 0](#src_dir): **Source directory creation**\n",
    "1. [Step 1](#introduction): **Introduction**\n",
    "1. [Step 2](#eigen__c): **`eigen.C`**\n",
    "    1. [Step 2.a](#eigen__c__variables): *The variables used in `eigen.C`*\n",
    "    1. [Step 2.b](#eigen__c__phi): *Determining $\\phi$*\n",
    "    1. [Step 2.c](#eigen__c__eigenvalues): *The eigenvalues of a $3\\times3$ symmetric matrix*\n",
    "1. [Step 3](#code_validation): **Code validation**\n",
    "1. [Step 4](#latex_pdf_output): **Output this notebook to $\\LaTeX$-formatted PDF file**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='src_dir'></a>\n",
    "\n",
    "# Step 0: Source directory creation \\[Back to [top](#toc)\\]\n",
    "$$\\label{src_dir}$$\n",
    "\n",
    "We will now use the [cmdline_helper.py NRPy+ module](Tutorial-Tutorial-cmdline_helper.ipynb) to create the source directory within the `IllinoisGRMHD` NRPy+ directory, if it does not exist yet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Step 0: Creation of the IllinoisGRMHD source directory\n",
    "# Step 0a: Add NRPy's directory to the path\n",
    "# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory\n",
    "import os,sys\n",
    "nrpy_dir_path = os.path.join(\"..\",\"..\")\n",
    "if nrpy_dir_path not in sys.path:\n",
    "    sys.path.append(nrpy_dir_path)\n",
    "\n",
    "# Step 0b: Load up cmdline_helper and create the directory\n",
    "import cmdline_helper as cmd\n",
    "IGM_src_dir_path = os.path.join(\"..\",\"src\")\n",
    "cmd.mkdir(IGM_src_dir_path)\n",
    "\n",
    "# Step 0c: Create the output file path\n",
    "outfile_path__eigen__C = os.path.join(IGM_src_dir_path,\"eigen.C\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='introduction'></a>\n",
    "\n",
    "# Step 1: Introduction \\[Back to [top](#toc)\\]\n",
    "$$\\label{introduction}$$\n",
    "\n",
    "In this tutorial notebook we will implement an algorithm to evaluate the eigenvalues of a $3\\times3$ symmetric matrix. Our method will be analytical and will follow closely [this discussion](https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3%C3%973_matrices).\n",
    "\n",
    "Let $\\mathcal{M}$ be a $3\\times3$ symmetric matrix,\n",
    "\n",
    "$$\n",
    "\\mathcal{M} =\n",
    "\\begin{pmatrix}\n",
    "M_{11} & M_{12} & M_{13}\\\\\n",
    "M_{12} & M_{22} & M_{23}\\\\\n",
    "M_{13} & M_{23} & M_{33}\n",
    "\\end{pmatrix}\\ .\n",
    "$$\n",
    "\n",
    "To obtain the eigenvalues of $\\mathcal{M}$, we must solve the *characteristic equation*\n",
    "\n",
    "$$\n",
    "\\det\\left(\\lambda I_{3\\times3} - \\mathcal{M}\\right) = 0\\ ,\n",
    "$$\n",
    "\n",
    "where $\\lambda$ represents the eigenvalues of $\\mathcal{M}$ and $I_{3\\times3} = {\\rm diag}\\left(1,1,1\\right)$ is the $3\\times3$ identity matrix. For this particular case, the characteristic equation of $\\mathcal{M}$ is then given by\n",
    "\n",
    "$$\n",
    "\\lambda^{3} - {\\rm tr}\\left(\\mathcal{M}\\right)\\lambda^{2} + \\left[\\frac{{\\rm tr}\\left(\\mathcal{M}^{2}\\right) - {\\rm tr}\\left(\\mathcal{M}\\right)^{2}}{2}\\right]\\lambda - \\det\\left(\\mathcal{M}\\right) = 0\\ .\n",
    "$$\n",
    "\n",
    "Now let $\\mathcal{M} = n\\mathcal{N} + mI_{3\\times3}$, so that the matrices $\\mathcal{M}$ and $\\mathcal{N}$ have the same eigenvectors. Then, $\\kappa$ is an eigenvalue of $\\mathcal{N}$ if, and only if, $\\lambda = n\\kappa + m$ is an eigenvalue of $\\mathcal{M}$. Now, let us look at the following identities:\n",
    "\n",
    "$$\n",
    "\\mathcal{N} = \\frac{1}{n}\\left(\\mathcal{M} - mI_{3\\times3}\\right)\\ .\n",
    "$$\n",
    "\n",
    "Choosing $m \\equiv \\frac{1}{3}{\\rm tr}\\left(\\mathcal{M}\\right)$, we get\n",
    "\n",
    "$$\n",
    "{\\rm tr}\\left(\\mathcal{N}\\right) = \\frac{1}{n}\\left(\\mathcal{M} - 3m\\right)=0\\ .\n",
    "$$\n",
    "\n",
    "Also,\n",
    "\n",
    "$$\n",
    "{\\rm tr}\\left(\\mathcal{N}^{2}\\right) = \\frac{1}{n^{2}}\\left[N_{11}^{2}+N_{22}^{2}+N_{33}^{2}+2\\left(N_{12}^{2}+N_{13}^{2}+N_{23}^{2}\\right)\\right]\\ ,\n",
    "$$\n",
    "\n",
    "so that if we choose $n\\equiv\\sqrt{\\frac{N_{11}^{2}+N_{22}^{2}+N_{33}^{2}+2\\left(N_{12}^{2}+N_{13}^{2}+N_{23}^{2}\\right)}{6}}$ we get\n",
    "\n",
    "$$\n",
    "{\\rm tr}\\left(\\mathcal{N}^{2}\\right) = 6\\ .\n",
    "$$\n",
    "\n",
    "Then, if we look at the characteristic equation for the matrix $\\mathcal{N}$,\n",
    "\n",
    "$$\n",
    "\\kappa^{3} - {\\rm tr}\\left(\\mathcal{N}\\right)\\kappa^{2} + \\left[\\frac{{\\rm tr}\\left(\\mathcal{N}^{2}\\right) - {\\rm tr}\\left(\\mathcal{N}\\right)^{2}}{2}\\right]\\kappa - \\det\\left(\\mathcal{N}\\right) = 0\\ ,\n",
    "$$\n",
    "\n",
    "we see that it can be greatly simplified with our choices of $m$ and $n$,\n",
    "\n",
    "$$\n",
    "\\kappa^{3} - 3\\kappa - \\det\\left(\\mathcal{N}\\right) = 0\\ .\n",
    "$$\n",
    "\n",
    "Further simplification of this characteristic equation can be obtained by using\n",
    "\n",
    "$$\n",
    "\\begin{align}\n",
    "\\kappa &\\equiv 2\\cos\\phi\\ ,\\\\\n",
    "\\cos\\left(3\\phi\\right) &= 4\\cos^{3}\\phi - 3\\cos\\phi\\ ,\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "so that\n",
    "\n",
    "$$\n",
    "\\begin{align}\n",
    "0 &= 8\\cos^{3}\\phi - 6\\cos\\phi - \\det\\left(\\mathcal{N}\\right)\\\\\n",
    "  &= 2\\cos\\left(3\\phi\\right) - \\det\\left(\\mathcal{N}\\right)\\\\\n",
    "\\implies \\phi &= \\frac{1}{3}\\arccos\\frac{\\det\\left(\\mathcal{N}\\right)}{2} + \\frac{2k\\pi}{3}\\ ,\\ k=0,1,2\\ ,\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "which, finally, yields\n",
    "\n",
    "$$\n",
    "\\boxed{\\kappa\\left(k\\right) = 2\\cos\\left(\\frac{1}{3}\\arccos\\frac{\\det\\left(\\mathcal{N}\\right)}{2}+\\frac{2k\\pi}{3}\\right)}\\ .\n",
    "$$\n",
    "\n",
    "Once we have $\\kappa$, we can find the eigenvectors of $\\mathcal{M}$ doing\n",
    "\n",
    "$$\n",
    "\\boxed{\n",
    "\\begin{align}\n",
    "\\lambda_{1} &= m + 2n\\kappa(0)\\\\\n",
    "\\lambda_{2} &= m + 2n\\kappa(1)\\\\\n",
    "\\lambda_{3} &= 3m - \\lambda_{1} - \\lambda_{2}\n",
    "\\end{align}\n",
    "}\\ ,\n",
    "$$\n",
    "\n",
    "where we have used the fact that ${\\rm tr}\\left(\\mathcal{M}\\right)=\\lambda_{1}+\\lambda_{2}+\\lambda_{3}$ to compute $\\lambda_{3}$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='eigen__c'></a>\n",
    "\n",
    "# Step 2: `eigen.C` \\[Back to [top](#toc)\\]\n",
    "$$\\label{eigen__c}$$\n",
    "\n",
    "<a id='eigen__c__variables'></a>\n",
    "\n",
    "## Step 2.a: The variables used in `eigen.C` \\[Back to [top](#toc)\\]\n",
    "$$\\label{eigen__c__variables}$$\n",
    "\n",
    "In the algorithm below, we define the following quantities\n",
    "\n",
    "$$\n",
    "\\boxed{\n",
    "\\begin{align}\n",
    "\\mathcal{K} &= \\mathcal{M} - mI_{3\\times3}\\\\\n",
    "m &= \\frac{{\\rm tr}\\left(\\mathcal{M}\\right)}{3}\\\\\n",
    "q &= \\frac{\\det\\left(\\mathcal{K}\\right)}{2}\\\\\n",
    "p &= n^{2} = \\frac{{\\rm tr}\\left(\\mathcal{K}^{2}\\right)}{6}\n",
    "\\end{align}\n",
    "}\\ .\n",
    "$$\n",
    "\n",
    "We these definitions, we have the following quantities to be implemented:\n",
    "\n",
    "$$\n",
    "\\boxed{ m = \\frac{\\left(M_{11} + M_{22} + M_{33}\\right)}{3} }\\ .\n",
    "$$\n",
    "\n",
    "The matrix $\\mathcal{K}$ is simply\n",
    "\n",
    "$$\n",
    "\\boxed{\n",
    "\\mathcal{K} =\n",
    "\\begin{pmatrix}\n",
    "M_{11}-m & M_{12} & M_{13}\\\\\n",
    "M_{12} & M_{22}-m & M_{23}\\\\\n",
    "M_{13} & M_{23} & M_{33}-m\n",
    "\\end{pmatrix}\n",
    "}\\ .\n",
    "$$\n",
    "\n",
    "Straightforwardly, we have\n",
    "\n",
    "$$\n",
    "\\boxed{q = \\frac{K_{11}K_{22}K_{33} +\n",
    "          K_{12}K_{23}K_{13} +\n",
    "          K_{13}K_{12}K_{23} -\n",
    "          K_{13}K_{22}K_{13} -\n",
    "          K_{12}K_{12}K_{33} -\n",
    "          K_{11}K_{23}K_{23}\n",
    "          }{2}\n",
    "          }\\ .\n",
    "$$\n",
    "\n",
    "Since $\\mathcal{K}$ is symmetric as well, we have\n",
    "\n",
    "$$\n",
    "\\boxed{p = \\frac{K_{11}^{2} + K_{22}^{2} + K_{33}^{2} + 2\\left(K_{12}^{2} + K_{13}^{2} + K_{23}^{2}\\right)}{6}}\\ .\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing ../src/eigen.C\n"
     ]
    }
   ],
   "source": [
    "%%writefile $outfile_path__eigen__C\n",
    "//\n",
    "// This subroutine calcualtes the eigenvalues of a real, symmetric 3x3\n",
    "// matrix M={{M11,M12,M13},{M12,M22,M23},{M13,M23,M33}} based on the\n",
    "// algorithm described in\n",
    "// http://en.wikipedia.org/wiki/Eigenvalue_algorithm#Eigenvalues_of_3.C3.973_matrices\n",
    "// which simply solve the cubic equation Det( M - lamnda I)=0 analytically.\n",
    "// The eigenvalues are stored in lam1, lam2 and lam3.\n",
    "//\n",
    "void eigenvalues_3by3_real_sym_matrix(CCTK_REAL & lam1, CCTK_REAL & lam2, CCTK_REAL & lam3,\n",
    "                                      CCTK_REAL M11, CCTK_REAL M12, CCTK_REAL M13, CCTK_REAL M22, CCTK_REAL M23, CCTK_REAL M33)\n",
    "{\n",
    "  CCTK_REAL m = (M11 + M22 + M33)/3.0;\n",
    "  CCTK_REAL K11 = M11 - m, K12 = M12, K13 = M13, K22 = M22-m, K23 = M23, K33=M33-m;\n",
    "  CCTK_REAL q = 0.5* (K11*K22*K33 + K12*K23*K13 + K13*K12*K23 - K13*K22*K13\n",
    "                      - K12*K12*K33 - K11*K23*K23);\n",
    "  CCTK_REAL p = ( SQR(K11) + SQR(K22) + SQR(K33) + 2.0*(SQR(K12) + SQR(K13) + SQR(K23) ) )/6.0;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='eigen__c__phi'></a>\n",
    "\n",
    "## Step 2.b: Determining $\\phi$ \\[Back to [top](#toc)\\]\n",
    "$$\\label{eigen__c__phi}$$\n",
    "\n",
    "We then employ the following criterion to determine $\\phi$:\n",
    "\n",
    "$$\n",
    "\\phi\n",
    "=\n",
    "\\left\\{\n",
    "\\begin{matrix}\n",
    "0 &,\\ {\\rm if}\\ \\left|q\\right| \\geq p^{3/2}\\ ,\\\\\n",
    "\\arccos\\left(\\frac{q}{p^{3/2}}\\right) &,\\ {\\rm otherwise}\\ .\n",
    "\\end{matrix}\n",
    "\\right.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to ../src/eigen.C\n"
     ]
    }
   ],
   "source": [
    "%%writefile -a $outfile_path__eigen__C\n",
    "\n",
    "  CCTK_REAL phi;\n",
    "  CCTK_REAL p32 = sqrt(p*p*p);\n",
    "  if (fabs(q) >= fabs(p32) ) {\n",
    "    phi = 0.0;\n",
    "  } else {\n",
    "    phi = acos(q/p32)/3.0;\n",
    "  }\n",
    "  if (phi<0.0) phi += M_PI/3.0;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='eigen__c__eigenvalues'></a>\n",
    "\n",
    "## Step 2.c: The eigenvalues of a $3\\times3$ symmetric matrix \\[Back to [top](#toc)\\]\n",
    "$$\\label{eigen__c__eigenvalues}$$\n",
    "\n",
    "Finally, the eigenvalues are computed using\n",
    "\n",
    "$$\n",
    "\\boxed{\n",
    "\\begin{align}\n",
    "\\lambda_{1} &= m + 2\\sqrt{p}\\cos\\phi\\\\\n",
    "\\lambda_{2} &= m - \\sqrt{p}\\cos\\phi - \\sqrt{3p}\\sin\\phi\\\\\n",
    "\\lambda_{3} &= m - \\sqrt{p}\\cos\\phi + \\sqrt{3p}\\sin\\phi\n",
    "\\end{align}\n",
    "}\\ .\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to ../src/eigen.C\n"
     ]
    }
   ],
   "source": [
    "%%writefile -a $outfile_path__eigen__C\n",
    "\n",
    "  CCTK_REAL sqrtp = sqrt(p);\n",
    "  CCTK_REAL sqrtp_cosphi = sqrtp*cos(phi);\n",
    "  CCTK_REAL sqrtp_sqrt3_sinphi = sqrtp*sqrt(3.0)*sin(phi);\n",
    "  lam1 = m + 2.0*sqrtp_cosphi;\n",
    "  lam2 = m - sqrtp_cosphi - sqrtp_sqrt3_sinphi;\n",
    "  lam3 = m - sqrtp_cosphi + sqrtp_sqrt3_sinphi;\n",
    "}\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='code_validation'></a>\n",
    "\n",
    "# Step 3: Code validation \\[Back to [top](#toc)\\]\n",
    "$$\\label{code_validation}$$\n",
    "\n",
    "First we download the original `IllinoisGRMHD` source code and then compare it to the source code generated by this tutorial notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation test for eigen.C: FAILED!\n",
      "Diff:\n",
      "16a17\n",
      "> \n",
      "24a26\n",
      "> \n",
      "31a34\n",
      "> \n"
     ]
    }
   ],
   "source": [
    "# Verify if the code generated by this tutorial module\n",
    "# matches the original IllinoisGRMHD source code\n",
    "\n",
    "# First download the original IllinoisGRMHD source code\n",
    "import urllib\n",
    "from os import path\n",
    "\n",
    "original_IGM_file_url  = \"https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/IllinoisGRMHD/src/eigen.C\"\n",
    "original_IGM_file_name = \"eigen-original.C\"\n",
    "original_IGM_file_path = os.path.join(IGM_src_dir_path,original_IGM_file_name)\n",
    "\n",
    "# Then download the original IllinoisGRMHD source code\n",
    "# We try it here in a couple of ways in an attempt to keep\n",
    "# the code more portable\n",
    "try:\n",
    "    original_IGM_file_code = urllib.request.urlopen(original_IGM_file_url).read().decode(\"utf-8\")\n",
    "    # Write down the file the original IllinoisGRMHD source code\n",
    "    with open(original_IGM_file_path,\"w\") as file:\n",
    "        file.write(original_IGM_file_code)\n",
    "except:\n",
    "    try:\n",
    "        original_IGM_file_code = urllib.urlopen(original_IGM_file_url).read().decode(\"utf-8\")\n",
    "        # Write down the file the original IllinoisGRMHD source code\n",
    "        with open(original_IGM_file_path,\"w\") as file:\n",
    "            file.write(original_IGM_file_code)\n",
    "    except:\n",
    "        # If all else fails, hope wget does the job\n",
    "        !wget -O $original_IGM_file_path $original_IGM_file_url\n",
    "\n",
    "# Perform validation\n",
    "Validation__eigen__C  = !diff $original_IGM_file_path $outfile_path__eigen__C\n",
    "\n",
    "if Validation__eigen__C == []:\n",
    "    # If the validation passes, we do not need to store the original IGM source code file\n",
    "    !rm $original_IGM_file_path\n",
    "    print(\"Validation test for eigen.C: PASSED!\")\n",
    "else:\n",
    "    # If the validation fails, we keep the original IGM source code file\n",
    "    print(\"Validation test for eigen.C: FAILED!\")\n",
    "    # We also print out the difference between the code generated\n",
    "    # in this tutorial module and the original IGM source code\n",
    "    print(\"Diff:\")\n",
    "    for diff_line in Validation__eigen__C:\n",
    "        print(diff_line)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='latex_pdf_output'></a>\n",
    "\n",
    "# Step 4: Output this notebook to $\\LaTeX$-formatted PDF file \\[Back to [top](#toc)\\]\n",
    "$$\\label{latex_pdf_output}$$\n",
    "\n",
    "The following code cell converts this Jupyter notebook into a proper, clickable $\\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename\n",
    "[Tutorial-IllinoisGRMHD__eigen.pdf](Tutorial-IllinoisGRMHD__eigen.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "latex_nrpy_style_path = os.path.join(nrpy_dir_path,\"latex_nrpy_style.tplx\")\n",
    "#!jupyter nbconvert --to latex --template $latex_nrpy_style_path --log-level='WARN' Tutorial-IllinoisGRMHD__eigen.ipynb\n",
    "#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__eigen.tex\n",
    "#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__eigen.tex\n",
    "#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__eigen.tex\n",
    "!rm -f Tut*.out Tut*.aux Tut*.log"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}