1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 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 sepa_mixing.c
17 * @ingroup DEFPLUGINS_SEPA
18 * @brief mixing/star inequality separator
19 * @author Weikun Chen
20 * @author Marc Pfetsch
21 *
22 * This separator generates cuts based on the mixing set
23 * \f[
24 * X = \{ (x,y) \in \{0,1\}^{N \cup M} \times \mathbb{R} \, : \, y \geq a_i x_i, \, \textrm{for} \, i \in N, \,
25 * y \leq u - a_i x_i, \, \textrm{for} \, i \in M, \, 0 \leq y \leq u \},
26 * \f]
27 * where \f$0 \leq a_i \leq u\f$ for all \f$i\f$. This information can be obtained directly from the variable bounds data
28 * structure. The separator will generate three classes of cuts.
29 *
30 * VLB: Let \f$T\f$ be a subset of \f$N\f$, wlog, \f$T = \{1,\ldots,r\}\f$ with \f$a_1 \leq a_2 \leq \ldots \leq a_r\f$.
31 * Let \f$a_0 = 0\f$. The mixing/star VLB cut is of the form \f$ y \geq \sum_{i=1}^r (a_i - a_{i-1})x_i \f$.
32 *
33 * VUB: Let \f$T\f$ be a subset of \f$M\f$, wlog, \f$T = \{1,\ldots,r\}\f$ with \f$a_1 \leq a_2 \leq \ldots \leq a_r\f$.
34 * Let \f$a_0 = 0\f$. The mixing/star VUB cut is of the form \f$ y \leq u - \sum_{i=1}^r (a_i - a_{i-1})x_i \f$.
35 *
36 * CONFLICT: Consider \f$i \in N\f$ and \f$j \in M\f$ with \f$a_i + a_j > u\f$. The conflict cut is
37 * \f$x_i + x_j \leq 1\f$.
38 *
39 * A small example is described in the following to see the generated cuts.
40 * \f[
41 * Y = \{ (x,y) \in \{0,1\}^{4} \times \mathbb{R} \, : \, y \geq 2x_1, \, y \geq 3x_2, \, y \leq 4 - x_3, \,
42 * y \leq 4 - 2 x_4, \, 0 \leq y \leq 4 \}.
43 * \f]
44 * In this small example, the mixing/star cuts \f$y \geq 2x_1 + x_2\f$ (VLB) and \f$y \leq 4 - x_3 - x_4\f$ (VUB) will be
45 * considered to be generated. Besides the mixing cuts, we also consider the conflict cut \f$x_1 + x_3 \leq 1\f$ (CONFLICT).
46 *
47 *
48 * For an overview see:
49 * Atamturk, A., Nemhauser, G.L. and Savelsbergh, M.W.,@n
50 * The mixed vertex packing problem.@n
51 * Mathematical Programming, 89(1), 35-53, 2000.
52 *
53 * Some remarks:
54 * - Besides the mixing inequality, we also add the conflict inequality.
55 * - Currently, the performance is bad on the neos-565672 instance.
56 * The reason is that, after adding the separator, SCIP spends a lot of time at the stage of cutting plane generation.
57 * - We do not consider sparsity of the cuts as we aim to find a most violated cut.
58 * - Besides the most violated cut we consider, we also add an additional variable to make the cut be the strongest one,
59 * even the additional variable does not contribute any to the violation.
60 *
61 */
62
63 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
64
65 #include "blockmemshell/memory.h"
66 #include "scip/pub_implics.h"
67 #include "scip/pub_lp.h"
68 #include "scip/pub_message.h"
69 #include "scip/pub_misc.h"
70 #include "scip/pub_sepa.h"
71 #include "scip/pub_var.h"
72 #include "scip/scip_branch.h"
73 #include "scip/scip_cut.h"
74 #include "scip/scip_lp.h"
75 #include "scip/scip_mem.h"
76 #include "scip/scip_message.h"
77 #include "scip/scip_numerics.h"
78 #include "scip/scip_param.h"
79 #include "scip/scip_prob.h"
80 #include "scip/scip_sepa.h"
81 #include "scip/scip_sol.h"
82 #include "scip/scip_solvingstats.h"
83 #include "scip/scip_var.h"
84 #include "scip/sepa_mixing.h"
85 #include "scip/scip_tree.h"
86 #include "scip/sepa_mixing.h"
87 #include <string.h>
88
89
90 #define SEPA_NAME "mixing"
91 #define SEPA_DESC "mixing inequality separator"
92 #define DEFAULT_MAXROUNDS -1 /**< maximal number of mixing separation rounds per node (-1: unlimited) */
93 #define DEFAULT_MAXROUNDSROOT -1 /**< maximal number of mixing separation rounds in the root node (-1: unlimited) */
94 #define SEPA_PRIORITY -50
95 #define SEPA_FREQ 10
96 #define SEPA_MAXBOUNDDIST 1.0
97 #define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
98 #define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
99
100 #define DEFAULT_USELOCALBOUNDS FALSE /**< should local bounds be used? */
101 #define DEFAULT_ISCUTSONINTS FALSE /**< should general/implied integer variables be used to generate cuts? */
102 #define DEFAULT_MAXNUNSUCCESSFUL 10 /**< maximal number of consecutive unsuccessful iterations */
103
104 /** separator-specific data for the mixing separator */
105 struct SCIP_SepaData
106 {
107 SCIP_Bool uselocalbounds; /**< should local bounds be used? */
108 SCIP_Bool iscutsonints; /**< should general/implied integer variables be used to generate cuts? */
109 int maxrounds; /**< maximal number of mixing separation rounds per node (-1: unlimited) */
110 int maxroundsroot; /**< maximal number of mixing separation rounds in the root node (-1: unlimited) */
111 int nunsuccessful; /**< number of consecutive unsuccessful iterations */
112 int maxnunsuccessful; /**< maximal number of consecutive unsuccessful iterations */
113 };
114
115 /*
116 * local methods
117 */
118
119 /** adds the given cut */
120 static
121 SCIP_RETCODE addCut(
122 SCIP* scip, /**< SCIP data structure */
123 SCIP_SEPA* sepa, /**< separator */
124 SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
125 SCIP_Real* cutcoefs, /**< coefficients of active variables in cut */
126 int* cutinds, /**< problem indices of variables in cut */
127 int cutnnz, /**< number of non-zeros in cut */
128 SCIP_Real cutrhs, /**< right hand side of cut */
129 SCIP_Bool cutislocal, /**< Is the cut only locally valid? */
130 SCIP_Bool* cutoff, /**< pointer to store whether a cutoff has been detected */
131 int* ncuts /**< pointer to update number of cuts added */
132 )
133 {
134 char cutname[SCIP_MAXSTRLEN];
135 SCIP_VAR** vars;
136 SCIP_ROW* cut;
137 int v;
138
139 assert(cutcoefs != NULL);
140 assert(cutinds != NULL);
141 assert(ncuts != NULL);
142 assert(cutoff != NULL);
143
144 *cutoff = FALSE;
145
146 /* get active problem variables */
147 vars = SCIPgetVars(scip);
148
149 /* construct cut name */
(1) Event invalid_type: |
Argument "SCIPgetNLPs(scip)" to format specifier "%d" was expected to have type "int" but has type "long long". [details] |
150 (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "mix%d_x%d", SCIPgetNLPs(scip), *ncuts);
151
152 /* create empty cut */
153 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs,
154 cutislocal, FALSE, TRUE) );
155
156 /* cache the row extension and only flush them if the cut gets added */
157 SCIP_CALL( SCIPcacheRowExtensions(scip, cut) );
158
159 /* collect all non-zero coefficients */
160 for( v = 0; v < cutnnz; ++v )
161 {
162 SCIP_CALL( SCIPaddVarToRow(scip, cut, vars[cutinds[v]], cutcoefs[v]) );
163 }
164
165 /* flush all changes before adding the cut */
166 SCIP_CALL( SCIPflushRowExtensions(scip, cut) );
167
168 if( SCIPisCutEfficacious(scip, sol, cut) )
169 {
170 /* set cut rank */
171 SCIProwChgRank(cut, 1);
172
173 #ifdef SCIP_DEBUG
174 SCIPdebugMsg(scip, "-> found cut (eff: %f): ", SCIPgetCutEfficacy(scip, sol, cut));
175 SCIP_CALL( SCIPprintRow(scip, cut, NULL) );
176 #endif
177
178 if( cutislocal )
179 {
180 /* local cuts are added to the sepastore */
181 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, cutoff) );
182 }
183 else
184 {
185 SCIP_CALL( SCIPaddPoolCut(scip, cut) );
186 }
187 (*ncuts)++;
188 }
189
190 /* release the row */
191 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
192
193 return SCIP_OKAY;
194 }
195
196 /** searches and adds mixing cuts that are violated by the given solution value array
197 *
198 * This function implements a separation heuristic that runs in linear time in comparison to the quadratic exact
199 * algorithm in Atamturk et al.:
200 * - Lower and upper bounds are considered separately. Then possible conflict cuts.
201 * - Variable lower/upper bounds data are collected, i.e., the corresponding binary variables and coefficients.
202 * - These lists are sorted non-increasingly according to the solution values.
203 * - Then binary variables are added in turn as long as their coefficients increase in order to make the coefficients
204 * nonnegative. This clearly makes the cuts heuristic, since it is order dependent, but also sparser.
205 * - The procedure stops as soon as it reaches 0 solution values.
206 * - If the cut is efficious it is added.
207 *
208 * @todo Check whether one wants to sort according to the quotient of solution values and coefficients to increase the
209 * chance of having smaller coefficients in the front.
210 */
211 static
212 SCIP_RETCODE separateCuts(
213 SCIP* scip, /**< SCIP data structure */
214 SCIP_SEPA* sepa, /**< separator */
215 SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
216 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
217 int* ncuts /**< pointer to store the number of generated cuts */
218 )
219 {
220 SCIP_SEPADATA* sepadata;
221 SCIP_VAR* var;
222 SCIP_VAR** vars;
223 SCIP_Real* vlbmixcoefs;
224 SCIP_Real* vlbmixsols;
225 SCIP_Real* vubmixcoefs;
226 SCIP_Real* vubmixsols;
227 SCIP_Real* cutcoefs;
228 SCIP_Real cutrhs;
229 int* vlbmixinds;
230 int* vubmixinds;
231 int* cutinds;
232 int* vlbmixsigns;
233 int* vubmixsigns;
234 int firstvar;
235 int nmaxvars;
236 int nvars;
237 int i;
238 int k;
239
240 assert(sepa != NULL);
241 assert(cutoff != NULL);
242 assert(ncuts != NULL);
243
244 *cutoff = FALSE;
245 *ncuts = 0;
246
247 /* exit if there are no binary variables - ignore integer variables that have 0/1 bounds */
248 nmaxvars = SCIPgetNBinVars(scip);
249 if( nmaxvars <= 0 )
250 return SCIP_OKAY;
251
252 sepadata = SCIPsepaGetData(sepa);
253 assert(sepadata != NULL);
254
255 /* get the index of the first considered variable */
256 if( sepadata->iscutsonints )
257 {
258 /* generate cuts based on all nonbinary variabels */
259 firstvar = SCIPgetNBinVars(scip);
260 }
261 else
262 {
263 /* only generate cuts based on continuous variables */
264 firstvar = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip);
265 }
266 nvars = SCIPgetNVars(scip);
267 if( firstvar == nvars )
268 return SCIP_OKAY;
269
270 vars = SCIPgetVars(scip);
271
272 /* allocate temporary memory */
273 SCIP_CALL( SCIPallocBufferArray(scip, &vlbmixcoefs, nmaxvars) );
274 SCIP_CALL( SCIPallocBufferArray(scip, &vlbmixsols, nmaxvars) );
275 SCIP_CALL( SCIPallocBufferArray(scip, &vlbmixinds, nmaxvars) );
276 SCIP_CALL( SCIPallocBufferArray(scip, &vlbmixsigns, nmaxvars) );
277 SCIP_CALL( SCIPallocBufferArray(scip, &vubmixcoefs, nmaxvars) );
278 SCIP_CALL( SCIPallocBufferArray(scip, &vubmixsols, nmaxvars) );
279 SCIP_CALL( SCIPallocBufferArray(scip, &vubmixinds, nmaxvars) );
280 SCIP_CALL( SCIPallocBufferArray(scip, &vubmixsigns, nmaxvars) );
281 SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nmaxvars + 1) );
282 SCIP_CALL( SCIPallocBufferArray(scip, &cutinds, nmaxvars + 1) );
283
284 for( i = firstvar; i < nvars; i++ )
285 {
286 SCIP_VAR** vlbvars;
287 SCIP_Real* vlbcoefs;
288 SCIP_Real* vlbconsts;
289 SCIP_VAR** vubvars;
290 SCIP_Real* vubcoefs;
291 SCIP_Real* vubconsts;
292 SCIP_Real maxabscoef;
293 SCIP_Real activity;
294 SCIP_Real lastcoef;
295 SCIP_Real varsolval;
296 SCIP_Real lb = SCIP_INVALID;
297 SCIP_Real ub = SCIP_INVALID;
298 SCIP_Bool islocallb = FALSE; /* Is it a local lower bound or global lower bound? */
299 SCIP_Bool islocalub = FALSE; /* Is it a local upper bound or global upper bound? */
300 SCIP_Bool cutislocal; /* Is it a local cut or global cut? */
301 int vlbmixsize = 0;
302 int vubmixsize = 0;
303 int cutnnz = 0;
304 int maxabsind;
305 int maxabssign;
306 int nvlb;
307 int nvub;
308 int j;
309
310 var = vars[i];
311 assert( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY );
312
313 if( SCIPvarGetProbindex(var) < 0 )
314 continue;
315
316 nvlb = SCIPvarGetNVlbs(var);
317 nvub = SCIPvarGetNVubs(var);
318
319 if( nvlb == 0 && nvub == 0 )
320 continue;
321
322 /* skip lower bound if the LP solution value is equal to the upper bound of the continuous variable */
323 varsolval = SCIPgetSolVal(scip, sol, var);
324 if( SCIPisFeasEQ(scip, SCIPvarGetUbLocal(var), varsolval) )
325 goto VUB;
326
327 if( nvlb == 0 )
328 goto VUB;
329
330 /* get variable lower variable bounds information */
331 vlbvars = SCIPvarGetVlbVars(var);
332 vlbcoefs = SCIPvarGetVlbCoefs(var);
333 vlbconsts = SCIPvarGetVlbConstants(var);
334
335 maxabscoef = 0.0;
336 maxabsind = -1;
337 maxabssign = 0;
338
339 lb = SCIPvarGetLbGlobal(var);
340 if( sepadata->uselocalbounds && SCIPisLT(scip, lb, SCIPvarGetLbLocal(var)) )
341 {
342 /* this is a lcoal cut */
343 islocallb = TRUE;
344 lb = SCIPvarGetLbLocal(var);
345 }
346
347 #ifdef SCIP_DEBUG
348 for( j = 0; j < nvlb; j++ )
349 {
350 SCIP_Real tmplb;
351
352 if( SCIPvarIsBinary(vlbvars[j]) && SCIPvarGetProbindex(vlbvars[j]) >= 0 )
353 {
354 tmplb = (vlbcoefs[j] > 0) ? vlbconsts[j] : (vlbconsts[j] + vlbcoefs[j]);
355 assert( SCIPisFeasLE(scip, tmplb, lb) );
356 }
357 }
358 #endif
359
360 assert( SCIPisFeasLE(scip, lb, SCIPvarGetUbLocal(var)) );
361
362 /* extract the useful variable bounds information (binary and nonredundant) */
363 for( j = 0; j < nvlb; j++ )
364 {
365 /* consider only active and binary variables */
366 if( SCIPvarIsBinary(vlbvars[j]) && SCIPvarGetProbindex(vlbvars[j]) >= 0 )
367 {
368 SCIP_Real maxactivity;
369 SCIP_Real coef;
370
371 maxactivity = (vlbcoefs[j] > 0) ? (vlbconsts[j] + vlbcoefs[j]) : vlbconsts[j];
372
373 /* skip redundant variable bound constraints */
374 if( SCIPisFeasLE(scip, maxactivity, lb) )
375 continue;
376
377 if( vlbcoefs[j] > 0 )
378 {
379 coef = maxactivity - lb;
380 vlbmixsigns[vlbmixsize] = 0;
381 }
382 else
383 {
384 coef = lb - maxactivity;
385 vlbmixsigns[vlbmixsize] = 1;
386 }
387 assert(vlbmixsize <= nvars);
388
389 vlbmixcoefs[vlbmixsize] = REALABS(coef);
390 vlbmixinds[vlbmixsize] = SCIPvarGetProbindex(vlbvars[j]);
391 vlbmixsols[vlbmixsize] = (! vlbmixsigns[vlbmixsize]) ? SCIPgetSolVal(scip, sol, vlbvars[j]) : (1.0 - SCIPgetSolVal(scip, sol, vlbvars[j]));
392
393 /* update the maximal coefficient if needed */
394 if( maxabscoef < vlbmixcoefs[vlbmixsize] )
395 {
396 maxabscoef = vlbmixcoefs[vlbmixsize];
397 maxabsind = vlbmixinds[vlbmixsize];
398 maxabssign = vlbmixsigns[vlbmixsize];
399 }
400
401 ++vlbmixsize;
402
403 /* stop if size is exceeded; possibly ignore redundant variable bounds */
404 if( vlbmixsize >= nmaxvars )
405 break;
406 }
407 }
408 assert( vlbmixsize <= nmaxvars );
409
410 /* stop if no variable lower bounds information exists */
411 if( vlbmixsize == 0 )
412 goto VUB;
413
414 /* stop if the current solution value of the transformed continuous variable is larger than the maximal coefficient */
415 if( SCIPisFeasGT(scip, varsolval - lb, maxabscoef) )
416 goto VUB;
417
418 /* sort the solution values in non-increasing order */
419 SCIPsortDownRealRealIntInt(vlbmixsols, vlbmixcoefs, vlbmixinds, vlbmixsigns, vlbmixsize);
420
421 /* add the continuous variable */
422 cutcoefs[cutnnz] = -1;
423 cutinds[cutnnz] = SCIPvarGetProbindex(var);
424 cutrhs = -lb;
425 cutnnz++;
426
427 activity = -(varsolval - lb);
428 lastcoef = 0.0;
429
430 /* loop over the variables and add the variable to the cut if its coefficient is larger than that of the last variable */
431 for( j = 0; j < vlbmixsize; j++ )
432 {
433 SCIP_Real solval;
434
435 solval = vlbmixsols[j];
436
437 /* stop if we can not find a violated cut or we reached 0 solution values */
438 if( activity + solval * (maxabscoef - lastcoef) < 0.0 || SCIPisFeasZero(scip, solval) )
439 break;
440 else
441 {
442 /* skip if we have already added a variable with bigger coefficient */
443 if( SCIPisLE(scip, vlbmixcoefs[j], lastcoef) )
444 continue;
445 else
446 {
447 activity += (vlbmixcoefs[j] - lastcoef) * solval;
448 if( vlbmixsigns[j] )
449 {
450 cutcoefs[cutnnz] = lastcoef - vlbmixcoefs[j];
451 cutrhs -= vlbmixcoefs[j] - lastcoef;
452 }
453 else
454 cutcoefs[cutnnz] = vlbmixcoefs[j] - lastcoef;
455 cutinds[cutnnz++] = vlbmixinds[j];
456 lastcoef = vlbmixcoefs[j];
457 }
458 }
459 }
460
461 /* add the variable with maximal coefficient to make sure the cut is strong enough */
462 if( SCIPisGT(scip, maxabscoef, lastcoef) )
463 {
464 /* do not update activity: either the corresponding solval is 0.0 or the cut will not become efficious */
465 if( maxabssign )
466 {
467 cutcoefs[cutnnz] = lastcoef - maxabscoef;
468 cutrhs -= maxabscoef - lastcoef;
469 }
470 else
471 cutcoefs[cutnnz] = maxabscoef - lastcoef;
472 cutinds[cutnnz++] = maxabsind;
473 }
474 assert( cutnnz <= nmaxvars + 1 );
475
476 /* add the cut if the violtion is good enough and the number of nonzero coefficients is larger than 2 */
477 if( SCIPisEfficacious(scip, activity) && cutnnz > 2 )
478 {
479 SCIP_CALL( addCut(scip, sepa, sol, cutcoefs, cutinds, cutnnz, cutrhs, islocallb, cutoff, ncuts) );
480 }
481
482 VUB:
483 if( nvub == 0 )
484 goto CONFLICT;
485
486 /* get variable upper bounds information */
487 vubvars = SCIPvarGetVubVars(var);
488 vubcoefs = SCIPvarGetVubCoefs(var);
489 vubconsts = SCIPvarGetVubConstants(var);
490
491 maxabscoef = 0.0;
492 maxabsind = -1;
493 maxabssign = 0;
494
495 /* stop if the lower bound is equal to the solution value of the continuous variable */
496 if( SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), varsolval) )
497 goto CONFLICT;
498
499 ub = SCIPvarGetUbGlobal(var);
500 if( sepadata->uselocalbounds && SCIPisGT(scip, ub, SCIPvarGetUbLocal(var)) )
501 {
502 /* this is a lcoal cut */
503 islocalub = TRUE;
504 ub = SCIPvarGetUbLocal(var);
505 }
506
507 #ifdef SCIP_DEBUG
508 for( j = 0; j < nvub; j++ )
509 {
510 SCIP_Real tmpub;
511
512 if( SCIPvarIsBinary(vubvars[j]) && SCIPvarGetProbindex(vubvars[j]) >= 0 )
513 {
514 tmpub = (vubcoefs[j] < 0) ? vubconsts[j] : (vubconsts[j] + vubcoefs[j]);
515 assert( SCIPisFeasGE(scip, tmpub, ub) );
516 }
517 }
518 #endif
519
520 assert( SCIPisFeasLE(scip, SCIPvarGetLbLocal(var), ub) );
521
522 /* extract the useful variable bounds information (binary and nonredundant) */
523 for( j = 0; j < nvub; j++ )
524 {
525 /* consider only active and binary variables */
526 if( SCIPvarIsBinary(vubvars[j]) && SCIPvarGetProbindex(vubvars[j]) >= 0 )
527 {
528 SCIP_Real minactivity;
529 SCIP_Real coef;
530
531 minactivity = (vubcoefs[j] < 0) ? (vubconsts[j] + vubcoefs[j]) : vubconsts[j];
532
533 /* skip redundant variable bound constraints */
534 if( SCIPisFeasLE(scip, ub, minactivity) )
535 continue;
536
537 if( vubcoefs[j] > 0 )
538 {
539 coef = ub - minactivity;
540 vubmixsigns[vubmixsize] = 1;
541 }
542 else
543 {
544 coef = minactivity - ub;
545 vubmixsigns[vubmixsize] = 0;
546 }
547
548 vubmixcoefs[vubmixsize] = REALABS(coef);
549 vubmixinds[vubmixsize] = SCIPvarGetProbindex(vubvars[j]);
550 vubmixsols[vubmixsize] = (! vubmixsigns[vubmixsize]) ? SCIPgetSolVal(scip, sol, vubvars[j]): (1.0 - SCIPgetSolVal(scip, sol, vubvars[j]));
551
552 /* update the maximal coefficient if needed */
553 if( maxabscoef < vubmixcoefs[vubmixsize] )
554 {
555 maxabscoef = vubmixcoefs[vubmixsize];
556 maxabsind = vubmixinds[vubmixsize];
557 maxabssign = vubmixsigns[vubmixsize];
558 }
559
560 ++vubmixsize;
561
562 /* stop if size is exceeded; possibly ignore redundant variable bounds */
563 if( vubmixsize >= nmaxvars )
564 break;
565 }
566 }
567 assert( vubmixsize <= nmaxvars );
568
569 /* stop if no variable upper bounds information exists */
570 if( vubmixsize == 0 )
571 goto CONFLICT;
572
573 /* stop if the current solution value of transformed continuous variable is larger than the maximal coefficient */
574 if( SCIPisFeasGT(scip, ub - varsolval, maxabscoef) )
575 goto CONFLICT;
576
577 /* sort the solution values in non-increasing order */
578 SCIPsortDownRealRealIntInt(vubmixsols, vubmixcoefs, vubmixinds, vubmixsigns, vubmixsize);
579
580 /* add the continuous variables */
581 cutnnz = 0;
582 cutcoefs[cutnnz] = 1;
583 cutinds[cutnnz] = SCIPvarGetProbindex(var);
584 cutrhs = ub;
585 cutnnz++;
586
587 activity = varsolval - ub;
588 lastcoef = 0.0;
589
590 for( j = 0; j < vubmixsize; j++ )
591 {
592 SCIP_Real solval;
593
594 solval = vubmixsols[j];
595
596 /* stop if we can not find a violated cut or we reached 0 solution values */
597 if( activity + solval * (maxabscoef - lastcoef) < 0.0 || SCIPisFeasZero(scip, solval) )
598 break;
599 else
600 {
601 /* skip if we have already added a variable with bigger coefficient */
602 if( SCIPisLE(scip, vubmixcoefs[j], lastcoef) )
603 continue;
604 else
605 {
606 activity += (vubmixcoefs[j] - lastcoef) * solval;
607 if( vubmixsigns[j] )
608 {
609 cutcoefs[cutnnz] = lastcoef - vubmixcoefs[j];
610 cutrhs -= vubmixcoefs[j] - lastcoef;
611 }
612 else
613 cutcoefs[cutnnz] = vubmixcoefs[j] - lastcoef;
614 cutinds[cutnnz++] = vubmixinds[j];
615 lastcoef = vubmixcoefs[j];
616 }
617 }
618 }
619
620 /* add the variable with maximal coefficient if needed */
621 if( SCIPisGT(scip, maxabscoef, lastcoef) )
622 {
623 /* do not update activity: either the corresponding solval is 0.0 or the cut will not become efficious */
624 if( maxabssign )
625 {
626 cutcoefs[cutnnz] = lastcoef - maxabscoef;
627 cutrhs -= maxabscoef - lastcoef;
628 }
629 else
630 cutcoefs[cutnnz] = maxabscoef - lastcoef;
631 cutinds[cutnnz++] = maxabsind;
632 }
633 assert( cutnnz <= nmaxvars + 1 );
634
635 /* add the cut if the violtion is good enough and the number of nonzero coefficients is larger than 2 */
636 if( SCIPisEfficacious(scip, activity) && cutnnz > 2 )
637 {
638 SCIP_CALL( addCut(scip, sepa, sol, cutcoefs, cutinds, cutnnz, cutrhs, islocalub, cutoff, ncuts) );
639 }
640
641 CONFLICT:
642 /* combine the variable lower bounds information and upper bounds information together to generate cuts */
643 /* stop if no useful variable lower (or upper) bounds information exists */
644 if( vlbmixsize == 0 || vubmixsize == 0 )
645 continue;
646
647 assert( lb != SCIP_INVALID ); /*lint !e777*/
648 assert( ub != SCIP_INVALID ); /*lint !e777*/
649
650 cutislocal = islocallb || islocalub;
651 for( j = 0; j < vlbmixsize; j++ )
652 {
653 SCIP_Real solval;
654
655 solval = vlbmixsols[j];
656
657 /* stop if no violated cut exists */
658 if( ! SCIPisEfficacious(scip, solval + vubmixsols[0] - 1.0) )
659 break;
660
661 for( k = 0; k < vubmixsize; k++ )
662 {
663 /* only consider the inequality if its violation is good enough */
664 if( SCIPisEfficacious(scip, solval + vubmixsols[k] - 1.0) )
665 {
666 SCIP_Real tmp;
667
668 tmp = lb + vlbmixcoefs[j] + vubmixcoefs[k] - ub;
669
670 /* add the cut if it is valid */
671 if( SCIPisEfficacious(scip, tmp) )
672 {
673 cutnnz = 2;
674 cutrhs = 1.0;
675 cutcoefs[0] = vlbmixsigns[j] ? -1.0 : 1.0;
676 cutcoefs[1] = vubmixsigns[k] ? -1.0 : 1.0;
677 cutinds[0] = vlbmixinds[j];
678 cutinds[1] = vubmixinds[k];
679 cutrhs = vlbmixsigns[j] ? (cutrhs - 1.0) : cutrhs;
680 cutrhs = vubmixsigns[k] ? (cutrhs - 1.0) : cutrhs;
681 SCIP_CALL( addCut(scip, sepa, sol, cutcoefs, cutinds, cutnnz, cutrhs, cutislocal, cutoff, ncuts) );
682 }
683 }
684 else
685 break;
686 }
687 }
688 }
689
690 /* free temporary memory */
691 SCIPfreeBufferArray(scip, &cutinds);
692 SCIPfreeBufferArray(scip, &cutcoefs);
693 SCIPfreeBufferArray(scip, &vubmixsigns);
694 SCIPfreeBufferArray(scip, &vubmixinds);
695 SCIPfreeBufferArray(scip, &vubmixsols);
696 SCIPfreeBufferArray(scip, &vubmixcoefs);
697 SCIPfreeBufferArray(scip, &vlbmixsigns);
698 SCIPfreeBufferArray(scip, &vlbmixinds);
699 SCIPfreeBufferArray(scip, &vlbmixsols);
700 SCIPfreeBufferArray(scip, &vlbmixcoefs);
701
702 return SCIP_OKAY;
703 }
704
705
706 /*
707 * Callback methods of separator
708 */
709
710 /** copy method for separator plugins (called when SCIP copies plugins) */
711 static
712 SCIP_DECL_SEPACOPY(sepaCopyMixing)
713 { /*lint --e{715}*/
714 assert(scip != NULL);
715 assert(sepa != NULL);
716 assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0);
717
718 /* call inclusion method of separator */
719 SCIP_CALL( SCIPincludeSepaMixing(scip) );
720
721 return SCIP_OKAY;
722 }
723
724 /** destructor of separator to free user data (called when SCIP is exiting) */
725 static
726 SCIP_DECL_SEPAFREE(sepaFreeMixing)
727 { /*lint --e{715}*/
728 SCIP_SEPADATA* sepadata;
729
730 assert(scip != NULL);
731 assert(sepa != NULL);
732 assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0);
733
734 /* get separation data and free it */
735 sepadata = SCIPsepaGetData(sepa);
736 assert(sepadata != NULL);
737 SCIPfreeBlockMemory(scip, &sepadata);
738
739 /* reset data pointer to NULL */
740 SCIPsepaSetData(sepa, NULL);
741
742 return SCIP_OKAY;
743 }
744
745
746 /** LP solution separation method of separator */
747 static
748 SCIP_DECL_SEPAEXECLP(sepaExeclpMixing)
749 { /*lint --e{715}*/
750 SCIP_SEPADATA* sepadata;
751 SCIP_Bool cutoff;
752 int nbinvars;
753 int nvars;
754 int ncuts;
755 int ncalls;
756
757 assert(sepa != NULL);
758 assert(scip != NULL);
759 assert(result != NULL);
760
761 *result = SCIP_DIDNOTRUN;
762 ncalls = SCIPsepaGetNCallsAtNode(sepa);
763 sepadata = SCIPsepaGetData(sepa);
764 assert(sepadata != NULL);
765
766 /* only call the mixing cut separator a given number of times at each node */
767 if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot)
768 || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) )
769 return SCIP_OKAY;
770
771 /* gets numver of active problem variables and number of binary variables */
772 SCIP_CALL( SCIPgetVarsData(scip, NULL, &nvars, &nbinvars, NULL, NULL, NULL) );
773
774 /* if all the active problem variables are binary, stop */
775 if( nvars == nbinvars )
776 return SCIP_OKAY;
777
778 /* call the cut separation */
779 SCIP_CALL( separateCuts(scip, sepa, NULL, &cutoff, &ncuts) );
780
781 /* adjust result code */
782 if( cutoff )
783 *result = SCIP_CUTOFF;
784 else if( ncuts > 0 )
785 {
786 SCIPdebugMsg(scip, "mixing separator generated %d cuts.\n", ncuts);
787 *result = SCIP_SEPARATED;
788 }
789 else
790 *result = SCIP_DIDNOTFIND;
791
792 return SCIP_OKAY;
793 }
794
795 /** arbitrary primal solution separation method of separator */
796 static
797 SCIP_DECL_SEPAEXECSOL(sepaExecSolMixing)
798 { /*lint --e{715}*/
799 SCIP_SEPADATA* sepadata;
800 SCIP_Bool cutoff;
801 int nbinvars;
802 int nvars;
803 int ncuts;
804 int ncalls;
805
806 assert(sepa != NULL);
807 assert(scip != NULL);
808 assert(result != NULL);
809
810 *result = SCIP_DIDNOTRUN;
811 sepadata = SCIPsepaGetData(sepa);
812 assert(sepadata != NULL);
813
814 /* do not run if we have reached the maximal number of consecutive unsuccessful calls */
815 if( sepadata->nunsuccessful >= sepadata->maxnunsuccessful )
816 return SCIP_OKAY;
817
818 /* only call the mixing cut separator a given number of times at each node */
819 ncalls = SCIPsepaGetNCallsAtNode(sepa);
820 if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot)
821 || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) )
822 return SCIP_OKAY;
823
824 /* gets numver of active problem variables and number of binary variables */
825 nvars = SCIPgetNVars(scip);
826 nbinvars = SCIPgetNBinVars(scip);
827
828 /* if all the active problem variables are binary, stop */
829 if( nvars == nbinvars )
830 return SCIP_OKAY;
831
832 /* call the cut separation */
833 SCIP_CALL( separateCuts(scip, sepa, sol, &cutoff, &ncuts) );
834
835 /* adjust result code */
836 if( cutoff )
837 {
838 sepadata->nunsuccessful = 0;
839 *result = SCIP_CUTOFF;
840 }
841 else if( ncuts > 0 )
842 {
843 SCIPdebugMsg(scip, "mixing separator generated %d cuts.\n", ncuts);
844 sepadata->nunsuccessful = 0;
845 *result = SCIP_SEPARATED;
846 }
847 else
848 {
849 ++sepadata->nunsuccessful;
850 *result = SCIP_DIDNOTFIND;
851 }
852
853 return SCIP_OKAY;
854 }
855
856
857 /*
858 * separator specific interface methods
859 */
860
861 /** creates the mixing separator and includes it in SCIP */
862 SCIP_RETCODE SCIPincludeSepaMixing(
863 SCIP* scip /**< SCIP data structure */
864 )
865 {
866 SCIP_SEPADATA* sepadata;
867 SCIP_SEPA* sepa;
868
869 /* create mixing separator data */
870 SCIP_CALL( SCIPallocBlockMemory(scip, &sepadata) );
871 assert(sepadata != NULL);
872 sepadata->nunsuccessful = 0;
873
874 /* include separator */
875 SCIP_CALL( SCIPincludeSepaBasic(scip, &sepa, SEPA_NAME, SEPA_DESC, SEPA_PRIORITY, SEPA_FREQ, SEPA_MAXBOUNDDIST,
876 SEPA_USESSUBSCIP, SEPA_DELAY,
877 sepaExeclpMixing, sepaExecSolMixing,
878 sepadata) );
879 assert(sepa != NULL);
880
881 /* set non-NULL pointers to callback methods */
882 SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopyMixing) );
883 SCIP_CALL( SCIPsetSepaFree(scip, sepa, sepaFreeMixing) );
884
885 /* add separator parameters */
886 SCIP_CALL( SCIPaddBoolParam(scip, "separating/mixing/uselocalbounds",
887 "Should local bounds be used?",
888 &sepadata->uselocalbounds, TRUE, DEFAULT_USELOCALBOUNDS, NULL, NULL) );
889
890 SCIP_CALL( SCIPaddBoolParam(scip, "separating/mixing/iscutsonints",
891 "Should general integer variables be used to generate cuts?",
892 &sepadata->iscutsonints, TRUE, DEFAULT_ISCUTSONINTS, NULL, NULL) );
893
894 SCIP_CALL( SCIPaddIntParam(scip, "separating/mixing/maxrounds",
895 "maximal number of mixing separation rounds per node (-1: unlimited)",
896 &sepadata->maxrounds, FALSE, DEFAULT_MAXROUNDS, -1, INT_MAX, NULL, NULL) );
897
898 SCIP_CALL( SCIPaddIntParam(scip, "separating/mixing/maxroundsroot",
899 "maximal number of mixing separation rounds in the root node (-1: unlimited)",
900 &sepadata->maxroundsroot, FALSE, DEFAULT_MAXROUNDSROOT, -1, INT_MAX, NULL, NULL) );
901
902 SCIP_CALL( SCIPaddIntParam(scip, "separating/mixing/maxnunsuccessful",
903 "maximal number of consecutive unsuccessful iterations",
904 &sepadata->maxnunsuccessful, FALSE, DEFAULT_MAXNUNSUCCESSFUL, -1, INT_MAX, NULL, NULL) );
905
906 return SCIP_OKAY;
907 }
908