Summary
OsqpSolver::solve() calls solver.initSolver() on an already-initialized OsqpEigen::Solver whenever a cold start is requested (warm-starting disabled). With osqp-eigen ≥ v0.9.0 (the first release compatible with OSQP v1.0.0), initSolver() now aborts in this situation with:
[OsqpEigen::Solver::initSolver] The solver has been already initialized. Please use clearSolver() method to deallocate memory.
Older osqp-eigen (e.g. v0.8.1, which targets OSQP 0.6.x) tolerated the repeated initSolver() call, so the bug was latent until OSQP 1.0.
Environment
wbc: master, built with -DSOLVER_OSQP=ON
osqp-eigen: v0.9.0+ (reproduced on master / v0.11.0)
osqp: 1.0.0
Root cause
In src/solvers/osqp/OsqpSolver.cpp, solve():
if(!allow_warm_start) // when warm-start is disabled ...
configured = false; // ... force the (re-)configure path every cycle
if(!configured){
resize(qp.nq, nc);
configured = true;
solver.initSolver(); // <-- called again on a solver that is still initialized
}
When solve() is called with allow_warm_start = false, configured is reset to false on every cycle, so the init branch runs again on the second and subsequent cycles — but solver was already initialized on the first cycle. Newer osqp-eigen rejects a second initSolver() without a preceding clearSolver().
Proposed fix
Tear the solver down before re-initializing on the cold-start path:
if(!configured){
+ if(solver.isInitialized())
+ solver.clearSolver();
resize(qp.nq, nc);
configured = true;
solver.initSolver();
}
This is a no-op on the very first solve (isInitialized() is false) and never runs on the warm-start path (the if(!configured) branch is skipped), so it is safe for both the OSQP 0.6.x and OSQP 1.0 builds of osqp-eigen. It also makes the "warm-start disabled" case an actual clean cold start rather than relying on the old wrapper silently ignoring the duplicate initSolver().
Note on a related, separate issue
A few lines below, the constraint-count change is handled by re-resize()-ing but not re-initializing the solver:
if(solver.data()->getData()->m != nc)
resize(qp.nq, nc);
If the number of constraints ever changes between calls, the subsequent update* calls operate on a solver whose workspace still has the old dimensions. This is orthogonal to the crash above and may deserve its own fix (e.g. clearSolver() + initSolver() on dimension change).
@dmronga
Summary
OsqpSolver::solve()callssolver.initSolver()on an already-initializedOsqpEigen::Solverwhenever a cold start is requested (warm-starting disabled). Withosqp-eigen ≥ v0.9.0(the first release compatible with OSQPv1.0.0),initSolver()now aborts in this situation with:Older
osqp-eigen(e.g. v0.8.1, which targets OSQP 0.6.x) tolerated the repeatedinitSolver()call, so the bug was latent until OSQP 1.0.Environment
wbc:master, built with-DSOLVER_OSQP=ONosqp-eigen: v0.9.0+ (reproduced on master / v0.11.0)osqp: 1.0.0Root cause
In
src/solvers/osqp/OsqpSolver.cpp,solve():When
solve()is called withallow_warm_start = false,configuredis reset tofalseon every cycle, so the init branch runs again on the second and subsequent cycles — butsolverwas already initialized on the first cycle. Newerosqp-eigenrejects a secondinitSolver()without a precedingclearSolver().Proposed fix
Tear the solver down before re-initializing on the cold-start path:
if(!configured){ + if(solver.isInitialized()) + solver.clearSolver(); resize(qp.nq, nc); configured = true; solver.initSolver(); }This is a no-op on the very first solve (
isInitialized()isfalse) and never runs on the warm-start path (theif(!configured)branch is skipped), so it is safe for both the OSQP 0.6.x and OSQP 1.0 builds ofosqp-eigen. It also makes the "warm-start disabled" case an actual clean cold start rather than relying on the old wrapper silently ignoring the duplicateinitSolver().Note on a related, separate issue
A few lines below, the constraint-count change is handled by re-
resize()-ing but not re-initializing the solver:If the number of constraints ever changes between calls, the subsequent
update*calls operate on a solver whose workspace still has the old dimensions. This is orthogonal to the crash above and may deserve its own fix (e.g.clearSolver()+initSolver()on dimension change).@dmronga