ILOG CPLEX 11.0 User's Manual > Infeasibility and Unboundedness > Repairing Infeasibilities with FeasOpt > Example: FeasOpt in Concert Technology

The following examples show you how to use FeasOpt. These fragments of code are written in Concert Technology for C++ users, but the same principles apply to the other APIs as well. The examples begin with a model similar to one that you have seen repeatedly in this manual.

   IloEnv env;
   try {
      IloModel model(env);
      IloNumVarArray x(env);
      IloRangeArray con(env);
      IloNumArray vals(env);
      IloNumArray infeas(env);

      x.add(IloNumVar(env, 0.0, 40.0));
      x.add(IloNumVar(env));
      x.add(IloNumVar(env));
      
      model.add(IloMaximize(env, x[0] + 2 * x[1] + 3 * x[2]));
      con.add( - x[0] +     x[1] + x[2] <=  20);
      con.add(   x[0] - 3 * x[1] + x[2] <=  30);
      con.add(   x[0] +     x[1] + x[2] >= 150);
      model.add(con);

If you extract that model and solve it, by means of the following lines, you find that it is infeasible.

      IloCplex cplex(model);
      cplex.exportModel("toto.lp");
      cplex.solve();
      if ( cplex.getStatus() == IloAlgorithm::Infeasible ||
           cplex.getStatus() == IloAlgorithm::InfeasibleOrUnbounded ) {
           env.out() << endl << "*** Model is infeasible ***" << endl << endl;

Now the following lines invoke FeasOpt to locate a feasible solution:

        // begin feasOpt analysis
        
        cplex.setOut(env.getNullStream());
        IloNumArray lb(env);
        IloNumArray ub(env);
        
        // first feasOpt call
        
        env.out() << endl << "*** First feasOpt call ***" << endl;
        env.out() << "*** Consider all constraints ***" << endl;
        int rows = con.getSize();
        lb.add(rows, 1.0);
        ub.add(rows, 1.0);
        
        if ( cplex.feasOpt(con, lb, ub) ) {
        env.out() << endl;
        cplex.getInfeasibilities(infeas,con);
        env.out() << "*** Suggested bound changes = " << infeas << endl;
        env.out() << "*** Feasible objective value would be = "
               << cplex.getObjValue() << endl;
        env.out() << "Solution status    = " << cplex.getStatus() << endl;
        env.out() << "Solution obj value = " << cplex.getObjValue() << endl;
        cplex.getValues(vals, x);
        env.out() << "Values             = " << vals << endl;
        env.out() << endl;
        }
        else {
        env.out() << "*** Could not repair the infeasibility" << endl;
        throw (-1);
        }

The code first turns off logging to the screen by the optimizers, simply to avoid unnecessary output. It then allocates arrays lb and ub, to contain the preferences as input. The preference is set to 1.0 for all three constraints in both directions to indicate that any change to a constraint range will be permitted.

Then FeasOpt is called. If the FeasOpt call succeeds, then several lines of output give the results. Here is the output:

*** First feasOpt call ***
*** Consider all constraints ***

*** Suggested bound changes = [50, -0, -0]
*** Feasible objective value would be = 50
Solution status    = Infeasible
Solution obj value = 50
Values             = [40, 30, 80]

There are several items of note in this output. First, you see that FeasOpt recommends only the first constraint to be modified, namely, by increasing its lower bound by 50 units.

The solution values of [40, 30, 80] would be feasible in the modified form of the constraint, but not in the original form. This situation is reflected by the fact that the solution status has not changed from its value of Infeasible. In other words, this change to the righthand side (RHS) of the constraint is only a suggestion from FeasOpt; the model itself has not changed, and the proposed solution is still infeasible in it.

To get a more concrete idea, assume that this constraint represents a limit on a supply, and assume further that increasing the supply to 70 is not practical. Now rerun FeasOpt, not allowing this constraint to be modified, like this:

// second feasOpt call
        
env.out() << endl << "*** Second feasOpt call ***" << endl;
env.out() << "*** Consider all but first constraint ***" << endl;
        
lb[0]=ub[0]=0.0;
        
if ( cplex.feasOpt(con, lb, ub) ) {
  env.out() << endl;
  cplex.getInfeasibilities(infeas,con);
  env.out() << "*** Suggested bound changes = " << infeas << endl;
  env.out() << "*** Feasible objective value would be = "
            << cplex.getObjValue() << endl;
  env.out() << "Solution status    = " << cplex.getStatus() << endl;
  env.out() << "Solution obj value = " << cplex.getObjValue() << endl;
  cplex.getValues(vals, x);
  env.out() << "Values             = " << vals << endl; 
  env.out() << endl;
  }
else {
  env.out() << "*** Could not repair the infeasibility" << endl;
  throw (-1);
  }

Those lines disallow any changes to the first constraint by setting lb[0]=ub[0]=0.0. FeasOpt runs again, and here are the results of this second run:

*** Second feasOpt call ***
*** Consider all but first constraint ***
*** Suggested bound changes = [-0, -0, -50]
*** Feasible objective value would be = 50
Solution status    = Infeasible
Solution obj value = 50
Values             = [40, 17.5, 42.5]

Notice that the projected maximal objective value is quite different from the first time, as are the optimal values of the three variables. This solution was completely unaffected by the previous call to FeasOpt. This solution also is infeasible with respect to the original model, as you would expect. (If it had been feasible, you would not have needed FeasOpt in the first place.) The negative suggested bound change of the third constraint means that FeasOpt suggests decreasing the upper bound of the third constraint by 50 units, tranforming this constraint:

x[0] +     x[1] + x[2] >= 150

into

x[0] +     x[1] + x[2] >= 100

That second call changed the range of a constraint. Now consider changes to the bounds.

 // third feasOpt call
         
 env.out() << endl << "*** Third feasOpt call ***" << endl;
 env.out() << "*** Consider all bounds ***" << endl;
         
 // re-use preferences - they happen to be right dimension
 lb[0]=ub[0]=1.0;
 lb[1]=ub[1]=1.0;
 lb[2]=ub[2]=1.0;
         
 if ( cplex.feasOpt(x, lb, ub) ) {
    env.out() << endl;
    cplex.getInfeasibilities(infeas,x);
    env.out() << "*** Suggested bound changes = " << infeas << endl;
    env.out() << "*** Feasible objective value would be = "
              << cplex.getObjValue() << endl;
    env.out() << "Solution status    = " << cplex.getStatus() << endl;
    env.out() << "Solution obj value = " << cplex.getObjValue()<< endl;
    cplex.getValues(vals, x);
    env.out() << "Values             = " << vals << endl;
    env.out() << endl;
    }
 else {
      env.out() << "*** Could not repair the infeasibility" << endl;
      throw (-1);
      }

In those lines, all six bounds (lower and upper bounds of three variables) are considered for possible modification because a preference of 1.0 is set for each of them. Here is the result:

*** Third feasOpt call ***
*** Consider all bounds ***

*** Suggested bound changes = [25, 0, 0]
*** Feasible objective value would be = 25
Solution status    = Infeasible
Solution obj value = 25
Values             = [65, 30, 55]

Those results suggest modifying only one bound, the upper bound on the first variable. And just as you might expect, the solution value for that first variable is exactly at its upper bound; there is no incentive in the weighted penalty function to set the bound any higher than it has to be to achieve feasibility.

Now assume for some reason it is undesirable to let this variable have its bound modified. The final call to FeasOpt changes the preference to achieve this effect, like this:

 // fourth feasOpt call
         
 env.out() << endl << "*** Fourth feasOpt call ***" << endl;
 env.out() << "*** Consider all bounds except first ***" << endl;
 lb[0]=ub[0]=0.0;
         
 if ( cplex.feasOpt(x, lb, ub) ) {
    env.out() << endl;
    cplex.getInfeasibilities(infeas,x);
    env.out() << "*** Suggested bound changes = " << infeas << endl;
    env.out() << "*** Feasible objective value would be = "
              << cplex.getObjValue() << endl;
    env.out() << "Solution status    = " << cplex.getStatus() << endl;
    env.out() << "Solution obj value = " << cplex.getObjValue() << endl;
    cplex.getValues(vals, x);
    env.out() << "Values             = " << vals << endl;
    env.out() << endl;
 }
 else {
    env.out() << "*** Could not repair the infeasibility" << endl;
    throw (-1);
 }         

Then after the fourth call of FeasOpt, the output to the screen looks like this:

*** Fourth feasOpt call ***
*** Consider all bounds except first ***
*** Could not repair the infeasibility
Unknown exception caught

This is a correct outcome, and a more nearly complete application should catch this exception and handle it appropriately. FeasOpt is telling the user here that no modification to the model is possible under this set of preferences: only the bounds on the last two variables are permitted to change according to the preferences expressed by the user, and they are already at [0,+inf], so the upper bound can not increase, and no negative value for the lower bounds would ever improve the feasibility of this model. Not every infeasibility can be repaired, and an application calling FeasOpt will usually need to take this possibility into account.