Scippy

SCIP

Solving Constraint Integer Programs

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