Scippy

GCG

Branch-and-Price & Column Generation for Everyone

heur_gcgrounding.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_gcgrounding.c
29  * @brief LP gcgrounding heuristic that tries to recover from intermediate infeasibilities
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_gcgrounding.h"
40 #include "relax_gcg.h"
41 #include "gcg.h"
42 
43 
44 #define HEUR_NAME "gcgrounding"
45 #define HEUR_DESC "LP rounding heuristic on original variables with infeasibility recovering"
46 #define HEUR_DISPCHAR 'R'
47 #define HEUR_PRIORITY -1000
48 #define HEUR_FREQ 1
49 #define HEUR_FREQOFS 0
50 #define HEUR_MAXDEPTH -1
51 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
52 #define HEUR_USESSUBSCIP FALSE
53 
54 #define DEFAULT_SUCCESSFACTOR 100 /**< number of calls per found solution that are considered as standard success,
55  * a higher factor causes the heuristic to be called more often
56  */
57 
58 
59 /* locally defined heuristic data */
60 struct SCIP_HeurData
61 {
62  SCIP_SOL* sol; /**< working solution */
63  SCIP_Longint lastlp; /**< last LP number where the heuristic was applied */
64  int successfactor; /**< number of calls per found solution that are considered as standard success,
65  * a higher factor causes the heuristic to be called more often
66  */
67 };
68 
69 
70 
71 
72 /*
73  * local methods
74  */
75 
76 /** update row violation arrays after a row's activity value changed */
77 static
79  SCIP* scip, /**< SCIP data structure */
80  SCIP_ROW* row, /**< LP row */
81  SCIP_ROW** violrows, /**< array with currently violated rows */
82  int* violrowpos, /**< position of LP rows in violrows array */
83  int* nviolrows, /**< pointer to the number of currently violated rows */
84  SCIP_Real oldactivity, /**< old activity value of LP row */
85  SCIP_Real newactivity /**< new activity value of LP row */
86  )
87 {
88  SCIP_Real lhs;
89  SCIP_Real rhs;
90  SCIP_Bool oldviol;
91  SCIP_Bool newviol;
92 
93  assert(violrows != NULL);
94  assert(violrowpos != NULL);
95  assert(nviolrows != NULL);
96 
97  lhs = SCIProwGetLhs(row);
98  rhs = SCIProwGetRhs(row);
99  oldviol = (SCIPisFeasLT(scip, oldactivity, lhs) || SCIPisFeasGT(scip, oldactivity, rhs));
100  newviol = (SCIPisFeasLT(scip, newactivity, lhs) || SCIPisFeasGT(scip, newactivity, rhs));
101  if( oldviol != newviol )
102  {
103  int rowpos;
104 
105  rowpos = SCIProwGetLPPos(row);
106  assert(rowpos >= 0);
107 
108  if( oldviol )
109  {
110  int violpos;
111 
112  /* the row violation was repaired: remove row from violrows array, decrease violation count */
113  violpos = violrowpos[rowpos];
114  assert(0 <= violpos && violpos < *nviolrows);
115  assert(violrows[violpos] == row);
116  violrowpos[rowpos] = -1;
117  if( violpos != *nviolrows-1 )
118  {
119  violrows[violpos] = violrows[*nviolrows-1];
120  violrowpos[SCIProwGetLPPos(violrows[violpos])] = violpos;
121  }
122  (*nviolrows)--;
123  }
124  else
125  {
126  /* the row is now violated: add row to violrows array, increase violation count */
127  assert(violrowpos[rowpos] == -1);
128  violrows[*nviolrows] = row;
129  violrowpos[rowpos] = *nviolrows;
130  (*nviolrows)++;
131  }
132  }
133 }
134 
135 /** update row activities after a variable's solution value changed */
136 static
137 SCIP_RETCODE updateActivities(
138  SCIP* scip, /**< SCIP data structure */
139  SCIP_Real* activities, /**< LP row activities */
140  SCIP_ROW** violrows, /**< array with currently violated rows */
141  int* violrowpos, /**< position of LP rows in violrows array */
142  int* nviolrows, /**< pointer to the number of currently violated rows */
143  int nlprows, /**< number of rows in current LP */
144  SCIP_VAR* var, /**< variable that has been changed */
145  SCIP_Real oldsolval, /**< old solution value of variable */
146  SCIP_Real newsolval /**< new solution value of variable */
147  )
148 {
149  SCIP_COL* col;
150  SCIP_ROW** colrows;
151  SCIP_Real* colvals;
152  SCIP_Real delta;
153  int ncolrows;
154  int r;
155 
156  assert(activities != NULL);
157  assert(nviolrows != NULL);
158  assert(0 <= *nviolrows && *nviolrows <= nlprows);
159 
160  delta = newsolval - oldsolval;
161  col = SCIPvarGetCol(var);
162  colrows = SCIPcolGetRows(col);
163  colvals = SCIPcolGetVals(col);
164  ncolrows = SCIPcolGetNLPNonz(col);
165  assert(ncolrows == 0 || (colrows != NULL && colvals != NULL));
166 
167  for( r = 0; r < ncolrows; ++r )
168  {
169  SCIP_ROW* row;
170  int rowpos;
171 
172  row = colrows[r];
173  rowpos = SCIProwGetLPPos(row);
174  assert(-1 <= rowpos && rowpos < nlprows);
175 
176  if( rowpos >= 0 && !SCIProwIsLocal(row) )
177  {
178  SCIP_Real oldactivity;
179 
180  assert(SCIProwIsInLP(row));
181 
182  /* update row activity */
183  oldactivity = activities[rowpos];
184  if( !SCIPisInfinity(scip, -oldactivity) && !SCIPisInfinity(scip, oldactivity) )
185  {
186  SCIP_Real newactivity;
187 
188  newactivity = oldactivity + delta * colvals[r];
189  if( SCIPisInfinity(scip, newactivity) )
190  newactivity = SCIPinfinity(scip);
191  else if( SCIPisInfinity(scip, -newactivity) )
192  newactivity = -SCIPinfinity(scip);
193  activities[rowpos] = newactivity;
194 
195  /* update row violation arrays */
196  updateViolations(scip, row, violrows, violrowpos, nviolrows, oldactivity, newactivity);
197  }
198  }
199  }
200 
201  return SCIP_OKAY;
202 }
203 
204 /** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows;
205  * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
206  * rounding in a direction is forbidden, if this forces the objective value over the upper bound
207  */
208 static
209 SCIP_RETCODE selectRounding(
210  SCIP* scip, /**< SCIP data structure */
211  SCIP_SOL* sol, /**< primal solution */
212  SCIP_Real minobj, /**< minimal objective value possible after rounding remaining fractional vars */
213  SCIP_ROW* row, /**< LP row */
214  int direction, /**< should the activity be increased (+1) or decreased (-1)? */
215  SCIP_VAR** roundvar, /**< pointer to store the rounding variable, returns NULL if impossible */
216  SCIP_Real* oldsolval, /**< pointer to store old (fractional) solution value of rounding variable */
217  SCIP_Real* newsolval /**< pointer to store new (rounded) solution value of rounding variable */
218  )
219 {
220  SCIP_Real bestdeltaobj;
221  SCIP_COL** rowcols;
222  SCIP_Real* rowvals;
223  int nrowcols;
224  int minnlocks;
225  int c;
226 
227  assert(direction == +1 || direction == -1);
228  assert(roundvar != NULL);
229  assert(oldsolval != NULL);
230  assert(newsolval != NULL);
231 
232  /* get row entries */
233  rowcols = SCIProwGetCols(row);
234  rowvals = SCIProwGetVals(row);
235  nrowcols = SCIProwGetNLPNonz(row);
236 
237  /* select rounding variable */
238  minnlocks = INT_MAX;
239  bestdeltaobj = SCIPinfinity(scip);
240  *roundvar = NULL;
241  for( c = 0; c < nrowcols; ++c )
242  {
243  SCIP_COL* col;
244  SCIP_VAR* var;
245  SCIP_VARTYPE vartype;
246 
247  col = rowcols[c];
248  var = SCIPcolGetVar(col);
249 
250  vartype = SCIPvarGetType(var);
251  if( vartype == SCIP_VARTYPE_BINARY || vartype == SCIP_VARTYPE_INTEGER )
252  {
253  SCIP_Real solval;
254 
255  solval = SCIPgetSolVal(scip, sol, var);
256 
257  if( !SCIPisFeasIntegral(scip, solval) )
258  {
259  SCIP_Real val;
260  SCIP_Real roundval;
261  SCIP_Real obj;
262  SCIP_Real deltaobj;
263  int nlocks;
264 
265  val = rowvals[c];
266  obj = SCIPvarGetObj(var);
267 
268  if( direction * val < 0.0 )
269  {
270  /* rounding down */
271  nlocks = SCIPvarGetNLocksDown(var);
272  if( nlocks <= minnlocks )
273  {
274  roundval = SCIPfeasFloor(scip, solval);
275  deltaobj = obj * (roundval - solval);
276  if( (nlocks < minnlocks || deltaobj < bestdeltaobj) && minobj - obj < SCIPgetCutoffbound(scip) )
277  {
278  minnlocks = nlocks;
279  bestdeltaobj = deltaobj;
280  *roundvar = var;
281  *oldsolval = solval;
282  *newsolval = roundval;
283  }
284  }
285  }
286  else
287  {
288  /* rounding up */
289  assert(direction * val > 0.0);
290  nlocks = SCIPvarGetNLocksUp(var);
291  if( nlocks <= minnlocks )
292  {
293  roundval = SCIPfeasCeil(scip, solval);
294  deltaobj = obj * (roundval - solval);
295  if( (nlocks < minnlocks || deltaobj < bestdeltaobj) && minobj + obj < SCIPgetCutoffbound(scip) )
296  {
297  minnlocks = nlocks;
298  bestdeltaobj = deltaobj;
299  *roundvar = var;
300  *oldsolval = solval;
301  *newsolval = roundval;
302  }
303  }
304  }
305  }
306  }
307  }
308 
309  return SCIP_OKAY;
310 }
311 
312 /** returns a variable, that increases the activity of the row */
313 static
315  SCIP* scip, /**< SCIP data structure */
316  SCIP_SOL* sol, /**< primal solution */
317  SCIP_Real minobj, /**< minimal objective value possible after rounding remaining fractional vars */
318  SCIP_ROW* row, /**< LP row */
319  SCIP_VAR** roundvar, /**< pointer to store the rounding variable, returns NULL if impossible */
320  SCIP_Real* oldsolval, /**< old (fractional) solution value of rounding variable */
321  SCIP_Real* newsolval /**< new (rounded) solution value of rounding variable */
322  )
323 {
324  return selectRounding(scip, sol, minobj, row, +1, roundvar, oldsolval, newsolval);
325 }
326 
327 /** returns a variable, that decreases the activity of the row */
328 static
330  SCIP* scip, /**< SCIP data structure */
331  SCIP_SOL* sol, /**< primal solution */
332  SCIP_Real minobj, /**< minimal objective value possible after rounding remaining fractional vars */
333  SCIP_ROW* row, /**< LP row */
334  SCIP_VAR** roundvar, /**< pointer to store the rounding variable, returns NULL if impossible */
335  SCIP_Real* oldsolval, /**< old (fractional) solution value of rounding variable */
336  SCIP_Real* newsolval /**< new (rounded) solution value of rounding variable */
337  )
338 {
339  return selectRounding(scip, sol, minobj, row, -1, roundvar, oldsolval, newsolval);
340 }
341 
342 /** returns a fractional variable, that has most impact on rows in opposite direction, i.e. that is most crucial to
343  * fix in the other direction;
344  * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
345  * rounding in a direction is forbidden, if this forces the objective value over the upper bound
346  */
347 static
349  SCIP* scip, /**< SCIP data structure */
350  SCIP_SOL* sol, /**< primal solution */
351  SCIP_Real minobj, /**< minimal objective value possible after rounding remaining fractional vars */
352  SCIP_VAR** lpcands, /**< fractional variables in LP */
353  int nlpcands, /**< number of fractional variables in LP */
354  SCIP_VAR** roundvar, /**< pointer to store the rounding variable, returns NULL if impossible */
355  SCIP_Real* oldsolval, /**< old (fractional) solution value of rounding variable */
356  SCIP_Real* newsolval /**< new (rounded) solution value of rounding variable */
357  )
358 {
359  SCIP_Real bestdeltaobj;
360  int maxnlocks;
361  int v;
362 
363  assert(roundvar != NULL);
364  assert(oldsolval != NULL);
365  assert(newsolval != NULL);
366 
367  /* select rounding variable */
368  maxnlocks = -1;
369  bestdeltaobj = SCIPinfinity(scip);
370  *roundvar = NULL;
371  for( v = 0; v < nlpcands; ++v )
372  {
373  SCIP_VAR* var;
374  SCIP_Real solval;
375 
376  var = lpcands[v];
377  assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER);
378 
379  solval = SCIPgetSolVal(scip, sol, var);
380  if( !SCIPisFeasIntegral(scip, solval) )
381  {
382  SCIP_Real roundval;
383  SCIP_Real obj;
384  SCIP_Real deltaobj;
385  int nlocks;
386 
387  obj = SCIPvarGetObj(var);
388 
389  /* rounding down */
390  nlocks = SCIPvarGetNLocksUp(var);
391  if( nlocks >= maxnlocks )
392  {
393  roundval = SCIPfeasFloor(scip, solval);
394  deltaobj = obj * (roundval - solval);
395  if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj - obj < SCIPgetCutoffbound(scip) )
396  {
397  maxnlocks = nlocks;
398  bestdeltaobj = deltaobj;
399  *roundvar = var;
400  *oldsolval = solval;
401  *newsolval = roundval;
402  }
403  }
404 
405  /* rounding up */
406  nlocks = SCIPvarGetNLocksDown(var);
407  if( nlocks >= maxnlocks )
408  {
409  roundval = SCIPfeasCeil(scip, solval);
410  deltaobj = obj * (roundval - solval);
411  if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj + obj < SCIPgetCutoffbound(scip) )
412  {
413  maxnlocks = nlocks;
414  bestdeltaobj = deltaobj;
415  *roundvar = var;
416  *oldsolval = solval;
417  *newsolval = roundval;
418  }
419  }
420  }
421  }
422 
423  return SCIP_OKAY;
424 }
425 
426 
427 
428 
429 /*
430  * Callback methods
431  */
432 
433 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
434 #define heurCopyGcgrounding NULL
435 
436 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
437 static
438 SCIP_DECL_HEURFREE(heurFreeGcgrounding) /*lint --e{715}*/
439 { /*lint --e{715}*/
440  SCIP_HEURDATA* heurdata;
441 
442  assert(heur != NULL);
443  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
444  assert(scip != NULL);
445 
446  /* free heuristic data */
447  heurdata = SCIPheurGetData(heur);
448  assert(heurdata != NULL);
449  SCIPfreeMemory(scip, &heurdata);
450  SCIPheurSetData(heur, NULL);
451 
452  return SCIP_OKAY;
453 }
454 
455 
456 
457 /** initialization method of primal heuristic (called after problem was transformed) */
458 static
459 SCIP_DECL_HEURINIT(heurInitGcgrounding) /*lint --e{715}*/
460 { /*lint --e{715}*/
461  SCIP_HEURDATA* heurdata;
462 
463  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
464 
465  heurdata = SCIPheurGetData(heur);
466  assert(heurdata != NULL);
467 
468  /* create heuristic data */
469  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
470  heurdata->lastlp = -1;
471 
472  return SCIP_OKAY;
473 }
474 
475 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
476 static
477 SCIP_DECL_HEUREXIT(heurExitGcgrounding) /*lint --e{715}*/
478 { /*lint --e{715}*/
479  SCIP_HEURDATA* heurdata;
480 
481  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
482 
483  /* free heuristic data */
484  heurdata = SCIPheurGetData(heur);
485  assert(heurdata != NULL);
486  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
487 
488  return SCIP_OKAY;
489 }
490 
491 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
492 static
493 SCIP_DECL_HEURINITSOL(heurInitsolGcgrounding)
494 {
495  SCIP_HEURDATA* heurdata;
496 
497  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
498 
499  heurdata = SCIPheurGetData(heur);
500  assert(heurdata != NULL);
501  heurdata->lastlp = -1;
502 
503  return SCIP_OKAY;
504 }
505 
506 
507 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
508 #define heurExitsolGcgrounding NULL
509 
510 
511 /** execution method of primal heuristic */
512 static
513 SCIP_DECL_HEUREXEC(heurExecGcgrounding) /*lint --e{715}*/
514 { /*lint --e{715}*/
515  SCIP* masterprob;
516  SCIP_HEURDATA* heurdata;
517  SCIP_SOL* sol;
518  SCIP_VAR** lpcands;
519  SCIP_Real* lpcandssol;
520  SCIP_ROW** lprows;
521  SCIP_Real* activities;
522  SCIP_ROW** violrows;
523  int* violrowpos;
524  SCIP_Real obj;
525  SCIP_Real minobj;
526  int nlpcands;
527  int nlprows;
528  int nfrac;
529  int nviolrows;
530  int c;
531  int r;
532  SCIP_Longint nlps;
533  SCIP_Longint ncalls;
534  SCIP_Longint nsolsfound;
535  SCIP_Longint nnodes;
536 
537  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
538  assert(scip != NULL);
539  assert(result != NULL);
540 
541  /* get master problem */
542  masterprob = GCGgetMasterprob(scip);
543  assert(masterprob != NULL);
544 
545  *result = SCIP_DIDNOTRUN;
546 
547  /* do not execute the heuristic on invalid relaxation solutions
548  * (which is the case if the node has been cut off)
549  */
550  if( !SCIPisRelaxSolValid(scip) )
551  {
552  SCIPdebugMessage("skipping GCG rounding: invalid relaxation solution\n");
553  return SCIP_OKAY;
554  }
555 
556  /* only call heuristic, if an optimal LP solution is at hand */
557  if( SCIPgetStage(masterprob) > SCIP_STAGE_SOLVING || SCIPgetLPSolstat(masterprob) != SCIP_LPSOLSTAT_OPTIMAL )
558  return SCIP_OKAY;
559 
560  /* get heuristic data */
561  heurdata = SCIPheurGetData(heur);
562  assert(heurdata != NULL);
563 
564  /* don't call heuristic, if we have already processed the current LP solution */
565  nlps = SCIPgetNLPs(masterprob);
566  if( nlps == heurdata->lastlp )
567  return SCIP_OKAY;
568  heurdata->lastlp = nlps;
569 
570  /* don't call heuristic, if it was not successful enough in the past */
571  ncalls = SCIPheurGetNCalls(heur);
572  nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + SCIPheurGetNSolsFound(heur);
573  nnodes = SCIPgetNNodes(scip);
574  if( nnodes % ((ncalls/heurdata->successfactor)/(nsolsfound+1)+1) != 0 )
575  return SCIP_OKAY;
576 
577  /* get fractional variables, that should be integral */
578  SCIP_CALL( SCIPgetExternBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL, NULL, NULL) );
579  nfrac = nlpcands;
580 
581  /* only call heuristic, if LP solution is fractional */
582  if( nfrac == 0 )
583  return SCIP_OKAY;
584 
585  *result = SCIP_DIDNOTFIND;
586 
587  /* get LP rows */
588  SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) );
589 
590  SCIPdebugMessage("executing GCG rounding heuristic: %d LP rows, %d fractionals\n", nlprows, nfrac);
591 
592  /* get memory for activities, violated rows, and row violation positions */
593  SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) );
594  SCIP_CALL( SCIPallocBufferArray(scip, &violrows, nlprows) );
595  SCIP_CALL( SCIPallocBufferArray(scip, &violrowpos, nlprows) );
596 
597  /* get the activities for all globally valid rows;
598  * the rows should be feasible, but due to numerical inaccuracies in the LP solver, they can be violated
599  */
600  nviolrows = 0;
601  for( r = 0; r < nlprows; ++r )
602  {
603  SCIP_ROW* row;
604 
605  row = lprows[r];
606  assert(SCIProwGetLPPos(row) == r);
607 
608  if( !SCIProwIsLocal(row) )
609  {
610  activities[r] = SCIPgetRowSolActivity(scip, row, GCGrelaxGetCurrentOrigSol(scip));
611  if( SCIPisFeasLT(scip, activities[r], SCIProwGetLhs(row) )
612  || SCIPisFeasGT(scip, activities[r], SCIProwGetRhs(row)) )
613  {
614  violrows[nviolrows] = row;
615  violrowpos[r] = nviolrows;
616  nviolrows++;
617  }
618  else
619  violrowpos[r] = -1;
620  }
621  }
622 
623  /* get the working solution from heuristic's local data */
624  sol = heurdata->sol;
625  assert(sol != NULL);
626 
627  /* copy the current LP solution to the working solution */
628  SCIP_CALL( SCIPlinkRelaxSol(scip, sol) );
629 
630  /* calculate the minimal objective value possible after rounding fractional variables */
631  minobj = SCIPgetSolTransObj(scip, sol);
632  /* since the heuristic timing was changed to AFTERNODE, it might happen that it is called on a
633  * node with has been cut off; in that case, delay the heuristic
634  */
635  if( minobj >= SCIPgetCutoffbound(scip) )
636  {
637  *result = SCIP_DELAYED;
638  SCIPfreeBufferArray(scip, &violrowpos);
639  SCIPfreeBufferArray(scip, &violrows);
640  SCIPfreeBufferArray(scip, &activities);
641  return SCIP_OKAY;
642  }
643  for( c = 0; c < nlpcands; ++c )
644  {
645  SCIP_Real bestroundval;
646 
647  obj = SCIPvarGetObj(lpcands[c]);
648  bestroundval = obj > 0.0 ? SCIPfeasFloor(scip, lpcandssol[c]) : SCIPfeasCeil(scip, lpcandssol[c]);
649  minobj += obj * (bestroundval - lpcandssol[c]);
650  }
651 
652  /* try to round remaining variables in order to become/stay feasible */
653  while( nfrac > 0 )
654  {
655  SCIP_VAR* roundvar;
656  SCIP_Real oldsolval;
657  SCIP_Real newsolval;
658 
659  SCIPdebugMessage("GCG rounding heuristic: nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n",
660  nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj));
661 
662  /* minobj < SCIPgetCutoffbound(scip) should be true, otherwise the rounding variable selection
663  * should have returned NULL. Due to possible cancellation we use SCIPisLE. */
664  assert( SCIPisLE(scip, minobj, SCIPgetCutoffbound(scip)) );
665 
666  /* choose next variable to process:
667  * - if a violated row exists, round a variable decreasing the violation, that has least impact on other rows
668  * - otherwise, round a variable, that has strongest devastating impact on rows in opposite direction
669  */
670  if( nviolrows > 0 )
671  {
672  SCIP_ROW* row;
673  int rowpos;
674 
675  row = violrows[nviolrows-1];
676  rowpos = SCIProwGetLPPos(row);
677  assert(0 <= rowpos && rowpos < nlprows);
678  assert(violrowpos[rowpos] == nviolrows-1);
679 
680  SCIPdebugMessage("GCG rounding heuristic: try to fix violated row <%s>: %g <= %g <= %g\n",
681  SCIProwGetName(row), SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row));
682  if( SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) )
683  {
684  /* lhs is violated: select a variable rounding, that increases the activity */
685  SCIP_CALL( selectIncreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) );
686  }
687  else
688  {
689  assert(SCIPisFeasGT(scip, activities[rowpos], SCIProwGetRhs(row)));
690  /* rhs is violated: select a variable rounding, that decreases the activity */
691  SCIP_CALL( selectDecreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) );
692  }
693  }
694  else
695  {
696  SCIPdebugMessage("GCG rounding heuristic: search rounding variable and try to stay feasible\n");
697  SCIP_CALL( selectEssentialRounding(scip, sol, minobj, lpcands, nlpcands, &roundvar, &oldsolval, &newsolval) );
698  }
699 
700  /* check, whether rounding was possible */
701  if( roundvar == NULL )
702  {
703  SCIPdebugMessage("GCG rounding heuristic: -> didn't find a rounding variable\n");
704  break;
705  }
706 
707  SCIPdebugMessage("GCG rounding heuristic: -> round var <%s>, oldval=%g, newval=%g, obj=%g\n",
708  SCIPvarGetName(roundvar), oldsolval, newsolval, SCIPvarGetObj(roundvar));
709 
710  /* update row activities of globally valid rows */
711  SCIP_CALL( updateActivities(scip, activities, violrows, violrowpos, &nviolrows, nlprows,
712  roundvar, oldsolval, newsolval) );
713 
714  /* store new solution value and decrease fractionality counter */
715  SCIP_CALL( SCIPsetSolVal(scip, sol, roundvar, newsolval) );
716  nfrac--;
717 
718  /* update minimal objective value possible after rounding remaining variables */
719  obj = SCIPvarGetObj(roundvar);
720  if( obj > 0.0 && newsolval > oldsolval )
721  minobj += obj;
722  else if( obj < 0.0 && newsolval < oldsolval )
723  minobj -= obj;
724 
725  SCIPdebugMessage("GCG rounding heuristic: -> nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n",
726  nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj));
727  }
728 
729  /* check, if the new solution is feasible */
730  if( nfrac == 0 && nviolrows == 0 )
731  {
732  SCIP_Bool stored;
733 
734  /* check solution for feasibility, and add it to solution store if possible
735  * neither integrality nor feasibility of LP rows has to be checked, because this is already
736  * done in the rounding heuristic itself; however, be better check feasibility of LP rows,
737  * because of numerical problems with activity updating
738  */
739  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, FALSE, TRUE, &stored) );
740 
741  if( stored )
742  {
743 #ifdef SCIP_DEBUG
744  SCIPdebugMessage("found feasible rounded solution:\n");
745  SCIPprintSol(scip, sol, NULL, FALSE);
746 #endif
747  *result = SCIP_FOUNDSOL;
748  }
749  }
750 
751  /* free memory buffers */
752  SCIPfreeBufferArray(scip, &violrowpos);
753  SCIPfreeBufferArray(scip, &violrows);
754  SCIPfreeBufferArray(scip, &activities);
755 
756  return SCIP_OKAY;
757 }
758 
759 
760 
761 
762 /*
763  * heuristic specific interface methods
764  */
765 
766 /** creates the GCG rounding heuristic with infeasibility recovering and includes it in SCIP */
768  SCIP* scip /**< SCIP data structure */
769  )
770 {
771  SCIP_HEURDATA* heurdata;
772 
773  /* create heuristic data */
774  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
775 
776  /* include heuristic */
777  SCIP_CALL( SCIPincludeHeur(scip, HEUR_NAME, HEUR_DESC, HEUR_DISPCHAR, HEUR_PRIORITY, HEUR_FREQ, HEUR_FREQOFS,
780  heurFreeGcgrounding, heurInitGcgrounding, heurExitGcgrounding,
781  heurInitsolGcgrounding, heurExitsolGcgrounding, heurExecGcgrounding,
782  heurdata) );
783 
784  /* add rounding primal heuristic parameters */
785  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/successfactor",
786  "number of calls per found solution that are considered as standard success, a higher factor causes the heuristic to be called more often",
787  &heurdata->successfactor, TRUE, DEFAULT_SUCCESSFACTOR, -1, INT_MAX, NULL, NULL) );
788 
789  return SCIP_OKAY;
790 }
791 
static void updateViolations(SCIP *scip, SCIP_ROW *row, SCIP_ROW **violrows, int *violrowpos, int *nviolrows, SCIP_Real oldactivity, SCIP_Real newactivity)
#define HEUR_NAME
#define HEUR_TIMING
GCG interface methods.
#define HEUR_FREQOFS
static SCIP_DECL_HEURINITSOL(heurInitsolGcgrounding)
static SCIP_RETCODE selectRounding(SCIP *scip, SCIP_SOL *sol, SCIP_Real minobj, SCIP_ROW *row, int direction, SCIP_VAR **roundvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
SCIP_Longint lastlp
SCIP_SOL * sol
#define HEUR_USESSUBSCIP
#define HEUR_FREQ
static SCIP_RETCODE selectEssentialRounding(SCIP *scip, SCIP_SOL *sol, SCIP_Real minobj, SCIP_VAR **lpcands, int nlpcands, SCIP_VAR **roundvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
#define heurExitsolGcgrounding
SCIP_RETCODE SCIPincludeHeurGcgrounding(SCIP *scip)
static SCIP_DECL_HEUREXEC(heurExecGcgrounding)
SCIP * GCGgetMasterprob(SCIP *scip)
Definition: relax_gcg.c:3920
LP gcgrounding heuristic that tries to recover from intermediate infeasibilities.
static SCIP_RETCODE selectIncreaseRounding(SCIP *scip, SCIP_SOL *sol, SCIP_Real minobj, SCIP_ROW *row, SCIP_VAR **roundvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
#define HEUR_DESC
static SCIP_RETCODE selectDecreaseRounding(SCIP *scip, SCIP_SOL *sol, SCIP_Real minobj, SCIP_ROW *row, SCIP_VAR **roundvar, SCIP_Real *oldsolval, SCIP_Real *newsolval)
#define HEUR_MAXDEPTH
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)
#define HEUR_PRIORITY
SCIP_SOL * GCGrelaxGetCurrentOrigSol(SCIP *scip)
Definition: relax_gcg.c:4183
#define DEFAULT_SUCCESSFACTOR
#define HEUR_DISPCHAR
GCG relaxator.
static SCIP_DECL_HEURINIT(heurInitGcgrounding)
static SCIP_DECL_HEURFREE(heurFreeGcgrounding)
static SCIP_DECL_HEUREXIT(heurExitGcgrounding)
#define heurCopyGcgrounding