Ejemplo n.º 1
0
//*******************************************************************
int CSolver:: MipOptimize() {
      int rcStatus;
      char buff[100];
      int bFailed ;

      bFailed = 0;

      SetProblemType('M');   // sets callback data flags

      m_cbData.bMip = true;


      CPXsetmipcallbackfunc(m_env,lpcallback, &m_cbData);

//!   m_status = CPXmipoptimize(m_env,m_lp);
	  m_status = CPXmipopt(m_env, m_lp);
      
      if (m_status != 0) {
          CPXgeterrorstring(m_env, m_status, m_error);
          sprintf(buff, "CPXmipoptimize failed return code = %d", m_status );
	      Message(buff);
	      Message(m_error);
          bFailed = 1;
	      return -1;
      }

      CPXsetmipcallbackfunc(m_env,NULL,NULL);

      m_lpstat = CPXgetstat(m_env,m_lp);

      if( bFailed == 1) {
          sprintf(buff, "CPXgetstat value = %d", m_lpstat);
          Message(buff);
      }

      CreateSolArrays();

      // get solution and place into arrays
//!   CPXgetmobjval(m_env, m_lp, &m_obj);
	  CPXgetobjval(m_env, m_lp, &m_obj);

//!   rcStatus = CPXgetmx(m_env,m_lp, m_x, 0, m_nObjItems-1 );
	  rcStatus = CPXgetx(m_env, m_lp, m_x, 0, m_nObjItems - 1);
      if ( rcStatus ){
           CPXgeterrorstring(m_env, m_status, m_error);
	   Message(m_error);
 	   return -1;
      } 

 //!  rcStatus = CPXgetmslack(m_env, m_lp, m_slack, 0, m_nRhsItems-1 );
	  rcStatus = CPXgetslack(m_env, m_lp, m_slack, 0, m_nRhsItems - 1);
      if ( rcStatus ){
	       CPXgeterrorstring(m_env, m_status, m_error);
	       Message(m_error);
	       return -1;
      }

// save the basis to arrays
//      m_status = CPXgetbase(m_env, m_lp, m_pCstat, m_pRstat);

//     if ( m_status ) {
//	      Message("Getting basis failed!");
//	      return -1;
//     }

      return 0;

}
Ejemplo n.º 2
0
int
main (void)
{
/* Declare pointers for the variables and arrays that will contain
   the data which define the LP problem.  The setproblemdata() routine
   allocates space for the problem data.  */

   char     *probname = NULL;
   int      numcols;
   int      numrows;
   int      objsen;
   double   *obj = NULL;
   double   *rhs = NULL;
   char     *sense = NULL;
   int      *matbeg = NULL;
   int      *matcnt = NULL;
   int      *matind = NULL;
   double   *matval = NULL;
   double   *lb = NULL;
   double   *ub = NULL;
   char     *ctype = NULL;

   /* Declare and allocate space for the variables and arrays where we will
      store the optimization results including the status, objective value,
      variable values, and row slacks. */

   int      solstat;
   double   objval;
   double   x[NUMCOLS];
   double   slack[NUMROWS];


   CPXENVptr     env = NULL;
   CPXLPptr      lp = NULL;
   int           status;
   int           i, j;
   int           cur_numrows, cur_numcols;

   /* Initialize the CPLEX environment */

   env = CPXopenCPLEX (&status);

   /* If an error occurs, the status value indicates the reason for
      failure.  A call to CPXgeterrorstring will produce the text of
      the error message.  Note that CPXopenCPLEX produces no output,
      so the only way to see the cause of the error is to use
      CPXgeterrorstring.  For other CPLEX routines, the errors will
      be seen if the CPXPARAM_ScreenOutput indicator is set to CPX_ON.  */

   if ( env == NULL ) {
      char  errmsg[CPXMESSAGEBUFSIZE];
      fprintf (stderr, "Could not open CPLEX environment.\n");
      CPXgeterrorstring (env, status, errmsg);
      fprintf (stderr, "%s", errmsg);
      goto TERMINATE;
   }

   /* Turn on output to the screen */

   status = CPXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON);
   if ( status ) {
      fprintf (stderr,
               "Failure to turn on screen indicator, error %d.\n", status);
      goto TERMINATE;
   }

   /* Fill in the data for the problem.  */

   status = setproblemdata (&probname, &numcols, &numrows, &objsen, &obj,
                            &rhs, &sense, &matbeg, &matcnt, &matind, &matval,
                            &lb, &ub, &ctype);
   if ( status ) {
      fprintf (stderr, "Failed to build problem data arrays.\n");
      goto TERMINATE;
   }

   /* Create the problem. */

   lp = CPXcreateprob (env, &status, probname);

   /* A returned pointer of NULL may mean that not enough memory
      was available or there was some other problem.  In the case of
      failure, an error message will have been written to the error
      channel from inside CPLEX.  In this example, the setting of
      the parameter CPXPARAM_ScreenOutput causes the error message to
      appear on stdout.  */

   if ( lp == NULL ) {
      fprintf (stderr, "Failed to create LP.\n");
      goto TERMINATE;
   }

   /* Now copy the problem data into the lp */

   status = CPXcopylp (env, lp, numcols, numrows, objsen, obj, rhs,
                       sense, matbeg, matcnt, matind, matval,
                       lb, ub, NULL);

   if ( status ) {
      fprintf (stderr, "Failed to copy problem data.\n");
      goto TERMINATE;
   }

   /* Now copy the ctype array */

   status = CPXcopyctype (env, lp, ctype);
   if ( status ) {
      fprintf (stderr, "Failed to copy ctype\n");
      goto TERMINATE;
   }


   /* Optimize the problem and obtain solution. */

   status = CPXmipopt (env, lp);
   if ( status ) {
      fprintf (stderr, "Failed to optimize MIP.\n");
      goto TERMINATE;
   }

   solstat = CPXgetstat (env, lp);

   /* Write the output to the screen. */

   printf ("\nSolution status = %d\n", solstat);

   status = CPXgetobjval (env, lp, &objval);
   if ( status ) {
      fprintf (stderr,"No MIP objective value available.  Exiting...\n");
      goto TERMINATE;
   }

   printf ("Solution value  = %f\n\n", objval);

   /* The size of the problem should be obtained by asking CPLEX what
      the actual size is, rather than using what was passed to CPXcopylp.
      cur_numrows and cur_numcols store the current number of rows and
      columns, respectively.  */

   cur_numrows = CPXgetnumrows (env, lp);
   cur_numcols = CPXgetnumcols (env, lp);

   status = CPXgetx (env, lp, x, 0, cur_numcols-1);
   if ( status ) {
      fprintf (stderr, "Failed to get optimal integer x.\n");
      goto TERMINATE;
   }

   status = CPXgetslack (env, lp, slack, 0, cur_numrows-1);
   if ( status ) {
      fprintf (stderr, "Failed to get optimal slack values.\n");
      goto TERMINATE;
   }

   for (i = 0; i < cur_numrows; i++) {
      printf ("Row %d:  Slack = %10f\n", i, slack[i]);
   }

   for (j = 0; j < cur_numcols; j++) {
      printf ("Column %d:  Value = %10f\n", j, x[j]);
   }

   /* Finally, write a copy of the problem to a file. */

   status = CPXwriteprob (env, lp, "mipex1.lp", NULL);
   if ( status ) {
      fprintf (stderr, "Failed to write LP to disk.\n");
      goto TERMINATE;
   }


TERMINATE:

   /* Free up the problem as allocated by CPXcreateprob, if necessary */

   if ( lp != NULL ) {
      status = CPXfreeprob (env, &lp);
      if ( status ) {
         fprintf (stderr, "CPXfreeprob failed, error code %d.\n", status);
      }
   }

   /* Free up the CPLEX environment, if necessary */

   if ( env != NULL ) {
      status = CPXcloseCPLEX (&env);

      /* Note that CPXcloseCPLEX produces no output,
         so the only way to see the cause of the error is to use
         CPXgeterrorstring.  For other CPLEX routines, the errors will
         be seen if the CPXPARAM_ScreenOutput indicator is set to CPX_ON. */

      if ( status ) {
         char  errmsg[CPXMESSAGEBUFSIZE];
         fprintf (stderr, "Could not close CPLEX environment.\n");
         CPXgeterrorstring (env, status, errmsg);
         fprintf (stderr, "%s", errmsg);
      }
   }

   /* Free up the problem data arrays, if necessary. */

   free_and_null ((char **) &probname);
   free_and_null ((char **) &obj);
   free_and_null ((char **) &rhs);
   free_and_null ((char **) &sense);
   free_and_null ((char **) &matbeg);
   free_and_null ((char **) &matcnt);
   free_and_null ((char **) &matind);
   free_and_null ((char **) &matval);
   free_and_null ((char **) &lb);
   free_and_null ((char **) &ub);
   free_and_null ((char **) &ctype);

   return (status);

}  /* END main */
Ejemplo n.º 3
0
/*
 * The function returns a true value if the tested KKT conditions are
 * satisfied and false otherwise.
 */
static int
checkkkt (CPXCENVptr env, CPXLPptr lp, int const *cone, double tol)
{
   int cols = CPXgetnumcols (env, lp);
   int rows = CPXgetnumrows (env, lp);
   int qcons = CPXgetnumqconstrs (env, lp);
   double *dslack = NULL, *pi = NULL, *socppi = NULL;
   double *val = NULL, *rhs = NULL;
   int *ind = NULL;
   char *sense = NULL;
   double *x = NULL, *slack = NULL, *qslack = NULL;
   double *sum = NULL;
   qbuf_type qbuf;
   CPXCHANNELptr resc, warnc, errc, logc;
   int ok = 0, skip = 0;
   int status;
   int i, j, q;

   qbuf_init (&qbuf);

   /* Get the channels on which we may report. */
   if ( (status = CPXgetchannels (env, &resc, &warnc, &errc, &logc)) != 0 )
      goto TERMINATE;

   /* Fetch results and problem data that we need to check the KKT
    * conditions.
    */
   CPXmsg (logc, "Fetching results ... ");
   if ( (cols  > 0 && (dslack = malloc (cols *  sizeof (*dslack))) == NULL) ||
        (rows  > 0 && (pi =     malloc (rows *  sizeof (*pi)))     == NULL) ||
        (qcons > 0 && (socppi = malloc (qcons * sizeof (*socppi))) == NULL) ||
        (cols  > 0 && (x =      malloc (cols *  sizeof (*x)))      == NULL) ||
        (rows  > 0 && (sense =  malloc (rows *  sizeof (*sense)))  == NULL ) ||
        (rows  > 0 && (slack =  malloc (rows *  sizeof (*slack)))  == NULL ) ||
        (qcons > 0 && (qslack = malloc (qcons * sizeof (*qslack))) == NULL) ||
        (cols  > 0 && (sum =    malloc (cols *  sizeof (*sum)))    == NULL) ||
        (cols  > 0 && (val =    malloc (cols *  sizeof (*val)))    == NULL) ||
        (cols  > 0 && (ind =    malloc (cols *  sizeof (*ind)))    == NULL) ||
        (rows  > 0 && (rhs =    malloc (rows *  sizeof (*rhs)))    == NULL) )
   {
      CPXmsg (errc, "Out of memory!\n");
      goto TERMINATE;
   }

   /* Fetch problem data. */
   if ( (status = CPXgetsense (env, lp, sense, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXgetrhs (env, lp, rhs, 0, rows - 1)) != 0 )
      goto TERMINATE;

   /* Fetch solution information. */
   if ( (status = CPXgetx (env, lp, x, 0, cols - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXgetpi (env, lp, pi, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = getsocpconstrmultipliers (env, lp, dslack, socppi)) != 0 )
      goto TERMINATE;
   if ( (status = CPXgetslack (env, lp, slack, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXgetqconstrslack (env, lp, qslack, 0, qcons - 1)) != 0 )
      goto TERMINATE;
   CPXmsg (logc, "ok.\n");

   /* Print out the solution data we just fetched. */
   CPXmsg (resc, "x      = [");
   for (j = 0; j < cols; ++j)
      CPXmsg (resc, " %+7.3f", x[j]);
   CPXmsg (resc, " ]\n");
   CPXmsg (resc, "dslack = [");
   for (j = 0; j < cols; ++j)
      CPXmsg (resc, " %+7.3f", dslack[j]);
   CPXmsg (resc, " ]\n");
   CPXmsg (resc, "pi     = [");
   for (i = 0; i < rows; ++i)
      CPXmsg (resc, " %+7.3f", pi[i]);
   CPXmsg (resc, " ]\n");
   CPXmsg (resc, "slack  = [");
   for (i = 0; i < rows; ++i)
      CPXmsg (resc, " %+7.3f", slack[i]);
   CPXmsg (resc, " ]\n");
   CPXmsg (resc, "socppi = [");
   for (q = 0; q < qcons; ++q)
      CPXmsg (resc, " %+7.3f", socppi[q]);
   CPXmsg (resc, " ]\n");
   CPXmsg (resc, "qslack = [");
   for (q = 0; q < qcons; ++q)
      CPXmsg (resc, " %+7.3f", qslack[q]);
   CPXmsg (resc, " ]\n");

   /* Test primal feasibility. */
   CPXmsg (logc, "Testing primal feasibility ... ");
   /* This example illustrates the use of dual vectors returned by CPLEX
    * to verify dual feasibility, so we do not test primal feasibility
    * here. */
   CPXmsg (logc, "ok.\n");

   /* Test dual feasibility.
    * We must have
    * - for all <= constraints the respective pi value is non-negative,
    * - for all >= constraints the respective pi value is non-positive,
    * - since all quadratic constraints are <= constraints the socppi
    *   value must be non-negative for all quadratic constraints,
    * - the dslack value for all non-cone variables must be non-negative.
    * Note that we do not support ranged constraints here.
    */
   CPXmsg (logc, "Testing dual feasibility ... ");
   for (i = 0; i < rows; ++i) {
      switch (sense[i]) {
      case 'L':
         if ( pi[i] < -tol ) {
            CPXmsg (errc, "<= row %d has invalid dual multiplier %f.\n",
                    i, pi[i]);
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( pi[i] > tol ) {
            CPXmsg (errc, ">= row %d has invalid dual multiplier %f.\n",
                    i, pi[i]);
            goto TERMINATE;
         }
         break;
      case 'E':
         /* Nothing to check here. */
         break;
      }
   }
   for (q = 0; q < qcons; ++q) {
      if ( socppi[q] < -tol ) {
         CPXmsg (errc, "Quadratic constraint %d has invalid dual multiplier %f.\n",
                 q, socppi[q]);
         goto TERMINATE;
      }
   }
   for (j = 0; j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE && dslack[j] < -tol ) {
         CPXmsg (errc, "dslack value for column %d is invalid: %f\n", j, dslack[j]);
         goto TERMINATE;
      }
   }
   CPXmsg (logc, "ok.\n");

   /* Test complementary slackness.
    * For each constraint either the constraint must have zero slack or
    * the dual multiplier for the constraint must be 0. Again, we must
    * consider the special case in which a variable is not explicitly
    * contained in a second order cone constraint (conestat[j] == 0).
    */
   CPXmsg (logc, "Testing complementary slackness ... ");
   for (i = 0; i < rows; ++i) {
      if ( fabs (slack[i]) > tol && fabs (pi[i]) > tol ) {
         CPXmsg (errc, "Complementary slackness not satisfied for row %d (%f, %f)\n",
                 i, slack[i], pi[i]);
         goto TERMINATE;
      }
   }
   for (q = 0; q < qcons; ++q) {
      if ( fabs (qslack[q]) > tol && fabs (socppi[q]) > tol ) {
         CPXmsg (errc, "Complementary slackness not satisfied for cone %d (%f, %f).\n",
                 q, qslack[q], socppi[q]);
         goto TERMINATE;
      }
   }
   for (j = 0; j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE ) {
         if ( fabs (x[j]) > tol && fabs (dslack[j]) > tol ) {
            CPXmsg (errc, "Complementary slackness not satisfied for non-cone variable %f (%f, %f).\n",
                    j, x[j], dslack[j]);
            goto TERMINATE;
         }
      }
   }
   CPXmsg (logc, "ok.\n");

   /* Test stationarity.
    * We must have
    *  c - g[i]'(X)*pi[i] = 0
    * where c is the objective function, g[i] is the i-th constraint of the
    * problem, g[i]'(x) is the derivate of g[i] with respect to x and X is the
    * optimal solution.
    * We need to distinguish the following cases:
    * - linear constraints g(x) = ax - b. The derivative of such a
    *   constraint is g'(x) = a.
    * - second order constraints g(x[1],...,x[n]) = -x[1] + |(x[2],...,x[n])|
    *   the derivative of such a constraint is
    *     g'(x) = (-1, x[2]/|(x[2],...,x[n])|, ..., x[n]/|(x[2],...,x[n])|
    *   (here |.| denotes the Euclidean norm).
    * - bound constraints g(x) = -x for variables that are not explicitly
    *   contained in any second order cone constraint. The derivative for
    *   such a constraint is g'(x) = -1.
    * Note that it may happen that the derivative of a second order cone
    * constraint is not defined at the optimal solution X (this happens if
    * X=0). In this case we just skip the stationarity test.
    */
   CPXmsg (logc, "Testing stationarity ... ");
   /* Initialize sum = c. */
   if ( (status = CPXgetobj (env, lp, sum, 0, cols - 1)) != 0 )
      goto TERMINATE;

   /* Handle linear constraints. */
   for (i = 0; i < rows; ++i) {
      int nz, surplus, beg;
      int n;

      status = CPXgetrows (env, lp, &nz, &beg, ind, val, cols, &surplus,
                           i, i);
      if ( status != 0 )
         goto TERMINATE;
      for (n = 0; n < nz; ++n) {
         sum[ind[n]] -= pi[i] * val[n];
      }
   }
   /* Handle second order cone constraints. */
   for (q = 0; q < qcons; ++q) {
      double norm = 0.0;
      int n;

      if ( !getqconstr (env, lp, q, &qbuf) )
         goto TERMINATE;

      for (n = 0; n < qbuf.qnz; ++n) {
         if ( qbuf.qval[n] > 0 )
            norm += x[qbuf.qcol[n]] * x[qbuf.qcol[n]];
      }
      norm = sqrt (norm);
      if ( fabs (norm) <= tol ) {
         CPXmsg (warnc, "WARNING: Cannot test stationarity at non-differentiable point.\n");
         skip = 1;
         break;
      }

      for (n = 0; n < qbuf.qnz; ++n) {
         if ( qbuf.qval[n] < 0 )
            sum[qbuf.qcol[n]] -= socppi[q];
         else
            sum[qbuf.qcol[n]] += socppi[q] * x[qbuf.qcol[n]] / norm;
      }
   }
   /* Handle variables that do not appear in any second order cone constraint.
    */
   for (j = 0; !skip && j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE ) {
         sum[j] -= dslack[j];
      }
   }

   /* Now test that all the entries in sum[] are 0.
    */
   for (j = 0; !skip && j < cols; ++j) {
      if ( fabs (sum[j]) > tol ) {
         CPXmsg (errc, "Stationarity not satisfied at index %d: %f\n",
                 j, sum[j]);
         goto TERMINATE;
      }
   }
   CPXmsg (logc, "ok.\n");

   CPXmsg (logc, "KKT conditions are satisfied.\n");

   ok = 1;
 TERMINATE:
   if ( !ok )
      CPXmsg (logc, "failed.\n");
   qbuf_clear (&qbuf);
   free (rhs);
   free (ind);
   free (val);
   free (sum);
   free (qslack);
   free (slack);
   free (sense);
   free (x);
   free (socppi);
   free (pi);
   free (dslack);

   return ok;
}
int
main (void)
{
   int status, solstat;
   CPXENVptr env;
   CPXLPptr lp;
   int i;
   double x[NUMCOLS];
   double cpi[NUMCOLS];
   double rpi[NUMROWS];
   double qpi[NUMQS];
   double slack[NUMROWS], qslack[NUMQS];
   double kktsum[NUMCOLS];

   /* ********************************************************************** *
    *                                                                        *
    *    S E T U P   P R O B L E M                                           *
    *                                                                        *
    * ********************************************************************** */

   /* Create CPLEX environment and enable screen output.
    */
   env = CPXopenCPLEX (&status);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON);
   if ( status != 0 )
      goto TERMINATE;

   /* Create the problem object and populate it.
    */
   lp = CPXcreateprob (env, &status, "qcpdual");
   if ( status != 0 )
      goto TERMINATE;
   status = CPXnewcols (env, lp, NUMCOLS, obj, lb, ub, NULL, cname);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXaddrows (env, lp, 0, NUMROWS, NUMNZS, rhs, sense,
                        rmatbeg, rmatind, rmatval, NULL, rname);
   if ( status != 0 )
      goto TERMINATE;
   for (i = 0; i < NUMQS; ++i) {
      int const linend = (i == NUMQS - 1) ? NUMLINNZ : linbeg[i + 1];
      int const quadend = (i == NUMQS - 1) ? NUMQUADNZ : quadbeg[i + 1];

      status = CPXaddqconstr (env, lp, linend - linbeg[i],
                              quadend - quadbeg[i], qrhs[i], qsense[i],
                              &linind[linbeg[i]], &linval[linbeg[i]],
                              &quadrow[quadbeg[i]], &quadcol[quadbeg[i]],
                              &quadval[quadbeg[i]], qname[i]);
      if ( status != 0 )
         goto TERMINATE;
   }

   /* ********************************************************************** *
    *                                                                        *
    *    O P T I M I Z E   P R O B L E M                                     *
    *                                                                        *
    * ********************************************************************** */
   status = CPXsetdblparam (env, CPXPARAM_Barrier_QCPConvergeTol, 1e-10);
   if ( status != 0 )
      goto TERMINATE;

   /* Solve the problem.
    */
   status = CPXbaropt (env, lp);
   if ( status != 0 )
      goto TERMINATE;

   solstat = CPXgetstat (env, lp);

   if ( solstat != CPX_STAT_OPTIMAL ) {
      fprintf (stderr, "No optimal solution found!\n");
      goto TERMINATE;
   }

   /* ********************************************************************** *
    *                                                                        *
    *    Q U E R Y   S O L U T I O N                                         *
    *                                                                        *
    * ********************************************************************** */

   /* Optimal solution and slacks for linear and quadratic constraints. */
   status = CPXgetx (env, lp, x, 0, NUMCOLS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXgetslack (env, lp, slack, 0, NUMROWS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXgetqconstrslack (env, lp, qslack, 0, NUMQS - 1);
   if ( status != 0 )
      goto TERMINATE;
   /* Dual multipliers for linear constraints and bound constraints. */
   status = CPXgetdj (env, lp, cpi, 0, NUMCOLS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXgetpi (env, lp, rpi, 0, NUMROWS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = getqconstrmultipliers (env, lp, x, qpi, ZEROTOL);
   if ( status != 0 )
      goto TERMINATE;

   /* ********************************************************************** *
    *                                                                        *
    *    C H E C K   K K T   C O N D I T I O N S                             *
    *                                                                        *
    *    Here we verify that the optimal solution computed by CPLEX (and     *
    *    the qpi[] values computed above) satisfy the KKT conditions.        *
    *                                                                        *
    * ********************************************************************** */

   /* Primal feasibility: This example is about duals so we skip this test. */

   /* Dual feasibility: We must verify
    * - for <= constraints (linear or quadratic) the dual
    *   multiplier is non-positive.
    * - for >= constraints (linear or quadratic) the dual
    *   multiplier is non-negative.
    */
   for (i = 0; i < NUMROWS; ++i) {
      switch (sense[i]) {
      case 'E': /* nothing */ break;
      case 'R': /* nothing */ break;
      case 'L':
         if ( rpi[i] > ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for <= row %d: %f\n",
                     i, rpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( rpi[i] < -ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for >= row %d: %f\n",
                     i, rpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      }
   }
   for (i = 0; i < NUMQS; ++i) {
      switch (qsense[i]) {
      case 'E': /* nothing */ break;
      case 'L':
         if ( qpi[i] > ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for <= quad %d: %f\n",
                     i, qpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( qpi[i] < -ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for >= quad %d: %f\n",
                     i, qpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      }
   }

   /* Complementary slackness.
    * For any constraint the product of primal slack and dual multiplier
    * must be 0.
    */
   for (i = 0; i < NUMROWS; ++i) {
      if ( sense[i] != 'E' && fabs (slack[i] * rpi[i]) > ZEROTOL ) {
         fprintf (stderr,
                  "Complementary slackness test failed for row %d: %f\n",
                  i, fabs (slack[i] * rpi[i]));
         status = -1;
         goto TERMINATE;
      }
   }
   for (i = 0; i < NUMQS; ++i) {
      if ( qsense[i] != 'E' && fabs (qslack[i] * qpi[i]) > ZEROTOL ) {
         fprintf (stderr,
                  "Complementary slackness test failed for quad %d: %f\n",
                  i, fabs (qslack[i] * qpi[i]));
         status = -1;
         goto TERMINATE;
      }
   }
   for (i = 0; i < NUMCOLS; ++i) {
      if ( ub[i] < CPX_INFBOUND ) {
         double const slk = ub[i] - x[i];
         double const dual = cpi[i] < -ZEROTOL ? cpi[i] : 0.0;
         if ( fabs (slk * dual) > ZEROTOL ) {
            fprintf (stderr,
                     "Complementary slackness test failed for ub %d: %f\n",
                     i, fabs (slk * dual));
            status = -1;
            goto TERMINATE;
         }
      }
      if ( lb[i] > -CPX_INFBOUND ) {
         double const slk = x[i] - lb[i];
         double const dual = cpi[i] > ZEROTOL ? cpi[i] : 0.0;
         if ( fabs (slk * dual) > ZEROTOL ) {
            printf ("lb=%f, x=%f, cpi=%f\n", lb[i], x[i], cpi[i]);
            fprintf (stderr,
                     "Complementary slackness test failed for lb %d: %f\n",
                     i, fabs (slk * dual));
            status = -1;
            goto TERMINATE;
         }
      }
   }

   /* Stationarity.
    * The difference between objective function and gradient at optimal
    * solution multiplied by dual multipliers must be 0, i.e., for the
    * optimal solution x
    * 0 == c
    *      - sum(r in rows)  r'(x)*rpi[r]
    *      - sum(q in quads) q'(x)*qpi[q]
    *      - sum(c in cols)  b'(x)*cpi[c]
    * where r' and q' are the derivatives of a row or quadratic constraint,
    * x is the optimal solution and rpi[r] and qpi[q] are the dual
    * multipliers for row r and quadratic constraint q.
    * b' is the derivative of a bound constraint and cpi[c] the dual bound
    * multiplier for column c.
    */

   /* Objective function. */
   for (i = 0; i < NUMCOLS; ++i)
      kktsum[i] = obj[i];

   /* Linear constraints.
    * The derivative of a linear constraint ax - b (<)= 0 is just a.
    */
   for (i = 0; i < NUMROWS; ++i) {
      int const end = (i == NUMROWS - 1) ? NUMNZS : rmatbeg[i + 1];
      int k;

      for (k = rmatbeg[i]; k < end; ++k)
         kktsum[rmatind[k]] -= rpi[i] * rmatval[k];
   }

   /* Quadratic constraints.
    * The derivative of a constraint xQx + ax - b <= 0 is
    * Qx + Q'x + a.
    */
   for (i = 0; i < NUMQS; ++i) {
      int j;
      int k;

      for (j = linbeg[i]; j < linbeg[i] + linnzcnt[i]; ++j)
         kktsum[linind[j]] -= qpi[i] * linval[j];
      for (k = quadbeg[i]; k < quadbeg[i] + quadnzcnt[i]; ++k) {
         kktsum[quadrow[k]] -= qpi[i] * x[quadcol[k]] * quadval[k];
         kktsum[quadcol[k]] -= qpi[i] * x[quadrow[k]] * quadval[k];
      }
   }

   /* Bounds.
    * The derivative for lower bounds is -1 and that for upper bounds
    * is 1.
    * CPLEX already returns dj with the appropriate sign so there is
    * no need to distinguish between different bound types here.
    */
   for (i = 0; i < NUMCOLS; ++i) {
      kktsum[i] -= cpi[i];
   }

   for (i = 0; i < NUMCOLS; ++i) {
      if ( fabs (kktsum[i]) > ZEROTOL ) {
         fprintf (stderr, "Stationarity test failed at index %d: %f\n",
                  i, kktsum[i]);
         status = -1;
         goto TERMINATE;
      }
   }

   /* KKT conditions satisfied. Dump out the optimal solutions and
    * the dual values.
    */

   printf ("Optimal solution satisfies KKT conditions.\n");
   printf ("  x[] =");
   for (i = 0; i < NUMCOLS; ++i)
      printf (" %7.3f", x[i]);
   printf ("\n");
   printf ("cpi[] =");
   for (i = 0; i < NUMCOLS; ++i)
      printf (" %7.3f", cpi[i]);
   printf ("\n");
   printf ("rpi[] =");
   for (i = 0; i < NUMROWS; ++i)
      printf (" %7.3f", rpi[i]);
   printf ("\n");
   printf ("qpi[] =");
   for (i = 0; i < NUMQS; ++i)
      printf (" %7.3f", qpi[i]);
   printf ("\n");
   
 TERMINATE:
   /* ********************************************************************** *
    *                                                                        *
    *    C L E A N U P                                                       *
    *                                                                        *
    * ********************************************************************** */

   status = CPXfreeprob (env, &lp);
   if ( status != 0 ) {
      fprintf (stderr, "WARNING: Failed to free problem: %d\n", status);
   }
   status = CPXcloseCPLEX (&env);
   if ( status != 0 ) {
      fprintf (stderr, "WARNING: Failed to close CPLEX: %d\n", status);
   }

   return status;
}