Originally posted by: UserCplex
Hello,
I have searched for this in the forum but could not find posts that explicitly detail the components required to have multithreaded MIP solutions with user cut callbacks. Hence, this post.
Before the details, a background of how I develop and solve the MIP in my case.
My MIP, [MIP] is:
Min cx
s.t. Ax = b
x >= 0, binary.
I begin with the LP relaxation of the above, called [LP].
Global variables declared are:
CPXENVptr env;
CPXLPptr lp;
Given a current fractional LP solution, I have a function
bool find_violated_cuts(double* x, int wherefrom, void* cbdata){
}
I have a global variable bool INTO_CB which is true if MIP optimization has started and is false if I am still working with [LP].
I have twelve different class of inequalities numbered 1 through 12, say.
Each one of these inequality classes has a separation routine that takes in the current LP solution and attempts to find violations. So, the structure of the (say) first separation routine is thus:
void sep1(double *x);
So, within find_violated_cuts(), I have:
sep1(x);
...
sep12(x);
As suggested in this thread: https://www.ibm.com/developerworks/community/forums/html/topic?id=cf534d14-c3f1-47e2-84ed-feb44fb66958
all of my data structures have a first dimension [] pertaining to the thread number within which it is read/written so as to avoid race conditions.
So, all of my non-cplex user defined data structures accessed in find_violated_cuts() and subsequently in the sepx() functions are thread safe.
When a violation is found, the appropriate cut is added to [LP] within find_violated_cuts() based on the value of INTO_CB
if (!INTO_CB)
status = CPXaddrows(env, lp, 0, 1, ctr, rhs, sense, rmatbeg, rmatind, rmatval, NULL, conname);
else
CPXcutcallbackadd(env, cbdata, wherefrom, ctr, rhs[0], sense[0], rmatind, rmatval, CPX_USECUT_PURGE);
And the LP is re-solved.
Eventually, when no more violations are found and the [LP]'s solution is fractional, I convert the variables to binary using
int status = CPXchgctype(env, lp, cnt, cons tint * indices, ctype);
//where ctype = 'B';
Once this is done, I give problem [MIP] to be solved to CPLEX via CPXmipopt. INTO_CB is made true. (Hence, CPXcutcallbackadd() will be run within the above function.)
My user cut callback function registered with CPLEX is structured thus:
int CPXPUBLIC LP::mycutcallback(CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p){}
Within this function, I access the following CPLEX functions at different times/places:
CUTINFOptr cutinfo = (CUTINFOptr)cbhandle;
int depth;
status = CPXgetcallbacknodeinfo(env, cbdata, wherefrom, 0, CPX_CALLBACK_INFO_NODE_DEPTH, &depth);
CPXgetcallbacknodex(env, cbdata, wherefrom, cutinfo->x, 0, cutinfo->numcols - 1);
cutinfo->find_violated_cuts(cutinfo->x, wherefrom, cbdata);
My questions specifically are:
Are all of the CPLEX functions that I access in the framework about thread-safe?
In particular, if two threads are solving the LP subproblem at two different nodes of the BB Tree, given the framework above, there are going to be calls to CPXgetcallbacknodeinfo, CPXgetcallbacknodex, and CPXcutcallbackadd from different BB Tree nodes in different threads. Are these thread-safe calls? Is there need for any explicit lock needed around any of the above functions across threads?
Are there any other guidelines/frameworks to follow to develop multithreaded user cut callbacks/lazy cuts/branch callback, etc.?
Are there CPLEX provided examples with source codes that explain the questions above?
Thanks.
#CPLEXOptimizers#DecisionOptimization