coin-or / qpOASES

Open-source C++ implementation of the recently proposed online active set strategy
GNU Lesser General Public License v2.1
365 stars 127 forks source link

Hotstart bug when solving QPs in a looped function #83

Open svigerske opened 4 years ago

svigerske commented 4 years ago

Issue created by migration from Trac.

Original creator: lrjcool

Original creation time: 2018-02-28 10:22:48

Assignee: ferreau

Version: 3.2.1

Hey guys. I am using qpOASES 3.2.1 and I have installed it properly. I have been trying to solve a series of QPs by calling the hotstart in a looped function (for some reason I have to solve the QPs in a looped function instead of the main function, imagine an embedded controller). This is what my code looks like (slightly modified from example1):

#include <qpOASES.hpp>

void solve_qp(int i);

USING_NAMESPACE_QPOASES

int main( )
{
    for (int i = 0; i < 2; i++)
        solve_qp(i);
    return 0;
}

void solve_qp(int i)
{ 
    static QProblem mysolver(2, 1);
    if (i == 0) //init soliver in the first loop
    {
        real_t H[2 * 2] = { 1.0, 0.0, 0.0, 0.5 };
        real_t A[1 * 2] = { 1.0, 1.0 };
        real_t g[2] = { 1.5, 1.0 };
        real_t lb[2] = { 0.5, -2.0 };
        real_t ub[2] = { 5.0, 2.0 };
        real_t lbA[1] = { -1.0 };
        real_t ubA[1] = { 2.0 };
        /* Solve first QP. */
        int_t nWSR = 10;
        mysolver.init(H, g, A, lb, ub, lbA, ubA, nWSR);
        real_t xOpt[2];
        mysolver.getPrimalSolution(xOpt);
    }
    else // use hotstart from the second loop
    {
        real_t g_new[2] = { 1.0, 1.5 };
        real_t lb_new[2] = { 0.0, -1.0 };
        real_t ub_new[2] = { 5.0, -0.5 };
        real_t lbA_new[1] = { -2.0 };
        real_t ubA_new[1] = { 1.0 };
        int_t nWSR = 10;
        mysolver.hotstart(g_new, lb_new, ub_new, lbA_new, ubA_new, nWSR);
        real_t xOpt[2];
        mysolver.getPrimalSolution(xOpt);
    }
}

The key part is that I created a static QProblem object in the loop function, trying to store its information after the first loop is over. But I get the error "Premature homotopy termination because QP is infeasible" for the hotstart in the second loop. Any idea to solve this issue? Is it possible to use hotstart in a looped function? Thanks very much!

svigerske commented 4 years ago

Comment by @apotschka created at 2018-03-06 08:08:26

Hi lrjcool,

I cannot reproduce your report with the current svn/trunk version of qpOASES:

potschka`@`alf:~/svn/qpOASES$ cat examples/bug_83.cpp 
 #include <qpOASES.hpp>

 void solve_qp(int i);

 USING_NAMESPACE_QPOASES

 int main( )
 {
         for (int i = 0; i < 2; i++)
                 solve_qp(i);
         return 0;
 }

 void solve_qp(int i)
 {
         static QProblem mysolver(2, 1);
         if (i == 0) //init soliver in the first loop
         {
                 real_t H[2 * 2] = { 1.0, 0.0, 0.0, 0.5 };
                 real_t A[1 * 2] = { 1.0, 1.0 };
                 real_t g[2] = { 1.5, 1.0 };
                 real_t lb[2] = { 0.5, -2.0 };
                 real_t ub[2] = { 5.0, 2.0 };
                 real_t lbA[1] = { -1.0 };
                 real_t ubA[1] = { 2.0 };
                 /* Solve first QP. */
                 int_t nWSR = 10;
                 mysolver.init(H, g, A, lb, ub, lbA, ubA, nWSR);
                 real_t xOpt[2];
                 mysolver.getPrimalSolution(xOpt);
         }
         else // use hotstart from the second loop
         {
                 real_t g_new[2] = { 1.0, 1.5 };
                 real_t lb_new[2] = { 0.0, -1.0 };
                 real_t ub_new[2] = { 5.0, -0.5 };
                 real_t lbA_new[1] = { -2.0 };
                 real_t ubA_new[1] = { 1.0 };
                 int_t nWSR = 10;
                 mysolver.hotstart(g_new, lb_new, ub_new, lbA_new, ubA_new, nWSR);
                 real_t xOpt[2];
                 mysolver.getPrimalSolution(xOpt);
         }
 }
potschka`@`alf:~/svn/qpOASES$ svn diff
Index: examples/Makefile
===================================================================
--- examples/Makefile   (revision 261)
+++ examples/Makefile   (working copy)
`@``@` -48,6 +48,7 `@``@`
        ${BINDIR}/example4${EXE} \
        ${BINDIR}/example5${EXE} \
        ${BINDIR}/exampleLP${EXE} \
+       ${BINDIR}/bug_83${EXE} \
        ${BINDIR}/qrecipe${EXE} \
        ${BINDIR}/qrecipeSchur${EXE}

potschka`@`alf:~/svn/qpOASES$ bin/bug_83 

####################   qpOASES  --  QP NO.   1   #####################

    Iter   |    StepLength    |       Info       |   nFX   |   nAC    
 ----------+------------------+------------------+---------+--------- 
       0   |   5.833333e-01   |   ADD CON    0   |     1   |     1   
       1   |   1.000000e+00   |    QP SOLVED     |     1   |     1   

####################   qpOASES  --  QP NO.   2   #####################

    Iter   |    StepLength    |       Info       |   nFX   |   nAC    
 ----------+------------------+------------------+---------+--------- 
       0   |   1.666667e-01   |   ADD BND    1   |     2   |     0   
       1   |   1.000000e+00   |    QP SOLVED     |     2   |     0   

This was on Ubuntu 16.04. What's your machine setup?

svigerske commented 4 years ago

Comment by oscarjgv24 created at 2018-07-01 14:39:36

I can confirm that this is happening on my qpOASES version as well.

I have Ubuntu 18.04, and I simply compiled the example1a.cpp program with the "hotstart" lane in a loop like this.

/ Solve second QP. / nWSR = 10; for(int i=0;i<10;i++){ example.hotstart( H_new,g_new,A_new,lb_new,ub_new,lbA_new,ubA_new, nWSR,0 ); }

And it gives me the output,

################### qpOASES -- QP NO. 3 #####################

Iter   |    StepLength    |       Info       |   nFX   |   nAC    

----------+------------------+------------------+---------+--------- 0 | 1.000000e+00 | QP SOLVED | 1 | 1
ERROR: Maximum number of working set recalculations performed ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved ->ERROR: Unable to perform homotopy as previous QP is not solved

Anyone knows what is the problem with this?

Regards, Oscar

svigerske commented 4 years ago

Comment by bowzheng created at 2018-11-08 23:52:18

Hi guys,

I ran into the problem as well when putting QP hoststart in a loop. I can reproduce the errors @oscarjgv24 has mentioned. The code is as follows.

#include "qpOASES.hpp"

void solve_qp(int i);

USING_NAMESPACE_QPOASES

int main( )
{
  for (int i = 0; i < 2; i++)
    solve_qp(i);
  return 0;
}

void solve_qp(int i)
{ 
  static QProblem mysolver(2, 1);
  if (i == 0) //init soliver in the first loop
  {
    real_t H[2 * 2] = { 1.0, 0.0, 0.0, 0.5 };
    real_t A[1 * 2] = { 1.0, 1.0 };
    real_t g[2] = { 1.5, 1.0 };
    real_t lb[2] = { 0.5, -2.0 };
    real_t ub[2] = { 5.0, 2.0 };
    real_t lbA[1] = { -1.0 };
    real_t ubA[1] = { 2.0 };
    /* Solve first QP. */
    int_t nWSR = 10;
    mysolver.init(H, g, A, lb, ub, lbA, ubA, nWSR);
    real_t xOpt[2];
    mysolver.getPrimalSolution(xOpt);
  }
  else // use hotstart from the second loop
  {
    real_t g_new[2] = { 1.0, 1.5 };
    real_t lb_new[2] = { 0.0, -1.0 };
    real_t ub_new[2] = { 5.0, -0.5 };
    real_t lbA_new[1] = { -2.0 };
    real_t ubA_new[1] = { 1.0 };
    int_t nWSR = 10;
    for (int i = 0; i < 5; i++) { // we add loop here
      mysolver.hotstart(g_new, lb_new, ub_new, lbA_new, ubA_new, nWSR);
      real_t xOpt[2];
      mysolver.getPrimalSolution(xOpt);
    }
  }
}

Machine: Ubuntu 16.04 Code Version: 3.2.1 Complier: bazel 0.18.1

The output is as follows.

####################   qpOASES  --  QP NO.   1   #####################

    Iter   |    StepLength    |       Info       |   nFX   |   nAC
 ----------+------------------+------------------+---------+---------
       0   |   5.833333e-01   |   ADD CON    0   |     1   |     1
       1   |   1.000000e+00   |    QP SOLVED     |     1   |     1

####################   qpOASES  --  QP NO.   2   #####################

    Iter   |    StepLength    |       Info       |   nFX   |   nAC
 ----------+------------------+------------------+---------+---------
       0   |   1.250000e-01   |   ADD BND    1   |     2   |     0
       1   |   6.800000e-01   |   REM BND    0   |     1   |     0
       2   |   1.000000e+00   |    QP SOLVED     |     1   |     0

####################   qpOASES  --  QP NO.   3   #####################

    Iter   |    StepLength    |       Info       |   nFX   |   nAC
 ----------+------------------+------------------+---------+---------
       0   |   1.000000e+00   |    QP SOLVED     |     1   |     0
ERROR:  Maximum number of working set recalculations performed
->ERROR:  Unable to perform homotopy as previous QP is not solved
  ->ERROR:  Unable to perform homotopy as previous QP is not solved
    ->ERROR:  Unable to perform homotopy as previous QP is not solved
      ->ERROR:  Unable to perform homotopy as previous QP is not solved

It would be great if we can get helped with this issue.

Best, Bryan

MoreInfoy commented 3 years ago

just define the nWSR in you for-loop not out of it, but i do not know the reason

apotschka commented 3 years ago

The reason is the following: nWSR is currently used as an input and an output variable. This means that the maximum number of allowed working set changes will decrease monotonically and will eventually trigger the error. Just make sure to set nWSR before each call to hotstart.