|
/* |
|
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 */ |
-lpthread
should be specified last to avoid ''undefined reference topthread_getspecific
''.