Skip to content

Instantly share code, notes, and snippets.

@lyeskhalil
Last active December 9, 2021 05:39
Show Gist options
  • Save lyeskhalil/5f86c4e3bd23e353d98c72999e423355 to your computer and use it in GitHub Desktop.
Save lyeskhalil/5f86c4e3bd23e353d98c72999e423355 to your computer and use it in GitHub Desktop.
Get CPLEX's presolved model with the cuts added at the root

Get CPLEX's presolved model with the cuts added at the root

Based on Rodolfo Carvajal's gist.

Compile:
1- Adjust CPX_PATH and ARCH based on your machine;
2- make cpx.

Usage:
./get_cuts <input_filename> <output_filename> where <input_filename> is a path to a MIP instance (LP, MPS, SAV formats; whatever CPLEX can process); <output_filename> is similar and assumes the directory of choice exists already.

Notes:

  • Tested with CPLEX 12.10 on MacOS
  • Code uses CPXX functions, thus assuming a 64-bit architecture; would probably also work on 32-bit by changing all CPXX to CPX and #include <cplexx.h> to #include <cplex.h>
/*
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 */
CPX_PATH = /Applications/CPLEX_Studio1210/cplex/
CC = g++
ARCH = x86-64_osx
cpx:
$(CC) -g -L $(CPX_PATH)/lib/$(ARCH)/static_pic/ -L lib/ -I $(CPX_PATH)/include/ilcplex/ -o get_cuts get_cuts.cpp -lm -lpthread -lcplex -ldl
@rahulptel
Copy link

-lpthread should be specified last to avoid ''undefined reference to pthread_getspecific''.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment