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?
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");