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