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