Scippy

GCG

Branch-and-Price & Column Generation for Everyone

heur_gcgzirounding.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_gcgzirounding.c
29  * @brief zirounding primal heuristic
30  * @author Gregor Hendel
31  * @author Christian Puchert
32  */
33 
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35 
36 #include <string.h>
37 
38 #include "heur_gcgzirounding.h"
39 #include "gcg.h"
40 #include "relax_gcg.h"
41 
42 #define HEUR_NAME "gcgzirounding"
43 #define HEUR_DESC "rounding heuristic on original variables as suggested by C. Wallace taking row slacks and bounds into account"
44 #define HEUR_DISPCHAR 'z'
45 #define HEUR_PRIORITY -500
46 /* #define HEUR_FREQ 1 */
47 #define HEUR_FREQ -1 /* / @todo heuristic deactivated due to false solutions */
48 #define HEUR_FREQOFS 0
49 #define HEUR_MAXDEPTH -1
50 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
51 #define HEUR_USESSUBSCIP FALSE
52 
53 #define DEFAULT_MAXROUNDINGLOOPS 2 /**< delimits the number of main loops, can be set to -1 for no limit */
54 #define DEFAULT_STOPZIROUND TRUE /**< deactivation check is enabled by default */
55 #define DEFAULT_STOPPERCENTAGE 0.02 /**< the tolerance percentage after which zirounding will not be executed anymore */
56 #define DEFAULT_MINSTOPNCALLS 1000 /**< number of heuristic calls before deactivation check */
57 
58 #define MINSHIFT 1e-4 /**< minimum shift value for every single step */
59 
60 /*
61  * Data structures
62  */
63 
64 /** primal heuristic data */
65 struct SCIP_HeurData
66 {
67  SCIP_SOL* sol; /**< working solution */
68  SCIP_Longint lastlp; /**< the number of the last LP for which ZIRounding was called */
69  int maxroundingloops; /**< limits rounding loops in execution */
70  SCIP_Bool stopziround; /**< sets deactivation check */
71  SCIP_Real stoppercentage; /**< threshold for deactivation check */
72  int minstopncalls; /**< number of heuristic calls before deactivation check */
73 };
74 
76 {
79 };
80 typedef enum Direction DIRECTION;
81 
82 /*
83  * Local methods
84  */
85 
86 /** returns the fractionality of a value x, which is calculated as zivalue(x) = min(x-floor(x), ceil(x)-x) */
87 static
88 SCIP_Real getZiValue(
89  SCIP* scip, /**< pointer to current SCIP data structure */
90  SCIP_Real val /**< the value for which the fractionality should be computed */
91  )
92 {
93  SCIP_Real upgap; /* the gap between val and ceil(val) */
94  SCIP_Real downgap; /* the gap between val and floor(val) */
95 
96  assert(scip != NULL);
97 
98  upgap = SCIPfeasCeil(scip, val) - val;
99  downgap = val - SCIPfeasFloor(scip, val);
100 
101  return MIN(upgap, downgap);
102 }
103 
104 /** determines shifting bounds for variable */
105 static
107  SCIP* scip, /**< pointer to current SCIP data structure */
108  SCIP_VAR* var, /**< the variable for which lb and ub have to be calculated */
109  SCIP_Real currentvalue, /**< the current value of var in the working solution */
110  SCIP_Real* upperbound, /**< pointer to store the calculated upper bound on the variable shift */
111  SCIP_Real* lowerbound, /**< pointer to store the calculated lower bound on the variable shift */
112  SCIP_Real* upslacks, /**< array that contains the slacks between row activities and the right hand sides of the rows */
113  SCIP_Real* downslacks, /**< array that contains lhs slacks */
114  int nslacks, /**< current number of slacks */
115  SCIP_Bool* numericalerror /**< flag to determine whether a numerical error occurred */
116  )
117 {
118  SCIP_COL* col;
119  SCIP_ROW** colrows;
120  SCIP_Real* colvals;
121  int ncolvals;
122  int i;
123 
124  assert(scip != NULL);
125  assert(var != NULL);
126  assert(upslacks != NULL);
127  assert(downslacks != NULL);
128  assert(upperbound != NULL);
129  assert(lowerbound != NULL);
130 
131  /* get the column associated to the variable, the nonzero rows and the nonzero coefficients */
132  col = SCIPvarGetCol(var);
133  colrows = SCIPcolGetRows(col);
134  colvals = SCIPcolGetVals(col);
135  ncolvals = SCIPcolGetNLPNonz(col);
136 
137  /* only proceed, when variable has nonzero coefficients */
138  if( ncolvals == 0 )
139  return;
140 
141  assert(colvals != NULL);
142  assert(colrows != NULL);
143 
144  /* initialize the bounds on the shift to be the gap of the current solution value to the bounds of the variable */
145  if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
146  *upperbound = SCIPinfinity(scip);
147  else
148  *upperbound = SCIPvarGetUbGlobal(var) - currentvalue;
149 
150  if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
151  *lowerbound = SCIPinfinity(scip);
152  else
153  *lowerbound = currentvalue - SCIPvarGetLbGlobal(var);
154 
155  /* go through every nonzero row coefficient corresponding to var to determine bounds for shifting
156  * in such a way that shifting maintains feasibility in every LP row.
157  * a lower or upper bound as it is calculated in zirounding always has to be >= 0.0.
158  * if one of these values is significantly < 0.0, this will cause the abort of execution of the heuristic so that
159  * infeasible solutions are avoided
160  */
161  for( i = 0; i < ncolvals && MAX(*lowerbound, *upperbound) >= MINSHIFT; ++i )
162  {
163  SCIP_ROW* row;
164  int rowpos;
165 
166  row = colrows[i];
167  rowpos = SCIProwGetLPPos(row);
168 
169  /* the row might currently not be in the LP, ignore it! */
170  if( rowpos == -1 )
171  continue;
172 
173  assert(0 <= rowpos && rowpos < nslacks);
174 
175  /* all bounds and slacks as they are calculated in zirounding always have to be greater equal zero.
176  * It might however be due to numerical issues, e.g. with scaling, that they are not. Better abort in this case.
177  */
178  if( SCIPisFeasLT(scip, *lowerbound, 0.0) || SCIPisFeasLT(scip, *upperbound, 0.0)
179  || SCIPisFeasLT(scip, upslacks[rowpos], 0.0) || SCIPisFeasLT(scip, downslacks[rowpos] , 0.0) )
180  {
181  *numericalerror = TRUE;
182  return;
183  }
184 
185  SCIPdebugMsg(scip, "colval: %15.8g, downslack: %15.8g, upslack: %5.2g, lb: %5.2g, ub: %5.2g\n", colvals[i], downslacks[rowpos], upslacks[rowpos],
186  *lowerbound, *upperbound);
187 
188  /* if coefficient > 0, rounding up might violate up slack and rounding down might violate down slack
189  * thus search for the minimum so that no constraint is violated; vice versa for coefficient < 0
190  */
191  if( colvals[i] > 0 )
192  {
193  if( !SCIPisInfinity(scip, upslacks[rowpos]) )
194  {
195  SCIP_Real upslack;
196  upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
197  *upperbound = MIN(*upperbound, upslack/colvals[i]);
198  }
199 
200  if( !SCIPisInfinity(scip, downslacks[rowpos]) )
201  {
202  SCIP_Real downslack;
203  downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
204  *lowerbound = MIN(*lowerbound, downslack/colvals[i]);
205  }
206  }
207  else
208  {
209  assert(colvals[i] != 0.0);
210 
211  if( !SCIPisInfinity(scip, upslacks[rowpos]) )
212  {
213  SCIP_Real upslack;
214  upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
215  *lowerbound = MIN(*lowerbound, -upslack/colvals[i]);
216  }
217 
218  if( !SCIPisInfinity(scip, downslacks[rowpos]) )
219  {
220  SCIP_Real downslack;
221  downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
222  *upperbound = MIN(*upperbound, -downslack/colvals[i]);
223  }
224  }
225  }
226 }
227 
228 /** when a variable is shifted, the activities and slacks of all rows it appears in have to be updated */
229 static
230 SCIP_RETCODE updateSlacks(
231  SCIP* scip, /**< pointer to current SCIP data structure */
232  SCIP_SOL* sol, /**< working solution */
233  SCIP_VAR* var, /**< pointer to variable to be modified */
234  SCIP_Real shiftvalue, /**< the value by which the variable is shifted */
235  SCIP_Real* upslacks, /**< upslacks of all rows the variable appears in */
236  SCIP_Real* downslacks, /**< downslacks of all rows the variable appears in */
237  SCIP_Real* activities, /**< activities of the LP rows */
238  SCIP_VAR** slackvars, /**< the slack variables for equality rows */
239  SCIP_Real* slackcoeffs, /**< the slack variable coefficients */
240  int nslacks /**< size of the arrays */
241  )
242 {
243  SCIP_COL* col; /* the corresponding column of variable var */
244  SCIP_ROW** rows; /* pointer to the nonzero coefficient rows for variable var */
245  int nrows; /* the number of nonzeros */
246  SCIP_Real* colvals; /* array to store the nonzero coefficients */
247  int i;
248 
249  assert(scip != NULL);
250  assert(sol != NULL);
251  assert(var != NULL);
252  assert(upslacks != NULL);
253  assert(downslacks != NULL);
254  assert(activities != NULL);
255  assert(nslacks >= 0);
256 
257  col = SCIPvarGetCol(var);
258  assert(col != NULL);
259 
260  rows = SCIPcolGetRows(col);
261  nrows = SCIPcolGetNLPNonz(col);
262  colvals = SCIPcolGetVals(col);
263  assert(nrows == 0 || (rows != NULL && colvals != NULL));
264 
265  /* go through all rows the shifted variable appears in */
266  for( i = 0; i < nrows; ++i )
267  {
268  int rowpos;
269 
270  rowpos = SCIProwGetLPPos(rows[i]);
271  assert(-1 <= rowpos && rowpos < nslacks);
272 
273  /* if the row is in the LP, update its activity, up and down slack */
274  if( rowpos >= 0 )
275  {
276  SCIP_Real val;
277 
278  val = colvals[i] * shiftvalue;
279 
280  /* if the row is an equation, we update its slack variable instead of its activities */
281  if( SCIPisFeasEQ(scip, SCIProwGetLhs(rows[i]), SCIProwGetRhs(rows[i])) )
282  {
283  SCIP_Real slackvarshiftval;
284  SCIP_Real slackvarsolval;
285 
286  assert(slackvars[rowpos] != NULL);
287  assert(!SCIPisZero(scip, slackcoeffs[rowpos]));
288 
289  slackvarsolval = SCIPgetSolVal(scip, sol, slackvars[rowpos]);
290  slackvarshiftval = -val / slackcoeffs[rowpos];
291 
292  assert(SCIPisFeasGE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetLbGlobal(slackvars[rowpos])));
293  assert(SCIPisFeasLE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetUbGlobal(slackvars[rowpos])));
294 
295  SCIP_CALL( SCIPsetSolVal(scip, sol, slackvars[rowpos], slackvarsolval + slackvarshiftval) );
296  }
297  else if( !SCIPisInfinity(scip, -activities[rowpos]) && !SCIPisInfinity(scip, activities[rowpos]) )
298  activities[rowpos] += val;
299 
300  /* the slacks of the row now can be updated independently of its type */
301  if( !SCIPisInfinity(scip, upslacks[rowpos]) )
302  upslacks[rowpos] -= val;
303  if( !SCIPisInfinity(scip, -downslacks[rowpos]) )
304  downslacks[rowpos] += val;
305 
306  assert(!SCIPisFeasNegative(scip, upslacks[rowpos]));
307  assert(!SCIPisFeasNegative(scip, downslacks[rowpos]));
308  }
309  }
310  return SCIP_OKAY;
311 }
312 
313 /** finds a continuous slack variable for an equation row, NULL if none exists */
314 static
316  SCIP* scip, /**< pointer to current SCIP data structure */
317  SCIP_ROW* row, /**< the row for which a slack variable is searched */
318  SCIP_VAR** varpointer, /**< pointer to store the slack variable */
319  SCIP_Real* coeffpointer /**< pointer to store the coefficient of the slack variable */
320  )
321 {
322  int v;
323  SCIP_COL** rowcols;
324  SCIP_Real* rowvals;
325  int nrowvals;
326 
327  assert(row != NULL);
328  assert(varpointer != NULL);
329  assert(coeffpointer != NULL);
330 
331  rowcols = SCIProwGetCols(row);
332  rowvals = SCIProwGetVals(row);
333  nrowvals = SCIProwGetNNonz(row);
334 
335  assert(nrowvals == 0 || rowvals != NULL);
336  assert(nrowvals == 0 || rowcols != NULL);
337 
338  /* iterate over the row variables. Stop after the first unfixed continuous variable was found. */
339  for( v = nrowvals - 1; v >= 0; --v )
340  {
341  SCIP_VAR* colvar;
342 
343  assert(rowcols[v] != NULL);
344  if( SCIPcolGetLPPos(rowcols[v]) == -1 )
345  continue;
346 
347  colvar = SCIPcolGetVar(rowcols[v]);
348 
349  if( SCIPvarGetType(colvar) == SCIP_VARTYPE_CONTINUOUS
350  && !SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(colvar), SCIPvarGetUbGlobal(colvar))
351  && SCIPcolGetNLPNonz(rowcols[v]) == 1 )
352  {
353  SCIPdebugMsg(scip, " slack variable for row %s found: %s\n", SCIProwGetName(row), SCIPvarGetName(colvar));
354 
355  *coeffpointer = rowvals[v];
356  *varpointer = colvar;
357 
358  return;
359  }
360  }
361 
362  *varpointer = NULL;
363  *coeffpointer = 0.0;
364 
365  SCIPdebugMsg(scip, "No slack variable for row %s found. \n", SCIProwGetName(row));
366 }
367 
368 /*
369  * Callback methods of primal heuristic
370  */
371 
372 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
373 static
374 SCIP_DECL_HEURFREE(heurFreeGcgzirounding)
375 { /*lint --e{715}*/
376  SCIP_HEURDATA* heurdata;
377 
378  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
379 
380  heurdata = SCIPheurGetData(heur);
381  assert(heurdata != NULL);
382 
383  /* free heuristic data */
384  SCIPfreeBlockMemory(scip, &heurdata);
385  SCIPheurSetData(heur, NULL);
386 
387  return SCIP_OKAY;
388 }
389 
390 /** initialization method of primal heuristic (called after problem was transformed) */
391 static
392 SCIP_DECL_HEURINIT(heurInitGcgzirounding)
393 { /*lint --e{715}*/
394  SCIP_HEURDATA* heurdata;
395 
396  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
397 
398  heurdata = SCIPheurGetData(heur);
399  assert(heurdata != NULL);
400 
401  /* create working solution */
402  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
403 
404  return SCIP_OKAY;
405 }
406 
407 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
408 static
409 SCIP_DECL_HEUREXIT(heurExitGcgzirounding) /*lint --e{715}*/
410 { /*lint --e{715}*/
411  SCIP_HEURDATA* heurdata;
412 
413  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
414 
415  heurdata = SCIPheurGetData(heur);
416  assert(heurdata != NULL);
417 
418  /* free working solution */
419  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
420 
421  return SCIP_OKAY;
422 }
423 
424 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
425 static
426 SCIP_DECL_HEURINITSOL(heurInitsolGcgzirounding)
427 { /*lint --e{715}*/
428  SCIP_HEURDATA* heurdata;
429 
430  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
431 
432  heurdata = SCIPheurGetData(heur);
433  assert(heurdata != NULL);
434 
435  heurdata->lastlp = -1;
436 
437  return SCIP_OKAY;
438 }
439 
440 /** execution method of primal heuristic */
441 static
442 SCIP_DECL_HEUREXEC(heurExecGcgzirounding)
443 { /*lint --e{715}*/
444  SCIP* masterprob;
445  SCIP_HEURDATA* heurdata;
446  SCIP_SOL* sol;
447  SCIP_VAR** lpcands;
448  SCIP_VAR** zilpcands;
449  SCIP_VAR** slackvars;
450  SCIP_ROW** rows;
451  SCIP_Real* upslacks;
452  SCIP_Real* downslacks;
453  SCIP_Real* activities;
454  SCIP_Real* slackvarcoeffs;
455  SCIP_Bool* rowneedsslackvar;
456 
457  SCIP_Longint nlps;
458  int currentlpcands;
459  int nlpcands;
460  int i;
461  int c;
462  int nslacks;
463  int nroundings;
464 
465  SCIP_Bool improvementfound;
466  SCIP_Bool numericalerror;
467 
468  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
469  assert(result != NULL);
470 
471  /* get master problem */
472  masterprob = GCGgetMasterprob(scip);
473  assert(masterprob != NULL);
474 
475  *result = SCIP_DIDNOTRUN;
476 
477  /* do not call heuristic of node was already detected to be infeasible */
478  if( nodeinfeasible )
479  return SCIP_OKAY;
480 
481  /* do not execute the heuristic on invalid relaxation solutions
482  * (which is the case if the node has been cut off)
483  */
484  if( !SCIPisRelaxSolValid(scip) )
485  {
486  SCIPdebugMsg(scip, "skipping GCG ZI rounding: invalid relaxation solution\n");
487  return SCIP_OKAY;
488  }
489 
490  /* only call heuristic if an optimal LP-solution is at hand */
491  if( SCIPgetLPSolstat(masterprob) != SCIP_LPSOLSTAT_OPTIMAL )
492  return SCIP_OKAY;
493 
494  /* get heuristic data */
495  heurdata = SCIPheurGetData(heur);
496  assert(heurdata != NULL);
497 
498  /* Do not call heuristic if deactivation check is enabled and percentage of found solutions in relation
499  * to number of calls falls below heurdata->stoppercentage */
500  if( heurdata->stopziround && SCIPheurGetNCalls(heur) >= heurdata->minstopncalls
501  && SCIPheurGetNSolsFound(heur)/(SCIP_Real)SCIPheurGetNCalls(heur) < heurdata->stoppercentage )
502  return SCIP_OKAY;
503 
504  /* assure that heuristic has not already been called after the last LP had been solved */
505  nlps = SCIPgetNLPs(masterprob);
506  if( nlps == heurdata->lastlp )
507  return SCIP_OKAY;
508 
509  heurdata->lastlp = nlps;
510 
511  /* get fractional variables */
512  SCIP_CALL( SCIPgetExternBranchCands(scip, &lpcands, NULL, NULL, &nlpcands, NULL, NULL, NULL, NULL) );
513 
514  /* make sure that there is at least one fractional variable that should be integral */
515  if( nlpcands == 0 )
516  return SCIP_OKAY;
517 
518  assert(nlpcands > 0);
519  assert(lpcands != NULL);
520 
521  /* get LP rows data */
522  rows = SCIPgetLPRows(scip);
523  nslacks = SCIPgetNLPRows(scip);
524 
525  /* cannot do anything if LP is empty */
526  if( nslacks == 0 )
527  return SCIP_OKAY;
528 
529  assert(rows != NULL);
530  assert(nslacks > 0);
531 
532  /* get the working solution from heuristic's local data */
533  sol = heurdata->sol;
534  assert(sol != NULL);
535 
536  *result = SCIP_DIDNOTFIND;
537 
538  zilpcands = NULL;
539 
540  /* copy the current LP solution to the working solution and allocate memory for local data */
541  SCIP_CALL( SCIPlinkRelaxSol(scip, sol) );
542  SCIP_CALL( SCIPallocBufferArray(scip, &zilpcands, nlpcands) );
543 
544  /* copy necessary data to local arrays */
545  BMScopyMemoryArray(zilpcands, lpcands, nlpcands);
546 
547  /* allocate buffer data arrays */
548  SCIP_CALL( SCIPallocBufferArray(scip, &slackvars, nslacks) );
549  SCIP_CALL( SCIPallocBufferArray(scip, &upslacks, nslacks) );
550  SCIP_CALL( SCIPallocBufferArray(scip, &downslacks, nslacks) );
551  SCIP_CALL( SCIPallocBufferArray(scip, &slackvarcoeffs, nslacks) );
552  SCIP_CALL( SCIPallocBufferArray(scip, &rowneedsslackvar, nslacks) );
553  SCIP_CALL( SCIPallocBufferArray(scip, &activities, nslacks) );
554 
555  BMSclearMemoryArray(slackvars, nslacks);
556  BMSclearMemoryArray(slackvarcoeffs, nslacks);
557  BMSclearMemoryArray(rowneedsslackvar, nslacks);
558 
559  numericalerror = FALSE;
560  nroundings = 0;
561 
562  /* loop over fractional variables and involved LP rows to find all rows which require a slack variable */
563  for( c = 0; c < nlpcands; ++c )
564  {
565  SCIP_VAR* cand;
566  SCIP_ROW** candrows;
567  int r;
568  int ncandrows;
569 
570  cand = zilpcands[c];
571  assert(cand != NULL);
572  assert(SCIPcolGetLPPos(SCIPvarGetCol(cand)) >= 0);
573 
574  candrows = SCIPcolGetRows(SCIPvarGetCol(cand));
575  ncandrows = SCIPcolGetNLPNonz(SCIPvarGetCol(cand));
576 
577  assert(candrows == NULL || ncandrows > 0);
578 
579  for( r = 0; r < ncandrows; ++r )
580  {
581  int rowpos;
582 
583  assert(candrows != NULL); /* to please flexelint */
584  assert(candrows[r] != NULL);
585  rowpos = SCIProwGetLPPos(candrows[r]);
586 
587  if( rowpos >= 0 && SCIPisFeasEQ(scip, SCIProwGetLhs(candrows[r]), SCIProwGetRhs(candrows[r])) )
588  {
589  rowneedsslackvar[rowpos] = TRUE;
590  SCIPdebugMsg(scip, " Row %s needs slack variable for variable %s\n", SCIProwGetName(candrows[r]), SCIPvarGetName(cand));
591  }
592  }
593  }
594 
595  /* calculate row slacks for every every row that belongs to the current LP and ensure, that the current solution
596  * has no violated constraint -- if any constraint is violated, i.e. a slack is significantly smaller than zero,
597  * this will cause the termination of the heuristic because Zirounding does not provide feasibility recovering
598  */
599  for( i = 0; i < nslacks; ++i )
600  {
601  SCIP_ROW* row;
602  SCIP_Real lhs;
603  SCIP_Real rhs;
604 
605  row = rows[i];
606 
607  assert(row != NULL);
608 
609  lhs = SCIProwGetLhs(row);
610  rhs = SCIProwGetRhs(row);
611 
612  /* get row activity */
613  activities[i] = SCIPgetRowSolActivity(scip, row, sol);
614  /** @todo This assertion has been commented out due to numerical troubles */
615 /* assert( SCIPisFeasLE(scip, lhs, activities[i]) && SCIPisFeasLE(scip, activities[i], rhs) ); */
616 
617  /* in special case if LHS or RHS is (-)infinity slacks have to be initialized as infinity */
618  if( SCIPisInfinity(scip, -lhs) )
619  downslacks[i] = SCIPinfinity(scip);
620  else
621  downslacks[i] = activities[i] - lhs;
622 
623  if( SCIPisInfinity(scip, rhs) )
624  upslacks[i] = SCIPinfinity(scip);
625  else
626  upslacks[i] = rhs - activities[i];
627 
628  SCIPdebugMsg(scip, "lhs:%5.2f <= act:%5.2g <= rhs:%5.2g --> down: %5.2g, up:%5.2g\n", lhs, activities[i], rhs, downslacks[i], upslacks[i]);
629 
630  /* row is an equation. Try to find a slack variable in the row, i.e.,
631  * a continuous variable which occurs only in this row. If no such variable exists,
632  * there is no hope for an IP-feasible solution in this round
633  */
634  if( SCIPisFeasEQ(scip, lhs, rhs) && rowneedsslackvar[i] )
635  {
636  /* @todo: This is only necessary for rows containing fractional variables. */
637  rowFindSlackVar(scip, row, &(slackvars[i]), &(slackvarcoeffs[i]));
638 
639  if( slackvars[i] == NULL )
640  {
641  SCIPdebugMsg(scip, "No slack variable found for equation %s, terminating ZI Round heuristic\n", SCIProwGetName(row));
642  goto TERMINATE;
643  }
644  else
645  {
646  SCIP_Real ubslackvar;
647  SCIP_Real lbslackvar;
648  SCIP_Real solvalslackvar;
649  SCIP_Real coeffslackvar;
650  SCIP_Real ubgap;
651  SCIP_Real lbgap;
652 
653  assert(SCIPvarGetType(slackvars[i]) == SCIP_VARTYPE_CONTINUOUS);
654  solvalslackvar = SCIPgetSolVal(scip, sol, slackvars[i]);
655  ubslackvar = SCIPvarGetUbGlobal(slackvars[i]);
656  lbslackvar = SCIPvarGetLbGlobal(slackvars[i]);
657 
658  coeffslackvar = slackvarcoeffs[i];
659  assert(!SCIPisFeasZero(scip, coeffslackvar));
660 
661  ubgap = MAX(0.0, ubslackvar - solvalslackvar);
662  lbgap = MAX(0.0, solvalslackvar - lbslackvar);
663 
664  if( SCIPisPositive(scip, coeffslackvar) )
665  {
666  if( !SCIPisInfinity(scip, lbslackvar) )
667  upslacks[i] += coeffslackvar * lbgap;
668  else
669  upslacks[i] = SCIPinfinity(scip);
670  if( !SCIPisInfinity(scip, ubslackvar) )
671  downslacks[i] += coeffslackvar * ubgap;
672  else
673  downslacks[i] = SCIPinfinity(scip);
674  }
675  else
676  {
677  if( !SCIPisInfinity(scip, ubslackvar) )
678  upslacks[i] -= coeffslackvar * ubgap;
679  else
680  upslacks[i] = SCIPinfinity(scip);
681  if( !SCIPisInfinity(scip, lbslackvar) )
682  downslacks[i] -= coeffslackvar * lbgap;
683  else
684  downslacks[i] = SCIPinfinity(scip);
685  }
686  SCIPdebugMsg(scip, " Slack variable for row %s at pos %d: %g <= %s = %g <= %g; Coeff %g, upslack = %g, downslack = %g \n",
687  SCIProwGetName(row), SCIProwGetLPPos(row), lbslackvar, SCIPvarGetName(slackvars[i]), solvalslackvar, ubslackvar, coeffslackvar,
688  upslacks[i], downslacks[i]);
689  }
690  }
691  /* due to numerical inaccuracies, the rows might be feasible, even if the slacks are
692  * significantly smaller than zero -> terminate
693  */
694  if( SCIPisFeasLT(scip, upslacks[i], 0.0) || SCIPisFeasLT(scip, downslacks[i], 0.0) )
695  goto TERMINATE;
696  }
697 
698  assert(nslacks == 0 || (upslacks != NULL && downslacks != NULL && activities != NULL));
699 
700  /* initialize number of remaining variables and flag to enter the main loop */
701  currentlpcands = nlpcands;
702  improvementfound = TRUE;
703 
704  /* iterate over variables as long as there are fractional variables left */
705  while( currentlpcands > 0 && improvementfound && (heurdata->maxroundingloops == -1 || nroundings < heurdata->maxroundingloops) )
706  { /*lint --e{850}*/
707  improvementfound = FALSE;
708  nroundings++;
709  SCIPdebugMsg(scip, "GCG zirounding enters while loop for %d time with %d candidates left. \n", nroundings, currentlpcands);
710 
711  /* check for every remaining fractional variable if a shifting decreases ZI-value of the variable */
712  for( c = 0; c < currentlpcands; ++c )
713  {
714  SCIP_VAR* var;
715  SCIP_Real oldsolval;
716  SCIP_Real upperbound;
717  SCIP_Real lowerbound;
718  SCIP_Real up;
719  SCIP_Real down;
720  SCIP_Real ziup;
721  SCIP_Real zidown;
722  SCIP_Real zicurrent;
723  SCIP_Real shiftval;
724  SCIP_Real newsolval;
725 
726  DIRECTION direction;
727 
728  var = zilpcands[c];
729  oldsolval = SCIPgetSolVal(scip, sol, var);
730  newsolval = oldsolval;
731 
732  /* It may be that external branching candidates are stored double,
733  * so the variable may have already been considered;
734  * in that case, ignore it
735  */
736  if( SCIPisFeasIntegral(scip, oldsolval) )
737  {
738  zilpcands[c] = zilpcands[currentlpcands - 1];
739  currentlpcands--;
740 
741  /* counter is decreased if end of candidates array has not been reached yet */
742  if( c < currentlpcands )
743  c--;
744 
745  continue;
746  }
747 
748  assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
749 
750  /* calculate bounds for variable and make sure that there are no numerical inconsistencies */
751  upperbound = SCIPinfinity(scip);
752  lowerbound = SCIPinfinity(scip);
753  calculateBounds(scip, var, oldsolval, &upperbound, &lowerbound, upslacks, downslacks, nslacks, &numericalerror);
754 
755  if( numericalerror )
756  goto TERMINATE;
757 
758  /* continue if only marginal shifts are possible */
759  if( MAX(upperbound, lowerbound) < MINSHIFT )
760  {
761  /* stop immediately if a variable has not been rounded during the last round */
762  if( nroundings == heurdata->maxroundingloops )
763  break;
764  else
765  continue;
766  }
767 
768  /* calculate the possible values after shifting */
769  up = oldsolval + upperbound;
770  down = oldsolval - lowerbound;
771 
772  /* if the variable is integer or implicit binary, do not shift further than the nearest integer */
773  if( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY )
774  {
775  SCIP_Real ceilx;
776  SCIP_Real floorx;
777 
778  ceilx = SCIPfeasCeil(scip, oldsolval);
779  floorx = SCIPfeasFloor(scip, oldsolval);
780  up = MIN(up, ceilx);
781  down = MAX(down, floorx);
782  }
783 
784  /* calculate necessary values */
785  ziup = getZiValue(scip, up);
786  zidown = getZiValue(scip, down);
787  zicurrent = getZiValue(scip, oldsolval);
788 
789  /* calculate the shifting direction that reduces ZI-value the most,
790  * if both directions improve ZI-value equally, take the direction which improves the objective
791  */
792  if( SCIPisFeasLT(scip, zidown, zicurrent) || SCIPisFeasLT(scip, ziup, zicurrent) )
793  {
794  if( SCIPisFeasEQ(scip,ziup, zidown) )
795  direction = SCIPisFeasGE(scip, SCIPvarGetObj(var), 0.0) ? DIRECTION_DOWN : DIRECTION_UP;
796  else if( SCIPisFeasLT(scip, zidown, ziup) )
797  direction = DIRECTION_DOWN;
798  else
799  direction = DIRECTION_UP;
800 
801  /* once a possible shifting direction and value have been found, variable value is updated */
802  shiftval = (direction == DIRECTION_UP ? up - oldsolval : down - oldsolval);
803 
804  /* this improves numerical stability in some cases */
805  if( direction == DIRECTION_UP )
806  shiftval = MIN(shiftval, upperbound);
807  else
808  shiftval = MIN(shiftval, lowerbound);
809 
810  /* update the solution */
811  newsolval = direction == DIRECTION_UP ? up : down;
812  SCIP_CALL( SCIPsetSolVal(scip, sol, var, newsolval) );
813 
814  /* update the rows activities and slacks */
815  SCIP_CALL( updateSlacks(scip, sol, var, shiftval, upslacks,
816  downslacks, activities, slackvars, slackvarcoeffs, nslacks) );
817 
818  SCIPdebugMsg(scip, "GCG zirounding update step : %d var index, oldsolval=%g, shiftval=%g\n",
819  SCIPvarGetIndex(var), oldsolval, shiftval);
820  /* since at least one improvement has been found, heuristic will enter main loop for another time because the improvement
821  * might affect many LP rows and their current slacks and thus make further rounding steps possible */
822  improvementfound = TRUE;
823  }
824 
825  /* if solution value of variable has become feasibly integral due to rounding step,
826  * variable is put at the end of remaining candidates array so as not to be considered in future loops
827  */
828  if( SCIPisFeasIntegral(scip, newsolval) )
829  {
830  zilpcands[c] = zilpcands[currentlpcands - 1];
831  currentlpcands--;
832 
833  /* counter is decreased if end of candidates array has not been reached yet */
834  if( c < currentlpcands )
835  c--;
836  }
837  else if( nroundings == heurdata->maxroundingloops )
838  goto TERMINATE;
839  }
840  }
841 
842  /* in case that no candidate is left for rounding after the final main loop
843  * the found solution has to be checked for feasibility in the original problem
844  */
845  if( currentlpcands == 0 )
846  {
847  SCIP_Bool stored;
848  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, FALSE, &stored) );
849  if( stored )
850  {
851 #ifdef SCIP_DEBUG
852  SCIPdebugMsg(scip, "found feasible rounded solution:\n");
853  SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) );
854 #endif
855  SCIPstatisticMessage(" GCG ZI Round solution value: %g \n", SCIPgetSolOrigObj(scip, sol));
856  *result = SCIP_FOUNDSOL;
857  }
858  }
859 
860  /* free memory for all locally allocated data */
861  TERMINATE:
862  SCIPfreeBufferArrayNull(scip, &activities);
863  SCIPfreeBufferArrayNull(scip, &rowneedsslackvar);
864  SCIPfreeBufferArrayNull(scip, &slackvarcoeffs);
865  SCIPfreeBufferArrayNull(scip, &downslacks);
866  SCIPfreeBufferArrayNull(scip, &upslacks);
867  SCIPfreeBufferArrayNull(scip, &slackvars);
868  SCIPfreeBufferArrayNull(scip, &zilpcands);
869 
870  return SCIP_OKAY;
871 }
872 
873 /*
874  * primal heuristic specific interface methods
875  */
876 
877 /** creates the GCG zirounding primal heuristic and includes it in SCIP */
879  SCIP* scip /**< SCIP data structure */
880  )
881 {
882  SCIP_HEURDATA* heurdata;
883  SCIP_HEUR* heur;
884 
885  /* create GCG zirounding primal heuristic data */
886  SCIP_CALL( SCIPallocBlockMemory(scip, &heurdata) );
887 
888  /* include primal heuristic */
889  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
891  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecGcgzirounding, heurdata) );
892 
893  assert(heur != NULL);
894 
895  /* set non-NULL pointers to callback methods */
896  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeGcgzirounding) );
897  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitGcgzirounding) );
898  SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitGcgzirounding) );
899  SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolGcgzirounding) );
900 
901  /* add GCG zirounding primal heuristic parameters */
902  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/gcgzirounding/maxroundingloops",
903  "determines maximum number of rounding loops",
904  &heurdata->maxroundingloops, TRUE, DEFAULT_MAXROUNDINGLOOPS, 0, INT_MAX, NULL, NULL) );
905  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/gcgzirounding/stopziround",
906  "flag to determine if Zirounding is deactivated after a certain percentage of unsuccessful calls",
907  &heurdata->stopziround, TRUE, DEFAULT_STOPZIROUND, NULL, NULL) );
908  SCIP_CALL( SCIPaddRealParam(scip,"heuristics/gcgzirounding/stoppercentage",
909  "if percentage of found solutions falls below this parameter, Zirounding will be deactivated",
910  &heurdata->stoppercentage, TRUE, DEFAULT_STOPPERCENTAGE, 0.0, 1.0, NULL, NULL) );
911  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/gcgzirounding/minstopncalls",
912  "determines the minimum number of calls before percentage-based deactivation of"
913  " Zirounding is applied", &heurdata->minstopncalls, TRUE, DEFAULT_MINSTOPNCALLS, 1, INT_MAX, NULL, NULL) );
914 
915  return SCIP_OKAY;
916 }
#define HEUR_USESSUBSCIP
#define HEUR_MAXDEPTH
SCIP_Real stoppercentage
static void calculateBounds(SCIP *scip, SCIP_VAR *var, SCIP_Real currentvalue, SCIP_Real *upperbound, SCIP_Real *lowerbound, SCIP_Real *upslacks, SCIP_Real *downslacks, int nslacks, SCIP_Bool *numericalerror)
GCG interface methods.
@ DIRECTION_DOWN
#define DEFAULT_STOPZIROUND
zirounding primal heuristic
#define HEUR_PRIORITY
static SCIP_DECL_HEUREXEC(heurExecGcgzirounding)
SCIP_Longint lastlp
#define HEUR_FREQ
SCIP_SOL * sol
SCIP_RETCODE SCIPincludeHeurGcgzirounding(SCIP *scip)
static SCIP_DECL_HEURINIT(heurInitGcgzirounding)
#define DEFAULT_MAXROUNDINGLOOPS
#define DEFAULT_MINSTOPNCALLS
#define MINSHIFT
#define HEUR_DESC
SCIP * GCGgetMasterprob(SCIP *scip)
Definition: relax_gcg.c:3920
#define HEUR_FREQOFS
static void rowFindSlackVar(SCIP *scip, SCIP_ROW *row, SCIP_VAR **varpointer, SCIP_Real *coeffpointer)
enum Direction DIRECTION
static SCIP_Real getZiValue(SCIP *scip, SCIP_Real val)
GCG relaxator.
#define HEUR_DISPCHAR
#define HEUR_TIMING
static SCIP_RETCODE updateSlacks(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real shiftvalue, SCIP_Real *upslacks, SCIP_Real *downslacks, SCIP_Real *activities, SCIP_VAR **slackvars, SCIP_Real *slackcoeffs, int nslacks)
@ DIRECTION_UP
#define HEUR_NAME
static SCIP_DECL_HEURFREE(heurFreeGcgzirounding)
static SCIP_DECL_HEURINITSOL(heurInitsolGcgzirounding)
static SCIP_DECL_HEUREXIT(heurExitGcgzirounding)
SCIP_Bool stopziround
#define DEFAULT_STOPPERCENTAGE