我为donlp2编写了一个简单的cython包装器,这是一个C优化库。该库广泛使用全局变量,并假定调用者已编写具有预定义名称的函数,因此函数可以调用它们。 (例如,有一个函数ef和egradf分别评估函数及其梯度)
使用“cdef extern”为全局变量编写包装器非常简单,使用“cdef public”创建C库期望的函数。我还使用view.array将double *指针转换为可以传递给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
包装器代码适用于一些简单的玩具箱,如下所示:
import cydon
import numpy as np
def main():
def ef(x):
return 100*(x[1]-x[0]**2)**2 + (x[0]-1)**2
def egradf(x, g):
g[0] = 200*(x[0]**2-x[1])*x[0] + 2*(x[0]-1)
g[1] = 200*(x[1] - x[0]**2)
x0 = np.array([15,-15])
n = x0.size
low = -1.0e10 * np.ones(n)
up = 1.0e10 * np.ones(n)
def eval_extern(mode, x):
fx = 100*(x[1]-x[0]**2)**2 + (x[0]-1)**2
if mode == 1:
return fx
elif mode == 2:
gradfx = np.ones(2)
gradfx[0] = 200*(x[0]**2-x[1])*x[0] + 2*(x[0]-1)
gradfx[1] = 200*(x[1] - x[0]**2)
return fx, gradfx
problem = cydon.DonlpProblem(
x0=x0,
ef=None,
egradf=None,
bloc=True,
eval_extern=eval_extern,
activeConstraintTolerance=1.00e-1,
low=low,
up=up,
nonlin=0,
silent=False,
name="blabloc"
)
problem.run()
if __name__ == "__main__":
main()
我实际想要解决的问题涉及更多设置,使用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)
段错误发生在C库完成任何实际工作之前。它只是初始化变量。第8690行是
foo = malloc_wrapper(sizeOfMalloc);
和第204行只是呼叫
global_mem_malloc();
在包含的头文件中,foo被定义为double *。请注意,malloc_wrapper中的内存分配成功并且函数已成功返回。这是写给foo的失败。
有关如何缩小导致此问题或如何解决问题的建议?