Here is a tiny "tutorial" how to use the feasopt approach directly from the IDE. It is build on the opl/examples/opl/prodmilp example shipped with CPLEX.
The idea is that you relax all constraints and try to find a solution that minimizes violation of the constraints.
The original model is this
{string} Products = ...;
{string} Resources = ...;
{string} Machines = ...;
float MaxProduction = ...;
tuple typeProductData {
float demand;
float incost;
float outcost;
float use[Resources];
string machine;
}
typeProductData Product[Products] = ...;
float Capacity[Resources] = ...;
float RentCost[Machines] = ...;
dvar boolean Rent[Machines];
dvar float+ Inside[Products];
dvar float+ Outside[Products];
minimize
sum( p in Products )
( Product[p].incost * Inside[p] +
Product[p].outcost * Outside[p] ) +
sum( m in Machines )
RentCost[m] * Rent[m];
subject to {
forall( r in Resources )
ctCapacity:
sum( p in Products )
Product[p].use[r] * Inside[p] <= Capacity[r];
forall( p in Products )
ctDemand:
Inside[p] + Outside[p] >= Product[p].demand;
forall( p in Products )
ctMaxProd:
Inside[p] <= MaxProduction * Rent[Product[p].machine];
}
In order to apply this relaxation approach we first create a data file phase.dat with this content
findFeasible = [0];
and add that file to the project and its run configuration. We will modify the .mod file so that we can either solve the original model (findFeasible[0] = 0) or the relaxed model (findFeasible[0] = 1).
To this end we add the following block to the data definition of the model:
// Data and variables related to finding a first feasible solution.
// We have a one-element array findFeasible[] that tells us what model we are solving.
// If findFeasible[0] is 0 then we want to solve the original model. If findFeasible[0]
// is 1 then we want to solve a relaxation of the model that is aimed at finding a
// (first) feasible solution. In the phase.dat file the value of findFeasible[0] is
// set to 0 so that by default we solve the original model.
// In addition we have a continuous non-negative variable for each constraint we want
// to relax when going for a first feasible solution.
int findFeasible[0..0] = ...;
dvar float+ relaxCtCapacity[Resources];
dvar float+ relaxCtDemand[Products];
dvar float+ relaxCtMaxProd[Products];
and modify the objective function as follows:
minimize
// The objective function consists of two parts: The original objective function and
// the sum of constraint violations. If findFeasible[0] is 0 then only the first part
// is active, if findFeasible[0] is 1 then only the second part is active.
(1 - findFeasible[0]) * (
// Original objective function
sum( p in Products )
( Product[p].incost * Inside[p] +
Product[p].outcost * Outside[p] ) +
sum( m in Machines )
RentCost[m] * Rent[m]
) +
findFeasible[0] * (
// Sum of violation of constraints.
sum(r in Resources) relaxCtCapacity[r] +
sum(p in Products) relaxCtDemand[p] +
sum(p in Products) relaxCtMaxProd[p]
);
This is essentially two completely different objective functions, depending on the value of findFeasible[0]. In case findFeasible[0] is 0, no relaxation of constraints is allowed, so we add the following constraints:
// If we are not looking for a first feasible solution then constraint
// violation is not allowed and all the variables indicating a violation
// are fixed to 0.
if ( findFeasible[0] == 0) {
forall (r in Resources) relaxCtCapacity[r] == 0;
forall (p in Products) relaxCtDemand[p] == 0;
forall (p in Products) relaxCtMaxProd[p] == 0;
}
Finally, we add the new variables to the constraints in the original model so that these constraints can be relaxed in case findFeasible[0] = 1 (in case findFeasible[0] = 0 all variables are fixed to 0 and no relaxation is possible):
forall( r in Resources )
ctCapacity:
sum( p in Products )
Product[p].use[r] * Inside[p] <= Capacity[r] + relaxCtCapacity[r];
forall( p in Products )
ctDemand:
Inside[p] + Outside[p] >= Product[p].demand - relaxCtDemand[p];
forall( p in Products )
ctMaxProd:
Inside[p] <= MaxProduction * Rent[Product[p].machine] + relaxCtMaxProd[p];
With all this in place, we have to do the following:
-
Solve the relaxed model.
-
If we find a solution with objective value 0 then this solution does not violate any constraints. In that case we can install that solution as objective for the original model.
-
Solve the original model.
We do all that by adding the following scripting block to the end of the .mod file:
main {
thisOplModel.generate();
// Create a copy of the original model. In that copy set findFeasible[0] to 1
// so that the model aims at finding a first feasible solution.
// Solve that model.
var def = thisOplModel.modelDefinition;
var dat = thisOplModel.dataElements;
var feasCplex = new IloCplex();
var feasOpl = new IloOplModel(def, feasCplex);
dat.findFeasible[0] = 1;
feasOpl.addDataSource(dat);
feasOpl.generate();
writeln("Solve model to find a (first) feasible solution ...");
feasCplex.solve();
// The objective in the solved model is the sum of constraint violations. If
// that is 0 then no constraints are violated and we have found a feasible
// solution. We can use that solution as a MIP start for the original model.
var objval = feasCplex.getObjValue();
writeln("Objective after feasopt: " + objval);
if ( Opl.abs(objval) < 1e-6 ) {
writeln("-> feasible solution found");
var start = new IloOplCplexVectors();
start.attach(thisOplModel.Rent, feasOpl.Rent.solutionValue);
start.setStart(cplex);
}
else {
writeln("-> no feasible solution found");
}
// Release memory acquired for the copied model.
feasOpl.end();
feasCplex.end();
// Now solve the original model with the MIP start installed above.
writeln("Solve original model ...");
cplex.solve();
writeln("Objective for real model: " + cplex.getObjValue());
thisOplModel.postProcess();
0;
}
I am also attaching the relevant files here.
(for the probe parameter I am not aware of any deprecation, what makes you think it is deprecated?)
#CPLEXOptimizers#DecisionOptimization