为什么在NodeCallback中没有一些NodeID

时间:2019-07-28 20:00:30

标签: visual-c++ cplex

我从BranchCallback获得了NodeID(getNodeID)和Parent,并从NodeCallback获得了可变分支。当我绘制树搜索算法时,某些节点在这些节点之前没有任何变量分支,并且当我看到VS C ++的控制台时,没有那些NodeId的记录。但是我确定BranchCallback中有很多信息,例如那些节点的目标值。为什么?enter image description here

1 个答案:

答案 0 :(得分:1)

请注意(如我在另一篇文章中所述),控制台日志中显示的NodeID与您在回调中获得的NodeID不同。因此,这两件事是无法关联的。

然后,对于您要询问的其他内容,您不清楚如何获取任何数据。特别是,如果您在分支回调中收集节点,然后仅稍后在节点回调中计算我们的分支变量,则此方法可能不适用于已修剪的节点。为什么不从分支回调中也获得分支变量呢? 这是一个简单的示例,该怎么做:

#include <map>
#include <limits>
#include <iostream>
#include <ilcplex/ilocplex.h>
#include <ilconcert/ilothread.h>

using std::cout;
using std::endl;

/** The info we collect for a node. */
struct Info {
   IloCplex::MIPCallbackI::NodeId id;        /**< The node's id. */
   IloCplex::MIPCallbackI::NodeId parent;    /**< ID of the parent (root is 0). */
   IloNumVar                      branchVar; /**< The variable on which CPLEX branched
                                              * to create this node. */
   IloNum                         branchVal; /**< The value CPLEX used for branching. */
   IloCplex::BranchDirection      branchDir; /**< The direction into which CPLEX branched. */
};

// Overload operator< so that we can use NodeIds as keys in maps.
bool operator<(IloCplex::MIPCallbackI::NodeId const &n1,
               IloCplex::MIPCallbackI::NodeId const &n2)
{
   return n1._id < n2._id;
}

// Map node ids to Info objects so that you can easily get from the info
// for a child to the info of its parent.
IloFastMutex lck;
typedef std::map<IloCplex::MIPCallbackI::NodeId,Info> MapType;
MapType nodeMap;

// A simple branch callback that tracks CPLEX branching decisions.
ILOBRANCHCALLBACK0(BranchCallback) {
   IloInt const n = getNbranches();
   IloCplex::MIPCallbackI::NodeId me = getNodeId();
   IloNumVarArray x(getEnv());
   IloNumArray bounds(getEnv());
   IloCplex::BranchDirectionArray dirs(getEnv());
   for (IloInt b = 0; b < n; ++b) {
      // Get what CPLEX plans to do and perform the exact same branch.
      // Then record all the information abouth the newly created branch.
      getBranch(x, bounds, dirs, b);
      Info info;
      info.id = makeBranch(b);
      info.parent = me;
      if ( x.getSize() == 1 ) {
         info.branchVar = x[0];
         info.branchVal = bounds[0];
         info.branchDir = dirs[0];
      }
      else {
         // CPLEX branches on more than one variable. We don't record that.
         info.branchVar = 0;
         info.branchVal = std::numeric_limits<double>::quiet_NaN();
         info.branchDir = IloCplex::BranchGlobal;
      }
      lck.lock();
      nodeMap.insert(MapType::value_type(info.id, info));
      lck.unlock();
   }
   dirs.end();
   bounds.end();
   x.end();
}

int
main(int argc, char **argv) {
   for (int a = 1; a < argc; ++a) {
      IloEnv env;
      IloModel model(env);
      IloCplex cplex(model);
      cplex.importModel(model, argv[a]);
      cplex.use(BranchCallback(env));
      nodeMap.clear();
      cplex.setParam(IloCplex::Threads, cplex.getNumCores());
      //cplex.setParam(IloCplex::Threads, 1);
      //cplex.setParam(IloCplex::NodeLim, 10);
      cplex.solve();

      // Print information about all the nodes.
      for (MapType::const_iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) {
         Info const &i = it->second;
         cout << "Node " << i.id << " created from " << i.parent
              << " by branching "
              << (i.branchDir == IloCplex::BranchUp ? "up" : "down")
              << " on " << (i.branchVar.getImpl() ? i.branchVar.getName() : "more than one variable")
              << " with value " << i.branchVal
              << endl;
      }
      env.end();
   }
   return 0;
}

如果您还想跟踪选择节点的顺序,请添加一个字段

   IloInt order;

转到Info类,并使用像这样的节点回调:

// A simple node callback that tracks the order in which nodes are selected.
ILONODECALLBACK0(NodeCallback) {
   // The order that CPLEX plans to execute next is at index 0.
   IloCplex::MIPCallbackI::NodeId next = getNodeId(0);
   lck.lock();
   static IloInt order = 0;
   nodeMap[next].order = order++;
   lck.unlock();
}

(除了简单的计数器,您还可以使用时间戳或其他方式)。