Scippy

SCIP

Solving Constraint Integer Programs

sepastore.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 sepastore.c
17  * @brief methods for storing separated cuts
18  * @author Tobias Achterberg
19  * @author Marc Pfetsch
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include <assert.h>
25 
26 #include "scip/def.h"
27 #include "scip/set.h"
28 #include "scip/stat.h"
29 #include "scip/lp.h"
30 #include "scip/var.h"
31 #include "scip/tree.h"
32 #include "scip/sepastore.h"
33 #include "scip/event.h"
34 #include "scip/sepa.h"
35 #include "scip/cons.h"
36 #include "scip/debug.h"
37 
38 #include "scip/struct_sepastore.h"
39 
40 
41 
42 /*
43  * dynamic memory arrays
44  */
45 
46 /** resizes cuts and score arrays to be able to store at least num entries */
47 static
49  SCIP_SEPASTORE* sepastore, /**< separation storage */
50  SCIP_SET* set, /**< global SCIP settings */
51  int num /**< minimal number of slots in array */
52  )
53 {
54  assert(sepastore != NULL);
55  assert(set != NULL);
56 
57  if( num > sepastore->cutssize )
58  {
59  int newsize;
60 
61  newsize = SCIPsetCalcMemGrowSize(set, num);
62  SCIP_ALLOC( BMSreallocMemoryArray(&sepastore->cuts, newsize) );
63  SCIP_ALLOC( BMSreallocMemoryArray(&sepastore->efficacies, newsize) );
64  SCIP_ALLOC( BMSreallocMemoryArray(&sepastore->objparallelisms, newsize) );
65  SCIP_ALLOC( BMSreallocMemoryArray(&sepastore->orthogonalities, newsize) );
66  SCIP_ALLOC( BMSreallocMemoryArray(&sepastore->scores, newsize) );
67  sepastore->cutssize = newsize;
68  }
69  assert(num <= sepastore->cutssize);
70 
71  return SCIP_OKAY;
72 }
73 
74 
75 
76 
77 /** creates separation storage */
79  SCIP_SEPASTORE** sepastore /**< pointer to store separation storage */
80  )
81 {
82  assert(sepastore != NULL);
83 
84  SCIP_ALLOC( BMSallocMemory(sepastore) );
85 
86  (*sepastore)->cuts = NULL;
87  (*sepastore)->efficacies = NULL;
88  (*sepastore)->objparallelisms = NULL;
89  (*sepastore)->orthogonalities = NULL;
90  (*sepastore)->scores = NULL;
91  (*sepastore)->cutssize = 0;
92  (*sepastore)->ncuts = 0;
93  (*sepastore)->nforcedcuts = 0;
94  (*sepastore)->ncutsfound = 0;
95  (*sepastore)->ncutsfoundround = 0;
96  (*sepastore)->ncutsapplied = 0;
97  (*sepastore)->initiallp = FALSE;
98  (*sepastore)->forcecuts = FALSE;
99 
100  return SCIP_OKAY;
101 }
102 
103 /** frees separation storage */
105  SCIP_SEPASTORE** sepastore /**< pointer to store separation storage */
106  )
107 {
108  assert(sepastore != NULL);
109  assert(*sepastore != NULL);
110  assert((*sepastore)->ncuts == 0);
111 
112  BMSfreeMemoryArrayNull(&(*sepastore)->cuts);
113  BMSfreeMemoryArrayNull(&(*sepastore)->efficacies);
114  BMSfreeMemoryArrayNull(&(*sepastore)->objparallelisms);
115  BMSfreeMemoryArrayNull(&(*sepastore)->orthogonalities);
116  BMSfreeMemoryArrayNull(&(*sepastore)->scores);
117  BMSfreeMemory(sepastore);
118 
119  return SCIP_OKAY;
120 }
121 
122 /** informs separation storage, that the setup of the initial LP starts now */
124  SCIP_SEPASTORE* sepastore /**< separation storage */
125  )
126 {
127  assert(sepastore != NULL);
128  assert(!sepastore->initiallp);
129  assert(sepastore->ncuts == 0);
130 
131  sepastore->initiallp = TRUE;
132 }
133 
134 /** informs separation storage, that the setup of the initial LP is now finished */
136  SCIP_SEPASTORE* sepastore /**< separation storage */
137  )
138 {
139  assert(sepastore != NULL);
140  assert(sepastore->initiallp);
141  assert(sepastore->ncuts == 0);
142 
143  sepastore->initiallp = FALSE;
144 }
145 
146 /** informs separation storage, that the following cuts should be used in any case */
148  SCIP_SEPASTORE* sepastore /**< separation storage */
149  )
150 {
151  assert(sepastore != NULL);
152  assert(!sepastore->forcecuts);
153 
154  sepastore->forcecuts = TRUE;
155 }
156 
157 /** informs separation storage, that the following cuts should no longer be used in any case */
159  SCIP_SEPASTORE* sepastore /**< separation storage */
160  )
161 {
162  assert(sepastore != NULL);
163  assert(sepastore->forcecuts);
164 
165  sepastore->forcecuts = FALSE;
166 }
167 
168 /** checks cut for redundancy due to activity bounds */
169 static
171  SCIP_SEPASTORE* sepastore, /**< separation storage */
172  SCIP_SET* set, /**< global SCIP settings */
173  SCIP_STAT* stat, /**< problem statistics data */
174  SCIP_ROW* cut /**< separated cut */
175  )
176 {
177  SCIP_Real minactivity;
178  SCIP_Real maxactivity;
179  SCIP_Real lhs;
180  SCIP_Real rhs;
181 
182  assert(sepastore != NULL);
183  assert(cut != NULL);
184 
185  /* modifiable cuts cannot be declared redundant, since we don't know all coefficients */
186  if( SCIProwIsModifiable(cut) )
187  return FALSE;
188 
189  /* check for activity redundancy */
190  lhs = SCIProwGetLhs(cut);
191  rhs = SCIProwGetRhs(cut);
192  minactivity = SCIProwGetMinActivity(cut, set, stat);
193  maxactivity = SCIProwGetMaxActivity(cut, set, stat);
194  if( SCIPsetIsLE(set, lhs, minactivity) && SCIPsetIsLE(set, maxactivity, rhs) )
195  {
196  SCIPdebugMessage("ignoring activity redundant cut <%s> (sides=[%g,%g], act=[%g,%g])\n",
197  SCIProwGetName(cut), lhs, rhs, minactivity, maxactivity);
198  /*SCIPdebug(SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
199  return TRUE;
200  }
201 
202  return FALSE;
203 }
204 
205 /** checks cut for redundancy or infeasibility due to activity bounds */
206 static
208  SCIP_SEPASTORE* sepastore, /**< separation storage */
209  SCIP_SET* set, /**< global SCIP settings */
210  SCIP_STAT* stat, /**< problem statistics data */
211  SCIP_ROW* cut, /**< separated cut */
212  SCIP_Bool* infeasible /**< pointer to store whether the cut has been detected to be infeasible */
213  )
214 {
215  SCIP_Real minactivity;
216  SCIP_Real maxactivity;
217  SCIP_Real lhs;
218  SCIP_Real rhs;
219 
220  assert(sepastore != NULL);
221  assert(cut != NULL);
222  assert(infeasible != NULL);
223 
224  *infeasible = FALSE;
225 
226  /* modifiable cuts cannot be declared redundant or infeasible, since we don't know all coefficients */
227  if( SCIProwIsModifiable(cut) )
228  return FALSE;
229 
230  /* check for activity redundancy */
231  lhs = SCIProwGetLhs(cut);
232  rhs = SCIProwGetRhs(cut);
233  minactivity = SCIProwGetMinActivity(cut, set, stat);
234  maxactivity = SCIProwGetMaxActivity(cut, set, stat);
235  if( SCIPsetIsLE(set, lhs, minactivity) && SCIPsetIsLE(set, maxactivity, rhs) )
236  {
237  SCIPdebugMessage("ignoring activity redundant cut <%s> (sides=[%g,%g], act=[%g,%g])\n",
238  SCIProwGetName(cut), lhs, rhs, minactivity, maxactivity);
239  /*SCIPdebug(SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
240  return TRUE;
241  }
242  if ( SCIPsetIsFeasGT(set, minactivity, rhs) || SCIPsetIsFeasLT(set, maxactivity, lhs) )
243  {
244  SCIPdebugMessage("cut <%s> is infeasible (sides=[%g,%g], act=[%g,%g])\n",
245  SCIProwGetName(cut), lhs, rhs, minactivity, maxactivity);
246  /*SCIPdebug(SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
247  *infeasible = TRUE;
248  return TRUE;
249  }
250 
251  return FALSE;
252 }
253 
254 /** checks whether a cut with only one variable can be applied as boundchange
255  *
256  * This is the case if the bound change would prove infeasibility (w.r.t feastol),
257  * or if the new bound is at least epsilon better than the old bound.
258  * In the latter case, also the opposite bound has to be taken into account.
259  */
260 static
262  SCIP_SET* set, /**< global SCIP settings */
263  SCIP_ROW* cut /**< cut with a single variable */
264 )
265 {
266  SCIP_COL** cols;
267  SCIP_Real* vals;
268  SCIP_VAR* var;
269  SCIP_Real lhs;
270  SCIP_Real rhs;
271  SCIP_Bool local;
272  SCIP_Real oldlb;
273  SCIP_Real oldub;
274 
275  assert(set != NULL);
276  assert(!SCIProwIsModifiable(cut));
277  assert(SCIProwGetNNonz(cut) == 1);
278 
279  /* get the single variable and its coefficient of the cut */
280  cols = SCIProwGetCols(cut);
281  assert(cols != NULL);
282 
283  var = SCIPcolGetVar(cols[0]);
284  vals = SCIProwGetVals(cut);
285  assert(vals != NULL);
286  assert(!SCIPsetIsZero(set, vals[0]));
287 
288  /* if the coefficient is nearly zero, we better ignore this cut for numerical reasons */
289  if( SCIPsetIsFeasZero(set, vals[0]) )
290  return FALSE;
291 
292  local = SCIProwIsLocal(cut);
293 
294  oldlb = local ? SCIPvarGetLbLocal(var) : SCIPvarGetLbGlobal(var);
295  oldub = local ? SCIPvarGetUbLocal(var) : SCIPvarGetUbGlobal(var);
296 
297  /* get the left hand side of the cut and convert it to a bound */
298  lhs = SCIProwGetLhs(cut);
299  if( !SCIPsetIsInfinity(set, -lhs) )
300  {
301  lhs -= SCIProwGetConstant(cut);
302  if( vals[0] > 0.0 )
303  {
304  /* coefficient is positive -> lhs corresponds to lower bound */
305  SCIP_Real newlb;
306 
307  newlb = lhs/vals[0];
308 
309  if( SCIPsetIsFeasGT(set, newlb, oldub) || SCIPsetIsGT(set, MIN(newlb, oldub), oldlb) )
310  return TRUE;
311  }
312  else
313  {
314  /* coefficient is negative -> lhs corresponds to upper bound */
315  SCIP_Real newub;
316 
317  newub = lhs/vals[0];
318 
319  if( SCIPsetIsFeasLT(set, newub, oldlb) || SCIPsetIsLT(set, MAX(newub, oldlb), oldub) )
320  return TRUE;
321  }
322  }
323 
324  /* get the right hand side of the cut and convert it to a bound */
325  rhs = SCIProwGetRhs(cut);
326  if( !SCIPsetIsInfinity(set, rhs) )
327  {
328  rhs -= SCIProwGetConstant(cut);
329  if( vals[0] > 0.0 )
330  {
331  /* coefficient is positive -> rhs corresponds to upper bound */
332  SCIP_Real newub;
333 
334  newub = rhs/vals[0];
335 
336  if( SCIPsetIsFeasLT(set, newub, oldlb) || SCIPsetIsLT(set, MAX(newub, oldlb), oldub) )
337  return TRUE;
338  }
339  else
340  {
341  /* coefficient is negative -> rhs corresponds to lower bound */
342  SCIP_Real newlb;
343 
344  newlb = rhs/vals[0];
345 
346  if( SCIPsetIsFeasGT(set, newlb, oldub) || SCIPsetIsGT(set, MIN(newlb, oldub), oldlb) )
347  return TRUE;
348  }
349  }
350 
351  return FALSE;
352 }
353 
354 /** adds cut stored as LP row to separation storage and captures it;
355  * if the cut should be forced to be used, an infinite score has to be used
356  */
357 static
359  SCIP_SEPASTORE* sepastore, /**< separation storage */
360  BMS_BLKMEM* blkmem, /**< block memory */
361  SCIP_SET* set, /**< global SCIP settings */
362  SCIP_STAT* stat, /**< problem statistics data */
363  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
364  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
365  SCIP_LP* lp, /**< LP data */
366  SCIP_SOL* sol, /**< primal solution that was separated, or NULL for LP solution */
367  SCIP_ROW* cut, /**< separated cut */
368  SCIP_Bool forcecut, /**< should the cut be forced to enter the LP? */
369  SCIP_Bool root, /**< are we at the root node? */
370  SCIP_Bool* infeasible /**< pointer to store whether the cut is infeasible */
371  )
372 {
373  SCIP_Real cutefficacy;
374  SCIP_Real cutobjparallelism;
375  SCIP_Real cutscore;
376  SCIP_Bool redundant;
377  int pos;
378 
379  assert(sepastore != NULL);
380  assert(sepastore->nforcedcuts <= sepastore->ncuts);
381  assert(set != NULL);
382  assert(cut != NULL);
383  assert(sol != NULL || !SCIProwIsInLP(cut));
384  assert(!SCIPsetIsInfinity(set, -SCIProwGetLhs(cut)) || !SCIPsetIsInfinity(set, SCIProwGetRhs(cut)));
385  assert(eventqueue != NULL);
386  assert(eventfilter != NULL);
387 
388  /* in the root node, every local cut is a global cut, and global cuts are nicer in many ways...*/
389  if( root && SCIProwIsLocal(cut) )
390  {
391  SCIPdebugMessage("change local flag of cut <%s> to FALSE due to addition in root node\n", SCIProwGetName(cut));
392 
394 
395  assert(!SCIProwIsLocal(cut));
396  }
397 
398  /* check cut for redundancy or infeasibility */
399  redundant = sepastoreIsCutRedundantOrInfeasible(sepastore, set, stat, cut, infeasible);
400  /* Note that we add infeasible rows in any case, since we cannot be sure whether the return values are handled
401  * correctly. In this way, the LP becomes infeasible. */
402 
403  /* in each separation round, make sure that at least one (even redundant) cut enters the LP to avoid cycling */
404  if( !forcecut && sepastore->ncuts > 0 && redundant )
405  return SCIP_OKAY;
406 
407  /* if only one cut is currently present in the cut store, it could be redundant; in this case, it can now be removed
408  * again, because now a non redundant cut enters the store
409  */
410  if( sepastore->ncuts == 1 && sepastoreIsCutRedundant(sepastore, set, stat, sepastore->cuts[0]) )
411  {
412  /* check, if the row deletions from separation storage events are tracked if so, issue ROWDELETEDSEPA event */
413  if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWDELETEDSEPA) != 0 )
414  {
415  SCIP_EVENT* event;
416 
417  SCIP_CALL( SCIPeventCreateRowDeletedSepa(&event, blkmem, sepastore->cuts[0]) );
418  SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) );
419  }
420 
421  SCIP_CALL( SCIProwRelease(&sepastore->cuts[0], blkmem, set, lp) );
422  sepastore->ncuts = 0;
423  sepastore->nforcedcuts = 0;
424  }
425 
426  /* a cut is forced to enter the LP if
427  * - we construct the initial LP, or
428  * - it has infinite score factor, or
429  * - it is a bound change that can be applied
430  * if it is a non-forced cut and no cuts should be added, abort
431  */
432  forcecut = forcecut || sepastore->initiallp || sepastore->forcecuts || (!SCIProwIsModifiable(cut) && SCIProwGetNNonz(cut) == 1 && sepastoreIsBdchgApplicable(set, cut));
433  if( !forcecut && SCIPsetGetSepaMaxcuts(set, root) == 0 )
434  return SCIP_OKAY;
435 
436  /* get enough memory to store the cut */
437  SCIP_CALL( sepastoreEnsureCutsMem(sepastore, set, sepastore->ncuts+1) );
438  assert(sepastore->ncuts < sepastore->cutssize);
439 
440  if( forcecut )
441  {
442  cutefficacy = SCIPsetInfinity(set);
443  cutscore = SCIPsetInfinity(set);
444  cutobjparallelism = 1.0;
445  }
446  else
447  {
448  /* initialize values to invalid (will be initialized during cut filtering) */
449  cutefficacy = SCIP_INVALID;
450  cutscore = SCIP_INVALID;
451 
452  /* initialize parallelism to objective (constant throughout filtering) */
453  if( set->sepa_objparalfac > 0.0 )
454  cutobjparallelism = SCIProwGetObjParallelism(cut, set, lp);
455  else
456  cutobjparallelism = 0.0; /* no need to calculate it */
457  }
458 
459  SCIPdebugMessage("adding cut <%s> to separation storage of size %d (forcecut=%u, len=%d)\n",
460  SCIProwGetName(cut), sepastore->ncuts, forcecut, SCIProwGetNNonz(cut));
461  /*SCIPdebug(SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
462 
463  /* capture the cut */
464  SCIProwCapture(cut);
465 
466  /* add cut to arrays */
467  if( forcecut )
468  {
469  /* make room at the beginning of the array for forced cut */
470  pos = sepastore->nforcedcuts;
471  sepastore->cuts[sepastore->ncuts] = sepastore->cuts[pos];
472  sepastore->efficacies[sepastore->ncuts] = sepastore->efficacies[pos];
473  sepastore->objparallelisms[sepastore->ncuts] = sepastore->objparallelisms[pos];
474  sepastore->orthogonalities[sepastore->ncuts] = sepastore->orthogonalities[pos];
475  sepastore->scores[sepastore->ncuts] = sepastore->scores[pos];
476  sepastore->nforcedcuts++;
477  }
478  else
479  pos = sepastore->ncuts;
480 
481  sepastore->cuts[pos] = cut;
482  sepastore->efficacies[pos] = cutefficacy;
483  sepastore->objparallelisms[pos] = cutobjparallelism;
484  sepastore->orthogonalities[pos] = 1.0;
485  sepastore->scores[pos] = cutscore;
486  sepastore->ncuts++;
487 
488  /* check, if the row addition to separation storage events are tracked
489  * if so, issue ROWADDEDSEPA event
490  */
491  if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWADDEDSEPA) != 0 )
492  {
493  SCIP_EVENT* event;
494 
495  SCIP_CALL( SCIPeventCreateRowAddedSepa(&event, blkmem, cut) );
496  SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) );
497  }
498 
499  return SCIP_OKAY;
500 }
501 
502 /** removes a non-forced cut from the separation storage */
503 static
505  SCIP_SEPASTORE* sepastore, /**< separation storage */
506  BMS_BLKMEM* blkmem, /**< block memory */
507  SCIP_SET* set, /**< global SCIP settings */
508  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
509  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
510  SCIP_LP* lp, /**< LP data */
511  int pos /**< position of cut to delete */
512  )
513 {
514  assert(sepastore != NULL);
515  assert(sepastore->cuts != NULL);
516  assert(sepastore->nforcedcuts <= pos && pos < sepastore->ncuts);
517 
518  /* check, if the row deletions from separation storage events are tracked
519  * if so, issue ROWDELETEDSEPA event
520  */
521  if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWDELETEDSEPA) != 0 )
522  {
523  SCIP_EVENT* event;
524 
525  SCIP_CALL( SCIPeventCreateRowDeletedSepa(&event, blkmem, sepastore->cuts[pos]) );
526  SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) );
527  }
528 
529  /* release the row */
530  SCIP_CALL( SCIProwRelease(&sepastore->cuts[pos], blkmem, set, lp) );
531 
532  /* move last cut to the empty position */
533  sepastore->cuts[pos] = sepastore->cuts[sepastore->ncuts-1];
534  sepastore->efficacies[pos] = sepastore->efficacies[sepastore->ncuts-1];
535  sepastore->objparallelisms[pos] = sepastore->objparallelisms[sepastore->ncuts-1];
536  sepastore->orthogonalities[pos] = sepastore->orthogonalities[sepastore->ncuts-1];
537  sepastore->scores[pos] = sepastore->scores[sepastore->ncuts-1];
538  sepastore->ncuts--;
539 
540  return SCIP_OKAY;
541 }
542 
543 /** adds cut to separation storage and captures it;
544  * if the cut should be forced to enter the LP, an infinite score has to be used
545  */
547  SCIP_SEPASTORE* sepastore, /**< separation storage */
548  BMS_BLKMEM* blkmem, /**< block memory */
549  SCIP_SET* set, /**< global SCIP settings */
550  SCIP_STAT* stat, /**< problem statistics data */
551  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
552  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
553  SCIP_LP* lp, /**< LP data */
554  SCIP_SOL* sol, /**< primal solution that was separated, or NULL for LP solution */
555  SCIP_ROW* cut, /**< separated cut */
556  SCIP_Bool forcecut, /**< should the cut be forced to enter the LP? */
557  SCIP_Bool root, /**< are we at the root node? */
558  SCIP_Bool* infeasible /**< pointer to store whether the cut is infeasible */
559  )
560 {
561  assert(sepastore != NULL);
562  assert(cut != NULL);
563  assert(!SCIProwIsInLP(cut));
564  assert(!SCIPsetIsInfinity(set, -SCIProwGetLhs(cut)) || !SCIPsetIsInfinity(set, SCIProwGetRhs(cut)));
565 
566  /* debug: check cut for feasibility */
567  SCIP_CALL( SCIPdebugCheckRow(set, cut) ); /*lint !e506 !e774*/
568 
569  /* update statistics of total number of found cuts */
570  if( !sepastore->initiallp )
571  {
572  sepastore->ncutsfound++;
573  sepastore->ncutsfoundround++;
574  }
575 
576  /* add LP row cut to separation storage */
577  SCIP_CALL( sepastoreAddCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, sol, cut, forcecut, root, infeasible) );
578 
579  return SCIP_OKAY;
580 }
581 
582 /** applies a lower bound change */
583 static
585  SCIP_SEPASTORE* sepastore, /**< separation storage */
586  BMS_BLKMEM* blkmem, /**< block memory */
587  SCIP_SET* set, /**< global SCIP settings */
588  SCIP_STAT* stat, /**< problem statistics */
589  SCIP_PROB* transprob, /**< transformed problem */
590  SCIP_PROB* origprob, /**< original problem */
591  SCIP_TREE* tree, /**< branch and bound tree */
592  SCIP_LP* lp, /**< LP data */
593  SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
594  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
595  SCIP_VAR* var, /**< problem variable */
596  SCIP_Real bound, /**< new lower bound of variable */
597  SCIP_Bool local, /**< is it a local bound change? (otherwise global) */
598  SCIP_Bool* applied, /**< pointer to store whether the domain change was applied */
599  SCIP_Bool* cutoff /**< pointer to store TRUE, if an infeasibility has been detected */
600  )
601 {
602  assert(sepastore != NULL);
603  assert(cutoff != NULL);
604  assert(applied != NULL);
605 
606  if( local )
607  {
608  /* apply the local bound change or detect a cutoff */
609  if( SCIPsetIsGT(set, bound, SCIPvarGetLbLocal(var)) )
610  {
611  SCIPdebugMessage(" -> applying bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
613 
614  if( SCIPsetIsFeasLE(set, bound, SCIPvarGetUbLocal(var)) )
615  {
616  SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetCurrentNode(tree), blkmem, set, stat, transprob, origprob, tree, lp, branchcand,
617  eventqueue, var, bound, SCIP_BOUNDTYPE_LOWER, FALSE) );
618  }
619  else
620  *cutoff = TRUE;
621 
622  *applied = TRUE;
623  }
624  else
625  {
626  SCIPdebugMessage(" -> ignoring bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
628  }
629  }
630  else
631  {
632  /* apply the global bound change or detect a global cutoff which means we can cutoff the root node */
633  if( SCIPsetIsGT(set, bound, SCIPvarGetLbGlobal(var)) )
634  {
635  SCIPdebugMessage(" -> applying global bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
637 
638  if( SCIPsetIsFeasLE(set, bound, SCIPvarGetUbGlobal(var)) )
639  {
640  SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob, tree, lp, branchcand,
641  eventqueue, var, bound, SCIP_BOUNDTYPE_LOWER, FALSE) );
642  }
643  else
644  {
645  /* we are done with solving since a global bound change is infeasible */
646  SCIPnodeCutoff(SCIPtreeGetRootNode(tree), set, stat, tree);
647  *cutoff = TRUE;
648  }
649 
650  *applied = TRUE;
651  }
652  else
653  {
654  SCIPdebugMessage(" -> ignoring global bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
656  }
657  }
658 
659  return SCIP_OKAY;
660 }
661 
662 /** applies an upper bound change */
663 static
665  SCIP_SEPASTORE* sepastore, /**< separation storage */
666  BMS_BLKMEM* blkmem, /**< block memory */
667  SCIP_SET* set, /**< global SCIP settings */
668  SCIP_STAT* stat, /**< problem statistics */
669  SCIP_PROB* transprob, /**< transformed problem */
670  SCIP_PROB* origprob, /**< original problem */
671  SCIP_TREE* tree, /**< branch and bound tree */
672  SCIP_LP* lp, /**< LP data */
673  SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
674  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
675  SCIP_VAR* var, /**< problem variable */
676  SCIP_Real bound, /**< new upper bound of variable */
677  SCIP_Bool local, /**< is it a local bound change? (otherwise global) */
678  SCIP_Bool* applied, /**< pointer to store whether the domain change was applied */
679  SCIP_Bool* cutoff /**< pointer to store TRUE, if an infeasibility has been detected */
680  )
681 {
682  assert(sepastore != NULL);
683  assert(cutoff != NULL);
684  assert(applied != NULL);
685 
686  if( local )
687  {
688  /* apply the local bound change or detect a cutoff */
689  if( SCIPsetIsLT(set, bound, SCIPvarGetUbLocal(var)) )
690  {
691  SCIPdebugMessage(" -> applying bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
693 
694  if( SCIPsetIsFeasGE(set, bound, SCIPvarGetLbLocal(var)) )
695  {
696  SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetCurrentNode(tree), blkmem, set, stat, transprob, origprob, tree, lp, branchcand,
697  eventqueue, var, bound, SCIP_BOUNDTYPE_UPPER, FALSE) );
698  }
699  else
700  *cutoff = TRUE;
701 
702  *applied = TRUE;
703  }
704  else
705  {
706  SCIPdebugMessage(" -> ignoring bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
708  }
709  }
710  else
711  {
712  /* apply the global bound change or detect a global cutoff which means we can cutoff the root node */
713  if( SCIPsetIsLT(set, bound, SCIPvarGetUbGlobal(var)) )
714  {
715  SCIPdebugMessage(" -> applying global bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
717 
718  if( SCIPsetIsFeasGE(set, bound, SCIPvarGetLbGlobal(var)) )
719  {
720  SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob, tree, lp, branchcand,
721  eventqueue, var, bound, SCIP_BOUNDTYPE_UPPER, FALSE) );
722  }
723  else
724  {
725  /* we are done with solving since a global bound change is infeasible */
726  SCIPnodeCutoff(SCIPtreeGetRootNode(tree), set, stat, tree);
727  *cutoff = TRUE;
728  }
729 
730  *applied = TRUE;
731  }
732  else
733  {
734  SCIPdebugMessage(" -> ignoring global bound change: <%s>: [%.20g,%.20g] -> [%.20g,%.20g]\n",
736  }
737  }
738 
739  return SCIP_OKAY;
740 }
741 
742 /** applies a cut that is a bound change directly as bound change instead of adding it as row to the LP */
743 static
745  SCIP_SEPASTORE* sepastore, /**< separation storage */
746  BMS_BLKMEM* blkmem, /**< block memory */
747  SCIP_SET* set, /**< global SCIP settings */
748  SCIP_STAT* stat, /**< problem statistics */
749  SCIP_PROB* transprob, /**< transformed problem */
750  SCIP_PROB* origprob, /**< original problem */
751  SCIP_TREE* tree, /**< branch and bound tree */
752  SCIP_LP* lp, /**< LP data */
753  SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
754  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
755  SCIP_ROW* cut, /**< cut with a single variable */
756  SCIP_EFFICIACYCHOICE efficiacychoice, /**< type of solution to base feasibility computation on */
757  SCIP_Bool* applied, /**< pointer to store whether the domain change was applied */
758  SCIP_Bool* cutoff /**< pointer to store whether an empty domain was created */
759  )
760 {
761  SCIP_COL** cols;
762  SCIP_Real* vals;
763  SCIP_VAR* var;
764  SCIP_Real lhs;
765  SCIP_Real rhs;
766  SCIP_Bool local;
767 
768  assert(sepastore != NULL);
769  assert(!SCIProwIsModifiable(cut));
770  assert(SCIProwGetNNonz(cut) == 1);
771  assert(cutoff != NULL);
772  assert(applied != NULL);
773 
774  *applied = FALSE;
775  *cutoff = FALSE;
776 
777  /* get the single variable and its coefficient of the cut */
778  cols = SCIProwGetCols(cut);
779  assert(cols != NULL);
780 
781  var = SCIPcolGetVar(cols[0]);
782  vals = SCIProwGetVals(cut);
783  assert(vals != NULL);
784  assert(!SCIPsetIsZero(set, vals[0]));
785 
786  /* if the coefficient is nearly zero, we better ignore this cut for numerical reasons */
787  if( SCIPsetIsFeasZero(set, vals[0]) )
788  return SCIP_OKAY;
789 
790  local = SCIProwIsLocal(cut);
791 
792  /* get the left hand side of the cut and convert it to a bound */
793  lhs = SCIProwGetLhs(cut);
794  if( !SCIPsetIsInfinity(set, -lhs) )
795  {
796  lhs -= SCIProwGetConstant(cut);
797  if( vals[0] > 0.0 )
798  {
799  /* coefficient is positive -> lhs corresponds to lower bound */
800  SCIP_CALL( sepastoreApplyLb(sepastore, blkmem, set, stat, transprob, origprob, tree, lp, branchcand, eventqueue,
801  var, lhs/vals[0], local, applied, cutoff) );
802  }
803  else
804  {
805  /* coefficient is negative -> lhs corresponds to upper bound */
806  SCIP_CALL( sepastoreApplyUb(sepastore, blkmem, set, stat, transprob, origprob, tree, lp, branchcand, eventqueue,
807  var, lhs/vals[0], local, applied, cutoff) );
808  }
809  }
810 
811  /* get the right hand side of the cut and convert it to a bound */
812  rhs = SCIProwGetRhs(cut);
813  if( !SCIPsetIsInfinity(set, rhs) )
814  {
815  rhs -= SCIProwGetConstant(cut);
816  if( vals[0] > 0.0 )
817  {
818  /* coefficient is positive -> rhs corresponds to upper bound */
819  SCIP_CALL( sepastoreApplyUb(sepastore, blkmem, set, stat, transprob, origprob, tree, lp, branchcand, eventqueue,
820  var, rhs/vals[0], local, applied, cutoff) );
821  }
822  else
823  {
824  /* coefficient is negative -> rhs corresponds to lower bound */
825  SCIP_CALL( sepastoreApplyLb(sepastore, blkmem, set, stat, transprob, origprob, tree, lp, branchcand, eventqueue,
826  var, rhs/vals[0], local, applied, cutoff) );
827  }
828  }
829 
830  /* count the bound change as applied cut */
831  if( *applied && !sepastore->initiallp )
832  sepastore->ncutsapplied++;
833 
834  if( *applied && !sepastore->initiallp && set->sepa_feastolfac > 0.0 )
835  {
836  SCIP_Real infeasibility;
837 
838  /* calculate cut's infeasibility */
839  switch ( efficiacychoice )
840  {
842  infeasibility = -SCIProwGetLPFeasibility(cut, set, stat, lp);
843  break;
845  infeasibility = -SCIProwGetRelaxFeasibility(cut, set, stat);
846  break;
848  infeasibility = -SCIProwGetNLPFeasibility(cut, set, stat);
849  break;
850  default:
851  SCIPerrorMessage("Invalid efficiacy choice.\n");
852  return SCIP_INVALIDCALL;
853  }
854  /* a cut a*x<=b is applied as boundchange x<=b/a, so also the infeasibility needs to be divided by a (aka. scaled) */
855  infeasibility /= REALABS(vals[0]);
856 
857  /* reduce relaxation feasibility tolerance */
858  if( infeasibility > 0.0 && (set->sepa_primfeastol == SCIP_INVALID || set->sepa_primfeastol > set->sepa_feastolfac * infeasibility) ) /*lint !e777*/
859  {
860  SCIP_Real epsilon = SCIPsetEpsilon(set);
861 
862  set->sepa_primfeastol = MAX(set->sepa_feastolfac * infeasibility, epsilon);
863  SCIPdebugMessage("reduced feasibility tolerance for relaxations to %g due to cut <%s>\n", set->sepa_primfeastol, SCIProwGetName(cut));
864  }
865  }
866 
867  return SCIP_OKAY;
868 }
869 
870 /** updates the orthogonalities and scores of the non-forced cuts after the given cut was added to the LP */
871 static
873  SCIP_SEPASTORE* sepastore, /**< separation storage */
874  BMS_BLKMEM* blkmem, /**< block memory */
875  SCIP_SET* set, /**< global SCIP settings */
876  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
877  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
878  SCIP_LP* lp, /**< LP data */
879  SCIP_ROW* cut, /**< cut that was applied */
880  SCIP_Real mincutorthogonality /**< minimal orthogonality of cuts to apply to LP */
881  )
882 {
883  int pos;
884 
885  assert(sepastore != NULL);
886 
887  pos = sepastore->nforcedcuts;
888  while( pos < sepastore->ncuts )
889  {
890  SCIP_Real thisortho;
891 
892  /* update orthogonality */
893  thisortho = SCIProwGetOrthogonality(cut, sepastore->cuts[pos], set->sepa_orthofunc);
894  if( thisortho < sepastore->orthogonalities[pos] )
895  {
896  if( thisortho < mincutorthogonality )
897  {
898  /* cut is too parallel: release the row and delete the cut */
899  SCIPdebugMessage(" -> deleting parallel cut <%s> after adding <%s> (pos=%d, len=%d, orthogonality=%g, score=%g)\n",
900  SCIProwGetName(sepastore->cuts[pos]), SCIProwGetName(cut), pos, SCIProwGetNNonz(cut), thisortho, sepastore->scores[pos]);
901  SCIP_CALL( sepastoreDelCut(sepastore, blkmem, set, eventqueue, eventfilter, lp, pos) );
902  continue;
903  }
904  else
905  {
906  /* recalculate score */
907  sepastore->orthogonalities[pos] = thisortho;
908  assert( sepastore->objparallelisms[pos] != SCIP_INVALID ); /*lint !e777*/
909  assert( sepastore->scores[pos] != SCIP_INVALID ); /*lint !e777*/
910  assert( sepastore->efficacies[pos] != SCIP_INVALID ); /*lint !e777*/
911  sepastore->scores[pos] = sepastore->efficacies[pos]
912  + set->sepa_objparalfac * sepastore->objparallelisms[pos]
913  + set->sepa_orthofac * thisortho;
914  }
915  }
916  pos++;
917  }
918 
919  return SCIP_OKAY;
920 }
921 
922 /** applies the given cut to the LP and updates the orthogonalities and scores of remaining cuts */
923 static
925  SCIP_SEPASTORE* sepastore, /**< separation storage */
926  BMS_BLKMEM* blkmem, /**< block memory */
927  SCIP_SET* set, /**< global SCIP settings */
928  SCIP_STAT* stat, /**< problem statistics */
929  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
930  SCIP_EVENTFILTER* eventfilter, /**< global event filter */
931  SCIP_LP* lp, /**< LP data */
932  SCIP_ROW* cut, /**< cut to apply to the LP */
933  SCIP_Real mincutorthogonality,/**< minimal orthogonality of cuts to apply to LP */
934  int depth, /**< depth of current node */
935  SCIP_EFFICIACYCHOICE efficiacychoice, /**< type of solution to base feasibility computation on */
936  int* ncutsapplied /**< pointer to count the number of applied cuts */
937  )
938 {
939  assert(sepastore != NULL);
940  assert(ncutsapplied != NULL);
941 
942  /* a row could have been added twice to the separation store; add it only once! */
943  if( !SCIProwIsInLP(cut) )
944  {
945  /* add cut to the LP and capture it */
946  SCIP_CALL( SCIPlpAddRow(lp, blkmem, set, eventqueue, eventfilter, cut, depth) );
947 
948  /* update statistics -> only if we are not in the initial lp (cuts are only counted if added during run) */
949  if( !sepastore->initiallp )
950  {
951  sepastore->ncutsapplied++;
952 
953  /* increase count of applied cuts for origins of row */
954  switch ( cut->origintype )
955  {
957  assert( cut->origin != NULL );
959  break;
961  assert( cut->origin != NULL );
963  break;
965  /* do nothing - cannot update statistics */
966  break;
967  default:
968  SCIPerrorMessage("unkown type of row origin.\n");
969  return SCIP_INVALIDDATA;
970  }
971  }
972 
973  /* update the orthogonalities */
974  SCIP_CALL( sepastoreUpdateOrthogonalities(sepastore, blkmem, set, eventqueue, eventfilter, lp, cut, mincutorthogonality) );
975  (*ncutsapplied)++;
976 
977  if( !sepastore->initiallp && set->sepa_feastolfac > 0.0 )
978  {
979  SCIP_Real infeasibility;
980 
981  /* calculate cut's infeasibility */
982  switch ( efficiacychoice )
983  {
985  infeasibility = -SCIProwGetLPFeasibility(cut, set, stat, lp);
986  break;
988  infeasibility = -SCIProwGetRelaxFeasibility(cut, set, stat);
989  break;
991  infeasibility = -SCIProwGetNLPFeasibility(cut, set, stat);
992  break;
993  default:
994  SCIPerrorMessage("Invalid efficiacy choice.\n");
995  return SCIP_INVALIDCALL;
996  }
997 
998  /* reduce relaxation feasibility tolerance */
999  if( infeasibility > 0.0 && (set->sepa_primfeastol == SCIP_INVALID || set->sepa_primfeastol > set->sepa_feastolfac * infeasibility) ) /*lint !e777*/
1000  {
1001  SCIP_Real epsilon = SCIPsetEpsilon(set);
1002 
1003  set->sepa_primfeastol = MAX(set->sepa_feastolfac * infeasibility, epsilon);
1004  SCIPdebugMessage("reduced feasibility tolerance for relaxations to %g due to cut <%s>\n", set->sepa_primfeastol, SCIProwGetName(cut));
1005  }
1006  }
1007  }
1008 
1009  return SCIP_OKAY;
1010 }
1011 
1012 /** returns the position of the best non-forced cut in the cuts array */
1013 static
1015  SCIP_SEPASTORE* sepastore /**< separation storage */
1016  )
1017 {
1018  SCIP_Real bestscore;
1019  int bestpos;
1020  int pos;
1021 
1022  assert(sepastore != NULL);
1023 
1024  bestscore = SCIP_REAL_MIN;
1025  bestpos = -1;
1026  for( pos = sepastore->nforcedcuts; pos < sepastore->ncuts; pos++ )
1027  {
1028  /* check if cut is current best cut */
1029  assert( sepastore->scores[pos] != SCIP_INVALID ); /*lint !e777*/
1030  if( sepastore->scores[pos] > bestscore )
1031  {
1032  bestscore = sepastore->scores[pos];
1033  bestpos = pos;
1034  }
1035  }
1036 
1037  return bestpos;
1038 }
1039 
1040 /** computes score for current LP solution and initialized orthogonalities */
1041 static
1043  SCIP_SEPASTORE* sepastore, /**< separation storage */
1044  SCIP_SET* set, /**< global SCIP settings */
1045  SCIP_STAT* stat, /**< problem statistics */
1046  SCIP_LP* lp, /**< LP data */
1047  SCIP_Bool handlepool, /**< whether the efficacy of cuts in the pool should be reduced */
1048  int pos, /**< position of cut to handle */
1049  SCIP_EFFICIACYCHOICE efficiacychoice /**< type of solution to base efficiacy computation on */
1050  )
1051 {
1052  SCIP_ROW* cut;
1053  SCIP_Real cutefficacy;
1054  SCIP_Real cutscore;
1055 
1056  cut = sepastore->cuts[pos];
1057 
1058  /* calculate cut's efficacy */
1059  switch ( efficiacychoice )
1060  {
1062  cutefficacy = SCIProwGetLPEfficacy(cut, set, stat, lp);
1063  break;
1065  cutefficacy = SCIProwGetRelaxEfficacy(cut, set, stat);
1066  break;
1068  cutefficacy = SCIProwGetNLPEfficacy(cut, set, stat);
1069  break;
1070  default:
1071  SCIPerrorMessage("Invalid efficiacy choice.\n");
1072  return SCIP_INVALIDCALL;
1073  }
1074 
1075  /* If a cut is not member of the cut pool, we slightly decrease its score to prefer identical
1076  * cuts which are in the cut pool. This is because the conversion of cuts into linear
1077  * constraints after a restart looks at the cut pool and cannot find tight non-pool cuts.
1078  */
1079  if( handlepool && !SCIProwIsInGlobalCutpool(cut) )
1080  cutefficacy *= 0.9999;
1081 
1082  /* calculate resulting score */
1083  assert( sepastore->objparallelisms[pos] != SCIP_INVALID ); /*lint !e777*/
1084  cutscore = cutefficacy + set->sepa_objparalfac * sepastore->objparallelisms[pos] + set->sepa_orthofac * 1.0;
1085  assert( !SCIPsetIsInfinity(set, cutscore) );
1086 
1087  sepastore->efficacies[pos] = cutefficacy;
1088  sepastore->scores[pos] = cutscore;
1089 
1090  /* make sure that the orthogonalities are initialized to 1.0 */
1091  sepastore->orthogonalities[pos] = 1.0;
1092 
1093  return SCIP_OKAY;
1094 }
1095 
1096 /** adds cuts to the LP and clears separation storage */
1098  SCIP_SEPASTORE* sepastore, /**< separation storage */
1099  BMS_BLKMEM* blkmem, /**< block memory */
1100  SCIP_SET* set, /**< global SCIP settings */
1101  SCIP_STAT* stat, /**< problem statistics */
1102  SCIP_PROB* transprob, /**< transformed problem */
1103  SCIP_PROB* origprob, /**< original problem */
1104  SCIP_TREE* tree, /**< branch and bound tree */
1105  SCIP_LP* lp, /**< LP data */
1106  SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1107  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1108  SCIP_EVENTFILTER* eventfilter, /**< global event filter */
1109  SCIP_Bool root, /**< are we at the root node? */
1110  SCIP_EFFICIACYCHOICE efficiacychoice, /**< type of solution to base efficiacy computation on */
1111  SCIP_Bool* cutoff /**< pointer to store whether an empty domain was created */
1112  )
1113 {
1114  SCIP_NODE* node;
1115  SCIP_Real mincutorthogonality;
1116  SCIP_Bool applied;
1117  int depth;
1118  int maxsepacuts;
1119  int ncutsapplied;
1120  int pos;
1121 
1122  assert(sepastore != NULL);
1123  assert(set != NULL);
1124  assert(tree != NULL);
1125  assert(lp != NULL);
1126  assert(cutoff != NULL);
1127 
1128  *cutoff = FALSE;
1129 
1130  SCIPdebugMessage("applying %d cuts\n", sepastore->ncuts);
1131 
1132  node = SCIPtreeGetCurrentNode(tree);
1133  assert(node != NULL);
1134 
1135  /* get maximal number of cuts to add to the LP */
1136  maxsepacuts = SCIPsetGetSepaMaxcuts(set, root);
1137  ncutsapplied = 0;
1138 
1139  /* get depth of current node */
1140  depth = SCIPnodeGetDepth(node);
1141 
1142  /* calculate minimal cut orthogonality */
1143  mincutorthogonality = (root ? set->sepa_minorthoroot : set->sepa_minortho);
1144  mincutorthogonality = MAX(mincutorthogonality, set->num_epsilon);
1145 
1146  /* Compute scores for all non-forced cuts and initialize orthogonalities - make sure all cuts are initialized again for the current LP solution */
1147  for( pos = sepastore->nforcedcuts; pos < sepastore->ncuts; pos++ )
1148  {
1149  SCIP_CALL( computeScore(sepastore, set, stat, lp, TRUE, pos, efficiacychoice) );
1150  }
1151 
1152  /* apply all forced cuts */
1153  for( pos = 0; pos < sepastore->nforcedcuts && !(*cutoff); pos++ )
1154  {
1155  SCIP_ROW* cut;
1156 
1157  cut = sepastore->cuts[pos];
1158  assert(SCIPsetIsInfinity(set, sepastore->scores[pos]));
1159 
1160  /* if the cut is a bound change (i.e. a row with only one variable), add it as bound change instead of LP row */
1161  applied = FALSE;
1162  if( !SCIProwIsModifiable(cut) && SCIProwGetNNonz(cut) == 1 )
1163  {
1164  SCIPdebugMessage(" -> applying forced cut <%s> as boundchange\n", SCIProwGetName(cut));
1165  SCIP_CALL( sepastoreApplyBdchg(sepastore, blkmem, set, stat, transprob, origprob, tree, lp, branchcand, eventqueue, cut, efficiacychoice, &applied, cutoff) );
1166 
1167  assert(applied || !sepastoreIsBdchgApplicable(set, cut));
1168  }
1169 
1170  if( !applied )
1171  {
1172  /* add cut to the LP and update orthogonalities */
1173  SCIPdebugMessage(" -> applying forced cut <%s>\n", SCIProwGetName(cut));
1174  /*SCIPdebug( SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
1175  SCIP_CALL( sepastoreApplyCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, cut, mincutorthogonality, depth, efficiacychoice, &ncutsapplied) );
1176  }
1177  }
1178 
1179  /* apply non-forced cuts */
1180  while( ncutsapplied < maxsepacuts && sepastore->ncuts > sepastore->nforcedcuts && !(*cutoff) )
1181  {
1182  SCIP_ROW* cut;
1183  int bestpos;
1184 
1185  /* get best non-forced cut */
1186  bestpos = sepastoreGetBestCut(sepastore);
1187  assert(sepastore->nforcedcuts <= bestpos && bestpos < sepastore->ncuts);
1188  assert(sepastore->scores[bestpos] != SCIP_INVALID ); /*lint !e777*/
1189  assert(sepastore->efficacies[bestpos] != SCIP_INVALID ); /*lint !e777*/
1190  cut = sepastore->cuts[bestpos];
1191  assert(SCIProwIsModifiable(cut) || SCIProwGetNNonz(cut) != 1 || !sepastoreIsBdchgApplicable(set, cut)); /* applicable bound changes are forced cuts */
1192  assert(!SCIPsetIsInfinity(set, sepastore->scores[bestpos]));
1193 
1194  SCIPdebugMessage(" -> applying cut <%s> (pos=%d/%d, len=%d, efficacy=%g, objparallelism=%g, orthogonality=%g, score=%g)\n",
1195  SCIProwGetName(cut), bestpos, sepastore->ncuts, SCIProwGetNNonz(cut), sepastore->efficacies[bestpos], sepastore->objparallelisms[bestpos],
1196  sepastore->orthogonalities[bestpos], sepastore->scores[bestpos]);
1197  /*SCIPdebug(SCIProwPrint(cut, set->scip->messagehdlr, NULL));*/
1198 
1199  /* capture cut such that it is not destroyed in sepastoreDelCut() */
1200  SCIProwCapture(cut);
1201 
1202  /* release the row and delete the cut (also issuing ROWDELETEDSEPA event) */
1203  SCIP_CALL( sepastoreDelCut(sepastore, blkmem, set, eventqueue, eventfilter, lp, bestpos) );
1204 
1205  /* Do not add (non-forced) non-violated cuts.
1206  * Note: do not take SCIPsetIsEfficacious(), because constraint handlers often add cuts w.r.t. SCIPsetIsFeasPositive().
1207  * Note2: if separating/feastolfac != -1, constraint handlers may even add cuts w.r.t. SCIPsetIsPositive(); those are currently rejected here
1208  */
1209  if( SCIPsetIsFeasPositive(set, sepastore->efficacies[bestpos]) )
1210  {
1211  /* add cut to the LP and update orthogonalities */
1212  SCIP_CALL( sepastoreApplyCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, cut, mincutorthogonality, depth, efficiacychoice, &ncutsapplied) );
1213  }
1214 
1215  /* release cut */
1216  SCIP_CALL( SCIProwRelease(&cut, blkmem, set, lp) );
1217  }
1218 
1219  /* clear the separation storage and reset statistics for separation round */
1220  SCIP_CALL( SCIPsepastoreClearCuts(sepastore, blkmem, set, eventqueue, eventfilter, lp) );
1221 
1222  return SCIP_OKAY;
1223 }
1224 
1225 /** clears the separation storage without adding the cuts to the LP */
1227  SCIP_SEPASTORE* sepastore, /**< separation storage */
1228  BMS_BLKMEM* blkmem, /**< block memory */
1229  SCIP_SET* set, /**< global SCIP settings */
1230  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1231  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
1232  SCIP_LP* lp /**< LP data */
1233  )
1234 {
1235  int c;
1236 
1237  assert(sepastore != NULL);
1238 
1239  SCIPdebugMessage("clearing %d cuts\n", sepastore->ncuts);
1240 
1241  /* release cuts */
1242  for( c = 0; c < sepastore->ncuts; ++c )
1243  {
1244  /* check, if the row deletions from separation storage events are tracked
1245  * if so, issue ROWDELETEDSEPA event
1246  */
1247  if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWDELETEDSEPA) != 0 )
1248  {
1249  SCIP_EVENT* event;
1250 
1251  SCIP_CALL( SCIPeventCreateRowDeletedSepa(&event, blkmem, sepastore->cuts[c]) );
1252  SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) );
1253  }
1254 
1255  SCIP_CALL( SCIProwRelease(&sepastore->cuts[c], blkmem, set, lp) );
1256  }
1257 
1258  /* reset counters */
1259  sepastore->ncuts = 0;
1260  sepastore->nforcedcuts = 0;
1261  sepastore->ncutsfoundround = 0;
1262 
1263  /* if we have just finished the initial LP construction, free the (potentially large) cuts array */
1264  if( sepastore->initiallp )
1265  {
1266  BMSfreeMemoryArrayNull(&sepastore->cuts);
1267  sepastore->cutssize = 0;
1268  }
1269 
1270  return SCIP_OKAY;
1271 }
1272 
1273 /** removes cuts that are inefficacious w.r.t. the current LP solution from separation storage without adding the cuts to the LP */
1275  SCIP_SEPASTORE* sepastore, /**< separation storage */
1276  BMS_BLKMEM* blkmem, /**< block memory */
1277  SCIP_SET* set, /**< global SCIP settings */
1278  SCIP_STAT* stat, /**< problem statistics data */
1279  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1280  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
1281  SCIP_LP* lp, /**< LP data */
1282  SCIP_Bool root, /**< are we at the root node? */
1283  SCIP_EFFICIACYCHOICE efficiacychoice /**< type of solution to base efficiacy computation on */
1284  )
1285 {
1286  int cnt;
1287  int c;
1288 
1289  assert( sepastore != NULL );
1290 
1291  /* check non-forced cuts only */
1292  cnt = 0;
1293  c = sepastore->nforcedcuts;
1294  while( c < sepastore->ncuts )
1295  {
1296  assert( sepastore->efficacies[c] == SCIP_INVALID ); /*lint !e777*/
1297  SCIP_CALL( computeScore(sepastore, set, stat, lp, FALSE, c, efficiacychoice) );
1298  if( !SCIPsetIsEfficacious(set, root, sepastore->efficacies[c]) )
1299  {
1300  SCIP_CALL( sepastoreDelCut(sepastore, blkmem, set, eventqueue, eventfilter, lp, c) );
1301  ++cnt;
1302  }
1303  else
1304  ++c;
1305  }
1306  SCIPdebugMessage("removed %d non-efficacious cuts\n", cnt);
1307 
1308  return SCIP_OKAY;
1309 }
1310 
1311 /** indicates whether a cut is applicable
1312  *
1313  * A cut is applicable if it is modifiable, not a bound change, or a bound change that changes bounds by at least epsilon.
1314  */
1316  SCIP_SET* set, /**< global SCIP settings */
1317  SCIP_ROW* cut /**< cut to check */
1318  )
1319 {
1320  return SCIProwIsModifiable(cut) || SCIProwGetNNonz(cut) != 1 || sepastoreIsBdchgApplicable(set, cut);
1321 }
1322 
1323 /** get cuts in the separation storage */
1325  SCIP_SEPASTORE* sepastore /**< separation storage */
1326  )
1327 {
1328  assert(sepastore != NULL);
1329 
1330  return sepastore->cuts;
1331 }
1332 
1333 /** get number of cuts in the separation storage */
1335  SCIP_SEPASTORE* sepastore /**< separation storage */
1336  )
1337 {
1338  assert(sepastore != NULL);
1339 
1340  return sepastore->ncuts;
1341 }
1342 
1343 /** get total number of cuts found so far */
1345  SCIP_SEPASTORE* sepastore /**< separation storage */
1346  )
1347 {
1348  assert(sepastore != NULL);
1349 
1350  return sepastore->ncutsfound;
1351 }
1352 
1353 /** get number of cuts found so far in current separation round */
1355  SCIP_SEPASTORE* sepastore /**< separation storage */
1356  )
1357 {
1358  assert(sepastore != NULL);
1359 
1360  return sepastore->ncutsfoundround;
1361 }
1362 
1363 /** get total number of cuts applied to the LPs */
1365  SCIP_SEPASTORE* sepastore /**< separation storage */
1366  )
1367 {
1368  assert(sepastore != NULL);
1369 
1370  return sepastore->ncutsapplied;
1371 }
1372