Scippy

SCIP

Solving Constraint Integer Programs

prop_rootredcost.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 prop_rootredcost.c
17  * @brief reduced cost strengthening using root node reduced costs and the cutoff bound
18  * @author Tobias Achterberg
19  * @author Stefan Heinz
20  *
21  * This propagator uses the root reduced cost to (globally) propagate against the cutoff bound. The propagator checks if
22  * the variables with non-zero root reduced cost can exceed the cutoff bound. If this is the case the corresponding
23  * bound can be tightened.
24  *
25  * The propagate is performed during the search any time a new cutoff bound (primal solution) is found.
26  *
27  * @todo do not sort the variables; just store the cutoff bound which leads to a fixing. If that appears loop over all
28  * variables and fix and store the next cutoff bound which leads to an fixing
29  * @todo resolve the root LP in case of repropagation and update root reduced costs use root LP counter to check if new
30  * best root combinations might be available
31  */
32 
33 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
34 
35 #include <assert.h>
36 #include <string.h>
37 
38 #include "scip/prop_rootredcost.h"
39 
40 /**@name Propagator properties
41  *
42  * @{
43  */
44 
45 #define PROP_NAME "rootredcost"
46 #define PROP_DESC "reduced cost strengthening using root node reduced costs and the cutoff bound"
47 #define PROP_TIMING SCIP_PROPTIMING_BEFORELP | SCIP_PROPTIMING_AFTERLPLOOP
48 #define PROP_PRIORITY +10000000 /**< propagator priority */
49 #define PROP_FREQ 1 /**< propagator frequency */
50 #define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */
51 
52 /**@} */
53 
54 
55 /*
56  * Data structures
57  */
58 
59 /** propagator data */
60 struct SCIP_PropData
61 {
62  SCIP_VAR** redcostvars; /**< variables with non-zero root reduced cost */
63  SCIP_Real lastcutoffbound; /**< cutoff bound for which the root reduced costs were already processed */
64  int nredcostvars; /**< number of variables with non-zero root reduced cost */
65  int nredcostbinvars; /**< number of binary variables with non-zero root reduced cost */
66  int glbfirstnonfixed; /**< index of first globally non-fixed binary variable */
67  SCIP_Bool initialized; /**< is the propagator data initialized */
68 };
69 
70 
71 /**@name Local methods
72  *
73  * @{
74  */
75 
76 /** reset structure memember of propagator data structure */
77 static
79  SCIP* scip, /**< SCIP data structure */
80  SCIP_PROPDATA* propdata /**< propagator data to reset */
81  )
82 {
83  propdata->redcostvars = NULL;
84  propdata->lastcutoffbound = SCIP_INVALID;
85  propdata->nredcostvars = 0;
86  propdata->nredcostbinvars = 0;
87  propdata->glbfirstnonfixed = 0;
88  propdata->initialized = FALSE;
89 }
90 
91 /** compare variables with non-zero reduced cost w.r.t.
92  * (i) the cutoff bound which would lead to a fixing
93  * (ii) variable problem index;
94  */
95 static
96 SCIP_DECL_SORTPTRCOMP(varCompRedcost)
97 {
98  SCIP_VAR* var1 = (SCIP_VAR*)elem1;
99  SCIP_VAR* var2 = (SCIP_VAR*)elem2;
100  SCIP_Real key1;
101  SCIP_Real key2;
102 
103  assert(SCIPvarIsBinary(var1));
104  assert(SCIPvarGetBestRootRedcost(var1) != 0.0);
105 
106  assert(SCIPvarIsBinary(var2));
107  assert(SCIPvarGetBestRootRedcost(var2) != 0.0);
108 
109  /* collect sorting key for both variables */
112 
113  if( key1 < key2 )
114  return -1;
115  else if( key1 > key2 )
116  return +1;
117 
118  /* second criteria use the problem index
119  *
120  * @note The problem index is unique. That means the resulting sorting is unique.
121  */
122  return SCIPvarCompare(var1, var2);
123 }
124 
125 /** create propagator data structure */
126 static
128  SCIP* scip, /**< SCIP data structure */
129  SCIP_PROPDATA** propdata /**< pointer to store the created propagator data */
130  )
131 {
132  SCIP_CALL( SCIPallocMemory(scip, propdata) );
133 
134  propdataReset(scip, *propdata);
135 
136  return SCIP_OKAY;
137 }
138 
139 /** counts the number of variables with non-zero root reduced cost */
140 static
142  SCIP* scip, /**< SCIP data structure */
143  SCIP_VAR** vars, /**< variable array */
144  int nvars /**< number of variables */
145  )
146 {
147  int count;
148  int v;
149 
150  count = 0;
151 
152  /* count number of variables with non-zero root reduced cost */
153  for( v = 0; v < nvars; ++v )
154  {
155  SCIP_Real redcost;
156 
157  assert(vars[v] != NULL);
158 
159  redcost = SCIPvarGetBestRootRedcost(vars[v]);
160  if( !SCIPisFeasZero(scip, redcost) )
161  count++;
162  }
163 
164  return count;
165 }
166 
167 /** free propagator data */
168 static
170  SCIP* scip, /**< SCIP data structure */
171  SCIP_PROPDATA* propdata /**< propagator data */
172  )
173 {
174  int v;
175 
176  /* release all variables */
177  for( v = 0; v < propdata->nredcostvars; ++v )
178  {
179  SCIP_CALL( SCIPreleaseVar(scip, &propdata->redcostvars[v]) );
180  }
181 
182  /* free memory for non-zero reduced cost variables */
183  SCIPfreeMemoryArrayNull(scip, &propdata->redcostvars);
184 
185  propdataReset(scip, propdata);
186 
187  return SCIP_OKAY;
188 }
189 
190 /** initializate the propagator */
191 static
193  SCIP* scip, /**< SCIP data structure */
194  SCIP_PROPDATA* propdata /**< propagator data */
195  )
196 {
197  SCIP_VAR** vars;
198  int nvars;
199  int nbinvars;
200  int nredcostvars;
201  int nredcostbinvars;
202  int v;
203 
204  assert(scip != NULL);
205  assert(propdata != NULL);
206 
207  /* check if the propagator data structure is already initialized */
208  if( propdata->initialized )
209  return SCIP_OKAY;
210 
211  /* get problem variables */
212  vars = SCIPgetVars(scip);
213  nvars = SCIPgetNVars(scip);
214  nbinvars = SCIPgetNBinVars(scip);
215 
216  /* count binary variables with non-zero root reduced cost */
217  nredcostbinvars = countNonZeroRootRedcostVars(scip, vars, nbinvars);
218  SCIPdebugMessage("There are %d (poor) binary variables with non-zero root reduced cost <%s>.\n", nredcostbinvars, SCIPgetProbName(scip));
219 
220  /* count non-binary variables with non-zero root reduced cost */
221  nredcostvars = countNonZeroRootRedcostVars(scip, &vars[nbinvars], nvars - nbinvars);
222 
223  nredcostvars += nredcostbinvars;
224 
225  /* collect the variables with non-zero reduced costs */
226  if( nredcostvars > 0 )
227  {
228  int k;
229 
230  k = 0;
231  SCIP_CALL( SCIPallocMemoryArray(scip, &propdata->redcostvars, nredcostvars) );
232 
233  SCIPdebugMessage("Store non-zero root reduced cost variables at address <%p>.\n", (void*)propdata->redcostvars);
234 
235  for( v = 0; v < nvars; ++v )
236  {
237  SCIP_Real redcost;
238  SCIP_VAR* var;
239 
240  var = vars[v];
241  redcost = SCIPvarGetBestRootRedcost(var);
242 
243  if( SCIPisFeasZero(scip, redcost) )
244  continue;
245 
246  assert(k < nredcostvars);
247 
248  /* check if one of the non-binary variables is implicit binary */
249  if( k >= nredcostbinvars && SCIPvarIsBinary(var) )
250  {
251  /* move the first non-binary variable to end of the array */
252  propdata->redcostvars[k] = propdata->redcostvars[nredcostbinvars];
253 
254  /* place the binary variable at the end of the binary section */
255  propdata->redcostvars[nredcostbinvars] = var;
256  nredcostbinvars++;
257  }
258  else
259  propdata->redcostvars[k] = var;
260 
261  /* captures the variable */
262  SCIP_CALL( SCIPcaptureVar(scip, var) ) ;
263 
264  k++;
265 
266  /* check if already visited all variable with non-zero redcostective coefficient */
267  if( k == nredcostvars )
268  break;
269  }
270 
271  /* sort binary variables with respect to their cutoff bound which would lead to an fixing; this order can be used
272  * to efficiently propagate the binary variables
273  */
274  SCIPsortDownPtr((void**)propdata->redcostvars, varCompRedcost, nredcostbinvars);
275 
276  assert(k == nredcostvars);
277 
278  SCIPdebugMessage("variables with non-zero redcostective coefficient: %d binaries, %d non-binaries\n", nredcostbinvars, nredcostvars - nredcostbinvars);
279  }
280 
281  propdata->nredcostvars = nredcostvars;
282  propdata->nredcostbinvars = nredcostbinvars;
283  propdata->glbfirstnonfixed = 0;
284  propdata->lastcutoffbound = SCIPinfinity(scip);
285  propdata->initialized = TRUE;
286 
287  return SCIP_OKAY;
288 }
289 
290 /** propagates the root reduced cost against the cutoff bound for the given variable */
291 static
293  SCIP* scip, /**< SCIP data structure */
294  SCIP_VAR* var, /**< variable to propagate */
295  SCIP_Real cutoffbound, /**< cutoff bound to use */
296  SCIP_Bool* infeasible, /**< pointer to store whether the new domain is empty */
297  SCIP_Bool* tightened /**< pointer to store if the bound was tightened */
298  )
299 {
300  SCIP_Real rootsol;
301  SCIP_Real rootredcost;
302  SCIP_Real rootlpobjval;
303  SCIP_Real newbd;
304 
305  rootredcost = SCIPvarGetBestRootRedcost(var);
306  assert(rootredcost != SCIP_INVALID); /*lint !e777*/
307  assert(!SCIPisFeasZero(scip, rootredcost));
308 
309  rootsol = SCIPvarGetBestRootSol(var);
310  rootlpobjval = SCIPvarGetBestRootLPObjval(var);
311 
312  /* calculate reduced cost based bound */
313  newbd = rootsol + (cutoffbound - rootlpobjval) / rootredcost;
314 
315  if( SCIPisFeasPositive(scip, rootredcost) )
316  {
317  assert(SCIPisFeasLE(scip, rootsol, SCIPvarGetLbGlobal(var))); /* lb might have been increased in the meantime */
318 
319  /* strengthen upper bound */
320  SCIP_CALL( SCIPtightenVarUbGlobal(scip, var, newbd, FALSE, infeasible, tightened) );
321  }
322  else
323  {
324  assert(SCIPisFeasNegative(scip, rootredcost));
325  assert(SCIPisFeasGE(scip, rootsol, SCIPvarGetUbGlobal(var))); /* ub might have been decreased in the meantime */
326 
327  /* strengthen lower bound */
328  SCIP_CALL( SCIPtightenVarLbGlobal(scip, var, newbd, FALSE, infeasible, tightened) );
329  }
330 
331  return SCIP_OKAY;
332 }
333 
334 /** propagate binary variables with non-zero root reduced cost */
335 static
337  SCIP* scip, /**< SCIP data structure */
338  SCIP_PROPDATA* propdata, /**< propagator data structure */
339  SCIP_Real cutoffbound, /**< cutoff bound to use */
340  int* nchgbds, /**< pointer to store the number of bound changes */
341  SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */
342  )
343 {
344  SCIP_VAR** redcostvars;
345  int v;
346 
347  assert(!(*cutoff));
348 
349  /* the binary variables are stored in the beginning of the variable array; these variables are sorted w.r.t. cutoff
350  * bound which would lead to a fixing; that give us an abort criteria (see below)
351  */
352  redcostvars = propdata->redcostvars;
353  assert(redcostvars != NULL);
354 
355 #ifndef NDEBUG
356  /* check that the binary variables are correct sorted
357  *
358  * @note In case the assertion fails it indicates that a new LP root solving arose after we initialized the
359  * propagator; The new LP solution destroyed the sorting of the binary variables since the reduced cost of the
360  * variables changed. This could lead to potentially miss a domain reductions. Currently, it is not possible to
361  * check if a new LP root changing the root reduced costs. This case, however, should not happen in the current
362  * SCIP version.
363  */
364  for( v = 1; v < propdata->nredcostbinvars; ++v )
365  assert(varCompRedcost(redcostvars[v-1], redcostvars[v]) == 1);
366 
367  /* check that the variables before glbfirstnonfixed are globally fixed */
368  for( v = 0; v < propdata->glbfirstnonfixed; ++v )
369  {
370  SCIP_VAR* var;
371 
372  var = redcostvars[v];
373  assert(var != NULL);
374 
375  assert(SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5);
376  }
377 #endif
378 
379  /* propagate binary variables */
380  for( v = propdata->glbfirstnonfixed; v < propdata->nredcostbinvars; ++v )
381  {
382  SCIP_VAR* var;
383  SCIP_Bool tightened;
384 
385  var = redcostvars[v];
386  assert(var != NULL);
387 
388  /* check if the variables is already globally fixed; if so continue with the next potential candidate */
389  if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5)
390  continue;
391 
392  /* try to tighten the variable bound */
393  SCIP_CALL( propagateRootRedcostVar(scip, var, cutoffbound, cutoff, &tightened) );
394 
395  if( tightened )
396  {
397  /* @note The variable might not be globally fixed right away since this would destroy the local internal data
398  * structure of a search node; the bound change is in that case pending; hence we cannot assert that the
399  * variable is globally fixed
400  */
401  assert(!(*cutoff));
402 
403  SCIPdebugMessage("globally fixed binary variable <%s> [%g,%g] bestroot sol <%g>, redcost <%g>, lpobj <%g>\n",
406 
407  (*nchgbds)++;
408  }
409  else
410  {
411  assert(!tightened);
412 
413  /* The binary variables are sorted in non-increasing manner w.r.t. their cutoff bound which would lead to a
414  * global fixing; That is, abs(rootredcost) + rootlpobjval. Depending on the sign of the reduced cost the
415  * following two cases can arise for binary variables which are not fixed globally yet:
416  *
417  * - redcost > 0 --> newub = 0.0 + (cutoffbound - lpobjval) / redcost --> newub = 0 <=> cutoffbound < redcost + lpobjval = sorting key
418  * - redcost < 0 --> newlb = 1.0 + (cutoffbound - lpobjval) / redcost --> newlb = 1 <=> cutoffbound < -redcost + lpobjval = sorting key
419  *
420  * Due to the order of the binary variables it follows if one binary variable cannot be fixed anymore all the
421  * remaining once can also not be fixed since these have only an smaller or equal cutoff bound which would lead
422  * to global fixing. Hence, we can break that loop.
423  *
424  * Note that variables with non-zero reduced cost are sitting at one of their bound. That is the lower one if
425  * the reduced cost are positive and the upper bound if the reduced cost are negative. For binary variables
426  * that is 0 for the lower bound and 1 for the upper bound.
427  */
428  SCIPdebugMessage("interrupt propagation for binary variables after %d from %d binary variables\n",
429  v, propdata->nredcostbinvars);
430 
431  if( *cutoff )
432  {
433  SCIPdebugMessage("detected cutoff: binary variable <%s> [%g,%g], redcost <%g>, rootsol <%g>, rootlpobjval <%g>\n",
436  }
437 
438  break;
439  }
440  }
441  /* store the index of the variable which is not globally fixed */
442  propdata->glbfirstnonfixed = v;
443 
444 #if 0 /* due to numerics it might be that the abort criteria did not work correctly, because the sorting mechanism may
445  * have evaluated variables with a really small difference in their reduced cost values but with really huge
446  * lpobjval as the same
447  */
448 #ifndef NDEBUG
449  /* check that the abort criteria works; that means none of the remaining binary variables can be fixed */
450  for( ; v < propdata->nredcostbinvars && !(*cutoff); ++v )
451  {
452  SCIP_VAR* var;
453  SCIP_Bool tightened;
454 
455  var = redcostvars[v];
456  assert(var != NULL);
457 
458  /* check if the variables is already globally fixed; if so continue with the potential candidate */
459  if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5)
460  continue;
461 
462  /* try to tighten the variable bound */
463  SCIP_CALL( propagateRootRedcostVar(scip, var, cutoffbound, cutoff, &tightened) );
464  assert(!tightened);
465  assert(!(*cutoff));
466  }
467 #endif
468 #endif
469 
470  return SCIP_OKAY;
471 }
472 
473 /**@} */
474 
475 
476 /**@name Callback methods of propagator
477  *
478  * @{
479  */
480 
481 /** copy method for propagator plugins (called when SCIP copies plugins) */
482 static
483 SCIP_DECL_PROPCOPY(propCopyRootredcost)
484 { /*lint --e{715}*/
485  assert(scip != NULL);
486  assert(prop != NULL);
487  assert(strcmp(SCIPpropGetName(prop), PROP_NAME) == 0);
488 
489  /* call inclusion method of propagator */
491 
492  return SCIP_OKAY;
493 }
494 
495 /** destructor of propagator to free user data (called when SCIP is exiting) */
496 static
497 SCIP_DECL_PROPFREE(propFreeRootredcost)
498 { /*lint --e{715}*/
499  SCIP_PROPDATA* propdata;
500 
501  /* free propagator data */
502  propdata = SCIPpropGetData(prop);
503  assert(propdata != NULL);
504  assert(propdata->redcostvars == NULL);
505 
506  SCIPfreeMemory(scip, &propdata);
507  SCIPpropSetData(prop, NULL);
508 
509  return SCIP_OKAY;
510 }
511 
512 /** solving process deinitialization method of propagator (called before branch and bound process data is freed) */
513 static
514 SCIP_DECL_PROPEXITSOL(propExitsolRootredcost)
515 { /*lint --e{715}*/
516  SCIP_PROPDATA* propdata;
517 
518  propdata = SCIPpropGetData(prop);
519  assert(propdata != NULL);
520 
521  /* reset propagator data structure */
522  SCIP_CALL( propdataExit(scip, propdata) );
523 
524  return SCIP_OKAY;
525 }
526 
527 /** execution method of propagator */
528 static
529 SCIP_DECL_PROPEXEC(propExecRootredcost)
530 { /*lint --e{715}*/
531  SCIP_PROPDATA* propdata;
532  SCIP_VAR** redcostvars;
533  SCIP_Real cutoffbound;
534  SCIP_Real lpobjval;
535  SCIP_Bool cutoff;
536  int nredcostvars;
537  int nchgbds;
538  int v;
539 
540  *result = SCIP_DIDNOTRUN;
541 
542  /* in case we have a zero objective fucntion, we skip the root reduced cost propagator */
543  if( SCIPgetNObjVars(scip) == 0 )
544  return SCIP_OKAY;
545 
546  /* propagator can only be applied during solving stage */
547  if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
548  return SCIP_OKAY;
549 
550  /* the propagator should run in all nodes except the root node; for the root node the poor redcost propagator does
551  * the job already
552  */
553  if( SCIPgetDepth(scip) < 1 )
554  return SCIP_OKAY;
555 
556  /* first check root LP objective value if it exists */
557  lpobjval = SCIPgetLPRootObjval(scip);
558  if( lpobjval == SCIP_INVALID ) /*lint !e777*/
559  return SCIP_OKAY;
560 
561  /* do not run in probing mode since this propagator chnages globally variable bounds */
562  if( SCIPinProbing(scip) )
563  return SCIP_OKAY;
564 
565  /* get propagator data */
566  propdata = SCIPpropGetData(prop);
567  assert(propdata != NULL);
568 
569  /* get current cutoff bound */
570  cutoffbound = SCIPgetCutoffbound(scip);
571 
572  /* reduced cost strengthening can only be applied, if we have a finite upper bound on the LP value */
573  if( SCIPisInfinity(scip, cutoffbound) )
574  return SCIP_OKAY;
575 
576  /* initialize propagator data structure */
577  SCIP_CALL( propdataInit(scip, propdata) );
578  assert(cutoffbound <= propdata->lastcutoffbound);
579 
580  if( cutoffbound == propdata->lastcutoffbound ) /*lint !e777*/
581  return SCIP_OKAY;
582 
583  /* get variables */
584  redcostvars = propdata->redcostvars;
585  nredcostvars = propdata->nredcostvars;
586 
587  /* since no variables has non-zero reduced cost do nothing */
588  if( nredcostvars == 0 )
589  return SCIP_OKAY;
590 
591  /* store cutoff bound to remember later that for that particular cutoff bound the propagation was already
592  * preformed
593  */
594  propdata->lastcutoffbound = cutoffbound;
595 
596  SCIPdebugMessage("searching for root reduced cost fixings\n");
597  SCIPdebugMessage("-> cutoffbound <%g>\n", cutoffbound);
598  SCIPdebugMessage("-> LP objective value <%g>\n", lpobjval);
599 
600  *result = SCIP_DIDNOTFIND;
601  nchgbds = 0;
602  cutoff = FALSE;
603 
604  /* propagate the binary variables with non-zero root reduced cost */
605  SCIP_CALL( propagateBinaryBestRootRedcost(scip, propdata, cutoffbound, &nchgbds, &cutoff) );
606 
607  /* check reduced costs for non-binary variables that were columns of the root LP */
608  for( v = propdata->nredcostbinvars; v < nredcostvars && !cutoff; ++v )
609  {
610  SCIP_VAR* var;
611  SCIP_Bool tightened;
612 
613  var = redcostvars[v];
614  assert(var != NULL);
615 
616  /* try to tighten the variable bound */
617  SCIP_CALL( propagateRootRedcostVar(scip, var, cutoffbound, &cutoff, &tightened) );
618 
619  if( tightened )
620  nchgbds++;
621  }
622 
623  /* evaluate propagation results */
624  if( cutoff )
625  {
626  /* we are done with solving since the cutoff is w.r.t. a global bound change; cutoff root node */
627  SCIP_CALL( SCIPcutoffNode(scip, SCIPgetRootNode(scip)) );
628  (*result) = SCIP_CUTOFF;
629  }
630  else if( nchgbds > 0 )
631  (*result) = SCIP_REDUCEDDOM;
632 
633  SCIPdebugMessage("tightened %d variable domains (%u cutoff)\n", nchgbds, cutoff);
634 
635  return SCIP_OKAY;
636 }
637 
638 /**@} */
639 
640 /**@name Interface methods
641  *
642  * @{
643  */
644 
645 /** creates the root node reduced cost strengthening propagator and includes it in SCIP */
647  SCIP* scip /**< SCIP data structure */
648  )
649 {
650  SCIP_PROPDATA* propdata;
651  SCIP_PROP* prop;
652 
653  /* create rootredcost propagator data */
654  SCIP_CALL( propdataCreate(scip, &propdata) );
655 
656  /* include propagator */
658  propExecRootredcost, propdata) );
659 
660  assert(prop != NULL);
661 
662  /* set optional callbacks via setter functions */
663  SCIP_CALL( SCIPsetPropCopy(scip, prop, propCopyRootredcost) );
664  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeRootredcost) );
665  SCIP_CALL( SCIPsetPropExitsol(scip, prop, propExitsolRootredcost) );
666 
667  return SCIP_OKAY;
668 }
669 
670 /**@} */
671