Beispiel #1
0
int
main(int argc, char **argv)
{
   int status;
   CPXENVptr *env;
   CPXLPptr *lp;
   char const *modelfile = NULL;
   CPXASYNCptr *handle;
   CPXENVGROUPptr group;
   int i;
   int jobs = 0;
   int active;
   int *finished;
   char const **machine;
   char cwd[MAX_PATH_LEN];
   char usrfunc[MAX_PATH_LEN];
   int frequency;
   double absgap = 1e-6;
   int bestidx;
   CPXDIM c, cols;
   double *x;
   enum {
      OUTPUT_SILENT, OUTPUT_PREFIXED, OUTPUT_LOG
   } output = OUTPUT_SILENT;

#if defined(USE_MPI)
   int numprocs, rank;

   MPI_Init(&argc, &argv);
   MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
   if ( numprocs < 3 ) {
      fprintf (stderr, "Invalid number of processors (%d)\n", numprocs);
      abort ();
   }
   MPI_Comm_rank (MPI_COMM_WORLD, &rank);
   if ( rank != 0 ) {
      fprintf (stderr, "Master must have rank 0!\n");
      MPI_Finalize ();
      abort ();
   }
   machine = malloc (sizeof (*machine) * numprocs);
   if ( machine == NULL ) {
      fprintf (stderr, "Out of memory!\n");
      abort ();
   }
   for (i = 0; i < numprocs; ++i)
      machine[i] = "mpimachine";
   jobs = numprocs - 1;
#elif defined(USE_PROCESS)
   char const *bin = "./cplex";

   machine = malloc (sizeof (*machine) * argc);
   if ( machine == NULL ) {
      fprintf (stderr, "Out of memory!\n");
      abort ();
   }
#elif defined(USE_TCPIP)
   machine = malloc (sizeof (*machine) * argc);
   if ( machine == NULL ) {
      fprintf (stderr, "Out of memory!\n");
      abort ();
   }
#else
#   error "No transport type selected"
#endif


   /* Parse the command line. */
   for (i = 1; i < argc; ++i) {
      if ( strncmp (argv[i], "-model=", 7) == 0 )
         modelfile = argv[i] + 7;
#if defined(USE_MPI)
#elif defined(USE_PROCESS)
      else if ( strncmp (argv[i], "-machine=", 9) == 0 )
         machine[jobs++] = argv[i] + 9;
      else if ( strncmp (argv[i], "-bin=", 5) == 0 )
         bin = argv[i] + 5;
#elif defined(USE_TCPIP)
      else if ( strncmp (argv[i], "-address=", 9) == 0 )
         machine[jobs++] = argv[i];
#endif
      else if ( strncmp (argv[i], "-absgap=", 8) == 0 )
         absgap = strtod (argv[i] + 8, NULL);
      else if ( strcmp (argv[i], "-output-prefixed") == 0 )
         output = OUTPUT_PREFIXED;
      else if ( strcmp (argv[i], "-output-log") == 0 )
         output = OUTPUT_LOG;
   }

   /* Validate arguments.
    */
   if ( modelfile == NULL ) {
      fprintf (stderr, "No model file specified with -model=<modelfile>\n");
      abort ();
   }
   if ( jobs < 1 ) {
     fprintf (stderr, "Invalid job count %d\n", jobs);
     abort ();
   }

   /* Allocate working arrays. */
   if ( (env = malloc (sizeof (*env) * jobs)) == NULL ||
        (handle = malloc (sizeof (*handle) * jobs)) == NULL ||
        (lp = malloc (sizeof (*lp) * jobs)) == NULL ||
        (finished = calloc (jobs, sizeof (*finished))) == NULL ||
        (remotestats = calloc (jobs, sizeof (*remotestats))) == NULL )
   {
      fprintf (stderr, "Out of memory!\n");
      abort ();
   }

   /* Find the place at which to find the shared object that implements
    * the user function. On Windows the path to the current directory is
    * likely to contain blanks, so better quote it.
    */
   getcwd (cwd, sizeof (cwd));
   usrfunc[0] = 0;
#ifdef _WIN32
   strcat (usrfunc, "-libpath=\"");
   strcat (usrfunc, cwd);
   strcat (usrfunc, "\"");
#else
   strcat (usrfunc, "-libpath=");
   strcat (usrfunc, cwd);
#endif

   /* Create a remote object instances. */
   for (i = 0; i < jobs; ++i) {
      /* These values define how we connect to the remote object. It is
       * important to use a transport configuration that actually supports
       * disconnect/reconnect. For the "processtransport" this means to use
       * named pipes instead of anonymous pipes or stdio.
       */
      char const *transport;
      char const *args[16];
      int nextarg = 0;
      char *logpath = NULL;

#if defined(USE_MPI)
      char rankbuf[256];

      sprintf (rankbuf, "-remoterank=%d", i + 1);
      transport = "mpitransport";
      args[nextarg++] = rankbuf;
#elif defined(USE_PROCESS)
      char logbuf[1024];
      transport = "processtransport";
      /* If the machine is not "localhost" then use ssh to connect to
       * this machine. Otherwise just fork a process on the local
       * machine.
       */
      if ( machine[i] != NULL && strcmp (machine[i], "localhost") != 0 ) {
         args[nextarg++] = "/usr/bin/ssh";
         args[nextarg++] = machine[i];
      }
      args[nextarg++] = bin;
      args[nextarg++] = "-worker=process";
      if ( machine[i] != NULL )
         args[nextarg++] = "-stdio";
      else
         args[nextarg++] = "-namedpipes=.";
      args[nextarg++] = usrfunc;
      args[nextarg++] = "-userfunction=parmipopt_userfunction=REGISTER_USERFUNCTION";
      sprintf (logbuf, "-logfile=server%d.log", i);
      if ( (args[nextarg] = logpath = strdup (logbuf)) != NULL )
         ++nextarg;
#elif defined(USE_TCPIP)
      transport = "tcpiptransport";
      args[nextarg++] = machine[i];
#endif


      printf ("Creating env on %s\n", machine[i]);
      env[i] = CPXXopenCPLEXremote(transport, nextarg, args, &status);
      if ( status || env[i] == NULL ) {
         fprintf (stderr, "CPXXopenCPLEXremote: %d\n", status);
         abort ();
      }
      free (logpath);

      /* Enable output */
      switch (output) {
      case OUTPUT_SILENT:
         /* nothing */
         break;
      case OUTPUT_PREFIXED:
         {
            CPXCHANNELptr cres, cwar, cerr, clog;

            if ( (status = CPXXgetchannels (env[i], &cres, &cwar, &cerr, &clog)) != 0 ) {
               fprintf (stderr, "CPXXgetchannels: %d\n", status);
               abort ();
            }
            if ( (status = CPXXaddfuncdest (env[i], cres, env[i], printer)) != 0 ||
                 (status = CPXXaddfuncdest (env[i], cwar, env[i], printer)) != 0 ||
                 (status = CPXXaddfuncdest (env[i], clog, env[i], printer)) != 0 )
            {
               fprintf (stderr, "CPXXaddfpdest: %d\n", status);
               abort ();
            }
         }
         break;
      case OUTPUT_LOG:
         {
            if ( (status = CPXXsetintparam (env[i], CPXPARAM_ScreenOutput, CPX_ON)) != 0 ) {
               fprintf (stderr, "CPXXgetchannels: %d\n", status);
               abort ();
            }
         }
         break;
      }

      /* Create empty problem object for this remote solver. */
      printf ("Creating LP %d\n", i);
      lp[i] = CPXXcreateprob(env[i], &status, "problem");
      if ( status || lp[i] == NULL ) {
         fprintf (stderr, "CPXXcreateprob: %d\n", status);
         abort ();
      }

      /* Install and configure callbacks. */
      remotestats[i].env = env[i];
      remotestats[i].idx = i;
      if ( (status = CPXXsetinfohandler (env[i], infohandler, &remotestats[i])) != 0 ) {
         fprintf (stderr, "CPXXsetinfohandler: %d\n", status);
         abort ();
      }

      if ( (status = changeObjdiff (env[i], 1e-5)) != 0 ) {
         fprintf (stderr, "changeObjdiff: %d\n", status);
         abort ();
      }

      if ( (status = installCallback (env[i])) != 0 ) {
         fprintf (stderr, "installCallback: %d\n", status);
         abort ();
      }

      /* Apply predefined perameter settings for this solver. */
      applySettings (env[i], i);
   }

   /* Put all environments into one group so that we can use multicasts
    * on operations that are the same for all solvers and/or imply lots
    * of data exchange.
    */
   status = CPXXcreateenvgroup (&group, jobs, env);
   if ( status != 0 ) {
      fprintf (stderr, "CPXXcreateenvgroup: %d\n", status);
      abort ();
   }

   /* Read the model into all remote solver. */
   status = CPXXreadcopyprob_multicast (group, modelfile, NULL);
   if ( status != 0 ) {
      fprintf (stderr, "CPXXreadcopyprob_multicast: %d\n", status);
      abort ();
   }
   objsen = CPXXgetobjsen (env[0], lp[0]);

   /* We set the thread count for each solver to 1 so that we do not
    * run into problems if multiple solves are performed on the same
    * machine.
    */
   status = CPXXsetintparam_multicast (group, CPXPARAM_Threads, 1);
   if ( status != 0 ) {
      fprintf (stderr, "CPXXsetintparam_multicast: %d\n", status);
      abort ();
   }

   /* Start an asynchronous solve on each remote solver. */
   for (i = 0; i < jobs; ++i) {
      printf ("Solving %d\n", i);
      if ( (status = CPXXmipopt_async (env[i], lp[i], &handle[i])) != 0 ) {
         fprintf (stderr, "CPXXmipopt_async: %d\n", status);
         abort ();
      }
   }

   /* All solves are started. Loop until the stopping criterion is met. */
   active = jobs;
   frequency = 10000; /* Print current bounds every two seconds. */
   while (active > 0) {
      int running = 0;
      /* Check if we shold stop all solves.
       * We stop them if the absolute mipgap is reached.
       */
      if ( primal.valid && dual.valid &&
           ((objsen == CPX_MIN && dual.bound + absgap >= primal.bound) ||
            (objsen == CPX_MAX && dual.bound - absgap <= primal.bound)) )
      {
         printf ("Stopping criterion reached. Stopping all pending solves.\n");
         for (i = 0; i < jobs; ++i) {
            if ( !finished[i] )
               CPXXasynckill (handle[i]);
         }
         break;
      }
      if ( --frequency == 0 ) {
         printf ("dual=%f, primal=%f\n", dual.bound, primal.bound);
         frequency = 10000;
      }

      /* Loop over all solvers and test if they are still running. */
      for (i = 0; i < jobs; ++i) {
         if ( finished[i] )
            continue;
         CPXXasynctest (handle[i], &running);
         if ( !running ) {
            /* The job is finished. We have a solution, so kill all
             * others. */
            int j;
            --active;
            finished[i] = 1;
            printf ("First job (%d) is finished, killing the rest\n", i);
            for (j = 0; j < jobs; ++j) {
               if ( j != i )
                  CPXXasynckill (handle[j]);
            }
            break;
         }
      }
      millisleep (10);
   }

   /* All solves have finished. Join them. */
   for (i = 0; i < jobs; ++i) {
      double obj = -CPX_INFBOUND;
      int stat;
      
      status = CPXXmipopt_join (&handle[i]);
      if ( status ) {
         fprintf (stderr, "CPXXmipopt_join: %d\n", status);
         abort ();
      }

      status = CPXXgetobjval (env[i], lp[i], &obj);
      if ( status == CPXERR_NO_SOLN ) {
         /* No feasible solution found (yet) on this machine.
          * Just set objective function to a very big value
          */
         obj = (objsen == CPX_MIN) ? CPX_INFBOUND : -CPX_INFBOUND;
      }
      else if ( status ) {
         fprintf (stderr, "CPXXgetobjval: %d\n", status);
         abort ();
      }
      stat = CPXXgetstat (env[i], lp[i]);
      printf ("Job %d: %f, stat %d\n", i, obj, stat);
      printf ("\t%f, %f, %f\n", remotestats[i].dettime,
              remotestats[i].dual, remotestats[i].primal);

      if ( (status = removeCallback (env[i])) != 0 ) {
         fprintf (stderr, "removeCallback: %d\n", status);
         abort ();
      }
   }

   /* Fetch the x vector from the solver that produced the best
    * primal bound. */
   bestidx = primal.idx;
   cols = CPXXgetnumcols (env[bestidx], lp[bestidx]);
   if ( (x = malloc (cols * sizeof (*x))) == NULL ) {
      fprintf (stderr, "Out of memory!\n");
      abort ();
   }
   status = CPXXgetx (env[bestidx], lp[bestidx], x, 0, cols - 1);
   if ( status ) {
      fprintf (stderr, "CPXXgetx: %d\n", status);
      abort ();
   }
   printf ("Optimal solution:\n");
   for (c = 0; c < cols; ++c)
      printf ("x[%5d]: %f\n", c, x[c]);
   free (x);

   CPXXfreeenvgroup (&group);

   /* Close the CPLEX objects in _reverse_ order. */
   for (i = jobs - 1; i >= 0; --i)
      CPXXcloseCPLEX (&env[i]);
   free (remotestats);
   free (env);
   free (lp);
   free (handle);
   free (finished);
   free ((char **)machine);

#ifdef USE_MPI
   MPI_Finalize ();
#endif


   return 0;
}
Beispiel #2
0
int
main (void)
{
   char     probname[16];  /* Problem name is max 16 characters */

   /* Declare and allocate space for the variables and arrays where we
      will store the optimization results including the status, objective
      value, variable values, dual values, row slacks and variable
      reduced costs. */

   int      solstat;
   double   objval;
   double   x[NUMCOLS];
   double   pi[NUMROWS];
   double   slack[NUMROWS];
   double   dj[NUMCOLS];


   CPXENVptr     env = NULL;
   CPXLPptr      lp = NULL;
   int           status;
   CPXDIM        i, j;
   CPXDIM        cur_numrows, cur_numcols;
   char          errmsg[CPXMESSAGEBUFSIZE];

   CPXCHANNELptr  cpxerror   = NULL;
   CPXCHANNELptr  cpxwarning = NULL;
   CPXCHANNELptr  cpxresults = NULL;
   CPXCHANNELptr  ourchannel = NULL;

   char *errorlabel = "cpxerror";
   char *warnlabel  = "cpxwarning";
   char *reslabel   = "cpxresults";
   char *ourlabel   = "Our Channel";

   CPXFILEptr fpout  = 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 indicator is set to CPX_ON.  */

   /* Since the message handler is yet to be set up, we'll call our
      messaging function directly to print out any errors  */

   if ( env == NULL ) {
      ourmsgfunc ("Our Message", "Could not open CPLEX environment.\n");
      goto TERMINATE;
   }

   /* Now get the standard channels.  If an error, just call our
      message function directly. */

   status = CPXXgetchannels (env, &cpxresults, &cpxwarning, &cpxerror, NULL);
   if ( status ) {
      ourmsgfunc ("Our Message", "Could not get standard channels.\n");
      CPXXgeterrorstring (env, status, errmsg);
      ourmsgfunc ("Our Message", errmsg);
      goto TERMINATE;
   }

   /* Now set up the error channel first.  The label will be "cpxerror" */

   status = CPXXaddfuncdest (env, cpxerror, errorlabel, ourmsgfunc);
   if ( status ) {
      ourmsgfunc ("Our Message", "Could not set up error message handler.\n");
      CPXXgeterrorstring (env, status, errmsg);
      ourmsgfunc ("Our Message", errmsg);
   }

   /* Now that we have the error message handler set up, all CPLEX
      generated errors will go through ourmsgfunc.  So we don't have
      to use CPXXgeterrorstring to determine the text of the message.
      We can also use CPXXmsg to do any other printing.  */

   status = CPXXaddfuncdest (env, cpxwarning, warnlabel, ourmsgfunc);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to set up handler for cpxwarning.\n");
      goto TERMINATE;
   }

   status = CPXXaddfuncdest (env, cpxresults, reslabel, ourmsgfunc);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to set up handler for cpxresults.\n");
      goto TERMINATE;
   }
   
   /* Now turn on the iteration display. */

   status = CPXXsetintparam (env, CPXPARAM_Simplex_Display, 2);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to turn on simplex display level.\n");
      goto TERMINATE;
   }

   /* Create the problem. */

   strcpy (probname, "example");
   lp = CPXXcreateprob (env, &status, probname);

   /* 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.  */

   if ( lp == NULL ) {
      CPXXmsg (cpxerror, "Failed to create LP.\n");
      goto TERMINATE;
   }

   /* Now populate the problem with the data. */

   status = populatebycolumn (env, lp);

   if ( status ) {
      CPXXmsg (cpxerror, "Failed to populate problem data.\n");
      goto TERMINATE;
   }


   /* Optimize the problem and obtain solution. */

   status = CPXXlpopt (env, lp);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to optimize LP.\n");
      goto TERMINATE;
   }

   status = CPXXsolution (env, lp, &solstat, &objval, x, pi, slack, dj);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to obtain solution.\n");
      goto TERMINATE;
   }


   /* Write the output to the screen.  We will also write it to a
      file as well by setting up a file destination and a function
      destination. */

   ourchannel = CPXXaddchannel (env);
   if ( ourchannel == NULL ) {
      CPXXmsg (cpxerror, "Failed to set up our private channel.\n");
      goto TERMINATE;
   }

   fpout = CPXXfopen ("lpex5.msg", "w");
   if ( fpout == NULL ) {
      CPXXmsg (cpxerror, "Failed to open lpex5.msg file for output.\n");
      goto TERMINATE;
   }
   status = CPXXaddfpdest (env, ourchannel, fpout);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to set up output file destination.\n");
      goto TERMINATE;
   }

   status = CPXXaddfuncdest (env, ourchannel, ourlabel, ourmsgfunc);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to set up our output function.\n");
      goto TERMINATE;
   }

   /* Now any message to channel ourchannel will go into the file 
      and into the file opened above. */

   CPXXmsg (ourchannel, "\nSolution status = %d\n", solstat);
   CPXXmsg (ourchannel, "Solution value  = %f\n\n", objval);

   /* The size of the problem should be obtained by asking CPLEX what
      the actual size is, rather than using sizes from when the problem
      was built.  cur_numrows and cur_numcols store the current number 
      of rows and columns, respectively.  */

   cur_numrows = CPXXgetnumrows (env, lp);
   cur_numcols = CPXXgetnumcols (env, lp);
   for (i = 0; i < cur_numrows; i++) {
      CPXXmsg (ourchannel, "Row %d:  Slack = %10f  Pi = %10f\n", 
              i, slack[i], pi[i]);
   }

   for (j = 0; j < cur_numcols; j++) {
      CPXXmsg (ourchannel, "Column %d:  Value = %10f  Reduced cost = %10f\n",
              j, x[j], dj[j]);
   }

   /* Finally, write a copy of the problem to a file. */

   status = CPXXwriteprob (env, lp, "lpex5.lp", NULL);
   if ( status ) {
      CPXXmsg (cpxerror, "Failed to write LP to disk.\n");
      goto TERMINATE;
   }
   
   
TERMINATE:

   /* First check if ourchannel is open */

   if ( ourchannel != NULL ) {
      int  chanstat;
      chanstat = CPXXdelfuncdest (env, ourchannel, ourlabel, ourmsgfunc);
      if ( chanstat ) {
         strcpy (errmsg, "CPXXdelfuncdest failed.\n");
         ourmsgfunc ("Our Message", errmsg); 
         if (!status)  status = chanstat;
      }
      if ( fpout != NULL ) {
         chanstat = CPXXdelfpdest (env, ourchannel, fpout);
         if ( chanstat ) {
            strcpy (errmsg, "CPXXdelfpdest failed.\n");
            ourmsgfunc ("Our Message", errmsg);
            if (!status)  status = chanstat;
         }
         CPXXfclose (fpout);
      }

      chanstat = CPXXdelchannel (env, &ourchannel);
      if ( chanstat ) {
         strcpy (errmsg, "CPXXdelchannel failed.\n");
         ourmsgfunc ("Our Message", errmsg);
         if (!status)  status = chanstat;
      }
   }

   /* Free up the problem as allocated by CPXXcreateprob, if necessary */

   if ( lp != NULL ) {
      status = CPXXfreeprob (env, &lp);
      if ( status ) {
         strcpy (errmsg, "CPXXfreeprob failed.\n");
         ourmsgfunc ("Our Message", errmsg);
      }
   }

   /* Now delete our function destinations from the 3 CPLEX channels. */
   if ( cpxresults != NULL ) {
      int  chanstat;
      chanstat = CPXXdelfuncdest (env, cpxresults, reslabel, ourmsgfunc);
      if ( chanstat && !status ) {
         status = chanstat;
         strcpy (errmsg, "Failed to delete cpxresults function.\n");
         ourmsgfunc ("Our Message", errmsg);
      }
   }

   if ( cpxwarning != NULL ) {
      int  chanstat;
      chanstat = CPXXdelfuncdest (env, cpxwarning, warnlabel, ourmsgfunc);
      if ( chanstat && !status ) {
         status = chanstat;
         strcpy (errmsg, "Failed to delete cpxwarning function.\n");
         ourmsgfunc ("Our Message", errmsg);
      }
   }

   if ( cpxerror != NULL ) {
      int  chanstat;
      chanstat = CPXXdelfuncdest (env, cpxerror, errorlabel, ourmsgfunc);
      if ( chanstat && !status ) {
         status = chanstat;
         strcpy (errmsg, "Failed to delete cpxerror function.\n");
         ourmsgfunc ("Our Message", errmsg);
      }
   }

   /* Free up the CPLEX environment, if necessary */

   if ( env != NULL ) {
      status = CPXXcloseCPLEX (&env);

      /* Note that CPXXcloseCPLEX 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 indicator is set to CPX_ON. */

      if ( status ) {
         strcpy (errmsg, "Could not close CPLEX environment.\n");
         ourmsgfunc ("Our Message", errmsg);
         CPXXgeterrorstring (env, status, errmsg);
         ourmsgfunc ("Our Message", errmsg);
      }
   }
     
   return (status);

}  /* END main */
Beispiel #3
0
/* This function creates the following model:
 *   Minimize
 *    obj: x1 + x2 + x3 + x4 + x5 + x6
 *   Subject To
 *    c1: x1 + x2      + x5      = 8
 *    c2:           x3 + x5 + x6 = 10
 *    q1: [ -x1^2 + x2^2 + x3^2 ] <= 0
 *    q2: [ -x4^2 + x5^2 ] <= 0
 *   Bounds
 *    x2 Free
 *    x3 Free
 *    x5 Free
 *   End
 * which is a second order cone program in standard form.
 * The function returns a true value on success and false on error.
 * The function also sets up *cone_p as follows:
 * (*cone_p)[j] >= 0              Column j is contained in a cone constraint
 *                                and is the cone head variable of that
 *                                constraint. The index of the respective
 *                                quadratic constraint is given by (*cone_p)[j].
 * (*cone_p)[j] = NOT_CONE_HEAD   Column j is contained in a cone constraint
 *                                but is not the cone head variable of that
 *                                constraint.
 * (*cone_p)[j] = NOT_IN_CONE     Column j is not contained in any cone
 *                                constraint.
 */
static int
createmodel (CPXENVptr env, CPXLPptr lp, CPXDIM **cone_p)
{
   /* Column data. */
   static double const obj[]        = {  1.0,  1.0,  1.0,  1.0,  1.0,  1.0 };
   static double const lb[]         = {  0.0, -INF, -INF,  0.0, -INF,  0.0 };
   static double const ub[]         = {  INF,  INF,  INF,  INF,  INF,  INF };
   static char const *const cname[] = { "x1", "x2", "x3", "x4", "x5", "x6" };

   /* Row data. */
   static double const rval[]       = { 1.0, 1.0, 1.0,    1.0, 1.0, 1.0 };
   static CPXDIM const rind[]       = {   0,   1,   4,      2,   4,   5 };
   static CPXNNZ const rbeg[]       = {   0,                3           };
   static double const rhs[]        = { 8.0,             10.0           };
   static char const sense[]        = { 'E',              'E'           };
   static char const *const rname[] = { "c1",             "c2"          };

   /* Data for second order cone constraints. */
   static double const qval[]  = { -1.0, 1.0, 1.0 }; /* Same for all Q cons. */
   static double const qrhs    = 0.0;                /* Same for all Q cons. */
   static char const qsense    = 'L';                /* Same for all Q cons. */
   static CPXDIM const qind1[] = { 0, 1, 2 };
   static CPXDIM const qind2[] = { 3, 4 };

   int status;
   int ok = 0;
   CPXDIM *cone = NULL;

   CPXCHANNELptr errc;

   /* Get the channel for printing error messages. */
   if ( (status = CPXXgetchannels (env, NULL, NULL, &errc, NULL)) != 0 )
      goto TERMINATE;

   cone = malloc ((sizeof (obj) / sizeof (obj[0])) * sizeof (*cone));
   if ( cone == NULL ) {
      CPXXmsg (errc, "Out of memory!\n");
      goto TERMINATE;
   }

   status = CPXXchgobjsen (env, lp, CPX_MIN);
   if ( status != 0 )
      goto TERMINATE;

   status = CPXXnewcols (env, lp, sizeof (obj) / sizeof (obj[0]),
                         obj, lb, ub, NULL, cname);
   if ( status != 0 )
      goto TERMINATE;

   status = CPXXaddrows (env, lp, 0, sizeof (rhs) / sizeof (rhs[0]),
                         sizeof (rval) / sizeof (rval[0]), rhs, sense,
                         rbeg, rind, rval, NULL, rname);
   if ( status != 0 )
      goto TERMINATE;

   status = CPXXaddqconstr (env, lp, 0, sizeof (qind1) / sizeof (qind1[0]),
                            qrhs, qsense, NULL, NULL,
                            qind1, qind1, qval, "q1");
   if ( status != 0 )
      goto TERMINATE;
   cone[0] = 0;
   cone[1] = NOT_CONE_HEAD;
   cone[2] = NOT_CONE_HEAD;
   status = CPXXaddqconstr (env, lp, 0, sizeof (qind2) / sizeof (qind2[0]),
                            qrhs, qsense, NULL, NULL,
                            qind2, qind2, qval, "q2");
   if ( status != 0 )
      goto TERMINATE;
   cone[3] = 1;
   cone[4] = NOT_CONE_HEAD;

   cone[5] = NOT_IN_CONE;

   ok = 1;
 TERMINATE:
   if ( !ok )
      free (cone);

   *cone_p = cone;

   return ok;
}
Beispiel #4
0
int
main (void)
{
   CPXENVptr env;
   CPXLPptr lp = NULL;
   CPXDIM *cone = NULL;
   int status;
   CPXCHANNELptr resc, warnc, errc, logc;
   int retval = -1;

   /* Initialize CPLEX and get a reference to the output channels.
    * If any of this fails immediately terminate the program.
    */
   env = CPXXopenCPLEX (&status);
   if ( env == NULL || status != 0 )
      abort ();

   status = CPXXgetchannels (env, &resc, &warnc, &errc, &logc);
   if ( status != 0 )
      abort ();

   /* CPLEX is fully setup. Enable output.
    */
   status = CPXXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON);
   if ( status != 0 )
      goto TERMINATE;

   /* Create model. */
   lp = CPXXcreateprob (env, &status, "xsocpex1");
   if ( lp == NULL || status != 0 )
      goto TERMINATE;
   if ( !createmodel (env, lp, &cone) )
      goto TERMINATE;

   /* Solve the problem to optimality. */
   CPXXmsg (logc, "Optimizing ...\n");
   status = CPXXsetdblparam (env, CPXPARAM_Barrier_QCPConvergeTol, CONVTOL);
   if ( status != 0 )
      goto TERMINATE;
   if ( (status = CPXXhybbaropt (env, lp, CPX_ALG_NONE)) != 0 )
      goto TERMINATE;

   if ( CPXXgetstat (env, lp) != CPX_STAT_OPTIMAL ) {
      CPXXmsg (errc, "Cannot test KKT conditions on non-optimal solution.\n");
      goto TERMINATE;
   }

   /* Now test KKT conditions on the result. */
   if ( !checkkkt (env, lp, cone, TESTTOL) ) {
      CPXXmsg (logc, "Testing of KKT conditions failed.\n");
      CPXXmsg (errc, "Testing of KKT conditions failed.\n");
      goto TERMINATE;
   }

   CPXXmsg (resc, "KKT conditions are satisfied.\n");
   retval = 0;
 TERMINATE:
   free (cone);
   if ( lp != NULL )
      CPXXfreeprob (env, &lp);
   CPXXcloseCPLEX (&env);

   return retval;
}
Beispiel #5
0
/*
 * The function returns a true value if the tested KKT conditions are
 * satisfied and false otherwise.
 */
static int
checkkkt (CPXCENVptr env, CPXLPptr lp, CPXDIM const *cone, double tol)
{
   CPXDIM cols = CPXXgetnumcols (env, lp);
   CPXDIM rows = CPXXgetnumrows (env, lp);
   CPXDIM qcons = CPXXgetnumqconstrs (env, lp);
   double *dslack = NULL, *pi = NULL, *socppi = NULL;
   double *val = NULL, *rhs = NULL;
   CPXDIM *ind = NULL;
   char *sense = NULL;
   double *x = NULL, *slack = NULL, *qslack = NULL;
   double *sum = NULL;
   qbuf_type qbuf;
   CPXCHANNELptr resc, warnc, errc, logc;
   int ok = 0, skip = 0;
   int status;
   CPXDIM i, j, q;

   qbuf_init (&qbuf);

   /* Get the channels on which we may report. */
   if ( (status = CPXXgetchannels (env, &resc, &warnc, &errc, &logc)) != 0 )
      goto TERMINATE;

   /* Fetch results and problem data that we need to check the KKT
    * conditions.
    */
   CPXXmsg (logc, "Fetching results ... ");
   if ( (cols > 0 && (dslack = malloc (cols * sizeof (*dslack))) == NULL) ||
        (rows > 0 && (pi = malloc (rows * sizeof (*pi))) == NULL) ||
        (qcons > 0 && (socppi = malloc (qcons * sizeof (*socppi))) == NULL) ||
        (cols > 0 && (x = malloc (cols * sizeof (*x))) == NULL) ||
        (rows > 0 && (sense = malloc (rows * sizeof (*sense))) == NULL ) ||
        (rows > 0 && (slack = malloc (rows * sizeof (*slack))) == NULL ) ||
        (qcons > 0 && (qslack = malloc (qcons * sizeof (*qslack))) == NULL) ||
        (cols > 0 && (sum = malloc (cols * sizeof (*sum))) == NULL) ||
        (cols > 0 && (val = malloc (cols * sizeof (*val))) == NULL) ||
        (cols > 0 && (ind = malloc (cols * sizeof (*ind))) == NULL) ||
        (rows > 0 && (rhs = malloc (rows * sizeof (*rhs))) == NULL) )
   {
      CPXXmsg (errc, "Out of memory!\n");
      goto TERMINATE;
   }

   /* Fetch problem data. */
   if ( (status = CPXXgetsense (env, lp, sense, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXXgetrhs (env, lp, rhs, 0, rows - 1)) != 0 )
      goto TERMINATE;

   /* Fetch solution information. */
   if ( (status = CPXXgetx (env, lp, x, 0, cols - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXXgetpi (env, lp, pi, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = getsocpconstrmultipliers (env, lp, dslack, socppi)) != 0 )
      goto TERMINATE;
   if ( (status = CPXXgetslack (env, lp, slack, 0, rows - 1)) != 0 )
      goto TERMINATE;
   if ( (status = CPXXgetqconstrslack (env, lp, qslack, 0, qcons - 1)) != 0 )
      goto TERMINATE;
   CPXXmsg (logc, "ok.\n");

   /* Print out the solution data we just fetched. */
   CPXXmsg (resc, "x      = [");
   for (j = 0; j < cols; ++j)
      CPXXmsg (resc, " %+7.3f", x[j]);
   CPXXmsg (resc, " ]\n");
   CPXXmsg (resc, "dslack = [");
   for (j = 0; j < cols; ++j)
      CPXXmsg (resc, " %+7.3f", dslack[j]);
   CPXXmsg (resc, " ]\n");
   CPXXmsg (resc, "pi     = [");
   for (i = 0; i < rows; ++i)
      CPXXmsg (resc, " %+7.3f", pi[i]);
   CPXXmsg (resc, " ]\n");
   CPXXmsg (resc, "slack  = [");
   for (i = 0; i < rows; ++i)
      CPXXmsg (resc, " %+7.3f", slack[i]);
   CPXXmsg (resc, " ]\n");
   CPXXmsg (resc, "socppi = [");
   for (q = 0; q < qcons; ++q)
      CPXXmsg (resc, " %+7.3f", socppi[q]);
   CPXXmsg (resc, " ]\n");
   CPXXmsg (resc, "qslack = [");
   for (q = 0; q < qcons; ++q)
      CPXXmsg (resc, " %+7.3f", qslack[q]);
   CPXXmsg (resc, " ]\n");

   /* Test primal feasibility. */
   CPXXmsg (logc, "Testing primal feasibility ... ");
   /* This example illustrates the use of dual vectors returned by CPLEX
    * to verify dual feasibility, so we do not test primal feasibility
    * here. */
   CPXXmsg (logc, "ok.\n");

   /* Test dual feasibility.
    * We must have
    * - for all <= constraints the respective pi value is non-negative,
    * - for all >= constraints the respective pi value is non-positive,
    * - since all quadratic constraints are <= constraints the socppi
    *   value must be non-negative for all quadratic constraints,
    * - the dslack value for all non-cone variables must be non-negative.
    * Note that we do not support ranged constraints here.
    */
   CPXXmsg (logc, "Testing dual feasibility ... ");
   for (i = 0; i < rows; ++i) {
      switch (sense[i]) {
      case 'L':
         if ( pi[i] < -tol ) {
            CPXXmsg (errc, "<= row %d has invalid dual multiplier %f.\n",
                     i, pi[i]);
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( pi[i] > tol ) {
            CPXXmsg (errc, ">= row %d has invalid dual multiplier %f.\n",
                     i, pi[i]);
            goto TERMINATE;
         }
         break;
      case 'E':
         /* Nothing to check here. */
         break;
      }
   }
   for (q = 0; q < qcons; ++q) {
      if ( socppi[q] < -tol ) {
         CPXXmsg (errc, "Quadratic constraint %d has invalid dual multiplier %f.\n",
                  q, socppi[q]);
         goto TERMINATE;
      }
   }
   for (j = 0; j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE && dslack[j] < -tol ) {
         CPXXmsg (errc, "dslack value for column %d is invalid: %f\n", j, dslack[j]);
         goto TERMINATE;
      }
   }
   CPXXmsg (logc, "ok.\n");

   /* Test complementary slackness.
    * For each constraint either the constraint must have zero slack or
    * the dual multiplier for the constraint must be 0. Again, we must
    * consider the special case in which a variable is not explicitly
    * contained in a second order cone constraint (conestat[j] == 0).
    */
   CPXXmsg (logc, "Testing complementary slackness ... ");
   for (i = 0; i < rows; ++i) {
      if ( fabs (slack[i]) > tol && fabs (pi[i]) > tol ) {
         CPXXmsg (errc, "Complementary slackness not satisfied for row %d (%f, %f)\n",
                  i, slack[i], pi[i]);
         goto TERMINATE;
      }
   }
   for (q = 0; q < qcons; ++q) {
      if ( fabs (qslack[q]) > tol && fabs (socppi[q]) > tol ) {
         CPXXmsg (errc, "Complementary slackness not satisfied for cone %d (%f, %f).\n",
                  q, qslack[q], socppi[q]);
         goto TERMINATE;
      }
   }
   for (j = 0; j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE ) {
         if ( fabs (x[j]) > tol && fabs (dslack[j]) > tol ) {
            CPXXmsg (errc, "Complementary slackness not satisfied for non-cone variable %f (%f, %f).\n",
                     j, x[j], dslack[j]);
            goto TERMINATE;
         }
      }
   }
   CPXXmsg (logc, "ok.\n");

   /* Test stationarity.
    * We must have
    *  c - g[i]'(X)*pi[i] = 0
    * where c is the objective function, g[i] is the i-th constraint of the
    * problem, g[i]'(x) is the derivate of g[i] with respect to x and X is the
    * optimal solution.
    * We need to distinguish the following cases:
    * - linear constraints g(x) = ax - b. The derivative of such a
    *   constraint is g'(x) = a.
    * - second order constraints g(x[1],...,x[n]) = -x[1] + |(x[2],...,x[n])|
    *   the derivative of such a constraint is
    *     g'(x) = (-1, x[2]/|(x[2],...,x[n])|, ..., x[n]/|(x[2],...,x[n])|
    *   (here |.| denotes the Euclidean norm).
    * - bound constraints g(x) = -x for variables that are not explicitly
    *   contained in any second order cone constraint. The derivative for
    *   such a constraint is g'(x) = -1.
    * Note that it may happen that the derivative of a second order cone
    * constraint is not defined at the optimal solution X (this happens if
    * X=0). In this case we just skip the stationarity test.
    */
   CPXXmsg (logc, "Testing stationarity ... ");
   /* Initialize sum = c. */
   if ( (status = CPXXgetobj (env, lp, sum, 0, cols - 1)) != 0 )
      goto TERMINATE;

   /* Handle linear constraints. */
   for (i = 0; i < rows; ++i) {
      CPXNNZ nz, surplus, beg;
      CPXNNZ n;

      status = CPXXgetrows (env, lp, &nz, &beg, ind, val, cols, &surplus,
                            i, i);
      if ( status != 0 )
         goto TERMINATE;
      for (n = 0; n < nz; ++n) {
         sum[ind[n]] -= pi[i] * val[n];
      }
   }
   /* Handle second order cone constraints. */
   for (q = 0; q < qcons; ++q) {
      double norm = 0.0;
      CPXNNZ n;

      if ( !getqconstr (env, lp, q, &qbuf) )
         goto TERMINATE;

      for (n = 0; n < qbuf.qnz; ++n) {
         if ( qbuf.qval[n] > 0 )
            norm += x[qbuf.qcol[n]] * x[qbuf.qcol[n]];
      }
      norm = sqrt (norm);
      if ( fabs (norm) <= tol ) {
         CPXXmsg (warnc, "WARNING: Cannot test stationarity at non-differentiable point.\n");
         skip = 1;
         break;
      }

      for (n = 0; n < qbuf.qnz; ++n) {
         if ( qbuf.qval[n] < 0 )
            sum[qbuf.qcol[n]] -= socppi[q];
         else
            sum[qbuf.qcol[n]] += socppi[q] * x[qbuf.qcol[n]] / norm;
      }
   }
   /* Handle variables that do not appear in any second order cone constraint.
    */
   for (j = 0; !skip && j < cols; ++j) {
      if ( cone[j] == NOT_IN_CONE ) {
         sum[j] -= dslack[j];
      }
   }

   /* Now test that all the entries in sum[] are 0.
    */
   for (j = 0; !skip && j < cols; ++j) {
      if ( fabs (sum[j]) > tol ) {
         CPXXmsg (errc, "Stationarity not satisfied at index %d: %f\n",
                  j, sum[j]);
         goto TERMINATE;
      }
   }
   CPXXmsg (logc, "ok.\n");

   CPXXmsg (logc, "KKT conditions are satisfied.\n");

   ok = 1;
 TERMINATE:
   if ( !ok )
      CPXXmsg (logc, "failed.\n");
   qbuf_clear (&qbuf);
   free (rhs);
   free (ind);
   free (val);
   free (sum);
   free (qslack);
   free (slack);
   free (sense);
   free (x);
   free (socppi);
   free (pi);
   free (dslack);

   return ok;
}
Beispiel #6
0
/* Read quadratic constraint number Q from LP and store it in qbuf.
 * The buffers in qbuf are expanded if they do not provide enough room
 * to store the constraint.
 * The function returns a true value on success and false if it fails.
 */
static int
getqconstr (CPXCENVptr env, CPXCLPptr lp, int q, qbuf_type *qbuf)
{
   int ok = 0;
   int status;
   CPXDIM linnz, linsurplus;
   CPXNNZ qnz, qsurplus;
   CPXCHANNELptr errc;

   /* Get the channel for printing error messages. */
   if ( (status = CPXXgetchannels (env, NULL, NULL, &errc, NULL)) != 0 )
      goto TERMINATE;

   /* Call CPXXgetqconstr() a first time with zero-length buffers to figure
    * how long the buffers must be.
    */
   status = CPXXgetqconstr (env, lp, &linnz, &qnz, NULL, NULL,
                            NULL, NULL, 0, &linsurplus,
                            NULL, NULL, NULL, 0, &qsurplus, q);
   if ( status != CPXERR_NEGATIVE_SURPLUS )
      goto TERMINATE;

   /* Adjust buffers for linear coefficients if necessary.
    */
   if ( linsurplus < 0 && -linsurplus >= qbuf->lcap ) {
      CPXDIM newcap = -linsurplus;

      if ( (qbuf->lind = realloc (qbuf->lind, newcap * sizeof (*qbuf->lind))) == NULL ||
           (qbuf->lval = realloc (qbuf->lval, newcap * sizeof (*qbuf->lval))) == NULL )
      {
         CPXXmsg (errc, "Out of memory!\n");
         goto TERMINATE;
      }
      qbuf->lcap = newcap;
   }

   /* Adjust buffers for quadratic coefficients if necessary.
    */
   if ( qsurplus < 0 && -qsurplus >= qbuf->qcap ) {
      CPXNNZ newcap = -qsurplus;

      if ( (qbuf->qcol = realloc (qbuf->qcol, newcap * sizeof (*qbuf->qcol))) == NULL ||
           (qbuf->qrow = realloc (qbuf->qrow, newcap * sizeof (*qbuf->qrow))) == NULL ||
           (qbuf->qval = realloc (qbuf->qval, newcap * sizeof (*qbuf->qval))) == NULL )
      {
         CPXXmsg (errc, "Out of memory!\n");
         goto TERMINATE;
      }
      qbuf->qcap = newcap;
   }

   /* Now all buffers in QBUF are long enough to store the constraint we
    * want to query. Copy the constraint into QBUF.
    */
   status = CPXXgetqconstr (env, lp, &qbuf->lnz, &qbuf->qnz, &qbuf->rhs,
                            &qbuf->sense,
                            qbuf->lind, qbuf->lval, qbuf->lcap, &linsurplus,
                            qbuf->qrow, qbuf->qcol, qbuf->qval, qbuf->qcap,
                            &qsurplus, q);
   if ( status != 0 )
      goto TERMINATE;

   ok = 1;
 TERMINATE:
   return ok;
}
Beispiel #7
0
static int 
rowsteel (int numprod, int tweeks, double const *rate,
          double const *inv0, double const *avail, double const *flatmarket,
          double const *prodcost, double const *invcost,
          double const *flatrevenue, CPXENVptr env, CPXLPptr lp)
{
   double *obj      = NULL;
   double *rhs      = NULL;
   char   *sense    = NULL;

   /* Row representation of the model */
   CPXNNZ *rmatbeg  = NULL;
   CPXDIM *rmatind  = NULL;
   double *rmatval  = NULL;

   CPXCHANNELptr  cpxerror = NULL;

   int    t,p;      /* Various loop counters */
   CPXDIM j;
   CPXNNZ k;

   int  status = 0;

   printf ("Building model by row.\n");

   status = CPXXgetchannels (env, NULL, NULL, &cpxerror, NULL);
   if ( status )  goto TERMINATE;

   /* Set the objective sense to be maximize */

   status = CPXXchgobjsen (env, lp, CPX_MAX);
   if ( status ) {
      CPXXmsg (cpxerror, "Could not change objective sense. Error %d\n",
               status);
      goto TERMINATE;
   }

   /* Set up the variables for the problem.  */

   /* Now fill in the bounds and the objective.  
    * For each variable type, we allocate a vector to hold the 
    * objective coefficients for one product, and multiple time
    * periods.  Note that we allocate and free the vector for
    * each decision variable type to maintain the locality of the code.
    */

   /* First, do the Make variables in (p,t) order.  The bounds are
    * (0, infinity), while the objective for Make[p][t] is -prodcost[p]
    */

   obj = malloc (tweeks*sizeof(*obj));
   if ( obj == NULL ) {
      status = -2;
      goto TERMINATE;
   }

   for (p = 0; p < numprod; p++) {
      for (t = 0; t < tweeks; t++) {
         obj[t]    = -prodcost[p];
      }
      status = CPXXnewcols (env, lp, tweeks, obj, NULL, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d. Error %d.\n",
                 "CPXXnewcols failed to add Make variables\n",
                 p, status);
         goto TERMINATE;
      }
   }

   /* Free up the obj array. */

   free (obj);  obj = NULL;
   
   /* Now do the Inv variables in (p,t) order. The bounds are
    * (0, infinity), while the objective for Inv[p][t] is -invcost[p]
    */

   obj = malloc (tweeks*sizeof(*obj));
   if ( obj == NULL ) {
      status = -2;
      goto TERMINATE;
   }

   for (p = 0; p < numprod; p++) {
      for (t = 0; t < tweeks; t++) {
         obj[t]    = -invcost[p];
      }
      status = CPXXnewcols (env, lp, tweeks, obj, NULL, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d. Error %d.\n",
                 "CPXXnewcols failed to add Inv variables\n",
                 p, status);
         goto TERMINATE;
      }
   }

   /* Free up the obj array. */

   free (obj);  obj = NULL;
   
   /* Now do the Sell[p][t] variables in (p,t) order.  The bounds on
    * Sell[p][t] are (0, flatmarket[p*tweeks+t]), and the objective
    * coefficient is flatrevenue[p*tweeks+t].  Because of this
    * structure, we do not need to allocate a temporary objective
    * coefficient array.  
    */

   for (p = 0; p < numprod; p++) {
      status = CPXXnewcols (env, lp, tweeks, &flatrevenue[p*tweeks], 
                           NULL, &flatmarket[p*tweeks], NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d. Error %d.\n",
                 "CPXXnewcols failed to add Make variables\n",
                 p, status);
         goto TERMINATE;
      }
   }

   /* Now generate the constraints.  */

   /* There are tweeks time constraints, each with numprod coefficients.
    * We allocate space for the sense values, and space to hold the matrix.
    * The rhs values are in avail, so there is no need to allocate rhs.
    */

   sense   = malloc (tweeks*sizeof(*sense));
   rmatbeg = malloc (tweeks*sizeof(*rmatbeg));
   rmatind = malloc (tweeks*numprod*sizeof(*rmatind));
   rmatval = malloc (tweeks*numprod*sizeof(*rmatval));

   if ( sense   == NULL ||
        rmatbeg == NULL ||
        rmatind == NULL ||
        rmatval == NULL   ) {
      status = -2;
      goto TERMINATE;
   }

   /* Now generate the time constraints.  The senses are all 'L'.
    * There are numprod  nonzeros in each of these constraints, 
    * with nonzero value (1/rate[p]) for variable Make[p][t]. */

   k = 0;   /* The count of the nonzeros */

   for (t = 0; t < tweeks; t++) {
      sense[t] = 'L';
      rmatbeg[t] = k;
      for (p = 0; p < numprod; p++) {
         rmatind[k] = p*tweeks + t;  /* Formula for Make variable */
         rmatval[k] = 1.0/rate[p];
         k++;
      }
   }

   status = CPXXaddrows (env, lp, 0, tweeks, k, avail, sense, 
                        rmatbeg, rmatind, rmatval, NULL, NULL);
   if ( status ) {
      CPXXmsg (cpxerror, 
              "CPXXaddrows failed to add time constraints. Error %d.\n",
              status);
      goto TERMINATE;
   }

   free (sense);   sense   = NULL;
   free (rmatbeg); rmatbeg = NULL;
   free (rmatind); rmatind = NULL;
   free (rmatval); rmatval = NULL;

   /* Allocate space for each product's balance constraints.  rhs
    * and sense are needed for each time period, and each constraint
    * balance[p,t] has 4 nonzeros. 
    */ 

   rhs     = malloc (tweeks*sizeof(*rhs));
   rmatbeg = malloc (tweeks*sizeof(*rmatbeg));
   rmatind = malloc (4*tweeks*sizeof(*rmatind));
   rmatval = malloc (4*tweeks*sizeof(*rmatval));

   if ( rhs     == NULL ||
        rmatbeg == NULL ||
        rmatind == NULL ||
        rmatval == NULL   ) {
      status = -2;
      goto TERMINATE;
   }

   /* Now generate the balance constraints.  Add rows by product.
    * The rhs values are -inv0[p] for the first time period,
    * and 0.0 otherwise.  The senses are all 'E'.
    * Handle t=0 specially.
    * Use order of variables Inv[t-1], Make, Inv[t], Sell so that
    * rmatind is in column order.
    */

   for (p = 0; p < numprod; p++) {
   
      k = 0;  /* Initialize the nonzero count for each product */

      for (t = 0; t < tweeks; t++) {
         rmatbeg[t] = k;
         if ( t > 0 ) {
            rhs[t]   = 0.0;
            j = (numprod+p)*tweeks + t-1;  /* Inv[p][t-1] index */
            rmatind[k] = j;
            rmatval[k] = 1.0;
            k++;
         }
         else {
            rhs[t] = -inv0[p];
         }
         rmatind[k] = p*tweeks + t;   /* Make[p][t] index */
         rmatval[k] = 1.0;
         k++;
         rmatind[k] = (numprod+p)*tweeks + t;  /* Inv[p][t] index */
         rmatval[k] = -1.0;
         k++;
         rmatind[k] = (2*numprod+p)*tweeks + t;  /* Sell[p][t] index */
         rmatval[k] = -1.0;
         k++;
      }
      /* Now add the rows to the problem */

      status = CPXXaddrows (env, lp, 0, tweeks, k, rhs, NULL, 
                           rmatbeg, rmatind, rmatval, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d. Error %d.\n",
                 "CPXXaddrows failed to add balance constraint\n",
                 p, status);
         goto TERMINATE;
      }
 
   }

   /* Free up arrays used to build balance constraints */

   free (rhs);     rhs   = NULL;
   free (rmatbeg); rmatbeg = NULL;  
   free (rmatind); rmatind = NULL;  
   free (rmatval); rmatval = NULL;  

TERMINATE:

   if ( rmatbeg != NULL )   free ( rmatbeg );
   if ( rmatind != NULL )   free ( rmatind );
   if ( rmatval != NULL )   free ( rmatval );
   if ( rhs     != NULL )   free ( rhs );
   if ( sense   != NULL )   free ( sense );
   if ( obj     != NULL )   free ( obj );

   return (status);

} /* END rowsteel */
Beispiel #8
0
static int 
colsteel (int numprod, int tweeks, double const *rate,
          double const *inv0, double const *avail, double const *flatmarket,
          double const *prodcost, double const *invcost,
          double const *flatrevenue, CPXENVptr env, CPXLPptr lp)
{
   double *obj      = NULL;
   char   *sense    = NULL;
   CPXNNZ *cmatbeg  = NULL;
   CPXDIM *cmatind  = NULL;
   double *cmatval  = NULL;
   double *lb       = NULL;
   double *ub       = NULL;

   CPXCHANNELptr  cpxerror = NULL;

   int    t,p;      /* Various loop counters */
   CPXNNZ k;

   int  status = 0;

   printf ("Building model by column.\n");

   status = CPXXgetchannels (env, NULL, NULL, &cpxerror, NULL);
   if ( status )  goto TERMINATE;

   /* Set the objective sense to be maximize */

   status = CPXXchgobjsen (env, lp, CPX_MAX);
   if ( status ) {
      CPXXmsg (cpxerror, "Could not change objective sense. Error %d\n",
               status);
      goto TERMINATE;
   }

   /* Set up the constraints for the problem */

   /* First do the time constraints.  Allocate space for a sense array. */

   sense = malloc (tweeks*sizeof(*sense));
   if ( sense == NULL ) {
      status = -2;
      goto TERMINATE;
   }

   for (t = 0; t < tweeks; t++) {
      sense[t] = 'L';
   }

   status = CPXXnewrows (env, lp, tweeks, avail, sense, NULL, NULL);
   if ( status ) {
      CPXXmsg (cpxerror, 
              "CPXXnewrows failed to add time constraints.  Error %d\n",
              status);
      goto TERMINATE;
   }

   /* Free up the temporary sense array */
   free (sense);  sense = NULL;

   /* Now do the balance constraints.  
      Can do this without temporary arrays, because the only nonzero
      is the negative of the inventory levels.  */

   for (p = 0; p < numprod; p++) {
      double  temprhs = -inv0[p];

      /* Fill in the initial inventory level */
      status   = CPXXnewrows (env, lp, 1, &temprhs, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d.  Error %d.\n", 
                 "CPXXnewrows failed to add initial inventory constraint\n",
                 p, status);
         goto TERMINATE;
      }

      /* The remaining balance constraints have 0.0 as the rhs value,
         and are equality constraints.  No need to specify the arrays */

      status   = CPXXnewrows (env, lp, tweeks-1, NULL, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d.  Error %d.\n", 
                 "CPXXnewrows failed to add other inventory constraints\n",
                 p, status);
         goto TERMINATE;
      }

   }

   /* Now, add the variables.  For each set of variables, we allocate
    * arrays to hold the data for the CPXXaddcols() calls, and then free
    * them up, so that each set of variables is maintained in a "local"
    * piece of code.
    */

   /* First, do the Make variables.  Each Make[p][t] variable has
    * objective coefficient -prodcost[p], and bounds of (0, +infinity).
    * Each Make[p][t] variable appears in the constraints 
    * time(t) and balance(p,t).
    * Create temporary arrays to hold the objective coefficients,
    * lower and upper bounds, and matrix coefficients for one product.
    */

   obj     = malloc (tweeks*sizeof(*obj));
   cmatbeg = malloc (tweeks*sizeof(*cmatbeg));
   cmatind = malloc (2*tweeks*sizeof(*cmatind));
   cmatval = malloc (2*tweeks*sizeof(*cmatval));

   if ( obj     == NULL ||
        cmatbeg == NULL ||
        cmatind == NULL ||
        cmatval == NULL   ) {
      status = -2;
      goto TERMINATE;
   }

   for (p = 0; p < numprod; p++) {
      k = 0;     /* Reset the nonzero count for each product */
      for (t = 0; t < tweeks; t++) {
         cmatbeg[t] = k;
         obj[t]     = -prodcost[p];
         cmatind[k] = t;
         cmatval[k] = 1.0/rate[p];
         k++;
         cmatind[k] = (p + 1)*tweeks + t;
         cmatval[k] = 1.0;
         k++;
      }
      status = CPXXaddcols (env, lp, tweeks, k, obj, cmatbeg, cmatind,
                           cmatval, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d.  Error %d.\n", 
                 "CPXXaddcols failed to add Make variables\n",
                 p, status);
         goto TERMINATE;
      }
   }

   /* Free up allocated memory used to add Make variables */

   free (obj);     obj = NULL;
   free (cmatbeg); cmatbeg = NULL;
   free (cmatind); cmatind = NULL;
   free (cmatval); cmatval = NULL;
   
   /* Now do the Inv variables in (p,t) order.  Each Inv[p][t] variable
    * has objective coefficient -invcost[p], and bounds of (0, +infinity).
    * If t is not tweeks-1, then Inv[p][t] appears in constraints
    *   balance(p,t) and balance(p,t+1).
    * If t is tweeks-1, then Inv[p][t] appears only in
    *   constraint balance(p,t).
    * Create temporary arrays to hold the objective coefficients,
    * lower and upper bounds, and matrix coefficients for one product.
    */

   obj     = malloc (tweeks*sizeof(*obj));
   cmatbeg = malloc (tweeks*sizeof(*cmatbeg));
   cmatind = malloc (2*tweeks*sizeof(*cmatind));
   cmatval = malloc (2*tweeks*sizeof(*cmatval));

   if ( obj     == NULL ||
        cmatbeg == NULL ||
        cmatind == NULL ||
        cmatval == NULL   ) {
      status = -2;
      goto TERMINATE;
   }

   for (p = 0; p < numprod; p++) {
      k = 0;   /* Reset the nonzero count for each product */
      for (t = 0; t < tweeks; t++) {
         obj[t]     = -invcost[p];
         cmatbeg[t] = k;
         cmatind[k] = (p+1)*tweeks + t;
         cmatval[k] = -1.0;
         k++;
         cmatind[k] = (p+1)*tweeks + t+1;
         cmatval[k] = 1.0;
         k++;
      }
      /* Now repair the coefficient for t=tweeks-1 by passing k-1 as
       * the number of nonzeros, so that the last coefficient is 
       * ignored. 
       */
      status = CPXXaddcols (env, lp, tweeks, k-1, obj, cmatbeg, cmatind,
                           cmatval, NULL, NULL, NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d.  Error %d.\n", 
                 "CPXXaddcols failed to add Inv variables\n",
                 p, status);
         goto TERMINATE;
      }
   }

   /* Free up allocated memory used to add Inv variables */

   free (obj);     obj = NULL;
   free (cmatbeg); cmatbeg = NULL;
   free (cmatind); cmatind = NULL;
   free (cmatval); cmatval = NULL;
   

   /* Now do the Sell[p][t] variables.  The objective coefficients of
    * Sell[p][t] is flatrevenue[p*tweeks+t], which means that
    * we can pull the objective coefficients from the slice of that
    * array.  The lower bounds are 0.0, and the upper bounds are
    * flatmarket[p*tweeks+t], which means that we can pull the
    * upper bounds from the slice of that array.  Each Sell variable
    * appears only in the balance(p,t) constraint. 
    * Create temporary arrays to hold the bounds, and matrix 
    * coefficients for one product.
    */

   cmatbeg = malloc (tweeks*sizeof(*cmatbeg));
   cmatind = malloc (tweeks*sizeof(*cmatind));
   cmatval = malloc (tweeks*sizeof(*cmatval));

   if ( cmatbeg == NULL ||
        cmatind == NULL ||
        cmatval == NULL   ) {
      status = -2;
      goto TERMINATE;
   }


   for (p = 0; p < numprod; p++) {
      k = 0;     /* Reset the nonzero count for each product */
      for (t = 0; t < tweeks; t++) {
         cmatbeg[t] = k;
         cmatind[k] = (p+1)*tweeks + t;
         cmatval[k] = -1.0;
         k++;
      }
      status = CPXXaddcols (env, lp, tweeks, k, &flatrevenue[p*tweeks], 
                           cmatbeg, cmatind, cmatval, NULL, 
                           &flatmarket[p*tweeks], NULL);
      if ( status ) {
         CPXXmsg (cpxerror, "%sfor product %d.  Error %d.\n", 
                 "CPXXaddcols failed to add Sell variables\n",
                 p, status);
         goto TERMINATE;
      }

   }

   /* Free up allocated memory used to add Sell variables */

   free (lb);      lb  = NULL;
   free (cmatbeg); cmatbeg = NULL;
   free (cmatind); cmatind = NULL;
   free (cmatval); cmatval = NULL;

TERMINATE:

   if ( obj     != NULL )   free ( obj );
   if ( sense   != NULL )   free ( sense );
   if ( cmatbeg != NULL )   free ( cmatbeg );
   if ( cmatind != NULL )   free ( cmatind );
   if ( cmatval != NULL )   free ( cmatval );
   if ( lb      != NULL )   free ( lb );
   if ( ub      != NULL )   free ( ub );

   return (status);

} /* END colsteel */
Beispiel #9
0
static int
steelt_opt_and_soln (void (CPXPUBLIC *errmsgfunc)(void *, const char *), 
                     void *handle, int numprod, int tweeks, double const *rate,
                     double const *inv0, double const *avail,
                     double const *flatmarket, double const *prodcost,
                     double const *invcost, double const *flatrevenue,
                     int *lpstat_p, double *objval_p, double *flatmake, 
                     double *flatinv, double *flatsell, int buildbycolumn)
{
   char const *probname = "steelT";

   CPXENVptr     env = NULL;
   CPXLPptr      lp  = NULL;
   CPXCHANNELptr cpxerror   = NULL;
   CPXCHANNELptr cpxwarning = NULL;
   int    status = 0;
   int    lpstat;
   double objval;


   env = CPXXopenCPLEX (&status);

   /* If an error occurs, the status value indicates the reason for
      failure.  We'll call the errmsgfunc with the error, because
      we can't call CPXXmsg if CPXXopenCPLEX failed.  */

   if ( env == NULL ) {
      char  errmsg[CPXMESSAGEBUFSIZE];
      if ( errmsgfunc != NULL ) {
         (*errmsgfunc) (handle, "Could not open CPLEX environment.\n");
         CPXXgeterrorstring (env, status, errmsg);
         (*errmsgfunc) (handle, errmsg);
      }
      goto TERMINATE;
   }

   status = CPXXgetchannels (env, NULL, &cpxwarning, &cpxerror, NULL);
   if ( status ) {
      if ( errmsgfunc != NULL ) {
         (*errmsgfunc) (handle, "Error in CPXXgetchannels.\n");
      }
      goto TERMINATE;
   }

   if ( errmsgfunc != NULL ) {
      status = CPXXaddfuncdest (env, cpxerror, handle, errmsgfunc);
      if ( !status ) {
	 status = CPXXaddfuncdest (env, cpxwarning, handle, errmsgfunc);
      }
   
      if ( status ) {
	 (*errmsgfunc) (handle, "Error in CPXXaddfuncdest.\n");
	 goto TERMINATE;
      }
   }

   /* Now that the channels have the error message function, we can
      use CPXXmsg for errors. */

   /* Create the problem */

   lp = CPXXcreateprob (env, &status, probname);

   if ( lp == NULL ) {
      status = -2;
      CPXXmsg (cpxerror, "Failed to create LP.\n");
      goto TERMINATE;
   }

   if ( buildbycolumn )
      status = colsteel (numprod, tweeks, rate, inv0, avail, flatmarket,
                         prodcost, invcost, flatrevenue, env, lp);
   else
      status = rowsteel (numprod, tweeks, rate, inv0, avail, flatmarket,
                         prodcost, invcost, flatrevenue, env, lp);

   if ( status )  goto TERMINATE;


   status = CPXXlpopt (env, lp);
   if ( status ) {
      CPXXmsg (cpxerror,"Optimization failed. Status %d.\n", status);
      goto TERMINATE;
   }

   status = CPXXsolution (env, lp, &lpstat, &objval, NULL, NULL, NULL, NULL);
   if ( status ) {
      CPXXmsg (cpxerror, "Solution failed. Status %d.\n", status); 
      goto TERMINATE;
   }

   *objval_p = objval;
   *lpstat_p = lpstat;

   /* Now get the solution.  Use the fact that the variables are
    * added in the order Make, Inv, Sell, and that the slices of
    * the x vector can be used to copy over the solution into
    * the flat... arrays. */

   /* Get the Make variables */
   status = CPXXgetx (env, lp, flatmake, 0, numprod*tweeks-1);
   if ( status ) {
      CPXXmsg (cpxerror, "CPXXgetx failed to get Make solution.  Status %d.\n",
              status);
      goto TERMINATE;
   }

   /* Get the Inv variables */
   status = CPXXgetx (env, lp, flatinv, numprod*tweeks, 2*numprod*tweeks-1);
   if ( status ) {
      CPXXmsg (cpxerror, "CPXXgetx failed to get Inv solution.  Status %d.\n",
              status);
      goto TERMINATE;
   }

   /* Get the Sell variables */
   status = CPXXgetx (env, lp, flatsell, 2*numprod*tweeks, 
                     3*numprod*tweeks-1);
   if ( status ) {
      CPXXmsg (cpxerror, "CPXXgetx failed to get Sell solution.  Status %d.\n",
              status);
      goto TERMINATE;
   }

TERMINATE:
   if ( lp != NULL )   CPXXfreeprob (env, &lp);

   if ( errmsgfunc != NULL && env != NULL ) {
      int  dfstat;
      dfstat = CPXXdelfuncdest (env, cpxerror, handle, errmsgfunc);
      if ( !dfstat ) {
         dfstat = CPXXdelfuncdest (env, cpxwarning, handle, errmsgfunc);
      }
      if ( dfstat ) {
         (*errmsgfunc) (handle, "CPXXdelfuncdest failed.\n");
      }
      if ( dfstat && !status )  status = dfstat;
   }
   
   /* Free up the CPLEX environment, if necessary */

   if ( env != NULL ) {
      int  closestat;
      closestat = CPXXcloseCPLEX (&env);

      if ( closestat && errmsgfunc != NULL) {
      char  errmsg[CPXMESSAGEBUFSIZE];
         (*errmsgfunc) (handle, "Could not close CPLEX environment.\n");
         CPXXgeterrorstring (env, closestat, errmsg);
         (*errmsgfunc) (handle, errmsg);
      }
      if ( closestat && !status ) status = closestat;
   }

   return (status);

} /* END steelt_opt_and_soln */