Scippy

SCIP

Solving Constraint Integer Programs

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