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