{ "cells": [ { "cell_type": "markdown", "metadata": { "nbsphinx": "hidden" }, "source": [ "This notebook is part of the `galgebra` documentation: https://galgebra.readthedocs.io/." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to using GAlgebra\n", "\n", "This is a tutorial to introduce you to `galgebra`, a symbolic geometric algebra library for python.\n", "\n", "A geometric algebra is defined by a set of symbols that represent the basis vectors of a real vector space, a metric tensor, and possible a set of coordinate symbols. If coordinates are defined the metric tensor can be a function of them.\n", "\n", "The following cell imports all the functions needed in the tutorial from `sympy`, `ga`, and `printer`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import sympy\n", "from galgebra.ga import Ga\n", "from galgebra.printer import latex\n", "from IPython.display import Math\n", "\n", "# tell sympy to use our printing by default\n", "sympy.init_printing(latex_printer=latex, use_latex='mathjax')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Printing in sympy\n", "\n", "Sympy will show pretty $\\LaTeX$ renderings of symbolic expressions by default" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle n^{2}$" ], "text/plain": [ " 2\n", "n " ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sympy.S('n')**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But if we want to append freeform text on the same line, we must use `Math`, `latex`, and f-strings in tandem:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle y = n^{2}$" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Math(f\"y = { latex(sympy.S('n')**2) }\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating an algebra\n", "\n", "To start with we will define the geometric algebra of a 3 dimensional Euclidaen vector space, `o3d`, with coordinates $x$, $y$, and $z$ and unit vectors $e_x$, $e_y$, and $e_z$." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "xyz = (x, y, z) = sympy.symbols('x y z', real=True)\n", "o3d = Ga('e_x e_y e_z', g=[1, 1, 1], coords=xyz)\n", "grad = o3d.grad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The metric tensor $g$ is:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle g = \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & 1 & 0\\\\0 & 0 & 1\\end{array}\\right]$" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Math(f'g = {latex(o3d.g)}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating multivectors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The most general element of a geometric algebra is a multivector. To define a scalar `S`, a vector `V`, a bivector `B`, and a pseudo-scalar `P` (these are the only pure grade multivectors we can have in three dimensions):" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle S = S$" ], "text/plain": [ "S" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o3d.mv('S', 'scalar')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle V = V^{x} \\boldsymbol{e}_{x} + V^{y} \\boldsymbol{e}_{y} + V^{z} \\boldsymbol{e}_{z}$" ], "text/plain": [ "V__x*e_x + V__y*e_y + V__z*e_z" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o3d.mv('V', 'vector')" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle B = B^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + B^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + B^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}$" ], "text/plain": [ "B__xy*e_x^e_y + B__xz*e_x^e_z + B__yz*e_y^e_z" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o3d.mv('B', 'bivector')" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle I = I^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}$" ], "text/plain": [ "I__xyz*e_x^e_y^e_z" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o3d.mv('I', 'pseudo')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also extract the basis vectors from `o3d`. If we name them `ex`, `ey`, and `ez` and form vectors from linear combinations of them:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\boldsymbol{e}_{x}, \\boldsymbol{e}_{y}, \\boldsymbol{e}_{z}$" ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex, ey, ez = o3d.mv()\n", "Math(f'{latex(ex)}, {latex(ey)}, {latex(ez)}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multivector operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Binary operations that we can apply to vectors or multivectors in general are addition, `+`, subtraction, `-`, geometric product, `*`, inner (dot) product, `|`, outer (wedge) product, `^`, left contraction, `<`, right contraction, `>`.\n", "Because operator precedence is immuatable in Python we need to always use parenthesis to determine the correct order of the operations in our expression. Examples for `+`, `-`, `*`, `|`, and `^` follow:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \n", "\\begin{align}\n", " a &= a^{x} \\boldsymbol{e}_{x} + a^{y} \\boldsymbol{e}_{y} + a^{z} \\boldsymbol{e}_{z} \\\\\n", " b &= b^{x} \\boldsymbol{e}_{x} + b^{y} \\boldsymbol{e}_{y} + b^{z} \\boldsymbol{e}_{z}\n", "\\end{align}\n", "$" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = o3d.mv('a','vector')\n", "b = o3d.mv('b','vector')\n", "Math(fr'''\n", "\\begin{{align}}\n", " a &= {latex(a)} \\\\\n", " b &= {latex(b)}\n", "\\end{{align}}\n", "''')" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \n", "\\begin{align}\n", " a+b &= \\left ( a^{x} + b^{x}\\right ) \\boldsymbol{e}_{x} + \\left ( a^{y} + b^{y}\\right ) \\boldsymbol{e}_{y} + \\left ( a^{z} + b^{z}\\right ) \\boldsymbol{e}_{z} \\\\\n", " a-b &= \\left ( a^{x} - b^{x}\\right ) \\boldsymbol{e}_{x} + \\left ( a^{y} - b^{y}\\right ) \\boldsymbol{e}_{y} + \\left ( a^{z} - b^{z}\\right ) \\boldsymbol{e}_{z} \\\\\n", " ab &= \\left ( a^{x} b^{x} + a^{y} b^{y} + a^{z} b^{z}\\right ) + \\left ( a^{x} b^{y} - a^{y} b^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( a^{x} b^{z} - a^{z} b^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( a^{y} b^{z} - a^{z} b^{y}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", " a\\cdot b &= a^{x} b^{x} + a^{y} b^{y} + a^{z} b^{z} \\\\\n", " a \\rfloor b &= a^{x} b^{x} + a^{y} b^{y} + a^{z} b^{z} \\\\\n", " a \\lfloor b &= a^{x} b^{x} + a^{y} b^{y} + a^{z} b^{z} \\\\\n", " a\\wedge b &= \\left ( a^{x} b^{y} - a^{y} b^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( a^{x} b^{z} - a^{z} b^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( a^{y} b^{z} - a^{z} b^{y}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}\n", "\\end{align}\n", "$" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Math(fr'''\n", "\\begin{{align}}\n", " a+b &= {latex(a+b)} \\\\\n", " a-b &= {latex(a-b)} \\\\\n", " ab &= {latex(a*b)} \\\\\n", " a\\cdot b &= {latex(a|b)} \\\\\n", " a \\rfloor b &= {latex(ab)} \\\\\n", " a\\wedge b &= {latex(a^b)}\n", "\\end{{align}}\n", "''')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle B = B^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + B^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + B^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}$" ], "text/plain": [ "B__xy*e_x^e_y + B__xz*e_x^e_z + B__yz*e_y^e_z" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = o3d.mv('B','bivector')\n", "B" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \n", "\\begin{align}\n", "BB &= - {\\left ( B^{xy} \\right )}^{2} - {\\left ( B^{xz} \\right )}^{2} - {\\left ( B^{yz} \\right )}^{2} \\\\\n", "a+B &= a^{x} \\boldsymbol{e}_{x} + a^{y} \\boldsymbol{e}_{y} + a^{z} \\boldsymbol{e}_{z} + B^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + B^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + B^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "a-B &= a^{x} \\boldsymbol{e}_{x} + a^{y} \\boldsymbol{e}_{y} + a^{z} \\boldsymbol{e}_{z} - B^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} - B^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} - B^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "aB &= \\left ( - B^{xy} a^{y} - B^{xz} a^{z}\\right ) \\boldsymbol{e}_{x} + \\left ( B^{xy} a^{x} - B^{yz} a^{z}\\right ) \\boldsymbol{e}_{y} + \\left ( B^{xz} a^{x} + B^{yz} a^{y}\\right ) \\boldsymbol{e}_{z} + \\left ( B^{xy} a^{z} - B^{xz} a^{y} + B^{yz} a^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "a\\cdot B &= \\left ( - B^{xy} a^{y} - B^{xz} a^{z}\\right ) \\boldsymbol{e}_{x} + \\left ( B^{xy} a^{x} - B^{yz} a^{z}\\right ) \\boldsymbol{e}_{y} + \\left ( B^{xz} a^{x} + B^{yz} a^{y}\\right ) \\boldsymbol{e}_{z} \\\\\n", "a \\rfloor B &= \\left ( - B^{xy} a^{y} - B^{xz} a^{z}\\right ) \\boldsymbol{e}_{x} + \\left ( B^{xy} a^{x} - B^{yz} a^{z}\\right ) \\boldsymbol{e}_{y} + \\left ( B^{xz} a^{x} + B^{yz} a^{y}\\right ) \\boldsymbol{e}_{z} \\\\\n", "a \\lfloor B &= 0 \\\\\n", "a\\wedge B &= \\left ( B^{xy} a^{z} - B^{xz} a^{y} + B^{yz} a^{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "\\end{align}\n", "$" ], "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Math(fr'''\n", "\\begin{{align}}\n", "BB &= {latex(B*B)} \\\\\n", "a+B &= {latex(a+B)} \\\\\n", "a-B &= {latex(a-B)} \\\\\n", "aB &= {latex(a*B)} \\\\\n", "a\\cdot B &= {latex(a|B)} \\\\\n", "a \\rfloor B &= {latex(aB)} \\\\\n", "a\\wedge B &= {latex(a^B)} \\\\\n", "\\end{{align}}\n", "''')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More examples\n", "\n", "Additionally, we can define multivector fields that are functions of the coordinates. Some concrete examples are (vector and bivector fields):" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \n", "\\begin{align}\n", "\\text{Vector Field:} && V_f &= x^{2} \\boldsymbol{e}_{x} + y^{2} \\boldsymbol{e}_{y} + z^{2} \\boldsymbol{e}_{z} \\\\\n", "\\text{Bivector Field:} && B_f &= z \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + y \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + x \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}\n", "\\end{align}\n", "$" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Vf = x**2*ex + y**2*ey + z**2*ez\n", "Bf = x*(ey^ez) + y*(ex^ez) + z*(ex^ey)\n", "Math(fr'''\n", "\\begin{{align}}\n", "\\text{{Vector Field:}} && V_f &= {latex(Vf)} \\\\\n", "\\text{{Bivector Field:}} && B_f &= {latex(Bf)}\n", "\\end{{align}}\n", "''')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to binary algebraic operations the most important member functions for multivectors are `grade(i)`, `rev()`, and `norm2()`. For a general multivector, `M`, we have:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle M = M + M^{x} \\boldsymbol{e}_{x} + M^{y} \\boldsymbol{e}_{y} + M^{z} \\boldsymbol{e}_{z} + M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} + M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}$" ], "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = o3d.mv('M', 'mv')\n", "Math('M = %s' % latex(M))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \n", "\\begin{align}\n", "\\text{Grade 0:} && \\left_0 &= M \\\\\n", "\\text{Grade 1:} && \\left_1 &= M^{x} \\boldsymbol{e}_{x} + M^{y} \\boldsymbol{e}_{y} + M^{z} \\boldsymbol{e}_{z} \\\\\n", "\\text{Grade 2:} && \\left_2 &= M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "\\text{Grade 3:} && \\left_3 &= M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\\n", "\\text{Reverse:} && M^\\dagger &= M + M^{x} \\boldsymbol{e}_{x} + M^{y} \\boldsymbol{e}_{y} + M^{z} \\boldsymbol{e}_{z} - M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} - M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} - M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} - M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}\n", "\\end{align}\n", "$" ], "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Math(fr'''\n", "\\begin{{align}}\n", "\\text{{Grade 0:}} && \\left_0 &= {latex(M.grade(0))} \\\\\n", "\\text{{Grade 1:}} && \\left_1 &= {latex(M.grade(1))} \\\\\n", "\\text{{Grade 2:}} && \\left_2 &= {latex(M.grade(2))} \\\\\n", "\\text{{Grade 3:}} && \\left_3 &= {latex(M.grade(3))} \\\\\n", "\\text{{Reverse:}} && M^\\dagger &= {latex(M.rev())}\n", "\\end{{align}}\n", "''')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More printing options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A problem in displaying multivectors is that the expression can be very long and does not display nicely on the page. To alleviate this problem one can use the multivector member function `Fmt()`. The default is `Fmt(1)` which displays the multivector on one line, `Fmt(2)` displayes the multivector one grade per line, and `Fmt(3)` displayes the mulitvector one base or basis blade per line. Some examples are: " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle M + M^{x} \\boldsymbol{e}_{x} + M^{y} \\boldsymbol{e}_{y} + M^{z} \\boldsymbol{e}_{z} + M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} + M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}$" ], "text/plain": [ "M + M__x*e_x + M__y*e_y + M__z*e_z + M__xy*e_x^e_y + M__xz*e_x^e_z + M__yz*e_y^e_z + M__xyz*e_x^e_y^e_z" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.Fmt(1)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\begin{aligned}[t] & M \\\\ & + M^{x} \\boldsymbol{e}_{x} + M^{y} \\boldsymbol{e}_{y} + M^{z} \\boldsymbol{e}_{z} \\\\ & + M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\ & + M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{aligned} $" ], "text/plain": [ " M\n", " + M__x*e_x + M__y*e_y + M__z*e_z\n", " + M__xy*e_x^e_y + M__xz*e_x^e_z + M__yz*e_y^e_z\n", " + M__xyz*e_x^e_y^e_z" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.Fmt(2)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\begin{aligned}[t] & M \\\\ & + M^{x} \\boldsymbol{e}_{x} \\\\ & + M^{y} \\boldsymbol{e}_{y} \\\\ & + M^{z} \\boldsymbol{e}_{z} \\\\ & + M^{xy} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\ & + M^{xz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\ & + M^{yz} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\\\ & + M^{xyz} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{aligned} $" ], "text/plain": [ " M\n", " + M__x*e_x\n", " + M__y*e_y\n", " + M__z*e_z\n", " + M__xy*e_x^e_y\n", " + M__xz*e_x^e_z\n", " + M__yz*e_y^e_z\n", " + M__xyz*e_x^e_y^e_z" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.Fmt(3)" ] } ], "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.8.0" } }, "nbformat": 4, "nbformat_minor": 4 }