Scippy

GCG

Branch-and-Price & Column Generation for Everyone

heur_gcgrins.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program */
4 /* GCG --- Generic Column Generation */
5 /* a Dantzig-Wolfe decomposition based extension */
6 /* of the branch-cut-and-price framework */
7 /* SCIP --- Solving Constraint Integer Programs */
8 /* */
9 /* Copyright (C) 2010-2021 Operations Research, RWTH Aachen University */
10 /* Zuse Institute Berlin (ZIB) */
11 /* */
12 /* This program is free software; you can redistribute it and/or */
13 /* modify it under the terms of the GNU Lesser General Public License */
14 /* as published by the Free Software Foundation; either version 3 */
15 /* of the License, or (at your option) any later version. */
16 /* */
17 /* This program is distributed in the hope that it will be useful, */
18 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
19 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
20 /* GNU Lesser General Public License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with this program; if not, write to the Free Software */
24 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
25 /* */
26 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27 
28 /**@file heur_gcgrins.c
29  * @brief LNS heuristic that combines the incumbent with the LP optimum
30  * @author Timo Berthold
31  * @author Christian Puchert
32  */
33 
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35 
36 #include <assert.h>
37 #include <string.h>
38 
39 #include "heur_gcgrins.h"
40 #include "gcg.h"
41 
42 #include "scip/scipdefplugins.h"
43 #include "scip/cons_linear.h"
44 
45 #define HEUR_NAME "gcgrins"
46 #define HEUR_DESC "relaxation induced neighborhood search by Danna, Rothberg, and Le Pape"
47 #define HEUR_DISPCHAR 'N'
48 #define HEUR_PRIORITY -1101000
49 #define HEUR_FREQ 20
50 #define HEUR_FREQOFS 5
51 #define HEUR_MAXDEPTH -1
52 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
53 #define HEUR_USESSUBSCIP TRUE /**< does the heuristic use a secondary SCIP instance? */
54 
55 #define DEFAULT_NODESOFS 500 /**< number of nodes added to the contingent of the total nodes */
56 #define DEFAULT_MAXNODES 5000 /**< maximum number of nodes to regard in the subproblem */
57 #define DEFAULT_MINNODES 500 /**< minimum number of nodes to regard in the subproblem */
58 #define DEFAULT_MINIMPROVE 0.01 /**< factor by which RINS should at least improve the incumbent */
59 #define DEFAULT_MINFIXINGRATE 0.0 /**< minimum percentage of integer variables that have to be fixed */
60 #define DEFAULT_NODESQUOT 0.1 /**< subproblem nodes in relation to nodes of the original problem */
61 #define DEFAULT_NWAITINGNODES 200 /**< number of nodes without incumbent change that heuristic should wait */
62 #define DEFAULT_USELPROWS FALSE /**< should subproblem be created out of the rows in the LP rows,
63  * otherwise, the copy constructors of the constraints handlers are used */
64 #define DEFAULT_COPYCUTS TRUE /**< if DEFAULT_USELPROWS is FALSE, then should all active cuts from the cutpool
65  * of the original scip be copied to constraints of the subscip
66  */
67 
68 
69 /*
70  * Data structures
71  */
72 
73 /** primal heuristic data */
74 struct SCIP_HeurData
75 {
76  int nodesofs; /**< number of nodes added to the contingent of the total nodes */
77  int maxnodes; /**< maximum number of nodes to regard in the subproblem */
78  int minnodes; /**< minimum number of nodes to regard in the subproblem */
79  SCIP_Real minfixingrate; /**< minimum percentage of integer variables that have to be fixed */
80  int nwaitingnodes; /**< number of nodes without incumbent change that heuristic should wait */
81  SCIP_Real minimprove; /**< factor by which RINS should at least improve the incumbent */
82  SCIP_Longint usednodes; /**< nodes already used by RINS in earlier calls */
83  SCIP_Real nodesquot; /**< subproblem nodes in relation to nodes of the original problem */
84  SCIP_Bool uselprows; /**< should subproblem be created out of the rows in the LP rows? */
85  SCIP_Bool copycuts; /**< if uselprows == FALSE, should all active cuts from cutpool be copied
86  * to constraints in subproblem?
87  */
88 #ifdef SCIP_STATISTIC
89  SCIP_Longint nfixfails; /**< number of abortions due to a bad fixing rate */
90  SCIP_Real avgfixrate; /**< average rate of variables that are fixed */
91  SCIP_Real avgzerorate; /**< average rate of fixed variables that are zero */
92  SCIP_Longint totalsols; /**< total number of subSCIP solutions (including those which have not
93  * been added)
94  */
95  SCIP_Real subsciptime; /**< total subSCIP solving time in seconds */
96  SCIP_Real bestprimalbd; /**< objective value of best solution found by this heuristic */
97 #endif
98 };
99 
100 /*
101  * Local methods
102  */
103 
104 /** creates a subproblem for subscip by fixing a number of variables */
105 static
106 SCIP_RETCODE createSubproblem(
107  SCIP* scip, /**< original SCIP data structure */
108  SCIP* subscip, /**< SCIP data structure for the subproblem */
109  SCIP_VAR** subvars, /**< the variables of the subproblem */
110  SCIP_Real minfixingrate, /**< percentage of integer variables that have to be fixed */
111  SCIP_Bool uselprows, /**< should subproblem be created out of the rows in the LP rows? */
112  SCIP_Real* intfixingrate, /**< percentage of integers that get actually fixed */
113  SCIP_Real* zerofixingrate, /**< percentage of variables fixed to zero */
114  SCIP_Bool* success /**< pointer to store whether the problem was created successfully */
115  )
116 {
117  SCIP_SOL* bestsol; /* incumbent solution of the original problem */
118  SCIP_VAR** vars; /* original scip variables */
119  SCIP_ROW** rows; /* original scip rows */
120 
121  int nrows;
122  int nvars;
123  int nbinvars;
124  int nintvars;
125  int i;
126 
127  int fixingcounter;
128  int zerocounter;
129 
130  assert(scip != NULL);
131  assert(subscip != NULL);
132  assert(subvars != NULL);
133 
134  assert(0.0 <= minfixingrate && minfixingrate <= 1.0);
135 
136  /* get required data of the original problem */
137  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
138  bestsol = SCIPgetBestSol(scip);
139  assert(bestsol != NULL);
140 
141  fixingcounter = 0;
142  zerocounter = 0;
143 
144  /* change bounds of variables of the subproblem */
145  for( i = 0; i < nbinvars + nintvars; i++ )
146  {
147  SCIP_Real lpsolval;
148  SCIP_Real solval;
149 
150  /* get the current relaxation solution and the incumbent solution for each variable */
151  lpsolval = SCIPgetRelaxSolVal(scip, vars[i]);
152  solval = SCIPgetSolVal(scip, bestsol, vars[i]);
153 
154  /* iff both solutions are equal, variable is fixed to that value in the subproblem, otherwise it is just copied */
155  if( SCIPisFeasEQ(scip, lpsolval, solval) )
156  {
157  /* perform the bound change */
158  SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[i], solval) );
159  SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[i], solval) );
160  fixingcounter++;
161  if( SCIPisZero(scip, solval) )
162  zerocounter++;
163  }
164  }
165 
166  /* abort, if all variables were fixed */
167  if( fixingcounter == nbinvars + nintvars )
168  {
169  *intfixingrate = 1.0;
170  *zerofixingrate = 1.0;
171  *success = FALSE;
172  return SCIP_OKAY;
173  }
174  else
175  {
176  *intfixingrate = (SCIP_Real)fixingcounter / (SCIP_Real)(MAX(nbinvars + nintvars, 1));
177  *zerofixingrate = (SCIP_Real)zerocounter / MAX((SCIP_Real)fixingcounter, 1.0);
178  }
179 
180  /* abort, if the amount of fixed variables is insufficient */
181  if( *intfixingrate < minfixingrate )
182  {
183  *success = FALSE;
184  SCIPstatisticPrintf("gcgrins statistic: fixed only %5.2f ( %5.2f zero) integer variables --> abort \n", *intfixingrate, *zerofixingrate);
185  return SCIP_OKAY;
186  }
187 
188  if( uselprows )
189  {
190  /* get the rows and their number */
191  SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
192 
193  /* copy all rows to linear constraints */
194  for( i = 0; i < nrows; i++ )
195  {
196  SCIP_CONS* cons;
197  SCIP_VAR** consvars;
198  SCIP_COL** cols;
199  SCIP_Real constant;
200  SCIP_Real lhs;
201  SCIP_Real rhs;
202  SCIP_Real* vals;
203  int nnonz;
204  int j;
205 
206  /* ignore rows that are only locally valid */
207  if( SCIProwIsLocal(rows[i]) )
208  continue;
209 
210  /* get the row's data */
211  constant = SCIProwGetConstant(rows[i]);
212  lhs = SCIProwGetLhs(rows[i]) - constant;
213  rhs = SCIProwGetRhs(rows[i]) - constant;
214  vals = SCIProwGetVals(rows[i]);
215  nnonz = SCIProwGetNNonz(rows[i]);
216  cols = SCIProwGetCols(rows[i]);
217 
218  assert( lhs <= rhs );
219 
220  /* allocate memory array to be filled with the corresponding subproblem variables */
221  SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nnonz) );
222  for( j = 0; j < nnonz; j++ )
223  consvars[j] = subvars[SCIPvarGetProbindex(SCIPcolGetVar(cols[j]))];
224 
225  /* create a new linear constraint and add it to the subproblem */
226  SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, SCIProwGetName(rows[i]), nnonz, consvars, vals, lhs, rhs,
227  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) );
228  SCIP_CALL( SCIPaddCons(subscip, cons) );
229  SCIP_CALL( SCIPreleaseCons(subscip, &cons) );
230 
231  /* free temporary memory */
232  SCIPfreeBufferArray(scip, &consvars);
233  }
234  }
235 
236  *success = TRUE;
237  return SCIP_OKAY;
238 }
239 
240 
241 /** creates a new solution for the original problem by copying the solution of the subproblem */
242 static
243 SCIP_RETCODE createNewSol(
244  SCIP* scip, /**< original SCIP data structure */
245  SCIP* subscip, /**< SCIP structure of the subproblem */
246  SCIP_VAR** subvars, /**< the variables of the subproblem */
247  SCIP_HEUR* heur, /**< RINS heuristic structure */
248  SCIP_SOL* subsol, /**< solution of the subproblem */
249  SCIP_Bool* success /**< used to store whether new solution was found or not */
250 )
251 {
252 #ifdef SCIP_STATISTIC
253  SCIP_HEURDATA* heurdata;
254 #endif
255  SCIP_VAR** vars; /* the original problem's variables */
256  int nvars;
257  SCIP_Real* subsolvals; /* solution values of the subproblem */
258  SCIP_SOL* newsol; /* solution to be created for the original problem */
259 
260  assert(scip != NULL);
261  assert(subscip != NULL);
262  assert(subvars != NULL);
263  assert(subsol != NULL);
264 
265 #ifdef SCIP_STATISTIC
266  /* get heuristic data */
267  heurdata = SCIPheurGetData(heur);
268  assert( heurdata != NULL );
269 #endif
270 
271  /* get variables' data */
272  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
273  /* sub-SCIP may have more variables than the number of active (transformed) variables in the main SCIP
274  * since constraint copying may have required the copy of variables that are fixed in the main SCIP
275  */
276  assert(nvars <= SCIPgetNOrigVars(subscip));
277 
278  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
279 
280  /* copy the solution */
281  SCIP_CALL( SCIPgetSolVals(subscip, subsol, nvars, subvars, subsolvals) );
282 
283  /* create new solution for the original problem */
284  SCIP_CALL( SCIPcreateSol(scip, &newsol, heur) );
285  SCIP_CALL( SCIPsetSolVals(scip, newsol, nvars, vars, subsolvals) );
286 
287  /* try to add new solution to scip and free it immediately */
288  SCIP_CALL( SCIPtrySol(scip, newsol, FALSE, FALSE, TRUE, TRUE, TRUE, success) );
289 
290 #ifdef SCIP_STATISTIC
291  if( *success )
292  {
293  if( SCIPgetSolTransObj(scip, newsol) < heurdata->bestprimalbd )
294  heurdata->bestprimalbd = SCIPgetSolTransObj(scip, newsol);
295  }
296 #endif
297 
298  SCIP_CALL( SCIPfreeSol(scip, &newsol) );
299 
300  SCIPfreeBufferArray(scip, &subsolvals);
301 
302  return SCIP_OKAY;
303 }
304 
305 /*
306  * Callback methods of primal heuristic
307  */
308 
309 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
310 static
311 SCIP_DECL_HEURFREE(heurFreeGcgrins)
312 { /*lint --e{715}*/
313  SCIP_HEURDATA* heurdata;
314 
315  assert(heur != NULL);
316  assert(scip != NULL);
317 
318  /* get heuristic data */
319  heurdata = SCIPheurGetData(heur);
320  assert(heurdata != NULL);
321 
322  /* free heuristic data */
323  SCIPfreeMemory(scip, &heurdata);
324  SCIPheurSetData(heur, NULL);
325 
326  return SCIP_OKAY;
327 }
328 
329 
330 /** initialization method of primal heuristic (called after problem was transformed) */
331 static
332 SCIP_DECL_HEURINIT(heurInitGcgrins)
333 { /*lint --e{715}*/
334  SCIP_HEURDATA* heurdata;
335 
336  assert(heur != NULL);
337  assert(scip != NULL);
338 
339  /* get heuristic data */
340  heurdata = SCIPheurGetData(heur);
341  assert(heurdata != NULL);
342 
343  /* initialize data */
344  heurdata->usednodes = 0;
345 
346  return SCIP_OKAY;
347 }
348 
349 #ifdef SCIP_STATISTIC
350 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
351 static
352 SCIP_DECL_HEURINITSOL(heurInitsolGcgrins)
353 { /*lint --e{715}*/
354  SCIP_HEURDATA* heurdata;
355 
356  assert(heur != NULL);
357  assert(scip != NULL);
358 
359  /* get heuristic data */
360  heurdata = SCIPheurGetData(heur);
361  assert(heurdata != NULL);
362 
363  /* initialize statistical data */
364  heurdata->avgfixrate = 0.0;
365  heurdata->avgzerorate = 0.0;
366  heurdata->totalsols = 0;
367  heurdata->subsciptime = 0.0;
368  heurdata->bestprimalbd = SCIPinfinity(scip);
369 
370  return SCIP_OKAY;
371 }
372 
373 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
374 static
375 SCIP_DECL_HEUREXITSOL(heurExitsolGcgrins)
376 { /*lint --e{715}*/
377  SCIP_HEURDATA* heurdata;
378  SCIP_Longint ncalls;
379 
380  assert(heur != NULL);
381  assert(scip != NULL);
382 
383  /* get heuristic data */
384  heurdata = SCIPheurGetData(heur);
385  assert(heurdata != NULL);
386 
387  ncalls = SCIPheurGetNCalls(heur);
388  heurdata->avgfixrate /= MAX((SCIP_Real)ncalls, 1.0);
389  heurdata->avgzerorate /= MAX((SCIP_Real)ncalls, 1.0);
390 
391  /* print detailed statistics */
392  SCIPstatisticPrintf("LNS Statistics -- %s:\n", SCIPheurGetName(heur));
393  SCIPstatisticPrintf("Calls : %13"SCIP_LONGINT_FORMAT"\n", ncalls);
394  SCIPstatisticPrintf("Failed Fixings : %13"SCIP_LONGINT_FORMAT"\n", heurdata->nfixfails);
395  SCIPstatisticPrintf("Sols : %13"SCIP_LONGINT_FORMAT"\n", SCIPheurGetNSolsFound(heur));
396  SCIPstatisticPrintf("Improving Sols : %13"SCIP_LONGINT_FORMAT"\n", SCIPheurGetNBestSolsFound(heur));
397  SCIPstatisticPrintf("Total Sols : %13"SCIP_LONGINT_FORMAT"\n", heurdata->totalsols);
398  SCIPstatisticPrintf("subSCIP time : %13.2f\n", heurdata->subsciptime);
399  SCIPstatisticPrintf("subSCIP nodes : %13"SCIP_LONGINT_FORMAT"\n", heurdata->usednodes);
400  SCIPstatisticPrintf("Avg. fixing rate : %13.2f\n", 100.0 * heurdata->avgfixrate);
401  SCIPstatisticPrintf("Avg. zero rate : %13.2f\n", 100.0 * heurdata->avgzerorate);
402  SCIPstatisticPrintf("Best primal bd. :");
403  if( SCIPisInfinity(scip, heurdata->bestprimalbd) )
404  SCIPstatisticPrintf(" infinity\n");
405  else
406  SCIPstatisticPrintf(" %13.6e\n", heurdata->bestprimalbd);
407  SCIPstatisticPrintf("\n");
408 
409  return SCIP_OKAY;
410 }
411 #endif
412 
413 
414 /** execution method of primal heuristic */
415 static
416 SCIP_DECL_HEUREXEC(heurExecGcgrins)
417 { /*lint --e{715}*/
418  SCIP_Longint nstallnodes;
419 
420  SCIP* masterprob;
421  SCIP_HEURDATA* heurdata; /* heuristic's data */
422  SCIP* subscip; /* the subproblem created by RINS */
423  SCIP_VAR** vars; /* original problem's variables */
424  SCIP_VAR** subvars; /* subproblem's variables */
425  SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */
426 
427  SCIP_Real timelimit; /* timelimit for the subproblem */
428  SCIP_Real memorylimit;
429  SCIP_Real cutoff; /* objective cutoff for the subproblem */
430  SCIP_Real upperbound;
431  SCIP_Real allfixingrate; /* percentage of all variables fixed */
432  SCIP_Real intfixingrate; /* percentage of integer variables fixed */
433  SCIP_Real zerofixingrate; /* percentage of variables fixed to zero */
434 
435  int nvars;
436  int nbinvars;
437  int nintvars;
438  int i;
439 
440  SCIP_Bool success;
441  SCIP_RETCODE retcode;
442 
443  assert(heur != NULL);
444  assert(scip != NULL);
445  assert(result != NULL);
446 
447  /* get master problem */
448  masterprob = GCGgetMasterprob(scip);
449  assert(masterprob != NULL);
450 
451  /* get heuristic data */
452  heurdata = SCIPheurGetData(heur);
453  assert(heurdata != NULL);
454 
455  *result = SCIP_DELAYED;
456 
457  /* do not execute the heuristic on invalid relaxation solutions
458  * (which is the case if the node has been cut off)
459  */
460  if( !SCIPisRelaxSolValid(scip) )
461  {
462  SCIPdebugMessage("skipping GCG RINS: invalid relaxation solution\n");
463  return SCIP_OKAY;
464  }
465 
466  /* only call heuristic, if feasible solution is available */
467  if( SCIPgetNSols(scip) <= 0 )
468  return SCIP_OKAY;
469 
470  /* only call heuristic, if an optimal LP solution is at hand */
471  if( SCIPgetStage(masterprob) > SCIP_STAGE_SOLVING || SCIPgetLPSolstat(masterprob) != SCIP_LPSOLSTAT_OPTIMAL )
472  return SCIP_OKAY;
473 
474  /* only call heuristic, if the best solution comes from transformed problem */
475  assert(SCIPgetBestSol(scip) != NULL);
476  if( SCIPsolGetOrigin(SCIPgetBestSol(scip)) == SCIP_SOLORIGIN_ORIGINAL )
477  return SCIP_OKAY;
478 
479  /* only call heuristic, if enough nodes were processed since last incumbent */
480  if( SCIPgetNNodes(scip) - SCIPgetSolNodenum(scip,SCIPgetBestSol(scip)) < heurdata->nwaitingnodes )
481  return SCIP_OKAY;
482 
483  *result = SCIP_DIDNOTRUN;
484 
485  /* check whether there is enough time and memory left */
486  timelimit = 0.0;
487  memorylimit = 0.0;
488  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
489  if( !SCIPisInfinity(scip, timelimit) )
490  timelimit -= SCIPgetSolvingTime(scip);
491  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
492 
493  /* substract the memory already used by the main SCIP and the estimated memory usage of external software */
494  if( !SCIPisInfinity(scip, memorylimit) )
495  {
496  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
497  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
498  }
499 
500  /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */
501  if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 )
502  return SCIP_OKAY;
503 
504  /* calculate the maximal number of branching nodes until heuristic is aborted */
505  nstallnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip));
506 
507  /* reward RINS if it succeeded often */
508  nstallnodes = (SCIP_Longint)(nstallnodes * 3.0 * (SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur) + 1.0));
509  nstallnodes -= 100 * SCIPheurGetNCalls(heur); /* count the setup costs for the sub-MIP as 100 nodes */
510  nstallnodes += heurdata->nodesofs;
511 
512  /* determine the node limit for the current process */
513  nstallnodes -= heurdata->usednodes;
514  nstallnodes = MIN(nstallnodes, heurdata->maxnodes);
515 
516  /* check whether we have enough nodes left to call subproblem solving */
517  if( nstallnodes < heurdata->minnodes )
518  return SCIP_OKAY;
519 
520  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
521 
522  /* check whether discrete variables are available */
523  if( nbinvars == 0 && nintvars == 0 )
524  return SCIP_OKAY;
525 
526  if( SCIPisStopped(scip) )
527  return SCIP_OKAY;
528 
529  *result = SCIP_DIDNOTFIND;
530 
531  /* initializing the subproblem */
532  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
533  SCIP_CALL( SCIPcreate(&subscip) );
534 
535  /* create the variable mapping hash map */
536  SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), nvars) );
537 
538  if( heurdata->uselprows )
539  {
540  char probname[SCIP_MAXSTRLEN];
541 
542  /* copy all plugins */
543  SCIP_CALL( SCIPincludeDefaultPlugins(subscip) );
544 
545  /* get name of the original problem and add the string "_gcgrinssub" */
546  (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_gcgrinssub", SCIPgetProbName(scip));
547 
548  /* create the subproblem */
549  SCIP_CALL( SCIPcreateProb(subscip, probname, NULL, NULL, NULL, NULL, NULL, NULL, NULL) );
550 
551  /* copy all variables */
552  SCIP_CALL( SCIPcopyVars(scip, subscip, varmapfw, NULL, NULL, NULL, 0, TRUE) );
553  }
554  else
555  {
556  SCIP_Bool valid;
557  valid = FALSE;
558 
559  SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "gcgrins", TRUE, FALSE, FALSE, TRUE, &valid) ); /** @todo check for thread safeness */
560 
561  if( heurdata->copycuts )
562  {
563  /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */
564  SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, TRUE, NULL) );
565  }
566 
567  SCIPdebugMessage("Copying the SCIP instance was %s complete.\n", valid ? "" : "not ");
568  }
569 
570  for( i = 0; i < nvars; i++ )
571  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]);
572 
573  /* free hash map */
574  SCIPhashmapFree(&varmapfw);
575 
576  success = FALSE;
577 
578  SCIPstatisticPrintf("gcgrins statistic: called at node %"SCIP_LONGINT_FORMAT"\n", SCIPgetNNodes(scip));
579 
580  /* create a new problem, which fixes variables with same value in bestsol and LP relaxation */
581  SCIP_CALL( createSubproblem(scip, subscip, subvars, heurdata->minfixingrate, heurdata->uselprows, &intfixingrate, &zerofixingrate, &success) );
582  SCIPdebugMessage("RINS subproblem: %d vars, %d cons, success=%u\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip), success);
583 
584 #ifdef SCIP_STATISTIC
585  heurdata->avgfixrate += intfixingrate;
586  heurdata->avgzerorate += zerofixingrate;
587 #endif
588 
589  if( !success )
590  {
591  *result = SCIP_DIDNOTRUN;
592 #ifdef SCIP_STATISTIC
593  ++heurdata->nfixfails;
594 #endif
595  goto TERMINATE;
596  }
597 
598  /* disable output to console */
599  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
600 
601  /* set limits for the subproblem */
602  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nstallnodes) );
603  SCIP_CALL( SCIPsetIntParam(subscip, "limits/bestsol", 3) );
604  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
605  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
606 
607  /* forbid recursive call of heuristics and separators solving subMIPs */
608  SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) );
609 
610  /* disable cutting plane separation */
611  SCIP_CALL( SCIPsetSeparating(subscip, SCIP_PARAMSETTING_OFF, TRUE) );
612 
613  /* disable expensive presolving */
614  SCIP_CALL( SCIPsetPresolving(subscip, SCIP_PARAMSETTING_FAST, TRUE) );
615 
616  /* use best estimate node selection */
617  if( SCIPfindNodesel(subscip, "estimate") != NULL && !SCIPisParamFixed(subscip, "nodeselection/estimate/stdpriority") )
618  {
619  SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/estimate/stdpriority", INT_MAX/4) );
620  }
621 
622  /* use inference branching */
623  if( SCIPfindBranchrule(subscip, "inference") != NULL && !SCIPisParamFixed(subscip, "branching/inference/priority") )
624  {
625  SCIP_CALL( SCIPsetIntParam(subscip, "branching/inference/priority", INT_MAX/4) );
626  }
627 
628  /* disable conflict analysis */
629  if( !SCIPisParamFixed(subscip, "conflict/enable") )
630  {
631  SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/enable", FALSE) );
632  }
633 
634  /* add an objective cutoff */
635  assert(!SCIPisInfinity(scip,SCIPgetUpperbound(scip)));
636 
637  upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip);
638  if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) )
639  {
640  cutoff = (1-heurdata->minimprove)*SCIPgetUpperbound(scip) + heurdata->minimprove*SCIPgetLowerbound(scip);
641  }
642  else
643  {
644  if( SCIPgetUpperbound ( scip ) >= 0 )
645  cutoff = ( 1 - heurdata->minimprove ) * SCIPgetUpperbound ( scip );
646  else
647  cutoff = ( 1 + heurdata->minimprove ) * SCIPgetUpperbound ( scip );
648  }
649  cutoff = MIN(upperbound, cutoff );
650  SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) );
651 
652  /* presolve the subproblem */
653  retcode = SCIPpresolve(subscip);
654 
655  /* errors in solving the subproblem should not kill the overall solving process;
656  * hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
657  */
658  if( retcode != SCIP_OKAY )
659  {
660 #ifndef NDEBUG
661  SCIP_CALL( retcode );
662 #endif
663  SCIPwarningMessage(scip, "Error while presolving subproblem in GCG RINS heuristic; sub-SCIP terminated with code <%d>\n",retcode);
664  goto TERMINATE;
665  }
666 
667  SCIPdebugMessage("GCG RINS presolved subproblem: %d vars, %d cons, success=%u\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip), success);
668 
669  allfixingrate = (SCIPgetNOrigVars(subscip) - SCIPgetNVars(subscip)) / (SCIP_Real)SCIPgetNOrigVars(subscip);
670 
671  /* additional variables added in presolving may lead to the subSCIP having more variables than the original */
672  allfixingrate = MAX(allfixingrate, 0.0);
673 
674  /* after presolving, we should have at least reached a certain fixing rate over ALL variables (including continuous)
675  * to ensure that not only the MIP but also the LP relaxation is easy enough
676  */
677  if( allfixingrate >= heurdata->minfixingrate / 2.0 )
678  {
679  SCIP_SOL** subsols;
680  int nsubsols;
681 
682  /* solve the subproblem */
683  retcode = SCIPsolve(subscip);
684 
685  /* Errors in solving the subproblem should not kill the overall solving process
686  * Hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
687  */
688  if( retcode != SCIP_OKAY )
689  {
690 #ifndef NDEBUG
691  SCIP_CALL( retcode );
692 #endif
693  SCIPwarningMessage(scip, "Error while solving subproblem in GCG RINS heuristic; sub-SCIP terminated with code <%d>\n",retcode);
694  }
695 
696 #ifdef SCIP_STATISTIC
697  heurdata->usednodes += SCIPgetNNodes(subscip);
698  heurdata->subsciptime += SCIPgetTotalTime(subscip);
699 #endif
700 
701  /* check, whether a solution was found;
702  * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted
703  */
704  nsubsols = SCIPgetNSols(subscip);
705  subsols = SCIPgetSols(subscip);
706 #ifdef SCIP_STATISTIC
707  heurdata->totalsols += nsubsols;
708 #endif
709  success = FALSE;
710  for( i = 0; i < nsubsols && !success; ++i )
711  {
712  SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &success) );
713  if( success )
714  *result = SCIP_FOUNDSOL;
715  }
716 
717  SCIPstatisticPrintf("gcgrins statistic: fixed %6.3f integer variables ( %6.3f zero), %6.3f all variables, needed %6.1f sec (SCIP time: %6.1f sec), %"SCIP_LONGINT_FORMAT" nodes, found %d solutions, solution %10.4f found at node %"SCIP_LONGINT_FORMAT"\n",
718  intfixingrate, zerofixingrate, allfixingrate, SCIPgetSolvingTime(subscip), SCIPgetSolvingTime(scip), SCIPgetNNodes(subscip), nsubsols,
719  success ? SCIPgetPrimalbound(scip) : SCIPinfinity(scip), nsubsols > 0 ? SCIPsolGetNodenum(SCIPgetBestSol(subscip)) : -1 );
720  }
721  else
722  {
723  SCIPstatisticPrintf("gcgrins statistic: fixed only %6.3f integer variables ( %6.3f zero), %6.3f all variables --> abort \n", intfixingrate, zerofixingrate, allfixingrate);
724  }
725 
726  TERMINATE:
727  /* free subproblem */
728  SCIPfreeBufferArray(scip, &subvars);
729  SCIP_CALL( SCIPfree(&subscip) );
730 
731  return SCIP_OKAY;
732 }
733 
734 /*
735  * primal heuristic specific interface methods
736  */
737 
738 /** creates the GCG RINS primal heuristic and includes it in SCIP */
740  SCIP* scip /**< SCIP data structure */
741  )
742 {
743  SCIP_HEURDATA* heurdata;
744  SCIP_HEUR* heur;
745 
746  /* create GCG RINS primal heuristic data */
747  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
748 
749  /* include primal heuristic */
750  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
752  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecGcgrins, heurdata) );
753 
754  assert(heur != NULL);
755 
756  /* set non-NULL pointers to callback methods */
757  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeGcgrins) );
758  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitGcgrins) );
759 #ifdef SCIP_STATISTIC
760  SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolGcgrins) );
761  SCIP_CALL( SCIPsetHeurExitsol(scip, heur, heurExitsolGcgrins) );
762 #endif
763 
764  /* add RINS primal heuristic parameters */
765  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/nodesofs",
766  "number of nodes added to the contingent of the total nodes",
767  &heurdata->nodesofs, FALSE, DEFAULT_NODESOFS, 0, INT_MAX, NULL, NULL) );
768 
769  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxnodes",
770  "maximum number of nodes to regard in the subproblem",
771  &heurdata->maxnodes, TRUE, DEFAULT_MAXNODES, 0, INT_MAX, NULL, NULL) );
772 
773  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/minnodes",
774  "minimum number of nodes required to start the subproblem",
775  &heurdata->minnodes, TRUE, DEFAULT_MINNODES, 0, INT_MAX, NULL, NULL) );
776 
777  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/nodesquot",
778  "contingent of sub problem nodes in relation to the number of nodes of the original problem",
779  &heurdata->nodesquot, FALSE, DEFAULT_NODESQUOT, 0.0, 1.0, NULL, NULL) );
780 
781  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/nwaitingnodes",
782  "number of nodes without incumbent change that heuristic should wait",
783  &heurdata->nwaitingnodes, TRUE, DEFAULT_NWAITINGNODES, 0, INT_MAX, NULL, NULL) );
784 
785  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minimprove",
786  "factor by which "HEUR_NAME" should at least improve the incumbent",
787  &heurdata->minimprove, TRUE, DEFAULT_MINIMPROVE, 0.0, 1.0, NULL, NULL) );
788 
789  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minfixingrate",
790  "minimum percentage of integer variables that have to be fixed",
791  &heurdata->minfixingrate, FALSE, DEFAULT_MINFIXINGRATE, 0.0, 1.0, NULL, NULL) );
792 
793  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/uselprows",
794  "should subproblem be created out of the rows in the LP rows?",
795  &heurdata->uselprows, TRUE, DEFAULT_USELPROWS, NULL, NULL) );
796 
797  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/copycuts",
798  "if uselprows == FALSE, should all active cuts from cutpool be copied to constraints in subproblem?",
799  &heurdata->copycuts, TRUE, DEFAULT_COPYCUTS, NULL, NULL) );
800 
801  return SCIP_OKAY;
802 }
#define HEUR_DESC
Definition: heur_gcgrins.c:46
#define DEFAULT_NODESOFS
Definition: heur_gcgrins.c:55
static SCIP_DECL_HEURINITSOL(heurInitsolGcgdins)
Definition: heur_gcgdins.c:485
#define DEFAULT_USELPROWS
Definition: heur_gcgrins.c:62
GCG interface methods.
static SCIP_DECL_HEURINIT(heurInitGcgrins)
Definition: heur_gcgrins.c:332
SCIP_Real minimprove
Definition: heur_gcgdins.c:80
#define DEFAULT_MINNODES
Definition: heur_gcgrins.c:57
#define DEFAULT_MAXNODES
Definition: heur_gcgrins.c:56
#define HEUR_PRIORITY
Definition: heur_gcgrins.c:48
static SCIP_DECL_HEURFREE(heurFreeGcgrins)
Definition: heur_gcgrins.c:311
#define HEUR_DISPCHAR
Definition: heur_gcgrins.c:47
#define DEFAULT_NWAITINGNODES
Definition: heur_gcgrins.c:61
SCIP_Bool copycuts
Definition: heur_gcgdins.c:89
static SCIP_RETCODE createNewSol(SCIP *scip, SCIP *subscip, SCIP_VAR **subvars, SCIP_HEUR *heur, SCIP_SOL *subsol, SCIP_Bool *success)
Definition: heur_gcgrins.c:243
SCIP * GCGgetMasterprob(SCIP *scip)
Definition: relax_gcg.c:3920
LNS heuristic that combines the incumbent with the LP optimum.
#define DEFAULT_MINFIXINGRATE
Definition: heur_gcgrins.c:59
SCIP_RETCODE SCIPincludeHeurGcgrins(SCIP *scip)
Definition: heur_gcgrins.c:739
#define HEUR_FREQ
Definition: heur_gcgrins.c:49
#define DEFAULT_COPYCUTS
Definition: heur_gcgrins.c:64
#define DEFAULT_NODESQUOT
Definition: heur_gcgrins.c:60
#define HEUR_USESSUBSCIP
Definition: heur_gcgrins.c:53
#define HEUR_FREQOFS
Definition: heur_gcgrins.c:50
static SCIP_DECL_HEUREXEC(heurExecGcgrins)
Definition: heur_gcgrins.c:416
#define HEUR_MAXDEPTH
Definition: heur_gcgrins.c:51
static SCIP_DECL_HEUREXITSOL(heurExitsolGcgdins)
Definition: heur_gcgdins.c:529
SCIP_Bool uselprows
Definition: heur_gcgdins.c:88
#define HEUR_NAME
Definition: heur_gcgrins.c:45
#define HEUR_TIMING
Definition: heur_gcgrins.c:52
SCIP_Longint usednodes
Definition: heur_gcgdins.c:81
#define DEFAULT_MINIMPROVE
Definition: heur_gcgrins.c:58
SCIP_Real minfixingrate
Definition: heur_gcgrens.c:84
SCIP_Real nodesquot
Definition: heur_gcgdins.c:82
static SCIP_RETCODE createSubproblem(SCIP *scip, SCIP *subscip, SCIP_VAR **subvars, SCIP_Real minfixingrate, SCIP_Bool uselprows, SCIP_Real *intfixingrate, SCIP_Real *zerofixingrate, SCIP_Bool *success)
Definition: heur_gcgrins.c:106