Scippy

SCIP

Solving Constraint Integer Programs

heur_clique.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_clique.c
17  * @brief LNS heuristic using a clique partition to restrict the search neighborhood
18  * @brief clique primal heuristic
19  * @author Stefan Heinz
20  * @author Michael Winkler
21  */
22 
23 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
24 
25 #include <assert.h>
26 #include <string.h>
27 
28 #include "scip/scip.h"
29 #include "scip/heur_clique.h"
30 #include "scip/cons_logicor.h"
31 
32 
33 #define HEUR_NAME "clique"
34 #define HEUR_DESC "LNS heuristic using a clique partition to restrict the search neighborhood"
35 #define HEUR_DISPCHAR 'Q'
36 #define HEUR_PRIORITY -1000500
37 #define HEUR_FREQ -1
38 #define HEUR_FREQOFS 0
39 #define HEUR_MAXDEPTH -1
40 #define HEUR_TIMING SCIP_HEURTIMING_BEFORENODE
41 #define HEUR_USESSUBSCIP TRUE /**< does the heuristic use a secondary SCIP instance? */
42 
43 #define DEFAULT_MAXNODES 5000LL /**< maximum number of nodes to regard in the subproblem */
44 #define DEFAULT_MINFIXINGRATE 0.5 /**< minimum percentage of integer variables that have to be fixed */
45 #define DEFAULT_MINIMPROVE 0.01 /**< factor by which clique heuristic should at least improve the
46  * incumbent
47  */
48 #define DEFAULT_MINNODES 500LL /**< minimum number of nodes to regard in the subproblem */
49 #define DEFAULT_NODESOFS 500LL /**< number of nodes added to the contingent of the total nodes */
50 #define DEFAULT_NODESQUOT 0.1 /**< subproblem nodes in relation to nodes of the original problem */
51 #define DEFAULT_MAXPROPROUNDS 2 /**< maximum number of propagation rounds during probing */
52 #define DEFAULT_INITSEED 0 /**< random seed value to initialize the random permutation
53  * value for variables
54  */
55 #define DEFAULT_MULTIPLIER 1.1 /**< value to increase node number to determine the next run */
56 #define DEFAULT_COPYCUTS TRUE /**< should all active cuts from the cutpool of the
57  * original scip be copied to constraints of the subscip
58  */
59 
60 
61 /*
62  * Data structures
63  */
64 
65 /** primal heuristic data */
66 struct SCIP_HeurData
67 {
68  SCIP_Longint maxnodes; /**< maximum number of nodes to regard in the subproblem */
69  SCIP_Longint minnodes; /**< minimum number of nodes to regard in the subproblem */
70  SCIP_Longint nodesofs; /**< number of nodes added to the contingent of the total nodes */
71  SCIP_Longint usednodes; /**< nodes already used by clique heuristic in earlier calls */
72  SCIP_Real minfixingrate; /**< minimum percentage of integer variables that have to be fixed */
73  SCIP_Real minimprove; /**< factor by which clique heuristic should at least improve the incumbent */
74  SCIP_Real nodesquot; /**< subproblem nodes in relation to nodes of the original problem */
75  int maxproprounds; /**< maximum number of propagation rounds during probing */
76  SCIP_Longint nnodefornextrun; /**< node number for next run */
77  SCIP_Real multiplier; /**< multiplier to determine next node number */
78  int initseed; /**< initial random seed value */
79  unsigned int seed; /**< seed value for random number generator */
80  SCIP_Bool copycuts; /**< should all active cuts from cutpool be copied to constraints in
81  * subproblem?
82  */
83 };
84 
85 /*
86  * Local methods
87  */
88 
89 /** comparison method for sorting variables by non-decreasing index */
90 static
92 {
93  SCIP_VAR* var1;
94  SCIP_VAR* var2;
95 
96  assert(elem1 != NULL);
97  assert(elem2 != NULL);
98 
99  var1 = (SCIP_VAR*)elem1;
100  var2 = (SCIP_VAR*)elem2;
101 
102  if( SCIPvarGetObj(var1) < SCIPvarGetObj(var2) )
103  return -1;
104  else if( SCIPvarGetObj(var1) > SCIPvarGetObj(var2) )
105  return +1;
106  else
107  return 0;
108 }
109 
110 /** sort the binary variable array w.r.t. the clique partition; thereby ensure the current order within the cliques are
111  * not changed
112  */
113 static
115  SCIP* scip, /**< SCIP data structure */
116  SCIP_VAR** binvars, /**< array of binary variables to sort */
117  int nbinvars, /**< number of binary variables */
118  int* cliquepartition, /**< clique partition to use */
119  int ncliques /**< number of cliques */
120  )
121 {
122  SCIP_VAR*** varpointers;
123  SCIP_VAR** vars;
124  int* cliquecount;
125  int nextpos;
126  int c;
127  int v;
128  int cliquenumber;
129 
130  assert(scip != NULL);
131  assert(binvars != NULL);
132  assert(cliquepartition != NULL);
133 
134  /* @note: we don't want to loose order from same clique numbers, so we need a stable sorting algorithm, or we first
135  * count all clique items and alloc temporary memory for a bucket sort */
136  /* sort variables after clique-numbers */
137  SCIP_CALL( SCIPallocBufferArray(scip, &cliquecount, ncliques) );
138  BMSclearMemoryArray(cliquecount, ncliques);
139 
140  /* first we count for each clique the number of elements */
141  for( v = nbinvars - 1; v >= 0; --v )
142  {
143  assert(0 <= cliquepartition[v] && cliquepartition[v] < ncliques);
144  ++(cliquecount[cliquepartition[v]]);
145  }
146 
147  SCIP_CALL( SCIPallocBufferArray(scip, &vars, nbinvars) );
148 #ifndef NDEBUG
149  BMSclearMemoryArray(vars, nbinvars);
150 #endif
151  SCIP_CALL( SCIPallocBufferArray(scip, &varpointers, ncliques) );
152 
153  nextpos = 0;
154  /* now we initialize all start pointers for each clique, so they will be ordered */
155  for( c = 0; c < ncliques; ++c )
156  {
157  /* to reach the goal that all variables of each clique will be standing next to each other we will initialize the
158  * starting pointers for each clique by adding the number of each clique to the last clique starting pointer
159  * e.g. clique1 has 4 elements and clique2 has 3 elements the the starting pointer for clique1 will be the pointer
160  * to vars[0], the starting pointer to clique2 will be the pointer to vars[4] and to clique3 it will be
161  * vars[7]
162  *
163  */
164  varpointers[c] = (SCIP_VAR**) (vars + nextpos);
165  assert(cliquecount[c] > 0);
166  nextpos += cliquecount[c];
167  assert(nextpos > 0);
168  }
169  assert(nextpos == nbinvars);
170 
171  /* now we copy all variable to the right order in our temporary variable array */
172  for( v = 0; v < nbinvars; ++v )
173  {
174  *(varpointers[cliquepartition[v]]) = binvars[v];
175  ++(varpointers[cliquepartition[v]]);
176  }
177 #ifndef NDEBUG
178  for( v = 0; v < nbinvars; ++v )
179  assert(vars[v] != NULL);
180 #endif
181 
182  /* move all variables back to our variable array */
183  BMScopyMemoryArray(binvars, vars, nbinvars);
184 
185  cliquenumber = 0;
186  nextpos = cliquecount[0];
187 
188  c = 1;
189  for( v = 0; v < nbinvars; ++v )
190  {
191  if( v == nextpos )
192  {
193  nextpos += cliquecount[c];
194  ++c;
195  ++cliquenumber;
196  }
197  cliquepartition[v] = cliquenumber;
198  }
199  assert(cliquepartition[v - 1] == ncliques - 1);
200 
201 #ifndef NDEBUG
202  for( v = 1; v < nbinvars; ++v )
203  assert(SCIPvarGetObj(binvars[v - 1]) <= SCIPvarGetObj(binvars[v - 1]));
204 #endif
205 
206 #if 0
207 #ifndef NDEBUG
208  c = -1;
209  for( v = 0; v < nbinvars; ++v )
210  {
211  if( cliquepartition[v] > c )
212  {
213  ++c;
214 #if 0
215  if( c == 1 )
216  break;
217 #endif
218  assert(cliquepartition[v] == c);
219  printf("Clique %d (%d elements): ",c, cliquecount[c]);
220  }
221 #if 0
222  printf("%s ", SCIPvarGetName(binvars[v]));
223 #endif
224  if( v < nbinvars - 1 && cliquepartition[v + 1] > c )
225  {
226  printf("\n");
227  }
228  }
229  printf("\n");
230 #endif
231 #endif
232 
233  /* free temporary memory */
234  SCIPfreeBufferArray(scip, &varpointers);
235  SCIPfreeBufferArray(scip, &vars);
236  SCIPfreeBufferArray(scip, &cliquecount);
237 
238  return SCIP_OKAY;
239 }
240 
241 /** apply clique fixing using probing */
242 static
244  SCIP* scip, /**< original SCIP data structure */
245  SCIP_HEURDATA* heurdata, /**< structure containing heurdata */
246  SCIP_VAR** binvars, /**< binary variables order w.r.t. to clique partition */
247  int nbinvars, /**< number of binary variables */
248  int* cliquepartition, /**< clique partition of all binary variables */
249  int ncliques, /**< number of cliques */
250  SCIP_VAR** onefixvars, /**< array to store all variables which are stored to one */
251  int* nonefixvars, /**< pointer to store the number of variables fixed to one */
252  SCIP_SOL* sol, /**< working solution */
253  int* probingdepthofonefix,/**< pointer to store in which depth the last fixing to was applied */
254  SCIP_Bool* cutoff, /**< pointer to store whether the propagation stopped with infeasibility */
255  SCIP_RESULT* result /**< pointer to store the result (solution found) */
256  )
257 {
258 #if 0
259  SCIP_Bool success;
260 #endif
261  SCIP_Bool alreadyone;
262  SCIP_Bool allfixed;
263  int bestpos;
264  int v;
265  int c;
266 #ifdef SCIP_DEBUG
267  int nsolsround;
268  int nsolstried;
269 #endif
270 
271  assert(scip != NULL);
272  assert(heurdata != NULL);
273  assert(binvars != NULL);
274  assert(onefixvars != NULL);
275  assert(nonefixvars != NULL);
276  assert(sol != NULL);
277  assert(probingdepthofonefix != NULL);
278  assert(cutoff != NULL);
279  assert(result != NULL);
280 
281  *cutoff = FALSE;
282  *probingdepthofonefix = 0;
283 
284 #ifdef SCIP_DEBUG
285  nsolsround = 0;
286  nsolstried = 0;
287 #endif
288  v = 0;
289  /* @todo maybe try to fix more than one variable to one in each probing node, to gain faster results */
290  for( c = 0; c < ncliques; ++c )
291  {
292  alreadyone = FALSE;
293  allfixed = TRUE;
294  bestpos = nbinvars;
295 
296  /* find first unfixed variable in this clique */
297  while( v < nbinvars && cliquepartition[v] == c )
298  {
299  if( SCIPvarGetLbLocal(binvars[v]) > 0.5 )
300  alreadyone = TRUE;
301  else if( allfixed && SCIPvarGetUbLocal(binvars[v]) > 0.5 )
302  {
303  bestpos = v;
304  allfixed = FALSE;
305  }
306 
307  ++v;
308  }
309  if( v == nbinvars && allfixed )
310  break;
311 
312  /* if all clique variables are fixed, continue with the next clique */
313  if( allfixed )
314  continue;
315 
316  assert(bestpos < nbinvars);
317  assert(c == cliquepartition[bestpos]);
318 
319  SCIP_CALL( SCIPnewProbingNode(scip) );
320 
321  v = bestpos;
322  if( !alreadyone )
323  {
324  *probingdepthofonefix = SCIPgetProbingDepth(scip);
325  onefixvars[(*nonefixvars)] = binvars[v];
326  ++(*nonefixvars);
327 
328  /* fix best possible clique variable to 1 */
329  SCIP_CALL( SCIPfixVarProbing(scip, binvars[v], 1.0) );
330  SCIPdebugMessage("probing: fixing variable <%s> to 1\n", SCIPvarGetName(binvars[v]));
331 
332  ++v;
333  }
334 
335  /* fix rest of unfixed clique variables to 0 */
336  while( v < nbinvars && cliquepartition[v] == c )
337  {
338  if( SCIPvarGetUbLocal(binvars[v]) > 0.5 && SCIPvarGetLbLocal(binvars[v]) < 0.5 )
339  {
340  SCIP_CALL( SCIPfixVarProbing(scip, binvars[v], 0.0) );
341  SCIPdebugMessage("probing: fixing variable <%s> to 0\n", SCIPvarGetName(binvars[v]));
342  }
343  ++v;
344  }
345 
346  /* propagate fixings */
347  SCIP_CALL( SCIPpropagateProbing(scip, heurdata->maxproprounds, cutoff, NULL) );
348 
349  if( *cutoff )
350  break;
351 
352  /* @todo need to be check if it's ok to always try to round and check the solution in each probing step */
353 #if 0
354 
355 #ifdef SCIP_DEBUG
356  ++nsolsround;
357 #endif
358  /* create solution from probing run and try to round it */
359  SCIP_CALL( SCIPlinkCurrentSol(scip, sol) );
360  SCIP_CALL( SCIProundSol(scip, sol, &success) );
361 
362  if( success )
363  {
364  SCIPdebugMessage("clique heuristic found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, sol));
365 
366 #ifdef SCIP_DEBUG
367  ++nsolstried;
368 #endif
369  /* try to add solution to SCIP */
370  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &success) );
371 
372  /* check, if solution was feasible and good enough */
373  if( success )
374  {
375  SCIPdebugMessage(" -> solution was feasible and good enough\n");
376  *result = SCIP_FOUNDSOL;
377  }
378  }
379 #endif
380 
381  if( SCIPisStopped(scip) )
382  return SCIP_OKAY;
383 
384 #if 0
385  /* if the rest of all variables are in cliques with one variable stop */
386  if( nbinvars - v == ncliques - c )
387  break;
388 #endif
389  }
390  assert((*probingdepthofonefix > 0 && *nonefixvars > 0) || (*probingdepthofonefix == 0 && *nonefixvars == 0));
391  assert(*cutoff || (nbinvars - v == ncliques - c) || (v == nbinvars && (c == ncliques || c == ncliques - 1)));
392 
393  SCIPdebugMessage("fixed %d of %d variables in probing\n", v, nbinvars);
394  SCIPdebugMessage("applied %d of %d cliques in probing\n", c, ncliques);
395  SCIPdebugMessage("probing was %sfeasible\n", (*cutoff) ? "in" : "");
396 #ifdef SCIP_DEBUG
397  SCIPdebugMessage("clique heuristic rounded %d solutions and tried %d of them\n", nsolsround, nsolstried);
398 #endif
399  return SCIP_OKAY;
400 }
401 
402 /** creates a new solution for the original problem by copying the solution of the subproblem */
403 static
405  SCIP* scip, /**< original SCIP data structure */
406  SCIP* subscip, /**< SCIP structure of the subproblem */
407  SCIP_VAR** subvars, /**< the variables of the subproblem */
408  SCIP_SOL* newsol, /**< working solution */
409  SCIP_SOL* subsol, /**< solution of the subproblem */
410  SCIP_Bool* success /**< used to store whether new solution was found or not */
411  )
412 {
413  SCIP_VAR** vars; /* the original problem's variables */
414  int nvars;
415  SCIP_Real* subsolvals; /* solution values of the subproblem */
416 
417  assert(scip != NULL);
418  assert(subscip != NULL);
419  assert(subvars != NULL);
420  assert(subsol != NULL);
421  assert(success != NULL);
422 
423  /* get variables' data */
424  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
425 
426  /* sub-SCIP may have more variables than the number of active (transformed) variables in the main SCIP
427  * since constraint copying may have required the copy of variables that are fixed in the main SCIP
428  */
429  assert(nvars <= SCIPgetNOrigVars(subscip));
430 
431  SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) );
432 
433  /* copy the solution */
434  SCIP_CALL( SCIPgetSolVals(subscip, subsol, nvars, subvars, subsolvals) );
435 
436  SCIP_CALL( SCIPsetSolVals(scip, newsol, nvars, vars, subsolvals) );
437 
438  /* try to add new solution to scip and free it immediately */
439  SCIP_CALL( SCIPtrySol(scip, newsol, FALSE, TRUE, TRUE, TRUE, success) );
440 
441  SCIPfreeBufferArray(scip, &subsolvals);
442 
443  return SCIP_OKAY;
444 }
445 
446 /*
447  * Callback methods of primal heuristic
448  */
449 
450 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
451 static
452 SCIP_DECL_HEURCOPY(heurCopyClique)
453 { /*lint --e{715}*/
454  assert(scip != NULL);
455  assert(heur != NULL);
456  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
457 
458  /* call inclusion method of primal heuristic */
460 
461  return SCIP_OKAY;
462 }
463 
464 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
465 static
466 SCIP_DECL_HEURFREE(heurFreeClique)
467 { /*lint --e{715}*/
468  SCIP_HEURDATA* heurdata;
469 
470  assert(heur != NULL);
471  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
472  assert(scip != NULL);
473 
474  /* free heuristic data */
475  heurdata = SCIPheurGetData(heur);
476  assert(heurdata != NULL);
477 
478  SCIPfreeMemory(scip, &heurdata);
479  SCIPheurSetData(heur, NULL);
480 
481  return SCIP_OKAY;
482 }
483 
484 
485 /** initialization method of primal heuristic (called after problem was transformed) */
486 static
487 SCIP_DECL_HEURINIT(heurInitClique)
488 { /*lint --e{715}*/
489  SCIP_HEURDATA* heurdata;
490 
491  assert(heur != NULL);
492  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
493  assert(scip != NULL);
494 
495  /* reset heuristic data */
496  heurdata = SCIPheurGetData(heur);
497  assert(heurdata != NULL);
498 
499  /* set the seed value to the initial random seed value */
500  heurdata->seed = (unsigned int) heurdata->initseed;
501 
502  heurdata->usednodes = 0;
503 
504  return SCIP_OKAY;
505 }
506 
507 
508 /** execution method of primal heuristic */
509 static
510 SCIP_DECL_HEUREXEC(heurExecClique)
511 { /*lint --e{715}*/
512  SCIP_HEURDATA* heurdata;
513  SCIP_VAR** vars;
514  int nvars;
515  SCIP_VAR** binvars;
516  int nbinvars;
517  int* cliquepartition;
518  int ncliques;
519 #if 0
520  SCIP_Longint tmpnnodes;
521 #endif
522  SCIP_Bool cutoff;
523  SCIP_Bool backtrackcutoff;
524  SCIP_Bool lperror;
525 
526  int probingdepthofonefix;
527  SCIP_VAR** onefixvars;
528  int nonefixvars;
529  SCIP_Bool enabledconflicts;
530  SCIP_LPSOLSTAT lpstatus;
531  SCIP_CONS* conflictcons;
532  SCIP_Bool shortconflict;
533  SCIP_Bool allfixsolfound;
534  SCIP_Bool backtracked;
535  char consname[SCIP_MAXSTRLEN];
536 
537  SCIP_Real timelimit; /* timelimit for the subproblem */
538  SCIP_Real memorylimit;
539  SCIP_Longint nstallnodes; /* number of stalling nodes for the subproblem */
540 
541  SCIP_SOL* sol;
542 
543  assert(heur != NULL);
544  assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
545  assert(scip != NULL);
546  assert(result != NULL);
547  /* assert(SCIPhasCurrentNodeLP(scip)); */
548 
549  *result = SCIP_DIDNOTRUN;
550 
551  /* get heuristic's data */
552  heurdata = SCIPheurGetData(heur);
553  assert(heurdata != NULL);
554 
555 #if 0
556  if( heurdata->nnodefornextrun != SCIPgetNNodes(scip) )
557  return SCIP_OKAY;
558 #endif
559  /* get all binary variables */
560  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) );
561 
562  if( nbinvars < 2 )
563  {
564  heurdata->nnodefornextrun = INT_MAX;
565  return SCIP_OKAY;
566  }
567 
568  /* check for necessary information to apply this heuristic */
569  if( SCIPgetNCliques(scip) == 0 && SCIPgetNImplications(scip) == 0 )
570  {
571  heurdata->nnodefornextrun = INT_MAX;
572  return SCIP_OKAY;
573  }
574 
575  /* calculate the maximal number of branching nodes until heuristic is aborted */
576  nstallnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip));
577 
578  /* reward variable bounds heuristic if it succeeded often */
579  nstallnodes = (SCIP_Longint)(nstallnodes * 3.0 * (SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur) + 1.0));
580  nstallnodes -= 100 * SCIPheurGetNCalls(heur); /* count the setup costs for the sub-MIP as 100 nodes */
581  nstallnodes += heurdata->nodesofs;
582 
583  /* determine the node limit for the current process */
584  nstallnodes -= heurdata->usednodes;
585  nstallnodes = MIN(nstallnodes, heurdata->maxnodes);
586 
587  /* check whether we have enough nodes left to call subproblem solving */
588  if( nstallnodes < heurdata->minnodes )
589  {
590  SCIPdebugMessage("skipping "HEUR_NAME": nstallnodes=%"SCIP_LONGINT_FORMAT", minnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, heurdata->minnodes);
591  return SCIP_OKAY;
592  }
593 
594  *result = SCIP_DIDNOTFIND;
595 
596  onefixvars = NULL;
597  sol = NULL;
598 
599  /* allocate memory */
600  SCIP_CALL( SCIPduplicateBufferArray(scip, &binvars, vars, nbinvars) );
601  SCIP_CALL( SCIPallocBufferArray(scip, &cliquepartition, nbinvars) );
602 
603 #if 1
604  /* @todo change sorting after some attempts to random variable order */
605  if( SCIPgetNNodes(scip) == 1 )
606  {
607  /* sort variables after increasing objective value */
608  SCIPsortPtr((void**)binvars, varObjSort, nbinvars);
609  }
610  else
611  {
612  SCIPpermuteArray((void**)binvars, 0, nbinvars, &(heurdata->seed));
613  }
614 #endif
615 
616  /* get clique partitions */
617  SCIP_CALL( SCIPcalcCliquePartition(scip, binvars, nbinvars, cliquepartition, &ncliques) );
618  /* @todo get negated clique partition and use this too, or maybe mix both */
619 
620  SCIPdebugMessage("found %d cliques\n", ncliques);
621 
622  /* disable conflict analysis, because we can it better than SCIP itself, cause we have more information */
623  SCIP_CALL( SCIPgetBoolParam(scip, "conflict/enable", &enabledconflicts) );
624 
625  if( !SCIPisParamFixed(scip, "conflict/enable") )
626  {
627  SCIP_CALL( SCIPsetBoolParam(scip, "conflict/enable", FALSE) );
628  }
629 
630  if( ncliques == nbinvars )
631  {
632  heurdata->nnodefornextrun = INT_MAX;
633  goto TERMINATE;
634  }
635 
636  /* sort the cliques together by respecting the current order (which is w.r.t. the objective coefficients */
637  SCIP_CALL( stableSortBinvars(scip, binvars, nbinvars, cliquepartition, ncliques) );
638 
639  if( !SCIPisLPConstructed(scip) && SCIPhasCurrentNodeLP(scip) )
640  {
641  SCIP_Bool nodecutoff;
642 
643  SCIP_CALL( SCIPconstructLP(scip, &nodecutoff) );
644  SCIP_CALL( SCIPflushLP(scip) );
645  if( nodecutoff )
646  goto TERMINATE;
647  }
648 
649  /* start probing */
650  SCIP_CALL( SCIPstartProbing(scip) );
651 
652  /* create a solution */
653  SCIP_CALL( SCIPcreateSol(scip, &sol, heur) );
654 
655  /* allocate memory for all variables which will be fixed to one during probing */
656  SCIP_CALL(SCIPallocBufferArray(scip, &onefixvars, ncliques) );
657  nonefixvars = 0;
658 
659  /* apply fixings due to clique information */
660  SCIP_CALL( applyCliqueFixings(scip, heurdata, binvars, nbinvars, cliquepartition, ncliques, onefixvars, &nonefixvars, sol, &probingdepthofonefix, &cutoff, result) );
661 
662  if( SCIPisStopped(scip) )
663  goto TERMINATE;
664 
665  backtrackcutoff = FALSE;
666  backtracked = FALSE;
667 
668  /* try to repair probing */
669  if( cutoff && nonefixvars > 0)
670  {
671  assert(probingdepthofonefix > 0);
672 
673  SCIP_CALL( SCIPbacktrackProbing(scip, probingdepthofonefix - 1) );
674 
675  /* fix the last variable, which was fixed to 1 and led to the cutoff, to 0 */
676  SCIP_CALL( SCIPfixVarProbing(scip, onefixvars[nonefixvars - 1], 0.0) );
677 
678  backtracked = TRUE;
679 
680  /* propagate fixings */
681  SCIP_CALL( SCIPpropagateProbing(scip, heurdata->maxproprounds, &backtrackcutoff, NULL) );
682  }
683 
684  /*************************** Probing LP Solving ***************************/
685 
686  lpstatus = SCIP_LPSOLSTAT_ERROR;
687  lperror = FALSE;
688  allfixsolfound = FALSE;
689  /* solve lp only if the problem is still feasible */
690  if( !backtrackcutoff && SCIPhasCurrentNodeLP(scip) )
691  {
692 #if 1
693  SCIPdebugMessage("starting solving clique-lp at time %g\n", SCIPgetSolvingTime(scip));
694 
695  /* solve LP; errors in the LP solver should not kill the overall solving process, if the LP is just needed for a
696  * heuristic. hence in optimized mode, the return code is caught and a warning is printed, only in debug mode,
697  * SCIP will stop.
698  */
699 #ifdef NDEBUG
700  {
701  SCIP_Bool retstat;
702  retstat = SCIPsolveProbingLP(scip, -1, &lperror, NULL);
703  if( retstat != SCIP_OKAY )
704  {
705  SCIPwarningMessage(scip, "Error while solving LP in clique heuristic; LP solve terminated with code <%d>\n",
706  retstat);
707  }
708  }
709 #else
710  SCIP_CALL( SCIPsolveProbingLP(scip, -1, &lperror, NULL) );
711 #endif
712  SCIPdebugMessage("ending solving clique-lp at time %g\n", SCIPgetSolvingTime(scip));
713 
714  lpstatus = SCIPgetLPSolstat(scip);
715 
716  SCIPdebugMessage(" -> new LP iterations: %"SCIP_LONGINT_FORMAT"\n", SCIPgetNLPIterations(scip));
717  SCIPdebugMessage(" -> error=%u, status=%d\n", lperror, lpstatus);
718  }
719 
720  /* check if this is a feasible solution */
721  if( lpstatus == SCIP_LPSOLSTAT_OPTIMAL && !lperror )
722  {
723  SCIP_Bool stored;
724  SCIP_Bool success;
725 
726  /* copy the current LP solution to the working solution */
727  SCIP_CALL( SCIPlinkLPSol(scip, sol) );
728 
729  SCIP_CALL( SCIProundSol(scip, sol, &success) );
730 
731  if( success )
732  {
733  SCIPdebugMessage("clique heuristic found roundable primal solution: obj=%g\n",
734  SCIPgetSolOrigObj(scip, sol));
735 
736  /* check solution for feasibility, and add it to solution store if possible.
737  * Neither integrality nor feasibility of LP rows have to be checked, because they
738  * are guaranteed by the heuristic at this stage.
739  */
740 #ifdef SCIP_DEBUG
741  SCIP_CALL( SCIPtrySol(scip, sol, TRUE, TRUE, TRUE, TRUE, &stored) );
742 #else
743  SCIP_CALL( SCIPtrySol(scip, sol, FALSE, TRUE, FALSE, FALSE, &stored) );
744 #endif
745  if( stored )
746  {
747  SCIPdebugMessage("found feasible solution:\n");
748  SCIPdebug( SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) ) );
749  *result = SCIP_FOUNDSOL;
750  allfixsolfound = TRUE;
751  }
752  }
753  }
754 #endif
755 
756  /*************************** END Probing LP Solving ***************************/
757  /*************************** Create Conflict ***************************/
758 
759  if( lpstatus == SCIP_LPSOLSTAT_INFEASIBLE || lpstatus == SCIP_LPSOLSTAT_OBJLIMIT || backtrackcutoff )
760  {
761  /* in case the last fixing in both direction led to infeasibility or to a reached objlimit than our conflict will
762  * only include all variable before that last fixing
763  */
764  shortconflict = cutoff && (nonefixvars > 0);
765 
766  /* create own conflict */
767  (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "conf%"SCIP_LONGINT_FORMAT"", SCIPgetNNodes(scip));
768 
769  /* get negated variables for our conflict */
770  SCIP_CALL( SCIPgetNegatedVars(scip, nonefixvars, onefixvars, onefixvars) );
771 
772  /* create conflict constraint */
773  SCIP_CALL( SCIPcreateConsLogicor(scip, &conflictcons, consname, (shortconflict ? nonefixvars - 1 : nonefixvars), onefixvars,
775  SCIP_CALL( SCIPaddConsNode(scip, SCIPgetCurrentNode(scip), conflictcons, NULL) );
776  SCIPdebugPrintCons(scip, conflictcons, NULL);
777  SCIP_CALL( SCIPreleaseCons(scip, &conflictcons) );
778  }
779 
780  /*************************** End Conflict ***************************/
781 
782  /*************************** Start Subscip Solving ***************************/
783 
784  /* if no solution has been found yet and the subproblem is still feasible --> fix all other variables by subscip if
785  * necessary
786  */
787  if( !allfixsolfound && lpstatus != SCIP_LPSOLSTAT_INFEASIBLE && lpstatus != SCIP_LPSOLSTAT_OBJLIMIT && !backtrackcutoff )
788  {
789  SCIP* subscip;
790  SCIP_VAR** subvars;
791  SCIP_HASHMAP* varmap;
792  SCIP_Bool valid;
793  int i;
794 
795  valid = FALSE;
796 
797  /* get all variables again because SCIPconstructLP() might have changed the variables array */
798  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
799 
800  /* create subproblem */
801  SCIP_CALL( SCIPcreate(&subscip) );
802 
803  /* allocate temporary memory for subscip variables */
804  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
805 
806  /* create the variable mapping hash map */
807  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) );
808 
809  SCIP_CALL( SCIPcopy(scip, subscip, varmap, NULL, "_clique", FALSE, FALSE, TRUE, &valid) );
810 
811  if( heurdata->copycuts )
812  {
813  /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */
814  SCIP_CALL( SCIPcopyCuts(scip, subscip, varmap, NULL, FALSE, NULL) );
815  }
816 
817  for( i = 0; i < nvars; i++ )
818  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmap, vars[i]);
819 
820  /* free hash map */
821  SCIPhashmapFree(&varmap);
822 
823  /* do not abort subproblem on CTRL-C */
824  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
825 
826  /* disable output to console */
827  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
828 
829  /* check whether there is enough time and memory left */
830  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
831  if( !SCIPisInfinity(scip, timelimit) )
832  timelimit -= SCIPgetSolvingTime(scip);
833  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
834 
835  /* substract the memory already used by the main SCIP and the estimated memory usage of external software */
836  if( !SCIPisInfinity(scip, memorylimit) )
837  {
838  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
839  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
840  }
841 
842  /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */
843  if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 )
844  {
845  /* free subproblem */
846  SCIPfreeBufferArray(scip, &subvars);
847  SCIP_CALL( SCIPfree(&subscip) );
848 
849  goto TERMINATE;
850  }
851 
852  /* set limits for the subproblem */
853  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", nstallnodes) );
854  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", heurdata->maxnodes) );
855  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
856  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
857 
858  /* forbid call of heuristics and separators solving sub-CIPs */
859  SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) );
860 
861  /* disable cutting plane separation */
863 
864  /* disable expensive presolving */
866 
867  /* employ a limit on the number of enforcement rounds in the quadratic constraint handler; this fixes the issue that
868  * sometimes the quadratic constraint handler needs hundreds or thousands of enforcement rounds to determine the
869  * feasibility status of a single node without fractional branching candidates by separation (namely for uflquad
870  * instances); however, the solution status of the sub-SCIP might get corrupted by this; hence no deductions shall be
871  * made for the original SCIP
872  */
873  if( SCIPfindConshdlr(subscip, "quadratic") != NULL && !SCIPisParamFixed(subscip, "constraints/quadratic/enfolplimit") )
874  {
875  SCIP_CALL( SCIPsetIntParam(subscip, "constraints/quadratic/enfolplimit", 10) );
876  }
877 
878 #ifdef SCIP_DEBUG
879  /* for debugging clique heuristic, enable MIP output */
880  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) );
881  SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 100000000) );
882 #endif
883 
884  /* if there is already a solution, add an objective cutoff */
885  if( SCIPgetNSols(scip) > 0 )
886  {
887  SCIP_Real upperbound;
888  SCIP_Real minimprove;
889  SCIP_Real cutoffbound;
890 
891  minimprove = heurdata->minimprove;
892  cutoffbound = SCIPinfinity(scip);
893  assert( !SCIPisInfinity(scip,SCIPgetUpperbound(scip)) );
894 
895  upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip);
896 
897  if( !SCIPisInfinity(scip, -1.0 * SCIPgetLowerbound(scip)) )
898  {
899  cutoffbound = (1-minimprove) * SCIPgetUpperbound(scip) + minimprove * SCIPgetLowerbound(scip);
900  }
901  else
902  {
903  if( SCIPgetUpperbound ( scip ) >= 0 )
904  cutoffbound = (1 - minimprove) * SCIPgetUpperbound(scip);
905  else
906  cutoffbound = (1 + minimprove) * SCIPgetUpperbound(scip);
907  }
908  cutoffbound = MIN(upperbound, cutoffbound);
909  SCIP_CALL( SCIPsetObjlimit(subscip, cutoffbound) );
910  SCIPdebugMessage("setting objlimit for subscip to %g\n", cutoffbound);
911  }
912 
913  SCIPdebugMessage("starting solving clique-submip at time %g\n", SCIPgetSolvingTime(scip));
914 
915  /* solve the subproblem */
916  /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic.
917  * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop.
918  */
919 #ifdef NDEBUG
920  {
921  SCIP_RETCODE retstat;
922  retstat = SCIPpresolve(subscip);
923  if( retstat != SCIP_OKAY )
924  {
925  SCIPwarningMessage(scip, "Error while presolving subMIP in clique heuristic; sub-SCIP terminated with code <%d>\n", retstat);
926  }
927  }
928 #else
929  SCIP_CALL( SCIPpresolve(subscip) );
930 #endif
931 
932  SCIPdebugMessage("clique heuristic presolved subproblem: %d vars, %d cons; fixing value = %g\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip), ((nvars - SCIPgetNVars(subscip)) / (SCIP_Real)nvars));
933 
934  /* after presolving, we should have at least reached a certain fixing rate over ALL variables (including continuous)
935  * to ensure that not only the MIP but also the LP relaxation is easy enough
936  */
937  if( ((nvars - SCIPgetNVars(subscip)) / (SCIP_Real)nvars) >= (heurdata->minfixingrate / 2.0) )
938  {
939  SCIP_SOL** subsols;
940  SCIP_Bool success;
941  int nsubsols;
942 
943  SCIPdebugMessage("solving subproblem: nstallnodes=%"SCIP_LONGINT_FORMAT", maxnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, heurdata->maxnodes);
944 
945 #ifdef NDEBUG
946  {
947  SCIP_RETCODE retstat;
948  retstat = SCIPsolve(subscip);
949  if( retstat != SCIP_OKAY )
950  {
951  SCIPwarningMessage(scip, "Error while solving subMIP in clique heuristic; sub-SCIP terminated with code <%d>\n",retstat);
952  }
953  }
954 #else
955  SCIP_CALL( SCIPsolve(subscip) );
956 #endif
957  SCIPdebugMessage("ending solving clique-submip at time %g, status = %d\n", SCIPgetSolvingTime(scip), SCIPgetStatus(subscip));
958 
959  /* check, whether a solution was found; due to numerics, it might happen that not all solutions are feasible ->
960  * try all solutions until one was accepted
961  */
962  nsubsols = SCIPgetNSols(subscip);
963  subsols = SCIPgetSols(subscip);
964  success = FALSE;
965 
966  for( i = 0; i < nsubsols && !success; ++i )
967  {
968  SCIP_CALL( createNewSol(scip, subscip, subvars, sol, subsols[i], &success) );
969  }
970  if( success )
971  *result = SCIP_FOUNDSOL;
972 
973  /* if subscip was infeasible we can add a conflict too */
974  if( SCIPgetStatus(subscip) == SCIP_STATUS_INFEASIBLE )
975  {
976  /* in case the last fixing in both direction led to infeasibility or to a reached objlimit than our conflict will only include all variable before that last fixing */
977  shortconflict = backtracked;
978 
979  /* create own conflict */
980  (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "conf%"SCIP_LONGINT_FORMAT"", SCIPgetNNodes(scip));
981 
982  /* get negated variables for our conflict */
983  SCIP_CALL( SCIPgetNegatedVars(scip, nonefixvars, onefixvars, onefixvars) );
984 
985  /* create conflict constraint */
986  SCIP_CALL( SCIPcreateConsLogicor(scip, &conflictcons, consname, (shortconflict ? nonefixvars - 1 : nonefixvars), onefixvars,
988  SCIP_CALL( SCIPaddConsNode(scip, SCIPgetCurrentNode(scip), conflictcons, NULL) );
989  SCIPdebugPrintCons(scip, conflictcons, NULL);
990  SCIP_CALL( SCIPreleaseCons(scip, &conflictcons) );
991  }
992 
993  }
994 
995 #ifdef SCIP_DEBUG
996  SCIP_CALL( SCIPprintStatistics(subscip, NULL) );
997 #endif
998 
999  /* free subproblem */
1000  SCIPfreeBufferArray(scip, &subvars);
1001  SCIP_CALL( SCIPfree(&subscip) );
1002  }
1003 
1004  /*************************** End Subscip Solving ***************************/
1005 
1006  TERMINATE:
1007 
1008  /* reset the conflict analysis */
1009  if( !SCIPisParamFixed(scip, "conflict/enable") )
1010  {
1011  SCIP_CALL( SCIPsetBoolParam(scip, "conflict/enable", enabledconflicts) );
1012  }
1013 
1014  /* free conflict variables */
1015  if( onefixvars != NULL )
1016  SCIPfreeBufferArray(scip, &onefixvars);
1017 
1018  /* freeing solution */
1019  if( sol != NULL )
1020  {
1021  SCIP_CALL( SCIPfreeSol(scip, &sol) );
1022  }
1023 
1024  /* end probing */
1025  if( SCIPinProbing(scip) )
1026  {
1027  SCIP_CALL( SCIPendProbing(scip) );
1028  }
1029 
1030  SCIPfreeBufferArray(scip, &cliquepartition);
1031  SCIPfreeBufferArray(scip, &binvars);
1032 
1033 #if 0
1034  /* calculate next node number to run this heuristic */
1035  tmpnnodes = (SCIP_Longint) SCIPceil(scip, heurdata->nnodefornextrun * heurdata->multiplier);
1036  heurdata->nnodefornextrun = MIN(tmpnnodes, INT_MAX);
1037  SCIPdebugMessage("Next run will be at node %"SCIP_LONGINT_FORMAT".\n", heurdata->nnodefornextrun);
1038 #endif
1039  return SCIP_OKAY;
1040 }
1041 
1042 /*
1043  * primal heuristic specific interface methods
1044  */
1045 
1046 /** creates the clique primal heuristic and includes it in SCIP */
1048  SCIP* scip /**< SCIP data structure */
1049  )
1050 {
1051  SCIP_HEURDATA* heurdata;
1052  SCIP_HEUR* heur;
1053 
1054  /* create clique primal heuristic data */
1055  SCIP_CALL( SCIPallocMemory(scip, &heurdata) );
1056 
1057  /* include primal heuristic */
1058  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
1060  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecClique, heurdata) );
1061 
1062  assert(heur != NULL);
1063 
1064  /* set non-NULL pointers to callback methods */
1065  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyClique) );
1066  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeClique) );
1067  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitClique) );
1068 
1069  /* add clique primal heuristic parameters */
1070 
1071  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/multiplier",
1072  "value to increase nodenumber to determine the next run",
1073  &heurdata->multiplier, TRUE, DEFAULT_MULTIPLIER, 0.0, SCIP_REAL_MAX, NULL, NULL) );
1074 
1075  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/initseed",
1076  "initial random seed value to permutate variables",
1077  &(heurdata->initseed), TRUE, DEFAULT_INITSEED, 0, INT_MAX, NULL, NULL) );
1078 
1079  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minfixingrate",
1080  "minimum percentage of integer variables that have to be fixable",
1081  &heurdata->minfixingrate, FALSE, DEFAULT_MINFIXINGRATE, 0.0, 1.0, NULL, NULL) );
1082 
1083  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/maxnodes",
1084  "maximum number of nodes to regard in the subproblem",
1085  &heurdata->maxnodes, TRUE, DEFAULT_MAXNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1086 
1087  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/nodesofs",
1088  "number of nodes added to the contingent of the total nodes",
1089  &heurdata->nodesofs, FALSE, DEFAULT_NODESOFS, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1090 
1091  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/"HEUR_NAME"/minnodes",
1092  "minimum number of nodes required to start the subproblem",
1093  &heurdata->minnodes, TRUE, DEFAULT_MINNODES, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
1094 
1095  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/nodesquot",
1096  "contingent of sub problem nodes in relation to the number of nodes of the original problem",
1097  &heurdata->nodesquot, FALSE, DEFAULT_NODESQUOT, 0.0, 1.0, NULL, NULL) );
1098 
1099  SCIP_CALL( SCIPaddRealParam(scip, "heuristics/"HEUR_NAME"/minimprove",
1100  "factor by which "HEUR_NAME" heuristic should at least improve the incumbent",
1101  &heurdata->minimprove, TRUE, DEFAULT_MINIMPROVE, 0.0, 1.0, NULL, NULL) );
1102 
1103  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/"HEUR_NAME"/maxproprounds",
1104  "maximum number of propagation rounds during probing (-1 infinity)",
1105  &heurdata->maxproprounds, TRUE, DEFAULT_MAXPROPROUNDS, -1, INT_MAX/4, NULL, NULL) );
1106 
1107  SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/"HEUR_NAME"/copycuts",
1108  "should all active cuts from cutpool be copied to constraints in subproblem?",
1109  &heurdata->copycuts, TRUE, DEFAULT_COPYCUTS, NULL, NULL) );
1110 
1111  return SCIP_OKAY;
1112 }
1113