static int CPXPUBLIC usersetbranch (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int brtype, CPXDIM sos, int nodecnt, CPXDIM bdcnt, const CPXDIM *nodebeg, const CPXDIM *indices, const char *lu, const double *bd, const double *nodeest, int *useraction_p) { int status = 0; CPXDIM j, bestj = -1; CPXDIM cols; double maxobj = -CPX_INFBOUND; double maxinf = -CPX_INFBOUND; double xj_inf; double xj_lo; double objval; double *x = NULL; double *obj = NULL; int *feas = NULL; char varlu[1]; double varbd[1]; CPXCNT seqnum1, seqnum2; CPXCLPptr lp; /* Initialize useraction to indicate no user action taken */ *useraction_p = CPX_CALLBACK_DEFAULT; /* If CPLEX is choosing an SOS branch, take it */ if ( sos >= 0 ) return (status); /* Get pointer to the problem */ status = CPXXgetcallbacklp (env, cbdata, wherefrom, &lp); if ( status ) { fprintf (stdout, "Can't get LP pointer.\n"); goto TERMINATE; } cols = CPXXgetnumcols (env, lp); if ( cols <= 0 ) { fprintf (stdout, "Can't get number of columns.\n"); status = CPXERR_CALLBACK; goto TERMINATE; } /* Get solution values and objective coefficients */ x = malloc (cols * sizeof (*x)); obj = malloc (cols * sizeof (*obj)); feas = malloc (cols * sizeof (*feas)); if ( x == NULL || obj == NULL || feas == NULL ) { fprintf (stdout, "Out of memory."); status = CPXERR_CALLBACK; goto TERMINATE; } status = CPXXgetcallbacknodex (env, cbdata, wherefrom, x, 0, cols-1); if ( status ) { fprintf (stdout, "Can't get node solution."); goto TERMINATE; } status = CPXXgetcallbacknodeobjval (env, cbdata, wherefrom, &objval); if ( status ) { fprintf (stdout, "Can't get node objective value."); goto TERMINATE; } status = CPXXgetobj (env, lp, obj, 0, cols-1); if ( status ) { fprintf (stdout, "Can't get obj."); goto TERMINATE; } status = CPXXgetcallbacknodeintfeas (env, cbdata, wherefrom, feas, 0, cols-1); if ( status ) { fprintf (stdout, "Can't get variable feasible status for node."); goto TERMINATE; } /* Branch on var with largest objective coefficient among those with largest infeasibility */ for (j = 0; j < cols; j++) { if ( feas[j] == CPX_INTEGER_INFEASIBLE ) { xj_inf = x[j] - floor (x[j]); if ( xj_inf > 0.5 ) xj_inf = 1.0 - xj_inf; if ( xj_inf >= maxinf && (xj_inf > maxinf || fabs (obj[j]) >= maxobj) ) { bestj = j; maxinf = xj_inf; maxobj = fabs (obj[j]); } } } /* If there weren't any eligible variables, take default branch */ if ( bestj < 0 ) { goto TERMINATE; } /* Now set up node descriptions */ xj_lo = floor (x[bestj]); /* Up node */ varlu[0] = 'L'; varbd[0] = xj_lo + 1; status = CPXXbranchcallbackbranchbds (env, cbdata, wherefrom, 1, &bestj, varlu, varbd, objval, NULL, &seqnum1); if ( status ) goto TERMINATE; /* Down node */ varlu[0] = 'U'; varbd[0] = xj_lo; status = CPXXbranchcallbackbranchbds (env, cbdata, wherefrom, 1, &bestj, varlu, varbd, objval, NULL, &seqnum2); if ( status ) goto TERMINATE; /* Set useraction to indicate a user-specified branch */ *useraction_p = CPX_CALLBACK_SET; TERMINATE: free_and_null ((char **) &x); free_and_null ((char **) &obj); free_and_null ((char **) &feas); return (status); } /* END usersetbranch */
static int CPXPUBLIC mycutcallback (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) { int status = 0; CUTINFOptr cutinfo = (CUTINFOptr) cbhandle; CPXDIM numcols = cutinfo->numcols; CPXDIM numcuts = cutinfo->num; double *x = cutinfo->x; CPXNNZ *beg = cutinfo->beg; CPXDIM *ind = cutinfo->ind; double *val = cutinfo->val; double *rhs = cutinfo->rhs; CPXDIM *cutind = NULL; double *cutval = NULL; double cutvio; int addcuts = 0; CPXDIM i, j; CPXNNZ k; CPXDIM cutnz; *useraction_p = CPX_CALLBACK_DEFAULT; status = CPXXgetcallbacknodex (env, cbdata, wherefrom, x, 0, numcols-1); if ( status ) { fprintf(stderr, "Failed to get node solution.\n"); goto TERMINATE; } for (i = 0; i < numcuts; i++) { cutvio = -rhs[i]; k = beg[i]; cutnz = beg[i+1] - k; cutind = ind + k; cutval = val + k; for (j = 0; j < cutnz; j++) { cutvio += x[cutind[j]] * cutval[j]; } /* Use a cut violation tolerance of 0.01 */ if ( cutvio > 0.01 ) { status = CPXXcutcallbackadd (env, cbdata, wherefrom, cutnz, rhs[i], 'L', cutind, cutval, 1); if ( status ) { fprintf (stderr, "Failed to add cut.\n"); goto TERMINATE; } addcuts++; } } /* Tell CPLEX that cuts have been created */ if ( addcuts > 0 ) { *useraction_p = CPX_CALLBACK_SET; } TERMINATE: return (status); } /* END mycutcallback */
static int CPXPUBLIC 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; /* Data structures to add the Benders' cut */ CPXDIM k, nzcnt, cur_x_col, cur_v_col, cur_u_col; int worker_lp_sol_stat, sense, purgeable; double rhs; double eps_ray = 1e-03; *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: do_separate = 1; break; case CPX_CALLBACK_MIP_CUT_LAST: do_separate = 1; break; case CPX_CALLBACK_MIP_CUT_LOOP: do_separate = 0; break; default: fprintf (stderr, "Unexpected value of wherefrom: %d\n", wherefrom); do_separate = 0; } if( !do_separate ) goto TERMINATE; /* Get the current x solution */ status = CPXXgetcallbacknodex (env, cbdata, wherefrom, user_cbhandle->x, 0, user_cbhandle->num_x_cols-1); if ( status ) { fprintf (stderr, "Error in CPXXgetcallbacknodex: status = %d\n", status); goto TERMINATE; } /* Update the objective function in the worker LP: minimize sum(k in V0) sum((i,j) in A) x(i,j) * v(k,i,j) - sum(k in V0) u(k,0) + sum(k in V0) u(k,k) */ for (k = 1; k < user_cbhandle->num_nodes; ++k) { for (cur_x_col = 0; cur_x_col < user_cbhandle->num_x_cols; ++cur_x_col) { user_cbhandle->indices[cur_x_col] = (k-1) * user_cbhandle->num_x_cols + cur_x_col; } status = CPXXchgobj(user_cbhandle->env, user_cbhandle->lp, user_cbhandle->num_x_cols, user_cbhandle->indices, user_cbhandle->x); if ( status ) { fprintf (stderr, "Error in CPXXchgobj: status = %d\n", status); goto TERMINATE; } } /* Solve the worker LP and look for a violated cut A violated cut is available iff worker_lp_sol_stat == CPX_STAT_UNBOUNDED */ status = CPXXprimopt (user_cbhandle->env, user_cbhandle->lp); if ( status ) { fprintf (stderr, "Error in CPXXprimopt: status = %d\n", status); goto TERMINATE; } worker_lp_sol_stat = CPXXgetstat (user_cbhandle->env, user_cbhandle->lp); if ( worker_lp_sol_stat != CPX_STAT_UNBOUNDED) goto TERMINATE; /* Get the violated cut as an unbounded ray of the worker LP */ status = CPXXgetray (user_cbhandle->env, user_cbhandle->lp, user_cbhandle->ray); if ( status ) { fprintf (stderr, "Error in CPXXgetray: status = %d\n", status); goto TERMINATE; } /* Compute the cut from the unbounded ray. The cut is: sum((i,j) in A) (sum(k in V0) v(k,i,j)) * x(i,j) >= sum(k in V0) u(k,0) - u(k,k) */ nzcnt = 0; for (cur_x_col = 0; cur_x_col < user_cbhandle->num_x_cols; ++cur_x_col) { user_cbhandle->cutind[nzcnt] = cur_x_col; user_cbhandle->cutval[nzcnt] = 0.; for (k = 1; k < user_cbhandle->num_nodes; ++k) { cur_v_col = (k-1) * user_cbhandle->num_x_cols + cur_x_col; if ( user_cbhandle->ray[cur_v_col] > eps_ray ) { user_cbhandle->cutval[nzcnt] += user_cbhandle->ray[cur_v_col]; } } if ( user_cbhandle->cutval[nzcnt] > eps_ray ) { ++nzcnt; } } sense = 'G'; rhs = 0.; for (k = 1; k < user_cbhandle->num_nodes; ++k) { cur_u_col = user_cbhandle->num_v_cols + (k-1) * user_cbhandle->num_nodes; if ( fabs (user_cbhandle->ray[cur_u_col]) > eps_ray ) { rhs += user_cbhandle->ray[cur_u_col]; } cur_u_col = user_cbhandle->num_v_cols + (k-1) * user_cbhandle->num_nodes + k; if ( fabs (user_cbhandle->ray[cur_u_col]) > eps_ray ) { rhs -= user_cbhandle->ray[cur_u_col]; } } purgeable = CPX_USECUT_FORCE; /* With this choice of the purgeable parameter, the cut is added to the current relaxation and it cannot be purged. Note that the value CPX_USECUT_FILTER is not allowed if Benders' cuts are added as lazy constraints (i.e., if wherefrom is CPX_CALLBACK_MIP_CUT_LOOP or CPX_CALLBACK_MIP_CUT_LAST). Possible values and meaning of the purgeable parameter are illustrated in the documentation of CPXcutcallbackadd */ /* Add the cut to the master ILP */ status = CPXXcutcallbackadd (env, cbdata, wherefrom, nzcnt, rhs, sense, user_cbhandle->cutind, user_cbhandle->cutval, purgeable); if ( status ) { fprintf (stderr, "Error in CPXXcutcallbackadd: status = %d\n", status); goto TERMINATE; } /* Tell CPLEX that cuts have been created */ *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 */
static int CPXPUBLIC mycutcallback (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) { int status = 0; CUTINFOptr cutinfo = (CUTINFOptr) cbhandle; CPXDIM numcols = cutinfo->numcols; CPXDIM numcuts = cutinfo->num; double *x = cutinfo->x; CPXNNZ *beg = cutinfo->beg; CPXDIM *ind = cutinfo->ind; double *val = cutinfo->val; double *rhs = cutinfo->rhs; CPXDIM *cutind = NULL; double *cutval = NULL; double cutvio; int addedcuts = 0; CPXDIM i, j; CPXNNZ k; CPXDIM cutnz; *useraction_p = CPX_CALLBACK_DEFAULT; /* If we are called as a user cut callback, decide first if we want to add cuts or abort the cut loop. When adding user cuts with purgeable flag set to CPX_USECUT_PURGE or CPX_USECUT_FILTER, it is important to avoid the possibility of an infinite cut loop, where the same cuts are added to the LP and then immediately purged at every cut pass. Such a situation can be avoided, for instance, by applying a tailing off criterion and aborting the cut loop where no progress in the objval is observed. Note, however, that the same approach must not be applied with lazy constraints. In this case, if lazy constraints are added with purgeable flag set to CPX_USECUT_PURGE, adding the same lazy constraint more than once could be required to ensure the correctness of the final result. */ if ( wherefrom == CPX_CALLBACK_MIP_CUT_LOOP || wherefrom == CPX_CALLBACK_MIP_CUT_LAST ) { CPXCNT oldnodeid = cutinfo->nodeid; double oldnodeobjval = cutinfo->nodeobjval; /* Retrieve nodeid and node objval of the current node */ status = CPXXgetcallbacknodeinfo (env, cbdata, wherefrom, 0, CPX_CALLBACK_INFO_NODE_SEQNUM_LONG, &cutinfo->nodeid); if ( status ) { fprintf(stderr, "Failed to get node id.\n"); goto TERMINATE; } status = CPXXgetcallbacknodeinfo (env, cbdata, wherefrom, 0, CPX_CALLBACK_INFO_NODE_OBJVAL, &cutinfo->nodeobjval); if ( status ) { fprintf(stderr, "Failed to get node objval.\n"); goto TERMINATE; } /* Abort the cut loop if we are stuck at the same node as before and there is no progress in the node objval */ if ( oldnodeid == cutinfo->nodeid ) { double objchg = (cutinfo->nodeobjval - oldnodeobjval); /* Multiply objchg by objsen to normalize the change in the objective function to the case of a minimization problem */ objchg *= cutinfo->objsen; if ( objchg <= EPSOBJ ) { *useraction_p = CPX_CALLBACK_ABORT_CUT_LOOP; goto TERMINATE; } } } /* If we reached this point, we are .. in a lazyconstraint callback, or .. in a user cut callback, and cuts seem to help improving the node objval. In both cases, we retrieve the x solution and look for violated cuts. */ status = CPXXgetcallbacknodex (env, cbdata, wherefrom, x, 0, numcols-1); if ( status ) { fprintf(stderr, "Failed to get node solution.\n"); goto TERMINATE; } for (i = 0; i < numcuts; i++) { cutvio = -rhs[i]; k = beg[i]; cutnz = beg[i+1] - k; cutind = ind + k; cutval = val + k; for (j = 0; j < cutnz; j++) { cutvio += x[cutind[j]] * cutval[j]; } /* Use a cut violation tolerance of 0.01 */ if ( cutvio > 0.01 ) { status = CPXXcutcallbackadd (env, cbdata, wherefrom, cutnz, rhs[i], 'L', cutind, cutval, CPX_USECUT_PURGE); if ( status ) { fprintf (stderr, "Failed to add cut.\n"); goto TERMINATE; } addedcuts++; } } /* Tell CPLEX that cuts have been created */ if ( addedcuts > 0 ) { *useraction_p = CPX_CALLBACK_SET; } TERMINATE: return (status); } /* END mycutcallback */