Exemple #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;
}
/* ********************************************************************** *
 *                                                                        *
 *    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;
}