static int populatebycolumn (CPXENVptr env, CPXLPptr lp) { int status = 0; double obj[NUMCOLS]; double lb[NUMCOLS]; double ub[NUMCOLS]; char const *colname[NUMCOLS]; CPXNNZ matbeg[NUMCOLS]; CPXDIM matind[NUMNZ]; double matval[NUMNZ]; double rhs[NUMROWS]; char sense[NUMROWS]; char const *rowname[NUMROWS]; /* To build the problem by column, create the rows, and then add the columns. */ status = CPXXchgobjsen (env, lp, CPX_MAX); /* Problem is maximization */ if ( status ) goto TERMINATE; /* Now create the new rows. First, populate the arrays. */ rowname[0] = "c1"; sense[0] = 'L'; rhs[0] = 20.0; rowname[1] = "c2"; sense[1] = 'L'; rhs[1] = 30.0; status = CPXXnewrows (env, lp, NUMROWS, rhs, sense, NULL, rowname); if ( status ) goto TERMINATE; /* Now add the new columns. First, populate the arrays. */ obj[0] = 1.0; obj[1] = 2.0; obj[2] = 3.0; matbeg[0] = 0; matbeg[1] = 2; matbeg[2] = 4; matind[0] = 0; matind[2] = 0; matind[4] = 0; matval[0] = -1.0; matval[2] = 1.0; matval[4] = 1.0; matind[1] = 1; matind[3] = 1; matind[5] = 1; matval[1] = 1.0; matval[3] = -3.0; matval[5] = 1.0; lb[0] = 0.0; lb[1] = 0.0; lb[2] = 0.0; ub[0] = 40.0; ub[1] = CPX_INFBOUND; ub[2] = CPX_INFBOUND; colname[0] = "x1"; colname[1] = "x2"; colname[2] = "x3"; status = CPXXaddcols (env, lp, NUMCOLS, NUMNZ, obj, matbeg, matind, matval, lb, ub, colname); if ( status ) goto TERMINATE; TERMINATE: return (status); } /* END populatebycolumn */
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 */
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 */
/* 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; }
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 */