cvxpy_codegen
cvxpy_codegen copied to clipboard
Scalar bug in codegenmodule.c.jinja parameter value copy
Scalar parameters are not handled properly in the bit of code that copies parameter values in:
/* Copy parameter values into the parameter structure. */
{% for p in named_params %}
// memcpy(params.{{ p.name }}, {{ p.name }}->data, {{ p.size[0]*p.size[1] }}*sizeof(double));
memcpy(params.{{ p.name }}, PyArray_DATA({{ p.name }}), {{ p.size[0]*p.size[1] }}*sizeof(double));
{% endfor %}
All parameters are being treated as matrices (as pointers to array), and or scalar parameters this is causing the parameter value itself to be passed in as the first argument to memcpy instead of a pointer.
The issue can be replicated by running the following script, which generates code for a second-order cone program with a single constraint of the form ||Ax + b|| - c*x - d <= 0
import numpy as np
import cvxpy as cvx
from cvxpy_codegen import codegen
m = 3
n = 3
A = cvx.Parameter(m,n, name='A')
b = cvx.Parameter(m, name='b')
c = cvx.Parameter(1,n, name='c')
d = cvx.Parameter(1, name='d')
x = cvx.Variable(n, name='x')
f = np.array( [[0,0,0],
[0,0,0],
[0,0,1]] )
objective = cvx.Minimize( cvx.norm(f.T*x) )
A.value = np.zeros( (m,n) )
b.value = np.zeros( (m,1) )
c.value = np.zeros( (1,n) )
d.value = 0
constraints = []
constraints.append( cvx.norm(A*x + b) - c*x - d <= 0 )
prob = cvx.Problem(objective, constraints)
codegen(prob, 'min_z_example')
Building the generated example executable works fine, however building the Python extension fails with the following error:
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -DPYTHON -DDLONG -DLDL_LONG -DCTRLC=1 -Iecos/include -Iecos/external/amd/include -Iecos/external/ldl/include -Iecos/external/SuiteSparse_config -I/usr/lib/python2.7/dist-packages/numpy/core/include -I/usr/include/python2.7 -c codegenmodule.c -o build/temp.linux-x86_64-2.7/codegenmodule.o
codegenmodule.c: In function ‘solve_wrapper’:
codegenmodule.c:59:10: error: incompatible type for argument 1 of ‘memcpy’
memcpy(params.d, PyArray_DATA(d), 1*sizeof(double));
^
In file included from /usr/include/features.h:367:0,
from /usr/include/limits.h:25,
from /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed/limits.h:168,
from /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed/syslimits.h:7,
from /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed/limits.h:34,
from /usr/include/python2.7/Python.h:19,
from codegenmodule.c:20:
/usr/include/x86_64-linux-gnu/bits/string3.h:50:1: note: expected ‘void * restrict’ but argument is of type ‘double’
__NTH (memcpy (void *__restrict __dest, const void *__restrict __src,
^
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
The following change seems to fix the issue:
/* Copy parameter values into the parameter structure. */
{% for p in named_params %}
{% if p.is_scalar %}
memcpy(¶ms.{{ p.name }}, PyArray_DATA({{ p.name }}), {{ p.size[0]*p.size[1] }}*sizeof(double));
{% else %}
// memcpy(params.{{ p.name }}, {{ p.name }}->data, {{ p.size[0]*p.size[1] }}*sizeof(double));
memcpy(params.{{ p.name }}, PyArray_DATA({{ p.name }}), {{ p.size[0]*p.size[1] }}*sizeof(double));
{% endif %}
{% endfor %}