SCIP-SDP  4.0.0
heur_sdpinnerlp.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 
57 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
58 
59 #include <assert.h>
60 #include <string.h>
61 
62 #include "heur_sdpinnerlp.h"
63 #include "cons_sdp.h"
64 #include "scip/cons_linear.h"
65 
66 #define HEUR_NAME "sdpinnerlp"
67 #define HEUR_DESC "inner approximation LP heuristic for SDPs"
68 #define HEUR_DISPCHAR '!'
69 #define HEUR_PRIORITY -1001000
70 #define HEUR_FREQ -1
71 #define HEUR_FREQOFS 0
72 #define HEUR_MAXDEPTH -1
73 #define HEUR_TIMING SCIP_HEURTIMING_BEFOREPRESOL
74 #define HEUR_USESSUBSCIP TRUE /* does the heuristic use a secondary SCIP instance? */
75 
76 
77 /*
78  * Default parameter settings
79  */
80 
81 #define DEFAULT_STALLNODELIMIT 100L
82 #define DEFAULT_MAXSIZE 10000
85 /* locally defined heuristic data */
86 struct SCIP_HeurData
87 {
88  SCIP_Longint stallnodelimit;
89  int maxsize;
90 };
91 
92 
93 /*
94  * Callback methods
95  */
96 
98 static
99 SCIP_DECL_HEURCOPY(heurCopySdpInnerlp)
100 { /*lint --e{715}*/
101  assert( scip != NULL );
102  assert( heur != NULL );
103  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
104 
105  /* call inclusion method of primal heuristic */
106  SCIP_CALL( SCIPincludeHeurSdpInnerlp(scip) );
107 
108  return SCIP_OKAY;
109 }
110 
112 static
113 SCIP_DECL_HEURFREE(heurFreeSdpInnerlp)
114 { /*lint --e{715}*/
115  SCIP_HEURDATA* heurdata;
116 
117  assert( heur != NULL );
118  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
119  assert( scip != NULL );
120 
121  /* free heuristic data */
122  heurdata = SCIPheurGetData(heur);
123  assert(heurdata != NULL);
124 
125  SCIPfreeBlockMemory(scip, &heurdata);
126  SCIPheurSetData(heur, NULL);
127 
128  return SCIP_OKAY;
129 }
130 
132 static
133 SCIP_DECL_HEUREXEC(heurExecSdpInnerlp)
134 { /*lint --e{715}*/
135  SCIP_HEURDATA* heurdata;
136  SCIP_HASHMAP* varmapfw;
137  SCIP_CONSHDLR* conshdlrsdp;
138  SCIP_CONS** conss;
139  SCIP_VAR** subvars;
140  SCIP_VAR** vars;
141  SCIP_Bool success;
142  SCIP_Real timelimit;
143  SCIP_SOL** subsols;
144  SCIP* subscip;
145  int totalsize = 0;
146  int nsubsols;
147  int nconss;
148  int nvars;
149  int c;
150  int i;
151 
152  assert( heur != NULL );
153  assert( strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0 );
154  assert( scip != NULL );
155  assert( result != NULL );
156 
157  *result = SCIP_DELAYED;
158 
159  /* do not call heuristic if node was already detected to be infeasible */
160  if ( nodeinfeasible )
161  return SCIP_OKAY;
162 
163  *result = SCIP_DIDNOTRUN;
164 
165  /* compute time limit */
166  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
167  if ( ! SCIPisInfinity(scip, timelimit) )
168  {
169  timelimit = MAX(0, timelimit - SCIPgetSolvingTime(scip) );
170  }
171  if ( timelimit <= 0.01 )
172  return SCIP_OKAY;
173 
174  /* get heuristic data */
175  heurdata = SCIPheurGetData(heur);
176  assert( heurdata != NULL );
177 
178  /* estimate size of problem */
179  nconss = SCIPgetNConss(scip);
180  conss = SCIPgetConss(scip);
181  /* find SDP constraint handler */
182  conshdlrsdp = SCIPfindConshdlr(scip, "SDP");
183  if ( conshdlrsdp == NULL )
184  return SCIP_OKAY;
185 
186  for (c = 0; c < nconss && totalsize < heurdata->maxsize; ++c)
187  {
188  int blocksize;
189 
190  /* skip non-SDP constraints */
191  assert( conss[c] != NULL );
192  if ( SCIPconsGetHdlr(conss[c]) != conshdlrsdp )
193  continue;
194 
195  blocksize = SCIPconsSdpGetBlocksize(scip, conss[c]);
196  totalsize += (blocksize * (blocksize - 1))/2;
197  }
198 
199  if ( totalsize >= heurdata->maxsize )
200  {
201  SCIPdebugMsg(scip, "Skipping <%s>, because size would be too large.\n", SCIPheurGetName(heur));
202  return SCIP_OKAY;
203  }
204 
205  /* get original variable data */
206  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
207 
208  /* create subscip */
209  SCIP_CALL( SCIPcreate(&subscip) );
210 
211  /* create the variable mapping hash map */
212  SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), nvars) );
213 
214  /* create a problem copy as sub SCIP */
215  SCIP_CALL( SCIPcopyLargeNeighborhoodSearch(scip, subscip, varmapfw, "sdpinnerlp", NULL, NULL, 0, FALSE,
216  FALSE, &success, NULL) );
217 
218  /* find SDP constraint handler */
219  conshdlrsdp = SCIPfindConshdlr(subscip, "SDP");
220  if ( conshdlrsdp == NULL )
221  {
222  SCIP_CALL( SCIPfree(&subscip) );
223  return SCIP_OKAY;
224  }
225 
226  /* copy subproblem variables into the same order as the source SCIP variables */
227  SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) );
228  for( i = 0; i < nvars; i++ )
229  subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]);
230 
231  /* free hash map */
232  SCIPhashmapFree(&varmapfw);
233 
234  /* get orginal constraints: we have to copy them, because we are adding constraints which possibly leads to a reallocation */
235  nconss = SCIPgetNConss(subscip);
236  SCIP_CALL( SCIPduplicateBufferArray(scip, &conss, SCIPgetConss(subscip), nconss) );
237 
238  /* loop through all constraints */
239  for (c = 0; c < nconss; ++c)
240  {
241  char name[SCIP_MAXSTRLEN];
242  SCIP_Real** matrices = NULL;
243  SCIP_Real* constmatrix;
244  SCIP_VAR** consvars;
245  SCIP_Real* consvals;
246  SCIP_VAR** rayvars;
247  SCIP_CONS* cons;
248  SCIP_VAR** sdpvars;
249  SCIP_Real val;
250  int blocksize;
251  int nsdpvars;
252  int s;
253  int t;
254 
255  assert( conss[c] != NULL );
256 
257  /* skip non-SDP constraints */
258  if ( SCIPconsGetHdlr(conss[c]) != conshdlrsdp )
259  continue;
260 
261  blocksize = SCIPconsSdpGetBlocksize(subscip, conss[c]);
262  nsdpvars = SCIPconsSdpGetNVars(subscip, conss[c]);
263  sdpvars = SCIPconsSdpGetVars(subscip, conss[c]);
264 
265  /* get matrices */
266  SCIP_CALL( SCIPallocBufferArray(subscip, &constmatrix, blocksize * blocksize) );
267  SCIP_CALL( SCIPconsSdpGetFullConstMatrix(subscip, conss[c], constmatrix) );
268 
269  SCIP_CALL( SCIPallocBufferArray(subscip, &matrices, nsdpvars) );
270  for (i = 0; i < nsdpvars; ++i)
271  {
272  SCIP_CALL( SCIPallocBufferArray(subscip, &matrices[i], blocksize * blocksize) );
273  SCIP_CALL( SCIPconsSdpGetFullAj(subscip, conss[c], i, matrices[i]) );
274  }
275 
276  SCIP_CALL( SCIPallocBufferArray(subscip, &consvars, nsdpvars + 2 * blocksize) );
277  SCIP_CALL( SCIPallocBufferArray(subscip, &consvals, nsdpvars + 2 * blocksize) );
278  SCIP_CALL( SCIPallocBufferArray(subscip, &rayvars, blocksize * blocksize) );
279 
280  /* Create ray variables: Variable rayvars[s * blocksize + t] corresponds to a rank-1 matrix. The submatrix indexed
281  * by (s,t) is (1,1;1,1) or (1,-1;-1,1) depending on whether s < t or s > t. If s = t, we have a diagonal matrix
282  * with a 1. */
283  for (s = 0; s < blocksize; ++s)
284  {
285  for (t = 0; t <= s; ++t)
286  {
287  SCIP_Bool nonzero = FALSE;
288 
289  /* check whether entry (s,t) has a nonzero somewhere - otherwise we do not need variables or constraints */
290  if ( SCIPisZero(subscip, constmatrix[s * blocksize + t]) )
291  {
292  for (i = 0; i < nsdpvars; ++i)
293  {
294  val = matrices[i][s * blocksize + t];
295  if ( ! SCIPisZero(subscip, val) )
296  {
297  nonzero = TRUE;
298  break;
299  }
300  }
301  }
302  else
303  nonzero = TRUE;
304 
305  if ( nonzero || s == t )
306  {
307  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "ray%d#%d", s, t);
308  SCIP_CALL( SCIPcreateVarBasic(subscip, &rayvars[s * blocksize + t], name, 0.0, SCIPinfinity(subscip), 0.0, SCIP_VARTYPE_CONTINUOUS) );
309  SCIP_CALL( SCIPaddVar(subscip, rayvars[s * blocksize + t]) );
310 
311  if ( s != t )
312  {
313  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "ray%d#%d", t, s);
314  SCIP_CALL( SCIPcreateVarBasic(subscip, &rayvars[t * blocksize + s], name, 0.0, SCIPinfinity(subscip), 0.0, SCIP_VARTYPE_CONTINUOUS) );
315  SCIP_CALL( SCIPaddVar(subscip, rayvars[t * blocksize + s]) );
316  }
317  }
318  else
319  {
320  rayvars[s * blocksize + t] = NULL;
321  rayvars[t * blocksize + s] = NULL;
322  }
323  }
324  }
325 
326  /* loop over all possible entries */
327  for (s = 0; s < blocksize; ++s)
328  {
329  for (t = 0; t <= s; ++t)
330  {
331  int cnt = 0;
332 
333  /* skip 0-entries */
334  if ( rayvars[s * blocksize + t] == NULL )
335  continue;
336 
337  assert( rayvars[t * blocksize + s] != NULL );
338 
339  /* add entries for matrices */
340  for (i = 0; i < nsdpvars; ++i)
341  {
342  val = matrices[i][s * blocksize + t];
343  if ( ! SCIPisZero(subscip, val) )
344  {
345  consvars[cnt] = sdpvars[i];
346  consvals[cnt++] = val;
347  }
348  }
349 
350  /* add ray variables: -1 because we have to bring the variables to the LHS */
351  consvars[cnt] = rayvars[s * blocksize + t];
352  consvals[cnt++] = -1.0;
353 
354  if ( s != t )
355  {
356  consvars[cnt] = rayvars[t * blocksize + s];
357  consvals[cnt++] = +1.0;
358  }
359  else
360  {
361  /* add all off-diagonal variables */
362  for (i = 0; i < blocksize; ++i)
363  {
364  if ( i != s )
365  {
366  if ( rayvars[s * blocksize + i] != NULL )
367  {
368  assert( rayvars[i * blocksize + s] != NULL );
369 
370  consvars[cnt] = rayvars[s * blocksize + i];
371  consvals[cnt++] = -1.0;
372 
373  consvars[cnt] = rayvars[i * blocksize + s];
374  consvals[cnt++] = -1.0;
375  }
376  }
377  }
378  }
379 
380  /* add linear constraint */
381  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "lin%d#%d", s, t);
382  SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, name, cnt, consvars, consvals, constmatrix[s * blocksize + t], constmatrix[s * blocksize + t],
383  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) );
384  SCIP_CALL( SCIPaddCons(subscip, cons) );
385 #ifdef SCIP_MORE_DEBUG
386  SCIP_CALL( SCIPprintCons(subscip, cons, NULL) );
387 #endif
388  SCIP_CALL( SCIPreleaseCons(subscip, &cons) );
389  }
390  }
391 
392  /* delete SDP constraint */
393  SCIP_CALL( SCIPdelCons(subscip, conss[c]) );
394 
395  for (i = 0; i < blocksize * blocksize; ++i)
396  {
397  if ( rayvars[i] != NULL )
398  {
399  SCIP_CALL( SCIPreleaseVar(subscip, &rayvars[i]) );
400  }
401  }
402  SCIPfreeBufferArray(subscip, &rayvars);
403  SCIPfreeBufferArray(subscip, &consvals);
404  SCIPfreeBufferArray(subscip, &consvars);
405 
406  for (i = nsdpvars-1; i >= 0; --i)
407  SCIPfreeBufferArray(subscip, &matrices[i]);
408  SCIPfreeBufferArray(subscip, &matrices);
409  SCIPfreeBufferArray(subscip, &constmatrix);
410  }
411 
412 #if 0
413  SCIP_CALL( SCIPwriteOrigProblem(subscip, "debug.lp", "lp", FALSE) );
414 #endif
415 
416  /* set individual time limit */
417  if ( ! SCIPisInfinity(scip, timelimit) )
418  {
419  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
420  }
421 
422  /* set stall node limit */
423  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", heurdata->stallnodelimit) );
424 
425 #ifdef SCIP_MORE_DEBUG
426  SCIPinfoMessage(scip, NULL, "\nSolving inner LP subproblem ...\n");
427  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) );
428 #else
429  SCIPdebugMsg(scip, "Solving inner LP subproblem ...\n");
430  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
431 #endif
432 
433  /* turn off recursive use */
434  SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/sdpinnerlp/freq", -1) );
435 
436  SCIP_CALL( SCIPsolve(subscip) );
437 
438  /* check, whether a solution was found */
439  nsubsols = SCIPgetNSols(subscip);
440  subsols = SCIPgetSols(subscip);
441  success = FALSE;
442  for (i = 0; i < nsubsols && ! success; ++i)
443  {
444  SCIP_SOL* newsol;
445 
446  SCIP_CALL( SCIPtranslateSubSol(scip, subscip, subsols[i], heur, subvars, &newsol) );
447 
448  SCIP_CALL( SCIPtrySolFree(scip, &newsol, FALSE, FALSE, TRUE, TRUE, TRUE, &success) );
449  if ( success )
450  {
451  SCIPdebugMsg(scip, "Found solution.\n");
452  *result = SCIP_FOUNDSOL;
453  }
454  }
455 
456  SCIP_CALL( SCIPfree(&subscip) );
457 
458  SCIPfreeBufferArray(scip, &subvars);
459  SCIPfreeBufferArray(scip, &conss);
460 
461  return SCIP_OKAY;
462 }
463 
464 
465 /*
466  * heuristic specific interface methods
467  */
468 
471  SCIP* scip
472  )
473 {
474  SCIP_HEURDATA* heurdata;
475  SCIP_HEUR* heur;
476 
477  /* create innerlp primal heuristic data */
478  SCIP_CALL( SCIPallocBlockMemory(scip, &heurdata) );
479 
480  /* include primal heuristic */
481  SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
483  HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecSdpInnerlp, heurdata) );
484 
485  assert( heur != NULL );
486 
487  /* set non-NULL pointers to callback methods */
488  SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopySdpInnerlp) );
489  SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeSdpInnerlp) );
490 
491  SCIP_CALL( SCIPaddLongintParam(scip, "heuristics/" HEUR_NAME "/stallnodelimit",
492  "limit on number of nodes since last improving incumbent solutions",
493  &heurdata->stallnodelimit, FALSE, DEFAULT_STALLNODELIMIT, -1LL, SCIP_LONGINT_MAX, NULL, NULL) );
494 
495  SCIP_CALL( SCIPaddIntParam(scip, "heuristics/" HEUR_NAME "/maxsize",
496  "maximal size of the inner problem",
497  &heurdata->maxsize, FALSE, DEFAULT_MAXSIZE, -1, INT_MAX, NULL, NULL) );
498 
499  return SCIP_OKAY;
500 }
int SCIPconsSdpGetNVars(SCIP *scip, SCIP_CONS *cons)
Definition: cons_sdp.c:8115
static SCIP_DECL_HEURCOPY(heurCopySdpInnerlp)
SCIP_RETCODE SCIPconsSdpGetFullAj(SCIP *scip, SCIP_CONS *cons, int j, SCIP_Real *Aj)
Definition: cons_sdp.c:8166
#define HEUR_NAME
SCIP_RETCODE SCIPincludeHeurSdpInnerlp(SCIP *scip)
#define HEUR_DISPCHAR
static SCIP_DECL_HEUREXEC(heurExecSdpInnerlp)
#define DEFAULT_STALLNODELIMIT
#define HEUR_DESC
static SCIP_DECL_HEURFREE(heurFreeSdpInnerlp)
set up inner approximation LP formulation and run heuristics
Constraint handler for SDP-constraints.
SCIP_RETCODE SCIPconsSdpGetFullConstMatrix(SCIP *scip, SCIP_CONS *cons, SCIP_Real *mat)
Definition: cons_sdp.c:8201
#define HEUR_FREQOFS
#define HEUR_TIMING
SCIP_VAR ** SCIPconsSdpGetVars(SCIP *scip, SCIP_CONS *cons)
Definition: cons_sdp.c:8132
int SCIPconsSdpGetBlocksize(SCIP *scip, SCIP_CONS *cons)
Definition: cons_sdp.c:8149
#define HEUR_PRIORITY
#define DEFAULT_MAXSIZE
#define HEUR_MAXDEPTH
#define HEUR_USESSUBSCIP
#define HEUR_FREQ