Scippy

SCIP

Solving Constraint Integer Programs

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