ILOSTLBEGIN // Use a user cut callback when the added constraints strengthen the // formulation. Use a lazy constraint callback when the added constraints // remove part of the feasible region. Use a cut callback when you // are not certain. ILOUSERCUTCALLBACK3(CtCallback, IloExprArray, lhs, IloNumArray, rhs, IloNum, eps) { IloInt n = lhs.getSize(); for (IloInt i = 0; i < n; i++) { IloNum xrhs = rhs[i]; if ( xrhs < IloInfinity && getValue(lhs[i]) > xrhs + eps ) { IloRange cut; try { cut = (lhs[i] <= xrhs); add(cut).end(); rhs[i] = IloInfinity; } catch (...) { cut.end(); throw; } } } }
bool getCut( IloNumArray& vals, IloNumVarArray& vars, CutMode cutmode, int set, IloCplex::ControlCallbackI::IntegerFeasibilityArray feas, IloRange& cut, int** graph, int* old_winner ){ int n = vals.getSize()/2; bool marked[n]; for( int i = 0; i < n; ++i ) marked[i] = false; int winner; int count_marked = 0; list<int> indices; int acc[n][n];//mudar para lista adjacencia memset(acc, 0, sizeof(acc)); static int cont=0; for( int i = 0; i < n; ++i ){ if(feas[i+n*set] == IloCplex::ControlCallbackI::Infeasible) marked[i] = false; else{ marked[i]=true; count_marked++; } } //int cont=0; int cnt=0; while( count_marked < n ){ //choose the star node cnt++; winner = -1; for( int i = 0; i < n; ++i ){ if(old_winner[i+n*set])continue; if( !marked[i] ){ if( winner == -1 ){ winner = i; } else if( cutmode == CLQ2B && fabs(vals[i+n*set]-0.5) < fabs(vals[winner+n*set]-0.5) && vals[i+n*set] > 0 && vals[i+n*set] < 1 ){ winner = i; } else if( cutmode == CLQ2A && vals[i+n*set] > vals[winner+n*set] && vals[i+n*set] < 1 ){ winner = i; } } } if(winner==-1)return false; //?? old_winner[winner+n*set]=1; ++count_marked; marked[winner] = true; indices.push_back( winner+n*set ); for( int i = 0; i < n; ++i ){ if( !graph[i][winner] ){ if( !marked[i] ){ marked[i] = true; ++count_marked; } } } } list<int>::iterator it = indices.begin(); float sum = 0; while( it != indices.end() ){ sum += vals[*it]; ++it; } for(list<int>::iterator it1 = indices.begin(); it1!= indices.end(); it1++){ for(list<int>::iterator it2 = indices.begin(); it2!= indices.end(); it2++){ int v1 = *it1; int v2 = *it2; if(v1==v2)continue; v1 -= n*set; v2 -= n*set; if(!graph[v1][v2] || !graph[v2][v1]){ puts("ERRO NAO CLICK"); printf("%d %d\n", v1, v2); exit(-1); } } } if(sum > 1+0.000001 ){ it = indices.begin(); while( it != indices.end() ){ cut.setLinearCoef( vars[*it], 1 ); //printf("x[%d] ", *it); ++it; } //printf(" <= 1\n"); return true; } return false; }
// Test KKT conditions on the solution. // The function returns true if the tested KKT conditions are satisfied // and false otherwise. // The function assumes that the model currently extracted to CPLEX is fully // described by obj, vars and rngs. static bool checkkkt (IloCplex& cplex, IloObjective const& obj, IloNumVarArray const& vars, IloRangeArray const& rngs, IloIntArray const& cone, double tol) { IloEnv env = cplex.getEnv(); IloModel model = cplex.getModel(); IloNumArray x(env), dslack(env); IloNumArray pi(env, rngs.getSize()), slack(env); // Read primal and dual solution information. cplex.getValues(x, vars); cplex.getSlacks(slack, rngs); // pi for second order cone constraints. getsocpconstrmultipliers(cplex, vars, rngs, pi, dslack); // pi for linear constraints. for (IloInt i = 0; i < rngs.getSize(); ++i) { IloRange r = rngs[i]; if ( !r.getQuadIterator().ok() ) pi[idx(r)] = cplex.getDual(r); } // Print out the data we just fetched. streamsize oprec = env.out().precision(3); ios_base::fmtflags oflags = env.out().setf(ios::fixed | ios::showpos); env.out() << "x = ["; for (IloInt i = 0; i < x.getSize(); ++i) env.out() << " " << x[i]; env.out() << " ]" << endl; env.out() << "dslack = ["; for (IloInt i = 0; i < dslack.getSize(); ++i) env.out() << " " << dslack[i]; env.out() << " ]" << endl; env.out() << "pi = ["; for (IloInt i = 0; i < rngs.getSize(); ++i) env.out() << " " << pi[i]; env.out() << " ]" << endl; env.out() << "slack = ["; for (IloInt i = 0; i < rngs.getSize(); ++i) env.out() << " " << slack[i]; env.out() << " ]" << endl; env.out().precision(oprec); env.out().flags(oflags); // Test 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. // 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, // - the dslack value for all non-cone variables must be non-negative. // Note that we do not support ranged constraints here. for (IloInt i = 0; i < vars.getSize(); ++i) { IloNumVar v = vars[i]; if ( cone[i] == NOT_IN_CONE && dslack[i] < -tol ) { env.error() << "Dual multiplier for " << v << " is not feasible: " << dslack[i] << endl; return false; } } for (IloInt i = 0; i < rngs.getSize(); ++i) { IloRange r = rngs[i]; if ( fabs (r.getLB() - r.getUB()) <= tol ) { // Nothing to check for equality constraints. } else if ( r.getLB() > -IloInfinity && pi[i] > tol ) { env.error() << "Dual multiplier " << pi[i] << " for >= constraint" << endl << r << endl << "not feasible" << endl; return false; } else if ( r.getUB() < IloInfinity && pi[i] < -tol ) { env.error() << "Dual multiplier " << pi[i] << " for <= constraint" << endl << r << endl << "not feasible" << endl; return false; } } // Test complementary slackness. // For each constraint either the constraint must have zero slack or // the dual multiplier for the constraint must be 0. We must also // consider the special case in which a variable is not explicitly // contained in a second order cone constraint. for (IloInt i = 0; i < vars.getSize(); ++i) { if ( cone[i] == NOT_IN_CONE ) { if ( fabs(x[i]) > tol && dslack[i] > tol ) { env.error() << "Invalid complementary slackness for " << vars[i] << ":" << endl << " " << x[i] << " and " << dslack[i] << endl; return false; } } } for (IloInt i = 0; i < rngs.getSize(); ++i) { if ( fabs(slack[i]) > tol && fabs(pi[i]) > tol ) { env.error() << "Invalid complementary slackness for " << endl << rngs[i] << ":" << endl << " " << slack[i] << " and " << pi[i] << endl; return false; } } // 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. IloNumArray sum(env, vars.getSize()); for (IloExpr::LinearIterator it = obj.getLinearIterator(); it.ok(); ++it) sum[idx(it.getVar())] = it.getCoef(); for (IloInt i = 0; i < vars.getSize(); ++i) { IloNumVar v = vars[i]; if ( cone[i] == NOT_IN_CONE ) sum[i] -= dslack[i]; } for (IloInt i = 0; i < rngs.getSize(); ++i) { IloRange r = rngs[i]; if ( r.getQuadIterator().ok() ) { // Quadratic (second order cone) constraint. IloNum norm = 0.0; for (IloExpr::QuadIterator q = r.getQuadIterator(); q.ok(); ++q) { if ( q.getCoef() > 0 ) norm += x[idx(q.getVar1())] * x[idx(q.getVar1())]; } norm = sqrt(norm); if ( fabs(norm) <= tol ) { // Derivative is not defined. Skip test. env.warning() << "Cannot test stationarity at non-differentiable point." << endl; return true; } else { for (IloExpr::QuadIterator q = r.getQuadIterator(); q.ok(); ++q) { if ( q.getCoef() < 0 ) sum[idx(q.getVar1())] -= pi[i]; else sum[idx(q.getVar1())] += pi[i] * x[idx(q.getVar1())] / norm; } } } else { // Linear constraint. for (IloExpr::LinearIterator l = r.getLinearIterator(); l.ok(); ++l) sum[idx(l.getVar())] -= pi[i] * l.getCoef(); } } // Now test that all elements in sum[] are 0. for (IloInt i = 0; i < vars.getSize(); ++i) { if ( fabs(sum[i]) > tol ) { env.error() << "Invalid stationarity " << sum[i] << " for " << vars[i] << endl; return false; } } return true; }
/** Create the dual of a linear program. * The function can only dualize programs of the form * <code>Ax <= b, x >= 0</code>. The data in <code>primalVars</code> and * <code>dualRows</code> as well as in <code>primalRows</code> and * <code>dualVars</code> is in 1-to-1-correspondence. * @param primalObj Objective function of primal problem. * @param primalVars Variables in primal problem. * @param primalRows Rows in primal problem. * @param dualObj Objective function of dual will be stored here. * @param dualVars All dual variables will be stored here. * @param dualRows All dual rows will be stored here. */ void BendersOpt::makeDual(IloObjective const &primalObj, IloNumVarArray const &primalVars, IloRangeArray const &primalRows, IloObjective *dualObj, IloNumVarArray *dualVars, IloRangeArray *dualRows) { // To keep the code simple we only support problems // of the form Ax <= b, b >= 0 here. We leave it as a reader's // exercise to extend the function to something that can handle // any kind of linear model. for (IloInt j = 0; j < primalVars.getSize(); ++j) if ( primalVars[j].getLB() != 0 || primalVars[j].getUB() < IloInfinity ) { std::stringstream s; s << "Cannot dualize variable " << primalVars[j]; throw s.str(); } for (IloInt i = 0; i < primalRows.getSize(); ++i) if ( primalRows[i].getLB() > -IloInfinity || primalRows[i].getUB() >= IloInfinity ) { std::stringstream s; s << "Cannot dualize constraint " << primalRows[i]; std::cerr << s.str() << std::endl; throw s.str(); } // The dual of // min/max c^T x // Ax <= b // x >= 0 // is // max/min y^T b // y^T A <= c // y <= 0 // We scale y by -1 to get >= 0 IloEnv env = primalVars.getEnv(); IloObjective obj(env, 0.0, primalObj.getSense() == IloObjective::Minimize ? IloObjective::Maximize : IloObjective::Minimize); IloRangeArray rows(env); IloNumVarArray y(env); std::map<IloNumVar,IloInt,ExtractableLess<IloNumVar> > v2i; for (IloInt j = 0; j < primalVars.getSize(); ++j) { IloNumVar x = primalVars[j]; v2i.insert(std::map<IloNumVar,IloInt,ExtractableLess<IloNumVar> >::value_type(x, j)); rows.add(IloRange(env, -IloInfinity, 0, x.getName())); } for (IloExpr::LinearIterator it = primalObj.getLinearIterator(); it.ok(); ++it) rows[v2i[it.getVar()]].setUB(it.getCoef()); for (IloInt i = 0; i < primalRows.getSize(); ++i) { IloRange r = primalRows[i]; IloNumColumn col(env); col += obj(-r.getUB()); for (IloExpr::LinearIterator it = r.getLinearIterator(); it.ok(); ++it) col += rows[v2i[it.getVar()]](-it.getCoef()); y.add(IloNumVar(col, 0, IloInfinity, IloNumVar::Float, r.getName())); } *dualObj = obj; *dualVars = y; *dualRows = rows; }
/** Extract the master block from <code>problem</code>. * The constructor also sets up the solver for the newly created master * block. The master block can only be extracted if all sub-blocks have * already been extracted. * @param problem The problem from which to extract the master. * @param blocks The sub blocks that have already been extracted. */ BendersOpt::Block::Block(Problem const *problem, BlockVector const &blocks) : env(), number(-1), vars(0), rows(0), cplex(0), cb(0) { IloNumVarArray problemVars = problem->getVariables(); IloRangeArray problemRanges = problem->getRows(); IloExpr masterObj(env); IloNumVarArray masterVars(env); IloRangeArray masterRows(env); // Find columns that do not intersect block variables and // copy them to the master block. IdxMap idxMap; RowSet rowSet; for (IloInt j = 0; j < problemVars.getSize(); ++j) { IloNumVar x = problemVars[j]; if ( problem->getBlock(x) < 0 ) { // Column is not in a block. Copy it to the master. IloNumVar v(env, x.getLB(), x.getUB(), x.getType(), x.getName()); varMap.insert(VarMap::value_type(v, x)); masterObj += problem->getObjCoef(x) * v; idxMap[x] = masterVars.getSize(); masterVars.add(v); } else { // Column is in a block. Collect all rows that intersect // this column. RowSet const &intersected = problem->getIntersectedRows(x); for (RowSet::const_iterator it = intersected.begin(); it != intersected.end(); ++it) rowSet.insert(*it); idxMap[x] = -1; } } // Pick up the rows that we need to copy. // These are the rows that are only intersected by master variables, // that is, the rows that are not in any block's rowset. for (IloInt i = 0; i < problemRanges.getSize(); ++i) { IloRange r = problemRanges[i]; if ( rowSet.find(r) == rowSet.end() ) { IloRange masterRow(env, r.getLB(), r.getUB(), r.getName()); IloExpr lhs(env); for (IloExpr::LinearIterator it = r.getLinearIterator(); it.ok(); ++it) { lhs += it.getCoef() * masterVars[idxMap[it.getVar()]]; } masterRow.setExpr(lhs); masterRows.add(masterRow); } } // Adjust variable indices in blocks so that reference to variables // in the original problem become references to variables in the master. for (BlockVector::const_iterator b = blocks.begin(); b != blocks.end(); ++b) { for (std::vector<FixData>::iterator it = (*b)->fixed.begin(); it != (*b)->fixed.end(); ++it) it->col = idxMap[problemVars[it->col]]; } // Create the eta variables, one for each block. // See the comments at the top of this file for details about the // eta variables. IloInt const firsteta = masterVars.getSize(); for (BlockVector::size_type i = 0; i < blocks.size(); ++i) { std::stringstream s; s << "_eta" << i; IloNumVar eta(env, 0.0, IloInfinity, s.str().c_str()); masterObj += eta; masterVars.add(eta); } // Create model and solver instance vars = masterVars; rows = masterRows; IloModel model(env); model.add(obj = IloObjective(env, masterObj, problem->getObjSense())); model.add(vars); model.add(rows); cplex = IloCplex(model); cplex.use(cb = new (env) LazyConstraintCallback(env, this, blocks, firsteta)); for (IloExpr::LinearIterator it = obj.getLinearIterator(); it.ok(); ++it) objMap.insert(ObjMap::value_type(it.getVar(), it.getCoef())); }
/** Extract sub block number <code>n</code> from <code>problem</code>. * The constructor creates a representation of block number <code>n</code> * as described in <code>problem</code>. * The constructor will also connect the newly created block to a remote * object solver instance. * @param problem The problem from which the block is to be extracted. * @param n Index of the block to be extracted. * @param argc Argument for IloCplex constructor. * @param argv Argument for IloCplex constructor. * @param machines List of machines to which to connect. If the code is * compiled for the TCP/IP transport then the block will * be connected to <code>machines[n]</code>. */ BendersOpt::Block::Block(Problem const *problem, IloInt n, int argc, char const *const *argv, std::vector<char const *> const &machines) : env(), number(n), vars(0), rows(0), cplex(0), cb(0) { IloNumVarArray problemVars = problem->getVariables(); IloRangeArray problemRanges = problem->getRows(); // Create a map that maps variables in the original model to their // respective index in problemVars. std::map<IloNumVar,IloInt,ExtractableLess<IloNumVar> > origIdxMap; for (IloInt j = 0; j < problemVars.getSize(); ++j) origIdxMap.insert(std::map<IloNumVar,IloInt,ExtractableLess<IloNumVar> >::value_type(problemVars[j], j)); // Copy non-fixed variables from original problem into primal problem. IloExpr primalObj(env); IloNumVarArray primalVars(env); IloRangeArray primalRows(env); IdxMap idxMap; // Index of original variable in block's primal model RowSet rowSet; for (IloInt j = 0; j < problemVars.getSize(); ++j) { IloNumVar x = problemVars[j]; if ( problem->getBlock(x) == number ) { // Create column in block LP with exactly the same data. if ( x.getType() != IloNumVar::Float ) { std::stringstream s; s << "Cannot create non-continuous block variable " << x; std::cerr << s.str() << std::endl; throw s.str(); } IloNumVar v(env, x.getLB(), x.getUB(), x.getType(), x.getName()); // Normalize objective function to 'minimize' double coef = problem->getObjCoef(x); if ( problem->getObjSense() != IloObjective::Minimize ) coef *= -1.0; primalObj += coef * v; // Record the index that the copied variable has in the // block model. idxMap.insert(IdxMap::value_type(x, primalVars.getSize())); primalVars.add(v); // Mark the rows that are intersected by this column // so that we can collect them later. RowSet const &intersected = problem->getIntersectedRows(x); for (RowSet::const_iterator it = intersected.begin(); it != intersected.end(); ++it) rowSet.insert(*it); } else idxMap.insert(IdxMap::value_type(x, -1)); } // Now copy all rows that intersect block variables. for (IloInt i = 0; i < problemRanges.getSize(); ++i) { IloRange r = problemRanges[i]; if ( rowSet.find(r) == rowSet.end() ) continue; // Create a copy of the row, normalizing it to '<=' double factor = 1.0; if ( r.getLB() > -IloInfinity ) factor = -1.0; IloRange primalR(env, factor < 0 ? -r.getUB() : r.getLB(), factor < 0 ? -r.getLB() : r.getUB(), r.getName()); IloExpr lhs(env); for (IloExpr::LinearIterator it = r.getLinearIterator(); it.ok(); ++it) { IloNumVar v = it.getVar(); double const val = factor * it.getCoef(); if ( problem->getBlock(v) != number ) { // This column is not explicitly in this block. This means // that it is a column that will be fixed by the master. // We collect all such columns so that we can adjust the // dual objective function according to concrete fixings. // Store information about variables in this block that // will be fixed by master solves. fixed.push_back(FixData(primalRows.getSize(), origIdxMap[v], -val)); } else { // The column is an ordinary in this block. Just copy it. lhs += primalVars[idxMap[v]] * val; } } primalR.setExpr(lhs); primalRows.add(primalR); lhs.end(); } // Create the dual of the primal model we just created. // Note that makeDual _always_ returns a 'maximize' objective. IloObjective objective(env, primalObj, IloObjective::Minimize); makeDual(objective, primalVars, primalRows, &obj, &vars, &rows); objective.end(); primalRows.endElements(); primalRows.end(); primalVars.endElements(); primalVars.end(); primalObj.end(); // Create a model. IloModel model(env); model.add(obj); model.add(vars); model.add(rows); for (IloExpr::LinearIterator it = obj.getLinearIterator(); it.ok(); ++it) objMap.insert(ObjMap::value_type(it.getVar(), it.getCoef())); // Finally create the IloCplex instance that will solve // the problems associated with this block. char const **transargv = new char const *[argc + 3]; for (int i = 0; i < argc; ++i) transargv[i] = argv[i]; #if defined(USE_MPI) char extra[128]; sprintf (extra, "-remoterank=%d", static_cast<int>(number + 1)); transargv[argc++] = extra; (void)machines; #elif defined(USE_PROCESS) char extra[128]; sprintf (extra, "-logfile=block%04d.log", static_cast<int>(number)); transargv[argc++] = extra; (void)machines; #elif defined(USE_TCPIP) transargv[argc++] = machines[number]; #endif cplex = IloCplex(model, TRANSPORT, argc, transargv); delete[] transargv; // Suppress output from this block's solver. cplex.setOut(env.getNullStream()); cplex.setWarning(env.getNullStream()); }