int main (void) { int status; CPXENVptr env; CPXLPptr lp; CPXDIM 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 = CPXXopenCPLEX (&status); if ( status != 0 ) goto TERMINATE; status = CPXXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON); if ( status != 0 ) goto TERMINATE; /* Create the problem object and populate it. */ lp = CPXXcreateprob (env, &status, "qcpdual"); if ( status != 0 ) goto TERMINATE; status = CPXXnewcols (env, lp, NUMCOLS, obj, lb, ub, NULL, cname); if ( status != 0 ) goto TERMINATE; status = CPXXaddrows (env, lp, 0, NUMROWS, NUMNZS, rhs, sense, rmatbeg, rmatind, rmatval, NULL, rname); if ( status != 0 ) goto TERMINATE; for (i = 0; i < NUMQS; ++i) { CPXNNZ const linend = (i == NUMQS - 1) ? NUMLINNZ : linbeg[i + 1]; CPXNNZ const quadend = (i == NUMQS - 1) ? NUMQUADNZ : quadbeg[i + 1]; status = CPXXaddqconstr (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 = CPXXsetdblparam (env, CPXPARAM_Barrier_QCPConvergeTol, 1e-10); if ( status != 0 ) goto TERMINATE; /* Solve the problem. */ status = CPXXbaropt (env, lp); if ( status != 0 ) goto TERMINATE; if ( CPXXgetstat (env, lp) != 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 = CPXXgetx (env, lp, x, 0, NUMCOLS - 1); if ( status != 0 ) goto TERMINATE; status = CPXXgetslack (env, lp, slack, 0, NUMROWS - 1); if ( status != 0 ) goto TERMINATE; status = CPXXgetqconstrslack (env, lp, qslack, 0, NUMQS - 1); if ( status != 0 ) goto TERMINATE; /* Dual multipliers for linear constraints and bound constraints. */ status = CPXXgetdj (env, lp, cpi, 0, NUMCOLS - 1); if ( status != 0 ) goto TERMINATE; status = CPXXgetpi (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) { CPXNNZ const end = (i == NUMROWS - 1) ? NUMNZS : rmatbeg[i + 1]; CPXNNZ 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) { CPXDIM j; CPXNNZ 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 = CPXXfreeprob (env, &lp); if ( status != 0 ) { fprintf (stderr, "WARNING: Failed to free problem: %d\n", status); } status = CPXXcloseCPLEX (&env); if ( status != 0 ) { fprintf (stderr, "WARNING: Failed to close CPLEX: %d\n", status); } return status; }
int main (int argc, char **argv) { int result = 0; IloEnv env; try { static int indices[] = { 0, 1, 2, 3, 4, 5, 6 }; IloModel model(env); /* ***************************************************************** * * * * S E T U P P R O B L E M * * * * The model we setup here is * * Minimize * * obj: 3x1 - x2 + 3x3 + 2x4 + x5 + 2x6 + 4x7 * * Subject To * * c1: x1 + x2 = 4 * * c2: x1 + x3 >= 3 * * c3: x6 + x7 <= 5 * * c4: -x1 + x7 >= -2 * * q1: [ -x1^2 + x2^2 ] <= 0 * * q2: [ 4.25x3^2 -2x3*x4 + 4.25x4^2 - 2x4*x5 + 4x5^2 ] + 2 x1 <= 9.0 * q3: [ x6^2 - x7^2 ] >= 4 * * Bounds * * 0 <= x1 <= 3 * * x2 Free * * 0 <= x3 <= 0.5 * * x4 Free * * x5 Free * * x7 Free * * End * * * * ***************************************************************** */ IloNumVarArray x(env); x.add(IloNumVar(env, 0, 3, "x1")); x.add(IloNumVar(env, -IloInfinity, IloInfinity, "x2")); x.add(IloNumVar(env, 0, 0.5, "x3")); x.add(IloNumVar(env, -IloInfinity, IloInfinity, "x4")); x.add(IloNumVar(env, -IloInfinity, IloInfinity, "x5")); x.add(IloNumVar(env, 0, IloInfinity, "x6")); x.add(IloNumVar(env, -IloInfinity, IloInfinity, "x7")); for (IloInt i = 0; i < x.getSize(); ++i) x[i].setObject(&indices[i]); IloObjective obj = IloMinimize(env, 3*x[0] - x[1] + 3*x[2] + 2*x[3] + x[4] + 2*x[5] + 4*x[6], "obj"); model.add(obj); IloRangeArray linear(env); linear.add(IloRange(env, 4.0, x[0] + x[1], 4.0, "c1")); linear.add(IloRange(env, 3.0, x[0] + x[2], IloInfinity, "c2")); linear.add(IloRange(env, -IloInfinity, x[5] + x[6], 5.0, "c3")); linear.add(IloRange(env, -2.0, -x[0] + x[6], IloInfinity, "c4")); for (IloInt i = 0; i < linear.getSize(); ++i) linear[i].setObject(&indices[i]); model.add(linear); IloRangeArray quad(env); quad.add(IloRange(env, -IloInfinity, -x[0]*x[0] + x[1] * x[1], 0, "q1")); quad.add(IloRange(env, -IloInfinity, 4.25*x[2]*x[2] - 2*x[2]*x[3] + 4.25*x[3]*x[3] + -2*x[3]*x[4] + 4*x[4]*x[4] + 2*x[0], 9.0, "q2")); quad.add(IloRange(env, 4.0, x[5]*x[5] - x[6]*x[6], IloInfinity, "q3")); for (IloInt i = 0; i < quad.getSize(); ++i) quad[i].setObject(&indices[i]); model.add(quad); /* ***************************************************************** * * * * O P T I M I Z E P R O B L E M * * * * ***************************************************************** */ IloCplex cplex(model); cplex.setParam(IloCplex::Param::Barrier::QCPConvergeTol, 1e-10); cplex.solve(); /* ***************************************************************** * * * * Q U E R Y S O L U T I O N * * * * ***************************************************************** */ IloNumArray xval(env); IloNumArray slack(env); IloNumArray qslack(env); IloNumArray cpi(env); IloNumArray rpi(env); IloNumArray qpi; cplex.getValues(x, xval); cplex.getSlacks(slack, linear); cplex.getSlacks(qslack, quad); cplex.getReducedCosts(cpi, x); cplex.getDuals(rpi, linear); qpi = getqconstrmultipliers(cplex, xval, quad); /* ***************************************************************** * * * * 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 (IloInt i = 0; i < linear.getSize(); ++i) { if ( linear[i].getLB() <= -IloInfinity ) { // <= constraint if ( rpi[i] > ZEROTOL ) { cerr << "Dual feasibility test failed for <= row " << i << ": " << rpi[i] << endl; result = -1; } } else if ( linear[i].getUB() >= IloInfinity ) { // >= constraint if ( rpi[i] < -ZEROTOL ) { cerr << "Dual feasibility test failed for >= row " << i << ":" << rpi[i] << endl; result = -1; } } else { // nothing to do for equality constraints } } for (IloInt q = 0; q < quad.getSize(); ++q) { if ( quad[q].getLB() <= -IloInfinity ) { // <= constraint if ( qpi[q] > ZEROTOL ) { cerr << "Dual feasibility test failed for <= quad " << q << ": " << qpi[q] << endl; result = -1; } } else if ( quad[q].getUB() >= IloInfinity ) { // >= constraint if ( qpi[q] < -ZEROTOL ) { cerr << "Dual feasibility test failed for >= quad " << q << ":" << qpi[q] << endl; result = -1; } } else { // nothing to do for equality constraints } } // Complementary slackness. // For any constraint the product of primal slack and dual multiplier // must be 0. for (IloInt i = 0; i < linear.getSize(); ++i) { if ( fabs (linear[i].getUB() - linear[i].getLB()) > ZEROTOL && fabs (slack[i] * rpi[i]) > ZEROTOL ) { cerr << "Complementary slackness test failed for row " << i << ": " << fabs (slack[i] * rpi[i]) << endl; result = -1; } } for (IloInt q = 0; q < quad.getSize(); ++q) { if ( fabs (quad[q].getUB() - quad[q].getLB()) > ZEROTOL && fabs (qslack[q] * qpi[q]) > ZEROTOL ) { cerr << "Complementary slackness test failed for quad " << q << ":" << fabs (qslack[q] * qpi[q]) << endl; result = -1; } } for (IloInt j = 0; j < x.getSize(); ++j) { if ( x[j].getUB() < IloInfinity ) { double const slk = x[j].getUB() - xval[j]; double const dual = cpi[j] < -ZEROTOL ? cpi[j] : 0.0; if ( fabs (slk * dual) > ZEROTOL ) { cerr << "Complementary slackness test failed for ub " << j << ": " << fabs (slk * dual) << endl; result = -1; } } if ( x[j].getLB() > -IloInfinity ) { double const slk = xval[j] - x[j].getLB(); double const dual = cpi[j] > ZEROTOL ? cpi[j] : 0.0; if ( fabs (slk * dual) > ZEROTOL ) { cerr << "Complementary slackness test failed for lb " << j << ": " << fabs (slk * dual) << endl; result = -1; } } } // 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. IloNumArray kktsum(env, x.getSize()); // Objective function. for (IloExpr::LinearIterator it = obj.getLinearIterator(); it.ok(); ++it) kktsum[idx(it.getVar())] = it.getCoef(); // Linear constraints. // The derivative of a linear constraint ax - b (<)= 0 is just a. for (IloInt i = 0; i < linear.getSize(); ++i) { for (IloExpr::LinearIterator it = linear[i].getLinearIterator(); it.ok(); ++it) kktsum[idx(it.getVar())] -= rpi[i] * it.getCoef(); } // Quadratic constraints. // The derivative of a constraint xQx + ax - b <= 0 is // Qx + Q'x + a. for (IloInt q = 0; q < quad.getSize(); ++q) { for (IloExpr::LinearIterator it = quad[q].getLinearIterator(); it.ok(); ++it) kktsum[idx(it.getVar())] -= qpi[q] * it.getCoef(); for (IloExpr::QuadIterator it = quad[q].getQuadIterator(); it.ok(); ++it) { kktsum[idx(it.getVar1())] -= qpi[q] * xval[idx(it.getVar2())] * it.getCoef(); kktsum[idx(it.getVar2())] -= qpi[q] * xval[idx(it.getVar1())] * it.getCoef(); } } // 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 (IloInt j = 0; j < x.getSize(); ++j) kktsum[j] -= cpi[j]; for (IloInt j = 0; j < kktsum.getSize(); ++j) { if ( fabs (kktsum[j]) > ZEROTOL ) { cerr << "Stationarity test failed at index " << j << ": " << kktsum[j] << endl; result = -1; } } if ( result == 0) { // KKT conditions satisfied. Dump out the optimal solutions and // the dual values. streamsize oprec = cout.precision(3); ios_base::fmtflags oflags = cout.setf(ios::fixed | ios::showpos); cout << "Optimal solution satisfies KKT conditions." << endl; cout << " x[] = " << xval << endl; cout << " cpi[] = " << cpi << endl; cout << " rpi[] = " << rpi << endl; cout << " qpi[] = " << qpi << endl; cout.precision(oprec); cout.flags(oflags); } } catch (IloException& e) { cerr << "Concert exception caught: " << e << endl; result = -1; } catch (...) { cerr << "Unknown exception caught" << endl; result = -1; } env.end(); return result; } // END main