Decision Optimization

Decision Optimization

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


#Analytics
#DecisionOptimization
#DecisionOptimization
 View Only
Expand all | Collapse all

BranchcallbackI with multiple children

  • 1.  BranchcallbackI with multiple children

    Posted 10/04/13 04:06 AM

    Originally posted by: VKV7_Anulark_Naber


    I am trying to implement my own branching scheme. The model is created using opl. According to the suggestions found in the Forum (Paul Rubin and others) regarding the multiple children and branchcallback, I could figure it out thus far. Please find my codes here in the Attachement.

    At the first Level, I want to branch on variable "x" and at the next Level to branch on variable "y". These varaibles must be mapped with those of opl model. Since I am not a programmer expert, but have sufficient knowledge in the branch and cut, I am not sure if my codes work.

    From the log file, I don't think that the branchcallback instances are cretated at all, although cplex does recognize the existence of branchcallback with the warning message. The log file is also attached.

    Could somebody take a look at the codes, and please give me suggestions if they are correctly implemented or how to. Thanks in advance.


    #CPLEXOptimizers
    #DecisionOptimization


  • 2.  Re: BranchcallbackI with multiple children

    Posted 10/04/13 11:30 AM

    I took a quick look at your code and found some issues. Actually, I wonder why your code does not crash.

    The first and maybe worst issue is that your are using the same instance of BranchInfo for all nodes! Instead of doing that you should use a new instance of that class for each new node. This means that code like this

            BranchInfo *pinfo1 = pinfo;
            pinfo1->level = 1;

            //create two composite children with attached info
            Nid1 = makeBranch(vars1, vals1, dirs1, getObjValue(), pinfo1);
            Nid2 = makeBranch(vars2, vals2, dirs2, getObjValue(), pinfo1);

    should be rewritten to something like

            BranchInfo *pinfo1 = new BranchInfo(*pinfo);
            pinfo1->level = 1;
            BranchInfo *pinfo2 = new BranchInfo(*pinfo);
            pinfo2->level = 1;
            Nid1 = makeBranch(vars1, vals1, dirs1, getObjValue(), pinfo1);
            Nid2 = makeBranch(vars2, vals2, dirs2, getObjValue(), pinfo1);

    Note that if a node has a NodeData registered with it then CPLEX will call that node data's destructor when CPLEX has finished processing the node. So what is pinfo in your code will point to a deleted object once the current node is finished. Since you keep accessing that object I wonder why your code does not crash.

    Another issue is this:

        if (pinfo->level == 2) //let Cplex make branches at 2nd level as usual
            return;

    That means that in some cases you let CPLEX create its default nodes (which is fine) but do not attach any data to the nodes CPLEX creates. That means that

        pinfo = dynamic_cast <BranchInfo *> (getNodeData());// get the current node's attachment, if any

    can easily result in pinfo=0. However, you never check this and always unconditionally access pinfo->level. That should usually produce a crash.

    I am not sure if the atRoot field is handled correctly in your callback, in particular if you invoke IloCplex::solve() multiple times with the same callback instance or if you use multiple threads in the tree search.

    As far as I understand your code, you should at least see output from this line

    cout << pinfo->level <<" "

    at the root node. But I don't see any such output in your log. Can you please add an endl to this output (so that the line is not cached in the output stream) and also add some output to the first line of your callback. Does this stuff get printed? If not then your callback is never invoked. Do you have other callbacks or is the branch callback the only callback?

    It also seems to me as if your code has some problems like uninitialized values that a compiler can easily spot. Please crank up all the warning options of your compiler to see those warnings.

    I did not check if your handling/mapping of OPL variables is correct because I think the first thing to understand is why your callback does not get invoked at all.


    #CPLEXOptimizers
    #DecisionOptimization


  • 3.  Re: BranchcallbackI with multiple children

    Posted 10/07/13 12:06 PM

    Originally posted by: VKV7_Anulark_Naber


    Daniel,

    Thank you very much for your answers. I have to admit that I do not understand how BranchCallBack works. Let's call this BranchCallBack object StartEndBranch and the user info /nodedata is BranchInfo. 

    cplex.use(new StartEndBranch(env, &opl, 0));
    Through the use function, cplex invokes a StartEndBranch object with two parameters: opl and zero. Are these parameters passed everytime a node is considered for branching? Even if it is a composite node with attached info?

    My intention is to create 4 nodes, in the first level make 2 branches with BranchInfo attached to each 1st-level node, which is branched into 2 branches/nodes the second level. BranchInfo stores information on which variables to branch into 4 nodes and at what values for both levels.

    How do I know that the current node is the node that BranchInfo must be recreated/recalculated at to which variables should be branched? Indeed in the second level, I can make 2 branches without BranchInfo for further branching (a change from the previous code), so the branches are created only by fixing variables at certain values in vars and vals.

    I use atRoot similar to Paul's codes in Java, to distinguish the node as to BranchInfo must be created or not.

    In my program, I invoke solve() twice. The first time with node =1, so I can get the LP relaxation info. I suppose Branchcallback is not invoked at this stage until the second solve(). It is also the only callback I use in this program.

    You were right, I could not see the output from this statement cout << pinfo->level <<" "; which I don't know why.

    I noticed that if I do not  let cplex make branches as usual with the return statement, the program crashes.

    As for warnings, I always got these two:

    1>C:\Program Files\IBM ILOG\CPLEX 12.5.1 (64 bits)\opl\include\ilopl/iloenv.h(470): warning C4291: 'void *operator new(size_t,const IloEnv &)' : no matching operator delete found; memory will not be freed if initialization throws an exception

    1> C:\Program Files\IBM ILOG\CPLEX 12.5.1 (64 bits)\opl\include\ilconcert/iloenv.h(1904) : see declaration of 'operator new'

    1>C:\Program Files\IBM ILOG\CPLEX 12.5.1 (64 bits)\opl\include\ilopl/iloenv.h(472): warning C4291: 'void *operator new(size_t,const IloEnv &)' : no matching operator delete found; memory will not be freed if initialization throws an exception
    1>C:\Program Files\IBM ILOG\CPLEX 12.5.1 (64 bits)\opl\include\ilconcert/iloenv.h(1904) : see declaration of 'operator new'

    From the log output in my last email

    I will change the program as you suggested. In the meantime, I would appreciate any other advice, examples, or documentations.

    Anulark

    PS. I tried this main method for StartEndBranch, and the program crashes.

    void main () {return;} ;

    why?


    #CPLEXOptimizers
    #DecisionOptimization


  • 4.  Re: BranchcallbackI with multiple children

    Posted 10/09/13 02:55 PM

    Originally posted by: VKV7_Anulark_Naber


    I would appreciate any advice or feedback on this post, please.

    Ann


    #CPLEXOptimizers
    #DecisionOptimization


  • 5.  Re: BranchcallbackI with multiple children

    Posted 10/10/13 05:29 AM

    If you have trouble understanding how branch callbacks work: did you take a look at the iloadmipex1.cpp and iloadmipex3.cpp examples that come with CPLEX? Those examples use branch callbacks and could help you to understand how things work.

    With respect to

    Through the use function, cplex invokes a StartEndBranch object with two parameters: opl and zero. Are these parameters passed everytime a node is considered for branching? Even if it is a composite node with attached info?

    What you pass to the use function is an instance of class StartEndBranch. The arguments opl and zero are passed to the StartEndBranch constructor to instantiate this class. At each node in the branch and bound tree CPLEX invokes the main() method of the callback instance that was passed to use().

    If you use

    void main () {return;}

    as main() method and you get a crash, does it help to register your callback with this line of code (note the additional '(env)')?

    cplex.use(new (env) StartEndBranch(env, &opl, 0));

    Do you get any meaningful backtrace for the crash? Is it a hard crash or just an uncaught exception.

    Here is a (mostly untested) example code that mimicks a 4-way branch by means of two subsequent binary branches.

    #include <ilcplex/ilocplex.h>
    #include <ilconcert/iloiterator.h>

    /** Branch callback that allows creation of 4-way branches.
     * A 4-way branch is realized as two levels of 2-way branches.
     * In this simple implementation we just use 4-way branches that branch
     * up and down on two variables. More complicated ways of 4-way branches
     * are of course possible.
     */
    class Branch4 : public IloCplex::BranchCallbackI {
       /** Node data that specifies the branch(es) to be taken on the second
        * level of a 4-way branch.
        * Since we use a simple 4-way branching this class is also simple: it
        * just specifies the variables to branch on as well as the branching
        * direction and the bound to use.
        */
       struct Level2 : public IloCplex::MIPCallbackI::NodeData {
          IloNumVar const                 leftVar;    /**< Variable for left branch. */
          IloCplex::BranchDirection const leftDir;    /**< Direction for left branch. */
          double const                    leftBound;  /**< Bound for left branch. */

          IloNumVar const                 rightVar;   /**< Variable for right branch. */
          IloCplex::BranchDirection const rightDir;   /**< Direction for right branch. */
          double const                    rightBound; /**< Bound for right branch. */

          /** Create branching information for a single child.
           */
          Level2(IloNumVar lVar, IloCplex::BranchDirection lDir, double lBound)
             : leftVar(lVar), leftDir(lDir), leftBound(lBound),
               rightVar(0), rightDir(IloCplex::BranchDown), rightBound(IloInfinity)
          {
          }
          /** Create branching information for two children.
           */
          Level2(IloNumVar lVar, IloCplex::BranchDirection lDir, double lBound,
                   IloNumVar rVar, IloCplex::BranchDirection rDir, double rBound)
             : leftVar(lVar), leftDir(lDir), leftBound(lBound),
               rightVar(rVar), rightDir(rDir), rightBound(rBound)
          {
          }
                   
       };

       /** Integer variables in the model.
        * This array is required to select the variables on which we branch.
        */
       IloNumVarArray vars;
    public:
       /** Constructor.
        */
       Branch4(IloEnv env, IloNumVarArray v)
          : IloCplex::BranchCallbackI(env), vars(v)
       {
       }

       /** Function to duplicate this callback.
        * This function is required by the BranchCallbackI super class.
        */
       IloCplex::CallbackI *duplicateCallback() const {
          return new (getEnv()) Branch4(getEnv(), vars);
       }

       /** Function that is invoked by CPLEX on each node.
        * It is responsible for creating new branches. If the function neither
        * explicitly prune()s the node nor explicitly creates at least one
        * branch then CPLEX will use the branches it would have created itself.
        */
       void main() {
          // The objective function estimate for new nodes we create.
          double const estimate = getObjValue();

          // Figure out if we are in a 4-way branching. The intermediate
          // nodes in a 4-way branching have node data attached to them that
          // describe the next level to be created.
          Level2 *const level2 = dynamic_cast<Level2 *>(getNodeData());
          if ( level2 ) {
             // We have a node data object. That means that we are at an
             // intermediate node of a multi-level branch. We just create
             // the branch that is described in the node data object.
             int created = 0;

             std::cout << "Creating second level of 4-way branch." << std::endl;
             if ( level2->leftVar.getImpl() ) {
                // A left-child is specified: create it.
                makeBranch(level2->leftVar, level2->leftBound, level2->leftDir,
                           estimate);
                ++created;
             }
             if ( level2->rightVar.getImpl() ) {
                // A left-child is specified: create it.
                makeBranch(level2->rightVar, level2->rightBound, level2->rightDir,
                           estimate);
                ++created;
             }
             if ( created == 0 ) {
                // No children were specified. That is an exceptional corner
                // case. No children means the node should be pruned.
                prune();
             }
          }
          else {
             // We don't have any node data. That is we need to decide what
             // to do: Either create the branches that CPLEX would create
             // or create a multi-way branching.
             // For this example we just pick a random number and create
             // a multi-way branching if the random number is a multiple of 7.
             if ( (rand() % 7) == 0 ) {
                // We use a very simple strategy to create a 4-way branching:
                // Go through the variables and find the two variables
                // That have the most fractional values. Then we perform all
                // 4 possible up/down branching combinations on those two.
                IloInt v1 = -1, v2 = -1;
                double f1 = 2, f2 = 2;

                IloNumArray vals(getEnv());
                getValues(vals, vars);
                for (IloInt i = 0; i < vars.getSize(); ++i) {
                   double const f = fabs(round(vals[i]) - vals[i]);
                   if ( v1 < 0 ) {
                      v1 = i; f1 = f;
                   }
                   else if ( v2 < 0 ) {
                      if ( f > f1 ) {
                         v2 = v1;
                         f2 = f1;
                         v1 = i;
                         f1 = f;
                      }
                      else {
                         v2 = i;
                         f2 = f;
                      }
                   }
                   else if ( f > f1 ) {
                      v2 = v1;
                      f2 = f1;
                      v1 = i;
                      f1 = f;
                   }
                   else if ( f > f2 ) {
                      v2 = i;
                      f2 = f;
                   }
                }

                if ( v1 >= 0 && v2 >= 0 ) {
                   // Setup information for two-level branches.
                   // On the first level we branch on v1 (this is done by
                   // calling makeBranch()), on the second level we branch
                   // on v2 (this is stored in the node data.
                   std::cout << "Creating a 4 way branch on "
                             << vars[v1] << " (" << vals[v1] << ") and "
                             << vars[v2] << " (" << vals[v2] << ")" << std::endl;
                   makeBranch(vars[v1], floor(vals[v1]), IloCplex::BranchDown, estimate,
                              new Level2(vars[v2], IloCplex::BranchDown, floor(vals[v2]),
                                           vars[v2], IloCplex::BranchUp, ceil(vals[v2])));
                   makeBranch(vars[v1], ceil(vals[v1]), IloCplex::BranchUp, estimate,
                              new Level2(vars[v2], IloCplex::BranchDown, floor(vals[v2]),
                                           vars[v2], IloCplex::BranchUp, ceil(vals[v2])));
                }
                vals.end();
             }
             else {
                // Just do nothing. This will result in creation of the branches
                // that CPLEX would have created.
             }
          }
       }
    };

    /** main() function to test the callback.
     * The function expects model files on the command line.
     */
    int
    main(int argc, char **argv)
    {
       srand(0);

       for (int i = 1; i < argc; ++i) {
          try {
             // Create an IloCplex instance and load the model.
             IloEnv env;
             IloModel model(env);
             IloCplex cplex(model);
             cplex.importModel(model, argv[i]);

             // Now get a list of all integer variables in the model.
             // This list is required for the branch callback.
             IloNumVarArray intVars(env);
             for (IloIterator<IloNumVar> it(env); it.ok(); ++it) {
                IloNumVar v = *it;
                if ( v.getType() != IloNumVar::Float )
                   intVars.add(v);
             }
             std::cout << "Found " << intVars.getSize() << " integer variables."
                       << std::endl;

             // Register an instance of the branch callback with IloCplex.
             cplex.use(new (env) Branch4(env, intVars));

             // Solve the model.
             if ( cplex.solve() )
                std::cout << "Objective: " << cplex.getObjValue() << std::endl;
             else
                std:: cout << "INFEASIBLE" << std::endl;

             env.end();
          } catch (IloException& e) {
             std::cerr << "IloException: " << e << std::endl;
          }
       }
       return 0;
    }

    I hope this helps.


    #CPLEXOptimizers
    #DecisionOptimization


  • 6.  Re: BranchcallbackI with multiple children

    Posted 10/21/13 11:23 AM

    Originally posted by: VKV7_Anulark_Naber


    Hi Daniel,

    your example really helps a lot. My code works to some extent, although I still get a corrupted heap, whose exception I cannot catch in BranchCallbackI. Here are my concrete questions:

    1. What can be a cause of corrupted heap that can occur inside BranchCallbackI?

    2.How can I write NodeId to a file, when getNodeNumber is not a method of BranchCallback in c++API? I cannot find a method to convert nodeid to nodenumber or string like in Java API.

    3. I have two-levels branching (first on x, second on y) = 4 nodes. From the output, it seems that the composite nodes(branched by x in the first level) are not branched immediately based on y (my intention). Are the first level nodes evaluated for its LPs before and put into the active node list waiting to be selected? If then, the branching info on y that I put in NodeData would be useless, since the y values can changed after the first branching. do I understand it correctly? How would I force cplex to make the second level branching immediately?

    4. With BranchCallback, the number of thread is automatically set to 1. Can BranchCallback handle the multiple thread?

    5. When I fix values of binary variables on a branch, how does cplex handle the fixed values? Does it add constraints or fix bounds and how?

    Thanks in advance.


    #CPLEXOptimizers
    #DecisionOptimization


  • 7.  Re: BranchcallbackI with multiple children

    Posted 10/29/13 03:03 PM

    > 1. What can be a cause of corrupted heap that can occur inside BranchCallbackI?
    >
    Many things can cause that. Without looking at your code and the crash report it is hard to make a guess. You should try to run your code through something like valgrind or purify. These tools check all memory accesses and tell you as soon as you do something forbidden (which will eventually result in heap corruption).

    > 2.How can I write NodeId to a file, when getNodeNumber is not a method of BranchCallback in
    > c++API? I cannot find a method to convert nodeid to nodenumber or string like in Java API.
    >
    According to the reference documentation getNodeId() is a member function of class BranchCallback and returns the id of the current node. That should be all you need.

    > 3. I have two-levels branching (first on x, second on y) = 4 nodes. From the output, it seems
    > that the composite nodes(branched by x in the first level) are not branched immediately based
    > on y (my intention). Are the first level nodes evaluated for its LPs before and put into the active
    > node list waiting to be selected? If then, the branching info on y that I put in NodeData would be
    > useless, since the y values can changed after the first branching. do I understand it correctly?
    > How would I force cplex to make the second level branching immediately?
    >
    Yes, I think you are correct. There is no way to force CPLEX to make the second level branch directly. However, I attached a file that implements multi-level branching in a different way. It is more flexible (and should also be thread safe) than what I posted before. In the attached branch callback the nodes created are either one of the nodes you planned to create or nodes that are exactly the same as the original node. So y values will not be useless.

    > 4. With BranchCallback, the number of thread is automatically set to 1. Can BranchCallback
    > handle the multiple thread?
    >
    You can explicitly set IloCplex::IntParam::Threads to a value >1 to get back multi-threaded behavior. The number of cores on a machine can be obtained by IloCplex::getNumCores(). However, when doing multi-threaded callbacks you have to make your callbacks thread safe (they may be invoked in parallel). As far as I can tell the code I attached is thread safe.

    > 5. When I fix values of binary variables on a branch, how does cplex handle the fixed values?
    > Does it add constraints or fix bounds and how?
    >
    CPLEX will update the bounds for those variables.


    #CPLEXOptimizers
    #DecisionOptimization


  • 8.  Re: BranchcallbackI with multiple children

    Posted 10/30/13 01:51 PM

    Originally posted by: VKV7_Anulark_Naber


    Thank you very much. It's working now.

    One questions though. Can we stop and resume branchcallback anytime in the branch and cut process?


    #CPLEXOptimizers
    #DecisionOptimization


  • 9.  Re: BranchcallbackI with multiple children

    Posted 10/30/13 04:45 PM

     

    You cannot disconnect and reconnect the branch callback on the fly, but within the callback  you can always ask CPLEX how it would have branched and then add those branches yourself.

    Paul

     


    #CPLEXOptimizers
    #DecisionOptimization


  • 10.  Re: BranchcallbackI with multiple children

    Posted 10/31/13 04:13 AM

    Originally posted by: VKV7_Anulark_Naber


    Thank you.


    #CPLEXOptimizers
    #DecisionOptimization


  • 11.  Re: BranchcallbackI with multiple children

    Posted 10/31/13 04:24 AM

    Alternatively, you can just do nothing in a branch callback. If you neither create at least one branch nor prune the node then CPLEX will create its default branches.

    There also is function BranchCallbackI::makeBranch(IloInt n, NodeData *data = 0) that creates the n-th CPLEX default branch.


    #CPLEXOptimizers
    #DecisionOptimization


  • 12.  Re: BranchcallbackI with multiple children

    Posted 08/27/14 09:32 AM

    Originally posted by: anahana


    Hi guys, I hope you don't mind me jumping into this discussion.

    I'm running nbranch.cpp file and it seems to be working fine. However, I don't understand how it selects the next node to process. I assume that since it is designed to make several branches from the same node (N), the next node to process should always be N (actually the copy of it) until you reach the end where "there is exactly one BranchInfo left". If this is the case, when are the created children processed? and how can I distinguish the child node from the copy node?

    Regards, 

     


    #CPLEXOptimizers
    #DecisionOptimization


  • 13.  Re: BranchcallbackI with multiple children

    Posted 08/27/14 11:04 AM

    nbranch.cpp does not explicitly select any nodes. It just lets CPLEX decide what node to pick next. In this code, if you create N nodes from a single node then the N nodes are not created immediately. The code only creates the first of them. At this point it also created a fake node F that has information about how to create the remaining N-1 children in its node data. When CPLEX decides to process node F then at this node the first of the remaining N-1 children is created along with a node F' that contains information for the remaining N-2 children. This process is applied recursively when CPLEX decides to process F'.

    In order to distinguish a plain child node from an F node just look at the node's node data. For an F node the node data will always be non-NULL. For a plain child node it will always be NULL.


    #CPLEXOptimizers
    #DecisionOptimization


  • 14.  Re: BranchcallbackI with multiple children

    Posted 08/28/14 08:03 AM

    Originally posted by: anahana


    Thanks, it does make sense now. Is it possible to retrieve, or access, this node data from a nodeCallBack? Things like the branching variable, direction, and/or bounds.

    I tried this:

    ILONODECALLBACK0(temp)
    {
    NodeData *dat = new NodeData();

    for(IloInt i = 0; i < getNremainingNodes(); i++)

    if(getNodeData(i))

    {

    dat = getNodeData(i);

    break;

    }

    }

    During execution, you can see that dat stores the nodeData of node i properly (if it exists), but I only have access to the destructor ~NodeData() and the method getDataType(). 

     

    Regards,


    #CPLEXOptimizers
    #DecisionOptimization


  • 15.  Re: BranchcallbackI with multiple children

    Posted 08/28/14 10:04 AM

    This statement

    NodeData *dat = new NodeData();

    is a memory leak. There is no need to create a new instance of NodeData. In order to get access to the methods of the BranchInfo class you need an instance of that class, of course. So you have to downcast the return value of getNodeData():

    BranchInfo *info = (BranchInfo)getNodeData(i);

    After that you can look at info->vars, info->dirs, info->bnds, etc. Better code would be

    BranchInfo *info = dynamic_cast<BranchInfo *>(getNodeData(i));

    but some compilers require extra flags to get dynamic_cast<>() working correctly.


    #CPLEXOptimizers
    #DecisionOptimization


  • 16.  Re: BranchcallbackI with multiple children

    Posted 08/28/14 10:24 AM

    Originally posted by: anahana


    True, I forgot the delete statement. 

    Thanks for the help, it's working fine now. 

     

    Regards, 


    #CPLEXOptimizers
    #DecisionOptimization


  • 17.  Re: BranchcallbackI with multiple children

    Posted 10/31/14 09:01 AM

    Originally posted by: anahana


    Hi again,

    I am testing the nBranch.cpp code and noticed something strange.

    If I understood correctly, the line: createBranch(VarVector(), BndVector(), DirVector(), estimate, prev); copies the parent because the variable argument sent to createBranch (VarVector()) is empty, correct? The same holds for BndVector and DirVector.

    Here's the situation:

    Suppose I have a linked list of 10 nodes to create from node N (5 up branches and 5 down). I first create a child based on the top entry of the linked list, remove it from the list, and create a copy of N (call it N1) for the remaining 9 branches. Next I choose N1 through a node callback and repeat. I noticed that the values of the variables in N differ from the values of the variables in N1 (and any other copy of N). Why? Aren't the copies supposed to be identical to the parent? The difference is in the variable that was selected to create the child node. It is as if the copy is also branched on the selected variable. 

    Regards,


    #CPLEXOptimizers
    #DecisionOptimization


  • 18.  Re: BranchcallbackI with multiple children

    Posted 11/12/14 09:48 AM

    If I understand correctly you start with a node N and have a list L with branches to create from N. Then you do:

    - create a child C from N by using the information in first(L)
    - removeFirst(L)
    - create a child N1 as a copy of N (without any bound changes or constraints) with L as node data (L is now one element shorter due to the previous step)

    When N1 is processed it looks as if the same branching decisions were applied to this node as for C.

    Did I get this correct? When you check the local bounds of the variables at N1, are they equal to the local bounds at N or equal to the local bounds at C (the latter would indicate that indeed some sort of branching happened).

    Note that it is definitely possible that nBranch.cpp contains bugs. I never tested that thoroughly. It was just meant to illustrate the general strategy.


    #CPLEXOptimizers
    #DecisionOptimization


  • 19.  Re: BranchcallbackI with multiple children

    Posted 11/13/14 06:24 AM

    Originally posted by: anahana


    Yes, you understood correctly what happens. 

    Checking the bounds for BP won't help much, but I did check the variables' values before creating L and after going back to what is supposed to be a copy (N1). They are different. I am attaching a small example code to illustrate. It uses a file form MIPLIB as a model.

    Look at the screen prints for the BASE CASE and the COPY. Ignore the first COPY output since it happens before nodeSelect instructs CPLEX to go to the copy node.

    Maybe I am doing something wrong, I'm not sure, but the code seems fine to me.

    Thanks.


    #CPLEXOptimizers
    #DecisionOptimization


  • 20.  Re: BranchcallbackI with multiple children

    Posted 11/17/14 02:47 AM

    I looked at your code. I could not reproduce your exact problem but I found something similar. What I did was the following: At node N collect the current node-local bounds of all variables and store them in the node data of N1. At node N1 then collect the node-local bounds of all variables and compare them to the ones of N. Sometimes these bounds differ and that of course may imply different solutions at N and N1.

    The reason for this is that between the invocation of the branch callbacks for N and N1 CPLEX does some node-preprocessing for node N1. In some cases this pre-processing is able to tighten bounds or even fix variables.

    Thus, there is no guarantee that you will have the same solutions at N and N1, differences are expected. Is this a problem for your application or just surprising behavior?


    #CPLEXOptimizers
    #DecisionOptimization


  • 21.  Re: BranchcallbackI with multiple children

    Posted 11/17/14 04:35 AM

    Originally posted by: anahana


    Thanks for the explanation. This indeed causes a problem for my application because I can no longer create more than two branches from the same node. Since CPLEX does not allow for more than two calls of the makeBranch function, the "branch and copy" trick in nBranch seemed to do the job. However, we know now that we can't really copy a node. Any ideas what else I can do?

    Regards, 


    #CPLEXOptimizers
    #DecisionOptimization


  • 22.  Re: BranchcallbackI with multiple children

    Posted 11/21/14 03:55 AM

    I am not clear why this is actually a problem. IMO you can still use the code in nbranch.cpp. Why is it a problem that node N1 has tighter bounds than node N? Node N1 does contain any additional branching decisions. The additional bound tightenings were already implicit in N, so conceptually N1 is still just a copy of N.


    #CPLEXOptimizers
    #DecisionOptimization


  • 23.  Re: BranchcallbackI with multiple children

    Posted 11/21/14 04:38 AM

    Originally posted by: anahana


    Yes, you can still use nBranch and CPLEX runs smoothly. That is not an issue.

    As for why N and N1 being different is a problem, well, I am trying to assess different branching variables under similar circumstances, or as similar as possible. For now, this will have to do. I don't know if N1 being different from N will negatively affect what I am trying to do, or positively for that matter, but at least I know now that I need to somehow account for the additional bounds created for N1.

    Thank you. 


    #CPLEXOptimizers
    #DecisionOptimization