SCIP-SDP  3.1.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 if propagation w.r.t. objective is not allowed */
227  if( !SCIPallowObjProp(scip) )
228  return SCIP_OKAY;
229 
230  /* do not run in: presolving, repropagation, probing mode, if no objective propagation is allowed */
231  if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING || SCIPinRepropagation(scip) || SCIPinProbing(scip) || !SCIPallowObjProp(scip) )
232  {
233  SCIPdebugMessage("Aborting propExecSdpObbt because we are in presolving, repropagation, probing mode or no objective propagation is allowed!\n");
234  return SCIP_OKAY;
235  }
236 
237  vars = SCIPgetVars(scip);
238  nvars = SCIPgetNVars(scip);
239  propdata = SCIPpropGetData(prop);
240 
241  assert( propdata != NULL );
242 
243  if ( SCIPnodeGetNumber(SCIPgetCurrentNode(scip)) == propdata->lastnode )
244  {
245  SCIPdebugMessage("Not running again for node %lld!\n", propdata->lastnode);
246  return SCIP_OKAY;
247  }
248  else
249  {
250  propdata->lastnode = SCIPnodeGetNumber(SCIPgetCurrentNode(scip));
251  }
252 
253  /* start probing */
254  SCIP_CALL( SCIPstartProbing(scip) );
255  SCIPdebugMessage("start probing\n");
256 
257  SCIP_CALL( addObjCutoff(scip) );
258 
259  /* make sure that we don't use the objective cutoff for the changed objective */
260  SCIP_CALL( SCIPgetBoolParam(scip, "relaxing/SDP/objlimit", &oldobjlimitparam) );
261  SCIP_CALL( SCIPsetBoolParam(scip, "relaxing/SDP/objlimit", FALSE) );
262 
263  /* allocate memory to save bounds */
264  SCIP_CALL( SCIPallocBufferArray(scip, &newbounds, 2*nvars) );/*lint !e647*/
265  SCIP_CALL( SCIPallocBufferArray(scip, &newboundinds, 2*nvars) );/*lint !e647*/
266 
267  *result = SCIP_DIDNOTFIND;
268 
269  /* set objective coefficients to zero */
270  for( v = 0; v < nvars; ++v )
271  {
272  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 0.0) );
273  }
274 
275  nnewbounds = 0;
276 
277  for (v = 0; v < nvars; v++)
278  {
279  /* do not propagate binary or continous variables if the corresponding flag is set to false */
280  if ( (( ! propdata->propbin ) && SCIPvarIsBinary(vars[v])) || (( ! propdata->propcont ) && ( ! SCIPvarIsIntegral(vars[v]))) )
281  {
282 #ifdef SCIP_MORE_DEBUG
283  if ( SCIPvarIsBinary(vars[v]) )
284  {
285  SCIPdebugMessage("Skipping binary variable %s\n", SCIPvarGetName(vars[v]));
286  }
287  else
288  {
289  SCIPdebugMessage("Skipping continuous variable %s\n", SCIPvarGetName(vars[v]));
290  }
291 #endif
292  continue;
293  }
294 
295  /* get the value of this variable for the current relaxation */
296  relaxval = SCIPgetRelaxSolVal(scip, vars[v]);
297 
298  /* only try obbt for the lower bound if it is not tight for the current relaxation's solution */
299  if ( SCIPisFeasGT(scip, relaxval, SCIPvarGetLbLocal(vars[v])) )
300  {
301  /* set the objective to minimize y_v */
302  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 1.0) );
303 
304  /* solve the probing problem */
305  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
306 
307  /* 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
308  * probably not get better for the other variables as we only change the objective */
309  relaxsdp = SCIPfindRelax(scip, "SDP");
310 
311  if (! SCIPrelaxSdpSolvedProbing(relaxsdp))
312  {
313  SCIPdebugMessage("Aborting sdp-obbt, as we were unable to solve a probing sdp!\n");
314  if ( *result != SCIP_REDUCEDDOM )
315  *result = SCIP_DIDNOTRUN;
316  break;
317  }
318 
319  /* if the problem is infeasible, return with cutoff */
320  if ( ! SCIPrelaxSdpIsFeasible(relaxsdp) )
321  {
322  SCIPdebugMessage("Probing sdp infeasible, so there can't be a better solution for this problem!\n");
323  *result = SCIP_CUTOFF;
324  break;
325  }
326 
327  /* check if we managed to tighten the bound */
328  success = FALSE; /* this will be ignored, we check solvedProbing instead */
329  SCIP_CALL( SCIPrelaxSdpRelaxVal(relaxsdp, &success, &probingval) );
330 
331  /* only update if we improved the bound by at least gaptol, everything else might be inexactness of the solver */
332  if ( SCIPisGT(scip, probingval - propdata->sdpsolvergaptol, SCIPvarGetLbLocal(vars[v])) )
333  {
334  /* update bound */
335  SCIPdebugMessage("Obbt-Sdp tightened lower bound of variable %s from %f to %f !\n",
336  SCIPvarGetName(vars[v]), SCIPvarGetLbLocal(vars[v]), probingval - propdata->sdpsolvergaptol);
337 
338  newbounds[nnewbounds] = probingval;
339  newboundinds[nnewbounds] = -1 * (v+1);
340  nnewbounds++;
341  *result = SCIP_REDUCEDDOM;
342  }
343 #ifdef SCIP_MORE_DEBUG
344  else
345  {
346  SCIPdebugMessage("Obbt-Sdp found lower bound of %f for variable %s, worse than old bound %f !\n",
347  probingval, SCIPvarGetName(vars[v]), SCIPvarGetLbLocal(vars[v]));
348  }
349 #endif
350  }
351 #ifdef SCIP_MORE_DEBUG
352  else
353  {
354  SCIPdebugMessage("Skipping obbt for lower bound %f of variable %s, as current relaxation's solution is tight.\n",
355  SCIPvarGetLbLocal(vars[v]), SCIPvarGetName(vars[v]));
356  }
357 #endif
358 
359  /* only try obbt for the upper bound if it is not tight for the current relaxation's solution */
360  if ( SCIPisFeasLT(scip, relaxval, SCIPvarGetUbLocal(vars[v])) )
361  {
362  /* set the objective to maximize y_v (minimize -y_v) */
363  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], -1.0) );
364 
365  /* solve the probing problem */
366  SCIP_CALL( SCIPsolveProbingRelax(scip, &cutoff) );
367 
368  /* 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
369  * probably not get better for the other variables as we only change the objective */
370  relaxsdp = SCIPfindRelax(scip, "SDP");
371 
372  if (! SCIPrelaxSdpSolvedProbing(relaxsdp))
373  {
374  SCIPdebugMessage("Aborting sdp-obbt, as we were unable to solve a probing sdp!\n");
375  if ( *result != SCIP_REDUCEDDOM )
376  *result = SCIP_DIDNOTRUN;
377  break;
378  }
379 
380  /* if the problem is infeasible, return with cutoff */
381  if ( ! SCIPrelaxSdpIsFeasible(relaxsdp) )
382  {
383  SCIPdebugMessage("Probing sdp infeasible, so there can't be a better solution for this problem!\n");
384  *result = SCIP_CUTOFF;
385  break;
386  }
387 
388  /* check if we managed to tighten the bound */
389  success = FALSE; /* this will be ignored, we check solvedProbing instead */
390  SCIP_CALL( SCIPrelaxSdpRelaxVal(relaxsdp, &success, &probingval) );
391 
392  /* only update if we improved the bound by at least gaptol, everything else might be inexactness of the solver */
393  if ( SCIPisLT(scip, -probingval + propdata->sdpsolvergaptol, SCIPvarGetUbLocal(vars[v])) )
394  {
395  SCIPdebugMessage("Obbt-Sdp tightened upper bound of variable %s from %f to %f !\n",
396  SCIPvarGetName(vars[v]), SCIPvarGetUbLocal(vars[v]), -probingval + propdata->sdpsolvergaptol);
397 
398  newbounds[nnewbounds] = -probingval;
399  newboundinds[nnewbounds] = v + 1;
400  nnewbounds++;
401  *result = SCIP_REDUCEDDOM;
402  }
403 #ifdef SCIP_MORE_DEBUG
404  else
405  {
406  SCIPdebugMessage("Obbt-Sdp found upper bound of %f for variable %s, worse than old bound %f !\n",
407  -probingval, SCIPvarGetName(vars[v]), SCIPvarGetUbLocal(vars[v]));
408  }
409 #endif
410  }
411 #ifdef SCIP_MORE_DEBUG
412  else
413  {
414  SCIPdebugMessage("Skipping obbt for upper bound %f of variable %s, as current relaxation's solution is tight.\n",
415  SCIPvarGetUbLocal(vars[v]), SCIPvarGetName(vars[v]));
416  }
417 #endif
418 
419  /* reset the objective coefficient to zero for the next variable */
420  SCIP_CALL( SCIPchgVarObjProbing(scip, vars[v], 0.0) );
421  }
422 
423  SCIP_CALL( SCIPendProbing(scip) );
424  SCIPdebugMessage("end probing\n");
425  SCIP_CALL( SCIPsetBoolParam(scip, "relaxing/SDP/objlimit", oldobjlimitparam) );
426 
427  for (i = 0; i < nnewbounds; i++)
428  {
429  if ( newboundinds[i] < 0)
430  {
431  SCIP_CALL( SCIPchgVarLb(scip, vars[-1 * newboundinds[i] - 1], newbounds[i]) ); /*lint !e679*/
432  }
433  else
434  {
435  SCIP_CALL( SCIPchgVarUb(scip, vars[newboundinds[i] - 1], newbounds[i]) );
436  }
437  }
438 
439  SCIPfreeBufferArray(scip, &newboundinds);
440  SCIPfreeBufferArray(scip, &newbounds);
441 
442  return SCIP_OKAY;
443 
444 #else
445  *result = SCIP_DIDNOTRUN;
446 
447  return SCIP_OKAY;
448 #endif
449 }
450 
451 
452 
453 /*
454  * propagator specific interface methods
455  */
456 
459  SCIP* scip
460  )
461 {
462  SCIP_PROPDATA* propdata;
463  SCIP_PROP* prop;
464 
465  /* create SdpObbt propagator data */
466  propdata = NULL;
467  SCIP_CALL( SCIPallocMemory(scip, &propdata) );
468  propdata->lastnode = -1;
469 
470  /* include propagator */
471  /* use SCIPincludePropBasic() plus setter functions if you want to set callbacks one-by-one and your code should
472  * compile independent of new callbacks being added in future SCIP versions
473  */
474  SCIP_CALL( SCIPincludePropBasic(scip, &prop, PROP_NAME, PROP_DESC, PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING,
475  propExecSdpObbt, propdata) );
476 
477  assert(prop != NULL);
478 
479  /* set optional callbacks via setter functions */
480  SCIP_CALL( SCIPsetPropCopy(scip, prop, propCopySdpObbt) );
481  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSdpObbt) );
482  SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSdpObbt) );
483  SCIP_CALL( SCIPsetPropInitsol(scip, prop, propInitsolSdpObbt) );
484 
485  /* add SdpObbt propagator parameters */
486  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/propbin",
487  "Should optimization-based bound tightening be performed for binary variables?",
488  &propdata->propbin, TRUE, DEFAULT_PROPBIN, NULL, NULL) );
489 
490  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/propcont",
491  "Should optimization-based bound tightening be performed for continuous variables?",
492  &propdata->propcont, TRUE, DEFAULT_PROPCONT, NULL, NULL) );
493 
494  return SCIP_OKAY;
495 }
#define PROP_TIMING
Definition: prop_sdpobbt.c:58
SCIP_RETCODE SCIPincludePropSdpObbt(SCIP *scip)
Definition: prop_sdpobbt.c:458
#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:5150
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:5233
#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:5250