   # cypari2: Python bindings for PARI/GP¶

### Universiteit Gent / OpenDreamKit¶

(joint work with Vincent Delecroix, Luca De Feo, Vincent Klein, …)

## Quick examples¶

In :
from cypari2 import Pari
pari = Pari()
pari.zeta(2)

Out:
1.64493406684823
In :
pari.ellinit([-112, 400]).ellanalyticrank()

Out:
[3, 10.3910994007158]
In :
pari.polmodular(3)

Out:
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)

## Plotting (using SVG images)¶

In :
pari("my(Z=lfuninit(1, )); ploth(x=0, 100, lfunhardy(Z,x))")

Out:
[0.E-307, 100.000000000000, -7.26626527057968, 7.80311802062338]

## Python interaction¶

In :
discs = set(pari.quaddisc(-1-n) for n in range(200))
sorted([D for D in discs if D.qfbclassno() == 1], reverse=True)

Out:
[-3, -4, -7, -8, -11, -19, -43, -67, -163]
In :
def cube(x):
return x**3

pari.apply(cube, range(10))

Out:
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

## Errors¶

In :
1 / pari.Mod(4, 6)

---------------------------------------------------------------------------
PariError                                 Traceback (most recent call last)
<ipython-input-27-817f380fe1ec> in <module>()
----> 1 1 / pari.Mod(4, 6)

cypari2/gen.pyx in cypari2.gen.Gen.__div__()

cypari2/handle_error.pyx in cypari2.handle_error._pari_err_handle()

PariError: impossible inverse in Fp_inv: Mod(2, 6)

## Implementation¶

cypari2 is written in Cython, a Python-like language compiling to C (like gp2c but for Python)

PARI objects (GEN) are wrapped in a Python object Gen (independent of the PARI type):

In :
x = pari.pi()
print(type(x))
print(x.type())

<type 'cypari2.gen.Gen'>
t_REAL


## Auto-generation¶

Cython code wrapping most GP functions is auto-generated from pari.desc. Example:

Function: cos
Class: basic
Section: transcendental
C-Name: gcos
Prototype: Gp
Help: cos(x): cosine of x.
Doc: cosine of $x$.

This becomes

def cos(self, x, long precision=0):
r'''
Cosine of :math:x.
'''
x = objtogen(x)
sig_on()
cdef GEN _x = (<Gen>x).g
precision = prec_bits_to_words(precision)
cdef GEN _ret = gcos(_x, precision)
return new_gen(_ret)


## Performance¶

cypari2 is very fast:

• Cython allows direct C calls to the PARI library
• Objects remain on the PARI stack if possible, no needless copying (several unsafe operations require cloning anyway: resizing PARI stack, getting/setting entry of vector/matrix)

Only overhead comes from dealing with Python objects and methods

In :
pari.allocatemem(2**28)
def hilberttrace(n):
m = pari.mathilbert(n)
m.trace()

PARI stack size set to 268435456 bytes, maximum size set to 268435456

In :
%time hilberttrace(1000)

CPU times: user 5 ms, sys: 13 ms, total: 18 ms
Wall time: 18.7 ms

In :
%%script gp -q
hilberttrace(n) = {my(m=mathilbert(n)); trace(m); return;}
hilberttrace(1000);

time = 61 ms.


## History (a.k.a. why is it called cypari2?)¶

• ≤ 2006: Very early versions of SageMath using Pyrex/Cython wrappers of PARI
• 2012: Marc Culler and Nathan Dunfield forked these into a separate Python package cypari (to use it with their SnapPy topology package)
• 2015: auto-generation of wrappers in SageMath
• 2016: these were made into a separate package cypari2
• 2018: cypari2 version 2 released, keeping GENs on the PARI stack instead of always copying
• cypari and cypari2 are still separate packages because of build/packaging issues and Windows compatibility of cysignals

## TODO¶

• better support for functions
• support iterators (problem: PARI/GP iterators are not easily usable from C)
• support attributes like .clgp (problem: omega() versus .omega incompatibility)
• slicing (M[,1] for a matrix M)

## Try it!¶

Assuming Python ≥ 2.7 and PARI/GP version ≥ 2.9.4 (installed in a location where Python will find it):

pip install cypari2

...or use SageMath.

In [ ]: