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