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 /**@file conflict.c
16 * @ingroup OTHER_CFILES
17 * @brief methods and datastructures for conflict analysis
18 * @author Tobias Achterberg
19 * @author Timo Berthold
20 * @author Stefan Heinz
21 * @author Marc Pfetsch
22 * @author Michael Winkler
23 * @author Jakob Witzig
24 *
25 * This file implements a conflict analysis method like the one used in modern
26 * SAT solvers like zchaff. The algorithm works as follows:
27 *
28 * Given is a set of bound changes that are not allowed being applied simultaneously, because they
29 * render the current node infeasible (e.g. because a single constraint is infeasible in the these
30 * bounds, or because the LP relaxation is infeasible). The goal is to deduce a clause on variables
31 * -- a conflict clause -- representing the "reason" for this conflict, i.e., the branching decisions
32 * or the deductions (applied e.g. in domain propagation) that lead to the conflict. This clause can
33 * then be added to the constraint set to help cutting off similar parts of the branch and bound
34 * tree, that would lead to the same conflict. A conflict clause can also be generated, if the
35 * conflict was detected by a locally valid constraint. In this case, the resulting conflict clause
36 * is also locally valid in the same depth as the conflict detecting constraint. If all involved
37 * variables are binary, a linear (set covering) constraint can be generated, otherwise a bound
38 * disjunction constraint is generated. Details are given in
39 *
40 * Tobias Achterberg, Conflict Analysis in Mixed Integer Programming@n
41 * Discrete Optimization, 4, 4-20 (2007)
42 *
43 * See also @ref CONF. Here is an outline of the algorithm:
44 *
45 * -# Put all the given bound changes to a priority queue, which is ordered,
46 * such that the bound change that was applied last due to branching or deduction
47 * is at the top of the queue. The variables in the queue are always active
48 * problem variables. Because binary variables are preferred over general integer
49 * variables, integer variables are put on the priority queue prior to the binary
50 * variables. Create an empty conflict set.
51 * -# Remove the top bound change b from the priority queue.
52 * -# Perform the following case distinction:
53 * -# If the remaining queue is non-empty, and bound change b' (the one that is now
54 * on the top of the queue) was applied at the same depth level as b, and if
55 * b was a deduction with known inference reason, and if the inference constraint's
56 * valid depth is smaller or equal to the conflict detecting constraint's valid
57 * depth:
58 * - Resolve bound change b by asking the constraint that inferred the
59 * bound change to put all the bound changes on the priority queue, that
60 * lead to the deduction of b.
61 * Note that these bound changes have at most the same inference depth
62 * level as b, and were deduced earlier than b.
63 * -# Otherwise, the bound change b was a branching decision or a deduction with
64 * missing inference reason, or the inference constraint's validity is more local
65 * than the one of the conflict detecting constraint.
66 * - If a the bound changed corresponds to a binary variable, add it or its
67 * negation to the conflict set, depending on which of them is currently fixed to
68 * FALSE (i.e., the conflict set consists of literals that cannot be FALSE
69 * altogether at the same time).
70 * - Otherwise put the bound change into the conflict set.
71 * Note that if the bound change was a branching, all deduced bound changes
72 * remaining in the priority queue have smaller inference depth level than b,
73 * since deductions are always applied after the branching decisions. However,
74 * there is the possibility, that b was a deduction, where the inference
75 * reason was not given or the inference constraint was too local.
76 * With this lack of information, we must treat the deduced bound change like
77 * a branching, and there may exist other deduced bound changes of the same
78 * inference depth level in the priority queue.
79 * -# If priority queue is non-empty, goto step 2.
80 * -# The conflict set represents the conflict clause saying that at least one
81 * of the conflict variables must take a different value. The conflict set is then passed
82 * to the conflict handlers, that may create a corresponding constraint (e.g. a logicor
83 * constraint or bound disjunction constraint) out of these conflict variables and
84 * add it to the problem.
85 *
86 * If all deduced bound changes come with (global) inference information, depending on
87 * the conflict analyzing strategy, the resulting conflict set has the following property:
88 * - 1-FirstUIP: In the depth level where the conflict was found, at most one variable
89 * assigned at that level is member of the conflict set. This conflict variable is the
90 * first unique implication point of its depth level (FUIP).
91 * - All-FirstUIP: For each depth level, at most one variable assigned at that level is
92 * member of the conflict set. This conflict variable is the first unique implication
93 * point of its depth level (FUIP).
94 *
95 * The user has to do the following to get the conflict analysis running in its
96 * current implementation:
97 * - A constraint handler or propagator supporting the conflict analysis must implement
98 * the CONSRESPROP/PROPRESPROP call, that processes a bound change inference b and puts all
99 * the reason bounds leading to the application of b with calls to
100 * SCIPaddConflictBound() on the conflict queue (algorithm step 3.(a)).
101 * - If the current bounds lead to a deduction of a bound change (e.g. in domain
102 * propagation), a constraint handler should call SCIPinferVarLbCons() or
103 * SCIPinferVarUbCons(), thus providing the constraint that infered the bound change.
104 * A propagator should call SCIPinferVarLbProp() or SCIPinferVarUbProp() instead,
105 * thus providing a pointer to itself.
106 * - If (in the current bounds) an infeasibility is detected, the constraint handler or
107 * propagator should
108 * 1. call SCIPinitConflictAnalysis() to initialize the conflict queue,
109 * 2. call SCIPaddConflictBound() for each bound that lead to the conflict,
110 * 3. call SCIPanalyzeConflictCons() or SCIPanalyzeConflict() to analyze the conflict
111 * and add an appropriate conflict constraint.
112 */
113
114 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
115
116 #include "lpi/lpi.h"
117 #include "scip/clock.h"
118 #include "scip/conflict.h"
119 #include "scip/conflictstore.h"
120 #include "scip/cons.h"
121 #include "scip/cons_linear.h"
122 #include "scip/cuts.h"
123 #include "scip/history.h"
124 #include "scip/lp.h"
125 #include "scip/presolve.h"
126 #include "scip/prob.h"
127 #include "scip/prop.h"
128 #include "scip/pub_conflict.h"
129 #include "scip/pub_cons.h"
130 #include "scip/pub_lp.h"
131 #include "scip/pub_message.h"
132 #include "scip/pub_misc.h"
133 #include "scip/pub_misc_sort.h"
134 #include "scip/pub_paramset.h"
135 #include "scip/pub_prop.h"
136 #include "scip/pub_tree.h"
137 #include "scip/pub_var.h"
138 #include "scip/scip_conflict.h"
139 #include "scip/scip_cons.h"
140 #include "scip/scip_mem.h"
141 #include "scip/scip_sol.h"
142 #include "scip/scip_var.h"
143 #include "scip/set.h"
144 #include "scip/sol.h"
145 #include "scip/struct_conflict.h"
146 #include "scip/struct_lp.h"
147 #include "scip/struct_prob.h"
148 #include "scip/struct_set.h"
149 #include "scip/struct_stat.h"
150 #include "scip/struct_tree.h"
151 #include "scip/struct_var.h"
152 #include "scip/tree.h"
153 #include "scip/var.h"
154 #include "scip/visual.h"
155 #include <string.h>
156 #if defined(_WIN32) || defined(_WIN64)
157 #else
158 #include <strings.h> /*lint --e{766}*/
159 #endif
160
161
162
163 #define BOUNDSWITCH 0.51 /**< threshold for bound switching - see cuts.c */
164 #define POSTPROCESS FALSE /**< apply postprocessing to the cut - see cuts.c */
165 #define USEVBDS FALSE /**< use variable bounds - see cuts.c */
166 #define ALLOWLOCAL FALSE /**< allow to generate local cuts - see cuts. */
167 #define MINFRAC 0.05 /**< minimal fractionality of floor(rhs) - see cuts.c */
168 #define MAXFRAC 0.999 /**< maximal fractionality of floor(rhs) - see cuts.c */
169
170 /*#define SCIP_CONFGRAPH*/
171
172
173 #ifdef SCIP_CONFGRAPH
174 /*
175 * Output of Conflict Graph
176 */
177
178 #include <stdio.h>
179
180 static FILE* confgraphfile = NULL; /**< output file for current conflict graph */
181 static SCIP_BDCHGINFO* confgraphcurrentbdchginfo = NULL; /**< currently resolved bound change */
182 static int confgraphnconflictsets = 0; /**< number of conflict sets marked in the graph */
183
184 /** writes a node section to the conflict graph file */
185 static
confgraphWriteNode(void * idptr,const char * label,const char * nodetype,const char * fillcolor,const char * bordercolor)186 void confgraphWriteNode(
187 void* idptr, /**< id of the node */
188 const char* label, /**< label of the node */
189 const char* nodetype, /**< type of the node */
190 const char* fillcolor, /**< color of the node's interior */
191 const char* bordercolor /**< color of the node's border */
192 )
193 {
194 assert(confgraphfile != NULL);
195
196 SCIPgmlWriteNode(confgraphfile, (unsigned int)(size_t)idptr, label, nodetype, fillcolor, bordercolor);
197 }
198
199 /** writes an edge section to the conflict graph file */
200 static
confgraphWriteEdge(void * source,void * target,const char * color)201 void confgraphWriteEdge(
202 void* source, /**< source node of the edge */
203 void* target, /**< target node of the edge */
204 const char* color /**< color of the edge */
205 )
206 {
207 assert(confgraphfile != NULL);
208
209 #ifndef SCIP_CONFGRAPH_EDGE
210 SCIPgmlWriteArc(confgraphfile, (unsigned int)(size_t)source, (unsigned int)(size_t)target, NULL, color);
211 #else
212 SCIPgmlWriteEdge(confgraphfile, (unsigned int)(size_t)source, (unsigned int)(size_t)target, NULL, color);
213 #endif
214 }
215
216 /** creates a file to output the current conflict graph into; adds the conflict vertex to the graph */
217 static
confgraphCreate(SCIP_SET * set,SCIP_CONFLICT * conflict)218 SCIP_RETCODE confgraphCreate(
219 SCIP_SET* set, /**< global SCIP settings */
220 SCIP_CONFLICT* conflict /**< conflict analysis data */
221 )
222 {
223 char fname[SCIP_MAXSTRLEN];
224
225 assert(conflict != NULL);
226 assert(confgraphfile == NULL);
227
228 (void) SCIPsnprintf(fname, SCIP_MAXSTRLEN, "conf%p%d.gml", conflict, conflict->count);
229 SCIPinfoMessage(set->scip, NULL, "storing conflict graph in file <%s>\n", fname);
230
231 confgraphfile = fopen(fname, "w");
232
233 if( confgraphfile == NULL )
234 {
235 SCIPerrorMessage("cannot open graph file <%s>\n", fname);
236 SCIPABORT(); /*lint !e527*/
237 return SCIP_WRITEERROR;
238 }
239
240 SCIPgmlWriteOpening(confgraphfile, TRUE);
241
242 confgraphWriteNode(NULL, "conflict", "ellipse", "#ff0000", "#000000");
243
244 confgraphcurrentbdchginfo = NULL;
245
246 return SCIP_OKAY;
247 }
248
249 /** closes conflict graph file */
250 static
confgraphFree(void)251 void confgraphFree(
252 void
253 )
254 {
255 if( confgraphfile != NULL )
256 {
257 SCIPgmlWriteClosing(confgraphfile);
258
259 fclose(confgraphfile);
260
261 confgraphfile = NULL;
262 confgraphnconflictsets = 0;
263 }
264 }
265
266 /** adds a bound change node to the conflict graph and links it to the currently resolved bound change */
267 static
confgraphAddBdchg(SCIP_BDCHGINFO * bdchginfo)268 void confgraphAddBdchg(
269 SCIP_BDCHGINFO* bdchginfo /**< bound change to add to the conflict graph */
270 )
271 {
272 const char* colors[] = {
273 "#8888ff", /* blue for constraint resolving */
274 "#ffff00", /* yellow for propagator resolving */
275 "#55ff55" /* green branching decision */
276 };
277 char label[SCIP_MAXSTRLEN];
278 char depth[SCIP_MAXSTRLEN];
279 int col;
280
281 switch( SCIPbdchginfoGetChgtype(bdchginfo) )
282 {
283 case SCIP_BOUNDCHGTYPE_BRANCHING:
284 col = 2;
285 break;
286 case SCIP_BOUNDCHGTYPE_CONSINFER:
287 col = 0;
288 break;
289 case SCIP_BOUNDCHGTYPE_PROPINFER:
290 col = (SCIPbdchginfoGetInferProp(bdchginfo) == NULL ? 1 : 0);
291 break;
292 default:
293 SCIPerrorMessage("invalid bound change type\n");
294 col = 0;
295 SCIPABORT();
296 break;
297 }
298
299 if( SCIPbdchginfoGetDepth(bdchginfo) == INT_MAX )
300 (void) SCIPsnprintf(depth, SCIP_MAXSTRLEN, "dive");
301 else
302 (void) SCIPsnprintf(depth, SCIP_MAXSTRLEN, "%d", SCIPbdchginfoGetDepth(bdchginfo));
303 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "%s %s %g\n[%s:%d]", SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
304 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
305 SCIPbdchginfoGetNewbound(bdchginfo), depth, SCIPbdchginfoGetPos(bdchginfo));
306 confgraphWriteNode(bdchginfo, label, "ellipse", colors[col], "#000000");
307 confgraphWriteEdge(bdchginfo, confgraphcurrentbdchginfo, "#000000");
308 }
309
310 /** links the already existing bound change node to the currently resolved bound change */
311 static
confgraphLinkBdchg(SCIP_BDCHGINFO * bdchginfo)312 void confgraphLinkBdchg(
313 SCIP_BDCHGINFO* bdchginfo /**< bound change to add to the conflict graph */
314 )
315 {
316 confgraphWriteEdge(bdchginfo, confgraphcurrentbdchginfo, "#000000");
317 }
318
319 /** marks the given bound change to be the currently resolved bound change */
320 static
confgraphSetCurrentBdchg(SCIP_BDCHGINFO * bdchginfo)321 void confgraphSetCurrentBdchg(
322 SCIP_BDCHGINFO* bdchginfo /**< bound change to add to the conflict graph */
323 )
324 {
325 confgraphcurrentbdchginfo = bdchginfo;
326 }
327
328 /** marks given conflict set in the conflict graph */
329 static
confgraphMarkConflictset(SCIP_CONFLICTSET * conflictset)330 void confgraphMarkConflictset(
331 SCIP_CONFLICTSET* conflictset /**< conflict set */
332 )
333 {
334 char label[SCIP_MAXSTRLEN];
335 int i;
336
337 assert(conflictset != NULL);
338
339 confgraphnconflictsets++;
340 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "conf %d (%d)", confgraphnconflictsets, conflictset->validdepth);
341 confgraphWriteNode((void*)(size_t)confgraphnconflictsets, label, "rectangle", "#ff00ff", "#000000");
342 for( i = 0; i < conflictset->nbdchginfos; ++i )
343 confgraphWriteEdge((void*)(size_t)confgraphnconflictsets, conflictset->bdchginfos[i], "#ff00ff");
344 }
345
346 #endif
347
348 /*
349 * Conflict Handler
350 */
351
352 /** compares two conflict handlers w. r. to their priority */
SCIP_DECL_SORTPTRCOMP(SCIPconflicthdlrComp)353 SCIP_DECL_SORTPTRCOMP(SCIPconflicthdlrComp)
354 { /*lint --e{715}*/
355 return ((SCIP_CONFLICTHDLR*)elem2)->priority - ((SCIP_CONFLICTHDLR*)elem1)->priority;
356 }
357
358 /** comparison method for sorting conflict handler w.r.t. to their name */
SCIP_DECL_SORTPTRCOMP(SCIPconflicthdlrCompName)359 SCIP_DECL_SORTPTRCOMP(SCIPconflicthdlrCompName)
360 {
361 return strcmp(SCIPconflicthdlrGetName((SCIP_CONFLICTHDLR*)elem1), SCIPconflicthdlrGetName((SCIP_CONFLICTHDLR*)elem2));
362 }
363
364 /** method to call, when the priority of a conflict handler was changed */
365 static
SCIP_DECL_PARAMCHGD(paramChgdConflicthdlrPriority)366 SCIP_DECL_PARAMCHGD(paramChgdConflicthdlrPriority)
367 { /*lint --e{715}*/
368 SCIP_PARAMDATA* paramdata;
369
370 paramdata = SCIPparamGetData(param);
371 assert(paramdata != NULL);
372
373 /* use SCIPsetConflicthdlrPriority() to mark the conflicthdlrs unsorted */
374 SCIP_CALL( SCIPsetConflicthdlrPriority(scip, (SCIP_CONFLICTHDLR*)paramdata, SCIPparamGetInt(param)) ); /*lint !e740*/
375
376 return SCIP_OKAY;
377 }
378
379 /** copies the given conflict handler to a new scip */
SCIPconflicthdlrCopyInclude(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set)380 SCIP_RETCODE SCIPconflicthdlrCopyInclude(
381 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
382 SCIP_SET* set /**< SCIP_SET of SCIP to copy to */
383 )
384 {
385 assert(conflicthdlr != NULL);
386 assert(set != NULL);
387 assert(set->scip != NULL);
388
389 if( conflicthdlr->conflictcopy != NULL )
390 {
391 SCIPsetDebugMsg(set, "including conflict handler %s in subscip %p\n", SCIPconflicthdlrGetName(conflicthdlr), (void*)set->scip);
392 SCIP_CALL( conflicthdlr->conflictcopy(set->scip, conflicthdlr) );
393 }
394
395 return SCIP_OKAY;
396 }
397
398 /** internal method for creating a conflict handler */
399 static
doConflicthdlrCreate(SCIP_CONFLICTHDLR ** conflicthdlr,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,BMS_BLKMEM * blkmem,const char * name,const char * desc,int priority,SCIP_DECL_CONFLICTCOPY ((* conflictcopy)),SCIP_DECL_CONFLICTFREE ((* conflictfree)),SCIP_DECL_CONFLICTINIT ((* conflictinit)),SCIP_DECL_CONFLICTEXIT ((* conflictexit)),SCIP_DECL_CONFLICTINITSOL ((* conflictinitsol)),SCIP_DECL_CONFLICTEXITSOL ((* conflictexitsol)),SCIP_DECL_CONFLICTEXEC ((* conflictexec)),SCIP_CONFLICTHDLRDATA * conflicthdlrdata)400 SCIP_RETCODE doConflicthdlrCreate(
401 SCIP_CONFLICTHDLR** conflicthdlr, /**< pointer to conflict handler data structure */
402 SCIP_SET* set, /**< global SCIP settings */
403 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
404 BMS_BLKMEM* blkmem, /**< block memory for parameter settings */
405 const char* name, /**< name of conflict handler */
406 const char* desc, /**< description of conflict handler */
407 int priority, /**< priority of the conflict handler */
408 SCIP_DECL_CONFLICTCOPY((*conflictcopy)), /**< copy method of conflict handler or NULL if you don't want to copy your plugin into sub-SCIPs */
409 SCIP_DECL_CONFLICTFREE((*conflictfree)), /**< destructor of conflict handler */
410 SCIP_DECL_CONFLICTINIT((*conflictinit)), /**< initialize conflict handler */
411 SCIP_DECL_CONFLICTEXIT((*conflictexit)), /**< deinitialize conflict handler */
412 SCIP_DECL_CONFLICTINITSOL((*conflictinitsol)),/**< solving process initialization method of conflict handler */
413 SCIP_DECL_CONFLICTEXITSOL((*conflictexitsol)),/**< solving process deinitialization method of conflict handler */
414 SCIP_DECL_CONFLICTEXEC((*conflictexec)), /**< conflict processing method of conflict handler */
415 SCIP_CONFLICTHDLRDATA* conflicthdlrdata /**< conflict handler data */
416 )
417 {
418 char paramname[SCIP_MAXSTRLEN];
419 char paramdesc[SCIP_MAXSTRLEN];
420
421 assert(conflicthdlr != NULL);
422 assert(name != NULL);
423 assert(desc != NULL);
424
425 SCIP_ALLOC( BMSallocMemory(conflicthdlr) );
426 BMSclearMemory(*conflicthdlr);
427
428 SCIP_ALLOC( BMSduplicateMemoryArray(&(*conflicthdlr)->name, name, strlen(name)+1) );
429 SCIP_ALLOC( BMSduplicateMemoryArray(&(*conflicthdlr)->desc, desc, strlen(desc)+1) );
430 (*conflicthdlr)->priority = priority;
431 (*conflicthdlr)->conflictcopy = conflictcopy;
432 (*conflicthdlr)->conflictfree = conflictfree;
433 (*conflicthdlr)->conflictinit = conflictinit;
434 (*conflicthdlr)->conflictexit = conflictexit;
435 (*conflicthdlr)->conflictinitsol = conflictinitsol;
436 (*conflicthdlr)->conflictexitsol = conflictexitsol;
437 (*conflicthdlr)->conflictexec = conflictexec;
438 (*conflicthdlr)->conflicthdlrdata = conflicthdlrdata;
439 (*conflicthdlr)->initialized = FALSE;
440
441 SCIP_CALL( SCIPclockCreate(&(*conflicthdlr)->setuptime, SCIP_CLOCKTYPE_DEFAULT) );
442 SCIP_CALL( SCIPclockCreate(&(*conflicthdlr)->conflicttime, SCIP_CLOCKTYPE_DEFAULT) );
443
444 /* add parameters */
445 (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "conflict/%s/priority", name);
446 (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "priority of conflict handler <%s>", name);
447 SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname, paramdesc, &(*conflicthdlr)->priority, TRUE, \
448 priority, INT_MIN, INT_MAX, paramChgdConflicthdlrPriority, (SCIP_PARAMDATA*)(*conflicthdlr)) ); /*lint !e740*/
449
450 return SCIP_OKAY;
451 }
452
453 /** creates a conflict handler */
SCIPconflicthdlrCreate(SCIP_CONFLICTHDLR ** conflicthdlr,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,BMS_BLKMEM * blkmem,const char * name,const char * desc,int priority,SCIP_DECL_CONFLICTCOPY ((* conflictcopy)),SCIP_DECL_CONFLICTFREE ((* conflictfree)),SCIP_DECL_CONFLICTINIT ((* conflictinit)),SCIP_DECL_CONFLICTEXIT ((* conflictexit)),SCIP_DECL_CONFLICTINITSOL ((* conflictinitsol)),SCIP_DECL_CONFLICTEXITSOL ((* conflictexitsol)),SCIP_DECL_CONFLICTEXEC ((* conflictexec)),SCIP_CONFLICTHDLRDATA * conflicthdlrdata)454 SCIP_RETCODE SCIPconflicthdlrCreate(
455 SCIP_CONFLICTHDLR** conflicthdlr, /**< pointer to conflict handler data structure */
456 SCIP_SET* set, /**< global SCIP settings */
457 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
458 BMS_BLKMEM* blkmem, /**< block memory for parameter settings */
459 const char* name, /**< name of conflict handler */
460 const char* desc, /**< description of conflict handler */
461 int priority, /**< priority of the conflict handler */
462 SCIP_DECL_CONFLICTCOPY((*conflictcopy)), /**< copy method of conflict handler or NULL if you don't want to
463 * copy your plugin into sub-SCIPs */
464 SCIP_DECL_CONFLICTFREE((*conflictfree)), /**< destructor of conflict handler */
465 SCIP_DECL_CONFLICTINIT((*conflictinit)), /**< initialize conflict handler */
466 SCIP_DECL_CONFLICTEXIT((*conflictexit)), /**< deinitialize conflict handler */
467 SCIP_DECL_CONFLICTINITSOL((*conflictinitsol)),/**< solving process initialization method of conflict handler */
468 SCIP_DECL_CONFLICTEXITSOL((*conflictexitsol)),/**< solving process deinitialization method of conflict handler */
469 SCIP_DECL_CONFLICTEXEC((*conflictexec)), /**< conflict processing method of conflict handler */
470 SCIP_CONFLICTHDLRDATA* conflicthdlrdata /**< conflict handler data */
471 )
472 {
473 assert(conflicthdlr != NULL);
474 assert(name != NULL);
475 assert(desc != NULL);
476
477 SCIP_CALL_FINALLY( doConflicthdlrCreate(conflicthdlr, set, messagehdlr, blkmem, name, desc, priority,
478 conflictcopy, conflictfree, conflictinit, conflictexit, conflictinitsol, conflictexitsol, conflictexec,
479 conflicthdlrdata), (void) SCIPconflicthdlrFree(conflicthdlr, set) );
480
481 return SCIP_OKAY;
482 }
483
484 /** calls destructor and frees memory of conflict handler */
SCIPconflicthdlrFree(SCIP_CONFLICTHDLR ** conflicthdlr,SCIP_SET * set)485 SCIP_RETCODE SCIPconflicthdlrFree(
486 SCIP_CONFLICTHDLR** conflicthdlr, /**< pointer to conflict handler data structure */
487 SCIP_SET* set /**< global SCIP settings */
488 )
489 {
490 assert(conflicthdlr != NULL);
491 if( *conflicthdlr == NULL )
492 return SCIP_OKAY;
493 assert(!(*conflicthdlr)->initialized);
494 assert(set != NULL);
495
496 /* call destructor of conflict handler */
497 if( (*conflicthdlr)->conflictfree != NULL )
498 {
499 SCIP_CALL( (*conflicthdlr)->conflictfree(set->scip, *conflicthdlr) );
500 }
501
502 SCIPclockFree(&(*conflicthdlr)->conflicttime);
503 SCIPclockFree(&(*conflicthdlr)->setuptime);
504
505 BMSfreeMemoryArrayNull(&(*conflicthdlr)->name);
506 BMSfreeMemoryArrayNull(&(*conflicthdlr)->desc);
507 BMSfreeMemory(conflicthdlr);
508
509 return SCIP_OKAY;
510 }
511
512 /** calls initialization method of conflict handler */
SCIPconflicthdlrInit(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set)513 SCIP_RETCODE SCIPconflicthdlrInit(
514 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
515 SCIP_SET* set /**< global SCIP settings */
516 )
517 {
518 assert(conflicthdlr != NULL);
519 assert(set != NULL);
520
521 if( conflicthdlr->initialized )
522 {
523 SCIPerrorMessage("conflict handler <%s> already initialized\n", conflicthdlr->name);
524 return SCIP_INVALIDCALL;
525 }
526
527 if( set->misc_resetstat )
528 {
529 SCIPclockReset(conflicthdlr->setuptime);
530 SCIPclockReset(conflicthdlr->conflicttime);
531 }
532
533 /* call initialization method of conflict handler */
534 if( conflicthdlr->conflictinit != NULL )
535 {
536 /* start timing */
537 SCIPclockStart(conflicthdlr->setuptime, set);
538
539 SCIP_CALL( conflicthdlr->conflictinit(set->scip, conflicthdlr) );
540
541 /* stop timing */
542 SCIPclockStop(conflicthdlr->setuptime, set);
543 }
544 conflicthdlr->initialized = TRUE;
545
546 return SCIP_OKAY;
547 }
548
549 /** calls exit method of conflict handler */
SCIPconflicthdlrExit(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set)550 SCIP_RETCODE SCIPconflicthdlrExit(
551 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
552 SCIP_SET* set /**< global SCIP settings */
553 )
554 {
555 assert(conflicthdlr != NULL);
556 assert(set != NULL);
557
558 if( !conflicthdlr->initialized )
559 {
560 SCIPerrorMessage("conflict handler <%s> not initialized\n", conflicthdlr->name);
561 return SCIP_INVALIDCALL;
562 }
563
564 /* call deinitialization method of conflict handler */
565 if( conflicthdlr->conflictexit != NULL )
566 {
567 /* start timing */
568 SCIPclockStart(conflicthdlr->setuptime, set);
569
570 SCIP_CALL( conflicthdlr->conflictexit(set->scip, conflicthdlr) );
571
572 /* stop timing */
573 SCIPclockStop(conflicthdlr->setuptime, set);
574 }
575 conflicthdlr->initialized = FALSE;
576
577 return SCIP_OKAY;
578 }
579
580 /** informs conflict handler that the branch and bound process is being started */
SCIPconflicthdlrInitsol(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set)581 SCIP_RETCODE SCIPconflicthdlrInitsol(
582 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
583 SCIP_SET* set /**< global SCIP settings */
584 )
585 {
586 assert(conflicthdlr != NULL);
587 assert(set != NULL);
588
589 /* call solving process initialization method of conflict handler */
590 if( conflicthdlr->conflictinitsol != NULL )
591 {
592 /* start timing */
593 SCIPclockStart(conflicthdlr->setuptime, set);
594
595 SCIP_CALL( conflicthdlr->conflictinitsol(set->scip, conflicthdlr) );
596
597 /* stop timing */
598 SCIPclockStop(conflicthdlr->setuptime, set);
599 }
600
601 return SCIP_OKAY;
602 }
603
604 /** informs conflict handler that the branch and bound process data is being freed */
SCIPconflicthdlrExitsol(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set)605 SCIP_RETCODE SCIPconflicthdlrExitsol(
606 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
607 SCIP_SET* set /**< global SCIP settings */
608 )
609 {
610 assert(conflicthdlr != NULL);
611 assert(set != NULL);
612
613 /* call solving process deinitialization method of conflict handler */
614 if( conflicthdlr->conflictexitsol != NULL )
615 {
616 /* start timing */
617 SCIPclockStart(conflicthdlr->setuptime, set);
618
619 SCIP_CALL( conflicthdlr->conflictexitsol(set->scip, conflicthdlr) );
620
621 /* stop timing */
622 SCIPclockStop(conflicthdlr->setuptime, set);
623 }
624
625 return SCIP_OKAY;
626 }
627
628 /** calls execution method of conflict handler */
SCIPconflicthdlrExec(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set,SCIP_NODE * node,SCIP_NODE * validnode,SCIP_BDCHGINFO ** bdchginfos,SCIP_Real * relaxedbds,int nbdchginfos,SCIP_CONFTYPE conftype,SCIP_Bool usescutoffbound,SCIP_Bool resolved,SCIP_RESULT * result)629 SCIP_RETCODE SCIPconflicthdlrExec(
630 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
631 SCIP_SET* set, /**< global SCIP settings */
632 SCIP_NODE* node, /**< node to add conflict constraint to */
633 SCIP_NODE* validnode, /**< node at which the constraint is valid */
634 SCIP_BDCHGINFO** bdchginfos, /**< bound change resembling the conflict set */
635 SCIP_Real* relaxedbds, /**< array with relaxed bounds which are efficient to create a valid conflict */
636 int nbdchginfos, /**< number of bound changes in the conflict set */
637 SCIP_CONFTYPE conftype, /**< type of the conflict */
638 SCIP_Bool usescutoffbound, /**< depends the conflict on the cutoff bound? */
639 SCIP_Bool resolved, /**< was the conflict set already used to create a constraint? */
640 SCIP_RESULT* result /**< pointer to store the result of the callback method */
641 )
642 {
643 assert(conflicthdlr != NULL);
644 assert(set != NULL);
645 assert(bdchginfos != NULL || nbdchginfos == 0);
646 assert(result != NULL);
647
648 /* call solution start method of conflict handler */
649 *result = SCIP_DIDNOTRUN;
650 if( conflicthdlr->conflictexec != NULL )
651 {
652 /* start timing */
653 SCIPclockStart(conflicthdlr->conflicttime, set);
654
655 SCIP_CALL( conflicthdlr->conflictexec(set->scip, conflicthdlr, node, validnode, bdchginfos, relaxedbds, nbdchginfos,
656 conftype, usescutoffbound, set->conf_separate, (SCIPnodeGetDepth(validnode) > 0), set->conf_dynamic,
657 set->conf_removable, resolved, result) );
658
659 /* stop timing */
660 SCIPclockStop(conflicthdlr->conflicttime, set);
661
662 if( *result != SCIP_CONSADDED
663 && *result != SCIP_DIDNOTFIND
664 && *result != SCIP_DIDNOTRUN )
665 {
666 SCIPerrorMessage("execution method of conflict handler <%s> returned invalid result <%d>\n",
667 conflicthdlr->name, *result);
668 return SCIP_INVALIDRESULT;
669 }
670 }
671
672 return SCIP_OKAY;
673 }
674
675 /** gets user data of conflict handler */
SCIPconflicthdlrGetData(SCIP_CONFLICTHDLR * conflicthdlr)676 SCIP_CONFLICTHDLRDATA* SCIPconflicthdlrGetData(
677 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
678 )
679 {
680 assert(conflicthdlr != NULL);
681
682 return conflicthdlr->conflicthdlrdata;
683 }
684
685 /** sets user data of conflict handler; user has to free old data in advance! */
SCIPconflicthdlrSetData(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_CONFLICTHDLRDATA * conflicthdlrdata)686 void SCIPconflicthdlrSetData(
687 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
688 SCIP_CONFLICTHDLRDATA* conflicthdlrdata /**< new conflict handler user data */
689 )
690 {
691 assert(conflicthdlr != NULL);
692
693 conflicthdlr->conflicthdlrdata = conflicthdlrdata;
694 }
695
696 /** set copy method of conflict handler */
SCIPconflicthdlrSetCopy(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTCOPY ((* conflictcopy)))697 void SCIPconflicthdlrSetCopy(
698 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
699 SCIP_DECL_CONFLICTCOPY((*conflictcopy)) /**< copy method of the conflict handler */
700 )
701 {
702 assert(conflicthdlr != NULL);
703
704 conflicthdlr->conflictcopy = conflictcopy;
705 }
706
707 /** set destructor of conflict handler */
SCIPconflicthdlrSetFree(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTFREE ((* conflictfree)))708 void SCIPconflicthdlrSetFree(
709 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
710 SCIP_DECL_CONFLICTFREE((*conflictfree)) /**< destructor of conflict handler */
711 )
712 {
713 assert(conflicthdlr != NULL);
714
715 conflicthdlr->conflictfree = conflictfree;
716 }
717
718 /** set initialization method of conflict handler */
SCIPconflicthdlrSetInit(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTINIT ((* conflictinit)))719 void SCIPconflicthdlrSetInit(
720 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
721 SCIP_DECL_CONFLICTINIT((*conflictinit)) /**< initialization method conflict handler */
722 )
723 {
724 assert(conflicthdlr != NULL);
725
726 conflicthdlr->conflictinit = conflictinit;
727 }
728
729 /** set deinitialization method of conflict handler */
SCIPconflicthdlrSetExit(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTEXIT ((* conflictexit)))730 void SCIPconflicthdlrSetExit(
731 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
732 SCIP_DECL_CONFLICTEXIT((*conflictexit)) /**< deinitialization method conflict handler */
733 )
734 {
735 assert(conflicthdlr != NULL);
736
737 conflicthdlr->conflictexit = conflictexit;
738 }
739
740 /** set solving process initialization method of conflict handler */
SCIPconflicthdlrSetInitsol(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTINITSOL ((* conflictinitsol)))741 void SCIPconflicthdlrSetInitsol(
742 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
743 SCIP_DECL_CONFLICTINITSOL((*conflictinitsol))/**< solving process initialization method of conflict handler */
744 )
745 {
746 assert(conflicthdlr != NULL);
747
748 conflicthdlr->conflictinitsol = conflictinitsol;
749 }
750
751 /** set solving process deinitialization method of conflict handler */
SCIPconflicthdlrSetExitsol(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_DECL_CONFLICTEXITSOL ((* conflictexitsol)))752 void SCIPconflicthdlrSetExitsol(
753 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
754 SCIP_DECL_CONFLICTEXITSOL((*conflictexitsol))/**< solving process deinitialization method of conflict handler */
755 )
756 {
757 assert(conflicthdlr != NULL);
758
759 conflicthdlr->conflictexitsol = conflictexitsol;
760 }
761
762 /** gets name of conflict handler */
SCIPconflicthdlrGetName(SCIP_CONFLICTHDLR * conflicthdlr)763 const char* SCIPconflicthdlrGetName(
764 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
765 )
766 {
767 assert(conflicthdlr != NULL);
768
769 return conflicthdlr->name;
770 }
771
772 /** gets description of conflict handler */
SCIPconflicthdlrGetDesc(SCIP_CONFLICTHDLR * conflicthdlr)773 const char* SCIPconflicthdlrGetDesc(
774 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
775 )
776 {
777 assert(conflicthdlr != NULL);
778
779 return conflicthdlr->desc;
780 }
781
782 /** gets priority of conflict handler */
SCIPconflicthdlrGetPriority(SCIP_CONFLICTHDLR * conflicthdlr)783 int SCIPconflicthdlrGetPriority(
784 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
785 )
786 {
787 assert(conflicthdlr != NULL);
788
789 return conflicthdlr->priority;
790 }
791
792 /** sets priority of conflict handler */
SCIPconflicthdlrSetPriority(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_SET * set,int priority)793 void SCIPconflicthdlrSetPriority(
794 SCIP_CONFLICTHDLR* conflicthdlr, /**< conflict handler */
795 SCIP_SET* set, /**< global SCIP settings */
796 int priority /**< new priority of the conflict handler */
797 )
798 {
799 assert(conflicthdlr != NULL);
800 assert(set != NULL);
801
802 conflicthdlr->priority = priority;
803 set->conflicthdlrssorted = FALSE;
804 }
805
806 /** is conflict handler initialized? */
SCIPconflicthdlrIsInitialized(SCIP_CONFLICTHDLR * conflicthdlr)807 SCIP_Bool SCIPconflicthdlrIsInitialized(
808 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
809 )
810 {
811 assert(conflicthdlr != NULL);
812
813 return conflicthdlr->initialized;
814 }
815
816 /** enables or disables all clocks of \p conflicthdlr, depending on the value of the flag */
SCIPconflicthdlrEnableOrDisableClocks(SCIP_CONFLICTHDLR * conflicthdlr,SCIP_Bool enable)817 void SCIPconflicthdlrEnableOrDisableClocks(
818 SCIP_CONFLICTHDLR* conflicthdlr, /**< the conflict handler for which all clocks should be enabled or disabled */
819 SCIP_Bool enable /**< should the clocks of the conflict handler be enabled? */
820 )
821 {
822 assert(conflicthdlr != NULL);
823
824 SCIPclockEnableOrDisable(conflicthdlr->setuptime, enable);
825 SCIPclockEnableOrDisable(conflicthdlr->conflicttime, enable);
826 }
827
828 /** gets time in seconds used in this conflict handler for setting up for next stages */
SCIPconflicthdlrGetSetupTime(SCIP_CONFLICTHDLR * conflicthdlr)829 SCIP_Real SCIPconflicthdlrGetSetupTime(
830 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
831 )
832 {
833 assert(conflicthdlr != NULL);
834
835 return SCIPclockGetTime(conflicthdlr->setuptime);
836 }
837
838 /** gets time in seconds used in this conflict handler */
SCIPconflicthdlrGetTime(SCIP_CONFLICTHDLR * conflicthdlr)839 SCIP_Real SCIPconflicthdlrGetTime(
840 SCIP_CONFLICTHDLR* conflicthdlr /**< conflict handler */
841 )
842 {
843 assert(conflicthdlr != NULL);
844
845 return SCIPclockGetTime(conflicthdlr->conflicttime);
846 }
847
848 /*
849 * Conflict LP Bound Changes
850 */
851
852
853 /** create conflict LP bound change data structure */
854 static
lpbdchgsCreate(SCIP_LPBDCHGS ** lpbdchgs,SCIP_SET * set,int ncols)855 SCIP_RETCODE lpbdchgsCreate(
856 SCIP_LPBDCHGS** lpbdchgs, /**< pointer to store the conflict LP bound change data structure */
857 SCIP_SET* set, /**< global SCIP settings */
858 int ncols /**< number of columns */
859 )
860 {
861 SCIP_CALL( SCIPsetAllocBuffer(set, lpbdchgs) );
862
863 SCIP_CALL( SCIPsetAllocBufferArray(set, &(*lpbdchgs)->bdchginds, ncols) );
864 SCIP_CALL( SCIPsetAllocBufferArray(set, &(*lpbdchgs)->bdchglbs, ncols) );
865 SCIP_CALL( SCIPsetAllocBufferArray(set, &(*lpbdchgs)->bdchgubs, ncols) );
866 SCIP_CALL( SCIPsetAllocBufferArray(set, &(*lpbdchgs)->bdchgcolinds, ncols) );
867 SCIP_CALL( SCIPsetAllocBufferArray(set, &(*lpbdchgs)->usedcols, ncols) );
868 BMSclearMemoryArray((*lpbdchgs)->usedcols, ncols);
869
870 (*lpbdchgs)->nbdchgs = 0;
871
872 return SCIP_OKAY;
873 }
874
875 /** reset conflict LP bound change data structure */
876 static
lpbdchgsReset(SCIP_LPBDCHGS * lpbdchgs,int ncols)877 void lpbdchgsReset(
878 SCIP_LPBDCHGS* lpbdchgs, /**< conflict LP bound change data structure */
879 int ncols /**< number of columns */
880 )
881 {
882 assert(lpbdchgs != NULL);
883
884 BMSclearMemoryArray(lpbdchgs->usedcols, ncols);
885 lpbdchgs->nbdchgs = 0;
886 }
887
888 /** free conflict LP bound change data structure */
889 static
lpbdchgsFree(SCIP_LPBDCHGS ** lpbdchgs,SCIP_SET * set)890 void lpbdchgsFree(
891 SCIP_LPBDCHGS** lpbdchgs, /**< pointer to store the conflict LP bound change data structure */
892 SCIP_SET* set /**< global SCIP settings */
893 )
894 {
895 SCIPsetFreeBufferArray(set, &(*lpbdchgs)->usedcols);
896 SCIPsetFreeBufferArray(set, &(*lpbdchgs)->bdchgcolinds);
897 SCIPsetFreeBufferArray(set, &(*lpbdchgs)->bdchgubs);
898 SCIPsetFreeBufferArray(set, &(*lpbdchgs)->bdchglbs);
899 SCIPsetFreeBufferArray(set, &(*lpbdchgs)->bdchginds);
900
901 SCIPsetFreeBuffer(set, lpbdchgs);
902 }
903
904 /*
905 * Proof Sets
906 */
907
908 /** return the char associated with the type of the variable */
909 static
varGetChar(SCIP_VAR * var)910 char varGetChar(
911 SCIP_VAR* var /**< variable */
912 )
913 {
914 SCIP_VARTYPE vartype = SCIPvarGetType(var);
915
916 return (!SCIPvarIsIntegral(var) ? 'C' :
917 (vartype == SCIP_VARTYPE_BINARY ? 'B' :
918 (vartype == SCIP_VARTYPE_INTEGER ? 'I' : 'M')));
919 }
920
921 /** resets the data structure of a proofset */
922 static
proofsetClear(SCIP_PROOFSET * proofset)923 void proofsetClear(
924 SCIP_PROOFSET* proofset /**< proof set */
925 )
926 {
927 assert(proofset != NULL);
928
929 proofset->nnz = 0;
930 proofset->rhs = 0.0;
931 proofset->validdepth = 0;
932 proofset->conflicttype = SCIP_CONFTYPE_UNKNOWN;
933 }
934
935 /** creates a proofset */
936 static
proofsetCreate(SCIP_PROOFSET ** proofset,BMS_BLKMEM * blkmem)937 SCIP_RETCODE proofsetCreate(
938 SCIP_PROOFSET** proofset, /**< proof set */
939 BMS_BLKMEM* blkmem /**< block memory of transformed problem */
940 )
941 {
942 assert(proofset != NULL);
943
944 SCIP_ALLOC( BMSallocBlockMemory(blkmem, proofset) );
945 (*proofset)->vals = NULL;
946 (*proofset)->inds = NULL;
947 (*proofset)->rhs = 0.0;
948 (*proofset)->nnz = 0;
949 (*proofset)->size = 0;
950 (*proofset)->validdepth = 0;
951 (*proofset)->conflicttype = SCIP_CONFTYPE_UNKNOWN;
952
953 return SCIP_OKAY;
954 }
955
956 /** creates and clears the proofset */
957 static
conflictInitProofset(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem)958 SCIP_RETCODE conflictInitProofset(
959 SCIP_CONFLICT* conflict, /**< conflict analysis data */
960 BMS_BLKMEM* blkmem /**< block memory of transformed problem */
961 )
962 {
963 assert(conflict != NULL);
964 assert(blkmem != NULL);
965
966 SCIP_CALL( proofsetCreate(&conflict->proofset, blkmem) );
967
968 return SCIP_OKAY;
969 }
970
971 /** frees a proofset */
972 static
proofsetFree(SCIP_PROOFSET ** proofset,BMS_BLKMEM * blkmem)973 void proofsetFree(
974 SCIP_PROOFSET** proofset, /**< proof set */
975 BMS_BLKMEM* blkmem /**< block memory */
976 )
977 {
978 assert(proofset != NULL);
979 assert(*proofset != NULL);
980 assert(blkmem != NULL);
981
982 BMSfreeBlockMemoryArrayNull(blkmem, &(*proofset)->vals, (*proofset)->size);
983 BMSfreeBlockMemoryArrayNull(blkmem, &(*proofset)->inds, (*proofset)->size);
984 BMSfreeBlockMemory(blkmem, proofset);
985 (*proofset) = NULL;
986 }
987
988 #ifdef SCIP_DEBUG
989 static
proofsetPrint(SCIP_PROOFSET * proofset,SCIP_SET * set,SCIP_PROB * transprob)990 void proofsetPrint(
991 SCIP_PROOFSET* proofset,
992 SCIP_SET* set,
993 SCIP_PROB* transprob
994 )
995 {
996 SCIP_VAR** vars;
997 int i;
998
999 assert(proofset != NULL);
1000
1001 vars = SCIPprobGetVars(transprob);
1002 assert(vars != NULL);
1003
1004 printf("proofset: ");
1005 for( i = 0; i < proofset->nnz; i++ )
1006 printf("%+.15g <%s> ", proofset->vals[i], SCIPvarGetName(vars[proofset->inds[i]]));
1007 printf(" <= %.15g\n", proofset->rhs);
1008 }
1009 #endif
1010
1011 /** return the indices of variables in the proofset */
1012 static
proofsetGetInds(SCIP_PROOFSET * proofset)1013 int* proofsetGetInds(
1014 SCIP_PROOFSET* proofset /**< proof set */
1015 )
1016 {
1017 assert(proofset != NULL);
1018
1019 return proofset->inds;
1020 }
1021
1022 /** return coefficient of variable in the proofset with given probindex */
1023 static
proofsetGetVals(SCIP_PROOFSET * proofset)1024 SCIP_Real* proofsetGetVals(
1025 SCIP_PROOFSET* proofset /**< proof set */
1026 )
1027 {
1028 assert(proofset != NULL);
1029
1030 return proofset->vals;
1031 }
1032
1033 /** return the right-hand side if a proofset */
1034 static
proofsetGetRhs(SCIP_PROOFSET * proofset)1035 SCIP_Real proofsetGetRhs(
1036 SCIP_PROOFSET* proofset /**< proof set */
1037 )
1038 {
1039 assert(proofset != NULL);
1040
1041 return proofset->rhs;
1042 }
1043
1044 /** returns the number of variables in the proofset */
1045 static
proofsetGetNVars(SCIP_PROOFSET * proofset)1046 int proofsetGetNVars(
1047 SCIP_PROOFSET* proofset /**< proof set */
1048 )
1049 {
1050 assert(proofset != NULL);
1051
1052 return proofset->nnz;
1053 }
1054
1055 /** returns the number of variables in the proofset */
1056 static
proofsetGetConftype(SCIP_PROOFSET * proofset)1057 SCIP_CONFTYPE proofsetGetConftype(
1058 SCIP_PROOFSET* proofset /**< proof set */
1059 )
1060 {
1061 assert(proofset != NULL);
1062
1063 return proofset->conflicttype;
1064 }
1065
1066 /** adds given data as aggregation row to the proofset */
1067 static
proofsetAddSparseData(SCIP_PROOFSET * proofset,BMS_BLKMEM * blkmem,SCIP_Real * vals,int * inds,int nnz,SCIP_Real rhs)1068 SCIP_RETCODE proofsetAddSparseData(
1069 SCIP_PROOFSET* proofset, /**< proof set */
1070 BMS_BLKMEM* blkmem, /**< block memory */
1071 SCIP_Real* vals, /**< variable coefficients */
1072 int* inds, /**< variable array */
1073 int nnz, /**< size of variable and coefficient array */
1074 SCIP_Real rhs /**< right-hand side of the aggregation row */
1075 )
1076 {
1077 assert(proofset != NULL);
1078 assert(blkmem != NULL);
1079
1080 if( proofset->size == 0 )
1081 {
1082 assert(proofset->vals == NULL);
1083 assert(proofset->inds == NULL);
1084
1085 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &proofset->vals, vals, nnz) );
1086 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &proofset->inds, inds, nnz) );
1087
1088 proofset->size = nnz;
1089 }
1090 else
1091 {
1092 int i;
1093
1094 assert(proofset->vals != NULL);
1095 assert(proofset->inds != NULL);
1096
1097 if( proofset->size < nnz )
1098 {
1099 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &proofset->vals, proofset->size, nnz) );
1100 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &proofset->inds, proofset->size, nnz) );
1101 proofset->size = nnz;
1102 }
1103
1104 for( i = 0; i < nnz; i++ )
1105 {
1106 proofset->vals[i] = vals[i];
1107 proofset->inds[i] = inds[i];
1108 }
1109 }
1110
1111 proofset->rhs = rhs;
1112 proofset->nnz = nnz;
1113
1114 return SCIP_OKAY;
1115 }
1116
1117 /** adds an aggregation row to the proofset */
1118 static
proofsetAddAggrrow(SCIP_PROOFSET * proofset,SCIP_SET * set,BMS_BLKMEM * blkmem,SCIP_AGGRROW * aggrrow)1119 SCIP_RETCODE proofsetAddAggrrow(
1120 SCIP_PROOFSET* proofset, /**< proof set */
1121 SCIP_SET* set, /**< global SCIP settings */
1122 BMS_BLKMEM* blkmem, /**< block memory */
1123 SCIP_AGGRROW* aggrrow /**< aggregation row to add */
1124 )
1125 {
1126 SCIP_Real* vals;
1127 int* inds;
1128 int nnz;
1129 int i;
1130
1131 assert(proofset != NULL);
1132 assert(set != NULL);
1133
1134 inds = SCIPaggrRowGetInds(aggrrow);
1135 assert(inds != NULL);
1136
1137 nnz = SCIPaggrRowGetNNz(aggrrow);
1138 assert(nnz > 0);
1139
1140 SCIP_CALL( SCIPsetAllocBufferArray(set, &vals, nnz) );
1141
1142 for( i = 0; i < nnz; i++ )
1143 {
1144 vals[i] = SCIPaggrRowGetProbvarValue(aggrrow, inds[i]);
1145 }
1146
1147 SCIP_CALL( proofsetAddSparseData(proofset, blkmem, vals, inds, nnz, SCIPaggrRowGetRhs(aggrrow)) );
1148
1149 SCIPsetFreeBufferArray(set, &vals);
1150
1151 return SCIP_OKAY;
1152 }
1153
1154 /** Removes a given variable @p var from position @p pos from the proofset and updates the right-hand side according
1155 * to sign of the coefficient, i.e., rhs -= coef * bound, where bound = lb if coef >= 0 and bound = ub, otherwise.
1156 *
1157 * @note: The list of non-zero indices and coefficients will be updated by swapping the last non-zero index to @p pos.
1158 */
1159 static
proofsetCancelVarWithBound(SCIP_PROOFSET * proofset,SCIP_SET * set,SCIP_VAR * var,int pos,SCIP_Bool * valid)1160 void proofsetCancelVarWithBound(
1161 SCIP_PROOFSET* proofset,
1162 SCIP_SET* set,
1163 SCIP_VAR* var,
1164 int pos,
1165 SCIP_Bool* valid
1166 )
1167 {
1168 assert(proofset != NULL);
1169 assert(var != NULL);
1170 assert(pos >= 0 && pos < proofset->nnz);
1171 assert(valid != NULL);
1172
1173 *valid = TRUE;
1174
1175 /* cancel with lower bound */
1176 if( proofset->vals[pos] > 0.0 )
1177 {
1178 proofset->rhs -= proofset->vals[pos] * SCIPvarGetLbGlobal(var);
1179 }
1180 /* cancel with upper bound */
1181 else
1182 {
1183 assert(proofset->vals[pos] < 0.0);
1184 proofset->rhs -= proofset->vals[pos] * SCIPvarGetUbGlobal(var);
1185 }
1186
1187 --proofset->nnz;
1188
1189 proofset->vals[pos] = proofset->vals[proofset->nnz];
1190 proofset->inds[pos] = proofset->inds[proofset->nnz];
1191 proofset->vals[proofset->nnz] = 0.0;
1192 proofset->inds[proofset->nnz] = 0;
1193
1194 if( SCIPsetIsInfinity(set, proofset->rhs) )
1195 *valid = FALSE;
1196 }
1197
1198 /*
1199 * Conflict Sets
1200 */
1201
1202 /** resizes the array of the temporary bound change informations to be able to store at least num bound change entries */
1203 static
conflictEnsureTmpbdchginfosMem(SCIP_CONFLICT * conflict,SCIP_SET * set,int num)1204 SCIP_RETCODE conflictEnsureTmpbdchginfosMem(
1205 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1206 SCIP_SET* set, /**< global SCIP settings */
1207 int num /**< minimal number of slots in arrays */
1208 )
1209 {
1210 assert(conflict != NULL);
1211 assert(set != NULL);
1212
1213 if( num > conflict->tmpbdchginfossize )
1214 {
1215 int newsize;
1216
1217 newsize = SCIPsetCalcMemGrowSize(set, num);
1218 SCIP_ALLOC( BMSreallocMemoryArray(&conflict->tmpbdchginfos, newsize) );
1219 conflict->tmpbdchginfossize = newsize;
1220 }
1221 assert(num <= conflict->tmpbdchginfossize);
1222
1223 return SCIP_OKAY;
1224 }
1225
1226 /** creates a temporary bound change information object that is destroyed after the conflict sets are flushed */
1227 static
conflictCreateTmpBdchginfo(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_Real oldbound,SCIP_Real newbound,SCIP_BDCHGINFO ** bdchginfo)1228 SCIP_RETCODE conflictCreateTmpBdchginfo(
1229 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1230 BMS_BLKMEM* blkmem, /**< block memory */
1231 SCIP_SET* set, /**< global SCIP settings */
1232 SCIP_VAR* var, /**< active variable that changed the bounds */
1233 SCIP_BOUNDTYPE boundtype, /**< type of bound for var: lower or upper bound */
1234 SCIP_Real oldbound, /**< old value for bound */
1235 SCIP_Real newbound, /**< new value for bound */
1236 SCIP_BDCHGINFO** bdchginfo /**< pointer to store bound change information */
1237 )
1238 {
1239 assert(conflict != NULL);
1240
1241 SCIP_CALL( conflictEnsureTmpbdchginfosMem(conflict, set, conflict->ntmpbdchginfos+1) );
1242 SCIP_CALL( SCIPbdchginfoCreate(&conflict->tmpbdchginfos[conflict->ntmpbdchginfos], blkmem,
1243 var, boundtype, oldbound, newbound) );
1244 *bdchginfo = conflict->tmpbdchginfos[conflict->ntmpbdchginfos];
1245 conflict->ntmpbdchginfos++;
1246
1247 return SCIP_OKAY;
1248 }
1249
1250 /** frees all temporarily created bound change information data */
1251 static
conflictFreeTmpBdchginfos(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem)1252 void conflictFreeTmpBdchginfos(
1253 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1254 BMS_BLKMEM* blkmem /**< block memory */
1255 )
1256 {
1257 int i;
1258
1259 assert(conflict != NULL);
1260
1261 for( i = 0; i < conflict->ntmpbdchginfos; ++i )
1262 SCIPbdchginfoFree(&conflict->tmpbdchginfos[i], blkmem);
1263 conflict->ntmpbdchginfos = 0;
1264 }
1265
1266 /** clears the given conflict set */
1267 static
conflictsetClear(SCIP_CONFLICTSET * conflictset)1268 void conflictsetClear(
1269 SCIP_CONFLICTSET* conflictset /**< conflict set */
1270 )
1271 {
1272 assert(conflictset != NULL);
1273
1274 conflictset->nbdchginfos = 0;
1275 conflictset->validdepth = 0;
1276 conflictset->insertdepth = 0;
1277 conflictset->conflictdepth = 0;
1278 conflictset->repropdepth = 0;
1279 conflictset->repropagate = TRUE;
1280 conflictset->usescutoffbound = FALSE;
1281 conflictset->hasrelaxonlyvar = FALSE;
1282 conflictset->conflicttype = SCIP_CONFTYPE_UNKNOWN;
1283 }
1284
1285 /** creates an empty conflict set */
1286 static
conflictsetCreate(SCIP_CONFLICTSET ** conflictset,BMS_BLKMEM * blkmem)1287 SCIP_RETCODE conflictsetCreate(
1288 SCIP_CONFLICTSET** conflictset, /**< pointer to store the conflict set */
1289 BMS_BLKMEM* blkmem /**< block memory of transformed problem */
1290 )
1291 {
1292 assert(conflictset != NULL);
1293
1294 SCIP_ALLOC( BMSallocBlockMemory(blkmem, conflictset) );
1295 (*conflictset)->bdchginfos = NULL;
1296 (*conflictset)->relaxedbds = NULL;
1297 (*conflictset)->sortvals = NULL;
1298 (*conflictset)->bdchginfossize = 0;
1299
1300 conflictsetClear(*conflictset);
1301
1302 return SCIP_OKAY;
1303 }
1304
1305 /** creates a copy of the given conflict set, allocating an additional amount of memory */
1306 static
conflictsetCopy(SCIP_CONFLICTSET ** targetconflictset,BMS_BLKMEM * blkmem,SCIP_CONFLICTSET * sourceconflictset,int nadditionalelems)1307 SCIP_RETCODE conflictsetCopy(
1308 SCIP_CONFLICTSET** targetconflictset, /**< pointer to store the conflict set */
1309 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
1310 SCIP_CONFLICTSET* sourceconflictset, /**< source conflict set */
1311 int nadditionalelems /**< number of additional elements to allocate memory for */
1312 )
1313 {
1314 int targetsize;
1315
1316 assert(targetconflictset != NULL);
1317 assert(sourceconflictset != NULL);
1318
1319 targetsize = sourceconflictset->nbdchginfos + nadditionalelems;
1320 SCIP_ALLOC( BMSallocBlockMemory(blkmem, targetconflictset) );
1321 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*targetconflictset)->bdchginfos, targetsize) );
1322 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*targetconflictset)->relaxedbds, targetsize) );
1323 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*targetconflictset)->sortvals, targetsize) );
1324 (*targetconflictset)->bdchginfossize = targetsize;
1325
1326 BMScopyMemoryArray((*targetconflictset)->bdchginfos, sourceconflictset->bdchginfos, sourceconflictset->nbdchginfos);
1327 BMScopyMemoryArray((*targetconflictset)->relaxedbds, sourceconflictset->relaxedbds, sourceconflictset->nbdchginfos);
1328 BMScopyMemoryArray((*targetconflictset)->sortvals, sourceconflictset->sortvals, sourceconflictset->nbdchginfos);
1329
1330 (*targetconflictset)->nbdchginfos = sourceconflictset->nbdchginfos;
1331 (*targetconflictset)->validdepth = sourceconflictset->validdepth;
1332 (*targetconflictset)->insertdepth = sourceconflictset->insertdepth;
1333 (*targetconflictset)->conflictdepth = sourceconflictset->conflictdepth;
1334 (*targetconflictset)->repropdepth = sourceconflictset->repropdepth;
1335 (*targetconflictset)->usescutoffbound = sourceconflictset->usescutoffbound;
1336 (*targetconflictset)->hasrelaxonlyvar = sourceconflictset->hasrelaxonlyvar;
1337 (*targetconflictset)->conflicttype = sourceconflictset->conflicttype;
1338
1339 return SCIP_OKAY;
1340 }
1341
1342 /** frees a conflict set */
1343 static
conflictsetFree(SCIP_CONFLICTSET ** conflictset,BMS_BLKMEM * blkmem)1344 void conflictsetFree(
1345 SCIP_CONFLICTSET** conflictset, /**< pointer to the conflict set */
1346 BMS_BLKMEM* blkmem /**< block memory of transformed problem */
1347 )
1348 {
1349 assert(conflictset != NULL);
1350 assert(*conflictset != NULL);
1351
1352 BMSfreeBlockMemoryArrayNull(blkmem, &(*conflictset)->bdchginfos, (*conflictset)->bdchginfossize);
1353 BMSfreeBlockMemoryArrayNull(blkmem, &(*conflictset)->relaxedbds, (*conflictset)->bdchginfossize);
1354 BMSfreeBlockMemoryArrayNull(blkmem, &(*conflictset)->sortvals, (*conflictset)->bdchginfossize);
1355 BMSfreeBlockMemory(blkmem, conflictset);
1356 }
1357
1358 /** resizes the arrays of the conflict set to be able to store at least num bound change entries */
1359 static
conflictsetEnsureBdchginfosMem(SCIP_CONFLICTSET * conflictset,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)1360 SCIP_RETCODE conflictsetEnsureBdchginfosMem(
1361 SCIP_CONFLICTSET* conflictset, /**< conflict set */
1362 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
1363 SCIP_SET* set, /**< global SCIP settings */
1364 int num /**< minimal number of slots in arrays */
1365 )
1366 {
1367 assert(conflictset != NULL);
1368 assert(set != NULL);
1369
1370 if( num > conflictset->bdchginfossize )
1371 {
1372 int newsize;
1373
1374 newsize = SCIPsetCalcMemGrowSize(set, num);
1375 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &conflictset->bdchginfos, conflictset->bdchginfossize, newsize) );
1376 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &conflictset->relaxedbds, conflictset->bdchginfossize, newsize) );
1377 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &conflictset->sortvals, conflictset->bdchginfossize, newsize) );
1378 conflictset->bdchginfossize = newsize;
1379 }
1380 assert(num <= conflictset->bdchginfossize);
1381
1382 return SCIP_OKAY;
1383 }
1384
1385 /** calculates the score of the conflict set
1386 *
1387 * the score is weighted sum of number of bound changes, repropagation depth, and valid depth
1388 */
1389 static
conflictsetCalcScore(SCIP_CONFLICTSET * conflictset,SCIP_SET * set)1390 SCIP_Real conflictsetCalcScore(
1391 SCIP_CONFLICTSET* conflictset, /**< conflict set */
1392 SCIP_SET* set /**< global SCIP settings */
1393 )
1394 {
1395 assert(conflictset != NULL);
1396
1397 return -(set->conf_weightsize * conflictset->nbdchginfos
1398 + set->conf_weightrepropdepth * conflictset->repropdepth
1399 + set->conf_weightvaliddepth * conflictset->validdepth);
1400 }
1401
1402 /** calculates the score of a bound change within a conflict */
1403 static
calcBdchgScore(SCIP_Real prooflhs,SCIP_Real proofact,SCIP_Real proofactdelta,SCIP_Real proofcoef,int depth,int currentdepth,SCIP_VAR * var,SCIP_SET * set)1404 SCIP_Real calcBdchgScore(
1405 SCIP_Real prooflhs, /**< lhs of proof constraint */
1406 SCIP_Real proofact, /**< activity of the proof constraint */
1407 SCIP_Real proofactdelta, /**< activity change */
1408 SCIP_Real proofcoef, /**< coefficient in proof constraint */
1409 int depth, /**< bound change depth */
1410 int currentdepth, /**< current depth */
1411 SCIP_VAR* var, /**< variable corresponding to bound change */
1412 SCIP_SET* set /**< global SCIP settings */
1413 )
1414 {
1415 SCIP_COL* col;
1416 SCIP_Real score;
1417
1418 score = set->conf_proofscorefac * (1.0 - proofactdelta/(prooflhs - proofact));
1419 score = MAX(score, 0.0);
1420 score += set->conf_depthscorefac * (SCIP_Real)(depth+1)/(SCIP_Real)(currentdepth+1);
1421
1422 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
1423 col = SCIPvarGetCol(var);
1424 else
1425 col = NULL;
1426
1427 if( proofcoef > 0.0 )
1428 {
1429 if( col != NULL && SCIPcolGetNNonz(col) > 0 )
1430 score += set->conf_uplockscorefac
1431 * (SCIP_Real)(SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL))/(SCIP_Real)(SCIPcolGetNNonz(col));
1432 else
1433 score += set->conf_uplockscorefac * SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL);
1434 }
1435 else
1436 {
1437 if( col != NULL && SCIPcolGetNNonz(col) > 0 )
1438 score += set->conf_downlockscorefac
1439 * (SCIP_Real)(SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL))/(SCIP_Real)(SCIPcolGetNNonz(col));
1440 else
1441 score += set->conf_downlockscorefac * SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL);
1442 }
1443
1444 return score;
1445 }
1446
1447 /** check if the bound change info (which is the potential next candidate which is queued) is valid for the current
1448 * conflict analysis; a bound change info can get invalid if after this one was added to the queue, a weaker bound
1449 * change was added to the queue (due the bound widening idea) which immediately makes this bound change redundant; due
1450 * to the priority we did not removed that bound change info since that cost O(log(n)); hence we have to skip/ignore it
1451 * now
1452 *
1453 * The following situations can occur before for example the bound change info (x >= 3) is potentially popped from the
1454 * queue.
1455 *
1456 * Postcondition: the reason why (x >= 3) was queued is that at this time point no lower bound of x was involved yet in
1457 * the current conflict or the lower bound which was involved until then was stronger, e.g., (x >= 2).
1458 *
1459 * 1) during the time until (x >= 3) gets potentially popped no weaker lower bound was added to the queue, in that case
1460 * the conflictlbcount is valid and conflictlb is 3; that is (var->conflictlbcount == conflict->count &&
1461 * var->conflictlb == 3)
1462 *
1463 * 2) a weaker bound change info gets queued (e.g., x >= 4); this bound change is popped before (x >= 3) since it has
1464 * higher priority (which is the time stamp of the bound change info and (x >= 4) has to be done after (x >= 3)
1465 * during propagation or branching)
1466 *
1467 * a) if (x >= 4) is popped and added to the conflict set the conflictlbcount is still valid and conflictlb is at
1468 * most 4; that is (var->conflictlbcount == conflict->count && var->conflictlb >= 4); it follows that any bound
1469 * change info which is stronger than (x >= 4) gets ignored (for example x >= 2)
1470 *
1471 * b) if (x >= 4) is popped and resolved without introducing a new lower bound on x until (x >= 3) is a potentially
1472 * candidate the conflictlbcount indicates that bound change is currently not present; that is
1473 * (var->conflictlbcount != conflict->count)
1474 *
1475 * c) if (x >= 4) is popped and resolved and a new lower bound on x (e.g., x >= 2) is introduced until (x >= 3) is
1476 * pooped, the conflictlbcount indicates that bound change is currently present; that is (var->conflictlbcount ==
1477 * conflict->count); however the (x >= 3) only has be explained if conflictlb matches that one; that is
1478 * (var->conflictlb == bdchginfo->newbound); otherwise it redundant/invalid.
1479 */
1480 static
bdchginfoIsInvalid(SCIP_CONFLICT * conflict,SCIP_BDCHGINFO * bdchginfo)1481 SCIP_Bool bdchginfoIsInvalid(
1482 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1483 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
1484 )
1485 {
1486 SCIP_VAR* var;
1487
1488 assert(bdchginfo != NULL);
1489
1490 var = SCIPbdchginfoGetVar(bdchginfo);
1491 assert(var != NULL);
1492
1493 /* the bound change info of a binary (domained) variable can never be invalid since the concepts of relaxed bounds
1494 * and bound widening do not make sense for these type of variables
1495 */
1496 if( SCIPvarIsBinary(var) )
1497 return FALSE;
1498
1499 /* check if the bdchginfo is invaild since a tight/weaker bound change was already explained */
1500 if( SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER )
1501 {
1502 if( var->conflictlbcount != conflict->count || var->conflictlb != SCIPbdchginfoGetNewbound(bdchginfo) ) /*lint !e777*/
1503 {
1504 assert(!SCIPvarIsBinary(var));
1505 return TRUE;
1506 }
1507 }
1508 else
1509 {
1510 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_UPPER);
1511
1512 if( var->conflictubcount != conflict->count || var->conflictub != SCIPbdchginfoGetNewbound(bdchginfo) ) /*lint !e777*/
1513 {
1514 assert(!SCIPvarIsBinary(var));
1515 return TRUE;
1516 }
1517 }
1518
1519 return FALSE;
1520 }
1521
1522 /** adds a bound change to a conflict set */
1523 static
conflictsetAddBound(SCIP_CONFLICTSET * conflictset,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd)1524 SCIP_RETCODE conflictsetAddBound(
1525 SCIP_CONFLICTSET* conflictset, /**< conflict set */
1526 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
1527 SCIP_SET* set, /**< global SCIP settings */
1528 SCIP_BDCHGINFO* bdchginfo, /**< bound change to add to the conflict set */
1529 SCIP_Real relaxedbd /**< relaxed bound */
1530 )
1531 {
1532 SCIP_BDCHGINFO** bdchginfos;
1533 SCIP_Real* relaxedbds;
1534 int* sortvals;
1535 SCIP_VAR* var;
1536 SCIP_BOUNDTYPE boundtype;
1537 int idx;
1538 int sortval;
1539 int pos;
1540
1541 assert(conflictset != NULL);
1542 assert(bdchginfo != NULL);
1543
1544 /* allocate memory for additional element */
1545 SCIP_CALL( conflictsetEnsureBdchginfosMem(conflictset, blkmem, set, conflictset->nbdchginfos+1) );
1546
1547 /* insert the new bound change in the arrays sorted by increasing variable index and by bound type */
1548 bdchginfos = conflictset->bdchginfos;
1549 relaxedbds = conflictset->relaxedbds;
1550 sortvals = conflictset->sortvals;
1551 var = SCIPbdchginfoGetVar(bdchginfo);
1552 boundtype = SCIPbdchginfoGetBoundtype(bdchginfo);
1553 idx = SCIPvarGetIndex(var);
1554 assert(idx < INT_MAX/2);
1555 assert((int)boundtype == 0 || (int)boundtype == 1);
1556 sortval = 2*idx + (int)boundtype; /* first sorting criteria: variable index, second criteria: boundtype */
1557
1558 /* insert new element into the sorted arrays; if an element exits with the same value insert the new element afterwards
1559 *
1560 * @todo check if it better (faster) to first search for the position O(log n) and compare the sort values and if
1561 * they are equal just replace the element and if not run the insert method O(n)
1562 */
1563
1564 SCIPsortedvecInsertIntPtrReal(sortvals, (void**)bdchginfos, relaxedbds, sortval, (void*)bdchginfo, relaxedbd, &conflictset->nbdchginfos, &pos);
1565 assert(pos == conflictset->nbdchginfos - 1 || sortval < sortvals[pos+1]);
1566
1567 /* merge multiple bound changes */
1568 if( pos > 0 && sortval == sortvals[pos-1] )
1569 {
1570 /* this is a multiple bound change */
1571 if( SCIPbdchginfoIsTighter(bdchginfo, bdchginfos[pos-1]) )
1572 {
1573 /* remove the "old" bound change since the "new" one in tighter */
1574 SCIPsortedvecDelPosIntPtrReal(sortvals, (void**)bdchginfos, relaxedbds, pos-1, &conflictset->nbdchginfos);
1575 }
1576 else if( SCIPbdchginfoIsTighter(bdchginfos[pos-1], bdchginfo) )
1577 {
1578 /* remove the "new" bound change since the "old" one is tighter */
1579 SCIPsortedvecDelPosIntPtrReal(sortvals, (void**)bdchginfos, relaxedbds, pos, &conflictset->nbdchginfos);
1580 }
1581 else
1582 {
1583 /* both bound change are equivalent; hence, keep the worse relaxed bound and remove one of them */
1584 relaxedbds[pos-1] = boundtype == SCIP_BOUNDTYPE_LOWER ? MAX(relaxedbds[pos-1], relaxedbd) : MIN(relaxedbds[pos-1], relaxedbd);
1585 SCIPsortedvecDelPosIntPtrReal(sortvals, (void**)bdchginfos, relaxedbds, pos, &conflictset->nbdchginfos);
1586 }
1587 }
1588
1589 if( SCIPvarIsRelaxationOnly(var) )
1590 conflictset->hasrelaxonlyvar = TRUE;
1591
1592 return SCIP_OKAY;
1593 }
1594
1595 /** adds given bound changes to a conflict set */
1596 static
conflictsetAddBounds(SCIP_CONFLICT * conflict,SCIP_CONFLICTSET * conflictset,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_BDCHGINFO ** bdchginfos,int nbdchginfos)1597 SCIP_RETCODE conflictsetAddBounds(
1598 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1599 SCIP_CONFLICTSET* conflictset, /**< conflict set */
1600 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
1601 SCIP_SET* set, /**< global SCIP settings */
1602 SCIP_BDCHGINFO** bdchginfos, /**< bound changes to add to the conflict set */
1603 int nbdchginfos /**< number of bound changes to add */
1604 )
1605 {
1606 SCIP_BDCHGINFO** confbdchginfos;
1607 SCIP_BDCHGINFO* bdchginfo;
1608 SCIP_Real* confrelaxedbds;
1609 int* confsortvals;
1610 int confnbdchginfos;
1611 int idx;
1612 int sortval;
1613 int i;
1614 SCIP_BOUNDTYPE boundtype;
1615
1616 assert(conflict != NULL);
1617 assert(conflictset != NULL);
1618 assert(blkmem != NULL);
1619 assert(set != NULL);
1620 assert(bdchginfos != NULL || nbdchginfos == 0);
1621
1622 /* nothing to add */
1623 if( nbdchginfos == 0 )
1624 return SCIP_OKAY;
1625
1626 assert(bdchginfos != NULL);
1627
1628 /* only one element to add, use the single insertion method */
1629 if( nbdchginfos == 1 )
1630 {
1631 bdchginfo = bdchginfos[0];
1632 assert(bdchginfo != NULL);
1633
1634 if( !bdchginfoIsInvalid(conflict, bdchginfo) )
1635 {
1636 SCIP_CALL( conflictsetAddBound(conflictset, blkmem, set, bdchginfo, SCIPbdchginfoGetRelaxedBound(bdchginfo)) );
1637 }
1638 else
1639 {
1640 SCIPsetDebugMsg(set, "-> bound change info [%d:<%s> %s %g] is invaild -> ignore it\n", SCIPbdchginfoGetDepth(bdchginfo),
1641 SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
1642 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
1643 SCIPbdchginfoGetNewbound(bdchginfo));
1644 }
1645
1646 return SCIP_OKAY;
1647 }
1648
1649 confnbdchginfos = conflictset->nbdchginfos;
1650
1651 /* allocate memory for additional element */
1652 SCIP_CALL( conflictsetEnsureBdchginfosMem(conflictset, blkmem, set, confnbdchginfos + nbdchginfos) );
1653
1654 confbdchginfos = conflictset->bdchginfos;
1655 confrelaxedbds = conflictset->relaxedbds;
1656 confsortvals = conflictset->sortvals;
1657
1658 assert(SCIP_BOUNDTYPE_LOWER == FALSE); /*lint !e641 !e506*/
1659 assert(SCIP_BOUNDTYPE_UPPER == TRUE); /*lint !e641 !e506*/
1660
1661 for( i = 0; i < nbdchginfos; ++i )
1662 {
1663 bdchginfo = bdchginfos[i];
1664 assert(bdchginfo != NULL);
1665
1666 /* add only valid bound change infos */
1667 if( !bdchginfoIsInvalid(conflict, bdchginfo) )
1668 {
1669 /* calculate sorting value */
1670 boundtype = SCIPbdchginfoGetBoundtype(bdchginfo);
1671 assert(SCIPbdchginfoGetVar(bdchginfo) != NULL);
1672
1673 idx = SCIPvarGetIndex(SCIPbdchginfoGetVar(bdchginfo));
1674 assert(idx < INT_MAX/2);
1675
1676 assert((int)boundtype == 0 || (int)boundtype == 1);
1677 sortval = 2*idx + (int)boundtype; /* first sorting criteria: variable index, second criteria: boundtype */
1678
1679 /* add new element */
1680 confbdchginfos[confnbdchginfos] = bdchginfo;
1681 confrelaxedbds[confnbdchginfos] = SCIPbdchginfoGetRelaxedBound(bdchginfo);
1682 confsortvals[confnbdchginfos] = sortval;
1683 ++confnbdchginfos;
1684
1685 if( SCIPvarIsRelaxationOnly(SCIPbdchginfoGetVar(bdchginfo)) )
1686 conflictset->hasrelaxonlyvar = TRUE;
1687 }
1688 else
1689 {
1690 SCIPsetDebugMsg(set, "-> bound change info [%d:<%s> %s %g] is invaild -> ignore it\n", SCIPbdchginfoGetDepth(bdchginfo),
1691 SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
1692 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
1693 SCIPbdchginfoGetNewbound(bdchginfo));
1694 }
1695 }
1696 assert(confnbdchginfos <= conflictset->nbdchginfos + nbdchginfos);
1697
1698 /* sort and merge the new conflict set */
1699 if( confnbdchginfos > conflictset->nbdchginfos )
1700 {
1701 int k = 0;
1702
1703 /* sort array */
1704 SCIPsortIntPtrReal(confsortvals, (void**)confbdchginfos, confrelaxedbds, confnbdchginfos);
1705
1706 i = 1;
1707 /* merge multiple bound changes */
1708 while( i < confnbdchginfos )
1709 {
1710 assert(i > k);
1711
1712 /* is this a multiple bound change */
1713 if( confsortvals[k] == confsortvals[i] )
1714 {
1715 if( SCIPbdchginfoIsTighter(confbdchginfos[k], confbdchginfos[i]) )
1716 ++i;
1717 else if( SCIPbdchginfoIsTighter(confbdchginfos[i], confbdchginfos[k]) )
1718 {
1719 /* replace worse bound change info by tighter bound change info */
1720 confbdchginfos[k] = confbdchginfos[i];
1721 confrelaxedbds[k] = confrelaxedbds[i];
1722 confsortvals[k] = confsortvals[i];
1723 ++i;
1724 }
1725 else
1726 {
1727 assert(confsortvals[k] == confsortvals[i]);
1728
1729 /* both bound change are equivalent; hence, keep the worse relaxed bound and remove one of them */
1730 confrelaxedbds[k] = (confsortvals[k] % 2 == 0) ? MAX(confrelaxedbds[k], confrelaxedbds[i]) : MIN(confrelaxedbds[k], confrelaxedbds[i]);
1731 ++i;
1732 }
1733 }
1734 else
1735 {
1736 /* all bound change infos must be valid */
1737 assert(!bdchginfoIsInvalid(conflict, confbdchginfos[k]));
1738
1739 ++k;
1740 /* move next comparison element to the correct position */
1741 if( k != i )
1742 {
1743 confbdchginfos[k] = confbdchginfos[i];
1744 confrelaxedbds[k] = confrelaxedbds[i];
1745 confsortvals[k] = confsortvals[i];
1746 }
1747 ++i;
1748 }
1749 }
1750 /* last bound change infos must also be valid */
1751 assert(!bdchginfoIsInvalid(conflict, confbdchginfos[k]));
1752 /* the number of bound change infos cannot be decreased, it would mean that the conflict set was not merged
1753 * before
1754 */
1755 assert(conflictset->nbdchginfos <= k + 1 );
1756 assert(k + 1 <= confnbdchginfos);
1757
1758 conflictset->nbdchginfos = k + 1;
1759 }
1760
1761 return SCIP_OKAY;
1762 }
1763
1764 /** calculates the conflict and the repropagation depths of the conflict set */
1765 static
conflictsetCalcConflictDepth(SCIP_CONFLICTSET * conflictset)1766 void conflictsetCalcConflictDepth(
1767 SCIP_CONFLICTSET* conflictset /**< conflict set */
1768 )
1769 {
1770 int maxdepth[2];
1771 int i;
1772
1773 assert(conflictset != NULL);
1774 assert(conflictset->validdepth <= conflictset->insertdepth);
1775
1776 /* get the depth of the last and last but one bound change */
1777 maxdepth[0] = conflictset->validdepth;
1778 maxdepth[1] = conflictset->validdepth;
1779 for( i = 0; i < conflictset->nbdchginfos; ++i )
1780 {
1781 int depth;
1782
1783 depth = SCIPbdchginfoGetDepth(conflictset->bdchginfos[i]);
1784 assert(depth >= 0);
1785 if( depth > maxdepth[0] )
1786 {
1787 maxdepth[1] = maxdepth[0];
1788 maxdepth[0] = depth;
1789 }
1790 else if( depth > maxdepth[1] )
1791 maxdepth[1] = depth;
1792 }
1793 assert(maxdepth[0] >= maxdepth[1]);
1794
1795 conflictset->conflictdepth = maxdepth[0];
1796 conflictset->repropdepth = maxdepth[1];
1797 }
1798
1799 /** identifies the depth, at which the conflict set should be added:
1800 * - if the branching rule operates on variables only, and if all branching variables up to a certain
1801 * depth level are member of the conflict, the conflict constraint can only be violated in the subtree
1802 * of the node at that depth, because in all other nodes, at least one of these branching variables
1803 * violates its conflicting bound, such that the conflict constraint is feasible
1804 * - if there is at least one branching variable in a node, we assume, that this branching was performed
1805 * on variables, and that the siblings of this node are disjunct w.r.t. the branching variables' fixings
1806 * - we have to add the conflict set at least in the valid depth of the initial conflict set,
1807 * so we start searching at the first branching after this depth level, i.e. validdepth+1
1808 */
1809 static
conflictsetCalcInsertDepth(SCIP_CONFLICTSET * conflictset,SCIP_SET * set,SCIP_TREE * tree)1810 SCIP_RETCODE conflictsetCalcInsertDepth(
1811 SCIP_CONFLICTSET* conflictset, /**< conflict set */
1812 SCIP_SET* set, /**< global SCIP settings */
1813 SCIP_TREE* tree /**< branch and bound tree */
1814 )
1815 {
1816 SCIP_Bool* branchingincluded;
1817 int currentdepth;
1818 int i;
1819
1820 assert(conflictset != NULL);
1821 assert(set != NULL);
1822 assert(tree != NULL);
1823
1824 /* the conflict set must not be inserted prior to its valid depth */
1825 conflictset->insertdepth = conflictset->validdepth;
1826 assert(conflictset->insertdepth >= 0);
1827
1828 currentdepth = SCIPtreeGetCurrentDepth(tree);
1829 assert(currentdepth == tree->pathlen-1);
1830
1831 /* mark the levels for which a branching variable is included in the conflict set */
1832 SCIP_CALL( SCIPsetAllocBufferArray(set, &branchingincluded, currentdepth+2) );
1833 BMSclearMemoryArray(branchingincluded, currentdepth+2);
1834 for( i = 0; i < conflictset->nbdchginfos; ++i )
1835 {
1836 int depth;
1837
1838 depth = SCIPbdchginfoGetDepth(conflictset->bdchginfos[i]);
1839 depth = MIN(depth, currentdepth+1); /* put diving/probing/strong branching changes in this depth level */
1840 branchingincluded[depth] = TRUE;
1841 }
1842
1843 /* skip additional depth levels where branching on the conflict variables was applied */
1844 while( conflictset->insertdepth < currentdepth && branchingincluded[conflictset->insertdepth+1] )
1845 conflictset->insertdepth++;
1846
1847 /* free temporary memory */
1848 SCIPsetFreeBufferArray(set, &branchingincluded);
1849
1850 assert(conflictset->validdepth <= conflictset->insertdepth && conflictset->insertdepth <= currentdepth);
1851
1852 return SCIP_OKAY;
1853 }
1854
1855 /** checks whether the first conflict set is redundant to the second one */
1856 static
conflictsetIsRedundant(SCIP_CONFLICTSET * conflictset1,SCIP_CONFLICTSET * conflictset2)1857 SCIP_Bool conflictsetIsRedundant(
1858 SCIP_CONFLICTSET* conflictset1, /**< first conflict conflict set */
1859 SCIP_CONFLICTSET* conflictset2 /**< second conflict conflict set */
1860 )
1861 {
1862 int i1;
1863 int i2;
1864
1865 assert(conflictset1 != NULL);
1866 assert(conflictset2 != NULL);
1867
1868 /* if conflictset1 has smaller validdepth, it is definitely not redundant to conflictset2 */
1869 if( conflictset1->validdepth < conflictset2->validdepth )
1870 return FALSE;
1871
1872 /* check, if all bound changes in conflictset2 are also present at least as tight in conflictset1;
1873 * we can stop immediately, if more bound changes are remaining in conflictset2 than in conflictset1
1874 */
1875 for( i1 = 0, i2 = 0; i2 < conflictset2->nbdchginfos && conflictset1->nbdchginfos - i1 >= conflictset2->nbdchginfos - i2;
1876 ++i1, ++i2 )
1877 {
1878 int sortval;
1879
1880 assert(i2 == 0 || conflictset2->sortvals[i2-1] < conflictset2->sortvals[i2]);
1881
1882 sortval = conflictset2->sortvals[i2];
1883 for( ; i1 < conflictset1->nbdchginfos && conflictset1->sortvals[i1] < sortval; ++i1 ) /*lint !e445*/
1884 {
1885 /* while scanning conflictset1, check consistency */
1886 assert(i1 == 0 || conflictset1->sortvals[i1-1] < conflictset1->sortvals[i1]);
1887 }
1888 if( i1 >= conflictset1->nbdchginfos || conflictset1->sortvals[i1] > sortval
1889 || SCIPbdchginfoIsTighter(conflictset2->bdchginfos[i2], conflictset1->bdchginfos[i1]) )
1890 return FALSE;
1891 }
1892
1893 return (i2 == conflictset2->nbdchginfos);
1894 }
1895
1896 #ifdef SCIP_DEBUG
1897 /** prints a conflict set to the screen */
1898 static
conflictsetPrint(SCIP_CONFLICTSET * conflictset)1899 void conflictsetPrint(
1900 SCIP_CONFLICTSET* conflictset /**< conflict set */
1901 )
1902 {
1903 int i;
1904
1905 assert(conflictset != NULL);
1906 for( i = 0; i < conflictset->nbdchginfos; ++i )
1907 {
1908 SCIPdebugPrintf(" [%d:<%s> %s %g(%g)]", SCIPbdchginfoGetDepth(conflictset->bdchginfos[i]),
1909 SCIPvarGetName(SCIPbdchginfoGetVar(conflictset->bdchginfos[i])),
1910 SCIPbdchginfoGetBoundtype(conflictset->bdchginfos[i]) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
1911 SCIPbdchginfoGetNewbound(conflictset->bdchginfos[i]), conflictset->relaxedbds[i]);
1912 }
1913 SCIPdebugPrintf("\n");
1914 }
1915 #endif
1916
1917 /** resizes proofsets array to be able to store at least num entries */
1918 static
conflictEnsureProofsetsMem(SCIP_CONFLICT * conflict,SCIP_SET * set,int num)1919 SCIP_RETCODE conflictEnsureProofsetsMem(
1920 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1921 SCIP_SET* set, /**< global SCIP settings */
1922 int num /**< minimal number of slots in array */
1923 )
1924 {
1925 assert(conflict != NULL);
1926 assert(set != NULL);
1927
1928 if( num > conflict->proofsetssize )
1929 {
1930 int newsize;
1931
1932 newsize = SCIPsetCalcMemGrowSize(set, num);
1933 SCIP_ALLOC( BMSreallocMemoryArray(&conflict->proofsets, newsize) );
1934 conflict->proofsetssize = newsize;
1935 }
1936 assert(num <= conflict->proofsetssize);
1937
1938 return SCIP_OKAY;
1939 }
1940
1941 /** resizes conflictsets array to be able to store at least num entries */
1942 static
conflictEnsureConflictsetsMem(SCIP_CONFLICT * conflict,SCIP_SET * set,int num)1943 SCIP_RETCODE conflictEnsureConflictsetsMem(
1944 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1945 SCIP_SET* set, /**< global SCIP settings */
1946 int num /**< minimal number of slots in array */
1947 )
1948 {
1949 assert(conflict != NULL);
1950 assert(set != NULL);
1951
1952 if( num > conflict->conflictsetssize )
1953 {
1954 int newsize;
1955
1956 newsize = SCIPsetCalcMemGrowSize(set, num);
1957 SCIP_ALLOC( BMSreallocMemoryArray(&conflict->conflictsets, newsize) );
1958 SCIP_ALLOC( BMSreallocMemoryArray(&conflict->conflictsetscores, newsize) );
1959 conflict->conflictsetssize = newsize;
1960 }
1961 assert(num <= conflict->conflictsetssize);
1962
1963 return SCIP_OKAY;
1964 }
1965
1966 /** add a proofset to the list of all proofsets */
1967 static
conflictInsertProofset(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_PROOFSET * proofset)1968 SCIP_RETCODE conflictInsertProofset(
1969 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1970 SCIP_SET* set, /**< global SCIP settings */
1971 SCIP_PROOFSET* proofset /**< proof set to add */
1972 )
1973 {
1974 assert(conflict != NULL);
1975 assert(proofset != NULL);
1976
1977 /* insert proofset into the sorted proofsets array */
1978 SCIP_CALL( conflictEnsureProofsetsMem(conflict, set, conflict->nproofsets + 1) );
1979
1980 conflict->proofsets[conflict->nproofsets] = proofset;
1981 ++conflict->nproofsets;
1982
1983 return SCIP_OKAY;
1984 }
1985
1986 /** inserts conflict set into sorted conflictsets array and deletes the conflict set pointer */
1987 static
conflictInsertConflictset(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_CONFLICTSET ** conflictset)1988 SCIP_RETCODE conflictInsertConflictset(
1989 SCIP_CONFLICT* conflict, /**< conflict analysis data */
1990 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
1991 SCIP_SET* set, /**< global SCIP settings */
1992 SCIP_CONFLICTSET** conflictset /**< pointer to conflict set to insert */
1993 )
1994 {
1995 SCIP_Real score;
1996 int pos;
1997 int i;
1998 int j;
1999
2000 assert(conflict != NULL);
2001 assert(set != NULL);
2002 assert(conflictset != NULL);
2003 assert(*conflictset != NULL);
2004 assert((*conflictset)->validdepth <= (*conflictset)->insertdepth);
2005 assert(set->conf_allowlocal || (*conflictset)->validdepth == 0);
2006
2007 /* calculate conflict and repropagation depth */
2008 conflictsetCalcConflictDepth(*conflictset);
2009
2010 /* if we apply repropagations, the conflict set should be inserted at most at its repropdepth */
2011 if( set->conf_repropagate )
2012 (*conflictset)->insertdepth = MIN((*conflictset)->insertdepth, (*conflictset)->repropdepth);
2013 else
2014 (*conflictset)->repropdepth = INT_MAX;
2015 assert((*conflictset)->insertdepth <= (*conflictset)->repropdepth);
2016
2017 SCIPsetDebugMsg(set, "inserting conflict set (valid: %d, insert: %d, conf: %d, reprop: %d):\n",
2018 (*conflictset)->validdepth, (*conflictset)->insertdepth, (*conflictset)->conflictdepth, (*conflictset)->repropdepth);
2019 SCIPdebug(conflictsetPrint(*conflictset));
2020
2021 /* get the score of the conflict set */
2022 score = conflictsetCalcScore(*conflictset, set);
2023
2024 /* check, if conflict set is redundant to a better conflict set */
2025 for( pos = 0; pos < conflict->nconflictsets && score < conflict->conflictsetscores[pos]; ++pos )
2026 {
2027 /* check if conflict set is redundant with respect to conflictsets[pos] */
2028 if( conflictsetIsRedundant(*conflictset, conflict->conflictsets[pos]) )
2029 {
2030 SCIPsetDebugMsg(set, " -> conflict set is redundant to: ");
2031 SCIPdebug(conflictsetPrint(conflict->conflictsets[pos]));
2032 conflictsetFree(conflictset, blkmem);
2033 return SCIP_OKAY;
2034 }
2035
2036 /**@todo like in sepastore.c: calculate overlap between conflictsets -> large overlap reduces score */
2037 }
2038
2039 /* insert conflictset into the sorted conflictsets array */
2040 SCIP_CALL( conflictEnsureConflictsetsMem(conflict, set, conflict->nconflictsets + 1) );
2041 for( i = conflict->nconflictsets; i > pos; --i )
2042 {
2043 assert(score >= conflict->conflictsetscores[i-1]);
2044 conflict->conflictsets[i] = conflict->conflictsets[i-1];
2045 conflict->conflictsetscores[i] = conflict->conflictsetscores[i-1];
2046 }
2047 conflict->conflictsets[pos] = *conflictset;
2048 conflict->conflictsetscores[pos] = score;
2049 conflict->nconflictsets++;
2050
2051 /* remove worse conflictsets that are redundant to the new conflictset */
2052 for( i = pos+1, j = pos+1; i < conflict->nconflictsets; ++i )
2053 {
2054 if( conflictsetIsRedundant(conflict->conflictsets[i], *conflictset) )
2055 {
2056 SCIPsetDebugMsg(set, " -> conflict set dominates: ");
2057 SCIPdebug(conflictsetPrint(conflict->conflictsets[i]));
2058 conflictsetFree(&conflict->conflictsets[i], blkmem);
2059 }
2060 else
2061 {
2062 assert(j <= i);
2063 conflict->conflictsets[j] = conflict->conflictsets[i];
2064 conflict->conflictsetscores[j] = conflict->conflictsetscores[i];
2065 j++;
2066 }
2067 }
2068 assert(j <= conflict->nconflictsets);
2069 conflict->nconflictsets = j;
2070
2071 #ifdef SCIP_CONFGRAPH
2072 confgraphMarkConflictset(*conflictset);
2073 #endif
2074
2075 *conflictset = NULL; /* ownership of pointer is now in the conflictsets array */
2076
2077 return SCIP_OKAY;
2078 }
2079
2080 /** calculates the maximal size of conflict sets to be used */
2081 static
conflictCalcMaxsize(SCIP_SET * set,SCIP_PROB * prob)2082 int conflictCalcMaxsize(
2083 SCIP_SET* set, /**< global SCIP settings */
2084 SCIP_PROB* prob /**< problem data */
2085 )
2086 {
2087 int maxsize;
2088
2089 assert(set != NULL);
2090 assert(prob != NULL);
2091
2092 maxsize = (int)(set->conf_maxvarsfac * (prob->nvars - prob->ncontvars));
2093 maxsize = MAX(maxsize, set->conf_minmaxvars);
2094
2095 return maxsize;
2096 }
2097
2098 /** increases the conflict score of the variable in the given direction */
2099 static
incVSIDS(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BOUNDTYPE boundtype,SCIP_Real value,SCIP_Real weight)2100 SCIP_RETCODE incVSIDS(
2101 SCIP_VAR* var, /**< problem variable */
2102 BMS_BLKMEM* blkmem, /**< block memory */
2103 SCIP_SET* set, /**< global SCIP settings */
2104 SCIP_STAT* stat, /**< dynamic problem statistics */
2105 SCIP_BOUNDTYPE boundtype, /**< type of bound for which the score should be increased */
2106 SCIP_Real value, /**< value of the bound */
2107 SCIP_Real weight /**< weight of this VSIDS updates */
2108 )
2109 {
2110 SCIP_BRANCHDIR branchdir;
2111
2112 assert(var != NULL);
2113 assert(stat != NULL);
2114
2115 /* weight the VSIDS by the given weight */
2116 weight *= stat->vsidsweight;
2117
2118 if( SCIPsetIsZero(set, weight) )
2119 return SCIP_OKAY;
2120
2121 branchdir = (boundtype == SCIP_BOUNDTYPE_LOWER ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS); /*lint !e641*/
2122 SCIP_CALL( SCIPvarIncVSIDS(var, blkmem, set, stat, branchdir, value, weight) );
2123 SCIPhistoryIncVSIDS(stat->glbhistory, branchdir, weight);
2124 SCIPhistoryIncVSIDS(stat->glbhistorycrun, branchdir, weight);
2125
2126 return SCIP_OKAY;
2127 }
2128
2129 /** update conflict statistics */
2130 static
updateStatistics(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_CONFLICTSET * conflictset,int insertdepth)2131 SCIP_RETCODE updateStatistics(
2132 SCIP_CONFLICT* conflict, /**< conflict analysis data */
2133 BMS_BLKMEM* blkmem, /**< block memory */
2134 SCIP_SET* set, /**< global SCIP settings */
2135 SCIP_STAT* stat, /**< dynamic problem statistics */
2136 SCIP_CONFLICTSET* conflictset, /**< conflict set to add to the tree */
2137 int insertdepth /**< depth level at which the conflict set should be added */
2138 )
2139 {
2140 if( insertdepth > 0 )
2141 {
2142 conflict->nappliedlocconss++;
2143 conflict->nappliedlocliterals += conflictset->nbdchginfos;
2144 }
2145 else
2146 {
2147 int i;
2148 int conflictlength;
2149 conflictlength = conflictset->nbdchginfos;
2150
2151 for( i = 0; i < conflictlength; i++ )
2152 {
2153 SCIP_VAR* var;
2154 SCIP_BRANCHDIR branchdir;
2155 SCIP_BOUNDTYPE boundtype;
2156 SCIP_Real bound;
2157
2158 assert(stat != NULL);
2159
2160 var = conflictset->bdchginfos[i]->var;
2161 boundtype = SCIPbdchginfoGetBoundtype(conflictset->bdchginfos[i]);
2162 bound = conflictset->relaxedbds[i];
2163
2164 branchdir = (boundtype == SCIP_BOUNDTYPE_LOWER ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS); /*lint !e641*/
2165
2166 SCIP_CALL( SCIPvarIncNActiveConflicts(var, blkmem, set, stat, branchdir, bound, (SCIP_Real)conflictlength) );
2167 SCIPhistoryIncNActiveConflicts(stat->glbhistory, branchdir, (SCIP_Real)conflictlength);
2168 SCIPhistoryIncNActiveConflicts(stat->glbhistorycrun, branchdir, (SCIP_Real)conflictlength);
2169
2170 /* each variable which is part of the conflict gets an increase in the VSIDS */
2171 SCIP_CALL( incVSIDS(var, blkmem, set, stat, boundtype, bound, set->conf_conflictweight) );
2172 }
2173 conflict->nappliedglbconss++;
2174 conflict->nappliedglbliterals += conflictset->nbdchginfos;
2175 }
2176
2177 return SCIP_OKAY;
2178 }
2179
2180
2181 /** check conflict set for redundancy, other conflicts in the same conflict analysis could have led to global reductions
2182 * an made this conflict set redundant
2183 */
2184 static
checkRedundancy(SCIP_SET * set,SCIP_CONFLICTSET * conflictset)2185 SCIP_Bool checkRedundancy(
2186 SCIP_SET* set, /**< global SCIP settings */
2187 SCIP_CONFLICTSET* conflictset /**< conflict set */
2188 )
2189 {
2190 SCIP_BDCHGINFO** bdchginfos;
2191 SCIP_VAR* var;
2192 SCIP_Real* relaxedbds;
2193 SCIP_Real bound;
2194 int v;
2195
2196 assert(set != NULL);
2197 assert(conflictset != NULL);
2198
2199 bdchginfos = conflictset->bdchginfos;
2200 relaxedbds = conflictset->relaxedbds;
2201 assert(bdchginfos != NULL);
2202 assert(relaxedbds != NULL);
2203
2204 /* check all boundtypes and bounds for redundancy */
2205 for( v = conflictset->nbdchginfos - 1; v >= 0; --v )
2206 {
2207 var = SCIPbdchginfoGetVar(bdchginfos[v]);
2208 assert(var != NULL);
2209 assert(SCIPvarGetProbindex(var) >= 0);
2210
2211 /* check if the relaxed bound is really a relaxed bound */
2212 assert(SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_LOWER || SCIPsetIsGE(set, relaxedbds[v], SCIPbdchginfoGetNewbound(bdchginfos[v])));
2213 assert(SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_UPPER || SCIPsetIsLE(set, relaxedbds[v], SCIPbdchginfoGetNewbound(bdchginfos[v])));
2214
2215 bound = relaxedbds[v];
2216
2217 if( SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_UPPER )
2218 {
2219 if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS )
2220 {
2221 assert(SCIPsetIsIntegral(set, bound));
2222 bound += 1.0;
2223 }
2224
2225 /* check if the bound is already fulfilled globally */
2226 if( SCIPsetIsFeasGE(set, SCIPvarGetLbGlobal(var), bound) )
2227 return TRUE;
2228 }
2229 else
2230 {
2231 assert(SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_LOWER);
2232
2233 if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS )
2234 {
2235 assert(SCIPsetIsIntegral(set, bound));
2236 bound -= 1.0;
2237 }
2238
2239 /* check if the bound is already fulfilled globally */
2240 if( SCIPsetIsFeasLE(set, SCIPvarGetUbGlobal(var), bound) )
2241 return TRUE;
2242 }
2243 }
2244
2245 return FALSE;
2246 }
2247
2248 /** find global fixings which can be derived from the new conflict set */
2249 static
detectImpliedBounds(SCIP_SET * set,SCIP_PROB * prob,SCIP_CONFLICTSET * conflictset,int * nbdchgs,int * nredvars,SCIP_Bool * redundant)2250 SCIP_RETCODE detectImpliedBounds(
2251 SCIP_SET* set, /**< global SCIP settings */
2252 SCIP_PROB* prob, /**< transformed problem after presolve */
2253 SCIP_CONFLICTSET* conflictset, /**< conflict set to add to the tree */
2254 int* nbdchgs, /**< number of global deducted bound changes due to the conflict set */
2255 int* nredvars, /**< number of redundant and removed variables from conflict set */
2256 SCIP_Bool* redundant /**< did we found a global reduction on a conflict set variable, which makes this conflict redundant */
2257 )
2258 {
2259 SCIP_BDCHGINFO** bdchginfos;
2260 SCIP_Real* relaxedbds;
2261 SCIP_VAR* var;
2262 SCIP_Bool* boundtypes;
2263 SCIP_Real* bounds;
2264 SCIP_Longint* nbinimpls;
2265 int* sortvals;
2266 SCIP_Real bound;
2267 SCIP_Bool isupper;
2268 int ntrivialredvars;
2269 int nbdchginfos;
2270 int nzeroimpls;
2271 int v;
2272
2273 assert(set != NULL);
2274 assert(prob != NULL);
2275 assert(SCIPprobIsTransformed(prob));
2276 assert(conflictset != NULL);
2277 assert(nbdchgs != NULL);
2278 assert(nredvars != NULL);
2279 /* only check conflict sets with more than one variable */
2280 assert(conflictset->nbdchginfos > 1);
2281
2282 *nbdchgs = 0;
2283 *nredvars = 0;
2284
2285 /* due to other conflict in the same conflict analysis, this conflict set might have become redundant */
2286 *redundant = checkRedundancy(set, conflictset);
2287
2288 if( *redundant )
2289 return SCIP_OKAY;
2290
2291 bdchginfos = conflictset->bdchginfos;
2292 relaxedbds = conflictset->relaxedbds;
2293 nbdchginfos = conflictset->nbdchginfos;
2294 sortvals = conflictset->sortvals;
2295
2296 assert(bdchginfos != NULL);
2297 assert(relaxedbds != NULL);
2298 assert(sortvals != NULL);
2299
2300 /* check if the boolean representation of boundtypes matches the 'standard' definition */
2301 assert(SCIP_BOUNDTYPE_LOWER == FALSE); /*lint !e641 !e506*/
2302 assert(SCIP_BOUNDTYPE_UPPER == TRUE); /*lint !e641 !e506*/
2303
2304 ntrivialredvars = 0;
2305
2306 /* due to multiple conflict sets for one conflict, it can happen, that we already have redundant information in the
2307 * conflict set
2308 */
2309 for( v = nbdchginfos - 1; v >= 0; --v )
2310 {
2311 var = SCIPbdchginfoGetVar(bdchginfos[v]);
2312 bound = relaxedbds[v];
2313 isupper = (SCIP_Bool) SCIPboundtypeOpposite(SCIPbdchginfoGetBoundtype(bdchginfos[v]));
2314
2315 /* for integral variable we can increase/decrease the conflicting bound */
2316 if( SCIPvarIsIntegral(var) )
2317 bound += (isupper ? -1.0 : +1.0);
2318
2319 /* if conflict variable cannot fulfill the conflict we can remove it */
2320 if( (isupper && SCIPsetIsFeasLT(set, bound, SCIPvarGetLbGlobal(var))) ||
2321 (!isupper && SCIPsetIsFeasGT(set, bound, SCIPvarGetUbGlobal(var))) )
2322 {
2323 SCIPsetDebugMsg(set, "remove redundant variable <%s> from conflict set\n", SCIPvarGetName(var));
2324
2325 bdchginfos[v] = bdchginfos[nbdchginfos - 1];
2326 relaxedbds[v] = relaxedbds[nbdchginfos - 1];
2327 sortvals[v] = sortvals[nbdchginfos - 1];
2328
2329 --nbdchginfos;
2330 ++ntrivialredvars;
2331 }
2332 }
2333 assert(ntrivialredvars + nbdchginfos == conflictset->nbdchginfos);
2334
2335 SCIPsetDebugMsg(set, "trivially removed %d redundant of %d variables from conflictset (%p)\n", ntrivialredvars, conflictset->nbdchginfos, (void*)conflictset);
2336 conflictset->nbdchginfos = nbdchginfos;
2337
2338 /* all variables where removed, the conflict cannot be fulfilled, i.e., we have an infeasibility proof */
2339 if( conflictset->nbdchginfos == 0 )
2340 return SCIP_OKAY;
2341
2342 /* do not check to big or trivial conflicts */
2343 if( conflictset->nbdchginfos > set->conf_maxvarsdetectimpliedbounds || conflictset->nbdchginfos == 1 )
2344 {
2345 *nredvars = ntrivialredvars;
2346 return SCIP_OKAY;
2347 }
2348
2349 /* create array of boundtypes, and bound values in conflict set */
2350 SCIP_CALL( SCIPsetAllocBufferArray(set, &boundtypes, nbdchginfos) );
2351 SCIP_CALL( SCIPsetAllocBufferArray(set, &bounds, nbdchginfos) );
2352 /* memory for the estimates for binary implications used for sorting */
2353 SCIP_CALL( SCIPsetAllocBufferArray(set, &nbinimpls, nbdchginfos) );
2354
2355 nzeroimpls = 0;
2356
2357 /* collect estimates and initialize variables, boundtypes, and bounds array */
2358 for( v = 0; v < nbdchginfos; ++v )
2359 {
2360 var = SCIPbdchginfoGetVar(bdchginfos[v]);
2361 boundtypes[v] = (SCIP_Bool) SCIPboundtypeOpposite(SCIPbdchginfoGetBoundtype(bdchginfos[v]));
2362 bounds[v] = relaxedbds[v];
2363
2364 assert(SCIPvarGetProbindex(var) >= 0);
2365
2366 /* check if the relaxed bound is really a relaxed bound */
2367 assert(SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_LOWER || SCIPsetIsGE(set, relaxedbds[v], SCIPbdchginfoGetNewbound(bdchginfos[v])));
2368 assert(SCIPbdchginfoGetBoundtype(bdchginfos[v]) == SCIP_BOUNDTYPE_UPPER || SCIPsetIsLE(set, relaxedbds[v], SCIPbdchginfoGetNewbound(bdchginfos[v])));
2369
2370 /* for continuous variables, we can only use the relaxed version of the bounds negation: !(x <= u) -> x >= u */
2371 if( SCIPvarIsBinary(var) )
2372 {
2373 if( !boundtypes[v] )
2374 {
2375 assert(SCIPsetIsZero(set, bounds[v]));
2376 bounds[v] = 1.0;
2377 nbinimpls[v] = (SCIP_Longint)SCIPvarGetNCliques(var, TRUE) * 2;
2378 }
2379 else
2380 {
2381 assert(SCIPsetIsEQ(set, bounds[v], 1.0));
2382 bounds[v] = 0.0;
2383 nbinimpls[v] = (SCIP_Longint)SCIPvarGetNCliques(var, FALSE) * 2;
2384 }
2385 }
2386 else if( SCIPvarIsIntegral(var) )
2387 {
2388 assert(SCIPsetIsIntegral(set, bounds[v]));
2389
2390 bounds[v] += ((!boundtypes[v]) ? +1.0 : -1.0);
2391 nbinimpls[v] = (boundtypes[v] ? SCIPvarGetNVlbs(var) : SCIPvarGetNVubs(var));
2392 }
2393 else if( ((!boundtypes[v]) && SCIPsetIsFeasEQ(set, SCIPvarGetLbGlobal(var), bounds[v]))
2394 || ((boundtypes[v]) && SCIPsetIsFeasEQ(set, SCIPvarGetUbGlobal(var), bounds[v])) )
2395 {
2396 /* the literal is satisfied in global bounds (may happen due to weak "negation" of continuous variables)
2397 * -> discard the conflict constraint
2398 */
2399 break;
2400 }
2401 else
2402 {
2403 nbinimpls[v] = (boundtypes[v] ? SCIPvarGetNVlbs(var) : SCIPvarGetNVubs(var));
2404 }
2405
2406 if( nbinimpls[v] == 0 )
2407 ++nzeroimpls;
2408 }
2409
2410 /* starting to derive global bound changes */
2411 if( v == nbdchginfos && ((!set->conf_fullshortenconflict && nzeroimpls < 2) || (set->conf_fullshortenconflict && nzeroimpls < nbdchginfos)) )
2412 {
2413 SCIP_VAR** vars;
2414 SCIP_Bool* redundants;
2415 SCIP_Bool glbinfeas;
2416
2417 /* sort variables in increasing order of binary implications to gain speed later on */
2418 SCIPsortLongPtrRealRealBool(nbinimpls, (void**)bdchginfos, relaxedbds, bounds, boundtypes, v);
2419
2420 SCIPsetDebugMsg(set, "checking for global reductions and redundant conflict variables(in %s) on conflict:\n", SCIPprobGetName(prob));
2421 SCIPsetDebugMsg(set, "[");
2422 for( v = 0; v < nbdchginfos; ++v )
2423 {
2424 SCIPsetDebugMsgPrint(set, "%s %s %g", SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfos[v])), (!boundtypes[v]) ? ">=" : "<=", bounds[v]);
2425 if( v < nbdchginfos - 1 )
2426 SCIPsetDebugMsgPrint(set, ", ");
2427 }
2428 SCIPsetDebugMsgPrint(set, "]\n");
2429
2430 SCIP_CALL( SCIPsetAllocBufferArray(set, &vars, v) );
2431 SCIP_CALL( SCIPsetAllocCleanBufferArray(set, &redundants, v) );
2432
2433 /* initialize conflict variable data */
2434 for( v = 0; v < nbdchginfos; ++v )
2435 vars[v] = SCIPbdchginfoGetVar(bdchginfos[v]);
2436
2437 SCIP_CALL( SCIPshrinkDisjunctiveVarSet(set->scip, vars, bounds, boundtypes, redundants, nbdchginfos, nredvars, \
2438 nbdchgs, redundant, &glbinfeas, set->conf_fullshortenconflict) );
2439
2440 if( glbinfeas )
2441 {
2442 SCIPsetDebugMsg(set, "conflict set (%p) led to global infeasibility\n", (void*) conflictset);
2443
2444 /* clear the memory array before freeing it */
2445 BMSclearMemoryArray(redundants, nbdchginfos);
2446 goto TERMINATE;
2447 }
2448
2449 #ifdef SCIP_DEBUG
2450 if( *nbdchgs > 0 )
2451 {
2452 SCIPsetDebugMsg(set, "conflict set (%p) led to %d global bound reductions\n", (void*) conflictset, *nbdchgs);
2453 }
2454 #endif
2455
2456 /* remove as redundant marked variables */
2457 if( *redundant )
2458 {
2459 SCIPsetDebugMsg(set, "conflict set (%p) is redundant because at least one global reduction, fulfills the conflict constraint\n", (void*)conflictset);
2460
2461 /* clear the memory array before freeing it */
2462 BMSclearMemoryArray(redundants, nbdchginfos);
2463 }
2464 else if( *nredvars > 0 )
2465 {
2466 assert(bdchginfos == conflictset->bdchginfos);
2467 assert(relaxedbds == conflictset->relaxedbds);
2468 assert(sortvals == conflictset->sortvals);
2469
2470 for( v = nbdchginfos - 1; v >= 0; --v )
2471 {
2472 /* if conflict variable was marked to be redundant remove it */
2473 if( redundants[v] )
2474 {
2475 SCIPsetDebugMsg(set, "remove redundant variable <%s> from conflict set\n", SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfos[v])));
2476
2477 bdchginfos[v] = bdchginfos[nbdchginfos - 1];
2478 relaxedbds[v] = relaxedbds[nbdchginfos - 1];
2479 sortvals[v] = sortvals[nbdchginfos - 1];
2480
2481 /* reset redundants[v] to 0 */
2482 redundants[v] = 0;
2483
2484 --nbdchginfos;
2485 }
2486 }
2487 assert((*nredvars) + nbdchginfos == conflictset->nbdchginfos);
2488
2489 SCIPsetDebugMsg(set, "removed %d redundant of %d variables from conflictset (%p)\n", (*nredvars), conflictset->nbdchginfos, (void*)conflictset);
2490 conflictset->nbdchginfos = nbdchginfos;
2491 }
2492 else
2493 {
2494 /* clear the memory array before freeing it */
2495 BMSclearMemoryArray(redundants, nbdchginfos);
2496 }
2497
2498 TERMINATE:
2499 SCIPsetFreeCleanBufferArray(set, &redundants);
2500 SCIPsetFreeBufferArray(set, &vars);
2501 }
2502
2503 /* free temporary memory */
2504 SCIPsetFreeBufferArray(set, &nbinimpls);
2505 SCIPsetFreeBufferArray(set, &bounds);
2506 SCIPsetFreeBufferArray(set, &boundtypes);
2507
2508 *nredvars += ntrivialredvars;
2509
2510 return SCIP_OKAY;
2511 }
2512
2513 /** tighten the bound of a singleton variable in a constraint
2514 *
2515 * if the bound is contradicting with a global bound we cannot tighten the bound directly.
2516 * in this case we need to create and add a constraint of size one such that propagating this constraint will
2517 * enforce the infeasibility.
2518 */
2519 static
tightenSingleVar(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,SCIP_TREE * tree,BMS_BLKMEM * blkmem,SCIP_PROB * origprob,SCIP_PROB * transprob,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_VAR * var,SCIP_Real val,SCIP_Real rhs,SCIP_CONFTYPE prooftype,int validdepth)2520 SCIP_RETCODE tightenSingleVar(
2521 SCIP_CONFLICT* conflict, /**< conflict analysis data */
2522 SCIP_SET* set, /**< global SCIP settings */
2523 SCIP_STAT* stat, /**< dynamic SCIP statistics */
2524 SCIP_TREE* tree, /**< tree data */
2525 BMS_BLKMEM* blkmem, /**< block memory */
2526 SCIP_PROB* origprob, /**< original problem */
2527 SCIP_PROB* transprob, /**< transformed problem */
2528 SCIP_REOPT* reopt, /**< reoptimization data */
2529 SCIP_LP* lp, /**< LP data */
2530 SCIP_BRANCHCAND* branchcand, /**< branching candidates */
2531 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
2532 SCIP_CLIQUETABLE* cliquetable, /**< clique table */
2533 SCIP_VAR* var, /**< problem variable */
2534 SCIP_Real val, /**< coefficient of the variable */
2535 SCIP_Real rhs, /**< rhs of the constraint */
2536 SCIP_CONFTYPE prooftype, /**< type of the proof */
2537 int validdepth /**< depth where the bound change is valid */
2538 )
2539 {
2540 SCIP_Real newbound;
2541 SCIP_Bool applyglobal;
2542 SCIP_BOUNDTYPE boundtype;
2543
2544 assert(tree != NULL);
2545 assert(validdepth >= 0);
2546
2547 applyglobal = (validdepth <= SCIPtreeGetEffectiveRootDepth(tree));
2548
2549 /* if variable and coefficient are integral the rhs can be rounded down */
2550 if( SCIPvarIsIntegral(var) && SCIPsetIsIntegral(set, val) )
2551 newbound = SCIPsetFeasFloor(set, rhs)/val;
2552 else
2553 newbound = rhs/val;
2554
2555 boundtype = (val > 0.0 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER);
2556 SCIPvarAdjustBd(var, set, boundtype, &newbound);
2557
2558 /* skip numerical unstable bound changes */
2559 if( applyglobal
2560 && ((boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsLE(set, newbound, SCIPvarGetLbGlobal(var)))
2561 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsGE(set, newbound, SCIPvarGetUbGlobal(var)))) )
2562 {
2563 return SCIP_OKAY;
2564 }
2565
2566 /* the new bound contradicts a global bound, we can cutoff the root node immediately */
2567 if( applyglobal
2568 && ((boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsGT(set, newbound, SCIPvarGetUbGlobal(var)))
2569 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsLT(set, newbound, SCIPvarGetLbGlobal(var)))) )
2570 {
2571 SCIPsetDebugMsg(set, "detected global infeasibility at var <%s>: locdom=[%g,%g] glbdom=[%g,%g] new %s bound=%g\n",
2572 SCIPvarGetName(var), SCIPvarGetLbLocal(var),
2573 SCIPvarGetUbLocal(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var),
2574 (boundtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper"), newbound);
2575 SCIP_CALL( SCIPnodeCutoff(tree->path[0], set, stat, tree, transprob, origprob, reopt, lp, blkmem) );
2576 }
2577 else
2578 {
2579 if( lp->strongbranching || !applyglobal )
2580 {
2581 SCIP_CONS* cons;
2582 SCIP_Real conslhs;
2583 SCIP_Real consrhs;
2584 char name[SCIP_MAXSTRLEN];
2585
2586 SCIPsetDebugMsg(set, "add constraint <%s>[%c] %s %g to node #%lld in depth %d\n",
2587 SCIPvarGetName(var), varGetChar(var), boundtype == SCIP_BOUNDTYPE_UPPER ? "<=" : ">=", newbound,
2588 SCIPnodeGetNumber(tree->path[validdepth]), validdepth);
2589
2590 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "pc_fix_%s", SCIPvarGetName(var));
2591
2592 if( boundtype == SCIP_BOUNDTYPE_UPPER )
2593 {
2594 conslhs = -SCIPsetInfinity(set);
2595 consrhs = newbound;
2596 }
2597 else
2598 {
2599 conslhs = newbound;
2600 consrhs = SCIPsetInfinity(set);
2601 }
2602
2603 SCIP_CALL( SCIPcreateConsLinear(set->scip, &cons, name, 0, NULL, NULL, conslhs, consrhs,
2604 FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) );
2605
2606 SCIP_CALL( SCIPaddCoefLinear(set->scip, cons, var, 1.0) );
2607
2608 if( applyglobal )
2609 {
2610 SCIP_CALL( SCIPprobAddCons(transprob, set, stat, cons) );
2611 }
2612 else
2613 {
2614 SCIP_CALL( SCIPnodeAddCons(tree->path[validdepth], blkmem, set, stat, tree, cons) );
2615 }
2616
2617 SCIP_CALL( SCIPconsRelease(&cons, blkmem, set) );
2618 }
2619 else
2620 {
2621 assert(applyglobal);
2622
2623 SCIPsetDebugMsg(set, "change global %s bound of <%s>[%c]: %g -> %g\n",
2624 (boundtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper"),
2625 SCIPvarGetName(var), varGetChar(var),
2626 (boundtype == SCIP_BOUNDTYPE_LOWER ? SCIPvarGetLbGlobal(var) : SCIPvarGetUbGlobal(var)),
2627 newbound);
2628
2629 SCIP_CALL( SCIPnodeAddBoundchg(tree->path[0], blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, \
2630 eventqueue, cliquetable, var, newbound, boundtype, FALSE) );
2631
2632 /* mark the node in the validdepth to be propagated again */
2633 SCIPnodePropagateAgain(tree->path[0], set, stat, tree);
2634 }
2635 }
2636
2637 if( applyglobal )
2638 ++conflict->nglbchgbds;
2639 else
2640 ++conflict->nlocchgbds;
2641
2642 if( prooftype == SCIP_CONFTYPE_INFEASLP || prooftype == SCIP_CONFTYPE_ALTINFPROOF )
2643 {
2644 ++conflict->dualproofsinfnnonzeros; /* we count a global bound reduction as size 1 */
2645 ++conflict->ndualproofsinfsuccess;
2646 ++conflict->ninflpsuccess;
2647
2648 if( applyglobal )
2649 ++conflict->ndualproofsinfglobal;
2650 else
2651 ++conflict->ndualproofsinflocal;
2652 }
2653 else
2654 {
2655 ++conflict->dualproofsbndnnonzeros; /* we count a global bound reduction as size 1 */
2656 ++conflict->ndualproofsbndsuccess;
2657 ++conflict->nboundlpsuccess;
2658
2659 if( applyglobal )
2660 ++conflict->ndualproofsbndglobal;
2661 else
2662 ++conflict->ndualproofsbndlocal;
2663 }
2664
2665 return SCIP_OKAY;
2666 }
2667
2668 /** calculates the minimal activity of a given aggregation row */
2669 static
aggrRowGetMinActivity(SCIP_SET * set,SCIP_PROB * transprob,SCIP_AGGRROW * aggrrow,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool * infdelta)2670 SCIP_Real aggrRowGetMinActivity(
2671 SCIP_SET* set, /**< global SCIP settings */
2672 SCIP_PROB* transprob, /**< transformed problem data */
2673 SCIP_AGGRROW* aggrrow, /**< aggregation row */
2674 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables (or NULL for global bounds) */
2675 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables (or NULL for global bounds) */
2676 SCIP_Bool* infdelta /**< pointer to store whether at least one variable contributes with an infinite value */
2677 )
2678 {
2679 SCIP_VAR** vars;
2680 SCIP_Real QUAD(minact);
2681 int* inds;
2682 int nnz;
2683 int i;
2684
2685 vars = SCIPprobGetVars(transprob);
2686 assert(vars != NULL);
2687
2688 nnz = SCIPaggrRowGetNNz(aggrrow);
2689 inds = SCIPaggrRowGetInds(aggrrow);
2690
2691 QUAD_ASSIGN(minact, 0.0);
2692
2693 if( infdelta != NULL )
2694 *infdelta = FALSE;
2695
2696 for( i = 0; i < nnz; i++ )
2697 {
2698 SCIP_Real val;
2699 SCIP_Real QUAD(delta);
2700 int v = inds[i];
2701
2702 assert(SCIPvarGetProbindex(vars[v]) == v);
2703
2704 val = SCIPaggrRowGetProbvarValue(aggrrow, v);
2705
2706 if( val > 0.0 )
2707 {
2708 SCIP_Real bnd = (curvarlbs == NULL ? SCIPvarGetLbGlobal(vars[v]) : curvarlbs[v]);
2709 SCIPquadprecProdDD(delta, val, bnd);
2710 }
2711 else
2712 {
2713 SCIP_Real bnd = (curvarubs == NULL ? SCIPvarGetUbGlobal(vars[v]) : curvarubs[v]);
2714 SCIPquadprecProdDD(delta, val, bnd);
2715 }
2716
2717 /* update minimal activity */
2718 SCIPquadprecSumQQ(minact, minact, delta);
2719
2720 if( infdelta != NULL && SCIPsetIsInfinity(set, REALABS(QUAD_TO_DBL(delta))) )
2721 {
2722 *infdelta = TRUE;
2723 goto TERMINATE;
2724 }
2725 }
2726
2727 TERMINATE:
2728 /* check whether the minimal activity is infinite */
2729 if( SCIPsetIsInfinity(set, QUAD_TO_DBL(minact)) )
2730 return SCIPsetInfinity(set);
2731 if( SCIPsetIsInfinity(set, -QUAD_TO_DBL(minact)) )
2732 return -SCIPsetInfinity(set);
2733
2734 return QUAD_TO_DBL(minact);
2735 }
2736
2737 /** calculates the minimal activity of a given set of bounds and coefficients */
2738 static
getMinActivity(SCIP_SET * set,SCIP_PROB * transprob,SCIP_Real * coefs,int * inds,int nnz,SCIP_Real * curvarlbs,SCIP_Real * curvarubs)2739 SCIP_Real getMinActivity(
2740 SCIP_SET* set, /**< global SCIP settings */
2741 SCIP_PROB* transprob, /**< transformed problem data */
2742 SCIP_Real* coefs, /**< coefficients in sparse representation */
2743 int* inds, /**< non-zero indices */
2744 int nnz, /**< number of non-zero indices */
2745 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables (or NULL for global bounds) */
2746 SCIP_Real* curvarubs /**< current upper bounds of active problem variables (or NULL for global bounds) */
2747 )
2748 {
2749 SCIP_VAR** vars;
2750 SCIP_Real QUAD(minact);
2751 int i;
2752
2753 assert(coefs != NULL);
2754 assert(inds != NULL);
2755
2756 vars = SCIPprobGetVars(transprob);
2757 assert(vars != NULL);
2758
2759 QUAD_ASSIGN(minact, 0.0);
2760
2761 for( i = 0; i < nnz; i++ )
2762 {
2763 SCIP_Real val;
2764 SCIP_Real QUAD(delta);
2765 int v = inds[i];
2766
2767 assert(SCIPvarGetProbindex(vars[v]) == v);
2768
2769 val = coefs[i];
2770
2771 if( val > 0.0 )
2772 {
2773 SCIP_Real bnd;
2774
2775 assert(curvarlbs == NULL || !SCIPsetIsInfinity(set, -curvarlbs[v]));
2776
2777 bnd = (curvarlbs == NULL ? SCIPvarGetLbGlobal(vars[v]) : curvarlbs[v]);
2778 SCIPquadprecProdDD(delta, val, bnd);
2779 }
2780 else
2781 {
2782 SCIP_Real bnd;
2783
2784 assert(curvarubs == NULL || !SCIPsetIsInfinity(set, curvarubs[v]));
2785
2786 bnd = (curvarubs == NULL ? SCIPvarGetUbGlobal(vars[v]) : curvarubs[v]);
2787 SCIPquadprecProdDD(delta, val, bnd);
2788 }
2789
2790 /* update minimal activity */
2791 SCIPquadprecSumQQ(minact, minact, delta);
2792 }
2793
2794 /* check whether the minmal activity is infinite */
2795 if( SCIPsetIsInfinity(set, QUAD_TO_DBL(minact)) )
2796 return SCIPsetInfinity(set);
2797 if( SCIPsetIsInfinity(set, -QUAD_TO_DBL(minact)) )
2798 return -SCIPsetInfinity(set);
2799
2800 return QUAD_TO_DBL(minact);
2801 }
2802
2803 /** calculates the minimal activity of a given set of bounds and coefficients */
2804 static
getMaxActivity(SCIP_SET * set,SCIP_PROB * transprob,SCIP_Real * coefs,int * inds,int nnz,SCIP_Real * curvarlbs,SCIP_Real * curvarubs)2805 SCIP_Real getMaxActivity(
2806 SCIP_SET* set, /**< global SCIP settings */
2807 SCIP_PROB* transprob, /**< transformed problem data */
2808 SCIP_Real* coefs, /**< coefficients in sparse representation */
2809 int* inds, /**< non-zero indices */
2810 int nnz, /**< number of non-zero indices */
2811 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables (or NULL for global bounds) */
2812 SCIP_Real* curvarubs /**< current upper bounds of active problem variables (or NULL for global bounds) */
2813 )
2814 {
2815 SCIP_VAR** vars;
2816 SCIP_Real QUAD(maxact);
2817 int i;
2818
2819 assert(coefs != NULL);
2820 assert(inds != NULL);
2821
2822 vars = SCIPprobGetVars(transprob);
2823 assert(vars != NULL);
2824
2825 QUAD_ASSIGN(maxact, 0.0);
2826
2827 for( i = 0; i < nnz; i++ )
2828 {
2829 SCIP_Real val;
2830 SCIP_Real QUAD(delta);
2831 int v = inds[i];
2832
2833 assert(SCIPvarGetProbindex(vars[v]) == v);
2834
2835 val = coefs[i];
2836
2837 if( val < 0.0 )
2838 {
2839 SCIP_Real bnd;
2840
2841 assert(curvarlbs == NULL || !SCIPsetIsInfinity(set, -curvarlbs[v]));
2842
2843 bnd = (curvarlbs == NULL ? SCIPvarGetLbGlobal(vars[v]) : curvarlbs[v]);
2844 SCIPquadprecProdDD(delta, val, bnd);
2845 }
2846 else
2847 {
2848 SCIP_Real bnd;
2849
2850 assert(curvarubs == NULL || !SCIPsetIsInfinity(set, curvarubs[v]));
2851
2852 bnd = (curvarubs == NULL ? SCIPvarGetUbGlobal(vars[v]) : curvarubs[v]);
2853 SCIPquadprecProdDD(delta, val, bnd);
2854 }
2855
2856 /* update maximal activity */
2857 SCIPquadprecSumQQ(maxact, maxact, delta);
2858 }
2859
2860 /* check whether the maximal activity got infinite */
2861 if( SCIPsetIsInfinity(set, QUAD_TO_DBL(maxact)) )
2862 return SCIPsetInfinity(set);
2863 if( SCIPsetIsInfinity(set, -QUAD_TO_DBL(maxact)) )
2864 return -SCIPsetInfinity(set);
2865
2866 return QUAD_TO_DBL(maxact);
2867 }
2868
2869 static
propagateLongProof(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,SCIP_REOPT * reopt,SCIP_TREE * tree,BMS_BLKMEM * blkmem,SCIP_PROB * origprob,SCIP_PROB * transprob,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real * coefs,int * inds,int nnz,SCIP_Real rhs,SCIP_CONFTYPE conflicttype,int validdepth)2870 SCIP_RETCODE propagateLongProof(
2871 SCIP_CONFLICT* conflict, /**< conflict analysis data */
2872 SCIP_SET* set, /**< global SCIP settings */
2873 SCIP_STAT* stat, /**< dynamic SCIP statistics */
2874 SCIP_REOPT* reopt, /**< reoptimization data */
2875 SCIP_TREE* tree, /**< tree data */
2876 BMS_BLKMEM* blkmem, /**< block memory */
2877 SCIP_PROB* origprob, /**< original problem */
2878 SCIP_PROB* transprob, /**< transformed problem */
2879 SCIP_LP* lp, /**< LP data */
2880 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
2881 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
2882 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
2883 SCIP_Real* coefs, /**< coefficients in sparse representation */
2884 int* inds, /**< non-zero indices */
2885 int nnz, /**< number of non-zero indices */
2886 SCIP_Real rhs, /**< right-hand side */
2887 SCIP_CONFTYPE conflicttype, /**< type of the conflict */
2888 int validdepth /**< depth where the proof is valid */
2889 )
2890 {
2891 SCIP_VAR** vars;
2892 SCIP_Real minact;
2893 int i;
2894
2895 assert(coefs != NULL);
2896 assert(inds != NULL);
2897 assert(nnz >= 0);
2898
2899 vars = SCIPprobGetVars(transprob);
2900 minact = getMinActivity(set, transprob, coefs, inds, nnz, NULL, NULL);
2901
2902 /* we cannot find global tightenings */
2903 if( SCIPsetIsInfinity(set, -minact) )
2904 return SCIP_OKAY;
2905
2906 for( i = 0; i < nnz; i++ )
2907 {
2908 SCIP_VAR* var;
2909 SCIP_Real val;
2910 SCIP_Real resminact;
2911 SCIP_Real lb;
2912 SCIP_Real ub;
2913 int pos;
2914
2915 pos = inds[i];
2916 val = coefs[i];
2917 var = vars[pos];
2918 lb = SCIPvarGetLbGlobal(var);
2919 ub = SCIPvarGetUbGlobal(var);
2920
2921 assert(!SCIPsetIsZero(set, val));
2922
2923 resminact = minact;
2924
2925 /* we got a potential new upper bound */
2926 if( val > 0.0 )
2927 {
2928 SCIP_Real newub;
2929
2930 resminact -= (val * lb);
2931 newub = (rhs - resminact)/val;
2932
2933 if( SCIPsetIsInfinity(set, newub) )
2934 continue;
2935
2936 /* we cannot tighten the upper bound */
2937 if( SCIPsetIsGE(set, newub, ub) )
2938 continue;
2939
2940 SCIP_CALL( tightenSingleVar(conflict, set, stat, tree, blkmem, origprob, transprob, reopt, lp, branchcand, \
2941 eventqueue, cliquetable, var, val, rhs-resminact, conflicttype, validdepth) );
2942 }
2943 /* we got a potential new lower bound */
2944 else
2945 {
2946 SCIP_Real newlb;
2947
2948 resminact -= (val * ub);
2949 newlb = (rhs - resminact)/val;
2950
2951 if( SCIPsetIsInfinity(set, -newlb) )
2952 continue;
2953
2954 /* we cannot tighten the lower bound */
2955 if( SCIPsetIsLE(set, newlb, lb) )
2956 continue;
2957
2958 SCIP_CALL( tightenSingleVar(conflict, set, stat, tree, blkmem, origprob, transprob, reopt, lp, branchcand, \
2959 eventqueue, cliquetable, var, val, rhs-resminact, conflicttype, validdepth) );
2960 }
2961
2962 /* the minimal activity should stay unchanged because we tightened the bound that doesn't contribute to the
2963 * minimal activity
2964 */
2965 assert(SCIPsetIsEQ(set, minact, getMinActivity(set, transprob, coefs, inds, nnz, NULL, NULL)));
2966 }
2967
2968 return SCIP_OKAY;
2969 }
2970
2971
2972 /** creates a proof constraint and tries to add it to the storage */
2973 static
createAndAddProofcons(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,SCIP_PROOFSET * proofset,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * origprob,SCIP_PROB * transprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,BMS_BLKMEM * blkmem)2974 SCIP_RETCODE createAndAddProofcons(
2975 SCIP_CONFLICT* conflict, /**< conflict analysis data */
2976 SCIP_CONFLICTSTORE* conflictstore, /**< conflict pool data */
2977 SCIP_PROOFSET* proofset, /**< proof set */
2978 SCIP_SET* set, /**< global SCIP settings */
2979 SCIP_STAT* stat, /**< dynamic SCIP statistics */
2980 SCIP_PROB* origprob, /**< original problem */
2981 SCIP_PROB* transprob, /**< transformed problem */
2982 SCIP_TREE* tree, /**< tree data */
2983 SCIP_REOPT* reopt, /**< reoptimization data */
2984 SCIP_LP* lp, /**< LP data */
2985 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
2986 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
2987 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
2988 BMS_BLKMEM* blkmem /**< block memory */
2989 )
2990 {
2991 SCIP_CONS* cons;
2992 SCIP_CONS* upgdcons;
2993 SCIP_VAR** vars;
2994 SCIP_Real* coefs;
2995 int* inds;
2996 SCIP_Real rhs;
2997 SCIP_Real fillin;
2998 SCIP_Real globalminactivity;
2999 SCIP_Bool applyglobal;
3000 SCIP_Bool toolong;
3001 SCIP_Bool contonly;
3002 SCIP_Bool hasrelaxvar;
3003 SCIP_CONFTYPE conflicttype;
3004 char name[SCIP_MAXSTRLEN];
3005 int nnz;
3006 int i;
3007
3008 assert(conflict != NULL);
3009 assert(conflictstore != NULL);
3010 assert(proofset != NULL);
3011 assert(proofset->validdepth == 0 || proofset->validdepth < SCIPtreeGetFocusDepth(tree));
3012
3013 nnz = proofsetGetNVars(proofset);
3014
3015 if( nnz == 0 )
3016 return SCIP_OKAY;
3017
3018 vars = SCIPprobGetVars(transprob);
3019
3020 rhs = proofsetGetRhs(proofset);
3021 assert(!SCIPsetIsInfinity(set, rhs));
3022
3023 coefs = proofsetGetVals(proofset);
3024 assert(coefs != NULL);
3025
3026 inds = proofsetGetInds(proofset);
3027 assert(inds != NULL);
3028
3029 conflicttype = proofsetGetConftype(proofset);
3030
3031 applyglobal = (proofset->validdepth <= SCIPtreeGetEffectiveRootDepth(tree));
3032
3033 if( applyglobal )
3034 {
3035 SCIP_Real globalmaxactivity = getMaxActivity(set, transprob, coefs, inds, nnz, NULL, NULL);
3036
3037 /* check whether the alternative proof is redundant */
3038 if( SCIPsetIsLE(set, globalmaxactivity, rhs) )
3039 return SCIP_OKAY;
3040
3041 /* check whether the constraint proves global infeasibility */
3042 globalminactivity = getMinActivity(set, transprob, coefs, inds, nnz, NULL, NULL);
3043 if( SCIPsetIsGT(set, globalminactivity, rhs) )
3044 {
3045 SCIPsetDebugMsg(set, "detect global infeasibility: minactivity=%g, rhs=%g\n", globalminactivity, rhs);
3046
3047 SCIP_CALL( SCIPnodeCutoff(tree->path[proofset->validdepth], set, stat, tree, transprob, origprob, reopt, lp, blkmem) );
3048
3049 goto UPDATESTATISTICS;
3050 }
3051 }
3052
3053 if( set->conf_minmaxvars >= nnz )
3054 toolong = FALSE;
3055 else
3056 {
3057 SCIP_Real maxnnz;
3058
3059 if( transprob->startnconss < 100 )
3060 maxnnz = 0.85 * transprob->nvars;
3061 else
3062 maxnnz = (SCIP_Real)transprob->nvars;
3063
3064 fillin = nnz;
3065 if( conflicttype == SCIP_CONFTYPE_INFEASLP || conflicttype == SCIP_CONFTYPE_ALTINFPROOF )
3066 {
3067 fillin += SCIPconflictstoreGetNDualInfProofs(conflictstore) * SCIPconflictstoreGetAvgNnzDualInfProofs(conflictstore);
3068 fillin /= (SCIPconflictstoreGetNDualInfProofs(conflictstore) + 1.0);
3069 toolong = (fillin > MIN(2.0 * stat->avgnnz, maxnnz));
3070 }
3071 else
3072 {
3073 assert(conflicttype == SCIP_CONFTYPE_BNDEXCEEDING || conflicttype == SCIP_CONFTYPE_ALTBNDPROOF);
3074
3075 fillin += SCIPconflictstoreGetNDualBndProofs(conflictstore) * SCIPconflictstoreGetAvgNnzDualBndProofs(conflictstore);
3076 fillin /= (SCIPconflictstoreGetNDualBndProofs(conflictstore) + 1.0);
3077 toolong = (fillin > MIN(1.5 * stat->avgnnz, maxnnz));
3078 }
3079
3080 toolong = (toolong && (nnz > set->conf_maxvarsfac * transprob->nvars));
3081 }
3082
3083 /* don't store global dual proofs that are too long / have too many non-zeros */
3084 if( toolong )
3085 {
3086 if( applyglobal )
3087 {
3088 SCIP_CALL( propagateLongProof(conflict, set, stat, reopt, tree, blkmem, origprob, transprob, lp, branchcand,
3089 eventqueue, cliquetable, coefs, inds, nnz, rhs, conflicttype, proofset->validdepth) );
3090 }
3091 return SCIP_OKAY;
3092 }
3093
3094 /* check if conflict contains variables that are invalid after a restart to label it appropriately */
3095 hasrelaxvar = FALSE;
3096 contonly = TRUE;
3097 for( i = 0; i < nnz && (!hasrelaxvar || contonly); ++i )
3098 {
3099 hasrelaxvar |= SCIPvarIsRelaxationOnly(vars[inds[i]]);
3100
3101 if( SCIPvarIsIntegral(vars[inds[i]]) )
3102 contonly = FALSE;
3103 }
3104
3105 if( !applyglobal && contonly )
3106 return SCIP_OKAY;
3107
3108 if( conflicttype == SCIP_CONFTYPE_INFEASLP || conflicttype == SCIP_CONFTYPE_ALTINFPROOF )
3109 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "dualproof_inf_%" SCIP_LONGINT_FORMAT, conflict->ndualproofsinfsuccess);
3110 else if( conflicttype == SCIP_CONFTYPE_BNDEXCEEDING || conflicttype == SCIP_CONFTYPE_ALTBNDPROOF )
3111 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "dualproof_bnd_%" SCIP_LONGINT_FORMAT, conflict->ndualproofsbndsuccess);
3112 else
3113 return SCIP_INVALIDCALL;
3114
3115 SCIP_CALL( SCIPcreateConsLinear(set->scip, &cons, name, 0, NULL, NULL, -SCIPsetInfinity(set), rhs,
3116 FALSE, FALSE, FALSE, FALSE, TRUE, !applyglobal,
3117 FALSE, TRUE, TRUE, FALSE) );
3118
3119 for( i = 0; i < nnz; i++ )
3120 {
3121 int v = inds[i];
3122 SCIP_CALL( SCIPaddCoefLinear(set->scip, cons, vars[v], coefs[i]) );
3123 }
3124
3125 /* do not upgrade linear constraints of size 1 */
3126 if( nnz > 1 )
3127 {
3128 upgdcons = NULL;
3129 /* try to automatically convert a linear constraint into a more specific and more specialized constraint */
3130 SCIP_CALL( SCIPupgradeConsLinear(set->scip, cons, &upgdcons) );
3131 if( upgdcons != NULL )
3132 {
3133 SCIP_CALL( SCIPreleaseCons(set->scip, &cons) );
3134 cons = upgdcons;
3135
3136 if( conflicttype == SCIP_CONFTYPE_INFEASLP )
3137 conflicttype = SCIP_CONFTYPE_ALTINFPROOF;
3138 else if( conflicttype == SCIP_CONFTYPE_BNDEXCEEDING )
3139 conflicttype = SCIP_CONFTYPE_ALTBNDPROOF;
3140 }
3141 }
3142
3143 /* mark constraint to be a conflict */
3144 SCIPconsMarkConflict(cons);
3145
3146 /* add constraint to storage */
3147 if( conflicttype == SCIP_CONFTYPE_INFEASLP || conflicttype == SCIP_CONFTYPE_ALTINFPROOF )
3148 {
3149 /* add dual proof to storage */
3150 SCIP_CALL( SCIPconflictstoreAddDualraycons(conflictstore, cons, blkmem, set, stat, transprob, reopt, hasrelaxvar) );
3151 }
3152 else
3153 {
3154 SCIP_Real scale = 1.0;
3155 SCIP_Bool updateside = FALSE;
3156
3157 /* In some cases the constraint could not be updated to a more special type. However, it is possible that
3158 * constraint got scaled. Therefore, we need to be very careful when updating the lhs/rhs after the incumbent
3159 * solution has improved.
3160 */
3161 if( conflicttype == SCIP_CONFTYPE_BNDEXCEEDING )
3162 {
3163 SCIP_Real side;
3164
3165 #ifndef NDEBUG
3166 SCIP_CONSHDLR* conshdlr = SCIPconsGetHdlr(cons);
3167
3168 assert(conshdlr != NULL);
3169 assert(strcmp(SCIPconshdlrGetName(conshdlr), "linear") == 0);
3170 #endif
3171 side = SCIPgetLhsLinear(set->scip, cons);
3172
3173 if( !SCIPsetIsInfinity(set, -side) )
3174 {
3175 if( SCIPsetIsZero(set, side) )
3176 {
3177 scale = -1.0;
3178 }
3179 else
3180 {
3181 scale = proofsetGetRhs(proofset) / side;
3182 assert(SCIPsetIsNegative(set, scale));
3183 }
3184 }
3185 else
3186 {
3187 side = SCIPgetRhsLinear(set->scip, cons);
3188 assert(!SCIPsetIsInfinity(set, side));
3189
3190 if( SCIPsetIsZero(set, side) )
3191 {
3192 scale = 1.0;
3193 }
3194 else
3195 {
3196 scale = proofsetGetRhs(proofset) / side;
3197 assert(SCIPsetIsPositive(set, scale));
3198 }
3199 }
3200 updateside = TRUE;
3201 }
3202
3203 /* add dual proof to storage */
3204 SCIP_CALL( SCIPconflictstoreAddDualsolcons(conflictstore, cons, blkmem, set, stat, transprob, reopt, scale, updateside, hasrelaxvar) );
3205 }
3206
3207 if( applyglobal ) /*lint !e774*/
3208 {
3209 /* add the constraint to the global problem */
3210 SCIP_CALL( SCIPprobAddCons(transprob, set, stat, cons) );
3211 }
3212 else
3213 {
3214 SCIP_CALL( SCIPnodeAddCons(tree->path[proofset->validdepth], blkmem, set, stat, tree, cons) );
3215 }
3216
3217 SCIPsetDebugMsg(set, "added proof-constraint to node %p (#%lld) in depth %d (nproofconss %d)\n",
3218 (void*)tree->path[proofset->validdepth], SCIPnodeGetNumber(tree->path[proofset->validdepth]),
3219 proofset->validdepth,
3220 (conflicttype == SCIP_CONFTYPE_INFEASLP || conflicttype == SCIP_CONFTYPE_ALTINFPROOF)
3221 ? SCIPconflictstoreGetNDualInfProofs(conflictstore) : SCIPconflictstoreGetNDualBndProofs(conflictstore));
3222
3223 /* release the constraint */
3224 SCIP_CALL( SCIPreleaseCons(set->scip, &cons) );
3225
3226 UPDATESTATISTICS:
3227 /* update statistics */
3228 if( conflicttype == SCIP_CONFTYPE_INFEASLP || conflicttype == SCIP_CONFTYPE_ALTINFPROOF )
3229 {
3230 conflict->dualproofsinfnnonzeros += nnz;
3231 if( applyglobal ) /*lint !e774*/
3232 ++conflict->ndualproofsinfglobal;
3233 else
3234 ++conflict->ndualproofsinflocal;
3235 ++conflict->ndualproofsinfsuccess;
3236 }
3237 else
3238 {
3239 assert(conflicttype == SCIP_CONFTYPE_BNDEXCEEDING || conflicttype == SCIP_CONFTYPE_ALTBNDPROOF);
3240 conflict->dualproofsbndnnonzeros += nnz;
3241 if( applyglobal ) /*lint !e774*/
3242 ++conflict->ndualproofsbndglobal;
3243 else
3244 ++conflict->ndualproofsbndlocal;
3245
3246 ++conflict->ndualproofsbndsuccess;
3247 }
3248 return SCIP_OKAY;
3249 }
3250
3251 /* create proof constraints out of proof sets */
3252 static
conflictFlushProofset(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable)3253 SCIP_RETCODE conflictFlushProofset(
3254 SCIP_CONFLICT* conflict, /**< conflict analysis data */
3255 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
3256 BMS_BLKMEM* blkmem, /**< block memory */
3257 SCIP_SET* set, /**< global SCIP settings */
3258 SCIP_STAT* stat, /**< dynamic problem statistics */
3259 SCIP_PROB* transprob, /**< transformed problem after presolve */
3260 SCIP_PROB* origprob, /**< original problem */
3261 SCIP_TREE* tree, /**< branch and bound tree */
3262 SCIP_REOPT* reopt, /**< reoptimization data structure */
3263 SCIP_LP* lp, /**< current LP data */
3264 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
3265 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3266 SCIP_CLIQUETABLE* cliquetable /**< clique table data structure */
3267 )
3268 {
3269 assert(conflict != NULL);
3270
3271 if( proofsetGetConftype(conflict->proofset) != SCIP_CONFTYPE_UNKNOWN )
3272 {
3273 /* only one variable has a coefficient different to zero, we add this bound change instead of a constraint */
3274 if( proofsetGetNVars(conflict->proofset) == 1 )
3275 {
3276 SCIP_VAR** vars;
3277 SCIP_Real* coefs;
3278 int* inds;
3279 SCIP_Real rhs;
3280
3281 vars = SCIPprobGetVars(transprob);
3282
3283 coefs = proofsetGetVals(conflict->proofset);
3284 inds = proofsetGetInds(conflict->proofset);
3285 rhs = proofsetGetRhs(conflict->proofset);
3286
3287 SCIP_CALL( tightenSingleVar(conflict, set, stat, tree, blkmem, origprob, transprob, reopt, lp, \
3288 branchcand, eventqueue, cliquetable, vars[inds[0]], coefs[0], rhs, conflict->proofset->conflicttype,
3289 conflict->proofset->validdepth) );
3290 }
3291 else
3292 {
3293 SCIP_Bool skipinitialproof = FALSE;
3294
3295 /* prefer an infeasibility proof
3296 *
3297 * todo: check whether this is really what we want
3298 */
3299 if( set->conf_prefinfproof && proofsetGetConftype(conflict->proofset) == SCIP_CONFTYPE_BNDEXCEEDING )
3300 {
3301 int i;
3302
3303 for( i = 0; i < conflict->nproofsets; i++ )
3304 {
3305 if( proofsetGetConftype(conflict->proofsets[i]) == SCIP_CONFTYPE_INFEASLP )
3306 {
3307 skipinitialproof = TRUE;
3308 break;
3309 }
3310 }
3311 }
3312
3313 if( !skipinitialproof )
3314 {
3315 /* create and add the original proof */
3316 SCIP_CALL( createAndAddProofcons(conflict, conflictstore, conflict->proofset, set, stat, origprob, transprob, \
3317 tree, reopt, lp, branchcand, eventqueue, cliquetable, blkmem) );
3318 }
3319 }
3320
3321 /* clear the proof set anyway */
3322 proofsetClear(conflict->proofset);
3323 }
3324
3325 if( conflict->nproofsets > 0 )
3326 {
3327 int i;
3328
3329 for( i = 0; i < conflict->nproofsets; i++ )
3330 {
3331 assert(conflict->proofsets[i] != NULL);
3332 assert(proofsetGetConftype(conflict->proofsets[i]) != SCIP_CONFTYPE_UNKNOWN);
3333
3334 /* only one variable has a coefficient different to zero, we add this bound change instead of a constraint */
3335 if( proofsetGetNVars(conflict->proofsets[i]) == 1 )
3336 {
3337 SCIP_VAR** vars;
3338 SCIP_Real* coefs;
3339 int* inds;
3340 SCIP_Real rhs;
3341
3342 vars = SCIPprobGetVars(transprob);
3343
3344 coefs = proofsetGetVals(conflict->proofsets[i]);
3345 inds = proofsetGetInds(conflict->proofsets[i]);
3346 rhs = proofsetGetRhs(conflict->proofsets[i]);
3347
3348 SCIP_CALL( tightenSingleVar(conflict, set, stat, tree, blkmem, origprob, transprob, reopt, lp,
3349 branchcand, eventqueue, cliquetable, vars[inds[0]], coefs[0], rhs,
3350 conflict->proofsets[i]->conflicttype, conflict->proofsets[i]->validdepth) );
3351 }
3352 else
3353 {
3354 /* create and add proof constraint */
3355 SCIP_CALL( createAndAddProofcons(conflict, conflictstore, conflict->proofsets[i], set, stat, origprob, \
3356 transprob, tree, reopt, lp, branchcand, eventqueue, cliquetable, blkmem) );
3357 }
3358 }
3359
3360 /* free all proofsets */
3361 for( i = 0; i < conflict->nproofsets; i++ )
3362 proofsetFree(&conflict->proofsets[i], blkmem);
3363
3364 conflict->nproofsets = 0;
3365 }
3366
3367 return SCIP_OKAY;
3368 }
3369
3370 /** adds the given conflict set as conflict constraint to the problem */
3371 static
conflictAddConflictCons(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_CONFLICTSET * conflictset,int insertdepth,SCIP_Bool * success)3372 SCIP_RETCODE conflictAddConflictCons(
3373 SCIP_CONFLICT* conflict, /**< conflict analysis data */
3374 BMS_BLKMEM* blkmem, /**< block memory */
3375 SCIP_SET* set, /**< global SCIP settings */
3376 SCIP_STAT* stat, /**< dynamic problem statistics */
3377 SCIP_PROB* transprob, /**< transformed problem after presolve */
3378 SCIP_PROB* origprob, /**< original problem */
3379 SCIP_TREE* tree, /**< branch and bound tree */
3380 SCIP_REOPT* reopt, /**< reoptimization data structure */
3381 SCIP_LP* lp, /**< current LP data */
3382 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
3383 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3384 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
3385 SCIP_CONFLICTSET* conflictset, /**< conflict set to add to the tree */
3386 int insertdepth, /**< depth level at which the conflict set should be added */
3387 SCIP_Bool* success /**< pointer to store whether the addition was successful */
3388 )
3389 {
3390 SCIP_Bool redundant;
3391 int h;
3392
3393 assert(conflict != NULL);
3394 assert(tree != NULL);
3395 assert(tree->path != NULL);
3396 assert(conflictset != NULL);
3397 assert(conflictset->validdepth <= insertdepth);
3398 assert(success != NULL);
3399
3400 *success = FALSE;
3401 redundant = FALSE;
3402
3403 /* try to derive global bound changes and shorten the conflictset by using implication and clique and variable bound
3404 * information
3405 */
3406 if( conflictset->nbdchginfos > 1 && insertdepth == 0 && !lp->strongbranching )
3407 {
3408 int nbdchgs;
3409 int nredvars;
3410 #ifdef SCIP_DEBUG
3411 int oldnbdchginfos = conflictset->nbdchginfos;
3412 #endif
3413 assert(conflictset->validdepth == 0);
3414
3415 /* check conflict set on debugging solution */
3416 SCIP_CALL( SCIPdebugCheckConflict(blkmem, set, tree->root, conflictset->bdchginfos, conflictset->relaxedbds, conflictset->nbdchginfos) );
3417
3418 SCIPclockStart(conflict->dIBclock, set);
3419
3420 /* find global bound changes which can be derived from the new conflict set */
3421 SCIP_CALL( detectImpliedBounds(set, transprob, conflictset, &nbdchgs, &nredvars, &redundant) );
3422
3423 /* all variables where removed, we have an infeasibility proof */
3424 if( conflictset->nbdchginfos == 0 )
3425 return SCIP_OKAY;
3426
3427 /* debug check for reduced conflict set */
3428 if( nredvars > 0 )
3429 {
3430 /* check conflict set on debugging solution */
3431 SCIP_CALL( SCIPdebugCheckConflict(blkmem, set, tree->root, conflictset->bdchginfos, conflictset->relaxedbds, conflictset->nbdchginfos) ); /*lint !e506 !e774*/
3432 }
3433
3434 #ifdef SCIP_DEBUG
3435 SCIPsetDebugMsg(set, " -> conflict set removed %d redundant variables (old nvars %d, new nvars = %d)\n", nredvars, oldnbdchginfos, conflictset->nbdchginfos);
3436 SCIPsetDebugMsg(set, " -> conflict set led to %d global bound changes %s(cdpt:%d, fdpt:%d, confdpt:%d, len:%d):\n",
3437 nbdchgs, redundant ? "(conflict became redundant) " : "", SCIPtreeGetCurrentDepth(tree), SCIPtreeGetFocusDepth(tree),
3438 conflictset->conflictdepth, conflictset->nbdchginfos);
3439 conflictsetPrint(conflictset);
3440 #endif
3441
3442 SCIPclockStop(conflict->dIBclock, set);
3443
3444 if( redundant )
3445 {
3446 if( nbdchgs > 0 )
3447 *success = TRUE;
3448
3449 return SCIP_OKAY;
3450 }
3451 }
3452
3453 /* in case the conflict set contains only one bound change which is globally valid we apply that bound change
3454 * directly (except if we are in strong branching or diving - in this case a bound change would yield an unflushed LP
3455 * and is not handled when restoring the information)
3456 *
3457 * @note A bound change can only be applied if it is are related to the active node or if is a global bound
3458 * change. Bound changes which are related to any other node cannot be handled at point due to the internal
3459 * data structure
3460 */
3461 if( conflictset->nbdchginfos == 1 && insertdepth == 0 && !lp->strongbranching && !lp->diving )
3462 {
3463 SCIP_VAR* var;
3464 SCIP_Real bound;
3465 SCIP_BOUNDTYPE boundtype;
3466
3467 var = conflictset->bdchginfos[0]->var;
3468 assert(var != NULL);
3469
3470 boundtype = SCIPboundtypeOpposite((SCIP_BOUNDTYPE) conflictset->bdchginfos[0]->boundtype);
3471 bound = conflictset->relaxedbds[0];
3472
3473 /* for continuous variables, we can only use the relaxed version of the bounds negation: !(x <= u) -> x >= u */
3474 if( SCIPvarIsIntegral(var) )
3475 {
3476 assert(SCIPsetIsIntegral(set, bound));
3477 bound += (boundtype == SCIP_BOUNDTYPE_LOWER ? +1.0 : -1.0);
3478 }
3479
3480 SCIPsetDebugMsg(set, " -> apply global bound change: <%s> %s %g\n",
3481 SCIPvarGetName(var), boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", bound);
3482
3483 SCIP_CALL( SCIPnodeAddBoundchg(tree->path[conflictset->validdepth], blkmem, set, stat, transprob, origprob, tree,
3484 reopt, lp, branchcand, eventqueue, cliquetable, var, bound, boundtype, FALSE) );
3485
3486 *success = TRUE;
3487 SCIP_CALL( updateStatistics(conflict, blkmem, set, stat, conflictset, insertdepth) );
3488 }
3489 else if( !conflictset->hasrelaxonlyvar )
3490 {
3491 /* sort conflict handlers by priority */
3492 SCIPsetSortConflicthdlrs(set);
3493
3494 /* call conflict handlers to create a conflict constraint */
3495 for( h = 0; h < set->nconflicthdlrs; ++h )
3496 {
3497 SCIP_RESULT result;
3498
3499 assert(conflictset->conflicttype != SCIP_CONFTYPE_UNKNOWN);
3500
3501 SCIP_CALL( SCIPconflicthdlrExec(set->conflicthdlrs[h], set, tree->path[insertdepth],
3502 tree->path[conflictset->validdepth], conflictset->bdchginfos, conflictset->relaxedbds,
3503 conflictset->nbdchginfos, conflictset->conflicttype, conflictset->usescutoffbound, *success, &result) );
3504 if( result == SCIP_CONSADDED )
3505 {
3506 *success = TRUE;
3507 SCIP_CALL( updateStatistics(conflict, blkmem, set, stat, conflictset, insertdepth) );
3508 }
3509
3510 SCIPsetDebugMsg(set, " -> call conflict handler <%s> (prio=%d) to create conflict set with %d bounds returned result %d\n",
3511 SCIPconflicthdlrGetName(set->conflicthdlrs[h]), SCIPconflicthdlrGetPriority(set->conflicthdlrs[h]),
3512 conflictset->nbdchginfos, result);
3513 }
3514 }
3515 else
3516 {
3517 SCIPsetDebugMsg(set, " -> skip conflict set with relaxation-only variable\n");
3518 /* TODO would be nice to still create a constraint?, if we can make sure that we the constraint does not survive a restart */
3519 }
3520
3521 return SCIP_OKAY;
3522 }
3523
3524 /** adds the collected conflict constraints to the corresponding nodes; the best set->conf_maxconss conflict constraints
3525 * are added to the node of their validdepth; additionally (if not yet added, and if repropagation is activated), the
3526 * conflict constraint that triggers the earliest repropagation is added to the node of its validdepth
3527 */
SCIPconflictFlushConss(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable)3528 SCIP_RETCODE SCIPconflictFlushConss(
3529 SCIP_CONFLICT* conflict, /**< conflict analysis data */
3530 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3531 SCIP_SET* set, /**< global SCIP settings */
3532 SCIP_STAT* stat, /**< dynamic problem statistics */
3533 SCIP_PROB* transprob, /**< transformed problem */
3534 SCIP_PROB* origprob, /**< original problem */
3535 SCIP_TREE* tree, /**< branch and bound tree */
3536 SCIP_REOPT* reopt, /**< reoptimization data structure */
3537 SCIP_LP* lp, /**< current LP data */
3538 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
3539 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3540 SCIP_CLIQUETABLE* cliquetable /**< clique table data structure */
3541 )
3542 {
3543 assert(conflict != NULL);
3544 assert(set != NULL);
3545 assert(stat != NULL);
3546 assert(transprob != NULL);
3547 assert(tree != NULL);
3548
3549 /* is there anything to do? */
3550 if( conflict->nconflictsets > 0 )
3551 {
3552 SCIP_CONFLICTSET* repropconflictset;
3553 int nconflictsetsused;
3554 int focusdepth;
3555 #ifndef NDEBUG
3556 int currentdepth;
3557 #endif
3558 int cutoffdepth;
3559 int repropdepth;
3560 int maxconflictsets;
3561 int maxsize;
3562 int i;
3563
3564 /* calculate the maximal number of conflict sets to accept, and the maximal size of each accepted conflict set */
3565 maxconflictsets = (set->conf_maxconss == -1 ? INT_MAX : set->conf_maxconss);
3566 maxsize = conflictCalcMaxsize(set, transprob);
3567
3568 focusdepth = SCIPtreeGetFocusDepth(tree);
3569 #ifndef NDEBUG
3570 currentdepth = SCIPtreeGetCurrentDepth(tree);
3571 assert(focusdepth <= currentdepth);
3572 assert(currentdepth == tree->pathlen-1);
3573 #endif
3574
3575 SCIPsetDebugMsg(set, "flushing %d conflict sets at focus depth %d (maxconflictsets: %d, maxsize: %d)\n",
3576 conflict->nconflictsets, focusdepth, maxconflictsets, maxsize);
3577
3578 /* mark the focus node to have produced conflict sets in the visualization output */
3579 SCIPvisualFoundConflict(stat->visual, stat, tree->path[focusdepth]);
3580
3581 /* insert the conflict sets at the corresponding nodes */
3582 nconflictsetsused = 0;
3583 cutoffdepth = INT_MAX;
3584 repropdepth = INT_MAX;
3585 repropconflictset = NULL;
3586 for( i = 0; i < conflict->nconflictsets && nconflictsetsused < maxconflictsets; ++i )
3587 {
3588 SCIP_CONFLICTSET* conflictset;
3589
3590 conflictset = conflict->conflictsets[i];
3591 assert(conflictset != NULL);
3592 assert(0 <= conflictset->validdepth);
3593 assert(conflictset->validdepth <= conflictset->insertdepth);
3594 assert(conflictset->insertdepth <= focusdepth);
3595 assert(conflictset->insertdepth <= conflictset->repropdepth);
3596 assert(conflictset->repropdepth <= currentdepth || conflictset->repropdepth == INT_MAX); /* INT_MAX for dive/probing/strong */
3597 assert(conflictset->conflictdepth <= currentdepth || conflictset->conflictdepth == INT_MAX); /* INT_MAX for dive/probing/strong */
3598
3599 /* ignore conflict sets that are only valid at a node that was already cut off */
3600 if( conflictset->insertdepth >= cutoffdepth )
3601 {
3602 SCIPsetDebugMsg(set, " -> ignoring conflict set with insertdepth %d >= cutoffdepth %d\n",
3603 conflictset->validdepth, cutoffdepth);
3604 continue;
3605 }
3606
3607 /* if no conflict bounds exist, the node and its sub tree in the conflict set's valid depth can be
3608 * cut off completely
3609 */
3610 if( conflictset->nbdchginfos == 0 )
3611 {
3612 SCIPsetDebugMsg(set, " -> empty conflict set in depth %d cuts off sub tree at depth %d\n",
3613 focusdepth, conflictset->validdepth);
3614
3615 SCIP_CALL( SCIPnodeCutoff(tree->path[conflictset->validdepth], set, stat, tree, transprob, origprob, reopt, lp, blkmem) );
3616 cutoffdepth = conflictset->validdepth;
3617 continue;
3618 }
3619
3620 /* if the conflict set is too long, use the conflict set only if it decreases the repropagation depth */
3621 if( conflictset->nbdchginfos > maxsize )
3622 {
3623 SCIPsetDebugMsg(set, " -> conflict set is too long: %d > %d literals\n", conflictset->nbdchginfos, maxsize);
3624 if( set->conf_keepreprop && conflictset->repropagate && conflictset->repropdepth < repropdepth )
3625 {
3626 repropdepth = conflictset->repropdepth;
3627 repropconflictset = conflictset;
3628 }
3629 }
3630 else
3631 {
3632 SCIP_Bool success;
3633
3634 /* call conflict handlers to create a conflict constraint */
3635 SCIP_CALL( conflictAddConflictCons(conflict, blkmem, set, stat, transprob, origprob, tree, reopt, lp, \
3636 branchcand, eventqueue, cliquetable, conflictset, conflictset->insertdepth, &success) );
3637
3638 /* if no conflict bounds exist, the node and its sub tree in the conflict set's valid depth can be
3639 * cut off completely
3640 */
3641 if( conflictset->nbdchginfos == 0 )
3642 {
3643 assert(!success);
3644
3645 SCIPsetDebugMsg(set, " -> empty conflict set in depth %d cuts off sub tree at depth %d\n",
3646 focusdepth, conflictset->validdepth);
3647
3648 SCIP_CALL( SCIPnodeCutoff(tree->path[conflictset->validdepth], set, stat, tree, transprob, origprob, \
3649 reopt, lp, blkmem) );
3650 cutoffdepth = conflictset->validdepth;
3651 continue;
3652 }
3653
3654 if( success )
3655 {
3656 SCIPsetDebugMsg(set, " -> conflict set %d/%d added (cdpt:%d, fdpt:%d, insert:%d, valid:%d, conf:%d, reprop:%d, len:%d):\n",
3657 nconflictsetsused+1, maxconflictsets, SCIPtreeGetCurrentDepth(tree), SCIPtreeGetFocusDepth(tree),
3658 conflictset->insertdepth, conflictset->validdepth, conflictset->conflictdepth, conflictset->repropdepth,
3659 conflictset->nbdchginfos);
3660 SCIPdebug(conflictsetPrint(conflictset));
3661
3662 if( conflictset->repropagate && conflictset->repropdepth <= repropdepth )
3663 {
3664 repropdepth = conflictset->repropdepth;
3665 repropconflictset = NULL;
3666 }
3667 nconflictsetsused++;
3668 }
3669 }
3670 }
3671
3672 /* reactivate propagation on the first node where one of the new conflict sets trigger a deduction */
3673 if( set->conf_repropagate && repropdepth < cutoffdepth && repropdepth < tree->pathlen )
3674 {
3675 assert(0 <= repropdepth && repropdepth < tree->pathlen);
3676 assert((int) tree->path[repropdepth]->depth == repropdepth);
3677
3678 /* if the conflict constraint of smallest repropagation depth was not yet added, insert it now */
3679 if( repropconflictset != NULL )
3680 {
3681 SCIP_Bool success;
3682
3683 assert(repropconflictset->repropagate);
3684 assert(repropconflictset->repropdepth == repropdepth);
3685
3686 SCIP_CALL( conflictAddConflictCons(conflict, blkmem, set, stat, transprob, origprob, tree, reopt, lp, \
3687 branchcand, eventqueue, cliquetable, repropconflictset, repropdepth, &success) );
3688
3689 /* if no conflict bounds exist, the node and its sub tree in the conflict set's valid depth can be
3690 * cut off completely
3691 */
3692 if( repropconflictset->nbdchginfos == 0 )
3693 {
3694 assert(!success);
3695
3696 SCIPsetDebugMsg(set, " -> empty reprop conflict set in depth %d cuts off sub tree at depth %d\n",
3697 focusdepth, repropconflictset->validdepth);
3698
3699 SCIP_CALL( SCIPnodeCutoff(tree->path[repropconflictset->validdepth], set, stat, tree, transprob, \
3700 origprob, reopt, lp, blkmem) );
3701 }
3702
3703 #ifdef SCIP_DEBUG
3704 if( success )
3705 {
3706 SCIPsetDebugMsg(set, " -> additional reprop conflict set added (cdpt:%d, fdpt:%d, insert:%d, valid:%d, conf:%d, reprop:%d, len:%d):\n",
3707 SCIPtreeGetCurrentDepth(tree), SCIPtreeGetFocusDepth(tree),
3708 repropconflictset->insertdepth, repropconflictset->validdepth, repropconflictset->conflictdepth,
3709 repropconflictset->repropdepth, repropconflictset->nbdchginfos);
3710 SCIPdebug(conflictsetPrint(repropconflictset));
3711 }
3712 #endif
3713 }
3714
3715 /* mark the node in the repropdepth to be propagated again */
3716 SCIPnodePropagateAgain(tree->path[repropdepth], set, stat, tree);
3717
3718 SCIPsetDebugMsg(set, "marked node %p in depth %d to be repropagated due to conflicts found in depth %d\n",
3719 (void*)tree->path[repropdepth], repropdepth, focusdepth);
3720 }
3721
3722 /* free the conflict store */
3723 for( i = 0; i < conflict->nconflictsets; ++i )
3724 {
3725 conflictsetFree(&conflict->conflictsets[i], blkmem);
3726 }
3727 conflict->nconflictsets = 0;
3728 }
3729
3730 /* free all temporarily created bound change information data */
3731 conflictFreeTmpBdchginfos(conflict, blkmem);
3732
3733 return SCIP_OKAY;
3734 }
3735
3736 /** returns the current number of conflict sets in the conflict set storage */
SCIPconflictGetNConflicts(SCIP_CONFLICT * conflict)3737 int SCIPconflictGetNConflicts(
3738 SCIP_CONFLICT* conflict /**< conflict analysis data */
3739 )
3740 {
3741 assert(conflict != NULL);
3742
3743 return conflict->nconflictsets;
3744 }
3745
3746 /** returns the total number of conflict constraints that were added to the problem */
SCIPconflictGetNAppliedConss(SCIP_CONFLICT * conflict)3747 SCIP_Longint SCIPconflictGetNAppliedConss(
3748 SCIP_CONFLICT* conflict /**< conflict analysis data */
3749 )
3750 {
3751 assert(conflict != NULL);
3752
3753 return conflict->nappliedglbconss + conflict->nappliedlocconss;
3754 }
3755
3756 /** returns the total number of literals in conflict constraints that were added to the problem */
SCIPconflictGetNAppliedLiterals(SCIP_CONFLICT * conflict)3757 SCIP_Longint SCIPconflictGetNAppliedLiterals(
3758 SCIP_CONFLICT* conflict /**< conflict analysis data */
3759 )
3760 {
3761 assert(conflict != NULL);
3762
3763 return conflict->nappliedglbliterals + conflict->nappliedlocliterals;
3764 }
3765
3766 /** returns the total number of global bound changes applied by the conflict analysis */
SCIPconflictGetNGlobalChgBds(SCIP_CONFLICT * conflict)3767 SCIP_Longint SCIPconflictGetNGlobalChgBds(
3768 SCIP_CONFLICT* conflict /**< conflict analysis data */
3769 )
3770 {
3771 assert(conflict != NULL);
3772
3773 return conflict->nglbchgbds;
3774 }
3775
3776 /** returns the total number of conflict constraints that were added globally to the problem */
SCIPconflictGetNAppliedGlobalConss(SCIP_CONFLICT * conflict)3777 SCIP_Longint SCIPconflictGetNAppliedGlobalConss(
3778 SCIP_CONFLICT* conflict /**< conflict analysis data */
3779 )
3780 {
3781 assert(conflict != NULL);
3782
3783 return conflict->nappliedglbconss;
3784 }
3785
3786 /** returns the total number of literals in conflict constraints that were added globally to the problem */
SCIPconflictGetNAppliedGlobalLiterals(SCIP_CONFLICT * conflict)3787 SCIP_Longint SCIPconflictGetNAppliedGlobalLiterals(
3788 SCIP_CONFLICT* conflict /**< conflict analysis data */
3789 )
3790 {
3791 assert(conflict != NULL);
3792
3793 return conflict->nappliedglbliterals;
3794 }
3795
3796 /** returns the total number of local bound changes applied by the conflict analysis */
SCIPconflictGetNLocalChgBds(SCIP_CONFLICT * conflict)3797 SCIP_Longint SCIPconflictGetNLocalChgBds(
3798 SCIP_CONFLICT* conflict /**< conflict analysis data */
3799 )
3800 {
3801 assert(conflict != NULL);
3802
3803 return conflict->nlocchgbds;
3804 }
3805
3806 /** returns the total number of conflict constraints that were added locally to the problem */
SCIPconflictGetNAppliedLocalConss(SCIP_CONFLICT * conflict)3807 SCIP_Longint SCIPconflictGetNAppliedLocalConss(
3808 SCIP_CONFLICT* conflict /**< conflict analysis data */
3809 )
3810 {
3811 assert(conflict != NULL);
3812
3813 return conflict->nappliedlocconss;
3814 }
3815
3816 /** returns the total number of literals in conflict constraints that were added locally to the problem */
SCIPconflictGetNAppliedLocalLiterals(SCIP_CONFLICT * conflict)3817 SCIP_Longint SCIPconflictGetNAppliedLocalLiterals(
3818 SCIP_CONFLICT* conflict /**< conflict analysis data */
3819 )
3820 {
3821 assert(conflict != NULL);
3822
3823 return conflict->nappliedlocliterals;
3824 }
3825
3826
3827
3828
3829 /*
3830 * Propagation Conflict Analysis
3831 */
3832
3833 /** returns whether bound change has a valid reason that can be resolved in conflict analysis */
3834 static
bdchginfoIsResolvable(SCIP_BDCHGINFO * bdchginfo)3835 SCIP_Bool bdchginfoIsResolvable(
3836 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
3837 )
3838 {
3839 assert(bdchginfo != NULL);
3840 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
3841
3842 return (SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_CONSINFER
3843 || (SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_PROPINFER
3844 && SCIPbdchginfoGetInferProp(bdchginfo) != NULL));
3845 }
3846
3847 /** compares two conflict set entries, such that bound changes infered later are
3848 * ordered prior to ones that were infered earlier
3849 */
3850 static
SCIP_DECL_SORTPTRCOMP(conflictBdchginfoComp)3851 SCIP_DECL_SORTPTRCOMP(conflictBdchginfoComp)
3852 { /*lint --e{715}*/
3853 SCIP_BDCHGINFO* bdchginfo1;
3854 SCIP_BDCHGINFO* bdchginfo2;
3855
3856 bdchginfo1 = (SCIP_BDCHGINFO*)elem1;
3857 bdchginfo2 = (SCIP_BDCHGINFO*)elem2;
3858 assert(bdchginfo1 != NULL);
3859 assert(bdchginfo2 != NULL);
3860 assert(!SCIPbdchginfoIsRedundant(bdchginfo1));
3861 assert(!SCIPbdchginfoIsRedundant(bdchginfo2));
3862
3863 if( bdchginfo1 == bdchginfo2 )
3864 return 0;
3865
3866 if( !SCIPbdchgidxIsEarlierNonNull(SCIPbdchginfoGetIdx(bdchginfo1), SCIPbdchginfoGetIdx(bdchginfo2)) )
3867 return -1;
3868 else
3869 return +1;
3870 }
3871
3872 /** return TRUE if conflict analysis is applicable; In case the function return FALSE there is no need to initialize the
3873 * conflict analysis since it will not be applied
3874 */
SCIPconflictApplicable(SCIP_SET * set)3875 SCIP_Bool SCIPconflictApplicable(
3876 SCIP_SET* set /**< global SCIP settings */
3877 )
3878 {
3879 /* check, if propagation conflict analysis is enabled */
3880 if( !set->conf_enable || !set->conf_useprop )
3881 return FALSE;
3882
3883 /* check, if there are any conflict handlers to use a conflict set */
3884 if( set->nconflicthdlrs == 0 )
3885 return FALSE;
3886
3887 return TRUE;
3888 }
3889
3890 /** creates conflict analysis data for propagation conflicts */
SCIPconflictCreate(SCIP_CONFLICT ** conflict,BMS_BLKMEM * blkmem,SCIP_SET * set)3891 SCIP_RETCODE SCIPconflictCreate(
3892 SCIP_CONFLICT** conflict, /**< pointer to conflict analysis data */
3893 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3894 SCIP_SET* set /**< global SCIP settings */
3895 )
3896 {
3897 assert(conflict != NULL);
3898
3899 SCIP_ALLOC( BMSallocMemory(conflict) );
3900
3901 SCIP_CALL( SCIPclockCreate(&(*conflict)->dIBclock, SCIP_CLOCKTYPE_DEFAULT) );
3902 SCIP_CALL( SCIPclockCreate(&(*conflict)->propanalyzetime, SCIP_CLOCKTYPE_DEFAULT) );
3903 SCIP_CALL( SCIPclockCreate(&(*conflict)->inflpanalyzetime, SCIP_CLOCKTYPE_DEFAULT) );
3904 SCIP_CALL( SCIPclockCreate(&(*conflict)->boundlpanalyzetime, SCIP_CLOCKTYPE_DEFAULT) );
3905 SCIP_CALL( SCIPclockCreate(&(*conflict)->sbanalyzetime, SCIP_CLOCKTYPE_DEFAULT) );
3906 SCIP_CALL( SCIPclockCreate(&(*conflict)->pseudoanalyzetime, SCIP_CLOCKTYPE_DEFAULT) );
3907
3908 /* enable or disable timing depending on the parameter statistic timing */
3909 SCIPconflictEnableOrDisableClocks((*conflict), set->time_statistictiming);
3910
3911 SCIP_CALL( SCIPpqueueCreate(&(*conflict)->bdchgqueue, set->mem_arraygrowinit, set->mem_arraygrowfac,
3912 conflictBdchginfoComp, NULL) );
3913 SCIP_CALL( SCIPpqueueCreate(&(*conflict)->forcedbdchgqueue, set->mem_arraygrowinit, set->mem_arraygrowfac,
3914 conflictBdchginfoComp, NULL) );
3915 SCIP_CALL( conflictsetCreate(&(*conflict)->conflictset, blkmem) );
3916 (*conflict)->conflictsets = NULL;
3917 (*conflict)->conflictsetscores = NULL;
3918 (*conflict)->tmpbdchginfos = NULL;
3919 (*conflict)->conflictsetssize = 0;
3920 (*conflict)->nconflictsets = 0;
3921 (*conflict)->proofsets = NULL;
3922 (*conflict)->proofsetssize = 0;
3923 (*conflict)->nproofsets = 0;
3924 (*conflict)->tmpbdchginfossize = 0;
3925 (*conflict)->ntmpbdchginfos = 0;
3926 (*conflict)->count = 0;
3927 (*conflict)->nglbchgbds = 0;
3928 (*conflict)->nappliedglbconss = 0;
3929 (*conflict)->nappliedglbliterals = 0;
3930 (*conflict)->nlocchgbds = 0;
3931 (*conflict)->nappliedlocconss = 0;
3932 (*conflict)->nappliedlocliterals = 0;
3933 (*conflict)->npropcalls = 0;
3934 (*conflict)->npropsuccess = 0;
3935 (*conflict)->npropconfconss = 0;
3936 (*conflict)->npropconfliterals = 0;
3937 (*conflict)->npropreconvconss = 0;
3938 (*conflict)->npropreconvliterals = 0;
3939 (*conflict)->ninflpcalls = 0;
3940 (*conflict)->ninflpsuccess = 0;
3941 (*conflict)->ninflpconfconss = 0;
3942 (*conflict)->ninflpconfliterals = 0;
3943 (*conflict)->ninflpreconvconss = 0;
3944 (*conflict)->ninflpreconvliterals = 0;
3945 (*conflict)->ninflpiterations = 0;
3946 (*conflict)->nboundlpcalls = 0;
3947 (*conflict)->nboundlpsuccess = 0;
3948 (*conflict)->nboundlpconfconss = 0;
3949 (*conflict)->nboundlpconfliterals = 0;
3950 (*conflict)->nboundlpreconvconss = 0;
3951 (*conflict)->nboundlpreconvliterals = 0;
3952 (*conflict)->nboundlpiterations = 0;
3953 (*conflict)->nsbcalls = 0;
3954 (*conflict)->nsbsuccess = 0;
3955 (*conflict)->nsbconfconss = 0;
3956 (*conflict)->nsbconfliterals = 0;
3957 (*conflict)->nsbreconvconss = 0;
3958 (*conflict)->nsbreconvliterals = 0;
3959 (*conflict)->nsbiterations = 0;
3960 (*conflict)->npseudocalls = 0;
3961 (*conflict)->npseudosuccess = 0;
3962 (*conflict)->npseudoconfconss = 0;
3963 (*conflict)->npseudoconfliterals = 0;
3964 (*conflict)->npseudoreconvconss = 0;
3965 (*conflict)->npseudoreconvliterals = 0;
3966 (*conflict)->ndualproofsinfglobal = 0;
3967 (*conflict)->ndualproofsinflocal = 0;
3968 (*conflict)->ndualproofsinfsuccess = 0;
3969 (*conflict)->dualproofsinfnnonzeros = 0;
3970 (*conflict)->ndualproofsbndglobal = 0;
3971 (*conflict)->ndualproofsbndlocal = 0;
3972 (*conflict)->ndualproofsbndsuccess = 0;
3973 (*conflict)->dualproofsbndnnonzeros = 0;
3974
3975 SCIP_CALL( conflictInitProofset((*conflict), blkmem) );
3976
3977 return SCIP_OKAY;
3978 }
3979
3980 /** frees conflict analysis data for propagation conflicts */
SCIPconflictFree(SCIP_CONFLICT ** conflict,BMS_BLKMEM * blkmem)3981 SCIP_RETCODE SCIPconflictFree(
3982 SCIP_CONFLICT** conflict, /**< pointer to conflict analysis data */
3983 BMS_BLKMEM* blkmem /**< block memory of transformed problem */
3984 )
3985 {
3986 assert(conflict != NULL);
3987 assert(*conflict != NULL);
3988 assert((*conflict)->nconflictsets == 0);
3989 assert((*conflict)->ntmpbdchginfos == 0);
3990
3991 #ifdef SCIP_CONFGRAPH
3992 confgraphFree();
3993 #endif
3994
3995 SCIPclockFree(&(*conflict)->dIBclock);
3996 SCIPclockFree(&(*conflict)->propanalyzetime);
3997 SCIPclockFree(&(*conflict)->inflpanalyzetime);
3998 SCIPclockFree(&(*conflict)->boundlpanalyzetime);
3999 SCIPclockFree(&(*conflict)->sbanalyzetime);
4000 SCIPclockFree(&(*conflict)->pseudoanalyzetime);
4001 SCIPpqueueFree(&(*conflict)->bdchgqueue);
4002 SCIPpqueueFree(&(*conflict)->forcedbdchgqueue);
4003 conflictsetFree(&(*conflict)->conflictset, blkmem);
4004 proofsetFree(&(*conflict)->proofset, blkmem);
4005
4006 BMSfreeMemoryArrayNull(&(*conflict)->conflictsets);
4007 BMSfreeMemoryArrayNull(&(*conflict)->conflictsetscores);
4008 BMSfreeMemoryArrayNull(&(*conflict)->proofsets);
4009 BMSfreeMemoryArrayNull(&(*conflict)->tmpbdchginfos);
4010 BMSfreeMemory(conflict);
4011
4012 return SCIP_OKAY;
4013 }
4014
4015 /** clears the conflict queue and the current conflict set */
4016 static
conflictClear(SCIP_CONFLICT * conflict)4017 void conflictClear(
4018 SCIP_CONFLICT* conflict /**< conflict analysis data */
4019 )
4020 {
4021 assert(conflict != NULL);
4022
4023 SCIPpqueueClear(conflict->bdchgqueue);
4024 SCIPpqueueClear(conflict->forcedbdchgqueue);
4025 conflictsetClear(conflict->conflictset);
4026 }
4027
4028 /** initializes the propagation conflict analysis by clearing the conflict candidate queue */
SCIPconflictInit(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_CONFTYPE conftype,SCIP_Bool usescutoffbound)4029 SCIP_RETCODE SCIPconflictInit(
4030 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4031 SCIP_SET* set, /**< global SCIP settings */
4032 SCIP_STAT* stat, /**< problem statistics */
4033 SCIP_PROB* prob, /**< problem data */
4034 SCIP_CONFTYPE conftype, /**< type of the conflict */
4035 SCIP_Bool usescutoffbound /**< depends the conflict on a cutoff bound? */
4036 )
4037 {
4038 assert(conflict != NULL);
4039 assert(set != NULL);
4040 assert(stat != NULL);
4041 assert(prob != NULL);
4042
4043 SCIPsetDebugMsg(set, "initializing conflict analysis\n");
4044
4045 /* clear the conflict candidate queue and the conflict set */
4046 conflictClear(conflict);
4047
4048 /* set conflict type */
4049 assert(conftype == SCIP_CONFTYPE_BNDEXCEEDING || conftype == SCIP_CONFTYPE_INFEASLP
4050 || conftype == SCIP_CONFTYPE_PROPAGATION);
4051 conflict->conflictset->conflicttype = conftype;
4052
4053 /* set whether a cutoff bound is involved */
4054 conflict->conflictset->usescutoffbound = usescutoffbound;
4055
4056 /* increase the conflict counter, such that binary variables of new conflict set and new conflict queue are labeled
4057 * with this new counter
4058 */
4059 conflict->count++;
4060 if( conflict->count == 0 ) /* make sure, 0 is not a valid conflict counter (may happen due to integer overflow) */
4061 conflict->count = 1;
4062
4063 /* increase the conflict score weight for history updates of future conflict reasons */
4064 if( stat->nnodes > stat->lastconflictnode )
4065 {
4066 assert(0.0 < set->conf_scorefac && set->conf_scorefac <= 1.0);
4067 stat->vsidsweight /= set->conf_scorefac;
4068 assert(stat->vsidsweight > 0.0);
4069
4070 /* if the conflict score for the next conflict exceeds 1000.0, rescale all history conflict scores */
4071 if( stat->vsidsweight >= 1000.0 )
4072 {
4073 int v;
4074
4075 for( v = 0; v < prob->nvars; ++v )
4076 {
4077 SCIP_CALL( SCIPvarScaleVSIDS(prob->vars[v], 1.0/stat->vsidsweight) );
4078 }
4079 SCIPhistoryScaleVSIDS(stat->glbhistory, 1.0/stat->vsidsweight);
4080 SCIPhistoryScaleVSIDS(stat->glbhistorycrun, 1.0/stat->vsidsweight);
4081 stat->vsidsweight = 1.0;
4082 }
4083 stat->lastconflictnode = stat->nnodes;
4084 }
4085
4086 #ifdef SCIP_CONFGRAPH
4087 confgraphFree();
4088 SCIP_CALL( confgraphCreate(set, conflict) );
4089 #endif
4090
4091 return SCIP_OKAY;
4092 }
4093
4094 /** marks bound to be present in the current conflict and returns whether a bound which is at least as tight was already
4095 * member of the current conflict (i.e., the given bound change does not need to be added)
4096 */
4097 static
conflictMarkBoundCheckPresence(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd)4098 SCIP_Bool conflictMarkBoundCheckPresence(
4099 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4100 SCIP_SET* set, /**< global SCIP settings */
4101 SCIP_BDCHGINFO* bdchginfo, /**< bound change to add to the conflict set */
4102 SCIP_Real relaxedbd /**< relaxed bound */
4103 )
4104 {
4105 SCIP_VAR* var;
4106 SCIP_Real newbound;
4107
4108 assert(conflict != NULL);
4109
4110 var = SCIPbdchginfoGetVar(bdchginfo);
4111 newbound = SCIPbdchginfoGetNewbound(bdchginfo);
4112 assert(var != NULL);
4113
4114 switch( SCIPbdchginfoGetBoundtype(bdchginfo) )
4115 {
4116 case SCIP_BOUNDTYPE_LOWER:
4117 /* check if the variables lower bound is already member of the conflict */
4118 if( var->conflictlbcount == conflict->count )
4119 {
4120 /* the variable is already member of the conflict; hence check if the new bound is redundant */
4121 if( var->conflictlb > newbound )
4122 {
4123 SCIPsetDebugMsg(set, "ignoring redundant bound change <%s> >= %g since a stronger lower bound exist <%s> >= %g\n",
4124 SCIPvarGetName(var), newbound, SCIPvarGetName(var), var->conflictlb);
4125 return TRUE;
4126 }
4127 else if( var->conflictlb == newbound ) /*lint !e777*/
4128 {
4129 SCIPsetDebugMsg(set, "ignoring redundant bound change <%s> >= %g since this lower bound is already present\n", SCIPvarGetName(var), newbound);
4130 SCIPsetDebugMsg(set, "adjust relaxed lower bound <%g> -> <%g>\n", var->conflictlb, relaxedbd);
4131 var->conflictrelaxedlb = MAX(var->conflictrelaxedlb, relaxedbd);
4132 return TRUE;
4133 }
4134 }
4135
4136 /* add the variable lower bound to the current conflict */
4137 var->conflictlbcount = conflict->count;
4138
4139 /* remember the lower bound and relaxed bound to allow only better/tighter lower bounds for that variables
4140 * w.r.t. this conflict
4141 */
4142 var->conflictlb = newbound;
4143 var->conflictrelaxedlb = relaxedbd;
4144
4145 return FALSE;
4146
4147 case SCIP_BOUNDTYPE_UPPER:
4148 /* check if the variables upper bound is already member of the conflict */
4149 if( var->conflictubcount == conflict->count )
4150 {
4151 /* the variable is already member of the conflict; hence check if the new bound is redundant */
4152 if( var->conflictub < newbound )
4153 {
4154 SCIPsetDebugMsg(set, "ignoring redundant bound change <%s> <= %g since a stronger upper bound exist <%s> <= %g\n",
4155 SCIPvarGetName(var), newbound, SCIPvarGetName(var), var->conflictub);
4156 return TRUE;
4157 }
4158 else if( var->conflictub == newbound ) /*lint !e777*/
4159 {
4160 SCIPsetDebugMsg(set, "ignoring redundant bound change <%s> <= %g since this upper bound is already present\n", SCIPvarGetName(var), newbound);
4161 SCIPsetDebugMsg(set, "adjust relaxed upper bound <%g> -> <%g>\n", var->conflictub, relaxedbd);
4162 var->conflictrelaxedub = MIN(var->conflictrelaxedub, relaxedbd);
4163 return TRUE;
4164 }
4165 }
4166
4167 /* add the variable upper bound to the current conflict */
4168 var->conflictubcount = conflict->count;
4169
4170 /* remember the upper bound and relaxed bound to allow only better/tighter upper bounds for that variables
4171 * w.r.t. this conflict
4172 */
4173 var->conflictub = newbound;
4174 var->conflictrelaxedub = relaxedbd;
4175
4176 return FALSE;
4177
4178 default:
4179 SCIPerrorMessage("invalid bound type %d\n", SCIPbdchginfoGetBoundtype(bdchginfo));
4180 SCIPABORT();
4181 return FALSE; /*lint !e527*/
4182 }
4183 }
4184
4185 /** puts bound change into the current conflict set */
4186 static
conflictAddConflictBound(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd)4187 SCIP_RETCODE conflictAddConflictBound(
4188 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4189 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
4190 SCIP_SET* set, /**< global SCIP settings */
4191 SCIP_BDCHGINFO* bdchginfo, /**< bound change to add to the conflict set */
4192 SCIP_Real relaxedbd /**< relaxed bound */
4193 )
4194 {
4195 assert(conflict != NULL);
4196 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
4197
4198 /* check if the relaxed bound is really a relaxed bound */
4199 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER || SCIPsetIsGE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4200 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_UPPER || SCIPsetIsLE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4201
4202 SCIPsetDebugMsg(set, "putting bound change <%s> %s %g(%g) at depth %d to current conflict set\n",
4203 SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
4204 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", SCIPbdchginfoGetNewbound(bdchginfo),
4205 relaxedbd, SCIPbdchginfoGetDepth(bdchginfo));
4206
4207 /* mark the bound to be member of the conflict and check if a bound which is at least as tight is already member of
4208 * the conflict
4209 */
4210 if( !conflictMarkBoundCheckPresence(conflict, set, bdchginfo, relaxedbd) )
4211 {
4212 /* add the bound change to the current conflict set */
4213 SCIP_CALL( conflictsetAddBound(conflict->conflictset, blkmem, set, bdchginfo, relaxedbd) );
4214
4215 #ifdef SCIP_CONFGRAPH
4216 if( bdchginfo != confgraphcurrentbdchginfo )
4217 confgraphAddBdchg(bdchginfo);
4218 #endif
4219 }
4220 #ifdef SCIP_CONFGRAPH
4221 else
4222 confgraphLinkBdchg(bdchginfo);
4223 #endif
4224
4225 return SCIP_OKAY;
4226 }
4227
4228 /** returns whether the negation of the given bound change would lead to a globally valid literal */
4229 static
isBoundchgUseless(SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo)4230 SCIP_Bool isBoundchgUseless(
4231 SCIP_SET* set, /**< global SCIP settings */
4232 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
4233 )
4234 {
4235 SCIP_VAR* var;
4236 SCIP_BOUNDTYPE boundtype;
4237 SCIP_Real bound;
4238
4239 var = SCIPbdchginfoGetVar(bdchginfo);
4240 boundtype = SCIPbdchginfoGetBoundtype(bdchginfo);
4241 bound = SCIPbdchginfoGetNewbound(bdchginfo);
4242
4243 return (SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS
4244 && ((boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGE(set, bound, SCIPvarGetUbGlobal(var)))
4245 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLE(set, bound, SCIPvarGetLbGlobal(var)))));
4246 }
4247
4248 /** adds given bound change information to the conflict candidate queue */
4249 static
conflictQueueBound(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd)4250 SCIP_RETCODE conflictQueueBound(
4251 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4252 SCIP_SET* set, /**< global SCIP settings */
4253 SCIP_BDCHGINFO* bdchginfo, /**< bound change information */
4254 SCIP_Real relaxedbd /**< relaxed bound */
4255 )
4256 {
4257 assert(conflict != NULL);
4258 assert(set != NULL);
4259 assert(bdchginfo != NULL);
4260 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
4261
4262 /* check if the relaxed bound is really a relaxed bound */
4263 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER || SCIPsetIsGE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4264 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_UPPER || SCIPsetIsLE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4265
4266 /* mark the bound to be member of the conflict and check if a bound which is at least as tight is already member of
4267 * the conflict
4268 */
4269 if( !conflictMarkBoundCheckPresence(conflict, set, bdchginfo, relaxedbd) )
4270 {
4271 /* insert the bound change into the conflict queue */
4272 if( (!set->conf_preferbinary || SCIPvarIsBinary(SCIPbdchginfoGetVar(bdchginfo)))
4273 && !isBoundchgUseless(set, bdchginfo) )
4274 {
4275 SCIP_CALL( SCIPpqueueInsert(conflict->bdchgqueue, (void*)bdchginfo) );
4276 }
4277 else
4278 {
4279 SCIP_CALL( SCIPpqueueInsert(conflict->forcedbdchgqueue, (void*)bdchginfo) );
4280 }
4281
4282 #ifdef SCIP_CONFGRAPH
4283 confgraphAddBdchg(bdchginfo);
4284 #endif
4285 }
4286 #ifdef SCIP_CONFGRAPH
4287 else
4288 confgraphLinkBdchg(bdchginfo);
4289 #endif
4290
4291 return SCIP_OKAY;
4292 }
4293
4294 /** convert variable and bound change to active variable */
4295 static
convertToActiveVar(SCIP_VAR ** var,SCIP_SET * set,SCIP_BOUNDTYPE * boundtype,SCIP_Real * bound)4296 SCIP_RETCODE convertToActiveVar(
4297 SCIP_VAR** var, /**< pointer to variable */
4298 SCIP_SET* set, /**< global SCIP settings */
4299 SCIP_BOUNDTYPE* boundtype, /**< pointer to type of bound that was changed: lower or upper bound */
4300 SCIP_Real* bound /**< pointer to bound to convert, or NULL */
4301 )
4302 {
4303 SCIP_Real scalar;
4304 SCIP_Real constant;
4305
4306 scalar = 1.0;
4307 constant = 0.0;
4308
4309 /* transform given varibale to active varibale */
4310 SCIP_CALL( SCIPvarGetProbvarSum(var, set, &scalar, &constant) );
4311 assert(SCIPvarGetStatus(*var) == SCIP_VARSTATUS_FIXED || scalar != 0.0); /*lint !e777*/
4312
4313 if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_FIXED )
4314 return SCIP_OKAY;
4315
4316 /* if the scalar of the aggregation is negative, we have to switch the bound type */
4317 if( scalar < 0.0 )
4318 (*boundtype) = SCIPboundtypeOpposite(*boundtype);
4319
4320 if( bound != NULL )
4321 {
4322 (*bound) -= constant;
4323 (*bound) /= scalar;
4324 }
4325
4326 return SCIP_OKAY;
4327 }
4328
4329 /** adds variable's bound to conflict candidate queue */
4330 static
conflictAddBound(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd)4331 SCIP_RETCODE conflictAddBound(
4332 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4333 BMS_BLKMEM* blkmem, /**< block memory */
4334 SCIP_SET* set, /**< global SCIP settings */
4335 SCIP_STAT* stat, /**< dynamic problem statistics */
4336 SCIP_VAR* var, /**< problem variable */
4337 SCIP_BOUNDTYPE boundtype, /**< type of bound that was changed: lower or upper bound */
4338 SCIP_BDCHGINFO* bdchginfo, /**< bound change info, or NULL */
4339 SCIP_Real relaxedbd /**< relaxed bound */
4340 )
4341 {
4342 assert(SCIPvarIsActive(var));
4343 assert(bdchginfo != NULL);
4344 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
4345
4346 SCIPsetDebugMsg(set, " -> adding bound <%s> %s %.15g(%.15g) [status:%d, type:%d, depth:%d, pos:%d, reason:<%s>, info:%d] to candidates\n",
4347 SCIPvarGetName(var),
4348 boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4349 SCIPbdchginfoGetNewbound(bdchginfo), relaxedbd,
4350 SCIPvarGetStatus(var), SCIPvarGetType(var),
4351 SCIPbdchginfoGetDepth(bdchginfo), SCIPbdchginfoGetPos(bdchginfo),
4352 SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_BRANCHING ? "branch"
4353 : (SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_CONSINFER
4354 ? SCIPconsGetName(SCIPbdchginfoGetInferCons(bdchginfo))
4355 : (SCIPbdchginfoGetInferProp(bdchginfo) != NULL ? SCIPpropGetName(SCIPbdchginfoGetInferProp(bdchginfo))
4356 : "none")),
4357 SCIPbdchginfoGetChgtype(bdchginfo) != SCIP_BOUNDCHGTYPE_BRANCHING ? SCIPbdchginfoGetInferInfo(bdchginfo) : -1);
4358
4359 /* the local bound change may be resolved and has to be put on the candidate queue;
4360 * we even put bound changes without inference information on the queue in order to automatically
4361 * eliminate multiple insertions of the same bound change
4362 */
4363 assert(SCIPbdchginfoGetVar(bdchginfo) == var);
4364 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == boundtype);
4365 assert(SCIPbdchginfoGetDepth(bdchginfo) >= 0);
4366 assert(SCIPbdchginfoGetPos(bdchginfo) >= 0);
4367
4368 /* the relaxed bound should be a relaxation */
4369 assert(boundtype == SCIP_BOUNDTYPE_LOWER ? SCIPsetIsLE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)) : SCIPsetIsGE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4370
4371 /* the relaxed bound should be worse then the old bound of the bound change info */
4372 assert(boundtype == SCIP_BOUNDTYPE_LOWER ? SCIPsetIsGT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)) : SCIPsetIsLT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)));
4373
4374 /* put bound change information into priority queue */
4375 SCIP_CALL( conflictQueueBound(conflict, set, bdchginfo, relaxedbd) );
4376
4377 /* each variable which is add to the conflict graph gets an increase in the VSIDS
4378 *
4379 * @note That is different to the VSIDS preseted in the literature
4380 */
4381 SCIP_CALL( incVSIDS(var, blkmem, set, stat, boundtype, relaxedbd, set->conf_conflictgraphweight) );
4382
4383 return SCIP_OKAY;
4384 }
4385
4386 /** adds variable's bound to conflict candidate queue */
SCIPconflictAddBound(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx)4387 SCIP_RETCODE SCIPconflictAddBound(
4388 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4389 BMS_BLKMEM* blkmem, /**< block memory */
4390 SCIP_SET* set, /**< global SCIP settings */
4391 SCIP_STAT* stat, /**< dynamic problem statistics */
4392 SCIP_VAR* var, /**< problem variable */
4393 SCIP_BOUNDTYPE boundtype, /**< type of bound that was changed: lower or upper bound */
4394 SCIP_BDCHGIDX* bdchgidx /**< bound change index (time stamp of bound change), or NULL for current time */
4395 )
4396 {
4397 SCIP_BDCHGINFO* bdchginfo;
4398
4399 assert(conflict != NULL);
4400 assert(stat != NULL);
4401 assert(var != NULL);
4402
4403 /* convert bound to active problem variable */
4404 SCIP_CALL( convertToActiveVar(&var, set, &boundtype, NULL) );
4405
4406 /* we can ignore fixed variables */
4407 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED )
4408 return SCIP_OKAY;
4409
4410 /* if the variable is multi-aggregated, add the bounds of all aggregation variables */
4411 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
4412 {
4413 SCIP_VAR** vars;
4414 SCIP_Real* scalars;
4415 int nvars;
4416 int i;
4417
4418 vars = SCIPvarGetMultaggrVars(var);
4419 scalars = SCIPvarGetMultaggrScalars(var);
4420 nvars = SCIPvarGetMultaggrNVars(var);
4421 for( i = 0; i < nvars; ++i )
4422 {
4423 SCIP_CALL( SCIPconflictAddBound(conflict, blkmem, set, stat, vars[i],
4424 (scalars[i] < 0.0 ? SCIPboundtypeOpposite(boundtype) : boundtype), bdchgidx) );
4425 }
4426
4427 return SCIP_OKAY;
4428 }
4429 assert(SCIPvarIsActive(var));
4430
4431 /* get bound change information */
4432 bdchginfo = SCIPvarGetBdchgInfo(var, boundtype, bdchgidx, FALSE);
4433
4434 /* if bound of variable was not changed (this means it is still the global bound), we can ignore the conflicting
4435 * bound
4436 */
4437 if( bdchginfo == NULL )
4438 return SCIP_OKAY;
4439
4440 assert(SCIPbdchgidxIsEarlier(SCIPbdchginfoGetIdx(bdchginfo), bdchgidx));
4441
4442 SCIP_CALL( conflictAddBound(conflict, blkmem, set, stat, var, boundtype, bdchginfo, SCIPbdchginfoGetNewbound(bdchginfo)) );
4443
4444 return SCIP_OKAY;
4445 }
4446
4447 /** adds variable's bound to conflict candidate queue */
SCIPconflictAddRelaxedBound(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx,SCIP_Real relaxedbd)4448 SCIP_RETCODE SCIPconflictAddRelaxedBound(
4449 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4450 BMS_BLKMEM* blkmem, /**< block memory */
4451 SCIP_SET* set, /**< global SCIP settings */
4452 SCIP_STAT* stat, /**< dynamic problem statistics */
4453 SCIP_VAR* var, /**< problem variable */
4454 SCIP_BOUNDTYPE boundtype, /**< type of bound that was changed: lower or upper bound */
4455 SCIP_BDCHGIDX* bdchgidx, /**< bound change index (time stamp of bound change), or NULL for current time */
4456 SCIP_Real relaxedbd /**< the relaxed bound */
4457 )
4458 {
4459 SCIP_BDCHGINFO* bdchginfo;
4460 int nbdchgs;
4461
4462 assert(conflict != NULL);
4463 assert(stat != NULL);
4464 assert(var != NULL);
4465
4466 if( !SCIPvarIsActive(var) )
4467 {
4468 /* convert bound to active problem variable */
4469 SCIP_CALL( convertToActiveVar(&var, set, &boundtype, &relaxedbd) );
4470
4471 /* we can ignore fixed variables */
4472 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED )
4473 return SCIP_OKAY;
4474
4475 /* if the variable is multi-aggregated, add the bounds of all aggregation variables */
4476 if(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
4477 {
4478 SCIPsetDebugMsg(set, "ignoring relaxed bound information since variable <%s> is multi-aggregated active\n", SCIPvarGetName(var));
4479
4480 SCIP_CALL( SCIPconflictAddBound(conflict, blkmem, set, stat, var, boundtype, bdchgidx) );
4481
4482 return SCIP_OKAY;
4483 }
4484 }
4485 assert(SCIPvarIsActive(var));
4486
4487 /* get bound change information */
4488 bdchginfo = SCIPvarGetBdchgInfo(var, boundtype, bdchgidx, FALSE);
4489
4490 /* if bound of variable was not changed (this means it is still the global bound), we can ignore the conflicting
4491 * bound
4492 */
4493 if( bdchginfo == NULL )
4494 return SCIP_OKAY;
4495
4496 /* check that the bound change info is not a temporary one */
4497 assert(SCIPbdchgidxGetPos(&bdchginfo->bdchgidx) >= 0);
4498
4499 /* get the position of the bound change information within the bound change array of the variable */
4500 nbdchgs = (int) bdchginfo->pos;
4501 assert(nbdchgs >= 0);
4502
4503 /* if the relaxed bound should be ignored, set the relaxed bound to the bound given by the bdchgidx; that ensures
4504 * that the loop(s) below will be skipped
4505 */
4506 if( set->conf_ignorerelaxedbd )
4507 relaxedbd = SCIPbdchginfoGetNewbound(bdchginfo);
4508
4509 /* search for the bound change information which includes the relaxed bound */
4510 if( boundtype == SCIP_BOUNDTYPE_LOWER )
4511 {
4512 SCIP_Real newbound;
4513
4514 /* adjust relaxed lower bound w.r.t. variable type */
4515 SCIPvarAdjustLb(var, set, &relaxedbd);
4516
4517 /* due to numericis we compare the relaxed lower bound to the one present at the particular time point and take
4518 * the better one
4519 */
4520 newbound = SCIPbdchginfoGetNewbound(bdchginfo);
4521 relaxedbd = MIN(relaxedbd, newbound);
4522
4523 /* check if relaxed lower bound is smaller or equal to global lower bound; if so we can ignore the conflicting
4524 * bound
4525 */
4526 if( SCIPsetIsLE(set, relaxedbd, SCIPvarGetLbGlobal(var)) )
4527 return SCIP_OKAY;
4528
4529 while( nbdchgs > 0 )
4530 {
4531 assert(SCIPsetIsLE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4532
4533 /* check if the old lower bound is greater than or equal to relaxed lower bound; if not we found the bound
4534 * change info which we need to report
4535 */
4536 if( SCIPsetIsGT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)) )
4537 break;
4538
4539 bdchginfo = SCIPvarGetBdchgInfoLb(var, nbdchgs-1);
4540
4541 SCIPsetDebugMsg(set, "lower bound change %d oldbd=%.15g, newbd=%.15g, depth=%d, pos=%d, redundant=%u\n",
4542 nbdchgs, SCIPbdchginfoGetOldbound(bdchginfo), SCIPbdchginfoGetNewbound(bdchginfo),
4543 SCIPbdchginfoGetDepth(bdchginfo), SCIPbdchginfoGetPos(bdchginfo),
4544 SCIPbdchginfoIsRedundant(bdchginfo));
4545
4546 /* if bound change is redundant (this means it now a global bound), we can ignore the conflicting bound */
4547 if( SCIPbdchginfoIsRedundant(bdchginfo) )
4548 return SCIP_OKAY;
4549
4550 nbdchgs--;
4551 }
4552 assert(SCIPsetIsGT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)));
4553 }
4554 else
4555 {
4556 SCIP_Real newbound;
4557
4558 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
4559
4560 /* adjust relaxed upper bound w.r.t. variable type */
4561 SCIPvarAdjustUb(var, set, &relaxedbd);
4562
4563 /* due to numericis we compare the relaxed upper bound to the one present at the particular time point and take
4564 * the better one
4565 */
4566 newbound = SCIPbdchginfoGetNewbound(bdchginfo);
4567 relaxedbd = MAX(relaxedbd, newbound);
4568
4569 /* check if relaxed upper bound is greater or equal to global upper bound; if so we can ignore the conflicting
4570 * bound
4571 */
4572 if( SCIPsetIsGE(set, relaxedbd, SCIPvarGetUbGlobal(var)) )
4573 return SCIP_OKAY;
4574
4575 while( nbdchgs > 0 )
4576 {
4577 assert(SCIPsetIsGE(set, relaxedbd, SCIPbdchginfoGetNewbound(bdchginfo)));
4578
4579 /* check if the old upper bound is smaller than or equal to the relaxed upper bound; if not we found the
4580 * bound change info which we need to report
4581 */
4582 if( SCIPsetIsLT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)) )
4583 break;
4584
4585 bdchginfo = SCIPvarGetBdchgInfoUb(var, nbdchgs-1);
4586
4587 SCIPsetDebugMsg(set, "upper bound change %d oldbd=%.15g, newbd=%.15g, depth=%d, pos=%d, redundant=%u\n",
4588 nbdchgs, SCIPbdchginfoGetOldbound(bdchginfo), SCIPbdchginfoGetNewbound(bdchginfo),
4589 SCIPbdchginfoGetDepth(bdchginfo), SCIPbdchginfoGetPos(bdchginfo),
4590 SCIPbdchginfoIsRedundant(bdchginfo));
4591
4592 /* if bound change is redundant (this means it now a global bound), we can ignore the conflicting bound */
4593 if( SCIPbdchginfoIsRedundant(bdchginfo) )
4594 return SCIP_OKAY;
4595
4596 nbdchgs--;
4597 }
4598 assert(SCIPsetIsLT(set, relaxedbd, SCIPbdchginfoGetOldbound(bdchginfo)));
4599 }
4600
4601 assert(SCIPbdchgidxIsEarlier(SCIPbdchginfoGetIdx(bdchginfo), bdchgidx));
4602
4603 /* put bound change information into priority queue */
4604 SCIP_CALL( conflictAddBound(conflict, blkmem, set, stat, var, boundtype, bdchginfo, relaxedbd) );
4605
4606 return SCIP_OKAY;
4607 }
4608
4609 /** checks if the given variable is already part of the current conflict set or queued for resolving with the same or
4610 * even stronger bound
4611 */
SCIPconflictIsVarUsed(SCIP_CONFLICT * conflict,SCIP_VAR * var,SCIP_SET * set,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool * used)4612 SCIP_RETCODE SCIPconflictIsVarUsed(
4613 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4614 SCIP_VAR* var, /**< problem variable */
4615 SCIP_SET* set, /**< global SCIP settings */
4616 SCIP_BOUNDTYPE boundtype, /**< type of bound for which the score should be increased */
4617 SCIP_BDCHGIDX* bdchgidx, /**< bound change index (time stamp of bound change), or NULL for current time */
4618 SCIP_Bool* used /**< pointer to store if the variable is already used */
4619 )
4620 {
4621 SCIP_Real newbound;
4622
4623 /* convert bound to active problem variable */
4624 SCIP_CALL( convertToActiveVar(&var, set, &boundtype, NULL) );
4625
4626 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
4627 *used = FALSE;
4628 else
4629 {
4630 assert(SCIPvarIsActive(var));
4631 assert(var != NULL);
4632
4633 switch( boundtype )
4634 {
4635 case SCIP_BOUNDTYPE_LOWER:
4636
4637 newbound = SCIPgetVarLbAtIndex(set->scip, var, bdchgidx, FALSE);
4638
4639 if( var->conflictlbcount == conflict->count && var->conflictlb >= newbound )
4640 {
4641 SCIPsetDebugMsg(set, "already queued bound change <%s> >= %g\n", SCIPvarGetName(var), newbound);
4642 *used = TRUE;
4643 }
4644 else
4645 *used = FALSE;
4646 break;
4647 case SCIP_BOUNDTYPE_UPPER:
4648
4649 newbound = SCIPgetVarUbAtIndex(set->scip, var, bdchgidx, FALSE);
4650
4651 if( var->conflictubcount == conflict->count && var->conflictub <= newbound )
4652 {
4653 SCIPsetDebugMsg(set, "already queued bound change <%s> <= %g\n", SCIPvarGetName(var), newbound);
4654 *used = TRUE;
4655 }
4656 else
4657 *used = FALSE;
4658 break;
4659 default:
4660 SCIPerrorMessage("invalid bound type %d\n", boundtype);
4661 SCIPABORT();
4662 *used = FALSE; /*lint !e527*/
4663 }
4664 }
4665
4666 return SCIP_OKAY;
4667 }
4668
4669 /** returns the conflict lower bound if the variable is present in the current conflict set; otherwise the global lower
4670 * bound
4671 */
SCIPconflictGetVarLb(SCIP_CONFLICT * conflict,SCIP_VAR * var)4672 SCIP_Real SCIPconflictGetVarLb(
4673 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4674 SCIP_VAR* var /**< problem variable */
4675 )
4676 {
4677 if( var->conflictlbcount == conflict->count )
4678 {
4679 assert(EPSGE(var->conflictlb, var->conflictrelaxedlb, 1e-09));
4680 return var->conflictrelaxedlb;
4681 }
4682
4683 return SCIPvarGetLbGlobal(var);
4684 }
4685
4686 /** returns the conflict upper bound if the variable is present in the current conflict set; otherwise the global upper
4687 * bound
4688 */
SCIPconflictGetVarUb(SCIP_CONFLICT * conflict,SCIP_VAR * var)4689 SCIP_Real SCIPconflictGetVarUb(
4690 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4691 SCIP_VAR* var /**< problem variable */
4692 )
4693 {
4694 if( var->conflictubcount == conflict->count )
4695 {
4696 assert(EPSLE(var->conflictub, var->conflictrelaxedub, 1e-09));
4697 return var->conflictrelaxedub;
4698 }
4699
4700 return SCIPvarGetUbGlobal(var);
4701 }
4702
4703 /** removes and returns next conflict analysis candidate from the candidate queue */
4704 static
conflictRemoveCand(SCIP_CONFLICT * conflict)4705 SCIP_BDCHGINFO* conflictRemoveCand(
4706 SCIP_CONFLICT* conflict /**< conflict analysis data */
4707 )
4708 {
4709 SCIP_BDCHGINFO* bdchginfo;
4710 SCIP_VAR* var;
4711
4712 assert(conflict != NULL);
4713
4714 if( SCIPpqueueNElems(conflict->forcedbdchgqueue) > 0 )
4715 bdchginfo = (SCIP_BDCHGINFO*)(SCIPpqueueRemove(conflict->forcedbdchgqueue));
4716 else
4717 bdchginfo = (SCIP_BDCHGINFO*)(SCIPpqueueRemove(conflict->bdchgqueue));
4718
4719 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
4720
4721 /* if we have a candidate this one should be valid for the current conflict analysis */
4722 assert(!bdchginfoIsInvalid(conflict, bdchginfo));
4723
4724 /* mark the bound change to be no longer in the conflict (it will be either added again to the conflict set or
4725 * replaced by resolving, which might add a weaker change on the same bound to the queue)
4726 */
4727 var = SCIPbdchginfoGetVar(bdchginfo);
4728 if( SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER )
4729 {
4730 var->conflictlbcount = 0;
4731 var->conflictrelaxedlb = SCIP_REAL_MIN;
4732 }
4733 else
4734 {
4735 assert(SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_UPPER);
4736 var->conflictubcount = 0;
4737 var->conflictrelaxedub = SCIP_REAL_MAX;
4738 }
4739
4740 #ifdef SCIP_CONFGRAPH
4741 confgraphSetCurrentBdchg(bdchginfo);
4742 #endif
4743
4744 return bdchginfo;
4745 }
4746
4747 /** returns next conflict analysis candidate from the candidate queue without removing it */
4748 static
conflictFirstCand(SCIP_CONFLICT * conflict)4749 SCIP_BDCHGINFO* conflictFirstCand(
4750 SCIP_CONFLICT* conflict /**< conflict analysis data */
4751 )
4752 {
4753 SCIP_BDCHGINFO* bdchginfo;
4754
4755 assert(conflict != NULL);
4756
4757 if( SCIPpqueueNElems(conflict->forcedbdchgqueue) > 0 )
4758 {
4759 /* get next potetioal candidate */
4760 bdchginfo = (SCIP_BDCHGINFO*)(SCIPpqueueFirst(conflict->forcedbdchgqueue));
4761
4762 /* check if this candidate is valid */
4763 if( bdchginfoIsInvalid(conflict, bdchginfo) )
4764 {
4765 SCIPdebugMessage("bound change info [%d:<%s> %s %g] is invaild -> pop it from the force queue\n", SCIPbdchginfoGetDepth(bdchginfo),
4766 SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
4767 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4768 SCIPbdchginfoGetNewbound(bdchginfo));
4769
4770 /* pop the invalid bound change info from the queue */
4771 (void)(SCIPpqueueRemove(conflict->forcedbdchgqueue));
4772
4773 /* call method recursively to get next conflict analysis candidate */
4774 bdchginfo = conflictFirstCand(conflict);
4775 }
4776 }
4777 else
4778 {
4779 bdchginfo = (SCIP_BDCHGINFO*)(SCIPpqueueFirst(conflict->bdchgqueue));
4780
4781 /* check if this candidate is valid */
4782 if( bdchginfo != NULL && bdchginfoIsInvalid(conflict, bdchginfo) )
4783 {
4784 SCIPdebugMessage("bound change info [%d:<%s> %s %g] is invaild -> pop it from the queue\n", SCIPbdchginfoGetDepth(bdchginfo),
4785 SCIPvarGetName(SCIPbdchginfoGetVar(bdchginfo)),
4786 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4787 SCIPbdchginfoGetNewbound(bdchginfo));
4788
4789 /* pop the invalid bound change info from the queue */
4790 (void)(SCIPpqueueRemove(conflict->bdchgqueue));
4791
4792 /* call method recursively to get next conflict analysis candidate */
4793 bdchginfo = conflictFirstCand(conflict);
4794 }
4795 }
4796 assert(bdchginfo == NULL || !SCIPbdchginfoIsRedundant(bdchginfo));
4797
4798 return bdchginfo;
4799 }
4800
4801 /** adds the current conflict set (extended by all remaining bound changes in the queue) to the pool of conflict sets */
4802 static
conflictAddConflictset(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_TREE * tree,int validdepth,SCIP_Bool diving,SCIP_Bool repropagate,SCIP_Bool * success,int * nliterals)4803 SCIP_RETCODE conflictAddConflictset(
4804 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4805 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
4806 SCIP_SET* set, /**< global SCIP settings */
4807 SCIP_STAT* stat, /**< dynamic problem statistics */
4808 SCIP_TREE* tree, /**< branch and bound tree */
4809 int validdepth, /**< minimal depth level at which the conflict set is valid */
4810 SCIP_Bool diving, /**< are we in strong branching or diving mode? */
4811 SCIP_Bool repropagate, /**< should the constraint trigger a repropagation? */
4812 SCIP_Bool* success, /**< pointer to store whether the conflict set is valid */
4813 int* nliterals /**< pointer to store the number of literals in the generated conflictset */
4814 )
4815 {
4816 SCIP_CONFLICTSET* conflictset;
4817 SCIP_BDCHGINFO** bdchginfos;
4818 int nbdchginfos;
4819 int currentdepth;
4820 int focusdepth;
4821
4822 assert(conflict != NULL);
4823 assert(conflict->conflictset != NULL);
4824 assert(set != NULL);
4825 assert(stat != NULL);
4826 assert(tree != NULL);
4827 assert(success != NULL);
4828 assert(nliterals != NULL);
4829 assert(SCIPpqueueNElems(conflict->forcedbdchgqueue) == 0);
4830
4831 *success = FALSE;
4832 *nliterals = 0;
4833
4834 /* check, whether local conflicts are allowed */
4835 validdepth = MAX(validdepth, conflict->conflictset->validdepth);
4836 if( !set->conf_allowlocal && validdepth > 0 )
4837 return SCIP_OKAY;
4838
4839 focusdepth = SCIPtreeGetFocusDepth(tree);
4840 currentdepth = SCIPtreeGetCurrentDepth(tree);
4841 assert(currentdepth == tree->pathlen-1);
4842 assert(focusdepth <= currentdepth);
4843 assert(0 <= conflict->conflictset->validdepth && conflict->conflictset->validdepth <= currentdepth);
4844 assert(0 <= validdepth && validdepth <= currentdepth);
4845
4846 /* get the elements of the bound change queue */
4847 bdchginfos = (SCIP_BDCHGINFO**)SCIPpqueueElems(conflict->bdchgqueue);
4848 nbdchginfos = SCIPpqueueNElems(conflict->bdchgqueue);
4849
4850 /* create a copy of the current conflict set, allocating memory for the additional elements of the queue */
4851 SCIP_CALL( conflictsetCopy(&conflictset, blkmem, conflict->conflictset, nbdchginfos) );
4852 conflictset->validdepth = validdepth;
4853 conflictset->repropagate = repropagate;
4854
4855 /* add the valid queue elements to the conflict set */
4856 SCIPsetDebugMsg(set, "adding %d variables from the queue as temporary conflict variables\n", nbdchginfos);
4857 SCIP_CALL( conflictsetAddBounds(conflict, conflictset, blkmem, set, bdchginfos, nbdchginfos) );
4858
4859 /* calculate the depth, at which the conflictset should be inserted */
4860 SCIP_CALL( conflictsetCalcInsertDepth(conflictset, set, tree) );
4861 assert(conflictset->validdepth <= conflictset->insertdepth && conflictset->insertdepth <= currentdepth);
4862 SCIPsetDebugMsg(set, " -> conflict with %d literals found at depth %d is active in depth %d and valid in depth %d\n",
4863 conflictset->nbdchginfos, currentdepth, conflictset->insertdepth, conflictset->validdepth);
4864
4865 /* if all branching variables are in the conflict set, the conflict set is of no use;
4866 * don't use conflict sets that are only valid in the probing path but not in the problem tree
4867 */
4868 if( (diving || conflictset->insertdepth < currentdepth) && conflictset->insertdepth <= focusdepth )
4869 {
4870 /* if the conflict should not be located only in the subtree where it is useful, put it to its valid depth level */
4871 if( !set->conf_settlelocal )
4872 conflictset->insertdepth = conflictset->validdepth;
4873
4874 *nliterals = conflictset->nbdchginfos;
4875 SCIPsetDebugMsg(set, " -> final conflict set has %d literals\n", *nliterals);
4876
4877 /* check conflict set on debugging solution */
4878 SCIP_CALL( SCIPdebugCheckConflict(blkmem, set, tree->path[validdepth], \
4879 conflictset->bdchginfos, conflictset->relaxedbds, conflictset->nbdchginfos) ); /*lint !e506 !e774*/
4880
4881 /* move conflictset to the conflictset storage */
4882 SCIP_CALL( conflictInsertConflictset(conflict, blkmem, set, &conflictset) );
4883 *success = TRUE;
4884 }
4885 else
4886 {
4887 /* free the temporary conflict set */
4888 conflictsetFree(&conflictset, blkmem);
4889 }
4890
4891 return SCIP_OKAY;
4892 }
4893
4894 /** tries to resolve given bound change
4895 * - resolutions on local constraints are only applied, if the constraint is valid at the
4896 * current minimal valid depth level, because this depth level is the topmost level to add the conflict
4897 * constraint to anyways
4898 *
4899 * @note it is sufficient to explain the relaxed bound change
4900 */
4901 static
conflictResolveBound(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_BDCHGINFO * bdchginfo,SCIP_Real relaxedbd,int validdepth,SCIP_Bool * resolved)4902 SCIP_RETCODE conflictResolveBound(
4903 SCIP_CONFLICT* conflict, /**< conflict analysis data */
4904 SCIP_SET* set, /**< global SCIP settings */
4905 SCIP_BDCHGINFO* bdchginfo, /**< bound change to resolve */
4906 SCIP_Real relaxedbd, /**< the relaxed bound */
4907 int validdepth, /**< minimal depth level at which the conflict is valid */
4908 SCIP_Bool* resolved /**< pointer to store whether the bound change was resolved */
4909 )
4910 {
4911 SCIP_VAR* actvar;
4912 SCIP_CONS* infercons;
4913 SCIP_PROP* inferprop;
4914 SCIP_RESULT result;
4915
4916 #ifndef NDEBUG
4917 int nforcedbdchgqueue;
4918 int nbdchgqueue;
4919
4920 /* store the current size of the conflict queues */
4921 assert(conflict != NULL);
4922 nforcedbdchgqueue = SCIPpqueueNElems(conflict->forcedbdchgqueue);
4923 nbdchgqueue = SCIPpqueueNElems(conflict->bdchgqueue);
4924 #else
4925 assert(conflict != NULL);
4926 #endif
4927
4928 assert(resolved != NULL);
4929 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
4930
4931 *resolved = FALSE;
4932
4933 actvar = SCIPbdchginfoGetVar(bdchginfo);
4934 assert(actvar != NULL);
4935 assert(SCIPvarIsActive(actvar));
4936
4937 #ifdef SCIP_DEBUG
4938 {
4939 int i;
4940 SCIPsetDebugMsg(set, "processing next conflicting bound (depth: %d, valid depth: %d, bdchgtype: %s [%s], vartype: %d): [<%s> %s %g(%g)]\n",
4941 SCIPbdchginfoGetDepth(bdchginfo), validdepth,
4942 SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_BRANCHING ? "branch"
4943 : SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_CONSINFER ? "cons" : "prop",
4944 SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_BRANCHING ? "-"
4945 : SCIPbdchginfoGetChgtype(bdchginfo) == SCIP_BOUNDCHGTYPE_CONSINFER
4946 ? SCIPconsGetName(SCIPbdchginfoGetInferCons(bdchginfo))
4947 : SCIPbdchginfoGetInferProp(bdchginfo) == NULL ? "-"
4948 : SCIPpropGetName(SCIPbdchginfoGetInferProp(bdchginfo)),
4949 SCIPvarGetType(actvar), SCIPvarGetName(actvar),
4950 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4951 SCIPbdchginfoGetNewbound(bdchginfo), relaxedbd);
4952 SCIPsetDebugMsg(set, " - conflict set :");
4953
4954 for( i = 0; i < conflict->conflictset->nbdchginfos; ++i )
4955 {
4956 SCIPsetDebugMsgPrint(set, " [%d:<%s> %s %g(%g)]", SCIPbdchginfoGetDepth(conflict->conflictset->bdchginfos[i]),
4957 SCIPvarGetName(SCIPbdchginfoGetVar(conflict->conflictset->bdchginfos[i])),
4958 SCIPbdchginfoGetBoundtype(conflict->conflictset->bdchginfos[i]) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4959 SCIPbdchginfoGetNewbound(conflict->conflictset->bdchginfos[i]), conflict->conflictset->relaxedbds[i]);
4960 }
4961 SCIPsetDebugMsgPrint(set, "\n");
4962 SCIPsetDebugMsg(set, " - forced candidates :");
4963
4964 for( i = 0; i < SCIPpqueueNElems(conflict->forcedbdchgqueue); ++i )
4965 {
4966 SCIP_BDCHGINFO* info = (SCIP_BDCHGINFO*)(SCIPpqueueElems(conflict->forcedbdchgqueue)[i]);
4967 SCIPsetDebugMsgPrint(set, " [%d:<%s> %s %g(%g)]", SCIPbdchginfoGetDepth(info), SCIPvarGetName(SCIPbdchginfoGetVar(info)),
4968 bdchginfoIsInvalid(conflict, info) ? "<!>" : SCIPbdchginfoGetBoundtype(info) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4969 SCIPbdchginfoGetNewbound(info), SCIPbdchginfoGetRelaxedBound(info));
4970 }
4971 SCIPsetDebugMsgPrint(set, "\n");
4972 SCIPsetDebugMsg(set, " - optional candidates:");
4973
4974 for( i = 0; i < SCIPpqueueNElems(conflict->bdchgqueue); ++i )
4975 {
4976 SCIP_BDCHGINFO* info = (SCIP_BDCHGINFO*)(SCIPpqueueElems(conflict->bdchgqueue)[i]);
4977 SCIPsetDebugMsgPrint(set, " [%d:<%s> %s %g(%g)]", SCIPbdchginfoGetDepth(info), SCIPvarGetName(SCIPbdchginfoGetVar(info)),
4978 bdchginfoIsInvalid(conflict, info) ? "<!>" : SCIPbdchginfoGetBoundtype(info) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
4979 SCIPbdchginfoGetNewbound(info), SCIPbdchginfoGetRelaxedBound(info));
4980 }
4981 SCIPsetDebugMsgPrint(set, "\n");
4982 }
4983 #endif
4984
4985 /* check, if the bound change can and should be resolved:
4986 * - resolutions on local constraints should only be applied, if the constraint is valid at the
4987 * current minimal valid depth level (which is initialized with the valid depth level of the initial
4988 * conflict set), because this depth level is the topmost level to add the conflict constraint to anyways
4989 */
4990 switch( SCIPbdchginfoGetChgtype(bdchginfo) )
4991 {
4992 case SCIP_BOUNDCHGTYPE_CONSINFER:
4993 infercons = SCIPbdchginfoGetInferCons(bdchginfo);
4994 assert(infercons != NULL);
4995
4996 if( SCIPconsIsGlobal(infercons) || SCIPconsGetValidDepth(infercons) <= validdepth )
4997 {
4998 SCIP_VAR* infervar;
4999 int inferinfo;
5000 SCIP_BOUNDTYPE inferboundtype;
5001 SCIP_BDCHGIDX* bdchgidx;
5002
5003 /* resolve bound change by asking the constraint that infered the bound to put all bounds that were
5004 * the reasons for the conflicting bound change on the priority queue
5005 */
5006 infervar = SCIPbdchginfoGetInferVar(bdchginfo);
5007 inferinfo = SCIPbdchginfoGetInferInfo(bdchginfo);
5008 inferboundtype = SCIPbdchginfoGetInferBoundtype(bdchginfo);
5009 bdchgidx = SCIPbdchginfoGetIdx(bdchginfo);
5010 assert(infervar != NULL);
5011
5012 SCIPsetDebugMsg(set, "resolving bound <%s> %s %g(%g) [status:%d, type:%d, depth:%d, pos:%d]: <%s> %s %g [cons:<%s>(%s), info:%d]\n",
5013 SCIPvarGetName(actvar),
5014 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
5015 SCIPbdchginfoGetNewbound(bdchginfo), relaxedbd,
5016 SCIPvarGetStatus(actvar), SCIPvarGetType(actvar),
5017 SCIPbdchginfoGetDepth(bdchginfo), SCIPbdchginfoGetPos(bdchginfo),
5018 SCIPvarGetName(infervar),
5019 inferboundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
5020 SCIPgetVarBdAtIndex(set->scip, infervar, inferboundtype, bdchgidx, TRUE),
5021 SCIPconsGetName(infercons),
5022 SCIPconsIsGlobal(infercons) ? "global" : "local",
5023 inferinfo);
5024
5025 /* in case the inference variables is not an active variables, we need to transform the relaxed bound */
5026 if( actvar != infervar )
5027 {
5028 SCIP_VAR* var;
5029 SCIP_Real scalar;
5030 SCIP_Real constant;
5031
5032 assert(SCIPvarGetStatus(infervar) == SCIP_VARSTATUS_AGGREGATED
5033 || SCIPvarGetStatus(infervar) == SCIP_VARSTATUS_NEGATED
5034 || (SCIPvarGetStatus(infervar) == SCIP_VARSTATUS_MULTAGGR && SCIPvarGetMultaggrNVars(infervar) == 1));
5035
5036 scalar = 1.0;
5037 constant = 0.0;
5038
5039 var = infervar;
5040
5041 /* transform given varibale to active varibale */
5042 SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &scalar, &constant) );
5043 assert(var == actvar);
5044
5045 relaxedbd *= scalar;
5046 relaxedbd += constant;
5047 }
5048
5049 SCIP_CALL( SCIPconsResolvePropagation(infercons, set, infervar, inferinfo, inferboundtype, bdchgidx, relaxedbd, &result) );
5050 *resolved = (result == SCIP_SUCCESS);
5051 }
5052 break;
5053
5054 case SCIP_BOUNDCHGTYPE_PROPINFER:
5055 inferprop = SCIPbdchginfoGetInferProp(bdchginfo);
5056 if( inferprop != NULL )
5057 {
5058 SCIP_VAR* infervar;
5059 int inferinfo;
5060 SCIP_BOUNDTYPE inferboundtype;
5061 SCIP_BDCHGIDX* bdchgidx;
5062
5063 /* resolve bound change by asking the propagator that infered the bound to put all bounds that were
5064 * the reasons for the conflicting bound change on the priority queue
5065 */
5066 infervar = SCIPbdchginfoGetInferVar(bdchginfo);
5067 inferinfo = SCIPbdchginfoGetInferInfo(bdchginfo);
5068 inferboundtype = SCIPbdchginfoGetInferBoundtype(bdchginfo);
5069 bdchgidx = SCIPbdchginfoGetIdx(bdchginfo);
5070 assert(infervar != NULL);
5071
5072 SCIPsetDebugMsg(set, "resolving bound <%s> %s %g(%g) [status:%d, depth:%d, pos:%d]: <%s> %s %g [prop:<%s>, info:%d]\n",
5073 SCIPvarGetName(actvar),
5074 SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
5075 SCIPbdchginfoGetNewbound(bdchginfo), relaxedbd,
5076 SCIPvarGetStatus(actvar), SCIPbdchginfoGetDepth(bdchginfo), SCIPbdchginfoGetPos(bdchginfo),
5077 SCIPvarGetName(infervar),
5078 inferboundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
5079 SCIPgetVarBdAtIndex(set->scip, infervar, inferboundtype, bdchgidx, TRUE),
5080 SCIPpropGetName(inferprop), inferinfo);
5081
5082 SCIP_CALL( SCIPpropResolvePropagation(inferprop, set, infervar, inferinfo, inferboundtype, bdchgidx, relaxedbd, &result) );
5083 *resolved = (result == SCIP_SUCCESS);
5084 }
5085 break;
5086
5087 case SCIP_BOUNDCHGTYPE_BRANCHING:
5088 assert(!(*resolved));
5089 break;
5090
5091 default:
5092 SCIPerrorMessage("invalid bound change type <%d>\n", SCIPbdchginfoGetChgtype(bdchginfo));
5093 return SCIP_INVALIDDATA;
5094 }
5095
5096 SCIPsetDebugMsg(set, "resolving status: %u\n", *resolved);
5097
5098 #ifndef NDEBUG
5099 /* subtract the size of the conflicq queues */
5100 nforcedbdchgqueue -= SCIPpqueueNElems(conflict->forcedbdchgqueue);
5101 nbdchgqueue -= SCIPpqueueNElems(conflict->bdchgqueue);
5102
5103 /* in case the bound change was not resolved, the conflict queues should have the same size (contents) */
5104 assert((*resolved) || (nforcedbdchgqueue == 0 && nbdchgqueue == 0));
5105 #endif
5106
5107 return SCIP_OKAY;
5108 }
5109
5110 /** if only one conflicting bound change of the last depth level was used, and if this can be resolved,
5111 * creates GRASP-like reconvergence conflict constraints in the conflict graph up to the branching variable of this
5112 * depth level
5113 */
5114 static
conflictCreateReconvergenceConss(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_TREE * tree,SCIP_Bool diving,int validdepth,SCIP_BDCHGINFO * firstuip,int * nreconvconss,int * nreconvliterals)5115 SCIP_RETCODE conflictCreateReconvergenceConss(
5116 SCIP_CONFLICT* conflict, /**< conflict analysis data */
5117 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
5118 SCIP_SET* set, /**< global SCIP settings */
5119 SCIP_STAT* stat, /**< problem statistics */
5120 SCIP_PROB* prob, /**< problem data */
5121 SCIP_TREE* tree, /**< branch and bound tree */
5122 SCIP_Bool diving, /**< are we in strong branching or diving mode? */
5123 int validdepth, /**< minimal depth level at which the initial conflict set is valid */
5124 SCIP_BDCHGINFO* firstuip, /**< first UIP of conflict graph */
5125 int* nreconvconss, /**< pointer to store the number of generated reconvergence constraints */
5126 int* nreconvliterals /**< pointer to store the number of literals generated reconvergence constraints */
5127 )
5128 {
5129 SCIP_BDCHGINFO* uip;
5130 SCIP_CONFTYPE conftype;
5131 SCIP_Bool usescutoffbound;
5132 int firstuipdepth;
5133 int focusdepth;
5134 int currentdepth;
5135 int maxvaliddepth;
5136
5137 assert(conflict != NULL);
5138 assert(firstuip != NULL);
5139 assert(nreconvconss != NULL);
5140 assert(nreconvliterals != NULL);
5141 assert(!SCIPbdchginfoIsRedundant(firstuip));
5142
5143 focusdepth = SCIPtreeGetFocusDepth(tree);
5144 currentdepth = SCIPtreeGetCurrentDepth(tree);
5145 assert(currentdepth == tree->pathlen-1);
5146 assert(focusdepth <= currentdepth);
5147
5148 /* check, whether local constraints are allowed; however, don't generate reconvergence constraints that are only valid
5149 * in the probing path and not in the problem tree (i.e. that exceed the focusdepth)
5150 */
5151 maxvaliddepth = (set->conf_allowlocal ? MIN(currentdepth-1, focusdepth) : 0);
5152 if( validdepth > maxvaliddepth )
5153 return SCIP_OKAY;
5154
5155 firstuipdepth = SCIPbdchginfoGetDepth(firstuip);
5156
5157 conftype = conflict->conflictset->conflicttype;
5158 usescutoffbound = conflict->conflictset->usescutoffbound;
5159
5160 /* for each succeeding UIP pair of the last depth level, create one reconvergence constraint */
5161 uip = firstuip;
5162 while( uip != NULL && SCIPbdchginfoGetDepth(uip) == SCIPbdchginfoGetDepth(firstuip) && bdchginfoIsResolvable(uip) )
5163 {
5164 SCIP_BDCHGINFO* oppositeuip;
5165 SCIP_BDCHGINFO* bdchginfo;
5166 SCIP_BDCHGINFO* nextuip;
5167 SCIP_VAR* uipvar;
5168 SCIP_Real oppositeuipbound;
5169 SCIP_BOUNDTYPE oppositeuipboundtype;
5170 int nresolutions;
5171
5172 assert(!SCIPbdchginfoIsRedundant(uip));
5173
5174 SCIPsetDebugMsg(set, "creating reconvergence constraint for UIP <%s> %s %g in depth %d pos %d\n",
5175 SCIPvarGetName(SCIPbdchginfoGetVar(uip)), SCIPbdchginfoGetBoundtype(uip) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
5176 SCIPbdchginfoGetNewbound(uip), SCIPbdchginfoGetDepth(uip), SCIPbdchginfoGetPos(uip));
5177
5178 /* initialize conflict data */
5179 SCIP_CALL( SCIPconflictInit(conflict, set, stat, prob, conftype, usescutoffbound) );
5180
5181 conflict->conflictset->conflicttype = conftype;
5182 conflict->conflictset->usescutoffbound = usescutoffbound;
5183
5184 /* create a temporary bound change information for the negation of the UIP's bound change;
5185 * this bound change information is freed in the SCIPconflictFlushConss() call;
5186 * for reconvergence constraints for continuous variables we can only use the "negation" !(x <= u) == (x >= u);
5187 * during conflict analysis, we treat a continuous bound "x >= u" in the conflict set as "x > u", and in the
5188 * generated constraint this is negated again to "x <= u" which is correct.
5189 */
5190 uipvar = SCIPbdchginfoGetVar(uip);
5191 oppositeuipboundtype = SCIPboundtypeOpposite(SCIPbdchginfoGetBoundtype(uip));
5192 oppositeuipbound = SCIPbdchginfoGetNewbound(uip);
5193 if( SCIPvarIsIntegral(uipvar) )
5194 {
5195 assert(SCIPsetIsIntegral(set, oppositeuipbound));
5196 oppositeuipbound += (oppositeuipboundtype == SCIP_BOUNDTYPE_LOWER ? +1.0 : -1.0);
5197 }
5198 SCIP_CALL( conflictCreateTmpBdchginfo(conflict, blkmem, set, uipvar, oppositeuipboundtype, \
5199 oppositeuipboundtype == SCIP_BOUNDTYPE_LOWER ? SCIP_REAL_MIN : SCIP_REAL_MAX, oppositeuipbound, &oppositeuip) );
5200
5201 /* put the negated UIP into the conflict set */
5202 SCIP_CALL( conflictAddConflictBound(conflict, blkmem, set, oppositeuip, oppositeuipbound) );
5203
5204 /* put positive UIP into priority queue */
5205 SCIP_CALL( conflictQueueBound(conflict, set, uip, SCIPbdchginfoGetNewbound(uip) ) );
5206
5207 /* resolve the queue until the next UIP is reached */
5208 bdchginfo = conflictFirstCand(conflict);
5209 nextuip = NULL;
5210 nresolutions = 0;
5211 while( bdchginfo != NULL && validdepth <= maxvaliddepth )
5212 {
5213 SCIP_BDCHGINFO* nextbdchginfo;
5214 SCIP_Real relaxedbd;
5215 SCIP_Bool forceresolve;
5216 int bdchgdepth;
5217
5218 /* check if the next bound change must be resolved in every case */
5219 forceresolve = (SCIPpqueueNElems(conflict->forcedbdchgqueue) > 0);
5220
5221 /* remove currently processed candidate and get next conflicting bound from the conflict candidate queue before
5222 * we remove the candidate we have to collect the relaxed bound since removing the candidate from the queue
5223 * invalidates the relaxed bound
5224 */
5225 assert(bdchginfo == conflictFirstCand(conflict));
5226 relaxedbd = SCIPbdchginfoGetRelaxedBound(bdchginfo);
5227 bdchginfo = conflictRemoveCand(conflict);
5228 nextbdchginfo = conflictFirstCand(conflict);
5229 bdchgdepth = SCIPbdchginfoGetDepth(bdchginfo);
5230 assert(bdchginfo != NULL);
5231 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
5232 assert(nextbdchginfo == NULL || SCIPbdchginfoGetDepth(bdchginfo) >= SCIPbdchginfoGetDepth(nextbdchginfo)
5233 || forceresolve);
5234 assert(bdchgdepth <= firstuipdepth);
5235
5236 /* bound changes that are higher in the tree than the valid depth of the conflict can be ignored;
5237 * multiple insertions of the same bound change can be ignored
5238 */
5239 if( bdchgdepth > validdepth && bdchginfo != nextbdchginfo )
5240 {
5241 SCIP_VAR* actvar;
5242 SCIP_Bool resolved;
5243
5244 actvar = SCIPbdchginfoGetVar(bdchginfo);
5245 assert(actvar != NULL);
5246 assert(SCIPvarIsActive(actvar));
5247
5248 /* check if we have to resolve the bound change in this depth level
5249 * - the starting uip has to be resolved
5250 * - a bound change should be resolved, if it is in the fuip's depth level and not the
5251 * next uip (i.e., if it is not the last bound change in the fuip's depth level)
5252 * - a forced bound change must be resolved in any case
5253 */
5254 resolved = FALSE;
5255 if( bdchginfo == uip
5256 || (bdchgdepth == firstuipdepth
5257 && nextbdchginfo != NULL
5258 && SCIPbdchginfoGetDepth(nextbdchginfo) == bdchgdepth)
5259 || forceresolve )
5260 {
5261 SCIP_CALL( conflictResolveBound(conflict, set, bdchginfo, relaxedbd, validdepth, &resolved) );
5262 }
5263
5264 if( resolved )
5265 nresolutions++;
5266 else if( forceresolve )
5267 {
5268 /* variable cannot enter the conflict clause: we have to make the conflict clause local, s.t.
5269 * the unresolved bound change is active in the whole sub tree of the conflict clause
5270 */
5271 assert(bdchgdepth >= validdepth);
5272 validdepth = bdchgdepth;
5273
5274 SCIPsetDebugMsg(set, "couldn't resolve forced bound change on <%s> -> new valid depth: %d\n",
5275 SCIPvarGetName(actvar), validdepth);
5276 }
5277 else if( bdchginfo != uip )
5278 {
5279 assert(conflict->conflictset != NULL);
5280 assert(conflict->conflictset->nbdchginfos >= 1); /* starting UIP is already member of the conflict set */
5281
5282 /* if this is the first variable of the conflict set besides the current starting UIP, it is the next
5283 * UIP (or the first unresolvable bound change)
5284 */
5285 if( bdchgdepth == firstuipdepth && conflict->conflictset->nbdchginfos == 1 )
5286 {
5287 assert(nextuip == NULL);
5288 nextuip = bdchginfo;
5289 }
5290
5291 /* put bound change into the conflict set */
5292 SCIP_CALL( conflictAddConflictBound(conflict, blkmem, set, bdchginfo, relaxedbd) );
5293 assert(conflict->conflictset->nbdchginfos >= 2);
5294 }
5295 else
5296 assert(conflictFirstCand(conflict) == NULL); /* the starting UIP was not resolved */
5297 }
5298
5299 /* get next conflicting bound from the conflict candidate queue (this does not need to be nextbdchginfo, because
5300 * due to resolving the bound changes, a variable could be added to the queue which must be
5301 * resolved before nextbdchginfo)
5302 */
5303 bdchginfo = conflictFirstCand(conflict);
5304 }
5305 assert(nextuip != uip);
5306
5307 /* if only one propagation was resolved, the reconvergence constraint is already member of the constraint set
5308 * (it is exactly the constraint that produced the propagation)
5309 */
5310 if( nextuip != NULL && nresolutions >= 2 && bdchginfo == NULL && validdepth <= maxvaliddepth )
5311 {
5312 int nlits;
5313 SCIP_Bool success;
5314
5315 assert(SCIPbdchginfoGetDepth(nextuip) == SCIPbdchginfoGetDepth(uip));
5316
5317 /* check conflict graph frontier on debugging solution */
5318 SCIP_CALL( SCIPdebugCheckConflictFrontier(blkmem, set, tree->path[validdepth], \
5319 bdchginfo, conflict->conflictset->bdchginfos, conflict->conflictset->relaxedbds, \
5320 conflict->conflictset->nbdchginfos, conflict->bdchgqueue, conflict->forcedbdchgqueue) ); /*lint !e506 !e774*/
5321
5322 SCIPsetDebugMsg(set, "creating reconvergence constraint from UIP <%s> to UIP <%s> in depth %d with %d literals after %d resolutions\n",
5323 SCIPvarGetName(SCIPbdchginfoGetVar(uip)), SCIPvarGetName(SCIPbdchginfoGetVar(nextuip)),
5324 SCIPbdchginfoGetDepth(uip), conflict->conflictset->nbdchginfos, nresolutions);
5325
5326 /* call the conflict handlers to create a conflict set */
5327 SCIP_CALL( conflictAddConflictset(conflict, blkmem, set, stat, tree, validdepth, diving, FALSE, &success, &nlits) );
5328 if( success )
5329 {
5330 (*nreconvconss)++;
5331 (*nreconvliterals) += nlits;
5332 }
5333 }
5334
5335 /* clear the conflict candidate queue and the conflict set (to make sure, oppositeuip is not referenced anymore) */
5336 conflictClear(conflict);
5337
5338 uip = nextuip;
5339 }
5340
5341 conflict->conflictset->conflicttype = conftype;
5342 conflict->conflictset->usescutoffbound = usescutoffbound;
5343
5344 return SCIP_OKAY;
5345 }
5346
5347 /** analyzes conflicting bound changes that were added with calls to SCIPconflictAddBound() and
5348 * SCIPconflictAddRelaxedBound(), and on success, calls the conflict handlers to create a conflict constraint out of
5349 * the resulting conflict set; afterwards the conflict queue and the conflict set is cleared
5350 */
5351 static
conflictAnalyze(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_TREE * tree,SCIP_Bool diving,int validdepth,SCIP_Bool mustresolve,int * nconss,int * nliterals,int * nreconvconss,int * nreconvliterals)5352 SCIP_RETCODE conflictAnalyze(
5353 SCIP_CONFLICT* conflict, /**< conflict analysis data */
5354 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
5355 SCIP_SET* set, /**< global SCIP settings */
5356 SCIP_STAT* stat, /**< problem statistics */
5357 SCIP_PROB* prob, /**< problem data */
5358 SCIP_TREE* tree, /**< branch and bound tree */
5359 SCIP_Bool diving, /**< are we in strong branching or diving mode? */
5360 int validdepth, /**< minimal depth level at which the initial conflict set is valid */
5361 SCIP_Bool mustresolve, /**< should the conflict set only be used, if a resolution was applied? */
5362 int* nconss, /**< pointer to store the number of generated conflict constraints */
5363 int* nliterals, /**< pointer to store the number of literals in generated conflict constraints */
5364 int* nreconvconss, /**< pointer to store the number of generated reconvergence constraints */
5365 int* nreconvliterals /**< pointer to store the number of literals generated reconvergence constraints */
5366 )
5367 {
5368 SCIP_BDCHGINFO* bdchginfo;
5369 SCIP_BDCHGINFO** firstuips;
5370 SCIP_CONFTYPE conftype;
5371 int nfirstuips;
5372 int focusdepth;
5373 int currentdepth;
5374 int maxvaliddepth;
5375 int resolvedepth;
5376 int nresolutions;
5377 int lastconsnresolutions;
5378 int lastconsresoldepth;
5379
5380 assert(conflict != NULL);
5381 assert(conflict->conflictset != NULL);
5382 assert(conflict->conflictset->nbdchginfos >= 0);
5383 assert(set != NULL);
5384 assert(stat != NULL);
5385 assert(0 <= validdepth && validdepth <= SCIPtreeGetCurrentDepth(tree));
5386 assert(nconss != NULL);
5387 assert(nliterals != NULL);
5388 assert(nreconvconss != NULL);
5389 assert(nreconvliterals != NULL);
5390
5391 focusdepth = SCIPtreeGetFocusDepth(tree);
5392 currentdepth = SCIPtreeGetCurrentDepth(tree);
5393 assert(currentdepth == tree->pathlen-1);
5394 assert(focusdepth <= currentdepth);
5395
5396 resolvedepth = ((set->conf_fuiplevels >= 0 && set->conf_fuiplevels <= currentdepth)
5397 ? currentdepth - set->conf_fuiplevels + 1 : 0);
5398 assert(0 <= resolvedepth && resolvedepth <= currentdepth + 1);
5399
5400 /* if we must resolve at least one bound change, find the first UIP at least in the last depth level */
5401 if( mustresolve )
5402 resolvedepth = MIN(resolvedepth, currentdepth);
5403
5404 SCIPsetDebugMsg(set, "analyzing conflict with %d+%d conflict candidates and starting conflict set of size %d in depth %d (resolvedepth=%d)\n",
5405 SCIPpqueueNElems(conflict->forcedbdchgqueue), SCIPpqueueNElems(conflict->bdchgqueue),
5406 conflict->conflictset->nbdchginfos, currentdepth, resolvedepth);
5407
5408 *nconss = 0;
5409 *nliterals = 0;
5410 *nreconvconss = 0;
5411 *nreconvliterals = 0;
5412
5413 /* check, whether local conflicts are allowed; however, don't generate conflict constraints that are only valid in the
5414 * probing path and not in the problem tree (i.e. that exceed the focusdepth)
5415 */
5416 maxvaliddepth = (set->conf_allowlocal ? MIN(currentdepth-1, focusdepth) : 0);
5417 if( validdepth > maxvaliddepth )
5418 return SCIP_OKAY;
5419
5420 /* allocate temporary memory for storing first UIPs (in each depth level, at most two bound changes can be flagged
5421 * as UIP, namely a binary and a non-binary bound change)
5422 */
5423 SCIP_CALL( SCIPsetAllocBufferArray(set, &firstuips, 2*(currentdepth+1)) ); /*lint !e647*/
5424
5425 /* process all bound changes in the conflict candidate queue */
5426 nresolutions = 0;
5427 lastconsnresolutions = (mustresolve ? 0 : -1);
5428 lastconsresoldepth = (mustresolve ? currentdepth : INT_MAX);
5429 bdchginfo = conflictFirstCand(conflict);
5430 nfirstuips = 0;
5431
5432 /* check if the initial reason on debugging solution */
5433 SCIP_CALL( SCIPdebugCheckConflictFrontier(blkmem, set, tree->path[validdepth], \
5434 NULL, conflict->conflictset->bdchginfos, conflict->conflictset->relaxedbds, conflict->conflictset->nbdchginfos, \
5435 conflict->bdchgqueue, conflict->forcedbdchgqueue) ); /*lint !e506 !e774*/
5436
5437 while( bdchginfo != NULL && validdepth <= maxvaliddepth )
5438 {
5439 SCIP_BDCHGINFO* nextbdchginfo;
5440 SCIP_Real relaxedbd;
5441 SCIP_Bool forceresolve;
5442 int bdchgdepth;
5443
5444 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
5445
5446 /* check if the next bound change must be resolved in every case */
5447 forceresolve = (SCIPpqueueNElems(conflict->forcedbdchgqueue) > 0);
5448
5449 /* resolve next bound change in queue */
5450 bdchgdepth = SCIPbdchginfoGetDepth(bdchginfo);
5451 assert(0 <= bdchgdepth && bdchgdepth <= currentdepth);
5452 assert(SCIPvarIsActive(SCIPbdchginfoGetVar(bdchginfo)));
5453 assert(bdchgdepth < tree->pathlen);
5454 assert(tree->path[bdchgdepth] != NULL);
5455 assert(tree->path[bdchgdepth]->domchg != NULL);
5456 assert(SCIPbdchginfoGetPos(bdchginfo) < (int)tree->path[bdchgdepth]->domchg->domchgbound.nboundchgs);
5457 assert(tree->path[bdchgdepth]->domchg->domchgbound.boundchgs[SCIPbdchginfoGetPos(bdchginfo)].var
5458 == SCIPbdchginfoGetVar(bdchginfo));
5459 assert(tree->path[bdchgdepth]->domchg->domchgbound.boundchgs[SCIPbdchginfoGetPos(bdchginfo)].newbound
5460 == SCIPbdchginfoGetNewbound(bdchginfo)
5461 || (SCIPbdchginfoGetBoundtype(bdchginfo) == SCIP_BOUNDTYPE_LOWER
5462 ? SCIPvarGetLbGlobal(SCIPbdchginfoGetVar(bdchginfo)) : SCIPvarGetUbGlobal(SCIPbdchginfoGetVar(bdchginfo)))
5463 == SCIPbdchginfoGetNewbound(bdchginfo)); /*lint !e777*/
5464 assert((SCIP_BOUNDTYPE)tree->path[bdchgdepth]->domchg->domchgbound.boundchgs[SCIPbdchginfoGetPos(bdchginfo)].boundtype
5465 == SCIPbdchginfoGetBoundtype(bdchginfo));
5466
5467 /* create intermediate conflict constraint */
5468 assert(nresolutions >= lastconsnresolutions);
5469 if( !forceresolve )
5470 {
5471 if( nresolutions == lastconsnresolutions )
5472 lastconsresoldepth = bdchgdepth; /* all intermediate depth levels consisted of only unresolved bound changes */
5473 else if( bdchgdepth < lastconsresoldepth && (set->conf_interconss == -1 || *nconss < set->conf_interconss) )
5474 {
5475 int nlits;
5476 SCIP_Bool success;
5477
5478 /* call the conflict handlers to create a conflict set */
5479 SCIPsetDebugMsg(set, "creating intermediate conflictset after %d resolutions up to depth %d (valid at depth %d): %d conflict bounds, %d bounds in queue\n",
5480 nresolutions, bdchgdepth, validdepth, conflict->conflictset->nbdchginfos,
5481 SCIPpqueueNElems(conflict->bdchgqueue));
5482
5483 SCIP_CALL( conflictAddConflictset(conflict, blkmem, set, stat, tree, validdepth, diving, TRUE, &success, &nlits) );
5484 lastconsnresolutions = nresolutions;
5485 lastconsresoldepth = bdchgdepth;
5486 if( success )
5487 {
5488 (*nconss)++;
5489 (*nliterals) += nlits;
5490 }
5491 }
5492 }
5493
5494 /* remove currently processed candidate and get next conflicting bound from the conflict candidate queue before
5495 * we remove the candidate we have to collect the relaxed bound since removing the candidate from the queue
5496 * invalidates the relaxed bound
5497 */
5498 assert(bdchginfo == conflictFirstCand(conflict));
5499 relaxedbd = SCIPbdchginfoGetRelaxedBound(bdchginfo);
5500 bdchginfo = conflictRemoveCand(conflict);
5501 nextbdchginfo = conflictFirstCand(conflict);
5502 assert(bdchginfo != NULL);
5503 assert(!SCIPbdchginfoIsRedundant(bdchginfo));
5504 assert(nextbdchginfo == NULL || SCIPbdchginfoGetDepth(bdchginfo) >= SCIPbdchginfoGetDepth(nextbdchginfo)
5505 || forceresolve);
5506
5507 /* we don't need to resolve bound changes that are already active in the valid depth of the current conflict set,
5508 * because the conflict set can only be added locally at the valid depth, and all bound changes applied in this
5509 * depth or earlier can be removed from the conflict constraint, since they are already applied in the constraint's
5510 * subtree;
5511 * if the next bound change on the remaining queue is equal to the current bound change,
5512 * this is a multiple insertion in the conflict candidate queue and we can ignore the current
5513 * bound change
5514 */
5515 if( bdchgdepth > validdepth && bdchginfo != nextbdchginfo )
5516 {
5517 SCIP_VAR* actvar;
5518 SCIP_Bool resolved;
5519
5520 actvar = SCIPbdchginfoGetVar(bdchginfo);
5521 assert(actvar != NULL);
5522 assert(SCIPvarIsActive(actvar));
5523
5524 /* check if we want to resolve the bound change in this depth level
5525 * - bound changes should be resolved, if
5526 * (i) we must apply at least one resolution and didn't resolve a bound change yet, or
5527 * (ii) their depth level is at least equal to the minimal resolving depth, and
5528 * they are not the last remaining conflicting bound change in their depth level
5529 * (iii) the bound change resolving is forced (i.e., the forced queue was non-empty)
5530 */
5531 resolved = FALSE;
5532 if( (mustresolve && nresolutions == 0)
5533 || (bdchgdepth >= resolvedepth
5534 && nextbdchginfo != NULL
5535 && SCIPbdchginfoGetDepth(nextbdchginfo) == bdchgdepth)
5536 || forceresolve )
5537 {
5538 SCIP_CALL( conflictResolveBound(conflict, set, bdchginfo, relaxedbd, validdepth, &resolved) );
5539 }
5540
5541 if( resolved )
5542 nresolutions++;
5543 else if( forceresolve )
5544 {
5545 /* variable cannot enter the conflict clause: we have to make the conflict clause local, s.t.
5546 * the unresolved bound change is active in the whole sub tree of the conflict clause
5547 */
5548 assert(bdchgdepth >= validdepth);
5549 validdepth = bdchgdepth;
5550
5551 SCIPsetDebugMsg(set, "couldn't resolve forced bound change on <%s> -> new valid depth: %d\n",
5552 SCIPvarGetName(actvar), validdepth);
5553 }
5554 else
5555 {
5556 /* if this is a UIP (the last bound change in its depth level), it can be used to generate a
5557 * UIP reconvergence constraint
5558 */
5559 if( nextbdchginfo == NULL || SCIPbdchginfoGetDepth(nextbdchginfo) != bdchgdepth )
5560 {
5561 assert(nfirstuips < 2*(currentdepth+1));
5562 firstuips[nfirstuips] = bdchginfo;
5563 nfirstuips++;
5564 }
5565
5566 /* put variable into the conflict set, using the literal that is currently fixed to FALSE */
5567 SCIP_CALL( conflictAddConflictBound(conflict, blkmem, set, bdchginfo, relaxedbd) );
5568 }
5569 }
5570
5571 /* check conflict graph frontier on debugging solution */
5572 SCIP_CALL( SCIPdebugCheckConflictFrontier(blkmem, set, tree->path[validdepth], \
5573 bdchginfo, conflict->conflictset->bdchginfos, conflict->conflictset->relaxedbds, conflict->conflictset->nbdchginfos, \
5574 conflict->bdchgqueue, conflict->forcedbdchgqueue) ); /*lint !e506 !e774*/
5575
5576 /* get next conflicting bound from the conflict candidate queue (this needs not to be nextbdchginfo, because
5577 * due to resolving the bound changes, a bound change could be added to the queue which must be
5578 * resolved before nextbdchginfo)
5579 */
5580 bdchginfo = conflictFirstCand(conflict);
5581 }
5582
5583 /* check, if a valid conflict set was found */
5584 if( bdchginfo == NULL
5585 && nresolutions > lastconsnresolutions
5586 && validdepth <= maxvaliddepth
5587 && (!mustresolve || nresolutions > 0 || conflict->conflictset->nbdchginfos == 0)
5588 && SCIPpqueueNElems(conflict->forcedbdchgqueue) == 0 )
5589 {
5590 int nlits;
5591 SCIP_Bool success;
5592
5593 /* call the conflict handlers to create a conflict set */
5594 SCIP_CALL( conflictAddConflictset(conflict, blkmem, set, stat, tree, validdepth, diving, TRUE, &success, &nlits) );
5595 if( success )
5596 {
5597 (*nconss)++;
5598 (*nliterals) += nlits;
5599 }
5600 }
5601
5602 /* produce reconvergence constraints defined by succeeding UIP's of the last depth level */
5603 if( set->conf_reconvlevels != 0 && validdepth <= maxvaliddepth )
5604 {
5605 int reconvlevels;
5606 int i;
5607
5608 reconvlevels = (set->conf_reconvlevels == -1 ? INT_MAX : set->conf_reconvlevels);
5609 for( i = 0; i < nfirstuips; ++i )
5610 {
5611 if( SCIPbdchginfoHasInferenceReason(firstuips[i])
5612 && currentdepth - SCIPbdchginfoGetDepth(firstuips[i]) < reconvlevels )
5613 {
5614 SCIP_CALL( conflictCreateReconvergenceConss(conflict, blkmem, set, stat, prob, tree, diving, \
5615 validdepth, firstuips[i], nreconvconss, nreconvliterals) );
5616 }
5617 }
5618 }
5619
5620 /* free the temporary memory */
5621 SCIPsetFreeBufferArray(set, &firstuips);
5622
5623 /* store last conflict type */
5624 conftype = conflict->conflictset->conflicttype;
5625
5626 /* clear the conflict candidate queue and the conflict set */
5627 conflictClear(conflict);
5628
5629 /* restore last conflict type */
5630 conflict->conflictset->conflicttype = conftype;
5631
5632 return SCIP_OKAY;
5633 }
5634
5635 /** analyzes conflicting bound changes that were added with calls to SCIPconflictAddBound(), and on success, calls the
5636 * conflict handlers to create a conflict constraint out of the resulting conflict set;
5637 * updates statistics for propagation conflict analysis
5638 */
SCIPconflictAnalyze(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_TREE * tree,int validdepth,SCIP_Bool * success)5639 SCIP_RETCODE SCIPconflictAnalyze(
5640 SCIP_CONFLICT* conflict, /**< conflict analysis data */
5641 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
5642 SCIP_SET* set, /**< global SCIP settings */
5643 SCIP_STAT* stat, /**< problem statistics */
5644 SCIP_PROB* prob, /**< problem data */
5645 SCIP_TREE* tree, /**< branch and bound tree */
5646 int validdepth, /**< minimal depth level at which the initial conflict set is valid */
5647 SCIP_Bool* success /**< pointer to store whether a conflict constraint was created, or NULL */
5648 )
5649 {
5650 int nconss;
5651 int nliterals;
5652 int nreconvconss;
5653 int nreconvliterals;
5654
5655 assert(conflict != NULL);
5656 assert(conflict->conflictset != NULL);
5657 assert(set != NULL);
5658 assert(prob != NULL);
5659
5660 if( success != NULL )
5661 *success = FALSE;
5662
5663 /* check if the conflict analysis is applicable */
5664 if( !SCIPconflictApplicable(set) )
5665 return SCIP_OKAY;
5666
5667 /* check, if the conflict set will get too large with high probability */
5668 if( conflict->conflictset->nbdchginfos + SCIPpqueueNElems(conflict->bdchgqueue)
5669 + SCIPpqueueNElems(conflict->forcedbdchgqueue) >= 2*conflictCalcMaxsize(set, prob) )
5670 return SCIP_OKAY;
5671
5672 SCIPsetDebugMsg(set, "analyzing conflict after infeasible propagation in depth %d\n", SCIPtreeGetCurrentDepth(tree));
5673
5674 /* start timing */
5675 SCIPclockStart(conflict->propanalyzetime, set);
5676
5677 conflict->npropcalls++;
5678
5679 /* analyze the conflict set, and create a conflict constraint on success */
5680 SCIP_CALL( conflictAnalyze(conflict, blkmem, set, stat, prob, tree, FALSE, validdepth, TRUE, &nconss, &nliterals, \
5681 &nreconvconss, &nreconvliterals) );
5682 conflict->npropsuccess += (nconss > 0 ? 1 : 0);
5683 conflict->npropconfconss += nconss;
5684 conflict->npropconfliterals += nliterals;
5685 conflict->npropreconvconss += nreconvconss;
5686 conflict->npropreconvliterals += nreconvliterals;
5687 if( success != NULL )
5688 *success = (nconss > 0);
5689
5690 /* stop timing */
5691 SCIPclockStop(conflict->propanalyzetime, set);
5692
5693 return SCIP_OKAY;
5694 }
5695
5696 /** gets time in seconds used for preprocessing global conflict constraint before appliance */
SCIPconflictGetGlobalApplTime(SCIP_CONFLICT * conflict)5697 SCIP_Real SCIPconflictGetGlobalApplTime(
5698 SCIP_CONFLICT* conflict /**< conflict analysis data */
5699 )
5700 {
5701 assert(conflict != NULL);
5702
5703 return SCIPclockGetTime(conflict->dIBclock);
5704 }
5705
5706 /** gets time in seconds used for analyzing propagation conflicts */
SCIPconflictGetPropTime(SCIP_CONFLICT * conflict)5707 SCIP_Real SCIPconflictGetPropTime(
5708 SCIP_CONFLICT* conflict /**< conflict analysis data */
5709 )
5710 {
5711 assert(conflict != NULL);
5712
5713 return SCIPclockGetTime(conflict->propanalyzetime);
5714 }
5715
5716 /** gets number of calls to propagation conflict analysis */
SCIPconflictGetNPropCalls(SCIP_CONFLICT * conflict)5717 SCIP_Longint SCIPconflictGetNPropCalls(
5718 SCIP_CONFLICT* conflict /**< conflict analysis data */
5719 )
5720 {
5721 assert(conflict != NULL);
5722
5723 return conflict->npropcalls;
5724 }
5725
5726 /** gets number of calls to propagation conflict analysis that yield at least one conflict constraint */
SCIPconflictGetNPropSuccess(SCIP_CONFLICT * conflict)5727 SCIP_Longint SCIPconflictGetNPropSuccess(
5728 SCIP_CONFLICT* conflict /**< conflict analysis data */
5729 )
5730 {
5731 assert(conflict != NULL);
5732
5733 return conflict->npropsuccess;
5734 }
5735
5736 /** gets number of conflict constraints detected in propagation conflict analysis */
SCIPconflictGetNPropConflictConss(SCIP_CONFLICT * conflict)5737 SCIP_Longint SCIPconflictGetNPropConflictConss(
5738 SCIP_CONFLICT* conflict /**< conflict analysis data */
5739 )
5740 {
5741 assert(conflict != NULL);
5742
5743 return conflict->npropconfconss;
5744 }
5745
5746 /** gets total number of literals in conflict constraints created in propagation conflict analysis */
SCIPconflictGetNPropConflictLiterals(SCIP_CONFLICT * conflict)5747 SCIP_Longint SCIPconflictGetNPropConflictLiterals(
5748 SCIP_CONFLICT* conflict /**< conflict analysis data */
5749 )
5750 {
5751 assert(conflict != NULL);
5752
5753 return conflict->npropconfliterals;
5754 }
5755
5756 /** gets number of reconvergence constraints detected in propagation conflict analysis */
SCIPconflictGetNPropReconvergenceConss(SCIP_CONFLICT * conflict)5757 SCIP_Longint SCIPconflictGetNPropReconvergenceConss(
5758 SCIP_CONFLICT* conflict /**< conflict analysis data */
5759 )
5760 {
5761 assert(conflict != NULL);
5762
5763 return conflict->npropreconvconss;
5764 }
5765
5766 /** gets total number of literals in reconvergence constraints created in propagation conflict analysis */
SCIPconflictGetNPropReconvergenceLiterals(SCIP_CONFLICT * conflict)5767 SCIP_Longint SCIPconflictGetNPropReconvergenceLiterals(
5768 SCIP_CONFLICT* conflict /**< conflict analysis data */
5769 )
5770 {
5771 assert(conflict != NULL);
5772
5773 return conflict->npropreconvliterals;
5774 }
5775
5776
5777
5778
5779 /*
5780 * Infeasible LP Conflict Analysis
5781 */
5782
5783 /** ensures, that side change arrays can store at least num entries */
5784 static
ensureSidechgsSize(SCIP_SET * set,int ** sidechginds,SCIP_Real ** sidechgoldlhss,SCIP_Real ** sidechgoldrhss,SCIP_Real ** sidechgnewlhss,SCIP_Real ** sidechgnewrhss,int * sidechgssize,int num)5785 SCIP_RETCODE ensureSidechgsSize(
5786 SCIP_SET* set, /**< global SCIP settings */
5787 int** sidechginds, /**< pointer to side change index array */
5788 SCIP_Real** sidechgoldlhss, /**< pointer to side change old left hand sides array */
5789 SCIP_Real** sidechgoldrhss, /**< pointer to side change old right hand sides array */
5790 SCIP_Real** sidechgnewlhss, /**< pointer to side change new left hand sides array */
5791 SCIP_Real** sidechgnewrhss, /**< pointer to side change new right hand sides array */
5792 int* sidechgssize, /**< pointer to size of side change arrays */
5793 int num /**< minimal number of entries to be able to store in side change arrays */
5794 )
5795 {
5796 assert(sidechginds != NULL);
5797 assert(sidechgoldlhss != NULL);
5798 assert(sidechgoldrhss != NULL);
5799 assert(sidechgnewlhss != NULL);
5800 assert(sidechgnewrhss != NULL);
5801 assert(sidechgssize != NULL);
5802
5803 if( num > *sidechgssize )
5804 {
5805 int newsize;
5806
5807 newsize = SCIPsetCalcMemGrowSize(set, num);
5808 SCIP_CALL( SCIPsetReallocBufferArray(set, sidechginds, newsize) );
5809 SCIP_CALL( SCIPsetReallocBufferArray(set, sidechgoldlhss, newsize) );
5810 SCIP_CALL( SCIPsetReallocBufferArray(set, sidechgoldrhss, newsize) );
5811 SCIP_CALL( SCIPsetReallocBufferArray(set, sidechgnewlhss, newsize) );
5812 SCIP_CALL( SCIPsetReallocBufferArray(set, sidechgnewrhss, newsize) );
5813 *sidechgssize = newsize;
5814 }
5815 assert(num <= *sidechgssize);
5816
5817 return SCIP_OKAY;
5818 }
5819
5820 /** adds removal of row's side to side change arrays; finite sides are only replaced by near infinite sides, such
5821 * that the row's sense in the LP solver is not changed
5822 */
5823 static
addSideRemoval(SCIP_SET * set,SCIP_ROW * row,SCIP_Real lpiinfinity,int ** sidechginds,SCIP_Real ** sidechgoldlhss,SCIP_Real ** sidechgoldrhss,SCIP_Real ** sidechgnewlhss,SCIP_Real ** sidechgnewrhss,int * sidechgssize,int * nsidechgs)5824 SCIP_RETCODE addSideRemoval(
5825 SCIP_SET* set, /**< global SCIP settings */
5826 SCIP_ROW* row, /**< LP row to change the sides for */
5827 SCIP_Real lpiinfinity, /**< value treated as infinity in LP solver */
5828 int** sidechginds, /**< pointer to side change index array */
5829 SCIP_Real** sidechgoldlhss, /**< pointer to side change old left hand sides array */
5830 SCIP_Real** sidechgoldrhss, /**< pointer to side change old right hand sides array */
5831 SCIP_Real** sidechgnewlhss, /**< pointer to side change new left hand sides array */
5832 SCIP_Real** sidechgnewrhss, /**< pointer to side change new right hand sides array */
5833 int* sidechgssize, /**< pointer to size of side change arrays */
5834 int* nsidechgs /**< pointer to number of used slots in side change arrays */
5835 )
5836 {
5837 SCIP_Real lhs;
5838 SCIP_Real rhs;
5839 SCIP_Real constant;
5840
5841 assert(sidechginds != NULL);
5842 assert(sidechgoldlhss != NULL);
5843 assert(sidechgoldrhss != NULL);
5844 assert(sidechgnewlhss != NULL);
5845 assert(sidechgnewrhss != NULL);
5846 assert(sidechgssize != NULL);
5847 assert(nsidechgs != NULL);
5848
5849 lhs = SCIProwGetLhs(row);
5850 rhs = SCIProwGetRhs(row);
5851 constant = SCIProwGetConstant(row);
5852 assert(!SCIPsetIsInfinity(set, -lhs) || !SCIPsetIsInfinity(set, rhs));
5853
5854 /* get memory to store additional side change */
5855 SCIP_CALL( ensureSidechgsSize(set, sidechginds, sidechgoldlhss, sidechgoldrhss, sidechgnewlhss, sidechgnewrhss, \
5856 sidechgssize, (*nsidechgs)+1) );
5857 assert(*nsidechgs < *sidechgssize);
5858 assert(*sidechginds != NULL);
5859 assert(*sidechgoldlhss != NULL);
5860 assert(*sidechgoldrhss != NULL);
5861 assert(*sidechgnewlhss != NULL);
5862 assert(*sidechgnewrhss != NULL);
5863
5864 /* store side change */
5865 (*sidechginds)[*nsidechgs] = SCIProwGetLPPos(row);
5866 if( SCIPsetIsInfinity(set, -lhs) )
5867 {
5868 (*sidechgoldlhss)[*nsidechgs] = -lpiinfinity;
5869 (*sidechgnewlhss)[*nsidechgs] = -lpiinfinity;
5870 }
5871 else
5872 {
5873 (*sidechgoldlhss)[*nsidechgs] = lhs - constant;
5874 (*sidechgnewlhss)[*nsidechgs] = -lpiinfinity;
5875 }
5876 if( SCIPsetIsInfinity(set, rhs) )
5877 {
5878 (*sidechgoldrhss)[*nsidechgs] = lpiinfinity;
5879 (*sidechgnewrhss)[*nsidechgs] = lpiinfinity;
5880 }
5881 else
5882 {
5883 (*sidechgoldrhss)[*nsidechgs] = rhs - constant;
5884 (*sidechgnewrhss)[*nsidechgs] = lpiinfinity;
5885 }
5886 (*nsidechgs)++;
5887
5888 return SCIP_OKAY;
5889 }
5890
5891 /** inserts variable's new bounds into bound change arrays */
5892 static
addBdchg(SCIP_SET * set,SCIP_VAR * var,SCIP_Real newlb,SCIP_Real newub,SCIP_LPBDCHGS * oldlpbdchgs,SCIP_LPBDCHGS * relaxedlpbdchgs,SCIP_LPI * lpi)5893 SCIP_RETCODE addBdchg(
5894 SCIP_SET* set, /**< global SCIP settings */
5895 SCIP_VAR* var, /**< variable to change the LP bounds for */
5896 SCIP_Real newlb, /**< new lower bound */
5897 SCIP_Real newub, /**< new upper bound */
5898 SCIP_LPBDCHGS* oldlpbdchgs, /**< old LP bound changes used for reset the LP bound change */
5899 SCIP_LPBDCHGS* relaxedlpbdchgs, /**< relaxed LP bound changes used for reset the LP bound change */
5900 SCIP_LPI* lpi /**< pointer to LPi to access infinity of LP solver; necessary to set correct value */
5901 )
5902 {
5903 assert(newlb <= newub);
5904 assert(oldlpbdchgs != NULL);
5905 assert(relaxedlpbdchgs != NULL);
5906
5907 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
5908 {
5909 SCIP_COL* col;
5910 int idx;
5911 int c;
5912
5913 col = SCIPvarGetCol(var);
5914 c = SCIPcolGetLPPos(col);
5915
5916 if( c >= 0 )
5917 {
5918 /* store old bound change for resetting the LP later */
5919 if( !oldlpbdchgs->usedcols[c] )
5920 {
5921 idx = oldlpbdchgs->nbdchgs;
5922 oldlpbdchgs->usedcols[c] = TRUE;
5923 oldlpbdchgs->bdchgcolinds[c] = idx;
5924 oldlpbdchgs->nbdchgs++;
5925
5926 oldlpbdchgs->bdchginds[idx] = c;
5927 oldlpbdchgs->bdchglbs[idx] = SCIPvarGetLbLP(var, set);
5928 oldlpbdchgs->bdchgubs[idx] = SCIPvarGetUbLP(var, set);
5929 }
5930 assert(oldlpbdchgs->bdchginds[oldlpbdchgs->bdchgcolinds[c]] == c);
5931 assert((SCIPlpiIsInfinity(lpi, -oldlpbdchgs->bdchglbs[oldlpbdchgs->bdchgcolinds[c]]) && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var, set))) ||
5932 SCIPsetIsEQ(set, oldlpbdchgs->bdchglbs[oldlpbdchgs->bdchgcolinds[c]], SCIPvarGetLbLP(var, set)));
5933 assert((SCIPlpiIsInfinity(lpi, oldlpbdchgs->bdchgubs[oldlpbdchgs->bdchgcolinds[c]]) && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var, set))) ||
5934 SCIPsetIsEQ(set, oldlpbdchgs->bdchgubs[oldlpbdchgs->bdchgcolinds[c]], SCIPvarGetUbLP(var, set)));
5935
5936 /* store bound change for conflict analysis */
5937 if( !relaxedlpbdchgs->usedcols[c] )
5938 {
5939 idx = relaxedlpbdchgs->nbdchgs;
5940 relaxedlpbdchgs->usedcols[c] = TRUE;
5941 relaxedlpbdchgs->bdchgcolinds[c] = idx;
5942 relaxedlpbdchgs->nbdchgs++;
5943
5944 /* remember the positive for later further bound widenings */
5945 relaxedlpbdchgs->bdchginds[idx] = c;
5946 }
5947 else
5948 {
5949 idx = relaxedlpbdchgs->bdchgcolinds[c];
5950 assert(relaxedlpbdchgs->bdchginds[idx] == c);
5951
5952 /* the new bound should be the same or more relaxed */
5953 assert(relaxedlpbdchgs->bdchglbs[idx] >= newlb ||
5954 (SCIPlpiIsInfinity(lpi, -relaxedlpbdchgs->bdchglbs[idx]) && SCIPsetIsInfinity(set, -newlb)));
5955 assert(relaxedlpbdchgs->bdchgubs[idx] <= newub ||
5956 (SCIPlpiIsInfinity(lpi, relaxedlpbdchgs->bdchgubs[idx]) && SCIPsetIsInfinity(set, newub)));
5957 }
5958
5959 /* set the new bounds for the LP with the correct infinity value */
5960 relaxedlpbdchgs->bdchglbs[idx] = SCIPsetIsInfinity(set, -newlb) ? -SCIPlpiInfinity(lpi) : newlb;
5961 relaxedlpbdchgs->bdchgubs[idx] = SCIPsetIsInfinity(set, newub) ? SCIPlpiInfinity(lpi) : newub;
5962 if( SCIPsetIsInfinity(set, -oldlpbdchgs->bdchglbs[idx]) )
5963 oldlpbdchgs->bdchglbs[idx] = -SCIPlpiInfinity(lpi);
5964 if( SCIPsetIsInfinity(set, oldlpbdchgs->bdchgubs[idx]) )
5965 oldlpbdchgs->bdchgubs[idx] = SCIPlpiInfinity(lpi);
5966 }
5967 }
5968
5969 return SCIP_OKAY;
5970 }
5971
5972 /** ensures, that candidate array can store at least num entries */
5973 static
ensureCandsSize(SCIP_SET * set,SCIP_VAR *** cands,SCIP_Real ** candscores,SCIP_Real ** newbounds,SCIP_Real ** proofactdeltas,int * candssize,int num)5974 SCIP_RETCODE ensureCandsSize(
5975 SCIP_SET* set, /**< global SCIP settings */
5976 SCIP_VAR*** cands, /**< pointer to candidate array */
5977 SCIP_Real** candscores, /**< pointer to candidate score array */
5978 SCIP_Real** newbounds, /**< pointer to candidate new bounds array */
5979 SCIP_Real** proofactdeltas, /**< pointer to candidate proof delta array */
5980 int* candssize, /**< pointer to size of array */
5981 int num /**< minimal number of candidates to store in array */
5982 )
5983 {
5984 assert(cands != NULL);
5985 assert(candssize != NULL);
5986
5987 if( num > *candssize )
5988 {
5989 int newsize;
5990
5991 newsize = SCIPsetCalcMemGrowSize(set, num);
5992 SCIP_CALL( SCIPsetReallocBufferArray(set, cands, newsize) );
5993 SCIP_CALL( SCIPsetReallocBufferArray(set, candscores, newsize) );
5994 SCIP_CALL( SCIPsetReallocBufferArray(set, newbounds, newsize) );
5995 SCIP_CALL( SCIPsetReallocBufferArray(set, proofactdeltas, newsize) );
5996 *candssize = newsize;
5997 }
5998 assert(num <= *candssize);
5999
6000 return SCIP_OKAY;
6001 }
6002
6003 /** adds variable to candidate list, if the current best bound corresponding to the proof coefficient is local;
6004 * returns the array position in the candidate list, where the new candidate was inserted, or -1 if the
6005 * variable can relaxed to global bounds immediately without increasing the proof's activity;
6006 * the candidates are sorted with respect to the following two criteria:
6007 * - prefer bound changes that have been applied deeper in the tree, to get a more global conflict
6008 * - prefer variables with small Farkas coefficient to get rid of as many bound changes as possible
6009 */
6010 static
addCand(SCIP_SET * set,int currentdepth,SCIP_VAR * var,int lbchginfopos,int ubchginfopos,SCIP_Real proofcoef,SCIP_Real prooflhs,SCIP_Real proofact,SCIP_VAR *** cands,SCIP_Real ** candscores,SCIP_Real ** newbounds,SCIP_Real ** proofactdeltas,int * candssize,int * ncands,int firstcand)6011 SCIP_RETCODE addCand(
6012 SCIP_SET* set, /**< global SCIP settings */
6013 int currentdepth, /**< current depth in the tree */
6014 SCIP_VAR* var, /**< variable to add to candidate array */
6015 int lbchginfopos, /**< positions of currently active lower bound change information in variable's array */
6016 int ubchginfopos, /**< positions of currently active upper bound change information in variable's array */
6017 SCIP_Real proofcoef, /**< coefficient of variable in infeasibility/bound proof */
6018 SCIP_Real prooflhs, /**< left hand side of infeasibility/bound proof */
6019 SCIP_Real proofact, /**< activity of infeasibility/bound proof row */
6020 SCIP_VAR*** cands, /**< pointer to candidate array for undoing bound changes */
6021 SCIP_Real** candscores, /**< pointer to candidate score array for undoing bound changes */
6022 SCIP_Real** newbounds, /**< pointer to candidate new bounds array for undoing bound changes */
6023 SCIP_Real** proofactdeltas, /**< pointer to proof activity increase array for undoing bound changes */
6024 int* candssize, /**< pointer to size of cands arrays */
6025 int* ncands, /**< pointer to count number of candidates in bound change list */
6026 int firstcand /**< position of first unprocessed bound change candidate */
6027 )
6028 {
6029 SCIP_Real oldbound;
6030 SCIP_Real newbound;
6031 SCIP_Real QUAD(proofactdelta);
6032 SCIP_Real score;
6033 int depth;
6034 int i;
6035 SCIP_Bool resolvable;
6036
6037 assert(set != NULL);
6038 assert(var != NULL);
6039 assert(-1 <= lbchginfopos && lbchginfopos <= var->nlbchginfos);
6040 assert(-1 <= ubchginfopos && ubchginfopos <= var->nubchginfos);
6041 assert(!SCIPsetIsZero(set, proofcoef));
6042 assert(SCIPsetIsGT(set, prooflhs, proofact));
6043 assert(cands != NULL);
6044 assert(candscores != NULL);
6045 assert(newbounds != NULL);
6046 assert(proofactdeltas != NULL);
6047 assert(candssize != NULL);
6048 assert(ncands != NULL);
6049 assert(*ncands <= *candssize);
6050 assert(0 <= firstcand && firstcand <= *ncands);
6051
6052 /* in the infeasibility or dual bound proof, the variable's bound is chosen to maximize the proof's activity */
6053 if( proofcoef > 0.0 )
6054 {
6055 assert(ubchginfopos >= 0); /* otherwise, undoBdchgsProof() should already have relaxed the local bound */
6056
6057 /* calculate the difference of current bound to the previous bound the variable was set to */
6058 if( ubchginfopos == var->nubchginfos )
6059 {
6060 /* current bound is the strong branching or diving bound */
6061 oldbound = SCIPvarGetUbLP(var, set);
6062 newbound = SCIPvarGetUbLocal(var);
6063 depth = currentdepth+1;
6064 resolvable = FALSE;
6065 }
6066 else
6067 {
6068 /* current bound is the result of a local bound change */
6069 resolvable = bdchginfoIsResolvable(&var->ubchginfos[ubchginfopos]);
6070 depth = var->ubchginfos[ubchginfopos].bdchgidx.depth;
6071 oldbound = var->ubchginfos[ubchginfopos].newbound;
6072 newbound = var->ubchginfos[ubchginfopos].oldbound;
6073 }
6074 }
6075 else
6076 {
6077 assert(lbchginfopos >= 0); /* otherwise, undoBdchgsProof() should already have relaxed the local bound */
6078
6079 /* calculate the difference of current bound to the previous bound the variable was set to */
6080 if( lbchginfopos == var->nlbchginfos )
6081 {
6082 /* current bound is the strong branching or diving bound */
6083 oldbound = SCIPvarGetLbLP(var, set);
6084 newbound = SCIPvarGetLbLocal(var);
6085 depth = currentdepth+1;
6086 resolvable = FALSE;
6087 }
6088 else
6089 {
6090 /* current bound is the result of a local bound change */
6091 resolvable = bdchginfoIsResolvable(&var->lbchginfos[lbchginfopos]);
6092 depth = var->lbchginfos[lbchginfopos].bdchgidx.depth;
6093 oldbound = var->lbchginfos[lbchginfopos].newbound;
6094 newbound = var->lbchginfos[lbchginfopos].oldbound;
6095 }
6096 }
6097
6098 /* calculate the increase in the proof's activity */
6099 SCIPquadprecSumDD(proofactdelta, newbound, -oldbound);
6100 SCIPquadprecProdQD(proofactdelta, proofactdelta, proofcoef);
6101 assert(QUAD_TO_DBL(proofactdelta) > 0.0);
6102
6103 /* calculate score for undoing the bound change */
6104 score = calcBdchgScore(prooflhs, proofact, QUAD_TO_DBL(proofactdelta), proofcoef, depth, currentdepth, var, set);
6105
6106 if( !resolvable )
6107 {
6108 score += 10.0;
6109 if( !SCIPvarIsBinary(var) )
6110 score += 10.0;
6111 }
6112
6113 /* get enough memory to store new candidate */
6114 SCIP_CALL( ensureCandsSize(set, cands, candscores, newbounds, proofactdeltas, candssize, (*ncands)+1) );
6115 assert(*cands != NULL);
6116 assert(*candscores != NULL);
6117 assert(*newbounds != NULL);
6118 assert(*proofactdeltas != NULL);
6119
6120 SCIPsetDebugMsg(set, " -> local <%s> %s %g, relax <%s> %s %g, proofcoef=%g, dpt=%d, resolve=%u, delta=%g, score=%g\n",
6121 SCIPvarGetName(var), proofcoef > 0.0 ? "<=" : ">=", oldbound,
6122 SCIPvarGetName(var), proofcoef > 0.0 ? "<=" : ">=", newbound,
6123 proofcoef, depth, resolvable, QUAD_TO_DBL(proofactdelta), score);
6124
6125 /* insert variable in candidate list without touching the already processed candidates */
6126 for( i = *ncands; i > firstcand && score > (*candscores)[i-1]; --i )
6127 {
6128 (*cands)[i] = (*cands)[i-1];
6129 (*candscores)[i] = (*candscores)[i-1];
6130 (*newbounds)[i] = (*newbounds)[i-1];
6131 (*proofactdeltas)[i] = (*proofactdeltas)[i-1];
6132 }
6133 (*cands)[i] = var;
6134 (*candscores)[i] = score;
6135 (*newbounds)[i] = newbound;
6136 (*proofactdeltas)[i] = QUAD_TO_DBL(proofactdelta);
6137 (*ncands)++;
6138
6139 return SCIP_OKAY;
6140 }
6141
6142 /** after changing the global bound of a variable, the bdchginfos that are now redundant are replaced with
6143 * oldbound = newbound = global bound; if the current bdchginfo is of such kind, the bound is equal to the
6144 * global bound and we can ignore it by installing a -1 as the corresponding bound change info position
6145 */
6146 static
skipRedundantBdchginfos(SCIP_VAR * var,int * lbchginfopos,int * ubchginfopos)6147 void skipRedundantBdchginfos(
6148 SCIP_VAR* var, /**< problem variable */
6149 int* lbchginfopos, /**< pointer to lower bound change information position */
6150 int* ubchginfopos /**< pointer to upper bound change information position */
6151 )
6152 {
6153 assert(var != NULL);
6154 assert(lbchginfopos != NULL);
6155 assert(ubchginfopos != NULL);
6156 assert(-1 <= *lbchginfopos && *lbchginfopos <= var->nlbchginfos);
6157 assert(-1 <= *ubchginfopos && *ubchginfopos <= var->nubchginfos);
6158 assert(*lbchginfopos == -1 || *lbchginfopos == var->nlbchginfos
6159 || var->lbchginfos[*lbchginfopos].redundant
6160 == (var->lbchginfos[*lbchginfopos].oldbound == var->lbchginfos[*lbchginfopos].newbound)); /*lint !e777*/
6161 assert(*ubchginfopos == -1 || *ubchginfopos == var->nubchginfos
6162 || var->ubchginfos[*ubchginfopos].redundant
6163 == (var->ubchginfos[*ubchginfopos].oldbound == var->ubchginfos[*ubchginfopos].newbound)); /*lint !e777*/
6164
6165 if( *lbchginfopos >= 0 && *lbchginfopos < var->nlbchginfos && var->lbchginfos[*lbchginfopos].redundant )
6166 {
6167 assert(SCIPvarGetLbGlobal(var) == var->lbchginfos[*lbchginfopos].oldbound); /*lint !e777*/
6168 *lbchginfopos = -1;
6169 }
6170 if( *ubchginfopos >= 0 && *ubchginfopos < var->nubchginfos && var->ubchginfos[*ubchginfopos].redundant )
6171 {
6172 assert(SCIPvarGetUbGlobal(var) == var->ubchginfos[*ubchginfopos].oldbound); /*lint !e777*/
6173 *ubchginfopos = -1;
6174 }
6175 }
6176
6177 /** undoes bound changes on variables, still leaving the given infeasibility proof valid */
6178 static
undoBdchgsProof(SCIP_SET * set,SCIP_PROB * prob,int currentdepth,SCIP_Real * proofcoefs,SCIP_Real prooflhs,SCIP_Real * proofact,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,int * lbchginfoposs,int * ubchginfoposs,SCIP_LPBDCHGS * oldlpbdchgs,SCIP_LPBDCHGS * relaxedlpbdchgs,SCIP_Bool * resolve,SCIP_LPI * lpi)6179 SCIP_RETCODE undoBdchgsProof(
6180 SCIP_SET* set, /**< global SCIP settings */
6181 SCIP_PROB* prob, /**< problem data */
6182 int currentdepth, /**< current depth in the tree */
6183 SCIP_Real* proofcoefs, /**< coefficients in infeasibility proof */
6184 SCIP_Real prooflhs, /**< left hand side of proof */
6185 SCIP_Real* proofact, /**< current activity of proof */
6186 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
6187 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
6188 int* lbchginfoposs, /**< positions of currently active lower bound change information in variables' arrays */
6189 int* ubchginfoposs, /**< positions of currently active upper bound change information in variables' arrays */
6190 SCIP_LPBDCHGS* oldlpbdchgs, /**< old LP bound changes used for reset the LP bound change, or NULL */
6191 SCIP_LPBDCHGS* relaxedlpbdchgs, /**< relaxed LP bound changes used for reset the LP bound change, or NULL */
6192 SCIP_Bool* resolve, /**< pointer to store whether the changed LP should be resolved again, or NULL */
6193 SCIP_LPI* lpi /**< pointer to LPi to access infinity of LP solver; necessary to set correct values */
6194 )
6195 {
6196 SCIP_VAR** vars;
6197 SCIP_VAR** cands;
6198 SCIP_Real* candscores;
6199 SCIP_Real* newbounds;
6200 SCIP_Real* proofactdeltas;
6201 int nvars;
6202 int ncands;
6203 int candssize;
6204 int v;
6205 int i;
6206
6207 assert(prob != NULL);
6208 assert(proofcoefs != NULL);
6209 assert(SCIPsetIsFeasGT(set, prooflhs, (*proofact)));
6210 assert(curvarlbs != NULL);
6211 assert(curvarubs != NULL);
6212 assert(lbchginfoposs != NULL);
6213 assert(ubchginfoposs != NULL);
6214
6215 if( resolve != NULL )
6216 *resolve = FALSE;
6217
6218 vars = prob->vars;
6219 nvars = prob->nvars;
6220 assert(nvars == 0 || vars != NULL);
6221
6222 /* calculate the order in which the bound changes are tried to be undone, and relax all bounds if this doesn't
6223 * increase the proof's activity
6224 */
6225 SCIP_CALL( SCIPsetAllocBufferArray(set, &cands, nvars) );
6226 SCIP_CALL( SCIPsetAllocBufferArray(set, &candscores, nvars) );
6227 SCIP_CALL( SCIPsetAllocBufferArray(set, &newbounds, nvars) );
6228 SCIP_CALL( SCIPsetAllocBufferArray(set, &proofactdeltas, nvars) );
6229 ncands = 0;
6230 candssize = nvars;
6231 for( v = 0; v < nvars; ++v )
6232 {
6233 SCIP_VAR* var;
6234 SCIP_Bool relaxed;
6235
6236 var = vars[v];
6237
6238 /* after changing the global bound of a variable, the bdchginfos that are now redundant are replaced with
6239 * oldbound = newbound = global bound; if the current bdchginfo is of such kind, the bound is equal to the
6240 * global bound and we can ignore it
6241 */
6242 skipRedundantBdchginfos(var, &lbchginfoposs[v], &ubchginfoposs[v]);
6243
6244 /* ignore variables already relaxed to global bounds */
6245 if( (lbchginfoposs[v] == -1 && ubchginfoposs[v] == -1) )
6246 {
6247 proofcoefs[v] = 0.0;
6248 continue;
6249 }
6250
6251 /* relax bounds that are not used in the proof to the global bounds */
6252 relaxed = FALSE;
6253 if( !SCIPsetIsNegative(set, proofcoefs[v]) )
6254 {
6255 /* the lower bound is not used */
6256 if( lbchginfoposs[v] >= 0 )
6257 {
6258 SCIPsetDebugMsg(set, " -> relaxing variable <%s>[%g,%g] to [%g,%g]: proofcoef=%g, %g <= %g\n",
6259 SCIPvarGetName(var), curvarlbs[v], curvarubs[v], SCIPvarGetLbGlobal(var), curvarubs[v],
6260 proofcoefs[v], prooflhs, (*proofact));
6261 curvarlbs[v] = SCIPvarGetLbGlobal(var);
6262 lbchginfoposs[v] = -1;
6263 relaxed = TRUE;
6264 }
6265 }
6266 if( !SCIPsetIsPositive(set, proofcoefs[v]) )
6267 {
6268 /* the upper bound is not used */
6269 if( ubchginfoposs[v] >= 0 )
6270 {
6271 SCIPsetDebugMsg(set, " -> relaxing variable <%s>[%g,%g] to [%g,%g]: proofcoef=%g, %g <= %g\n",
6272 SCIPvarGetName(var), curvarlbs[v], curvarubs[v], curvarlbs[v], SCIPvarGetUbGlobal(var),
6273 proofcoefs[v], prooflhs, (*proofact));
6274 curvarubs[v] = SCIPvarGetUbGlobal(var);
6275 ubchginfoposs[v] = -1;
6276 relaxed = TRUE;
6277 }
6278 }
6279 if( relaxed && oldlpbdchgs != NULL )
6280 {
6281 SCIP_CALL( addBdchg(set, var, curvarlbs[v], curvarubs[v], oldlpbdchgs, relaxedlpbdchgs, lpi) );
6282 }
6283
6284 /* add bound to candidate list */
6285 if( lbchginfoposs[v] >= 0 || ubchginfoposs[v] >= 0 )
6286 {
6287 SCIP_CALL( addCand(set, currentdepth, var, lbchginfoposs[v], ubchginfoposs[v], proofcoefs[v],
6288 prooflhs, (*proofact), &cands, &candscores, &newbounds, &proofactdeltas, &candssize, &ncands, 0) );
6289 }
6290 /* we can set the proof coefficient to zero, because the variable is not needed */
6291 else
6292 proofcoefs[v] = 0.0;
6293 }
6294
6295 /* try to undo remaining local bound changes while still keeping the proof row violated:
6296 * bound changes can be undone, if prooflhs > proofact + proofactdelta;
6297 * afterwards, the current proof activity has to be updated
6298 */
6299 for( i = 0; i < ncands; ++i )
6300 {
6301 assert(proofactdeltas[i] > 0.0);
6302 assert((lbchginfoposs[SCIPvarGetProbindex(cands[i])] >= 0) != (ubchginfoposs[SCIPvarGetProbindex(cands[i])] >= 0));
6303
6304 /* when relaxing a constraint we still need to stay infeasible; therefore we need to do the comparison in
6305 * feasibility tolerance because if 'prooflhs' is (feas-))equal to 'proofact + proofactdeltas[i]' it would mean
6306 * that there is no violation
6307 */
6308 if( SCIPsetIsFeasGT(set, prooflhs, (*proofact) + proofactdeltas[i]) )
6309 {
6310 v = SCIPvarGetProbindex(cands[i]);
6311 assert(0 <= v && v < nvars);
6312 assert((lbchginfoposs[v] >= 0) != (ubchginfoposs[v] >= 0));
6313
6314 SCIPsetDebugMsg(set, " -> relaxing variable <%s>[%g,%g] to [%g,%g]: proofcoef=%g, %g <= %g + %g\n",
6315 SCIPvarGetName(cands[i]), curvarlbs[v], curvarubs[v],
6316 proofcoefs[v] > 0.0 ? curvarlbs[v] : newbounds[i],
6317 proofcoefs[v] > 0.0 ? newbounds[i] : curvarubs[v],
6318 proofcoefs[v], prooflhs, (*proofact), proofactdeltas[i]);
6319
6320 #ifndef NDEBUG
6321 {
6322 SCIP_Real QUAD(verifylb);
6323 SCIP_Real QUAD(verifyub);
6324
6325 SCIPquadprecSumDD(verifylb, newbounds[i], -curvarlbs[v]);
6326 SCIPquadprecProdQD(verifylb, verifylb, proofcoefs[v]);
6327
6328 SCIPquadprecSumDD(verifyub, newbounds[i], -curvarubs[v]);
6329 SCIPquadprecProdQD(verifyub, verifyub, proofcoefs[v]);
6330
6331 assert((SCIPsetIsPositive(set, proofcoefs[v]) && SCIPsetIsGT(set, newbounds[i], curvarubs[v]))
6332 || (SCIPsetIsNegative(set, proofcoefs[v]) && SCIPsetIsLT(set, newbounds[i], curvarlbs[v])));
6333 assert((SCIPsetIsPositive(set, proofcoefs[v])
6334 && SCIPsetIsEQ(set, proofactdeltas[i], QUAD_TO_DBL(verifyub)))
6335 || (SCIPsetIsNegative(set, proofcoefs[v])
6336 && SCIPsetIsEQ(set, proofactdeltas[i], QUAD_TO_DBL(verifylb))));
6337 assert(!SCIPsetIsZero(set, proofcoefs[v]));
6338 }
6339 #endif
6340
6341 if( proofcoefs[v] > 0.0 )
6342 {
6343 assert(ubchginfoposs[v] >= 0);
6344 assert(lbchginfoposs[v] == -1);
6345 curvarubs[v] = newbounds[i];
6346 ubchginfoposs[v]--;
6347 }
6348 else
6349 {
6350 assert(lbchginfoposs[v] >= 0);
6351 assert(ubchginfoposs[v] == -1);
6352 curvarlbs[v] = newbounds[i];
6353 lbchginfoposs[v]--;
6354 }
6355 if( oldlpbdchgs != NULL )
6356 {
6357 SCIP_CALL( addBdchg(set, cands[i], curvarlbs[v], curvarubs[v], oldlpbdchgs, relaxedlpbdchgs, lpi) );
6358 }
6359 (*proofact) += proofactdeltas[i];
6360 if( resolve != NULL && SCIPvarIsInLP(cands[i]) )
6361 *resolve = TRUE;
6362
6363 /* after changing the global bound of a variable, the bdchginfos that are now redundant are replaced with
6364 * oldbound = newbound = global bound; if the current bdchginfo is of such kind, the bound is equal to the
6365 * global bound and we can ignore it
6366 */
6367 skipRedundantBdchginfos(cands[i], &lbchginfoposs[v], &ubchginfoposs[v]);
6368
6369 /* insert the new local bound of the variable into the candidate list */
6370 if( lbchginfoposs[v] >= 0 || ubchginfoposs[v] >= 0 )
6371 {
6372 SCIP_CALL( addCand(set, currentdepth, cands[i], lbchginfoposs[v], ubchginfoposs[v], proofcoefs[v],
6373 prooflhs, (*proofact), &cands, &candscores, &newbounds, &proofactdeltas, &candssize, &ncands, i+1) );
6374 }
6375 else
6376 proofcoefs[v] = 0.0;
6377 }
6378 }
6379
6380 /* free the buffer for the sorted bound change candidates */
6381 SCIPsetFreeBufferArray(set, &proofactdeltas);
6382 SCIPsetFreeBufferArray(set, &newbounds);
6383 SCIPsetFreeBufferArray(set, &candscores);
6384 SCIPsetFreeBufferArray(set, &cands);
6385
6386 return SCIP_OKAY;
6387 }
6388
6389 /* because calculations might cancel out some values, we stop the infeasibility analysis if a value is bigger than
6390 * 2^53 = 9007199254740992
6391 */
6392 #define NUMSTOP 9007199254740992.0
6393
6394 /** analyzes an infeasible LP and undoes additional bound changes while staying infeasible */
6395 static
undoBdchgsDualfarkas(SCIP_SET * set,SCIP_PROB * prob,SCIP_LP * lp,int currentdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,int * lbchginfoposs,int * ubchginfoposs,SCIP_LPBDCHGS * oldlpbdchgs,SCIP_LPBDCHGS * relaxedlpbdchgs,SCIP_Bool * valid,SCIP_Bool * resolve,SCIP_Real * farkascoefs,SCIP_Real farkaslhs,SCIP_Real * farkasactivity)6396 SCIP_RETCODE undoBdchgsDualfarkas(
6397 SCIP_SET* set, /**< global SCIP settings */
6398 SCIP_PROB* prob, /**< problem data */
6399 SCIP_LP* lp, /**< LP data */
6400 int currentdepth, /**< current depth in the tree */
6401 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
6402 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
6403 int* lbchginfoposs, /**< positions of currently active lower bound change information in variables' arrays */
6404 int* ubchginfoposs, /**< positions of currently active upper bound change information in variables' arrays */
6405 SCIP_LPBDCHGS* oldlpbdchgs, /**< old LP bound changes used for reset the LP bound change, or NULL */
6406 SCIP_LPBDCHGS* relaxedlpbdchgs, /**< relaxed LP bound changes used for reset the LP bound change, or NULL */
6407 SCIP_Bool* valid, /**< pointer to store whether the unfixings are valid */
6408 SCIP_Bool* resolve, /**< pointer to store whether the changed LP should be resolved again */
6409 SCIP_Real* farkascoefs, /**< coefficients in the proof constraint */
6410 SCIP_Real farkaslhs, /**< lhs of the proof constraint */
6411 SCIP_Real* farkasactivity /**< maximal activity of the proof constraint */
6412 )
6413 {
6414 SCIP_LPI* lpi;
6415
6416 assert(prob != NULL);
6417 assert(lp != NULL);
6418 assert(lp->flushed);
6419 assert(lp->solved);
6420 assert(curvarlbs != NULL);
6421 assert(curvarubs != NULL);
6422 assert(lbchginfoposs != NULL);
6423 assert(ubchginfoposs != NULL);
6424 assert(valid != NULL);
6425 assert(resolve != NULL);
6426
6427 SCIPsetDebugMsg(set, "undoing bound changes in infeasible LP: cutoff=%g\n", lp->cutoffbound);
6428
6429 *valid = FALSE;
6430 *resolve = FALSE;
6431
6432 lpi = SCIPlpGetLPI(lp);
6433
6434 /* check, if the Farkas row is still violated (using current bounds and ignoring local rows) */
6435 if( SCIPsetIsFeasGT(set, farkaslhs, *farkasactivity) )
6436 {
6437 /* undo bound changes while keeping the infeasibility proof valid */
6438 SCIP_CALL( undoBdchgsProof(set, prob, currentdepth, farkascoefs, farkaslhs, farkasactivity, \
6439 curvarlbs, curvarubs, lbchginfoposs, ubchginfoposs, oldlpbdchgs, relaxedlpbdchgs, resolve, lpi) );
6440
6441 *valid = TRUE;
6442
6443 /* resolving does not make sense: the old dual ray is still valid -> resolving will not change the solution */
6444 *resolve = FALSE;
6445 }
6446
6447 return SCIP_OKAY;
6448 }
6449
6450 /** analyzes an LP exceeding the objective limit and undoes additional bound changes while staying beyond the
6451 * objective limit
6452 */
6453 static
undoBdchgsDualsol(SCIP_SET * set,SCIP_PROB * prob,SCIP_LP * lp,int currentdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,int * lbchginfoposs,int * ubchginfoposs,SCIP_LPBDCHGS * oldlpbdchgs,SCIP_LPBDCHGS * relaxedlpbdchgs,SCIP_Bool * valid,SCIP_Bool * resolve,SCIP_Real * dualcoefs,SCIP_Real duallhs,SCIP_Real * dualactivity)6454 SCIP_RETCODE undoBdchgsDualsol(
6455 SCIP_SET* set, /**< global SCIP settings */
6456 SCIP_PROB* prob, /**< problem data */
6457 SCIP_LP* lp, /**< LP data */
6458 int currentdepth, /**< current depth in the tree */
6459 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
6460 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
6461 int* lbchginfoposs, /**< positions of currently active lower bound change information in variables' arrays */
6462 int* ubchginfoposs, /**< positions of currently active upper bound change information in variables' arrays */
6463 SCIP_LPBDCHGS* oldlpbdchgs, /**< old LP bound changes used for reset the LP bound change, or NULL */
6464 SCIP_LPBDCHGS* relaxedlpbdchgs, /**< relaxed LP bound changes used for reset the LP bound change, or NULL */
6465 SCIP_Bool* valid, /**< pointer to store whether the unfixings are valid */
6466 SCIP_Bool* resolve, /**< pointer to store whether the changed LP should be resolved again */
6467 SCIP_Real* dualcoefs, /**< coefficients in the proof constraint */
6468 SCIP_Real duallhs, /**< lhs of the proof constraint */
6469 SCIP_Real* dualactivity /**< maximal activity of the proof constraint */
6470 )
6471 {
6472 SCIP_LPI* lpi;
6473
6474 assert(set != NULL);
6475 assert(prob != NULL);
6476 assert(lp != NULL);
6477 assert(lp->flushed);
6478 assert(lp->solved);
6479 assert(curvarlbs != NULL);
6480 assert(curvarubs != NULL);
6481 assert(lbchginfoposs != NULL);
6482 assert(ubchginfoposs != NULL);
6483 assert(valid != NULL);
6484 assert(resolve != NULL);
6485
6486 *valid = FALSE;
6487 *resolve = FALSE;
6488
6489 SCIPsetDebugMsg(set, "undoing bound changes in LP exceeding cutoff: cutoff=%g\n", lp->cutoffbound);
6490
6491 /* get LP solver interface */
6492 lpi = SCIPlpGetLPI(lp);
6493
6494 /* check, if the dual row is still violated (using current bounds and ignoring local rows) */
6495 if( SCIPsetIsFeasGT(set, duallhs, *dualactivity) )
6496 {
6497 /* undo bound changes while keeping the infeasibility proof valid */
6498 SCIP_CALL( undoBdchgsProof(set, prob, currentdepth, dualcoefs, duallhs, dualactivity, curvarlbs, curvarubs, \
6499 lbchginfoposs, ubchginfoposs, oldlpbdchgs, relaxedlpbdchgs, resolve, lpi) );
6500
6501 *valid = TRUE;
6502 }
6503
6504 return SCIP_OKAY;
6505 }
6506
6507 /** applies conflict analysis starting with given bound changes, that could not be undone during previous
6508 * infeasibility analysis
6509 */
6510 static
conflictAnalyzeRemainingBdchgs(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_TREE * tree,SCIP_Bool diving,int * lbchginfoposs,int * ubchginfoposs,int * nconss,int * nliterals,int * nreconvconss,int * nreconvliterals)6511 SCIP_RETCODE conflictAnalyzeRemainingBdchgs(
6512 SCIP_CONFLICT* conflict, /**< conflict analysis data */
6513 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
6514 SCIP_SET* set, /**< global SCIP settings */
6515 SCIP_STAT* stat, /**< problem statistics */
6516 SCIP_PROB* prob, /**< problem data */
6517 SCIP_TREE* tree, /**< branch and bound tree */
6518 SCIP_Bool diving, /**< are we in strong branching or diving mode? */
6519 int* lbchginfoposs, /**< positions of currently active lower bound change information in variables' arrays */
6520 int* ubchginfoposs, /**< positions of currently active upper bound change information in variables' arrays */
6521 int* nconss, /**< pointer to store the number of generated conflict constraints */
6522 int* nliterals, /**< pointer to store the number of literals in generated conflict constraints */
6523 int* nreconvconss, /**< pointer to store the number of generated reconvergence constraints */
6524 int* nreconvliterals /**< pointer to store the number of literals generated reconvergence constraints */
6525 )
6526 {
6527 SCIP_VAR** vars;
6528 SCIP_VAR* var;
6529 SCIP_CONFTYPE conftype;
6530 SCIP_Bool usescutoffbound;
6531 int nvars;
6532 int v;
6533 int nbdchgs;
6534 int maxsize;
6535
6536 assert(prob != NULL);
6537 assert(lbchginfoposs != NULL);
6538 assert(ubchginfoposs != NULL);
6539 assert(nconss != NULL);
6540 assert(nliterals != NULL);
6541 assert(nreconvconss != NULL);
6542 assert(nreconvliterals != NULL);
6543
6544 *nconss = 0;
6545 *nliterals = 0;
6546 *nreconvconss = 0;
6547 *nreconvliterals = 0;
6548
6549 vars = prob->vars;
6550 nvars = prob->nvars;
6551 assert(nvars == 0 || vars != NULL);
6552
6553 maxsize = 2*conflictCalcMaxsize(set, prob);
6554
6555 /* initialize conflict data */
6556 conftype = conflict->conflictset->conflicttype;
6557 usescutoffbound = conflict->conflictset->usescutoffbound;
6558
6559 SCIP_CALL( SCIPconflictInit(conflict, set, stat, prob, conftype, usescutoffbound) );
6560
6561 conflict->conflictset->conflicttype = conftype;
6562 conflict->conflictset->usescutoffbound = usescutoffbound;
6563
6564 /* add remaining bound changes to conflict queue */
6565 SCIPsetDebugMsg(set, "initial conflict set after undoing bound changes:\n");
6566
6567 nbdchgs = 0;
6568 for( v = 0; v < nvars && nbdchgs < maxsize; ++v )
6569 {
6570 var = vars[v];
6571 assert(var != NULL);
6572 assert(var->nlbchginfos >= 0);
6573 assert(var->nubchginfos >= 0);
6574 assert(-1 <= lbchginfoposs[v] && lbchginfoposs[v] <= var->nlbchginfos);
6575 assert(-1 <= ubchginfoposs[v] && ubchginfoposs[v] <= var->nubchginfos);
6576
6577 if( lbchginfoposs[v] == var->nlbchginfos || ubchginfoposs[v] == var->nubchginfos )
6578 {
6579 SCIP_BDCHGINFO* bdchginfo;
6580 SCIP_Real relaxedbd;
6581
6582 /* the strong branching or diving bound stored in the column is responsible for the conflict:
6583 * it cannot be resolved and therefore has to be directly put into the conflict set
6584 */
6585 assert((lbchginfoposs[v] == var->nlbchginfos) != (ubchginfoposs[v] == var->nubchginfos)); /* only one can be tight in the dual! */
6586 assert(lbchginfoposs[v] < var->nlbchginfos || SCIPvarGetLbLP(var, set) > SCIPvarGetLbLocal(var));
6587 assert(ubchginfoposs[v] < var->nubchginfos || SCIPvarGetUbLP(var, set) < SCIPvarGetUbLocal(var));
6588
6589 /* create an artificial bound change information for the diving/strong branching bound change;
6590 * they are freed in the SCIPconflictFlushConss() call
6591 */
6592 if( lbchginfoposs[v] == var->nlbchginfos )
6593 {
6594 SCIP_CALL( conflictCreateTmpBdchginfo(conflict, blkmem, set, var, SCIP_BOUNDTYPE_LOWER,
6595 SCIPvarGetLbLocal(var), SCIPvarGetLbLP(var, set), &bdchginfo) );
6596 relaxedbd = SCIPvarGetLbLP(var, set);
6597 }
6598 else
6599 {
6600 SCIP_CALL( conflictCreateTmpBdchginfo(conflict, blkmem, set, var, SCIP_BOUNDTYPE_UPPER,
6601 SCIPvarGetUbLocal(var), SCIPvarGetUbLP(var, set), &bdchginfo) );
6602 relaxedbd = SCIPvarGetUbLP(var, set);
6603 }
6604
6605 /* put variable into the conflict set */
6606 SCIPsetDebugMsg(set, " force: <%s> %s %g [status: %d, type: %d, dive/strong]\n",
6607 SCIPvarGetName(var), lbchginfoposs[v] == var->nlbchginfos ? ">=" : "<=",
6608 lbchginfoposs[v] == var->nlbchginfos ? SCIPvarGetLbLP(var, set) : SCIPvarGetUbLP(var, set),
6609 SCIPvarGetStatus(var), SCIPvarGetType(var));
6610 SCIP_CALL( conflictAddConflictBound(conflict, blkmem, set, bdchginfo, relaxedbd) );
6611
6612 /* each variable which is add to the conflict graph gets an increase in the VSIDS
6613 *
6614 * @note That is different to the VSIDS preseted in the literature
6615 */
6616 SCIP_CALL( incVSIDS(var, blkmem, set, stat, SCIPbdchginfoGetBoundtype(bdchginfo), relaxedbd, set->conf_conflictgraphweight) );
6617 nbdchgs++;
6618 }
6619 else
6620 {
6621 /* put remaining bound changes into conflict candidate queue */
6622 if( lbchginfoposs[v] >= 0 )
6623 {
6624 SCIP_CALL( conflictAddBound(conflict, blkmem, set, stat, var, SCIP_BOUNDTYPE_LOWER, \
6625 &var->lbchginfos[lbchginfoposs[v]], SCIPbdchginfoGetNewbound(&var->lbchginfos[lbchginfoposs[v]])) );
6626 nbdchgs++;
6627 }
6628 if( ubchginfoposs[v] >= 0 )
6629 {
6630 assert(!SCIPbdchginfoIsRedundant(&var->ubchginfos[ubchginfoposs[v]]));
6631 SCIP_CALL( conflictAddBound(conflict, blkmem, set, stat, var, SCIP_BOUNDTYPE_UPPER, \
6632 &var->ubchginfos[ubchginfoposs[v]], SCIPbdchginfoGetNewbound(&var->ubchginfos[ubchginfoposs[v]])) );
6633 nbdchgs++;
6634 }
6635 }
6636 }
6637
6638 if( v == nvars )
6639 {
6640 /* analyze the conflict set, and create conflict constraints on success */
6641 SCIP_CALL( conflictAnalyze(conflict, blkmem, set, stat, prob, tree, diving, 0, FALSE, nconss, nliterals, \
6642 nreconvconss, nreconvliterals) );
6643 }
6644
6645 return SCIP_OKAY;
6646 }
6647
6648 /** adds a weighted LP row to an aggregation row */
6649 static
addRowToAggrRow(SCIP_SET * set,SCIP_ROW * row,SCIP_Real weight,SCIP_AGGRROW * aggrrow)6650 SCIP_RETCODE addRowToAggrRow(
6651 SCIP_SET* set, /**< global SCIP settings */
6652 SCIP_ROW* row, /**< LP row */
6653 SCIP_Real weight, /**< weight for scaling */
6654 SCIP_AGGRROW* aggrrow /**< aggregation row */
6655 )
6656 {
6657 assert(set != NULL);
6658 assert(row != NULL);
6659 assert(weight != 0.0);
6660
6661 /* add minimal value to dual row's left hand side: y_i < 0 -> lhs, y_i > 0 -> rhs */
6662 if( weight < 0.0 )
6663 {
6664 assert(!SCIPsetIsInfinity(set, -row->lhs));
6665 SCIP_CALL( SCIPaggrRowAddRow(set->scip, aggrrow, row, weight, -1) );
6666 }
6667 else
6668 {
6669 assert(!SCIPsetIsInfinity(set, row->rhs));
6670 SCIP_CALL( SCIPaggrRowAddRow(set->scip, aggrrow, row, weight, +1) );
6671 }
6672 SCIPsetDebugMsg(set, " -> add %s row <%s>[%g,%g](lp depth: %d): dual=%g -> dualrhs=%g\n",
6673 row->local ? "local" : "global",
6674 SCIProwGetName(row), row->lhs - row->constant, row->rhs - row->constant,
6675 row->lpdepth, weight, SCIPaggrRowGetRhs(aggrrow));
6676
6677 return SCIP_OKAY;
6678 }
6679
6680 /** checks validity of an LP row and a corresponding weight */
6681 static
checkDualFeasibility(SCIP_SET * set,SCIP_ROW * row,SCIP_Real weight,SCIP_Bool * zerocontribution)6682 SCIP_Bool checkDualFeasibility(
6683 SCIP_SET* set, /**< global SCIP settings */
6684 SCIP_ROW* row, /**< LP row */
6685 SCIP_Real weight, /**< weight for scaling */
6686 SCIP_Bool* zerocontribution /**< pointer to store whether every row entry is zero within tolerances */
6687 )
6688 {
6689 SCIP_Bool valid = TRUE;
6690
6691 *zerocontribution = TRUE;
6692
6693 /* dual solution values of 0.0 are always valid */
6694 if( REALABS(weight) > QUAD_EPSILON )
6695 {
6696 *zerocontribution = FALSE;
6697
6698 /* check dual feasibility */
6699 if( (SCIPsetIsInfinity(set, -row->lhs) && weight > 0.0) || (SCIPsetIsInfinity(set, row->rhs) && weight < 0.0) )
6700 {
6701 int i;
6702
6703 /* ignore slight numerical violations if the contribution of every component of the row is close to zero */
6704 if( weight > 0.0 )
6705 *zerocontribution = SCIPsetIsDualfeasZero(set, row->rhs * weight);
6706 else
6707 *zerocontribution = SCIPsetIsDualfeasZero(set, row->lhs * weight);
6708
6709 for( i = 0; i < row->len && *zerocontribution; i++ )
6710 {
6711 if( !SCIPsetIsDualfeasZero(set, weight * row->vals[i]) )
6712 *zerocontribution = FALSE;
6713 }
6714
6715 if( !(*zerocontribution) )
6716 {
6717 SCIPsetDebugMsg(set, " -> invalid dual solution value %g for row <%s>: lhs=%g, rhs=%g\n",
6718 weight, SCIProwGetName(row), row->lhs, row->rhs);
6719
6720 valid = FALSE;
6721 }
6722 }
6723 }
6724
6725 return valid;
6726 }
6727
6728 /** sort local rows by increasing depth and number of nonzeros as tie-breaker */
6729 static
sortLocalRows(SCIP_SET * set,SCIP_AGGRROW * aggrrow,SCIP_ROW ** rows,int * rowinds,int * rowdepth,int nrows)6730 SCIP_RETCODE sortLocalRows(
6731 SCIP_SET* set, /**< global SCIP settings */
6732 SCIP_AGGRROW* aggrrow, /**< aggregation row */
6733 SCIP_ROW** rows, /**< array of local rows */
6734 int* rowinds, /**< array of row indices */
6735 int* rowdepth, /**< array of LP depths */
6736 int nrows /**< number of local rows */
6737 )
6738 {
6739 int* rownnz;
6740 int i;
6741
6742 assert(aggrrow != NULL);
6743 assert(rows != NULL);
6744 assert(nrows > 0);
6745 assert(rowinds != NULL);
6746 assert(rowdepth != NULL);
6747
6748 /* sort row indices by increasing depth */
6749 SCIPsortIntInt(rowdepth, rowinds, nrows);
6750 assert(rowdepth[0] <= rowdepth[nrows-1]);
6751
6752 SCIP_CALL( SCIPsetAllocBufferArray(set, &rownnz, nrows) );
6753
6754 /* get number of nonzero entries for every row */
6755 for( i = 0; i < nrows; i++ )
6756 {
6757 SCIP_ROW* row = rows[rowinds[i]];
6758 assert(row != NULL);
6759
6760 rownnz[i] = row->len;
6761 }
6762
6763 /* since SCIP has no stable sorting function we sort each bucket separately */
6764 for( i = 0; i < nrows; i++ )
6765 {
6766 int j = i;
6767 int d = rowdepth[i];
6768
6769 /* search for the next row with a greater depth */
6770 while( j+1 < nrows && rowdepth[j+1] == d )
6771 j++;
6772
6773 /* the bucket has size one */
6774 if( j == i )
6775 continue;
6776
6777 assert(j-i+1 <= nrows);
6778
6779 /* sort row indices by increasing number of nonzero elements */
6780 SCIPsortIntIntInt(&rownnz[i], &rowdepth[i], &rowinds[i], j-i+1);
6781 assert(rownnz[i] <= rownnz[j]);
6782
6783 i = j;
6784 } /*lint --e{850} i is modified in the body of the for loop */
6785
6786 #ifndef NDEBUG
6787 for( i = 0; i < nrows-1; i++ )
6788 assert(rowdepth[i] < rowdepth[i+1] || (rowdepth[i] == rowdepth[i+1] && rownnz[i] <= rownnz[i+1]));
6789 #endif
6790
6791 SCIPsetFreeBufferArray(set, &rownnz);
6792
6793 return SCIP_OKAY;
6794 }
6795
6796 /** adds locally valid rows to the proof constraint */
6797 static
addLocalRows(SCIP_SET * set,SCIP_PROB * transprob,SCIP_LP * lp,SCIP_AGGRROW * proofrow,SCIP_ROW ** rows,SCIP_Real * dualsols,int * localrowinds,int * localrowdepth,int nlocalrows,SCIP_Real * proofact,int * validdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool * valid)6798 SCIP_RETCODE addLocalRows(
6799 SCIP_SET* set, /**< global SCIP settings */
6800 SCIP_PROB* transprob, /**< transformed problem */
6801 SCIP_LP* lp, /**< LP data */
6802 SCIP_AGGRROW* proofrow, /**< aggregated row representing the proof */
6803 SCIP_ROW** rows, /**< array if locally valid rows */
6804 SCIP_Real* dualsols, /**< dual solution vector */
6805 int* localrowinds, /**< array of row indecies */
6806 int* localrowdepth, /**< array of row depths */
6807 int nlocalrows, /**< number of local rows stored in rows array */
6808 SCIP_Real* proofact, /**< pointer to store the activity of the proof constraint */
6809 int* validdepth, /**< pointer to store the depth where the proof constraint is valid */
6810 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
6811 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
6812 SCIP_Bool* valid /**< pointer store whether the proof constraint is valid */
6813 )
6814 {
6815 SCIP_Bool infdelta;
6816 int i;
6817
6818 assert(set != NULL);
6819 assert(lp != NULL);
6820
6821 *validdepth = 0;
6822
6823 if( !set->conf_uselocalrows )
6824 return SCIP_OKAY;
6825
6826 SCIPsetDebugMsg(set, "add local rows to dual proof:\n");
6827
6828 /* check whether the proof is already valid, e.g., violated within the local bounds */
6829 *proofact = aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, &infdelta);
6830
6831 /* we stop if the minimal activity is infinite but all variables have a finite activity delta (bad numerics) */
6832 if( !infdelta && SCIPsetIsInfinity(set, REALABS(*proofact)) )
6833 {
6834 *valid = FALSE;
6835 return SCIP_OKAY;
6836 }
6837
6838 /* break if the proof is valid w.r.t local bounds
6839 * note: it can happen that the proof contains a variable with an infinite activity delta.
6840 * here, we don't break immediately because we might be able to fix it by adding local rows
6841 */
6842 if( !infdelta && SCIPsetIsGT(set, *proofact, SCIPaggrRowGetRhs(proofrow)) )
6843 {
6844 *valid = TRUE;
6845 return SCIP_OKAY;
6846 }
6847
6848 /* sort local rows by depth */
6849 SCIP_CALL( sortLocalRows(set, proofrow, rows, localrowinds, localrowdepth, nlocalrows) );
6850
6851 /* add successively local rows */
6852 for( i = 0; i < nlocalrows; ++i )
6853 {
6854 SCIP_ROW* row;
6855 int r;
6856
6857 r = localrowinds[i];
6858 row = rows[r];
6859
6860 assert(row != NULL);
6861 assert(row->len == 0 || row->cols != NULL);
6862 assert(row->len == 0 || row->vals != NULL);
6863 assert(row == lp->lpirows[r]);
6864 assert(row->local);
6865 assert(row->lpdepth == localrowdepth[i]);
6866
6867 /* ignore dual solution values of 0.0 (in this case: y_i == 0) */
6868 if( REALABS(dualsols[r]) > 0.0 )
6869 {
6870 #ifndef NDEBUG
6871 SCIP_Bool zerocontribution;
6872
6873 /* check dual feasibility */
6874 *valid = checkDualFeasibility(set, row, dualsols[r], &zerocontribution);
6875 assert(*valid);
6876 assert(!zerocontribution);
6877 #endif
6878
6879 if( SCIPsetIsDualfeasZero(set, dualsols[r]) )
6880 continue;
6881
6882 /* add row to dual proof */
6883 SCIP_CALL( addRowToAggrRow(set, row, -dualsols[r], proofrow) );
6884
6885 /* update depth where the proof is valid */
6886 if( *validdepth < localrowdepth[i] )
6887 *validdepth = localrowdepth[i];
6888
6889 /* get the new minimal activity */
6890 *proofact = aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, &infdelta);
6891
6892 /* we stop if the minimal activity is infinite but all variables have a finite activity delta (bad numerics) */
6893 if( !infdelta && SCIPsetIsInfinity(set, REALABS(*proofact)) )
6894 {
6895 *valid = FALSE;
6896 goto TERMINATE;
6897 }
6898
6899 /* break if the proof is valid w.r.t local bounds */
6900 if( !infdelta && SCIPsetIsGT(set, *proofact, SCIPaggrRowGetRhs(proofrow)) )
6901 {
6902 *valid = TRUE;
6903 break;
6904 }
6905 }
6906 }
6907
6908 /* remove all nearly zero coefficients */
6909 SCIPaggrRowRemoveZeros(set->scip, proofrow, TRUE, valid);
6910
6911 TERMINATE:
6912 if( !(*valid) )
6913 {
6914 SCIPsetDebugMsg(set, " -> proof is not valid: %g <= %g\n", *proofact, SCIPaggrRowGetRhs(proofrow));
6915 SCIPsetDebugMsg(set, " -> stop due to numerical troubles\n");
6916 }
6917 else
6918 {
6919 *proofact = aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, &infdelta);
6920
6921 /* we stop if the minimal activity is infinite but all variables have a finite activity delta (bad numerics) */
6922 if( !infdelta && SCIPsetIsInfinity(set, REALABS(*proofact)) )
6923 {
6924 *valid = FALSE;
6925 SCIPsetDebugMsg(set, " -> proof is not valid: %g <= %g [infdelta: %d]\n", *proofact, SCIPaggrRowGetRhs(proofrow), infdelta);
6926 }
6927 else if( infdelta || SCIPsetIsLE(set, *proofact, SCIPaggrRowGetRhs(proofrow)) )
6928 {
6929 *valid = FALSE;
6930 SCIPsetDebugMsg(set, " -> proof is not valid: %g <= %g [infdelta: %d]\n", *proofact, SCIPaggrRowGetRhs(proofrow), infdelta);
6931 }
6932 }
6933
6934 return SCIP_OKAY;
6935 }
6936
6937 /** calculates a Farkas proof from the current dual LP solution */
6938 static
getFarkasProof(SCIP_SET * set,SCIP_PROB * prob,SCIP_LP * lp,SCIP_LPI * lpi,SCIP_TREE * tree,SCIP_AGGRROW * farkasrow,SCIP_Real * farkasact,int * validdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool * valid)6939 SCIP_RETCODE getFarkasProof(
6940 SCIP_SET* set, /**< global SCIP settings */
6941 SCIP_PROB* prob, /**< transformed problem */
6942 SCIP_LP* lp, /**< LP data */
6943 SCIP_LPI* lpi, /**< LPI data */
6944 SCIP_TREE* tree, /**< tree data */
6945 SCIP_AGGRROW* farkasrow, /**< aggregated row representing the proof */
6946 SCIP_Real* farkasact, /**< maximal activity of the proof constraint */
6947 int* validdepth, /**< pointer to store the valid depth of the proof constraint */
6948 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
6949 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
6950 SCIP_Bool* valid /**< pointer store whether the proof constraint is valid */
6951 )
6952 {
6953 SCIP_ROW** rows;
6954 SCIP_Real* dualfarkas;
6955 SCIP_ROW* row;
6956 int* localrowinds;
6957 int* localrowdepth;
6958 SCIP_Bool infdelta;
6959 int nlocalrows;
6960 int nrows;
6961 int r;
6962
6963 assert(set != NULL);
6964 assert(prob != NULL);
6965 assert(lp != NULL);
6966 assert(lp->flushed);
6967 assert(lp->solved);
6968 assert(curvarlbs != NULL);
6969 assert(curvarubs != NULL);
6970 assert(valid != NULL);
6971
6972 assert(SCIPlpiIsPrimalInfeasible(lpi) || SCIPlpiIsObjlimExc(lpi) || SCIPlpiIsDualFeasible(lpi));
6973 assert(SCIPlpiIsPrimalInfeasible(lpi) || !SCIPlpDivingObjChanged(lp));
6974
6975 /* get LP rows and problem variables */
6976 rows = SCIPlpGetRows(lp);
6977 nrows = SCIPlpGetNRows(lp);
6978 assert(nrows == 0 || rows != NULL);
6979 assert(nrows == lp->nlpirows);
6980
6981 /* it can happen that infeasibility is detetected within LP presolve. in that case, the LP solver may not be able to
6982 * to return the dual ray.
6983 */
6984 if( !SCIPlpiHasDualRay(lpi) )
6985 {
6986 *valid = FALSE;
6987 return SCIP_OKAY;
6988 }
6989
6990 assert(farkasrow != NULL);
6991
6992 /* allocate temporary memory */
6993 SCIP_CALL( SCIPsetAllocBufferArray(set, &dualfarkas, nrows) );
6994 BMSclearMemoryArray(dualfarkas, nrows);
6995
6996 /* get dual Farkas values of rows */
6997 SCIP_CALL( SCIPlpiGetDualfarkas(lpi, dualfarkas) );
6998
6999 localrowinds = NULL;
7000 localrowdepth = NULL;
7001 nlocalrows = 0;
7002
7003 /* calculate the Farkas row */
7004 (*valid) = TRUE;
7005 (*validdepth) = 0;
7006
7007 for( r = 0; r < nrows; ++r )
7008 {
7009 row = rows[r];
7010 assert(row != NULL);
7011 assert(row->len == 0 || row->cols != NULL);
7012 assert(row->len == 0 || row->vals != NULL);
7013 assert(row == lp->lpirows[r]);
7014
7015 /* ignore dual ray values of 0.0 (in this case: y_i == z_i == 0) */
7016 if( REALABS(dualfarkas[r]) > 0.0 )
7017 {
7018 SCIP_Bool zerocontribution;
7019
7020 /* check dual feasibility */
7021 *valid = checkDualFeasibility(set, row, dualfarkas[r], &zerocontribution);
7022
7023 if( !(*valid) )
7024 goto TERMINATE;
7025
7026 if( zerocontribution )
7027 continue;
7028
7029 if( SCIPsetIsDualfeasZero(set, dualfarkas[r]) )
7030 continue;
7031
7032 if( !row->local )
7033 {
7034 SCIP_CALL( addRowToAggrRow(set, row, -dualfarkas[r], farkasrow) );
7035
7036 /* due to numerical reasons we want to stop */
7037 if( REALABS(SCIPaggrRowGetRhs(farkasrow)) > NUMSTOP )
7038 {
7039 (*valid) = FALSE;
7040 goto TERMINATE;
7041 }
7042 }
7043 else
7044 {
7045 int lpdepth = SCIProwGetLPDepth(row);
7046
7047 if( nlocalrows == 0 && lpdepth < SCIPtreeGetFocusDepth(tree) )
7048 {
7049 SCIP_CALL( SCIPsetAllocBufferArray(set, &localrowinds, nrows-r) );
7050 SCIP_CALL( SCIPsetAllocBufferArray(set, &localrowdepth, nrows-r) );
7051 }
7052
7053 if( lpdepth < SCIPtreeGetFocusDepth(tree) )
7054 {
7055 assert(localrowinds != NULL);
7056 assert(localrowdepth != NULL);
7057
7058 localrowinds[nlocalrows] = r;
7059 localrowdepth[nlocalrows++] = lpdepth;
7060 }
7061 }
7062 }
7063 }
7064
7065 /* remove all coefficients that are too close to zero */
7066 SCIPaggrRowRemoveZeros(set->scip, farkasrow, TRUE, valid);
7067
7068 if( !(*valid) )
7069 goto TERMINATE;
7070
7071 infdelta = FALSE;
7072
7073 /* calculate the current Farkas activity, always using the best bound w.r.t. the Farkas coefficient */
7074 *farkasact = aggrRowGetMinActivity(set, prob, farkasrow, curvarlbs, curvarubs, &infdelta);
7075
7076 SCIPsetDebugMsg(set, " -> farkasact=%g farkasrhs=%g [infdelta: %d], \n",
7077 (*farkasact), SCIPaggrRowGetRhs(farkasrow), infdelta);
7078
7079 /* The constructed proof is not valid, this can happen due to numerical reasons,
7080 * e.g., we only consider rows r with !SCIPsetIsZero(set, dualfarkas[r]),
7081 * or because of local rows were ignored so far.
7082 * Due to the latter case, it might happen at least one variable contributes
7083 * with an infinite value to the activity (see: https://git.zib.de/integer/scip/issues/2743)
7084 */
7085 if( infdelta || SCIPsetIsFeasLE(set, *farkasact, SCIPaggrRowGetRhs(farkasrow)))
7086 {
7087 /* add contribution of local rows */
7088 if( nlocalrows > 0 && set->conf_uselocalrows > 0 )
7089 {
7090 SCIP_CALL( addLocalRows(set, prob, lp, farkasrow, rows, dualfarkas, localrowinds, localrowdepth,
7091 nlocalrows, farkasact, validdepth, curvarlbs, curvarubs, valid) );
7092 }
7093 else
7094 {
7095 (*valid) = FALSE;
7096 SCIPsetDebugMsg(set, " -> proof is not valid to due infinite activity delta\n");
7097 }
7098 }
7099
7100 TERMINATE:
7101
7102 SCIPfreeBufferArrayNull(set->scip, &localrowdepth);
7103 SCIPfreeBufferArrayNull(set->scip, &localrowinds);
7104 SCIPsetFreeBufferArray(set, &dualfarkas);
7105
7106 return SCIP_OKAY;
7107 }
7108
7109 /** calculates a Farkas proof from the current dual LP solution */
7110 static
getDualProof(SCIP_SET * set,SCIP_PROB * transprob,SCIP_LP * lp,SCIP_LPI * lpi,SCIP_TREE * tree,SCIP_AGGRROW * farkasrow,SCIP_Real * farkasact,int * validdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool * valid)7111 SCIP_RETCODE getDualProof(
7112 SCIP_SET* set, /**< global SCIP settings */
7113 SCIP_PROB* transprob, /**< transformed problem */
7114 SCIP_LP* lp, /**< LP data */
7115 SCIP_LPI* lpi, /**< LPI data */
7116 SCIP_TREE* tree, /**< tree data */
7117 SCIP_AGGRROW* farkasrow, /**< aggregated row representing the proof */
7118 SCIP_Real* farkasact, /**< maximal activity of the proof constraint */
7119 int* validdepth, /**< pointer to store the valid depth of the proof constraint */
7120 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
7121 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
7122 SCIP_Bool* valid /**< pointer store whether the proof constraint is valid */
7123 )
7124 {
7125 SCIP_RETCODE retcode;
7126 SCIP_ROW** rows;
7127 SCIP_ROW* row;
7128 SCIP_Real* primsols;
7129 SCIP_Real* dualsols;
7130 SCIP_Real* redcosts;
7131 int* localrowinds;
7132 int* localrowdepth;
7133 SCIP_Real maxabsdualsol;
7134 SCIP_Bool infdelta;
7135 int nlocalrows;
7136 int nrows;
7137 int ncols;
7138 int r;
7139
7140 assert(set != NULL);
7141 assert(transprob != NULL);
7142 assert(lp != NULL);
7143 assert(lp->flushed);
7144 assert(lp->solved);
7145 assert(curvarlbs != NULL);
7146 assert(curvarubs != NULL);
7147 assert(valid != NULL);
7148
7149 *validdepth = 0;
7150 *valid = TRUE;
7151
7152 localrowinds = NULL;
7153 localrowdepth = NULL;
7154 nlocalrows = 0;
7155
7156 /* get LP rows and problem variables */
7157 rows = SCIPlpGetRows(lp);
7158 nrows = SCIPlpGetNRows(lp);
7159 ncols = SCIPlpGetNCols(lp);
7160 assert(nrows == 0 || rows != NULL);
7161 assert(nrows == lp->nlpirows);
7162
7163 /* get temporary memory */
7164 SCIP_CALL( SCIPsetAllocBufferArray(set, &primsols, ncols) );
7165 SCIP_CALL( SCIPsetAllocBufferArray(set, &dualsols, nrows) );
7166 SCIP_CALL( SCIPsetAllocBufferArray(set, &redcosts, ncols) );
7167
7168 /* get solution from LPI */
7169 retcode = SCIPlpiGetSol(lpi, NULL, primsols, dualsols, NULL, redcosts);
7170 if( retcode == SCIP_LPERROR ) /* on an error in the LP solver, just abort the conflict analysis */
7171 {
7172 (*valid) = FALSE;
7173 goto TERMINATE;
7174 }
7175 SCIP_CALL( retcode );
7176 #ifdef SCIP_DEBUG
7177 {
7178 SCIP_Real objval;
7179 SCIP_CALL( SCIPlpiGetObjval(lpi, &objval) );
7180 SCIPsetDebugMsg(set, " -> LP objval: %g\n", objval);
7181 }
7182 #endif
7183
7184 /* check whether the dual solution is numerically stable */
7185 maxabsdualsol = 0;
7186 for( r = 0; r < nrows; r++ )
7187 {
7188 SCIP_Real absdualsol = REALABS(dualsols[r]);
7189
7190 if( absdualsol > maxabsdualsol )
7191 maxabsdualsol = absdualsol;
7192 }
7193
7194 /* don't consider dual solution with maxabsdualsol > 1e+07, this would almost cancel out the objective constraint */
7195 if( maxabsdualsol > 1e+07 )
7196 {
7197 (*valid) = FALSE;
7198 goto TERMINATE;
7199 }
7200
7201 /* clear the proof */
7202 SCIPaggrRowClear(farkasrow);
7203
7204 /* Let y be the dual solution and r be the reduced cost vector. Let z be defined as
7205 * z_i := y_i if i is a global row,
7206 * z_i := 0 if i is a local row.
7207 * Define the set X := {x | lhs <= Ax <= rhs, lb <= x <= ub, c^Tx <= c*}, with c* being the current primal bound.
7208 * Then the following inequalities are valid for all x \in X:
7209 * - c* <= -c^Tx
7210 * <=> z^TAx - c* <= (z^TA - c^T) x
7211 * <=> z^TAx - c* <= (y^TA - c^T - (y-z)^TA) x
7212 * <=> z^TAx - c* <= (-r^T - (y-z)^TA) x (dual feasibility of (y,r): y^TA + r^T == c^T)
7213 * Because lhs <= Ax <= rhs and lb <= x <= ub, the inequality can be relaxed to give
7214 * min{z^Tq | lhs <= q <= rhs} - c* <= max{(-r^T - (y-z)^TA) x | lb <= x <= ub}, or X = {}.
7215 *
7216 * The resulting dual row is: z^T{lhs,rhs} - c* <= (-r^T - (y-z)^TA){lb,ub},
7217 * where lhs, rhs, lb, and ub are selected in order to maximize the feasibility of the row.
7218 */
7219
7220 /* add the objective function to the aggregation row with current cutoff bound as right-hand side
7221 *
7222 * use a slightly tighter cutoff bound, because solutions with equal objective value should also be declared
7223 * infeasible
7224 */
7225 SCIP_CALL( SCIPaggrRowAddObjectiveFunction(set->scip, farkasrow, lp->cutoffbound - SCIPsetSumepsilon(set), 1.0) );
7226
7227 /* dual row: z^T{lhs,rhs} - c* <= (-r^T - (y-z)^TA){lb,ub}
7228 * process rows: add z^T{lhs,rhs} to the dual row's left hand side, and -(y-z)^TA to the dual row's coefficients
7229 */
7230 for( r = 0; r < nrows; ++r )
7231 {
7232 row = rows[r];
7233 assert(row != NULL);
7234 assert(row->len == 0 || row->cols != NULL);
7235 assert(row->len == 0 || row->vals != NULL);
7236 assert(row == lp->lpirows[r]);
7237
7238 /* ignore dual solution values of 0.0 (in this case: y_i == z_i == 0) */
7239 if( REALABS(dualsols[r]) > 0.0 )
7240 {
7241 SCIP_Bool zerocontribution;
7242
7243 /* check dual feasibility */
7244 *valid = checkDualFeasibility(set, row, dualsols[r], &zerocontribution);
7245
7246 if( !(*valid) )
7247 goto TERMINATE;
7248
7249 if( zerocontribution )
7250 continue;
7251
7252 if( SCIPsetIsDualfeasZero(set, dualsols[r]) )
7253 continue;
7254
7255 /* skip local row */
7256 if( !row->local )
7257 {
7258 SCIP_CALL( addRowToAggrRow(set, row, -dualsols[r], farkasrow) );
7259
7260 /* due to numerical reasons we want to stop */
7261 if( REALABS(SCIPaggrRowGetRhs(farkasrow)) > NUMSTOP )
7262 {
7263 (*valid) = FALSE;
7264 goto TERMINATE;
7265 }
7266 }
7267 else
7268 {
7269 int lpdepth = SCIProwGetLPDepth(row);
7270
7271 if( nlocalrows == 0 && lpdepth < SCIPtreeGetFocusDepth(tree) )
7272 {
7273 SCIP_CALL( SCIPsetAllocBufferArray(set, &localrowinds, nrows-r) );
7274 SCIP_CALL( SCIPsetAllocBufferArray(set, &localrowdepth, nrows-r) );
7275 }
7276
7277 if( lpdepth < SCIPtreeGetFocusDepth(tree) )
7278 {
7279 assert(localrowinds != NULL);
7280 assert(localrowdepth != NULL);
7281
7282 localrowinds[nlocalrows] = r;
7283 localrowdepth[nlocalrows++] = lpdepth;
7284 }
7285 }
7286 }
7287 }
7288
7289 /* remove all nearly zero coefficients */
7290 SCIPaggrRowRemoveZeros(set->scip, farkasrow, TRUE, valid);
7291
7292 if( !(*valid) )
7293 goto TERMINATE;
7294
7295 infdelta = FALSE;
7296
7297 /* check validity of the proof */
7298 *farkasact = aggrRowGetMinActivity(set, transprob, farkasrow, curvarlbs, curvarubs, &infdelta);
7299
7300 SCIPsetDebugMsg(set, " -> farkasact=%g farkasrhs=%g [infdelta: %d], \n",
7301 (*farkasact), SCIPaggrRowGetRhs(farkasrow), infdelta);
7302
7303 /* The constructed proof is not valid, this can happen due to numerical reasons,
7304 * e.g., we only consider rows r with !SCIPsetIsZero(set, dualsol[r]),
7305 * or because of local rows were ignored so far.
7306 * Due to the latter case, it might happen at least one variable contributes
7307 * with an infinite value to the activity (see: https://git.zib.de/integer/scip/issues/2743)
7308 */
7309 if( infdelta || SCIPsetIsFeasLE(set, *farkasact, SCIPaggrRowGetRhs(farkasrow)))
7310 {
7311 /* add contribution of local rows */
7312 if( nlocalrows > 0 && set->conf_uselocalrows > 0 )
7313 {
7314 SCIP_CALL( addLocalRows(set, transprob, lp, farkasrow, rows, dualsols, localrowinds, localrowdepth,
7315 nlocalrows, farkasact, validdepth, curvarlbs, curvarubs, valid) );
7316 }
7317 else
7318 {
7319 (*valid) = FALSE;
7320 SCIPsetDebugMsg(set, " -> proof is not valid to due infinite activity delta\n");
7321 }
7322 }
7323
7324 TERMINATE:
7325
7326 SCIPfreeBufferArrayNull(set->scip, &localrowdepth);
7327 SCIPfreeBufferArrayNull(set->scip, &localrowinds);
7328 SCIPsetFreeBufferArray(set, &redcosts);
7329 SCIPsetFreeBufferArray(set, &dualsols);
7330 SCIPsetFreeBufferArray(set, &primsols);
7331
7332 return SCIP_OKAY;
7333 }
7334
7335 #ifdef SCIP_DEBUG
7336 static
debugPrintViolationInfo(SCIP_SET * set,SCIP_Real minact,SCIP_Real rhs,const char * infostr)7337 void debugPrintViolationInfo(
7338 SCIP_SET* set, /**< global SCIP settings */
7339 SCIP_Real minact, /**< min activity */
7340 SCIP_Real rhs, /**< right hand side */
7341 const char* infostr /**< additional info for this debug message, or NULL */
7342 )
7343 {
7344 SCIPsetDebugMsg(set, "-> %sminact=%.15g rhs=%.15g violation=%.15g\n",infostr != NULL ? infostr : "" , minact, rhs, minact - rhs);
7345 }
7346 #else
7347 #define debugPrintViolationInfo(...) /**/
7348 #endif
7349
7350 /** apply coefficient tightening */
7351 static
tightenCoefficients(SCIP_SET * set,SCIP_PROOFSET * proofset,int * nchgcoefs,SCIP_Bool * redundant)7352 void tightenCoefficients(
7353 SCIP_SET* set, /**< global SCIP settings */
7354 SCIP_PROOFSET* proofset, /**< proof set */
7355 int* nchgcoefs, /**< pointer to store number of changed coefficients */
7356 SCIP_Bool* redundant /**< pointer to store whether the proof set is redundant */
7357 )
7358 {
7359 #ifdef SCIP_DEBUG
7360 SCIP_Real absmax = 0.0;
7361 SCIP_Real absmin = SCIPsetInfinity(set);
7362 int i;
7363
7364 for( i = 0; i < proofset->nnz; i++ )
7365 {
7366 absmax = MAX(absmax, REALABS(proofset->vals[i]));
7367 absmin = MIN(absmin, REALABS(proofset->vals[i]));
7368 }
7369 #endif
7370
7371 (*redundant) = SCIPcutsTightenCoefficients(set->scip, FALSE, proofset->vals, &proofset->rhs, proofset->inds, &proofset->nnz, nchgcoefs);
7372
7373 #ifdef SCIP_DEBUG
7374 {
7375 SCIP_Real newabsmax = 0.0;
7376 SCIP_Real newabsmin = SCIPsetInfinity(set);
7377
7378 for( i = 0; i < proofset->nnz; i++ )
7379 {
7380 newabsmax = MAX(newabsmax, REALABS(proofset->vals[i]));
7381 newabsmin = MIN(newabsmin, REALABS(proofset->vals[i]));
7382 }
7383
7384 SCIPsetDebugMsg(set, "coefficient tightening: [%.15g,%.15g] -> [%.15g,%.15g] (nnz: %d, nchg: %d rhs: %.15g)\n",
7385 absmin, absmax, newabsmin, newabsmax, proofsetGetNVars(proofset), *nchgcoefs, proofsetGetRhs(proofset));
7386 printf("coefficient tightening: [%.15g,%.15g] -> [%.15g,%.15g] (nnz: %d, nchg: %d rhs: %.15g)\n",
7387 absmin, absmax, newabsmin, newabsmax, proofsetGetNVars(proofset), *nchgcoefs, proofsetGetRhs(proofset));
7388 }
7389 #endif
7390 }
7391
7392 /** try to generate alternative proofs by applying subadditive functions */
7393 static
separateAlternativeProofs(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_TREE * tree,BMS_BLKMEM * blkmem,SCIP_AGGRROW * proofrow,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_CONFTYPE conflicttype)7394 SCIP_RETCODE separateAlternativeProofs(
7395 SCIP_CONFLICT* conflict, /**< conflict analysis data */
7396 SCIP_SET* set, /**< global SCIP settings */
7397 SCIP_STAT* stat, /**< dynamic SCIP statistics */
7398 SCIP_PROB* transprob, /**< transformed problem */
7399 SCIP_TREE* tree, /**< tree data */
7400 BMS_BLKMEM* blkmem, /**< block memory */
7401 SCIP_AGGRROW* proofrow, /**< proof rows data */
7402 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
7403 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
7404 SCIP_CONFTYPE conflicttype /**< type of the conflict */
7405 )
7406 {
7407 SCIP_VAR** vars;
7408 SCIP_SOL* refsol;
7409 SCIP_Real* cutcoefs;
7410 SCIP_Real cutefficacy;
7411 SCIP_Real cutrhs;
7412 SCIP_Real proofefficiacy;
7413 SCIP_Real efficiacynorm;
7414 SCIP_Bool islocal;
7415 SCIP_Bool cutsuccess;
7416 SCIP_Bool success;
7417 SCIP_Bool infdelta;
7418 int* cutinds;
7419 int* inds;
7420 int cutnnz;
7421 int nnz;
7422 int nvars;
7423 int i;
7424
7425 vars = SCIPprobGetVars(transprob);
7426 nvars = SCIPprobGetNVars(transprob);
7427
7428 inds = SCIPaggrRowGetInds(proofrow);
7429 nnz = SCIPaggrRowGetNNz(proofrow);
7430
7431 proofefficiacy = aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, &infdelta);
7432
7433 if( infdelta )
7434 return SCIP_OKAY;
7435
7436 proofefficiacy -= SCIPaggrRowGetRhs(proofrow);
7437
7438 efficiacynorm = SCIPaggrRowCalcEfficacyNorm(set->scip, proofrow);
7439 proofefficiacy /= MAX(1e-6, efficiacynorm);
7440
7441 /* create reference solution */
7442 SCIP_CALL( SCIPcreateSol(set->scip, &refsol, NULL) );
7443
7444 /* initialize with average solution */
7445 for( i = 0; i < nvars; i++ )
7446 {
7447 SCIP_CALL( SCIPsolSetVal(refsol, set, stat, tree, vars[i], SCIPvarGetAvgSol(vars[i])) );
7448 }
7449
7450 /* set all variables that are part of the proof to its active local bound */
7451 for( i = 0; i < nnz; i++ )
7452 {
7453 SCIP_Real val = SCIPaggrRowGetProbvarValue(proofrow, inds[i]);
7454
7455 if( val > 0.0 )
7456 {
7457 SCIP_CALL( SCIPsolSetVal(refsol, set, stat, tree, vars[inds[i]], curvarubs[inds[i]]) );
7458 }
7459 else
7460 {
7461 SCIP_CALL( SCIPsolSetVal(refsol, set, stat, tree, vars[inds[i]], curvarlbs[inds[i]]) );
7462 }
7463 }
7464
7465 SCIP_CALL( SCIPsetAllocBufferArray(set, &cutcoefs, nvars) );
7466 SCIP_CALL( SCIPsetAllocBufferArray(set, &cutinds, nvars) );
7467
7468 cutnnz = 0;
7469 cutefficacy = -SCIPsetInfinity(set);
7470
7471 /* apply flow cover */
7472 SCIP_CALL( SCIPcalcFlowCover(set->scip, refsol, POSTPROCESS, BOUNDSWITCH, ALLOWLOCAL, proofrow, \
7473 cutcoefs, &cutrhs, cutinds, &cutnnz, &cutefficacy, NULL, &islocal, &cutsuccess) );
7474 success = cutsuccess;
7475
7476 /* apply MIR */
7477 SCIP_CALL( SCIPcutGenerationHeuristicCMIR(set->scip, refsol, POSTPROCESS, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, INT_MAX, \
7478 NULL, NULL, MINFRAC, MAXFRAC, proofrow, cutcoefs, &cutrhs, cutinds, &cutnnz, &cutefficacy, NULL, \
7479 &islocal, &cutsuccess) );
7480 success = (success || cutsuccess);
7481
7482 /* replace the current proof */
7483 if( success && !islocal && SCIPsetIsPositive(set, cutefficacy) && cutefficacy * nnz > proofefficiacy * cutnnz )
7484 {
7485 SCIP_PROOFSET* alternativeproofset;
7486 SCIP_Bool redundant;
7487 int nchgcoefs;
7488
7489 SCIP_CALL( proofsetCreate(&alternativeproofset, blkmem) );
7490 alternativeproofset->conflicttype = (conflicttype == SCIP_CONFTYPE_INFEASLP ? SCIP_CONFTYPE_ALTINFPROOF : SCIP_CONFTYPE_ALTBNDPROOF);
7491
7492 SCIP_CALL( proofsetAddSparseData(alternativeproofset, blkmem, cutcoefs, cutinds, cutnnz, cutrhs) );
7493
7494 /* apply coefficient tightening */
7495 tightenCoefficients(set, alternativeproofset, &nchgcoefs, &redundant);
7496
7497 if( !redundant )
7498 {
7499 SCIP_CALL( conflictInsertProofset(conflict, set, alternativeproofset) );
7500 }
7501 else
7502 {
7503 proofsetFree(&alternativeproofset, blkmem);
7504 }
7505 } /*lint !e438*/
7506
7507 SCIPsetFreeBufferArray(set, &cutinds);
7508 SCIPsetFreeBufferArray(set, &cutcoefs);
7509
7510 SCIP_CALL( SCIPfreeSol(set->scip, &refsol) );
7511
7512 return SCIP_OKAY;
7513 }
7514
7515 /** tighten a given infeasibility proof a^Tx <= b with minact > b w.r.t. local bounds
7516 *
7517 * 1) Apply cut generating functions
7518 * - c-MIR
7519 * - Flow-cover
7520 * - TODO: implement other subadditive functions
7521 * 2) Remove continuous variables contributing with its global bound
7522 * - TODO: implement a variant of non-zero-cancellation
7523 */
7524 static
tightenDualproof(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,BMS_BLKMEM * blkmem,SCIP_PROB * transprob,SCIP_TREE * tree,SCIP_AGGRROW * proofrow,int validdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool initialproof)7525 SCIP_RETCODE tightenDualproof(
7526 SCIP_CONFLICT* conflict, /**< conflict analysis data */
7527 SCIP_SET* set, /**< global SCIP settings */
7528 SCIP_STAT* stat, /**< dynamic SCIP statistics */
7529 BMS_BLKMEM* blkmem, /**< block memory */
7530 SCIP_PROB* transprob, /**< transformed problem */
7531 SCIP_TREE* tree, /**< tree data */
7532 SCIP_AGGRROW* proofrow, /**< aggregated row representing the proof */
7533 int validdepth, /**< depth where the proof is valid */
7534 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
7535 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
7536 SCIP_Bool initialproof /**< do we analyze the initial reason of infeasibility? */
7537 )
7538 {
7539 SCIP_VAR** vars;
7540 SCIP_Real* vals;
7541 int* inds;
7542 SCIP_PROOFSET* proofset;
7543 SCIP_Bool valid;
7544 SCIP_Bool redundant;
7545 int nnz;
7546 int nchgcoefs;
7547 int nbinvars;
7548 int ncontvars;
7549 int nintvars;
7550 int i;
7551
7552 assert(conflict->proofset != NULL);
7553 assert(curvarlbs != NULL);
7554 assert(curvarubs != NULL);
7555
7556 vars = SCIPprobGetVars(transprob);
7557 nbinvars = 0;
7558 nintvars = 0;
7559 ncontvars = 0;
7560
7561 inds = SCIPaggrRowGetInds(proofrow);
7562 nnz = SCIPaggrRowGetNNz(proofrow);
7563
7564 /* count number of binary, integer, and continuous variables */
7565 for( i = 0; i < nnz; i++ )
7566 {
7567 assert(SCIPvarGetProbindex(vars[inds[i]]) == inds[i]);
7568
7569 if( SCIPvarIsBinary(vars[inds[i]]) )
7570 ++nbinvars;
7571 else if( SCIPvarIsIntegral(vars[inds[i]]) )
7572 ++nintvars;
7573 else
7574 ++ncontvars;
7575 }
7576
7577 SCIPsetDebugMsg(set, "start dual proof tightening:\n");
7578 SCIPsetDebugMsg(set, "-> tighten dual proof: nvars=%d (bin=%d, int=%d, cont=%d)\n",
7579 nnz, nbinvars, nintvars, ncontvars);
7580 debugPrintViolationInfo(set, aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, NULL), SCIPaggrRowGetRhs(proofrow), NULL);
7581
7582 /* try to find an alternative proof of local infeasibility that is stronger */
7583 if( set->conf_sepaaltproofs )
7584 {
7585 SCIP_CALL( separateAlternativeProofs(conflict, set, stat, transprob, tree, blkmem, proofrow, curvarlbs, curvarubs,
7586 conflict->conflictset->conflicttype) );
7587 }
7588
7589 if( initialproof )
7590 proofset = conflict->proofset;
7591 else
7592 {
7593 SCIP_CALL( proofsetCreate(&proofset, blkmem) );
7594 }
7595
7596 /* start with a proofset containing all variables with a non-zero coefficient in the dual proof */
7597 SCIP_CALL( proofsetAddAggrrow(proofset, set, blkmem, proofrow) );
7598 proofset->conflicttype = conflict->conflictset->conflicttype;
7599 proofset->validdepth = validdepth;
7600
7601 /* get proof data */
7602 vals = proofsetGetVals(proofset);
7603 inds = proofsetGetInds(proofset);
7604 nnz = proofsetGetNVars(proofset);
7605
7606 #ifndef NDEBUG
7607 for( i = 0; i < nnz; i++ )
7608 {
7609 int idx = inds[i];
7610 if( vals[i] > 0.0 )
7611 assert(!SCIPsetIsInfinity(set, -curvarlbs[idx]));
7612 if( vals[i] < 0.0 )
7613 assert(!SCIPsetIsInfinity(set, curvarubs[idx]));
7614 }
7615 #endif
7616
7617 /* remove continuous variable contributing with their global bound
7618 *
7619 * todo: check whether we also want to do that for bound exceeding proofs, but then we cannot update the
7620 * conflict anymore
7621 */
7622 if( proofset->conflicttype == SCIP_CONFTYPE_INFEASLP )
7623 {
7624 /* remove all continuous variables that have equal global and local bounds (ub or lb depend on the sign)
7625 * from the proof
7626 */
7627
7628 for( i = 0; i < nnz && nnz > 1; )
7629 {
7630 SCIP_Real val;
7631 int idx = inds[i];
7632
7633 assert(vars[idx] != NULL);
7634
7635 val = vals[i];
7636 assert(!SCIPsetIsZero(set, val));
7637
7638 /* skip integral variables */
7639 if( SCIPvarGetType(vars[idx]) != SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(vars[idx]) != SCIP_VARTYPE_IMPLINT )
7640 {
7641 i++;
7642 continue;
7643 }
7644 else
7645 {
7646 SCIP_Real glbbd;
7647 SCIP_Real locbd;
7648
7649 /* get appropriate global and local bounds */
7650 glbbd = (val < 0.0 ? SCIPvarGetUbGlobal(vars[idx]) : SCIPvarGetLbGlobal(vars[idx]));
7651 locbd = (val < 0.0 ? curvarubs[idx] : curvarlbs[idx]);
7652
7653 if( !SCIPsetIsEQ(set, glbbd, locbd) )
7654 {
7655 i++;
7656 continue;
7657 }
7658
7659 SCIPsetDebugMsg(set, "-> remove continuous variable <%s>: glb=[%g,%g], loc=[%g,%g], val=%g\n",
7660 SCIPvarGetName(vars[idx]), SCIPvarGetLbGlobal(vars[idx]), SCIPvarGetUbGlobal(vars[idx]),
7661 curvarlbs[idx], curvarubs[idx], val);
7662
7663 proofsetCancelVarWithBound(proofset, set, vars[idx], i, &valid);
7664 assert(valid); /* this should be always fulfilled at this place */
7665
7666 --nnz;
7667 }
7668 }
7669 }
7670
7671 /* apply coefficient tightening to initial proof */
7672 tightenCoefficients(set, proofset, &nchgcoefs, &redundant);
7673
7674 /* it can happen that the constraints is almost globally redundant w.r.t to the maximal activity,
7675 * e.g., due to numerics. in this case, we want to discard the proof
7676 */
7677 if( redundant )
7678 {
7679 #ifndef NDEBUG
7680 SCIP_Real eps = MIN(0.01, 10.0*set->num_feastol);
7681 assert(proofset->rhs - getMaxActivity(set, transprob, proofset->vals, proofset->inds, proofset->nnz, NULL, NULL) < eps);
7682 #endif
7683 if( initialproof )
7684 {
7685 proofsetClear(proofset);
7686 }
7687 else
7688 {
7689 proofsetFree(&proofset, blkmem);
7690 }
7691 }
7692 else
7693 {
7694 if( !initialproof )
7695 {
7696 SCIP_CALL( conflictInsertProofset(conflict, set, proofset) );
7697 }
7698
7699 if( nchgcoefs > 0 )
7700 {
7701 if( proofset->conflicttype == SCIP_CONFTYPE_INFEASLP )
7702 proofset->conflicttype = SCIP_CONFTYPE_ALTINFPROOF;
7703 else if( proofset->conflicttype == SCIP_CONFTYPE_BNDEXCEEDING )
7704 proofset->conflicttype = SCIP_CONFTYPE_ALTBNDPROOF;
7705 }
7706 }
7707
7708 return SCIP_OKAY;
7709 }
7710
7711 /** perform conflict analysis based on a dual unbounded ray
7712 *
7713 * given an aggregation of rows lhs <= a^Tx such that lhs > maxactivity. if the constraint has size one we add a
7714 * bound change instead of the constraint.
7715 */
7716 static
conflictAnalyzeDualProof(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,BMS_BLKMEM * blkmem,SCIP_PROB * origprob,SCIP_PROB * transprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_AGGRROW * proofrow,int validdepth,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,SCIP_Bool initialproof,SCIP_Bool * globalinfeasible,SCIP_Bool * success)7717 SCIP_RETCODE conflictAnalyzeDualProof(
7718 SCIP_CONFLICT* conflict, /**< conflict analysis data */
7719 SCIP_SET* set, /**< global SCIP settings */
7720 SCIP_STAT* stat, /**< dynamic SCIP statistics */
7721 BMS_BLKMEM* blkmem, /**< block memory */
7722 SCIP_PROB* origprob, /**< original problem */
7723 SCIP_PROB* transprob, /**< transformed problem */
7724 SCIP_TREE* tree, /**< tree data */
7725 SCIP_REOPT* reopt, /**< reoptimization data */
7726 SCIP_LP* lp, /**< LP data */
7727 SCIP_AGGRROW* proofrow, /**< aggregated row representing the proof */
7728 int validdepth, /**< valid depth of the dual proof */
7729 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
7730 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
7731 SCIP_Bool initialproof, /**< do we analyze the initial reason of infeasibility? */
7732 SCIP_Bool* globalinfeasible, /**< pointer to store whether global infeasibility could be proven */
7733 SCIP_Bool* success /**< pointer to store success result */
7734 )
7735 {
7736 SCIP_Real rhs;
7737 SCIP_Real minact;
7738 SCIP_Bool infdelta;
7739 int nnz;
7740
7741 assert(set != NULL);
7742 assert(transprob != NULL);
7743 assert(validdepth >= 0);
7744 assert(validdepth == 0 || validdepth < SCIPtreeGetFocusDepth(tree));
7745
7746 /* get sparse data */
7747 nnz = SCIPaggrRowGetNNz(proofrow);
7748 rhs = SCIPaggrRowGetRhs(proofrow);
7749
7750 *globalinfeasible = FALSE;
7751 *success = FALSE;
7752
7753 /* get minimal activity w.r.t. local bounds */
7754 minact = aggrRowGetMinActivity(set, transprob, proofrow, curvarlbs, curvarubs, &infdelta);
7755
7756 if( infdelta )
7757 return SCIP_OKAY;
7758
7759 /* only run is the proof proves local infeasibility */
7760 if( SCIPsetIsFeasLE(set, minact, rhs) )
7761 return SCIP_OKAY;
7762
7763 /* if the farkas-proof is empty, the node and its sub tree can be cut off completely */
7764 if( nnz == 0 )
7765 {
7766 SCIPsetDebugMsg(set, " -> empty farkas-proof in depth %d cuts off sub tree at depth %d\n", SCIPtreeGetFocusDepth(tree), validdepth);
7767
7768 SCIP_CALL( SCIPnodeCutoff(tree->path[validdepth], set, stat, tree, transprob, origprob, reopt, lp, blkmem) );
7769
7770 *globalinfeasible = TRUE;
7771 *success = TRUE;
7772
7773 ++conflict->ndualproofsinfsuccess;
7774
7775 return SCIP_OKAY;
7776 }
7777
7778 /* try to enforce the constraint based on a dual ray */
7779 SCIP_CALL( tightenDualproof(conflict, set, stat, blkmem, transprob, tree, proofrow, validdepth,
7780 curvarlbs, curvarubs, initialproof) );
7781
7782 if( *globalinfeasible )
7783 {
7784 SCIPsetDebugMsg(set, "detect global: cutoff root node\n");
7785 SCIP_CALL( SCIPnodeCutoff(tree->path[0], set, stat, tree, transprob, origprob, reopt, lp, blkmem) );
7786 *success = TRUE;
7787
7788 ++conflict->ndualproofsinfsuccess;
7789 }
7790
7791 return SCIP_OKAY;
7792 }
7793
7794 /** try to find a subset of changed bounds leading to an infeasible LP
7795 *
7796 * 1. call undoBdchgsDualfarkas() or undoBdchgsDualsol()
7797 * -> update lb/ubchginfoposs arrays
7798 * -> store additional changes in bdchg and curvarlbs/ubs arrays
7799 * -> apply additional changes to the LPI
7800 * 2. (optional) if additional bound changes were undone:
7801 * -> resolve LP
7802 * -> goto 1.
7803 * 3. redo all bound changes in the LPI to restore the LPI to its original state
7804 * 4. analyze conflict
7805 * -> put remaining changed bounds (see lb/ubchginfoposs arrays) into starting conflict set
7806 */
7807 static
runBoundHeuristic(SCIP_CONFLICT * conflict,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * origprob,SCIP_PROB * transprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_LPI * lpi,BMS_BLKMEM * blkmem,SCIP_Real * proofcoefs,SCIP_Real * prooflhs,SCIP_Real * proofactivity,SCIP_Real * curvarlbs,SCIP_Real * curvarubs,int * lbchginfoposs,int * ubchginfoposs,int * iterations,SCIP_Bool marklpunsolved,SCIP_Bool * dualproofsuccess,SCIP_Bool * valid)7808 SCIP_RETCODE runBoundHeuristic(
7809 SCIP_CONFLICT* conflict, /**< conflict data */
7810 SCIP_SET* set, /**< global SCIP settings */
7811 SCIP_STAT* stat, /**< problem statistics */
7812 SCIP_PROB* origprob, /**< original problem */
7813 SCIP_PROB* transprob, /**< transformed problem */
7814 SCIP_TREE* tree, /**< branch and bound tree */
7815 SCIP_REOPT* reopt, /**< reoptimization data */
7816 SCIP_LP* lp, /**< LP data */
7817 SCIP_LPI* lpi, /**< LPI data */
7818 BMS_BLKMEM* blkmem, /**< block memory */
7819 SCIP_Real* proofcoefs, /**< coefficients in the proof constraint */
7820 SCIP_Real* prooflhs, /**< lhs of the proof constraint */
7821 SCIP_Real* proofactivity, /**< maximal activity of the proof constraint */
7822 SCIP_Real* curvarlbs, /**< current lower bounds of active problem variables */
7823 SCIP_Real* curvarubs, /**< current upper bounds of active problem variables */
7824 int* lbchginfoposs, /**< positions of currently active lower bound change information in variables' arrays */
7825 int* ubchginfoposs, /**< positions of currently active upper bound change information in variables' arrays */
7826 int* iterations, /**< pointer to store the total number of LP iterations used */
7827 SCIP_Bool marklpunsolved, /**< whether LP should be marked unsolved after analysis (needed for strong branching) */
7828 SCIP_Bool* dualproofsuccess, /**< pointer to store success result of dual proof analysis */
7829 SCIP_Bool* valid /**< pointer to store whether the result is still a valid proof */
7830 )
7831 {
7832 SCIP_LPBDCHGS* oldlpbdchgs;
7833 SCIP_LPBDCHGS* relaxedlpbdchgs;
7834 SCIP_Bool solvelp;
7835 SCIP_Bool resolve;
7836 int ncols;
7837
7838 assert(set != NULL);
7839
7840 /* get number of columns in the LP */
7841 ncols = SCIPlpGetNCols(lp);
7842
7843 /* get temporary memory for remembering bound changes on LPI columns */
7844 SCIP_CALL( lpbdchgsCreate(&oldlpbdchgs, set, ncols) );
7845 SCIP_CALL( lpbdchgsCreate(&relaxedlpbdchgs, set, ncols) );
7846
7847 /* undo as many bound changes as possible with the current LP solution */
7848 resolve = FALSE;
7849 if( (*valid) )
7850 {
7851 int currentdepth;
7852 currentdepth = SCIPtreeGetCurrentDepth(tree);
7853
7854 if( SCIPlpiIsPrimalInfeasible(lpi) )
7855 {
7856 SCIP_CALL( undoBdchgsDualfarkas(set, transprob, lp, currentdepth, curvarlbs, curvarubs, lbchginfoposs, \
7857 ubchginfoposs, oldlpbdchgs, relaxedlpbdchgs, valid, &resolve, proofcoefs, *prooflhs, proofactivity) );
7858 }
7859 else
7860 {
7861 assert(SCIPlpiIsDualFeasible(lpi) || SCIPlpiIsObjlimExc(lpi));
7862 SCIP_CALL( undoBdchgsDualsol(set, transprob, lp, currentdepth, curvarlbs, curvarubs, lbchginfoposs, ubchginfoposs, \
7863 oldlpbdchgs, relaxedlpbdchgs, valid, &resolve, proofcoefs, *prooflhs, proofactivity) );
7864 }
7865 }
7866
7867 /* check if we want to solve the LP */
7868 assert(SCIPprobAllColsInLP(transprob, set, lp));
7869 solvelp = (set->conf_maxlploops != 0 && set->conf_lpiterations != 0);
7870
7871 if( (*valid) && resolve && solvelp )
7872 {
7873 SCIP_RETCODE retcode;
7874 SCIP_ROW** rows;
7875 int* sidechginds;
7876 SCIP_Real* sidechgoldlhss;
7877 SCIP_Real* sidechgoldrhss;
7878 SCIP_Real* sidechgnewlhss;
7879 SCIP_Real* sidechgnewrhss;
7880 SCIP_Real lpiinfinity;
7881 SCIP_Bool globalinfeasible;
7882 int maxlploops;
7883 int lpiterations;
7884 int sidechgssize;
7885 int nsidechgs;
7886 int nrows;
7887 int nloops;
7888 int r;
7889
7890 /* get infinity value of LP solver */
7891 lpiinfinity = SCIPlpiInfinity(lpi);
7892
7893 /* temporarily disable objective limit and install an iteration limit */
7894 maxlploops = (set->conf_maxlploops >= 0 ? set->conf_maxlploops : INT_MAX);
7895 lpiterations = (set->conf_lpiterations >= 0 ? set->conf_lpiterations : INT_MAX);
7896 SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_OBJLIM, lpiinfinity) );
7897 SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPITLIM, lpiterations) );
7898
7899 /* get LP rows */
7900 rows = SCIPlpGetRows(lp);
7901 nrows = SCIPlpGetNRows(lp);
7902 assert(nrows == 0 || rows != NULL);
7903
7904 /* get temporary memory for remembering side changes on LPI rows */
7905 SCIP_CALL( SCIPsetAllocBufferArray(set, &sidechginds, nrows) );
7906 SCIP_CALL( SCIPsetAllocBufferArray(set, &sidechgoldlhss, nrows) );
7907 SCIP_CALL( SCIPsetAllocBufferArray(set, &sidechgoldrhss, nrows) );
7908 SCIP_CALL( SCIPsetAllocBufferArray(set, &sidechgnewlhss, nrows) );
7909 SCIP_CALL( SCIPsetAllocBufferArray(set, &sidechgnewrhss, nrows) );
7910 sidechgssize = nrows;
7911 nsidechgs = 0;
7912
7913 /* remove all local rows by setting their sides to infinity;
7914 * finite sides are only changed to near infinity, such that the row's sense in the LP solver
7915 * is not affected (e.g. CPLEX cannot handle free rows)
7916 */
7917 for( r = 0; r < nrows; ++r )
7918 {
7919 assert(SCIProwGetLPPos(rows[r]) == r);
7920
7921 if( SCIProwIsLocal(rows[r]) )
7922 {
7923 SCIPsetDebugMsg(set, " -> removing local row <%s> [%g,%g]\n",
7924 SCIProwGetName(rows[r]), SCIProwGetLhs(rows[r]), SCIProwGetRhs(rows[r]));
7925 SCIP_CALL( addSideRemoval(set, rows[r], lpiinfinity, &sidechginds, &sidechgoldlhss, &sidechgoldrhss,
7926 &sidechgnewlhss, &sidechgnewrhss, &sidechgssize, &nsidechgs) );
7927 }
7928 }
7929
7930 /* apply changes of local rows to the LP solver */
7931 if( nsidechgs > 0 )
7932 {
7933 SCIP_CALL( SCIPlpiChgSides(lpi, nsidechgs, sidechginds, sidechgnewlhss, sidechgnewrhss) );
7934 }
7935
7936 /* undo as many additional bound changes as possible by resolving the LP */
7937 assert((*valid));
7938 assert(resolve);
7939 nloops = 0;
7940 globalinfeasible = FALSE;
7941 while( (*valid) && resolve && nloops < maxlploops )
7942 {
7943 int iter;
7944
7945 assert(!globalinfeasible);
7946
7947 nloops++;
7948 resolve = FALSE;
7949
7950 SCIPsetDebugMsg(set, "infeasible LP conflict analysis loop %d (changed col bounds: %d)\n", nloops, relaxedlpbdchgs->nbdchgs);
7951
7952 /* apply bound changes to the LP solver */
7953 assert(relaxedlpbdchgs->nbdchgs >= 0);
7954 if( relaxedlpbdchgs->nbdchgs > 0 )
7955 {
7956 SCIPsetDebugMsg(set, " -> applying %d bound changes to the LP solver\n", relaxedlpbdchgs->nbdchgs);
7957 SCIP_CALL( SCIPlpiChgBounds(lpi, relaxedlpbdchgs->nbdchgs, relaxedlpbdchgs->bdchginds, \
7958 relaxedlpbdchgs->bdchglbs, relaxedlpbdchgs->bdchgubs) );
7959
7960 /* reset conflict LP bound change data structure */
7961 lpbdchgsReset(relaxedlpbdchgs, ncols);
7962 }
7963
7964 /* start LP timer */
7965 SCIPclockStart(stat->conflictlptime, set);
7966
7967 /* resolve LP */
7968 retcode = SCIPlpiSolveDual(lpi);
7969
7970 /* stop LP timer */
7971 SCIPclockStop(stat->conflictlptime, set);
7972
7973 /* check return code of LP solving call */
7974 if( retcode == SCIP_LPERROR )
7975 {
7976 (*valid) = FALSE;
7977 break;
7978 }
7979 SCIP_CALL( retcode );
7980
7981 /* count number of LP iterations */
7982 SCIP_CALL( SCIPlpiGetIterations(lpi, &iter) );
7983 (*iterations) += iter;
7984 stat->nconflictlps++;
7985 stat->nconflictlpiterations += iter;
7986 SCIPsetDebugMsg(set, " -> resolved LP in %d iterations (total: %" SCIP_LONGINT_FORMAT ") (infeasible:%u)\n",
7987 iter, stat->nconflictlpiterations, SCIPlpiIsPrimalInfeasible(lpi));
7988
7989 /* evaluate result */
7990 if( SCIPlpiIsDualFeasible(lpi) || SCIPlpiIsObjlimExc(lpi) )
7991 {
7992 SCIP_Real objval;
7993
7994 SCIP_CALL( SCIPlpiGetObjval(lpi, &objval) );
7995 (*valid) = (objval >= lp->lpiobjlim && !SCIPlpDivingObjChanged(lp));
7996 }
7997 else
7998 (*valid) = SCIPlpiIsPrimalInfeasible(lpi);
7999
8000 if( (*valid) )
8001 {
8002 int currentdepth;
8003 currentdepth = SCIPtreeGetCurrentDepth(tree);
8004
8005 /* undo additional bound changes */
8006 if( SCIPlpiIsPrimalInfeasible(lpi) )
8007 {
8008 SCIP_AGGRROW* farkasrow;
8009 int* inds;
8010 int validdepth;
8011 int nnz;
8012 int v;
8013
8014 #ifndef NDEBUG
8015 SCIP_VAR** vars = SCIPprobGetVars(transprob);
8016 #endif
8017
8018 SCIP_CALL( SCIPaggrRowCreate(set->scip, &farkasrow) );
8019
8020 /* the original LP exceeds the current cutoff bound, thus, we have not constructed the Farkas proof */
8021 SCIP_CALL( getFarkasProof(set, transprob, lp, lpi, tree, farkasrow, proofactivity, &validdepth,
8022 curvarlbs, curvarubs, valid) );
8023
8024 /* the constructed Farkas proof is not valid, we need to break here */
8025 if( !(*valid) )
8026 {
8027 SCIPaggrRowFree(set->scip, &farkasrow);
8028 break;
8029 }
8030
8031 /* start dual proof analysis */
8032 if( set->conf_useinflp == 'd' || set->conf_useinflp == 'b' )
8033 {
8034 /* change the conflict type */
8035 SCIP_CONFTYPE oldconftype = conflict->conflictset->conflicttype;
8036 conflict->conflictset->conflicttype = SCIP_CONFTYPE_INFEASLP;
8037
8038 /* start dual proof analysis */
8039 SCIP_CALL( conflictAnalyzeDualProof(conflict, set, stat, blkmem, origprob, transprob, tree, reopt, lp, \
8040 farkasrow, validdepth, curvarlbs, curvarubs, FALSE, &globalinfeasible, dualproofsuccess) );
8041
8042 conflict->conflictset->conflicttype = oldconftype;
8043 }
8044
8045 /* todo: in theory, we could apply conflict graph analysis for locally valid proofs, too, but this needs to be implemented */
8046 if( globalinfeasible || validdepth > SCIPtreeGetEffectiveRootDepth(tree) )
8047 {
8048 SCIPaggrRowFree(set->scip, &farkasrow);
8049 goto TERMINATE;
8050 }
8051
8052 BMSclearMemoryArray(proofcoefs, SCIPprobGetNVars(transprob));
8053 (*prooflhs) = -SCIPaggrRowGetRhs(farkasrow);
8054 (*proofactivity) = -(*proofactivity);
8055
8056 inds = SCIPaggrRowGetInds(farkasrow);
8057 nnz = SCIPaggrRowGetNNz(farkasrow);
8058
8059 for( v = 0; v < nnz; v++ )
8060 {
8061 int i = inds[v];
8062
8063 assert(SCIPvarGetProbindex(vars[i]) == inds[v]);
8064
8065 proofcoefs[i] = -SCIPaggrRowGetProbvarValue(farkasrow, i);
8066 }
8067
8068 /* free aggregation rows */
8069 SCIPaggrRowFree(set->scip, &farkasrow);
8070
8071 SCIP_CALL( undoBdchgsDualfarkas(set, transprob, lp, currentdepth, curvarlbs, curvarubs, lbchginfoposs, \
8072 ubchginfoposs, oldlpbdchgs, relaxedlpbdchgs, valid, &resolve, proofcoefs, (*prooflhs), proofactivity) );
8073 }
8074 else
8075 {
8076 SCIP_AGGRROW* proofrow;
8077 int* inds;
8078 int validdepth;
8079 int nnz;
8080 int v;
8081
8082 #ifndef NDEBUG
8083 SCIP_VAR** vars = SCIPprobGetVars(transprob);
8084 #endif
8085
8086 assert(SCIPlpiIsDualFeasible(lpi) || SCIPlpiIsObjlimExc(lpi));
8087
8088 SCIP_CALL( SCIPaggrRowCreate(set->scip, &proofrow) );
8089
8090 SCIP_CALL( getDualProof(set, transprob, lp, lpi, tree, proofrow, proofactivity, &validdepth,
8091 curvarlbs, curvarubs, valid) );
8092
8093 /* the constructed dual proof is not valid, we need to break here */
8094 if( !(*valid) || validdepth > SCIPtreeGetEffectiveRootDepth(tree) )
8095 {
8096 SCIPaggrRowFree(set->scip, &proofrow);
8097 break;
8098 }
8099 /* in contrast to the infeasible case we don't want to analyze the (probably identical) proof again. */
8100
8101 BMSclearMemoryArray(proofcoefs, SCIPprobGetNVars(transprob));
8102 (*prooflhs) = -SCIPaggrRowGetRhs(proofrow);
8103 (*proofactivity) = -(*proofactivity);
8104
8105 inds = SCIPaggrRowGetInds(proofrow);
8106 nnz = SCIPaggrRowGetNNz(proofrow);
8107
8108 for( v = 0; v < nnz; v++ )
8109 {
8110 int i = inds[v];
8111
8112 assert(SCIPvarGetProbindex(vars[i]) == inds[v]);
8113
8114 proofcoefs[i] = -SCIPaggrRowGetProbvarValue(proofrow, i);
8115 }
8116
8117 /* free aggregation rows */
8118 SCIPaggrRowFree(set->scip, &proofrow);
8119
8120 SCIP_CALL( undoBdchgsDualsol(set, transprob, lp, currentdepth, curvarlbs, curvarubs, lbchginfoposs, \
8121 ubchginfoposs, oldlpbdchgs, relaxedlpbdchgs, valid, &resolve, proofcoefs, *prooflhs, proofactivity) );
8122 }
8123 }
8124 assert(!resolve || (*valid));
8125 assert(!resolve || relaxedlpbdchgs->nbdchgs > 0);
8126 SCIPsetDebugMsg(set, " -> finished infeasible LP conflict analysis loop %d (iter: %d, nbdchgs: %d)\n",
8127 nloops, iter, relaxedlpbdchgs->nbdchgs);
8128 }
8129
8130 SCIPsetDebugMsg(set, "finished undoing bound changes after %d loops (valid=%u, nbdchgs: %d)\n",
8131 nloops, (*valid), oldlpbdchgs->nbdchgs);
8132
8133 TERMINATE:
8134 /* reset variables to local bounds */
8135 if( oldlpbdchgs->nbdchgs > 0 )
8136 {
8137 SCIP_CALL( SCIPlpiChgBounds(lpi, oldlpbdchgs->nbdchgs, oldlpbdchgs->bdchginds, oldlpbdchgs->bdchglbs, oldlpbdchgs->bdchgubs) );
8138 }
8139
8140 /* reset changes of local rows */
8141 if( nsidechgs > 0 )
8142 {
8143 SCIP_CALL( SCIPlpiChgSides(lpi, nsidechgs, sidechginds, sidechgoldlhss, sidechgoldrhss) );
8144 }
8145
8146 /* mark the LP unsolved */
8147 if( oldlpbdchgs->nbdchgs > 0 || nsidechgs > 0 )
8148 {
8149 /* The LPI data are out of sync with LP data. Thus, the LP should be marked
8150 * unsolved. However, for strong branching calls, the LP has to have status 'solved'; in
8151 * this case, marklpunsolved is FALSE and synchronization is performed later. */
8152 if ( marklpunsolved )
8153 {
8154 lp->solved = FALSE;
8155 lp->primalfeasible = FALSE;
8156 lp->primalchecked = FALSE;
8157 lp->dualfeasible = FALSE;
8158 lp->dualchecked = FALSE;
8159 lp->lpobjval = SCIP_INVALID;
8160 lp->lpsolstat = SCIP_LPSOLSTAT_NOTSOLVED;
8161 }
8162 }
8163
8164 /* reinstall old objective and iteration limits in LP solver */
8165 SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_OBJLIM, lp->lpiobjlim) );
8166 SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPITLIM, lp->lpiitlim) );
8167
8168 /* free temporary memory */
8169 SCIPsetFreeBufferArray(set, &sidechgnewrhss);
8170 SCIPsetFreeBufferArray(set, &sidechgnewlhss);
8171 SCIPsetFreeBufferArray(set, &sidechgoldrhss);
8172 SCIPsetFreeBufferArray(set, &sidechgoldlhss);
8173 SCIPsetFreeBufferArray(set, &sidechginds);
8174 }
8175
8176 /* free temporary memory */
8177 lpbdchgsFree(&relaxedlpbdchgs, set);
8178 lpbdchgsFree(&oldlpbdchgs, set);
8179
8180 return SCIP_OKAY;
8181 }
8182
8183 /** actually performs analysis of infeasible LP */
8184 static
conflictAnalyzeLP(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool diving,SCIP_Bool * dualproofsuccess,int * iterations,int * nconss,int * nliterals,int * nreconvconss,int * nreconvliterals,SCIP_Bool marklpunsolved)8185 SCIP_RETCODE conflictAnalyzeLP(
8186 SCIP_CONFLICT* conflict, /**< conflict analysis data */
8187 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
8188 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
8189 SCIP_SET* set, /**< global SCIP settings */
8190 SCIP_STAT* stat, /**< problem statistics */
8191 SCIP_PROB* transprob, /**< transformed problem */
8192 SCIP_PROB* origprob, /**< original problem */
8193 SCIP_TREE* tree, /**< branch and bound tree */
8194 SCIP_REOPT* reopt, /**< reoptimization data structure */
8195 SCIP_LP* lp, /**< LP data */
8196 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
8197 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
8198 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
8199 SCIP_Bool diving, /**< are we in strong branching or diving mode? */
8200 SCIP_Bool* dualproofsuccess, /**< pointer to store success result of dual proof analysis */
8201 int* iterations, /**< pointer to store the total number of LP iterations used */
8202 int* nconss, /**< pointer to store the number of generated conflict constraints */
8203 int* nliterals, /**< pointer to store the number of literals in generated conflict constraints */
8204 int* nreconvconss, /**< pointer to store the number of generated reconvergence constraints */
8205 int* nreconvliterals, /**< pointer to store the number of literals generated reconvergence constraints */
8206 SCIP_Bool marklpunsolved /**< whether LP should be marked unsolved after analysis (needed for strong branching) */
8207 )
8208 {
8209 SCIP_VAR** vars;
8210 SCIP_AGGRROW* farkasrow;
8211 SCIP_LPI* lpi;
8212 SCIP_Bool valid;
8213 SCIP_Bool globalinfeasible;
8214 int* lbchginfoposs;
8215 int* ubchginfoposs;
8216 int validdepth;
8217 int nvars;
8218 int v;
8219 SCIP_Real* curvarlbs;
8220 SCIP_Real* curvarubs;
8221 SCIP_Real farkasactivity;
8222
8223 assert(conflict != NULL);
8224 assert(conflict->nconflictsets == 0);
8225 assert(set != NULL);
8226 assert(SCIPprobAllColsInLP(transprob, set, lp)); /* LP conflict analysis is only valid, if all variables are known */
8227 assert(stat != NULL);
8228 assert(transprob != NULL);
8229 assert(lp != NULL);
8230 assert(lp->flushed);
8231 assert(lp->solved);
8232 assert(iterations != NULL);
8233 assert(nconss != NULL);
8234 assert(nliterals != NULL);
8235 assert(nreconvconss != NULL);
8236 assert(nreconvliterals != NULL);
8237
8238 *iterations = 0;
8239 *nconss = 0;
8240 *nliterals = 0;
8241 *nreconvconss = 0;
8242 *nreconvliterals = 0;
8243
8244 vars = transprob->vars;
8245 nvars = transprob->nvars;
8246
8247 valid = TRUE;
8248 validdepth = 0;
8249
8250 /* get LP solver interface */
8251 lpi = SCIPlpGetLPI(lp);
8252 assert(SCIPlpiIsPrimalInfeasible(lpi) || SCIPlpiIsObjlimExc(lpi) || SCIPlpiIsDualFeasible(lpi));
8253 assert(SCIPlpiIsPrimalInfeasible(lpi) || !SCIPlpDivingObjChanged(lp));
8254
8255 if( !SCIPlpiIsPrimalInfeasible(lpi) )
8256 {
8257 SCIP_Real objval;
8258
8259 assert(!SCIPlpDivingObjChanged(lp));
8260
8261 /* make sure, a dual feasible solution exists, that exceeds the objective limit;
8262 * With FASTMIP setting, CPLEX does not apply the final pivot to reach the dual solution exceeding the objective
8263 * limit. Therefore, we have to either turn off FASTMIP and resolve the problem or continue solving it without
8264 * objective limit for at least one iteration. It seems that the strategy to continue with FASTMIP for one
8265 * additional simplex iteration yields better results.
8266 */
8267 SCIP_CALL( SCIPlpiGetObjval(lpi, &objval) );
8268 if( objval < lp->lpiobjlim )
8269 {
8270 SCIP_RETCODE retcode;
8271
8272 /* temporarily disable objective limit and install an iteration limit */
8273 SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_OBJLIM, SCIPlpiInfinity(lpi)) );
8274 SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPITLIM, 1) );
8275
8276 /* start LP timer */
8277 SCIPclockStart(stat->conflictlptime, set);
8278
8279 /* resolve LP */
8280 retcode = SCIPlpiSolveDual(lpi);
8281
8282 /* stop LP timer */
8283 SCIPclockStop(stat->conflictlptime, set);
8284
8285 /* check return code of LP solving call */
8286 valid = (retcode != SCIP_LPERROR);
8287 if( valid )
8288 {
8289 int iter;
8290
8291 SCIP_CALL( retcode );
8292
8293 /* count number of LP iterations */
8294 SCIP_CALL( SCIPlpiGetIterations(lpi, &iter) );
8295 (*iterations) += iter;
8296 stat->nconflictlps++;
8297 stat->nconflictlpiterations += iter;
8298 SCIPsetDebugMsg(set, " -> resolved objlim exceeding LP in %d iterations (total: %" SCIP_LONGINT_FORMAT ") (infeasible:%u, objlim: %u, optimal:%u)\n",
8299 iter, stat->nconflictlpiterations, SCIPlpiIsPrimalInfeasible(lpi), SCIPlpiIsObjlimExc(lpi), SCIPlpiIsOptimal(lpi));
8300 valid = (SCIPlpiIsObjlimExc(lpi) || SCIPlpiIsPrimalInfeasible(lpi) || SCIPlpiIsDualFeasible(lpi));
8301 }
8302
8303 /* reinstall old objective and iteration limits in LP solver */
8304 SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_OBJLIM, lp->lpiobjlim) );
8305 SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPITLIM, lp->lpiitlim) );
8306
8307 /* abort, if the LP produced an error */
8308 if( !valid )
8309 return SCIP_OKAY;
8310 }
8311 }
8312 assert(SCIPlpiIsPrimalInfeasible(lpi) || SCIPlpiIsObjlimExc(lpi) || SCIPlpiIsDualFeasible(lpi));
8313
8314 if( !SCIPlpiIsPrimalInfeasible(lpi) )
8315 {
8316 SCIP_Real objval;
8317
8318 assert(!SCIPlpDivingObjChanged(lp));
8319
8320 SCIP_CALL( SCIPlpiGetObjval(lpi, &objval) );
8321 if( objval < lp->lpiobjlim )
8322 {
8323 SCIPsetDebugMsg(set, " -> LP does not exceed the cutoff bound: obj=%g, cutoff=%g\n", objval, lp->lpiobjlim);
8324 return SCIP_OKAY;
8325 }
8326 else
8327 {
8328 SCIPsetDebugMsg(set, " -> LP exceeds the cutoff bound: obj=%g, cutoff=%g\n", objval, lp->lpiobjlim);
8329 }
8330 }
8331
8332 assert(valid);
8333
8334 SCIP_CALL( SCIPaggrRowCreate(set->scip, &farkasrow) );
8335 SCIP_CALL( SCIPsetAllocBufferArray(set, &lbchginfoposs, transprob->nvars) );
8336 SCIP_CALL( SCIPsetAllocBufferArray(set, &ubchginfoposs, transprob->nvars) );
8337
8338 farkasactivity = 0.0;
8339
8340 /* get temporary memory for remembering variables' current bounds and corresponding bound change information
8341 * positions in variable's bound change information arrays
8342 */
8343 SCIP_CALL( SCIPsetAllocBufferArray(set, &curvarlbs, nvars) );
8344 SCIP_CALL( SCIPsetAllocBufferArray(set, &curvarubs, nvars) );
8345
8346 /* get current bounds and current positions in lb/ubchginfos arrays of variables */
8347 valid = TRUE;
8348 for( v = 0; v < nvars && valid; ++v )
8349 {
8350 SCIP_VAR* var;
8351
8352 var = vars[v];
8353
8354 curvarlbs[v] = SCIPvarGetLbLP(var, set);
8355 curvarubs[v] = SCIPvarGetUbLP(var, set);
8356 lbchginfoposs[v] = var->nlbchginfos-1;
8357 ubchginfoposs[v] = var->nubchginfos-1;
8358 assert(diving || SCIPsetIsEQ(set, curvarlbs[v], SCIPvarGetLbLocal(var)));
8359 assert(diving || SCIPsetIsEQ(set, curvarubs[v], SCIPvarGetUbLocal(var)));
8360
8361 /* check, if last bound changes were due to strong branching or diving */
8362 if( diving )
8363 {
8364 SCIP_Real lb;
8365 SCIP_Real ub;
8366
8367 lb = SCIPvarGetLbLocal(var);
8368 ub = SCIPvarGetUbLocal(var);
8369 if( SCIPsetIsGT(set, curvarlbs[v], lb) )
8370 lbchginfoposs[v] = var->nlbchginfos;
8371 else if( SCIPsetIsLT(set, curvarlbs[v], lb) )
8372 {
8373 /* the bound in the diving LP was relaxed -> the LP is not a subproblem of the current node -> abort! */
8374 /**@todo we could still analyze such a conflict, but we would have to take care with our data structures */
8375 valid = FALSE;
8376 }
8377 if( SCIPsetIsLT(set, curvarubs[v], ub) )
8378 ubchginfoposs[v] = var->nubchginfos;
8379 else if( SCIPsetIsGT(set, curvarubs[v], ub) )
8380 {
8381 /* the bound in the diving LP was relaxed -> the LP is not a subproblem of the current node -> abort! */
8382 /**@todo we could still analyze such a conflict, but we would have to take care with our data structures */
8383 valid = FALSE;
8384 }
8385 }
8386 }
8387
8388 if( !valid )
8389 goto TERMINATE;
8390
8391 /* the LP is prooven to be infeasible */
8392 if( SCIPlpiIsPrimalInfeasible(lpi) )
8393 {
8394 SCIP_CALL( getFarkasProof(set, transprob, lp, lpi, tree, farkasrow, &farkasactivity, &validdepth,
8395 curvarlbs, curvarubs, &valid) );
8396 }
8397 /* the LP is dual feasible and/or exceeds the current incumbant solution */
8398 else
8399 {
8400 assert(SCIPlpiIsDualFeasible(lpi) || SCIPlpiIsObjlimExc(lpi));
8401 SCIP_CALL( getDualProof(set, transprob, lp, lpi, tree, farkasrow, &farkasactivity, &validdepth,
8402 curvarlbs, curvarubs, &valid) );
8403 }
8404
8405 if( !valid || validdepth >= SCIPtreeGetCurrentDepth(tree) )
8406 goto TERMINATE;
8407
8408 globalinfeasible = FALSE;
8409
8410 /* start dual proof analysis */
8411 if( ((set->conf_useinflp == 'b' || set->conf_useinflp == 'd') && conflict->conflictset->conflicttype == SCIP_CONFTYPE_INFEASLP)
8412 || ((set->conf_useboundlp == 'b' || set->conf_useboundlp == 'd') && conflict->conflictset->conflicttype == SCIP_CONFTYPE_BNDEXCEEDING) )
8413 {
8414 /* start dual proof analysis */
8415 SCIP_CALL( conflictAnalyzeDualProof(conflict, set, stat, blkmem, origprob, transprob, tree, reopt, lp, farkasrow, \
8416 validdepth, curvarlbs, curvarubs, TRUE, &globalinfeasible, dualproofsuccess) );
8417 }
8418
8419 assert(valid);
8420
8421 /* todo: in theory, we could apply conflict graph analysis for locally valid proofs, too, but this needs to be implemented */
8422 if( !globalinfeasible && validdepth <= SCIPtreeGetEffectiveRootDepth(tree)
8423 && (((set->conf_useinflp == 'b' || set->conf_useinflp == 'c') && conflict->conflictset->conflicttype == SCIP_CONFTYPE_INFEASLP)
8424 || ((set->conf_useboundlp == 'b' || set->conf_useboundlp == 'c') && conflict->conflictset->conflicttype == SCIP_CONFTYPE_BNDEXCEEDING)) )
8425 {
8426 SCIP_Real* farkascoefs;
8427 SCIP_Real farkaslhs;
8428 int* inds;
8429 int nnz;
8430
8431 #ifdef SCIP_DEBUG
8432 {
8433 SCIP_Real objlim;
8434 SCIPsetDebugMsg(set, "analyzing conflict on infeasible LP (infeasible: %u, objlimexc: %u, optimal:%u) in depth %d (diving: %u)\n",
8435 SCIPlpiIsPrimalInfeasible(lpi), SCIPlpiIsObjlimExc(lpi), SCIPlpiIsOptimal(lpi), SCIPtreeGetCurrentDepth(tree), diving);
8436
8437 SCIP_CALL( SCIPlpiGetRealpar(lpi, SCIP_LPPAR_OBJLIM, &objlim) );
8438 SCIPsetDebugMsg(set, " -> objective limit in LP solver: %g (in LP: %g)\n", objlim, lp->lpiobjlim);
8439 }
8440 #endif
8441
8442 SCIP_CALL( SCIPsetAllocBufferArray(set, &farkascoefs, SCIPprobGetNVars(transprob)) );
8443 BMSclearMemoryArray(farkascoefs, SCIPprobGetNVars(transprob));
8444
8445 farkaslhs = -SCIPaggrRowGetRhs(farkasrow);
8446 farkasactivity = -farkasactivity;
8447
8448 inds = SCIPaggrRowGetInds(farkasrow);
8449 nnz = SCIPaggrRowGetNNz(farkasrow);
8450
8451 for( v = 0; v < nnz; v++ )
8452 {
8453 int i = inds[v];
8454
8455 assert(SCIPvarGetProbindex(vars[i]) == inds[v]);
8456
8457 farkascoefs[i] = -SCIPaggrRowGetProbvarValue(farkasrow, i);
8458 }
8459
8460 SCIP_CALL( runBoundHeuristic(conflict, set, stat, origprob, transprob, tree, reopt, lp, lpi, blkmem, farkascoefs,
8461 &farkaslhs, &farkasactivity, curvarlbs, curvarubs, lbchginfoposs, ubchginfoposs, iterations, marklpunsolved,
8462 dualproofsuccess, &valid) );
8463
8464 SCIPsetFreeBufferArray(set, &farkascoefs);
8465
8466 if( !valid )
8467 goto FLUSHPROOFSETS;
8468
8469 /* analyze the conflict starting with remaining bound changes */
8470 SCIP_CALL( conflictAnalyzeRemainingBdchgs(conflict, blkmem, set, stat, transprob, tree, diving, \
8471 lbchginfoposs, ubchginfoposs, nconss, nliterals, nreconvconss, nreconvliterals) );
8472
8473 /* flush conflict set storage */
8474 SCIP_CALL( SCIPconflictFlushConss(conflict, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, \
8475 eventqueue, cliquetable) );
8476 }
8477
8478 FLUSHPROOFSETS:
8479 /* flush proof set */
8480 if( proofsetGetNVars(conflict->proofset) > 0 || conflict->nproofsets > 0 )
8481 {
8482 SCIP_CALL( conflictFlushProofset(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, reopt, lp, \
8483 branchcand, eventqueue, cliquetable) );
8484 }
8485
8486 TERMINATE:
8487 SCIPsetFreeBufferArray(set, &curvarubs);
8488 SCIPsetFreeBufferArray(set, &curvarlbs);
8489 SCIPsetFreeBufferArray(set, &ubchginfoposs);
8490 SCIPsetFreeBufferArray(set, &lbchginfoposs);
8491 SCIPaggrRowFree(set->scip, &farkasrow);
8492
8493 return SCIP_OKAY;
8494 }
8495
8496 /** analyzes an infeasible LP to find out the bound changes on variables that were responsible for the infeasibility;
8497 * on success, calls standard conflict analysis with the responsible variables as starting conflict set, thus creating
8498 * a conflict constraint out of the resulting conflict set;
8499 * updates statistics for infeasible LP conflict analysis
8500 */
8501 static
conflictAnalyzeInfeasibleLP(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * success)8502 SCIP_RETCODE conflictAnalyzeInfeasibleLP(
8503 SCIP_CONFLICT* conflict, /**< conflict analysis data */
8504 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
8505 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
8506 SCIP_SET* set, /**< global SCIP settings */
8507 SCIP_STAT* stat, /**< problem statistics */
8508 SCIP_PROB* transprob, /**< transformed problem */
8509 SCIP_PROB* origprob, /**< original problem */
8510 SCIP_TREE* tree, /**< branch and bound tree */
8511 SCIP_REOPT* reopt, /**< reoptimization data structure */
8512 SCIP_LP* lp, /**< LP data */
8513 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
8514 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
8515 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
8516 SCIP_Bool* success /**< pointer to store whether a conflict constraint was created, or NULL */
8517 )
8518 {
8519 SCIP_Bool dualraysuccess = FALSE;
8520 SCIP_Longint olddualproofsuccess;
8521 int iterations;
8522 int nconss;
8523 int nliterals;
8524 int nreconvconss;
8525 int nreconvliterals;
8526
8527 assert(conflict != NULL);
8528 assert(set != NULL);
8529 assert(lp != NULL);
8530 assert(SCIPprobAllColsInLP(transprob, set, lp)); /* LP conflict analysis is only valid, if all variables are known */
8531
8532 assert(success == NULL || *success == FALSE);
8533
8534 /* check, if infeasible LP conflict analysis is enabled */
8535 if( !set->conf_enable || set->conf_useinflp == 'o' )
8536 return SCIP_OKAY;
8537
8538 /* check, if there are any conflict handlers to use a conflict set */
8539 if( set->nconflicthdlrs == 0 )
8540 return SCIP_OKAY;
8541
8542 SCIPsetDebugMsg(set, "analyzing conflict on infeasible LP in depth %d (solstat: %d, objchanged: %u)\n",
8543 SCIPtreeGetCurrentDepth(tree), SCIPlpGetSolstat(lp), SCIPlpDivingObjChanged(lp));
8544
8545 /* start timing */
8546 SCIPclockStart(conflict->inflpanalyzetime, set);
8547 conflict->ninflpcalls++;
8548
8549 conflict->conflictset->conflicttype = SCIP_CONFTYPE_INFEASLP;
8550
8551 olddualproofsuccess = conflict->ndualproofsinfsuccess;
8552
8553 /* perform conflict analysis */
8554 SCIP_CALL( conflictAnalyzeLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue, \
8555 cliquetable, SCIPlpDiving(lp), &dualraysuccess, &iterations, &nconss, &nliterals, &nreconvconss, &nreconvliterals, TRUE) );
8556 conflict->ninflpsuccess += ((nconss > 0 || conflict->ndualproofsinfsuccess > olddualproofsuccess) ? 1 : 0);
8557 conflict->ninflpiterations += iterations;
8558 conflict->ninflpconfconss += nconss;
8559 conflict->ninflpconfliterals += nliterals;
8560 conflict->ninflpreconvconss += nreconvconss;
8561 conflict->ninflpreconvliterals += nreconvliterals;
8562 if( success != NULL )
8563 *success = (nconss > 0 || conflict->ndualproofsinfsuccess > olddualproofsuccess);
8564
8565 /* stop timing */
8566 SCIPclockStop(conflict->inflpanalyzetime, set);
8567
8568 return SCIP_OKAY;
8569 }
8570
8571 /** analyzes a bound exceeding LP to find out the bound changes on variables that were responsible for exceeding the
8572 * primal bound;
8573 * on success, calls standard conflict analysis with the responsible variables as starting conflict set, thus creating
8574 * a conflict constraint out of the resulting conflict set;
8575 * updates statistics for bound exceeding LP conflict analysis
8576 */
8577 static
conflictAnalyzeBoundexceedingLP(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * success)8578 SCIP_RETCODE conflictAnalyzeBoundexceedingLP(
8579 SCIP_CONFLICT* conflict, /**< conflict analysis data */
8580 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
8581 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
8582 SCIP_SET* set, /**< global SCIP settings */
8583 SCIP_STAT* stat, /**< problem statistics */
8584 SCIP_PROB* transprob, /**< transformed problem */
8585 SCIP_PROB* origprob, /**< original problem */
8586 SCIP_TREE* tree, /**< branch and bound tree */
8587 SCIP_REOPT* reopt, /**< reoptimization data structure */
8588 SCIP_LP* lp, /**< LP data */
8589 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
8590 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
8591 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
8592 SCIP_Bool* success /**< pointer to store whether a conflict constraint was created, or NULL */
8593 )
8594 {
8595 SCIP_Bool dualraysuccess;
8596 SCIP_Longint oldnsuccess;
8597 int iterations;
8598 int nconss;
8599 int nliterals;
8600 int nreconvconss;
8601 int nreconvliterals;
8602
8603 assert(conflict != NULL);
8604 assert(set != NULL);
8605 assert(lp != NULL);
8606 assert(!SCIPlpDivingObjChanged(lp));
8607 assert(SCIPprobAllColsInLP(transprob, set, lp)); /* LP conflict analysis is only valid, if all variables are known */
8608
8609 assert(success == NULL || *success == FALSE);
8610
8611 /* check, if bound exceeding LP conflict analysis is enabled */
8612 if( !set->conf_enable || set->conf_useboundlp == 'o')
8613 return SCIP_OKAY;
8614
8615 /* check, if there are any conflict handlers to use a conflict set */
8616 if( set->nconflicthdlrs == 0 )
8617 return SCIP_OKAY;
8618
8619 SCIPsetDebugMsg(set, "analyzing conflict on bound exceeding LP in depth %d (solstat: %d)\n",
8620 SCIPtreeGetCurrentDepth(tree), SCIPlpGetSolstat(lp));
8621
8622 /* start timing */
8623 SCIPclockStart(conflict->boundlpanalyzetime, set);
8624 conflict->nboundlpcalls++;
8625
8626 /* mark the conflict to depend on the cutoff bound */
8627 conflict->conflictset->conflicttype = SCIP_CONFTYPE_BNDEXCEEDING;
8628 conflict->conflictset->usescutoffbound = TRUE;
8629
8630 oldnsuccess = conflict->ndualproofsbndsuccess + conflict->ndualproofsinfsuccess;
8631
8632 /* perform conflict analysis */
8633 SCIP_CALL( conflictAnalyzeLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue, \
8634 cliquetable, SCIPlpDiving(lp), &dualraysuccess, &iterations, &nconss, &nliterals, &nreconvconss, &nreconvliterals, TRUE) );
8635 conflict->nboundlpsuccess += ((nconss > 0 || conflict->ndualproofsbndsuccess + conflict->ndualproofsinfsuccess > oldnsuccess) ? 1 : 0);
8636 conflict->nboundlpiterations += iterations;
8637 conflict->nboundlpconfconss += nconss;
8638 conflict->nboundlpconfliterals += nliterals;
8639 conflict->nboundlpreconvconss += nreconvconss;
8640 conflict->nboundlpreconvliterals += nreconvliterals;
8641 if( success != NULL )
8642 *success = (nconss > 0 || conflict->ndualproofsbndsuccess + conflict->ndualproofsinfsuccess > oldnsuccess);
8643
8644 /* stop timing */
8645 SCIPclockStop(conflict->boundlpanalyzetime, set);
8646
8647 return SCIP_OKAY;
8648 }
8649
8650 /** analyzes an infeasible or bound exceeding LP to find out the bound changes on variables that were responsible for the
8651 * infeasibility or for exceeding the primal bound;
8652 * on success, calls standard conflict analysis with the responsible variables as starting conflict set, thus creating
8653 * a conflict constraint out of the resulting conflict set;
8654 * updates statistics for infeasible or bound exceeding LP conflict analysis;
8655 * may only be called if SCIPprobAllColsInLP()
8656 */
SCIPconflictAnalyzeLP(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * success)8657 SCIP_RETCODE SCIPconflictAnalyzeLP(
8658 SCIP_CONFLICT* conflict, /**< conflict analysis data */
8659 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
8660 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
8661 SCIP_SET* set, /**< global SCIP settings */
8662 SCIP_STAT* stat, /**< problem statistics */
8663 SCIP_PROB* transprob, /**< transformed problem */
8664 SCIP_PROB* origprob, /**< original problem */
8665 SCIP_TREE* tree, /**< branch and bound tree */
8666 SCIP_REOPT* reopt, /**< reoptimization data structure */
8667 SCIP_LP* lp, /**< LP data */
8668 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
8669 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
8670 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
8671 SCIP_Bool* success /**< pointer to store whether a conflict constraint was created, or NULL */
8672 )
8673 {
8674 SCIP_LPSOLVALS storedsolvals;
8675 SCIP_COLSOLVALS* storedcolsolvals;
8676 SCIP_ROWSOLVALS* storedrowsolvals;
8677 int c;
8678 int r;
8679
8680 if( success != NULL )
8681 *success = FALSE;
8682
8683 /* check if the conflict analysis is applicable */
8684 if( !set->conf_enable || (set->conf_useinflp == 'o' && set->conf_useboundlp == 'o') )
8685 return SCIP_OKAY;
8686
8687 /* in rare cases, it might happen that the solution stati of the LP and the LPI are out of sync; in particular this
8688 * happens when a new incumbent which cuts off the current node is found during the LP solving loop; in this case the
8689 * LP has status objlimit, but if diving has been used, the LPI only has the basis information, but is not solved
8690 *
8691 * @todo: alternatively, solve the LPI
8692 */
8693 if( !SCIPlpiWasSolved(SCIPlpGetLPI(lp)) )
8694 return SCIP_OKAY;
8695
8696 /* LP conflict analysis is only valid, if all variables are known */
8697 assert( SCIPprobAllColsInLP(transprob, set, lp) );
8698 assert( SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_INFEASIBLE || SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OBJLIMIT
8699 || (SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OPTIMAL && set->lp_disablecutoff == 1) );
8700
8701 /* save status */
8702 storedsolvals.lpsolstat = lp->lpsolstat;
8703 storedsolvals.lpobjval = lp->lpobjval;
8704 storedsolvals.primalfeasible = lp->primalfeasible;
8705 storedsolvals.primalchecked = lp->primalchecked;
8706 storedsolvals.dualfeasible = lp->dualfeasible;
8707 storedsolvals.dualchecked = lp->dualchecked;
8708 storedsolvals.solisbasic = lp->solisbasic;
8709 storedsolvals.lpissolved = lp->solved;
8710
8711 /* store solution values */
8712 SCIP_CALL( SCIPsetAllocBufferArray(set, &storedcolsolvals, lp->ncols) );
8713 SCIP_CALL( SCIPsetAllocBufferArray(set, &storedrowsolvals, lp->nrows) );
8714 for (c = 0; c < lp->ncols; ++c)
8715 {
8716 SCIP_COL* col;
8717
8718 col = lp->cols[c];
8719 assert( col != NULL );
8720
8721 storedcolsolvals[c].primsol = col->primsol;
8722 storedcolsolvals[c].redcost = col->redcost;
8723 storedcolsolvals[c].basisstatus = col->basisstatus; /*lint !e641 !e732*/
8724 }
8725 for (r = 0; r < lp->nrows; ++r)
8726 {
8727 SCIP_ROW* row;
8728
8729 row = lp->rows[r];
8730 assert( row != NULL );
8731
8732 if ( lp->lpsolstat == SCIP_LPSOLSTAT_INFEASIBLE )
8733 storedrowsolvals[r].dualsol = row->dualfarkas;
8734 else
8735 {
8736 assert( lp->lpsolstat == SCIP_LPSOLSTAT_OBJLIMIT ||
8737 (SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OPTIMAL && set->lp_disablecutoff == 1) );
8738 storedrowsolvals[r].dualsol = row->dualsol;
8739 }
8740 storedrowsolvals[r].activity = row->activity;
8741 storedrowsolvals[r].basisstatus = row->basisstatus; /*lint !e641 !e732*/
8742 }
8743
8744 /* check, if the LP was infeasible or bound exceeding */
8745 if( SCIPlpiIsPrimalInfeasible(SCIPlpGetLPI(lp)) )
8746 {
8747 SCIP_CALL( conflictAnalyzeInfeasibleLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, \
8748 reopt, lp, branchcand, eventqueue, cliquetable, success) );
8749 }
8750 else
8751 {
8752 SCIP_CALL( conflictAnalyzeBoundexceedingLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, \
8753 reopt, lp, branchcand, eventqueue, cliquetable, success) );
8754 }
8755
8756 /* possibly restore solution values */
8757 if( lp->flushed && SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_NOTSOLVED )
8758 {
8759 /* restore status */
8760 lp->lpsolstat = storedsolvals.lpsolstat;
8761 lp->lpobjval = storedsolvals.lpobjval;
8762 lp->primalfeasible = storedsolvals.primalfeasible;
8763 lp->primalchecked = storedsolvals.primalchecked;
8764 lp->dualfeasible = storedsolvals.dualfeasible;
8765 lp->dualchecked = storedsolvals.dualchecked;
8766 lp->solisbasic = storedsolvals.solisbasic;
8767 lp->solved = storedsolvals.lpissolved;
8768
8769 for (c = 0; c < lp->ncols; ++c)
8770 {
8771 SCIP_COL* col;
8772
8773 col = lp->cols[c];
8774 assert( col != NULL );
8775 col->primsol = storedcolsolvals[c].primsol;
8776 col->redcost = storedcolsolvals[c].redcost;
8777 col->basisstatus = storedcolsolvals[c].basisstatus; /*lint !e641 !e732*/
8778 }
8779 for (r = 0; r < lp->nrows; ++r)
8780 {
8781 SCIP_ROW* row;
8782
8783 row = lp->rows[r];
8784 assert( row != NULL );
8785
8786 if ( lp->lpsolstat == SCIP_LPSOLSTAT_INFEASIBLE )
8787 row->dualfarkas = storedrowsolvals[r].dualsol;
8788 else
8789 {
8790 assert( lp->lpsolstat == SCIP_LPSOLSTAT_OBJLIMIT );
8791 row->dualsol = storedrowsolvals[r].dualsol;
8792 }
8793 row->activity = storedrowsolvals[r].activity;
8794 row->basisstatus = storedrowsolvals[r].basisstatus; /*lint !e641 !e732*/
8795 }
8796 }
8797 SCIPsetFreeBufferArray(set, &storedrowsolvals);
8798 SCIPsetFreeBufferArray(set, &storedcolsolvals);
8799
8800 return SCIP_OKAY;
8801 }
8802
8803 /** gets time in seconds used for analyzing infeasible LP conflicts */
SCIPconflictGetInfeasibleLPTime(SCIP_CONFLICT * conflict)8804 SCIP_Real SCIPconflictGetInfeasibleLPTime(
8805 SCIP_CONFLICT* conflict /**< conflict analysis data */
8806 )
8807 {
8808 assert(conflict != NULL);
8809
8810 return SCIPclockGetTime(conflict->inflpanalyzetime);
8811 }
8812
8813 /** gets number of calls to infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPCalls(SCIP_CONFLICT * conflict)8814 SCIP_Longint SCIPconflictGetNInfeasibleLPCalls(
8815 SCIP_CONFLICT* conflict /**< conflict analysis data */
8816 )
8817 {
8818 assert(conflict != NULL);
8819
8820 return conflict->ninflpcalls;
8821 }
8822
8823 /** gets number of calls to infeasible LP conflict analysis that yield at least one conflict constraint */
SCIPconflictGetNInfeasibleLPSuccess(SCIP_CONFLICT * conflict)8824 SCIP_Longint SCIPconflictGetNInfeasibleLPSuccess(
8825 SCIP_CONFLICT* conflict /**< conflict analysis data */
8826 )
8827 {
8828 assert(conflict != NULL);
8829
8830 return conflict->ninflpsuccess;
8831 }
8832
8833 /** gets number of conflict constraints detected in infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPConflictConss(SCIP_CONFLICT * conflict)8834 SCIP_Longint SCIPconflictGetNInfeasibleLPConflictConss(
8835 SCIP_CONFLICT* conflict /**< conflict analysis data */
8836 )
8837 {
8838 assert(conflict != NULL);
8839
8840 return conflict->ninflpconfconss;
8841 }
8842
8843 /** gets total number of literals in conflict constraints created in infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPConflictLiterals(SCIP_CONFLICT * conflict)8844 SCIP_Longint SCIPconflictGetNInfeasibleLPConflictLiterals(
8845 SCIP_CONFLICT* conflict /**< conflict analysis data */
8846 )
8847 {
8848 assert(conflict != NULL);
8849
8850 return conflict->ninflpconfliterals;
8851 }
8852
8853 /** gets number of reconvergence constraints detected in infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPReconvergenceConss(SCIP_CONFLICT * conflict)8854 SCIP_Longint SCIPconflictGetNInfeasibleLPReconvergenceConss(
8855 SCIP_CONFLICT* conflict /**< conflict analysis data */
8856 )
8857 {
8858 assert(conflict != NULL);
8859
8860 return conflict->ninflpreconvconss;
8861 }
8862
8863 /** gets total number of literals in reconvergence constraints created in infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPReconvergenceLiterals(SCIP_CONFLICT * conflict)8864 SCIP_Longint SCIPconflictGetNInfeasibleLPReconvergenceLiterals(
8865 SCIP_CONFLICT* conflict /**< conflict analysis data */
8866 )
8867 {
8868 assert(conflict != NULL);
8869
8870 return conflict->ninflpreconvliterals;
8871 }
8872
8873 /** gets number of LP iterations in infeasible LP conflict analysis */
SCIPconflictGetNInfeasibleLPIterations(SCIP_CONFLICT * conflict)8874 SCIP_Longint SCIPconflictGetNInfeasibleLPIterations(
8875 SCIP_CONFLICT* conflict /**< conflict analysis data */
8876 )
8877 {
8878 assert(conflict != NULL);
8879
8880 return conflict->ninflpiterations;
8881 }
8882
8883 /** gets time in seconds used for analyzing bound exceeding LP conflicts */
SCIPconflictGetBoundexceedingLPTime(SCIP_CONFLICT * conflict)8884 SCIP_Real SCIPconflictGetBoundexceedingLPTime(
8885 SCIP_CONFLICT* conflict /**< conflict analysis data */
8886 )
8887 {
8888 assert(conflict != NULL);
8889
8890 return SCIPclockGetTime(conflict->boundlpanalyzetime);
8891 }
8892
8893 /** gets number of calls to bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPCalls(SCIP_CONFLICT * conflict)8894 SCIP_Longint SCIPconflictGetNBoundexceedingLPCalls(
8895 SCIP_CONFLICT* conflict /**< conflict analysis data */
8896 )
8897 {
8898 assert(conflict != NULL);
8899
8900 return conflict->nboundlpcalls;
8901 }
8902
8903 /** gets number of calls to bound exceeding LP conflict analysis that yield at least one conflict constraint */
SCIPconflictGetNBoundexceedingLPSuccess(SCIP_CONFLICT * conflict)8904 SCIP_Longint SCIPconflictGetNBoundexceedingLPSuccess(
8905 SCIP_CONFLICT* conflict /**< conflict analysis data */
8906 )
8907 {
8908 assert(conflict != NULL);
8909
8910 return conflict->nboundlpsuccess;
8911 }
8912
8913 /** gets number of conflict constraints detected in bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPConflictConss(SCIP_CONFLICT * conflict)8914 SCIP_Longint SCIPconflictGetNBoundexceedingLPConflictConss(
8915 SCIP_CONFLICT* conflict /**< conflict analysis data */
8916 )
8917 {
8918 assert(conflict != NULL);
8919
8920 return conflict->nboundlpconfconss;
8921 }
8922
8923 /** gets total number of literals in conflict constraints created in bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPConflictLiterals(SCIP_CONFLICT * conflict)8924 SCIP_Longint SCIPconflictGetNBoundexceedingLPConflictLiterals(
8925 SCIP_CONFLICT* conflict /**< conflict analysis data */
8926 )
8927 {
8928 assert(conflict != NULL);
8929
8930 return conflict->nboundlpconfliterals;
8931 }
8932
8933 /** gets number of reconvergence constraints detected in bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPReconvergenceConss(SCIP_CONFLICT * conflict)8934 SCIP_Longint SCIPconflictGetNBoundexceedingLPReconvergenceConss(
8935 SCIP_CONFLICT* conflict /**< conflict analysis data */
8936 )
8937 {
8938 assert(conflict != NULL);
8939
8940 return conflict->nboundlpreconvconss;
8941 }
8942
8943 /** gets total number of literals in reconvergence constraints created in bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPReconvergenceLiterals(SCIP_CONFLICT * conflict)8944 SCIP_Longint SCIPconflictGetNBoundexceedingLPReconvergenceLiterals(
8945 SCIP_CONFLICT* conflict /**< conflict analysis data */
8946 )
8947 {
8948 assert(conflict != NULL);
8949
8950 return conflict->nboundlpreconvliterals;
8951 }
8952
8953 /** gets number of LP iterations in bound exceeding LP conflict analysis */
SCIPconflictGetNBoundexceedingLPIterations(SCIP_CONFLICT * conflict)8954 SCIP_Longint SCIPconflictGetNBoundexceedingLPIterations(
8955 SCIP_CONFLICT* conflict /**< conflict analysis data */
8956 )
8957 {
8958 assert(conflict != NULL);
8959
8960 return conflict->nboundlpiterations;
8961 }
8962
8963
8964
8965
8966 /*
8967 * infeasible strong branching conflict analysis
8968 */
8969
8970 /** analyses infeasible strong branching sub problems for conflicts */
SCIPconflictAnalyzeStrongbranch(SCIP_CONFLICT * conflict,SCIP_CONFLICTSTORE * conflictstore,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_COL * col,SCIP_Bool * downconflict,SCIP_Bool * upconflict)8971 SCIP_RETCODE SCIPconflictAnalyzeStrongbranch(
8972 SCIP_CONFLICT* conflict, /**< conflict analysis data */
8973 SCIP_CONFLICTSTORE* conflictstore, /**< conflict store */
8974 BMS_BLKMEM* blkmem, /**< block memory buffers */
8975 SCIP_SET* set, /**< global SCIP settings */
8976 SCIP_STAT* stat, /**< dynamic problem statistics */
8977 SCIP_PROB* transprob, /**< transformed problem */
8978 SCIP_PROB* origprob, /**< original problem */
8979 SCIP_TREE* tree, /**< branch and bound tree */
8980 SCIP_REOPT* reopt, /**< reoptimization data structure */
8981 SCIP_LP* lp, /**< LP data */
8982 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
8983 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
8984 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
8985 SCIP_COL* col, /**< LP column with at least one infeasible strong branching subproblem */
8986 SCIP_Bool* downconflict, /**< pointer to store whether a conflict constraint was created for an
8987 * infeasible downwards branch, or NULL */
8988 SCIP_Bool* upconflict /**< pointer to store whether a conflict constraint was created for an
8989 * infeasible upwards branch, or NULL */
8990 )
8991 {
8992 int* cstat;
8993 int* rstat;
8994 SCIP_RETCODE retcode;
8995 SCIP_Bool resolve;
8996 SCIP_Real oldlb;
8997 SCIP_Real oldub;
8998 SCIP_Real newlb;
8999 SCIP_Real newub;
9000 SCIP_Bool dualraysuccess;
9001 int iter;
9002 int nconss;
9003 int nliterals;
9004 int nreconvconss;
9005 int nreconvliterals;
9006
9007 assert(stat != NULL);
9008 assert(lp != NULL);
9009 assert(lp->flushed);
9010 assert(lp->solved);
9011 assert(SCIPprobAllColsInLP(transprob, set, lp)); /* LP conflict analysis is only valid, if all variables are known */
9012 assert(col != NULL);
9013 assert((col->sbdownvalid && SCIPsetIsGE(set, col->sbdown, lp->cutoffbound)
9014 && SCIPsetFeasCeil(set, col->primsol-1.0) >= col->lb - 0.5)
9015 || (col->sbupvalid && SCIPsetIsGE(set, col->sbup, lp->cutoffbound)
9016 && SCIPsetFeasFloor(set, col->primsol+1.0) <= col->ub + 0.5));
9017 assert(SCIPtreeGetCurrentDepth(tree) > 0);
9018
9019 if( downconflict != NULL )
9020 *downconflict = FALSE;
9021 if( upconflict != NULL )
9022 *upconflict = FALSE;
9023
9024 /* check, if infeasible LP conflict analysis is enabled */
9025 if( !set->conf_enable || !set->conf_usesb )
9026 return SCIP_OKAY;
9027
9028 /* check, if there are any conflict handlers to use a conflict set */
9029 if( set->nconflicthdlrs == 0 )
9030 return SCIP_OKAY;
9031
9032 /* inform the LPI that strong branch is (temporarily) finished */
9033 SCIP_CALL( SCIPlpiEndStrongbranch(lp->lpi) );
9034
9035 /* start timing */
9036 SCIPclockStart(conflict->sbanalyzetime, set);
9037
9038 /* get temporary memory for storing current LP basis */
9039 SCIP_CALL( SCIPsetAllocBufferArray(set, &cstat, lp->nlpicols) );
9040 SCIP_CALL( SCIPsetAllocBufferArray(set, &rstat, lp->nlpirows) );
9041
9042 /* get current LP basis */
9043 SCIP_CALL( SCIPlpiGetBase(lp->lpi, cstat, rstat) );
9044
9045 /* remember old bounds */
9046 oldlb = col->lb;
9047 oldub = col->ub;
9048
9049 resolve = FALSE;
9050
9051 /* is down branch infeasible? */
9052 if( col->sbdownvalid && SCIPsetIsGE(set, col->sbdown, lp->cutoffbound) )
9053 {
9054 newub = SCIPsetFeasCeil(set, col->primsol-1.0);
9055 if( newub >= col->lb - 0.5 )
9056 {
9057 SCIPsetDebugMsg(set, "analyzing conflict on infeasible downwards strongbranch for variable <%s>[%g,%g] in depth %d\n",
9058 SCIPvarGetName(SCIPcolGetVar(col)), SCIPvarGetLbLocal(SCIPcolGetVar(col)), SCIPvarGetUbLocal(SCIPcolGetVar(col)),
9059 SCIPtreeGetCurrentDepth(tree));
9060
9061 conflict->conflictset->conflicttype = SCIP_CONFTYPE_INFEASLP;
9062 conflict->nsbcalls++;
9063
9064 /* change the upper bound */
9065 col->ub = newub;
9066 SCIP_CALL( SCIPlpiChgBounds(lp->lpi, 1, &col->lpipos, &col->lb, &col->ub) );
9067
9068 /* start LP timer */
9069 SCIPclockStart(stat->conflictlptime, set);
9070
9071 /* resolve the LP */
9072 retcode = SCIPlpiSolveDual(lp->lpi);
9073
9074 /* stop LP timer */
9075 SCIPclockStop(stat->conflictlptime, set);
9076
9077 /* check return code of LP solving call */
9078 if( retcode != SCIP_LPERROR )
9079 {
9080 SCIP_CALL( retcode );
9081
9082 /* count number of LP iterations */
9083 SCIP_CALL( SCIPlpiGetIterations(lp->lpi, &iter) );
9084 stat->nconflictlps++;
9085 stat->nconflictlpiterations += iter;
9086 conflict->nsbiterations += iter;
9087 SCIPsetDebugMsg(set, " -> resolved downwards strong branching LP in %d iterations\n", iter);
9088
9089 /* perform conflict analysis on infeasible LP; last parameter guarantees status 'solved' on return */
9090 SCIP_CALL( conflictAnalyzeLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, reopt, \
9091 lp, branchcand, eventqueue, cliquetable, TRUE, &dualraysuccess, &iter, &nconss, &nliterals, \
9092 &nreconvconss, &nreconvliterals, FALSE) );
9093 conflict->nsbsuccess += ((nconss > 0 || dualraysuccess) ? 1 : 0);
9094 conflict->nsbiterations += iter;
9095 conflict->nsbconfconss += nconss;
9096 conflict->nsbconfliterals += nliterals;
9097 conflict->nsbreconvconss += nreconvconss;
9098 conflict->nsbreconvliterals += nreconvliterals;
9099 if( downconflict != NULL )
9100 *downconflict = (nconss > 0);
9101 }
9102
9103 /* reset the upper bound */
9104 col->ub = oldub;
9105 SCIP_CALL( SCIPlpiChgBounds(lp->lpi, 1, &col->lpipos, &col->lb, &col->ub) );
9106
9107 /* reset LP basis */
9108 SCIP_CALL( SCIPlpiSetBase(lp->lpi, cstat, rstat) );
9109
9110 /* mark the LP to be resolved at the end */
9111 resolve = TRUE;
9112 }
9113 }
9114
9115 /* is up branch infeasible? */
9116 if( col->sbupvalid && SCIPsetIsGE(set, col->sbup, lp->cutoffbound) )
9117 {
9118 newlb = SCIPsetFeasFloor(set, col->primsol+1.0);
9119 if( newlb <= col->ub + 0.5 )
9120 {
9121 SCIPsetDebugMsg(set, "analyzing conflict on infeasible upwards strongbranch for variable <%s>[%g,%g] in depth %d\n",
9122 SCIPvarGetName(SCIPcolGetVar(col)), SCIPvarGetLbLocal(SCIPcolGetVar(col)), SCIPvarGetUbLocal(SCIPcolGetVar(col)),
9123 SCIPtreeGetCurrentDepth(tree));
9124
9125 conflict->conflictset->conflicttype = SCIP_CONFTYPE_INFEASLP;
9126 conflict->nsbcalls++;
9127
9128 /* change the lower bound */
9129 col->lb = newlb;
9130 SCIP_CALL( SCIPlpiChgBounds(lp->lpi, 1, &col->lpipos, &col->lb, &col->ub) );
9131
9132 /* start LP timer */
9133 SCIPclockStart(stat->conflictlptime, set);
9134
9135 /* resolve the LP */
9136 retcode = SCIPlpiSolveDual(lp->lpi);
9137
9138 /* stop LP timer */
9139 SCIPclockStop(stat->conflictlptime, set);
9140
9141 /* check return code of LP solving call */
9142 if( retcode != SCIP_LPERROR )
9143 {
9144 SCIP_CALL( retcode );
9145
9146 /* count number of LP iterations */
9147 SCIP_CALL( SCIPlpiGetIterations(lp->lpi, &iter) );
9148 stat->nconflictlps++;
9149 stat->nconflictlpiterations += iter;
9150 conflict->nsbiterations += iter;
9151 SCIPsetDebugMsg(set, " -> resolved upwards strong branching LP in %d iterations\n", iter);
9152
9153 /* perform conflict analysis on infeasible LP; last parameter guarantees status 'solved' on return */
9154 SCIP_CALL( conflictAnalyzeLP(conflict, conflictstore, blkmem, set, stat, transprob, origprob, tree, reopt, \
9155 lp, branchcand, eventqueue, cliquetable, TRUE, &dualraysuccess, &iter, &nconss, &nliterals, \
9156 &nreconvconss, &nreconvliterals, FALSE) );
9157 conflict->nsbsuccess += ((nconss > 0 || dualraysuccess) ? 1 : 0);
9158 conflict->nsbiterations += iter;
9159 conflict->nsbconfconss += nconss;
9160 conflict->nsbconfliterals += nliterals;
9161 conflict->nsbreconvconss += nreconvconss;
9162 conflict->nsbreconvliterals += nreconvliterals;
9163 if( upconflict != NULL )
9164 *upconflict = (nconss > 0);
9165 }
9166
9167 /* reset the lower bound */
9168 col->lb = oldlb;
9169 SCIP_CALL( SCIPlpiChgBounds(lp->lpi, 1, &col->lpipos, &col->lb, &col->ub) );
9170
9171 /* reset LP basis */
9172 SCIP_CALL( SCIPlpiSetBase(lp->lpi, cstat, rstat) );
9173
9174 /* mark the LP to be resolved at the end */
9175 resolve = TRUE;
9176 }
9177 }
9178
9179 /* free temporary memory for storing current LP basis */
9180 SCIPsetFreeBufferArray(set, &rstat);
9181 SCIPsetFreeBufferArray(set, &cstat);
9182
9183 assert(lp->flushed);
9184
9185 /* resolve LP if something has changed in order to synchronize LPI and LP */
9186 if ( resolve )
9187 {
9188 /* start LP timer */
9189 SCIPclockStart(stat->conflictlptime, set);
9190
9191 /* resolve the LP */
9192 SCIP_CALL( SCIPlpiSolveDual(lp->lpi) );
9193
9194 /* stop LP timer */
9195 SCIPclockStop(stat->conflictlptime, set);
9196 }
9197
9198 /* stop timing */
9199 SCIPclockStop(conflict->sbanalyzetime, set);
9200
9201 /* inform the LPI that strong branch starts (again) */
9202 SCIP_CALL( SCIPlpiStartStrongbranch(lp->lpi) );
9203
9204 return SCIP_OKAY;
9205 }
9206
9207 /** gets time in seconds used for analyzing infeasible strong branching conflicts */
SCIPconflictGetStrongbranchTime(SCIP_CONFLICT * conflict)9208 SCIP_Real SCIPconflictGetStrongbranchTime(
9209 SCIP_CONFLICT* conflict /**< conflict analysis data */
9210 )
9211 {
9212 assert(conflict != NULL);
9213
9214 return SCIPclockGetTime(conflict->sbanalyzetime);
9215 }
9216
9217 /** gets number of successful calls to dual proof analysis derived from infeasible LPs */
SCIPconflictGetNDualproofsInfSuccess(SCIP_CONFLICT * conflict)9218 SCIP_Longint SCIPconflictGetNDualproofsInfSuccess(
9219 SCIP_CONFLICT* conflict /**< conflict analysis data */
9220 )
9221 {
9222 assert(conflict != NULL);
9223
9224 return conflict->ndualproofsinfsuccess;
9225 }
9226
9227 /** gets number of globally valid dual proof constraints derived from infeasible LPs */
SCIPconflictGetNDualproofsInfGlobal(SCIP_CONFLICT * conflict)9228 SCIP_Longint SCIPconflictGetNDualproofsInfGlobal(
9229 SCIP_CONFLICT* conflict /**< conflict analysis data */
9230 )
9231 {
9232 assert(conflict != NULL);
9233
9234 return conflict->ndualproofsinfglobal;
9235 }
9236
9237 /** gets number of locally valid dual proof constraints derived from infeasible LPs */
SCIPconflictGetNDualproofsInfLocal(SCIP_CONFLICT * conflict)9238 SCIP_Longint SCIPconflictGetNDualproofsInfLocal(
9239 SCIP_CONFLICT* conflict /**< conflict analysis data */
9240 )
9241 {
9242 assert(conflict != NULL);
9243
9244 return conflict->ndualproofsinflocal;
9245 }
9246
9247 /** gets average length of dual proof constraints derived from infeasible LPs */
SCIPconflictGetNDualproofsInfNonzeros(SCIP_CONFLICT * conflict)9248 SCIP_Longint SCIPconflictGetNDualproofsInfNonzeros(
9249 SCIP_CONFLICT* conflict /**< conflict analysis data */
9250 )
9251 {
9252 assert(conflict != NULL);
9253
9254 return conflict->dualproofsinfnnonzeros;
9255 }
9256
9257 /** gets number of successfully analyzed dual proofs derived from bound exceeding LPs */
SCIPconflictGetNDualproofsBndSuccess(SCIP_CONFLICT * conflict)9258 SCIP_Longint SCIPconflictGetNDualproofsBndSuccess(
9259 SCIP_CONFLICT* conflict /**< conflict analysis data */
9260 )
9261 {
9262 assert(conflict != NULL);
9263
9264 return conflict->ndualproofsbndsuccess;
9265 }
9266
9267 /** gets number of globally applied dual proofs derived from bound exceeding LPs */
SCIPconflictGetNDualproofsBndGlobal(SCIP_CONFLICT * conflict)9268 SCIP_Longint SCIPconflictGetNDualproofsBndGlobal(
9269 SCIP_CONFLICT* conflict /**< conflict analysis data */
9270 )
9271 {
9272 assert(conflict != NULL);
9273
9274 return conflict->ndualproofsbndglobal;
9275 }
9276
9277 /** gets number of locally applied dual proofs derived from bound exceeding LPs */
SCIPconflictGetNDualproofsBndLocal(SCIP_CONFLICT * conflict)9278 SCIP_Longint SCIPconflictGetNDualproofsBndLocal(
9279 SCIP_CONFLICT* conflict /**< conflict analysis data */
9280 )
9281 {
9282 assert(conflict != NULL);
9283
9284 return conflict->ndualproofsbndlocal;
9285 }
9286
9287 /** gets average length of dual proofs derived from bound exceeding LPs */
SCIPconflictGetNDualproofsBndNonzeros(SCIP_CONFLICT * conflict)9288 SCIP_Longint SCIPconflictGetNDualproofsBndNonzeros(
9289 SCIP_CONFLICT* conflict /**< conflict analysis data */
9290 )
9291 {
9292 assert(conflict != NULL);
9293
9294 return conflict->dualproofsbndnnonzeros;
9295 }
9296
9297 /** gets number of calls to infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchCalls(SCIP_CONFLICT * conflict)9298 SCIP_Longint SCIPconflictGetNStrongbranchCalls(
9299 SCIP_CONFLICT* conflict /**< conflict analysis data */
9300 )
9301 {
9302 assert(conflict != NULL);
9303
9304 return conflict->nsbcalls;
9305 }
9306
9307 /** gets number of calls to infeasible strong branching conflict analysis that yield at least one conflict constraint */
SCIPconflictGetNStrongbranchSuccess(SCIP_CONFLICT * conflict)9308 SCIP_Longint SCIPconflictGetNStrongbranchSuccess(
9309 SCIP_CONFLICT* conflict /**< conflict analysis data */
9310 )
9311 {
9312 assert(conflict != NULL);
9313
9314 return conflict->nsbsuccess;
9315 }
9316
9317 /** gets number of conflict constraints detected in infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchConflictConss(SCIP_CONFLICT * conflict)9318 SCIP_Longint SCIPconflictGetNStrongbranchConflictConss(
9319 SCIP_CONFLICT* conflict /**< conflict analysis data */
9320 )
9321 {
9322 assert(conflict != NULL);
9323
9324 return conflict->nsbconfconss;
9325 }
9326
9327 /** gets total number of literals in conflict constraints created in infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchConflictLiterals(SCIP_CONFLICT * conflict)9328 SCIP_Longint SCIPconflictGetNStrongbranchConflictLiterals(
9329 SCIP_CONFLICT* conflict /**< conflict analysis data */
9330 )
9331 {
9332 assert(conflict != NULL);
9333
9334 return conflict->nsbconfliterals;
9335 }
9336
9337 /** gets number of reconvergence constraints detected in infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchReconvergenceConss(SCIP_CONFLICT * conflict)9338 SCIP_Longint SCIPconflictGetNStrongbranchReconvergenceConss(
9339 SCIP_CONFLICT* conflict /**< conflict analysis data */
9340 )
9341 {
9342 assert(conflict != NULL);
9343
9344 return conflict->nsbreconvconss;
9345 }
9346
9347 /** gets total number of literals in reconvergence constraints created in infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchReconvergenceLiterals(SCIP_CONFLICT * conflict)9348 SCIP_Longint SCIPconflictGetNStrongbranchReconvergenceLiterals(
9349 SCIP_CONFLICT* conflict /**< conflict analysis data */
9350 )
9351 {
9352 assert(conflict != NULL);
9353
9354 return conflict->nsbreconvliterals;
9355 }
9356
9357 /** gets number of LP iterations in infeasible strong branching conflict analysis */
SCIPconflictGetNStrongbranchIterations(SCIP_CONFLICT * conflict)9358 SCIP_Longint SCIPconflictGetNStrongbranchIterations(
9359 SCIP_CONFLICT* conflict /**< conflict analysis data */
9360 )
9361 {
9362 assert(conflict != NULL);
9363
9364 return conflict->nsbiterations;
9365 }
9366
9367
9368
9369
9370 /*
9371 * pseudo solution conflict analysis
9372 */
9373
9374 /** analyzes a pseudo solution with objective value exceeding the current cutoff to find out the bound changes on
9375 * variables that were responsible for the objective value degradation;
9376 * on success, calls standard conflict analysis with the responsible variables as starting conflict set, thus creating
9377 * a conflict constraint out of the resulting conflict set;
9378 * updates statistics for pseudo solution conflict analysis
9379 */
SCIPconflictAnalyzePseudo(SCIP_CONFLICT * conflict,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * success)9380 SCIP_RETCODE SCIPconflictAnalyzePseudo(
9381 SCIP_CONFLICT* conflict, /**< conflict analysis data */
9382 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
9383 SCIP_SET* set, /**< global SCIP settings */
9384 SCIP_STAT* stat, /**< problem statistics */
9385 SCIP_PROB* transprob, /**< transformed problem */
9386 SCIP_PROB* origprob, /**< original problem */
9387 SCIP_TREE* tree, /**< branch and bound tree */
9388 SCIP_REOPT* reopt, /**< reoptimization data structure */
9389 SCIP_LP* lp, /**< LP data */
9390 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9391 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9392 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9393 SCIP_Bool* success /**< pointer to store whether a conflict constraint was created, or NULL */
9394 )
9395 {
9396 SCIP_VAR** vars;
9397 SCIP_VAR* var;
9398 SCIP_Real* curvarlbs;
9399 SCIP_Real* curvarubs;
9400 int* lbchginfoposs;
9401 int* ubchginfoposs;
9402 SCIP_Real* pseudocoefs;
9403 SCIP_Real pseudolhs;
9404 SCIP_Real pseudoact;
9405 int nvars;
9406 int v;
9407
9408 assert(conflict != NULL);
9409 assert(conflict->nconflictsets == 0);
9410 assert(set != NULL);
9411 assert(stat != NULL);
9412 assert(transprob != NULL);
9413 assert(lp != NULL);
9414 assert(!SCIPsetIsInfinity(set, -SCIPlpGetPseudoObjval(lp, set, transprob)));
9415 assert(!SCIPsetIsInfinity(set, lp->cutoffbound));
9416
9417 if( success != NULL )
9418 *success = FALSE;
9419
9420 /* check, if pseudo solution conflict analysis is enabled */
9421 if( !set->conf_enable || !set->conf_usepseudo )
9422 return SCIP_OKAY;
9423
9424 /* check, if there are any conflict handlers to use a conflict set */
9425 if( set->nconflicthdlrs == 0 )
9426 return SCIP_OKAY;
9427
9428 SCIPsetDebugMsg(set, "analyzing pseudo solution (obj: %g) that exceeds objective limit (%g)\n",
9429 SCIPlpGetPseudoObjval(lp, set, transprob), lp->cutoffbound);
9430
9431 conflict->conflictset->conflicttype = SCIP_CONFTYPE_BNDEXCEEDING;
9432 conflict->conflictset->usescutoffbound = TRUE;
9433
9434 /* start timing */
9435 SCIPclockStart(conflict->pseudoanalyzetime, set);
9436 conflict->npseudocalls++;
9437
9438 vars = transprob->vars;
9439 nvars = transprob->nvars;
9440 assert(nvars == 0 || vars != NULL);
9441
9442 /* The current primal bound c* gives an upper bound for the current pseudo objective value:
9443 * min{c^T x | lb <= x <= ub} <= c*.
9444 * We have to transform this row into a >= inequality in order to use methods above:
9445 * -c* <= max{-c^T x | lb <= x <= ub}.
9446 * In the local subproblem, this row is violated. We want to undo bound changes while still keeping the
9447 * row violated.
9448 */
9449
9450 /* get temporary memory for remembering variables' current bounds and corresponding bound change information
9451 * positions in variable's bound change information arrays
9452 */
9453 SCIP_CALL( SCIPsetAllocBufferArray(set, &curvarlbs, nvars) );
9454 SCIP_CALL( SCIPsetAllocBufferArray(set, &curvarubs, nvars) );
9455 SCIP_CALL( SCIPsetAllocBufferArray(set, &lbchginfoposs, nvars) );
9456 SCIP_CALL( SCIPsetAllocBufferArray(set, &ubchginfoposs, nvars) );
9457
9458 /* get temporary memory for infeasibility proof coefficients */
9459 SCIP_CALL( SCIPsetAllocBufferArray(set, &pseudocoefs, nvars) );
9460
9461 /* use a slightly tighter cutoff bound, because solutions with equal objective value should also be declared
9462 * infeasible
9463 */
9464 pseudolhs = -(lp->cutoffbound - SCIPsetSumepsilon(set));
9465
9466 /* store the objective values as infeasibility proof coefficients, and recalculate the pseudo activity */
9467 pseudoact = 0.0;
9468 for( v = 0; v < nvars; ++v )
9469 {
9470 var = vars[v];
9471 pseudocoefs[v] = -SCIPvarGetObj(var);
9472 curvarlbs[v] = SCIPvarGetLbLocal(var);
9473 curvarubs[v] = SCIPvarGetUbLocal(var);
9474 lbchginfoposs[v] = var->nlbchginfos-1;
9475 ubchginfoposs[v] = var->nubchginfos-1;
9476
9477 if( SCIPsetIsZero(set, pseudocoefs[v]) )
9478 {
9479 pseudocoefs[v] = 0.0;
9480 continue;
9481 }
9482
9483 if( pseudocoefs[v] > 0.0 )
9484 pseudoact += pseudocoefs[v] * curvarubs[v];
9485 else
9486 pseudoact += pseudocoefs[v] * curvarlbs[v];
9487 }
9488 assert(SCIPsetIsFeasEQ(set, pseudoact, -SCIPlpGetPseudoObjval(lp, set, transprob)));
9489 SCIPsetDebugMsg(set, " -> recalculated pseudo infeasibility proof: %g <= %g\n", pseudolhs, pseudoact);
9490
9491 /* check, if the pseudo row is still violated (after recalculation of pseudo activity) */
9492 if( SCIPsetIsFeasGT(set, pseudolhs, pseudoact) )
9493 {
9494 int nconss;
9495 int nliterals;
9496 int nreconvconss;
9497 int nreconvliterals;
9498
9499 /* undo bound changes without destroying the infeasibility proof */
9500 SCIP_CALL( undoBdchgsProof(set, transprob, SCIPtreeGetCurrentDepth(tree), pseudocoefs, pseudolhs, &pseudoact,
9501 curvarlbs, curvarubs, lbchginfoposs, ubchginfoposs, NULL, NULL, NULL, lp->lpi) );
9502
9503 /* analyze conflict on remaining bound changes */
9504 SCIP_CALL( conflictAnalyzeRemainingBdchgs(conflict, blkmem, set, stat, transprob, tree, FALSE, \
9505 lbchginfoposs, ubchginfoposs, &nconss, &nliterals, &nreconvconss, &nreconvliterals) );
9506 conflict->npseudosuccess += (nconss > 0 ? 1 : 0);
9507 conflict->npseudoconfconss += nconss;
9508 conflict->npseudoconfliterals += nliterals;
9509 conflict->npseudoreconvconss += nreconvconss;
9510 conflict->npseudoreconvliterals += nreconvliterals;
9511 if( success != NULL )
9512 *success = (nconss > 0);
9513 }
9514
9515 /* free temporary memory */
9516 SCIPsetFreeBufferArray(set, &pseudocoefs);
9517 SCIPsetFreeBufferArray(set, &ubchginfoposs);
9518 SCIPsetFreeBufferArray(set, &lbchginfoposs);
9519 SCIPsetFreeBufferArray(set, &curvarubs);
9520 SCIPsetFreeBufferArray(set, &curvarlbs);
9521
9522 /* flush conflict set storage */
9523 SCIP_CALL( SCIPconflictFlushConss(conflict, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue, cliquetable) );
9524
9525 /* stop timing */
9526 SCIPclockStop(conflict->pseudoanalyzetime, set);
9527
9528 return SCIP_OKAY;
9529 }
9530
9531 /** gets time in seconds used for analyzing pseudo solution conflicts */
SCIPconflictGetPseudoTime(SCIP_CONFLICT * conflict)9532 SCIP_Real SCIPconflictGetPseudoTime(
9533 SCIP_CONFLICT* conflict /**< conflict analysis data */
9534 )
9535 {
9536 assert(conflict != NULL);
9537
9538 return SCIPclockGetTime(conflict->pseudoanalyzetime);
9539 }
9540
9541 /** gets number of calls to pseudo solution conflict analysis */
SCIPconflictGetNPseudoCalls(SCIP_CONFLICT * conflict)9542 SCIP_Longint SCIPconflictGetNPseudoCalls(
9543 SCIP_CONFLICT* conflict /**< conflict analysis data */
9544 )
9545 {
9546 assert(conflict != NULL);
9547
9548 return conflict->npseudocalls;
9549 }
9550
9551 /** gets number of calls to pseudo solution conflict analysis that yield at least one conflict constraint */
SCIPconflictGetNPseudoSuccess(SCIP_CONFLICT * conflict)9552 SCIP_Longint SCIPconflictGetNPseudoSuccess(
9553 SCIP_CONFLICT* conflict /**< conflict analysis data */
9554 )
9555 {
9556 assert(conflict != NULL);
9557
9558 return conflict->npseudosuccess;
9559 }
9560
9561 /** gets number of conflict constraints detected in pseudo solution conflict analysis */
SCIPconflictGetNPseudoConflictConss(SCIP_CONFLICT * conflict)9562 SCIP_Longint SCIPconflictGetNPseudoConflictConss(
9563 SCIP_CONFLICT* conflict /**< conflict analysis data */
9564 )
9565 {
9566 assert(conflict != NULL);
9567
9568 return conflict->npseudoconfconss;
9569 }
9570
9571 /** gets total number of literals in conflict constraints created in pseudo solution conflict analysis */
SCIPconflictGetNPseudoConflictLiterals(SCIP_CONFLICT * conflict)9572 SCIP_Longint SCIPconflictGetNPseudoConflictLiterals(
9573 SCIP_CONFLICT* conflict /**< conflict analysis data */
9574 )
9575 {
9576 assert(conflict != NULL);
9577
9578 return conflict->npseudoconfliterals;
9579 }
9580
9581 /** gets number of reconvergence constraints detected in pseudo solution conflict analysis */
SCIPconflictGetNPseudoReconvergenceConss(SCIP_CONFLICT * conflict)9582 SCIP_Longint SCIPconflictGetNPseudoReconvergenceConss(
9583 SCIP_CONFLICT* conflict /**< conflict analysis data */
9584 )
9585 {
9586 assert(conflict != NULL);
9587
9588 return conflict->npseudoreconvconss;
9589 }
9590
9591 /** gets total number of literals in reconvergence constraints created in pseudo solution conflict analysis */
SCIPconflictGetNPseudoReconvergenceLiterals(SCIP_CONFLICT * conflict)9592 SCIP_Longint SCIPconflictGetNPseudoReconvergenceLiterals(
9593 SCIP_CONFLICT* conflict /**< conflict analysis data */
9594 )
9595 {
9596 assert(conflict != NULL);
9597
9598 return conflict->npseudoreconvliterals;
9599 }
9600
9601
9602 /** enables or disables all clocks of \p conflict, depending on the value of the flag */
SCIPconflictEnableOrDisableClocks(SCIP_CONFLICT * conflict,SCIP_Bool enable)9603 void SCIPconflictEnableOrDisableClocks(
9604 SCIP_CONFLICT* conflict, /**< the conflict analysis data for which all clocks should be enabled or disabled */
9605 SCIP_Bool enable /**< should the clocks of the conflict analysis data be enabled? */
9606 )
9607 {
9608 assert(conflict != NULL);
9609
9610 SCIPclockEnableOrDisable(conflict->boundlpanalyzetime, enable);
9611 SCIPclockEnableOrDisable(conflict->dIBclock, enable);
9612 SCIPclockEnableOrDisable(conflict->inflpanalyzetime, enable);
9613 SCIPclockEnableOrDisable(conflict->propanalyzetime, enable);
9614 SCIPclockEnableOrDisable(conflict->pseudoanalyzetime, enable);
9615 SCIPclockEnableOrDisable(conflict->sbanalyzetime, enable);
9616 }
9617
9618