src/glpios10.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
alpar@1
     1
/* glpios10.c (feasibility pump heuristic) */
alpar@1
     2
alpar@1
     3
/***********************************************************************
alpar@1
     4
*  This code is part of GLPK (GNU Linear Programming Kit).
alpar@1
     5
*
alpar@1
     6
*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
alpar@1
     7
*  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
alpar@1
     8
*  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
alpar@1
     9
*  E-mail: <mao@gnu.org>.
alpar@1
    10
*
alpar@1
    11
*  GLPK is free software: you can redistribute it and/or modify it
alpar@1
    12
*  under the terms of the GNU General Public License as published by
alpar@1
    13
*  the Free Software Foundation, either version 3 of the License, or
alpar@1
    14
*  (at your option) any later version.
alpar@1
    15
*
alpar@1
    16
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
alpar@1
    17
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
alpar@1
    18
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
alpar@1
    19
*  License for more details.
alpar@1
    20
*
alpar@1
    21
*  You should have received a copy of the GNU General Public License
alpar@1
    22
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
alpar@1
    23
***********************************************************************/
alpar@1
    24
alpar@1
    25
#include "glpios.h"
alpar@1
    26
#include "glprng.h"
alpar@1
    27
alpar@1
    28
/***********************************************************************
alpar@1
    29
*  NAME
alpar@1
    30
*
alpar@1
    31
*  ios_feas_pump - feasibility pump heuristic
alpar@1
    32
*
alpar@1
    33
*  SYNOPSIS
alpar@1
    34
*
alpar@1
    35
*  #include "glpios.h"
alpar@1
    36
*  void ios_feas_pump(glp_tree *T);
alpar@1
    37
*
alpar@1
    38
*  DESCRIPTION
alpar@1
    39
*
alpar@1
    40
*  The routine ios_feas_pump is a simple implementation of the Feasi-
alpar@1
    41
*  bility Pump heuristic.
alpar@1
    42
*
alpar@1
    43
*  REFERENCES
alpar@1
    44
*
alpar@1
    45
*  M.Fischetti, F.Glover, and A.Lodi. "The feasibility pump." Math.
alpar@1
    46
*  Program., Ser. A 104, pp. 91-104 (2005). */
alpar@1
    47
alpar@1
    48
struct VAR
alpar@1
    49
{     /* binary variable */
alpar@1
    50
      int j;
alpar@1
    51
      /* ordinal number */
alpar@1
    52
      int x;
alpar@1
    53
      /* value in the rounded solution (0 or 1) */
alpar@1
    54
      double d;
alpar@1
    55
      /* sorting key */
alpar@1
    56
};
alpar@1
    57
alpar@1
    58
static int fcmp(const void *x, const void *y)
alpar@1
    59
{     /* comparison routine */
alpar@1
    60
      const struct VAR *vx = x, *vy = y;
alpar@1
    61
      if (vx->d > vy->d)
alpar@1
    62
         return -1;
alpar@1
    63
      else if (vx->d < vy->d)
alpar@1
    64
         return +1;
alpar@1
    65
      else
alpar@1
    66
         return 0;
alpar@1
    67
}
alpar@1
    68
alpar@1
    69
void ios_feas_pump(glp_tree *T)
alpar@1
    70
{     glp_prob *P = T->mip;
alpar@1
    71
      int n = P->n;
alpar@1
    72
      glp_prob *lp = NULL;
alpar@1
    73
      struct VAR *var = NULL;
alpar@1
    74
      RNG *rand = NULL;
alpar@1
    75
      GLPCOL *col;
alpar@1
    76
      glp_smcp parm;
alpar@1
    77
      int j, k, new_x, nfail, npass, nv, ret, stalling;
alpar@1
    78
      double dist, tol;
alpar@1
    79
      xassert(glp_get_status(P) == GLP_OPT);
alpar@1
    80
      /* this heuristic is applied only once on the root level */
alpar@1
    81
      if (!(T->curr->level == 0 && T->curr->solved == 1)) goto done;
alpar@1
    82
      /* determine number of binary variables */
alpar@1
    83
      nv = 0;
alpar@1
    84
      for (j = 1; j <= n; j++)
alpar@1
    85
      {  col = P->col[j];
alpar@1
    86
         /* if x[j] is continuous, skip it */
alpar@1
    87
         if (col->kind == GLP_CV) continue;
alpar@1
    88
         /* if x[j] is fixed, skip it */
alpar@1
    89
         if (col->type == GLP_FX) continue;
alpar@1
    90
         /* x[j] is non-fixed integer */
alpar@1
    91
         xassert(col->kind == GLP_IV);
alpar@1
    92
         if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0)
alpar@1
    93
         {  /* x[j] is binary */
alpar@1
    94
            nv++;
alpar@1
    95
         }
alpar@1
    96
         else
alpar@1
    97
         {  /* x[j] is general integer */
alpar@1
    98
            if (T->parm->msg_lev >= GLP_MSG_ALL)
alpar@1
    99
               xprintf("FPUMP heuristic cannot be applied due to genera"
alpar@1
   100
                  "l integer variables\n");
alpar@1
   101
            goto done;
alpar@1
   102
         }
alpar@1
   103
      }
alpar@1
   104
      /* there must be at least one binary variable */
alpar@1
   105
      if (nv == 0) goto done;
alpar@1
   106
      if (T->parm->msg_lev >= GLP_MSG_ALL)
alpar@1
   107
         xprintf("Applying FPUMP heuristic...\n");
alpar@1
   108
      /* build the list of binary variables */
alpar@1
   109
      var = xcalloc(1+nv, sizeof(struct VAR));
alpar@1
   110
      k = 0;
alpar@1
   111
      for (j = 1; j <= n; j++)
alpar@1
   112
      {  col = P->col[j];
alpar@1
   113
         if (col->kind == GLP_IV && col->type == GLP_DB)
alpar@1
   114
            var[++k].j = j;
alpar@1
   115
      }
alpar@1
   116
      xassert(k == nv);
alpar@1
   117
      /* create working problem object */
alpar@1
   118
      lp = glp_create_prob();
alpar@1
   119
more: /* copy the original problem object to keep it intact */
alpar@1
   120
      glp_copy_prob(lp, P, GLP_OFF);
alpar@1
   121
      /* we are interested to find an integer feasible solution, which
alpar@1
   122
         is better than the best known one */
alpar@1
   123
      if (P->mip_stat == GLP_FEAS)
alpar@1
   124
      {  int *ind;
alpar@1
   125
         double *val, bnd;
alpar@1
   126
         /* add a row and make it identical to the objective row */
alpar@1
   127
         glp_add_rows(lp, 1);
alpar@1
   128
         ind = xcalloc(1+n, sizeof(int));
alpar@1
   129
         val = xcalloc(1+n, sizeof(double));
alpar@1
   130
         for (j = 1; j <= n; j++)
alpar@1
   131
         {  ind[j] = j;
alpar@1
   132
            val[j] = P->col[j]->coef;
alpar@1
   133
         }
alpar@1
   134
         glp_set_mat_row(lp, lp->m, n, ind, val);
alpar@1
   135
         xfree(ind);
alpar@1
   136
         xfree(val);
alpar@1
   137
         /* introduce upper (minimization) or lower (maximization)
alpar@1
   138
            bound to the original objective function; note that this
alpar@1
   139
            additional constraint is not violated at the optimal point
alpar@1
   140
            to LP relaxation */
alpar@1
   141
#if 0 /* modified by xypron <xypron.glpk@gmx.de> */
alpar@1
   142
         if (P->dir == GLP_MIN)
alpar@1
   143
         {  bnd = P->mip_obj - 0.10 * (1.0 + fabs(P->mip_obj));
alpar@1
   144
            if (bnd < P->obj_val) bnd = P->obj_val;
alpar@1
   145
            glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0);
alpar@1
   146
         }
alpar@1
   147
         else if (P->dir == GLP_MAX)
alpar@1
   148
         {  bnd = P->mip_obj + 0.10 * (1.0 + fabs(P->mip_obj));
alpar@1
   149
            if (bnd > P->obj_val) bnd = P->obj_val;
alpar@1
   150
            glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0);
alpar@1
   151
         }
alpar@1
   152
         else
alpar@1
   153
            xassert(P != P);
alpar@1
   154
#else
alpar@1
   155
         bnd = 0.1 * P->obj_val + 0.9 * P->mip_obj;
alpar@1
   156
         /* xprintf("bnd = %f\n", bnd); */
alpar@1
   157
         if (P->dir == GLP_MIN)
alpar@1
   158
            glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0);
alpar@1
   159
         else if (P->dir == GLP_MAX)
alpar@1
   160
            glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0);
alpar@1
   161
         else
alpar@1
   162
            xassert(P != P);
alpar@1
   163
#endif
alpar@1
   164
      }
alpar@1
   165
      /* reset pass count */
alpar@1
   166
      npass = 0;
alpar@1
   167
      /* invalidate the rounded point */
alpar@1
   168
      for (k = 1; k <= nv; k++)
alpar@1
   169
         var[k].x = -1;
alpar@1
   170
pass: /* next pass starts here */
alpar@1
   171
      npass++;
alpar@1
   172
      if (T->parm->msg_lev >= GLP_MSG_ALL)
alpar@1
   173
         xprintf("Pass %d\n", npass);
alpar@1
   174
      /* initialize minimal distance between the basic point and the
alpar@1
   175
         rounded one obtained during this pass */
alpar@1
   176
      dist = DBL_MAX;
alpar@1
   177
      /* reset failure count (the number of succeeded iterations failed
alpar@1
   178
         to improve the distance) */
alpar@1
   179
      nfail = 0;
alpar@1
   180
      /* if it is not the first pass, perturb the last rounded point
alpar@1
   181
         rather than construct it from the basic solution */
alpar@1
   182
      if (npass > 1)
alpar@1
   183
      {  double rho, temp;
alpar@1
   184
         if (rand == NULL)
alpar@1
   185
            rand = rng_create_rand();
alpar@1
   186
         for (k = 1; k <= nv; k++)
alpar@1
   187
         {  j = var[k].j;
alpar@1
   188
            col = lp->col[j];
alpar@1
   189
            rho = rng_uniform(rand, -0.3, 0.7);
alpar@1
   190
            if (rho < 0.0) rho = 0.0;
alpar@1
   191
            temp = fabs((double)var[k].x - col->prim);
alpar@1
   192
            if (temp + rho > 0.5) var[k].x = 1 - var[k].x;
alpar@1
   193
         }
alpar@1
   194
         goto skip;
alpar@1
   195
      }
alpar@1
   196
loop: /* innermost loop begins here */
alpar@1
   197
      /* round basic solution (which is assumed primal feasible) */
alpar@1
   198
      stalling = 1;
alpar@1
   199
      for (k = 1; k <= nv; k++)
alpar@1
   200
      {  col = lp->col[var[k].j];
alpar@1
   201
         if (col->prim < 0.5)
alpar@1
   202
         {  /* rounded value is 0 */
alpar@1
   203
            new_x = 0;
alpar@1
   204
         }
alpar@1
   205
         else
alpar@1
   206
         {  /* rounded value is 1 */
alpar@1
   207
            new_x = 1;
alpar@1
   208
         }
alpar@1
   209
         if (var[k].x != new_x)
alpar@1
   210
         {  stalling = 0;
alpar@1
   211
            var[k].x = new_x;
alpar@1
   212
         }
alpar@1
   213
      }
alpar@1
   214
      /* if the rounded point has not changed (stalling), choose and
alpar@1
   215
         flip some its entries heuristically */
alpar@1
   216
      if (stalling)
alpar@1
   217
      {  /* compute d[j] = |x[j] - round(x[j])| */
alpar@1
   218
         for (k = 1; k <= nv; k++)
alpar@1
   219
         {  col = lp->col[var[k].j];
alpar@1
   220
            var[k].d = fabs(col->prim - (double)var[k].x);
alpar@1
   221
         }
alpar@1
   222
         /* sort the list of binary variables by descending d[j] */
alpar@1
   223
         qsort(&var[1], nv, sizeof(struct VAR), fcmp);
alpar@1
   224
         /* choose and flip some rounded components */
alpar@1
   225
         for (k = 1; k <= nv; k++)
alpar@1
   226
         {  if (k >= 5 && var[k].d < 0.35 || k >= 10) break;
alpar@1
   227
            var[k].x = 1 - var[k].x;
alpar@1
   228
         }
alpar@1
   229
      }
alpar@1
   230
skip: /* check if the time limit has been exhausted */
alpar@1
   231
      if (T->parm->tm_lim < INT_MAX &&
alpar@1
   232
         (double)(T->parm->tm_lim - 1) <=
alpar@1
   233
         1000.0 * xdifftime(xtime(), T->tm_beg)) goto done;
alpar@1
   234
      /* build the objective, which is the distance between the current
alpar@1
   235
         (basic) point and the rounded one */
alpar@1
   236
      lp->dir = GLP_MIN;
alpar@1
   237
      lp->c0 = 0.0;
alpar@1
   238
      for (j = 1; j <= n; j++)
alpar@1
   239
         lp->col[j]->coef = 0.0;
alpar@1
   240
      for (k = 1; k <= nv; k++)
alpar@1
   241
      {  j = var[k].j;
alpar@1
   242
         if (var[k].x == 0)
alpar@1
   243
            lp->col[j]->coef = +1.0;
alpar@1
   244
         else
alpar@1
   245
         {  lp->col[j]->coef = -1.0;
alpar@1
   246
            lp->c0 += 1.0;
alpar@1
   247
         }
alpar@1
   248
      }
alpar@1
   249
      /* minimize the distance with the simplex method */
alpar@1
   250
      glp_init_smcp(&parm);
alpar@1
   251
      if (T->parm->msg_lev <= GLP_MSG_ERR)
alpar@1
   252
         parm.msg_lev = T->parm->msg_lev;
alpar@1
   253
      else if (T->parm->msg_lev <= GLP_MSG_ALL)
alpar@1
   254
      {  parm.msg_lev = GLP_MSG_ON;
alpar@1
   255
         parm.out_dly = 10000;
alpar@1
   256
      }
alpar@1
   257
      ret = glp_simplex(lp, &parm);
alpar@1
   258
      if (ret != 0)
alpar@1
   259
      {  if (T->parm->msg_lev >= GLP_MSG_ERR)
alpar@1
   260
            xprintf("Warning: glp_simplex returned %d\n", ret);
alpar@1
   261
         goto done;
alpar@1
   262
      }
alpar@1
   263
      ret = glp_get_status(lp);
alpar@1
   264
      if (ret != GLP_OPT)
alpar@1
   265
      {  if (T->parm->msg_lev >= GLP_MSG_ERR)
alpar@1
   266
            xprintf("Warning: glp_get_status returned %d\n", ret);
alpar@1
   267
         goto done;
alpar@1
   268
      }
alpar@1
   269
      if (T->parm->msg_lev >= GLP_MSG_DBG)
alpar@1
   270
         xprintf("delta = %g\n", lp->obj_val);
alpar@1
   271
      /* check if the basic solution is integer feasible; note that it
alpar@1
   272
         may be so even if the minimial distance is positive */
alpar@1
   273
      tol = 0.3 * T->parm->tol_int;
alpar@1
   274
      for (k = 1; k <= nv; k++)
alpar@1
   275
      {  col = lp->col[var[k].j];
alpar@1
   276
         if (tol < col->prim && col->prim < 1.0 - tol) break;
alpar@1
   277
      }
alpar@1
   278
      if (k > nv)
alpar@1
   279
      {  /* okay; the basic solution seems to be integer feasible */
alpar@1
   280
         double *x = xcalloc(1+n, sizeof(double));
alpar@1
   281
         for (j = 1; j <= n; j++)
alpar@1
   282
         {  x[j] = lp->col[j]->prim;
alpar@1
   283
            if (P->col[j]->kind == GLP_IV) x[j] = floor(x[j] + 0.5);
alpar@1
   284
         }
alpar@1
   285
#if 1 /* modified by xypron <xypron.glpk@gmx.de> */
alpar@1
   286
         /* reset direction and right-hand side of objective */
alpar@1
   287
         lp->c0  = P->c0;
alpar@1
   288
         lp->dir = P->dir;
alpar@1
   289
         /* fix integer variables */
alpar@1
   290
         for (k = 1; k <= nv; k++)
alpar@1
   291
         {  lp->col[var[k].j]->lb   = x[var[k].j];
alpar@1
   292
            lp->col[var[k].j]->ub   = x[var[k].j];
alpar@1
   293
            lp->col[var[k].j]->type = GLP_FX;
alpar@1
   294
         }
alpar@1
   295
         /* copy original objective function */
alpar@1
   296
         for (j = 1; j <= n; j++)
alpar@1
   297
            lp->col[j]->coef = P->col[j]->coef;
alpar@1
   298
         /* solve original LP and copy result */
alpar@1
   299
         ret = glp_simplex(lp, &parm);
alpar@1
   300
         if (ret != 0)
alpar@1
   301
         {  if (T->parm->msg_lev >= GLP_MSG_ERR)
alpar@1
   302
               xprintf("Warning: glp_simplex returned %d\n", ret);
alpar@1
   303
            goto done;
alpar@1
   304
         }
alpar@1
   305
         ret = glp_get_status(lp);
alpar@1
   306
         if (ret != GLP_OPT)
alpar@1
   307
         {  if (T->parm->msg_lev >= GLP_MSG_ERR)
alpar@1
   308
               xprintf("Warning: glp_get_status returned %d\n", ret);
alpar@1
   309
            goto done;
alpar@1
   310
         }
alpar@1
   311
         for (j = 1; j <= n; j++)
alpar@1
   312
            if (P->col[j]->kind != GLP_IV) x[j] = lp->col[j]->prim;
alpar@1
   313
#endif
alpar@1
   314
         ret = glp_ios_heur_sol(T, x);
alpar@1
   315
         xfree(x);
alpar@1
   316
         if (ret == 0)
alpar@1
   317
         {  /* the integer solution is accepted */
alpar@1
   318
            if (ios_is_hopeful(T, T->curr->bound))
alpar@1
   319
            {  /* it is reasonable to apply the heuristic once again */
alpar@1
   320
               goto more;
alpar@1
   321
            }
alpar@1
   322
            else
alpar@1
   323
            {  /* the best known integer feasible solution just found
alpar@1
   324
                  is close to optimal solution to LP relaxation */
alpar@1
   325
               goto done;
alpar@1
   326
            }
alpar@1
   327
         }
alpar@1
   328
      }
alpar@1
   329
      /* the basic solution is fractional */
alpar@1
   330
      if (dist == DBL_MAX ||
alpar@1
   331
          lp->obj_val <= dist - 1e-6 * (1.0 + dist))
alpar@1
   332
      {  /* the distance is reducing */
alpar@1
   333
         nfail = 0, dist = lp->obj_val;
alpar@1
   334
      }
alpar@1
   335
      else
alpar@1
   336
      {  /* improving the distance failed */
alpar@1
   337
         nfail++;
alpar@1
   338
      }
alpar@1
   339
      if (nfail < 3) goto loop;
alpar@1
   340
      if (npass < 5) goto pass;
alpar@1
   341
done: /* delete working objects */
alpar@1
   342
      if (lp != NULL) glp_delete_prob(lp);
alpar@1
   343
      if (var != NULL) xfree(var);
alpar@1
   344
      if (rand != NULL) rng_delete_rand(rand);
alpar@1
   345
      return;
alpar@1
   346
}
alpar@1
   347
alpar@1
   348
/* eof */