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 branch_lookahead.c
17 * @ingroup DEFPLUGINS_BRANCH
18 * @ingroup BRANCHINGRULES
19 * @brief lookahead LP branching rule
20 * @author Christoph Schubert
21 * @author Gerald Gamrath
22 *
23 * The (multi-level) lookahead branching rule applies strong branching to every fractional value of the LP solution
24 * at the current node of the branch-and-bound tree, as well as recursivly to every temporary child problem created by this
25 * strong branching. The rule selects the candidate with the best proven dual bound.
26 *
27 * The branching rule was motivated by the following technical report:
28 *
29 * @par
30 * Wasu Glankwamdee and Jeff Linderoth@n
31 * Lookahead Branching for Mixed Integer Programming@n
32 * Technical Report 06T-004, Department of Industrial and Systems Engineering, Lehigh University.
33 *
34 * For a more mathematical description and a comparison between lookahead branching and other branching rules
35 * in SCIP, we refer to
36 *
37 * @par
38 * Christoph Schubert@n
39 * Multi-Level Lookahead Branching@n
40 * Master Thesis, Technische Universität Berlin, 2017@n
41 */
42
43 /* Supported defines:
44 * PRINTNODECONS: prints the binary constraints added
45 * SCIP_DEBUG: prints detailed execution information
46 * SCIP_STATISTIC: prints some statistics after the branching rule is freed */
47
48 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
49
50 #include "blockmemshell/memory.h"
51 #include "lpi/lpi.h"
52 #include "scip/branch_lookahead.h"
53 #include "scip/cons_logicor.h"
54 #include "scip/pub_branch.h"
55 #include "scip/pub_message.h"
56 #include "scip/pub_misc.h"
57 #include "scip/pub_tree.h"
58 #include "scip/pub_var.h"
59 #include "scip/scip_branch.h"
60 #include "scip/scip_cons.h"
61 #include "scip/scip_general.h"
62 #include "scip/scip_lp.h"
63 #include "scip/scip_mem.h"
64 #include "scip/scip_message.h"
65 #include "scip/scip_numerics.h"
66 #include "scip/scip_param.h"
67 #include "scip/scip_prob.h"
68 #include "scip/scip_probing.h"
69 #include "scip/scip_sol.h"
70 #include "scip/scip_solvingstats.h"
71 #include "scip/scip_tree.h"
72 #include "scip/scip_var.h"
73 #include <string.h>
74
75 #define BRANCHRULE_NAME "lookahead"
76 #define BRANCHRULE_DESC "full strong branching over multiple levels"
77 #define BRANCHRULE_PRIORITY 0
78 #define BRANCHRULE_MAXDEPTH -1
79 #define BRANCHRULE_MAXBOUNDDIST 1.0
80
81 #define DEFAULT_USEBINARYCONSTRAINTS FALSE /**< should binary constraints be collected and applied? */
82 #define DEFAULT_ADDCLIQUE FALSE /**< add binary constraints with two variables found at the root node also as a clique? */
83 #define DEFAULT_ADDBINCONSROW 0 /**< should binary constraints be added as rows to the base LP?
84 * (0: no, 1: separate, 2: as initial rows) */
85 #define DEFAULT_USEDOMAINREDUCTION TRUE /**< Should domain reductions be collected and applied? */
86 #define DEFAULT_MERGEDOMAINREDUCTIONS FALSE /**< should domain reductions of feasible siblings should be merged? */
87 #define DEFAULT_PREFERSIMPLEBOUNDS FALSE /**< should domain reductions only be applied if there are simple bound changes? */
88 #define DEFAULT_ONLYVIOLDOMREDS FALSE /**< Should only domain reductions that violate the LP solution be applied? */
89 #define DEFAULT_MAXNVIOLATEDCONS 1 /**< How many constraints that are violated by the base lp solution
90 * should be gathered until the rule is stopped and they are added? */
91 #define DEFAULT_MAXNVIOLATEDBINCONS 0 /**< How many binary constraints that are violated by the base lp
92 * solution should be gathered until the rule is stopped and they are
93 * added? */
94 #define DEFAULT_MAXNVIOLATEDDOMREDS 1 /**< How many domain reductions that are violated by the base lp solution
95 * should be gathered until the rule is stopped and they are added? */
96 #define DEFAULT_STOREUNVIOLATEDSOL TRUE /**< If only non violating constraints are added, should the branching
97 * decision be stored till the next call? */
98 #define DEFAULT_REEVALAGE 10LL /**< Max number of LPs solved after which a previous prob branching
99 * result is recalculated. */
100 #define DEFAULT_REEVALAGEFSB 10LL /**< Max number of LPs solved after which a previous FSB scoring
101 * result is recalculated. */
102 #define DEFAULT_RECURSIONDEPTH 2 /**< The max depth of LAB. */
103 #define DEFAULT_ADDNONVIOCONS FALSE /**< Should binary constraints, that are not violated by the base LP, be
104 * collected and added? */
105 #define DEFAULT_PROPAGATE TRUE /**< Should domain propagation be executed before each temporary node is
106 * solved? */
107 #define DEFAULT_USELEVEL2DATA TRUE /**< should branching data generated at depth level 2 be stored for re-using it? */
108 #define DEFAULT_APPLYCHILDBOUNDS FALSE /**< should bounds known for child nodes be applied? */
109 #define DEFAULT_ENFORCEMAXDOMREDS FALSE /**< should the maximum number of domain reductions maxnviolateddomreds be enforced? */
110 #define DEFAULT_UPDATEBRANCHINGRESULTS FALSE /**< should branching results (and scores) be updated w.r.t. proven dual bounds? */
111 #define DEFAULT_MAXPROPROUNDS 0 /**< maximum number of propagation rounds to perform at temporary
112 * nodes (-1: unlimited, 0: SCIP default) */
113 #define DEFAULT_ABBREVIATED TRUE /**< Toggles the abbreviated LAB. */
114 #define DEFAULT_MAXNCANDS 4 /**< If abbreviated: The max number of candidates to consider at the base node */
115 #define DEFAULT_MAXNDEEPERCANDS 2 /**< If abbreviated: The max number of candidates to consider per deeper node
116 * (0: same as base node) */
117 #define DEFAULT_REUSEBASIS TRUE /**< If abbreviated: Should the information gathered to obtain the best
118 * candidates be reused? */
119 #define DEFAULT_ABBREVPSEUDO FALSE /**< If abbreviated: Use pseudo costs to estimate the score of a
120 * candidate. */
121 #define DEFAULT_LEVEL2AVGSCORE FALSE /**< should the average score be used for uninitialized scores in level 2? */
122 #define DEFAULT_LEVEL2ZEROSCORE FALSE /**< should uninitialized scores be set to 0? */
123 #define DEFAULT_SCORINGFUNCTION 'a' /**< scoring function to be used at the base level */
124 #define DEFAULT_DEEPERSCORINGFUNCTION 'x' /**< scoring function to be used at deeper levels */
125 #define DEFAULT_SCORINGSCORINGFUNCTION 'd' /**< scoring function to be used for FSB scoring */
126 #define DEFAULT_MINWEIGHT 0.8 /**< default value for the weight of the minimum in the convex combination of two
127 * child gains (taken from the paper) */
128 #define DEFAULT_WORSEFACTOR -1.0 /**< if the FSB score is of a candidate is worse than the best by this factor, skip this candidate (-1: disable) */
129 #define DEFAULT_FILTERBYMAXGAIN FALSE /**< should lookahead branching only be applied if the max gain in level 1 is not uniquely that of the best candidate? */
130
131 #ifdef SCIP_DEBUG
132 /* Adjusted debug message that also prints the current probing depth. */
133 #define LABdebugMessage(scip,lvl,...) do \
134 { \
135 SCIP_STAGE stage; \
136 SCIPverbMessage(scip, lvl, NULL, "[%s:%-4d] ", __FILE__, __LINE__); \
137 stage = SCIPgetStage(scip); \
138 if( stage == SCIP_STAGE_INIT ) \
139 { \
140 SCIPverbMessage(scip, lvl, NULL, "Init : "); \
141 } \
142 else if( stage == SCIP_STAGE_FREE ) \
143 { \
144 SCIPverbMessage(scip, lvl, NULL, "Free : "); \
145 } \
146 else if( SCIPinProbing(scip) ) \
147 { \
148 SCIPverbMessage(scip, lvl, NULL, "%*sDepth %i: ", \
149 2 * SCIPgetProbingDepth(scip), "", SCIPgetProbingDepth(scip)); \
150 } \
151 else \
152 { \
153 SCIPverbMessage(scip, lvl, NULL, "Base : "); \
154 } \
155 SCIPverbMessage(scip, lvl, NULL, __VA_ARGS__); \
156 } \
157 while( FALSE )
158
159 /* Writes a debug message without the leading information. Can be used to append something to an output of LABdebugMessage*/
160 #define LABdebugMessagePrint(scip,lvl,...) do \
161 { \
162 SCIPverbMessage(scip, lvl, NULL, __VA_ARGS__); \
163 } \
164 while( FALSE )
165 #else
166 #define LABdebugMessage(scip,lvl,...) /**/
167 /*#define LABdebugMessagePrint(scip,lvl,...) only used with SCIP_DEBUG defined */
168 #endif
169
170 /*
171 * Data structures
172 */
173
174 /** A struct holding information to speed up the solving time for solving a problem again. This is filled by the FSB
175 * scoring routine that is run to get the best candidates. It is then read by the actual ALAB routine. */
176 typedef struct
177 {
178 SCIP_LPISTATE* lpistate; /**< the basis information that may be set before another solve lp call */
179 SCIP_LPINORMS* lpinorms; /**< the norms that may be set before another solve lp call */
180 SCIP_Bool primalfeas; /**< indicates whether the solution was primal feasible */
181 SCIP_Bool dualfeas; /**< indicates whether the solution was dual feasible */
182 } WARMSTARTINFO;
183
184 /** Allocates the warm start information on the buffer and initializes it with default values. */
185 static
warmStartInfoCreate(SCIP * scip,WARMSTARTINFO ** warmstartinfo)186 SCIP_RETCODE warmStartInfoCreate(
187 SCIP* scip, /**< SCIP data structure */
188 WARMSTARTINFO** warmstartinfo /**< the warmstartinfo to allocate and initialize */
189 )
190 {
191 assert(scip != NULL);
192 assert(warmstartinfo != NULL);
193
194 SCIP_CALL( SCIPallocBlockMemory(scip, warmstartinfo) );
195
196 (*warmstartinfo)->lpistate = NULL;
197 (*warmstartinfo)->lpinorms = NULL;
198 (*warmstartinfo)->primalfeas = FALSE;
199 (*warmstartinfo)->dualfeas = FALSE;
200
201 return SCIP_OKAY;
202 }
203
204 /** checks that the warm start info can be read into the lp solver. */
205 static
warmStartInfoIsAvailable(WARMSTARTINFO * warmstartinfo)206 SCIP_Bool warmStartInfoIsAvailable(
207 WARMSTARTINFO* warmstartinfo /**< the warm start info to check (may be NULL) */
208 )
209 {
210 return warmstartinfo != NULL && warmstartinfo->lpistate != NULL;
211 }
212
213 /** Frees the allocated buffer memory of the warm start info. */
214 static
warmStartInfoFree(SCIP * scip,WARMSTARTINFO ** warmstartinfo)215 SCIP_RETCODE warmStartInfoFree(
216 SCIP* scip, /**< SCIP data structure */
217 WARMSTARTINFO** warmstartinfo /**< the warm start info to free */
218 )
219 {
220 SCIP_LPI* lpi;
221 BMS_BLKMEM* blkmem;
222
223 assert(scip != NULL);
224 assert(warmstartinfo != NULL);
225
226 SCIP_CALL( SCIPgetLPI(scip, &lpi) );
227 blkmem = SCIPblkmem(scip);
228
229 if( (*warmstartinfo)->lpistate != NULL )
230 {
231 SCIP_CALL( SCIPlpiFreeState(lpi, blkmem, &(*warmstartinfo)->lpistate) );
232 }
233
234 if( (*warmstartinfo)->lpinorms != NULL )
235 {
236 SCIP_CALL( SCIPlpiFreeNorms(lpi, blkmem, &(*warmstartinfo)->lpinorms) );
237 }
238
239 SCIPfreeBlockMemory(scip, warmstartinfo);
240
241 return SCIP_OKAY;
242 }
243
244 /** A struct containing all information needed to branch on a variable. */
245 typedef struct
246 {
247 SCIP_VAR* branchvar; /**< the variable to branch on */
248 SCIP_Real branchval; /**< the fractional value to branch on */
249 SCIP_Real fracval; /**< the fractional part of the value to branch on (val - floor(val)) */
250 WARMSTARTINFO* downwarmstartinfo; /**< the warm start info containing the lp data from a previous down branch */
251 WARMSTARTINFO* upwarmstartinfo; /**< the warm start info containing the lp data from a previous up branch */
252 } CANDIDATE;
253
254 /** Allocates the candidate on the buffer and initializes it with default values. */
255 static
candidateCreate(SCIP * scip,CANDIDATE ** candidate)256 SCIP_RETCODE candidateCreate(
257 SCIP* scip, /**< SCIP data structure */
258 CANDIDATE** candidate /**< the candidate to allocate and initialize */
259 )
260 {
261 assert(scip != NULL);
262 assert(candidate != NULL);
263
264 SCIP_CALL( SCIPallocBlockMemory(scip, candidate) );
265
266 (*candidate)->downwarmstartinfo = NULL;
267 (*candidate)->upwarmstartinfo = NULL;
268 (*candidate)->branchvar = NULL;
269
270 return SCIP_OKAY;
271 }
272
273 /** free the warm starting information for the given candidate */
274 static
candidateFreeWarmStartInfo(SCIP * scip,CANDIDATE * candidate)275 SCIP_RETCODE candidateFreeWarmStartInfo(
276 SCIP* scip, /**< SCIP data structure */
277 CANDIDATE* candidate /**< the candidate to free the warm starting information for */
278 )
279 {
280 assert(scip != NULL);
281 assert(candidate != NULL);
282
283 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "freeing warmstart info of candidate <%s>(%u/%u)...\n",
284 SCIPvarGetName(candidate->branchvar),
285 candidate->upwarmstartinfo != NULL, candidate->downwarmstartinfo != NULL);
286
287 if( candidate->upwarmstartinfo != NULL )
288 {
289 SCIP_CALL( warmStartInfoFree(scip, &candidate->upwarmstartinfo) );
290 }
291 if( candidate->downwarmstartinfo != NULL )
292 {
293 SCIP_CALL( warmStartInfoFree(scip, &candidate->downwarmstartinfo) );
294 }
295
296 return SCIP_OKAY;
297 }
298
299
300 /** Frees the allocated buffer memory of the candidate and clears the contained lpi memories. */
301 static
candidateFree(SCIP * scip,CANDIDATE ** candidate)302 SCIP_RETCODE candidateFree(
303 SCIP* scip, /**< SCIP data structure */
304 CANDIDATE** candidate /**< the candidate to free */
305 )
306 {
307 assert(scip != NULL);
308 assert(candidate != NULL);
309
310 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "freeing candidate <%s>(%u/%u)...\n",
311 (*candidate) != NULL ? SCIPvarGetName((*candidate)->branchvar) : "none",
312 (*candidate)->upwarmstartinfo != NULL, (*candidate)->downwarmstartinfo != NULL);
313
314 /* if a candidate is freed, we no longer need the content of the warm start info */
315 SCIP_CALL( candidateFreeWarmStartInfo(scip, *candidate) );
316
317 SCIPfreeBlockMemory(scip, candidate);
318 return SCIP_OKAY;
319 }
320
321 /** Store the current lp solution in the warm start info for further usage. */
322 static
candidateStoreWarmStartInfo(SCIP * scip,CANDIDATE * candidate,SCIP_Bool down)323 SCIP_RETCODE candidateStoreWarmStartInfo(
324 SCIP* scip, /**< SCIP data structure */
325 CANDIDATE* candidate, /**< the branching candidate */
326 SCIP_Bool down /**< is the info for down branching? */
327 )
328 {
329 SCIP_LPI* lpi;
330 BMS_BLKMEM* blkmem;
331 WARMSTARTINFO* warmstartinfo;
332
333 assert(scip != NULL);
334 assert(candidate != NULL);
335
336 SCIP_CALL( SCIPgetLPI(scip, &lpi) );
337 blkmem = SCIPblkmem(scip);
338
339 if( down )
340 {
341 if( candidate->downwarmstartinfo == NULL )
342 {
343 SCIP_CALL( warmStartInfoCreate(scip, &candidate->downwarmstartinfo) );
344 }
345 warmstartinfo = candidate->downwarmstartinfo;
346 }
347 else
348 {
349 if( candidate->upwarmstartinfo == NULL )
350 {
351 SCIP_CALL( warmStartInfoCreate(scip, &candidate->upwarmstartinfo) );
352 }
353 warmstartinfo = candidate->upwarmstartinfo;
354 }
355
356 SCIP_CALL( SCIPlpiGetState(lpi, blkmem, &warmstartinfo->lpistate) );
357
358 SCIP_CALL( SCIPlpiGetNorms(lpi, blkmem, &warmstartinfo->lpinorms) );
359
360 warmstartinfo->primalfeas = SCIPlpiIsPrimalFeasible(lpi);
361 warmstartinfo->dualfeas = SCIPlpiIsDualFeasible(lpi);
362
363 assert(warmstartinfo->lpistate != NULL);
364 /* warmstartinfo->lpinorms may be NULL */
365
366 return SCIP_OKAY;
367 }
368
369 /** returns whether the candidate has stored warm starting information for the given direction */
370 static
candidateHasWarmStartInfo(CANDIDATE * candidate,SCIP_Bool down)371 SCIP_Bool candidateHasWarmStartInfo(
372 CANDIDATE* candidate, /**< the branching candidate */
373 SCIP_Bool down /**< is the info for down branching? */
374 )
375 {
376 assert(candidate != NULL);
377
378 return warmStartInfoIsAvailable(down ? candidate->downwarmstartinfo : candidate->upwarmstartinfo);
379 }
380
381
382 /** loads the warm starting information of the candidate for the given direction */
383 static
candidateLoadWarmStartInfo(SCIP * scip,CANDIDATE * candidate,SCIP_Bool down)384 SCIP_RETCODE candidateLoadWarmStartInfo(
385 SCIP* scip, /**< SCIP data structure */
386 CANDIDATE* candidate, /**< the branching candidate */
387 SCIP_Bool down /**< is the info for down branching? */
388 )
389 {
390 WARMSTARTINFO* warmstartinfo;
391
392 assert(scip != NULL);
393 assert(candidate != NULL);
394
395 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "loading basis...\n");
396
397 if( down )
398 warmstartinfo = candidate->downwarmstartinfo;
399 else
400 warmstartinfo = candidate->upwarmstartinfo;
401
402 /* As we solved the very same LP some time earlier and stored the state (the basis) and the norms, we can now set those in
403 * the LP solver, such that the solution does not (in best case) need any further calculation.
404 * Some iterations may occur, as the conflict analysis may have added some constraints in the meantime. */
405 SCIP_CALL( SCIPsetProbingLPState(scip, &(warmstartinfo->lpistate), &(warmstartinfo->lpinorms), warmstartinfo->primalfeas,
406 warmstartinfo->dualfeas) );
407
408 /* The state and norms will be freed later by the SCIP framework. Therefore they are set to NULL to enforce that we won't
409 * free them on our own. */
410 assert(warmstartinfo->lpistate == NULL);
411 assert(warmstartinfo->lpinorms == NULL);
412
413 return SCIP_OKAY;
414 }
415
416
417 /** Holds the information needed for branching on a variable. */
418 typedef struct
419 {
420 SCIP_VAR* branchvar; /**< the variable to branch on, may be NULL */
421 SCIP_Real branchval; /**< the fractional value to branch on */
422 SCIP_Real* downlowerbounds; /**< variable lower bounds for down child */
423 SCIP_Real* downupperbounds; /**< variable upper bounds for down child */
424 SCIP_Real* uplowerbounds; /**< variable lower bounds for up child */
425 SCIP_Real* upupperbounds; /**< variable upper bounds for up child */
426 SCIP_Real downdb; /**< dual bound for down branch */
427 SCIP_Real updb; /**< dual bound for the up branch */
428 SCIP_Real proveddb; /**< proven dual bound for the current node */
429 SCIP_Real score; /**< score of the branching decision */
430 SCIP_Bool downdbvalid; /**< Indicator for the validity of the downdb value. Is FALSE, if no actual
431 * branching occurred or the value was determined by an LP not solved to
432 * optimality. */
433 SCIP_Bool updbvalid; /**< Indicator for the validity of the updb value. Is FALSE, if no actual
434 * branching occurred or the value was determined by an LP not solved to
435 * optimality. */
436 SCIP_Bool boundsvalid; /**< are variable bounds for down and up child valid? */
437 int boundssize; /**< size of bounds arrays */
438 } BRANCHINGDECISION;
439
440 /** initialize a branching decsion with default values */
441 static
branchingDecisionInit(SCIP * scip,BRANCHINGDECISION * decision)442 void branchingDecisionInit(
443 SCIP* scip, /**< SCIP data structure */
444 BRANCHINGDECISION* decision /**< the decision to initialize */
445 )
446 {
447 assert(scip != NULL);
448 assert(decision != NULL);
449
450 decision->branchvar = NULL;
451 decision->branchval = SCIP_INVALID;
452 decision->downlowerbounds = NULL;
453 decision->downupperbounds = NULL;
454 decision->uplowerbounds = NULL;
455 decision->upupperbounds = NULL;
456 decision->downdb = -SCIPinfinity(scip);
457 decision->downdbvalid = FALSE;
458 decision->updb = -SCIPinfinity(scip);
459 decision->updbvalid = FALSE;
460 decision->boundsvalid = FALSE;
461 decision->proveddb = -SCIPinfinity(scip);
462 decision->score = -SCIPinfinity(scip);
463 decision->boundssize = 0;
464 }
465
466
467 /** allocates a branching decision in the buffer and initializes it with default values. */
468 static
branchingDecisionCreate(SCIP * scip,BRANCHINGDECISION ** decision)469 SCIP_RETCODE branchingDecisionCreate(
470 SCIP* scip, /**< SCIP data structure */
471 BRANCHINGDECISION** decision /**< pointer to the decision to allocate and initialize */
472 )
473 {
474 assert(scip != NULL);
475 assert(decision != NULL);
476
477 SCIP_CALL( SCIPallocBuffer(scip, decision) );
478
479 branchingDecisionInit(scip, *decision);
480
481 return SCIP_OKAY;
482 }
483
484 /** copies the data from the source branching decision storage to the target storage;
485 * this is used to store the most important information (i.e., the dual bounds obtained) so that it can be used in a
486 * subsequent call in case the LP solution did not change because we only added bound changes that did not forbid the
487 * current LP solution;
488 * however, we do not want to store all the domain changes for the two potential child nodes for this rare case, they
489 * will be identified when processing the child nodes anyway
490 */
491 static
branchingDecisionCopy(BRANCHINGDECISION * sourcedecision,BRANCHINGDECISION * targetdecision)492 void branchingDecisionCopy(
493 BRANCHINGDECISION* sourcedecision, /**< the source branching decision */
494 BRANCHINGDECISION* targetdecision /**< the target branching decision */
495 )
496 {
497 assert(sourcedecision != NULL);
498 assert(targetdecision != NULL);
499
500 targetdecision->branchvar = sourcedecision->branchvar;
501 targetdecision->branchval = sourcedecision->branchval;
502 targetdecision->downdb = sourcedecision->downdb;
503 targetdecision->downdbvalid = sourcedecision->downdbvalid;
504 targetdecision->updb = sourcedecision->updb;
505 targetdecision->updbvalid = sourcedecision->updbvalid;
506 targetdecision->proveddb = sourcedecision->proveddb;
507 targetdecision->score = sourcedecision->score;
508
509 assert(targetdecision->downlowerbounds == NULL);
510 assert(targetdecision->downupperbounds == NULL);
511 assert(targetdecision->uplowerbounds == NULL);
512 assert(targetdecision->upupperbounds == NULL);
513 assert(targetdecision->boundsvalid == FALSE);
514 assert(targetdecision->boundssize == 0);
515 }
516
517 /** Checks whether the given branching decision can be used to branch on. */
518 static
branchingDecisionIsValid(BRANCHINGDECISION * decision)519 SCIP_Bool branchingDecisionIsValid(
520 BRANCHINGDECISION* decision /**< the branching decision to check */
521 )
522 {
523 assert(decision != NULL);
524
525 /* a branching decision is deemed valid, if the var pointer is not on the default NULL value (see the allocate method) */
526 return decision->branchvar != NULL;
527 }
528
529 /* ensure that the array that stores the bounds for both child nodes is large enough */
530 static
branchingDecisionEnsureBoundArraysSize(SCIP * scip,BRANCHINGDECISION * decision,int nvars)531 SCIP_RETCODE branchingDecisionEnsureBoundArraysSize(
532 SCIP* scip, /**< SCIP data structure */
533 BRANCHINGDECISION* decision, /**< branching decision */
534 int nvars /**< number of problem variables */
535 )
536 {
537 assert(decision != NULL);
538
539 if( decision->boundssize == 0 )
540 {
541 decision->boundssize = nvars;
542 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &decision->downlowerbounds, decision->boundssize) );
543 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &decision->downupperbounds, decision->boundssize) );
544 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &decision->uplowerbounds, decision->boundssize) );
545 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &decision->upupperbounds, decision->boundssize) );
546 }
547 assert(decision->boundssize == nvars);
548
549 return SCIP_OKAY;
550 }
551
552 /** Frees the allocated memory of the branching decision. */
553 static
branchingDecisionFree(SCIP * scip,BRANCHINGDECISION ** decision)554 void branchingDecisionFree(
555 SCIP* scip, /**< SCIP data structure */
556 BRANCHINGDECISION** decision /**< pointer to the decision to be freed */
557 )
558 {
559 assert(scip != NULL);
560 assert(decision != NULL);
561
562 if( (*decision)->boundssize != 0 )
563 {
564 assert((*decision)->downlowerbounds != NULL);
565 assert((*decision)->downupperbounds != NULL);
566 assert((*decision)->uplowerbounds != NULL);
567 assert((*decision)->upupperbounds != NULL);
568
569 SCIPfreeBlockMemoryArray(scip, &(*decision)->downlowerbounds, (*decision)->boundssize);
570 SCIPfreeBlockMemoryArray(scip, &(*decision)->downupperbounds, (*decision)->boundssize);
571 SCIPfreeBlockMemoryArray(scip, &(*decision)->uplowerbounds, (*decision)->boundssize);
572 SCIPfreeBlockMemoryArray(scip, &(*decision)->upupperbounds, (*decision)->boundssize);
573 }
574
575 SCIPfreeBuffer(scip, decision);
576 }
577
578 /** A container to hold the result of a branching. */
579 typedef struct
580 {
581 SCIP_Real objval; /**< The objective value of the solved lp. Only contains meaningful data, if
582 * cutoff == FALSE. */
583 SCIP_Real dualbound; /**< The best dual bound for this branching, may be changed by deeper level
584 * branchings. */
585 SCIP_Longint niterations; /**< The number of probing iterations needed in sub branch. */
586 SCIP_Bool cutoff; /**< Indicates whether the node was infeasible and was cutoff. */
587 SCIP_Bool dualboundvalid; /**< Is the value of the dual bound valid? That means, was the according LP
588 * or the sub problems solved to optimality? */
589 int ndeepestcutoffs; /**< number of cutoffs on the lowest level below this child */
590 SCIP_Real deeperscore; /**< best score computed for the deeper lookahead level */
591 SCIP_Real bestgain; /**< best gain (w.r.t. to the base lp) on the lowest level below this child */
592 SCIP_Real totalgains; /**< sum over all gains that are valid in both children */
593 int ntotalgains; /**< number of gains summed in totalgains */
594 int ndeepestnodes; /**< number of nodes processed in the deepest level */
595 } BRANCHINGRESULTDATA;
596
597 /** Allocates a branching result in the buffer. */
598 static
branchingResultDataCreate(SCIP * scip,BRANCHINGRESULTDATA ** resultdata)599 SCIP_RETCODE branchingResultDataCreate(
600 SCIP* scip, /**< SCIP data structure */
601 BRANCHINGRESULTDATA** resultdata /**< pointer to the result to be allocated */
602 )
603 {
604 assert(scip != NULL);
605 assert(resultdata != NULL);
606
607 SCIP_CALL( SCIPallocBuffer(scip, resultdata) );
608
609 return SCIP_OKAY;
610 }
611
612 /** Initiates the branching result with default values. */
613 static
branchingResultDataInit(SCIP * scip,BRANCHINGRESULTDATA * resultdata)614 void branchingResultDataInit(
615 SCIP* scip, /**< SCIP data structure */
616 BRANCHINGRESULTDATA* resultdata /**< pointer to the result to be initialized */
617 )
618 {
619 assert(scip != NULL);
620 assert(resultdata != NULL);
621
622 resultdata->objval = -SCIPinfinity(scip);
623 resultdata->dualbound = -SCIPinfinity(scip);
624 resultdata->cutoff = FALSE;
625 resultdata->dualboundvalid = FALSE;
626 resultdata->niterations = 0;
627 resultdata->ndeepestcutoffs = 0;
628 resultdata->deeperscore = -SCIPinfinity(scip);
629 resultdata->bestgain = 0.;
630 resultdata->totalgains = 0.;
631 resultdata->ntotalgains = 0;
632 resultdata->ndeepestnodes = 0;
633 }
634
635 /** Copies the data from the source to the target. */
636 static
branchingResultDataCopy(BRANCHINGRESULTDATA * sourcedata,BRANCHINGRESULTDATA * targetdata)637 void branchingResultDataCopy(
638 BRANCHINGRESULTDATA* sourcedata, /**< the source branching result */
639 BRANCHINGRESULTDATA* targetdata /**< the target branching result */
640 )
641 {
642 assert(sourcedata != NULL);
643 assert(targetdata != NULL);
644
645 targetdata->cutoff = sourcedata->cutoff;
646 targetdata->objval = sourcedata->objval;
647 targetdata->dualbound = sourcedata->dualbound;
648 targetdata->dualboundvalid = sourcedata->dualboundvalid;
649 targetdata->niterations = sourcedata->niterations;
650 targetdata->ndeepestcutoffs = sourcedata->ndeepestcutoffs;
651 targetdata->deeperscore = sourcedata->deeperscore;
652 targetdata->bestgain = sourcedata->bestgain;
653 targetdata->totalgains = sourcedata->totalgains;
654 targetdata->ntotalgains = sourcedata->ntotalgains;
655 targetdata->ndeepestnodes = sourcedata->ndeepestnodes;
656 }
657
658 /** Frees the allocated buffer memory of the branching result. */
659 static
branchingResultDataFree(SCIP * scip,BRANCHINGRESULTDATA ** resultdata)660 void branchingResultDataFree(
661 SCIP* scip, /**< SCIP data structure */
662 BRANCHINGRESULTDATA** resultdata /**< pointer to the result to be freed */
663 )
664 {
665 assert(scip != NULL);
666 assert(resultdata != NULL);
667
668 SCIPfreeBuffer(scip, resultdata);
669 }
670
671 /** a container to hold the result of a second-level LP */
672 typedef struct
673 {
674 SCIP_Real lpobjval; /**< the objective value of the solved lp; only contains meaningful data, if
675 * cutoff == FALSE. */
676 SCIP_Real branchval1; /**< new bound for first branching variable */
677 SCIP_Real branchval2; /**< new bound for second branching variable */
678 unsigned int branchvar1:30; /**< problem index of first branching variable */
679 unsigned int branchvar2:30; /**< problem index of second branching variable */
680 unsigned int branchdir1:1; /**< branching direction for first branching variable (0:down, 1:up) */
681 unsigned int branchdir2:1; /**< branching direction for second branching variable (0:down, 1:up) */
682 unsigned int cutoff:1; /**< indicates whether the node was infeasible and was cut off. */
683 unsigned int valid:1; /**< is the lpobjval a valid dual bound? */
684 } LEVEL2RESULT;
685
686 /** a container to hold the results of all second-level LPs */
687 typedef struct
688 {
689 LEVEL2RESULT** level2results; /**< array with all level2 results */
690 SCIP_Real branchval1; /**< new bound for first branching variable */
691 SCIP_Real branchval2; /**< new bound for second branching variable */
692 int nlevel2results; /**< number of level2 results stored */
693 int level2resultssize; /**< size of level2results array */
694 unsigned int branchvar1:30; /**< problem index of first branching variable */
695 unsigned int branchvar2:30; /**< problem index of second branching variable */
696 unsigned int branchdir1:1; /**< branching direction for first branching variable (0:down, 1:up) */
697 unsigned int branchdir2:1; /**< branching direction for second branching variable (0:down, 1:up) */
698 } LEVEL2DATA;
699
700 /** allocates a double branching result in the memory and fills it with the information stored in the level 2 data */
701 static
level2resultCreateFromData(SCIP * scip,LEVEL2DATA * data,LEVEL2RESULT ** result)702 SCIP_RETCODE level2resultCreateFromData(
703 SCIP* scip, /**< SCIP data structure */
704 LEVEL2DATA* data, /**< level2 data */
705 LEVEL2RESULT** result /**< pointer to the result to be allocated */
706 )
707 {
708 assert(scip != NULL);
709 assert(data != NULL);
710 assert(result != NULL);
711
712 SCIP_CALL( SCIPallocBlockMemory(scip, result) );
713
714 if( data->branchvar1 < data->branchvar2 )
715 {
716 (*result)->branchval1 = data->branchval1;
717 (*result)->branchval2 = data->branchval2;
718 (*result)->branchvar1 = data->branchvar1; /*lint !e732*/
719 (*result)->branchvar2 = data->branchvar2; /*lint !e732*/
720 (*result)->branchdir1 = data->branchdir1;
721 (*result)->branchdir2 = data->branchdir2;
722 }
723 else
724 {
725 (*result)->branchval1 = data->branchval2;
726 (*result)->branchval2 = data->branchval1;
727 (*result)->branchvar1 = data->branchvar2; /*lint !e732*/
728 (*result)->branchvar2 = data->branchvar1; /*lint !e732*/
729 (*result)->branchdir1 = data->branchdir2;
730 (*result)->branchdir2 = data->branchdir1;
731 }
732
733 return SCIP_OKAY;
734 }
735
736
737 #ifdef SCIP_DEBUG
738 /** prints the double branching result */
739 static
level2resultPrint(SCIP * scip,LEVEL2RESULT * result)740 void level2resultPrint(
741 SCIP* scip, /**< SCIP data structure */
742 LEVEL2RESULT* result /**< pointer to the result to be initialized */
743 )
744 {
745 SCIP_VAR** vars;
746
747 assert(result != NULL);
748
749 vars = SCIPgetVars(scip);
750
751 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH,
752 "level 2 result: <%s> %s %g + <%s> %s %g: lpval: %.9g, inf: %d, valid: %d\n",
753 SCIPvarGetName(vars[result->branchvar1]), result->branchdir1 ? ">=" : "<=", result->branchval1,
754 SCIPvarGetName(vars[result->branchvar2]), result->branchdir2 ? ">=" : "<=", result->branchval2,
755 result->lpobjval, result->cutoff, result->valid);
756 }
757 #else
758 #define level2resultPrint(scip,result) /**/
759 #endif
760
761 /** frees the allocated memory of the double branching result */
762 static
level2resultFree(SCIP * scip,LEVEL2RESULT ** result)763 void level2resultFree(
764 SCIP* scip, /**< SCIP data structure */
765 LEVEL2RESULT** result /**< pointer to the result to be freed */
766 )
767 {
768 assert(scip != NULL);
769 assert(result != NULL);
770
771 SCIPfreeBlockMemory(scip, result);
772 }
773
774 /** returns TRUE iff both level 2 results are equal; two branchings are equal if they branched on the same variables
775 * with the same values
776 */
777 static
level2resultEqual(LEVEL2RESULT * result1,LEVEL2RESULT * result2)778 SCIP_Bool level2resultEqual(
779 LEVEL2RESULT* result1, /**< first level 2 result */
780 LEVEL2RESULT* result2 /**< second level 2 result */
781 )
782 {
783 assert(result1->branchvar1 < result1->branchvar2);
784 assert(result2->branchvar1 < result2->branchvar2);
785
786 /* check all cases */
787 if( result1->branchvar1 != result2->branchvar1
788 || result1->branchvar2 != result2->branchvar2
789 || result1->branchdir1 != result2->branchdir1
790 || result1->branchdir2 != result2->branchdir2
791 || result1->branchval1 > result2->branchval1 + 0.5
792 || result1->branchval1 < result2->branchval1 - 0.5
793 || result1->branchval2 > result2->branchval2 + 0.5
794 || result1->branchval2 < result2->branchval2 - 0.5)
795 return FALSE;
796
797 return TRUE;
798 }
799
800 /** allocates the level2 data */
801 static
level2dataCreate(SCIP * scip,LEVEL2DATA ** data)802 SCIP_RETCODE level2dataCreate(
803 SCIP* scip, /**< SCIP data structure */
804 LEVEL2DATA** data /**< pointer to the data to be allocated */
805 )
806 {
807 assert(scip != NULL);
808 assert(data != NULL);
809
810 SCIP_CALL( SCIPallocBlockMemory(scip, data) );
811
812 (*data)->level2results = NULL;
813 (*data)->branchval1 = -SCIPinfinity(scip);
814 (*data)->branchval2 = -SCIPinfinity(scip);
815 (*data)->nlevel2results = 0;
816 (*data)->level2resultssize = 0;
817 (*data)->branchvar1 = 0;
818 (*data)->branchvar2 = 0;
819 (*data)->branchdir1 = 0;
820 (*data)->branchdir2 = 0;
821
822 return SCIP_OKAY;
823 }
824
825 /** frees the allocated memory of the level2 data */
826 static
level2dataFree(SCIP * scip,LEVEL2DATA ** data)827 void level2dataFree(
828 SCIP* scip, /**< SCIP data structure */
829 LEVEL2DATA** data /**< pointer to the data to be freed */
830 )
831 {
832 assert(scip != NULL);
833 assert(data != NULL);
834
835 while( (*data)->nlevel2results > 0 )
836 {
837 --(*data)->nlevel2results;
838 level2resultFree(scip, &((*data)->level2results[(*data)->nlevel2results]));
839 }
840 assert((*data)->nlevel2results == 0);
841
842 if( (*data)->level2results != NULL )
843 {
844 SCIPfreeBlockMemoryArray(scip, &(*data)->level2results, (*data)->level2resultssize);
845 }
846
847 SCIPfreeBlockMemory(scip, data);
848 }
849
850 /** ensures that level2results can store at least one more element */
851 static
level2dataEnsureSize(SCIP * scip,LEVEL2DATA * data)852 SCIP_RETCODE level2dataEnsureSize(
853 SCIP* scip, /**< SCIP data structure */
854 LEVEL2DATA* data /**< level2 data */
855 )
856 {
857 assert(scip != NULL);
858 assert(data != NULL);
859
860 if( data->nlevel2results >= data->level2resultssize )
861 {
862 int newsize = SCIPcalcMemGrowSize(scip, data->level2resultssize + 1);
863
864 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &data->level2results, data->level2resultssize, newsize) );
865 data->level2resultssize = newsize;
866 }
867
868 return SCIP_OKAY;
869 }
870
871 /** get a result from the level 2 data */
872 static
level2dataGetResult(SCIP * scip,LEVEL2DATA * data,LEVEL2RESULT ** result)873 SCIP_RETCODE level2dataGetResult(
874 SCIP* scip, /**< SCIP data structure */
875 LEVEL2DATA* data, /**< level2 data */
876 LEVEL2RESULT** result /**< pointer to store result */
877 )
878 {
879 LEVEL2RESULT* tmpresult;
880 int i;
881
882 assert(data != NULL);
883 assert(result != NULL);
884
885 *result = NULL;
886
887 /* we branched twice on the same variable; the result cannot be stored already */
888 if( data->branchvar1 == data->branchvar2 )
889 {
890 assert(SCIPvarGetType(SCIPgetVars(scip)[data->branchvar1]) != SCIP_VARTYPE_BINARY);
891 return SCIP_OKAY;
892 }
893
894 SCIP_CALL( level2resultCreateFromData(scip, data, &tmpresult) );
895
896 /* search for a level 2 result with the same branching decisions */
897 for( i = 0; i < data->nlevel2results; ++i )
898 {
899 if( level2resultEqual(data->level2results[i], tmpresult) )
900 {
901 *result = data->level2results[i];
902 }
903 }
904
905 level2resultFree(scip, &tmpresult);
906
907 return SCIP_OKAY;
908 }
909
910
911 /** store a new result in the level 2 data */
912 static
level2dataStoreResult(SCIP * scip,LEVEL2DATA * data,SCIP_Real lpobjval,SCIP_Bool cutoff,SCIP_Bool valid,SCIP_Bool * duplicate)913 SCIP_RETCODE level2dataStoreResult(
914 SCIP* scip, /**< SCIP data structure */
915 LEVEL2DATA* data, /**< level2 data */
916 SCIP_Real lpobjval, /**< LP objective value */
917 SCIP_Bool cutoff, /**< was the LP infeasible? */
918 SCIP_Bool valid, /**< is the LP value a valid dual bound? */
919 SCIP_Bool* duplicate /**< pointer to store whether information for the same branching decisions was already stored */
920 )
921 {
922 LEVEL2RESULT* result;
923 int i;
924
925 assert(scip != NULL);
926 assert(data != NULL);
927 assert(duplicate != NULL);
928
929 *duplicate = FALSE;
930
931 /* we branched twice on the same variable; the result cannot be re-used lated */
932 if( data->branchvar1 == data->branchvar2 )
933 {
934 assert(SCIPvarGetType(SCIPgetVars(scip)[data->branchvar1]) != SCIP_VARTYPE_BINARY);
935 return SCIP_OKAY;
936 }
937
938 SCIP_CALL( level2dataEnsureSize(scip, data) );
939
940 SCIP_CALL( level2resultCreateFromData(scip, data, &result) );
941
942 result->lpobjval = lpobjval;
943 result->cutoff = cutoff;
944 result->valid = valid;
945
946 /* search for a level 2 result with the same branching decisions*/
947 for( i = 0; i < data->nlevel2results; ++i )
948 {
949 if( level2resultEqual( data->level2results[i], result) )
950 {
951 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "same level2 node already processed:\n");
952 level2resultPrint(scip, data->level2results[i]);
953 level2resultPrint(scip, result);
954 *duplicate = TRUE;
955 }
956 }
957
958 data->level2results[data->nlevel2results] = result;
959 ++data->nlevel2results;
960 assert(data->nlevel2results <= data->level2resultssize);
961
962 return SCIP_OKAY;
963 }
964
965
966 /** The data that is preserved over multiple runs of the branching rule. */
967 typedef struct
968 {
969 BRANCHINGDECISION* olddecision; /**< The previous decision that gets used for the case that in the previous run
970 * only non-violating implied binary constraints were added.*/
971 SCIP_Longint oldnnodelpiterations; /**< node LP iterations when previous branching decision was stored */
972 SCIP_Longint oldnnodelps; /**< node LPs when previous branching decision was stored */
973 SCIP_Longint oldntotalnodes; /**< node at which previous branching decision was stored */
974 SCIP_Longint* lastbranchid; /**< The node id at which the var was last branched on (for a given branching
975 * var). */
976 SCIP_Longint* lastbranchnlps; /**< The number of (non-probing) LPs that where solved when the var was last
977 * branched on. */
978 SCIP_Real* lastbranchlpobjval; /**< The lp objval at which var was last branched on. */
979 BRANCHINGRESULTDATA** lastbranchupres; /**< The result of the last up branching for a given var. */
980 BRANCHINGRESULTDATA** lastbranchdownres; /**< The result of the last down branching for a given var. */
981 int restartindex; /**< The index at which the iteration over the number of candidates starts. */
982 int nvars; /**< The number of variables that can be stored in the arrays. */
983 } PERSISTENTDATA;
984
985 /** The parameter that can be changed by the user/caller and alter the behaviour of the lookahead branching. */
986 typedef struct
987 {
988 SCIP_Longint reevalage; /**< The number of "normal" (not probing) lps that may have been solved before
989 * we stop using old data and start recalculating new first level data. */
990 SCIP_Longint reevalagefsb; /**< The number of "normal" (not probing) lps that may have been solved before
991 * we stop using old FSB data and start recalculating new first level data. */
992 int maxnviolatedcons; /**< The number of constraints (domain reductions and binary constraints) we
993 * want to gather before restarting the run. Set to -1 for an unbounded
994 * number of constraints. */
995 int maxnviolatedbincons;/**< The number of binary constraints we want to gather before restarting the
996 * run. Set to -1 for an undbounded number of binary constraints. */
997 int maxnviolateddomreds;/**< The number of domain reductions we want to gather before restarting the
998 * run. Set to -1 for an undbounded number of domain reductions. */
999 int recursiondepth; /**< How deep should the recursion go? Default for Lookahead: 2 */
1000 int maxncands; /**< If abbreviated == TRUE, at most how many candidates should be handled at the base node? */
1001 int maxndeepercands; /**< If abbreviated == TRUE, at most how many candidates should be handled in deeper nodes? */
1002 SCIP_Bool usedomainreduction; /**< indicates whether the data for domain reductions should be gathered and
1003 * used. */
1004 SCIP_Bool mergedomainreductions; /**< should domain reductions of feasible siblings should be merged? */
1005 SCIP_Bool prefersimplebounds; /**< should domain reductions only be applied if there are simple bound changes? */
1006 SCIP_Bool onlyvioldomreds; /**< Should only domain reductions that violate the LP solution be applied? */
1007 SCIP_Bool usebincons; /**< indicates whether the data for the implied binary constraints should
1008 * be gathered and used */
1009 int addbinconsrow; /**< should binary constraints be added as rows to the base LP?
1010 * (0: no, 1: separate, 2: as initial rows) */
1011 SCIP_Bool addnonviocons; /**< Should constraints be added, that are not violated by the base LP? */
1012 SCIP_Bool abbreviated; /**< Should the abbreviated version be used? */
1013 SCIP_Bool reusebasis; /**< If abbreviated == TRUE, should the solution lp-basis of the FSB run be
1014 * used in the first abbreviated level? */
1015 SCIP_Bool storeunviolatedsol; /**< Should a solution/decision be stored, to speed up the next iteration
1016 * after adding the constraints/domreds? */
1017 SCIP_Bool abbrevpseudo; /**< If abbreviated == TRUE, should pseudocost values be used, to approximate
1018 * the scoring? */
1019 SCIP_Bool level2avgscore; /**< should the average score be used for uninitialized scores in level 2? */
1020 SCIP_Bool level2zeroscore; /**< should uninitialized scores in level 2 be set to zero? */
1021 SCIP_Bool addclique; /**< add binary constraints with two variables found at the root node also as a clique? */
1022 SCIP_Bool propagate; /**< Should the problem be propagated before solving each inner node? */
1023 SCIP_Bool uselevel2data; /**< should branching data generated at depth level 2 be stored for re-using it? */
1024 SCIP_Bool applychildbounds; /**< should bounds known for child nodes be applied? */
1025 SCIP_Bool enforcemaxdomreds; /**< should the maximum number of domain reductions maxnviolateddomreds be enforced? */
1026 SCIP_Bool updatebranchingresults; /**< should branching results (and scores) be updated w.r.t. proven dual bounds? */
1027 SCIP_Bool inscoring; /**< are we currently in FSB-scoring (only used internally) */
1028 int maxproprounds; /**< maximum number of propagation rounds to perform at temporary nodes
1029 * (-1: unlimited, 0: SCIP default) */
1030 char scoringfunction; /**< scoring function at base level */
1031 char deeperscoringfunction; /**< scoring function at deeper levels */
1032 char scoringscoringfunction;/**< scoring function for FSB scoring */
1033 SCIP_Real minweight; /**< weight of the min gain of two child problems */
1034 SCIP_Real worsefactor; /**< if the FSB score is of a candidate is worse than the best by this factor, skip this candidate (-1: disable) */
1035 SCIP_Bool filterbymaxgain; /**< should lookahead branching only be applied if the max gain in level 1 is not uniquely that of the best candidate? */
1036 } CONFIGURATION;
1037
1038
1039 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
1040 #define MAXRESULT SCIP_DELAYNODE
1041
1042 /** returns a human readable name for the given result enum value */
1043 static
getStatusString(SCIP_RESULT result)1044 const char* getStatusString(
1045 SCIP_RESULT result /**< enum value to get the string representation for */
1046 )
1047 {
1048 assert(result >= 1);
1049 assert(result <= 18);
1050
1051 switch( result )
1052 {
1053 case SCIP_DIDNOTRUN:
1054 return "SCIP_DIDNOTRUN";
1055 case SCIP_DELAYED:
1056 return "SCIP_DELAYED";
1057 case SCIP_DIDNOTFIND:
1058 return "SCIP_DIDNOTFIND";
1059 case SCIP_FEASIBLE:
1060 return "SCIP_FEASIBLE";
1061 case SCIP_INFEASIBLE:
1062 return "SCIP_INFEASIBLE";
1063 case SCIP_UNBOUNDED:
1064 return "SCIP_UNBOUNDED";
1065 case SCIP_CUTOFF:
1066 return "SCIP_CUTOFF";
1067 case SCIP_SEPARATED:
1068 return "SCIP_SEPARATED";
1069 case SCIP_NEWROUND:
1070 return "SCIP_NEWROUND";
1071 case SCIP_REDUCEDDOM:
1072 return "SCIP_REDUCEDDOM";
1073 case SCIP_CONSADDED:
1074 return "SCIP_CONSADDED";
1075 case SCIP_CONSCHANGED:
1076 return "SCIP_CONSCHANGED";
1077 case SCIP_BRANCHED:
1078 return "SCIP_BRANCHED";
1079 case SCIP_SOLVELP:
1080 return "SCIP_SOLVELP";
1081 case SCIP_FOUNDSOL:
1082 return "SCIP_FOUNDSOL";
1083 case SCIP_SUSPENDED:
1084 return "SCIP_SUSPENDED";
1085 case SCIP_SUCCESS:
1086 return "SCIP_SUCCESS";
1087 case SCIP_DELAYNODE:
1088 return "SCIP_DELAYNODE";
1089 default:
1090 SCIPerrorMessage("result code %d not treated in lookahead branching rule\n", result);
1091 SCIPABORT();
1092 return "UNKNOWN";
1093 }
1094 }
1095 #endif
1096
1097 #ifdef SCIP_STATISTIC
1098 /** The data used for some statistical analysis. */
1099 typedef struct
1100 {
1101 int* nresults; /**< Array of counters for each result state the lookahead branching finished.
1102 * The first (0) entry is unused, as the result states are indexed 1-based
1103 * and we use this index as our array index. */
1104 int* nsinglecutoffs; /**< The number of single cutoffs on a (probing) node per probingdepth. */
1105 int* nfullcutoffs; /**< The number of double cutoffs on a (probing) node per probingdepth. */
1106 int* nlpssolved; /**< The number of all lps solved for a given probingdepth (incl. FSB). */
1107 int* nlpssolvedfsb; /**< The number of lps solved by the initial FSB to get the FSB scores. */
1108 int* nduplicatelps; /**< The number of lps solved for duplicate grand-child nodes. */
1109 SCIP_Longint* nlpiterations; /**< The number of all lp iterations needed for a given probingdepth
1110 * (incl. FSB). */
1111 SCIP_Longint* nlpiterationsfsb; /**< The number of lp iterations needed to get the FSB scores. */
1112 int* npropdomred; /**< The number of domain reductions based on domain propagation per
1113 * progingdepth. */
1114 int* noldbranchused; /**< The number of times old branching data is used (see the reevalage
1115 * parameter in the CONFIGURATION struct) */
1116 int* noldbranchusedfsb; /**< The number of times old FSB scoring data is used (see the reevalagefsb
1117 * parameter in the CONFIGURATION struct) */
1118 int* chosenfsbcand; /**< If abbreviated, this is the number of times each candidate was finally
1119 * chosen by the following LAB */
1120 int* stopafterfsb; /**< If abbreviated, this is the number of times the rule was stopped after
1121 * scoring candidates by FSB, e.g., by adding constraints or domreds. */
1122 int* cutoffafterfsb; /**< If abbreviated, this is the number of times the rule was stopped after
1123 * scoring candidates by FSB because of a found cutoff. */
1124 int* domredafterfsb; /**< If abbreviated, this is the number of times the rule was stopped after
1125 * scoring candidates by FSB because of a found domain reduction. */
1126 int nsinglecandidate; /**< number of times a single candidate was given to the recursion routine */
1127 int nsingleafterfilter; /**< number of times a single candidate remained after filtering */
1128 int noldcandidate; /**< number of times the old candidate from last call with nonviolating
1129 * reductions was branched on */
1130 int nlperrorcalls; /**< number of times an LP error occured and LAB branched without completely
1131 * evaluating all candidates */
1132 int nlimitcalls; /**< number of times a time limit was reached and LAB branched without
1133 * completely evaluating all candidates */
1134 int ntotalresults; /**< The total sum of the entries in nresults. */
1135 int nbinconst; /**< The number of binary constraints added to the base node. */
1136 int nbinconstvio; /**< The number of binary constraints added to the base node, that are violated
1137 * by the LP at that node. */
1138 int ndomred; /**< The number of domain reductions added to the base node. */
1139 int ndomredvio; /**< The number of domain reductions added to the base node, that are violated
1140 * by the LP at that node. */
1141 int ndepthreached; /**< The number of times the branching was aborted due to a too small depth. */
1142 int ndomredcons; /**< The number of binary constraints ignored, as they would be dom reds. */
1143 int ncutoffproofnodes; /**< The number of nodes needed to prove all found cutoffs. */
1144 int ndomredproofnodes; /**< The number of nodes needed to prove all found domreds. */
1145 int ncliquesadded; /**< The number of cliques added in the root node. */
1146 int maxnbestcands; /**< if abbreviated, this is the maximum number of candidates to investigate */
1147 int recursiondepth; /**< The recursiondepth of the LAB. Can be used to access the depth-dependent
1148 * arrays contained in the statistics. */
1149 } STATISTICS;
1150
1151 /** Initializes the statistics with the start values. */
1152 static
statisticsInit(STATISTICS * statistics)1153 void statisticsInit(
1154 STATISTICS* statistics /**< the statistics to be initialized */
1155 )
1156 {
1157 int i;
1158
1159 assert(statistics != NULL);
1160 assert(statistics->recursiondepth > 0);
1161
1162 statistics->nsinglecandidate = 0;
1163 statistics->nsingleafterfilter = 0;
1164 statistics->noldcandidate = 0;
1165 statistics->nlperrorcalls = 0;
1166 statistics->nlimitcalls = 0;
1167 statistics->ntotalresults = 0;
1168 statistics->nbinconst = 0;
1169 statistics->nbinconstvio = 0;
1170 statistics->ndomredvio = 0;
1171 statistics->ndepthreached = 0;
1172 statistics->ndomred = 0;
1173 statistics->ndomredcons = 0;
1174 statistics->ncutoffproofnodes = 0;
1175 statistics->ndomredproofnodes = 0;
1176 statistics->ncliquesadded = 0;
1177
1178 for( i = 0; i <= MAXRESULT; i++)
1179 {
1180 statistics->nresults[i] = 0;
1181 }
1182
1183 for( i = 0; i < statistics->recursiondepth; i++ )
1184 {
1185 statistics->noldbranchused[i] = 0;
1186 statistics->noldbranchusedfsb[i] = 0;
1187 statistics->npropdomred[i] = 0;
1188 statistics->nfullcutoffs[i] = 0;
1189 statistics->nlpssolved[i] = 0;
1190 statistics->nlpssolvedfsb[i] = 0;
1191 statistics->nduplicatelps[i] = 0;
1192 statistics->nlpiterations[i] = 0;
1193 statistics->nlpiterationsfsb[i] = 0;
1194 statistics->nsinglecutoffs[i] = 0;
1195 statistics->stopafterfsb[i] = 0;
1196 statistics->cutoffafterfsb[i] = 0;
1197 statistics->domredafterfsb[i] = 0;
1198 }
1199
1200 for( i = 0; i < statistics->maxnbestcands; i++ )
1201 {
1202 statistics->chosenfsbcand[i] = 0;
1203 }
1204 }
1205
1206 /** Prints the content of the statistics to stdout. */
1207 static
statisticsPrint(SCIP * scip,STATISTICS * statistics)1208 void statisticsPrint(
1209 SCIP* scip, /**< SCIP data structure */
1210 STATISTICS* statistics /**< the statistics to print */
1211 )
1212 {
1213 assert(scip != NULL);
1214 assert(statistics != NULL);
1215 assert(statistics->recursiondepth > 0);
1216
1217 /* only print something, if we have any statistics */
1218 if( statistics->ntotalresults > 0 )
1219 {
1220 int i;
1221
1222 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Lookahead Branching was called <%i> times.\n", statistics->ntotalresults);
1223
1224 for( i = 1; i <= MAXRESULT; i++ )
1225 {
1226 SCIP_RESULT currentresult = (SCIP_RESULT)i;
1227 /* see type_result.h for the id <-> enum mapping */
1228 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Result <%s> was chosen <%i> times\n", getStatusString(currentresult),
1229 statistics->nresults[i]);
1230 }
1231
1232 for( i = 0; i < statistics->maxnbestcands; i++ )
1233 {
1234 if( statistics->chosenfsbcand[i] > 0 )
1235 {
1236 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "The %i. variable (w.r.t. the FSB score) was chosen as the final result %i times.\n",
1237 i+1, statistics->chosenfsbcand[i]);
1238 }
1239 }
1240
1241 for( i = 0; i < statistics->recursiondepth; i++ )
1242 {
1243 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, branching was stopped after the scoring FSB %i times, %i times because of a cutoff and %i times because of a domain reduction\n",
1244 i, statistics->stopafterfsb[i], statistics->cutoffafterfsb[i], statistics->domredafterfsb[i]);
1245 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, <%i> fullcutoffs and <%i> single cutoffs were found.\n",
1246 i, statistics->nfullcutoffs[i], statistics->nsinglecutoffs[i]);
1247 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, <%i> LPs were solved, <%i> of them to calculate the FSB score, <%i> were saved for duplicate grandchildren.\n",
1248 i, statistics->nlpssolved[i], statistics->nlpssolvedfsb[i], statistics->nduplicatelps[i]);
1249 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, <%" SCIP_LONGINT_FORMAT "> iterations were needed to solve the LPs, <%"
1250 SCIP_LONGINT_FORMAT "> of them to calculate the FSB score.\n", i, statistics->nlpiterations[i],
1251 statistics->nlpiterationsfsb[i]);
1252 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, a decision was discarded <%i> times due to domain reduction because of"
1253 " propagation.\n", i, statistics->npropdomred[i]);
1254 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "In depth <%i>, old LAB branching results were used in <%i> cases, old FSB scores in <%d> cases.\n",
1255 i, statistics->noldbranchused[i], statistics->noldbranchusedfsb[i]);
1256 }
1257
1258 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "One single branching candidate was given <%i> times, after filtering, a single candidate remained <%i> times.\n",
1259 statistics->nsinglecandidate, statistics->nsingleafterfilter);
1260 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "The old branching candidate was used <%i> times.\n",
1261 statistics->noldcandidate);
1262 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "An LP error led to branching before all candidates were evaluated <%i> times.\n",
1263 statistics->nlperrorcalls);
1264 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "A reached (time) limit led to branching before all candidates were evaluated <%i> times.\n",
1265 statistics->nlimitcalls);
1266 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Depth limit was reached <%i> times.\n", statistics->ndepthreached);
1267 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Ignored <%i> binary constraints, that would be domain reductions.\n",
1268 statistics->ndomredcons);
1269 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Added <%i> binary constraints, of which <%i> where violated by the base LP.\n",
1270 statistics->nbinconst, statistics->nbinconstvio);
1271 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Reduced the domain of <%i> vars, <%i> of them where violated by the base LP.\n",
1272 statistics->ndomred, statistics->ndomredvio);
1273 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Added <%i> cliques found as binary constraint in the root node\n",
1274 statistics->ncliquesadded);
1275 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Needed <%i> additional nodes to prove the cutoffs of base nodes\n",
1276 statistics->ncutoffproofnodes);
1277 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Needed <%i> additional nodes to prove the domain reductions\n",
1278 statistics->ndomredproofnodes);
1279 }
1280 }
1281
1282 /** Helper struct to store the statistical data needed in a single run. */
1283 typedef struct
1284 {
1285 int ncutoffproofnodes; /**< The number of nodes needed to prove the current cutoff. */
1286 } LOCALSTATISTICS;
1287
1288 /** Allocates the local statistics in buffer memory and initializes it with default values. */
1289 static
localStatisticsAllocate(SCIP * scip,LOCALSTATISTICS ** localstats)1290 SCIP_RETCODE localStatisticsAllocate(
1291 SCIP* scip, /**< SCIP data structure */
1292 LOCALSTATISTICS** localstats /**< pointer to the local statistics to allocate and initialize */
1293 )
1294 {
1295 assert(scip != NULL);
1296 assert(localstats != NULL);
1297
1298 SCIP_CALL( SCIPallocBuffer(scip, localstats) );
1299
1300 (*localstats)->ncutoffproofnodes = 0;
1301
1302 return SCIP_OKAY;
1303 }
1304
1305 /** Frees the allocated buffer memory of the local statistics. */
1306 static
localStatisticsFree(SCIP * scip,LOCALSTATISTICS ** localstats)1307 void localStatisticsFree(
1308 SCIP* scip, /**< SCIP data structure */
1309 LOCALSTATISTICS** localstats /**< pointer to the local statistics to be freed */
1310 )
1311 {
1312 assert(scip != NULL);
1313 assert(localstats != NULL);
1314
1315 SCIPfreeBuffer(scip, localstats);
1316 }
1317 #endif
1318
1319 /** branching rule data */
1320 struct SCIP_BranchruleData
1321 {
1322 CONFIGURATION* config; /**< the parameter that influence the behaviour of the lookahead branching */
1323 PERSISTENTDATA* persistent; /**< the data that persists over multiple branching decisions */
1324 SCIP_Bool isinitialized; /**< indicates whether the fields in this struct are initialized */
1325 #ifdef SCIP_STATISTIC
1326 STATISTICS* statistics; /**< statistical data container */
1327 #endif
1328 };
1329
1330 /** all constraints that were created and may be added to the base node */
1331 typedef struct
1332 {
1333 SCIP_VAR*** consvars; /**< array containing the variables for each constraint to be created */
1334 int* nconsvars; /**< number of vars in each element of 'consvars' */
1335 SCIP_Bool* violated; /**< indicating whether a constraint is violated by the base solution */
1336 int nelements; /**< number of elements in 'consvars' and 'nconsvars' */
1337 int memorysize; /**< number of entries that the array 'consvars' may hold before the
1338 * array is reallocated. */
1339 int nviolatedcons; /**< number of constraints that are violated by the base LP solution. */
1340 } CONSTRAINTLIST;
1341
1342 /** Allocate and initialize the list holding the constraints. */
1343 static
constraintListCreate(SCIP * scip,CONSTRAINTLIST ** conslist,int startsize)1344 SCIP_RETCODE constraintListCreate(
1345 SCIP* scip, /**< SCIP data structure */
1346 CONSTRAINTLIST** conslist, /**< Pointer to the list to be allocated and initialized. */
1347 int startsize /**< The number of entries the list initially can hold. */
1348 )
1349 {
1350 assert(scip != NULL);
1351 assert(conslist != NULL);
1352 assert(startsize > 0);
1353
1354 SCIP_CALL( SCIPallocBuffer(scip, conslist) );
1355 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*conslist)->consvars, startsize) );
1356 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*conslist)->nconsvars, startsize) );
1357 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*conslist)->violated, startsize) );
1358
1359 /* We start without any constraints */
1360 (*conslist)->nelements = 0;
1361 (*conslist)->memorysize = startsize;
1362 (*conslist)->nviolatedcons = 0;
1363
1364 return SCIP_OKAY;
1365 }
1366
1367 /** Append an element to the end of the list of constraints. */
1368 static
constraintListAppend(SCIP * scip,CONSTRAINTLIST * list,SCIP_VAR ** consvars,int nconsvars,SCIP_Bool violated)1369 SCIP_RETCODE constraintListAppend(
1370 SCIP* scip, /**< SCIP data structure */
1371 CONSTRAINTLIST* list, /**< list to add the consvars to */
1372 SCIP_VAR** consvars, /**< array of variables for the constraint to be created later */
1373 int nconsvars, /**< number of elements in 'consvars' */
1374 SCIP_Bool violated /**< indicates whether the constraint is violated by the base lp */
1375 )
1376 {
1377 assert(scip != NULL);
1378 assert(list != NULL);
1379 assert(consvars != NULL);
1380 assert(nconsvars > 0);
1381
1382 /* In case the list tries to hold more elements than it has space, reallocate */
1383 if( list->memorysize == list->nelements )
1384 {
1385 /* resize the array, such that it can hold the new element */
1386 int newmemsize = SCIPcalcMemGrowSize(scip, list->memorysize + 1);
1387 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &list->consvars, list->memorysize, newmemsize) );
1388 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &list->nconsvars, list->memorysize, newmemsize) );
1389 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &list->violated, list->memorysize, newmemsize) );
1390 list->memorysize = newmemsize;
1391 }
1392
1393 /* Set the new vars at the first unused place, which is the length used as index */
1394 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &list->consvars[list->nelements], consvars, nconsvars) ); /*lint !e866*/
1395 list->nconsvars[list->nelements] = nconsvars;
1396 list->violated[list->nelements] = violated;
1397 list->nelements++;
1398
1399 return SCIP_OKAY;
1400 }
1401
1402 /** Free all resources of a constraint list in opposite order to the allocation. */
1403 static
constraintListFree(SCIP * scip,CONSTRAINTLIST ** conslist)1404 void constraintListFree(
1405 SCIP* scip, /**< SCIP data structure */
1406 CONSTRAINTLIST** conslist /**< Pointer to the list to be freed. */
1407 )
1408 {
1409 int i;
1410
1411 assert(scip != NULL);
1412 assert(conslist != NULL);
1413
1414 for( i = 0; i < (*conslist)->nelements; i++ )
1415 {
1416 SCIPfreeBlockMemoryArray(scip, &(*conslist)->consvars[i], (*conslist)->nconsvars[i]);
1417 }
1418
1419 SCIPfreeBlockMemoryArray(scip, &(*conslist)->violated, (*conslist)->memorysize);
1420 SCIPfreeBlockMemoryArray(scip, &(*conslist)->nconsvars, (*conslist)->memorysize);
1421 SCIPfreeBlockMemoryArray(scip, &(*conslist)->consvars, (*conslist)->memorysize);
1422 SCIPfreeBuffer(scip, conslist);
1423 }
1424
1425 /**
1426 * list of binary variables currently branched on
1427 * a down branching (x <= 0) is saved as the negated variable (1-x)
1428 * an up branching (x >= 1) is saved as the original variable (x)
1429 * these variables are used to build the binary constraint in case that a ('binary') branch is cut off
1430 */
1431 typedef struct
1432 {
1433 SCIP_VAR** binaryvars; /**< The binary variables currently branched on. */
1434 int nbinaryvars; /**< The number of entries in 'nbinaryvars'. */
1435 int memorysize; /**< The number of entries that the array 'binaryvars' may hold before the
1436 * array is reallocated. */
1437 } BINARYVARLIST;
1438
1439 /** Allocates and initializes the BINARYVARLIST struct. */
1440 static
binaryVarListCreate(SCIP * scip,BINARYVARLIST ** list,int startsize)1441 SCIP_RETCODE binaryVarListCreate(
1442 SCIP* scip, /**< SCIP data structure */
1443 BINARYVARLIST** list, /**< Pointer to the list to be allocated and initialized. */
1444 int startsize /**< The number of entries the list initially can hold. */
1445 )
1446 {
1447 assert(scip != NULL);
1448 assert(list != NULL);
1449 assert(startsize > 0);
1450
1451 SCIP_CALL( SCIPallocBuffer(scip, list) );
1452 SCIP_CALL( SCIPallocBufferArray(scip, &(*list)->binaryvars, startsize) );
1453
1454 /* We start with no entries and the (current) max length */
1455 (*list)->nbinaryvars = 0;
1456 (*list)->memorysize = startsize;
1457
1458 return SCIP_OKAY;
1459 }
1460
1461 /** Appends a binary variable to the list, reallocating the list if necessary. */
1462 static
binaryVarListAppend(SCIP * scip,BINARYVARLIST * list,SCIP_VAR * vartoadd)1463 void binaryVarListAppend(
1464 SCIP* scip, /**< SCIP data structure */
1465 BINARYVARLIST* list, /**< The list to add the var to. */
1466 SCIP_VAR* vartoadd /**< The binary var to add to the list. */
1467 )
1468 {
1469 assert(scip != NULL);
1470 assert(list != NULL);
1471 assert(vartoadd != NULL);
1472 assert(SCIPvarIsBinary(vartoadd));
1473 assert(list->nbinaryvars < list->memorysize);
1474
1475 /* Set the new var at the first unused place, which is the length used as index */
1476 list->binaryvars[list->nbinaryvars] = vartoadd;
1477 list->nbinaryvars++;
1478 }
1479
1480 /** Remove the last element from the list. */
1481 static
binaryVarListDrop(BINARYVARLIST * list)1482 void binaryVarListDrop(
1483 BINARYVARLIST* list /**< The list to remove the last element from. */
1484 )
1485 {
1486 assert(list != NULL);
1487 assert(list->nbinaryvars > 0);
1488 assert(list->binaryvars[list->nbinaryvars-1] != NULL);
1489
1490 /* decrement the number of entries in the actual list */
1491 list->nbinaryvars--;
1492 }
1493
1494 /** Frees all resources allocated by a BINARYVARLIST in opposite order of allocation. */
1495 static
binaryVarListFree(SCIP * scip,BINARYVARLIST ** list)1496 void binaryVarListFree(
1497 SCIP* scip, /**< SCIP data structure */
1498 BINARYVARLIST** list /**< Pointer to the list to free */
1499 )
1500 {
1501 assert(scip != NULL);
1502 assert(list != NULL);
1503
1504 SCIPfreeBufferArray(scip, &(*list)->binaryvars);
1505 SCIPfreeBuffer(scip, list);
1506 }
1507
1508 /** struct holding the relevant data for handling binary constraints */
1509 typedef struct
1510 {
1511 BINARYVARLIST* binaryvars; /**< current binary vars, used to fill the conslist */
1512 CONSTRAINTLIST* conslist; /**< list of constraints to be created */
1513 } BINCONSDATA;
1514
1515 /** Allocate and initialize the BINCONSDATA struct. */
1516 static
binConsDataCreate(SCIP * scip,BINCONSDATA ** consdata,int maxdepth,int nstartcons)1517 SCIP_RETCODE binConsDataCreate(
1518 SCIP* scip, /**< SCIP data structure */
1519 BINCONSDATA** consdata, /**< Pointer to the struct to be allocated and initialized. */
1520 int maxdepth, /**< The depth of the recursion as an upper bound of branch vars to hold. */
1521 int nstartcons /**< The start size of the array containing the constraints. */
1522 )
1523 {
1524 assert(scip != NULL);
1525 assert(consdata != NULL);
1526 assert(maxdepth > 0);
1527 assert(nstartcons > 0);
1528
1529 SCIP_CALL( SCIPallocBuffer(scip, consdata) );
1530 SCIP_CALL( binaryVarListCreate(scip, &(*consdata)->binaryvars, maxdepth) );
1531 SCIP_CALL( constraintListCreate(scip, &(*consdata)->conslist, nstartcons) );
1532
1533 return SCIP_OKAY;
1534 }
1535
1536 /** Free all resources in a BINCONSDATA in opposite order of allocation. */
1537 static
binConsDataFree(SCIP * scip,BINCONSDATA ** consdata)1538 void binConsDataFree(
1539 SCIP* scip, /**< SCIP data structure */
1540 BINCONSDATA** consdata /**< Pointer to the struct to be freed. */
1541 )
1542 {
1543 assert(scip != NULL);
1544 assert(consdata != NULL);
1545
1546 constraintListFree(scip, &(*consdata)->conslist);
1547 binaryVarListFree(scip, &(*consdata)->binaryvars);
1548 SCIPfreeBuffer(scip, consdata);
1549 }
1550
1551 /** A struct acting as a fixed list of candidates */
1552 typedef struct
1553 {
1554 CANDIDATE** candidates; /**< the array of candidates */
1555 int ncandidates; /**< the number of actual entries in candidates (without trailing NULLs); this
1556 * is NOT the length of the candidates array, but the number of candidates in
1557 * it */
1558 } CANDIDATELIST;
1559
1560 /** allocates the candidate list on the buffer WITHOUT initializing the contained array of candidates. */
1561 static
candidateListCreate(SCIP * scip,CANDIDATELIST ** candidatelist,int ncandidates)1562 SCIP_RETCODE candidateListCreate(
1563 SCIP* scip, /**< SCIP data structure */
1564 CANDIDATELIST** candidatelist, /**< the candidate list to allocate */
1565 int ncandidates /**< the number of candidates the list must hold */
1566 )
1567 {
1568 assert(scip != NULL);
1569 assert(candidatelist != NULL);
1570 assert(ncandidates >= 0);
1571
1572 SCIP_CALL( SCIPallocBuffer(scip, candidatelist) );
1573
1574 if( ncandidates > 0 )
1575 {
1576 SCIP_CALL( SCIPallocBufferArray(scip, &(*candidatelist)->candidates, ncandidates) );
1577 }
1578 else
1579 (*candidatelist)->candidates = NULL;
1580
1581 (*candidatelist)->ncandidates = ncandidates;
1582
1583 return SCIP_OKAY;
1584 }
1585
1586 /** allocates the given list and fills it with all fractional candidates of the current LP solution. */
1587 static
candidateListGetAllFractionalCandidates(SCIP * scip,CANDIDATELIST ** candidatelist)1588 SCIP_RETCODE candidateListGetAllFractionalCandidates(
1589 SCIP* scip, /**< SCIP data structure */
1590 CANDIDATELIST** candidatelist /**< the list to allocate and fill */
1591 )
1592 {
1593 SCIP_VAR** lpcands;
1594 SCIP_Real* lpcandssol;
1595 SCIP_Real* lpcandsfrac;
1596 int nlpcands;
1597 int i;
1598
1599 assert(scip != NULL);
1600 assert(candidatelist != NULL);
1601
1602 /* get all fractional candidates */
1603 SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, NULL, NULL) );
1604
1605 assert(lpcands != NULL);
1606 assert(lpcandssol != NULL);
1607 assert(lpcandsfrac != NULL);
1608
1609 SCIP_CALL( candidateListCreate(scip, candidatelist, nlpcands) );
1610
1611 for( i = 0; i < nlpcands; i++ )
1612 {
1613 CANDIDATE* candidate;
1614
1615 SCIP_CALL( candidateCreate(scip, &candidate) );
1616 assert(candidate != NULL);
1617
1618 candidate->branchvar = lpcands[i];
1619 candidate->branchval = lpcandssol[i];
1620 candidate->fracval = lpcandsfrac[i];
1621
1622 (*candidatelist)->candidates[i] = candidate;
1623
1624 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "created candidate <%s>...\n",
1625 (candidate) != NULL ? SCIPvarGetName((candidate)->branchvar) : "none");
1626 }
1627
1628 return SCIP_OKAY;
1629 }
1630
1631 /** frees the allocated buffer memory of the candidate list and frees the contained candidates. */
1632 static
candidateListFree(SCIP * scip,CANDIDATELIST ** candidatelist)1633 SCIP_RETCODE candidateListFree(
1634 SCIP* scip, /**< SCIP data structure */
1635 CANDIDATELIST** candidatelist /**< the list to be freed */
1636 )
1637 {
1638 int i;
1639
1640 assert(scip != NULL);
1641 assert(candidatelist != NULL);
1642 assert((*candidatelist)->ncandidates > 0 || (*candidatelist)->candidates == NULL);
1643
1644 if( (*candidatelist)->candidates != NULL )
1645 {
1646 for( i = (*candidatelist)->ncandidates - 1; i >= 0; i-- )
1647 {
1648 CANDIDATE* cand = (*candidatelist)->candidates[i];
1649 if( cand != NULL )
1650 {
1651 SCIP_CALL( candidateFree(scip, &cand) );
1652 }
1653 }
1654
1655 SCIPfreeBufferArray(scip, &(*candidatelist)->candidates);
1656 }
1657 SCIPfreeBuffer(scip, candidatelist);
1658
1659 return SCIP_OKAY;
1660 }
1661
1662 /** keeps only the first candidates and frees the remaining ones */
1663 static
candidateListKeep(SCIP * scip,CANDIDATELIST * candidatelist,int nindices)1664 SCIP_RETCODE candidateListKeep(
1665 SCIP* scip, /**< SCIP data structure */
1666 CANDIDATELIST* candidatelist, /**< the list to allocate and fill */
1667 int nindices /**< the number of candidates to keep (starting from 0) */
1668 )
1669 {
1670 int i;
1671
1672 assert(scip != NULL);
1673 assert(candidatelist != NULL);
1674 assert(0 < nindices);
1675 assert(nindices <= candidatelist->ncandidates);
1676
1677 /* only keep the first nindices candidates and free the remaining ones */
1678 for( i = nindices; i < candidatelist->ncandidates; i++ )
1679 {
1680 CANDIDATE* cand = candidatelist->candidates[i];
1681 if( cand != NULL )
1682 {
1683 SCIP_CALL( candidateFree(scip, &cand) );
1684 candidatelist->candidates[i] = NULL;
1685 }
1686 }
1687 candidatelist->ncandidates = nindices;
1688
1689 return SCIP_OKAY;
1690 }
1691
1692 /** all domain reductions found through cutoff of branches */
1693 typedef struct
1694 {
1695 SCIP_Real* lowerbounds; /**< The new lower bounds found for each variable in the problem. */
1696 SCIP_Real* upperbounds; /**< The new upper bounds found for each variable in the problem. */
1697 SCIP_Shortbool* baselpviolated; /**< Indicates whether the base lp solution violates the new bounds of a var.*/
1698 int nviolatedvars; /**< Tracks the number of vars that have a violated (by the base lp) new lower
1699 * or upper bound. */
1700 int nchangedvars; /**< Tracks the number of vars, that have a changed domain. (a change on both,
1701 * upper and lower bound, counts as one.) */
1702 int nsimplebounds; /**< number of changed bounds resulting from infeasible child nodes */
1703 #ifdef SCIP_STATISTIC
1704 int* lowerboundnproofs; /**< The number of nodes needed to prove the lower bound for each variable. */
1705 int* upperboundnproofs; /**< The number of nodes needed to prove the upper bound for each variable. */
1706 #endif
1707 } DOMAINREDUCTIONS;
1708
1709 /** allocate the struct on the buffer and initialize it with the default values */
1710 static
domainReductionsCreate(SCIP * scip,DOMAINREDUCTIONS ** domreds)1711 SCIP_RETCODE domainReductionsCreate(
1712 SCIP* scip, /**< SCIP data structure */
1713 DOMAINREDUCTIONS** domreds /**< The struct that has to be allocated and initialized. */
1714 )
1715 {
1716 SCIP_VAR** vars;
1717 int ntotalvars;
1718 int v;
1719
1720 assert(scip != NULL);
1721 assert(domreds != NULL);
1722
1723 /* The arrays saves the data for all variables in the problem via the ProbIndex. See SCIPvarGetProbindex() */
1724 vars = SCIPgetVars(scip);
1725 ntotalvars = SCIPgetNVars(scip);
1726
1727 /* Allocate the struct and the contained arrays; initialize flags to FALSE */
1728 SCIP_CALL( SCIPallocBuffer(scip, domreds) );
1729 SCIP_CALL( SCIPallocBufferArray(scip, &(*domreds)->lowerbounds, ntotalvars) );
1730 SCIP_CALL( SCIPallocBufferArray(scip, &(*domreds)->upperbounds, ntotalvars) );
1731 SCIP_CALL( SCIPallocClearBufferArray(scip, &(*domreds)->baselpviolated, ntotalvars) );
1732 #ifdef SCIP_STATISTIC
1733 SCIP_CALL( SCIPallocClearBufferArray(scip, &(*domreds)->lowerboundnproofs, ntotalvars) );
1734 SCIP_CALL( SCIPallocClearBufferArray(scip, &(*domreds)->upperboundnproofs, ntotalvars) );
1735 #endif
1736
1737 for( v = 0; v < ntotalvars; ++v )
1738 {
1739 (*domreds)->lowerbounds[v] = SCIPvarGetLbLocal(vars[v]);
1740 (*domreds)->upperbounds[v] = SCIPvarGetUbLocal(vars[v]);
1741 }
1742
1743 /* At the start we have no domain reductions for any variable. */
1744 (*domreds)->nviolatedvars = 0;
1745 (*domreds)->nchangedvars = 0;
1746 (*domreds)->nsimplebounds = 0;
1747
1748 return SCIP_OKAY;
1749 }
1750
1751 /** frees the given DOMAINREDUCTIONS and all contained Arrays in the opposite order of allocation */
1752 static
domainReductionsFree(SCIP * scip,DOMAINREDUCTIONS ** domreds)1753 void domainReductionsFree(
1754 SCIP* scip, /**< SCIP data structure */
1755 DOMAINREDUCTIONS** domreds /**< Pointer to the struct to be freed. */
1756 )
1757 {
1758 assert(scip != NULL);
1759 assert(domreds != NULL);
1760
1761 #ifdef SCIP_STATISTIC
1762 SCIPfreeBufferArray(scip, &(*domreds)->upperboundnproofs);
1763 SCIPfreeBufferArray(scip, &(*domreds)->lowerboundnproofs);
1764 #endif
1765 SCIPfreeBufferArray(scip, &(*domreds)->baselpviolated);
1766 SCIPfreeBufferArray(scip, &(*domreds)->upperbounds);
1767 SCIPfreeBufferArray(scip, &(*domreds)->lowerbounds);
1768 SCIPfreeBuffer(scip, domreds);
1769 }
1770
1771 /** information about the current status of the branching */
1772 typedef struct
1773 {
1774 SCIP_Bool addedbinconss; /**< were binary constraints added? */
1775 SCIP_Bool depthtoosmall; /**< was the remaining depth too small to branch on? */
1776 SCIP_Bool lperror; /**< did an error occur while solving an LP */
1777 SCIP_Bool cutoff; /**< was the current node cut off? */
1778 SCIP_Bool domredcutoff; /**< was the current node cut off due to domain reductions? */
1779 SCIP_Bool domred; /**< were domain reductions added due to information obtained through
1780 * branching? */
1781 SCIP_Bool limitreached; /**< was a limit (time, node, user, ...) reached? */
1782 SCIP_Bool maxnconsreached; /**< was the max number of constraints (bin conss and dom red) reached? */
1783 } STATUS;
1784
1785 /** Allocates the status on the buffer memory and initializes it with default values. */
1786 static
statusCreate(SCIP * scip,STATUS ** status)1787 SCIP_RETCODE statusCreate(
1788 SCIP* scip, /**< SCIP data structure */
1789 STATUS** status /**< the status to be allocated */
1790 )
1791 {
1792 assert(scip != NULL);
1793 assert(status != NULL);
1794
1795 SCIP_CALL( SCIPallocBuffer(scip, status) );
1796
1797 (*status)->addedbinconss = FALSE;
1798 (*status)->depthtoosmall = FALSE;
1799 (*status)->lperror = FALSE;
1800 (*status)->cutoff = FALSE;
1801 (*status)->domred = FALSE;
1802 (*status)->domredcutoff = FALSE;
1803 (*status)->limitreached = FALSE;
1804 (*status)->maxnconsreached = FALSE;
1805
1806 return SCIP_OKAY;
1807 }
1808
1809 /** frees the allocated buffer memory of the status */
1810 static
statusFree(SCIP * scip,STATUS ** status)1811 void statusFree(
1812 SCIP* scip, /**< SCIP data structure */
1813 STATUS** status /**< the status to be freed */
1814 )
1815 {
1816 assert(scip != NULL);
1817 assert(status != NULL);
1818 SCIPfreeBuffer(scip, status);
1819 }
1820
1821 /** container struct to keep the calculated score for each variable */
1822 typedef struct
1823 {
1824 SCIP_Real* scores; /**< the scores for each problem variable */
1825 SCIP_Real* downgains; /**< the downgains for each problem variable */
1826 SCIP_Real* upgains; /**< the upgains for each problem variable */
1827 CANDIDATE** bestsortedcands; /**< array containing the best sorted variable indices w.r.t. their score */
1828 int nbestsortedcands; /**< number of elements in bestsortedcands */
1829 SCIP_Real scoresum; /**< sum of set scores */
1830 int nsetscores; /**< number of set scores */
1831 } SCORECONTAINER;
1832
1833 /** resets the array containing the sorted indices w.r.t. their score. */
1834 static
scoreContainterResetBestSortedCands(SCORECONTAINER * scorecontainer)1835 void scoreContainterResetBestSortedCands(
1836 SCORECONTAINER* scorecontainer /**< the score container to reset */
1837 )
1838 {
1839 assert(scorecontainer != NULL);
1840
1841 BMSclearMemoryArray(scorecontainer->bestsortedcands, scorecontainer->nbestsortedcands);
1842 }
1843
1844 /** allocates the score container and inits it with default values */
1845 static
scoreContainerCreate(SCIP * scip,SCORECONTAINER ** scorecontainer,CONFIGURATION * config)1846 SCIP_RETCODE scoreContainerCreate(
1847 SCIP* scip, /**< SCIP data structure */
1848 SCORECONTAINER** scorecontainer, /**< pointer to the score container to init */
1849 CONFIGURATION* config /**< config struct with the user configuration */
1850 )
1851 {
1852 int ntotalvars;
1853 int ncands = config->maxncands;
1854 int i;
1855
1856 assert(scip != NULL);
1857 assert(scorecontainer != NULL);
1858 assert(config != NULL);
1859
1860 /* the container saves the score for all variables in the problem via the ProbIndex, see SCIPvarGetProbindex() */
1861 ntotalvars = SCIPgetNVars(scip);
1862
1863 if( SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) < ncands )
1864 ncands = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip);
1865
1866 SCIP_CALL( SCIPallocBuffer(scip, scorecontainer) );
1867 SCIP_CALL( SCIPallocBufferArray(scip, &(*scorecontainer)->scores, ntotalvars) );
1868 SCIP_CALL( SCIPallocBufferArray(scip, &(*scorecontainer)->downgains, ntotalvars) );
1869 SCIP_CALL( SCIPallocBufferArray(scip, &(*scorecontainer)->upgains, ntotalvars) );
1870 SCIP_CALL( SCIPallocBufferArray(scip, &(*scorecontainer)->bestsortedcands, ncands) );
1871
1872 (*scorecontainer)->nbestsortedcands = ncands;
1873 (*scorecontainer)->scoresum = 0.0;
1874 (*scorecontainer)->nsetscores = 0;
1875
1876 scoreContainterResetBestSortedCands(*scorecontainer);
1877
1878 /* init the scores to something negative, as scores are always non negative */
1879 for( i = 0; i < ntotalvars; i++ )
1880 {
1881 (*scorecontainer)->scores[i] = -1.0;
1882 (*scorecontainer)->downgains[i] = -1.0;
1883 (*scorecontainer)->upgains[i] = -1.0;
1884 }
1885
1886 return SCIP_OKAY;
1887 }
1888
1889 /** Finds the insertion index for the given score in the candidate list. The score of each candidate is taken from the
1890 * scorecontainer. The first elements of the candidate list have to be sorted, as this method uses binary search to find
1891 * the correct insertion point
1892 */
1893 static
findInsertionPoint(SCIP * scip,SCORECONTAINER * scorecontainer,SCIP_Real scoretoinsert,CANDIDATE ** candidates,int ncandidates)1894 int findInsertionPoint(
1895 SCIP* scip, /**< SCIP data structure */
1896 SCORECONTAINER* scorecontainer, /**< container with all the scores for each candidate */
1897 SCIP_Real scoretoinsert, /**< score to find the insertion index for */
1898 CANDIDATE** candidates, /**< candidate list where the first nsorted elements are sorted (w.r.t. their
1899 * score) */
1900 int ncandidates /**< number of elements in candidates to consider, starting from 0 */
1901 )
1902 {
1903 int left = 0;
1904 int right = ncandidates - 1;
1905
1906 assert(scip != NULL);
1907 assert(scorecontainer != NULL);
1908 assert(candidates != NULL);
1909 assert(ncandidates >= 0);
1910
1911 while( left <= right )
1912 {
1913 int mid = left + ((right - left) / 2);
1914 SCIP_Real midscore = -SCIPinfinity(scip);
1915 CANDIDATE *midcand = candidates[mid];
1916
1917 if( midcand != NULL)
1918 {
1919 SCIP_VAR* midvar;
1920 int midindex;
1921
1922 midvar = midcand->branchvar;
1923 midindex = SCIPvarGetProbindex(midvar);
1924 midscore = scorecontainer->scores[midindex];
1925 }
1926
1927 if( SCIPisGT(scip, scoretoinsert, midscore) )
1928 right = mid - 1;
1929 else
1930 left = mid + 1;
1931 }
1932
1933 return right + 1;
1934 }
1935
1936 /** Inserts the given probindex into the sorted array in the container, moving all indices after it to the right. Then
1937 * returns the element that does not fit into the array any longer. */
1938 static
scoreContainerUpdateSortOrder(SCORECONTAINER * scorecontainer,CANDIDATE * candidate,int insertpoint)1939 CANDIDATE* scoreContainerUpdateSortOrder(
1940 SCORECONTAINER* scorecontainer, /**< container to insert the index into */
1941 CANDIDATE* candidate, /**< the probindex of a variable to store */
1942 int insertpoint /**< point to store the index at */
1943 )
1944 {
1945 int i;
1946 CANDIDATE* movecand = candidate;
1947
1948 assert(scorecontainer != NULL);
1949 assert(candidate != NULL);
1950 assert(insertpoint >= 0);
1951
1952 for( i = insertpoint; i < scorecontainer->nbestsortedcands; i++ )
1953 {
1954 CANDIDATE* oldcand = scorecontainer->bestsortedcands[i];
1955 scorecontainer->bestsortedcands[i] = movecand;
1956 movecand = oldcand;
1957 }
1958
1959 return movecand;
1960 }
1961
1962 /** sets the score for the variable in the score container */
1963 static
scoreContainerSetScore(SCIP * scip,SCORECONTAINER * scorecontainer,CANDIDATE * cand,SCIP_Real score,SCIP_Real downgain,SCIP_Real upgain)1964 SCIP_RETCODE scoreContainerSetScore(
1965 SCIP* scip, /**< SCIP data structure */
1966 SCORECONTAINER* scorecontainer, /**< the container to write into */
1967 CANDIDATE* cand, /**< candidate to add the score for */
1968 SCIP_Real score, /**< score to add */
1969 SCIP_Real downgain, /**< LP gain in down child */
1970 SCIP_Real upgain /**< LP gain in up child */
1971 )
1972 {
1973 CANDIDATE* droppedcandidate;
1974 int probindex;
1975 int insertpoint;
1976
1977 assert(scip != NULL);
1978 assert(scorecontainer != NULL);
1979 assert(cand != NULL);
1980 assert(SCIPisGE(scip, score, -0.2));
1981
1982 probindex = SCIPvarGetProbindex(cand->branchvar);
1983 assert(probindex >= 0);
1984
1985 if( scorecontainer->scores[probindex] < -0.5 )
1986 {
1987 ++scorecontainer->nsetscores;
1988 scorecontainer->scoresum += score;
1989 }
1990 else
1991 {
1992 scorecontainer->scoresum += (score - scorecontainer->scores[probindex]);
1993 }
1994
1995 scorecontainer->scores[probindex] = score;
1996 scorecontainer->downgains[probindex] = downgain;
1997 scorecontainer->upgains[probindex] = upgain;
1998
1999 /* find the point in the sorted array where the new score should be inserted */
2000 insertpoint = findInsertionPoint(scip, scorecontainer, score, scorecontainer->bestsortedcands,
2001 scorecontainer->nbestsortedcands);
2002
2003 /* insert the current variable (cand) at the position calculated above, returning the candidate that
2004 * was removed at the end of the list; this candidate can be the given candidate for the case that the score does not
2005 * belong to the best ones */
2006 droppedcandidate = scoreContainerUpdateSortOrder(scorecontainer, cand, insertpoint);
2007
2008 /* remove the warm start info from the dropped candidate */
2009 if( droppedcandidate != NULL )
2010 {
2011 SCIP_CALL( candidateFreeWarmStartInfo(scip, droppedcandidate) );
2012 }
2013
2014 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Stored score <%.9g> for var <%s>.\n", score, SCIPvarGetName(cand->branchvar));
2015
2016 return SCIP_OKAY;
2017 }
2018
2019 /** Frees the score container and all of its contained arrays. */
2020 static
scoreContainerFree(SCIP * scip,SCORECONTAINER ** scorecontainer)2021 SCIP_RETCODE scoreContainerFree(
2022 SCIP* scip, /**< SCIP data structure */
2023 SCORECONTAINER** scorecontainer /**< score container to free */
2024 )
2025 {
2026 assert(scip != NULL);
2027 assert(scorecontainer != NULL);
2028
2029 /* don't free the candidates inside the cands array, as those are handled by the candidate list */
2030 SCIPfreeBufferArray(scip, &(*scorecontainer)->bestsortedcands);
2031 SCIPfreeBufferArray(scip, &(*scorecontainer)->upgains);
2032 SCIPfreeBufferArray(scip, &(*scorecontainer)->downgains);
2033 SCIPfreeBufferArray(scip, &(*scorecontainer)->scores);
2034 SCIPfreeBuffer(scip, scorecontainer);
2035
2036 return SCIP_OKAY;
2037 }
2038
2039 /*
2040 * Local methods for the logic
2041 */
2042
2043 /** branches recursively on all given candidates */
2044 static
2045 SCIP_RETCODE selectVarRecursive(
2046 SCIP* scip, /**< SCIP data structure */
2047 STATUS* status, /**< current status */
2048 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
2049 CONFIGURATION* config, /**< the configuration of the branching rule */
2050 SCIP_SOL* baselpsol, /**< base lp solution */
2051 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found */
2052 BINCONSDATA* binconsdata, /**< container collecting all binary constraints */
2053 CANDIDATELIST* candidatelist, /**< list of candidates to branch on */
2054 BRANCHINGDECISION* decision, /**< struct to store the final decision */
2055 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores */
2056 LEVEL2DATA* level2data, /**< level 2 LP results data */
2057 int recursiondepth, /**< remaining recursion depth */
2058 SCIP_Real lpobjval, /**< LP objective value of current probing node*/
2059 SCIP_Real baselpobjval, /**< LP objective value of focus node (not probing) */
2060 SCIP_Longint* niterations, /**< pointer to store the total number of iterations for this variable */
2061 int* ndeepestcutoffs, /**< pointer to store the total number of cutoffs on the deepest level */
2062 SCIP_Real* bestgain, /**< pointer to store the best gain found with these candidates */
2063 SCIP_Real* totalgains, /**< pointer to store the sum over all gains that are valid in both children */
2064 int* ntotalgains, /**< pointer to store the number of gains summed in totalgains */
2065 int* ndeepestnodes /**< pointer to store the number of nodes processed in the deepest level */
2066 #ifdef SCIP_STATISTIC
2067 ,STATISTICS* statistics /**< general statistical data */
2068 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
2069 ,SCIP_Real* firstscoreptr /**< pointer to store score of first candidate, or NULL */
2070 ,SCIP_Real* bestscoreptr /**< pointer to store best score, or NULL */
2071 #endif
2072 );
2073
2074 /** Adds the given lower bound to the DOMAINREDUCTIONS struct. */
2075 static
addLowerBound(SCIP * scip,SCIP_VAR * var,SCIP_Real lowerbound,SCIP_SOL * baselpsol,SCIP_Bool simplechange,DOMAINREDUCTIONS * domainreductions,int nproofnodes,int force)2076 void addLowerBound(
2077 SCIP* scip, /**< SCIP data structure */
2078 SCIP_VAR* var, /**< The variable the bound should be added for. */
2079 SCIP_Real lowerbound, /**< The new lower bound for the variable. */
2080 SCIP_SOL* baselpsol, /**< The LP solution of the base problem. Used to check whether the domain
2081 * reduction is violated by it. */
2082 SCIP_Bool simplechange, /**< does the change result from an infeasible child node? */
2083 DOMAINREDUCTIONS* domainreductions /**< The struct the domain reduction should be added to. */
2084 #ifdef SCIP_STATISTIC
2085 ,int nproofnodes /**< The number of nodes needed to prove the new lower bound. */
2086 ,int force /**< should the number of proof nodes be added even if the bound is known already? */
2087 #endif
2088 )
2089 {
2090 int varindex;
2091 SCIP_Real basesolutionval;
2092
2093 assert(scip != NULL);
2094 assert(var != NULL);
2095 assert(baselpsol != NULL);
2096 assert(domainreductions != NULL);
2097 #ifdef SCIP_STATISTIC
2098 assert(nproofnodes >= 0);
2099 #endif
2100
2101 /* The arrays inside DOMAINREDUCTIONS are indexed via the problem index. */
2102 varindex = SCIPvarGetProbindex(var);
2103
2104 lowerbound = SCIPadjustedVarLb(scip, var, lowerbound);
2105
2106 if( SCIPisLT(scip, domainreductions->lowerbounds[varindex], lowerbound) )
2107 {
2108 /* the new lower bound is stronger (greater) than the old one,
2109 * so we update the bound and number of proof nodes */
2110 domainreductions->lowerbounds[varindex] = lowerbound;
2111 domainreductions->nchangedvars++;
2112 if( simplechange )
2113 domainreductions->nsimplebounds++;
2114 #ifdef SCIP_STATISTIC
2115 domainreductions->lowerboundnproofs[varindex] = nproofnodes;
2116 }
2117 else
2118 {
2119 /* if the given lower bound is equal to the old one we take the smaller number of proof nodes */
2120 if( SCIPisEQ(scip, domainreductions->lowerbounds[varindex], lowerbound) &&
2121 (force || domainreductions->lowerboundnproofs[varindex] > nproofnodes) )
2122 domainreductions->lowerboundnproofs[varindex] = nproofnodes;
2123 #endif
2124 }
2125
2126 /* we get the solution value to check whether the domain reduction is violated in the base LP */
2127 basesolutionval = SCIPgetSolVal(scip, baselpsol, var);
2128
2129 /* in case the new lower bound is greater than the base solution val and the base solution val is not violated by a
2130 * previously found bound, we increment the nviolatedvars counter and set the baselpviolated flag */
2131 if( SCIPisFeasGT(scip, domainreductions->lowerbounds[varindex], basesolutionval)
2132 && !domainreductions->baselpviolated[varindex] )
2133 {
2134 domainreductions->baselpviolated[varindex] = TRUE;
2135 domainreductions->nviolatedvars++;
2136 }
2137 }
2138
2139 /** Adds the given upper bound to the DOMAINREDUCTIONS struct. */
2140 static
addUpperBound(SCIP * scip,SCIP_VAR * var,SCIP_Real upperbound,SCIP_SOL * baselpsol,SCIP_Bool simplechange,DOMAINREDUCTIONS * domainreductions,int nproofnodes,int force)2141 void addUpperBound(
2142 SCIP* scip, /**< SCIP data structure */
2143 SCIP_VAR* var, /**< The variable the bound should be added for. */
2144 SCIP_Real upperbound, /**< The new upper bound for the variable. */
2145 SCIP_SOL* baselpsol, /**< The LP solution of the base problem. Used to check whether the domain
2146 * reduction is violated by it. */
2147 SCIP_Bool simplechange, /**< does the change result from an infeasible child node? */
2148 DOMAINREDUCTIONS* domainreductions /**< The struct the domain reduction should be added to. */
2149 #ifdef SCIP_STATISTIC
2150 ,int nproofnodes /**< The number of nodes needed to prove the new lower bound. */
2151 ,int force /**< should the number of proof nodes be added even if the bound is known already? */
2152 #endif
2153 )
2154 {
2155 int varindex;
2156 SCIP_Real basesolutionval;
2157
2158 assert(scip != NULL);
2159 assert(var != NULL);
2160 assert(baselpsol != NULL);
2161 assert(domainreductions != NULL);
2162 #ifdef SCIP_STATISTIC
2163 assert(nproofnodes >= 0);
2164 #endif
2165
2166 /* The arrays inside DOMAINREDUCTIONS are indexed via the problem index. */
2167 varindex = SCIPvarGetProbindex(var);
2168
2169 upperbound = SCIPadjustedVarUb(scip, var, upperbound);
2170
2171 if( SCIPisLE(scip, domainreductions->upperbounds[varindex], upperbound) )
2172 {
2173 #ifdef SCIP_STATISTIC
2174 /* if the given upper bound is equal to the old one we take the smaller number of proof nodes */
2175 if( SCIPisEQ(scip, domainreductions->upperbounds[varindex], upperbound) &&
2176 (force || domainreductions->upperboundnproofs[varindex] > nproofnodes) )
2177 domainreductions->upperboundnproofs[varindex] = nproofnodes;
2178 #endif
2179 }
2180 else
2181 {
2182 /* the new upper bound is stronger (smaller) than the old one,
2183 * so we update the bound and number of proof nodes */
2184 domainreductions->upperbounds[varindex] = upperbound;
2185 domainreductions->nchangedvars++;
2186 if( simplechange )
2187 domainreductions->nsimplebounds++;
2188 #ifdef SCIP_STATISTIC
2189 domainreductions->upperboundnproofs[varindex] = nproofnodes;
2190 #endif
2191 }
2192
2193 /* We get the solution value to check whether the domain reduction is violated in the base LP */
2194 basesolutionval = SCIPgetSolVal(scip, baselpsol, var);
2195
2196 /* In case the new upper bound is smaller than the base solution val and the base solution val is not violated by a
2197 * previously found bound, we increment the nviolatedvars counter and set the baselpviolated flag. */
2198 if( SCIPisFeasLT(scip, domainreductions->upperbounds[varindex], basesolutionval)
2199 && !domainreductions->baselpviolated[varindex] )
2200 {
2201 domainreductions->baselpviolated[varindex] = TRUE;
2202 domainreductions->nviolatedvars++;
2203 }
2204 }
2205
2206 /** apply the domain reductions from a single struct to another one; this may be used in case one of the two child
2207 * problems of a variable is infeasible
2208 */
2209 static
applySingleDeeperDomainReductions(SCIP * scip,SCIP_SOL * baselpsol,int maxstoredomreds,DOMAINREDUCTIONS * targetdomreds,DOMAINREDUCTIONS * domreds)2210 void applySingleDeeperDomainReductions(
2211 SCIP* scip, /**< SCIP data structure */
2212 SCIP_SOL* baselpsol, /**< The LP solution of the base problem. Used to check whether the domain
2213 * reduction is violated by it. */
2214 int maxstoredomreds, /**< maximum number of domain reductions to store */
2215 DOMAINREDUCTIONS* targetdomreds, /**< The target that should be filled with the merged data. */
2216 DOMAINREDUCTIONS* domreds /**< source domain reductions */
2217 )
2218 {
2219 SCIP_VAR** vars;
2220 int nvars;
2221 int i;
2222
2223 assert(scip != NULL);
2224 assert(baselpsol != NULL);
2225 assert(targetdomreds != NULL);
2226 assert(domreds != NULL);
2227
2228 /* as the bounds are tracked for all vars we have to iterate over all vars */
2229 vars = SCIPgetVars(scip);
2230 nvars = SCIPgetNVars(scip);
2231
2232 assert(vars != NULL);
2233 assert(nvars > 0);
2234
2235 for( i = 0; i < nvars; i++ )
2236 {
2237 if( targetdomreds->nviolatedvars >= maxstoredomreds )
2238 return;
2239
2240 #ifdef SCIP_STATISTIC
2241 /* adjust the proof nodes */
2242 addLowerBound(scip, vars[i], domreds->lowerbounds[i], baselpsol, TRUE, targetdomreds,
2243 domreds->lowerboundnproofs[i], FALSE);
2244 #else
2245 addLowerBound(scip, vars[i], domreds->lowerbounds[i], baselpsol, TRUE, targetdomreds);
2246 #endif
2247
2248 if( targetdomreds->nviolatedvars >= maxstoredomreds )
2249 return;
2250
2251 #ifdef SCIP_STATISTIC
2252 addUpperBound(scip, vars[i], domreds->upperbounds[i], baselpsol, TRUE, targetdomreds,
2253 domreds->upperboundnproofs[i], FALSE);
2254 #else
2255 addUpperBound(scip, vars[i], domreds->upperbounds[i], baselpsol, TRUE, targetdomreds);
2256 #endif
2257 }
2258 }
2259
2260 /**
2261 * merges the domain reduction data from the two given branching children data into the target parent data
2262 */
2263 static
applyDeeperDomainReductions(SCIP * scip,SCIP_SOL * baselpsol,int maxstoredomreds,DOMAINREDUCTIONS * targetdomreds,DOMAINREDUCTIONS * downdomreds,DOMAINREDUCTIONS * updomreds)2264 void applyDeeperDomainReductions(
2265 SCIP* scip, /**< SCIP data structure */
2266 SCIP_SOL* baselpsol, /**< The LP solution of the base problem. Used to check whether the domain
2267 * reduction is violated by it. */
2268 int maxstoredomreds, /**< maximum number of domain reductions to store */
2269 DOMAINREDUCTIONS* targetdomreds, /**< The target that should be filled with the merged data. */
2270 DOMAINREDUCTIONS* downdomreds, /**< One of the source DOMAINREDUCTIONS. */
2271 DOMAINREDUCTIONS* updomreds /**< The other source DOMAINREDUCTIONS. */
2272 )
2273 {
2274 SCIP_VAR** vars;
2275 int nvars;
2276 int i;
2277
2278 assert(scip != NULL);
2279 assert(baselpsol != NULL);
2280 assert(targetdomreds != NULL);
2281 assert(downdomreds != NULL);
2282 assert(updomreds != NULL);
2283
2284 /* as the bounds are tracked for all vars we have to iterate over all vars */
2285 vars = SCIPgetVars(scip);
2286 nvars = SCIPgetNVars(scip);
2287
2288 assert(vars != NULL);
2289 assert(nvars > 0);
2290
2291 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Combining domain reductions from up and down child.\n");
2292 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Previous number of changed variable domains: %d\n",
2293 targetdomreds->nchangedvars);
2294
2295 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Number of changed variable domains in up child: %d\n",
2296 updomreds->nchangedvars);
2297 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Number of changed variable domains in down child: %d\n",
2298 downdomreds->nchangedvars);
2299
2300 for( i = 0; i < nvars; i++ )
2301 {
2302 SCIP_Real newlowerbound;
2303 SCIP_Real newupperbound;
2304
2305 assert(vars[i] != NULL);
2306
2307 if( targetdomreds->nviolatedvars >= maxstoredomreds )
2308 return;
2309
2310 /* the MIN of both lower bounds represents a valid lower bound at the parent node */
2311 newlowerbound = MIN(downdomreds->lowerbounds[i], updomreds->lowerbounds[i]);
2312
2313 /* This MIN can now be added via the default add method */
2314 #ifdef SCIP_STATISTIC
2315 addLowerBound(scip, vars[i], newlowerbound, baselpsol, FALSE, targetdomreds,
2316 MIN(4, downdomreds->lowerboundnproofs[i] + updomreds->lowerboundnproofs[i] + 2), FALSE);
2317 #else
2318 addLowerBound(scip, vars[i], newlowerbound, baselpsol, FALSE, targetdomreds);
2319 #endif
2320
2321 if( targetdomreds->nviolatedvars >= maxstoredomreds )
2322 return;
2323
2324 /* the MAX of both upper bounds represents a valid upper bound at the parent node */
2325 newupperbound = MAX(downdomreds->upperbounds[i], updomreds->upperbounds[i]);
2326
2327 /* This MAX can now be added via the default add method */
2328 #ifdef SCIP_STATISTIC
2329 addUpperBound(scip, vars[i], newupperbound, baselpsol, FALSE, targetdomreds,
2330 MIN(4, downdomreds->upperboundnproofs[i] + updomreds->upperboundnproofs[i] + 2), FALSE);
2331 #else
2332 addUpperBound(scip, vars[i], newupperbound, baselpsol, FALSE, targetdomreds);
2333 #endif
2334 }
2335
2336 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Subsequent number of changed variable domains: %d\n",
2337 targetdomreds->nchangedvars);
2338 }
2339
2340 /** Applies the domain reductions to the current node. */
2341 static
applyDomainReductions(SCIP * scip,CONFIGURATION * config,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domreds,SCIP_Bool * domredcutoff,SCIP_Bool * domred,STATISTICS * statistics)2342 SCIP_RETCODE applyDomainReductions(
2343 SCIP* scip, /**< SCIP data structure */
2344 CONFIGURATION* config, /**< the configuration of the branching rule */
2345 SCIP_SOL* baselpsol, /**< The LP solution of the base problem. Used to check whether the domain
2346 * reduction is violated by it. */
2347 DOMAINREDUCTIONS* domreds, /**< The domain reductions that should be applied to the current node. */
2348 SCIP_Bool* domredcutoff, /**< pointer to store whether a cutoff was found due to domain reductions */
2349 SCIP_Bool* domred /**< pointer to store whether a domain change was added */
2350 #ifdef SCIP_STATISTIC
2351 ,STATISTICS* statistics /**< The statistics container. */
2352 #endif
2353 )
2354 {
2355 int i;
2356 SCIP_VAR** probvars;
2357 int nprobvars;
2358 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2359 int nboundsadded = 0;
2360 int nboundsaddedvio = 0;
2361 #endif
2362
2363 assert(scip != NULL);
2364 assert(baselpsol != NULL);
2365 assert(domreds != NULL);
2366 assert(domredcutoff != NULL);
2367 assert(domred != NULL);
2368 #ifdef SCIP_STATISTIC
2369 assert(statistics != NULL);
2370 #endif
2371
2372 /* initially we have no cutoff */
2373 *domredcutoff = FALSE;
2374
2375 /* as the bounds are tracked for all vars we have to iterate over all vars */
2376 probvars = SCIPgetVars(scip);
2377 nprobvars = SCIPgetNVars(scip);
2378
2379 assert(probvars != NULL);
2380 assert(nprobvars > 0);
2381
2382 if( config->prefersimplebounds && domreds->nsimplebounds == 0 )
2383 return SCIP_OKAY;
2384
2385 for( i = 0; i < nprobvars && !(*domredcutoff); i++ )
2386 {
2387 SCIP_VAR* var;
2388 SCIP_Real proposedbound;
2389 SCIP_Real baselpval;
2390 #ifdef SCIP_DEBUG
2391 SCIP_Real oldbound;
2392 #endif
2393 SCIP_Bool infeasible;
2394 SCIP_Bool tightened;
2395
2396 var = probvars[i];
2397
2398 assert(var != NULL);
2399
2400 baselpval = SCIPgetSolVal(scip, baselpsol, var);
2401
2402 if( SCIPisGT(scip, domreds->lowerbounds[i], SCIPvarGetLbLocal(var)) )
2403 {
2404 /* apply lower bound */
2405 #ifdef SCIP_DEBUG
2406 oldbound = SCIPvarGetLbLocal(var);
2407 #endif
2408 proposedbound = domreds->lowerbounds[i];
2409
2410 if( config->onlyvioldomreds && SCIPisGE(scip, baselpval, proposedbound) )
2411 continue;
2412
2413 /* add the new bound */
2414 SCIP_CALL( SCIPtightenVarLb(scip, var, proposedbound, TRUE, &infeasible, &tightened) );
2415
2416 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2417 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Variable <%s>, old lower bound <%g>, proposed lower bound <%g>, new "
2418 "lower bound <%g>\n", SCIPvarGetName(var), oldbound, proposedbound, SCIPvarGetLbLocal(var));
2419 #endif
2420
2421 if( infeasible )
2422 {
2423 /* the domain reduction may result in an empty model (ub < lb) */
2424 *domredcutoff = TRUE;
2425 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The domain reduction of variable <%s> resulted in an empty "
2426 "model.\n", SCIPvarGetName(var));
2427 }
2428 else if( tightened )
2429 {
2430 /* the lb is now strictly greater than before */
2431 *domred = TRUE;
2432 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2433 nboundsadded++;
2434 #endif
2435 #ifdef SCIP_STATISTIC
2436 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The lower bound of variable <%s> was successfully tightened (%d).\n",
2437 SCIPvarGetName(var), domreds->lowerboundnproofs[i]);
2438 statistics->ndomredproofnodes += domreds->lowerboundnproofs[i];
2439 #endif
2440
2441 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2442 if( SCIPisLT(scip, baselpval, SCIPvarGetLbLocal(var)) )
2443 {
2444 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The lower bound of variable <%s> is violated by the base lp "
2445 "value <%g>.\n", SCIPvarGetName(var), baselpval);
2446
2447 nboundsaddedvio++;
2448 }
2449 #endif
2450 }
2451 }
2452
2453 if( SCIPisLT(scip, domreds->upperbounds[i], SCIPvarGetUbLocal(var)) )
2454 {
2455 /* apply upper bound */
2456 #ifdef SCIP_DEBUG
2457 oldbound = SCIPvarGetUbLocal(var);
2458 #endif
2459 proposedbound = domreds->upperbounds[i];
2460
2461 if( config->onlyvioldomreds && SCIPisLE(scip, baselpval, proposedbound) )
2462 continue;
2463
2464 /* add the new bound */
2465 SCIP_CALL( SCIPtightenVarUb(scip, var, proposedbound, TRUE, &infeasible, &tightened) );
2466
2467 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2468 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Variable <%s>, old upper bound <%g>, proposed upper bound <%g>, new "
2469 "upper bound <%g>\n", SCIPvarGetName(var), oldbound, proposedbound, SCIPvarGetUbLocal(var));
2470 #endif
2471
2472 if( infeasible )
2473 {
2474 /* the domain reduction may result in an empty model (ub < lb) */
2475 *domredcutoff = TRUE;
2476 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The domain reduction of variable <%s> resulted in an empty "
2477 "model.\n", SCIPvarGetName(var));
2478 }
2479 else if( tightened )
2480 {
2481 /* the ub is now strictly smaller than before */
2482 *domred = TRUE;
2483 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2484 nboundsadded++;
2485 #endif
2486 #ifdef SCIP_STATISTIC
2487 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The upper bound of variable <%s> was successfully tightened (%d).\n",
2488 SCIPvarGetName(var), domreds->upperboundnproofs[i]);
2489 statistics->ndomredproofnodes += domreds->upperboundnproofs[i];
2490 #endif
2491
2492 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
2493 if( SCIPisGT(scip, baselpval, SCIPvarGetUbLocal(var)) )
2494 {
2495 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The upper bound of variable <%s> is violated by the base lp "
2496 "value <%g>.\n", SCIPvarGetName(var), baselpval);
2497
2498 nboundsaddedvio++;
2499 }
2500 #endif
2501 }
2502 }
2503 }
2504
2505 #ifdef SCIP_STATISTIC
2506 statistics->ndomred += nboundsadded;
2507 statistics->ndomredvio += nboundsaddedvio;
2508 #endif
2509
2510 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Truly changed <%d> domains of the problem, <%d> of them are violated by the "
2511 "base lp.\n", nboundsadded, nboundsaddedvio);
2512 return SCIP_OKAY;
2513 }
2514
2515 /** Copies the current LP solution into the given pointer. Needs to be freed after usage! */
2516 static
copyCurrentSolution(SCIP * scip,SCIP_SOL ** lpsol)2517 SCIP_RETCODE copyCurrentSolution(
2518 SCIP* scip, /**< SCIP data structure */
2519 SCIP_SOL** lpsol /**< pointer to store the solution into */
2520 )
2521 {
2522 assert(scip != NULL);
2523 assert(lpsol != NULL);
2524
2525 /* create temporary solution */
2526 SCIP_CALL( SCIPcreateLPSol(scip, lpsol, NULL) );
2527
2528 /* unlink the solution, so that newly solved lps don't have any influence on our copy */
2529 SCIP_CALL( SCIPunlinkSol(scip, *lpsol) );
2530
2531 return SCIP_OKAY;
2532 }
2533
2534 /** Executes the branching on a given variable with a given value. */
2535 static
branchOnVar(SCIP * scip,CONFIGURATION * config,BRANCHINGDECISION * decision)2536 SCIP_RETCODE branchOnVar(
2537 SCIP* scip /**< SCIP data structure */,
2538 CONFIGURATION* config, /**< config struct with the user configuration */
2539 BRANCHINGDECISION* decision /**< the decision with all the needed data */
2540 )
2541 {
2542 SCIP_VAR* bestvar;
2543 SCIP_Real bestval;
2544 SCIP_NODE* downchild = NULL;
2545 SCIP_NODE* upchild = NULL;
2546
2547 assert(scip != NULL);
2548 assert(decision != NULL);
2549 assert(config != NULL);
2550
2551 bestvar = decision->branchvar;
2552 bestval = decision->branchval;
2553 assert(bestvar != NULL);
2554
2555 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Effective branching on var <%s> with value <%g(%g)>. Old domain: [%g..%g].\n",
2556 SCIPvarGetName(bestvar), bestval, SCIPgetSolVal(scip, NULL, bestvar), SCIPvarGetLbLocal(bestvar), SCIPvarGetUbLocal(bestvar));
2557
2558 assert(!SCIPisIntegral(scip, bestval));
2559
2560 /* branch on the given variable */
2561 assert(SCIPisLT(scip, SCIPvarGetLbLocal(bestvar), bestval));
2562 assert(SCIPisLT(scip, bestval, SCIPvarGetUbLocal(bestvar)));
2563 SCIP_CALL( SCIPbranchVarVal(scip, bestvar, bestval, &downchild, NULL, &upchild) );
2564
2565 SCIPdebugMsg(scip, "down child (node %" SCIP_LONGINT_FORMAT "): branching bound change <%s> <= %g\n",
2566 SCIPnodeGetNumber(downchild), SCIPvarGetName(bestvar), SCIPfeasFloor(scip, bestval));
2567 SCIPdebugMsg(scip, "up child (node %" SCIP_LONGINT_FORMAT "): branching bound change <%s> >= %g\n",
2568 SCIPnodeGetNumber(upchild), SCIPvarGetName(bestvar), SCIPfeasCeil(scip, bestval));
2569
2570 assert(downchild != NULL);
2571 assert(upchild != NULL);
2572
2573 /* update the lower bounds in the children; we must not do this if columns are missing in the LP
2574 * (e.g., because we are doing branch-and-price) or the problem should be solved exactly */
2575 if( SCIPallColsInLP(scip) && !SCIPisExactSolve(scip) )
2576 {
2577 SCIP_Real bestdown = decision->downdb;
2578 SCIP_Bool bestdownvalid = decision->downdbvalid;
2579 SCIP_Real bestup = decision->updb;
2580 SCIP_Bool bestupvalid = decision->updbvalid;
2581 SCIP_Real provedbound = decision->proveddb;
2582
2583 /* update the lower bound for the LPs for further children of both created nodes */
2584 SCIP_CALL( SCIPupdateNodeLowerbound(scip, downchild, bestdownvalid ? MAX(bestdown, provedbound) : provedbound) );
2585 SCIP_CALL( SCIPupdateNodeLowerbound(scip, upchild, bestupvalid ? MAX(bestup, provedbound) : provedbound) );
2586
2587 if( decision->boundsvalid && config->applychildbounds )
2588 {
2589 SCIP_VAR** vars;
2590 int nvars;
2591 int i;
2592
2593 assert(decision->downlowerbounds != NULL);
2594 assert(decision->downupperbounds != NULL);
2595 assert(decision->uplowerbounds != NULL);
2596 assert(decision->upupperbounds != NULL);
2597
2598 nvars = SCIPgetNVars(scip);
2599 vars = SCIPgetVars(scip);
2600
2601 assert(nvars == decision->boundssize);
2602
2603 for( i = 0; i < nvars; i++ )
2604 {
2605 SCIP_VAR* var = vars[i];
2606 SCIP_Real currentlb;
2607 SCIP_Real currentub;
2608 SCIP_Real newlb = decision->downlowerbounds[i];
2609 SCIP_Real newub = decision->downupperbounds[i];
2610 assert(var != NULL);
2611
2612 currentlb = SCIPvarGetLbLocal(var);
2613 currentub = SCIPvarGetUbLocal(var);
2614
2615 /* update the lower bound of the lower child in case it is better than the current one */
2616 if( SCIPisGT(scip, newlb, currentlb) )
2617 {
2618 SCIP_CALL( SCIPchgVarLbNode(scip, downchild, var, newlb) );
2619
2620 SCIPdebugMsg(scip, "down child (node %" SCIP_LONGINT_FORMAT "): add bound change <%s> >= %g\n",
2621 SCIPnodeGetNumber(downchild), SCIPvarGetName(var), newlb);
2622 }
2623
2624 /* update the upper bound of the lower child in case it is better than the current one AND it is not the
2625 * branching variable, as its upper bound is already updated
2626 */
2627 if( SCIPisLT(scip, newub, currentub) && var != bestvar )
2628 {
2629 SCIP_CALL( SCIPchgVarUbNode(scip, downchild, var, newub) );
2630
2631 SCIPdebugMsg(scip, "down child (node %" SCIP_LONGINT_FORMAT "): add bound change <%s> <= %g\n",
2632 SCIPnodeGetNumber(downchild), SCIPvarGetName(var), newub);
2633 }
2634
2635 newlb = decision->uplowerbounds[i];
2636 newub = decision->upupperbounds[i];
2637
2638 /* update the lower bound of the upper child in case it is better than the current one AND it is not the
2639 * branching variable, as its lower bound is already updated
2640 */
2641 if( SCIPisGT(scip, newlb, currentlb) && var != bestvar)
2642 {
2643 SCIP_CALL( SCIPchgVarLbNode(scip, upchild, var, newlb) );
2644
2645 SCIPdebugMsg(scip, "up child (node %" SCIP_LONGINT_FORMAT "): add bound change <%s> >= %g\n",
2646 SCIPnodeGetNumber(upchild), SCIPvarGetName(var), newlb);
2647 }
2648
2649 /* update the upper bound of the upper child in case it is better than the current one */
2650 if( SCIPisLT(scip, newub, currentub) )
2651 {
2652 SCIP_CALL( SCIPchgVarUbNode(scip, upchild, var, newub) );
2653
2654 SCIPdebugMsg(scip, "up child (node %" SCIP_LONGINT_FORMAT "): add bound change <%s> <= %g\n",
2655 SCIPnodeGetNumber(upchild), SCIPvarGetName(var), newub);
2656 }
2657 }
2658 }
2659 }
2660 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, " -> down child's lowerbound: %.9g, estimate: %.9g\n",
2661 SCIPnodeGetLowerbound(downchild), SCIPnodeGetEstimate(downchild));
2662 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, " -> up child's lowerbound: %.9g, estimate: %.9g\n",
2663 SCIPnodeGetLowerbound(upchild), SCIPnodeGetEstimate(upchild));
2664
2665 return SCIP_OKAY;
2666 }
2667
2668 /** Get the number of iterations the last LP needed */
2669 static
getNIterationsLastLP(SCIP * scip,SCIP_Longint * iterations)2670 SCIP_RETCODE getNIterationsLastLP(
2671 SCIP* scip, /**< SCIP data structure */
2672 SCIP_Longint* iterations /**< pointer to store the number of iterations */
2673 )
2674 {
2675 SCIP_LPI* lpi;
2676 int tmpiter;
2677
2678 assert(scip != NULL);
2679 assert(iterations != NULL);
2680
2681 /* get the LP interface of the last solved LP */
2682 SCIP_CALL( SCIPgetLPI(scip, &lpi) );
2683
2684 /* get the number of iterations from the interface */
2685 SCIP_CALL( SCIPlpiGetIterations(lpi, &tmpiter) );
2686
2687 *iterations = (SCIP_Longint)tmpiter;
2688
2689 return SCIP_OKAY;
2690 }
2691
2692 /** Creates a new probing node with a new bound for the given candidate and solves the corresponding LP. */
2693 static
executeBranching(SCIP * scip,CONFIGURATION * config,SCIP_Bool downbranching,CANDIDATE * candidate,BRANCHINGRESULTDATA * resultdata,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domreds,STATUS * status)2694 SCIP_RETCODE executeBranching(
2695 SCIP* scip, /**< SCIP data structure */
2696 CONFIGURATION* config, /**< configuration to control the behavior */
2697 SCIP_Bool downbranching, /**< the branching direction */
2698 CANDIDATE* candidate, /**< the candidate to branch on */
2699 BRANCHINGRESULTDATA* resultdata, /**< pointer to the result data which gets filled with the status */
2700 SCIP_SOL* baselpsol, /**< the base lp solution */
2701 DOMAINREDUCTIONS* domreds, /**< struct to store the domain reduction found during propagation */
2702 STATUS* status /**< status will contain updated lperror and limit fields */
2703 )
2704 {
2705 SCIP_Real oldupperbound;
2706 SCIP_Real oldlowerbound;
2707 SCIP_Real newbound;
2708 SCIP_LPSOLSTAT solstat;
2709 SCIP_VAR* branchvar;
2710 SCIP_Real branchval;
2711
2712 assert(scip != NULL);
2713 assert(candidate != NULL);
2714 assert(resultdata != NULL);
2715 assert(status != NULL);
2716 assert(config != NULL);
2717 assert(status != NULL);
2718
2719 branchvar = candidate->branchvar;
2720 branchval = candidate->branchval;
2721
2722 assert(branchvar != NULL);
2723 assert(!SCIPisFeasIntegral(scip, branchval));
2724
2725 if( downbranching )
2726 {
2727 /* round the given value down, so that it can be used as the new upper bound */
2728 newbound = SCIPfeasFloor(scip, branchval);
2729 }
2730 else
2731 {
2732 /* round the given value up, so that it can be used as the new lower bound */
2733 newbound = SCIPfeasCeil(scip, branchval);
2734 }
2735
2736 oldupperbound = SCIPvarGetUbLocal(branchvar);
2737 oldlowerbound = SCIPvarGetLbLocal(branchvar);
2738
2739 #ifdef SCIP_DEBUG
2740 if( downbranching )
2741 {
2742 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "DownBranching: Var=<%s>, Proposed upper bound=<%g>, "
2743 "old bounds=[<%g>..<%g>], new bounds=[<%g>..<%g>]\n", SCIPvarGetName(branchvar), newbound, oldlowerbound,
2744 oldupperbound, oldlowerbound, newbound);
2745 }
2746 else
2747 {
2748 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "UpBranching: Var=<%s>, Proposed lower bound=<%g>, "
2749 "old bounds=[<%g>..<%g>], new bounds=[<%g>..<%g>]\n", SCIPvarGetName(branchvar), newbound, oldlowerbound,
2750 oldupperbound, newbound, oldupperbound);
2751 }
2752 #endif
2753
2754 if( (downbranching && newbound < oldlowerbound - 0.5)
2755 || (!downbranching && newbound > oldupperbound + 0.5) )
2756 {
2757 /* if lb > ub we can cutoff this node */
2758 resultdata->cutoff = TRUE;
2759
2760 return SCIP_OKAY;
2761 }
2762
2763 assert(!resultdata->cutoff);
2764
2765 SCIP_CALL( SCIPnewProbingNode(scip) );
2766
2767 if( downbranching )
2768 {
2769 /* down branching preparations */
2770 if( SCIPisFeasLT(scip, newbound, oldupperbound) )
2771 {
2772 /* If the new upper bound is smaller than the old upper bound and also
2773 * greater than (or equal to) the old lower bound, we set the new upper bound.
2774 * oldLowerBound <= newUpperBound < oldUpperBound */
2775 SCIP_CALL( SCIPchgVarUbProbing(scip, branchvar, newbound) );
2776 }
2777 }
2778 else
2779 {
2780 /* up branching preparations */
2781 if( SCIPisFeasGT(scip, newbound, oldlowerbound) )
2782 {
2783 /* If the new lower bound is greater than the old lower bound and also
2784 * smaller than (or equal to) the old upper bound, we set the new lower bound.
2785 * oldLowerBound < newLowerBound <= oldUpperBound
2786 */
2787 SCIP_CALL( SCIPchgVarLbProbing(scip, branchvar, newbound) );
2788 }
2789 }
2790
2791 /* restore the stored LP data (e.g., the basis) from a filtering run */
2792 if( candidateHasWarmStartInfo(candidate, downbranching) )
2793 {
2794 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Restoring lp information for %s branch of variable <%s>\n",
2795 downbranching ? "down" : "up", SCIPvarGetName(branchvar));
2796 SCIP_CALL( candidateLoadWarmStartInfo(scip, candidate, downbranching) );
2797 }
2798
2799 /* apply domain propagation */
2800 if( config->propagate )
2801 {
2802 SCIP_Longint ndomredsfound = 0;
2803
2804 SCIP_CALL( SCIPpropagateProbing(scip, config->maxproprounds, &resultdata->cutoff, &ndomredsfound) );
2805
2806 if( ndomredsfound > 0 )
2807 {
2808 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Found %" SCIP_LONGINT_FORMAT " domain reductions via propagation.\n", ndomredsfound);
2809
2810 /* domreds != NULL iff config->usedomainreduction */
2811 if( domreds != NULL )
2812 {
2813 int i;
2814 SCIP_VAR** problemvars = SCIPgetVars(scip);
2815 int nproblemvars = SCIPgetNVars(scip);
2816
2817 assert(problemvars != NULL);
2818
2819 assert(config->usedomainreduction);
2820
2821 for( i = 0; i < nproblemvars; i++ )
2822 {
2823 SCIP_Real lowerbound;
2824 SCIP_Real upperbound;
2825 SCIP_VAR* var = problemvars[i];
2826 assert(var != NULL);
2827
2828 lowerbound = SCIPvarGetLbLocal(var);
2829 upperbound = SCIPvarGetUbLocal(var);
2830 #ifdef SCIP_STATISTIC
2831 addLowerBound(scip, var, lowerbound, baselpsol, FALSE, domreds, 0, FALSE);
2832 addUpperBound(scip, var, upperbound, baselpsol, FALSE, domreds, 0, FALSE);
2833 #else
2834 addLowerBound(scip, var, lowerbound, baselpsol, FALSE, domreds);
2835 addUpperBound(scip, var, upperbound, baselpsol, FALSE, domreds);
2836 #endif
2837 }
2838 }
2839 }
2840 }
2841
2842 if( !resultdata->cutoff )
2843 {
2844 /* solve the prepared probing LP */
2845 SCIP_CALL( SCIPsolveProbingLP(scip, -1, &status->lperror, &resultdata->cutoff) );
2846
2847 /* store the number of iterations needed */
2848 SCIP_CALL( getNIterationsLastLP(scip, &resultdata->niterations) );
2849
2850 solstat = SCIPgetLPSolstat(scip);
2851 assert(solstat != SCIP_LPSOLSTAT_UNBOUNDEDRAY);
2852
2853 /* for us an error occurred, if an error during the solving occurred or the lp could not be solved but was not
2854 * cutoff */
2855 status->lperror = status->lperror || (solstat == SCIP_LPSOLSTAT_NOTSOLVED && resultdata->cutoff == FALSE);
2856
2857 /* if we seem to have reached a {time, iteration}-limit or the user cancelled the execution, we want to stop
2858 * further calculations and instead return the current calculation state */
2859 status->limitreached = (solstat == SCIP_LPSOLSTAT_ITERLIMIT) || (solstat == SCIP_LPSOLSTAT_TIMELIMIT);
2860
2861 if( resultdata->cutoff )
2862 {
2863 resultdata->objval = SCIPinfinity(scip);
2864 resultdata->dualbound = SCIPinfinity(scip);
2865 resultdata->dualboundvalid = TRUE;
2866 }
2867 else if( !status->limitreached && !status->lperror )
2868 {
2869 SCIP_Bool foundsol = FALSE;
2870
2871 SCIP_CALL( SCIPtryStrongbranchLPSol(scip, &foundsol, &resultdata->cutoff) );
2872
2873 /* if we have no error, we save the new objective value and the cutoff decision in the resultdata */
2874 resultdata->objval = SCIPgetLPObjval(scip);
2875 resultdata->dualbound = SCIPgetLPObjval(scip);
2876 resultdata->dualboundvalid = TRUE;
2877 resultdata->cutoff = resultdata->cutoff || SCIPisGE(scip, resultdata->objval, SCIPgetCutoffbound(scip));
2878
2879 assert(solstat != SCIP_LPSOLSTAT_INFEASIBLE || resultdata->cutoff);
2880 }
2881 }
2882
2883 return SCIP_OKAY;
2884 }
2885
2886 /** Creates a logic or constraint based on the given 'consvars'. This array has to consist of the negated
2887 * versions of the variables present on a cutoff "path" (path means all variables from the root directly
2888 * to the cutoff node).
2889 * Let x_1, ..., x_n be the variables on a path to a cutoff with the branchings x_i <= 1 for all i.
2890 * Summed up the constraints would look like x_1 + ... x_n <= n-1.
2891 * Let y_i = 1 - x_i. Then we have y_1 + ... + y_n >= 1 which is a logic or constraint.
2892 */
2893 static
createBinaryConstraint(SCIP * scip,CONFIGURATION * config,SCIP_CONS ** constraint,char * constraintname,SCIP_VAR ** consvars,int nconsvars)2894 SCIP_RETCODE createBinaryConstraint(
2895 SCIP* scip, /**< SCIP data structure */
2896 CONFIGURATION* config, /**< configuration containing flags changing the behavior */
2897 SCIP_CONS** constraint, /**< pointer to store the created constraint in */
2898 char* constraintname, /**< name of the new constraint */
2899 SCIP_VAR** consvars, /**< array containing the negated binary vars */
2900 int nconsvars /**< the number of elements in 'consvars' */
2901 )
2902 {
2903 SCIP_Bool initial;
2904 SCIP_Bool separate;
2905 SCIP_Bool removable;
2906 SCIP_Bool enforce = FALSE;
2907 SCIP_Bool check = FALSE;
2908 SCIP_Bool propagate = TRUE;
2909 SCIP_Bool local = TRUE;
2910 SCIP_Bool modifiable = FALSE;
2911 SCIP_Bool dynamic = FALSE;
2912 SCIP_Bool stickingatnode = FALSE;
2913
2914 assert(scip != NULL);
2915 assert(config != NULL);
2916 assert(constraint != NULL);
2917 assert(constraintname != NULL);
2918 assert(consvars != NULL);
2919 assert(nconsvars > 0);
2920
2921 initial = (config->addbinconsrow == 2);
2922 separate = (config->addbinconsrow == 1);
2923 removable = (config->addbinconsrow == 1);
2924
2925 /* creating a logic or constraint based on the list of vars in 'consvars'.
2926 * A logic or constraints looks like that: y_1 + ... + y_n >= 1.
2927 */
2928 SCIP_CALL( SCIPcreateConsLogicor(scip, constraint, constraintname, nconsvars, consvars, initial, separate, enforce,
2929 check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
2930 return SCIP_OKAY;
2931 }
2932
2933 /**
2934 * Create a name for the binary constraint.
2935 */
2936 static
createBinaryConstraintName(SCIP_VAR ** binaryvars,int nbinaryvars,char * constraintname)2937 void createBinaryConstraintName(
2938 SCIP_VAR** binaryvars, /**< the variables contained in the constraint */
2939 int nbinaryvars, /**< the number of elements in 'binaryvars' */
2940 char* constraintname /**< the char pointer to store the name in */
2941 )
2942 {
2943 int i;
2944
2945 assert(binaryvars != NULL);
2946 assert(nbinaryvars > 0);
2947 assert(constraintname != NULL);
2948 assert(binaryvars[0] != NULL);
2949
2950 (void) SCIPsnprintf(constraintname, SCIP_MAXSTRLEN, "lookahead_bin_%s", SCIPvarGetName(binaryvars[0]));
2951
2952 for( i = 1; i < nbinaryvars; i++ )
2953 {
2954 size_t oldlen;
2955 SCIP_VAR* var = binaryvars[i];
2956 assert(var != NULL);
2957
2958 oldlen = strlen(constraintname);
2959 (void) strncat(constraintname, "_", SCIP_MAXSTRLEN-oldlen);
2960 (void) strncat(constraintname, SCIPvarGetName(var), SCIP_MAXSTRLEN-oldlen-1);
2961 }
2962 }
2963
2964 /**
2965 * Add the constraints found during the lookahead branching.
2966 * The implied binary bounds were found when two or more consecutive branchings of binary variables were cutoff. Then these
2967 * branching constraints can be combined into a single 'binary constraint'.
2968 */
2969 static
addBinaryConstraint(SCIP * scip,CONFIGURATION * config,BINCONSDATA * binconsdata,SCIP_SOL * baselpsol,STATISTICS * statistics)2970 SCIP_RETCODE addBinaryConstraint(
2971 SCIP* scip, /**< SCIP data structure */
2972 CONFIGURATION* config, /**< the configuration of the branching rule */
2973 BINCONSDATA* binconsdata, /**< collected binary constraints */
2974 SCIP_SOL* baselpsol /**< the original lp solution, used to check the violation of the constraint */
2975 #ifdef SCIP_STATISTIC
2976 ,STATISTICS* statistics /**< statistics data */
2977 #endif
2978 )
2979 {
2980 assert(scip != NULL);
2981 assert(config != NULL);
2982 assert(binconsdata != NULL);
2983 assert(baselpsol != NULL);
2984 assert(binconsdata->binaryvars != NULL);
2985 assert(binconsdata->binaryvars->nbinaryvars > 0);
2986
2987 /* if we only have one var for the constraint, we can ignore it as it is already added as a domain reduction. */
2988 if( binconsdata->binaryvars->nbinaryvars > 1 )
2989 {
2990 int i;
2991 SCIP_VAR** negatedvars;
2992 SCIP_Real lhssum = 0.0;
2993 SCIP_Bool violated;
2994
2995 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Adding binary constraint for <%i> vars.\n",
2996 binconsdata->binaryvars->nbinaryvars);
2997
2998 SCIP_CALL( SCIPallocBufferArray(scip, &negatedvars, binconsdata->binaryvars->nbinaryvars) );
2999
3000 for( i = 0; i < binconsdata->binaryvars->nbinaryvars; i++ )
3001 {
3002 SCIP_VAR* var = binconsdata->binaryvars->binaryvars[i];
3003 assert(var != NULL);
3004 assert(SCIPvarIsBinary(var));
3005
3006 SCIP_CALL( SCIPgetNegatedVar(scip, var, &negatedvars[i]) );
3007 lhssum += SCIPgetSolVal(scip, baselpsol, negatedvars[i]);
3008 }
3009
3010 violated = (lhssum < 1);
3011
3012 if( config->addnonviocons || violated )
3013 {
3014 SCIP_CALL( constraintListAppend(scip, binconsdata->conslist, negatedvars,
3015 binconsdata->binaryvars->nbinaryvars, violated) );
3016
3017 /* the constraint we will be building is a logic or: we have a list of binary variables that were
3018 * cutoff while we branched on with >= 1. So we have the constraint: x_1 + ... + x_n <= n-1.
3019 * Let y = (1-x), then we have an equivalent formulation: y_1 + ... + y_n >= 1. If the base lp
3020 * is violating this constraint we count this for our number of violated constraints and bounds. */
3021 if( violated )
3022 binconsdata->conslist->nviolatedcons++;
3023 }
3024
3025 SCIPfreeBufferArray(scip, &negatedvars);
3026 }
3027 #ifdef SCIP_STATISTIC
3028 else
3029 {
3030 assert(statistics != NULL);
3031 statistics->ndomredcons++;
3032 }
3033 #endif
3034
3035 return SCIP_OKAY;
3036 }
3037
3038 /** applies the binary constraints to the original problem. */
3039 static
applyBinaryConstraints(SCIP * scip,SCIP_NODE * basenode,CONSTRAINTLIST * conslist,CONFIGURATION * config,SCIP_Bool * consadded,SCIP_Bool * cutoff,SCIP_Bool * boundchange,STATISTICS * statistics)3040 SCIP_RETCODE applyBinaryConstraints(
3041 SCIP* scip, /**< SCIP data structure */
3042 SCIP_NODE* basenode, /**< original branching node */
3043 CONSTRAINTLIST* conslist, /**< list of constraints to be added */
3044 CONFIGURATION* config, /**< the configuration of the branching rule */
3045 SCIP_Bool* consadded, /**< pointer to store whether at least one constraint was added */
3046 SCIP_Bool* cutoff, /**< pointer to store whether the original problem was made infeasible */
3047 SCIP_Bool* boundchange /**< pointer to store whether a bound change has been applied by adding the
3048 * constraint as a clique */
3049 #ifdef SCIP_STATISTIC
3050 ,STATISTICS* statistics /**< statistics data */
3051 #endif
3052 )
3053 {
3054 int nconsadded = 0;
3055 int i;
3056 #ifdef SCIP_STATISTIC
3057 int nvioconsadded = 0;
3058
3059 assert(statistics != NULL);
3060 #endif
3061 assert(basenode != NULL);
3062 assert(conslist != NULL);
3063 assert(config != NULL);
3064 assert(consadded != NULL);
3065 assert(cutoff != NULL);
3066 assert(boundchange != NULL);
3067
3068 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "processing %d binary constraints.\n", conslist->nelements);
3069
3070 if( conslist->nelements == 0 )
3071 return SCIP_OKAY;
3072
3073 for( i = 0; i < conslist->nelements; i++ )
3074 {
3075 SCIP_VAR** vars = conslist->consvars[i];
3076 int nvars = conslist->nconsvars[i];
3077 int v;
3078 #ifdef SCIP_STATISTIC
3079 SCIP_Bool violated = conslist->violated[i];
3080 #endif
3081
3082 assert(vars != NULL);
3083
3084 for( v = 0; v < nvars; ++v )
3085 {
3086 assert(vars[v] != NULL);
3087 assert(SCIPvarIsBinary(vars[v]));
3088
3089 if( SCIPvarGetLbLocal(vars[v]) > 0.5 )
3090 break;
3091 }
3092
3093 /* no variable is fixed to 1 yet, so constraint is not redundant */
3094 if( v == nvars )
3095 {
3096 SCIP_CONS* constraint;
3097 char constraintname[SCIP_MAXSTRLEN];
3098
3099 /* create a name for the new constraint */
3100 createBinaryConstraintName(vars, nvars, constraintname);
3101 /* create the constraint with the freshly created name */
3102 SCIP_CALL( createBinaryConstraint(scip, config, &constraint, constraintname, vars, nvars) );
3103
3104 #ifdef PRINTNODECONS
3105 SCIPinfoMessage(scip, NULL, "Created constraint:\n");
3106 SCIP_CALL( SCIPprintCons(scip, constraint, NULL) );
3107 SCIPinfoMessage(scip, NULL, "\n");
3108 #endif
3109 /* add the constraint to the given node */
3110 SCIP_CALL( SCIPaddConsNode(scip, basenode, constraint, NULL) );
3111
3112 nconsadded++;
3113
3114 #ifdef SCIP_STATISTIC
3115 if( violated )
3116 nvioconsadded++;
3117 #endif
3118
3119 /* release the constraint, as it is no longer needed */
3120 SCIP_CALL( SCIPreleaseCons(scip, &constraint) );
3121
3122 /* a 2-variable logicor constraint can be expressend as a clique on the negated variables;
3123 * add it to the clique table if we are at the root node */
3124 if( nvars == 2 && config->addclique && SCIPgetNNodes(scip) == 1 )
3125 {
3126 SCIP_Bool* values;
3127 SCIP_Bool infeasible;
3128 int nbdchgs;
3129
3130 SCIP_CALL( SCIPallocClearBufferArray(scip, &values, nvars) );
3131
3132 /* a two-variable logicor constraint x + y >= 1 yields the implication x == 0 -> y == 1, and is represented
3133 * by the clique inequality ~x + ~y <= 1
3134 */
3135 SCIP_CALL( SCIPaddClique(scip, vars, values, nvars, FALSE, &infeasible, &nbdchgs) );
3136
3137 #ifdef SCIP_STATISTIC
3138 statistics->ncliquesadded++;
3139 #endif
3140
3141 if( infeasible )
3142 *cutoff = TRUE;
3143
3144 if( nbdchgs > 0 )
3145 *boundchange = TRUE;
3146
3147 SCIPfreeBufferArray(scip, &values);
3148 }
3149 }
3150 }
3151
3152 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "added %d/%d binary constraints.\n", nconsadded, conslist->nelements);
3153
3154 if( nconsadded > 0 )
3155 {
3156 *consadded = TRUE;
3157
3158 #ifdef SCIP_STATISTIC
3159 statistics->nbinconst += nconsadded;
3160 statistics->nbinconstvio += nvioconsadded;
3161 #endif
3162 }
3163
3164 return SCIP_OKAY;
3165 }
3166
3167 /** checks whether the given bounds are still the bounds of the given variable */
3168 static
areBoundsChanged(SCIP * scip,SCIP_VAR * var,SCIP_Real lowerbound,SCIP_Real upperbound)3169 SCIP_Bool areBoundsChanged(
3170 SCIP* scip, /**< SCIP data structure */
3171 SCIP_VAR* var, /**< variable to check the bounds of */
3172 SCIP_Real lowerbound, /**< reference lower bound */
3173 SCIP_Real upperbound /**< reference upper bound */
3174 )
3175 {
3176 assert(scip != NULL);
3177 assert(var != NULL);
3178 assert(SCIPisFeasIntegral(scip, lowerbound));
3179 assert(SCIPisFeasIntegral(scip, upperbound));
3180 assert(!SCIPisEQ(scip, lowerbound, upperbound));
3181 assert(SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS);
3182
3183 /* due to roundings the value might have changed slightly without an actual influence on the integral value */
3184 return SCIPvarGetLbLocal(var) > lowerbound + 0.5 || SCIPvarGetUbLocal(var) < upperbound - 0.5;
3185 }
3186
3187 /** Checks whether the branching rule should continue or terminate with the currently gathered data */
3188 static
isBranchFurther(STATUS * status,SCIP_Bool checkdomreds)3189 SCIP_Bool isBranchFurther(
3190 STATUS* status, /**< current status */
3191 SCIP_Bool checkdomreds /**< should domain reductions be checked? */
3192 )
3193 {
3194 assert(status != NULL);
3195
3196 return !status->lperror && !status->cutoff && !status->limitreached
3197 && !status->maxnconsreached && (!checkdomreds || !status->domred);
3198 }
3199
3200 /** Checks whether the branching rule should continue or terminate with the currently gathered data. Additionally decrements
3201 * the given loopcounter. This is needed to better emulate the behavior of FSB by LAB with a depth of 1. */
3202 static
isBranchFurtherLoopDecrement(STATUS * status,int * loopcounter)3203 SCIP_Bool isBranchFurtherLoopDecrement(
3204 STATUS* status, /**< current status */
3205 int* loopcounter /**< the counter to decrement */
3206 )
3207 {
3208 SCIP_Bool branchfurther;
3209
3210 assert(status != NULL);
3211 assert(loopcounter != NULL);
3212
3213 branchfurther = isBranchFurther(status, FALSE);
3214
3215 if( !branchfurther )
3216 (*loopcounter)--;
3217
3218 return branchfurther;
3219 }
3220
3221 /** determines whether the previous LAB result of a variable should be reused */
3222 static
isUseOldBranching(SCIP * scip,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_VAR * branchvar)3223 SCIP_Bool isUseOldBranching(
3224 SCIP* scip, /**< SCIP data structure */
3225 PERSISTENTDATA* persistent, /**< data storage over multiple calls to the rule */
3226 CONFIGURATION* config, /**< the configuration of the branching rule */
3227 SCIP_VAR* branchvar /**< variable to check */
3228 )
3229 {
3230 assert(scip != NULL);
3231 assert(config != NULL);
3232 assert(branchvar != NULL);
3233
3234 /* an old branching can be reused, if we are still at the same node and just a few LPs were solved in between */
3235 if( config->inscoring )
3236 {
3237 return SCIPgetVarStrongbranchNode(scip, branchvar) == SCIPgetNNodes(scip)
3238 && SCIPgetVarStrongbranchLPAge(scip, branchvar) < config->reevalagefsb;
3239 }
3240 else
3241 {
3242 return persistent->lastbranchid[SCIPvarGetProbindex(branchvar)] == SCIPgetNNodes(scip)
3243 && SCIPgetNLPs(scip) - persistent->lastbranchnlps[SCIPvarGetProbindex(branchvar)] < config->reevalage;
3244 }
3245 }
3246
3247 /** retrieves previous LAB result for the given variable */
3248 static
getOldBranching(SCIP * scip,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real * oldlpobjval)3249 SCIP_RETCODE getOldBranching(
3250 SCIP* scip, /**< SCIP data structure */
3251 PERSISTENTDATA* persistent, /**< data storage over multiple calls to the rule */
3252 CONFIGURATION* config, /**< the configuration of the branching rule */
3253 SCIP_VAR* branchvar, /**< variable to get previous results for */
3254 BRANCHINGRESULTDATA* downbranchingresult,/**< pointer to store the previous down result in */
3255 BRANCHINGRESULTDATA* upbranchingresult, /**< pointer to store the previous up result in */
3256 SCIP_Real* oldlpobjval /**< pointer to store the previous base lp objval in */
3257 )
3258 {
3259 assert(scip != NULL);
3260 assert(persistent != NULL);
3261 assert(branchvar != NULL);
3262 assert(downbranchingresult != NULL);
3263 assert(upbranchingresult != NULL);
3264 assert(oldlpobjval != NULL);
3265
3266 if( config->inscoring )
3267 {
3268 SCIP_CALL( SCIPgetVarStrongbranchLast(scip, branchvar, &downbranchingresult->dualbound, &upbranchingresult->dualbound,
3269 &downbranchingresult->dualboundvalid, &upbranchingresult->dualboundvalid, NULL, oldlpobjval) );
3270 downbranchingresult->objval = downbranchingresult->dualbound;
3271 upbranchingresult->objval = upbranchingresult->dualbound;
3272 }
3273 else
3274 {
3275 int varindex = SCIPvarGetProbindex(branchvar);
3276
3277 branchingResultDataCopy(persistent->lastbranchdownres[varindex], downbranchingresult);
3278 branchingResultDataCopy(persistent->lastbranchupres[varindex], upbranchingresult);
3279 *oldlpobjval = persistent->lastbranchlpobjval[varindex];
3280 }
3281
3282 #ifdef SCIP_DEBUG
3283 {
3284 SCIP_Real downgain;
3285 SCIP_Real upgain;
3286
3287 downgain = MAX(downbranchingresult->dualbound - *oldlpobjval, 0);
3288 upgain = MAX(upbranchingresult->dualbound - *oldlpobjval, 0);
3289
3290 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Lookahead branching on variable <%s> already performed (lpage=%"
3291 SCIP_LONGINT_FORMAT ", down=%.9g (%+g), up=%.9g (%+g))\n", SCIPvarGetName(branchvar),
3292 SCIPgetNLPs(scip) - persistent->lastbranchnlps[SCIPvarGetProbindex(branchvar)],
3293 downbranchingresult->dualbound, downgain, upbranchingresult->dualbound, upgain);
3294 }
3295 #endif
3296
3297 return SCIP_OKAY;
3298 }
3299
3300 /** stores the LAB result for use in a later call to the branching rule */
3301 static
updateOldBranching(SCIP * scip,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_VAR * branchvar,SCIP_Real branchval,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3302 SCIP_RETCODE updateOldBranching(
3303 SCIP* scip, /**< SCIP data structure */
3304 PERSISTENTDATA* persistent, /**< data storage over multiple calls to the rule */
3305 CONFIGURATION* config, /**< the configuration of the branching rule */
3306 SCIP_VAR* branchvar, /**< variable to store previous results for */
3307 SCIP_Real branchval, /**< the value of branchvar */
3308 BRANCHINGRESULTDATA* downbranchingresult,/**< down branching result to store */
3309 BRANCHINGRESULTDATA* upbranchingresult, /**< up branching result to store */
3310 SCIP_Real lpobjval /**< base lp obj val */
3311 )
3312 {
3313 assert(scip != NULL);
3314 assert(persistent != NULL);
3315 assert(branchvar != NULL);
3316 assert(downbranchingresult != NULL);
3317 assert(upbranchingresult != NULL);
3318
3319 if( config->inscoring )
3320 {
3321 SCIP_Longint niterations = downbranchingresult->niterations + upbranchingresult->niterations;
3322
3323 SCIP_CALL( SCIPsetVarStrongbranchData(scip, branchvar, lpobjval, branchval, downbranchingresult->dualbound,
3324 upbranchingresult->dualbound, downbranchingresult->dualboundvalid, upbranchingresult->dualboundvalid, niterations,
3325 INT_MAX) );
3326 }
3327 else
3328 {
3329 int varindex = SCIPvarGetProbindex(branchvar);
3330
3331 branchingResultDataCopy(downbranchingresult, persistent->lastbranchdownres[varindex]);
3332 branchingResultDataCopy(upbranchingresult, persistent->lastbranchupres[varindex]);
3333 persistent->lastbranchlpobjval[varindex] = lpobjval;
3334 persistent->lastbranchid[varindex] = SCIPgetNNodes(scip);
3335 persistent->lastbranchnlps[varindex] = SCIPgetNLPs(scip);
3336 }
3337
3338 return SCIP_OKAY;
3339 }
3340
3341 /** calculates the FSB scores for the given candidates */
3342 static
getFSBResult(SCIP * scip,STATUS * status,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domainreductions,BINCONSDATA * binconsdata,CANDIDATELIST * candidatelist,BRANCHINGDECISION * decision,SCORECONTAINER * scorecontainer,LEVEL2DATA * level2data,SCIP_Real lpobjval,STATISTICS * statistics,LOCALSTATISTICS * localstats)3343 SCIP_RETCODE getFSBResult(
3344 SCIP* scip, /**< SCIP data structure */
3345 STATUS* status, /**< current status */
3346 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
3347 CONFIGURATION* config, /**< the configuration of the branching rule */
3348 SCIP_SOL* baselpsol, /**< base lp solution */
3349 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found; or NULL */
3350 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
3351 CANDIDATELIST* candidatelist, /**< list containing all candidates to consider */
3352 BRANCHINGDECISION* decision, /**< struct to store the final decision */
3353 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
3354 LEVEL2DATA* level2data, /**< level 2 LP results data */
3355 SCIP_Real lpobjval /**< base LP objective value */
3356 #ifdef SCIP_STATISTIC
3357 ,STATISTICS* statistics /**< general statistical data */
3358 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
3359 #endif
3360 )
3361 {
3362 assert(scip != NULL);
3363 assert(config != NULL);
3364 assert(candidatelist != NULL);
3365 assert(status != NULL);
3366 assert(scorecontainer != NULL);
3367 assert(SCIPinProbing(scip));
3368 assert(SCIPgetProbingDepth(scip) >= 0 && SCIPgetProbingDepth(scip) < config->recursiondepth);
3369
3370 /* inform configuration that we are in scoring mode now */
3371 config->inscoring = TRUE;
3372
3373 #ifdef SCIP_STATISTIC
3374 SCIP_CALL( selectVarRecursive(scip, status, persistent, config, baselpsol, domainreductions,
3375 binconsdata, candidatelist, decision, scorecontainer, level2data, 1,
3376 lpobjval, lpobjval, NULL, NULL, NULL, NULL, NULL, NULL,
3377 statistics, localstats, NULL, NULL) );
3378 #else
3379 SCIP_CALL( selectVarRecursive(scip, status, persistent, config, baselpsol, domainreductions,
3380 binconsdata, candidatelist, decision, scorecontainer, level2data, 1,
3381 lpobjval, lpobjval, NULL, NULL, NULL, NULL, NULL, NULL) );
3382 #endif
3383
3384 /* inform configuration that we leave scoring mode now */
3385 config->inscoring = FALSE;
3386
3387 return SCIP_OKAY;
3388 }
3389
3390 #ifdef SCIP_DEBUG
3391 /** prints the given candidate list */
3392 static
printCandidates(SCIP * scip,SCIP_VERBLEVEL lvl,CANDIDATELIST * candidatelist)3393 void printCandidates(
3394 SCIP* scip, /**< SCIP data structure */
3395 SCIP_VERBLEVEL lvl, /**< verbosity level to print the list in */
3396 CANDIDATELIST* candidatelist /**< the list to be printed */
3397 )
3398 {
3399 int ncands;
3400 int i;
3401
3402 assert(scip != NULL);
3403 assert(candidatelist != NULL);
3404
3405 ncands = candidatelist->ncandidates;
3406
3407 LABdebugMessagePrint(scip, lvl, "[");
3408
3409 for( i = 0; i < ncands; i++ )
3410 {
3411 CANDIDATE* cand = candidatelist->candidates[i];
3412
3413 assert(cand != NULL);
3414 assert(cand->branchvar != NULL);
3415
3416 LABdebugMessagePrint(scip, lvl, "%s", SCIPvarGetName(cand->branchvar));
3417 if(i != ncands-1)
3418 {
3419 LABdebugMessagePrint(scip, lvl, ", ");
3420 }
3421 }
3422 LABdebugMessagePrint(scip, lvl, "]\n");
3423 }
3424 #endif
3425
3426 /** calculates the score based on the down and up branching result */
3427 static
calculateScoreFromResult(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3428 SCIP_Real calculateScoreFromResult(
3429 SCIP* scip, /**< SCIP data structure */
3430 SCIP_VAR* branchvar, /**< variable to get the score for */
3431 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3432 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3433 SCIP_Real lpobjval /**< objective value to get difference to as gain */
3434 )
3435 {
3436 SCIP_Real score;
3437 SCIP_Real downgain = SCIPsumepsilon(scip);
3438 SCIP_Real upgain = SCIPsumepsilon(scip);
3439
3440 assert(scip != NULL);
3441 assert(branchvar != NULL);
3442 assert(downbranchingresult != NULL);
3443 assert(upbranchingresult != NULL);
3444
3445 /* the gain is the difference of the dualbound of a child and the reference objective value;
3446 * by bounding it by zero we are safe from numerical troubles
3447 */
3448 if( !downbranchingresult->cutoff )
3449 downgain = MAX(downgain, downbranchingresult->dualbound - lpobjval);
3450 if( !upbranchingresult->cutoff )
3451 upgain = MAX(upgain, upbranchingresult->dualbound - lpobjval);
3452
3453 downgain = 100.0 * downgain;
3454 upgain = 100.0 * upgain;
3455
3456 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3457 * realistic gain for the infeasible child;
3458 * if both children are infeasible we just reset the initial zero values again
3459 */
3460 if( downbranchingresult->cutoff )
3461 downgain = 2 * upgain;
3462 if( upbranchingresult->cutoff )
3463 upgain = 2 * downgain;
3464
3465 score = SCIPgetBranchScore(scip, branchvar, downgain, upgain);
3466
3467 return score;
3468 }
3469
3470 /** calculates the score based on the down and up branching result */
3471 static
calculateScoreFromResult2(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3472 SCIP_Real calculateScoreFromResult2(
3473 SCIP* scip, /**< SCIP data structure */
3474 SCIP_VAR* branchvar, /**< variable to get the score for */
3475 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3476 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3477 SCIP_Real lpobjval /**< objective value to get difference to as gain */
3478 )
3479 {
3480 SCIP_Real lpscore;
3481 SCIP_Real dbscore;
3482 SCIP_Real score;
3483 SCIP_Real downgain = SCIPsumepsilon(scip);
3484 SCIP_Real upgain = SCIPsumepsilon(scip);
3485
3486 assert(scip != NULL);
3487 assert(branchvar != NULL);
3488 assert(downbranchingresult != NULL);
3489 assert(upbranchingresult != NULL);
3490
3491 /* the gain is the difference of the dualbound of a child and the reference objective value;
3492 * by bounding it by zero we are safe from numerical troubles
3493 */
3494 if( !downbranchingresult->cutoff )
3495 downgain = MAX(downgain, downbranchingresult->objval - lpobjval);
3496 if( !upbranchingresult->cutoff )
3497 upgain = MAX(upgain, upbranchingresult->objval - lpobjval);
3498
3499 downgain = 100.0 * downgain;
3500 upgain = 100.0 * upgain;
3501
3502 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3503 * realistic gain for the infeasible child;
3504 * if both children are infeasible we just reset the initial zero values again
3505 */
3506 if( downbranchingresult->cutoff )
3507 downgain = 2 * upgain;
3508 if( upbranchingresult->cutoff )
3509 upgain = 2 * downgain;
3510
3511 lpscore = SCIPgetBranchScore(scip, branchvar, downgain, upgain);
3512
3513 /* the gain is the difference of the dualbound of a child and the reference objective value;
3514 * by bounding it by zero we are safe from numerical troubles
3515 */
3516 if( !downbranchingresult->cutoff )
3517 downgain = MAX(SCIPsumepsilon(scip), downbranchingresult->dualbound - lpobjval); /*lint !e666*/
3518 if( !upbranchingresult->cutoff )
3519 upgain = MAX(SCIPsumepsilon(scip), upbranchingresult->dualbound - lpobjval); /*lint !e666*/
3520
3521 downgain = 100.0 * downgain;
3522 upgain = 100.0 * upgain;
3523
3524 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3525 * realistic gain for the infeasible child;
3526 * if both children are infeasible we just reset the initial zero values again
3527 */
3528 if( downbranchingresult->cutoff )
3529 downgain = 2 * upgain;
3530 if( upbranchingresult->cutoff )
3531 upgain = 2 * downgain;
3532
3533 dbscore = SCIPgetBranchScore(scip, branchvar, downgain, upgain);
3534
3535 score = SCIPgetBranchScore(scip, branchvar, lpscore, dbscore);
3536
3537 return score;
3538 }
3539
3540 /** calculates the score based on the down and up branching scores */
3541 static
calculateScoreFromDeeperscore(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult)3542 SCIP_Real calculateScoreFromDeeperscore(
3543 SCIP* scip, /**< SCIP data structure */
3544 SCIP_VAR* branchvar, /**< variable to get the score for */
3545 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3546 BRANCHINGRESULTDATA* upbranchingresult /**< branching result of the up branch */
3547 )
3548 {
3549 SCIP_Real score;
3550 SCIP_Real downscore;
3551 SCIP_Real upscore;
3552
3553 assert(scip != NULL);
3554 assert(branchvar != NULL);
3555 assert(downbranchingresult != NULL);
3556 assert(upbranchingresult != NULL);
3557
3558 assert(downbranchingresult->deeperscore >= -0.2 || downbranchingresult->cutoff || SCIPisStopped(scip));
3559 assert(upbranchingresult->deeperscore >= -0.2 || upbranchingresult->cutoff || SCIPisStopped(scip));
3560
3561 downscore = sqrt(downbranchingresult->deeperscore);
3562 upscore = sqrt(upbranchingresult->deeperscore);
3563
3564 downscore = MAX(downscore, SCIPsumepsilon(scip)); /*lint !e666*/
3565 upscore = MAX(upscore, SCIPsumepsilon(scip)); /*lint !e666*/
3566
3567 if( downbranchingresult->cutoff )
3568 downscore = 2 * upscore;
3569 if( upbranchingresult->cutoff )
3570 upscore = 2 * downscore;
3571
3572 score = SCIPgetBranchScore(scip, branchvar, downscore, upscore);
3573
3574 return score;
3575 }
3576
3577 /** calculates the score based on the down and up branching scores */
3578 static
calculateScoreFromDeeperscoreAndCutoffs(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult)3579 SCIP_Real calculateScoreFromDeeperscoreAndCutoffs(
3580 SCIP* scip, /**< SCIP data structure */
3581 SCIP_VAR* branchvar, /**< variable to get the score for */
3582 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3583 BRANCHINGRESULTDATA* upbranchingresult /**< branching result of the up branch */
3584 )
3585 {
3586 SCIP_Real score;
3587 SCIP_Real downscore;
3588 SCIP_Real upscore;
3589 SCIP_Real totaldowngains;
3590 SCIP_Real totalupgains;
3591 SCIP_Real nlowestlevelcutoffs;
3592 int ntotaldowngains;
3593 int ntotalupgains;
3594
3595 assert(scip != NULL);
3596 assert(branchvar != NULL);
3597 assert(downbranchingresult != NULL);
3598 assert(upbranchingresult != NULL);
3599
3600 assert(downbranchingresult->deeperscore >= -0.2 || downbranchingresult->cutoff || SCIPisStopped(scip));
3601 assert(upbranchingresult->deeperscore >= -0.2 || upbranchingresult->cutoff || SCIPisStopped(scip));
3602
3603 nlowestlevelcutoffs = (1.0 * downbranchingresult->ndeepestcutoffs + upbranchingresult->ndeepestcutoffs)/(MAX(1,downbranchingresult->ndeepestnodes + upbranchingresult->ndeepestnodes));
3604 totaldowngains = downbranchingresult->totalgains;
3605 totalupgains = upbranchingresult->totalgains;
3606 ntotaldowngains = MAX(1, downbranchingresult->ntotalgains);
3607 ntotalupgains = MAX(1, upbranchingresult->ntotalgains);
3608
3609 downscore = sqrt(downbranchingresult->deeperscore);
3610 upscore = sqrt(upbranchingresult->deeperscore);
3611
3612 downscore = MAX(downscore, SCIPsumepsilon(scip)); /*lint !e666*/
3613 upscore = MAX(upscore, SCIPsumepsilon(scip)); /*lint !e666*/
3614
3615 if( downbranchingresult->cutoff )
3616 downscore = 2 * upscore;
3617 if( upbranchingresult->cutoff )
3618 upscore = 2 * downscore;
3619
3620 score = SCIPgetBranchScore(scip, branchvar, downscore, upscore);
3621
3622 downscore = sqrt(totaldowngains/ntotaldowngains);
3623 upscore = sqrt(totalupgains/ntotalupgains);
3624
3625 downscore = MAX(downscore, SCIPsumepsilon(scip)); /*lint !e666*/
3626 upscore = MAX(upscore, SCIPsumepsilon(scip)); /*lint !e666*/
3627
3628 score += SCIPgetBranchScore(scip, branchvar, downscore, upscore)*nlowestlevelcutoffs;
3629
3630 return score;
3631 }
3632
3633 /** calculates the combined gain, weighted with parameters given by the user configuration */
3634 static
calculateWeightedGain(SCIP * scip,CONFIGURATION * config,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3635 SCIP_Real calculateWeightedGain(
3636 SCIP* scip, /**< SCIP data structure */
3637 CONFIGURATION* config, /**< LAB configuration */
3638 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3639 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3640 SCIP_Real lpobjval /**< objective value to get difference to as gain */
3641 )
3642 {
3643 SCIP_Real downgain = 0.0;
3644 SCIP_Real upgain = 0.0;
3645
3646 assert(config != NULL);
3647 assert(downbranchingresult != NULL);
3648 assert(upbranchingresult != NULL);
3649
3650 /* the gain is the difference of the dualbound of a child and the reference objective value;
3651 * by bounding it by zero we are safe from numerical troubles
3652 */
3653 if( !downbranchingresult->cutoff )
3654 downgain = MAX(0, downbranchingresult->dualbound - lpobjval);
3655 if( !upbranchingresult->cutoff )
3656 upgain = MAX(0, upbranchingresult->dualbound - lpobjval);
3657
3658 if( config->scoringfunction == 's' )
3659 {
3660 if( downbranchingresult->cutoff )
3661 downgain = SCIPinfinity(scip);
3662 if( upbranchingresult->cutoff )
3663 upgain = SCIPinfinity(scip);
3664 }
3665 else
3666 {
3667 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3668 * realistic gain for the infeasible child;
3669 * if both children are infeasible we just reset the initial zero values again
3670 */
3671 if( downbranchingresult->cutoff )
3672 downgain = upgain;
3673 if( upbranchingresult->cutoff )
3674 upgain = downgain;
3675 }
3676
3677 return config->minweight * MIN(downgain, upgain) + (1.0 - config->minweight) * MAX(downgain, upgain);
3678 }
3679
3680 /** calculates the score as mentioned in the lookahead branching paper by Glankwamdee and Linderoth;
3681 * their score scales the number of cutoffs on the last layer of a 2-level temporary branching tree with the average gain of
3682 * every last level problem; together with the best gain for each branch of a variable we get the final score
3683 */
3684 static
calculateScaledCutoffScore(BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult)3685 SCIP_Real calculateScaledCutoffScore(
3686 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3687 BRANCHINGRESULTDATA* upbranchingresult /**< branching result of the up branch */
3688 )
3689 {
3690 SCIP_Real bestdowngain;
3691 SCIP_Real bestupgain;
3692 SCIP_Real totaldowngains;
3693 SCIP_Real totalupgains;
3694 int nlowestlevelcutoffs;
3695 int ntotaldowngains;
3696 int ntotalupgains;
3697
3698 assert(downbranchingresult != NULL);
3699 assert(upbranchingresult != NULL);
3700
3701 nlowestlevelcutoffs = downbranchingresult->ndeepestcutoffs + upbranchingresult->ndeepestcutoffs;
3702 bestdowngain = downbranchingresult->bestgain;
3703 bestupgain = upbranchingresult->bestgain;
3704 totaldowngains = downbranchingresult->totalgains;
3705 totalupgains = upbranchingresult->totalgains;
3706 ntotaldowngains = MAX(1, downbranchingresult->ntotalgains);
3707 ntotalupgains = MAX(1, upbranchingresult->ntotalgains);
3708
3709 return bestdowngain + bestupgain + (totaldowngains/ntotaldowngains + totalupgains/ntotalupgains)*nlowestlevelcutoffs;
3710 }
3711
3712 /** calculates the score as mentioned in the lookahead branching paper by Glankwamdee and Linderoth;
3713 * their score scales the number of cutoffs on the last layer of a 2-level temporary branching tree with the average gain of
3714 * every last level problem; together with the best gain for each branch of a variable we get the final score
3715 */
3716 static
calculateWeightedCutoffScore(CONFIGURATION * config,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult)3717 SCIP_Real calculateWeightedCutoffScore(
3718 CONFIGURATION* config, /**< LAB configuration */
3719 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3720 BRANCHINGRESULTDATA* upbranchingresult /**< branching result of the up branch */
3721 )
3722 {
3723 SCIP_Real bestdowngain;
3724 SCIP_Real bestupgain;
3725 SCIP_Real totaldowngains;
3726 SCIP_Real totalupgains;
3727 SCIP_Real nlowestlevelcutoffs;
3728 int ntotaldowngains;
3729 int ntotalupgains;
3730
3731 assert(downbranchingresult != NULL);
3732 assert(upbranchingresult != NULL);
3733
3734 nlowestlevelcutoffs = (1.0 * downbranchingresult->ndeepestcutoffs + upbranchingresult->ndeepestcutoffs)/(downbranchingresult->ndeepestnodes + upbranchingresult->ndeepestnodes);
3735 bestdowngain = downbranchingresult->bestgain;
3736 bestupgain = upbranchingresult->bestgain;
3737 totaldowngains = downbranchingresult->totalgains;
3738 totalupgains = upbranchingresult->totalgains;
3739 ntotaldowngains = MAX(1, downbranchingresult->ntotalgains);
3740 ntotalupgains = MAX(1, upbranchingresult->ntotalgains);
3741
3742 return config->minweight*MIN(bestdowngain, bestupgain) + (1.0 - config->minweight)*MAX(bestdowngain, bestupgain) + (totaldowngains/ntotaldowngains + totalupgains/ntotalupgains)*nlowestlevelcutoffs;
3743 }
3744
3745 static
calculateCutoffScore(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3746 SCIP_Real calculateCutoffScore(
3747 SCIP* scip, /**< SCIP data structure */
3748 SCIP_VAR* branchvar, /**< variable to get the score for */
3749 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3750 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3751 SCIP_Real lpobjval /**< objective value to get difference to as gain */
3752 )
3753 {
3754 SCIP_Real score;
3755 SCIP_Real downgain = SCIPsumepsilon(scip);
3756 SCIP_Real upgain = SCIPsumepsilon(scip);
3757 SCIP_Real gap;
3758 int nlowestlevelcutoffs;
3759
3760 assert(downbranchingresult != NULL);
3761 assert(upbranchingresult != NULL);
3762
3763 nlowestlevelcutoffs = 0;
3764
3765 /* the gain is the difference of the dualbound of a child and the reference objective value;
3766 * by bounding it by zero we are safe from numerical troubles
3767 */
3768 if( !downbranchingresult->cutoff )
3769 {
3770 nlowestlevelcutoffs += downbranchingresult->ndeepestcutoffs;
3771 downgain = MAX(downgain, downbranchingresult->dualbound - lpobjval);
3772 }
3773 if( !upbranchingresult->cutoff )
3774 {
3775 nlowestlevelcutoffs += upbranchingresult->ndeepestcutoffs;
3776 upgain = MAX(upgain, upbranchingresult->dualbound - lpobjval);
3777 }
3778
3779 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3780 * realistic gain for the infeasible child;
3781 * if both children are infeasible we just reset the initial zero values again
3782 */
3783 if( downbranchingresult->cutoff )
3784 {
3785 nlowestlevelcutoffs += 2 * SCIPgetNPseudoBranchCands(scip);
3786 downgain = 2 * upgain;
3787 }
3788 if( upbranchingresult->cutoff )
3789 {
3790 nlowestlevelcutoffs += 2 * SCIPgetNPseudoBranchCands(scip);
3791 upgain = 2 * downgain;
3792 }
3793
3794 gap = SCIPgetCutoffbound(scip) - lpobjval;
3795
3796 downgain = downgain/gap;
3797 upgain = upgain/gap;
3798
3799 score = 1.0 * nlowestlevelcutoffs + SCIPgetBranchScore(scip, branchvar, downgain, upgain);
3800
3801 return score;
3802 }
3803
3804 static
calculateRelCutoffScore(SCIP * scip,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval)3805 SCIP_Real calculateRelCutoffScore(
3806 SCIP* scip, /**< SCIP data structure */
3807 SCIP_VAR* branchvar, /**< variable to get the score for */
3808 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3809 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3810 SCIP_Real lpobjval /**< objective value to get difference to as gain */
3811 )
3812 {
3813 SCIP_Real score;
3814 SCIP_Real downgain = SCIPsumepsilon(scip);
3815 SCIP_Real upgain = SCIPsumepsilon(scip);
3816 SCIP_Real gap;
3817 int factor;
3818 SCIP_Real nlowestlevelcutoffs;
3819
3820 assert(downbranchingresult != NULL);
3821 assert(upbranchingresult != NULL);
3822
3823 assert(downbranchingresult->ndeepestnodes + upbranchingresult->ndeepestnodes > 0 || (downbranchingresult->cutoff && upbranchingresult->cutoff));
3824
3825 nlowestlevelcutoffs = (1.0 * downbranchingresult->ndeepestcutoffs + upbranchingresult->ndeepestcutoffs)/(1 + downbranchingresult->ndeepestnodes + upbranchingresult->ndeepestnodes);
3826
3827 factor = SCIPgetNPseudoBranchCands(scip);
3828 if( factor > SCIPgetNLPRows(scip) )
3829 factor = SCIPgetNLPRows(scip);
3830 factor = factor * factor;
3831
3832 /* the gain is the difference of the dualbound of a child and the reference objective value;
3833 * by bounding it by zero we are safe from numerical troubles
3834 */
3835 if( !downbranchingresult->cutoff )
3836 {
3837 downgain = MAX(downgain, downbranchingresult->dualbound - lpobjval);
3838 }
3839 if( !upbranchingresult->cutoff )
3840 {
3841 upgain = MAX(upgain, upbranchingresult->dualbound - lpobjval);
3842 }
3843
3844 /* in case a child is infeasible and therefore cutoff we take the gain of the other child to receive a somewhat
3845 * realistic gain for the infeasible child;
3846 * if both children are infeasible we just reset the initial zero values again
3847 */
3848 if( downbranchingresult->cutoff )
3849 {
3850 downgain = 2 * upgain;
3851 }
3852 if( upbranchingresult->cutoff )
3853 {
3854 upgain = 2 * downgain;
3855 }
3856
3857 gap = SCIPgetCutoffbound(scip) - lpobjval;
3858
3859 downgain = downgain/gap;
3860 upgain = upgain/gap;
3861
3862 score = factor * nlowestlevelcutoffs + SCIPgetBranchScore(scip, branchvar, downgain, upgain);
3863
3864 return score;
3865 }
3866
3867 /** scoring method that selects an actual scoring method based on the user configuration */
3868 static
calculateScore(SCIP * scip,CONFIGURATION * config,SCIP_VAR * branchvar,BRANCHINGRESULTDATA * downbranchingresult,BRANCHINGRESULTDATA * upbranchingresult,SCIP_Real lpobjval,SCIP_Real baselpobjval)3869 SCIP_Real calculateScore(
3870 SCIP* scip, /**< SCIP data structure */
3871 CONFIGURATION* config, /**< LAB configuration */
3872 SCIP_VAR* branchvar, /**< variable to get the score for */
3873 BRANCHINGRESULTDATA* downbranchingresult,/**< branching result of the down branch */
3874 BRANCHINGRESULTDATA* upbranchingresult, /**< branching result of the up branch */
3875 SCIP_Real lpobjval, /**< objective value to get difference to as gain */
3876 SCIP_Real baselpobjval /**< base objective value to get difference to as gain */
3877 )
3878 {
3879 SCIP_Real score;
3880 char scoringfunction;
3881
3882 assert(scip != NULL);
3883 assert(config != NULL);
3884 assert(branchvar != NULL);
3885 assert(downbranchingresult != NULL);
3886 assert(upbranchingresult != NULL);
3887
3888 if( config->inscoring )
3889 scoringfunction = config->scoringscoringfunction;
3890 else if( SCIPgetProbingDepth(scip) > 0 )
3891 scoringfunction = config->deeperscoringfunction;
3892 else
3893 scoringfunction = config->scoringfunction;
3894
3895 switch( scoringfunction )
3896 {
3897 case 's':
3898 score = calculateScaledCutoffScore(downbranchingresult, upbranchingresult);
3899 break;
3900 case 'w':
3901 score = calculateWeightedCutoffScore(config, downbranchingresult, upbranchingresult);
3902 break;
3903 case 'f':
3904 score = calculateWeightedGain(scip, config, downbranchingresult, upbranchingresult, baselpobjval);
3905 break;
3906 case 'p':
3907 score = calculateScoreFromDeeperscore(scip, branchvar, downbranchingresult, upbranchingresult);
3908 break;
3909 case 'a':
3910 score = calculateScoreFromDeeperscoreAndCutoffs(scip, branchvar, downbranchingresult, upbranchingresult);
3911 break;
3912 case 'l':
3913 score = calculateScoreFromResult2(scip, branchvar, downbranchingresult, upbranchingresult, lpobjval);
3914 break;
3915 case 'c':
3916 score = calculateCutoffScore(scip, branchvar, downbranchingresult, upbranchingresult, lpobjval);
3917 break;
3918 case 'r':
3919 score = calculateRelCutoffScore(scip, branchvar, downbranchingresult, upbranchingresult, lpobjval);
3920 break;
3921 case 'x':
3922 score = calculateScoreFromResult(scip, branchvar, downbranchingresult, upbranchingresult, baselpobjval);
3923 break;
3924 default:
3925 assert(scoringfunction == 'd');
3926 score = calculateScoreFromResult(scip, branchvar, downbranchingresult, upbranchingresult, lpobjval);
3927 }
3928
3929 return score;
3930 }
3931
3932 /** calculates the score based on the pseudocosts of the given variable */
3933 static
calculateScoreFromPseudocosts(SCIP * scip,CANDIDATE * lpcand)3934 SCIP_Real calculateScoreFromPseudocosts(
3935 SCIP* scip, /**< SCIP data structure */
3936 CANDIDATE* lpcand /**< candidate to get the score for */
3937 )
3938 {
3939 SCIP_Real downpseudocost;
3940 SCIP_Real uppseudocost;
3941 SCIP_Real score;
3942
3943 assert(scip != NULL);
3944 assert(lpcand != NULL);
3945
3946 downpseudocost = SCIPgetVarPseudocostVal(scip, lpcand->branchvar, 0-lpcand->fracval);
3947 uppseudocost = SCIPgetVarPseudocostVal(scip, lpcand->branchvar, 1-lpcand->fracval);
3948
3949 score = SCIPgetBranchScore(scip, lpcand->branchvar, downpseudocost, uppseudocost);
3950
3951 return score;
3952 }
3953
3954 #ifdef SCIP_DEBUG
3955 /** prints the names of the candidates of the given candidate list with their corresponding scores */
3956 static
printCandidateList(SCIP * scip,CANDIDATELIST * candidatelist,SCORECONTAINER * scorecontainer)3957 void printCandidateList(
3958 SCIP* scip, /**< SCIP data structure */
3959 CANDIDATELIST* candidatelist, /**< list to be printed */
3960 SCORECONTAINER* scorecontainer /**< container with all scores */
3961 )
3962 {
3963 int i;
3964
3965 assert(scip != NULL);
3966 assert(candidatelist != NULL);
3967 assert(scorecontainer != NULL);
3968
3969 for( i = 0; i < candidatelist->ncandidates; i++ )
3970 {
3971 SCIP_VAR* var = candidatelist->candidates[i]->branchvar;
3972 SCIP_Real score = scorecontainer->scores[SCIPvarGetProbindex(var)];
3973
3974 assert(var != NULL);
3975
3976 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, " Index %2i: Var %s Score %.9g\n", i, SCIPvarGetName(var), score);
3977 }
3978 }
3979 #endif
3980
3981 /** sorts the best candidates (w.r.t. the score in the container) of the candidate list to the front of the list */
3982 static
sortFirstCandidatesByScore(SCIP * scip,CANDIDATELIST * candidatelist,SCORECONTAINER * scorecontainer,int nbestcandidates)3983 void sortFirstCandidatesByScore(
3984 SCIP* scip, /**< SCIP data structure */
3985 CANDIDATELIST* candidatelist, /**< candidates to be sorted */
3986 SCORECONTAINER* scorecontainer, /**< container with the scores for each candidate */
3987 int nbestcandidates /**< number of candidates that should be kept sorted at the start of the list*/
3988 )
3989 {
3990 int i;
3991
3992 assert(scip != NULL);
3993 assert(candidatelist != NULL);
3994 assert(scorecontainer != NULL);
3995 assert(candidatelist->ncandidates > 0);
3996 assert(nbestcandidates <= candidatelist->ncandidates);
3997
3998 for( i = 1; i < candidatelist->ncandidates; i++ )
3999 {
4000 CANDIDATE* movecand = candidatelist->candidates[i];
4001 int moveprobindex;
4002 SCIP_Real movescore;
4003 int nsorted;
4004 int insertionindex;
4005 assert(movecand != NULL);
4006
4007 moveprobindex = SCIPvarGetProbindex(movecand->branchvar);
4008 movescore = scorecontainer->scores[moveprobindex];
4009
4010 /* the length of the sorted portion of the array, starting at 0 */
4011 nsorted = MIN(i, nbestcandidates);
4012
4013 insertionindex = findInsertionPoint(scip, scorecontainer, movescore, candidatelist->candidates, nsorted);
4014
4015 assert(insertionindex <= nsorted);
4016
4017 /* if no change has to be made, skip the reordering;
4018 * if the insertionindex lies after the sorted block, skip the reordering
4019 */
4020 if( insertionindex != i && insertionindex < nsorted )
4021 {
4022 int j;
4023 CANDIDATE* reordercand = movecand;
4024
4025 /* move everything inside the sorted block one place further */
4026 for( j = insertionindex; j < nsorted; j++ )
4027 {
4028 CANDIDATE* oldcand = candidatelist->candidates[j];
4029 assert(oldcand != NULL);
4030
4031 candidatelist->candidates[j] = reordercand;
4032 reordercand = oldcand;
4033 }
4034 /* the dropped element gets placed in the position of the actually moved element */
4035 candidatelist->candidates[i] = reordercand;
4036 }
4037 }
4038
4039 #ifdef SCIP_DEBUG
4040 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "All %i candidates, with the first %i candidates sorted by their FSB score:"
4041 "\n", candidatelist->ncandidates, nbestcandidates);
4042 printCandidateList(scip, candidatelist, scorecontainer);
4043 #endif
4044 }
4045
4046 /** checks whether the given candidates is reliable, so that its pseudocosts may be used */
4047 static
isCandidateReliable(SCIP * scip,SCIP_VAR * branchvar)4048 SCIP_Bool isCandidateReliable(
4049 SCIP* scip, /**< SCIP data structure */
4050 SCIP_VAR* branchvar /**< var to check for reliability */
4051 )
4052 {
4053 SCIP_Real size;
4054 SCIP_Real downsize;
4055 SCIP_Real upsize;
4056 SCIP_Real reliable = 5;
4057
4058 assert(scip != NULL);
4059 assert(branchvar != NULL);
4060
4061 downsize = SCIPgetVarPseudocostCountCurrentRun(scip, branchvar, SCIP_BRANCHDIR_DOWNWARDS);
4062 upsize = SCIPgetVarPseudocostCountCurrentRun(scip, branchvar, SCIP_BRANCHDIR_UPWARDS);
4063 size = MIN(downsize, upsize);
4064
4065 return size >= reliable;
4066 }
4067
4068 /** checks whether the current problem is feasible or cutoff */
4069 static
isCurrentNodeCutoff(SCIP * scip)4070 SCIP_Bool isCurrentNodeCutoff(
4071 SCIP* scip /**< SCIP data structure */
4072 )
4073 {
4074 assert(scip != NULL);
4075
4076 return (SCIPgetCutoffdepth(scip) <= SCIPgetDepth(scip));
4077 }
4078
4079 /** Ensures that the scores are present in the scorecontainer for each of the candidates to consider */
4080 static
ensureScoresPresent(SCIP * scip,STATUS * status,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domainreductions,BINCONSDATA * binconsdata,CANDIDATELIST * allcandidates,BRANCHINGDECISION * decision,SCORECONTAINER * scorecontainer,LEVEL2DATA * level2data,SCIP_Real lpobjval,STATISTICS * statistics,LOCALSTATISTICS * localstats)4081 SCIP_RETCODE ensureScoresPresent(
4082 SCIP* scip, /**< SCIP data structure */
4083 STATUS* status, /**< current status */
4084 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
4085 CONFIGURATION* config, /**< the configuration of the branching rule */
4086 SCIP_SOL* baselpsol, /**< base lp solution */
4087 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found; or NULL */
4088 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
4089 CANDIDATELIST* allcandidates, /**< list containing all candidates to consider */
4090 BRANCHINGDECISION* decision, /**< struct to store the final decision */
4091 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
4092 LEVEL2DATA* level2data, /**< level 2 LP results data */
4093 SCIP_Real lpobjval /**< base LP objective value */
4094 #ifdef SCIP_STATISTIC
4095 ,STATISTICS* statistics /**< general statistical data */
4096 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
4097 #endif
4098 )
4099 {
4100 int i;
4101 int nunscoredcandidates = 0;
4102 int* candidateunscored;
4103
4104 assert(scip != NULL);
4105 assert(config != NULL);
4106 assert(status != NULL);
4107 assert(allcandidates != NULL);
4108 assert(scorecontainer != NULL);
4109 assert(allcandidates->candidates != NULL || allcandidates->ncandidates == 0);
4110
4111 SCIP_CALL( SCIPallocBufferArray(scip, &candidateunscored, allcandidates->ncandidates) );
4112
4113 /* filter the candidates based on the presence of a score in the 'scorecontainer'. Only those without a score need a
4114 * new one.
4115 */
4116 for( i = 0; i < allcandidates->ncandidates; i++ )
4117 {
4118 CANDIDATE* lpcand = allcandidates->candidates[i];
4119 SCIP_VAR* branchvar = lpcand->branchvar;
4120 int probindex = SCIPvarGetProbindex(branchvar);
4121 SCIP_Real knownscore = scorecontainer->scores[probindex];
4122
4123 assert(lpcand != NULL);
4124 assert(branchvar != NULL);
4125
4126 if( SCIPisLT(scip, knownscore, 0.0) )
4127 {
4128 if( config->abbrevpseudo && isCandidateReliable(scip, branchvar) )
4129 {
4130 SCIP_Real score = calculateScoreFromPseudocosts(scip, lpcand);
4131 SCIP_CALL( scoreContainerSetScore(scip, scorecontainer, lpcand, score, 0.0, 0.0) );
4132 }
4133 else if( config->level2avgscore && SCIPgetProbingDepth(scip) > 0 )
4134 {
4135 assert(scorecontainer->nsetscores > 0);
4136 SCIP_CALL( scoreContainerSetScore(scip, scorecontainer, lpcand,
4137 scorecontainer->scoresum / scorecontainer->nsetscores, 0.0, 0.0) );
4138 }
4139 else if( config->level2zeroscore && SCIPgetProbingDepth(scip) > 0 )
4140 {
4141 assert(scorecontainer->nsetscores > 0);
4142 SCIP_CALL( scoreContainerSetScore(scip, scorecontainer, lpcand,
4143 -0.1, 0.0, 0.0) );
4144 }
4145 else
4146 {
4147 /* score is unknown and needs to be calculated */
4148 candidateunscored[nunscoredcandidates] = i;
4149 nunscoredcandidates++;
4150 }
4151 }
4152 }
4153
4154 if( nunscoredcandidates > 0 )
4155 {
4156 CANDIDATELIST* unscoredcandidates;
4157
4158 /* allocate the list of candidates without any score (gets updated further on) */
4159 SCIP_CALL( candidateListCreate(scip, &unscoredcandidates, nunscoredcandidates) );
4160
4161 /* move the unscored candidates to the temp list */
4162 for( i = 0; i < nunscoredcandidates; i++ )
4163 {
4164 int candindex = candidateunscored[i];
4165
4166 assert(allcandidates->candidates[candindex] != NULL);
4167
4168 unscoredcandidates->candidates[i] = allcandidates->candidates[candindex];
4169 }
4170
4171 #ifdef SCIP_DEBUG
4172 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Of the given %i candidates, %i have no score: ",
4173 allcandidates->ncandidates, nunscoredcandidates);
4174 printCandidates(scip, SCIP_VERBLEVEL_HIGH, unscoredcandidates);
4175 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Calculating the FSB result to get a score for the remaining "
4176 "candidates.\n");
4177 #endif
4178
4179 /* Calculate all remaining FSB scores and collect the scores in the container */;
4180 #ifdef SCIP_STATISTIC
4181 SCIP_CALL( getFSBResult(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, unscoredcandidates,
4182 decision, scorecontainer, level2data, lpobjval, statistics, localstats) );
4183 #else
4184 SCIP_CALL( getFSBResult(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, unscoredcandidates,
4185 decision, scorecontainer, level2data, lpobjval) );
4186 #endif
4187
4188 /* move the now scored candidates back to the original list */
4189 for( i = 0; i < nunscoredcandidates; i++ )
4190 {
4191 assert(allcandidates->candidates[candidateunscored[i]] == unscoredcandidates->candidates[i]);
4192
4193 assert(unscoredcandidates->candidates[i] != NULL);
4194 unscoredcandidates->candidates[i] = NULL;
4195 }
4196
4197 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Calculated the scores for the remaining candidates\n");
4198
4199 SCIP_CALL( candidateListFree(scip, &unscoredcandidates) );
4200 }
4201
4202 /* reset the best sorted indices, as those are only valid on the FSB run already completed */
4203 scoreContainterResetBestSortedCands(scorecontainer);
4204
4205 SCIPfreeBufferArray(scip, &candidateunscored);
4206
4207 return SCIP_OKAY;
4208 }
4209
4210 /** Get the candidates to temporarily branch on. In the LAB case this is the complete list of possible candidates. In the
4211 * ALAB case only the 'best' candidates are returned. */
4212 static
filterCandidates(SCIP * scip,STATUS * status,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domainreductions,BINCONSDATA * binconsdata,CANDIDATELIST * candidatelist,BRANCHINGDECISION * decision,SCORECONTAINER * scorecontainer,LEVEL2DATA * level2data,SCIP_Real lpobjval,STATISTICS * statistics,LOCALSTATISTICS * localstats)4213 SCIP_RETCODE filterCandidates(
4214 SCIP* scip, /**< SCIP data structure */
4215 STATUS* status, /**< current status */
4216 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
4217 CONFIGURATION* config, /**< the configuration of the branching rule */
4218 SCIP_SOL* baselpsol, /**< base lp solution */
4219 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found; or NULL */
4220 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
4221 CANDIDATELIST* candidatelist, /**< list of candidates to branch on */
4222 BRANCHINGDECISION* decision, /**< struct to store the final decision */
4223 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
4224 LEVEL2DATA* level2data, /**< level 2 LP results data */
4225 SCIP_Real lpobjval /**< base LP objective value */
4226 #ifdef SCIP_STATISTIC
4227 ,STATISTICS* statistics /**< general statistical data */
4228 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
4229 #endif
4230 )
4231 {
4232 assert(scip != NULL);
4233 assert(config != NULL);
4234 assert(status != NULL);
4235 assert(candidatelist != NULL);
4236 assert(SCIPinProbing(scip));
4237
4238 /* abbreviated LAB: only use the "best" candidates */
4239 if( config->abbreviated )
4240 {
4241 assert(scorecontainer != NULL);
4242
4243 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Getting the best (at most) %i of the given %i candidates: ",
4244 config->maxncands, candidatelist->ncandidates);
4245 #ifdef SCIP_DEBUG
4246 printCandidates(scip, SCIP_VERBLEVEL_HIGH, candidatelist);
4247 #endif
4248
4249 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "%s", "Ensuring that all candidates have a score.\n");
4250 #ifdef SCIP_STATISTIC
4251 SCIP_CALL( ensureScoresPresent(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
4252 decision, scorecontainer, level2data, lpobjval, statistics, localstats) );
4253 #else
4254 SCIP_CALL( ensureScoresPresent(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
4255 decision, scorecontainer, level2data, lpobjval) );
4256 #endif
4257
4258 /* if we didn't find any domreds or constraints during the FSB scoring, we branch on */
4259 if( isBranchFurther(status, SCIPgetProbingDepth(scip) == 0) )
4260 {
4261 int nusedcands;
4262 int i;
4263
4264 if( SCIPgetProbingDepth(scip) == 0 || config->maxndeepercands == 0 )
4265 nusedcands = MIN(config->maxncands, candidatelist->ncandidates);
4266 else
4267 nusedcands = MIN(config->maxndeepercands, candidatelist->ncandidates);
4268
4269 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "%s", "Filter the candidates by their score.\n");
4270
4271 sortFirstCandidatesByScore(scip, candidatelist, scorecontainer, nusedcands);
4272
4273 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Best candidate according to FSB scores: <%s>\n",
4274 SCIPvarGetName(candidatelist->candidates[0]->branchvar));
4275
4276 if( config->worsefactor >= 0 )
4277 {
4278 for( i = 1; i < nusedcands; ++i )
4279 {
4280 if( scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[0]->branchvar)] >
4281 config->worsefactor * scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[i]->branchvar)] )
4282 break;
4283 }
4284 nusedcands = i;
4285 }
4286
4287 if( config->filterbymaxgain && SCIPgetProbingDepth(scip) == 0 )
4288 {
4289 SCIP_Real maxgain;
4290 SCIP_Real bestmaxgain = MAX(scorecontainer->downgains[SCIPvarGetProbindex(candidatelist->candidates[0]->branchvar)],
4291 scorecontainer->upgains[SCIPvarGetProbindex(candidatelist->candidates[0]->branchvar)]); /*lint !e666*/
4292
4293 if( bestmaxgain == 0.0 )
4294 nusedcands = 1;
4295 else
4296 {
4297 for( i = nusedcands - 1; i >= 1; --i )
4298 {
4299 maxgain = MAX(scorecontainer->downgains[SCIPvarGetProbindex(candidatelist->candidates[i]->branchvar)],
4300 scorecontainer->upgains[SCIPvarGetProbindex(candidatelist->candidates[i]->branchvar)]); /*lint !e666*/
4301
4302 if( SCIPisSumLE(scip, maxgain / bestmaxgain, 1.0) )
4303 {
4304 --nusedcands;
4305
4306 if( i < nusedcands )
4307 {
4308 CANDIDATE* tmp = candidatelist->candidates[i];
4309 candidatelist->candidates[i] = candidatelist->candidates[nusedcands];
4310 candidatelist->candidates[nusedcands] = tmp;
4311 }
4312 }
4313 }
4314 }
4315 }
4316
4317 if( SCIPgetProbingDepth(scip) > 0 && scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[0]->branchvar)] > -0.05)
4318 {
4319 for( i = 1; i < nusedcands; ++i )
4320 {
4321 if( scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[i]->branchvar)] < -0.05 )
4322 break;
4323 }
4324 nusedcands = i;
4325 }
4326
4327 SCIP_CALL( candidateListKeep(scip, candidatelist, nusedcands) );
4328 }
4329 #ifdef SCIP_DEBUG
4330 else
4331 {
4332 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Strong Branching would have stopped.\n");
4333 }
4334 #endif
4335
4336 if( isCurrentNodeCutoff(scip) )
4337 status->cutoff = TRUE;
4338 }
4339 else
4340 {
4341 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Getting the branching candidates by selecting all candidates.\n");
4342 }
4343
4344 return SCIP_OKAY;
4345 }
4346
4347 /** Executes the general branching on a variable in a given direction (up/down) and repeats the algorithm from the new node */
4348 static
executeBranchingRecursive(SCIP * scip,STATUS * status,CONFIGURATION * config,SCIP_SOL * baselpsol,CANDIDATE * candidate,SCIP_Real localbaselpsolval,SCIP_Real baselpobjval,int recursiondepth,DOMAINREDUCTIONS * domainreductions,BINCONSDATA * binconsdata,LEVEL2DATA * level2data,BRANCHINGRESULTDATA * branchingresult,SCORECONTAINER * scorecontainer,SCIP_Bool downbranching,STATISTICS * statistics,LOCALSTATISTICS * localstats)4349 SCIP_RETCODE executeBranchingRecursive(
4350 SCIP* scip, /**< SCIP data structure */
4351 STATUS* status, /**< current status */
4352 CONFIGURATION* config, /**< the configuration of the branching rule */
4353 SCIP_SOL* baselpsol, /**< the base lp solution */
4354 CANDIDATE* candidate, /**< candidate to branch on */
4355 SCIP_Real localbaselpsolval, /**< the objective value of the current temporary problem */
4356 SCIP_Real baselpobjval, /**< LP objective value of focus node (not probing) */
4357 int recursiondepth, /**< remaining recursion depth */
4358 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found; or NULL */
4359 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
4360 LEVEL2DATA* level2data, /**< level 2 LP results data */
4361 BRANCHINGRESULTDATA* branchingresult, /**< container to store the result of the branching in */
4362 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
4363 SCIP_Bool downbranching /**< should we branch up or down in here? */
4364 #ifdef SCIP_STATISTIC
4365 ,STATISTICS* statistics /**< general statistical data */
4366 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
4367 #endif
4368 )
4369 {
4370 int probingdepth;
4371 SCIP_VAR* branchvar;
4372 SCIP_Real branchvalfrac;
4373 SCIP_Real branchval;
4374 SCIP_Bool varisbinary;
4375 SCIP_Bool solvedlp = TRUE;
4376
4377 assert(scip != NULL);
4378 assert(status != NULL);
4379 assert(config != NULL);
4380 assert(candidate != NULL);
4381 assert(branchingresult != NULL);
4382
4383 branchvar = candidate->branchvar;
4384 branchvalfrac = candidate->fracval;
4385 branchval = candidate->branchval;
4386
4387 assert(branchvar != NULL);
4388
4389 probingdepth = SCIPgetProbingDepth(scip);
4390 varisbinary = SCIPvarIsBinary(branchvar);
4391
4392 if( binconsdata != NULL && varisbinary )
4393 {
4394 if( downbranching )
4395 {
4396 /* In case that the branch variable is binary, add the negated var to the list.
4397 * This list is used to generate a set packing constraint for cutoff branches which were reached by only using
4398 * binary variables.
4399 * DownBranching on a binary variable x means: x <= 0
4400 * When this cutoff occurs we have that: x >= 1 <=> 1-x <= 0
4401 */
4402 SCIP_VAR* negbranchvar;
4403
4404 SCIP_CALL( SCIPgetNegatedVar(scip, branchvar, &negbranchvar) );
4405
4406 assert(negbranchvar != NULL);
4407
4408 binaryVarListAppend(scip, binconsdata->binaryvars, negbranchvar);
4409 }
4410 else
4411 {
4412 /* In case that the branch variable is binary, add the var to the list.
4413 * This list is used to generate a set packing constraint for cutoff branches which were reached by only using
4414 * binary variables.
4415 * UpBranching on a binary variable x means: x >= 1
4416 * When this cutoff occurs we have that: x <= 0
4417 */
4418 binaryVarListAppend(scip, binconsdata->binaryvars, branchvar);
4419 }
4420 }
4421
4422 if( level2data != NULL )
4423 {
4424 SCIP_Real newbound = downbranching ? SCIPfeasFloor(scip, branchval) : SCIPfeasCeil(scip, branchval);
4425
4426 if( SCIPgetProbingDepth(scip) == 0 )
4427 {
4428 assert(SCIPvarGetProbindex(branchvar) >= 0);
4429 level2data->branchvar1 = (unsigned int) SCIPvarGetProbindex(branchvar);
4430 level2data->branchdir1 = !downbranching;
4431 level2data->branchval1 = newbound;
4432 }
4433 else
4434 {
4435 LEVEL2RESULT* result;
4436
4437 assert(SCIPgetProbingDepth(scip) == 1);
4438 assert(SCIPvarGetProbindex(branchvar) >= 0);
4439
4440 level2data->branchvar2 = (unsigned int) SCIPvarGetProbindex(branchvar);
4441 level2data->branchdir2 = !downbranching;
4442 level2data->branchval2 = newbound;
4443
4444 SCIP_CALL( level2dataGetResult(scip, level2data, &result) );
4445
4446 /* we already processed a similar level 2 node */
4447 if( result != NULL )
4448 {
4449 solvedlp = FALSE;
4450 #ifdef SCIP_STATISTIC
4451 statistics->nduplicatelps[probingdepth]++;
4452 #endif
4453 branchingresult->objval = result->lpobjval;
4454 branchingresult->dualbound = result->lpobjval;
4455 branchingresult->dualboundvalid = result->valid;
4456 branchingresult->cutoff = result->cutoff;
4457 branchingresult->niterations = 0;
4458
4459 if( !branchingresult->cutoff && branchingresult->dualboundvalid
4460 && SCIPisGE(scip, branchingresult->objval, SCIPgetCutoffbound(scip)) )
4461 branchingresult->cutoff = TRUE;
4462
4463 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH,
4464 "Use old %s branching result on var <%s> with 'val > %g' and bounds [<%g>..<%g>]: objval <%.9g>, cutoff <%d> "
4465 "(the parent objval was <%.9g>)\n",
4466 downbranching ? "down" : "up", SCIPvarGetName(branchvar), branchval, SCIPvarGetLbLocal(branchvar),
4467 SCIPvarGetUbLocal(branchvar), branchingresult->objval, branchingresult->cutoff, localbaselpsolval);
4468 }
4469 }
4470 }
4471
4472 if( solvedlp )
4473 {
4474 SCIP_CALL( executeBranching(scip, config, downbranching, candidate, branchingresult, baselpsol, domainreductions,
4475 status) );
4476
4477 assert(SCIPgetProbingDepth(scip) == 1 || SCIPgetProbingDepth(scip) == 2);
4478
4479 if( level2data != NULL && SCIPgetProbingDepth(scip) == 2)
4480 {
4481 SCIP_Bool duplicate;
4482
4483 SCIP_CALL( level2dataStoreResult(scip, level2data, branchingresult->objval, branchingresult->cutoff, branchingresult->dualboundvalid, &duplicate) );
4484 assert(!duplicate);
4485 }
4486
4487 #ifdef SCIP_STATISTIC
4488 statistics->nlpssolved[probingdepth]++;
4489 statistics->nlpiterations[probingdepth] += branchingresult->niterations;
4490
4491 if( config->inscoring )
4492 {
4493 statistics->nlpssolvedfsb[probingdepth]++;
4494 statistics->nlpiterationsfsb[probingdepth] += branchingresult->niterations;
4495 }
4496 #endif
4497 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Solving the LP took %" SCIP_LONGINT_FORMAT " iterations (status %d).\n",
4498 branchingresult->niterations, SCIPgetLPSolstat(scip));
4499
4500 #ifdef SCIP_DEBUG
4501 if( status->lperror )
4502 {
4503 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The LP could not be solved.\n");
4504 }
4505 else if( branchingresult->cutoff )
4506 {
4507 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The solved LP was infeasible and as such is cutoff\n");
4508 }
4509 else
4510 {
4511 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The solved LP was feasible and has an objval <%.9g> (the parent objval was "
4512 "<%.9g>)\n", branchingresult->objval, localbaselpsolval);
4513 }
4514 #endif
4515 }
4516
4517 if( !branchingresult->cutoff && !status->lperror && !status->limitreached )
4518 {
4519 SCIP_Real localgain;
4520
4521 localgain = MAX(0, branchingresult->objval - localbaselpsolval);
4522
4523 /* update pseudo costs */
4524 if( downbranching )
4525 {
4526 SCIP_CALL( SCIPupdateVarPseudocost(scip, branchvar, 0.0 - branchvalfrac, localgain, 1.0) );
4527 }
4528 else
4529 {
4530 SCIP_CALL( SCIPupdateVarPseudocost(scip, branchvar, 1.0 - branchvalfrac, localgain, 1.0) );
4531 }
4532 }
4533
4534 if( solvedlp && !branchingresult->cutoff && !status->lperror && !status->limitreached )
4535 {
4536 /* store the warm start information in the candidate, so that it can be reused in a later branching */
4537 if( config->reusebasis && config->inscoring )
4538 {
4539 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Storing warm start information for %s branching on var <%s>\n",
4540 downbranching ? "down" : "up", SCIPvarGetName(branchvar));
4541
4542 SCIP_CALL( candidateStoreWarmStartInfo(scip, candidate, downbranching) );
4543 }
4544
4545 if( recursiondepth > 1 && !config->inscoring )
4546 {
4547 CANDIDATELIST* candidatelist;
4548
4549 SCIP_CALL( candidateListGetAllFractionalCandidates(scip, &candidatelist) );
4550 assert(candidatelist != NULL);
4551
4552 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "%sbranching has <%i> candidates.\n", downbranching ? "Down" : "Up",
4553 candidatelist->ncandidates);
4554
4555 if( candidatelist->ncandidates > 0 )
4556 {
4557 BRANCHINGDECISION* deeperdecision;
4558 STATUS* deeperstatus;
4559 PERSISTENTDATA* deeperpersistent = NULL;
4560 SCIP_Real deeperlpobjval = branchingresult->objval;
4561 #ifdef SCIP_STATISTIC
4562 LOCALSTATISTICS* deeperlocalstats;
4563
4564 SCIP_CALL( localStatisticsAllocate(scip, &deeperlocalstats) );
4565 #endif
4566 SCIP_CALL( statusCreate(scip, &deeperstatus) );
4567
4568 SCIP_CALL( branchingDecisionCreate(scip, &deeperdecision) );
4569
4570 #ifdef SCIP_STATISTIC
4571 SCIP_CALL( filterCandidates(scip, deeperstatus, deeperpersistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
4572 deeperdecision, scorecontainer, level2data, deeperlpobjval,
4573 statistics, localstats) );
4574 #else
4575 SCIP_CALL( filterCandidates(scip, deeperstatus, deeperpersistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
4576 deeperdecision, scorecontainer, level2data, deeperlpobjval) );
4577 #endif
4578 if( deeperstatus->lperror )
4579 {
4580 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "ignoring lperror in filtering call...\n");
4581 deeperstatus->lperror = FALSE;
4582 }
4583 if( deeperstatus->cutoff )
4584 {
4585 branchingresult->ndeepestnodes += 2;
4586 branchingresult->ndeepestcutoffs += 2;
4587 }
4588
4589 /* the status may have changed because of FSB to get the best candidates */
4590 if( isBranchFurther(deeperstatus, FALSE) )
4591 {
4592 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Now the objval is <%.9g>\n", branchingresult->objval);
4593
4594 #ifdef SCIP_STATISTIC
4595 deeperlocalstats->ncutoffproofnodes = 0;
4596 SCIP_CALL( selectVarRecursive(scip, deeperstatus, deeperpersistent, config, baselpsol, domainreductions,
4597 binconsdata, candidatelist, deeperdecision, scorecontainer, level2data, recursiondepth - 1,
4598 deeperlpobjval, baselpobjval, &branchingresult->niterations, &branchingresult->ndeepestcutoffs,
4599 &branchingresult->bestgain, &branchingresult->totalgains, &branchingresult->ntotalgains,
4600 &branchingresult->ndeepestnodes,
4601 statistics, deeperlocalstats, NULL, NULL) );
4602 #else
4603 SCIP_CALL( selectVarRecursive(scip, deeperstatus, deeperpersistent, config, baselpsol, domainreductions,
4604 binconsdata, candidatelist, deeperdecision, scorecontainer, level2data, recursiondepth - 1,
4605 deeperlpobjval, baselpobjval, &branchingresult->niterations, &branchingresult->ndeepestcutoffs,
4606 &branchingresult->bestgain, &branchingresult->totalgains, &branchingresult->ntotalgains,
4607 &branchingresult->ndeepestnodes) );
4608 #endif
4609
4610 assert(deeperstatus->cutoff || deeperstatus->domred || deeperstatus->lperror
4611 || branchingresult->ndeepestnodes == 8
4612 || branchingresult->ndeepestnodes == 2 * candidatelist->ncandidates
4613 || SCIPisStopped(scip));
4614
4615 /* the proved dual bound of the deeper branching cannot be less than the current dual bound, as every deeper
4616 * node has more/tighter constraints and as such cannot be better than the base LP. */
4617 assert(SCIPisGE(scip, deeperdecision->proveddb, branchingresult->dualbound));
4618 branchingresult->dualbound = deeperdecision->proveddb;
4619 branchingresult->deeperscore = deeperdecision->score;
4620 branchingresult->dualboundvalid = TRUE;
4621 }
4622 #ifdef SCIP_STATISTIC
4623 else
4624 {
4625 assert(SCIPgetProbingDepth(scip) == probingdepth + 1);
4626
4627 statistics->stopafterfsb[probingdepth+1]++;
4628
4629 if( deeperstatus->cutoff )
4630 {
4631 statistics->cutoffafterfsb[probingdepth+1]++;
4632 }
4633 else if( deeperstatus->domred )
4634 {
4635 statistics->domredafterfsb[probingdepth+1]++;
4636 }
4637 }
4638 #endif
4639 /* deeperstatus->cutoff is TRUE, if any up/down child pair of the up child were cutoff */
4640 if( deeperstatus->cutoff )
4641 {
4642 branchingresult->cutoff = TRUE;
4643 #ifdef SCIP_STATISTIC
4644 localstats->ncutoffproofnodes += deeperlocalstats->ncutoffproofnodes;
4645 #endif
4646 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Both deeper children were cutoff, so the %s branch is "
4647 "cutoff\n", downbranching ? "down" : "up");
4648 }
4649
4650 branchingDecisionFree(scip, &deeperdecision);
4651 statusFree(scip, &deeperstatus);
4652 #ifdef SCIP_STATISTIC
4653 localStatisticsFree(scip, &deeperlocalstats);
4654 #endif
4655 }
4656 else
4657 {
4658 branchingresult->deeperscore = (branchingresult->dualbound - baselpobjval) * (branchingresult->dualbound - baselpobjval) * 10;
4659 }
4660 SCIP_CALL( candidateListFree(scip, &candidatelist) );
4661 }
4662 }
4663
4664 if( recursiondepth == 1 && !config->inscoring )
4665 {
4666 branchingresult->ndeepestnodes++;
4667 /* this is a cutoff on the lowest tree level */
4668 if( branchingresult->cutoff )
4669 {
4670 branchingresult->ndeepestcutoffs++;
4671 }
4672 }
4673
4674 if( binconsdata != NULL && varisbinary )
4675 {
4676 /* the current branching child is infeasible and we only branched on binary variables in lookahead branching */
4677 if( solvedlp && branchingresult->cutoff && !status->lperror && SCIPallColsInLP(scip)
4678 && binconsdata->binaryvars->nbinaryvars == (probingdepth + 1) )
4679 {
4680 #ifdef SCIP_STATISTIC
4681 SCIP_CALL( addBinaryConstraint(scip, config, binconsdata, baselpsol, statistics) );
4682 #else
4683 SCIP_CALL( addBinaryConstraint(scip, config, binconsdata, baselpsol) );
4684 #endif
4685 }
4686
4687 binaryVarListDrop(binconsdata->binaryvars);
4688 }
4689
4690 /* reset the probing depth to undo the previous branching */
4691 SCIP_CALL( SCIPbacktrackProbing(scip, probingdepth) );
4692
4693 return SCIP_OKAY;
4694 }
4695
4696 /** branches recursively on all given candidates */
4697 static
selectVarRecursive(SCIP * scip,STATUS * status,PERSISTENTDATA * persistent,CONFIGURATION * config,SCIP_SOL * baselpsol,DOMAINREDUCTIONS * domainreductions,BINCONSDATA * binconsdata,CANDIDATELIST * candidatelist,BRANCHINGDECISION * decision,SCORECONTAINER * scorecontainer,LEVEL2DATA * level2data,int recursiondepth,SCIP_Real lpobjval,SCIP_Real baselpobjval,SCIP_Longint * niterations,int * ndeepestcutoffs,SCIP_Real * bestgain,SCIP_Real * totalgains,int * ntotalgains,int * ndeepestnodes,STATISTICS * statistics,LOCALSTATISTICS * localstats,SCIP_Real * firstscoreptr,SCIP_Real * bestscoreptr)4698 SCIP_RETCODE selectVarRecursive(
4699 SCIP* scip, /**< SCIP data structure */
4700 STATUS* status, /**< current status */
4701 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
4702 CONFIGURATION* config, /**< the configuration of the branching rule */
4703 SCIP_SOL* baselpsol, /**< base lp solution */
4704 DOMAINREDUCTIONS* domainreductions, /**< container collecting all domain reductions found; or NULL */
4705 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
4706 CANDIDATELIST* candidatelist, /**< list of candidates to branch on */
4707 BRANCHINGDECISION* decision, /**< struct to store the final decision */
4708 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
4709 LEVEL2DATA* level2data, /**< level 2 LP results data */
4710 int recursiondepth, /**< remaining recursion depth */
4711 SCIP_Real lpobjval, /**< LP objective value of current probing node*/
4712 SCIP_Real baselpobjval, /**< LP objective value of focus node (not probing) */
4713 SCIP_Longint* niterations, /**< pointer to store the total number of iterations for this variable; or NULL*/
4714 int* ndeepestcutoffs, /**< pointer to store the total number of cutoffs on the deepest level; or NULL */
4715 SCIP_Real* bestgain, /**< pointer to store the best gain found with these candidates; or NULL */
4716 SCIP_Real* totalgains, /**< pointer to store the sum over all gains that are valid in both children;
4717 * or NULL, if bestgain == NULL */
4718 int* ntotalgains, /**< pointer to store the number of gains summed in totalgains;
4719 * or NULL, if bestgain == NULL */
4720 int* ndeepestnodes /**< pointer to store the number of nodes processed in the deepest level */
4721 #ifdef SCIP_STATISTIC
4722 ,STATISTICS* statistics /**< general statistical data */
4723 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
4724 ,SCIP_Real* firstscoreptr /**< pointer to store score of first candidate, or NULL */
4725 ,SCIP_Real* bestscoreptr /**< pointer to store best score, or NULL */
4726 #endif
4727 )
4728 {
4729 BRANCHINGRESULTDATA* downbranchingresult = NULL;
4730 BRANCHINGRESULTDATA* upbranchingresult = NULL;
4731 BRANCHINGRESULTDATA* bestdownbranchingresult = NULL;
4732 BRANCHINGRESULTDATA* bestupbranchingresult = NULL;
4733 SCIP_LPI* lpi;
4734 SCIP_Real bestscore = -SCIPinfinity(scip);
4735 SCIP_Real bestscorelowerbound;
4736 SCIP_Real bestscoreupperbound;
4737 SCIP_Real bestscoringlpobjval = -SCIPinfinity(scip);
4738 int start = 0;
4739 int i;
4740 int c;
4741 int nlpcands;
4742 int probingdepth;
4743 SCIP_Bool stopafterinfeasible = FALSE;
4744
4745 assert(scip != NULL);
4746 assert(status != NULL);
4747 assert(config != NULL);
4748 assert(!config->usedomainreduction || domainreductions != NULL);
4749 assert(candidatelist != NULL);
4750 assert(candidatelist->ncandidates > 0);
4751 assert(decision != NULL);
4752 assert(recursiondepth >= 1);
4753 #ifdef SCIP_STATISTIC
4754 assert(statistics != NULL);
4755
4756 if( firstscoreptr != NULL )
4757 *firstscoreptr = -1.0;
4758 if( bestscoreptr != NULL )
4759 *bestscoreptr = -1.0;
4760 #endif
4761
4762 nlpcands = candidatelist->ncandidates;
4763 probingdepth = SCIPgetProbingDepth(scip);
4764 assert(probingdepth >= 0 && probingdepth < config->recursiondepth);
4765
4766 if( persistent != NULL && (!config->abbreviated || config->inscoring) && probingdepth == 0 )
4767 start = persistent->restartindex;
4768
4769 /* init default decision */
4770 decision->branchvar = candidatelist->candidates[0]->branchvar;
4771 decision->branchval = candidatelist->candidates[0]->branchval;
4772 decision->downdb = lpobjval;
4773 decision->downdbvalid = TRUE;
4774 decision->updb = lpobjval;
4775 decision->updbvalid = TRUE;
4776 decision->proveddb = lpobjval;
4777 decision->score = 0.0;
4778
4779 bestscorelowerbound = SCIPvarGetLbLocal(decision->branchvar);
4780 bestscoreupperbound = SCIPvarGetUbLocal(decision->branchvar);
4781
4782 SCIP_CALL( branchingResultDataCreate(scip, &downbranchingresult) );
4783 SCIP_CALL( branchingResultDataCreate(scip, &upbranchingresult) );
4784
4785 SCIP_CALL( branchingResultDataCreate(scip, &bestdownbranchingresult) );
4786 SCIP_CALL( branchingResultDataCreate(scip, &bestupbranchingresult) );
4787
4788 assert(downbranchingresult != NULL);
4789 assert(upbranchingresult != NULL);
4790
4791 if( config->inscoring )
4792 {
4793 SCIP_CALL( SCIPgetBoolParam(scip, "branching/forceallchildren", &stopafterinfeasible) );
4794 stopafterinfeasible = !stopafterinfeasible;
4795 }
4796
4797 SCIP_CALL( SCIPgetLPI(scip, &lpi) );
4798
4799 #ifdef SCIP_DEBUG
4800 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Started selectVarRecursive with <%i> candidates: ", nlpcands);
4801 printCandidates(scip, SCIP_VERBLEVEL_HIGH, candidatelist);
4802 #endif
4803
4804 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Starting loop from index %d\n", start);
4805
4806 /* iterate over all current branching candidates and evaluate their two potential child nodes by:
4807 * - potentially applying domain propagation at each node before
4808 * - solving the LP at the nodes to obtain a dual bound
4809 * - potentially evaluating branching candidates at the potential child node again by applying this method recursively
4810 *
4811 * some improvements of the general scheme:
4812 * - results obtained for a candidate in a previous lookahead branching call at this node may be re-used
4813 * - while i counts the number of candidates evaluated in this call, we do not always start at the front
4814 * of the candidate array, but rather store at which index we stopped last time (e.g., because a domain reduction was
4815 * found and applied) and start from that index next time. Even though the set of branching candidates is probably different
4816 * it is often reasonably close and we avoid evaluating the same variables again and again.
4817 */
4818 for( i = 0, c = start;
4819 isBranchFurtherLoopDecrement(status, &c) && i < nlpcands && !SCIPisStopped(scip); i++, c++)
4820 {
4821 DOMAINREDUCTIONS* downdomainreductions = NULL;
4822 DOMAINREDUCTIONS* updomainreductions = NULL;
4823 SCIP_Bool useoldbranching = FALSE;
4824 SCIP_Real oldlpobjval = -SCIPinfinity(scip);
4825 CANDIDATE* candidate;
4826 SCIP_VAR* branchvar;
4827 SCIP_Real branchval;
4828 SCIP_Real branchlb;
4829 SCIP_Real branchub;
4830
4831 c = c % nlpcands;
4832
4833 candidate = candidatelist->candidates[c];
4834
4835 assert(candidate != NULL);
4836
4837 branchvar = candidate->branchvar;
4838 branchval = candidate->branchval;
4839
4840 assert(branchvar != NULL);
4841
4842 branchlb = SCIPvarGetLbLocal(branchvar);
4843 branchub = SCIPvarGetUbLocal(branchvar);
4844
4845 if( SCIPisEQ(scip, branchlb, branchub) )
4846 {
4847 /* if both bounds are equal the variable is fixed and we cannot branch
4848 * this may happen if domain propagation on other candidates finds better bounds for the current candidate
4849 */
4850 status->domred = TRUE;
4851 #ifdef SCIP_STATISTIC
4852 statistics->npropdomred[probingdepth]++;
4853 #endif
4854 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Domain Propagation changed the bounds of a branching candidate."
4855 "\n");
4856 continue;
4857 }
4858
4859 /* @todo apply already found domainreductions for this candidate? */
4860
4861 #ifdef SCIP_STATISTIC
4862 /* Reset the cutoffproofnodes, as the number of proof nodes from previous branching vars (which where not
4863 * cutoff, as we didn't break the loop) is not relevant for the min total sum of proof nodes.
4864 */
4865 localstats->ncutoffproofnodes = 0;
4866 #endif
4867
4868 branchingResultDataInit(scip, downbranchingresult);
4869 branchingResultDataInit(scip, upbranchingresult);
4870
4871 /* use old lookahead branching result, if last call on this variable is not too long ago */
4872 if( persistent != NULL && (config->inscoring || probingdepth == 0) && isUseOldBranching(scip, persistent, config, branchvar) )
4873 {
4874 SCIP_CALL( getOldBranching(scip, persistent, config, branchvar, downbranchingresult, upbranchingresult,
4875 &oldlpobjval) );
4876 useoldbranching = TRUE;
4877 #ifdef SCIP_STATISTIC
4878 if( config->inscoring )
4879 statistics->noldbranchusedfsb[probingdepth]++;
4880 else
4881 statistics->noldbranchused[probingdepth]++;
4882 #endif
4883 }
4884 else
4885 {
4886 SCIP_Bool down;
4887 int k;
4888
4889 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, "Started branching on var <%s> with val <%g> and bounds "
4890 "[<%g>..<%g>]\n", SCIPvarGetName(branchvar), branchval, SCIPvarGetLbLocal(branchvar),
4891 SCIPvarGetUbLocal(branchvar));
4892
4893 if( config->usedomainreduction )
4894 {
4895 SCIP_CALL( domainReductionsCreate(scip, &downdomainreductions) );
4896 SCIP_CALL( domainReductionsCreate(scip, &updomainreductions) );
4897 }
4898
4899 down = SCIPisStrongbranchDownFirst(scip, branchvar);
4900
4901 /* @todo break if result is infeasible (probably only in first layer)? */
4902 for( k = 0; k < 2; ++k )
4903 {
4904 DOMAINREDUCTIONS* localdomainreductions;
4905 BRANCHINGRESULTDATA* localbranchingresult;
4906 BRANCHINGRESULTDATA* otherbranchingresult;
4907
4908 localdomainreductions = down ? downdomainreductions : updomainreductions;
4909 localbranchingresult = down ? downbranchingresult : upbranchingresult;
4910 otherbranchingresult = down ? upbranchingresult : downbranchingresult;
4911
4912 #ifdef SCIP_STATISTIC
4913 SCIP_CALL( executeBranchingRecursive(scip, status, config, baselpsol, candidate, lpobjval, baselpobjval,
4914 recursiondepth, localdomainreductions, binconsdata, level2data, localbranchingresult, scorecontainer,
4915 down, statistics, localstats) );
4916 #else
4917
4918 SCIP_CALL( executeBranchingRecursive(scip, status, config, baselpsol, candidate, lpobjval, baselpobjval,
4919 recursiondepth, localdomainreductions, binconsdata, level2data, localbranchingresult, scorecontainer,
4920 down) );
4921 #endif
4922
4923 /* check whether a new solutions rendered the previous child infeasible */
4924 if( SCIPallColsInLP(scip) && !otherbranchingresult->cutoff )
4925 {
4926 if( k == 1 && SCIPisGE(scip, otherbranchingresult->dualbound, SCIPgetCutoffbound(scip)) )
4927 {
4928 otherbranchingresult->cutoff = TRUE;
4929 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH,
4930 "The %s branching changed the cutoffbound and rendered the %s branching result infeasible.\n",
4931 down ? "down" : "up", down ? "up" : "down");
4932 }
4933 }
4934 if( stopafterinfeasible && k == 0 && localbranchingresult->cutoff )
4935 break;
4936
4937 /* the second iteration of the loop should branch in the other direction */
4938 down = !down;
4939 }
4940
4941 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, "-> down=%.9g (gain=%.9g, valid=%u, inf=%u), up=%.9g "
4942 "(gain=%.9g, valid=%u, inf=%u)\n", downbranchingresult->dualbound,
4943 downbranchingresult->dualbound - lpobjval, downbranchingresult->dualboundvalid,
4944 downbranchingresult->cutoff, upbranchingresult->dualbound, upbranchingresult->dualbound - lpobjval,
4945 upbranchingresult->dualboundvalid, upbranchingresult->cutoff);
4946
4947 if( niterations != NULL )
4948 *niterations += downbranchingresult->niterations + upbranchingresult->niterations;
4949
4950 /* store results of branching call */
4951 if( persistent != NULL && !upbranchingresult->cutoff && !downbranchingresult->cutoff && (config->inscoring || probingdepth == 0) )
4952 {
4953 SCIP_CALL( updateOldBranching(scip, persistent, config, branchvar, branchval, downbranchingresult,
4954 upbranchingresult, lpobjval) );
4955 }
4956 }
4957
4958 if( ndeepestcutoffs != NULL )
4959 *ndeepestcutoffs += downbranchingresult->ndeepestcutoffs + upbranchingresult->ndeepestcutoffs;
4960
4961 if( ndeepestnodes != NULL )
4962 *ndeepestnodes += downbranchingresult->ndeepestnodes + upbranchingresult->ndeepestnodes;
4963
4964 if( !status->lperror && !status->limitreached )
4965 {
4966 SCIP_Real scoringlpobjval = useoldbranching ? oldlpobjval : lpobjval;
4967 SCIP_Real score = calculateScore(scip, config, branchvar, downbranchingresult, upbranchingresult,
4968 scoringlpobjval, baselpobjval);
4969
4970 #ifdef SCIP_STATISTIC
4971 if( i == 0 && firstscoreptr != NULL )
4972 *firstscoreptr = score;
4973 #endif
4974
4975 if( bestgain != NULL && !config->inscoring && SCIPgetProbingDepth(scip) == 1 && !useoldbranching )
4976 {
4977 assert(totalgains != NULL);
4978 assert(ntotalgains != NULL);
4979
4980 *bestgain = MAX(*bestgain, score);
4981
4982 if( !downbranchingresult->cutoff && !upbranchingresult->cutoff )
4983 {
4984 (*totalgains) += score;
4985 (*ntotalgains)++;
4986 }
4987 }
4988
4989 /* both child nodes are infeasible -> the current node is infeasible */
4990 if( SCIPallColsInLP(scip) && upbranchingresult->cutoff && downbranchingresult->cutoff )
4991 {
4992 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, " -> variable <%s> is infeasible in both directions\n",
4993 SCIPvarGetName(branchvar));
4994
4995 /* this cutoff may be transferred to a higher level as a domain reduction/valid bound */
4996 status->cutoff = TRUE;
4997 #ifdef SCIP_STATISTIC
4998 statistics->nfullcutoffs[probingdepth]++;
4999 localstats->ncutoffproofnodes += 2;
5000 #endif
5001 }
5002 /* up child is infeasible */
5003 else if( SCIPallColsInLP(scip) && upbranchingresult->cutoff )
5004 {
5005 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, " -> variable <%s> is infeasible in upward branch\n",
5006 SCIPvarGetName(branchvar));
5007
5008 /* apply down branching bound change at current node if we proved that this node is really infeasible and
5009 * parameters are set accordingly
5010 */
5011 if( config->usedomainreduction && !useoldbranching )
5012 {
5013 #ifdef SCIP_STATISTIC
5014 assert(localstats->ncutoffproofnodes == 0 || localstats->ncutoffproofnodes == 2);
5015 addUpperBound(scip, branchvar, branchval, baselpsol, TRUE, domainreductions,
5016 2 + localstats->ncutoffproofnodes, TRUE);
5017 #else
5018 addUpperBound(scip, branchvar, branchval, baselpsol, TRUE, domainreductions);
5019 #endif
5020 }
5021
5022 /* the proved bound is given by the bound of the down child alone */
5023 if( downbranchingresult->dualboundvalid )
5024 {
5025 decision->proveddb = MAX(decision->proveddb, downbranchingresult->dualbound);
5026 }
5027
5028 #ifdef SCIP_STATISTIC
5029 statistics->nsinglecutoffs[probingdepth]++;
5030 #endif
5031 }
5032 /* down child is infeasible */
5033 else if( SCIPallColsInLP(scip) && downbranchingresult->cutoff )
5034 {
5035 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, " -> variable <%s> is infeasible in downward branch\n",
5036 SCIPvarGetName(branchvar));
5037
5038 /* apply up branching bound change at current node if we proved that this node is really infeasible and
5039 * parameters are set accordingly
5040 */
5041 if( config->usedomainreduction && !useoldbranching )
5042 {
5043 #ifdef SCIP_STATISTIC
5044 assert(localstats->ncutoffproofnodes == 0 || localstats->ncutoffproofnodes == 2);
5045 addLowerBound(scip, branchvar, branchval, baselpsol, TRUE, domainreductions,
5046 2 + localstats->ncutoffproofnodes, TRUE);
5047 #else
5048 addLowerBound(scip, branchvar, branchval, baselpsol, TRUE, domainreductions);
5049 #endif
5050 }
5051
5052 /* the proved bound is given by the bound of the up child alone */
5053 if( upbranchingresult->dualboundvalid )
5054 {
5055 decision->proveddb = MAX(decision->proveddb, upbranchingresult->dualbound);
5056 }
5057
5058 #ifdef SCIP_STATISTIC
5059 statistics->nsinglecutoffs[probingdepth]++;
5060 #endif
5061 }
5062 /* "normal" case: both child nodes are LP-feasible */
5063 else
5064 {
5065 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Neither branch is cut off and no limit reached.\n");
5066
5067 /* the proved dual bound is the minimum of the dual bounds of both child nodes */
5068 if( upbranchingresult->dualboundvalid && downbranchingresult->dualboundvalid )
5069 {
5070 decision->proveddb = MAX(decision->proveddb, MIN(upbranchingresult->dualbound,
5071 downbranchingresult->dualbound));
5072 }
5073 }
5074
5075 /* merge domain changes from the two child nodes */
5076 if( updomainreductions != NULL && config->usedomainreduction && SCIPallColsInLP(scip) )
5077 {
5078 int maxstoredomreds = INT_MAX;
5079
5080 assert(downdomainreductions != NULL);
5081
5082 if( config->enforcemaxdomreds && config->maxnviolateddomreds > 0)
5083 maxstoredomreds = config->maxnviolateddomreds;
5084
5085 if( !upbranchingresult->cutoff && !downbranchingresult->cutoff && config->mergedomainreductions )
5086 applyDeeperDomainReductions(scip, baselpsol, maxstoredomreds, domainreductions, downdomainreductions,
5087 updomainreductions);
5088 else if( upbranchingresult->cutoff && !downbranchingresult->cutoff )
5089 applySingleDeeperDomainReductions(scip, baselpsol, maxstoredomreds, domainreductions, downdomainreductions);
5090 else if( downbranchingresult->cutoff && !upbranchingresult->cutoff )
5091 applySingleDeeperDomainReductions(scip, baselpsol, maxstoredomreds, domainreductions, updomainreductions);
5092 }
5093
5094 if( config->updatebranchingresults && bestscore > -1.0 &&
5095 (SCIPisGT(scip, decision->proveddb, bestdownbranchingresult->dualbound)
5096 || SCIPisGT(scip, decision->proveddb, bestupbranchingresult->dualbound)) )
5097 {
5098 SCIP_Real newscore;
5099
5100 bestdownbranchingresult->dualbound = MAX(bestdownbranchingresult->dualbound, decision->proveddb);
5101 bestupbranchingresult->dualbound = MAX(bestupbranchingresult->dualbound, decision->proveddb);
5102
5103 newscore = calculateScore(scip, config, decision->branchvar, bestdownbranchingresult, bestupbranchingresult,
5104 bestscoringlpobjval, baselpobjval);
5105
5106 if( newscore > bestscore )
5107 {
5108 bestscore = newscore;
5109
5110 #ifdef SCIP_STATISTIC
5111 if( bestscoreptr != NULL )
5112 *bestscoreptr = newscore;
5113 #endif
5114 decision->score = newscore;
5115 decision->downdb = bestdownbranchingresult->dualbound;
5116 decision->updb = bestupbranchingresult->dualbound;
5117 }
5118 }
5119
5120 /* the current candidate variable has a better score than the best candidate investigated so far */
5121 if( SCIPisRelGT(scip, score, bestscore) )
5122 {
5123 int nvars = SCIPgetNVars(scip);
5124
5125 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Old best var <%s> with bounds [<%g>..<%g>] and score %.9g\n",
5126 SCIPvarGetName(decision->branchvar), bestscorelowerbound, bestscoreupperbound, bestscore);
5127
5128 bestscore = score;
5129
5130 #ifdef SCIP_STATISTIC
5131 if( bestscoreptr != NULL )
5132 *bestscoreptr = score;
5133 #endif
5134 decision->branchvar = candidate->branchvar;
5135 decision->branchval = candidate->branchval;
5136 decision->downdb = downbranchingresult->dualbound;
5137 decision->downdbvalid = downbranchingresult->dualboundvalid;
5138 decision->updb = upbranchingresult->dualbound;
5139 decision->updbvalid = upbranchingresult->dualboundvalid;
5140 decision->score = score;
5141
5142 branchingResultDataCopy(downbranchingresult, bestdownbranchingresult);
5143 branchingResultDataCopy(upbranchingresult, bestupbranchingresult);
5144
5145 /* store domain reductions found at the child nodes */
5146 if( !config->inscoring && updomainreductions != NULL )
5147 {
5148 assert(downdomainreductions != NULL);
5149
5150 SCIP_CALL( branchingDecisionEnsureBoundArraysSize(scip, decision, nvars) );
5151
5152 BMScopyMemoryArray(decision->uplowerbounds, updomainreductions->lowerbounds, nvars);
5153 BMScopyMemoryArray(decision->upupperbounds, updomainreductions->upperbounds, nvars);
5154 BMScopyMemoryArray(decision->downlowerbounds, downdomainreductions->lowerbounds, nvars);
5155 BMScopyMemoryArray(decision->downupperbounds, downdomainreductions->upperbounds, nvars);
5156 decision->boundsvalid = TRUE;
5157 }
5158 else
5159 {
5160 decision->boundsvalid = FALSE;
5161 }
5162
5163 bestscorelowerbound = branchlb;
5164 bestscoreupperbound = branchub;
5165 bestscoringlpobjval = scoringlpobjval;
5166 assert(!SCIPisEQ(scip, bestscorelowerbound, bestscoreupperbound));
5167
5168 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "New best var <%s> with bounds [<%g>..<%g>] and score %.9g\n",
5169 SCIPvarGetName(decision->branchvar), bestscorelowerbound, bestscoreupperbound, bestscore);
5170 }
5171
5172 #ifdef SCIP_DEBUG
5173 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, " -> cand %d/%d var <%s> (solval=%.9g, downgain=%.9g->%.9g, upgain=%.9g->%.9g,"
5174 " score=%.9g) -- best: <%s> (%.9g)\n", c, nlpcands, SCIPvarGetName(branchvar), branchval,
5175 MAX(downbranchingresult->objval - scoringlpobjval, 0), MAX(downbranchingresult->dualbound - scoringlpobjval, 0),
5176 MAX(upbranchingresult->objval - scoringlpobjval, 0), MAX(upbranchingresult->dualbound - scoringlpobjval, 0),
5177 score, SCIPvarGetName(decision->branchvar), bestscore);
5178 #endif
5179
5180 if( config->inscoring )
5181 {
5182 assert(scorecontainer != NULL);
5183 /* only for abbreviated lookahead branching: we are in the FSB filtering step and store the score for this
5184 * variable and the warm starting basis to reuse it in the subsequent lookahead evaluation of the best
5185 * candidates
5186 */
5187 SCIP_CALL( scoreContainerSetScore(scip, scorecontainer, candidate, score,
5188 downbranchingresult->dualbound - scoringlpobjval, upbranchingresult->dualbound - scoringlpobjval) );
5189 }
5190
5191 if( probingdepth == 0 && (binconsdata != NULL || domainreductions != NULL) && !useoldbranching
5192 && (config->maxnviolatedcons >= 0 || config->maxnviolatedbincons >= 0 || config->maxnviolateddomreds >= 0 ) )
5193 {
5194 int nbincons = 0;
5195 int ndomreds = 0;
5196
5197 if( binconsdata != NULL )
5198 {
5199 assert(binconsdata != NULL); /* for lint */
5200 nbincons = binconsdata->conslist->nviolatedcons;
5201 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Found %d binary constraints (%d violated by the LP solution)\n",
5202 binconsdata->conslist->nelements, nbincons);
5203
5204 if( (config->maxnviolatedbincons > 0) && (nbincons >= config->maxnviolatedbincons) )
5205 {
5206 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The max number of violated binary constraints <%i> is "
5207 "exceeded.\n", config->maxnviolatedbincons);
5208 status->maxnconsreached = TRUE;
5209 }
5210 }
5211
5212 if( domainreductions != NULL )
5213 {
5214 assert(domainreductions != NULL); /* for lint */
5215 ndomreds = domainreductions->nviolatedvars;
5216 if( config->prefersimplebounds && ndomreds > domainreductions->nsimplebounds )
5217 ndomreds = domainreductions->nsimplebounds;
5218
5219 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Found %d bound changes (%d violated by the LP solution)\n",
5220 domainreductions->nchangedvars, ndomreds);
5221
5222 if( (config->maxnviolateddomreds > 0) && (ndomreds >= config->maxnviolateddomreds) )
5223 {
5224 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The max number of violated bound changes <%i> is "
5225 "exceeded.\n", config->maxnviolateddomreds);
5226 status->maxnconsreached = TRUE;
5227 }
5228 }
5229
5230 if( config->maxnviolatedcons > 0 && (nbincons + ndomreds >= config->maxnviolatedcons) )
5231 {
5232 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The max number of violated binary constraints and bound "
5233 "changes <%d> is exceeded.\n", config->maxnviolatedcons);
5234 status->maxnconsreached = TRUE;
5235 }
5236 }
5237 }
5238
5239 if( !(status->domred && decision->branchvar == candidate->branchvar) && areBoundsChanged(scip, decision->branchvar, bestscorelowerbound, bestscoreupperbound) )
5240 {
5241 /* in case the bounds of the current highest scored solution have changed due to domain propagation during
5242 * the lookahead branching we can/should not branch on this variable but instead report the domain
5243 * reduction */
5244 status->domred = TRUE;
5245 #ifdef SCIP_STATISTIC
5246 statistics->npropdomred[probingdepth]++;
5247 #endif
5248 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Domain Propagation changed the bounds of a branching candidate."
5249 "\n");
5250 }
5251
5252 /* free domain reductions */
5253 if( updomainreductions != NULL )
5254 {
5255 assert(downdomainreductions != NULL);
5256
5257 domainReductionsFree(scip, &updomainreductions);
5258 domainReductionsFree(scip, &downdomainreductions);
5259 }
5260 }
5261
5262 branchingResultDataFree(scip, &bestupbranchingresult);
5263 branchingResultDataFree(scip, &bestdownbranchingresult);
5264
5265 branchingResultDataFree(scip, &upbranchingresult);
5266 branchingResultDataFree(scip, &downbranchingresult);
5267
5268 if( persistent != NULL && (!config->abbreviated || config->inscoring) && probingdepth == 0 )
5269 {
5270 persistent->restartindex = c;
5271 }
5272
5273 return SCIP_OKAY;
5274 }
5275
5276 /** checks whether the current decision should be stored. This is the case if we found domain reductions
5277 * or constraints that will be applied, but none of them cuts off the current LP solution.
5278 * Then our current decision still holds true for the next call and can be reused without further calculations
5279 */
5280 static
isStoreDecision(CONFIGURATION * config,BINCONSDATA * binconsdata,DOMAINREDUCTIONS * domainreductions)5281 SCIP_Bool isStoreDecision(
5282 CONFIGURATION* config, /**< the configuration of the branching rule */
5283 BINCONSDATA* binconsdata, /**< container collecting all binary constraints; or NULL */
5284 DOMAINREDUCTIONS* domainreductions /**< container collecting all domain reductions found; or NULL */
5285 )
5286 {
5287 assert(config != NULL);
5288
5289 if( !config->storeunviolatedsol )
5290 return FALSE;
5291
5292 /* there are violating binary constraints */
5293 if( binconsdata != NULL && binconsdata->conslist->nviolatedcons > 0 )
5294 return FALSE;
5295
5296 /* there are violating domain changes */
5297 if( domainreductions != NULL && domainreductions->nviolatedvars > 0 )
5298 return FALSE;
5299
5300 /* return TRUE if there is at least one domain change or binary constraint */
5301 return (domainreductions != NULL && domainreductions->nchangedvars > 0)
5302 || (binconsdata != NULL && binconsdata->conslist->nelements > 0);
5303 }
5304
5305 /** starting point to obtain a branching decision via LAB/ALAB. */
5306 static
selectVarStart(SCIP * scip,CONFIGURATION * config,PERSISTENTDATA * persistent,STATUS * status,BRANCHINGDECISION * decision,SCORECONTAINER * scorecontainer,CANDIDATELIST * candidatelist,STATISTICS * statistics,LOCALSTATISTICS * localstats)5307 SCIP_RETCODE selectVarStart(
5308 SCIP* scip, /**< SCIP data structure */
5309 CONFIGURATION* config, /**< the configuration of the branching rule */
5310 PERSISTENTDATA* persistent, /**< container to store data over multiple calls to the branching rule; or NULL */
5311 STATUS* status, /**< current status */
5312 BRANCHINGDECISION* decision, /**< struct to store the final decision */
5313 SCORECONTAINER* scorecontainer, /**< container to retrieve already calculated scores; or NULL */
5314 CANDIDATELIST* candidatelist /**< list of candidates to branch on */
5315 #ifdef SCIP_STATISTIC
5316 ,STATISTICS* statistics /**< general statistical data */
5317 ,LOCALSTATISTICS* localstats /**< local statistics, may be disregarded */
5318 #endif
5319 )
5320 {
5321 int recursiondepth;
5322 DOMAINREDUCTIONS* domainreductions = NULL;
5323 BINCONSDATA* binconsdata = NULL;
5324 LEVEL2DATA* level2data = NULL;
5325 SCIP_SOL* baselpsol = NULL;
5326 SCIP_Real lpobjval;
5327 #ifdef SCIP_STATISTIC
5328 SCIP_Real firstscore = -1.0;
5329 SCIP_Real bestscore = -1.0;
5330 int chosencandnr = -1;
5331 SCIP_Bool performedlab = FALSE;
5332 #endif
5333
5334 assert(scip != NULL);
5335 assert(config != NULL);
5336 assert(status != NULL);
5337 assert(decision != NULL);
5338 assert(candidatelist != NULL);
5339 #ifdef SCIP_STATISTIC
5340 assert(statistics != NULL);
5341 #endif
5342
5343 recursiondepth = config->recursiondepth;
5344 lpobjval = SCIPgetLPObjval(scip);
5345
5346 assert(recursiondepth > 0);
5347
5348 if( SCIP_MAXTREEDEPTH <= (SCIPgetDepth(scip) + recursiondepth) )
5349 {
5350 /* we need at least 'recursiondepth' space for the branching */
5351 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Cannot perform probing in selectVarRecursive, depth limit reached. "
5352 "Current:<%i>, Max:<%i>\n", SCIP_MAXTREEDEPTH, SCIPgetDepth(scip) + recursiondepth);
5353 status->depthtoosmall = TRUE;
5354 #ifdef SCIP_STATISTIC
5355 statistics->ndepthreached++;
5356 #endif
5357 return SCIP_OKAY;
5358 }
5359
5360 assert(!config->inscoring);
5361
5362 if( candidatelist->ncandidates == 1 )
5363 {
5364 decision->branchvar = candidatelist->candidates[0]->branchvar;
5365 decision->branchval = candidatelist->candidates[0]->branchval;
5366 decision->downdb = lpobjval;
5367 decision->downdbvalid = TRUE;
5368 decision->updb = lpobjval;
5369 decision->updbvalid = TRUE;
5370 decision->proveddb = lpobjval;
5371
5372 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Only one candidate (<%s>) is given. This one is chosen without "
5373 "calculations.\n", SCIPvarGetName(decision->branchvar));
5374
5375 #ifdef SCIP_STATISTIC
5376 statistics->nsinglecandidate++;
5377 #endif
5378 return SCIP_OKAY;
5379 }
5380 assert(!SCIPinProbing(scip));
5381
5382 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The objective value of the base lp is <%.9g>\n", lpobjval);
5383
5384 if( config->usedomainreduction || config->usebincons )
5385 {
5386 /* we have to copy the current solution before getting the candidates, as we possibly solve some LPs during
5387 * the getter and as such would get a wrong LP copied */
5388 SCIP_CALL( copyCurrentSolution(scip, &baselpsol) );
5389 }
5390
5391 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "About to start probing.\n");
5392 SCIP_CALL( SCIPstartStrongbranch(scip, TRUE) );
5393 SCIPenableVarHistory(scip);
5394
5395 /* create the binary constraint data */
5396 if( config->usebincons )
5397 {
5398 SCIP_CALL( binConsDataCreate(scip, &binconsdata, recursiondepth,
5399 (int)SCIPceil(scip, 0.5*candidatelist->ncandidates)) );
5400 }
5401
5402 /* collect domain reductions in FSB scoring or LAB branching */
5403 if( config->usedomainreduction )
5404 {
5405 SCIP_CALL( domainReductionsCreate(scip, &domainreductions) );
5406 }
5407
5408 #ifdef SCIP_STATISTIC
5409 SCIP_CALL( filterCandidates(scip, status, persistent, config, baselpsol, domainreductions, NULL, candidatelist,
5410 decision, scorecontainer, level2data, lpobjval,
5411 statistics, localstats) );
5412 #else
5413 SCIP_CALL( filterCandidates(scip, status, persistent, config, baselpsol, domainreductions, NULL, candidatelist,
5414 decision, scorecontainer, level2data, lpobjval) );
5415 #endif
5416
5417 if( candidatelist->ncandidates == 1 )
5418 {
5419 decision->branchvar = candidatelist->candidates[0]->branchvar;
5420 decision->branchval = candidatelist->candidates[0]->branchval;
5421 decision->downdb = lpobjval;
5422 decision->downdbvalid = TRUE;
5423 decision->updb = lpobjval;
5424 decision->updbvalid = TRUE;
5425 decision->proveddb = lpobjval;
5426
5427 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Only one candidate (<%s>) is given. This one is chosen without "
5428 "calculations.\n", SCIPvarGetName(decision->branchvar));
5429
5430 #ifdef SCIP_STATISTIC
5431 statistics->nsingleafterfilter++;
5432 #endif
5433 goto TERMINATE;
5434 }
5435
5436 /* the status may have changed because of FSB to get the best candidates
5437 * if that is the case, we already changed the base node and should start again */
5438 if( isBranchFurther(status, TRUE) && candidatelist->ncandidates > 1 )
5439 {
5440 assert(candidatelist->ncandidates > 0);
5441
5442 SCIPstatistic(performedlab = TRUE);
5443
5444 /* we do not need the level 2 data for FSB scoring, so we do not need to create it before */
5445 if( recursiondepth == 2 && config->uselevel2data )
5446 {
5447 SCIP_CALL( level2dataCreate(scip, &level2data) );
5448 }
5449
5450 #ifdef SCIP_STATISTIC
5451 SCIP_CALL( selectVarRecursive(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
5452 decision, scorecontainer, level2data, recursiondepth, lpobjval, lpobjval, NULL, NULL, NULL, NULL, NULL, NULL,
5453 statistics, localstats, &firstscore, &bestscore) );
5454 #else
5455 SCIP_CALL( selectVarRecursive(scip, status, persistent, config, baselpsol, domainreductions, binconsdata, candidatelist,
5456 decision, scorecontainer, level2data, recursiondepth, lpobjval, lpobjval, NULL, NULL, NULL, NULL, NULL, NULL) );
5457 #endif
5458
5459 if( level2data != NULL )
5460 {
5461 level2dataFree(scip, &level2data);
5462 }
5463
5464 /* only unviolating constraints and domain changes: store branching decision */
5465 if( persistent != NULL && !status->lperror && isStoreDecision(config, binconsdata, domainreductions) )
5466 {
5467 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "store decision: lpiters=%lld, cand <%s>[%g,%g - %g]\n",
5468 SCIPgetNNodeLPIterations(scip), SCIPvarGetName(decision->branchvar),
5469 SCIPvarGetLbLocal(decision->branchvar), SCIPvarGetUbLocal(decision->branchvar),
5470 SCIPgetSolVal(scip, NULL, decision->branchvar));
5471
5472 persistent->oldntotalnodes = SCIPgetNTotalNodes(scip);
5473 persistent->oldnnodelpiterations = SCIPgetNNodeLPIterations(scip);
5474 persistent->oldnnodelps = SCIPgetNNodeLPs(scip) + SCIPgetNNodeZeroIterationLPs(scip);
5475 branchingDecisionCopy(decision, persistent->olddecision);
5476 }
5477
5478 #ifdef SCIP_STATISTIC
5479 if( config->abbreviated && !status->cutoff && !status->maxnconsreached
5480 && !status->addedbinconss && !status->domred)
5481 {
5482 if( candidatelist->ncandidates > 0 )
5483 {
5484 assert(candidatelist->ncandidates <= statistics->maxnbestcands);
5485
5486 /* find the "FSB-index" of the decision */
5487 for( chosencandnr = 0; chosencandnr < candidatelist->ncandidates; ++chosencandnr )
5488 {
5489 if( decision->branchvar == candidatelist->candidates[chosencandnr]->branchvar )
5490 {
5491 break;
5492 }
5493 }
5494 assert(chosencandnr < candidatelist->ncandidates);
5495 }
5496 }
5497 }
5498 else
5499 {
5500 int probingdepth = 0;
5501 if( SCIPinProbing(scip) )
5502 probingdepth = SCIPgetProbingDepth(scip);
5503 statistics->stopafterfsb[probingdepth]++;
5504
5505 if( status->cutoff )
5506 {
5507 statistics->cutoffafterfsb[probingdepth]++;
5508 }
5509 else if( status->maxnconsreached )
5510 {
5511 statistics->domredafterfsb[probingdepth]++;
5512 }
5513 #endif
5514 }
5515
5516 TERMINATE:
5517 SCIP_CALL( SCIPendStrongbranch(scip) );
5518 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Ended probing.\n");
5519
5520 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Applying found data to the base node.\n");
5521
5522 /* apply domain reductions */
5523 if( domainreductions != NULL )
5524 {
5525 assert(config->usedomainreduction);
5526
5527 if( !status->cutoff )
5528 {
5529 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Applying domain reductions to the base node.\n");
5530 #ifdef SCIP_STATISTIC
5531 SCIP_CALL( applyDomainReductions(scip, config, baselpsol, domainreductions, &status->domredcutoff,
5532 &status->domred, statistics) );
5533 #else
5534 SCIP_CALL( applyDomainReductions(scip, config, baselpsol, domainreductions, &status->domredcutoff,
5535 &status->domred) );
5536 #endif
5537 }
5538 domainReductionsFree(scip, &domainreductions);
5539 }
5540
5541 /* apply binary constraints */
5542 if( binconsdata != NULL )
5543 {
5544 assert(config->usebincons);
5545 assert(binconsdata->binaryvars->nbinaryvars == 0);
5546
5547 if( !status->cutoff )
5548 {
5549 SCIP_NODE* basenode = SCIPgetCurrentNode(scip);
5550
5551 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Applying %d binary constraints to the base node.\n", binconsdata->conslist->nelements);
5552 #ifdef SCIP_STATISTIC
5553 SCIP_CALL( applyBinaryConstraints(scip, basenode, binconsdata->conslist, config,
5554 &status->addedbinconss, &status->cutoff, &status->domred, statistics) );
5555 #else
5556 SCIP_CALL( applyBinaryConstraints(scip, basenode, binconsdata->conslist, config,
5557 &status->addedbinconss, &status->cutoff, &status->domred) );
5558 #endif
5559 }
5560 else
5561 {
5562 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Discarding %d binary constraints because the base node is cut off.\n", binconsdata->conslist->nelements);
5563 }
5564 binConsDataFree(scip, &binconsdata);
5565 }
5566 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Applied found data to the base node.\n");
5567
5568 #if defined(SCIP_DEBUG) || defined(SCIP_STATISTIC)
5569 if( config->abbreviated )
5570 {
5571 if( status->domred )
5572 {
5573 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Lookahead Branching has added domain reductions. LAB restarts.\n");
5574
5575 #ifdef SCIP_STATISTIC
5576 if( candidatelist->ncandidates == 1 )
5577 statistics->nsingleafterfilter--;
5578 #endif
5579 }
5580 else if( status->addedbinconss )
5581 {
5582 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Lookahead Branching has added binary constraints. LAB restarts.\n");
5583
5584 #ifdef SCIP_STATISTIC
5585 if( candidatelist->ncandidates == 1 )
5586 statistics->nsingleafterfilter--;
5587 #endif
5588 }
5589 else if( status->cutoff )
5590 {
5591 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Lookahead Branching cut this node off.\n");
5592 }
5593 else if( candidatelist->ncandidates > 0 )
5594 {
5595 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Strong Branching would branch on variable <%s>\n",
5596 SCIPvarGetName(candidatelist->candidates[0]->branchvar));
5597
5598 if( isBranchFurther(status, FALSE) && branchingDecisionIsValid(decision) )
5599 {
5600 #ifdef SCIP_STATISTIC
5601 if( chosencandnr >= 0 )
5602 {
5603 ++statistics->chosenfsbcand[chosencandnr];
5604
5605 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "node %lld chose candidate %d score %16.9g vs %16.9g FSB: %16.9g vs %16.9g\n",
5606 SCIPnodeGetNumber(SCIPgetCurrentNode(scip)), chosencandnr,
5607 scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[chosencandnr]->branchvar)],
5608 scorecontainer->scores[SCIPvarGetProbindex(candidatelist->candidates[0]->branchvar)],
5609 bestscore, firstscore);
5610 }
5611 else
5612 assert(!performedlab);
5613 #endif
5614
5615 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Lookahead Branching branches on variable <%s>\n",
5616 SCIPvarGetName(decision->branchvar));
5617 }
5618 }
5619 else
5620 {
5621 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Something unexpected happened.");
5622 SCIPABORT();
5623 }
5624 }
5625 #endif
5626
5627 if( baselpsol != NULL )
5628 {
5629 SCIP_CALL( SCIPfreeSol(scip, &baselpsol) );
5630 }
5631
5632 return SCIP_OKAY;
5633 }
5634
5635 /**
5636 * We can use the previous result, stored in the branchruledata, if the branchingvariable (as an indicator) is set and
5637 * the current lp solution is equal to the previous lp solution.
5638 *
5639 * @return \ref TRUE, if we can branch on the previous decision, \ref FALSE, else.
5640 */
5641 static
isUsePreviousResult(SCIP * scip,SCIP_BRANCHRULEDATA * branchruledata)5642 SCIP_Bool isUsePreviousResult(
5643 SCIP* scip, /**< SCIP data structure */
5644 SCIP_BRANCHRULEDATA* branchruledata /**< branching rule data */
5645 )
5646 {
5647 PERSISTENTDATA* persistent;
5648
5649 assert(scip != NULL);
5650 assert(branchruledata != NULL);
5651
5652 persistent = branchruledata->persistent;
5653 assert(persistent != NULL);
5654
5655 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "check if previous result should be used: valid=%d, "\
5656 "nodes=%lld (old=%lld), iterations=%lld (old=%lld), lps=%lld (old=%lld)\n",
5657 branchingDecisionIsValid(persistent->olddecision),
5658 SCIPgetNTotalNodes(scip), persistent->oldntotalnodes,
5659 SCIPgetNNodeLPIterations(scip), persistent->oldnnodelpiterations,
5660 SCIPgetNNodeLPs(scip) + SCIPgetNNodeZeroIterationLPs(scip), persistent->oldnnodelps);
5661
5662 return branchingDecisionIsValid(persistent->olddecision)
5663 && (persistent->oldntotalnodes == SCIPgetNTotalNodes(scip))
5664 && (persistent->oldnnodelpiterations == SCIPgetNNodeLPIterations(scip))
5665 && (persistent->oldnnodelps == SCIPgetNNodeLPs(scip) + SCIPgetNNodeZeroIterationLPs(scip));
5666 }
5667
5668 /**
5669 * Uses the results from the previous run saved in the branchruledata to branch.
5670 * This is the case, if in the previous run only non-violating constraints were added. In that case we can use the
5671 * branching decision we would have made then.
5672 * If everything worked, the result pointer contains SCIP_BRANCHED.
5673 *
5674 * @return \ref SCIP_OKAY is returned if everything worked. Otherwise a suitable error code is passed. See \ref
5675 * SCIP_Retcode "SCIP_RETCODE" for a complete list of error codes.
5676 */
5677 static
usePreviousResult(SCIP * scip,SCIP_BRANCHRULEDATA * branchruledata,SCIP_RESULT * result)5678 SCIP_RETCODE usePreviousResult(
5679 SCIP* scip, /**< SCIP data structure */
5680 SCIP_BRANCHRULEDATA* branchruledata, /**< branching rule data */
5681 SCIP_RESULT* result /**< the pointer to the branching result */
5682 )
5683 {
5684 assert(scip != NULL);
5685 assert(branchruledata != NULL);
5686 assert(result != NULL);
5687 assert(branchruledata->config != NULL);
5688 assert(branchruledata->persistent != NULL);
5689 assert(branchruledata->persistent->olddecision != NULL);
5690
5691 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Branching based on previous solution.\n");
5692
5693 /* execute the actual branching */
5694 SCIP_CALL( branchOnVar(scip, branchruledata->config, branchruledata->persistent->olddecision) );
5695 *result = SCIP_BRANCHED;
5696
5697 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Branched based on previous solution. Variable <%s>\n",
5698 SCIPvarGetName(branchruledata->persistent->olddecision->branchvar));
5699
5700 /* reset the var pointer, as this is our indicator whether we should branch on prev data in the next call */
5701 branchruledata->persistent->olddecision->branchvar = NULL;
5702
5703 return SCIP_OKAY;
5704 }
5705
5706 /** free persistent data structure */
5707 static
freePersistent(SCIP * scip,SCIP_BRANCHRULEDATA * branchruledata)5708 SCIP_RETCODE freePersistent(
5709 SCIP* scip, /**< SCIP data structure */
5710 SCIP_BRANCHRULEDATA* branchruledata /**< branching rule data */
5711 )
5712 {
5713 PERSISTENTDATA* persistent;
5714 int nvars;
5715 int i;
5716
5717 assert(scip != NULL);
5718 assert(branchruledata != NULL);
5719
5720 persistent = branchruledata->persistent;
5721 assert(persistent != NULL);
5722
5723 nvars = persistent->nvars;
5724
5725 for( i = nvars - 1; i >= 0; i--)
5726 {
5727 assert(persistent->lastbranchdownres[i] != NULL);
5728 assert(persistent->lastbranchupres[i] != NULL);
5729
5730 SCIPfreeBlockMemory(scip, &persistent->lastbranchdownres[i]); /*lint !e866*/
5731 SCIPfreeBlockMemory(scip, &persistent->lastbranchupres[i]); /*lint !e866*/
5732 }
5733
5734 SCIPfreeBlockMemory(scip, &branchruledata->persistent->olddecision);
5735
5736 assert(persistent->lastbranchlpobjval != NULL);
5737 assert(persistent->lastbranchdownres != NULL);
5738 assert(persistent->lastbranchupres != NULL);
5739 assert(persistent->lastbranchnlps != NULL);
5740 assert(persistent->lastbranchid != NULL);
5741
5742 SCIPfreeBlockMemoryArray(scip, &persistent->lastbranchlpobjval, nvars);
5743 SCIPfreeBlockMemoryArray(scip, &persistent->lastbranchdownres, nvars);
5744 SCIPfreeBlockMemoryArray(scip, &persistent->lastbranchupres, nvars);
5745 SCIPfreeBlockMemoryArray(scip, &persistent->lastbranchnlps, nvars);
5746 SCIPfreeBlockMemoryArray(scip, &persistent->lastbranchid, nvars);
5747
5748 branchruledata->isinitialized = FALSE;
5749
5750 return SCIP_OKAY;
5751 }
5752
5753 /** initializes the branchruledata and the contained structs */
5754 static
initBranchruleData(SCIP * scip,SCIP_BRANCHRULEDATA * branchruledata)5755 SCIP_RETCODE initBranchruleData(
5756 SCIP* scip, /**< SCIP data structure */
5757 SCIP_BRANCHRULEDATA* branchruledata /**< the branch rule data to initialize */
5758 )
5759 {
5760 int nvars;
5761 int i;
5762
5763 assert(scip != NULL);
5764 assert(branchruledata != NULL);
5765
5766 /* the branching rule data is already initialized and no new variables have been added in the meantime */
5767 if( branchruledata->isinitialized &&
5768 (SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) == branchruledata->persistent->nvars) )
5769 return SCIP_OKAY;
5770
5771 if( branchruledata->isinitialized )
5772 {
5773 SCIP_CALL( freePersistent(scip, branchruledata) );
5774 }
5775
5776 /* The variables given by the SCIPgetVars() array are sorted with the binaries at first and the integer variables
5777 * directly afterwards. With the SCIPvarGetProbindex() method we can access the index of a given variable in the
5778 * SCIPgetVars() array and as such we can use it to access our arrays which should only contain binary and integer
5779 * variables.
5780 */
5781 nvars = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip);
5782
5783 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &branchruledata->persistent->lastbranchid, nvars) );
5784 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &branchruledata->persistent->lastbranchnlps, nvars) );
5785 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &branchruledata->persistent->lastbranchupres, nvars) );
5786 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &branchruledata->persistent->lastbranchdownres, nvars) );
5787 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &branchruledata->persistent->lastbranchlpobjval, nvars) );
5788 branchruledata->persistent->nvars = nvars;
5789 branchruledata->persistent->oldntotalnodes = -1;
5790 branchruledata->persistent->oldnnodelpiterations = -1;
5791 branchruledata->persistent->oldnnodelps = -1;
5792
5793 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata->persistent->olddecision) );
5794 branchingDecisionInit(scip, branchruledata->persistent->olddecision);
5795
5796 for( i = 0; i < nvars; i++ )
5797 {
5798 branchruledata->persistent->lastbranchid[i] = -1;
5799 branchruledata->persistent->lastbranchnlps[i] = 0;
5800
5801 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata->persistent->lastbranchupres[i]) ); /*lint !e866*/
5802 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata->persistent->lastbranchdownres[i]) ); /*lint !e866*/
5803 }
5804
5805 branchruledata->isinitialized = TRUE;
5806
5807 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Initialized the branchruledata\n");
5808
5809 return SCIP_OKAY;
5810 }
5811
5812 /*
5813 * Callback methods of branching rule
5814 */
5815
5816 /** copy method for branchrule plugins (called when SCIP copies plugins) */
5817 static
SCIP_DECL_BRANCHCOPY(branchCopyLookahead)5818 SCIP_DECL_BRANCHCOPY(branchCopyLookahead)
5819 { /*lint --e{715}*/
5820 assert(scip != NULL);
5821 assert(branchrule != NULL);
5822 assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0);
5823
5824 return SCIP_OKAY;
5825 }
5826
5827 /** destructor of branching rule to free user data (called when SCIP is exiting) */
5828 static
SCIP_DECL_BRANCHFREE(branchFreeLookahead)5829 SCIP_DECL_BRANCHFREE(branchFreeLookahead)
5830 { /*lint --e{715}*/
5831 SCIP_BRANCHRULEDATA* branchruledata;
5832
5833 branchruledata = SCIPbranchruleGetData(branchrule);
5834 assert(branchruledata != NULL);
5835 assert(branchruledata->config != NULL);
5836 assert(branchruledata->persistent != NULL);
5837
5838 SCIPfreeBlockMemory(scip, &branchruledata->persistent);
5839 SCIPfreeBlockMemory(scip, &branchruledata->config);
5840 SCIPfreeBlockMemory(scip, &branchruledata);
5841 SCIPbranchruleSetData(branchrule, NULL);
5842
5843 return SCIP_OKAY;
5844 }
5845
5846 /** initialization method of branching rule (called after problem was transformed) */
5847 static
SCIP_DECL_BRANCHINIT(branchInitLookahead)5848 SCIP_DECL_BRANCHINIT(branchInitLookahead)
5849 { /*lint --e{715}*/
5850 SCIP_BRANCHRULEDATA* branchruledata;
5851
5852 assert(scip != NULL);
5853 assert(branchrule != NULL);
5854
5855 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Entering branchInitLookahead\n");
5856
5857 branchruledata = SCIPbranchruleGetData(branchrule);
5858 assert(branchruledata != NULL);
5859 assert(branchruledata->persistent != NULL);
5860
5861 branchruledata->persistent->restartindex = 0;
5862
5863 #ifdef SCIP_STATISTIC
5864 {
5865 int recursiondepth;
5866 int maxncands;
5867
5868 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Allocating space for the statistics struct.\n");
5869
5870 recursiondepth = branchruledata->config->recursiondepth;
5871 maxncands = branchruledata->config->maxncands;
5872 maxncands = MIN(maxncands, SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip));
5873
5874 SCIP_CALL( SCIPallocMemory(scip, &branchruledata->statistics) );
5875 /* RESULT enum is 1 based, so use MAXRESULT + 1 as array size with unused 0 element */
5876 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nresults, MAXRESULT + 1) );
5877 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nsinglecutoffs, recursiondepth) );
5878 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nfullcutoffs, recursiondepth) );
5879 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nlpssolved, recursiondepth) );
5880 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nlpssolvedfsb, recursiondepth) );
5881 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nlpiterations, recursiondepth) );
5882 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nlpiterationsfsb, recursiondepth) );
5883 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->nduplicatelps, recursiondepth) );
5884 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->npropdomred, recursiondepth) );
5885 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->noldbranchused, recursiondepth) );
5886 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->noldbranchusedfsb, recursiondepth) );
5887 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->chosenfsbcand, maxncands) );
5888 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->domredafterfsb, recursiondepth) );
5889 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->cutoffafterfsb, recursiondepth) );
5890 SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->statistics->stopafterfsb, recursiondepth) );
5891
5892 branchruledata->statistics->recursiondepth = recursiondepth;
5893 branchruledata->statistics->maxnbestcands = maxncands;
5894
5895 statisticsInit(branchruledata->statistics);
5896 }
5897 #endif
5898
5899 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Leaving branchInitLookahead\n");
5900
5901 return SCIP_OKAY;
5902 }
5903
5904
5905 /** deinitialization method of branching rule (called before transformed problem is freed) */
5906 static
SCIP_DECL_BRANCHEXIT(branchExitLookahead)5907 SCIP_DECL_BRANCHEXIT(branchExitLookahead)
5908 { /*lint --e{715}*/
5909 #ifdef SCIP_STATISTIC
5910 SCIP_BRANCHRULEDATA* branchruledata;
5911 STATISTICS* statistics;
5912
5913 branchruledata = SCIPbranchruleGetData(branchrule);
5914 assert(branchruledata != NULL);
5915
5916 statistics = branchruledata->statistics;
5917 assert(statistics != NULL);
5918
5919 statisticsPrint(scip, statistics);
5920
5921 SCIPfreeMemoryArray(scip, &statistics->stopafterfsb);
5922 SCIPfreeMemoryArray(scip, &statistics->cutoffafterfsb);
5923 SCIPfreeMemoryArray(scip, &statistics->domredafterfsb);
5924 SCIPfreeMemoryArray(scip, &statistics->chosenfsbcand);
5925 SCIPfreeMemoryArray(scip, &statistics->noldbranchusedfsb);
5926 SCIPfreeMemoryArray(scip, &statistics->noldbranchused);
5927 SCIPfreeMemoryArray(scip, &statistics->npropdomred);
5928 SCIPfreeMemoryArray(scip, &statistics->nlpiterationsfsb);
5929 SCIPfreeMemoryArray(scip, &statistics->nlpiterations);
5930 SCIPfreeMemoryArray(scip, &statistics->nduplicatelps);
5931 SCIPfreeMemoryArray(scip, &statistics->nlpssolvedfsb);
5932 SCIPfreeMemoryArray(scip, &statistics->nlpssolved);
5933 SCIPfreeMemoryArray(scip, &statistics->nfullcutoffs);
5934 SCIPfreeMemoryArray(scip, &statistics->nsinglecutoffs);
5935 SCIPfreeMemoryArray(scip, &statistics->nresults);
5936 SCIPfreeMemory(scip, &statistics);
5937 #endif
5938
5939 return SCIP_OKAY;
5940 }
5941
5942 /** solving process deinitialization method of branching rule (called before branch and bound process data is freed) */
5943 static
SCIP_DECL_BRANCHEXITSOL(branchExitSolLookahead)5944 SCIP_DECL_BRANCHEXITSOL(branchExitSolLookahead)
5945 { /*lint --e{715}*/
5946 SCIP_BRANCHRULEDATA* branchruledata;
5947
5948 branchruledata = SCIPbranchruleGetData(branchrule);
5949 assert(branchruledata != NULL);
5950
5951 if( branchruledata->isinitialized )
5952 {
5953 SCIP_CALL( freePersistent(scip, branchruledata) );
5954 }
5955
5956 return SCIP_OKAY;
5957 }
5958
5959 /** branching execution method for fractional LP solutions */
5960 static
SCIP_DECL_BRANCHEXECLP(branchExeclpLookahead)5961 SCIP_DECL_BRANCHEXECLP(branchExeclpLookahead)
5962 { /*lint --e{715}*/
5963 SCIP_BRANCHRULEDATA* branchruledata;
5964 CONFIGURATION* config;
5965 SCIP_Bool userusebincons;
5966
5967 assert(branchrule != NULL);
5968 assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0);
5969 assert(scip != NULL);
5970 assert(result != NULL);
5971
5972 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Entering branchExeclpLookahead at node %lld.\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip)));
5973
5974 branchruledata = SCIPbranchruleGetData(branchrule);
5975 assert(branchruledata != NULL);
5976
5977 config = branchruledata->config;
5978
5979 /* we are only allowed to add binary constraints, if the corresponding flag is given */
5980 userusebincons = config->usebincons;
5981 config->usebincons = config->usebincons && allowaddcons;
5982
5983 SCIP_CALL( initBranchruleData(scip, branchruledata) );
5984
5985 if( config->storeunviolatedsol
5986 && isUsePreviousResult(scip, branchruledata) )
5987 {
5988 /* in case we stopped the previous run without a branching decision, we have stored the decision and execute it
5989 * now */
5990 SCIP_CALL( usePreviousResult(scip, branchruledata, result) );
5991
5992 #ifdef SCIP_STATISTIC
5993 branchruledata->statistics->noldcandidate++;
5994 #endif
5995 }
5996 else
5997 {
5998 BRANCHINGDECISION* decision;
5999 SCORECONTAINER* scorecontainer = NULL;
6000 CANDIDATELIST* candidatelist;
6001 STATUS* status;
6002 #ifdef SCIP_STATISTIC
6003 LOCALSTATISTICS* localstats;
6004 #endif
6005
6006 /* create a struct to store the algorithm status */
6007 SCIP_CALL( statusCreate(scip, &status) );
6008
6009 /* create a struct to store the branching decision (in case there is one) */
6010 SCIP_CALL( branchingDecisionCreate(scip, &decision) );
6011 if( config->abbreviated )
6012 {
6013 /* allocate and init the container used to store the FSB scores, later used to filter the candidates */
6014 SCIP_CALL( scoreContainerCreate(scip, &scorecontainer, config) );
6015 }
6016
6017 SCIP_CALL( candidateListGetAllFractionalCandidates(scip, &candidatelist) );
6018
6019 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "The base lp has <%i> variables with fractional value.\n",
6020 candidatelist->ncandidates);
6021
6022 /* execute the main logic */
6023 #ifdef SCIP_STATISTIC
6024 /* create a struct to store the statistics needed for this single run */
6025 SCIP_CALL( localStatisticsAllocate(scip, &localstats) );
6026 SCIP_CALL( selectVarStart(scip, config, branchruledata->persistent, status, decision,
6027 scorecontainer, candidatelist, branchruledata->statistics, localstats) );
6028 #else
6029 SCIP_CALL( selectVarStart(scip, config, branchruledata->persistent, status, decision,
6030 scorecontainer, candidatelist) );
6031 #endif
6032
6033 if( status->cutoff || status->domredcutoff )
6034 {
6035 *result = SCIP_CUTOFF;
6036 #ifdef SCIP_STATISTIC
6037 branchruledata->statistics->ncutoffproofnodes += localstats->ncutoffproofnodes;
6038 #endif
6039 }
6040 else if( status->addedbinconss )
6041 {
6042 *result = SCIP_CONSADDED;
6043 }
6044 else if( status->domred )
6045 {
6046 *result = SCIP_REDUCEDDOM;
6047 }
6048 else if( status->lperror )
6049 {
6050 #ifdef SCIP_STATISTIC
6051 ++branchruledata->statistics->nlperrorcalls;
6052 #endif
6053 if( !branchingDecisionIsValid(decision) )
6054 {
6055 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "LP error with no valid candidate: select first candidate variable\n");
6056
6057 assert(candidatelist->ncandidates > 0);
6058 decision->branchvar = candidatelist->candidates[0]->branchvar;
6059 decision->branchval = candidatelist->candidates[0]->branchval;
6060 }
6061 }
6062 else if( status->maxnconsreached )
6063 {
6064 /* this case may occure if the domain reductions that reached the limit were already applied via domain
6065 * propagation
6066 */
6067 *result = SCIP_REDUCEDDOM;
6068 }
6069 #ifdef SCIP_STATISTIC
6070 else if( status->limitreached )
6071 {
6072 ++branchruledata->statistics->nlimitcalls;
6073 }
6074 #endif
6075
6076 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Result before branching is %s\n", getStatusString(*result));
6077
6078 if( *result != SCIP_CUTOFF /* a variable could not be branched in any direction or any of the calculated domain
6079 * reductions was infeasible */
6080 && *result != SCIP_REDUCEDDOM /* the domain of a variable was reduced by evaluating the calculated cutoffs */
6081 && *result != SCIP_CONSADDED /* implied binary constraints were already added */
6082 && !status->depthtoosmall /* branching depth wasn't high enough */
6083 && branchingDecisionIsValid(decision)
6084 /*&& (0 <= bestcand && bestcand < nlpcands)*/ /* no valid candidate index could be found */
6085 )
6086 {
6087 LABdebugMessage(scip, SCIP_VERBLEVEL_NORMAL, " -> %d candidates, selected variable <%s> (solval=%g, down=%.9g, "
6088 "up=%.9g)\n", candidatelist->ncandidates, SCIPvarGetName(decision->branchvar), decision->branchval,
6089 decision->downdb, decision->updb);
6090
6091 /* execute the branching as a result of the branching logic */
6092 SCIP_CALL( branchOnVar(scip, config, decision) );
6093
6094 *result = SCIP_BRANCHED;
6095 }
6096
6097 #ifdef SCIP_DEBUG
6098 LABdebugMessage(scip, SCIP_VERBLEVEL_FULL, "Result after branching is %s\n", getStatusString(*result));
6099
6100 if( *result == SCIP_BRANCHED )
6101 {
6102 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Finished LookaheadBranching by branching.\n");
6103 }
6104 else if( *result == SCIP_REDUCEDDOM )
6105 {
6106 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Finished LookaheadBranching by reducing domains.\n");
6107 }
6108 else if( *result == SCIP_CUTOFF )
6109 {
6110 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Finished LookaheadBranching by cutting off, as the current "
6111 "problem is infeasible.\n");
6112 }
6113 else if( *result == SCIP_CONSADDED )
6114 {
6115 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Finished LookaheadBranching by adding constraints.\n");
6116 }
6117 else if( status->depthtoosmall )
6118 {
6119 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: The remaining tree depth did not allow for multi level "
6120 "lookahead branching.\n");
6121 }
6122 else
6123 {
6124 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Result: Could not find any variable to branch on.\n");
6125 }
6126 #endif
6127
6128 #ifdef SCIP_STATISTIC
6129 localStatisticsFree(scip, &localstats);
6130 #endif
6131 SCIP_CALL( candidateListFree(scip, &candidatelist) );
6132
6133 /* scorecontainer != NULL iff branchruledata->config->abbreviated == TRUE */
6134 if( scorecontainer != NULL )
6135 {
6136 SCIP_CALL( scoreContainerFree(scip, &scorecontainer) );
6137 }
6138 branchingDecisionFree(scip, &decision);
6139 statusFree(scip, &status);
6140 }
6141
6142 #ifdef SCIP_STATISTIC
6143 assert(*result >= 1);
6144 assert(*result <= MAXRESULT);
6145 branchruledata->statistics->ntotalresults++;
6146 branchruledata->statistics->nresults[*result]++;
6147
6148 if( config->abbreviated )
6149 {
6150 int sum;
6151 int i;
6152
6153 sum = branchruledata->statistics->nsinglecandidate + branchruledata->statistics->nsingleafterfilter
6154 + branchruledata->statistics->noldcandidate + branchruledata->statistics->nlperrorcalls
6155 + branchruledata->statistics->nlimitcalls;
6156
6157 for( i = 0; i < branchruledata->statistics->maxnbestcands; i++ )
6158 {
6159 sum += branchruledata->statistics->chosenfsbcand[i];
6160 }
6161 if( sum != branchruledata->statistics->nresults[SCIP_BRANCHED] )
6162 {
6163 printf("branched = %d != sum = %d (%d/%d/%d/%d/%d)\n",
6164 branchruledata->statistics->nresults[SCIP_BRANCHED], sum,
6165 branchruledata->statistics->nsinglecandidate, branchruledata->statistics->nsingleafterfilter,
6166 branchruledata->statistics->noldcandidate,
6167 branchruledata->statistics->nlperrorcalls, branchruledata->statistics->nlimitcalls);
6168 assert(SCIPisStopped(scip));
6169 }
6170 assert(sum == branchruledata->statistics->nresults[SCIP_BRANCHED]);
6171 }
6172
6173 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "#### ncutoffproofnodes: %d ndomredproofnodes: %d\n",
6174 branchruledata->statistics->ncutoffproofnodes, branchruledata->statistics->ndomredproofnodes);
6175 #endif
6176
6177 config->usebincons = userusebincons;
6178
6179 LABdebugMessage(scip, SCIP_VERBLEVEL_HIGH, "Exiting branchExeclpLookahead.\n");
6180 return SCIP_OKAY;
6181 }
6182
6183 /*
6184 * branching rule specific interface methods
6185 */
6186
6187 /** creates the lookahead branching rule and includes it in SCIP */
SCIPincludeBranchruleLookahead(SCIP * scip)6188 SCIP_RETCODE SCIPincludeBranchruleLookahead(
6189 SCIP* scip /**< SCIP data structure */
6190 )
6191 {
6192 SCIP_BRANCHRULEDATA* branchruledata;
6193 SCIP_BRANCHRULE* branchrule;
6194
6195 /* create lookahead branching rule data */
6196 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata) );
6197 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata->config) );
6198 SCIP_CALL( SCIPallocBlockMemory(scip, &branchruledata->persistent) );
6199 branchruledata->persistent->restartindex = 0;
6200 branchruledata->isinitialized = FALSE;
6201 branchruledata->config->inscoring = FALSE;
6202
6203 /* include branching rule */
6204 SCIP_CALL( SCIPincludeBranchruleBasic(scip, &branchrule, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY,
6205 BRANCHRULE_MAXDEPTH, BRANCHRULE_MAXBOUNDDIST, branchruledata) );
6206
6207 assert(branchrule != NULL);
6208
6209 /* set non fundamental callbacks via setter functions */
6210 SCIP_CALL( SCIPsetBranchruleCopy(scip, branchrule, branchCopyLookahead) );
6211 SCIP_CALL( SCIPsetBranchruleFree(scip, branchrule, branchFreeLookahead) );
6212 SCIP_CALL( SCIPsetBranchruleInit(scip, branchrule, branchInitLookahead) );
6213 SCIP_CALL( SCIPsetBranchruleExit(scip, branchrule, branchExitLookahead) );
6214 SCIP_CALL( SCIPsetBranchruleExitsol(scip, branchrule, branchExitSolLookahead) );
6215 SCIP_CALL( SCIPsetBranchruleExecLp(scip, branchrule, branchExeclpLookahead) );
6216
6217 /* add lookahead branching rule parameters */
6218 SCIP_CALL( SCIPaddBoolParam(scip,
6219 "branching/lookahead/useimpliedbincons",
6220 "should binary constraints be collected and applied?",
6221 &branchruledata->config->usebincons, TRUE, DEFAULT_USEBINARYCONSTRAINTS, NULL, NULL) );
6222 SCIP_CALL( SCIPaddIntParam(scip,
6223 "branching/lookahead/addbinconsrow",
6224 "should binary constraints be added as rows to the base LP? (0: no, 1: separate, 2: as initial rows)",
6225 &branchruledata->config->addbinconsrow, TRUE, DEFAULT_ADDBINCONSROW, 0, 2, NULL, NULL) );
6226 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/maxnviolatedcons",
6227 "how many constraints that are violated by the base lp solution should be gathered until the rule is stopped and "\
6228 "they are added? [0 for unrestricted]",
6229 &branchruledata->config->maxnviolatedcons, TRUE, DEFAULT_MAXNVIOLATEDCONS, 0, INT_MAX, NULL, NULL) );
6230 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/maxnviolatedbincons",
6231 "how many binary constraints that are violated by the base lp solution should be gathered until the rule is "\
6232 "stopped and they are added? [0 for unrestricted]",
6233 &branchruledata->config->maxnviolatedbincons, TRUE, DEFAULT_MAXNVIOLATEDBINCONS, 0, INT_MAX, NULL, NULL) );
6234 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/maxnviolateddomreds",
6235 "how many domain reductions that are violated by the base lp solution should be gathered until the rule is "\
6236 "stopped and they are added? [0 for unrestricted]",
6237 &branchruledata->config->maxnviolateddomreds, TRUE, DEFAULT_MAXNVIOLATEDDOMREDS, 0, INT_MAX, NULL, NULL) );
6238 SCIP_CALL( SCIPaddLongintParam(scip,
6239 "branching/lookahead/reevalage",
6240 "max number of LPs solved after which a previous prob branching results are recalculated",
6241 &branchruledata->config->reevalage, TRUE, DEFAULT_REEVALAGE, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
6242 SCIP_CALL( SCIPaddLongintParam(scip,
6243 "branching/lookahead/reevalagefsb",
6244 "max number of LPs solved after which a previous FSB scoring results are recalculated",
6245 &branchruledata->config->reevalagefsb, TRUE, DEFAULT_REEVALAGEFSB, 0LL, SCIP_LONGINT_MAX, NULL, NULL) );
6246 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/recursiondepth",
6247 "the max depth of LAB.",
6248 &branchruledata->config->recursiondepth, TRUE, DEFAULT_RECURSIONDEPTH, 1, INT_MAX, NULL, NULL) );
6249 SCIP_CALL( SCIPaddBoolParam(scip,
6250 "branching/lookahead/usedomainreduction",
6251 "should domain reductions be collected and applied?",
6252 &branchruledata->config->usedomainreduction, TRUE, DEFAULT_USEDOMAINREDUCTION, NULL, NULL) );
6253 SCIP_CALL( SCIPaddBoolParam(scip,
6254 "branching/lookahead/mergedomainreductions",
6255 "should domain reductions of feasible siblings should be merged?",
6256 &branchruledata->config->mergedomainreductions, TRUE, DEFAULT_MERGEDOMAINREDUCTIONS, NULL, NULL) );
6257 SCIP_CALL( SCIPaddBoolParam(scip,
6258 "branching/lookahead/prefersimplebounds",
6259 "should domain reductions only be applied if there are simple bound changes?",
6260 &branchruledata->config->prefersimplebounds, TRUE, DEFAULT_PREFERSIMPLEBOUNDS, NULL, NULL) );
6261 SCIP_CALL( SCIPaddBoolParam(scip,
6262 "branching/lookahead/onlyvioldomreds",
6263 "should only domain reductions that violate the LP solution be applied?",
6264 &branchruledata->config->onlyvioldomreds, TRUE, DEFAULT_ONLYVIOLDOMREDS, NULL, NULL) );
6265 SCIP_CALL( SCIPaddBoolParam(scip,
6266 "branching/lookahead/addnonviocons",
6267 "should binary constraints, that are not violated by the base LP, be collected and added?",
6268 &branchruledata->config->addnonviocons, TRUE, DEFAULT_ADDNONVIOCONS, NULL, NULL) );
6269 SCIP_CALL( SCIPaddBoolParam(scip,
6270 "branching/lookahead/abbreviated",
6271 "toggles the abbreviated LAB.",
6272 &branchruledata->config->abbreviated, TRUE, DEFAULT_ABBREVIATED, NULL, NULL) );
6273 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/maxncands",
6274 "if abbreviated: The max number of candidates to consider at the node.",
6275 &branchruledata->config->maxncands, TRUE, DEFAULT_MAXNCANDS, 1, INT_MAX, NULL, NULL) );
6276 SCIP_CALL( SCIPaddIntParam(scip, "branching/lookahead/maxndeepercands",
6277 "if abbreviated: The max number of candidates to consider per deeper node.",
6278 &branchruledata->config->maxndeepercands, TRUE, DEFAULT_MAXNDEEPERCANDS, 0, INT_MAX, NULL, NULL) );
6279 SCIP_CALL( SCIPaddBoolParam(scip,
6280 "branching/lookahead/reusebasis",
6281 "if abbreviated: Should the information gathered to obtain the best candidates be reused?",
6282 &branchruledata->config->reusebasis, TRUE, DEFAULT_REUSEBASIS, NULL, NULL) );
6283 SCIP_CALL( SCIPaddBoolParam(scip,
6284 "branching/lookahead/storeunviolatedsol",
6285 "if only non violating constraints are added, should the branching decision be stored till the next call?",
6286 &branchruledata->config->storeunviolatedsol, TRUE, DEFAULT_STOREUNVIOLATEDSOL, NULL, NULL) );
6287 SCIP_CALL( SCIPaddBoolParam(scip,
6288 "branching/lookahead/abbrevpseudo",
6289 "if abbreviated: Use pseudo costs to estimate the score of a candidate.",
6290 &branchruledata->config->abbrevpseudo, TRUE, DEFAULT_ABBREVPSEUDO, NULL, NULL) );
6291 SCIP_CALL( SCIPaddBoolParam(scip,
6292 "branching/lookahead/level2avgscore",
6293 "should the average score be used for uninitialized scores in level 2?",
6294 &branchruledata->config->level2avgscore, TRUE, DEFAULT_LEVEL2AVGSCORE, NULL, NULL) );
6295 SCIP_CALL( SCIPaddBoolParam(scip,
6296 "branching/lookahead/level2zeroscore",
6297 "should uninitialized scores in level 2 be set to 0?",
6298 &branchruledata->config->level2zeroscore, TRUE, DEFAULT_LEVEL2ZEROSCORE, NULL, NULL) );
6299 SCIP_CALL( SCIPaddBoolParam(scip,
6300 "branching/lookahead/addclique",
6301 "add binary constraints with two variables found at the root node also as a clique",
6302 &branchruledata->config->addclique, TRUE, DEFAULT_ADDCLIQUE, NULL, NULL) );
6303 SCIP_CALL( SCIPaddBoolParam(scip,
6304 "branching/lookahead/propagate",
6305 "should domain propagation be executed before each temporary node is solved?",
6306 &branchruledata->config->propagate, TRUE, DEFAULT_PROPAGATE, NULL, NULL) );
6307 SCIP_CALL( SCIPaddBoolParam(scip,
6308 "branching/lookahead/uselevel2data",
6309 "should branching data generated at depth level 2 be stored for re-using it?",
6310 &branchruledata->config->uselevel2data, TRUE, DEFAULT_USELEVEL2DATA, NULL, NULL) );
6311 SCIP_CALL( SCIPaddBoolParam(scip,
6312 "branching/lookahead/applychildbounds",
6313 "should bounds known for child nodes be applied?",
6314 &branchruledata->config->applychildbounds, TRUE, DEFAULT_APPLYCHILDBOUNDS, NULL, NULL) );
6315 SCIP_CALL( SCIPaddBoolParam(scip,
6316 "branching/lookahead/enforcemaxdomreds",
6317 "should the maximum number of domain reductions maxnviolateddomreds be enforced?",
6318 &branchruledata->config->enforcemaxdomreds, TRUE, DEFAULT_ENFORCEMAXDOMREDS, NULL, NULL) );
6319 SCIP_CALL( SCIPaddBoolParam(scip,
6320 "branching/lookahead/updatebranchingresults",
6321 "should branching results (and scores) be updated w.r.t. proven dual bounds?",
6322 &branchruledata->config->updatebranchingresults, TRUE, DEFAULT_UPDATEBRANCHINGRESULTS, NULL, NULL) );
6323 SCIP_CALL( SCIPaddIntParam(scip,
6324 "branching/lookahead/maxproprounds",
6325 "maximum number of propagation rounds to perform at each temporary node (-1: unlimited, 0: SCIP default)",
6326 &branchruledata->config->maxproprounds, TRUE, DEFAULT_MAXPROPROUNDS, -1, INT_MAX, NULL, NULL) );
6327 SCIP_CALL( SCIPaddCharParam(scip,
6328 "branching/lookahead/scoringfunction",
6329 "scoring function to be used at the base level",
6330 &branchruledata->config->scoringfunction, TRUE, DEFAULT_SCORINGFUNCTION, "dfswplcra", NULL, NULL) );
6331 SCIP_CALL( SCIPaddCharParam(scip,
6332 "branching/lookahead/deeperscoringfunction",
6333 "scoring function to be used at deeper levels",
6334 &branchruledata->config->deeperscoringfunction, TRUE, DEFAULT_DEEPERSCORINGFUNCTION, "dfswlcrx", NULL, NULL) );
6335 SCIP_CALL( SCIPaddCharParam(scip,
6336 "branching/lookahead/scoringscoringfunction",
6337 "scoring function to be used during FSB scoring",
6338 &branchruledata->config->scoringscoringfunction, TRUE, DEFAULT_SCORINGSCORINGFUNCTION, "dfswlcr", NULL, NULL) );
6339 SCIP_CALL( SCIPaddRealParam(scip,
6340 "branching/lookahead/minweight",
6341 "if scoringfunction is 's', this value is used to weight the min of the gains of two child problems in the convex combination",
6342 &branchruledata->config->minweight, TRUE, DEFAULT_MINWEIGHT, 0.0, SCIP_REAL_MAX, NULL, NULL) );
6343 SCIP_CALL( SCIPaddRealParam(scip,
6344 "branching/lookahead/worsefactor",
6345 "if the FSB score is of a candidate is worse than the best by this factor, skip this candidate (-1: disable)",
6346 &branchruledata->config->worsefactor, TRUE, DEFAULT_WORSEFACTOR, -1.0, SCIP_REAL_MAX, NULL, NULL) );
6347 SCIP_CALL( SCIPaddBoolParam(scip,
6348 "branching/lookahead/filterbymaxgain",
6349 "should lookahead branching only be applied if the max gain in level 1 is not uniquely that of the best candidate?",
6350 &branchruledata->config->filterbymaxgain, TRUE, DEFAULT_FILTERBYMAXGAIN, NULL, NULL) );
6351
6352 return SCIP_OKAY;
6353 }
6354