|
/* |
|
Compile with "make cpx" |
|
*/ |
|
|
|
#include <cplexx.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
/* Declarations for functions in this program */ |
|
|
|
static int CPXPUBLIC |
|
cutCallbackShareRootCuts (CPXCENVptr env, void *cbdata, int wherefrom, |
|
void *cbhandle, int *useraction_p); |
|
|
|
static void |
|
free_and_null (char **ptr), |
|
usage (char *progname); |
|
|
|
|
|
int cutCallbackShareRootCuts (CPXCENVptr env, |
|
void *cbdata, |
|
int wherefrom, |
|
void *cbhandle, |
|
int *useraction_p) |
|
{ |
|
int status; |
|
*useraction_p = CPX_CALLBACK_DEFAULT; |
|
|
|
// CPX_CALLBACK_MIP_CUT_LAST indicates that CPLEX is done adding cuts and the user has a last chance to add cuts |
|
if(wherefrom == CPX_CALLBACK_MIP_CUT_LAST) |
|
{ |
|
// With this, we stop the optimization after the callback is executed |
|
*useraction_p = CPX_CALLBACK_FAIL; |
|
|
|
CPXLPptr tmpLp, copy; |
|
status = CPXXgetcallbacknodelp(env, cbdata, wherefrom, &tmpLp); |
|
copy = CPXXcloneprob(env, tmpLp, &status); |
|
if ( status ) { |
|
fprintf (stderr, "Failed to clone LP.\n"); |
|
} |
|
|
|
CPXDIM precols; |
|
char *prectype; |
|
precols = CPXXgetnumcols (env, copy); |
|
prectype = static_cast<char*>(malloc (precols * sizeof(char))); |
|
status = CPXXgetcallbackctype (env, cbdata, wherefrom, prectype, 0, precols-1); |
|
status = CPXXcopyctype (env, copy, prectype); |
|
status = CPXXwriteprob(env, copy, static_cast<char*>(cbhandle), NULL); |
|
if ( status ) { |
|
fprintf (stderr, "Failed to write presolve/cut MIP.\n"); |
|
} |
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
int |
|
main (int argc, |
|
char *argv[]) |
|
{ |
|
int status = 0; |
|
|
|
/* Declare and allocate space for the variables and arrays where |
|
we will store the optimization results, including the status, |
|
objective value, and variable values */ |
|
|
|
int solstat; |
|
char filename[strlen(argv[2])]; |
|
|
|
CPXENVptr env = NULL; |
|
CPXLPptr mip = NULL; |
|
|
|
/* Initialize the CPLEX environment */ |
|
|
|
env = CPXXopenCPLEX (&status); |
|
|
|
/* If an error occurs, the status value indicates the reason for |
|
failure. A call to CPXXgeterrorstring will produce the text of |
|
the error message. Note that CPXXopenCPLEX produces no |
|
output, so the only way to see the cause of the error is to use |
|
CPXXgeterrorstring. For other CPLEX routines, the errors will |
|
be seen if the CPXPARAM_ScreenOutput parameter is set to CPX_ON */ |
|
|
|
if ( env == NULL ) { |
|
char errmsg[CPXMESSAGEBUFSIZE]; |
|
fprintf (stderr, "Could not open CPLEX environment.\n"); |
|
CPXXgeterrorstring (env, status, errmsg); |
|
fprintf (stderr, "%s", errmsg); |
|
goto TERMINATE; |
|
} |
|
|
|
/* Turn on output to the screen */ |
|
|
|
status = CPXXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON); |
|
if ( status ) { |
|
fprintf (stderr, |
|
"Failure to turn on screen indicator, error %d.\n", |
|
status); |
|
goto TERMINATE; |
|
} |
|
|
|
/* Create the problem, using the filename as the problem name */ |
|
|
|
mip = CPXXcreateprob (env, &status, argv[1]); |
|
|
|
strcpy(filename, argv[2]); |
|
|
|
/* A returned pointer of NULL may mean that not enough memory |
|
was available or there was some other problem. In the case of |
|
failure, an error message will have been written to the error |
|
channel from inside CPLEX. In this example, the setting of |
|
the parameter CPXPARAM_ScreenOutput causes the error message to |
|
appear on stdout. Note that most CPLEX routines return |
|
an error code to indicate the reason for failure */ |
|
|
|
if ( mip == NULL ) { |
|
fprintf (stderr, "Failed to create LP.\n"); |
|
goto TERMINATE; |
|
} |
|
|
|
/* Now read the file, and copy the data into the created lp */ |
|
|
|
status = CPXXreadcopyprob (env, mip, argv[1], NULL); |
|
if ( status ) { |
|
fprintf (stderr, |
|
"Failed to read and copy the problem data.\n"); |
|
goto TERMINATE; |
|
} |
|
|
|
/* Set up to use MIP callbacks */ |
|
|
|
status = CPXXsetusercutcallbackfunc(env, cutCallbackShareRootCuts, &filename); |
|
|
|
/* Set MIP log interval to 1 */ |
|
|
|
status = CPXXsetcntparam (env, CPXPARAM_MIP_Interval, 1); |
|
if ( status ) goto TERMINATE; |
|
|
|
/* Set node limit to 1 */ |
|
|
|
status = CPXXsetcntparam (env, CPXPARAM_MIP_Limits_Nodes, 1); |
|
if ( status ) goto TERMINATE; |
|
|
|
/* Optimize the problem and obtain solution */ |
|
|
|
status = CPXXmipopt (env, mip); |
|
if ( status ) { |
|
fprintf (stderr, "Failed to optimize MIP.\n"); |
|
goto TERMINATE; |
|
} |
|
|
|
solstat = CPXXgetstat (env, mip); |
|
printf ("Solution status %d.\n", solstat); |
|
|
|
|
|
TERMINATE: |
|
|
|
/* Free the problem as allocated by CPXXcreateprob and |
|
CPXXreadcopyprob, if necessary */ |
|
|
|
if ( mip != NULL ) { |
|
int xstatus = CPXXfreeprob (env, &mip); |
|
|
|
if ( !status ) status = xstatus; |
|
} |
|
|
|
/* Free the CPLEX environment, if necessary */ |
|
|
|
if ( env != NULL ) { |
|
int xstatus = CPXXcloseCPLEX (&env); |
|
|
|
if ( !status ) status = xstatus; |
|
} |
|
|
|
return (status); |
|
|
|
} /* END main */ |
|
|
|
|
|
/* This simple routine frees up the pointer *ptr, and sets *ptr to |
|
NULL */ |
|
|
|
static void |
|
free_and_null (char **ptr) |
|
{ |
|
if ( *ptr != NULL ) { |
|
free (*ptr); |
|
*ptr = NULL; |
|
} |
|
} /* END free_and_null */ |
|
|
|
|
|
static void |
|
usage (char *progname) |
|
{ |
|
fprintf (stderr, |
|
"Usage: %s input_filename output_filename\n", progname); |
|
fprintf (stderr, |
|
" input_filename, output_filename Name of a file, with .mps, .lp, or .sav\n"); |
|
fprintf (stderr, |
|
" extension, and a possible, additional .gz\n"); |
|
fprintf (stderr, |
|
" extension\n"); |
|
} /* END usage */ |
Thanks, this is a very useful tool. I wonder if it is possible to modify it so that it still extracts the cuts if the problem gets solved at the root (e.g. if the status is 103 instead of 114 in the case of an infeasible problem)?