Scippy

SCIP

Solving Constraint Integer Programs

sepa_impliedbounds.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file sepa_impliedbounds.c
17  * @brief implied bounds separator
18  * @author Kati Wolter
19  * @author Tobias Achterberg
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include <assert.h>
25 #include <string.h>
26 
28 #include "scip/pub_misc.h"
29 
30 
31 #define SEPA_NAME "impliedbounds"
32 #define SEPA_DESC "implied bounds separator"
33 #define SEPA_PRIORITY -50
34 #define SEPA_FREQ 0
35 #define SEPA_MAXBOUNDDIST 0.0
36 #define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
37 #define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
38 
39 #define RELCUTCOEFMAXRANGE 1.0 /**< maximal allowed range of cut coefficients, relative to 1/feastol */
40 
41 
42 /*
43  * Local methods
44  */
45 
46 /* adds given cut with two variables, if it is violated */
47 static
49  SCIP* scip, /**< SCIP data structure */
50  SCIP_SEPA* sepa, /**< separator */
51  SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
52  SCIP_Real val1, /**< given coefficient of first variable */
53  SCIP_VAR* var1, /**< given first variable */
54  SCIP_Real solval1, /**< current LP solution value of first variable */
55  SCIP_Real val2, /**< given coefficient of second variable */
56  SCIP_VAR* var2, /**< given second variable */
57  SCIP_Real solval2, /**< current LP solution value of second variable */
58  SCIP_Real rhs, /**< given right hand side of the cut to add */
59  SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
60  int* ncuts /**< pointer to update number of cuts added */
61  )
62 {
63  SCIP_Real activity;
64 
65  assert(ncuts != NULL);
66  assert(cutoff != NULL);
67  *cutoff = FALSE;
68 
69  /* calculate activity of cut */
70  activity = val1 * solval1 + val2 * solval2;
71  /*debugMessage(" -> %g<%s>[%g] + %g<%s>[%g] <= %g (act: %g)\n",
72  val1, SCIPvarGetName(var1), solval1, val2, SCIPvarGetName(var2), solval2, rhs, activity);*/
73 
74  /* check, if cut is violated */
75  if( SCIPisEfficacious(scip, activity - rhs) )
76  {
77  SCIP_ROW* cut;
78  char cutname[SCIP_MAXSTRLEN];
79 
80  /* create cut */
81  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "implbd%d_%d", SCIPgetNLPs(scip), *ncuts);
82  SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), rhs, FALSE, FALSE, TRUE) );
83  SCIP_CALL( SCIPcacheRowExtensions(scip, cut) );
84  SCIP_CALL( SCIPaddVarToRow(scip, cut, var1, val1) );
85  SCIP_CALL( SCIPaddVarToRow(scip, cut, var2, val2) );
86  SCIP_CALL( SCIPflushRowExtensions(scip, cut) );
87  /* set cut rank: for implied bounds we always set to 1 */
88  SCIProwChgRank(cut, 1);
89 
90 #ifdef SCIP_DEBUG
91  SCIPdebugMessage(" -> found cut (activity = %g): ", activity);
92  SCIP_CALL( SCIPprintRow(scip, cut, NULL) );
93 #endif
94 
95  /* add cut */
96  SCIP_CALL( SCIPaddCut(scip, sol, cut, FALSE, cutoff) );
97  if ( ! (*cutoff) )
98  {
99  SCIP_CALL( SCIPaddPoolCut(scip, cut) );
100  (*ncuts)++;
101  }
102 
103  /* release cut */
104  SCIP_CALL( SCIPreleaseRow(scip, &cut) );
105  }
106 
107  return SCIP_OKAY;
108 }
109 
110 /** searches and adds implied bound cuts that are violated by the given solution value array */
111 static
113  SCIP* scip, /**< SCIP data structure */
114  SCIP_SEPA* sepa, /**< separator */
115  SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
116  SCIP_Real* solvals, /**< array with solution values of all problem variables */
117  SCIP_VAR** fracvars, /**< array of fractional variables */
118  SCIP_Real* fracvals, /**< solution values of fractional variables */
119  int nfracs, /**< number of fractional variables */
120  SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
121  int* ncuts /**< pointer to store the number of generated cuts */
122  )
123 {
124  int i;
125 
126  assert(solvals != NULL);
127  assert(fracvars != NULL || nfracs == 0);
128  assert(fracvals != NULL || nfracs == 0);
129  assert(cutoff != NULL);
130  assert(ncuts != NULL);
131 
132  *cutoff = FALSE;
133  *ncuts = 0;
134 
135  SCIPdebugMessage("searching for implied bound cuts\n");
136 
137  /* search binary variables for violated implications */
138  for( i = 0; i < nfracs; i++ )
139  {
140  SCIP_BOUNDTYPE* impltypes;
141  SCIP_Real* implbounds;
142  SCIP_VAR** implvars;
143  int nimpl;
144  int j;
145 
146  assert(fracvars != NULL);
147  assert(fracvals != NULL);
148 
149  /* only process binary variables */
150  if( SCIPvarGetType(fracvars[i]) != SCIP_VARTYPE_BINARY )
151  continue;
152 
153  /* get implications of x == 1 */
154  nimpl = SCIPvarGetNImpls(fracvars[i], TRUE);
155  implvars = SCIPvarGetImplVars(fracvars[i], TRUE);
156  impltypes = SCIPvarGetImplTypes(fracvars[i], TRUE);
157  implbounds = SCIPvarGetImplBounds(fracvars[i], TRUE);
158 
159  /*debugMessage("%d implications for <%s>[%g] == 1\n", nimpl, SCIPvarGetName(fracvars[i]), fracvals[i]);*/
160 
161  /* try to add cuts for implications of x == 1
162  * x == 1 -> y <= p: y <= ub + x * (p - ub) <==> y + (ub - p) * x <= ub
163  * x == 1 -> y >= p: y >= lb + x * (p - lb) <==> -y + (p - lb) * x <= -lb
164  * with lb (ub) global lower (upper) bound of y
165  */
166  for( j = 0; j < nimpl; j++ )
167  {
168  SCIP_Real solval;
169 
170  assert(implvars != NULL);
171  assert(impltypes != NULL);
172  assert(implbounds != NULL);
173 
174  /* consider only implications with active implvar */
175  if( SCIPvarGetProbindex(implvars[j]) < 0 )
176  continue;
177 
178  solval = solvals[SCIPvarGetProbindex(implvars[j])];
179  if( impltypes[j] == SCIP_BOUNDTYPE_UPPER )
180  {
181  SCIP_Real ub;
182 
183  /* implication x == 1 -> y <= p */
184  ub = SCIPvarGetUbGlobal(implvars[j]);
185 
186  /* consider only nonredundant and numerical harmless implications */
187  if( SCIPisLE(scip, implbounds[j], ub) && (ub - implbounds[j]) * SCIPfeastol(scip) <= RELCUTCOEFMAXRANGE )
188  {
189  /* add cut if violated */
190  SCIP_CALL( addCut(scip, sepa, sol, 1.0, implvars[j], solval, (ub - implbounds[j]), fracvars[i], fracvals[i],
191  ub, cutoff, ncuts) );
192  if ( *cutoff )
193  return SCIP_OKAY;
194  }
195  }
196  else
197  {
198  SCIP_Real lb;
199 
200  /* implication x == 1 -> y >= p */
201  lb = SCIPvarGetLbGlobal(implvars[j]);
202  assert(impltypes[j] == SCIP_BOUNDTYPE_LOWER);
203 
204  /* consider only nonredundant and numerical harmless implications */
205  if( SCIPisGE(scip, implbounds[j], lb) && (implbounds[j] - lb) * SCIPfeastol(scip) <= RELCUTCOEFMAXRANGE )
206  {
207  /* add cut if violated */
208  SCIP_CALL( addCut(scip, sepa, sol, -1.0, implvars[j], solval, (implbounds[j] - lb), fracvars[i], fracvals[i],
209  -lb, cutoff, ncuts) );
210  if ( *cutoff )
211  return SCIP_OKAY;
212  }
213  }
214  }
215 
216  /* get implications of x == 0 */
217  nimpl = SCIPvarGetNImpls(fracvars[i], FALSE);
218  implvars = SCIPvarGetImplVars(fracvars[i], FALSE);
219  impltypes = SCIPvarGetImplTypes(fracvars[i], FALSE);
220  implbounds = SCIPvarGetImplBounds(fracvars[i], FALSE);
221 
222  /*debugMessage("%d implications for <%s>[%g] == 0\n", nimpl, SCIPvarGetName(fracvars[i]), fracvals[i]);*/
223 
224  /* try to add cuts for implications of x == 0
225  * x == 0 -> y <= p: y <= p + x * (ub - p) <==> y + (p - ub) * x <= p
226  * x == 0 -> y >= p: y >= p + x * (lb - p) <==> -y + (lb - p) * x <= -p
227  * with lb (ub) global lower (upper) bound of y
228  */
229  for( j = 0; j < nimpl; j++ )
230  {
231  SCIP_Real solval;
232 
233  /* consider only implications with active implvar */
234  if( SCIPvarGetProbindex(implvars[j]) < 0 )
235  continue;
236 
237  solval = solvals[SCIPvarGetProbindex(implvars[j])];
238  if( impltypes[j] == SCIP_BOUNDTYPE_UPPER )
239  {
240  SCIP_Real ub;
241 
242  /* implication x == 0 -> y <= p */
243  ub = SCIPvarGetUbGlobal(implvars[j]);
244 
245  /* consider only nonredundant and numerical harmless implications */
246  if( SCIPisLE(scip, implbounds[j], ub) && (ub - implbounds[j]) * SCIPfeastol(scip) < RELCUTCOEFMAXRANGE )
247  {
248  /* add cut if violated */
249  SCIP_CALL( addCut(scip, sepa, sol, 1.0, implvars[j], solval, (implbounds[j] - ub), fracvars[i], fracvals[i],
250  implbounds[j], cutoff, ncuts) );
251  if ( *cutoff )
252  return SCIP_OKAY;
253  }
254  }
255  else
256  {
257  SCIP_Real lb;
258 
259  /* implication x == 0 -> y >= p */
260  lb = SCIPvarGetLbGlobal(implvars[j]);
261  assert(impltypes[j] == SCIP_BOUNDTYPE_LOWER);
262 
263  /* consider only nonredundant and numerical harmless implications */
264  if( SCIPisGE(scip, implbounds[j], lb) && (implbounds[j] - lb) * SCIPfeastol(scip) < RELCUTCOEFMAXRANGE )
265  {
266  /* add cut if violated */
267  SCIP_CALL( addCut(scip, sepa, sol, -1.0, implvars[j], solval, (lb - implbounds[j]), fracvars[i], fracvals[i],
268  -implbounds[j], cutoff, ncuts) );
269  if ( *cutoff )
270  return SCIP_OKAY;
271  }
272  }
273  }
274  }
275 
276  return SCIP_OKAY;
277 }
278 
279 
280 /*
281  * Callback methods of separator
282  */
283 
284 /** copy method for separator plugins (called when SCIP copies plugins) */
285 static
286 SCIP_DECL_SEPACOPY(sepaCopyImpliedbounds)
287 { /*lint --e{715}*/
288  assert(scip != NULL);
289  assert(sepa != NULL);
290  assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0);
291 
292  /* call inclusion method of constraint handler */
294 
295  return SCIP_OKAY;
296 }
297 
298 
299 /** LP solution separation method of separator */
300 static
301 SCIP_DECL_SEPAEXECLP(sepaExeclpImpliedbounds)
302 { /*lint --e{715}*/
303  SCIP_VAR** vars;
304  SCIP_VAR** fracvars;
305  SCIP_Real* solvals;
306  SCIP_Real* fracvals;
307  SCIP_Bool cutoff;
308  int nvars;
309  int nbinvars;
310  int nfracs;
311  int ncuts;
312 
313  assert(sepa != NULL);
314  assert(scip != NULL);
315 
316  *result = SCIP_DIDNOTRUN;
317 
318  /* gets active problem variables */
319  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) );
320  if( nbinvars == 0 )
321  return SCIP_OKAY;
322 
323  /* get fractional problem variables */
324  /* todo try out also separating fractional implicit integer variables */
325  SCIP_CALL( SCIPgetLPBranchCands(scip, &fracvars, &fracvals, NULL, &nfracs, NULL, NULL) );
326  if( nfracs == 0 )
327  return SCIP_OKAY;
328 
329  /* get solution values for all variables */
330  SCIP_CALL( SCIPallocBufferArray(scip, &solvals, nvars) );
331  SCIP_CALL( SCIPgetVarSols(scip, nvars, vars, solvals) );
332 
333  /* call the cut separation */
334  SCIP_CALL( separateCuts(scip, sepa, NULL, solvals, fracvars, fracvals, nfracs, &cutoff, &ncuts) );
335 
336  /* adjust result code */
337  if ( cutoff )
338  *result = SCIP_CUTOFF;
339  else if ( ncuts > 0 )
340  *result = SCIP_SEPARATED;
341  else
342  *result = SCIP_DIDNOTFIND;
343 
344  /* free temporary memory */
345  SCIPfreeBufferArray(scip, &solvals);
346 
347  return SCIP_OKAY;
348 }
349 
350 
351 /** arbitrary primal solution separation method of separator */
352 static
353 SCIP_DECL_SEPAEXECSOL(sepaExecsolImpliedbounds)
354 { /*lint --e{715}*/
355  SCIP_VAR** vars;
356  SCIP_VAR** fracvars;
357  SCIP_Real* solvals;
358  SCIP_Real* fracvals;
359  SCIP_Bool cutoff;
360  int nvars;
361  int nbinvars;
362  int nfracs;
363  int ncuts;
364  int i;
365 
366  assert(sepa != NULL);
367  assert(scip != NULL);
368 
369  *result = SCIP_DIDNOTRUN;
370 
371  /* gets active problem variables */
372  SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) );
373  if( nbinvars == 0 )
374  return SCIP_OKAY;
375 
376  /* get solution values for all variables */
377  SCIP_CALL( SCIPallocBufferArray(scip, &solvals, nvars) );
378  SCIP_CALL( SCIPgetSolVals(scip, sol, nvars, vars, solvals) );
379 
380  /* get binary problem variables that are fractional in given solution */
381  SCIP_CALL( SCIPallocBufferArray(scip, &fracvars, nbinvars) );
382  SCIP_CALL( SCIPallocBufferArray(scip, &fracvals, nbinvars) );
383  nfracs = 0;
384  for( i = 0; i < nbinvars; ++i )
385  {
386  if( !SCIPisFeasIntegral(scip, solvals[i]) )
387  {
388  fracvars[nfracs] = vars[i];
389  fracvals[nfracs] = solvals[i];
390  nfracs++;
391  }
392  }
393 
394  /* call the cut separation */
395  ncuts = 0;
396  cutoff = FALSE;
397 
398  if( nfracs > 0 )
399  {
400  SCIP_CALL( separateCuts(scip, sepa, sol, solvals, fracvars, fracvals, nfracs, &cutoff, &ncuts) );
401  }
402 
403  /* adjust result code */
404  if ( cutoff )
405  *result = SCIP_CUTOFF;
406  else if ( ncuts > 0 )
407  *result = SCIP_SEPARATED;
408  else
409  *result = SCIP_DIDNOTFIND;
410 
411  /* free temporary memory */
412  SCIPfreeBufferArray(scip, &fracvals);
413  SCIPfreeBufferArray(scip, &fracvars);
414  SCIPfreeBufferArray(scip, &solvals);
415 
416  return SCIP_OKAY;
417 }
418 
419 
420 /*
421  * separator specific interface methods
422  */
423 
424 /** creates the impliedbounds separator and includes it in SCIP */
426  SCIP* scip /**< SCIP data structure */
427  )
428 {
429  SCIP_SEPADATA* sepadata;
430  SCIP_SEPA* sepa;
431 
432  /* create impliedbounds separator data */
433  sepadata = NULL;
434 
435  /* include separator */
438  sepaExeclpImpliedbounds, sepaExecsolImpliedbounds,
439  sepadata) );
440 
441  assert(sepa != NULL);
442 
443  /* set non-NULL pointers to callback methods */
444  SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopyImpliedbounds) );
445 
446  return SCIP_OKAY;
447 }
448