Как интегрировать выходные данные из обратных вызовов в C ++ / CPLEX - PullRequest
0 голосов
/ 11 июля 2019

Я использовал макро обратные вызовы в моем коде, и я хочу объединить выходные данные вместе. На каждом узле мне нужны значения Pseudocosts, slacks, variableBranch и т. Д. Но я не знаю, как интегрировать эти данные, которые я получаю, с различными обратными вызовами. Я не запускаю все обратные вызовы вместе. Каждый раз, когда я запускаю код с разными обратными вызовами, значения NodeID или Node не равны. Например, на рис. 1 я запускаю BranchCallback для получения Pseudocosts, а на рис. 3 я использую UserCutCallback для получения значений переменных на каждом узле. Как видно на рис. 1, последний узел равен 126, но на рис. 3 последний узел равен 164. Я хочу создать структуру данных в excel для каждого узла, но я не знаю, какое количество узлов мне нужно учитывать? 126 или 164? Например, на рис. 1, могу ли я сказать, что вся информация (значения псевдостанов) об узле 10 принадлежит узлу 10 на рис3? А на рис3 вся информация (значения слабых мест) об узле 10 принадлежит узлу 10 на рис1?

ILOUSERCUTCALLBACK1(Myvalue, IloArray<IloNumVarArray>, vars) {
for (int i = 0; i < nbworkers; ++i) {
for (int j = 0; j < nbmachines; j++) {
cout << "getvalue(" << vars[i][j] << ") = "
<< getValue(vars[i][j]) << endl;
}
}
}

enter image description here enter image description here

1 Ответ

3 голосов
/ 11 июля 2019

Вы спрашиваете много вещей одновременно. Я собираюсь ответить на вопрос, который связан с предметом. Для всего остального, пожалуйста, создайте новый вопрос и покажите ваш код, ваш фактический результат и объясните, чем он отличается от ожидаемого результата.

Класс IloCplex имеет функцию out () . Эта функция возвращает ссылку на поток, в который CPLEX отправляет все свои выходные данные. Вы можете передать эту ссылку своим обратным вызовам, а затем записать в этот поток свои обратные вызовы.

Например:

ILOUSERCUTCALLBACK1(MyCallback, std::ostream &, output) {
  output << "Message from callback" << std::endl;
}

, а затем

cplex.use(MyCallback(cplex.getEnv(), cplex.out()));

для создания и регистрации обратного вызова.

ОБНОВЛЕНИЕ После того, как вы отредактировали свой вопрос, кажется, что ваша проблема не в том, чтобы напечатать вывод из обратного вызова, а что-то еще.

Прежде всего, обратите внимание, что ожидается получение разных путей поиска, если вы выполните один запуск с обратным вызовом пользователя, сокращенным, а другой - с обратным вызовом ветви. Невозможно связать номера узлов или идентификаторы узлов от одного прогона к другому. Статистику, которую вы хотите получить, необходимо получить с помощью один прогон .

Кроме того, для идентификации узла не следует использовать номер узла или количество обработанных узлов (число в самом левом столбце в журнале). Вместо этого вы должны использовать идентификатор узла. Это то, что известно как порядковый номер в C ++ API. Это only вещь, которую вы можете использовать для идентификации узла. Эти идентификаторы должны совпадать с идентификаторами узлов, показанными в правой части журнала, в случае, если динамический поиск отключен (что происходит автоматически, если вы используете контрольные обратные вызовы). Эти идентификаторы узлов доступны во всех обратных вызовах, и вы можете использовать их для сбора информации, собранной из различных обратных вызовов для одного и того же узла.

Обновление 2 : На самом деле я был не прав. Число, возвращаемое getNodeId(), и число, указанное в столбце NodeID журнала, отличаются, см. Мой ответ на на этот вопрос . Нет никакого способа связать эти два числа. Извините за путаницу. Я думаю, что вы задавали подобный вопрос и на другом форуме, и я утверждал, что эти два числа совпадают. Это тоже было неправильно. Снова извините.

Так что, по сути, ваш единственный способ связать вещи в обратном вызове с вещами в журнале - это выполнить однопоточный прогон, а затем посмотреть порядок, в котором печатаются вещи.

Однако, чтобы отследить дерево (наряду с псевдо-стоимостью и т. Д.), Вам не нужен журнал. Вы можете сделать все от обратного вызова, просто используя порядковые номера. Самая сложная часть - это отслеживание родительско-дочерних отношений, что можно сделать так (не то, что это не поточно-безопасный):

struct Parent {
   typedef IloCplex::MIPCallbackI::NodeId NodeId;
   struct Less {
      bool operator()(NodeId const &n1, NodeId const &n2) const {
         return n1._id < n2._id;
      }
   };
   typedef std::map<NodeId,NodeId,Less> MapType;
   MapType parents;
   void set(NodeId child, NodeId parent) { parents[child] = parent; }
   IloCplex::MIPCallbackI::NodeId get(NodeId child) const {
      MapType::const_iterator it = parents.find(child);
      return (it == parents.end()) ? NodeId() : it->second;
   }
};

Parent parent;

ILOBRANCHCALLBACK0(BranchCallback) {
   std::cout << "CALLBACK[B]: " << getNodeId()
             << " (" << parent.get(getNodeId()) << ")" << std::endl;
   int const n = getNbranches();
   for (int i = 0; i < n; ++i) {
      NodeId id = makeBranch(i);
      parent.set(id, getNodeId());
   }
}
...