SCIP-SDP  4.0.0
heur_sdpfracround.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 
38 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
39 
40 #include <assert.h>
41 #include <string.h>
42 
43 #include "heur_sdpfracround.h"
44 #include "relax_sdp.h"
45 
46 /* turn off lint warnings for whole file: */
47 /*lint --e{788,818}*/
48 
49 #define HEUR_NAME "sdpfracround"
50 #define HEUR_DESC "fractional rounding heuristic for SDPs"
51 #define HEUR_DISPCHAR '^'
52 #define HEUR_PRIORITY -1000500
53 #define HEUR_FREQ 1
54 #define HEUR_FREQOFS 0
55 #define HEUR_MAXDEPTH -1
56 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
57 #define HEUR_USESSUBSCIP FALSE /* does the heuristic use a secondary SCIP instance? */
58 
59 
60 /*
61  * Default parameter settings
62  */
63 
64 #define DEFAULT_RUNFORLP FALSE
66 /* locally defined heuristic data */
67 struct SCIP_HeurData
68 {
69  SCIP_SOL* sol;
70  SCIP_Bool runforlp;
71 };
72 
73 
74 /*
75  * Callback methods
76  */
77 
79 static
80 SCIP_DECL_HEURCOPY(heurCopySdpfracround)
81 { /*lint --e{715}*/
82  assert( scip != NULL );
83  assert( heur != NULL );
84  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
85 
86  /* call inclusion method of primal heuristic */
87  SCIP_CALL( SCIPincludeHeurSdpFracround(scip) );
88 
89  return SCIP_OKAY;
90 }
91 
93 static
94 SCIP_DECL_HEURFREE(heurFreeSdpfracround)
95 { /*lint --e{715}*/
96  SCIP_HEURDATA* heurdata;
97 
98  assert( heur != NULL );
99  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
100  assert( scip != NULL );
101 
102  /* free heuristic data */
103  heurdata = SCIPheurGetData(heur);
104  assert(heurdata != NULL);
105 
106  SCIPfreeBlockMemory(scip, &heurdata);
107  SCIPheurSetData(heur, NULL);
108 
109  return SCIP_OKAY;
110 }
111 
113 static
114 SCIP_DECL_HEURINIT(heurInitSdpfracround)
115 { /*lint --e{715}*/
116  SCIP_HEURDATA* heurdata;
117 
118  assert( heur != NULL );
119  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
120 
121  /* get heuristic data */
122  heurdata = SCIPheurGetData(heur);
123  assert( heurdata != NULL );
124 
125  /* create working solution */
126  SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
127 
128  return SCIP_OKAY;
129 }
130 
132 static
133 SCIP_DECL_HEUREXIT(heurExitSdpfracround)
134 { /*lint --e{715}*/
135  SCIP_HEURDATA* heurdata;
136 
137  assert( heur != NULL );
138  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
139 
140  /* get heuristic data */
141  heurdata = SCIPheurGetData(heur);
142  assert( heurdata != NULL );
143 
144  /* free working solution */
145  SCIP_CALL( SCIPfreeSol(scip, &heurdata->sol) );
146 
147  return SCIP_OKAY;
148 }
149 
151 static
152 SCIP_DECL_HEUREXEC(heurExecSdpfracround)
153 { /*lint --e{715}*/
154  SCIP_HEURDATA* heurdata;
155  SCIP_CONSHDLR* conshdlrsdp;
156  SCIP_RELAX* relaxsdp;
157  SCIP_Real* sdpcandssol;
158  SCIP_Real* sdpcandsfrac;
159  SCIP_VAR** vars;
160  SCIP_VAR** sdpcands;
161  SCIP_SOL* relaxsol = NULL;
162  SCIP_Bool usesdp = TRUE;
163  SCIP_Bool cutoff = FALSE;
164  int nsdpcands = 0;
165  int ncontvars;
166  int freq = -1;
167  int nfixed = 0;
168  int nrounded = 0;
169  int nvars;
170  int v;
171 
172  assert( heur != NULL );
173  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
174  assert( scip != NULL );
175  assert( result != NULL );
176 
177  *result = SCIP_DELAYED;
178 
179  /* do not call heuristic if node was already detected to be infeasible */
180  if ( nodeinfeasible )
181  return SCIP_OKAY;
182 
183  *result = SCIP_DIDNOTRUN;
184 
185  /* get heuristic data */
186  heurdata = SCIPheurGetData(heur);
187  assert( heurdata != NULL );
188 
189  /* do not run if relaxation solution is not available and we do not want to run for LPs or no LP solution is available */
190  if ( ! SCIPisRelaxSolValid(scip) )
191  {
192  /* exit if we do not want to run for LPs */
193  if ( ! heurdata->runforlp )
194  return SCIP_OKAY;
195 
196  /* exit if LP is not solved */
197  if ( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL )
198  return SCIP_OKAY;
199 
200  /* avoid solving for sub-SCIPs */
201  if ( SCIPgetSubscipDepth(scip) > 0 )
202  return SCIP_OKAY;
203 
204  usesdp = FALSE;
205  }
206 
207  /* get relaxator - exit if not found */
208  relaxsdp = SCIPfindRelax(scip, "SDP");
209  if ( relaxsdp == NULL )
210  return SCIP_OKAY;
211 
212  conshdlrsdp = SCIPfindConshdlr(scip, "SDP");
213  if ( conshdlrsdp == NULL )
214  return SCIP_OKAY;
215 
216  /* exit if there are no SDP constraints */
217  if ( SCIPconshdlrGetNConss(conshdlrsdp) <= 0 )
218  return SCIP_OKAY;
219 
220  /* get number of continuous variables */
221  ncontvars = SCIPgetNContVars(scip) + SCIPgetNImplVars(scip);
222 
223  /* decide which solution to use */
224  if ( usesdp )
225  {
226  SCIP_CALL( SCIPcreateRelaxSol(scip, &relaxsol, heur) );
227  }
228 
229  /* prepare probing mode */
230  SCIP_CALL( SCIPstartProbing(scip) );
231 
232  /* get SDP/LP solution */
233  vars = SCIPgetVars(scip);
234  nvars = SCIPgetNVars(scip);
235  SCIP_CALL( SCIPallocBufferArray(scip, &sdpcands, nvars) );
236  SCIP_CALL( SCIPallocBufferArray(scip, &sdpcandssol, nvars) );
237  SCIP_CALL( SCIPallocBufferArray(scip, &sdpcandsfrac, nvars) );
238 
239  /* prepare solution to be changed */
240  SCIP_CALL( SCIPlinkRelaxSol(scip, heurdata->sol) );
241 
242  /* collect fractional unfixed values */
243  for (v = 0; v < nvars; ++v)
244  {
245  SCIP_Real val;
246  SCIP_VAR* var;
247 
248  var = vars[v];
249  if ( SCIPvarIsIntegral(var) )
250  {
251  val = SCIPgetSolVal(scip, relaxsol, var);
252  if ( ! SCIPisFeasIntegral(scip, val) )
253  {
254  sdpcandssol[nsdpcands] = val;
255  sdpcandsfrac[nsdpcands] = SCIPfeasFrac(scip, val);
256  sdpcands[nsdpcands++] = var;
257  }
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, &sdpcandsfrac);
293  SCIPfreeBufferArray(scip, &sdpcandssol);
294  SCIPfreeBufferArray(scip, &sdpcands);
295 
296  return SCIP_OKAY;
297  }
298 
299  *result = SCIP_DIDNOTFIND;
300 
301  SCIPdebugMsg(scip, "Node %"SCIP_LONGINT_FORMAT": executing SDP fractional rounding heuristic (depth %d, %d fractionals).\n",
302  SCIPgetNNodes(scip), SCIPgetDepth(scip), nsdpcands);
303  SCIPdebugMsg(scip, "Fixed %d bounds of variables with integral values.\n", nfixed);
304 
305  if ( ! usesdp )
306  {
307  /* temporarily change relaxator frequency, since otherwise relaxation will not be solved */
308  freq = SCIPrelaxGetFreq(relaxsdp);
309  SCIP_CALL( SCIPsetIntParam(scip, "relaxing/SDP/freq", 1) );
310  }
311 
312  /* sort according to increasing fractinal parts */
313  SCIPsortRealRealPtr(sdpcandsfrac, sdpcandssol, (void*) sdpcands, nsdpcands);
314 
315  /* perform rounding loop */
316  nfixed = 0;
317  for (v = 0; v < nsdpcands && ! cutoff; ++v)
318  {
319  SCIP_Longint ndomreds;
320  SCIP_VAR* var;
321  SCIP_Real newval;
322  SCIP_Real val;
323  SCIP_Real ceilval;
324  SCIP_Real floorval;
325  SCIP_Real lb;
326  SCIP_Real ub;
327  SCIP_Bool lbadjust;
328  SCIP_Bool ubadjust;
329 
330  /* get next variable from permuted candidate array */
331  var = sdpcands[v];
332  val = sdpcandssol[v];
333  lb = SCIPvarGetLbLocal(var);
334  ub = SCIPvarGetUbLocal(var);
335 
336  assert( SCIPvarIsIntegral(var) );
337  assert( ! SCIPisFeasIntegral(scip, val) );
338 
339  ceilval = SCIPfeasCeil(scip, val);
340  floorval = SCIPfeasFloor(scip, val);
341 
342  /* Abort if rounded ceil and floor value lie outside the variable domain. Otherwise, check if bounds allow only
343  * one rounding direction, anyway */
344  if ( lb > ceilval + 0.5 || ub < floorval - 0.5 )
345  {
346  cutoff = TRUE;
347  break;
348  }
349  else if ( SCIPisFeasEQ(scip, lb, ceilval) )
350  newval = ceilval;
351  else if ( SCIPisFeasEQ(scip, ub, floorval) )
352  newval = floorval;
353  else
354  {
355  newval = SCIPfeasRound(scip, val);
356  ++nrounded;
357  }
358 
359  lbadjust = SCIPisGT(scip, newval, lb);
360  ubadjust = SCIPisLT(scip, newval, ub);
361 
362  if ( lbadjust || ubadjust )
363  {
364  SCIP_CALL( SCIPnewProbingNode(scip) );
365 
366  /* tighten the bounds to fix the variable for the probing node */
367  if ( lbadjust )
368  {
369  SCIP_CALL( SCIPchgVarLbProbing(scip, var, newval) );
370  }
371 
372  if ( ubadjust )
373  {
374  SCIP_CALL( SCIPchgVarUbProbing(scip, var, newval) );
375  }
376  ++nfixed;
377 
378  /* call propagation routines for the reduced problem */
379  SCIP_CALL( SCIPpropagateProbing(scip, 1, &cutoff, &ndomreds) );
380  }
381 
382  /* change solution value */
383  if ( ! cutoff )
384  {
385  SCIP_CALL( SCIPsetSolVal(scip, heurdata->sol, var, newval) );
386  }
387  }
388 
389  /* check solution */
390  if ( ! cutoff )
391  {
392  SCIP_Bool success;
393 
394  SCIPdebugMsg(scip, "Rounded %d variables.\n", nrounded);
395 
396  /* if there are no continuous variables, we can just try the solution */
397  if ( ncontvars == 0 )
398  {
399 #ifndef NDEBUG
400  /* assert that solution is really integral */
401  for (v = 0; v < nvars; ++v)
402  assert( ! SCIPvarIsIntegral(vars[v]) || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, heurdata->sol, vars[v])) );
403 #endif
404 
405  /* try to add solution to SCIP - do not need to check integrality here */
406  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, TRUE, &success) );
407 
408  if ( success )
409  {
410  SCIPdebugMsg(scip, "Found solution for full integral instance.\n");
411  *result = SCIP_FOUNDSOL;
412  }
413  else
414  SCIPdebugMsg(scip, "Solution not feasible for full integral instance.\n");
415  }
416  else if ( nfixed > 0 )
417  {
418  /* if there are continuous variables, we need to solve a final SDP */
419  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
420 
421  /* if solving was successfull */
422  if ( SCIPrelaxSdpSolvedProbing(relaxsdp) )
423  {
424  if ( SCIPrelaxSdpIsFeasible(relaxsdp) )
425  {
426  /* check solution */
427  SCIP_CALL( SCIPlinkRelaxSol(scip, heurdata->sol) );
428 
429  /* try to add solution to SCIP: check all constraints, including integrality */
430  SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, TRUE, TRUE, TRUE, TRUE, &success) );
431 
432  /* check, if solution was feasible and good enough */
433  if ( success )
434  {
435  SCIPdebugMsg(scip, "Solution was feasible and good enough.\n");
436  *result = SCIP_FOUNDSOL;
437  }
438  else
439  SCIPdebugMsg(scip, "Solution was not feasible.\n");
440  }
441  else
442  SCIPdebugMsg(scip, "Problem was infeasible.\n");
443  }
444  }
445  else
446  SCIPdebugMsg(scip, "No fixings have been performed.\n");
447  }
448  else
449  SCIPdebugMsg(scip, "Reached cutoff after %d roundings.\n", nrounded);
450 
451  /* free local problem */
452  SCIP_CALL( SCIPendProbing(scip) );
453 
454  /* reset frequency of relaxator */
455  if ( ! usesdp )
456  {
457  SCIP_CALL( SCIPsetIntParam(scip, "relaxing/SDP/freq", freq) );
458  }
459 
460  SCIPfreeBufferArray(scip, &sdpcandsfrac);
461  SCIPfreeBufferArray(scip, &sdpcandssol);
462  SCIPfreeBufferArray(scip, &sdpcands);
463 
464  SCIPdebugMsg(scip, "Finished fractional rounding heuristic.\n");
465 
466  return SCIP_OKAY;
467 }
468 
469 
470 /*
471  * heuristic specific interface methods
472  */
473 
476  SCIP* scip
477  )
478 {
479  SCIP_HEURDATA* heurdata;
480  SCIP_HEUR* heur;
481 
482  /* create fractional rounding primal heuristic data */
483  SCIP_CALL( SCIPallocBlockMemory(scip, &heurdata) );
484 
485  /* include primal heuristic */
486  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
488  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecSdpfracround, heurdata) );
489 
490  assert( heur != NULL );
491 
492  /* set non-NULL pointers to callback methods */
493  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopySdpfracround) );
494  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeSdpfracround) );
495  SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitSdpfracround) );
496  SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitSdpfracround) );
497 
498  /* fractional rounding heuristic parameters */
499  SCIP_CALL( SCIPaddBoolParam(scip,
500  "heuristics/sdpfracround/runforlp",
501  "Should fractional rounding be applied if we are solving LPs?",
502  &heurdata->runforlp, FALSE, DEFAULT_RUNFORLP, NULL, NULL) );
503 
504  return SCIP_OKAY;
505 }
static SCIP_DECL_HEUREXIT(heurExitSdpfracround)
#define HEUR_FREQOFS
#define HEUR_TIMING
#define HEUR_NAME
SDP-relaxator.
#define HEUR_FREQ
static SCIP_DECL_HEURCOPY(heurCopySdpfracround)
#define HEUR_USESSUBSCIP
fractional rounding heuristic for SDPs
#define HEUR_DISPCHAR
static SCIP_DECL_HEURINIT(heurInitSdpfracround)
#define HEUR_MAXDEPTH
SCIP_Bool SCIPrelaxSdpSolvedProbing(SCIP_RELAX *relax)
Definition: relax_sdp.c:5579
SCIP_Bool SCIPrelaxSdpIsFeasible(SCIP_RELAX *relax)
Definition: relax_sdp.c:5596
static SCIP_DECL_HEUREXEC(heurExecSdpfracround)
#define HEUR_DESC
SCIP_RETCODE SCIPincludeHeurSdpFracround(SCIP *scip)
static SCIP_DECL_HEURFREE(heurFreeSdpfracround)
#define HEUR_PRIORITY
#define DEFAULT_RUNFORLP