PARI is a mathematical library dedicated to high speed computations in algorithmic number theory. It supports infinite precision integers, arbitrary precision reals, complex numbers, p-adic numbers, polynomials, number fields, vectors, matrices...
It provides a great number of powerful algorithms in the fields of transcendental functions, algebraic number theory (elementary, number fields), elliptic curves (L-series, Neron-Tate heights, global reduction over Q), polynomials and linear algebra (including eigenvalues calculations, LLL and normal basis reduction,...).
You can download sources, executables and documentation for PARI using the URL:
ftp://megrez.ceremab.u-bordeaux.fr/pub/pari/
Python is an interpreted object-oriented language with a very nice looking and powerfull syntax. It can be rather easily extended by adding ``modules'' which are small C functions which usually provide access to independently written libraries, by converting arguments of function calls from python objects to C types and also the other way.
You can learn a lot more about python (and download the sources) at the URL:
http://www.python.org/
(if you are in France, you can also try: ftp://ftp.ibp.fr/pub/python/ which should be much faster. You can also look at my page on computer languages for further information on Python (manuals, reference sheets, comparison to other languages...): http://www.eleves.ens.fr:8080/home/fermigie/lang.html ).
PARI comes with an interpreted language called GP which is not very expressive nor easy to program with. It is clearly lacking the input/output and formatting functions that are one of the strong points of Python.
If one wants to use the full strength of PARI one must program in C (or C++). But since there is no syntaxic support in C for arithmetical operations on complex object, the C program tends to be rather hard to code, debug, read and maintain (as a lot of C programs are).
Garbage collecting in C must be dealt with explicitly, when is is automatic in Python.
Python is safer than C, in that an error leads usually to an exception being raised, rather than a core dumped.
As an interpreted language, python support very fast coding / testing cycles, and python's support for exceptions makes the search for errors much easier than the use of a debugger (assuming that there is no errors in the interface program, parimodule.c).
Since python strong point is certainly not speed, one should use mainly PariPython for experimenting with algorithms and debugging. Once the algorithms are cleanly defined and tested, one could translate them into C for efficiency. These new C functions could then be added to the GP core (at the expense, unfortunately, of a recompilation of the python executable) and be called from Python scripts.
PariPython stands currently as 2 distincts programs: parimodule.c which implements the physical connection between the python interpreter and PARI and its interpreter (GP), and Pari.py (written in python) which is a higher level program, and provides (among other things) the python interpreter with most of the functions in GP. The exact subdivision of the tasks between these two programs may change when this software reaches a stable state.
Get the sources for PariPython from there (http://www.eleves.ens.fr:8080/home/fermigie/PariPython/PariPython.tar.gz) .
Then get the sources of python and PARI (if PARI is already installed on your system, you only need 'libpari.a' and the PARI include files).
Put the file 'parimodule.c' in the 'Modules' directory of the python source tree.
Add the line:
pari parimodule.c -I$(HOME)/include/pari -L$(HOME)/lib -lpari -DULONG_NOT_DEFINED -DLONG_IS_32BIT
at the end of the 'Setup' file. Make sure that the line fits on just one line, and use the right '-D' options. (I have given the options for a Sun Sparc workstation. For other architectures, guess the right options from the Makefile in the PARI source directory.) Also correct the path to '.../include/pari' and '.../lib' according to how your system is installed.
Then 'make' python in the main python directory and move / rename the executable to whatever you find appropriate.
Also put the file 'Pari.py' in the directory where the python library files stand, or hack the environment variable 'PYTHONPATH' to point to where the 'Pari.py' file is.
If you just want to play with PariPython a bit and have a Sparc workstation under SunOS, then you can also download a precompiled executable (with PARI version 1.39.03 and Python 1.3-BETA-1). (But remember you still need the file Pari.py).
The pari module is used by simply using the import command:
import parior
from pari import *It provides an object constructor, GEN(), which construct a PARI GEN object from a python integer, real, list or tuple (long ints are not currently supported). You can also construct a GEN from a string, which is simply evaluated as a GP expression. This is especially useful when dealing with polynomials. Ex:
from pari import GEN
x = GEN(1) # An integer.
# Note: Python long integers are currently not supported.
y = GEN(1.0) # A real number
z = GEN('1.0') # A real number. Note that z != y because of precision loss.
u = GEN('x^3 + 2*x + 1') # A polynomial.
v = GEN([1,2,3,4]) # Creates a vector.
w = GEN((1,2,(3,4))) # A vector, whose 3 component is a vector.
t = GEN(w) # A copy of w.
There is also a special constructor for converting lists of lists
(or vectors of vectors, or lists of vectors) to matrices:
from pari import matrix x = matrix([[1,2],[3,4]])
The type of a GEN object (an integer between 1 and 19) can be accessed by attribute '.type'. Ex:
>>> from Pari import * >>> x=GEN(1) >>> print x.type 1
Once a GEN objects is created, you can add, substract, unary minus, multiply, divide (this is exact division here, so integer/integer leads to a fraction), exponentiate, modulo, divmod, compare them. Comparison is OK for objects which are comparable by PARI (that is numbers). You can also check objects equality using function equal(x,y).
You can also print them or str them (means convert them to a string representation).
When an object is a vector or a matrix, you can subscript it. Changing an object by subscripting it also changes all the references to the same object. It means that if I type:
A = GEN([1,2,3]) B = A A[1] = 2then B[1] is also equal to 2.
Most arithmetical operations are overloaded, which means that if you try to, say, add a GEN object to a python integer, real, string (containing a valid GP expression), the python object is first converted to a corresponding GEN object.
Unfortunately, it doesn't work currently when the python object is a list or tuple (that should be converted to a vector), when the operation is '*' or '+' (this is due to a ``feature'' of the python interpreter).
Use the function 'set_prec(prec)' to change the precision in which the computations are done for real numbers (prec is actually the number of words used to carry the real numbers, and not the number of digit). Note that operations in integers are actually in 'infinite precision'.
Other functions, that are mostly intended to be used by higher level functions (i.e. at the python level), are:
The module 'Pari' implemented by the file 'Pari.py' solves these problems. It provides the class 'gen' which is a wrapper class around the type GEN. That means that gen objects should (hopefully) behave at least as well as GEN objects.
This module gives you access to all the functions known to GP, simply by calling them (e.g. 'x = Pari.sin(1)' or 'e=Pari.initel([1,2,3,4,5])'). Note that arguments of the function are automatically converted using the 'gen()' constructor.
Beware that if you import all the namespace of the Pari module (using 'from Pari import *'), some of these functions may clash with builtin python functions ('sort', for example). So be careful.
Read the source for more informations.
Though GEN (or gen) vectors behave a lots like python lists (or tuples), there are very important differences:
from pari import GEN v = GEN([1,2,3]) # for instance. for x in v.list(): print f(x) # whatever...This is certainly more elegant than:
from pari import GEN v = GEN([1,2,3]) for i in range(1, len(v)+1): x = v[i] print f(x)
Only GEN integers, real or vectors can by initialized directly from there counterparts in python. For other types, one should probably use the conversion from string feature. Example with polynomials:
from pari import GEN
# Creates a polynomial of degree 3.
p = GEN('x^3') + 1*'x' + 4
# Note that due to type coercion rules, this is equivalent, though shorter,
# than:
p = GEN('x^3') + GEN(1)*GEN('x') + GEN(4)
Most of the programming constructs of the GP langage can't be accessed from the python level. This is not annoying for things like if, while, for or function definitions, but it is a lot more annoying for integrals infinite sums or products...
One should be aware that importing functions from GP in the python name-space can lead to confusion (example, there is a GP function 'sort' that would clash with python builtin function).
One should also realize that not all GP functionnalities are currently available from python.
The exponentiation ^ operator is not supported by python (it has the precedence of logical operators, so x^2*y would be interpreted as x^(2*y) and not (as expected) (x^2)*y). One should use pow(x,y) instead.
This software is still in development and in testing. I have written a few algorithms with it but I can't guarantee that everything works.
If you happen to find a bug or want some feature to be added, don't hesitate to write me a mail at the address:
stefane.fermigier@ens.frDo it also if you have written PariPython code that you think could be added to an examples directory.
Copyright Stefane Fermigier, universite Paris 7, 1995.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stefane Fermigier not
be used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
STEFANE FERMIGIER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STEFANE FERMIGIER BE LIABLE FOR ANY SPECIAL,
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Here is an example of a small program in PariPython.
# Laska-Kraus-Connell algorithm. # See J. Cremona, Algorithm for Modular Elliptic curves, p. 47. from Pari import gen, initell, smallinitell, factor, gcd, floor, valuation # I could have imported '*' but that would have been a little mode dangerous. EllipErr = "elliptic curve algorithm error" # Gives the minimal model over Q of the elliptic curve e. def ModelMin(e): # Check input type. if type(e) != type(gen(0)): e = gen(e) if e.type != 17: raise EllipErr, 'e not an elliptic curve in modelmin' if e.lg == 6: E = smallinitell(e) elif e.lg == 14 or e.lg == 20: E = e else: raise EllipErr, 'e not an elliptic curve in modelmin' # Retrieve or compute useful variables. delta = E[12] c4, c6 = E[10], E[11] u = 1 t = 0 g = gcd(c6*c6, delta) p_list = factor(6*g)[1] # Loop. for p in p_list.list(): d = floor(valuation(g, p)/12) u = u*pow(p,d) if p == 2: a = c4/pow(2, 4*d) b = c6/pow(2, 6*d) if a % 2 == 1 and b % 4 == 3: t = t - (b+1)/4 elif a % 16 == 0 and (b % 16 in (0, 8)): t = b/8 else: u = u/2 t = 0 elif p == 3: if valuation(c6, 3) == 6*d + 2: u = u/3 # Compute new coeffs. c4 = c4 / pow(u, 4) c6 = c6 / pow(u, 6) a1 = c4 % 2 a2 = -(a1 + c6) % 3 if a2 == 2: a2 = -1 a3 = (t + a1*a2) % 2 b2 = a1*a1 + 4*a2 b4 = (b2*b2 - c4) / 24 a4 = (b4 - a1*a3) / 2 b6 = (36*b2*b4 - pow(b2, 3) - c6) / 216 a6 = (b6 - a3*a3) / 4 # Format output according to input. if e.lg == 6: return gen([a1, a2, a3, a4, a6]) if e.lg == 14: return smallinitell([a1, a2, a3, a4, a6]) if e.lg == 20: return initell([a1, a2, a3, a4, a6]) # test. print ModelMin([1,2,3,4,5]) print ModelMin(smallinitell([10,2,3,4,5])) print ModelMin(initell([1,20,3,4,5]))