用Cython包装的代码意外地分段

2024-04-24 22:20:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我为donlp2编写了一个简单的cython包装器,这是一个C优化库。该库广泛使用全局变量,并假设调用者编写了具有预定义名称的函数,以便函数可以调用它们。(例如,有一个函数ef和egradf分别计算函数及其梯度)

使用“cdefextern”作为全局变量,使用“cdefpublic”创建C库所需的函数,编写包装器非常简单。我也用过视图.array将双*指针投射到可以传递给python函数的cython数组。这样,我的包装器就可以使用C库来优化纯python中定义的函数和渐变。在

下面是包装器代码:

from libc.string cimport strcpy
from cython cimport view

cdef extern void donlp2()

#global varaibles used by donlp2
#only import those variables that are necessary

cdef extern int n
cdef extern int nlin
cdef extern int nonlin
cdef extern int nstep
cdef extern int iterma

cdef extern int icf
cdef extern int icgf

cdef extern double *x
cdef extern char name[41]

cdef extern double del0
cdef extern double tau
cdef extern double tau0
cdef extern int analyt
cdef extern double epsdif
cdef extern int nreset
cdef extern int silent
cdef extern double *low
cdef extern double *up
cdef extern double optite

#Below are used only if bloc is True
cdef extern double *xtr
cdef extern double *fu
cdef extern double **fugrad
cdef extern int bloc

class DonlpProblem:
    """
    Contains all the inputs, including python functions, to 
    solve a constrained nonlinear problem using donlp2.
    """

    def __init__(self,
                 x0, 
                 ef,
                 egradf,
                 low,
                 up,
                 nonlin,
                 activeConstraintTolerance,
                 name,
                 bloc=False,
                 eval_extern=None,
                 econ=None,
                 maxIter=4000,
                 maxBacktrackIter=20,
                 descentVsFeasibilityWeight=0.1, 
                 analyticDerivatives=True,
                 silent=False,
                 nreset=4):
        self.bloc = bloc
        if self.bloc:
            self.eval_extern = eval_extern
        else:
            self.ef = ef
            self.egradf = egradf
        self.econ = econ
        self.n = x0.size
        assert(nonlin+self.n == low.size)
        assert(nonlin+self.n == up.size)
        self.x0 = x0
        self.low = low
        self.up = up
        self.nonlin = nonlin
        self.maxIter = maxIter
        self.maxBacktrackIter = maxBacktrackIter
        self.name = name
        self.activeConstraintTolerance = activeConstraintTolerance
        self.descentVsFeasibilityWeight = descentVsFeasibilityWeight
        self.silent = silent
        self.analyticDerivatives = analyticDerivatives
        self.nreset = nreset

    def run(self):
        """
        Solve problem using donlp2.
        """
        global globalDonlpProblem
        globalDonlpProblem = self
        donlp2()

    def _user_init_size(self):
        """
        Set global variables related to problem size and maximum number
        of iterations.
        """
        global n, nlin, nonlin, iterma, nstep
        n = self.n
        nlin = 0
        nonlin = self.nonlin
        iterma = self.maxIter
        nstep = self.maxBacktrackIter

    def _user_init(self):
        """
        Initialize various problem data unrelated to sizes. This includes
        the problem name, initial point, tolerances, bound constraints,
        and whether analytic gradients are given.
        """
        global name, x, del0, tau0, tau, analyt, epsdif, nreset
        global silent, low, up, bloc

        strcpy(name, self.name)

        for i, xi in enumerate(self.x0):
            x[i+1] = xi

        for i, lowi in enumerate(self.low):
            low[i+1] = lowi

        for i, upi in enumerate(self.up):
            up[i+1] = upi

        bloc = <int> self.bloc
        del0 = self.activeConstraintTolerance
        tau0 = 0.5e0
        tau  = self.descentVsFeasibilityWeight
        analyt = <int>self.analyticDerivatives
        epsdif = 0.e0
        nreset = self.nreset
        silent = <int>self.silent


cdef public void user_init_size():
    """
    Called by donlp, delegate to problem object.
    """
    globalDonlpProblem._user_init_size()

cdef public void user_init(void):
    """
    Called by donlp, delegate to problem object.
    """
    globalDonlpProblem._user_init() 

cdef public void ef(double *x, double *fx):
    """
    Called by donlp, delegate to problem object.
    """
    global icf
    icf += 1
    cdef int xSize = globalDonlpProblem.n+1
    cdef view.array xarr = <double[:xSize]> x
    fx[0] = globalDonlpProblem.ef(xarr[1:])

cdef public void egradf(double *x, double *gradf):
    """
    Called by donlp, delegate to problem object.
    """
    global icgf
    icgf += 1
    cdef int xSize = globalDonlpProblem.n+1
    cdef view.array xarr = <double[:xSize]> x
    cdef view.array gradArr = <double [:xSize]> gradf
    globalDonlpProblem.egradf(xarr[1:], gradArr[1:])

cdef public void eval_extern(int mode):
    """
    Called by donlp, delegate to problem object.
    """
    global icf, icgf
    global fu, fugrad

    cdef int xSize = globalDonlpProblem.n+1
    cdef view.array xarr = <double[:xSize]> xtr
    if mode == 1:
        icf += 1
        fu[0] = globalDonlpProblem.eval_extern(mode, xarr[1:])
    elif mode == 2:
        icf += 1
        icgf += 1
        tmp1, tmp2 = globalDonlpProblem.eval_extern(mode, xarr[1:])
        fu[0] = tmp1
        for i in range(tmp2.size):
            fugrad[i+1][0] = tmp2[i]

cdef public void econ(int type, int *liste, double *x, 
                      double *con, int *err):
    pass

cdef public void econgrad(int *liste, int shift, 
                          double *x, double **grad):
    pass

cdef public void newx(double *x, double *u, int itstep, 
                      double **accinf, int *cont):
    cont[0] = 1

cdef public void setup(void):
    pass

cdef public void solchk(void):
    pass

包装器代码适用于一些简单的玩具箱,如下所示:

^{pr2}$

我实际上想解决的问题涉及更多的设置,使用numpy和cvxopt的数组操作。当我创建它时,代码会立即分段错误。在gdb中单步执行并使用valgrind只会显示优化库中的一行如下所示:

foo = malloc_wrapper(size);

以valgrind的以下错误终止:

==31631== Process terminating with default action of signal 11 (SIGSEGV)
==31631==  Bad permissions for mapped region at address 0x8BFF930
==31631==    at 0x17984DBC: global_mem_malloc (donlp2.c:8690)
==31631==    by 0x17985FA1: donlp2 (donlp2.c:204)
==31631==    by 0x179504D2: __pyx_pw_5cydon_12DonlpProblem_3run (cydon.c:2215)
==31631==    by 0x4E78BD7: PyObject_Call (abstract.c:2529)
==31631==    by 0x4F1BFA1: PyEval_EvalFrameEx (ceval.c:4239)
==31631==    by 0x4F22C08: PyEval_EvalCodeEx (ceval.c:3253)
==31631==    by 0x4F209B4: PyEval_EvalFrameEx (ceval.c:4117)
==31631==    by 0x4F21E47: PyEval_EvalFrameEx (ceval.c:4107)
==31631==    by 0x4F22C08: PyEval_EvalCodeEx (ceval.c:3253)
==31631==    by 0x4F22C81: PyEval_EvalCode (ceval.c:667)
==31631==    by 0x4F46350: PyRun_FileExFlags (pythonrun.c:1370)
==31631==    by 0x4F465F6: PyRun_SimpleFileExFlags (pythonrun.c:948)

segfault发生在C库完成任何实际工作之前。它只是初始化变量。8690线是

foo = malloc_wrapper(sizeOfMalloc);

204号线就是电话

global_mem_malloc();

在包含的头文件中,foo被定义为double*。注意,malloc\u包装器内的内存分配成功,函数成功返回。写foo失败了。在

有什么建议可以缩小造成这种情况的原因,或者如何解决它?在


Tags: selfsizebyexternpublicglobalintdouble