1

I would like to call PARI/GP from Python. I need to use ellisdivisible(E; P; n;{&Q}) function of PARI (see function no 3.15.35 on page 441 in this link:), so I have to pass 2 vectors or arrays (e.g, E = ellinit([0,-1,1,0,0], K);P = [0,0];), how I do that?

To call a PARI function(in C) of single argument/variable from Python (given by Thomas Baruchel), we have something like below -

import ctypes

# load the library
pari=ctypes.cdll.LoadLibrary("libpari.so")

# set the right return type of the functions
pari.stoi.restype = ctypes.POINTER(ctypes.c_long)
pari.nextprime.restype = ctypes.POINTER(ctypes.c_long)

# initialize the library 
pari.pari_init(2**19,0)

def nextprime(v):
  g = pari.nextprime(pari.stoi(ctypes.c_long(v))) # nextprime(argument) is a PARI function
  return pari.itos(g)



print( nextprime(456) )

For example I tried -

h=(0,0,0, 4,6)
pari.stoi.restype = ctypes.POINTER(ctypes.c_long*5)
pari.ellinit.restype = ctypes.POINTER(ctypes.c_long)
def ellinit(v):
  g = pari.ellinit(pari.stoi(ctypes.c_long(v)*5))
  return pari.itos(g)


print(ellinit(h))

I got below error -

   File "C:\Users\miron\Desktop\trash5\x\f.py", line 68, in <module>
    print( ellinit(h) )
  File "C:\Users\miron\Desktop\trash5\x\f.py", line 62, in ellinit
    g = pari.ellinit(pari.stoi(ctypes.c_long(v)*5))
TypeError: an integer is required (got type tuple)

How do I pass a tuple/array/vector? Thanks.

Edit: Failed attempt to get ellisdivisible(E; P; n;{&Q}) -

from ctypes import *

pari = cdll.LoadLibrary("C:\\Program Files\\Python37\\Pari64-2-11-3\\libpari.dll")

pari.stoi.restype = POINTER(c_long)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.ellinit.restype = POINTER(POINTER(c_long))
#-------------------------CHANGE 1
pari.ellisdivisible.restype = c_long
Q = pari.stoi(c_long(0))
#-------------------------
(t_VEC, t_COL, t_MAT) = (17, 18, 19)  # incomplete
precision = c_long(38)
pari.pari_init(2 ** 19, 0)
def t_vec(numbers):
    l = len(numbers) + 1
    p1 = pari.cgetg(c_long(l), c_long(t_VEC))
    for i in range(1, l):
        p1[i] = pari.stoi(c_long(numbers[i - 1]))
    return p1
def main():
    h = (0, 0, 0, 0, 1)
    P=(0,0)
    res = pari.ellinit(t_vec(h), pari.stoi(c_long(1)), precision)
#---------------CHANGE 2
   # res = pari.ellinit(t_vec(h), pari.stoi(c_long(1)), precision).disc
    y = pari.ellisdivisible(res, t_vec(P), pari.stoi(c_long(5)), byref(Q))
    print(pari.itos(y))
#---------------
    for i in range(1, 13):
        print(pari.itos(res[i]))

if __name__ == '__main__':
    main()

The error is -

Traceback (most recent call last):
  File "C:\Users\miron\Desktop\trash5\x\ex - Copy (2).py", line 34, in <module>
    main()
  File "C:\Users\miron\Desktop\trash5\x\ex - Copy (2).py", line 28, in main
    print(pari.itos(y))
OSError: exception: access violation reading 0x0000000000000009
  • Not a full answer, but it looks like one issue in your code comes from the conversion between Python list/tuple to `ctypes` arrays. Thus, it is probably more a `ctypes` question than a `pari-gp` one. See https://stackoverflow.com/a/4145859/2560053 and tell if it helps improving a little your code. – Thomas Baruchel Mar 21 '20 at 17:35
  • @ThomasBaruchel No :( ... what kind of change should I make instead of `ctypes.c_long*5` in `ctypes.POINTER(ctypes.c_long*5)`? – Consider Non-Trivial Cases Mar 21 '20 at 17:45
  • The `v` in `ctypes.c_long(v)` is your tuple, but it's not possible to _convert a tuple to an integer_ like that. What should the result be, in your opinion? This depends on the type `pari.stoi` accepts. If your first code works, that should men that it accepts `long`, not tuples. – ForceBru Mar 21 '20 at 17:48
  • @ForceBru that is the problem, I am reading "User’s Guide to the PARI library (version 2.7.7)" but can't figure it out...... – Consider Non-Trivial Cases Mar 21 '20 at 17:59

1 Answers1

4

Python tuples or C-arrays cannot be used directly because PARI is Using PARI/GP specific vectors where the type/length is encoded in the first element.

In section 4.4.1 Creation of PARI objects it says:

The basic function which creates a PARI object is GEN cgetg(long l, long t) l specifies the number of longwords to be allocated to the object, and t is the type of the object, in symbolic form (see Section 4.5 for the list of these). The precise effect of this function is as follows: it first creates on the PARI stack a chunk of memory of size length longwords, and saves the address of the chunk which it will in the end return. If the stack has been used up, a message to the effect that “the PARI stack overflows” is printed, and an error raised. Otherwise, it sets the type and length of the PARI object. In effect, it fills its first codeword (z[0]).

see https://pari.math.u-bordeaux.fr/pub/pari/manuals/2.7.6/libpari.pdf

In the examples in this document you can see that to create a vector with two elements, it is called with the size l=3 to get a suitable vector. The first element of the actual number vector does not start with index 0, but with index 1 (see section 4.5.15 in this PDF document).

With

git clone http://pari.math.u-bordeaux.fr/git/pari.git   

the source code for PARI can be fetched.

There you can see the different types at the end in src/headers/parigen.h. It is an enum and the type we need is t_VEC. The corresponding integer is 17.

We can therefore now define a small function that converts a tupel into a GP vector like this:

def t_vec(numbers):
    l = len(numbers) + 1
    p1 = pari.cgetg(c_long(l), c_long(t_VEC))
    for i in range(1, l):
        p1[i] = pari.stoi(c_long(numbers[i - 1]))
    return p1

We could then call ellinit so:

h = (0, 0, 0, 4, 6)
res = pari.ellinit(t_vec(h), pari.stoi(c_long(1)), precision)

In order to test it with your [0, 0, 0, 4, 6] parameters, you can call GP from the command line:

? ellinit([0, 0, 0, 4, 6], 1)
%1 = [0, 0, 0, 4, 6, 0, 8, 24, -16, -192, -5184, -19648, 110592/307, Vecsmall([1]), [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, 0]]

A small, self-contained Python program of the example on page 441 of the cited PDF document might look like this:

from ctypes import *

pari = cdll.LoadLibrary("libpari.so")

pari.stoi.restype = POINTER(c_long)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.ellinit.restype = POINTER(POINTER(c_long))
pari.ellisdivisible.restype = c_long
pari.nfinit0.restype = POINTER(c_long)
pari.polcyclo_eval.restype = POINTER(c_long)
pari.fetch_user_var.restype = c_long
pari.pol_x.restype = POINTER(c_long)

(t_VEC, t_COL, t_MAT) = (17, 18, 19)  # incomplete
precision = c_long(38)

pari.pari_init(2 ** 19, 0)


def t_vec(numbers):
    l = len(numbers) + 1
    p1 = pari.cgetg(c_long(l), c_long(t_VEC))
    for i in range(1, l):
        p1[i] = pari.stoi(c_long(numbers[i - 1]))
    return p1


def main():
    t = pari.pol_x(pari.fetch_user_var(bytes("t", "utf8")))
    Q = pari.pol_x(pari.fetch_user_var(bytes("Q", "utf8")))
    K = pari.nfinit0(pari.polcyclo_eval(11, t), c_long(0), precision)
    h = (0, -1, 1, 0, 0)
    res = pari.ellinit(t_vec(h), K, precision)
    P = (0, 0)
    y = pari.ellisdivisible(res, t_vec(P), pari.stoi(c_long(5)), byref(Q))

    pari.pari_printf(bytes("Q: %Ps\n", "utf8"), Q)

    print("ellisdivisible =", y)


if __name__ == '__main__':
    main()

Test

Now we can call the Python program and, and compare with it with the output of the interactive GP program, it actually gives the same result:

Q: [Mod(-t^7 - t^6 - t^5 - t^4 + 1, t^10 + t^9 + t^8 + t^7 + t^6 + t^5 + t^4 + t^3 + t^2 + t + 1), Mod(-t^9 - 2*t^8 - 2*t^7 - 3*t^6 - 3*t^5 - 2*t^4 - 2*t^3 - t^2 - 1, t^10 + t^9 + t^8 + t^7 + t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)]
ellisdivisible = 1
Stephan Schlecht
  • 18,637
  • 1
  • 22
  • 33
  • I can't thank you enough!!! Superb!!! Please feel free to edit the question so it becomes more accessible to people. Thanks again. – Consider Non-Trivial Cases Mar 22 '20 at 18:34
  • I added `pari.ellisdivisible.restype = POINTER(POINTER(c_long));P=(0,0); y=pari.ellisdivisible(res,t_vec(P),5,0,0)` but couldn't get the original function `ellisdivisible(E; P; n;{&Q})`, why is that? could you please look into this? How do I pass `E, P, n, {&Q}` ? – Consider Non-Trivial Cases Mar 22 '20 at 21:37
  • @Andrew Without testing it, I would assume `pari.ellisdivisible.restype = c_long` then `Q = pari.stoi(c_long(0))` and finally `y = pari.ellisdivisible(res, t_vec(P), pari.stoi(c_long(5)), byref(Q))`. See https://pari.math.u-bordeaux.fr/dochtml/html-stable/Elliptic_curves.html#ellisdivisible there the library syntax says that a long is returned and third parameter should be a pari.stoi(c_long(5)). – Stephan Schlecht Mar 22 '20 at 23:01
  • No luck! I have edited the post see the error, i got similar error, in attempting a different way... :( .....BTW plz see this post, since you have experience it would be easier for you to write an answer: https://stackoverflow.com/questions/60805819/sending-a-polynomial-to-pari-gp-from-pyhon-ctypes – Consider Non-Trivial Cases Mar 23 '20 at 01:16
  • 1
    I have updated the answer regarding the output of Q and ellisdivisible. – Stephan Schlecht Mar 23 '20 at 07:27