static int CPXPUBLIC usersolve (CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) { int status = 0; CPXCNT nodecount; CPXLPptr nodelp; *useraction_p = CPX_CALLBACK_DEFAULT; /* Get pointer to LP subproblem */ status = CPXXgetcallbacknodelp (env, cbdata, wherefrom, &nodelp); if ( status ) goto TERMINATE; /* Find out what node is being processed */ status = CPXXgetcallbackinfo (env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODE_COUNT_LONG, &nodecount); if ( status ) goto TERMINATE; /* Solve initial node with primal, others with dual */ if ( nodecount < 1 ) status = CPXXprimopt (env, nodelp); else status = CPXXdualopt (env, nodelp); /* If the solve was OK, set return to say optimization has been done in callback, otherwise return the CPLEX error code */ if ( !status ) *useraction_p = CPX_CALLBACK_SET; TERMINATE: return (status); } /* END usersolve */
static int CPXPUBLIC solvecallback (CPXCENVptr env, void *cbdata, int wherefrom, void *userinfo, int *useraction_p) { int status = 0; int lpstatus = 0; CPXLPptr nodelp = NULL; double *prex = NULL; MYCBptr mycbinfo = (MYCBptr) userinfo; CPXLPptr mip = mycbinfo->mip; double *relx = mycbinfo->relx; CPXDIM cols; int prestat; *useraction_p = CPX_CALLBACK_DEFAULT; /* Only use callback for solving the root relaxation (node 0) */ if ( mycbinfo->count > 0 ) { goto TERMINATE; } mycbinfo->count++; /* Extract the LP to be solved */ status = CPXXgetcallbacknodelp (env, cbdata, wherefrom, &nodelp); if ( status ) goto TERMINATE; cols = CPXXgetnumcols (env, nodelp); prex = malloc (cols * sizeof (*prex)); if ( prex == NULL ) { status = CPXERR_NO_MEMORY; goto TERMINATE; } /* Use MIP presolve to crush the original solution. Note that MIP presolve can only crush primal solutions */ status = CPXXgetprestat (env, mip, &prestat, NULL, NULL, NULL, NULL); if ( status ) goto TERMINATE; /* If a presolved model exists, then relx is crushed down to prex, the corresponding solution for the presolved model; otherwise, prex is just a copy of relx */ if ( prestat ) { status = CPXXcrushx (env, mip, relx, prex); if ( status ) goto TERMINATE; } else { memcpy (prex, relx, cols * sizeof (*relx)); } /* Feed the crushed solution into 'nodelp' */ status = CPXXcopystart (env, nodelp, NULL, NULL, prex, NULL, NULL, NULL); /* Use primal to reoptimize, since we only have a primal solution */ status = CPXXprimopt (env, nodelp); if ( status ) goto TERMINATE; lpstatus = CPXXgetstat (env, nodelp); if ( lpstatus == CPX_STAT_OPTIMAL || lpstatus == CPX_STAT_OPTIMAL_INFEAS || lpstatus == CPX_STAT_INFEASIBLE ) { *useraction_p = CPX_CALLBACK_SET; } TERMINATE: free_and_null ((char **) &prex); return (status); } /* END solvecallback */
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 */