Как использовать Tuple / Array / Vector для вызова PARI / GP с Python (ctypes)? - PullRequest
1 голос
/ 21 марта 2020

Я хотел бы позвонить PARI / GP с Python. Мне нужно использовать ellisdivisible(E; P; n;{&Q}) функцию PARI (см. Функцию № 3.15.35 на стр. 441 в этой ссылке:) , поэтому мне нужно передать 2 вектора или массива (например, E = ellinit([0,-1,1,0,0], K);P = [0,0];) как я это делаю?

Чтобы вызвать функцию PARI (в C) с одним аргументом / переменной из Python (задано Томасом Барухелем) , у нас есть что-то вроде ниже -

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

Например, я попытался -

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

Я получил ошибку ниже -

   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)

Как передать кортеж / массив / вектор? Спасибо.

Редактировать: Неудачная попытка получить 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()

Ошибка -

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

1 Ответ

4 голосов
/ 22 марта 2020

Python кортежи или C -массивы нельзя использовать напрямую, поскольку PARI использует векторы PARI / GP, определяющие c векторов, где тип / длина кодируются в первом элементе.

В разделе 4.4 .1 Creation of PARI objects там написано:

Функция basi c, которая создает объект PARI, - это GEN cgetg (long l, long t) l, который определяет количество длинных слов, которые должны быть выделены объекту. и t - тип объекта в символьной форме c (список приведен в разделе 4.5). Точный эффект этой функции заключается в следующем: она сначала создает в стеке PARI фрагмент памяти длинных слов длины размера и сохраняет адрес фрагмента, который будет возвращен в конце. Если стек израсходован, выводится сообщение о том, что «переполнение стека PARI» и возникает ошибка. В противном случае он устанавливает тип и длину объекта PARI. По сути, оно заполняет свое первое кодовое слово (z [0]).

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

В примерах в этом документе вы можете видеть, что чтобы создать вектор с двумя элементами, он вызывается с размером l = 3 , чтобы получить подходящий вектор. Первый элемент вектора фактических чисел начинается не с индекса 0, а с индекса 1 (см. Раздел 4.5.15 в этом документе PDF).

С

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

исходного кода для PARI можно получить.

Там вы можете увидеть различные типы в конце в src / headers / parigen.h. Это перечисление, и нам нужен тип t_VE C. Соответствующее целое число равно 17.

Теперь мы можем определить небольшую функцию, которая преобразует тупель в вектор GP, как это:

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

Затем мы можем вызвать ellinit так:

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

Чтобы протестировать его с параметрами [0, 0, 0, 4, 6], вы можете вызвать GP из командной строки:

? 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]]

Маленький, сам * Содержащаяся Python программа примера на странице 441 цитируемого документа PDF может выглядеть следующим образом:

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

Теперь мы можем вызвать * Программа 1054 * и, и сравните с ней с выводом интерактивной программы GP, фактически дает тот же результат:

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
...