{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SymPy tutorial\n",
    "\n",
    "**SymPy** is a Python package for performing **symbolic mathematics**\n",
    "which can perform algebra, integrate and differentiate equations, \n",
    "find solutions to differential equations, and *numerically solve\n",
    "messy equations* -- along other uses.\n",
    "\n",
    "CHANGE LOG\n",
    "    \n",
    "    2017-06-12  First revision since 2015-12-26.\n",
    "\n",
    "Let's import sympy and initialize its pretty print functionality \n",
    "which will print equations using LaTeX.\n",
    "Jupyter notebooks uses Mathjax to render equations\n",
    "so we specify that option."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import sympy as sym\n",
    "sym.init_printing(use_latex='mathjax')\n",
    "\n",
    "#  If you were not in a notebook environment,\n",
    "#  but working within a terminal, use:\n",
    "#\n",
    "#  sym.init_printing(use_unicode=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Usage\n",
    "\n",
    "These sections are illustrated with examples drawn from\n",
    "[rlabbe](https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python/blob/master/Appendix-A-Installation.ipynb) from his appendix for Kalman Filters.\n",
    "\n",
    "It is important to distinguish a Python variable\n",
    "from a **declared symbol** in sympy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left [ \\phi, \\quad x\\right ]$$"
      ],
      "text/plain": [
       "[\\phi, x]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phi, x = sym.symbols('\\phi, x')\n",
    "\n",
    "#  x here is a sympy symbol, and we form a list:\n",
    "[ phi, x ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how we used a LaTeX expression for the symbol `phi`.\n",
    "This is not necessary, but if you do the output will render nicely as LaTeX.\n",
    "\n",
    "Also notice how $x$ did not have a numerical value for the list to evaluate.\n",
    "\n",
    "So what is the **derivative** of $\\sqrt{\\phi}$ ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\frac{1}{2 \\sqrt{\\phi}}$$"
      ],
      "text/plain": [
       " 1  \n",
       "────\n",
       "2⋅√φ"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sym.diff('sqrt(phi)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can **factor** equations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left(\\phi - 1\\right) \\left(\\phi^{2} + 1\\right)$$"
      ],
      "text/plain": [
       "           ⎛    2    ⎞\n",
       "(\\phi - 1)⋅⎝\\phi  + 1⎠"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sym.factor( phi**3 - phi**2 + phi - 1 )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and we can **expand** them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\phi^{2} - 3 \\phi - 4$$"
      ],
      "text/plain": [
       "    2             \n",
       "\\phi  - 3⋅\\phi - 4"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "((phi+1)*(phi-4)).expand()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also use strings for equations that use symbols that you have not defined:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$2 t + 2$$"
      ],
      "text/plain": [
       "2⋅t + 2"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = sym.expand('(t+1)*2')\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Symbolic solution\n",
    "\n",
    "Now let's use sympy to compute the **Jacobian** of a matrix. \n",
    "Suppose we have a function,\n",
    "\n",
    "$$h=\\sqrt{(x^2 + z^2)}$$\n",
    "\n",
    "for which we want to find the Jacobian with respect to x, y, and z."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left[\\begin{matrix}\\frac{x}{\\sqrt{x^{2} + z^{2}}} & 0 & \\frac{z}{\\sqrt{x^{2} + z^{2}}}\\end{matrix}\\right]$$"
      ],
      "text/plain": [
       "⎡     x                z      ⎤\n",
       "⎢────────────  0  ────────────⎥\n",
       "⎢   _________        _________⎥\n",
       "⎢  ╱  2    2        ╱  2    2 ⎥\n",
       "⎣╲╱  x  + z       ╲╱  x  + z  ⎦"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x, y, z = sym.symbols('x y z')\n",
    "\n",
    "H = sym.Matrix([sym.sqrt(x**2 + z**2)])\n",
    "\n",
    "state = sym.Matrix([x, y, z])\n",
    "\n",
    "H.jacobian(state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's compute the discrete process noise matrix $\\mathbf{Q}_k$ given the continuous process noise matrix \n",
    "$$\\mathbf{Q} = \\Phi_s \\begin{bmatrix}0&0&0\\\\0&0&0\\\\0&0&1\\end{bmatrix}$$\n",
    "\n",
    "and the equation\n",
    "\n",
    "$$\\mathbf{Q} = \\int_0^{\\Delta t} \\Phi(t)\\mathbf{Q}\\Phi^T(t) dt$$\n",
    "\n",
    "where \n",
    "$$\\Phi(t) = \\begin{bmatrix}1 & \\Delta t & {\\Delta t}^2/2 \\\\ 0 & 1 & \\Delta t\\\\ 0& 0& 1\\end{bmatrix}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left[\\begin{matrix}\\frac{\\Delta{t}^{5}}{20} & \\frac{\\Delta{t}^{4}}{8} & \\frac{\\Delta{t}^{3}}{6}\\\\\\frac{\\Delta{t}^{4}}{8} & \\frac{\\Delta{t}^{3}}{3} & \\frac{\\Delta{t}^{2}}{2}\\\\\\frac{\\Delta{t}^{3}}{6} & \\frac{\\Delta{t}^{2}}{2} & \\Delta{t}\\end{matrix}\\right]$$"
      ],
      "text/plain": [
       "⎡         5           4           3⎤\n",
       "⎢\\Delta{t}   \\Delta{t}   \\Delta{t} ⎥\n",
       "⎢──────────  ──────────  ──────────⎥\n",
       "⎢    20          8           6     ⎥\n",
       "⎢                                  ⎥\n",
       "⎢         4           3           2⎥\n",
       "⎢\\Delta{t}   \\Delta{t}   \\Delta{t} ⎥\n",
       "⎢──────────  ──────────  ──────────⎥\n",
       "⎢    8           3           2     ⎥\n",
       "⎢                                  ⎥\n",
       "⎢         3           2            ⎥\n",
       "⎢\\Delta{t}   \\Delta{t}             ⎥\n",
       "⎢──────────  ──────────  \\Delta{t} ⎥\n",
       "⎣    6           2                 ⎦"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dt = sym.symbols('\\Delta{t}')\n",
    "\n",
    "F_k = sym.Matrix([[1, dt, dt**2/2],\n",
    "                  [0,  1,      dt],\n",
    "                  [0,  0,      1]])\n",
    "\n",
    "Q = sym.Matrix([[0,0,0],\n",
    "                [0,0,0],\n",
    "                [0,0,1]])\n",
    "\n",
    "sym.integrate(F_k*Q*F_k.T,(dt, 0, dt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Numerical solution\n",
    "\n",
    "You can find the *numerical value* of an equation by substituting in a value for a variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$8$$"
      ],
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = sym.symbols('x')\n",
    "\n",
    "w = (x**2) - (3*x) + 4\n",
    "w.subs(x, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Typically we want a numerical solution where the analytic solution is messy,\n",
    "that is, we want a **solver**.\n",
    "This is done by specifying a sympy equation, for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left\\{3, 5\\right\\}$$"
      ],
      "text/plain": [
       "{3, 5}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "LHS = (x**2) - (8*x) + 15\n",
    "RHS = 0\n",
    "#  where both RHS and LHS can be complicated expressions.\n",
    "\n",
    "solved = sym.solveset( sym.Eq(LHS, RHS), x, domain=sym.S.Reals )\n",
    "#  Notice how the domain solution can be specified.\n",
    "\n",
    "solved\n",
    "#  A set of solution(s) is returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Solution set was not empty.\n"
     ]
    }
   ],
   "source": [
    "#  Testing whether any solution(s) were found:\n",
    "if solved != sym.S.EmptySet:\n",
    "    print(\"Solution set was not empty.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sympy.sets.sets.FiniteSet"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#  sympy sets are not like the usual Python sets...\n",
    "type(solved)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "([3, 5], <type 'list'>)\n"
     ]
    }
   ],
   "source": [
    "#  ... but can easily to converted to a Python list:\n",
    "l = list(solved)\n",
    "print( l, type(l) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$$\\left\\{- 2 i, 2 i\\right\\}$$"
      ],
      "text/plain": [
       "{-2⋅ⅈ, 2⋅ⅈ}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "LHS = (x**2)\n",
    "RHS = -4\n",
    "#  where both RHS and LHS can be complicated expressions.\n",
    "\n",
    "solved = sym.solveset( sym.Eq(LHS, RHS), x )\n",
    "#  Leaving out the domain will include the complex domain.\n",
    "\n",
    "solved"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Application to financial economics\n",
    "\n",
    "We used sympy to deduce parameters of Gaussian mixtures\n",
    "in module `lib/ys_gauss_mix.py` and the explanatory notebook\n",
    "is rendered at https://git.io/gmix "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
