1 /* glpios11.c (process cuts stored in the local cut pool) */
2
3 /***********************************************************************
4 * This code is part of GLPK (GNU Linear Programming Kit).
5 * Copyright (C) 2005-2018 Free Software Foundation, Inc.
6 * Written by Andrew Makhorin <mao@gnu.org>.
7 *
8 * GLPK is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * GLPK is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
20 ***********************************************************************/
21
22 #include "draft.h"
23 #include "env.h"
24 #include "ios.h"
25
26 /***********************************************************************
27 * NAME
28 *
29 * ios_process_cuts - process cuts stored in the local cut pool
30 *
31 * SYNOPSIS
32 *
33 * #include "glpios.h"
34 * void ios_process_cuts(glp_tree *T);
35 *
36 * DESCRIPTION
37 *
38 * The routine ios_process_cuts analyzes each cut currently stored in
39 * the local cut pool, which must be non-empty, and either adds the cut
40 * to the current subproblem or just discards it. All cuts are assumed
41 * to be locally valid. On exit the local cut pool remains unchanged.
42 *
43 * REFERENCES
44 *
45 * 1. E.Balas, S.Ceria, G.Cornuejols, "Mixed 0-1 Programming by
46 * Lift-and-Project in a Branch-and-Cut Framework", Management Sc.,
47 * 42 (1996) 1229-1246.
48 *
49 * 2. G.Andreello, A.Caprara, and M.Fischetti, "Embedding Cuts in
50 * a Branch&Cut Framework: a Computational Study with {0,1/2}-Cuts",
51 * Preliminary Draft, October 28, 2003, pp.6-8. */
52
53 struct info
54 { /* estimated cut efficiency */
55 IOSCUT *cut;
56 /* pointer to cut in the cut pool */
57 char flag;
58 /* if this flag is set, the cut is included into the current
59 subproblem */
60 double eff;
61 /* cut efficacy (normalized residual) */
62 double deg;
63 /* lower bound to objective degradation */
64 };
65
fcmp(const void * arg1,const void * arg2)66 static int CDECL fcmp(const void *arg1, const void *arg2)
67 { const struct info *info1 = arg1, *info2 = arg2;
68 if (info1->deg == 0.0 && info2->deg == 0.0)
69 { if (info1->eff > info2->eff) return -1;
70 if (info1->eff < info2->eff) return +1;
71 }
72 else
73 { if (info1->deg > info2->deg) return -1;
74 if (info1->deg < info2->deg) return +1;
75 }
76 return 0;
77 }
78
79 static double parallel(IOSCUT *a, IOSCUT *b, double work[]);
80
81 #ifdef NEW_LOCAL /* 02/II-2018 */
ios_process_cuts(glp_tree * T)82 void ios_process_cuts(glp_tree *T)
83 { IOSPOOL *pool;
84 IOSCUT *cut;
85 GLPAIJ *aij;
86 struct info *info;
87 int k, kk, max_cuts, len, ret, *ind;
88 double *val, *work, rhs;
89 /* the current subproblem must exist */
90 xassert(T->curr != NULL);
91 /* the pool must exist and be non-empty */
92 pool = T->local;
93 xassert(pool != NULL);
94 xassert(pool->m > 0);
95 /* allocate working arrays */
96 info = xcalloc(1+pool->m, sizeof(struct info));
97 ind = xcalloc(1+T->n, sizeof(int));
98 val = xcalloc(1+T->n, sizeof(double));
99 work = xcalloc(1+T->n, sizeof(double));
100 for (k = 1; k <= T->n; k++) work[k] = 0.0;
101 /* build the list of cuts stored in the cut pool */
102 for (k = 1; k <= pool->m; k++)
103 info[k].cut = pool->row[k], info[k].flag = 0;
104 /* estimate efficiency of all cuts in the cut pool */
105 for (k = 1; k <= pool->m; k++)
106 { double temp, dy, dz;
107 cut = info[k].cut;
108 /* build the vector of cut coefficients and compute its
109 Euclidean norm */
110 len = 0; temp = 0.0;
111 for (aij = cut->ptr; aij != NULL; aij = aij->r_next)
112 { xassert(1 <= aij->col->j && aij->col->j <= T->n);
113 len++, ind[len] = aij->col->j, val[len] = aij->val;
114 temp += aij->val * aij->val;
115 }
116 if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON;
117 /* transform the cut to express it only through non-basic
118 (auxiliary and structural) variables */
119 len = glp_transform_row(T->mip, len, ind, val);
120 /* determine change in the cut value and in the objective
121 value for the adjacent basis by simulating one step of the
122 dual simplex */
123 switch (cut->type)
124 { case GLP_LO: rhs = cut->lb; break;
125 case GLP_UP: rhs = cut->ub; break;
126 default: xassert(cut != cut);
127 }
128 ret = _glp_analyze_row(T->mip, len, ind, val, cut->type,
129 rhs, 1e-9, NULL, NULL, NULL, NULL, &dy, &dz);
130 /* determine normalized residual and lower bound to objective
131 degradation */
132 if (ret == 0)
133 { info[k].eff = fabs(dy) / sqrt(temp);
134 /* if some reduced costs violates (slightly) their zero
135 bounds (i.e. have wrong signs) due to round-off errors,
136 dz also may have wrong sign being close to zero */
137 if (T->mip->dir == GLP_MIN)
138 { if (dz < 0.0) dz = 0.0;
139 info[k].deg = + dz;
140 }
141 else /* GLP_MAX */
142 { if (dz > 0.0) dz = 0.0;
143 info[k].deg = - dz;
144 }
145 }
146 else if (ret == 1)
147 { /* the constraint is not violated at the current point */
148 info[k].eff = info[k].deg = 0.0;
149 }
150 else if (ret == 2)
151 { /* no dual feasible adjacent basis exists */
152 info[k].eff = 1.0;
153 info[k].deg = DBL_MAX;
154 }
155 else
156 xassert(ret != ret);
157 /* if the degradation is too small, just ignore it */
158 if (info[k].deg < 0.01) info[k].deg = 0.0;
159 }
160 /* sort the list of cuts by decreasing objective degradation and
161 then by decreasing efficacy */
162 qsort(&info[1], pool->m, sizeof(struct info), fcmp);
163 /* only first (most efficient) max_cuts in the list are qualified
164 as candidates to be added to the current subproblem */
165 max_cuts = (T->curr->level == 0 ? 90 : 10);
166 if (max_cuts > pool->m) max_cuts = pool->m;
167 /* add cuts to the current subproblem */
168 #if 0
169 xprintf("*** adding cuts ***\n");
170 #endif
171 for (k = 1; k <= max_cuts; k++)
172 { int i, len;
173 /* if this cut seems to be inefficient, skip it */
174 if (info[k].deg < 0.01 && info[k].eff < 0.01) continue;
175 /* if the angle between this cut and every other cut included
176 in the current subproblem is small, skip this cut */
177 for (kk = 1; kk < k; kk++)
178 { if (info[kk].flag)
179 { if (parallel(info[k].cut, info[kk].cut, work) > 0.90)
180 break;
181 }
182 }
183 if (kk < k) continue;
184 /* add this cut to the current subproblem */
185 #if 0
186 xprintf("eff = %g; deg = %g\n", info[k].eff, info[k].deg);
187 #endif
188 cut = info[k].cut, info[k].flag = 1;
189 i = glp_add_rows(T->mip, 1);
190 if (cut->name != NULL)
191 glp_set_row_name(T->mip, i, cut->name);
192 xassert(T->mip->row[i]->origin == GLP_RF_CUT);
193 T->mip->row[i]->klass = cut->klass;
194 len = 0;
195 for (aij = cut->ptr; aij != NULL; aij = aij->r_next)
196 len++, ind[len] = aij->col->j, val[len] = aij->val;
197 glp_set_mat_row(T->mip, i, len, ind, val);
198 switch (cut->type)
199 { case GLP_LO: rhs = cut->lb; break;
200 case GLP_UP: rhs = cut->ub; break;
201 default: xassert(cut != cut);
202 }
203 glp_set_row_bnds(T->mip, i, cut->type, rhs, rhs);
204 }
205 /* free working arrays */
206 xfree(info);
207 xfree(ind);
208 xfree(val);
209 xfree(work);
210 return;
211 }
212 #else
ios_process_cuts(glp_tree * T)213 void ios_process_cuts(glp_tree *T)
214 { IOSPOOL *pool;
215 IOSCUT *cut;
216 IOSAIJ *aij;
217 struct info *info;
218 int k, kk, max_cuts, len, ret, *ind;
219 double *val, *work;
220 /* the current subproblem must exist */
221 xassert(T->curr != NULL);
222 /* the pool must exist and be non-empty */
223 pool = T->local;
224 xassert(pool != NULL);
225 xassert(pool->size > 0);
226 /* allocate working arrays */
227 info = xcalloc(1+pool->size, sizeof(struct info));
228 ind = xcalloc(1+T->n, sizeof(int));
229 val = xcalloc(1+T->n, sizeof(double));
230 work = xcalloc(1+T->n, sizeof(double));
231 for (k = 1; k <= T->n; k++) work[k] = 0.0;
232 /* build the list of cuts stored in the cut pool */
233 for (k = 0, cut = pool->head; cut != NULL; cut = cut->next)
234 k++, info[k].cut = cut, info[k].flag = 0;
235 xassert(k == pool->size);
236 /* estimate efficiency of all cuts in the cut pool */
237 for (k = 1; k <= pool->size; k++)
238 { double temp, dy, dz;
239 cut = info[k].cut;
240 /* build the vector of cut coefficients and compute its
241 Euclidean norm */
242 len = 0; temp = 0.0;
243 for (aij = cut->ptr; aij != NULL; aij = aij->next)
244 { xassert(1 <= aij->j && aij->j <= T->n);
245 len++, ind[len] = aij->j, val[len] = aij->val;
246 temp += aij->val * aij->val;
247 }
248 if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON;
249 /* transform the cut to express it only through non-basic
250 (auxiliary and structural) variables */
251 len = glp_transform_row(T->mip, len, ind, val);
252 /* determine change in the cut value and in the objective
253 value for the adjacent basis by simulating one step of the
254 dual simplex */
255 ret = _glp_analyze_row(T->mip, len, ind, val, cut->type,
256 cut->rhs, 1e-9, NULL, NULL, NULL, NULL, &dy, &dz);
257 /* determine normalized residual and lower bound to objective
258 degradation */
259 if (ret == 0)
260 { info[k].eff = fabs(dy) / sqrt(temp);
261 /* if some reduced costs violates (slightly) their zero
262 bounds (i.e. have wrong signs) due to round-off errors,
263 dz also may have wrong sign being close to zero */
264 if (T->mip->dir == GLP_MIN)
265 { if (dz < 0.0) dz = 0.0;
266 info[k].deg = + dz;
267 }
268 else /* GLP_MAX */
269 { if (dz > 0.0) dz = 0.0;
270 info[k].deg = - dz;
271 }
272 }
273 else if (ret == 1)
274 { /* the constraint is not violated at the current point */
275 info[k].eff = info[k].deg = 0.0;
276 }
277 else if (ret == 2)
278 { /* no dual feasible adjacent basis exists */
279 info[k].eff = 1.0;
280 info[k].deg = DBL_MAX;
281 }
282 else
283 xassert(ret != ret);
284 /* if the degradation is too small, just ignore it */
285 if (info[k].deg < 0.01) info[k].deg = 0.0;
286 }
287 /* sort the list of cuts by decreasing objective degradation and
288 then by decreasing efficacy */
289 qsort(&info[1], pool->size, sizeof(struct info), fcmp);
290 /* only first (most efficient) max_cuts in the list are qualified
291 as candidates to be added to the current subproblem */
292 max_cuts = (T->curr->level == 0 ? 90 : 10);
293 if (max_cuts > pool->size) max_cuts = pool->size;
294 /* add cuts to the current subproblem */
295 #if 0
296 xprintf("*** adding cuts ***\n");
297 #endif
298 for (k = 1; k <= max_cuts; k++)
299 { int i, len;
300 /* if this cut seems to be inefficient, skip it */
301 if (info[k].deg < 0.01 && info[k].eff < 0.01) continue;
302 /* if the angle between this cut and every other cut included
303 in the current subproblem is small, skip this cut */
304 for (kk = 1; kk < k; kk++)
305 { if (info[kk].flag)
306 { if (parallel(info[k].cut, info[kk].cut, work) > 0.90)
307 break;
308 }
309 }
310 if (kk < k) continue;
311 /* add this cut to the current subproblem */
312 #if 0
313 xprintf("eff = %g; deg = %g\n", info[k].eff, info[k].deg);
314 #endif
315 cut = info[k].cut, info[k].flag = 1;
316 i = glp_add_rows(T->mip, 1);
317 if (cut->name != NULL)
318 glp_set_row_name(T->mip, i, cut->name);
319 xassert(T->mip->row[i]->origin == GLP_RF_CUT);
320 T->mip->row[i]->klass = cut->klass;
321 len = 0;
322 for (aij = cut->ptr; aij != NULL; aij = aij->next)
323 len++, ind[len] = aij->j, val[len] = aij->val;
324 glp_set_mat_row(T->mip, i, len, ind, val);
325 xassert(cut->type == GLP_LO || cut->type == GLP_UP);
326 glp_set_row_bnds(T->mip, i, cut->type, cut->rhs, cut->rhs);
327 }
328 /* free working arrays */
329 xfree(info);
330 xfree(ind);
331 xfree(val);
332 xfree(work);
333 return;
334 }
335 #endif
336
337 #if 0
338 /***********************************************************************
339 * Given a cut a * x >= b (<= b) the routine efficacy computes the cut
340 * efficacy as follows:
341 *
342 * eff = d * (a * x~ - b) / ||a||,
343 *
344 * where d is -1 (in case of '>= b') or +1 (in case of '<= b'), x~ is
345 * the vector of values of structural variables in optimal solution to
346 * LP relaxation of the current subproblem, ||a|| is the Euclidean norm
347 * of the vector of cut coefficients.
348 *
349 * If the cut is violated at point x~, the efficacy eff is positive,
350 * and its value is the Euclidean distance between x~ and the cut plane
351 * a * x = b in the space of structural variables.
352 *
353 * Following geometrical intuition, it is quite natural to consider
354 * this distance as a first-order measure of the expected efficacy of
355 * the cut: the larger the distance the better the cut [1]. */
356
357 static double efficacy(glp_tree *T, IOSCUT *cut)
358 { glp_prob *mip = T->mip;
359 IOSAIJ *aij;
360 double s = 0.0, t = 0.0, temp;
361 for (aij = cut->ptr; aij != NULL; aij = aij->next)
362 { xassert(1 <= aij->j && aij->j <= mip->n);
363 s += aij->val * mip->col[aij->j]->prim;
364 t += aij->val * aij->val;
365 }
366 temp = sqrt(t);
367 if (temp < DBL_EPSILON) temp = DBL_EPSILON;
368 if (cut->type == GLP_LO)
369 temp = (s >= cut->rhs ? 0.0 : (cut->rhs - s) / temp);
370 else if (cut->type == GLP_UP)
371 temp = (s <= cut->rhs ? 0.0 : (s - cut->rhs) / temp);
372 else
373 xassert(cut != cut);
374 return temp;
375 }
376 #endif
377
378 /***********************************************************************
379 * Given two cuts a1 * x >= b1 (<= b1) and a2 * x >= b2 (<= b2) the
380 * routine parallel computes the cosine of angle between the cut planes
381 * a1 * x = b1 and a2 * x = b2 (which is the acute angle between two
382 * normals to these planes) in the space of structural variables as
383 * follows:
384 *
385 * cos phi = (a1' * a2) / (||a1|| * ||a2||),
386 *
387 * where (a1' * a2) is a dot product of vectors of cut coefficients,
388 * ||a1|| and ||a2|| are Euclidean norms of vectors a1 and a2.
389 *
390 * Note that requirement cos phi = 0 forces the cuts to be orthogonal,
391 * i.e. with disjoint support, while requirement cos phi <= 0.999 means
392 * only avoiding duplicate (parallel) cuts [1]. */
393
394 #ifdef NEW_LOCAL /* 02/II-2018 */
parallel(IOSCUT * a,IOSCUT * b,double work[])395 static double parallel(IOSCUT *a, IOSCUT *b, double work[])
396 { GLPAIJ *aij;
397 double s = 0.0, sa = 0.0, sb = 0.0, temp;
398 for (aij = a->ptr; aij != NULL; aij = aij->r_next)
399 { work[aij->col->j] = aij->val;
400 sa += aij->val * aij->val;
401 }
402 for (aij = b->ptr; aij != NULL; aij = aij->r_next)
403 { s += work[aij->col->j] * aij->val;
404 sb += aij->val * aij->val;
405 }
406 for (aij = a->ptr; aij != NULL; aij = aij->r_next)
407 work[aij->col->j] = 0.0;
408 temp = sqrt(sa) * sqrt(sb);
409 if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON;
410 return s / temp;
411 }
412 #else
parallel(IOSCUT * a,IOSCUT * b,double work[])413 static double parallel(IOSCUT *a, IOSCUT *b, double work[])
414 { IOSAIJ *aij;
415 double s = 0.0, sa = 0.0, sb = 0.0, temp;
416 for (aij = a->ptr; aij != NULL; aij = aij->next)
417 { work[aij->j] = aij->val;
418 sa += aij->val * aij->val;
419 }
420 for (aij = b->ptr; aij != NULL; aij = aij->next)
421 { s += work[aij->j] * aij->val;
422 sb += aij->val * aij->val;
423 }
424 for (aij = a->ptr; aij != NULL; aij = aij->next)
425 work[aij->j] = 0.0;
426 temp = sqrt(sa) * sqrt(sb);
427 if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON;
428 return s / temp;
429 }
430 #endif
431
432 /* eof */
433