static int buildnetwork (CPXENVptr env, CPXLPptr lp) { char sense[NUMNODES]; double rhs[NUMNODES]; int ind[2]; double val[2]; char *name[1]; char buffer[100]; int i, j, varindex; int zero = 0; double dblzero = 0.0; double dblone = 1.0; char binary = 'B'; int status = 0; /* Create constraint placeholders --- One constraint for each node (flow constraints) */ for (i = 0; i < NUMNODES; i++) { rhs[i] = demand[i]; sense[i] = 'G'; } status = CPXnewrows (env, lp, NUMNODES, rhs, sense, NULL, NULL); if ( status ) { fprintf (stderr, "Could not create new rows.\n"); goto TERMINATE; } /* Add flow variables */ for (j = 0; j < NUMEDGES; j++) { ind[0] = orig[j]; /* Flow leaves origin */ val[0] = -1.0; ind[1] = dest[j]; /* Flow arrives at destination */ val[1] = 1.0; name[0] = buffer; sprintf(buffer, "x%d%d", orig[j], dest[j]); status = CPXaddcols (env, lp, 1, 2, &unitcost[j], &zero, ind, val, NULL, NULL, name); if ( status ) { fprintf (stderr, "Failed to add flow edge.\n"); goto TERMINATE; } } /* Add fixed charge variables */ for (j = 0; j < NUMEDGES; j++) { name[0] = buffer; sprintf(buffer, "f%d%d", orig[j], dest[j]); status = CPXaddcols (env, lp, 1, 0, &fixedcost[j], &zero, ind, val, &dblzero, &dblone, name); if ( status ) { fprintf (stderr, "Failed to add fixed charge variable.\n"); goto TERMINATE; } varindex = NUMEDGES+j; status = CPXchgctype (env, lp, 1, &varindex, &binary); if ( status ) { fprintf (stderr, "Failed to change variable type.\n"); goto TERMINATE; } } /* Add indicator constraints -- f = 0 -> x <= 0 */ for (j = 0; j < NUMEDGES; j++) { varindex = j; sprintf(buffer, "indicator%d", j); status = CPXaddindconstr (env, lp, NUMEDGES+j, 1, 1, 0.0, 'L', &varindex, &dblone, buffer); if ( status ) { fprintf (stderr, "Failed to add indicator constraint."); goto TERMINATE; } } TERMINATE: return (status); } /* END buildnetwork */
static int buildmodel (CPXENVptr env, CPXLPptr lp) { int colcnt = NUMVARS*NUMMONTHS*NUMPRODUCTS; double *obj = NULL; double *lb = NULL; double *ub = NULL; char *ctype = NULL; int *rmatind = NULL; double *rmatval = NULL; int indicator; int rmatbeg[1]; double rhs[1]; char sense[1]; int m, p; int status = 0; status = CPXchgobjsen (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 = (double *) malloc (colcnt * sizeof(double)); lb = (double *) malloc (colcnt * sizeof(double)); ub = (double *) malloc (colcnt * sizeof(double)); ctype = (char *) malloc (colcnt * sizeof(char)); rmatind = (int * ) malloc (colcnt * sizeof(int)); rmatval = (double *) malloc (colcnt * sizeof(double)); 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 = CPXnewcols (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 = CPXaddindconstr (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 = CPXaddrows (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 = CPXaddrows (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 = CPXnewcols (env, lp, 1, obj, lb, ub, ctype, NULL); if ( status ) { fprintf (stderr, "Could not add new columns.\n"); goto TERMINATE; } totalindex = CPXgetnumcols (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 = CPXaddrows (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 = CPXaddrows (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 = CPXaddrows (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 = CPXaddrows (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 = CPXaddindconstr (env, lp, indicator, 0, 1, 20.0, 'G', rmatind, rmatval, NULL); indicator = varindex (m, VEGOIL2, IS_USED); status = CPXaddindconstr (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++) { int 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 = CPXaddrows (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 */