Cython using gmp arithmetic

夙愿已清 提交于 2021-01-28 09:15:27

问题


I'm trying to implement a simple code in cython using Jupyter notebook (I use python 2) and using gmp arithmetic in order to handle very large integers. I'm not a gmp/cython expert. My question is : how do I print the value a in the function fib().

The following code returns {}. As fas as I can understand it has to do with stdout. For instance I tried gmp_printf and it didn't work.

%%cython --link-args=-lgmp

cdef extern from "gmp.h":
    ctypedef struct mpz_t:
        pass

    cdef void mpz_init(mpz_t)  
    cdef void mpz_init_set_ui(mpz_t, unsigned int)

    cdef void mpz_add(mpz_t, mpz_t, mpz_t)
    cdef void mpz_sub(mpz_t, mpz_t, mpz_t)
    cdef void mpz_add_ui(mpz_t, const mpz_t, unsigned long int)

    cdef void mpz_set(mpz_t, mpz_t)

    cdef void mpz_clear(mpz_t)
    cdef unsigned long int mpz_get_ui(mpz_t)

    cdef void mpz_set_ui(mpz_t, unsigned long int)

    cdef int gmp_printf (const char*, ...)
    cdef size_t mpz_out_str (FILE , int , const mpz_t)

def fib(unsigned long int n):
    cdef mpz_t a,b
    mpz_init(a)
    mpz_init(b)
    mpz_init_set_ui(a,1)
    mpz_init_set_ui(b,1)
    cdef int i
    for i in range(n):        
        mpz_add(a,a,b)
        mpz_sub(b,a,b)
    return a

And the result

fib(10)
{}

If I use return mpz_get_ui(a) instead of return a the code is working fine, but this is not the thing I really want (to get a long integer).

EDIT. I compared the previous code with another one again in cython but not using mpz.

%%cython
def pyfib(unsigned long int n):
    a,b=1,1
    for i in range(n):
        a=a+b
        b=a-b
    return a

and finally the same code but using mpz from gmpy2

%%cython
import gmpy2
from gmpy2 import mpz
def pyfib_with_gmpy2(unsigned long int n):
    cdef int i
    a,b=mpz(1),mpz(1)
    for i in range(n):
        a=a+b
        b=a-b
    return a    

Then

timeit fib(700000)
1 loops, best of 3: 3.19 s per loop

and

timeit pyfib(700000)
1 loops, best of 3: 11 s per loop

and

timeit pyfib_with_gmpy2(700000)
1 loops, best of 3: 3.28 s per loop

回答1:


(Answer mostly summarises a bunch of comments)

The immediate issue you were having was that Python has no real way to handle C structs. To get around this, Cython tries to convert structs to dictionaries when they are passed to Python (if possible). In this particular case, mpz_t is treated as "opaque" by C (and thus Cython) so you aren't supposed to know about its members. Therefore Cython "helpfully" converts it to an empty dictionary (a correct representation of all the members it knows about).

For an immediate fix I suggested using the gmpy library, which is an existing Python/Cython wrapping of GMP. This is probably a better choice than repeating the effort to wrap it.


As a general solution to this sort of problem there are two obvious options.

  1. You could create a cdef wrapper class. The documentation I have linked is for C++, but the idea could be applied to C as well (with new/'del' replaced with 'malloc'/'free'). This is ultimately a Python class (so can be returned from Cython to Python) but contains a C struct, which you can manipulate directly in Cython. The approach is pretty well documented and doesn't need repeating here.

  2. You could convert the mpz_t back to a Python integer at the end of the function. I feel this makes most sense, since ultimately they represent the same thing. The code shown below is a rough outline and hasn't been tested (I don't have gmp installed):

    cdef mpz_to_py_int(mpz_t x):
        # get bytes that describe the integer
        cdef const mp_limb_t* x_data = mpz_limbs_read(x)
        # view as a unsigned char* (i.e. as bytes)
        cdef unsigned char* x_data_bytes = <unsigned char*>x_data
        # cast to a memoryview then pass that to the int classmethod "from_bytes"
        # assuming big endian (python 3.2+ required)
        out = int.from_bytes(<unsigned char[:mpz_size(x):1]>x_data_bytes,'big')
    
        # correct using sign
        if mpz_sign(x) < 0:
           return -out
        else
           return out
    


来源:https://stackoverflow.com/questions/48447427/cython-using-gmp-arithmetic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!