SCIP-SDP  4.0.0
heur_sdprand.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of SCIPSDP - a solving framework for mixed-integer */
4 /* semidefinite programs based on SCIP. */
5 /* */
6 /* Copyright (C) 2011-2013 Discrete Optimization, TU Darmstadt */
7 /* EDOM, FAU Erlangen-Nürnberg */
8 /* 2014-2021 Discrete Optimization, TU Darmstadt */
9 /* */
10 /* */
11 /* This program is free software; you can redistribute it and/or */
12 /* modify it under the terms of the GNU Lesser General Public License */
13 /* as published by the Free Software Foundation; either version 3 */
14 /* of the License, or (at your option) any later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
19 /* GNU Lesser General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with this program; if not, write to the Free Software */
23 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
24 /* */
25 /* */
26 /* Based on SCIP - Solving Constraint Integer Programs */
27 /* Copyright (C) 2002-2021 Zuse Institute Berlin */
28 /* SCIP is distributed under the terms of the SCIP Academic Licence, */
29 /* see file COPYING in the SCIP distribution. */
30 /* */
31 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
32 
39 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
40 
41 #include <assert.h>
42 #include <string.h>
43 
44 #include "heur_sdprand.h"
45 #include "relax_sdp.h"
46 
47 /* turn off lint warnings for whole file: */
48 /*lint --e{788,818}*/
49 
50 #define HEUR_NAME "sdprand"
51 #define HEUR_DESC "randomized rounding heuristic for SDPs"
52 #define HEUR_DISPCHAR '~'
53 #define HEUR_PRIORITY -1001000
54 #define HEUR_FREQ 1
55 #define HEUR_FREQOFS 0
56 #define HEUR_MAXDEPTH -1
57 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
58 #define HEUR_USESSUBSCIP FALSE /* does the heuristic use a secondary SCIP instance? */
59 
60 
61 /*
62  * Default parameter settings
63  */
64 
65 #define DEFAULT_RANDSEED 211
66 #define DEFAULT_RUNFORLP FALSE
68 /* locally defined heuristic data */
69 struct SCIP_HeurData
70 {
71  SCIP_SOL* sol;
72  SCIP_RANDNUMGEN* randnumgen;
73  SCIP_Bool runforlp;
74 };
75 
76 
77 /*
78  * Callback methods
79  */
80 
82 static
83 SCIP_DECL_HEURCOPY(heurCopySdprand)
84 { /*lint --e{715}*/
85  assert( scip != NULL );
86  assert( heur != NULL );
87  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
88 
89  /* call inclusion method of primal heuristic */
90  SCIP_CALL( SCIPincludeHeurSdpRand(scip) );
91 
92  return SCIP_OKAY;
93 }
94 
96 static
97 SCIP_DECL_HEURFREE(heurFreeSdprand)
98 { /*lint --e{715}*/
99  SCIP_HEURDATA* heurdata;
100 
101  assert( heur != NULL );
102  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
103  assert( scip != NULL );
104 
105  /* free heuristic data */
106  heurdata = SCIPheurGetData(heur);
107  assert(heurdata != NULL);
108 
109  SCIPfreeBlockMemory(scip, &heurdata);
110  SCIPheurSetData(heur, NULL);
111 
112  return SCIP_OKAY;
113 }
114 
116 static
117 SCIP_DECL_HEURINIT(heurInitSdprand)
118 { /*lint --e{715}*/
119  SCIP_HEURDATA* heurdata;
120 
121  assert( heur != NULL );
122  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
123 
124  /* get heuristic data */
125  heurdata = SCIPheurGetData(heur);
126  assert( heurdata != NULL );
127 
128  /* create working solution and random number generator */
129  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
130  SCIP_CALL( SCIPcreateRandom(scip, &(heurdata->randnumgen), SCIPinitializeRandomSeed(scip, DEFAULT_RANDSEED), TRUE) );
131 
132  return SCIP_OKAY;
133 }
134 
136 static
137 SCIP_DECL_HEUREXIT(heurExitSdprand)
138 { /*lint --e{715}*/
139  SCIP_HEURDATA* heurdata;
140 
141  assert( heur != NULL );
142  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
143 
144  /* get heuristic data */
145  heurdata = SCIPheurGetData(heur);
146  assert( heurdata != NULL );
147 
148  /* free working solution and random number generator */
149  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
150  SCIPfreeRandom(scip, &(heurdata->randnumgen));
151 
152  return SCIP_OKAY;
153 }
154 
156 static
157 SCIP_DECL_HEUREXEC(heurExecSdprand)
158 { /*lint --e{715}*/
159  SCIP_HEURDATA* heurdata;
160  SCIP_CONSHDLR* conshdlrsdp;
161  SCIP_RELAX* relaxsdp;
162  SCIP_Real* sdpcandssol;
163  SCIP_VAR** vars;
164  SCIP_SOL* relaxsol = NULL;
165  SCIP_Bool usesdp = TRUE;
166  SCIP_Bool cutoff = FALSE;
167  int* sdpcands;
168  int nsdpcands = 0;
169  int ncontvars;
170  int freq = -1;
171  int nfixed = 0;
172  int nrounded = 0;
173  int nvars;
174  int v;
175 
176  assert( heur != NULL );
177  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
178  assert( scip != NULL );
179  assert( result != NULL );
180 
181  *result = SCIP_DELAYED;
182 
183  /* do not call heuristic if node was already detected to be infeasible */
184  if ( nodeinfeasible )
185  return SCIP_OKAY;
186 
187  *result = SCIP_DIDNOTRUN;
188 
189  /* get heuristic data */
190  heurdata = SCIPheurGetData(heur);
191  assert( heurdata != NULL );
192 
193  /* do not run if relaxation solution is not available and we do not want to run for LPs or no LP solution is available */
194  if ( ! SCIPisRelaxSolValid(scip) )
195  {
196  /* exit if we do not want to run for LPs */
197  if ( ! heurdata->runforlp )
198  return SCIP_OKAY;
199 
200  /* exit if LP is not solved */
201  if ( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL )
202  return SCIP_OKAY;
203 
204  /* avoid solving for sub-SCIPs */
205  if ( SCIPgetSubscipDepth(scip) > 0 )
206  return SCIP_OKAY;
207 
208  usesdp = FALSE;
209  }
210 
211  /* get relaxator - exit if not found (use LP randomized rounding) */
212  relaxsdp = SCIPfindRelax(scip, "SDP");
213  if ( relaxsdp == NULL )
214  return SCIP_OKAY;
215 
216  conshdlrsdp = SCIPfindConshdlr(scip, "SDP");
217  if ( conshdlrsdp == NULL )
218  return SCIP_OKAY;
219 
220  /* exit if there are no SDP constraints */
221  if ( SCIPconshdlrGetNConss(conshdlrsdp) <= 0 )
222  return SCIP_OKAY;
223 
224  /* get number of continuous variables */
225  ncontvars = SCIPgetNContVars(scip) + SCIPgetNImplVars(scip);
226 
227  /* decide which solution to use */
228  if ( usesdp )
229  {
230  SCIP_CALL( SCIPcreateRelaxSol(scip, &relaxsol, heur) );
231  }
232 
233  /* prepare probing mode */
234  SCIP_CALL( SCIPstartProbing(scip) );
235 
236  /* get SDP/LP solution */
237  vars = SCIPgetVars(scip);
238  nvars = SCIPgetNVars(scip);
239  SCIP_CALL( SCIPallocBufferArray(scip, &sdpcands, nvars) );
240  SCIP_CALL( SCIPallocBufferArray(scip, &sdpcandssol, nvars) );
241 
242  /* prepare solution to be changed */
243  SCIP_CALL( SCIPlinkRelaxSol(scip, heurdata->sol) );
244 
245  /* collect fractional unfixed values */
246  for (v = 0; v < nvars; ++v)
247  {
248  SCIP_Real val;
249  SCIP_VAR* var;
250 
251  var = vars[v];
252  val = SCIPgetSolVal(scip, relaxsol, var);
253  sdpcandssol[v] = val;
254  if ( SCIPvarIsIntegral(var) )
255  {
256  if ( ! SCIPisFeasIntegral(scip, val) )
257  sdpcands[nsdpcands++] = v;
258  else
259  {
260  /* make sure value is really integral */
261  val = SCIPfeasRound(scip, val);
262 
263  /* fixing variable to integral value */
264  if ( SCIPisGT(scip, val, SCIPvarGetLbLocal(var)) )
265  {
266  SCIP_CALL( SCIPchgVarLbProbing(scip, var, val) );
267  ++nfixed;
268  }
269  if ( SCIPisLT(scip, val, SCIPvarGetUbLocal(var)) )
270  {
271  SCIP_CALL( SCIPchgVarUbProbing(scip, var, val) );
272  ++nfixed;
273  }
274 
275  /* to avoid numerical noise, make sure variable integral */
276  SCIP_CALL( SCIPsetSolVal(scip, heurdata->sol, var, val) );
277  }
278  }
279  }
280 
281  /* possibly free relaxation (LP or SDP) solution */
282  if ( relaxsol != NULL )
283  {
284  SCIP_CALL( SCIPfreeSol(scip, &relaxsol) );
285  }
286 
287  /* do not proceed, if there are no fractional variables */
288  if ( nsdpcands == 0 )
289  {
290  SCIP_CALL( SCIPendProbing(scip) );
291 
292  SCIPfreeBufferArray(scip, &sdpcandssol);
293  SCIPfreeBufferArray(scip, &sdpcands);
294 
295  return SCIP_OKAY;
296  }
297 
298  *result = SCIP_DIDNOTFIND;
299 
300  SCIPdebugMsg(scip, "Node %"SCIP_LONGINT_FORMAT": executing SDP randomized rounding heuristic (depth %d, %d fractionals).\n",
301  SCIPgetNNodes(scip), SCIPgetDepth(scip), nsdpcands);
302  SCIPdebugMsg(scip, "Fixed %d bounds of variables with integral values.\n", nfixed);
303 
304  if ( ! usesdp )
305  {
306  /* temporarily change relaxator frequency, since otherwise relaxation will not be solved */
307  freq = SCIPrelaxGetFreq(relaxsdp);
308  SCIP_CALL( SCIPsetIntParam(scip, "relaxing/SDP/freq", 1) );
309  }
310 
311  /* permute variables */
312  SCIPrandomPermuteIntArray(heurdata->randnumgen, sdpcands, 0, nsdpcands);
313 
314  /* perform rounding loop */
315  nfixed = 0;
316  for (v = 0; v < nsdpcands && ! cutoff; ++v)
317  {
318  SCIP_Longint ndomreds;
319  SCIP_VAR* var;
320  SCIP_Real newval;
321  SCIP_Real val;
322  SCIP_Real ceilval;
323  SCIP_Real floorval;
324  SCIP_Real lb;
325  SCIP_Real ub;
326  SCIP_Real r;
327  SCIP_Bool lbadjust;
328  SCIP_Bool ubadjust;
329 
330  /* get next variable from permuted candidate array */
331  assert( 0 <= sdpcands[v] && sdpcands[v] < nvars );
332  var = vars[sdpcands[v]];
333  val = sdpcandssol[sdpcands[v]];
334  lb = SCIPvarGetLbLocal(var);
335  ub = SCIPvarGetUbLocal(var);
336 
337  assert( SCIPvarIsIntegral(var) );
338  assert( ! SCIPisFeasIntegral(scip, val) );
339 
340  ceilval = SCIPfeasCeil(scip, val);
341  floorval = SCIPfeasFloor(scip, val);
342 
343  /* Abort if rounded ceil and floor value lie outside the variable domain. Otherwise, check if bounds allow only
344  * one rounding direction, anyway */
345  if ( lb > ceilval + 0.5 || ub < floorval - 0.5 )
346  {
347  cutoff = TRUE;
348  break;
349  }
350  else if ( SCIPisFeasEQ(scip, lb, ceilval) )
351  newval = ceilval;
352  else if ( SCIPisFeasEQ(scip, ub, floorval) )
353  newval = floorval;
354  else
355  {
356  /* if the variable is not fixed and its value is fractional */
357  r = SCIPrandomGetReal(heurdata->randnumgen, 0.0, 1.0);
358 
359  /* depending on random value, round variable up/down */
360  if ( SCIPfeasFrac(scip, val) <= r )
361  newval = floorval;
362  else
363  newval = ceilval;
364 
365  ++nrounded;
366  }
367 
368  lbadjust = SCIPisGT(scip, newval, lb);
369  ubadjust = SCIPisLT(scip, newval, ub);
370 
371  if ( lbadjust || ubadjust )
372  {
373  SCIP_CALL( SCIPnewProbingNode(scip) );
374 
375  /* tighten the bounds to fix the variable for the probing node */
376  if ( lbadjust )
377  {
378  SCIP_CALL( SCIPchgVarLbProbing(scip, var, newval) );
379  }
380 
381  if ( ubadjust )
382  {
383  SCIP_CALL( SCIPchgVarUbProbing(scip, var, newval) );
384  }
385  ++nfixed;
386 
387  /* call propagation routines for the reduced problem */
388  SCIP_CALL( SCIPpropagateProbing(scip, 1, &cutoff, &ndomreds) );
389  }
390 
391  /* change solution value */
392  if ( ! cutoff )
393  {
394  SCIP_CALL( SCIPsetSolVal(scip, heurdata->sol, var, newval) );
395  }
396  }
397 
398  /* check solution */
399  if ( ! cutoff )
400  {
401  SCIP_Bool success;
402 
403  SCIPdebugMsg(scip, "Rounded %d variables.\n", nrounded);
404 
405  /* if there are no continuous variables, we can just try the solution */
406  if ( ncontvars == 0 )
407  {
408 #ifndef NDEBUG
409  /* assert that solution is really integral */
410  for (v = 0; v < nvars; ++v)
411  assert( ! SCIPvarIsIntegral(vars[v]) || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, heurdata->sol, vars[v])) );
412 #endif
413 
414  /* try to add solution to SCIP - do not need to check integrality here */
415  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, TRUE, &success) );
416 
417  if ( success )
418  {
419  SCIPdebugMsg(scip, "Found solution for full integral instance.\n");
420  *result = SCIP_FOUNDSOL;
421  }
422  else
423  SCIPdebugMsg(scip, "Solution not feasible for full integral instance.\n");
424  }
425  else if ( nfixed > 0 )
426  {
427  /* if there are continuous variables, we need to solve a final SDP */
428  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
429 
430  /* if solving was successfull */
431  if ( SCIPrelaxSdpSolvedProbing(relaxsdp) )
432  {
433  if ( SCIPrelaxSdpIsFeasible(relaxsdp) )
434  {
435  /* check solution */
436  SCIP_CALL( SCIPlinkRelaxSol(scip, heurdata->sol) );
437 
438  /* try to add solution to SCIP: check all constraints, including integrality */
439  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, TRUE, TRUE, TRUE, TRUE, &success) );
440 
441  /* check, if solution was feasible and good enough */
442  if ( success )
443  {
444  SCIPdebugMsg(scip, "Solution was feasible and good enough.\n");
445  *result = SCIP_FOUNDSOL;
446  }
447  else
448  SCIPdebugMsg(scip, "Solution was not feasible.\n");
449  }
450  else
451  SCIPdebugMsg(scip, "Problem was infeasible.\n");
452  }
453  }
454  else
455  SCIPdebugMsg(scip, "No fixings have been performed.\n");
456  }
457  else
458  SCIPdebugMsg(scip, "Reached cutoff after %d roundings.\n", nrounded);
459 
460  /* free local problem */
461  SCIP_CALL( SCIPendProbing(scip) );
462 
463  /* reset frequency of relaxator */
464  if ( ! usesdp )
465  {
466  SCIP_CALL( SCIPsetIntParam(scip, "relaxing/SDP/freq", freq) );
467  }
468 
469  SCIPfreeBufferArray(scip, &sdpcandssol);
470  SCIPfreeBufferArray(scip, &sdpcands);
471 
472  SCIPdebugMsg(scip, "Finished randomized rounding heuristic.\n");
473 
474  return SCIP_OKAY;
475 }
476 
477 
478 /*
479  * heuristic specific interface methods
480  */
481 
484  SCIP* scip
485  )
486 {
487  SCIP_HEURDATA* heurdata;
488  SCIP_HEUR* heur;
489 
490  /* create randomized rounding primal heuristic data */
491  SCIP_CALL( SCIPallocBlockMemory(scip, &heurdata) );
492 
493  /* include primal heuristic */
494  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
496  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecSdprand, heurdata) );
497 
498  assert( heur != NULL );
499 
500  /* set non-NULL pointers to callback methods */
501  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopySdprand) );
502  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeSdprand) );
503  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitSdprand) );
504  SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitSdprand) );
505 
506  /* randomized rounding heuristic parameters */
507  SCIP_CALL( SCIPaddBoolParam(scip,
508  "heuristics/sdprand/runforlp",
509  "Should randomized rounding be applied if we are solving LPs?",
510  &heurdata->runforlp, FALSE, DEFAULT_RUNFORLP, NULL, NULL) );
511 
512  return SCIP_OKAY;
513 }
#define HEUR_DESC
Definition: heur_sdprand.c:51
static SCIP_DECL_HEUREXEC(heurExecSdprand)
Definition: heur_sdprand.c:157
#define HEUR_MAXDEPTH
Definition: heur_sdprand.c:56
SDP-relaxator.
#define DEFAULT_RUNFORLP
Definition: heur_sdprand.c:66
static SCIP_DECL_HEURFREE(heurFreeSdprand)
Definition: heur_sdprand.c:97
#define HEUR_PRIORITY
Definition: heur_sdprand.c:53
SCIP_RETCODE SCIPincludeHeurSdpRand(SCIP *scip)
Definition: heur_sdprand.c:483
#define DEFAULT_RANDSEED
Definition: heur_sdprand.c:65
#define HEUR_NAME
Definition: heur_sdprand.c:50
SCIP_Bool SCIPrelaxSdpSolvedProbing(SCIP_RELAX *relax)
Definition: relax_sdp.c:5579
#define HEUR_DISPCHAR
Definition: heur_sdprand.c:52
static SCIP_DECL_HEURINIT(heurInitSdprand)
Definition: heur_sdprand.c:117
#define HEUR_FREQOFS
Definition: heur_sdprand.c:55
randomized rounding heuristic for SDPs
static SCIP_DECL_HEURCOPY(heurCopySdprand)
Definition: heur_sdprand.c:83
#define HEUR_FREQ
Definition: heur_sdprand.c:54
static SCIP_DECL_HEUREXIT(heurExitSdprand)
Definition: heur_sdprand.c:137
SCIP_Bool SCIPrelaxSdpIsFeasible(SCIP_RELAX *relax)
Definition: relax_sdp.c:5596
#define HEUR_USESSUBSCIP
Definition: heur_sdprand.c:58
#define HEUR_TIMING
Definition: heur_sdprand.c:57