Cython使用GMP算术 [英] Cython using gmp arithmetic

查看:101
本文介绍了Cython使用GMP算术的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Jupyter笔记本(我使用python 2)并使用gmp算术在cython中实现一个简单的代码,以便处理非常大的整数.我不是gmp/cython专家.我的问题是:如何在函数fib()中打印值 a .

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

以下代码返回{}. 据我所知,它与stdout有关.例如,我尝试了gmp_printf,但没有成功.

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

结果

fib(10)
{}

如果我使用return mpz_get_ui(a)而不是return a 代码可以正常工作,但这不是我真正想要的(获取长整数).

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

编辑. 我在cython中将之前的代码与另一个代码再次进行了比较,但没有使用mpz.

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

最后是相同的代码,但使用的是来自gmpy2的mpz

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    

然后

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

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

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

推荐答案

(答案大多总结了一堆评论)

您遇到的直接问题是Python没有真正的方法来处理C结构.为了解决这个问题,Cython尝试在将结构传递给Python时将其转换为字典(如果可能的话).在这种特定情况下,mpz_t被C(以及Cython)视为不透明",因此您不应该知道其成员.因此,Cython可以有帮助地"将其转换为一个空字典(它知道的所有成员的正确表示形式).

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

对于立即修复,我建议使用 gmpy 库,该库是现有的Python/Cython包装GMP.与重复包装它相比,这可能是一个更好的选择.

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. 您可以创建

  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.

您可以在函数末尾将mpz_t转换回Python整数.我觉得这很有意义,因为最终它们代表了同一件事.下面显示的代码是一个粗略的概述,尚未经过测试(我没有安装gmp):

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

这篇关于Cython使用GMP算术的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆