Scippy

SCIP

Solving Constraint Integer Programs

heur_oneopt.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_oneopt.c
17  * @brief improvement heuristic that alters single variable values
18  * @author Timo Berthold
19  */
20 
21 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
22 
23 #include <assert.h>
24 #include <string.h>
25 
26 #include "scip/heur_oneopt.h"
27 
28 /* @note if heuristic runs in root node timing is change there to (SCIP_HEURTIMING_DURINGLPLOOP |
29  * SCIP_HEURTIMING_BEFORENODE), see SCIP_DECL_HEURINITSOL callback
30  */
31 
32 #define HEUR_NAME "oneopt"
33 #define HEUR_DESC "1-opt heuristic which tries to improve setting of single integer variables"
34 #define HEUR_DISPCHAR 'b'
35 #define HEUR_PRIORITY -20000
36 #define HEUR_FREQ 1
37 #define HEUR_FREQOFS 0
38 #define HEUR_MAXDEPTH -1
39 #define HEUR_TIMING SCIP_HEURTIMING_BEFOREPRESOL | SCIP_HEURTIMING_AFTERNODE
40 #define HEUR_USESSUBSCIP FALSE /**< does the heuristic use a secondary SCIP instance? */
41 
42 #define DEFAULT_WEIGHTEDOBJ TRUE /**< should the objective be weighted with the potential shifting value when sorting the shifting candidates? */
43 #define DEFAULT_DURINGROOT TRUE /**< should the heuristic be called before and during the root node? */
44 #define DEFAULT_BEFOREPRESOL FALSE /**< should the heuristic be called before presolving */
45 #define DEFAULT_FORCELPCONSTRUCTION FALSE /**< should the construction of the LP be forced even if LP solving is deactivated? */
46 
47 /*
48  * Data structures
49  */
50 
51 /** primal heuristic data */
52 struct SCIP_HeurData
53 {
54  int lastsolindex; /**< index of the last solution for which oneopt was performed */
55  SCIP_Bool weightedobj; /**< should the objective be weighted with the potential shifting value when sorting the shifting candidates? */
56  SCIP_Bool duringroot; /**< should the heuristic be called before and during the root node? */
57  SCIP_Bool forcelpconstruction;/**< should the construction of the LP be forced even if LP solving is deactivated? */
58  SCIP_Bool beforepresol; /**< should the heuristic be called before presolving */
59 };
60 
61 
62 /*
63  * Local methods
64  */
65 
66 /** creates a new solution for the original problem by copying the solution of the subproblem */
67 static
69  SCIP* scip, /**< original SCIP data structure */
70  SCIP* subscip, /**< SCIP structure of the subproblem */
71  SCIP_VAR** subvars, /**< the variables of the subproblem */
72  SCIP_HEUR* heur, /**< zeroobj heuristic structure */
73  SCIP_SOL* subsol, /**< solution of the subproblem */
74  SCIP_Bool* success /**< used to store whether new solution was found or not */
75  )
76 {
77  SCIP_VAR** vars; /* the original problem's variables */
78  int nvars; /* the original problem's number of variables */
79  SCIP_Real* subsolvals; /* solution values of the subproblem */
80  SCIP_SOL* newsol; /* solution to be created for the original problem */
81 
82  assert(scip != NULL);
83  assert(subscip != NULL);
84  assert(subvars != NULL);
85  assert(subsol != NULL);
86 
87  /* get variables' data */
88  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
89 
90  /* sub-SCIP may have more variables than the number of active (transformed) variables in the main SCIP
91  * since constraint copying may have required the copy of variables that are fixed in the main SCIP
92  */
93  assert(nvars <= SCIPgetNOrigVars(subscip));
94 
95  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
96 
97  /* copy the solution */
98  SCIP_CALL( SCIPgetSolVals(subscip, subsol, nvars, subvars, subsolvals) );
99 
100  /* create new solution for the original problem */
101  SCIP_CALL( SCIPcreateSol(scip, &newsol, heur) );
102  SCIP_CALL( SCIPsetSolVals(scip, newsol, nvars, vars, subsolvals) );
103 
104  /* try to add new solution to scip and free it immediately */
105  SCIP_CALL( SCIPtrySolFree(scip, &newsol, FALSE, TRUE, TRUE, TRUE, success) );
106 
107  SCIPfreeBufferArray(scip, &subsolvals);
108 
109  return SCIP_OKAY;
110 }
111 
112 static
114  SCIP* scip, /**< SCIP data structure */
115  SCIP_VAR* var, /**< variable that should be shifted */
116  SCIP_Real solval, /**< current solution value */
117  SCIP_Real* activities /**< LP row activities */
118  )
119 {
120  SCIP_Real lb;
121  SCIP_Real ub;
122  SCIP_Real obj;
123  SCIP_Real shiftval;
124 
125  SCIP_COL* col;
126  SCIP_ROW** colrows;
127  SCIP_Real* colvals;
128 
129  int ncolrows;
130  int i;
131 
132  SCIP_Bool shiftdown;
133 
134  /* get variable's solution value, global bounds and objective coefficient */
135  lb = SCIPvarGetLbGlobal(var);
136  ub = SCIPvarGetUbGlobal(var);
137  obj = SCIPvarGetObj(var);
138  shiftval = 0.0;
139  shiftdown = TRUE;
140 
141  /* determine shifting direction and maximal possible shifting w.r.t. corresponding bound */
142  if( obj > 0.0 && SCIPisFeasGE(scip, solval - 1.0, lb) )
143  shiftval = SCIPfeasFloor(scip, solval - lb);
144  else if( obj < 0.0 && SCIPisFeasLE(scip, solval + 1.0, ub) )
145  {
146  shiftval = SCIPfeasFloor(scip, ub - solval);
147  shiftdown = FALSE;
148  }
149  else
150  return 0.0;
151 
152 
153  SCIPdebugMessage("Try to shift %s variable <%s> with\n", shiftdown ? "down" : "up", SCIPvarGetName(var) );
154  SCIPdebugMessage(" lb:<%g> <= val:<%g> <= ub:<%g> and obj:<%g> by at most: <%g>\n", lb, solval, ub, obj, shiftval);
155 
156  /* get data of LP column */
157  col = SCIPvarGetCol(var);
158  colrows = SCIPcolGetRows(col);
159  colvals = SCIPcolGetVals(col);
160  ncolrows = SCIPcolGetNLPNonz(col);
161 
162  assert(ncolrows == 0 || (colrows != NULL && colvals != NULL));
163 
164  /* find minimal shift value, st. all rows stay valid */
165  for( i = 0; i < ncolrows && shiftval > 0.0; ++i )
166  {
167  SCIP_ROW* row;
168  int rowpos;
169 
170  row = colrows[i];
171  rowpos = SCIProwGetLPPos(row);
172  assert(-1 <= rowpos && rowpos < SCIPgetNLPRows(scip) );
173 
174  /* only global rows need to be valid */
175  if( rowpos >= 0 && !SCIProwIsLocal(row) )
176  {
177  SCIP_Real shiftvalrow;
178 
179  assert(SCIProwIsInLP(row));
180 
181  if( shiftdown == (colvals[i] > 0) )
182  shiftvalrow = SCIPfeasFloor(scip, (activities[rowpos] - SCIProwGetLhs(row)) / ABS(colvals[i]));
183  else
184  shiftvalrow = SCIPfeasFloor(scip, (SCIProwGetRhs(row) - activities[rowpos]) / ABS(colvals[i]));
185 #ifdef SCIP_DEBUG
186  if( shiftvalrow < shiftval )
187  {
188  SCIPdebugMessage(" -> The shift value had to be reduced to <%g>, because of row <%s>.\n",
189  shiftvalrow, SCIProwGetName(row));
190  SCIPdebugMessage(" lhs:<%g> <= act:<%g> <= rhs:<%g>, colval:<%g>\n",
191  SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row), colvals[i]);
192  }
193 #endif
194  shiftval = MIN(shiftval, shiftvalrow);
195  }
196  }
197  if( shiftdown )
198  shiftval *= -1.0;
199 
200  return shiftval;
201 }
202 
203 
204 /** update row activities after a variable's solution value changed */
205 static
207  SCIP* scip, /**< SCIP data structure */
208  SCIP_Real* activities, /**< LP row activities */
209  SCIP_VAR* var, /**< variable that has been changed */
210  SCIP_Real shiftval /**< value that is added to variable */
211  )
212 {
213  SCIP_Real* colvals;
214  SCIP_ROW** colrows;
215  SCIP_COL* col;
216 
217  int ncolrows;
218  int i;
219 
220  assert(activities != NULL);
221 
222  /* get data of column associated to variable */
223  col = SCIPvarGetCol(var);
224  colrows = SCIPcolGetRows(col);
225  colvals = SCIPcolGetVals(col);
226  ncolrows = SCIPcolGetNLPNonz(col);
227  assert(ncolrows == 0 || (colrows != NULL && colvals != NULL));
228 
229  /* enumerate all rows with nonzero entry in this column */
230  for( i = 0; i < ncolrows; ++i )
231  {
232  SCIP_ROW* row;
233  int rowpos;
234 
235  row = colrows[i];
236  rowpos = SCIProwGetLPPos(row);
237  assert(-1 <= rowpos && rowpos < SCIPgetNLPRows(scip) );
238 
239  /* update row activity, only regard global rows in the LP */
240  if( rowpos >= 0 && !SCIProwIsLocal(row) )
241  {
242  activities[rowpos] += shiftval * colvals[i];
243 
244  if( SCIPisInfinity(scip, activities[rowpos]) )
245  activities[rowpos] = SCIPinfinity(scip);
246  else if( SCIPisInfinity(scip, -activities[rowpos]) )
247  activities[rowpos] = -SCIPinfinity(scip);
248  }
249  }
250 
251  return SCIP_OKAY;
252 }
253 
254 /*
255  * Callback methods of primal heuristic
256  */
257 
258 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
259 static
260 SCIP_DECL_HEURCOPY(heurCopyOneopt)
261 { /*lint --e{715}*/
262  assert(scip != NULL);
263  assert(heur != NULL);
264  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
265 
266  /* call inclusion method of primal heuristic */
268 
269  return SCIP_OKAY;
270 }
271 
272 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
273 static
274 SCIP_DECL_HEURFREE(heurFreeOneopt)
275 { /*lint --e{715}*/
276  SCIP_HEURDATA* heurdata;
277 
278  assert(heur != NULL);
279  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
280  assert(scip != NULL);
281 
282  /* free heuristic data */
283  heurdata = SCIPheurGetData(heur);
284  assert(heurdata != NULL);
285  SCIPfreeMemory(scip, &heurdata);
286  SCIPheurSetData(heur, NULL);
287 
288  return SCIP_OKAY;
289 }
290 
291 
292 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
293 static
294 SCIP_DECL_HEURINITSOL(heurInitsolOneopt)
295 {
296  SCIP_HEURDATA* heurdata;
297 
298  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
299 
300  /* create heuristic data */
301  heurdata = SCIPheurGetData(heur);
302  assert(heurdata != NULL);
303 
304  /* if the heuristic is called at the root node, we may want to be called during the cut-and-price loop and even before the first LP solve */
305  if( heurdata->duringroot && SCIPheurGetFreqofs(heur) == 0 )
307 
308  return SCIP_OKAY;
309 }
310 
311 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
312 static
313 SCIP_DECL_HEUREXITSOL(heurExitsolOneopt)
314 {
315  assert(heur != NULL);
316  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
317 
318  /* reset the timing mask to its default value */
320 
321  return SCIP_OKAY;
322 }
323 
324 /** execution method of primal heuristic */
325 static
326 SCIP_DECL_HEUREXEC(heurExecOneopt)
327 { /*lint --e{715}*/
328 
329  SCIP_HEURDATA* heurdata;
330  SCIP_SOL* bestsol; /* incumbent solution */
331  SCIP_SOL* worksol; /* heuristic's working solution */
332  SCIP_VAR** vars; /* SCIP variables */
333  SCIP_VAR** shiftcands; /* shiftable variables */
334  SCIP_ROW** lprows; /* SCIP LP rows */
335  SCIP_Real* activities; /* row activities for working solution */
336  SCIP_Real* shiftvals;
337 
338  SCIP_Real lb;
339  SCIP_Real ub;
340  SCIP_Bool localrows;
341  SCIP_Bool valid;
342  int nchgbound;
343  int nbinvars;
344  int nintvars;
345  int nvars;
346  int nlprows;
347  int i;
348  int nshiftcands;
349  int shiftcandssize;
350  SCIP_RETCODE retcode;
351 
352  assert(heur != NULL);
353  assert(scip != NULL);
354  assert(result != NULL);
355 
356  /* get heuristic's data */
357  heurdata = SCIPheurGetData(heur);
358  assert(heurdata != NULL);
359 
360  *result = SCIP_DELAYED;
361 
362  /* we only want to process each solution once */
363  bestsol = SCIPgetBestSol(scip);
364  if( bestsol == NULL || heurdata->lastsolindex == SCIPsolGetIndex(bestsol) )
365  return SCIP_OKAY;
366 
367  /* reset the timing mask to its default value (at the root node it could be different) */
368  if( SCIPgetNNodes(scip) > 1 )
370 
371  /* get problem variables */
372  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
373  nintvars += nbinvars;
374 
375  /* do not run if there are no discrete variables */
376  if( nintvars == 0 )
377  {
378  *result = SCIP_DIDNOTRUN;
379  return SCIP_OKAY;
380  }
381 
382  if( heurtiming == SCIP_HEURTIMING_BEFOREPRESOL )
383  {
384  SCIP* subscip; /* the subproblem created by zeroobj */
385  SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */
386  SCIP_VAR** subvars; /* subproblem's variables */
387  SCIP_Real* subsolvals; /* solution values of the subproblem */
388 
389  SCIP_Real timelimit; /* time limit for zeroobj subproblem */
390  SCIP_Real memorylimit; /* memory limit for zeroobj subproblem */
391 
392  SCIP_SOL* startsol;
393  SCIP_SOL** subsols;
394  int nsubsols;
395 
396  if( !heurdata->beforepresol )
397  return SCIP_OKAY;
398 
399  /* check whether there is enough time and memory left */
400  timelimit = 0.0;
401  memorylimit = 0.0;
402  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
403  if( !SCIPisInfinity(scip, timelimit) )
404  timelimit -= SCIPgetSolvingTime(scip);
405  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
406 
407  /* substract the memory already used by the main SCIP and the estimated memory usage of external software */
408  if( !SCIPisInfinity(scip, memorylimit) )
409  {
410  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
411  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
412  }
413 
414  /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */
415  if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 )
416  return SCIP_OKAY;
417 
418  /* initialize the subproblem */
419  SCIP_CALL( SCIPcreate(&subscip) );
420 
421  /* create the variable mapping hash map */
422  SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) );
423  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
424 
425  /* copy complete SCIP instance */
426  valid = FALSE;
427  SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "oneopt", TRUE, FALSE, TRUE, &valid) );
428  SCIP_CALL( SCIPtransformProb(subscip) );
429 
430  /* get variable image */
431  for( i = 0; i < nvars; i++ )
432  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]);
433 
434  /* copy the solution */
435  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
436  SCIP_CALL( SCIPgetSolVals(scip, bestsol, nvars, vars, subsolvals) );
437 
438  /* create start solution for the subproblem */
439  SCIP_CALL( SCIPcreateOrigSol(subscip, &startsol, NULL) );
440  SCIP_CALL( SCIPsetSolVals(subscip, startsol, nvars, subvars, subsolvals) );
441 
442  /* try to add new solution to sub-SCIP and free it immediately */
443  valid = FALSE;
444  SCIP_CALL( SCIPtrySolFree(subscip, &startsol, FALSE, FALSE, FALSE, FALSE, &valid) );
445  SCIPfreeBufferArray(scip, &subsolvals);
446  SCIPhashmapFree(&varmapfw);
447 
448  /* deactivate basically everything except oneopt in the sub-SCIP */
452  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", 1LL) );
453  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
454  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
455  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
456  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
457 
458  /* if necessary, some of the parameters have to be unfixed first */
459  if( SCIPisParamFixed(subscip, "lp/solvefreq") )
460  {
461  SCIPwarningMessage(scip, "unfixing parameter lp/solvefreq in subscip of oneopt heuristic\n");
462  SCIP_CALL( SCIPunfixParam(subscip, "lp/solvefreq") );
463  }
464  SCIP_CALL( SCIPsetIntParam(subscip, "lp/solvefreq", -1) );
465 
466  if( SCIPisParamFixed(subscip, "heuristics/oneopt/freq") )
467  {
468  SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/freq in subscip of oneopt heuristic\n");
469  SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/freq") );
470  }
471  SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/oneopt/freq", 1) );
472 
473  if( SCIPisParamFixed(subscip, "heuristics/oneopt/forcelpconstruction") )
474  {
475  SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/forcelpconstruction in subscip of oneopt heuristic\n");
476  SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/forcelpconstruction") );
477  }
478  SCIP_CALL( SCIPsetBoolParam(subscip, "heuristics/oneopt/forcelpconstruction", TRUE) );
479 
480  /* avoid recursive call, which would lead to an endless loop */
481  if( SCIPisParamFixed(subscip, "heuristics/oneopt/beforepresol") )
482  {
483  SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/beforepresol in subscip of oneopt heuristic\n");
484  SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/beforepresol") );
485  }
486  SCIP_CALL( SCIPsetBoolParam(subscip, "heuristics/oneopt/beforepresol", FALSE) );
487 
488  if( valid )
489  {
490  retcode = SCIPsolve(subscip);
491 
492  /* errors in solving the subproblem should not kill the overall solving process;
493  * hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
494  */
495  if( retcode != SCIP_OKAY )
496  {
497 #ifndef NDEBUG
498  SCIP_CALL( retcode );
499 #endif
500  SCIPwarningMessage(scip, "Error while solving subproblem in zeroobj heuristic; sub-SCIP terminated with code <%d>\n",retcode);
501  }
502 
503 #ifdef SCIP_DEBUG
504  SCIP_CALL( SCIPprintStatistics(subscip, NULL) );
505 #endif
506  }
507 
508  /* check, whether a solution was found;
509  * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted
510  */
511  nsubsols = SCIPgetNSols(subscip);
512  subsols = SCIPgetSols(subscip);
513  valid = FALSE;
514  for( i = 0; i < nsubsols && !valid; ++i )
515  {
516  SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &valid) );
517  if( valid )
518  *result = SCIP_FOUNDSOL;
519  }
520 
521  /* free subproblem */
522  SCIPfreeBufferArray(scip, &subvars);
523  SCIP_CALL( SCIPfree(&subscip) );
524 
525  return SCIP_OKAY;
526  }
527 
528  /* we can only work on solutions valid in the transformed space */
529  if( SCIPsolIsOriginal(bestsol) )
530  return SCIP_OKAY;
531 
532  if( heurtiming == SCIP_HEURTIMING_BEFORENODE && (SCIPhasCurrentNodeLP(scip) || heurdata->forcelpconstruction) )
533  {
534  SCIP_Bool cutoff;
535  cutoff = FALSE;
536  SCIP_CALL( SCIPconstructLP(scip, &cutoff) );
537  SCIP_CALL( SCIPflushLP(scip) );
538 
539  /* get problem variables again, SCIPconstructLP() might have added new variables */
540  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
541  nintvars += nbinvars;
542  }
543 
544  /* we need an LP */
545  if( SCIPgetNLPRows(scip) == 0 )
546  return SCIP_OKAY;
547 
548  *result = SCIP_DIDNOTFIND;
549 
550  nchgbound = 0;
551 
552  /* initialize data */
553  nshiftcands = 0;
554  shiftcandssize = 8;
555  heurdata->lastsolindex = SCIPsolGetIndex(bestsol);
556  SCIP_CALL( SCIPcreateSolCopy(scip, &worksol, bestsol) );
557  SCIPsolSetHeur(worksol,heur);
558 
559  SCIPdebugMessage("Starting bound adjustment in 1-opt heuristic\n");
560 
561  /* maybe change solution values due to global bound changes first */
562  for( i = nvars - 1; i >= 0; --i )
563  {
564  SCIP_VAR* var;
565  SCIP_Real solval;
566 
567  var = vars[i];
568  lb = SCIPvarGetLbGlobal(var);
569  ub = SCIPvarGetUbGlobal(var);
570 
571  solval = SCIPgetSolVal(scip, bestsol,var);
572  /* old solution value is smaller than the actual lower bound */
573  if( SCIPisFeasLT(scip, solval, lb) )
574  {
575  /* set the solution value to the global lower bound */
576  SCIP_CALL( SCIPsetSolVal(scip, worksol, var, lb) );
577  ++nchgbound;
578  SCIPdebugMessage("var <%s> type %d, old solval %g now fixed to lb %g\n", SCIPvarGetName(var), SCIPvarGetType(var), solval, lb);
579  }
580  /* old solution value is greater than the actual upper bound */
581  else if( SCIPisFeasGT(scip, solval, SCIPvarGetUbGlobal(var)) )
582  {
583  /* set the solution value to the global upper bound */
584  SCIP_CALL( SCIPsetSolVal(scip, worksol, var, ub) );
585  ++nchgbound;
586  SCIPdebugMessage("var <%s> type %d, old solval %g now fixed to ub %g\n", SCIPvarGetName(var), SCIPvarGetType(var), solval, ub);
587  }
588  }
589 
590  SCIPdebugMessage("number of bound changes (due to global bounds) = %d\n", nchgbound);
591  SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) );
592  SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) );
593 
594  localrows = FALSE;
595  valid = TRUE;
596 
597  /* initialize activities */
598  for( i = 0; i < nlprows; ++i )
599  {
600  SCIP_ROW* row;
601 
602  row = lprows[i];
603  assert(SCIProwGetLPPos(row) == i);
604 
605  if( !SCIProwIsLocal(row) )
606  {
607  activities[i] = SCIPgetRowSolActivity(scip, row, worksol);
608  SCIPdebugMessage("Row <%s> has activity %g\n", SCIProwGetName(row), activities[i]);
609  if( SCIPisFeasLT(scip, activities[i], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[i], SCIProwGetRhs(row)) )
610  {
611  valid = FALSE;
612  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
613  SCIPdebugMessage("row <%s> activity %g violates bounds, lhs = %g, rhs = %g\n", SCIProwGetName(row), activities[i], SCIProwGetLhs(row), SCIProwGetRhs(row));
614  break;
615  }
616  }
617  else
618  localrows = TRUE;
619  }
620 
621  if( !valid )
622  {
623  /** @todo try to correct lp rows */
624  SCIPdebugMessage("Some global bound changes were not valid in lp rows.\n");
625  goto TERMINATE;
626  }
627 
628  SCIP_CALL( SCIPallocBufferArray(scip, &shiftcands, shiftcandssize) );
629  SCIP_CALL( SCIPallocBufferArray(scip, &shiftvals, shiftcandssize) );
630 
631 
632  SCIPdebugMessage("Starting 1-opt heuristic\n");
633 
634  /* enumerate all integer variables and find out which of them are shiftable */
635  for( i = 0; i < nintvars; i++ )
636  {
637  if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN )
638  {
639  SCIP_Real shiftval;
640  SCIP_Real solval;
641 
642  /* find out whether the variable can be shifted */
643  solval = SCIPgetSolVal(scip, worksol, vars[i]);
644  shiftval = calcShiftVal(scip, vars[i], solval, activities);
645 
646  /* insert the variable into the list of shifting candidates */
647  if( !SCIPisFeasZero(scip, shiftval) )
648  {
649  SCIPdebugMessage(" -> Variable <%s> can be shifted by <%1.1f> \n", SCIPvarGetName(vars[i]), shiftval);
650 
651  if( nshiftcands == shiftcandssize)
652  {
653  shiftcandssize *= 8;
654  SCIP_CALL( SCIPreallocBufferArray(scip, &shiftcands, shiftcandssize) );
655  SCIP_CALL( SCIPreallocBufferArray(scip, &shiftvals, shiftcandssize) );
656  }
657  shiftcands[nshiftcands] = vars[i];
658  shiftvals[nshiftcands] = shiftval;
659  nshiftcands++;
660  }
661  }
662  }
663 
664  /* if at least one variable can be shifted, shift variables sorted by their objective */
665  if( nshiftcands > 0 )
666  {
667  SCIP_Real shiftval;
668  SCIP_Real solval;
669  SCIP_VAR* var;
670 
671  /* the case that exactly one variable can be shifted is slightly easier */
672  if( nshiftcands == 1 )
673  {
674  var = shiftcands[0];
675  assert(var != NULL);
676  solval = SCIPgetSolVal(scip, worksol, var);
677  shiftval = shiftvals[0];
678  assert(!SCIPisFeasZero(scip,shiftval));
679  SCIPdebugMessage(" Only one shiftcand found, var <%s>, which is now shifted by<%1.1f> \n",
680  SCIPvarGetName(var), shiftval);
681  SCIP_CALL( SCIPsetSolVal(scip, worksol, var, solval+shiftval) );
682  }
683  else
684  {
685  SCIP_Real* objcoeffs;
686 
687  SCIP_CALL( SCIPallocBufferArray(scip, &objcoeffs, nshiftcands) );
688 
689  SCIPdebugMessage(" %d shiftcands found \n", nshiftcands);
690 
691  /* sort the variables by their objective, optionally weighted with the shiftval */
692  if( heurdata->weightedobj )
693  {
694  for( i = 0; i < nshiftcands; ++i )
695  objcoeffs[i] = SCIPvarGetObj(shiftcands[i])*shiftvals[i];
696  }
697  else
698  {
699  for( i = 0; i < nshiftcands; ++i )
700  objcoeffs[i] = SCIPvarGetObj(shiftcands[i]);
701  }
702 
703  /* sort arrays with respect to the first one */
704  SCIPsortRealPtr(objcoeffs, (void**)shiftcands, nshiftcands);
705 
706  /* try to shift each variable -> Activities have to be updated */
707  for( i = 0; i < nshiftcands; ++i )
708  {
709  var = shiftcands[i];
710  assert(var != NULL);
711  solval = SCIPgetSolVal(scip, worksol, var);
712  shiftval = calcShiftVal(scip, var, solval, activities);
713  SCIPdebugMessage(" -> Variable <%s> is now shifted by <%1.1f> \n", SCIPvarGetName(vars[i]), shiftval);
714  assert(i > 0 || !SCIPisFeasZero(scip, shiftval));
715  SCIP_CALL( SCIPsetSolVal(scip, worksol, var, solval+shiftval) );
716  SCIP_CALL( updateRowActivities(scip, activities, var, shiftval) );
717  }
718 
719  SCIPfreeBufferArray(scip, &objcoeffs);
720  }
721 
722  /* if the problem is a pure IP, try to install the solution, if it is a MIP, solve LP again to set the continuous
723  * variables to the best possible value
724  */
725  if( nvars == nintvars || !SCIPhasCurrentNodeLP(scip) || SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL )
726  {
727  SCIP_Bool success;
728 
729  /* since we ignore local rows, we cannot guarantee their feasibility and have to set the checklprows flag to
730  * TRUE if local rows are present
731  */
732  SCIP_CALL( SCIPtrySol(scip, worksol, FALSE, FALSE, FALSE, localrows, &success) );
733 
734  if( success )
735  {
736  SCIPdebugMessage("found feasible shifted solution:\n");
737  SCIPdebug( SCIP_CALL( SCIPprintSol(scip, worksol, NULL, FALSE) ) );
738  heurdata->lastsolindex = SCIPsolGetIndex(bestsol);
739  *result = SCIP_FOUNDSOL;
740  }
741  }
742  else
743  {
744  SCIP_Bool lperror;
745 #ifdef NDEBUG
746  SCIP_RETCODE retstat;
747 #endif
748 
749  SCIPdebugMessage("shifted solution should be feasible -> solve LP to fix continuous variables to best values\n");
750 
751  /* start diving to calculate the LP relaxation */
752  SCIP_CALL( SCIPstartDive(scip) );
753 
754  /* set the bounds of the variables: fixed for integers, global bounds for continuous */
755  for( i = 0; i < nvars; ++i )
756  {
757  if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN )
758  {
759  SCIP_CALL( SCIPchgVarLbDive(scip, vars[i], SCIPvarGetLbGlobal(vars[i])) );
760  SCIP_CALL( SCIPchgVarUbDive(scip, vars[i], SCIPvarGetUbGlobal(vars[i])) );
761  }
762  }
763  /* apply this after global bounds to not cause an error with intermediate empty domains */
764  for( i = 0; i < nintvars; ++i )
765  {
766  if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN )
767  {
768  solval = SCIPgetSolVal(scip, worksol, vars[i]);
769  SCIP_CALL( SCIPchgVarLbDive(scip, vars[i], solval) );
770  SCIP_CALL( SCIPchgVarUbDive(scip, vars[i], solval) );
771  }
772  }
773 
774  /* solve LP */
775  SCIPdebugMessage(" -> old LP iterations: %"SCIP_LONGINT_FORMAT"\n", SCIPgetNLPIterations(scip));
776 
777  /**@todo in case of an MINLP, if SCIPisNLPConstructed() is TRUE, say, rather solve the NLP instead of the LP */
778  /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic.
779  * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
780  */
781 #ifdef NDEBUG
782  retstat = SCIPsolveDiveLP(scip, -1, &lperror, NULL);
783  if( retstat != SCIP_OKAY )
784  {
785  SCIPwarningMessage(scip, "Error while solving LP in Oneopt heuristic; LP solve terminated with code <%d>\n",retstat);
786  }
787 #else
788  SCIP_CALL( SCIPsolveDiveLP(scip, -1, &lperror, NULL) );
789 #endif
790 
791  SCIPdebugMessage(" -> new LP iterations: %"SCIP_LONGINT_FORMAT"\n", SCIPgetNLPIterations(scip));
792  SCIPdebugMessage(" -> error=%u, status=%d\n", lperror, SCIPgetLPSolstat(scip));
793 
794  /* check if this is a feasible solution */
795  if( !lperror && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
796  {
797  SCIP_Bool success;
798 
799  /* copy the current LP solution to the working solution */
800  SCIP_CALL( SCIPlinkLPSol(scip, worksol) );
801  SCIP_CALL( SCIPtrySol(scip, worksol, FALSE, FALSE, FALSE, FALSE, &success) );
802 
803  /* check solution for feasibility */
804  if( success )
805  {
806  SCIPdebugMessage("found feasible shifted solution:\n");
807  SCIPdebug( SCIP_CALL( SCIPprintSol(scip, worksol, NULL, FALSE) ) );
808  heurdata->lastsolindex = SCIPsolGetIndex(bestsol);
809  *result = SCIP_FOUNDSOL;
810  }
811  }
812 
813  /* terminate the diving */
814  SCIP_CALL( SCIPendDive(scip) );
815  }
816  }
817  SCIPdebugMessage("Finished 1-opt heuristic\n");
818 
819  SCIPfreeBufferArray(scip, &shiftvals);
820  SCIPfreeBufferArray(scip, &shiftcands);
821 
822  TERMINATE:
823  SCIPfreeBufferArray(scip, &activities);
824  SCIP_CALL( SCIPfreeSol(scip, &worksol) );
825 
826  return SCIP_OKAY;
827 }
828 
829 /*
830  * primal heuristic specific interface methods
831  */
832 
833 /** creates the oneopt primal heuristic and includes it in SCIP */
835  SCIP* scip /**< SCIP data structure */
836  )
837 {
838  SCIP_HEURDATA* heurdata;
839  SCIP_HEUR* heur;
840 
841  /* create Oneopt primal heuristic data */
842  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
843  heurdata->lastsolindex = -1;
844 
845  /* include primal heuristic */
846  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
848  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecOneopt, heurdata) );
849 
850  assert(heur != NULL);
851 
852  /* set non-NULL pointers to callback methods */
853  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyOneopt) );
854  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeOneopt) );
855  SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolOneopt) );
856  SCIP_CALL( SCIPsetHeurExitsol(scip, heur, heurExitsolOneopt) );
857  /* add oneopt primal heuristic parameters */
858 
859  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/oneopt/weightedobj",
860  "should the objective be weighted with the potential shifting value when sorting the shifting candidates?",
861  &heurdata->weightedobj, TRUE, DEFAULT_WEIGHTEDOBJ, NULL, NULL) );
862 
863  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/oneopt/duringroot",
864  "should the heuristic be called before and during the root node?",
865  &heurdata->duringroot, TRUE, DEFAULT_DURINGROOT, NULL, NULL) );
866 
867  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/oneopt/forcelpconstruction",
868  "should the construction of the LP be forced even if LP solving is deactivated?",
869  &heurdata->forcelpconstruction, TRUE, DEFAULT_FORCELPCONSTRUCTION, NULL, NULL) );
870 
871  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/oneopt/beforepresol",
872  "should the heuristic be called before presolving?",
873  &heurdata->beforepresol, TRUE, DEFAULT_BEFOREPRESOL, NULL, NULL) );
874 
875  return SCIP_OKAY;
876 }
877