Scippy

GCG

Branch-and-Price & Column Generation for Everyone

colpool.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program */
4 /* GCG --- Generic Column Generation */
5 /* a Dantzig-Wolfe decomposition based extension */
6 /* of the branch-cut-and-price framework */
7 /* SCIP --- Solving Constraint Integer Programs */
8 /* */
9 /* Copyright (C) 2010-2021 Operations Research, RWTH Aachen University */
10 /* Zuse Institute Berlin (ZIB) */
11 /* */
12 /* This program is free software; you can redistribute it and/or */
13 /* modify it under the terms of the GNU Lesser General Public License */
14 /* as published by the Free Software Foundation; either version 3 */
15 /* of the License, or (at your option) any later version. */
16 /* */
17 /* This program is distributed in the hope that it will be useful, */
18 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
19 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
20 /* GNU Lesser General Public License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with this program; if not, write to the Free Software */
24 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
25 /* */
26 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27 
28 /**@file colpool.c
29  * @brief methods for storing cols in a col pool (based on SCIP's cut pool)
30  * @author Jonas Witt
31  */
32 
33 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
34 
35 #include <assert.h>
36 
37 #include "scip/clock.h"
38 
39 #include "pub_gcgcol.h"
40 #include "colpool.h"
41 #include "struct_colpool.h"
42 #include "pricestore_gcg.h"
43 #include "struct_pricestore_gcg.h"
44 #include "pricer_gcg.h"
45 
46 #define GCG_USESMALLTABLES FALSE
47 #define GCG_HASHSIZE_COLPOOLS_SMALL 100 /**< size of hash table in col pools for small problems */
48 #define GCG_HASHSIZE_COLPOOLS 500 /**< size of hash table in col pools */
49 
50 /*
51  * Hash functions
52  */
53 
54 /** gets the hash key of a col */
55 static
56 SCIP_DECL_HASHGETKEY(hashGetKeyCol)
57 { /*lint --e{715}*/
58  GCG_COL* col;
59 
60  col = (GCG_COL*)elem;
61  assert(col != NULL);
62 
63  /* the key of a col is the col itself */
64  return col;
65 }
66 
67 /** returns TRUE iff both cols are identical */
68 static
69 SCIP_DECL_HASHKEYEQ(hashKeyEqCol)
70 { /*lint --e{715}*/
71  /* Warning: The comparison of real values is made against default epsilon.
72  * This is ugly, but we have no settings at hand.
73  */
74  SCIP* scip;
75  GCG_COL* col1;
76  GCG_COL* col2;
77  int i;
78 
79  scip = (SCIP*) userptr;
80  col1 = (GCG_COL*)key1;
81  col2 = (GCG_COL*)key2;
82  assert(col1 != NULL);
83  assert(col2 != NULL);
84 
85  assert(col1->vars != NULL || col1->nvars == 0);
86  assert(col2->vars != NULL || col2->nvars == 0);
87 
88  /* compare the trivial characteristics of the cols */
89  if( col1->probnr != col2->probnr
90  || col1->isray != col2->isray
91  || col1->nvars != col2->nvars
92  )
93  return FALSE;
94 
95  /* compare variables and coresponding values in sorted arrays */
96  for( i = 0; i < col1->nvars; ++i )
97  {
98  if( col1->vars[i] != col2->vars[i]
99  || !SCIPisEQ(scip, col1->vals[i], col2->vals[i]))
100  return FALSE;
101  }
102 
103  return TRUE;
104 }
105 
106 static
107 SCIP_DECL_HASHKEYVAL(hashKeyValCol)
108 { /*lint --e{715}*/
109  GCG_COL* col;
110  unsigned int keyval;
111 
112  col = (GCG_COL*)key;
113  assert(col != NULL);
114 
115  /* TODO: Improve hash function (but then we would have to store additional values for each col) */
116  keyval = SCIPhashFour(SCIPrealHashCode(col->nvars > 0 ? col->vals[0] : 0.0), col->probnr, col->nvars,
117  col->isray);
118 
119  return keyval;
120 }
121 
122 
123 
124 /*
125  * dynamic memory arrays
126  */
127 
128 /** resizes cols array to be able to store at least num entries */
129 static
130 SCIP_RETCODE colpoolEnsureColsMem(
131  GCG_COLPOOL* colpool, /**< col pool */
132  int num /**< minimal number of slots in array */
133  )
134 {
135  assert(colpool != NULL);
136 
137  if( num > colpool->colssize )
138  {
139  int newsize;
140 
141  newsize = SCIPcalcMemGrowSize(colpool->scip, num);
142  SCIP_CALL( SCIPreallocBlockMemoryArray(colpool->scip, &colpool->cols, colpool->colssize, newsize) );
143  colpool->colssize = newsize;
144  }
145  assert(num <= colpool->colssize);
146 
147  return SCIP_OKAY;
148 }
149 
150 /*
151  * Col methods
152  */
153 
154 /*
155  * Colpool methods
156  */
157 
158 /** creates col pool */
159 SCIP_RETCODE GCGcolpoolCreate(
160  SCIP* scip, /**< SCIP data structure */
161  GCG_COLPOOL** colpool, /**< pointer to store col pool */
162  int agelimit /**< maximum age a col can reach before it is deleted from the pool (-1 fpr no limit) */
163  )
164 {
165  assert(colpool != NULL);
166  assert(agelimit >= -1);
167 
168  SCIP_CALL( SCIPallocMemory(scip, colpool) );
169 
170  SCIP_CALL( SCIPcreateClock(scip, &(*colpool)->poolclock) );
171 
172  SCIP_CALL( SCIPhashtableCreate(&(*colpool)->hashtable, SCIPblkmem(scip),
174  hashGetKeyCol, hashKeyEqCol, hashKeyValCol, (void*) scip) );
175 
176  (*colpool)->scip = scip;
177  (*colpool)->nodenr = -1;
178  (*colpool)->infarkas = FALSE;
179  (*colpool)->cols = NULL;
180  (*colpool)->colssize = 0;
181  (*colpool)->ncols = 0;
182  (*colpool)->agelimit = agelimit;
183  (*colpool)->processedlp = -1;
184  (*colpool)->processedlpsol = -1;
185  (*colpool)->firstunprocessed = 0;
186  (*colpool)->firstunprocessedsol = 0;
187  (*colpool)->maxncols = 0;
188  (*colpool)->ncalls = 0;
189  (*colpool)->ncolsfound = 0;
190 
191  return SCIP_OKAY;
192 }
193 
194 /** frees col pool */
195 SCIP_RETCODE GCGcolpoolFree(
196  SCIP* scip, /**< SCIP data structure */
197  GCG_COLPOOL** colpool /**< pointer to store col pool */
198  )
199 {
200  assert(scip == (*colpool)->scip);
201  assert(colpool != NULL);
202  assert(*colpool != NULL);
203 
204  /* remove all cols from the pool */
205  SCIP_CALL( GCGcolpoolClear(*colpool) );
206 
207  SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "Pricing time in colpool = %f sec\n", GCGcolpoolGetTime(*colpool));
208 
209  /* free clock */
210  SCIP_CALL( SCIPfreeClock(scip, &(*colpool)->poolclock) );
211 
212  /* free hash table */
213  SCIPhashtableFree(&(*colpool)->hashtable);
214 
215  SCIPfreeBlockMemoryArrayNull(scip, &(*colpool)->cols, (*colpool)->colssize);
216  SCIPfreeMemory(scip, colpool);
217 
218  return SCIP_OKAY;
219 }
220 
221 /** removes the col from the col pool */
222 static
223 SCIP_RETCODE colpoolDelCol(
224  GCG_COLPOOL* colpool, /**< col pool */
225  GCG_COL* col, /**< col to remove */
226  SCIP_Bool freecol /**< should the col be freed? */
227  )
228 {
229  int pos;
230 
231  assert(colpool != NULL);
232  assert(colpool->firstunprocessed <= colpool->ncols);
233  assert(colpool->firstunprocessedsol <= colpool->ncols);
234  assert(col != NULL);
235 
236  pos = col->pos;
237  assert(0 <= pos && pos < colpool->ncols);
238  assert(colpool->cols[pos] == col);
239 
240  /* remove the col from the hash table */
241  assert(SCIPhashtableExists(colpool->hashtable, (void*)col));
242  SCIP_CALL( SCIPhashtableRemove(colpool->hashtable, (void*)col) );
243 
244  /* free the col */
245  if( freecol )
246  GCGfreeGcgCol(&colpool->cols[pos]);
247 
248  /* move the last col of the pool to the free position */
249  if( pos < colpool->ncols-1 )
250  {
251  colpool->cols[pos] = colpool->cols[colpool->ncols-1];
252  colpool->cols[pos]->pos = pos;
253  }
254 
255  colpool->ncols--;
256 
257  return SCIP_OKAY;
258 }
259 
260 
261 /** removes all rows from the col pool */
262 SCIP_RETCODE GCGcolpoolClear(
263  GCG_COLPOOL* colpool /**< col pool */
264  )
265 {
266  int i;
267 
268  assert(colpool != NULL);
269 
270  /* free cols (in reverse order!) */
271  for( i = colpool->ncols - 1; i >= 0; --i )
272  {
273  SCIP_CALL( colpoolDelCol(colpool, colpool->cols[i], TRUE) );
274  }
275  colpool->ncols = 0;
276 
277  return SCIP_OKAY;
278 }
279 
280 /** if not already existing, adds col to col pool and captures it */
281 SCIP_RETCODE GCGcolpoolAddCol(
282  GCG_COLPOOL* colpool, /**< col pool */
283  GCG_COL* col, /**< column to add */
284  SCIP_Bool* success /**< pointer to store if col was added */
285  )
286 {
287  assert(colpool != NULL);
288  assert(col != NULL);
289  assert(success != NULL);
290 
291  *success = FALSE;
292 
293  /* check in hash table, if col already exists in the pool */
294  if( SCIPhashtableRetrieve(colpool->hashtable, (void*)col) == NULL )
295  {
296  SCIP_CALL( GCGcolpoolAddNewCol(colpool, col) );
297  *success = TRUE;
298  }
299 
300  return SCIP_OKAY;
301 }
302 
303 /** adds row to col pool and captures it; doesn't check for multiple cols */
304 SCIP_RETCODE GCGcolpoolAddNewCol(
305  GCG_COLPOOL* colpool, /**< col pool */
306  GCG_COL* col /**< column to add */
307  )
308 {
309 
310  assert(colpool != NULL);
311  assert(col != NULL);
312 
313  col->pos = colpool->ncols;
314 
315  /* add col to the pool */
316  SCIP_CALL( colpoolEnsureColsMem(colpool, colpool->ncols+1) );
317  colpool->cols[colpool->ncols] = col;
318  colpool->ncols++;
319  colpool->maxncols = MAX(colpool->maxncols, colpool->ncols);
320 
321  /* insert col in the hash table */
322  SCIP_CALL( SCIPhashtableInsert(colpool->hashtable, (void*)col) );
323 
324  return SCIP_OKAY;
325 }
326 
327 /** removes the LP row from the col pool */
328 SCIP_RETCODE GCGcolpoolDelCol(
329  GCG_COLPOOL* colpool, /**< col pool */
330  GCG_COL* col, /**< col to remove */
331  SCIP_Bool freecol /**< should the col be freed? */
332  )
333 {
334  assert(colpool != NULL);
335  assert(col != NULL);
336 
337  /* find the col in hash table */
338  col = (GCG_COL*)SCIPhashtableRetrieve(colpool->hashtable, (void*)col);
339  if( col == NULL )
340  {
341  SCIPerrorMessage("col %p is not existing in colpool %p\n", col, colpool);
342  return SCIP_INVALIDDATA;
343  }
344 
345  SCIP_CALL( colpoolDelCol(colpool, col, freecol) );
346 
347  return SCIP_OKAY;
348 }
349 
350 
351 /** prices cols of the col pool */
352 SCIP_RETCODE GCGcolpoolPrice(
353  SCIP* scip, /**< SCIP data structure */
354  GCG_COLPOOL* colpool, /**< col pool */
355  GCG_PRICESTORE* pricestore, /**< GCG price storage */
356  SCIP_SOL* sol, /**< solution to be separated (or NULL for LP-solution) */
357  SCIP_Bool* foundvars /**< pointer to store the result of the separation call */
358  )
359 {
360  GCG_COL* col;
361  int firstunproc;
362  int oldncols;
363  int c;
364 
365  assert(colpool != NULL);
366  assert(colpool->firstunprocessed <= colpool->ncols);
367  assert(colpool->firstunprocessedsol <= colpool->ncols);
368  assert(foundvars != NULL);
369  assert(SCIPnodeGetType(SCIPgetCurrentNode(colpool->scip)) != SCIP_NODETYPE_PROBINGNODE);
370 
371  colpool->ncalls++;
372 
373  SCIPdebugMessage("separating%s col pool %p with %d cols, beginning with col %d\n", ( sol == NULL ) ? "" : " solution from", (void*)colpool, colpool->ncols, firstunproc);
374 
375  /* start timing */
376  SCIP_CALL( SCIPstartClock(colpool->scip, colpool->poolclock) );
377 
378  /* remember the current total number of found cols */
379  oldncols = GCGpricestoreGetNCols(pricestore);
380 
381  /* process all unprocessed cols in the pool */
382  *foundvars = FALSE;
383 
384  for( c = colpool->ncols - 1; c >= 0; --c )
385  {
386  SCIP_Real redcost;
387 
388  col = colpool->cols[c];
389  assert(col != NULL);
390  assert(col->pos == c);
391 
392  redcost = GCGcolGetRedcost(col);
393 
394  if( SCIPisDualfeasNegative(scip, redcost) )
395  {
396  /* insert col in separation storage */
397  SCIPdebugMessage(" -> col %p from the col pool (redcost: %g)\n",
398  (void*)col, redcost );
399 
400  SCIP_CALL( GCGpricestoreAddCol(scip, pricestore, col, FALSE) );
401 
402  SCIP_CALL( colpoolDelCol(colpool, col, FALSE) );
403 
404  col->age = 0;
405  }
406  else
407  {
408  col->age++;
409  if( GCGcolIsAged(col, colpool->agelimit) )
410  {
411  SCIP_CALL( colpoolDelCol(colpool, col, TRUE) );
412  }
413  }
414  }
415 
416  /* update the number of found cols */
417  colpool->ncolsfound += GCGpricestoreGetNCols(pricestore) - oldncols; /*lint !e776*/
418 
419  if( GCGpricestoreGetNCols(pricestore) - oldncols > 0 )
420  *foundvars = TRUE;
421 
422  /* stop timing */
423  SCIP_CALL( SCIPstopClock(colpool->scip, colpool->poolclock) );
424 
425  return SCIP_OKAY;
426 }
427 
428 /** update node at which columns of column pool are feasible */
429 SCIP_RETCODE GCGcolpoolUpdateNode(
430  GCG_COLPOOL* colpool /**< col pool */
431  )
432 {
433  assert(colpool != NULL);
434  assert(SCIPnodeGetType(SCIPgetCurrentNode(colpool->scip)) != SCIP_NODETYPE_PROBINGNODE);
435 
436  if( colpool->nodenr < 0 )
437  {
438  colpool->nodenr = SCIPnodeGetNumber(SCIPgetCurrentNode(colpool->scip));
439  }
440  else if( colpool->nodenr != SCIPnodeGetNumber(SCIPgetCurrentNode(colpool->scip)) )
441  {
442  SCIP_CALL( GCGcolpoolClear(colpool) );
443 
444  colpool->nodenr = SCIPnodeGetNumber(SCIPgetCurrentNode(colpool->scip));
445  }
446 
447  return SCIP_OKAY;
448 }
449 
450 /** update reduced cost and compute master coefs of columns in column pool */
452  GCG_COLPOOL* colpool /**< col pool */
453  )
454 {
455  GCG_COL** cols;
456  int ncols;
457 
458  int i;
459 
460  ncols = GCGcolpoolGetNCols(colpool);
461  cols = GCGcolpoolGetCols(colpool);
462 
463  for( i = 0; i < ncols; ++i )
464  {
465  GCG_COL* col;
466  SCIP_Real redcost;
467 
468  col = cols[i];
469 
470  SCIP_CALL( GCGcomputeColMastercoefs(colpool->scip, col) );
471 
472  redcost = GCGcomputeRedCostGcgCol(colpool->scip, colpool->infarkas, col, NULL);
473 
474  GCGcolUpdateRedcost(col, redcost, FALSE);
475  }
476 
477  return SCIP_OKAY;
478 }
479 
480 /** gets number of cols in the col pool */
482  GCG_COLPOOL* colpool /**< col pool */
483  )
484 {
485  colpool->infarkas = TRUE;
486 }
487 
488 /** gets number of cols in the col pool */
490  GCG_COLPOOL* colpool /**< col pool */
491  )
492 {
493  colpool->infarkas = FALSE;
494 }
495 
496 
497 /** gets array of cols in the col pool */
499  GCG_COLPOOL* colpool /**< col pool */
500  )
501 {
502  assert(colpool != NULL);
503 
504  return colpool->cols;
505 }
506 
507 /** gets number of cols in the col pool */
509  GCG_COLPOOL* colpool /**< col pool */
510  )
511 {
512  assert(colpool != NULL);
513 
514  return colpool->ncols;
515 }
516 
517 /** gets maximum number of cols that were stored in the col pool at the same time */
519  GCG_COLPOOL* colpool /**< col pool */
520  )
521 {
522  assert(colpool != NULL);
523 
524  return colpool->maxncols;
525 }
526 
527 /** gets time in seconds used for separating cols from the pool */
529  GCG_COLPOOL* colpool /**< col pool */
530  )
531 {
532  assert(colpool != NULL);
533 
534  return SCIPgetClockTime(colpool->scip, colpool->poolclock);
535 }
536 
537 /** get number of times, the col pool was separated */
538 SCIP_Longint GCGcolpoolGetNCalls(
539  GCG_COLPOOL* colpool /**< col pool */
540  )
541 {
542  assert(colpool != NULL);
543 
544  return colpool->ncalls;
545 }
546 
547 /** get total number of cols that were separated from the col pool */
549  GCG_COLPOOL* colpool /**< col pool */
550  )
551 {
552  assert(colpool != NULL);
553 
554  return colpool->ncolsfound;
555 }
SCIP_Longint GCGcolpoolGetNColsFound(GCG_COLPOOL *colpool)
Definition: colpool.c:548
SCIP_RETCODE GCGcolpoolDelCol(GCG_COLPOOL *colpool, GCG_COL *col, SCIP_Bool freecol)
Definition: colpool.c:328
SCIP_Real GCGcolpoolGetTime(GCG_COLPOOL *colpool)
Definition: colpool.c:528
void GCGcolUpdateRedcost(GCG_COL *gcgcol, SCIP_Real redcost, SCIP_Bool growold)
Definition: gcgcol.c:375
int GCGpricestoreGetNCols(GCG_PRICESTORE *pricestore)
GCG_COL ** GCGcolpoolGetCols(GCG_COLPOOL *colpool)
Definition: colpool.c:498
SCIP_Real GCGcolGetRedcost(GCG_COL *gcgcol)
Definition: gcgcol.c:359
static SCIP_RETCODE colpoolEnsureColsMem(GCG_COLPOOL *colpool, int num)
Definition: colpool.c:130
#define GCG_HASHSIZE_COLPOOLS_SMALL
Definition: colpool.c:47
int firstunprocessedsol
int GCGcolpoolGetMaxNCols(GCG_COLPOOL *colpool)
Definition: colpool.c:518
SCIP_Bool GCGcolIsAged(GCG_COL *col, int agelimit)
Definition: gcgcol.c:640
void GCGcolpoolEndFarkas(GCG_COLPOOL *colpool)
Definition: colpool.c:489
public methods for working with gcg columns
SCIP_RETCODE GCGcolpoolUpdateRedcost(GCG_COLPOOL *colpool)
Definition: colpool.c:451
int probnr
Definition: struct_gcgcol.h:53
#define GCG_USESMALLTABLES
Definition: colpool.c:46
int firstunprocessed
datastructures for storing priced cols
GCG_COL ** cols
GCG variable pricer.
static SCIP_RETCODE colpoolDelCol(GCG_COLPOOL *colpool, GCG_COL *col, SCIP_Bool freecol)
Definition: colpool.c:223
int GCGcolpoolGetNCols(GCG_COLPOOL *colpool)
Definition: colpool.c:508
static SCIP_DECL_HASHGETKEY(hashGetKeyCol)
Definition: colpool.c:56
SCIP_Real * vals
Definition: struct_gcgcol.h:55
internal methods for storing cols in a col pool
static SCIP_DECL_HASHKEYEQ(hashKeyEqCol)
Definition: colpool.c:69
void GCGcolpoolStartFarkas(GCG_COLPOOL *colpool)
Definition: colpool.c:481
SCIP_Longint ncalls
static SCIP_DECL_HASHKEYVAL(hashKeyValCol)
Definition: colpool.c:107
SCIP_RETCODE GCGpricestoreAddCol(SCIP *scip, GCG_PRICESTORE *pricestore, GCG_COL *col, SCIP_Bool forcecol)
SCIP_Longint GCGcolpoolGetNCalls(GCG_COLPOOL *colpool)
Definition: colpool.c:538
SCIP_Bool isray
Definition: struct_gcgcol.h:58
void GCGfreeGcgCol(GCG_COL **gcgcol)
Definition: gcgcol.c:135
SCIP_RETCODE GCGcolpoolAddNewCol(GCG_COLPOOL *colpool, GCG_COL *col)
Definition: colpool.c:304
SCIP_Longint ncolsfound
SCIP_RETCODE GCGcolpoolAddCol(GCG_COLPOOL *colpool, GCG_COL *col, SCIP_Bool *success)
Definition: colpool.c:281
SCIP_RETCODE GCGcolpoolFree(SCIP *scip, GCG_COLPOOL **colpool)
Definition: colpool.c:195
SCIP_RETCODE GCGcolpoolClear(GCG_COLPOOL *colpool)
Definition: colpool.c:262
SCIP_Longint nodenr
SCIP_RETCODE GCGcolpoolUpdateNode(GCG_COLPOOL *colpool)
Definition: colpool.c:429
SCIP_Real GCGcomputeRedCostGcgCol(SCIP *scip, SCIP_Bool infarkas, GCG_COL *gcgcol, SCIP_Real *objvalptr)
#define GCG_HASHSIZE_COLPOOLS
Definition: colpool.c:48
data structures for storing cols in a col pool
SCIP_CLOCK * poolclock
SCIP_RETCODE GCGcolpoolPrice(SCIP *scip, GCG_COLPOOL *colpool, GCG_PRICESTORE *pricestore, SCIP_SOL *sol, SCIP_Bool *foundvars)
Definition: colpool.c:352
SCIP_HASHTABLE * hashtable
SCIP_RETCODE GCGcolpoolCreate(SCIP *scip, GCG_COLPOOL **colpool, int agelimit)
Definition: colpool.c:159
methods for storing priced cols (based on SCIP's separation storage)
SCIP_RETCODE GCGcomputeColMastercoefs(SCIP *scip, GCG_COL *gcgcol)
SCIP_Bool infarkas
SCIP_VAR ** vars
Definition: struct_gcgcol.h:54