{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SnapPy and SageMath are friends!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* SnapPy always uses part of Sage, namely its interface to PARI, repackaged into the stand-alone module CyPari:\n",
    "\n",
    "    - Smith normal form for homology computations.\n",
    "\n",
    "    - Arbitrary precision arithmetic.\n",
    "\n",
    "    - Available on PyPI.\n",
    "    \n",
    "\n",
    "* When installed into Sage, via:\n",
    "```\n",
    "sage -pip install --no-use-wheel snappy\n",
    "```\n",
    "SnapPy gains additional functionality.  GUI still works:\n",
    "```\n",
    "sage -python -m snappy.app\n",
    "```\n",
    "\n",
    "\n",
    "* Installed on SageMathCloud.\n",
    "   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sage-specific features."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Numeric return types are native Sage types."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sage.rings.real_mpfr.RealNumber"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%gui tk\n",
    "import snappy\n",
    "M = snappy.Manifold('m004')\n",
    "type(M.volume())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 7.48 ms, sys: 1.13 ms, total: 8.61 ms\n",
      "Wall time: 15.2 ms\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "2.02988321281930725004240510854904057188337861506059958403497821"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "MH = M.high_precision()\n",
    "%time MH.volume()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Real Field with 212 bits of precision"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "vol = MH.volume()\n",
    "vol.parent()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0.866025403784438646763723170752936183471402626905190314027903489725966508454400018540573093378624287837813070707703351514984972547499476239405827756047186824264046615951152791033987410050542337461632507656171633451661443325336127334460918985613523565830183930794009524993268689929694733825173753288025*I"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.tetrahedra_shapes('rect', bits_prec=1000)[0]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The quad-double precision kernel allows computing very complicated Dirichlet domains.  \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "15.2039621209048\n",
      "The Dirichlet construction failed.\n"
     ]
    }
   ],
   "source": [
    "B = snappy.Manifold('13a100')\n",
    "print(B.volume())\n",
    "try:\n",
    "    B.dirichlet_domain()\n",
    "except RuntimeError, error:\n",
    "    print(error)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting the link editor.\n",
      "Select Tools->Send to SnapPy to load the link complement.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(None, None)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "BH = B.high_precision()\n",
    "D = BH.dirichlet_domain(); D\n",
    "B.plink(), D.view()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because of local rigidity, the shapes are always algebraic numbers.  We can use LLL to recover their exact expressions, following Goodman, Neumann, et. al."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<SetOfAAN: [0.5 + 0.8660254037844386*I, 0.5 + 0.8660254037844386*I]>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "TF = M.tetrahedra_field_gens()\n",
    "TF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Number Field in z with defining polynomial x^2 - x + 1,\n",
       " <ApproxAN: 0.5 + 0.866025403784*I>,\n",
       " [z, z])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "TF.find_field(100, 10, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finite covers are of central interest in 3-manifold theory, e.g. Agol's recent solution to the Virtual Haken Conjecture. SnapPy has some basic ability here:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 3.66 s, sys: 11.3 ms, total: 3.67 s\n",
      "Wall time: 3.67 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time len(M.covers(9))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, in Sage one can use GAP or Magma to explore *much* bigger covers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 213 ms, sys: 30.2 ms, total: 243 ms\n",
      "Wall time: 284 ms\n"
     ]
    }
   ],
   "source": [
    "from sage.all import gap, magma, PSL\n",
    "gap(1), magma(1) # one-time startup cost\n",
    "%time covers = M.covers(9, method='gap')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 55.1 ms, sys: 11.6 ms, total: 66.7 ms\n",
      "Wall time: 224 ms\n"
     ]
    }
   ],
   "source": [
    "%time covers = M.covers(9, method='magma')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Z/21 + Z,\n",
       " Z + Z + Z,\n",
       " Z + Z + Z,\n",
       " Z + Z + Z,\n",
       " Z/21 + Z,\n",
       " Z/4 + Z/4 + Z,\n",
       " Z/3 + Z + Z + Z,\n",
       " Z/3 + Z + Z + Z,\n",
       " Z/3 + Z + Z + Z,\n",
       " Z/3 + Z + Z + Z,\n",
       " Z/76 + Z/76 + Z]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[C.homology() for C in covers]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Generators:\n",
       "   a,b\n",
       "Relators:\n",
       "   aaabABBAb"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G = M.fundamental_group(); G"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ a^3*b*a^-1*b^-2*a^-1*b ]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G_gap = gap(G)\n",
    "G_gap.RelatorsOfFpGroup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "167.999999999999"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = G_gap.GQuotients(PSL(2,7))[1]\n",
    "C = M.cover(f.Kernel())\n",
    "C.volume()/M.volume()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "576\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Multiplicative Abelian group isomorphic to C2 x C4 x C4 x C4 x C4 x C20 x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z x Z"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G_mag = magma(G)\n",
    "H = G_mag.LowIndexSubgroups(12)[174]\n",
    "N = G_mag.Core(H)\n",
    "print(G_mag.Index(N))\n",
    "H = M.cover(N).homology()\n",
    "from sage.all import AbelianGroup\n",
    "AbelianGroup(H.elementary_divisors())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Verified computation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As pioneered by HIKMOT, SnapPy can use interval arithmetic to *prove* that a given manifold is hyperbolic and provide intervals where the exact shapes must lie.  Uses Sage's complex interval types and the Newton interval method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True,\n",
       " [1.171146365? + 1.625447436?*I,\n",
       "  0.1105132481? + 0.7195638777?*I,\n",
       "  0.5400664982? + 1.0871004030?*I,\n",
       "  1.097382711? + 0.886360266?*I,\n",
       "  0.302481620? + 0.8770938905?*I])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "E = snappy.Manifold('K14n1234')\n",
    "success, shapes = E.verify_hyperbolicity()\n",
    "success, shapes[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.24484363906224e-10"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z0 = shapes[0]\n",
    "z0.diameter()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.53171489530889204000291398071224051868662363632504549344497621627402116334874914750325382397429079390135189615865916625036497160206157552137518606107459293503610749359326584929731245435811280206005757224925183922994477338855957052162232575043174260030219734392466310677413445991369115552977045218972e-295"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "better_shapes = E.verify_hyperbolicity(bits_prec=1000)[1]\n",
    "max(z.diameter() for z in better_shapes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By Mostow rigidity, a (finite-volume) hyperbolic structure is unique.  When the manifold has cusps, there is a canonical ideal cell decomposition associated to the hyperbolic structure.  SnapPy uses this to decide when two hyperbolic manifolds are homeomorphic.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "F = snappy.Manifold('K14n1235')\n",
    "E.is_isometric_to(F)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That was a numerical calculation and not rigorous because SnapPy might have miscalculated the canonical decompositions.  Let's fix that."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(17, 'rLLvLvALQMQQcceiklpnmpmmoqnoqoqiidaihuaddexnckngk_bBrtRs')"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "E.num_tetrahedra(), E.triangulation_isosig()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AvLLLMvLLvzAQMMQQQccdgjhimrswsoxtvuyzrwxuyzwxzgfaagfegjlgcxokkpdbamgpmpmg'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "E.isometry_signature(verified=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ALLzvLzzQvzPQLMQPQcccdkinmjoputqustxwywvvxyzzzqffaaaaofgaamatacwargggqtpr'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "F.isometry_signature(verified=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** The latter two strings encode the *homeomorphism type* of these manifolds."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Link Diagrams: Spherogram\n",
    "\n",
    "Spherogram is a separately installable module, mostly pure-Python, which deals with knot and link diagrams.  Basic data structure is a planar diagram."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<plink.editor.LinkEditor instance at 0x497089680>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import spherogram\n",
    "K = spherogram.random_link(300, 1, consistent_twist_regions=True)\n",
    "K\n",
    "K.view()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<Link: 1 comp; 51 cross>, <Link: 1 comp; 68 cross>)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "K_orig = K.copy()\n",
    "K.simplify('global')\n",
    "K, K_orig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<Link: 1 comp; 51 cross>, <Link: 1 comp; 68 cross>)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "K, K_orig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<plink.editor.LinkEditor instance at 0x497b1b3f8>"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "K.view()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(81.1912781926650, 22.1410630468135)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "vol = K.exterior().volume()\n",
    "vol, vol/3.667"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "48*t^26 - 944*t^25 + 9108*t^24 - 57412*t^23 + 265626*t^22 - 960719*t^21 + 2824548*t^20 - 6929507*t^19 + 14449840*t^18 - 25955924*t^17 + 40558062*t^16 - 55520974*t^15 + 66906792*t^14 - 71177087*t^13 + 66906792*t^12 - 55520974*t^11 + 40558062*t^10 - 25955924*t^9 + 14449840*t^8 - 6929507*t^7 + 2824548*t^6 - 960719*t^5 + 265626*t^4 - 57412*t^3 + 9108*t^2 - 944*t + 48"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "K.alexander_polynomial()  # 'local' algorithm of Bar-Natan"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "L = spherogram.Link('L13n131')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-q^-2 + 2*q^-1 - 3 + 4*q - 6*q^2 + 5*q^3 - 6*q^4 + 4*q^5 - 4*q^6 + 3*q^7 - q^8 + q^9"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L.jones_polynomial()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L.signature()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ 0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
       "[ 1 -1  0  0  0  0  0  0  0  0  0  0  0]\n",
       "[ 0  0  0 -1  0  0  0  0  0  0  0  0  0]\n",
       "[-1  1  0  1 -1  0  0  0  0  0  0  0  0]\n",
       "[ 0 -1  0  0  1 -1  0  0  0  0  0  0  0]\n",
       "[ 0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
       "[ 0  0  0  0  0  0  1 -1  0  0  0  0  0]\n",
       "[ 0  0 -1  0  0  1  0  0  0  0  0  0  0]\n",
       "[ 0  0  0  0  0  0  0  1  0 -1  0  0  0]\n",
       "[ 0  0  0  0  0  0  0  0  0  1 -1  0  0]\n",
       "[ 0  0  0  0  0  0  0  0  0  0  1 -1  0]\n",
       "[ 0  0  0  0  0 -1  0  0  0  0  0  1  0]\n",
       "[ 0  0  0  0  0  0 -1  0  1  0  0  0  0]"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L.seifert_matrix()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "D = L.morse_diagram()  # Uses Sage's interface to GLPK"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(D.is_bridge())\n",
    "B = D.bridge()\n",
    "len(B.crossings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[-1, 2, -3, 4, -3, -2, 1, -2, 1, -2, 3, -4, -3, -3, -3, 2, -3]"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L.braid_word()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Spherogram 1.5 and SageMath 7.2 links and braids are friends"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sage.knots.link.Link"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L_sage = L.sage_link()\n",
    "type(L_sage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L_sage_and_back = spherogram.Link(L_sage)\n",
    "\n",
    "L_sage_and_back.exterior().is_isometric_to(L.exterior())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(s0^-1*s1*s2^-1*s3*s2^-1*(s1^-1*s0)^2*s1^-1*s2*s3^-1*s2^-3*s1*s2^-1,\n",
       " Braid group on 5 strands)"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w = L.braid_word(as_sage_braid=True)\n",
    "w, w.parent()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Link: 2 comp; 17 cross>"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L_braid = spherogram.Link(braid_closure=w)\n",
    "L_braid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Link L13n131: 2 comp; 13 cross>"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L_braid.simplify('global')\n",
    "L"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Other 3-manifold software."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Regina\n",
    "\n",
    "Regina focuses on the purely topological side of things, especially normal surface theory.  Includes some support for higher-dimensional manifolds. Like SnapPy, it has a stand-alone GUI and also a Python interface. \n",
    "\n",
    "See http://unhyperbolic.org/sageRegina/ for how to install it painlessly into Sage.\n",
    "\n",
    "### t3m\n",
    "\n",
    "Light-weight pure Python library for dealing with triangulations of 3-manifolds.  Included with SnapPy as `snappy.snap.t3mlite`. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Possible projects for Sage Days 74"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Make SnapPy (even) easier to install into SageMath\n",
    "\n",
    "   - Sage optional package?\n",
    "\n",
    "   - SageMath binaries for OS X < 10.11? "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Sage's \"attach\" blocks Tkinter \n",
    "\n",
    "In Sage's IPython-based interpreter, the mechanism behind Sage's attach functionality blocks IPython's ability to integrate with Tk (and other GUI's) event loops. (See trac #15152).\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modernize CyPari\n",
    "\n",
    "CyPari is based on Sage circa 2012, and there have been improvements since, with more to come (Demeyer et. al.)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modularization\n",
    "\n",
    "Modularize some parts of the SageMath kernel, for example interval arithmetic, for use in stand-alone SnapPy."
   ]
  }
 ],
 "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.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
