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