0

so far I modelled optimization problems directly in IBM ILOG CPLEX Optimization Studio.

Now, I want to try out using C++ to formulate models while using the CPLEX API.

For this purpose, I started out with a very simple problem, just to learn C++ and to get a feel how to use the CPLEX API.

The problem looks like this in CPLEX:

range j = 1..3;

dvar int+ x[i];
dvar float+ y[j];

maximize -3*x[1] - 2*x[2] + 4*x[3] + y[1] - 3*y[2] - 2*y[3];

subject to {
  c1: -2*x[1] + 4*x[2] - x[3] + 3*y[1] - 2*y[2] + 3*y[3] <= -4;
  c2: -x[1] - 2*x[2] + 4*x[3] + 2*y[1] + 4*y[2] - 5*y[3] <= 2;
  c3: forall(i in i) x[i] <= 6;
}

Now, I simply want to program this same model in C++. Here is my current code:

#include <iostream>
#include "ilcplex//ilocplex.h";
using namespace std;

int main() {

    IloEnv env;
    IloModel Model(env);

    IloIntVar x1(env, 0, IloInfinity, "x1");
    IloIntVar x2(env, 0, IloInfinity, "x2");
    IloIntVar x3(env, 0, IloInfinity, "x3");
    IloNumVar y1(env, 0, IloInfinity, ILOFLOAT, "y1");
    IloNumVar y2(env, 0, IloInfinity, ILOFLOAT, "y2");
    IloNumVar y3(env, 0, IloInfinity, ILOFLOAT, "y3");

    Model.add(IloMaximize(env, -3 * x1 - 2 * x2 + 4 * x3 + y1 - 3 * y2 - 2 * y3));

    Model.add((-2 * x1 + 4 * x2 - x3 + 3 * y1 - 2 * y2 + 3 * y3) <= -4);
    Model.add((-x1 - 2 * x2 + 4 * x3 + 2 * y1 + 4 * y2 - 5 * y3) <= 2);
    Model.add(x1 <= 6);
    Model.add(x2 <= 6);
    Model.add(x3 <= 6);

    IloCplex cplex(Model);

    if (!cplex.solve()) {
        env.error() << "Optimization failed" << endl;
        throw(-1);
    }

    double obj = cplex.getObjValue();
    cout << "\n\n\t objective value: " << obj << endl;  

    cout << "x1 = " << cplex.getValue(x1) << endl;
    cout << "x2 = " << cplex.getValue(x2) << endl;
    cout << "x3 = " << cplex.getValue(x3) << endl;
    cout << "y1 = " << cplex.getValue(y1) << endl;
    cout << "y2 = " << cplex.getValue(y2) << endl;
    cout << "y3 = " << cplex.getValue(y3) << endl;
}

Honestly, I've looked at this for a while now and I can't figure out why these two programs don't yield the same solution.

The objective value of the CPLEX program is 2.429 which is the correct solution. The C++ program prints out optimization failed, meaning that it didn't find a solution.

Does anyone see the mistake I made?

Arctic_Skill
  • 43
  • 1
  • 1
  • 5
  • seems like something that should be asked to `CPLEX` customer service. – Ahmed AEK Aug 11 '23 at 11:31
  • what results do you get? How do they differ? – 463035818_is_not_an_ai Aug 11 '23 at 12:27
  • Give the actual values of your variables from your output, not just the value of the objective function. Are you requiring that all variables are positive in the second code? – lastchance Aug 11 '23 at 13:14
  • Yes, all the variables should be positive. x1,x2,x3 should be integer values and y1,y2,y3 can be float numbers. the optimal solution obtained in CPLEX looks like this: x1 = 4, x2 = 0, x3 = 5 y1 = 0.14, y2 = 0, y3 = 2.86 objective value = 2.429 – Arctic_Skill Aug 11 '23 at 13:28

2 Answers2

1

Interesting - I tried this with CPLEX 20.1 and VS2022 on Windows as I needed to refresh my C++ CPLEX stuff anyway. I agree that your code as above is reported as infeasible, but a small change as below then seems to solve fine:

IloIntVar x1(env, 0, 6, "x1");
IloIntVar x2(env, 0, 6, "x2");
IloIntVar x3(env, 0, 6, "x3");
IloNumVar y1(env, 0, IloInfinity, ILOFLOAT, "y1");
IloNumVar y2(env, 0, IloInfinity, ILOFLOAT, "y2");
IloNumVar y3(env, 0, IloInfinity, ILOFLOAT, "y3");

Model.add(IloMaximize(env, -3 * x1 - 2 * x2 + 4 * x3 + y1 - 3 * y2 - 2 * y3));

Model.add((-2 * x1 + 4 * x2 - x3 + 3 * y1 - 2 * y2 + 3 * y3) <= -4);
Model.add((-x1 - 2 * x2 + 4 * x3 + 2 * y1 + 4 * y2 - 5 * y3) <= 2);
//Model.add(x1 <= 6);
//Model.add(x2 <= 6);
//Model.add(x3 <= 6);

i.e. the change is to set the bounds of those x variables to 6 when declaring them rather than separately via constraints. Here is the output log of the revised model:

Version identifier: 20.1.0.0 | 2020-11-10 | 9bedb6d68
Found incumbent of value -6.000000 after 0.00 sec. (0.00 ticks)
Tried aggregator 1 time.
Reduced MIP has 2 rows, 6 columns, and 12 nonzeros.
Reduced MIP has 0 binaries, 3 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.00 sec. (0.00 ticks)
Tried aggregator 1 time.
Reduced MIP has 2 rows, 6 columns, and 12 nonzeros.
Reduced MIP has 0 binaries, 3 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.00 sec. (0.00 ticks)
MIP emphasis: balance optimality and feasibility.
MIP search method: dynamic search.
Parallel mode: deterministic, using up to 16 threads.
Root relaxation solution time = 0.00 sec. (0.00 ticks)

        Nodes                                         Cuts/
   Node  Left     Objective  IInf  Best Integer    Best Bound    ItCnt     Gap

*     0+    0                           -6.0000                           0.00%
*     0+    0                           -1.9524                           0.00%
      0     0        4.0000     1       -1.9524        4.0000        3  304.88%
*     0+    0                            1.9231        4.0000           108.00%
*     0+    0                            2.2857        4.0000            75.00%
      0     0        2.5000     2        2.2857    MIRcuts: 1        5    9.37%
*     0+    0                            2.4286        2.5000             2.94%
      0     0        cutoff              2.4286                      5     ---
Elapsed time = 0.05 sec. (0.05 ticks, tree = 0.01 MB, solutions = 5)

Mixed integer rounding cuts applied:  1

Root node processing (before b&c):
  Real time             =    0.05 sec. (0.05 ticks)
Parallel b&c, 16 threads:
  Real time             =    0.00 sec. (0.00 ticks)
  Sync time (average)   =    0.00 sec.
  Wait time (average)   =    0.00 sec.
                          ------------
Total (root+branch&cut) =    0.05 sec. (0.05 ticks)


         objective value: 2.42857
x1 = 4
x2 = -0
x3 = 5
y1 = 0.142857
y2 = 0
y3 = 2.85714

I expected that this should generate a very similar model after CPLEX' presolve and give the same results, but it clearly does something different. I'd report this to IBM for further investigation.

Edit: More digging. It looks like it doesn't like declaring the IloIntVars with an upper bound of IloInfinity. If I export the model as an LP file, I get unexpected bounds on those variables, setting them all to zero:

\ENCODING=ISO-8859-1
\Problem name: IloCplex

Maximize
 obj1: - 3 x1 - 2 x2 + 4 x3 + y1 - 3 y2 - 2 y3
Subject To
 c1: - 2 x1 + 4 x2 - x3 + 3 y1 - 2 y2 + 3 y3 <= -4
 c2: - x1 - 2 x2 + 4 x3 + 2 y1 + 4 y2 - 5 y3 <= 2
 c3: x1 <= 6
 c4: x2 <= 6
 c5: x3 <= 6
Bounds
      x1 = 0
      x2 = 0
      x3 = 0
Generals
 x1  x2  x3 
End

If I change the declarations to the following, it seems to work fine:

IloNumVar x1(env, 0, IloInfinity, ILOINT, "x1");
IloNumVar x2(env, 0, IloInfinity, ILOINT, "x2");
IloNumVar x3(env, 0, IloInfinity, ILOINT, "x3");
TimChippingtonDerrick
  • 2,042
  • 1
  • 12
  • 13
  • Thank you for doing such thorough investigations! Yes, it was indeed the IloInfinity on the integer variables that caused the problem. Often times it's just the small details that you overlook. – Arctic_Skill Aug 17 '23 at 07:48
1

With IloIntVar you should use IloIntMax as the upper bound and not IloInfinity

Alex Fleischer
  • 9,276
  • 2
  • 12
  • 15