drake icon indicating copy to clipboard operation
drake copied to clipboard

CLP Fails to Solve Simple QP

Open cohnt opened this issue 7 months ago • 8 comments

What happened?

ClpSolver cannot solve the following very simple QP:

from pydrake.all import ClpSolver, ClarabelSolver, MathematicalProgram

prog = MathematicalProgram()
x = prog.NewContinuousVariables(2)
prog.AddLinearConstraint(x[0] + x[1] == 1)
prog.AddBoundingBoxConstraint(0, 1, x[1])
prog.AddQuadraticErrorCost(1, [-0.1, 0], x)

solver = ClpSolver()
result = solver.Solve(prog)
print(result.is_success())

solver = ClarabelSolver()
result = solver.Solve(prog)
print(result.is_success())

Version

No response

What operating system are you using?

No response

What installation option are you using?

No response

Relevant log output


cohnt avatar May 07 '25 20:05 cohnt

CLP is excluded from ChooseBestSolver for QPs because it tripped a valgrind error (#14933). The example prints a warning:

>>> result = solver.Solve(prog)
WARNING:drake:Currently CLP solver has a memory issue when solving a QP. The user should be aware of this risk.

I am not sure if the memory error is still present (we've upgraded CLP since then), but that could be one explanation.

jwnimmer-tri avatar May 07 '25 21:05 jwnimmer-tri

Good to know. Should we keep the issue open for now?

cohnt avatar May 07 '25 21:05 cohnt

I would say the primary bug is that our documentation says that CLP cannot solve QPs but yet it claims that it can at runtime, which seems like it's a consequence of not following up on #14933 and the TODO in #15541. Until we decide one way or other other whether it's supposed to work, we can't tell whether the expected behavior here should have been "solve it and return true" or "throw an exception that QPs are not supported".

jwnimmer-tri avatar May 07 '25 21:05 jwnimmer-tri

I would suggest that this example is strong evidence that CLP cannot solve QPs. Happy to PR a change to make it not claim it can solve QPs at runtime.

(And if so, would it be a breaking change? Bugfix? Would it need to follow the deprecation schedule?)

cohnt avatar May 07 '25 21:05 cohnt

On the other hand, CLP has a bunch of QP unit tests in solvers/test/clp_solvers_test.cc which are passing.

jwnimmer-tri avatar May 07 '25 22:05 jwnimmer-tri

I'll use #22988 to see whether the valgrind report is still a problem.

Edit: No longer a problem.

jwnimmer-tri avatar May 07 '25 22:05 jwnimmer-tri

Here's a C++ version of the test, that can be added into solvers/test/clp_solvers_test.cc. This really is a very simple and obviously feasible QP, so either CLP can't be trusted to solve QPs, or there's a bug somewhere in the parsing.

GTEST_TEST(ClpSolverTest, QuadraticProgram22985) {
  MathematicalProgram prog;
  auto x = prog.NewContinuousVariables(2);
  prog.AddLinearConstraint(x[0] + x[1] == 1);
  prog.AddBoundingBoxConstraint(0, 1, x[1]);
  prog.AddQuadraticErrorCost(1, Eigen::Vector2d(-0.1, 0), x);

  ClpSolver solver;
  auto result = solver.Solve(prog, {}, {});
  EXPECT_TRUE(result.is_success());
}

cohnt avatar May 07 '25 22:05 cohnt

Assigned to @hongkai-dai for disposition.

sherm1 avatar May 08 '25 16:05 sherm1

I've tried to investigate this a little further. Looks like CLP returns the solution status 10, which may be the secondary status described here?

Regardless, I think we should probably be including a more detailed warning if the user tries to solve a QP with CLP. Right now, we just say

WARNING:drake:Currently CLP solver has a memory issue when solving a QP. The user should be aware of this risk.

(which seems to no longer be the case per #22988), but says nothing about the fact that it may straight up return the wrong answer. I'm happy to make that PR.

(Really, I think we shouldn't allow solving QPs with CLP at all, but it seems the jury is still out on that question.)

cohnt avatar Jul 10 '25 20:07 cohnt

FWIW: #247 claims there is an issue with the presolver when solving QPs in CLP and this program is trivial to pre-solve x0 away. I haven't actually looked that this is the issue, but I wouldn't be surprised if it is.

AlexandreAmice avatar Jul 14 '25 13:07 AlexandreAmice

I tried disabling the presolve by setting dblParam_[ClpPresolveTolerance] to 0 (or even -1) in ClpModel.cpp, but it still failed. So it seems like the presolve might not be the problem.

cohnt avatar Jul 14 '25 20:07 cohnt

Sorry for the late reply. I will write the optimization program in CLP native API, not through Drake, and then reproduce the error so that we can report on CLP upstream.

hongkai-dai avatar Jul 20 '25 15:07 hongkai-dai

I can reproduce the problem using CLP native code without using Drake's code. I filed the issue in CLP upstream https://github.com/coin-or/Clp/issues/316

hongkai-dai avatar Jul 20 '25 21:07 hongkai-dai

BTW, we do have a warning inside ClpSolver when the user attempts to solve a QP with CLP https://github.com/RobotLocomotion/drake/blob/23c696b59ea3739dbfc21e5b9701735781b71165/solvers/clp_solver.cc#L37C1-L41C47

hongkai-dai avatar Jul 20 '25 21:07 hongkai-dai

Thank you for filing the issue upstream!

BTW, we do have a warning inside ClpSolver when the user attempts to solve a QP with CLP

Yes, I added that warning in #23199.

cohnt avatar Jul 20 '25 22:07 cohnt

Yes, I added that warning in https://github.com/RobotLocomotion/drake/pull/23199.

Ah I see, thank you very much for adding the warning, very helpful!

hongkai-dai avatar Jul 21 '25 00:07 hongkai-dai

As suggested by the CLP author https://github.com/coin-or/Clp/issues/316#issuecomment-3096258927, we could switch to the most up-to-date CLP.

hongkai-dai avatar Jul 21 '25 16:07 hongkai-dai