Decision Optimization

Decision Optimization

Delivers prescriptive analytics capabilities and decision intelligence to improve decision-making.

 View Only
  • 1.  Branch Callback function usage in cpp (concert technology)

    Posted Thu May 14, 2015 05:05 PM

    Originally posted by: MuberraOzmen


    Hello,

    We are constructing the integer L shaped method for a two stage stochastic program. Actually what we are trying to do is generating an optimality cut using incumbent callback, adding this cut by cut callback and then generating a child node with this new cut. We stuck in using branch callback. We would be very thankful if anyone who have performed something similar to this and know how to use branchcall back can help us. This is our code:

     

    #include <ilcplex/ilocplex.h>
    #include <stdlib.h>
    #include <math.h>
     
    ILOSTLBEGIN
     
    IloEnv env;
     
    typedef IloArray<IloNumArray> TwoDMatrix;
    typedef IloArray<TwoDMatrix> ThreeDMatrix;
    //typedef IloArray<ThreeDMatrix> FourDMatrix;
     
    typedef IloArray<IloNumVarArray> TwoDVariable;
    typedef IloArray<TwoDVariable> ThreeDVariable;
    typedef IloArray<ThreeDVariable> FourDVariable;
    typedef IloArray<FourDVariable> FiveDVariable;
     
    //IloInt numCuts = 0;
     
    int NumATMs;
    int NumCenters;
    int NumScenarios;
    /*float L=0;
    int Period = 5;
    float Capacity;
    float cR = 10;
    float cO = 20;
    int addcut;*/
     
    IloNumArray prob;
    TwoDMatrix Distance;
    ThreeDMatrix Scenario;
     
    /*IloNum  tetaR;
    IloNum zbar = IloInfinity;
    IloExpr A;
    int Sr = 0;*/
     
     
    ILOINCUMBENTCALLBACK1(OptimalityCutGeneration, TwoDVariable, x, IloNumVar, teta) {
     
    IloNum Oldteta;
    TwoDMatrix OldX;
     
     
    try{
    IloEnv env = getEnv();
     
    Oldteta = getValue(teta);
     
    for (int i = 0; i < NumATMs; i++) {
    for (int j = 0; j < NumCenters; j++) {
    OldX[i][j] = getValue(x[i][j]);
    Sr = Sr + OldX[i][j];
    }
    }
     
     
    for (int i = 0; i < NumATMs; i++) {
    for (int j = 0; j < NumCenters; j++) {
    if (OldX[i][j]>0)
    {
    A += x[i][j];
    }
    else
    {
    A += -x[i][j];
    }
    }
    }
    A.end();
     
    //Subproblem solution//
    IloEnv subenv;
    IloModel SubProblem(subenv);
     
    FiveDVariable y(subenv, NumATMs);
    for (int i = 0; i < NumATMs; i++){
    y[i] = FourDVariable(subenv, NumATMs);
    for (int j = 0; j < NumATMs; j++){
    y[i][j] = ThreeDVariable(subenv, Period);
    for (int t = 0; t < Period; t++){
    y[i][j][t] = TwoDVariable(subenv, NumCenters);
    for (int k = 0; k < NumCenters; k++){
    y[i][j][t][k] = IloNumVarArray(subenv, NumScenarios, 0, 1, ILOINT);
    }
    }
    }
    }
     
    ThreeDVariable O(subenv, NumCenters);
    for (int k = 0; k < NumCenters; k++){
    O[k] = TwoDVariable(subenv, Period);
    for (int t = 0; t < Period; t++){
    O[k][t] = IloNumVarArray(subenv, NumScenarios, 0, IloInfinity, ILOFLOAT);
    }
    }
     
    IloExpr ExpCost;
     
    for (int g = 0; g < NumScenarios ; g++){
    for (int t = 0; t < Period; t++){
    for (int i = 0; i < NumATMs; i++){
    for (int j = 0; j < NumATMs; j++){
    for (int k = 0; k < NumCenters; k++){
    ExpCost += prob[g] * cR*Distance[i][j] * y[i][j][t][k][g];
    }
    }
    }
    }
    }
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int k = 0; k < NumCenters; k++){
    ExpCost += prob[g]*cO*O[k][t][g];
    }
    }
    }
    IloObjective Obj_Sub = IloMinimize(subenv, ExpCost);
     
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int i = 0; i < NumATMs; i++){
    IloExpr Const1;
    for (int j = 0; j < NumATMs; j++){
    for (int k = 0; k < NumCenters; k++){
    Const1 += y[i][j][t][k][g];
    }
    }
    SubProblem.add(Const1 == Scenario[i][t][g]);
    Const1.end();
    }
    }
    }
     
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int j = 0; j < NumATMs; j++){
    IloExpr Const2;
    for (int i = 0; i < NumATMs; i++){
    for (int k = 0; k < NumCenters; k++){
    Const2 += y[i][j][t][k][g];
    }
    }
    SubProblem.add(Const2 == Scenario[j][t][g]);
    Const2.end();
    }
    }
    }
     
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int j = 0; j < NumATMs; j++){
    for (int i = 0; i < NumATMs; i++){
    for (int k = 0; k < NumCenters; k++){
    SubProblem.add(y[i][j][t][k][g] <= OldX[i][k]);
    }
    }
    }
    }
    }
     
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int j = 0; j < NumATMs; j++){
    for (int i = 0; i < NumATMs; i++){
    for (int k = 0; k < NumCenters; k++){
    SubProblem.add(y[i][j][t][k][g] <= OldX[j][k]);
    }
    }
    }
    }
    }
     
    for (int g = 0; g < NumScenarios; g++){
    for (int t = 0; t < Period; t++){
    for (int k = 0; k < NumCenters; k++){
    IloExpr Const3;
    for (int i = 0; i < NumATMs; i++){
    for (int j = 0; j < NumATMs; j++){
    Const3 += Distance[i][j]*y[i][j][t][k][g];
    }
    }
    SubProblem.add(Const3 - O[k][t][g] <= Capacity);
    Const3.end();
    }
    }
    }
     
    //add subtour elimination constraints
     
    IloCplex cplex(subenv);
    cplex.extract(SubProblem);
    cplex.exportModel("subproblem.lp");
    cplex.solve();
     
    tetaR = cplex.getValue(Obj_Sub);
     
    if (tetaR < zbar){
    zbar = tetaR;
    }
     
     
     
    if (Oldteta>=tetaR)
    {
    addcut = 0;
     
    }
    else
    {
    addcut = 1;
    }
     
    }
    catch (IloException &e){
    cerr << "Error in callback: " << e << endl;
    throw;
    }
    }
     
    ILOUSERCUTCALLBACK5(OptimalityCutAddition, int, addcut, IloNum, tetaR, float, L, IloExpr, A, int, Sr){
     
    if (addcut){
    add(teta >= (tetaR - L) * A - (tetaR - L)*(Sr - 1) + L);
    } //else yazılması gerekebilir
    }
     
     
    ILOBRANCHCALLBACK1(ChildNodeGenaration, int, addcut){
     
    if (addcut){
     
    makeBranch(const )
    //makeBranch; hiç bilmiyoruz nasıl kullanıldığını
     
    } //else yazılması gerekebilir
     
    }
     
    int main(int argc, char **data) {
     
     
    ////////Initialization///////
     
    int NumATMs=0, NumCenters=0, NumScenarios=0;
    TwoDMatrix Distance(env);
     
    //////Data file Reading/////
     
    const char* filename = "datafile.txt";
     
    if (argc >1)
     
    filename = data[1];
     
    ifstream file(filename);
     
    if (!file) {
     
    cerr << "ERROR: could not open file '" << filename << "' for reading" << endl;
     
    cerr << "usage:   " << data[0] << " <file>" << endl;
     
    throw(-1);
     
    }
     
    file >> NumATMs >> NumCenters >> NumScenarios ;
    file.close();
     
    printf("*\n");
     
     
     
     
    const char* filename2 = "Distance.txt";
     
    if (argc >1)
     
    filename = data[1];
     
    ifstream file2(filename2);
     
    if (!file2) {
     
    cerr << "ERROR: could not open file '" << filename2 << "' for reading" << endl;
     
    cerr << "usage:   " << data[0] << " <file2>" << endl;
     
    throw(-1);
     
    }
     
     
    for (int i = 0; i < NumATMs; i++)
    {
    for (int j = 0; j < NumATMs; j++)
    {
    file2 >> Distance[i][j];
    }
    }
    file2.close();
    /*printf("%d\n", Distance[6][4]);*/
     
     
    //Distance okunamadı, senaryolar okunmadı
     
    env.out() << "NumATMs: " << NumATMs << endl;
    env.out() << "NumCenters: " << NumCenters << endl;
    env.out() << "NumScenarios: " << NumScenarios << endl;
     
    printf("NumATMs: %d ",NumATMs);
    printf("NumCenters: %d ", NumCenters);
    printf("NumScenarios: %d ", NumScenarios);
    printf("*\n");
     
    //////First Stage Decision Variables/////
    IloNumVar teta(env, 0, IloInfinity, ILOFLOAT);
    TwoDVariable x(env, NumATMs);
     
     
    for (int i = 0; i < NumATMs; i++)
    {
    x[i] = IloNumVarArray(env, NumCenters, 0, 1, ILOINT);
    }
     
    ///////Initial RMP//////////
     
    IloModel RMP(env);
    IloObjective Obj = IloMinimize(env, teta);
    RMP.add(Obj);
    for (int i = 0; i < NumATMs; i++) {
    IloExpr assign_ATMs(env);
    for (int j = 0; j < NumCenters; j++){
    assign_ATMs += x[i][j];
    }
    RMP.add(assign_ATMs == 1);
    assign_ATMs.end();
    }
     
     
    IloCplex cplex(RMP);
    /*cplex.use(OptimalityCutGeneration(env, x, teta));
    cplex.use(OptimalityCutAddition(env, addcut, tetaR, L, A, Sr));
    cplex.use(ChildNodeGenaration(env, addcut));*/
     
     
    cplex.extract(RMP);
     
    cplex.exportModel("RMP_Model.lp");
     
    cplex.solve();
     
     
     
     
     
    double a, b, c;
    a = cplex.getValue(x[5][0]);
    b = cplex.getValue(x[5][1]);
    printf("%f\n", a);
    printf("%f\n", b);
     
     
    env.end();
    printf("*");
    return 0;
     
     
    }
     

    #CPLEXOptimizers
    #DecisionOptimization


  • 2.  Re: Branch Callback function usage in cpp (concert technology)

    Posted Mon May 18, 2015 12:39 AM

    Could you please tell us more explicitly what exactly your problem is?

    Also, using a combination of incumbent and branch callback looks overly complicated at first glance. I think you may be able to achieve the same by just using a lazy constraint callback. That may also simplify coding a lot: You could move into the lazy constraint callback the code that checks the incumbent and is currently in the incumbent callback. Then, if you detect the incumbent to be infeasible, you could directly inject the violated constraint from the lazy constraint callback. No need to use more than one callback here.

    You may want to take a look at the ilobendersatsp.cpp example shipped with CPLEX. That uses a lazy constraint callback to implement Benders' decomposition and may serve as a good example how to do lazy constraints.


    #CPLEXOptimizers
    #DecisionOptimization


  • 3.  Re: Branch Callback function usage in cpp (concert technology)

    Posted Wed May 27, 2015 11:41 AM

    Originally posted by: MuberraOzmen


    Thank you for your answer. First let me tell more about the problem.

    We have a two stage stochastic program. At first stage we are solving an assignment problem, assigning some ATMs to centers. The centers have vehicles to visit ATMs. Those ATMs may ask for a visit from centers they are assigned. So in the second stage we are routing the vehicles of each center for five days depending on the demand scenarios.

    In the attachment I am sending our code again which is more neat. Actually, as I stated before, our problem is about the usage of integer L shaped method. What we are trying to do is stopping the branch and bound tree of first stage problem when an integer feasible solution is found by incumbent callback. Then in the incumbent function solving the second stage and controlling whether or not the current node is optimal with respect to second stage. If it is not, we are generating a constraint for first stage problem. We want to add this cut using cutcallback and then by using branch callback generating a child node from the current node that would be feasible with this new added constraint. 

    The problem is not being able to continue optimization once incumbent callback function is called. That is we were never able to go into cut callback an branchcallback functions. 


    #CPLEXOptimizers
    #DecisionOptimization


  • 4.  Re: Branch Callback function usage in cpp (concert technology)

    Posted Fri May 29, 2015 04:42 AM

    OK, I still think that you can get away with a lazy constraint callback. A lazy constraint callback is invoked whenever CPLEX finds an integer feasible solution (it is invoked right before the incumbent callback). In such a callback you can do the following (which is similar to your current incumbent callback implementation):

    ILOLAZYCONSTRAINTCALLBACK1(OptimalityCutGeneration, TwoDVariable, x, IloNumVar, teta) {
     
       IloNum Oldteta;
       TwoDMatrix OldX;
     
     
       try{
           // Everything here is exactly as in your current code
           ...
           //add subtour elimination constraints
     
          IloCplex cplex(subenv);
          cplex.extract(SubProblem);
          cplex.exportModel("subproblem.lp");
          cplex.solve();
     
          tetaR = cplex.getValue(Obj_Sub);
     
          if (tetaR < zbar){
             zbar = tetaR;
          }
     
     
     
          if (Oldteta>=tetaR)
          {
             // Nothing to do here.
          }
          else
          {
              // This is different to your current code: Add a constraint to CPLEX
              add(teta >= (tetaR - L) * A - (tetaR - L)*(Sr - 1) + L);
          }
     
       }
       catch (IloException &e){
          cerr << "Error in callback: " << e << endl;
          throw;
       }
    }
    

    The interesting thing here is this line

    add(teta >= (tetaR - L) * A - (tetaR - L)*(Sr - 1) + L);

    The lazy constraint callback allows you to inject an additional constraint that CPLEX will add to the model for the rest of the tree search. You will not have to explicitly branch on that constraint, CPLEX will take care of everything automatically.

    In case you want to stick with your three callbacks: Your problem is how to communicate data (the cuts) between the different callbacks. In order to do that you would have to use node user data which can be set and queried using the callbacks setNodeData() and getNodeData() functions. What you would have to do is the following:

    1. In the incumbent callback, if you notice that the current node is infeasible, then store all the information required by the cut and branch callback as node data using setNodeData().
    2. In the cut and branch callback check whether getNodeData() returns a non-NULL pointer. If that is the case then the current node was infeasible and you have to create a cut or branch.

    Here is a small skeleton code to illustrate what I described:

    struct Cut : public IloCplex::MIPCallbackI::NodeData {
       IloNum theta;
       // You probably need more fields
       ...
    };
    
    ILOINCUMBENTCALLBACK1(OptimalityCutGeneration, TwoDVariable, x, IloNumVar, teta) {
       ...
       if (Oldteta>=tetaR)
       {
          // nothing
       }
       else
       {
          // A cut must be added. Create a new instance of Cut and store in
          // that all data the cut and branch callback need to create the cut.
          Cut *cut = new Cut();
          cut->theta = ...;
          setNodeData(cut);
       }   
    }
    
    ILOUSERCUTCALLBACK5(OptimalityCutAddition, int, addcut, IloNum, tetaR, float, L, IloExpr, A, int, Sr){
       Cut *const cut = dynamic_cast<Cut *>(getNodeData());
       if ( cut ) {
          // There is a cut to be added.
          // Create the cut from the data in *cut and add it.
          IloExpr lhs = ...;
          IloNum rhs = ...;
          add(lhs <= rhs);
       }
    }
     
     
    ILOBRANCHCALLBACK1(ChildNodeGenaration, int, addcut){
       Cut *const cut = dynamic_cast<Cut *>(getNodeData());
       if ( cut ) {
          // There is a cut to be added. Create a single child node that has
          // this cut.
          IloExpr lhs = ...;
          IloNum rhs = ...;
          makeBranch(lhs <= rhs, getObjValue());
       }
       else {
          // No cut to be generated. Don't do anything. This will just create
          // the same branches that CPLEX would have created on its own.
       }
    }
    

    Note that the incumbent callback may be invoked more than once per node, so your node data should probably not be a single cut but rather a list of cuts.


    #CPLEXOptimizers
    #DecisionOptimization