1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 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 visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file cons_knapsack.c
17 * @ingroup DEFPLUGINS_CONS
18 * @brief Constraint handler for knapsack constraints of the form \f$a^T x \le b\f$, x binary and \f$a \ge 0\f$.
19 * @author Tobias Achterberg
20 * @author Xin Liu
21 * @author Kati Wolter
22 * @author Michael Winkler
23 * @author Tobias Fischer
24 */
25
26 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
27
28 #include "blockmemshell/memory.h"
29 #include "scip/cons_knapsack.h"
30 #include "scip/cons_linear.h"
31 #include "scip/cons_logicor.h"
32 #include "scip/cons_setppc.h"
33 #include "scip/pub_cons.h"
34 #include "scip/pub_event.h"
35 #include "scip/pub_implics.h"
36 #include "scip/pub_lp.h"
37 #include "scip/pub_message.h"
38 #include "scip/pub_misc.h"
39 #include "scip/pub_misc_select.h"
40 #include "scip/pub_misc_sort.h"
41 #include "scip/pub_sepa.h"
42 #include "scip/pub_var.h"
43 #include "scip/scip_branch.h"
44 #include "scip/scip_conflict.h"
45 #include "scip/scip_cons.h"
46 #include "scip/scip_copy.h"
47 #include "scip/scip_cut.h"
48 #include "scip/scip_event.h"
49 #include "scip/scip_general.h"
50 #include "scip/scip_lp.h"
51 #include "scip/scip_mem.h"
52 #include "scip/scip_message.h"
53 #include "scip/scip_numerics.h"
54 #include "scip/scip_param.h"
55 #include "scip/scip_prob.h"
56 #include "scip/scip_probing.h"
57 #include "scip/scip_sol.h"
58 #include "scip/scip_solvingstats.h"
59 #include "scip/scip_tree.h"
60 #include "scip/scip_var.h"
61 #include <ctype.h>
62 #include <string.h>
63
64 #ifdef WITH_CARDINALITY_UPGRADE
65 #include "scip/cons_cardinality.h"
66 #endif
67
68 /* constraint handler properties */
69 #define CONSHDLR_NAME "knapsack"
70 #define CONSHDLR_DESC "knapsack constraint of the form a^T x <= b, x binary and a >= 0"
71 #define CONSHDLR_SEPAPRIORITY +600000 /**< priority of the constraint handler for separation */
72 #define CONSHDLR_ENFOPRIORITY -600000 /**< priority of the constraint handler for constraint enforcing */
73 #define CONSHDLR_CHECKPRIORITY -600000 /**< priority of the constraint handler for checking feasibility */
74 #define CONSHDLR_SEPAFREQ 0 /**< frequency for separating cuts; zero means to separate only in the root node */
75 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
76 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
77 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
78 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
79 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
80 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
81 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
82
83 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS
84 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
85
86 #define EVENTHDLR_NAME "knapsack"
87 #define EVENTHDLR_DESC "bound change event handler for knapsack constraints"
88 #define EVENTTYPE_KNAPSACK SCIP_EVENTTYPE_LBCHANGED \
89 | SCIP_EVENTTYPE_UBTIGHTENED \
90 | SCIP_EVENTTYPE_VARFIXED \
91 | SCIP_EVENTTYPE_VARDELETED \
92 | SCIP_EVENTTYPE_IMPLADDED /**< variable events that should be caught by the event handler */
93
94 #define LINCONSUPGD_PRIORITY +100000 /**< priority of the constraint handler for upgrading of linear constraints */
95
96 #define MAX_USECLIQUES_SIZE 1000 /**< maximal number of items in knapsack where clique information is used */
97 #define MAX_ZEROITEMS_SIZE 10000 /**< maximal number of items to store in the zero list in preprocessing */
98
99 #define KNAPSACKRELAX_MAXDELTA 0.1 /**< maximal allowed rounding distance for scaling in knapsack relaxation */
100 #define KNAPSACKRELAX_MAXDNOM 1000LL /**< maximal allowed denominator in knapsack rational relaxation */
101 #define KNAPSACKRELAX_MAXSCALE 1000.0 /**< maximal allowed scaling factor in knapsack rational relaxation */
102
103 #define DEFAULT_SEPACARDFREQ 1 /**< multiplier on separation frequency, how often knapsack cuts are separated */
104 #define DEFAULT_MAXROUNDS 5 /**< maximal number of separation rounds per node (-1: unlimited) */
105 #define DEFAULT_MAXROUNDSROOT -1 /**< maximal number of separation rounds in the root node (-1: unlimited) */
106 #define DEFAULT_MAXSEPACUTS 50 /**< maximal number of cuts separated per separation round */
107 #define DEFAULT_MAXSEPACUTSROOT 200 /**< maximal number of cuts separated per separation round in the root node */
108 #define DEFAULT_MAXCARDBOUNDDIST 0.0 /**< maximal relative distance from current node's dual bound to primal bound compared
109 * to best node's dual bound for separating knapsack cuts */
110 #define DEFAULT_DISAGGREGATION TRUE /**< should disaggregation of knapsack constraints be allowed in preprocessing? */
111 #define DEFAULT_SIMPLIFYINEQUALITIES TRUE/**< should presolving try to simplify knapsacks */
112 #define DEFAULT_NEGATEDCLIQUE TRUE /**< should negated clique information be used in solving process */
113
114 #define MAXABSVBCOEF 1e+5 /**< maximal absolute coefficient in variable bounds used for knapsack relaxation */
115 #define USESUPADDLIFT FALSE /**< should lifted minimal cover inequalities using superadditive up-lifting be separated in addition */
116
117 #define DEFAULT_PRESOLUSEHASHING TRUE /**< should hash table be used for detecting redundant constraints in advance */
118 #define HASHSIZE_KNAPSACKCONS 500 /**< minimal size of hash table in linear constraint tables */
119
120 #define DEFAULT_PRESOLPAIRWISE TRUE /**< should pairwise constraint comparison be performed in presolving? */
121 #define NMINCOMPARISONS 200000 /**< number for minimal pairwise presolving comparisons */
122 #define MINGAINPERNMINCOMPARISONS 1e-06 /**< minimal gain per minimal pairwise presolving comparisons to repeat pairwise
123 * comparison round */
124 #define DEFAULT_DUALPRESOLVING TRUE /**< should dual presolving steps be performed? */
125 #define DEFAULT_DETECTCUTOFFBOUND TRUE /**< should presolving try to detect constraints parallel to the objective
126 * function defining an upper bound and prevent these constraints from
127 * entering the LP */
128 #define DEFAULT_DETECTLOWERBOUND TRUE /**< should presolving try to detect constraints parallel to the objective
129 * function defining a lower bound and prevent these constraints from
130 * entering the LP */
131 #define DEFAULT_CLIQUEEXTRACTFACTOR 0.5 /**< lower clique size limit for greedy clique extraction algorithm (relative to largest clique) */
132 #define MAXCOVERSIZEITERLEWI 1000 /**< maximal size for which LEWI are iteratively separated by reducing the feasible set */
133
134 #define DEFAULT_USEGUBS FALSE /**< should GUB information be used for separation? */
135 #define GUBCONSGROWVALUE 6 /**< memory growing value for GUB constraint array */
136 #define GUBSPLITGNC1GUBS FALSE /**< should GNC1 GUB conss without F vars be split into GOC1 and GR GUB conss? */
137 #define DEFAULT_CLQPARTUPDATEFAC 1.5 /**< factor on the growth of global cliques to decide when to update a previous
138 * (negated) clique partition (used only if updatecliquepartitions is set to TRUE) */
139 #define DEFAULT_UPDATECLIQUEPARTITIONS FALSE /**< should clique partition information be updated when old partition seems outdated? */
140 #define MAXNCLIQUEVARSCOMP 1000000 /**< limit on number of pairwise comparisons in clique partitioning algorithm */
141 #ifdef WITH_CARDINALITY_UPGRADE
142 #define DEFAULT_UPGDCARDINALITY FALSE /**< if TRUE then try to update knapsack constraints to cardinality constraints */
143 #endif
144
145 /* @todo maybe use event SCIP_EVENTTYPE_VARUNLOCKED to decide for another dual-presolving run on a constraint */
146
147 /*
148 * Data structures
149 */
150
151 /** constraint handler data */
152 struct SCIP_ConshdlrData
153 {
154 int* ints1; /**< cleared memory array, all entries are set to zero in initpre, if you use this
155 * you have to clear it at the end, exists only in presolving stage */
156 int* ints2; /**< cleared memory array, all entries are set to zero in initpre, if you use this
157 * you have to clear it at the end, exists only in presolving stage */
158 SCIP_Longint* longints1; /**< cleared memory array, all entries are set to zero in initpre, if you use this
159 * you have to clear it at the end, exists only in presolving stage */
160 SCIP_Longint* longints2; /**< cleared memory array, all entries are set to zero in initpre, if you use this
161 * you have to clear it at the end, exists only in presolving stage */
162 SCIP_Bool* bools1; /**< cleared memory array, all entries are set to zero in initpre, if you use this
163 * you have to clear it at the end, exists only in presolving stage */
164 SCIP_Bool* bools2; /**< cleared memory array, all entries are set to zero in initpre, if you use this
165 * you have to clear it at the end, exists only in presolving stage */
166 SCIP_Bool* bools3; /**< cleared memory array, all entries are set to zero in initpre, if you use this
167 * you have to clear it at the end, exists only in presolving stage */
168 SCIP_Bool* bools4; /**< cleared memory array, all entries are set to zero in initpre, if you use this
169 * you have to clear it at the end, exists only in presolving stage */
170 SCIP_Real* reals1; /**< cleared memory array, all entries are set to zero in consinit, if you use this
171 * you have to clear it at the end */
172 int ints1size; /**< size of ints1 array */
173 int ints2size; /**< size of ints2 array */
174 int longints1size; /**< size of longints1 array */
175 int longints2size; /**< size of longints2 array */
176 int bools1size; /**< size of bools1 array */
177 int bools2size; /**< size of bools2 array */
178 int bools3size; /**< size of bools3 array */
179 int bools4size; /**< size of bools4 array */
180 int reals1size; /**< size of reals1 array */
181 SCIP_EVENTHDLR* eventhdlr; /**< event handler for bound change events */
182 SCIP_Real maxcardbounddist; /**< maximal relative distance from current node's dual bound to primal bound compared
183 * to best node's dual bound for separating knapsack cuts */
184 int sepacardfreq; /**< multiplier on separation frequency, how often knapsack cuts are separated */
185 int maxrounds; /**< maximal number of separation rounds per node (-1: unlimited) */
186 int maxroundsroot; /**< maximal number of separation rounds in the root node (-1: unlimited) */
187 int maxsepacuts; /**< maximal number of cuts separated per separation round */
188 int maxsepacutsroot; /**< maximal number of cuts separated per separation round in the root node */
189 SCIP_Bool disaggregation; /**< should disaggregation of knapsack constraints be allowed in preprocessing? */
190 SCIP_Bool simplifyinequalities;/**< should presolving try to cancel down or delete coefficients in inequalities */
191 SCIP_Bool negatedclique; /**< should negated clique information be used in solving process */
192 SCIP_Bool presolpairwise; /**< should pairwise constraint comparison be performed in presolving? */
193 SCIP_Bool presolusehashing; /**< should hash table be used for detecting redundant constraints in advance */
194 SCIP_Bool dualpresolving; /**< should dual presolving steps be performed? */
195 SCIP_Bool usegubs; /**< should GUB information be used for separation? */
196 SCIP_Bool detectcutoffbound; /**< should presolving try to detect constraints parallel to the objective
197 * function defining an upper bound and prevent these constraints from
198 * entering the LP */
199 SCIP_Bool detectlowerbound; /**< should presolving try to detect constraints parallel to the objective
200 * function defining a lower bound and prevent these constraints from
201 * entering the LP */
202 SCIP_Bool updatecliquepartitions; /**< should clique partition information be updated when old partition seems outdated? */
203 SCIP_Real cliqueextractfactor;/**< lower clique size limit for greedy clique extraction algorithm (relative to largest clique) */
204 SCIP_Real clqpartupdatefac; /**< factor on the growth of global cliques to decide when to update a previous
205 * (negated) clique partition (used only if updatecliquepartitions is set to TRUE) */
206 #ifdef WITH_CARDINALITY_UPGRADE
207 SCIP_Bool upgdcardinality; /**< if TRUE then try to update knapsack constraints to cardinality constraints */
208 SCIP_Bool upgradedcard; /**< whether we have already upgraded knapsack constraints to cardinality constraints */
209 #endif
210 };
211
212
213 /** constraint data for knapsack constraints */
214 struct SCIP_ConsData
215 {
216 SCIP_VAR** vars; /**< variables in knapsack constraint */
217 SCIP_Longint* weights; /**< weights of variables in knapsack constraint */
218 SCIP_EVENTDATA** eventdata; /**< event data for bound change events of the variables */
219 int* cliquepartition; /**< clique indices of the clique partition */
220 int* negcliquepartition; /**< clique indices of the negated clique partition */
221 SCIP_ROW* row; /**< corresponding LP row */
222 int nvars; /**< number of variables in knapsack constraint */
223 int varssize; /**< size of vars, weights, and eventdata arrays */
224 int ncliques; /**< number of cliques in the clique partition */
225 int nnegcliques; /**< number of cliques in the negated clique partition */
226 int ncliqueslastnegpart;/**< number of global cliques the last time a negated clique partition was computed */
227 int ncliqueslastpart; /**< number of global cliques the last time a clique partition was computed */
228 SCIP_Longint capacity; /**< capacity of knapsack */
229 SCIP_Longint weightsum; /**< sum of all weights */
230 SCIP_Longint onesweightsum; /**< sum of weights of variables fixed to one */
231 unsigned int presolvedtiming:5; /**< max level in which the knapsack constraint is already presolved */
232 unsigned int sorted:1; /**< are the knapsack items sorted by weight? */
233 unsigned int cliquepartitioned:1;/**< is the clique partition valid? */
234 unsigned int negcliquepartitioned:1;/**< is the negated clique partition valid? */
235 unsigned int merged:1; /**< are the constraint's equal variables already merged? */
236 unsigned int cliquesadded:1; /**< were the cliques of the knapsack already added to clique table? */
237 unsigned int varsdeleted:1; /**< were variables deleted after last cleanup? */
238 unsigned int existmultaggr:1; /**< does this constraint contain multi-aggregations */
239 };
240
241 /** event data for bound changes events */
242 struct SCIP_EventData
243 {
244 SCIP_CONS* cons; /**< knapsack constraint to process the bound change for */
245 SCIP_Longint weight; /**< weight of variable */
246 int filterpos; /**< position of event in variable's event filter */
247 };
248
249
250 /** data structure to combine two sorting key values */
251 struct sortkeypair
252 {
253 SCIP_Real key1; /**< first sort key value */
254 SCIP_Real key2; /**< second sort key value */
255 };
256 typedef struct sortkeypair SORTKEYPAIR;
257
258 /** status of GUB constraint */
259 enum GUBVarstatus
260 {
261 GUBVARSTATUS_UNINITIAL = -1, /** unintitialized variable status */
262 GUBVARSTATUS_CAPACITYEXCEEDED = 0, /** variable with weight exceeding the knapsack capacity */
263 GUBVARSTATUS_BELONGSTOSET_R = 1, /** variable in noncovervars R */
264 GUBVARSTATUS_BELONGSTOSET_F = 2, /** variable in noncovervars F */
265 GUBVARSTATUS_BELONGSTOSET_C2 = 3, /** variable in covervars C2 */
266 GUBVARSTATUS_BELONGSTOSET_C1 = 4 /** variable in covervars C1 */
267 };
268 typedef enum GUBVarstatus GUBVARSTATUS;
269
270 /** status of variable in GUB constraint */
271 enum GUBConsstatus
272 {
273 GUBCONSSTATUS_UNINITIAL = -1, /** unintitialized GUB constraint status */
274 GUBCONSSTATUS_BELONGSTOSET_GR = 0, /** all GUB variables are in noncovervars R */
275 GUBCONSSTATUS_BELONGSTOSET_GF = 1, /** all GUB variables are in noncovervars F (and noncovervars R) */
276 GUBCONSSTATUS_BELONGSTOSET_GC2 = 2, /** all GUB variables are in covervars C2 */
277 GUBCONSSTATUS_BELONGSTOSET_GNC1 = 3, /** some GUB variables are in covervars C1, others in noncovervars R or F */
278 GUBCONSSTATUS_BELONGSTOSET_GOC1 = 4 /** all GUB variables are in covervars C1 */
279 };
280 typedef enum GUBConsstatus GUBCONSSTATUS;
281
282 /** data structure of GUB constraints */
283 struct SCIP_GUBCons
284 {
285 int* gubvars; /**< indices of GUB variables in knapsack constraint */
286 GUBVARSTATUS* gubvarsstatus; /**< status of GUB variables */
287 int ngubvars; /**< number of GUB variables */
288 int gubvarssize; /**< size of gubvars array */
289 };
290 typedef struct SCIP_GUBCons SCIP_GUBCONS;
291
292 /** data structure of a set of GUB constraints */
293 struct SCIP_GUBSet
294 {
295 SCIP_GUBCONS** gubconss; /**< GUB constraints in GUB set */
296 GUBCONSSTATUS* gubconsstatus; /**< status of GUB constraints */
297 int ngubconss; /**< number of GUB constraints */
298 int nvars; /**< number of variables in knapsack constraint */
299 int* gubconssidx; /**< index of GUB constraint (in gubconss array) of each knapsack variable */
300 int* gubvarsidx; /**< index in GUB constraint (in gubvars array) of each knapsack variable */
301 };
302 typedef struct SCIP_GUBSet SCIP_GUBSET;
303
304 /*
305 * Local methods
306 */
307
308 /** comparison method for two sorting key pairs */
309 static
SCIP_DECL_SORTPTRCOMP(compSortkeypairs)310 SCIP_DECL_SORTPTRCOMP(compSortkeypairs)
311 {
312 SORTKEYPAIR* sortkeypair1 = (SORTKEYPAIR*)elem1;
313 SORTKEYPAIR* sortkeypair2 = (SORTKEYPAIR*)elem2;
314
315 if( sortkeypair1->key1 < sortkeypair2->key1 )
316 return -1;
317 else if( sortkeypair1->key1 > sortkeypair2->key1 )
318 return +1;
319 else if( sortkeypair1->key2 < sortkeypair2->key2 )
320 return -1;
321 else if( sortkeypair1->key2 > sortkeypair2->key2 )
322 return +1;
323 else
324 return 0;
325 }
326
327 /** creates event data */
328 static
eventdataCreate(SCIP * scip,SCIP_EVENTDATA ** eventdata,SCIP_CONS * cons,SCIP_Longint weight)329 SCIP_RETCODE eventdataCreate(
330 SCIP* scip, /**< SCIP data structure */
331 SCIP_EVENTDATA** eventdata, /**< pointer to store event data */
332 SCIP_CONS* cons, /**< constraint */
333 SCIP_Longint weight /**< weight of variable */
334 )
335 {
336 assert(eventdata != NULL);
337
338 SCIP_CALL( SCIPallocBlockMemory(scip, eventdata) );
339 (*eventdata)->cons = cons;
340 (*eventdata)->weight = weight;
341
342 return SCIP_OKAY;
343 }
344
345 /** frees event data */
346 static
eventdataFree(SCIP * scip,SCIP_EVENTDATA ** eventdata)347 SCIP_RETCODE eventdataFree(
348 SCIP* scip, /**< SCIP data structure */
349 SCIP_EVENTDATA** eventdata /**< pointer to event data */
350 )
351 {
352 assert(eventdata != NULL);
353
354 SCIPfreeBlockMemory(scip, eventdata);
355
356 return SCIP_OKAY;
357 }
358
359 /** sorts items in knapsack with nonincreasing weights */
360 static
sortItems(SCIP_CONSDATA * consdata)361 void sortItems(
362 SCIP_CONSDATA* consdata /**< constraint data */
363 )
364 {
365 assert(consdata != NULL);
366 assert(consdata->nvars == 0 || consdata->vars != NULL);
367 assert(consdata->nvars == 0 || consdata->weights != NULL);
368 assert(consdata->nvars == 0 || consdata->eventdata != NULL);
369 assert(consdata->nvars == 0 || (consdata->cliquepartition != NULL && consdata->negcliquepartition != NULL));
370
371 if( !consdata->sorted )
372 {
373 int pos;
374 int lastcliquenum;
375 int v;
376
377 /* sort of five joint arrays of Long/pointer/pointer/ints/ints,
378 * sorted by first array in non-increasing order via sort template */
379 SCIPsortDownLongPtrPtrIntInt(
380 consdata->weights,
381 (void**)consdata->vars,
382 (void**)consdata->eventdata,
383 consdata->cliquepartition,
384 consdata->negcliquepartition,
385 consdata->nvars);
386
387 v = consdata->nvars - 1;
388 /* sort all items with same weight according to their variable index, used for hash value for fast pairwise comparison of all constraints */
389 while( v >= 0 )
390 {
391 int w = v - 1;
392
393 while( w >= 0 && consdata->weights[v] == consdata->weights[w] )
394 --w;
395
396 if( v - w > 1 )
397 {
398 /* sort all corresponding parts of arrays for which the weights are equal by using the variable index */
399 SCIPsortPtrPtrIntInt(
400 (void**)(&(consdata->vars[w+1])),
401 (void**)(&(consdata->eventdata[w+1])),
402 &(consdata->cliquepartition[w+1]),
403 &(consdata->negcliquepartition[w+1]),
404 SCIPvarComp,
405 v - w);
406 }
407 v = w;
408 }
409
410 /* we need to make sure that our clique numbers of our normal clique will be in increasing order without gaps */
411 if( consdata->cliquepartitioned )
412 {
413 lastcliquenum = 0;
414
415 for( pos = 0; pos < consdata->nvars; ++pos )
416 {
417 /* if the clique number in the normal clique at position pos is greater than the last found clique number the
418 * partition is invalid */
419 if( consdata->cliquepartition[pos] > lastcliquenum )
420 {
421 consdata->cliquepartitioned = FALSE;
422 break;
423 }
424 else if( consdata->cliquepartition[pos] == lastcliquenum )
425 ++lastcliquenum;
426 }
427 }
428 /* we need to make sure that our clique numbers of our negated clique will be in increasing order without gaps */
429 if( consdata->negcliquepartitioned )
430 {
431 lastcliquenum = 0;
432
433 for( pos = 0; pos < consdata->nvars; ++pos )
434 {
435 /* if the clique number in the negated clique at position pos is greater than the last found clique number the
436 * partition is invalid */
437 if( consdata->negcliquepartition[pos] > lastcliquenum )
438 {
439 consdata->negcliquepartitioned = FALSE;
440 break;
441 }
442 else if( consdata->negcliquepartition[pos] == lastcliquenum )
443 ++lastcliquenum;
444 }
445 }
446
447 consdata->sorted = TRUE;
448 }
449 #ifndef NDEBUG
450 {
451 /* check if the weight array is sorted in a non-increasing way */
452 int i;
453 for( i = 0; i < consdata->nvars-1; ++i )
454 assert(consdata->weights[i] >= consdata->weights[i+1]);
455 }
456 #endif
457 }
458
459 /** calculates a partition of the variables into cliques */
460 static
calcCliquepartition(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONSDATA * consdata,SCIP_Bool normalclique,SCIP_Bool negatedclique)461 SCIP_RETCODE calcCliquepartition(
462 SCIP* scip, /**< SCIP data structure */
463 SCIP_CONSHDLRDATA* conshdlrdata, /**< knapsack constraint handler data */
464 SCIP_CONSDATA* consdata, /**< constraint data */
465 SCIP_Bool normalclique, /**< Should normal cliquepartition be created? */
466 SCIP_Bool negatedclique /**< Should negated cliquepartition be created? */
467 )
468 {
469 SCIP_Bool ispartitionoutdated;
470 SCIP_Bool isnegpartitionoutdated;
471 assert(consdata != NULL);
472 assert(consdata->nvars == 0 || (consdata->cliquepartition != NULL && consdata->negcliquepartition != NULL));
473
474 /* rerun eventually if number of global cliques increased considerably since last partition */
475 ispartitionoutdated = (conshdlrdata->updatecliquepartitions && consdata->ncliques > 1
476 && SCIPgetNCliques(scip) >= (int)(conshdlrdata->clqpartupdatefac * consdata->ncliqueslastpart));
477
478 if( normalclique && ( !consdata->cliquepartitioned || ispartitionoutdated ) )
479 {
480 SCIP_CALL( SCIPcalcCliquePartition(scip, consdata->vars, consdata->nvars, consdata->cliquepartition, &consdata->ncliques) );
481 consdata->cliquepartitioned = TRUE;
482 consdata->ncliqueslastpart = SCIPgetNCliques(scip);
483 }
484
485 /* rerun eventually if number of global cliques increased considerably since last negated partition */
486 isnegpartitionoutdated = (conshdlrdata->updatecliquepartitions && consdata->nnegcliques > 1
487 && SCIPgetNCliques(scip) >= (int)(conshdlrdata->clqpartupdatefac * consdata->ncliqueslastnegpart));
488
489 if( negatedclique && (!consdata->negcliquepartitioned || isnegpartitionoutdated) )
490 {
491 SCIP_CALL( SCIPcalcNegatedCliquePartition(scip, consdata->vars, consdata->nvars, consdata->negcliquepartition, &consdata->nnegcliques) );
492 consdata->negcliquepartitioned = TRUE;
493 consdata->ncliqueslastnegpart = SCIPgetNCliques(scip);
494 }
495 assert(!consdata->cliquepartitioned || consdata->ncliques <= consdata->nvars);
496 assert(!consdata->negcliquepartitioned || consdata->nnegcliques <= consdata->nvars);
497
498 return SCIP_OKAY;
499 }
500
501 /** installs rounding locks for the given variable in the given knapsack constraint */
502 static
lockRounding(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)503 SCIP_RETCODE lockRounding(
504 SCIP* scip, /**< SCIP data structure */
505 SCIP_CONS* cons, /**< knapsack constraint */
506 SCIP_VAR* var /**< variable of constraint entry */
507 )
508 {
509 SCIP_CALL( SCIPlockVarCons(scip, var, cons, FALSE, TRUE) );
510
511 return SCIP_OKAY;
512 }
513
514 /** removes rounding locks for the given variable in the given knapsack constraint */
515 static
unlockRounding(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)516 SCIP_RETCODE unlockRounding(
517 SCIP* scip, /**< SCIP data structure */
518 SCIP_CONS* cons, /**< knapsack constraint */
519 SCIP_VAR* var /**< variable of constraint entry */
520 )
521 {
522 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, FALSE, TRUE) );
523
524 return SCIP_OKAY;
525 }
526
527 /** catches bound change events for variables in knapsack */
528 static
catchEvents(SCIP * scip,SCIP_CONS * cons,SCIP_CONSDATA * consdata,SCIP_EVENTHDLR * eventhdlr)529 SCIP_RETCODE catchEvents(
530 SCIP* scip, /**< SCIP data structure */
531 SCIP_CONS* cons, /**< constraint */
532 SCIP_CONSDATA* consdata, /**< constraint data */
533 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */
534 )
535 {
536 int i;
537
538 assert(cons != NULL);
539 assert(consdata != NULL);
540 assert(consdata->nvars == 0 || consdata->vars != NULL);
541 assert(consdata->nvars == 0 || consdata->weights != NULL);
542 assert(consdata->nvars == 0 || consdata->eventdata != NULL);
543
544 for( i = 0; i < consdata->nvars; i++)
545 {
546 SCIP_CALL( eventdataCreate(scip, &consdata->eventdata[i], cons, consdata->weights[i]) );
547 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[i], EVENTTYPE_KNAPSACK,
548 eventhdlr, consdata->eventdata[i], &consdata->eventdata[i]->filterpos) );
549 }
550
551 return SCIP_OKAY;
552 }
553
554 /** drops bound change events for variables in knapsack */
555 static
dropEvents(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_EVENTHDLR * eventhdlr)556 SCIP_RETCODE dropEvents(
557 SCIP* scip, /**< SCIP data structure */
558 SCIP_CONSDATA* consdata, /**< constraint data */
559 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */
560 )
561 {
562 int i;
563
564 assert(consdata != NULL);
565 assert(consdata->nvars == 0 || consdata->vars != NULL);
566 assert(consdata->nvars == 0 || consdata->weights != NULL);
567 assert(consdata->nvars == 0 || consdata->eventdata != NULL);
568
569 for( i = 0; i < consdata->nvars; i++)
570 {
571 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[i], EVENTTYPE_KNAPSACK,
572 eventhdlr, consdata->eventdata[i], consdata->eventdata[i]->filterpos) );
573 SCIP_CALL( eventdataFree(scip, &consdata->eventdata[i]) );
574 }
575
576 return SCIP_OKAY;
577 }
578
579 /** ensures, that vars and vals arrays can store at least num entries */
580 static
consdataEnsureVarsSize(SCIP * scip,SCIP_CONSDATA * consdata,int num,SCIP_Bool transformed)581 SCIP_RETCODE consdataEnsureVarsSize(
582 SCIP* scip, /**< SCIP data structure */
583 SCIP_CONSDATA* consdata, /**< knapsack constraint data */
584 int num, /**< minimum number of entries to store */
585 SCIP_Bool transformed /**< is constraint from transformed problem? */
586 )
587 {
588 assert(consdata != NULL);
589 assert(consdata->nvars <= consdata->varssize);
590
591 if( num > consdata->varssize )
592 {
593 int newsize;
594
595 newsize = SCIPcalcMemGrowSize(scip, num);
596 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->vars, consdata->varssize, newsize) );
597 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->weights, consdata->varssize, newsize) );
598 if( transformed )
599 {
600 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->eventdata, consdata->varssize, newsize) );
601 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->cliquepartition, consdata->varssize, newsize) );
602 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->negcliquepartition, consdata->varssize, newsize) );
603 }
604 else
605 {
606 assert(consdata->eventdata == NULL);
607 assert(consdata->cliquepartition == NULL);
608 assert(consdata->negcliquepartition == NULL);
609 }
610 consdata->varssize = newsize;
611 }
612 assert(num <= consdata->varssize);
613
614 return SCIP_OKAY;
615 }
616
617 /** updates all weight sums for fixed and unfixed variables */
618 static
updateWeightSums(SCIP_CONSDATA * consdata,SCIP_VAR * var,SCIP_Longint weightdelta)619 void updateWeightSums(
620 SCIP_CONSDATA* consdata, /**< knapsack constraint data */
621 SCIP_VAR* var, /**< variable for this weight */
622 SCIP_Longint weightdelta /**< difference between the old and the new weight of the variable */
623 )
624 {
625 assert(consdata != NULL);
626 assert(var != NULL);
627
628 consdata->weightsum += weightdelta;
629
630 if( SCIPvarGetLbLocal(var) > 0.5 )
631 consdata->onesweightsum += weightdelta;
632
633 assert(consdata->weightsum >= 0);
634 assert(consdata->onesweightsum >= 0);
635 }
636
637 /** creates knapsack constraint data */
638 static
consdataCreate(SCIP * scip,SCIP_CONSDATA ** consdata,int nvars,SCIP_VAR ** vars,SCIP_Longint * weights,SCIP_Longint capacity)639 SCIP_RETCODE consdataCreate(
640 SCIP* scip, /**< SCIP data structure */
641 SCIP_CONSDATA** consdata, /**< pointer to store constraint data */
642 int nvars, /**< number of variables in knapsack */
643 SCIP_VAR** vars, /**< variables of knapsack */
644 SCIP_Longint* weights, /**< weights of knapsack items */
645 SCIP_Longint capacity /**< capacity of knapsack */
646 )
647 {
648 int v;
649 SCIP_Longint constant;
650
651 assert(consdata != NULL);
652
653 SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
654
655 constant = 0L;
656 (*consdata)->vars = NULL;
657 (*consdata)->weights = NULL;
658 (*consdata)->nvars = 0;
659 if( nvars > 0 )
660 {
661 SCIP_VAR** varsbuffer;
662 SCIP_Longint* weightsbuffer;
663 int k;
664
665 SCIP_CALL( SCIPallocBufferArray(scip, &varsbuffer, nvars) );
666 SCIP_CALL( SCIPallocBufferArray(scip, &weightsbuffer, nvars) );
667
668 k = 0;
669 for( v = 0; v < nvars; ++v )
670 {
671 assert(vars[v] != NULL);
672 assert(SCIPvarIsBinary(vars[v]));
673
674 /* all weight have to be non negative */
675 assert( weights[v] >= 0 );
676
677 if( weights[v] > 0 )
678 {
679 /* treat fixed variables as constants if problem compression is enabled */
680 if( SCIPisConsCompressionEnabled(scip) && SCIPvarGetLbGlobal(vars[v]) > SCIPvarGetUbGlobal(vars[v]) - 0.5 )
681 {
682 /* only if the variable is fixed to 1, we add its weight to the constant */
683 if( SCIPvarGetUbGlobal(vars[v]) > 0.5 )
684 constant += weights[v];
685 }
686 else
687 {
688 varsbuffer[k] = vars[v];
689 weightsbuffer[k] = weights[v];
690 ++k;
691 }
692 }
693 }
694 assert(k >= 0);
695
696 (*consdata)->nvars = k;
697
698 /* copy the active variables and weights into the constraint data structure */
699 if( k > 0 )
700 {
701 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->vars, varsbuffer, k) );
702 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->weights, weightsbuffer, k) );
703 }
704
705 /* free buffer storage */
706 SCIPfreeBufferArray(scip, &weightsbuffer);
707 SCIPfreeBufferArray(scip, &varsbuffer);
708 }
709
710 /* capacity has to be greater or equal to zero */
711 assert(capacity >= 0);
712 assert(constant >= 0);
713
714 (*consdata)->varssize = (*consdata)->nvars;
715 (*consdata)->capacity = capacity - constant;
716 (*consdata)->eventdata = NULL;
717 (*consdata)->cliquepartition = NULL;
718 (*consdata)->negcliquepartition = NULL;
719 (*consdata)->row = NULL;
720 (*consdata)->weightsum = 0;
721 (*consdata)->onesweightsum = 0;
722 (*consdata)->ncliques = 0;
723 (*consdata)->nnegcliques = 0;
724 (*consdata)->presolvedtiming = 0;
725 (*consdata)->sorted = FALSE;
726 (*consdata)->cliquepartitioned = FALSE;
727 (*consdata)->negcliquepartitioned = FALSE;
728 (*consdata)->ncliqueslastpart = -1;
729 (*consdata)->ncliqueslastnegpart = -1;
730 (*consdata)->merged = FALSE;
731 (*consdata)->cliquesadded = FALSE;
732 (*consdata)->varsdeleted = FALSE;
733 (*consdata)->existmultaggr = FALSE;
734
735 /* get transformed variables, if we are in the transformed problem */
736 if( SCIPisTransformed(scip) )
737 {
738 SCIP_CALL( SCIPgetTransformedVars(scip, (*consdata)->nvars, (*consdata)->vars, (*consdata)->vars) );
739
740 for( v = 0; v < (*consdata)->nvars; v++ )
741 {
742 SCIP_VAR* var = SCIPvarGetProbvar((*consdata)->vars[v]);
743 assert(var != NULL);
744 (*consdata)->existmultaggr = (*consdata)->existmultaggr || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
745 }
746
747 /* allocate memory for additional data structures */
748 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->eventdata, (*consdata)->nvars) );
749 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->cliquepartition, (*consdata)->nvars) );
750 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->negcliquepartition, (*consdata)->nvars) );
751 }
752
753 /* calculate sum of weights and capture variables */
754 for( v = 0; v < (*consdata)->nvars; ++v )
755 {
756 /* calculate sum of weights */
757 updateWeightSums(*consdata, (*consdata)->vars[v], (*consdata)->weights[v]);
758
759 /* capture variables */
760 SCIP_CALL( SCIPcaptureVar(scip, (*consdata)->vars[v]) );
761 }
762 return SCIP_OKAY;
763 }
764
765 /** frees knapsack constraint data */
766 static
consdataFree(SCIP * scip,SCIP_CONSDATA ** consdata,SCIP_EVENTHDLR * eventhdlr)767 SCIP_RETCODE consdataFree(
768 SCIP* scip, /**< SCIP data structure */
769 SCIP_CONSDATA** consdata, /**< pointer to the constraint data */
770 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */
771 )
772 {
773 assert(consdata != NULL);
774 assert(*consdata != NULL);
775
776 if( (*consdata)->row != NULL )
777 {
778 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->row) );
779 }
780 if( (*consdata)->eventdata != NULL )
781 {
782 SCIP_CALL( dropEvents(scip, *consdata, eventhdlr) );
783 SCIPfreeBlockMemoryArray(scip, &(*consdata)->eventdata, (*consdata)->varssize);
784 }
785 if( (*consdata)->negcliquepartition != NULL )
786 {
787 SCIPfreeBlockMemoryArray(scip, &(*consdata)->negcliquepartition, (*consdata)->varssize);
788 }
789 if( (*consdata)->cliquepartition != NULL )
790 {
791 SCIPfreeBlockMemoryArray(scip, &(*consdata)->cliquepartition, (*consdata)->varssize);
792 }
793 if( (*consdata)->vars != NULL )
794 {
795 int v;
796
797 /* release variables */
798 for( v = 0; v < (*consdata)->nvars; v++ )
799 {
800 assert((*consdata)->vars[v] != NULL);
801 SCIP_CALL( SCIPreleaseVar(scip, &((*consdata)->vars[v])) );
802 }
803
804 assert( (*consdata)->weights != NULL );
805 assert( (*consdata)->varssize > 0 );
806 SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, (*consdata)->varssize);
807 SCIPfreeBlockMemoryArray(scip, &(*consdata)->weights, (*consdata)->varssize);
808 }
809
810 SCIPfreeBlockMemory(scip, consdata);
811
812 return SCIP_OKAY;
813 }
814
815 /** changes a single weight in knapsack constraint data */
816 static
consdataChgWeight(SCIP_CONSDATA * consdata,int item,SCIP_Longint newweight)817 void consdataChgWeight(
818 SCIP_CONSDATA* consdata, /**< knapsack constraint data */
819 int item, /**< item number */
820 SCIP_Longint newweight /**< new weight of item */
821 )
822 {
823 SCIP_Longint oldweight;
824 SCIP_Longint weightdiff;
825
826 assert(consdata != NULL);
827 assert(0 <= item && item < consdata->nvars);
828
829 oldweight = consdata->weights[item];
830 weightdiff = newweight - oldweight;
831 consdata->weights[item] = newweight;
832
833 /* update weight sums for all and fixed variables */
834 updateWeightSums(consdata, consdata->vars[item], weightdiff);
835
836 if( consdata->eventdata != NULL )
837 {
838 assert(consdata->eventdata[item] != NULL);
839 assert(consdata->eventdata[item]->weight == oldweight);
840 consdata->eventdata[item]->weight = newweight;
841 }
842
843 consdata->presolvedtiming = 0;
844 consdata->sorted = FALSE;
845
846 /* recalculate cliques extraction after a weight was increased */
847 if( oldweight < newweight )
848 {
849 consdata->cliquesadded = FALSE;
850 }
851 }
852
853 /** creates LP row corresponding to knapsack constraint */
854 static
createRelaxation(SCIP * scip,SCIP_CONS * cons)855 SCIP_RETCODE createRelaxation(
856 SCIP* scip, /**< SCIP data structure */
857 SCIP_CONS* cons /**< knapsack constraint */
858 )
859 {
860 SCIP_CONSDATA* consdata;
861 int i;
862
863 consdata = SCIPconsGetData(cons);
864 assert(consdata != NULL);
865 assert(consdata->row == NULL);
866
867 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &consdata->row, cons, SCIPconsGetName(cons),
868 -SCIPinfinity(scip), (SCIP_Real)consdata->capacity,
869 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsRemovable(cons)) );
870
871 SCIP_CALL( SCIPcacheRowExtensions(scip, consdata->row) );
872 for( i = 0; i < consdata->nvars; ++i )
873 {
874 SCIP_CALL( SCIPaddVarToRow(scip, consdata->row, consdata->vars[i], (SCIP_Real)consdata->weights[i]) );
875 }
876 SCIP_CALL( SCIPflushRowExtensions(scip, consdata->row) );
877
878 return SCIP_OKAY;
879 }
880
881 /** adds linear relaxation of knapsack constraint to the LP */
882 static
addRelaxation(SCIP * scip,SCIP_CONS * cons,SCIP_Bool * cutoff)883 SCIP_RETCODE addRelaxation(
884 SCIP* scip, /**< SCIP data structure */
885 SCIP_CONS* cons, /**< knapsack constraint */
886 SCIP_Bool* cutoff /**< whether a cutoff has been detected */
887 )
888 {
889 SCIP_CONSDATA* consdata;
890
891 assert( cutoff != NULL );
892 *cutoff = FALSE;
893
894 consdata = SCIPconsGetData(cons);
895 assert(consdata != NULL);
896
897 if( consdata->row == NULL )
898 {
899 SCIP_CALL( createRelaxation(scip, cons) );
900 }
901 assert(consdata->row != NULL);
902
903 /* insert LP row as cut */
904 if( !SCIProwIsInLP(consdata->row) )
905 {
906 SCIPdebugMsg(scip, "adding relaxation of knapsack constraint <%s> (capacity %" SCIP_LONGINT_FORMAT "): ",
907 SCIPconsGetName(cons), consdata->capacity);
908 SCIPdebug( SCIP_CALL(SCIPprintRow(scip, consdata->row, NULL)) );
909 SCIP_CALL( SCIPaddRow(scip, consdata->row, FALSE, cutoff) );
910 }
911
912 return SCIP_OKAY;
913 }
914
915 /** checks knapsack constraint for feasibility of given solution: returns TRUE iff constraint is feasible */
916 static
checkCons(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Bool checklprows,SCIP_Bool printreason,SCIP_Bool * violated)917 SCIP_RETCODE checkCons(
918 SCIP* scip, /**< SCIP data structure */
919 SCIP_CONS* cons, /**< constraint to check */
920 SCIP_SOL* sol, /**< solution to check, NULL for current solution */
921 SCIP_Bool checklprows, /**< Do constraints represented by rows in the current LP have to be checked? */
922 SCIP_Bool printreason, /**< Should the reason for the violation be printed? */
923 SCIP_Bool* violated /**< pointer to store whether the constraint is violated */
924 )
925 {
926 SCIP_CONSDATA* consdata;
927
928 assert(violated != NULL);
929
930 consdata = SCIPconsGetData(cons);
931 assert(consdata != NULL);
932
933 SCIPdebugMsg(scip, "checking knapsack constraint <%s> for feasibility of solution %p (lprows=%u)\n",
934 SCIPconsGetName(cons), (void*)sol, checklprows);
935
936 *violated = FALSE;
937
938 if( checklprows || consdata->row == NULL || !SCIProwIsInLP(consdata->row) )
939 {
940 SCIP_Real sum;
941 SCIP_Longint integralsum;
942 SCIP_Bool ishuge;
943 SCIP_Real absviol;
944 SCIP_Real relviol;
945 int v;
946
947 /* increase age of constraint; age is reset to zero, if a violation was found only in case we are in
948 * enforcement
949 */
950 if( sol == NULL )
951 {
952 SCIP_CALL( SCIPincConsAge(scip, cons) );
953 }
954
955 sum = 0.0;
956 integralsum = 0;
957 /* we perform a more exact comparison if the capacity does not exceed the huge value */
958 if( SCIPisHugeValue(scip, (SCIP_Real) consdata->capacity) )
959 {
960 ishuge = TRUE;
961
962 /* sum over all weight times the corresponding solution value */
963 for( v = consdata->nvars - 1; v >= 0; --v )
964 {
965 assert(SCIPvarIsBinary(consdata->vars[v]));
966 sum += consdata->weights[v] * SCIPgetSolVal(scip, sol, consdata->vars[v]);
967 }
968 }
969 else
970 {
971 ishuge = FALSE;
972
973 /* sum over all weight for which the variable has a solution value of 1 in feastol */
974 for( v = consdata->nvars - 1; v >= 0; --v )
975 {
976 assert(SCIPvarIsBinary(consdata->vars[v]));
977
978 if( SCIPgetSolVal(scip, sol, consdata->vars[v]) > 0.5 )
979 integralsum += consdata->weights[v];
980 }
981 }
982
983 /* calculate constraint violation and update it in solution */
984 absviol = ishuge ? sum : (SCIP_Real)integralsum;
985 absviol -= consdata->capacity;
986 relviol = SCIPrelDiff(absviol + consdata->capacity, (SCIP_Real)consdata->capacity);
987 if( sol != NULL )
988 SCIPupdateSolLPConsViolation(scip, sol, absviol, relviol);
989
990 if( SCIPisFeasPositive(scip, absviol) )
991 {
992 *violated = TRUE;
993
994 /* only reset constraint age if we are in enforcement */
995 if( sol == NULL )
996 {
997 SCIP_CALL( SCIPresetConsAge(scip, cons) );
998 }
999
1000 if( printreason )
1001 {
1002 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
1003
1004 SCIPinfoMessage(scip, NULL, ";\n");
1005 SCIPinfoMessage(scip, NULL, "violation: the capacity is violated by %.15g\n", absviol);
1006 }
1007 }
1008 }
1009
1010 return SCIP_OKAY;
1011 }
1012
1013 /* IDX computes the integer index for the optimal solution array */
1014 #define IDX(j,d) ((j)*(intcap)+(d))
1015
1016 /** solves knapsack problem in maximization form exactly using dynamic programming;
1017 * if needed, one can provide arrays to store all selected items and all not selected items
1018 *
1019 * @note in case you provide the solitems or nonsolitems array you also have to provide the counter part, as well
1020 *
1021 * @note the algorithm will first compute a greedy solution and terminate
1022 * if the greedy solution is proven to be optimal.
1023 * The dynamic programming algorithm runs with a time and space complexity
1024 * of O(nitems * capacity).
1025 *
1026 * @todo If only the objective is relevant, it is easy to change the code to use only one slice with O(capacity) space.
1027 * There are recursive methods (see the book by Kellerer et al.) that require O(capacity) space, but it remains
1028 * to be checked whether they are faster and whether they can reconstruct the solution.
1029 * Dembo and Hammer (see Kellerer et al. Section 5.1.3, page 126) found a method that relies on a fast probing method.
1030 * This fixes additional elements to 0 or 1 similar to a reduced cost fixing.
1031 * This could be implemented, however, it would be technically a bit cumbersome,
1032 * since one needs the greedy solution and the LP-value for this.
1033 * This is currently only available after the redundant items have already been sorted out.
1034 */
SCIPsolveKnapsackExactly(SCIP * scip,int nitems,SCIP_Longint * weights,SCIP_Real * profits,SCIP_Longint capacity,int * items,int * solitems,int * nonsolitems,int * nsolitems,int * nnonsolitems,SCIP_Real * solval,SCIP_Bool * success)1035 SCIP_RETCODE SCIPsolveKnapsackExactly(
1036 SCIP* scip, /**< SCIP data structure */
1037 int nitems, /**< number of available items */
1038 SCIP_Longint* weights, /**< item weights */
1039 SCIP_Real* profits, /**< item profits */
1040 SCIP_Longint capacity, /**< capacity of knapsack */
1041 int* items, /**< item numbers */
1042 int* solitems, /**< array to store items in solution, or NULL */
1043 int* nonsolitems, /**< array to store items not in solution, or NULL */
1044 int* nsolitems, /**< pointer to store number of items in solution, or NULL */
1045 int* nnonsolitems, /**< pointer to store number of items not in solution, or NULL */
1046 SCIP_Real* solval, /**< pointer to store optimal solution value, or NULL */
1047 SCIP_Bool* success /**< pointer to store if an error occured during solving
1048 * (normally a memory problem) */
1049 )
1050 {
1051 SCIP_RETCODE retcode;
1052 SCIP_Real* tempsort;
1053 SCIP_Real* optvalues;
1054 int intcap;
1055 int d;
1056 int j;
1057 int greedymedianpos;
1058 SCIP_Longint weightsum;
1059 int* myitems;
1060 SCIP_Longint* myweights;
1061 SCIP_Real* realweights;
1062 int* allcurrminweight;
1063 SCIP_Real* myprofits;
1064 int nmyitems;
1065 SCIP_Longint gcd;
1066 SCIP_Longint minweight;
1067 SCIP_Longint maxweight;
1068 int currminweight;
1069 SCIP_Longint greedysolweight;
1070 SCIP_Real greedysolvalue;
1071 SCIP_Real greedyupperbound;
1072 SCIP_Bool eqweights;
1073 SCIP_Bool intprofits;
1074
1075 assert(weights != NULL);
1076 assert(profits != NULL);
1077 assert(capacity >= 0);
1078 assert(items != NULL);
1079 assert(nitems >= 0);
1080 assert(success != NULL);
1081
1082 *success = TRUE;
1083
1084 #ifndef NDEBUG
1085 for( j = nitems - 1; j >= 0; --j )
1086 assert(weights[j] >= 0);
1087 #endif
1088
1089 SCIPdebugMsg(scip, "Solving knapsack exactly.\n");
1090
1091 /* initializing solution value */
1092 if( solval != NULL )
1093 *solval = 0.0;
1094
1095 /* init solution information */
1096 if( solitems != NULL )
1097 {
1098 assert(items != NULL);
1099 assert(nsolitems != NULL);
1100 assert(nonsolitems != NULL);
1101 assert(nnonsolitems != NULL);
1102
1103 *nnonsolitems = 0;
1104 *nsolitems = 0;
1105 }
1106
1107 /* allocate temporary memory */
1108 SCIP_CALL( SCIPallocBufferArray(scip, &myweights, nitems) );
1109 SCIP_CALL( SCIPallocBufferArray(scip, &myprofits, nitems) );
1110 SCIP_CALL( SCIPallocBufferArray(scip, &myitems, nitems) );
1111 nmyitems = 0;
1112 weightsum = 0;
1113 minweight = SCIP_LONGINT_MAX;
1114 maxweight = 0;
1115
1116 /* remove unnecessary items */
1117 for( j = 0; j < nitems; ++j )
1118 {
1119 assert(0 <= weights[j] && weights[j] < SCIP_LONGINT_MAX);
1120
1121 /* item does not fit */
1122 if( weights[j] > capacity )
1123 {
1124 if( solitems != NULL )
1125 nonsolitems[(*nnonsolitems)++] = items[j]; /*lint !e413*/
1126 }
1127 /* item is not profitable */
1128 else if( profits[j] <= 0.0 )
1129 {
1130 if( solitems != NULL )
1131 nonsolitems[(*nnonsolitems)++] = items[j]; /*lint !e413*/
1132 }
1133 /* item always fits */
1134 else if( weights[j] == 0 )
1135 {
1136 if( solitems != NULL )
1137 solitems[(*nsolitems)++] = items[j]; /*lint !e413*/
1138
1139 if( solval != NULL )
1140 *solval += profits[j];
1141 }
1142 /* all important items */
1143 else
1144 {
1145 myweights[nmyitems] = weights[j];
1146 myprofits[nmyitems] = profits[j];
1147 myitems[nmyitems] = items[j];
1148
1149 /* remember smallest item */
1150 if( myweights[nmyitems] < minweight )
1151 minweight = myweights[nmyitems];
1152
1153 /* remember bigest item */
1154 if( myweights[nmyitems] > maxweight )
1155 maxweight = myweights[nmyitems];
1156
1157 weightsum += myweights[nmyitems];
1158 ++nmyitems;
1159 }
1160 }
1161
1162 intprofits = TRUE;
1163 /* check if all profits are integer to strengthen the upper bound on the greedy solution */
1164 for( j = 0; j < nmyitems && intprofits; ++j )
1165 intprofits = intprofits && SCIPisIntegral(scip, myprofits[j]);
1166
1167 /* if no item is left then goto end */
1168 if( nmyitems == 0 )
1169 {
1170 SCIPdebugMsg(scip, "After preprocessing no items are left.\n");
1171
1172 goto TERMINATE;
1173 }
1174
1175 /* if all items fit, we also do not need to do the expensive stuff later on */
1176 if( weightsum > 0 && weightsum <= capacity )
1177 {
1178 SCIPdebugMsg(scip, "After preprocessing all items fit into knapsack.\n");
1179
1180 for( j = nmyitems - 1; j >= 0; --j )
1181 {
1182 if( solitems != NULL )
1183 solitems[(*nsolitems)++] = myitems[j]; /*lint !e413*/
1184
1185 if( solval != NULL )
1186 *solval += myprofits[j];
1187 }
1188
1189 goto TERMINATE;
1190 }
1191
1192 assert(0 < minweight && minweight <= capacity );
1193 assert(0 < maxweight && maxweight <= capacity);
1194
1195 /* make weights relatively prime */
1196 eqweights = TRUE;
1197 if( maxweight > 1 )
1198 {
1199 /* determine greatest common divisor */
1200 gcd = myweights[nmyitems - 1];
1201 for( j = nmyitems - 2; j >= 0 && gcd >= 2; --j )
1202 gcd = SCIPcalcGreComDiv(gcd, myweights[j]);
1203
1204 SCIPdebugMsg(scip, "Gcd is %" SCIP_LONGINT_FORMAT ".\n", gcd);
1205
1206 /* divide by greatest common divisor */
1207 if( gcd > 1 )
1208 {
1209 for( j = nmyitems - 1; j >= 0; --j )
1210 {
1211 myweights[j] /= gcd;
1212 eqweights = eqweights && (myweights[j] == 1);
1213 }
1214 capacity /= gcd;
1215 minweight /= gcd;
1216 }
1217 else
1218 eqweights = FALSE;
1219 }
1220 assert(minweight <= capacity);
1221
1222 /* if only one item fits, then take the best */
1223 if( minweight > capacity / 2 )
1224 {
1225 int p;
1226
1227 SCIPdebugMsg(scip, "Only one item fits into knapsack, so take the best.\n");
1228
1229 p = nmyitems - 1;
1230
1231 /* find best item */
1232 for( j = nmyitems - 2; j >= 0; --j )
1233 {
1234 if( myprofits[j] > myprofits[p] )
1235 p = j;
1236 }
1237
1238 /* update solution information */
1239 if( solitems != NULL )
1240 {
1241 assert(nsolitems != NULL && nonsolitems != NULL && nnonsolitems != NULL);
1242
1243 solitems[(*nsolitems)++] = myitems[p];
1244 for( j = nmyitems - 1; j >= 0; --j )
1245 {
1246 if( j != p )
1247 nonsolitems[(*nnonsolitems)++] = myitems[j];
1248 }
1249 }
1250 /* update solution value */
1251 if( solval != NULL )
1252 *solval += myprofits[p];
1253
1254 goto TERMINATE;
1255 }
1256
1257 /* if all items have the same weight, then take the best */
1258 if( eqweights )
1259 {
1260 SCIP_Real addval = 0.0;
1261
1262 SCIPdebugMsg(scip, "All weights are equal, so take the best.\n");
1263
1264 SCIPsortDownRealIntLong(myprofits, myitems, myweights, nmyitems);
1265
1266 /* update solution information */
1267 if( solitems != NULL || solval != NULL )
1268 {
1269 SCIP_Longint i;
1270
1271 /* if all items would fit we had handled this case before */
1272 assert((SCIP_Longint) nmyitems > capacity);
1273 assert(nsolitems != NULL && nonsolitems != NULL && nnonsolitems != NULL);
1274
1275 /* take the first best items into the solution */
1276 for( i = capacity - 1; i >= 0; --i )
1277 {
1278 if( solitems != NULL )
1279 solitems[(*nsolitems)++] = myitems[i];
1280 addval += myprofits[i];
1281 }
1282
1283 if( solitems != NULL )
1284 {
1285 /* the rest are not in the solution */
1286 for( i = nmyitems - 1; i >= capacity; --i )
1287 nonsolitems[(*nnonsolitems)++] = myitems[i];
1288 }
1289 }
1290 /* update solution value */
1291 if( solval != NULL )
1292 {
1293 assert(addval > 0.0);
1294 *solval += addval;
1295 }
1296
1297 goto TERMINATE;
1298 }
1299
1300 SCIPdebugMsg(scip, "Determine greedy solution.\n");
1301
1302 /* sort myitems (plus corresponding arrays myweights and myprofits) such that
1303 * p_1/w_1 >= p_2/w_2 >= ... >= p_n/w_n, this is only used for the greedy solution
1304 */
1305 SCIP_CALL( SCIPallocBufferArray(scip, &tempsort, nmyitems) );
1306 SCIP_CALL( SCIPallocBufferArray(scip, &realweights, nmyitems) );
1307
1308 for( j = 0; j < nmyitems; ++j )
1309 {
1310 tempsort[j] = myprofits[j]/((SCIP_Real) myweights[j]);
1311 realweights[j] = (SCIP_Real)myweights[j];
1312 }
1313
1314 SCIPselectWeightedDownRealLongRealInt(tempsort, myweights, myprofits, myitems, realweights,
1315 (SCIP_Real)capacity, nmyitems, &greedymedianpos);
1316
1317 SCIPfreeBufferArray(scip, &realweights);
1318 SCIPfreeBufferArray(scip, &tempsort);
1319
1320 /* initialize values for greedy solution information */
1321 greedysolweight = 0;
1322 greedysolvalue = 0.0;
1323
1324 /* determine greedy solution */
1325 for( j = 0; j < greedymedianpos; ++j )
1326 {
1327 assert(myweights[j] <= capacity);
1328
1329 /* update greedy solution weight and value */
1330 greedysolweight += myweights[j];
1331 greedysolvalue += myprofits[j];
1332 }
1333
1334 assert(0 < greedysolweight && greedysolweight <= capacity);
1335 assert(greedysolvalue > 0.0);
1336
1337 /* If the greedy solution is optimal by comparing to the LP solution, we take this solution. This happens if:
1338 * - the greedy solution reaches the capacity, because then the LP solution is integral;
1339 * - the greedy solution has an objective that is at least the LP value rounded down in case that all profits are integer, too. */
1340 greedyupperbound = greedysolvalue + myprofits[j] * (SCIP_Real) (capacity - greedysolweight)/((SCIP_Real) myweights[j]);
1341 if( intprofits )
1342 greedyupperbound = SCIPfloor(scip, greedyupperbound);
1343 if( greedysolweight == capacity || SCIPisGE(scip, greedysolvalue, greedyupperbound) )
1344 {
1345 SCIPdebugMsg(scip, "Greedy solution is optimal.\n");
1346
1347 /* update solution information */
1348 if( solitems != NULL )
1349 {
1350 int l;
1351
1352 assert(nsolitems != NULL && nonsolitems != NULL && nnonsolitems != NULL);
1353
1354 /* collect items */
1355 for( l = 0; l < j; ++l )
1356 solitems[(*nsolitems)++] = myitems[l];
1357 for ( ; l < nmyitems; ++l )
1358 nonsolitems[(*nnonsolitems)++] = myitems[l];
1359 }
1360 /* update solution value */
1361 if( solval != NULL )
1362 {
1363 assert(greedysolvalue > 0.0);
1364 *solval += greedysolvalue;
1365 }
1366
1367 goto TERMINATE;
1368 }
1369
1370 /* in the following table we do not need the first minweight columns */
1371 capacity -= (minweight - 1);
1372
1373 /* we can only handle integers */
1374 if( capacity >= INT_MAX )
1375 {
1376 SCIPdebugMsg(scip, "Capacity is to big, so we cannot handle it here.\n");
1377
1378 *success = FALSE;
1379 goto TERMINATE;
1380 }
1381 assert(capacity < INT_MAX);
1382
1383 intcap = (int)capacity;
1384 assert(intcap >= 0);
1385 assert(nmyitems > 0);
1386 assert(sizeof(size_t) >= sizeof(int)); /*lint !e506*/ /* no following conversion should be messed up */
1387
1388 /* this condition checks whether we will try to allocate a correct number of bytes and do not have an overflow, while
1389 * computing the size for the allocation
1390 */
1391 if( intcap < 0 || (intcap > 0 && (((size_t)nmyitems) > (SIZE_MAX / (size_t)intcap / sizeof(*optvalues)) || ((size_t)nmyitems) * ((size_t)intcap) * sizeof(*optvalues) > ((size_t)INT_MAX) )) ) /*lint !e571*/
1392 {
1393 SCIPdebugMsg(scip, "Too much memory (%lu) would be consumed.\n", (unsigned long) (((size_t)nmyitems) * ((size_t)intcap) * sizeof(*optvalues))); /*lint !e571*/
1394
1395 *success = FALSE;
1396 goto TERMINATE;
1397 }
1398
1399 /* allocate temporary memory and check for memory exceedance */
1400 retcode = SCIPallocBufferArray(scip, &optvalues, nmyitems * intcap);
1401 if( retcode == SCIP_NOMEMORY )
1402 {
1403 SCIPdebugMsg(scip, "Did not get enough memory.\n");
1404
1405 *success = FALSE;
1406 goto TERMINATE;
1407 }
1408 else
1409 {
1410 SCIP_CALL( retcode );
1411 }
1412
1413 SCIPdebugMsg(scip, "Start real exact algorithm.\n");
1414
1415 /* we memorize at each step the current minimal weight to later on know which value in our optvalues matrix is valid;
1416 * each value entries of the j-th row of optvalues is valid if the index is >= allcurrminweight[j], otherwise it is
1417 * invalid; a second possibility would be to clear the whole optvalues, which should be more expensive than storing
1418 * 'nmyitem' values
1419 */
1420 SCIP_CALL( SCIPallocBufferArray(scip, &allcurrminweight, nmyitems) );
1421 assert(myweights[0] - minweight < INT_MAX);
1422 currminweight = (int) (myweights[0] - minweight);
1423 allcurrminweight[0] = currminweight;
1424
1425 /* fills first row of dynamic programming table with optimal values */
1426 for( d = currminweight; d < intcap; ++d )
1427 optvalues[d] = myprofits[0];
1428
1429 /* fills dynamic programming table with optimal values */
1430 for( j = 1; j < nmyitems; ++j )
1431 {
1432 int intweight;
1433
1434 /* compute important part of weight, which will be represented in the table */
1435 intweight = (int)(myweights[j] - minweight);
1436 assert(0 <= intweight && intweight < intcap);
1437
1438 /* copy all nonzeros from row above */
1439 for( d = currminweight; d < intweight && d < intcap; ++d )
1440 optvalues[IDX(j,d)] = optvalues[IDX(j-1,d)];
1441
1442 /* update corresponding row */
1443 for( d = intweight; d < intcap; ++d )
1444 {
1445 /* if index d < current minweight then optvalues[IDX(j-1,d)] is not initialized, i.e. should be 0 */
1446 if( d < currminweight )
1447 optvalues[IDX(j,d)] = myprofits[j];
1448 else
1449 {
1450 SCIP_Real sumprofit;
1451
1452 if( d - myweights[j] < currminweight )
1453 sumprofit = myprofits[j];
1454 else
1455 sumprofit = optvalues[IDX(j-1,(int)(d-myweights[j]))] + myprofits[j];
1456
1457 optvalues[IDX(j,d)] = MAX(sumprofit, optvalues[IDX(j-1,d)]);
1458 }
1459 }
1460
1461 /* update currminweight */
1462 if( intweight < currminweight )
1463 currminweight = intweight;
1464
1465 allcurrminweight[j] = currminweight;
1466 }
1467
1468 /* update optimal solution by following the table */
1469 if( solitems != NULL )
1470 {
1471 assert(nsolitems != NULL && nonsolitems != NULL && nnonsolitems != NULL);
1472 d = intcap - 1;
1473
1474 SCIPdebugMsg(scip, "Fill the solution vector after solving exactly.\n");
1475
1476 /* insert all items in (non-) solution vector */
1477 for( j = nmyitems - 1; j > 0; --j )
1478 {
1479 /* if the following condition holds this means all remaining items does not fit anymore */
1480 if( d < allcurrminweight[j] )
1481 {
1482 /* we cannot have exceeded our capacity */
1483 assert((SCIP_Longint) d >= -minweight);
1484 break;
1485 }
1486
1487 /* collect solution items; the first condition means that no further item can fit anymore, but this does */
1488 if( d < allcurrminweight[j-1] || optvalues[IDX(j,d)] > optvalues[IDX(j-1,d)] )
1489 {
1490 solitems[(*nsolitems)++] = myitems[j];
1491
1492 /* check that we do not have an underflow */
1493 assert(myweights[j] <= (INT_MAX + (SCIP_Longint) d));
1494 d = (int)(d - myweights[j]);
1495 }
1496 /* collect non-solution items */
1497 else
1498 nonsolitems[(*nnonsolitems)++] = myitems[j];
1499 }
1500
1501 /* insert remaining items */
1502 if( d >= allcurrminweight[j] )
1503 {
1504 assert(j == 0);
1505 solitems[(*nsolitems)++] = myitems[j];
1506 }
1507 else
1508 {
1509 assert(j >= 0);
1510 assert(d < allcurrminweight[j]);
1511
1512 for( ; j >= 0; --j )
1513 nonsolitems[(*nnonsolitems)++] = myitems[j];
1514 }
1515
1516 assert(*nsolitems + *nnonsolitems == nitems);
1517 }
1518
1519 /* update solution value */
1520 if( solval != NULL )
1521 *solval += optvalues[IDX(nmyitems-1,intcap-1)];
1522 SCIPfreeBufferArray(scip, &allcurrminweight);
1523
1524 /* free all temporary memory */
1525 SCIPfreeBufferArray(scip, &optvalues);
1526
1527 TERMINATE:
1528 SCIPfreeBufferArray(scip, &myitems);
1529 SCIPfreeBufferArray(scip, &myprofits);
1530 SCIPfreeBufferArray(scip, &myweights);
1531
1532 return SCIP_OKAY;
1533 }
1534
1535 /** solves knapsack problem in maximization form approximately by solving the LP-relaxation of the problem using Dantzig's
1536 * method and rounding down the solution; if needed, one can provide arrays to store all selected items and all not
1537 * selected items
1538 */
SCIPsolveKnapsackApproximately(SCIP * scip,int nitems,SCIP_Longint * weights,SCIP_Real * profits,SCIP_Longint capacity,int * items,int * solitems,int * nonsolitems,int * nsolitems,int * nnonsolitems,SCIP_Real * solval)1539 SCIP_RETCODE SCIPsolveKnapsackApproximately(
1540 SCIP* scip, /**< SCIP data structure */
1541 int nitems, /**< number of available items */
1542 SCIP_Longint* weights, /**< item weights */
1543 SCIP_Real* profits, /**< item profits */
1544 SCIP_Longint capacity, /**< capacity of knapsack */
1545 int* items, /**< item numbers */
1546 int* solitems, /**< array to store items in solution, or NULL */
1547 int* nonsolitems, /**< array to store items not in solution, or NULL */
1548 int* nsolitems, /**< pointer to store number of items in solution, or NULL */
1549 int* nnonsolitems, /**< pointer to store number of items not in solution, or NULL */
1550 SCIP_Real* solval /**< pointer to store optimal solution value, or NULL */
1551 )
1552 {
1553 SCIP_Real* tempsort;
1554 SCIP_Longint solitemsweight;
1555 SCIP_Real* realweights;
1556 int j;
1557 int criticalindex;
1558
1559 assert(weights != NULL);
1560 assert(profits != NULL);
1561 assert(capacity >= 0);
1562 assert(items != NULL);
1563 assert(nitems >= 0);
1564
1565 if( solitems != NULL )
1566 {
1567 *nsolitems = 0;
1568 *nnonsolitems = 0;
1569 }
1570 if( solval != NULL )
1571 *solval = 0.0;
1572
1573 /* initialize data for median search */
1574 SCIP_CALL( SCIPallocBufferArray(scip, &tempsort, nitems) );
1575 SCIP_CALL( SCIPallocBufferArray(scip, &realweights, nitems) );
1576 for( j = nitems - 1; j >= 0; --j )
1577 {
1578 tempsort[j] = profits[j]/((SCIP_Real) weights[j]);
1579 realweights[j] = (SCIP_Real)weights[j];
1580 }
1581
1582 /* partially sort indices such that all elements that are larger than the break item appear first */
1583 SCIPselectWeightedDownRealLongRealInt(tempsort, weights, profits, items, realweights, (SCIP_Real)capacity, nitems, &criticalindex);
1584
1585 /* selects items as long as they fit into the knapsack */
1586 solitemsweight = 0;
1587 for( j = 0; j < nitems && solitemsweight + weights[j] <= capacity; ++j )
1588 {
1589 if( solitems != NULL )
1590 solitems[(*nsolitems)++] = items[j];
1591
1592 if( solval != NULL )
1593 (*solval) += profits[j];
1594 solitemsweight += weights[j];
1595 }
1596 if ( solitems != NULL )
1597 {
1598 for( ; j < nitems; j++ )
1599 nonsolitems[(*nnonsolitems)++] = items[j];
1600 }
1601
1602 SCIPfreeBufferArray(scip, &realweights);
1603 SCIPfreeBufferArray(scip, &tempsort);
1604
1605 return SCIP_OKAY;
1606 }
1607
1608 #ifdef SCIP_DEBUG
1609 /** prints all nontrivial GUB constraints and their LP solution values */
1610 static
GUBsetPrint(SCIP * scip,SCIP_GUBSET * gubset,SCIP_VAR ** vars,SCIP_Real * solvals)1611 void GUBsetPrint(
1612 SCIP* scip, /**< SCIP data structure */
1613 SCIP_GUBSET* gubset, /**< GUB set data structure */
1614 SCIP_VAR** vars, /**< variables in knapsack constraint */
1615 SCIP_Real* solvals /**< solution values of variables in knapsack constraint; or NULL */
1616 )
1617 {
1618 int nnontrivialgubconss;
1619 int c;
1620
1621 nnontrivialgubconss = 0;
1622
1623 SCIPdebugMsg(scip, " Nontrivial GUBs of current GUB set:\n");
1624
1625 /* print out all nontrivial GUB constraints, i.e., with more than one variable */
1626 for( c = 0; c < gubset->ngubconss; c++ )
1627 {
1628 SCIP_Real gubsolval;
1629
1630 assert(gubset->gubconss[c]->ngubvars >= 0);
1631
1632 /* nontrivial GUB */
1633 if( gubset->gubconss[c]->ngubvars > 1 )
1634 {
1635 int v;
1636
1637 gubsolval = 0.0;
1638 SCIPdebugMsg(scip, " GUB<%d>:\n", c);
1639
1640 /* print GUB var */
1641 for( v = 0; v < gubset->gubconss[c]->ngubvars; v++ )
1642 {
1643 int currentvar;
1644
1645 currentvar = gubset->gubconss[c]->gubvars[v];
1646 if( solvals != NULL )
1647 {
1648 gubsolval += solvals[currentvar];
1649 SCIPdebugMsg(scip, " +<%s>(%4.2f)\n", SCIPvarGetName(vars[currentvar]), solvals[currentvar]);
1650 }
1651 else
1652 {
1653 SCIPdebugMsg(scip, " +<%s>\n", SCIPvarGetName(vars[currentvar]));
1654 }
1655 }
1656
1657 /* check whether LP solution satisfies the GUB constraint */
1658 if( solvals != NULL )
1659 {
1660 SCIPdebugMsg(scip, " =%4.2f <= 1 %s\n", gubsolval,
1661 SCIPisFeasGT(scip, gubsolval, 1.0) ? "--> violated" : "");
1662 }
1663 else
1664 {
1665 SCIPdebugMsg(scip, " <= 1 %s\n", SCIPisFeasGT(scip, gubsolval, 1.0) ? "--> violated" : "");
1666 }
1667 nnontrivialgubconss++;
1668 }
1669 }
1670
1671 SCIPdebugMsg(scip, " --> %d/%d nontrivial GUBs\n", nnontrivialgubconss, gubset->ngubconss);
1672 }
1673 #endif
1674
1675 /** creates an empty GUB constraint */
1676 static
GUBconsCreate(SCIP * scip,SCIP_GUBCONS ** gubcons)1677 SCIP_RETCODE GUBconsCreate(
1678 SCIP* scip, /**< SCIP data structure */
1679 SCIP_GUBCONS** gubcons /**< pointer to store GUB constraint data */
1680 )
1681 {
1682 assert(scip != NULL);
1683 assert(gubcons != NULL);
1684
1685 /* allocate memory for GUB constraint data structures */
1686 SCIP_CALL( SCIPallocBuffer(scip, gubcons) );
1687 (*gubcons)->gubvarssize = GUBCONSGROWVALUE;
1688 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubcons)->gubvars, (*gubcons)->gubvarssize) );
1689 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubcons)->gubvarsstatus, (*gubcons)->gubvarssize) );
1690
1691 (*gubcons)->ngubvars = 0;
1692
1693 return SCIP_OKAY;
1694 }
1695
1696 /** frees GUB constraint */
1697 static
GUBconsFree(SCIP * scip,SCIP_GUBCONS ** gubcons)1698 void GUBconsFree(
1699 SCIP* scip, /**< SCIP data structure */
1700 SCIP_GUBCONS** gubcons /**< pointer to GUB constraint data structure */
1701 )
1702 {
1703 assert(scip != NULL);
1704 assert(gubcons != NULL);
1705 assert((*gubcons)->gubvars != NULL);
1706 assert((*gubcons)->gubvarsstatus != NULL);
1707
1708 /* free allocated memory */
1709 SCIPfreeBufferArray(scip, &(*gubcons)->gubvarsstatus);
1710 SCIPfreeBufferArray(scip, &(*gubcons)->gubvars);
1711 SCIPfreeBuffer(scip, gubcons);
1712 }
1713
1714 /** adds variable to given GUB constraint */
1715 static
GUBconsAddVar(SCIP * scip,SCIP_GUBCONS * gubcons,int var)1716 SCIP_RETCODE GUBconsAddVar(
1717 SCIP* scip, /**< SCIP data structure */
1718 SCIP_GUBCONS* gubcons, /**< GUB constraint data */
1719 int var /**< index of given variable in knapsack constraint */
1720 )
1721 {
1722 assert(scip != NULL);
1723 assert(gubcons != NULL);
1724 assert(gubcons->ngubvars >= 0 && gubcons->ngubvars < gubcons->gubvarssize);
1725 assert(gubcons->gubvars != NULL);
1726 assert(gubcons->gubvarsstatus != NULL);
1727 assert(var >= 0);
1728
1729 /* add variable to GUB constraint */
1730 gubcons->gubvars[gubcons->ngubvars] = var;
1731 gubcons->gubvarsstatus[gubcons->ngubvars] = GUBVARSTATUS_UNINITIAL;
1732 gubcons->ngubvars++;
1733
1734 /* increase space allocated to GUB constraint if the number of variables reaches the size */
1735 if( gubcons->ngubvars == gubcons->gubvarssize )
1736 {
1737 int newlen;
1738
1739 newlen = gubcons->gubvarssize + GUBCONSGROWVALUE;
1740 SCIP_CALL( SCIPreallocBufferArray(scip, &gubcons->gubvars, newlen) );
1741 SCIP_CALL( SCIPreallocBufferArray(scip, &gubcons->gubvarsstatus, newlen) );
1742
1743 gubcons->gubvarssize = newlen;
1744 }
1745
1746 return SCIP_OKAY;
1747 }
1748
1749 /** deletes variable from its current GUB constraint */
1750 static
GUBconsDelVar(SCIP * scip,SCIP_GUBCONS * gubcons,int var,int gubvarsidx)1751 SCIP_RETCODE GUBconsDelVar(
1752 SCIP* scip, /**< SCIP data structure */
1753 SCIP_GUBCONS* gubcons, /**< GUB constraint data */
1754 int var, /**< index of given variable in knapsack constraint */
1755 int gubvarsidx /**< index of the variable in its current GUB constraint */
1756 )
1757 {
1758 assert(scip != NULL);
1759 assert(gubcons != NULL);
1760 assert(var >= 0);
1761 assert(gubvarsidx >= 0 && gubvarsidx < gubcons->ngubvars);
1762 assert(gubcons->ngubvars >= gubvarsidx+1);
1763 assert(gubcons->gubvars[gubvarsidx] == var);
1764
1765 /* delete variable from GUB by swapping it replacing in by the last variable in the GUB constraint */
1766 gubcons->gubvars[gubvarsidx] = gubcons->gubvars[gubcons->ngubvars-1];
1767 gubcons->gubvarsstatus[gubvarsidx] = gubcons->gubvarsstatus[gubcons->ngubvars-1];
1768 gubcons->ngubvars--;
1769
1770 /* decrease space allocated for the GUB constraint, if the last GUBCONSGROWVALUE+1 array entries are now empty */
1771 if( gubcons->ngubvars < gubcons->gubvarssize - GUBCONSGROWVALUE && gubcons->ngubvars > 0 )
1772 {
1773 int newlen;
1774
1775 newlen = gubcons->gubvarssize - GUBCONSGROWVALUE;
1776
1777 SCIP_CALL( SCIPreallocBufferArray(scip, &gubcons->gubvars, newlen) );
1778 SCIP_CALL( SCIPreallocBufferArray(scip, &gubcons->gubvarsstatus, newlen) );
1779
1780 gubcons->gubvarssize = newlen;
1781 }
1782
1783 return SCIP_OKAY;
1784 }
1785
1786 /** moves variable from current GUB constraint to a different existing (nonempty) GUB constraint */
1787 static
GUBsetMoveVar(SCIP * scip,SCIP_GUBSET * gubset,SCIP_VAR ** vars,int var,int oldgubcons,int newgubcons)1788 SCIP_RETCODE GUBsetMoveVar(
1789 SCIP* scip, /**< SCIP data structure */
1790 SCIP_GUBSET* gubset, /**< GUB set data structure */
1791 SCIP_VAR** vars, /**< variables in knapsack constraint */
1792 int var, /**< index of given variable in knapsack constraint */
1793 int oldgubcons, /**< index of old GUB constraint of given variable */
1794 int newgubcons /**< index of new GUB constraint of given variable */
1795 )
1796 {
1797 int oldgubvaridx;
1798 int replacevar;
1799 int j;
1800
1801 assert(scip != NULL);
1802 assert(gubset != NULL);
1803 assert(var >= 0);
1804 assert(oldgubcons >= 0 && oldgubcons < gubset->ngubconss);
1805 assert(newgubcons >= 0 && newgubcons < gubset->ngubconss);
1806 assert(oldgubcons != newgubcons);
1807 assert(gubset->gubconssidx[var] == oldgubcons);
1808 assert(gubset->gubconss[oldgubcons]->ngubvars > 0);
1809 assert(gubset->gubconss[newgubcons]->ngubvars >= 0);
1810
1811 SCIPdebugMsg(scip, " moving variable<%s> from GUB<%d> to GUB<%d>\n", SCIPvarGetName(vars[var]), oldgubcons, newgubcons);
1812
1813 oldgubvaridx = gubset->gubvarsidx[var];
1814
1815 /* delete variable from old GUB constraint by replacing it by the last variable of the GUB constraint */
1816 SCIP_CALL( GUBconsDelVar(scip, gubset->gubconss[oldgubcons], var, oldgubvaridx) );
1817
1818 /* in GUB set, update stored index of variable in old GUB constraint for the variable used for replacement;
1819 * replacement variable is given by old position of the deleted variable
1820 */
1821 replacevar = gubset->gubconss[oldgubcons]->gubvars[oldgubvaridx];
1822 assert(gubset->gubvarsidx[replacevar] == gubset->gubconss[oldgubcons]->ngubvars);
1823 gubset->gubvarsidx[replacevar] = oldgubvaridx;
1824
1825 /* add variable to the end of new GUB constraint */
1826 SCIP_CALL( GUBconsAddVar(scip, gubset->gubconss[newgubcons], var) );
1827 assert(gubset->gubconss[newgubcons]->gubvars[gubset->gubconss[newgubcons]->ngubvars-1] == var);
1828
1829 /* in GUB set, update stored index of GUB of moved variable and stored index of variable in this GUB constraint */
1830 gubset->gubconssidx[var] = newgubcons;
1831 gubset->gubvarsidx[var] = gubset->gubconss[newgubcons]->ngubvars-1;
1832
1833 /* delete old GUB constraint if it became empty */
1834 if( gubset->gubconss[oldgubcons]->ngubvars == 0 )
1835 {
1836 SCIPdebugMsg(scip, "deleting empty GUB cons<%d> from current GUB set\n", oldgubcons);
1837 #ifdef SCIP_DEBUG
1838 GUBsetPrint(scip, gubset, vars, NULL);
1839 #endif
1840
1841 /* free old GUB constraint */
1842 GUBconsFree(scip, &gubset->gubconss[oldgubcons]);
1843
1844 /* if empty GUB was not the last one in GUB set data structure, replace it by last GUB constraint */
1845 if( oldgubcons != gubset->ngubconss-1 )
1846 {
1847 gubset->gubconss[oldgubcons] = gubset->gubconss[gubset->ngubconss-1];
1848 gubset->gubconsstatus[oldgubcons] = gubset->gubconsstatus[gubset->ngubconss-1];
1849
1850 /* in GUB set, update stored index of GUB constraint for all variable of the GUB constraint used for replacement;
1851 * replacement GUB is given by old position of the deleted GUB
1852 */
1853 for( j = 0; j < gubset->gubconss[oldgubcons]->ngubvars; j++ )
1854 {
1855 assert(gubset->gubconssidx[gubset->gubconss[oldgubcons]->gubvars[j]] == gubset->ngubconss-1);
1856 gubset->gubconssidx[gubset->gubconss[oldgubcons]->gubvars[j]] = oldgubcons;
1857 }
1858 }
1859
1860 /* update number of GUB constraints */
1861 gubset->ngubconss--;
1862
1863 /* variable should be at given new position, unless new GUB constraint replaced empty old GUB constraint
1864 * (because it was at the end of the GUB constraint array)
1865 */
1866 assert(gubset->gubconssidx[var] == newgubcons
1867 || (newgubcons == gubset->ngubconss && gubset->gubconssidx[var] == oldgubcons));
1868 }
1869 #ifndef NDEBUG
1870 else
1871 assert(gubset->gubconssidx[var] == newgubcons);
1872 #endif
1873
1874 return SCIP_OKAY;
1875 }
1876
1877 /** swaps two variables in the same GUB constraint */
1878 static
GUBsetSwapVars(SCIP * scip,SCIP_GUBSET * gubset,int var1,int var2)1879 void GUBsetSwapVars(
1880 SCIP* scip, /**< SCIP data structure */
1881 SCIP_GUBSET* gubset, /**< GUB set data structure */
1882 int var1, /**< first variable to be swapped */
1883 int var2 /**< second variable to be swapped */
1884 )
1885 {
1886 int gubcons;
1887 int var1idx;
1888 GUBVARSTATUS var1status;
1889 int var2idx;
1890 GUBVARSTATUS var2status;
1891
1892 assert(scip != NULL);
1893 assert(gubset != NULL);
1894
1895 gubcons = gubset->gubconssidx[var1];
1896 assert(gubcons == gubset->gubconssidx[var2]);
1897
1898 /* nothing to be done if both variables are the same */
1899 if( var1 == var2 )
1900 return;
1901
1902 /* swap index and status of variables in GUB constraint */
1903 var1idx = gubset->gubvarsidx[var1];
1904 var1status = gubset->gubconss[gubcons]->gubvarsstatus[var1idx];
1905 var2idx = gubset->gubvarsidx[var2];
1906 var2status = gubset->gubconss[gubcons]->gubvarsstatus[var2idx];
1907
1908 gubset->gubvarsidx[var1] = var2idx;
1909 gubset->gubconss[gubcons]->gubvars[var1idx] = var2;
1910 gubset->gubconss[gubcons]->gubvarsstatus[var1idx] = var2status;
1911
1912 gubset->gubvarsidx[var2] = var1idx;
1913 gubset->gubconss[gubcons]->gubvars[var2idx] = var1;
1914 gubset->gubconss[gubcons]->gubvarsstatus[var2idx] = var1status;
1915 }
1916
1917 /** initializes partition of knapsack variables into nonoverlapping trivial GUB constraints (GUB with one variable) */
1918 static
GUBsetCreate(SCIP * scip,SCIP_GUBSET ** gubset,int nvars,SCIP_Longint * weights,SCIP_Longint capacity)1919 SCIP_RETCODE GUBsetCreate(
1920 SCIP* scip, /**< SCIP data structure */
1921 SCIP_GUBSET** gubset, /**< pointer to store GUB set data structure */
1922 int nvars, /**< number of variables in the knapsack constraint */
1923 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
1924 SCIP_Longint capacity /**< capacity of knapsack */
1925 )
1926 {
1927 int i;
1928
1929 assert(scip != NULL);
1930 assert(gubset != NULL);
1931 assert(nvars > 0);
1932 assert(weights != NULL);
1933 assert(capacity >= 0);
1934
1935 /* allocate memory for GUB set data structures */
1936 SCIP_CALL( SCIPallocBuffer(scip, gubset) );
1937 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubset)->gubconss, nvars) );
1938 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubset)->gubconsstatus, nvars) );
1939 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubset)->gubconssidx, nvars) );
1940 SCIP_CALL( SCIPallocBufferArray(scip, &(*gubset)->gubvarsidx, nvars) );
1941 (*gubset)->ngubconss = nvars;
1942 (*gubset)->nvars = nvars;
1943
1944 /* initialize the set of GUB constraints */
1945 for( i = 0; i < nvars; i++ )
1946 {
1947 /* assign each variable to a new (trivial) GUB constraint */
1948 SCIP_CALL( GUBconsCreate(scip, &(*gubset)->gubconss[i]) );
1949 SCIP_CALL( GUBconsAddVar(scip, (*gubset)->gubconss[i], i) );
1950
1951 /* set status of GUB constraint to initial */
1952 (*gubset)->gubconsstatus[i] = GUBCONSSTATUS_UNINITIAL;
1953
1954 (*gubset)->gubconssidx[i] = i;
1955 (*gubset)->gubvarsidx[i] = 0;
1956 assert((*gubset)->gubconss[i]->ngubvars == 1);
1957
1958 /* already updated status of variable in GUB constraint if it exceeds the capacity of the knapsack */
1959 if( weights[i] > capacity )
1960 (*gubset)->gubconss[(*gubset)->gubconssidx[i]]->gubvarsstatus[(*gubset)->gubvarsidx[i]] = GUBVARSTATUS_CAPACITYEXCEEDED;
1961 }
1962
1963 return SCIP_OKAY;
1964 }
1965
1966 /** frees GUB set data structure */
1967 static
GUBsetFree(SCIP * scip,SCIP_GUBSET ** gubset)1968 void GUBsetFree(
1969 SCIP* scip, /**< SCIP data structure */
1970 SCIP_GUBSET** gubset /**< pointer to GUB set data structure */
1971 )
1972 {
1973 int i;
1974
1975 assert(scip != NULL);
1976 assert(gubset != NULL);
1977 assert((*gubset)->gubconss != NULL);
1978 assert((*gubset)->gubconsstatus != NULL);
1979 assert((*gubset)->gubconssidx != NULL);
1980 assert((*gubset)->gubvarsidx != NULL);
1981
1982 /* free all GUB constraints */
1983 for( i = (*gubset)->ngubconss-1; i >= 0; --i )
1984 {
1985 assert((*gubset)->gubconss[i] != NULL);
1986 GUBconsFree(scip, &(*gubset)->gubconss[i]);
1987 }
1988
1989 /* free allocated memory */
1990 SCIPfreeBufferArray( scip, &(*gubset)->gubvarsidx );
1991 SCIPfreeBufferArray( scip, &(*gubset)->gubconssidx );
1992 SCIPfreeBufferArray( scip, &(*gubset)->gubconsstatus );
1993 SCIPfreeBufferArray( scip, &(*gubset)->gubconss );
1994 SCIPfreeBuffer(scip, gubset);
1995 }
1996
1997 #ifndef NDEBUG
1998 /** checks whether GUB set data structure is consistent */
1999 static
GUBsetCheck(SCIP * scip,SCIP_GUBSET * gubset,SCIP_VAR ** vars)2000 SCIP_RETCODE GUBsetCheck(
2001 SCIP* scip, /**< SCIP data structure */
2002 SCIP_GUBSET* gubset, /**< GUB set data structure */
2003 SCIP_VAR** vars /**< variables in the knapsack constraint */
2004 )
2005 {
2006 int i;
2007 int gubconsidx;
2008 int gubvaridx;
2009 SCIP_VAR* var1;
2010 SCIP_VAR* var2;
2011 SCIP_Bool var1negated;
2012 SCIP_Bool var2negated;
2013
2014 assert(scip != NULL);
2015 assert(gubset != NULL);
2016
2017 SCIPdebugMsg(scip, " GUB set consistency check:\n");
2018
2019 /* checks for all knapsack vars consistency of stored index of associated gubcons and corresponding index in gubvars */
2020 for( i = 0; i < gubset->nvars; i++ )
2021 {
2022 gubconsidx = gubset->gubconssidx[i];
2023 gubvaridx = gubset->gubvarsidx[i];
2024
2025 if( gubset->gubconss[gubconsidx]->gubvars[gubvaridx] != i )
2026 {
2027 SCIPdebugMsg(scip, " var<%d> should be in GUB<%d> at position<%d>, but stored is var<%d> instead\n", i,
2028 gubconsidx, gubvaridx, gubset->gubconss[gubconsidx]->gubvars[gubvaridx] );
2029 }
2030 assert(gubset->gubconss[gubconsidx]->gubvars[gubvaridx] == i);
2031 }
2032
2033 /* checks for each GUB whether all pairs of its variables have a common clique */
2034 for( i = 0; i < gubset->ngubconss; i++ )
2035 {
2036 int j;
2037
2038 for( j = 0; j < gubset->gubconss[i]->ngubvars; j++ )
2039 {
2040 int k;
2041
2042 /* get corresponding active problem variable */
2043 var1 = vars[gubset->gubconss[i]->gubvars[j]];
2044 var1negated = FALSE;
2045 SCIP_CALL( SCIPvarGetProbvarBinary(&var1, &var1negated) );
2046
2047 for( k = j+1; k < gubset->gubconss[i]->ngubvars; k++ )
2048 {
2049 /* get corresponding active problem variable */
2050 var2 = vars[gubset->gubconss[i]->gubvars[k]];
2051 var2negated = FALSE;
2052 SCIP_CALL( SCIPvarGetProbvarBinary(&var2, &var2negated) );
2053
2054 if( !SCIPvarsHaveCommonClique(var1, !var1negated, var2, !var2negated, TRUE) )
2055 {
2056 SCIPdebugMsg(scip, " GUB<%d>: var<%d,%s> and var<%d,%s> do not share a clique\n", i, j,
2057 SCIPvarGetName(vars[gubset->gubconss[i]->gubvars[j]]), k,
2058 SCIPvarGetName(vars[gubset->gubconss[i]->gubvars[k]]));
2059 SCIPdebugMsg(scip, " GUB<%d>: var<%d,%s> and var<%d,%s> do not share a clique\n", i, j,
2060 SCIPvarGetName(var1), k,
2061 SCIPvarGetName(var2));
2062 }
2063
2064 /* @todo: in case we used also negated cliques for the GUB partition, this assert has to be changed */
2065 assert(SCIPvarsHaveCommonClique(var1, !var1negated, var2, !var2negated, TRUE));
2066 }
2067 }
2068 }
2069 SCIPdebugMsg(scip, " --> successful\n");
2070
2071 return SCIP_OKAY;
2072 }
2073 #endif
2074
2075 /** calculates a partition of the given set of binary variables into cliques;
2076 * afterwards the output array contains one value for each variable, such that two variables got the same value iff they
2077 * were assigned to the same clique;
2078 * the first variable is always assigned to clique 0, and a variable can only be assigned to clique i if at least one of
2079 * the preceding variables was assigned to clique i-1;
2080 * note: in contrast to SCIPcalcCliquePartition(), variables with LP value 1 are put into trivial cliques (with one
2081 * variable) and for the remaining variables, a partition with a small number of cliques is constructed
2082 */
2083
2084 static
GUBsetCalcCliquePartition(SCIP * const scip,SCIP_VAR ** const vars,int const nvars,int * const cliquepartition,int * const ncliques,SCIP_Real * solvals)2085 SCIP_RETCODE GUBsetCalcCliquePartition(
2086 SCIP*const scip, /**< SCIP data structure */
2087 SCIP_VAR**const vars, /**< binary variables in the clique from which at most one can be set to 1 */
2088 int const nvars, /**< number of variables in the clique */
2089 int*const cliquepartition, /**< array of length nvars to store the clique partition */
2090 int*const ncliques, /**< pointer to store number of cliques actually contained in the partition */
2091 SCIP_Real* solvals /**< solution values of all given binary variables */
2092 )
2093 {
2094 SCIP_VAR** tmpvars;
2095 SCIP_VAR** cliquevars;
2096 SCIP_Bool* cliquevalues;
2097 SCIP_Bool* tmpvalues;
2098 int* varseq;
2099 int* sortkeys;
2100 int ncliquevars;
2101 int maxncliquevarscomp;
2102 int nignorevars;
2103 int nvarsused;
2104 int i;
2105
2106 assert(scip != NULL);
2107 assert(nvars == 0 || vars != NULL);
2108 assert(nvars == 0 || cliquepartition != NULL);
2109 assert(ncliques != NULL);
2110
2111 if( nvars == 0 )
2112 {
2113 *ncliques = 0;
2114 return SCIP_OKAY;
2115 }
2116
2117 /* allocate temporary memory for storing the variables of the current clique */
2118 SCIP_CALL( SCIPallocBufferArray(scip, &cliquevars, nvars) );
2119 SCIP_CALL( SCIPallocBufferArray(scip, &cliquevalues, nvars) );
2120 SCIP_CALL( SCIPallocBufferArray(scip, &tmpvalues, nvars) );
2121 SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpvars, vars, nvars) );
2122 SCIP_CALL( SCIPallocBufferArray(scip, &varseq, nvars) );
2123 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeys, nvars) );
2124
2125 /* initialize the cliquepartition array with -1 */
2126 /* initialize the tmpvalues array */
2127 for( i = nvars - 1; i >= 0; --i )
2128 {
2129 tmpvalues[i] = TRUE;
2130 cliquepartition[i] = -1;
2131 }
2132
2133 /* get corresponding active problem variables */
2134 SCIP_CALL( SCIPvarsGetProbvarBinary(&tmpvars, &tmpvalues, nvars) );
2135
2136 /* ignore variables with LP value 1 (will be assigned to trivial GUBs at the end) and sort remaining variables
2137 * by nondecreasing number of cliques the variables are in
2138 */
2139 nignorevars = 0;
2140 nvarsused = 0;
2141 for( i = 0; i < nvars; i++ )
2142 {
2143 if( SCIPisFeasEQ(scip, solvals[i], 1.0) )
2144 {
2145 /* variables with LP value 1 are put to the end of varseq array and will not be sorted */
2146 varseq[nvars-1-nignorevars] = i;
2147 nignorevars++;
2148 }
2149 else
2150 {
2151 /* remaining variables are put to the front of varseq array and will be sorted by their number of cliques */
2152 varseq[nvarsused] = i;
2153 sortkeys[nvarsused] = SCIPvarGetNCliques(tmpvars[i], tmpvalues[i]);
2154 nvarsused++;
2155 }
2156 }
2157 assert(nvarsused + nignorevars == nvars);
2158
2159 /* sort variables with LP value less than 1 by nondecreasing order of the number of cliques they are in */
2160 SCIPsortIntInt(sortkeys, varseq, nvarsused);
2161
2162 maxncliquevarscomp = MIN(nvars*nvars, MAXNCLIQUEVARSCOMP);
2163
2164 /* calculate the clique partition */
2165 *ncliques = 0;
2166 for( i = 0; i < nvars; ++i )
2167 {
2168 if( cliquepartition[varseq[i]] == -1 )
2169 {
2170 int j;
2171
2172 /* variable starts a new clique */
2173 cliquepartition[varseq[i]] = *ncliques;
2174 cliquevars[0] = tmpvars[varseq[i]];
2175 cliquevalues[0] = tmpvalues[varseq[i]];
2176 ncliquevars = 1;
2177
2178 /* if variable is not active (multi-aggregated or fixed), it cannot be in any clique and
2179 * if the variable has LP value 1 we do not want it to be in nontrivial cliques
2180 */
2181 if( SCIPvarIsActive(tmpvars[varseq[i]]) && i < nvarsused )
2182 {
2183 /* greedily fill up the clique */
2184 for( j = i + 1; j < nvarsused; ++j )
2185 {
2186 /* if variable is not active (multi-aggregated or fixed), it cannot be in any clique */
2187 if( cliquepartition[varseq[j]] == -1 && SCIPvarIsActive(tmpvars[varseq[j]]) )
2188 {
2189 int k;
2190
2191 /* check if every variable in the actual clique is in clique with the new variable */
2192 for( k = ncliquevars - 1; k >= 0; --k )
2193 {
2194 if( !SCIPvarsHaveCommonClique(tmpvars[varseq[j]], tmpvalues[varseq[j]], cliquevars[k],
2195 cliquevalues[k], TRUE) )
2196 break;
2197 }
2198
2199 if( k == -1 )
2200 {
2201 /* put the variable into the same clique */
2202 cliquepartition[varseq[j]] = cliquepartition[varseq[i]];
2203 cliquevars[ncliquevars] = tmpvars[varseq[j]];
2204 cliquevalues[ncliquevars] = tmpvalues[varseq[j]];
2205 ++ncliquevars;
2206 }
2207 }
2208 }
2209 }
2210
2211 /* this clique is finished */
2212 ++(*ncliques);
2213 }
2214 assert(cliquepartition[varseq[i]] >= 0 && cliquepartition[varseq[i]] < i + 1);
2215
2216 /* break if we reached the maximal number of comparisons */
2217 if( i * nvars > maxncliquevarscomp )
2218 break;
2219 }
2220 /* if we had too many variables fill up the cliquepartition and put each variable in a separate clique */
2221 for( ; i < nvars; ++i )
2222 {
2223 if( cliquepartition[varseq[i]] == -1 )
2224 {
2225 cliquepartition[varseq[i]] = *ncliques;
2226 ++(*ncliques);
2227 }
2228 }
2229
2230 /* free temporary memory */
2231 SCIPfreeBufferArray(scip, &sortkeys);
2232 SCIPfreeBufferArray(scip, &varseq);
2233 SCIPfreeBufferArray(scip, &tmpvars);
2234 SCIPfreeBufferArray(scip, &tmpvalues);
2235 SCIPfreeBufferArray(scip, &cliquevalues);
2236 SCIPfreeBufferArray(scip, &cliquevars);
2237
2238 return SCIP_OKAY;
2239 }
2240
2241 /** constructs sophisticated partition of knapsack variables into non-overlapping GUBs; current partition uses trivial GUBs */
2242 static
GUBsetGetCliquePartition(SCIP * scip,SCIP_GUBSET * gubset,SCIP_VAR ** vars,SCIP_Real * solvals)2243 SCIP_RETCODE GUBsetGetCliquePartition(
2244 SCIP* scip, /**< SCIP data structure */
2245 SCIP_GUBSET* gubset, /**< GUB set data structure */
2246 SCIP_VAR** vars, /**< variables in the knapsack constraint */
2247 SCIP_Real* solvals /**< solution values of all knapsack variables */
2248 )
2249 {
2250 int* cliquepartition;
2251 int* gubfirstvar;
2252 int ncliques;
2253 int currentgubconsidx;
2254 int newgubconsidx;
2255 int cliqueidx;
2256 int nvars;
2257 int i;
2258
2259 assert(scip != NULL);
2260 assert(gubset != NULL);
2261 assert(vars != NULL);
2262
2263 nvars = gubset->nvars;
2264 assert(nvars >= 0);
2265
2266 /* allocate temporary memory for clique partition */
2267 SCIP_CALL( SCIPallocBufferArray(scip, &cliquepartition, nvars) );
2268
2269 /* compute sophisticated clique partition */
2270 SCIP_CALL( GUBsetCalcCliquePartition(scip, vars, nvars, cliquepartition, &ncliques, solvals) );
2271
2272 /* allocate temporary memory for GUB set data structure */
2273 SCIP_CALL( SCIPallocBufferArray(scip, &gubfirstvar, ncliques) );
2274
2275 /* translate GUB partition into GUB set data structure */
2276 for( i = 0; i < ncliques; i++ )
2277 {
2278 /* initialize first variable for every GUB */
2279 gubfirstvar[i] = -1;
2280 }
2281 /* move every knapsack variable into GUB defined by clique partition */
2282 for( i = 0; i < nvars; i++ )
2283 {
2284 assert(cliquepartition[i] >= 0);
2285
2286 cliqueidx = cliquepartition[i];
2287 currentgubconsidx = gubset->gubconssidx[i];
2288 assert(gubset->gubconss[currentgubconsidx]->ngubvars == 1 );
2289
2290 /* variable is first element in GUB constraint defined by clique partition */
2291 if( gubfirstvar[cliqueidx] == -1 )
2292 {
2293 /* corresponding GUB constraint in GUB set data structure was already constructed (as initial trivial GUB);
2294 * note: no assert for gubconssidx, because it can changed due to deleting empty GUBs in GUBsetMoveVar()
2295 */
2296 assert(gubset->gubvarsidx[i] == 0);
2297 assert(gubset->gubconss[gubset->gubconssidx[i]]->gubvars[gubset->gubvarsidx[i]] == i);
2298
2299 /* remember the first variable found for the current GUB */
2300 gubfirstvar[cliqueidx] = i;
2301 }
2302 /* variable is additional element of GUB constraint defined by clique partition */
2303 else
2304 {
2305 assert(gubfirstvar[cliqueidx] >= 0 && gubfirstvar[cliqueidx] < i);
2306
2307 /* move variable to GUB constraint defined by clique partition; index of this GUB constraint is given by the
2308 * first variable of this GUB constraint
2309 */
2310 newgubconsidx = gubset->gubconssidx[gubfirstvar[cliqueidx]];
2311 assert(newgubconsidx != currentgubconsidx); /* because initially every variable is in a different GUB */
2312 SCIP_CALL( GUBsetMoveVar(scip, gubset, vars, i, currentgubconsidx, newgubconsidx) );
2313
2314 assert(gubset->gubconss[gubset->gubconssidx[i]]->gubvars[gubset->gubvarsidx[i]] == i);
2315 }
2316 }
2317
2318 #ifdef SCIP_DEBUG
2319 /* prints GUB set data structure */
2320 GUBsetPrint(scip, gubset, vars, solvals);
2321 #endif
2322
2323 #ifndef NDEBUG
2324 /* checks consistency of GUB set data structure */
2325 SCIP_CALL( GUBsetCheck(scip, gubset, vars) );
2326 #endif
2327
2328 /* free temporary memory */
2329 SCIPfreeBufferArray(scip, &gubfirstvar);
2330 SCIPfreeBufferArray(scip, &cliquepartition);
2331
2332 return SCIP_OKAY;
2333 }
2334
2335 /** gets a most violated cover C (\f$\sum_{j \in C} a_j > a_0\f$) for a given knapsack constraint \f$\sum_{j \in N} a_j x_j \leq a_0\f$
2336 * taking into consideration the following fixing: \f$j \in C\f$, if \f$j \in N_1 = \{j \in N : x^*_j = 1\}\f$ and
2337 * \f$j \in N \setminus C\f$, if \f$j \in N_0 = \{j \in N : x^*_j = 0\}\f$, if one exists.
2338 */
2339 static
getCover(SCIP * scip,SCIP_VAR ** vars,int nvars,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * covervars,int * noncovervars,int * ncovervars,int * nnoncovervars,SCIP_Longint * coverweight,SCIP_Bool * found,SCIP_Bool modtransused,int * ntightened,SCIP_Bool * fractional)2340 SCIP_RETCODE getCover(
2341 SCIP* scip, /**< SCIP data structure */
2342 SCIP_VAR** vars, /**< variables in knapsack constraint */
2343 int nvars, /**< number of variables in knapsack constraint */
2344 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2345 SCIP_Longint capacity, /**< capacity of knapsack */
2346 SCIP_Real* solvals, /**< solution values of all problem variables */
2347 int* covervars, /**< pointer to store cover variables */
2348 int* noncovervars, /**< pointer to store noncover variables */
2349 int* ncovervars, /**< pointer to store number of cover variables */
2350 int* nnoncovervars, /**< pointer to store number of noncover variables */
2351 SCIP_Longint* coverweight, /**< pointer to store weight of cover */
2352 SCIP_Bool* found, /**< pointer to store whether a cover was found */
2353 SCIP_Bool modtransused, /**< should modified transformed separation problem be used to find cover */
2354 int* ntightened, /**< pointer to store number of variables with tightened upper bound */
2355 SCIP_Bool* fractional /**< pointer to store whether the LP sol for knapsack vars is fractional */
2356 )
2357 {
2358 SCIP_Longint* transweights;
2359 SCIP_Real* transprofits;
2360 SCIP_Longint transcapacity;
2361 SCIP_Longint fixedonesweight;
2362 SCIP_Longint itemsweight;
2363 SCIP_Bool infeasible;
2364 int* fixedones;
2365 int* fixedzeros;
2366 int* items;
2367 int nfixedones;
2368 int nfixedzeros;
2369 int nitems;
2370 int j;
2371
2372 assert(scip != NULL);
2373 assert(vars != NULL);
2374 assert(nvars > 0);
2375 assert(weights != NULL);
2376 assert(capacity >= 0);
2377 assert(solvals != NULL);
2378 assert(covervars != NULL);
2379 assert(noncovervars != NULL);
2380 assert(ncovervars != NULL);
2381 assert(nnoncovervars != NULL);
2382 assert(coverweight != NULL);
2383 assert(found != NULL);
2384 assert(ntightened != NULL);
2385 assert(fractional != NULL);
2386
2387 SCIPdebugMsg(scip, " get cover for knapsack constraint\n");
2388
2389 /* allocates temporary memory */
2390 SCIP_CALL( SCIPallocBufferArray(scip, &transweights, nvars) );
2391 SCIP_CALL( SCIPallocBufferArray(scip, &transprofits, nvars) );
2392 SCIP_CALL( SCIPallocBufferArray(scip, &fixedones, nvars) );
2393 SCIP_CALL( SCIPallocBufferArray(scip, &fixedzeros, nvars) );
2394 SCIP_CALL( SCIPallocBufferArray(scip, &items, nvars) );
2395
2396 *found = FALSE;
2397 *ncovervars = 0;
2398 *nnoncovervars = 0;
2399 *coverweight = 0;
2400 *fractional = TRUE;
2401
2402 /* gets the following sets
2403 * N_1 = {j in N : x*_j = 1} (fixedones),
2404 * N_0 = {j in N : x*_j = 0} (fixedzeros) and
2405 * N\(N_0 & N_1) (items),
2406 * where x*_j is the solution value of variable x_j
2407 */
2408 nfixedones = 0;
2409 nfixedzeros = 0;
2410 nitems = 0;
2411 fixedonesweight = 0;
2412 itemsweight = 0;
2413 *ntightened = 0;
2414 for( j = 0; j < nvars; j++ )
2415 {
2416 assert(SCIPvarIsBinary(vars[j]));
2417
2418 /* tightens upper bound of x_j if weight of x_j is greater than capacity of knapsack */
2419 if( weights[j] > capacity )
2420 {
2421 SCIP_CALL( SCIPtightenVarUb(scip, vars[j], 0.0, FALSE, &infeasible, NULL) );
2422 assert(!infeasible);
2423 (*ntightened)++;
2424 continue;
2425 }
2426
2427 /* variable x_j has solution value one */
2428 if( SCIPisFeasEQ(scip, solvals[j], 1.0) )
2429 {
2430 fixedones[nfixedones] = j;
2431 nfixedones++;
2432 fixedonesweight += weights[j];
2433 }
2434 /* variable x_j has solution value zero */
2435 else if( SCIPisFeasEQ(scip, solvals[j], 0.0) )
2436 {
2437 fixedzeros[nfixedzeros] = j;
2438 nfixedzeros++;
2439 }
2440 /* variable x_j has fractional solution value */
2441 else
2442 {
2443 assert( SCIPisFeasGT(scip, solvals[j], 0.0) && SCIPisFeasLT(scip, solvals[j], 1.0) );
2444 items[nitems] = j;
2445 nitems++;
2446 itemsweight += weights[j];
2447 }
2448 }
2449 assert(nfixedones + nfixedzeros + nitems == nvars - (*ntightened));
2450
2451 /* sets whether the LP solution x* for the knapsack variables is fractional; if it is not fractional we stop
2452 * the separation routine
2453 */
2454 assert(nitems >= 0);
2455 if( nitems == 0 )
2456 {
2457 *fractional = FALSE;
2458 goto TERMINATE;
2459 }
2460 assert(*fractional);
2461
2462 /* transforms the traditional separation problem (under consideration of the following fixing:
2463 * z_j = 1 for all j in N_1, z_j = 0 for all j in N_0)
2464 *
2465 * min sum_{j in N\(N_0 & N_1)} (1 - x*_j) z_j
2466 * sum_{j in N\(N_0 & N_1)} a_j z_j >= (a_0 + 1) - sum_{j in N_1} a_j
2467 * z_j in {0,1}, j in N\(N_0 & N_1)
2468 *
2469 * to a knapsack problem in maximization form by complementing the variables
2470 *
2471 * sum_{j in N\(N_0 & N_1)} (1 - x*_j) -
2472 * max sum_{j in N\(N_0 & N_1)} (1 - x*_j) z_j
2473 * sum_{j in N\(N_0 & N_1)} a_j z_j <= sum_{j in N\N_0} a_j - (a_0 + 1)
2474 * z_j in {0,1}, j in N\(N_0 & N_1)
2475 */
2476
2477 /* gets weight and profit of variables in transformed knapsack problem */
2478 for( j = 0; j < nitems; j++ )
2479 {
2480 transweights[j] = weights[items[j]];
2481 transprofits[j] = 1.0 - solvals[items[j]];
2482 }
2483 /* gets capacity of transformed knapsack problem */
2484 transcapacity = fixedonesweight + itemsweight - capacity - 1;
2485
2486 /* if capacity of transformed knapsack problem is less than zero, there is no cover
2487 * (when variables fixed to zero are not used)
2488 */
2489 if( transcapacity < 0 )
2490 {
2491 assert(!(*found));
2492 goto TERMINATE;
2493 }
2494
2495 if( modtransused )
2496 {
2497 /* transforms the modified separation problem (under consideration of the following fixing:
2498 * z_j = 1 for all j in N_1, z_j = 0 for all j in N_0)
2499 *
2500 * min sum_{j in N\(N_0 & N_1)} (1 - x*_j) a_j z_j
2501 * sum_{j in N\(N_0 & N_1)} a_j z_j >= (a_0 + 1) - sum_{j in N_1} a_j
2502 * z_j in {0,1}, j in N\(N_0 & N_1)
2503 *
2504 * to a knapsack problem in maximization form by complementing the variables
2505 *
2506 * sum_{j in N\(N_0 & N_1)} (1 - x*_j) a_j -
2507 * max sum_{j in N\(N_0 & N_1)} (1 - x*_j) a_j z_j
2508 * sum_{j in N\(N_0 & N_1)} a_j z_j <= sum_{j in N\N_0} a_j - (a_0 + 1)
2509 * z_j in {0,1}, j in N\(N_0 & N_1)
2510 */
2511
2512 /* gets weight and profit of variables in modified transformed knapsack problem */
2513 for( j = 0; j < nitems; j++ )
2514 {
2515 transprofits[j] *= weights[items[j]];
2516 assert(SCIPisFeasPositive(scip, transprofits[j]));
2517 }
2518 }
2519
2520 /* solves (modified) transformed knapsack problem approximately by solving the LP-relaxation of the (modified)
2521 * transformed knapsack problem using Dantzig's method and rounding down the solution.
2522 * let z* be the solution, then
2523 * j in C, if z*_j = 0 and
2524 * i in N\C, if z*_j = 1.
2525 */
2526 SCIP_CALL( SCIPsolveKnapsackApproximately(scip, nitems, transweights, transprofits, transcapacity, items,
2527 noncovervars, covervars, nnoncovervars, ncovervars, NULL) );
2528 /*assert(checkSolveKnapsack(scip, nitems, transweights, transprofits, items, weights, solvals, modtransused));*/
2529
2530 /* constructs cover C (sum_{j in C} a_j > a_0) */
2531 for( j = 0; j < *ncovervars; j++ )
2532 {
2533 (*coverweight) += weights[covervars[j]];
2534 }
2535
2536 /* adds all variables from N_1 to C */
2537 for( j = 0; j < nfixedones; j++ )
2538 {
2539 covervars[*ncovervars] = fixedones[j];
2540 (*ncovervars)++;
2541 (*coverweight) += weights[fixedones[j]];
2542 }
2543
2544 /* adds all variables from N_0 to N\C */
2545 for( j = 0; j < nfixedzeros; j++ )
2546 {
2547 noncovervars[*nnoncovervars] = fixedzeros[j];
2548 (*nnoncovervars)++;
2549 }
2550 assert((*ncovervars) + (*nnoncovervars) == nvars - (*ntightened));
2551 assert((*coverweight) > capacity);
2552 *found = TRUE;
2553
2554 TERMINATE:
2555 /* frees temporary memory */
2556 SCIPfreeBufferArray(scip, &items);
2557 SCIPfreeBufferArray(scip, &fixedzeros);
2558 SCIPfreeBufferArray(scip, &fixedones);
2559 SCIPfreeBufferArray(scip, &transprofits);
2560 SCIPfreeBufferArray(scip, &transweights);
2561
2562 SCIPdebugMsg(scip, " get cover for knapsack constraint -- end\n");
2563
2564 return SCIP_OKAY;
2565 }
2566
2567 #ifndef NDEBUG
2568 /** checks if minweightidx is set correctly
2569 */
2570 static
checkMinweightidx(SCIP_Longint * weights,SCIP_Longint capacity,int * covervars,int ncovervars,SCIP_Longint coverweight,int minweightidx,int j)2571 SCIP_Bool checkMinweightidx(
2572 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2573 SCIP_Longint capacity, /**< capacity of knapsack */
2574 int* covervars, /**< pointer to store cover variables */
2575 int ncovervars, /**< pointer to store number of cover variables */
2576 SCIP_Longint coverweight, /**< pointer to store weight of cover */
2577 int minweightidx, /**< index of variable in cover variables with minimum weight */
2578 int j /**< current index in cover variables */
2579 )
2580 {
2581 SCIP_Longint minweight;
2582 int i;
2583
2584 assert(weights != NULL);
2585 assert(covervars != NULL);
2586 assert(ncovervars > 0);
2587
2588 minweight = weights[covervars[minweightidx]];
2589
2590 /* checks if all cover variables before index j have weight greater than minweight */
2591 for( i = 0; i < j; i++ )
2592 {
2593 assert(weights[covervars[i]] > minweight);
2594 if( weights[covervars[i]] <= minweight )
2595 return FALSE;
2596 }
2597
2598 /* checks if all variables before index j cannot be removed, i.e. i cannot be the next minweightidx */
2599 for( i = 0; i < j; i++ )
2600 {
2601 assert(coverweight - weights[covervars[i]] <= capacity);
2602 if( coverweight - weights[covervars[i]] > capacity )
2603 return FALSE;
2604 }
2605 return TRUE;
2606 }
2607 #endif
2608
2609
2610 /** gets partition \f$(C_1,C_2)\f$ of minimal cover \f$C\f$, i.e. \f$C_1 \cup C_2 = C\f$ and \f$C_1 \cap C_2 = \emptyset\f$,
2611 * with \f$C_1\f$ not empty; chooses partition as follows \f$C_2 = \{ j \in C : x^*_j = 1 \}\f$ and \f$C_1 = C \setminus C_2\f$
2612 */
2613 static
getPartitionCovervars(SCIP * scip,SCIP_Real * solvals,int * covervars,int ncovervars,int * varsC1,int * varsC2,int * nvarsC1,int * nvarsC2)2614 void getPartitionCovervars(
2615 SCIP* scip, /**< SCIP data structure */
2616 SCIP_Real* solvals, /**< solution values of all problem variables */
2617 int* covervars, /**< cover variables */
2618 int ncovervars, /**< number of cover variables */
2619 int* varsC1, /**< pointer to store variables in C1 */
2620 int* varsC2, /**< pointer to store variables in C2 */
2621 int* nvarsC1, /**< pointer to store number of variables in C1 */
2622 int* nvarsC2 /**< pointer to store number of variables in C2 */
2623 )
2624 {
2625 int j;
2626
2627 assert(scip != NULL);
2628 assert(ncovervars >= 0);
2629 assert(solvals != NULL);
2630 assert(covervars != NULL);
2631 assert(varsC1 != NULL);
2632 assert(varsC2 != NULL);
2633 assert(nvarsC1 != NULL);
2634 assert(nvarsC2 != NULL);
2635
2636 *nvarsC1 = 0;
2637 *nvarsC2 = 0;
2638 for( j = 0; j < ncovervars; j++ )
2639 {
2640 assert(SCIPisFeasGT(scip, solvals[covervars[j]], 0.0));
2641
2642 /* variable has solution value one */
2643 if( SCIPisGE(scip, solvals[covervars[j]], 1.0) )
2644 {
2645 varsC2[*nvarsC2] = covervars[j];
2646 (*nvarsC2)++;
2647 }
2648 /* variable has solution value less than one */
2649 else
2650 {
2651 assert(SCIPisLT(scip, solvals[covervars[j]], 1.0));
2652 varsC1[*nvarsC1] = covervars[j];
2653 (*nvarsC1)++;
2654 }
2655 }
2656 assert((*nvarsC1) + (*nvarsC2) == ncovervars);
2657 }
2658
2659 /** changes given partition (C_1,C_2) of minimal cover C, if |C1| = 1, by moving one and two (if possible) variables from
2660 * C2 to C1 if |C1| = 1 and |C1| = 0, respectively.
2661 */
2662 static
changePartitionCovervars(SCIP * scip,SCIP_Longint * weights,int * varsC1,int * varsC2,int * nvarsC1,int * nvarsC2)2663 SCIP_RETCODE changePartitionCovervars(
2664 SCIP* scip, /**< SCIP data structure */
2665 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2666 int* varsC1, /**< pointer to store variables in C1 */
2667 int* varsC2, /**< pointer to store variables in C2 */
2668 int* nvarsC1, /**< pointer to store number of variables in C1 */
2669 int* nvarsC2 /**< pointer to store number of variables in C2 */
2670 )
2671 {
2672 SCIP_Real* sortkeysC2;
2673 int j;
2674
2675 assert(*nvarsC1 >= 0 && *nvarsC1 <= 1);
2676 assert(*nvarsC2 > 0);
2677
2678 /* allocates temporary memory */
2679 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysC2, *nvarsC2) );
2680
2681 /* sorts variables in C2 such that a_1 >= .... >= a_|C2| */
2682 for( j = 0; j < *nvarsC2; j++ )
2683 sortkeysC2[j] = (SCIP_Real) weights[varsC2[j]];
2684 SCIPsortDownRealInt(sortkeysC2, varsC2, *nvarsC2);
2685
2686 /* adds one or two variable from C2 with smallest weight to C1 and removes them from C2 */
2687 assert(*nvarsC2 == 1 || weights[varsC2[(*nvarsC2)-1]] <= weights[varsC2[(*nvarsC2)-2]]);
2688 while( *nvarsC1 < 2 && *nvarsC2 > 0 )
2689 {
2690 varsC1[*nvarsC1] = varsC2[(*nvarsC2)-1];
2691 (*nvarsC1)++;
2692 (*nvarsC2)--;
2693 }
2694
2695 /* frees temporary memory */
2696 SCIPfreeBufferArray(scip, &sortkeysC2);
2697
2698 return SCIP_OKAY;
2699 }
2700
2701 /** changes given partition (C_1,C_2) of feasible set C, if |C1| = 1, by moving one variable from C2 to C1 */
2702 static
changePartitionFeasiblesetvars(SCIP * scip,SCIP_Longint * weights,int * varsC1,int * varsC2,int * nvarsC1,int * nvarsC2)2703 SCIP_RETCODE changePartitionFeasiblesetvars(
2704 SCIP* scip, /**< SCIP data structure */
2705 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2706 int* varsC1, /**< pointer to store variables in C1 */
2707 int* varsC2, /**< pointer to store variables in C2 */
2708 int* nvarsC1, /**< pointer to store number of variables in C1 */
2709 int* nvarsC2 /**< pointer to store number of variables in C2 */
2710 )
2711 {
2712 SCIP_Real* sortkeysC2;
2713 int j;
2714
2715 assert(*nvarsC1 >= 0 && *nvarsC1 <= 1);
2716 assert(*nvarsC2 > 0);
2717
2718 /* allocates temporary memory */
2719 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysC2, *nvarsC2) );
2720
2721 /* sorts variables in C2 such that a_1 >= .... >= a_|C2| */
2722 for( j = 0; j < *nvarsC2; j++ )
2723 sortkeysC2[j] = (SCIP_Real) weights[varsC2[j]];
2724 SCIPsortDownRealInt(sortkeysC2, varsC2, *nvarsC2);
2725
2726 /* adds variable from C2 with smallest weight to C1 and removes it from C2 */
2727 assert(*nvarsC2 == 1 || weights[varsC2[(*nvarsC2)-1]] <= weights[varsC2[(*nvarsC2)-2]]);
2728 varsC1[*nvarsC1] = varsC2[(*nvarsC2)-1];
2729 (*nvarsC1)++;
2730 (*nvarsC2)--;
2731
2732 /* frees temporary memory */
2733 SCIPfreeBufferArray(scip, &sortkeysC2);
2734
2735 return SCIP_OKAY;
2736 }
2737
2738
2739 /** gets partition \f$(F,R)\f$ of \f$N \setminus C\f$ where \f$C\f$ is a minimal cover, i.e. \f$F \cup R = N \setminus C\f$
2740 * and \f$F \cap R = \emptyset\f$; chooses partition as follows \f$R = \{ j \in N \setminus C : x^*_j = 0 \}\f$ and
2741 * \f$F = (N \setminus C) \setminus F\f$
2742 */
2743 static
getPartitionNoncovervars(SCIP * scip,SCIP_Real * solvals,int * noncovervars,int nnoncovervars,int * varsF,int * varsR,int * nvarsF,int * nvarsR)2744 void getPartitionNoncovervars(
2745 SCIP* scip, /**< SCIP data structure */
2746 SCIP_Real* solvals, /**< solution values of all problem variables */
2747 int* noncovervars, /**< noncover variables */
2748 int nnoncovervars, /**< number of noncover variables */
2749 int* varsF, /**< pointer to store variables in F */
2750 int* varsR, /**< pointer to store variables in R */
2751 int* nvarsF, /**< pointer to store number of variables in F */
2752 int* nvarsR /**< pointer to store number of variables in R */
2753 )
2754 {
2755 int j;
2756
2757 assert(scip != NULL);
2758 assert(nnoncovervars >= 0);
2759 assert(solvals != NULL);
2760 assert(noncovervars != NULL);
2761 assert(varsF != NULL);
2762 assert(varsR != NULL);
2763 assert(nvarsF != NULL);
2764 assert(nvarsR != NULL);
2765
2766 *nvarsF = 0;
2767 *nvarsR = 0;
2768
2769 for( j = 0; j < nnoncovervars; j++ )
2770 {
2771 /* variable has solution value zero */
2772 if( SCIPisFeasEQ(scip, solvals[noncovervars[j]], 0.0) )
2773 {
2774 varsR[*nvarsR] = noncovervars[j];
2775 (*nvarsR)++;
2776 }
2777 /* variable has solution value greater than zero */
2778 else
2779 {
2780 assert(SCIPisFeasGT(scip, solvals[noncovervars[j]], 0.0));
2781 varsF[*nvarsF] = noncovervars[j];
2782 (*nvarsF)++;
2783 }
2784 }
2785 assert((*nvarsF) + (*nvarsR) == nnoncovervars);
2786 }
2787
2788 /** sorts variables in F, C_2, and R according to the second level lifting sequence that will be used in the sequential
2789 * lifting procedure
2790 */
2791 static
getLiftingSequence(SCIP * scip,SCIP_Real * solvals,SCIP_Longint * weights,int * varsF,int * varsC2,int * varsR,int nvarsF,int nvarsC2,int nvarsR)2792 SCIP_RETCODE getLiftingSequence(
2793 SCIP* scip, /**< SCIP data structure */
2794 SCIP_Real* solvals, /**< solution values of all problem variables */
2795 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2796 int* varsF, /**< pointer to store variables in F */
2797 int* varsC2, /**< pointer to store variables in C2 */
2798 int* varsR, /**< pointer to store variables in R */
2799 int nvarsF, /**< number of variables in F */
2800 int nvarsC2, /**< number of variables in C2 */
2801 int nvarsR /**< number of variables in R */
2802 )
2803 {
2804 SORTKEYPAIR** sortkeypairsF;
2805 SORTKEYPAIR* sortkeypairsFstore;
2806 SCIP_Real* sortkeysC2;
2807 SCIP_Real* sortkeysR;
2808 int j;
2809
2810 assert(scip != NULL);
2811 assert(solvals != NULL);
2812 assert(weights != NULL);
2813 assert(varsF != NULL);
2814 assert(varsC2 != NULL);
2815 assert(varsR != NULL);
2816 assert(nvarsF >= 0);
2817 assert(nvarsC2 >= 0);
2818 assert(nvarsR >= 0);
2819
2820 /* allocates temporary memory */
2821 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairsF, nvarsF) );
2822 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairsFstore, nvarsF) );
2823 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysC2, nvarsC2) );
2824 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysR, nvarsR) );
2825
2826 /* gets sorting key for variables in F corresponding to the following lifting sequence
2827 * sequence 1: non-increasing absolute difference between x*_j and the value the variable is fixed to, i.e.
2828 * x*_1 >= x*_2 >= ... >= x*_|F|
2829 * in case of equality uses
2830 * sequence 4: non-increasing a_j, i.e. a_1 >= a_2 >= ... >= a_|C_2|
2831 */
2832 for( j = 0; j < nvarsF; j++ )
2833 {
2834 sortkeypairsF[j] = &(sortkeypairsFstore[j]);
2835 sortkeypairsF[j]->key1 = solvals[varsF[j]];
2836 sortkeypairsF[j]->key2 = (SCIP_Real) weights[varsF[j]];
2837 }
2838
2839 /* gets sorting key for variables in C_2 corresponding to the following lifting sequence
2840 * sequence 4: non-increasing a_j, i.e. a_1 >= a_2 >= ... >= a_|C_2|
2841 */
2842 for( j = 0; j < nvarsC2; j++ )
2843 sortkeysC2[j] = (SCIP_Real) weights[varsC2[j]];
2844
2845 /* gets sorting key for variables in R corresponding to the following lifting sequence
2846 * sequence 4: non-increasing a_j, i.e. a_1 >= a_2 >= ... >= a_|R|
2847 */
2848 for( j = 0; j < nvarsR; j++ )
2849 sortkeysR[j] = (SCIP_Real) weights[varsR[j]];
2850
2851 /* sorts F, C2 and R */
2852 if( nvarsF > 0 )
2853 {
2854 SCIPsortDownPtrInt((void**)sortkeypairsF, varsF, compSortkeypairs, nvarsF);
2855 }
2856 if( nvarsC2 > 0 )
2857 {
2858 SCIPsortDownRealInt(sortkeysC2, varsC2, nvarsC2);
2859 }
2860 if( nvarsR > 0)
2861 {
2862 SCIPsortDownRealInt(sortkeysR, varsR, nvarsR);
2863 }
2864
2865 /* frees temporary memory */
2866 SCIPfreeBufferArray(scip, &sortkeysR);
2867 SCIPfreeBufferArray(scip, &sortkeysC2);
2868 SCIPfreeBufferArray(scip, &sortkeypairsFstore);
2869 SCIPfreeBufferArray(scip, &sortkeypairsF);
2870
2871 return SCIP_OKAY;
2872 }
2873
2874 /** categorizes GUBs of knapsack GUB partion into GOC1, GNC1, GF, GC2, and GR and computes a lifting sequence of the GUBs
2875 * for the sequential GUB wise lifting procedure
2876 */
2877 static
getLiftingSequenceGUB(SCIP * scip,SCIP_GUBSET * gubset,SCIP_Real * solvals,SCIP_Longint * weights,int * varsC1,int * varsC2,int * varsF,int * varsR,int nvarsC1,int nvarsC2,int nvarsF,int nvarsR,int * gubconsGC1,int * gubconsGC2,int * gubconsGFC1,int * gubconsGR,int * ngubconsGC1,int * ngubconsGC2,int * ngubconsGFC1,int * ngubconsGR,int * ngubconscapexceed,int * maxgubvarssize)2878 SCIP_RETCODE getLiftingSequenceGUB(
2879 SCIP* scip, /**< SCIP data structure */
2880 SCIP_GUBSET* gubset, /**< GUB set data structure */
2881 SCIP_Real* solvals, /**< solution values of variables in knapsack constraint */
2882 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
2883 int* varsC1, /**< variables in C1 */
2884 int* varsC2, /**< variables in C2 */
2885 int* varsF, /**< variables in F */
2886 int* varsR, /**< variables in R */
2887 int nvarsC1, /**< number of variables in C1 */
2888 int nvarsC2, /**< number of variables in C2 */
2889 int nvarsF, /**< number of variables in F */
2890 int nvarsR, /**< number of variables in R */
2891 int* gubconsGC1, /**< pointer to store GUBs in GC1(GNC1+GOC1) */
2892 int* gubconsGC2, /**< pointer to store GUBs in GC2 */
2893 int* gubconsGFC1, /**< pointer to store GUBs in GFC1(GNC1+GF) */
2894 int* gubconsGR, /**< pointer to store GUBs in GR */
2895 int* ngubconsGC1, /**< pointer to store number of GUBs in GC1(GNC1+GOC1) */
2896 int* ngubconsGC2, /**< pointer to store number of GUBs in GC2 */
2897 int* ngubconsGFC1, /**< pointer to store number of GUBs in GFC1(GNC1+GF) */
2898 int* ngubconsGR, /**< pointer to store number of GUBs in GR */
2899 int* ngubconscapexceed, /**< pointer to store number of GUBs with only capacity exceeding variables */
2900 int* maxgubvarssize /**< pointer to store the maximal size of GUB constraints */
2901 )
2902 {
2903 SORTKEYPAIR** sortkeypairsGFC1;
2904 SORTKEYPAIR* sortkeypairsGFC1store;
2905 SCIP_Real* sortkeysC1;
2906 SCIP_Real* sortkeysC2;
2907 SCIP_Real* sortkeysR;
2908 int* nC1varsingubcons;
2909 int var;
2910 int gubconsidx;
2911 int varidx;
2912 int ngubconss;
2913 int ngubconsGOC1;
2914 int targetvar;
2915 int nvarsprocessed;
2916 int i;
2917 int j;
2918
2919 #if GUBSPLITGNC1GUBS
2920 SCIP_Bool gubconswithF;
2921 int origngubconss;
2922 origngubconss = gubset->ngubconss;
2923 #endif
2924
2925 assert(scip != NULL);
2926 assert(gubset != NULL);
2927 assert(solvals != NULL);
2928 assert(weights != NULL);
2929 assert(varsC1 != NULL);
2930 assert(varsC2 != NULL);
2931 assert(varsF != NULL);
2932 assert(varsR != NULL);
2933 assert(nvarsC1 > 0);
2934 assert(nvarsC2 >= 0);
2935 assert(nvarsF >= 0);
2936 assert(nvarsR >= 0);
2937 assert(gubconsGC1 != NULL);
2938 assert(gubconsGC2 != NULL);
2939 assert(gubconsGFC1 != NULL);
2940 assert(gubconsGR != NULL);
2941 assert(ngubconsGC1 != NULL);
2942 assert(ngubconsGC2 != NULL);
2943 assert(ngubconsGFC1 != NULL);
2944 assert(ngubconsGR != NULL);
2945 assert(maxgubvarssize != NULL);
2946
2947 ngubconss = gubset->ngubconss;
2948 nvarsprocessed = 0;
2949 ngubconsGOC1 = 0;
2950
2951 /* GUBs are categorized into different types according to the variables in volved
2952 * - GOC1: involves variables in C1 only -- no C2, R, F
2953 * - GNC1: involves variables in C1 and F (and R) -- no C2
2954 * - GF: involves variables in F (and R) only -- no C1, C2
2955 * - GC2: involves variables in C2 only -- no C1, R, F
2956 * - GR: involves variables in R only -- no C1, C2, F
2957 * which requires splitting GUBs in case they include variable in F and R.
2958 *
2959 * afterwards all GUBs (except GOC1 GUBs, which we do not need to lift) are sorted by a two level lifting sequence.
2960 * - first ordering level is: GFC1 (GNC1+GF), GC2, and GR.
2961 * - second ordering level is
2962 * GFC1: non-increasing number of variables in F and non-increasing max{x*_k : k in GFC1_j} in case of equality
2963 * GC2: non-increasing max{ a_k : k in GC2_j}; note that |GFC2_j| = 1
2964 * GR: non-increasing max{ a_k : k in GR_j}
2965 *
2966 * in additon, another GUB union, which is helpful for the lifting procedure, is formed
2967 * - GC1: GUBs of category GOC1 and GNC1
2968 * with second ordering level non-decreasing min{ a_k : k in GC1_j };
2969 * note that min{ a_k : k in GC1_j } always comes from the first variable in the GUB
2970 */
2971
2972 /* allocates temporary memory */
2973 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysC1, nvarsC1) );
2974 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysC2, nvarsC2) );
2975 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeysR, nvarsR) );
2976
2977 /* to get the GUB lifting sequence, we first sort all variables in F, C2, and R
2978 * - F: non-increasing x*_j and non-increasing a_j in case of equality
2979 * - C2: non-increasing a_j
2980 * - R: non-increasing a_j
2981 * furthermore, sort C1 variables as needed for initializing the minweight table (non-increasing a_j).
2982 */
2983
2984 /* gets sorting key for variables in C1 corresponding to the following ordering
2985 * non-decreasing a_j, i.e. a_1 <= a_2 <= ... <= a_|C_1|
2986 */
2987 for( j = 0; j < nvarsC1; j++ )
2988 {
2989 /* gets sortkeys */
2990 sortkeysC1[j] = (SCIP_Real) weights[varsC1[j]];
2991
2992 /* update status of variable in its gub constraint */
2993 gubconsidx = gubset->gubconssidx[varsC1[j]];
2994 varidx = gubset->gubvarsidx[varsC1[j]];
2995 gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] = GUBVARSTATUS_BELONGSTOSET_C1;
2996 }
2997
2998 /* gets sorting key for variables in F corresponding to the following ordering
2999 * non-increasing x*_j, i.e., x*_1 >= x*_2 >= ... >= x*_|F|, and
3000 * non-increasing a_j, i.e., a_1 >= a_2 >= ... >= a_|F| in case of equality
3001 * and updates status of each variable in F in GUB set data structure
3002 */
3003 for( j = 0; j < nvarsF; j++ )
3004 {
3005 /* update status of variable in its gub constraint */
3006 gubconsidx = gubset->gubconssidx[varsF[j]];
3007 varidx = gubset->gubvarsidx[varsF[j]];
3008 gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] = GUBVARSTATUS_BELONGSTOSET_F;
3009 }
3010
3011 /* gets sorting key for variables in C2 corresponding to the following ordering
3012 * non-increasing a_j, i.e., a_1 >= a_2 >= ... >= a_|C2|
3013 * and updates status of each variable in F in GUB set data structure
3014 */
3015 for( j = 0; j < nvarsC2; j++ )
3016 {
3017 /* gets sortkeys */
3018 sortkeysC2[j] = (SCIP_Real) weights[varsC2[j]];
3019
3020 /* update status of variable in its gub constraint */
3021 gubconsidx = gubset->gubconssidx[varsC2[j]];
3022 varidx = gubset->gubvarsidx[varsC2[j]];
3023 gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] = GUBVARSTATUS_BELONGSTOSET_C2;
3024 }
3025
3026 /* gets sorting key for variables in R corresponding to the following ordering
3027 * non-increasing a_j, i.e., a_1 >= a_2 >= ... >= a_|R|
3028 * and updates status of each variable in F in GUB set data structure
3029 */
3030 for( j = 0; j < nvarsR; j++ )
3031 {
3032 /* gets sortkeys */
3033 sortkeysR[j] = (SCIP_Real) weights[varsR[j]];
3034
3035 /* update status of variable in its gub constraint */
3036 gubconsidx = gubset->gubconssidx[varsR[j]];
3037 varidx = gubset->gubvarsidx[varsR[j]];
3038 gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] = GUBVARSTATUS_BELONGSTOSET_R;
3039 }
3040
3041 /* sorts C1, F, C2 and R */
3042 assert(nvarsC1 > 0);
3043 SCIPsortRealInt(sortkeysC1, varsC1, nvarsC1);
3044
3045 if( nvarsC2 > 0 )
3046 {
3047 SCIPsortDownRealInt(sortkeysC2, varsC2, nvarsC2);
3048 }
3049 if( nvarsR > 0)
3050 {
3051 SCIPsortDownRealInt(sortkeysR, varsR, nvarsR);
3052 }
3053
3054 /* frees temporary memory */
3055 SCIPfreeBufferArray(scip, &sortkeysR);
3056 SCIPfreeBufferArray(scip, &sortkeysC2);
3057 SCIPfreeBufferArray(scip, &sortkeysC1);
3058
3059 /* allocate and initialize temporary memory for sorting GUB constraints */
3060 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairsGFC1, ngubconss) );
3061 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairsGFC1store, ngubconss) );
3062 SCIP_CALL( SCIPallocBufferArray(scip, &nC1varsingubcons, ngubconss) );
3063 BMSclearMemoryArray(nC1varsingubcons, ngubconss);
3064 for( i = 0; i < ngubconss; i++)
3065 {
3066 sortkeypairsGFC1[i] = &(sortkeypairsGFC1store[i]);
3067 sortkeypairsGFC1[i]->key1 = 0.0;
3068 sortkeypairsGFC1[i]->key2 = 0.0;
3069 }
3070 *ngubconsGC1 = 0;
3071 *ngubconsGC2 = 0;
3072 *ngubconsGFC1 = 0;
3073 *ngubconsGR = 0;
3074 *ngubconscapexceed = 0;
3075 *maxgubvarssize = 0;
3076
3077 #ifndef NDEBUG
3078 for( i = 0; i < gubset->ngubconss; i++ )
3079 assert(gubset->gubconsstatus[i] == GUBCONSSTATUS_UNINITIAL);
3080 #endif
3081
3082 /* stores GUBs of group GC1 (GOC1+GNC1) and part of the GUBs of group GFC1 (GNC1 GUBs) and sorts variables in these GUBs
3083 * s.t. C1 variables come first (will automatically be sorted by non-decreasing weight).
3084 * gets sorting keys for GUBs of type GFC1 corresponding to the following ordering
3085 * non-increasing number of variables in F, and
3086 * non-increasing max{x*_k : k in GFC1_j} in case of equality
3087 */
3088 for( i = 0; i < nvarsC1; i++ )
3089 {
3090 int nvarsC1capexceed;
3091
3092 nvarsC1capexceed = 0;
3093
3094 var = varsC1[i];
3095 gubconsidx = gubset->gubconssidx[var];
3096 varidx = gubset->gubvarsidx[var];
3097
3098 assert(gubconsidx >= 0 && gubconsidx < ngubconss);
3099 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] == GUBVARSTATUS_BELONGSTOSET_C1);
3100
3101 /* current C1 variable is put to the front of its GUB where C1 part is stored by non-decreasing weigth;
3102 * note that variables in C1 are already sorted by non-decreasing weigth
3103 */
3104 targetvar = gubset->gubconss[gubconsidx]->gubvars[nC1varsingubcons[gubconsidx]];
3105 GUBsetSwapVars(scip, gubset, var, targetvar);
3106 nC1varsingubcons[gubconsidx]++;
3107
3108 /* the GUB was already handled (status set and stored in its group) by another variable of the GUB */
3109 if( gubset->gubconsstatus[gubconsidx] != GUBCONSSTATUS_UNINITIAL )
3110 {
3111 assert(gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GOC1
3112 || gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
3113 continue;
3114 }
3115
3116 /* determine the status of the current GUB constraint, GOC1 or GNC1; GUBs involving R variables are split into
3117 * GOC1/GNC1 and GF, if wanted. also update sorting key if GUB is of type GFC1 (GNC1)
3118 */
3119 #if GUBSPLITGNC1GUBS
3120 gubconswithF = FALSE;
3121 #endif
3122 for( j = 0; j < gubset->gubconss[gubconsidx]->ngubvars; j++ )
3123 {
3124 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[j] != GUBVARSTATUS_BELONGSTOSET_C2);
3125
3126 /* C1-variable: update number of C1/capacity exceeding variables */
3127 if( gubset->gubconss[gubconsidx]->gubvarsstatus[j] == GUBVARSTATUS_BELONGSTOSET_C1 )
3128 {
3129 nvarsC1capexceed++;
3130 nvarsprocessed++;
3131 }
3132 /* F-variable: update sort key (number of F variables in GUB) of corresponding GFC1-GUB */
3133 else if( gubset->gubconss[gubconsidx]->gubvarsstatus[j] == GUBVARSTATUS_BELONGSTOSET_F )
3134 {
3135 #if GUBSPLITGNC1GUBS
3136 gubconswithF = TRUE;
3137 #endif
3138 sortkeypairsGFC1[*ngubconsGFC1]->key1 += 1.0;
3139
3140 if( solvals[gubset->gubconss[gubconsidx]->gubvars[j]] > sortkeypairsGFC1[*ngubconsGFC1]->key2 )
3141 sortkeypairsGFC1[*ngubconsGFC1]->key2 = solvals[gubset->gubconss[gubconsidx]->gubvars[j]];
3142 }
3143 else if( gubset->gubconss[gubconsidx]->gubvarsstatus[j] == GUBVARSTATUS_CAPACITYEXCEEDED )
3144 {
3145 nvarsC1capexceed++;
3146 }
3147 else
3148 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[j] == GUBVARSTATUS_BELONGSTOSET_R);
3149 }
3150
3151 /* update set of GC1 GUBs */
3152 gubconsGC1[*ngubconsGC1] = gubconsidx;
3153 (*ngubconsGC1)++;
3154
3155 /* update maximum size of all GUB constraints */
3156 if( gubset->gubconss[gubconsidx]->gubvarssize > *maxgubvarssize )
3157 *maxgubvarssize = gubset->gubconss[gubconsidx]->gubvarssize;
3158
3159 /* set status of GC1-GUB (GOC1 or GNC1) and update set of GFC1 GUBs */
3160 if( nvarsC1capexceed == gubset->gubconss[gubconsidx]->ngubvars )
3161 {
3162 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GOC1;
3163 ngubconsGOC1++;
3164 }
3165 else
3166 {
3167 #if GUBSPLITGNC1GUBS
3168 /* only variables in C1 and R -- no in F: GUB will be split into GR and GOC1 GUBs */
3169 if( !gubconswithF )
3170 {
3171 GUBVARSTATUS movevarstatus;
3172
3173 assert(gubset->ngubconss < gubset->nvars);
3174
3175 /* create a new GUB for GR part of splitting */
3176 SCIP_CALL( GUBconsCreate(scip, &gubset->gubconss[gubset->ngubconss]) );
3177 gubset->ngubconss++;
3178 ngubconss = gubset->ngubconss;
3179
3180 /* fill GR with R variables in current GUB */
3181 for( j = gubset->gubconss[gubconsidx]->ngubvars-1; j >= 0; j-- )
3182 {
3183 movevarstatus = gubset->gubconss[gubconsidx]->gubvarsstatus[j];
3184 if( movevarstatus != GUBVARSTATUS_BELONGSTOSET_C1 )
3185 {
3186 assert(movevarstatus == GUBVARSTATUS_BELONGSTOSET_R || movevarstatus == GUBVARSTATUS_CAPACITYEXCEEDED);
3187 SCIP_CALL( GUBsetMoveVar(scip, gubset, vars, gubset->gubconss[gubconsidx]->gubvars[j],
3188 gubconsidx, ngubconss-1) );
3189 gubset->gubconss[ngubconss-1]->gubvarsstatus[gubset->gubconss[ngubconss-1]->ngubvars-1] =
3190 movevarstatus;
3191 }
3192 }
3193
3194 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GOC1;
3195 ngubconsGOC1++;
3196
3197 gubset->gubconsstatus[ngubconss-1] = GUBCONSSTATUS_BELONGSTOSET_GR;
3198 gubconsGR[*ngubconsGR] = ngubconss-1;
3199 (*ngubconsGR)++;
3200 }
3201 /* variables in C1, F, and maybe R: GNC1 GUB */
3202 else
3203 {
3204 assert(gubconswithF);
3205
3206 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GNC1;
3207 gubconsGFC1[*ngubconsGFC1] = gubconsidx;
3208 (*ngubconsGFC1)++;
3209 }
3210 #else
3211 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GNC1;
3212 gubconsGFC1[*ngubconsGFC1] = gubconsidx;
3213 (*ngubconsGFC1)++;
3214 #endif
3215 }
3216 }
3217
3218 /* stores GUBs of group GC2 (only trivial GUBs); sorting is not required because the C2 variables (which we loop over)
3219 * are already sorted correctly
3220 */
3221 for( i = 0; i < nvarsC2; i++ )
3222 {
3223 var = varsC2[i];
3224 gubconsidx = gubset->gubconssidx[var];
3225 varidx = gubset->gubvarsidx[var];
3226
3227 assert(gubconsidx >= 0 && gubconsidx < ngubconss);
3228 assert(gubset->gubconss[gubconsidx]->ngubvars == 1);
3229 assert(varidx == 0);
3230 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] == GUBVARSTATUS_BELONGSTOSET_C2);
3231 assert(gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_UNINITIAL);
3232
3233 /* set status of GC2 GUB */
3234 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GC2;
3235
3236 /* update group of GC2 GUBs */
3237 gubconsGC2[*ngubconsGC2] = gubconsidx;
3238 (*ngubconsGC2)++;
3239
3240 /* update maximum size of all GUB constraints */
3241 if( gubset->gubconss[gubconsidx]->gubvarssize > *maxgubvarssize )
3242 *maxgubvarssize = gubset->gubconss[gubconsidx]->gubvarssize;
3243
3244 nvarsprocessed++;
3245 }
3246
3247 /* stores remaining part of the GUBs of group GFC1 (GF GUBs) and gets GUB sorting keys corresp. to following ordering
3248 * non-increasing number of variables in F, and
3249 * non-increasing max{x*_k : k in GFC1_j} in case of equality
3250 */
3251 for( i = 0; i < nvarsF; i++ )
3252 {
3253 var = varsF[i];
3254 gubconsidx = gubset->gubconssidx[var];
3255 varidx = gubset->gubvarsidx[var];
3256
3257 assert(gubconsidx >= 0 && gubconsidx < ngubconss);
3258 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] == GUBVARSTATUS_BELONGSTOSET_F);
3259
3260 nvarsprocessed++;
3261
3262 /* the GUB was already handled (status set and stored in its group) by another variable of the GUB */
3263 if( gubset->gubconsstatus[gubconsidx] != GUBCONSSTATUS_UNINITIAL )
3264 {
3265 assert(gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GF
3266 || gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
3267 continue;
3268 }
3269
3270 /* set status of GF GUB */
3271 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GF;
3272
3273 /* update sorting key of corresponding GFC1 GUB */
3274 for( j = 0; j < gubset->gubconss[gubconsidx]->ngubvars; j++ )
3275 {
3276 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[j] != GUBVARSTATUS_BELONGSTOSET_C2
3277 && gubset->gubconss[gubconsidx]->gubvarsstatus[j] != GUBVARSTATUS_BELONGSTOSET_C1);
3278
3279 /* F-variable: update sort key (number of F variables in GUB) of corresponding GFC1-GUB */
3280 if( gubset->gubconss[gubconsidx]->gubvarsstatus[j] == GUBVARSTATUS_BELONGSTOSET_F )
3281 {
3282 sortkeypairsGFC1[*ngubconsGFC1]->key1 += 1.0;
3283
3284 if( solvals[gubset->gubconss[gubconsidx]->gubvars[j]] > sortkeypairsGFC1[*ngubconsGFC1]->key2 )
3285 sortkeypairsGFC1[*ngubconsGFC1]->key2 = solvals[gubset->gubconss[gubconsidx]->gubvars[j]];
3286 }
3287 }
3288
3289 /* update set of GFC1 GUBs */
3290 gubconsGFC1[*ngubconsGFC1] = gubconsidx;
3291 (*ngubconsGFC1)++;
3292
3293 /* update maximum size of all GUB constraints */
3294 if( gubset->gubconss[gubconsidx]->gubvarssize > *maxgubvarssize )
3295 *maxgubvarssize = gubset->gubconss[gubconsidx]->gubvarssize;
3296 }
3297
3298 /* stores GUBs of group GR; sorting is not required because the R variables (which we loop over) are already sorted
3299 * correctly
3300 */
3301 for( i = 0; i < nvarsR; i++ )
3302 {
3303 var = varsR[i];
3304 gubconsidx = gubset->gubconssidx[var];
3305 varidx = gubset->gubvarsidx[var];
3306
3307 assert(gubconsidx >= 0 && gubconsidx < ngubconss);
3308 assert(gubset->gubconss[gubconsidx]->gubvarsstatus[varidx] == GUBVARSTATUS_BELONGSTOSET_R);
3309
3310 nvarsprocessed++;
3311
3312 /* the GUB was already handled (status set and stored in its group) by another variable of the GUB */
3313 if( gubset->gubconsstatus[gubconsidx] != GUBCONSSTATUS_UNINITIAL )
3314 {
3315 assert(gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GR
3316 || gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GF
3317 || gubset->gubconsstatus[gubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
3318 continue;
3319 }
3320
3321 /* set status of GR GUB */
3322 gubset->gubconsstatus[gubconsidx] = GUBCONSSTATUS_BELONGSTOSET_GR;
3323
3324 /* update set of GR GUBs */
3325 gubconsGR[*ngubconsGR] = gubconsidx;
3326 (*ngubconsGR)++;
3327
3328 /* update maximum size of all GUB constraints */
3329 if( gubset->gubconss[gubconsidx]->gubvarssize > *maxgubvarssize )
3330 *maxgubvarssize = gubset->gubconss[gubconsidx]->gubvarssize;
3331 }
3332 assert(nvarsprocessed == nvarsC1 + nvarsC2 + nvarsF + nvarsR);
3333
3334 /* update number of GUBs with only capacity exceeding variables (will not be used for lifting) */
3335 (*ngubconscapexceed) = ngubconss - (ngubconsGOC1 + (*ngubconsGC2) + (*ngubconsGFC1) + (*ngubconsGR));
3336 assert(*ngubconscapexceed >= 0);
3337 #ifndef NDEBUG
3338 {
3339 int check;
3340
3341 check = 0;
3342
3343 /* remaining not handled GUBs should only contain capacity exceeding variables */
3344 for( i = 0; i < ngubconss; i++ )
3345 {
3346 if( gubset->gubconsstatus[i] == GUBCONSSTATUS_UNINITIAL )
3347 check++;
3348 }
3349 assert(check == *ngubconscapexceed);
3350 }
3351 #endif
3352
3353 /* sort GFCI GUBs according to computed sorting keys */
3354 if( (*ngubconsGFC1) > 0 )
3355 {
3356 SCIPsortDownPtrInt((void**)sortkeypairsGFC1, gubconsGFC1, compSortkeypairs, (*ngubconsGFC1));
3357 }
3358
3359 /* free temporary memory */
3360 #if GUBSPLITGNC1GUBS
3361 ngubconss = origngubconss;
3362 #endif
3363 SCIPfreeBufferArray(scip, &nC1varsingubcons);
3364 SCIPfreeBufferArray(scip, &sortkeypairsGFC1store);
3365 SCIPfreeBufferArray(scip, &sortkeypairsGFC1);
3366
3367 return SCIP_OKAY;
3368 }
3369
3370 /** enlarges minweight table to at least the given length */
3371 static
enlargeMinweights(SCIP * scip,SCIP_Longint ** minweightsptr,int * minweightslen,int * minweightssize,int newlen)3372 SCIP_RETCODE enlargeMinweights(
3373 SCIP* scip, /**< SCIP data structure */
3374 SCIP_Longint** minweightsptr, /**< pointer to minweights table */
3375 int* minweightslen, /**< pointer to store number of entries in minweights table (incl. z=0) */
3376 int* minweightssize, /**< pointer to current size of minweights table */
3377 int newlen /**< new length of minweights table */
3378 )
3379 {
3380 int j;
3381
3382 assert(minweightsptr != NULL);
3383 assert(*minweightsptr != NULL);
3384 assert(minweightslen != NULL);
3385 assert(*minweightslen >= 0);
3386 assert(minweightssize != NULL);
3387 assert(*minweightssize >= 0);
3388
3389 if( newlen > *minweightssize )
3390 {
3391 int newsize;
3392
3393 /* reallocate table memory */
3394 newsize = SCIPcalcMemGrowSize(scip, newlen);
3395 SCIP_CALL( SCIPreallocBufferArray(scip, minweightsptr, newsize) );
3396 *minweightssize = newsize;
3397 }
3398 assert(newlen <= *minweightssize);
3399
3400 /* initialize new elements */
3401 for( j = *minweightslen; j < newlen; ++j )
3402 (*minweightsptr)[j] = SCIP_LONGINT_MAX;
3403 *minweightslen = newlen;
3404
3405 return SCIP_OKAY;
3406 }
3407
3408 /** lifts given inequality
3409 * sum_{j in M_1} x_j <= alpha_0
3410 * valid for
3411 * S^0 = { x in {0,1}^|M_1| : sum_{j in M_1} a_j x_j <= a_0 - sum_{j in M_2} a_j }
3412 * to a valid inequality
3413 * sum_{j in M_1} x_j + sum_{j in F} alpha_j x_j + sum_{j in M_2} alpha_j x_j + sum_{j in R} alpha_j x_j
3414 * <= alpha_0 + sum_{j in M_2} alpha_j
3415 * for
3416 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0 };
3417 * uses sequential up-lifting for the variables in F, sequential down-lifting for the variable in M_2, and
3418 * sequential up-lifting for the variables in R; procedure can be used to strengthen minimal cover inequalities and
3419 * extended weight inequalities.
3420 */
3421 static
sequentialUpAndDownLifting(SCIP * scip,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * varsM1,int * varsM2,int * varsF,int * varsR,int nvarsM1,int nvarsM2,int nvarsF,int nvarsR,int alpha0,int * liftcoefs,SCIP_Real * cutact,int * liftrhs)3422 SCIP_RETCODE sequentialUpAndDownLifting(
3423 SCIP* scip, /**< SCIP data structure */
3424 SCIP_VAR** vars, /**< variables in knapsack constraint */
3425 int nvars, /**< number of variables in knapsack constraint */
3426 int ntightened, /**< number of variables with tightened upper bound */
3427 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
3428 SCIP_Longint capacity, /**< capacity of knapsack */
3429 SCIP_Real* solvals, /**< solution values of all problem variables */
3430 int* varsM1, /**< variables in M_1 */
3431 int* varsM2, /**< variables in M_2 */
3432 int* varsF, /**< variables in F */
3433 int* varsR, /**< variables in R */
3434 int nvarsM1, /**< number of variables in M_1 */
3435 int nvarsM2, /**< number of variables in M_2 */
3436 int nvarsF, /**< number of variables in F */
3437 int nvarsR, /**< number of variables in R */
3438 int alpha0, /**< rights hand side of given valid inequality */
3439 int* liftcoefs, /**< pointer to store lifting coefficient of vars in knapsack constraint */
3440 SCIP_Real* cutact, /**< pointer to store activity of lifted valid inequality */
3441 int* liftrhs /**< pointer to store right hand side of the lifted valid inequality */
3442 )
3443 {
3444 SCIP_Longint* minweights;
3445 SCIP_Real* sortkeys;
3446 SCIP_Longint fixedonesweight;
3447 int minweightssize;
3448 int minweightslen;
3449 int j;
3450 int w;
3451
3452 assert(scip != NULL);
3453 assert(vars != NULL);
3454 assert(nvars >= 0);
3455 assert(weights != NULL);
3456 assert(capacity >= 0);
3457 assert(solvals != NULL);
3458 assert(varsM1 != NULL);
3459 assert(varsM2 != NULL);
3460 assert(varsF != NULL);
3461 assert(varsR != NULL);
3462 assert(nvarsM1 >= 0 && nvarsM1 <= nvars - ntightened);
3463 assert(nvarsM2 >= 0 && nvarsM2 <= nvars - ntightened);
3464 assert(nvarsF >= 0 && nvarsF <= nvars - ntightened);
3465 assert(nvarsR >= 0 && nvarsR <= nvars - ntightened);
3466 assert(nvarsM1 + nvarsM2 + nvarsF + nvarsR == nvars - ntightened);
3467 assert(alpha0 >= 0);
3468 assert(liftcoefs != NULL);
3469 assert(cutact != NULL);
3470 assert(liftrhs != NULL);
3471
3472 /* allocates temporary memory */
3473 minweightssize = nvarsM1 + 1;
3474 SCIP_CALL( SCIPallocBufferArray(scip, &minweights, minweightssize) );
3475 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeys, nvarsM1) );
3476
3477 /* initializes data structures */
3478 BMSclearMemoryArray(liftcoefs, nvars);
3479 *cutact = 0.0;
3480
3481 /* sets lifting coefficient of variables in M1, sorts variables in M1 such that a_1 <= a_2 <= ... <= a_|M1|
3482 * and calculates activity of the current valid inequality
3483 */
3484 for( j = 0; j < nvarsM1; j++ )
3485 {
3486 assert(liftcoefs[varsM1[j]] == 0);
3487 liftcoefs[varsM1[j]] = 1;
3488 sortkeys[j] = (SCIP_Real) (weights[varsM1[j]]);
3489 (*cutact) += solvals[varsM1[j]];
3490 }
3491
3492 SCIPsortRealInt(sortkeys, varsM1, nvarsM1);
3493
3494 /* initializes (i = 1) the minweight table, defined as: minweights_i[w] =
3495 * min sum_{j in M_1} a_j x_j + sum_{k=1}^{i-1} a_{j_k} x_{j_k}
3496 * s.t. sum_{j in M_1} x_j + sum_{k=1}^{i-1} alpha_{j_k} x_{j_k} >= w
3497 * x_j in {0,1} for j in M_1 & {j_i,...,j_i-1},
3498 * for i = 1,...,t with t = |N\M1| and w = 0,...,|M1| + sum_{k=1}^{i-1} alpha_{j_k};
3499 */
3500 minweights[0] = 0;
3501 for( w = 1; w <= nvarsM1; w++ )
3502 minweights[w] = minweights[w-1] + weights[varsM1[w-1]];
3503 minweightslen = nvarsM1 + 1;
3504
3505 /* gets sum of weights of variables fixed to one, i.e. sum of weights of variables in M_2 */
3506 fixedonesweight = 0;
3507 for( j = 0; j < nvarsM2; j++ )
3508 fixedonesweight += weights[varsM2[j]];
3509 assert(fixedonesweight >= 0);
3510
3511 /* initializes right hand side of lifted valid inequality */
3512 *liftrhs = alpha0;
3513
3514 /* sequentially up-lifts all variables in F: */
3515 for( j = 0; j < nvarsF; j++ )
3516 {
3517 SCIP_Longint weight;
3518 int liftvar;
3519 int liftcoef;
3520 int z;
3521
3522 liftvar = varsF[j];
3523 weight = weights[liftvar];
3524 assert(liftvar >= 0 && liftvar < nvars);
3525 assert(SCIPisFeasGT(scip, solvals[liftvar], 0.0));
3526 assert(weight > 0);
3527
3528 /* knapsack problem is infeasible:
3529 * sets z = 0
3530 */
3531 if( capacity - fixedonesweight - weight < 0 )
3532 {
3533 z = 0;
3534 }
3535 /* knapsack problem is feasible:
3536 * sets z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - fixedonesweight - a_{j_i} } = liftrhs,
3537 * if minweights_i[liftrhs] <= a_0 - fixedonesweight - a_{j_i}
3538 */
3539 else if( minweights[*liftrhs] <= capacity - fixedonesweight - weight )
3540 {
3541 z = *liftrhs;
3542 }
3543 /* knapsack problem is feasible:
3544 * uses binary search to find z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - fixedonesweight - a_{j_i} }
3545 */
3546 else
3547 {
3548 int left;
3549 int right;
3550 int middle;
3551
3552 assert((*liftrhs) + 1 >= minweightslen || minweights[(*liftrhs) + 1] > capacity - fixedonesweight - weight);
3553 left = 0;
3554 right = (*liftrhs) + 1;
3555 while( left < right - 1 )
3556 {
3557 middle = (left + right) / 2;
3558 assert(0 <= middle && middle < minweightslen);
3559 if( minweights[middle] <= capacity - fixedonesweight - weight )
3560 left = middle;
3561 else
3562 right = middle;
3563 }
3564 assert(left == right - 1);
3565 assert(0 <= left && left < minweightslen);
3566 assert(minweights[left] <= capacity - fixedonesweight - weight );
3567 assert(left == minweightslen - 1 || minweights[left+1] > capacity - fixedonesweight - weight);
3568
3569 /* now z = left */
3570 z = left;
3571 assert(z <= *liftrhs);
3572 }
3573
3574 /* calculates lifting coefficients alpha_{j_i} = liftrhs - z */
3575 liftcoef = (*liftrhs) - z;
3576 liftcoefs[liftvar] = liftcoef;
3577 assert(liftcoef >= 0 && liftcoef <= (*liftrhs) + 1);
3578
3579 /* minweight table and activity of current valid inequality will not change, if alpha_{j_i} = 0 */
3580 if( liftcoef == 0 )
3581 continue;
3582
3583 /* updates activity of current valid inequality */
3584 (*cutact) += liftcoef * solvals[liftvar];
3585
3586 /* enlarges current minweight table:
3587 * from minweightlen = |M1| + sum_{k=1}^{i-1} alpha_{j_k} + 1 entries
3588 * to |M1| + sum_{k=1}^{i } alpha_{j_k} + 1 entries
3589 * and sets minweights_i[w] = infinity for
3590 * w = |M1| + sum_{k=1}^{i-1} alpha_{j_k} + 1 , ... , |M1| + sum_{k=1}^{i} alpha_{j_k}
3591 */
3592 SCIP_CALL( enlargeMinweights(scip, &minweights, &minweightslen, &minweightssize, minweightslen + liftcoef) );
3593
3594 /* updates minweight table: minweight_i+1[w] =
3595 * min{ minweights_i[w], a_{j_i}}, if w < alpha_j_i
3596 * min{ minweights_i[w], minweights_i[w - alpha_j_i] + a_j_i}, if w >= alpha_j_i
3597 */
3598 for( w = minweightslen - 1; w >= 0; w-- )
3599 {
3600 SCIP_Longint min;
3601 if( w < liftcoef )
3602 {
3603 min = MIN(minweights[w], weight);
3604 minweights[w] = min;
3605 }
3606 else
3607 {
3608 assert(w >= liftcoef);
3609 min = MIN(minweights[w], minweights[w - liftcoef] + weight);
3610 minweights[w] = min;
3611 }
3612 }
3613 }
3614 assert(minweights[0] == 0);
3615
3616 /* sequentially down-lifts all variables in M_2: */
3617 for( j = 0; j < nvarsM2; j++ )
3618 {
3619 SCIP_Longint weight;
3620 int liftvar;
3621 int liftcoef;
3622 int left;
3623 int right;
3624 int middle;
3625 int z;
3626
3627 liftvar = varsM2[j];
3628 weight = weights[liftvar];
3629 assert(SCIPisFeasEQ(scip, solvals[liftvar], 1.0));
3630 assert(liftvar >= 0 && liftvar < nvars);
3631 assert(weight > 0);
3632
3633 /* uses binary search to find
3634 * z = max { w : 0 <= w <= |M_1| + sum_{k=1}^{i-1} alpha_{j_k}, minweights_[w] <= a_0 - fixedonesweight + a_{j_i}}
3635 */
3636 left = 0;
3637 right = minweightslen;
3638 while( left < right - 1 )
3639 {
3640 middle = (left + right) / 2;
3641 assert(0 <= middle && middle < minweightslen);
3642 if( minweights[middle] <= capacity - fixedonesweight + weight )
3643 left = middle;
3644 else
3645 right = middle;
3646 }
3647 assert(left == right - 1);
3648 assert(0 <= left && left < minweightslen);
3649 assert(minweights[left] <= capacity - fixedonesweight + weight );
3650 assert(left == minweightslen - 1 || minweights[left+1] > capacity - fixedonesweight + weight);
3651
3652 /* now z = left */
3653 z = left;
3654 assert(z >= *liftrhs);
3655
3656 /* calculates lifting coefficients alpha_{j_i} = z - liftrhs */
3657 liftcoef = z - (*liftrhs);
3658 liftcoefs[liftvar] = liftcoef;
3659 assert(liftcoef >= 0);
3660
3661 /* updates sum of weights of variables fixed to one */
3662 fixedonesweight -= weight;
3663
3664 /* updates right-hand side of current valid inequality */
3665 (*liftrhs) += liftcoef;
3666 assert(*liftrhs >= alpha0);
3667
3668 /* minweight table and activity of current valid inequality will not change, if alpha_{j_i} = 0 */
3669 if( liftcoef == 0 )
3670 continue;
3671
3672 /* updates activity of current valid inequality */
3673 (*cutact) += liftcoef * solvals[liftvar];
3674
3675 /* enlarges current minweight table:
3676 * from minweightlen = |M1| + sum_{k=1}^{i-1} alpha_{j_k} + 1 entries
3677 * to |M1| + sum_{k=1}^{i } alpha_{j_k} + 1 entries
3678 * and sets minweights_i[w] = infinity for
3679 * w = |M1| + sum_{k=1}^{i-1} alpha_{j_k} + 1 , ... , |M1| + sum_{k=1}^{i} alpha_{j_k}
3680 */
3681 SCIP_CALL( enlargeMinweights(scip, &minweights, &minweightslen, &minweightssize, minweightslen + liftcoef) );
3682
3683 /* updates minweight table: minweight_i+1[w] =
3684 * min{ minweights_i[w], a_{j_i}}, if w < alpha_j_i
3685 * min{ minweights_i[w], minweights_i[w - alpha_j_i] + a_j_i}, if w >= alpha_j_i
3686 */
3687 for( w = minweightslen - 1; w >= 0; w-- )
3688 {
3689 SCIP_Longint min;
3690 if( w < liftcoef )
3691 {
3692 min = MIN(minweights[w], weight);
3693 minweights[w] = min;
3694 }
3695 else
3696 {
3697 assert(w >= liftcoef);
3698 min = MIN(minweights[w], minweights[w - liftcoef] + weight);
3699 minweights[w] = min;
3700 }
3701 }
3702 }
3703 assert(fixedonesweight == 0);
3704 assert(*liftrhs >= alpha0);
3705
3706 /* sequentially up-lifts all variables in R: */
3707 for( j = 0; j < nvarsR; j++ )
3708 {
3709 SCIP_Longint weight;
3710 int liftvar;
3711 int liftcoef;
3712 int z;
3713
3714 liftvar = varsR[j];
3715 weight = weights[liftvar];
3716 assert(liftvar >= 0 && liftvar < nvars);
3717 assert(SCIPisFeasEQ(scip, solvals[liftvar], 0.0));
3718 assert(weight > 0);
3719 assert(capacity - weight >= 0);
3720 assert((*liftrhs) + 1 >= minweightslen || minweights[(*liftrhs) + 1] > capacity - weight);
3721
3722 /* sets z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - a_{j_i} } = liftrhs,
3723 * if minweights_i[liftrhs] <= a_0 - a_{j_i}
3724 */
3725 if( minweights[*liftrhs] <= capacity - weight )
3726 {
3727 z = *liftrhs;
3728 }
3729 /* uses binary search to find z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - a_{j_i} }
3730 */
3731 else
3732 {
3733 int left;
3734 int right;
3735 int middle;
3736
3737 left = 0;
3738 right = (*liftrhs) + 1;
3739 while( left < right - 1)
3740 {
3741 middle = (left + right) / 2;
3742 assert(0 <= middle && middle < minweightslen);
3743 if( minweights[middle] <= capacity - weight )
3744 left = middle;
3745 else
3746 right = middle;
3747 }
3748 assert(left == right - 1);
3749 assert(0 <= left && left < minweightslen);
3750 assert(minweights[left] <= capacity - weight );
3751 assert(left == minweightslen - 1 || minweights[left+1] > capacity - weight);
3752
3753 /* now z = left */
3754 z = left;
3755 assert(z <= *liftrhs);
3756 }
3757
3758 /* calculates lifting coefficients alpha_{j_i} = liftrhs - z */
3759 liftcoef = (*liftrhs) - z;
3760 liftcoefs[liftvar] = liftcoef;
3761 assert(liftcoef >= 0 && liftcoef <= *liftrhs);
3762
3763 /* minweight table and activity of current valid inequality will not change, if alpha_{j_i} = 0 */
3764 if( liftcoef == 0 )
3765 continue;
3766
3767 /* updates activity of current valid inequality */
3768 (*cutact) += liftcoef * solvals[liftvar];
3769
3770 /* updates minweight table: minweight_i+1[w] =
3771 * min{ minweight_i[w], a_{j_i}}, if w < alpha_j_i
3772 * min{ minweight_i[w], minweight_i[w - alpha_j_i] + a_j_i}, if w >= alpha_j_i
3773 */
3774 for( w = *liftrhs; w >= 0; w-- )
3775 {
3776 SCIP_Longint min;
3777 if( w < liftcoef )
3778 {
3779 min = MIN(minweights[w], weight);
3780 minweights[w] = min;
3781 }
3782 else
3783 {
3784 assert(w >= liftcoef);
3785 min = MIN(minweights[w], minweights[w - liftcoef] + weight);
3786 minweights[w] = min;
3787 }
3788 }
3789 }
3790
3791 /* frees temporary memory */
3792 SCIPfreeBufferArray(scip, &sortkeys);
3793 SCIPfreeBufferArray(scip, &minweights);
3794
3795 return SCIP_OKAY;
3796 }
3797
3798 /** adds two minweight values in a safe way, i.e,, ensures no overflow */
3799 static
safeAddMinweightsGUB(SCIP_Longint val1,SCIP_Longint val2)3800 SCIP_Longint safeAddMinweightsGUB(
3801 SCIP_Longint val1, /**< first value to add */
3802 SCIP_Longint val2 /**< second value to add */
3803 )
3804 {
3805 assert(val1 >= 0);
3806 assert(val2 >= 0);
3807
3808 if( val1 >= SCIP_LONGINT_MAX || val2 >= SCIP_LONGINT_MAX )
3809 return SCIP_LONGINT_MAX;
3810 else
3811 {
3812 assert(val1 <= SCIP_LONGINT_MAX - val2);
3813 return (val1 + val2);
3814 }
3815 }
3816
3817 /** computes minweights table for lifting with GUBs by combining unfished and fished tables */
3818 static
computeMinweightsGUB(SCIP_Longint * minweights,SCIP_Longint * finished,SCIP_Longint * unfinished,int minweightslen)3819 void computeMinweightsGUB(
3820 SCIP_Longint* minweights, /**< minweight table to compute */
3821 SCIP_Longint* finished, /**< given finished table */
3822 SCIP_Longint* unfinished, /**< given unfinished table */
3823 int minweightslen /**< length of minweight, finished, and unfinished tables */
3824 )
3825 {
3826 int w1;
3827 int w2;
3828
3829 /* minweights_i[w] = min{finished_i[w1] + unfinished_i[w2] : w1>=0, w2>=0, w1+w2=w};
3830 * note that finished and unfished arrays sorted by non-decreasing weight
3831 */
3832
3833 /* initialize minweight with w2 = 0 */
3834 w2 = 0;
3835 assert(unfinished[w2] == 0);
3836 for( w1 = 0; w1 < minweightslen; w1++ )
3837 minweights[w1] = finished[w1];
3838
3839 /* consider w2 = 1, ..., minweightslen-1 */
3840 for( w2 = 1; w2 < minweightslen; w2++ )
3841 {
3842 if( unfinished[w2] >= SCIP_LONGINT_MAX )
3843 break;
3844
3845 for( w1 = 0; w1 < minweightslen - w2; w1++ )
3846 {
3847 SCIP_Longint temp;
3848
3849 temp = safeAddMinweightsGUB(finished[w1], unfinished[w2]);
3850 if( temp <= minweights[w1+w2] )
3851 minweights[w1+w2] = temp;
3852 }
3853 }
3854 }
3855
3856 /** lifts given inequality
3857 * sum_{j in C_1} x_j <= alpha_0
3858 * valid for
3859 * S^0 = { x in {0,1}^|C_1| : sum_{j in C_1} a_j x_j <= a_0 - sum_{j in C_2} a_j;
3860 * sum_{j in Q_i} x_j <= 1, forall i in I }
3861 * to a valid inequality
3862 * sum_{j in C_1} x_j + sum_{j in F} alpha_j x_j + sum_{j in C_2} alpha_j x_j + sum_{j in R} alpha_j x_j
3863 * <= alpha_0 + sum_{j in C_2} alpha_j
3864 * for
3865 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0; sum_{j in Q_i} x_j <= 1, forall i in I };
3866 * uses sequential up-lifting for the variables in GUB constraints in gubconsGFC1,
3867 * sequential down-lifting for the variables in GUB constraints in gubconsGC2, and
3868 * sequential up-lifting for the variabels in GUB constraints in gubconsGR.
3869 */
3870 static
sequentialUpAndDownLiftingGUB(SCIP * scip,SCIP_GUBSET * gubset,SCIP_VAR ** vars,int ngubconscapexceed,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * gubconsGC1,int * gubconsGC2,int * gubconsGFC1,int * gubconsGR,int ngubconsGC1,int ngubconsGC2,int ngubconsGFC1,int ngubconsGR,int alpha0,int * liftcoefs,SCIP_Real * cutact,int * liftrhs,int maxgubvarssize)3871 SCIP_RETCODE sequentialUpAndDownLiftingGUB(
3872 SCIP* scip, /**< SCIP data structure */
3873 SCIP_GUBSET* gubset, /**< GUB set data structure */
3874 SCIP_VAR** vars, /**< variables in knapsack constraint */
3875 int ngubconscapexceed, /**< number of GUBs with only capacity exceeding variables */
3876 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
3877 SCIP_Longint capacity, /**< capacity of knapsack */
3878 SCIP_Real* solvals, /**< solution values of all knapsack variables */
3879 int* gubconsGC1, /**< GUBs in GC1(GNC1+GOC1) */
3880 int* gubconsGC2, /**< GUBs in GC2 */
3881 int* gubconsGFC1, /**< GUBs in GFC1(GNC1+GF) */
3882 int* gubconsGR, /**< GUBs in GR */
3883 int ngubconsGC1, /**< number of GUBs in GC1(GNC1+GOC1) */
3884 int ngubconsGC2, /**< number of GUBs in GC2 */
3885 int ngubconsGFC1, /**< number of GUBs in GFC1(GNC1+GF) */
3886 int ngubconsGR, /**< number of GUBs in GR */
3887 int alpha0, /**< rights hand side of given valid inequality */
3888 int* liftcoefs, /**< pointer to store lifting coefficient of vars in knapsack constraint */
3889 SCIP_Real* cutact, /**< pointer to store activity of lifted valid inequality */
3890 int* liftrhs, /**< pointer to store right hand side of the lifted valid inequality */
3891 int maxgubvarssize /**< maximal size of GUB constraints */
3892 )
3893 {
3894 SCIP_Longint* minweights;
3895 SCIP_Longint* finished;
3896 SCIP_Longint* unfinished;
3897 int* gubconsGOC1;
3898 int* gubconsGNC1;
3899 int* liftgubvars;
3900 SCIP_Longint fixedonesweight;
3901 SCIP_Longint weight;
3902 SCIP_Longint weightdiff1;
3903 SCIP_Longint weightdiff2;
3904 SCIP_Longint min;
3905 int minweightssize;
3906 int minweightslen;
3907 int nvars;
3908 int varidx;
3909 int liftgubconsidx;
3910 int liftvar;
3911 int sumliftcoef;
3912 int liftcoef;
3913 int ngubconsGOC1;
3914 int ngubconsGNC1;
3915 int left;
3916 int right;
3917 int middle;
3918 int nliftgubvars;
3919 int tmplen;
3920 int tmpsize;
3921 int j;
3922 int k;
3923 int w;
3924 int z;
3925 #ifndef NDEBUG
3926 int ngubconss;
3927 int nliftgubC1;
3928
3929 assert(gubset != NULL);
3930 ngubconss = gubset->ngubconss;
3931 #else
3932 assert(gubset != NULL);
3933 #endif
3934
3935 nvars = gubset->nvars;
3936
3937 assert(scip != NULL);
3938 assert(vars != NULL);
3939 assert(nvars >= 0);
3940 assert(weights != NULL);
3941 assert(capacity >= 0);
3942 assert(solvals != NULL);
3943 assert(gubconsGC1 != NULL);
3944 assert(gubconsGC2 != NULL);
3945 assert(gubconsGFC1 != NULL);
3946 assert(gubconsGR != NULL);
3947 assert(ngubconsGC1 >= 0 && ngubconsGC1 <= ngubconss - ngubconscapexceed);
3948 assert(ngubconsGC2 >= 0 && ngubconsGC2 <= ngubconss - ngubconscapexceed);
3949 assert(ngubconsGFC1 >= 0 && ngubconsGFC1 <= ngubconss - ngubconscapexceed);
3950 assert(ngubconsGR >= 0 && ngubconsGR <= ngubconss - ngubconscapexceed);
3951 assert(alpha0 >= 0);
3952 assert(liftcoefs != NULL);
3953 assert(cutact != NULL);
3954 assert(liftrhs != NULL);
3955
3956 minweightssize = ngubconsGC1+1;
3957
3958 /* allocates temporary memory */
3959 SCIP_CALL( SCIPallocBufferArray(scip, &liftgubvars, maxgubvarssize) );
3960 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGOC1, ngubconsGC1) );
3961 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGNC1, ngubconsGC1) );
3962 SCIP_CALL( SCIPallocBufferArray(scip, &minweights, minweightssize) );
3963 SCIP_CALL( SCIPallocBufferArray(scip, &finished, minweightssize) );
3964 SCIP_CALL( SCIPallocBufferArray(scip, &unfinished, minweightssize) );
3965
3966 /* initializes data structures */
3967 BMSclearMemoryArray(liftcoefs, nvars);
3968 *cutact = 0.0;
3969
3970 /* gets GOC1 and GNC1 GUBs, sets lifting coefficient of variables in C1 and calculates activity of the current
3971 * valid inequality
3972 */
3973 ngubconsGOC1 = 0;
3974 ngubconsGNC1 = 0;
3975 for( j = 0; j < ngubconsGC1; j++ )
3976 {
3977 if( gubset->gubconsstatus[gubconsGC1[j]] == GUBCONSSTATUS_BELONGSTOSET_GOC1 )
3978 {
3979 gubconsGOC1[ngubconsGOC1] = gubconsGC1[j];
3980 ngubconsGOC1++;
3981 }
3982 else
3983 {
3984 assert(gubset->gubconsstatus[gubconsGC1[j]] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
3985 gubconsGNC1[ngubconsGNC1] = gubconsGC1[j];
3986 ngubconsGNC1++;
3987 }
3988 for( k = 0; k < gubset->gubconss[gubconsGC1[j]]->ngubvars
3989 && gubset->gubconss[gubconsGC1[j]]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_C1; k++ )
3990 {
3991 varidx = gubset->gubconss[gubconsGC1[j]]->gubvars[k];
3992 assert(varidx >= 0 && varidx < nvars);
3993 assert(liftcoefs[varidx] == 0);
3994
3995 liftcoefs[varidx] = 1;
3996 (*cutact) += solvals[varidx];
3997 }
3998 assert(k >= 1);
3999 }
4000 assert(ngubconsGOC1 + ngubconsGFC1 + ngubconsGC2 + ngubconsGR == ngubconss - ngubconscapexceed);
4001 assert(ngubconsGOC1 + ngubconsGNC1 == ngubconsGC1);
4002
4003 /* initialize the minweight tables, defined as: for i = 1,...,m with m = |I| and w = 0,...,|gubconsGC1|;
4004 * - finished_i[w] =
4005 * min sum_{k = 1,2,...,i-1} sum_{j in Q_k} a_j x_j
4006 * s.t. sum_{k = 1,2,...,i-1} sum_{j in Q_k} alpha_j x_j >= w
4007 * sum_{j in Q_k} x_j <= 1
4008 * x_j in {0,1} forall j in Q_k forall k = 1,2,...,i-1,
4009 * - unfinished_i[w] =
4010 * min sum_{k = i+1,...,m} sum_{j in Q_k && j in C1} a_j x_j
4011 * s.t. sum_{k = i+1,...,m} sum_{j in Q_k && j in C1} x_j >= w
4012 * sum_{j in Q_k} x_j <= 1
4013 * x_j in {0,1} forall j in Q_k forall k = 1,2,...,i-1,
4014 * - minweights_i[w] = min{finished_i[w1] + unfinished_i[w2] : w1>=0, w2>=0, w1+w2=w};
4015 */
4016
4017 /* initialize finished table; note that variables in GOC1 GUBs (includes C1 and capacity exceeding variables)
4018 * are sorted s.t. C1 variables come first and are sorted by non-decreasing weight.
4019 * GUBs in the group GCI are sorted by non-decreasing min{ a_k : k in GC1_j } where min{ a_k : k in GC1_j } always
4020 * comes from the first variable in the GUB
4021 */
4022 assert(ngubconsGOC1 <= ngubconsGC1);
4023 finished[0] = 0;
4024 for( w = 1; w <= ngubconsGOC1; w++ )
4025 {
4026 liftgubconsidx = gubconsGOC1[w-1];
4027
4028 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GOC1);
4029 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C1);
4030
4031 varidx = gubset->gubconss[liftgubconsidx]->gubvars[0];
4032
4033 assert(varidx >= 0 && varidx < nvars);
4034 assert(liftcoefs[varidx] == 1);
4035
4036 min = weights[varidx];
4037 finished[w] = finished[w-1] + min;
4038
4039 #ifndef NDEBUG
4040 for( k = 1; k < gubset->gubconss[liftgubconsidx]->ngubvars
4041 && gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_C1; k++ )
4042 {
4043 varidx = gubset->gubconss[liftgubconsidx]->gubvars[k];
4044 assert(varidx >= 0 && varidx < nvars);
4045 assert(liftcoefs[varidx] == 1);
4046 assert(weights[varidx] >= min);
4047 }
4048 #endif
4049 }
4050 for( w = ngubconsGOC1+1; w <= ngubconsGC1; w++ )
4051 finished[w] = SCIP_LONGINT_MAX;
4052
4053 /* initialize unfinished table; note that variables in GNC1 GUBs
4054 * are sorted s.t. C1 variables come first and are sorted by non-decreasing weight.
4055 * GUBs in the group GCI are sorted by non-decreasing min{ a_k : k in GC1_j } where min{ a_k : k in GC1_j } always
4056 * comes from the first variable in the GUB
4057 */
4058 assert(ngubconsGNC1 <= ngubconsGC1);
4059 unfinished[0] = 0;
4060 for( w = 1; w <= ngubconsGNC1; w++ )
4061 {
4062 liftgubconsidx = gubconsGNC1[w-1];
4063
4064 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
4065 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C1);
4066
4067 varidx = gubset->gubconss[liftgubconsidx]->gubvars[0];
4068
4069 assert(varidx >= 0 && varidx < nvars);
4070 assert(liftcoefs[varidx] == 1);
4071
4072 min = weights[varidx];
4073 unfinished[w] = unfinished[w-1] + min;
4074
4075 #ifndef NDEBUG
4076 for( k = 1; k < gubset->gubconss[liftgubconsidx]->ngubvars
4077 && gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_C1; k++ )
4078 {
4079 varidx = gubset->gubconss[liftgubconsidx]->gubvars[k];
4080 assert(varidx >= 0 && varidx < nvars);
4081 assert(liftcoefs[varidx] == 1);
4082 assert(weights[varidx] >= min );
4083 }
4084 #endif
4085 }
4086 for( w = ngubconsGNC1 + 1; w <= ngubconsGC1; w++ )
4087 unfinished[w] = SCIP_LONGINT_MAX;
4088
4089 /* initialize minweights table; note that variables in GC1 GUBs
4090 * are sorted s.t. C1 variables come first and are sorted by non-decreasing weight.
4091 * we can directly initialize minweights instead of computing it from finished and unfinished (which would be more time
4092 * consuming) because is it has to be build using weights from C1 only.
4093 */
4094 assert(ngubconsGOC1 + ngubconsGNC1 == ngubconsGC1);
4095 minweights[0] = 0;
4096 for( w = 1; w <= ngubconsGC1; w++ )
4097 {
4098 liftgubconsidx = gubconsGC1[w-1];
4099
4100 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GOC1
4101 || gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
4102 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C1);
4103
4104 varidx = gubset->gubconss[liftgubconsidx]->gubvars[0];
4105
4106 assert(varidx >= 0 && varidx < nvars);
4107 assert(liftcoefs[varidx] == 1);
4108
4109 min = weights[varidx];
4110 minweights[w] = minweights[w-1] + min;
4111
4112 #ifndef NDEBUG
4113 for( k = 1; k < gubset->gubconss[liftgubconsidx]->ngubvars
4114 && gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_C1; k++ )
4115 {
4116 varidx = gubset->gubconss[liftgubconsidx]->gubvars[k];
4117 assert(varidx >= 0 && varidx < nvars);
4118 assert(liftcoefs[varidx] == 1);
4119 assert(weights[varidx] >= min);
4120 }
4121 #endif
4122 }
4123 minweightslen = ngubconsGC1 + 1;
4124
4125 /* gets sum of weights of variables fixed to one, i.e. sum of weights of C2 variables GC2 GUBs */
4126 fixedonesweight = 0;
4127 for( j = 0; j < ngubconsGC2; j++ )
4128 {
4129 varidx = gubset->gubconss[gubconsGC2[j]]->gubvars[0];
4130
4131 assert(gubset->gubconss[gubconsGC2[j]]->ngubvars == 1);
4132 assert(varidx >= 0 && varidx < nvars);
4133 assert(gubset->gubconss[gubconsGC2[j]]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C2);
4134
4135 fixedonesweight += weights[varidx];
4136 }
4137 assert(fixedonesweight >= 0);
4138
4139 /* initializes right hand side of lifted valid inequality */
4140 *liftrhs = alpha0;
4141
4142 /* sequentially up-lifts all variables in GFC1 GUBs */
4143 for( j = 0; j < ngubconsGFC1; j++ )
4144 {
4145 liftgubconsidx = gubconsGFC1[j];
4146 assert(liftgubconsidx >= 0 && liftgubconsidx < ngubconss);
4147
4148 /* GNC1 GUB: update unfinished table (remove current GUB, i.e., remove min weight of C1 vars in GUB) and
4149 * compute minweight table via updated unfinished table and aleady upto date finished table;
4150 */
4151 k = 0;
4152 if( gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1 )
4153 {
4154 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1);
4155 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C1);
4156 assert(ngubconsGNC1 > 0);
4157
4158 /* get number of C1 variables of current GNC1 GUB and put them into array of variables in GUB that
4159 * are considered for the lifting, i.e., not capacity exceeding
4160 */
4161 for( ; k < gubset->gubconss[liftgubconsidx]->ngubvars
4162 && gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_C1; k++ )
4163 liftgubvars[k] = gubset->gubconss[liftgubconsidx]->gubvars[k];
4164 assert(k >= 1);
4165
4166 /* update unfinished table by removing current GNC1 GUB, i.e, remove C1 variable with minimal weight
4167 * unfinished[w] = MAX{unfinished[w], unfinished[w+1] - weight}, "weight" is the minimal weight of current GUB
4168 */
4169 weight = weights[liftgubvars[0]];
4170
4171 weightdiff2 = unfinished[ngubconsGNC1] - weight;
4172 unfinished[ngubconsGNC1] = SCIP_LONGINT_MAX;
4173 for( w = ngubconsGNC1-1; w >= 1; w-- )
4174 {
4175 weightdiff1 = weightdiff2;
4176 weightdiff2 = unfinished[w] - weight;
4177
4178 if( unfinished[w] < weightdiff1 )
4179 unfinished[w] = weightdiff1;
4180 else
4181 break;
4182 }
4183 ngubconsGNC1--;
4184
4185 /* computes minweights table by combining unfished and fished tables */
4186 computeMinweightsGUB(minweights, finished, unfinished, minweightslen);
4187 assert(minweights[0] == 0);
4188 }
4189 /* GF GUB: no update of unfinished table (and minweight table) required because GF GUBs have no C1 variables and
4190 * are therefore not in the unfinished table
4191 */
4192 else
4193 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GF);
4194
4195 #ifndef NDEBUG
4196 nliftgubC1 = k;
4197 #endif
4198 nliftgubvars = k;
4199 sumliftcoef = 0;
4200
4201 /* compute lifting coefficient of F and R variables in GNC1 and GF GUBs (C1 vars have already liftcoef 1) */
4202 for( ; k < gubset->gubconss[liftgubconsidx]->ngubvars; k++ )
4203 {
4204 if( gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_F
4205 || gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_R )
4206 {
4207 liftvar = gubset->gubconss[liftgubconsidx]->gubvars[k];
4208 weight = weights[liftvar];
4209 assert(weight > 0);
4210 assert(liftvar >= 0 && liftvar < nvars);
4211 assert(capacity - weight >= 0);
4212
4213 /* put variable into array of variables in GUB that are considered for the lifting,
4214 * i.e., not capacity exceeding
4215 */
4216 liftgubvars[nliftgubvars] = liftvar;
4217 nliftgubvars++;
4218
4219 /* knapsack problem is infeasible:
4220 * sets z = 0
4221 */
4222 if( capacity - fixedonesweight - weight < 0 )
4223 {
4224 z = 0;
4225 }
4226 /* knapsack problem is feasible:
4227 * sets z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - fixedonesweight - a_{j_i} } = liftrhs,
4228 * if minweights_i[liftrhs] <= a_0 - fixedonesweight - a_{j_i}
4229 */
4230 else if( minweights[*liftrhs] <= capacity - fixedonesweight - weight )
4231 {
4232 z = *liftrhs;
4233 }
4234 /* knapsack problem is feasible:
4235 * binary search to find z = max {w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - fixedonesweight - a_{j_i}}
4236 */
4237 else
4238 {
4239 assert((*liftrhs) + 1 >= minweightslen || minweights[(*liftrhs) + 1] > capacity - fixedonesweight - weight);
4240 left = 0;
4241 right = (*liftrhs) + 1;
4242 while( left < right - 1 )
4243 {
4244 middle = (left + right) / 2;
4245 assert(0 <= middle && middle < minweightslen);
4246 if( minweights[middle] <= capacity - fixedonesweight - weight )
4247 left = middle;
4248 else
4249 right = middle;
4250 }
4251 assert(left == right - 1);
4252 assert(0 <= left && left < minweightslen);
4253 assert(minweights[left] <= capacity - fixedonesweight - weight);
4254 assert(left == minweightslen - 1 || minweights[left+1] > capacity - fixedonesweight - weight);
4255
4256 /* now z = left */
4257 z = left;
4258 assert(z <= *liftrhs);
4259 }
4260
4261 /* calculates lifting coefficients alpha_{j_i} = liftrhs - z */
4262 liftcoef = (*liftrhs) - z;
4263 liftcoefs[liftvar] = liftcoef;
4264 assert(liftcoef >= 0 && liftcoef <= (*liftrhs) + 1);
4265
4266 /* updates activity of current valid inequality */
4267 (*cutact) += liftcoef * solvals[liftvar];
4268
4269 /* updates sum of all lifting coefficients in GUB */
4270 sumliftcoef += liftcoefs[liftvar];
4271 }
4272 else
4273 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_CAPACITYEXCEEDED);
4274 }
4275 /* at least one variable is in F or R (j = number of C1 variables in current GUB) */
4276 assert(nliftgubvars > nliftgubC1);
4277
4278 /* activity of current valid inequality will not change if (sum of alpha_{j_i} in GUB) = 0
4279 * and finished and minweight table can be updated easily as only C1 variables need to be considered;
4280 * not needed for GF GUBs
4281 */
4282 if( sumliftcoef == 0 )
4283 {
4284 if( gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GNC1 )
4285 {
4286 weight = weights[liftgubvars[0]];
4287 /* update finished table and minweights table by applying special case of
4288 * finished[w] = MIN{finished[w], finished[w-1] + weight}, "weight" is the minimal weight of current GUB
4289 * minweights[w] = MIN{minweights[w], minweights[w-1] + weight}, "weight" is the minimal weight of current GUB
4290 */
4291 for( w = minweightslen-1; w >= 1; w-- )
4292 {
4293 SCIP_Longint tmpval;
4294
4295 tmpval = safeAddMinweightsGUB(finished[w-1], weight);
4296 finished[w] = MIN(finished[w], tmpval);
4297
4298 tmpval = safeAddMinweightsGUB(minweights[w-1], weight);
4299 minweights[w] = MIN(minweights[w], tmpval);
4300 }
4301 }
4302 else
4303 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GF);
4304
4305 continue;
4306 }
4307
4308 /* enlarges current minweights tables(finished, unfinished, minweights):
4309 * from minweightlen = |gubconsGC1| + sum_{k=1,2,...,i-1}sum_{j in Q_k} alpha_j + 1 entries
4310 * to |gubconsGC1| + sum_{k=1,2,...,i }sum_{j in Q_k} alpha_j + 1 entries
4311 * and sets minweights_i[w] = infinity for
4312 * w = |gubconsGC1| + sum_{k=1,2,..,i-1}sum_{j in Q_k} alpha_j+1,..,|C1| + sum_{k=1,2,..,i}sum_{j in Q_k} alpha_j
4313 */
4314 tmplen = minweightslen; /* will be updated in enlargeMinweights() */
4315 tmpsize = minweightssize;
4316 SCIP_CALL( enlargeMinweights(scip, &unfinished, &tmplen, &tmpsize, tmplen + sumliftcoef) );
4317 tmplen = minweightslen;
4318 tmpsize = minweightssize;
4319 SCIP_CALL( enlargeMinweights(scip, &finished, &tmplen, &tmpsize, tmplen + sumliftcoef) );
4320 SCIP_CALL( enlargeMinweights(scip, &minweights, &minweightslen, &minweightssize, minweightslen + sumliftcoef) );
4321
4322 /* update finished table and minweight table;
4323 * note that instead of computing minweight table from updated finished and updated unfinished table again
4324 * (for the lifting coefficient, we had to update unfinished table and compute minweight table), we here
4325 * only need to update the minweight table and the updated finished in the same way (i.e., computing for minweight
4326 * not needed because only finished table changed at this point and the change was "adding" one weight)
4327 *
4328 * update formular for minweight table is: minweight_i+1[w] =
4329 * min{ minweights_i[w], min{ minweights_i[w - alpha_k]^{+} + a_k : k in GUB_j_i } }
4330 * formular for finished table has the same pattern.
4331 */
4332 for( w = minweightslen-1; w >= 0; w-- )
4333 {
4334 SCIP_Longint minminweight;
4335 SCIP_Longint minfinished;
4336
4337 for( k = 0; k < nliftgubvars; k++ )
4338 {
4339 liftcoef = liftcoefs[liftgubvars[k]];
4340 weight = weights[liftgubvars[k]];
4341
4342 if( w < liftcoef )
4343 {
4344 minfinished = MIN(finished[w], weight);
4345 minminweight = MIN(minweights[w], weight);
4346
4347 finished[w] = minfinished;
4348 minweights[w] = minminweight;
4349 }
4350 else
4351 {
4352 SCIP_Longint tmpval;
4353
4354 assert(w >= liftcoef);
4355
4356 tmpval = safeAddMinweightsGUB(finished[w-liftcoef], weight);
4357 minfinished = MIN(finished[w], tmpval);
4358
4359 tmpval = safeAddMinweightsGUB(minweights[w-liftcoef], weight);
4360 minminweight = MIN(minweights[w], tmpval);
4361
4362 finished[w] = minfinished;
4363 minweights[w] = minminweight;
4364 }
4365 }
4366 }
4367 assert(minweights[0] == 0);
4368 }
4369 assert(ngubconsGNC1 == 0);
4370
4371 /* note: now the unfinished table no longer exists, i.e., it is "0, MAX, MAX, ..." and minweight equals to finished;
4372 * therefore, only work with minweight table from here on
4373 */
4374
4375 /* sequentially down-lifts C2 variables contained in trivial GC2 GUBs */
4376 for( j = 0; j < ngubconsGC2; j++ )
4377 {
4378 liftgubconsidx = gubconsGC2[j];
4379
4380 assert(liftgubconsidx >=0 && liftgubconsidx < ngubconss);
4381 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GC2);
4382 assert(gubset->gubconss[liftgubconsidx]->ngubvars == 1);
4383 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[0] == GUBVARSTATUS_BELONGSTOSET_C2);
4384
4385 liftvar = gubset->gubconss[liftgubconsidx]->gubvars[0]; /* C2 GUBs contain only one variable */
4386 weight = weights[liftvar];
4387
4388 assert(liftvar >= 0 && liftvar < nvars);
4389 assert(SCIPisFeasEQ(scip, solvals[liftvar], 1.0));
4390 assert(weight > 0);
4391
4392 /* uses binary search to find
4393 * z = max { w : 0 <= w <= |C_1| + sum_{k=1}^{i-1} alpha_{j_k}, minweights_[w] <= a_0 - fixedonesweight + a_{j_i}}
4394 */
4395 left = 0;
4396 right = minweightslen;
4397 while( left < right - 1 )
4398 {
4399 middle = (left + right) / 2;
4400 assert(0 <= middle && middle < minweightslen);
4401 if( minweights[middle] <= capacity - fixedonesweight + weight )
4402 left = middle;
4403 else
4404 right = middle;
4405 }
4406 assert(left == right - 1);
4407 assert(0 <= left && left < minweightslen);
4408 assert(minweights[left] <= capacity - fixedonesweight + weight);
4409 assert(left == minweightslen - 1 || minweights[left + 1] > capacity - fixedonesweight + weight);
4410
4411 /* now z = left */
4412 z = left;
4413 assert(z >= *liftrhs);
4414
4415 /* calculates lifting coefficients alpha_{j_i} = z - liftrhs */
4416 liftcoef = z - (*liftrhs);
4417 liftcoefs[liftvar] = liftcoef;
4418 assert(liftcoef >= 0);
4419
4420 /* updates sum of weights of variables fixed to one */
4421 fixedonesweight -= weight;
4422
4423 /* updates right-hand side of current valid inequality */
4424 (*liftrhs) += liftcoef;
4425 assert(*liftrhs >= alpha0);
4426
4427 /* minweight table and activity of current valid inequality will not change, if alpha_{j_i} = 0 */
4428 if( liftcoef == 0 )
4429 continue;
4430
4431 /* updates activity of current valid inequality */
4432 (*cutact) += liftcoef * solvals[liftvar];
4433
4434 /* enlarges current minweight table:
4435 * from minweightlen = |gubconsGC1| + sum_{k=1,2,...,i-1}sum_{j in Q_k} alpha_j + 1 entries
4436 * to |gubconsGC1| + sum_{k=1,2,...,i }sum_{j in Q_k} alpha_j + 1 entries
4437 * and sets minweights_i[w] = infinity for
4438 * w = |C1| + sum_{k=1,2,...,i-1}sum_{j in Q_k} alpha_j + 1 , ... , |C1| + sum_{k=1,2,...,i}sum_{j in Q_k} alpha_j
4439 */
4440 SCIP_CALL( enlargeMinweights(scip, &minweights, &minweightslen, &minweightssize, minweightslen + liftcoef) );
4441
4442 /* updates minweight table: minweight_i+1[w] =
4443 * min{ minweights_i[w], a_{j_i}}, if w < alpha_j_i
4444 * min{ minweights_i[w], minweights_i[w - alpha_j_i] + a_j_i}, if w >= alpha_j_i
4445 */
4446 for( w = minweightslen - 1; w >= 0; w-- )
4447 {
4448 if( w < liftcoef )
4449 {
4450 min = MIN(minweights[w], weight);
4451 minweights[w] = min;
4452 }
4453 else
4454 {
4455 SCIP_Longint tmpval;
4456
4457 assert(w >= liftcoef);
4458
4459 tmpval = safeAddMinweightsGUB(minweights[w-liftcoef], weight);
4460 min = MIN(minweights[w], tmpval);
4461 minweights[w] = min;
4462 }
4463 }
4464 }
4465 assert(fixedonesweight == 0);
4466 assert(*liftrhs >= alpha0);
4467
4468 /* sequentially up-lifts variables in GUB constraints in GR GUBs */
4469 for( j = 0; j < ngubconsGR; j++ )
4470 {
4471 liftgubconsidx = gubconsGR[j];
4472
4473 assert(liftgubconsidx >=0 && liftgubconsidx < ngubconss);
4474 assert(gubset->gubconsstatus[liftgubconsidx] == GUBCONSSTATUS_BELONGSTOSET_GR);
4475
4476 sumliftcoef = 0;
4477 nliftgubvars = 0;
4478 for( k = 0; k < gubset->gubconss[liftgubconsidx]->ngubvars; k++ )
4479 {
4480 if(gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_BELONGSTOSET_R )
4481 {
4482 liftvar = gubset->gubconss[liftgubconsidx]->gubvars[k];
4483 weight = weights[liftvar];
4484 assert(weight > 0);
4485 assert(liftvar >= 0 && liftvar < nvars);
4486 assert(capacity - weight >= 0);
4487 assert((*liftrhs) + 1 >= minweightslen || minweights[(*liftrhs) + 1] > capacity - weight);
4488
4489 /* put variable into array of variables in GUB that are considered for the lifting,
4490 * i.e., not capacity exceeding
4491 */
4492 liftgubvars[nliftgubvars] = liftvar;
4493 nliftgubvars++;
4494
4495 /* sets z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - a_{j_i} } = liftrhs,
4496 * if minweights_i[liftrhs] <= a_0 - a_{j_i}
4497 */
4498 if( minweights[*liftrhs] <= capacity - weight )
4499 {
4500 z = *liftrhs;
4501 }
4502 /* uses binary search to find z = max { w : 0 <= w <= liftrhs, minweights_i[w] <= a_0 - a_{j_i} }
4503 */
4504 else
4505 {
4506 left = 0;
4507 right = (*liftrhs) + 1;
4508 while( left < right - 1 )
4509 {
4510 middle = (left + right) / 2;
4511 assert(0 <= middle && middle < minweightslen);
4512 if( minweights[middle] <= capacity - weight )
4513 left = middle;
4514 else
4515 right = middle;
4516 }
4517 assert(left == right - 1);
4518 assert(0 <= left && left < minweightslen);
4519 assert(minweights[left] <= capacity - weight);
4520 assert(left == minweightslen - 1 || minweights[left + 1] > capacity - weight);
4521
4522 /* now z = left */
4523 z = left;
4524 assert(z <= *liftrhs);
4525 }
4526 /* calculates lifting coefficients alpha_{j_i} = liftrhs - z */
4527 liftcoef = (*liftrhs) - z;
4528 liftcoefs[liftvar] = liftcoef;
4529 assert(liftcoef >= 0 && liftcoef <= (*liftrhs) + 1);
4530
4531 /* updates activity of current valid inequality */
4532 (*cutact) += liftcoef * solvals[liftvar];
4533
4534 /* updates sum of all lifting coefficients in GUB */
4535 sumliftcoef += liftcoefs[liftvar];
4536 }
4537 else
4538 assert(gubset->gubconss[liftgubconsidx]->gubvarsstatus[k] == GUBVARSTATUS_CAPACITYEXCEEDED);
4539 }
4540 assert(nliftgubvars >= 1); /* at least one variable is in R */
4541
4542 /* minweight table and activity of current valid inequality will not change if (sum of alpha_{j_i} in GUB) = 0 */
4543 if( sumliftcoef == 0 )
4544 continue;
4545
4546 /* updates minweight table: minweight_i+1[w] =
4547 * min{ minweights_i[w], min{ minweights_i[w - alpha_k]^{+} + a_k : k in GUB_j_i } }
4548 */
4549 for( w = *liftrhs; w >= 0; w-- )
4550 {
4551 for( k = 0; k < nliftgubvars; k++ )
4552 {
4553 liftcoef = liftcoefs[liftgubvars[k]];
4554 weight = weights[liftgubvars[k]];
4555
4556 if( w < liftcoef )
4557 {
4558 min = MIN(minweights[w], weight);
4559 minweights[w] = min;
4560 }
4561 else
4562 {
4563 SCIP_Longint tmpval;
4564
4565 assert(w >= liftcoef);
4566
4567 tmpval = safeAddMinweightsGUB(minweights[w-liftcoef], weight);
4568 min = MIN(minweights[w], tmpval);
4569 minweights[w] = min;
4570 }
4571 }
4572 }
4573 assert(minweights[0] == 0);
4574 }
4575
4576 /* frees temporary memory */
4577 SCIPfreeBufferArray(scip, &minweights);
4578 SCIPfreeBufferArray(scip, &finished);
4579 SCIPfreeBufferArray(scip, &unfinished);
4580 SCIPfreeBufferArray(scip, &liftgubvars);
4581 SCIPfreeBufferArray(scip, &gubconsGOC1 );
4582 SCIPfreeBufferArray(scip, &gubconsGNC1);
4583
4584 return SCIP_OKAY;
4585 }
4586
4587 /** lifts given minimal cover inequality
4588 * \f[
4589 * \sum_{j \in C} x_j \leq |C| - 1
4590 * \f]
4591 * valid for
4592 * \f[
4593 * S^0 = \{ x \in {0,1}^{|C|} : \sum_{j \in C} a_j x_j \leq a_0 \}
4594 * \f]
4595 * to a valid inequality
4596 * \f[
4597 * \sum_{j \in C} x_j + \sum_{j \in N \setminus C} \alpha_j x_j \leq |C| - 1
4598 * \f]
4599 * for
4600 * \f[
4601 * S = \{ x \in {0,1}^{|N|} : \sum_{j \in N} a_j x_j \leq a_0 \};
4602 * \f]
4603 * uses superadditive up-lifting for the variables in \f$N \setminus C\f$.
4604 */
4605 static
superadditiveUpLifting(SCIP * scip,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * covervars,int * noncovervars,int ncovervars,int nnoncovervars,SCIP_Longint coverweight,SCIP_Real * liftcoefs,SCIP_Real * cutact)4606 SCIP_RETCODE superadditiveUpLifting(
4607 SCIP* scip, /**< SCIP data structure */
4608 SCIP_VAR** vars, /**< variables in knapsack constraint */
4609 int nvars, /**< number of variables in knapsack constraint */
4610 int ntightened, /**< number of variables with tightened upper bound */
4611 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
4612 SCIP_Longint capacity, /**< capacity of knapsack */
4613 SCIP_Real* solvals, /**< solution values of all problem variables */
4614 int* covervars, /**< cover variables */
4615 int* noncovervars, /**< noncover variables */
4616 int ncovervars, /**< number of cover variables */
4617 int nnoncovervars, /**< number of noncover variables */
4618 SCIP_Longint coverweight, /**< weight of cover */
4619 SCIP_Real* liftcoefs, /**< pointer to store lifting coefficient of vars in knapsack constraint */
4620 SCIP_Real* cutact /**< pointer to store activity of lifted valid inequality */
4621 )
4622 {
4623 SCIP_Longint* maxweightsums;
4624 SCIP_Longint* intervalends;
4625 SCIP_Longint* rhos;
4626 SCIP_Real* sortkeys;
4627 SCIP_Longint lambda;
4628 int j;
4629 int h;
4630
4631 assert(scip != NULL);
4632 assert(vars != NULL);
4633 assert(nvars >= 0);
4634 assert(weights != NULL);
4635 assert(capacity >= 0);
4636 assert(solvals != NULL);
4637 assert(covervars != NULL);
4638 assert(noncovervars != NULL);
4639 assert(ncovervars > 0 && ncovervars <= nvars);
4640 assert(nnoncovervars >= 0 && nnoncovervars <= nvars - ntightened);
4641 assert(ncovervars + nnoncovervars == nvars - ntightened);
4642 assert(liftcoefs != NULL);
4643 assert(cutact != NULL);
4644
4645 /* allocates temporary memory */
4646 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeys, ncovervars) );
4647 SCIP_CALL( SCIPallocBufferArray(scip, &maxweightsums, ncovervars + 1) );
4648 SCIP_CALL( SCIPallocBufferArray(scip, &intervalends, ncovervars) );
4649 SCIP_CALL( SCIPallocBufferArray(scip, &rhos, ncovervars) );
4650
4651 /* initializes data structures */
4652 BMSclearMemoryArray(liftcoefs, nvars);
4653 *cutact = 0.0;
4654
4655 /* sets lifting coefficient of variables in C, sorts variables in C such that a_1 >= a_2 >= ... >= a_|C|
4656 * and calculates activity of current valid inequality
4657 */
4658 for( j = 0; j < ncovervars; j++ )
4659 {
4660 assert(liftcoefs[covervars[j]] == 0.0);
4661 liftcoefs[covervars[j]] = 1.0;
4662 sortkeys[j] = (SCIP_Real) weights[covervars[j]];
4663 (*cutact) += solvals[covervars[j]];
4664 }
4665 SCIPsortDownRealInt(sortkeys, covervars, ncovervars);
4666
4667 /* calculates weight excess of cover C */
4668 lambda = coverweight - capacity;
4669 assert(lambda > 0);
4670
4671 /* calculates A_h for h = 0,...,|C|, I_h for h = 1,...,|C| and rho_h for h = 1,...,|C| */
4672 maxweightsums[0] = 0;
4673 for( h = 1; h <= ncovervars; h++ )
4674 {
4675 maxweightsums[h] = maxweightsums[h-1] + weights[covervars[h-1]];
4676 intervalends[h-1] = maxweightsums[h] - lambda;
4677 rhos[h-1] = MAX(0, weights[covervars[h-1]] - weights[covervars[0]] + lambda);
4678 }
4679
4680 /* sorts variables in N\C such that a_{j_1} <= a_{j_2} <= ... <= a_{j_t} */
4681 for( j = 0; j < nnoncovervars; j++ )
4682 sortkeys[j] = (SCIP_Real) (weights[noncovervars[j]]);
4683 SCIPsortRealInt(sortkeys, noncovervars, nnoncovervars);
4684
4685 /* calculates lifting coefficient for all variables in N\C */
4686 h = 0;
4687 for( j = 0; j < nnoncovervars; j++ )
4688 {
4689 int liftvar;
4690 SCIP_Longint weight;
4691 SCIP_Real liftcoef;
4692
4693 liftvar = noncovervars[j];
4694 weight = weights[liftvar];
4695
4696 while( intervalends[h] < weight )
4697 h++;
4698
4699 if( h == 0 )
4700 liftcoef = h;
4701 else
4702 {
4703 if( weight <= intervalends[h-1] + rhos[h] )
4704 {
4705 SCIP_Real tmp1;
4706 SCIP_Real tmp2;
4707 tmp1 = (SCIP_Real) (intervalends[h-1] + rhos[h] - weight);
4708 tmp2 = (SCIP_Real) rhos[1];
4709 liftcoef = h - ( tmp1 / tmp2 );
4710 }
4711 else
4712 liftcoef = h;
4713 }
4714
4715 /* sets lifting coefficient */
4716 assert(liftcoefs[liftvar] == 0.0);
4717 liftcoefs[liftvar] = liftcoef;
4718
4719 /* updates activity of current valid inequality */
4720 (*cutact) += liftcoef * solvals[liftvar];
4721 }
4722
4723 /* frees temporary memory */
4724 SCIPfreeBufferArray(scip, &rhos);
4725 SCIPfreeBufferArray(scip, &intervalends);
4726 SCIPfreeBufferArray(scip, &maxweightsums);
4727 SCIPfreeBufferArray(scip, &sortkeys);
4728
4729 return SCIP_OKAY;
4730 }
4731
4732
4733 /** separates lifted minimal cover inequalities using sequential up- and down-lifting and GUB information, if wanted, for
4734 * given knapsack problem
4735 */
4736 static
separateSequLiftedMinimalCoverInequality(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * mincovervars,int * nonmincovervars,int nmincovervars,int nnonmincovervars,SCIP_SOL * sol,SCIP_GUBSET * gubset,SCIP_Bool * cutoff,int * ncuts)4737 SCIP_RETCODE separateSequLiftedMinimalCoverInequality(
4738 SCIP* scip, /**< SCIP data structure */
4739 SCIP_CONS* cons, /**< originating constraint of the knapsack problem, or NULL */
4740 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
4741 SCIP_VAR** vars, /**< variables in knapsack constraint */
4742 int nvars, /**< number of variables in knapsack constraint */
4743 int ntightened, /**< number of variables with tightened upper bound */
4744 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
4745 SCIP_Longint capacity, /**< capacity of knapsack */
4746 SCIP_Real* solvals, /**< solution values of all problem variables */
4747 int* mincovervars, /**< mincover variables */
4748 int* nonmincovervars, /**< nonmincover variables */
4749 int nmincovervars, /**< number of mincover variables */
4750 int nnonmincovervars, /**< number of nonmincover variables */
4751 SCIP_SOL* sol, /**< primal SCIP solution to separate, NULL for current LP solution */
4752 SCIP_GUBSET* gubset, /**< GUB set data structure, NULL if no GUB information should be used */
4753 SCIP_Bool* cutoff, /**< pointer to store whether a cutoff has been detected */
4754 int* ncuts /**< pointer to add up the number of found cuts */
4755 )
4756 {
4757 int* varsC1;
4758 int* varsC2;
4759 int* varsF;
4760 int* varsR;
4761 int nvarsC1;
4762 int nvarsC2;
4763 int nvarsF;
4764 int nvarsR;
4765 SCIP_Real cutact;
4766 int* liftcoefs;
4767 int liftrhs;
4768
4769 assert( cutoff != NULL );
4770 *cutoff = FALSE;
4771
4772 /* allocates temporary memory */
4773 SCIP_CALL( SCIPallocBufferArray(scip, &varsC1, nvars) );
4774 SCIP_CALL( SCIPallocBufferArray(scip, &varsC2, nvars) );
4775 SCIP_CALL( SCIPallocBufferArray(scip, &varsF, nvars) );
4776 SCIP_CALL( SCIPallocBufferArray(scip, &varsR, nvars) );
4777 SCIP_CALL( SCIPallocBufferArray(scip, &liftcoefs, nvars) );
4778
4779 /* gets partition (C_1,C_2) of C, i.e. C_1 & C_2 = C and C_1 cap C_2 = emptyset, with C_1 not empty; chooses partition
4780 * as follows
4781 * C_2 = { j in C : x*_j = 1 } and
4782 * C_1 = C\C_2
4783 */
4784 getPartitionCovervars(scip, solvals, mincovervars, nmincovervars, varsC1, varsC2, &nvarsC1, &nvarsC2);
4785 assert(nvarsC1 + nvarsC2 == nmincovervars);
4786 assert(nmincovervars > 0);
4787 assert(nvarsC1 >= 0); /* nvarsC1 > 0 does not always hold, because relaxed knapsack conss may already be violated */
4788
4789 /* changes partition (C_1,C_2) of minimal cover C, if |C1| = 1, by moving one variable from C2 to C1 */
4790 if( nvarsC1 < 2 && nvarsC2 > 0)
4791 {
4792 SCIP_CALL( changePartitionCovervars(scip, weights, varsC1, varsC2, &nvarsC1, &nvarsC2) );
4793 assert(nvarsC1 >= 1);
4794 }
4795 assert(nvarsC2 == 0 || nvarsC1 >= 1);
4796
4797 /* gets partition (F,R) of N\C, i.e. F & R = N\C and F cap R = emptyset; chooses partition as follows
4798 * R = { j in N\C : x*_j = 0 } and
4799 * F = (N\C)\F
4800 */
4801 getPartitionNoncovervars(scip, solvals, nonmincovervars, nnonmincovervars, varsF, varsR, &nvarsF, &nvarsR);
4802 assert(nvarsF + nvarsR == nnonmincovervars);
4803 assert(nvarsC1 + nvarsC2 + nvarsF + nvarsR == nvars - ntightened);
4804
4805 /* lift cuts without GUB information */
4806 if( gubset == NULL )
4807 {
4808 /* sorts variables in F, C_2, R according to the second level lifting sequence that will be used in the sequential
4809 * lifting procedure
4810 */
4811 SCIP_CALL( getLiftingSequence(scip, solvals, weights, varsF, varsC2, varsR, nvarsF, nvarsC2, nvarsR) );
4812
4813 /* lifts minimal cover inequality sum_{j in C_1} x_j <= |C_1| - 1 valid for
4814 *
4815 * S^0 = { x in {0,1}^|C_1| : sum_{j in C_1} a_j x_j <= a_0 - sum_{j in C_2} a_j }
4816 *
4817 * to a valid inequality sum_{j in C_1} x_j + sum_{j in N\C_1} alpha_j x_j <= |C_1| - 1 + sum_{j in C_2} alpha_j for
4818 *
4819 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0 },
4820 *
4821 * uses sequential up-lifting for the variables in F, sequential down-lifting for the variable in C_2 and sequential
4822 * up-lifting for the variables in R according to the second level lifting sequence
4823 */
4824 SCIP_CALL( sequentialUpAndDownLifting(scip, vars, nvars, ntightened, weights, capacity, solvals, varsC1, varsC2,
4825 varsF, varsR, nvarsC1, nvarsC2, nvarsF, nvarsR, nvarsC1 - 1, liftcoefs, &cutact, &liftrhs) );
4826 }
4827 /* lift cuts with GUB information */
4828 else
4829 {
4830 int* gubconsGC1;
4831 int* gubconsGC2;
4832 int* gubconsGFC1;
4833 int* gubconsGR;
4834 int ngubconsGC1;
4835 int ngubconsGC2;
4836 int ngubconsGFC1;
4837 int ngubconsGR;
4838 int ngubconss;
4839 int nconstightened;
4840 int maxgubvarssize;
4841
4842 assert(nvars == gubset->nvars);
4843
4844 ngubconsGC1 = 0;
4845 ngubconsGC2 = 0;
4846 ngubconsGFC1 = 0;
4847 ngubconsGR = 0;
4848 ngubconss = gubset->ngubconss;
4849 nconstightened = 0;
4850 maxgubvarssize = 0;
4851
4852 /* allocates temporary memory */
4853 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGC1, ngubconss) );
4854 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGC2, ngubconss) );
4855 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGFC1, ngubconss) );
4856 SCIP_CALL( SCIPallocBufferArray(scip, &gubconsGR, ngubconss) );
4857
4858 /* categorizies GUBs of knapsack GUB partion into GOC1, GNC1, GF, GC2, and GR and computes a lifting sequence of
4859 * the GUBs for the sequential GUB wise lifting procedure
4860 */
4861 SCIP_CALL( getLiftingSequenceGUB(scip, gubset, solvals, weights, varsC1, varsC2, varsF, varsR, nvarsC1,
4862 nvarsC2, nvarsF, nvarsR, gubconsGC1, gubconsGC2, gubconsGFC1, gubconsGR, &ngubconsGC1, &ngubconsGC2,
4863 &ngubconsGFC1, &ngubconsGR, &nconstightened, &maxgubvarssize) );
4864
4865 /* lifts minimal cover inequality sum_{j in C_1} x_j <= |C_1| - 1 valid for
4866 *
4867 * S^0 = { x in {0,1}^|C_1| : sum_{j in C_1} a_j x_j <= a_0 - sum_{j in C_2} a_j,
4868 * sum_{j in Q_i} x_j <= 1, forall i in I }
4869 *
4870 * to a valid inequality sum_{j in C_1} x_j + sum_{j in N\C_1} alpha_j x_j <= |C_1| - 1 + sum_{j in C_2} alpha_j for
4871 *
4872 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0, sum_{j in Q_i} x_j <= 1, forall i in I },
4873 *
4874 * uses sequential up-lifting for the variables in GUB constraints in gubconsGFC1,
4875 * sequential down-lifting for the variables in GUB constraints in gubconsGC2, and
4876 * sequential up-lifting for the variabels in GUB constraints in gubconsGR.
4877 */
4878 SCIP_CALL( sequentialUpAndDownLiftingGUB(scip, gubset, vars, nconstightened, weights, capacity, solvals, gubconsGC1,
4879 gubconsGC2, gubconsGFC1, gubconsGR, ngubconsGC1, ngubconsGC2, ngubconsGFC1, ngubconsGR,
4880 MIN(nvarsC1 - 1, ngubconsGC1), liftcoefs, &cutact, &liftrhs, maxgubvarssize) );
4881
4882 /* frees temporary memory */
4883 SCIPfreeBufferArray(scip, &gubconsGR);
4884 SCIPfreeBufferArray(scip, &gubconsGFC1);
4885 SCIPfreeBufferArray(scip, &gubconsGC2);
4886 SCIPfreeBufferArray(scip, &gubconsGC1);
4887 }
4888
4889 /* checks, if lifting yielded a violated cut */
4890 if( SCIPisEfficacious(scip, (cutact - liftrhs)/sqrt((SCIP_Real)MAX(liftrhs, 1))) )
4891 {
4892 SCIP_ROW* row;
4893 char name[SCIP_MAXSTRLEN];
4894 int j;
4895
4896 /* creates LP row */
4897 assert( cons == NULL || sepa == NULL );
4898 if ( cons != NULL )
4899 {
4900 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_mcseq%" SCIP_LONGINT_FORMAT "", SCIPconsGetName(cons), SCIPconshdlrGetNCutsFound(SCIPconsGetHdlr(cons)));
4901 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs,
4902 cons != NULL ? SCIPconsIsLocal(cons) : FALSE, FALSE,
4903 cons != NULL ? SCIPconsIsRemovable(cons) : TRUE) );
4904 }
4905 else if ( sepa != NULL )
4906 {
4907 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_mcseq_%" SCIP_LONGINT_FORMAT "", SCIPsepaGetName(sepa), SCIPsepaGetNCutsFound(sepa));
4908 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &row, sepa, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
4909 }
4910 else
4911 {
4912 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nn_mcseq_%d", *ncuts);
4913 SCIP_CALL( SCIPcreateEmptyRowUnspec(scip, &row, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
4914 }
4915
4916 /* adds all variables in the knapsack constraint with calculated lifting coefficient to the cut */
4917 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
4918 assert(nvarsC1 + nvarsC2 + nvarsF + nvarsR == nvars - ntightened);
4919 for( j = 0; j < nvarsC1; j++ )
4920 {
4921 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsC1[j]], 1.0) );
4922 }
4923 for( j = 0; j < nvarsC2; j++ )
4924 {
4925 if( liftcoefs[varsC2[j]] > 0 )
4926 {
4927 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsC2[j]], (SCIP_Real)liftcoefs[varsC2[j]]) );
4928 }
4929 }
4930 for( j = 0; j < nvarsF; j++ )
4931 {
4932 if( liftcoefs[varsF[j]] > 0 )
4933 {
4934 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsF[j]], (SCIP_Real)liftcoefs[varsF[j]]) );
4935 }
4936 }
4937 for( j = 0; j < nvarsR; j++ )
4938 {
4939 if( liftcoefs[varsR[j]] > 0 )
4940 {
4941 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsR[j]], (SCIP_Real)liftcoefs[varsR[j]]) );
4942 }
4943 }
4944 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
4945
4946 /* checks, if cut is violated enough */
4947 if( SCIPisCutEfficacious(scip, sol, row) )
4948 {
4949 if( cons != NULL )
4950 {
4951 SCIP_CALL( SCIPresetConsAge(scip, cons) );
4952 }
4953 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) );
4954 (*ncuts)++;
4955 }
4956 SCIP_CALL( SCIPreleaseRow(scip, &row) );
4957 }
4958
4959 /* frees temporary memory */
4960 SCIPfreeBufferArray(scip, &liftcoefs);
4961 SCIPfreeBufferArray(scip, &varsR);
4962 SCIPfreeBufferArray(scip, &varsF);
4963 SCIPfreeBufferArray(scip, &varsC2);
4964 SCIPfreeBufferArray(scip, &varsC1);
4965
4966 return SCIP_OKAY;
4967 }
4968
4969 /** separates lifted extended weight inequalities using sequential up- and down-lifting for given knapsack problem */
4970 static
separateSequLiftedExtendedWeightInequality(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * feassetvars,int * nonfeassetvars,int nfeassetvars,int nnonfeassetvars,SCIP_SOL * sol,SCIP_Bool * cutoff,int * ncuts)4971 SCIP_RETCODE separateSequLiftedExtendedWeightInequality(
4972 SCIP* scip, /**< SCIP data structure */
4973 SCIP_CONS* cons, /**< constraint that originates the knapsack problem, or NULL */
4974 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
4975 SCIP_VAR** vars, /**< variables in knapsack constraint */
4976 int nvars, /**< number of variables in knapsack constraint */
4977 int ntightened, /**< number of variables with tightened upper bound */
4978 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
4979 SCIP_Longint capacity, /**< capacity of knapsack */
4980 SCIP_Real* solvals, /**< solution values of all problem variables */
4981 int* feassetvars, /**< variables in feasible set */
4982 int* nonfeassetvars, /**< variables not in feasible set */
4983 int nfeassetvars, /**< number of variables in feasible set */
4984 int nnonfeassetvars, /**< number of variables not in feasible set */
4985 SCIP_SOL* sol, /**< primal SCIP solution to separate, NULL for current LP solution */
4986 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
4987 int* ncuts /**< pointer to add up the number of found cuts */
4988 )
4989 {
4990 int* varsT1;
4991 int* varsT2;
4992 int* varsF;
4993 int* varsR;
4994 int* liftcoefs;
4995 SCIP_Real cutact;
4996 int nvarsT1;
4997 int nvarsT2;
4998 int nvarsF;
4999 int nvarsR;
5000 int liftrhs;
5001 int j;
5002
5003 assert( cutoff != NULL );
5004 *cutoff = FALSE;
5005
5006 /* allocates temporary memory */
5007 SCIP_CALL( SCIPallocBufferArray(scip, &varsT1, nvars) );
5008 SCIP_CALL( SCIPallocBufferArray(scip, &varsT2, nvars) );
5009 SCIP_CALL( SCIPallocBufferArray(scip, &varsF, nvars) );
5010 SCIP_CALL( SCIPallocBufferArray(scip, &varsR, nvars) );
5011 SCIP_CALL( SCIPallocBufferArray(scip, &liftcoefs, nvars) );
5012
5013 /* gets partition (T_1,T_2) of T, i.e. T_1 & T_2 = T and T_1 cap T_2 = emptyset, with T_1 not empty; chooses partition
5014 * as follows
5015 * T_2 = { j in T : x*_j = 1 } and
5016 * T_1 = T\T_2
5017 */
5018 getPartitionCovervars(scip, solvals, feassetvars, nfeassetvars, varsT1, varsT2, &nvarsT1, &nvarsT2);
5019 assert(nvarsT1 + nvarsT2 == nfeassetvars);
5020
5021 /* changes partition (T_1,T_2) of feasible set T, if |T1| = 0, by moving one variable from T2 to T1 */
5022 if( nvarsT1 == 0 && nvarsT2 > 0)
5023 {
5024 SCIP_CALL( changePartitionFeasiblesetvars(scip, weights, varsT1, varsT2, &nvarsT1, &nvarsT2) );
5025 assert(nvarsT1 == 1);
5026 }
5027 assert(nvarsT2 == 0 || nvarsT1 > 0);
5028
5029 /* gets partition (F,R) of N\T, i.e. F & R = N\T and F cap R = emptyset; chooses partition as follows
5030 * R = { j in N\T : x*_j = 0 } and
5031 * F = (N\T)\F
5032 */
5033 getPartitionNoncovervars(scip, solvals, nonfeassetvars, nnonfeassetvars, varsF, varsR, &nvarsF, &nvarsR);
5034 assert(nvarsF + nvarsR == nnonfeassetvars);
5035 assert(nvarsT1 + nvarsT2 + nvarsF + nvarsR == nvars - ntightened);
5036
5037 /* sorts variables in F, T_2, and R according to the second level lifting sequence that will be used in the sequential
5038 * lifting procedure (the variable removed last from the initial cover does not have to be lifted first, therefore it
5039 * is included in the sorting routine)
5040 */
5041 SCIP_CALL( getLiftingSequence(scip, solvals, weights, varsF, varsT2, varsR, nvarsF, nvarsT2, nvarsR) );
5042
5043 /* lifts extended weight inequality sum_{j in T_1} x_j <= |T_1| valid for
5044 *
5045 * S^0 = { x in {0,1}^|T_1| : sum_{j in T_1} a_j x_j <= a_0 - sum_{j in T_2} a_j }
5046 *
5047 * to a valid inequality sum_{j in T_1} x_j + sum_{j in N\T_1} alpha_j x_j <= |T_1| + sum_{j in T_2} alpha_j for
5048 *
5049 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0 },
5050 *
5051 * uses sequential up-lifting for the variables in F, sequential down-lifting for the variable in T_2 and sequential
5052 * up-lifting for the variabels in R according to the second level lifting sequence
5053 */
5054 SCIP_CALL( sequentialUpAndDownLifting(scip, vars, nvars, ntightened, weights, capacity, solvals, varsT1, varsT2, varsF, varsR,
5055 nvarsT1, nvarsT2, nvarsF, nvarsR, nvarsT1, liftcoefs, &cutact, &liftrhs) );
5056
5057 /* checks, if lifting yielded a violated cut */
5058 if( SCIPisEfficacious(scip, (cutact - liftrhs)/sqrt((SCIP_Real)MAX(liftrhs, 1))) )
5059 {
5060 SCIP_ROW* row;
5061 char name[SCIP_MAXSTRLEN];
5062
5063 /* creates LP row */
5064 assert( cons == NULL || sepa == NULL );
5065 if( cons != NULL )
5066 {
5067 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_ewseq%" SCIP_LONGINT_FORMAT "", SCIPconsGetName(cons), SCIPconshdlrGetNCutsFound(SCIPconsGetHdlr(cons)));
5068 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &row, SCIPconsGetHdlr(cons), name, -SCIPinfinity(scip), (SCIP_Real)liftrhs,
5069 cons != NULL ? SCIPconsIsLocal(cons) : FALSE, FALSE,
5070 cons != NULL ? SCIPconsIsRemovable(cons) : TRUE) );
5071 }
5072 else if ( sepa != NULL )
5073 {
5074 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_ewseq_%" SCIP_LONGINT_FORMAT "", SCIPsepaGetName(sepa), SCIPsepaGetNCutsFound(sepa));
5075 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &row, sepa, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
5076 }
5077 else
5078 {
5079 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nn_ewseq_%d", *ncuts);
5080 SCIP_CALL( SCIPcreateEmptyRowUnspec(scip, &row, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
5081 }
5082
5083 /* adds all variables in the knapsack constraint with calculated lifting coefficient to the cut */
5084 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
5085 assert(nvarsT1 + nvarsT2 + nvarsF + nvarsR == nvars - ntightened);
5086 for( j = 0; j < nvarsT1; j++ )
5087 {
5088 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsT1[j]], 1.0) );
5089 }
5090 for( j = 0; j < nvarsT2; j++ )
5091 {
5092 if( liftcoefs[varsT2[j]] > 0 )
5093 {
5094 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsT2[j]], (SCIP_Real)liftcoefs[varsT2[j]]) );
5095 }
5096 }
5097 for( j = 0; j < nvarsF; j++ )
5098 {
5099 if( liftcoefs[varsF[j]] > 0 )
5100 {
5101 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsF[j]], (SCIP_Real)liftcoefs[varsF[j]]) );
5102 }
5103 }
5104 for( j = 0; j < nvarsR; j++ )
5105 {
5106 if( liftcoefs[varsR[j]] > 0 )
5107 {
5108 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[varsR[j]], (SCIP_Real)liftcoefs[varsR[j]]) );
5109 }
5110 }
5111 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
5112
5113 /* checks, if cut is violated enough */
5114 if( SCIPisCutEfficacious(scip, sol, row) )
5115 {
5116 if( cons != NULL )
5117 {
5118 SCIP_CALL( SCIPresetConsAge(scip, cons) );
5119 }
5120 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) );
5121 (*ncuts)++;
5122 }
5123 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5124 }
5125
5126 /* frees temporary memory */
5127 SCIPfreeBufferArray(scip, &liftcoefs);
5128 SCIPfreeBufferArray(scip, &varsR);
5129 SCIPfreeBufferArray(scip, &varsF);
5130 SCIPfreeBufferArray(scip, &varsT2);
5131 SCIPfreeBufferArray(scip, &varsT1);
5132
5133 return SCIP_OKAY;
5134 }
5135
5136 /** separates lifted minimal cover inequalities using superadditive up-lifting for given knapsack problem */
5137 static
separateSupLiftedMinimalCoverInequality(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * mincovervars,int * nonmincovervars,int nmincovervars,int nnonmincovervars,SCIP_Longint mincoverweight,SCIP_SOL * sol,SCIP_Bool * cutoff,int * ncuts)5138 SCIP_RETCODE separateSupLiftedMinimalCoverInequality(
5139 SCIP* scip, /**< SCIP data structure */
5140 SCIP_CONS* cons, /**< constraint that originates the knapsack problem, or NULL */
5141 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
5142 SCIP_VAR** vars, /**< variables in knapsack constraint */
5143 int nvars, /**< number of variables in knapsack constraint */
5144 int ntightened, /**< number of variables with tightened upper bound */
5145 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
5146 SCIP_Longint capacity, /**< capacity of knapsack */
5147 SCIP_Real* solvals, /**< solution values of all problem variables */
5148 int* mincovervars, /**< mincover variables */
5149 int* nonmincovervars, /**< nonmincover variables */
5150 int nmincovervars, /**< number of mincover variables */
5151 int nnonmincovervars, /**< number of nonmincover variables */
5152 SCIP_Longint mincoverweight, /**< weight of minimal cover */
5153 SCIP_SOL* sol, /**< primal SCIP solution to separate, NULL for current LP solution */
5154 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
5155 int* ncuts /**< pointer to add up the number of found cuts */
5156 )
5157 {
5158 SCIP_Real* realliftcoefs;
5159 SCIP_Real cutact;
5160 int liftrhs;
5161
5162 assert( cutoff != NULL );
5163 *cutoff = FALSE;
5164 cutact = 0.0;
5165
5166 /* allocates temporary memory */
5167 SCIP_CALL( SCIPallocBufferArray(scip, &realliftcoefs, nvars) );
5168
5169 /* lifts minimal cover inequality sum_{j in C} x_j <= |C| - 1 valid for
5170 *
5171 * S^0 = { x in {0,1}^|C| : sum_{j in C} a_j x_j <= a_0 }
5172 *
5173 * to a valid inequality sum_{j in C} x_j + sum_{j in N\C} alpha_j x_j <= |C| - 1 for
5174 *
5175 * S = { x in {0,1}^|N| : sum_{j in N} a_j x_j <= a_0 },
5176 *
5177 * uses superadditive up-lifting for the variables in N\C.
5178 */
5179 SCIP_CALL( superadditiveUpLifting(scip, vars, nvars, ntightened, weights, capacity, solvals, mincovervars,
5180 nonmincovervars, nmincovervars, nnonmincovervars, mincoverweight, realliftcoefs, &cutact) );
5181 liftrhs = nmincovervars - 1;
5182
5183 /* checks, if lifting yielded a violated cut */
5184 if( SCIPisEfficacious(scip, (cutact - liftrhs)/sqrt((SCIP_Real)MAX(liftrhs, 1))) )
5185 {
5186 SCIP_ROW* row;
5187 char name[SCIP_MAXSTRLEN];
5188 int j;
5189
5190 /* creates LP row */
5191 assert( cons == NULL || sepa == NULL );
5192 if ( cons != NULL )
5193 {
5194 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_mcsup%" SCIP_LONGINT_FORMAT "", SCIPconsGetName(cons), SCIPconshdlrGetNCutsFound(SCIPconsGetHdlr(cons)));
5195 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &row, SCIPconsGetHdlr(cons), name, -SCIPinfinity(scip), (SCIP_Real)liftrhs,
5196 cons != NULL ? SCIPconsIsLocal(cons) : FALSE, FALSE,
5197 cons != NULL ? SCIPconsIsRemovable(cons) : TRUE) );
5198 }
5199 else if ( sepa != NULL )
5200 {
5201 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_mcsup%" SCIP_LONGINT_FORMAT "", SCIPsepaGetName(sepa), SCIPsepaGetNCutsFound(sepa));
5202 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &row, sepa, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
5203 }
5204 else
5205 {
5206 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nn_mcsup_%d", *ncuts);
5207 SCIP_CALL( SCIPcreateEmptyRowUnspec(scip, &row, name, -SCIPinfinity(scip), (SCIP_Real)liftrhs, FALSE, FALSE, TRUE) );
5208 }
5209
5210 /* adds all variables in the knapsack constraint with calculated lifting coefficient to the cut */
5211 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
5212 assert(nmincovervars + nnonmincovervars == nvars - ntightened);
5213 for( j = 0; j < nmincovervars; j++ )
5214 {
5215 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[mincovervars[j]], 1.0) );
5216 }
5217 for( j = 0; j < nnonmincovervars; j++ )
5218 {
5219 assert(SCIPisFeasGE(scip, realliftcoefs[nonmincovervars[j]], 0.0));
5220 if( SCIPisFeasGT(scip, realliftcoefs[nonmincovervars[j]], 0.0) )
5221 {
5222 SCIP_CALL( SCIPaddVarToRow(scip, row, vars[nonmincovervars[j]], realliftcoefs[nonmincovervars[j]]) );
5223 }
5224 }
5225 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
5226
5227 /* checks, if cut is violated enough */
5228 if( SCIPisCutEfficacious(scip, sol, row) )
5229 {
5230 if( cons != NULL )
5231 {
5232 SCIP_CALL( SCIPresetConsAge(scip, cons) );
5233 }
5234 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) );
5235 (*ncuts)++;
5236 }
5237 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5238 }
5239
5240 /* frees temporary memory */
5241 SCIPfreeBufferArray(scip, &realliftcoefs);
5242
5243 return SCIP_OKAY;
5244 }
5245
5246 /** converts given cover C to a minimal cover by removing variables in the reverse order in which the variables were chosen
5247 * to be in C, i.e. in the order of non-increasing (1 - x*_j)/a_j, if the transformed separation problem was used to find
5248 * C and in the order of non-increasing (1 - x*_j), if the modified transformed separation problem was used to find C;
5249 * note that all variables with x*_j = 1 will be removed last
5250 */
5251 static
makeCoverMinimal(SCIP * scip,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * covervars,int * noncovervars,int * ncovervars,int * nnoncovervars,SCIP_Longint * coverweight,SCIP_Bool modtransused)5252 SCIP_RETCODE makeCoverMinimal(
5253 SCIP* scip, /**< SCIP data structure */
5254 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
5255 SCIP_Longint capacity, /**< capacity of knapsack */
5256 SCIP_Real* solvals, /**< solution values of all problem variables */
5257 int* covervars, /**< pointer to store cover variables */
5258 int* noncovervars, /**< pointer to store noncover variables */
5259 int* ncovervars, /**< pointer to store number of cover variables */
5260 int* nnoncovervars, /**< pointer to store number of noncover variables */
5261 SCIP_Longint* coverweight, /**< pointer to store weight of cover */
5262 SCIP_Bool modtransused /**< TRUE if mod trans sepa prob was used to find cover */
5263 )
5264 {
5265 SORTKEYPAIR** sortkeypairs;
5266 SORTKEYPAIR** sortkeypairssorted;
5267 SCIP_Longint minweight;
5268 int nsortkeypairs;
5269 int minweightidx;
5270 int j;
5271 int k;
5272
5273 assert(scip != NULL);
5274 assert(covervars != NULL);
5275 assert(noncovervars != NULL);
5276 assert(ncovervars != NULL);
5277 assert(*ncovervars > 0);
5278 assert(nnoncovervars != NULL);
5279 assert(*nnoncovervars >= 0);
5280 assert(coverweight != NULL);
5281 assert(*coverweight > 0);
5282 assert(*coverweight > capacity);
5283
5284 /* allocates temporary memory; we need two arrays for the keypairs in order to be able to free them in the correct
5285 * order */
5286 nsortkeypairs = *ncovervars;
5287 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairs, nsortkeypairs) );
5288 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeypairssorted, nsortkeypairs) );
5289
5290 /* sorts C in the reverse order in which the variables were chosen to be in the cover, i.e.
5291 * such that (1 - x*_1)/a_1 >= ... >= (1 - x*_|C|)/a_|C|, if trans separation problem was used to find C
5292 * such that (1 - x*_1) >= ... >= (1 - x*_|C|), if modified trans separation problem was used to find C
5293 * note that all variables with x*_j = 1 are in the end of the sorted C, so they will be removed last from C
5294 */
5295 assert(*ncovervars == nsortkeypairs);
5296 if( modtransused )
5297 {
5298 for( j = 0; j < *ncovervars; j++ )
5299 {
5300 SCIP_CALL( SCIPallocBuffer(scip, &(sortkeypairs[j])) ); /*lint !e866 */
5301 sortkeypairssorted[j] = sortkeypairs[j];
5302
5303 sortkeypairs[j]->key1 = solvals[covervars[j]];
5304 sortkeypairs[j]->key2 = (SCIP_Real) weights[covervars[j]];
5305 }
5306 }
5307 else
5308 {
5309 for( j = 0; j < *ncovervars; j++ )
5310 {
5311 SCIP_CALL( SCIPallocBuffer(scip, &(sortkeypairs[j])) ); /*lint !e866 */
5312 sortkeypairssorted[j] = sortkeypairs[j];
5313
5314 sortkeypairs[j]->key1 = (solvals[covervars[j]] - 1.0) / ((SCIP_Real) weights[covervars[j]]);
5315 sortkeypairs[j]->key2 = (SCIP_Real) (-weights[covervars[j]]);
5316 }
5317 }
5318 SCIPsortPtrInt((void**)sortkeypairssorted, covervars, compSortkeypairs, *ncovervars);
5319
5320 /* gets j' with a_j' = min{ a_j : j in C } */
5321 minweightidx = 0;
5322 minweight = weights[covervars[minweightidx]];
5323 for( j = 1; j < *ncovervars; j++ )
5324 {
5325 if( weights[covervars[j]] <= minweight )
5326 {
5327 minweightidx = j;
5328 minweight = weights[covervars[minweightidx]];
5329 }
5330 }
5331 assert(minweightidx >= 0 && minweightidx < *ncovervars);
5332 assert(minweight > 0 && minweight <= *coverweight);
5333
5334 j = 0;
5335 /* removes variables from C until the remaining variables form a minimal cover */
5336 while( j < *ncovervars && ((*coverweight) - minweight > capacity) )
5337 {
5338 assert(minweightidx >= j);
5339 assert(checkMinweightidx(weights, capacity, covervars, *ncovervars, *coverweight, minweightidx, j));
5340
5341 /* if sum_{i in C} a_i - a_j <= a_0, j cannot be removed from C */
5342 if( (*coverweight) - weights[covervars[j]] <= capacity )
5343 {
5344 ++j;
5345 continue;
5346 }
5347
5348 /* adds j to N\C */
5349 noncovervars[*nnoncovervars] = covervars[j];
5350 (*nnoncovervars)++;
5351
5352 /* removes j from C */
5353 (*coverweight) -= weights[covervars[j]];
5354 for( k = j; k < (*ncovervars) - 1; k++ )
5355 covervars[k] = covervars[k+1];
5356 (*ncovervars)--;
5357
5358 /* updates j' with a_j' = min{ a_j : j in C } */
5359 if( j == minweightidx )
5360 {
5361 minweightidx = 0;
5362 minweight = weights[covervars[minweightidx]];
5363 for( k = 1; k < *ncovervars; k++ )
5364 {
5365 if( weights[covervars[k]] <= minweight )
5366 {
5367 minweightidx = k;
5368 minweight = weights[covervars[minweightidx]];
5369 }
5370 }
5371 assert(minweight > 0 && minweight <= *coverweight);
5372 assert(minweightidx >= 0 && minweightidx < *ncovervars);
5373 }
5374 else
5375 {
5376 assert(minweightidx > j);
5377 minweightidx--;
5378 }
5379 /* j needs to stay the same */
5380 }
5381 assert((*coverweight) > capacity);
5382 assert((*coverweight) - minweight <= capacity);
5383
5384 /* frees temporary memory */
5385 for( j = nsortkeypairs-1; j >= 0; j-- )
5386 SCIPfreeBuffer(scip, &(sortkeypairs[j])); /*lint !e866 */
5387 SCIPfreeBufferArray(scip, &sortkeypairssorted);
5388 SCIPfreeBufferArray(scip, &sortkeypairs);
5389
5390 return SCIP_OKAY;
5391 }
5392
5393 /** converts given initial cover C_init to a feasible set by removing variables in the reverse order in which
5394 * they were chosen to be in C_init:
5395 * non-increasing (1 - x*_j)/a_j, if transformed separation problem was used to find C_init
5396 * non-increasing (1 - x*_j), if modified transformed separation problem was used to find C_init.
5397 * separates lifted extended weight inequalities using sequential up- and down-lifting for this feasible set
5398 * and all subsequent feasible sets.
5399 */
5400 static
getFeasibleSet(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,SCIP_VAR ** vars,int nvars,int ntightened,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Real * solvals,int * covervars,int * noncovervars,int * ncovervars,int * nnoncovervars,SCIP_Longint * coverweight,SCIP_Bool modtransused,SCIP_SOL * sol,SCIP_Bool * cutoff,int * ncuts)5401 SCIP_RETCODE getFeasibleSet(
5402 SCIP* scip, /**< SCIP data structure */
5403 SCIP_CONS* cons, /**< constraint that originates the knapsack problem */
5404 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
5405 SCIP_VAR** vars, /**< variables in knapsack constraint */
5406 int nvars, /**< number of variables in knapsack constraint */
5407 int ntightened, /**< number of variables with tightened upper bound */
5408 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
5409 SCIP_Longint capacity, /**< capacity of knapsack */
5410 SCIP_Real* solvals, /**< solution values of all problem variables */
5411 int* covervars, /**< pointer to store cover variables */
5412 int* noncovervars, /**< pointer to store noncover variables */
5413 int* ncovervars, /**< pointer to store number of cover variables */
5414 int* nnoncovervars, /**< pointer to store number of noncover variables */
5415 SCIP_Longint* coverweight, /**< pointer to store weight of cover */
5416 SCIP_Bool modtransused, /**< TRUE if mod trans sepa prob was used to find cover */
5417 SCIP_SOL* sol, /**< primal SCIP solution to separate, NULL for current LP solution */
5418 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
5419 int* ncuts /**< pointer to add up the number of found cuts */
5420 )
5421 {
5422 SCIP_Real* sortkeys;
5423 int j;
5424 int k;
5425
5426 assert(scip != NULL);
5427 assert(covervars != NULL);
5428 assert(noncovervars != NULL);
5429 assert(ncovervars != NULL);
5430 assert(*ncovervars > 0);
5431 assert(nnoncovervars != NULL);
5432 assert(*nnoncovervars >= 0);
5433 assert(coverweight != NULL);
5434 assert(*coverweight > 0);
5435 assert(*coverweight > capacity);
5436 assert(*ncovervars + *nnoncovervars == nvars - ntightened);
5437 assert(cutoff != NULL);
5438
5439 *cutoff = FALSE;
5440
5441 /* allocates temporary memory */
5442 SCIP_CALL( SCIPallocBufferArray(scip, &sortkeys, *ncovervars) );
5443
5444 /* sorts C in the reverse order in which the variables were chosen to be in the cover, i.e.
5445 * such that (1 - x*_1)/a_1 >= ... >= (1 - x*_|C|)/a_|C|, if trans separation problem was used to find C
5446 * such that (1 - x*_1) >= ... >= (1 - x*_|C|), if modified trans separation problem was used to find C
5447 * note that all variables with x*_j = 1 are in the end of the sorted C, so they will be removed last from C
5448 */
5449 if( modtransused )
5450 {
5451 for( j = 0; j < *ncovervars; j++ )
5452 {
5453 sortkeys[j] = solvals[covervars[j]];
5454 assert(SCIPisFeasGE(scip, sortkeys[j], 0.0));
5455 }
5456 }
5457 else
5458 {
5459 for( j = 0; j < *ncovervars; j++ )
5460 {
5461 sortkeys[j] = (solvals[covervars[j]] - 1.0) / ((SCIP_Real) weights[covervars[j]]);
5462 assert(SCIPisFeasLE(scip, sortkeys[j], 0.0));
5463 }
5464 }
5465 SCIPsortRealInt(sortkeys, covervars, *ncovervars);
5466
5467 /* removes variables from C_init and separates lifted extended weight inequalities using sequential up- and down-lifting;
5468 * in addition to an extended weight inequality this gives cardinality inequalities */
5469 while( *ncovervars >= 2 )
5470 {
5471 /* adds first element of C_init to N\C_init */
5472 noncovervars[*nnoncovervars] = covervars[0];
5473 (*nnoncovervars)++;
5474
5475 /* removes first element from C_init */
5476 (*coverweight) -= weights[covervars[0]];
5477 for( k = 0; k < (*ncovervars) - 1; k++ )
5478 covervars[k] = covervars[k+1];
5479 (*ncovervars)--;
5480
5481 assert(*ncovervars + *nnoncovervars == nvars - ntightened);
5482 if( (*coverweight) <= capacity )
5483 {
5484 SCIP_CALL( separateSequLiftedExtendedWeightInequality(scip, cons, sepa, vars, nvars, ntightened, weights, capacity, solvals,
5485 covervars, noncovervars, *ncovervars, *nnoncovervars, sol, cutoff, ncuts) );
5486 }
5487
5488 /* stop if cover is too large */
5489 if ( *ncovervars >= MAXCOVERSIZEITERLEWI )
5490 break;
5491 }
5492
5493 /* frees temporary memory */
5494 SCIPfreeBufferArray(scip, &sortkeys);
5495
5496 return SCIP_OKAY;
5497 }
5498
5499 /** separates different classes of valid inequalities for the 0-1 knapsack problem */
SCIPseparateKnapsackCuts(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,SCIP_VAR ** vars,int nvars,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_SOL * sol,SCIP_Bool usegubs,SCIP_Bool * cutoff,int * ncuts)5500 SCIP_RETCODE SCIPseparateKnapsackCuts(
5501 SCIP* scip, /**< SCIP data structure */
5502 SCIP_CONS* cons, /**< originating constraint of the knapsack problem, or NULL */
5503 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
5504 SCIP_VAR** vars, /**< variables in knapsack constraint */
5505 int nvars, /**< number of variables in knapsack constraint */
5506 SCIP_Longint* weights, /**< weights of variables in knapsack constraint */
5507 SCIP_Longint capacity, /**< capacity of knapsack */
5508 SCIP_SOL* sol, /**< primal SCIP solution to separate, NULL for current LP solution */
5509 SCIP_Bool usegubs, /**< should GUB information be used for separation? */
5510 SCIP_Bool* cutoff, /**< pointer to store whether a cutoff has been detected */
5511 int* ncuts /**< pointer to add up the number of found cuts */
5512 )
5513 {
5514 SCIP_Real* solvals;
5515 int* covervars;
5516 int* noncovervars;
5517 SCIP_Bool coverfound;
5518 SCIP_Bool fractional;
5519 SCIP_Bool modtransused;
5520 SCIP_Longint coverweight;
5521 int ncovervars;
5522 int nnoncovervars;
5523 int ntightened;
5524
5525 assert(scip != NULL);
5526 assert(capacity >= 0);
5527 assert(cutoff != NULL);
5528 assert(ncuts != NULL);
5529
5530 *cutoff = FALSE;
5531
5532 if( nvars == 0 )
5533 return SCIP_OKAY;
5534
5535 assert(vars != NULL);
5536 assert(nvars > 0);
5537 assert(weights != NULL);
5538
5539 /* increase age of constraint (age is reset to zero, if a cut was found) */
5540 if( cons != NULL )
5541 {
5542 SCIP_CALL( SCIPincConsAge(scip, cons) );
5543 }
5544
5545 /* allocates temporary memory */
5546 SCIP_CALL( SCIPallocBufferArray(scip, &solvals, nvars) );
5547 SCIP_CALL( SCIPallocBufferArray(scip, &covervars, nvars) );
5548 SCIP_CALL( SCIPallocBufferArray(scip, &noncovervars, nvars) );
5549
5550 /* gets solution values of all problem variables */
5551 SCIP_CALL( SCIPgetSolVals(scip, sol, nvars, vars, solvals) );
5552
5553 #ifdef SCIP_DEBUG
5554 {
5555 int i;
5556
5557 SCIPdebugMsg(scip, "separate cuts for knapsack constraint originated by cons <%s>:\n",
5558 cons == NULL ? "-" : SCIPconsGetName(cons));
5559 for( i = 0; i < nvars; ++i )
5560 {
5561 SCIPdebugMsgPrint(scip, "%+" SCIP_LONGINT_FORMAT "<%s>(%g)", weights[i], SCIPvarGetName(vars[i]), solvals[i]);
5562 }
5563 SCIPdebugMsgPrint(scip, " <= %" SCIP_LONGINT_FORMAT "\n", capacity);
5564 }
5565 #endif
5566
5567 /* LMCI1 (lifted minimal cover inequalities using sequential up- and down-lifting) using GUB information
5568 */
5569 if( usegubs )
5570 {
5571 SCIP_GUBSET* gubset;
5572
5573 SCIPdebugMsg(scip, "separate LMCI1-GUB cuts:\n");
5574
5575 /* initializes partion of knapsack variables into nonoverlapping GUB constraints */
5576 SCIP_CALL( GUBsetCreate(scip, &gubset, nvars, weights, capacity) );
5577
5578 /* constructs sophisticated partition of knapsack variables into nonoverlapping GUBs */
5579 SCIP_CALL( GUBsetGetCliquePartition(scip, gubset, vars, solvals) );
5580 assert(gubset->ngubconss <= nvars);
5581
5582 /* gets a most violated initial cover C_init ( sum_{j in C_init} a_j > a_0 ) by using the
5583 * MODIFIED transformed separation problem and taking into account the following fixing:
5584 * j in C_init, if j in N_1 = {j in N : x*_j = 1} and
5585 * j in N\C_init, if j in N_0 = {j in N : x*_j = 0},
5586 * if one exists
5587 */
5588 modtransused = TRUE;
5589 SCIP_CALL( getCover(scip, vars, nvars, weights, capacity, solvals, covervars, noncovervars, &ncovervars,
5590 &nnoncovervars, &coverweight, &coverfound, modtransused, &ntightened, &fractional) );
5591
5592 assert(!coverfound || !fractional || ncovervars + nnoncovervars == nvars - ntightened);
5593
5594 /* if x* is not fractional we stop the separation routine */
5595 if( !fractional )
5596 {
5597 SCIPdebugMsg(scip, " LMCI1-GUB terminated by no variable with fractional LP value.\n");
5598
5599 /* frees memory for GUB set data structure */
5600 GUBsetFree(scip, &gubset);
5601
5602 goto TERMINATE;
5603 }
5604
5605 /* if no cover was found we stop the separation routine for lifted minimal cover inequality */
5606 if( coverfound )
5607 {
5608 /* converts initial cover C_init to a minimal cover C by removing variables in the reverse order in which the
5609 * variables were chosen to be in C_init; note that variables with x*_j = 1 will be removed last
5610 */
5611 SCIP_CALL( makeCoverMinimal(scip, weights, capacity, solvals, covervars, noncovervars, &ncovervars,
5612 &nnoncovervars, &coverweight, modtransused) );
5613
5614 /* only separate with GUB information if we have at least one nontrivial GUB (with more than one variable) */
5615 if( gubset->ngubconss < nvars )
5616 {
5617 /* separates lifted minimal cover inequalities using sequential up- and down-lifting and GUB information */
5618 SCIP_CALL( separateSequLiftedMinimalCoverInequality(scip, cons, sepa, vars, nvars, ntightened, weights, capacity,
5619 solvals, covervars, noncovervars, ncovervars, nnoncovervars, sol, gubset, cutoff, ncuts) );
5620 }
5621 else
5622 {
5623 /* separates lifted minimal cover inequalities using sequential up- and down-lifting, but do not use trivial
5624 * GUB information
5625 */
5626 SCIP_CALL( separateSequLiftedMinimalCoverInequality(scip, cons, sepa, vars, nvars, ntightened, weights, capacity,
5627 solvals, covervars, noncovervars, ncovervars, nnoncovervars, sol, NULL, cutoff, ncuts) );
5628 }
5629 }
5630
5631 /* frees memory for GUB set data structure */
5632 GUBsetFree(scip, &gubset);
5633 }
5634 else
5635 {
5636 /* LMCI1 (lifted minimal cover inequalities using sequential up- and down-lifting)
5637 * (and LMCI2 (lifted minimal cover inequalities using superadditive up-lifting))
5638 */
5639
5640 /* gets a most violated initial cover C_init ( sum_{j in C_init} a_j > a_0 ) by using the
5641 * MODIFIED transformed separation problem and taking into account the following fixing:
5642 * j in C_init, if j in N_1 = {j in N : x*_j = 1} and
5643 * j in N\C_init, if j in N_0 = {j in N : x*_j = 0},
5644 * if one exists
5645 */
5646 SCIPdebugMsg(scip, "separate LMCI1 cuts:\n");
5647 modtransused = TRUE;
5648 SCIP_CALL( getCover(scip, vars, nvars, weights, capacity, solvals, covervars, noncovervars, &ncovervars,
5649 &nnoncovervars, &coverweight, &coverfound, modtransused, &ntightened, &fractional) );
5650 assert(!coverfound || !fractional || ncovervars + nnoncovervars == nvars - ntightened);
5651
5652 /* if x* is not fractional we stop the separation routine */
5653 if( !fractional )
5654 goto TERMINATE;
5655
5656 /* if no cover was found we stop the separation routine for lifted minimal cover inequality */
5657 if( coverfound )
5658 {
5659 /* converts initial cover C_init to a minimal cover C by removing variables in the reverse order in which the
5660 * variables were chosen to be in C_init; note that variables with x*_j = 1 will be removed last
5661 */
5662 SCIP_CALL( makeCoverMinimal(scip, weights, capacity, solvals, covervars, noncovervars, &ncovervars,
5663 &nnoncovervars, &coverweight, modtransused) );
5664
5665 /* separates lifted minimal cover inequalities using sequential up- and down-lifting */
5666 SCIP_CALL( separateSequLiftedMinimalCoverInequality(scip, cons, sepa, vars, nvars, ntightened, weights, capacity,
5667 solvals, covervars, noncovervars, ncovervars, nnoncovervars, sol, NULL, cutoff, ncuts) );
5668
5669 if( USESUPADDLIFT ) /*lint !e506 !e774*/
5670 {
5671 SCIPdebugMsg(scip, "separate LMCI2 cuts:\n");
5672 /* separates lifted minimal cover inequalities using superadditive up-lifting */
5673 SCIP_CALL( separateSupLiftedMinimalCoverInequality(scip, cons, sepa, vars, nvars, ntightened, weights, capacity,
5674 solvals, covervars, noncovervars, ncovervars, nnoncovervars, coverweight, sol, cutoff, ncuts) );
5675 }
5676 }
5677 }
5678
5679 /* LEWI (lifted extended weight inequalities using sequential up- and down-lifting) */
5680 if ( ! (*cutoff) )
5681 {
5682 /* gets a most violated initial cover C_init ( sum_{j in C_init} a_j > a_0 ) by using the
5683 * transformed separation problem and taking into account the following fixing:
5684 * j in C_init, if j in N_1 = {j in N : x*_j = 1} and
5685 * j in N\C_init, if j in N_0 = {j in N : x*_j = 0},
5686 * if one exists
5687 */
5688 SCIPdebugMsg(scip, "separate LEWI cuts:\n");
5689 modtransused = FALSE;
5690 SCIP_CALL( getCover(scip, vars, nvars, weights, capacity, solvals, covervars, noncovervars, &ncovervars,
5691 &nnoncovervars, &coverweight, &coverfound, modtransused, &ntightened, &fractional) );
5692 assert(fractional);
5693 assert(!coverfound || ncovervars + nnoncovervars == nvars - ntightened);
5694
5695 /* if no cover was found we stop the separation routine */
5696 if( coverfound )
5697 {
5698 /* converts initial cover C_init to a feasible set by removing variables in the reverse order in which
5699 * they were chosen to be in C_init and separates lifted extended weight inequalities using sequential
5700 * up- and down-lifting for this feasible set and all subsequent feasible sets.
5701 */
5702 SCIP_CALL( getFeasibleSet(scip, cons, sepa, vars, nvars, ntightened, weights, capacity, solvals, covervars, noncovervars,
5703 &ncovervars, &nnoncovervars, &coverweight, modtransused, sol, cutoff, ncuts) );
5704 }
5705 }
5706
5707 TERMINATE:
5708 /* frees temporary memory */
5709 SCIPfreeBufferArray(scip, &noncovervars);
5710 SCIPfreeBufferArray(scip, &covervars);
5711 SCIPfreeBufferArray(scip, &solvals);
5712
5713 return SCIP_OKAY;
5714 }
5715
5716 /* relaxes given general linear constraint into a knapsack constraint and separates lifted knapsack cover inequalities */
SCIPseparateRelaxedKnapsack(SCIP * scip,SCIP_CONS * cons,SCIP_SEPA * sepa,int nknapvars,SCIP_VAR ** knapvars,SCIP_Real * knapvals,SCIP_Real valscale,SCIP_Real rhs,SCIP_SOL * sol,SCIP_Bool * cutoff,int * ncuts)5717 SCIP_RETCODE SCIPseparateRelaxedKnapsack(
5718 SCIP* scip, /**< SCIP data structure */
5719 SCIP_CONS* cons, /**< originating constraint of the knapsack problem, or NULL */
5720 SCIP_SEPA* sepa, /**< originating separator of the knapsack problem, or NULL */
5721 int nknapvars, /**< number of variables in the continuous knapsack constraint */
5722 SCIP_VAR** knapvars, /**< variables in the continuous knapsack constraint */
5723 SCIP_Real* knapvals, /**< coefficients of the variables in the continuous knapsack constraint */
5724 SCIP_Real valscale, /**< -1.0 if lhs of row is used as rhs of c. k. constraint, +1.0 otherwise */
5725 SCIP_Real rhs, /**< right hand side of the continuous knapsack constraint */
5726 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
5727 SCIP_Bool* cutoff, /**< pointer to store whether a cutoff was found */
5728 int* ncuts /**< pointer to add up the number of found cuts */
5729 )
5730 {
5731 SCIP_VAR** binvars;
5732 SCIP_VAR** consvars;
5733 SCIP_Real* binvals;
5734 SCIP_Longint* consvals;
5735 SCIP_Longint minact;
5736 SCIP_Longint maxact;
5737 SCIP_Real intscalar;
5738 SCIP_Bool success;
5739 int nbinvars;
5740 int nconsvars;
5741 int i;
5742
5743 int* tmpindices;
5744 int tmp;
5745 SCIP_CONSHDLR* conshdlr;
5746 SCIP_CONSHDLRDATA* conshdlrdata;
5747 SCIP_Bool noknapsackconshdlr;
5748 SCIP_Bool usegubs;
5749
5750 assert(nknapvars > 0);
5751 assert(knapvars != NULL);
5752 assert(cutoff != NULL);
5753
5754 tmpindices = NULL;
5755
5756 SCIPdebugMsg(scip, "separate linear constraint <%s> relaxed to knapsack\n", cons != NULL ? SCIPconsGetName(cons) : "-");
5757 SCIPdebug( if( cons != NULL ) { SCIPdebugPrintCons(scip, cons, NULL); } );
5758
5759 binvars = SCIPgetVars(scip);
5760
5761 /* all variables which are of integral type can be potentially of binary type; this can be checked via the method SCIPvarIsBinary(var) */
5762 nbinvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
5763
5764 *cutoff = FALSE;
5765
5766 if( nbinvars == 0 )
5767 return SCIP_OKAY;
5768
5769 /* set up data structures */
5770 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nbinvars) );
5771 SCIP_CALL( SCIPallocBufferArray(scip, &consvals, nbinvars) );
5772
5773 /* get conshdlrdata to use cleared memory */
5774 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
5775 if( conshdlr == NULL )
5776 {
5777 noknapsackconshdlr = TRUE;
5778 usegubs = DEFAULT_USEGUBS;
5779
5780 SCIP_CALL( SCIPallocBufferArray(scip, &binvals, nbinvars) );
5781 BMSclearMemoryArray(binvals, nbinvars);
5782 }
5783 else
5784 {
5785 noknapsackconshdlr = FALSE;
5786 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5787 assert(conshdlrdata != NULL);
5788 usegubs = conshdlrdata->usegubs;
5789
5790 SCIP_CALL( SCIPallocBufferArray(scip, &tmpindices, nknapvars) );
5791
5792 /* increase array size to avoid an endless loop in the next block; this might happen if continuous variables
5793 * change their types to SCIP_VARTYPE_BINARY during presolving
5794 */
5795 if( conshdlrdata->reals1size == 0 )
5796 {
5797 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->reals1, conshdlrdata->reals1size, 1) );
5798 conshdlrdata->reals1size = 1;
5799 conshdlrdata->reals1[0] = 0.0;
5800 }
5801
5802 assert(conshdlrdata->reals1size > 0);
5803
5804 /* next if condition should normally not be true, because it means that presolving has created more binary
5805 * variables than binary + integer variables existed at the constraint initialization method, but for example if you would
5806 * transform all integers into their binary representation then it maybe happens
5807 */
5808 if( conshdlrdata->reals1size < nbinvars )
5809 {
5810 int oldsize = conshdlrdata->reals1size;
5811
5812 conshdlrdata->reals1size = nbinvars;
5813 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->reals1, oldsize, conshdlrdata->reals1size) );
5814 BMSclearMemoryArray(&(conshdlrdata->reals1[oldsize]), conshdlrdata->reals1size - oldsize); /*lint !e866 */
5815 }
5816 binvals = conshdlrdata->reals1;
5817
5818 /* check for cleared array, all entries have to be zero */
5819 #ifndef NDEBUG
5820 for( tmp = nbinvars - 1; tmp >= 0; --tmp )
5821 {
5822 assert(binvals[tmp] == 0);
5823 }
5824 #endif
5825 }
5826
5827 tmp = 0;
5828
5829 /* relax continuous knapsack constraint:
5830 * 1. make all variables binary:
5831 * if x_j is continuous or integer variable substitute:
5832 * - a_j < 0: x_j = lb or x_j = b*z + d with variable lower bound b*z + d with binary variable z
5833 * - a_j > 0: x_j = ub or x_j = b*z + d with variable upper bound b*z + d with binary variable z
5834 * 2. convert coefficients of all variables to positive integers:
5835 * - scale all coefficients a_j to a~_j integral
5836 * - substitute x~_j = 1 - x_j if a~_j < 0
5837 */
5838
5839 /* replace integer and continuous variables with binary variables */
5840 for( i = 0; i < nknapvars; i++ )
5841 {
5842 SCIP_VAR* var;
5843
5844 var = knapvars[i];
5845
5846 if( SCIPvarIsBinary(var) && SCIPvarIsActive(var) )
5847 {
5848 SCIP_Real solval;
5849 assert(0 <= SCIPvarGetProbindex(var) && SCIPvarGetProbindex(var) < nbinvars);
5850
5851 solval = SCIPgetSolVal(scip, sol, var);
5852
5853 /* knapsack relaxation assumes solution values between 0.0 and 1.0 for binary variables */
5854 if( SCIPisFeasLT(scip, solval, 0.0 )
5855 || SCIPisFeasGT(scip, solval, 1.0) )
5856 {
5857 SCIPdebugMsg(scip, "Solution value %.15g <%s> outside domain [0.0, 1.0]\n",
5858 solval, SCIPvarGetName(var));
5859 goto TERMINATE;
5860 }
5861
5862 binvals[SCIPvarGetProbindex(var)] += valscale * knapvals[i];
5863 if( !noknapsackconshdlr )
5864 {
5865 assert(tmpindices != NULL);
5866
5867 tmpindices[tmp] = SCIPvarGetProbindex(var);
5868 ++tmp;
5869 }
5870 SCIPdebugMsg(scip, " -> binary variable %+.15g<%s>(%.15g)\n", valscale * knapvals[i], SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var));
5871 }
5872 else if( valscale * knapvals[i] > 0.0 )
5873 {
5874 SCIP_VAR** zvlb;
5875 SCIP_Real* bvlb;
5876 SCIP_Real* dvlb;
5877 SCIP_Real bestlbsol;
5878 int bestlbtype;
5879 int nvlb;
5880 int j;
5881
5882 /* a_j > 0: substitution with lb or vlb */
5883 nvlb = SCIPvarGetNVlbs(var);
5884 zvlb = SCIPvarGetVlbVars(var);
5885 bvlb = SCIPvarGetVlbCoefs(var);
5886 dvlb = SCIPvarGetVlbConstants(var);
5887
5888 /* search for lb or vlb with maximal bound value */
5889 bestlbsol = SCIPvarGetLbGlobal(var);
5890 bestlbtype = -1;
5891 for( j = 0; j < nvlb; j++ )
5892 {
5893 /* use only numerical stable vlb with binary variable z */
5894 if( SCIPvarIsBinary(zvlb[j]) && SCIPvarIsActive(zvlb[j]) && REALABS(bvlb[j]) <= MAXABSVBCOEF )
5895 {
5896 SCIP_Real vlbsol;
5897
5898 if( (bvlb[j] >= 0.0 && SCIPisGT(scip, bvlb[j] * SCIPvarGetLbLocal(zvlb[j]) + dvlb[j], SCIPvarGetUbLocal(var))) ||
5899 (bvlb[j] <= 0.0 && SCIPisGT(scip, bvlb[j] * SCIPvarGetUbLocal(zvlb[j]) + dvlb[j], SCIPvarGetUbLocal(var))) )
5900 {
5901 *cutoff = TRUE;
5902 SCIPdebugMsg(scip, "variable bound <%s>[%g,%g] >= %g<%s>[%g,%g] + %g implies local cutoff\n",
5903 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var),
5904 bvlb[j], SCIPvarGetName(zvlb[j]), SCIPvarGetLbLocal(zvlb[j]), SCIPvarGetUbLocal(zvlb[j]), dvlb[j]);
5905 goto TERMINATE;
5906 }
5907
5908 assert(0 <= SCIPvarGetProbindex(zvlb[j]) && SCIPvarGetProbindex(zvlb[j]) < nbinvars);
5909 vlbsol = bvlb[j] * SCIPgetSolVal(scip, sol, zvlb[j]) + dvlb[j];
5910 if( SCIPisGE(scip, vlbsol, bestlbsol) )
5911 {
5912 bestlbsol = vlbsol;
5913 bestlbtype = j;
5914 }
5915 }
5916 }
5917
5918 /* if no lb or vlb with binary variable was found, we have to abort */
5919 if( SCIPisInfinity(scip, -bestlbsol) )
5920 goto TERMINATE;
5921
5922 if( bestlbtype == -1 )
5923 {
5924 rhs -= valscale * knapvals[i] * bestlbsol;
5925 SCIPdebugMsg(scip, " -> non-binary variable %+.15g<%s>(%.15g) replaced with lower bound %.15g (rhs=%.15g)\n",
5926 valscale * knapvals[i], SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var), SCIPvarGetLbGlobal(var), rhs);
5927 }
5928 else
5929 {
5930 assert(0 <= SCIPvarGetProbindex(zvlb[bestlbtype]) && SCIPvarGetProbindex(zvlb[bestlbtype]) < nbinvars);
5931 rhs -= valscale * knapvals[i] * dvlb[bestlbtype];
5932 binvals[SCIPvarGetProbindex(zvlb[bestlbtype])] += valscale * knapvals[i] * bvlb[bestlbtype];
5933
5934 if( SCIPisInfinity(scip, REALABS(binvals[SCIPvarGetProbindex(zvlb[bestlbtype])])) )
5935 goto TERMINATE;
5936
5937 if( !noknapsackconshdlr )
5938 {
5939 assert(tmpindices != NULL);
5940
5941 tmpindices[tmp] = SCIPvarGetProbindex(zvlb[bestlbtype]);
5942 ++tmp;
5943 }
5944 SCIPdebugMsg(scip, " -> non-binary variable %+.15g<%s>(%.15g) replaced with variable lower bound %+.15g<%s>(%.15g) %+.15g (rhs=%.15g)\n",
5945 valscale * knapvals[i], SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var),
5946 bvlb[bestlbtype], SCIPvarGetName(zvlb[bestlbtype]),
5947 SCIPgetSolVal(scip, sol, zvlb[bestlbtype]), dvlb[bestlbtype], rhs);
5948 }
5949 }
5950 else
5951 {
5952 SCIP_VAR** zvub;
5953 SCIP_Real* bvub;
5954 SCIP_Real* dvub;
5955 SCIP_Real bestubsol;
5956 int bestubtype;
5957 int nvub;
5958 int j;
5959
5960 assert(valscale * knapvals[i] < 0.0);
5961
5962 /* a_j < 0: substitution with ub or vub */
5963 nvub = SCIPvarGetNVubs(var);
5964 zvub = SCIPvarGetVubVars(var);
5965 bvub = SCIPvarGetVubCoefs(var);
5966 dvub = SCIPvarGetVubConstants(var);
5967
5968 /* search for ub or vub with minimal bound value */
5969 bestubsol = SCIPvarGetUbGlobal(var);
5970 bestubtype = -1;
5971 for( j = 0; j < nvub; j++ )
5972 {
5973 /* use only numerical stable vub with active binary variable z */
5974 if( SCIPvarIsBinary(zvub[j]) && SCIPvarIsActive(zvub[j]) && REALABS(bvub[j]) <= MAXABSVBCOEF )
5975 {
5976 SCIP_Real vubsol;
5977
5978 if( (bvub[j] >= 0.0 && SCIPisLT(scip, bvub[j] * SCIPvarGetUbLocal(zvub[j]) + dvub[j], SCIPvarGetLbLocal(var))) ||
5979 (bvub[j] <= 0.0 && SCIPisLT(scip, bvub[j] * SCIPvarGetLbLocal(zvub[j]) + dvub[j], SCIPvarGetLbLocal(var))) )
5980 {
5981 *cutoff = TRUE;
5982 SCIPdebugMsg(scip, "variable bound <%s>[%g,%g] <= %g<%s>[%g,%g] + %g implies local cutoff\n",
5983 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var),
5984 bvub[j], SCIPvarGetName(zvub[j]), SCIPvarGetLbLocal(zvub[j]), SCIPvarGetUbLocal(zvub[j]), dvub[j]);
5985 goto TERMINATE;
5986 }
5987
5988 assert(0 <= SCIPvarGetProbindex(zvub[j]) && SCIPvarGetProbindex(zvub[j]) < nbinvars);
5989 vubsol = bvub[j] * SCIPgetSolVal(scip, sol, zvub[j]) + dvub[j];
5990 if( SCIPisLE(scip, vubsol, bestubsol) )
5991 {
5992 bestubsol = vubsol;
5993 bestubtype = j;
5994 }
5995 }
5996 }
5997
5998 /* if no ub or vub with binary variable was found, we have to abort */
5999 if( SCIPisInfinity(scip, bestubsol) )
6000 goto TERMINATE;
6001
6002 if( bestubtype == -1 )
6003 {
6004 rhs -= valscale * knapvals[i] * bestubsol;
6005 SCIPdebugMsg(scip, " -> non-binary variable %+.15g<%s>(%.15g) replaced with upper bound %.15g (rhs=%.15g)\n",
6006 valscale * knapvals[i], SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var), SCIPvarGetUbGlobal(var), rhs);
6007 }
6008 else
6009 {
6010 assert(0 <= SCIPvarGetProbindex(zvub[bestubtype]) && SCIPvarGetProbindex(zvub[bestubtype]) < nbinvars);
6011 rhs -= valscale * knapvals[i] * dvub[bestubtype];
6012 binvals[SCIPvarGetProbindex(zvub[bestubtype])] += valscale * knapvals[i] * bvub[bestubtype];
6013
6014 if( SCIPisInfinity(scip, REALABS(binvals[SCIPvarGetProbindex(zvub[bestubtype])])) )
6015 goto TERMINATE;
6016
6017 if( !noknapsackconshdlr )
6018 {
6019 assert(tmpindices != NULL);
6020
6021 tmpindices[tmp] = SCIPvarGetProbindex(zvub[bestubtype]);
6022 ++tmp;
6023 }
6024 SCIPdebugMsg(scip, " -> non-binary variable %+.15g<%s>(%.15g) replaced with variable upper bound %+.15g<%s>(%.15g) %+.15g (rhs=%.15g)\n",
6025 valscale * knapvals[i], SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var),
6026 bvub[bestubtype], SCIPvarGetName(zvub[bestubtype]),
6027 SCIPgetSolVal(scip, sol, zvub[bestubtype]), dvub[bestubtype], rhs);
6028 }
6029 }
6030 }
6031
6032 /* convert coefficients of all (now binary) variables to positive integers:
6033 * - make all coefficients integral
6034 * - make all coefficients positive (substitute negated variable)
6035 */
6036 nconsvars = 0;
6037
6038 /* calculate scalar which makes all coefficients integral in relative allowed difference in between
6039 * -SCIPepsilon(scip) and KNAPSACKRELAX_MAXDELTA
6040 */
6041 SCIP_CALL( SCIPcalcIntegralScalar(binvals, nbinvars, -SCIPepsilon(scip), KNAPSACKRELAX_MAXDELTA,
6042 KNAPSACKRELAX_MAXDNOM, KNAPSACKRELAX_MAXSCALE, &intscalar, &success) );
6043 SCIPdebugMsg(scip, " -> intscalar = %.15g\n", intscalar);
6044
6045 /* if coefficients cannot be made integral, we have to use a scalar of 1.0 and only round fractional coefficients down */
6046 if( !success )
6047 intscalar = 1.0;
6048
6049 /* make all coefficients integral and positive:
6050 * - scale a~_j = a_j * intscalar
6051 * - substitute x~_j = 1 - x_j if a~_j < 0
6052 */
6053 rhs = rhs * intscalar;
6054
6055 SCIPdebugMsg(scip, " -> rhs = %.15g\n", rhs);
6056 minact = 0;
6057 maxact = 0;
6058 for( i = 0; i < nbinvars; i++ )
6059 {
6060 SCIP_VAR* var;
6061 SCIP_Longint val;
6062
6063 val = (SCIP_Longint)SCIPfloor(scip, binvals[i] * intscalar);
6064 if( val == 0 )
6065 continue;
6066
6067 if( val > 0 )
6068 {
6069 var = binvars[i];
6070 SCIPdebugMsg(scip, " -> positive scaled binary variable %+" SCIP_LONGINT_FORMAT "<%s> (unscaled %.15g): not changed (rhs=%.15g)\n",
6071 val, SCIPvarGetName(var), binvals[i], rhs);
6072 }
6073 else
6074 {
6075 assert(val < 0);
6076
6077 SCIP_CALL( SCIPgetNegatedVar(scip, binvars[i], &var) );
6078 val = -val; /*lint !e2704*/
6079 rhs += val;
6080 SCIPdebugMsg(scip, " -> negative scaled binary variable %+" SCIP_LONGINT_FORMAT "<%s> (unscaled %.15g): substituted by (1 - <%s>) (rhs=%.15g)\n",
6081 -val, SCIPvarGetName(binvars[i]), binvals[i], SCIPvarGetName(var), rhs);
6082 }
6083
6084 if( SCIPvarGetLbLocal(var) > 0.5 )
6085 minact += val;
6086 if( SCIPvarGetUbLocal(var) > 0.5 )
6087 maxact += val;
6088 consvals[nconsvars] = val;
6089 consvars[nconsvars] = var;
6090 nconsvars++;
6091 }
6092
6093 if( nconsvars > 0 )
6094 {
6095 SCIP_Longint capacity;
6096
6097 assert(consvars != NULL);
6098 assert(consvals != NULL);
6099 capacity = (SCIP_Longint)SCIPfeasFloor(scip, rhs);
6100
6101 #ifdef SCIP_DEBUG
6102 {
6103 SCIP_Real act;
6104
6105 SCIPdebugMsg(scip, " -> linear constraint <%s> relaxed to knapsack:", cons != NULL ? SCIPconsGetName(cons) : "-");
6106 act = 0.0;
6107 for( i = 0; i < nconsvars; ++i )
6108 {
6109 SCIPdebugMsgPrint(scip, " %+" SCIP_LONGINT_FORMAT "<%s>(%.15g)", consvals[i], SCIPvarGetName(consvars[i]),
6110 SCIPgetSolVal(scip, sol, consvars[i]));
6111 act += consvals[i] * SCIPgetSolVal(scip, sol, consvars[i]);
6112 }
6113 SCIPdebugMsgPrint(scip, " <= %" SCIP_LONGINT_FORMAT " (%.15g) [act: %.15g, min: %" SCIP_LONGINT_FORMAT " max: %" SCIP_LONGINT_FORMAT "]\n",
6114 capacity, rhs, act, minact, maxact);
6115 }
6116 #endif
6117
6118 if( minact > capacity )
6119 {
6120 SCIPdebugMsg(scip, "minactivity of knapsack relaxation implies local cutoff\n");
6121 *cutoff = TRUE;
6122 goto TERMINATE;
6123 }
6124
6125 if( maxact > capacity )
6126 {
6127 /* separate lifted cut from relaxed knapsack constraint */
6128 SCIP_CALL( SCIPseparateKnapsackCuts(scip, cons, sepa, consvars, nconsvars, consvals, capacity, sol, usegubs, cutoff, ncuts) );
6129 }
6130 }
6131
6132 TERMINATE:
6133 /* free data structures */
6134 if( noknapsackconshdlr)
6135 {
6136 SCIPfreeBufferArray(scip, &binvals);
6137 }
6138 else
6139 {
6140 /* clear binvals */
6141 for( --tmp; tmp >= 0; --tmp)
6142 {
6143 assert(tmpindices != NULL);
6144 binvals[tmpindices[tmp]] = 0;
6145 }
6146 SCIPfreeBufferArray(scip, &tmpindices);
6147 }
6148 SCIPfreeBufferArray(scip, &consvals);
6149 SCIPfreeBufferArray(scip, &consvars);
6150
6151 return SCIP_OKAY;
6152 }
6153
6154 /** separates given knapsack constraint */
6155 static
separateCons(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Bool sepacuts,SCIP_Bool usegubs,SCIP_Bool * cutoff,int * ncuts)6156 SCIP_RETCODE separateCons(
6157 SCIP* scip, /**< SCIP data structure */
6158 SCIP_CONS* cons, /**< knapsack constraint */
6159 SCIP_SOL* sol, /**< primal SCIP solution, NULL for current LP solution */
6160 SCIP_Bool sepacuts, /**< should knapsack cuts be separated? */
6161 SCIP_Bool usegubs, /**< should GUB information be used for separation? */
6162 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
6163 int* ncuts /**< pointer to add up the number of found cuts */
6164 )
6165 {
6166 SCIP_CONSDATA* consdata;
6167 SCIP_Bool violated;
6168
6169 assert(ncuts != NULL);
6170 assert(cutoff != NULL);
6171 *cutoff = FALSE;
6172
6173 consdata = SCIPconsGetData(cons);
6174 assert(consdata != NULL);
6175
6176 SCIPdebugMsg(scip, "separating knapsack constraint <%s>\n", SCIPconsGetName(cons));
6177
6178 /* check knapsack constraint itself for feasibility */
6179 SCIP_CALL( checkCons(scip, cons, sol, (sol != NULL), FALSE, &violated) );
6180
6181 if( violated )
6182 {
6183 /* add knapsack constraint as LP row to the LP */
6184 SCIP_CALL( addRelaxation(scip, cons, cutoff) );
6185 (*ncuts)++;
6186 }
6187 else if( sepacuts )
6188 {
6189 SCIP_CALL( SCIPseparateKnapsackCuts(scip, cons, NULL, consdata->vars, consdata->nvars, consdata->weights,
6190 consdata->capacity, sol, usegubs, cutoff, ncuts) );
6191 }
6192
6193 return SCIP_OKAY;
6194 }
6195
6196 /** adds coefficient to constraint data */
6197 static
addCoef(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Longint weight)6198 SCIP_RETCODE addCoef(
6199 SCIP* scip, /**< SCIP data structure */
6200 SCIP_CONS* cons, /**< knapsack constraint */
6201 SCIP_VAR* var, /**< variable to add to knapsack */
6202 SCIP_Longint weight /**< weight of variable in knapsack */
6203 )
6204 {
6205 SCIP_CONSDATA* consdata;
6206
6207 consdata = SCIPconsGetData(cons);
6208 assert(consdata != NULL);
6209 assert(SCIPvarIsBinary(var));
6210 assert(weight > 0);
6211
6212 /* add the new coefficient to the LP row */
6213 if( consdata->row != NULL )
6214 {
6215 SCIP_CALL( SCIPaddVarToRow(scip, consdata->row, var, (SCIP_Real)weight) );
6216 }
6217
6218 /* check for fixed variable */
6219 if( SCIPvarGetLbGlobal(var) > 0.5 )
6220 {
6221 /* variable is fixed to one: reduce capacity */
6222 consdata->capacity -= weight;
6223 }
6224 else if( SCIPvarGetUbGlobal(var) > 0.5 )
6225 {
6226 SCIP_Bool negated;
6227
6228 /* get binary representative of variable */
6229 SCIP_CALL( SCIPgetBinvarRepresentative(scip, var, &var, &negated) );
6230
6231 /* insert coefficient */
6232 SCIP_CALL( consdataEnsureVarsSize(scip, consdata, consdata->nvars+1, SCIPconsIsTransformed(cons)) );
6233 consdata->vars[consdata->nvars] = var;
6234 consdata->weights[consdata->nvars] = weight;
6235 consdata->nvars++;
6236
6237 /* capture variable */
6238 SCIP_CALL( SCIPcaptureVar(scip, var) );
6239
6240 /* install the rounding locks of variable */
6241 SCIP_CALL( lockRounding(scip, cons, var) );
6242
6243 /* catch events */
6244 if( SCIPconsIsTransformed(cons) )
6245 {
6246 SCIP_CONSHDLRDATA* conshdlrdata;
6247
6248 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
6249 assert(conshdlrdata != NULL);
6250 SCIP_CALL( eventdataCreate(scip, &consdata->eventdata[consdata->nvars-1], cons, weight) );
6251 SCIP_CALL( SCIPcatchVarEvent(scip, var, EVENTTYPE_KNAPSACK,
6252 conshdlrdata->eventhdlr, consdata->eventdata[consdata->nvars-1],
6253 &consdata->eventdata[consdata->nvars-1]->filterpos) );
6254
6255 if( !consdata->existmultaggr && SCIPvarGetStatus(SCIPvarGetProbvar(var)) == SCIP_VARSTATUS_MULTAGGR )
6256 consdata->existmultaggr = TRUE;
6257
6258 /* mark constraint to be propagated and presolved */
6259 SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
6260 consdata->presolvedtiming = 0;
6261 consdata->cliquesadded = FALSE; /* new coefficient might lead to larger cliques */
6262 }
6263
6264 /* update weight sums */
6265 updateWeightSums(consdata, var, weight);
6266
6267 consdata->sorted = FALSE;
6268 consdata->cliquepartitioned = FALSE;
6269 consdata->negcliquepartitioned = FALSE;
6270 consdata->merged = FALSE;
6271 }
6272
6273 return SCIP_OKAY;
6274 }
6275
6276 /** deletes coefficient at given position from constraint data */
6277 static
delCoefPos(SCIP * scip,SCIP_CONS * cons,int pos)6278 SCIP_RETCODE delCoefPos(
6279 SCIP* scip, /**< SCIP data structure */
6280 SCIP_CONS* cons, /**< knapsack constraint */
6281 int pos /**< position of coefficient to delete */
6282 )
6283 {
6284 SCIP_CONSDATA* consdata;
6285 SCIP_VAR* var;
6286
6287 consdata = SCIPconsGetData(cons);
6288 assert(consdata != NULL);
6289 assert(0 <= pos && pos < consdata->nvars);
6290
6291 var = consdata->vars[pos];
6292 assert(var != NULL);
6293 assert(SCIPconsIsTransformed(cons) == SCIPvarIsTransformed(var));
6294
6295 /* delete the coefficient from the LP row */
6296 if( consdata->row != NULL )
6297 {
6298 SCIP_CALL( SCIPaddVarToRow(scip, consdata->row, var, -(SCIP_Real)consdata->weights[pos]) );
6299 }
6300
6301 /* remove the rounding locks of variable */
6302 SCIP_CALL( unlockRounding(scip, cons, var) );
6303
6304 /* drop events and mark constraint to be propagated and presolved */
6305 if( SCIPconsIsTransformed(cons) )
6306 {
6307 SCIP_CONSHDLRDATA* conshdlrdata;
6308
6309 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
6310 assert(conshdlrdata != NULL);
6311 SCIP_CALL( SCIPdropVarEvent(scip, var, EVENTTYPE_KNAPSACK,
6312 conshdlrdata->eventhdlr, consdata->eventdata[pos], consdata->eventdata[pos]->filterpos) );
6313 SCIP_CALL( eventdataFree(scip, &consdata->eventdata[pos]) );
6314
6315 SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
6316 consdata->presolvedtiming = 0;
6317 consdata->sorted = (consdata->sorted && pos == consdata->nvars - 1);
6318 }
6319
6320 /* decrease weight sums */
6321 updateWeightSums(consdata, var, -consdata->weights[pos]);
6322
6323 /* move the last variable to the free slot */
6324 consdata->vars[pos] = consdata->vars[consdata->nvars-1];
6325 consdata->weights[pos] = consdata->weights[consdata->nvars-1];
6326 if( consdata->eventdata != NULL )
6327 consdata->eventdata[pos] = consdata->eventdata[consdata->nvars-1];
6328
6329 /* release variable */
6330 SCIP_CALL( SCIPreleaseVar(scip, &var) );
6331
6332 /* try to use old clique partitions */
6333 if( consdata->cliquepartitioned )
6334 {
6335 assert(consdata->cliquepartition != NULL);
6336 /* if the clique number is equal to the number of variables we have only cliques with one element, so we don't
6337 * change the clique number */
6338 if( consdata->cliquepartition[consdata->nvars - 1] != consdata->nvars - 1 )
6339 {
6340 int oldcliqenum;
6341
6342 oldcliqenum = consdata->cliquepartition[pos];
6343 consdata->cliquepartition[pos] = consdata->cliquepartition[consdata->nvars-1];
6344
6345 /* the following if and else cases assure that we have increasing clique numbers */
6346 if( consdata->cliquepartition[pos] > pos )
6347 consdata->cliquepartitioned = FALSE; /* recalculate the clique partition after a coefficient was removed */
6348 else
6349 {
6350 int i;
6351 int cliquenumbefore;
6352
6353 /* if the old clique number was greater than the new one we have to check that before a bigger clique number
6354 * occurs the same as the old one is still in the cliquepartition */
6355 if( oldcliqenum > consdata->cliquepartition[pos] )
6356 {
6357 for( i = 0; i < consdata->nvars; ++i )
6358 if( oldcliqenum == consdata->cliquepartition[i] )
6359 break;
6360 else if( oldcliqenum < consdata->cliquepartition[i] )
6361 {
6362 consdata->cliquepartitioned = FALSE; /* recalculate the clique partition after a coefficient was removed */
6363 break;
6364 }
6365 /* if we reached the end in the for loop, it means we have deleted the last element of the clique with
6366 * the biggest index, so decrease the number of cliques
6367 */
6368 if( i == consdata->nvars )
6369 --(consdata->ncliques);
6370 }
6371 /* if the old clique number was smaller than the new one we have to check the front for an element with
6372 * clique number minus 1 */
6373 else if( oldcliqenum < consdata->cliquepartition[pos] )
6374 {
6375 cliquenumbefore = consdata->cliquepartition[pos] - 1;
6376 for( i = pos - 1; i >= 0 && i >= cliquenumbefore && consdata->cliquepartition[i] < cliquenumbefore; --i ); /*lint !e722*/
6377
6378 if( i < cliquenumbefore )
6379 consdata->cliquepartitioned = FALSE; /* recalculate the clique partition after a coefficient was removed */
6380 }
6381 /* if we deleted the last element of the clique with biggest index, we have to decrease the clique number */
6382 else if( pos == consdata->nvars - 1)
6383 {
6384 cliquenumbefore = consdata->cliquepartition[pos];
6385 for( i = pos - 1; i >= 0 && i >= cliquenumbefore && consdata->cliquepartition[i] < cliquenumbefore; --i ); /*lint !e722*/
6386
6387 if( i < cliquenumbefore )
6388 --(consdata->ncliques);
6389 }
6390 /* if the old clique number is equal to the new one the cliquepartition should be ok */
6391 }
6392 }
6393 else
6394 --(consdata->ncliques);
6395 }
6396
6397 if( consdata->negcliquepartitioned )
6398 {
6399 assert(consdata->negcliquepartition != NULL);
6400 /* if the clique number is equal to the number of variables we have only cliques with one element, so we don't
6401 * change the clique number */
6402 if( consdata->negcliquepartition[consdata->nvars-1] != consdata->nvars - 1 )
6403 {
6404 int oldcliqenum;
6405
6406 oldcliqenum = consdata->negcliquepartition[pos];
6407 consdata->negcliquepartition[pos] = consdata->negcliquepartition[consdata->nvars-1];
6408
6409 /* the following if and else cases assure that we have increasing clique numbers */
6410 if( consdata->negcliquepartition[pos] > pos )
6411 consdata->negcliquepartitioned = FALSE; /* recalculate the clique partition after a coefficient was removed */
6412 else
6413 {
6414 int i;
6415 int cliquenumbefore;
6416
6417 /* if the old clique number was greater than the new one we have to check that, before a bigger clique number
6418 * occurs, the same as the old one occurs */
6419 if( oldcliqenum > consdata->negcliquepartition[pos] )
6420 {
6421 for( i = 0; i < consdata->nvars; ++i )
6422 if( oldcliqenum == consdata->negcliquepartition[i] )
6423 break;
6424 else if( oldcliqenum < consdata->negcliquepartition[i] )
6425 {
6426 consdata->negcliquepartitioned = FALSE; /* recalculate the negated clique partition after a coefficient was removed */
6427 break;
6428 }
6429 /* if we reached the end in the for loop, it means we have deleted the last element of the clique with
6430 * the biggest index, so decrease the number of negated cliques
6431 */
6432 if( i == consdata->nvars )
6433 --(consdata->nnegcliques);
6434 }
6435 /* if the old clique number was smaller than the new one we have to check the front for an element with
6436 * clique number minus 1 */
6437 else if( oldcliqenum < consdata->negcliquepartition[pos] )
6438 {
6439 cliquenumbefore = consdata->negcliquepartition[pos] - 1;
6440 for( i = pos - 1; i >= 0 && i >= cliquenumbefore && consdata->negcliquepartition[i] < cliquenumbefore; --i ); /*lint !e722*/
6441
6442 if( i < cliquenumbefore )
6443 consdata->negcliquepartitioned = FALSE; /* recalculate the negated clique partition after a coefficient was removed */
6444 }
6445 /* if we deleted the last element of the clique with biggest index, we have to decrease the clique number */
6446 else if( pos == consdata->nvars - 1)
6447 {
6448 cliquenumbefore = consdata->negcliquepartition[pos];
6449 for( i = pos - 1; i >= 0 && i >= cliquenumbefore && consdata->negcliquepartition[i] < cliquenumbefore; --i ); /*lint !e722*/
6450
6451 if( i < cliquenumbefore )
6452 --(consdata->nnegcliques);
6453 }
6454 /* otherwise if the old clique number is equal to the new one the cliquepartition should be ok */
6455 }
6456 }
6457 else
6458 --(consdata->nnegcliques);
6459 }
6460
6461 --(consdata->nvars);
6462
6463 return SCIP_OKAY;
6464 }
6465
6466 /** removes all items with weight zero from knapsack constraint */
6467 static
removeZeroWeights(SCIP * scip,SCIP_CONS * cons)6468 SCIP_RETCODE removeZeroWeights(
6469 SCIP* scip, /**< SCIP data structure */
6470 SCIP_CONS* cons /**< knapsack constraint */
6471 )
6472 {
6473 SCIP_CONSDATA* consdata;
6474 int v;
6475
6476 consdata = SCIPconsGetData(cons);
6477 assert(consdata != NULL);
6478
6479 for( v = consdata->nvars-1; v >= 0; --v )
6480 {
6481 if( consdata->weights[v] == 0 )
6482 {
6483 SCIP_CALL( delCoefPos(scip, cons, v) );
6484 }
6485 }
6486
6487 return SCIP_OKAY;
6488 }
6489
6490 /* perform deletion of variables in all constraints of the constraint handler */
6491 static
performVarDeletions(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss)6492 SCIP_RETCODE performVarDeletions(
6493 SCIP* scip, /**< SCIP data structure */
6494 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6495 SCIP_CONS** conss, /**< array of constraints */
6496 int nconss /**< number of constraints */
6497 )
6498 {
6499 SCIP_CONSDATA* consdata;
6500 int i;
6501 int v;
6502
6503 assert(scip != NULL);
6504 assert(conshdlr != NULL);
6505 assert(conss != NULL);
6506 assert(nconss >= 0);
6507 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
6508
6509 /* iterate over all constraints */
6510 for( i = 0; i < nconss; i++ )
6511 {
6512 consdata = SCIPconsGetData(conss[i]);
6513
6514 /* constraint is marked, that some of its variables were deleted */
6515 if( consdata->varsdeleted )
6516 {
6517 /* iterate over all variables of the constraint and delete them from the constraint */
6518 for( v = consdata->nvars - 1; v >= 0; --v )
6519 {
6520 if( SCIPvarIsDeleted(consdata->vars[v]) )
6521 {
6522 SCIP_CALL( delCoefPos(scip, conss[i], v) );
6523 }
6524 }
6525 consdata->varsdeleted = FALSE;
6526 }
6527 }
6528
6529 return SCIP_OKAY;
6530 }
6531
6532 /** replaces multiple occurrences of a variable or its negation by a single coefficient */
6533 static
mergeMultiples(SCIP * scip,SCIP_CONS * cons,SCIP_Bool * cutoff)6534 SCIP_RETCODE mergeMultiples(
6535 SCIP* scip, /**< SCIP data structure */
6536 SCIP_CONS* cons, /**< knapsack constraint */
6537 SCIP_Bool* cutoff /**< pointer to store whether the node can be cut off */
6538 )
6539 {
6540 SCIP_CONSDATA* consdata;
6541 int v;
6542 int prev;
6543
6544 assert(scip != NULL);
6545 assert(cons != NULL);
6546 assert(cutoff != NULL);
6547
6548 consdata = SCIPconsGetData(cons);
6549 assert(consdata != NULL);
6550
6551 *cutoff = FALSE;
6552
6553 if( consdata->merged )
6554 return SCIP_OKAY;
6555
6556 if( consdata->nvars <= 1 )
6557 {
6558 consdata->merged = TRUE;
6559 return SCIP_OKAY;
6560 }
6561
6562 assert(consdata->vars != NULL || consdata->nvars == 0);
6563
6564 /* sorting array after indices of variables, that's only for faster merging */
6565 SCIPsortPtrPtrLongIntInt((void**)consdata->vars, (void**)consdata->eventdata, consdata->weights,
6566 consdata->cliquepartition, consdata->negcliquepartition, SCIPvarCompActiveAndNegated, consdata->nvars);
6567
6568 /* knapsack-sorting (decreasing weights) now lost */
6569 consdata->sorted = FALSE;
6570
6571 v = consdata->nvars - 1;
6572 prev = v - 1;
6573 /* loop backwards through the items: deletion only affects rear items */
6574 while( prev >= 0 )
6575 {
6576 SCIP_VAR* var1;
6577 SCIP_VAR* var2;
6578 SCIP_Bool negated1;
6579 SCIP_Bool negated2;
6580
6581 negated1 = FALSE;
6582 negated2 = FALSE;
6583
6584 var1 = consdata->vars[v];
6585 assert(SCIPvarIsBinary(var1));
6586 assert(SCIPvarIsActive(var1) || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED);
6587 if( SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED )
6588 {
6589 var1 = SCIPvarGetNegatedVar(var1);
6590 negated1 = TRUE;
6591 }
6592 assert(var1 != NULL);
6593
6594 var2 = consdata->vars[prev];
6595 assert(SCIPvarIsBinary(var2));
6596 assert(SCIPvarIsActive(var2) || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED);
6597 if( SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED )
6598 {
6599 var2 = SCIPvarGetNegatedVar(var2);
6600 negated2 = TRUE;
6601 }
6602 assert(var2 != NULL);
6603
6604 if( var1 == var2 )
6605 {
6606 /* both variables are either active or negated */
6607 if( negated1 == negated2 )
6608 {
6609 /* variables var1 and var2 are equal: add weight of var1 to var2, and delete var1 */
6610 consdataChgWeight(consdata, prev, consdata->weights[v] + consdata->weights[prev]);
6611 SCIP_CALL( delCoefPos(scip, cons, v) );
6612 }
6613 /* variables var1 and var2 are opposite: subtract smaller weight from larger weight, reduce capacity,
6614 * and delete item of smaller weight
6615 */
6616 else if( consdata->weights[v] == consdata->weights[prev] )
6617 {
6618 /* both variables eliminate themselves: w*x + w*(1-x) == w */
6619 consdata->capacity -= consdata->weights[v];
6620 SCIP_CALL( delCoefPos(scip, cons, v) ); /* this does not affect var2, because var2 stands before var1 */
6621 SCIP_CALL( delCoefPos(scip, cons, prev) );
6622
6623 --prev;
6624 }
6625 else if( consdata->weights[v] < consdata->weights[prev] )
6626 {
6627 consdata->capacity -= consdata->weights[v];
6628 consdataChgWeight(consdata, prev, consdata->weights[prev] - consdata->weights[v]);
6629 assert(consdata->weights[prev] > 0);
6630 SCIP_CALL( delCoefPos(scip, cons, v) ); /* this does not affect var2, because var2 stands before var1 */
6631 }
6632 else
6633 {
6634 consdata->capacity -= consdata->weights[prev];
6635 consdataChgWeight(consdata, v, consdata->weights[v] - consdata->weights[prev]);
6636 assert(consdata->weights[v] > 0);
6637 SCIP_CALL( delCoefPos(scip, cons, prev) ); /* attention: normally we lose our order */
6638 /* restore order iff necessary */
6639 if( consdata->nvars != v ) /* otherwise the order still stands */
6640 {
6641 assert(prev == 0 || ((prev > 0) && (SCIPvarIsActive(consdata->vars[prev - 1]) || SCIPvarGetStatus(consdata->vars[prev - 1]) == SCIP_VARSTATUS_NEGATED)) );
6642 /* either that was the last pair or both, the negated and "normal" variable in front doesn't match var1, so the order is irrelevant */
6643 if( prev == 0 || (var1 != consdata->vars[prev - 1] && var1 != SCIPvarGetNegatedVar(consdata->vars[prev - 1])) )
6644 --prev;
6645 else /* we need to let v at the same position*/
6646 {
6647 consdata->cliquesadded = FALSE; /* reduced capacity might lead to larger cliques */
6648 /* don't decrease v, the same variable may exist up front */
6649 --prev;
6650 continue;
6651 }
6652 }
6653 }
6654 consdata->cliquesadded = FALSE; /* reduced capacity might lead to larger cliques */
6655 }
6656 v = prev;
6657 --prev;
6658 }
6659
6660 consdata->merged = TRUE;
6661
6662 /* check infeasibility */
6663 if( consdata->onesweightsum > consdata->capacity )
6664 {
6665 SCIPdebugMsg(scip, "merge multiples detected cutoff.\n");
6666 *cutoff = TRUE;
6667 return SCIP_OKAY;
6668 }
6669
6670 return SCIP_OKAY;
6671 }
6672
6673 /** in case the knapsack constraint is independent of every else, solve the knapsack problem (exactly) and apply the
6674 * fixings (dual reductions)
6675 */
6676 static
dualPresolving(SCIP * scip,SCIP_CONS * cons,int * nfixedvars,int * ndelconss,SCIP_Bool * deleted)6677 SCIP_RETCODE dualPresolving(
6678 SCIP* scip, /**< SCIP data structure */
6679 SCIP_CONS* cons, /**< knapsack constraint */
6680 int* nfixedvars, /**< pointer to count number of fixings */
6681 int* ndelconss, /**< pointer to count number of deleted constraints */
6682 SCIP_Bool* deleted /**< pointer to store if the constraint is deleted */
6683 )
6684 {
6685 SCIP_CONSDATA* consdata;
6686 SCIP_VAR** vars;
6687 SCIP_Real* profits;
6688 int* solitems;
6689 int* nonsolitems;
6690 int* items;
6691 SCIP_Real solval;
6692 SCIP_Bool infeasible;
6693 SCIP_Bool tightened;
6694 SCIP_Bool applicable;
6695 int nsolitems;
6696 int nnonsolitems;
6697 int nvars;
6698 int v;
6699
6700 assert(!SCIPconsIsModifiable(cons));
6701
6702 /* constraints for which the check flag is set to FALSE, did not contribute to the lock numbers; therefore, we cannot
6703 * use the locks to decide for a dual reduction using this constraint; for example after a restart the cuts which are
6704 * added to the problems have the check flag set to FALSE
6705 */
6706 if( !SCIPconsIsChecked(cons) )
6707 return SCIP_OKAY;
6708
6709 consdata = SCIPconsGetData(cons);
6710 assert(consdata != NULL);
6711
6712 nvars = consdata->nvars;
6713 vars = consdata->vars;
6714
6715 SCIP_CALL( SCIPallocBufferArray(scip, &profits, nvars) );
6716 SCIP_CALL( SCIPallocBufferArray(scip, &items, nvars) );
6717 SCIP_CALL( SCIPallocBufferArray(scip, &solitems, nvars) );
6718 SCIP_CALL( SCIPallocBufferArray(scip, &nonsolitems, nvars) );
6719
6720 applicable = TRUE;
6721
6722 /* check if we can apply the dual reduction; this can be done if the knapsack has the only locks on this constraint;
6723 * collect object values which are the profits of the knapsack problem
6724 */
6725 for( v = 0; v < nvars; ++v )
6726 {
6727 SCIP_VAR* var;
6728 SCIP_Bool negated;
6729
6730 var = vars[v];
6731 assert(var != NULL);
6732
6733 /* the variable should not be (globally) fixed */
6734 assert(SCIPvarGetLbGlobal(var) < 0.5 && SCIPvarGetUbGlobal(var) > 0.5);
6735
6736 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > 0
6737 || SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) > 1 )
6738 {
6739 applicable = FALSE;
6740 break;
6741 }
6742
6743 negated = FALSE;
6744
6745 /* get the active variable */
6746 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &negated) );
6747 assert(SCIPvarIsActive(var));
6748
6749 if( negated )
6750 profits[v] = SCIPvarGetObj(var);
6751 else
6752 profits[v] = -SCIPvarGetObj(var);
6753
6754 SCIPdebugMsg(scip, "variable <%s> -> item size %" SCIP_LONGINT_FORMAT ", profit <%g>\n",
6755 SCIPvarGetName(vars[v]), consdata->weights[v], profits[v]);
6756 items[v] = v;
6757 }
6758
6759 if( applicable )
6760 {
6761 SCIP_Bool success;
6762
6763 SCIPdebugMsg(scip, "the knapsack constraint <%s> is independent to rest of the problem\n", SCIPconsGetName(cons));
6764 SCIPdebugPrintCons(scip, cons, NULL);
6765
6766 /* solve knapsack problem exactly */
6767 SCIP_CALL( SCIPsolveKnapsackExactly(scip, consdata->nvars, consdata->weights, profits, consdata->capacity,
6768 items, solitems, nonsolitems, &nsolitems, &nnonsolitems, &solval, &success) );
6769
6770 if( success )
6771 {
6772 SCIP_VAR* var;
6773
6774 /* apply solution of the knapsack as dual reductions */
6775 for( v = 0; v < nsolitems; ++v )
6776 {
6777 var = vars[solitems[v]];
6778 assert(var != NULL);
6779
6780 SCIPdebugMsg(scip, "variable <%s> only locked up in knapsack constraints: dual presolve <%s>[%.15g,%.15g] >= 1.0\n",
6781 SCIPvarGetName(var), SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
6782 SCIP_CALL( SCIPtightenVarLb(scip, var, 1.0, TRUE, &infeasible, &tightened) );
6783 assert(!infeasible);
6784 assert(tightened);
6785 (*nfixedvars)++;
6786 }
6787
6788 for( v = 0; v < nnonsolitems; ++v )
6789 {
6790 var = vars[nonsolitems[v]];
6791 assert(var != NULL);
6792
6793 SCIPdebugMsg(scip, "variable <%s> has no down locks: dual presolve <%s>[%.15g,%.15g] <= 0.0\n",
6794 SCIPvarGetName(var), SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
6795 SCIP_CALL( SCIPtightenVarUb(scip, var, 0.0, TRUE, &infeasible, &tightened) );
6796 assert(!infeasible);
6797 assert(tightened);
6798 (*nfixedvars)++;
6799 }
6800
6801 SCIP_CALL( SCIPdelCons(scip, cons) );
6802 (*ndelconss)++;
6803 (*deleted) = TRUE;
6804 }
6805 }
6806
6807 SCIPfreeBufferArray(scip, &nonsolitems);
6808 SCIPfreeBufferArray(scip, &solitems);
6809 SCIPfreeBufferArray(scip, &items);
6810 SCIPfreeBufferArray(scip, &profits);
6811
6812 return SCIP_OKAY;
6813 }
6814
6815 /** check if the knapsack constraint is parallel to objective function; if so update the cutoff bound and avoid that the
6816 * constraint enters the LP by setting the initial and separated flag to FALSE
6817 */
6818 static
checkParallelObjective(SCIP * scip,SCIP_CONS * cons,SCIP_CONSHDLRDATA * conshdlrdata)6819 SCIP_RETCODE checkParallelObjective(
6820 SCIP* scip, /**< SCIP data structure */
6821 SCIP_CONS* cons, /**< knapsack constraint */
6822 SCIP_CONSHDLRDATA* conshdlrdata /**< knapsack constraint handler data */
6823 )
6824 {
6825 SCIP_CONSDATA* consdata;
6826 SCIP_VAR** vars;
6827 SCIP_VAR* var;
6828 SCIP_Real offset;
6829 SCIP_Real scale;
6830 SCIP_Real objval;
6831 SCIP_Bool applicable;
6832 SCIP_Bool negated;
6833 int nobjvars;
6834 int nvars;
6835 int v;
6836
6837 assert(scip != NULL);
6838 assert(cons != NULL);
6839 assert(conshdlrdata != NULL);
6840
6841 consdata = SCIPconsGetData(cons);
6842 assert(consdata != NULL);
6843
6844 nvars = consdata->nvars;
6845 nobjvars = SCIPgetNObjVars(scip);
6846
6847 /* check if the knapsack constraints has the same number of variables as the objective function and if the initial
6848 * and/or separated flag is set to FALSE
6849 */
6850 if( nvars != nobjvars || (!SCIPconsIsInitial(cons) && !SCIPconsIsSeparated(cons)) )
6851 return SCIP_OKAY;
6852
6853 /* There are no variables in the ojective function and in the constraint. Thus, the constraint is redundant. Since we
6854 * have a pure feasibility problem, we do not want to set a cutoff or lower bound.
6855 */
6856 if( nobjvars == 0 )
6857 return SCIP_OKAY;
6858
6859 vars = consdata->vars;
6860 assert(vars != NULL);
6861
6862 applicable = TRUE;
6863 offset = 0.0;
6864 scale = 1.0;
6865
6866 for( v = 0; v < nvars && applicable; ++v )
6867 {
6868 negated = FALSE;
6869 var = vars[v];
6870 assert(var != NULL);
6871
6872 if( SCIPvarIsNegated(var) )
6873 {
6874 negated = TRUE;
6875 var = SCIPvarGetNegatedVar(var);
6876 assert(var != NULL);
6877 }
6878
6879 objval = SCIPvarGetObj(var);
6880
6881 /* if a variable has a zero objective coefficient the knapsack constraint is not parallel to objective function */
6882 if( SCIPisZero(scip, objval) )
6883 applicable = FALSE;
6884 else
6885 {
6886 SCIP_Real weight;
6887
6888 weight = (SCIP_Real)consdata->weights[v];
6889
6890 if( negated )
6891 {
6892 if( v == 0 )
6893 {
6894 /* the first variable defines the scale */
6895 scale = weight / -objval;
6896
6897 offset += weight;
6898 }
6899 else if( SCIPisEQ(scip, -objval * scale, weight) )
6900 offset += weight;
6901 else
6902 applicable = FALSE;
6903 }
6904 else if( v == 0 )
6905 {
6906 /* the first variable define the scale */
6907 scale = weight / objval;
6908 }
6909 else if( !SCIPisEQ(scip, objval * scale, weight) )
6910 applicable = FALSE;
6911 }
6912 }
6913
6914 if( applicable )
6915 {
6916 if( SCIPisPositive(scip, scale) && conshdlrdata->detectcutoffbound )
6917 {
6918 SCIP_Real cutoffbound;
6919
6920 /* avoid that the knapsack constraint enters the LP since it is parallel to the objective function */
6921 SCIP_CALL( SCIPsetConsInitial(scip, cons, FALSE) );
6922 SCIP_CALL( SCIPsetConsSeparated(scip, cons, FALSE) );
6923
6924 cutoffbound = (consdata->capacity - offset) / scale;
6925
6926 SCIPdebugMsg(scip, "constraint <%s> is parallel to objective function and provids a cutoff bound <%g>\n",
6927 SCIPconsGetName(cons), cutoffbound);
6928
6929 /* increase the cutoff bound value by an epsilon to ensue that solution with the value of the cutoff bound are
6930 * still excepted
6931 */
6932 cutoffbound += SCIPcutoffbounddelta(scip);
6933
6934 SCIPdebugMsg(scip, "constraint <%s> is parallel to objective function and provids a cutoff bound <%g>\n",
6935 SCIPconsGetName(cons), cutoffbound);
6936
6937 if( cutoffbound < SCIPgetCutoffbound(scip) )
6938 {
6939 SCIPdebugMsg(scip, "update cutoff bound <%g>\n", cutoffbound);
6940
6941 SCIP_CALL( SCIPupdateCutoffbound(scip, cutoffbound) );
6942 }
6943 else
6944 {
6945 /* in case the cutoff bound is worse then currently known one we avoid additionaly enforcement and
6946 * propagation
6947 */
6948 SCIP_CALL( SCIPsetConsEnforced(scip, cons, FALSE) );
6949 SCIP_CALL( SCIPsetConsPropagated(scip, cons, FALSE) );
6950 }
6951 }
6952 else if( SCIPisNegative(scip, scale) && conshdlrdata->detectlowerbound )
6953 {
6954 SCIP_Real lowerbound;
6955
6956 /* avoid that the knapsack constraint enters the LP since it is parallel to the objective function */
6957 SCIP_CALL( SCIPsetConsInitial(scip, cons, FALSE) );
6958 SCIP_CALL( SCIPsetConsSeparated(scip, cons, FALSE) );
6959
6960 lowerbound = (consdata->capacity - offset) / scale;
6961
6962 SCIPdebugMsg(scip, "constraint <%s> is parallel to objective function and provids a lower bound <%g>\n",
6963 SCIPconsGetName(cons), lowerbound);
6964
6965 SCIP_CALL( SCIPupdateLocalLowerbound(scip, lowerbound) );
6966 }
6967 }
6968
6969 return SCIP_OKAY;
6970 }
6971
6972 /** sort the variables and weights w.r.t. the clique partition; thereby ensure the current order of the variables when a
6973 * weight of one variable is greater or equal another weight and both variables are in the same cliques */
6974 static
stableSort(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_VAR ** vars,SCIP_Longint * weights,int * cliquestartposs,SCIP_Bool usenegatedclique)6975 SCIP_RETCODE stableSort(
6976 SCIP* scip, /**< SCIP data structure */
6977 SCIP_CONSDATA* consdata, /**< knapsack constraint data */
6978 SCIP_VAR** vars, /**< array for sorted variables */
6979 SCIP_Longint* weights, /**< array for sorted weights */
6980 int* cliquestartposs, /**< starting position array for each clique */
6981 SCIP_Bool usenegatedclique /**< should negated or normal clique partition be used */
6982 )
6983 {
6984 SCIP_VAR** origvars;
6985 int norigvars;
6986 SCIP_Longint* origweights;
6987 int* cliquepartition;
6988 int ncliques;
6989
6990 SCIP_VAR*** varpointers;
6991 SCIP_Longint** weightpointers;
6992 int* cliquecount;
6993
6994 int nextpos;
6995 int c;
6996 int v;
6997
6998 assert(scip != NULL);
6999 assert(consdata != NULL);
7000 assert(vars != NULL);
7001 assert(weights != NULL);
7002 assert(cliquestartposs != NULL);
7003
7004 origweights = consdata->weights;
7005 origvars = consdata->vars;
7006 norigvars = consdata->nvars;
7007
7008 assert(origvars != NULL || norigvars == 0);
7009 assert(origweights != NULL || norigvars == 0);
7010
7011 if( norigvars == 0 )
7012 return SCIP_OKAY;
7013
7014 if( usenegatedclique )
7015 {
7016 assert(consdata->negcliquepartitioned);
7017
7018 cliquepartition = consdata->negcliquepartition;
7019 ncliques = consdata->nnegcliques;
7020 }
7021 else
7022 {
7023 assert(consdata->cliquepartitioned);
7024
7025 cliquepartition = consdata->cliquepartition;
7026 ncliques = consdata->ncliques;
7027 }
7028
7029 assert(cliquepartition != NULL);
7030 assert(ncliques > 0);
7031
7032 /* we first count all clique items and alloc temporary memory for a bucket sort */
7033 SCIP_CALL( SCIPallocBufferArray(scip, &cliquecount, ncliques) );
7034 BMSclearMemoryArray(cliquecount, ncliques);
7035
7036 /* first we count for each clique the number of elements */
7037 for( v = norigvars - 1; v >= 0; --v )
7038 {
7039 assert(0 <= cliquepartition[v] && cliquepartition[v] < ncliques);
7040 ++(cliquecount[cliquepartition[v]]);
7041 }
7042
7043 /*@todo: maybe it is better to put largest cliques up front */
7044
7045 #ifndef NDEBUG
7046 BMSclearMemoryArray(vars, norigvars);
7047 BMSclearMemoryArray(weights, norigvars);
7048 #endif
7049 SCIP_CALL( SCIPallocBufferArray(scip, &varpointers, ncliques) );
7050 SCIP_CALL( SCIPallocBufferArray(scip, &weightpointers, ncliques) );
7051
7052 nextpos = 0;
7053 /* now we initialize all start pointers for each clique, so they will be ordered */
7054 for( c = 0; c < ncliques; ++c )
7055 {
7056 /* to reach the goal that all variables of each clique will be standing next to each other we will initialize the
7057 * starting pointers for each clique by adding the number of each clique to the last clique starting pointer
7058 * e.g. clique1 has 4 elements and clique2 has 3 elements the the starting pointer for clique1 will be the pointer
7059 * to vars[0], the starting pointer to clique2 will be the pointer to vars[4] and to clique3 it will be
7060 * vars[7]
7061 *
7062 */
7063 varpointers[c] = (SCIP_VAR**) (vars + nextpos);
7064 cliquestartposs[c] = nextpos;
7065 weightpointers[c] = (SCIP_Longint*) (weights + nextpos);
7066 assert(cliquecount[c] > 0);
7067 nextpos += cliquecount[c];
7068 assert(nextpos > 0);
7069 }
7070 assert(nextpos == norigvars);
7071 cliquestartposs[c] = nextpos;
7072
7073 /* now we copy all variable and weights to the right order */
7074 for( v = 0; v < norigvars; ++v )
7075 {
7076 *(varpointers[cliquepartition[v]]) = origvars[v]; /*lint !e613*/
7077 ++(varpointers[cliquepartition[v]]);
7078 *(weightpointers[cliquepartition[v]]) = origweights[v]; /*lint !e613*/
7079 ++(weightpointers[cliquepartition[v]]);
7080 }
7081 #ifndef NDEBUG
7082 for( v = 0; v < norigvars; ++v )
7083 {
7084 assert(vars[v] != NULL);
7085 assert(weights[v] > 0);
7086 }
7087 #endif
7088
7089 /* free temporary memory */
7090 SCIPfreeBufferArray(scip, &weightpointers);
7091 SCIPfreeBufferArray(scip, &varpointers);
7092 SCIPfreeBufferArray(scip, &cliquecount);
7093
7094 return SCIP_OKAY;
7095 }
7096
7097 /** deletes all fixed variables from knapsack constraint, and replaces variables with binary representatives */
7098 static
applyFixings(SCIP * scip,SCIP_CONS * cons,SCIP_Bool * cutoff)7099 SCIP_RETCODE applyFixings(
7100 SCIP* scip, /**< SCIP data structure */
7101 SCIP_CONS* cons, /**< knapsack constraint */
7102 SCIP_Bool* cutoff /**< pointer to store whether the node can be cut off, or NULL if this
7103 * information is not needed; in this case, we apply all fixings
7104 * instead of stopping after the first infeasible one */
7105 )
7106 {
7107 SCIP_CONSDATA* consdata;
7108 int v;
7109
7110 assert(scip != NULL);
7111 assert(cons != NULL);
7112
7113 consdata = SCIPconsGetData(cons);
7114 assert(consdata != NULL);
7115 assert(consdata->nvars == 0 || consdata->vars != NULL);
7116
7117 if( cutoff != NULL )
7118 *cutoff = FALSE;
7119
7120 SCIPdebugMsg(scip, "apply fixings:\n");
7121 SCIPdebugPrintCons(scip, cons, NULL);
7122
7123 /* check infeasibility */
7124 if ( consdata->onesweightsum > consdata->capacity )
7125 {
7126 SCIPdebugMsg(scip, "apply fixings detected cutoff.\n");
7127
7128 if( cutoff != NULL )
7129 *cutoff = TRUE;
7130
7131 return SCIP_OKAY;
7132 }
7133
7134 /* all multi-aggregations should be resolved */
7135 consdata->existmultaggr = FALSE;
7136
7137 v = 0;
7138 while( v < consdata->nvars )
7139 {
7140 SCIP_VAR* var;
7141
7142 var = consdata->vars[v];
7143 assert(SCIPvarIsBinary(var));
7144
7145 if( SCIPvarGetLbGlobal(var) > 0.5 )
7146 {
7147 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(var), 1.0));
7148 consdata->capacity -= consdata->weights[v];
7149 SCIP_CALL( delCoefPos(scip, cons, v) );
7150 consdata->cliquesadded = FALSE; /* reduced capacity might lead to larger cliques */
7151 }
7152 else if( SCIPvarGetUbGlobal(var) < 0.5 )
7153 {
7154 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(var), 0.0));
7155 SCIP_CALL( delCoefPos(scip, cons, v) );
7156 }
7157 else
7158 {
7159 SCIP_VAR* repvar;
7160 SCIP_VAR* negvar;
7161 SCIP_VAR* workvar;
7162 SCIP_Longint weight;
7163 SCIP_Bool negated;
7164
7165 weight = consdata->weights[v];
7166
7167 /* get binary representative of variable */
7168 SCIP_CALL( SCIPgetBinvarRepresentative(scip, var, &repvar, &negated) );
7169 assert(repvar != NULL);
7170
7171 /* check for multi-aggregation */
7172 if( SCIPvarIsNegated(repvar) )
7173 {
7174 workvar = SCIPvarGetNegatedVar(repvar);
7175 assert(workvar != NULL);
7176 negated = TRUE;
7177 }
7178 else
7179 {
7180 workvar = repvar;
7181 negated = FALSE;
7182 }
7183
7184 /* @todo maybe resolve the problem that the eliminating of the multi-aggregation leads to a non-knapsack
7185 * constraint (converting into a linear constraint), for example the multi-aggregation consist of a non-binary
7186 * variable or due to resolving now their are non-integral coefficients or a non-integral capacity
7187 *
7188 * If repvar is not negated so workvar = repvar, otherwise workvar = 1 - repvar. This means,
7189 * weight * workvar = weight * (a_1*y_1 + ... + a_n*y_n + c)
7190 *
7191 * The explanation for the following block:
7192 * 1a) If repvar is a multi-aggregated variable weight * repvar should be replaced by
7193 * weight * (a_1*y_1 + ... + a_n*y_n + c).
7194 * 1b) If repvar is a negated variable of a multi-aggregated variable weight * repvar should be replaced by
7195 * weight - weight * (a_1*y_1 + ... + a_n*y_n + c), for better further use here we switch the sign of weight
7196 * so now we have the replacement -weight + weight * (a_1*y_1 + ... + a_n*y_n + c).
7197 * 2) For all replacement variable we check:
7198 * 2a) weight * a_i < 0 than we add -weight * a_i * y_i_neg to the constraint and adjust the capacity through
7199 * capacity -= weight * a_i caused by the negation of y_i.
7200 * 2b) weight * a_i >= 0 than we add weight * a_i * y_i to the constraint.
7201 * 3a) If repvar was not negated we need to subtract weight * c from capacity.
7202 * 3b) If repvar was negated we need to subtract weight * (c - 1) from capacity(note we switched the sign of
7203 * weight in this case.
7204 */
7205 if( SCIPvarGetStatus(workvar) == SCIP_VARSTATUS_MULTAGGR )
7206 {
7207 SCIP_VAR** aggrvars;
7208 SCIP_Real* aggrscalars;
7209 SCIP_Real aggrconst;
7210 int naggrvars;
7211 int i;
7212
7213 SCIP_CALL( SCIPflattenVarAggregationGraph(scip, workvar) );
7214 naggrvars = SCIPvarGetMultaggrNVars(workvar);
7215 aggrvars = SCIPvarGetMultaggrVars(workvar);
7216 aggrscalars = SCIPvarGetMultaggrScalars(workvar);
7217 aggrconst = SCIPvarGetMultaggrConstant(workvar);
7218 assert((aggrvars != NULL && aggrscalars != NULL) || naggrvars == 0);
7219
7220 if( !SCIPisIntegral(scip, weight * aggrconst) )
7221 {
7222 SCIPerrorMessage("try to resolve a multi-aggregation with a non-integral value for weight*aggrconst = %g\n", weight*aggrconst);
7223 return SCIP_ERROR;
7224 }
7225
7226 /* if workvar was negated, we have to flip the weight */
7227 if( negated )
7228 weight *= -1;
7229
7230 for( i = naggrvars - 1; i >= 0; --i )
7231 {
7232 assert(aggrvars != NULL);
7233 assert(aggrscalars != NULL);
7234
7235 if( !SCIPvarIsBinary(aggrvars[i]) )
7236 {
7237 SCIPerrorMessage("try to resolve a multi-aggregation with a non-binary %svariable <%s> with bounds [%g,%g]\n",
7238 SCIPvarIsIntegral(aggrvars[i]) ? "integral " : "", SCIPvarGetName(aggrvars[i]), SCIPvarGetLbGlobal(aggrvars[i]), SCIPvarGetUbGlobal(aggrvars[i]));
7239 return SCIP_ERROR;
7240 }
7241 if( !SCIPisIntegral(scip, weight * aggrscalars[i]) )
7242 {
7243 SCIPerrorMessage("try to resolve a multi-aggregation with a non-integral value for weight*aggrscalars = %g\n", weight*aggrscalars[i]);
7244 return SCIP_ERROR;
7245 }
7246 /* if the new coefficient is smaller than zero, we need to add the negated variable instead and adjust the capacity */
7247 if( SCIPisNegative(scip, weight * aggrscalars[i]) )
7248 {
7249 SCIP_CALL( SCIPgetNegatedVar(scip, aggrvars[i], &negvar));
7250 assert(negvar != NULL);
7251 SCIP_CALL( addCoef(scip, cons, negvar, (SCIP_Longint)(SCIPfloor(scip, -weight * aggrscalars[i] + 0.5))) );
7252 consdata->capacity -= (SCIP_Longint)(SCIPfloor(scip, weight * aggrscalars[i] + 0.5));
7253 }
7254 else
7255 {
7256 SCIP_CALL( addCoef(scip, cons, aggrvars[i], (SCIP_Longint)(SCIPfloor(scip, weight * aggrscalars[i] + 0.5))) );
7257 }
7258 }
7259 /* delete old coefficient */
7260 SCIP_CALL( delCoefPos(scip, cons, v) );
7261
7262 /* adjust the capacity with the aggregation constant and if necessary the extra weight through the negation */
7263 if( negated )
7264 consdata->capacity -= (SCIP_Longint)SCIPfloor(scip, weight * (aggrconst - 1) + 0.5);
7265 else
7266 consdata->capacity -= (SCIP_Longint)SCIPfloor(scip, weight * aggrconst + 0.5);
7267
7268 if( consdata->capacity < 0 )
7269 {
7270 if( cutoff != NULL )
7271 {
7272 *cutoff = TRUE;
7273 break;
7274 }
7275 }
7276 }
7277 /* check, if the variable should be replaced with the representative */
7278 else if( repvar != var )
7279 {
7280 /* delete old (aggregated) variable */
7281 SCIP_CALL( delCoefPos(scip, cons, v) );
7282
7283 /* add representative instead */
7284 SCIP_CALL( addCoef(scip, cons, repvar, weight) );
7285 }
7286 else
7287 ++v;
7288 }
7289 }
7290 assert(consdata->onesweightsum == 0);
7291
7292 SCIPdebugMsg(scip, "after applyFixings, before merging:\n");
7293 SCIPdebugPrintCons(scip, cons, NULL);
7294
7295 /* if aggregated variables have been replaced, multiple entries of the same variable are possible and we have to
7296 * clean up the constraint
7297 */
7298 if( cutoff != NULL && !(*cutoff) )
7299 {
7300 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
7301 SCIPdebugMsg(scip, "after applyFixings and merging:\n");
7302 SCIPdebugPrintCons(scip, cons, NULL);
7303 }
7304
7305 return SCIP_OKAY;
7306 }
7307
7308
7309 /** propagation method for knapsack constraints */
7310 static
propagateCons(SCIP * scip,SCIP_CONS * cons,SCIP_Bool * cutoff,SCIP_Bool * redundant,int * nfixedvars,SCIP_Bool usenegatedclique)7311 SCIP_RETCODE propagateCons(
7312 SCIP* scip, /**< SCIP data structure */
7313 SCIP_CONS* cons, /**< knapsack constraint */
7314 SCIP_Bool* cutoff, /**< pointer to store whether the node can be cut off */
7315 SCIP_Bool* redundant, /**< pointer to store whether constraint is redundant */
7316 int* nfixedvars, /**< pointer to count number of fixings */
7317 SCIP_Bool usenegatedclique /**< should negated clique information be used */
7318 )
7319 {
7320 SCIP_CONSDATA* consdata;
7321 SCIP_Bool infeasible;
7322 SCIP_Bool tightened;
7323 SCIP_Longint* secondmaxweights;
7324 SCIP_Longint minweightsum;
7325 SCIP_Longint residualcapacity;
7326
7327 int nvars;
7328 int i;
7329 int nnegcliques;
7330
7331 SCIP_VAR** myvars;
7332 SCIP_Longint* myweights;
7333 int* cliquestartposs;
7334 int* cliqueendposs;
7335 SCIP_Longint localminweightsum;
7336 SCIP_Bool foundmax;
7337 int c;
7338
7339 assert(scip != NULL);
7340 assert(cons != NULL);
7341 assert(cutoff != NULL);
7342 assert(redundant != NULL);
7343 assert(nfixedvars != NULL);
7344
7345 consdata = SCIPconsGetData(cons);
7346 assert(consdata != NULL);
7347
7348 *cutoff = FALSE;
7349 *redundant = FALSE;
7350
7351 SCIPdebugMsg(scip, "propagating knapsack constraint <%s>\n", SCIPconsGetName(cons));
7352
7353 /* increase age of constraint; age is reset to zero, if a conflict or a propagation was found */
7354 if( !SCIPinRepropagation(scip) )
7355 {
7356 SCIP_CALL( SCIPincConsAge(scip, cons) );
7357 }
7358
7359 #ifndef NDEBUG
7360 /* assert that only active or negated variables are present */
7361 for( i = 0; i < consdata->nvars && consdata->merged; ++i )
7362 {
7363 assert(SCIPvarIsActive(consdata->vars[i]) || SCIPvarIsNegated(consdata->vars[i]) || SCIPvarGetStatus(consdata->vars[i]) == SCIP_VARSTATUS_FIXED);
7364 }
7365 #endif
7366
7367 usenegatedclique = usenegatedclique && consdata->merged;
7368
7369 /* init for debugging */
7370 myvars = NULL;
7371 myweights = NULL;
7372 cliquestartposs = NULL;
7373 secondmaxweights = NULL;
7374 minweightsum = 0;
7375 nvars = consdata->nvars;
7376 /* make sure, the items are sorted by non-increasing weight */
7377 sortItems(consdata);
7378
7379 do
7380 {
7381 localminweightsum = 0;
7382
7383 /* (1) compute the minimum weight of the knapsack constraint using negated clique information;
7384 * a negated clique means, that at most one of the clique variables can be zero
7385 * - minweightsum = sum_{negated cliques C} ( sum(wi : i \in C) - W_max(C) ), where W_max(C) is the maximal weight of C
7386 *
7387 * if for i \in C (a negated clique) oneweightsum + minweightsum - wi + W_max(C) > capacity => xi = 1
7388 * since replacing i with the element of maximal weight leads to infeasibility
7389 */
7390 if( usenegatedclique && nvars > 0 )
7391 {
7392 SCIP_CONSHDLRDATA* conshdlrdata;
7393 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
7394 assert(conshdlrdata != NULL);
7395
7396 /* compute clique partitions */
7397 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, FALSE, TRUE) );
7398 nnegcliques = consdata->nnegcliques;
7399
7400 /* if we have no real negated cliques we can stop here */
7401 if( nnegcliques == nvars )
7402 {
7403 /* run the standard algorithm that does not involve cliques */
7404 usenegatedclique = FALSE;
7405 break;
7406 }
7407
7408 /* allocate temporary memory and initialize it */
7409 SCIP_CALL( SCIPduplicateBufferArray(scip, &myvars, consdata->vars, nvars) );
7410 SCIP_CALL( SCIPduplicateBufferArray(scip, &myweights, consdata->weights, nvars) ) ;
7411 SCIP_CALL( SCIPallocBufferArray(scip, &cliquestartposs, nnegcliques + 1) );
7412 SCIP_CALL( SCIPallocBufferArray(scip, &cliqueendposs, nnegcliques) );
7413 SCIP_CALL( SCIPallocBufferArray(scip, &secondmaxweights, nnegcliques) );
7414 BMSclearMemoryArray(secondmaxweights, nnegcliques);
7415
7416 /* resort variables to avoid quadratic algorithm later on */
7417 SCIP_CALL( stableSort(scip, consdata, myvars, myweights, cliquestartposs, TRUE) );
7418
7419 /* save the end positions of the cliques because start positions are moved in the following loop */
7420 for( c = 0; c < nnegcliques; ++c )
7421 {
7422 cliqueendposs[c] = cliquestartposs[c+1] - 1;
7423 assert(cliqueendposs[c] - cliquestartposs[c] >= 0);
7424 }
7425
7426 c = 0;
7427 foundmax = FALSE;
7428 i = 0;
7429
7430 while( i < nvars )
7431 {
7432 /* ignore variables of the negated clique which are fixed to one since these are counted in
7433 * consdata->onesweightsum
7434 */
7435
7436 /* if there are only one variable negated cliques left we can stop */
7437 if( nnegcliques - c == nvars - i )
7438 {
7439 minweightsum += localminweightsum;
7440 localminweightsum = 0;
7441 break;
7442 }
7443
7444 /* for summing up the minimum active weights due to cliques we have to omit the biggest weights of each
7445 * clique, we can only skip this clique if this variables is not fixed to zero, otherwise we have to fix all
7446 * other clique variables to one
7447 */
7448 if( cliquestartposs[c] == i )
7449 {
7450 assert(myweights[i] > 0);
7451 ++c;
7452 minweightsum += localminweightsum;
7453 localminweightsum = 0;
7454 foundmax = TRUE;
7455
7456 if( SCIPvarGetLbLocal(myvars[i]) > 0.5 )
7457 foundmax = FALSE;
7458
7459 if( SCIPvarGetUbLocal(myvars[i]) > 0.5 )
7460 {
7461 ++i;
7462 continue;
7463 }
7464 }
7465
7466 if( SCIPvarGetLbLocal(myvars[i]) < 0.5 )
7467 {
7468 assert(myweights[i] > 0);
7469
7470 if( SCIPvarGetUbLocal(myvars[i]) > 0.5 )
7471 {
7472 assert(myweights[i] <= myweights[cliquestartposs[c - 1]]);
7473
7474 if( !foundmax )
7475 {
7476 foundmax = TRUE;
7477
7478 /* overwrite cliquestartpos to the position of the first unfixed variable in this clique */
7479 cliquestartposs[c - 1] = i;
7480 ++i;
7481
7482 continue;
7483 }
7484 /* memorize second max weight for each clique */
7485 if( secondmaxweights[c - 1] == 0 )
7486 secondmaxweights[c - 1] = myweights[i];
7487
7488 localminweightsum += myweights[i];
7489 }
7490 /* we found a fixed variable to zero so all other variables in this negated clique have to be fixed to one */
7491 else
7492 {
7493 int v;
7494 /* fix all other variables of the negated clique to 1 */
7495 for( v = cliquestartposs[c - 1]; v < cliquestartposs[c]; ++v )
7496 {
7497 if( v != i && SCIPvarGetLbLocal(myvars[v]) < 0.5 )
7498 {
7499 SCIPdebugMsg(scip, " -> fixing variable <%s> to 1, due to negated clique information\n", SCIPvarGetName(myvars[v]));
7500 SCIP_CALL( SCIPinferBinvarCons(scip, myvars[v], TRUE, cons, SCIPvarGetIndex(myvars[i]), &infeasible, &tightened) );
7501
7502 if( infeasible )
7503 {
7504 assert( SCIPvarGetUbLocal(myvars[v]) < 0.5 );
7505
7506 /* analyze the infeasibility if conflict analysis is applicable */
7507 if( SCIPisConflictAnalysisApplicable(scip) )
7508 {
7509 /* conflict analysis can only be applied in solving stage */
7510 assert(SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPinProbing(scip));
7511
7512 /* initialize the conflict analysis */
7513 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) );
7514
7515 /* add the two variables which are fixed to zero within a negated clique */
7516 SCIP_CALL( SCIPaddConflictBinvar(scip, myvars[i]) );
7517 SCIP_CALL( SCIPaddConflictBinvar(scip, myvars[v]) );
7518
7519 /* start the conflict analysis */
7520 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
7521 }
7522 *cutoff = TRUE;
7523 break;
7524 }
7525 assert(tightened);
7526 ++(*nfixedvars);
7527 SCIP_CALL( SCIPresetConsAge(scip, cons) );
7528 }
7529 }
7530
7531 /* reset local minweightsum for clique because all fixed to one variables are now counted in consdata->onesweightsum */
7532 localminweightsum = 0;
7533 /* we can jump to the end of this clique */
7534 i = cliqueendposs[c - 1];
7535
7536 if( *cutoff )
7537 break;
7538 }
7539 }
7540 ++i;
7541 }
7542 /* add last clique minweightsum */
7543 minweightsum += localminweightsum;
7544
7545 SCIPdebugMsg(scip, "knapsack constraint <%s> has minimum weight sum of <%" SCIP_LONGINT_FORMAT ">\n",
7546 SCIPconsGetName(cons), minweightsum + consdata->onesweightsum );
7547
7548 /* check, if weights of fixed variables don't exceeds knapsack capacity */
7549 if( !(*cutoff) && consdata->capacity >= minweightsum + consdata->onesweightsum )
7550 {
7551 SCIP_Longint maxcliqueweight = -1LL;
7552
7553 /* loop over cliques */
7554 for( c = 0; c < nnegcliques; ++c )
7555 {
7556 SCIP_VAR* maxvar;
7557 SCIP_Bool maxvarfixed;
7558 int endvarposclique;
7559 int startvarposclique;
7560
7561 assert(myvars != NULL);
7562 assert(nnegcliques == consdata->nnegcliques);
7563 assert(myweights != NULL);
7564 assert(secondmaxweights != NULL);
7565 assert(cliquestartposs != NULL);
7566
7567 endvarposclique = cliqueendposs[c];
7568 startvarposclique = cliquestartposs[c];
7569
7570 maxvar = myvars[startvarposclique];
7571
7572 /* no need to process this negated clique because all variables are already fixed (which we detect from a fixed maxvar) */
7573 if( SCIPvarGetUbLocal(maxvar) - SCIPvarGetLbLocal(maxvar) < 0.5 )
7574 continue;
7575
7576 maxcliqueweight = myweights[startvarposclique];
7577 maxvarfixed = FALSE;
7578 /* if the sum of all weights of fixed variables to one plus the minimalweightsum (minimal weight which is already
7579 * used in this knapsack due to negated cliques) plus any weight minus the second largest weight in this clique
7580 * exceeds the capacity the maximum weight variable can be fixed to zero.
7581 */
7582 if( consdata->onesweightsum + minweightsum + (maxcliqueweight - secondmaxweights[c]) > consdata->capacity )
7583 {
7584 #ifndef NDEBUG
7585 SCIP_Longint oldonesweightsum = consdata->onesweightsum;
7586 #endif
7587 assert(maxcliqueweight >= secondmaxweights[c]);
7588 assert(SCIPvarGetLbLocal(maxvar) < 0.5 && SCIPvarGetUbLocal(maxvar) > 0.5);
7589
7590 SCIPdebugMsg(scip, " -> fixing variable <%s> to 0\n", SCIPvarGetName(maxvar));
7591 SCIP_CALL( SCIPresetConsAge(scip, cons) );
7592 SCIP_CALL( SCIPinferBinvarCons(scip, maxvar, FALSE, cons, cliquestartposs[c], &infeasible, &tightened) );
7593 assert(consdata->onesweightsum == oldonesweightsum);
7594 assert(!infeasible);
7595 assert(tightened);
7596 (*nfixedvars)++;
7597 maxvarfixed = TRUE;
7598 }
7599 /* the remaining cliques are singletons such that all subsequent variables have a weight that
7600 * fits into the knapsack
7601 */
7602 else if( nnegcliques - c == nvars - startvarposclique )
7603 break;
7604 /* early termination of the remaining loop because no further variable fixings are possible:
7605 *
7606 * the gain in any of the following negated cliques (the additional term if the maximum weight variable was set to 1, and the second
7607 * largest was set to 0) does not suffice to infer additional variable fixings because
7608 *
7609 * - the cliques are sorted by decreasing maximum weight -> for all c' >= c: maxweights[c'] <= maxcliqueweight
7610 * - their second largest elements are at least as large as the smallest weight of the knapsack
7611 */
7612 else if( consdata->onesweightsum + minweightsum + (maxcliqueweight - consdata->weights[nvars - 1]) <= consdata->capacity )
7613 break;
7614
7615 /* loop over items with non-maximal weight (omitting the first position) */
7616 for( i = endvarposclique; i > startvarposclique; --i )
7617 {
7618 /* there should be no variable fixed to 0 between startvarposclique + 1 and endvarposclique unless we
7619 * messed up the clique preprocessing in the previous loop to filter those variables out */
7620 assert(SCIPvarGetUbLocal(myvars[i]) > 0.5);
7621
7622 /* only check variables of negated cliques for which no variable is locally fixed */
7623 if( SCIPvarGetLbLocal(myvars[i]) < 0.5 )
7624 {
7625 assert(maxcliqueweight >= myweights[i]);
7626 assert(i == endvarposclique || myweights[i] >= myweights[i+1]);
7627
7628 /* we fix the members of this clique with non-maximal weight in two cases to 1:
7629 *
7630 * the maxvar was already fixed to 0 because it has a huge gain.
7631 *
7632 * if for i \in C (a negated clique) onesweightsum - wi + W_max(c) > capacity => xi = 1
7633 * since replacing i with the element of maximal weight leads to infeasibility */
7634 if( maxvarfixed || consdata->onesweightsum + minweightsum - myweights[i] + maxcliqueweight > consdata->capacity )
7635 {
7636 #ifndef NDEBUG
7637 SCIP_Longint oldonesweightsum = consdata->onesweightsum;
7638 #endif
7639 SCIPdebugMsg(scip, " -> fixing variable <%s> to 1, due to negated clique information\n", SCIPvarGetName(myvars[i]));
7640 SCIP_CALL( SCIPinferBinvarCons(scip, myvars[i], TRUE, cons, -i, &infeasible, &tightened) );
7641 assert(consdata->onesweightsum == oldonesweightsum + myweights[i]);
7642 assert(!infeasible);
7643 assert(tightened);
7644 ++(*nfixedvars);
7645 SCIP_CALL( SCIPresetConsAge(scip, cons) );
7646
7647 /* update minweightsum because now the variable is fixed to one and its weight is counted by
7648 * consdata->onesweightsum
7649 */
7650 minweightsum -= myweights[i];
7651 assert(minweightsum >= 0);
7652 }
7653 else
7654 break;
7655 }
7656 }
7657 #ifndef NDEBUG
7658 /* in debug mode, we assert that we did not miss possible fixings by the break above */
7659 for( ; i > startvarposclique; --i )
7660 {
7661 SCIP_Bool varisfixed = SCIPvarGetUbLocal(myvars[i]) - SCIPvarGetLbLocal(myvars[i]) < 0.5;
7662 SCIP_Bool exceedscapacity = consdata->onesweightsum + minweightsum - myweights[i] + maxcliqueweight > consdata->capacity;
7663
7664 assert(i == endvarposclique || myweights[i] >= myweights[i+1]);
7665 assert(varisfixed || !exceedscapacity);
7666 }
7667 #endif
7668 }
7669 }
7670 SCIPfreeBufferArray(scip, &secondmaxweights);
7671 SCIPfreeBufferArray(scip, &cliqueendposs);
7672 SCIPfreeBufferArray(scip, &cliquestartposs);
7673 SCIPfreeBufferArray(scip, &myweights);
7674 SCIPfreeBufferArray(scip, &myvars);
7675 }
7676
7677 assert(consdata->negcliquepartitioned || minweightsum == 0);
7678 }
7679 while( FALSE );
7680
7681 assert(usenegatedclique || minweightsum == 0);
7682 /* check, if weights of fixed variables already exceed knapsack capacity */
7683 if( consdata->capacity < minweightsum + consdata->onesweightsum )
7684 {
7685 SCIPdebugMsg(scip, " -> cutoff - fixed weight: %" SCIP_LONGINT_FORMAT ", capacity: %" SCIP_LONGINT_FORMAT " \n",
7686 consdata->onesweightsum, consdata->capacity);
7687
7688 SCIP_CALL( SCIPresetConsAge(scip, cons) );
7689 *cutoff = TRUE;
7690
7691 /* analyze the cutoff in SOLVING stage and if conflict analysis is turned on */
7692 if( (SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPinProbing(scip)) && SCIPisConflictAnalysisApplicable(scip) )
7693 {
7694 /* start conflict analysis with the fixed-to-one variables, add only as many as needed to exceed the capacity */
7695 SCIP_Longint weight;
7696
7697 weight = 0;
7698
7699 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) );
7700
7701 for( i = 0; i < nvars && weight <= consdata->capacity; i++ )
7702 {
7703 if( SCIPvarGetLbLocal(consdata->vars[i]) > 0.5)
7704 {
7705 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[i]) );
7706 weight += consdata->weights[i];
7707 }
7708 }
7709
7710 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
7711 }
7712
7713 return SCIP_OKAY;
7714 }
7715
7716 /* the algorithm below is a special case of propagation involving negated cliques */
7717 if( !usenegatedclique )
7718 {
7719 assert(consdata->sorted);
7720 residualcapacity = consdata->capacity - consdata->onesweightsum;
7721
7722 /* fix all variables to zero, that don't fit into the knapsack anymore */
7723 for( i = 0; i < nvars && consdata->weights[i] > residualcapacity; ++i )
7724 {
7725 /* if all weights of fixed variables to one plus any weight exceeds the capacity the variables have to be fixed
7726 * to zero
7727 */
7728 if( SCIPvarGetLbLocal(consdata->vars[i]) < 0.5 )
7729 {
7730 if( SCIPvarGetUbLocal(consdata->vars[i]) > 0.5 )
7731 {
7732 assert(consdata->onesweightsum + consdata->weights[i] > consdata->capacity);
7733 SCIPdebugMsg(scip, " -> fixing variable <%s> to 0\n", SCIPvarGetName(consdata->vars[i]));
7734 SCIP_CALL( SCIPresetConsAge(scip, cons) );
7735 SCIP_CALL( SCIPinferBinvarCons(scip, consdata->vars[i], FALSE, cons, i, &infeasible, &tightened) );
7736 assert(!infeasible);
7737 assert(tightened);
7738 (*nfixedvars)++;
7739 }
7740 }
7741 }
7742 }
7743
7744 /* check if the knapsack is now redundant */
7745 if( !SCIPconsIsModifiable(cons) )
7746 {
7747 SCIP_Longint unfixedweightsum = consdata->onesweightsum;
7748
7749 /* sum up the weights of all unfixed variables, plus the weight sum of all variables fixed to one already */
7750 for( i = 0; i < nvars; ++i )
7751 {
7752 if( SCIPvarGetLbLocal(consdata->vars[i]) + 0.5 < SCIPvarGetUbLocal(consdata->vars[i]) )
7753 {
7754 unfixedweightsum += consdata->weights[i];
7755
7756 /* the weight sum is larger than the capacity, so the constraint is not redundant */
7757 if( unfixedweightsum > consdata->capacity )
7758 return SCIP_OKAY;
7759 }
7760 }
7761 /* we summed up all (unfixed and fixed to one) weights and did not exceed the capacity, so the constraint is redundant */
7762 SCIPdebugMsg(scip, " -> knapsack constraint <%s> is redundant: weightsum=%" SCIP_LONGINT_FORMAT ", unfixedweightsum=%" SCIP_LONGINT_FORMAT ", capacity=%" SCIP_LONGINT_FORMAT "\n",
7763 SCIPconsGetName(cons), consdata->weightsum, unfixedweightsum, consdata->capacity);
7764 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
7765 *redundant = TRUE;
7766 }
7767
7768 return SCIP_OKAY;
7769 }
7770
7771 /** all but one variable fit into the knapsack constraint, so we can upgrade this constraint to an logicor constraint
7772 * containing all negated variables of this knapsack constraint
7773 */
7774 static
upgradeCons(SCIP * scip,SCIP_CONS * cons,int * ndelconss,int * naddconss)7775 SCIP_RETCODE upgradeCons(
7776 SCIP* scip, /**< SCIP data structure */
7777 SCIP_CONS* cons, /**< knapsack constraint */
7778 int* ndelconss, /**< pointer to store the amount of deleted constraints */
7779 int* naddconss /**< pointer to count number of added constraints */
7780 )
7781 {
7782 SCIP_CONS* newcons;
7783 SCIP_CONSDATA* consdata;
7784
7785 assert(scip != NULL);
7786 assert(cons != NULL);
7787 assert(ndelconss != NULL);
7788 assert(naddconss != NULL);
7789
7790 consdata = SCIPconsGetData(cons);
7791 assert(consdata != NULL);
7792 assert(consdata->nvars > 1);
7793
7794 /* if the knapsack constraint consists only of two variables, we can upgrade it to a set-packing constraint */
7795 if( consdata->nvars == 2 )
7796 {
7797 SCIPdebugMsg(scip, "upgrading knapsack constraint <%s> to a set-packing constraint", SCIPconsGetName(cons));
7798
7799 SCIP_CALL( SCIPcreateConsSetpack(scip, &newcons, SCIPconsGetName(cons), consdata->nvars, consdata->vars,
7800 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
7801 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
7802 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
7803 SCIPconsIsStickingAtNode(cons)) );
7804 }
7805 /* if the knapsack constraint consists of at least three variables, we can upgrade it to a logicor constraint
7806 * containing all negated variables of the knapsack
7807 */
7808 else
7809 {
7810 SCIP_VAR** consvars;
7811
7812 SCIPdebugMsg(scip, "upgrading knapsack constraint <%s> to a logicor constraint", SCIPconsGetName(cons));
7813
7814 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, consdata->nvars) );
7815 SCIP_CALL( SCIPgetNegatedVars(scip, consdata->nvars, consdata->vars, consvars) );
7816
7817 SCIP_CALL( SCIPcreateConsLogicor(scip, &newcons, SCIPconsGetName(cons), consdata->nvars, consvars,
7818 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
7819 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
7820 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
7821 SCIPconsIsStickingAtNode(cons)) );
7822
7823 SCIPfreeBufferArray(scip, &consvars);
7824 }
7825
7826 SCIP_CALL( SCIPaddCons(scip, newcons) );
7827 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
7828 ++(*naddconss);
7829
7830 SCIP_CALL( SCIPdelCons(scip, cons) );
7831 ++(*ndelconss);
7832
7833 return SCIP_OKAY;
7834 }
7835
7836 /** delete redundant variables
7837 *
7838 * i.e. 5x1 + 5x2 + 5x3 + 2x4 + 1x5 <= 13 => x4, x5 always fits into the knapsack, so we can delete them
7839 *
7840 * i.e. 5x1 + 5x2 + 5x3 + 2x4 + 1x5 <= 8 and we have the cliqueinformation (x1,x2,x3) is a clique
7841 * => x4, x5 always fits into the knapsack, so we can delete them
7842 *
7843 * i.e. 5x1 + 5x2 + 5x3 + 1x4 + 1x5 <= 6 and we have the cliqueinformation (x1,x2,x3) is a clique and (x4,x5) too
7844 * => we create the set partitioning constraint x4 + x5 <= 1 and delete them in this knapsack
7845 */
7846 static
deleteRedundantVars(SCIP * scip,SCIP_CONS * cons,SCIP_Longint frontsum,int splitpos,int * nchgcoefs,int * nchgsides,int * naddconss)7847 SCIP_RETCODE deleteRedundantVars(
7848 SCIP* scip, /**< SCIP data structure */
7849 SCIP_CONS* cons, /**< knapsack constraint */
7850 SCIP_Longint frontsum, /**< sum of front items which fit if we try to take from the first till the last */
7851 int splitpos, /**< split position till when all front items are fitting, splitpos is the
7852 * first which did not fit */
7853 int* nchgcoefs, /**< pointer to store the amount of changed coefficients */
7854 int* nchgsides, /**< pointer to store the amount of changed sides */
7855 int* naddconss /**< pointer to count number of added constraints */
7856 )
7857 {
7858 SCIP_CONSHDLRDATA* conshdlrdata;
7859 SCIP_CONSDATA* consdata;
7860 SCIP_VAR** vars;
7861 SCIP_Longint* weights;
7862 SCIP_Longint capacity;
7863 SCIP_Longint gcd;
7864 int nvars;
7865 int w;
7866
7867 assert(scip != NULL);
7868 assert(cons != NULL);
7869 assert(nchgcoefs != NULL);
7870 assert(nchgsides != NULL);
7871 assert(naddconss != NULL);
7872
7873 consdata = SCIPconsGetData(cons);
7874 assert(consdata != NULL);
7875 assert(0 < frontsum && frontsum < consdata->weightsum);
7876 assert(0 < splitpos && splitpos < consdata->nvars);
7877
7878 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
7879 assert(conshdlrdata != NULL);
7880
7881 vars = consdata->vars;
7882 weights = consdata->weights;
7883 nvars = consdata->nvars;
7884 capacity = consdata->capacity;
7885
7886 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
7887 * weight must not be sorted by their index
7888 */
7889 #ifndef NDEBUG
7890 for( w = nvars - 1; w > 0; --w )
7891 assert(weights[w] <= weights[w-1]);
7892 #endif
7893
7894 /* if there are no variables rear to splitpos, the constraint has no redundant variables */
7895 if( consdata->nvars - 1 == splitpos )
7896 return SCIP_OKAY;
7897
7898 assert(frontsum + weights[splitpos] > capacity);
7899
7900 /* detect redundant variables */
7901 if( consdata->weightsum - weights[splitpos] <= capacity )
7902 {
7903 /* all rear items are redundant, because leaving one item in front and incl. of splitpos out the rear itmes always
7904 * fit
7905 */
7906 SCIPdebugMsg(scip, "Found redundant variables in constraint <%s>.\n", SCIPconsGetName(cons));
7907
7908 /* delete items and update capacity */
7909 for( w = nvars - 1; w > splitpos; --w )
7910 {
7911 consdata->capacity -= weights[w];
7912 SCIP_CALL( delCoefPos(scip, cons, w) );
7913 }
7914 assert(w == splitpos);
7915
7916 ++(*nchgsides);
7917 *nchgcoefs += (nvars - splitpos);
7918
7919 /* division by greatest common divisor */
7920 gcd = weights[w];
7921 for( ; w >= 0 && gcd > 1; --w )
7922 {
7923 gcd = SCIPcalcGreComDiv(gcd, weights[w]);
7924 }
7925
7926 /* normalize if possible */
7927 if( gcd > 1 )
7928 {
7929 for( w = splitpos; w >= 0; --w )
7930 {
7931 consdataChgWeight(consdata, w, weights[w]/gcd);
7932 }
7933 (*nchgcoefs) += nvars;
7934
7935 consdata->capacity /= gcd;
7936 ++(*nchgsides);
7937 }
7938
7939 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
7940 * weight must not be sorted by their index
7941 */
7942 #ifndef NDEBUG
7943 for( w = consdata->nvars - 1; w > 0; --w )
7944 assert(weights[w] <= weights[w - 1]);
7945 #endif
7946 }
7947 /* rear items can only be redundant, when the sum is smaller to the weight at splitpos and all rear items would
7948 * always fit into the knapsack, therefor the item directly after splitpos needs to be smaller than the one at
7949 * splitpos and needs to fit into the knapsack
7950 */
7951 else if( conshdlrdata->disaggregation && frontsum + weights[splitpos + 1] <= capacity )
7952 {
7953 int* clqpart;
7954 int nclq;
7955 int len;
7956
7957 len = nvars - (splitpos + 1);
7958 /* allocate temporary memory */
7959 SCIP_CALL( SCIPallocBufferArray(scip, &clqpart, len) );
7960
7961 /* calculate clique partition */
7962 SCIP_CALL( SCIPcalcCliquePartition(scip, &(consdata->vars[splitpos+1]), len, clqpart, &nclq) );
7963
7964 /* check if we found at least one clique */
7965 if( nclq < len )
7966 {
7967 SCIP_Longint maxactduetoclq;
7968 int cliquenum;
7969
7970 maxactduetoclq = 0;
7971 cliquenum = 0;
7972
7973 /* calculate maximum activity due to cliques */
7974 for( w = 0; w < len; ++w )
7975 {
7976 assert(clqpart[w] >= 0 && clqpart[w] <= w);
7977 if( clqpart[w] == cliquenum )
7978 {
7979 maxactduetoclq += weights[w + splitpos + 1];
7980 ++cliquenum;
7981 }
7982 }
7983
7984 /* all rear items are redundant due to clique information, if maxactduetoclq is smaller than the weight before,
7985 * so delete them and create for all clique the corresponding clique constraints and update the capacity
7986 */
7987 if( frontsum + maxactduetoclq <= capacity )
7988 {
7989 SCIP_VAR** clqvars;
7990 int nclqvars;
7991 int c;
7992
7993 assert(maxactduetoclq < weights[splitpos]);
7994
7995 SCIPdebugMsg(scip, "Found redundant variables in constraint <%s> due to clique information.\n", SCIPconsGetName(cons));
7996
7997 /* allocate temporary memory */
7998 SCIP_CALL( SCIPallocBufferArray(scip, &clqvars, len - nclq + 1) );
7999
8000 for( c = 0; c < nclq; ++c )
8001 {
8002 nclqvars = 0;
8003 for( w = 0; w < len; ++w )
8004 {
8005 if( clqpart[w] == c )
8006 {
8007 clqvars[nclqvars] = vars[w + splitpos + 1];
8008 ++nclqvars;
8009 }
8010 }
8011
8012 /* we found a real clique so extract this constraint, because we do not know who this information generated so */
8013 if( nclqvars > 1 )
8014 {
8015 SCIP_CONS* cliquecons;
8016 char name[SCIP_MAXSTRLEN];
8017
8018 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_clq_%" SCIP_LONGINT_FORMAT "_%d", SCIPconsGetName(cons), capacity, c);
8019 SCIP_CALL( SCIPcreateConsSetpack(scip, &cliquecons, name, nclqvars, clqvars,
8020 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
8021 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
8022 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
8023 SCIPconsIsStickingAtNode(cons)) );
8024 SCIPdebugMsg(scip, " -> adding clique constraint: ");
8025 SCIPdebugPrintCons(scip, cliquecons, NULL);
8026 SCIP_CALL( SCIPaddCons(scip, cliquecons) );
8027 SCIP_CALL( SCIPreleaseCons(scip, &cliquecons) );
8028 ++(*naddconss);
8029 }
8030 }
8031
8032 /* delete items and update capacity */
8033 for( w = nvars - 1; w > splitpos; --w )
8034 {
8035 SCIP_CALL( delCoefPos(scip, cons, w) );
8036 ++(*nchgcoefs);
8037 }
8038 consdata->capacity -= maxactduetoclq;
8039 assert(frontsum <= consdata->capacity);
8040 ++(*nchgsides);
8041
8042 assert(w == splitpos);
8043
8044 /* renew weights pointer */
8045 weights = consdata->weights;
8046
8047 /* division by greatest common divisor */
8048 gcd = weights[w];
8049 for( ; w >= 0 && gcd > 1; --w )
8050 {
8051 gcd = SCIPcalcGreComDiv(gcd, weights[w]);
8052 }
8053
8054 /* normalize if possible */
8055 if( gcd > 1 )
8056 {
8057 for( w = splitpos; w >= 0; --w )
8058 {
8059 consdataChgWeight(consdata, w, weights[w]/gcd);
8060 }
8061 (*nchgcoefs) += nvars;
8062
8063 consdata->capacity /= gcd;
8064 ++(*nchgsides);
8065 }
8066
8067 /* free temporary memory */
8068 SCIPfreeBufferArray(scip, &clqvars);
8069
8070 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
8071 * weight must not be sorted by their index
8072 */
8073 #ifndef NDEBUG
8074 for( w = consdata->nvars - 1; w > 0; --w )
8075 assert(weights[w] <= weights[w - 1]);
8076 #endif
8077 }
8078 }
8079
8080 /* free temporary memory */
8081 SCIPfreeBufferArray(scip, &clqpart);
8082 }
8083
8084 return SCIP_OKAY;
8085 }
8086
8087 /* detect redundant variables which always fits into the knapsack
8088 *
8089 * i.e. 5x1 + 5x2 + 5x3 + 2x4 + 1x5 <= 13 => x4, x5 always fits into the knapsack, so we can delete them
8090 *
8091 * i.e. 5x1 + 5x2 + 5x3 + 2x4 + 1x5 <= 8 and we have the cliqueinformation (x1,x2,x3) is a clique
8092 * => x4, x5 always fits into the knapsack, so we can delete them
8093 *
8094 * i.e. 5x1 + 5x2 + 5x3 + 1x4 + 1x5 <= 6 and we have the cliqueinformation (x1,x2,x3) is a clique and (x4,x5) too
8095 * => we create the set partitioning constraint x4 + x5 <= 1 and delete them in this knapsack
8096 */
8097 static
detectRedundantVars(SCIP * scip,SCIP_CONS * cons,int * ndelconss,int * nchgcoefs,int * nchgsides,int * naddconss)8098 SCIP_RETCODE detectRedundantVars(
8099 SCIP* scip, /**< SCIP data structure */
8100 SCIP_CONS* cons, /**< knapsack constraint */
8101 int* ndelconss, /**< pointer to store the amount of deleted constraints */
8102 int* nchgcoefs, /**< pointer to store the amount of changed coefficients */
8103 int* nchgsides, /**< pointer to store the amount of changed sides */
8104 int* naddconss /**< pointer to count number of added constraints */
8105 )
8106 {
8107 SCIP_CONSHDLRDATA* conshdlrdata;
8108 SCIP_CONSDATA* consdata;
8109 SCIP_VAR** vars;
8110 SCIP_Longint* weights;
8111 SCIP_Longint capacity;
8112 SCIP_Longint sum;
8113 int noldchgcoefs;
8114 int nvars;
8115 int v;
8116 int w;
8117
8118 assert(scip != NULL);
8119 assert(cons != NULL);
8120 assert(ndelconss != NULL);
8121 assert(nchgcoefs != NULL);
8122 assert(nchgsides != NULL);
8123 assert(naddconss != NULL);
8124
8125 consdata = SCIPconsGetData(cons);
8126 assert(consdata != NULL);
8127 assert(consdata->nvars >= 2);
8128 assert(consdata->weightsum > consdata->capacity);
8129
8130 noldchgcoefs = *nchgcoefs;
8131 vars = consdata->vars;
8132 weights = consdata->weights;
8133 nvars = consdata->nvars;
8134 capacity = consdata->capacity;
8135 sum = 0;
8136
8137 /* search for maximal fitting items */
8138 for( v = 0; v < nvars && sum + weights[v] <= capacity; ++v )
8139 sum += weights[v];
8140
8141 assert(v < nvars);
8142
8143 /* all but one variable fit into the knapsack, so we can upgrade this constraint to a logicor */
8144 if( v == nvars - 1 )
8145 {
8146 SCIP_CALL( upgradeCons(scip, cons, ndelconss, naddconss) );
8147 assert(SCIPconsIsDeleted(cons));
8148
8149 return SCIP_OKAY;
8150 }
8151
8152 if( v < nvars - 1 )
8153 {
8154 /* try to delete variables */
8155 SCIP_CALL( deleteRedundantVars(scip, cons, sum, v, nchgcoefs, nchgsides, naddconss) );
8156 assert(consdata->nvars > 1);
8157
8158 /* all but one variable fit into the knapsack, so we can upgrade this constraint to a logicor */
8159 if( v == consdata->nvars - 1 )
8160 {
8161 SCIP_CALL( upgradeCons(scip, cons, ndelconss, naddconss) );
8162 assert(SCIPconsIsDeleted(cons));
8163 }
8164
8165 return SCIP_OKAY;
8166 }
8167
8168 /* if we already found some redundant variables, stop here */
8169 if( *nchgcoefs > noldchgcoefs )
8170 return SCIP_OKAY;
8171
8172 assert(vars == consdata->vars);
8173 assert(weights == consdata->weights);
8174 assert(nvars == consdata->nvars);
8175 assert(capacity == consdata->capacity);
8176
8177 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
8178 assert(conshdlrdata != NULL);
8179 /* calculate clique partition */
8180 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, TRUE, FALSE) );
8181
8182 /* check for real existing cliques */
8183 if( consdata->cliquepartition[v] < v )
8184 {
8185 SCIP_Longint sumfront;
8186 SCIP_Longint maxactduetoclqfront;
8187 int* clqpart;
8188 int cliquenum;
8189
8190 sumfront = 0;
8191 maxactduetoclqfront = 0;
8192
8193 clqpart = consdata->cliquepartition;
8194 cliquenum = 0;
8195
8196 /* calculate maximal activity due to cliques */
8197 for( w = 0; w < nvars; ++w )
8198 {
8199 assert(clqpart[w] >= 0 && clqpart[w] <= w);
8200 if( clqpart[w] == cliquenum )
8201 {
8202 if( maxactduetoclqfront + weights[w] <= capacity )
8203 {
8204 maxactduetoclqfront += weights[w];
8205 ++cliquenum;
8206 }
8207 else
8208 break;
8209 }
8210 sumfront += weights[w];
8211 }
8212 assert(w >= v);
8213
8214 /* if all items fit, then delete the whole constraint but create clique constraints which led to this
8215 * information
8216 */
8217 if( conshdlrdata->disaggregation && w == nvars )
8218 {
8219 SCIP_VAR** clqvars;
8220 int nclqvars;
8221 int c;
8222 int ncliques;
8223
8224 assert(maxactduetoclqfront <= capacity);
8225
8226 SCIPdebugMsg(scip, "Found redundant constraint <%s> due to clique information.\n", SCIPconsGetName(cons));
8227
8228 ncliques = consdata->ncliques;
8229
8230 /* allocate temporary memory */
8231 SCIP_CALL( SCIPallocBufferArray(scip, &clqvars, nvars - ncliques + 1) );
8232
8233 for( c = 0; c < ncliques; ++c )
8234 {
8235 nclqvars = 0;
8236 for( w = 0; w < nvars; ++w )
8237 {
8238 if( clqpart[w] == c )
8239 {
8240 clqvars[nclqvars] = vars[w];
8241 ++nclqvars;
8242 }
8243 }
8244
8245 /* we found a real clique so extract this constraint, because we do not know who this information generated so */
8246 if( nclqvars > 1 )
8247 {
8248 SCIP_CONS* cliquecons;
8249 char name[SCIP_MAXSTRLEN];
8250
8251 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_clq_%" SCIP_LONGINT_FORMAT "_%d", SCIPconsGetName(cons), capacity, c);
8252 SCIP_CALL( SCIPcreateConsSetpack(scip, &cliquecons, name, nclqvars, clqvars,
8253 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
8254 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
8255 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
8256 SCIPconsIsStickingAtNode(cons)) );
8257 SCIPdebugMsg(scip, " -> adding clique constraint: ");
8258 SCIPdebugPrintCons(scip, cliquecons, NULL);
8259 SCIP_CALL( SCIPaddCons(scip, cliquecons) );
8260 SCIP_CALL( SCIPreleaseCons(scip, &cliquecons) );
8261 ++(*naddconss);
8262 }
8263 }
8264
8265 /* delete old constraint */
8266 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
8267 ++(*ndelconss);
8268
8269 SCIPfreeBufferArray(scip, &clqvars);
8270
8271 return SCIP_OKAY;
8272 }
8273
8274 if( w > v && w < nvars - 1 )
8275 {
8276 /* try to delete variables */
8277 SCIP_CALL( deleteRedundantVars(scip, cons, sumfront, w, nchgcoefs, nchgsides, naddconss) );
8278 }
8279 }
8280
8281 return SCIP_OKAY;
8282 }
8283
8284 /** divides weights by their greatest common divisor and divides capacity by the same value, rounding down the result */
8285 static
normalizeWeights(SCIP_CONS * cons,int * nchgcoefs,int * nchgsides)8286 void normalizeWeights(
8287 SCIP_CONS* cons, /**< knapsack constraint */
8288 int* nchgcoefs, /**< pointer to count total number of changed coefficients */
8289 int* nchgsides /**< pointer to count number of side changes */
8290 )
8291 {
8292 SCIP_CONSDATA* consdata;
8293 SCIP_Longint gcd;
8294 int i;
8295
8296 assert(nchgcoefs != NULL);
8297 assert(nchgsides != NULL);
8298 assert(!SCIPconsIsModifiable(cons));
8299
8300 consdata = SCIPconsGetData(cons);
8301 assert(consdata != NULL);
8302 assert(consdata->row == NULL); /* we are in presolve, so no LP row exists */
8303 assert(consdata->onesweightsum == 0); /* all fixed variables should have been removed */
8304 assert(consdata->weightsum > consdata->capacity); /* otherwise, the constraint is redundant */
8305 assert(consdata->nvars >= 1);
8306
8307 /* sort items, because we can stop earlier if the smaller weights are evaluated first */
8308 sortItems(consdata);
8309
8310 gcd = consdata->weights[consdata->nvars-1];
8311 for( i = consdata->nvars-2; i >= 0 && gcd >= 2; --i )
8312 {
8313 assert(SCIPvarGetLbLocal(consdata->vars[i]) < 0.5);
8314 assert(SCIPvarGetUbLocal(consdata->vars[i]) > 0.5); /* all fixed variables should have been removed */
8315
8316 gcd = SCIPcalcGreComDiv(gcd, consdata->weights[i]);
8317 }
8318
8319 if( gcd >= 2 )
8320 {
8321 SCIPdebugMessage("knapsack constraint <%s>: dividing weights by %" SCIP_LONGINT_FORMAT "\n", SCIPconsGetName(cons), gcd);
8322
8323 for( i = 0; i < consdata->nvars; ++i )
8324 {
8325 consdataChgWeight(consdata, i, consdata->weights[i]/gcd);
8326 }
8327 consdata->capacity /= gcd;
8328 (*nchgcoefs) += consdata->nvars;
8329 (*nchgsides)++;
8330
8331 /* weight should still be sorted, because the reduction preserves this */
8332 #ifndef NDEBUG
8333 for( i = consdata->nvars - 1; i > 0; --i )
8334 assert(consdata->weights[i] <= consdata->weights[i - 1]);
8335 #endif
8336 consdata->sorted = TRUE;
8337 }
8338 }
8339
8340 /** dual weights tightening for knapsack constraints
8341 *
8342 * 1. a) check if all two pairs exceed the capacity, then we can upgrade this constraint to a set-packing constraint
8343 * b) check if all but the smallest weight fit into the knapsack, then we can upgrade this constraint to a logicor
8344 * constraint
8345 *
8346 * 2. check if besides big coefficients, that fit only by itself, for a certain amount of variables all combination of
8347 * these are a minimal cover, then might reduce the weights and the capacity, e.g.
8348 *
8349 * +219y1 + 180y2 + 74x1 + 70x2 + 63x3 + 62x4 + 53x5 <= 219 <=> 3y1 + 3y2 + x1 + x2 + x3 + x4 + x5 <= 3
8350 *
8351 * 3. use the duality between a^Tx <= capacity <=> a^T~x >= weightsum - capacity to tighten weights, e.g.
8352 *
8353 * 11x1 + 10x2 + 7x3 + 7x4 + 5x5 <= 27 <=> 11~x1 + 10~x2 + 7~x3 + 7~x4 + 5~x5 >= 13
8354 *
8355 * the above constraint can be changed to 8~x1 + 8~x2 + 6.5~x3 + 6.5~x4 + 5~x5 >= 13
8356 *
8357 * 16~x1 + 16~x2 + 13~x3 + 13~x4 + 10~x5 >= 26 <=> 16x1 + 16x2 + 13x3 + 13x4 + 10x5 <= 42
8358 */
8359 static
dualWeightsTightening(SCIP * scip,SCIP_CONS * cons,int * ndelconss,int * nchgcoefs,int * nchgsides,int * naddconss)8360 SCIP_RETCODE dualWeightsTightening(
8361 SCIP* scip, /**< SCIP data structure */
8362 SCIP_CONS* cons, /**< knapsack constraint */
8363 int* ndelconss, /**< pointer to store the amount of deleted constraints */
8364 int* nchgcoefs, /**< pointer to store the amount of changed coefficients */
8365 int* nchgsides, /**< pointer to store the amount of changed sides */
8366 int* naddconss /**< pointer to count number of added constraints */
8367 )
8368 {
8369 SCIP_CONSDATA* consdata;
8370 SCIP_Longint* weights;
8371 SCIP_Longint dualcapacity;
8372 SCIP_Longint reductionsum;
8373 SCIP_Longint capacity;
8374 SCIP_Longint exceedsum;
8375 int oldnchgcoefs;
8376 int nvars;
8377 int vbig;
8378 int v;
8379 int w;
8380 #ifndef NDEBUG
8381 int oldnchgsides;
8382 #endif
8383
8384 assert(scip != NULL);
8385 assert(cons != NULL);
8386 assert(ndelconss != NULL);
8387 assert(nchgcoefs != NULL);
8388 assert(nchgsides != NULL);
8389 assert(naddconss != NULL);
8390
8391 #ifndef NDEBUG
8392 oldnchgsides = *nchgsides;
8393 #endif
8394
8395 consdata = SCIPconsGetData(cons);
8396 assert(consdata != NULL);
8397 assert(consdata->weightsum > consdata->capacity);
8398 assert(consdata->nvars >= 2);
8399 assert(consdata->sorted);
8400
8401 /* constraint should be merged */
8402 assert(consdata->merged);
8403
8404 nvars = consdata->nvars;
8405 weights = consdata->weights;
8406 capacity = consdata->capacity;
8407
8408 oldnchgcoefs = *nchgcoefs;
8409
8410 /* case 1. */
8411 if( weights[nvars - 1] + weights[nvars - 2] > capacity )
8412 {
8413 SCIP_CONS* newcons;
8414
8415 /* two variable are enough to exceed the constraint, so we can update it to a set-packing
8416 *
8417 * e.g. 5x1 + 4x2 + 3x3 <= 5 <=> x1 + x2 + x3 <= 1
8418 */
8419 SCIPdebugMsg(scip, "upgrading knapsack constraint <%s> to a set-packing constraint", SCIPconsGetName(cons));
8420
8421 SCIP_CALL( SCIPcreateConsSetpack(scip, &newcons, SCIPconsGetName(cons), consdata->nvars, consdata->vars,
8422 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
8423 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
8424 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
8425 SCIPconsIsStickingAtNode(cons)) );
8426
8427 SCIP_CALL( SCIPaddCons(scip, newcons) );
8428 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
8429 ++(*naddconss);
8430
8431 SCIP_CALL( SCIPdelCons(scip, cons) );
8432 ++(*ndelconss);
8433
8434 return SCIP_OKAY;
8435 }
8436
8437 /* all but one variable fit into the knapsack, so we can upgrade this constraint to a logicor */
8438 if( consdata->weightsum - weights[nvars - 1] <= consdata->capacity )
8439 {
8440 SCIP_CALL( upgradeCons(scip, cons, ndelconss, naddconss) );
8441 assert(SCIPconsIsDeleted(cons));
8442
8443 return SCIP_OKAY;
8444 }
8445
8446 /* early termination, if the pair with biggest coeffcients together does not exceed the dualcapacity */
8447 /* @todo might be changed/removed when improving the coeffcients tightening */
8448 if( consdata->weightsum - capacity > weights[0] + weights[1] )
8449 return SCIP_OKAY;
8450
8451 /* case 2. */
8452
8453 v = 0;
8454
8455 /* @todo generalize the following algorithm for several parts of the knapsack
8456 *
8457 * the following is done without looking at the dualcapacity; it is enough to check whether for a certain amount of
8458 * variables each combination is a minimal cover, some examples
8459 *
8460 * +74x1 + 70x2 + 63x3 + 62x4 + 53x5 <= 219 <=> 74~x1 + 70~x2 + 63~x3 + 62~x4 + 53~x5 >= 103
8461 * <=> ~x1 + ~x2 + ~x3 + ~x4 + ~x5 >= 2
8462 * <=> x1 + x2 + x3 + x4 + x5 <= 3
8463 *
8464 * +219y1 + 180y_2 +74x1 + 70x2 + 63x3 + 62x4 + 53x5 <= 219 <=> 3y1 + 3y2 + x1 + x2 + x3 + x4 + x5 <= 3
8465 *
8466 */
8467
8468 /* determine big weights that fit only by itself */
8469 while( v < nvars && weights[v] + weights[nvars - 1] > capacity )
8470 ++v;
8471
8472 vbig = v;
8473 assert(vbig < nvars - 1);
8474 exceedsum = 0;
8475
8476 /* determine the amount needed to exceed the capacity */
8477 while( v < nvars && exceedsum <= capacity )
8478 {
8479 exceedsum += weights[v];
8480 ++v;
8481 }
8482
8483 /* if we exceeded the capacity we might reduce the weights */
8484 if( exceedsum > capacity )
8485 {
8486 assert(vbig > 0 || v < nvars);
8487
8488 /* all small weights were needed to exceed the capacity */
8489 if( v == nvars )
8490 {
8491 SCIP_Longint newweight = (SCIP_Longint)nvars - vbig - 1;
8492 assert(newweight > 0);
8493
8494 /* reduce big weights */
8495 for( v = 0; v < vbig; ++v )
8496 {
8497 if( weights[v] > newweight )
8498 {
8499 consdataChgWeight(consdata, v, newweight);
8500 ++(*nchgcoefs);
8501 }
8502 }
8503
8504 /* reduce small weights */
8505 for( ; v < nvars; ++v )
8506 {
8507 if( weights[v] > 1 )
8508 {
8509 consdataChgWeight(consdata, v, 1LL);
8510 ++(*nchgcoefs);
8511 }
8512 }
8513
8514 consdata->capacity = newweight;
8515
8516 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
8517 * weight must not be sorted by their index
8518 */
8519 #ifndef NDEBUG
8520 for( v = nvars - 1; v > 0; --v )
8521 assert(weights[v] <= weights[v-1]);
8522 #endif
8523
8524 return SCIP_OKAY;
8525 }
8526 /* a certain amount of small variables exceed the capacity, so check if this holds for all combinations of the
8527 * small weights
8528 */
8529 else
8530 {
8531 SCIP_Longint exceedsumback = 0;
8532 int nexceed = v - vbig;
8533
8534 assert(nexceed > 1);
8535
8536 /* determine weightsum of the same amount as before but of the smallest weight */
8537 for( w = nvars - 1; w >= nvars - nexceed; --w )
8538 exceedsumback += weights[w];
8539
8540 assert(w >= 0);
8541
8542 /* if the same amount but with the smallest possible weights also exceed the capacity, it holds for all
8543 * combinations of all small weights
8544 */
8545 if( exceedsumback > capacity )
8546 {
8547 SCIP_Longint newweight = nexceed - 1;
8548
8549 /* taking out the smallest element needs to fit */
8550 assert(exceedsumback - weights[nvars - 1] <= capacity);
8551
8552 /* reduce big weights */
8553 for( v = 0; v < vbig; ++v )
8554 {
8555 if( weights[v] > newweight )
8556 {
8557 consdataChgWeight(consdata, v, newweight);
8558 ++(*nchgcoefs);
8559 }
8560 }
8561
8562 /* reduce small weights */
8563 for( ; v < nvars; ++v )
8564 {
8565 if( weights[v] > 1 )
8566 {
8567 consdataChgWeight(consdata, v, 1LL);
8568 ++(*nchgcoefs);
8569 }
8570 }
8571
8572 consdata->capacity = newweight;
8573
8574 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
8575 * weight must not be sorted by their index
8576 */
8577 #ifndef NDEBUG
8578 for( v = nvars - 1; v > 0; --v )
8579 assert(weights[v] <= weights[v-1]);
8580 #endif
8581 return SCIP_OKAY;
8582 }
8583 }
8584 }
8585 else
8586 {
8587 /* if the following assert fails we have either a redundant constraint or a set-packing constraint, this should
8588 * not happen here
8589 */
8590 assert(vbig > 0 && vbig < nvars);
8591
8592 /* either choose a big coefficients or all other variables
8593 *
8594 * 973x1 + 189x2 + 189x3 + 145x4 + 110x5 + 104x6 + 93x7 + 71x8 + 68x9 + 10x10 <= 979
8595 *
8596 * either choose x1, or all other variables (weightsum of x2 to x10 is 979 above), so we can tighten this
8597 * constraint to
8598 *
8599 * 9x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 <= 9
8600 */
8601
8602 if( weights[vbig - 1] > (SCIP_Longint)nvars - vbig || weights[vbig] > 1 )
8603 {
8604 SCIP_Longint newweight = (SCIP_Longint)nvars - vbig;
8605 #ifndef NDEBUG
8606 SCIP_Longint resweightsum = consdata->weightsum;
8607
8608 for( v = 0; v < vbig; ++v )
8609 resweightsum -= weights[v];
8610
8611 assert(exceedsum == resweightsum);
8612 #endif
8613 assert(newweight > 0);
8614
8615 /* reduce big weights */
8616 for( v = 0; v < vbig; ++v )
8617 {
8618 if( weights[v] > newweight )
8619 {
8620 consdataChgWeight(consdata, v, newweight);
8621 ++(*nchgcoefs);
8622 }
8623 }
8624
8625 /* reduce small weights */
8626 for( ; v < nvars; ++v )
8627 {
8628 if( weights[v] > 1 )
8629 {
8630 consdataChgWeight(consdata, v, 1LL);
8631 ++(*nchgcoefs);
8632 }
8633 }
8634
8635 consdata->capacity = newweight;
8636
8637 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
8638 * weight must not be sorted by their index
8639 */
8640 #ifndef NDEBUG
8641 for( v = nvars - 1; v > 0; --v )
8642 assert(weights[v] <= weights[v-1]);
8643 #endif
8644 return SCIP_OKAY;
8645 }
8646 }
8647
8648 /* case 3. */
8649
8650 dualcapacity = consdata->weightsum - capacity;
8651 reductionsum = 0;
8652 v = 0;
8653
8654 /* reduce big weights
8655 *
8656 * e.g. 11x0 + 11x1 + 10x2 + 10x3 <= 32 <=> 11~x0 + 11~x1 + 10~x2 + 10~x3 >= 10
8657 * <=> 10~x0 + 10~x1 + 10~x2 + 10~x3 >= 10
8658 * <=> x0 + x1 + x2 + x3 <= 3
8659 */
8660 while( weights[v] > dualcapacity )
8661 {
8662 reductionsum += (weights[v] - dualcapacity);
8663 consdataChgWeight(consdata, v, dualcapacity);
8664 ++v;
8665 assert(v < nvars);
8666 }
8667 (*nchgcoefs) += v;
8668
8669 /* skip weights equal to the dualcapacity, because we cannot change them */
8670 while( v < nvars && weights[v] == dualcapacity )
8671 ++v;
8672
8673 /* any negated variable out of the first n - 1 items is enough to fulfill the constraint, so we can update it to a logicor
8674 * after a possible removal of the last, redundant item
8675 *
8676 * e.g. 10x1 + 10x2 + 10x3 <= 20 <=> 10~x1 + 10~x2 + 10~x3 >= 10 <=> ~x1 + ~x2 + ~x3 >= 1
8677 */
8678 if( v >= nvars - 1 )
8679 {
8680 /* the last weight is not enough to satisfy the dual capacity -> remove this redundant item */
8681 if( v == nvars - 1 )
8682 {
8683 SCIP_CALL( delCoefPos(scip, cons, nvars - 1) );
8684 }
8685 SCIP_CALL( upgradeCons(scip, cons, ndelconss, naddconss) );
8686 assert(SCIPconsIsDeleted(cons));
8687
8688 return SCIP_OKAY;
8689 }
8690 else /* v < nvars - 1 <=> at least two items with weight smaller than the dual capacity */
8691 {
8692 /* @todo generalize the following algorithm for more than two variables */
8693
8694 if( weights[nvars - 1] + weights[nvars - 2] >= dualcapacity )
8695 {
8696 /* we have a dual-knapsack constraint where we either need to choose one variable out of a subset (big
8697 * coefficients) of all or two variables of the rest
8698 *
8699 * e.g. 9x1 + 9x2 + 6x3 + 4x4 <= 19 <=> 9~x1 + 9~x2 + 6~x3 + 4~x4 >= 9
8700 * <=> 2~x1 + 2~x2 + ~x3 + ~x4 >= 2
8701 * <=> 2x1 + 2x2 + x3 + x4 <= 4
8702 *
8703 * 3x1 + 3x2 + 2x3 + 2x4 + 2x5 + 2x6 + x7 <= 12 <=> 3~x1 + 3~x2 + 2~x3 + 2~x4 + 2~x5 + 2~x6 + ~x7 >= 3
8704 * <=> 2~x1 + 2~x2 + ~x3 + ~x4 + ~x5 + ~x6 + ~x7 >= 2
8705 * <=> 2 x1 + 2 x2 + x3 + x4 + x5 + x6 + x7 <= 7
8706 *
8707 */
8708 if( v > 0 && weights[nvars - 2] > 1 )
8709 {
8710 int ncoefchg = 0;
8711
8712 /* reduce all bigger weights */
8713 for( w = 0; w < v; ++w )
8714 {
8715 if( weights[w] > 2 )
8716 {
8717 consdataChgWeight(consdata, w, 2LL);
8718 ++ncoefchg;
8719 }
8720 else
8721 {
8722 assert(weights[0] == 2);
8723 assert(weights[v - 1] == 2);
8724 break;
8725 }
8726 }
8727
8728 /* reduce all smaller weights */
8729 for( w = v; w < nvars; ++w )
8730 {
8731 if( weights[w] > 1 )
8732 {
8733 consdataChgWeight(consdata, w, 1LL);
8734 ++ncoefchg;
8735 }
8736 }
8737 assert(ncoefchg > 0);
8738
8739 (*nchgcoefs) += ncoefchg;
8740
8741 /* correct the capacity */
8742 consdata->capacity = (-2 + v * 2 + nvars - v); /*lint !e647*/
8743 assert(consdata->capacity > 0);
8744 assert(weights[0] <= consdata->capacity);
8745 assert(consdata->weightsum > consdata->capacity);
8746 /* reset the reductionsum */
8747 reductionsum = 0;
8748 }
8749 else if( v == 0 )
8750 {
8751 assert(weights[nvars - 2] == 1);
8752 }
8753 }
8754 else
8755 {
8756 SCIP_Longint minweight = weights[nvars - 1];
8757 SCIP_Longint newweight = dualcapacity - minweight;
8758 SCIP_Longint restsumweights = 0;
8759 SCIP_Longint sumcoef;
8760 SCIP_Bool sumcoefcase = FALSE;
8761 int startv = v;
8762 int end;
8763 int k;
8764
8765 assert(weights[nvars - 1] + weights[nvars - 2] <= capacity);
8766
8767 /* reduce big weights of pairs that exceed the dualcapacity
8768 *
8769 * e.g. 9x1 + 9x2 + 6x3 + 4x4 + 4x5 + 4x6 <= 27 <=> 9~x1 + 9~x2 + 6~x3 + 4~x4 + 4~x5 + 4~x6 >= 9
8770 * <=> 9~x1 + 9~x2 + 5~x3 + 4~x4 + 4~x5 + 4~x6 >= 9
8771 * <=> 9x1 + 9x2 + 5x3 + 4x4 + 4x5 + 4x6 <= 27
8772 */
8773 while( weights[v] > newweight )
8774 {
8775 reductionsum += (weights[v] - newweight);
8776 consdataChgWeight(consdata, v, newweight);
8777 ++v;
8778 assert(v < nvars);
8779 }
8780 (*nchgcoefs) += (v - startv);
8781
8782 /* skip equal weights */
8783 while( weights[v] == newweight )
8784 ++v;
8785
8786 if( v > 0 )
8787 {
8788 for( w = v; w < nvars; ++w )
8789 restsumweights += weights[w];
8790 }
8791 else
8792 restsumweights = consdata->weightsum;
8793
8794 if( restsumweights < dualcapacity )
8795 {
8796 /* we found redundant variables, which does not influence the feasibility of any integral solution, e.g.
8797 *
8798 * +61x1 + 61x2 + 61x3 + 61x4 + 61x5 + 61x6 + 35x7 + 10x8 <= 350 <=>
8799 * +61~x1 + 61~x2 + 61~x3 + 61~x4 + 61~x5 + 61~x6 + 35~x7 + 10~x8 >= 61
8800 */
8801 if( startv == v )
8802 {
8803 /* remove redundant variables */
8804 for( w = nvars - 1; w >= v; --w )
8805 {
8806 SCIP_CALL( delCoefPos(scip, cons, v) );
8807 ++(*nchgcoefs);
8808 }
8809
8810 #ifndef NDEBUG
8811 /* each coefficients should exceed the dualcapacity by itself */
8812 for( ; w >= 0; --w )
8813 assert(weights[w] == dualcapacity);
8814 #endif
8815 /* for performance reasons we do not update the capacity(, i.e. reduce it by reductionsum) and directly
8816 * upgrade this constraint
8817 */
8818 SCIP_CALL( upgradeCons(scip, cons, ndelconss, naddconss) );
8819 assert(SCIPconsIsDeleted(cons));
8820
8821 return SCIP_OKAY;
8822 }
8823
8824 /* special case where we have three different coefficient types
8825 *
8826 * e.g. 9x1 + 9x2 + 6x3 + 6x4 + 4x5 + 4x6 <= 29 <=> 9~x1 + 9~x2 + 6~x3 + 6~x4 + 4~x5 + 4~x6 >= 9
8827 * <=> 9~x1 + 9~x2 + 5~x3 + 5~x4 + 4~x5 + 4~x6 >= 9
8828 * <=> 3~x1 + 3~x2 + 2~x3 + 2~x4 + ~x5 + ~x6 >= 3
8829 * <=> 3x1 + 3x2 + 2x3 + 2x4 + x5 + x6 <= 9
8830 */
8831 if( weights[v] > 1 || (weights[startv] > (SCIP_Longint)nvars - v) || (startv > 0 && weights[0] == (SCIP_Longint)nvars - v + 1) )
8832 {
8833 SCIP_Longint newcap;
8834
8835 /* adjust smallest coefficients, which all together do not exceed the dualcapacity */
8836 for( w = nvars - 1; w >= v; --w )
8837 {
8838 if( weights[w] > 1 )
8839 {
8840 consdataChgWeight(consdata, w, 1LL);
8841 ++(*nchgcoefs);
8842 }
8843 }
8844
8845 /* adjust middle sized coefficients, which when choosing also one small coefficients exceed the
8846 * dualcapacity
8847 */
8848 newweight = (SCIP_Longint)nvars - v;
8849 assert(newweight > 1);
8850 for( ; w >= startv; --w )
8851 {
8852 if( weights[w] > newweight )
8853 {
8854 consdataChgWeight(consdata, w, newweight);
8855 ++(*nchgcoefs);
8856 }
8857 else
8858 assert(weights[w] == newweight);
8859 }
8860
8861 /* adjust big sized coefficients, where each of them exceeds the dualcapacity by itself */
8862 ++newweight;
8863 assert(newweight > 2);
8864 for( ; w >= 0; --w )
8865 {
8866 if( weights[w] > newweight )
8867 {
8868 consdataChgWeight(consdata, w, newweight);
8869 ++(*nchgcoefs);
8870 }
8871 else
8872 assert(weights[w] == newweight);
8873 }
8874
8875 /* update the capacity */
8876 newcap = ((SCIP_Longint)startv - 1) * newweight + ((SCIP_Longint)v - startv) * (newweight - 1) + ((SCIP_Longint)nvars - v);
8877 if( consdata->capacity > newcap )
8878 {
8879 consdata->capacity = newcap;
8880 ++(*nchgsides);
8881 }
8882 else
8883 assert(consdata->capacity == newcap);
8884 }
8885 assert(weights[v] == 1 && (weights[startv] == (SCIP_Longint)nvars - v) && (startv == 0 || weights[0] == (SCIP_Longint)nvars - v + 1));
8886
8887 /* the new dualcapacity should still be equal to the (nvars - v + 1) */
8888 assert(consdata->weightsum - consdata->capacity == (SCIP_Longint)nvars - v + 1);
8889
8890 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
8891 * weight must not be sorted by their index
8892 */
8893 #ifndef NDEBUG
8894 for( w = nvars - 1; w > 0; --w )
8895 assert(weights[w] <= weights[w - 1]);
8896 #endif
8897 return SCIP_OKAY;
8898 }
8899
8900 /* check if all rear items have the same weight as the last one, so we cannot tighten the constraint further */
8901 end = nvars - 2;
8902 while( end >= 0 && weights[end] == weights[end + 1] )
8903 {
8904 assert(end >= v);
8905 --end;
8906 }
8907
8908 if( v >= end )
8909 goto TERMINATE;
8910
8911 end = nvars - 2;
8912
8913 /* can we stop early, another special reduction case might exist */
8914 if( 2 * weights[end] > dualcapacity )
8915 {
8916 restsumweights = 0;
8917
8918 /* determine capacity of the small items */
8919 for( w = end + 1; w < nvars; ++w )
8920 restsumweights += weights[w];
8921
8922 if( restsumweights * 2 <= dualcapacity )
8923 {
8924 /* check for further posssible reductions in the middle */
8925 while( v < end && restsumweights + weights[v] >= dualcapacity )
8926 ++v;
8927
8928 if( v >= end )
8929 goto TERMINATE;
8930
8931 /* dualcapacity is even, we can set the middle weights to dualcapacity/2 */
8932 if( (dualcapacity & 1) == 0 )
8933 {
8934 newweight = dualcapacity / 2;
8935
8936 /* set all middle coefficients */
8937 for( ; v <= end; ++v )
8938 {
8939 if( weights[v] > newweight )
8940 {
8941 reductionsum += (weights[v] - newweight);
8942 consdataChgWeight(consdata, v, newweight);
8943 ++(*nchgcoefs);
8944 }
8945 }
8946 }
8947 /* dualcapacity is odd, we can set the middle weights to dualcapacity but therefor need to multiply all
8948 * other coefficients by 2
8949 */
8950 else
8951 {
8952 /* correct the reductionsum */
8953 reductionsum *= 2;
8954
8955 /* multiply big coefficients by 2 */
8956 for( w = 0; w < v; ++w )
8957 {
8958 consdataChgWeight(consdata, w, weights[w] * 2);
8959 }
8960
8961 newweight = dualcapacity;
8962 /* set all middle coefficients */
8963 for( ; v <= end; ++v )
8964 {
8965 reductionsum += (2 * weights[v] - newweight);
8966 consdataChgWeight(consdata, v, newweight);
8967 }
8968
8969 /* multiply small coefficients by 2 */
8970 for( w = end + 1; w < nvars; ++w )
8971 {
8972 consdataChgWeight(consdata, w, weights[w] * 2);
8973 }
8974 (*nchgcoefs) += nvars;
8975
8976 dualcapacity *= 2;
8977 consdata->capacity *= 2;
8978 ++(*nchgsides);
8979 }
8980 }
8981
8982 goto TERMINATE;
8983 }
8984
8985 /* further reductions using the next possible coefficient sum
8986 *
8987 * e.g. 9x1 + 8x2 + 7x3 + 3x4 + x5 <= 19 <=> 9~x1 + 8~x2 + 7~x3 + 3~x4 + ~x5 >= 9
8988 * <=> 9~x1 + 8~x2 + 6~x3 + 3~x4 + ~x5 >= 9
8989 * <=> 9x1 + 8x2 + 6x3 + 3x4 + x5 <= 18
8990 */
8991 /* @todo loop for "k" can be extended, same coefficient when determine next sumcoef can be left out */
8992 for( k = 0; k < 4; ++k )
8993 {
8994 /* determine next minimal coefficient sum */
8995 switch( k )
8996 {
8997 case 0:
8998 sumcoef = weights[nvars - 1] + weights[nvars - 2];
8999 break;
9000 case 1:
9001 assert(nvars >= 3);
9002 sumcoef = weights[nvars - 1] + weights[nvars - 3];
9003 break;
9004 case 2:
9005 assert(nvars >= 4);
9006 if( weights[nvars - 1] + weights[nvars - 4] < weights[nvars - 2] + weights[nvars - 3] )
9007 {
9008 sumcoefcase = TRUE;
9009 sumcoef = weights[nvars - 1] + weights[nvars - 4];
9010 }
9011 else
9012 {
9013 sumcoefcase = FALSE;
9014 sumcoef = weights[nvars - 2] + weights[nvars - 3];
9015 }
9016 break;
9017 case 3:
9018 assert(nvars >= 5);
9019 if( sumcoefcase )
9020 {
9021 sumcoef = MIN(weights[nvars - 1] + weights[nvars - 5], weights[nvars - 2] + weights[nvars - 3]);
9022 }
9023 else
9024 {
9025 sumcoef = MIN(weights[nvars - 1] + weights[nvars - 4], weights[nvars - 1] + weights[nvars - 2] + weights[nvars - 3]);
9026 }
9027 break;
9028 default:
9029 return SCIP_ERROR;
9030 }
9031
9032 /* tighten next coefficients that, pair with the current small coefficient, exceed the dualcapacity */
9033 minweight = weights[end];
9034 while( minweight <= sumcoef )
9035 {
9036 newweight = dualcapacity - minweight;
9037 startv = v;
9038 assert(v < nvars);
9039
9040 /* @todo check for further reductions, when two times the minweight exceeds the dualcapacity */
9041 /* shrink big coefficients */
9042 while( weights[v] + minweight > dualcapacity && 2 * minweight <= dualcapacity )
9043 {
9044 reductionsum += (weights[v] - newweight);
9045 consdataChgWeight(consdata, v, newweight);
9046 ++v;
9047 assert(v < nvars);
9048 }
9049 (*nchgcoefs) += (v - startv);
9050
9051 /* skip unchangable weights */
9052 while( weights[v] + minweight == dualcapacity )
9053 {
9054 assert(v < nvars);
9055 ++v;
9056 }
9057
9058 --end;
9059 /* skip same end weights */
9060 while( end >= 0 && weights[end] == weights[end + 1] )
9061 --end;
9062
9063 if( v >= end )
9064 goto TERMINATE;
9065
9066 minweight = weights[end];
9067 }
9068
9069 if( v >= end )
9070 goto TERMINATE;
9071
9072 /* now check if a combination of small coefficients allows us to tighten big coefficients further */
9073 if( sumcoef < minweight )
9074 {
9075 minweight = sumcoef;
9076 newweight = dualcapacity - minweight;
9077 startv = v;
9078 assert(v < nvars);
9079
9080 /* shrink big coefficients */
9081 while( weights[v] + minweight > dualcapacity && 2 * minweight <= dualcapacity )
9082 {
9083 reductionsum += (weights[v] - newweight);
9084 consdataChgWeight(consdata, v, newweight);
9085 ++v;
9086 assert(v < nvars);
9087 }
9088 (*nchgcoefs) += (v - startv);
9089
9090 /* skip unchangable weights */
9091 while( weights[v] + minweight == dualcapacity )
9092 {
9093 assert(v < nvars);
9094 ++v;
9095 }
9096 }
9097
9098 if( v >= end )
9099 goto TERMINATE;
9100
9101 /* can we stop early, another special reduction case might exist */
9102 if( 2 * weights[end] > dualcapacity )
9103 {
9104 restsumweights = 0;
9105
9106 /* determine capacity of the small items */
9107 for( w = end + 1; w < nvars; ++w )
9108 restsumweights += weights[w];
9109
9110 if( restsumweights * 2 <= dualcapacity )
9111 {
9112 /* check for further posssible reductions in the middle */
9113 while( v < end && restsumweights + weights[v] >= dualcapacity )
9114 ++v;
9115
9116 if( v >= end )
9117 goto TERMINATE;
9118
9119 /* dualcapacity is even, we can set the middle weights to dualcapacity/2 */
9120 if( (dualcapacity & 1) == 0 )
9121 {
9122 newweight = dualcapacity / 2;
9123
9124 /* set all middle coefficients */
9125 for( ; v <= end; ++v )
9126 {
9127 if( weights[v] > newweight )
9128 {
9129 reductionsum += (weights[v] - newweight);
9130 consdataChgWeight(consdata, v, newweight);
9131 ++(*nchgcoefs);
9132 }
9133 }
9134 }
9135 /* dualcapacity is odd, we can set the middle weights to dualcapacity but therefor need to multiply all
9136 * other coefficients by 2
9137 */
9138 else
9139 {
9140 /* correct the reductionsum */
9141 reductionsum *= 2;
9142
9143 /* multiply big coefficients by 2 */
9144 for( w = 0; w < v; ++w )
9145 {
9146 consdataChgWeight(consdata, w, weights[w] * 2);
9147 }
9148
9149 newweight = dualcapacity;
9150 /* set all middle coefficients */
9151 for( ; v <= end; ++v )
9152 {
9153 reductionsum += (2 * weights[v] - newweight);
9154 consdataChgWeight(consdata, v, newweight);
9155 }
9156
9157 /* multiply small coefficients by 2 */
9158 for( w = end + 1; w < nvars; ++w )
9159 {
9160 consdataChgWeight(consdata, w, weights[w] * 2);
9161 }
9162 (*nchgcoefs) += nvars;
9163
9164 dualcapacity *= 2;
9165 consdata->capacity *= 2;
9166 ++(*nchgsides);
9167 }
9168 }
9169
9170 goto TERMINATE;
9171 }
9172
9173 /* cannot tighten any further */
9174 if( 2 * sumcoef > dualcapacity )
9175 goto TERMINATE;
9176 }
9177 }
9178 }
9179
9180 TERMINATE:
9181 /* correct capacity */
9182 if( reductionsum > 0 )
9183 {
9184 assert(v > 0);
9185
9186 consdata->capacity -= reductionsum;
9187 ++(*nchgsides);
9188
9189 assert(consdata->weightsum - dualcapacity == consdata->capacity);
9190 }
9191 assert(weights[0] <= consdata->capacity);
9192
9193 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal
9194 * weight must not be sorted by their index
9195 */
9196 #ifndef NDEBUG
9197 for( w = nvars - 1; w > 0; --w )
9198 assert(weights[w] <= weights[w - 1]);
9199 #endif
9200
9201 if( oldnchgcoefs < *nchgcoefs )
9202 {
9203 assert(!SCIPconsIsDeleted(cons));
9204
9205 /* it might be that we can divide the weights by their greatest common divisor */
9206 normalizeWeights(cons, nchgcoefs, nchgsides);
9207 }
9208 else
9209 {
9210 assert(oldnchgcoefs == *nchgcoefs);
9211 assert(oldnchgsides == *nchgsides);
9212 }
9213
9214 return SCIP_OKAY;
9215 }
9216
9217
9218 /** fixes variables with weights bigger than the capacity and delete redundant constraints, also sort weights */
9219 static
prepareCons(SCIP * scip,SCIP_CONS * cons,int * nfixedvars,int * ndelconss,int * nchgcoefs)9220 SCIP_RETCODE prepareCons(
9221 SCIP* scip, /**< SCIP data structure */
9222 SCIP_CONS* cons, /**< knapsack constraint */
9223 int* nfixedvars, /**< pointer to store the amount of fixed variables */
9224 int* ndelconss, /**< pointer to store the amount of deleted constraints */
9225 int* nchgcoefs /**< pointer to store the amount of changed coefficients */
9226 )
9227 {
9228 SCIP_VAR** vars;
9229 SCIP_CONSDATA* consdata;
9230 SCIP_Longint* weights;
9231 SCIP_Longint capacity;
9232 SCIP_Bool infeasible;
9233 SCIP_Bool fixed;
9234 int nvars;
9235 int v;
9236
9237 assert(scip != NULL);
9238 assert(cons != NULL);
9239 assert(nfixedvars != NULL);
9240 assert(ndelconss != NULL);
9241 assert(nchgcoefs != NULL);
9242
9243 consdata = SCIPconsGetData(cons);
9244 assert(consdata != NULL);
9245
9246 nvars = consdata->nvars;
9247
9248 /* no variables left, then delete constraint */
9249 if( nvars == 0 )
9250 {
9251 assert(consdata->capacity >= 0);
9252
9253 SCIP_CALL( SCIPdelCons(scip, cons) );
9254 ++(*ndelconss);
9255
9256 return SCIP_OKAY;
9257 }
9258
9259 /* sort items */
9260 sortItems(consdata);
9261
9262 vars = consdata->vars;
9263 weights = consdata->weights;
9264 capacity = consdata->capacity;
9265 v = 0;
9266
9267 /* check for weights bigger than the capacity */
9268 while( v < nvars && weights[v] > capacity )
9269 {
9270 SCIP_CALL( SCIPfixVar(scip, vars[v], 0.0, &infeasible, &fixed) );
9271 assert(!infeasible);
9272
9273 if( fixed )
9274 ++(*nfixedvars);
9275
9276 ++v;
9277 }
9278
9279 /* if we fixed at least one variable we need to delete them from the constraint */
9280 if( v > 0 )
9281 {
9282 if( v == nvars )
9283 {
9284 SCIP_CALL( SCIPdelCons(scip, cons) );
9285 ++(*ndelconss);
9286
9287 return SCIP_OKAY;
9288 }
9289
9290 /* delete all position from back to front */
9291 for( --v; v >= 0; --v )
9292 {
9293 SCIP_CALL( delCoefPos(scip, cons, v) );
9294 ++(*nchgcoefs);
9295 }
9296
9297 /* sort items again because of deletion */
9298 sortItems(consdata);
9299 assert(vars == consdata->vars);
9300 assert(weights == consdata->weights);
9301 }
9302 assert(consdata->sorted);
9303 assert(weights[0] <= capacity);
9304
9305 if( !SCIPisHugeValue(scip, (SCIP_Real) capacity) && consdata->weightsum <= capacity )
9306 {
9307 SCIP_CALL( SCIPdelCons(scip, cons) );
9308 ++(*ndelconss);
9309 }
9310
9311 return SCIP_OKAY;
9312 }
9313
9314
9315 /** tries to simplify weights and delete redundant variables in knapsack a^Tx <= capacity
9316 *
9317 * 1. use the duality between a^Tx <= capacity <=> -a^T~x <= capacity - weightsum to tighten weights, e.g.
9318 *
9319 * 11x1 + 10x2 + 7x3 + 5x4 + 5x5 <= 25 <=> -10~x1 - 10~x2 - 7~x3 - 5~x4 - 5~x5 <= -13
9320 *
9321 * the above constraint can be changed to
9322 *
9323 * -8~x1 - 8~x2 - 7~x3 - 5~x4 - 5~x5 <= -12 <=> 8x1 + 8x2 + 7x3 + 5x4 + 5x5 <= 20
9324 *
9325 * 2. if variables in a constraint do not affect the (in-)feasibility of the constraint, we can delete them, e.g.
9326 *
9327 * 7x1 + 6x2 + 5x3 + 5x4 + x5 + x6 <= 20 => x5 and x6 are redundant and can be removed
9328 *
9329 * 3. Tries to use gcd information an all but one weight to change this not-included weight and normalize the
9330 * constraint further, e.g.
9331 *
9332 * 9x1 + 6x2 + 6x3 + 5x4 <= 13 => 9x1 + 6x2 + 6x3 + 6x4 <= 12 => 3x1 + 2x2 + 2x3 + 2x4 <= 4 => 4x1 + 2x2 + 2x3 + 2x4 <= 4
9333 * => 2x1 + x2 + x3 + x4 <= 2
9334 * 9x1 + 6x2 + 6x3 + 7x4 <= 13 => 9x1 + 6x2 + 6x3 + 6x4 <= 12 => see above
9335 */
9336 static
simplifyInequalities(SCIP * scip,SCIP_CONS * cons,int * nfixedvars,int * ndelconss,int * nchgcoefs,int * nchgsides,int * naddconss,SCIP_Bool * cutoff)9337 SCIP_RETCODE simplifyInequalities(
9338 SCIP* scip, /**< SCIP data structure */
9339 SCIP_CONS* cons, /**< knapsack constraint */
9340 int* nfixedvars, /**< pointer to store the amount of fixed variables */
9341 int* ndelconss, /**< pointer to store the amount of deleted constraints */
9342 int* nchgcoefs, /**< pointer to store the amount of changed coefficients */
9343 int* nchgsides, /**< pointer to store the amount of changed sides */
9344 int* naddconss, /**< pointer to count number of added constraints */
9345 SCIP_Bool* cutoff /**< pointer to store whether the node can be cut off */
9346 )
9347 {
9348 SCIP_VAR** vars;
9349 SCIP_CONSDATA* consdata;
9350 SCIP_Longint* weights;
9351 SCIP_Longint restweight;
9352 SCIP_Longint newweight;
9353 SCIP_Longint weight;
9354 SCIP_Longint oldgcd;
9355 SCIP_Longint rest;
9356 SCIP_Longint gcd;
9357 int oldnchgcoefs;
9358 int oldnchgsides;
9359 int candpos;
9360 int candpos2;
9361 int offsetv;
9362 int nvars;
9363 int v;
9364
9365 assert(scip != NULL);
9366 assert(cons != NULL);
9367 assert(nfixedvars != NULL);
9368 assert(ndelconss != NULL);
9369 assert(nchgcoefs != NULL);
9370 assert(nchgsides != NULL);
9371 assert(naddconss != NULL);
9372 assert(cutoff != NULL);
9373 assert(!SCIPconsIsModifiable(cons));
9374
9375 consdata = SCIPconsGetData(cons);
9376 assert( consdata != NULL );
9377
9378 *cutoff = FALSE;
9379
9380 /* remove double enties and also combinations of active and negated variables */
9381 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
9382 assert(consdata->merged);
9383 if( *cutoff )
9384 return SCIP_OKAY;
9385
9386 assert(consdata->capacity >= 0);
9387
9388 /* fix variables with big coefficients and remove redundant constraints, sort weights */
9389 SCIP_CALL( prepareCons(scip, cons, nfixedvars, ndelconss, nchgcoefs) );
9390
9391 if( SCIPconsIsDeleted(cons) )
9392 return SCIP_OKAY;
9393
9394 if( !SCIPisHugeValue(scip, (SCIP_Real) consdata->capacity) )
9395 {
9396 /* 1. dual weights tightening */
9397 SCIP_CALL( dualWeightsTightening(scip, cons, ndelconss, nchgcoefs, nchgsides, naddconss) );
9398
9399 if( SCIPconsIsDeleted(cons) )
9400 return SCIP_OKAY;
9401 /* 2. delete redundant variables */
9402 SCIP_CALL( detectRedundantVars(scip, cons, ndelconss, nchgcoefs, nchgsides, naddconss) );
9403
9404 if( SCIPconsIsDeleted(cons) )
9405 return SCIP_OKAY;
9406 }
9407
9408 weights = consdata->weights;
9409 nvars = consdata->nvars;
9410
9411 #ifndef NDEBUG
9412 /* constraint might not be sorted, but the weights are already sorted */
9413 for( v = nvars - 1; v > 0; --v )
9414 assert(weights[v] <= weights[v-1]);
9415 #endif
9416
9417 /* determine greatest common divisor */
9418 gcd = weights[nvars - 1];
9419 for( v = nvars - 2; v >= 0 && gcd > 1; --v )
9420 {
9421 gcd = SCIPcalcGreComDiv(gcd, weights[v]);
9422 }
9423
9424 /* divide the constraint by their greatest common divisor */
9425 if( gcd >= 2 )
9426 {
9427 for( v = nvars - 1; v >= 0; --v )
9428 {
9429 consdataChgWeight(consdata, v, weights[v]/gcd);
9430 }
9431 (*nchgcoefs) += nvars;
9432
9433 consdata->capacity /= gcd;
9434 (*nchgsides)++;
9435 }
9436 assert(consdata->nvars == nvars);
9437
9438 /* weight should still be sorted, because the reduction preserves this, but corresponding variables with equal weight
9439 * must not be sorted by their index
9440 */
9441 #ifndef NDEBUG
9442 for( v = nvars - 1; v > 0; --v )
9443 assert(weights[v] <= weights[v-1]);
9444 #endif
9445
9446 /* 3. start gcd procedure for all variables */
9447 do
9448 {
9449 SCIPdebug( oldnchgcoefs = *nchgcoefs; )
9450 SCIPdebug( oldnchgsides = *nchgsides; )
9451
9452 vars = consdata->vars;
9453 weights = consdata->weights;
9454 nvars = consdata->nvars;
9455
9456 /* stop if we have two coefficients which are one in absolute value */
9457 if( weights[nvars - 1] == 1 && weights[nvars - 2] == 1 )
9458 return SCIP_OKAY;
9459
9460 v = 0;
9461 /* determine coefficients as big as the capacity, these we do not need to take into account when calculating the
9462 * gcd
9463 */
9464 while( weights[v] == consdata->capacity )
9465 {
9466 ++v;
9467 assert(v < nvars);
9468 }
9469
9470 /* all but one variable are as big as the capacity, this is handled elsewhere */
9471 if( v == nvars - 1 )
9472 return SCIP_OKAY;
9473
9474 offsetv = v;
9475
9476 gcd = -1;
9477 candpos = -1;
9478 candpos2 = -1;
9479
9480 /* calculate greatest common divisor over all integer and binary variables and determine the candidate where we might
9481 * change the coefficient
9482 */
9483 for( v = nvars - 1; v >= offsetv; --v )
9484 {
9485 weight = weights[v];
9486 assert(weight >= 1);
9487
9488 oldgcd = gcd;
9489
9490 if( gcd == -1 )
9491 {
9492 gcd = weights[v];
9493 assert(gcd >= 1);
9494 }
9495 else
9496 {
9497 /* calculate greatest common divisor for all variables */
9498 gcd = SCIPcalcGreComDiv(gcd, weight);
9499 }
9500
9501 /* if the greatest commmon divisor has become 1, we might have found the possible coefficient to change or we
9502 * can terminate
9503 */
9504 if( gcd == 1 )
9505 {
9506 /* found candidate */
9507 if( candpos == -1 )
9508 {
9509 gcd = oldgcd;
9510 candpos = v;
9511
9512 /* if both first coefficients have a gcd of 1, both are candidates for the coefficient change */
9513 if( v == nvars - 2 )
9514 candpos2 = v + 1;
9515 }
9516 /* two different variables lead to a gcd of one, so we cannot change a coefficient */
9517 else
9518 {
9519 if( candpos == v + 1 && candpos2 == v + 2 )
9520 {
9521 assert(candpos2 == nvars - 1);
9522
9523 /* take new candidates */
9524 candpos = candpos2;
9525
9526 /* recalculate gcd from scratch */
9527 gcd = weights[v+1];
9528 assert(gcd >= 1);
9529
9530 /* calculate greatest common divisor for variables */
9531 gcd = SCIPcalcGreComDiv(gcd, weights[v]);
9532 if( gcd == 1 )
9533 return SCIP_OKAY;
9534 }
9535 else
9536 /* cannot determine a possible coefficient for reduction */
9537 return SCIP_OKAY;
9538 }
9539 }
9540 }
9541 assert(gcd >= 2);
9542
9543 /* we should have found one coefficient, that led to a gcd of 1, otherwise we could normalize the constraint
9544 * further
9545 */
9546 assert(((candpos >= offsetv) || (candpos == -1 && offsetv > 0)) && candpos < nvars);
9547
9548 /* determine the remainder of the capacity and the gcd */
9549 rest = consdata->capacity % gcd;
9550 assert(rest >= 0);
9551 assert(rest < gcd);
9552
9553 if( candpos == -1 )
9554 {
9555 /* we assume that the constraint was normalized */
9556 assert(rest > 0);
9557
9558 /* replace old with new capacity */
9559 consdata->capacity -= rest;
9560 ++(*nchgsides);
9561
9562 /* replace old big coefficients with new capacity */
9563 for( v = 0; v < offsetv; ++v )
9564 {
9565 consdataChgWeight(consdata, v, consdata->capacity);
9566 }
9567
9568 *nchgcoefs += offsetv;
9569 goto CONTINUE;
9570 }
9571
9572 /* determine the remainder of the coefficient candidate and the gcd */
9573 restweight = weights[candpos] % gcd;
9574 assert(restweight >= 1);
9575 assert(restweight < gcd);
9576
9577 /* calculate new coefficient */
9578 if( restweight > rest )
9579 newweight = weights[candpos] - restweight + gcd;
9580 else
9581 newweight = weights[candpos] - restweight;
9582
9583 assert(newweight == 0 || SCIPcalcGreComDiv(gcd, newweight) == gcd);
9584
9585 SCIPdebugMsg(scip, "gcd = %" SCIP_LONGINT_FORMAT ", rest = %" SCIP_LONGINT_FORMAT ", restweight = %" SCIP_LONGINT_FORMAT "; possible new weight of variable <%s> %" SCIP_LONGINT_FORMAT ", possible new capacity %" SCIP_LONGINT_FORMAT ", offset of coefficients as big as capacity %d\n", gcd, rest, restweight, SCIPvarGetName(vars[candpos]), newweight, consdata->capacity - rest, offsetv);
9586
9587 /* must not change weights and capacity if one variable would be removed and we have a big coefficient,
9588 * e.g., 11x1 + 6x2 + 6x3 + 5x4 <= 11 => gcd = 6, offsetv = 1 => newweight = 0, but we would lose x1 = 1 => x4 = 0
9589 */
9590 if( newweight == 0 && offsetv > 0 )
9591 return SCIP_OKAY;
9592
9593 if( rest > 0 )
9594 {
9595 /* replace old with new capacity */
9596 consdata->capacity -= rest;
9597 ++(*nchgsides);
9598
9599 /* replace old big coefficients with new capacity */
9600 for( v = 0; v < offsetv; ++v )
9601 {
9602 consdataChgWeight(consdata, v, consdata->capacity);
9603 }
9604
9605 *nchgcoefs += offsetv;
9606 }
9607
9608 if( newweight == 0 )
9609 {
9610 /* delete redundant coefficient */
9611 SCIP_CALL( delCoefPos(scip, cons, candpos) );
9612 assert(consdata->nvars == nvars - 1);
9613 --nvars;
9614 }
9615 else
9616 {
9617 /* replace old with new coefficient */
9618 consdataChgWeight(consdata, candpos, newweight);
9619 }
9620 ++(*nchgcoefs);
9621
9622 assert(consdata->vars == vars);
9623 assert(consdata->nvars == nvars);
9624 assert(consdata->weights == weights);
9625
9626 CONTINUE:
9627 /* now constraint can be normalized, dividing it by the gcd */
9628 for( v = nvars - 1; v >= 0; --v )
9629 {
9630 consdataChgWeight(consdata, v, weights[v]/gcd);
9631 }
9632 (*nchgcoefs) += nvars;
9633
9634 consdata->capacity /= gcd;
9635 ++(*nchgsides);
9636
9637 SCIPdebugPrintCons(scip, cons, NULL);
9638
9639 SCIPdebugMsg(scip, "we did %d coefficient changes and %d side changes on constraint %s when applying one round of the gcd algorithm\n", *nchgcoefs - oldnchgcoefs, *nchgsides - oldnchgsides, SCIPconsGetName(cons));
9640 }
9641 while( nvars >= 2 );
9642
9643 return SCIP_OKAY;
9644 }
9645
9646
9647 /** inserts an element into the list of binary zero implications */
9648 static
insertZerolist(SCIP * scip,int ** liftcands,int * nliftcands,int ** firstidxs,SCIP_Longint ** zeroweightsums,int ** zeroitems,int ** nextidxs,int * zeroitemssize,int * nzeroitems,int probindex,SCIP_Bool value,int knapsackidx,SCIP_Longint knapsackweight,SCIP_Bool * memlimitreached)9649 SCIP_RETCODE insertZerolist(
9650 SCIP* scip, /**< SCIP data structure */
9651 int** liftcands, /**< array of the lifting candidates */
9652 int* nliftcands, /**< number of lifting candidates */
9653 int** firstidxs, /**< array of first zeroitems indices */
9654 SCIP_Longint** zeroweightsums, /**< array of sums of weights of the implied-to-zero items */
9655 int** zeroitems, /**< pointer to zero items array */
9656 int** nextidxs, /**< pointer to array of next zeroitems indeces */
9657 int* zeroitemssize, /**< pointer to size of zero items array */
9658 int* nzeroitems, /**< pointer to length of zero items array */
9659 int probindex, /**< problem index of variable y in implication y == v -> x == 0 */
9660 SCIP_Bool value, /**< value v of variable y in implication */
9661 int knapsackidx, /**< index of variable x in knapsack */
9662 SCIP_Longint knapsackweight, /**< weight of variable x in knapsack */
9663 SCIP_Bool* memlimitreached /**< pointer to store whether the memory limit was reached */
9664 )
9665 {
9666 int nzeros;
9667
9668 assert(liftcands != NULL);
9669 assert(liftcands[value] != NULL);
9670 assert(nliftcands != NULL);
9671 assert(firstidxs != NULL);
9672 assert(firstidxs[value] != NULL);
9673 assert(zeroweightsums != NULL);
9674 assert(zeroweightsums[value] != NULL);
9675 assert(zeroitems != NULL);
9676 assert(nextidxs != NULL);
9677 assert(zeroitemssize != NULL);
9678 assert(nzeroitems != NULL);
9679 assert(*nzeroitems <= *zeroitemssize);
9680 assert(0 <= probindex && probindex < SCIPgetNVars(scip) - SCIPgetNContVars(scip));
9681 assert(memlimitreached != NULL);
9682
9683 nzeros = *nzeroitems;
9684
9685 /* allocate enough memory */
9686 if( nzeros == *zeroitemssize )
9687 {
9688 /* we explicitly construct the complete implication graph where the knapsack variables are involved;
9689 * this can be too huge - abort on memory limit
9690 */
9691 if( *zeroitemssize >= MAX_ZEROITEMS_SIZE )
9692 {
9693 SCIPdebugMsg(scip, "memory limit of %d bytes reached in knapsack preprocessing - abort collecting zero items\n",
9694 *zeroitemssize);
9695 *memlimitreached = TRUE;
9696 return SCIP_OKAY;
9697 }
9698 *zeroitemssize *= 2;
9699 *zeroitemssize = MIN(*zeroitemssize, MAX_ZEROITEMS_SIZE);
9700 SCIP_CALL( SCIPreallocBufferArray(scip, zeroitems, *zeroitemssize) );
9701 SCIP_CALL( SCIPreallocBufferArray(scip, nextidxs, *zeroitemssize) );
9702 }
9703 assert(nzeros < *zeroitemssize);
9704
9705 if( *memlimitreached )
9706 *memlimitreached = FALSE;
9707
9708 /* insert element */
9709 (*zeroitems)[nzeros] = knapsackidx;
9710 (*nextidxs)[nzeros] = firstidxs[value][probindex];
9711 if( firstidxs[value][probindex] == 0 )
9712 {
9713 liftcands[value][nliftcands[value]] = probindex;
9714 ++nliftcands[value];
9715 }
9716 firstidxs[value][probindex] = nzeros;
9717 ++(*nzeroitems);
9718 zeroweightsums[value][probindex] += knapsackweight;
9719
9720 return SCIP_OKAY;
9721 }
9722
9723 #define MAX_CLIQUELENGTH 50
9724 /** applies rule (3) of the weight tightening procedure, which can lift other variables into the knapsack:
9725 * (3) for a clique C let C(xi == v) := C \ {j: xi == v -> xj == 0}),
9726 * let cliqueweightsum(xi == v) := sum(W(C(xi == v)))
9727 * if cliqueweightsum(xi == v) < capacity:
9728 * - fixing variable xi to v would make the knapsack constraint redundant
9729 * - the weight of the variable or its negation (depending on v) can be increased as long as it has the same
9730 * redundancy effect:
9731 * wi' := capacity - cliqueweightsum(xi == v)
9732 * this rule can also be applied to binary variables not in the knapsack!
9733 */
9734 static
tightenWeightsLift(SCIP * scip,SCIP_CONS * cons,int * nchgcoefs,SCIP_Bool * cutoff)9735 SCIP_RETCODE tightenWeightsLift(
9736 SCIP* scip, /**< SCIP data structure */
9737 SCIP_CONS* cons, /**< knapsack constraint */
9738 int* nchgcoefs, /**< pointer to count total number of changed coefficients */
9739 SCIP_Bool* cutoff /**< pointer to store whether the node can be cut off */
9740 )
9741 {
9742 SCIP_CONSDATA* consdata;
9743 SCIP_VAR** binvars;
9744 int nbinvars;
9745 int* liftcands[2]; /* binary variables that have at least one entry in zeroitems */
9746 int* firstidxs[2]; /* first index in zeroitems for each binary variable/value pair, or zero for empty list */
9747 SCIP_Longint* zeroweightsums[2]; /* sums of weights of the implied-to-zero items */
9748 int* zeroitems; /* item number in knapsack that is implied to zero */
9749 int* nextidxs; /* next index in zeroitems for the same binary variable, or zero for end of list */
9750 int zeroitemssize;
9751 int nzeroitems;
9752 SCIP_Bool* zeroiteminserted[2];
9753 SCIP_Bool memlimitreached;
9754 int nliftcands[2];
9755 SCIP_Bool* cliqueused;
9756 SCIP_Bool* itemremoved;
9757 SCIP_Longint maxcliqueweightsum;
9758 SCIP_VAR** addvars;
9759 SCIP_Longint* addweights;
9760 SCIP_Longint addweightsum;
9761 int nvars;
9762 int cliquenum;
9763 int naddvars;
9764 int val;
9765 int i;
9766
9767 int* tmpindices;
9768 SCIP_Bool* tmpboolindices;
9769 int* tmpindices2;
9770 SCIP_Bool* tmpboolindices2;
9771 int* tmpindices3;
9772 SCIP_Bool* tmpboolindices3;
9773 int tmp;
9774 int tmp2;
9775 int tmp3;
9776 SCIP_CONSHDLR* conshdlr;
9777 SCIP_CONSHDLRDATA* conshdlrdata;
9778
9779 assert(nchgcoefs != NULL);
9780 assert(!SCIPconsIsModifiable(cons));
9781
9782 consdata = SCIPconsGetData(cons);
9783 assert(consdata != NULL);
9784 assert(consdata->row == NULL); /* we are in presolve, so no LP row exists */
9785 assert(consdata->weightsum > consdata->capacity); /* otherwise, the constraint is redundant */
9786 assert(consdata->nvars > 0);
9787 assert(consdata->merged);
9788
9789 nvars = consdata->nvars;
9790
9791 /* check if the knapsack has too many items/cliques for applying this costly method */
9792 if( (!consdata->cliquepartitioned && nvars > MAX_USECLIQUES_SIZE) || consdata->ncliques > MAX_USECLIQUES_SIZE )
9793 return SCIP_OKAY;
9794
9795 /* sort items, s.t. the heaviest one is in the first position */
9796 sortItems(consdata);
9797
9798 if( !consdata->cliquepartitioned && nvars > MAX_USECLIQUES_SIZE )
9799 return SCIP_OKAY;
9800
9801 /* we have to consider all integral variables since even integer and implicit integer variables can have binary bounds */
9802 nbinvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
9803 assert(nbinvars > 0);
9804 binvars = SCIPgetVars(scip);
9805
9806 /* get conshdlrdata to use cleared memory */
9807 conshdlr = SCIPconsGetHdlr(cons);
9808 assert(conshdlr != NULL);
9809 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9810 assert(conshdlrdata != NULL);
9811
9812 /* allocate temporary memory for the list of implied to zero variables */
9813 zeroitemssize = MIN(nbinvars, MAX_ZEROITEMS_SIZE); /* initial size of zeroitems buffer */
9814 SCIP_CALL( SCIPallocBufferArray(scip, &liftcands[0], nbinvars) );
9815 SCIP_CALL( SCIPallocBufferArray(scip, &liftcands[1], nbinvars) );
9816
9817 assert(conshdlrdata->ints1size > 0);
9818 assert(conshdlrdata->ints2size > 0);
9819 assert(conshdlrdata->longints1size > 0);
9820 assert(conshdlrdata->longints2size > 0);
9821
9822 /* next if conditions should normally not be true, because it means that presolving has created more binary variables
9823 * than binary + integer variables existed at the presolving initialization method, but for example if you would
9824 * transform all integers into their binary representation then it maybe happens
9825 */
9826 if( conshdlrdata->ints1size < nbinvars )
9827 {
9828 int oldsize = conshdlrdata->ints1size;
9829
9830 conshdlrdata->ints1size = nbinvars;
9831 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->ints1, oldsize, conshdlrdata->ints1size) );
9832 BMSclearMemoryArray(&(conshdlrdata->ints1[oldsize]), conshdlrdata->ints1size - oldsize); /*lint !e866*/
9833 }
9834 if( conshdlrdata->ints2size < nbinvars )
9835 {
9836 int oldsize = conshdlrdata->ints2size;
9837
9838 conshdlrdata->ints2size = nbinvars;
9839 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->ints2, oldsize, conshdlrdata->ints2size) );
9840 BMSclearMemoryArray(&(conshdlrdata->ints2[oldsize]), conshdlrdata->ints2size - oldsize); /*lint !e866*/
9841 }
9842 if( conshdlrdata->longints1size < nbinvars )
9843 {
9844 int oldsize = conshdlrdata->longints1size;
9845
9846 conshdlrdata->longints1size = nbinvars;
9847 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->longints1, oldsize, conshdlrdata->longints1size) );
9848 BMSclearMemoryArray(&(conshdlrdata->longints1[oldsize]), conshdlrdata->longints1size - oldsize); /*lint !e866*/
9849 }
9850 if( conshdlrdata->longints2size < nbinvars )
9851 {
9852 int oldsize = conshdlrdata->longints2size;
9853
9854 conshdlrdata->longints2size = nbinvars;
9855 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->longints2, oldsize, conshdlrdata->longints2size) );
9856 BMSclearMemoryArray(&(conshdlrdata->longints2[oldsize]), conshdlrdata->longints2size - oldsize); /*lint !e866*/
9857 }
9858
9859 firstidxs[0] = conshdlrdata->ints1;
9860 firstidxs[1] = conshdlrdata->ints2;
9861 zeroweightsums[0] = conshdlrdata->longints1;
9862 zeroweightsums[1] = conshdlrdata->longints2;
9863
9864 /* check for cleared arrays, all entries are zero */
9865 #ifndef NDEBUG
9866 for( tmp = nbinvars - 1; tmp >= 0; --tmp )
9867 {
9868 assert(firstidxs[0][tmp] == 0);
9869 assert(firstidxs[1][tmp] == 0);
9870 assert(zeroweightsums[0][tmp] == 0);
9871 assert(zeroweightsums[1][tmp] == 0);
9872 }
9873 #endif
9874
9875 SCIP_CALL( SCIPallocBufferArray(scip, &zeroitems, zeroitemssize) );
9876 SCIP_CALL( SCIPallocBufferArray(scip, &nextidxs, zeroitemssize) );
9877
9878 zeroitems[0] = -1; /* dummy element */
9879 nextidxs[0] = -1;
9880 nzeroitems = 1;
9881 nliftcands[0] = 0;
9882 nliftcands[1] = 0;
9883
9884 assert(conshdlrdata->bools1size > 0);
9885 assert(conshdlrdata->bools2size > 0);
9886
9887 /* next if conditions should normally not be true, because it means that presolving has created more binary variables
9888 * than binary + integer variables existed at the presolving initialization method, but for example if you would
9889 * transform all integers into their binary representation then it maybe happens
9890 */
9891 if( conshdlrdata->bools1size < nbinvars )
9892 {
9893 int oldsize = conshdlrdata->bools1size;
9894
9895 conshdlrdata->bools1size = nbinvars;
9896 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->bools1, oldsize, conshdlrdata->bools1size) );
9897 BMSclearMemoryArray(&(conshdlrdata->bools1[oldsize]), conshdlrdata->bools1size - oldsize); /*lint !e866*/
9898 }
9899 if( conshdlrdata->bools2size < nbinvars )
9900 {
9901 int oldsize = conshdlrdata->bools2size;
9902
9903 conshdlrdata->bools2size = nbinvars;
9904 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->bools2, oldsize, conshdlrdata->bools2size) );
9905 BMSclearMemoryArray(&(conshdlrdata->bools2[oldsize]), conshdlrdata->bools2size - oldsize); /*lint !e866*/
9906 }
9907
9908 zeroiteminserted[0] = conshdlrdata->bools1;
9909 zeroiteminserted[1] = conshdlrdata->bools2;
9910
9911 /* check for cleared arrays, all entries are zero */
9912 #ifndef NDEBUG
9913 for( tmp = nbinvars - 1; tmp >= 0; --tmp )
9914 {
9915 assert(zeroiteminserted[0][tmp] == 0);
9916 assert(zeroiteminserted[1][tmp] == 0);
9917 }
9918 #endif
9919
9920 SCIP_CALL( SCIPallocBufferArray(scip, &tmpboolindices3, consdata->nvars) );
9921 SCIP_CALL( SCIPallocBufferArray(scip, &tmpboolindices2, 2 * nbinvars) );
9922 SCIP_CALL( SCIPallocBufferArray(scip, &tmpindices3, consdata->nvars) );
9923 SCIP_CALL( SCIPallocBufferArray(scip, &tmpindices2, 2 * nbinvars) );
9924 SCIP_CALL( SCIPallocBufferArray(scip, &tmpindices, 2 * nbinvars) );
9925 SCIP_CALL( SCIPallocBufferArray(scip, &tmpboolindices, 2 * nbinvars) );
9926
9927 tmp2 = 0;
9928 tmp3 = 0;
9929
9930 memlimitreached = FALSE;
9931 for( i = 0; i < consdata->nvars && !memlimitreached; ++i )
9932 {
9933 SCIP_CLIQUE** cliques;
9934 SCIP_VAR* var;
9935 SCIP_Longint weight;
9936 SCIP_Bool value;
9937 int varprobindex;
9938 int ncliques;
9939 int j;
9940
9941 tmp = 0;
9942
9943 /* get corresponding active problem variable */
9944 var = consdata->vars[i];
9945 weight = consdata->weights[i];
9946 value = TRUE;
9947 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
9948 varprobindex = SCIPvarGetProbindex(var);
9949 assert(0 <= varprobindex && varprobindex < nbinvars);
9950
9951 /* update the zeroweightsum */
9952 zeroweightsums[!value][varprobindex] += weight; /*lint !e514*/
9953 tmpboolindices3[tmp3] = !value;
9954 tmpindices3[tmp3] = varprobindex;
9955 ++tmp3;
9956
9957 /* initialize the arrays of inserted zero items */
9958 /* first add the implications (~x == 1 -> x == 0) */
9959 {
9960 SCIP_Bool implvalue;
9961 int probindex;
9962
9963 probindex = SCIPvarGetProbindex(var);
9964 assert(0 <= probindex && probindex < nbinvars);
9965
9966 implvalue = !value;
9967
9968 /* insert the item into the list of the implied variable/value */
9969 assert( !zeroiteminserted[implvalue][probindex] );
9970
9971 if( firstidxs[implvalue][probindex] == 0 )
9972 {
9973 tmpboolindices2[tmp2] = implvalue;
9974 tmpindices2[tmp2] = probindex;
9975 ++tmp2;
9976 }
9977 SCIP_CALL( insertZerolist(scip, liftcands, nliftcands, firstidxs, zeroweightsums,
9978 &zeroitems, &nextidxs, &zeroitemssize, &nzeroitems, probindex, implvalue, i, weight,
9979 &memlimitreached) );
9980 zeroiteminserted[implvalue][probindex] = TRUE;
9981 tmpboolindices[tmp] = implvalue;
9982 tmpindices[tmp] = probindex;
9983 ++tmp;
9984 }
9985
9986 /* get the cliques where the knapsack item is member of with value 1 */
9987 ncliques = SCIPvarGetNCliques(var, value);
9988 cliques = SCIPvarGetCliques(var, value);
9989 for( j = 0; j < ncliques && !memlimitreached; ++j )
9990 {
9991 SCIP_VAR** cliquevars;
9992 SCIP_Bool* cliquevalues;
9993 int ncliquevars;
9994 int k;
9995
9996 ncliquevars = SCIPcliqueGetNVars(cliques[j]);
9997
9998 /* discard big cliques */
9999 if( ncliquevars > MAX_CLIQUELENGTH )
10000 continue;
10001
10002 cliquevars = SCIPcliqueGetVars(cliques[j]);
10003 cliquevalues = SCIPcliqueGetValues(cliques[j]);
10004
10005 for( k = ncliquevars - 1; k >= 0; --k )
10006 {
10007 SCIP_Bool implvalue;
10008 int probindex;
10009
10010 if( var == cliquevars[k] )
10011 continue;
10012
10013 probindex = SCIPvarGetProbindex(cliquevars[k]);
10014 if( probindex == -1 )
10015 continue;
10016
10017 assert(0 <= probindex && probindex < nbinvars);
10018 implvalue = cliquevalues[k];
10019
10020 /* insert the item into the list of the clique variable/value */
10021 if( !zeroiteminserted[implvalue][probindex] )
10022 {
10023 if( firstidxs[implvalue][probindex] == 0 )
10024 {
10025 tmpboolindices2[tmp2] = implvalue;
10026 tmpindices2[tmp2] = probindex;
10027 ++tmp2;
10028 }
10029
10030 SCIP_CALL( insertZerolist(scip, liftcands, nliftcands, firstidxs, zeroweightsums,
10031 &zeroitems, &nextidxs, &zeroitemssize, &nzeroitems, probindex, implvalue, i, weight,
10032 &memlimitreached) );
10033 zeroiteminserted[implvalue][probindex] = TRUE;
10034 tmpboolindices[tmp] = implvalue;
10035 tmpindices[tmp] = probindex;
10036 ++tmp;
10037
10038 if( memlimitreached )
10039 break;
10040 }
10041 }
10042 }
10043 /* clear zeroiteminserted */
10044 for( --tmp; tmp >= 0; --tmp)
10045 zeroiteminserted[tmpboolindices[tmp]][tmpindices[tmp]] = FALSE;
10046 }
10047 SCIPfreeBufferArray(scip, &tmpboolindices);
10048
10049 /* calculate the clique partition and the maximal sum of weights using the clique information */
10050 assert(consdata->sorted);
10051 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, TRUE, FALSE) );
10052
10053 assert(conshdlrdata->bools3size > 0);
10054
10055 /* next if condition should normally not be true, because it means that presolving has created more binary variables
10056 * in one constraint than binary + integer variables existed in the whole problem at the presolving initialization
10057 * method, but for example if you would transform all integers into their binary representation then it maybe happens
10058 */
10059 if( conshdlrdata->bools3size < consdata->nvars )
10060 {
10061 int oldsize = conshdlrdata->bools3size;
10062
10063 conshdlrdata->bools3size = consdata->nvars;;
10064 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->bools3, oldsize, conshdlrdata->bools3size) );
10065 BMSclearMemoryArray(&(conshdlrdata->bools3[oldsize]), conshdlrdata->bools3size - oldsize); /*lint !e866*/
10066 }
10067
10068 cliqueused = conshdlrdata->bools3;
10069
10070 /* check for cleared array, all entries are zero */
10071 #ifndef NDEBUG
10072 for( tmp = consdata->nvars - 1; tmp >= 0; --tmp )
10073 assert(cliqueused[tmp] == 0);
10074 #endif
10075
10076 maxcliqueweightsum = 0;
10077 tmp = 0;
10078
10079 /* calculates maximal weight of cliques */
10080 for( i = 0; i < consdata->nvars; ++i )
10081 {
10082 cliquenum = consdata->cliquepartition[i];
10083 assert(0 <= cliquenum && cliquenum < consdata->nvars);
10084
10085 if( !cliqueused[cliquenum] )
10086 {
10087 maxcliqueweightsum += consdata->weights[i];
10088 cliqueused[cliquenum] = TRUE;
10089 tmpindices[tmp] = cliquenum;
10090 ++tmp;
10091 }
10092 }
10093 /* clear cliqueused */
10094 for( --tmp; tmp >= 0; --tmp)
10095 cliqueused[tmp] = FALSE;
10096
10097 assert(conshdlrdata->bools4size > 0);
10098
10099 /* next if condition should normally not be true, because it means that presolving has created more binary variables
10100 * in one constraint than binary + integer variables existed in the whole problem at the presolving initialization
10101 * method, but for example if you would transform all integers into their binary representation then it maybe happens
10102 */
10103 if( conshdlrdata->bools4size < consdata->nvars )
10104 {
10105 int oldsize = conshdlrdata->bools4size;
10106
10107 conshdlrdata->bools4size = consdata->nvars;
10108 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->bools4, oldsize, conshdlrdata->bools4size) );
10109 BMSclearMemoryArray(&conshdlrdata->bools4[oldsize], conshdlrdata->bools4size - oldsize); /*lint !e866*/
10110 }
10111
10112 itemremoved = conshdlrdata->bools4;
10113
10114 /* check for cleared array, all entries are zero */
10115 #ifndef NDEBUG
10116 for( tmp = consdata->nvars - 1; tmp >= 0; --tmp )
10117 assert(itemremoved[tmp] == 0);
10118 #endif
10119
10120 /* for each binary variable xi and each fixing v, calculate the cliqueweightsum and update the weight of the
10121 * variable in the knapsack (this is sequence-dependent because the new or modified weights have to be
10122 * included in subsequent cliqueweightsum calculations)
10123 */
10124 SCIP_CALL( SCIPallocBufferArray(scip, &addvars, 2*nbinvars) );
10125 SCIP_CALL( SCIPallocBufferArray(scip, &addweights, 2*nbinvars) );
10126 naddvars = 0;
10127 addweightsum = 0;
10128 for( val = 0; val < 2 && addweightsum < consdata->capacity; ++val )
10129 {
10130 for( i = 0; i < nliftcands[val] && addweightsum < consdata->capacity; ++i )
10131 {
10132 SCIP_Longint cliqueweightsum;
10133 int probindex;
10134 int idx;
10135 int j;
10136
10137 tmp = 0;
10138
10139 probindex = liftcands[val][i];
10140 assert(0 <= probindex && probindex < nbinvars);
10141
10142 /* ignore empty zero lists and variables that cannot be lifted anyways */
10143 if( firstidxs[val][probindex] == 0
10144 || maxcliqueweightsum - zeroweightsums[val][probindex] + addweightsum >= consdata->capacity )
10145 continue;
10146
10147 /* mark the items that are implied to zero by setting the current variable to the current value */
10148 for( idx = firstidxs[val][probindex]; idx != 0; idx = nextidxs[idx] )
10149 {
10150 assert(0 < idx && idx < nzeroitems);
10151 assert(0 <= zeroitems[idx] && zeroitems[idx] < consdata->nvars);
10152 itemremoved[zeroitems[idx]] = TRUE;
10153 }
10154
10155 /* calculate the residual cliqueweight sum */
10156 cliqueweightsum = addweightsum; /* the previously added items are single-element cliques */
10157 for( j = 0; j < consdata->nvars; ++j )
10158 {
10159 cliquenum = consdata->cliquepartition[j];
10160 assert(0 <= cliquenum && cliquenum < consdata->nvars);
10161 if( !itemremoved[j] )
10162 {
10163 if( !cliqueused[cliquenum] )
10164 {
10165 cliqueweightsum += consdata->weights[j];
10166 cliqueused[cliquenum] = TRUE;
10167 tmpindices[tmp] = cliquenum;
10168 ++tmp;
10169 }
10170
10171 if( cliqueweightsum >= consdata->capacity )
10172 break;
10173 }
10174 }
10175
10176 /* check if the weight of the variable/value can be increased */
10177 if( cliqueweightsum < consdata->capacity )
10178 {
10179 SCIP_VAR* var;
10180 SCIP_Longint weight;
10181
10182 /* insert the variable (with value TRUE) in the list of additional items */
10183 assert(naddvars < 2*nbinvars);
10184 var = binvars[probindex];
10185 if( val == FALSE )
10186 {
10187 SCIP_CALL( SCIPgetNegatedVar(scip, var, &var) );
10188 }
10189 weight = consdata->capacity - cliqueweightsum;
10190 addvars[naddvars] = var;
10191 addweights[naddvars] = weight;
10192 addweightsum += weight;
10193 naddvars++;
10194
10195 SCIPdebugMsg(scip, "knapsack constraint <%s>: adding lifted item %" SCIP_LONGINT_FORMAT "<%s>\n",
10196 SCIPconsGetName(cons), weight, SCIPvarGetName(var));
10197 }
10198
10199 /* clear itemremoved */
10200 for( idx = firstidxs[val][probindex]; idx != 0; idx = nextidxs[idx] )
10201 {
10202 assert(0 < idx && idx < nzeroitems);
10203 assert(0 <= zeroitems[idx] && zeroitems[idx] < consdata->nvars);
10204 itemremoved[zeroitems[idx]] = FALSE;
10205 }
10206 /* clear cliqueused */
10207 for( --tmp; tmp >= 0; --tmp)
10208 cliqueused[tmpindices[tmp]] = FALSE;
10209 }
10210 }
10211
10212 /* clear part of zeroweightsums */
10213 for( --tmp3; tmp3 >= 0; --tmp3)
10214 zeroweightsums[tmpboolindices3[tmp3]][tmpindices3[tmp3]] = 0;
10215
10216 /* clear rest of zeroweightsums and firstidxs */
10217 for( --tmp2; tmp2 >= 0; --tmp2)
10218 {
10219 zeroweightsums[tmpboolindices2[tmp2]][tmpindices2[tmp2]] = 0;
10220 firstidxs[tmpboolindices2[tmp2]][tmpindices2[tmp2]] = 0;
10221 }
10222
10223 /* add all additional item weights */
10224 for( i = 0; i < naddvars; ++i )
10225 {
10226 SCIP_CALL( addCoef(scip, cons, addvars[i], addweights[i]) );
10227 }
10228 *nchgcoefs += naddvars;
10229
10230 if( naddvars > 0 )
10231 {
10232 /* if new items were added, multiple entries of the same variable are possible and we have to clean up the constraint */
10233 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
10234 }
10235
10236 /* free temporary memory */
10237 SCIPfreeBufferArray(scip, &addweights);
10238 SCIPfreeBufferArray(scip, &addvars);
10239 SCIPfreeBufferArray(scip, &tmpindices);
10240 SCIPfreeBufferArray(scip, &tmpindices2);
10241 SCIPfreeBufferArray(scip, &tmpindices3);
10242 SCIPfreeBufferArray(scip, &tmpboolindices2);
10243 SCIPfreeBufferArray(scip, &tmpboolindices3);
10244 SCIPfreeBufferArray(scip, &nextidxs);
10245 SCIPfreeBufferArray(scip, &zeroitems);
10246 SCIPfreeBufferArray(scip, &liftcands[1]);
10247 SCIPfreeBufferArray(scip, &liftcands[0]);
10248
10249 return SCIP_OKAY;
10250 }
10251
10252 /** tightens item weights and capacity in presolving:
10253 * given a knapsack sum(wi*xi) <= capacity
10254 * (1) let weightsum := sum(wi)
10255 * if weightsum - wi < capacity:
10256 * - not using item i would make the knapsack constraint redundant
10257 * - wi and capacity can be changed to have the same redundancy effect and the same results for
10258 * fixing xi to zero or one, but with a reduced wi and tightened capacity to tighten the LP relaxation
10259 * - change coefficients:
10260 * wi' := weightsum - capacity
10261 * capacity' := capacity - (wi - wi')
10262 * (2) increase weights from front to back(sortation is necessary) if there is no space left for another weight
10263 * - determine the four(can be adjusted) minimal weightsums of the knapsack, i.e. in increasing order
10264 * weights[nvars - 1], weights[nvars - 2], MIN(weights[nvars - 3], weights[nvars - 1] + weights[nvars - 2]),
10265 * MIN(MAX(weights[nvars - 3], weights[nvars - 1] + weights[nvars - 2]), weights[nvars - 4]), note that there
10266 * can be multiple times the same weight, this can be improved
10267 * - check if summing up a minimal weightsum with a big weight exceeds the capacity, then we can increase the big
10268 * weight, to capacity - lastmininmalweightsum, e.g. :
10269 * 19x1 + 15x2 + 10x3 + 5x4 + 5x5 <= 19
10270 * -> minimal weightsums: 5, 5, 10, 10
10271 * -> 15 + 5 > 19 => increase 15 to 19 - 0 = 19
10272 * -> 10 + 10 > 19 => increase 10 to 19 - 5 = 14, resulting in
10273 * 19x1 + 19x2 + 14x3 + 5x4 + 5x5 <= 19
10274 * (3) let W(C) be the maximal weight of clique C,
10275 * cliqueweightsum := sum(W(C))
10276 * if cliqueweightsum - W(C) < capacity:
10277 * - not using any item of C would make the knapsack constraint redundant
10278 * - weights wi, i in C, and capacity can be changed to have the same redundancy effect and the same results for
10279 * fixing xi, i in C, to zero or one, but with a reduced wi and tightened capacity to tighten the LP relaxation
10280 * - change coefficients:
10281 * delta := capacity - (cliqueweightsum - W(C))
10282 * wi' := max(wi - delta, 0)
10283 * capacity' := capacity - delta
10284 * This rule has to add the used cliques in order to ensure they are enforced - otherwise, the reduction might
10285 * introduce infeasible solutions.
10286 * (4) for a clique C let C(xi == v) := C \ {j: xi == v -> xj == 0}),
10287 * let cliqueweightsum(xi == v) := sum(W(C(xi == v)))
10288 * if cliqueweightsum(xi == v) < capacity:
10289 * - fixing variable xi to v would make the knapsack constraint redundant
10290 * - the weight of the variable or its negation (depending on v) can be increased as long as it has the same
10291 * redundancy effect:
10292 * wi' := capacity - cliqueweightsum(xi == v)
10293 * This rule can also be applied to binary variables not in the knapsack!
10294 * (5) if min{w} + wi > capacity:
10295 * - using item i would force to fix other items to zero
10296 * - wi can be increased to the capacity
10297 */
10298 static
tightenWeights(SCIP * scip,SCIP_CONS * cons,SCIP_PRESOLTIMING presoltiming,int * nchgcoefs,int * nchgsides,int * naddconss,int * ndelconss,SCIP_Bool * cutoff)10299 SCIP_RETCODE tightenWeights(
10300 SCIP* scip, /**< SCIP data structure */
10301 SCIP_CONS* cons, /**< knapsack constraint */
10302 SCIP_PRESOLTIMING presoltiming, /**< current presolving timing */
10303 int* nchgcoefs, /**< pointer to count total number of changed coefficients */
10304 int* nchgsides, /**< pointer to count number of side changes */
10305 int* naddconss, /**< pointer to count number of added constraints */
10306 int* ndelconss, /**< pointer to count number of deleted constraints */
10307 SCIP_Bool* cutoff /**< pointer to store whether the node can be cut off */
10308 )
10309 {
10310 SCIP_CONSHDLRDATA* conshdlrdata;
10311 SCIP_CONSDATA* consdata;
10312 SCIP_Longint* weights;
10313 SCIP_Longint sumcoef;
10314 SCIP_Longint capacity;
10315 SCIP_Longint newweight;
10316 SCIP_Longint maxweight;
10317 SCIP_Longint minweight;
10318 SCIP_Bool sumcoefcase = FALSE;
10319 int startpos;
10320 int backpos;
10321 int nvars;
10322 int pos;
10323 int k;
10324 int i;
10325
10326 assert(nchgcoefs != NULL);
10327 assert(nchgsides != NULL);
10328 assert(!SCIPconsIsModifiable(cons));
10329
10330 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
10331 assert(conshdlrdata != NULL);
10332
10333 consdata = SCIPconsGetData(cons);
10334 assert(consdata != NULL);
10335 assert(consdata->row == NULL); /* we are in presolve, so no LP row exists */
10336 assert(consdata->onesweightsum == 0); /* all fixed variables should have been removed */
10337 assert(consdata->weightsum > consdata->capacity); /* otherwise, the constraint is redundant */
10338 assert(consdata->nvars > 0);
10339
10340 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
10341 if( *cutoff )
10342 return SCIP_OKAY;
10343
10344 /* apply rule (1) */
10345 if( (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 )
10346 {
10347 do
10348 {
10349 assert(consdata->merged);
10350
10351 /* sort items, s.t. the heaviest one is in the first position */
10352 sortItems(consdata);
10353
10354 for( i = 0; i < consdata->nvars; ++i )
10355 {
10356 SCIP_Longint weight;
10357
10358 weight = consdata->weights[i];
10359 if( consdata->weightsum - weight < consdata->capacity )
10360 {
10361 newweight = consdata->weightsum - consdata->capacity;
10362 consdataChgWeight(consdata, i, newweight);
10363 consdata->capacity -= (weight - newweight);
10364 (*nchgcoefs)++;
10365 (*nchgsides)++;
10366 assert(!consdata->sorted);
10367 SCIPdebugMsg(scip, "knapsack constraint <%s>: changed weight of <%s> from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT ", capacity from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10368 SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[i]), weight, newweight,
10369 consdata->capacity + (weight-newweight), consdata->capacity);
10370 }
10371 else
10372 break;
10373 }
10374 }
10375 while( !consdata->sorted && consdata->weightsum > consdata->capacity );
10376 }
10377
10378 /* check for redundancy */
10379 if( consdata->weightsum <= consdata->capacity )
10380 return SCIP_OKAY;
10381
10382 pos = 0;
10383 while( pos < consdata->nvars && consdata->weights[pos] == consdata->capacity )
10384 ++pos;
10385
10386 sumcoef = 0;
10387 weights = consdata->weights;
10388 nvars = consdata->nvars;
10389 capacity = consdata->capacity;
10390
10391 if( (presoltiming & (SCIP_PRESOLTIMING_FAST | SCIP_PRESOLTIMING_MEDIUM)) != 0 &&
10392 pos < nvars && weights[pos] + weights[pos + 1] > capacity )
10393 {
10394 /* further reductions using the next possible coefficient sum
10395 *
10396 * e.g. 19x1 + 15x2 + 10x3 + 5x4 + 5x5 <= 19 <=> 19x1 + 19x2 + 14x3 + 5x4 + 5x5 <= 19
10397 */
10398 /* @todo loop for "k" can be extended, same coefficient when determine next sumcoef can be left out */
10399 for( k = 0; k < 4; ++k )
10400 {
10401 newweight = capacity - sumcoef;
10402
10403 /* determine next minimal coefficient sum */
10404 switch( k )
10405 {
10406 case 0:
10407 sumcoef = weights[nvars - 1];
10408 backpos = nvars - 1;
10409 break;
10410 case 1:
10411 sumcoef = weights[nvars - 2];
10412 backpos = nvars - 2;
10413 break;
10414 case 2:
10415 if( weights[nvars - 3] < weights[nvars - 1] + weights[nvars - 2] )
10416 {
10417 sumcoefcase = TRUE;
10418 sumcoef = weights[nvars - 3];
10419 backpos = nvars - 3;
10420 }
10421 else
10422 {
10423 sumcoefcase = FALSE;
10424 sumcoef = weights[nvars - 1] + weights[nvars - 2];
10425 backpos = nvars - 2;
10426 }
10427 break;
10428 default:
10429 assert(k == 3);
10430 if( sumcoefcase )
10431 {
10432 if( weights[nvars - 4] < weights[nvars - 1] + weights[nvars - 2] )
10433 {
10434 sumcoef = weights[nvars - 4];
10435 backpos = nvars - 4;
10436 }
10437 else
10438 {
10439 sumcoef = weights[nvars - 1] + weights[nvars - 2];
10440 backpos = nvars - 2;
10441 }
10442 }
10443 else
10444 {
10445 sumcoef = weights[nvars - 3];
10446 backpos = nvars - 3;
10447 }
10448 break;
10449 }
10450
10451 if( backpos <= pos )
10452 break;
10453
10454 /* tighten next coefficients that, paired with the current small coefficient, exceed the capacity */
10455 maxweight = weights[pos];
10456 startpos = pos;
10457 while( 2 * maxweight > capacity && maxweight + sumcoef > capacity )
10458 {
10459 assert(newweight > weights[pos]);
10460
10461 SCIPdebugMsg(scip, "in constraint <%s> changing weight %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10462 SCIPconsGetName(cons), maxweight, newweight);
10463
10464 consdataChgWeight(consdata, pos, newweight);
10465
10466 ++pos;
10467 assert(pos < nvars);
10468
10469 maxweight = weights[pos];
10470
10471 if( backpos <= pos )
10472 break;
10473 }
10474 (*nchgcoefs) += (pos - startpos);
10475
10476 /* skip unchangable weights */
10477 while( pos < nvars && weights[pos] + sumcoef == capacity )
10478 ++pos;
10479
10480 /* check special case were there is only one weight left to tighten
10481 *
10482 * e.g. 95x1 + 59x2 + 37x3 + 36x4 <= 95 (37 > 36)
10483 *
10484 * => 95x1 + 59x2 + 59x3 + 36x4 <= 95
10485 *
10486 * 197x1 + 120x2 + 77x3 + 10x4 <= 207 (here we cannot tighten the coefficient further)
10487 */
10488 if( pos + 1 == backpos && weights[pos] > sumcoef &&
10489 ((k == 0) || (k == 1 && weights[nvars - 1] + sumcoef + weights[pos] > capacity)) )
10490 {
10491 newweight = capacity - sumcoef;
10492 assert(newweight > weights[pos]);
10493
10494 SCIPdebugMsg(scip, "in constraint <%s> changing weight %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10495 SCIPconsGetName(cons), maxweight, newweight);
10496
10497 consdataChgWeight(consdata, pos, newweight);
10498
10499 break;
10500 }
10501
10502 if( backpos <= pos )
10503 break;
10504 }
10505 }
10506
10507 /* apply rule (2) (don't apply, if the knapsack has too many items for applying this costly method) */
10508 if( (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
10509 {
10510 if( conshdlrdata->disaggregation && consdata->nvars - pos <= MAX_USECLIQUES_SIZE && consdata->nvars >= 2 &&
10511 pos > 0 && (SCIP_Longint)consdata->nvars - pos <= consdata->capacity &&
10512 consdata->weights[pos - 1] == consdata->capacity && (pos == consdata->nvars || consdata->weights[pos] == 1) )
10513 {
10514 SCIP_VAR** clqvars;
10515 SCIP_CONS* cliquecons;
10516 char name[SCIP_MAXSTRLEN];
10517 int* clqpart;
10518 int nclqvars;
10519 int nclq;
10520 int len;
10521 int c;
10522 int w;
10523
10524 assert(!SCIPconsIsDeleted(cons));
10525
10526 if( pos == consdata->nvars )
10527 {
10528 SCIPdebugMsg(scip, "upgrading knapsack constraint <%s> to a set-packing constraint", SCIPconsGetName(cons));
10529
10530 SCIP_CALL( SCIPcreateConsSetpack(scip, &cliquecons, SCIPconsGetName(cons), pos, consdata->vars,
10531 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
10532 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
10533 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
10534 SCIPconsIsStickingAtNode(cons)) );
10535
10536 SCIP_CALL( SCIPaddCons(scip, cliquecons) );
10537 SCIP_CALL( SCIPreleaseCons(scip, &cliquecons) );
10538 ++(*naddconss);
10539
10540 /* delete old constraint */
10541 SCIP_CALL( SCIPdelCons(scip, cons) );
10542 ++(*ndelconss);
10543
10544 return SCIP_OKAY;
10545 }
10546
10547 len = consdata->nvars - pos;
10548
10549 /* allocate temporary memory */
10550 SCIP_CALL( SCIPallocBufferArray(scip, &clqpart, len) );
10551
10552 /* calculate clique partition */
10553 SCIP_CALL( SCIPcalcCliquePartition(scip, &(consdata->vars[pos]), len, clqpart, &nclq) );
10554 assert(nclq <= len);
10555
10556 #ifndef NDEBUG
10557 /* clique numbers must be at least as high as the index */
10558 for( w = 0; w < nclq; ++w )
10559 assert(clqpart[w] <= w);
10560 #endif
10561
10562 SCIPdebugMsg(scip, "Disaggregating knapsack constraint <%s> due to clique information.\n", SCIPconsGetName(cons));
10563
10564 /* allocate temporary memory */
10565 SCIP_CALL( SCIPallocBufferArray(scip, &clqvars, pos + len - nclq + 1) );
10566
10567 /* copy corresponding variables with big coefficients */
10568 for( w = pos - 1; w >= 0; --w )
10569 clqvars[w] = consdata->vars[w];
10570
10571 /* create for each clique a set-packing constraint */
10572 for( c = 0; c < nclq; ++c )
10573 {
10574 nclqvars = pos;
10575
10576 for( w = c; w < len; ++w )
10577 {
10578 if( clqpart[w] == c )
10579 {
10580 assert(nclqvars < pos + len - nclq + 1);
10581 clqvars[nclqvars] = consdata->vars[w + pos];
10582 ++nclqvars;
10583 }
10584 }
10585
10586 assert(nclqvars > 1);
10587
10588 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_clq_%" SCIP_LONGINT_FORMAT "_%d", SCIPconsGetName(cons), consdata->capacity, c);
10589 SCIP_CALL( SCIPcreateConsSetpack(scip, &cliquecons, name, nclqvars, clqvars,
10590 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
10591 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
10592 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
10593 SCIPconsIsStickingAtNode(cons)) );
10594 SCIPdebugMsg(scip, " -> adding clique constraint: ");
10595 SCIPdebugPrintCons(scip, cliquecons, NULL);
10596 SCIP_CALL( SCIPaddCons(scip, cliquecons) );
10597 SCIP_CALL( SCIPreleaseCons(scip, &cliquecons) );
10598 ++(*naddconss);
10599 }
10600
10601 /* delete old constraint */
10602 SCIP_CALL( SCIPdelCons(scip, cons) );
10603 ++(*ndelconss);
10604
10605 SCIPfreeBufferArray(scip, &clqvars);
10606 SCIPfreeBufferArray(scip, &clqpart);
10607
10608 return SCIP_OKAY;
10609 }
10610 else if( consdata->nvars <= MAX_USECLIQUES_SIZE || (consdata->cliquepartitioned && consdata->ncliques <= MAX_USECLIQUES_SIZE) )
10611 {
10612 SCIP_Longint* maxcliqueweights;
10613 SCIP_Longint* newweightvals;
10614 int* newweightidxs;
10615 SCIP_Longint cliqueweightsum;
10616
10617 SCIP_CALL( SCIPallocBufferArray(scip, &maxcliqueweights, consdata->nvars) );
10618 SCIP_CALL( SCIPallocBufferArray(scip, &newweightvals, consdata->nvars) );
10619 SCIP_CALL( SCIPallocBufferArray(scip, &newweightidxs, consdata->nvars) );
10620
10621 /* repeat as long as changes have been applied */
10622 do
10623 {
10624 int ncliques;
10625 int cliquenum;
10626 SCIP_Bool zeroweights;
10627
10628 assert(consdata->merged);
10629
10630 /* sort items, s.t. the heaviest one is in the first position */
10631 sortItems(consdata);
10632
10633 /* calculate a clique partition */
10634 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, TRUE, FALSE) );
10635
10636 /* if there are only single element cliques, rule (2) is equivalent to rule (1) */
10637 if( consdata->cliquepartition[consdata->nvars - 1] == consdata->nvars - 1 )
10638 break;
10639
10640 /* calculate the maximal weight of the cliques and store the clique type */
10641 cliqueweightsum = 0;
10642 ncliques = 0;
10643
10644 for( i = 0; i < consdata->nvars; ++i )
10645 {
10646 SCIP_Longint weight;
10647
10648 cliquenum = consdata->cliquepartition[i];
10649 assert(0 <= cliquenum && cliquenum <= ncliques);
10650
10651 weight = consdata->weights[i];
10652 assert(weight > 0);
10653
10654 if( cliquenum == ncliques )
10655 {
10656 maxcliqueweights[ncliques] = weight;
10657 cliqueweightsum += weight;
10658 ++ncliques;
10659 }
10660
10661 assert(maxcliqueweights[cliquenum] >= weight);
10662 }
10663
10664 /* apply rule on every clique */
10665 zeroweights = FALSE;
10666 for( i = 0; i < ncliques; ++i )
10667 {
10668 SCIP_Longint delta;
10669
10670 delta = consdata->capacity - (cliqueweightsum - maxcliqueweights[i]);
10671 if( delta > 0 )
10672 {
10673 SCIP_Longint newcapacity;
10674 #ifndef NDEBUG
10675 SCIP_Longint newmincliqueweight;
10676 #endif
10677 SCIP_Longint newminweightsuminclique;
10678 SCIP_Bool forceclique;
10679 int nnewweights;
10680 int j;
10681
10682 SCIPdebugMsg(scip, "knapsack constraint <%s>: weights of clique %d (maxweight: %" SCIP_LONGINT_FORMAT ") can be tightened: cliqueweightsum=%" SCIP_LONGINT_FORMAT ", capacity=%" SCIP_LONGINT_FORMAT " -> delta: %" SCIP_LONGINT_FORMAT "\n",
10683 SCIPconsGetName(cons), i, maxcliqueweights[i], cliqueweightsum, consdata->capacity, delta);
10684 newcapacity = consdata->capacity - delta;
10685 forceclique = FALSE;
10686 nnewweights = 0;
10687 #ifndef NDEBUG
10688 newmincliqueweight = newcapacity + 1;
10689 for( j = 0; j < i; ++j )
10690 assert(consdata->cliquepartition[j] < i); /* no element j < i can be in clique i */
10691 #endif
10692 for( j = i; j < consdata->nvars; ++j )
10693 {
10694 if( consdata->cliquepartition[j] == i )
10695 {
10696 newweight = consdata->weights[j] - delta;
10697 newweight = MAX(newweight, 0);
10698
10699 /* cache the new weight */
10700 assert(nnewweights < consdata->nvars);
10701 newweightvals[nnewweights] = newweight;
10702 newweightidxs[nnewweights] = j;
10703 nnewweights++;
10704
10705 #ifndef NDEBUG
10706 assert(newweight <= newmincliqueweight); /* items are sorted by non-increasing weight! */
10707 newmincliqueweight = newweight;
10708 #endif
10709 }
10710 }
10711
10712 /* check if our clique information results out of this knapsack constraint and if so check if we would loose the clique information */
10713 if( nnewweights > 1 )
10714 {
10715 #ifndef NDEBUG
10716 j = newweightidxs[nnewweights - 2];
10717 assert(0 <= j && j < consdata->nvars);
10718 assert(consdata->cliquepartition[j] == i);
10719 j = newweightidxs[nnewweights - 1];
10720 assert(0 <= j && j < consdata->nvars);
10721 assert(consdata->cliquepartition[j] == i);
10722 #endif
10723
10724 newminweightsuminclique = newweightvals[nnewweights - 2];
10725 newminweightsuminclique += newweightvals[nnewweights - 1];
10726
10727 /* check if these new two minimal weights both fit into the knapsack;
10728 * if this is true, we have to add a clique constraint in order to enforce the clique
10729 * (otherwise, the knapsack might have been one of the reasons for the clique, and the weight
10730 * reduction might be infeasible, i.e., allows additional solutions)
10731 */
10732 if( newminweightsuminclique <= newcapacity )
10733 forceclique = TRUE;
10734 }
10735
10736 /* check if we really want to apply the change */
10737 if( conshdlrdata->disaggregation || !forceclique )
10738 {
10739 SCIPdebugMsg(scip, " -> change capacity from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT " (forceclique:%u)\n",
10740 consdata->capacity, newcapacity, forceclique);
10741 consdata->capacity = newcapacity;
10742 (*nchgsides)++;
10743
10744 for( k = 0; k < nnewweights; ++k )
10745 {
10746 j = newweightidxs[k];
10747 assert(0 <= j && j < consdata->nvars);
10748 assert(consdata->cliquepartition[j] == i);
10749
10750 /* apply the weight change */
10751 SCIPdebugMsg(scip, " -> change weight of <%s> from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10752 SCIPvarGetName(consdata->vars[j]), consdata->weights[j], newweightvals[k]);
10753 consdataChgWeight(consdata, j, newweightvals[k]);
10754 (*nchgcoefs)++;
10755 assert(!consdata->sorted);
10756 zeroweights = zeroweights || (newweightvals[k] == 0);
10757 }
10758 /* if before the weight update at least one pair of weights did not fit into the knapsack and now fits,
10759 * we have to make sure, the clique is enforced - the clique might have been constructed partially from
10760 * this constraint, and by reducing the weights, this clique information is not contained anymore in the
10761 * knapsack constraint
10762 */
10763 if( forceclique )
10764 {
10765 SCIP_CONS* cliquecons;
10766 char name[SCIP_MAXSTRLEN];
10767 SCIP_VAR** cliquevars;
10768
10769 SCIP_CALL( SCIPallocBufferArray(scip, &cliquevars, nnewweights) );
10770 for( k = 0; k < nnewweights; ++k )
10771 cliquevars[k] = consdata->vars[newweightidxs[k]];
10772
10773 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_clq_%" SCIP_LONGINT_FORMAT "_%d", SCIPconsGetName(cons), consdata->capacity, i);
10774 SCIP_CALL( SCIPcreateConsSetpack(scip, &cliquecons, name, nnewweights, cliquevars,
10775 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
10776 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
10777 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
10778 SCIPconsIsStickingAtNode(cons)) );
10779 SCIPdebugMsg(scip, " -> adding clique constraint: ");
10780 SCIPdebugPrintCons(scip, cliquecons, NULL);
10781 SCIP_CALL( SCIPaddCons(scip, cliquecons) );
10782 SCIP_CALL( SCIPreleaseCons(scip, &cliquecons) );
10783 SCIPfreeBufferArray(scip, &cliquevars);
10784 (*naddconss)++;
10785 }
10786 }
10787 }
10788 }
10789 if( zeroweights )
10790 {
10791 SCIP_CALL( removeZeroWeights(scip, cons) );
10792 }
10793 }
10794 while( !consdata->sorted && consdata->weightsum > consdata->capacity );
10795
10796 /* free temporary memory */
10797 SCIPfreeBufferArray(scip, &newweightidxs);
10798 SCIPfreeBufferArray(scip, &newweightvals);
10799 SCIPfreeBufferArray(scip, &maxcliqueweights);
10800
10801 /* check for redundancy */
10802 if( consdata->weightsum <= consdata->capacity )
10803 return SCIP_OKAY;
10804 }
10805 }
10806
10807 /* apply rule (3) */
10808 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 )
10809 {
10810 SCIP_CALL( tightenWeightsLift(scip, cons, nchgcoefs, cutoff) );
10811 }
10812
10813 /* check for redundancy */
10814 if( consdata->weightsum <= consdata->capacity )
10815 return SCIP_OKAY;
10816
10817 if( (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 )
10818 {
10819 /* apply rule (4) (all but smallest weight) */
10820 assert(consdata->merged);
10821 sortItems(consdata);
10822 minweight = consdata->weights[consdata->nvars-1];
10823 for( i = 0; i < consdata->nvars-1; ++i )
10824 {
10825 SCIP_Longint weight;
10826
10827 weight = consdata->weights[i];
10828 assert(weight >= minweight);
10829 if( minweight + weight > consdata->capacity )
10830 {
10831 if( weight < consdata->capacity )
10832 {
10833 SCIPdebugMsg(scip, "knapsack constraint <%s>: changed weight of <%s> from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10834 SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[i]), weight, consdata->capacity);
10835 assert(consdata->sorted);
10836 consdataChgWeight(consdata, i, consdata->capacity); /* this does not destroy the weight order! */
10837 assert(i == 0 || consdata->weights[i-1] >= consdata->weights[i]);
10838 consdata->sorted = TRUE;
10839 (*nchgcoefs)++;
10840 }
10841 }
10842 else
10843 break;
10844 }
10845
10846 /* apply rule (5) (smallest weight) */
10847 if( consdata->nvars >= 2 )
10848 {
10849 SCIP_Longint weight;
10850
10851 minweight = consdata->weights[consdata->nvars-2];
10852 weight = consdata->weights[consdata->nvars-1];
10853 assert(minweight >= weight);
10854 if( minweight + weight > consdata->capacity && weight < consdata->capacity )
10855 {
10856 SCIPdebugMsg(scip, "knapsack constraint <%s>: changed weight of <%s> from %" SCIP_LONGINT_FORMAT " to %" SCIP_LONGINT_FORMAT "\n",
10857 SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[consdata->nvars-1]), weight, consdata->capacity);
10858 assert(consdata->sorted);
10859 consdataChgWeight(consdata, consdata->nvars-1, consdata->capacity); /* this does not destroy the weight order! */
10860 assert(minweight >= consdata->weights[consdata->nvars-1]);
10861 consdata->sorted = TRUE;
10862 (*nchgcoefs)++;
10863 }
10864 }
10865 }
10866
10867 return SCIP_OKAY;
10868 }
10869
10870
10871 #ifdef SCIP_DEBUG
10872 static
printClique(SCIP_VAR ** cliquevars,int ncliquevars)10873 void printClique(
10874 SCIP_VAR** cliquevars,
10875 int ncliquevars
10876 )
10877 {
10878 int b;
10879 SCIPdebugMessage("adding new Clique: ");
10880 for( b = 0; b < ncliquevars; ++b )
10881 SCIPdebugPrintf("%s ", SCIPvarGetName(cliquevars[b]));
10882 SCIPdebugPrintf("\n");
10883 }
10884 #endif
10885
10886 /** adds negated cliques of the knapsack constraint to the global clique table */
10887 static
addNegatedCliques(SCIP * const scip,SCIP_CONS * const cons,SCIP_Bool * const cutoff,int * const nbdchgs)10888 SCIP_RETCODE addNegatedCliques(
10889 SCIP*const scip, /**< SCIP data structure */
10890 SCIP_CONS*const cons, /**< knapsack constraint */
10891 SCIP_Bool*const cutoff, /**< pointer to store whether the node can be cut off */
10892 int*const nbdchgs /**< pointer to count the number of performed bound changes */
10893 )
10894 {
10895 SCIP_CONSDATA* consdata;
10896 SCIP_CONSHDLRDATA* conshdlrdata;
10897 SCIP_VAR** poscliquevars;
10898 SCIP_VAR** cliquevars;
10899 SCIP_Longint* maxweights;
10900 SCIP_Longint* gainweights;
10901 int* gaincliquepartition;
10902 SCIP_Bool* cliqueused;
10903 SCIP_Longint minactduetonegcliques;
10904 SCIP_Longint freecapacity;
10905 SCIP_Longint lastweight;
10906 SCIP_Longint beforelastweight;
10907 int nposcliquevars;
10908 int ncliquevars;
10909 int nvars;
10910 int nnegcliques;
10911 int lastcliqueused;
10912 int thisnbdchgs;
10913 int v;
10914 int w;
10915
10916 assert(scip != NULL);
10917 assert(cons != NULL);
10918 assert(cutoff != NULL);
10919 assert(nbdchgs != NULL);
10920
10921 *cutoff = FALSE;
10922
10923 consdata = SCIPconsGetData(cons);
10924 assert(consdata != NULL);
10925
10926 nvars = consdata->nvars;
10927
10928 /* check whether the cliques have already been added */
10929 if( consdata->cliquesadded || nvars == 0 )
10930 return SCIP_OKAY;
10931
10932 /* make sure, the items are merged */
10933 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
10934 if( *cutoff )
10935 return SCIP_OKAY;
10936
10937 /* make sure, items are sorted by non-increasing weight */
10938 sortItems(consdata);
10939
10940 assert(consdata->merged);
10941
10942 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
10943 assert(conshdlrdata != NULL);
10944
10945 /* calculate a clique partition */
10946 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, FALSE, TRUE) );
10947 nnegcliques = consdata->nnegcliques;
10948
10949 /* if we have no negated cliques, stop */
10950 if( nnegcliques == nvars )
10951 return SCIP_OKAY;
10952
10953 /* get temporary memory */
10954 SCIP_CALL( SCIPallocBufferArray(scip, &poscliquevars, nvars) );
10955 SCIP_CALL( SCIPallocBufferArray(scip, &cliquevars, nvars) );
10956 SCIP_CALL( SCIPallocClearBufferArray(scip, &gainweights, nvars) );
10957 SCIP_CALL( SCIPallocBufferArray(scip, &gaincliquepartition, nvars) );
10958 SCIP_CALL( SCIPallocBufferArray(scip, &maxweights, nnegcliques) );
10959 SCIP_CALL( SCIPallocClearBufferArray(scip, &cliqueused, nnegcliques) );
10960
10961 nnegcliques = 0;
10962 minactduetonegcliques = 0;
10963
10964 /* determine maximal weights for all negated cliques and calculate minimal weightsum due to negated cliques */
10965 for( v = 0; v < nvars; ++v )
10966 {
10967 assert(0 <= consdata->negcliquepartition[v] && consdata->negcliquepartition[v] <= nnegcliques);
10968 assert(consdata->weights[v] > 0);
10969
10970 if( consdata->negcliquepartition[v] == nnegcliques )
10971 {
10972 nnegcliques++;
10973 maxweights[consdata->negcliquepartition[v]] = consdata->weights[v];
10974 }
10975 else
10976 minactduetonegcliques += consdata->weights[v];
10977 }
10978
10979 nposcliquevars = 0;
10980
10981 /* add cliques, using negated cliques information */
10982 if( minactduetonegcliques > 0 )
10983 {
10984 /* free capacity is the rest of not used capacity if the smallest amount of weights due to negated cliques are used */
10985 freecapacity = consdata->capacity - minactduetonegcliques;
10986
10987 SCIPdebugPrintCons(scip, cons, NULL);
10988 SCIPdebugMsg(scip, "Try to add negated cliques in knapsack constraint handler for constraint %s; capacity = %" SCIP_LONGINT_FORMAT ", minactivity(due to neg. cliques) = %" SCIP_LONGINT_FORMAT ", freecapacity = %" SCIP_LONGINT_FORMAT ".\n",
10989 SCIPconsGetName(cons), consdata->capacity, minactduetonegcliques, freecapacity);
10990
10991 /* calculate possible gain by switching chosen items in negated cliques */
10992 for( v = 0; v < nvars; ++v )
10993 {
10994 if( !cliqueused[consdata->negcliquepartition[v]] )
10995 {
10996 cliqueused[consdata->negcliquepartition[v]] = TRUE;
10997 for( w = v + 1; w < nvars; ++w )
10998 {
10999 /* if we would take the biggest weight instead of another what would we gain, take weight[v] instead of
11000 * weight[w] (which are both in a negated clique) */
11001 if( consdata->negcliquepartition[v] == consdata->negcliquepartition[w]
11002 && consdata->weights[v] > consdata->weights[w] )
11003 {
11004 poscliquevars[nposcliquevars] = consdata->vars[w];
11005 gainweights[nposcliquevars] = maxweights[consdata->negcliquepartition[v]] - consdata->weights[w];
11006 gaincliquepartition[nposcliquevars] = consdata->negcliquepartition[v];
11007 ++nposcliquevars;
11008 }
11009 }
11010 }
11011 }
11012
11013 /* try to create negated cliques */
11014 if( nposcliquevars > 0 )
11015 {
11016 /* sort possible gain per substitution of the clique members */
11017 SCIPsortDownLongPtrInt(gainweights,(void**) poscliquevars, gaincliquepartition, nposcliquevars);
11018
11019 for( v = 0; v < nposcliquevars; ++v )
11020 {
11021 SCIP_CALL( SCIPgetNegatedVar(scip, poscliquevars[v], &cliquevars[0]) );
11022 ncliquevars = 1;
11023 lastweight = gainweights[v];
11024 beforelastweight = -1;
11025 lastcliqueused = gaincliquepartition[v];
11026 /* clear cliqueused to get an unused array */
11027 BMSclearMemoryArray(cliqueused, nnegcliques);
11028 cliqueused[gaincliquepartition[v]] = TRUE;
11029
11030 /* taking bigger weights make the knapsack redundant so we will create cliques, only take items which are not
11031 * in the same negated clique and by taking two of them would exceed the free capacity */
11032 for( w = v + 1; w < nposcliquevars && !cliqueused[gaincliquepartition[w]] && gainweights[w] + lastweight > freecapacity; ++w )
11033 {
11034 beforelastweight = lastweight;
11035 lastweight = gainweights[w];
11036 lastcliqueused = gaincliquepartition[w];
11037 cliqueused[gaincliquepartition[w]] = TRUE;
11038 SCIP_CALL( SCIPgetNegatedVar(scip, poscliquevars[w], &cliquevars[ncliquevars]) );
11039 ++ncliquevars;
11040 }
11041
11042 if( ncliquevars > 1 )
11043 {
11044 SCIPdebug( printClique(cliquevars, ncliquevars) );
11045 assert(beforelastweight > 0);
11046 /* add the clique to the clique table */
11047 /* this really happens, e.g., on enigma.mps from the short test set */
11048 SCIP_CALL( SCIPaddClique(scip, cliquevars, NULL, ncliquevars, FALSE, cutoff, &thisnbdchgs) );
11049 if( *cutoff )
11050 goto TERMINATE;
11051 *nbdchgs += thisnbdchgs;
11052
11053 /* reset last used clique to get slightly different cliques */
11054 cliqueused[lastcliqueused] = FALSE;
11055
11056 /* try to replace the last item in the clique by a different item to obtain a slightly different clique */
11057 for( ++w; w < nposcliquevars && !cliqueused[gaincliquepartition[w]] && beforelastweight + gainweights[w] > freecapacity; ++w )
11058 {
11059 SCIP_CALL( SCIPgetNegatedVar(scip, poscliquevars[w], &cliquevars[ncliquevars - 1]) );
11060 SCIPdebug( printClique(cliquevars, ncliquevars) );
11061 SCIP_CALL( SCIPaddClique(scip, cliquevars, NULL, ncliquevars, FALSE, cutoff, &thisnbdchgs) );
11062 if( *cutoff )
11063 goto TERMINATE;
11064 *nbdchgs += thisnbdchgs;
11065 }
11066 }
11067 }
11068 }
11069 }
11070
11071 TERMINATE:
11072 /* free temporary memory */
11073 SCIPfreeBufferArray(scip, &cliqueused);
11074 SCIPfreeBufferArray(scip, &maxweights);
11075 SCIPfreeBufferArray(scip, &gaincliquepartition);
11076 SCIPfreeBufferArray(scip, &gainweights);
11077 SCIPfreeBufferArray(scip, &cliquevars);
11078 SCIPfreeBufferArray(scip, &poscliquevars);
11079
11080 return SCIP_OKAY;
11081 }
11082
11083 /** greedy clique detection by considering weights and capacity
11084 *
11085 * greedily detects cliques by first sorting the items by decreasing weights (optional) and then collecting greedily
11086 * 1) neighboring items which exceed the capacity together => one clique
11087 * 2) looping through the remaining items and finding the largest set of preceding items to build a clique => possibly many more cliques
11088 */
11089 static
greedyCliqueAlgorithm(SCIP * const scip,SCIP_VAR ** items,SCIP_Longint * weights,int nitems,SCIP_Longint capacity,SCIP_Bool sorteditems,SCIP_Real cliqueextractfactor,SCIP_Bool * const cutoff,int * const nbdchgs)11090 SCIP_RETCODE greedyCliqueAlgorithm(
11091 SCIP*const scip, /**< SCIP data structure */
11092 SCIP_VAR** items, /**< array of variable items */
11093 SCIP_Longint* weights, /**< weights of the items */
11094 int nitems, /**< the number of items */
11095 SCIP_Longint capacity, /**< maximum free capacity of the knapsack */
11096 SCIP_Bool sorteditems, /**< are the items sorted by their weights nonincreasing? */
11097 SCIP_Real cliqueextractfactor,/**< lower clique size limit for greedy clique extraction algorithm (relative to largest clique) */
11098 SCIP_Bool*const cutoff, /**< pointer to store whether the node can be cut off */
11099 int*const nbdchgs /**< pointer to count the number of performed bound changes */
11100 )
11101 {
11102 SCIP_Longint lastweight;
11103 int ncliquevars;
11104 int i;
11105 int thisnbdchgs;
11106
11107 if( nitems <= 1 )
11108 return SCIP_OKAY;
11109
11110 /* sort possible gain per substitution of the clique members */
11111 if( ! sorteditems )
11112 SCIPsortDownLongPtr(weights,(void**) items, nitems);
11113
11114 ncliquevars = 1;
11115 lastweight = weights[0];
11116
11117 /* taking these two weights together violates the knapsack => include into clique */
11118 for( i = 1; i < nitems && weights[i] + lastweight > capacity; ++i )
11119 {
11120 lastweight = weights[i];
11121 ++ncliquevars;
11122 }
11123
11124 if( ncliquevars > 1 )
11125 {
11126 SCIP_Longint compareweight;
11127 SCIP_VAR** cliquevars;
11128 int compareweightidx;
11129 int minclqsize;
11130 int nnzadded;
11131
11132 /* add the clique to the clique table */
11133 SCIPdebug( printClique(items, ncliquevars) );
11134 SCIP_CALL( SCIPaddClique(scip, items, NULL, ncliquevars, FALSE, cutoff, &thisnbdchgs) );
11135
11136 if( *cutoff )
11137 return SCIP_OKAY;
11138
11139 *nbdchgs += thisnbdchgs;
11140 nnzadded = ncliquevars;
11141
11142 /* no more cliques to be found (don't know if this can actually happen, since the knapsack could be replaced by a set-packing constraint)*/
11143 if( ncliquevars == nitems )
11144 return SCIP_OKAY;
11145
11146 /* copy items in order into buffer array and deduce more cliques */
11147 SCIP_CALL( SCIPduplicateBufferArray(scip, &cliquevars, items, ncliquevars) );
11148
11149 /* try to replace the last item in the clique by a different item to obtain a slightly different clique */
11150 /* loop over remaining, smaller items and compare each item backwards against larger weights, starting with the second smallest weight */
11151 compareweightidx = ncliquevars - 2;
11152 assert(i == nitems || weights[i] + weights[ncliquevars - 1] <= capacity);
11153
11154 /* determine minimum clique size for the following loop */
11155 minclqsize = (int)(cliqueextractfactor * ncliquevars);
11156 minclqsize = MAX(minclqsize, 2);
11157
11158 /* loop over the remaining variables and the larger items of the first clique until we
11159 * find another clique or reach the size limit */
11160 while( compareweightidx >= 0 && i < nitems && ! (*cutoff)
11161 && ncliquevars >= minclqsize /* stop at a given minimum clique size */
11162 && nnzadded <= 2 * nitems /* stop if enough nonzeros were added to the cliquetable */
11163 )
11164 {
11165 compareweight = weights[compareweightidx];
11166 assert(compareweight > 0);
11167
11168 /* include this item together with all items that have a weight at least as large as the compare weight in a clique */
11169 if( compareweight + weights[i] > capacity )
11170 {
11171 assert(compareweightidx == ncliquevars -2);
11172 cliquevars[ncliquevars - 1] = items[i];
11173 SCIPdebug( printClique(cliquevars, ncliquevars) );
11174 SCIP_CALL( SCIPaddClique(scip, cliquevars, NULL, ncliquevars, FALSE, cutoff, &thisnbdchgs) );
11175
11176 nnzadded += ncliquevars;
11177
11178 /* stop when there is a cutoff */
11179 if( ! (*cutoff) )
11180 *nbdchgs += thisnbdchgs;
11181
11182 /* go to next smaller item */
11183 ++i;
11184 }
11185 else
11186 {
11187 /* choose a preceding, larger weight to compare small items against. Clique size is reduced by 1 simultaneously */
11188 compareweightidx--;
11189 ncliquevars --;
11190 }
11191 }
11192
11193 SCIPfreeBufferArray(scip, &cliquevars);
11194 }
11195
11196 return SCIP_OKAY;
11197 }
11198
11199 /** adds cliques of the knapsack constraint to the global clique table */
11200 static
addCliques(SCIP * const scip,SCIP_CONS * const cons,SCIP_Real cliqueextractfactor,SCIP_Bool * const cutoff,int * const nbdchgs)11201 SCIP_RETCODE addCliques(
11202 SCIP*const scip, /**< SCIP data structure */
11203 SCIP_CONS*const cons, /**< knapsack constraint */
11204 SCIP_Real cliqueextractfactor,/**< lower clique size limit for greedy clique extraction algorithm (relative to largest clique) */
11205 SCIP_Bool*const cutoff, /**< pointer to store whether the node can be cut off */
11206 int*const nbdchgs /**< pointer to count the number of performed bound changes */
11207 )
11208 {
11209 SCIP_CONSDATA* consdata;
11210 SCIP_CONSHDLRDATA* conshdlrdata;
11211 int i;
11212 SCIP_Longint minactduetonegcliques;
11213 SCIP_Longint freecapacity;
11214 int nnegcliques;
11215 int cliquenum;
11216 SCIP_VAR** poscliquevars;
11217 SCIP_Longint* gainweights;
11218 int nposcliquevars;
11219 SCIP_Longint* secondmaxweights;
11220 int nvars;
11221
11222 assert(scip != NULL);
11223 assert(cons != NULL);
11224 assert(cutoff != NULL);
11225 assert(nbdchgs != NULL);
11226
11227 *cutoff = FALSE;
11228
11229 consdata = SCIPconsGetData(cons);
11230 assert(consdata != NULL);
11231
11232 nvars = consdata->nvars;
11233
11234 /* check whether the cliques have already been added */
11235 if( consdata->cliquesadded || nvars == 0 )
11236 return SCIP_OKAY;
11237
11238 /* make sure, the items are merged */
11239 SCIP_CALL( mergeMultiples(scip, cons, cutoff) );
11240 if( *cutoff )
11241 return SCIP_OKAY;
11242
11243 /* make sure, the items are sorted by non-increasing weight */
11244 sortItems(consdata);
11245
11246 assert(consdata->merged);
11247
11248 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
11249 assert(conshdlrdata != NULL);
11250
11251 /* calculate a clique partition */
11252 SCIP_CALL( calcCliquepartition(scip, conshdlrdata, consdata, FALSE, TRUE) );
11253 nnegcliques = consdata->nnegcliques;
11254 assert(nnegcliques <= nvars);
11255
11256 /* get temporary memory */
11257 SCIP_CALL( SCIPallocBufferArray(scip, &poscliquevars, nvars) );
11258 SCIP_CALL( SCIPallocBufferArray(scip, &gainweights, nvars) );
11259 BMSclearMemoryArray(gainweights, nvars);
11260 SCIP_CALL( SCIPallocBufferArray(scip, &secondmaxweights, nnegcliques) );
11261 BMSclearMemoryArray(secondmaxweights, nnegcliques);
11262
11263 minactduetonegcliques = 0;
11264
11265 /* calculate minimal activity due to negated cliques, and determine second maximal weight in each clique */
11266 if( nnegcliques < nvars )
11267 {
11268 nnegcliques = 0;
11269
11270 for( i = 0; i < nvars; ++i )
11271 {
11272 SCIP_Longint weight;
11273
11274 cliquenum = consdata->negcliquepartition[i];
11275 assert(0 <= cliquenum && cliquenum <= nnegcliques);
11276
11277 weight = consdata->weights[i];
11278 assert(weight > 0);
11279
11280 if( cliquenum == nnegcliques )
11281 nnegcliques++;
11282 else
11283 {
11284 minactduetonegcliques += weight;
11285 if( secondmaxweights[cliquenum] == 0 )
11286 secondmaxweights[cliquenum] = weight;
11287 }
11288 }
11289 }
11290
11291 /* add cliques, using negated cliques information */
11292 if( minactduetonegcliques > 0 )
11293 {
11294 /* free capacity is the rest of not used capacity if the smallest amount of weights due to negated cliques are used */
11295 freecapacity = consdata->capacity - minactduetonegcliques;
11296
11297 SCIPdebugPrintCons(scip, cons, NULL);
11298 SCIPdebugMsg(scip, "Try to add cliques in knapsack constraint handler for constraint %s; capacity = %" SCIP_LONGINT_FORMAT ", minactivity(due to neg. cliques) = %" SCIP_LONGINT_FORMAT ", freecapacity = %" SCIP_LONGINT_FORMAT ".\n",
11299 SCIPconsGetName(cons), consdata->capacity, minactduetonegcliques, freecapacity);
11300
11301 /* create negated cliques out of negated cliques, if we do not take the smallest weight of a cliques ... */
11302 SCIP_CALL( addNegatedCliques(scip, cons, cutoff, nbdchgs ) );
11303
11304 if( *cutoff )
11305 goto TERMINATE;
11306
11307 nposcliquevars = 0;
11308
11309 for( i = nvars - 1; i >= 0; --i )
11310 {
11311 /* if we would take the biggest weight instead of the second biggest */
11312 cliquenum = consdata->negcliquepartition[i];
11313 if( consdata->weights[i] > secondmaxweights[cliquenum] )
11314 {
11315 poscliquevars[nposcliquevars] = consdata->vars[i];
11316 gainweights[nposcliquevars] = consdata->weights[i] - secondmaxweights[cliquenum];
11317 ++nposcliquevars;
11318 }
11319 }
11320
11321 /* use the gain weights and free capacity to derive greedily cliques */
11322 if( nposcliquevars > 1 )
11323 {
11324 SCIP_CALL( greedyCliqueAlgorithm(scip, poscliquevars, gainweights, nposcliquevars, freecapacity, FALSE, cliqueextractfactor, cutoff, nbdchgs) );
11325
11326 if( *cutoff )
11327 goto TERMINATE;
11328 }
11329 }
11330
11331 /* build cliques by using the items with the maximal weights */
11332 SCIP_CALL( greedyCliqueAlgorithm(scip, consdata->vars, consdata->weights, nvars, consdata->capacity, TRUE, cliqueextractfactor, cutoff, nbdchgs) );
11333
11334 TERMINATE:
11335 /* free temporary memory and mark the constraint */
11336 SCIPfreeBufferArray(scip, &secondmaxweights);
11337 SCIPfreeBufferArray(scip, &gainweights);
11338 SCIPfreeBufferArray(scip, &poscliquevars);
11339 consdata->cliquesadded = TRUE;
11340
11341 return SCIP_OKAY;
11342 }
11343
11344
11345 /** gets the key of the given element */
11346 static
SCIP_DECL_HASHGETKEY(hashGetKeyKnapsackcons)11347 SCIP_DECL_HASHGETKEY(hashGetKeyKnapsackcons)
11348 { /*lint --e{715}*/
11349 /* the key is the element itself */
11350 return elem;
11351 }
11352
11353 /** returns TRUE iff both keys are equal; two constraints are equal if they have the same variables and the
11354 * same coefficients
11355 */
11356 static
SCIP_DECL_HASHKEYEQ(hashKeyEqKnapsackcons)11357 SCIP_DECL_HASHKEYEQ(hashKeyEqKnapsackcons)
11358 {
11359 #ifndef NDEBUG
11360 SCIP* scip;
11361 #endif
11362 SCIP_CONSDATA* consdata1;
11363 SCIP_CONSDATA* consdata2;
11364 int i;
11365
11366 consdata1 = SCIPconsGetData((SCIP_CONS*)key1);
11367 consdata2 = SCIPconsGetData((SCIP_CONS*)key2);
11368 assert(consdata1->sorted);
11369 assert(consdata2->sorted);
11370 #ifndef NDEBUG
11371 scip = (SCIP*)userptr;
11372 assert(scip != NULL);
11373 #endif
11374
11375 /* checks trivial case */
11376 if( consdata1->nvars != consdata2->nvars )
11377 return FALSE;
11378
11379 for( i = consdata1->nvars - 1; i >= 0; --i )
11380 {
11381 /* tests if variables are equal */
11382 if( consdata1->vars[i] != consdata2->vars[i] )
11383 {
11384 assert(SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == 1 ||
11385 SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == -1);
11386 return FALSE;
11387 }
11388 assert(SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == 0);
11389
11390 /* tests if weights are equal too */
11391 if( consdata1->weights[i] != consdata2->weights[i] )
11392 return FALSE;
11393 }
11394
11395 return TRUE;
11396 }
11397
11398 /** returns the hash value of the key */
11399 static
SCIP_DECL_HASHKEYVAL(hashKeyValKnapsackcons)11400 SCIP_DECL_HASHKEYVAL(hashKeyValKnapsackcons)
11401 {
11402 #ifndef NDEBUG
11403 SCIP* scip;
11404 #endif
11405 SCIP_CONSDATA* consdata;
11406 uint64_t firstweight;
11407 int minidx;
11408 int mididx;
11409 int maxidx;
11410
11411 consdata = SCIPconsGetData((SCIP_CONS*)key);
11412 assert(consdata != NULL);
11413 assert(consdata->nvars > 0);
11414
11415 #ifndef NDEBUG
11416 scip = (SCIP*)userptr;
11417 assert(scip != NULL);
11418 #endif
11419
11420 /* sorts the constraints */
11421 sortItems(consdata);
11422
11423 minidx = SCIPvarGetIndex(consdata->vars[0]);
11424 mididx = SCIPvarGetIndex(consdata->vars[consdata->nvars / 2]);
11425 maxidx = SCIPvarGetIndex(consdata->vars[consdata->nvars - 1]);
11426 assert(minidx >= 0 && mididx >= 0 && maxidx >= 0);
11427
11428 /* hash value depends on vectors of variable indices */
11429 firstweight = (uint64_t)consdata->weights[0];
11430 return SCIPhashSix(consdata->nvars, minidx, mididx, maxidx, firstweight>>32, firstweight);
11431 }
11432
11433 /** compares each constraint with all other constraints for possible redundancy and removes or changes constraint
11434 * accordingly; in contrast to preprocessConstraintPairs(), it uses a hash table
11435 */
11436 static
detectRedundantConstraints(SCIP * scip,BMS_BLKMEM * blkmem,SCIP_CONS ** conss,int nconss,SCIP_Bool * cutoff,int * ndelconss)11437 SCIP_RETCODE detectRedundantConstraints(
11438 SCIP* scip, /**< SCIP data structure */
11439 BMS_BLKMEM* blkmem, /**< block memory */
11440 SCIP_CONS** conss, /**< constraint set */
11441 int nconss, /**< number of constraints in constraint set */
11442 SCIP_Bool* cutoff, /**< pointer to store whether the problem is infeasible */
11443 int* ndelconss /**< pointer to count number of deleted constraints */
11444 )
11445 {
11446 SCIP_HASHTABLE* hashtable;
11447 int hashtablesize;
11448 int c;
11449
11450 assert(scip != NULL);
11451 assert(blkmem != NULL);
11452 assert(conss != NULL);
11453 assert(ndelconss != NULL);
11454
11455 /* create a hash table for the constraint set */
11456 hashtablesize = nconss;
11457 hashtablesize = MAX(hashtablesize, HASHSIZE_KNAPSACKCONS);
11458 SCIP_CALL( SCIPhashtableCreate(&hashtable, blkmem, hashtablesize,
11459 hashGetKeyKnapsackcons, hashKeyEqKnapsackcons, hashKeyValKnapsackcons, (void*) scip) );
11460
11461 /* check all constraints in the given set for redundancy */
11462 for( c = nconss - 1; c >= 0; --c )
11463 {
11464 SCIP_CONS* cons0;
11465 SCIP_CONS* cons1;
11466 SCIP_CONSDATA* consdata0;
11467
11468 cons0 = conss[c];
11469
11470 if( !SCIPconsIsActive(cons0) || SCIPconsIsModifiable(cons0) )
11471 continue;
11472
11473 consdata0 = SCIPconsGetData(cons0);
11474 assert(consdata0 != NULL);
11475 if( consdata0->nvars == 0 )
11476 {
11477 if( consdata0->capacity < 0 )
11478 {
11479 *cutoff = TRUE;
11480 goto TERMINATE;
11481 }
11482 else
11483 {
11484 SCIP_CALL( SCIPdelCons(scip, cons0) );
11485 ++(*ndelconss);
11486 continue;
11487 }
11488 }
11489
11490 /* get constraint from current hash table with same variables and same weights as cons0 */
11491 cons1 = (SCIP_CONS*)(SCIPhashtableRetrieve(hashtable, (void*)cons0));
11492
11493 if( cons1 != NULL )
11494 {
11495 SCIP_CONS* consstay;
11496 SCIP_CONS* consdel;
11497 SCIP_CONSDATA* consdata1;
11498
11499 assert(SCIPconsIsActive(cons1));
11500 assert(!SCIPconsIsModifiable(cons1));
11501
11502 /* constraint found: create a new constraint with same coefficients and best left and right hand side;
11503 * delete old constraints afterwards
11504 */
11505 consdata1 = SCIPconsGetData(cons1);
11506
11507 assert(consdata1 != NULL);
11508 assert(consdata0->nvars > 0 && consdata0->nvars == consdata1->nvars);
11509
11510 assert(consdata0->sorted && consdata1->sorted);
11511 assert(consdata0->vars[0] == consdata1->vars[0]);
11512 assert(consdata0->weights[0] == consdata1->weights[0]);
11513
11514 SCIPdebugMsg(scip, "knapsack constraints <%s> and <%s> with equal coefficients\n",
11515 SCIPconsGetName(cons0), SCIPconsGetName(cons1));
11516
11517 /* check which constraint has to stay; */
11518 if( consdata0->capacity < consdata1->capacity )
11519 {
11520 consstay = cons0;
11521 consdel = cons1;
11522
11523 /* exchange consdel with consstay in hashtable */
11524 SCIP_CALL( SCIPhashtableRemove(hashtable, (void*) consdel) );
11525 SCIP_CALL( SCIPhashtableInsert(hashtable, (void*) consstay) );
11526 }
11527 else
11528 {
11529 consstay = cons1;
11530 consdel = cons0;
11531 }
11532
11533 /* update flags of constraint which caused the redundancy s.t. nonredundant information doesn't get lost */
11534 SCIP_CALL( SCIPupdateConsFlags(scip, consstay, consdel) );
11535
11536 /* delete consdel */
11537 SCIP_CALL( SCIPdelCons(scip, consdel) );
11538 ++(*ndelconss);
11539
11540 assert(SCIPconsIsActive(consstay));
11541 }
11542 else
11543 {
11544 /* no such constraint in current hash table: insert cons0 into hash table */
11545 SCIP_CALL( SCIPhashtableInsert(hashtable, (void*) cons0) );
11546 }
11547 }
11548
11549 TERMINATE:
11550 /* free hash table */
11551 SCIPhashtableFree(&hashtable);
11552
11553 return SCIP_OKAY;
11554 }
11555
11556
11557 /** compares constraint with all prior constraints for possible redundancy or aggregation,
11558 * and removes or changes constraint accordingly
11559 */
11560 static
preprocessConstraintPairs(SCIP * scip,SCIP_CONS ** conss,int firstchange,int chkind,int * ndelconss)11561 SCIP_RETCODE preprocessConstraintPairs(
11562 SCIP* scip, /**< SCIP data structure */
11563 SCIP_CONS** conss, /**< constraint set */
11564 int firstchange, /**< first constraint that changed since last pair preprocessing round */
11565 int chkind, /**< index of constraint to check against all prior indices upto startind */
11566 int* ndelconss /**< pointer to count number of deleted constraints */
11567 )
11568 {
11569 SCIP_CONS* cons0;
11570 SCIP_CONSDATA* consdata0;
11571 int c;
11572
11573 assert(scip != NULL);
11574 assert(conss != NULL);
11575 assert(firstchange <= chkind);
11576 assert(ndelconss != NULL);
11577
11578 /* get the constraint to be checked against all prior constraints */
11579 cons0 = conss[chkind];
11580 assert(cons0 != NULL);
11581 assert(SCIPconsIsActive(cons0));
11582 assert(!SCIPconsIsModifiable(cons0));
11583
11584 consdata0 = SCIPconsGetData(cons0);
11585 assert(consdata0 != NULL);
11586 assert(consdata0->nvars >= 1);
11587 assert(consdata0->merged);
11588
11589 /* sort the constraint */
11590 sortItems(consdata0);
11591
11592 /* see #2970 */
11593 if( consdata0->capacity == 0 )
11594 return SCIP_OKAY;
11595
11596 /* check constraint against all prior constraints */
11597 for( c = (consdata0->presolvedtiming == SCIP_PRESOLTIMING_EXHAUSTIVE ? firstchange : 0); c < chkind; ++c )
11598 {
11599 SCIP_CONS* cons1;
11600 SCIP_CONSDATA* consdata1;
11601 SCIP_Bool iscons0incons1contained;
11602 SCIP_Bool iscons1incons0contained;
11603 SCIP_Real quotient;
11604 int v;
11605 int v0;
11606 int v1;
11607
11608 cons1 = conss[c];
11609 assert(cons1 != NULL);
11610 if( !SCIPconsIsActive(cons1) || SCIPconsIsModifiable(cons1) )
11611 continue;
11612
11613 consdata1 = SCIPconsGetData(cons1);
11614 assert(consdata1 != NULL);
11615
11616 /* if both constraints didn't change since last pair processing, we can ignore the pair */
11617 if( consdata0->presolvedtiming >= SCIP_PRESOLTIMING_EXHAUSTIVE && consdata1->presolvedtiming >= SCIP_PRESOLTIMING_EXHAUSTIVE ) /*lint !e574*/
11618 continue;
11619
11620 assert(consdata1->nvars >= 1);
11621 assert(consdata1->merged);
11622
11623 /* sort the constraint */
11624 sortItems(consdata1);
11625
11626 /* see #2970 */
11627 if( consdata1->capacity == 0 )
11628 continue;
11629
11630 quotient = ((SCIP_Real) consdata0->capacity) / ((SCIP_Real) consdata1->capacity);
11631
11632 if( consdata0->nvars > consdata1->nvars )
11633 {
11634 iscons0incons1contained = FALSE;
11635 iscons1incons0contained = TRUE;
11636 v = consdata1->nvars - 1;
11637 }
11638 else if( consdata0->nvars < consdata1->nvars )
11639 {
11640 iscons0incons1contained = TRUE;
11641 iscons1incons0contained = FALSE;
11642 v = consdata0->nvars - 1;
11643 }
11644 else
11645 {
11646 iscons0incons1contained = TRUE;
11647 iscons1incons0contained = TRUE;
11648 v = consdata0->nvars - 1;
11649 }
11650
11651 SCIPdebugMsg(scip, "preprocess knapsack constraint pair <%s> and <%s>\n", SCIPconsGetName(cons0), SCIPconsGetName(cons1));
11652
11653 /* check consdata0 against consdata1:
11654 * 1. if all variables var_i of cons1 are in cons0 and for each of these variables
11655 * (consdata0->weights[i] / quotient) >= consdata1->weights[i] cons1 is redundant
11656 * 2. if all variables var_i of cons0 are in cons1 and for each of these variables
11657 * (consdata0->weights[i] / quotient) <= consdata1->weights[i] cons0 is redundant
11658 */
11659 v0 = consdata0->nvars - 1;
11660 v1 = consdata1->nvars - 1;
11661
11662 while( v >= 0 )
11663 {
11664 assert(iscons0incons1contained || iscons1incons0contained);
11665
11666 /* now there are more variables in cons1 left */
11667 if( v1 > v0 )
11668 {
11669 iscons1incons0contained = FALSE;
11670 if( !iscons0incons1contained )
11671 break;
11672 }
11673 /* now there are more variables in cons0 left */
11674 else if( v1 < v0 )
11675 {
11676 iscons0incons1contained = FALSE;
11677 if( !iscons1incons0contained )
11678 break;
11679 }
11680
11681 assert(v == v0 || v == v1);
11682 assert(v0 >= 0);
11683 assert(v1 >= 0);
11684
11685 /* both variables are the same */
11686 if( consdata0->vars[v0] == consdata1->vars[v1] )
11687 {
11688 /* if cons1 is possible contained in cons0 (consdata0->weights[v0] / quotient) must be greater equals consdata1->weights[v1] */
11689 if( iscons1incons0contained && SCIPisLT(scip, ((SCIP_Real) consdata0->weights[v0]) / quotient, (SCIP_Real) consdata1->weights[v1]) )
11690 {
11691 iscons1incons0contained = FALSE;
11692 if( !iscons0incons1contained )
11693 break;
11694 }
11695 /* if cons0 is possible contained in cons1 (consdata0->weight[v0] / quotient) must be less equals consdata1->weight[v1] */
11696 else if( iscons0incons1contained && SCIPisGT(scip, ((SCIP_Real) consdata0->weights[v0]) / quotient, (SCIP_Real) consdata1->weights[v1]) )
11697 {
11698 iscons0incons1contained = FALSE;
11699 if( !iscons1incons0contained )
11700 break;
11701 }
11702 --v0;
11703 --v1;
11704 --v;
11705 }
11706 else
11707 {
11708 /* both constraints have a variables which is not part of the other constraint, so stop */
11709 if( iscons0incons1contained && iscons1incons0contained )
11710 {
11711 iscons0incons1contained = FALSE;
11712 iscons1incons0contained = FALSE;
11713 break;
11714 }
11715 assert(iscons0incons1contained ? (v1 >= v0) : iscons1incons0contained);
11716 assert(iscons1incons0contained ? (v1 <= v0) : iscons0incons1contained);
11717 /* continue to the next variable */
11718 if( iscons0incons1contained )
11719 --v1;
11720 else
11721 --v0;
11722 }
11723 }
11724 /* neither one constraint was contained in another or we checked all variables of one constraint against the
11725 * other
11726 */
11727 assert(!iscons1incons0contained || !iscons0incons1contained || v0 == -1 || v1 == -1);
11728
11729 if( iscons1incons0contained )
11730 {
11731 SCIPdebugMsg(scip, "knapsack constraint <%s> is redundant\n", SCIPconsGetName(cons1));
11732 SCIPdebugPrintCons(scip, cons1, NULL);
11733
11734 /* update flags of constraint which caused the redundancy s.t. nonredundant information doesn't get lost */
11735 SCIP_CALL( SCIPupdateConsFlags(scip, cons0, cons1) );
11736
11737 SCIP_CALL( SCIPdelCons(scip, cons1) );
11738 ++(*ndelconss);
11739 }
11740 else if( iscons0incons1contained )
11741 {
11742 SCIPdebugMsg(scip, "knapsack constraint <%s> is redundant\n", SCIPconsGetName(cons0));
11743 SCIPdebugPrintCons(scip, cons0, NULL);
11744
11745 /* update flags of constraint which caused the redundancy s.t. nonredundant information doesn't get lost */
11746 SCIP_CALL( SCIPupdateConsFlags(scip, cons1, cons0) );
11747
11748 SCIP_CALL( SCIPdelCons(scip, cons0) );
11749 ++(*ndelconss);
11750 break;
11751 }
11752 }
11753
11754 return SCIP_OKAY;
11755 }
11756
11757 /** helper function to enforce constraints */
11758 static
enforceConstraint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_RESULT * result)11759 SCIP_RETCODE enforceConstraint(
11760 SCIP* scip, /**< SCIP data structure */
11761 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
11762 SCIP_CONS** conss, /**< constraints to process */
11763 int nconss, /**< number of constraints */
11764 int nusefulconss, /**< number of useful (non-obsolete) constraints to process */
11765 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
11766 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
11767 )
11768 {
11769 SCIP_CONSHDLRDATA* conshdlrdata;
11770 SCIP_Bool violated;
11771 SCIP_Bool cutoff = FALSE;
11772 int maxncuts;
11773 int ncuts = 0;
11774 int i;
11775
11776 *result = SCIP_FEASIBLE;
11777
11778 SCIPdebugMsg(scip, "knapsack enforcement of %d/%d constraints for %s solution\n", nusefulconss, nconss,
11779 sol == NULL ? "LP" : "relaxation");
11780
11781 /* get maximal number of cuts per round */
11782 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11783 assert(conshdlrdata != NULL);
11784 maxncuts = (SCIPgetDepth(scip) == 0 ? conshdlrdata->maxsepacutsroot : conshdlrdata->maxsepacuts);
11785
11786 /* search for violated useful knapsack constraints */
11787 for( i = 0; i < nusefulconss && ncuts < maxncuts && ! cutoff; i++ )
11788 {
11789 SCIP_CALL( checkCons(scip, conss[i], sol, FALSE, FALSE, &violated) );
11790 if( violated )
11791 {
11792 /* add knapsack constraint as LP row to the relaxation */
11793 SCIP_CALL( addRelaxation(scip, conss[i], &cutoff) );
11794 ncuts++;
11795 }
11796 }
11797
11798 /* as long as no violations were found, search for violated obsolete knapsack constraints */
11799 for( i = nusefulconss; i < nconss && ncuts == 0 && ! cutoff; i++ )
11800 {
11801 SCIP_CALL( checkCons(scip, conss[i], sol, FALSE, FALSE, &violated) );
11802 if( violated )
11803 {
11804 /* add knapsack constraint as LP row to the relaxation */
11805 SCIP_CALL( addRelaxation(scip, conss[i], &cutoff) );
11806 ncuts++;
11807 }
11808 }
11809
11810 /* adjust the result code */
11811 if ( cutoff )
11812 *result = SCIP_CUTOFF;
11813 else if ( ncuts > 0 )
11814 *result = SCIP_SEPARATED;
11815
11816 return SCIP_OKAY;
11817 }
11818
11819 /*
11820 * Linear constraint upgrading
11821 */
11822
11823 /** creates and captures a knapsack constraint out of a linear inequality */
11824 static
createNormalizedKnapsack(SCIP * scip,SCIP_CONS ** cons,const char * name,int nvars,SCIP_VAR ** vars,SCIP_Real * vals,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable,SCIP_Bool stickingatnode)11825 SCIP_RETCODE createNormalizedKnapsack(
11826 SCIP* scip, /**< SCIP data structure */
11827 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11828 const char* name, /**< name of constraint */
11829 int nvars, /**< number of variables in the constraint */
11830 SCIP_VAR** vars, /**< array with variables of constraint entries */
11831 SCIP_Real* vals, /**< array with inequality coefficients */
11832 SCIP_Real lhs, /**< left hand side of inequality */
11833 SCIP_Real rhs, /**< right hand side of inequality */
11834 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
11835 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
11836 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
11837 * Usually set to TRUE. */
11838 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
11839 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11840 SCIP_Bool check, /**< should the constraint be checked for feasibility?
11841 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11842 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
11843 * Usually set to TRUE. */
11844 SCIP_Bool local, /**< is constraint only valid locally?
11845 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
11846 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
11847 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
11848 * adds coefficients to this constraint. */
11849 SCIP_Bool dynamic, /**< is constraint subject to aging?
11850 * Usually set to FALSE. Set to TRUE for own cuts which
11851 * are separated as constraints. */
11852 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
11853 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
11854 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
11855 * if it may be moved to a more global node?
11856 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
11857 )
11858 {
11859 SCIP_VAR** transvars;
11860 SCIP_Longint* weights;
11861 SCIP_Longint capacity;
11862 SCIP_Longint weight;
11863 int mult;
11864 int v;
11865
11866 assert(nvars == 0 || vars != NULL);
11867 assert(nvars == 0 || vals != NULL);
11868 assert(SCIPisInfinity(scip, -lhs) != SCIPisInfinity(scip, rhs));
11869
11870 /* get temporary memory */
11871 SCIP_CALL( SCIPallocBufferArray(scip, &transvars, nvars) );
11872 SCIP_CALL( SCIPallocBufferArray(scip, &weights, nvars) );
11873
11874 /* if the right hand side is non-infinite, we have to negate all variables with negative coefficient;
11875 * otherwise, we have to negate all variables with positive coefficient and multiply the row with -1
11876 */
11877 if( SCIPisInfinity(scip, rhs) )
11878 {
11879 mult = -1;
11880 capacity = (SCIP_Longint)SCIPfeasFloor(scip, -lhs);
11881 }
11882 else
11883 {
11884 mult = +1;
11885 capacity = (SCIP_Longint)SCIPfeasFloor(scip, rhs);
11886 }
11887
11888 /* negate positive or negative variables */
11889 for( v = 0; v < nvars; ++v )
11890 {
11891 assert(SCIPisFeasIntegral(scip, vals[v]));
11892 weight = mult * (SCIP_Longint)SCIPfeasFloor(scip, vals[v]);
11893 if( weight > 0 )
11894 {
11895 transvars[v] = vars[v];
11896 weights[v] = weight;
11897 }
11898 else
11899 {
11900 SCIP_CALL( SCIPgetNegatedVar(scip, vars[v], &transvars[v]) );
11901 weights[v] = -weight; /*lint !e2704*/
11902 capacity -= weight;
11903 }
11904 assert(transvars[v] != NULL);
11905 }
11906
11907 /* create the constraint */
11908 SCIP_CALL( SCIPcreateConsKnapsack(scip, cons, name, nvars, transvars, weights, capacity,
11909 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
11910
11911 /* free temporary memory */
11912 SCIPfreeBufferArray(scip, &weights);
11913 SCIPfreeBufferArray(scip, &transvars);
11914
11915 return SCIP_OKAY;
11916 }
11917
11918 /** tries to upgrade a linear constraint into a knapsack constraint */
11919 static
SCIP_DECL_LINCONSUPGD(linconsUpgdKnapsack)11920 SCIP_DECL_LINCONSUPGD(linconsUpgdKnapsack)
11921 { /*lint --e{715}*/
11922 SCIP_Bool upgrade;
11923
11924 assert(upgdcons != NULL);
11925
11926 /* check, if linear constraint can be upgraded to a knapsack constraint
11927 * - all variables must be binary
11928 * - all coefficients must be integral
11929 * - exactly one of the sides must be infinite
11930 */
11931 upgrade = (nposbin + nnegbin + nposimplbin + nnegimplbin == nvars)
11932 && (ncoeffspone + ncoeffsnone + ncoeffspint + ncoeffsnint == nvars)
11933 && (SCIPisInfinity(scip, -lhs) != SCIPisInfinity(scip, rhs));
11934
11935 if( upgrade )
11936 {
11937 SCIPdebugMsg(scip, "upgrading constraint <%s> to knapsack constraint\n", SCIPconsGetName(cons));
11938
11939 /* create the knapsack constraint (an automatically upgraded constraint is always unmodifiable) */
11940 assert(!SCIPconsIsModifiable(cons));
11941 SCIP_CALL( createNormalizedKnapsack(scip, upgdcons, SCIPconsGetName(cons), nvars, vars, vals, lhs, rhs,
11942 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
11943 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
11944 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
11945 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
11946 }
11947
11948 return SCIP_OKAY;
11949 }
11950
11951
11952 /*
11953 * Callback methods of constraint handler
11954 */
11955
11956 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
11957 /**! [SnippetConsCopyKnapsack] */
11958 static
SCIP_DECL_CONSHDLRCOPY(conshdlrCopyKnapsack)11959 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyKnapsack)
11960 { /*lint --e{715}*/
11961 assert(scip != NULL);
11962 assert(conshdlr != NULL);
11963 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
11964
11965 /* call inclusion method of constraint handler */
11966 SCIP_CALL( SCIPincludeConshdlrKnapsack(scip) );
11967
11968 *valid = TRUE;
11969
11970 return SCIP_OKAY;
11971 }
11972 /**! [SnippetConsCopyKnapsack] */
11973
11974 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
11975 /**! [SnippetConsFreeKnapsack] */
11976 static
SCIP_DECL_CONSFREE(consFreeKnapsack)11977 SCIP_DECL_CONSFREE(consFreeKnapsack)
11978 { /*lint --e{715}*/
11979 SCIP_CONSHDLRDATA* conshdlrdata;
11980
11981 /* free constraint handler data */
11982 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11983 assert(conshdlrdata != NULL);
11984
11985 SCIPfreeBlockMemory(scip, &conshdlrdata);
11986
11987 SCIPconshdlrSetData(conshdlr, NULL);
11988
11989 return SCIP_OKAY;
11990 }
11991 /**! [SnippetConsFreeKnapsack] */
11992
11993
11994 /** initialization method of constraint handler (called after problem was transformed) */
11995 static
SCIP_DECL_CONSINIT(consInitKnapsack)11996 SCIP_DECL_CONSINIT(consInitKnapsack)
11997 { /*lint --e{715}*/
11998 SCIP_CONSHDLRDATA* conshdlrdata;
11999 int nvars;
12000
12001 assert( scip != NULL );
12002 assert( conshdlr != NULL );
12003
12004 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12005 assert(conshdlrdata != NULL);
12006
12007 /* all variables which are of integral type can be binary; this can be checked via the method SCIPvarIsBinary(var) */
12008 nvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
12009
12010 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->reals1, nvars) );
12011 conshdlrdata->reals1size = nvars;
12012
12013 return SCIP_OKAY;
12014 }
12015
12016 /** deinitialization method of constraint handler (called before transformed problem is freed) */
12017 static
SCIP_DECL_CONSEXIT(consExitKnapsack)12018 SCIP_DECL_CONSEXIT(consExitKnapsack)
12019 { /*lint --e{715}*/
12020 SCIP_CONSHDLRDATA* conshdlrdata;
12021
12022 assert( scip != NULL );
12023 assert( conshdlr != NULL );
12024
12025 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12026 assert(conshdlrdata != NULL);
12027
12028 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->reals1, conshdlrdata->reals1size);
12029 conshdlrdata->reals1size = 0;
12030
12031 return SCIP_OKAY;
12032 }
12033
12034
12035 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
12036 static
SCIP_DECL_CONSINITPRE(consInitpreKnapsack)12037 SCIP_DECL_CONSINITPRE(consInitpreKnapsack)
12038 { /*lint --e{715}*/
12039 SCIP_CONSHDLRDATA* conshdlrdata;
12040 int nvars;
12041
12042 assert(scip != NULL);
12043 assert(conshdlr != NULL);
12044 assert(nconss == 0 || conss != NULL);
12045
12046 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12047 assert(conshdlrdata != NULL);
12048
12049 /* all variables which are of integral type can be binary; this can be checked via the method SCIPvarIsBinary(var) */
12050 nvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
12051
12052 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->ints1, nvars) );
12053 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->ints2, nvars) );
12054 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->longints1, nvars) );
12055 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->longints2, nvars) );
12056 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->bools1, nvars) );
12057 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->bools2, nvars) );
12058 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->bools3, nvars) );
12059 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->bools4, nvars) );
12060
12061 conshdlrdata->ints1size = nvars;
12062 conshdlrdata->ints2size = nvars;
12063 conshdlrdata->longints1size = nvars;
12064 conshdlrdata->longints2size = nvars;
12065 conshdlrdata->bools1size = nvars;
12066 conshdlrdata->bools2size = nvars;
12067 conshdlrdata->bools3size = nvars;
12068 conshdlrdata->bools4size = nvars;
12069
12070 #ifdef WITH_CARDINALITY_UPGRADE
12071 conshdlrdata->upgradedcard = FALSE;
12072 #endif
12073
12074 return SCIP_OKAY;
12075 }
12076
12077
12078 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
12079 static
SCIP_DECL_CONSEXITPRE(consExitpreKnapsack)12080 SCIP_DECL_CONSEXITPRE(consExitpreKnapsack)
12081 { /*lint --e{715}*/
12082 SCIP_CONSHDLRDATA* conshdlrdata;
12083 int c;
12084
12085 assert(scip != NULL);
12086 assert(conshdlr != NULL);
12087
12088 for( c = 0; c < nconss; ++c )
12089 {
12090 if( !SCIPconsIsDeleted(conss[c]) )
12091 {
12092 /* since we are not allowed to detect infeasibility in the exitpre stage, we dont give an infeasible pointer */
12093 SCIP_CALL( applyFixings(scip, conss[c], NULL) );
12094 }
12095 }
12096
12097 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12098 assert(conshdlrdata != NULL);
12099
12100 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->ints1, conshdlrdata->ints1size);
12101 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->ints2, conshdlrdata->ints2size);
12102 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->longints1, conshdlrdata->longints1size);
12103 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->longints2, conshdlrdata->longints2size);
12104 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bools1, conshdlrdata->bools1size);
12105 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bools2, conshdlrdata->bools2size);
12106 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bools3, conshdlrdata->bools3size);
12107 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bools4, conshdlrdata->bools4size);
12108
12109 conshdlrdata->ints1size = 0;
12110 conshdlrdata->ints2size = 0;
12111 conshdlrdata->longints1size = 0;
12112 conshdlrdata->longints2size = 0;
12113 conshdlrdata->bools1size = 0;
12114 conshdlrdata->bools2size = 0;
12115 conshdlrdata->bools3size = 0;
12116 conshdlrdata->bools4size = 0;
12117
12118 return SCIP_OKAY;
12119 }
12120
12121
12122 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
12123 static
SCIP_DECL_CONSEXITSOL(consExitsolKnapsack)12124 SCIP_DECL_CONSEXITSOL(consExitsolKnapsack)
12125 { /*lint --e{715}*/
12126 SCIP_CONSDATA* consdata;
12127 int c;
12128
12129 assert( scip != NULL );
12130
12131 /* release the rows of all constraints */
12132 for( c = 0; c < nconss; ++c )
12133 {
12134 consdata = SCIPconsGetData(conss[c]);
12135 assert(consdata != NULL);
12136
12137 if( consdata->row != NULL )
12138 {
12139 SCIP_CALL( SCIPreleaseRow(scip, &consdata->row) );
12140 }
12141 }
12142
12143 return SCIP_OKAY;
12144 }
12145
12146 /** frees specific constraint data */
12147 static
SCIP_DECL_CONSDELETE(consDeleteKnapsack)12148 SCIP_DECL_CONSDELETE(consDeleteKnapsack)
12149 { /*lint --e{715}*/
12150 SCIP_CONSHDLRDATA* conshdlrdata;
12151
12152 assert(conshdlr != NULL);
12153 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12154
12155 /* get event handler */
12156 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12157 assert(conshdlrdata != NULL);
12158 assert(conshdlrdata->eventhdlr != NULL);
12159
12160 /* free knapsack constraint */
12161 SCIP_CALL( consdataFree(scip, consdata, conshdlrdata->eventhdlr) );
12162
12163 return SCIP_OKAY;
12164 }
12165
12166 /** transforms constraint data into data belonging to the transformed problem */
12167 /**! [SnippetConsTransKnapsack]*/
12168 static
SCIP_DECL_CONSTRANS(consTransKnapsack)12169 SCIP_DECL_CONSTRANS(consTransKnapsack)
12170 { /*lint --e{715}*/
12171 SCIP_CONSHDLRDATA* conshdlrdata;
12172 SCIP_CONSDATA* sourcedata;
12173 SCIP_CONSDATA* targetdata;
12174
12175 assert(conshdlr != NULL);
12176 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12177 assert(SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMING);
12178 assert(sourcecons != NULL);
12179 assert(targetcons != NULL);
12180
12181 sourcedata = SCIPconsGetData(sourcecons);
12182 assert(sourcedata != NULL);
12183 assert(sourcedata->row == NULL); /* in original problem, there cannot be LP rows */
12184
12185 /* get event handler */
12186 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12187 assert(conshdlrdata != NULL);
12188 assert(conshdlrdata->eventhdlr != NULL);
12189
12190 /* create target constraint data */
12191 SCIP_CALL( consdataCreate(scip, &targetdata,
12192 sourcedata->nvars, sourcedata->vars, sourcedata->weights, sourcedata->capacity) );
12193
12194 /* create target constraint */
12195 SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
12196 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
12197 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),
12198 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons),
12199 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
12200
12201 /* catch events for variables */
12202 SCIP_CALL( catchEvents(scip, *targetcons, targetdata, conshdlrdata->eventhdlr) );
12203
12204 return SCIP_OKAY;
12205 }
12206 /**! [SnippetConsTransKnapsack]*/
12207
12208 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
12209 static
SCIP_DECL_CONSINITLP(consInitlpKnapsack)12210 SCIP_DECL_CONSINITLP(consInitlpKnapsack)
12211 { /*lint --e{715}*/
12212 int i;
12213
12214 *infeasible = FALSE;
12215
12216 for( i = 0; i < nconss && !(*infeasible); i++ )
12217 {
12218 assert(SCIPconsIsInitial(conss[i]));
12219 SCIP_CALL( addRelaxation(scip, conss[i], infeasible) );
12220 }
12221
12222 return SCIP_OKAY;
12223 }
12224
12225 /** separation method of constraint handler for LP solutions */
12226 static
SCIP_DECL_CONSSEPALP(consSepalpKnapsack)12227 SCIP_DECL_CONSSEPALP(consSepalpKnapsack)
12228 { /*lint --e{715}*/
12229 SCIP_CONSHDLRDATA* conshdlrdata;
12230 SCIP_Bool sepacardinality;
12231 SCIP_Bool cutoff;
12232
12233 SCIP_Real loclowerbound;
12234 SCIP_Real glblowerbound;
12235 SCIP_Real cutoffbound;
12236 SCIP_Real maxbound;
12237
12238 int depth;
12239 int nrounds;
12240 int sepafreq;
12241 int sepacardfreq;
12242 int ncuts;
12243 int maxsepacuts;
12244 int i;
12245
12246 *result = SCIP_DIDNOTRUN;
12247
12248 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12249 assert(conshdlrdata != NULL);
12250
12251 depth = SCIPgetDepth(scip);
12252 nrounds = SCIPgetNSepaRounds(scip);
12253
12254 SCIPdebugMsg(scip, "knapsack separation of %d/%d constraints, round %d (max %d/%d)\n",
12255 nusefulconss, nconss, nrounds, conshdlrdata->maxroundsroot, conshdlrdata->maxrounds);
12256
12257 /* only call the separator a given number of times at each node */
12258 if( (depth == 0 && conshdlrdata->maxroundsroot >= 0 && nrounds >= conshdlrdata->maxroundsroot)
12259 || (depth > 0 && conshdlrdata->maxrounds >= 0 && nrounds >= conshdlrdata->maxrounds) )
12260 return SCIP_OKAY;
12261
12262 /* check, if we should additionally separate knapsack cuts */
12263 sepafreq = SCIPconshdlrGetSepaFreq(conshdlr);
12264 sepacardfreq = sepafreq * conshdlrdata->sepacardfreq;
12265 sepacardinality = (conshdlrdata->sepacardfreq >= 0)
12266 && ((sepacardfreq == 0 && depth == 0) || (sepacardfreq >= 1 && (depth % sepacardfreq == 0)));
12267
12268 /* check dual bound to see if we want to produce knapsack cuts at this node */
12269 loclowerbound = SCIPgetLocalLowerbound(scip);
12270 glblowerbound = SCIPgetLowerbound(scip);
12271 cutoffbound = SCIPgetCutoffbound(scip);
12272 maxbound = glblowerbound + conshdlrdata->maxcardbounddist * (cutoffbound - glblowerbound);
12273 sepacardinality = sepacardinality && SCIPisLE(scip, loclowerbound, maxbound);
12274 sepacardinality = sepacardinality && (SCIPgetNLPBranchCands(scip) > 0);
12275
12276 /* get the maximal number of cuts allowed in a separation round */
12277 maxsepacuts = (depth == 0 ? conshdlrdata->maxsepacutsroot : conshdlrdata->maxsepacuts);
12278
12279 *result = SCIP_DIDNOTFIND;
12280 ncuts = 0;
12281 cutoff = FALSE;
12282
12283 /* separate useful constraints */
12284 for( i = 0; i < nusefulconss && ncuts < maxsepacuts && !SCIPisStopped(scip); i++ )
12285 {
12286 SCIP_CALL( separateCons(scip, conss[i], NULL, sepacardinality, conshdlrdata->usegubs, &cutoff, &ncuts) );
12287 }
12288
12289 /* adjust return value */
12290 if ( cutoff )
12291 *result = SCIP_CUTOFF;
12292 else if ( ncuts > 0 )
12293 *result = SCIP_SEPARATED;
12294
12295 return SCIP_OKAY;
12296 }
12297
12298
12299 /** separation method of constraint handler for arbitrary primal solutions */
12300 static
SCIP_DECL_CONSSEPASOL(consSepasolKnapsack)12301 SCIP_DECL_CONSSEPASOL(consSepasolKnapsack)
12302 { /*lint --e{715}*/
12303 SCIP_CONSHDLRDATA* conshdlrdata;
12304 SCIP_Bool sepacardinality;
12305 SCIP_Bool cutoff;
12306
12307 int depth;
12308 int nrounds;
12309 int sepafreq;
12310 int sepacardfreq;
12311 int ncuts;
12312 int maxsepacuts;
12313 int i;
12314
12315 *result = SCIP_DIDNOTRUN;
12316
12317 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12318 assert(conshdlrdata != NULL);
12319
12320 depth = SCIPgetDepth(scip);
12321 nrounds = SCIPgetNSepaRounds(scip);
12322
12323 SCIPdebugMsg(scip, "knapsack separation of %d/%d constraints, round %d (max %d/%d)\n",
12324 nusefulconss, nconss, nrounds, conshdlrdata->maxroundsroot, conshdlrdata->maxrounds);
12325
12326 /* only call the separator a given number of times at each node */
12327 if( (depth == 0 && conshdlrdata->maxroundsroot >= 0 && nrounds >= conshdlrdata->maxroundsroot)
12328 || (depth > 0 && conshdlrdata->maxrounds >= 0 && nrounds >= conshdlrdata->maxrounds) )
12329 return SCIP_OKAY;
12330
12331 /* check, if we should additionally separate knapsack cuts */
12332 sepafreq = SCIPconshdlrGetSepaFreq(conshdlr);
12333 sepacardfreq = sepafreq * conshdlrdata->sepacardfreq;
12334 sepacardinality = (conshdlrdata->sepacardfreq >= 0)
12335 && ((sepacardfreq == 0 && depth == 0) || (sepacardfreq >= 1 && (depth % sepacardfreq == 0)));
12336
12337 /* get the maximal number of cuts allowed in a separation round */
12338 maxsepacuts = (depth == 0 ? conshdlrdata->maxsepacutsroot : conshdlrdata->maxsepacuts);
12339
12340 *result = SCIP_DIDNOTFIND;
12341 ncuts = 0;
12342 cutoff = FALSE;
12343
12344 /* separate useful constraints */
12345 for( i = 0; i < nusefulconss && ncuts < maxsepacuts && !SCIPisStopped(scip); i++ )
12346 {
12347 SCIP_CALL( separateCons(scip, conss[i], sol, sepacardinality, conshdlrdata->usegubs, &cutoff, &ncuts) );
12348 }
12349
12350 /* adjust return value */
12351 if ( cutoff )
12352 *result = SCIP_CUTOFF;
12353 else if( ncuts > 0 )
12354 *result = SCIP_SEPARATED;
12355
12356 return SCIP_OKAY;
12357 }
12358
12359 /** constraint enforcing method of constraint handler for LP solutions */
12360 static
SCIP_DECL_CONSENFOLP(consEnfolpKnapsack)12361 SCIP_DECL_CONSENFOLP(consEnfolpKnapsack)
12362 { /*lint --e{715}*/
12363 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, result) );
12364
12365 return SCIP_OKAY;
12366 }
12367
12368 /** constraint enforcing method of constraint handler for relaxation solutions */
12369 static
SCIP_DECL_CONSENFORELAX(consEnforelaxKnapsack)12370 SCIP_DECL_CONSENFORELAX(consEnforelaxKnapsack)
12371 { /*lint --e{715}*/
12372 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, result) );
12373
12374 return SCIP_OKAY;
12375 }
12376
12377 /** constraint enforcing method of constraint handler for pseudo solutions */
12378 static
SCIP_DECL_CONSENFOPS(consEnfopsKnapsack)12379 SCIP_DECL_CONSENFOPS(consEnfopsKnapsack)
12380 { /*lint --e{715}*/
12381 SCIP_Bool violated;
12382 int i;
12383
12384 for( i = 0; i < nconss; i++ )
12385 {
12386 SCIP_CALL( checkCons(scip, conss[i], NULL, TRUE, FALSE, &violated) );
12387 if( violated )
12388 {
12389 *result = SCIP_INFEASIBLE;
12390 return SCIP_OKAY;
12391 }
12392 }
12393 *result = SCIP_FEASIBLE;
12394
12395 return SCIP_OKAY;
12396 }
12397
12398 /** feasibility check method of constraint handler for integral solutions */
12399 static
SCIP_DECL_CONSCHECK(consCheckKnapsack)12400 SCIP_DECL_CONSCHECK(consCheckKnapsack)
12401 { /*lint --e{715}*/
12402 SCIP_Bool violated;
12403 int i;
12404
12405 *result = SCIP_FEASIBLE;
12406
12407 for( i = 0; i < nconss && (*result == SCIP_FEASIBLE || completely); i++ )
12408 {
12409 SCIP_CALL( checkCons(scip, conss[i], sol, checklprows, printreason, &violated) );
12410 if( violated )
12411 *result = SCIP_INFEASIBLE;
12412 }
12413
12414 return SCIP_OKAY;
12415 }
12416
12417 /** domain propagation method of constraint handler */
12418 static
SCIP_DECL_CONSPROP(consPropKnapsack)12419 SCIP_DECL_CONSPROP(consPropKnapsack)
12420 { /*lint --e{715}*/
12421 SCIP_CONSHDLRDATA* conshdlrdata;
12422 SCIP_Bool cutoff;
12423 SCIP_Bool redundant;
12424 SCIP_Bool inpresolve;
12425 int nfixedvars;
12426 int i;
12427
12428 cutoff = FALSE;
12429 nfixedvars = 0;
12430
12431 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12432 assert(conshdlrdata != NULL);
12433
12434 inpresolve = (SCIPgetStage(scip) < SCIP_STAGE_INITSOLVE);
12435 assert(!inpresolve || SCIPinProbing(scip));
12436
12437 /* process useful constraints */
12438 for( i = 0; i < nmarkedconss && !cutoff; i++ )
12439 {
12440 /* do not propagate constraints with multi-aggregated variables, which should only happen in probing mode,
12441 * otherwise the multi-aggregation should be resolved
12442 */
12443 if( inpresolve && SCIPconsGetData(conss[i])->existmultaggr )
12444 continue;
12445 #ifndef NDEBUG
12446 else
12447 assert(!(SCIPconsGetData(conss[i])->existmultaggr));
12448 #endif
12449
12450 SCIP_CALL( propagateCons(scip, conss[i], &cutoff, &redundant, &nfixedvars, conshdlrdata->negatedclique) );
12451
12452 /* unmark the constraint to be propagated */
12453 SCIP_CALL( SCIPunmarkConsPropagate(scip, conss[i]) );
12454 }
12455
12456 /* adjust result code */
12457 if( cutoff )
12458 *result = SCIP_CUTOFF;
12459 else if( nfixedvars > 0 )
12460 *result = SCIP_REDUCEDDOM;
12461 else
12462 *result = SCIP_DIDNOTFIND;
12463
12464 return SCIP_OKAY; /*lint !e438*/
12465 }
12466
12467 /** presolving method of constraint handler */
12468 static
SCIP_DECL_CONSPRESOL(consPresolKnapsack)12469 SCIP_DECL_CONSPRESOL(consPresolKnapsack)
12470 { /*lint --e{574,715}*/
12471 SCIP_CONSHDLRDATA* conshdlrdata;
12472 SCIP_CONSDATA* consdata;
12473 SCIP_CONS* cons;
12474 SCIP_Bool cutoff;
12475 SCIP_Bool redundant;
12476 SCIP_Bool success;
12477 int oldnfixedvars;
12478 int oldnchgbds;
12479 int oldndelconss;
12480 int oldnaddconss;
12481 int oldnchgcoefs;
12482 int oldnchgsides;
12483 int firstchange;
12484 int c;
12485 SCIP_Bool newchanges;
12486
12487 /* remember old preprocessing counters */
12488 cutoff = FALSE;
12489 oldnfixedvars = *nfixedvars;
12490 oldnchgbds = *nchgbds;
12491 oldndelconss = *ndelconss;
12492 oldnaddconss = *naddconss;
12493 oldnchgcoefs = *nchgcoefs;
12494 oldnchgsides = *nchgsides;
12495 firstchange = INT_MAX;
12496
12497 newchanges = (nrounds == 0 || nnewfixedvars > 0 || nnewaggrvars > 0 || nnewchgbds > 0 || nnewupgdconss > 0);
12498
12499 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12500 assert(conshdlrdata != NULL);
12501
12502 for( c = 0; c < nconss && !SCIPisStopped(scip); c++ )
12503 {
12504 int thisnfixedvars;
12505 int thisnchgbds;
12506
12507 cons = conss[c];
12508 consdata = SCIPconsGetData(cons);
12509 assert(consdata != NULL);
12510
12511 /* update data structures */
12512 /* todo if UBTIGHTENED events were caught, we could move this block after the continue */
12513 if( newchanges || *nfixedvars > oldnfixedvars || *nchgbds > oldnchgbds )
12514 {
12515 SCIP_CALL( applyFixings(scip, cons, &cutoff) );
12516 if( cutoff )
12517 break;
12518 }
12519
12520 /* force presolving the constraint in the initial round */
12521 if( nrounds == 0 )
12522 consdata->presolvedtiming = 0;
12523 else if( consdata->presolvedtiming >= presoltiming )
12524 continue;
12525
12526 SCIPdebugMsg(scip, "presolving knapsack constraint <%s>\n", SCIPconsGetName(cons));
12527 SCIPdebugPrintCons(scip, cons, NULL);
12528 consdata->presolvedtiming = presoltiming;
12529
12530 thisnfixedvars = *nfixedvars;
12531 thisnchgbds = *nchgbds;
12532
12533 /* merge constraint, so propagation works better */
12534 SCIP_CALL( mergeMultiples(scip, cons, &cutoff) );
12535 if( cutoff )
12536 break;
12537
12538 /* add cliques in the knapsack to the clique table */
12539 if( (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
12540 {
12541 SCIP_CALL( addCliques(scip, cons, conshdlrdata->cliqueextractfactor, &cutoff, nchgbds) );
12542 if( cutoff )
12543 break;
12544 }
12545
12546 /* propagate constraint */
12547 if( presoltiming < SCIP_PRESOLTIMING_EXHAUSTIVE )
12548 {
12549 SCIP_CALL( propagateCons(scip, cons, &cutoff, &redundant, nfixedvars, (presoltiming & SCIP_PRESOLTIMING_MEDIUM)) );
12550
12551 if( cutoff )
12552 break;
12553 if( redundant )
12554 {
12555 (*ndelconss)++;
12556 continue;
12557 }
12558 }
12559
12560 /* remove again all fixed variables, if further fixings were found */
12561 if( *nfixedvars > thisnfixedvars || *nchgbds > thisnchgbds )
12562 {
12563 SCIP_CALL( applyFixings(scip, cons, &cutoff) );
12564 if( cutoff )
12565 break;
12566
12567 thisnfixedvars = *nfixedvars;
12568 }
12569
12570 if( !SCIPconsIsModifiable(cons) )
12571 {
12572 /* check again for redundancy (applyFixings() might have decreased weightsum due to fixed-to-zero vars) */
12573 if( consdata->weightsum <= consdata->capacity )
12574 {
12575 SCIPdebugMsg(scip, " -> knapsack constraint <%s> is redundant: weightsum=%" SCIP_LONGINT_FORMAT ", capacity=%" SCIP_LONGINT_FORMAT "\n",
12576 SCIPconsGetName(cons), consdata->weightsum, consdata->capacity);
12577 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
12578 continue;
12579 }
12580
12581 /* divide weights by their greatest common divisor */
12582 normalizeWeights(cons, nchgcoefs, nchgsides);
12583
12584 /* try to simplify inequalities */
12585 if( conshdlrdata->simplifyinequalities && (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 )
12586 {
12587 SCIP_CALL( simplifyInequalities(scip, cons, nfixedvars, ndelconss, nchgcoefs, nchgsides, naddconss, &cutoff) );
12588 if( cutoff )
12589 break;
12590
12591 if( SCIPconsIsDeleted(cons) )
12592 continue;
12593
12594 /* remove again all fixed variables, if further fixings were found */
12595 if( *nfixedvars > thisnfixedvars )
12596 {
12597 SCIP_CALL(applyFixings(scip, cons, &cutoff));
12598 if( cutoff )
12599 break;
12600 }
12601 }
12602
12603 /* tighten capacity and weights */
12604 SCIP_CALL( tightenWeights(scip, cons, presoltiming, nchgcoefs, nchgsides, naddconss, ndelconss, &cutoff) );
12605 if( cutoff )
12606 break;
12607
12608 if( SCIPconsIsActive(cons) )
12609 {
12610 if( conshdlrdata->dualpresolving && SCIPallowStrongDualReds(scip) && (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
12611 {
12612 /* in case the knapsack constraints is independent of everything else, solve the knapsack and apply the
12613 * dual reduction
12614 */
12615 SCIP_CALL( dualPresolving(scip, cons, nchgbds, ndelconss, &redundant) );
12616 if( redundant )
12617 continue;
12618 }
12619
12620 /* check if knapsack constraint is parallel to objective function */
12621 SCIP_CALL( checkParallelObjective(scip, cons, conshdlrdata) );
12622 }
12623 }
12624 /* remember the first changed constraint to begin the next aggregation round with */
12625 if( firstchange == INT_MAX && consdata->presolvedtiming != SCIP_PRESOLTIMING_EXHAUSTIVE )
12626 firstchange = c;
12627 }
12628
12629 /* preprocess pairs of knapsack constraints */
12630 if( !cutoff && conshdlrdata->presolusehashing && (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
12631 {
12632 /* detect redundant constraints; fast version with hash table instead of pairwise comparison */
12633 SCIP_CALL( detectRedundantConstraints(scip, SCIPblkmem(scip), conss, nconss, &cutoff, ndelconss) );
12634 }
12635
12636 if( (*ndelconss != oldndelconss) || (*nchgsides != oldnchgsides) || (*nchgcoefs != oldnchgcoefs) || (*naddconss != oldnaddconss) )
12637 success = TRUE;
12638 else
12639 success = FALSE;
12640
12641 if( !cutoff && firstchange < nconss && conshdlrdata->presolpairwise && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 )
12642 {
12643 SCIP_Longint npaircomparisons;
12644
12645 npaircomparisons = 0;
12646 oldndelconss = *ndelconss;
12647 oldnchgsides = *nchgsides;
12648 oldnchgcoefs = *nchgcoefs;
12649
12650 for( c = firstchange; c < nconss && !cutoff && !SCIPisStopped(scip); ++c )
12651 {
12652 cons = conss[c];
12653 if( !SCIPconsIsActive(cons) || SCIPconsIsModifiable(cons) )
12654 continue;
12655
12656 npaircomparisons += ((SCIPconsGetData(cons)->presolvedtiming < SCIP_PRESOLTIMING_EXHAUSTIVE) ? (SCIP_Longint) c : ((SCIP_Longint) c - (SCIP_Longint) firstchange));
12657
12658 SCIP_CALL( preprocessConstraintPairs(scip, conss, firstchange, c, ndelconss) );
12659
12660 if( npaircomparisons > NMINCOMPARISONS )
12661 {
12662 if( (*ndelconss != oldndelconss) || (*nchgsides != oldnchgsides) || (*nchgcoefs != oldnchgcoefs) )
12663 success = TRUE;
12664 if( ((SCIP_Real) (*ndelconss - oldndelconss) + ((SCIP_Real) (*nchgsides - oldnchgsides))/2.0 +
12665 ((SCIP_Real) (*nchgcoefs - oldnchgcoefs))/10.0) / ((SCIP_Real) npaircomparisons) < MINGAINPERNMINCOMPARISONS )
12666 break;
12667 oldndelconss = *ndelconss;
12668 oldnchgsides = *nchgsides;
12669 oldnchgcoefs = *nchgcoefs;
12670 npaircomparisons = 0;
12671 }
12672 }
12673 }
12674 #ifdef WITH_CARDINALITY_UPGRADE
12675 /* @todo upgrade to cardinality constraints: the code below relies on disabling the checking of the knapsack
12676 * constraint in the original problem, because the upgrade ensures that at most the given number of continuous
12677 * variables has a nonzero value, but not that the binary variables corresponding to the continuous variables with
12678 * value zero are set to zero as well. This can cause problems if the user accesses the values of the binary
12679 * variables (as the MIPLIB solution checker does), or the transformed problem is freed and the original problem
12680 * (possibly with some user modifications) is re-optimized. Until there is a way to force the binary variables to 0
12681 * as well, we better keep this code disabled. */
12682 /* upgrade to cardinality constraints - only try to upgrade towards the end of presolving, since the process below is quite expensive */
12683 if ( ! cutoff && conshdlrdata->upgdcardinality && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 && SCIPisPresolveFinished(scip) && ! conshdlrdata->upgradedcard )
12684 {
12685 SCIP_HASHMAP* varhash;
12686 SCIP_VAR** cardvars;
12687 SCIP_Real* cardweights;
12688 int noldupgdconss;
12689 int nscipvars;
12690 int makeupgrade;
12691
12692 noldupgdconss = *nupgdconss;
12693 nscipvars = SCIPgetNVars(scip);
12694 SCIP_CALL( SCIPallocClearBufferArray(scip, &cardvars, nscipvars) );
12695 SCIP_CALL( SCIPallocClearBufferArray(scip, &cardweights, nscipvars) );
12696
12697 /* set up hash map */
12698 SCIP_CALL( SCIPhashmapCreate(&varhash, SCIPblkmem(scip), nscipvars) );
12699
12700 /* We loop through all cardinality constraints twice:
12701 * - First, determine for each binary variable the number of cardinality constraints that can be upgraded to a
12702 * knapsack constraint and contain this variable; this number has to coincide with the number of variable up
12703 * locks; otherwise it would be infeasible to delete the knapsack constraints after the constraint update.
12704 * - Second, upgrade knapsack constraints to cardinality constraints. */
12705 for (makeupgrade = 0; makeupgrade < 2; ++makeupgrade)
12706 {
12707 for (c = nconss-1; c >= 0 && ! SCIPisStopped(scip); --c)
12708 {
12709 SCIP_CONS* cardcons;
12710 SCIP_VAR** vars;
12711 SCIP_Longint* weights;
12712 int nvars;
12713 int v;
12714
12715 cons = conss[c];
12716 assert( cons != NULL );
12717 consdata = SCIPconsGetData(cons);
12718 assert( consdata != NULL );
12719
12720 nvars = consdata->nvars;
12721 vars = consdata->vars;
12722 weights = consdata->weights;
12723
12724 /* Check, whether linear knapsack can be upgraded to a cardinality constraint:
12725 * - all variables must be binary (always true)
12726 * - all coefficients must be 1.0
12727 * - the right hand side must be smaller than nvars
12728 */
12729 if ( consdata->capacity >= nvars )
12730 continue;
12731
12732 /* the weights are sorted: check first and last weight */
12733 assert( consdata->sorted );
12734 if ( weights[0] != 1 || weights[nvars-1] != 1 )
12735 continue;
12736
12737 /* check whether all variables are of the form 0 <= x_v <= u_v y_v for y_v \in \{0,1\} and zero objective */
12738 for (v = 0; v < nvars; ++v)
12739 {
12740 SCIP_BOUNDTYPE* impltypes;
12741 SCIP_Real* implbounds;
12742 SCIP_VAR** implvars;
12743 SCIP_VAR* var;
12744 int nimpls;
12745 int j;
12746
12747 var = consdata->vars[v];
12748 assert( var != NULL );
12749 assert( SCIPvarIsBinary(var) );
12750
12751 /* ignore non-active variables */
12752 if ( ! SCIPvarIsActive(var) )
12753 break;
12754
12755 /* be sure that implication variable has zero objective */
12756 if ( ! SCIPisZero(scip, SCIPvarGetObj(var)) )
12757 break;
12758
12759 nimpls = SCIPvarGetNImpls(var, FALSE);
12760 implvars = SCIPvarGetImplVars(var, FALSE);
12761 implbounds = SCIPvarGetImplBounds(var, FALSE);
12762 impltypes = SCIPvarGetImplTypes(var, FALSE);
12763
12764 for (j = 0; j < nimpls; ++j)
12765 {
12766 /* be sure that continuous variable is fixed to 0 */
12767 if ( impltypes[j] != SCIP_BOUNDTYPE_UPPER )
12768 continue;
12769
12770 /* cannot currently deal with nonzero fixings */
12771 if ( ! SCIPisZero(scip, implbounds[j]) )
12772 continue;
12773
12774 /* number of down locks should be one */
12775 if ( SCIPvarGetNLocksDownType(vars[v], SCIP_LOCKTYPE_MODEL) != 1 )
12776 continue;
12777
12778 cardvars[v] = implvars[j];
12779 cardweights[v] = (SCIP_Real) v;
12780
12781 break;
12782 }
12783
12784 /* found no variable upper bound candidate -> exit */
12785 if ( j >= nimpls )
12786 break;
12787 }
12788
12789 /* did not find fitting variable upper bound for some variable -> exit */
12790 if ( v < nvars )
12791 break;
12792
12793 /* save number of knapsack constraints that can be upgraded to a cardinality constraint,
12794 * in which the binary variable is involved in */
12795 if ( makeupgrade == 0 )
12796 {
12797 for (v = 0; v < nvars; ++v)
12798 {
12799 if ( SCIPhashmapExists(varhash, vars[v]) )
12800 {
12801 int image;
12802
12803 image = SCIPhashmapGetImageInt(varhash, vars[v]);
12804 SCIP_CALL( SCIPhashmapSetImageInt(varhash, vars[v], image + 1) );
12805 assert( image + 1 == SCIPhashmapGetImageInt(varhash, vars[v]) );
12806 }
12807 else
12808 {
12809 SCIP_CALL( SCIPhashmapInsertInt(varhash, vars[v], 1) );
12810 assert( 1 == SCIPhashmapGetImageInt(varhash, vars[v]) );
12811 assert( SCIPhashmapExists(varhash, vars[v]) );
12812 }
12813 }
12814 }
12815 else
12816 {
12817 SCIP_CONS* origcons;
12818
12819 /* for each variable: check whether the number of cardinality constraints that can be upgraded to a
12820 * knapsack constraint coincides with the number of variable up locks */
12821 for (v = 0; v < nvars; ++v)
12822 {
12823 assert( SCIPhashmapExists(varhash, vars[v]) );
12824 if ( SCIPvarGetNLocksUpType(vars[v], SCIP_LOCKTYPE_MODEL) != SCIPhashmapGetImageInt(varhash, vars[v]) )
12825 break;
12826 }
12827 if ( v < nvars )
12828 break;
12829
12830 /* store that we have upgraded */
12831 conshdlrdata->upgradedcard = TRUE;
12832
12833 /* at this point we found suitable variable upper bounds */
12834 SCIPdebugMessage("Upgrading knapsack constraint <%s> to cardinality constraint ...\n", SCIPconsGetName(cons));
12835
12836 /* create cardinality constraint */
12837 assert( ! SCIPconsIsModifiable(cons) );
12838 SCIP_CALL( SCIPcreateConsCardinality(scip, &cardcons, SCIPconsGetName(cons), nvars, cardvars, (int) consdata->capacity, vars, cardweights,
12839 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
12840 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
12841 SCIPconsIsLocal(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
12842 #ifdef SCIP_DEBUG
12843 SCIPprintCons(scip, cons, NULL);
12844 SCIPinfoMessage(scip, NULL, "\n");
12845 SCIPprintCons(scip, cardcons, NULL);
12846 SCIPinfoMessage(scip, NULL, "\n");
12847 #endif
12848 SCIP_CALL( SCIPaddCons(scip, cardcons) );
12849 SCIP_CALL( SCIPreleaseCons(scip, &cardcons) );
12850 ++(*nupgdconss);
12851
12852 /* delete oknapsack constraint */
12853 SCIP_CALL( SCIPdelCons(scip, cons) );
12854 ++(*ndelconss);
12855
12856 /* We need to disable the original knapsack constraint, since it might happen that the binary variables
12857 * are 1 although the continuous variables are 0. Thus, the knapsack constraint might be violated,
12858 * although the cardinality constraint is satisfied. */
12859 origcons = SCIPfindOrigCons(scip, SCIPconsGetName(cons));
12860 assert( origcons != NULL );
12861 SCIP_CALL( SCIPsetConsChecked(scip, origcons, FALSE) );
12862
12863 for (v = 0; v < nvars; ++v)
12864 {
12865 int image;
12866
12867 assert ( SCIPhashmapExists(varhash, vars[v]) );
12868 image = SCIPhashmapGetImageInt(varhash, vars[v]);
12869 SCIP_CALL( SCIPhashmapSetImageInt(varhash, vars[v], image - 1) );
12870 assert( image - 1 == SCIPhashmapGetImageInt(varhash, vars[v]) );
12871 }
12872 }
12873 }
12874 }
12875 SCIPhashmapFree(&varhash);
12876 SCIPfreeBufferArray(scip, &cardweights);
12877 SCIPfreeBufferArray(scip, &cardvars);
12878
12879 if ( *nupgdconss > noldupgdconss )
12880 success = TRUE;
12881 }
12882 #endif
12883
12884 if( cutoff )
12885 *result = SCIP_CUTOFF;
12886 else if( success || *nfixedvars > oldnfixedvars || *nchgbds > oldnchgbds )
12887 *result = SCIP_SUCCESS;
12888 else
12889 *result = SCIP_DIDNOTFIND;
12890
12891 return SCIP_OKAY;
12892 }
12893
12894 /** propagation conflict resolving method of constraint handler */
12895 static
SCIP_DECL_CONSRESPROP(consRespropKnapsack)12896 SCIP_DECL_CONSRESPROP(consRespropKnapsack)
12897 { /*lint --e{715}*/
12898 SCIP_CONSDATA* consdata;
12899 SCIP_Longint capsum;
12900 int i;
12901
12902 assert(result != NULL);
12903
12904 consdata = SCIPconsGetData(cons);
12905 assert(consdata != NULL);
12906
12907 /* check if we fixed a binary variable to one (due to negated clique) */
12908 if( inferinfo >= 0 && SCIPvarGetLbLocal(infervar) > 0.5 )
12909 {
12910 for( i = 0; i < consdata->nvars; ++i )
12911 {
12912 if( SCIPvarGetIndex(consdata->vars[i]) == inferinfo )
12913 {
12914 assert( SCIPgetVarUbAtIndex(scip, consdata->vars[i], bdchgidx, FALSE) < 0.5 );
12915 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[i]) );
12916 break;
12917 }
12918 }
12919 assert(i < consdata->nvars);
12920 }
12921 else
12922 {
12923 /* according to negated cliques the minweightsum and all variables which are fixed to one which led to a fixing of
12924 * another negated clique variable to one, the inferinfo was chosen to be the negative of the position in the
12925 * knapsack constraint, see one above call of SCIPinferBinvarCons
12926 */
12927 if( inferinfo < 0 )
12928 capsum = 0;
12929 else
12930 {
12931 /* locate the inference variable and calculate the capacity that has to be used up to conclude infervar == 0;
12932 * inferinfo stores the position of the inference variable (but maybe the variables were resorted)
12933 */
12934 if( inferinfo < consdata->nvars && consdata->vars[inferinfo] == infervar )
12935 capsum = consdata->weights[inferinfo];
12936 else
12937 {
12938 for( i = 0; i < consdata->nvars && consdata->vars[i] != infervar; ++i )
12939 {}
12940 assert(i < consdata->nvars);
12941 capsum = consdata->weights[i];
12942 }
12943 }
12944
12945 /* add fixed-to-one variables up to the point, that their weight plus the weight of the conflict variable exceeds
12946 * the capacity
12947 */
12948 if( capsum <= consdata->capacity )
12949 {
12950 for( i = 0; i < consdata->nvars; i++ )
12951 {
12952 if( SCIPgetVarLbAtIndex(scip, consdata->vars[i], bdchgidx, FALSE) > 0.5 )
12953 {
12954 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[i]) );
12955 capsum += consdata->weights[i];
12956 if( capsum > consdata->capacity )
12957 break;
12958 }
12959 }
12960 }
12961 }
12962
12963 /* NOTE: It might be the case that capsum < consdata->capacity. This is due the fact that the fixing of the variable
12964 * to zero can included negated clique information. A negated clique means, that at most one of the clique
12965 * variables can be zero. These information can be used to compute a minimum activity of the constraint and
12966 * used to fix variables to zero.
12967 *
12968 * Even if capsum < consdata->capacity we still reported a complete reason since the minimum activity is based
12969 * on global variable bounds. It might even be the case that we reported to many variables which are fixed to
12970 * one.
12971 */
12972 *result = SCIP_SUCCESS;
12973
12974 return SCIP_OKAY;
12975 }
12976
12977 /** variable rounding lock method of constraint handler */
12978 /**! [SnippetConsLockKnapsack] */
12979 static
SCIP_DECL_CONSLOCK(consLockKnapsack)12980 SCIP_DECL_CONSLOCK(consLockKnapsack)
12981 { /*lint --e{715}*/
12982 SCIP_CONSDATA* consdata;
12983 int i;
12984
12985 consdata = SCIPconsGetData(cons);
12986 assert(consdata != NULL);
12987
12988 for( i = 0; i < consdata->nvars; i++)
12989 {
12990 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->vars[i], locktype, nlocksneg, nlockspos) );
12991 }
12992
12993 return SCIP_OKAY;
12994 }
12995 /**! [SnippetConsLockKnapsack] */
12996
12997
12998 /** variable deletion method of constraint handler */
12999 static
SCIP_DECL_CONSDELVARS(consDelvarsKnapsack)13000 SCIP_DECL_CONSDELVARS(consDelvarsKnapsack)
13001 {
13002 assert(scip != NULL);
13003 assert(conshdlr != NULL);
13004 assert(conss != NULL || nconss == 0);
13005
13006 if( nconss > 0 )
13007 {
13008 SCIP_CALL( performVarDeletions(scip, conshdlr, conss, nconss) );
13009 }
13010
13011 return SCIP_OKAY;
13012 }
13013
13014 /** constraint display method of constraint handler */
13015 static
SCIP_DECL_CONSPRINT(consPrintKnapsack)13016 SCIP_DECL_CONSPRINT(consPrintKnapsack)
13017 { /*lint --e{715}*/
13018 SCIP_CONSDATA* consdata;
13019 int i;
13020
13021 assert( scip != NULL );
13022 assert( conshdlr != NULL );
13023 assert( cons != NULL );
13024
13025 consdata = SCIPconsGetData(cons);
13026 assert(consdata != NULL);
13027
13028 for( i = 0; i < consdata->nvars; ++i )
13029 {
13030 if( i > 0 )
13031 SCIPinfoMessage(scip, file, " ");
13032 SCIPinfoMessage(scip, file, "%+" SCIP_LONGINT_FORMAT, consdata->weights[i]);
13033 SCIP_CALL( SCIPwriteVarName(scip, file, consdata->vars[i], TRUE) );
13034 }
13035 SCIPinfoMessage(scip, file, " <= %" SCIP_LONGINT_FORMAT "", consdata->capacity);
13036
13037 return SCIP_OKAY;
13038 }
13039
13040 /** constraint copying method of constraint handler */
13041 static
SCIP_DECL_CONSCOPY(consCopyKnapsack)13042 SCIP_DECL_CONSCOPY(consCopyKnapsack)
13043 { /*lint --e{715}*/
13044 SCIP_VAR** sourcevars;
13045 SCIP_Longint* weights;
13046 SCIP_Real* coefs;
13047 const char* consname;
13048 int nvars;
13049 int v;
13050
13051 /* get variables and coefficients of the source constraint */
13052 sourcevars = SCIPgetVarsKnapsack(sourcescip, sourcecons);
13053 nvars = SCIPgetNVarsKnapsack(sourcescip, sourcecons);
13054 weights = SCIPgetWeightsKnapsack(sourcescip, sourcecons);
13055
13056 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nvars) );
13057 for( v = 0; v < nvars; ++v )
13058 coefs[v] = (SCIP_Real) weights[v];
13059
13060 if( name != NULL )
13061 consname = name;
13062 else
13063 consname = SCIPconsGetName(sourcecons);
13064
13065 /* copy the logic using the linear constraint copy method */
13066 SCIP_CALL( SCIPcopyConsLinear(scip, cons, sourcescip, consname, nvars, sourcevars, coefs,
13067 -SCIPinfinity(scip), (SCIP_Real) SCIPgetCapacityKnapsack(sourcescip, sourcecons), varmap, consmap,
13068 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode, global, valid) );
13069 assert(cons != NULL);
13070
13071 SCIPfreeBufferArray(scip, &coefs);
13072
13073 return SCIP_OKAY;
13074 }
13075
13076 /** constraint parsing method of constraint handler */
13077 static
SCIP_DECL_CONSPARSE(consParseKnapsack)13078 SCIP_DECL_CONSPARSE(consParseKnapsack)
13079 { /*lint --e{715}*/
13080 SCIP_VAR* var;
13081 SCIP_Longint weight;
13082 SCIP_VAR** vars;
13083 SCIP_Longint* weights;
13084 SCIP_Longint capacity;
13085 char* endptr;
13086 int nread;
13087 int nvars;
13088 int varssize;
13089
13090 assert(scip != NULL);
13091 assert(success != NULL);
13092 assert(str != NULL);
13093 assert(name != NULL);
13094 assert(cons != NULL);
13095
13096 *success = TRUE;
13097
13098 nvars = 0;
13099 varssize = 5;
13100 SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) );
13101 SCIP_CALL( SCIPallocBufferArray(scip, &weights, varssize) );
13102
13103 while( *str != '\0' )
13104 {
13105 /* try to parse coefficient, and stop if not successful (probably reached <=) */
13106 if( sscanf(str, "%" SCIP_LONGINT_FORMAT "%n", &weight, &nread) < 1 )
13107 break;
13108
13109 str += nread;
13110
13111 /* skip whitespace */
13112 while( isspace((int)*str) )
13113 ++str;
13114
13115 /* parse variable name */
13116 SCIP_CALL( SCIPparseVarName(scip, str, &var, &endptr) );
13117 if( var == NULL )
13118 {
13119 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown variable name at '%s'\n", str);
13120 *success = FALSE;
13121 break;
13122 }
13123
13124 str = endptr;
13125
13126 /* store weight and variable */
13127 if( varssize <= nvars )
13128 {
13129 varssize = SCIPcalcMemGrowSize(scip, varssize+1);
13130 SCIP_CALL( SCIPreallocBufferArray(scip, &vars, varssize) );
13131 SCIP_CALL( SCIPreallocBufferArray(scip, &weights, varssize) );
13132 }
13133
13134 vars[nvars] = var;
13135 weights[nvars] = weight;
13136 ++nvars;
13137
13138 /* skip whitespace */
13139 while( isspace((int)*str) )
13140 ++str;
13141 }
13142
13143 if( *success )
13144 {
13145 if( strncmp(str, "<= ", 3) != 0 )
13146 {
13147 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected '<= ' at begin of '%s'\n", str);
13148 *success = FALSE;
13149 }
13150 else
13151 {
13152 str += 3;
13153 }
13154 }
13155
13156 if( *success )
13157 {
13158 /* coverity[secure_coding] */
13159 if( sscanf(str, "%" SCIP_LONGINT_FORMAT, &capacity) != 1 )
13160 {
13161 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "error parsing capacity from '%s'\n", str);
13162 *success = FALSE;
13163 }
13164 else
13165 {
13166 SCIP_CALL( SCIPcreateConsKnapsack(scip, cons, name, nvars, vars, weights, capacity,
13167 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
13168 }
13169 }
13170
13171 SCIPfreeBufferArray(scip, &vars);
13172 SCIPfreeBufferArray(scip, &weights);
13173
13174 return SCIP_OKAY;
13175 }
13176
13177 /** constraint method of constraint handler which returns the variables (if possible) */
13178 static
SCIP_DECL_CONSGETVARS(consGetVarsKnapsack)13179 SCIP_DECL_CONSGETVARS(consGetVarsKnapsack)
13180 { /*lint --e{715}*/
13181 SCIP_CONSDATA* consdata;
13182
13183 consdata = SCIPconsGetData(cons);
13184 assert(consdata != NULL);
13185
13186 if( varssize < consdata->nvars )
13187 (*success) = FALSE;
13188 else
13189 {
13190 assert(vars != NULL);
13191
13192 BMScopyMemoryArray(vars, consdata->vars, consdata->nvars);
13193 (*success) = TRUE;
13194 }
13195
13196 return SCIP_OKAY;
13197 }
13198
13199 /** constraint method of constraint handler which returns the number of variables (if possible) */
13200 static
SCIP_DECL_CONSGETNVARS(consGetNVarsKnapsack)13201 SCIP_DECL_CONSGETNVARS(consGetNVarsKnapsack)
13202 { /*lint --e{715}*/
13203 SCIP_CONSDATA* consdata;
13204
13205 consdata = SCIPconsGetData(cons);
13206 assert(consdata != NULL);
13207
13208 (*nvars) = consdata->nvars;
13209 (*success) = TRUE;
13210
13211 return SCIP_OKAY;
13212 }
13213
13214 /*
13215 * Event handler
13216 */
13217
13218 /** execution method of bound change event handler */
13219 static
SCIP_DECL_EVENTEXEC(eventExecKnapsack)13220 SCIP_DECL_EVENTEXEC(eventExecKnapsack)
13221 { /*lint --e{715}*/
13222 SCIP_CONSDATA* consdata;
13223
13224 assert(eventdata != NULL);
13225 assert(eventdata->cons != NULL);
13226
13227 consdata = SCIPconsGetData(eventdata->cons);
13228 assert(consdata != NULL);
13229
13230 switch( SCIPeventGetType(event) )
13231 {
13232 case SCIP_EVENTTYPE_LBTIGHTENED:
13233 consdata->onesweightsum += eventdata->weight;
13234 consdata->presolvedtiming = 0;
13235 SCIP_CALL( SCIPmarkConsPropagate(scip, eventdata->cons) );
13236 break;
13237 case SCIP_EVENTTYPE_LBRELAXED:
13238 consdata->onesweightsum -= eventdata->weight;
13239 break;
13240 case SCIP_EVENTTYPE_UBTIGHTENED:
13241 consdata->presolvedtiming = 0;
13242 SCIP_CALL( SCIPmarkConsPropagate(scip, eventdata->cons) );
13243 break;
13244 case SCIP_EVENTTYPE_VARFIXED: /* the variable should be removed from the constraint in presolving */
13245 if( !consdata->existmultaggr )
13246 {
13247 SCIP_VAR* var;
13248 var = SCIPeventGetVar(event);
13249 assert(var != NULL);
13250
13251 /* if the variable was aggregated or multiaggregated, we must signal to propagation that we are no longer merged */
13252 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
13253 {
13254 consdata->existmultaggr = TRUE;
13255 consdata->merged = FALSE;
13256 }
13257 else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED ||
13258 (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED && SCIPvarGetStatus(SCIPvarGetNegatedVar(var)) == SCIP_VARSTATUS_AGGREGATED) )
13259 consdata->merged = FALSE;
13260 }
13261 /*lint -fallthrough*/
13262 case SCIP_EVENTTYPE_IMPLADDED: /* further preprocessing might be possible due to additional implications */
13263 consdata->presolvedtiming = 0;
13264 break;
13265 case SCIP_EVENTTYPE_VARDELETED:
13266 consdata->varsdeleted = TRUE;
13267 break;
13268 default:
13269 SCIPerrorMessage("invalid event type %lx\n", SCIPeventGetType(event));
13270 return SCIP_INVALIDDATA;
13271 }
13272
13273 return SCIP_OKAY;
13274 }
13275
13276
13277 /*
13278 * constraint specific interface methods
13279 */
13280
13281 /** creates the handler for knapsack constraints and includes it in SCIP */
SCIPincludeConshdlrKnapsack(SCIP * scip)13282 SCIP_RETCODE SCIPincludeConshdlrKnapsack(
13283 SCIP* scip /**< SCIP data structure */
13284 )
13285 {
13286 SCIP_EVENTHDLRDATA* eventhdlrdata;
13287 SCIP_CONSHDLRDATA* conshdlrdata;
13288 SCIP_CONSHDLR* conshdlr;
13289
13290 /* create knapsack constraint handler data */
13291 SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
13292
13293 /* include event handler for bound change events */
13294 eventhdlrdata = NULL;
13295 conshdlrdata->eventhdlr = NULL;
13296 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlr), EVENTHDLR_NAME, EVENTHDLR_DESC,
13297 eventExecKnapsack, eventhdlrdata) );
13298
13299 /* get event handler for bound change events */
13300 if( conshdlrdata->eventhdlr == NULL )
13301 {
13302 SCIPerrorMessage("event handler for knapsack constraints not found\n");
13303 return SCIP_PLUGINNOTFOUND;
13304 }
13305
13306 /* include constraint handler */
13307 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
13308 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
13309 consEnfolpKnapsack, consEnfopsKnapsack, consCheckKnapsack, consLockKnapsack,
13310 conshdlrdata) );
13311
13312 assert(conshdlr != NULL);
13313
13314 /* set non-fundamental callbacks via specific setter functions */
13315 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyKnapsack, consCopyKnapsack) );
13316 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteKnapsack) );
13317 SCIP_CALL( SCIPsetConshdlrDelvars(scip, conshdlr, consDelvarsKnapsack) );
13318 SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitKnapsack) );
13319 SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreKnapsack) );
13320 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolKnapsack) );
13321 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeKnapsack) );
13322 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsKnapsack) );
13323 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsKnapsack) );
13324 SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitKnapsack) );
13325 SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreKnapsack) );
13326 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpKnapsack) );
13327 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseKnapsack) );
13328 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolKnapsack,CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
13329 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintKnapsack) );
13330 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropKnapsack, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
13331 CONSHDLR_PROP_TIMING) );
13332 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropKnapsack) );
13333 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpKnapsack, consSepasolKnapsack, CONSHDLR_SEPAFREQ,
13334 CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
13335 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransKnapsack) );
13336 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxKnapsack) );
13337
13338 if( SCIPfindConshdlr(scip,"linear") != NULL )
13339 {
13340 /* include the linear constraint to knapsack constraint upgrade in the linear constraint handler */
13341 SCIP_CALL( SCIPincludeLinconsUpgrade(scip, linconsUpgdKnapsack, LINCONSUPGD_PRIORITY, CONSHDLR_NAME) );
13342 }
13343
13344 /* add knapsack constraint handler parameters */
13345 SCIP_CALL( SCIPaddIntParam(scip,
13346 "constraints/" CONSHDLR_NAME "/sepacardfreq",
13347 "multiplier on separation frequency, how often knapsack cuts are separated (-1: never, 0: only at root)",
13348 &conshdlrdata->sepacardfreq, TRUE, DEFAULT_SEPACARDFREQ, -1, SCIP_MAXTREEDEPTH, NULL, NULL) );
13349 SCIP_CALL( SCIPaddRealParam(scip,
13350 "constraints/" CONSHDLR_NAME "/maxcardbounddist",
13351 "maximal relative distance from current node's dual bound to primal bound compared to best node's dual bound for separating knapsack cuts",
13352 &conshdlrdata->maxcardbounddist, TRUE, DEFAULT_MAXCARDBOUNDDIST, 0.0, 1.0, NULL, NULL) );
13353 SCIP_CALL( SCIPaddRealParam(scip,
13354 "constraints/" CONSHDLR_NAME "/cliqueextractfactor",
13355 "lower clique size limit for greedy clique extraction algorithm (relative to largest clique)",
13356 &conshdlrdata->cliqueextractfactor, TRUE, DEFAULT_CLIQUEEXTRACTFACTOR, 0.0, 1.0, NULL, NULL) );
13357 SCIP_CALL( SCIPaddIntParam(scip,
13358 "constraints/" CONSHDLR_NAME "/maxrounds",
13359 "maximal number of separation rounds per node (-1: unlimited)",
13360 &conshdlrdata->maxrounds, FALSE, DEFAULT_MAXROUNDS, -1, INT_MAX, NULL, NULL) );
13361 SCIP_CALL( SCIPaddIntParam(scip,
13362 "constraints/" CONSHDLR_NAME "/maxroundsroot",
13363 "maximal number of separation rounds per node in the root node (-1: unlimited)",
13364 &conshdlrdata->maxroundsroot, FALSE, DEFAULT_MAXROUNDSROOT, -1, INT_MAX, NULL, NULL) );
13365 SCIP_CALL( SCIPaddIntParam(scip,
13366 "constraints/" CONSHDLR_NAME "/maxsepacuts",
13367 "maximal number of cuts separated per separation round",
13368 &conshdlrdata->maxsepacuts, FALSE, DEFAULT_MAXSEPACUTS, 0, INT_MAX, NULL, NULL) );
13369 SCIP_CALL( SCIPaddIntParam(scip,
13370 "constraints/" CONSHDLR_NAME "/maxsepacutsroot",
13371 "maximal number of cuts separated per separation round in the root node",
13372 &conshdlrdata->maxsepacutsroot, FALSE, DEFAULT_MAXSEPACUTSROOT, 0, INT_MAX, NULL, NULL) );
13373 SCIP_CALL( SCIPaddBoolParam(scip,
13374 "constraints/" CONSHDLR_NAME "/disaggregation",
13375 "should disaggregation of knapsack constraints be allowed in preprocessing?",
13376 &conshdlrdata->disaggregation, TRUE, DEFAULT_DISAGGREGATION, NULL, NULL) );
13377 SCIP_CALL( SCIPaddBoolParam(scip,
13378 "constraints/" CONSHDLR_NAME "/simplifyinequalities",
13379 "should presolving try to simplify knapsacks",
13380 &conshdlrdata->simplifyinequalities, TRUE, DEFAULT_SIMPLIFYINEQUALITIES, NULL, NULL) );
13381 SCIP_CALL( SCIPaddBoolParam(scip,
13382 "constraints/" CONSHDLR_NAME "/negatedclique",
13383 "should negated clique information be used in solving process",
13384 &conshdlrdata->negatedclique, TRUE, DEFAULT_NEGATEDCLIQUE, NULL, NULL) );
13385 SCIP_CALL( SCIPaddBoolParam(scip,
13386 "constraints/" CONSHDLR_NAME "/presolpairwise",
13387 "should pairwise constraint comparison be performed in presolving?",
13388 &conshdlrdata->presolpairwise, TRUE, DEFAULT_PRESOLPAIRWISE, NULL, NULL) );
13389 SCIP_CALL( SCIPaddBoolParam(scip,
13390 "constraints/" CONSHDLR_NAME "/presolusehashing",
13391 "should hash table be used for detecting redundant constraints in advance",
13392 &conshdlrdata->presolusehashing, TRUE, DEFAULT_PRESOLUSEHASHING, NULL, NULL) );
13393 SCIP_CALL( SCIPaddBoolParam(scip,
13394 "constraints/" CONSHDLR_NAME "/dualpresolving",
13395 "should dual presolving steps be performed?",
13396 &conshdlrdata->dualpresolving, TRUE, DEFAULT_DUALPRESOLVING, NULL, NULL) );
13397 SCIP_CALL( SCIPaddBoolParam(scip,
13398 "constraints/" CONSHDLR_NAME "/usegubs",
13399 "should GUB information be used for separation?",
13400 &conshdlrdata->usegubs, TRUE, DEFAULT_USEGUBS, NULL, NULL) );
13401 SCIP_CALL( SCIPaddBoolParam(scip,
13402 "constraints/" CONSHDLR_NAME "/detectcutoffbound",
13403 "should presolving try to detect constraints parallel to the objective function defining an upper bound and prevent these constraints from entering the LP?",
13404 &conshdlrdata->detectcutoffbound, TRUE, DEFAULT_DETECTCUTOFFBOUND, NULL, NULL) );
13405 SCIP_CALL( SCIPaddBoolParam(scip,
13406 "constraints/" CONSHDLR_NAME "/detectlowerbound",
13407 "should presolving try to detect constraints parallel to the objective function defining a lower bound and prevent these constraints from entering the LP?",
13408 &conshdlrdata->detectlowerbound, TRUE, DEFAULT_DETECTLOWERBOUND, NULL, NULL) );
13409 SCIP_CALL( SCIPaddBoolParam(scip,
13410 "constraints/" CONSHDLR_NAME "/updatecliquepartitions",
13411 "should clique partition information be updated when old partition seems outdated?",
13412 &conshdlrdata->updatecliquepartitions, TRUE, DEFAULT_UPDATECLIQUEPARTITIONS, NULL, NULL) );
13413 SCIP_CALL( SCIPaddRealParam(scip,
13414 "constraints/" CONSHDLR_NAME "/clqpartupdatefac",
13415 "factor on the growth of global cliques to decide when to update a previous "
13416 "(negated) clique partition (used only if updatecliquepartitions is set to TRUE)",
13417 &conshdlrdata->clqpartupdatefac, TRUE, DEFAULT_CLQPARTUPDATEFAC, 1.0, 10.0, NULL, NULL) );
13418 #ifdef WITH_CARDINALITY_UPGRADE
13419 SCIP_CALL( SCIPaddBoolParam(scip,
13420 "constraints/" CONSHDLR_NAME "/upgdcardinality",
13421 "if TRUE then try to update knapsack constraints to cardinality constraints",
13422 &conshdlrdata->upgdcardinality, TRUE, DEFAULT_UPGDCARDINALITY, NULL, NULL) );
13423 #endif
13424 return SCIP_OKAY;
13425 }
13426
13427 /** creates and captures a knapsack constraint
13428 *
13429 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
13430 */
13431 /**! [SnippetConsCreationKnapsack] */
SCIPcreateConsKnapsack(SCIP * scip,SCIP_CONS ** cons,const char * name,int nvars,SCIP_VAR ** vars,SCIP_Longint * weights,SCIP_Longint capacity,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable,SCIP_Bool stickingatnode)13432 SCIP_RETCODE SCIPcreateConsKnapsack(
13433 SCIP* scip, /**< SCIP data structure */
13434 SCIP_CONS** cons, /**< pointer to hold the created constraint */
13435 const char* name, /**< name of constraint */
13436 int nvars, /**< number of items in the knapsack */
13437 SCIP_VAR** vars, /**< array with item variables */
13438 SCIP_Longint* weights, /**< array with item weights */
13439 SCIP_Longint capacity, /**< capacity of knapsack (right hand side of inequality) */
13440 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
13441 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
13442 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
13443 * Usually set to TRUE. */
13444 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
13445 * TRUE for model constraints, FALSE for additional, redundant constraints. */
13446 SCIP_Bool check, /**< should the constraint be checked for feasibility?
13447 * TRUE for model constraints, FALSE for additional, redundant constraints. */
13448 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
13449 * Usually set to TRUE. */
13450 SCIP_Bool local, /**< is constraint only valid locally?
13451 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
13452 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
13453 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
13454 * adds coefficients to this constraint. */
13455 SCIP_Bool dynamic, /**< is constraint subject to aging?
13456 * Usually set to FALSE. Set to TRUE for own cuts which
13457 * are separated as constraints. */
13458 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
13459 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
13460 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
13461 * if it may be moved to a more global node?
13462 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
13463 )
13464 {
13465 SCIP_CONSHDLRDATA* conshdlrdata;
13466 SCIP_CONSHDLR* conshdlr;
13467 SCIP_CONSDATA* consdata;
13468
13469 /* find the knapsack constraint handler */
13470 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
13471 if( conshdlr == NULL )
13472 {
13473 SCIPerrorMessage("knapsack constraint handler not found\n");
13474 return SCIP_PLUGINNOTFOUND;
13475 }
13476
13477 /* get event handler */
13478 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13479 assert(conshdlrdata != NULL);
13480 assert(conshdlrdata->eventhdlr != NULL);
13481
13482 /* create constraint data */
13483 SCIP_CALL( consdataCreate(scip, &consdata, nvars, vars, weights, capacity) );
13484
13485 /* create constraint */
13486 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
13487 local, modifiable, dynamic, removable, stickingatnode) );
13488
13489 /* catch events for variables */
13490 if( SCIPisTransformed(scip) )
13491 {
13492 SCIP_CALL( catchEvents(scip, *cons, consdata, conshdlrdata->eventhdlr) );
13493 }
13494
13495 return SCIP_OKAY;
13496 }
13497 /**! [SnippetConsCreationKnapsack] */
13498
13499 /** creates and captures a knapsack constraint
13500 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
13501 * method SCIPcreateConsKnapsack(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
13502 *
13503 * @see SCIPcreateConsKnapsack() for information about the basic constraint flag configuration
13504 *
13505 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
13506 */
SCIPcreateConsBasicKnapsack(SCIP * scip,SCIP_CONS ** cons,const char * name,int nvars,SCIP_VAR ** vars,SCIP_Longint * weights,SCIP_Longint capacity)13507 SCIP_RETCODE SCIPcreateConsBasicKnapsack(
13508 SCIP* scip, /**< SCIP data structure */
13509 SCIP_CONS** cons, /**< pointer to hold the created constraint */
13510 const char* name, /**< name of constraint */
13511 int nvars, /**< number of items in the knapsack */
13512 SCIP_VAR** vars, /**< array with item variables */
13513 SCIP_Longint* weights, /**< array with item weights */
13514 SCIP_Longint capacity /**< capacity of knapsack */
13515 )
13516 {
13517 assert(scip != NULL);
13518
13519 SCIP_CALL( SCIPcreateConsKnapsack(scip, cons, name, nvars, vars, weights, capacity,
13520 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
13521
13522 return SCIP_OKAY;
13523 }
13524
13525 /** adds new item to knapsack constraint */
SCIPaddCoefKnapsack(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Longint weight)13526 SCIP_RETCODE SCIPaddCoefKnapsack(
13527 SCIP* scip, /**< SCIP data structure */
13528 SCIP_CONS* cons, /**< constraint data */
13529 SCIP_VAR* var, /**< item variable */
13530 SCIP_Longint weight /**< item weight */
13531 )
13532 {
13533 assert(var != NULL);
13534 assert(scip != NULL);
13535
13536 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13537 {
13538 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13539 return SCIP_INVALIDDATA;
13540 }
13541
13542 SCIP_CALL( addCoef(scip, cons, var, weight) );
13543
13544 return SCIP_OKAY;
13545 }
13546
13547 /** gets the capacity of the knapsack constraint */
SCIPgetCapacityKnapsack(SCIP * scip,SCIP_CONS * cons)13548 SCIP_Longint SCIPgetCapacityKnapsack(
13549 SCIP* scip, /**< SCIP data structure */
13550 SCIP_CONS* cons /**< constraint data */
13551 )
13552 {
13553 SCIP_CONSDATA* consdata;
13554
13555 assert(scip != NULL);
13556
13557 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13558 {
13559 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13560 SCIPABORT();
13561 return 0; /*lint !e527*/
13562 }
13563
13564 consdata = SCIPconsGetData(cons);
13565 assert(consdata != NULL);
13566
13567 return consdata->capacity;
13568 }
13569
13570 /** changes capacity of the knapsack constraint
13571 *
13572 * @note This method can only be called during problem creation stage (SCIP_STAGE_PROBLEM)
13573 */
SCIPchgCapacityKnapsack(SCIP * scip,SCIP_CONS * cons,SCIP_Longint capacity)13574 SCIP_RETCODE SCIPchgCapacityKnapsack(
13575 SCIP* scip, /**< SCIP data structure */
13576 SCIP_CONS* cons, /**< constraint data */
13577 SCIP_Longint capacity /**< new capacity of knapsack */
13578 )
13579 {
13580 SCIP_CONSDATA* consdata;
13581
13582 assert(scip != NULL);
13583
13584 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13585 {
13586 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13587 return SCIP_INVALIDDATA;
13588 }
13589
13590 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13591 {
13592 SCIPerrorMessage("method can only be called during problem creation stage\n");
13593 return SCIP_INVALIDDATA;
13594 }
13595
13596 consdata = SCIPconsGetData(cons);
13597 assert(consdata != NULL);
13598
13599 consdata->capacity = capacity;
13600
13601 return SCIP_OKAY;
13602 }
13603
13604 /** gets the number of items in the knapsack constraint */
SCIPgetNVarsKnapsack(SCIP * scip,SCIP_CONS * cons)13605 int SCIPgetNVarsKnapsack(
13606 SCIP* scip, /**< SCIP data structure */
13607 SCIP_CONS* cons /**< constraint data */
13608 )
13609 {
13610 SCIP_CONSDATA* consdata;
13611
13612 assert(scip != NULL);
13613
13614 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13615 {
13616 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13617 SCIPABORT();
13618 return -1; /*lint !e527*/
13619 }
13620
13621 consdata = SCIPconsGetData(cons);
13622 assert(consdata != NULL);
13623
13624 return consdata->nvars;
13625 }
13626
13627 /** gets the array of variables in the knapsack constraint; the user must not modify this array! */
SCIPgetVarsKnapsack(SCIP * scip,SCIP_CONS * cons)13628 SCIP_VAR** SCIPgetVarsKnapsack(
13629 SCIP* scip, /**< SCIP data structure */
13630 SCIP_CONS* cons /**< constraint data */
13631 )
13632 {
13633 SCIP_CONSDATA* consdata;
13634
13635 assert(scip != NULL);
13636
13637 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13638 {
13639 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13640 SCIPABORT();
13641 return NULL; /*lint !e527*/
13642 }
13643
13644 consdata = SCIPconsGetData(cons);
13645 assert(consdata != NULL);
13646
13647 return consdata->vars;
13648 }
13649
13650 /** gets the array of weights in the knapsack constraint; the user must not modify this array! */
SCIPgetWeightsKnapsack(SCIP * scip,SCIP_CONS * cons)13651 SCIP_Longint* SCIPgetWeightsKnapsack(
13652 SCIP* scip, /**< SCIP data structure */
13653 SCIP_CONS* cons /**< constraint data */
13654 )
13655 {
13656 SCIP_CONSDATA* consdata;
13657
13658 assert(scip != NULL);
13659
13660 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13661 {
13662 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13663 SCIPABORT();
13664 return NULL; /*lint !e527*/
13665 }
13666
13667 consdata = SCIPconsGetData(cons);
13668 assert(consdata != NULL);
13669
13670 return consdata->weights;
13671 }
13672
13673 /** gets the dual solution of the knapsack constraint in the current LP */
SCIPgetDualsolKnapsack(SCIP * scip,SCIP_CONS * cons)13674 SCIP_Real SCIPgetDualsolKnapsack(
13675 SCIP* scip, /**< SCIP data structure */
13676 SCIP_CONS* cons /**< constraint data */
13677 )
13678 {
13679 SCIP_CONSDATA* consdata;
13680
13681 assert(scip != NULL);
13682
13683 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13684 {
13685 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13686 SCIPABORT();
13687 return SCIP_INVALID; /*lint !e527*/
13688 }
13689
13690 consdata = SCIPconsGetData(cons);
13691 assert(consdata != NULL);
13692
13693 if( consdata->row != NULL )
13694 return SCIProwGetDualsol(consdata->row);
13695 else
13696 return 0.0;
13697 }
13698
13699 /** gets the dual Farkas value of the knapsack constraint in the current infeasible LP */
SCIPgetDualfarkasKnapsack(SCIP * scip,SCIP_CONS * cons)13700 SCIP_Real SCIPgetDualfarkasKnapsack(
13701 SCIP* scip, /**< SCIP data structure */
13702 SCIP_CONS* cons /**< constraint data */
13703 )
13704 {
13705 SCIP_CONSDATA* consdata;
13706
13707 assert(scip != NULL);
13708
13709 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13710 {
13711 SCIPerrorMessage("constraint is not a knapsack constraint\n");
13712 SCIPABORT();
13713 return SCIP_INVALID; /*lint !e527*/
13714 }
13715
13716 consdata = SCIPconsGetData(cons);
13717 assert(consdata != NULL);
13718
13719 if( consdata->row != NULL )
13720 return SCIProwGetDualfarkas(consdata->row);
13721 else
13722 return 0.0;
13723 }
13724
13725 /** returns the linear relaxation of the given knapsack constraint; may return NULL if no LP row was yet created;
13726 * the user must not modify the row!
13727 */
SCIPgetRowKnapsack(SCIP * scip,SCIP_CONS * cons)13728 SCIP_ROW* SCIPgetRowKnapsack(
13729 SCIP* scip, /**< SCIP data structure */
13730 SCIP_CONS* cons /**< constraint data */
13731 )
13732 {
13733 SCIP_CONSDATA* consdata;
13734
13735 assert(scip != NULL);
13736
13737 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13738 {
13739 SCIPerrorMessage("constraint is not a knapsack\n");
13740 SCIPABORT();
13741 return NULL; /*lint !e527*/
13742 }
13743
13744 consdata = SCIPconsGetData(cons);
13745 assert(consdata != NULL);
13746
13747 return consdata->row;
13748 }
13749
13750 /** cleans up (multi-)aggregations and fixings from knapsack constraints */
SCIPcleanupConssKnapsack(SCIP * scip,SCIP_Bool onlychecked,SCIP_Bool * infeasible)13751 SCIP_RETCODE SCIPcleanupConssKnapsack(
13752 SCIP* scip, /**< SCIP data structure */
13753 SCIP_Bool onlychecked, /**< should only checked constraints be cleaned up? */
13754 SCIP_Bool* infeasible /**< pointer to return whether the problem was detected to be infeasible */
13755 )
13756 {
13757 SCIP_CONSHDLR* conshdlr;
13758 SCIP_CONS** conss;
13759 int nconss;
13760 int i;
13761
13762 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
13763 if( conshdlr == NULL )
13764 return SCIP_OKAY;
13765
13766 assert(infeasible != NULL);
13767 *infeasible = FALSE;
13768
13769 nconss = onlychecked ? SCIPconshdlrGetNCheckConss(conshdlr) : SCIPconshdlrGetNActiveConss(conshdlr);
13770 conss = onlychecked ? SCIPconshdlrGetCheckConss(conshdlr) : SCIPconshdlrGetConss(conshdlr);
13771
13772 for( i = 0; i < nconss; ++i )
13773 {
13774 SCIP_CALL( applyFixings(scip, conss[i], infeasible) );
13775
13776 if( *infeasible )
13777 break;
13778 }
13779
13780 return SCIP_OKAY;
13781 }
13782