Scippy

GCG

Branch-and-Price & Column Generation for Everyone

heur_gcgshifting.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program */
4 /* GCG --- Generic Column Generation */
5 /* a Dantzig-Wolfe decomposition based extension */
6 /* of the branch-cut-and-price framework */
7 /* SCIP --- Solving Constraint Integer Programs */
8 /* */
9 /* Copyright (C) 2010-2021 Operations Research, RWTH Aachen University */
10 /* Zuse Institute Berlin (ZIB) */
11 /* */
12 /* This program is free software; you can redistribute it and/or */
13 /* modify it under the terms of the GNU Lesser General Public License */
14 /* as published by the Free Software Foundation; either version 3 */
15 /* of the License, or (at your option) any later version. */
16 /* */
17 /* This program is distributed in the hope that it will be useful, */
18 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
19 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
20 /* GNU Lesser General Public License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with this program; if not, write to the Free Software */
24 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
25 /* */
26 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27 
28 /**@file heur_gcgshifting.c
29  * @brief LP gcgrounding heuristic that tries to recover from intermediate infeasibilities and shifts continuous variables
30  * @author Tobias Achterberg
31  * @author Christian Puchert
32  */
33 
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35 
36 #include <assert.h>
37 #include <string.h>
38 
39 #include "heur_gcgshifting.h"
40 #include "gcg.h"
41 #include "relax_gcg.h"
42 #include "scip/misc.h"
43 
44 
45 #define HEUR_NAME "gcgshifting"
46 #define HEUR_DESC "LP rounding heuristic on original variables with infeasibility recovering also using continuous variables"
47 #define HEUR_DISPCHAR 's'
48 #define HEUR_PRIORITY -5000
49 #define HEUR_FREQ 10
50 #define HEUR_FREQOFS 0
51 #define HEUR_MAXDEPTH -1
52 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
53 #define HEUR_USESSUBSCIP FALSE
54 
55 #define MAXSHIFTINGS 50 /**< maximal number of non improving shiftings */
56 #define WEIGHTFACTOR 1.1
57 #define DEFAULT_RANDSEED 31 /**< initial random seed */
58 
59 
60 /* locally defined heuristic data */
61 struct SCIP_HeurData
62 {
63  SCIP_SOL* sol; /**< working solution */
64  SCIP_RANDNUMGEN* randnumgen; /**< random number generator */
65  SCIP_Longint lastlp; /**< last LP number where the heuristic was applied */
66 };
67 
68 
69 
70 
71 /*
72  * local methods
73  */
74 
75 /** update row violation arrays after a row's activity value changed */
76 static
78  SCIP* scip, /**< SCIP data structure */
79  SCIP_ROW* row, /**< LP row */
80  SCIP_ROW** violrows, /**< array with currently violated rows */
81  int* violrowpos, /**< position of LP rows in violrows array */
82  int* nviolrows, /**< pointer to the number of currently violated rows */
83  SCIP_Real oldactivity, /**< old activity value of LP row */
84  SCIP_Real newactivity /**< new activity value of LP row */
85  )
86 {
87  SCIP_Real lhs;
88  SCIP_Real rhs;
89  SCIP_Bool oldviol;
90  SCIP_Bool newviol;
91 
92  assert(violrows != NULL);
93  assert(violrowpos != NULL);
94  assert(nviolrows != NULL);
95 
96  lhs = SCIProwGetLhs(row);
97  rhs = SCIProwGetRhs(row);
98  oldviol = (SCIPisFeasLT(scip, oldactivity, lhs) || SCIPisFeasGT(scip, oldactivity, rhs));
99  newviol = (SCIPisFeasLT(scip, newactivity, lhs) || SCIPisFeasGT(scip, newactivity, rhs));
100  if( oldviol != newviol )
101  {
102  int rowpos;
103 
104  rowpos = SCIProwGetLPPos(row);
105  assert(rowpos >= 0);
106 
107  if( oldviol )
108  {
109  int violpos;
110 
111  /* the row violation was repaired: remove row from violrows array, decrease violation count */
112  violpos = violrowpos[rowpos];
113  assert(0 <= violpos && violpos < *nviolrows);
114  assert(violrows[violpos] == row);
115  violrowpos[rowpos] = -1;
116  if( violpos != *nviolrows-1 )
117  {
118  violrows[violpos] = violrows[*nviolrows-1];
119  violrowpos[SCIProwGetLPPos(violrows[violpos])] = violpos;
120  }
121  (*nviolrows)--;
122  }
123  else
124  {
125  /* the row is now violated: add row to violrows array, increase violation count */
126  assert(violrowpos[rowpos] == -1);
127  violrows[*nviolrows] = row;
128  violrowpos[rowpos] = *nviolrows;
129  (*nviolrows)++;
130  }
131  }
132 }
133 
134 /** update row activities after a variable's solution value changed */
135 static
136 SCIP_RETCODE updateActivities(
137  SCIP* scip, /**< SCIP data structure */
138  SCIP_Real* activities, /**< LP row activities */
139  SCIP_ROW** violrows, /**< array with currently violated rows */
140  int* violrowpos, /**< position of LP rows in violrows array */
141  int* nviolrows, /**< pointer to the number of currently violated rows */
142  int nlprows, /**< number of rows in current LP */
143  SCIP_VAR* var, /**< variable that has been changed */
144  SCIP_Real oldsolval, /**< old solution value of variable */
145  SCIP_Real newsolval /**< new solution value of variable */
146  )
147 {
148  SCIP_COL* col;
149  SCIP_ROW** colrows;
150  SCIP_Real* colvals;
151  SCIP_Real delta;
152  int ncolrows;
153  int r;
154 
155  assert(activities != NULL);
156  assert(nviolrows != NULL);
157  assert(0 <= *nviolrows && *nviolrows <= nlprows);
158 
159  delta = newsolval - oldsolval;
160  col = SCIPvarGetCol(var);
161  colrows = SCIPcolGetRows(col);
162  colvals = SCIPcolGetVals(col);
163  ncolrows = SCIPcolGetNLPNonz(col);
164  assert(ncolrows == 0 || (colrows != NULL && colvals != NULL));
165 
166  for( r = 0; r < ncolrows; ++r )
167  {
168  SCIP_ROW* row;
169  int rowpos;
170 
171  row = colrows[r];
172  rowpos = SCIProwGetLPPos(row);
173  assert(-1 <= rowpos && rowpos < nlprows);
174 
175  if( rowpos >= 0 && !SCIProwIsLocal(row) )
176  {
177  SCIP_Real oldactivity;
178 
179  assert(SCIProwIsInLP(row));
180 
181  /* update row activity */
182  oldactivity = activities[rowpos];
183  if( !SCIPisInfinity(scip, -oldactivity) && !SCIPisInfinity(scip, oldactivity) )
184  {
185  SCIP_Real newactivity;
186 
187  newactivity = oldactivity + delta * colvals[r];
188  if( SCIPisInfinity(scip, newactivity) )
189  newactivity = SCIPinfinity(scip);
190  else if( SCIPisInfinity(scip, -newactivity) )
191  newactivity = -SCIPinfinity(scip);
192  activities[rowpos] = newactivity;
193 
194  /* update row violation arrays */
195  updateViolations(scip, row, violrows, violrowpos, nviolrows, oldactivity, newactivity);
196  }
197  }
198  }
199 
200  return SCIP_OKAY;
201 }
202 
203 /** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows;
204  * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
205  * prefer fractional integers over other variables in order to become integral during the process;
206  * shifting in a direction is forbidden, if this forces the objective value over the upper bound, or if the variable
207  * was already shifted in the opposite direction
208  */
209 static
210 SCIP_RETCODE selectShifting(
211  SCIP* scip, /**< SCIP data structure */
212  SCIP_SOL* sol, /**< primal solution */
213  SCIP_ROW* row, /**< LP row */
214  SCIP_Real rowactivity, /**< activity of LP row */
215  int direction, /**< should the activity be increased (+1) or decreased (-1)? */
216  SCIP_Real* nincreases, /**< array with weighted number of increasings per variables */
217  SCIP_Real* ndecreases, /**< array with weighted number of decreasings per variables */
218  SCIP_Real increaseweight, /**< current weight of increase/decrease updates */
219  SCIP_VAR** shiftvar, /**< pointer to store the shifting variable, returns NULL if impossible */
220  SCIP_Real* oldsolval, /**< pointer to store old solution value of shifting variable */
221  SCIP_Real* newsolval /**< pointer to store new (shifted) solution value of shifting variable */
222  )
223 {
224  SCIP_COL** rowcols;
225  SCIP_Real* rowvals;
226  int nrowcols;
227  SCIP_Real activitydelta;
228  SCIP_Real bestshiftscore;
229  SCIP_Real bestdeltaobj;
230  int c;
231 
232  assert(direction == +1 || direction == -1);
233  assert(nincreases != NULL);
234  assert(ndecreases != NULL);
235  assert(shiftvar != NULL);
236  assert(oldsolval != NULL);
237  assert(newsolval != NULL);
238 
239  /* get row entries */
240  rowcols = SCIProwGetCols(row);
241  rowvals = SCIProwGetVals(row);
242  nrowcols = SCIProwGetNLPNonz(row);
243 
244  /* calculate how much the activity must be shifted in order to become feasible */
245  activitydelta = (direction == +1 ? SCIProwGetLhs(row) - rowactivity : SCIProwGetRhs(row) - rowactivity);
246  assert((direction == +1 && SCIPisPositive(scip, activitydelta))
247  || (direction == -1 && SCIPisNegative(scip, activitydelta)));
248 
249  /* select shifting variable */
250  bestshiftscore = SCIP_REAL_MAX;
251  bestdeltaobj = SCIPinfinity(scip);
252  *shiftvar = NULL;
253  *newsolval = 0.0;
254  *oldsolval = 0.0;
255  for( c = 0; c < nrowcols; ++c )
256  {
257  SCIP_COL* col;
258  SCIP_VAR* var;
259  SCIP_Real val;
260  SCIP_Real solval;
261  SCIP_Real shiftscore;
262  SCIP_Bool isinteger;
263  SCIP_Bool isfrac;
264  SCIP_Bool increase;
265 
266  col = rowcols[c];
267  var = SCIPcolGetVar(col);
268  val = rowvals[c];
269  assert(!SCIPisZero(scip, val));
270  solval = SCIPgetSolVal(scip, sol, var);
271 
272  isinteger = (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER);
273  isfrac = isinteger && !SCIPisFeasIntegral(scip, solval);
274  increase = (direction * val > 0.0);
275 
276  /* calculate the score of the shifting (prefer smaller values) */
277  if( isfrac )
278  shiftscore = increase ? -1.0 / (SCIPvarGetNLocksUp(var) + 1.0) :
279  -1.0 / (SCIPvarGetNLocksDown(var) + 1.0);
280  else
281  {
282  int probindex;
283  probindex = SCIPvarGetProbindex(var);
284 
285  if( increase )
286  shiftscore = ndecreases[probindex]/increaseweight;
287  else
288  shiftscore = nincreases[probindex]/increaseweight;
289  if( isinteger )
290  shiftscore += 1.0;
291  }
292 
293  if( shiftscore <= bestshiftscore )
294  {
295  SCIP_Real deltaobj;
296  SCIP_Real shiftval;
297 
298  if( !increase )
299  {
300  /* shifting down */
301  assert(direction * val < 0.0);
302  if( isfrac )
303  shiftval = SCIPfeasFloor(scip, solval);
304  else
305  {
306  SCIP_Real lb;
307 
308  assert(activitydelta/val < 0.0);
309  shiftval = solval + activitydelta/val;
310  assert(shiftval <= solval); /* may be equal due to numerical digit erasement in the subtraction */
311  if( SCIPvarIsIntegral(var) )
312  shiftval = SCIPfeasFloor(scip, shiftval);
313  lb = SCIPvarGetLbGlobal(var);
314  shiftval = MAX(shiftval, lb);
315  }
316  }
317  else
318  {
319  /* shifting up */
320  assert(direction * val > 0.0);
321  if( isfrac )
322  shiftval = SCIPfeasCeil(scip, solval);
323  else
324  {
325  SCIP_Real ub;
326 
327  assert(activitydelta/val > 0.0);
328  shiftval = solval + activitydelta/val;
329  assert(shiftval >= solval); /* may be equal due to numerical digit erasement in the subtraction */
330  if( SCIPvarIsIntegral(var) )
331  shiftval = SCIPfeasCeil(scip, shiftval);
332  ub = SCIPvarGetUbGlobal(var);
333  shiftval = MIN(shiftval, ub);
334  }
335  }
336 
337  if( SCIPisEQ(scip, shiftval, solval) )
338  continue;
339 
340  deltaobj = SCIPvarGetObj(var) * (shiftval - solval);
341  if( shiftscore < bestshiftscore || deltaobj < bestdeltaobj )
342  {
343  bestshiftscore = shiftscore;
344  bestdeltaobj = deltaobj;
345  *shiftvar = var;
346  *oldsolval = solval;
347  *newsolval = shiftval;
348  }
349  }
350  }
351 
352  return SCIP_OKAY;
353 }
354 
355 /** returns a fractional variable, that has most impact on rows in opposite direction, i.e. that is most crucial to
356  * fix in the other direction;
357  * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
358  * shifting in a direction is forbidden, if this forces the objective value over the upper bound
359  */
360 static
362  SCIP* scip, /**< SCIP data structure */
363  SCIP_SOL* sol, /**< primal solution */
364  SCIP_Real minobj, /**< minimal objective value possible after shifting remaining fractional vars */
365  SCIP_VAR** lpcands, /**< fractional variables in LP */
366  int nlpcands, /**< number of fractional variables in LP */
367  SCIP_VAR** shiftvar, /**< pointer to store the shifting variable, returns NULL if impossible */
368  SCIP_Real* oldsolval, /**< old (fractional) solution value of shifting variable */
369  SCIP_Real* newsolval /**< new (shifted) solution value of shifting variable */
370  )
371 {
372  SCIP_Real bestdeltaobj;
373  int maxnlocks;
374  int v;
375 
376  assert(shiftvar != NULL);
377  assert(oldsolval != NULL);
378  assert(newsolval != NULL);
379 
380  /* select shifting variable */
381  maxnlocks = -1;
382  bestdeltaobj = SCIPinfinity(scip);
383  *shiftvar = NULL;
384  for( v = 0; v < nlpcands; ++v )
385  {
386  SCIP_VAR* var;
387  SCIP_Real solval;
388 
389  var = lpcands[v];
390  assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER);
391 
392  solval = SCIPgetSolVal(scip, sol, var);
393  if( !SCIPisFeasIntegral(scip, solval) )
394  {
395  SCIP_Real shiftval;
396  SCIP_Real obj;
397  SCIP_Real deltaobj;
398  int nlocks;
399 
400  obj = SCIPvarGetObj(var);
401 
402  /* shifting down */
403  nlocks = SCIPvarGetNLocksUp(var);
404  if( nlocks >= maxnlocks )
405  {
406  shiftval = SCIPfeasFloor(scip, solval);
407  deltaobj = obj * (shiftval - solval);
408  if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj - obj < SCIPgetCutoffbound(scip) )
409  {
410  maxnlocks = nlocks;
411  bestdeltaobj = deltaobj;
412  *shiftvar = var;
413  *oldsolval = solval;
414  *newsolval = shiftval;
415  }
416  }
417 
418  /* shifting up */
419  nlocks = SCIPvarGetNLocksDown(var);
420  if( nlocks >= maxnlocks )
421  {
422  shiftval = SCIPfeasCeil(scip, solval);
423  deltaobj = obj * (shiftval - solval);
424  if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj + obj < SCIPgetCutoffbound(scip) )
425  {
426  maxnlocks = nlocks;
427  bestdeltaobj = deltaobj;
428  *shiftvar = var;
429  *oldsolval = solval;
430  *newsolval = shiftval;
431  }
432  }
433  }
434  }
435 
436  return SCIP_OKAY;
437 }
438 
439 /** adds a given value to the fractionality counters of the rows in which the given variable appears */
440 static
442  int* nfracsinrow, /**< array to store number of fractional variables per row */
443  int nlprows, /**< number of rows in LP */
444  SCIP_VAR* var, /**< variable for which the counting should be updated */
445  int incval /**< value that should be added to the corresponding array entries */
446  )
447 {
448  SCIP_COL* col;
449  SCIP_ROW** rows;
450  int nrows;
451  int r;
452 
453  col = SCIPvarGetCol(var);
454  rows = SCIPcolGetRows(col);
455  nrows = SCIPcolGetNLPNonz(col);
456  for( r = 0; r < nrows; ++r )
457  {
458  int rowidx;
459 
460  rowidx = SCIProwGetLPPos(rows[r]);
461  assert(0 <= rowidx && rowidx < nlprows);
462  nfracsinrow[rowidx] += incval;
463  assert(nfracsinrow[rowidx] >= 0);
464  }
465 }
466 
467 
468 
469 /*
470  * Callback methods
471  */
472 
473 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
474 #define heurCopyGcgshifting NULL
475 
476 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
477 #define heurFreeGcgshifting NULL
478 
479 
480 /** initialization method of primal heuristic (called after problem was transformed) */
481 static
482 SCIP_DECL_HEURINIT(heurInitGcgshifting) /*lint --e{715}*/
483 { /*lint --e{715}*/
484  SCIP_HEURDATA* heurdata;
485 
486  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
487  assert(SCIPheurGetData(heur) == NULL);
488 
489  /* create heuristic data */
490  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
491  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
492  heurdata->lastlp = -1;
493 
494  /* create random number generator */
495  SCIP_CALL( SCIPcreateRandom(scip, &heurdata->randnumgen,
496  SCIPinitializeRandomSeed(scip, DEFAULT_RANDSEED), TRUE) );
497 
498  SCIPheurSetData(heur, heurdata);
499 
500  return SCIP_OKAY;
501 }
502 
503 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
504 static
505 SCIP_DECL_HEUREXIT(heurExitGcgshifting) /*lint --e{715}*/
506 { /*lint --e{715}*/
507  SCIP_HEURDATA* heurdata;
508 
509  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
510 
511  /* free heuristic data */
512  heurdata = SCIPheurGetData(heur);
513  assert(heurdata != NULL);
514  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
515 
516  /* free random number generator */
517  SCIPfreeRandom(scip, &heurdata->randnumgen);
518 
519  SCIPfreeMemory(scip, &heurdata);
520  SCIPheurSetData(heur, NULL);
521 
522  return SCIP_OKAY;
523 }
524 
525 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
526 static
527 SCIP_DECL_HEURINITSOL(heurInitsolGcgshifting)
528 {
529  SCIP_HEURDATA* heurdata;
530 
531  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
532 
533  heurdata = SCIPheurGetData(heur);
534  assert(heurdata != NULL);
535  heurdata->lastlp = -1;
536 
537  return SCIP_OKAY;
538 }
539 
540 
541 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
542 #define heurExitsolGcgshifting NULL
543 
544 
545 /** execution method of primal heuristic */
546 static
547 SCIP_DECL_HEUREXEC(heurExecGcgshifting) /*lint --e{715}*/
548 { /*lint --e{715}*/
549  SCIP* masterprob;
550  SCIP_HEURDATA* heurdata;
551  SCIP_SOL* sol;
552  SCIP_VAR** lpcands;
553  SCIP_Real* lpcandssol;
554  SCIP_ROW** lprows;
555  SCIP_Real* activities;
556  SCIP_ROW** violrows;
557  SCIP_Real* nincreases;
558  SCIP_Real* ndecreases;
559  int* violrowpos;
560  int* nfracsinrow;
561  SCIP_Real increaseweight;
562  SCIP_Real obj;
563  SCIP_Real minobj;
564  int nlpcands;
565  int nlprows;
566  int nvars;
567  int nfrac;
568  int nviolrows;
569  int minnviolrows;
570  int nnonimprovingshifts;
571  int c;
572  int r;
573  SCIP_Longint nlps;
574  SCIP_Longint ncalls;
575  SCIP_Longint nsolsfound;
576  SCIP_Longint nnodes;
577 
578  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
579  assert(scip != NULL);
580  assert(result != NULL);
581 
582  /* get master problem */
583  masterprob = GCGgetMasterprob(scip);
584  assert(masterprob != NULL);
585 
586  *result = SCIP_DIDNOTRUN;
587 
588  /* do not execute the heuristic on invalid relaxation solutions
589  * (which is the case if the node has been cut off)
590  */
591  if( !SCIPisRelaxSolValid(scip) )
592  {
593  SCIPdebugMessage("skipping GCG shifting: invalid relaxation solution\n");
594  return SCIP_OKAY;
595  }
596 
597  /* only call heuristic, if an optimal LP solution is at hand */
598  if( SCIPgetStage(masterprob) > SCIP_STAGE_SOLVING || SCIPgetLPSolstat(masterprob) != SCIP_LPSOLSTAT_OPTIMAL )
599  return SCIP_OKAY;
600 
601  /* get heuristic data */
602  heurdata = SCIPheurGetData(heur);
603  assert(heurdata != NULL);
604 
605  /* don't call heuristic, if we have already processed the current LP solution */
606  nlps = SCIPgetNLPs(masterprob);
607  if( nlps == heurdata->lastlp )
608  return SCIP_OKAY;
609  heurdata->lastlp = nlps;
610 
611  /* don't call heuristic, if it was not successful enough in the past */
612  ncalls = SCIPheurGetNCalls(heur);
613  nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + SCIPheurGetNSolsFound(heur);
614  nnodes = SCIPgetNNodes(scip);
615  if( nnodes % ((ncalls/100)/(nsolsfound+1)+1) != 0 )
616  return SCIP_OKAY;
617 
618  /* get fractional variables, that should be integral */
619  SCIP_CALL( SCIPgetExternBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL, NULL, NULL) );
620  nfrac = nlpcands;
621 
622  /* only call heuristic, if LP solution is fractional */
623  if( nfrac == 0 )
624  return SCIP_OKAY;
625 
626  *result = SCIP_DIDNOTFIND;
627 
628  /* get LP rows */
629  SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) );
630 
631  SCIPdebugMessage("executing GCG shifting heuristic: %d LP rows, %d fractionals\n", nlprows, nfrac);;
632 
633  /* get memory for activities, violated rows, and row violation positions */
634  nvars = SCIPgetNVars(scip);
635  SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) );
636  SCIP_CALL( SCIPallocBufferArray(scip, &violrows, nlprows) );
637  SCIP_CALL( SCIPallocBufferArray(scip, &violrowpos, nlprows) );
638  SCIP_CALL( SCIPallocBufferArray(scip, &nfracsinrow, nlprows) );
639  SCIP_CALL( SCIPallocBufferArray(scip, &nincreases, nvars) );
640  SCIP_CALL( SCIPallocBufferArray(scip, &ndecreases, nvars) );
641  BMSclearMemoryArray(nfracsinrow, nlprows);
642  BMSclearMemoryArray(nincreases, nvars);
643  BMSclearMemoryArray(ndecreases, nvars);
644 
645  /* get the activities for all globally valid rows;
646  * the rows should be feasible, but due to numerical inaccuracies in the LP solver, they can be violated
647  */
648  nviolrows = 0;
649  for( r = 0; r < nlprows; ++r )
650  {
651  SCIP_ROW* row;
652 
653  row = lprows[r];
654  assert(SCIProwGetLPPos(row) == r);
655 
656  if( !SCIProwIsLocal(row) )
657  {
658  activities[r] = SCIPgetRowSolActivity(scip, row, GCGrelaxGetCurrentOrigSol(scip));
659  if( SCIPisFeasLT(scip, activities[r], SCIProwGetLhs(row) )
660  || SCIPisFeasGT(scip, activities[r], SCIProwGetRhs(row)) )
661  {
662  violrows[nviolrows] = row;
663  violrowpos[r] = nviolrows;
664  nviolrows++;
665  }
666  else
667  violrowpos[r] = -1;
668  }
669  }
670 
671  /* calc the current number of fractional variables in rows */
672  for( c = 0; c < nlpcands; ++c )
673  addFracCounter(nfracsinrow, nlprows, lpcands[c], +1);
674 
675  /* get the working solution from heuristic's local data */
676  sol = heurdata->sol;
677  assert(sol != NULL);
678 
679  /* copy the current LP solution to the working solution */
680  SCIP_CALL( SCIPlinkRelaxSol(scip, sol) );
681 
682  /* calculate the minimal objective value possible after rounding fractional variables */
683  minobj = SCIPgetSolTransObj(scip, sol);
684  /* since the heuristic timing was changed to AFTERNODE, it might happen that it is called on a
685  * node with has been cut off; in that case, delay the heuristic
686  */
687  if( minobj >= SCIPgetCutoffbound(scip) )
688  {
689  *result = SCIP_DELAYED;
690  SCIPfreeBufferArray(scip, &ndecreases);
691  SCIPfreeBufferArray(scip, &nincreases);
692  SCIPfreeBufferArray(scip, &nfracsinrow);
693  SCIPfreeBufferArray(scip, &violrowpos);
694  SCIPfreeBufferArray(scip, &violrows);
695  SCIPfreeBufferArray(scip, &activities);
696  return SCIP_OKAY;
697  }
698  for( c = 0; c < nlpcands; ++c )
699  {
700  SCIP_Real bestshiftval;
701 
702  obj = SCIPvarGetObj(lpcands[c]);
703  bestshiftval = obj > 0.0 ? SCIPfeasFloor(scip, lpcandssol[c]) : SCIPfeasCeil(scip, lpcandssol[c]);
704  minobj += obj * (bestshiftval - lpcandssol[c]);
705  }
706 
707  /* try to shift remaining variables in order to become/stay feasible */
708  nnonimprovingshifts = 0;
709  minnviolrows = INT_MAX;
710  increaseweight = 1.0;
711  while( (nfrac > 0 || nviolrows > 0) && nnonimprovingshifts < MAXSHIFTINGS )
712  {
713  SCIP_VAR* shiftvar;
714  SCIP_Real oldsolval;
715  SCIP_Real newsolval;
716  SCIP_Bool oldsolvalisfrac;
717  int nprevviolrows;
718 
719  SCIPdebugMessage("GCG shifting heuristic: nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g), cutoff=%g\n",
720  nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj),
721  SCIPretransformObj(scip, SCIPgetCutoffbound(scip)));
722 
723  nprevviolrows = nviolrows;
724 
725  /* choose next variable to process:
726  * - if a violated row exists, shift a variable decreasing the violation, that has least impact on other rows
727  * - otherwise, shift a variable, that has strongest devastating impact on rows in opposite direction
728  */
729  shiftvar = NULL;
730  oldsolval = 0.0;
731  newsolval = 0.0;
732  if( nviolrows > 0 && (nfrac == 0 || nnonimprovingshifts < MAXSHIFTINGS-1) )
733  {
734  SCIP_ROW* row;
735  int rowidx;
736  int rowpos;
737  int direction;
738 
739  rowidx = -1;
740  rowpos = -1;
741  row = NULL;
742  if( nfrac > 0 )
743  {
744  for( rowidx = nviolrows-1; rowidx >= 0; --rowidx )
745  {
746  row = violrows[rowidx];
747  rowpos = SCIProwGetLPPos(row);
748  assert(violrowpos[rowpos] == rowidx);
749  if( nfracsinrow[rowpos] > 0 )
750  break;
751  }
752  }
753  if( rowidx == -1 )
754  {
755  rowidx = SCIPrandomGetInt(heurdata->randnumgen, 0, nviolrows-1);
756  row = violrows[rowidx];
757  rowpos = SCIProwGetLPPos(row);
758  assert(0 <= rowpos && rowpos < nlprows);
759  assert(violrowpos[rowpos] == rowidx);
760  assert(nfracsinrow[rowpos] == 0);
761  }
762  assert(violrowpos[rowpos] == rowidx);
763 
764  SCIPdebugMessage("GCG shifting heuristic: try to fix violated row <%s>: %g <= %g <= %g\n",
765  SCIProwGetName(row), SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row));
766  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
767 
768  /* get direction in which activity must be shifted */
769  assert(SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row))
770  || SCIPisFeasGT(scip, activities[rowpos], SCIProwGetRhs(row)));
771  direction = SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) ? +1 : -1;
772 
773  /* search a variable that can shift the activity in the necessary direction */
774  SCIP_CALL( selectShifting(scip, sol, row, activities[rowpos], direction,
775  nincreases, ndecreases, increaseweight, &shiftvar, &oldsolval, &newsolval) );
776  }
777 
778  if( shiftvar == NULL && nfrac > 0 )
779  {
780  SCIPdebugMessage("GCG shifting heuristic: search rounding variable and try to stay feasible\n");
781  SCIP_CALL( selectEssentialRounding(scip, sol, minobj, lpcands, nlpcands, &shiftvar, &oldsolval, &newsolval) );
782  }
783 
784  /* check, whether shifting was possible */
785  if( shiftvar == NULL || SCIPisEQ(scip, oldsolval, newsolval) )
786  {
787  SCIPdebugMessage("GCG shifting heuristic: -> didn't find a shifting variable\n");
788  break;
789  }
790 
791  SCIPdebugMessage("GCG shifting heuristic: -> shift var <%s>[%g,%g], type=%d, oldval=%g, newval=%g, obj=%g\n",
792  SCIPvarGetName(shiftvar), SCIPvarGetLbGlobal(shiftvar), SCIPvarGetUbGlobal(shiftvar), SCIPvarGetType(shiftvar),
793  oldsolval, newsolval, SCIPvarGetObj(shiftvar));
794 
795  /* update row activities of globally valid rows */
796  SCIP_CALL( updateActivities(scip, activities, violrows, violrowpos, &nviolrows, nlprows,
797  shiftvar, oldsolval, newsolval) );
798  if( nviolrows >= nprevviolrows )
799  nnonimprovingshifts++;
800  else if( nviolrows < minnviolrows )
801  {
802  minnviolrows = nviolrows;
803  nnonimprovingshifts = 0;
804  }
805 
806  /* store new solution value and decrease fractionality counter */
807  SCIP_CALL( SCIPsetSolVal(scip, sol, shiftvar, newsolval) );
808 
809  /* update fractionality counter and minimal objective value possible after shifting remaining variables */
810  oldsolvalisfrac = !SCIPisFeasIntegral(scip, oldsolval)
811  && (SCIPvarGetType(shiftvar) == SCIP_VARTYPE_BINARY || SCIPvarGetType(shiftvar) == SCIP_VARTYPE_INTEGER);
812  obj = SCIPvarGetObj(shiftvar);
813  if( (SCIPvarGetType(shiftvar) == SCIP_VARTYPE_BINARY || SCIPvarGetType(shiftvar) == SCIP_VARTYPE_INTEGER )
814  && oldsolvalisfrac )
815  {
816  assert(SCIPisFeasIntegral(scip, newsolval));
817  nfrac--;
818  nnonimprovingshifts = 0;
819  minnviolrows = INT_MAX;
820  addFracCounter(nfracsinrow, nlprows, shiftvar, -1);
821 
822  /* the rounding was already calculated into the minobj -> update only if rounding in "wrong" direction */
823  if( obj > 0.0 && newsolval > oldsolval )
824  minobj += obj;
825  else if( obj < 0.0 && newsolval < oldsolval )
826  minobj -= obj;
827  }
828  else
829  {
830  /* update minimal possible objective value */
831  minobj += obj * (newsolval - oldsolval);
832  }
833 
834  /* update increase/decrease arrays */
835  if( !oldsolvalisfrac )
836  {
837  int probindex;
838 
839  probindex = SCIPvarGetProbindex(shiftvar);
840  assert(0 <= probindex && probindex < nvars);
841  increaseweight *= WEIGHTFACTOR;
842  if( newsolval < oldsolval )
843  ndecreases[probindex] += increaseweight;
844  else
845  nincreases[probindex] += increaseweight;
846  if( increaseweight >= 1e+09 )
847  {
848  int i;
849 
850  for( i = 0; i < nvars; ++i )
851  {
852  nincreases[i] /= increaseweight;
853  ndecreases[i] /= increaseweight;
854  }
855  increaseweight = 1.0;
856  }
857  }
858 
859  SCIPdebugMessage("gcg shifting heuristic: -> nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n",
860  nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj));
861  }
862 
863  /* check, if the new solution is feasible */
864  if( nfrac == 0 && nviolrows == 0 )
865  {
866  SCIP_Bool stored;
867 
868  /* check solution for feasibility, and add it to solution store if possible
869  * neither integrality nor feasibility of LP rows has to be checked, because this is already
870  * done in the shifting heuristic itself; however, we better check feasibility of LP rows,
871  * because of numerical problems with activity updating
872  */
873  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, FALSE, TRUE, &stored) );
874 
875  if( stored )
876  {
877  SCIPdebugMessage("found feasible shifted solution:\n");
878  SCIPdebug(SCIPprintSol(scip, sol, NULL, FALSE));
879  *result = SCIP_FOUNDSOL;
880  }
881  }
882 
883  /* free memory buffers */
884  SCIPfreeBufferArray(scip, &ndecreases);
885  SCIPfreeBufferArray(scip, &nincreases);
886  SCIPfreeBufferArray(scip, &nfracsinrow);
887  SCIPfreeBufferArray(scip, &violrowpos);
888  SCIPfreeBufferArray(scip, &violrows);
889  SCIPfreeBufferArray(scip, &activities);
890 
891  return SCIP_OKAY;
892 }
893 
894 
895 
896 
897 /*
898  * heuristic specific interface methods
899  */
900 
901 /** creates the GCG shifting heuristic with infeasibility recovering and includes it in SCIP */
903  SCIP* scip /**< SCIP data structure */
904  )
905 {
906  /* include heuristic */
907  SCIP_CALL( SCIPincludeHeur(scip, HEUR_NAME, HEUR_DESC, HEUR_DISPCHAR, HEUR_PRIORITY, HEUR_FREQ, HEUR_FREQOFS,
909  heurCopyGcgshifting, heurFreeGcgshifting, heurInitGcgshifting, heurExitGcgshifting,
910  heurInitsolGcgshifting, heurExitsolGcgshifting, heurExecGcgshifting,
911  NULL) );
912 
913  return SCIP_OKAY;
914 }
915 
SCIP_RETCODE SCIPincludeHeurGcgshifting(SCIP *scip)
static void addFracCounter(int *nfracsinrow, int nlprows, SCIP_VAR *var, int incval)
GCG interface methods.
#define HEUR_MAXDEPTH
#define DEFAULT_RANDSEED
SCIP_Longint lastlp
#define heurFreeGcgshifting
SCIP_SOL * sol
#define HEUR_NAME
#define heurCopyGcgshifting
#define HEUR_FREQ
SCIP_RANDNUMGEN * randnumgen
static SCIP_DECL_HEUREXIT(heurExitGcgshifting)
static SCIP_DECL_HEURINIT(heurInitGcgshifting)
SCIP * GCGgetMasterprob(SCIP *scip)
Definition: relax_gcg.c:3920
static SCIP_RETCODE selectEssentialRounding(SCIP *scip, SCIP_SOL *sol, SCIP_Real minobj, SCIP_VAR **lpcands, int nlpcands, SCIP_VAR **shiftvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
#define HEUR_TIMING
#define heurExitsolGcgshifting
SCIP_SOL * GCGrelaxGetCurrentOrigSol(SCIP *scip)
Definition: relax_gcg.c:4183
#define MAXSHIFTINGS
#define HEUR_FREQOFS
static SCIP_RETCODE selectShifting(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *row, SCIP_Real rowactivity, int direction, SCIP_Real *nincreases, SCIP_Real *ndecreases, SCIP_Real increaseweight, SCIP_VAR **shiftvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
GCG relaxator.
#define HEUR_USESSUBSCIP
#define HEUR_DESC
#define HEUR_PRIORITY
LP gcgrounding heuristic that tries to recover from intermediate infeasibilities and shifts continuou...
static SCIP_DECL_HEUREXEC(heurExecGcgshifting)
static SCIP_DECL_HEURINITSOL(heurInitsolGcgshifting)
static void updateViolations(SCIP *scip, SCIP_ROW *row, SCIP_ROW **violrows, int *violrowpos, int *nviolrows, SCIP_Real oldactivity, SCIP_Real newactivity)
#define WEIGHTFACTOR
#define HEUR_DISPCHAR
static SCIP_RETCODE updateActivities(SCIP *scip, SCIP_Real *activities, SCIP_ROW **violrows, int *violrowpos, int *nviolrows, int nlprows, SCIP_VAR *var, SCIP_Real oldsolval, SCIP_Real newsolval)