Scippy

SCIP

Solving Constraint Integer Programs

heur_actconsdiving.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_actconsdiving.c
17  * @brief LP diving heuristic that chooses fixings w.r.t. the active constraints the variable appear in
18  * @author Tobias Achterberg
19  */
20 
21 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
22 
23 #include <assert.h>
24 #include <string.h>
25 
27 
28 
29 #define HEUR_NAME "actconsdiving"
30 #define HEUR_DESC "LP diving heuristic that chooses fixings w.r.t. the active constraints"
31 #define HEUR_DISPCHAR 'a'
32 #define HEUR_PRIORITY -1003700
33 #define HEUR_FREQ -1
34 #define HEUR_FREQOFS 5
35 #define HEUR_MAXDEPTH -1
36 #define HEUR_TIMING SCIP_HEURTIMING_AFTERLPPLUNGE
37 #define HEUR_USESSUBSCIP FALSE /**< does the heuristic use a secondary SCIP instance? */
38 
39 
40 /*
41  * Default parameter settings
42  */
43 
44 #define DEFAULT_MINRELDEPTH 0.0 /**< minimal relative depth to start diving */
45 #define DEFAULT_MAXRELDEPTH 1.0 /**< maximal relative depth to start diving */
46 #define DEFAULT_MAXLPITERQUOT 0.05 /**< maximal fraction of diving LP iterations compared to node LP iterations */
47 #define DEFAULT_MAXLPITEROFS 1000 /**< additional number of allowed LP iterations */
48 #define DEFAULT_MAXDIVEUBQUOT 0.8 /**< maximal quotient (curlowerbound - lowerbound)/(cutoffbound - lowerbound)
49  * where diving is performed (0.0: no limit) */
50 #define DEFAULT_MAXDIVEAVGQUOT 0.0 /**< maximal quotient (curlowerbound - lowerbound)/(avglowerbound - lowerbound)
51  * where diving is performed (0.0: no limit) */
52 #define DEFAULT_MAXDIVEUBQUOTNOSOL 0.1 /**< maximal UBQUOT when no solution was found yet (0.0: no limit) */
53 #define DEFAULT_MAXDIVEAVGQUOTNOSOL 0.0 /**< maximal AVGQUOT when no solution was found yet (0.0: no limit) */
54 #define DEFAULT_BACKTRACK TRUE /**< use one level of backtracking if infeasibility is encountered? */
55 
56 #define MINLPITER 10000 /**< minimal number of LP iterations allowed in each LP solving call */
57 
58 
59 /* locally defined heuristic data */
60 struct SCIP_HeurData
61 {
62  SCIP_SOL* sol; /**< working solution */
63  SCIP_Real minreldepth; /**< minimal relative depth to start diving */
64  SCIP_Real maxreldepth; /**< maximal relative depth to start diving */
65  SCIP_Real maxlpiterquot; /**< maximal fraction of diving LP iterations compared to node LP iterations */
66  int maxlpiterofs; /**< additional number of allowed LP iterations */
67  SCIP_Real maxdiveubquot; /**< maximal quotient (curlowerbound - lowerbound)/(cutoffbound - lowerbound)
68  * where diving is performed (0.0: no limit) */
69  SCIP_Real maxdiveavgquot; /**< maximal quotient (curlowerbound - lowerbound)/(avglowerbound - lowerbound)
70  * where diving is performed (0.0: no limit) */
71  SCIP_Real maxdiveubquotnosol; /**< maximal UBQUOT when no solution was found yet (0.0: no limit) */
72  SCIP_Real maxdiveavgquotnosol;/**< maximal AVGQUOT when no solution was found yet (0.0: no limit) */
73  SCIP_Bool backtrack; /**< use one level of backtracking if infeasibility is encountered? */
74  SCIP_Longint nlpiterations; /**< LP iterations used in this heuristic */
75  int nsuccess; /**< number of runs that produced at least one feasible solution */
76 };
77 
78 
79 /*
80  * local methods
81  */
82 
83 /** returns a score value for the given variable based on the active constraints that the variable appears in */
84 static
86  SCIP* scip, /**< SCIP data structure */
87  SCIP_VAR* var, /**< variable to get the score value for */
88  SCIP_Real* downscore, /**< pointer to store the score for branching downwards */
89  SCIP_Real* upscore /**< pointer to store the score for branching upwards */
90  )
91 {
92  SCIP_COL* col;
93  SCIP_ROW** rows;
94  SCIP_Real* vals;
95  int nrows;
96  int r;
97  int nactrows;
98  SCIP_Real downcoefsum;
99  SCIP_Real upcoefsum;
100  SCIP_Real score;
101 
102  assert(downscore != NULL);
103  assert(upscore != NULL);
104 
105  *downscore = 0.0;
106  *upscore = 0.0;
108  return 0.0;
109 
110  col = SCIPvarGetCol(var);
111  assert(col != NULL);
112 
113  rows = SCIPcolGetRows(col);
114  vals = SCIPcolGetVals(col);
115  nrows = SCIPcolGetNLPNonz(col);
116  nactrows = 0;
117  downcoefsum = 0.0;
118  upcoefsum = 0.0;
119  for( r = 0; r < nrows; ++r )
120  {
121  SCIP_Real activity;
122  SCIP_Real lhs;
123  SCIP_Real rhs;
124  SCIP_Real dualsol;
125 
126  /* calculate number of active constraint sides, i.e., count equations as two */
127  lhs = SCIProwGetLhs(rows[r]);
128  rhs = SCIProwGetRhs(rows[r]);
129  activity = SCIPgetRowLPActivity(scip, rows[r]);
130  dualsol = SCIProwGetDualsol(rows[r]);
131  if( SCIPisFeasEQ(scip, activity, lhs) )
132  {
133  SCIP_Real coef;
134 
135  nactrows++;
136  coef = vals[r] / SCIProwGetNorm(rows[r]);
137  if( SCIPisFeasPositive(scip, dualsol) )
138  {
139  if( coef > 0.0 )
140  downcoefsum += coef;
141  else
142  upcoefsum -= coef;
143  }
144  }
145  else if( SCIPisFeasEQ(scip, activity, rhs) )
146  {
147  SCIP_Real coef;
148 
149  nactrows++;
150  coef = vals[r] / SCIProwGetNorm(rows[r]);
151  if( SCIPisFeasNegative(scip, dualsol) )
152  {
153  if( coef > 0.0 )
154  upcoefsum += coef;
155  else
156  downcoefsum -= coef;
157  }
158  }
159  }
160  score = 1e-3*nactrows + (downcoefsum + 1e-6) * (upcoefsum + 1e-6);
161  *downscore = -downcoefsum;
162  *upscore = -upcoefsum;
163 
164  return score;
165 }
166 
167 
168 /*
169  * Callback methods
170  */
171 
172 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
173 static
174 SCIP_DECL_HEURCOPY(heurCopyActconsdiving)
175 { /*lint --e{715}*/
176  assert(scip != NULL);
177  assert(heur != NULL);
178  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
179 
180  /* call inclusion method of primal heuristic */
182 
183  return SCIP_OKAY;
184 }
185 
186 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
187 static
188 SCIP_DECL_HEURFREE(heurFreeActconsdiving) /*lint --e{715}*/
189 { /*lint --e{715}*/
190  SCIP_HEURDATA* heurdata;
191 
192  assert(heur != NULL);
193  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
194  assert(scip != NULL);
195 
196  /* free heuristic data */
197  heurdata = SCIPheurGetData(heur);
198  assert(heurdata != NULL);
199  SCIPfreeMemory(scip, &heurdata);
200  SCIPheurSetData(heur, NULL);
201 
202  return SCIP_OKAY;
203 }
204 
205 
206 /** initialization method of primal heuristic (called after problem was transformed) */
207 static
208 SCIP_DECL_HEURINIT(heurInitActconsdiving) /*lint --e{715}*/
209 { /*lint --e{715}*/
210  SCIP_HEURDATA* heurdata;
211 
212  assert(heur != NULL);
213  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
214 
215  /* get heuristic data */
216  heurdata = SCIPheurGetData(heur);
217  assert(heurdata != NULL);
218 
219  /* create working solution */
220  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
221 
222  /* initialize data */
223  heurdata->nlpiterations = 0;
224  heurdata->nsuccess = 0;
225 
226  return SCIP_OKAY;
227 }
228 
229 
230 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
231 static
232 SCIP_DECL_HEUREXIT(heurExitActconsdiving) /*lint --e{715}*/
233 { /*lint --e{715}*/
234  SCIP_HEURDATA* heurdata;
235 
236  assert(heur != NULL);
237  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
238 
239  /* get heuristic data */
240  heurdata = SCIPheurGetData(heur);
241  assert(heurdata != NULL);
242 
243  /* free working solution */
244  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
245 
246  return SCIP_OKAY;
247 }
248 
249 
250 /** execution method of primal heuristic */
251 static
252 SCIP_DECL_HEUREXEC(heurExecActconsdiving) /*lint --e{715}*/
253 { /*lint --e{715}*/
254  SCIP_HEURDATA* heurdata;
255  SCIP_LPSOLSTAT lpsolstat;
256  SCIP_VAR* var;
257  SCIP_VAR** lpcands;
258  SCIP_Real* lpcandssol;
259  SCIP_Real* lpcandsfrac;
260  SCIP_Real searchubbound;
261  SCIP_Real searchavgbound;
262  SCIP_Real searchbound;
263  SCIP_Real objval;
264  SCIP_Real oldobjval;
265  SCIP_Real frac;
266  SCIP_Real bestfrac;
267  SCIP_Bool bestcandmayrounddown;
268  SCIP_Bool bestcandmayroundup;
269  SCIP_Bool bestcandroundup;
270  SCIP_Bool mayrounddown;
271  SCIP_Bool mayroundup;
272  SCIP_Bool roundup;
273  SCIP_Bool lperror;
274  SCIP_Bool cutoff;
275  SCIP_Bool backtracked;
276  SCIP_Longint ncalls;
277  SCIP_Longint nsolsfound;
278  SCIP_Longint nlpiterations;
279  SCIP_Longint maxnlpiterations;
280  int nlpcands;
281  int startnlpcands;
282  int depth;
283  int maxdepth;
284  int maxdivedepth;
285  int divedepth;
286  SCIP_Real actscore;
287  SCIP_Real downscore;
288  SCIP_Real upscore;
289  SCIP_Real bestactscore;
290  int bestcand;
291  int c;
292 
293  assert(heur != NULL);
294  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
295  assert(scip != NULL);
296  assert(result != NULL);
297  assert(SCIPhasCurrentNodeLP(scip));
298 
299  *result = SCIP_DELAYED;
300 
301  /* do not call heuristic of node was already detected to be infeasible */
302  if( nodeinfeasible )
303  return SCIP_OKAY;
304 
305  /* only call heuristic, if an optimal LP solution is at hand */
307  return SCIP_OKAY;
308 
309  /* only call heuristic, if the LP objective value is smaller than the cutoff bound */
310  if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) )
311  return SCIP_OKAY;
312 
313  /* only call heuristic, if the LP solution is basic (which allows fast resolve in diving) */
314  if( !SCIPisLPSolBasic(scip) )
315  return SCIP_OKAY;
316 
317  /* don't dive two times at the same node */
318  if( SCIPgetLastDivenode(scip) == SCIPgetNNodes(scip) && SCIPgetDepth(scip) > 0 )
319  return SCIP_OKAY;
320 
321  *result = SCIP_DIDNOTRUN;
322 
323  /* get heuristic's data */
324  heurdata = SCIPheurGetData(heur);
325  assert(heurdata != NULL);
326 
327  /* only try to dive, if we are in the correct part of the tree, given by minreldepth and maxreldepth */
328  depth = SCIPgetDepth(scip);
329  maxdepth = SCIPgetMaxDepth(scip);
330  maxdepth = MAX(maxdepth, 30);
331  if( depth < heurdata->minreldepth*maxdepth || depth > heurdata->maxreldepth*maxdepth )
332  return SCIP_OKAY;
333 
334  /* calculate the maximal number of LP iterations until heuristic is aborted */
335  nlpiterations = SCIPgetNNodeLPIterations(scip);
336  ncalls = SCIPheurGetNCalls(heur);
337  nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + heurdata->nsuccess;
338  maxnlpiterations = (SCIP_Longint)((1.0 + 10.0*(nsolsfound+1.0)/(ncalls+1.0)) * heurdata->maxlpiterquot * nlpiterations);
339  maxnlpiterations += heurdata->maxlpiterofs;
340 
341  /* don't try to dive, if we took too many LP iterations during diving */
342  if( heurdata->nlpiterations >= maxnlpiterations )
343  return SCIP_OKAY;
344 
345  /* allow at least a certain number of LP iterations in this dive */
346  maxnlpiterations = MAX(maxnlpiterations, heurdata->nlpiterations + MINLPITER);
347 
348  /* get fractional variables that should be integral */
349  SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, NULL, NULL) );
350 
351  /* don't try to dive, if there are no fractional variables */
352  if( nlpcands == 0 )
353  return SCIP_OKAY;
354 
355  /* calculate the objective search bound */
356  if( SCIPgetNSolsFound(scip) == 0 )
357  {
358  if( heurdata->maxdiveubquotnosol > 0.0 )
359  searchubbound = SCIPgetLowerbound(scip)
360  + heurdata->maxdiveubquotnosol * (SCIPgetCutoffbound(scip) - SCIPgetLowerbound(scip));
361  else
362  searchubbound = SCIPinfinity(scip);
363  if( heurdata->maxdiveavgquotnosol > 0.0 )
364  searchavgbound = SCIPgetLowerbound(scip)
365  + heurdata->maxdiveavgquotnosol * (SCIPgetAvgLowerbound(scip) - SCIPgetLowerbound(scip));
366  else
367  searchavgbound = SCIPinfinity(scip);
368  }
369  else
370  {
371  if( heurdata->maxdiveubquot > 0.0 )
372  searchubbound = SCIPgetLowerbound(scip)
373  + heurdata->maxdiveubquot * (SCIPgetCutoffbound(scip) - SCIPgetLowerbound(scip));
374  else
375  searchubbound = SCIPinfinity(scip);
376  if( heurdata->maxdiveavgquot > 0.0 )
377  searchavgbound = SCIPgetLowerbound(scip)
378  + heurdata->maxdiveavgquot * (SCIPgetAvgLowerbound(scip) - SCIPgetLowerbound(scip));
379  else
380  searchavgbound = SCIPinfinity(scip);
381  }
382  searchbound = MIN(searchubbound, searchavgbound);
383  if( SCIPisObjIntegral(scip) )
384  searchbound = SCIPceil(scip, searchbound);
385 
386  /* calculate the maximal diving depth: 10 * min{number of integer variables, max depth} */
387  maxdivedepth = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip);
388  maxdivedepth = MIN(maxdivedepth, maxdepth);
389  maxdivedepth *= 10;
390 
391  *result = SCIP_DIDNOTFIND;
392 
393  /* start diving */
394  SCIP_CALL( SCIPstartProbing(scip) );
395 
396  /* enables collection of variable statistics during probing */
397  SCIPenableVarHistory(scip);
398 
399  /* get LP objective value */
400  lpsolstat = SCIP_LPSOLSTAT_OPTIMAL;
401  objval = SCIPgetLPObjval(scip);
402 
403  SCIPdebugMessage("(node %"SCIP_LONGINT_FORMAT") executing actconsdiving heuristic: depth=%d, %d fractionals, dualbound=%g, avgbound=%g, cutoffbound=%g, searchbound=%g\n",
404  SCIPgetNNodes(scip), SCIPgetDepth(scip), nlpcands, SCIPgetDualbound(scip), SCIPgetAvgDualbound(scip),
405  SCIPretransformObj(scip, SCIPgetCutoffbound(scip)), SCIPretransformObj(scip, searchbound));
406 
407  /* dive as long we are in the given objective, depth and iteration limits and fractional variables exist, but
408  * - if possible, we dive at least with the depth 10
409  * - if the number of fractional variables decreased at least with 1 variable per 2 dive depths, we continue diving
410  */
411  lperror = FALSE;
412  cutoff = FALSE;
413  divedepth = 0;
414  bestcandmayrounddown = FALSE;
415  bestcandmayroundup = FALSE;
416  startnlpcands = nlpcands;
417  while( !lperror && !cutoff && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL && nlpcands > 0
418  && (divedepth < 10
419  || nlpcands <= startnlpcands - divedepth/2
420  || (divedepth < maxdivedepth && heurdata->nlpiterations < maxnlpiterations && objval < searchbound))
421  && !SCIPisStopped(scip) )
422  {
423  divedepth++;
424  SCIP_CALL( SCIPnewProbingNode(scip) );
425 
426  /* choose variable fixing:
427  * - prefer variables that may not be rounded without destroying LP feasibility:
428  * - of these variables, round variable with least number of locks in corresponding direction
429  * - if all remaining fractional variables may be rounded without destroying LP feasibility:
430  * - round variable with least number of locks in opposite of its feasible rounding direction
431  */
432  bestcand = -1;
433  bestactscore = -1.0;
434  bestfrac = SCIP_INVALID;
435  bestcandmayrounddown = TRUE;
436  bestcandmayroundup = TRUE;
437  bestcandroundup = FALSE;
438  for( c = 0; c < nlpcands; ++c )
439  {
440  var = lpcands[c];
441  mayrounddown = SCIPvarMayRoundDown(var);
442  mayroundup = SCIPvarMayRoundUp(var);
443  frac = lpcandsfrac[c];
444  if( mayrounddown || mayroundup )
445  {
446  /* the candidate may be rounded: choose this candidate only, if the best candidate may also be rounded */
447  if( bestcandmayrounddown || bestcandmayroundup )
448  {
449  /* choose rounding direction:
450  * - if variable may be rounded in both directions, round corresponding to the fractionality
451  * - otherwise, round in the infeasible direction, because feasible direction is tried by rounding
452  * the current fractional solution
453  */
454  if( mayrounddown && mayroundup )
455  roundup = (frac > 0.5);
456  else
457  roundup = mayrounddown;
458 
459  if( roundup )
460  frac = 1.0 - frac;
461  actscore = getNActiveConsScore(scip, var, &downscore, &upscore);
462 
463  /* penalize too small fractions */
464  if( frac < 0.01 )
465  actscore *= 0.01;
466 
467  /* prefer decisions on binary variables */
468  if( !SCIPvarIsBinary(var) )
469  actscore *= 0.01;
470 
471  /* check, if candidate is new best candidate */
472  assert(0.0 < frac && frac < 1.0);
473  if( SCIPisGT(scip, actscore, bestactscore) || (SCIPisGE(scip, actscore, bestactscore) && frac < bestfrac) )
474  {
475  bestcand = c;
476  bestactscore = actscore;
477  bestfrac = frac;
478  bestcandmayrounddown = mayrounddown;
479  bestcandmayroundup = mayroundup;
480  bestcandroundup = roundup;
481  }
482  }
483  }
484  else
485  {
486  /* the candidate may not be rounded */
487  actscore = getNActiveConsScore(scip, var, &downscore, &upscore);
488  roundup = (downscore < upscore);
489  if( roundup )
490  frac = 1.0 - frac;
491 
492  /* penalize too small fractions */
493  if( frac < 0.01 )
494  actscore *= 0.01;
495 
496  /* prefer decisions on binary variables */
497  if( !SCIPvarIsBinary(var) )
498  actscore *= 0.01;
499 
500  /* check, if candidate is new best candidate: prefer unroundable candidates in any case */
501  assert(0.0 < frac && frac < 1.0);
502  if( bestcandmayrounddown || bestcandmayroundup || SCIPisGT(scip, actscore, bestactscore) ||
503  (SCIPisGE(scip, actscore, bestactscore) && frac < bestfrac) )
504  {
505  bestcand = c;
506  bestactscore = actscore;
507  bestfrac = frac;
508  bestcandmayrounddown = FALSE;
509  bestcandmayroundup = FALSE;
510  bestcandroundup = roundup;
511  }
512  assert(bestfrac < SCIP_INVALID);
513  }
514  }
515  assert(bestcand != -1);
516 
517  /* if all candidates are roundable, try to round the solution */
518  if( bestcandmayrounddown || bestcandmayroundup )
519  {
520  SCIP_Bool success;
521 
522  /* create solution from diving LP and try to round it */
523  SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) );
524  SCIP_CALL( SCIProundSol(scip, heurdata->sol, &success) );
525 
526  if( success )
527  {
528  SCIPdebugMessage("actconsdiving found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol));
529 
530  /* try to add solution to SCIP */
531  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) );
532 
533  /* check, if solution was feasible and good enough */
534  if( success )
535  {
536  SCIPdebugMessage(" -> solution was feasible and good enough\n");
537  *result = SCIP_FOUNDSOL;
538  }
539  }
540  }
541  assert(bestcand != -1);
542  var = lpcands[bestcand];
543 
544  backtracked = FALSE;
545  do
546  {
547  /* if the variable is already fixed or if the solution value is outside the domain, numerical troubles may have
548  * occured or variable was fixed by propagation while backtracking => Abort diving!
549  */
550  if( SCIPvarGetLbLocal(var) >= SCIPvarGetUbLocal(var) - 0.5 )
551  {
552  SCIPdebugMessage("Selected variable <%s> already fixed to [%g,%g] (solval: %.9f), diving aborted \n",
553  SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), lpcandssol[bestcand]);
554  cutoff = TRUE;
555  break;
556  }
557  if( SCIPisFeasLT(scip, lpcandssol[bestcand], SCIPvarGetLbLocal(var)) || SCIPisFeasGT(scip, lpcandssol[bestcand], SCIPvarGetUbLocal(var)) )
558  {
559  SCIPdebugMessage("selected variable's <%s> solution value is outside the domain [%g,%g] (solval: %.9f), diving aborted\n",
560  SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), lpcandssol[bestcand]);
561  assert(backtracked);
562  break;
563  }
564 
565  /* apply rounding of best candidate */
566  if( bestcandroundup == !backtracked )
567  {
568  /* round variable up */
569  SCIPdebugMessage(" dive %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT": var <%s>, round=%u/%u, sol=%g, oldbounds=[%g,%g], newbounds=[%g,%g]\n",
570  divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations,
571  SCIPvarGetName(var), bestcandmayrounddown, bestcandmayroundup,
572  lpcandssol[bestcand], SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var),
573  SCIPfeasCeil(scip, lpcandssol[bestcand]), SCIPvarGetUbLocal(var));
574  SCIP_CALL( SCIPchgVarLbProbing(scip, var, SCIPfeasCeil(scip, lpcandssol[bestcand])) );
575  }
576  else
577  {
578  /* round variable down */
579  SCIPdebugMessage(" dive %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT": var <%s>, round=%u/%u, sol=%g, oldbounds=[%g,%g], newbounds=[%g,%g]\n",
580  divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations,
581  SCIPvarGetName(var), bestcandmayrounddown, bestcandmayroundup,
582  lpcandssol[bestcand], SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var),
583  SCIPvarGetLbLocal(var), SCIPfeasFloor(scip, lpcandssol[bestcand]));
584  SCIP_CALL( SCIPchgVarUbProbing(scip, lpcands[bestcand], SCIPfeasFloor(scip, lpcandssol[bestcand])) );
585  }
586 
587  /* apply domain propagation */
588  SCIP_CALL( SCIPpropagateProbing(scip, 0, &cutoff, NULL) );
589  if( !cutoff )
590  {
591  /* resolve the diving LP */
592  /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic.
593  * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
594  */
595 #ifdef NDEBUG
596  SCIP_RETCODE retstat;
597  nlpiterations = SCIPgetNLPIterations(scip);
598  retstat = SCIPsolveProbingLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror, &cutoff);
599  if( retstat != SCIP_OKAY )
600  {
601  SCIPwarningMessage(scip, "Error while solving LP in Actconsdiving heuristic; LP solve terminated with code <%d>\n",retstat);
602  }
603 #else
604  nlpiterations = SCIPgetNLPIterations(scip);
605  SCIP_CALL( SCIPsolveProbingLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror, &cutoff) );
606 #endif
607 
608  if( lperror )
609  break;
610 
611  /* update iteration count */
612  heurdata->nlpiterations += SCIPgetNLPIterations(scip) - nlpiterations;
613 
614  /* get LP solution status, objective value, and fractional variables, that should be integral */
615  lpsolstat = SCIPgetLPSolstat(scip);
616  assert(cutoff || (lpsolstat != SCIP_LPSOLSTAT_OBJLIMIT && lpsolstat != SCIP_LPSOLSTAT_INFEASIBLE &&
617  (lpsolstat != SCIP_LPSOLSTAT_OPTIMAL || SCIPisLT(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)))));
618  }
619 
620  /* perform backtracking if a cutoff was detected */
621  if( cutoff && !backtracked && heurdata->backtrack )
622  {
623  SCIPdebugMessage(" *** cutoff detected at level %d - backtracking\n", SCIPgetProbingDepth(scip));
625  SCIP_CALL( SCIPnewProbingNode(scip) );
626  backtracked = TRUE;
627  }
628  else
629  backtracked = FALSE;
630  }
631  while( backtracked );
632 
633  if( !lperror && !cutoff && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL )
634  {
635  /* get new objective value */
636  oldobjval = objval;
637  objval = SCIPgetLPObjval(scip);
638 
639  /* update pseudo cost values */
640  if( SCIPisGT(scip, objval, oldobjval) )
641  {
642  if( bestcandroundup )
643  {
644  SCIP_CALL( SCIPupdateVarPseudocost(scip, lpcands[bestcand], 1.0-lpcandsfrac[bestcand],
645  objval - oldobjval, 1.0) );
646  }
647  else
648  {
649  SCIP_CALL( SCIPupdateVarPseudocost(scip, lpcands[bestcand], 0.0-lpcandsfrac[bestcand],
650  objval - oldobjval, 1.0) );
651  }
652  }
653 
654  /* get new fractional variables */
655  SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, NULL, NULL) );
656  }
657  SCIPdebugMessage(" -> lpsolstat=%d, objval=%g/%g, nfrac=%d\n", lpsolstat, objval, searchbound, nlpcands);
658  }
659 
660  /* check if a solution has been found */
661  if( nlpcands == 0 && !lperror && !cutoff && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL )
662  {
663  SCIP_Bool success;
664 
665  /* create solution from diving LP */
666  SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) );
667  SCIPdebugMessage("actconsdiving found primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol));
668 
669  /* try to add solution to SCIP */
670  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) );
671 
672  /* check, if solution was feasible and good enough */
673  if( success )
674  {
675  SCIPdebugMessage(" -> solution was feasible and good enough\n");
676  *result = SCIP_FOUNDSOL;
677  }
678  }
679 
680  /* end diving */
681  SCIP_CALL( SCIPendProbing(scip) );
682 
683  if( *result == SCIP_FOUNDSOL )
684  heurdata->nsuccess++;
685 
686  SCIPdebugMessage("(node %"SCIP_LONGINT_FORMAT") finished actconsdiving heuristic: %d fractionals, dive %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT", objval=%g/%g, lpsolstat=%d, cutoff=%u\n",
687  SCIPgetNNodes(scip), nlpcands, divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations,
688  SCIPretransformObj(scip, objval), SCIPretransformObj(scip, searchbound), lpsolstat, cutoff);
689 
690  return SCIP_OKAY;
691 }
692 
693 
694 /*
695  * heuristic specific interface methods
696  */
697 
698 /** creates the actconsdiving heuristic and includes it in SCIP */
700  SCIP* scip /**< SCIP data structure */
701  )
702 {
703  SCIP_HEURDATA* heurdata;
704  SCIP_HEUR* heur;
705 
706  /* create actconsdiving primal heuristic data */
707  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
708 
709  /* include primal heuristic */
710  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
712  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecActconsdiving, heurdata) );
713 
714  assert(heur != NULL);
715 
716  /* set non-NULL pointers to callback methods */
717  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyActconsdiving) );
718  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeActconsdiving) );
719  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitActconsdiving) );
720  SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitActconsdiving) );
721 
722  /* actconsdiving heuristic parameters */
724  "heuristics/actconsdiving/minreldepth",
725  "minimal relative depth to start diving",
726  &heurdata->minreldepth, TRUE, DEFAULT_MINRELDEPTH, 0.0, 1.0, NULL, NULL) );
728  "heuristics/actconsdiving/maxreldepth",
729  "maximal relative depth to start diving",
730  &heurdata->maxreldepth, TRUE, DEFAULT_MAXRELDEPTH, 0.0, 1.0, NULL, NULL) );
732  "heuristics/actconsdiving/maxlpiterquot",
733  "maximal fraction of diving LP iterations compared to node LP iterations",
734  &heurdata->maxlpiterquot, FALSE, DEFAULT_MAXLPITERQUOT, 0.0, SCIP_REAL_MAX, NULL, NULL) );
736  "heuristics/actconsdiving/maxlpiterofs",
737  "additional number of allowed LP iterations",
738  &heurdata->maxlpiterofs, FALSE, DEFAULT_MAXLPITEROFS, 0, INT_MAX, NULL, NULL) );
740  "heuristics/actconsdiving/maxdiveubquot",
741  "maximal quotient (curlowerbound - lowerbound)/(cutoffbound - lowerbound) where diving is performed (0.0: no limit)",
742  &heurdata->maxdiveubquot, TRUE, DEFAULT_MAXDIVEUBQUOT, 0.0, 1.0, NULL, NULL) );
744  "heuristics/actconsdiving/maxdiveavgquot",
745  "maximal quotient (curlowerbound - lowerbound)/(avglowerbound - lowerbound) where diving is performed (0.0: no limit)",
746  &heurdata->maxdiveavgquot, TRUE, DEFAULT_MAXDIVEAVGQUOT, 0.0, SCIP_REAL_MAX, NULL, NULL) );
748  "heuristics/actconsdiving/maxdiveubquotnosol",
749  "maximal UBQUOT when no solution was found yet (0.0: no limit)",
750  &heurdata->maxdiveubquotnosol, TRUE, DEFAULT_MAXDIVEUBQUOTNOSOL, 0.0, 1.0, NULL, NULL) );
752  "heuristics/actconsdiving/maxdiveavgquotnosol",
753  "maximal AVGQUOT when no solution was found yet (0.0: no limit)",
754  &heurdata->maxdiveavgquotnosol, TRUE, DEFAULT_MAXDIVEAVGQUOTNOSOL, 0.0, SCIP_REAL_MAX, NULL, NULL) );
756  "heuristics/actconsdiving/backtrack",
757  "use one level of backtracking if infeasibility is encountered?",
758  &heurdata->backtrack, FALSE, DEFAULT_BACKTRACK, NULL, NULL) );
759 
760  return SCIP_OKAY;
761 }
762 
763