src/glpios07.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
     1 /* glpios07.c (mixed cover cut generator) */
     2 
     3 /***********************************************************************
     4 *  This code is part of GLPK (GNU Linear Programming Kit).
     5 *
     6 *  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
     7 *  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
     8 *  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
     9 *  E-mail: <mao@gnu.org>.
    10 *
    11 *  GLPK is free software: you can redistribute it and/or modify it
    12 *  under the terms of the GNU General Public License as published by
    13 *  the Free Software Foundation, either version 3 of the License, or
    14 *  (at your option) any later version.
    15 *
    16 *  GLPK is distributed in the hope that it will be useful, but WITHOUT
    17 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    18 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    19 *  License for more details.
    20 *
    21 *  You should have received a copy of the GNU General Public License
    22 *  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
    23 ***********************************************************************/
    24 
    25 #include "glpios.h"
    26 
    27 /*----------------------------------------------------------------------
    28 -- COVER INEQUALITIES
    29 --
    30 -- Consider the set of feasible solutions to 0-1 knapsack problem:
    31 --
    32 --    sum a[j]*x[j] <= b,                                            (1)
    33 --  j in J
    34 --
    35 --    x[j] is binary,                                                (2)
    36 --
    37 -- where, wlog, we assume that a[j] > 0 (since 0-1 variables can be
    38 -- complemented) and a[j] <= b (since a[j] > b implies x[j] = 0).
    39 --
    40 -- A set C within J is called a cover if
    41 --
    42 --    sum a[j] > b.                                                  (3)
    43 --  j in C
    44 --
    45 -- For any cover C the inequality
    46 --
    47 --    sum x[j] <= |C| - 1                                            (4)
    48 --  j in C
    49 --
    50 -- is called a cover inequality and is valid for (1)-(2).
    51 --
    52 -- MIXED COVER INEQUALITIES
    53 --
    54 -- Consider the set of feasible solutions to mixed knapsack problem:
    55 --
    56 --    sum a[j]*x[j] + y <= b,                                        (5)
    57 --  j in J
    58 --
    59 --    x[j] is binary,                                                (6)
    60 --
    61 --    0 <= y <= u is continuous,                                     (7)
    62 --
    63 -- where again we assume that a[j] > 0.
    64 --
    65 -- Let C within J be some set. From (1)-(4) it follows that
    66 --
    67 --    sum a[j] > b - y                                               (8)
    68 --  j in C
    69 --
    70 -- implies
    71 --
    72 --    sum x[j] <= |C| - 1.                                           (9)
    73 --  j in C
    74 --
    75 -- Thus, we need to modify the inequality (9) in such a way that it be
    76 -- a constraint only if the condition (8) is satisfied.
    77 --
    78 -- Consider the following inequality:
    79 --
    80 --    sum x[j] <= |C| - t.                                          (10)
    81 --  j in C
    82 --
    83 -- If 0 < t <= 1, then (10) is equivalent to (9), because all x[j] are
    84 -- binary variables. On the other hand, if t <= 0, (10) being satisfied
    85 -- for any values of x[j] is not a constraint.
    86 --
    87 -- Let
    88 --
    89 --    t' = sum a[j] + y - b.                                        (11)
    90 --       j in C
    91 --
    92 -- It is understood that the condition t' > 0 is equivalent to (8).
    93 -- Besides, from (6)-(7) it follows that t' has an implied upper bound:
    94 --
    95 --    t'max = sum a[j] + u - b.                                     (12)
    96 --          j in C
    97 --
    98 -- This allows to express the parameter t having desired properties:
    99 --
   100 --    t = t' / t'max.                                               (13)
   101 --
   102 -- In fact, t <= 1 by definition, and t > 0 being equivalent to t' > 0
   103 -- is equivalent to (8).
   104 --
   105 -- Thus, the inequality (10), where t is given by formula (13) is valid
   106 -- for (5)-(7).
   107 --
   108 -- Note that if u = 0, then y = 0, so t = 1, and the conditions (8) and
   109 -- (10) is transformed to the conditions (3) and (4).
   110 --
   111 -- GENERATING MIXED COVER CUTS
   112 --
   113 -- To generate a mixed cover cut in the form (10) we need to find such
   114 -- set C which satisfies to the inequality (8) and for which, in turn,
   115 -- the inequality (10) is violated in the current point.
   116 --
   117 -- Substituting t from (13) to (10) gives:
   118 --
   119 --                        1
   120 --    sum x[j] <= |C| - -----  (sum a[j] + y - b),                  (14)
   121 --  j in C              t'max j in C
   122 --
   123 -- and finally we have the cut inequality in the standard form:
   124 --
   125 --    sum x[j] + alfa * y <= beta,                                  (15)
   126 --  j in C
   127 --
   128 -- where:
   129 --
   130 --    alfa = 1 / t'max,                                             (16)
   131 --
   132 --    beta = |C| - alfa *  (sum a[j] - b).                          (17)
   133 --                        j in C                                      */
   134 
   135 #if 1
   136 #define MAXTRY 1000
   137 #else
   138 #define MAXTRY 10000
   139 #endif
   140 
   141 static int cover2(int n, double a[], double b, double u, double x[],
   142       double y, int cov[], double *_alfa, double *_beta)
   143 {     /* try to generate mixed cover cut using two-element cover */
   144       int i, j, try = 0, ret = 0;
   145       double eps, alfa, beta, temp, rmax = 0.001;
   146       eps = 0.001 * (1.0 + fabs(b));
   147       for (i = 0+1; i <= n; i++)
   148       for (j = i+1; j <= n; j++)
   149       {  /* C = {i, j} */
   150          try++;
   151          if (try > MAXTRY) goto done;
   152          /* check if condition (8) is satisfied */
   153          if (a[i] + a[j] + y > b + eps)
   154          {  /* compute parameters for inequality (15) */
   155             temp = a[i] + a[j] - b;
   156             alfa = 1.0 / (temp + u);
   157             beta = 2.0 - alfa * temp;
   158             /* compute violation of inequality (15) */
   159             temp = x[i] + x[j] + alfa * y - beta;
   160             /* choose C providing maximum violation */
   161             if (rmax < temp)
   162             {  rmax = temp;
   163                cov[1] = i;
   164                cov[2] = j;
   165                *_alfa = alfa;
   166                *_beta = beta;
   167                ret = 1;
   168             }
   169          }
   170       }
   171 done: return ret;
   172 }
   173 
   174 static int cover3(int n, double a[], double b, double u, double x[],
   175       double y, int cov[], double *_alfa, double *_beta)
   176 {     /* try to generate mixed cover cut using three-element cover */
   177       int i, j, k, try = 0, ret = 0;
   178       double eps, alfa, beta, temp, rmax = 0.001;
   179       eps = 0.001 * (1.0 + fabs(b));
   180       for (i = 0+1; i <= n; i++)
   181       for (j = i+1; j <= n; j++)
   182       for (k = j+1; k <= n; k++)
   183       {  /* C = {i, j, k} */
   184          try++;
   185          if (try > MAXTRY) goto done;
   186          /* check if condition (8) is satisfied */
   187          if (a[i] + a[j] + a[k] + y > b + eps)
   188          {  /* compute parameters for inequality (15) */
   189             temp = a[i] + a[j] + a[k] - b;
   190             alfa = 1.0 / (temp + u);
   191             beta = 3.0 - alfa * temp;
   192             /* compute violation of inequality (15) */
   193             temp = x[i] + x[j] + x[k] + alfa * y - beta;
   194             /* choose C providing maximum violation */
   195             if (rmax < temp)
   196             {  rmax = temp;
   197                cov[1] = i;
   198                cov[2] = j;
   199                cov[3] = k;
   200                *_alfa = alfa;
   201                *_beta = beta;
   202                ret = 1;
   203             }
   204          }
   205       }
   206 done: return ret;
   207 }
   208 
   209 static int cover4(int n, double a[], double b, double u, double x[],
   210       double y, int cov[], double *_alfa, double *_beta)
   211 {     /* try to generate mixed cover cut using four-element cover */
   212       int i, j, k, l, try = 0, ret = 0;
   213       double eps, alfa, beta, temp, rmax = 0.001;
   214       eps = 0.001 * (1.0 + fabs(b));
   215       for (i = 0+1; i <= n; i++)
   216       for (j = i+1; j <= n; j++)
   217       for (k = j+1; k <= n; k++)
   218       for (l = k+1; l <= n; l++)
   219       {  /* C = {i, j, k, l} */
   220          try++;
   221          if (try > MAXTRY) goto done;
   222          /* check if condition (8) is satisfied */
   223          if (a[i] + a[j] + a[k] + a[l] + y > b + eps)
   224          {  /* compute parameters for inequality (15) */
   225             temp = a[i] + a[j] + a[k] + a[l] - b;
   226             alfa = 1.0 / (temp + u);
   227             beta = 4.0 - alfa * temp;
   228             /* compute violation of inequality (15) */
   229             temp = x[i] + x[j] + x[k] + x[l] + alfa * y - beta;
   230             /* choose C providing maximum violation */
   231             if (rmax < temp)
   232             {  rmax = temp;
   233                cov[1] = i;
   234                cov[2] = j;
   235                cov[3] = k;
   236                cov[4] = l;
   237                *_alfa = alfa;
   238                *_beta = beta;
   239                ret = 1;
   240             }
   241          }
   242       }
   243 done: return ret;
   244 }
   245 
   246 static int cover(int n, double a[], double b, double u, double x[],
   247       double y, int cov[], double *alfa, double *beta)
   248 {     /* try to generate mixed cover cut;
   249          input (see (5)):
   250          n        is the number of binary variables;
   251          a[1:n]   are coefficients at binary variables;
   252          b        is the right-hand side;
   253          u        is upper bound of continuous variable;
   254          x[1:n]   are values of binary variables at current point;
   255          y        is value of continuous variable at current point;
   256          output (see (15), (16), (17)):
   257          cov[1:r] are indices of binary variables included in cover C,
   258                   where r is the set cardinality returned on exit;
   259          alfa     coefficient at continuous variable;
   260          beta     is the right-hand side; */
   261       int j;
   262       /* perform some sanity checks */
   263       xassert(n >= 2);
   264       for (j = 1; j <= n; j++) xassert(a[j] > 0.0);
   265 #if 1 /* ??? */
   266       xassert(b > -1e-5);
   267 #else
   268       xassert(b > 0.0);
   269 #endif
   270       xassert(u >= 0.0);
   271       for (j = 1; j <= n; j++) xassert(0.0 <= x[j] && x[j] <= 1.0);
   272       xassert(0.0 <= y && y <= u);
   273       /* try to generate mixed cover cut */
   274       if (cover2(n, a, b, u, x, y, cov, alfa, beta)) return 2;
   275       if (cover3(n, a, b, u, x, y, cov, alfa, beta)) return 3;
   276       if (cover4(n, a, b, u, x, y, cov, alfa, beta)) return 4;
   277       return 0;
   278 }
   279 
   280 /*----------------------------------------------------------------------
   281 -- lpx_cover_cut - generate mixed cover cut.
   282 --
   283 -- SYNOPSIS
   284 --
   285 -- #include "glplpx.h"
   286 -- int lpx_cover_cut(LPX *lp, int len, int ind[], double val[],
   287 --    double work[]);
   288 --
   289 -- DESCRIPTION
   290 --
   291 -- The routine lpx_cover_cut generates a mixed cover cut for a given
   292 -- row of the MIP problem.
   293 --
   294 -- The given row of the MIP problem should be explicitly specified in
   295 -- the form:
   296 --
   297 --    sum{j in J} a[j]*x[j] <= b.                                    (1)
   298 --
   299 -- On entry indices (ordinal numbers) of structural variables, which
   300 -- have non-zero constraint coefficients, should be placed in locations
   301 -- ind[1], ..., ind[len], and corresponding constraint coefficients
   302 -- should be placed in locations val[1], ..., val[len]. The right-hand
   303 -- side b should be stored in location val[0].
   304 --
   305 -- The working array work should have at least nb locations, where nb
   306 -- is the number of binary variables in (1).
   307 --
   308 -- The routine generates a mixed cover cut in the same form as (1) and
   309 -- stores the cut coefficients and right-hand side in the same way as
   310 -- just described above.
   311 --
   312 -- RETURNS
   313 --
   314 -- If the cutting plane has been successfully generated, the routine
   315 -- returns 1 <= len' <= n, which is the number of non-zero coefficients
   316 -- in the inequality constraint. Otherwise, the routine returns zero. */
   317 
   318 static int lpx_cover_cut(LPX *lp, int len, int ind[], double val[],
   319       double work[])
   320 {     int cov[1+4], j, k, nb, newlen, r;
   321       double f_min, f_max, alfa, beta, u, *x = work, y;
   322       /* substitute and remove fixed variables */
   323       newlen = 0;
   324       for (k = 1; k <= len; k++)
   325       {  j = ind[k];
   326          if (lpx_get_col_type(lp, j) == LPX_FX)
   327             val[0] -= val[k] * lpx_get_col_lb(lp, j);
   328          else
   329          {  newlen++;
   330             ind[newlen] = ind[k];
   331             val[newlen] = val[k];
   332          }
   333       }
   334       len = newlen;
   335       /* move binary variables to the beginning of the list so that
   336          elements 1, 2, ..., nb correspond to binary variables, and
   337          elements nb+1, nb+2, ..., len correspond to rest variables */
   338       nb = 0;
   339       for (k = 1; k <= len; k++)
   340       {  j = ind[k];
   341          if (lpx_get_col_kind(lp, j) == LPX_IV &&
   342              lpx_get_col_type(lp, j) == LPX_DB &&
   343              lpx_get_col_lb(lp, j) == 0.0 &&
   344              lpx_get_col_ub(lp, j) == 1.0)
   345          {  /* binary variable */
   346             int ind_k;
   347             double val_k;
   348             nb++;
   349             ind_k = ind[nb], val_k = val[nb];
   350             ind[nb] = ind[k], val[nb] = val[k];
   351             ind[k] = ind_k, val[k] = val_k;
   352          }
   353       }
   354       /* now the specified row has the form:
   355          sum a[j]*x[j] + sum a[j]*y[j] <= b,
   356          where x[j] are binary variables, y[j] are rest variables */
   357       /* at least two binary variables are needed */
   358       if (nb < 2) return 0;
   359       /* compute implied lower and upper bounds for sum a[j]*y[j] */
   360       f_min = f_max = 0.0;
   361       for (k = nb+1; k <= len; k++)
   362       {  j = ind[k];
   363          /* both bounds must be finite */
   364          if (lpx_get_col_type(lp, j) != LPX_DB) return 0;
   365          if (val[k] > 0.0)
   366          {  f_min += val[k] * lpx_get_col_lb(lp, j);
   367             f_max += val[k] * lpx_get_col_ub(lp, j);
   368          }
   369          else
   370          {  f_min += val[k] * lpx_get_col_ub(lp, j);
   371             f_max += val[k] * lpx_get_col_lb(lp, j);
   372          }
   373       }
   374       /* sum a[j]*x[j] + sum a[j]*y[j] <= b ===>
   375          sum a[j]*x[j] + (sum a[j]*y[j] - f_min) <= b - f_min ===>
   376          sum a[j]*x[j] + y <= b - f_min,
   377          where y = sum a[j]*y[j] - f_min;
   378          note that 0 <= y <= u, u = f_max - f_min */
   379       /* determine upper bound of y */
   380       u = f_max - f_min;
   381       /* determine value of y at the current point */
   382       y = 0.0;
   383       for (k = nb+1; k <= len; k++)
   384       {  j = ind[k];
   385          y += val[k] * lpx_get_col_prim(lp, j);
   386       }
   387       y -= f_min;
   388       if (y < 0.0) y = 0.0;
   389       if (y > u) y = u;
   390       /* modify the right-hand side b */
   391       val[0] -= f_min;
   392       /* now the transformed row has the form:
   393          sum a[j]*x[j] + y <= b, where 0 <= y <= u */
   394       /* determine values of x[j] at the current point */
   395       for (k = 1; k <= nb; k++)
   396       {  j = ind[k];
   397          x[k] = lpx_get_col_prim(lp, j);
   398          if (x[k] < 0.0) x[k] = 0.0;
   399          if (x[k] > 1.0) x[k] = 1.0;
   400       }
   401       /* if a[j] < 0, replace x[j] by its complement 1 - x'[j] */
   402       for (k = 1; k <= nb; k++)
   403       {  if (val[k] < 0.0)
   404          {  ind[k] = - ind[k];
   405             val[k] = - val[k];
   406             val[0] += val[k];
   407             x[k] = 1.0 - x[k];
   408          }
   409       }
   410       /* try to generate a mixed cover cut for the transformed row */
   411       r = cover(nb, val, val[0], u, x, y, cov, &alfa, &beta);
   412       if (r == 0) return 0;
   413       xassert(2 <= r && r <= 4);
   414       /* now the cut is in the form:
   415          sum{j in C} x[j] + alfa * y <= beta */
   416       /* store the right-hand side beta */
   417       ind[0] = 0, val[0] = beta;
   418       /* restore the original ordinal numbers of x[j] */
   419       for (j = 1; j <= r; j++) cov[j] = ind[cov[j]];
   420       /* store cut coefficients at binary variables complementing back
   421          the variables having negative row coefficients */
   422       xassert(r <= nb);
   423       for (k = 1; k <= r; k++)
   424       {  if (cov[k] > 0)
   425          {  ind[k] = +cov[k];
   426             val[k] = +1.0;
   427          }
   428          else
   429          {  ind[k] = -cov[k];
   430             val[k] = -1.0;
   431             val[0] -= 1.0;
   432          }
   433       }
   434       /* substitute y = sum a[j]*y[j] - f_min */
   435       for (k = nb+1; k <= len; k++)
   436       {  r++;
   437          ind[r] = ind[k];
   438          val[r] = alfa * val[k];
   439       }
   440       val[0] += alfa * f_min;
   441       xassert(r <= len);
   442       len = r;
   443       return len;
   444 }
   445 
   446 /*----------------------------------------------------------------------
   447 -- lpx_eval_row - compute explictily specified row.
   448 --
   449 -- SYNOPSIS
   450 --
   451 -- #include "glplpx.h"
   452 -- double lpx_eval_row(LPX *lp, int len, int ind[], double val[]);
   453 --
   454 -- DESCRIPTION
   455 --
   456 -- The routine lpx_eval_row computes the primal value of an explicitly
   457 -- specified row using current values of structural variables.
   458 --
   459 -- The explicitly specified row may be thought as a linear form:
   460 --
   461 --    y = a[1]*x[m+1] + a[2]*x[m+2] + ... + a[n]*x[m+n],
   462 --
   463 -- where y is an auxiliary variable for this row, a[j] are coefficients
   464 -- of the linear form, x[m+j] are structural variables.
   465 --
   466 -- On entry column indices and numerical values of non-zero elements of
   467 -- the row should be stored in locations ind[1], ..., ind[len] and
   468 -- val[1], ..., val[len], where len is the number of non-zero elements.
   469 -- The array ind and val are not changed on exit.
   470 --
   471 -- RETURNS
   472 --
   473 -- The routine returns a computed value of y, the auxiliary variable of
   474 -- the specified row. */
   475 
   476 static double lpx_eval_row(LPX *lp, int len, int ind[], double val[])
   477 {     int n = lpx_get_num_cols(lp);
   478       int j, k;
   479       double sum = 0.0;
   480       if (len < 0)
   481          xerror("lpx_eval_row: len = %d; invalid row length\n", len);
   482       for (k = 1; k <= len; k++)
   483       {  j = ind[k];
   484          if (!(1 <= j && j <= n))
   485             xerror("lpx_eval_row: j = %d; column number out of range\n",
   486                j);
   487          sum += val[k] * lpx_get_col_prim(lp, j);
   488       }
   489       return sum;
   490 }
   491 
   492 /***********************************************************************
   493 *  NAME
   494 *
   495 *  ios_cov_gen - generate mixed cover cuts
   496 *
   497 *  SYNOPSIS
   498 *
   499 *  #include "glpios.h"
   500 *  void ios_cov_gen(glp_tree *tree);
   501 *
   502 *  DESCRIPTION
   503 *
   504 *  The routine ios_cov_gen generates mixed cover cuts for the current
   505 *  point and adds them to the cut pool. */
   506 
   507 void ios_cov_gen(glp_tree *tree)
   508 {     glp_prob *prob = tree->mip;
   509       int m = lpx_get_num_rows(prob);
   510       int n = lpx_get_num_cols(prob);
   511       int i, k, type, kase, len, *ind;
   512       double r, *val, *work;
   513       xassert(lpx_get_status(prob) == LPX_OPT);
   514       /* allocate working arrays */
   515       ind = xcalloc(1+n, sizeof(int));
   516       val = xcalloc(1+n, sizeof(double));
   517       work = xcalloc(1+n, sizeof(double));
   518       /* look through all rows */
   519       for (i = 1; i <= m; i++)
   520       for (kase = 1; kase <= 2; kase++)
   521       {  type = lpx_get_row_type(prob, i);
   522          if (kase == 1)
   523          {  /* consider rows of '<=' type */
   524             if (!(type == LPX_UP || type == LPX_DB)) continue;
   525             len = lpx_get_mat_row(prob, i, ind, val);
   526             val[0] = lpx_get_row_ub(prob, i);
   527          }
   528          else
   529          {  /* consider rows of '>=' type */
   530             if (!(type == LPX_LO || type == LPX_DB)) continue;
   531             len = lpx_get_mat_row(prob, i, ind, val);
   532             for (k = 1; k <= len; k++) val[k] = - val[k];
   533             val[0] = - lpx_get_row_lb(prob, i);
   534          }
   535          /* generate mixed cover cut:
   536             sum{j in J} a[j] * x[j] <= b */
   537          len = lpx_cover_cut(prob, len, ind, val, work);
   538          if (len == 0) continue;
   539          /* at the current point the cut inequality is violated, i.e.
   540             sum{j in J} a[j] * x[j] - b > 0 */
   541          r = lpx_eval_row(prob, len, ind, val) - val[0];
   542          if (r < 1e-3) continue;
   543          /* add the cut to the cut pool */
   544          glp_ios_add_row(tree, NULL, GLP_RF_COV, 0, len, ind, val,
   545             GLP_UP, val[0]);
   546       }
   547       /* free working arrays */
   548       xfree(ind);
   549       xfree(val);
   550       xfree(work);
   551       return;
   552 }
   553 
   554 /* eof */