Scippy

GCG

Branch-and-Price & Column Generation for Everyone

solver_knapsack.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 solver_knapsack.c
29  * @brief knapsack solver for pricing problems
30  * @author Gerald Gamrath
31  * @author Martin Bergner
32  * @author Christian Puchert
33  */
34 
35 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
36 
37 #include <assert.h>
38 #include <string.h>
39 
40 #include "solver_knapsack.h"
41 #include "scip/cons_linear.h"
42 #include "scip/cons_knapsack.h"
43 #include "pricer_gcg.h"
44 #include "pub_solver.h"
45 #include "relax_gcg.h"
46 #include "pub_gcgcol.h"
47 
48 #define SOLVER_NAME "knapsack"
49 #define SOLVER_DESC "knapsack solver for pricing problems"
50 #define SOLVER_PRIORITY 200
51 
52 #define SOLVER_HEURENABLED FALSE /**< indicates whether the heuristic solving method of the solver should be enabled */
53 #define SOLVER_EXACTENABLED TRUE /**< indicates whether the exact solving method of the solver should be enabled */
54 
55 /* knapsack pricing solver needs no solverdata */
56 /* struct GCG_SolverData {}; */
57 
58 
59 /*
60  * Local methods
61  */
62 
63 /** solve the pricing problem as a knapsack problem, either exactly or approximately */
64 static
65 SCIP_RETCODE solveKnapsack(
66  SCIP_Bool exactly, /**< should the pricing problem be solved to optimality or heuristically? */
67  SCIP* scip, /**< master problem SCIP data structure */
68  SCIP* pricingprob, /**< pricing problem SCIP data structure */
69  GCG_SOLVER* solver, /**< solver data structure */
70  int probnr, /**< problem number */
71  SCIP_Real* lowerbound, /**< pointer to store lower bound */
72  GCG_PRICINGSTATUS* status /**< pointer to store pricing problem status */
73  )
74 { /*lint -e715 */
75  SCIP_CONS* cons;
76  SCIP_CONSHDLR* conshdlr;
77  SCIP_VAR** consvars;
78  SCIP_Longint* consvals;
79  int nconsvars;
80  SCIP_VAR** solvars;
81  SCIP_Real* solvals;
82  int nsolvars;
83  SCIP_VAR** pricingprobvars;
84  int npricingprobvars;
85  int nconss;
86 
87  int nitems;
88  SCIP_Longint* weights;
89  SCIP_Real* profits;
90  SCIP_Real* ubs;
91  SCIP_Longint capacity;
92  SCIP_Longint prelcapacity;
93  int* items;
94  int* solitems;
95  int nsolitems;
96  int* nonsolitems;
97  int nnonsolitems;
98  SCIP_Real solval;
99  SCIP_Bool success;
100  GCG_COL* col;
101  SCIP_Bool inferbounds;
102  int i;
103  int j;
104  int k;
105 
106  /* check preconditions */
107  assert(scip != NULL);
108  assert(pricingprob != NULL);
109  assert(solver != NULL);
110  assert(lowerbound != NULL);
111  assert(status != NULL);
112 
113  assert(SCIPgetObjsense(pricingprob) == SCIP_OBJSENSE_MINIMIZE);
114 
115  pricingprobvars = SCIPgetVars(pricingprob);
116  npricingprobvars = SCIPgetNVars(pricingprob);
117 
118  SCIPdebugMessage("Knapsack solver -- checking prerequisites\n");
119 
121 
122  /* check prerequisites: the pricing problem can be solved as a knapsack problem only if
123  * - all variables are nonnegative integer variables
124  * - there is only one constraint, which has infinite lhs and integer rhs
125  */
126  if( SCIPgetNBinVars(pricingprob) + SCIPgetNIntVars(pricingprob) < npricingprobvars )
127  {
128  SCIPdebugMessage(" -> pricing problem has continuous variables\n");
129  return SCIP_OKAY;
130  }
131  for( i = SCIPgetNBinVars(pricingprob); i < SCIPgetNBinVars(pricingprob) + SCIPgetNIntVars(pricingprob); ++i )
132  {
133  if( SCIPisNegative(pricingprob, SCIPvarGetLbLocal(pricingprobvars[i])) )
134  {
135  SCIPdebugMessage(" -> pricing problem has variables with negative lower bounds\n");
136  return SCIP_OKAY;
137  }
138  }
139 
140  nconss = SCIPgetNConss(pricingprob);
141  if( nconss != 1 )
142  {
143  SCIPdebugMessage(" -> pricing problem has more than one constraint\n");
144  return SCIP_OKAY;
145  }
146 
147  cons = SCIPgetConss(pricingprob)[0];
148  assert(cons != NULL);
149 
150  conshdlr = SCIPconsGetHdlr(cons);
151  assert(conshdlr != NULL);
152 
153  consvals = NULL;
154 
155  /*
156  * Check if the constraint is a knapsack constraint, and in that case,
157  * get its variables, their coefficients as well as the capacity
158  *
159  * @note The constraint may be either of type 'linear' or 'knapsack';
160  * the latter might be the case if the pricing problem has already been treated before in the loop
161  * and if the constraint has therefore already been upgraded
162  */
163  if( strcmp(SCIPconshdlrGetName(conshdlr), "linear") == 0 )
164  {
165  SCIP_Real* realconsvals;
166 
167  if( !SCIPisIntegral(pricingprob, SCIPgetRhsLinear(pricingprob, cons)) ||
168  !SCIPisInfinity(pricingprob, - SCIPgetLhsLinear(pricingprob, cons)) )
169  {
170  SCIPdebugMessage(" -> pricing constraint is bounded from below or has fractional rhs\n");
171  return SCIP_OKAY;
172  }
173 
174  consvars = SCIPgetVarsLinear(pricingprob, cons);
175  realconsvals = SCIPgetValsLinear(pricingprob, cons);
176  nconsvars = SCIPgetNVarsLinear(pricingprob, cons);
177 
178  SCIP_CALL( SCIPallocBufferArray(pricingprob, &consvals, nconsvars) );
179 
180  /* Check integrality of coefficients */
181  for( i = 0; i < nconsvars; i++ )
182  {
183  if( !SCIPisIntegral(pricingprob, realconsvals[i]) )
184  {
185  SCIPdebugMessage(" -> pricing constraint has fractional coefficient\n");
186  SCIPfreeBufferArray(pricingprob, &consvals);
187  return SCIP_OKAY;
188  }
189  else
190  consvals[i] = (SCIP_Longint) SCIPfloor(pricingprob, realconsvals[i]);
191  }
192  capacity = (SCIP_Longint) SCIPfloor(pricingprob, SCIPgetRhsLinear(pricingprob, cons));
193 
194  /* Check signs of variable coefficients in constraint and objective;
195  * compute a preliminary capacity, used to deduce upper bounds for unbounded variables
196  */
197  prelcapacity = capacity;
198  inferbounds = FALSE;
199  for( i = 0; i < nconsvars; i++ )
200  {
201  if( SCIPisInfinity(pricingprob, SCIPvarGetUbLocal(consvars[i])) )
202  inferbounds = TRUE;
203 
204  if( consvals[i] < 0 )
205  {
206  /* If a variable has an infinite upper bound, the capacity is not deducible */
207  if( SCIPisInfinity(pricingprob, SCIPvarGetUbLocal(consvars[i])) )
208  {
209  SCIPdebugMessage(" -> variable with negative coefficient has no upper bound\n");
210  SCIPfreeBufferArray(pricingprob, &consvals);
211  return SCIP_OKAY;
212  }
213 
214  /* increase capacity */
215  prelcapacity -= (SCIP_Longint) SCIPfloor(pricingprob, consvals[i] * SCIPvarGetUbLocal(consvars[i]));
216  }
217  }
218 
219  SCIP_CALL( SCIPallocBufferArray(pricingprob, &ubs, nconsvars) );
220 
221  SCIPdebugMessage("Set variable upper bounds\n");
222 
223  /* infer upper bounds for unbounded variables */
224  for( i = 0; i < nconsvars; i++ )
225  {
226  if( inferbounds && SCIPisInfinity(pricingprob, SCIPvarGetUbLocal(consvars[i])) )
227  {
228  ubs[i] = SCIPfloor(pricingprob, ABS((SCIP_Real)prelcapacity/consvals[i]));
229  SCIPdebugMessage(" -> var <%s> %.2f/%"SCIP_LONGINT_FORMAT" = %.2f\n",
230  SCIPvarGetName(consvars[i]), (SCIP_Real)prelcapacity, consvals[i], ubs[i]);
231  }
232  else
233  {
234  ubs[i] = SCIPvarGetUbLocal(consvars[i]);
235  SCIPdebugMessage(" -> var <%s> %.2f\n", SCIPvarGetName(consvars[i]), ubs[i]);
236  }
237 
238  }
239  }
240  else if( strcmp(SCIPconshdlrGetName(conshdlr), "knapsack") == 0 )
241  {
242  SCIP_Longint* consweights = SCIPgetWeightsKnapsack(pricingprob, cons);
243 
244  SCIPdebugMessage("Use knapsack solver – constraint is already of type 'knapsack'\n");
245 
246  consvars = SCIPgetVarsKnapsack(pricingprob, cons);
247  nconsvars = SCIPgetNVarsKnapsack(pricingprob, cons);
248  capacity = SCIPgetCapacityKnapsack(pricingprob, cons);
249 
250  SCIP_CALL( SCIPallocBufferArray(pricingprob, &consvals, nconsvars) );
251  SCIP_CALL( SCIPallocBufferArray(pricingprob, &ubs, nconsvars) );
252  BMScopyMemoryArray(consvals, consweights, nconsvars);
253 
254  for( i = 0; i < nconsvars; ++i )
255  {
256  assert(consvals[i] >= 0);
257  assert(SCIPisGE(pricingprob, SCIPvarGetLbLocal(consvars[i]), 0.0));
258  assert(SCIPisLE(pricingprob, SCIPvarGetUbLocal(consvars[i]), 1.0));
259 
260  ubs[i] = SCIPvarGetUbLocal(consvars[i]);
261  }
262  }
263  else
264  {
265  SCIPdebugMessage(" -> constraint is of unknown type\n");
266  return SCIP_OKAY;
267  }
268 
269  *status = GCG_PRICINGSTATUS_UNKNOWN;
270 
271  /* Count number of knapsack items */
272  SCIPdebugMessage("Count number of knapsack items:\n");
273  nitems = 0;
274  for( i = 0; i < nconsvars; i++ )
275  {
276  assert(!SCIPisInfinity(pricingprob, ubs[i]));
277  SCIPdebugMessage(" -> <%s>: %d+%d\n", SCIPvarGetName(consvars[i]), nitems, (int)(ubs[i] - SCIPvarGetLbLocal(consvars[i]) + 0.5));
278  nitems += (int)(ubs[i] - SCIPvarGetLbLocal(consvars[i]) + 0.5);
279  }
280  SCIPdebugMessage("-> %d items\n", nitems);
281 
282  SCIP_CALL( SCIPallocBufferArray(pricingprob, &solvars, npricingprobvars) );
283  SCIP_CALL( SCIPallocBufferArray(pricingprob, &solvals, npricingprobvars) );
284 
285  SCIP_CALL( SCIPallocBufferArray(pricingprob, &items, nitems) );
286  SCIP_CALL( SCIPallocBufferArray(pricingprob, &weights, nitems) );
287  SCIP_CALL( SCIPallocBufferArray(pricingprob, &profits, nitems) );
288  SCIP_CALL( SCIPallocBufferArray(pricingprob, &solitems, nitems) );
289  SCIP_CALL( SCIPallocBufferArray(pricingprob, &nonsolitems, nitems) );
290 
291  BMSclearMemoryArray(weights, nitems);
292 
293  /* Map variables to knapsack items, and set profits */
294  SCIPdebugMessage("Set knapsack items\n");
295  k = 0;
296  for( i = 0; i < nconsvars; i++ )
297  {
298  assert(!SCIPisInfinity(pricingprob, ubs[i]));
299  for( j = 0; j < (int)(ubs[i] - SCIPvarGetLbLocal(consvars[i]) + 0.5); ++j )
300  {
301  items[k] = i;
302  profits[k] = - SCIPvarGetObj(consvars[i]);
303  SCIPdebugMessage(" -> item %3d: <%s> (index %3d)\n", k, SCIPvarGetName(consvars[i]), i);
304 
305  k++;
306  }
307  }
308  assert(k == nitems);
309 
310  /* Compute knapsack capacity, and set weights */
311  SCIPdebugMessage("Compute knapsack capacity, current capacity = %"SCIP_LONGINT_FORMAT"\n", capacity);
312  for( i = 0; i < nconsvars; i++ )
313  {
314  if( SCIPisEQ(pricingprob, SCIPvarGetUbLocal(consvars[i]), 0.0) )
315  continue;
316  if( SCIPisGE(pricingprob, SCIPvarGetLbLocal(consvars[i]), 1.0) )
317  {
318  SCIPdebugMessage(" -> variable <%s> has coeff %"SCIP_LONGINT_FORMAT" and lb %f --> increase capacity by %"SCIP_LONGINT_FORMAT"\n",
319  SCIPvarGetName(consvars[i]), consvals[i], SCIPvarGetLbLocal(consvars[i]),
320  (SCIP_Longint)SCIPfloor(pricingprob, SCIPvarGetLbLocal(consvars[i]) * consvals[i]));
321  capacity -= (SCIP_Longint)SCIPfloor(pricingprob, SCIPvarGetLbLocal(consvars[i])) * consvals[i];
322  }
323  }
324 
325  SCIPdebugMessage("Compute weights\n");
326 
327  for( k = 0; k < nitems; k++ )
328  {
329  i = items[k];
330  if( consvals[i] > 0 )
331  {
332  weights[k] = consvals[i];
333  SCIPdebugMessage(" -> item %3d: weight = %"SCIP_LONGINT_FORMAT"\n", k, weights[k]);
334  }
335  else
336  {
337  capacity -= consvals[i];
338  weights[k] = -consvals[i];
339  profits[k] *= -1.0;
340 
341  SCIPdebugMessage(" -> item %3d: weight = %"SCIP_LONGINT_FORMAT" (negated from consval = %"SCIP_LONGINT_FORMAT")\n", k, weights[k], consvals[i]);
342  }
343  }
344 
345  SCIPdebugMessage("Knapsack capacity = %"SCIP_LONGINT_FORMAT"\n", capacity);
346 
347  success = TRUE;
348 
349  /* problem is infeasible */
350  if( capacity < 0 )
351  {
352  SCIPdebugMessage("Pricing problem is infeasible\n");
354  goto TERMINATE;
355  }
356  else if( capacity == 0 )
357  {
358  SCIPdebugMessage("Knapsack has zero capacity\n");
359 
360  nsolitems = 0;
361  nnonsolitems = nitems;
362  for( i = 0; i < nitems; ++i )
363  nonsolitems[i] = items[i];
364  }
365  else
366  {
367  SCIPdebugMessage("Solve pricing problem as knapsack\n");
368 
369  /* solve knapsack problem, all result pointers are needed! */
370  if( exactly )
371  {
372  SCIP_CALL( SCIPsolveKnapsackExactly(pricingprob, nitems, weights, profits, capacity, items, solitems,
373  nonsolitems, &nsolitems, &nnonsolitems, &solval, &success ));
374  }
375  else
376  {
377  SCIP_CALL( SCIPsolveKnapsackApproximately(pricingprob, nitems, weights, profits, capacity, items, solitems,
378  nonsolitems, &nsolitems, &nnonsolitems, &solval ));
379  }
380 
381  if( !success )
382  {
383  SCIPwarningMessage(pricingprob, "Knapsack solver could not solve pricing problem!");
384  goto TERMINATE;
385  }
386  else if( exactly )
387  *status = GCG_PRICINGSTATUS_OPTIMAL;
388 
389  SCIPdebugMessage("Knapsack solved, solval = %g\n", solval);
390  }
391 
392  nsolvars = 0;
393 
394  for( i = 0; i < nsolitems; i++ )
395  {
396  assert(consvals[solitems[i]] >= 0 || !SCIPvarIsNegated(consvars[solitems[i]]));
397 
398  if( consvals[solitems[i]] >= 0 && !SCIPvarIsNegated(consvars[solitems[i]]) )
399  {
400  for( j = 0; j < nsolvars; ++j )
401  if( solvars[j] == consvars[solitems[i]] )
402  break;
403 
404  if( j == nsolvars )
405  {
406  solvars[j] = consvars[solitems[i]];
407  solvals[j] = 1.0;
408  ++nsolvars;
409  }
410  else
411  solvals[j] += 1.0;
412  }
413  }
414 
415  for( i = 0; i < nnonsolitems; i++ )
416  {
417  assert(consvals[nonsolitems[i]] >= 0 || !SCIPvarIsNegated(consvars[nonsolitems[i]]));
418 
419  if( consvals[nonsolitems[i]] < 0 || SCIPvarIsNegated(consvars[nonsolitems[i]]) )
420  {
421  SCIP_VAR* solvar = SCIPvarIsNegated(consvars[nonsolitems[i]]) ? SCIPvarGetNegatedVar(consvars[nonsolitems[i]]) : consvars[nonsolitems[i]];
422 
423  for( j = 0; j < nsolvars; ++j )
424  if( solvars[j] == solvar )
425  break;
426 
427  if( j == nsolvars )
428  {
429  solvars[j] = solvar;
430  solvals[j] = 1.0;
431  ++nsolvars;
432  }
433  else
434  solvals[j] += 1.0;
435  }
436  }
437 
438  for( i = 0; i < npricingprobvars; i++ )
439  {
440  if( SCIPisGE(pricingprob, SCIPvarGetLbLocal(pricingprobvars[i]), 1.0) )
441  {
442  for( j = 0; j < nsolvars; ++j )
443  if( solvars[j] == pricingprobvars[i] )
444  break;
445 
446  if( j == nsolvars )
447  {
448  solvars[j] = pricingprobvars[i];
449  solvals[j] = SCIPfloor(pricingprob, SCIPvarGetLbLocal(pricingprobvars[i]));
450  ++nsolvars;
451  }
452  else
453  solvals[j] += SCIPfloor(pricingprob, SCIPvarGetLbLocal(pricingprobvars[i]));
454  }
455  }
456 
457  SCIP_CALL( GCGcreateGcgCol(pricingprob, &col, probnr, solvars, solvals, nsolvars, FALSE, SCIPinfinity(pricingprob)) );
458  SCIP_CALL( GCGpricerAddCol(scip, col) );
459 
460  solval = 0.0;
461 
462  for( i = 0; i < nsolvars; ++i )
463  solval += solvals[i] * SCIPvarGetObj(solvars[i]);
464 
465  *lowerbound = exactly ? solval : -SCIPinfinity(pricingprob);
466 
467  TERMINATE:
468  SCIPfreeBufferArray(pricingprob, &nonsolitems);
469  SCIPfreeBufferArray(pricingprob, &solitems);
470  SCIPfreeBufferArray(pricingprob, &profits);
471  SCIPfreeBufferArray(pricingprob, &weights);
472  SCIPfreeBufferArray(pricingprob, &items);
473  SCIPfreeBufferArray(pricingprob, &solvals);
474  SCIPfreeBufferArray(pricingprob, &solvars);
475  SCIPfreeBufferArray(pricingprob, &ubs);
476  SCIPfreeBufferArray(pricingprob, &consvals);
477 
478  return SCIP_OKAY;
479 }
480 
481 /*
482  * Callback methods for pricing problem solver
483  */
484 
485 #define solverFreeKnapsack NULL
486 #define solverInitsolKnapsack NULL
487 #define solverExitsolKnapsack NULL
488 #define solverInitKnapsack NULL
489 #define solverExitKnapsack NULL
490 #define solverUpdateKnapsack NULL
491 
492 /** exact solving method for knapsack solver */
493 static
494 GCG_DECL_SOLVERSOLVE(solverSolveKnapsack)
495 { /*lint --e{715}*/
496 
497  /* solve the knapsack problem exactly */
498  SCIP_CALL( solveKnapsack(TRUE, scip, pricingprob, solver, probnr, lowerbound, status) );
499 
500  return SCIP_OKAY;
501 }
502 
503 
504 /** heuristic solving method of knapsack solver */
505 static
506 GCG_DECL_SOLVERSOLVEHEUR(solverSolveHeurKnapsack)
507 { /*lint --e{715}*/
508 
509  /* solve the knapsack problem approximately */
510  SCIP_CALL( solveKnapsack(FALSE, scip, pricingprob, solver, probnr, lowerbound, status) );
511 
512  return SCIP_OKAY;
513 }
514 
515 
516 /** creates the knapsack solver for pricing problems and includes it in GCG */
518  SCIP* scip /**< SCIP data structure */
519  )
520 {
523  solverUpdateKnapsack, solverSolveKnapsack, solverSolveHeurKnapsack,
526 
527  return SCIP_OKAY;
528 }
SCIP_RETCODE GCGcreateGcgCol(SCIP *pricingprob, GCG_COL **gcgcol, int probnr, SCIP_VAR **vars, SCIP_Real *vals, int nvars, SCIP_Bool isray, SCIP_Real redcost)
Definition: gcgcol.c:52
#define SOLVER_NAME
#define SOLVER_EXACTENABLED
public methods for working with gcg columns
#define SOLVER_PRIORITY
GCG variable pricer.
enum GCG_PricingStatus GCG_PRICINGSTATUS
static SCIP_RETCODE solveKnapsack(SCIP_Bool exactly, SCIP *scip, SCIP *pricingprob, GCG_SOLVER *solver, int probnr, SCIP_Real *lowerbound, GCG_PRICINGSTATUS *status)
knapsack solver for pricing problems
@ GCG_PRICINGSTATUS_NOTAPPLICABLE
@ GCG_PRICINGSTATUS_INFEASIBLE
@ GCG_PRICINGSTATUS_OPTIMAL
SCIP_RETCODE GCGpricerIncludeSolver(SCIP *scip, const char *name, const char *desc, int priority, SCIP_Bool heurenabled, SCIP_Bool exactenabled, GCG_DECL_SOLVERUPDATE((*solverupdate)), GCG_DECL_SOLVERSOLVE((*solversolve)), GCG_DECL_SOLVERSOLVEHEUR((*solversolveheur)), GCG_DECL_SOLVERFREE((*solverfree)), GCG_DECL_SOLVERINIT((*solverinit)), GCG_DECL_SOLVEREXIT((*solverexit)), GCG_DECL_SOLVERINITSOL((*solverinitsol)), GCG_DECL_SOLVEREXITSOL((*solverexitsol)), GCG_SOLVERDATA *solverdata)
#define SOLVER_DESC
static GCG_DECL_SOLVERSOLVE(solverSolveKnapsack)
#define solverExitsolKnapsack
GCG relaxator.
#define solverInitsolKnapsack
#define solverUpdateKnapsack
#define solverExitKnapsack
@ GCG_PRICINGSTATUS_UNKNOWN
SCIP_RETCODE GCGincludeSolverKnapsack(SCIP *scip)
#define solverInitKnapsack
SCIP_RETCODE GCGpricerAddCol(SCIP *scip, GCG_COL *col)
static GCG_DECL_SOLVERSOLVEHEUR(solverSolveHeurKnapsack)
#define SOLVER_HEURENABLED
#define solverFreeKnapsack