Skip to content

Commit

Permalink
Merge de8d02e into c1d13d4
Browse files Browse the repository at this point in the history
  • Loading branch information
Ed Barnard committed Mar 3, 2018
2 parents c1d13d4 + de8d02e commit 91becfa
Show file tree
Hide file tree
Showing 19 changed files with 282 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,5 +1,6 @@
Changes since last release
--------------------------
* Added `time_limit` option
* Added CUTEst interface
* Fixed bug in upper triangular `P` extraction. Now the solver can accept both complete `P` matrix or just the upper triangular part.
* Fixed [#33](https://github.com/oxfordcontrol/osqp/issues/33)
Expand Down
3 changes: 2 additions & 1 deletion docs/interfaces/solver_settings.rst
Expand Up @@ -52,7 +52,8 @@ The solver settings are displayed in the following table. The settings marked wi
+--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+
| :code:`adaptive_rho_fraction` | Adaptive rho interval as fraction of setup time (auto mode) | 0 < :code:`adaptive_rho_fraction` | 0.4 |
+--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+

| :code:`time_limit` * | Run time limit in seconds | 0 (disabled) or 0 <= :code:`time_limit` | 0 |
+--------------------------------+-------------------------------------------------------------+--------------------------------------------------------------+-----------------+

The boolean values :code:`True/False` are defined as :code:`1/0` in the C/C++ interfaces.

Expand Down
44 changes: 23 additions & 21 deletions docs/interfaces/status_values.rst
Expand Up @@ -6,24 +6,26 @@ Status values
These are the exit statuses, their respective constants and values returned by the solver as defined in `constants.h <https://github.com/oxfordcontrol/osqp/blob/master/include/constants.h>`_.
The *inaccurate* statuses define when the optimality, primal infeasibility or dual infeasibility conditions are satisfied with tolerances 10 times larger than the ones set.

+---------------------------------+------------------------------------+----------+
| Status | Constant | Value |
+=================================+====================================+==========+
| solved | OSQP_SOLVED | 1 |
+---------------------------------+------------------------------------+----------+
| solved inaccurate | OSQP_SOLVED_INACCURATE | 2 |
+---------------------------------+------------------------------------+----------+
| maximum iterations reached | OSQP_MAX_ITER_REACHED | -2 |
+---------------------------------+------------------------------------+----------+
| primal infeasible | OSQP_PRIMAL_INFEASIBLE | -3 |
+---------------------------------+------------------------------------+----------+
| primal infeasible inaccurate | OSQP_PRIMAL_INFEASIBLE_INACCURATE | 3 |
+---------------------------------+------------------------------------+----------+
| dual infeasible | OSQP_DUAL_INFEASIBLE | -4 |
+---------------------------------+------------------------------------+----------+
| dual infeasible inaccurate | OSQP_DUAL_INFEASIBLE_INACCURATE | 4 |
+---------------------------------+------------------------------------+----------+
| interrupted by user | OSQP_SIGINT | -5 |
+---------------------------------+------------------------------------+----------+
| unsolved | OSQP_UNSOLVED | -10 |
+---------------------------------+------------------------------------+----------+
+------------------------------+-----------------------------------+-------+
| Status | Constant | Value |
+==============================+===================================+=======+
| solved | OSQP_SOLVED | 1 |
+------------------------------+-----------------------------------+-------+
| solved inaccurate | OSQP_SOLVED_INACCURATE | 2 |
+------------------------------+-----------------------------------+-------+
| maximum iterations reached | OSQP_MAX_ITER_REACHED | -2 |
+------------------------------+-----------------------------------+-------+
| primal infeasible | OSQP_PRIMAL_INFEASIBLE | -3 |
+------------------------------+-----------------------------------+-------+
| primal infeasible inaccurate | OSQP_PRIMAL_INFEASIBLE_INACCURATE | 3 |
+------------------------------+-----------------------------------+-------+
| dual infeasible | OSQP_DUAL_INFEASIBLE | -4 |
+------------------------------+-----------------------------------+-------+
| dual infeasible inaccurate | OSQP_DUAL_INFEASIBLE_INACCURATE | 4 |
+------------------------------+-----------------------------------+-------+
| interrupted by user | OSQP_SIGINT | -5 |
+------------------------------+-----------------------------------+-------+
| run time limit reached | OSQP_TIME_LIMIT_REACHED | -6 |
+------------------------------+-----------------------------------+-------+
| unsolved | OSQP_UNSOLVED | -10 |
+------------------------------+-----------------------------------+-------+
8 changes: 7 additions & 1 deletion include/constants.h
Expand Up @@ -15,7 +15,6 @@ extern "C" {
/******************
* Solver Status *
******************/
// TODO: Add other statuses
#define OSQP_DUAL_INFEASIBLE_INACCURATE (4)
#define OSQP_PRIMAL_INFEASIBLE_INACCURATE (3)
#define OSQP_SOLVED_INACCURATE (2)
Expand All @@ -24,6 +23,9 @@ extern "C" {
#define OSQP_PRIMAL_INFEASIBLE (-3) /* primal infeasible */
#define OSQP_DUAL_INFEASIBLE (-4) /* dual infeasible */
#define OSQP_SIGINT (-5) /* interrupted by user */
#ifdef PROFILING
#define OSQP_TIME_LIMIT_REACHED (-6)
#endif
#define OSQP_UNSOLVED (-10) /* Unsolved. Only setup function has been called */


Expand Down Expand Up @@ -79,6 +81,10 @@ static const char *LINSYS_SOLVER_NAME[] = {
#define ADAPTIVE_RHO_TOLERANCE (5) ///< Tolerance for adopting new rho. Number of times the new rho is larger or smaller than the current one
#endif

#ifdef PROFILING
#define TIME_LIMIT (0) ///< Disable time limit as default
#endif

/* Printing */
#define PRINT_INTERVAL 200

Expand Down
10 changes: 10 additions & 0 deletions include/osqp.h
Expand Up @@ -372,6 +372,16 @@ c_int osqp_update_verbose(OSQPWorkspace * work, c_int verbose_new);

#endif // #ifndef EMBEDDED

#ifdef PROFILING
/**
* Update time_limit setting
* @param work Workspace
* @param time_limit_new New time_limit setting
* @return Exitflag
*/
c_int osqp_update_time_limit(OSQPWorkspace * work, c_float time_limit_new);
#endif

/** @} */


Expand Down
4 changes: 4 additions & 0 deletions include/types.h
Expand Up @@ -167,6 +167,10 @@ typedef struct {
c_int check_termination; ///< integer, check termination interval. If 0, termination checking is disabled
c_int warm_start; ///< boolean, warm start

#ifdef PROFILING
c_float time_limit; ///< maximum seconds allowed to solve the problem
#endif

} OSQPSettings;


Expand Down
6 changes: 5 additions & 1 deletion interfaces/matlab/codegen/render_workspace.m
Expand Up @@ -90,7 +90,11 @@ function write_settings( f, settings, embedded_flag )

fprintf(f, '%d, ', settings.scaled_termination);
fprintf(f, '%d, ', settings.check_termination);
fprintf(f, '%d', settings.warm_start);
fprintf(f, '%d,', settings.warm_start);

fprintf(f, '\n#ifdef PROFILING\n');
fprintf(f, '(c_float)%.20f, ', settings.time_limit);
fprintf(f, '\n#endif // PROFILING\n');

fprintf(f, '};\n\n');

Expand Down
12 changes: 11 additions & 1 deletion interfaces/matlab/osqp_mex.cpp
Expand Up @@ -54,7 +54,8 @@ const char* OSQP_SETTINGS_FIELDS[] = {"rho", //c_float
"verbose", //c_int
"scaled_termination", //c_int
"check_termination", //c_int
"warm_start"}; //c_int
"warm_start", //c_int
"time_limit"}; //c_float

const char* CSC_FIELDS[] = {"nzmax", //c_int
"m", //c_int
Expand Down Expand Up @@ -665,6 +666,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
plhs[0] = mxCreateDoubleScalar(OSQP_MAX_ITER_REACHED);
return;
}

if (!strcmp("OSQP_TIME_LIMIT_REACHED", constant)){
plhs[0] = mxCreateDoubleScalar(OSQP_TIME_LIMIT_REACHED);
return;
}

// Linear system solvers
if (!strcmp("SUITESPARSE_LDL_SOLVER", constant)){
Expand Down Expand Up @@ -806,6 +812,7 @@ mxArray* copySettingsToMxStruct(OSQPSettings* settings){
mxSetField(mxPtr, 0, "scaled_termination", mxCreateDoubleScalar(settings->scaled_termination));
mxSetField(mxPtr, 0, "check_termination", mxCreateDoubleScalar(settings->check_termination));
mxSetField(mxPtr, 0, "warm_start", mxCreateDoubleScalar(settings->warm_start));
mxSetField(mxPtr, 0, "time_limit", mxCreateDoubleScalar(settings->time_limit));

return mxPtr;
}
Expand Down Expand Up @@ -1070,6 +1077,7 @@ void copyMxStructToSettings(const mxArray* mxPtr, OSQPSettings* settings){
settings->scaled_termination = (c_int)mxGetScalar(mxGetField(mxPtr, 0, "scaled_termination"));
settings->check_termination = (c_int)mxGetScalar(mxGetField(mxPtr, 0, "check_termination"));
settings->warm_start = (c_int)mxGetScalar(mxGetField(mxPtr, 0, "warm_start"));
settings->time_limit = (c_float)mxGetScalar(mxGetField(mxPtr, 0, "time_limit"));

}

Expand Down Expand Up @@ -1106,6 +1114,8 @@ void copyUpdatedSettingsToWork(const mxArray* mxPtr ,OsqpData* osqpData){
(c_int)mxGetScalar(mxGetField(mxPtr, 0, "check_termination")));
osqp_update_warm_start(osqpData->work,
(c_int)mxGetScalar(mxGetField(mxPtr, 0, "warm_start")));
osqp_update_time_limit(osqpData->work,
(c_float)mxGetScalar(mxGetField(mxPtr, 0, "time_limit")));


// Check for settings that need special update
Expand Down
21 changes: 18 additions & 3 deletions interfaces/matlab/unittests/basic_tests.m
Expand Up @@ -168,9 +168,24 @@ function test_update_rho(testCase)
testCase.verifyEqual(res_default.info.iter, ...
res_updated_rho.info.iter)




end

function test_update_time_limit(testCase)
testCase.verifyEqual(testCase.options.time_limit, 0)

results = testCase.solver.solve();
testCase.verifyEqual(results.info.status_val, ...
testCase.solver.constant('OSQP_SOLVED'))

% Ensure the solver will time out
testCase.solver.update_settings(...
'time_limit', 1e-6,...
'max_iter', 2e9,...
'check_termination', 0);

results = testCase.solver.solve();
testCase.verifyEqual(results.info.status_val, ...
testCase.solver.constant('OSQP_TIME_LIMIT_REACHED'))
end

end
Expand Down
42 changes: 35 additions & 7 deletions interfaces/python/extension/include/osqpobjectpy.h
Expand Up @@ -218,26 +218,26 @@ static PyObject * OSQP_setup(OSQP *self, PyObject *args, PyObject *kwargs) {
"Ax", "Ai", "Ap", "l", "u", // Constraints
"scaling",
"adaptive_rho", "adaptive_rho_interval",
"adaptive_rho_tolerance", "adaptive_rho_fraction",
"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", NULL}; // Settings
"check_termination", "warm_start", "time_limit", NULL}; // Settings


#ifdef DLONG

#ifdef DFLOAT
static char * argparse_string = "(LL)O!O!O!O!O!O!O!O!O!|LLLffffLffffffLLLLLLL";
static char * argparse_string = "(LL)O!O!O!O!O!O!O!O!O!|LLLffffLffffffLLLLLLLf";
#else
static char * argparse_string = "(LL)O!O!O!O!O!O!O!O!O!|LLLddddLddddddLLLLLLL";
static char * argparse_string = "(LL)O!O!O!O!O!O!O!O!O!|LLLddddLddddddLLLLLLLd";
#endif

#else

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

#endif
Expand Down Expand Up @@ -280,7 +280,8 @@ static PyObject * OSQP_setup(OSQP *self, PyObject *args, PyObject *kwargs) {
&settings->verbose,
&settings->scaled_termination,
&settings->check_termination,
&settings->warm_start)) {
&settings->warm_start,
&settings->time_limit)) {
return NULL;
}

Expand Down Expand Up @@ -379,6 +380,10 @@ static PyObject *OSQP_constant(OSQP *self, PyObject *args) {
return Py_BuildValue("i", OSQP_MAX_ITER_REACHED);
}

if(!strcmp(constant_name, "OSQP_TIME_LIMIT_REACHED")){
return Py_BuildValue("i", OSQP_TIME_LIMIT_REACHED);
}

// Linear system solvers
if(!strcmp(constant_name, "SUITESPARSE_LDL_SOLVER")){
return Py_BuildValue("i", SUITESPARSE_LDL_SOLVER);
Expand Down Expand Up @@ -1112,6 +1117,28 @@ static PyObject *OSQP_update_warm_start(OSQP *self, PyObject *args){

}

static PyObject *OSQP_update_time_limit(OSQP *self, PyObject *args){
c_float time_limit_new;

#ifdef DFLOAT
static char * argparse_string = "f";
#else
static char * argparse_string = "d";
#endif

// Parse arguments
if( !PyArg_ParseTuple(args, argparse_string, &time_limit_new)) {
return NULL;
}

// Perform Update
osqp_update_time_limit(self->workspace, time_limit_new);

// Return None
Py_INCREF(Py_None);
return Py_None;

}

static PyMethodDef OSQP_methods[] = {
{"setup", (PyCFunction)OSQP_setup,METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Setup OSQP problem")},
Expand Down Expand Up @@ -1143,6 +1170,7 @@ static PyMethodDef OSQP_methods[] = {
{"update_scaled_termination", (PyCFunction)OSQP_update_scaled_termination, METH_VARARGS, PyDoc_STR("Update OSQP solver setting scaled_termination")},
{"update_check_termination", (PyCFunction)OSQP_update_check_termination, METH_VARARGS, PyDoc_STR("Update OSQP solver setting check_termination")},
{"update_warm_start", (PyCFunction)OSQP_update_warm_start, METH_VARARGS, PyDoc_STR("Update OSQP solver setting warm_start")},
{"update_time_limit", (PyCFunction)OSQP_update_time_limit, METH_VARARGS, PyDoc_STR("Update OSQP solver setting time_limit")},
{"_get_workspace", (PyCFunction)OSQP_get_workspace, METH_VARARGS, PyDoc_STR("Returns the OSQP workspace struct as a Python dictionary.")},
{NULL, NULL} /* sentinel */
};
Expand Down
5 changes: 3 additions & 2 deletions interfaces/python/extension/include/osqpworkspacepy.h
Expand Up @@ -214,7 +214,7 @@
OSQPSettings *settings = self->workspace->settings;

PyObject *return_dict = Py_BuildValue(
"{s:d,s:d,s:i,s:i,s:i,s:d,s:d,s:i,s:d,s:d,s:d, s:d, s:d, s:i, s:i, s:i, s:i}",
"{s:d,s:d,s:i,s:i,s:i,s:d,s:d,s:i,s:d,s:d,s:d, s:d, s:d, s:i, s:i, s:i, s:i, s:d}",
"rho", (double)settings->rho,
"sigma", (double)settings->sigma,
"scaling", settings->scaling,
Expand All @@ -231,7 +231,8 @@
"linsys_solver", settings->linsys_solver,
"warm_start", settings->warm_start,
"scaled_termination", settings->scaled_termination,
"check_termination", settings->check_termination);
"check_termination", settings->check_termination,
"time_limit", (double)settings->time_limit);
return return_dict;
}

Expand Down
7 changes: 6 additions & 1 deletion interfaces/python/module/codegen/utils.py
Expand Up @@ -102,7 +102,12 @@ def write_settings(f, settings, name, embedded_flag):

f.write("%d, " % settings['scaled_termination'])
f.write("%d, " % settings['check_termination'])
f.write("%d" % settings['warm_start'])
f.write("%d," % settings['warm_start'])


f.write("\n#ifdef PROFILING\n")
f.write("(c_float)%.20f " % settings['time_limit'])
f.write("\n#endif // PROFILING\n")

f.write("};\n\n")

Expand Down
6 changes: 5 additions & 1 deletion interfaces/python/module/interface.py
Expand Up @@ -208,7 +208,7 @@ def update_settings(self, **kwargs):
'alpha', 'delta', 'polish',
'polish_refine_iter',
'verbose', 'scaled_termination',
'check_termination',
'check_termination', 'time_limit',
"""

# get arguments
Expand All @@ -226,6 +226,7 @@ def update_settings(self, **kwargs):
scaled_termination = kwargs.pop('scaled_termination', None)
check_termination = kwargs.pop('check_termination', None)
warm_start = kwargs.pop('warm_start', None)
time_limit = kwargs.pop('time_limit', None)

# update them
if max_iter is not None:
Expand Down Expand Up @@ -270,6 +271,9 @@ def update_settings(self, **kwargs):
if warm_start is not None:
self._model.update_warm_start(warm_start)

if time_limit is not None:
self._model.update_time_limit(time_limit)

if max_iter is None and \
eps_abs is None and \
eps_rel is None and \
Expand Down
2 changes: 1 addition & 1 deletion interfaces/python/tests/codegen/test_codegen.py
Expand Up @@ -14,6 +14,6 @@


m = osqp.OSQP()
m.setup(P, q, A, l, u)
m.setup(P, q, A, l, u, time_limit=2)

m.codegen('code')
13 changes: 13 additions & 0 deletions interfaces/python/tests/unittests/basic_tests.py
Expand Up @@ -125,6 +125,19 @@ def test_update_rho(self):
# Assert same number of iterations
self.assertEqual(res_default.info.iter, res_updated_rho.info.iter)

def test_update_time_limit(self):
res = self.model.solve()
self.assertEqual(res.info.status_val,
self.model.constant('OSQP_SOLVED'))

# Ensure the solver will time out
self.model.update_settings(time_limit=1e-6, max_iter=2000000000,
check_termination=0)

res = self.model.solve()
self.assertEqual(res.info.status_val,
self.model.constant('OSQP_TIME_LIMIT_REACHED'))

def test_upper_triangular_P(self):
res_default = self.model.solve()

Expand Down

0 comments on commit 91becfa

Please sign in to comment.