示例#1
0
/* CPLEX does not provide a function to directly get the dual multipliers
 * for second order cone constraint.
 * Example qcpdual.c illustrates how the dual multipliers for a
 * quadratic constraint can be computed from that constraint's slack.
 * However, for SOCP we can do something simpler: we can read those
 * multipliers directly from the dual slacks for the
 * cone head variables. For a second order cone constraint
 *    x[1] >= |(x[2], ..., x[n])|
 * the dual multiplier is the dual slack value for x[1].
 */
static int
getsocpconstrmultipliers (CPXCENVptr env, CPXCLPptr lp,
                          double *dslack, double *socppi)
{
   int status = 0;
   int const cols = CPXgetnumcols (env, lp);
   int const qs = CPXgetnumqconstrs (env, lp);
   double *dense = NULL, *val = NULL;
   int *ind = NULL;
   int j, q;
   qbuf_type qbuf;

   qbuf_init (&qbuf);

   if ( (dense = malloc (sizeof (*dense) * cols)) == NULL ) {
      status = CPXERR_NO_MEMORY;
      goto TERMINATE;
   }

   /* First of all get the dual slack vector. This is the sum of
    * dual multipliers for bound constraints (as returned by CPXgetdj())
    * and the per-constraint dual slack vectors (as returned by
    * CPXgetqconstrdslack()).
    */

   /* Get dual multipliers for bound constraints. */
   if ( (status = CPXgetdj (env, lp, dense, 0, cols - 1)) != 0 )
      goto TERMINATE;

   /* Create a dense vector that holds the sum of dual slacks for all
    * quadratic constraints.
    */
   if ( (val = malloc (cols * sizeof (val))) == NULL ||
        (ind = malloc (cols * sizeof (ind))) == NULL )
   {
      status = CPXERR_NO_MEMORY;
      goto TERMINATE;
   }
   for (q = 0; q < qs; ++q) {
      int len = 0, surp;

      if ( (status = CPXgetqconstrdslack (env, lp, q, &len, ind, val,
                                          cols, &surp)) != 0 )
         goto TERMINATE;
      for (j = 0; j < len; ++j) {
         dense[ind[j]] += val[j];
      }
   }

   /* Now go through the socp constraints. For each constraint find the
    * cone head variable (the one variable that has a negative coefficient)
    * and pick up its dual slack. This is the dual multiplier for the
    * constraint.
    */
   for (q = 0; q < qs; ++q) {
      if ( !getqconstr (env, lp, q, &qbuf) ) {
         status = CPXERR_BAD_ARGUMENT;
         goto TERMINATE;
      }
      for (j = 0; j < qbuf.qnz; ++j) {
         if ( qbuf.qval[j] < 0.0 ) {
            /* This is the cone head variable. */
            socppi[q] = dense[qbuf.qcol[j]];
            break;
         }
      }
   }

   /* If the caller wants to have the dual slack vector then copy it
    * to the output array.
    */
   if ( dslack != NULL )
      memcpy (dslack, dense, sizeof (*dslack) * cols);

 TERMINATE:
   free (ind);
   free (val);
   free (dense);

   qbuf_clear (&qbuf);

   return status;
}
示例#2
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;
}
/* ********************************************************************** *
 *                                                                        *
 *    C A L C U L A T E   D U A L S   F O R   Q U A D   C O N S T R S     *
 *                                                                        *
 *    CPLEX does not give us the dual multipliers for quadratic           *
 *    constraints directly. This is because they may not be properly      *
 *    defined at the cone top and deciding whether we are at the cone     *
 *    top or not involves (problem specific) tolerance issues. CPLEX      *
 *    instead gives us all the values we need in order to compute the     *
 *    dual multipliers if we are not at the cone top.                     *
 *    Function getqconstrmultipliers() takes the following arguments:     *
 *    env       CPLEX environment that was used to create LP.             *
 *    lp        CPLEX problem object for which the dual multipliers are   *
 *              to be calculated.                                         *
 *    x         The optimal solution.                                     *
 *    qpi       Array that stores the dual multipliers upon success.      *
 *    zerotol   The tolerance that is used to decide whether a value      *
 *              if zero or not (used to decide about "cone top" or not).  *
 *                                                                        *
 * ********************************************************************** */
static int
getqconstrmultipliers (CPXCENVptr   env,
                       CPXCLPptr    lp,
                       double const *x,
                       double       *qpi,
                       double       zerotol)
{
   int const cols = CPXgetnumcols (env, lp);
   int const numqs = CPXgetnumqconstrs (env, lp);
   double *dense = NULL;
   double *grad = NULL;
   int *slackind = NULL;
   double *slackval = NULL;
   int status = 0;
   int i;

   if ( (dense    = malloc (sizeof (*dense) * cols)) == NULL ||
        (grad     = malloc (sizeof (*grad) * cols)) == NULL ||
        (slackind = malloc (sizeof (*slackind) * cols)) == NULL ||
        (slackval = malloc (sizeof (*slackval) * cols)) == NULL )
   {
      status = CPXERR_NO_MEMORY;
      goto TERMINATE;
   }

   /* Calculate dual multipliers for quadratic constraints from dual
    * slack vectors and optimal solutions.
    * The dual multiplier is essentially the dual slack divided
    * by the derivative evaluated at the optimal solution. If the optimal
    * solution is 0 then the derivative at this point is not defined (we are
    * at the cone top) and we cannot compute a dual multiplier.
    */
   for (i = 0; i < numqs; ++i) {
      int j, k;
      int surplus, len;
      int conetop;

      /* Clear out dense slack and gradient vector. */
      for (j = 0; j < cols; ++j) {
         dense[j] = 0.0;
         grad[j] = 0;
      }

      /* Get dual slack vector and expand it to a dense vector. */
      status = CPXgetqconstrdslack (env, lp, i, &len, slackind, slackval,
                                    cols, &surplus);
      if ( status != 0 )
         goto TERMINATE;
      for (k = 0; k < len; ++k)
         dense[slackind[k]] = slackval[k];

      /* Compute value of derivative at optimal solution. */
      /* The derivative of a quadratic constraint x^TQx + a^Tx + b <= 0
       * is Q^Tx + Qx + a.
       */
      conetop = 1;
      for (k = quadbeg[i]; k < quadbeg[i] + quadnzcnt[i]; ++k) {
         if ( fabs (x[quadrow[k]]) > zerotol ||
              fabs (x[quadcol[k]]) > zerotol )
            conetop = 0;
         grad[quadcol[k]] += quadval[k] * x[quadrow[k]];
         grad[quadrow[k]] += quadval[k] * x[quadcol[k]];
      }
      for (j = linbeg[i]; j < linbeg[i] + linnzcnt[i]; ++j) {
         grad[linind[j]] += linval[j];
         if ( fabs (x[linind[j]]) > zerotol )
            conetop = 0;
      }

      if ( conetop ) {
         fprintf (stderr,
                  "#### WARNING: Cannot compute dual multipler at cone top!\n");
         status = CPXERR_BAD_ARGUMENT;
         goto TERMINATE;
      }
      else {
         int ok = 0;
         double maxabs = -1.0;

         /* Compute qpi[i] as slack/gradient.
          * We may have several indices to choose from and use the one
          * with largest absolute value in the denominator.
          */
         for (j = 0; j < cols; ++j) {
            if ( fabs (grad[j]) > zerotol ) {
               if ( fabs (grad[j]) > maxabs ) {
                  qpi[i] = dense[j] / grad[j];
                  maxabs = fabs (grad[j]);
               }
               ok = 1;
            }
         }
            
         if ( !ok ) {
            /* Dual slack is all 0. qpi[i] can be anything, just set
             * to 0. */
            qpi[i] = 0.0;
         }
      }
   }

 TERMINATE:
   free (slackval);
   free (slackind);
   free (grad);
   free (dense);

   return status;
}