SCIP-SDP  4.0.0
prop_sdpredcost.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 
39 /*#define SCIP_DEBUG*/
40 
41 /* TODO: might want to add sdpsolvergaptol to parameters and only cut off / change bounds if values differ by more than gaptol, since
42  * the primal variable values may only be exact up to gaptol
43  */
44 
45 #include "prop_sdpredcost.h"
46 #include "scip/def.h" /* for SCIP_Real, _Bool, ... */
47 #include "relax_sdp.h" /* to get relaxation value */
48 #include "sdpi/sdpi.h" /* to get values of primal variables */
49 
50 #include <string.h>
51 #include <assert.h> /*lint !e451*/
52 
53 /* turn off lint warnings for whole file: */
54 /*lint --e{788,818}*/
55 
60 #define PROP_NAME "sdpredcost"
61 #define PROP_DESC "sdp reduced cost strengthening propagator"
62 #define PROP_TIMING SCIP_PROPTIMING_AFTERLPLOOP
63 #define PROP_PRIORITY +1000000
64 #define PROP_FREQ 1
65 #define PROP_DELAY FALSE
67 #define DEFAULT_SDPRCBIN TRUE
68 #define DEFAULT_SDPRCINTCONT TRUE
73 struct SCIP_PropData
74 {
75  /* these could also be freshly allocated for each node, but allocating them only once saves time */
76  SCIP_Real* lbvarvals;
77  SCIP_Real* ubvarvals;
78  int nvars;
79  SCIP_Bool forbins;
80  SCIP_Bool forintconts;
81 };
82 
89 static
91  SCIP* scip,
92  SCIP_VAR* var,
93  SCIP_Real primallbval,
94  SCIP_Real primalubval,
95  SCIP_Real cutoffbound,
96  SCIP_Real relaxval,
97  SCIP_RESULT* result
98  )
99 {
100  assert( scip != NULL );
101  assert( var != NULL );
102  assert( result != NULL );
103  assert( ! SCIPisInfinity(scip, REALABS(cutoffbound)) );
104  assert( ! SCIPisInfinity(scip, REALABS(relaxval)) );
105  assert( SCIPisFeasGE(scip, cutoffbound, relaxval) );
106 
107  *result = SCIP_DIDNOTFIND;
108 
109  /* skip fixed binary variables */
110  if ( SCIPvarGetLbLocal(var) > 0.5 || SCIPvarGetUbLocal(var) < 0.5 )
111  return SCIP_OKAY;
112 
113  /* check if variable can be fixed to zero */
114  if ( SCIPisGT(scip, primallbval, cutoffbound - relaxval) )
115  {
116  SCIPdebugMsg(scip, "Variable <%s> fixed to zero.\n", SCIPvarGetName(var));
117  SCIP_CALL( SCIPchgVarUb(scip, var, 0.0) );
118  *result = SCIP_REDUCEDDOM;
119 
120  /* check if we would also have to fix the variable to one, in that case, we can cut the node off, as there can't be a new optimal solution */
121  if ( SCIPisGT(scip, primalubval, cutoffbound - relaxval) )
122  *result = SCIP_CUTOFF;
123 
124  return SCIP_OKAY;
125  }
126 
127  /* check if variable can be fixed to one */
128  if ( SCIPisGT(scip, primalubval, cutoffbound - relaxval) )
129  {
130  SCIPdebugMsg(scip, "Variable <%s> fixed to one.\n", SCIPvarGetName(var));
131  SCIP_CALL( SCIPchgVarLb(scip, var, 1.0) );
132  *result = SCIP_REDUCEDDOM;
133  return SCIP_OKAY;
134  }
135 
136  return SCIP_OKAY;
137 }
138 
150 static
152  SCIP* scip,
153  SCIP_VAR* var,
154  SCIP_Real primallbval,
155  SCIP_Real primalubval,
156  SCIP_Real cutoffbound,
157  SCIP_Real relaxval,
158  SCIP_RESULT* result
159  )
160 {
161  SCIP_Real oldlb;
162  SCIP_Real oldub;
163  SCIP_Real newlb;
164  SCIP_Real newub;
165 
166  assert( scip != NULL );
167  assert( var != NULL );
168  assert( result != NULL );
169  assert( ! SCIPisInfinity(scip, REALABS(cutoffbound)) );
170  assert( ! SCIPisInfinity(scip, REALABS(relaxval)) );
171  assert( SCIPisFeasGE(scip, cutoffbound, relaxval) );
172 
173  *result = SCIP_DIDNOTFIND;
174 
175  oldlb = SCIPvarGetLbLocal(var);
176  oldub = SCIPvarGetUbLocal(var);
177  assert( SCIPisLE(scip, oldlb, oldub) );
178 
179  /* avoid fixed variables */
180  if ( SCIPisFeasEQ(scip, oldlb, oldub) )
181  return SCIP_OKAY;
182 
183  /* Compute new lower bound. Avoid division by 0 (checking > 0 is sufficient, as the variables are non-negative). The
184  * new lower bound is only valid if the old upper bound is finite. */
185  if ( ! SCIPisInfinity(scip, oldub) && SCIPisFeasPositive(scip, primalubval) )
186  {
187  newlb = oldub - (cutoffbound - relaxval) / primalubval;
188 
189  /* take the better value */
190  newlb = MAX(oldlb, newlb);
191  assert( ! SCIPisInfinity(scip, newlb) );
192  }
193  else
194  newlb = oldlb;
195 
196  /* Compute new upper bound. Avoid division by 0 (checking > 0 is sufficient, as the variables are non-negative). The
197  * new upper bound is only valid if the old lower bound is finite. */
198  if ( ! SCIPisInfinity(scip, -oldlb) && SCIPisFeasPositive(scip, primallbval) )
199  {
200  newub = oldlb + (cutoffbound - relaxval) / primallbval;
201 
202  /* take the better value */
203  newub = MIN(oldub, newub);
204  assert( ! SCIPisInfinity(scip, -newub) );
205  }
206  else
207  newub = oldub;
208 
209  /* check for infeasibility */
210  if ( SCIPisFeasGT(scip, newlb, newub) )
211  {
212  SCIPdebugMsg(scip, "Node is infeasible. New bounds for variable <%s>: [%g, %g]!\n", SCIPvarGetName(var), newlb, newub);
213  *result = SCIP_CUTOFF;
214  return SCIP_OKAY;
215  }
216 
217  /* try to tighten lower bound */
218  if ( ! SCIPisInfinity(scip, -newlb) && SCIPisFeasGT(scip, newlb, oldlb) )
219  {
220  SCIPdebugMsg(scip, "Changing lower bound of variable <%s> from %g to %g.\n", SCIPvarGetName(var), oldlb, newlb);
221  SCIP_CALL( SCIPchgVarLb(scip, var, newlb) );
222  *result = SCIP_REDUCEDDOM;
223  }
224 
225  /* try to tighten upper bound */
226  if ( ! SCIPisInfinity(scip, newub) && SCIPisFeasLT(scip, newub, oldub) )
227  {
228  SCIPdebugMsg(scip, "Changing upper bound of variable <%s> from %g to %g.\n", SCIPvarGetName(var), oldub, newub);
229  SCIP_CALL( SCIPchgVarUb(scip, var, newub) );
230  *result = SCIP_REDUCEDDOM;
231  }
232 
233  return SCIP_OKAY;
234 }
235 
237 static
238 SCIP_DECL_PROPEXEC(propExecSdpredcost)
239 {/*lint --e{715}*/
240  int v;
241  int nvars;
242  SCIP_VAR** vars;
243  SCIP_RELAX* relax;
244  SCIP_RESULT varresult;
245  SCIP_Real cutoffbound;
246  SCIP_Real relaxval;
247  SCIP_Bool sdpsolved;
248  SCIP_PROPDATA* propdata;
249  SCIP_Bool success;
250 
251  assert( scip != NULL );
252  assert( prop != NULL );
253  assert( result != NULL );
254 
255  *result = SCIP_DIDNOTRUN;
256 
257  /* do not run if propagation w.r.t. objective is not allowed */
258  if( ! SCIPallowWeakDualReds(scip) )
259  return SCIP_OKAY;
260 
261  /* we can't run before the relaxator is properly initialized */
262  if ( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING )
263  return SCIP_OKAY;
264 
265  cutoffbound = SCIPgetCutoffbound(scip);
266  if ( SCIPisInfinity(scip, cutoffbound) )
267  return SCIP_OKAY;
268 
269  relax = SCIPfindRelax(scip, "SDP"); /* get SDP relaxation handler */
270  assert( relax != NULL );
271 
272  /* we can only propagate if the SDP was solved in its original formulation */
273  if ( ! SCIPrelaxSdpSolvedOrig(relax) )
274  return SCIP_OKAY;
275 
276  /* we can only propagate for the last node for which the SDP was solved */
277  if ( SCIPrelaxSdpGetSdpNode(relax) != SCIPnodeGetNumber(SCIPgetCurrentNode(scip)) )
278  return SCIP_OKAY;
279 
280  SCIP_CALL( SCIPrelaxSdpRelaxVal(relax, &sdpsolved, &relaxval) );
281  if ( ! sdpsolved )
282  {
283  SCIPdebugMsg(scip, "Do not run propagation because SDP-relaxation was not solved properly.\n");
284  return SCIP_OKAY;
285  }
286 
287  /* avoid infinite relaxation objective values */
288  if ( SCIPisInfinity(scip, REALABS(relaxval)) )
289  return SCIP_OKAY;
290 
291  propdata = SCIPpropGetData(prop);
292  assert( propdata != NULL );
293 
294  SCIPdebugMsg(scip, "Running propExecSdpredcost ...\n");
295  *result = SCIP_DIDNOTFIND;
296 
297  nvars = SCIPgetNVars(scip);
298  vars = SCIPgetVars(scip);
299 
300  /* make sure that data arrays have the right size */
301  if ( nvars != propdata->nvars )
302  {
303  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->lbvarvals, propdata->nvars, nvars) );
304  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->ubvarvals, propdata->nvars, nvars) );
305  propdata->nvars = nvars;
306  }
307  SCIP_CALL( SCIPrelaxSdpGetPrimalBoundVars(scip, relax, vars, nvars, propdata->lbvarvals, propdata->ubvarvals, &success) );
308  if ( ! success )
309  return SCIP_OKAY;
310 
311  for (v = 0; v < nvars; v++)
312  {
313  if ( SCIPvarIsBinary(vars[v]) && propdata->forbins )
314  {
315  SCIP_CALL( sdpRedcostFixingBinary(scip, vars[v], propdata->lbvarvals[v], propdata->ubvarvals[v], cutoffbound, relaxval, &varresult) );
316 
317  if ( varresult == SCIP_REDUCEDDOM )
318  *result = SCIP_REDUCEDDOM;
319  }
320  else if ( ! SCIPvarIsBinary(vars[v]) && propdata->forintconts )
321  {
322  SCIP_CALL( sdpRedcostFixingIntCont(scip, vars[v], propdata->lbvarvals[v], propdata->ubvarvals[v], cutoffbound, relaxval, &varresult) );
323 
324  if ( varresult == SCIP_REDUCEDDOM )
325  *result = SCIP_REDUCEDDOM;
326  }
327  }
328 
329  return SCIP_OKAY;
330 }
331 
333 static
334 SCIP_DECL_PROPFREE(propFreeSdpredcost)
335 {/*lint --e{715}*/
336  SCIP_PROPDATA* propdata;
337 
338  propdata = SCIPpropGetData(prop);
339  assert( propdata != NULL );
340  SCIPfreeBlockMemory(scip, &propdata);
341 
342  SCIPpropSetData(prop, NULL);
343 
344  return SCIP_OKAY;
345 }
346 
348 static
349 SCIP_DECL_PROPINITSOL(propInitsolSdpredcost)
350 {
351  SCIP_PROPDATA* propdata;
352 
353  propdata = SCIPpropGetData(prop);
354  assert(propdata != NULL);
355 
356  propdata->nvars = SCIPgetNVars(scip);
357  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->lbvarvals), propdata->nvars) );
358  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->ubvarvals), propdata->nvars) );
359 
360  return SCIP_OKAY;
361 }
362 
364 static
365 SCIP_DECL_PROPEXITSOL(propExitsolSdpredcost)
366 {
367  SCIP_PROPDATA* propdata;
368 
369  propdata = SCIPpropGetData(prop);
370  assert( propdata != NULL );
371 
372  SCIPfreeBlockMemoryArrayNull(scip, &(propdata->lbvarvals), propdata->nvars);
373  SCIPfreeBlockMemoryArrayNull(scip, &(propdata->ubvarvals), propdata->nvars);
374 
375  return SCIP_OKAY;
376 }
377 
379 static
380 SCIP_DECL_PROPCOPY(propCopySdpredcost)
381 {
382  assert( scip != NULL );
383  assert( prop != NULL );
384  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
385 
386  /* call inclusion method of constraint handler */
387  SCIP_CALL( SCIPincludePropSdpredcost(scip) );
388 
389  return SCIP_OKAY;
390 }
391 
394  SCIP* scip
395  )
396 {
397  SCIP_PROPDATA* propdata = NULL;
398  SCIP_PROP* prop;
399 
400  /* create propagator data */
401  SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
402  propdata->nvars = 0;
403  propdata->lbvarvals = NULL;
404  propdata->ubvarvals = NULL;
405 
406  /* include propagator */
407  SCIP_CALL( SCIPincludePropBasic(scip, &prop, PROP_NAME, PROP_DESC, PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING,
408  propExecSdpredcost, propdata) );
409  assert( prop != NULL );
410 
411  /* set optional callbacks via setter functions */
412  SCIP_CALL( SCIPsetPropCopy(scip, prop, propCopySdpredcost) );
413  SCIP_CALL( SCIPsetPropInitsol(scip, prop, propInitsolSdpredcost) );
414  SCIP_CALL( SCIPsetPropExitsol(scip, prop, propExitsolSdpredcost) );
415  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSdpredcost) );
416 
417  /* add additional parameters */
418  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/forbins", "Should SDP reduced cost fixing be executed for binary variables?",
419  &(propdata->forbins), TRUE, DEFAULT_SDPRCBIN, NULL, NULL) );
420 
421  SCIP_CALL( SCIPaddBoolParam(scip, "propagating/" PROP_NAME "/forintconts", "Should SDP reduced cost fixing be executed for integer and continuous variables?",
422  &(propdata->forintconts), TRUE, DEFAULT_SDPRCINTCONT, NULL, NULL) );
423 
424  return SCIP_OKAY;
425 }
#define DEFAULT_SDPRCBIN
static SCIP_DECL_PROPFREE(propFreeSdpredcost)
static SCIP_DECL_PROPINITSOL(propInitsolSdpredcost)
#define PROP_DELAY
#define PROP_TIMING
#define PROP_FREQ
SDP-relaxator.
SCIP_RETCODE SCIPrelaxSdpRelaxVal(SCIP_RELAX *relax, SCIP_Bool *success, SCIP_Real *objval)
Definition: relax_sdp.c:5496
General interface methods for SDP-preprocessing (mainly fixing variables and removing empty rows/cols...
static SCIP_DECL_PROPEXEC(propExecSdpredcost)
SCIP_RETCODE SCIPrelaxSdpGetPrimalBoundVars(SCIP *scip, SCIP_RELAX *relax, SCIP_VAR **vars, int nvars, SCIP_Real *lbvars, SCIP_Real *ubvars, SCIP_Bool *success)
Definition: relax_sdp.c:5440
#define PROP_PRIORITY
static SCIP_DECL_PROPCOPY(propCopySdpredcost)
SCIP_Bool SCIPrelaxSdpSolvedOrig(SCIP_RELAX *relax)
Definition: relax_sdp.c:5562
reduced cost / dual fixing for SDPs
static SCIP_RETCODE sdpRedcostFixingIntCont(SCIP *scip, SCIP_VAR *var, SCIP_Real primallbval, SCIP_Real primalubval, SCIP_Real cutoffbound, SCIP_Real relaxval, SCIP_RESULT *result)
#define PROP_NAME
#define PROP_DESC
SCIP_Longint SCIPrelaxSdpGetSdpNode(SCIP_RELAX *relax)
Definition: relax_sdp.c:5551
static SCIP_DECL_PROPEXITSOL(propExitsolSdpredcost)
static SCIP_RETCODE sdpRedcostFixingBinary(SCIP *scip, SCIP_VAR *var, SCIP_Real primallbval, SCIP_Real primalubval, SCIP_Real cutoffbound, SCIP_Real relaxval, SCIP_RESULT *result)
SCIP_RETCODE SCIPincludePropSdpredcost(SCIP *scip)
#define DEFAULT_SDPRCINTCONT