Scippy

SCIP

Solving Constraint Integer Programs

cutpool.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 cutpool.c
17  * @brief methods for storing cuts in a cut pool
18  * @author Tobias Achterberg
19  * @author Stefan Heinz
20  * @author Gerald Gamrath
21  * @author Marc Pfetsch
22  * @author Kati Wolter
23  */
24 
25 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
26 
27 #include <assert.h>
28 
29 #include "scip/def.h"
30 #include "scip/set.h"
31 #include "scip/stat.h"
32 #include "scip/clock.h"
33 #include "scip/lp.h"
34 #include "scip/cons.h"
35 #include "scip/sepa.h"
36 #include "scip/sepastore.h"
37 #include "scip/cutpool.h"
38 #include "scip/pub_message.h"
39 #include "scip/pub_misc.h"
40 
41 #include "scip/struct_cutpool.h"
42 
43 
44 
45 /*
46  * Hash functions
47  */
48 
49 /** gets the hash key of a cut */
50 static
51 SCIP_DECL_HASHGETKEY(hashGetKeyCut)
52 { /*lint --e{715}*/
53  SCIP_CUT* cut;
54 
55  cut = (SCIP_CUT*)elem;
56  assert(cut != NULL);
57  assert(cut->row != NULL);
58 
59  /* the key of a cut is the row */
60  return cut->row;
61 }
62 
63 /** returns TRUE iff both cuts are identical */
64 static
65 SCIP_DECL_HASHKEYEQ(hashKeyEqCut)
66 { /*lint --e{715}*/
67  /* Warning: The comparison of real values is made against default epsilon.
68  * This is ugly, but we have no settings at hand.
69  */
70  SCIP_ROW* row1;
71  SCIP_ROW* row2;
72 
73  row1 = (SCIP_ROW*)key1;
74  row2 = (SCIP_ROW*)key2;
75  assert(row1 != NULL);
76  assert(row2 != NULL);
77 
78  /* Sort the column indices of both rows.
79  *
80  * The columns in a row are divided into two parts: LP columns, which are currently in the LP and non-LP columns;
81  * we sort the rows, but that only ensures that within these two parts, columns are sorted w.r.t. their index.
82  * Normally, this should be suficient, because a column contained in both rows should either be one of the LP columns
83  * for both or one of the non-LP columns for both.
84  * However, directly after a row was created, before it is added to the LP, the row is not linked to all its
85  * columns and all columns are treated as non-LP columns.
86  * Therefore, if exactly one of the rows has no LP columns, we cannot rely on the partition, because this row might
87  * just have been created and also columns that are in the LP might be in the non-LP columns part.
88  */
89  SCIProwSort(row1);
90  SCIProwSort(row2);
91  assert(row1->lpcolssorted);
92  assert(row1->nonlpcolssorted);
93  assert(row1->validminmaxidx);
94  assert(row2->lpcolssorted);
95  assert(row2->nonlpcolssorted);
96  assert(row2->validminmaxidx);
97 
98  /* currently we are only handling rows which are completely linked or not linked at all */
99  assert(row1->nunlinked == 0 || row1->nlpcols == 0);
100  assert(row2->nunlinked == 0 || row2->nlpcols == 0);
101 
102  /* compare the trivial characteristics of the rows */
103  if( row1->len != row2->len
104  || row1->minidx != row2->minidx
105  || row1->maxidx != row2->maxidx
106  || row1->nummaxval != row2->nummaxval
107  || REALABS(row1->lhs - row2->lhs) > SCIP_DEFAULT_EPSILON
108  || REALABS(row1->rhs - row2->rhs) > SCIP_DEFAULT_EPSILON
109  || REALABS(row1->sqrnorm - row2->sqrnorm) > SCIP_DEFAULT_SUMEPSILON
110  || REALABS(row1->sumnorm - row2->sumnorm) > SCIP_DEFAULT_SUMEPSILON
111  || REALABS(row1->maxval - row2->maxval) > SCIP_DEFAULT_EPSILON
112  )
113  return FALSE;
114 
115  /* both rows have LP columns, or none of them has, or one has only LP colums and the other only non-LP columns,
116  * so we can rely on the sorting of the columns
117  */
118  if( (row1->nlpcols == 0) == (row2->nlpcols == 0)
119  || (row1->nlpcols == 0 && row2->nlpcols == row2->len)
120  || (row1->nlpcols == row1->len && row2->nlpcols == 0) )
121  {
122  int i;
123 
124  if( (row1->nlpcols == 0) == (row2->nlpcols == 0) )
125  {
126 #ifndef NDEBUG
127  /* in debug mode, we check that we can rely on the partition into LP columns and non-LP columns */
128  int i2;
129 
130  i = 0;
131  i2 = row2->nlpcols;
132  while( i < row1->nlpcols && i2 < row2->len )
133  {
134  assert(row1->cols[i] != row2->cols[i2]);
135  if( row1->cols[i]->index < row2->cols[i2]->index )
136  ++i;
137  else
138  {
139  assert(row1->cols[i]->index > row2->cols[i2]->index);
140  ++i2;
141  }
142  }
143  assert(i == row1->nlpcols || i2 == row2->len);
144 
145  i = row1->nlpcols;
146  i2 = 0;
147  while( i < row1->len && i2 < row2->nlpcols )
148  {
149  assert(row1->cols[i] != row2->cols[i2]);
150  if( row1->cols[i]->index < row2->cols[i2]->index )
151  ++i;
152  else
153  {
154  assert(row1->cols[i]->index > row2->cols[i2]->index);
155  ++i2;
156  }
157  }
158  assert(i == row1->len || i2 == row2->nlpcols);
159 #endif
160 
161  /* both rows are linked and the number of lpcolumns is not equal so they cannot be equal */
162  if( row1->nlpcols != row2->nlpcols )
163  return FALSE;
164  }
165 
166  /* compare the columns of the rows */
167  for( i = 0; i < row1->len; ++i )
168  {
169  if( row1->cols[i] != row2->cols[i] )
170  return FALSE;
171  }
172 
173  /* compare the coefficients of the rows */
174  for( i = 0; i < row1->len; ++i )
175  {
176  if( REALABS(row1->vals[i] - row2->vals[i]) > SCIP_DEFAULT_EPSILON )
177  return FALSE;
178  }
179  }
180  /* one row has LP columns, but the other not, that could be because the one without was just created and isn't
181  * linked yet; in this case, one column could be an LP column in one row and a non-LP column in the other row, so we
182  * cannot rely on the partition; thus, we iteratively check whether the next column of row1 is either the next LP
183  * column of row2 or the next non-LP column of row2 and the coefficients are equal
184  */
185  else
186  {
187  int i1;
188  int ilp;
189  int inlp;
190 
191  /* ensure that row1 is the row without LP columns, switch the rows, if neccessary */
192  if( row2->nlpcols == 0 )
193  {
194  SCIP_ROW* tmprow;
195  tmprow = row2;
196  row2 = row1;
197  row1 = tmprow;
198  }
199  assert(row1->nlpcols == 0 && row2->nlpcols > 0);
200 
201  ilp = 0;
202  inlp = row2->nlpcols;
203 
204  /* compare the columns and coefficients of the rows */
205  for( i1 = 0; i1 < row1->len; ++i1 )
206  {
207  /* current column of row1 is the current LP column of row2, check the coefficient */
208  if( ilp < row2->nlpcols && row1->cols[i1] == row2->cols[ilp] )
209  {
210  if( REALABS(row1->vals[i1] - row2->vals[ilp]) > SCIP_DEFAULT_EPSILON )
211  return FALSE;
212  else
213  ++ilp;
214  }
215  /* current column of row1 is the current non-LP column of row2, check the coefficient */
216  else if( inlp < row2->len && row1->cols[i1] == row2->cols[inlp] )
217  {
218  if( REALABS(row1->vals[i1] - row2->vals[inlp]) > SCIP_DEFAULT_EPSILON )
219  return FALSE;
220  else
221  ++inlp;
222  }
223  /* current column of row1 is neither the current LP column of row2, nor the current non-LP column of row 2 */
224  else
225  return FALSE;
226  }
227  }
228 
229  return TRUE;
230 }
231 
232 static
233 SCIP_DECL_HASHKEYVAL(hashKeyValCut)
234 { /*lint --e{715}*/
235  SCIP_ROW* row;
236  unsigned int keyval;
237  int maxabsval;
238  SCIP_Real maxval;
239  SCIP_SET* set;
240 
241  set = (SCIP_SET*) userptr;
242  row = (SCIP_ROW*)key;
243  assert(row != NULL);
244 
245  maxval = SCIProwGetMaxval(row, set);
246  assert(row->nummaxval > 0);
247  assert(row->validminmaxidx);
248 
249  if( maxval > (SCIP_Real) INT_MAX )
250  maxabsval = 0;
251  else if( maxval < 1.0 )
252  maxabsval = (int) (10000*maxval);
253  else
254  maxabsval = (int) maxval;
255 
256  keyval = (row->maxidx << 29) + (row->len << 22) + (row->minidx << 11) + maxabsval; /*lint !e701*/
257 
258  return keyval;
259 }
260 
261 
262 
263 /*
264  * dynamic memory arrays
265  */
266 
267 /** resizes cuts array to be able to store at least num entries */
268 static
270  SCIP_CUTPOOL* cutpool, /**< cut pool */
271  SCIP_SET* set, /**< global SCIP settings */
272  int num /**< minimal number of slots in array */
273  )
274 {
275  assert(cutpool != NULL);
276  assert(set != NULL);
277 
278  if( num > cutpool->cutssize )
279  {
280  int newsize;
281 
282  newsize = SCIPsetCalcMemGrowSize(set, num);
283  SCIP_ALLOC( BMSreallocMemoryArray(&cutpool->cuts, newsize) );
284  cutpool->cutssize = newsize;
285  }
286  assert(num <= cutpool->cutssize);
287 
288  return SCIP_OKAY;
289 }
290 
291 
292 
293 /*
294  * Cut methods
295  */
296 
297 /** creates a cut and captures the row */
298 static
300  SCIP_CUT** cut, /**< pointer to store the cut */
301  BMS_BLKMEM* blkmem, /**< block memory */
302  SCIP_ROW* row /**< row this cut represents */
303  )
304 {
305  assert(cut != NULL);
306  assert(blkmem != NULL);
307  assert(row != NULL);
308 
309  /* allocate cut memory */
310  SCIP_ALLOC( BMSallocBlockMemory(blkmem, cut) );
311  (*cut)->row = row;
312  (*cut)->age = 0;
313  (*cut)->processedlp = -1;
314  (*cut)->processedlpsol = -1;
315  (*cut)->pos = -1;
316 
317  /* capture row */
318  SCIProwCapture(row);
319 
320  return SCIP_OKAY;
321 }
322 
323 /** frees a cut and releases the row */
324 static
326  SCIP_CUT** cut, /**< pointer to store the cut */
327  BMS_BLKMEM* blkmem, /**< block memory */
328  SCIP_SET* set, /**< global SCIP settings */
329  SCIP_LP* lp /**< current LP data */
330  )
331 {
332  assert(cut != NULL);
333  assert(*cut != NULL);
334  assert((*cut)->row != NULL);
335  assert(blkmem != NULL);
336 
337  /* release row */
338  SCIP_CALL( SCIProwRelease(&(*cut)->row, blkmem, set, lp) );
339 
340  /* free cut memory */
341  BMSfreeBlockMemory(blkmem, cut);
342 
343  return SCIP_OKAY;
344 }
345 
346 /** returns whether the cut's age exceeds the age limit */
347 static
349  SCIP_CUT* cut, /**< cut to check */
350  int agelimit /**< maximum age a cut can reach before it is deleted from the pool, or -1 */
351  )
352 {
353  assert(cut != NULL);
354 
355  return (agelimit >= 0 && cut->age > agelimit);
356 }
357 
358 /** gets the row of the cut */
360  SCIP_CUT* cut /**< cut */
361  )
362 {
363  assert(cut != NULL);
364 
365  return cut->row;
366 }
367 
368 /** gets the age of the cut: the number of consecutive cut pool separation rounds where the cut was neither in the LP nor violated */
370  SCIP_CUT* cut /**< cut */
371  )
372 {
373  assert(cut != NULL);
374 
375  return cut->age;
376 }
377 
378 
379 
380 /*
381  * Cutpool methods
382  */
383 
384 /** creates cut pool */
386  SCIP_CUTPOOL** cutpool, /**< pointer to store cut pool */
387  BMS_BLKMEM* blkmem, /**< block memory */
388  SCIP_SET* set, /**< global SCIP settings */
389  int agelimit, /**< maximum age a cut can reach before it is deleted from the pool */
390  SCIP_Bool globalcutpool /**< is this the global cut pool of SCIP? */
391  )
392 {
393  assert(cutpool != NULL);
394  assert(agelimit >= -1);
395 
396  SCIP_ALLOC( BMSallocMemory(cutpool) );
397 
398  SCIP_CALL( SCIPclockCreate(&(*cutpool)->poolclock, SCIP_CLOCKTYPE_DEFAULT) );
399 
400  SCIP_CALL( SCIPhashtableCreate(&(*cutpool)->hashtable, blkmem,
401  (set->misc_usesmalltables ? SCIP_HASHSIZE_CUTPOOLS_SMALL : SCIP_HASHSIZE_CUTPOOLS),
402  hashGetKeyCut, hashKeyEqCut, hashKeyValCut, (void*) set) );
403 
404  (*cutpool)->cuts = NULL;
405  (*cutpool)->cutssize = 0;
406  (*cutpool)->ncuts = 0;
407  (*cutpool)->nremovablecuts = 0;
408  (*cutpool)->agelimit = agelimit;
409  (*cutpool)->processedlp = -1;
410  (*cutpool)->processedlpsol = -1;
411  (*cutpool)->firstunprocessed = 0;
412  (*cutpool)->firstunprocessedsol = 0;
413  (*cutpool)->maxncuts = 0;
414  (*cutpool)->ncalls = 0;
415  (*cutpool)->ncutsfound = 0;
416  (*cutpool)->globalcutpool = globalcutpool;
417 
418  return SCIP_OKAY;
419 }
420 
421 /** frees cut pool */
423  SCIP_CUTPOOL** cutpool, /**< pointer to store cut pool */
424  BMS_BLKMEM* blkmem, /**< block memory */
425  SCIP_SET* set, /**< global SCIP settings */
426  SCIP_LP* lp /**< current LP data */
427  )
428 {
429  assert(cutpool != NULL);
430  assert(*cutpool != NULL);
431 
432  /* remove all cuts from the pool */
433  SCIP_CALL( SCIPcutpoolClear(*cutpool, blkmem, set, lp) );
434 
435  /* free clock */
436  SCIPclockFree(&(*cutpool)->poolclock);
437 
438  /* free hash table */
439  SCIPhashtableFree(&(*cutpool)->hashtable);
440 
441  BMSfreeMemoryArrayNull(&(*cutpool)->cuts);
442  BMSfreeMemory(cutpool);
443 
444  return SCIP_OKAY;
445 }
446 
447 /** removes all rows from the cut pool */
449  SCIP_CUTPOOL* cutpool, /**< cut pool */
450  BMS_BLKMEM* blkmem, /**< block memory */
451  SCIP_SET* set, /**< global SCIP settings */
452  SCIP_LP* lp /**< current LP data */
453  )
454 {
455  int i;
456 
457  assert(cutpool != NULL);
458 
459  /* free cuts */
460  for( i = 0; i < cutpool->ncuts; ++i )
461  {
462  if( cutpool->globalcutpool )
463  cutpool->cuts[i]->row->inglobalcutpool = FALSE;
464  SCIProwUnlock(cutpool->cuts[i]->row);
465  SCIP_CALL( cutFree(&cutpool->cuts[i], blkmem, set, lp) );
466  }
467  cutpool->ncuts = 0;
468  cutpool->nremovablecuts = 0;
469 
470  return SCIP_OKAY;
471 }
472 
473 /** if not already existing, adds row to cut pool and captures it */
475  SCIP_CUTPOOL* cutpool, /**< cut pool */
476  BMS_BLKMEM* blkmem, /**< block memory */
477  SCIP_SET* set, /**< global SCIP settings */
478  SCIP_ROW* row /**< cutting plane to add */
479  )
480 {
481  assert(cutpool != NULL);
482  assert(row != NULL);
483 
484  /* check in hash table, if cut already exists in the pool */
485  if( SCIPhashtableRetrieve(cutpool->hashtable, (void*)row) == NULL )
486  {
487  SCIP_CALL( SCIPcutpoolAddNewRow(cutpool, blkmem, set, row) );
488  }
489 
490  return SCIP_OKAY;
491 }
492 
493 /** adds row to cut pool and captures it; doesn't check for multiple cuts */
495  SCIP_CUTPOOL* cutpool, /**< cut pool */
496  BMS_BLKMEM* blkmem, /**< block memory */
497  SCIP_SET* set, /**< global SCIP settings */
498  SCIP_ROW* row /**< cutting plane to add */
499  )
500 {
501  SCIP_CUT* cut;
502 
503  assert(cutpool != NULL);
504  assert(row != NULL);
505 
506  /* check, if row is modifiable or local */
507  if( SCIProwIsModifiable(row) )
508  {
509  SCIPerrorMessage("cannot store modifiable row <%s> in a cut pool\n", SCIProwGetName(row));
510  return SCIP_INVALIDDATA;
511  }
512  if( SCIProwIsLocal(row) )
513  {
514  SCIPerrorMessage("cannot store locally valid row <%s> in a cut pool\n", SCIProwGetName(row));
515  return SCIP_INVALIDDATA;
516  }
517 
518  /* only called to ensure that minidx and maxidx are up-to-date */
519  (void) SCIProwGetMaxidx(row, set);
520  assert(row->validminmaxidx);
521 
522  /* create the cut */
523  SCIP_CALL( cutCreate(&cut, blkmem, row) );
524  cut->pos = cutpool->ncuts;
525 
526  /* add cut to the pool */
527  SCIP_CALL( cutpoolEnsureCutsMem(cutpool, set, cutpool->ncuts+1) );
528  cutpool->cuts[cutpool->ncuts] = cut;
529  cutpool->ncuts++;
530  cutpool->maxncuts = MAX(cutpool->maxncuts, cutpool->ncuts);
531  if( SCIProwIsRemovable(row) )
532  cutpool->nremovablecuts++;
533 
534  /* insert cut in the hash table */
535  SCIP_CALL( SCIPhashtableInsert(cutpool->hashtable, (void*)cut) );
536 
537  /* if this is the global cut pool of SCIP, mark the row to be member of the pool */
538  if( cutpool->globalcutpool )
539  row->inglobalcutpool = TRUE;
540 
541  /* lock the row */
542  SCIProwLock(row);
543 
544  return SCIP_OKAY;
545 }
546 
547 /** removes the cut from the cut pool */
548 static
550  SCIP_CUTPOOL* cutpool, /**< cut pool */
551  BMS_BLKMEM* blkmem, /**< block memory */
552  SCIP_SET* set, /**< global SCIP settings */
553  SCIP_STAT* stat, /**< problem statistics data */
554  SCIP_LP* lp, /**< current LP data */
555  SCIP_CUT* cut /**< cut to remove */
556  )
557 {
558  int pos;
559 
560  assert(cutpool != NULL);
561  assert(cutpool->firstunprocessed <= cutpool->ncuts);
562  assert(cutpool->firstunprocessedsol <= cutpool->ncuts);
563  assert(blkmem != NULL);
564  assert(stat != NULL);
565  assert(cutpool->processedlp <= stat->lpcount);
566  assert(cutpool->processedlpsol <= stat->lpcount);
567  assert(cut != NULL);
568  assert(cut->row != NULL);
569 
570  pos = cut->pos;
571  assert(0 <= pos && pos < cutpool->ncuts);
572  assert(cutpool->cuts[pos] == cut);
573 
574  /* decrease the number of removable cuts counter (row might have changed its removable status -> counting might not
575  * be correct
576  */
577  if( SCIProwIsRemovable(cut->row) && cutpool->nremovablecuts > 0 )
578  cutpool->nremovablecuts--;
579 
580  /* if this is the global cut pool of SCIP, mark the row to not be member anymore */
581  if( cutpool->globalcutpool )
582  cut->row->inglobalcutpool = FALSE;
583 
584  /* unlock the row */
585  SCIProwUnlock(cut->row);
586 
587  /* remove the cut from the hash table */
588  assert(SCIPhashtableExists(cutpool->hashtable, (void*)cut));
589  SCIP_CALL( SCIPhashtableRemove(cutpool->hashtable, (void*)cut) );
590 
591  /* free the cut */
592  SCIP_CALL( cutFree(&cutpool->cuts[pos], blkmem, set, lp) );
593 
594  /* move the last cut of the pool to the free position */
595  if( pos < cutpool->ncuts-1 )
596  {
597  cutpool->cuts[pos] = cutpool->cuts[cutpool->ncuts-1];
598  cutpool->cuts[pos]->pos = pos;
599  assert(cutpool->cuts[pos]->processedlp <= stat->lpcount);
600  assert(cutpool->cuts[pos]->processedlpsol <= stat->lpcount);
601  if( cutpool->cuts[pos]->processedlp < stat->lpcount )
602  cutpool->firstunprocessed = MIN(cutpool->firstunprocessed, pos);
603  if( cutpool->cuts[pos]->processedlpsol < stat->lpcount )
604  cutpool->firstunprocessedsol = MIN(cutpool->firstunprocessedsol, pos);
605  }
606  else
607  {
608  cutpool->firstunprocessed = MIN(cutpool->firstunprocessed, cutpool->ncuts-1);
609  cutpool->firstunprocessedsol = MIN(cutpool->firstunprocessedsol, cutpool->ncuts-1);
610  }
611 
612  cutpool->ncuts--;
613 
614  return SCIP_OKAY;
615 }
616 
617 /** removes the LP row from the cut pool */
619  SCIP_CUTPOOL* cutpool, /**< cut pool */
620  BMS_BLKMEM* blkmem, /**< block memory */
621  SCIP_SET* set, /**< global SCIP settings */
622  SCIP_STAT* stat, /**< problem statistics data */
623  SCIP_LP* lp, /**< current LP data */
624  SCIP_ROW* row /**< row to remove */
625  )
626 {
627  SCIP_CUT* cut;
628 
629  assert(cutpool != NULL);
630  assert(row != NULL);
631 
632  /* find the cut in hash table */
633  cut = (SCIP_CUT*)SCIPhashtableRetrieve(cutpool->hashtable, (void*)row);
634  if( cut == NULL )
635  {
636  SCIPerrorMessage("row <%s> is not existing in cutpool %p\n", SCIProwGetName(row), cutpool);
637  return SCIP_INVALIDDATA;
638  }
639 
640  SCIP_CALL( cutpoolDelCut(cutpool, blkmem, set, stat, lp, cut) );
641 
642  return SCIP_OKAY;
643 }
644 
645 
646 /** separates cuts of the cut pool */
648  SCIP_CUTPOOL* cutpool, /**< cut pool */
649  BMS_BLKMEM* blkmem, /**< block memory */
650  SCIP_SET* set, /**< global SCIP settings */
651  SCIP_STAT* stat, /**< problem statistics data */
652  SCIP_EVENTQUEUE* eventqueue, /**< event queue */
653  SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */
654  SCIP_LP* lp, /**< current LP data */
655  SCIP_SEPASTORE* sepastore, /**< separation storage */
656  SCIP_SOL* sol, /**< solution to be separated (or NULL for LP-solution) */
657  SCIP_Bool cutpoolisdelayed, /**< is the cutpool delayed (count cuts found)? */
658  SCIP_Bool root, /**< are we at the root node? */
659  SCIP_RESULT* result /**< pointer to store the result of the separation call */
660  )
661 {
662  SCIP_CUT* cut;
663  SCIP_Bool found;
664  SCIP_Bool cutoff;
665  int firstunproc;
666  int oldncuts;
667  int c;
668 
669  assert(cutpool != NULL);
670  assert(stat != NULL);
671  assert(cutpool->processedlp <= stat->lpcount);
672  assert(cutpool->processedlpsol <= stat->lpcount);
673  assert(cutpool->firstunprocessed <= cutpool->ncuts);
674  assert(cutpool->firstunprocessedsol <= cutpool->ncuts);
675  assert(result != NULL);
676 
677  *result = SCIP_DIDNOTRUN;
678 
679  /* don't separate cut pool in the root node, if there are no removable cuts */
680  if( root && cutpool->nremovablecuts == 0 )
681  return SCIP_OKAY;
682 
683  if ( sol == NULL )
684  {
685  if( cutpool->processedlp < stat->lpcount )
686  cutpool->firstunprocessed = 0;
687  if( cutpool->firstunprocessed == cutpool->ncuts )
688  return SCIP_OKAY;
689  firstunproc = cutpool->firstunprocessed;
690  }
691  else
692  {
693  if( cutpool->processedlpsol < stat->lpcount )
694  cutpool->firstunprocessedsol = 0;
695  if( cutpool->firstunprocessedsol == cutpool->ncuts )
696  return SCIP_OKAY;
697  firstunproc = cutpool->firstunprocessedsol;
698  }
699 
700  *result = SCIP_DIDNOTFIND;
701  cutpool->ncalls++;
702  found = FALSE;
703 
704  SCIPdebugMessage("separating%s cut pool %p with %d cuts, beginning with cut %d\n", ( sol == NULL ) ? "" : " solution from", (void*)cutpool, cutpool->ncuts, firstunproc);
705 
706  /* start timing */
707  SCIPclockStart(cutpool->poolclock, set);
708 
709  /* remember the current total number of found cuts */
710  oldncuts = SCIPsepastoreGetNCuts(sepastore);
711 
712  /* process all unprocessed cuts in the pool */
713  cutoff = FALSE;
714  for( c = firstunproc; c < cutpool->ncuts; ++c )
715  {
716  SCIP_Longint proclp;
717 
718  cut = cutpool->cuts[c];
719  assert(cut != NULL);
720  assert(cut->processedlp <= stat->lpcount);
721  assert(cut->processedlpsol <= stat->lpcount);
722  assert(cut->pos == c);
723 
724  proclp = ( sol == NULL ) ? cut->processedlp : cut->processedlpsol;
725 
726  if( proclp < stat->lpcount )
727  {
728  SCIP_ROW* row;
729 
730  if ( sol == NULL )
731  cut->processedlp = stat->lpcount;
732  else
733  cut->processedlpsol = stat->lpcount;
734 
735  row = cut->row;
736  if( !SCIProwIsInLP(row) )
737  {
738  /* if the cut is a bound change (i.e. a row with only one variable), add it as bound change instead of LP
739  * row; hence, we want to remove the bound change cut from the SCIP cut pool
740  */
741  if( !SCIProwIsModifiable(row) && SCIProwGetNNonz(row) == 1 )
742  {
743  /* insert bound change cut into separation store which will force that cut */
744  SCIP_CALL( SCIPsepastoreAddCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, sol, row, FALSE, root, &cutoff) );
745  SCIP_CALL( cutpoolDelCut(cutpool, blkmem, set, stat, lp, cut) );
746 
747  if ( cutoff )
748  break;
749  }
750  else if( (sol == NULL && SCIProwIsLPEfficacious(row, set, stat, lp, root)) || (sol != NULL && SCIProwIsSolEfficacious(row, set, stat, sol, root)) )
751  {
752  /* insert cut in separation storage */
753  SCIPdebugMessage(" -> separated cut <%s> from the cut pool (feasibility: %g)\n",
754  SCIProwGetName(row), ( sol == NULL ) ? SCIProwGetLPFeasibility(row, set, stat, lp) : SCIProwGetSolFeasibility(row, set, stat, sol) );
755  SCIP_CALL( SCIPsepastoreAddCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, sol, row, FALSE, root, &cutoff) );
756 
757  /* count cuts */
758  if ( cutpoolisdelayed )
759  {
760  if ( SCIProwGetOriginSepa(row) != NULL )
761  {
762  SCIP_SEPA* sepa;
763 
764  sepa = SCIProwGetOriginSepa(row);
765  SCIPsepaIncNCutsFound(sepa);
767  }
768  else if ( SCIProwGetOriginCons(row) != NULL )
769  {
770  SCIP_CONSHDLR* conshdlr;
771 
772  conshdlr = SCIProwGetOriginCons(row);
773  SCIPconshdlrIncNCutsFound(conshdlr);
774  }
775  }
776 
777  found = TRUE;
778  cut->age = 0;
779 
780  if ( cutoff )
781  break;
782  }
783  else
784  {
785  cut->age++;
786  if( cutIsAged(cut, cutpool->agelimit) )
787  {
788  SCIP_CALL( cutpoolDelCut(cutpool, blkmem, set, stat, lp, cut) );
789  }
790  }
791  }
792  }
793  }
794 
795  if ( sol == NULL )
796  {
797  cutpool->processedlp = stat->lpcount;
798  cutpool->firstunprocessed = cutpool->ncuts;
799  }
800  else
801  {
802  cutpool->processedlpsol = stat->lpcount;
803  cutpool->firstunprocessedsol = cutpool->ncuts;
804  }
805 
806  /* update the number of found cuts */
807  cutpool->ncutsfound += SCIPsepastoreGetNCuts(sepastore) - oldncuts; /*lint !e776*/
808 
809  /* stop timing */
810  SCIPclockStop(cutpool->poolclock, set);
811 
812  if ( cutoff )
813  *result = SCIP_CUTOFF;
814  else if( found )
815  *result = SCIP_SEPARATED;
816 
817  return SCIP_OKAY;
818 }
819 
820 /** gets array of cuts in the cut pool */
822  SCIP_CUTPOOL* cutpool /**< cut pool */
823  )
824 {
825  assert(cutpool != NULL);
826 
827  return cutpool->cuts;
828 }
829 
830 /** gets number of cuts in the cut pool */
832  SCIP_CUTPOOL* cutpool /**< cut pool */
833  )
834 {
835  assert(cutpool != NULL);
836 
837  return cutpool->ncuts;
838 }
839 
840 /** gets maximum number of cuts that were stored in the cut pool at the same time */
842  SCIP_CUTPOOL* cutpool /**< cut pool */
843  )
844 {
845  assert(cutpool != NULL);
846 
847  return cutpool->maxncuts;
848 }
849 
850 /** gets time in seconds used for separating cuts from the pool */
852  SCIP_CUTPOOL* cutpool /**< cut pool */
853  )
854 {
855  assert(cutpool != NULL);
856 
857  return SCIPclockGetTime(cutpool->poolclock);
858 }
859 
860 /** get number of times, the cut pool was separated */
862  SCIP_CUTPOOL* cutpool /**< cut pool */
863  )
864 {
865  assert(cutpool != NULL);
866 
867  return cutpool->ncalls;
868 }
869 
870 /** get total number of cuts that were separated from the cut pool */
872  SCIP_CUTPOOL* cutpool /**< cut pool */
873  )
874 {
875  assert(cutpool != NULL);
876 
877  return cutpool->ncutsfound;
878 }
879 
880