Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Derivatives interface #38

Merged
merged 19 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ env:

matrix:
include:
- os: linux
env:
- PYTHON_VERSION=2.7
- os: linux
env:
- PYTHON_VERSION=3.5
Expand All @@ -33,9 +30,6 @@ matrix:
- os: linux
env:
- PYTHON_VERSION=3.7
- os: osx
env:
- PYTHON_VERSION=2.7
- os: osx
env:
- PYTHON_VERSION=3.5
Expand All @@ -45,14 +39,6 @@ matrix:
- os: osx
env:
- PYTHON_VERSION=3.7
allow_failures:
- os: linux
env:
- PYTHON_VERSION=2.7
- os: osx
env:
- PYTHON_VERSION=2.7


install:
- if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
Expand All @@ -66,6 +52,7 @@ install:
- conda create -n testenv python=$PYTHON_VERSION pytest numpy
- source activate testenv
- conda install conda conda-verify conda-build anaconda-client twine
- conda install -c conda-forge numdifftools # TODO: Delete this test dependency
- conda info -a
# Add MKL shared libraries (for MKL pardiso) to the path
- |
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ environment:
PYPI_PASSWORD:
secure: 3gQtEWf4jXJLJKP8oM22oCShTJ3VI+BtRYczfzO3RSQ=
matrix:
- PYTHON_VERSION: 2.7
- PYTHON_VERSION: 3.5
- PYTHON_VERSION: 3.6
- PYTHON_VERSION: 3.7
Expand Down Expand Up @@ -40,6 +39,7 @@ install:
- conda config --set always_yes yes --set changeps1 no --set auto_update_conda no
- conda create -n testenv python=%PYTHON_VERSION% pytest numpy cmake conda conda-verify conda-build anaconda-client twine
- activate testenv
- conda install -c conda-forge numdifftools # TODO: Delete this test dependency
- conda install vs2008_express_vc_python_patch
# Fix for 64-bit Python 2.7 builds, courtesy vs2008_express_vc_python_patch
- call setup_x64
Expand Down
298 changes: 3 additions & 295 deletions extension/include/osqpmodulemethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,297 +5,6 @@
* OSQP methods independently from any object *
***********************************************************************/

static PyObject * OSQP_module_solve(OSQP *self, PyObject *args, PyObject *kwargs) {
c_int n, m; // Problem dimensions
c_int exitflag_setup, exitflag_solve;

// Variables for setup
PyOSQPData *pydata;
OSQPData * data;
OSQPSettings * settings;
OSQPWorkspace * workspace; // Pointer to C workspace structure
PyArrayObject *Px, *Pi, *Pp, *q, *Ax, *Ai, *Ap, *l, *u;


// Variables for solve
// Create status object
PyObject * status;
// Create obj_val object
PyObject * obj_val;
// Create solution objects
PyObject * x, *y, *prim_inf_cert, *dual_inf_cert;
// Define info related variables
static char *argparse_string_info;
PyObject *info_list;
PyObject *info;

// Results
PyObject *results_list;
PyObject *results;

npy_intp nd[1];
npy_intp md[1];

static char *kwlist[] = {"dims", // nvars and ncons
"Px", "Pi", "Pp", "q", // Cost function
"Ax", "Ai", "Ap", "l", "u", // Constraints
"scaling",
"adaptive_rho", "adaptive_rho_interval",
"adaptive_rho_tolerance", "adaptive_rho_fraction",
"rho", "sigma", "max_iter", "eps_abs", "eps_rel",
"eps_prim_inf", "eps_dual_inf", "alpha", "delta",
"linsys_solver", "polish",
"polish_refine_iter", "verbose",
"scaled_termination",
"check_termination", "warm_start",
"time_limit", NULL}; // Settings

#ifdef DLONG

// NB: linsys_solver is enum type which is stored as int (regardless on how c_int is defined).
#ifdef DFLOAT
static char * argparse_string_setup = "(LL)O!O!O!O!O!O!O!O!O!|LLLffffLffffffiLLLLLLf";
#else
static char * argparse_string_setup = "(LL)O!O!O!O!O!O!O!O!O!|LLLddddLddddddiLLLLLLd";
#endif

#else

#ifdef DFLOAT
static char * argparse_string_setup = "(ii)O!O!O!O!O!O!O!O!O!|iiiffffiffffffiiiiiiif";
#else
static char * argparse_string_setup = "(ii)O!O!O!O!O!O!O!O!O!|iiiddddiddddddiiiiiiid";
#endif

#endif

// Initialize settings
settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
osqp_set_default_settings(settings);

if( !PyArg_ParseTupleAndKeywords(args, kwargs, argparse_string_setup, kwlist,
&n, &m,
&PyArray_Type, &Px,
&PyArray_Type, &Pi,
&PyArray_Type, &Pp,
&PyArray_Type, &q,
&PyArray_Type, &Ax,
&PyArray_Type, &Ai,
&PyArray_Type, &Ap,
&PyArray_Type, &l,
&PyArray_Type, &u,
&settings->scaling,
&settings->adaptive_rho,
&settings->adaptive_rho_interval,
&settings->adaptive_rho_tolerance,
&settings->adaptive_rho_fraction,
&settings->rho,
&settings->sigma,
&settings->max_iter,
&settings->eps_abs,
&settings->eps_rel,
&settings->eps_prim_inf,
&settings->eps_dual_inf,
&settings->alpha,
&settings->delta,
&settings->linsys_solver,
&settings->polish,
&settings->polish_refine_iter,
&settings->verbose,
&settings->scaled_termination,
&settings->check_termination,
&settings->warm_start,
&settings->time_limit)) {
return (PyObject *) NULL;
}

// Create Data from parsed vectors
pydata = create_pydata(n, m, Px, Pi, Pp, q, Ax, Ai, Ap, l, u);
data = create_data(pydata);

// Perform setup and solve
// release the GIL
Py_BEGIN_ALLOW_THREADS;
// Create Workspace object
exitflag_setup = osqp_setup(&(workspace), data, settings);
exitflag_solve = osqp_solve(workspace);
// reacquire the GIL
Py_END_ALLOW_THREADS;

// Cleanup data and settings
free_data(data, pydata);
c_free(settings);

// Check successful setup and solve
if (exitflag_setup){ // Workspace allocation error
PyErr_SetString(PyExc_ValueError, "Workspace allocation error!");
return (PyObject *) NULL;
}

if(exitflag_solve){
PyErr_SetString(PyExc_ValueError, "OSQP solve error!");
return (PyObject *) NULL;
}

// Temporary solution
nd[0] = (npy_intp)workspace->data->n; // Dimensions in R^n
md[0] = (npy_intp)workspace->data->m; // Dimensions in R^m

// If problem is not primal or dual infeasible store it
if ((workspace->info->status_val != OSQP_PRIMAL_INFEASIBLE) &&
(workspace->info->status_val != OSQP_PRIMAL_INFEASIBLE_INACCURATE) &&
(workspace->info->status_val != OSQP_DUAL_INFEASIBLE) &&
(workspace->info->status_val != OSQP_DUAL_INFEASIBLE_INACCURATE)){

// Primal and dual solutions
x = (PyObject *)PyArrayFromCArray(workspace->solution->x, nd);
y = (PyObject *)PyArrayFromCArray(workspace->solution->y, md);

// Infeasibility certificates -> None values
prim_inf_cert = PyArray_EMPTY(1, nd, NPY_OBJECT, 0);
dual_inf_cert = PyArray_EMPTY(1, md, NPY_OBJECT, 0);

} else if (workspace->info->status_val == OSQP_PRIMAL_INFEASIBLE ||
workspace->info->status_val == OSQP_PRIMAL_INFEASIBLE_INACCURATE) {
// primal infeasible

// Primal and dual solution arrays -> None values
x = PyArray_EMPTY(1, nd, NPY_OBJECT, 0);
y = PyArray_EMPTY(1, md, NPY_OBJECT, 0);

// Primal infeasibility certificate
prim_inf_cert = (PyObject *)PyArrayFromCArray(workspace->delta_y, md);

// Dual infeasibility certificate -> None values
dual_inf_cert = PyArray_EMPTY(1, nd, NPY_OBJECT, 0);

// Set objective value to infinity
workspace->info->obj_val = NPY_INFINITY;

} else {
// dual infeasible

// Primal and dual solution arrays -> None values
x = PyArray_EMPTY(1, nd, NPY_OBJECT, 0);
y = PyArray_EMPTY(1, md, NPY_OBJECT, 0);

// Primal infeasibility certificate -> None values
prim_inf_cert = PyArray_EMPTY(1, md, NPY_OBJECT, 0);

// Dual infeasibility certificate
dual_inf_cert = (PyObject *)PyArrayFromCArray(workspace->delta_x, nd);

// Set objective value to -infinity
workspace->info->obj_val = -NPY_INFINITY;
}

/* CREATE INFO OBJECT */
// Store status string
status = PyUnicode_FromString(workspace->info->status);

// Store obj_val
if (workspace->info->status_val == OSQP_NON_CVX) { // non convex
obj_val = PyFloat_FromDouble(Py_NAN);
} else {
obj_val = PyFloat_FromDouble(workspace->info->obj_val);
}

#ifdef PROFILING

#ifdef DLONG

#ifdef DFLOAT
argparse_string_info = "LOLLOfffffffLf";
#else
argparse_string_info = "LOLLOdddddddLd";
#endif

#else

#ifdef DFLOAT
argparse_string_info = "iOiiOfffffffif";
#else
argparse_string_info = "iOiiOdddddddid";
#endif

#endif /* DLONG */

info_list = Py_BuildValue(argparse_string_info,
workspace->info->iter,
status,
workspace->info->status_val,
workspace->info->status_polish,
obj_val,
workspace->info->pri_res,
workspace->info->dua_res,
workspace->info->setup_time,
workspace->info->solve_time,
workspace->info->update_time,
workspace->info->polish_time,
workspace->info->run_time,
workspace->info->rho_updates,
workspace->info->rho_estimate
);
#else /* PROFILING */

#ifdef DLONG

#ifdef DFLOAT
argparse_string = "LOLLOffLf";
#else
argparse_string = "LOLLOddLd";
#endif

#else

#ifdef DFLOAT
argparse_string = "iOiiOffif";
#else
argparse_string = "iOiiOddid";
#endif

#endif /* DLONG */

info_list = Py_BuildValue(argparse_string_info,
workspace->info->iter,
status,
workspace->info->status_val,
workspace->info->status_polish,
obj_val,
workspace->info->pri_res,
workspace->info->dua_res,
workspace->info->rho_updates,
workspace->info->rho_estimate,
);
#endif /* PROFILING */

info = PyObject_CallObject((PyObject *) &OSQP_info_Type, info_list);

/* Release the info argument list. */
Py_DECREF(info_list);

/* CREATE RESULTS OBJECT */
results_list = Py_BuildValue("OOOOO", x, y, prim_inf_cert, dual_inf_cert, info);

/* Call the class object. */
results = PyObject_CallObject((PyObject *) &OSQP_results_Type, results_list);

// Delete results list
Py_DECREF(results_list);

// Cleanup workspace
if (osqp_cleanup(workspace)) {
PyErr_SetString(PyExc_ValueError, "Workspace deallocation error!");
return (PyObject *) NULL;
}

// Return results
return results;


}


static PyObject *OSQP_constant(OSQP *self, PyObject *args) {

char * constant_name; // String less than 32 chars
Expand Down Expand Up @@ -380,10 +89,9 @@ static PyObject *OSQP_constant(OSQP *self, PyObject *args) {


static PyMethodDef OSQP_module_methods[] = {
{"solve", (PyCFunction)OSQP_module_solve,METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Setup solve and cleanup OSQP problem. This function releases GIL.")},
{"constant", (PyCFunction)OSQP_constant, METH_VARARGS, PyDoc_STR("Return internal OSQP constant")},
{NULL, NULL} /* sentinel */
{"constant", (PyCFunction)OSQP_constant, METH_VARARGS, PyDoc_STR("Return internal OSQP constant")},
{NULL, NULL} /* sentinel */
};


#endif
Loading