/* 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; }
void CplexSolver::rc(DoubleVector & result)const{ result.resize(ncols()); CPXgetdj(_env, _prob, result.data(), 0, ncols() - 1); }
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; }