SCIP-SDP  3.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
prop_sdpobbt.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-2017 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-2017 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 /*#define SCIP_DEBUG*/
41 /*#define SCIP_MORE_DEBUG*/
42 
43 #include <assert.h>
44 #include <string.h>
45 
46 #include "prop_sdpobbt.h"
47 #include "relax_sdp.h"
48 
49 /* turn off lint warnings for whole file: */
50 /*lint --e{788,818}*/
51 
52 /* fundamental propagator properties */
53 #define PROP_NAME "sdp-obbt"
54 #define PROP_DESC "optimization-based bound tightening for SDPs"
55 #define PROP_PRIORITY -1100000
56 #define PROP_FREQ -1
57 #define PROP_DELAY FALSE
58 #define PROP_TIMING SCIP_PROPTIMING_AFTERLPLOOP
60 #define DEFAULT_PROPBIN FALSE
61 #define DEFAULT_PROPCONT TRUE
64 /*
65  * Data structures
66  */
67 
69 struct SCIP_PropData
70 {
71  SCIP_Bool propbin;
72  SCIP_Bool propcont;
73  long long int lastnode;
74  SCIP_Real sdpsolvergaptol;
75 };
76 
77 
78 /*
79  * Local methods
80  */
81 
82 /* the current bugfix branch (3.2.1) does not have SCIPsolveProbingRelax() -> do nothing */
83 #if ( (SCIP_VERSION > 321 || SCIP_SUBVERSION > 0) )
84 static
85 SCIP_RETCODE addObjCutoff(
86  SCIP* scip
87  )
88 {
89  SCIP_ROW* row;
90  SCIP_VAR** vars;
91  char rowname[SCIP_MAXSTRLEN];
92  int nvars;
93  int v;
94 
95  assert( scip != NULL );
96  assert( SCIPinProbing(scip) );
97 
98  SCIPdebugMessage("create objective cutoff and add it to the LP-constraints\n");
99 
100  nvars = SCIPgetNVars(scip);
101  vars = SCIPgetVars(scip);
102 
103  /* create objective cutoff row; set local flag to FALSE since primal cutoff is globally valid */
104  (void) SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "obbtsdp_objcutoff");
105  SCIP_CALL( SCIPcreateEmptyRowUnspec(scip, &row, rowname, -SCIPinfinity(scip), SCIPgetCutoffbound(scip), FALSE, FALSE, FALSE) );
106  SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
107 
108  for( v = 0; v < nvars; v++ )
109  {
110  SCIP_CALL( SCIPaddVarToRow(scip, row, vars[v], SCIPvarGetObj(vars[v])) );
111  }
112  SCIP_CALL( SCIPflushRowExtensions(scip, row) );
113 
114  /* add row to the LP-constraints */
115  SCIP_CALL( SCIPaddRowProbing(scip, row) );
116 
117  SCIP_CALL( SCIPreleaseRow(scip, &row) );
118 
119  return SCIP_OKAY;
120 }
121 #endif
122 
123 /*
124  * Callback methods of propagator
125  */
126 
127 
129 static
130 SCIP_DECL_PROPCOPY(propCopySdpObbt)
131 { /*lint --e{715}*/
132  assert( scip != NULL );
133  assert( prop != NULL );
134  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
135 
136  /* call inclusion method of constraint handler */
137  SCIP_CALL( SCIPincludePropSdpObbt(scip) );
138 
139  return SCIP_OKAY;
140 }
141 
142 
144 static
145 SCIP_DECL_PROPFREE(propFreeSdpObbt)
146 { /*lint --e{715}*/
147  SCIP_PROPDATA* propdata;
148 
149  propdata = SCIPpropGetData(prop);
150  assert(propdata != NULL);
151 
152  SCIPfreeMemory(scip, &propdata);
153  SCIPpropSetData(prop, NULL);
154 
155  return SCIP_OKAY;
156 }
157 
159 static
160 SCIP_DECL_PROPEXIT(propExitSdpObbt)
161 { /*lint --e{715}*/
162  SCIP_PROPDATA* propdata;
163 
164  assert( prop != NULL );
165 
166  propdata = SCIPpropGetData(prop);
167 
168  propdata->lastnode = -1; /* we reset this to be able to run again if a new problem is read */
169 
170  return SCIP_OKAY;
171 }
172 
174 static
175 SCIP_DECL_PROPINITSOL(propInitsolSdpObbt)
176 { /*lint --e{715}*/
177  SCIP_PROPDATA* propdata;
178 
179  assert( prop != NULL );
180 
181  propdata = SCIPpropGetData(prop);
182 
183  SCIP_CALL( SCIPgetRealParam(scip, "relaxing/SDP/sdpsolvergaptol", &(propdata->sdpsolvergaptol)) );
184 
185  return SCIP_OKAY;
186 }
187 
189 static
190 SCIP_DECL_PROPEXEC(propExecSdpObbt)
191 { /*lint --e{715}*/
192  /* the current bugfix branch (3.2.1) does not have SCIPsolveProbingRelax() -> do nothing */
193 #if ( (SCIP_VERSION > 321 || SCIP_SUBVERSION > 0) )
194  int nvars;
195  SCIP_VAR** vars;
196  int v;
197  SCIP_PROPDATA* propdata;
198  SCIP_Real relaxval;
199  SCIP_Bool cutoff;
200  SCIP_Bool oldobjlimitparam;
201  SCIP_Real probingval;
202  SCIP_Bool success;
203  SCIP_RELAX* relaxsdp;
204  /* newbounds and newboundinds save the bound tightenings that should be inserted after probing ends, newboundinds saves the bounds the entries of newbounds
205  * belong to, for this the variables are sorted 1 to nvars (and entry i means vars[i-1]), with negative entry for the lower bound and positive for the upper */
206  SCIP_Real* newbounds;
207  int* newboundinds;
208  int nnewbounds;
209  int i;
210 
211  assert( scip != NULL );
212  assert( prop != NULL );
213  assert( result != NULL );
214 
215  *result = SCIP_DIDNOTRUN;
216 
217  SCIPdebugMessage("Executing propExecSdpObbt! \n");
218 
219  /* if there is no cutoff-bound, we don't want to run */
220  if ( SCIPisInfinity(scip, SCIPgetCutoffbound(scip)) )
221  {
222  SCIPdebugMessage("Aborting propExecSdpObbt because of lack of cutoff-bound!\n");
223  return SCIP_OKAY;
224  }
225 
226  /* do not run in: presolving, repropagation, probing mode, if no objective propagation is allowed */
227  if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING || SCIPinRepropagation(scip) || SCIPinProbing(scip) || !SCIPallowObjProp(scip) )
228  {
229  SCIPdebugMessage("Aborting propExecSdpObbt because we are in presolving, repropagation, probing mode or no objective propagation is allowed!\n");
230  return SCIP_OKAY;
231  }
232 
233  vars = SCIPgetVars(scip);
234  nvars = SCIPgetNVars(scip);
235  propdata = SCIPpropGetData(prop);
236 
237  assert( propdata != NULL );
238 
239  if ( SCIPnodeGetNumber(SCIPgetCurrentNode(scip)) == propdata->lastnode )
240  {
241  SCIPdebugMessage("Not running again for node %lld!\n", propdata->lastnode);
242  return SCIP_OKAY;
243  }
244  else
245  {
246  propdata->lastnode = SCIPnodeGetNumber(SCIPgetCurrentNode(scip));
247  }
248 
249  /* start probing */
250  SCIP_CALL( SCIPstartProbing(scip) );
251  SCIPdebugMessage("start probing\n");
252 
253  SCIP_CALL( addObjCutoff(scip) );
254 
255  /* make sure that we don't use the objective cutoff for the changed objective */
256  SCIP_CALL( SCIPgetBoolParam(scip, "relaxing/SDP/objlimit", &oldobjlimitparam) );
257  SCIP_CALL( SCIPsetBoolParam(scip, "relaxing/SDP/objlimit", FALSE) );
258 
259  /* allocate memory to save bounds */
260  SCIP_CALL( SCIPallocBufferArray(scip, &newbounds, 2*nvars) );/*lint !e647*/
261  SCIP_CALL( SCIPallocBufferArray(scip, &newboundinds, 2*nvars) );/*lint !e647*/
262 
263  *result = SCIP_DIDNOTFIND;
264 
265  /* set objective coefficients to zero */
266  for( v = 0; v < nvars; ++v )
267  {
268  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 0.0) );
269  }
270 
271  nnewbounds = 0;
272 
273  for (v = 0; v < nvars; v++)
274  {
275  /* do not propagate binary or continous variables if the corresponding flag is set to false */
276  if ( (( ! propdata->propbin ) && SCIPvarIsBinary(vars[v])) || (( ! propdata->propcont ) && ( ! SCIPvarIsIntegral(vars[v]))) )
277  {
278 #ifdef SCIP_MORE_DEBUG
279  if ( SCIPvarIsBinary(vars[v]) )
280  {
281  SCIPdebugMessage("Skipping binary variable %s\n", SCIPvarGetName(vars[v]));
282  }
283  else
284  {
285  SCIPdebugMessage("Skipping continuous variable %s\n", SCIPvarGetName(vars[v]));
286  }
287 #endif
288  continue;
289  }
290 
291  /* get the value of this variable for the current relaxation */
292  relaxval = SCIPgetRelaxSolVal(scip, vars[v]);
293 
294  /* only try obbt for the lower bound if it is not tight for the current relaxation's solution */
295  if ( SCIPisFeasGT(scip, relaxval, SCIPvarGetLbLocal(vars[v])) )
296  {
297  /* set the objective to minimize y_v */
298  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 1.0) );
299 
300  /* solve the probing problem */
301  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
302 
303  /* as cutoff doesn't work for relax sdp, we have to check ourselves, if we didn't manage to solve successfully, we abort (as this will
304  * probably not get better for the other variables as we only change the objective */
305  relaxsdp = SCIPfindRelax(scip, "SDP");
306 
307  if (! SCIPrelaxSdpSolvedProbing(relaxsdp))
308  {
309  SCIPdebugMessage("Aborting sdp-obbt, as we were unable to solve a probing sdp!\n");
310  if ( *result != SCIP_REDUCEDDOM )
311  *result = SCIP_DIDNOTRUN;
312  break;
313  }
314 
315  /* if the problem is infeasible, return with cutoff */
316  if ( ! SCIPrelaxSdpIsFeasible(relaxsdp) )
317  {
318  SCIPdebugMessage("Probing sdp infeasible, so there can't be a better solution for this problem!\n");
319  *result = SCIP_CUTOFF;
320  break;
321  }
322 
323  /* check if we managed to tighten the bound */
324  success = FALSE; /* this will be ignored, we check solvedProbing instead */
325  SCIP_CALL( SCIPrelaxSdpRelaxVal(relaxsdp, &success, &probingval) );
326 
327  /* only update if we improved the bound by at least gaptol, everything else might be inexactness of the solver */
328  if ( SCIPisGT(scip, probingval - propdata->sdpsolvergaptol, SCIPvarGetLbLocal(vars[v])) )
329  {
330  /* update bound */
331  SCIPdebugMessage("Obbt-Sdp tightened lower bound of variable %s from %f to %f !\n",
332  SCIPvarGetName(vars[v]), SCIPvarGetLbLocal(vars[v]), probingval - propdata->sdpsolvergaptol);
333 
334  newbounds[nnewbounds] = probingval;
335  newboundinds[nnewbounds] = -1 * (v+1);
336  nnewbounds++;
337  *result = SCIP_REDUCEDDOM;
338  }
339 #ifdef SCIP_MORE_DEBUG
340  else
341  {
342  SCIPdebugMessage("Obbt-Sdp found lower bound of %f for variable %s, worse than old bound %f !\n",
343  probingval, SCIPvarGetName(vars[v]), SCIPvarGetLbLocal(vars[v]));
344  }
345 #endif
346  }
347 #ifdef SCIP_MORE_DEBUG
348  else
349  {
350  SCIPdebugMessage("Skipping obbt for lower bound %f of variable %s, as current relaxation's solution is tight.\n",
351  SCIPvarGetLbLocal(vars[v]), SCIPvarGetName(vars[v]));
352  }
353 #endif
354 
355  /* only try obbt for the upper bound if it is not tight for the current relaxation's solution */
356  if ( SCIPisFeasLT(scip, relaxval, SCIPvarGetUbLocal(vars[v])) )
357  {
358  /* set the objective to maximize y_v (minimize -y_v) */
359  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], -1.0) );
360 
361  /* solve the probing problem */
362  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
363 
364  /* as cutoff doesn't work for relax sdp, we have to check ourselves, if we didn't manage to solve successfully, we abort (as this will
365  * probably not get better for the other variables as we only change the objective */
366  relaxsdp = SCIPfindRelax(scip, "SDP");
367 
368  if (! SCIPrelaxSdpSolvedProbing(relaxsdp))
369  {
370  SCIPdebugMessage("Aborting sdp-obbt, as we were unable to solve a probing sdp!\n");
371  if ( *result != SCIP_REDUCEDDOM )
372  *result = SCIP_DIDNOTRUN;
373  break;
374  }
375 
376  /* if the problem is infeasible, return with cutoff */
377  if ( ! SCIPrelaxSdpIsFeasible(relaxsdp) )
378  {
379  SCIPdebugMessage("Probing sdp infeasible, so there can't be a better solution for this problem!\n");
380  *result = SCIP_CUTOFF;
381  break;
382  }
383 
384  /* check if we managed to tighten the bound */
385  success = FALSE; /* this will be ignored, we check solvedProbing instead */
386  SCIP_CALL( SCIPrelaxSdpRelaxVal(relaxsdp, &success, &probingval) );
387 
388  /* only update if we improved the bound by at least gaptol, everything else might be inexactness of the solver */
389  if ( SCIPisLT(scip, -probingval + propdata->sdpsolvergaptol, SCIPvarGetUbLocal(vars[v])) )
390  {
391  SCIPdebugMessage("Obbt-Sdp tightened upper bound of variable %s from %f to %f !\n",
392  SCIPvarGetName(vars[v]), SCIPvarGetUbLocal(vars[v]), -probingval + propdata->sdpsolvergaptol);
393 
394  newbounds[nnewbounds] = -probingval;
395  newboundinds[nnewbounds] = v + 1;
396  nnewbounds++;
397  *result = SCIP_REDUCEDDOM;
398  }
399 #ifdef SCIP_MORE_DEBUG
400  else
401  {
402  SCIPdebugMessage("Obbt-Sdp found upper bound of %f for variable %s, worse than old bound %f !\n",
403  -probingval, SCIPvarGetName(vars[v]), SCIPvarGetUbLocal(vars[v]));
404  }
405 #endif
406  }
407 #ifdef SCIP_MORE_DEBUG
408  else
409  {
410  SCIPdebugMessage("Skipping obbt for upper bound %f of variable %s, as current relaxation's solution is tight.\n",
411  SCIPvarGetUbLocal(vars[v]), SCIPvarGetName(vars[v]));
412  }
413 #endif
414 
415  /* reset the objective coefficient to zero for the next variable */
416  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 0.0) );
417  }
418 
419  SCIP_CALL( SCIPendProbing(scip) );
420  SCIPdebugMessage("end probing\n");
421  SCIP_CALL( SCIPsetBoolParam(scip, "relaxing/SDP/objlimit", oldobjlimitparam) );
422 
423  for (i = 0; i < nnewbounds; i++)
424  {
425  if ( newboundinds[i] < 0)
426  {
427  SCIP_CALL( SCIPchgVarLb(scip, vars[-1 * newboundinds[i] - 1], newbounds[i]) ); /*lint !e679*/
428  }
429  else
430  {
431  SCIP_CALL( SCIPchgVarUb(scip, vars[newboundinds[i] - 1], newbounds[i]) );
432  }
433  }
434 
435  SCIPfreeBufferArray(scip, &newboundinds);
436  SCIPfreeBufferArray(scip, &newbounds);
437 
438  return SCIP_OKAY;
439 
440 #else
441  *result = SCIP_DIDNOTRUN;
442 
443  return SCIP_OKAY;
444 #endif
445 }
446 
447 
448 
449 /*
450  * propagator specific interface methods
451  */
452 
455  SCIP* scip
456  )
457 {
458  SCIP_PROPDATA* propdata;
459  SCIP_PROP* prop;
460 
461  /* create SdpObbt propagator data */
462  propdata = NULL;
463  SCIP_CALL( SCIPallocMemory(scip, &propdata) );
464  propdata->lastnode = -1;
465 
466  /* include propagator */
467  /* use SCIPincludePropBasic() plus setter functions if you want to set callbacks one-by-one and your code should
468  * compile independent of new callbacks being added in future SCIP versions
469  */
470  SCIP_CALL( SCIPincludePropBasic(scip, &prop, PROP_NAME, PROP_DESC, PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING,
471  propExecSdpObbt, propdata) );
472 
473  assert(prop != NULL);
474 
475  /* set optional callbacks via setter functions */
476  SCIP_CALL( SCIPsetPropCopy(scip, prop, propCopySdpObbt) );
477  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSdpObbt) );
478  SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSdpObbt) );
479  SCIP_CALL( SCIPsetPropInitsol(scip, prop, propInitsolSdpObbt) );
480 
481  /* add SdpObbt propagator parameters */
482  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/propbin",
483  "Should optimization-based bound tightening be performed for binary variables?",
484  &propdata->propbin, TRUE, DEFAULT_PROPBIN, NULL, NULL) );
485 
486  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/propcont",
487  "Should optimization-based bound tightening be performed for continuous variables?",
488  &propdata->propcont, TRUE, DEFAULT_PROPCONT, NULL, NULL) );
489 
490  return SCIP_OKAY;
491 }
#define PROP_TIMING
Definition: prop_sdpobbt.c:58
SCIP_RETCODE SCIPincludePropSdpObbt(SCIP *scip)
Definition: prop_sdpobbt.c:454
#define PROP_NAME
Definition: prop_sdpobbt.c:53
static SCIP_DECL_PROPEXIT(propExitSdpObbt)
Definition: prop_sdpobbt.c:160
static SCIP_DECL_PROPEXEC(propExecSdpObbt)
Definition: prop_sdpobbt.c:190
static SCIP_DECL_PROPCOPY(propCopySdpObbt)
Definition: prop_sdpobbt.c:130
SDP-relaxator.
#define PROP_DESC
Definition: prop_sdpobbt.c:54
optimization-based bound tightening propagator for semidefinite programs
SCIP_RETCODE SCIPrelaxSdpRelaxVal(SCIP_RELAX *relax, SCIP_Bool *success, SCIP_Real *objval)
Definition: relax_sdp.c:1777
static SCIP_DECL_PROPINITSOL(propInitsolSdpObbt)
Definition: prop_sdpobbt.c:175
#define PROP_FREQ
Definition: prop_sdpobbt.c:56
static SCIP_DECL_PROPFREE(propFreeSdpObbt)
Definition: prop_sdpobbt.c:145
SCIP_Bool SCIPrelaxSdpSolvedProbing(SCIP_RELAX *relax)
Definition: relax_sdp.c:1860
#define DEFAULT_PROPBIN
Definition: prop_sdpobbt.c:60
#define DEFAULT_PROPCONT
Definition: prop_sdpobbt.c:61
#define PROP_PRIORITY
Definition: prop_sdpobbt.c:55
#define PROP_DELAY
Definition: prop_sdpobbt.c:57
SCIP_Bool SCIPrelaxSdpIsFeasible(SCIP_RELAX *relax)
Definition: relax_sdp.c:1877