cplexstudent cplexstudent - 1 year ago 171
C++ Question

c++ cplex access current solution to add constraint

i am trying to solve a LP-model in CPLEX using C++ and Concert Technology.

I want to implement constraints (the subtour elimination constraints, to be more specific) that needs to query the value of two of my variables in the current solution:
The variable array xvar is indicating the edges, yvar is representing the nodes.

I implement these constraints by solving n (= number of nodes) Min-Cut-Problems on a modified graph, which is constructed by adding an artificial source and an artifical sink and connect these to every node of the original graph.

From what i've read so far, do i need a lazy constraint or a callback or none of this?

This is where i create the model and get it solved, access the values of the variables in the solution etc:

// Step 2: Construct the necessary CPLEX objects and the LP model
IloCplex solver(env);

std::cout<< "Original Graph g: " <<std::endl;
std::cout<< net.g() <<std::endl;
MCFModel model(env, net);

// Step 3: Load the model into cplex and solve

// Step 4: Extract the solution from the solver
if(solver.getStatus() != IloAlgorithm::Optimal) throw "Could not solve to optimality!";
IloNumArray xsol ( env, net.g().nEdges() );
IloNumArray ysol ( env, net.g().nNodes() );
IloNumArray rsol ( env, net.g().nGroups() );
IloNumArray wisol ( env, net.g().nGroups() );
IloNum ksol;
NumMatrix wsol ( env, net.g().nGroups());

for(IloInt i = 0; i < net.g().nGroups(); i++){
wsol[i] = IloNumArray( env, net.g().nGroups() );

solver.getValues(xsol, model.xvar());
solver.getValues(ysol, model.yvar());
solver.getValues(rsol, model.rvar());
solver.getValues(wisol, model.wivar());

for (IloInt i = 0; i < net.g().nGroups(); i++){
wsol[i] = wisol;

// Step 5: Print the solution.

The constraint, i need the current values of the variables xvar and yvar for, is created here:

//build subset constraint y(S) -x(E(S))>= y_i
void MCFModel::buildSubsetCons(){
IloExpr lhs(m_env);
IloCplex cplex(m_env);
IloNumArray xtemp ( m_env, m_net.g().nEdges() );
IloNumArray ytemp ( m_env, m_net.g().nNodes() );

std::vector<Edge> mg_twin;
std::vector<int> mg_weights;
int mg_s;
int mg_t;
SGraph mgraph;
std::vector<int> f;
int nOrigEdges = m_net.g().nEdges();
int nOrigNodes = m_net.g().nNodes();

cplex.getValues(xtemp, m_xvar);
cplex.getValues(ytemp, m_yvar);

mgraph = m_net.g().mod_graph();
mg_s = mgraph.nNodes()-1;
mg_t = mgraph.nNodes();

std::cout<<"modified graph:"<<std::endl;

// fill the weight of original edges with 1/2*x_e
foreach_edge(e, m_net.g()){
// fill the weight of the edges from artificial source with zero
for(int i=0; i<m_net.g().nNodes(); i++){

// fill the weight of the edges to artificial sink with f(i)
// first step: calculate f(i):
foreach_node(i, m_net.g()){
foreach_adj_edge(e, i, m_net.g()){
f[i] = f[i] + xtemp[e->idx()];
f[i] = (-1)*f[i]/2;
f[i] = f[i] + ytemp[i];
// second step: fill the weights vector with it
for(int i=0; i<m_net.g().nNodes(); i++){

// calculate the big M = abs(sum_(i in N) f(i))
int M;
foreach_node(i, m_net.g()){
M = M + abs(f[i]);

// Build the twin vector of the not artificial edges for mgraph
mg_twin.resize(2*nOrigEdges + 2*nOrigNodes);

for(int i=0; i < nOrigEdges ; ++i){
mg_twin[i] = mgraph.edges()[nOrigEdges + i];
mg_twin[nOrigEdges + i] = mgraph.edges()[i];

//Start the PreflowPush for every node in the original graph
foreach_node(v, m_net.g()){
// "contract" the edge between s and v
// this equals to higher the weights of the edge (s,v) to a big value M
// weight of the edge from v to s lies in mg_weights[edges of original graph + index of node v]
mg_weights[m_net.g().nEdges() + v] = M;

//Start PreflowPush for v
PreflowPush<int> pp(mgraph, mg_twin, mg_weights, mg_s, mg_t);
std::cout << "Flowvalue modified graph: " << pp.minCut() << std::endl;

The Object pp is to solve the Min-Cut-Problem on the modified graph mgraph with artificial source and sink. The original graph is in m_net.g().

When i compile and run it, i get the following error:

terminate called after throwing an instance of 'IloCplex::Exception'

It seems to me, that it is not possible to access the values of xvar and yvar like this?
I do appreciate any help since i am quite lost how to do this.
Thank you very much!!

Answer Source

Two things...

I. I strongly suggest you to use a try-catch to better understand CPLEX Exceptions. You could perhaps understand the nature of the exception like this. As a matter of fact, I suggest you a try-catch-catch setting, sort of:

try {
//... YOUR CODE ...//
catch(IloException& e) {
    cerr << "CPLEX found the following exception: " << e << endl;
catch(...) {
    cerr << "The following unknown exception was found: " << endl;

II. The only way to interact with CPLEX during the optimization process is via a Callback, and, in the case of Subtour Elimination Constraints (SECs) you will need to separate both integer and fractional SECs.

II.1 INTEGER: The first one is the easiest one, an O(n) routine would help you identify all the connected components of a node solution, then you could add the subsequent cuts to prevent this particular SEC from appearing in other nodes. You could either enforce your cuts locally, i.e. only on the current sub-tree, using the addLocal() function, or globally, i.e. on the entire Branch-and-Cut tree, using the add() function. In any case, ALWAYS remember to add .end() to terminate the cut container. Otherwise you WILL have serious memory leak issues, trust me with this, lol. This callback needs to be a done via a Lazy Constraint Callback (ILOLAZYCONSTRAINTCALLBACK)

II.2 FRACTIONAL: The second one is by far more complex. The easiest way to make it work is to use Professor Lysgaard's CVRPSEP library. It is nowadays most efficient way of computing capacity cuts, Multistar, generalized multistar, framed capacity, strengthened comb and hypotour cuts. Additionally, is rather easy to link with any existing code. The linkage also needs to be embedded on the solution process, hence, a callback is also required. In this case, it would be a User Cut Callback (ILOUSERCUTCALLBACK).

One is glad to be of service Y