在回调(CPXcutcallbackadd)中添加用户剪切后,如何编写问题公式(CPXwriteprob)

How to write problem formulation (CPXwriteprob) after user cut is added in callback (CPXcutcallbackadd)?

本文关键字:何编写 问题 CPXwriteprob CPXcutcallbackadd 回调 添加 用户      更新时间:2023-10-16

我正试图在cplex回调函数中添加用户剪切。为了检查这些剪切,我在添加剪切后编写了一个lp文件。但是,lp文件中没有显示用户剪切。

根据这个论坛帖子(网址:http://www.ibm.com/developerworks/community/forums/html/topic?id=82b92bee-4ac7-41dd-b1fc-606eae3514f3),剪切应该以"u"开头。

我的代码的相关部分(基于示例文件bendersatsp.c):

int CPXPUBLIC benders_solver::benders_callback(CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p)
{
int status = 0;
int do_separate = 0;
USER_CBHANDLE *user_cbhandle = (USER_CBHANDLE *)cbhandle;
int purgeable;  
*useraction_p = CPX_CALLBACK_DEFAULT;
/* Decide if we want to separate cuts, depending on the parameter wherefrom */
switch (wherefrom) {
case CPX_CALLBACK_MIP_CUT_FEAS: // 115
do_separate = 1;
break;
case CPX_CALLBACK_MIP_CUT_LAST: // 114: indicates that CPLEX is done adding cuts and the user has a last chance to add cuts
do_separate = 1;
break;
case CPX_CALLBACK_MIP_CUT_LOOP: // 106: The callback was called from the cut loop CPLEX executes at each node.
do_separate = 0;
break;
default:
fprintf(stderr, "Unexpected value of wherefrom: %dn", wherefrom);
do_separate = 0;
}
if (!do_separate) goto TERMINATE;
/* Get the current x solution */
status = CPXgetcallbacknodex(env, cbdata, wherefrom, user_cbhandle->x, 0, user_cbhandle->num_x_cols - 1);
if (status) {
fprintf(stderr, "Error in CPXgetcallbacknodex: status = %dn", status);
goto TERMINATE;
}
CPXCLPptr lp_p;
status = CPXgetcallbacklp(env, cbdata, wherefrom, &lp_p); // store master problem to lp_p
changeSub_d(user_cbhandle, user_cbhandle->subenv, user_cbhandle->subproblem_d); 
/* Solve the worker LP and look for a violated cut */
CPXsetintparam(user_cbhandle->subenv, CPX_PARAM_PREIND, 0);
int optstatus = CPXlpopt(user_cbhandle->subenv, user_cbhandle->subproblem_d);
worker_lp_sol_stat = CPXgetstat(user_cbhandle->subenv, user_cbhandle->subproblem_d);
/* Make cut */
int matind[21000] = { 0 };              
double matval[21000] = { 0 };
double rhs[1];
char sense[1];
int nbnz{ 0 };      
status = addBendersCut(user_cbhandle, worker_lp_sol_stat, env, lp_p, cbdata, wherefrom);
int purgeable = CPX_USECUT_FORCE;
status = CPXcutcallbackadd(env, cbdata, wherefrom, nbnz, rhs[0], sense[0], matind, matval, purgeable);
status = CPXwriteprob(masterenv, masterproblem, "cback.lp", "LP");

/* Tell CPLEX that cuts have been created */
if (status)
goto TERMINATE;
*useraction_p = CPX_CALLBACK_SET;
TERMINATE:
/* If an error has been encountered, we fail */
if (status) *useraction_p = CPX_CALLBACK_FAIL;
return status;
} /* END benders_callback */

bool benders_solver::startSolve()
{
loadGeneralData(sol);
// Initialize the CPLEX environment 
masterenv = CPXopenCPLEX(&status);
subenv = CPXopenCPLEX(&status);
loadMasterProblem(); 
loadSubProblem(); 
USER_CBHANDLE user_cbhandle;
int separate_fractional_solutions = 1;
/* Init the cut callback data structure */
status = init_user_cbhandle(&user_cbhandle, separate_fractional_solutions);
/* Let MIP callbacks work on the original model */
status = CPXsetintparam(env, CPXPARAM_MIP_Strategy_CallbackReducedLP, CPX_OFF);
status = CPXsetintparam(env, CPX_PARAM_PRELINEAR, 0);
status = CPXsetintparam(env, CPX_PARAM_MIPCBREDLP, 0);
status = CPXsetintparam(env, CPXPARAM_Threads, 1);  
/* Turn on traditional search for use with control callbacks */
status = CPXsetintparam(env, CPXPARAM_MIP_Strategy_Search,      CPX_MIPSEARCH_TRADITIONAL); 
if (user_cbhandle->separate_fractional_solutions) {
status = CPXsetusercutcallbackfunc(env, benders_callback, user_cbhandle);
}
/* Optimize the problem and obtain solution status */
status = CPXsetintparam(masterenv, CPX_PARAM_PREIND, 0);
status = CPXsetdblparam(masterenv, CPX_PARAM_TILIM, 300);
status = CPXsetstrparam(masterenv, CPXPARAM_WorkDir, "c:/cplex/");
status = CPXsetintparam(masterenv, CPXPARAM_MIP_Strategy_File, 2);
status = CPXsetdblparam(masterenv, CPXPARAM_WorkMem, 6000);
status = CPXsetintparam(masterenv, CPX_PARAM_MEMORYEMPHASIS, 1); 
status = CPXmipopt(masterenv, masterproblem);
status = CPXsetlogfile(masterenv, NULL);
return true;
}

当您编写LP时,它看起来像是在使用原始模型对象(即masterproblem):

status = CPXwriteprob(masterenv, masterproblem, "cback.lp", "LP");

相反,您需要使用节点LP,如下所示:

CPXLPptr _lp;
status = CPXgetcallbacknodelp(env, cbdata, wherefrom, &_lp);
CPXwriteprob(env, _lp, "cback.lp", "LP");

正如你链接到的论坛帖子中提到的:

CPXcutcallbackadd()函数不使用参数来提供名称。在cback.lp文件中,您添加的剪切的名称将以带"u",后跟一个数字。所以你可以找到哪些行是剪切是你添加的,但你无法轻松地将它们映射回事物在您的代码中

另一个值得一提的要点(为了澄清我在评论中提到的内容)是,在回调之外,当使用CPXwriteprob时,您的剪切将不会显示(即,在回调中的树搜索过程中动态添加的任何剪切都不会导出)。但是,如果在优化之前使用CPXaddusercuts添加用户剪切,则它们会。

另一个与您类似的问题可以在developerWorks论坛上找到。