Scippy

SCIP

Solving Constraint Integer Programs

heur_undercover.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_undercover.c
17  * @brief Undercover primal heuristic for MINLPs
18  * @author Timo Berthold
19  * @author Ambros Gleixner
20  *
21  * The undercover heuristic is designed for mixed-integer nonlinear programs and tries to fix a subset of variables such
22  * as to make each constraint linear or convex. For this purpose it solves a binary program to automatically determine
23  * the minimum number of variable fixings necessary. As fixing values, we use values from the LP relaxation, the NLP
24  * relaxation, or the incumbent solution.
25  *
26  * @todo use the conflict analysis to analyze the infeasibility which arise after the probing of the cover worked and
27  * solve returned infeasible, instead of adding the Nogood/Conflict by hand; that has the advantage that the SCIP
28  * takes care of creating the conflict and might shrink the initial reason
29  *
30  * @todo do not use LP and NLP fixing values in the same run, e.g., fixingalts = "lni", but start a second dive if LP
31  * values fail
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 "scip/scip.h"
40 #include "scip/scipdefplugins.h"
41 #include "scip/heur_undercover.h"
42 #include "nlpi/exprinterpret.h"
43 
44 #define HEUR_NAME "undercover"
45 #define HEUR_DESC "solves a sub-CIP determined by a set covering approach"
46 #define HEUR_DISPCHAR 'U'
47 #define HEUR_PRIORITY -1110000
48 #define HEUR_FREQ 0
49 #define HEUR_FREQOFS 0
50 #define HEUR_MAXDEPTH -1
51 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
52 #define HEUR_USESSUBSCIP TRUE /**< does the heuristic use a secondary SCIP instance? */
53 
54 /* default values for user parameters, grouped by parameter type */
55 #define DEFAULT_FIXINGALTS "li" /**< sequence of fixing values used: 'l'p relaxation, 'n'lp relaxation, 'i'ncumbent solution */
56 
57 #define DEFAULT_MAXNODES (SCIP_Longint)500/**< maximum number of nodes to regard in the subproblem */
58 #define DEFAULT_MINNODES (SCIP_Longint)500/**< minimum number of nodes to regard in the subproblem */
59 #define DEFAULT_NODESOFS (SCIP_Longint)500/**< number of nodes added to the contingent of the total nodes */
60 
61 #define DEFAULT_CONFLICTWEIGHT 1000.0 /**< weight for conflict score in fixing order */
62 #define DEFAULT_CUTOFFWEIGHT 1.0 /**< weight for cutoff score in fixing order */
63 #define DEFAULT_INFERENCEWEIGHT 1.0 /**< weight for inference score in fixing order */
64 #define DEFAULT_MAXCOVERSIZEVARS 1.0 /**< maximum coversize (as fraction of total number of variables) */
65 #define DEFAULT_MAXCOVERSIZECONSS SCIP_REAL_MAX /**< maximum coversize (as ratio to the percentage of non-affected constraints) */
66 #define DEFAULT_MINCOVEREDREL 0.15 /**< minimum percentage of nonlinear constraints in the original problem */
67 #define DEFAULT_MINCOVEREDABS 5 /**< minimum number of nonlinear constraints in the original problem */
68 #define DEFAULT_MINIMPROVE 0.0 /**< factor by which heuristic should at least improve the incumbent */
69 #define DEFAULT_NODESQUOT 0.1 /**< subproblem nodes in relation to nodes of the original problem */
70 #define DEFAULT_RECOVERDIV 0.9 /**< fraction of covering variables in the last cover which need to change their value when recovering */
71 
72 #define DEFAULT_MAXBACKTRACKS 6 /**< maximum number of backtracks */
73 #define DEFAULT_MAXRECOVERS 0 /**< maximum number of recoverings */
74 #define DEFAULT_MAXREORDERS 1 /**< maximum number of reorderings of the fixing order */
75 
76 #define DEFAULT_COVERINGOBJ 'u' /**< objective function of the covering problem */
77 #define DEFAULT_FIXINGORDER 'v' /**< order in which variables should be fixed */
78 
79 #define DEFAULT_BEFORECUTS TRUE /**< should undercover called at root node before cut separation? */
80 #define DEFAULT_FIXINTFIRST FALSE /**< should integer variables in the cover be fixed first? */
81 #define DEFAULT_LOCKSROUNDING TRUE /**< shall LP values for integer vars be rounded according to locks? */
82 #define DEFAULT_ONLYCONVEXIFY FALSE /**< should we only fix/dom.red. variables creating nonconvexity? */
83 #define DEFAULT_POSTNLP TRUE /**< should the NLP heuristic be called to polish a feasible solution? */
84 #define DEFAULT_COVERBD FALSE /**< should bounddisjunction constraints be covered (or just copied)? */
85 #define DEFAULT_REUSECOVER FALSE /**< shall the cover be re-used if a conflict was added after an infeasible subproblem? */
86 #define DEFAULT_COPYCUTS TRUE /**< should all active cuts from the cutpool of the original scip be copied
87  * to constraints of the subscip
88  */
89 
90 /* local defines */
91 #define COVERINGOBJS "cdlmtu" /**< list of objective functions of the covering problem */
92 #define FIXINGORDERS "CcVv" /**< list of orders in which variables can be fixed */
93 #define MAXNLPFAILS 1 /**< maximum number of fails after which we give up solving the NLP relaxation */
94 #define MAXPOSTNLPFAILS 1 /**< maximum number of fails after which we give up calling NLP local search */
95 #define MINTIMELEFT 1.0 /**< don't start expensive parts of the heuristics if less than this amount of time left */
96 #define SUBMIPSETUPCOSTS 200 /**< number of nodes equivalent for the costs for setting up the sub-CIP */
97 
98 
99 /*
100  * Data structures
101  */
102 
103 /** primal heuristic data */
104 struct SCIP_HeurData
105 {
106  SCIP_CONSHDLR** nlconshdlrs; /**< array of nonlinear constraint handlers */
107  SCIP_HEUR* nlpheur; /**< pointer to NLP local search heuristics */
108  char* fixingalts; /**< sequence of fixing values used: 'l'p relaxation, 'n'lp relaxation, 'i'ncumbent solution */
109  SCIP_Longint maxnodes; /**< maximum number of nodes to regard in the subproblem */
110  SCIP_Longint minnodes; /**< minimum number of nodes to regard in the subproblem */
111  SCIP_Longint nodesofs; /**< number of nodes added to the contingent of the total nodes */
112  SCIP_Longint nusednodes; /**< nodes already used by heuristic in earlier calls */
113  SCIP_Real conflictweight; /**< weight for conflict score in fixing order */
114  SCIP_Real cutoffweight; /**< weight for cutoff score in fixing order */
115  SCIP_Real inferenceweight; /**< weight for inference score in foxing order */
116  SCIP_Real maxcoversizevars; /**< maximum coversize (as fraction of total number of variables) */
117  SCIP_Real maxcoversizeconss; /**< maximum coversize maximum coversize (as ratio to the percentage of non-affected constraints) */
118  SCIP_Real mincoveredrel; /**< minimum percentage of nonlinear constraints in the original problem */
119  SCIP_Real minimprove; /**< factor by which heuristic should at least improve the incumbent */
120  SCIP_Real nodesquot; /**< subproblem nodes in relation to nodes of the original problem */
121  SCIP_Real recoverdiv; /**< fraction of covering variables in the last cover which need to change their value when recovering */
122  int mincoveredabs; /**< minimum number of nonlinear constraints in the original problem */
123  int maxbacktracks; /**< maximum number of backtracks */
124  int maxrecovers; /**< maximum number of recoverings */
125  int maxreorders; /**< maximum number of reorderings of the fixing order */
126  int nfixingalts; /**< number of fixing alternatives */
127  int nnlpfails; /**< number of fails when solving the NLP relaxation after last success */
128  int npostnlpfails; /**< number of fails of the NLP local search after last success */
129  int nnlconshdlrs; /**< number of nonlinear constraint handlers */
130  char coveringobj; /**< objective function of the covering problem */
131  char fixingorder; /**< order in which variables should be fixed */
132  SCIP_Bool beforecuts; /**< should undercover be called at root node before cut separation? */
133  SCIP_Bool fixintfirst; /**< should integer variables in the cover be fixed first? */
134  SCIP_Bool globalbounds; /**< should global bounds on variables be used instead of local bounds at focus node? */
135  SCIP_Bool locksrounding; /**< shall LP values for integer vars be rounded according to locks? */
136  SCIP_Bool nlpsolved; /**< has current NLP relaxation already been solved successfully? */
137  SCIP_Bool nlpfailed; /**< has solving the NLP relaxation failed? */
138  SCIP_Bool onlyconvexify; /**< should we only fix/dom.red. variables creating nonconvexity? */
139  SCIP_Bool postnlp; /**< should the NLP heuristic be called to polish a feasible solution? */
140  SCIP_Bool coverbd; /**< should bounddisjunction constraints be covered (or just copied)? */
141  SCIP_Bool reusecover; /**< shall the cover be re-used if a conflict was added after an infeasible subproblem? */
142  SCIP_Bool copycuts; /**< should all active cuts from cutpool be copied to constraints in
143  * subproblem? */
144 };
145 
146 /** working memory for retrieving dense sparsity of Hessian matrices */
147 struct HessianData
148 {
149  SCIP_SOL* evalsol;
150  SCIP_Real* varvals;
151  SCIP_Bool* sparsity;
152  int nvars;
153 };
154 
155 /*
156  * Local methods
157  */
158 
159 
160 /** determines, whether a variable is fixed to the given value */
161 static
163  SCIP* scip, /**< SCIP data structure */
164  SCIP_VAR* var, /**< variable to check */
165  SCIP_Real val, /**< value to check */
166  SCIP_Bool global /**< should global bounds be used? */
167  )
168 {
169  SCIP_Bool isfixed;
170 
171  if( global )
172  isfixed = SCIPisFeasEQ(scip, val, SCIPvarGetLbGlobal(var)) && SCIPisFeasEQ(scip, val, SCIPvarGetUbGlobal(var));
173  else
174  isfixed = SCIPisFeasEQ(scip, val, SCIPvarGetLbLocal(var)) && SCIPisFeasEQ(scip, val, SCIPvarGetUbLocal(var));
175 
176  return isfixed;
177 }
178 
179 
180 /** determines, whether a term is already constant, because the variable is fixed or the coefficient is zero */
181 static
183  SCIP* scip, /**< SCIP data structure */
184  SCIP_VAR* var, /**< variable to check */
185  SCIP_Real coeff, /**< coefficient to check */
186  SCIP_Bool global /**< should global bounds be used? */
187  )
188 {
189  /* if the variable has zero coefficient in the original problem, the term is linear */
190  if( SCIPisZero(scip, coeff) )
191  return TRUE;
192 
193  /* if the variable is fixed in the original problem, the term is linear */
194  if( global )
195  return SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
196  else
197  return SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
198 
199 }
200 
201 
202 /** determines, whether a term is convex */
203 static
205  SCIP* scip, /**< SCIP data structure */
206  SCIP_Real lhs, /**< left hand side of the constraint */
207  SCIP_Real rhs, /**< right hand side of the constraint */
208  SCIP_Bool sign /**< signature of the term */
209  )
210 {
211  return sign ? SCIPisInfinity(scip, -lhs) : SCIPisInfinity(scip, rhs);
212 }
213 
214 
215 /** increases counters */
216 static
218  int* termcounter, /**< array to count in how many nonlinear terms a variable appears */
219  int* conscounter, /**< array to count in how many constraints a variable appears */
220  SCIP_Bool* consmarker, /**< was this variable already counted for this constraint? */
221  int idx /**< problem index of the variable */
222  )
223 {
224  termcounter[idx]++;
225  if( !consmarker[idx] )
226  {
227  conscounter[idx]++;
228  consmarker[idx] = TRUE;
229  }
230  return;
231 }
232 
233 
234 /** update time limit */
235 static
237  SCIP* scip, /**< SCIP data structure */
238  SCIP_CLOCK* clck, /**< clock timer */
239  SCIP_Real* timelimit /**< time limit */
240  )
241 {
242  *timelimit -= SCIPgetClockTime(scip, clck);
243  SCIP_CALL( SCIPresetClock(scip, clck) );
244  SCIP_CALL( SCIPstartClock(scip, clck) );
245 
246  return SCIP_OKAY;
247 }
248 
249 
250 /** analyzes a nonlinear row and adds constraints and fixings to the covering problem */
251 static
253  SCIP* scip, /**< original SCIP data structure */
254  SCIP_NLROW* nlrow, /**< nonlinear row representation of a nonlinear constraint */
255  SCIP_EXPRINT* exprint, /**< expression interpreter for computing sparsity pattern of the Hessian;
256  * if NULL, we will simply fix all variables in the expression tree */
257  struct HessianData* hessiandata, /**< working memory for retrieving dense sparsity of Hessian matrices */
258  SCIP* coveringscip, /**< SCIP data structure for the covering problem */
259  int nvars, /**< number of variables */
260  SCIP_VAR** coveringvars, /**< array to store the covering problem's variables */
261  int* termcounter, /**< counter array for number of nonlinear nonzeros per variable */
262  int* conscounter, /**< counter array for number of nonlinear constraints per variable */
263  SCIP_Bool* consmarker, /**< marker array if constraint has been counted in conscounter */
264  SCIP_Bool globalbounds, /**< should global bounds on variables be used instead of local bounds at focus node? */
265  SCIP_Bool onlyconvexify, /**< should we only fix/dom.red. variables creating nonconvexity? */
266  SCIP_Bool* success /**< pointer to store whether row was processed successfully */
267  )
268 {
269  SCIP_EXPRTREE* exprtree;
270  SCIP_Bool infeas;
271  SCIP_Bool fixed;
272  int t;
273  char name[SCIP_MAXSTRLEN];
274 
275  assert(scip != NULL);
276  assert(nlrow != NULL);
277  assert(exprint == NULL || hessiandata != NULL);
278  assert(coveringscip != NULL);
279  assert(nvars >= 1);
280  assert(coveringvars != NULL);
281  assert(termcounter != NULL);
282  assert(conscounter != NULL);
283  assert(consmarker != NULL);
284  assert(success != NULL);
285 
286  *success = FALSE;
287  BMSclearMemoryArray(consmarker, nvars);
288 
289  /* go through expression tree */
290  exprtree = SCIPnlrowGetExprtree(nlrow);
291  if( exprtree != NULL )
292  {
293  SCIP_VAR** exprtreevars;
294  int nexprtreevars;
295  int probidx1;
296  int probidx2;
297 
298  /* get variables in expression tree */
299  nexprtreevars = SCIPexprtreeGetNVars(exprtree);
300  exprtreevars = SCIPexprtreeGetVars(exprtree);
301 
302  if( exprtreevars != NULL && nexprtreevars > 0 )
303  {
304  SCIP_Bool usehessian;
305  int i;
306 
307  /* is an expression interpreter available which can return the sparsity pattern of the Hessian? */
308  usehessian = exprint != NULL && (SCIPexprintGetCapability() & SCIP_EXPRINTCAPABILITY_HESSIAN);
309  if( usehessian )
310  {
311  int idx1;
312  int idx2;
313 
314  assert(hessiandata != NULL);
315  assert(hessiandata->nvars == 0 || hessiandata->varvals != NULL);
316  assert(hessiandata->nvars == 0 || hessiandata->sparsity != NULL);
317 
318  /* compile expression tree */
319  SCIP_CALL( SCIPexprintCompile(exprint, exprtree) );
320 
321  /* ensure memory */
322  if( hessiandata->nvars < nexprtreevars )
323  {
324  SCIP_CALL( SCIPreallocBufferArray(scip, &hessiandata->varvals, nexprtreevars) );
325  SCIP_CALL( SCIPreallocBufferArray(scip, &hessiandata->sparsity, nexprtreevars*nexprtreevars) );
326  hessiandata->nvars = nexprtreevars;
327  }
328 
329  /* get point at which to evaluate the Hessian sparsity */
330  if( hessiandata->evalsol == NULL )
331  {
332  SCIP_CALL( SCIPcreateSol(scip, &hessiandata->evalsol, NULL) );
333  SCIP_CALL( SCIPlinkCurrentSol(scip, hessiandata->evalsol) );
334  }
335  SCIP_CALL( SCIPgetSolVals(scip, hessiandata->evalsol, nexprtreevars, exprtreevars, hessiandata->varvals) );
336 
337  /* get sparsity of the Hessian at current LP solution */
338  SCIP_CALL( SCIPexprintHessianSparsityDense(exprint, exprtree, hessiandata->varvals, hessiandata->sparsity) );
339 
340  for( idx1 = nexprtreevars-1; idx1 >= 0; idx1-- )
341  {
342  /* if constraints with inactive variables are present, we will have difficulties creating the sub-CIP later */
343  probidx1 = SCIPvarGetProbindex(exprtreevars[idx1]);
344  if( probidx1 == -1 )
345  {
346  SCIPdebugMessage("strange: inactive variables detected in nonlinear row <%s>\n", SCIPnlrowGetName(nlrow));
347  return SCIP_OKAY;
348  }
349 
350  /* nonzero diagonal element of the Hessian: fix */
351  if( hessiandata->sparsity[idx1*nexprtreevars + idx1]
352  && !termIsConstant(scip, exprtreevars[idx1], 1.0, globalbounds) )
353  {
354  SCIP_CALL( SCIPfixVar(coveringscip, coveringvars[probidx1], 1.0, &infeas, &fixed) );
355  assert(!infeas);
356  assert(fixed);
357 
358  /* update counters */
359  incCounters(termcounter, conscounter, consmarker, probidx1);
360 
361  SCIPdebugMessage("fixing var <%s> in covering problem to 1\n", SCIPvarGetName(coveringvars[probidx1]));
362 
363  /* if covering variable is fixed, then no need to still check non-diagonal elements */
364  continue;
365  }
366 
367  /* two different variables relate nonlinearly */
368  for( idx2 = nexprtreevars-1; idx2 > idx1; idx2-- )
369  {
370  SCIP_CONS* coveringcons;
371  SCIP_VAR* coveringconsvars[2];
372 
373  /* do not assume symmetry */
374  if( !hessiandata->sparsity[idx1*nexprtreevars + idx2] && !hessiandata->sparsity[idx2*nexprtreevars + idx1] )
375  continue;
376 
377  /* if diagonal has entry already, then covering constraint would always be satisfied, thus no need to add */
378  if( hessiandata->sparsity[idx2*nexprtreevars + idx2] && !termIsConstant(scip, exprtreevars[idx2], 1.0, globalbounds) )
379  continue;
380 
381  /* if constraints with inactive variables are present, we will have difficulties creating the sub-CIP later */
382  probidx2 = SCIPvarGetProbindex(exprtreevars[idx2]);
383  if( probidx2 == -1 )
384  {
385  SCIPdebugMessage("strange: inactive variables detected in nonlinear row <%s>\n", SCIPnlrowGetName(nlrow));
386  return SCIP_OKAY;
387  }
388 
389  /* if the term is linear because one of the variables is fixed, nothing to do */
390  if( termIsConstant(scip, exprtreevars[idx1], 1.0, globalbounds)
391  || termIsConstant(scip, exprtreevars[idx2], 1.0, globalbounds) )
392  continue;
393 
394  /* create covering constraint */
395  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering_%d_%d", SCIPnlrowGetName(nlrow), idx1, idx2);
396  coveringconsvars[0] = coveringvars[probidx1];
397  coveringconsvars[1] = coveringvars[probidx2];
398  SCIP_CALL( SCIPcreateConsSetcover(coveringscip, &coveringcons, name, 2, coveringconsvars,
399  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
400 
401  if( coveringcons == NULL )
402  {
403  SCIPdebugMessage("failed to create set covering constraint <%s>\n", name);
404  return SCIP_OKAY;
405  }
406 
407  /* add and release covering constraint */
408  SCIP_CALL( SCIPaddCons(coveringscip, coveringcons) );
409  SCIP_CALL( SCIPreleaseCons(coveringscip, &coveringcons) );
410 
411  SCIPdebugMessage("added covering constraint for vars <%s> and <%s> in covering problem\n", SCIPvarGetName(coveringvars[probidx1]), SCIPvarGetName(coveringvars[probidx2]));
412 
413  /* update counters for both variables */
414  incCounters(termcounter, conscounter, consmarker, probidx1);
415  incCounters(termcounter, conscounter, consmarker, probidx2);
416  }
417  }
418  }
419  /* fix all variables contained in the expression tree */
420  else
421  {
422  for( i = nexprtreevars-1; i >= 0; i-- )
423  {
424  assert(exprtreevars[i] != NULL);
425 
426  /* if constraints with inactive variables are present, we will have difficulties creating the sub-CIP later */
427  probidx1 = SCIPvarGetProbindex(exprtreevars[i]);
428  if( probidx1 == -1 )
429  {
430  SCIPdebugMessage("strange: inactive variable <%s> detected in nonlinear row <%s>\n",
431  SCIPvarGetName(exprtreevars[i]), SCIPnlrowGetName(nlrow));
432  return SCIP_OKAY;
433  }
434 
435  /* term is constant, nothing to do */
436  if( termIsConstant(scip, exprtreevars[i], 1.0, globalbounds) )
437  continue;
438 
439  /* otherwise fix variable */
440  SCIP_CALL( SCIPfixVar(coveringscip, coveringvars[probidx1], 1.0, &infeas, &fixed) );
441  assert(!infeas);
442  assert(fixed);
443 
444  /* update counters */
445  incCounters(termcounter, conscounter, consmarker, probidx1);
446 
447  SCIPdebugMessage("fixing var <%s> in covering problem to 1\n", SCIPvarGetName(coveringvars[probidx1]));
448  }
449  }
450  }
451  }
452 
453  /* go through all quadratic terms */
454  for( t = SCIPnlrowGetNQuadElems(nlrow)-1; t >= 0; t-- )
455  {
456  SCIP_QUADELEM* quadelem;
457  SCIP_VAR* bilinvar1;
458  SCIP_VAR* bilinvar2;
459  int probidx1;
460  int probidx2;
461 
462  /* get quadratic term */
463  quadelem = &SCIPnlrowGetQuadElems(nlrow)[t];
464 
465  /* get involved variables */
466  bilinvar1 = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx1];
467  bilinvar2 = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx2];
468  assert(bilinvar1 != NULL);
469  assert(bilinvar2 != NULL);
470 
471  /* if constraints with inactive variables are present, we will have difficulties creating the sub-CIP later */
472  probidx1 = SCIPvarGetProbindex(bilinvar1);
473  probidx2 = SCIPvarGetProbindex(bilinvar2);
474  if( probidx1 == -1 || probidx2 == -1 )
475  {
476  SCIPdebugMessage("inactive variables detected in nonlinear row <%s>\n", SCIPnlrowGetName(nlrow));
477  return SCIP_OKAY;
478  }
479 
480  /* we have a square term */
481  if( bilinvar1 == bilinvar2 )
482  {
483  /* term is constant, nothing to do */
484  if( termIsConstant(scip, bilinvar1, quadelem->coef, globalbounds) )
485  continue;
486 
487  /* if we only convexify and term is convex considering the bounds of the nlrow, nothing to do */
488  if( onlyconvexify && termIsConvex(scip, SCIPnlrowGetLhs(nlrow), SCIPnlrowGetRhs(nlrow), quadelem->coef >= 0) )
489  continue;
490 
491  /* otherwise variable has to be in the cover */
492  SCIP_CALL( SCIPfixVar(coveringscip, coveringvars[probidx1], 1.0, &infeas, &fixed) );
493  assert(!infeas);
494  assert(fixed);
495 
496  /* update counters */
497  incCounters(termcounter, conscounter, consmarker, probidx1);
498 
499  SCIPdebugMessage("fixing var <%s> in covering problem to 1\n", SCIPvarGetName(coveringvars[probidx1]));
500  }
501  /* we have a bilinear term */
502  else
503  {
504  SCIP_CONS* coveringcons;
505  SCIP_VAR* coveringconsvars[2];
506 
507  /* if the term is linear because one of the variables is fixed or the coefficient is zero, nothing to do */
508  if( termIsConstant(scip, bilinvar1, quadelem->coef, globalbounds)
509  || termIsConstant(scip, bilinvar2, quadelem->coef, globalbounds) )
510  continue;
511 
512  /* create covering constraint */
513  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering%d", SCIPnlrowGetName(nlrow), t);
514  coveringconsvars[0] = coveringvars[probidx1];
515  coveringconsvars[1] = coveringvars[probidx2];
516  SCIP_CALL( SCIPcreateConsSetcover(coveringscip, &coveringcons, name, 2, coveringconsvars,
517  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
518 
519  if( coveringcons == NULL )
520  {
521  SCIPdebugMessage("failed to create set covering constraint <%s>\n", name);
522  return SCIP_OKAY;
523  }
524 
525  /* add and release covering constraint */
526  SCIP_CALL( SCIPaddCons(coveringscip, coveringcons) );
527  SCIP_CALL( SCIPreleaseCons(coveringscip, &coveringcons) );
528 
529  /* update counters for both variables */
530  incCounters(termcounter, conscounter, consmarker, probidx1);
531  incCounters(termcounter, conscounter, consmarker, probidx2);
532  }
533  }
534 
535  *success = TRUE;
536 
537  return SCIP_OKAY;
538 }
539 
540 
541 /** creates the covering problem to determine a number of variables to be fixed */
542 static
544  SCIP* scip, /**< original SCIP data structure */
545  SCIP* coveringscip, /**< SCIP data structure for the covering problem */
546  SCIP_VAR** coveringvars, /**< array to store the covering problem's variables */
547  SCIP_Bool globalbounds, /**< should global bounds on variables be used instead of local bounds at focus node? */
548  SCIP_Bool onlyconvexify, /**< should we only fix/dom.red. variables creating nonconvexity? */
549  SCIP_Bool coverbd, /**< should bounddisjunction constraints be covered (or just copied)? */
550  char coveringobj, /**< objective function of the covering problem */
551  SCIP_Bool* success /**< pointer to store whether the problem was created successfully */
552  )
553 {
554  SCIP_VAR** vars;
555  SCIP_CONSHDLR* conshdlr;
556  SCIP_HASHMAP* nlrowmap;
557  SCIP_EXPRINT* exprint;
558  SCIP_Bool* consmarker;
559  int* conscounter;
560  int* termcounter;
561 
562  struct HessianData hessiandata;
563  int nlocksup;
564  int nlocksdown;
565  int nvars;
566  int i;
567  int probindex;
568  char name[SCIP_MAXSTRLEN];
569 
570  assert(scip != NULL);
571  assert(coveringscip != NULL);
572  assert(coveringvars != NULL);
573  assert(success != NULL);
574 
575  *success = FALSE;
576 
577  /* get required data of the original problem */
578  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
579 
580  /* create problem data structure */
581  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering", SCIPgetProbName(scip));
582  SCIP_CALL( SCIPcreateProb(coveringscip, name, NULL, NULL, NULL, NULL, NULL, NULL, NULL) );
583 
584  /* allocate and initialize to zero counter arrays for weighted objectives */
585  SCIP_CALL( SCIPallocBufferArray(scip, &consmarker, nvars) );
586  SCIP_CALL( SCIPallocBufferArray(scip, &conscounter, nvars) );
587  SCIP_CALL( SCIPallocBufferArray(scip, &termcounter, nvars) );
588  BMSclearMemoryArray(conscounter, nvars);
589  BMSclearMemoryArray(termcounter, nvars);
590 
591  /* create expression interpreter */
592  SCIP_CALL( SCIPexprintCreate(SCIPblkmem(scip), &exprint) );
593  assert(exprint != NULL);
594 
595  /* initialize empty hessiandata; memory will be allocated in method processNlRow() as required */
596  hessiandata.evalsol = NULL;
597  hessiandata.varvals = NULL;
598  hessiandata.sparsity = NULL;
599  hessiandata.nvars = 0;
600 
601  /* create covering variable for each variable in the original problem (fix it or not?) in the same order as in the
602  * original problem
603  */
604  for( i = 0; i < nvars; i++ )
605  {
606  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering", SCIPvarGetName(vars[i]));
607  SCIP_CALL( SCIPcreateVar(coveringscip, &coveringvars[i], name, 0.0, 1.0, 1.0, SCIP_VARTYPE_BINARY,
608  TRUE, FALSE, NULL, NULL, NULL, NULL, NULL) );
609  assert(coveringvars[i] != NULL);
610  SCIP_CALL( SCIPaddVar(coveringscip, coveringvars[i]) );
611  }
612 
613  /* first, go through some special constraint handlers which we do not want to treat by looking at their nlrow
614  * representation; we store these in a hash map and afterwards process all nlrows which are not found in the hash map
615  */
616  nlrowmap = NULL;
617  if( SCIPisNLPConstructed(scip) )
618  {
619  int nnlprows;
620 
621  nnlprows = SCIPgetNNLPNlRows(scip);
622  if( nnlprows > 0 )
623  {
624  int mapsize;
625 
626  /* calculate size of hash map */
627  conshdlr = SCIPfindConshdlr(scip, "quadratic");
628  mapsize = SCIPconshdlrGetNActiveConss(conshdlr);
629  conshdlr = SCIPfindConshdlr(scip, "soc");
630  mapsize += SCIPconshdlrGetNActiveConss(conshdlr);
631  mapsize = MAX(mapsize, nnlprows);
632  mapsize = SCIPcalcHashtableSize(2*mapsize);
633  assert(mapsize > 0);
634 
635  /* create hash map */
636  SCIP_CALL( SCIPhashmapCreate(&nlrowmap, SCIPblkmem(scip), mapsize) );
637  assert(nlrowmap != NULL);
638  }
639  }
640 
641  /* go through all and constraints in the original problem */
642  conshdlr = SCIPfindConshdlr(scip, "and");
643  if( conshdlr != NULL )
644  {
645  int c;
646 
647  for( c = SCIPconshdlrGetNActiveConss(conshdlr)-1; c >= 0; c-- )
648  {
649  SCIP_CONS* andcons;
650  SCIP_CONS* coveringcons;
651  SCIP_VAR** andvars;
652  SCIP_VAR* andres;
653  SCIP_VAR** coveringconsvars;
654  SCIP_Real* coveringconsvals;
655  SCIP_Bool negated;
656 
657  int ntofix;
658  int v;
659 
660  /* get constraint and variables */
661  andcons = SCIPconshdlrGetConss(conshdlr)[c];
662  assert(andcons != NULL);
663  andvars = SCIPgetVarsAnd(scip, andcons);
664  assert(andvars != NULL);
665 
666  /* "and" constraints are not passed to the NLP, hence nothing to store in the hash map */
667 
668  /* allocate memory for covering constraint */
669  SCIP_CALL( SCIPallocBufferArray(coveringscip, &coveringconsvars, SCIPgetNVarsAnd(scip, andcons)+1) );
670  SCIP_CALL( SCIPallocBufferArray(coveringscip, &coveringconsvals, SCIPgetNVarsAnd(scip, andcons)+1) );
671 
672  /* collect unfixed variables */
673  BMSclearMemoryArray(consmarker, nvars);
674  ntofix = 0;
675  for( v = SCIPgetNVarsAnd(scip, andcons)-1; v >= 0; v-- )
676  {
677  assert(andvars[v] != NULL);
678  negated = FALSE;
679 
680  /* if variable is fixed to 0, entire constraint can be linearized */
681  if( varIsFixed(scip, andvars[v], 0.0, globalbounds) )
682  {
683  ntofix = 0;
684  break;
685  }
686 
687  /* if variable is fixed, nothing to do */
688  if( termIsConstant(scip, andvars[v], 1.0, globalbounds) )
689  {
690  continue;
691  }
692 
693  /* if constraints with inactive variables are present, we have to find the corresponding active variable */
694  probindex = SCIPvarGetProbindex(andvars[v]);
695  if( probindex == -1 )
696  {
697  SCIP_VAR* repvar;
698 
699  /* get binary representative of variable */
700  SCIP_CALL( SCIPgetBinvarRepresentative(scip, andvars[v], &repvar, &negated) );
701  assert(repvar != NULL);
702  assert(SCIPvarGetStatus(repvar) != SCIP_VARSTATUS_FIXED);
703 
705  {
706  SCIPdebugMessage("strange: multiaggregated variable found <%s>\n", SCIPvarGetName(andvars[v]));
707  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(andcons));
708  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
709  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
710  goto TERMINATE;
711  }
712 
713  /* check for negation */
714  if( SCIPvarIsNegated(repvar) )
715  {
716  probindex = SCIPvarGetProbindex(SCIPvarGetNegationVar(repvar));
717  negated = TRUE;
718  }
719  else
720  {
721  assert(SCIPvarIsActive(repvar));
722  probindex = SCIPvarGetProbindex(repvar);
723  negated = FALSE;
724  }
725  }
726  assert(probindex >= 0);
727 
728  /* add covering variable for unfixed original variable */
729  if( negated )
730  {
731  SCIP_CALL( SCIPgetNegatedVar(coveringscip, coveringvars[probindex], &coveringconsvars[ntofix]) );
732  }
733  else
734  coveringconsvars[ntofix] = coveringvars[probindex];
735  coveringconsvals[ntofix] = 1.0;
736  ntofix++;
737  }
738  negated = FALSE;
739 
740  /* if constraints with inactive variables are present, we have to find the corresponding active variable */
741  andres = SCIPgetResultantAnd(scip, andcons);
742  assert(andres != NULL);
743  probindex = SCIPvarGetProbindex(andres);
744 
745  /* if resultant is fixed this constraint can be either linearized or is redundant because all operands can be fixed */
746  if( termIsConstant(scip, andres, 1.0, globalbounds) )
747  {
748  /* free memory for covering constraint */
749  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
750  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
751 
752  continue;
753  }
754 
755  if( probindex == -1 )
756  {
757  SCIP_VAR* repvar;
758 
759  /* get binary representative of variable */
760  SCIP_CALL( SCIPgetBinvarRepresentative(scip, SCIPgetResultantAnd(scip, andcons), &repvar, &negated) );
761  assert(repvar != NULL);
762  assert(SCIPvarGetStatus(repvar) != SCIP_VARSTATUS_FIXED);
763 
765  {
766  SCIPdebugMessage("strange: multiaggregated variable found <%s>\n", SCIPvarGetName(SCIPgetResultantAnd(scip, andcons)));
767  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(andcons));
768  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
769  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
770  goto TERMINATE;
771  }
772 
773  /* check for negation */
774  if( SCIPvarIsNegated(repvar) )
775  {
776  probindex = SCIPvarGetProbindex(SCIPvarGetNegationVar(repvar));
777  negated = TRUE;
778  }
779  else
780  {
781  assert(SCIPvarIsActive(repvar));
782  probindex = SCIPvarGetProbindex(repvar);
783  negated = FALSE;
784  }
785  }
786  else if( SCIPvarGetLbGlobal(andres) > 0.5 || SCIPvarGetUbGlobal(andres) < 0.5 )
787  {
788  /* free memory for covering constraint */
789  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
790  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
791 
792  continue;
793  }
794  assert(probindex >= 0);
795  assert(!termIsConstant(scip, (negated ? SCIPvarGetNegatedVar(vars[probindex]) : vars[probindex]), 1.0, globalbounds));
796 
797  /* if less than two variables are unfixed or the resultant variable is fixed, the entire constraint can be
798  * linearized anyway
799  */
800  if( ntofix >= 2 )
801  {
802  assert(ntofix <= SCIPgetNVarsAnd(scip, andcons));
803 
804  /* add covering variable for unfixed resultant */
805  if( negated )
806  {
807  SCIP_CALL( SCIPgetNegatedVar(coveringscip, coveringvars[probindex], &coveringconsvars[ntofix]) );
808  }
809  else
810  coveringconsvars[ntofix] = coveringvars[probindex];
811  coveringconsvals[ntofix] = (SCIP_Real)(ntofix - 1);
812  ntofix++;
813 
814  /* create covering constraint */
815  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering", SCIPconsGetName(andcons));
816  SCIP_CALL( SCIPcreateConsLinear(coveringscip, &coveringcons, name, ntofix, coveringconsvars, coveringconsvals,
817  (SCIP_Real)(ntofix - 2), SCIPinfinity(coveringscip),
818  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
819 
820  if( coveringcons == NULL )
821  {
822  SCIPdebugMessage("failed to create linear constraint <%s>\n", name);
823  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
824  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
825  goto TERMINATE;
826  }
827 
828  /* add and release covering constraint */
829  SCIP_CALL( SCIPaddCons(coveringscip, coveringcons) );
830  SCIP_CALL( SCIPreleaseCons(coveringscip, &coveringcons) );
831 
832  /* update counters */
833  for( v = ntofix-1; v >= 0; v-- )
834  if( SCIPvarIsNegated(coveringconsvars[v]) )
835  incCounters(termcounter, conscounter, consmarker, SCIPvarGetProbindex(SCIPvarGetNegationVar(coveringconsvars[v])));
836  else
837  incCounters(termcounter, conscounter, consmarker, SCIPvarGetProbindex(coveringconsvars[v]));
838  }
839 
840  /* free memory for covering constraint */
841  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
842  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
843  }
844  }
845 
846  /* go through all bounddisjunction constraints in the original problem */
847  conshdlr = SCIPfindConshdlr(scip, "bounddisjunction");
848  if( conshdlr != NULL && coverbd )
849  {
850  int c;
851 
852  for( c = SCIPconshdlrGetNActiveConss(conshdlr)-1; c >= 0; c-- )
853  {
854  SCIP_CONS* bdcons;
855  SCIP_CONS* coveringcons;
856  SCIP_VAR** bdvars;
857  SCIP_VAR** coveringconsvars;
858  SCIP_Real* coveringconsvals;
859 
860  int nbdvars;
861  int ntofix;
862  int v;
863 
864  /* get constraint and variables */
865  bdcons = SCIPconshdlrGetConss(conshdlr)[c];
866  assert(bdcons != NULL);
867  bdvars = SCIPgetVarsBounddisjunction(scip, bdcons);
868  assert(bdvars != NULL);
869  nbdvars = SCIPgetNVarsBounddisjunction(scip, bdcons);
870 
871  /* bounddisjunction constraints are not passed to the NLP, hence nothing to store in the hash map */
872 
873  /* allocate memory for covering constraint */
874  SCIP_CALL( SCIPallocBufferArray(coveringscip, &coveringconsvars, nbdvars) );
875  SCIP_CALL( SCIPallocBufferArray(coveringscip, &coveringconsvals, nbdvars) );
876 
877  /* collect unfixed variables */
878  BMSclearMemoryArray(consmarker, nvars);
879  ntofix = 0;
880  for( v = nbdvars-1; v >= 0; v-- )
881  {
882  SCIP_Bool negated;
883 
884  assert(bdvars[v] != NULL);
885  negated = FALSE;
886 
887  /* if variable is fixed, nothing to do */
888  if( varIsFixed(scip, bdvars[v], globalbounds ? SCIPvarGetLbGlobal(bdvars[v]) : SCIPvarGetLbLocal(bdvars[v]),
889  globalbounds) )
890  {
891  continue;
892  }
893 
894  /* if constraints with inactive variables are present, we have to find the corresponding active variable */
895  probindex = SCIPvarGetProbindex(bdvars[v]);
896  if( probindex == -1 )
897  {
898  SCIP_VAR* repvar;
899 
900  /* get binary representative of variable */
901  SCIP_CALL( SCIPgetBinvarRepresentative(scip, bdvars[v], &repvar, &negated) );
902  assert(repvar != NULL);
903  assert(SCIPvarGetStatus(repvar) != SCIP_VARSTATUS_FIXED);
904 
906  {
907  SCIPdebugMessage("strange: multiaggregated variable found <%s>\n", SCIPvarGetName(bdvars[v]));
908  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(bdcons));
909  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
910  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
911  goto TERMINATE;
912  }
913 
914  /* check for negation */
915  if( SCIPvarIsNegated(repvar) )
916  {
917  probindex = SCIPvarGetProbindex(SCIPvarGetNegationVar(repvar));
918  negated = TRUE;
919  }
920  else
921  {
922  assert(SCIPvarIsActive(repvar));
923  probindex = SCIPvarGetProbindex(repvar);
924  negated = FALSE;
925  }
926  }
927  assert(probindex >= 0);
928 
929  /* add covering variable for unfixed original variable */
930  if( negated )
931  {
932  SCIP_CALL( SCIPgetNegatedVar(coveringscip, coveringvars[probindex], &coveringconsvars[ntofix]) );
933  }
934  else
935  coveringconsvars[ntofix] = coveringvars[probindex];
936  coveringconsvals[ntofix] = 1.0;
937  ntofix++;
938  }
939 
940  /* if less than two variables are unfixed, the entire constraint can be linearized anyway */
941  if( ntofix >= 2 )
942  {
943  assert(ntofix <= nbdvars);
944 
945  /* create covering constraint */
946  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering", SCIPconsGetName(bdcons));
947  SCIP_CALL( SCIPcreateConsLinear(coveringscip, &coveringcons, name, ntofix, coveringconsvars, coveringconsvals,
948  (SCIP_Real)(ntofix - 1), SCIPinfinity(coveringscip),
949  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
950 
951  if( coveringcons == NULL )
952  {
953  SCIPdebugMessage("failed to create linear constraint <%s>\n", name);
954  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
955  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
956  goto TERMINATE;
957  }
958 
959  /* add and release covering constraint */
960  SCIP_CALL( SCIPaddCons(coveringscip, coveringcons) );
961  SCIP_CALL( SCIPreleaseCons(coveringscip, &coveringcons) );
962 
963  /* update counters */
964  for( v = ntofix-1; v >= 0; v-- )
965  if( SCIPvarIsNegated(coveringconsvars[v]) )
966  incCounters(termcounter, conscounter, consmarker, SCIPvarGetProbindex(SCIPvarGetNegationVar(coveringconsvars[v])));
967  else
968  incCounters(termcounter, conscounter, consmarker, SCIPvarGetProbindex(coveringconsvars[v]));
969  }
970 
971  /* free memory for covering constraint */
972  SCIPfreeBufferArray(coveringscip, &coveringconsvals);
973  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
974  }
975  }
976 
977  /* go through all indicator constraints in the original problem; fix the binary variable */
978  conshdlr = SCIPfindConshdlr(scip, "indicator");
979  if( conshdlr != NULL )
980  {
981  int c;
982 
983  for( c = SCIPconshdlrGetNActiveConss(conshdlr)-1; c >= 0; c-- )
984  {
985  SCIP_CONS* indcons;
986  SCIP_VAR* binvar;
987  SCIP_VAR* coveringvar;
988 
989  /* get constraint and variables */
990  indcons = SCIPconshdlrGetConss(conshdlr)[c];
991  assert(indcons != NULL);
992  binvar = SCIPgetBinaryVarIndicator(indcons);
993  assert(binvar != NULL);
994 
995  /* indicator constraints are not passed to the NLP, hence nothing to store in the hash map */
996 
997  /* if variable is fixed, nothing to do */
998  if( varIsFixed(scip, binvar, globalbounds ? SCIPvarGetLbGlobal(binvar) : SCIPvarGetLbLocal(binvar), globalbounds) )
999  {
1000  continue;
1001  }
1002 
1003  /* if constraints with inactive variables are present, we have to find the corresponding active variable */
1004  probindex = SCIPvarGetProbindex(binvar);
1005  if( probindex == -1 )
1006  {
1007  SCIP_VAR* repvar;
1008  SCIP_Bool negated;
1009 
1010  /* get binary representative of variable */
1011  negated = FALSE;
1012  SCIP_CALL( SCIPgetBinvarRepresentative(scip, binvar, &repvar, &negated) );
1013  assert(repvar != NULL);
1014  assert(SCIPvarGetStatus(repvar) != SCIP_VARSTATUS_FIXED);
1015 
1016  if( SCIPvarGetStatus(repvar) == SCIP_VARSTATUS_MULTAGGR )
1017  {
1018  SCIPdebugMessage("strange: multiaggregated variable found <%s>\n", SCIPvarGetName(binvar));
1019  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(indcons));
1020  goto TERMINATE;
1021  }
1022 
1023  /* check for negation */
1024  if( SCIPvarIsNegated(repvar) )
1025  probindex = SCIPvarGetProbindex(SCIPvarGetNegationVar(repvar));
1026  else
1027  {
1028  assert(SCIPvarIsActive(repvar));
1029  probindex = SCIPvarGetProbindex(repvar);
1030  }
1031  }
1032  assert(probindex >= 0);
1033 
1034  /* get covering variable for unfixed binary variable in indicator constraint */
1035  coveringvar = coveringvars[probindex];
1036 
1037  /* require covering variable to be fixed such that indicator is linearized */
1038  SCIP_CALL( SCIPchgVarLb(coveringscip, coveringvar, 1.0) );
1039 
1040  /* update counters */
1041  BMSclearMemoryArray(consmarker, nvars);
1042  incCounters(termcounter, conscounter, consmarker, probindex);
1043  }
1044  }
1045 
1046  /* go through all quadratic constraints in the original problem */
1047  conshdlr = SCIPfindConshdlr(scip, "quadratic");
1048  if( conshdlr != NULL )
1049  {
1050  int c;
1051 
1052  for( c = SCIPconshdlrGetNActiveConss(conshdlr)-1; c >= 0; c-- )
1053  {
1054  SCIP_CONS* quadcons;
1055  SCIP_NLROW* nlrow;
1056 
1057  /* get constraint */
1058  quadcons = SCIPconshdlrGetConss(conshdlr)[c];
1059  assert(quadcons != NULL);
1060 
1061  /* get nlrow representation and store it in hash map */
1062  SCIP_CALL( SCIPgetNlRowQuadratic(scip, quadcons, &nlrow) );
1063  assert(nlrow != NULL);
1064  if( nlrowmap != NULL )
1065  {
1066  assert(!SCIPhashmapExists(nlrowmap, nlrow));
1067  SCIP_CALL( SCIPhashmapInsert(nlrowmap, nlrow, quadcons) );
1068  }
1069 
1070  /* if we only want to convexify and curvature and bounds prove already convexity, nothing to do */
1071  if( onlyconvexify
1072  && ((SCIPisInfinity(scip, -SCIPgetLhsQuadratic(scip, quadcons)) && SCIPisConvexQuadratic(scip, quadcons))
1073  || (SCIPisInfinity(scip, SCIPgetRhsQuadratic(scip, quadcons)) && SCIPisConcaveQuadratic(scip, quadcons))) )
1074  continue;
1075 
1076  /* process nlrow */
1077  *success = FALSE;
1078  SCIP_CALL( processNlRow(scip, nlrow, exprint, &hessiandata, coveringscip, nvars, coveringvars,
1079  termcounter, conscounter, consmarker, globalbounds, onlyconvexify, success) );
1080 
1081  if( *success == FALSE )
1082  goto TERMINATE;
1083  }
1084 
1085  *success = FALSE;
1086  }
1087 
1088  /* go through all "soc" constraints in the original problem */
1089  conshdlr = SCIPfindConshdlr(scip, "soc");
1090  if( conshdlr != NULL && !onlyconvexify )
1091  {
1092  int c;
1093 
1094  for( c = SCIPconshdlrGetNActiveConss(conshdlr)-1; c >= 0; c-- )
1095  {
1096  SCIP_CONS* soccons;
1097  SCIP_CONS* coveringcons;
1098  SCIP_VAR** soclhsvars;
1099  SCIP_VAR* socrhsvar;
1100  SCIP_VAR** coveringconsvars;
1101  SCIP_NLROW* nlrow;
1102 
1103  int ntofix;
1104  int v;
1105 
1106  /* get constraints and variables */
1107  soccons = SCIPconshdlrGetConss(conshdlr)[c];
1108  assert(soccons != NULL);
1109  socrhsvar = SCIPgetRhsVarSOC(scip, soccons);
1110  assert(socrhsvar != NULL);
1111  soclhsvars = SCIPgetLhsVarsSOC(scip, soccons);
1112  assert(soclhsvars != NULL);
1113 
1114  /* get nlrow representation and store it in hash map */
1115  SCIP_CALL( SCIPgetNlRowSOC(scip, soccons, &nlrow) );
1116  assert(nlrow != NULL);
1117  if( nlrowmap != NULL )
1118  {
1119  assert(!SCIPhashmapExists(nlrowmap, nlrow));
1120  SCIP_CALL( SCIPhashmapInsert(nlrowmap, nlrow, soccons) );
1121  }
1122 
1123  /* allocate memory for covering constraint */
1124  SCIP_CALL( SCIPallocBufferArray(coveringscip, &coveringconsvars, SCIPgetNLhsVarsSOC(scip, soccons)+1) );
1125 
1126  /* collect unfixed variables */
1127  BMSclearMemoryArray(consmarker, nvars);
1128  ntofix = 0;
1129 
1130  /* soc constraints should contain only active and multi-aggregated variables; the latter we do not handle */
1131  probindex = SCIPvarGetProbindex(socrhsvar);
1132  if( probindex == -1 )
1133  {
1134  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(soccons));
1135  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
1136  goto TERMINATE;
1137  }
1138 
1139  /* add covering variable for unfixed rhs variable */
1140  if( !termIsConstant(scip, socrhsvar, SCIPgetRhsCoefSOC(scip, soccons), globalbounds) )
1141  {
1142  SCIP_CALL( SCIPgetNegatedVar(coveringscip, coveringvars[probindex], &coveringconsvars[ntofix]) );
1143  ntofix++;
1144  }
1145 
1146  /* go through lhs variables */
1147  for( v = SCIPgetNLhsVarsSOC(scip, soccons)-1; v >= 0; v-- )
1148  {
1149  assert(soclhsvars[v] != NULL);
1150 
1151  /* soc constraints should contain only active and multi-aggregated variables; the latter we do not handle */
1152  probindex = SCIPvarGetProbindex(soclhsvars[v]);
1153  if( probindex == -1 )
1154  {
1155  SCIPdebugMessage("inactive variables detected in constraint <%s>\n", SCIPconsGetName(soccons));
1156  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
1157  goto TERMINATE;
1158  }
1159 
1160  /* add covering variable for unfixed lhs variable */
1161  if( !termIsConstant(scip, soclhsvars[v], SCIPgetLhsCoefsSOC(scip, soccons)[v], globalbounds) )
1162  {
1163  SCIP_CALL( SCIPgetNegatedVar(coveringscip, coveringvars[probindex], &coveringconsvars[ntofix]) );
1164  ntofix++;
1165  }
1166  }
1167 
1168  if( ntofix >= 2 )
1169  {
1170  /* create covering constraint */
1171  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_covering", SCIPconsGetName(soccons));
1172  SCIP_CALL( SCIPcreateConsSetpack(coveringscip, &coveringcons, name, ntofix, coveringconsvars,
1173  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
1174 
1175  if( coveringcons == NULL )
1176  {
1177  SCIPdebugMessage("failed to create set packing constraint <%s>\n", name);
1178  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
1179  goto TERMINATE;
1180  }
1181 
1182  /* add and release covering constraint */
1183  SCIP_CALL( SCIPaddCons(coveringscip, coveringcons) );
1184  SCIP_CALL( SCIPreleaseCons(coveringscip, &coveringcons) );
1185 
1186  /* update counters */
1187  for( v = ntofix-1; v >= 0; v-- )
1188  incCounters(termcounter, conscounter, consmarker, SCIPvarGetProbindex(SCIPvarGetNegatedVar(coveringconsvars[v])));
1189  }
1190 
1191  /* free memory for covering constraint */
1192  SCIPfreeBufferArray(coveringscip, &coveringconsvars);
1193  }
1194  }
1195 
1196  /* go through all yet unprocessed nlrows */
1197  if( nlrowmap != NULL )
1198  {
1199  SCIP_NLROW** nlrows;
1200  int nnlrows;
1201 
1202  assert(SCIPisNLPConstructed(scip));
1203 
1204  /* get nlrows */
1205  nnlrows = SCIPgetNNLPNlRows(scip);
1206  nlrows = SCIPgetNLPNlRows(scip);
1207 
1208  for( i = nnlrows-1; i >= 0; i-- )
1209  {
1210  assert(nlrows[i] != NULL);
1211 
1212  /* nlrow or corresponding constraint already processed */
1213  if( SCIPhashmapExists(nlrowmap, nlrows[i]) )
1214  continue;
1215 
1216  /* process nlrow */
1217  *success = FALSE;
1218  SCIP_CALL( processNlRow(scip, nlrows[i], exprint, &hessiandata, coveringscip, nvars, coveringvars,
1219  termcounter, conscounter, consmarker, globalbounds, onlyconvexify, success) );
1220 
1221  if( *success == FALSE )
1222  goto TERMINATE;
1223  }
1224  }
1225 
1226  /* set objective function of covering problem */
1227  switch( coveringobj )
1228  {
1229  case 'c': /* number of influenced nonlinear constraints */
1230  for( i = nvars-1; i >= 0; i-- )
1231  {
1232  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i], (SCIP_Real) conscounter[i]) );
1233  }
1234  break;
1235  case 'd': /* domain size */
1236  for( i = nvars-1; i >= 0; i-- )
1237  {
1238  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i],
1239  (globalbounds ? SCIPvarGetUbGlobal(vars[i]) - SCIPvarGetLbGlobal(vars[i]) : SCIPvarGetUbLocal(vars[i]) - SCIPvarGetLbLocal(vars[i]))) );
1240  }
1241  break;
1242  case 'l': /* number of locks */
1243  for( i = nvars-1; i >= 0; i-- )
1244  {
1245  nlocksup = SCIPvarGetNLocksUp(vars[i]);
1246  nlocksdown = SCIPvarGetNLocksDown(vars[i]);
1247  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i], (SCIP_Real) (nlocksup+nlocksdown+1)) );
1248  }
1249  break;
1250  case 'm': /* min(up locks, down locks)+1 */
1251  for( i = nvars-1; i >= 0; i-- )
1252  {
1253  nlocksup = SCIPvarGetNLocksUp(vars[i]);
1254  nlocksdown = SCIPvarGetNLocksDown(vars[i]);
1255  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i], (SCIP_Real) (MIN(nlocksup, nlocksdown)+1)) );
1256  }
1257  break;
1258  case 't': /* number of influenced nonlinear terms */
1259  for( i = nvars-1; i >= 0; i-- )
1260  {
1261  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i], (SCIP_Real) termcounter[i]) );
1262  }
1263  break;
1264  case 'u': /* unit penalties */
1265  for( i = nvars-1; i >= 0; i-- )
1266  {
1267  SCIP_CALL( SCIPchgVarObj(coveringscip, coveringvars[i], 1.0) );
1268  }
1269  break;
1270  default:
1271  SCIPerrorMessage("invalid choice <%c> for covering objective\n", coveringobj);
1272  goto TERMINATE;
1273  }
1274 
1275  /* covering problem successfully set up */
1276  *success = TRUE;
1277 
1278  TERMINATE:
1279  /* free nlrow hash map */
1280  if( nlrowmap != NULL )
1281  {
1282  SCIPhashmapFree(&nlrowmap);
1283  }
1284 
1285  /* free hessian data */
1286  if( hessiandata.nvars > 0 )
1287  {
1288  assert(hessiandata.evalsol != NULL);
1289  assert(hessiandata.varvals != NULL);
1290  assert(hessiandata.sparsity != NULL);
1291 
1292  SCIPfreeBufferArray(scip, &hessiandata.sparsity);
1293  SCIPfreeBufferArray(scip, &hessiandata.varvals);
1294 
1295  SCIP_CALL( SCIPfreeSol(scip, &hessiandata.evalsol) );
1296  }
1297  else
1298  {
1299  assert(hessiandata.evalsol == NULL);
1300  assert(hessiandata.varvals == NULL);
1301  assert(hessiandata.sparsity == NULL);
1302  }
1303 
1304  /* free expression interpreter */
1305  SCIP_CALL( SCIPexprintFree(&exprint) );
1306 
1307  SCIPstatistic(
1308  {
1309  int nnonzs;
1310  nnonzs = 0;
1311  for( i = 0; i < nvars; ++i)
1312  nnonzs += termcounter[i];
1313  SCIPstatisticPrintf("UCstats nnz/var: %9.6f\n", nnonzs/(SCIP_Real)nvars);
1314  nnonzs = 0;
1315  for( i = 0; i < nvars; ++i)
1316  if( conscounter[i] > 0 )
1317  nnonzs++;
1318  SCIPstatisticPrintf("UCstats nlvars: %6d\n", nnonzs);
1319  }
1320  );
1321 
1322  /* free counter arrays for weighted objectives */
1323  SCIPfreeBufferArray(scip, &termcounter);
1324  SCIPfreeBufferArray(scip, &conscounter);
1325  SCIPfreeBufferArray(scip, &consmarker);
1326 
1327  return SCIP_OKAY;
1328 }
1329 
1330 
1331 /** adds a constraint to the covering problem to forbid the given cover */
1332 static
1334  SCIP* scip, /**< SCIP data structure of the covering problem */
1335  int nvars, /**< number of variables */
1336  SCIP_VAR** vars, /**< variable array */
1337  int coversize, /**< size of the cover */
1338  int* cover, /**< problem indices of the variables in the cover */
1339  int diversification, /**< how many unfixed variables have to change their value? */
1340  SCIP_Bool* success, /**< pointer to store whether the cutoff constraint was created successfully */
1341  SCIP_Bool* infeas /**< pointer to store whether the cutoff proves (local or global) infeasibility */
1342  )
1343 {
1344  SCIP_CONS* cons;
1345  SCIP_VAR** consvars;
1346 
1347  char name[SCIP_MAXSTRLEN];
1348  int nconsvars;
1349  int i;
1350 
1351  assert(scip != NULL);
1352  assert(vars != NULL);
1353  assert(nvars >= 1);
1354  assert(cover != NULL);
1355  assert(coversize >= 1);
1356  assert(coversize <= nvars);
1357  assert(diversification >= 1);
1358  assert(success != NULL);
1359  assert(infeas != NULL);
1360 
1361  *success = FALSE;
1362  *infeas = FALSE;
1363 
1364  /* allocate memory for constraint variables */
1365  SCIP_CALL( SCIPallocBufferArray(scip, &consvars, coversize) );
1366  nconsvars = 0;
1367  cons = NULL;
1368 
1369  /* create constraint name */
1370  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "forbid_cover_assignment");
1371 
1372  /* if all variables in the cover are binary and we require only one variable to change its value, then we create a
1373  * set covering constraint
1374  */
1375  if( diversification == 1 )
1376  {
1377  /* build up constraint */
1378  for( i = coversize-1; i >= 0; i-- )
1379  {
1380  if( !SCIPisFeasGE(scip, SCIPvarGetLbLocal(vars[cover[i]]), 1.0) )
1381  {
1382  SCIP_CALL( SCIPgetNegatedVar(scip, vars[cover[i]], &consvars[nconsvars]) );
1383  nconsvars++;
1384  }
1385  }
1386 
1387  /* if all covering variables are fixed to one, the constraint cannot be satisfied */
1388  if( nconsvars == 0 )
1389  {
1390  *infeas = TRUE;
1391  }
1392  else
1393  {
1394  /* create constraint */
1395  SCIP_CALL( SCIPcreateConsSetcover(scip, &cons, name, nconsvars, consvars,
1396  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
1397  }
1398  }
1399  /* if all variables in the cover are binary and we require more variables to change their value, then we create a
1400  * linear constraint
1401  */
1402  else
1403  {
1404  SCIP_Real* consvals;
1405  SCIP_Real rhs;
1406 
1407  /* build up constraint */
1408  SCIP_CALL( SCIPallocBufferArray(scip, &consvals, coversize) );
1409  for( i = coversize-1; i >= 0; i-- )
1410  {
1411  if( !SCIPisFeasGE(scip, SCIPvarGetLbLocal(vars[cover[i]]), 1.0) )
1412  {
1413  consvars[nconsvars] = vars[cover[i]];
1414  consvals[nconsvars] = 1.0;
1415  nconsvars++;
1416  }
1417  }
1418  rhs = (SCIP_Real) (nconsvars-diversification);
1419 
1420  /* if too many covering variables are fixed to 1, the constraint cannot be satisfied */
1421  if( rhs < 0 )
1422  {
1423  *infeas = TRUE;
1424  }
1425  else
1426  {
1427  /* create constraint */
1428  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name,
1429  nconsvars, consvars, consvals, -SCIPinfinity(scip), rhs,
1430  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
1431  }
1432 
1433  /* free memory */
1434  SCIPfreeBufferArray(scip, &consvals);
1435  }
1436 
1437  /* free memory */
1438  SCIPfreeBufferArray(scip, &consvars);
1439 
1440  /* if proven infeasible, we do not even add the constraint; otherwise we add and release the constraint if created
1441  * successfully
1442  */
1443  if( !(*infeas) && cons != NULL )
1444  {
1445  SCIP_CALL( SCIPaddCons(scip, cons) );
1446  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
1447  *success = TRUE;
1448  }
1449 
1450  return SCIP_OKAY;
1451 }
1452 
1453 
1454 /** adds a set covering or bound disjunction constraint to the original problem */
1455 static
1457  SCIP* scip, /**< SCIP data structure */
1458  int bdlen, /**< length of bound disjunction */
1459  SCIP_VAR** bdvars, /**< array of variables in bound disjunction */
1460  SCIP_BOUNDTYPE* bdtypes, /**< array of bound types in bound disjunction */
1461  SCIP_Real* bdbounds, /**< array of bounds in bound disjunction */
1462  SCIP_Bool local, /**< is constraint valid only locally? */
1463  SCIP_Bool dynamic, /**< is constraint subject to aging? */
1464  SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup? */
1465  SCIP_Bool* success /**< pointer to store whether the cutoff constraint was created successfully */
1466  )
1467 {
1468  SCIP_CONS* cons;
1469  SCIP_VAR** consvars;
1470  SCIP_Bool isbinary;
1471  char name[SCIP_MAXSTRLEN];
1472  int i;
1473 
1474  assert(scip != NULL);
1475  assert(bdlen >= 1);
1476  assert(bdvars != NULL);
1477  assert(bdtypes != NULL);
1478  assert(bdbounds != NULL);
1479  assert(success != NULL);
1480 
1481  /* initialize */
1482  *success = FALSE;
1483  cons = NULL;
1484  consvars = NULL;
1485 
1486  /* create constraint name */
1487  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "undercover_cutoff");
1488 
1489  /* check if all variables in the cover are binary */
1490  isbinary = TRUE;
1491  for( i = bdlen-1; i >= 0 && isbinary; i-- )
1492  {
1493  isbinary = SCIPvarIsBinary(bdvars[i]);
1494  }
1495 
1496  /* if all variables in the cover are binary, then we create a logicor constraint */
1497  if( isbinary )
1498  {
1499  /* allocate memory for constraint variables */
1500  SCIP_CALL( SCIPallocBufferArray(scip, &consvars, bdlen) );
1501 
1502  /* build up constraint */
1503  for( i = bdlen-1; i >= 0; i-- )
1504  {
1505  assert(bdtypes[i] == SCIP_BOUNDTYPE_LOWER || SCIPisFeasZero(scip, bdbounds[i]));
1506  assert(bdtypes[i] == SCIP_BOUNDTYPE_UPPER || SCIPisFeasEQ(scip, bdbounds[i], 1.0));
1507 
1508  if( bdtypes[i] == SCIP_BOUNDTYPE_LOWER )
1509  {
1510  consvars[i] = bdvars[i];
1511  }
1512  else
1513  {
1514  assert(bdtypes[i] == SCIP_BOUNDTYPE_UPPER);
1515  SCIP_CALL( SCIPgetNegatedVar(scip, bdvars[i], &consvars[i]) );
1516  }
1517  }
1518 
1519  /* create conflict constraint */
1520  SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, name, bdlen, consvars,
1521  FALSE, TRUE, FALSE, FALSE, TRUE, local, FALSE, dynamic, removable, FALSE) );
1522  }
1523  /* otherwise we create a bound disjunction constraint as given */
1524  else
1525  {
1526  /* create conflict constraint */
1527  SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &cons, name, bdlen, bdvars, bdtypes, bdbounds,
1528  FALSE, TRUE, FALSE, FALSE, TRUE, local, FALSE, dynamic, removable, FALSE) );
1529  }
1530 
1531  /* add and release constraint if created successfully */
1532  if( cons != NULL )
1533  {
1534  if( local )
1535  {
1536  SCIP_CALL( SCIPaddConsLocal(scip, cons, NULL) );
1537  }
1538  else
1539  {
1540  SCIP_CALL( SCIPaddCons(scip, cons) );
1541  }
1542 
1543  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
1544  *success = TRUE;
1545  }
1546 
1547  /* free memory */
1548  SCIPfreeBufferArrayNull(scip, &consvars);
1549 
1550  return SCIP_OKAY;
1551 }
1552 
1553 
1554 /** solve covering problem */
1555 static
1557  SCIP* coveringscip, /**< SCIP data structure for the covering problem */
1558  int ncoveringvars, /**< number of the covering problem's variables */
1559  SCIP_VAR** coveringvars, /**< array of the covering problem's variables */
1560  int* coversize, /**< size of the computed cover */
1561  int* cover, /**< array to store indices of the variables in the computed cover
1562  * (should be ready to hold ncoveringvars entries) */
1563  SCIP_Real timelimit, /**< time limit */
1564  SCIP_Real memorylimit, /**< memory limit */
1565  SCIP_Real objlimit, /**< upper bound on the cover size */
1566  SCIP_Bool* success /**< feasible cover found? */
1567  )
1568 {
1569  SCIP_Real* solvals;
1570  SCIP_Real totalpenalty;
1571  SCIP_RETCODE retcode;
1572  int i;
1573 
1574  assert(coveringscip != NULL);
1575  assert(coveringvars != NULL);
1576  assert(cover != NULL);
1577  assert(coversize != NULL);
1578  assert(timelimit > 0.0);
1579  assert(memorylimit > 0.0);
1580  assert(success != NULL);
1581 
1582  *success = FALSE;
1583 
1584  /* forbid call of heuristics and separators solving sub-CIPs */
1585  SCIP_CALL( SCIPsetSubscipsOff(coveringscip, TRUE) );
1586 
1587  /* set presolving and separation to fast */
1590 
1591  /* use inference branching */
1592  if( SCIPfindBranchrule(coveringscip, "inference") != NULL && !SCIPisParamFixed(coveringscip, "branching/inference/priority") )
1593  {
1594  SCIP_CALL( SCIPsetIntParam(coveringscip, "branching/inference/priority", INT_MAX/4) );
1595  }
1596 
1597  /* only solve root */
1598  SCIP_CALL( SCIPsetLongintParam(coveringscip, "limits/nodes", 1LL) );
1599 
1600  SCIPdebugMessage("timelimit = %g, memlimit = %g\n", timelimit, memorylimit);
1601 
1602  /* set time, memory, and objective limit */
1603  SCIP_CALL( SCIPsetRealParam(coveringscip, "limits/time", timelimit) );
1604  SCIP_CALL( SCIPsetRealParam(coveringscip, "limits/memory", memorylimit) );
1605  SCIP_CALL( SCIPsetObjlimit(coveringscip, objlimit) );
1606 
1607  /* do not abort on CTRL-C */
1608  SCIP_CALL( SCIPsetBoolParam(coveringscip, "misc/catchctrlc", FALSE) );
1609 
1610  /* disable output to console in optimized mode, enable in SCIP's debug mode */
1611 #ifdef SCIP_DEBUG
1612  SCIP_CALL( SCIPsetIntParam(coveringscip, "display/verblevel", 5) );
1613  SCIP_CALL( SCIPsetIntParam(coveringscip, "display/freq", 100000) );
1614 #else
1615  SCIP_CALL( SCIPsetIntParam(coveringscip, "display/verblevel", 0) );
1616 #endif
1617 
1618 
1619  /* solve covering problem */
1620  retcode = SCIPsolve(coveringscip);
1621 
1622  /* errors in solving the covering problem should not kill the overall solving process;
1623  * hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
1624  */
1625  if( retcode != SCIP_OKAY )
1626  {
1627 #ifndef NDEBUG
1628  SCIP_CALL( retcode );
1629 #endif
1630  SCIPwarningMessage(coveringscip, "Error while solving covering problem in Undercover heuristic; sub-SCIP terminated with code <%d>\n",retcode);
1631  return SCIP_OKAY;
1632  }
1633 
1634  /* check, whether a feasible cover was found */
1635  if( SCIPgetNSols(coveringscip) == 0 )
1636  return SCIP_OKAY;
1637 
1638  /* store solution */
1639  SCIP_CALL( SCIPallocBufferArray(coveringscip, &solvals, ncoveringvars) );
1640  SCIP_CALL( SCIPgetSolVals(coveringscip, SCIPgetBestSol(coveringscip), ncoveringvars, coveringvars, solvals) );
1641 
1642  *coversize = 0;
1643  totalpenalty = 0.0;
1644  for( i = 0; i < ncoveringvars; i++ )
1645  {
1646  if( solvals[i] > 0.5 )
1647  {
1648  cover[*coversize] = i;
1649  (*coversize)++;
1650  }
1651  totalpenalty += SCIPvarGetObj(coveringvars[i]);
1652  }
1653 
1654  /* print solution if we are in SCIP's debug mode */
1655  assert(SCIPgetBestSol(coveringscip) != NULL);
1656  SCIPdebugMessage("found a feasible cover: %d/%d variables fixed, normalized penalty=%g\n\n",
1657  *coversize, SCIPgetNOrigVars(coveringscip), SCIPgetSolOrigObj(coveringscip, SCIPgetBestSol(coveringscip))/(totalpenalty+SCIPsumepsilon(coveringscip)));
1658  SCIPdebug( SCIP_CALL( SCIPprintSol(coveringscip, SCIPgetBestSol(coveringscip), NULL, FALSE) ) );
1659  SCIPdebugMessage("\r \n");
1660 
1661  *success = TRUE;
1662 
1663  /* free array of solution values */
1664  SCIPfreeBufferArray(coveringscip, &solvals);
1665 
1666  return SCIP_OKAY;
1667 }
1668 
1669 
1670 /** computes fixing order and returns whether order has really been changed */
1671 static
1673  SCIP* scip, /**< original SCIP data structure */
1674  SCIP_HEURDATA* heurdata, /**< heuristic data */
1675  int nvars, /**< number of variables in the original problem */
1676  SCIP_VAR** vars, /**< variables in the original problem */
1677  int coversize, /**< size of the cover */
1678  int* cover, /**< problem indices of the variables in the cover */
1679  int lastfailed, /**< position in cover array of the variable the fixing of which yielded
1680  * infeasibility in last dive (or >= coversize, in which case *success
1681  * is always TRUE) */
1682  SCIP_Bool* success /**< has order really been changed? */
1683  )
1684 {
1685  SCIP_Real* scores;
1686  SCIP_Real bestscore;
1687  SCIP_Bool sortdown;
1688  int i;
1689 
1690  assert(scip != NULL);
1691  assert(heurdata != NULL);
1692  assert(nvars >= 1);
1693  assert(vars != NULL);
1694  assert(coversize >= 1);
1695  assert(cover != NULL);
1696  assert(lastfailed >= 0);
1697 
1698  *success = FALSE;
1699 
1700  /* if fixing the first variable had failed, do not try with another order */
1701  if( lastfailed == 0 )
1702  return SCIP_OKAY;
1703 
1704  /* allocate buffer array for score values */
1705  SCIP_CALL( SCIPallocBufferArray(scip, &scores, coversize) );
1706 
1707  /* initialize best score to infinite value */
1708  sortdown = (heurdata->fixingorder == 'c' || heurdata->fixingorder == 'v' );
1709  bestscore = sortdown ? -SCIPinfinity(scip) : +SCIPinfinity(scip);
1710 
1711  /* compute score values */
1712  for( i = coversize-1; i >= 0; i-- )
1713  {
1714  SCIP_VAR* var;
1715 
1716  /* get variable in the cover */
1717  assert(cover[i] >= 0);
1718  assert(cover[i] < nvars);
1719  var = vars[cover[i]];
1720 
1721  if( heurdata->fixingorder == 'C' || heurdata->fixingorder == 'c' )
1722  scores[i] = heurdata->conflictweight * SCIPgetVarConflictScore(scip, var)
1723  + heurdata->inferenceweight * SCIPgetVarAvgInferenceCutoffScore(scip, var, heurdata->cutoffweight);
1724  else if( heurdata->fixingorder == 'V' || heurdata->fixingorder == 'v' )
1725  scores[i] = cover[i];
1726  else
1727  return SCIP_PARAMETERWRONGVAL;
1728 
1729  assert(scores[i] >= 0.0);
1730 
1731  /* update best score */
1732  if( sortdown )
1733  bestscore = MAX(bestscore, scores[i]);
1734  else
1735  bestscore = MIN(bestscore, scores[i]);
1736 
1737  }
1738 
1739  /* put integers to the front */
1740  if( heurdata->fixintfirst )
1741  {
1742  for( i = coversize-1; i >= 0; i-- )
1743  {
1744  if( SCIPvarIsIntegral(vars[cover[i]]) )
1745  {
1746  if( sortdown )
1747  scores[i] += bestscore+1.0;
1748  else
1749  scores[i] = bestscore - 1.0/(scores[i]+1.0);
1750  }
1751  }
1752  }
1753 
1754  /* put last failed variable to the front */
1755  if( lastfailed < coversize )
1756  {
1757  if( sortdown )
1758  scores[lastfailed] += bestscore+2.0;
1759  else
1760  scores[lastfailed] = bestscore - 2.0/(scores[lastfailed]+1.0);
1761  i = cover[lastfailed];
1762  }
1763 
1764  /* sort by non-decreasing (negative) score */
1765  if( sortdown )
1766  SCIPsortDownRealInt(scores, cover, coversize);
1767  else
1768  SCIPsortRealInt(scores, cover, coversize);
1769 
1770  assert(lastfailed >= coversize || cover[0] == i);
1771 
1772  /* free buffer memory */
1773  SCIPfreeBufferArray(scip, &scores);
1774 
1775  *success = TRUE;
1776 
1777  return SCIP_OKAY;
1778 }
1779 
1780 
1781 /** gets fixing value */
1782 static
1784  SCIP* scip, /**< original SCIP data structure */
1785  SCIP_HEURDATA* heurdata, /**< heuristic data */
1786  SCIP_VAR* var, /**< variable in the original problem */
1787  SCIP_Real* val, /**< buffer for returning fixing value */
1788  char fixalt, /**< source of the fixing value: 'l'p relaxation, 'n'lp relaxation, 'i'ncumbent solution */
1789  SCIP_Bool* success, /**< could value be retrieved successfully? */
1790  int bdlen, /**< current length of probing path */
1791  SCIP_VAR** bdvars, /**< array of variables with bound changes along probing path */
1792  SCIP_BOUNDTYPE* bdtypes, /**< array of bound types in bound disjunction */
1793  SCIP_Real* oldbounds /**< array of bounds before fixing */
1794  )
1795 {
1796  assert(scip != NULL);
1797  assert(heurdata != NULL);
1798  assert(var != NULL);
1799  assert(val != NULL);
1800  assert(success != NULL);
1801 
1802  *success = FALSE;
1803 
1804  switch( fixalt )
1805  {
1806  case 'l':
1807  /* get the last LP solution value */
1808  *val = SCIPvarGetLPSol(var);
1809  *success = TRUE;
1810  break;
1811  case 'n':
1812  /* only call this function if NLP relaxation is available */
1813  assert(SCIPisNLPConstructed(scip));
1814 
1815  /* the solution values are already available */
1816  if( heurdata->nlpsolved )
1817  {
1818  assert(!heurdata->nlpfailed);
1819 
1820  /* retrieve NLP solution value */
1821  *val = SCIPvarGetNLPSol(var);
1822  *success = TRUE;
1823  }
1824  /* solve NLP relaxation unless it has not failed too often before */
1825  else if( !heurdata->nlpfailed )
1826  { /*lint --e{850}*/
1827  SCIP_NLPSOLSTAT stat;
1828  int i;
1829 
1830  /* restore bounds at start of probing, since otherwise, if in backtrack mode, NLP solver becomes most likely
1831  * locally infeasible
1832  */
1833  SCIP_CALL( SCIPstartDiveNLP(scip) );
1834 
1835  for( i = bdlen-1; i >= 0; i-- )
1836  {
1837  SCIP_VAR* relaxvar;
1838  SCIP_Real lb;
1839  SCIP_Real ub;
1840 
1841  relaxvar = bdvars[i];
1842 
1843  /* both bounds were tightened */
1844  if( i > 0 && bdvars[i-1] == relaxvar )
1845  {
1846  assert(bdtypes[i] != bdtypes[i-1]);
1847 
1848  lb = bdtypes[i] == SCIP_BOUNDTYPE_UPPER ? oldbounds[i] : oldbounds[i-1];
1849  ub = bdtypes[i] == SCIP_BOUNDTYPE_UPPER ? oldbounds[i-1] : oldbounds[i];
1850  i--;
1851  }
1852  /* lower bound was tightened */
1853  else if( bdtypes[i] == SCIP_BOUNDTYPE_UPPER )
1854  {
1855  lb = oldbounds[i];
1856  ub = SCIPvarGetUbLocal(relaxvar);
1857  }
1858  /* upper bound was tightened */
1859  else
1860  {
1861  lb = SCIPvarGetLbLocal(relaxvar);
1862  ub = oldbounds[i];
1863  }
1864 
1865  assert(SCIPisLE(scip, lb, SCIPvarGetLbLocal(relaxvar)));
1866  assert(SCIPisGE(scip, ub, SCIPvarGetUbLocal(relaxvar)));
1867 
1868  /* relax bounds */
1869  SCIP_CALL( SCIPchgVarBoundsDiveNLP(scip, relaxvar, lb, ub) );
1870  }
1871 
1872  /* activate NLP solver output if we are in SCIP's debug mode */
1874 
1875  /* set starting point to lp solution */
1877 
1878  /* solve NLP relaxation */
1879  SCIP_CALL( SCIPsolveDiveNLP(scip) );
1880  stat = SCIPgetNLPSolstat(scip);
1881  *success = stat == SCIP_NLPSOLSTAT_GLOBOPT || stat == SCIP_NLPSOLSTAT_LOCOPT || stat == SCIP_NLPSOLSTAT_FEASIBLE;
1882 
1883  SCIPdebugMessage("solving NLP relaxation to obtain fixing values %s (stat=%d)\n", *success ? "successful" : "failed", stat);
1884 
1885  if( *success )
1886  {
1887  /* solving succeeded */
1888  heurdata->nnlpfails = 0;
1889  heurdata->nlpsolved = TRUE;
1890 
1891  /* retrieve NLP solution value */
1892  *val = SCIPvarGetNLPSol(var);
1893  }
1894  else
1895  {
1896  /* solving failed */
1897  heurdata->nnlpfails++;
1898  heurdata->nlpfailed = TRUE;
1899  heurdata->nlpsolved = FALSE;
1900 
1901  SCIPdebugMessage("solving NLP relaxation failed (%d time%s%s)\n",
1902  heurdata->nnlpfails, heurdata->nnlpfails > 1 ? "s" : "", heurdata->nnlpfails >= MAXNLPFAILS ? ", will not be called again" : "");
1903  }
1904  }
1905  break;
1906  case 'i':
1907  /* only call this function if a feasible solution is available */
1908  assert(SCIPgetBestSol(scip) != NULL);
1909 
1910  /* get value in the incumbent solution */
1911  *val = SCIPgetSolVal(scip, SCIPgetBestSol(scip), var);
1912  *success = TRUE;
1913  break;
1914  default:
1915  break;
1916  }
1917 
1918  return SCIP_OKAY;
1919 }
1920 
1921 
1922 /** calculates up to four alternative values for backtracking, if fixing the variable failed.
1923  * The alternatives are the two bounds of the variable, and the averages of the bounds and the fixing value.
1924  * For infinite bounds, fixval +/- abs(fixval) will be used instead.
1925  */
1926 static
1928  SCIP* scip, /**< original SCIP data structure */
1929  SCIP_VAR* var, /**< variable to calculate alternatives for */
1930  SCIP_Real fixval, /**< reference fixing value */
1931  int* nalternatives, /**< number of fixing values computed */
1932  SCIP_Real* alternatives /**< array to store the alternative fixing values */
1933  )
1934 {
1935  SCIP_Real lb;
1936  SCIP_Real ub;
1937 
1938  /* for binary variables, there is only two possible fixing values */
1939  if( SCIPvarIsBinary(var) )
1940  {
1941  if( SCIPisFeasEQ(scip, fixval, 0.0) || SCIPisFeasEQ(scip, fixval, 1.0) )
1942  {
1943  alternatives[0] = 1.0 - fixval;
1944  *nalternatives = 1;
1945  }
1946  else
1947  {
1948  alternatives[0] = 0.0;
1949  alternatives[1] = 1.0;
1950  *nalternatives = 2;
1951  }
1952  return;
1953  }
1954 
1955  /* get bounds */
1956  lb = SCIPvarGetLbLocal(var);
1957  ub = SCIPvarGetUbLocal(var);
1958 
1959  /* if lower bound is infinite, use x'-|x'|; if x' is zero, use -1.0 instead */
1960  if( SCIPisInfinity(scip, -lb) )
1961  lb = SCIPisFeasZero(scip, fixval) ? -1.0 : fixval - ABS(fixval);
1962 
1963  /* if upper bound is infinite, use x'+|x'|; if x' is zero, use 1.0 instead */
1964  if( SCIPisInfinity(scip, ub) )
1965  ub = SCIPisFeasZero(scip, fixval) ? 1.0 : fixval + ABS(fixval);
1966 
1967  assert(!SCIPisEQ(scip, lb, ub));
1968 
1969  /* collect alternatives */
1970  *nalternatives = 0;
1971 
1972  /* use lower bound if not equal to x' */
1973  if( !SCIPisFeasEQ(scip, lb, fixval) )
1974  {
1975  alternatives[*nalternatives] = lb;
1976  (*nalternatives)++;
1977  }
1978 
1979  /* use upper bound if not equal to x' */
1980  if( !SCIPisFeasEQ(scip, ub, fixval) )
1981  {
1982  alternatives[*nalternatives] = ub;
1983  (*nalternatives)++;
1984  }
1985 
1986  /* use the average of x' and lower bound as alternative value, if this is not equal to any of the other values */
1987  if( !SCIPisFeasEQ(scip, lb, fixval) && (!SCIPvarIsIntegral(var) || !SCIPisFeasEQ(scip, lb, fixval-1)) )
1988  {
1989  alternatives[*nalternatives] = (lb+fixval)/2.0;
1990  (*nalternatives)++;
1991  }
1992 
1993  /* use the average of x' and upper bound as alternative value, if this is not equal to any of the other values */
1994  if( !SCIPisFeasEQ(scip, ub, fixval) && (!SCIPvarIsIntegral(var) || !SCIPisFeasEQ(scip, ub, fixval+1)) )
1995  {
1996  alternatives[*nalternatives] = (ub+fixval)/2.0;
1997  (*nalternatives)++;
1998  }
1999 
2000  assert(*nalternatives <= 4);
2001 
2002  return;
2003 }
2004 
2005 
2006 /** rounds the given fixing value */
2007 static
2009  SCIP* scip, /**< original SCIP data structure */
2010  SCIP_Real* val, /**< fixing value to be rounded */
2011  SCIP_VAR* var, /**< corresponding variable */
2012  SCIP_Bool locksrounding /**< shall we round according to locks? (otherwise to nearest integer) */
2013  )
2014 {
2015  SCIP_Real x;
2016 
2017  x = *val;
2018 
2019  /* if integral within feasibility tolerance, only shift to nearest integer */
2020  if( SCIPisFeasIntegral(scip, x) )
2021  x = SCIPfeasFrac(scip, x) < 0.5 ? SCIPfeasFloor(scip, x) : SCIPfeasCeil(scip, x);
2022 
2023  /* round in the direction of least locks with fractionality as tie breaker */
2024  else if( locksrounding )
2025  {
2026  if( SCIPvarGetNLocksDown(var) < SCIPvarGetNLocksUp(var) )
2027  x = SCIPfeasFloor(scip, x);
2028  else if( SCIPvarGetNLocksDown(var) > SCIPvarGetNLocksUp(var) )
2029  x = SCIPfeasCeil(scip, x);
2030  else
2031  x = SCIPfeasFrac(scip, x) < 0.5 ? SCIPfeasFloor(scip, x) : SCIPfeasCeil(scip, x);
2032  }
2033  /* round in the direction of least fractionality with locks as tie breaker */
2034  else
2035  {
2036  if( SCIPfeasFrac(scip, x) < 0.5)
2037  x = SCIPfeasFloor(scip, x);
2038  else if( SCIPfeasFrac(scip, x) > 0.5 )
2039  x = SCIPfeasCeil(scip, x);
2040  else
2041  x = SCIPvarGetNLocksDown(var) < SCIPvarGetNLocksUp(var) ? SCIPfeasFloor(scip, x) : SCIPfeasCeil(scip, x);
2042  }
2043 
2044  /* return rounded fixing value */
2045  *val = x;
2046 
2047  return SCIP_OKAY;
2048 }
2049 
2050 
2051 /** copy the solution of the subproblem to newsol */
2052 static
2054  SCIP* scip, /**< original SCIP data structure */
2055  SCIP* subscip, /**< SCIP structure of the subproblem */
2056  SCIP_VAR** subvars, /**< the variables of the subproblem */
2057  SCIP_SOL* subsol, /**< solution of the subproblem */
2058  SCIP_SOL** newsol /**< solution to the original problem */
2059  )
2060 {
2061  SCIP_VAR** vars; /* the original problem's variables */
2062  SCIP_Real* subsolvals; /* solution values of the subproblem */
2063  int nvars;
2064 
2065  assert(scip != NULL);
2066  assert(subscip != NULL);
2067  assert(subvars != NULL);
2068  assert(subsol != NULL);
2069  assert(newsol != NULL);
2070  assert(*newsol != NULL);
2071 
2072  /* get variables' data */
2073  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2074 
2075  /* sub-SCIP may have more variables than the number of active (transformed) variables in the main SCIP
2076  * since constraint copying may have required the copy of variables that are fixed in the main SCIP
2077  */
2078  assert(nvars <= SCIPgetNOrigVars(subscip));
2079 
2080  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
2081 
2082  /* copy the solution */
2083  SCIP_CALL( SCIPgetSolVals(subscip, subsol, nvars, subvars, subsolvals) );
2084  SCIP_CALL( SCIPsetSolVals(scip, *newsol, nvars, vars, subsolvals) );
2085 
2086  SCIPfreeBufferArray(scip, &subsolvals);
2087 
2088  return SCIP_OKAY;
2089 }
2090 
2091 
2092 /** solve subproblem and pass best feasible solution to original SCIP instance */
2093 static
2095  SCIP* scip, /**< SCIP data structure of the original problem */
2096  SCIP_HEUR* heur, /**< heuristic data structure */
2097  int coversize, /**< size of the cover */
2098  int* cover, /**< problem indices of the variables in the cover */
2099  SCIP_Real* fixingvals, /**< fixing values for the variables in the cover */
2100  SCIP_Real timelimit, /**< time limit */
2101  SCIP_Real memorylimit, /**< memory limit */
2102  SCIP_Longint nodelimit, /**< node limit */
2103  SCIP_Longint nstallnodes, /**< number of stalling nodes for the subproblem */
2104  SCIP_Bool* validsolved, /**< was problem constructed from a valid copy and solved (proven optimal or infeasible)? */
2105  SCIP_SOL** sol, /**< best solution found in subproblem (if feasible); *sol must be NULL, solution will be created */
2106  SCIP_Longint* nusednodes /**< number of nodes used for solving the subproblem */
2107  )
2108 {
2109  SCIP_HEURDATA* heurdata;
2110  SCIP* subscip;
2111  SCIP_VAR** subvars;
2112  SCIP_VAR** vars;
2113  SCIP_HASHMAP* varmap;
2114 
2115  SCIP_RETCODE retcode;
2116 
2117  int nvars;
2118  int i;
2119 
2120  assert(scip != NULL);
2121  assert(heur != NULL);
2122  assert(cover != NULL);
2123  assert(fixingvals != NULL);
2124  assert(coversize >= 1);
2125  assert(timelimit > 0.0);
2126  assert(memorylimit > 0.0);
2127  assert(nodelimit >= 1);
2128  assert(nstallnodes >= 1);
2129  assert(validsolved != NULL);
2130  assert(sol != NULL);
2131  assert(*sol == NULL);
2132  assert(nusednodes != NULL);
2133 
2134  *validsolved = FALSE;
2135  *nusednodes = 0;
2136 
2137  /* get heuristic data */
2138  heurdata = SCIPheurGetData(heur);
2139  assert(heurdata != NULL);
2140 
2141  /* get required data of the original problem */
2142  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2143 
2144  /* create subproblem */
2145  SCIP_CALL( SCIPcreate(&subscip) );
2146  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
2147 
2148  /* create the variable mapping hash map */
2149  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) );
2150 
2151  /* copy original problem to subproblem; do not copy pricers */
2152  SCIP_CALL( SCIPcopy(scip, subscip, varmap, NULL, "undercoversub", heurdata->globalbounds, FALSE, TRUE, validsolved) );
2153 
2154  if( heurdata->copycuts )
2155  {
2156  /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */
2157  SCIP_CALL( SCIPcopyCuts(scip, subscip, varmap, NULL, heurdata->globalbounds, NULL) );
2158  }
2159 
2160  SCIPdebugMessage("problem copied, copy %svalid\n", *validsolved ? "" : "in");
2161 
2162  /* store subproblem variables */
2163  for( i = nvars-1; i >= 0; i-- )
2164  {
2165  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmap, vars[i]);
2166  assert(subvars[i] != NULL);
2167  }
2168 
2169  /* fix subproblem variables in the cover */
2170  SCIPdebugMessage("fixing variables\n");
2171  for( i = coversize-1; i >= 0; i-- )
2172  {
2173  assert(cover[i] >= 0);
2174  assert(cover[i] < nvars);
2175 
2176  SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[cover[i]], fixingvals[i]) );
2177  SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[cover[i]], fixingvals[i]) );
2178  }
2179 
2180  /* set the parameters such that good solutions are found fast */
2181  SCIPdebugMessage("setting subproblem parameters\n");
2185 
2186  /* deactivate expensive pre-root heuristics, since it may happen that the lp relaxation of the subproblem is already
2187  * infeasible; in this case, we do not want to waste time on heuristics before solving the root lp */
2188  if( !SCIPisParamFixed(subscip, "heuristics/shiftandpropagate/freq") )
2189  {
2190  SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/shiftandpropagate/freq", -1) );
2191  }
2192 
2193  /* forbid recursive call of undercover heuristic */
2194  if( SCIPisParamFixed(subscip, "heuristics/"HEUR_NAME"/freq") )
2195  {
2196  SCIPwarningMessage(scip, "unfixing parameter heuristics/"HEUR_NAME"/freq in subscip of undercover heuristic to avoid recursive calls\n");
2197  SCIP_CALL( SCIPunfixParam(subscip, "heuristics/"HEUR_NAME"/freq") );
2198  }
2199  SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/"HEUR_NAME"/freq", -1) );
2200 
2201  SCIPdebugMessage("timelimit = %g, memlimit = %g, nodelimit = %"SCIP_LONGINT_FORMAT", nstallnodes = %"SCIP_LONGINT_FORMAT"\n", timelimit, memorylimit, nodelimit, nstallnodes);
2202 
2203  SCIPdebugMessage("timelimit = %g, memlimit = %g, nodelimit = %"SCIP_LONGINT_FORMAT", nstallnodes = %"SCIP_LONGINT_FORMAT"\n", timelimit, memorylimit, nodelimit, nstallnodes);
2204 
2205  /* set time, memory and node limits */
2206  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
2207  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
2208  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nodelimit) );
2209  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", nstallnodes) );
2210 
2211  /* do not abort subproblem on CTRL-C */
2212  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
2213 
2214  /* disable output to console in optimized mode, enable in SCIP's debug mode */
2215 #ifdef SCIP_DEBUG
2216  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) );
2217  SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 100000) );
2218 #else
2219  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
2220 #endif
2221 
2222  /* if there is already a solution, add an objective cutoff; note: this does not affect the validity of the subproblem
2223  * if we find solutions later, thus we do not set *validsolved to FALSE */
2224  if( SCIPgetNSols(scip) > 0 )
2225  {
2226  SCIP_Real cutoff;
2227  SCIP_Real upperbound;
2228 
2229  assert(!SCIPisInfinity(scip, SCIPgetUpperbound(scip)));
2230  upperbound = SCIPgetUpperbound(scip);
2231 
2232  if( SCIPisInfinity(scip, -SCIPgetLowerbound(scip)) )
2233  cutoff = (upperbound >= 0 ? 1.0 - heurdata->minimprove : 1.0 + heurdata->minimprove) * upperbound;
2234  else
2235  cutoff = (1.0 - heurdata->minimprove) * upperbound + heurdata->minimprove * SCIPgetLowerbound(scip);
2236 
2237  cutoff = MIN(upperbound, cutoff);
2238  SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) );
2239 
2240  SCIPdebugMessage("adding objective cutoff=%g (minimprove=%g)\n", cutoff, heurdata->minimprove);
2241  }
2242 
2243  /* solve subproblem */
2244  SCIPdebugMessage("solving subproblem started\n");
2245  retcode = SCIPsolve(subscip);
2246 
2247  /* Errors in solving the subproblem should not kill the overall solving process
2248  * Hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
2249  */
2250  if( retcode != SCIP_OKAY )
2251  {
2252 #ifndef NDEBUG
2253  SCIP_CALL( retcode );
2254 #endif
2255  SCIPwarningMessage(scip, "Error while solving subproblem in Undercover heuristic; sub-SCIP terminated with code <%d>\n",retcode);
2256  return SCIP_OKAY;
2257  }
2258 
2259  /* print solving statistics of subproblem if we are in SCIP's debug mode */
2260  SCIPdebug( SCIP_CALL( SCIPprintStatistics(subscip, NULL) ) );
2261 
2262  /* store solving status; note: if we proved infeasibility in presence of an objective cutoff beyond the primal bound,
2263  * the subproblem was not a valid copy */
2264  *validsolved = *validsolved && (SCIPgetStatus(subscip) == SCIP_STATUS_OPTIMAL
2265  || (SCIPgetStatus(subscip) == SCIP_STATUS_INFEASIBLE && (SCIPgetNSols(scip) == 0 || heurdata->minimprove <= 0.0)));
2266  *nusednodes = SCIPgetNNodes(subscip);
2267 
2268  /* if a solution was found for the subproblem, create corresponding solution in the original problem */
2269  if( SCIPgetNSols(subscip) > 0 && (SCIPgetStatus(subscip) != SCIP_STATUS_INFEASIBLE || heurdata->minimprove > 0.0) )
2270  {
2271  SCIP_SOL** subsols;
2272  SCIP_Bool success;
2273  int nsubsols;
2274 
2275  /* create solution */
2276  SCIP_CALL( SCIPcreateSol(scip, sol, heur) );
2277 
2278  /* check, whether a solution was found;
2279  * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted */
2280  nsubsols = SCIPgetNSols(subscip);
2281  subsols = SCIPgetSols(subscip);
2282  assert(subsols != NULL);
2283 
2284  success = FALSE;
2285  for( i = 0; i < nsubsols && !success; i++ )
2286  {
2287  /* transform solution to original problem */
2288  SCIP_CALL( copySol(scip, subscip, subvars, subsols[i], sol) );
2289 
2290  /* try to add new solution to scip */
2291  SCIP_CALL( SCIPtrySol(scip, *sol, FALSE, TRUE, TRUE, TRUE, &success) );
2292  }
2293 
2294  if( success )
2295  {
2296  assert(i >= 1);
2297  SCIPdebugMessage("heuristic found %d solutions in subproblem; solution %d feasible in original problem\n", nsubsols, i);
2298  }
2299  else
2300  {
2301  /* free solution structure, since we found no feasible solution */
2302  SCIP_CALL( SCIPfreeSol(scip, sol) );
2303  *sol = NULL;
2304  }
2305 
2306  /* if the best subproblem solution was not accepted in the original problem, we do not trust the solving status */
2307  *validsolved = *validsolved && i == 1;
2308  }
2309 
2310  /* free variable mapping hash map, array of subproblem variables, and subproblem */
2311  SCIPhashmapFree(&varmap);
2312  SCIPfreeBufferArray(scip, &subvars);
2313  SCIP_CALL( SCIPfree(&subscip) );
2314 
2315  return SCIP_OKAY;
2316 }
2317 
2318 
2319 /** perform fixing of a variable and record bound disjunction information */
2320 static
2322  SCIP* scip, /**< SCIP data structure */
2323  SCIP_VAR* var, /**< variable to fix */
2324  SCIP_Real val, /**< fixing value */
2325  SCIP_Bool* infeas, /**< pointer to store whether the fixing lead to infeasibility */
2326  int* bdlen, /**< current length of bound disjunction */
2327  SCIP_VAR** bdvars, /**< array of variables in bound disjunction */
2328  SCIP_BOUNDTYPE* bdtypes, /**< array of bound types in bound disjunction */
2329  SCIP_Real* bdbounds, /**< array of bounds in bound disjunction */
2330  SCIP_Real* oldbounds /**< array of bounds before fixing */
2331  )
2332 {
2333  SCIP_Longint ndomredsfound;
2334  SCIP_Real oldlb;
2335  SCIP_Real oldub;
2336  int oldbdlen;
2337 
2338  assert(scip != NULL);
2339  assert(var != NULL);
2340  assert(val >= SCIPvarGetLbLocal(var));
2341  assert(val <= SCIPvarGetUbLocal(var));
2342  assert(infeas != NULL);
2343  assert(bdlen != NULL);
2344  assert(*bdlen >= 0);
2345  assert(*bdlen < 2*SCIPgetNVars(scip)-1);
2346  assert(bdvars != NULL);
2347  assert(bdtypes != NULL);
2348  assert(bdbounds != NULL);
2349 
2350  assert(!SCIPvarIsIntegral(var) || SCIPisFeasIntegral(scip, val));
2351 
2352  /* remember length of probing path */
2353  oldbdlen = *bdlen;
2354 
2355  /* get bounds of the variable to fix */
2356  oldlb = SCIPvarGetLbLocal(var);
2357  oldub = SCIPvarGetUbLocal(var);
2358 
2359  /* decrease upper bound to fixing value */
2360  *infeas = FALSE;
2361  if( SCIPisUbBetter(scip, val, oldlb, oldub) )
2362  {
2363  /* create next probing node */
2364  SCIP_CALL( SCIPnewProbingNode(scip) );
2365  SCIP_CALL( SCIPchgVarUbProbing(scip, var, val) );
2366 
2367  SCIPdebugMessage("tentatively decreasing upper bound of variable <%s> to %g for probing\n",
2368  SCIPvarGetName(var), val);
2369 
2370  /* store bound disjunction information */
2371  bdvars[*bdlen] = var;
2372  bdtypes[*bdlen] = SCIP_BOUNDTYPE_LOWER;
2373  bdbounds[*bdlen] = SCIPvarIsIntegral(var) ? SCIPfeasCeil(scip, val)+1.0 : val;
2374  oldbounds[*bdlen] = oldub;
2375  (*bdlen)++;
2376 
2377  /* propagate the bound change; conflict analysis is performed automatically */
2378  SCIP_CALL( SCIPpropagateProbing(scip, 0, infeas, &ndomredsfound) );
2379  SCIPdebugMessage(" --> propagation reduced %lld further domains\n", ndomredsfound);
2380 
2381  /* if propagation led to a cutoff, we backtrack immediately */
2382  if( *infeas )
2383  {
2384  *bdlen = oldbdlen;
2385  return SCIP_OKAY;
2386  }
2387 
2388  /* store bound before propagation */
2389  oldbounds[*bdlen] = oldlb;
2390 
2391  /* move fixing value into the new domain, since it may be outside due to numerical issues or previous propagation */
2392  oldlb = SCIPvarGetLbLocal(var);
2393  oldub = SCIPvarGetUbLocal(var);
2394  val = MIN(val, oldub);
2395  val = MAX(val, oldlb);
2396 
2397  assert(!SCIPvarIsIntegral(var) || SCIPisFeasIntegral(scip, val));
2398  }
2399 
2400  /* update lower bound to fixing value */
2401  *infeas = FALSE;
2402  if( SCIPisLbBetter(scip, val, oldlb, oldub) )
2403  {
2404  /* create next probing node */
2405  SCIP_CALL( SCIPnewProbingNode(scip) );
2406  SCIP_CALL( SCIPchgVarLbProbing(scip, var, val) );
2407 
2408  SCIPdebugMessage("tentatively increasing lower bound of variable <%s> to %g for probing\n",
2409  SCIPvarGetName(var), val);
2410 
2411  /* store bound disjunction information */
2412  bdvars[*bdlen] = var;
2413  bdtypes[*bdlen] = SCIP_BOUNDTYPE_UPPER;
2414  bdbounds[*bdlen] = SCIPvarIsIntegral(var) ? SCIPfeasCeil(scip, val)-1.0 : val;
2415  (*bdlen)++;
2416 
2417  /* propagate the bound change */
2418  SCIP_CALL( SCIPpropagateProbing(scip, 0, infeas, &ndomredsfound) );
2419  SCIPdebugMessage(" --> propagation reduced %lld further domains\n", ndomredsfound);
2420 
2421  /* if propagation led to a cutoff, we backtrack immediately */
2422  if( *infeas )
2423  {
2424  *bdlen = oldbdlen;
2425  return SCIP_OKAY;
2426  }
2427  }
2428 
2429  return SCIP_OKAY;
2430 }
2431 
2432 static
2434  SCIP* scip, /**< original SCIP data structure */
2435  SCIP_HEURDATA* heurdata, /**< heuristic data structure */
2436  int* cover, /**< array with indices of the variables in the computed cover */
2437  int coversize, /**< size of the cover */
2438  SCIP_Real* fixingvals, /**< fixing values for the variables in the cover */
2439  int* bdlen, /**< current length of bound disjunction along the probing path */
2440  SCIP_VAR** bdvars, /**< array of variables in bound disjunction */
2441  SCIP_BOUNDTYPE* bdtypes, /**< array of bound types in bound disjunction */
2442  SCIP_Real* bdbounds, /**< array of bounds in bound disjunction */
2443  SCIP_Real* oldbounds, /**< array of bounds before fixing */
2444  int* nfixedints, /**< pointer to store number of fixed integer variables */
2445  int* nfixedconts, /**< pointer to store number of fixed continuous variables */
2446  int* lastfailed, /**< position in cover array of the variable the fixing of which yielded
2447  * infeasibility */
2448  SCIP_Bool* infeas /**< pointer to store whether fix-and-propagate led to an infeasibility */
2449  )
2450 {
2451  SCIP_VAR** vars; /* original problem's variables */
2452 
2453  int i;
2454  SCIP_Bool lpsolved;
2455 
2456  /* start probing in original problem */
2457  lpsolved = SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL;
2458  SCIP_CALL( SCIPstartProbing(scip) );
2459 
2460  /* initialize data */
2461  *nfixedints = 0;
2462  *nfixedconts = 0;
2463  *bdlen = 0;
2464  vars = SCIPgetVars(scip);
2465 
2466  /* round-fix-propagate-analyze-backtrack for each variable in the cover */
2467  for( i = 0; i < coversize && !(*infeas); i++ )
2468  {
2469  SCIP_Real* boundalts;
2470  SCIP_Real* usedvals;
2471  SCIP_Real val;
2472  int nbacktracks;
2473  int nboundalts;
2474  int nfailedvals;
2475  int nusedvals;
2476  int probingdepth;
2477  int idx;
2478 
2479  /* get probindex of next variable in the cover */
2480  idx = cover[i];
2481 
2482  /* nothing to do if the variable was already fixed, e.g., by propagation */
2483  if( SCIPisEQ(scip, SCIPvarGetLbLocal(vars[idx]), SCIPvarGetUbLocal(vars[idx])) )
2484  {
2485  fixingvals[i] = SCIPvarGetLbLocal(vars[idx]);
2486  continue;
2487  }
2488 
2489  /* we will store the fixing values already used to avoid try the same value twice */
2490  SCIP_CALL( SCIPallocBufferArray(scip, &usedvals, heurdata->maxbacktracks+1) );
2491  nusedvals = 0;
2492 
2493  /* backtracking loop */
2494  *infeas = TRUE;
2495  nfailedvals = 0;
2496  nboundalts = 0;
2497  boundalts = NULL;
2498  val = 0.0;
2499  for( nbacktracks = 0; nbacktracks <= heurdata->maxbacktracks+nfailedvals && *infeas; nbacktracks++ )
2500  {
2501  SCIP_Real oldlb;
2502  SCIP_Real oldub;
2503  SCIP_Bool usedbefore;
2504  int j;
2505 
2506  probingdepth = SCIPgetProbingDepth(scip);
2507 
2508  /* get fixing value */
2509  if( nbacktracks < heurdata->nfixingalts )
2510  {
2511  SCIP_Bool success;
2512 
2513  /* if the lp relaxation is not solved, we do not even try to retrieve the lp solution value;
2514  * if the NLP relaxation is not constructed, we do not even try to retrieve the NLP solution value;
2515  * if there is no feasible solution yet, we do not even try to obtain the value in the incumbent */
2516  success = FALSE;
2517  if( (heurdata->fixingalts[nbacktracks] != 'l' || lpsolved)
2518  && (heurdata->fixingalts[nbacktracks] != 'n' || !heurdata->nlpfailed)
2519  && (heurdata->fixingalts[nbacktracks] != 'i' || SCIPgetBestSol(scip) != NULL) )
2520  {
2521  SCIP_CALL( getFixingValue(scip, heurdata, vars[idx], &val, heurdata->fixingalts[nbacktracks], &success, *bdlen, bdvars, bdtypes, oldbounds) );
2522  }
2523 
2524  if( !success )
2525  {
2526  SCIPdebugMessage("retrieving fixing value '%c' for variable <%s> failed, trying next in the list\n",
2527  heurdata->fixingalts[nbacktracks], SCIPvarGetName(vars[idx]));
2528  nfailedvals++;
2529  continue;
2530  }
2531 
2532  /* for the first (successfully retrieved) fixing value, compute (at most 4) bound dependent
2533  * alternative fixing values */
2534  if( boundalts == NULL )
2535  {
2536  SCIP_CALL( SCIPallocBufferArray(scip, &boundalts, 4) );
2537  nboundalts = 0;
2538  calculateAlternatives(scip, vars[idx], val, &nboundalts, boundalts);
2539  assert(nboundalts >= 0);
2540  assert(nboundalts <= 4);
2541  }
2542  }
2543  /* get alternative fixing value */
2544  else if( boundalts != NULL && nbacktracks < heurdata->nfixingalts+nboundalts )
2545  {
2546  assert(nbacktracks-heurdata->nfixingalts >= 0);
2547  val = boundalts[nbacktracks-heurdata->nfixingalts];
2548  }
2549  else
2550  break;
2551 
2552  /* round fixing value */
2553  if( SCIPvarIsIntegral(vars[idx]) && !SCIPisIntegral(scip, val) )
2554  {
2555  SCIP_CALL( roundFixingValue(scip, &val, vars[idx], heurdata->locksrounding) );
2556  assert(SCIPisIntegral(scip, val));
2557  }
2558 
2559  /* move value into the domain, since it may be outside due to numerical issues or previous propagation */
2560  oldlb = SCIPvarGetLbLocal(vars[idx]);
2561  oldub = SCIPvarGetUbLocal(vars[idx]);
2562  val = MIN(val, oldub);
2563  val = MAX(val, oldlb);
2564 
2565  assert(!SCIPvarIsIntegral(vars[idx]) || SCIPisFeasIntegral(scip, val));
2566 
2567  /* check if this fixing value was already used */
2568  usedbefore = FALSE;
2569  for( j = nusedvals-1; j >= 0 && !usedbefore; j-- )
2570  usedbefore = SCIPisFeasEQ(scip, val, usedvals[j]);
2571 
2572  if( usedbefore )
2573  {
2574  nfailedvals++;
2575  continue;
2576  }
2577 
2578  /* store fixing value */
2579  assert(nusedvals < heurdata->maxbacktracks);
2580  usedvals[nusedvals] = val;
2581  nusedvals++;
2582 
2583  /* fix-propagate-analyze */
2584  SCIP_CALL( performFixing(scip, vars[idx], val, infeas, bdlen, bdvars, bdtypes, bdbounds, oldbounds) );
2585 
2586  /* if infeasible, backtrack and try alternative fixing value */
2587  if( *infeas )
2588  {
2589  SCIPdebugMessage(" --> cutoff detected - backtracking\n");
2590  SCIP_CALL( SCIPbacktrackProbing(scip, probingdepth) );
2591  }
2592  }
2593 
2594  /* free array of alternative backtracking values */
2595  if( boundalts != NULL)
2596  SCIPfreeBufferArray(scip, &boundalts);
2597  SCIPfreeBufferArray(scip, &usedvals);
2598 
2599  /* backtracking loop unsuccessful */
2600  if( *infeas )
2601  {
2602  SCIPdebugMessage("no feasible fixing value found for variable <%s> in fixing order\n",
2603  SCIPvarGetName(vars[idx]));
2604  break;
2605  }
2606  /* fixing successful */
2607  else
2608  {
2609  /* store successful fixing value */
2610  fixingvals[i] = val;
2611 
2612  /* statistics */
2613  if( SCIPvarGetType(vars[idx]) == SCIP_VARTYPE_CONTINUOUS )
2614  (*nfixedconts)++;
2615  else
2616  (*nfixedints)++;
2617  }
2618  }
2619  assert(*infeas || i == coversize);
2620  assert(!(*infeas) || i < coversize);
2621 
2622  /* end of dive */
2623  SCIP_CALL( SCIPendProbing(scip) );
2624 
2625  *lastfailed = i;
2626 
2627  return SCIP_OKAY;
2628 }
2629 
2630 /** main procedure of the undercover heuristic */
2631 static
2633  SCIP* scip, /**< original SCIP data structure */
2634  SCIP_HEUR* heur, /**< heuristic data structure */
2635  SCIP_RESULT* result, /**< result data structure */
2636  SCIP_Real timelimit, /**< time limit */
2637  SCIP_Real memorylimit, /**< memory limit */
2638  SCIP_Longint nstallnodes /**< number of stalling nodes for the subproblem */
2639  )
2640 {
2641  SCIP_HEURDATA* heurdata; /* heuristic data */
2642  SCIP_VAR** vars; /* original problem's variables */
2643  SCIP_CLOCK* clock; /* clock for updating time limit */
2644 
2645  SCIP* coveringscip; /* SCIP data structure for covering problem */
2646  SCIP_VAR** coveringvars; /* covering variables */
2647  SCIP_Real* fixingvals; /* array for storing fixing values used */
2648  int* cover; /* array to store problem indices of variables in the computed cover */
2649 
2650  SCIP_VAR** bdvars; /* array of variables in bound disjunction along the probing path */
2651  SCIP_BOUNDTYPE* bdtypes; /* array of bound types in bound disjunction along the probing path */
2652  SCIP_Real* bdbounds; /* array of bounds in bound disjunction along the probing path */
2653  SCIP_Real* oldbounds; /* array of bounds before fixing along the probing path */
2654 
2655  SCIP_Real maxcoversize;
2656 
2657  int coversize;
2658  int nvars;
2659  int ncovers;
2660  int nunfixeds;
2661  int nnlconss;
2662  int i;
2663 
2664  SCIP_Bool success;
2665  SCIP_Bool reusecover;
2666 
2667  assert(scip != NULL);
2668  assert(heur != NULL);
2669  assert(result != NULL);
2670  assert(*result == SCIP_DIDNOTFIND);
2671 
2672  /* create and start timing */
2673  SCIP_CALL( SCIPcreateClock(scip, &clock) );
2674  SCIP_CALL( SCIPstartClock(scip, clock) );
2675 
2676  /* initialize */
2677  fixingvals = NULL;
2678  cover = NULL;
2679  bdvars = NULL;
2680  bdtypes = NULL;
2681  bdbounds = NULL;
2682  oldbounds = NULL;
2683  coversize = 0;
2684 
2685  /* get heuristic data */
2686  heurdata = SCIPheurGetData(heur);
2687  assert(heurdata != NULL);
2688 
2689  /* NLP relaxation has not been solved yet (only solve once, not again for each cover or dive, because it is expensive) */
2690  heurdata->nlpsolved = FALSE;
2691 
2692  /* if solving the NLP relaxation has failed too often in previous runs, or NLP and NLP solver is not available, we do
2693  * not even try
2694  */
2695  heurdata->nlpfailed = heurdata->nnlpfails >= MAXNLPFAILS || !SCIPisNLPConstructed(scip) || SCIPgetNNlpis(scip) == 0;
2696 
2697  /* get variable data of original problem */
2698  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2699 
2700  /* get number of nonlinear constraints */
2701  nnlconss = 0;
2702  for( i = 0; i < heurdata->nnlconshdlrs; ++i )
2703  nnlconss += SCIPconshdlrGetNActiveConss(heurdata->nlconshdlrs[i]);
2704  assert(nnlconss >= 0);
2705  assert(nnlconss <= SCIPgetNConss(scip));
2706 
2707  /* run only if problem is sufficiently nonlinear */
2708  if( nnlconss < (SCIP_Real) SCIPgetNConss(scip) * heurdata->mincoveredrel || nnlconss < heurdata->mincoveredabs )
2709  {
2710  SCIPdebugMessage("too few nonlinear constraints present, not running\n");
2711 
2712  /* free clock */
2713  SCIP_CALL( SCIPfreeClock(scip, &clock) );
2714 
2715  return SCIP_OKAY;
2716  }
2717 
2718  /* calculate upper bound for cover size */
2719  maxcoversize = nvars*heurdata->maxcoversizevars;
2720  if( heurdata->maxcoversizeconss < SCIP_REAL_MAX )
2721  {
2722  SCIP_Real maxcoversizeconss;
2723  maxcoversizeconss = heurdata->maxcoversizeconss * nnlconss / ((SCIP_Real) SCIPgetNConss(scip));
2724  maxcoversize = MIN(maxcoversize, maxcoversizeconss);
2725  }
2726 
2727  /* create covering problem */
2728  success = FALSE;
2729  SCIP_CALL( SCIPcreate(&coveringscip) );
2730  SCIP_CALL( SCIPincludeDefaultPlugins(coveringscip) );
2731  SCIP_CALL( SCIPallocBufferArray(scip, &coveringvars, nvars) );
2732  SCIP_CALL( createCoveringProblem(scip, coveringscip, coveringvars, heurdata->globalbounds, heurdata->onlyconvexify,
2733  heurdata->coverbd, heurdata->coveringobj, &success) );
2734 
2735  if( !success )
2736  {
2737  SCIPdebugMessage("creating covering problem failed, terminating\n");
2738  goto TERMINATE;
2739  }
2740  else
2741  {
2742  SCIPdebugMessage("covering problem created successfully\n");
2743  }
2744 
2745  /* count number of unfixed covering variables */
2746  nunfixeds = 0;
2747  for( i = nvars-1; i >= 0; i-- )
2748  {
2749  if( SCIPisFeasEQ(coveringscip, SCIPvarGetLbGlobal(coveringvars[i]), 1.0) )
2750  nunfixeds++;
2751  }
2752 
2753  /* update time limit */
2754  SCIP_CALL( updateTimelimit(scip, clock, &timelimit) );
2755 
2756  if( timelimit <= MINTIMELEFT )
2757  {
2758  SCIPdebugMessage("time limit hit, terminating\n");
2759  goto TERMINATE;
2760  }
2761 
2762  /* update memory left */
2763  memorylimit -= SCIPgetMemUsed(coveringscip)/1048576.0;
2764  memorylimit -= SCIPgetMemExternEstim(coveringscip)/1048576.0;
2765 
2766  /* allocate memory for storing bound disjunction information along probing path */
2767  SCIP_CALL( SCIPallocBufferArray(scip, &bdvars, 2*nvars) );
2768  SCIP_CALL( SCIPallocBufferArray(scip, &bdtypes, 2*nvars) );
2769  SCIP_CALL( SCIPallocBufferArray(scip, &bdbounds, 2*nvars) );
2770  SCIP_CALL( SCIPallocBufferArray(scip, &oldbounds, 2*nvars) );
2771 
2772  /* initialize data for recovering loop */
2773  SCIP_CALL( SCIPallocBufferArray(scip, &cover, nvars) );
2774  SCIP_CALL( SCIPallocBufferArray(scip, &fixingvals, nvars) );
2775  ncovers = 0;
2776  success = FALSE;
2777  reusecover = FALSE;
2778 
2779  heurdata->nfixingalts = (int) strlen(heurdata->fixingalts);
2780  assert(heurdata->nfixingalts >= 1);
2781 
2782  /* recovering loop */
2783  while( (ncovers <= heurdata->maxrecovers || reusecover) && !success )
2784  {
2785  int lastfailed;
2786  int ndives;
2787  int nfixedints;
2788  int nfixedconts;
2789  int bdlen; /* current length of bound disjunction along the probing path */
2790 
2791  SCIP_Bool conflictcreated;
2792 
2793  SCIPdebugMessage("solving covering problem\n\n");
2794  success = FALSE;
2795  bdlen = 0;
2796  conflictcreated = FALSE;
2797 
2798  /* solve covering problem */
2799  if( !reusecover )
2800  {
2801  SCIP_CALL( solveCoveringProblem(coveringscip, nvars, coveringvars, &coversize, cover,
2802  timelimit, memorylimit + (SCIPgetMemExternEstim(coveringscip)+SCIPgetMemUsed(coveringscip))/1048576.0, maxcoversize, &success) );
2803 
2804  SCIPstatistic(
2805  if( ncovers == 0 && success )
2806  SCIPstatisticPrintf("UCstats coversize abs: %6d rel: %9.6f\n", coversize, 100.0*coversize /(SCIP_Real)nvars);
2807  );
2808 
2809  assert(coversize >= 0);
2810  assert(coversize <= nvars);
2811  ncovers++;
2812 
2813  /* free transformed covering problem immediately */
2814  SCIP_CALL( SCIPfreeTransform(coveringscip) );
2815 
2816  /* terminate if no feasible cover was found */
2817  if( !success )
2818  {
2819  SCIPdebugMessage("no feasible cover found in covering problem %d, terminating\n", ncovers);
2820  goto TERMINATE;
2821  }
2822 
2823  /* terminate, if cover is empty or too large */
2824  if( coversize == 0 || coversize > maxcoversize )
2825  {
2826  SCIPdebugMessage("terminating due to coversize=%d\n", coversize);
2827  goto TERMINATE;
2828  }
2829 
2830  /* terminate, if cover too large for the ratio of nonlinear constraints */
2831  if( heurdata->maxcoversizeconss < SCIP_REAL_MAX && coversize > heurdata->maxcoversizeconss * nnlconss / (SCIP_Real) SCIPgetNConss(scip) )
2832  {
2833  SCIPdebugMessage("terminating due to coversize=%d\n", coversize);
2834  goto TERMINATE;
2835  }
2836  }
2837 
2838  /* data setup */
2839  ndives = 0;
2840  nfixedints = 0;
2841  nfixedconts = 0;
2842  success = FALSE;
2843  lastfailed = reusecover ? MAX(1, coversize-1) : coversize;
2844 
2845  /* round-fix-propagate-analyze-backtrack-reorder loop */
2846  while( ndives <= heurdata->maxreorders && !success )
2847  {
2848  SCIP_Bool reordered;
2849  SCIP_Bool infeas;
2850 
2851  /* compute fixing order */
2852  SCIP_CALL( computeFixingOrder(scip, heurdata, nvars, vars, coversize, cover, lastfailed, &reordered) );
2853  reordered = reordered || ndives == 0;
2854  SCIPdebugMessage("%sordering variables in cover %s\n", ndives == 0 ? "" : "re", reordered ? "" : "failed");
2855 
2856  /* stop if order has not changed */
2857  if( !reordered )
2858  break;
2859 
2860  infeas = FALSE;
2861  SCIP_CALL( fixAndPropagate(scip, heurdata, cover, coversize, fixingvals, &bdlen, bdvars, bdtypes, bdbounds, oldbounds,
2862  &nfixedints, &nfixedconts, &lastfailed, &infeas) );
2863  ndives++;
2864  success = !infeas;
2865  }
2866 
2867  /* update time limit */
2868  SCIPdebugMessage("%d dive%s of fix-and-propagate for cover %d took %.1f seconds\n", ndives, ndives > 1 ? "s" : "", ncovers, SCIPgetClockTime(scip, clock));
2869  SCIP_CALL( updateTimelimit(scip, clock, &timelimit) );
2870 
2871  if( timelimit <= MINTIMELEFT )
2872  {
2873  SCIPdebugMessage("time limit hit, terminating\n");
2874  goto TERMINATE;
2875  }
2876 
2877  /* no feasible fixing could be found for the current cover */
2878  if( !success )
2879  {
2880  SCIPdebugMessage("no feasible fixing values found for cover %d\n", ncovers);
2881  }
2882  else
2883  {
2884  SCIP_SOL* sol;
2885  SCIP_Longint nsubnodes;
2886  SCIP_Bool validsolved;
2887 
2888  SCIPdebugMessage("heuristic successfully fixed %d variables (%d integral, %d continuous) during probing\n",
2889  nfixedints+nfixedconts, nfixedints, nfixedconts); /*lint !e771*/
2890 
2891  /* solve sub-CIP and pass feasible solutions to original problem */
2892  success = FALSE;
2893  validsolved = FALSE;
2894  sol = NULL;
2895  nsubnodes = 0;
2896 
2897  SCIP_CALL( solveSubproblem(scip, heur, coversize, cover, fixingvals,
2898  timelimit, memorylimit, heurdata->maxnodes, nstallnodes, &validsolved, &sol, &nsubnodes) );
2899 
2900  /* update number of sub-CIP nodes used by heuristic so far */
2901  heurdata->nusednodes += nsubnodes;
2902 
2903  /* if the subproblem was constructed from a valid copy and solved, try to forbid the assignment of fixing
2904  * values to variables in the cover
2905  */
2906  if( validsolved )
2907  {
2908  SCIP_Real maxvarsfac;
2909  SCIP_Bool useconf;
2910  int minmaxvars;
2911 
2912  SCIP_CALL( SCIPgetIntParam(scip, "conflict/minmaxvars", &minmaxvars) );
2913  SCIP_CALL( SCIPgetRealParam(scip, "conflict/maxvarsfac", &maxvarsfac) );
2914 
2915  useconf = bdlen > 0 && (bdlen <= minmaxvars || bdlen < maxvarsfac*nvars);
2916 
2917  if( useconf )
2918  {
2919  /* even if we had reset the global bounds at start of probing, the constraint might be only locally valid due to local constraints/cuts */
2920  SCIP_CALL( createConflict(scip, bdlen, bdvars, bdtypes, bdbounds, SCIPgetDepth(scip) > 0, TRUE, TRUE, &success) );
2921  conflictcreated = success;
2922  }
2923 
2924  SCIPdebugMessage("subproblem solved (%s), forbidding assignment in original problem %s, %sconflict length=%d\n",
2925  sol == NULL ? "infeasible" : "optimal",
2926  success ? "successful" : "failed", useconf ? "" : "skipped due to ", bdlen);
2927  }
2928 
2929  /* heuristic succeeded */
2930  success = (sol != NULL);
2931  if( success )
2932  {
2933  *result = SCIP_FOUNDSOL;
2934  success = TRUE;
2935 
2936  /* update time limit */
2937  SCIP_CALL( updateTimelimit(scip, clock, &timelimit) );
2938 
2939  /* call NLP local search heuristic unless it has failed too often */
2940  if( heurdata->postnlp && heurdata->npostnlpfails < MAXPOSTNLPFAILS )
2941  {
2942  if( nfixedconts == 0 && validsolved )
2943  {
2944  SCIPdebugMessage("subproblem solved to optimality while all covering variables are integral, hence skipping NLP local search\n");
2945  }
2946  else if( timelimit <= MINTIMELEFT )
2947  {
2948  SCIPdebugMessage("time limit hit, skipping NLP local search\n");
2949  }
2950  else if( heurdata->nlpheur == NULL )
2951  {
2952  SCIPdebugMessage("NLP heuristic not found, skipping NLP local search\n");
2953  }
2954  else
2955  {
2956  SCIP_RESULT nlpresult;
2957 
2958  SCIP_CALL( SCIPapplyHeurSubNlp(scip, heurdata->nlpheur, &nlpresult, sol, -1LL, timelimit, heurdata->minimprove, NULL) );
2959  SCIPdebugMessage("NLP local search %s\n", nlpresult == SCIP_FOUNDSOL ? "successful" : "failed");
2960 
2961  if( nlpresult == SCIP_FOUNDSOL )
2962  heurdata->npostnlpfails = 0;
2963  else
2964  heurdata->npostnlpfails++;
2965  }
2966  }
2967 
2968  /* free solution */
2969  SCIP_CALL( SCIPfreeSol(scip, &sol) );
2970  }
2971  }
2972 
2973  /* heuristic failed but we have another recovering try, hence we forbid the current cover in the covering problem */
2974  if( !success && ncovers <= heurdata->maxrecovers )
2975  {
2976  SCIP_Bool infeas;
2977  int diversification;
2978 
2979  /* compute minimal number of unfixed covering variables (in the cover) which have to change their value */
2980  diversification = (int) SCIPfeasCeil(scip, (heurdata->recoverdiv) * (SCIP_Real) nunfixeds);
2981  diversification = MAX(diversification, 1);
2982 
2983  /* forbid unsuccessful cover globally in covering problem */
2984  SCIP_CALL( forbidCover(coveringscip, nvars, coveringvars, coversize, cover, diversification, &success, &infeas) );
2985 
2986  if( infeas )
2987  {
2988  SCIPdebugMessage("recovering problem infeasible (diversification=%d), terminating\n", diversification);
2989  goto TERMINATE;
2990  }
2991  else if( !success )
2992  {
2993  SCIPdebugMessage("failed to forbid current cover in the covering problem, terminating\n");
2994  goto TERMINATE;
2995  }
2996  else
2997  {
2998  SCIPdebugMessage("added constraint to the covering problem in order to forbid current cover\n");
2999  success = FALSE;
3000  }
3001  }
3002 
3003  /* try to re-use the same cover at most once */
3004  if( heurdata->reusecover && !reusecover && conflictcreated )
3005  reusecover = TRUE;
3006  else
3007  reusecover = FALSE;
3008  }
3009 
3010  TERMINATE:
3011  if( *result != SCIP_FOUNDSOL && *result != SCIP_DELAYED )
3012  {
3013  SCIPdebugMessage("heuristic terminating unsuccessfully\n");
3014  }
3015 
3016  /* we must remain in NLP diving mode until here to be able to retrieve NLP solution values easily */
3017  /* assert((SCIPisNLPConstructed(scip) == FALSE && heurdata->nlpsolved == FALSE) ||
3018  * (SCIPisNLPConstructed(scip) == TRUE && heurdata->nlpsolved == SCIPnlpIsDiving(SCIPgetNLP(scip))));
3019  */
3020  if( heurdata->nlpsolved )
3021  {
3022  SCIP_CALL( SCIPendDiveNLP(scip) );
3023  }
3024 
3025  /* free arrays for storing the cover */
3026  SCIPfreeBufferArrayNull(scip, &fixingvals);
3027  SCIPfreeBufferArrayNull(scip, &cover);
3028 
3029  /* free arrays for storing bound disjunction information along probing path */
3030  SCIPfreeBufferArrayNull(scip, &oldbounds);
3031  SCIPfreeBufferArrayNull(scip, &bdbounds);
3032  SCIPfreeBufferArrayNull(scip, &bdtypes);
3033  SCIPfreeBufferArrayNull(scip, &bdvars);
3034 
3035  /* free covering problem */
3036  for( i = nvars-1; i >= 0; i-- )
3037  {
3038  SCIP_CALL( SCIPreleaseVar(coveringscip, &coveringvars[i]) );
3039  }
3040  SCIPfreeBufferArray(scip, &coveringvars);
3041  SCIP_CALL( SCIPfree(&coveringscip) );
3042 
3043  /* free clock */
3044  SCIP_CALL( SCIPfreeClock(scip, &clock) );
3045 
3046  return SCIP_OKAY;
3047 }
3048 
3049 
3050 /*
3051  * Callback methods of primal heuristic
3052  */
3053 
3054 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
3055 static
3056 SCIP_DECL_HEURCOPY(heurCopyUndercover)
3057 { /*lint --e{715}*/
3058  assert(scip != NULL);
3059  assert(heur != NULL);
3060  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
3061 
3062  /* call inclusion method of primal heuristic */
3064 
3065  return SCIP_OKAY;
3066 }
3067 
3068 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
3069 static
3070 SCIP_DECL_HEURFREE(heurFreeUndercover)
3071 { /*lint --e{715}*/
3072  SCIP_HEURDATA* heurdata;
3073 
3074  assert(scip != NULL);
3075  assert(heur != NULL);
3076 
3077  /* get heuristic data */
3078  heurdata = SCIPheurGetData(heur);
3079  assert(heurdata != NULL);
3080 
3081  /* free heuristic data */
3082  SCIPfreeMemory(scip, &heurdata);
3083  SCIPheurSetData(heur, NULL);
3084 
3085  return SCIP_OKAY;
3086 }
3087 
3088 
3089 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
3090 static
3091 SCIP_DECL_HEURINITSOL(heurInitsolUndercover)
3092 { /*lint --e{715}*/
3093  SCIP_HEURDATA* heurdata;
3094  int h;
3095 
3096  assert(heur != NULL);
3097  assert(scip != NULL);
3098 
3099  /* get heuristic's data */
3100  heurdata = SCIPheurGetData(heur);
3101  assert(heurdata != NULL);
3102 
3103  /* initialize counters to zero */
3104  heurdata->nusednodes = 0;
3105  heurdata->npostnlpfails = 0;
3106  heurdata->nnlpfails = 0;
3107 
3108  /* if the heuristic is called at the root node, we may want to be called directly after the initial root LP solve */
3109  if( heurdata->beforecuts && SCIPheurGetFreqofs(heur) == 0 )
3111 
3112  /* find nonlinear constraint handlers */
3113  SCIP_CALL( SCIPallocMemoryArray(scip, &heurdata->nlconshdlrs, 7) );/*lint !e506*/
3114  h = 0;
3115 
3116  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "and");
3117  if( heurdata->nlconshdlrs[h] != NULL )
3118  h++;
3119 
3120  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "quadratic");
3121  if( heurdata->nlconshdlrs[h] != NULL )
3122  h++;
3123 
3124  if( heurdata->coverbd )
3125  {
3126  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "bounddisjunction");
3127  if( heurdata->nlconshdlrs[h] != NULL )
3128  h++;
3129  }
3130 
3131  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "indicator");
3132  if( heurdata->nlconshdlrs[h] != NULL )
3133  h++;
3134 
3135  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "soc");
3136  if( heurdata->nlconshdlrs[h] != NULL )
3137  h++;
3138 
3139  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "nonlinear");
3140  if( heurdata->nlconshdlrs[h] != NULL )
3141  h++;
3142 
3143  heurdata->nlconshdlrs[h] = SCIPfindConshdlr(scip, "abspower");
3144  if( heurdata->nlconshdlrs[h] != NULL )
3145  h++;
3146 
3147  heurdata->nnlconshdlrs = h;
3148 
3149  /* find NLP local search heuristic */
3150  heurdata->nlpheur = SCIPfindHeur(scip, "subnlp");
3151 
3152  /* add global linear constraints to NLP relaxation */
3153  if( SCIPisNLPConstructed(scip) && heurdata->nlpheur != NULL )
3154  {
3155  SCIP_CALL( SCIPaddLinearConsToNlpHeurSubNlp(scip, heurdata->nlpheur, TRUE, TRUE) );
3156  }
3157 
3158  return SCIP_OKAY;
3159 }
3160 
3161 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed) */
3162 static
3163 SCIP_DECL_HEUREXITSOL(heurExitsolUndercover)
3164 {
3165  SCIP_HEURDATA* heurdata;
3166 
3167  assert(heur != NULL);
3168  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
3169 
3170  /* get heuristic's data */
3171  heurdata = SCIPheurGetData(heur);
3172  assert(heurdata != NULL);
3173 
3174  /* free array of nonlinear constraint handlers */
3175  SCIPfreeMemoryArray(scip, &heurdata->nlconshdlrs);
3176 
3177  /* reset timing, if it was changed temporary (at the root node) */
3179 
3180  return SCIP_OKAY;
3181 }
3182 
3183 /** execution method of primal heuristic */
3184 static
3185 SCIP_DECL_HEUREXEC(heurExecUndercover)
3186 { /*lint --e{715}*/
3187  SCIP_HEURDATA* heurdata; /* heuristic data */
3188  SCIP_Real timelimit; /* time limit for the subproblem */
3189  SCIP_Real memorylimit; /* memory limit for the subproblem */
3190  SCIP_Longint nstallnodes; /* number of stalling nodes for the subproblem */
3191  SCIP_Bool run;
3192 
3193  int h;
3194 
3195  assert(heur != NULL);
3196  assert(scip != NULL);
3197  assert(result != NULL);
3198 
3199  *result = SCIP_DIDNOTRUN;
3200 
3201  /* do not call heuristic of node was already detected to be infeasible */
3202  if( nodeinfeasible )
3203  return SCIP_OKAY;
3204 
3205  /* get heuristic's data */
3206  heurdata = SCIPheurGetData(heur);
3207  assert(heurdata != NULL);
3208 
3209  /* only call heuristic once at the root */
3210  if( SCIPgetDepth(scip) == 0 && SCIPheurGetNCalls(heur) > 0 )
3211  return SCIP_OKAY;
3212 
3213  /* if we want to use NLP fixing values exclusively and no NLP solver is available, we cannot run */
3214  if( strcmp(heurdata->fixingalts, "n") == 0 && SCIPgetNNlpis(scip) == 0 )
3215  {
3216  SCIPdebugMessage("skipping undercover heuristic: want to use NLP fixing values exclusively, but no NLP solver available\n");
3217  return SCIP_OKAY;
3218  }
3219 
3220  /* calculate stallnode limit */
3221  nstallnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip));
3222 
3223  /* reward heuristic if it succeeded often */
3224  nstallnodes = (SCIP_Longint)(nstallnodes * 3.0 * (SCIPheurGetNBestSolsFound(heur) + 1.0)/(SCIPheurGetNCalls(heur) + 1.0));
3225  nstallnodes -= SUBMIPSETUPCOSTS * SCIPheurGetNCalls(heur); /* account for the setup costs of the sub-CIP */
3226  nstallnodes += heurdata->nodesofs;
3227 
3228  /* determine the node limit for the current process */
3229  nstallnodes -= heurdata->nusednodes;
3230  nstallnodes = MIN(nstallnodes, heurdata->maxnodes);
3231  nstallnodes = MAX(nstallnodes, 1);
3232 
3233  /* only call heuristics if we have enough nodes left to call sub-CIP solving */
3234  if( nstallnodes < heurdata->minnodes )
3235  {
3236  SCIPdebugMessage("skipping undercover heuristic: nstallnodes=%"SCIP_LONGINT_FORMAT", minnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, heurdata->minnodes);
3237  return SCIP_OKAY;
3238  }
3239 
3240  /* only call heuristics if we have enough time left */
3241  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
3242  if( !SCIPisInfinity(scip, timelimit) )
3243  timelimit -= SCIPgetSolvingTime(scip);
3244  if( timelimit <= 2*MINTIMELEFT )
3245  {
3246  SCIPdebugMessage("skipping undercover heuristic: time left=%g\n", timelimit);
3247  return SCIP_OKAY;
3248  }
3249 
3250  /* only call heuristics if we have enough memory left */
3251  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
3252  if( !SCIPisInfinity(scip, memorylimit) )
3253  {
3254  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
3255  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
3256  }
3257 
3258  if( memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 )
3259  {
3260  SCIPdebugMessage("skipping undercover heuristic: too little memory\n");
3261  return SCIP_OKAY;
3262  }
3263 
3264  /* only call heuristic if nonlinear constraints are present */
3265  run = FALSE;
3266  for( h = heurdata->nnlconshdlrs-1; h >= 0 && !run; h-- )
3267  {
3268  run = (SCIPconshdlrGetNActiveConss(heurdata->nlconshdlrs[h]) > 0);
3269  }
3270 
3271  /* go through all nlrows and check for general nonlinearities */
3272  if( SCIPisNLPConstructed(scip) )
3273  {
3274  SCIP_NLROW** nlrows;
3275  int nnlrows;
3276  int i;
3277 
3278  /* get nlrows */
3279  nnlrows = SCIPgetNNLPNlRows(scip);
3280  nlrows = SCIPgetNLPNlRows(scip);
3281 
3282  /* check for an nlrow with nontrivial expression tree or quadratic terms; start from 0 since we expect the linear
3283  * nlrows at the end */
3284  for( i = nnlrows-1; i >= 0 && !run; i-- )
3285  {
3286  assert(nlrows[i] != NULL);
3287  run = SCIPnlrowGetExprtree(nlrows[i]) != NULL && SCIPexprtreeGetNVars(SCIPnlrowGetExprtree(nlrows[i])) > 0;
3288  run = run || SCIPnlrowGetNQuadVars(nlrows[i]) > 0;
3289  }
3290  }
3291 
3292  if( !run )
3293  {
3294  SCIPdebugMessage("skipping undercover heuristic: no nonlinear constraints found\n");
3295  return SCIP_OKAY;
3296  }
3297 
3298  /* only call heuristics if solving has not stopped yet */
3299  if( SCIPisStopped(scip) )
3300  return SCIP_OKAY;
3301 
3302  /* reset timing, if it was changed temporary (at the root node) */
3303  if( heurtiming != HEUR_TIMING )
3305 
3306  /* call heuristic */
3307  *result = SCIP_DIDNOTFIND;
3308  SCIPdebugMessage("calling undercover heuristic for <%s> at depth %d\n", SCIPgetProbName(scip), SCIPgetDepth(scip));
3309 
3310  SCIP_CALL( SCIPapplyUndercover(scip, heur, result, timelimit, memorylimit, nstallnodes) );
3311 
3312  return SCIP_OKAY;
3313 }
3314 
3315 
3316 /*
3317  * primal heuristic specific interface methods
3318  */
3319 
3320 /** creates the undercover primal heuristic and includes it in SCIP */
3322  SCIP* scip /**< SCIP data structure */
3323  )
3324 {
3325  SCIP_HEURDATA* heurdata;
3326  SCIP_HEUR* heur;
3327 
3328  /* create undercover primal heuristic data */
3329  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
3330 
3331  /* always use local bounds */
3332  heurdata->globalbounds = FALSE;
3333 
3334  /* include primal heuristic */
3335  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
3337  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecUndercover, heurdata) );
3338 
3339  assert(heur != NULL);
3340 
3341  /* set non-NULL pointers to callback methods */
3342  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyUndercover) );
3343  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeUndercover) );
3344  SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolUndercover) );
3345  SCIP_CALL( SCIPsetHeurExitsol(scip, heur, heurExitsolUndercover) );
3346 
3347  /* add string parameters */
3348  heurdata->fixingalts = NULL;
3349  SCIP_CALL( SCIPaddStringParam(scip, "heuristics/"HEUR_NAME"/fixingalts",
3350  "prioritized sequence of fixing values used ('l'p relaxation, 'n'lp relaxation, 'i'ncumbent solution)",
3351  &heurdata->fixingalts, FALSE, DEFAULT_FIXINGALTS, NULL, NULL) );
3352 
3353  /* add longint parameters */
3354  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/maxnodes",
3355  "maximum number of nodes to regard in the subproblem",
3356  &heurdata->maxnodes, TRUE, DEFAULT_MAXNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
3357 
3358  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/minnodes",
3359  "minimum number of nodes required to start the subproblem",
3360  &heurdata->minnodes, TRUE, DEFAULT_MINNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
3361 
3362  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/nodesofs",
3363  "number of nodes added to the contingent of the total nodes",
3364  &heurdata->nodesofs, FALSE, DEFAULT_NODESOFS, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
3365 
3366  /* add real parameters */
3367  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/conflictweight",
3368  "weight for conflict score in fixing order",
3369  &heurdata->conflictweight, TRUE, DEFAULT_CONFLICTWEIGHT, SCIP_REAL_MIN, SCIP_REAL_MAX, NULL, NULL) );
3370 
3371  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/cutoffweight",
3372  "weight for cutoff score in fixing order",
3373  &heurdata->cutoffweight, TRUE, DEFAULT_CUTOFFWEIGHT, 0.0, SCIP_REAL_MAX, NULL, NULL) );
3374 
3375  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/inferenceweight",
3376  "weight for inference score in fixing order",
3377  &heurdata->inferenceweight, TRUE, DEFAULT_INFERENCEWEIGHT, SCIP_REAL_MIN, SCIP_REAL_MAX, NULL, NULL) );
3378 
3379  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/maxcoversizevars",
3380  "maximum coversize (as fraction of total number of variables)",
3381  &heurdata->maxcoversizevars, TRUE, DEFAULT_MAXCOVERSIZEVARS, 0.0, 1.0, NULL, NULL) );
3382 
3383  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/maxcoversizeconss",
3384  "maximum coversize maximum coversize (as ratio to the percentage of non-affected constraints)",
3385  &heurdata->maxcoversizeconss, TRUE, DEFAULT_MAXCOVERSIZECONSS, 0.0, SCIP_REAL_MAX, NULL, NULL) );
3386 
3387  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/mincoveredrel",
3388  "minimum percentage of nonlinear constraints in the original problem",
3389  &heurdata->mincoveredrel, TRUE, DEFAULT_MINCOVEREDREL, 0.0, 1.0, NULL, NULL) );
3390 
3391  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minimprove",
3392  "factor by which the heuristic should at least improve the incumbent",
3393  &heurdata->minimprove, TRUE, DEFAULT_MINIMPROVE, -1.0, 1.0, NULL, NULL) );
3394 
3395  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/nodesquot",
3396  "contingent of sub problem nodes in relation to the number of nodes of the original problem",
3397  &heurdata->nodesquot, FALSE, DEFAULT_NODESQUOT, 0.0, 1.0, NULL, NULL) );
3398 
3399  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/recoverdiv",
3400  "fraction of covering variables in the last cover which need to change their value when recovering",
3401  &heurdata->recoverdiv, TRUE, DEFAULT_RECOVERDIV, 0.0, 1.0, NULL, NULL) );
3402 
3403  /* add int parameters */
3404  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/mincoveredabs",
3405  "minimum number of nonlinear constraints in the original problem",
3406  &heurdata->mincoveredabs, TRUE, DEFAULT_MINCOVEREDABS, 0, INT_MAX, NULL, NULL) );
3407 
3408  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxbacktracks",
3409  "maximum number of backtracks in fix-and-propagate",
3410  &heurdata->maxbacktracks, TRUE, DEFAULT_MAXBACKTRACKS, 0, INT_MAX, NULL, NULL) );
3411 
3412  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxrecovers",
3413  "maximum number of recoverings",
3414  &heurdata->maxrecovers, TRUE, DEFAULT_MAXRECOVERS, 0, INT_MAX, NULL, NULL) );
3415 
3416  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxreorders",
3417  "maximum number of reorderings of the fixing order",
3418  &heurdata->maxreorders, TRUE, DEFAULT_MAXREORDERS, 0, INT_MAX, NULL, NULL) );
3419 
3420  /* add char parameters */
3421  SCIP_CALL( SCIPaddCharParam(scip, "heuristics/"HEUR_NAME"/coveringobj",
3422  "objective function of the covering problem (influenced nonlinear 'c'onstraints/'t'erms, 'd'omain size, 'l'ocks, 'm'in of up/down locks, 'u'nit penalties)",
3423  &heurdata->coveringobj, TRUE, DEFAULT_COVERINGOBJ, COVERINGOBJS, NULL, NULL) );
3424 
3425  SCIP_CALL( SCIPaddCharParam(scip, "heuristics/"HEUR_NAME"/fixingorder",
3426  "order in which variables should be fixed (increasing 'C'onflict score, decreasing 'c'onflict score, increasing 'V'ariable index, decreasing 'v'ariable index",
3427  &heurdata->fixingorder, TRUE, DEFAULT_FIXINGORDER, FIXINGORDERS, NULL, NULL) );
3428 
3429  /* add bool parameters */
3430  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/beforecuts",
3431  "should the heuristic be called at root node before cut separation?",
3432  &heurdata->beforecuts, TRUE, DEFAULT_BEFORECUTS, NULL, NULL) );
3433 
3434  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/fixintfirst",
3435  "should integer variables in the cover be fixed first?",
3436  &heurdata->fixintfirst, TRUE, DEFAULT_FIXINTFIRST, NULL, NULL) );
3437 
3438  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/locksrounding",
3439  "shall LP values for integer vars be rounded according to locks?",
3440  &heurdata->locksrounding, TRUE, DEFAULT_LOCKSROUNDING, NULL, NULL) );
3441 
3442  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/onlyconvexify",
3443  "should we only fix variables in order to obtain a convex problem?",
3444  &heurdata->onlyconvexify, FALSE, DEFAULT_ONLYCONVEXIFY, NULL, NULL) );
3445 
3446  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/postnlp",
3447  "should the NLP heuristic be called to polish a feasible solution?",
3448  &heurdata->postnlp, FALSE, DEFAULT_POSTNLP, NULL, NULL) );
3449 
3450  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/coverbd",
3451  "should bounddisjunction constraints be covered (or just copied)?",
3452  &heurdata->coverbd, TRUE, DEFAULT_COVERBD, NULL, NULL) );
3453 
3454  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/copycuts",
3455  "should all active cuts from cutpool be copied to constraints in subproblem?",
3456  &heurdata->copycuts, TRUE, DEFAULT_COPYCUTS, NULL, NULL) );
3457 
3458  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/reusecover",
3459  "shall the cover be reused if a conflict was added after an infeasible subproblem?",
3460  &heurdata->reusecover, TRUE, DEFAULT_REUSECOVER, NULL, NULL) );
3461 
3462  return SCIP_OKAY;
3463 }
3464 
3465 
3466 /** computes a minimal set of covering variables */
3468  SCIP* scip, /**< SCIP data structure */
3469  int* coversize, /**< buffer for the size of the computed cover */
3470  SCIP_VAR** cover, /**< pointer to store the variables (of the original SCIP) in the computed cover
3471  * (should be ready to hold SCIPgetNVars(scip) entries) */
3472  SCIP_Real timelimit, /**< time limit */
3473  SCIP_Real memorylimit, /**< memory limit */
3474  SCIP_Real objlimit, /**< objective limit: upper bound on coversize */
3475  SCIP_Bool globalbounds, /**< should global bounds on variables be used instead of local bounds at focus node? */
3476  SCIP_Bool onlyconvexify, /**< should we only fix/dom.red. variables creating nonconvexity? */
3477  SCIP_Bool coverbd, /**< should bounddisjunction constraints be covered (or just copied)? */
3478  char coveringobj, /**< objective function of the covering problem ('b'ranching status,
3479  * influenced nonlinear 'c'onstraints/'t'erms, 'd'omain size, 'l'ocks,
3480  * 'm'in of up/down locks, 'u'nit penalties, constraint 'v'iolation) */
3481  SCIP_Bool* success /**< feasible cover found? */
3482  )
3483 {
3484  SCIP* coveringscip; /* SCIP instance for covering problem */
3485  SCIP_VAR** coveringvars; /* covering variables */
3486  SCIP_VAR** vars; /* original variables */
3487  int* coverinds; /* indices of variables in the cover */
3488  int nvars; /* number of original variables */
3489  int i;
3490 
3491  assert(scip != NULL);
3492  assert(coversize != NULL);
3493  assert(success != NULL);
3494 
3495  *success = FALSE;
3496 
3497  /* allocate memory for variables of the covering problem */
3498  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL ) );
3499  SCIP_CALL( SCIPallocBufferArray(scip, &coveringvars, nvars) );
3500  SCIP_CALL( SCIPallocBufferArray(scip, &coverinds, nvars) );
3501 
3502  /* create covering problem */
3503  SCIP_CALL( SCIPcreate(&coveringscip) );
3504  SCIP_CALL( SCIPincludeDefaultPlugins(coveringscip) );
3505  SCIP_CALL( createCoveringProblem(scip, coveringscip, coveringvars, globalbounds, onlyconvexify, coverbd, coveringobj, success) );
3506 
3507  if( *success )
3508  {
3509  /* solve covering problem */
3510  SCIPdebugMessage("solving covering problem\n\n");
3511 
3512  SCIP_CALL( solveCoveringProblem(coveringscip, nvars, coveringvars, coversize, coverinds,
3513  timelimit, memorylimit + (SCIPgetMemExternEstim(coveringscip)+SCIPgetMemUsed(coveringscip))/1048576.0, objlimit, success) );
3514 
3515  if( *success )
3516  {
3517  assert(*coversize >= 0);
3518  assert(*coversize <= nvars);
3519 
3520  /* return original variables in the cover */
3521  for( i = *coversize-1; i >= 0; i-- )
3522  {
3523  assert(coverinds[i] >= 0);
3524  assert(coverinds[i] < nvars);
3525  cover[i] = vars[coverinds[i]];
3526  }
3527  }
3528  }
3529  else
3530  {
3531  SCIPdebugMessage("failure: covering problem could not be created\n");
3532  }
3533 
3534  /* free covering problem */
3535  for( i = nvars-1; i >= 0; i-- )
3536  {
3537  SCIP_CALL( SCIPreleaseVar(coveringscip, &coveringvars[i]) );
3538  }
3539  SCIP_CALL( SCIPfree(&coveringscip) );
3540  SCIPfreeBufferArray(scip, &coverinds);
3541  SCIPfreeBufferArray(scip, &coveringvars);
3542 
3543  return SCIP_OKAY;
3544 }
3545