Scippy

SCIP

Solving Constraint Integer Programs

heur_simplerounding.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_simplerounding.c
17  * @brief simple and fast LP rounding heuristic
18  * @author Tobias Achterberg
19  * @author Marc Pfetsch
20  *
21  * The heuristic also tries to round relaxation solutions if available.
22  */
23 
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25 
26 #include <assert.h>
27 #include <string.h>
28 
30 
31 
32 #define HEUR_NAME "simplerounding"
33 #define HEUR_DESC "simple and fast LP rounding heuristic"
34 #define HEUR_DISPCHAR 'r'
35 #define HEUR_PRIORITY 0
36 #define HEUR_FREQ 1
37 #define HEUR_FREQOFS 0
38 #define HEUR_MAXDEPTH -1
39 #define HEUR_TIMING SCIP_HEURTIMING_DURINGLPLOOP | SCIP_HEURTIMING_DURINGPRICINGLOOP
40 #define HEUR_USESSUBSCIP FALSE /**< does the heuristic use a secondary SCIP instance? */
41 
42 #define DEFAULT_ONCEPERNODE FALSE /**< should the heuristic only be called once per node? */
43 
44 /* locally defined heuristic data */
45 struct SCIP_HeurData
46 {
47  SCIP_SOL* sol; /**< working solution */
48  SCIP_Longint lastlp; /**< last LP number where the heuristic was applied */
49  int nroundablevars; /**< number of variables that can be rounded (-1 if not yet calculated) */
50  SCIP_Bool oncepernode; /**< should the heuristic only be called once per node? */
51 };
52 
53 
54 /*
55  * Local methods
56  */
57 
58 /** perform rounding */
59 static
61  SCIP* scip, /**< SCIP main data structure */
62  SCIP_SOL* sol, /**< solution to round */
63  SCIP_VAR** cands, /**< candidate variables */
64  SCIP_Real* candssol, /**< solutions of candidate variables */
65  int ncands, /**< number of candidates */
66  SCIP_RESULT* result /**< pointer to store the result of the heuristic call */
67  )
68 {
69  int c;
70 
71  /* round all roundable fractional columns in the corresponding direction as long as no unroundable column was found */
72  for (c = 0; c < ncands; ++c)
73  {
74  SCIP_VAR* var;
75  SCIP_Real oldsolval;
76  SCIP_Real newsolval;
77  SCIP_Bool mayrounddown;
78  SCIP_Bool mayroundup;
79 
80  oldsolval = candssol[c];
81  assert( ! SCIPisFeasIntegral(scip, oldsolval) );
82  var = cands[c];
83  assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN );
84  mayrounddown = SCIPvarMayRoundDown(var);
85  mayroundup = SCIPvarMayRoundUp(var);
86  SCIPdebugMessage("simple rounding heuristic: var <%s>, val=%g, rounddown=%u, roundup=%u\n",
87  SCIPvarGetName(var), oldsolval, mayrounddown, mayroundup);
88 
89  /* choose rounding direction */
90  if ( mayrounddown && mayroundup )
91  {
92  /* we can round in both directions: round in objective function direction */
93  if ( SCIPvarGetObj(var) >= 0.0 )
94  newsolval = SCIPfeasFloor(scip, oldsolval);
95  else
96  newsolval = SCIPfeasCeil(scip, oldsolval);
97  }
98  else if ( mayrounddown )
99  newsolval = SCIPfeasFloor(scip, oldsolval);
100  else if ( mayroundup )
101  newsolval = SCIPfeasCeil(scip, oldsolval);
102  else
103  break;
104 
105  /* store new solution value */
106  SCIP_CALL( SCIPsetSolVal(scip, sol, var, newsolval) );
107  }
108 
109  /* check, if rounding was successful */
110  if( c == ncands )
111  {
112  SCIP_Bool stored;
113 
114  SCIP_CALL ( SCIPadjustImplicitSolVals(scip, sol, TRUE) );
115 
116  if( SCIPallColsInLP(scip) )
117  {
118  /* check solution for feasibility, and add it to solution store if possible
119  * neither integrality nor feasibility of LP rows has to be checked, because all fractional
120  * variables were already moved in feasible direction to the next integer
121  */
122  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, FALSE, &stored) );
123  }
124  else
125  {
126  /* if there are variables which are not present in the LP, e.g., for
127  * column generation, we need to check their bounds
128  */
129  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, TRUE, FALSE, FALSE, &stored) );
130  }
131 
132  if( stored )
133  {
134 #ifdef SCIP_DEBUG
135  SCIPdebugMessage("found feasible rounded solution:\n");
136  SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) );
137 #endif
138  *result = SCIP_FOUNDSOL;
139  }
140  }
141  return SCIP_OKAY;
142 }
143 
144 /** perform LP-rounding */
145 static
147  SCIP* scip, /**< SCIP main data structure */
148  SCIP_HEURDATA* heurdata, /**< heuristic data */
149  SCIP_HEURTIMING heurtiming, /**< heuristic timing mask */
150  SCIP_RESULT* result /**< pointer to store the result of the heuristic call */
151  )
152 {
153  SCIP_SOL* sol;
154  SCIP_VAR** lpcands;
155  SCIP_Real* lpcandssol;
156  SCIP_Longint nlps;
157  int nlpcands;
158 
159  /* only call heuristic, if an optimal LP solution is at hand */
161  return SCIP_OKAY;
162 
163  /* only call heuristic, if the LP objective value is smaller than the cutoff bound */
164  if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) )
165  return SCIP_OKAY;
166 
167  /* get fractional variables, that should be integral */
168  /* todo polish fractional implicit integer variables separately before trying the solution */
169  SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL) );
170 
171  /* only call heuristic, if LP solution is fractional; except we are called during pricing, in this case we
172  * want to detect a (mixed) integer (LP) solution which is primal feasible */
173  if ( nlpcands == 0 && heurtiming != SCIP_HEURTIMING_DURINGPRICINGLOOP )
174  return SCIP_OKAY;
175 
176  /* don't call heuristic, if there are more fractional variables than roundable ones */
177  if ( nlpcands > heurdata->nroundablevars )
178  return SCIP_OKAY;
179 
180  /* get the working solution from heuristic's local data */
181  sol = heurdata->sol;
182  assert( sol != NULL );
183 
184  /* copy the current LP solution to the working solution */
185  SCIP_CALL( SCIPlinkLPSol(scip, sol) );
186 
187  /* don't call heuristic, if we have already processed the current LP solution */
188  nlps = SCIPgetNLPs(scip);
189  if( nlps == heurdata->lastlp )
190  return SCIP_OKAY;
191  heurdata->lastlp = nlps;
192 
193  /* perform simple rounding */
194  SCIPdebugMessage("executing simple LP-rounding heuristic: %d fractionals\n", nlpcands);
195  SCIP_CALL( performSimpleRounding(scip, sol, lpcands, lpcandssol, nlpcands, result) );
196 
197  return SCIP_OKAY;
198 }
199 
200 /** perform relaxation solution rounding */
201 static
203  SCIP* scip, /**< SCIP main data structure */
204  SCIP_HEURDATA* heurdata, /**< heuristic data */
205  SCIP_RESULT* result /**< pointer to store the result of the heuristic call */
206  )
207 {
208  SCIP_SOL* sol;
209  SCIP_VAR** vars;
210  SCIP_VAR** relaxcands;
211  SCIP_Real* relaxcandssol;
212  int nrelaxcands;
213  int nbinvars;
214  int nintvars;
215  int nvars;
216  int v;
217 
218  /* do not call heuristic if no relaxation solution is available */
219  if ( ! SCIPisRelaxSolValid(scip) )
220  return SCIP_OKAY;
221 
222  /* get variables */
223  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
224  nvars = nbinvars + nintvars; /* consider integral variables (don't have to care for implicit ints) */
225 
226  /* get storage */
227  SCIP_CALL( SCIPallocBufferArray(scip, &relaxcands, nvars) );
228  SCIP_CALL( SCIPallocBufferArray(scip, &relaxcandssol, nvars) );
229 
230  /* get fractional variables, that should be integral */
231  nrelaxcands = 0;
232  for (v = 0; v < nvars; ++v)
233  {
234  SCIP_Real val;
235 
236  val = SCIPgetRelaxSolVal(scip, vars[v]);
237  if ( ! SCIPisFeasZero(scip, val) )
238  {
239  relaxcands[nrelaxcands] = vars[v];
240  relaxcandssol[nrelaxcands++] = val;
241  }
242  }
243 
244  /* don't call heuristic, if there are more fractional variables than roundable ones */
245  if ( nrelaxcands > heurdata->nroundablevars )
246  {
247  SCIPfreeBufferArray(scip, &relaxcands);
248  SCIPfreeBufferArray(scip, &relaxcandssol);
249  return SCIP_OKAY;
250  }
251 
252  /* get the working solution from heuristic's local data */
253  sol = heurdata->sol;
254  assert( sol != NULL );
255 
256  /* copy the current relaxation solution to the working solution */
257  SCIP_CALL( SCIPlinkRelaxSol(scip, sol) );
258 
259  /* perform simple rounding */
260  SCIPdebugMessage("executing simple rounding heuristic on relaxation solution: %d fractionals\n", nrelaxcands);
261  SCIP_CALL( performSimpleRounding(scip, sol, relaxcands, relaxcandssol, nrelaxcands, result) );
262 
263  /* free storage */
264  SCIPfreeBufferArray(scip, &relaxcands);
265  SCIPfreeBufferArray(scip, &relaxcandssol);
266 
267  return SCIP_OKAY;
268 }
269 
270 
271 /*
272  * Callback methods
273  */
274 
275 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
276 static
277 SCIP_DECL_HEURCOPY(heurCopySimplerounding)
278 { /*lint --e{715}*/
279  assert(scip != NULL);
280  assert(heur != NULL);
281  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
282 
283  /* call inclusion method of primal heuristic */
285 
286  return SCIP_OKAY;
287 }
288 
289 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
290 static
291 SCIP_DECL_HEURFREE(heurFreeSimplerounding) /*lint --e{715}*/
292 { /*lint --e{715}*/
293  SCIP_HEURDATA* heurdata;
294 
295  assert(heur != NULL);
296  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
297  assert(scip != NULL);
298 
299  /* free heuristic data */
300  heurdata = SCIPheurGetData(heur);
301  assert(heurdata != NULL);
302  SCIPfreeMemory(scip, &heurdata);
303  SCIPheurSetData(heur, NULL);
304 
305  return SCIP_OKAY;
306 }
307 
308 
309 /** initialization method of primal heuristic (called after problem was transformed) */
310 static
311 SCIP_DECL_HEURINIT(heurInitSimplerounding) /*lint --e{715}*/
312 { /*lint --e{715}*/
313  SCIP_HEURDATA* heurdata;
314 
315  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
316  heurdata = SCIPheurGetData(heur);
317  assert(heurdata != NULL);
318 
319  /* create heuristic data */
320  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
321  heurdata->lastlp = -1;
322  heurdata->nroundablevars = -1;
323 
324  return SCIP_OKAY;
325 }
326 
327 
328 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
329 static
330 SCIP_DECL_HEUREXIT(heurExitSimplerounding) /*lint --e{715}*/
331 { /*lint --e{715}*/
332  SCIP_HEURDATA* heurdata;
333 
334  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
335 
336  /* free heuristic data */
337  heurdata = SCIPheurGetData(heur);
338  assert(heurdata != NULL);
339  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
340 
341  return SCIP_OKAY;
342 }
343 
344 
345 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
346 static
347 SCIP_DECL_HEURINITSOL(heurInitsolSimplerounding)
348 {
349  SCIP_HEURDATA* heurdata;
350 
351  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
352 
353  heurdata = SCIPheurGetData(heur);
354  assert(heurdata != NULL);
355  heurdata->lastlp = -1;
356 
357  /* change the heuristic's timingmask, if nit should be called only once per node */
358  if( heurdata->oncepernode )
360 
361  return SCIP_OKAY;
362 }
363 
364 
365 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
366 static
367 SCIP_DECL_HEUREXITSOL(heurExitsolSimplerounding)
368 {
369  /* reset the timing mask to its default value */
371 
372  return SCIP_OKAY;
373 }
374 
375 
376 /** execution method of primal heuristic */
377 static
378 SCIP_DECL_HEUREXEC(heurExecSimplerounding) /*lint --e{715}*/
379 { /*lint --e{715}*/
380  SCIP_HEURDATA* heurdata;
381 
382  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
383  assert(result != NULL);
384  assert(SCIPhasCurrentNodeLP(scip));
385 
386  *result = SCIP_DIDNOTRUN;
387 
388  /* only call heuristic, if an optimal LP solution is at hand or if relaxation solution is available */
390  return SCIP_OKAY;
391 
392  /* only call heuristic, if the LP objective value is smaller than the cutoff bound */
393  if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) )
394  return SCIP_OKAY;
395 
396  /* get heuristic data */
397  heurdata = SCIPheurGetData(heur);
398  assert(heurdata != NULL);
399 
400  /* don't call heuristic, if we have already processed the current LP solution but no relaxation solution is available */
401  if ( SCIPgetNLPs(scip) == heurdata->lastlp && ! SCIPisRelaxSolValid(scip) )
402  return SCIP_OKAY;
403 
404  /* on our first call or after each pricing round, calculate the number of roundable variables */
405  if( heurdata->nroundablevars == -1 || heurtiming == SCIP_HEURTIMING_DURINGPRICINGLOOP )
406  {
407  SCIP_VAR** vars;
408  int nvars;
409  int nroundablevars;
410  int i;
411 
412  vars = SCIPgetVars(scip);
413  nvars = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip);
414  nroundablevars = 0;
415  for( i = 0; i < nvars; ++i )
416  {
417  if( SCIPvarMayRoundDown(vars[i]) || SCIPvarMayRoundUp(vars[i]) )
418  nroundablevars++;
419  }
420  heurdata->nroundablevars = nroundablevars;
421  }
422 
423  /* don't call heuristic if there are no roundable variables; except we are called during pricing, in this case we
424  * want to detect a (mixed) integer (LP) solution which is primal feasible */
425  if( heurdata->nroundablevars == 0 && heurtiming != SCIP_HEURTIMING_DURINGPRICINGLOOP )
426  return SCIP_OKAY;
427 
428  *result = SCIP_DIDNOTFIND;
429 
430  /* try to round LP solution */
431  SCIP_CALL( performLPSimpleRounding(scip, heurdata, heurtiming, result) );
432 
433  /* try to round relaxation solution */
434  SCIP_CALL( performRelaxSimpleRounding(scip, heurdata, result) );
435 
436  return SCIP_OKAY;
437 }
438 
439 /*
440  * heuristic specific interface methods
441  */
442 
443 /** creates the simple rounding heuristic and includes it in SCIP */
445  SCIP* scip /**< SCIP data structure */
446  )
447 {
448  SCIP_HEURDATA* heurdata;
449  SCIP_HEUR* heur;
450 
451  /* create heuristic data */
452  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
453 
454  /* include primal heuristic */
455  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
457  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecSimplerounding, heurdata) );
458  assert(heur != NULL);
459 
460  /* set non-NULL pointers to callback methods */
461  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopySimplerounding) );
462  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitSimplerounding) );
463  SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitSimplerounding) );
464  SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolSimplerounding) );
465  SCIP_CALL( SCIPsetHeurExitsol(scip, heur, heurExitsolSimplerounding) );
466  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeSimplerounding) );
467 
468  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/oncepernode",
469  "should the heuristic only be called once per node?",
470  &heurdata->oncepernode, TRUE, DEFAULT_ONCEPERNODE, NULL, NULL) );
471 
472  return SCIP_OKAY;
473 }
474