SCIP Doxygen Documentation
Loading...
Searching...
No Matches
cutsel_ensemble.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2026 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file cutsel_ensemble.c
26 * @ingroup DEFPLUGINS_CUTSEL
27 * @brief ensemble cut selector
28 * @author Mark Turner
29 *
30 * @todo separator hard limit on density is inappropriate for MINLP. Need to relax hard limit in case of all cuts dense
31 * @todo penalising via parallelism is overly costly if many cuts. Hash cuts before and find appropriate groups?
32 */
33
34/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35
36#include "scip/scip_cutsel.h"
37#include "scip/scip_cut.h"
38#include "scip/scip_lp.h"
41
42
43#define CUTSEL_NAME "ensemble"
44#define CUTSEL_DESC "weighted sum of many terms with optional filtering and penalties"
45#define CUTSEL_PRIORITY 7000
46
47#define RANDSEED 0x5EED
48
49#define DEFAULT_MINSCORE 0.0 /**< minimum score s.t. a cut can be selected */
50#define DEFAULT_EFFICACYWEIGHT 0.75 /**< weight of normed-efficacy in score calculation */
51#define DEFAULT_DIRCUTOFFDISTWEIGHT 0.0 /**< weight of normed-directed cutoff distance in score calculation */
52#define DEFAULT_OBJPARALWEIGHT 0.25 /**< weight of objective parallelism in score calculation */
53#define DEFAULT_INTSUPPORTWEIGHT 0.45 /**< weight of integral support in cut score calculation */
54#define DEFAULT_EXPIMPROVWEIGHT 0.1 /**< weight of normed-expected improvement in cut score calculation */
55#define DEFAULT_PSCOSTWEIGHT 0.75 /**< weight of normalised pseudo-costs in cut score calculation */
56#define DEFAULT_NLOCKSWEIGHT 0.25 /**< weight of normalised number of locks in cut score calculation */
57#define DEFAULT_MAXSPARSITYBONUS 0.5 /**< score given to a cut with complete sparsity */
58#define DEFAULT_SPARSITYENDBONUS 0.2 /**< the density at which a cut no longer receives additional score */
59#define DEFAULT_GOODNUMERICBONUS 0.0 /**< bonus provided for good numerics */
60#define DEFAULT_MAXCOEFRATIOBONUS 10000 /**< maximum coefficient ratio of cut for which numeric bonus is given */
61#define DEFAULT_PENALISELOCKS TRUE /**< whether having less locks should be rewarded instead of more */
62#define DEFAULT_PENALISEOBJPARAL TRUE /**< whether objective parallelism should be penalised not rewarded */
63#define DEFAULT_FILTERPARALCUTS FALSE /**< should cuts be filtered so no two parallel cuts are added */
64#define DEFAULT_MAXPARAL 0.95 /**< threshold for when two cuts are considered parallel to each other */
65#define DEFAULT_PENALISEPARALCUTS TRUE /**< should two parallel cuts be penalised instead of outright filtered */
66#define DEFAULT_PARALPENALTY 0.25 /**< penalty for weaker of two parallel cuts if penalising parallel cuts */
67#define DEFAULT_FILTERDENSECUTS TRUE /**< should cuts over a given density threshold be filtered */
68#define DEFAULT_MAXCUTDENSITY 0.425 /**< max allowed cut density if filtering dense cuts */
69#define DEFAULT_MAXNONZEROROOTROUND 4.5 /**< max nonzeros per round (root). Gets multiplied by num LP cols */
70#define DEFAULT_MAXNONZEROTREEROUND 9.5 /**< max nonzeros per round (tree). Gets multiplied by num LP cols */
71#define DEFAULT_MAXCUTS 200 /**< maximum number of cuts that can be considered by this cut selector */
72#define DEFAULT_MAXNUMVARS 50000 /**< maximum number of variables that a problem can have while calling this cut selector */
73
74/*
75 * Data structures
76 */
77
78/** cut selector data */
79struct SCIP_CutselData
80{
81 SCIP_RANDNUMGEN* randnumgen; /**< random generator for tie-breaking */
82 SCIP_Real minscore; /**< minimum score for a cut to be added to the LP */
83 SCIP_Real objparalweight; /**< weight of objective parallelism in cut score calculation */
84 SCIP_Real efficacyweight; /**< weight of normed-efficacy in cut score calculation */
85 SCIP_Real dircutoffdistweight;/**< weight of normed-directed cutoff distance in cut score calculation */
86 SCIP_Real expimprovweight; /**< weight of normed-expected improvement in cut score calculation */
87 SCIP_Real intsupportweight; /**< weight of integral support in cut score calculation */
88 SCIP_Real pscostweight; /**< weight of normalised pseudo-costs in cut score calculation */
89 SCIP_Real locksweight; /**< weight of normed-number of active locks in cut score calculation */
90 SCIP_Real maxsparsitybonus; /**< weight of maximum sparsity reward in cut score calculation */
91 SCIP_Real goodnumericsbonus; /**< weight of good numeric bonus in cut score calculation */
92 SCIP_Real endsparsitybonus; /**< max sparsity value for which a bonus is applied */
93 SCIP_Real maxparal; /**< threshold for when two cuts are considered parallel to each other */
94 SCIP_Real paralpenalty; /**< penalty for weaker of two parallel cuts if penalising parallel cuts */
95 SCIP_Real maxcutdensity; /**< max allowed cut density if filtering dense cuts */
96 SCIP_Real maxnonzerorootround;/**< max nonzeros per round (root). Gets multiplied by num LP cols */
97 SCIP_Real maxnonzerotreeround;/**< max nonzeros per round (tree). Gets multiplied by num LP cols */
98 SCIP_Bool filterparalcuts; /**< should cuts be filtered so no two parallel cuts are added */
99 SCIP_Bool penaliseparalcuts; /**< should two parallel cuts be penalised instead of outright filtered */
100 SCIP_Bool filterdensecuts; /**< should cuts over a given density threshold be filtered */
101 SCIP_Bool penaliselocks; /**< whether the number of locks should be penalised instead of rewarded */
102 SCIP_Bool penaliseobjparal; /**< whether objective parallelism should be penalised */
103 int maxcoefratiobonus; /**< maximum coefficient ratio for which numeric bonus is applied */
104 int maxcuts; /**< maximum number of cuts that can be considered by this cut selector */
105 int maxnumvars; /**< maximum number of variables that a problem can have while calling this cut selector */
106};
107
108
109/*
110 * Local methods
111 */
112
113/** returns the maximum score of cuts; if scores is not NULL, then stores the individual score of each cut in scores */
114static
116 SCIP* scip, /**< SCIP data structure */
117 SCIP_ROW** cuts, /**< array with cuts to score */
118 SCIP_CUTSELDATA* cutseldata, /**< cut selector data */
119 SCIP_Real* scores, /**< array to store the score of cuts or NULL */
120 SCIP_Bool root, /**< whether we are at the root node or not */
121 int ncuts /**< number of cuts in cuts array */
122 )
123{
124 SCIP_Real* effs;
125 SCIP_Real* dcds;
126 SCIP_Real* exps;
127 SCIP_Real* cutdensities;
128 SCIP_Real* cutlocks;
129 SCIP_Real* pscosts;
130 SCIP_SOL* sol;
131 SCIP_Real maxdcd = 0.0;
132 SCIP_Real maxeff = 0.0;
133 SCIP_Real maxexp = 0.0;
134 SCIP_Real maxpscost = 0.0;
135 SCIP_Real maxlocks = 0.0;
136 SCIP_Real ncols;
137
138 /* Get the solution that we use for directed cutoff distance calculations. Get the number of columns too */
140 ncols = SCIPgetNLPCols(scip);
141
142 /* Initialise all array information that we're going to use for scoring */
143 SCIP_CALL( SCIPallocBufferArray(scip, &effs, ncuts) );
144 SCIP_CALL( SCIPallocBufferArray(scip, &dcds, ncuts) );
145 SCIP_CALL( SCIPallocBufferArray(scip, &exps, ncuts) );
146 SCIP_CALL( SCIPallocBufferArray(scip, &cutdensities, ncuts) );
147 SCIP_CALL( SCIPallocBufferArray(scip, &cutlocks, ncuts) );
148 SCIP_CALL( SCIPallocBufferArray(scip, &pscosts, ncuts) );
149
150 /* Populate the number of cut locks, the pseudo-cost scores, and the cut densities */
151 for (int i = 0; i < ncuts; ++i )
152 {
153 SCIP_COL** cols;
154 SCIP_Real* cutvals;
155 SCIP_Real sqrcutnorm;
156 SCIP_Real ncutcols;
157 SCIP_Real cutalpha;
158
159 cols = SCIProwGetCols(cuts[i]);
160 cutvals = SCIProwGetVals(cuts[i]);
161 sqrcutnorm = MAX(SCIPsumepsilon(scip), SQR(SCIProwGetNorm(cuts[i]))); /*lint !e666*/
162 cutalpha = -SCIPgetRowFeasibility(scip, cuts[i]) / sqrcutnorm;
163 ncutcols = SCIProwGetNNonz(cuts[i]);
164 cutdensities[i] = ncutcols / ncols;
165 cutlocks[i] = 0;
166 pscosts[i] = 0;
167
168 for ( int j = 0; j < (int) ncutcols; ++j )
169 {
170 SCIP_VAR* colvar;
171 SCIP_Real colval;
172 SCIP_Real l1dist;
173
174 colval = SCIPcolGetPrimsol(cols[j]);
175 colvar = SCIPcolGetVar(cols[j]);
176 /* Get the number of active locks feature in the cut */
177 if( ! SCIPisInfinity(scip, SCIProwGetRhs(cuts[i])) && cutvals[j] > 0.0 )
178 cutlocks[i] += SCIPvarGetNLocksUp(colvar);
179 if( ! SCIPisInfinity(scip, -SCIProwGetLhs(cuts[i])) && cutvals[j] < 0.0 )
180 cutlocks[i] += SCIPvarGetNLocksUp(colvar);
181 if( ! SCIPisInfinity(scip, SCIProwGetRhs(cuts[i])) && cutvals[j] < 0.0 )
182 cutlocks[i] += SCIPvarGetNLocksDown(colvar);
183 if( ! SCIPisInfinity(scip, -SCIProwGetLhs(cuts[i])) && cutvals[j] > 0.0 )
184 cutlocks[i] += SCIPvarGetNLocksDown(colvar);
185
186 /* Get the L1 distance from the projection onto the cut and the LP solution in the variable direction */
187 l1dist = ABS(colval - (cutalpha * cutvals[j]));
188 pscosts[i] += SCIPgetVarPseudocostScore(scip, colvar, colval) * l1dist;
189 }
190 cutlocks[i] = cutlocks[i] / ncutcols; /*lint !e414*/
191
192 if( cutlocks[i] > maxlocks )
193 maxlocks = cutlocks[i];
194
195 if( pscosts[i] > maxpscost )
196 maxpscost = pscosts[i];
197 }
198
199 /* account for the case where maxlocks or maxpscost is 0 */
200 maxpscost = MAX(maxpscost, SCIPepsilon(scip)); /*lint !e666*/
201 maxlocks = MAX(maxlocks, 1);
202
203 for ( int i = 0; i < ncuts; i++ )
204 {
205 cutlocks[i] = cutlocks[i] / maxlocks; /*lint !e414*/
206 /* if locks are penalized, we complement the corresponding score */
207 if( cutseldata->penaliselocks )
208 cutlocks[i] = 1 - cutlocks[i];
209 pscosts[i] = pscosts[i] / maxpscost; /*lint !e414*/
210 }
211
212 /* Get the arrays / maximums of directed cutoff distance, efficacy, and expected improvement values. */
213 if ( sol != NULL && root )
214 {
215 for ( int i = 0; i < ncuts; i++ )
216 {
217 dcds[i] = SCIPgetCutLPSolCutoffDistance(scip, sol, cuts[i]);
218 maxdcd = MAX(maxdcd, dcds[i]);
219 }
220 }
221
222 for ( int i = 0; i < ncuts; ++i )
223 {
224 effs[i] = SCIPgetCutEfficacy(scip, NULL, cuts[i]);
225 exps[i] = effs[i] * SCIPgetRowObjParallelism(scip, cuts[i]);
226 maxeff = MAX(maxeff, effs[i]);
227 maxexp = MAX(maxexp, exps[i]);
228 }
229
230 /* Now score the cuts */
231 for ( int i = 0; i < ncuts; ++i )
232 {
233 SCIP_Real score;
234 SCIP_Real scaleddcd;
235 SCIP_Real scaledeff;
236 SCIP_Real scaledexp;
237 SCIP_Real objparallelism;
238 SCIP_Real intsupport;
240 SCIP_Real dynamism;
241 SCIP_Real mincutval;
242 SCIP_Real maxcutval;
243 SCIP_Real pscost;
244 SCIP_Real cutlock;
245
246 /* Get the integer support */
247 intsupport = SCIPgetRowNumIntCols(scip, cuts[i]) / (SCIP_Real) SCIProwGetNNonz(cuts[i]);
248 intsupport *= cutseldata->intsupportweight;
249
250 /* Get the objective parallelism and orthogonality */
251 if( ! cutseldata->penaliseobjparal )
252 objparallelism = cutseldata->objparalweight * SCIPgetRowObjParallelism(scip, cuts[i]);
253 else
254 objparallelism = cutseldata->objparalweight * (1 - SCIPgetRowObjParallelism(scip, cuts[i]));
255
256 /* Get the density score */
257 density = (cutseldata->maxsparsitybonus / cutseldata->endsparsitybonus) * -1 * cutdensities[i];
258 density += cutseldata->maxsparsitybonus;
259 density = MAX(density, 0.0);
260
261 /* Get the normalised pseudo-cost and number of locks score */
262 if( root )
263 pscost = 0.0;
264 else
265 pscost = cutseldata->pscostweight * pscosts[i];
266 cutlock = cutseldata->locksweight * cutlocks[i];
267
268 /* Get the dynamism (good numerics) score */
269 maxcutval = SCIPgetRowMaxCoef(scip, cuts[i]);
270 mincutval = SCIPgetRowMinCoef(scip, cuts[i]);
271 mincutval = mincutval > 0.0 ? mincutval : 1.0;
272 dynamism = cutseldata->maxcoefratiobonus >= maxcutval / mincutval ? cutseldata->goodnumericsbonus : 0.0;
273
274 /* Get the dcd / eff / exp score */
275 if ( sol != NULL && root )
276 {
277 if ( SCIPisSumLE(scip, dcds[i], 0.0))
278 scaleddcd = 0.0;
279 else
280 scaleddcd = cutseldata->dircutoffdistweight * SQR(LOG1P(dcds[i]) / LOG1P(maxdcd)); /*lint !e666*/
281 }
282 else
283 {
284 scaleddcd = 0.0;
285 }
286
287 if ( SCIPisSumLE(scip, exps[i], 0.0))
288 scaledexp = 0.0;
289 else
290 scaledexp = cutseldata->expimprovweight * SQR(LOG1P(exps[i]) / LOG1P(maxexp)); /*lint !e666*/
291
292 if ( SCIPisSumLE(scip, effs[i], 0.0))
293 {
294 scaledeff = 0.0;
295 }
296 else
297 {
298 if ( sol != NULL && root )
299 scaledeff = cutseldata->efficacyweight * SQR(LOG1P(effs[i]) / LOG1P(maxeff)); /*lint !e666*/
300 else
301 scaledeff = (cutseldata->efficacyweight + cutseldata->dircutoffdistweight) * SQR(LOG1P(effs[i]) / LOG1P(maxeff)); /*lint !e666*/
302 }
303
304 /* Combine all scores and introduce some minor randomness */
305 score = scaledeff + scaleddcd + scaledexp + objparallelism + intsupport + density + dynamism + pscost + cutlock;
306
307 score += SCIPrandomGetReal(cutseldata->randnumgen, 0.0, 1e-6);
308
309 scores[i] = score;
310 }
311
315 SCIPfreeBufferArray(scip, &cutdensities);
316 SCIPfreeBufferArray(scip, &cutlocks);
317 SCIPfreeBufferArray(scip, &pscosts);
318
319 return SCIP_OKAY;
320}
321
322
323/** move the cut with the highest score to the first position in the array; there must be at least one cut */
324static
326 SCIP_ROW** cuts, /**< array with cuts to perform selection algorithm */
327 SCIP_Real* scores, /**< array with scores of cuts to perform selection algorithm */
328 int ncuts /**< number of cuts in given array */
329 )
330{
331 int bestpos;
332 SCIP_Real bestscore;
333
334 assert(ncuts > 0);
335 assert(cuts != NULL);
336 assert(scores != NULL);
337
338 bestscore = scores[0];
339 bestpos = 0;
340
341 for( int i = 1; i < ncuts; ++i )
342 {
343 if( scores[i] > bestscore )
344 {
345 bestpos = i;
346 bestscore = scores[i];
347 }
348 }
349
350 SCIPswapPointers((void**) &cuts[bestpos], (void**) &cuts[0]);
351 SCIPswapReals(&scores[bestpos], &scores[0]);
352}
353
354/** filters the given array of cuts to enforce a maximum parallelism constraint
355 * w.r.t the given cut; moves filtered cuts to the end of the array and returns number of selected cuts */
356static
358 SCIP_ROW* cut, /**< cut to filter orthogonality with */
359 SCIP_ROW** cuts, /**< array with cuts to perform selection algorithm */
360 SCIP_Real* scores, /**< array with scores of cuts to perform selection algorithm */
361 int ncuts, /**< number of cuts in given array */
362 SCIP_Real maxparallel /**< maximal parallelism for all cuts that are not good */
363 )
364{
365 assert( cut != NULL );
366 assert( ncuts == 0 || cuts != NULL );
367 assert( ncuts == 0 || scores != NULL );
368
369 for( int i = ncuts - 1; i >= 0; --i )
370 {
371 SCIP_Real thisparallel;
372
373 thisparallel = SCIProwGetParallelism(cut, cuts[i], 'e');
374
375 if( thisparallel > maxparallel )
376 {
377 --ncuts;
378 SCIPswapPointers((void**) &cuts[i], (void**) &cuts[ncuts]);
379 SCIPswapReals(&scores[i], &scores[ncuts]);
380 }
381 }
382
383 return ncuts;
384}
385
386/** penalises any cut too parallel to cut by reducing the parallel cut's score. */
387static
389 SCIP* scip, /**< SCIP data structure */
390 SCIP_ROW* cut, /**< cut to filter orthogonality with */
391 SCIP_ROW** cuts, /**< array with cuts to perform selection algorithm */
392 SCIP_Real* scores, /**< array with scores of cuts to perform selection algorithm */
393 int ncuts, /**< number of cuts in given array */
394 SCIP_Real maxparallel, /**< maximal parallelism for all cuts that are not good */
395 SCIP_Real paralpenalty /**< penalty for weaker of two parallel cuts if penalising parallel cuts */
396 )
397{
398 assert( cut != NULL );
399 assert( ncuts == 0 || cuts != NULL );
400 assert( ncuts == 0 || scores != NULL );
401
402 for( int i = ncuts - 1; i >= 0; --i )
403 {
404 SCIP_Real thisparallel;
405
406 thisparallel = SCIProwGetParallelism(cut, cuts[i], 'e');
407
408 /* Filter cuts that are absolutely parallel still. Otherwise penalise if closely parallel */
409 if( thisparallel > 1 - SCIPsumepsilon(scip) )
410 {
411 --ncuts;
412 SCIPswapPointers((void**) &cuts[i], (void**) &cuts[ncuts]);
413 SCIPswapReals(&scores[i], &scores[ncuts]);
414 }
415 else if( thisparallel > maxparallel )
416 {
417 scores[i] -= paralpenalty;
418 }
419 }
420
421 return ncuts;
422}
423
424/** filters the given array of cuts to enforce a maximum density constraint,
425 * Moves filtered cuts to the end of the array and returns number of selected cuts */
426static
428 SCIP* scip, /**< SCIP data structure */
429 SCIP_ROW** cuts, /**< array with cuts to perform selection algorithm */
430 SCIP_Real maxdensity, /**< maximum density s.t. a cut is not filtered */
431 int ncuts /**< number of cuts in given array */
432 )
433{
434 SCIP_Real ncols;
435
436 assert( ncuts == 0 || cuts != NULL );
437
438 ncols = SCIPgetNLPCols(scip);
439
440 for( int i = ncuts - 1; i >= 0; --i )
441 {
442 SCIP_Real nvals;
443
444 nvals = SCIProwGetNNonz(cuts[i]);
445
446 if( maxdensity < nvals / ncols )
447 {
448 --ncuts;
449 SCIPswapPointers((void**) &cuts[i], (void**) &cuts[ncuts]);
450 }
451 }
452
453 return ncuts;
454}
455
456
457/*
458 * Callback methods of cut selector
459 */
460
461
462/** copy method for cut selector plugin (called when SCIP copies plugins) */
463static
464SCIP_DECL_CUTSELCOPY(cutselCopyEnsemble)
465{ /*lint --e{715}*/
466 assert(scip != NULL);
467 assert(cutsel != NULL);
468
470
471 /* call inclusion method of cut selector */
473
474 return SCIP_OKAY;
475}
476
477/** destructor of cut selector to free user data (called when SCIP is exiting) */
478/**! [SnippetCutselFreeEnsemble] */
479static
480SCIP_DECL_CUTSELFREE(cutselFreeEnsemble)
481{ /*lint --e{715}*/
482 SCIP_CUTSELDATA* cutseldata;
483
484 cutseldata = SCIPcutselGetData(cutsel);
485
486 SCIPfreeBlockMemory(scip, &cutseldata);
487
488 SCIPcutselSetData(cutsel, NULL);
489
490 return SCIP_OKAY;
491}
492/**! [SnippetCutselFreeEnsemble] */
493
494/** initialization method of cut selector (called after problem was transformed) */
495static
496SCIP_DECL_CUTSELINIT(cutselInitEnsemble)
497{ /*lint --e{715}*/
498 SCIP_CUTSELDATA* cutseldata;
499
500 cutseldata = SCIPcutselGetData(cutsel);
501 assert(cutseldata != NULL);
502
503 SCIP_CALL( SCIPcreateRandom(scip, &(cutseldata)->randnumgen, RANDSEED, TRUE) );
504
505 return SCIP_OKAY;
506}
507
508/** deinitialization method of cut selector (called before transformed problem is freed) */
509static
510SCIP_DECL_CUTSELEXIT(cutselExitEnsemble)
511{ /*lint --e{715}*/
512 SCIP_CUTSELDATA* cutseldata;
513
514 cutseldata = SCIPcutselGetData(cutsel);
515 assert(cutseldata != NULL);
516 assert(cutseldata->randnumgen != NULL);
517
518 SCIPfreeRandom(scip, &cutseldata->randnumgen);
519
520 return SCIP_OKAY;
521}
522
523/** cut selection method of cut selector */
524static
525SCIP_DECL_CUTSELSELECT(cutselSelectEnsemble)
526{ /*lint --e{715}*/
527 SCIP_CUTSELDATA* cutseldata;
528
529 assert(cutsel != NULL);
530 assert(result != NULL);
531
532 cutseldata = SCIPcutselGetData(cutsel);
533 assert(cutseldata != NULL);
534
535 if( ncuts > cutseldata->maxcuts || SCIPgetNVars(scip) > cutseldata->maxnumvars )
536 {
538 return SCIP_OKAY;
539 }
540
542
543 SCIP_CALL( SCIPselectCutsEnsemble(scip, cuts, forcedcuts, cutseldata, root, ncuts, nforcedcuts,
544 maxnselectedcuts, nselectedcuts) );
545
546 return SCIP_OKAY;
547}
548
549
550/*
551 * cut selector specific interface methods
552 */
553
554/** creates the ensemble cut selector and includes it in SCIP */
556 SCIP* scip /**< SCIP data structure */
557 )
558{
559 SCIP_CUTSELDATA* cutseldata;
560 SCIP_CUTSEL* cutsel;
561
562 /* create ensemble cut selector data */
563 SCIP_CALL( SCIPallocBlockMemory(scip, &cutseldata) );
564 BMSclearMemory(cutseldata);
565
567 cutseldata) );
568
569 assert(cutsel != NULL);
570
571 /* set non fundamental callbacks via setter functions */
572 SCIP_CALL( SCIPsetCutselCopy(scip, cutsel, cutselCopyEnsemble) );
573
574 SCIP_CALL( SCIPsetCutselFree(scip, cutsel, cutselFreeEnsemble) );
575 SCIP_CALL( SCIPsetCutselInit(scip, cutsel, cutselInitEnsemble) );
576 SCIP_CALL( SCIPsetCutselExit(scip, cutsel, cutselExitEnsemble) );
577
578 /* add ensemble cut selector parameters */
579 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/efficacyweight", "weight of normed-efficacy in cut "
580 "score calculation", &cutseldata->efficacyweight, FALSE, DEFAULT_EFFICACYWEIGHT, 0.0, SCIP_INVALID/10.0, NULL,
581 NULL) );
582
583 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/dircutoffdistweight", "weight of normed-directed "
584 "cutoff distance in cut score calculation", &cutseldata->dircutoffdistweight, FALSE,
586
587 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/objparalweight", "weight of objective parallelism "
588 "in cut score calculation", &cutseldata->objparalweight, FALSE, DEFAULT_OBJPARALWEIGHT, 0.0, SCIP_INVALID/10.0,
589 NULL, NULL) );
590
591 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/intsupportweight", "weight of integral support in "
592 "cut score calculation", &cutseldata->intsupportweight, FALSE, DEFAULT_INTSUPPORTWEIGHT, 0.0,
593 SCIP_INVALID/10.0, NULL, NULL) );
594
595 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/expimprovweight", "weight of normed-expected obj "
596 "improvement in cut score calculation", &cutseldata->expimprovweight, FALSE, DEFAULT_EXPIMPROVWEIGHT, 0.0,
597 SCIP_INVALID/10.0, NULL, NULL) );
598
599 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/minscore", "minimum score s.t. a cut can be added",
600 &cutseldata->minscore, FALSE, DEFAULT_MINSCORE, -SCIP_INVALID/10.0, SCIP_INVALID/10.0, NULL, NULL) );
601
602 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/pscostweight", "weight of normed-pseudo-costs in "
603 "cut score calculation", &cutseldata->pscostweight, FALSE, DEFAULT_PSCOSTWEIGHT, 0.0, SCIP_INVALID/10.0, NULL,
604 NULL) );
605
606 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/locksweight", "weight of normed-num-locks in cut "
607 "score calculation", &cutseldata->locksweight, FALSE, DEFAULT_NLOCKSWEIGHT, 0.0, SCIP_INVALID/10.0, NULL,
608 NULL) );
609
610 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/maxsparsitybonus", "weight of maximum sparsity "
611 "reward in cut score calculation", &cutseldata->maxsparsitybonus, FALSE, DEFAULT_MAXSPARSITYBONUS, 0.0,
612 SCIP_INVALID/10.0, NULL, NULL) );
613
614 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/goodnumericsbonus", "weight of good numerics bonus "
615 "(ratio of coefficients) in cut score calculation", &cutseldata->goodnumericsbonus, FALSE,
617
618 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/endsparsitybonus", "max sparsity value for which a "
619 "bonus is applied in cut score calculation", &cutseldata->endsparsitybonus, FALSE, DEFAULT_SPARSITYENDBONUS,
620 0.0, SCIP_INVALID/10.0, NULL, NULL) );
621
622 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/maxparal", "threshold for when two cuts are "
623 "considered parallel to each other", &cutseldata->maxparal, FALSE, DEFAULT_MAXPARAL, 0.0, 1.0, NULL, NULL) );
624
625 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/paralpenalty", "penalty for weaker of two parallel "
626 "cuts if penalising parallel cuts", &cutseldata->paralpenalty, TRUE, DEFAULT_PARALPENALTY, 0.0,
627 SCIP_INVALID/10.0, NULL, NULL) );
628
629 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/maxcutdensity", "max allowed cut density if "
630 "filtering dense cuts", &cutseldata->maxcutdensity, TRUE, DEFAULT_MAXCUTDENSITY, 0.0, 1.0, NULL, NULL) );
631
632 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/maxnonzerorootround", "max non-zeros per round "
633 "applied cuts (root). multiple num LP cols.", &cutseldata->maxnonzerorootround, FALSE,
635
636 SCIP_CALL( SCIPaddRealParam(scip, "cutselection/" CUTSEL_NAME "/maxnonzerotreeround", "max non-zeros per round "
637 "applied cuts (tree). multiple num LP cols.", &cutseldata->maxnonzerotreeround, FALSE,
639
640 SCIP_CALL( SCIPaddBoolParam(scip, "cutselection/" CUTSEL_NAME "/filterparalcuts", "should cuts be filtered so no "
641 "two parallel cuts are added", &cutseldata->filterparalcuts, FALSE, DEFAULT_FILTERPARALCUTS, NULL, NULL) );
642
643 SCIP_CALL( SCIPaddBoolParam(scip, "cutselection/" CUTSEL_NAME "/penaliseparalcuts", "should two parallel cuts be "
644 "penalised instead of outright filtered", &cutseldata->penaliseparalcuts, TRUE, DEFAULT_PENALISEPARALCUTS,
645 NULL, NULL) );
646
647 SCIP_CALL( SCIPaddBoolParam(scip, "cutselection/" CUTSEL_NAME "/filterdensecuts", "should cuts over a given density "
648 "threshold be filtered", &cutseldata->filterdensecuts, TRUE, DEFAULT_FILTERDENSECUTS, NULL, NULL) );
649
650 SCIP_CALL( SCIPaddBoolParam(scip, "cutselection/" CUTSEL_NAME "/penaliselocks", "should the number of locks be "
651 "penalised instead of rewarded", &cutseldata->penaliselocks, TRUE, DEFAULT_PENALISELOCKS, NULL, NULL) );
652
653 SCIP_CALL( SCIPaddBoolParam(scip, "cutselection/" CUTSEL_NAME "/penaliseobjparal", "should objective parallelism "
654 "be penalised instead of rewarded", &cutseldata->penaliseobjparal, TRUE, DEFAULT_PENALISEOBJPARAL, NULL, NULL)
655 );
656
657 SCIP_CALL( SCIPaddIntParam(scip, "cutselection/" CUTSEL_NAME "/maxcoefratiobonus", "max coefficient ratio for "
658 "which numeric bonus is applied.", &cutseldata->maxcoefratiobonus, TRUE, DEFAULT_MAXCOEFRATIOBONUS, 1, 1000000,
659 NULL, NULL) );
660
661 SCIP_CALL( SCIPaddIntParam(scip, "cutselection/" CUTSEL_NAME "/maxcuts", "max number of cuts such that cut "
662 "selector is applied.", &cutseldata->maxcuts, TRUE, DEFAULT_MAXCUTS, 1, 1000000, NULL, NULL) );
663
664 SCIP_CALL( SCIPaddIntParam(scip, "cutselection/" CUTSEL_NAME "/maxnumvars", "max number of variables such that cut "
665 "selector is applied.", &cutseldata->maxnumvars, TRUE, DEFAULT_MAXNUMVARS, 1, 1000000, NULL, NULL) );
666
667 return SCIP_OKAY;
668}
669
670/** perform a cut selection algorithm for the given array of cuts
671 *
672 * This is the selection method of the ensemble cut selector. It uses a weighted sum of normalised efficacy,
673 * normalised directed cutoff distance, normalised expected improvements, objective parallelism,
674 * integer support, sparsity, dynamism, pseudo-costs, and variable locks.
675 * In addition to the weighted sum score, there are optionally parallelism-based filtering and penalties,
676 * and density filtering.
677 * There are also additional budget constraints on the number of cuts that should be added.
678 * The input cuts array gets re-sorted such that the selected cuts come first and the remaining ones are the end.
679 */
681 SCIP* scip, /**< SCIP data structure */
682 SCIP_ROW** cuts, /**< array with cuts to perform selection algorithm */
683 SCIP_ROW** forcedcuts, /**< array with forced cuts */
684 SCIP_CUTSELDATA* cutseldata, /**< cut selector data */
685 SCIP_Bool root, /**< whether we are at the root node or not */
686 int ncuts, /**< number of cuts in cuts array */
687 int nforcedcuts, /**< number of forced cuts */
688 int maxselectedcuts, /**< maximal number of cuts from cuts array to select */
689 int* nselectedcuts /**< pointer to return number of selected cuts from cuts array */
690 )
691{
692 SCIP_Real* scores;
693 SCIP_Real* origscoresptr;
694 SCIP_Real nonzerobudget;
695 SCIP_Real budgettaken = 0.0;
696 SCIP_Real ncols;
697
698 assert(cuts != NULL && ncuts > 0);
699 assert(forcedcuts != NULL || nforcedcuts == 0);
700 assert(nselectedcuts != NULL);
701
702 *nselectedcuts = 0;
703 ncols = SCIPgetNLPCols(scip);
704
705 /* filter dense cuts */
706 if( cutseldata->filterdensecuts )
707 {
708 ncuts = filterWithDensity(scip, cuts, cutseldata->maxcutdensity, ncuts);
709 if( ncuts == 0 )
710 return SCIP_OKAY;
711 }
712
713 /* Initialise the score array */
714 SCIP_CALL( SCIPallocBufferArray(scip, &scores, ncuts) );
715 origscoresptr = scores;
716
717 /* compute scores of cuts */
718 SCIP_CALL( scoring(scip, cuts, cutseldata, scores, root, ncuts) );
719
720 /* perform cut selection algorithm for the cuts */
721
722 /* forced cuts are going to be selected so use them to filter cuts */
723 for( int i = 0; i < nforcedcuts && ncuts > 0; ++i )
724 {
725 if( cutseldata->filterparalcuts )
726 ncuts = filterWithParallelism(forcedcuts[i], cuts, scores, ncuts, cutseldata->maxparal);
727 else if( cutseldata->penaliseparalcuts )
728 ncuts = penaliseWithParallelism(scip, forcedcuts[i], cuts, scores, ncuts, cutseldata->maxparal, cutseldata->paralpenalty);
729 }
730
731 /* Get the budget depending on if we are the root or not */
732 nonzerobudget = root ? cutseldata->maxnonzerorootround : cutseldata->maxnonzerotreeround;
733
734 /* now greedily select the remaining cuts */
735 while( ncuts > 0 )
736 {
737 SCIP_ROW* selectedcut;
738
739 selectBestCut(cuts, scores, ncuts);
740 selectedcut = cuts[0];
741
742 /* if the best cut of the remaining cuts is considered bad, we discard it and all remaining cuts */
743 if( scores[0] < cutseldata->minscore )
744 break;
745
746 ++(*nselectedcuts);
747
748 /* if the maximal number of cuts was selected, we can stop here */
749 if( *nselectedcuts == maxselectedcuts )
750 break;
751
752 /* increase the non-zero budget counter of added cuts */
753 budgettaken += SCIProwGetNNonz(cuts[0]) / ncols;
754
755 /* move the pointers to the next position and filter the remaining cuts to enforce the maximum parallelism constraint */
756 ++cuts;
757 ++scores;
758 --ncuts;
759
760 if( cutseldata->filterparalcuts && ncuts > 0)
761 ncuts = filterWithParallelism(selectedcut, cuts, scores, ncuts, cutseldata->maxparal);
762 else if( cutseldata->penaliseparalcuts && ncuts > 0 )
763 ncuts = penaliseWithParallelism(scip, selectedcut, cuts, scores, ncuts, cutseldata->maxparal, cutseldata->paralpenalty);
764
765 /* Filter out all remaining cuts that would go over the non-zero budget threshold */
766 if( nonzerobudget - budgettaken < 1 && ncuts > 0 )
767 ncuts = filterWithDensity(scip, cuts, nonzerobudget - budgettaken, ncuts);
768 }
769
770 SCIPfreeBufferArray(scip, &origscoresptr);
771
772 return SCIP_OKAY;
773}
#define DEFAULT_EFFICACYWEIGHT
#define DEFAULT_INTSUPPORTWEIGHT
#define DEFAULT_PSCOSTWEIGHT
#define DEFAULT_OBJPARALWEIGHT
#define RANDSEED
#define CUTSEL_DESC
#define CUTSEL_PRIORITY
#define DEFAULT_DIRCUTOFFDISTWEIGHT
#define CUTSEL_NAME
#define DEFAULT_MAXCUTS
#define DEFAULT_SPARSITYENDBONUS
#define DEFAULT_MAXNONZEROTREEROUND
#define DEFAULT_FILTERDENSECUTS
#define DEFAULT_MAXSPARSITYBONUS
#define DEFAULT_MINSCORE
#define DEFAULT_PARALPENALTY
#define DEFAULT_PENALISEPARALCUTS
#define DEFAULT_GOODNUMERICBONUS
#define DEFAULT_PENALISELOCKS
#define DEFAULT_NLOCKSWEIGHT
static SCIP_RETCODE scoring(SCIP *scip, SCIP_ROW **cuts, SCIP_CUTSELDATA *cutseldata, SCIP_Real *scores, SCIP_Bool root, int ncuts)
static void selectBestCut(SCIP_ROW **cuts, SCIP_Real *scores, int ncuts)
static int filterWithDensity(SCIP *scip, SCIP_ROW **cuts, SCIP_Real maxdensity, int ncuts)
static int penaliseWithParallelism(SCIP *scip, SCIP_ROW *cut, SCIP_ROW **cuts, SCIP_Real *scores, int ncuts, SCIP_Real maxparallel, SCIP_Real paralpenalty)
static int filterWithParallelism(SCIP_ROW *cut, SCIP_ROW **cuts, SCIP_Real *scores, int ncuts, SCIP_Real maxparallel)
#define DEFAULT_EXPIMPROVWEIGHT
#define DEFAULT_MAXCOEFRATIOBONUS
#define DEFAULT_MAXCUTDENSITY
#define DEFAULT_MAXNONZEROROOTROUND
#define DEFAULT_PENALISEOBJPARAL
#define DEFAULT_MAXNUMVARS
#define DEFAULT_FILTERPARALCUTS
#define DEFAULT_MAXPARAL
ensemble cut selector
#define NULL
Definition def.h:257
#define LOG1P(x)
Definition def.h:213
#define SCIP_INVALID
Definition def.h:187
#define SCIP_Bool
Definition def.h:100
#define SCIP_STRINGEQ(name, reference, retcode)
Definition def.h:454
#define SCIP_Real
Definition def.h:165
#define ABS(x)
Definition def.h:225
#define SQR(x)
Definition def.h:208
#define TRUE
Definition def.h:102
#define FALSE
Definition def.h:103
#define MAX(x, y)
Definition def.h:229
#define SCIP_CALL(x)
Definition def.h:364
static const SCIP_Real density
Definition gastrans.c:145
SCIP_RETCODE SCIPselectCutsEnsemble(SCIP *scip, SCIP_ROW **cuts, SCIP_ROW **forcedcuts, SCIP_CUTSELDATA *cutseldata, SCIP_Bool root, int ncuts, int nforcedcuts, int maxselectedcuts, int *nselectedcuts)
SCIP_RETCODE SCIPincludeCutselEnsemble(SCIP *scip)
int SCIPgetNVars(SCIP *scip)
Definition scip_prob.c:2246
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:83
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:139
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:57
void SCIPswapPointers(void **pointer1, void **pointer2)
Definition misc.c:10511
void SCIPswapReals(SCIP_Real *value1, SCIP_Real *value2)
Definition misc.c:10498
SCIP_VAR * SCIPcolGetVar(SCIP_COL *col)
Definition lp.c:17425
SCIP_Real SCIPcolGetPrimsol(SCIP_COL *col)
Definition lp.c:17379
SCIP_Real SCIPgetCutEfficacy(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
Definition scip_cut.c:94
SCIP_Real SCIPgetCutLPSolCutoffDistance(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
Definition scip_cut.c:72
SCIP_RETCODE SCIPsetCutselInit(SCIP *scip, SCIP_CUTSEL *cutsel,)
SCIP_RETCODE SCIPincludeCutselBasic(SCIP *scip, SCIP_CUTSEL **cutsel, const char *name, const char *desc, int priority, SCIP_DECL_CUTSELSELECT((*cutselselect)), SCIP_CUTSELDATA *cutseldata)
Definition scip_cutsel.c:98
SCIP_RETCODE SCIPsetCutselCopy(SCIP *scip, SCIP_CUTSEL *cutsel,)
SCIP_RETCODE SCIPsetCutselExit(SCIP *scip, SCIP_CUTSEL *cutsel,)
SCIP_CUTSELDATA * SCIPcutselGetData(SCIP_CUTSEL *cutsel)
Definition cutsel.c:419
void SCIPcutselSetData(SCIP_CUTSEL *cutsel, SCIP_CUTSELDATA *cutseldata)
Definition cutsel.c:429
const char * SCIPcutselGetName(SCIP_CUTSEL *cutsel)
Definition cutsel.c:159
SCIP_RETCODE SCIPsetCutselFree(SCIP *scip, SCIP_CUTSEL *cutsel,)
int SCIPgetNLPCols(SCIP *scip)
Definition scip_lp.c:533
#define SCIPallocBufferArray(scip, ptr, num)
Definition scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition scip_mem.h:136
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
SCIP_Real SCIPgetRowMaxCoef(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1886
SCIP_Real SCIProwGetLhs(SCIP_ROW *row)
Definition lp.c:17686
SCIP_Real SCIPgetRowMinCoef(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1868
SCIP_Real SCIPgetRowFeasibility(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:2088
SCIP_Real SCIProwGetParallelism(SCIP_ROW *row1, SCIP_ROW *row2, char orthofunc)
Definition lp.c:7970
int SCIProwGetNNonz(SCIP_ROW *row)
Definition lp.c:17607
SCIP_COL ** SCIProwGetCols(SCIP_ROW *row)
Definition lp.c:17632
SCIP_Real SCIProwGetRhs(SCIP_ROW *row)
Definition lp.c:17696
SCIP_Real SCIProwGetNorm(SCIP_ROW *row)
Definition lp.c:17662
SCIP_Real SCIPgetRowObjParallelism(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:2154
int SCIPgetRowNumIntCols(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1832
SCIP_Real * SCIProwGetVals(SCIP_ROW *row)
Definition lp.c:17642
SCIP_SOL * SCIPgetBestSol(SCIP *scip)
Definition scip_sol.c:2986
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPepsilon(SCIP *scip)
SCIP_Real SCIPsumepsilon(SCIP *scip)
SCIP_Bool SCIPisSumLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
int SCIPvarGetNLocksDown(SCIP_VAR *var)
Definition var.c:4443
int SCIPvarGetNLocksUp(SCIP_VAR *var)
Definition var.c:4456
SCIP_Real SCIPgetVarPseudocostScore(SCIP *scip, SCIP_VAR *var, SCIP_Real solval)
Definition scip_var.c:11531
SCIP_Real SCIPrandomGetReal(SCIP_RANDNUMGEN *randnumgen, SCIP_Real minrandval, SCIP_Real maxrandval)
Definition misc.c:10245
return SCIP_OKAY
SCIPfreeRandom(scip, &heurdata->randnumgen)
SCIPcreateRandom(scip, &heurdata->randnumgen, DEFAULT_RANDSEED, TRUE))
static SCIP_SOL * sol
assert(minobj< SCIPgetCutoffbound(scip))
#define BMSclearMemory(ptr)
Definition memory.h:129
public methods for cuts and aggregation rows
public methods for cut selector plugins
public methods for the LP relaxation, rows and columns
public methods for random numbers
#define SCIP_DECL_CUTSELEXIT(x)
Definition type_cutsel.h:86
#define SCIP_DECL_CUTSELSELECT(x)
#define SCIP_DECL_CUTSELFREE(x)
Definition type_cutsel.h:70
struct SCIP_Cutsel SCIP_CUTSEL
Definition type_cutsel.h:52
struct SCIP_CutselData SCIP_CUTSELDATA
Definition type_cutsel.h:53
#define SCIP_DECL_CUTSELINIT(x)
Definition type_cutsel.h:78
#define SCIP_DECL_CUTSELCOPY(x)
Definition type_cutsel.h:62
struct SCIP_Row SCIP_ROW
Definition type_lp.h:105
struct SCIP_Col SCIP_COL
Definition type_lp.h:99
struct SCIP_RandNumGen SCIP_RANDNUMGEN
Definition type_misc.h:127
@ SCIP_DIDNOTFIND
Definition type_result.h:44
@ SCIP_SUCCESS
Definition type_result.h:58
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Sol SCIP_SOL
Definition type_sol.h:57
struct SCIP_Var SCIP_VAR
Definition type_var.h:166