{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "
\n", "\n", "\n", "\n", "
\n", "\n", "# cypari2: Python bindings for PARI/GP\n", "\n", "### Jeroen Demeyer\n", "\n", "### Universiteit Gent / OpenDreamKit\n", "\n", "(joint work with Vincent Delecroix, Luca De Feo, Vincent Klein, …)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Quick examples" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.64493406684823" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from cypari2 import Pari\n", "pari = Pari()\n", "pari.zeta(2)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 10.3910994007158]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pari.ellinit([-112, 400]).ellanalyticrank()" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "x^4 + (-y^3 + 2232*y^2 - 1069956*y + 36864000)*x^3 + (2232*y^3 + 2587918086*y^2 + 8900222976000*y + 452984832000000)*x^2 + (-1069956*y^3 + 8900222976000*y^2 - 770845966336000000*y + 1855425871872000000000)*x + (y^4 + 36864000*y^3 + 452984832000000*y^2 + 1855425871872000000000*y)" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pari.polmodular(3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Plotting (using SVG images)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "7.8031-7.26630100" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "[0.E-307, 100.000000000000, -7.26626527057968, 7.80311802062338]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pari(\"my(Z=lfuninit(1, [100])); ploth(x=0, 100, lfunhardy(Z,x))\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Python interaction" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[-3, -4, -7, -8, -11, -19, -43, -67, -163]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "discs = set(pari.quaddisc(-1-n) for n in range(200))\n", "sorted([D for D in discs if D.qfbclassno() == 1], reverse=True)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def cube(x):\n", " return x**3\n", "\n", "pari.apply(cube, range(10))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Errors" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "ename": "PariError", "evalue": "impossible inverse in Fp_inv: Mod(2, 6)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mPariError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mpari\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mMod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32mcypari2/gen.pyx\u001b[0m in \u001b[0;36mcypari2.gen.Gen.__div__\u001b[0;34m()\u001b[0m\n", "\u001b[0;32mcypari2/handle_error.pyx\u001b[0m in \u001b[0;36mcypari2.handle_error._pari_err_handle\u001b[0;34m()\u001b[0m\n", "\u001b[0;31mPariError\u001b[0m: impossible inverse in Fp_inv: Mod(2, 6)" ] } ], "source": [ "1 / pari.Mod(4, 6)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Implementation\n", "\n", "`cypari2` is written in Cython, a Python-like language compiling to C (like `gp2c` but for Python)\n", "\n", "PARI objects (`GEN`) are wrapped in a Python object `Gen` (independent of the PARI type):" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "t_REAL\n" ] } ], "source": [ "x = pari.pi()\n", "print(type(x))\n", "print(x.type())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Auto-generation\n", "\n", "Cython code wrapping most GP functions is auto-generated from `pari.desc`. Example:\n", "```\n", "Function: cos\n", "Class: basic\n", "Section: transcendental\n", "C-Name: gcos\n", "Prototype: Gp\n", "Help: cos(x): cosine of x.\n", "Doc: cosine of $x$.\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "This becomes\n", "```cython\n", " def cos(self, x, long precision=0):\n", " r'''\n", " Cosine of :math:`x`.\n", " '''\n", " x = objtogen(x)\n", " sig_on()\n", " cdef GEN _x = (x).g\n", " precision = prec_bits_to_words(precision)\n", " cdef GEN _ret = gcos(_x, precision)\n", " return new_gen(_ret)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Performance\n", "\n", "`cypari2` is very fast:\n", "\n", "- Cython allows direct C calls to the PARI library\n", "- Objects remain on the PARI stack if possible, no needless copying\n", " (several unsafe operations require cloning anyway: resizing PARI stack, getting/setting entry of vector/matrix)\n", "\n", "Only overhead comes from dealing with Python objects and methods" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PARI stack size set to 268435456 bytes, maximum size set to 268435456\n" ] } ], "source": [ "pari.allocatemem(2**28)\n", "def hilberttrace(n):\n", " m = pari.mathilbert(n)\n", " m.trace()" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 5 ms, sys: 13 ms, total: 18 ms\n", "Wall time: 18.7 ms\n" ] } ], "source": [ "%time hilberttrace(1000)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "time = \u001b[0;32m61 ms\u001b[0m.\n" ] } ], "source": [ "%%script gp -q --default timer=1\n", "allocatemem(2^28)\n", "hilberttrace(n) = {my(m=mathilbert(n)); trace(m); return;}\n", "hilberttrace(1000);" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## History (a.k.a. why is it called `cypari2`?)\n", "\n", "- ≤ 2006: Very early versions of SageMath using Pyrex/Cython wrappers of PARI\n", "- 2012: Marc Culler and Nathan Dunfield forked these into a separate Python package `cypari` (to use it with their `SnapPy` topology package)\n", "- 2015: auto-generation of wrappers in SageMath\n", "- 2016: these were made into a separate package `cypari2`\n", "- 2018: `cypari2` version 2 released, keeping `GEN`s on the PARI stack instead of always copying\n", "- `cypari` and `cypari2` are still separate packages because of build/packaging issues and Windows compatibility of `cysignals`" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## TODO\n", "\n", "- better support for functions\n", "- support iterators (problem: PARI/GP iterators are not easily usable from C)\n", "- support attributes like `.clgp` (problem: `omega()` versus `.omega` incompatibility)\n", "- slicing (`M[,1]` for a matrix `M`) " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Try it!\n", "\n", "Assuming Python ≥ 2.7 and PARI/GP version ≥ 2.9.4 (installed in a location where Python will find it):\n", "```\n", "pip install cypari2\n", "```\n", "\n", "...or use SageMath.\n", "\n", "Sources: https://github.com/sagemath/cypari2" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [] } ], "metadata": { "celltoolbar": "Slideshow", "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.15" } }, "nbformat": 4, "nbformat_minor": 2 }