예제 #1
0
static int
create_master_ILP   (CPXENVptr env, CPXLPptr lp, double **arc_cost, 
                     CPXDIM num_nodes)
{
   CPXDIM i, j;
   int status = 0;
  
   char sense;
   char const *colname = NULL;
   CPXNNZ nzcnt, rmatbeg;
   CPXDIM *rmatind = NULL;
   double rhs, *rmatval = NULL;
   
   
   /* Change problem type */
   
   status = CPXXchgprobtype (env, lp, CPXPROB_MILP);
   if ( status ) {
      fprintf (stderr, "Error in CPXXchgprobtype, status = %d.\n", status);
      goto TERMINATE;
   }

   /* Create arc variables x(i,j), one per time 
      For simplicity, also dummy variables x(i,i) are created.
      Those variables are fixed to 0 and do not partecipate to 
      the constraints */
   
   colname = malloc (100 * sizeof(*colname));
   if ( colname == NULL ) {
      fprintf (stderr, "No memory for colname array.\n");
      status = -1;
      goto TERMINATE;
   }
   for (i = 0; i < num_nodes; ++i) {
      for (j = 0; j < num_nodes; ++j) {
         double cost = ( i == j ? 0. : arc_cost[i][j] );
         double lb   = 0.;
         double ub   = ( i == j ? 0. : 1. );
         char type   = 'B';
         sprintf ((char *)colname, "x.%d.%d", i, j);
         status = CPXXnewcols (env, lp, 1, &cost, &lb, &ub, &type, &colname);
         if ( status ) {
            fprintf (stderr, "Error in CPXXnewcols, status = %d.\n", status);
            goto TERMINATE;
         }
      }
   }

   /* Init data structures to add degree constraints */
   
   rhs = 1.;
   sense = 'E';
   rmatbeg = 0;
   rmatind = malloc ((num_nodes-1) * sizeof (*rmatind));
   if ( rmatind == NULL ) {
      fprintf (stderr, "No memory for rmatind array.\n");
      status = -1;
      goto TERMINATE;
   }
   rmatval = malloc ((num_nodes-1) * sizeof (*rmatval));
   if ( rmatval == NULL ) {
      fprintf (stderr, "No memory for rmatval array.\n");
      status = -1;
      goto TERMINATE;
   }

   /* Add the out degree constraints, one at a time:
      forall i in V: sum((i,j) in delta+(i)) x(i,j) = 1 */

   for (i = 0; i < num_nodes; ++i) {
      nzcnt = 0;
      for (j = 0; j < num_nodes; ++j) {
         if ( i != j ) {
            rmatind[nzcnt]   = i * num_nodes + j;
            rmatval[nzcnt++] = 1.;
         }
      }
      status = CPXXaddrows (env, lp, 0, 1, num_nodes-1, &rhs, &sense,
                            &rmatbeg, rmatind, rmatval, NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Error in CPXXaddrows, status = %d.\n", status);
         goto TERMINATE;
      }
   }
   
   /* Add the in degree constraints, one at a time:
      forall i in V: sum((j,i) in delta-(i)) x(j,i) = 1 */

   for (i = 0; i < num_nodes; ++i) {
      nzcnt = 0;
      for (j = 0; j < num_nodes; ++j) {
         if (i != j ) {
            rmatind[nzcnt]   = j * num_nodes + i;
            rmatval[nzcnt++] = 1.;
         }
      }
      status = CPXXaddrows (env, lp, 0, 1, num_nodes-1, &rhs, &sense,
                            &rmatbeg, rmatind, rmatval, NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Error in CPXXaddrows, status = %d.\n", status);
         goto TERMINATE;
      }
   }

TERMINATE:

   free_and_null ((char **) &colname);
   free_and_null ((char **) &rmatind);
   free_and_null ((char **) &rmatval);
   
   return status;

} /* END create_master_ILP */
예제 #2
0
int
main (void)
{
   int status;
   CPXENVptr env;
   CPXLPptr lp;
   CPXDIM i;
   double x[NUMCOLS];
   double cpi[NUMCOLS];
   double rpi[NUMROWS];
   double qpi[NUMQS];
   double slack[NUMROWS], qslack[NUMQS];
   double kktsum[NUMCOLS];

   /* ********************************************************************** *
    *                                                                        *
    *    S E T U P   P R O B L E M                                           *
    *                                                                        *
    * ********************************************************************** */

   /* Create CPLEX environment and enable screen output.
    */
   env = CPXXopenCPLEX (&status);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXsetintparam (env, CPXPARAM_ScreenOutput, CPX_ON);
   if ( status != 0 )
      goto TERMINATE;

   /* Create the problem object and populate it.
    */
   lp = CPXXcreateprob (env, &status, "qcpdual");
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXnewcols (env, lp, NUMCOLS, obj, lb, ub, NULL, cname);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXaddrows (env, lp, 0, NUMROWS, NUMNZS, rhs, sense,
                         rmatbeg, rmatind, rmatval, NULL, rname);
   if ( status != 0 )
      goto TERMINATE;
   for (i = 0; i < NUMQS; ++i) {
      CPXNNZ const linend = (i == NUMQS - 1) ? NUMLINNZ : linbeg[i + 1];
      CPXNNZ const quadend = (i == NUMQS - 1) ? NUMQUADNZ : quadbeg[i + 1];

      status = CPXXaddqconstr (env, lp, linend - linbeg[i],
                               quadend - quadbeg[i], qrhs[i], qsense[i],
                               &linind[linbeg[i]], &linval[linbeg[i]],
                               &quadrow[quadbeg[i]], &quadcol[quadbeg[i]],
                               &quadval[quadbeg[i]], qname[i]);
      if ( status != 0 )
         goto TERMINATE;
   }

   /* ********************************************************************** *
    *                                                                        *
    *    O P T I M I Z E   P R O B L E M                                     *
    *                                                                        *
    * ********************************************************************** */
   status = CPXXsetdblparam (env, CPXPARAM_Barrier_QCPConvergeTol, 1e-10);
   if ( status != 0 )
      goto TERMINATE;

   /* Solve the problem.
    */
   status = CPXXbaropt (env, lp);
   if ( status != 0 )
      goto TERMINATE;

   if ( CPXXgetstat (env, lp) != CPX_STAT_OPTIMAL ) {
      fprintf (stderr, "No optimal solution found!\n");
      goto TERMINATE;
   }

   /* ********************************************************************** *
    *                                                                        *
    *    Q U E R Y   S O L U T I O N                                         *
    *                                                                        *
    * ********************************************************************** */

   /* Optimal solution and slacks for linear and quadratic constraints. */
   status = CPXXgetx (env, lp, x, 0, NUMCOLS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXgetslack (env, lp, slack, 0, NUMROWS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXgetqconstrslack (env, lp, qslack, 0, NUMQS - 1);
   if ( status != 0 )
      goto TERMINATE;
   /* Dual multipliers for linear constraints and bound constraints. */
   status = CPXXgetdj (env, lp, cpi, 0, NUMCOLS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = CPXXgetpi (env, lp, rpi, 0, NUMROWS - 1);
   if ( status != 0 )
      goto TERMINATE;
   status = getqconstrmultipliers (env, lp, x, qpi, ZEROTOL);
   if ( status != 0 )
      goto TERMINATE;

   /* ********************************************************************** *
    *                                                                        *
    *    C H E C K   K K T   C O N D I T I O N S                             *
    *                                                                        *
    *    Here we verify that the optimal solution computed by CPLEX (and     *
    *    the qpi[] values computed above) satisfy the KKT conditions.        *
    *                                                                        *
    * ********************************************************************** */

   /* Primal feasibility: This example is about duals so we skip this test. */

   /* Dual feasibility: We must verify
    * - for <= constraints (linear or quadratic) the dual
    *   multiplier is non-positive.
    * - for >= constraints (linear or quadratic) the dual
    *   multiplier is non-negative.
    */
   for (i = 0; i < NUMROWS; ++i) {
      switch (sense[i]) {
      case 'E': /* nothing */ break;
      case 'R': /* nothing */ break;
      case 'L':
         if ( rpi[i] > ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for <= row %d: %f\n",
                     i, rpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( rpi[i] < -ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for >= row %d: %f\n",
                     i, rpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      }
   }
   for (i = 0; i < NUMQS; ++i) {
      switch (qsense[i]) {
      case 'E': /* nothing */ break;
      case 'L':
         if ( qpi[i] > ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for <= quad %d: %f\n",
                     i, qpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      case 'G':
         if ( qpi[i] < -ZEROTOL ) {
            fprintf (stderr,
                     "Dual feasibility test failed for >= quad %d: %f\n",
                     i, qpi[i]);
            status = -1;
            goto TERMINATE;
         }
         break;
      }
   }

   /* Complementary slackness.
    * For any constraint the product of primal slack and dual multiplier
    * must be 0.
    */
   for (i = 0; i < NUMROWS; ++i) {
      if ( sense[i] != 'E' && fabs (slack[i] * rpi[i]) > ZEROTOL ) {
         fprintf (stderr,
                  "Complementary slackness test failed for row %d: %f\n",
                  i, fabs (slack[i] * rpi[i]));
         status = -1;
         goto TERMINATE;
      }
   }
   for (i = 0; i < NUMQS; ++i) {
      if ( qsense[i] != 'E' && fabs (qslack[i] * qpi[i]) > ZEROTOL ) {
         fprintf (stderr,
                  "Complementary slackness test failed for quad %d: %f\n",
                  i, fabs (qslack[i] * qpi[i]));
         status = -1;
         goto TERMINATE;
      }
   }
   for (i = 0; i < NUMCOLS; ++i) {
      if ( ub[i] < CPX_INFBOUND ) {
         double const slk = ub[i] - x[i];
         double const dual = cpi[i] < -ZEROTOL ? cpi[i] : 0.0;
         if ( fabs (slk * dual) > ZEROTOL ) {
            fprintf (stderr,
                     "Complementary slackness test failed for ub %d: %f\n",
                     i, fabs (slk * dual));
            status = -1;
            goto TERMINATE;
         }
      }
      if ( lb[i] > -CPX_INFBOUND ) {
         double const slk = x[i] - lb[i];
         double const dual = cpi[i] > ZEROTOL ? cpi[i] : 0.0;
         if ( fabs (slk * dual) > ZEROTOL ) {
            printf ("lb=%f, x=%f, cpi=%f\n", lb[i], x[i], cpi[i]);
            fprintf (stderr,
                     "Complementary slackness test failed for lb %d: %f\n",
                     i, fabs (slk * dual));
            status = -1;
            goto TERMINATE;
         }
      }
   }

   /* Stationarity.
    * The difference between objective function and gradient at optimal
    * solution multiplied by dual multipliers must be 0, i.e., for the
    * optimal solution x
    * 0 == c
    *      - sum(r in rows)  r'(x)*rpi[r]
    *      - sum(q in quads) q'(x)*qpi[q]
    *      - sum(c in cols)  b'(x)*cpi[c]
    * where r' and q' are the derivatives of a row or quadratic constraint,
    * x is the optimal solution and rpi[r] and qpi[q] are the dual
    * multipliers for row r and quadratic constraint q.
    * b' is the derivative of a bound constraint and cpi[c] the dual bound
    * multiplier for column c.
    */

   /* Objective function. */
   for (i = 0; i < NUMCOLS; ++i)
      kktsum[i] = obj[i];

   /* Linear constraints.
    * The derivative of a linear constraint ax - b (<)= 0 is just a.
    */
   for (i = 0; i < NUMROWS; ++i) {
      CPXNNZ const end = (i == NUMROWS - 1) ? NUMNZS : rmatbeg[i + 1];
      CPXNNZ k;

      for (k = rmatbeg[i]; k < end; ++k)
         kktsum[rmatind[k]] -= rpi[i] * rmatval[k];
   }

   /* Quadratic constraints.
    * The derivative of a constraint xQx + ax - b <= 0 is
    * Qx + Q'x + a.
    */
   for (i = 0; i < NUMQS; ++i) {
      CPXDIM j;
      CPXNNZ k;

      for (j = linbeg[i]; j < linbeg[i] + linnzcnt[i]; ++j)
         kktsum[linind[j]] -= qpi[i] * linval[j];
      for (k = quadbeg[i]; k < quadbeg[i] + quadnzcnt[i]; ++k) {
         kktsum[quadrow[k]] -= qpi[i] * x[quadcol[k]] * quadval[k];
         kktsum[quadcol[k]] -= qpi[i] * x[quadrow[k]] * quadval[k];
      }
   }

   /* Bounds.
    * The derivative for lower bounds is -1 and that for upper bounds
    * is 1.
    * CPLEX already returns dj with the appropriate sign so there is
    * no need to distinguish between different bound types here.
    */
   for (i = 0; i < NUMCOLS; ++i) {
      kktsum[i] -= cpi[i];
   }

   for (i = 0; i < NUMCOLS; ++i) {
      if ( fabs (kktsum[i]) > ZEROTOL ) {
         fprintf (stderr, "Stationarity test failed at index %d: %f\n",
                  i, kktsum[i]);
         status = -1;
         goto TERMINATE;
      }
   }

   /* KKT conditions satisfied. Dump out the optimal solutions and
    * the dual values.
    */
   printf ("Optimal solution satisfies KKT conditions.\n");
   printf ("  x[] =");
   for (i = 0; i < NUMCOLS; ++i)
      printf (" %7.3f", x[i]);
   printf ("\n");
   printf ("cpi[] =");
   for (i = 0; i < NUMCOLS; ++i)
      printf (" %7.3f", cpi[i]);
   printf ("\n");
   printf ("rpi[] =");
   for (i = 0; i < NUMROWS; ++i)
      printf (" %7.3f", rpi[i]);
   printf ("\n");
   printf ("qpi[] =");
   for (i = 0; i < NUMQS; ++i)
      printf (" %7.3f", qpi[i]);
   printf ("\n");
   
 TERMINATE:
   /* ********************************************************************** *
    *                                                                        *
    *    C L E A N U P                                                       *
    *                                                                        *
    * ********************************************************************** */

   status = CPXXfreeprob (env, &lp);
   if ( status != 0 ) {
      fprintf (stderr, "WARNING: Failed to free problem: %d\n", status);
   }
   status = CPXXcloseCPLEX (&env);
   if ( status != 0 ) {
      fprintf (stderr, "WARNING: Failed to close CPLEX: %d\n", status);
   }

   return status;
}
예제 #3
0
static int
init_user_cbhandle  (USER_CBHANDLE *user_cbhandle, CPXDIM num_nodes, 
                     int separate_fractional_solutions)
{
   CPXDIM i, j, k;
   int status = 0;
   
   /* Data structures to create columns and add rows */

   CPXDIM num_rows;
   CPXNNZ nzcnt;
   char *sense = NULL;
   double *rhs = NULL;
   CPXNNZ *rmatbeg = NULL;
   CPXDIM *rmatind = NULL;
   double *rmatval = NULL;
   char const *colname = NULL;

   /* Init user_cbhandle */

   user_cbhandle->separate_fractional_solutions = separate_fractional_solutions;
   user_cbhandle->num_nodes  = num_nodes;
   user_cbhandle->num_x_cols = num_nodes * num_nodes;
   user_cbhandle->num_v_cols = (num_nodes - 1) * user_cbhandle->num_x_cols;
   user_cbhandle->num_u_cols = (num_nodes - 1) * num_nodes;
   user_cbhandle->env        = NULL;
   user_cbhandle->lp         = NULL;
   user_cbhandle->x          = NULL;
   user_cbhandle->indices    = NULL;
   user_cbhandle->ray        = NULL;
   user_cbhandle->cutval     = NULL;
   user_cbhandle->cutind     = NULL;
   
   user_cbhandle->x = malloc (user_cbhandle->num_x_cols *
                              sizeof(*user_cbhandle->x));
   if ( user_cbhandle->x == NULL ) {
      fprintf (stderr, "No memory for x array.\n");
      goto TERMINATE;
   }
   user_cbhandle->indices = malloc (user_cbhandle->num_x_cols *
                                    sizeof(*user_cbhandle->indices));
   if ( user_cbhandle->indices == NULL ) {
      fprintf (stderr, "No memory for indices array.\n");
      goto TERMINATE;
   }
   user_cbhandle->ray = malloc ((user_cbhandle->num_v_cols +
                                 user_cbhandle->num_u_cols) *
                                sizeof(*user_cbhandle->ray));
   if ( user_cbhandle->ray == NULL ) {
      fprintf (stderr, "No memory for ray array.\n");
      goto TERMINATE;
   }
   user_cbhandle->cutval = malloc (user_cbhandle->num_x_cols *
                                   sizeof(*user_cbhandle->cutval));
   if ( user_cbhandle->cutval == NULL ) {
      fprintf (stderr, "No memory for cutval array.\n");
      goto TERMINATE;
   }
   user_cbhandle->cutind = malloc (user_cbhandle->num_x_cols *
                                   sizeof(*user_cbhandle->cutind));
   if ( user_cbhandle->cutind == NULL ) {
      fprintf (stderr, "No memory for cutind array.\n");
      goto TERMINATE;
   }
   
   /* Create the environment for the worker LP */

   user_cbhandle->env = CPXXopenCPLEX (&status);
   if ( user_cbhandle->env == NULL ) {
      fprintf (stderr, 
         "Could not open CPLEX environment for the worker LP: status = %d.\n", 
         status); 
      goto TERMINATE;
   }

   /* Turn off the presolve reductions */
   
   status = CPXXsetintparam (user_cbhandle->env, CPXPARAM_Preprocessing_Reduce, 0);
   if ( status ) {
      fprintf(stderr, 
         "Failed to set CPXPARAM_Preprocessing_Reduce, status = %d.\n", status);
      goto TERMINATE;
   }

   /* Create the worker LP */

   user_cbhandle->lp = CPXXcreateprob (user_cbhandle->env, &status, "atsp_worker.lp");
   if ( user_cbhandle->lp == NULL ) {
      fprintf (stderr, "Failed to create the worker LP: status = %d\n", status);
      goto TERMINATE;
   }

   /* Allocate memory for column names */

   colname = malloc (100 * sizeof(*colname));
   if ( colname == NULL ) {
      fprintf (stderr, "No memory for colname array.\n");
      status = -1;
      goto TERMINATE;
   } 
   
   /* Create variables v(k,i,j), one per time 
      For simplicity, also dummy variables v(k,i,i) are created.
      Those variables are fixed to 0 and do not partecipate to 
      the constraints */
   
   for (k = 1; k < num_nodes; ++k) {
      for (i = 0; i < num_nodes; ++i) {
         for (j = 0; j < num_nodes; ++j) {
            double ub = ( i == j ? 0. : CPX_INFBOUND );
            sprintf ((char *)colname, "v.%d.%d.%d", k, i, j);
            status = CPXXnewcols (user_cbhandle->env, user_cbhandle->lp, 1, 
                                  NULL, NULL, &ub, NULL, &colname);
            if ( status ) {
               fprintf (stderr, "Error in CPXXnewcols, status = %d.\n", status);
               goto TERMINATE;
            }
         }
      }
   }

   /* Create variables u(k,i), one per time */
   
   for (k = 1; k < num_nodes; ++k) {
      for (i = 0; i < num_nodes; ++i) {
         double obj = 0.;
         double lb = -CPX_INFBOUND;
         double ub = CPX_INFBOUND;
         sprintf ((char *)colname, "u.%d.%d", k, i);
         if ( i == 0 )
            obj = -1.;
         else if ( i == k )
            obj = 1.;
         status = CPXXnewcols (user_cbhandle->env, user_cbhandle->lp, 1, 
                               &obj, &lb, &ub, NULL, &colname); 
         if ( status ) {
            fprintf (stderr, "Error in CPXXnewcols, status = %d.\n", status);
            goto TERMINATE;
         }
      }
   }

   /* Init data structures for CPXaddrows */
   
   num_rows = user_cbhandle->num_x_cols * (num_nodes - 1);

   rhs = malloc (num_rows * sizeof (*rhs));
   if ( rhs == NULL ) {
      fprintf (stderr, "No memory for rhs array.\n");
      status = -1;
      goto TERMINATE;
   }
   sense = malloc (num_rows * sizeof (*sense));
   if ( sense == NULL ) {
      fprintf (stderr, "No memory for sense array.\n");
      status = -1;
      goto TERMINATE;
   }
   rmatbeg = malloc ( (num_rows + 1) * sizeof (*rmatbeg));
   if ( rmatbeg == NULL ) {
      fprintf (stderr, "No memory for rmatbeg array.\n");
      status = -1;
      goto TERMINATE;
   }
   rmatind = malloc (3 * num_rows * sizeof (*rmatind));
   if ( rmatind == NULL ) {
      fprintf (stderr, "No memory for rmatind array.\n");
      status = -1;
      goto TERMINATE;
   }
   rmatval = malloc (3 * num_rows * sizeof (*rmatval));
   if ( rmatval == NULL ) {
      fprintf (stderr, "No memory for rmatval array.\n");
      status = -1;
      goto TERMINATE;
   }

   /* Populate data structures for CPXaddrows and add all the constraints:
      forall k in V0, forall (i,j) in A: u(k,i) - u(k,j) <= v(k,i,j) */
   
   num_rows = 0;
   nzcnt = 0;
   for (k = 1; k < num_nodes; ++k) {
      for (i = 0; i < num_nodes; ++i) {
         for (j = 0; j < num_nodes; ++j) {
            if ( i != j ) {
               rhs[num_rows]     =  0.;
               sense[num_rows]   =  'L';
               rmatbeg[num_rows] =  nzcnt;
               rmatind[nzcnt]    =  (k-1) * user_cbhandle->num_x_cols +
                                    i * num_nodes + j;
               rmatval[nzcnt++]  = -1.;
               rmatind[nzcnt]    =  user_cbhandle->num_v_cols + 
                                    (k-1) * num_nodes + i; 
               rmatval[nzcnt++]  =  1.;
               rmatind[nzcnt]    =  user_cbhandle->num_v_cols + 
                                    (k-1) * num_nodes + j; 
               rmatval[nzcnt++]  = -1.;
               ++num_rows;
            }
         }
      }
   }
   rmatbeg[num_rows] = nzcnt; 

   status = CPXXaddrows (user_cbhandle->env, user_cbhandle->lp, 
                         0, num_rows, nzcnt, rhs, sense, 
                         rmatbeg, rmatind, rmatval, NULL, NULL);
   if ( status ) {
      fprintf (stderr, "Error in CPXXaddrows: status = %d\n", status);
      goto TERMINATE;
   }

TERMINATE:

   free_and_null ((char **) &colname); 
   free_and_null ((char **) &sense);
   free_and_null ((char **) &rhs);
   free_and_null ((char **) &rmatbeg);
   free_and_null ((char **) &rmatind);
   free_and_null ((char **) &rmatval);

   return status;

} /* END init_user_cbhandle */
예제 #4
0
파일: xfoodmanu.c 프로젝트: renvieir/ioc
static int
buildmodel (CPXENVptr env, CPXLPptr lp)
{

   CPXDIM colcnt = NUMVARS*NUMMONTHS*NUMPRODUCTS;

   double *obj     = NULL;
   double *lb      = NULL;
   double *ub      = NULL;
   char   *ctype   = NULL;
   CPXDIM *rmatind = NULL;
   double *rmatval = NULL;

   CPXDIM indicator;
   CPXNNZ rmatbeg[1];
   double rhs[1];
   char   sense[1];

   int    m, p;
   int    status = 0;

   status = CPXXchgobjsen (env, lp, CPX_MAX); /* Maximization problem */
   if ( status ) {
      fprintf (stderr, "Could not change objective sense.\n");
      goto TERMINATE;
   }

   rmatbeg[0] = 0;

   /* Allocate colcnt-sized arrays */

   obj     = malloc (colcnt * sizeof(*obj));
   lb      = malloc (colcnt * sizeof(*lb));
   ub      = malloc (colcnt * sizeof(*ub));
   ctype   = malloc (colcnt * sizeof(*ctype));
   rmatind = malloc (colcnt * sizeof(*rmatind));
   rmatval = malloc (colcnt * sizeof(*rmatval));

   if ( obj     == NULL ||
        lb      == NULL ||
        ub      == NULL ||
        ctype   == NULL ||
        rmatind == NULL ||
        rmatval == NULL   ) {
      fprintf (stderr, "Could not allocate colcnt arrays\n");
      status = CPXERR_NO_MEMORY;
      goto TERMINATE;
   }

   /* Create variables. For each month and each product, we have 3
      variables corresponding to the quantity used (semi-continuous),
      stored (continuous) and bought (continuous) and one binary
      variable indicating whether or not the product is used during
      this month. */

   for (m = 0; m < NUMMONTHS; m++) {
      for (p = 0; p < NUMPRODUCTS; p++) {

         /* The quantity bought is a continuous variable. It has a cost */

         obj[varindex(m, p, BUY)]    = -cost[m*NUMPRODUCTS + p];
         lb[varindex (m, p, BUY)]    = 0.0;
         ub[varindex (m, p, BUY)]    = CPX_INFBOUND;
         ctype[varindex (m, p, BUY)] = 'C';

         /* When an oil is used, the quantity must be at least 20
            tons. This is modeled as a semi-continuous variable. */

         obj[varindex (m, p, USE)]   = 0.0;
         lb[varindex (m, p, USE)]    = 20.0;
         ub[varindex (m, p, USE)]    = CPX_INFBOUND;
         ctype[varindex (m, p, USE)] = 'S';

         /* It is possible to store up to 1000 tons of each
            product. There are storage costs. */

         obj[varindex (m, p, STORE)]   = -5.0;
         lb[varindex (m, p, STORE)]    = 0.0;
         ub[varindex (m, p, STORE)]    = 1000.0;
         ctype[varindex (m, p, STORE)] = 'C';

         /* At the end, we must have exactly 500 tons of each
            product in storage. */

         if ( m == NUMMONTHS - 1 ) {
            lb[varindex (m, p, STORE)] = 500.0;
            ub[varindex (m, p, STORE)] = 500.0;
         }

         /* The variable indicating whether or not a product is
            used during a month is a binary variable. */

         obj[varindex (m, p, IS_USED)]   = 0.0;
         lb[varindex (m, p, IS_USED)]    = 0.0;
         ub[varindex (m, p, IS_USED)]    = 1.0;
         ctype[varindex (m, p, IS_USED)] = 'B';
      }
   }

   status = CPXXnewcols (env, lp, colcnt, obj, lb, ub, ctype, NULL);
   if ( status ) {
      fprintf (stderr, "Could not add new columns.\n");
      goto TERMINATE;
   }

   /* Constraints for each month */

   for (m = 0; m < NUMMONTHS; m++) {
      int totalindex;

      /* For each product, create an indicator constraint linking the
         quantity used and the binary variable indicating whether or
         not the product is used */

      for (p = 0; p < NUMPRODUCTS; p++) {
         indicator  = varindex (m, p, IS_USED);
         rmatind[0] = varindex (m, p, USE);
         rmatval[0] = 1.0;
         status = CPXXaddindconstr (env, lp, indicator, 1, 1, 0.0, 'L',
                                   rmatind, rmatval, NULL);
         if ( status ) {
            fprintf (stderr, "Could not add new indicator constraint.\n");
            goto TERMINATE;
         }
      }

      /* Not more than 200 tons of vegetable oil can be refined */

      rmatind[0] = varindex (m, VEGOIL1, USE);
      rmatind[1] = varindex (m, VEGOIL2, USE);
      rmatval[0] = 1.0;
      rmatval[1] = 1.0;
      rhs[0]     = 200.0;
      sense[0]   = 'L';
      status = CPXXaddrows (env, lp, 0, 1, 2, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);

      /* Not more than 250 tons of non-vegetable oil can be refined */

      rmatind[0] = varindex (m, OIL1, USE);
      rmatind[1] = varindex (m, OIL2, USE);
      rmatind[2] = varindex (m, OIL3, USE);
      rmatval[0] = 1.0;
      rmatval[1] = 1.0;
      rmatval[2] = 1.0;
      rhs[0]     = 250.0;
      sense[0]   = 'L';
      status = CPXXaddrows (env, lp, 0, 1, 3, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new rows.\n");
         goto TERMINATE;
      }

      /* Constraint on food composition */

      /* Add a variable corresponding to total quantity produced
         in a month */

      obj[0]   = 150.0;
      lb[0]    = 0.0;
      ub[0]    = CPX_INFBOUND;
      ctype[0] = 'C';

      status = CPXXnewcols (env, lp, 1, obj, lb, ub, ctype, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new columns.\n");
         goto TERMINATE;
      }
      totalindex = CPXXgetnumcols (env, lp) - 1;

      /* Total quantity = sum (quantities) */

      for (p = 0; p < NUMPRODUCTS; p++) {
         rmatind[p] = varindex (m, p, USE);
         rmatval[p] = 1.0;
      }
      rmatind[NUMPRODUCTS] = totalindex;
      rmatval[NUMPRODUCTS] = -1.0;
      rhs[0]               =  0.0;
      sense[0]             = 'E';
      status = CPXXaddrows (env, lp, 0, 1, NUMPRODUCTS + 1, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new rows.\n");
         goto TERMINATE;
      }

      /* Hardness constraints
          sum (quantity * hardness) >= 3 * total quantity
          sum (quantity * hardness) <= 6 * total quantity
      */

      for (p = 0; p < NUMPRODUCTS; p++) {
         rmatind[p] = varindex (m, p, USE);
         rmatval[p] = hardness[p];
      }
      rmatind[NUMPRODUCTS] = totalindex;
      rmatval[NUMPRODUCTS] = -3.0;
      rhs[0]               =  0.0;
      sense[0]             = 'G';
      status = CPXXaddrows (env, lp, 0, 1, NUMPRODUCTS + 1, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new rows.\n");
         goto TERMINATE;
      }

      rmatval[NUMPRODUCTS] = -6.0;
      sense[0]             = 'L';
      status = CPXXaddrows (env, lp, 0, 1, NUMPRODUCTS + 1, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new rows.\n");
         goto TERMINATE;
      }

      /* The food may never be made up of more than three oils */

      for (p = 0; p < NUMPRODUCTS; p++) {
         rmatind[p] = varindex (m, p, IS_USED);
         rmatval[p] = 1.0;
      }
      rhs[0]   = 3.0;
      sense[0] = 'L';
      status = CPXXaddrows (env, lp, 0, 1, NUMPRODUCTS, rhs,
                           sense, rmatbeg, rmatind, rmatval,
                           NULL, NULL);
      if ( status ) {
         fprintf (stderr, "Could not add new rows.\n");
         goto TERMINATE;
      }

      /* If product veg 1 or veg 2 is used then oil 3 must be used */

      indicator  = varindex (m, VEGOIL1, IS_USED);
      rmatind[0] = varindex (m, OIL3, USE);
      rmatval[0] = 1.0;
      status = CPXXaddindconstr (env, lp, indicator, 0, 1, 20.0, 'G',
                                rmatind, rmatval, NULL);

      indicator = varindex (m, VEGOIL2, IS_USED);
      status = CPXXaddindconstr (env, lp, indicator, 0, 1, 20.0, 'G',
                                rmatind, rmatval, NULL);

      if ( status ) {
         fprintf (stderr, "Could not add new indicator constraint.\n");
         goto TERMINATE;
      }

      /* We can store each product from one month to the next,
         starting with a stock of 500 tons */

      for (p = 0; p < NUMPRODUCTS; p++) {
         CPXNNZ n = 0;
         if ( m != 0 ) {
            rmatind[n]   = varindex (m-1, p, STORE); /* stored last month */
            rmatval[n++] = 1.0;
            rmatind[n]   = varindex (m, p, BUY);     /* bought this month */
            rmatval[n++] = 1.0;
            rmatind[n]   = varindex (m, p, USE);     /* used this month */
            rmatval[n++] = -1.0;
            rmatind[n]   = varindex (m, p, STORE);   /* stored this month */
            rmatval[n++] = -1.0;
            rhs[0]       = 0.0;
            sense[0]     = 'E';
         }
         else {
            rmatind[n]   = varindex (m, p, BUY);   /* bought this month */
            rmatval[n++] = 1.0;
            rmatind[n]   = varindex (m, p, USE);   /* used this month */
            rmatval[n++] = -1.0;
            rmatind[n]   = varindex (m, p, STORE); /* stored this month */
            rmatval[n++] = -1.0;
            rhs[0]       = -500.0;
            sense[0]     = 'E';
         }
         status = CPXXaddrows (env, lp, 0, 1, n, rhs,
                              sense, rmatbeg, rmatind, rmatval,
                              NULL, NULL);
         if ( status ) {
            fprintf (stderr, "Could not add new rows.\n");
            goto TERMINATE;
         }
      }
   }

 TERMINATE:

   free_and_null ((char **)&obj);
   free_and_null ((char **)&lb);
   free_and_null ((char **)&ub);
   free_and_null ((char **)&ctype);
   free_and_null ((char **)&rmatind);
   free_and_null ((char **)&rmatval);

   return (status);

}  /* END buildmodel */
int
main (void)
{
   /* Declare pointers for the variables and arrays that will contain
      the data which define the LP problem.  The setproblemdata() routine
      allocates space for the problem data.  */

   char     *probname = NULL;
   CPXDIM   numcols;
   CPXDIM   numrows;
   int      objsen;
   double   *obj = NULL;
   double   *rhs = NULL;
   char     *sense = NULL;
   CPXNNZ   *matbeg = NULL;
   CPXDIM   *matcnt = NULL;
   CPXDIM   *matind = NULL;
   double   *matval = NULL;
   double   *lb = NULL;
   double   *ub = NULL;
   CPXNNZ   *qmatbeg = NULL;
   CPXDIM   *qmatcnt = NULL;
   CPXDIM   *qmatind = NULL;
   double   *qmatval = NULL;

   /* Declare pointers for the variables that will contain the data
      for the constraint that cuts off certain local optima. */

   CPXDIM   numrows_extra;
   CPXNNZ   numnnz_extra;
   double   *rhs_extra = NULL;
   char     *sense_extra = NULL;
   CPXNNZ   *rmatbeg = NULL;
   CPXDIM   *rmatind = NULL;
   double   *rmatval = NULL;
   CPXDIM   rowind[1];

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

   CPXENVptr     env = NULL;
   CPXLPptr      lp = NULL;
   int           status;

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

   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;
   }

   /* Fill in the data for the problem.  */

   status = setproblemdata (&probname, &numcols, &numrows, &objsen, &obj,
                            &rhs, &sense, &matbeg, &matcnt, &matind,
                            &matval, &lb, &ub, &qmatbeg, &qmatcnt,
                            &qmatind, &qmatval,
                            &numrows_extra, &numnnz_extra, 
                            &rhs_extra, &sense_extra, 
                            &rmatbeg, &rmatind, &rmatval);
   if ( status ) {
      fprintf (stderr, "Failed to build problem data arrays.\n");
      goto TERMINATE;
   }

   /* Create the problem. */

   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 ) {
      fprintf (stderr, "Failed to create problem.\n");
      goto TERMINATE;
   }

   /* Now copy the LP part of the problem data into the lp */

   status = CPXXcopylp (env, lp, numcols, numrows, objsen, obj, rhs,
                       sense, matbeg, matcnt, matind, matval,
                       lb, ub, NULL);

   if ( status ) {
      fprintf (stderr, "Failed to copy problem data.\n");
      goto TERMINATE;
   }

   status = CPXXcopyquad (env, lp, qmatbeg, qmatcnt, qmatind, qmatval);
   if ( status ) {
      fprintf (stderr, "Failed to copy quadratic matrix.\n");
      goto TERMINATE;
   }


   /* When a non-convex objective function is present, CPLEX will
      return error CPXERR_Q_NOT_POS_DEF unless the parameter
      CPXPARAM_SolutionTarget is set to accept first-order optimal
      solutions.  */
   status = CPXXsetintparam (env, CPXPARAM_SolutionTarget, CPX_SOLUTIONTARGET_FIRSTORDER);
   if ( status ) goto TERMINATE;

   /* Optimize the problem and obtain solution. */

   status = optimize_and_report(env, lp, &solstat, &objval);
   if ( status ) goto TERMINATE;


   /* Add a constraint to cut off the solution at (-1, 1) */

   status = CPXXaddrows (env, lp, 0, numrows_extra, numnnz_extra,
                        rhs_extra, sense_extra,
                        rmatbeg, rmatind, rmatval,
                        NULL, NULL);
   if ( status ) goto TERMINATE;

   status = optimize_and_report(env, lp, &solstat, &objval);
   if ( status ) goto TERMINATE;


   /* Reverse the sense of the new constraint to cut off the solution at (1, 1) */

   rowind[0] = CPXXgetnumrows (env, lp) - 1;
   status    = CPXXchgsense (env, lp, 1, rowind, "L");
   if ( status ) goto TERMINATE;

   status = optimize_and_report(env, lp, &solstat, &objval);
   if ( status ) goto TERMINATE;

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

   status = CPXXwriteprob (env, lp, "indefqpex1.lp", NULL);
   if ( status ) {
      fprintf (stderr, "Failed to write LP to disk.\n");
      goto TERMINATE;
   }


TERMINATE:

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

   if ( lp != NULL ) {
      status = CPXXfreeprob (env, &lp);
      if ( status ) {
         fprintf (stderr, "CPXXfreeprob failed, error code %d.\n", status);
      }
   }

   /* 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 ) {
         char  errmsg[CPXMESSAGEBUFSIZE];
         fprintf (stderr, "Could not close CPLEX environment.\n");
         CPXXgeterrorstring (env, status, errmsg);
         fprintf (stderr, "%s", errmsg);
      }
   }

   /* Free up the problem data arrays, if necessary. */

   free_and_null ((char **) &probname);
   free_and_null ((char **) &obj);
   free_and_null ((char **) &rhs);
   free_and_null ((char **) &sense);
   free_and_null ((char **) &matbeg);
   free_and_null ((char **) &matcnt);
   free_and_null ((char **) &matind);
   free_and_null ((char **) &matval);
   free_and_null ((char **) &lb);
   free_and_null ((char **) &ub);
   free_and_null ((char **) &qmatbeg);
   free_and_null ((char **) &qmatcnt);
   free_and_null ((char **) &qmatind);
   free_and_null ((char **) &qmatval);
   free_and_null ((char **) &rhs_extra);
   free_and_null ((char **) &sense_extra);
   free_and_null ((char **) &rmatbeg);
   free_and_null ((char **) &rmatind);
   free_and_null ((char **) &rmatval);

   return (status);

}  /* END main */
예제 #6
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;
}
예제 #7
0
파일: xsteel.c 프로젝트: renvieir/ioc
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 */