Scippy

SCIP

Solving Constraint Integer Programs

heur_vbounds.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_vbounds.c
17  * @brief LNS heuristic uses the variable lower and upper bounds to determine the search neighborhood
18  * @author Timo Berthold
19  * @author Stefan Heinz
20  * @author Jens Schulz
21  */
22 
23 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
24 
25 #include <assert.h>
26 #include <string.h>
27 
28 #include "scip/scip.h"
29 #include "scip/scipdefplugins.h"
30 #include "scip/heur_vbounds.h"
31 
32 
33 #define HEUR_NAME "vbounds"
34 #define HEUR_DESC "LNS heuristic uses the variable lower and upper bounds to determine the search neighborhood"
35 #define HEUR_DISPCHAR 'V'
36 #define HEUR_PRIORITY -1106000
37 #define HEUR_FREQ -1
38 #define HEUR_FREQOFS 0
39 #define HEUR_MAXDEPTH -1
40 #define HEUR_TIMING SCIP_HEURTIMING_BEFORENODE
41 #define HEUR_USESSUBSCIP TRUE /**< does the heuristic use a secondary SCIP instance? */
42 
43 #define DEFAULT_MAXNODES 5000LL /* maximum number of nodes to regard in the subproblem */
44 #define DEFAULT_MINFIXINGRATE 0.5 /* minimum percentage of integer variables that have to be fixed */
45 #define DEFAULT_MINIMPROVE 0.01 /* factor by which vbounds heuristic should at least improve the incumbent */
46 #define DEFAULT_MINNODES 500LL /* minimum number of nodes to regard in the subproblem */
47 #define DEFAULT_NODESOFS 500LL /* number of nodes added to the contingent of the total nodes */
48 #define DEFAULT_NODESQUOT 0.1 /* subproblem nodes in relation to nodes of the original problem */
49 #define DEFAULT_MAXPROPROUNDS 2 /* maximum number of propagation rounds during probing */
50 #define DEFAULT_COPYCUTS TRUE /**< should all active cuts from the cutpool of the original scip be copied to
51  * constraints of the subscip
52  */
53 
54 
55 /*
56  * Data structures
57  */
58 
59 /** primal heuristic data */
60 struct SCIP_HeurData
61 {
62  SCIP_VAR** lbvars; /**< topological sorted variables with respect to the variable lower bound */
63  SCIP_VAR** ubvars; /**< topological sorted variables with respect to the variable upper bound */
64  SCIP_VAR** impvars; /**< topological sorted variables with respect to the variable lower and upper
65  * bound and with a corresponding improving objective coefficient */
66  int nlbvars; /**< number of variables in variable lower bound array */
67  int nubvars; /**< number of variables in variable upper bound array */
68  int nlbimpvars; /**< number of variables in variable improving lower bound array */
69  int nubimpvars; /**< number of variables in variable improving upper bound array */
70 
71  SCIP_Longint maxnodes; /**< maximum number of nodes to regard in the subproblem */
72  SCIP_Longint minnodes; /**< minimum number of nodes to regard in the subproblem */
73  SCIP_Longint nodesofs; /**< number of nodes added to the contingent of the total nodes */
74  SCIP_Longint usednodes; /**< nodes already used by vbounds heuristic in earlier calls */
75  SCIP_Real minfixingrate; /**< minimum percentage of integer variables that have to be fixed */
76  SCIP_Real minimprove; /**< factor by which vbounds heuristic should at least improve the incumbent */
77  SCIP_Real nodesquot; /**< subproblem nodes in relation to nodes of the original problem */
78  int maxproprounds; /**< maximum number of propagation rounds during probing */
79  SCIP_Bool initialized; /**< are the candidate list initialized? */
80  SCIP_Bool applicable; /**< is the heuristic applicable? */
81  SCIP_Bool copycuts; /**< should all active cuts from cutpool be copied to constraints in
82  * subproblem?
83  */
84 };
85 
86 /*
87  * Hash map callback methods
88  */
89 
90 /** hash key retrieval function for variables */
91 static
92 SCIP_DECL_HASHGETKEY(hashGetKeyVar)
93 { /*lint --e{715}*/
94  return elem;
95 }
96 
97 /** returns TRUE iff the indices of both variables are equal */
98 static
99 SCIP_DECL_HASHKEYEQ(hashKeyEqVar)
100 { /*lint --e{715}*/
101  if( key1 == key2 )
102  return TRUE;
103  return FALSE;
104 }
105 
106 /** returns the hash value of the key */
107 static
108 SCIP_DECL_HASHKEYVAL(hashKeyValVar)
109 { /*lint --e{715}*/
110  assert( SCIPvarGetIndex((SCIP_VAR*) key) >= 0 );
111  return (unsigned int) SCIPvarGetIndex((SCIP_VAR*) key);
112 }
113 
114 
115 /*
116  * Local methods
117  */
118 
119 /** reset heuristic data structure */
120 static
122  SCIP_HEURDATA* heurdata /**< structure containing heurdata */
123  )
124 {
125  heurdata->lbvars = NULL;
126  heurdata->ubvars = NULL;
127  heurdata->impvars = NULL;
128  heurdata->nlbvars = 0;
129  heurdata->nubvars = 0;
130  heurdata->nlbimpvars = 0;
131  heurdata->nubimpvars = 0;
132  heurdata->initialized = FALSE;
133  heurdata->applicable = FALSE;
134 }
135 
136 /** gets the requested variables bounds */
137 static
139  SCIP_VAR* var, /**< variable to get the variable bounds from */
140  SCIP_VAR*** vbvars, /**< pointer to store the variable bound array */
141  int* nvbvars, /**< pointer to store the number of variable bounds */
142  SCIP_Bool lowerbound /**< variable lower bounds? (otherwise variable upper bound) */
143  )
144 {
145  if( lowerbound )
146  {
147  /* get variable lower bounds */
148  (*vbvars) = SCIPvarGetVlbVars(var);
149  (*nvbvars) = SCIPvarGetNVlbs(var);
150  }
151  else
152  {
153  /* get variable upper bounds */
154  (*vbvars) = SCIPvarGetVubVars(var);
155  (*nvbvars) = SCIPvarGetNVubs(var);
156  }
157 }
158 
159 /** perform depth-first-search from the given variable using the variable lower or upper bounds of the variable */
160 static
162  SCIP* scip, /**< SCIP data structure */
163  SCIP_VAR* var, /**< variable to start the depth-first-search */
164  SCIP_HASHMAP* varPosMap, /**< mapping a variable to its position in the (used) variable array, or NULL */
165  SCIP_VAR** usedvars, /**< array of variables which are involved in the propagation, or NULL */
166  int* nusedvars, /**< number of variables which are involved in the propagation, or NULL */
167  SCIP_HASHTABLE* connected, /**< hash table storing if a node was already visited */
168  SCIP_VAR** sortedvars, /**< array that will contain the topological sorted variables */
169  int* nsortedvars, /**< pointer to store the number of already collects variables in the sorted variables array */
170  SCIP_Bool lowerbound /**< depth-first-search with respect to the variable lower bounds, otherwise variable upper bound */
171  )
172 {
173  SCIP_VAR** vbvars;
174  SCIP_VAR* vbvar;
175  SCIP_Real scalar;
176  SCIP_Real constant;
177  int nvbvars;
178  int v;
179 
180  assert(scip != NULL);
181  assert(var != NULL);
182  assert(varPosMap == NULL || (varPosMap != NULL && usedvars != NULL && nusedvars != NULL));
183  assert(sortedvars != NULL);
184  assert(nsortedvars != NULL);
185  assert(*nsortedvars >= 0);
186  assert(SCIPvarGetProbindex(var) > -1);
187  assert(SCIPhashtableExists(connected, var));
188 
189  /* mark variable as visited, remove variable from hash table */
190  SCIP_CALL( SCIPhashtableRemove(connected, var) );
191 
192  /* get variable bounds */
193  getVariableBounds(var, &vbvars, &nvbvars, lowerbound);
194 
195  SCIPdebugMessage("variable <%s> has %d variable %s bounds\n", SCIPvarGetName(var), nvbvars,
196  lowerbound ? "lower" : "upper");
197 
198  for( v = 0; v < nvbvars; ++v )
199  {
200  vbvar = vbvars[v];
201  assert(vbvar != NULL);
202 
203  scalar = 1.0;
204  constant = 0.0;
205 
206  /* transform variable bound variable to an active variable if possible */
207  SCIP_CALL( SCIPgetProbvarSum(scip, &vbvar, &scalar, &constant) );
208 
209  /* we could not resolve the variable bound variable to one active variable, therefore, ignore this variable bound */
210  if( !SCIPvarIsActive(vbvar) )
211  continue;
212 
213  /* insert variable bound variable into the hash table since they are involved in later propagation */
214  if( varPosMap != NULL && !SCIPhashmapExists(varPosMap, vbvar) )
215  {
216  SCIPdebugMessage("insert variable <%s> with position %d into the hash map\n", SCIPvarGetName(vbvar), *nusedvars);
217  SCIP_CALL( SCIPhashmapInsert(varPosMap, vbvar, (void*)(size_t)(*nusedvars)) );
218  usedvars[*nusedvars] = vbvar;
219  (*nusedvars)++;
220  }
221 
222  /* check if the variable bound variable was already visited */
223  if( SCIPhashtableExists(connected, vbvar) )
224  {
225  /* recursively call depth-first-search */
226  SCIP_CALL( depthFirstSearch(scip, vbvar, varPosMap, usedvars, nusedvars, connected, sortedvars, nsortedvars, lowerbound) );
227  }
228  }
229 
230  /* store variable in the sorted variable array */
231  sortedvars[(*nsortedvars)] = var;
232  (*nsortedvars)++;
233 
234  /* insert variable bound variable into the hash table since they are involve in the later propagation */
235  if( varPosMap != NULL && !SCIPhashmapExists(varPosMap, var) )
236  {
237  SCIPdebugMessage("insert variable <%s> with position %d into the hash map\n", SCIPvarGetName(var), *nusedvars);
238  SCIP_CALL( SCIPhashmapInsert(varPosMap, var, (void*) (size_t)(*nusedvars)) );
239  usedvars[*nusedvars] = var;
240  (*nusedvars)++;
241  }
242 
243  return SCIP_OKAY;
244 }
245 
246 /** create a topological sorted variable array of the given variables and stores if (needed) the involved variables into
247  * the corresponding variable array and hash map
248  *
249  * @note: for all arrays and the hash map (if requested) you need to allocate enough memory before calling this method
250  */
251 static
253  SCIP* scip, /**< SCIP data structure */
254  SCIP_VAR** vars, /**< variable which we want sort */
255  int nvars, /**< number of variables */
256  SCIP_HASHMAP* varPosMap, /**< mapping a variable to its position in the (used) variable array, or NULL */
257  SCIP_VAR** usedvars, /**< array of variables which are involved in the propagation, or NULL */
258  int* nusedvars, /**< number of variables which are involved in the propagation, or NULL */
259  SCIP_VAR** topovars, /**< array where the topological sorted variables are stored */
260  int* ntopovars, /**< pointer to store the number of topological sorted variables */
261  SCIP_Bool lowerbound /**< topological sorted with respect to the variable lower bounds, otherwise variable upper bound */
262  )
263 {
264  SCIP_VAR** sortedvars;
265  SCIP_VAR** vbvars;
266  SCIP_VAR* var;
267  SCIP_HASHTABLE* connected;
268  int nvbvars;
269  int hashsize;
270  int i;
271  int v;
272 
273  assert(scip != NULL);
274  assert(vars != NULL || nvars == 0);
275  assert(varPosMap == NULL || (varPosMap != NULL && usedvars != NULL && nusedvars != NULL));
276  assert(topovars != NULL);
277  assert(ntopovars != NULL);
278 
279  SCIPdebugMessage("create topological sorted variable array with respect to variables %s bounds\n",
280  lowerbound ? "lower" : "upper");
281 
282  if( nvars == 0 )
283  return SCIP_OKAY;
284 
285  assert(vars != NULL);
286 
287  /* allocate buffer array */
288  SCIP_CALL( SCIPallocBufferArray(scip, &sortedvars, nvars) );
289 
290  hashsize = SCIPcalcHashtableSize(5 * nvars);
291 
292  /* create hash table for variables which are (still) connected */
293  SCIP_CALL( SCIPhashtableCreate(&connected, SCIPblkmem(scip), hashsize, SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) );
294 
295  /* detect isolated variables; mark all variables which have at least one entering or leaving arc as connected */
296  for( v = 0; v < nvars; ++v )
297  {
298  var = vars[v];
299  assert(var != NULL);
300 
301  if( !SCIPvarIsActive(var) )
302  continue;
303 
304  /* get variable bounds */
305  getVariableBounds(var, &vbvars, &nvbvars, lowerbound);
306 
307  if( nvbvars > 0 && !SCIPhashtableExists(connected, var) )
308  {
309  SCIP_CALL( SCIPhashtableInsert(connected, var) );
310  }
311 
312  for( i = 0; i < nvbvars; ++i )
313  {
314  if( !SCIPvarIsActive(vbvars[i]) )
315  continue;
316 
317  /* there is a leaving arc, hence, the variable/node is connected */
318  assert(vbvars[i] != NULL);
319  if( !SCIPhashtableExists(connected, vbvars[i]) )
320  {
321  SCIP_CALL( SCIPhashtableInsert(connected, vbvars[i]) );
322  }
323  }
324  }
325 
326  /* loop over all "connected" variable and find for each connected component a "almost" topological sorted version */
327  for( v = 0; v < nvars; ++v )
328  {
329  if( SCIPhashtableExists(connected, vars[v]) )
330  {
331  int nsortedvars;
332 
333  SCIPdebugMessage("start depth-first-search with variable <%s>\n", SCIPvarGetName(vars[v]));
334 
335  /* use depth first search to get a "almost" topological sorted variables for the connected component which
336  * includes vars[v]
337  */
338  nsortedvars = 0;
339  SCIP_CALL( depthFirstSearch(scip, vars[v], varPosMap, usedvars, nusedvars, connected, sortedvars, &nsortedvars, lowerbound) );
340 
341  SCIPdebugMessage("detected connected component of size <%d>\n", nsortedvars);
342 
343  /* copy variables */
344  for( i = 0; i < nsortedvars; ++i )
345  {
346  topovars[(*ntopovars)] = sortedvars[i];
347  (*ntopovars)++;
348  }
349  }
350  }
351 
352  assert(*ntopovars <= nvars);
353  SCIPdebugMessage("topological sorted array contains %d of %d variables (variable %s bound)\n",
354  *ntopovars, nvars, lowerbound ? "lower" : "upper");
355 
356  /* free hash table */
357  SCIPhashtableFree(&connected);
358 
359  /* free buffer memory */
360  SCIPfreeBufferArray(scip, &sortedvars);
361 
362  return SCIP_OKAY;
363 }
364 
365 /** initialize candidate lists */
366 static
368  SCIP* scip, /**< original SCIP data structure */
369  SCIP_HEURDATA* heurdata /**< structure containing heurdata */
370  )
371 {
372  SCIP_VAR** allvars;
373  SCIP_VAR** vars;
374  SCIP_VAR** lbvars;
375  SCIP_VAR** ubvars;
376  SCIP_VAR** impvars;
377  SCIP_HASHTABLE* collectedvars;
378  int nallvars;
379  int nvars;
380  int nlbvars;
381  int nubvars;
382  int nlbimpvars;
383  int nubimpvars;
384  int v;
385 
386  SCIPdebugMessage("initialize variable bound heuristic (%s)\n", SCIPgetProbName(scip));
387 
388  allvars = SCIPgetVars(scip);
389  nallvars = SCIPgetNVars(scip);
390  nvars = 0;
391  nlbvars = 0;
392  nubvars = 0;
393  nlbimpvars = 0;
394  nubimpvars = 0;
395 
396  /* allocate memory for the arrays of the heurdata */
397  SCIP_CALL( SCIPallocBufferArray(scip, &vars, nallvars) );
398  SCIP_CALL( SCIPallocBufferArray(scip, &lbvars, nallvars) );
399  SCIP_CALL( SCIPallocBufferArray(scip, &ubvars, nallvars) );
400  SCIP_CALL( SCIPallocBufferArray(scip, &impvars, nallvars) );
401 
402  /* create the topological sorted variable array with respect to the variable lower bounds */
403  SCIP_CALL( createTopoSortedVars(scip, allvars, nallvars, NULL, vars, &nvars, lbvars, &nlbvars, TRUE) );
404 
405  /* create the topological sorted variable array with respect to the variable upper bounds */
406  SCIP_CALL( createTopoSortedVars(scip, allvars, nallvars, NULL, vars, &nvars, ubvars, &nubvars, FALSE) );
407 
408  /* create hash table for variables which are already collected */
409  SCIP_CALL( SCIPhashtableCreate(&collectedvars, SCIPblkmem(scip), SCIPcalcHashtableSize(nallvars), hashGetKeyVar, hashKeyEqVar, hashKeyValVar, NULL) );
410 
411  /* collect variables which improve the objective by fixing them to suggested bound */
412  for( v = 0; v < nlbvars; ++v )
413  {
414  if( SCIPisGE(scip, SCIPvarGetObj(lbvars[v]), 0.0) )
415  {
416  SCIP_CALL( SCIPhashtableInsert(collectedvars, lbvars[v]) );
417  assert(nlbimpvars < nallvars);
418  impvars[nlbimpvars] = lbvars[v];
419  nlbimpvars++;
420  }
421  }
422 
423  for( v = 0; v < nubvars; ++v )
424  {
425  if( SCIPisLE(scip, SCIPvarGetObj(ubvars[v]), 0.0) && !SCIPhashtableExists(collectedvars, ubvars[v]) )
426  {
427  assert(nlbimpvars + nubimpvars < nallvars);
428  impvars[nlbimpvars + nubimpvars] = ubvars[v];
429  nubimpvars++;
430  }
431  }
432 
433  /* free hash table */
434  SCIPhashtableFree(&collectedvars);
435 
436  /* check if the candidate lists contain enough candidates */
437  if( nlbvars >= heurdata->minfixingrate * nallvars )
438  {
439  SCIP_CALL( SCIPduplicateMemoryArray(scip, &heurdata->lbvars, lbvars, nlbvars) );
440  heurdata->nlbvars = nlbvars;
441  heurdata->applicable = TRUE;
442 
443  /* capture variable candidate list */
444  for( v = 0; v < nlbvars; ++v )
445  {
446  SCIP_CALL( SCIPcaptureVar(scip, heurdata->lbvars[v]) );
447  }
448  }
449  if( nubvars >= heurdata->minfixingrate * nallvars )
450  {
451  SCIP_CALL( SCIPduplicateMemoryArray(scip, &heurdata->ubvars, ubvars, nubvars) );
452  heurdata->nubvars = nubvars;
453  heurdata->applicable = TRUE;
454 
455  /* capture variable candidate list */
456  for( v = 0; v < nubvars; ++v )
457  {
458  SCIP_CALL( SCIPcaptureVar(scip, heurdata->ubvars[v]) );
459  }
460  }
461  if( nlbvars > nlbimpvars && nubvars > nubimpvars && nlbimpvars + nubimpvars >= heurdata->minfixingrate * nallvars )
462  {
463  assert(nlbimpvars < INT_MAX - nubimpvars);
464  SCIP_CALL( SCIPduplicateMemoryArray(scip, &heurdata->impvars, impvars, nlbimpvars + nubimpvars) );
465  heurdata->nlbimpvars = nlbimpvars;
466  heurdata->nubimpvars = nubimpvars;
467  heurdata->applicable = TRUE;
468 
469  /* capture variable candidate list */
470  for( v = 0; v < nlbimpvars + nubimpvars; ++v )
471  {
472  SCIP_CALL( SCIPcaptureVar(scip, heurdata->impvars[v]) );
473  }
474  }
475 
476  /* free buffer arrays */
477  SCIPfreeBufferArray(scip, &impvars);
478  SCIPfreeBufferArray(scip, &ubvars);
479  SCIPfreeBufferArray(scip, &lbvars);
480  SCIPfreeBufferArray(scip, &vars);
481 
482  /* initialize data */
483  heurdata->usednodes = 0;
484  heurdata->initialized = TRUE;
485 
486  SCIPstatisticMessage("lbvars %.3g, ubvars %.3g, impvars %.3g (%s)\n",
487  (nlbvars * 100.0) / nallvars, (nubvars * 100.0) / nallvars,
488  ((nlbimpvars + nubimpvars) * 100.0) / nallvars, SCIPgetProbName(scip));
489 
490  return SCIP_OKAY;
491 }
492 
493 /** apply variable bound fixing during probing */
494 static
496  SCIP* scip, /**< original SCIP data structure */
497  SCIP_HEURDATA* heurdata, /**< structure containing heurdata */
498  SCIP_VAR** vars, /**< variables to fix during probing */
499  int nlbvars, /**< number of variables to use the lower bound */
500  int nubvars, /**< number of variables to use the upper bound */
501  SCIP_SOL* sol, /**< working solution */
502  SCIP_Bool* infeasible, /**< pointer to store whether problem is infeasible */
503  SCIP_RESULT* result /**< pointer to store the result (solution found) */
504  )
505 {
506  SCIP_Bool success;
507  int v;
508 
509  /* for each variable in topological order: start at best bound (MINIMIZE: neg coeff --> ub, pos coeff: lb) */
510  for( v = 0; v < nlbvars + nubvars && !(*infeasible); ++v )
511  {
512  /* only check integer or binary variables */
513  if( SCIPvarGetType(vars[v]) == SCIP_VARTYPE_CONTINUOUS )
514  continue;
515 
516  /* skip variables which are already fixed */
517  if( SCIPvarGetLbLocal(vars[v]) + 0.5 > SCIPvarGetUbLocal(vars[v]) )
518  continue;
519 
520  if( v < nlbvars )
521  {
522  /* fix variable to lower bound */
523  SCIP_CALL( SCIPfixVarProbing(scip, vars[v], SCIPvarGetLbLocal(vars[v])) );
524  SCIPdebugMessage("fixing %d: variable <%s> to lower bound <%g> (%d pseudo cands)\n",
525  v, SCIPvarGetName(vars[v]), SCIPvarGetLbLocal(vars[v]), SCIPgetNPseudoBranchCands(scip));
526  }
527  else
528  {
529  /* fix variable to lower bound */
530  SCIP_CALL( SCIPfixVarProbing(scip, vars[v], SCIPvarGetUbLocal(vars[v])) );
531  SCIPdebugMessage("fixing %d: variable <%s> to upper bound <%g> (%d pseudo cands)\n",
532  v, SCIPvarGetName(vars[v]), SCIPvarGetUbLocal(vars[v]), SCIPgetNPseudoBranchCands(scip));
533  }
534 
535  /* check if problem is already infeasible */
536  SCIP_CALL( SCIPpropagateProbing(scip, heurdata->maxproprounds, infeasible, NULL) );
537 
538  if( !(*infeasible) )
539  {
540  /* create solution from probing run and try to round it */
541  SCIP_CALL( SCIPlinkCurrentSol(scip, sol) );
542  SCIP_CALL( SCIProundSol(scip, sol, &success) );
543 
544  if( success )
545  {
546  SCIPdebugMessage("vbound heuristic found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, sol));
547 
548  /* try to add solution to SCIP */
549  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &success) );
550 
551  /* check, if solution was feasible and good enough */
552  if( success )
553  {
554  SCIPdebugMessage(" -> solution was feasible and good enough\n");
555  *result = SCIP_FOUNDSOL;
556 
557  if( SCIPisStopped(scip) )
558  break;
559  }
560  }
561  }
562  }
563 
564  SCIPdebugMessage("probing ended with %sfeasible problem\n", (*infeasible) ? "in" : "");
565 
566  return SCIP_OKAY;
567 }
568 
569 /** creates a new solution for the original problem by copying the solution of the subproblem */
570 static
572  SCIP* scip, /**< original SCIP data structure */
573  SCIP* subscip, /**< SCIP structure of the subproblem */
574  SCIP_VAR** subvars, /**< the variables of the subproblem */
575  SCIP_SOL* newsol, /**< working solution */
576  SCIP_SOL* subsol, /**< solution of the subproblem */
577  SCIP_Bool* success /**< used to store whether new solution was found or not */
578  )
579 {
580  SCIP_VAR** vars; /* the original problem's variables */
581  int nvars;
582  SCIP_Real* subsolvals; /* solution values of the subproblem */
583 
584  assert( scip != NULL );
585  assert( subscip != NULL );
586  assert( subvars != NULL );
587  assert( subsol != NULL );
588 
589  /* get variables' data */
590  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
591 
592  /* sub-SCIP may have more variables than the number of active (transformed) variables in the main SCIP
593  * since constraint copying may have required the copy of variables that are fixed in the main SCIP
594  */
595  assert( nvars <= SCIPgetNOrigVars(subscip) );
596 
597  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
598 
599  /* copy the solution */
600  SCIP_CALL( SCIPgetSolVals(subscip, subsol, nvars, subvars, subsolvals) );
601 
602  SCIP_CALL( SCIPsetSolVals(scip, newsol, nvars, vars, subsolvals) );
603 
604  /* try to add new solution to scip and free it immediately */
605  SCIP_CALL( SCIPtrySol(scip, newsol, FALSE, TRUE, TRUE, TRUE, success) );
606 
607  SCIPfreeBufferArray(scip, &subsolvals);
608 
609  return SCIP_OKAY;
610 }
611 
612 /** main procedure of the vbounds heuristic */
613 static
615  SCIP* scip, /**< original SCIP data structure */
616  SCIP_HEUR* heur, /**< heuristic */
617  SCIP_HEURDATA* heurdata, /**< heuristic data structure */
618  SCIP_VAR** probvars, /**< variables to fix during probing */
619  int nlbvars, /**< number of variables to use the lower bound */
620  int nubvars, /**< number of variables to use the upper bound */
621  SCIP_RESULT* result /**< pointer to store the result */
622  )
623 {
624  SCIP_VAR** vars;
625  SCIP_SOL* newsol;
626  SCIP_Real timelimit; /* timelimit for the subproblem */
627  SCIP_Real memorylimit;
628  SCIP_Longint nstallnodes; /* number of stalling nodes for the subproblem */
629  SCIP_Bool infeasible;
630  int nvars;
631 
632  assert(heur != NULL);
633  assert(heurdata != NULL);
634 
635  /* initialize default values */
636  infeasible = FALSE;
637 
638  /* get variable data of original problem */
639  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
640 
641  if( nlbvars + nubvars < nvars * heurdata->minfixingrate )
642  return SCIP_OKAY;
643 
644  assert(nlbvars + nubvars > 0);
645 
646  /* calculate the maximal number of branching nodes until heuristic is aborted */
647  nstallnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip));
648 
649  /* reward variable bounds heuristic if it succeeded often */
650  nstallnodes = (SCIP_Longint)(nstallnodes * 3.0 * (SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur) + 1.0));
651  nstallnodes -= 100 * SCIPheurGetNCalls(heur); /* count the setup costs for the sub-MIP as 100 nodes */
652  nstallnodes += heurdata->nodesofs;
653 
654  /* determine the node limit for the current process */
655  nstallnodes -= heurdata->usednodes;
656  nstallnodes = MIN(nstallnodes, heurdata->maxnodes);
657 
658  /* check whether we have enough nodes left to call subproblem solving */
659  if( nstallnodes < heurdata->minnodes )
660  {
661  SCIPdebugMessage("skipping "HEUR_NAME": nstallnodes=%"SCIP_LONGINT_FORMAT", minnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, heurdata->minnodes);
662  return SCIP_OKAY;
663  }
664 
665  if( SCIPisStopped(scip) )
666  return SCIP_OKAY;
667 
668  SCIPdebugMessage("apply variable bounds heuristic at node %lld on %d variable lower bound and %d variable upper bounds\n",
669  SCIPnodeGetNumber(SCIPgetCurrentNode(scip)), nlbvars, nubvars);
670 
671  /* start probing */
672  SCIP_CALL( SCIPstartProbing(scip) );
673 
674  /* create temporary solution */
675  SCIP_CALL( SCIPcreateSol(scip, &newsol, heur) );
676 
677  /* apply the variable fixings */
678  SCIP_CALL( applyVboundsFixings(scip, heurdata, probvars, nlbvars, nubvars, newsol, &infeasible, result) );
679 
680  /* if a solution has been found --> fix all other variables by subscip if necessary */
681  if( !infeasible )
682  {
683  SCIP* subscip;
684  SCIP_VAR** subvars;
685  SCIP_HASHMAP* varmap;
686  SCIP_Bool valid;
687  int i;
688 
689  valid = FALSE;
690 
691  /* create subproblem */
692  SCIP_CALL( SCIPcreate(&subscip) );
693 
694  /* create the variable mapping hash map */
695  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) );
696 
697  SCIP_CALL( SCIPcopy(scip, subscip, varmap, NULL, "_vbounds", FALSE, FALSE, TRUE, &valid) );
698 
699  if( heurdata->copycuts )
700  {
701  /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */
702  SCIP_CALL( SCIPcopyCuts(scip, subscip, varmap, NULL, FALSE, NULL) );
703  }
704 
705  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
706 
707  for( i = 0; i < nvars; i++ )
708  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmap, vars[i]);
709 
710  /* free hash map */
711  SCIPhashmapFree(&varmap);
712 
713  /* do not abort subproblem on CTRL-C */
714  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
715 
716  /* disable output to console */
717  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
718 
719  /* check whether there is enough time and memory left */
720  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
721  if( !SCIPisInfinity(scip, timelimit) )
722  timelimit -= SCIPgetSolvingTime(scip);
723  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
724 
725  /* substract the memory already used by the main SCIP and the estimated memory usage of external software */
726  if( !SCIPisInfinity(scip, memorylimit) )
727  {
728  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
729  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
730  }
731 
732  /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */
733  if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 )
734  {
735  /* free subproblem */
736  SCIPfreeBufferArray(scip, &subvars);
737  SCIP_CALL( SCIPfree(&subscip) );
738 
739  goto TERMINATE;
740  }
741 
742  /* set limits for the subproblem */
743  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", nstallnodes) );
744  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", heurdata->maxnodes) );
745  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
746  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
747 
748  /* forbid call of heuristics and separators solving sub-CIPs */
749  SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) );
750 
751  /* disable cutting plane separation */
753 
754  /* disable expensive presolving */
756 
757 #ifdef SCIP_DEBUG
758  /* for debugging vbounds heuristic, enable MIP output */
759  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) );
760  SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 100000000) );
761 #endif
762 
763  /* employ a limit on the number of enforcement rounds in the quadratic constraint handlers; this fixes the issue that
764  * sometimes the quadratic constraint handler needs hundreds or thousands of enforcement rounds to determine the
765  * feasibility status of a single node without fractional branching candidates by separation (namely for uflquad
766  * instances); however, the solution status of the sub-SCIP might get corrupted by this; hence no decutions shall be
767  * made for the original SCIP
768  */
769  if( SCIPfindConshdlr(subscip, "quadratic") != NULL && !SCIPisParamFixed(subscip, "constraints/quadratic/enfolplimit") )
770  {
771  SCIP_CALL( SCIPsetIntParam(subscip, "constraints/quadratic/enfolplimit", 10) );
772  }
773 
774  /* if there is already a solution, add an objective cutoff */
775  if( SCIPgetNSols(scip) > 0 )
776  {
777  SCIP_Real upperbound;
778  SCIP_Real minimprove;
779  SCIP_Real cutoff;
780 
781  minimprove = heurdata->minimprove;
782  cutoff = SCIPinfinity(scip);
783  assert( !SCIPisInfinity(scip,SCIPgetUpperbound(scip)) );
784 
785  upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip);
786 
787  if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) )
788  {
789  cutoff = (1-minimprove)*SCIPgetUpperbound(scip) + minimprove*SCIPgetLowerbound(scip);
790  }
791  else
792  {
793  if( SCIPgetUpperbound ( scip ) >= 0 )
794  cutoff = ( 1 - minimprove ) * SCIPgetUpperbound ( scip );
795  else
796  cutoff = ( 1 + minimprove ) * SCIPgetUpperbound ( scip );
797  }
798  cutoff = MIN(upperbound, cutoff);
799  SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) );
800  }
801 
802  /* solve the subproblem */
803  /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic.
804  * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
805  */
806 #ifdef NDEBUG
807  {
808  SCIP_RETCODE retstat;
809  retstat = SCIPpresolve(subscip);
810  if( retstat != SCIP_OKAY )
811  {
812  SCIPwarningMessage(scip, "Error while presolving subMIP in vbounds heuristic; sub-SCIP terminated with code <%d>\n", retstat);
813  }
814  }
815 #else
816  SCIP_CALL( SCIPpresolve(subscip) );
817 #endif
818 
819  SCIPdebugMessage("vbounds heuristic presolved subproblem: %d vars, %d cons\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip));
820 
821  /* after presolving, we should have at least reached a certain fixing rate over ALL variables (including continuous)
822  * to ensure that not only the MIP but also the LP relaxation is easy enough
823  */
824  if( ( nvars - SCIPgetNVars(subscip) ) / (SCIP_Real)nvars >= heurdata->minfixingrate / 2.0 )
825  {
826  SCIP_SOL** subsols;
827  SCIP_Bool success;
828  int nsubsols;
829 
830  SCIPdebugMessage("solving subproblem: nstallnodes=%"SCIP_LONGINT_FORMAT", maxnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, heurdata->maxnodes);
831 
832 #ifdef NDEBUG
833  {
834  SCIP_RETCODE retstat;
835  retstat = SCIPsolve(subscip);
836  if( retstat != SCIP_OKAY )
837  {
838  SCIPwarningMessage(scip, "Error while solving subMIP in vbounds heuristic; sub-SCIP terminated with code <%d>\n",retstat);
839  }
840  }
841 #else
842  SCIP_CALL( SCIPsolve(subscip) );
843 #endif
844 
845  /* check, whether a solution was found; due to numerics, it might happen that not all solutions are feasible ->
846  * try all solutions until one was accepted
847  */
848  nsubsols = SCIPgetNSols(subscip);
849  subsols = SCIPgetSols(subscip);
850  success = FALSE;
851 
852  for( i = 0; i < nsubsols && !success; ++i )
853  {
854  SCIP_CALL( createNewSol(scip, subscip, subvars, newsol, subsols[i], &success) );
855  }
856  if( success )
857  *result = SCIP_FOUNDSOL;
858  }
859 
860 #ifdef SCIP_DEBUG
861  SCIP_CALL( SCIPprintStatistics(subscip, NULL) );
862 #endif
863 
864  /* free subproblem */
865  SCIPfreeBufferArray(scip, &subvars);
866  SCIP_CALL( SCIPfree(&subscip) );
867  }
868 
869  TERMINATE:
870  /* free solution */
871  SCIP_CALL( SCIPfreeSol(scip, &newsol) );
872 
873  /* exit probing mode */
874  SCIP_CALL( SCIPendProbing(scip) );
875 
876  return SCIP_OKAY;
877 }
878 
879 
880 /*
881  * Callback methods of primal heuristic
882  */
883 
884 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
885 static
886 SCIP_DECL_HEURCOPY(heurCopyVbounds)
887 { /*lint --e{715}*/
888  assert(scip != NULL);
889  assert(heur != NULL);
890  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
891 
892  /* call inclusion method of heuristic */
894 
895  return SCIP_OKAY;
896 }
897 
898 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
899 static
900 SCIP_DECL_HEURFREE(heurFreeVbounds)
901 { /*lint --e{715}*/
902  SCIP_HEURDATA* heurdata;
903 
904  /* free heuristic data */
905  heurdata = SCIPheurGetData(heur);
906 
907  SCIPfreeMemory(scip, &heurdata);
908  SCIPheurSetData(heur, NULL);
909 
910  return SCIP_OKAY;
911 }
912 
913 
914 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
915 static
916 SCIP_DECL_HEUREXITSOL(heurExitsolVbounds)
917 { /*lint --e{715}*/
918  SCIP_HEURDATA* heurdata;
919  int v;
920 
921  heurdata = SCIPheurGetData(heur);
922  assert(heurdata != NULL);
923 
924  /* release all variables */
925  for( v = 0; v < heurdata->nlbvars; ++v )
926  {
927  SCIP_CALL( SCIPreleaseVar(scip, &heurdata->lbvars[v]) );
928  }
929  /* release all variables */
930  for( v = 0; v < heurdata->nubvars; ++v )
931  {
932  SCIP_CALL( SCIPreleaseVar(scip, &heurdata->ubvars[v]) );
933  }
934  /* release all variables */
935  for( v = 0; v < heurdata->nlbimpvars + heurdata->nubimpvars; ++v )
936  {
937  SCIP_CALL( SCIPreleaseVar(scip, &heurdata->impvars[v]) );
938  }
939 
940  /* free varbounds array */
941  SCIPfreeMemoryArrayNull(scip, &heurdata->impvars);
942  SCIPfreeMemoryArrayNull(scip, &heurdata->lbvars);
943  SCIPfreeMemoryArrayNull(scip, &heurdata->ubvars);
944 
945  /* reset heuristic data structure */
946  heurdataReset(heurdata);
947 
948  return SCIP_OKAY;
949 }
950 
951 /** execution method of primal heuristic */
952 static
953 SCIP_DECL_HEUREXEC(heurExecVbounds)
954 { /*lint --e{715}*/
955  SCIP_HEURDATA* heurdata;
956 
957  assert( heur != NULL );
958  assert( scip != NULL );
959  assert( result != NULL );
960 
961  *result = SCIP_DIDNOTRUN;
962 
963  if( SCIPgetNPseudoBranchCands(scip) == 0 )
964  return SCIP_OKAY;
965 
966  heurdata = SCIPheurGetData(heur);
967  assert(heurdata != NULL);
968 
969  if( !heurdata->initialized )
970  {
971  SCIP_CALL( initializeCandsLists(scip, heurdata) );
972  }
973 
974  if( !heurdata->applicable )
975  return SCIP_OKAY;
976 
977  *result = SCIP_DIDNOTFIND;
978 
979  /* try variable lower and upper bounds which respect to objective coefficients */
980  SCIP_CALL( applyVbounds(scip, heur, heurdata, heurdata->impvars, heurdata->nlbimpvars, heurdata->nubimpvars, result) );
981 
982  /* try variable lower bounds */
983  SCIP_CALL( applyVbounds(scip, heur, heurdata, heurdata->lbvars, heurdata->nlbvars, 0, result) );
984 
985  /* try variable upper bounds */
986  SCIP_CALL( applyVbounds(scip, heur, heurdata, heurdata->ubvars, 0, heurdata->nubvars, result) );
987 
988  return SCIP_OKAY;
989 }
990 
991 /*
992  * primal heuristic specific interface methods
993  */
994 
995 /** creates the vbounds primal heuristic and includes it in SCIP */
997  SCIP* scip /**< SCIP data structure */
998  )
999 {
1000  SCIP_HEURDATA* heurdata;
1001  SCIP_HEUR* heur;
1002 
1003  /* create vbounds primal heuristic data */
1004  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
1005  heurdataReset(heurdata);
1006 
1007  /* include primal heuristic */
1008  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
1010  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecVbounds, heurdata) );
1011 
1012  assert(heur != NULL);
1013 
1014  /* set non-NULL pointers to callback methods */
1015  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyVbounds) );
1016  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeVbounds) );
1017  SCIP_CALL( SCIPsetHeurExitsol(scip, heur, heurExitsolVbounds) );
1018 
1019  /* add variable bounds primal heuristic parameters */
1020  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minfixingrate",
1021  "minimum percentage of integer variables that have to be fixable",
1022  &heurdata->minfixingrate, FALSE, DEFAULT_MINFIXINGRATE, 0.0, 1.0, NULL, NULL) );
1023 
1024  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/maxnodes",
1025  "maximum number of nodes to regard in the subproblem",
1026  &heurdata->maxnodes, TRUE,DEFAULT_MAXNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1027 
1028  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/nodesofs",
1029  "number of nodes added to the contingent of the total nodes",
1030  &heurdata->nodesofs, FALSE, DEFAULT_NODESOFS, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1031 
1032  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/minnodes",
1033  "minimum number of nodes required to start the subproblem",
1034  &heurdata->minnodes, TRUE, DEFAULT_MINNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1035 
1036  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/nodesquot",
1037  "contingent of sub problem nodes in relation to the number of nodes of the original problem",
1038  &heurdata->nodesquot, FALSE, DEFAULT_NODESQUOT, 0.0, 1.0, NULL, NULL) );
1039 
1040  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minimprove",
1041  "factor by which "HEUR_NAME" heuristic should at least improve the incumbent",
1042  &heurdata->minimprove, TRUE, DEFAULT_MINIMPROVE, 0.0, 1.0, NULL, NULL) );
1043 
1044  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxproprounds",
1045  "maximum number of propagation rounds during probing (-1 infinity)",
1046  &heurdata->maxproprounds, TRUE, DEFAULT_MAXPROPROUNDS, -1, INT_MAX/4, NULL, NULL) );
1047 
1048  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/copycuts",
1049  "should all active cuts from cutpool be copied to constraints in subproblem?",
1050  &heurdata->copycuts, TRUE, DEFAULT_COPYCUTS, NULL, NULL) );
1051 
1052  return SCIP_OKAY;
1053 }
1054