1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file var.c
17 * @ingroup OTHER_CFILES
18 * @brief methods for problem variables
19 * @author Tobias Achterberg
20 * @author Timo Berthold
21 * @author Gerald Gamrath
22 * @author Stefan Heinz
23 * @author Marc Pfetsch
24 * @author Michael Winkler
25 * @author Kati Wolter
26 * @author Stefan Vigerske
27 *
28 * @todo Possibly implement the access of bounds of multi-aggregated variables by accessing the
29 * corresponding linear constraint if it exists. This seems to require some work, since the linear
30 * constraint has to be stored. Moreover, it has even to be created in case the original constraint
31 * was deleted after multi-aggregation, but the bounds of the multi-aggregated variable should be
32 * changed. This has to be done with care in order to not loose the performance gains of
33 * multi-aggregation.
34 */
35
36 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
37
38 #include "scip/cons.h"
39 #include "scip/event.h"
40 #include "scip/history.h"
41 #include "scip/implics.h"
42 #include "scip/lp.h"
43 #include "scip/primal.h"
44 #include "scip/prob.h"
45 #include "scip/pub_cons.h"
46 #include "scip/pub_history.h"
47 #include "scip/pub_implics.h"
48 #include "scip/pub_lp.h"
49 #include "scip/pub_message.h"
50 #include "scip/pub_misc.h"
51 #include "scip/pub_misc_sort.h"
52 #include "scip/pub_prop.h"
53 #include "scip/pub_var.h"
54 #include "scip/relax.h"
55 #include "scip/set.h"
56 #include "scip/sol.h"
57 #include "scip/stat.h"
58 #include "scip/struct_event.h"
59 #include "scip/struct_lp.h"
60 #include "scip/struct_prob.h"
61 #include "scip/struct_set.h"
62 #include "scip/struct_stat.h"
63 #include "scip/struct_var.h"
64 #include "scip/tree.h"
65 #include "scip/var.h"
66 #include <string.h>
67
68 #define MAXIMPLSCLOSURE 100 /**< maximal number of descendants of implied variable for building closure
69 * in implication graph */
70 #define MAXABSVBCOEF 1e+5 /**< maximal absolute coefficient in variable bounds added due to implications */
71
72 /*
73 * hole, holelist, and domain methods
74 */
75
76 /** creates a new holelist element */
77 static
holelistCreate(SCIP_HOLELIST ** holelist,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real left,SCIP_Real right)78 SCIP_RETCODE holelistCreate(
79 SCIP_HOLELIST** holelist, /**< pointer to holelist to create */
80 BMS_BLKMEM* blkmem, /**< block memory for target holelist */
81 SCIP_SET* set, /**< global SCIP settings */
82 SCIP_Real left, /**< left bound of open interval in new hole */
83 SCIP_Real right /**< right bound of open interval in new hole */
84 )
85 {
86 assert(holelist != NULL);
87 assert(blkmem != NULL);
88 assert(SCIPsetIsLT(set, left, right));
89
90 SCIPsetDebugMsg(set, "create hole list element (%.15g,%.15g) in blkmem %p\n", left, right, (void*)blkmem);
91
92 SCIP_ALLOC( BMSallocBlockMemory(blkmem, holelist) );
93 (*holelist)->hole.left = left;
94 (*holelist)->hole.right = right;
95 (*holelist)->next = NULL;
96
97 return SCIP_OKAY;
98 }
99
100 /** frees all elements in the holelist */
101 static
holelistFree(SCIP_HOLELIST ** holelist,BMS_BLKMEM * blkmem)102 void holelistFree(
103 SCIP_HOLELIST** holelist, /**< pointer to holelist to free */
104 BMS_BLKMEM* blkmem /**< block memory for target holelist */
105 )
106 {
107 assert(holelist != NULL);
108 assert(blkmem != NULL);
109
110 while( *holelist != NULL )
111 {
112 SCIP_HOLELIST* next;
113
114 SCIPdebugMessage("free hole list element (%.15g,%.15g) in blkmem %p\n",
115 (*holelist)->hole.left, (*holelist)->hole.right, (void*)blkmem);
116
117 next = (*holelist)->next;
118 BMSfreeBlockMemory(blkmem, holelist);
119 assert(*holelist == NULL);
120
121 *holelist = next;
122 }
123 assert(*holelist == NULL);
124 }
125
126 /** duplicates a list of holes */
127 static
holelistDuplicate(SCIP_HOLELIST ** target,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_HOLELIST * source)128 SCIP_RETCODE holelistDuplicate(
129 SCIP_HOLELIST** target, /**< pointer to target holelist */
130 BMS_BLKMEM* blkmem, /**< block memory for target holelist */
131 SCIP_SET* set, /**< global SCIP settings */
132 SCIP_HOLELIST* source /**< holelist to duplicate */
133 )
134 {
135 assert(target != NULL);
136
137 while( source != NULL )
138 {
139 assert(source->next == NULL || SCIPsetIsGE(set, source->next->hole.left, source->hole.right));
140 SCIP_CALL( holelistCreate(target, blkmem, set, source->hole.left, source->hole.right) );
141 source = source->next;
142 target = &(*target)->next;
143 }
144
145 return SCIP_OKAY;
146 }
147
148 /** adds a hole to the domain */
149 static
domAddHole(SCIP_DOM * dom,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real left,SCIP_Real right,SCIP_Bool * added)150 SCIP_RETCODE domAddHole(
151 SCIP_DOM* dom, /**< domain to add hole to */
152 BMS_BLKMEM* blkmem, /**< block memory */
153 SCIP_SET* set, /**< global SCIP settings */
154 SCIP_Real left, /**< left bound of open interval in new hole */
155 SCIP_Real right, /**< right bound of open interval in new hole */
156 SCIP_Bool* added /**< pointer to store whether the hole was added (variable didn't had that hole before), or NULL */
157 )
158 {
159 SCIP_HOLELIST** insertpos;
160 SCIP_HOLELIST* next;
161
162 assert(dom != NULL);
163 assert(added != NULL);
164
165 /* search for the position of the new hole */
166 insertpos = &dom->holelist;
167 while( *insertpos != NULL && (*insertpos)->hole.left < left )
168 insertpos = &(*insertpos)->next;
169
170 /* check if new hole already exists in the hole list or is a sub hole of an existing one */
171 if( *insertpos != NULL && (*insertpos)->hole.left == left && (*insertpos)->hole.right >= right ) /*lint !e777 */
172 {
173 SCIPsetDebugMsg(set, "new hole (%.15g,%.15g) is redundant through known hole (%.15g,%.15g)\n",
174 left, right, (*insertpos)->hole.left, (*insertpos)->hole.right);
175 *added = FALSE;
176 return SCIP_OKAY;
177 }
178
179 /* add hole */
180 *added = TRUE;
181
182 next = *insertpos;
183 SCIP_CALL( holelistCreate(insertpos, blkmem, set, left, right) );
184 (*insertpos)->next = next;
185
186 return SCIP_OKAY;
187 }
188
189 /** merges overlapping holes into single holes, computes and moves lower and upper bound, respectively */
190 /**@todo the domMerge() method is currently called if a lower or an upper bound locally or globally changed; this could
191 * be more efficient if performed with the knowledge if it was a lower or an upper bound which triggered this
192 * merge */
193 static
domMerge(SCIP_DOM * dom,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real * newlb,SCIP_Real * newub)194 void domMerge(
195 SCIP_DOM* dom, /**< domain to merge */
196 BMS_BLKMEM* blkmem, /**< block memory */
197 SCIP_SET* set, /**< global SCIP settings */
198 SCIP_Real* newlb, /**< pointer to store new lower bound */
199 SCIP_Real* newub /**< pointer to store new upper bound */
200 )
201 {
202 SCIP_HOLELIST** holelistptr;
203 SCIP_HOLELIST** lastnextptr;
204 SCIP_Real* lastrightptr;
205
206 assert(dom != NULL);
207 assert(SCIPsetIsLE(set, dom->lb, dom->ub));
208
209 #ifndef NDEBUG
210 {
211 /* check if the holelist is sorted w.r.t. to the left interval bounds */
212 SCIP_Real lastleft;
213
214 holelistptr = &dom->holelist;
215
216 lastleft = -SCIPsetInfinity(set);
217
218 while( *holelistptr != NULL )
219 {
220 if( (*holelistptr)->next != NULL )
221 {
222 assert( SCIPsetIsLE(set, lastleft, (*holelistptr)->hole.left) );
223 lastleft = (*holelistptr)->hole.left;
224 }
225
226 holelistptr = &(*holelistptr)->next;
227 }
228 }
229 #endif
230
231 SCIPsetDebugMsg(set, "merge hole list\n");
232
233 holelistptr = &dom->holelist;
234 lastrightptr = &dom->lb; /* lower bound is the right bound of the hole (-infinity,lb) */
235 lastnextptr = holelistptr;
236
237 while( *holelistptr != NULL )
238 {
239 SCIPsetDebugMsg(set, "check hole (%.15g,%.15g) last right interval was <%.15g>\n", (*holelistptr)->hole.left, (*holelistptr)->hole.right, *lastrightptr);
240
241 /* check that the hole is not empty */
242 assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right));
243
244 if( SCIPsetIsGE(set, (*holelistptr)->hole.left, dom->ub) )
245 {
246 /* the remaining holes start behind the upper bound: remove them */
247 SCIPsetDebugMsg(set, "remove remaining hole since upper bound <%.15g> is less then the left hand side of the current hole\n", dom->ub);
248 holelistFree(holelistptr, blkmem);
249 assert(*holelistptr == NULL);
250
251 /* unlink this hole from the previous hole */
252 *lastnextptr = NULL;
253 }
254 else if( SCIPsetIsGT(set, (*holelistptr)->hole.right, dom->ub) )
255 {
256 /* the hole overlaps the upper bound: decrease upper bound, remove this hole and all remaining holes */
257 SCIPsetDebugMsg(set, "upper bound <%.15g> lays in current hole; store new upper bound and remove this and all remaining holes\n", dom->ub);
258
259 assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, dom->ub));
260
261 /* adjust upper bound */
262 dom->ub = (*holelistptr)->hole.left;
263
264 if(newub != NULL )
265 *newub = (*holelistptr)->hole.left;
266
267 /* remove remaining hole list */
268 holelistFree(holelistptr, blkmem);
269 assert(*holelistptr == NULL);
270
271 /* unlink this hole from the previous hole */
272 *lastnextptr = NULL;
273 }
274 else if( SCIPsetIsGT(set, *lastrightptr, (*holelistptr)->hole.left) )
275 {
276 /* the right bound of the last hole is greater than the left bound of this hole: increase the right bound of
277 * the last hole, delete this hole */
278 SCIP_HOLELIST* nextholelist;
279
280 if( SCIPsetIsEQ(set, *lastrightptr, dom->lb ) )
281 {
282 /* the reason for the overlap results from the lower bound hole (-infinity,lb); therefore, we can increase
283 * the lower bound */
284 SCIPsetDebugMsg(set, "lower bound <%.15g> lays in current hole; store new lower bound and remove hole\n", dom->lb);
285 *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);
286
287 /* adjust lower bound */
288 dom->lb = *lastrightptr;
289
290 if(newlb != NULL )
291 *newlb = *lastrightptr;
292 }
293 else
294 {
295 SCIPsetDebugMsg(set, "current hole overlaps with the previous one (...,%.15g); merge to (...,%.15g)\n",
296 *lastrightptr, MAX(*lastrightptr, (*holelistptr)->hole.right) );
297 *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);
298 }
299 nextholelist = (*holelistptr)->next;
300 (*holelistptr)->next = NULL;
301 holelistFree(holelistptr, blkmem);
302
303 /* connect the linked list after removing the hole */
304 *lastnextptr = nextholelist;
305
306 /* get next hole */
307 *holelistptr = nextholelist;
308 }
309 else
310 {
311 /* the holes do not overlap: update lastholelist and lastrightptr */
312 lastrightptr = &(*holelistptr)->hole.right;
313 lastnextptr = &(*holelistptr)->next;
314
315 /* get next hole */
316 holelistptr = &(*holelistptr)->next;
317 }
318 }
319
320 #ifndef NDEBUG
321 {
322 /* check that holes are merged */
323 SCIP_Real lastright;
324
325 lastright = dom->lb; /* lower bound is the right bound of the hole (-infinity,lb) */
326 holelistptr = &dom->holelist;
327
328 while( *holelistptr != NULL )
329 {
330 /* check the the last right interval is smaller or equal to the current left interval (none overlapping) */
331 assert( SCIPsetIsLE(set, lastright, (*holelistptr)->hole.left) );
332
333 /* check the hole property (check that the hole is not empty) */
334 assert( SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right) );
335 lastright = (*holelistptr)->hole.right;
336
337 /* get next hole */
338 holelistptr = &(*holelistptr)->next;
339 }
340
341 /* check the the last right interval is smaller or equal to the upper bound (none overlapping) */
342 assert( SCIPsetIsLE(set, lastright, dom->ub) );
343 }
344 #endif
345 }
346
347 /*
348 * domain change methods
349 */
350
351 /** ensures, that bound change info array for lower bound changes can store at least num entries */
352 static
varEnsureLbchginfosSize(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)353 SCIP_RETCODE varEnsureLbchginfosSize(
354 SCIP_VAR* var, /**< problem variable */
355 BMS_BLKMEM* blkmem, /**< block memory */
356 SCIP_SET* set, /**< global SCIP settings */
357 int num /**< minimum number of entries to store */
358 )
359 {
360 assert(var != NULL);
361 assert(var->nlbchginfos <= var->lbchginfossize);
362 assert(SCIPvarIsTransformed(var));
363
364 if( num > var->lbchginfossize )
365 {
366 int newsize;
367
368 newsize = SCIPsetCalcMemGrowSize(set, num);
369 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->lbchginfos, var->lbchginfossize, newsize) );
370 var->lbchginfossize = newsize;
371 }
372 assert(num <= var->lbchginfossize);
373
374 return SCIP_OKAY;
375 }
376
377 /** ensures, that bound change info array for upper bound changes can store at least num entries */
378 static
varEnsureUbchginfosSize(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)379 SCIP_RETCODE varEnsureUbchginfosSize(
380 SCIP_VAR* var, /**< problem variable */
381 BMS_BLKMEM* blkmem, /**< block memory */
382 SCIP_SET* set, /**< global SCIP settings */
383 int num /**< minimum number of entries to store */
384 )
385 {
386 assert(var != NULL);
387 assert(var->nubchginfos <= var->ubchginfossize);
388 assert(SCIPvarIsTransformed(var));
389
390 if( num > var->ubchginfossize )
391 {
392 int newsize;
393
394 newsize = SCIPsetCalcMemGrowSize(set, num);
395 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->ubchginfos, var->ubchginfossize, newsize) );
396 var->ubchginfossize = newsize;
397 }
398 assert(num <= var->ubchginfossize);
399
400 return SCIP_OKAY;
401 }
402
403 /** adds domain change info to the variable's lower bound change info array */
404 static
varAddLbchginfo(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real oldbound,SCIP_Real newbound,int depth,int pos,SCIP_VAR * infervar,SCIP_CONS * infercons,SCIP_PROP * inferprop,int inferinfo,SCIP_BOUNDTYPE inferboundtype,SCIP_BOUNDCHGTYPE boundchgtype)405 SCIP_RETCODE varAddLbchginfo(
406 SCIP_VAR* var, /**< problem variable */
407 BMS_BLKMEM* blkmem, /**< block memory */
408 SCIP_SET* set, /**< global SCIP settings */
409 SCIP_Real oldbound, /**< old value for bound */
410 SCIP_Real newbound, /**< new value for bound */
411 int depth, /**< depth in the tree, where the bound change takes place */
412 int pos, /**< position of the bound change in its bound change array */
413 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself) */
414 SCIP_CONS* infercons, /**< constraint that infered this bound change, or NULL */
415 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
416 int inferinfo, /**< user information for inference to help resolving the conflict */
417 SCIP_BOUNDTYPE inferboundtype, /**< type of bound for inference var: lower or upper bound */
418 SCIP_BOUNDCHGTYPE boundchgtype /**< bound change type: branching decision or infered bound change */
419 )
420 {
421 assert(var != NULL);
422 assert(SCIPsetIsLT(set, oldbound, newbound));
423 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, oldbound));
424 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
425 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 0.0));
426 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 1.0));
427 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
428 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
429 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
430
431 SCIPsetDebugMsg(set, "adding lower bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
432 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
433 infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
434 oldbound, newbound);
435
436 SCIP_CALL( varEnsureLbchginfosSize(var, blkmem, set, var->nlbchginfos+1) );
437 var->lbchginfos[var->nlbchginfos].oldbound = oldbound;
438 var->lbchginfos[var->nlbchginfos].newbound = newbound;
439 var->lbchginfos[var->nlbchginfos].var = var;
440 var->lbchginfos[var->nlbchginfos].bdchgidx.depth = depth;
441 var->lbchginfos[var->nlbchginfos].bdchgidx.pos = pos;
442 var->lbchginfos[var->nlbchginfos].pos = var->nlbchginfos; /*lint !e732*/
443 var->lbchginfos[var->nlbchginfos].boundchgtype = boundchgtype; /*lint !e641*/
444 var->lbchginfos[var->nlbchginfos].boundtype = SCIP_BOUNDTYPE_LOWER; /*lint !e641*/
445 var->lbchginfos[var->nlbchginfos].redundant = FALSE;
446 var->lbchginfos[var->nlbchginfos].inferboundtype = inferboundtype; /*lint !e641*/
447 var->lbchginfos[var->nlbchginfos].inferencedata.var = infervar;
448 var->lbchginfos[var->nlbchginfos].inferencedata.info = inferinfo;
449
450 /**@note The "pos" data member of the bound change info has a size of 27 bits */
451 assert(var->nlbchginfos < 1 << 27);
452
453 switch( boundchgtype )
454 {
455 case SCIP_BOUNDCHGTYPE_BRANCHING:
456 break;
457 case SCIP_BOUNDCHGTYPE_CONSINFER:
458 assert(infercons != NULL);
459 var->lbchginfos[var->nlbchginfos].inferencedata.reason.cons = infercons;
460 break;
461 case SCIP_BOUNDCHGTYPE_PROPINFER:
462 var->lbchginfos[var->nlbchginfos].inferencedata.reason.prop = inferprop;
463 break;
464 default:
465 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
466 return SCIP_INVALIDDATA;
467 }
468
469 var->nlbchginfos++;
470
471 assert(var->nlbchginfos < 2
472 || SCIPbdchgidxIsEarlier(&var->lbchginfos[var->nlbchginfos-2].bdchgidx,
473 &var->lbchginfos[var->nlbchginfos-1].bdchgidx));
474
475 return SCIP_OKAY;
476 }
477
478 /** adds domain change info to the variable's upper bound change info array */
479 static
varAddUbchginfo(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real oldbound,SCIP_Real newbound,int depth,int pos,SCIP_VAR * infervar,SCIP_CONS * infercons,SCIP_PROP * inferprop,int inferinfo,SCIP_BOUNDTYPE inferboundtype,SCIP_BOUNDCHGTYPE boundchgtype)480 SCIP_RETCODE varAddUbchginfo(
481 SCIP_VAR* var, /**< problem variable */
482 BMS_BLKMEM* blkmem, /**< block memory */
483 SCIP_SET* set, /**< global SCIP settings */
484 SCIP_Real oldbound, /**< old value for bound */
485 SCIP_Real newbound, /**< new value for bound */
486 int depth, /**< depth in the tree, where the bound change takes place */
487 int pos, /**< position of the bound change in its bound change array */
488 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself) */
489 SCIP_CONS* infercons, /**< constraint that infered this bound change, or NULL */
490 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
491 int inferinfo, /**< user information for inference to help resolving the conflict */
492 SCIP_BOUNDTYPE inferboundtype, /**< type of bound for inference var: lower or upper bound */
493 SCIP_BOUNDCHGTYPE boundchgtype /**< bound change type: branching decision or infered bound change */
494 )
495 {
496 assert(var != NULL);
497 assert(SCIPsetIsGT(set, oldbound, newbound));
498 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, oldbound));
499 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
500 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 1.0));
501 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 0.0));
502 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
503 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
504 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
505
506 SCIPsetDebugMsg(set, "adding upper bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
507 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
508 infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
509 oldbound, newbound);
510
511 SCIP_CALL( varEnsureUbchginfosSize(var, blkmem, set, var->nubchginfos+1) );
512 var->ubchginfos[var->nubchginfos].oldbound = oldbound;
513 var->ubchginfos[var->nubchginfos].newbound = newbound;
514 var->ubchginfos[var->nubchginfos].var = var;
515 var->ubchginfos[var->nubchginfos].bdchgidx.depth = depth;
516 var->ubchginfos[var->nubchginfos].bdchgidx.pos = pos;
517 var->ubchginfos[var->nubchginfos].pos = var->nubchginfos; /*lint !e732*/
518 var->ubchginfos[var->nubchginfos].boundchgtype = boundchgtype; /*lint !e641*/
519 var->ubchginfos[var->nubchginfos].boundtype = SCIP_BOUNDTYPE_UPPER; /*lint !e641*/
520 var->ubchginfos[var->nubchginfos].redundant = FALSE;
521 var->ubchginfos[var->nubchginfos].inferboundtype = inferboundtype; /*lint !e641*/
522 var->ubchginfos[var->nubchginfos].inferencedata.var = infervar;
523 var->ubchginfos[var->nubchginfos].inferencedata.info = inferinfo;
524
525 /**@note The "pos" data member of the bound change info has a size of 27 bits */
526 assert(var->nubchginfos < 1 << 27);
527
528 switch( boundchgtype )
529 {
530 case SCIP_BOUNDCHGTYPE_BRANCHING:
531 break;
532 case SCIP_BOUNDCHGTYPE_CONSINFER:
533 assert(infercons != NULL);
534 var->ubchginfos[var->nubchginfos].inferencedata.reason.cons = infercons;
535 break;
536 case SCIP_BOUNDCHGTYPE_PROPINFER:
537 var->ubchginfos[var->nubchginfos].inferencedata.reason.prop = inferprop;
538 break;
539 default:
540 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
541 return SCIP_INVALIDDATA;
542 }
543
544 var->nubchginfos++;
545
546 assert(var->nubchginfos < 2
547 || SCIPbdchgidxIsEarlier(&var->ubchginfos[var->nubchginfos-2].bdchgidx,
548 &var->ubchginfos[var->nubchginfos-1].bdchgidx));
549
550 return SCIP_OKAY;
551 }
552
553 /** applies single bound change */
SCIPboundchgApply(SCIP_BOUNDCHG * boundchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,int depth,int pos,SCIP_Bool * cutoff)554 SCIP_RETCODE SCIPboundchgApply(
555 SCIP_BOUNDCHG* boundchg, /**< bound change to apply */
556 BMS_BLKMEM* blkmem, /**< block memory */
557 SCIP_SET* set, /**< global SCIP settings */
558 SCIP_STAT* stat, /**< problem statistics */
559 SCIP_LP* lp, /**< current LP data */
560 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
561 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
562 int depth, /**< depth in the tree, where the bound change takes place */
563 int pos, /**< position of the bound change in its bound change array */
564 SCIP_Bool* cutoff /**< pointer to store whether an infeasible bound change was detected */
565 )
566 {
567 SCIP_VAR* var;
568
569 assert(boundchg != NULL);
570 assert(stat != NULL);
571 assert(depth > 0);
572 assert(pos >= 0);
573 assert(cutoff != NULL);
574
575 *cutoff = FALSE;
576
577 /* ignore redundant bound changes */
578 if( boundchg->redundant )
579 return SCIP_OKAY;
580
581 var = boundchg->var;
582 assert(var != NULL);
583 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
584 assert(!SCIPvarIsIntegral(var) || SCIPsetIsIntegral(set, boundchg->newbound));
585
586 /* apply bound change */
587 switch( boundchg->boundtype )
588 {
589 case SCIP_BOUNDTYPE_LOWER:
590 /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
591 if( SCIPsetIsGT(set, boundchg->newbound, var->locdom.lb) )
592 {
593 if( SCIPsetIsLE(set, boundchg->newbound, var->locdom.ub) )
594 {
595 /* add the bound change info to the variable's bound change info array */
596 switch( boundchg->boundchgtype )
597 {
598 case SCIP_BOUNDCHGTYPE_BRANCHING:
599 SCIPsetDebugMsg(set, " -> branching: new lower bound of <%s>[%g,%g]: %g\n",
600 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
601 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
602 NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_LOWER, SCIP_BOUNDCHGTYPE_BRANCHING) );
603 stat->lastbranchvar = var;
604 stat->lastbranchdir = SCIP_BRANCHDIR_UPWARDS;
605 stat->lastbranchvalue = boundchg->newbound;
606 break;
607
608 case SCIP_BOUNDCHGTYPE_CONSINFER:
609 assert(boundchg->data.inferencedata.reason.cons != NULL);
610 SCIPsetDebugMsg(set, " -> constraint <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
611 SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
612 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
613 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
614 boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
615 boundchg->data.inferencedata.info,
616 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
617 break;
618
619 case SCIP_BOUNDCHGTYPE_PROPINFER:
620 SCIPsetDebugMsg(set, " -> propagator <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
621 boundchg->data.inferencedata.reason.prop != NULL
622 ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
623 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
624 SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
625 boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
626 boundchg->data.inferencedata.info,
627 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
628 break;
629
630 default:
631 SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
632 return SCIP_INVALIDDATA;
633 }
634
635 /* change local bound of variable */
636 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
637 }
638 else
639 {
640 SCIPsetDebugMsg(set, " -> cutoff: new lower bound of <%s>[%g,%g]: %g\n",
641 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
642 *cutoff = TRUE;
643 boundchg->redundant = TRUE; /* bound change has not entered the lbchginfos array of the variable! */
644 }
645 }
646 else
647 {
648 /* mark bound change to be inactive */
649 SCIPsetDebugMsg(set, " -> inactive %s: new lower bound of <%s>[%g,%g]: %g\n",
650 (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
651 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
652 boundchg->redundant = TRUE;
653 }
654 break;
655
656 case SCIP_BOUNDTYPE_UPPER:
657 /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
658 if( SCIPsetIsLT(set, boundchg->newbound, var->locdom.ub) )
659 {
660 if( SCIPsetIsGE(set, boundchg->newbound, var->locdom.lb) )
661 {
662 /* add the bound change info to the variable's bound change info array */
663 switch( boundchg->boundchgtype )
664 {
665 case SCIP_BOUNDCHGTYPE_BRANCHING:
666 SCIPsetDebugMsg(set, " -> branching: new upper bound of <%s>[%g,%g]: %g\n",
667 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
668 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
669 NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_UPPER, SCIP_BOUNDCHGTYPE_BRANCHING) );
670 stat->lastbranchvar = var;
671 stat->lastbranchdir = SCIP_BRANCHDIR_DOWNWARDS;
672 stat->lastbranchvalue = boundchg->newbound;
673 break;
674
675 case SCIP_BOUNDCHGTYPE_CONSINFER:
676 assert(boundchg->data.inferencedata.reason.cons != NULL);
677 SCIPsetDebugMsg(set, " -> constraint <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
678 SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
679 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
680 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
681 boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
682 boundchg->data.inferencedata.info,
683 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
684 break;
685
686 case SCIP_BOUNDCHGTYPE_PROPINFER:
687 SCIPsetDebugMsg(set, " -> propagator <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
688 boundchg->data.inferencedata.reason.prop != NULL
689 ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
690 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
691 SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
692 boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
693 boundchg->data.inferencedata.info,
694 (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
695 break;
696
697 default:
698 SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
699 return SCIP_INVALIDDATA;
700 }
701
702 /* change local bound of variable */
703 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
704 }
705 else
706 {
707 SCIPsetDebugMsg(set, " -> cutoff: new upper bound of <%s>[%g,%g]: %g\n",
708 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
709 *cutoff = TRUE;
710 boundchg->redundant = TRUE; /* bound change has not entered the ubchginfos array of the variable! */
711 }
712 }
713 else
714 {
715 /* mark bound change to be inactive */
716 SCIPsetDebugMsg(set, " -> inactive %s: new upper bound of <%s>[%g,%g]: %g\n",
717 (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
718 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
719 boundchg->redundant = TRUE;
720 }
721 break;
722
723 default:
724 SCIPerrorMessage("unknown bound type\n");
725 return SCIP_INVALIDDATA;
726 }
727
728 /* update the branching and inference history */
729 if( !boundchg->applied && !boundchg->redundant )
730 {
731 assert(var == boundchg->var);
732
733 if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
734 {
735 SCIP_CALL( SCIPvarIncNBranchings(var, blkmem, set, stat,
736 (SCIP_BOUNDTYPE)boundchg->boundtype == SCIP_BOUNDTYPE_LOWER
737 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS, boundchg->newbound, depth) );
738 }
739 else if( stat->lastbranchvar != NULL )
740 {
741 /**@todo if last branching variable is unknown, retrieve it from the nodes' boundchg arrays */
742 SCIP_CALL( SCIPvarIncInferenceSum(stat->lastbranchvar, blkmem, set, stat, stat->lastbranchdir, stat->lastbranchvalue, 1.0) );
743 }
744 boundchg->applied = TRUE;
745 }
746
747 return SCIP_OKAY;
748 }
749
750 /** undoes single bound change */
SCIPboundchgUndo(SCIP_BOUNDCHG * boundchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue)751 SCIP_RETCODE SCIPboundchgUndo(
752 SCIP_BOUNDCHG* boundchg, /**< bound change to remove */
753 BMS_BLKMEM* blkmem, /**< block memory */
754 SCIP_SET* set, /**< global SCIP settings */
755 SCIP_STAT* stat, /**< problem statistics */
756 SCIP_LP* lp, /**< current LP data */
757 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
758 SCIP_EVENTQUEUE* eventqueue /**< event queue */
759 )
760 {
761 SCIP_VAR* var;
762
763 assert(boundchg != NULL);
764 assert(stat != NULL);
765
766 /* ignore redundant bound changes */
767 if( boundchg->redundant )
768 return SCIP_OKAY;
769
770 var = boundchg->var;
771 assert(var != NULL);
772 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
773
774 /* undo bound change: apply the previous bound change of variable */
775 switch( boundchg->boundtype )
776 {
777 case SCIP_BOUNDTYPE_LOWER:
778 var->nlbchginfos--;
779 assert(var->nlbchginfos >= 0);
780 assert(var->lbchginfos != NULL);
781 assert( SCIPsetIsFeasEQ(set, var->lbchginfos[var->nlbchginfos].newbound, var->locdom.lb) ); /*lint !e777*/
782 assert( SCIPsetIsFeasLE(set, boundchg->newbound, var->locdom.lb) ); /* current lb might be larger to intermediate global bound change */
783
784 SCIPsetDebugMsg(set, "removed lower bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
785 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
786 var->lbchginfos[var->nlbchginfos].bdchgidx.depth, var->lbchginfos[var->nlbchginfos].bdchgidx.pos,
787 var->lbchginfos[var->nlbchginfos].oldbound, var->lbchginfos[var->nlbchginfos].newbound);
788
789 /* reinstall the previous local bound */
790 SCIP_CALL( SCIPvarChgLbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
791 var->lbchginfos[var->nlbchginfos].oldbound) );
792
793 /* in case all bound changes are removed the local bound should match the global bound */
794 assert(var->nlbchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.lb, var->glbdom.lb));
795
796 break;
797
798 case SCIP_BOUNDTYPE_UPPER:
799 var->nubchginfos--;
800 assert(var->nubchginfos >= 0);
801 assert(var->ubchginfos != NULL);
802 assert( SCIPsetIsFeasEQ(set, var->ubchginfos[var->nubchginfos].newbound, var->locdom.ub) ); /*lint !e777*/
803 assert( SCIPsetIsFeasGE(set, boundchg->newbound, var->locdom.ub) ); /* current ub might be smaller to intermediate global bound change */
804
805 SCIPsetDebugMsg(set, "removed upper bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
806 SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
807 var->ubchginfos[var->nubchginfos].bdchgidx.depth, var->ubchginfos[var->nubchginfos].bdchgidx.pos,
808 var->ubchginfos[var->nubchginfos].oldbound, var->ubchginfos[var->nubchginfos].newbound);
809
810 /* reinstall the previous local bound */
811 SCIP_CALL( SCIPvarChgUbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
812 var->ubchginfos[var->nubchginfos].oldbound) );
813
814 /* in case all bound changes are removed the local bound should match the global bound */
815 assert(var->nubchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.ub, var->glbdom.ub));
816
817 break;
818
819 default:
820 SCIPerrorMessage("unknown bound type\n");
821 return SCIP_INVALIDDATA;
822 }
823
824 /* update last branching variable */
825 if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
826 {
827 stat->lastbranchvar = NULL;
828 stat->lastbranchvalue = SCIP_UNKNOWN;
829 }
830
831 return SCIP_OKAY;
832 }
833
834 /** applies single bound change to the global problem by changing the global bound of the corresponding variable */
835 static
boundchgApplyGlobal(SCIP_BOUNDCHG * boundchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * cutoff)836 SCIP_RETCODE boundchgApplyGlobal(
837 SCIP_BOUNDCHG* boundchg, /**< bound change to apply */
838 BMS_BLKMEM* blkmem, /**< block memory */
839 SCIP_SET* set, /**< global SCIP settings */
840 SCIP_STAT* stat, /**< problem statistics */
841 SCIP_LP* lp, /**< current LP data */
842 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
843 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
844 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
845 SCIP_Bool* cutoff /**< pointer to store whether an infeasible bound change was detected */
846 )
847 {
848 SCIP_VAR* var;
849 SCIP_Real newbound;
850 SCIP_BOUNDTYPE boundtype;
851
852 assert(boundchg != NULL);
853 assert(cutoff != NULL);
854
855 *cutoff = FALSE;
856
857 /* ignore redundant bound changes */
858 if( boundchg->redundant )
859 return SCIP_OKAY;
860
861 var = SCIPboundchgGetVar(boundchg);
862 newbound = SCIPboundchgGetNewbound(boundchg);
863 boundtype = SCIPboundchgGetBoundtype(boundchg);
864
865 /* check if the bound change is redundant which can happen due to a (better) global bound change which was performed
866 * after that bound change was applied
867 *
868 * @note a global bound change is not captured by the redundant member of the bound change data structure
869 */
870 if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasLE(set, newbound, SCIPvarGetLbGlobal(var)))
871 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasGE(set, newbound, SCIPvarGetUbGlobal(var))) )
872 {
873 return SCIP_OKAY;
874 }
875
876 SCIPsetDebugMsg(set, "applying global bound change: <%s>[%g,%g] %s %g\n",
877 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var),
878 boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", newbound);
879
880 /* check for cutoff */
881 if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, newbound, SCIPvarGetUbGlobal(var)))
882 || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, newbound, SCIPvarGetLbGlobal(var))) )
883 {
884 *cutoff = TRUE;
885 return SCIP_OKAY;
886 }
887
888 /* apply bound change */
889 SCIP_CALL( SCIPvarChgBdGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound, boundtype) );
890
891 return SCIP_OKAY;
892 }
893
894 /** captures branching and inference data of bound change */
895 static
boundchgCaptureData(SCIP_BOUNDCHG * boundchg)896 SCIP_RETCODE boundchgCaptureData(
897 SCIP_BOUNDCHG* boundchg /**< bound change to remove */
898 )
899 {
900 assert(boundchg != NULL);
901
902 /* capture variable associated with the bound change */
903 assert(boundchg->var != NULL);
904 SCIPvarCapture(boundchg->var);
905
906 switch( boundchg->boundchgtype )
907 {
908 case SCIP_BOUNDCHGTYPE_BRANCHING:
909 case SCIP_BOUNDCHGTYPE_PROPINFER:
910 break;
911
912 case SCIP_BOUNDCHGTYPE_CONSINFER:
913 assert(boundchg->data.inferencedata.var != NULL);
914 assert(boundchg->data.inferencedata.reason.cons != NULL);
915 SCIPconsCapture(boundchg->data.inferencedata.reason.cons);
916 break;
917
918 default:
919 SCIPerrorMessage("invalid bound change type\n");
920 return SCIP_INVALIDDATA;
921 }
922
923 return SCIP_OKAY;
924 }
925
926 /** releases branching and inference data of bound change */
927 static
boundchgReleaseData(SCIP_BOUNDCHG * boundchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)928 SCIP_RETCODE boundchgReleaseData(
929 SCIP_BOUNDCHG* boundchg, /**< bound change to remove */
930 BMS_BLKMEM* blkmem, /**< block memory */
931 SCIP_SET* set, /**< global SCIP settings */
932 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
933 SCIP_LP* lp /**< current LP data */
934
935 )
936 {
937 assert(boundchg != NULL);
938
939 switch( boundchg->boundchgtype )
940 {
941 case SCIP_BOUNDCHGTYPE_BRANCHING:
942 case SCIP_BOUNDCHGTYPE_PROPINFER:
943 break;
944
945 case SCIP_BOUNDCHGTYPE_CONSINFER:
946 assert(boundchg->data.inferencedata.var != NULL);
947 assert(boundchg->data.inferencedata.reason.cons != NULL);
948 SCIP_CALL( SCIPconsRelease(&boundchg->data.inferencedata.reason.cons, blkmem, set) );
949 break;
950
951 default:
952 SCIPerrorMessage("invalid bound change type\n");
953 return SCIP_INVALIDDATA;
954 }
955
956 /* release variable */
957 assert(boundchg->var != NULL);
958 SCIP_CALL( SCIPvarRelease(&boundchg->var, blkmem, set, eventqueue, lp) );
959
960 return SCIP_OKAY;
961 }
962
963 /** creates empty domain change data with dynamic arrays */
964 static
domchgCreate(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem)965 SCIP_RETCODE domchgCreate(
966 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
967 BMS_BLKMEM* blkmem /**< block memory */
968 )
969 {
970 assert(domchg != NULL);
971 assert(blkmem != NULL);
972
973 SCIP_ALLOC( BMSallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN)) );
974 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
975 (*domchg)->domchgdyn.nboundchgs = 0;
976 (*domchg)->domchgdyn.boundchgs = NULL;
977 (*domchg)->domchgdyn.nholechgs = 0;
978 (*domchg)->domchgdyn.holechgs = NULL;
979 (*domchg)->domchgdyn.boundchgssize = 0;
980 (*domchg)->domchgdyn.holechgssize = 0;
981
982 return SCIP_OKAY;
983 }
984
985 /** frees domain change data */
SCIPdomchgFree(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)986 SCIP_RETCODE SCIPdomchgFree(
987 SCIP_DOMCHG** domchg, /**< pointer to domain change */
988 BMS_BLKMEM* blkmem, /**< block memory */
989 SCIP_SET* set, /**< global SCIP settings */
990 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
991 SCIP_LP* lp /**< current LP data */
992 )
993 {
994 assert(domchg != NULL);
995 assert(blkmem != NULL);
996
997 if( *domchg != NULL )
998 {
999 int i;
1000
1001 /* release variables, branching and inference data associated with the bound changes */
1002 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1003 {
1004 SCIP_CALL( boundchgReleaseData(&(*domchg)->domchgbound.boundchgs[i], blkmem, set, eventqueue, lp) );
1005 }
1006
1007 /* free memory for bound and hole changes */
1008 switch( (*domchg)->domchgdyn.domchgtype )
1009 {
1010 case SCIP_DOMCHGTYPE_BOUND:
1011 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgbound.boundchgs, (*domchg)->domchgbound.nboundchgs);
1012 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND));
1013 break;
1014 case SCIP_DOMCHGTYPE_BOTH:
1015 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.boundchgs, (*domchg)->domchgboth.nboundchgs);
1016 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.holechgs, (*domchg)->domchgboth.nholechgs);
1017 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH));
1018 break;
1019 case SCIP_DOMCHGTYPE_DYNAMIC:
1020 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.boundchgs, (*domchg)->domchgdyn.boundchgssize);
1021 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);
1022 BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN));
1023 break;
1024 default:
1025 SCIPerrorMessage("invalid domain change type\n");
1026 return SCIP_INVALIDDATA;
1027 }
1028 }
1029
1030 return SCIP_OKAY;
1031 }
1032
1033 /** converts a static domain change data into a dynamic one */
1034 static
domchgMakeDynamic(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem)1035 SCIP_RETCODE domchgMakeDynamic(
1036 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
1037 BMS_BLKMEM* blkmem /**< block memory */
1038 )
1039 {
1040 assert(domchg != NULL);
1041 assert(blkmem != NULL);
1042
1043 SCIPdebugMessage("making domain change data %p pointing to %p dynamic\n", (void*)domchg, (void*)*domchg);
1044
1045 if( *domchg == NULL )
1046 {
1047 SCIP_CALL( domchgCreate(domchg, blkmem) );
1048 }
1049 else
1050 {
1051 switch( (*domchg)->domchgdyn.domchgtype )
1052 {
1053 case SCIP_DOMCHGTYPE_BOUND:
1054 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND), sizeof(SCIP_DOMCHGDYN)) );
1055 (*domchg)->domchgdyn.nholechgs = 0;
1056 (*domchg)->domchgdyn.holechgs = NULL;
1057 (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
1058 (*domchg)->domchgdyn.holechgssize = 0;
1059 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
1060 break;
1061 case SCIP_DOMCHGTYPE_BOTH:
1062 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGDYN)) );
1063 (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
1064 (*domchg)->domchgdyn.holechgssize = (*domchg)->domchgdyn.nholechgs;
1065 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
1066 break;
1067 case SCIP_DOMCHGTYPE_DYNAMIC:
1068 break;
1069 default:
1070 SCIPerrorMessage("invalid domain change type\n");
1071 return SCIP_INVALIDDATA;
1072 }
1073 }
1074 #ifndef NDEBUG
1075 {
1076 int i;
1077 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1078 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1079 || EPSISINT((*domchg)->domchgbound.boundchgs[i].newbound, 1e-06));
1080 }
1081 #endif
1082
1083 return SCIP_OKAY;
1084 }
1085
1086 /** converts a dynamic domain change data into a static one, using less memory than for a dynamic one */
SCIPdomchgMakeStatic(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)1087 SCIP_RETCODE SCIPdomchgMakeStatic(
1088 SCIP_DOMCHG** domchg, /**< pointer to domain change data */
1089 BMS_BLKMEM* blkmem, /**< block memory */
1090 SCIP_SET* set, /**< global SCIP settings */
1091 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1092 SCIP_LP* lp /**< current LP data */
1093 )
1094 {
1095 assert(domchg != NULL);
1096 assert(blkmem != NULL);
1097
1098 SCIPsetDebugMsg(set, "making domain change data %p pointing to %p static\n", (void*)domchg, (void*)*domchg);
1099
1100 if( *domchg != NULL )
1101 {
1102 switch( (*domchg)->domchgdyn.domchgtype )
1103 {
1104 case SCIP_DOMCHGTYPE_BOUND:
1105 if( (*domchg)->domchgbound.nboundchgs == 0 )
1106 {
1107 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1108 }
1109 break;
1110 case SCIP_DOMCHGTYPE_BOTH:
1111 if( (*domchg)->domchgboth.nholechgs == 0 )
1112 {
1113 if( (*domchg)->domchgbound.nboundchgs == 0 )
1114 {
1115 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1116 }
1117 else
1118 {
1119 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGBOUND)) );
1120 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
1121 }
1122 }
1123 break;
1124 case SCIP_DOMCHGTYPE_DYNAMIC:
1125 if( (*domchg)->domchgboth.nholechgs == 0 )
1126 {
1127 if( (*domchg)->domchgbound.nboundchgs == 0 )
1128 {
1129 SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
1130 }
1131 else
1132 {
1133 /* shrink dynamic size arrays to their minimal sizes */
1134 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
1135 (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
1136 BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);
1137
1138 /* convert into static domain change */
1139 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOUND)) );
1140 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
1141 }
1142 }
1143 else
1144 {
1145 /* shrink dynamic size arrays to their minimal sizes */
1146 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
1147 (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
1148 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.holechgs, \
1149 (*domchg)->domchgdyn.holechgssize, (*domchg)->domchgdyn.nholechgs) );
1150
1151 /* convert into static domain change */
1152 SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOTH)) );
1153 (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOTH; /*lint !e641*/
1154 }
1155 break;
1156 default:
1157 SCIPerrorMessage("invalid domain change type\n");
1158 return SCIP_INVALIDDATA;
1159 }
1160 #ifndef NDEBUG
1161 if( *domchg != NULL )
1162 {
1163 int i;
1164 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1165 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1166 || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
1167 }
1168 #endif
1169 }
1170
1171 return SCIP_OKAY;
1172 }
1173
1174 /** ensures, that boundchgs array can store at least num entries */
1175 static
domchgEnsureBoundchgsSize(SCIP_DOMCHG * domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)1176 SCIP_RETCODE domchgEnsureBoundchgsSize(
1177 SCIP_DOMCHG* domchg, /**< domain change data structure */
1178 BMS_BLKMEM* blkmem, /**< block memory */
1179 SCIP_SET* set, /**< global SCIP settings */
1180 int num /**< minimum number of entries to store */
1181 )
1182 {
1183 assert(domchg != NULL);
1184 assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1185
1186 if( num > domchg->domchgdyn.boundchgssize )
1187 {
1188 int newsize;
1189
1190 newsize = SCIPsetCalcMemGrowSize(set, num);
1191 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.boundchgs, domchg->domchgdyn.boundchgssize, newsize) );
1192 domchg->domchgdyn.boundchgssize = newsize;
1193 }
1194 assert(num <= domchg->domchgdyn.boundchgssize);
1195
1196 return SCIP_OKAY;
1197 }
1198
1199 /** ensures, that holechgs array can store at least num additional entries */
1200 static
domchgEnsureHolechgsSize(SCIP_DOMCHG * domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)1201 SCIP_RETCODE domchgEnsureHolechgsSize(
1202 SCIP_DOMCHG* domchg, /**< domain change data structure */
1203 BMS_BLKMEM* blkmem, /**< block memory */
1204 SCIP_SET* set, /**< global SCIP settings */
1205 int num /**< minimum number of additional entries to store */
1206 )
1207 {
1208 assert(domchg != NULL);
1209 assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1210
1211 if( num > domchg->domchgdyn.holechgssize )
1212 {
1213 int newsize;
1214
1215 newsize = SCIPsetCalcMemGrowSize(set, num);
1216 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.holechgs, domchg->domchgdyn.holechgssize, newsize) );
1217 domchg->domchgdyn.holechgssize = newsize;
1218 }
1219 assert(num <= domchg->domchgdyn.holechgssize);
1220
1221 return SCIP_OKAY;
1222 }
1223
1224 /** applies domain change */
SCIPdomchgApply(SCIP_DOMCHG * domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,int depth,SCIP_Bool * cutoff)1225 SCIP_RETCODE SCIPdomchgApply(
1226 SCIP_DOMCHG* domchg, /**< domain change to apply */
1227 BMS_BLKMEM* blkmem, /**< block memory */
1228 SCIP_SET* set, /**< global SCIP settings */
1229 SCIP_STAT* stat, /**< problem statistics */
1230 SCIP_LP* lp, /**< current LP data */
1231 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1232 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1233 int depth, /**< depth in the tree, where the domain change takes place */
1234 SCIP_Bool* cutoff /**< pointer to store whether an infeasible domain change was detected */
1235 )
1236 {
1237 int i;
1238
1239 assert(cutoff != NULL);
1240
1241 *cutoff = FALSE;
1242
1243 SCIPsetDebugMsg(set, "applying domain changes at %p in depth %d\n", (void*)domchg, depth);
1244
1245 if( domchg == NULL )
1246 return SCIP_OKAY;
1247
1248 /* apply bound changes */
1249 for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
1250 {
1251 SCIP_CALL( SCIPboundchgApply(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
1252 branchcand, eventqueue, depth, i, cutoff) );
1253 if( *cutoff )
1254 break;
1255 }
1256 SCIPsetDebugMsg(set, " -> %u bound changes (cutoff %u)\n", domchg->domchgbound.nboundchgs, *cutoff);
1257
1258 /* mark all bound changes after a cutoff redundant */
1259 for( ; i < (int)domchg->domchgbound.nboundchgs; ++i )
1260 domchg->domchgbound.boundchgs[i].redundant = TRUE;
1261
1262 /* apply holelist changes */
1263 if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
1264 {
1265 for( i = 0; i < domchg->domchgboth.nholechgs; ++i )
1266 *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].newlist;
1267 SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
1268 }
1269
1270 return SCIP_OKAY;
1271 }
1272
1273 /** undoes domain change */
SCIPdomchgUndo(SCIP_DOMCHG * domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue)1274 SCIP_RETCODE SCIPdomchgUndo(
1275 SCIP_DOMCHG* domchg, /**< domain change to remove */
1276 BMS_BLKMEM* blkmem, /**< block memory */
1277 SCIP_SET* set, /**< global SCIP settings */
1278 SCIP_STAT* stat, /**< problem statistics */
1279 SCIP_LP* lp, /**< current LP data */
1280 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1281 SCIP_EVENTQUEUE* eventqueue /**< event queue */
1282 )
1283 {
1284 int i;
1285
1286 SCIPsetDebugMsg(set, "undoing domain changes at %p\n", (void*)domchg);
1287 if( domchg == NULL )
1288 return SCIP_OKAY;
1289
1290 /* undo holelist changes */
1291 if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
1292 {
1293 for( i = domchg->domchgboth.nholechgs-1; i >= 0; --i )
1294 *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].oldlist;
1295 SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
1296 }
1297
1298 /* undo bound changes */
1299 for( i = domchg->domchgbound.nboundchgs-1; i >= 0; --i )
1300 {
1301 SCIP_CALL( SCIPboundchgUndo(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp, branchcand, eventqueue) );
1302 }
1303 SCIPsetDebugMsg(set, " -> %u bound changes\n", domchg->domchgbound.nboundchgs);
1304
1305 return SCIP_OKAY;
1306 }
1307
1308 /** applies domain change to the global problem */
SCIPdomchgApplyGlobal(SCIP_DOMCHG * domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool * cutoff)1309 SCIP_RETCODE SCIPdomchgApplyGlobal(
1310 SCIP_DOMCHG* domchg, /**< domain change to apply */
1311 BMS_BLKMEM* blkmem, /**< block memory */
1312 SCIP_SET* set, /**< global SCIP settings */
1313 SCIP_STAT* stat, /**< problem statistics */
1314 SCIP_LP* lp, /**< current LP data */
1315 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
1316 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
1317 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
1318 SCIP_Bool* cutoff /**< pointer to store whether an infeasible domain change was detected */
1319 )
1320 {
1321 int i;
1322
1323 assert(cutoff != NULL);
1324
1325 *cutoff = FALSE;
1326
1327 if( domchg == NULL )
1328 return SCIP_OKAY;
1329
1330 SCIPsetDebugMsg(set, "applying domain changes at %p to the global problem\n", (void*)domchg);
1331
1332 /* apply bound changes */
1333 for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
1334 {
1335 SCIP_CALL( boundchgApplyGlobal(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
1336 branchcand, eventqueue, cliquetable, cutoff) );
1337 if( *cutoff )
1338 break;
1339 }
1340 SCIPsetDebugMsg(set, " -> %u global bound changes\n", domchg->domchgbound.nboundchgs);
1341
1342 /**@todo globally apply holelist changes - how can this be done without confusing pointer updates? */
1343
1344 return SCIP_OKAY;
1345 }
1346
1347 /** adds bound change to domain changes */
SCIPdomchgAddBoundchg(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_VAR * var,SCIP_Real newbound,SCIP_BOUNDTYPE boundtype,SCIP_BOUNDCHGTYPE boundchgtype,SCIP_Real lpsolval,SCIP_VAR * infervar,SCIP_CONS * infercons,SCIP_PROP * inferprop,int inferinfo,SCIP_BOUNDTYPE inferboundtype)1348 SCIP_RETCODE SCIPdomchgAddBoundchg(
1349 SCIP_DOMCHG** domchg, /**< pointer to domain change data structure */
1350 BMS_BLKMEM* blkmem, /**< block memory */
1351 SCIP_SET* set, /**< global SCIP settings */
1352 SCIP_VAR* var, /**< variable to change the bounds for */
1353 SCIP_Real newbound, /**< new value for bound */
1354 SCIP_BOUNDTYPE boundtype, /**< type of bound for var: lower or upper bound */
1355 SCIP_BOUNDCHGTYPE boundchgtype, /**< type of bound change: branching decision or inference */
1356 SCIP_Real lpsolval, /**< solval of variable in last LP on path to node, or SCIP_INVALID if unknown */
1357 SCIP_VAR* infervar, /**< variable that was changed (parent of var, or var itself), or NULL */
1358 SCIP_CONS* infercons, /**< constraint that deduced the bound change, or NULL */
1359 SCIP_PROP* inferprop, /**< propagator that deduced the bound change, or NULL */
1360 int inferinfo, /**< user information for inference to help resolving the conflict */
1361 SCIP_BOUNDTYPE inferboundtype /**< type of bound for inference var: lower or upper bound */
1362 )
1363 {
1364 SCIP_BOUNDCHG* boundchg;
1365
1366 assert(domchg != NULL);
1367 assert(var != NULL);
1368 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
1369 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
1370 assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, boundtype == SCIP_BOUNDTYPE_LOWER ? 1.0 : 0.0));
1371 assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
1372 assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
1373 assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);
1374
1375 SCIPsetDebugMsg(set, "adding %s bound change <%s: %g> of variable <%s> to domain change at %p pointing to %p\n",
1376 boundtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper", boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
1377 newbound, var->name, (void*)domchg, (void*)*domchg);
1378
1379 /* if domain change data doesn't exist, create it;
1380 * if domain change is static, convert it into dynamic change
1381 */
1382 if( *domchg == NULL )
1383 {
1384 SCIP_CALL( domchgCreate(domchg, blkmem) );
1385 }
1386 else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
1387 {
1388 SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
1389 }
1390 assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1391
1392 /* get memory for additional bound change */
1393 SCIP_CALL( domchgEnsureBoundchgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nboundchgs+1) );
1394
1395 /* fill in the bound change data */
1396 boundchg = &(*domchg)->domchgdyn.boundchgs[(*domchg)->domchgdyn.nboundchgs];
1397 boundchg->var = var;
1398 switch( boundchgtype )
1399 {
1400 case SCIP_BOUNDCHGTYPE_BRANCHING:
1401 boundchg->data.branchingdata.lpsolval = lpsolval;
1402 break;
1403 case SCIP_BOUNDCHGTYPE_CONSINFER:
1404 assert(infercons != NULL);
1405 boundchg->data.inferencedata.var = infervar;
1406 boundchg->data.inferencedata.reason.cons = infercons;
1407 boundchg->data.inferencedata.info = inferinfo;
1408 break;
1409 case SCIP_BOUNDCHGTYPE_PROPINFER:
1410 boundchg->data.inferencedata.var = infervar;
1411 boundchg->data.inferencedata.reason.prop = inferprop;
1412 boundchg->data.inferencedata.info = inferinfo;
1413 break;
1414 default:
1415 SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
1416 return SCIP_INVALIDDATA;
1417 }
1418
1419 boundchg->newbound = newbound;
1420 boundchg->boundchgtype = boundchgtype; /*lint !e641*/
1421 boundchg->boundtype = boundtype; /*lint !e641*/
1422 boundchg->inferboundtype = inferboundtype; /*lint !e641*/
1423 boundchg->applied = FALSE;
1424 boundchg->redundant = FALSE;
1425 (*domchg)->domchgdyn.nboundchgs++;
1426
1427 /* capture branching and inference data associated with the bound changes */
1428 SCIP_CALL( boundchgCaptureData(boundchg) );
1429
1430 #ifdef SCIP_DISABLED_CODE /* expensive debug check */
1431 #ifdef SCIP_MORE_DEBUG
1432 {
1433 int i;
1434 for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
1435 assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
1436 || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
1437 }
1438 #endif
1439 #endif
1440
1441 return SCIP_OKAY;
1442 }
1443
1444 /** adds hole change to domain changes */
SCIPdomchgAddHolechg(SCIP_DOMCHG ** domchg,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_HOLELIST ** ptr,SCIP_HOLELIST * newlist,SCIP_HOLELIST * oldlist)1445 SCIP_RETCODE SCIPdomchgAddHolechg(
1446 SCIP_DOMCHG** domchg, /**< pointer to domain change data structure */
1447 BMS_BLKMEM* blkmem, /**< block memory */
1448 SCIP_SET* set, /**< global SCIP settings */
1449 SCIP_HOLELIST** ptr, /**< changed list pointer */
1450 SCIP_HOLELIST* newlist, /**< new value of list pointer */
1451 SCIP_HOLELIST* oldlist /**< old value of list pointer */
1452 )
1453 {
1454 SCIP_HOLECHG* holechg;
1455
1456 assert(domchg != NULL);
1457 assert(ptr != NULL);
1458
1459 /* if domain change data doesn't exist, create it;
1460 * if domain change is static, convert it into dynamic change
1461 */
1462 if( *domchg == NULL )
1463 {
1464 SCIP_CALL( domchgCreate(domchg, blkmem) );
1465 }
1466 else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
1467 {
1468 SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
1469 }
1470 assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/
1471
1472 /* get memory for additional hole change */
1473 SCIP_CALL( domchgEnsureHolechgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nholechgs+1) );
1474
1475 /* fill in the hole change data */
1476 holechg = &(*domchg)->domchgdyn.holechgs[(*domchg)->domchgdyn.nholechgs];
1477 holechg->ptr = ptr;
1478 holechg->newlist = newlist;
1479 holechg->oldlist = oldlist;
1480 (*domchg)->domchgdyn.nholechgs++;
1481
1482 return SCIP_OKAY;
1483 }
1484
1485
1486
1487
1488 /*
1489 * methods for variables
1490 */
1491
1492 /** returns adjusted lower bound value, which is rounded for integral variable types */
1493 static
adjustedLb(SCIP_SET * set,SCIP_VARTYPE vartype,SCIP_Real lb)1494 SCIP_Real adjustedLb(
1495 SCIP_SET* set, /**< global SCIP settings */
1496 SCIP_VARTYPE vartype, /**< type of variable */
1497 SCIP_Real lb /**< lower bound to adjust */
1498 )
1499 {
1500 if( lb < 0 && SCIPsetIsInfinity(set, -lb) )
1501 return -SCIPsetInfinity(set);
1502 else if( lb > 0 && SCIPsetIsInfinity(set, lb) )
1503 return SCIPsetInfinity(set);
1504 else if( vartype != SCIP_VARTYPE_CONTINUOUS )
1505 return SCIPsetFeasCeil(set, lb);
1506 else if( SCIPsetIsZero(set, lb) )
1507 return 0.0;
1508 else
1509 return lb;
1510 }
1511
1512 /** returns adjusted upper bound value, which is rounded for integral variable types */
1513 static
adjustedUb(SCIP_SET * set,SCIP_VARTYPE vartype,SCIP_Real ub)1514 SCIP_Real adjustedUb(
1515 SCIP_SET* set, /**< global SCIP settings */
1516 SCIP_VARTYPE vartype, /**< type of variable */
1517 SCIP_Real ub /**< upper bound to adjust */
1518 )
1519 {
1520 if( ub > 0 && SCIPsetIsInfinity(set, ub) )
1521 return SCIPsetInfinity(set);
1522 else if( ub < 0 && SCIPsetIsInfinity(set, -ub) )
1523 return -SCIPsetInfinity(set);
1524 else if( vartype != SCIP_VARTYPE_CONTINUOUS )
1525 return SCIPsetFeasFloor(set, ub);
1526 else if( SCIPsetIsZero(set, ub) )
1527 return 0.0;
1528 else
1529 return ub;
1530 }
1531
1532 /** removes (redundant) cliques, implications and variable bounds of variable from all other variables' implications and variable
1533 * bounds arrays, and optionally removes them also from the variable itself
1534 */
SCIPvarRemoveCliquesImplicsVbs(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_CLIQUETABLE * cliquetable,SCIP_SET * set,SCIP_Bool irrelevantvar,SCIP_Bool onlyredundant,SCIP_Bool removefromvar)1535 SCIP_RETCODE SCIPvarRemoveCliquesImplicsVbs(
1536 SCIP_VAR* var, /**< problem variable */
1537 BMS_BLKMEM* blkmem, /**< block memory */
1538 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
1539 SCIP_SET* set, /**< global SCIP settings */
1540 SCIP_Bool irrelevantvar, /**< has the variable become irrelevant? */
1541 SCIP_Bool onlyredundant, /**< should only the redundant implications and variable bounds be removed? */
1542 SCIP_Bool removefromvar /**< should the implications and variable bounds be removed from the var itself? */
1543 )
1544 {
1545 SCIP_Real lb;
1546 SCIP_Real ub;
1547
1548 assert(var != NULL);
1549 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
1550 assert(SCIPvarIsActive(var) || SCIPvarGetType(var) != SCIP_VARTYPE_BINARY);
1551
1552 lb = SCIPvarGetLbGlobal(var);
1553 ub = SCIPvarGetUbGlobal(var);
1554
1555 SCIPsetDebugMsg(set, "removing %s implications and vbounds of %s<%s>[%g,%g]\n",
1556 onlyredundant ? "redundant" : "all", irrelevantvar ? "irrelevant " : "", SCIPvarGetName(var), lb, ub);
1557
1558 /* remove implications of (fixed) binary variable */
1559 if( var->implics != NULL && (!onlyredundant || lb > 0.5 || ub < 0.5) )
1560 {
1561 SCIP_Bool varfixing;
1562
1563 assert(SCIPvarIsBinary(var));
1564
1565 varfixing = FALSE;
1566 do
1567 {
1568 SCIP_VAR** implvars;
1569 SCIP_BOUNDTYPE* impltypes;
1570 int nimpls;
1571 int i;
1572
1573 nimpls = SCIPimplicsGetNImpls(var->implics, varfixing);
1574 implvars = SCIPimplicsGetVars(var->implics, varfixing);
1575 impltypes = SCIPimplicsGetTypes(var->implics, varfixing);
1576
1577 for( i = 0; i < nimpls; i++ )
1578 {
1579 SCIP_VAR* implvar;
1580 SCIP_BOUNDTYPE impltype;
1581
1582 implvar = implvars[i];
1583 impltype = impltypes[i];
1584 assert(implvar != var);
1585
1586 /* remove for all implications z == 0 / 1 ==> x <= p / x >= p (x not binary)
1587 * the following variable bound from x's variable bounds
1588 * x <= b*z+d (z in vubs of x) , for z == 0 / 1 ==> x <= p
1589 * x >= b*z+d (z in vlbs of x) , for z == 0 / 1 ==> x >= p
1590 */
1591 if( impltype == SCIP_BOUNDTYPE_UPPER )
1592 {
1593 if( implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1594 {
1595 SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u ==> <%s> <= %g\n",
1596 SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
1597 SCIPimplicsGetBounds(var->implics, varfixing)[i]);
1598 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, varfixing) );
1599 implvar->closestvblpcount = -1;
1600 var->closestvblpcount = -1;
1601 }
1602 }
1603 else
1604 {
1605 if( implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1606 {
1607 SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u ==> <%s> >= %g\n",
1608 SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
1609 SCIPimplicsGetBounds(var->implics, varfixing)[i]);
1610 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, !varfixing) );
1611 implvar->closestvblpcount = -1;
1612 var->closestvblpcount = -1;
1613 }
1614 }
1615 }
1616 varfixing = !varfixing;
1617 }
1618 while( varfixing == TRUE );
1619
1620 if( removefromvar )
1621 {
1622 /* free the implications data structures */
1623 SCIPimplicsFree(&var->implics, blkmem);
1624 }
1625 }
1626
1627 /* remove the (redundant) variable lower bounds */
1628 if( var->vlbs != NULL )
1629 {
1630 SCIP_VAR** vars;
1631 SCIP_Real* coefs;
1632 SCIP_Real* constants;
1633 int nvbds;
1634 int newnvbds;
1635 int i;
1636
1637 nvbds = SCIPvboundsGetNVbds(var->vlbs);
1638 vars = SCIPvboundsGetVars(var->vlbs);
1639 coefs = SCIPvboundsGetCoefs(var->vlbs);
1640 constants = SCIPvboundsGetConstants(var->vlbs);
1641
1642 /* remove for all variable bounds x >= b*z+d the following implication from z's implications
1643 * z == ub ==> x >= b*ub + d , if b > 0
1644 * z == lb ==> x >= b*lb + d , if b < 0
1645 */
1646 newnvbds = 0;
1647 for( i = 0; i < nvbds; i++ )
1648 {
1649 SCIP_VAR* implvar;
1650 SCIP_Real coef;
1651
1652 assert(newnvbds <= i);
1653
1654 implvar = vars[i];
1655 assert(implvar != NULL);
1656
1657 coef = coefs[i];
1658 assert(!SCIPsetIsZero(set, coef));
1659
1660 /* check, if we want to remove the variable bound */
1661 if( onlyredundant )
1662 {
1663 SCIP_Real vbound;
1664
1665 vbound = MAX(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i]; /*lint !e666*/
1666 if( SCIPsetIsFeasGT(set, vbound, lb) )
1667 {
1668 /* the variable bound is not redundant: keep it */
1669 if( removefromvar )
1670 {
1671 if( newnvbds < i )
1672 {
1673 vars[newnvbds] = implvar;
1674 coefs[newnvbds] = coef;
1675 constants[newnvbds] = constants[i];
1676 }
1677 newnvbds++;
1678 }
1679 continue;
1680 }
1681 }
1682
1683 /* remove the corresponding implication */
1684 if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
1685 {
1686 SCIPsetDebugMsg(set, "deleting implication: <%s> == %d ==> <%s> >= %g\n",
1687 SCIPvarGetName(implvar), (coef > 0.0), SCIPvarGetName(var), MAX(coef, 0.0) + constants[i]);
1688 SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef > 0.0), var, SCIP_BOUNDTYPE_LOWER) );
1689 }
1690 if( coef > 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1691 {
1692 SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
1693 SCIPvarGetName(implvar), SCIPvarGetName(var));
1694 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, FALSE) );
1695 implvar->closestvblpcount = -1;
1696 var->closestvblpcount = -1;
1697 }
1698 else if( coef < 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1699 {
1700 SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
1701 SCIPvarGetName(implvar), SCIPvarGetName(var));
1702 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, TRUE) );
1703 implvar->closestvblpcount = -1;
1704 var->closestvblpcount = -1;
1705 }
1706 }
1707
1708 if( removefromvar )
1709 {
1710 /* update the number of variable bounds */
1711 SCIPvboundsShrink(&var->vlbs, blkmem, newnvbds);
1712 var->closestvblpcount = -1;
1713 }
1714 }
1715
1716 /**@todo in general, variable bounds like x >= b*z + d corresponding to an implication like z = ub ==> x >= b*ub + d
1717 * might be missing because we only add variable bounds with reasonably small value of b. thus, we currently
1718 * cannot remove such variables x from z's implications.
1719 */
1720
1721 /* remove the (redundant) variable upper bounds */
1722 if( var->vubs != NULL )
1723 {
1724 SCIP_VAR** vars;
1725 SCIP_Real* coefs;
1726 SCIP_Real* constants;
1727 int nvbds;
1728 int newnvbds;
1729 int i;
1730
1731 nvbds = SCIPvboundsGetNVbds(var->vubs);
1732 vars = SCIPvboundsGetVars(var->vubs);
1733 coefs = SCIPvboundsGetCoefs(var->vubs);
1734 constants = SCIPvboundsGetConstants(var->vubs);
1735
1736 /* remove for all variable bounds x <= b*z+d the following implication from z's implications
1737 * z == lb ==> x <= b*lb + d , if b > 0
1738 * z == ub ==> x <= b*ub + d , if b < 0
1739 */
1740 newnvbds = 0;
1741 for( i = 0; i < nvbds; i++ )
1742 {
1743 SCIP_VAR* implvar;
1744 SCIP_Real coef;
1745
1746 assert(newnvbds <= i);
1747
1748 implvar = vars[i];
1749 assert(implvar != NULL);
1750
1751 coef = coefs[i];
1752 assert(!SCIPsetIsZero(set, coef));
1753
1754 /* check, if we want to remove the variable bound */
1755 if( onlyredundant )
1756 {
1757 SCIP_Real vbound;
1758
1759 vbound = MIN(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i]; /*lint !e666*/
1760 if( SCIPsetIsFeasLT(set, vbound, ub) )
1761 {
1762 /* the variable bound is not redundant: keep it */
1763 if( removefromvar )
1764 {
1765 if( newnvbds < i )
1766 {
1767 vars[newnvbds] = implvar;
1768 coefs[newnvbds] = coefs[i];
1769 constants[newnvbds] = constants[i];
1770 }
1771 newnvbds++;
1772 }
1773 continue;
1774 }
1775 }
1776
1777 /* remove the corresponding implication */
1778 if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
1779 {
1780 SCIPsetDebugMsg(set, "deleting implication: <%s> == %d ==> <%s> <= %g\n",
1781 SCIPvarGetName(implvar), (coef < 0.0), SCIPvarGetName(var), MIN(coef, 0.0) + constants[i]);
1782 SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef < 0.0), var, SCIP_BOUNDTYPE_UPPER) );
1783 }
1784 if( coef < 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
1785 {
1786 SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
1787 SCIPvarGetName(implvar), SCIPvarGetName(var));
1788 SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, TRUE) );
1789 implvar->closestvblpcount = -1;
1790 var->closestvblpcount = -1;
1791 }
1792 else if( coef > 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
1793 {
1794 SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
1795 SCIPvarGetName(implvar), SCIPvarGetName(var));
1796 SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, FALSE) );
1797 implvar->closestvblpcount = -1;
1798 var->closestvblpcount = -1;
1799 }
1800 }
1801
1802 if( removefromvar )
1803 {
1804 /* update the number of variable bounds */
1805 SCIPvboundsShrink(&var->vubs, blkmem, newnvbds);
1806 var->closestvblpcount = -1;
1807 }
1808 }
1809
1810 /* remove the variable from all cliques */
1811 if( SCIPvarIsBinary(var) )
1812 {
1813 SCIPcliquelistRemoveFromCliques(var->cliquelist, cliquetable, var, irrelevantvar);
1814 SCIPcliquelistFree(&var->cliquelist, blkmem);
1815 }
1816
1817 /**@todo variable bounds like x <= b*z + d with z general integer are not removed from x's vbd arrays, because
1818 * z has no link (like in the binary case) to x
1819 */
1820
1821 return SCIP_OKAY;
1822 }
1823
1824 /** sets the variable name */
1825 static
varSetName(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_STAT * stat,const char * name)1826 SCIP_RETCODE varSetName(
1827 SCIP_VAR* var, /**< problem variable */
1828 BMS_BLKMEM* blkmem, /**< block memory */
1829 SCIP_STAT* stat, /**< problem statistics, or NULL */
1830 const char* name /**< name of variable, or NULL for automatic name creation */
1831 )
1832 {
1833 assert(blkmem != NULL);
1834 assert(var != NULL);
1835
1836 if( name == NULL )
1837 {
1838 char s[SCIP_MAXSTRLEN];
1839
1840 assert(stat != NULL);
1841
1842 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "_var%d_", stat->nvaridx);
1843 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, s, strlen(s)+1) );
1844 }
1845 else
1846 {
1847 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, name, strlen(name)+1) );
1848 }
1849
1850 return SCIP_OKAY;
1851 }
1852
1853
1854 /** creates variable; if variable is of integral type, fractional bounds are automatically rounded; an integer variable
1855 * with bounds zero and one is automatically converted into a binary variable
1856 */
1857 static
varCreate(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,const char * name,SCIP_Real lb,SCIP_Real ub,SCIP_Real obj,SCIP_VARTYPE vartype,SCIP_Bool initial,SCIP_Bool removable,SCIP_DECL_VARCOPY ((* varcopy)),SCIP_DECL_VARDELORIG ((* vardelorig)),SCIP_DECL_VARTRANS ((* vartrans)),SCIP_DECL_VARDELTRANS ((* vardeltrans)),SCIP_VARDATA * vardata)1858 SCIP_RETCODE varCreate(
1859 SCIP_VAR** var, /**< pointer to variable data */
1860 BMS_BLKMEM* blkmem, /**< block memory */
1861 SCIP_SET* set, /**< global SCIP settings */
1862 SCIP_STAT* stat, /**< problem statistics */
1863 const char* name, /**< name of variable, or NULL for automatic name creation */
1864 SCIP_Real lb, /**< lower bound of variable */
1865 SCIP_Real ub, /**< upper bound of variable */
1866 SCIP_Real obj, /**< objective function value */
1867 SCIP_VARTYPE vartype, /**< type of variable */
1868 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
1869 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
1870 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
1871 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
1872 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
1873 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
1874 SCIP_VARDATA* vardata /**< user data for this specific variable */
1875 )
1876 {
1877 int i;
1878
1879 assert(var != NULL);
1880 assert(blkmem != NULL);
1881 assert(stat != NULL);
1882
1883 /* adjust bounds of variable */
1884 lb = adjustedLb(set, vartype, lb);
1885 ub = adjustedUb(set, vartype, ub);
1886
1887 /* convert [0,1]-integers into binary variables and check that binary variables have correct bounds */
1888 if( (SCIPsetIsEQ(set, lb, 0.0) || SCIPsetIsEQ(set, lb, 1.0))
1889 && (SCIPsetIsEQ(set, ub, 0.0) || SCIPsetIsEQ(set, ub, 1.0)) )
1890 {
1891 if( vartype == SCIP_VARTYPE_INTEGER )
1892 vartype = SCIP_VARTYPE_BINARY;
1893 }
1894 else
1895 {
1896 if( vartype == SCIP_VARTYPE_BINARY )
1897 {
1898 SCIPerrorMessage("invalid bounds [%.2g,%.2g] for binary variable <%s>\n", lb, ub, name);
1899 return SCIP_INVALIDDATA;
1900 }
1901 }
1902
1903 assert(vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, lb, 0.0) || SCIPsetIsEQ(set, lb, 1.0));
1904 assert(vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, ub, 0.0) || SCIPsetIsEQ(set, ub, 1.0));
1905
1906 SCIP_ALLOC( BMSallocBlockMemory(blkmem, var) );
1907
1908 /* set variable's name */
1909 SCIP_CALL( varSetName(*var, blkmem, stat, name) );
1910
1911 #ifndef NDEBUG
1912 (*var)->scip = set->scip;
1913 #endif
1914 (*var)->obj = obj;
1915 (*var)->unchangedobj = obj;
1916 (*var)->branchfactor = 1.0;
1917 (*var)->rootsol = 0.0;
1918 (*var)->bestrootsol = 0.0;
1919 (*var)->bestrootredcost = 0.0;
1920 (*var)->bestrootlpobjval = SCIP_INVALID;
1921 (*var)->relaxsol = 0.0;
1922 (*var)->nlpsol = 0.0;
1923 (*var)->primsolavg = 0.5 * (lb + ub);
1924 (*var)->conflictlb = SCIP_REAL_MIN;
1925 (*var)->conflictub = SCIP_REAL_MAX;
1926 (*var)->conflictrelaxedlb = (*var)->conflictlb;
1927 (*var)->conflictrelaxedub = (*var)->conflictub;
1928 (*var)->lazylb = -SCIPsetInfinity(set);
1929 (*var)->lazyub = SCIPsetInfinity(set);
1930 (*var)->glbdom.holelist = NULL;
1931 (*var)->glbdom.lb = lb;
1932 (*var)->glbdom.ub = ub;
1933 (*var)->locdom.holelist = NULL;
1934 (*var)->locdom.lb = lb;
1935 (*var)->locdom.ub = ub;
1936 (*var)->varcopy = varcopy;
1937 (*var)->vardelorig = vardelorig;
1938 (*var)->vartrans = vartrans;
1939 (*var)->vardeltrans = vardeltrans;
1940 (*var)->vardata = vardata;
1941 (*var)->parentvars = NULL;
1942 (*var)->negatedvar = NULL;
1943 (*var)->vlbs = NULL;
1944 (*var)->vubs = NULL;
1945 (*var)->implics = NULL;
1946 (*var)->cliquelist = NULL;
1947 (*var)->eventfilter = NULL;
1948 (*var)->lbchginfos = NULL;
1949 (*var)->ubchginfos = NULL;
1950 (*var)->index = stat->nvaridx;
1951 (*var)->probindex = -1;
1952 (*var)->pseudocandindex = -1;
1953 (*var)->eventqueueindexobj = -1;
1954 (*var)->eventqueueindexlb = -1;
1955 (*var)->eventqueueindexub = -1;
1956 (*var)->parentvarssize = 0;
1957 (*var)->nparentvars = 0;
1958 (*var)->nuses = 0;
1959 (*var)->branchpriority = 0;
1960 (*var)->branchdirection = SCIP_BRANCHDIR_AUTO; /*lint !e641*/
1961 (*var)->lbchginfossize = 0;
1962 (*var)->nlbchginfos = 0;
1963 (*var)->ubchginfossize = 0;
1964 (*var)->nubchginfos = 0;
1965 (*var)->conflictlbcount = 0;
1966 (*var)->conflictubcount = 0;
1967 (*var)->closestvlbidx = -1;
1968 (*var)->closestvubidx = -1;
1969 (*var)->closestvblpcount = -1;
1970 (*var)->initial = initial;
1971 (*var)->removable = removable;
1972 (*var)->deleted = FALSE;
1973 (*var)->donotmultaggr = FALSE;
1974 (*var)->vartype = vartype; /*lint !e641*/
1975 (*var)->pseudocostflag = FALSE;
1976 (*var)->eventqueueimpl = FALSE;
1977 (*var)->deletable = FALSE;
1978 (*var)->delglobalstructs = FALSE;
1979 (*var)->relaxationonly = FALSE;
1980
1981 for( i = 0; i < NLOCKTYPES; i++ )
1982 {
1983 (*var)->nlocksdown[i] = 0;
1984 (*var)->nlocksup[i] = 0;
1985 }
1986
1987 stat->nvaridx++;
1988
1989 /* create branching and inference history entries */
1990 SCIP_CALL( SCIPhistoryCreate(&(*var)->history, blkmem) );
1991 SCIP_CALL( SCIPhistoryCreate(&(*var)->historycrun, blkmem) );
1992
1993 /* the value based history is only created on demand */
1994 (*var)->valuehistory = NULL;
1995
1996 return SCIP_OKAY;
1997 }
1998
1999 /** creates and captures an original problem variable; an integer variable with bounds
2000 * zero and one is automatically converted into a binary variable
2001 */
SCIPvarCreateOriginal(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,const char * name,SCIP_Real lb,SCIP_Real ub,SCIP_Real obj,SCIP_VARTYPE vartype,SCIP_Bool initial,SCIP_Bool removable,SCIP_DECL_VARDELORIG ((* vardelorig)),SCIP_DECL_VARTRANS ((* vartrans)),SCIP_DECL_VARDELTRANS ((* vardeltrans)),SCIP_DECL_VARCOPY ((* varcopy)),SCIP_VARDATA * vardata)2002 SCIP_RETCODE SCIPvarCreateOriginal(
2003 SCIP_VAR** var, /**< pointer to variable data */
2004 BMS_BLKMEM* blkmem, /**< block memory */
2005 SCIP_SET* set, /**< global SCIP settings */
2006 SCIP_STAT* stat, /**< problem statistics */
2007 const char* name, /**< name of variable, or NULL for automatic name creation */
2008 SCIP_Real lb, /**< lower bound of variable */
2009 SCIP_Real ub, /**< upper bound of variable */
2010 SCIP_Real obj, /**< objective function value */
2011 SCIP_VARTYPE vartype, /**< type of variable */
2012 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2013 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2014 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
2015 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
2016 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
2017 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2018 SCIP_VARDATA* vardata /**< user data for this specific variable */
2019 )
2020 {
2021 assert(var != NULL);
2022 assert(blkmem != NULL);
2023 assert(stat != NULL);
2024
2025 /* create variable */
2026 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2027 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2028
2029 /* set variable status and data */
2030 (*var)->varstatus = SCIP_VARSTATUS_ORIGINAL; /*lint !e641*/
2031 (*var)->data.original.origdom.holelist = NULL;
2032 (*var)->data.original.origdom.lb = lb;
2033 (*var)->data.original.origdom.ub = ub;
2034 (*var)->data.original.transvar = NULL;
2035
2036 /* capture variable */
2037 SCIPvarCapture(*var);
2038
2039 return SCIP_OKAY;
2040 }
2041
2042 /** creates and captures a loose variable belonging to the transformed problem; an integer variable with bounds
2043 * zero and one is automatically converted into a binary variable
2044 */
SCIPvarCreateTransformed(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,const char * name,SCIP_Real lb,SCIP_Real ub,SCIP_Real obj,SCIP_VARTYPE vartype,SCIP_Bool initial,SCIP_Bool removable,SCIP_DECL_VARDELORIG ((* vardelorig)),SCIP_DECL_VARTRANS ((* vartrans)),SCIP_DECL_VARDELTRANS ((* vardeltrans)),SCIP_DECL_VARCOPY ((* varcopy)),SCIP_VARDATA * vardata)2045 SCIP_RETCODE SCIPvarCreateTransformed(
2046 SCIP_VAR** var, /**< pointer to variable data */
2047 BMS_BLKMEM* blkmem, /**< block memory */
2048 SCIP_SET* set, /**< global SCIP settings */
2049 SCIP_STAT* stat, /**< problem statistics */
2050 const char* name, /**< name of variable, or NULL for automatic name creation */
2051 SCIP_Real lb, /**< lower bound of variable */
2052 SCIP_Real ub, /**< upper bound of variable */
2053 SCIP_Real obj, /**< objective function value */
2054 SCIP_VARTYPE vartype, /**< type of variable */
2055 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2056 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2057 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable, or NULL */
2058 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data, or NULL */
2059 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable, or NULL */
2060 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2061 SCIP_VARDATA* vardata /**< user data for this specific variable */
2062 )
2063 {
2064 assert(var != NULL);
2065 assert(blkmem != NULL);
2066
2067 /* create variable */
2068 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2069 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2070
2071 /* create event filter for transformed variable */
2072 SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );
2073
2074 /* set variable status and data */
2075 (*var)->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
2076
2077 /* capture variable */
2078 SCIPvarCapture(*var);
2079
2080 return SCIP_OKAY;
2081 }
2082
2083 /** copies and captures a variable from source to target SCIP; an integer variable with bounds zero and one is
2084 * automatically converted into a binary variable; in case the variable data cannot be copied the variable is not
2085 * copied at all
2086 */
SCIPvarCopy(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP * sourcescip,SCIP_VAR * sourcevar,SCIP_HASHMAP * varmap,SCIP_HASHMAP * consmap,SCIP_Bool global)2087 SCIP_RETCODE SCIPvarCopy(
2088 SCIP_VAR** var, /**< pointer to store the target variable */
2089 BMS_BLKMEM* blkmem, /**< block memory */
2090 SCIP_SET* set, /**< global SCIP settings */
2091 SCIP_STAT* stat, /**< problem statistics */
2092 SCIP* sourcescip, /**< source SCIP data structure */
2093 SCIP_VAR* sourcevar, /**< source variable */
2094 SCIP_HASHMAP* varmap, /**< a hashmap to store the mapping of source variables corresponding
2095 * target variables */
2096 SCIP_HASHMAP* consmap, /**< a hashmap to store the mapping of source constraints to the corresponding
2097 * target constraints */
2098 SCIP_Bool global /**< should global or local bounds be used? */
2099 )
2100 {
2101 SCIP_VARDATA* targetdata;
2102 SCIP_RESULT result;
2103 SCIP_Real lb;
2104 SCIP_Real ub;
2105
2106 assert(set != NULL);
2107 assert(blkmem != NULL);
2108 assert(stat != NULL);
2109 assert(sourcescip != NULL);
2110 assert(sourcevar != NULL);
2111 assert(var != NULL);
2112 assert(set->stage == SCIP_STAGE_PROBLEM);
2113 assert(varmap != NULL);
2114 assert(consmap != NULL);
2115
2116 /** @todo copy hole lists */
2117 assert(global || SCIPvarGetHolelistLocal(sourcevar) == NULL);
2118 assert(!global || SCIPvarGetHolelistGlobal(sourcevar) == NULL);
2119
2120 result = SCIP_DIDNOTRUN;
2121 targetdata = NULL;
2122
2123 if( SCIPvarGetStatus(sourcevar) == SCIP_VARSTATUS_ORIGINAL )
2124 {
2125 lb = SCIPvarGetLbOriginal(sourcevar);
2126 ub = SCIPvarGetUbOriginal(sourcevar);
2127 }
2128 else
2129 {
2130 lb = global ? SCIPvarGetLbGlobal(sourcevar) : SCIPvarGetLbLocal(sourcevar);
2131 ub = global ? SCIPvarGetUbGlobal(sourcevar) : SCIPvarGetUbLocal(sourcevar);
2132 }
2133
2134 /* creates and captures the variable in the target SCIP and initialize callback methods and variable data to NULL */
2135 SCIP_CALL( SCIPvarCreateOriginal(var, blkmem, set, stat, SCIPvarGetName(sourcevar),
2136 lb, ub, SCIPvarGetObj(sourcevar), SCIPvarGetType(sourcevar),
2137 SCIPvarIsInitial(sourcevar), SCIPvarIsRemovable(sourcevar),
2138 NULL, NULL, NULL, NULL, NULL) );
2139 assert(*var != NULL);
2140
2141 /* directly copy donotmultaggr flag */
2142 (*var)->donotmultaggr = sourcevar->donotmultaggr;
2143
2144 /* insert variable into mapping between source SCIP and the target SCIP */
2145 assert(!SCIPhashmapExists(varmap, sourcevar));
2146 SCIP_CALL( SCIPhashmapInsert(varmap, sourcevar, *var) );
2147
2148 /* in case there exists variable data and the variable data copy callback, try to copy variable data */
2149 if( sourcevar->vardata != NULL && sourcevar->varcopy != NULL )
2150 {
2151 SCIP_CALL( sourcevar->varcopy(set->scip, sourcescip, sourcevar, sourcevar->vardata,
2152 varmap, consmap, (*var), &targetdata, &result) );
2153
2154 /* evaluate result */
2155 if( result != SCIP_DIDNOTRUN && result != SCIP_SUCCESS )
2156 {
2157 SCIPerrorMessage("variable data copying method returned invalid result <%d>\n", result);
2158 return SCIP_INVALIDRESULT;
2159 }
2160
2161 assert(targetdata == NULL || result == SCIP_SUCCESS);
2162
2163 /* if copying was successful, add the created variable data to the variable as well as all callback methods */
2164 if( result == SCIP_SUCCESS )
2165 {
2166 (*var)->varcopy = sourcevar->varcopy;
2167 (*var)->vardelorig = sourcevar->vardelorig;
2168 (*var)->vartrans = sourcevar->vartrans;
2169 (*var)->vardeltrans = sourcevar->vardeltrans;
2170 (*var)->vardata = targetdata;
2171 }
2172 }
2173
2174 /* we initialize histories of the variables by copying the source variable-information */
2175 if( set->history_allowtransfer )
2176 {
2177 SCIPvarMergeHistories((*var), sourcevar, stat);
2178 }
2179
2180 /* in case the copying was successfully, add the created variable data to the variable as well as all callback
2181 * methods
2182 */
2183 if( result == SCIP_SUCCESS )
2184 {
2185 (*var)->varcopy = sourcevar->varcopy;
2186 (*var)->vardelorig = sourcevar->vardelorig;
2187 (*var)->vartrans = sourcevar->vartrans;
2188 (*var)->vardeltrans = sourcevar->vardeltrans;
2189 (*var)->vardata = targetdata;
2190 }
2191
2192 SCIPsetDebugMsg(set, "created copy <%s> of variable <%s>\n", SCIPvarGetName(*var), SCIPvarGetName(sourcevar));
2193
2194 return SCIP_OKAY;
2195 }
2196
2197 /** parse given string for a SCIP_Real bound */
2198 static
parseValue(SCIP_SET * set,const char * str,SCIP_Real * value,char ** endptr)2199 SCIP_RETCODE parseValue(
2200 SCIP_SET* set, /**< global SCIP settings */
2201 const char* str, /**< string to parse */
2202 SCIP_Real* value, /**< pointer to store the parsed value */
2203 char** endptr /**< pointer to store the final string position if successfully parsed */
2204 )
2205 {
2206 /* first check for infinity value */
2207 if( strncmp(str, "+inf", 4) == 0 )
2208 {
2209 *value = SCIPsetInfinity(set);
2210 (*endptr) = (char*)str + 4;
2211 }
2212 else if( strncmp(str, "-inf", 4) == 0 )
2213 {
2214 *value = -SCIPsetInfinity(set);
2215 (*endptr) = (char*)str + 4;
2216 }
2217 else
2218 {
2219 if( !SCIPstrToRealValue(str, value, endptr) )
2220 {
2221 SCIPerrorMessage("expected value: %s.\n", str);
2222 return SCIP_READERROR;
2223 }
2224 }
2225
2226 return SCIP_OKAY;
2227 }
2228
2229 /** parse the characters as bounds */
2230 static
parseBounds(SCIP_SET * set,const char * str,char * type,SCIP_Real * lb,SCIP_Real * ub,char ** endptr)2231 SCIP_RETCODE parseBounds(
2232 SCIP_SET* set, /**< global SCIP settings */
2233 const char* str, /**< string to parse */
2234 char* type, /**< bound type (global, local, or lazy) */
2235 SCIP_Real* lb, /**< pointer to store the lower bound */
2236 SCIP_Real* ub, /**< pointer to store the upper bound */
2237 char** endptr /**< pointer to store the final string position if successfully parsed (or NULL if an error occured) */
2238 )
2239 {
2240 char token[SCIP_MAXSTRLEN];
2241 char* tmpend;
2242
2243 SCIPsetDebugMsg(set, "parsing bounds: '%s'\n", str);
2244
2245 /* get bound type */
2246 SCIPstrCopySection(str, ' ', ' ', type, SCIP_MAXSTRLEN, endptr);
2247 if ( strncmp(type, "original", 8) != 0 && strncmp(type, "global", 6) != 0 && strncmp(type, "local", 5) != 0 && strncmp(type, "lazy", 4) != 0 )
2248 {
2249 SCIPsetDebugMsg(set, "unkown bound type <%s>\n", type);
2250 *endptr = NULL;
2251 return SCIP_OKAY;
2252 }
2253
2254 SCIPsetDebugMsg(set, "parsed bound type <%s>\n", type);
2255
2256 /* get lower bound */
2257 SCIPstrCopySection(str, '[', ',', token, SCIP_MAXSTRLEN, endptr);
2258 str = *endptr;
2259 SCIP_CALL( parseValue(set, token, lb, &tmpend) );
2260
2261 /* get upper bound */
2262 SCIP_CALL( parseValue(set, str, ub, endptr) );
2263
2264 SCIPsetDebugMsg(set, "parsed bounds: [%g,%g]\n", *lb, *ub);
2265
2266 /* skip end of bounds */
2267 while ( **endptr != '\0' && (**endptr == ']' || **endptr == ',') )
2268 ++(*endptr);
2269
2270 return SCIP_OKAY;
2271 }
2272
2273 /** parses a given string for a variable informations */
2274 static
varParse(SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,const char * str,char * name,SCIP_Real * lb,SCIP_Real * ub,SCIP_Real * obj,SCIP_VARTYPE * vartype,SCIP_Real * lazylb,SCIP_Real * lazyub,SCIP_Bool local,char ** endptr,SCIP_Bool * success)2275 SCIP_RETCODE varParse(
2276 SCIP_SET* set, /**< global SCIP settings */
2277 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2278 const char* str, /**< string to parse */
2279 char* name, /**< pointer to store the variable name */
2280 SCIP_Real* lb, /**< pointer to store the lower bound */
2281 SCIP_Real* ub, /**< pointer to store the upper bound */
2282 SCIP_Real* obj, /**< pointer to store the objective coefficient */
2283 SCIP_VARTYPE* vartype, /**< pointer to store the variable type */
2284 SCIP_Real* lazylb, /**< pointer to store if the lower bound is lazy */
2285 SCIP_Real* lazyub, /**< pointer to store if the upper bound is lazy */
2286 SCIP_Bool local, /**< should the local bound be applied */
2287 char** endptr, /**< pointer to store the final string position if successfully */
2288 SCIP_Bool* success /**< pointer store if the paring process was successful */
2289 )
2290 {
2291 SCIP_Real parsedlb;
2292 SCIP_Real parsedub;
2293 char token[SCIP_MAXSTRLEN];
2294 char* strptr;
2295 int i;
2296
2297 assert(lb != NULL);
2298 assert(ub != NULL);
2299 assert(obj != NULL);
2300 assert(vartype != NULL);
2301 assert(lazylb != NULL);
2302 assert(lazyub != NULL);
2303 assert(success != NULL);
2304
2305 (*success) = TRUE;
2306
2307 /* copy variable type */
2308 SCIPstrCopySection(str, '[', ']', token, SCIP_MAXSTRLEN, endptr);
2309 assert(str != *endptr);
2310 SCIPsetDebugMsg(set, "parsed variable type <%s>\n", token);
2311
2312 /* get variable type */
2313 if( strncmp(token, "binary", 3) == 0 )
2314 (*vartype) = SCIP_VARTYPE_BINARY;
2315 else if( strncmp(token, "integer", 3) == 0 )
2316 (*vartype) = SCIP_VARTYPE_INTEGER;
2317 else if( strncmp(token, "implicit", 3) == 0 )
2318 (*vartype) = SCIP_VARTYPE_IMPLINT;
2319 else if( strncmp(token, "continuous", 3) == 0 )
2320 (*vartype) = SCIP_VARTYPE_CONTINUOUS;
2321 else
2322 {
2323 SCIPmessagePrintWarning(messagehdlr, "unknown variable type\n");
2324 (*success) = FALSE;
2325 return SCIP_OKAY;
2326 }
2327
2328 /* move string pointer behind variable type */
2329 str = *endptr;
2330
2331 /* get variable name */
2332 SCIPstrCopySection(str, '<', '>', name, SCIP_MAXSTRLEN, endptr);
2333 assert(endptr != NULL);
2334 SCIPsetDebugMsg(set, "parsed variable name <%s>\n", name);
2335
2336 /* move string pointer behind variable name */
2337 str = *endptr;
2338
2339 /* cut out objective coefficient */
2340 SCIPstrCopySection(str, '=', ',', token, SCIP_MAXSTRLEN, endptr);
2341
2342 /* move string pointer behind objective coefficient */
2343 str = *endptr;
2344
2345 /* get objective coefficient */
2346 if( !SCIPstrToRealValue(token, obj, endptr) )
2347 {
2348 *endptr = NULL;
2349 return SCIP_READERROR;
2350 }
2351
2352 SCIPsetDebugMsg(set, "parsed objective coefficient <%g>\n", *obj);
2353
2354 /* parse global/original bounds */
2355 SCIP_CALL( parseBounds(set, str, token, lb, ub, endptr) );
2356 if ( *endptr == NULL )
2357 {
2358 SCIPerrorMessage("Expected bound type: %s.\n", token);
2359 return SCIP_READERROR;
2360 }
2361 assert(strncmp(token, "global", 6) == 0 || strncmp(token, "original", 8) == 0);
2362
2363 /* initialize the lazy bound */
2364 *lazylb = -SCIPsetInfinity(set);
2365 *lazyub = SCIPsetInfinity(set);
2366
2367 /* store pointer */
2368 strptr = *endptr;
2369
2370 /* possibly parse optional local and lazy bounds */
2371 for( i = 0; i < 2 && *endptr != NULL && **endptr != '\0'; ++i )
2372 {
2373 /* start after previous bounds */
2374 strptr = *endptr;
2375
2376 /* parse global bounds */
2377 SCIP_CALL( parseBounds(set, strptr, token, &parsedlb, &parsedub, endptr) );
2378
2379 /* stop if parsing of bounds failed */
2380 if( *endptr == NULL )
2381 break;
2382
2383 if( strncmp(token, "local", 5) == 0 && local )
2384 {
2385 *lb = parsedlb;
2386 *ub = parsedub;
2387 }
2388 else if( strncmp(token, "lazy", 4) == 0 )
2389 {
2390 *lazylb = parsedlb;
2391 *lazyub = parsedub;
2392 }
2393 }
2394
2395 /* restore pointer */
2396 if ( *endptr == NULL )
2397 *endptr = strptr;
2398
2399 /* check bounds for binary variables */
2400 if ( (*vartype) == SCIP_VARTYPE_BINARY )
2401 {
2402 if ( SCIPsetIsLT(set, *lb, 0.0) || SCIPsetIsGT(set, *ub, 1.0) )
2403 {
2404 SCIPerrorMessage("Parsed invalid bounds for binary variable <%s>: [%f, %f].\n", name, *lb, *ub);
2405 return SCIP_READERROR;
2406 }
2407 if ( !SCIPsetIsInfinity(set, -(*lazylb)) && !SCIPsetIsInfinity(set, *lazyub) &&
2408 ( SCIPsetIsLT(set, *lazylb, 0.0) || SCIPsetIsGT(set, *lazyub, 1.0) ) )
2409 {
2410 SCIPerrorMessage("Parsed invalid lazy bounds for binary variable <%s>: [%f, %f].\n", name, *lazylb, *lazyub);
2411 return SCIP_READERROR;
2412 }
2413 }
2414
2415 return SCIP_OKAY;
2416 }
2417
2418 /** parses variable information (in cip format) out of a string; if the parsing process was successful an original
2419 * variable is created and captured; if variable is of integral type, fractional bounds are automatically rounded; an
2420 * integer variable with bounds zero and one is automatically converted into a binary variable
2421 */
SCIPvarParseOriginal(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,SCIP_STAT * stat,const char * str,SCIP_Bool initial,SCIP_Bool removable,SCIP_DECL_VARCOPY ((* varcopy)),SCIP_DECL_VARDELORIG ((* vardelorig)),SCIP_DECL_VARTRANS ((* vartrans)),SCIP_DECL_VARDELTRANS ((* vardeltrans)),SCIP_VARDATA * vardata,char ** endptr,SCIP_Bool * success)2422 SCIP_RETCODE SCIPvarParseOriginal(
2423 SCIP_VAR** var, /**< pointer to variable data */
2424 BMS_BLKMEM* blkmem, /**< block memory */
2425 SCIP_SET* set, /**< global SCIP settings */
2426 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2427 SCIP_STAT* stat, /**< problem statistics */
2428 const char* str, /**< string to parse */
2429 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2430 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2431 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2432 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable */
2433 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data */
2434 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable */
2435 SCIP_VARDATA* vardata, /**< user data for this specific variable */
2436 char** endptr, /**< pointer to store the final string position if successfully */
2437 SCIP_Bool* success /**< pointer store if the paring process was successful */
2438 )
2439 {
2440 char name[SCIP_MAXSTRLEN];
2441 SCIP_Real lb;
2442 SCIP_Real ub;
2443 SCIP_Real obj;
2444 SCIP_VARTYPE vartype;
2445 SCIP_Real lazylb;
2446 SCIP_Real lazyub;
2447
2448 assert(var != NULL);
2449 assert(blkmem != NULL);
2450 assert(stat != NULL);
2451 assert(endptr != NULL);
2452 assert(success != NULL);
2453
2454 /* parse string in cip format for variable information */
2455 SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, &vartype, &lazylb, &lazyub, FALSE, endptr, success) );
2456
2457 if( *success ) /*lint !e774*/
2458 {
2459 /* create variable */
2460 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2461 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2462
2463 /* set variable status and data */
2464 (*var)->varstatus = SCIP_VARSTATUS_ORIGINAL; /*lint !e641*/
2465 (*var)->data.original.origdom.holelist = NULL;
2466 (*var)->data.original.origdom.lb = lb;
2467 (*var)->data.original.origdom.ub = ub;
2468 (*var)->data.original.transvar = NULL;
2469
2470 /* set lazy status of variable bounds */
2471 (*var)->lazylb = lazylb;
2472 (*var)->lazyub = lazyub;
2473
2474 /* capture variable */
2475 SCIPvarCapture(*var);
2476 }
2477
2478 return SCIP_OKAY;
2479 }
2480
2481 /** parses variable information (in cip format) out of a string; if the parsing process was successful a loose variable
2482 * belonging to the transformed problem is created and captured; if variable is of integral type, fractional bounds are
2483 * automatically rounded; an integer variable with bounds zero and one is automatically converted into a binary
2484 * variable
2485 */
SCIPvarParseTransformed(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,SCIP_STAT * stat,const char * str,SCIP_Bool initial,SCIP_Bool removable,SCIP_DECL_VARCOPY ((* varcopy)),SCIP_DECL_VARDELORIG ((* vardelorig)),SCIP_DECL_VARTRANS ((* vartrans)),SCIP_DECL_VARDELTRANS ((* vardeltrans)),SCIP_VARDATA * vardata,char ** endptr,SCIP_Bool * success)2486 SCIP_RETCODE SCIPvarParseTransformed(
2487 SCIP_VAR** var, /**< pointer to variable data */
2488 BMS_BLKMEM* blkmem, /**< block memory */
2489 SCIP_SET* set, /**< global SCIP settings */
2490 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2491 SCIP_STAT* stat, /**< problem statistics */
2492 const char* str, /**< string to parse */
2493 SCIP_Bool initial, /**< should var's column be present in the initial root LP? */
2494 SCIP_Bool removable, /**< is var's column removable from the LP (due to aging or cleanup)? */
2495 SCIP_DECL_VARCOPY ((*varcopy)), /**< copies variable data if wanted to subscip, or NULL */
2496 SCIP_DECL_VARDELORIG ((*vardelorig)), /**< frees user data of original variable */
2497 SCIP_DECL_VARTRANS ((*vartrans)), /**< creates transformed user data by transforming original user data */
2498 SCIP_DECL_VARDELTRANS ((*vardeltrans)), /**< frees user data of transformed variable */
2499 SCIP_VARDATA* vardata, /**< user data for this specific variable */
2500 char** endptr, /**< pointer to store the final string position if successfully */
2501 SCIP_Bool* success /**< pointer store if the paring process was successful */
2502 )
2503 {
2504 char name[SCIP_MAXSTRLEN];
2505 SCIP_Real lb;
2506 SCIP_Real ub;
2507 SCIP_Real obj;
2508 SCIP_VARTYPE vartype;
2509 SCIP_Real lazylb;
2510 SCIP_Real lazyub;
2511
2512 assert(var != NULL);
2513 assert(blkmem != NULL);
2514 assert(endptr != NULL);
2515 assert(success != NULL);
2516
2517 /* parse string in cip format for variable information */
2518 SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, &vartype, &lazylb, &lazyub, TRUE, endptr, success) );
2519
2520 if( *success ) /*lint !e774*/
2521 {
2522 /* create variable */
2523 SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, initial, removable,
2524 varcopy, vardelorig, vartrans, vardeltrans, vardata) );
2525
2526 /* create event filter for transformed variable */
2527 SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );
2528
2529 /* set variable status and data */
2530 (*var)->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
2531
2532 /* set lazy status of variable bounds */
2533 (*var)->lazylb = lazylb;
2534 (*var)->lazyub = lazyub;
2535
2536 /* capture variable */
2537 SCIPvarCapture(*var);
2538 }
2539
2540 return SCIP_OKAY;
2541 }
2542
2543 /** ensures, that parentvars array of var can store at least num entries */
2544 static
varEnsureParentvarsSize(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)2545 SCIP_RETCODE varEnsureParentvarsSize(
2546 SCIP_VAR* var, /**< problem variable */
2547 BMS_BLKMEM* blkmem, /**< block memory */
2548 SCIP_SET* set, /**< global SCIP settings */
2549 int num /**< minimum number of entries to store */
2550 )
2551 {
2552 assert(var->nparentvars <= var->parentvarssize);
2553
2554 if( num > var->parentvarssize )
2555 {
2556 int newsize;
2557
2558 newsize = SCIPsetCalcMemGrowSize(set, num);
2559 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->parentvars, var->parentvarssize, newsize) );
2560 var->parentvarssize = newsize;
2561 }
2562 assert(num <= var->parentvarssize);
2563
2564 return SCIP_OKAY;
2565 }
2566
2567 /** adds variable to parent list of a variable and captures parent variable */
2568 static
varAddParent(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_VAR * parentvar)2569 SCIP_RETCODE varAddParent(
2570 SCIP_VAR* var, /**< variable to add parent to */
2571 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
2572 SCIP_SET* set, /**< global SCIP settings */
2573 SCIP_VAR* parentvar /**< parent variable to add */
2574 )
2575 {
2576 assert(var != NULL);
2577 assert(parentvar != NULL);
2578
2579 /* the direct original counterpart must be stored as first parent */
2580 assert(var->nparentvars == 0 || SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL);
2581
2582 SCIPsetDebugMsg(set, "adding parent <%s>[%p] to variable <%s>[%p] in slot %d\n",
2583 parentvar->name, (void*)parentvar, var->name, (void*)var, var->nparentvars);
2584
2585 SCIP_CALL( varEnsureParentvarsSize(var, blkmem, set, var->nparentvars+1) );
2586
2587 var->parentvars[var->nparentvars] = parentvar;
2588 var->nparentvars++;
2589
2590 SCIPvarCapture(parentvar);
2591
2592 return SCIP_OKAY;
2593 }
2594
2595 /** deletes and releases all variables from the parent list of a variable, frees the memory of parents array */
2596 static
varFreeParents(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)2597 SCIP_RETCODE varFreeParents(
2598 SCIP_VAR** var, /**< pointer to variable */
2599 BMS_BLKMEM* blkmem, /**< block memory */
2600 SCIP_SET* set, /**< global SCIP settings */
2601 SCIP_EVENTQUEUE* eventqueue, /**< event queue (or NULL, if it's an original variable) */
2602 SCIP_LP* lp /**< current LP data (or NULL, if it's an original variable) */
2603 )
2604 {
2605 SCIP_VAR* parentvar;
2606 int i;
2607
2608 SCIPsetDebugMsg(set, "free parents of <%s>\n", (*var)->name);
2609
2610 /* release the parent variables and remove the link from the parent variable to the child */
2611 for( i = 0; i < (*var)->nparentvars; ++i )
2612 {
2613 assert((*var)->parentvars != NULL);
2614 parentvar = (*var)->parentvars[i];
2615 assert(parentvar != NULL);
2616
2617 switch( SCIPvarGetStatus(parentvar) )
2618 {
2619 case SCIP_VARSTATUS_ORIGINAL:
2620 assert(parentvar->data.original.transvar == *var);
2621 assert(&parentvar->data.original.transvar != var);
2622 parentvar->data.original.transvar = NULL;
2623 break;
2624
2625 case SCIP_VARSTATUS_AGGREGATED:
2626 assert(parentvar->data.aggregate.var == *var);
2627 assert(&parentvar->data.aggregate.var != var);
2628 parentvar->data.aggregate.var = NULL;
2629 break;
2630
2631 #if 0
2632 /* The following code is unclear: should the current variable be removed from its parents? */
2633 case SCIP_VARSTATUS_MULTAGGR:
2634 assert(parentvar->data.multaggr.vars != NULL);
2635 for( v = 0; v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] != *var; ++v )
2636 {}
2637 assert(v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] == *var);
2638 if( v < parentvar->data.multaggr.nvars-1 )
2639 {
2640 parentvar->data.multaggr.vars[v] = parentvar->data.multaggr.vars[parentvar->data.multaggr.nvars-1];
2641 parentvar->data.multaggr.scalars[v] = parentvar->data.multaggr.scalars[parentvar->data.multaggr.nvars-1];
2642 }
2643 parentvar->data.multaggr.nvars--;
2644 break;
2645 #endif
2646
2647 case SCIP_VARSTATUS_NEGATED:
2648 assert(parentvar->negatedvar == *var);
2649 assert((*var)->negatedvar == parentvar);
2650 parentvar->negatedvar = NULL;
2651 (*var)->negatedvar = NULL;
2652 break;
2653
2654 default:
2655 SCIPerrorMessage("parent variable is neither ORIGINAL, AGGREGATED nor NEGATED\n");
2656 return SCIP_INVALIDDATA;
2657 } /*lint !e788*/
2658
2659 SCIP_CALL( SCIPvarRelease(&(*var)->parentvars[i], blkmem, set, eventqueue, lp) );
2660 }
2661
2662 /* free parentvars array */
2663 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->parentvars, (*var)->parentvarssize);
2664
2665 return SCIP_OKAY;
2666 }
2667
2668 /** frees a variable */
2669 static
varFree(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)2670 SCIP_RETCODE varFree(
2671 SCIP_VAR** var, /**< pointer to variable */
2672 BMS_BLKMEM* blkmem, /**< block memory */
2673 SCIP_SET* set, /**< global SCIP settings */
2674 SCIP_EVENTQUEUE* eventqueue, /**< event queue (may be NULL, if it's not a column variable) */
2675 SCIP_LP* lp /**< current LP data (may be NULL, if it's not a column variable) */
2676 )
2677 {
2678 assert(var != NULL);
2679 assert(*var != NULL);
2680 assert(SCIPvarGetStatus(*var) != SCIP_VARSTATUS_COLUMN || &(*var)->data.col->var != var);
2681 assert((*var)->nuses == 0);
2682 assert((*var)->probindex == -1);
2683 assert((*var)->nlocksup[SCIP_LOCKTYPE_MODEL] == 0);
2684 assert((*var)->nlocksdown[SCIP_LOCKTYPE_MODEL] == 0);
2685
2686 SCIPsetDebugMsg(set, "free variable <%s> with status=%d\n", (*var)->name, SCIPvarGetStatus(*var));
2687
2688 switch( SCIPvarGetStatus(*var) )
2689 {
2690 case SCIP_VARSTATUS_ORIGINAL:
2691 assert((*var)->data.original.transvar == NULL); /* cannot free variable, if transformed variable is still existing */
2692 holelistFree(&(*var)->data.original.origdom.holelist, blkmem);
2693 assert((*var)->data.original.origdom.holelist == NULL);
2694 break;
2695 case SCIP_VARSTATUS_LOOSE:
2696 break;
2697 case SCIP_VARSTATUS_COLUMN:
2698 SCIP_CALL( SCIPcolFree(&(*var)->data.col, blkmem, set, eventqueue, lp) ); /* free corresponding LP column */
2699 break;
2700 case SCIP_VARSTATUS_FIXED:
2701 case SCIP_VARSTATUS_AGGREGATED:
2702 break;
2703 case SCIP_VARSTATUS_MULTAGGR:
2704 BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.vars, (*var)->data.multaggr.varssize);
2705 BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.scalars, (*var)->data.multaggr.varssize);
2706 break;
2707 case SCIP_VARSTATUS_NEGATED:
2708 break;
2709 default:
2710 SCIPerrorMessage("unknown variable status\n");
2711 return SCIP_INVALIDDATA;
2712 }
2713
2714 /* release all parent variables and free the parentvars array */
2715 SCIP_CALL( varFreeParents(var, blkmem, set, eventqueue, lp) );
2716
2717 /* free user data */
2718 if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_ORIGINAL )
2719 {
2720 if( (*var)->vardelorig != NULL )
2721 {
2722 SCIP_CALL( (*var)->vardelorig(set->scip, *var, &(*var)->vardata) );
2723 }
2724 }
2725 else
2726 {
2727 if( (*var)->vardeltrans != NULL )
2728 {
2729 SCIP_CALL( (*var)->vardeltrans(set->scip, *var, &(*var)->vardata) );
2730 }
2731 }
2732
2733 /* free event filter */
2734 if( (*var)->eventfilter != NULL )
2735 {
2736 SCIP_CALL( SCIPeventfilterFree(&(*var)->eventfilter, blkmem, set) );
2737 }
2738 assert((*var)->eventfilter == NULL);
2739
2740 /* free hole lists */
2741 holelistFree(&(*var)->glbdom.holelist, blkmem);
2742 holelistFree(&(*var)->locdom.holelist, blkmem);
2743 assert((*var)->glbdom.holelist == NULL);
2744 assert((*var)->locdom.holelist == NULL);
2745
2746 /* free variable bounds data structures */
2747 SCIPvboundsFree(&(*var)->vlbs, blkmem);
2748 SCIPvboundsFree(&(*var)->vubs, blkmem);
2749
2750 /* free implications data structures */
2751 SCIPimplicsFree(&(*var)->implics, blkmem);
2752
2753 /* free clique list data structures */
2754 SCIPcliquelistFree(&(*var)->cliquelist, blkmem);
2755
2756 /* free bound change information arrays */
2757 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->lbchginfos, (*var)->lbchginfossize);
2758 BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->ubchginfos, (*var)->ubchginfossize);
2759
2760 /* free branching and inference history entries */
2761 SCIPhistoryFree(&(*var)->history, blkmem);
2762 SCIPhistoryFree(&(*var)->historycrun, blkmem);
2763 SCIPvaluehistoryFree(&(*var)->valuehistory, blkmem);
2764
2765 /* free variable data structure */
2766 BMSfreeBlockMemoryArray(blkmem, &(*var)->name, strlen((*var)->name)+1);
2767 BMSfreeBlockMemory(blkmem, var);
2768
2769 return SCIP_OKAY;
2770 }
2771
2772 /** increases usage counter of variable */
SCIPvarCapture(SCIP_VAR * var)2773 void SCIPvarCapture(
2774 SCIP_VAR* var /**< variable */
2775 )
2776 {
2777 assert(var != NULL);
2778 assert(var->nuses >= 0);
2779
2780 SCIPdebugMessage("capture variable <%s> with nuses=%d\n", var->name, var->nuses);
2781 var->nuses++;
2782 }
2783
2784 /** decreases usage counter of variable, and frees memory if necessary */
SCIPvarRelease(SCIP_VAR ** var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)2785 SCIP_RETCODE SCIPvarRelease(
2786 SCIP_VAR** var, /**< pointer to variable */
2787 BMS_BLKMEM* blkmem, /**< block memory */
2788 SCIP_SET* set, /**< global SCIP settings */
2789 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
2790 SCIP_LP* lp /**< current LP data (or NULL, if it's an original variable) */
2791 )
2792 {
2793 assert(var != NULL);
2794 assert(*var != NULL);
2795 assert((*var)->nuses >= 1);
2796 assert(blkmem != NULL);
2797 assert((*var)->scip == set->scip);
2798
2799 SCIPsetDebugMsg(set, "release variable <%s> with nuses=%d\n", (*var)->name, (*var)->nuses);
2800 (*var)->nuses--;
2801 if( (*var)->nuses == 0 )
2802 {
2803 SCIP_CALL( varFree(var, blkmem, set, eventqueue, lp) );
2804 }
2805
2806 *var = NULL;
2807
2808 return SCIP_OKAY;
2809 }
2810
2811 /** change variable name */
SCIPvarChgName(SCIP_VAR * var,BMS_BLKMEM * blkmem,const char * name)2812 SCIP_RETCODE SCIPvarChgName(
2813 SCIP_VAR* var, /**< problem variable */
2814 BMS_BLKMEM* blkmem, /**< block memory */
2815 const char* name /**< name of variable */
2816 )
2817 {
2818 assert(name != NULL);
2819
2820 /* remove old variable name */
2821 BMSfreeBlockMemoryArray(blkmem, &var->name, strlen(var->name)+1);
2822
2823 /* set new variable name */
2824 SCIP_CALL( varSetName(var, blkmem, NULL, name) );
2825
2826 return SCIP_OKAY;
2827 }
2828
2829 /** initializes variable data structure for solving */
SCIPvarInitSolve(SCIP_VAR * var)2830 void SCIPvarInitSolve(
2831 SCIP_VAR* var /**< problem variable */
2832 )
2833 {
2834 assert(var != NULL);
2835
2836 SCIPhistoryReset(var->historycrun);
2837 var->conflictlbcount = 0;
2838 var->conflictubcount = 0;
2839 }
2840
2841 /** outputs the given bounds into the file stream */
2842 static
printBounds(SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,SCIP_Real lb,SCIP_Real ub,const char * name)2843 void printBounds(
2844 SCIP_SET* set, /**< global SCIP settings */
2845 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2846 FILE* file, /**< output file (or NULL for standard output) */
2847 SCIP_Real lb, /**< lower bound */
2848 SCIP_Real ub, /**< upper bound */
2849 const char* name /**< bound type name */
2850 )
2851 {
2852 assert(set != NULL);
2853
2854 SCIPmessageFPrintInfo(messagehdlr, file, ", %s=", name);
2855 if( SCIPsetIsInfinity(set, lb) )
2856 SCIPmessageFPrintInfo(messagehdlr, file, "[+inf,");
2857 else if( SCIPsetIsInfinity(set, -lb) )
2858 SCIPmessageFPrintInfo(messagehdlr, file, "[-inf,");
2859 else
2860 SCIPmessageFPrintInfo(messagehdlr, file, "[%.15g,", lb);
2861 if( SCIPsetIsInfinity(set, ub) )
2862 SCIPmessageFPrintInfo(messagehdlr, file, "+inf]");
2863 else if( SCIPsetIsInfinity(set, -ub) )
2864 SCIPmessageFPrintInfo(messagehdlr, file, "-inf]");
2865 else
2866 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g]", ub);
2867 }
2868
2869 /** prints hole list to file stream */
2870 static
printHolelist(SCIP_MESSAGEHDLR * messagehdlr,FILE * file,SCIP_HOLELIST * holelist,const char * name)2871 void printHolelist(
2872 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2873 FILE* file, /**< output file (or NULL for standard output) */
2874 SCIP_HOLELIST* holelist, /**< hole list pointer to hole of interest */
2875 const char* name /**< hole type name */
2876 )
2877 { /*lint --e{715}*/
2878 SCIP_Real left;
2879 SCIP_Real right;
2880
2881 if( holelist == NULL )
2882 return;
2883
2884 left = SCIPholelistGetLeft(holelist);
2885 right = SCIPholelistGetRight(holelist);
2886
2887 /* display first hole */
2888 SCIPmessageFPrintInfo(messagehdlr, file, ", %s=(%g,%g)", name, left, right);
2889 holelist = SCIPholelistGetNext(holelist);
2890
2891 while(holelist != NULL )
2892 {
2893 left = SCIPholelistGetLeft(holelist);
2894 right = SCIPholelistGetRight(holelist);
2895
2896 /* display hole */
2897 SCIPmessageFPrintInfo(messagehdlr, file, "(%g,%g)", left, right);
2898
2899 /* get next hole */
2900 holelist = SCIPholelistGetNext(holelist);
2901 }
2902 }
2903
2904 /** outputs variable information into file stream */
SCIPvarPrint(SCIP_VAR * var,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,FILE * file)2905 SCIP_RETCODE SCIPvarPrint(
2906 SCIP_VAR* var, /**< problem variable */
2907 SCIP_SET* set, /**< global SCIP settings */
2908 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
2909 FILE* file /**< output file (or NULL for standard output) */
2910 )
2911 {
2912 SCIP_HOLELIST* holelist;
2913 SCIP_Real lb;
2914 SCIP_Real ub;
2915 int i;
2916
2917 assert(var != NULL);
2918 assert(var->scip == set->scip);
2919
2920 /* type of variable */
2921 switch( SCIPvarGetType(var) )
2922 {
2923 case SCIP_VARTYPE_BINARY:
2924 SCIPmessageFPrintInfo(messagehdlr, file, " [binary]");
2925 break;
2926 case SCIP_VARTYPE_INTEGER:
2927 SCIPmessageFPrintInfo(messagehdlr, file, " [integer]");
2928 break;
2929 case SCIP_VARTYPE_IMPLINT:
2930 SCIPmessageFPrintInfo(messagehdlr, file, " [implicit]");
2931 break;
2932 case SCIP_VARTYPE_CONTINUOUS:
2933 SCIPmessageFPrintInfo(messagehdlr, file, " [continuous]");
2934 break;
2935 default:
2936 SCIPerrorMessage("unknown variable type\n");
2937 SCIPABORT();
2938 return SCIP_ERROR; /*lint !e527*/
2939 }
2940
2941 /* name */
2942 SCIPmessageFPrintInfo(messagehdlr, file, " <%s>:", var->name);
2943
2944 /* objective value */
2945 SCIPmessageFPrintInfo(messagehdlr, file, " obj=%.15g", var->obj);
2946
2947 /* bounds (global bounds for transformed variables, original bounds for original variables) */
2948 if( !SCIPvarIsTransformed(var) )
2949 {
2950 /* output original bound */
2951 lb = SCIPvarGetLbOriginal(var);
2952 ub = SCIPvarGetUbOriginal(var);
2953 printBounds(set, messagehdlr, file, lb, ub, "original bounds");
2954
2955 /* output lazy bound */
2956 lb = SCIPvarGetLbLazy(var);
2957 ub = SCIPvarGetUbLazy(var);
2958
2959 /* only display the lazy bounds if they are different from [-infinity,infinity] */
2960 if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
2961 printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");
2962
2963 holelist = SCIPvarGetHolelistOriginal(var);
2964 printHolelist(messagehdlr, file, holelist, "original holes");
2965 }
2966 else
2967 {
2968 /* output global bound */
2969 lb = SCIPvarGetLbGlobal(var);
2970 ub = SCIPvarGetUbGlobal(var);
2971 printBounds(set, messagehdlr, file, lb, ub, "global bounds");
2972
2973 /* output local bound */
2974 lb = SCIPvarGetLbLocal(var);
2975 ub = SCIPvarGetUbLocal(var);
2976 printBounds(set, messagehdlr, file, lb, ub, "local bounds");
2977
2978 /* output lazy bound */
2979 lb = SCIPvarGetLbLazy(var);
2980 ub = SCIPvarGetUbLazy(var);
2981
2982 /* only display the lazy bounds if they are different from [-infinity,infinity] */
2983 if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
2984 printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");
2985
2986 /* global hole list */
2987 holelist = SCIPvarGetHolelistGlobal(var);
2988 printHolelist(messagehdlr, file, holelist, "global holes");
2989
2990 /* local hole list */
2991 holelist = SCIPvarGetHolelistLocal(var);
2992 printHolelist(messagehdlr, file, holelist, "local holes");
2993 }
2994
2995 /* fixings and aggregations */
2996 switch( SCIPvarGetStatus(var) )
2997 {
2998 case SCIP_VARSTATUS_ORIGINAL:
2999 case SCIP_VARSTATUS_LOOSE:
3000 case SCIP_VARSTATUS_COLUMN:
3001 break;
3002
3003 case SCIP_VARSTATUS_FIXED:
3004 SCIPmessageFPrintInfo(messagehdlr, file, ", fixed:");
3005 if( SCIPsetIsInfinity(set, var->glbdom.lb) )
3006 SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
3007 else if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
3008 SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
3009 else
3010 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", var->glbdom.lb);
3011 break;
3012
3013 case SCIP_VARSTATUS_AGGREGATED:
3014 SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
3015 if( !SCIPsetIsZero(set, var->data.aggregate.constant) )
3016 SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.aggregate.constant);
3017 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.aggregate.scalar, SCIPvarGetName(var->data.aggregate.var));
3018 break;
3019
3020 case SCIP_VARSTATUS_MULTAGGR:
3021 SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
3022 if( var->data.multaggr.nvars == 0 || !SCIPsetIsZero(set, var->data.multaggr.constant) )
3023 SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.multaggr.constant);
3024 for( i = 0; i < var->data.multaggr.nvars; ++i )
3025 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.multaggr.scalars[i], SCIPvarGetName(var->data.multaggr.vars[i]));
3026 break;
3027
3028 case SCIP_VARSTATUS_NEGATED:
3029 SCIPmessageFPrintInfo(messagehdlr, file, ", negated: %.15g - <%s>", var->data.negate.constant, SCIPvarGetName(var->negatedvar));
3030 break;
3031
3032 default:
3033 SCIPerrorMessage("unknown variable status\n");
3034 SCIPABORT();
3035 return SCIP_ERROR; /*lint !e527*/
3036 }
3037
3038 SCIPmessageFPrintInfo(messagehdlr, file, "\n");
3039
3040 return SCIP_OKAY;
3041 }
3042
3043 /** issues a VARUNLOCKED event on the given variable */
3044 static
varEventVarUnlocked(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue)3045 SCIP_RETCODE varEventVarUnlocked(
3046 SCIP_VAR* var, /**< problem variable to change */
3047 BMS_BLKMEM* blkmem, /**< block memory */
3048 SCIP_SET* set, /**< global SCIP settings */
3049 SCIP_EVENTQUEUE* eventqueue /**< event queue */
3050 )
3051 {
3052 SCIP_EVENT* event;
3053
3054 assert(var != NULL);
3055 assert(var->nlocksdown[SCIP_LOCKTYPE_MODEL] <= 1 && var->nlocksup[SCIP_LOCKTYPE_MODEL] <= 1);
3056 assert(var->scip == set->scip);
3057
3058 /* issue VARUNLOCKED event on variable */
3059 SCIP_CALL( SCIPeventCreateVarUnlocked(&event, blkmem, var) );
3060 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
3061
3062 return SCIP_OKAY;
3063 }
3064
3065 /** modifies lock numbers for rounding */
SCIPvarAddLocks(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LOCKTYPE locktype,int addnlocksdown,int addnlocksup)3066 SCIP_RETCODE SCIPvarAddLocks(
3067 SCIP_VAR* var, /**< problem variable */
3068 BMS_BLKMEM* blkmem, /**< block memory */
3069 SCIP_SET* set, /**< global SCIP settings */
3070 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3071 SCIP_LOCKTYPE locktype, /**< type of the variable locks */
3072 int addnlocksdown, /**< increase in number of rounding down locks */
3073 int addnlocksup /**< increase in number of rounding up locks */
3074 )
3075 {
3076 SCIP_VAR* lockvar;
3077
3078 assert(var != NULL);
3079 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3080 assert(var->nlocksup[locktype] >= 0);
3081 assert(var->nlocksdown[locktype] >= 0);
3082 assert(var->scip == set->scip);
3083
3084 if( addnlocksdown == 0 && addnlocksup == 0 )
3085 return SCIP_OKAY;
3086
3087 #ifdef SCIP_DEBUG
3088 SCIPsetDebugMsg(set, "add rounding locks %d/%d to variable <%s> (locks=%d/%d, type=%u)\n",
3089 addnlocksdown, addnlocksup, var->name, var->nlocksdown[locktype], var->nlocksup[locktype], locktype);
3090 #endif
3091
3092 lockvar = var;
3093
3094 while( TRUE ) /*lint !e716 */
3095 {
3096 assert(lockvar != NULL);
3097
3098 switch( SCIPvarGetStatus(lockvar) )
3099 {
3100 case SCIP_VARSTATUS_ORIGINAL:
3101 if( lockvar->data.original.transvar != NULL )
3102 {
3103 lockvar = lockvar->data.original.transvar;
3104 break;
3105 }
3106 else
3107 {
3108 lockvar->nlocksdown[locktype] += addnlocksdown;
3109 lockvar->nlocksup[locktype] += addnlocksup;
3110
3111 assert(lockvar->nlocksdown[locktype] >= 0);
3112 assert(lockvar->nlocksup[locktype] >= 0);
3113
3114 return SCIP_OKAY;
3115 }
3116 case SCIP_VARSTATUS_LOOSE:
3117 case SCIP_VARSTATUS_COLUMN:
3118 case SCIP_VARSTATUS_FIXED:
3119 lockvar->nlocksdown[locktype] += addnlocksdown;
3120 lockvar->nlocksup[locktype] += addnlocksup;
3121
3122 assert(lockvar->nlocksdown[locktype] >= 0);
3123 assert(lockvar->nlocksup[locktype] >= 0);
3124
3125 if( locktype == SCIP_LOCKTYPE_MODEL && lockvar->nlocksdown[locktype] <= 1
3126 && lockvar->nlocksup[locktype] <= 1 )
3127 {
3128 SCIP_CALL( varEventVarUnlocked(lockvar, blkmem, set, eventqueue) );
3129 }
3130
3131 return SCIP_OKAY;
3132 case SCIP_VARSTATUS_AGGREGATED:
3133 if( lockvar->data.aggregate.scalar < 0.0 )
3134 {
3135 int tmp = addnlocksup;
3136
3137 addnlocksup = addnlocksdown;
3138 addnlocksdown = tmp;
3139 }
3140
3141 lockvar = lockvar->data.aggregate.var;
3142 break;
3143 case SCIP_VARSTATUS_MULTAGGR:
3144 {
3145 int v;
3146
3147 assert(!lockvar->donotmultaggr);
3148
3149 lockvar->nlocksdown[locktype] += addnlocksdown;
3150 lockvar->nlocksup[locktype] += addnlocksup;
3151
3152 assert(lockvar->nlocksdown[locktype] >= 0);
3153 assert(lockvar->nlocksup[locktype] >= 0);
3154
3155 for( v = lockvar->data.multaggr.nvars - 1; v >= 0; --v )
3156 {
3157 if( lockvar->data.multaggr.scalars[v] > 0.0 )
3158 {
3159 SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksdown,
3160 addnlocksup) );
3161 }
3162 else
3163 {
3164 SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksup,
3165 addnlocksdown) );
3166 }
3167 }
3168 return SCIP_OKAY;
3169 }
3170 case SCIP_VARSTATUS_NEGATED:
3171 {
3172 int tmp = addnlocksup;
3173
3174 assert(lockvar->negatedvar != NULL);
3175 assert(SCIPvarGetStatus(lockvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
3176 assert(lockvar->negatedvar->negatedvar == lockvar);
3177
3178 addnlocksup = addnlocksdown;
3179 addnlocksdown = tmp;
3180
3181 lockvar = lockvar->negatedvar;
3182 break;
3183 }
3184 default:
3185 SCIPerrorMessage("unknown variable status\n");
3186 return SCIP_INVALIDDATA;
3187 }
3188 }
3189 }
3190
3191 /** gets number of locks for rounding down of a special type */
SCIPvarGetNLocksDownType(SCIP_VAR * var,SCIP_LOCKTYPE locktype)3192 int SCIPvarGetNLocksDownType(
3193 SCIP_VAR* var, /**< problem variable */
3194 SCIP_LOCKTYPE locktype /**< type of variable locks */
3195 )
3196 {
3197 int nlocks;
3198 int i;
3199
3200 assert(var != NULL);
3201 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3202 assert(var->nlocksdown[locktype] >= 0);
3203
3204 switch( SCIPvarGetStatus(var) )
3205 {
3206 case SCIP_VARSTATUS_ORIGINAL:
3207 if( var->data.original.transvar != NULL )
3208 return SCIPvarGetNLocksDownType(var->data.original.transvar, locktype);
3209 else
3210 return var->nlocksdown[locktype];
3211
3212 case SCIP_VARSTATUS_LOOSE:
3213 case SCIP_VARSTATUS_COLUMN:
3214 case SCIP_VARSTATUS_FIXED:
3215 return var->nlocksdown[locktype];
3216
3217 case SCIP_VARSTATUS_AGGREGATED:
3218 if( var->data.aggregate.scalar > 0.0 )
3219 return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);
3220 else
3221 return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);
3222
3223 case SCIP_VARSTATUS_MULTAGGR:
3224 assert(!var->donotmultaggr);
3225 nlocks = 0;
3226 for( i = 0; i < var->data.multaggr.nvars; ++i )
3227 {
3228 if( var->data.multaggr.scalars[i] > 0.0 )
3229 nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
3230 else
3231 nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
3232 }
3233 return nlocks;
3234
3235 case SCIP_VARSTATUS_NEGATED:
3236 assert(var->negatedvar != NULL);
3237 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3238 assert(var->negatedvar->negatedvar == var);
3239 return SCIPvarGetNLocksUpType(var->negatedvar, locktype);
3240
3241 default:
3242 SCIPerrorMessage("unknown variable status\n");
3243 SCIPABORT();
3244 return INT_MAX; /*lint !e527*/
3245 }
3246 }
3247
3248 /** gets number of locks for rounding up of a special type */
SCIPvarGetNLocksUpType(SCIP_VAR * var,SCIP_LOCKTYPE locktype)3249 int SCIPvarGetNLocksUpType(
3250 SCIP_VAR* var, /**< problem variable */
3251 SCIP_LOCKTYPE locktype /**< type of variable locks */
3252 )
3253 {
3254 int nlocks;
3255 int i;
3256
3257 assert(var != NULL);
3258 assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
3259 assert(var->nlocksup[locktype] >= 0);
3260
3261 switch( SCIPvarGetStatus(var) )
3262 {
3263 case SCIP_VARSTATUS_ORIGINAL:
3264 if( var->data.original.transvar != NULL )
3265 return SCIPvarGetNLocksUpType(var->data.original.transvar, locktype);
3266 else
3267 return var->nlocksup[locktype];
3268
3269 case SCIP_VARSTATUS_LOOSE:
3270 case SCIP_VARSTATUS_COLUMN:
3271 case SCIP_VARSTATUS_FIXED:
3272 return var->nlocksup[locktype];
3273
3274 case SCIP_VARSTATUS_AGGREGATED:
3275 if( var->data.aggregate.scalar > 0.0 )
3276 return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);
3277 else
3278 return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);
3279
3280 case SCIP_VARSTATUS_MULTAGGR:
3281 assert(!var->donotmultaggr);
3282 nlocks = 0;
3283 for( i = 0; i < var->data.multaggr.nvars; ++i )
3284 {
3285 if( var->data.multaggr.scalars[i] > 0.0 )
3286 nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
3287 else
3288 nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
3289 }
3290 return nlocks;
3291
3292 case SCIP_VARSTATUS_NEGATED:
3293 assert(var->negatedvar != NULL);
3294 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3295 assert(var->negatedvar->negatedvar == var);
3296 return SCIPvarGetNLocksDownType(var->negatedvar, locktype);
3297
3298 default:
3299 SCIPerrorMessage("unknown variable status\n");
3300 SCIPABORT();
3301 return INT_MAX; /*lint !e527*/
3302 }
3303 }
3304
3305 /** gets number of locks for rounding down
3306 *
3307 * @note This method will always return variable locks of type model
3308 *
3309 * @note It is recommented to use SCIPvarGetNLocksDownType()
3310 */
SCIPvarGetNLocksDown(SCIP_VAR * var)3311 int SCIPvarGetNLocksDown(
3312 SCIP_VAR* var /**< problem variable */
3313 )
3314 {
3315 return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL);
3316 }
3317
3318 /** gets number of locks for rounding up
3319 *
3320 * @note This method will always return variable locks of type model
3321 *
3322 * @note It is recommented to use SCIPvarGetNLocksUpType()
3323 */
SCIPvarGetNLocksUp(SCIP_VAR * var)3324 int SCIPvarGetNLocksUp(
3325 SCIP_VAR* var /**< problem variable */
3326 )
3327 {
3328 return SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL);
3329 }
3330
3331 /** is it possible, to round variable down and stay feasible?
3332 *
3333 * @note This method will always check w.r.t variable locks of type model
3334 */
SCIPvarMayRoundDown(SCIP_VAR * var)3335 SCIP_Bool SCIPvarMayRoundDown(
3336 SCIP_VAR* var /**< problem variable */
3337 )
3338 {
3339 return (SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == 0);
3340 }
3341
3342 /** is it possible, to round variable up and stay feasible?
3343 *
3344 * @note This method will always check w.r.t. variable locks of type model
3345 */
SCIPvarMayRoundUp(SCIP_VAR * var)3346 SCIP_Bool SCIPvarMayRoundUp(
3347 SCIP_VAR* var /**< problem variable */
3348 )
3349 {
3350 return (SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 0);
3351 }
3352
3353 /** gets and captures transformed variable of a given variable; if the variable is not yet transformed,
3354 * a new transformed variable for this variable is created
3355 */
SCIPvarTransform(SCIP_VAR * origvar,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_OBJSENSE objsense,SCIP_VAR ** transvar)3356 SCIP_RETCODE SCIPvarTransform(
3357 SCIP_VAR* origvar, /**< original problem variable */
3358 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3359 SCIP_SET* set, /**< global SCIP settings */
3360 SCIP_STAT* stat, /**< problem statistics */
3361 SCIP_OBJSENSE objsense, /**< objective sense of original problem; transformed is always MINIMIZE */
3362 SCIP_VAR** transvar /**< pointer to store the transformed variable */
3363 )
3364 {
3365 char name[SCIP_MAXSTRLEN];
3366
3367 assert(origvar != NULL);
3368 assert(origvar->scip == set->scip);
3369 assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL);
3370 assert(SCIPsetIsEQ(set, origvar->glbdom.lb, origvar->locdom.lb));
3371 assert(SCIPsetIsEQ(set, origvar->glbdom.ub, origvar->locdom.ub));
3372 assert(origvar->vlbs == NULL);
3373 assert(origvar->vubs == NULL);
3374 assert(transvar != NULL);
3375
3376 /* check if variable is already transformed */
3377 if( origvar->data.original.transvar != NULL )
3378 {
3379 *transvar = origvar->data.original.transvar;
3380 SCIPvarCapture(*transvar);
3381 }
3382 else
3383 {
3384 int i;
3385
3386 /* create transformed variable */
3387 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "t_%s", origvar->name);
3388 SCIP_CALL( SCIPvarCreateTransformed(transvar, blkmem, set, stat, name,
3389 origvar->glbdom.lb, origvar->glbdom.ub, (SCIP_Real)objsense * origvar->obj,
3390 SCIPvarGetType(origvar), origvar->initial, origvar->removable,
3391 origvar->vardelorig, origvar->vartrans, origvar->vardeltrans, origvar->varcopy, NULL) );
3392
3393 /* copy the branch factor and priority */
3394 (*transvar)->branchfactor = origvar->branchfactor;
3395 (*transvar)->branchpriority = origvar->branchpriority;
3396 (*transvar)->branchdirection = origvar->branchdirection; /*lint !e732*/
3397
3398 /* duplicate hole lists */
3399 SCIP_CALL( holelistDuplicate(&(*transvar)->glbdom.holelist, blkmem, set, origvar->glbdom.holelist) );
3400 SCIP_CALL( holelistDuplicate(&(*transvar)->locdom.holelist, blkmem, set, origvar->locdom.holelist) );
3401
3402 /* link original and transformed variable */
3403 origvar->data.original.transvar = *transvar;
3404 SCIP_CALL( varAddParent(*transvar, blkmem, set, origvar) );
3405
3406 /* copy rounding locks */
3407 for( i = 0; i < NLOCKTYPES; i++ )
3408 {
3409 (*transvar)->nlocksdown[i] = origvar->nlocksdown[i];
3410 (*transvar)->nlocksup[i] = origvar->nlocksup[i];
3411 assert((*transvar)->nlocksdown[i] >= 0);
3412 assert((*transvar)->nlocksup[i] >= 0);
3413 }
3414
3415 /* copy doNotMultiaggr status */
3416 (*transvar)->donotmultaggr = origvar->donotmultaggr;
3417
3418 /* copy lazy bounds */
3419 (*transvar)->lazylb = origvar->lazylb;
3420 (*transvar)->lazyub = origvar->lazyub;
3421
3422 /* transfer eventual variable statistics; do not update global statistics, because this has been done
3423 * when original variable was created
3424 */
3425 SCIPhistoryUnite((*transvar)->history, origvar->history, FALSE);
3426
3427 /* transform user data */
3428 if( origvar->vartrans != NULL )
3429 {
3430 SCIP_CALL( origvar->vartrans(set->scip, origvar, origvar->vardata, *transvar, &(*transvar)->vardata) );
3431 }
3432 else
3433 (*transvar)->vardata = origvar->vardata;
3434 }
3435
3436 SCIPsetDebugMsg(set, "transformed variable: <%s>[%p] -> <%s>[%p]\n", origvar->name, (void*)origvar, (*transvar)->name, (void*)*transvar);
3437
3438 return SCIP_OKAY;
3439 }
3440
3441 /** gets corresponding transformed variable of an original or negated original variable */
SCIPvarGetTransformed(SCIP_VAR * origvar,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR ** transvar)3442 SCIP_RETCODE SCIPvarGetTransformed(
3443 SCIP_VAR* origvar, /**< original problem variable */
3444 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
3445 SCIP_SET* set, /**< global SCIP settings */
3446 SCIP_STAT* stat, /**< problem statistics */
3447 SCIP_VAR** transvar /**< pointer to store the transformed variable, or NULL if not existing yet */
3448 )
3449 {
3450 assert(origvar != NULL);
3451 assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED);
3452 assert(origvar->scip == set->scip);
3453
3454 if( SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED )
3455 {
3456 assert(origvar->negatedvar != NULL);
3457 assert(SCIPvarGetStatus(origvar->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
3458
3459 if( origvar->negatedvar->data.original.transvar == NULL )
3460 *transvar = NULL;
3461 else
3462 {
3463 SCIP_CALL( SCIPvarNegate(origvar->negatedvar->data.original.transvar, blkmem, set, stat, transvar) );
3464 }
3465 }
3466 else
3467 *transvar = origvar->data.original.transvar;
3468
3469 return SCIP_OKAY;
3470 }
3471
3472 /** converts loose transformed variable into column variable, creates LP column */
SCIPvarColumn(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_LP * lp)3473 SCIP_RETCODE SCIPvarColumn(
3474 SCIP_VAR* var, /**< problem variable */
3475 BMS_BLKMEM* blkmem, /**< block memory */
3476 SCIP_SET* set, /**< global SCIP settings */
3477 SCIP_STAT* stat, /**< problem statistics */
3478 SCIP_PROB* prob, /**< problem data */
3479 SCIP_LP* lp /**< current LP data */
3480 )
3481 {
3482 assert(var != NULL);
3483 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
3484 assert(var->scip == set->scip);
3485
3486 SCIPsetDebugMsg(set, "creating column for variable <%s>\n", var->name);
3487
3488 /* switch variable status */
3489 var->varstatus = SCIP_VARSTATUS_COLUMN; /*lint !e641*/
3490
3491 /* create column of variable */
3492 SCIP_CALL( SCIPcolCreate(&var->data.col, blkmem, set, stat, var, 0, NULL, NULL, var->removable) );
3493
3494 if( var->probindex != -1 )
3495 {
3496 /* inform problem about the variable's status change */
3497 SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );
3498
3499 /* inform LP, that problem variable is now a column variable and no longer loose */
3500 SCIP_CALL( SCIPlpUpdateVarColumn(lp, set, var) );
3501 }
3502
3503 return SCIP_OKAY;
3504 }
3505
3506 /** converts column transformed variable back into loose variable, frees LP column */
SCIPvarLoose(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_PROB * prob,SCIP_LP * lp)3507 SCIP_RETCODE SCIPvarLoose(
3508 SCIP_VAR* var, /**< problem variable */
3509 BMS_BLKMEM* blkmem, /**< block memory */
3510 SCIP_SET* set, /**< global SCIP settings */
3511 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3512 SCIP_PROB* prob, /**< problem data */
3513 SCIP_LP* lp /**< current LP data */
3514 )
3515 {
3516 assert(var != NULL);
3517 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
3518 assert(var->scip == set->scip);
3519 assert(var->data.col != NULL);
3520 assert(var->data.col->lppos == -1);
3521 assert(var->data.col->lpipos == -1);
3522
3523 SCIPsetDebugMsg(set, "deleting column for variable <%s>\n", var->name);
3524
3525 /* free column of variable */
3526 SCIP_CALL( SCIPcolFree(&var->data.col, blkmem, set, eventqueue, lp) );
3527
3528 /* switch variable status */
3529 var->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
3530
3531 if( var->probindex != -1 )
3532 {
3533 /* inform problem about the variable's status change */
3534 SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );
3535
3536 /* inform LP, that problem variable is now a loose variable and no longer a column */
3537 SCIP_CALL( SCIPlpUpdateVarLoose(lp, set, var) );
3538 }
3539
3540 return SCIP_OKAY;
3541 }
3542
3543 /** issues a VARFIXED event on the given variable and all its parents (except ORIGINAL parents);
3544 * the event issuing on the parents is necessary, because unlike with bound changes, the parent variables
3545 * are not informed about a fixing of an active variable they are pointing to
3546 */
3547 static
varEventVarFixed(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,int fixeventtype)3548 SCIP_RETCODE varEventVarFixed(
3549 SCIP_VAR* var, /**< problem variable to change */
3550 BMS_BLKMEM* blkmem, /**< block memory */
3551 SCIP_SET* set, /**< global SCIP settings */
3552 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3553 int fixeventtype /**< is this event a fixation(0), an aggregation(1), or a
3554 * multi-aggregation(2)
3555 */
3556 )
3557 {
3558 SCIP_EVENT* event;
3559 SCIP_VARSTATUS varstatus;
3560 int i;
3561
3562 assert(var != NULL);
3563 assert(var->scip == set->scip);
3564 assert(0 <= fixeventtype && fixeventtype <= 2);
3565
3566 /* issue VARFIXED event on variable */
3567 SCIP_CALL( SCIPeventCreateVarFixed(&event, blkmem, var) );
3568 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
3569
3570 #ifndef NDEBUG
3571 for( i = var->nparentvars -1; i >= 0; --i )
3572 {
3573 assert(SCIPvarGetStatus(var->parentvars[i]) != SCIP_VARSTATUS_MULTAGGR);
3574 }
3575 #endif
3576
3577 switch( fixeventtype )
3578 {
3579 case 0:
3580 /* process all parents of a fixed variable */
3581 for( i = var->nparentvars - 1; i >= 0; --i )
3582 {
3583 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3584
3585 assert(varstatus != SCIP_VARSTATUS_FIXED);
3586
3587 /* issue event on all not yet fixed parent variables, (that should already issued this event) except the original
3588 * one
3589 */
3590 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3591 {
3592 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3593 }
3594 }
3595 break;
3596 case 1:
3597 /* process all parents of a aggregated variable */
3598 for( i = var->nparentvars - 1; i >= 0; --i )
3599 {
3600 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3601
3602 assert(varstatus != SCIP_VARSTATUS_FIXED);
3603
3604 /* issue event for not aggregated parent variable, because for these and its parents the var event was already
3605 * issued(, except the original one)
3606 *
3607 * @note that even before an aggregated parent variable, there might be variables, for which the vent was not
3608 * yet issued
3609 */
3610 if( varstatus == SCIP_VARSTATUS_AGGREGATED )
3611 continue;
3612
3613 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3614 {
3615 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3616 }
3617 }
3618 break;
3619 case 2:
3620 /* process all parents of a aggregated variable */
3621 for( i = var->nparentvars - 1; i >= 0; --i )
3622 {
3623 varstatus = SCIPvarGetStatus(var->parentvars[i]);
3624
3625 assert(varstatus != SCIP_VARSTATUS_FIXED);
3626
3627 /* issue event on all parent variables except the original one */
3628 if( varstatus != SCIP_VARSTATUS_ORIGINAL )
3629 {
3630 SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
3631 }
3632 }
3633 break;
3634 default:
3635 SCIPerrorMessage("unknown variable fixation event origin\n");
3636 return SCIP_INVALIDDATA;
3637 }
3638
3639 return SCIP_OKAY;
3640 }
3641
3642 /** converts variable into fixed variable */
SCIPvarFix(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real fixedval,SCIP_Bool * infeasible,SCIP_Bool * fixed)3643 SCIP_RETCODE SCIPvarFix(
3644 SCIP_VAR* var, /**< problem variable */
3645 BMS_BLKMEM* blkmem, /**< block memory */
3646 SCIP_SET* set, /**< global SCIP settings */
3647 SCIP_STAT* stat, /**< problem statistics */
3648 SCIP_PROB* transprob, /**< tranformed problem data */
3649 SCIP_PROB* origprob, /**< original problem data */
3650 SCIP_PRIMAL* primal, /**< primal data */
3651 SCIP_TREE* tree, /**< branch and bound tree */
3652 SCIP_REOPT* reopt, /**< reoptimization data structure */
3653 SCIP_LP* lp, /**< current LP data */
3654 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
3655 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
3656 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
3657 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
3658 SCIP_Real fixedval, /**< value to fix variable at */
3659 SCIP_Bool* infeasible, /**< pointer to store whether the fixing is infeasible */
3660 SCIP_Bool* fixed /**< pointer to store whether the fixing was performed (variable was unfixed) */
3661 )
3662 {
3663 SCIP_Real obj;
3664 SCIP_Real childfixedval;
3665
3666 assert(var != NULL);
3667 assert(var->scip == set->scip);
3668 assert(SCIPsetIsEQ(set, var->glbdom.lb, var->locdom.lb));
3669 assert(SCIPsetIsEQ(set, var->glbdom.ub, var->locdom.ub));
3670 assert(infeasible != NULL);
3671 assert(fixed != NULL);
3672
3673 SCIPsetDebugMsg(set, "fix variable <%s>[%g,%g] to %g\n", var->name, var->glbdom.lb, var->glbdom.ub, fixedval);
3674
3675 *infeasible = FALSE;
3676 *fixed = FALSE;
3677
3678 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED )
3679 {
3680 *infeasible = !SCIPsetIsFeasEQ(set, fixedval, var->locdom.lb);
3681 SCIPsetDebugMsg(set, " -> variable already fixed to %g (fixedval=%g): infeasible=%u\n", var->locdom.lb, fixedval, *infeasible);
3682 return SCIP_OKAY;
3683 }
3684 else if( (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS && !SCIPsetIsFeasIntegral(set, fixedval))
3685 || SCIPsetIsFeasLT(set, fixedval, var->locdom.lb)
3686 || SCIPsetIsFeasGT(set, fixedval, var->locdom.ub) )
3687 {
3688 SCIPsetDebugMsg(set, " -> fixing infeasible: locdom=[%g,%g], fixedval=%g\n", var->locdom.lb, var->locdom.ub, fixedval);
3689 *infeasible = TRUE;
3690 return SCIP_OKAY;
3691 }
3692
3693 switch( SCIPvarGetStatus(var) )
3694 {
3695 case SCIP_VARSTATUS_ORIGINAL:
3696 if( var->data.original.transvar == NULL )
3697 {
3698 SCIPerrorMessage("cannot fix an untransformed original variable\n");
3699 return SCIP_INVALIDDATA;
3700 }
3701 SCIP_CALL( SCIPvarFix(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
3702 lp, branchcand, eventfilter, eventqueue, cliquetable, fixedval, infeasible, fixed) );
3703 break;
3704
3705 case SCIP_VARSTATUS_LOOSE:
3706 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
3707
3708 /* set the fixed variable's objective value to 0.0 */
3709 obj = var->obj;
3710 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
3711
3712 /* since we change the variable type form loose to fixed, we have to adjust the number of loose
3713 * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
3714 * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
3715 * objective of this variable is set to zero
3716 */
3717 SCIPlpDecNLoosevars(lp);
3718
3719 /* change variable's bounds to fixed value (thereby removing redundant implications and variable bounds) */
3720 holelistFree(&var->glbdom.holelist, blkmem);
3721 holelistFree(&var->locdom.holelist, blkmem);
3722 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );
3723 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );
3724
3725 /* explicitly set variable's bounds, even if the fixed value is in epsilon range of the old bound */
3726 var->glbdom.lb = fixedval;
3727 var->glbdom.ub = fixedval;
3728 var->locdom.lb = fixedval;
3729 var->locdom.ub = fixedval;
3730
3731 /* delete implications and variable bounds information */
3732 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
3733 assert(var->vlbs == NULL);
3734 assert(var->vubs == NULL);
3735 assert(var->implics == NULL);
3736 assert(var->cliquelist == NULL);
3737
3738 /* clear the history of the variable */
3739 SCIPhistoryReset(var->history);
3740 SCIPhistoryReset(var->historycrun);
3741
3742 /* convert variable into fixed variable */
3743 var->varstatus = SCIP_VARSTATUS_FIXED; /*lint !e641*/
3744
3745 /* inform problem about the variable's status change */
3746 if( var->probindex != -1 )
3747 {
3748 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
3749 }
3750
3751 /* reset the objective value of the fixed variable, thus adjusting the problem's objective offset */
3752 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
3753
3754 /* issue VARFIXED event */
3755 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 0) );
3756
3757 *fixed = TRUE;
3758 break;
3759
3760 case SCIP_VARSTATUS_COLUMN:
3761 SCIPerrorMessage("cannot fix a column variable\n");
3762 return SCIP_INVALIDDATA;
3763
3764 case SCIP_VARSTATUS_FIXED:
3765 SCIPerrorMessage("cannot fix a fixed variable again\n"); /*lint !e527*/
3766 SCIPABORT(); /* case is already handled in earlier if condition */
3767 return SCIP_INVALIDDATA; /*lint !e527*/
3768
3769 case SCIP_VARSTATUS_AGGREGATED:
3770 /* fix aggregation variable y in x = a*y + c, instead of fixing x directly */
3771 assert(SCIPsetIsZero(set, var->obj));
3772 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
3773 if( SCIPsetIsInfinity(set, fixedval) || SCIPsetIsInfinity(set, -fixedval) )
3774 childfixedval = (var->data.aggregate.scalar < 0.0 ? -fixedval : fixedval);
3775 else
3776 childfixedval = (fixedval - var->data.aggregate.constant)/var->data.aggregate.scalar;
3777 SCIP_CALL( SCIPvarFix(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
3778 branchcand, eventfilter, eventqueue, cliquetable, childfixedval, infeasible, fixed) );
3779 break;
3780
3781 case SCIP_VARSTATUS_MULTAGGR:
3782 SCIPerrorMessage("cannot fix a multiple aggregated variable\n");
3783 SCIPABORT();
3784 return SCIP_INVALIDDATA; /*lint !e527*/
3785
3786 case SCIP_VARSTATUS_NEGATED:
3787 /* fix negation variable x in x' = offset - x, instead of fixing x' directly */
3788 assert(SCIPsetIsZero(set, var->obj));
3789 assert(var->negatedvar != NULL);
3790 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
3791 assert(var->negatedvar->negatedvar == var);
3792 SCIP_CALL( SCIPvarFix(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
3793 branchcand, eventfilter, eventqueue, cliquetable, var->data.negate.constant - fixedval, infeasible, fixed) );
3794 break;
3795
3796 default:
3797 SCIPerrorMessage("unknown variable status\n");
3798 return SCIP_INVALIDDATA;
3799 }
3800
3801 return SCIP_OKAY;
3802 }
3803
3804 /** transforms given variables, scalars and constant to the corresponding active variables, scalars and constant
3805 *
3806 * If the number of needed active variables is greater than the available slots in the variable array, nothing happens except
3807 * that the required size is stored in the corresponding variable; hence, if afterwards the required size is greater than the
3808 * available slots (varssize), nothing happens; otherwise, the active variable representation is stored in the arrays.
3809 *
3810 * The reason for this approach is that we cannot reallocate memory, since we do not know how the
3811 * memory has been allocated (e.g., by a C++ 'new' or SCIP functions).
3812 */
SCIPvarGetActiveRepresentatives(SCIP_SET * set,SCIP_VAR ** vars,SCIP_Real * scalars,int * nvars,int varssize,SCIP_Real * constant,int * requiredsize,SCIP_Bool mergemultiples)3813 SCIP_RETCODE SCIPvarGetActiveRepresentatives(
3814 SCIP_SET* set, /**< global SCIP settings */
3815 SCIP_VAR** vars, /**< variable array to get active variables */
3816 SCIP_Real* scalars, /**< scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
3817 int* nvars, /**< pointer to number of variables and values in vars and scalars array */
3818 int varssize, /**< available slots in vars and scalars array */
3819 SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
3820 int* requiredsize, /**< pointer to store the required array size for the active variables */
3821 SCIP_Bool mergemultiples /**< should multiple occurrences of a var be replaced by a single coeff? */
3822 )
3823 {
3824 SCIP_VAR** activevars;
3825 SCIP_Real* activescalars;
3826 int nactivevars;
3827 SCIP_Real activeconstant;
3828 SCIP_Bool activeconstantinf;
3829 int activevarssize;
3830
3831 SCIP_VAR* var;
3832 SCIP_Real scalar;
3833 int v;
3834 int k;
3835
3836 SCIP_VAR** tmpvars;
3837 SCIP_VAR** multvars;
3838 SCIP_Real* tmpscalars;
3839 SCIP_Real* multscalars;
3840 int tmpvarssize;
3841 int ntmpvars;
3842 int nmultvars;
3843
3844 SCIP_VAR* multvar;
3845 SCIP_Real multscalar;
3846 SCIP_Real multconstant;
3847 int pos;
3848
3849 int noldtmpvars;
3850
3851 SCIP_VAR** tmpvars2;
3852 SCIP_Real* tmpscalars2;
3853 int tmpvarssize2;
3854 int ntmpvars2;
3855
3856 SCIP_Bool sortagain = FALSE;
3857
3858 assert(set != NULL);
3859 assert(nvars != NULL);
3860 assert(scalars != NULL || *nvars == 0);
3861 assert(constant != NULL);
3862 assert(requiredsize != NULL);
3863 assert(*nvars <= varssize);
3864
3865 *requiredsize = 0;
3866
3867 if( *nvars == 0 )
3868 return SCIP_OKAY;
3869
3870 assert(vars != NULL);
3871
3872 /* handle the "easy" case of just one variable and avoid memory allocation if the variable is already active */
3873 if( *nvars == 1 && (vars[0]->varstatus == ((int) SCIP_VARSTATUS_COLUMN) || vars[0]->varstatus == ((int) SCIP_VARSTATUS_LOOSE)) )
3874 {
3875 *requiredsize = 1;
3876
3877 return SCIP_OKAY;
3878 }
3879
3880 nactivevars = 0;
3881 activeconstant = 0.0;
3882 activeconstantinf = FALSE;
3883 activevarssize = (*nvars) * 2;
3884 ntmpvars = *nvars;
3885 tmpvarssize = *nvars;
3886
3887 tmpvarssize2 = 1;
3888
3889 /* allocate temporary memory */
3890 SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpvars2, tmpvarssize2) );
3891 SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpscalars2, tmpvarssize2) );
3892 SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
3893 SCIP_CALL( SCIPsetAllocBufferArray(set, &activescalars, activevarssize) );
3894 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );
3895 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpscalars, scalars, ntmpvars) );
3896
3897 /* to avoid unnecessary expanding of variable arrays while disaggregating several variables multiple times combine same variables
3898 * first, first get all corresponding variables with status loose, column, multaggr or fixed
3899 */
3900 for( v = ntmpvars - 1; v >= 0; --v )
3901 {
3902 var = tmpvars[v];
3903 scalar = tmpscalars[v];
3904
3905 assert(var != NULL);
3906 /* transforms given variable, scalar and constant to the corresponding active, fixed, or
3907 * multi-aggregated variable, scalar and constant; if the variable resolves to a fixed
3908 * variable, "scalar" will be 0.0 and the value of the sum will be stored in "constant".
3909 */
3910 SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &scalar, &activeconstant) );
3911 assert(var != NULL);
3912
3913 assert(SCIPsetIsInfinity(set, activeconstant) == (activeconstant == SCIPsetInfinity(set))); /*lint !e777*/
3914 assert(SCIPsetIsInfinity(set, -activeconstant) == (activeconstant == -SCIPsetInfinity(set))); /*lint !e777*/
3915
3916 activeconstantinf = SCIPsetIsInfinity(set, activeconstant) || SCIPsetIsInfinity(set, -activeconstant);
3917
3918 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
3919 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
3920 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
3921 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
3922
3923 tmpvars[v] = var;
3924 tmpscalars[v] = scalar;
3925 }
3926 noldtmpvars = ntmpvars;
3927
3928 /* sort all variables to combine equal variables easily */
3929 SCIPsortPtrReal((void**)tmpvars, tmpscalars, SCIPvarComp, noldtmpvars);
3930 ntmpvars = 0;
3931 for( v = 1; v < noldtmpvars; ++v )
3932 {
3933 /* combine same variables */
3934 if( SCIPvarCompare(tmpvars[v], tmpvars[ntmpvars]) == 0 )
3935 {
3936 tmpscalars[ntmpvars] += tmpscalars[v];
3937 }
3938 else
3939 {
3940 ++ntmpvars;
3941 if( v > ntmpvars )
3942 {
3943 tmpscalars[ntmpvars] = tmpscalars[v];
3944 tmpvars[ntmpvars] = tmpvars[v];
3945 }
3946 }
3947 }
3948 ++ntmpvars;
3949
3950 #ifdef SCIP_MORE_DEBUG
3951 for( v = 1; v < ntmpvars; ++v )
3952 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
3953 #endif
3954
3955 /* collect for each variable the representation in active variables */
3956 while( ntmpvars >= 1 )
3957 {
3958 --ntmpvars;
3959 ntmpvars2 = 0;
3960 var = tmpvars[ntmpvars];
3961 scalar = tmpscalars[ntmpvars];
3962
3963 assert(var != NULL);
3964
3965 /* TODO: maybe we should test here on SCIPsetIsZero() instead of 0.0 */
3966 if( scalar == 0.0 )
3967 continue;
3968
3969 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
3970 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
3971 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
3972 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
3973
3974 switch( SCIPvarGetStatus(var) )
3975 {
3976 case SCIP_VARSTATUS_LOOSE:
3977 case SCIP_VARSTATUS_COLUMN:
3978 /* x = a*y + c */
3979 if( nactivevars >= activevarssize )
3980 {
3981 activevarssize *= 2;
3982 SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
3983 SCIP_CALL( SCIPsetReallocBufferArray(set, &activescalars, activevarssize) );
3984 assert(nactivevars < activevarssize);
3985 }
3986 activevars[nactivevars] = var;
3987 activescalars[nactivevars] = scalar;
3988 nactivevars++;
3989 break;
3990
3991 case SCIP_VARSTATUS_MULTAGGR:
3992 /* x = a_1*y_1 + ... + a_n*y_n + c */
3993 nmultvars = var->data.multaggr.nvars;
3994 multvars = var->data.multaggr.vars;
3995 multscalars = var->data.multaggr.scalars;
3996 sortagain = TRUE;
3997
3998 if( nmultvars + ntmpvars > tmpvarssize )
3999 {
4000 while( nmultvars + ntmpvars > tmpvarssize )
4001 tmpvarssize *= 2;
4002 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
4003 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpscalars, tmpvarssize) );
4004 assert(nmultvars + ntmpvars <= tmpvarssize);
4005 }
4006
4007 if( nmultvars > tmpvarssize2 )
4008 {
4009 while( nmultvars > tmpvarssize2 )
4010 tmpvarssize2 *= 2;
4011 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars2, tmpvarssize2) );
4012 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpscalars2, tmpvarssize2) );
4013 assert(nmultvars <= tmpvarssize2);
4014 }
4015
4016 --nmultvars;
4017
4018 for( ; nmultvars >= 0; --nmultvars )
4019 {
4020 multvar = multvars[nmultvars];
4021 multscalar = multscalars[nmultvars];
4022 multconstant = 0;
4023
4024 assert(multvar != NULL);
4025 SCIP_CALL( SCIPvarGetProbvarSum(&multvar, set, &multscalar, &multconstant) );
4026 assert(multvar != NULL);
4027
4028 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
4029 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
4030 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
4031 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4032
4033 if( !activeconstantinf )
4034 {
4035 assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));
4036
4037 if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
4038 {
4039 assert(scalar != 0.0);
4040 if( scalar * multconstant > 0.0 )
4041 {
4042 activeconstant = SCIPsetInfinity(set);
4043 activeconstantinf = TRUE;
4044 }
4045 else
4046 {
4047 activeconstant = -SCIPsetInfinity(set);
4048 activeconstantinf = TRUE;
4049 }
4050 }
4051 else
4052 activeconstant += scalar * multconstant;
4053 }
4054 #ifndef NDEBUG
4055 else
4056 {
4057 assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
4058 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4059 assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
4060 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4061 }
4062 #endif
4063
4064 if( SCIPsortedvecFindPtr((void**)tmpvars, SCIPvarComp, multvar, ntmpvars, &pos) )
4065 {
4066 assert(SCIPvarCompare(tmpvars[pos], multvar) == 0);
4067 tmpscalars[pos] += scalar * multscalar;
4068 }
4069 else
4070 {
4071 tmpvars2[ntmpvars2] = multvar;
4072 tmpscalars2[ntmpvars2] = scalar * multscalar;
4073 ++(ntmpvars2);
4074 assert(ntmpvars2 <= tmpvarssize2);
4075 }
4076 }
4077
4078 if( ntmpvars2 > 0 )
4079 {
4080 /* sort all variables to combine equal variables easily */
4081 SCIPsortPtrReal((void**)tmpvars2, tmpscalars2, SCIPvarComp, ntmpvars2);
4082 pos = 0;
4083 for( v = 1; v < ntmpvars2; ++v )
4084 {
4085 /* combine same variables */
4086 if( SCIPvarCompare(tmpvars2[v], tmpvars2[pos]) == 0 )
4087 {
4088 tmpscalars2[pos] += tmpscalars2[v];
4089 }
4090 else
4091 {
4092 ++pos;
4093 if( v > pos )
4094 {
4095 tmpscalars2[pos] = tmpscalars2[v];
4096 tmpvars2[pos] = tmpvars2[v];
4097 }
4098 }
4099 }
4100 ntmpvars2 = pos + 1;
4101 #ifdef SCIP_MORE_DEBUG
4102 for( v = 1; v < ntmpvars2; ++v )
4103 {
4104 assert(SCIPvarCompare(tmpvars2[v], tmpvars2[v-1]) > 0);
4105 }
4106 for( v = 1; v < ntmpvars; ++v )
4107 {
4108 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
4109 }
4110 #endif
4111 v = ntmpvars - 1;
4112 k = ntmpvars2 - 1;
4113 pos = ntmpvars + ntmpvars2 - 1;
4114 ntmpvars += ntmpvars2;
4115
4116 while( v >= 0 && k >= 0 )
4117 {
4118 assert(pos >= 0);
4119 assert(SCIPvarCompare(tmpvars[v], tmpvars2[k]) != 0);
4120 if( SCIPvarCompare(tmpvars[v], tmpvars2[k]) >= 0 )
4121 {
4122 tmpvars[pos] = tmpvars[v];
4123 tmpscalars[pos] = tmpscalars[v];
4124 --v;
4125 }
4126 else
4127 {
4128 tmpvars[pos] = tmpvars2[k];
4129 tmpscalars[pos] = tmpscalars2[k];
4130 --k;
4131 }
4132 --pos;
4133 assert(pos >= 0);
4134 }
4135 while( v >= 0 )
4136 {
4137 assert(pos >= 0);
4138 tmpvars[pos] = tmpvars[v];
4139 tmpscalars[pos] = tmpscalars[v];
4140 --v;
4141 --pos;
4142 }
4143 while( k >= 0 )
4144 {
4145 assert(pos >= 0);
4146 tmpvars[pos] = tmpvars2[k];
4147 tmpscalars[pos] = tmpscalars2[k];
4148 --k;
4149 --pos;
4150 }
4151 }
4152 #ifdef SCIP_MORE_DEBUG
4153 for( v = 1; v < ntmpvars; ++v )
4154 {
4155 assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
4156 }
4157 #endif
4158
4159 if( !activeconstantinf )
4160 {
4161 assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));
4162
4163 multconstant = SCIPvarGetMultaggrConstant(var);
4164
4165 if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
4166 {
4167 assert(scalar != 0.0);
4168 if( scalar * multconstant > 0.0 )
4169 {
4170 activeconstant = SCIPsetInfinity(set);
4171 activeconstantinf = TRUE;
4172 }
4173 else
4174 {
4175 activeconstant = -SCIPsetInfinity(set);
4176 activeconstantinf = TRUE;
4177 }
4178 }
4179 else
4180 activeconstant += scalar * multconstant;
4181 }
4182 #ifndef NDEBUG
4183 else
4184 {
4185 multconstant = SCIPvarGetMultaggrConstant(var);
4186 assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
4187 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4188 assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
4189 (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
4190 }
4191 #endif
4192 break;
4193
4194 case SCIP_VARSTATUS_FIXED:
4195 case SCIP_VARSTATUS_ORIGINAL:
4196 case SCIP_VARSTATUS_AGGREGATED:
4197 case SCIP_VARSTATUS_NEGATED:
4198 default:
4199 /* case x = c, but actually we should not be here, since SCIPvarGetProbvarSum() returns a scalar of 0.0 for
4200 * fixed variables and is handled already
4201 */
4202 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
4203 assert(SCIPsetIsZero(set, var->glbdom.lb) && SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub));
4204 }
4205 }
4206
4207 if( mergemultiples )
4208 {
4209 if( sortagain )
4210 {
4211 /* sort variable and scalar array by variable index */
4212 SCIPsortPtrReal((void**)activevars, activescalars, SCIPvarComp, nactivevars);
4213
4214 /* eliminate duplicates and count required size */
4215 v = nactivevars - 1;
4216 while( v > 0 )
4217 {
4218 /* combine both variable since they are the same */
4219 if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
4220 {
4221 if( activescalars[v - 1] + activescalars[v] != 0.0 )
4222 {
4223 activescalars[v - 1] += activescalars[v];
4224 --nactivevars;
4225 activevars[v] = activevars[nactivevars];
4226 activescalars[v] = activescalars[nactivevars];
4227 }
4228 else
4229 {
4230 --nactivevars;
4231 activevars[v] = activevars[nactivevars];
4232 activescalars[v] = activescalars[nactivevars];
4233 --nactivevars;
4234 --v;
4235 activevars[v] = activevars[nactivevars];
4236 activescalars[v] = activescalars[nactivevars];
4237 }
4238 }
4239 --v;
4240 }
4241 }
4242 /* the variables were added in reverse order, we revert the order now;
4243 * this should not be necessary, but not doing this changes the behavior sometimes
4244 */
4245 else
4246 {
4247 SCIP_VAR* tmpvar;
4248 SCIP_Real tmpscalar;
4249
4250 for( v = 0; v < nactivevars / 2; ++v )
4251 {
4252 tmpvar = activevars[v];
4253 tmpscalar = activescalars[v];
4254 activevars[v] = activevars[nactivevars - 1 - v];
4255 activescalars[v] = activescalars[nactivevars - 1 - v];
4256 activevars[nactivevars - 1 - v] = tmpvar;
4257 activescalars[nactivevars - 1 - v] = tmpscalar;
4258 }
4259 }
4260 }
4261 *requiredsize = nactivevars;
4262
4263 if( varssize >= *requiredsize )
4264 {
4265 assert(vars != NULL);
4266
4267 *nvars = *requiredsize;
4268
4269 if( !SCIPsetIsInfinity(set, *constant) && !SCIPsetIsInfinity(set, -(*constant)) )
4270 {
4271 /* if the activeconstant is infinite, the constant pointer gets the same value, otherwise add the value */
4272 if( activeconstantinf )
4273 (*constant) = activeconstant;
4274 else
4275 (*constant) += activeconstant;
4276 }
4277 #ifndef NDEBUG
4278 else
4279 {
4280 assert(!SCIPsetIsInfinity(set, (*constant)) || !SCIPsetIsInfinity(set, -activeconstant));
4281 assert(!SCIPsetIsInfinity(set, -(*constant)) || !SCIPsetIsInfinity(set, activeconstant));
4282 }
4283 #endif
4284
4285 /* copy active variable and scalar array to the given arrays */
4286 for( v = 0; v < *nvars; ++v )
4287 {
4288 vars[v] = activevars[v];
4289 scalars[v] = activescalars[v]; /*lint !e613*/
4290 }
4291 }
4292
4293 assert(SCIPsetIsInfinity(set, *constant) == ((*constant) == SCIPsetInfinity(set))); /*lint !e777*/
4294 assert(SCIPsetIsInfinity(set, -(*constant)) == ((*constant) == -SCIPsetInfinity(set))); /*lint !e777*/
4295
4296 SCIPsetFreeBufferArray(set, &tmpscalars);
4297 SCIPsetFreeBufferArray(set, &tmpvars);
4298 SCIPsetFreeBufferArray(set, &activescalars);
4299 SCIPsetFreeBufferArray(set, &activevars);
4300 SCIPsetFreeBufferArray(set, &tmpscalars2);
4301 SCIPsetFreeBufferArray(set, &tmpvars2);
4302
4303 return SCIP_OKAY;
4304 }
4305
4306
4307 /** flattens aggregation graph of multi-aggregated variable in order to avoid exponential recursion later on */
SCIPvarFlattenAggregationGraph(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue)4308 SCIP_RETCODE SCIPvarFlattenAggregationGraph(
4309 SCIP_VAR* var, /**< problem variable */
4310 BMS_BLKMEM* blkmem, /**< block memory */
4311 SCIP_SET* set, /**< global SCIP settings */
4312 SCIP_EVENTQUEUE* eventqueue /**< event queue */
4313 )
4314 {
4315 int nlocksup[NLOCKTYPES];
4316 int nlocksdown[NLOCKTYPES];
4317 SCIP_Real multconstant;
4318 int multvarssize;
4319 int nmultvars;
4320 int multrequiredsize;
4321 int i;
4322
4323 assert( var != NULL );
4324 assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR );
4325 assert(var->scip == set->scip);
4326
4327 /* in order to update the locks on the active representation of the multi-aggregated variable, we remove all locks
4328 * on the current representation now and re-add the locks once the variable graph has been flattened, which
4329 * may lead to duplicate occurences of the same variable being merged
4330 *
4331 * Here is an example. Assume we have the multi-aggregation z = x + y.
4332 * z occures with positive coefficient in a <= constraint c1, so it has an uplock from there.
4333 * When the multi-aggregation is performed, all locks are added to the active representation,
4334 * so x and y both get an uplock from c1. However, z was not yet replaced by x + y in c1.
4335 * Next, a negation y = 1 - x is identified. Again, locks are moved, so that the uplock of y originating
4336 * from c1 is added to x as a downlock. Thus, x has both an up- and downlock from c1.
4337 * The multi-aggregation changes to z = x + 1 - x, which corresponds to the locks.
4338 * However, before z is replaced by that sum, SCIPvarFlattenAggregationGraph() is called
4339 * which changes z = x + y = x + 1 - x = 1, since it merges multiple occurences of the same variable.
4340 * The up- and downlock of x, however, is not removed when replacing z in c1 by its active representation,
4341 * because it is just 1 now. Therefore, we need to update locks when flattening the aggregation graph.
4342 * For this, the multi-aggregated variable knows its locks in addition to adding them to the active
4343 * representation, which corresponds to the locks from constraints where the variable was not replaced yet.
4344 * By removing the locks here, based on the old representation and adding them again after flattening,
4345 * we ensure that the locks are correct afterwards if coefficients were merged.
4346 */
4347 for( i = 0; i < NLOCKTYPES; ++i )
4348 {
4349 nlocksup[i] = var->nlocksup[i];
4350 nlocksdown[i] = var->nlocksdown[i];
4351
4352 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, -nlocksdown[i], -nlocksup[i]) );
4353 }
4354
4355 multconstant = var->data.multaggr.constant;
4356 nmultvars = var->data.multaggr.nvars;
4357 multvarssize = var->data.multaggr.varssize;
4358
4359 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize, TRUE) );
4360
4361 if( multrequiredsize > multvarssize )
4362 {
4363 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.vars), multvarssize, multrequiredsize) );
4364 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.scalars), multvarssize, multrequiredsize) );
4365 multvarssize = multrequiredsize;
4366 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize, TRUE) );
4367 assert( multrequiredsize <= multvarssize );
4368 }
4369 /**@note After the flattening the multi aggregation might resolve to be in fact an aggregation (or even a fixing?).
4370 * This issue is not resolved right now, since var->data.multaggr.nvars < 2 should not cause troubles. However, one
4371 * may loose performance hereby, since aggregated variables are easier to handle.
4372 *
4373 * Note, that there are two cases where SCIPvarFlattenAggregationGraph() is called: The easier one is that it is
4374 * called while installing the multi-aggregation. in principle, the described issue could be handled straightforward
4375 * in this case by aggregating or fixing the variable instead. The more complicated case is the one, when the
4376 * multi-aggregation is used, e.g., in linear presolving (and the variable is already declared to be multi-aggregated).
4377 *
4378 * By now, it is not allowed to fix or aggregate multi-aggregated variables which would be necessary in this case.
4379 *
4380 * The same issue appears in the SCIPvarGetProbvar...() methods.
4381 */
4382
4383 var->data.multaggr.constant = multconstant;
4384 var->data.multaggr.nvars = nmultvars;
4385 var->data.multaggr.varssize = multvarssize;
4386
4387 for( i = 0; i < NLOCKTYPES; ++i )
4388 {
4389 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
4390 }
4391
4392 return SCIP_OKAY;
4393 }
4394
4395 /** merge two variable histories together; a typical use case is that \p othervar is an image of the target variable
4396 * in a SCIP copy. Method should be applied with care, especially because no internal checks are performed whether
4397 * the history merge is reasonable
4398 *
4399 * @note Do not use this method if the two variables originate from two SCIP's with different objective functions, since
4400 * this corrupts the variable pseudo costs
4401 * @note Apply with care; no internal checks are performed if the two variables should be merged
4402 */
SCIPvarMergeHistories(SCIP_VAR * targetvar,SCIP_VAR * othervar,SCIP_STAT * stat)4403 void SCIPvarMergeHistories(
4404 SCIP_VAR* targetvar, /**< the variable that should contain both histories afterwards */
4405 SCIP_VAR* othervar, /**< the variable whose history is to be merged with that of the target variable */
4406 SCIP_STAT* stat /**< problem statistics */
4407 )
4408 {
4409 /* merge only the history of the current run into the target history */
4410 SCIPhistoryUnite(targetvar->history, othervar->historycrun, FALSE);
4411
4412 /* apply the changes also to the global history */
4413 SCIPhistoryUnite(stat->glbhistory, othervar->historycrun, FALSE);
4414 }
4415
4416 /** sets the history of a variable; this method is typically used within reoptimization to keep and update the variable
4417 * history over several iterations
4418 */
SCIPvarSetHistory(SCIP_VAR * var,SCIP_HISTORY * history,SCIP_STAT * stat)4419 void SCIPvarSetHistory(
4420 SCIP_VAR* var, /**< variable */
4421 SCIP_HISTORY* history, /**< the history which is to set */
4422 SCIP_STAT* stat /**< problem statistics */
4423 )
4424 {
4425 /* merge only the history of the current run into the target history */
4426 SCIPhistoryUnite(var->history, history, FALSE);
4427
4428 /* apply the changes also to the global history */
4429 SCIPhistoryUnite(stat->glbhistory, history, FALSE);
4430 }
4431
4432 /** tightens the bounds of both variables in aggregation x = a*y + c */
4433 static
varUpdateAggregationBounds(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_VAR * aggvar,SCIP_Real scalar,SCIP_Real constant,SCIP_Bool * infeasible,SCIP_Bool * fixed)4434 SCIP_RETCODE varUpdateAggregationBounds(
4435 SCIP_VAR* var, /**< problem variable */
4436 BMS_BLKMEM* blkmem, /**< block memory */
4437 SCIP_SET* set, /**< global SCIP settings */
4438 SCIP_STAT* stat, /**< problem statistics */
4439 SCIP_PROB* transprob, /**< tranformed problem data */
4440 SCIP_PROB* origprob, /**< original problem data */
4441 SCIP_PRIMAL* primal, /**< primal data */
4442 SCIP_TREE* tree, /**< branch and bound tree */
4443 SCIP_REOPT* reopt, /**< reoptimization data structure */
4444 SCIP_LP* lp, /**< current LP data */
4445 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
4446 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
4447 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
4448 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
4449 SCIP_VAR* aggvar, /**< variable y in aggregation x = a*y + c */
4450 SCIP_Real scalar, /**< multiplier a in aggregation x = a*y + c */
4451 SCIP_Real constant, /**< constant shift c in aggregation x = a*y + c */
4452 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
4453 SCIP_Bool* fixed /**< pointer to store whether the variables were fixed */
4454 )
4455 {
4456 SCIP_Real varlb;
4457 SCIP_Real varub;
4458 SCIP_Real aggvarlb;
4459 SCIP_Real aggvarub;
4460 SCIP_Bool aggvarbdschanged;
4461
4462 assert(var != NULL);
4463 assert(var->scip == set->scip);
4464 assert(aggvar != NULL);
4465 assert(!SCIPsetIsZero(set, scalar));
4466 assert(infeasible != NULL);
4467 assert(fixed != NULL);
4468
4469 *infeasible = FALSE;
4470 *fixed = FALSE;
4471
4472 SCIPsetDebugMsg(set, "updating bounds of variables in aggregation <%s> == %g*<%s> %+g\n", var->name, scalar, aggvar->name, constant);
4473 SCIPsetDebugMsg(set, " old bounds: <%s> [%g,%g] <%s> [%g,%g]\n",
4474 var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);
4475
4476 /* loop as long additional changes may be found */
4477 do
4478 {
4479 aggvarbdschanged = FALSE;
4480
4481 /* update the bounds of the aggregated variable x in x = a*y + c */
4482 if( scalar > 0.0 )
4483 {
4484 if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
4485 varlb = -SCIPsetInfinity(set);
4486 else
4487 varlb = aggvar->glbdom.lb * scalar + constant;
4488 if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
4489 varub = SCIPsetInfinity(set);
4490 else
4491 varub = aggvar->glbdom.ub * scalar + constant;
4492 }
4493 else
4494 {
4495 if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
4496 varub = SCIPsetInfinity(set);
4497 else
4498 varub = aggvar->glbdom.lb * scalar + constant;
4499 if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
4500 varlb = -SCIPsetInfinity(set);
4501 else
4502 varlb = aggvar->glbdom.ub * scalar + constant;
4503 }
4504 varlb = MAX(varlb, var->glbdom.lb);
4505 varub = MIN(varub, var->glbdom.ub);
4506 SCIPvarAdjustLb(var, set, &varlb);
4507 SCIPvarAdjustUb(var, set, &varub);
4508
4509 /* check the new bounds */
4510 if( SCIPsetIsGT(set, varlb, varub) )
4511 {
4512 /* the aggregation is infeasible */
4513 *infeasible = TRUE;
4514 return SCIP_OKAY;
4515 }
4516 else if( SCIPsetIsEQ(set, varlb, varub) )
4517 {
4518 /* the aggregated variable is fixed -> fix both variables */
4519 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4520 eventfilter, eventqueue, cliquetable, varlb, infeasible, fixed) );
4521 if( !(*infeasible) )
4522 {
4523 SCIP_Bool aggfixed;
4524
4525 SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4526 eventfilter, eventqueue, cliquetable, (varlb-constant)/scalar, infeasible, &aggfixed) );
4527 assert(*fixed == aggfixed);
4528 }
4529 return SCIP_OKAY;
4530 }
4531 else
4532 {
4533 if( SCIPsetIsGT(set, varlb, var->glbdom.lb) )
4534 {
4535 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varlb) );
4536 }
4537 if( SCIPsetIsLT(set, varub, var->glbdom.ub) )
4538 {
4539 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varub) );
4540 }
4541
4542 /* update the hole list of the aggregation variable */
4543 /**@todo update hole list of aggregation variable */
4544 }
4545
4546 /* update the bounds of the aggregation variable y in x = a*y + c -> y = (x-c)/a */
4547 if( scalar > 0.0 )
4548 {
4549 if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
4550 aggvarlb = -SCIPsetInfinity(set);
4551 else
4552 aggvarlb = (var->glbdom.lb - constant) / scalar;
4553 if( SCIPsetIsInfinity(set, var->glbdom.ub) )
4554 aggvarub = SCIPsetInfinity(set);
4555 else
4556 aggvarub = (var->glbdom.ub - constant) / scalar;
4557 }
4558 else
4559 {
4560 if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
4561 aggvarub = SCIPsetInfinity(set);
4562 else
4563 aggvarub = (var->glbdom.lb - constant) / scalar;
4564 if( SCIPsetIsInfinity(set, var->glbdom.ub) )
4565 aggvarlb = -SCIPsetInfinity(set);
4566 else
4567 aggvarlb = (var->glbdom.ub - constant) / scalar;
4568 }
4569 aggvarlb = MAX(aggvarlb, aggvar->glbdom.lb);
4570 aggvarub = MIN(aggvarub, aggvar->glbdom.ub);
4571 SCIPvarAdjustLb(aggvar, set, &aggvarlb);
4572 SCIPvarAdjustUb(aggvar, set, &aggvarub);
4573
4574 /* check the new bounds */
4575 if( SCIPsetIsGT(set, aggvarlb, aggvarub) )
4576 {
4577 /* the aggregation is infeasible */
4578 *infeasible = TRUE;
4579 return SCIP_OKAY;
4580 }
4581 else if( SCIPsetIsEQ(set, aggvarlb, aggvarub) )
4582 {
4583 /* the aggregation variable is fixed -> fix both variables */
4584 SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4585 eventfilter, eventqueue, cliquetable, aggvarlb, infeasible, fixed) );
4586 if( !(*infeasible) )
4587 {
4588 SCIP_Bool varfixed;
4589
4590 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4591 eventfilter, eventqueue, cliquetable, aggvarlb * scalar + constant, infeasible, &varfixed) );
4592 assert(*fixed == varfixed);
4593 }
4594 return SCIP_OKAY;
4595 }
4596 else
4597 {
4598 SCIP_Real oldbd;
4599 if( SCIPsetIsGT(set, aggvarlb, aggvar->glbdom.lb) )
4600 {
4601 oldbd = aggvar->glbdom.lb;
4602 SCIP_CALL( SCIPvarChgLbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarlb) );
4603 aggvarbdschanged = !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.lb);
4604 }
4605 if( SCIPsetIsLT(set, aggvarub, aggvar->glbdom.ub) )
4606 {
4607 oldbd = aggvar->glbdom.ub;
4608 SCIP_CALL( SCIPvarChgUbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarub) );
4609 aggvarbdschanged = aggvarbdschanged || !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.ub);
4610 }
4611
4612 /* update the hole list of the aggregation variable */
4613 /**@todo update hole list of aggregation variable */
4614 }
4615 }
4616 while( aggvarbdschanged );
4617
4618 SCIPsetDebugMsg(set, " new bounds: <%s> [%g,%g] <%s> [%g,%g]\n",
4619 var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);
4620
4621 return SCIP_OKAY;
4622 }
4623
4624 /** converts loose variable into aggregated variable */
SCIPvarAggregate(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_VAR * aggvar,SCIP_Real scalar,SCIP_Real constant,SCIP_Bool * infeasible,SCIP_Bool * aggregated)4625 SCIP_RETCODE SCIPvarAggregate(
4626 SCIP_VAR* var, /**< loose problem variable */
4627 BMS_BLKMEM* blkmem, /**< block memory */
4628 SCIP_SET* set, /**< global SCIP settings */
4629 SCIP_STAT* stat, /**< problem statistics */
4630 SCIP_PROB* transprob, /**< tranformed problem data */
4631 SCIP_PROB* origprob, /**< original problem data */
4632 SCIP_PRIMAL* primal, /**< primal data */
4633 SCIP_TREE* tree, /**< branch and bound tree */
4634 SCIP_REOPT* reopt, /**< reoptimization data structure */
4635 SCIP_LP* lp, /**< current LP data */
4636 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
4637 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
4638 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
4639 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
4640 SCIP_VAR* aggvar, /**< loose variable y in aggregation x = a*y + c */
4641 SCIP_Real scalar, /**< multiplier a in aggregation x = a*y + c */
4642 SCIP_Real constant, /**< constant shift c in aggregation x = a*y + c */
4643 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
4644 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
4645 )
4646 {
4647 SCIP_VAR** vars;
4648 SCIP_Real* coefs;
4649 SCIP_Real* constants;
4650 SCIP_Real obj;
4651 SCIP_Real branchfactor;
4652 SCIP_Bool fixed;
4653 int branchpriority;
4654 int nlocksdown[NLOCKTYPES];
4655 int nlocksup[NLOCKTYPES];
4656 int nvbds;
4657 int i;
4658 int j;
4659
4660 assert(var != NULL);
4661 assert(aggvar != NULL);
4662 assert(var->scip == set->scip);
4663 assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
4664 assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
4665 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
4666 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
4667 assert(infeasible != NULL);
4668 assert(aggregated != NULL);
4669
4670 /* check aggregation on debugging solution */
4671 SCIP_CALL( SCIPdebugCheckAggregation(set, var, &aggvar, &scalar, constant, 1) ); /*lint !e506 !e774*/
4672
4673 *infeasible = FALSE;
4674 *aggregated = FALSE;
4675
4676 /* get active problem variable of aggregation variable */
4677 SCIP_CALL( SCIPvarGetProbvarSum(&aggvar, set, &scalar, &constant) );
4678
4679 /* aggregation is a fixing, if the scalar is zero */
4680 if( SCIPsetIsZero(set, scalar) )
4681 {
4682 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand, eventfilter,
4683 eventqueue, cliquetable, constant, infeasible, aggregated) );
4684 return SCIP_OKAY;
4685 }
4686
4687 /* don't perform the aggregation if the aggregation variable is multi-aggregated itself */
4688 if( SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_MULTAGGR )
4689 return SCIP_OKAY;
4690
4691 /**@todo currently we don't perform the aggregation if the aggregation variable has a non-empty hole list; this
4692 * should be changed in the future
4693 */
4694 if( SCIPvarGetHolelistGlobal(var) != NULL )
4695 return SCIP_OKAY;
4696
4697 assert(aggvar->glbdom.lb == aggvar->locdom.lb); /*lint !e777*/
4698 assert(aggvar->glbdom.ub == aggvar->locdom.ub); /*lint !e777*/
4699 assert(SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_LOOSE);
4700
4701 SCIPsetDebugMsg(set, "aggregate variable <%s>[%g,%g] == %g*<%s>[%g,%g] %+g\n", var->name, var->glbdom.lb, var->glbdom.ub,
4702 scalar, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub, constant);
4703
4704 /* if variable and aggregation variable are equal, the variable can be fixed: x == a*x + c => x == c/(1-a) */
4705 if( var == aggvar )
4706 {
4707 if( SCIPsetIsEQ(set, scalar, 1.0) )
4708 *infeasible = !SCIPsetIsZero(set, constant);
4709 else
4710 {
4711 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
4712 eventfilter, eventqueue, cliquetable, constant/(1.0-scalar), infeasible, aggregated) );
4713 }
4714 return SCIP_OKAY;
4715 }
4716
4717 /* tighten the bounds of aggregated and aggregation variable */
4718 SCIP_CALL( varUpdateAggregationBounds(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
4719 branchcand, eventfilter, eventqueue, cliquetable, aggvar, scalar, constant, infeasible, &fixed) );
4720 if( *infeasible || fixed )
4721 {
4722 *aggregated = fixed;
4723 return SCIP_OKAY;
4724 }
4725
4726 /* delete implications and variable bounds of the aggregated variable from other variables, but keep them in the
4727 * aggregated variable
4728 */
4729 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, FALSE) );
4730 assert(var->cliquelist == NULL);
4731
4732 /* set the aggregated variable's objective value to 0.0 */
4733 obj = var->obj;
4734 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
4735
4736 /* unlock all locks */
4737 for( i = 0; i < NLOCKTYPES; i++ )
4738 {
4739 nlocksdown[i] = var->nlocksdown[i];
4740 nlocksup[i] = var->nlocksup[i];
4741
4742 var->nlocksdown[i] = 0;
4743 var->nlocksup[i] = 0;
4744 }
4745
4746 /* check, if variable should be used as NEGATED variable of the aggregation variable */
4747 if( SCIPvarIsBinary(var) && SCIPvarIsBinary(aggvar)
4748 && var->negatedvar == NULL && aggvar->negatedvar == NULL
4749 && SCIPsetIsEQ(set, scalar, -1.0) && SCIPsetIsEQ(set, constant, 1.0) )
4750 {
4751 /* link both variables as negation pair */
4752 var->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
4753 var->data.negate.constant = 1.0;
4754 var->negatedvar = aggvar;
4755 aggvar->negatedvar = var;
4756
4757 /* copy doNotMultiaggr status */
4758 aggvar->donotmultaggr |= var->donotmultaggr;
4759
4760 /* mark both variables to be non-deletable */
4761 SCIPvarMarkNotDeletable(var);
4762 SCIPvarMarkNotDeletable(aggvar);
4763 }
4764 else
4765 {
4766 /* convert variable into aggregated variable */
4767 var->varstatus = SCIP_VARSTATUS_AGGREGATED; /*lint !e641*/
4768 var->data.aggregate.var = aggvar;
4769 var->data.aggregate.scalar = scalar;
4770 var->data.aggregate.constant = constant;
4771
4772 /* copy doNotMultiaggr status */
4773 aggvar->donotmultaggr |= var->donotmultaggr;
4774
4775 /* mark both variables to be non-deletable */
4776 SCIPvarMarkNotDeletable(var);
4777 SCIPvarMarkNotDeletable(aggvar);
4778 }
4779
4780 /* make aggregated variable a parent of the aggregation variable */
4781 SCIP_CALL( varAddParent(aggvar, blkmem, set, var) );
4782
4783 /* relock the variable, thus increasing the locks of the aggregation variable */
4784 for( i = 0; i < NLOCKTYPES; i++ )
4785 {
4786 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
4787 }
4788
4789 /* move the variable bounds to the aggregation variable:
4790 * - add all variable bounds again to the variable, thus adding it to the aggregation variable
4791 * - free the variable bounds data structures
4792 */
4793 if( var->vlbs != NULL )
4794 {
4795 nvbds = SCIPvboundsGetNVbds(var->vlbs);
4796 vars = SCIPvboundsGetVars(var->vlbs);
4797 coefs = SCIPvboundsGetCoefs(var->vlbs);
4798 constants = SCIPvboundsGetConstants(var->vlbs);
4799 for( i = 0; i < nvbds && !(*infeasible); ++i )
4800 {
4801 SCIP_CALL( SCIPvarAddVlb(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
4802 eventqueue, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
4803 }
4804 }
4805 if( var->vubs != NULL )
4806 {
4807 nvbds = SCIPvboundsGetNVbds(var->vubs);
4808 vars = SCIPvboundsGetVars(var->vubs);
4809 coefs = SCIPvboundsGetCoefs(var->vubs);
4810 constants = SCIPvboundsGetConstants(var->vubs);
4811 for( i = 0; i < nvbds && !(*infeasible); ++i )
4812 {
4813 SCIP_CALL( SCIPvarAddVub(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
4814 eventqueue, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
4815 }
4816 }
4817 SCIPvboundsFree(&var->vlbs, blkmem);
4818 SCIPvboundsFree(&var->vubs, blkmem);
4819
4820 /* move the implications to the aggregation variable:
4821 * - add all implications again to the variable, thus adding it to the aggregation variable
4822 * - free the implications data structures
4823 */
4824 if( var->implics != NULL && SCIPvarGetType(aggvar) == SCIP_VARTYPE_BINARY )
4825 {
4826 assert(SCIPvarIsBinary(var));
4827 for( i = 0; i < 2; ++i )
4828 {
4829 SCIP_VAR** implvars;
4830 SCIP_BOUNDTYPE* impltypes;
4831 SCIP_Real* implbounds;
4832 int nimpls;
4833
4834 nimpls = SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i);
4835 implvars = SCIPimplicsGetVars(var->implics, (SCIP_Bool)i);
4836 impltypes = SCIPimplicsGetTypes(var->implics, (SCIP_Bool)i);
4837 implbounds = SCIPimplicsGetBounds(var->implics, (SCIP_Bool)i);
4838
4839 for( j = 0; j < nimpls && !(*infeasible); ++j )
4840 {
4841 /* @todo can't we omit transitive closure, because it should already have been done when adding the
4842 * implication to the aggregated variable?
4843 */
4844 SCIP_CALL( SCIPvarAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
4845 branchcand, eventqueue, (SCIP_Bool)i, implvars[j], impltypes[j], implbounds[j], FALSE, infeasible,
4846 NULL) );
4847 assert(nimpls == SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i));
4848 }
4849 }
4850 }
4851 SCIPimplicsFree(&var->implics, blkmem);
4852
4853 /* add the history entries to the aggregation variable and clear the history of the aggregated variable */
4854 SCIPhistoryUnite(aggvar->history, var->history, scalar < 0.0);
4855 SCIPhistoryUnite(aggvar->historycrun, var->historycrun, scalar < 0.0);
4856 SCIPhistoryReset(var->history);
4857 SCIPhistoryReset(var->historycrun);
4858
4859 /* update flags of aggregation variable */
4860 aggvar->removable &= var->removable;
4861
4862 /* update branching factors and priorities of both variables to be the maximum of both variables */
4863 branchfactor = MAX(aggvar->branchfactor, var->branchfactor);
4864 branchpriority = MAX(aggvar->branchpriority, var->branchpriority);
4865 SCIP_CALL( SCIPvarChgBranchFactor(aggvar, set, branchfactor) );
4866 SCIP_CALL( SCIPvarChgBranchPriority(aggvar, branchpriority) );
4867 SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
4868 SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );
4869
4870 /* update branching direction of both variables to agree to a single direction */
4871 if( scalar >= 0.0 )
4872 {
4873 if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
4874 {
4875 SCIP_CALL( SCIPvarChgBranchDirection(var, (SCIP_BRANCHDIR)aggvar->branchdirection) );
4876 }
4877 else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
4878 {
4879 SCIP_CALL( SCIPvarChgBranchDirection(aggvar, (SCIP_BRANCHDIR)var->branchdirection) );
4880 }
4881 else if( var->branchdirection != aggvar->branchdirection )
4882 {
4883 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
4884 }
4885 }
4886 else
4887 {
4888 if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
4889 {
4890 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIPbranchdirOpposite((SCIP_BRANCHDIR)aggvar->branchdirection)) );
4891 }
4892 else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
4893 {
4894 SCIP_CALL( SCIPvarChgBranchDirection(aggvar, SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection)) );
4895 }
4896 else if( var->branchdirection != aggvar->branchdirection )
4897 {
4898 SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
4899 }
4900 }
4901
4902 if( var->probindex != -1 )
4903 {
4904 /* inform problem about the variable's status change */
4905 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
4906 }
4907
4908 /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
4909 * variable and the problem's objective offset
4910 */
4911 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
4912
4913 /* issue VARFIXED event */
4914 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 1) );
4915
4916 *aggregated = TRUE;
4917
4918 return SCIP_OKAY;
4919 }
4920
4921 /** Tries to aggregate an equality a*x + b*y == c consisting of two (implicit) integral active problem variables x and
4922 * y. An integer aggregation (i.e. integral coefficients a' and b', such that a'*x + b'*y == c') is searched.
4923 *
4924 * This can lead to the detection of infeasibility (e.g. if c' is fractional), or to a rejection of the aggregation
4925 * (denoted by aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
4926 */
4927 static
tryAggregateIntVars(SCIP_SET * set,BMS_BLKMEM * blkmem,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_VAR * varx,SCIP_VAR * vary,SCIP_Real scalarx,SCIP_Real scalary,SCIP_Real rhs,SCIP_Bool * infeasible,SCIP_Bool * aggregated)4928 SCIP_RETCODE tryAggregateIntVars(
4929 SCIP_SET* set, /**< global SCIP settings */
4930 BMS_BLKMEM* blkmem, /**< block memory */
4931 SCIP_STAT* stat, /**< problem statistics */
4932 SCIP_PROB* transprob, /**< tranformed problem data */
4933 SCIP_PROB* origprob, /**< original problem data */
4934 SCIP_PRIMAL* primal, /**< primal data */
4935 SCIP_TREE* tree, /**< branch and bound tree */
4936 SCIP_REOPT* reopt, /**< reoptimization data structure */
4937 SCIP_LP* lp, /**< current LP data */
4938 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
4939 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
4940 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
4941 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
4942 SCIP_VAR* varx, /**< integral variable x in equality a*x + b*y == c */
4943 SCIP_VAR* vary, /**< integral variable y in equality a*x + b*y == c */
4944 SCIP_Real scalarx, /**< multiplier a in equality a*x + b*y == c */
4945 SCIP_Real scalary, /**< multiplier b in equality a*x + b*y == c */
4946 SCIP_Real rhs, /**< right hand side c in equality a*x + b*y == c */
4947 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
4948 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
4949 )
4950 {
4951 SCIP_VAR* aggvar;
4952 char aggvarname[SCIP_MAXSTRLEN];
4953 SCIP_Longint scalarxn = 0;
4954 SCIP_Longint scalarxd = 0;
4955 SCIP_Longint scalaryn = 0;
4956 SCIP_Longint scalaryd = 0;
4957 SCIP_Longint a;
4958 SCIP_Longint b;
4959 SCIP_Longint c;
4960 SCIP_Longint scm;
4961 SCIP_Longint gcd;
4962 SCIP_Longint currentclass;
4963 SCIP_Longint classstep;
4964 SCIP_Longint xsol;
4965 SCIP_Longint ysol;
4966 SCIP_Bool success;
4967 SCIP_VARTYPE vartype;
4968
4969 #define MAXDNOM 1000000LL
4970
4971 assert(set != NULL);
4972 assert(blkmem != NULL);
4973 assert(stat != NULL);
4974 assert(transprob != NULL);
4975 assert(origprob != NULL);
4976 assert(tree != NULL);
4977 assert(lp != NULL);
4978 assert(cliquetable != NULL);
4979 assert(branchcand != NULL);
4980 assert(eventqueue != NULL);
4981 assert(varx != NULL);
4982 assert(vary != NULL);
4983 assert(varx != vary);
4984 assert(infeasible != NULL);
4985 assert(aggregated != NULL);
4986 assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
4987 assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
4988 assert(SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(varx) == SCIP_VARTYPE_IMPLINT);
4989 assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
4990 assert(SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_IMPLINT);
4991 assert(!SCIPsetIsZero(set, scalarx));
4992 assert(!SCIPsetIsZero(set, scalary));
4993
4994 *infeasible = FALSE;
4995 *aggregated = FALSE;
4996
4997 /* get rational representation of coefficients */
4998 success = SCIPrealToRational(scalarx, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalarxn, &scalarxd);
4999 if( success )
5000 success = SCIPrealToRational(scalary, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalaryn, &scalaryd);
5001 if( !success )
5002 return SCIP_OKAY;
5003 assert(scalarxd >= 1);
5004 assert(scalaryd >= 1);
5005
5006 /* multiply equality with smallest common denominator */
5007 scm = SCIPcalcSmaComMul(scalarxd, scalaryd);
5008 a = (scm/scalarxd)*scalarxn;
5009 b = (scm/scalaryd)*scalaryn;
5010 rhs *= scm;
5011
5012 /* divide equality by the greatest common divisor of a and b */
5013 gcd = SCIPcalcGreComDiv(ABS(a), ABS(b));
5014 a /= gcd;
5015 b /= gcd;
5016 rhs /= gcd;
5017 assert(a != 0);
5018 assert(b != 0);
5019
5020 /* check, if right hand side is integral */
5021 if( !SCIPsetIsFeasIntegral(set, rhs) )
5022 {
5023 *infeasible = TRUE;
5024 return SCIP_OKAY;
5025 }
5026 c = (SCIP_Longint)(SCIPsetFeasFloor(set, rhs));
5027
5028 /* check that the scalar and constant in the aggregation are not too large to avoid numerical problems */
5029 if( REALABS((SCIP_Real)(c/a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
5030 || REALABS((SCIP_Real)(b)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
5031 || REALABS((SCIP_Real)(a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
5032 {
5033 return SCIP_OKAY;
5034 }
5035
5036 /* check, if we are in an easy case with either |a| = 1 or |b| = 1 */
5037 if( (a == 1 || a == -1) && SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER )
5038 {
5039 /* aggregate x = - b/a*y + c/a */
5040 /*lint --e{653}*/
5041 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5042 branchcand, eventfilter, eventqueue, vary, (SCIP_Real)(-b/a), (SCIP_Real)(c/a), infeasible, aggregated) );
5043 assert(*aggregated);
5044 return SCIP_OKAY;
5045 }
5046 if( (b == 1 || b == -1) && SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER )
5047 {
5048 /* aggregate y = - a/b*x + c/b */
5049 /*lint --e{653}*/
5050 SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5051 branchcand, eventfilter, eventqueue, varx, (SCIP_Real)(-a/b), (SCIP_Real)(c/b), infeasible, aggregated) );
5052 assert(*aggregated);
5053 return SCIP_OKAY;
5054 }
5055
5056 /* Both variables are integers, their coefficients are not multiples of each other, and they don't have any
5057 * common divisor. Let (x',y') be a solution of the equality
5058 * a*x + b*y == c -> a*x == c - b*y
5059 * Then x = -b*z + x', y = a*z + y' with z integral gives all solutions to the equality.
5060 */
5061
5062 /* find initial solution (x',y'):
5063 * - find y' such that c - b*y' is a multiple of a
5064 * - start in equivalence class c%a
5065 * - step through classes, where each step increases class number by (-b)%a, until class 0 is visited
5066 * - if equivalence class 0 is visited, we are done: y' equals the number of steps taken
5067 * - because a and b don't have a common divisor, each class is visited at most once, and at most a-1 steps are needed
5068 * - calculate x' with x' = (c - b*y')/a (which must be integral)
5069 *
5070 * Algorithm works for a > 0 only.
5071 */
5072 if( a < 0 )
5073 {
5074 a = -a;
5075 b = -b;
5076 c = -c;
5077 }
5078 assert(a > 0);
5079
5080 /* search upwards from ysol = 0 */
5081 ysol = 0;
5082 currentclass = c % a;
5083 if( currentclass < 0 )
5084 currentclass += a;
5085 assert(0 <= currentclass && currentclass < a);
5086
5087 classstep = (-b) % a;
5088
5089 if( classstep < 0 )
5090 classstep += a;
5091 assert(0 <= classstep && classstep < a);
5092
5093 while( currentclass != 0 )
5094 {
5095 assert(0 <= currentclass && currentclass < a);
5096 currentclass += classstep;
5097 if( currentclass >= a )
5098 currentclass -= a;
5099 ysol++;
5100 }
5101 assert(ysol < a);
5102 assert(((c - b*ysol) % a) == 0);
5103
5104 xsol = (c - b*ysol)/a;
5105
5106 /* determine variable type for new artificial variable:
5107 *
5108 * if both variables are implicit integer the new variable can be implicit too, because the integer implication on
5109 * these both variables should be enforced by some other variables, otherwise the new variable needs to be of
5110 * integral type
5111 */
5112 vartype = ((SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER)
5113 ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_IMPLINT);
5114
5115 /* feasible solutions are (x,y) = (x',y') + z*(-b,a)
5116 * - create new integer variable z with infinite bounds
5117 * - aggregate variable x = -b*z + x'
5118 * - aggregate variable y = a*z + y'
5119 * - the bounds of z are calculated automatically during aggregation
5120 */
5121 (void) SCIPsnprintf(aggvarname, SCIP_MAXSTRLEN, "agg%d", stat->nvaridx);
5122 SCIP_CALL( SCIPvarCreateTransformed(&aggvar, blkmem, set, stat,
5123 aggvarname, -SCIPsetInfinity(set), SCIPsetInfinity(set), 0.0, vartype,
5124 SCIPvarIsInitial(varx) || SCIPvarIsInitial(vary), SCIPvarIsRemovable(varx) && SCIPvarIsRemovable(vary),
5125 NULL, NULL, NULL, NULL, NULL) );
5126
5127 SCIP_CALL( SCIPprobAddVar(transprob, blkmem, set, lp, branchcand, eventfilter, eventqueue, aggvar) );
5128
5129 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5130 branchcand, eventfilter, eventqueue, aggvar, (SCIP_Real)(-b), (SCIP_Real)xsol, infeasible, aggregated) );
5131 assert(*aggregated || *infeasible);
5132
5133 if( !(*infeasible) )
5134 {
5135 SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5136 branchcand, eventfilter, eventqueue, aggvar, (SCIP_Real)a, (SCIP_Real)ysol, infeasible, aggregated) );
5137 assert(*aggregated || *infeasible);
5138 }
5139
5140 /* release z */
5141 SCIP_CALL( SCIPvarRelease(&aggvar, blkmem, set, eventqueue, lp) );
5142
5143 return SCIP_OKAY; /*lint !e438*/
5144 }
5145
5146 /** performs second step of SCIPaggregateVars():
5147 * the variable to be aggregated is chosen among active problem variables x' and y', preferring a less strict variable
5148 * type as aggregation variable (i.e. continuous variables are preferred over implicit integers, implicit integers
5149 * or integers over binaries). If none of the variables is continuous, it is tried to find an integer
5150 * aggregation (i.e. integral coefficients a'' and b'', such that a''*x' + b''*y' == c''). This can lead to
5151 * the detection of infeasibility (e.g. if c'' is fractional), or to a rejection of the aggregation (denoted by
5152 * aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
5153 *
5154 * @todo check for fixings, infeasibility, bound changes, or domain holes:
5155 * a) if there is no easy aggregation and we have one binary variable and another integer/implicit/binary variable
5156 * b) for implicit integer variables with fractional aggregation scalar (we cannot (for technical reasons) and do
5157 * not want to aggregate implicit integer variables, since we loose the corresponding divisibility property)
5158 */
SCIPvarTryAggregateVars(SCIP_SET * set,BMS_BLKMEM * blkmem,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_VAR * varx,SCIP_VAR * vary,SCIP_Real scalarx,SCIP_Real scalary,SCIP_Real rhs,SCIP_Bool * infeasible,SCIP_Bool * aggregated)5159 SCIP_RETCODE SCIPvarTryAggregateVars(
5160 SCIP_SET* set, /**< global SCIP settings */
5161 BMS_BLKMEM* blkmem, /**< block memory */
5162 SCIP_STAT* stat, /**< problem statistics */
5163 SCIP_PROB* transprob, /**< tranformed problem data */
5164 SCIP_PROB* origprob, /**< original problem data */
5165 SCIP_PRIMAL* primal, /**< primal data */
5166 SCIP_TREE* tree, /**< branch and bound tree */
5167 SCIP_REOPT* reopt, /**< reoptimization data structure */
5168 SCIP_LP* lp, /**< current LP data */
5169 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5170 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
5171 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
5172 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5173 SCIP_VAR* varx, /**< variable x in equality a*x + b*y == c */
5174 SCIP_VAR* vary, /**< variable y in equality a*x + b*y == c */
5175 SCIP_Real scalarx, /**< multiplier a in equality a*x + b*y == c */
5176 SCIP_Real scalary, /**< multiplier b in equality a*x + b*y == c */
5177 SCIP_Real rhs, /**< right hand side c in equality a*x + b*y == c */
5178 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
5179 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
5180 )
5181 {
5182 SCIP_Bool easyaggr;
5183 SCIP_Real maxscalar;
5184 SCIP_Real absquot;
5185
5186 assert(set != NULL);
5187 assert(blkmem != NULL);
5188 assert(stat != NULL);
5189 assert(transprob != NULL);
5190 assert(origprob != NULL);
5191 assert(tree != NULL);
5192 assert(lp != NULL);
5193 assert(cliquetable != NULL);
5194 assert(branchcand != NULL);
5195 assert(eventqueue != NULL);
5196 assert(varx != NULL);
5197 assert(vary != NULL);
5198 assert(varx != vary);
5199 assert(infeasible != NULL);
5200 assert(aggregated != NULL);
5201 assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
5202 assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
5203 assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
5204 assert(!SCIPsetIsZero(set, scalarx));
5205 assert(!SCIPsetIsZero(set, scalary));
5206
5207 *infeasible = FALSE;
5208 *aggregated = FALSE;
5209
5210 absquot = REALABS(scalarx / scalary);
5211 maxscalar = SCIPsetFeastol(set) / SCIPsetEpsilon(set);
5212 maxscalar = MAX(maxscalar, 1.0);
5213
5214 if( absquot > maxscalar || absquot < 1 / maxscalar )
5215 return SCIP_OKAY;
5216
5217 /* prefer aggregating the variable of more general type (preferred aggregation variable is varx) */
5218 if( SCIPvarGetType(vary) > SCIPvarGetType(varx) ||
5219 (SCIPvarGetType(vary) == SCIPvarGetType(varx) && !SCIPvarIsBinary(vary) && SCIPvarIsBinary(varx)) )
5220 {
5221 SCIP_VAR* var;
5222 SCIP_Real scalar;
5223
5224 /* switch the variables, such that varx is the variable of more general type (cont > implint > int > bin) */
5225 var = vary;
5226 vary = varx;
5227 varx = var;
5228 scalar = scalary;
5229 scalary = scalarx;
5230 scalarx = scalar;
5231 }
5232
5233 /* don't aggregate if the aggregation would lead to a binary variable aggregated to a non-binary variable */
5234 if( SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary) )
5235 return SCIP_OKAY;
5236
5237 assert(SCIPvarGetType(varx) >= SCIPvarGetType(vary));
5238
5239 /* figure out, which variable should be aggregated */
5240 easyaggr = FALSE;
5241
5242 /* check if it is an easy aggregation that means:
5243 *
5244 * a*x + b*y == c -> x == -b/a * y + c/a iff |b/a| > feastol and |a/b| > feastol
5245 */
5246 if( !SCIPsetIsFeasZero(set, scalary/scalarx) && !SCIPsetIsFeasZero(set, scalarx/scalary) )
5247 {
5248 if( SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(vary) < SCIP_VARTYPE_CONTINUOUS )
5249 {
5250 easyaggr = TRUE;
5251 }
5252 else if( SCIPsetIsFeasIntegral(set, scalary/scalarx) )
5253 {
5254 easyaggr = TRUE;
5255 }
5256 else if( SCIPsetIsFeasIntegral(set, scalarx/scalary) && SCIPvarGetType(vary) == SCIPvarGetType(varx) )
5257 {
5258 /* we have an easy aggregation if we flip the variables x and y */
5259 SCIP_VAR* var;
5260 SCIP_Real scalar;
5261
5262 /* switch the variables, such that varx is the aggregated variable */
5263 var = vary;
5264 vary = varx;
5265 varx = var;
5266 scalar = scalary;
5267 scalary = scalarx;
5268 scalarx = scalar;
5269 easyaggr = TRUE;
5270 }
5271 else if( SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS )
5272 {
5273 /* the aggregation is still easy if both variables are continuous */
5274 assert(SCIPvarGetType(vary) == SCIP_VARTYPE_CONTINUOUS); /* otherwise we are in the first case */
5275 easyaggr = TRUE;
5276 }
5277 }
5278
5279 /* did we find an "easy" aggregation? */
5280 if( easyaggr )
5281 {
5282 SCIP_Real scalar;
5283 SCIP_Real constant;
5284
5285 assert(SCIPvarGetType(varx) >= SCIPvarGetType(vary));
5286
5287 /* calculate aggregation scalar and constant: a*x + b*y == c => x == -b/a * y + c/a */
5288 scalar = -scalary/scalarx;
5289 constant = rhs/scalarx;
5290
5291 if( REALABS(constant) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
5292 return SCIP_OKAY;
5293
5294 /* check aggregation for integer feasibility */
5295 if( SCIPvarGetType(varx) != SCIP_VARTYPE_CONTINUOUS
5296 && SCIPvarGetType(vary) != SCIP_VARTYPE_CONTINUOUS
5297 && SCIPsetIsFeasIntegral(set, scalar) && !SCIPsetIsFeasIntegral(set, constant) )
5298 {
5299 *infeasible = TRUE;
5300 return SCIP_OKAY;
5301 }
5302
5303 /* if the aggregation scalar is fractional, we cannot (for technical reasons) and do not want to aggregate implicit integer variables,
5304 * since then we would loose the corresponding divisibility property
5305 */
5306 assert(SCIPvarGetType(varx) != SCIP_VARTYPE_IMPLINT || SCIPsetIsFeasIntegral(set, scalar));
5307
5308 /* aggregate the variable */
5309 SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5310 branchcand, eventfilter, eventqueue, vary, scalar, constant, infeasible, aggregated) );
5311 assert(*aggregated || *infeasible);
5312 }
5313 else if( (SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(varx) == SCIP_VARTYPE_IMPLINT)
5314 && (SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vary) == SCIP_VARTYPE_IMPLINT) )
5315 {
5316 /* the variables are both integral: we have to try to find an integer aggregation */
5317 SCIP_CALL( tryAggregateIntVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
5318 branchcand, eventfilter, eventqueue, varx, vary, scalarx, scalary, rhs, infeasible, aggregated) );
5319 }
5320
5321 return SCIP_OKAY;
5322 }
5323
5324 /** converts variable into multi-aggregated variable */
SCIPvarMultiaggregate(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,int naggvars,SCIP_VAR ** aggvars,SCIP_Real * scalars,SCIP_Real constant,SCIP_Bool * infeasible,SCIP_Bool * aggregated)5325 SCIP_RETCODE SCIPvarMultiaggregate(
5326 SCIP_VAR* var, /**< problem variable */
5327 BMS_BLKMEM* blkmem, /**< block memory */
5328 SCIP_SET* set, /**< global SCIP settings */
5329 SCIP_STAT* stat, /**< problem statistics */
5330 SCIP_PROB* transprob, /**< tranformed problem data */
5331 SCIP_PROB* origprob, /**< original problem data */
5332 SCIP_PRIMAL* primal, /**< primal data */
5333 SCIP_TREE* tree, /**< branch and bound tree */
5334 SCIP_REOPT* reopt, /**< reoptimization data structure */
5335 SCIP_LP* lp, /**< current LP data */
5336 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5337 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
5338 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
5339 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5340 int naggvars, /**< number n of variables in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5341 SCIP_VAR** aggvars, /**< variables y_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5342 SCIP_Real* scalars, /**< multipliers a_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5343 SCIP_Real constant, /**< constant shift c in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
5344 SCIP_Bool* infeasible, /**< pointer to store whether the aggregation is infeasible */
5345 SCIP_Bool* aggregated /**< pointer to store whether the aggregation was successful */
5346 )
5347 {
5348 SCIP_VAR** tmpvars;
5349 SCIP_Real* tmpscalars;
5350 SCIP_Real obj;
5351 SCIP_Real branchfactor;
5352 int branchpriority;
5353 SCIP_BRANCHDIR branchdirection;
5354 int nlocksdown[NLOCKTYPES];
5355 int nlocksup[NLOCKTYPES];
5356 int v;
5357 SCIP_Real tmpconstant;
5358 SCIP_Real tmpscalar;
5359 int ntmpvars;
5360 int tmpvarssize;
5361 int tmprequiredsize;
5362 int i;
5363
5364 assert(var != NULL);
5365 assert(var->scip == set->scip);
5366 assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
5367 assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
5368 assert(naggvars == 0 || aggvars != NULL);
5369 assert(naggvars == 0 || scalars != NULL);
5370 assert(infeasible != NULL);
5371 assert(aggregated != NULL);
5372
5373 SCIPsetDebugMsg(set, "trying multi-aggregating variable <%s> == ...%d vars... %+g\n", var->name, naggvars, constant);
5374
5375 /* check multi-aggregation on debugging solution */
5376 SCIP_CALL( SCIPdebugCheckAggregation(set, var, aggvars, scalars, constant, naggvars) ); /*lint !e506 !e774*/
5377
5378 *infeasible = FALSE;
5379 *aggregated = FALSE;
5380
5381 switch( SCIPvarGetStatus(var) )
5382 {
5383 case SCIP_VARSTATUS_ORIGINAL:
5384 if( var->data.original.transvar == NULL )
5385 {
5386 SCIPerrorMessage("cannot multi-aggregate an untransformed original variable\n");
5387 return SCIP_INVALIDDATA;
5388 }
5389 SCIP_CALL( SCIPvarMultiaggregate(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
5390 reopt, lp, cliquetable, branchcand, eventfilter, eventqueue, naggvars, aggvars, scalars, constant, infeasible, aggregated) );
5391 break;
5392
5393 case SCIP_VARSTATUS_LOOSE:
5394 assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
5395
5396 /* check if we would create a self-reference */
5397 ntmpvars = naggvars;
5398 tmpvarssize = naggvars;
5399 tmpconstant = constant;
5400 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpvars, aggvars, ntmpvars) );
5401 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpscalars, scalars, ntmpvars) );
5402
5403 /* get all active variables for multi-aggregation */
5404 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize, FALSE) );
5405 if( tmprequiredsize > tmpvarssize )
5406 {
5407 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpvars, tmpvarssize, tmprequiredsize) );
5408 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize, tmprequiredsize) );
5409 tmpvarssize = tmprequiredsize;
5410 SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize, FALSE) );
5411 assert( tmprequiredsize <= tmpvarssize );
5412 }
5413
5414 tmpscalar = 0.0;
5415
5416 /* iterate over all active variables of the multi-aggregation and filter all variables which are equal to the
5417 * possible multi-aggregated variable
5418 */
5419 for( v = ntmpvars - 1; v >= 0; --v )
5420 {
5421 assert(tmpvars[v] != NULL);
5422 assert(SCIPvarGetStatus(tmpvars[v]) == SCIP_VARSTATUS_LOOSE);
5423
5424 if( tmpvars[v]->index == var->index )
5425 {
5426 tmpscalar += tmpscalars[v];
5427 tmpvars[v] = tmpvars[ntmpvars - 1];
5428 tmpscalars[v] = tmpscalars[ntmpvars - 1];
5429 --ntmpvars;
5430 }
5431 }
5432
5433 /* this means that x = x + a_1*y_1 + ... + a_n*y_n + c */
5434 if( SCIPsetIsEQ(set, tmpscalar, 1.0) )
5435 {
5436 if( ntmpvars == 0 )
5437 {
5438 if( SCIPsetIsZero(set, tmpconstant) ) /* x = x */
5439 {
5440 SCIPsetDebugMsg(set, "Possible multi-aggregation was completely resolved and detected to be redundant.\n");
5441 goto TERMINATE;
5442 }
5443 else /* 0 = c and c != 0 */
5444 {
5445 SCIPsetDebugMsg(set, "Multi-aggregation was completely resolved and led to infeasibility.\n");
5446 *infeasible = TRUE;
5447 goto TERMINATE;
5448 }
5449 }
5450 else if( ntmpvars == 1 ) /* 0 = a*y + c => y = -c/a */
5451 {
5452 assert(tmpscalars[0] != 0.0);
5453 assert(tmpvars[0] != NULL);
5454
5455 SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(tmpvars[0]), -constant/tmpscalars[0]);
5456 SCIP_CALL( SCIPvarFix(tmpvars[0], blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
5457 branchcand, eventfilter, eventqueue, cliquetable, -constant/tmpscalars[0], infeasible, aggregated) );
5458 goto TERMINATE;
5459 }
5460 else if( ntmpvars == 2 ) /* 0 = a_1*y_1 + a_2*y_2 + c => y_1 = -a_2/a_1 * y_2 - c/a_1 */
5461 {
5462 /* both variables are different active problem variables, and both scalars are non-zero: try to aggregate them */
5463
5464 SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
5465 SCIPvarGetName(tmpvars[0]), SCIPvarGetName(tmpvars[1]), tmpscalars[0], tmpscalars[1], -tmpconstant);
5466
5467 SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
5468 cliquetable, branchcand, eventfilter, eventqueue, tmpvars[0], tmpvars[1], tmpscalars[0],
5469 tmpscalars[1], -tmpconstant, infeasible, aggregated) );
5470
5471 goto TERMINATE;
5472 }
5473 else
5474 /* @todo: it is possible to multi-aggregate another variable, does it make sense?,
5475 * rest looks like 0 = a_1*y_1 + ... + a_n*y_n + c and has at least three variables
5476 */
5477 goto TERMINATE;
5478 }
5479 /* this means that x = b*x + a_1*y_1 + ... + a_n*y_n + c */
5480 else if( !SCIPsetIsZero(set, tmpscalar) )
5481 {
5482 tmpscalar = 1 - tmpscalar;
5483 tmpconstant /= tmpscalar;
5484 for( v = ntmpvars - 1; v >= 0; --v )
5485 tmpscalars[v] /= tmpscalar;
5486 }
5487
5488 /* check, if we are in one of the simple cases */
5489 if( ntmpvars == 0 )
5490 {
5491 SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(var), tmpconstant);
5492 SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
5493 eventfilter, eventqueue, cliquetable, tmpconstant, infeasible, aggregated) );
5494 goto TERMINATE;
5495 }
5496
5497 /* if only one aggregation variable is left, we perform a normal aggregation instead of a multi-aggregation */
5498 if( ntmpvars == 1 )
5499 {
5500 SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
5501 SCIPvarGetName(var), SCIPvarGetName(tmpvars[0]), 1.0, -tmpscalars[0], tmpconstant);
5502
5503 SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
5504 cliquetable, branchcand, eventfilter, eventqueue, var, tmpvars[0], 1.0, -tmpscalars[0], tmpconstant,
5505 infeasible, aggregated) );
5506
5507 goto TERMINATE;
5508 }
5509
5510 /**@todo currently we don't perform the multi aggregation if the multi aggregation variable has a non
5511 * empty hole list; this should be changed in the future */
5512 if( SCIPvarGetHolelistGlobal(var) != NULL )
5513 goto TERMINATE;
5514
5515 /* if the variable is not allowed to be multi-aggregated */
5516 if( SCIPvarDoNotMultaggr(var) )
5517 {
5518 SCIPsetDebugMsg(set, "variable is not allowed to be multi-aggregated.\n");
5519 goto TERMINATE;
5520 }
5521
5522 /* if the variable to be multi-aggregated has implications or variable bounds (i.e. is the implied variable or
5523 * variable bound variable of another variable), we have to remove it from the other variables implications or
5524 * variable bounds
5525 */
5526 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
5527 assert(var->vlbs == NULL);
5528 assert(var->vubs == NULL);
5529 assert(var->implics == NULL);
5530 assert(var->cliquelist == NULL);
5531
5532 /* set the aggregated variable's objective value to 0.0 */
5533 obj = var->obj;
5534 SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );
5535
5536 /* since we change the variable type form loose to multi aggregated, we have to adjust the number of loose
5537 * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
5538 * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
5539 * objective of this variable is set to zero
5540 */
5541 SCIPlpDecNLoosevars(lp);
5542
5543 /* unlock all rounding locks */
5544 for( i = 0; i < NLOCKTYPES; i++ )
5545 {
5546 nlocksdown[i] = var->nlocksdown[i];
5547 nlocksup[i] = var->nlocksup[i];
5548
5549 var->nlocksdown[i] = 0;
5550 var->nlocksup[i] = 0;
5551 }
5552
5553 /* convert variable into multi-aggregated variable */
5554 var->varstatus = SCIP_VARSTATUS_MULTAGGR; /*lint !e641*/
5555 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.vars, tmpvars, ntmpvars) );
5556 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.scalars, tmpscalars, ntmpvars) );
5557 var->data.multaggr.constant = tmpconstant;
5558 var->data.multaggr.nvars = ntmpvars;
5559 var->data.multaggr.varssize = ntmpvars;
5560
5561 /* mark variable to be non-deletable */
5562 SCIPvarMarkNotDeletable(var);
5563
5564 /* relock the variable, thus increasing the locks of the aggregation variables */
5565 for( i = 0; i < NLOCKTYPES; i++ )
5566 {
5567 SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
5568 }
5569
5570 /* update flags and branching factors and priorities of aggregation variables;
5571 * update preferred branching direction of all aggregation variables that don't have a preferred direction yet
5572 */
5573 branchfactor = var->branchfactor;
5574 branchpriority = var->branchpriority;
5575 branchdirection = (SCIP_BRANCHDIR)var->branchdirection;
5576
5577 for( v = 0; v < ntmpvars; ++v )
5578 {
5579 assert(tmpvars[v] != NULL);
5580 tmpvars[v]->removable &= var->removable;
5581 branchfactor = MAX(tmpvars[v]->branchfactor, branchfactor);
5582 branchpriority = MAX(tmpvars[v]->branchpriority, branchpriority);
5583
5584 /* mark variable to be non-deletable */
5585 SCIPvarMarkNotDeletable(tmpvars[v]);
5586 }
5587 for( v = 0; v < ntmpvars; ++v )
5588 {
5589 SCIP_CALL( SCIPvarChgBranchFactor(tmpvars[v], set, branchfactor) );
5590 SCIP_CALL( SCIPvarChgBranchPriority(tmpvars[v], branchpriority) );
5591 if( (SCIP_BRANCHDIR)tmpvars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
5592 {
5593 if( tmpscalars[v] >= 0.0 )
5594 {
5595 SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], branchdirection) );
5596 }
5597 else
5598 {
5599 SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], SCIPbranchdirOpposite(branchdirection)) );
5600 }
5601 }
5602 }
5603 SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
5604 SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );
5605
5606 if( var->probindex != -1 )
5607 {
5608 /* inform problem about the variable's status change */
5609 SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
5610 }
5611
5612 /* issue VARFIXED event */
5613 SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 2) );
5614
5615 /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
5616 * variables and the problem's objective offset
5617 */
5618 SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventfilter, eventqueue, obj) );
5619
5620 *aggregated = TRUE;
5621
5622 TERMINATE:
5623 BMSfreeBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize);
5624 BMSfreeBlockMemoryArray(blkmem, &tmpvars, tmpvarssize);
5625
5626 break;
5627
5628 case SCIP_VARSTATUS_COLUMN:
5629 SCIPerrorMessage("cannot multi-aggregate a column variable\n");
5630 return SCIP_INVALIDDATA;
5631
5632 case SCIP_VARSTATUS_FIXED:
5633 SCIPerrorMessage("cannot multi-aggregate a fixed variable\n");
5634 return SCIP_INVALIDDATA;
5635
5636 case SCIP_VARSTATUS_AGGREGATED:
5637 SCIPerrorMessage("cannot multi-aggregate an aggregated variable\n");
5638 return SCIP_INVALIDDATA;
5639
5640 case SCIP_VARSTATUS_MULTAGGR:
5641 SCIPerrorMessage("cannot multi-aggregate a multiple aggregated variable again\n");
5642 return SCIP_INVALIDDATA;
5643
5644 case SCIP_VARSTATUS_NEGATED:
5645 /* aggregate negation variable x in x' = offset - x, instead of aggregating x' directly:
5646 * x' = a_1*y_1 + ... + a_n*y_n + c -> x = offset - x' = offset - a_1*y_1 - ... - a_n*y_n - c
5647 */
5648 assert(SCIPsetIsZero(set, var->obj));
5649 assert(var->negatedvar != NULL);
5650 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
5651 assert(var->negatedvar->negatedvar == var);
5652
5653 /* switch the signs of the aggregation scalars */
5654 for( v = 0; v < naggvars; ++v )
5655 scalars[v] *= -1.0;
5656
5657 /* perform the multi aggregation on the negation variable */
5658 SCIP_CALL( SCIPvarMultiaggregate(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
5659 cliquetable, branchcand, eventfilter, eventqueue, naggvars, aggvars, scalars,
5660 var->data.negate.constant - constant, infeasible, aggregated) );
5661
5662 /* switch the signs of the aggregation scalars again, to reset them to their original values */
5663 for( v = 0; v < naggvars; ++v )
5664 scalars[v] *= -1.0;
5665 break;
5666
5667 default:
5668 SCIPerrorMessage("unknown variable status\n");
5669 return SCIP_INVALIDDATA;
5670 }
5671
5672 return SCIP_OKAY;
5673 }
5674
5675 /** transformed variables are resolved to their active, fixed, or multi-aggregated problem variable of a variable,
5676 * or for original variables the same variable is returned
5677 */
5678 static
varGetActiveVar(SCIP_VAR * var)5679 SCIP_VAR* varGetActiveVar(
5680 SCIP_VAR* var /**< problem variable */
5681 )
5682 {
5683 SCIP_VAR* retvar;
5684
5685 assert(var != NULL);
5686
5687 retvar = var;
5688
5689 SCIPdebugMessage("get active variable of <%s>\n", var->name);
5690
5691 while( TRUE ) /*lint !e716 */
5692 {
5693 assert(retvar != NULL);
5694
5695 switch( SCIPvarGetStatus(retvar) )
5696 {
5697 case SCIP_VARSTATUS_ORIGINAL:
5698 case SCIP_VARSTATUS_LOOSE:
5699 case SCIP_VARSTATUS_COLUMN:
5700 case SCIP_VARSTATUS_FIXED:
5701 return retvar;
5702
5703 case SCIP_VARSTATUS_MULTAGGR:
5704 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
5705 if ( retvar->data.multaggr.nvars == 1 )
5706 retvar = retvar->data.multaggr.vars[0];
5707 else
5708 return retvar;
5709 break;
5710
5711 case SCIP_VARSTATUS_AGGREGATED:
5712 retvar = retvar->data.aggregate.var;
5713 break;
5714
5715 case SCIP_VARSTATUS_NEGATED:
5716 retvar = retvar->negatedvar;
5717 break;
5718
5719 default:
5720 SCIPerrorMessage("unknown variable status\n");
5721 SCIPABORT();
5722 return NULL; /*lint !e527*/
5723 }
5724 }
5725 }
5726
5727 /** returns whether variable is not allowed to be multi-aggregated */
SCIPvarDoNotMultaggr(SCIP_VAR * var)5728 SCIP_Bool SCIPvarDoNotMultaggr(
5729 SCIP_VAR* var /**< problem variable */
5730 )
5731 {
5732 SCIP_VAR* retvar;
5733
5734 assert(var != NULL);
5735
5736 retvar = varGetActiveVar(var);
5737 assert(retvar != NULL);
5738
5739 switch( SCIPvarGetStatus(retvar) )
5740 {
5741 case SCIP_VARSTATUS_ORIGINAL:
5742 case SCIP_VARSTATUS_LOOSE:
5743 case SCIP_VARSTATUS_COLUMN:
5744 case SCIP_VARSTATUS_FIXED:
5745 return retvar->donotmultaggr;
5746
5747 case SCIP_VARSTATUS_MULTAGGR:
5748 return FALSE;
5749
5750 case SCIP_VARSTATUS_AGGREGATED:
5751 case SCIP_VARSTATUS_NEGATED:
5752 default:
5753 SCIPerrorMessage("wrong variable status\n");
5754 SCIPABORT();
5755 return FALSE; /*lint !e527 */
5756 }
5757 }
5758
5759 /** gets negated variable x' = offset - x of problem variable x; the negated variable is created if not yet existing;
5760 * the negation offset of binary variables is always 1, the offset of other variables is fixed to lb + ub when the
5761 * negated variable is created
5762 */
SCIPvarNegate(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR ** negvar)5763 SCIP_RETCODE SCIPvarNegate(
5764 SCIP_VAR* var, /**< problem variable to negate */
5765 BMS_BLKMEM* blkmem, /**< block memory of transformed problem */
5766 SCIP_SET* set, /**< global SCIP settings */
5767 SCIP_STAT* stat, /**< problem statistics */
5768 SCIP_VAR** negvar /**< pointer to store the negated variable */
5769 )
5770 {
5771 assert(var != NULL);
5772 assert(var->scip == set->scip);
5773 assert(negvar != NULL);
5774
5775 /* check, if we already created the negated variable */
5776 if( var->negatedvar == NULL )
5777 {
5778 char negvarname[SCIP_MAXSTRLEN];
5779
5780 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED);
5781
5782 SCIPsetDebugMsg(set, "creating negated variable of <%s>\n", var->name);
5783
5784 /* negation is only possible for bounded variables */
5785 if( SCIPsetIsInfinity(set, -var->glbdom.lb) || SCIPsetIsInfinity(set, var->glbdom.ub) )
5786 {
5787 SCIPerrorMessage("cannot negate unbounded variable\n");
5788 return SCIP_INVALIDDATA;
5789 }
5790
5791 (void) SCIPsnprintf(negvarname, SCIP_MAXSTRLEN, "%s_neg", var->name);
5792
5793 /* create negated variable */
5794 SCIP_CALL( varCreate(negvar, blkmem, set, stat, negvarname, var->glbdom.lb, var->glbdom.ub, 0.0,
5795 SCIPvarGetType(var), var->initial, var->removable, NULL, NULL, NULL, NULL, NULL) );
5796 (*negvar)->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
5797 if( SCIPvarIsBinary(var) )
5798 (*negvar)->data.negate.constant = 1.0;
5799 else
5800 (*negvar)->data.negate.constant = var->glbdom.lb + var->glbdom.ub;
5801
5802 /* create event filter for transformed variable */
5803 if( SCIPvarIsTransformed(var) )
5804 {
5805 SCIP_CALL( SCIPeventfilterCreate(&(*negvar)->eventfilter, blkmem) );
5806 }
5807
5808 /* set the bounds corresponding to the negation variable */
5809 (*negvar)->glbdom.lb = (*negvar)->data.negate.constant - var->glbdom.ub;
5810 (*negvar)->glbdom.ub = (*negvar)->data.negate.constant - var->glbdom.lb;
5811 (*negvar)->locdom.lb = (*negvar)->data.negate.constant - var->locdom.ub;
5812 (*negvar)->locdom.ub = (*negvar)->data.negate.constant - var->locdom.lb;
5813 /**@todo create holes in the negated variable corresponding to the holes of the negation variable */
5814
5815 /* link the variables together */
5816 var->negatedvar = *negvar;
5817 (*negvar)->negatedvar = var;
5818
5819 /* mark both variables to be non-deletable */
5820 SCIPvarMarkNotDeletable(var);
5821 SCIPvarMarkNotDeletable(*negvar);
5822
5823 /* copy the branch factor and priority, and use the negative preferred branching direction */
5824 (*negvar)->branchfactor = var->branchfactor;
5825 (*negvar)->branchpriority = var->branchpriority;
5826 (*negvar)->branchdirection = SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection); /*lint !e641*/
5827
5828 /* copy doNotMultiaggr status */
5829 (*negvar)->donotmultaggr = var->donotmultaggr;
5830
5831 /* copy lazy bounds (they have to be flipped) */
5832 (*negvar)->lazylb = (*negvar)->data.negate.constant - var->lazyub;
5833 (*negvar)->lazyub = (*negvar)->data.negate.constant - var->lazylb;
5834
5835 /* make negated variable a parent of the negation variable (negated variable is captured as a parent) */
5836 SCIP_CALL( varAddParent(var, blkmem, set, *negvar) );
5837 assert((*negvar)->nuses == 1);
5838 }
5839 assert(var->negatedvar != NULL);
5840
5841 /* return the negated variable */
5842 *negvar = var->negatedvar;
5843
5844 /* exactly one variable of the negation pair has to be marked as negated variable */
5845 assert((SCIPvarGetStatus(*negvar) == SCIP_VARSTATUS_NEGATED) != (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED));
5846
5847 return SCIP_OKAY;
5848 }
5849
5850 /** informs variable that its position in problem's vars array changed */
5851 static
varSetProbindex(SCIP_VAR * var,int probindex)5852 void varSetProbindex(
5853 SCIP_VAR* var, /**< problem variable */
5854 int probindex /**< new problem index of variable (-1 for removal) */
5855 )
5856 {
5857 assert(var != NULL);
5858 assert(probindex >= 0 || var->vlbs == NULL);
5859 assert(probindex >= 0 || var->vubs == NULL);
5860 assert(probindex >= 0 || var->implics == NULL);
5861
5862 var->probindex = probindex;
5863 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
5864 {
5865 assert(var->data.col != NULL);
5866 var->data.col->var_probindex = probindex;
5867 }
5868 }
5869
5870 /** informs variable that its position in problem's vars array changed */
SCIPvarSetProbindex(SCIP_VAR * var,int probindex)5871 void SCIPvarSetProbindex(
5872 SCIP_VAR* var, /**< problem variable */
5873 int probindex /**< new problem index of variable */
5874 )
5875 {
5876 assert(var != NULL);
5877 assert(probindex >= 0);
5878
5879 varSetProbindex(var, probindex);
5880 }
5881
5882 /** gives the variable a new name
5883 *
5884 * @note the old pointer is overwritten, which might result in a memory leakage
5885 */
SCIPvarSetNamePointer(SCIP_VAR * var,const char * name)5886 void SCIPvarSetNamePointer(
5887 SCIP_VAR* var, /**< problem variable */
5888 const char* name /**< new name of variable */
5889 )
5890 {
5891 assert(var != NULL);
5892 assert(name != NULL);
5893
5894 var->name = (char*)name;
5895 }
5896
5897 /** informs variable that it will be removed from the problem; adjusts probindex and removes variable from the
5898 * implication graph;
5899 * If 'final' is TRUE, the thorough implication graph removal is not performed. Instead, only the
5900 * variable bounds and implication data structures of the variable are freed. Since in the final removal
5901 * of all variables from the transformed problem, this deletes the implication graph completely and is faster
5902 * than removing the variables one by one, each time updating all lists of the other variables.
5903 */
SCIPvarRemove(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_CLIQUETABLE * cliquetable,SCIP_SET * set,SCIP_Bool final)5904 SCIP_RETCODE SCIPvarRemove(
5905 SCIP_VAR* var, /**< problem variable */
5906 BMS_BLKMEM* blkmem, /**< block memory buffer */
5907 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
5908 SCIP_SET* set, /**< global SCIP settings */
5909 SCIP_Bool final /**< is this the final removal of all problem variables? */
5910 )
5911 {
5912 assert(SCIPvarGetProbindex(var) >= 0);
5913 assert(var->scip == set->scip);
5914
5915 /* if the variable is active in the transformed problem, remove it from the implication graph */
5916 if( SCIPvarIsTransformed(var)
5917 && (SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN) )
5918 {
5919 if( final )
5920 {
5921 /* just destroy the data structures */
5922 SCIPvboundsFree(&var->vlbs, blkmem);
5923 SCIPvboundsFree(&var->vubs, blkmem);
5924 SCIPimplicsFree(&var->implics, blkmem);
5925 }
5926 else
5927 {
5928 /* unlink the variable from all other variables' lists and free the data structures */
5929 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
5930 }
5931 }
5932
5933 /* mark the variable to be no longer a member of the problem */
5934 varSetProbindex(var, -1);
5935
5936 return SCIP_OKAY;
5937 }
5938
5939 /** marks the variable to be deleted from the problem */
SCIPvarMarkDeleted(SCIP_VAR * var)5940 void SCIPvarMarkDeleted(
5941 SCIP_VAR* var /**< problem variable */
5942 )
5943 {
5944 assert(var != NULL);
5945 assert(var->probindex != -1);
5946
5947 var->deleted = TRUE;
5948 }
5949
5950 /** marks the variable to not to be multi-aggregated */
SCIPvarMarkDoNotMultaggr(SCIP_VAR * var)5951 SCIP_RETCODE SCIPvarMarkDoNotMultaggr(
5952 SCIP_VAR* var /**< problem variable */
5953 )
5954 {
5955 SCIP_VAR* retvar;
5956
5957 assert(var != NULL);
5958
5959 retvar = varGetActiveVar(var);
5960 assert(retvar != NULL);
5961
5962 switch( SCIPvarGetStatus(retvar) )
5963 {
5964 case SCIP_VARSTATUS_ORIGINAL:
5965 case SCIP_VARSTATUS_LOOSE:
5966 case SCIP_VARSTATUS_COLUMN:
5967 case SCIP_VARSTATUS_FIXED:
5968 retvar->donotmultaggr = TRUE;
5969 break;
5970
5971 case SCIP_VARSTATUS_MULTAGGR:
5972 SCIPerrorMessage("cannot mark a multi-aggregated variable to not be multi-aggregated.\n");
5973 return SCIP_INVALIDDATA;
5974
5975 case SCIP_VARSTATUS_AGGREGATED:
5976 case SCIP_VARSTATUS_NEGATED:
5977 default:
5978 SCIPerrorMessage("wrong variable status\n");
5979 return SCIP_INVALIDDATA;
5980 }
5981
5982 return SCIP_OKAY;
5983 }
5984
5985 /** changes type of variable; cannot be called, if var belongs to a problem */
SCIPvarChgType(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_PRIMAL * primal,SCIP_LP * lp,SCIP_EVENTQUEUE * eventqueue,SCIP_VARTYPE vartype)5986 SCIP_RETCODE SCIPvarChgType(
5987 SCIP_VAR* var, /**< problem variable to change */
5988 BMS_BLKMEM* blkmem, /**< block memory */
5989 SCIP_SET* set, /**< global SCIP settings */
5990 SCIP_PRIMAL* primal, /**< primal data */
5991 SCIP_LP* lp, /**< current LP data */
5992 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
5993 SCIP_VARTYPE vartype /**< new type of variable */
5994 )
5995 {
5996 SCIP_EVENT* event;
5997 SCIP_VARTYPE oldtype;
5998
5999 assert(var != NULL);
6000
6001 SCIPdebugMessage("change type of <%s> from %d to %d\n", var->name, SCIPvarGetType(var), vartype);
6002
6003 if( var->probindex >= 0 )
6004 {
6005 SCIPerrorMessage("cannot change type of variable already in the problem\n");
6006 return SCIP_INVALIDDATA;
6007 }
6008
6009 oldtype = (SCIP_VARTYPE)var->vartype;
6010 var->vartype = vartype; /*lint !e641*/
6011
6012 if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
6013 {
6014 SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var, oldtype, vartype) );
6015 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6016 }
6017
6018 if( var->negatedvar != NULL )
6019 {
6020 assert(oldtype == (SCIP_VARTYPE)var->negatedvar->vartype
6021 || SCIPvarIsBinary(var) == SCIPvarIsBinary(var->negatedvar));
6022
6023
6024 var->negatedvar->vartype = vartype; /*lint !e641*/
6025
6026 if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
6027 {
6028 SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var->negatedvar, oldtype, vartype) );
6029 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6030 }
6031 }
6032
6033 return SCIP_OKAY;
6034 }
6035
6036 /** appends OBJCHANGED event to the event queue */
6037 static
varEventObjChanged(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_PRIMAL * primal,SCIP_LP * lp,SCIP_EVENTQUEUE * eventqueue,SCIP_Real oldobj,SCIP_Real newobj)6038 SCIP_RETCODE varEventObjChanged(
6039 SCIP_VAR* var, /**< problem variable to change */
6040 BMS_BLKMEM* blkmem, /**< block memory */
6041 SCIP_SET* set, /**< global SCIP settings */
6042 SCIP_PRIMAL* primal, /**< primal data */
6043 SCIP_LP* lp, /**< current LP data */
6044 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6045 SCIP_Real oldobj, /**< old objective value for variable */
6046 SCIP_Real newobj /**< new objective value for variable */
6047 )
6048 {
6049 SCIP_EVENT* event;
6050
6051 assert(var != NULL);
6052 assert(var->scip == set->scip);
6053 assert(var->eventfilter != NULL);
6054 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
6055 assert(SCIPvarIsTransformed(var));
6056
6057 /* In the case where the objcetive value of a variable is very close to epsilon, and it is aggregated
6058 * into a variable with a big objective value, round-off errors might make the assert oldobj != newobj fail.
6059 * Hence, we relax it by letting it pass if the variables are percieved the same and we use very large values
6060 * that make comparison with values close to epsilon inaccurate.
6061 */
6062 assert(!SCIPsetIsEQ(set, oldobj, newobj) ||
6063 (SCIPsetIsEQ(set, oldobj, newobj) && REALABS(newobj) > 1e+15 * SCIPsetEpsilon(set))
6064 );
6065
6066 SCIP_CALL( SCIPeventCreateObjChanged(&event, blkmem, var, oldobj, newobj) );
6067 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
6068
6069 return SCIP_OKAY;
6070 }
6071
6072 /** changes objective value of variable */
SCIPvarChgObj(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_PROB * prob,SCIP_PRIMAL * primal,SCIP_LP * lp,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newobj)6073 SCIP_RETCODE SCIPvarChgObj(
6074 SCIP_VAR* var, /**< variable to change */
6075 BMS_BLKMEM* blkmem, /**< block memory */
6076 SCIP_SET* set, /**< global SCIP settings */
6077 SCIP_PROB* prob, /**< problem data */
6078 SCIP_PRIMAL* primal, /**< primal data */
6079 SCIP_LP* lp, /**< current LP data */
6080 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6081 SCIP_Real newobj /**< new objective value for variable */
6082 )
6083 {
6084 SCIP_Real oldobj;
6085
6086 assert(var != NULL);
6087 assert(set != NULL);
6088 assert(var->scip == set->scip);
6089
6090 SCIPsetDebugMsg(set, "changing objective value of <%s> from %g to %g\n", var->name, var->obj, newobj);
6091
6092 if( !SCIPsetIsEQ(set, var->obj, newobj) )
6093 {
6094 switch( SCIPvarGetStatus(var) )
6095 {
6096 case SCIP_VARSTATUS_ORIGINAL:
6097 if( var->data.original.transvar != NULL )
6098 {
6099 assert(SCIPprobIsTransformed(prob));
6100
6101 SCIP_CALL( SCIPvarChgObj(var->data.original.transvar, blkmem, set, prob, primal, lp, eventqueue,
6102 (SCIP_Real) prob->objsense * newobj/prob->objscale) );
6103 }
6104 else
6105 assert(set->stage == SCIP_STAGE_PROBLEM);
6106
6107 var->obj = newobj;
6108 var->unchangedobj = newobj;
6109
6110 break;
6111
6112 case SCIP_VARSTATUS_LOOSE:
6113 case SCIP_VARSTATUS_COLUMN:
6114 oldobj = var->obj;
6115 var->obj = newobj;
6116
6117 /* update unchanged objective value of variable */
6118 if( !lp->divingobjchg )
6119 var->unchangedobj = newobj;
6120
6121 /* update the number of variables with non-zero objective coefficient;
6122 * we only want to do the update, if the variable is added to the problem;
6123 * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
6124 */
6125 if( SCIPvarIsActive(var) )
6126 SCIPprobUpdateNObjVars(prob, set, oldobj, var->obj);
6127
6128 SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
6129 break;
6130
6131 case SCIP_VARSTATUS_FIXED:
6132 case SCIP_VARSTATUS_AGGREGATED:
6133 case SCIP_VARSTATUS_MULTAGGR:
6134 case SCIP_VARSTATUS_NEGATED:
6135 SCIPerrorMessage("cannot change objective value of a fixed, aggregated, multi-aggregated, or negated variable\n");
6136 return SCIP_INVALIDDATA;
6137
6138 default:
6139 SCIPerrorMessage("unknown variable status\n");
6140 return SCIP_INVALIDDATA;
6141 }
6142 }
6143
6144 return SCIP_OKAY;
6145 }
6146
6147 /** adds value to objective value of variable */
SCIPvarAddObj(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_PROB * transprob,SCIP_PROB * origprob,SCIP_PRIMAL * primal,SCIP_TREE * tree,SCIP_REOPT * reopt,SCIP_LP * lp,SCIP_EVENTFILTER * eventfilter,SCIP_EVENTQUEUE * eventqueue,SCIP_Real addobj)6148 SCIP_RETCODE SCIPvarAddObj(
6149 SCIP_VAR* var, /**< variable to change */
6150 BMS_BLKMEM* blkmem, /**< block memory */
6151 SCIP_SET* set, /**< global SCIP settings */
6152 SCIP_STAT* stat, /**< problem statistics */
6153 SCIP_PROB* transprob, /**< transformed problem data */
6154 SCIP_PROB* origprob, /**< original problem data */
6155 SCIP_PRIMAL* primal, /**< primal data */
6156 SCIP_TREE* tree, /**< branch and bound tree */
6157 SCIP_REOPT* reopt, /**< reoptimization data structure */
6158 SCIP_LP* lp, /**< current LP data */
6159 SCIP_EVENTFILTER* eventfilter, /**< event filter for global (not variable dependent) events */
6160 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6161 SCIP_Real addobj /**< additional objective value for variable */
6162 )
6163 {
6164 assert(var != NULL);
6165 assert(set != NULL);
6166 assert(var->scip == set->scip);
6167 assert(set->stage < SCIP_STAGE_INITSOLVE);
6168
6169 SCIPsetDebugMsg(set, "adding %g to objective value %g of <%s>\n", addobj, var->obj, var->name);
6170
6171 if( !SCIPsetIsZero(set, addobj) )
6172 {
6173 SCIP_Real oldobj;
6174 int i;
6175
6176 switch( SCIPvarGetStatus(var) )
6177 {
6178 case SCIP_VARSTATUS_ORIGINAL:
6179 if( var->data.original.transvar != NULL )
6180 {
6181 SCIP_CALL( SCIPvarAddObj(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
6182 reopt, lp, eventfilter, eventqueue, (SCIP_Real) transprob->objsense * addobj/transprob->objscale) );
6183 }
6184 else
6185 assert(set->stage == SCIP_STAGE_PROBLEM);
6186
6187 var->obj += addobj;
6188 var->unchangedobj += addobj;
6189 assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));
6190
6191 break;
6192
6193 case SCIP_VARSTATUS_LOOSE:
6194 case SCIP_VARSTATUS_COLUMN:
6195 oldobj = var->obj;
6196 var->obj += addobj;
6197
6198 /* update unchanged objective value of variable */
6199 if( !lp->divingobjchg )
6200 {
6201 var->unchangedobj += addobj;
6202 assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));
6203 }
6204
6205 /* update the number of variables with non-zero objective coefficient;
6206 * we only want to do the update, if the variable is added to the problem;
6207 * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
6208 */
6209 if( SCIPvarIsActive(var) )
6210 SCIPprobUpdateNObjVars(transprob, set, oldobj, var->obj);
6211
6212 SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
6213 break;
6214
6215 case SCIP_VARSTATUS_FIXED:
6216 assert(SCIPsetIsEQ(set, var->locdom.lb, var->locdom.ub));
6217 SCIPprobAddObjoffset(transprob, var->locdom.lb * addobj);
6218 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6219 break;
6220
6221 case SCIP_VARSTATUS_AGGREGATED:
6222 /* x = a*y + c -> add a*addobj to obj. val. of y, and c*addobj to obj. offset of problem */
6223 SCIPprobAddObjoffset(transprob, var->data.aggregate.constant * addobj);
6224 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6225 SCIP_CALL( SCIPvarAddObj(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
6226 lp, eventfilter, eventqueue, var->data.aggregate.scalar * addobj) );
6227 break;
6228
6229 case SCIP_VARSTATUS_MULTAGGR:
6230 assert(!var->donotmultaggr);
6231 /* x = a_1*y_1 + ... + a_n*y_n + c -> add a_i*addobj to obj. val. of y_i, and c*addobj to obj. offset */
6232 SCIPprobAddObjoffset(transprob, var->data.multaggr.constant * addobj);
6233 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6234 for( i = 0; i < var->data.multaggr.nvars; ++i )
6235 {
6236 SCIP_CALL( SCIPvarAddObj(var->data.multaggr.vars[i], blkmem, set, stat, transprob, origprob, primal, tree,
6237 reopt, lp, eventfilter, eventqueue, var->data.multaggr.scalars[i] * addobj) );
6238 }
6239 break;
6240
6241 case SCIP_VARSTATUS_NEGATED:
6242 /* x' = offset - x -> add -addobj to obj. val. of x and offset*addobj to obj. offset of problem */
6243 assert(var->negatedvar != NULL);
6244 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
6245 assert(var->negatedvar->negatedvar == var);
6246 SCIPprobAddObjoffset(transprob, var->data.negate.constant * addobj);
6247 SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventfilter, eventqueue, transprob, origprob, tree, reopt, lp) );
6248 SCIP_CALL( SCIPvarAddObj(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
6249 eventfilter, eventqueue, -addobj) );
6250 break;
6251
6252 default:
6253 SCIPerrorMessage("unknown variable status\n");
6254 return SCIP_INVALIDDATA;
6255 }
6256 }
6257
6258 return SCIP_OKAY;
6259 }
6260
6261 /** changes objective value of variable in current dive */
SCIPvarChgObjDive(SCIP_VAR * var,SCIP_SET * set,SCIP_LP * lp,SCIP_Real newobj)6262 SCIP_RETCODE SCIPvarChgObjDive(
6263 SCIP_VAR* var, /**< problem variable to change */
6264 SCIP_SET* set, /**< global SCIP settings */
6265 SCIP_LP* lp, /**< current LP data */
6266 SCIP_Real newobj /**< new objective value for variable */
6267 )
6268 {
6269 assert(var != NULL);
6270 assert(set != NULL);
6271 assert(var->scip == set->scip);
6272 assert(lp != NULL);
6273
6274 SCIPsetDebugMsg(set, "changing objective of <%s> to %g in current dive\n", var->name, newobj);
6275
6276 if( SCIPsetIsZero(set, newobj) )
6277 newobj = 0.0;
6278
6279 /* change objective value of attached variables */
6280 switch( SCIPvarGetStatus(var) )
6281 {
6282 case SCIP_VARSTATUS_ORIGINAL:
6283 assert(var->data.original.transvar != NULL);
6284 SCIP_CALL( SCIPvarChgObjDive(var->data.original.transvar, set, lp, newobj) );
6285 break;
6286
6287 case SCIP_VARSTATUS_COLUMN:
6288 assert(var->data.col != NULL);
6289 SCIP_CALL( SCIPcolChgObj(var->data.col, set, lp, newobj) );
6290 break;
6291
6292 case SCIP_VARSTATUS_LOOSE:
6293 case SCIP_VARSTATUS_FIXED:
6294 /* nothing to do here: only the constant shift in objective function would change */
6295 break;
6296
6297 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
6298 assert(var->data.aggregate.var != NULL);
6299 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
6300 SCIP_CALL( SCIPvarChgObjDive(var->data.aggregate.var, set, lp, newobj / var->data.aggregate.scalar) );
6301 /* the constant can be ignored, because it would only affect the objective shift */
6302 break;
6303
6304 case SCIP_VARSTATUS_MULTAGGR:
6305 SCIPerrorMessage("cannot change diving objective value of a multi-aggregated variable\n");
6306 return SCIP_INVALIDDATA;
6307
6308 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
6309 assert(var->negatedvar != NULL);
6310 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
6311 assert(var->negatedvar->negatedvar == var);
6312 SCIP_CALL( SCIPvarChgObjDive(var->negatedvar, set, lp, -newobj) );
6313 /* the offset can be ignored, because it would only affect the objective shift */
6314 break;
6315
6316 default:
6317 SCIPerrorMessage("unknown variable status\n");
6318 return SCIP_INVALIDDATA;
6319 }
6320
6321 return SCIP_OKAY;
6322 }
6323
6324 /** adjust lower bound to integral value, if variable is integral */
SCIPvarAdjustLb(SCIP_VAR * var,SCIP_SET * set,SCIP_Real * lb)6325 void SCIPvarAdjustLb(
6326 SCIP_VAR* var, /**< problem variable */
6327 SCIP_SET* set, /**< global SCIP settings */
6328 SCIP_Real* lb /**< pointer to lower bound to adjust */
6329 )
6330 {
6331 assert(var != NULL);
6332 assert(set != NULL);
6333 assert(var->scip == set->scip);
6334 assert(lb != NULL);
6335
6336 SCIPsetDebugMsg(set, "adjust lower bound %g of <%s>\n", *lb, var->name);
6337
6338 *lb = adjustedLb(set, SCIPvarGetType(var), *lb);
6339 }
6340
6341 /** adjust upper bound to integral value, if variable is integral */
SCIPvarAdjustUb(SCIP_VAR * var,SCIP_SET * set,SCIP_Real * ub)6342 void SCIPvarAdjustUb(
6343 SCIP_VAR* var, /**< problem variable */
6344 SCIP_SET* set, /**< global SCIP settings */
6345 SCIP_Real* ub /**< pointer to upper bound to adjust */
6346 )
6347 {
6348 assert(var != NULL);
6349 assert(set != NULL);
6350 assert(var->scip == set->scip);
6351 assert(ub != NULL);
6352
6353 SCIPsetDebugMsg(set, "adjust upper bound %g of <%s>\n", *ub, var->name);
6354
6355 *ub = adjustedUb(set, SCIPvarGetType(var), *ub);
6356 }
6357
6358 /** adjust lower or upper bound to integral value, if variable is integral */
SCIPvarAdjustBd(SCIP_VAR * var,SCIP_SET * set,SCIP_BOUNDTYPE boundtype,SCIP_Real * bd)6359 void SCIPvarAdjustBd(
6360 SCIP_VAR* var, /**< problem variable */
6361 SCIP_SET* set, /**< global SCIP settings */
6362 SCIP_BOUNDTYPE boundtype, /**< type of bound to adjust */
6363 SCIP_Real* bd /**< pointer to bound to adjust */
6364 )
6365 {
6366 assert(boundtype == SCIP_BOUNDTYPE_LOWER || boundtype == SCIP_BOUNDTYPE_UPPER);
6367
6368 if( boundtype == SCIP_BOUNDTYPE_LOWER )
6369 SCIPvarAdjustLb(var, set, bd);
6370 else
6371 SCIPvarAdjustUb(var, set, bd);
6372 }
6373
6374 /** changes lower bound of original variable in original problem */
SCIPvarChgLbOriginal(SCIP_VAR * var,SCIP_SET * set,SCIP_Real newbound)6375 SCIP_RETCODE SCIPvarChgLbOriginal(
6376 SCIP_VAR* var, /**< problem variable to change */
6377 SCIP_SET* set, /**< global SCIP settings */
6378 SCIP_Real newbound /**< new bound for variable */
6379 )
6380 {
6381 int i;
6382
6383 assert(var != NULL);
6384 assert(!SCIPvarIsTransformed(var));
6385 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
6386 assert(set != NULL);
6387 assert(var->scip == set->scip);
6388 assert(set->stage == SCIP_STAGE_PROBLEM);
6389
6390 /* check that the bound is feasible */
6391 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, SCIPvarGetUbOriginal(var)));
6392 /* adjust bound to integral value if variable is of integral type */
6393 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
6394
6395 if( SCIPsetIsZero(set, newbound) )
6396 newbound = 0.0;
6397
6398 /* original domains are only stored for ORIGINAL variables, not for NEGATED */
6399 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
6400 {
6401 SCIPsetDebugMsg(set, "changing original lower bound of <%s> from %g to %g\n",
6402 var->name, var->data.original.origdom.lb, newbound);
6403
6404 if( SCIPsetIsEQ(set, var->data.original.origdom.lb, newbound) )
6405 return SCIP_OKAY;
6406
6407 /* change the bound */
6408 var->data.original.origdom.lb = newbound;
6409 }
6410 else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
6411 {
6412 assert( var->negatedvar != NULL );
6413 SCIP_CALL( SCIPvarChgUbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
6414 }
6415
6416 /* process parent variables */
6417 for( i = 0; i < var->nparentvars; ++i )
6418 {
6419 SCIP_VAR* parentvar;
6420
6421 parentvar = var->parentvars[i];
6422 assert(parentvar != NULL);
6423 assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
6424 assert(parentvar->negatedvar == var);
6425 assert(var->negatedvar == parentvar);
6426
6427 SCIP_CALL( SCIPvarChgUbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
6428 }
6429
6430 return SCIP_OKAY;
6431 }
6432
6433 /** changes upper bound of original variable in original problem */
SCIPvarChgUbOriginal(SCIP_VAR * var,SCIP_SET * set,SCIP_Real newbound)6434 SCIP_RETCODE SCIPvarChgUbOriginal(
6435 SCIP_VAR* var, /**< problem variable to change */
6436 SCIP_SET* set, /**< global SCIP settings */
6437 SCIP_Real newbound /**< new bound for variable */
6438 )
6439 {
6440 int i;
6441
6442 assert(var != NULL);
6443 assert(!SCIPvarIsTransformed(var));
6444 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
6445 assert(set != NULL);
6446 assert(var->scip == set->scip);
6447 assert(set->stage == SCIP_STAGE_PROBLEM);
6448
6449 /* check that the bound is feasible */
6450 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, SCIPvarGetLbOriginal(var)));
6451 /* adjust bound to integral value if variable is of integral type */
6452 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
6453
6454 if( SCIPsetIsZero(set, newbound) )
6455 newbound = 0.0;
6456
6457 /* original domains are only stored for ORIGINAL variables, not for NEGATED */
6458 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
6459 {
6460 SCIPsetDebugMsg(set, "changing original upper bound of <%s> from %g to %g\n",
6461 var->name, var->data.original.origdom.ub, newbound);
6462
6463 if( SCIPsetIsEQ(set, var->data.original.origdom.ub, newbound) )
6464 return SCIP_OKAY;
6465
6466 /* change the bound */
6467 var->data.original.origdom.ub = newbound;
6468 }
6469 else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
6470 {
6471 assert( var->negatedvar != NULL );
6472 SCIP_CALL( SCIPvarChgLbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
6473 }
6474
6475 /* process parent variables */
6476 for( i = 0; i < var->nparentvars; ++i )
6477 {
6478 SCIP_VAR* parentvar;
6479
6480 parentvar = var->parentvars[i];
6481 assert(parentvar != NULL);
6482 assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
6483 assert(parentvar->negatedvar == var);
6484 assert(var->negatedvar == parentvar);
6485
6486 SCIP_CALL( SCIPvarChgLbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
6487 }
6488
6489 return SCIP_OKAY;
6490 }
6491
6492 /** appends GLBCHANGED event to the event queue */
6493 static
varEventGlbChanged(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real oldbound,SCIP_Real newbound)6494 SCIP_RETCODE varEventGlbChanged(
6495 SCIP_VAR* var, /**< problem variable to change */
6496 BMS_BLKMEM* blkmem, /**< block memory */
6497 SCIP_SET* set, /**< global SCIP settings */
6498 SCIP_LP* lp, /**< current LP data */
6499 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
6500 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6501 SCIP_Real oldbound, /**< old lower bound for variable */
6502 SCIP_Real newbound /**< new lower bound for variable */
6503 )
6504 {
6505 assert(var != NULL);
6506 assert(var->eventfilter != NULL);
6507 assert(SCIPvarIsTransformed(var));
6508 assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
6509 assert(set != NULL);
6510 assert(var->scip == set->scip);
6511
6512 /* check, if the variable is being tracked for bound changes
6513 * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
6514 */
6515 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GLBCHANGED) != 0)
6516 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
6517 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
6518 {
6519 SCIP_EVENT* event;
6520
6521 SCIPsetDebugMsg(set, "issue GLBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
6522
6523 SCIP_CALL( SCIPeventCreateGlbChanged(&event, blkmem, var, oldbound, newbound) );
6524 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
6525 }
6526
6527 return SCIP_OKAY;
6528 }
6529
6530 /** appends GUBCHANGED event to the event queue */
6531 static
varEventGubChanged(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real oldbound,SCIP_Real newbound)6532 SCIP_RETCODE varEventGubChanged(
6533 SCIP_VAR* var, /**< problem variable to change */
6534 BMS_BLKMEM* blkmem, /**< block memory */
6535 SCIP_SET* set, /**< global SCIP settings */
6536 SCIP_LP* lp, /**< current LP data */
6537 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
6538 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6539 SCIP_Real oldbound, /**< old lower bound for variable */
6540 SCIP_Real newbound /**< new lower bound for variable */
6541 )
6542 {
6543 assert(var != NULL);
6544 assert(var->eventfilter != NULL);
6545 assert(SCIPvarIsTransformed(var));
6546 assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
6547 assert(set != NULL);
6548 assert(var->scip == set->scip);
6549
6550 /* check, if the variable is being tracked for bound changes
6551 * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
6552 */
6553 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GUBCHANGED) != 0)
6554 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
6555 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
6556 {
6557 SCIP_EVENT* event;
6558
6559 SCIPsetDebugMsg(set, "issue GUBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
6560
6561 SCIP_CALL( SCIPeventCreateGubChanged(&event, blkmem, var, oldbound, newbound) );
6562 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
6563 }
6564
6565 return SCIP_OKAY;
6566 }
6567
6568 /** appends GHOLEADDED event to the event queue */
6569 static
varEventGholeAdded(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_Real left,SCIP_Real right)6570 SCIP_RETCODE varEventGholeAdded(
6571 SCIP_VAR* var, /**< problem variable to change */
6572 BMS_BLKMEM* blkmem, /**< block memory */
6573 SCIP_SET* set, /**< global SCIP settings */
6574 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
6575 SCIP_Real left, /**< left bound of open interval in new hole */
6576 SCIP_Real right /**< right bound of open interval in new hole */
6577 )
6578 {
6579 assert(var != NULL);
6580 assert(var->eventfilter != NULL);
6581 assert(SCIPvarIsTransformed(var));
6582 assert(set != NULL);
6583 assert(var->scip == set->scip);
6584 assert(SCIPsetIsLT(set, left, right));
6585
6586 /* check, if the variable is being tracked for bound changes */
6587 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GHOLEADDED) != 0) )
6588 {
6589 SCIP_EVENT* event;
6590
6591 SCIPsetDebugMsg(set, "issue GHOLEADDED event for variable <%s>: (%.15g,%.15g)\n", var->name, left, right);
6592
6593 SCIP_CALL( SCIPeventCreateGholeAdded(&event, blkmem, var, left, right) );
6594 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
6595 }
6596
6597 return SCIP_OKAY;
6598 }
6599
6600 /** increases root bound change statistics after a global bound change */
6601 static
varIncRootboundchgs(SCIP_VAR * var,SCIP_SET * set,SCIP_STAT * stat)6602 void varIncRootboundchgs(
6603 SCIP_VAR* var, /**< problem variable to change */
6604 SCIP_SET* set, /**< global SCIP settings */
6605 SCIP_STAT* stat /**< problem statistics */
6606 )
6607 {
6608 assert(var != NULL);
6609 assert(set != NULL);
6610 assert(var->scip == set->scip);
6611 assert(stat != NULL);
6612
6613 if( SCIPvarIsActive(var) && SCIPvarIsTransformed(var) && set->stage == SCIP_STAGE_SOLVING )
6614 {
6615 stat->nrootboundchgs++;
6616 stat->nrootboundchgsrun++;
6617 if( SCIPvarIsIntegral(var) && SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) )
6618 {
6619 stat->nrootintfixings++;
6620 stat->nrootintfixingsrun++;
6621 }
6622 }
6623 }
6624
6625 /* forward declaration, because both methods call each other recursively */
6626
6627 /* performs the current change in upper bound, changes all parents accordingly */
6628 static
6629 SCIP_RETCODE varProcessChgUbGlobal(
6630 SCIP_VAR* var, /**< problem variable to change */
6631 BMS_BLKMEM* blkmem, /**< block memory */
6632 SCIP_SET* set, /**< global SCIP settings */
6633 SCIP_STAT* stat, /**< problem statistics */
6634 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6635 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
6636 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
6637 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6638 SCIP_Real newbound /**< new bound for variable */
6639 );
6640
6641 /** performs the current change in lower bound, changes all parents accordingly */
6642 static
varProcessChgLbGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real newbound)6643 SCIP_RETCODE varProcessChgLbGlobal(
6644 SCIP_VAR* var, /**< problem variable to change */
6645 BMS_BLKMEM* blkmem, /**< block memory */
6646 SCIP_SET* set, /**< global SCIP settings */
6647 SCIP_STAT* stat, /**< problem statistics */
6648 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6649 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
6650 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
6651 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6652 SCIP_Real newbound /**< new bound for variable */
6653 )
6654 {
6655 SCIP_VAR* parentvar;
6656 SCIP_Real oldbound;
6657 int i;
6658
6659 assert(var != NULL);
6660 /* local domains can violate global bounds but not more than feasibility epsilon */
6661 assert(SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb));
6662 assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
6663 assert(blkmem != NULL);
6664 assert(set != NULL);
6665 assert(var->scip == set->scip);
6666 assert(stat != NULL);
6667
6668 /* adjust bound to integral value if variable is of integral type */
6669 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
6670
6671 /* check that the bound is feasible */
6672 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound > var->glbdom.ub )
6673 {
6674 /* due to numerics we only want to be feasible in feasibility tolerance */
6675 assert(SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
6676 newbound = var->glbdom.ub;
6677 }
6678 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
6679
6680 assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/
6681
6682 SCIPsetDebugMsg(set, "process changing global lower bound of <%s> from %f to %f\n", var->name, var->glbdom.lb, newbound);
6683
6684 if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) ) /*lint !e777*/
6685 return SCIP_OKAY;
6686
6687 /* check bound on debugging solution */
6688 SCIP_CALL( SCIPdebugCheckLbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/
6689
6690 /* change the bound */
6691 oldbound = var->glbdom.lb;
6692 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
6693 var->glbdom.lb = newbound;
6694 assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
6695 assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );
6696
6697 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
6698 {
6699 /* merges overlapping holes into single holes, moves bounds respectively */
6700 domMerge(&var->glbdom, blkmem, set, &newbound, NULL);
6701 }
6702
6703 /* update the root bound changes counters */
6704 varIncRootboundchgs(var, set, stat);
6705
6706 /* update the lbchginfos array by replacing worse local bounds with the new global bound and changing the
6707 * redundant bound changes to be branching decisions
6708 */
6709 for( i = 0; i < var->nlbchginfos; ++i )
6710 {
6711 assert(var->lbchginfos[i].var == var);
6712
6713 if( var->lbchginfos[i].oldbound < var->glbdom.lb )
6714 {
6715 SCIPsetDebugMsg(set, " -> adjust lower bound change <%s>: %g -> %g due to new global lower bound %g\n",
6716 SCIPvarGetName(var), var->lbchginfos[i].oldbound, var->lbchginfos[i].newbound, var->glbdom.lb);
6717 var->lbchginfos[i].oldbound = var->glbdom.lb;
6718 if( SCIPsetIsLE(set, var->lbchginfos[i].newbound, var->glbdom.lb) )
6719 {
6720 /* this bound change is redundant due to the new global bound */
6721 var->lbchginfos[i].newbound = var->glbdom.lb;
6722 var->lbchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
6723 var->lbchginfos[i].redundant = TRUE;
6724 }
6725 else
6726 break; /* from now on, the remaining local bound changes are not redundant */
6727 }
6728 else
6729 break; /* from now on, the remaining local bound changes are not redundant */
6730 }
6731
6732 /* remove redundant implications and variable bounds */
6733 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
6734 && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
6735 {
6736 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
6737 }
6738
6739 /* issue bound change event */
6740 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
6741 if( var->eventfilter != NULL )
6742 {
6743 SCIP_CALL( varEventGlbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
6744 }
6745
6746 /* process parent variables */
6747 for( i = 0; i < var->nparentvars; ++i )
6748 {
6749 parentvar = var->parentvars[i];
6750 assert(parentvar != NULL);
6751
6752 switch( SCIPvarGetStatus(parentvar) )
6753 {
6754 case SCIP_VARSTATUS_ORIGINAL:
6755 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
6756 break;
6757
6758 case SCIP_VARSTATUS_COLUMN:
6759 case SCIP_VARSTATUS_LOOSE:
6760 case SCIP_VARSTATUS_FIXED:
6761 case SCIP_VARSTATUS_MULTAGGR:
6762 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
6763 return SCIP_INVALIDDATA;
6764
6765 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
6766 assert(parentvar->data.aggregate.var == var);
6767 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
6768 {
6769 SCIP_Real parentnewbound;
6770
6771 /* a > 0 -> change lower bound of y */
6772 assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, -oldbound)
6773 || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
6774 || (SCIPsetIsZero(set, parentvar->glbdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
6775
6776 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6777 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6778 else
6779 parentnewbound = newbound;
6780 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6781 }
6782 else
6783 {
6784 SCIP_Real parentnewbound;
6785
6786 /* a < 0 -> change upper bound of y */
6787 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
6788 assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, -oldbound)
6789 || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
6790 || (SCIPsetIsZero(set, parentvar->glbdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
6791
6792 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6793 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6794 else
6795 parentnewbound = -newbound;
6796 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6797 }
6798 break;
6799
6800 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
6801 assert(parentvar->negatedvar != NULL);
6802 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
6803 assert(parentvar->negatedvar->negatedvar == parentvar);
6804 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
6805 parentvar->data.negate.constant - newbound) );
6806 break;
6807
6808 default:
6809 SCIPerrorMessage("unknown variable status\n");
6810 return SCIP_INVALIDDATA;
6811 }
6812 }
6813
6814 return SCIP_OKAY;
6815 }
6816
6817 /** performs the current change in upper bound, changes all parents accordingly */
6818 static
varProcessChgUbGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real newbound)6819 SCIP_RETCODE varProcessChgUbGlobal(
6820 SCIP_VAR* var, /**< problem variable to change */
6821 BMS_BLKMEM* blkmem, /**< block memory */
6822 SCIP_SET* set, /**< global SCIP settings */
6823 SCIP_STAT* stat, /**< problem statistics */
6824 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6825 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
6826 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
6827 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
6828 SCIP_Real newbound /**< new bound for variable */
6829 )
6830 {
6831 SCIP_VAR* parentvar;
6832 SCIP_Real oldbound;
6833 int i;
6834
6835 assert(var != NULL);
6836 /* local domains can violate global bounds but not more than feasibility epsilon */
6837 assert(SCIPsetIsFeasLE(set, var->glbdom.lb , var->locdom.lb));
6838 assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
6839 assert(blkmem != NULL);
6840 assert(set != NULL);
6841 assert(var->scip == set->scip);
6842 assert(stat != NULL);
6843
6844 /* adjust bound to integral value if variable is of integral type */
6845 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
6846
6847 /* check that the bound is feasible */
6848 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound < var->glbdom.lb )
6849 {
6850 /* due to numerics we only want to be feasible in feasibility tolerance */
6851 assert(SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
6852 newbound = var->glbdom.lb;
6853 }
6854 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
6855
6856 assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/
6857
6858 SCIPsetDebugMsg(set, "process changing global upper bound of <%s> from %f to %f\n", var->name, var->glbdom.ub, newbound);
6859
6860 if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) ) /*lint !e777*/
6861 return SCIP_OKAY;
6862
6863 /* check bound on debugging solution */
6864 SCIP_CALL( SCIPdebugCheckUbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/
6865
6866 /* change the bound */
6867 oldbound = var->glbdom.ub;
6868 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
6869 var->glbdom.ub = newbound;
6870 assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
6871 assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );
6872
6873 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
6874 {
6875 /* merges overlapping holes into single holes, moves bounds respectively */
6876 domMerge(&var->glbdom, blkmem, set, NULL, &newbound);
6877 }
6878
6879 /* update the root bound changes counters */
6880 varIncRootboundchgs(var, set, stat);
6881
6882 /* update the ubchginfos array by replacing worse local bounds with the new global bound and changing the
6883 * redundant bound changes to be branching decisions
6884 */
6885 for( i = 0; i < var->nubchginfos; ++i )
6886 {
6887 assert(var->ubchginfos[i].var == var);
6888 if( var->ubchginfos[i].oldbound > var->glbdom.ub )
6889 {
6890 SCIPsetDebugMsg(set, " -> adjust upper bound change <%s>: %g -> %g due to new global upper bound %g\n",
6891 SCIPvarGetName(var), var->ubchginfos[i].oldbound, var->ubchginfos[i].newbound, var->glbdom.ub);
6892 var->ubchginfos[i].oldbound = var->glbdom.ub;
6893 if( SCIPsetIsGE(set, var->ubchginfos[i].newbound, var->glbdom.ub) )
6894 {
6895 /* this bound change is redundant due to the new global bound */
6896 var->ubchginfos[i].newbound = var->glbdom.ub;
6897 var->ubchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
6898 var->ubchginfos[i].redundant = TRUE;
6899 }
6900 else
6901 break; /* from now on, the remaining local bound changes are not redundant */
6902 }
6903 else
6904 break; /* from now on, the remaining local bound changes are not redundant */
6905 }
6906
6907 /* remove redundant implications and variable bounds */
6908 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
6909 && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
6910 {
6911 SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
6912 }
6913
6914 /* issue bound change event */
6915 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
6916 if( var->eventfilter != NULL )
6917 {
6918 SCIP_CALL( varEventGubChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
6919 }
6920
6921 /* process parent variables */
6922 for( i = 0; i < var->nparentvars; ++i )
6923 {
6924 parentvar = var->parentvars[i];
6925 assert(parentvar != NULL);
6926
6927 switch( SCIPvarGetStatus(parentvar) )
6928 {
6929 case SCIP_VARSTATUS_ORIGINAL:
6930 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
6931 break;
6932
6933 case SCIP_VARSTATUS_COLUMN:
6934 case SCIP_VARSTATUS_LOOSE:
6935 case SCIP_VARSTATUS_FIXED:
6936 case SCIP_VARSTATUS_MULTAGGR:
6937 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
6938 return SCIP_INVALIDDATA;
6939
6940 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
6941 assert(parentvar->data.aggregate.var == var);
6942 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
6943 {
6944 SCIP_Real parentnewbound;
6945
6946 /* a > 0 -> change upper bound of y */
6947 assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, oldbound)
6948 || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub,
6949 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
6950 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6951 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6952 else
6953 parentnewbound = newbound;
6954 SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6955 }
6956 else
6957 {
6958 SCIP_Real parentnewbound;
6959
6960 /* a < 0 -> change lower bound of y */
6961 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
6962 assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, oldbound)
6963 || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb,
6964 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
6965 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
6966 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
6967 else
6968 parentnewbound = -newbound;
6969 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
6970 }
6971 break;
6972
6973 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
6974 assert(parentvar->negatedvar != NULL);
6975 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
6976 assert(parentvar->negatedvar->negatedvar == parentvar);
6977 SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
6978 parentvar->data.negate.constant - newbound) );
6979 break;
6980
6981 default:
6982 SCIPerrorMessage("unknown variable status\n");
6983 return SCIP_INVALIDDATA;
6984 }
6985 }
6986
6987 return SCIP_OKAY;
6988 }
6989
6990 /** changes global lower bound of variable; if possible, adjusts bound to integral value;
6991 * updates local lower bound if the global bound is tighter
6992 */
SCIPvarChgLbGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real newbound)6993 SCIP_RETCODE SCIPvarChgLbGlobal(
6994 SCIP_VAR* var, /**< problem variable to change */
6995 BMS_BLKMEM* blkmem, /**< block memory */
6996 SCIP_SET* set, /**< global SCIP settings */
6997 SCIP_STAT* stat, /**< problem statistics */
6998 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
6999 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7000 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7001 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7002 SCIP_Real newbound /**< new bound for variable */
7003 )
7004 {
7005 assert(var != NULL);
7006 assert(blkmem != NULL);
7007 assert(set != NULL);
7008 assert(var->scip == set->scip);
7009
7010 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7011 * of the domain within feastol
7012 */
7013 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));
7014
7015 /* adjust bound to integral value if variable is of integral type */
7016 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7017
7018 /* check that the adjusted bound is feasible
7019 * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
7020 * here because we reset bounds to their original value!
7021 */
7022 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));
7023
7024 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7025 {
7026 /* we do not want to exceed the upperbound, which could have happened due to numerics */
7027 newbound = MIN(newbound, var->glbdom.ub);
7028 }
7029 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7030
7031 /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
7032 * SCIPvarFix() allows fixings that are outside of the domain within feastol
7033 */
7034 assert(lp == NULL || SCIPsetIsFeasLE(set, var->glbdom.lb, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));
7035
7036 SCIPsetDebugMsg(set, "changing global lower bound of <%s> from %g to %g\n", var->name, var->glbdom.lb, newbound);
7037
7038 if( SCIPsetIsEQ(set, var->glbdom.lb, newbound) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) ) /*lint !e777*/
7039 return SCIP_OKAY;
7040
7041 /* change bounds of attached variables */
7042 switch( SCIPvarGetStatus(var) )
7043 {
7044 case SCIP_VARSTATUS_ORIGINAL:
7045 if( var->data.original.transvar != NULL )
7046 {
7047 SCIP_CALL( SCIPvarChgLbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
7048 cliquetable, newbound) );
7049 }
7050 else
7051 {
7052 assert(set->stage == SCIP_STAGE_PROBLEM);
7053 if( newbound > SCIPvarGetLbLocal(var) )
7054 {
7055 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7056 }
7057 SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7058 }
7059 break;
7060
7061 case SCIP_VARSTATUS_COLUMN:
7062 case SCIP_VARSTATUS_LOOSE:
7063 if( newbound > SCIPvarGetLbLocal(var) )
7064 {
7065 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7066 }
7067 SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7068 break;
7069
7070 case SCIP_VARSTATUS_FIXED:
7071 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7072 return SCIP_INVALIDDATA;
7073
7074 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7075 assert(var->data.aggregate.var != NULL);
7076 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7077 {
7078 SCIP_Real childnewbound;
7079
7080 /* a > 0 -> change lower bound of y */
7081 assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
7082 || SCIPsetIsFeasEQ(set, var->glbdom.lb,
7083 var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7084 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7085 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7086 else
7087 childnewbound = newbound;
7088 SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7089 childnewbound) );
7090 }
7091 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7092 {
7093 SCIP_Real childnewbound;
7094
7095 /* a < 0 -> change upper bound of y */
7096 assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
7097 || SCIPsetIsFeasEQ(set, var->glbdom.lb,
7098 var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7099 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7100 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7101 else
7102 childnewbound = -newbound;
7103 SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7104 childnewbound) );
7105 }
7106 else
7107 {
7108 SCIPerrorMessage("scalar is zero in aggregation\n");
7109 return SCIP_INVALIDDATA;
7110 }
7111 break;
7112
7113 case SCIP_VARSTATUS_MULTAGGR:
7114 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
7115 return SCIP_INVALIDDATA;
7116
7117 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7118 assert(var->negatedvar != NULL);
7119 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
7120 assert(var->negatedvar->negatedvar == var);
7121 SCIP_CALL( SCIPvarChgUbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7122 var->data.negate.constant - newbound) );
7123 break;
7124
7125 default:
7126 SCIPerrorMessage("unknown variable status\n");
7127 return SCIP_INVALIDDATA;
7128 }
7129
7130 return SCIP_OKAY;
7131 }
7132
7133 /** changes global upper bound of variable; if possible, adjusts bound to integral value;
7134 * updates local upper bound if the global bound is tighter
7135 */
SCIPvarChgUbGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real newbound)7136 SCIP_RETCODE SCIPvarChgUbGlobal(
7137 SCIP_VAR* var, /**< problem variable to change */
7138 BMS_BLKMEM* blkmem, /**< block memory */
7139 SCIP_SET* set, /**< global SCIP settings */
7140 SCIP_STAT* stat, /**< problem statistics */
7141 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7142 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7143 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7144 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7145 SCIP_Real newbound /**< new bound for variable */
7146 )
7147 {
7148 assert(var != NULL);
7149 assert(blkmem != NULL);
7150 assert(set != NULL);
7151 assert(var->scip == set->scip);
7152
7153 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7154 * of the domain within feastol
7155 */
7156 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));
7157
7158 /* adjust bound to integral value if variable is of integral type */
7159 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7160
7161 /* check that the adjusted bound is feasible
7162 * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
7163 * here because we reset bounds to their original value!
7164 */
7165 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));
7166
7167 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7168 {
7169 /* we do not want to undercut the lowerbound, which could have happened due to numerics */
7170 newbound = MAX(newbound, var->glbdom.lb);
7171 }
7172 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7173
7174 /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
7175 * SCIPvarFix() allows fixings that are outside of the domain within feastol
7176 */
7177 assert(lp == NULL || SCIPsetIsFeasGE(set, var->glbdom.ub, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));
7178
7179 SCIPsetDebugMsg(set, "changing global upper bound of <%s> from %g to %g\n", var->name, var->glbdom.ub, newbound);
7180
7181 if( SCIPsetIsEQ(set, var->glbdom.ub, newbound) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) ) /*lint !e777*/
7182 return SCIP_OKAY;
7183
7184 /* change bounds of attached variables */
7185 switch( SCIPvarGetStatus(var) )
7186 {
7187 case SCIP_VARSTATUS_ORIGINAL:
7188 if( var->data.original.transvar != NULL )
7189 {
7190 SCIP_CALL( SCIPvarChgUbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7191 newbound) );
7192 }
7193 else
7194 {
7195 assert(set->stage == SCIP_STAGE_PROBLEM);
7196 if( newbound < SCIPvarGetUbLocal(var) )
7197 {
7198 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7199 }
7200 SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7201 }
7202 break;
7203
7204 case SCIP_VARSTATUS_COLUMN:
7205 case SCIP_VARSTATUS_LOOSE:
7206 if( newbound < SCIPvarGetUbLocal(var) )
7207 {
7208 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7209 }
7210 SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
7211 break;
7212
7213 case SCIP_VARSTATUS_FIXED:
7214 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7215 return SCIP_INVALIDDATA;
7216
7217 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7218 assert(var->data.aggregate.var != NULL);
7219 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7220 {
7221 SCIP_Real childnewbound;
7222
7223 /* a > 0 -> change lower bound of y */
7224 assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
7225 || SCIPsetIsFeasEQ(set, var->glbdom.ub,
7226 var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7227 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7228 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7229 else
7230 childnewbound = newbound;
7231 SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7232 childnewbound) );
7233 }
7234 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7235 {
7236 SCIP_Real childnewbound;
7237
7238 /* a < 0 -> change upper bound of y */
7239 assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
7240 || SCIPsetIsFeasEQ(set, var->glbdom.ub,
7241 var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7242 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7243 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7244 else
7245 childnewbound = -newbound;
7246 SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7247 childnewbound) );
7248 }
7249 else
7250 {
7251 SCIPerrorMessage("scalar is zero in aggregation\n");
7252 return SCIP_INVALIDDATA;
7253 }
7254 break;
7255
7256 case SCIP_VARSTATUS_MULTAGGR:
7257 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
7258 return SCIP_INVALIDDATA;
7259
7260 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7261 assert(var->negatedvar != NULL);
7262 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
7263 assert(var->negatedvar->negatedvar == var);
7264 SCIP_CALL( SCIPvarChgLbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
7265 var->data.negate.constant - newbound) );
7266 break;
7267
7268 default:
7269 SCIPerrorMessage("unknown variable status\n");
7270 return SCIP_INVALIDDATA;
7271 }
7272
7273 return SCIP_OKAY;
7274 }
7275
7276 /** changes lazy lower bound of the variable, this is only possible if the variable is not in the LP yet */
SCIPvarChgLbLazy(SCIP_VAR * var,SCIP_SET * set,SCIP_Real lazylb)7277 SCIP_RETCODE SCIPvarChgLbLazy(
7278 SCIP_VAR* var, /**< problem variable */
7279 SCIP_SET* set, /**< global SCIP settings */
7280 SCIP_Real lazylb /**< the lazy lower bound to be set */
7281 )
7282 {
7283 assert(var != NULL);
7284 assert(var->probindex != -1);
7285 assert(SCIPsetIsFeasGE(set, var->glbdom.ub, lazylb));
7286 assert(SCIPsetIsFeasGE(set, var->lazyub, lazylb));
7287 assert(set != NULL);
7288 assert(var->scip == set->scip);
7289
7290 /* variable should not be in the LP */
7291 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
7292 return SCIP_INVALIDCALL;
7293
7294 var->lazylb = lazylb;
7295
7296 return SCIP_OKAY;
7297 }
7298
7299 /** changes lazy upper bound of the variable, this is only possible if the variable is not in the LP yet */
SCIPvarChgUbLazy(SCIP_VAR * var,SCIP_SET * set,SCIP_Real lazyub)7300 SCIP_RETCODE SCIPvarChgUbLazy(
7301 SCIP_VAR* var, /**< problem variable */
7302 SCIP_SET* set, /**< global SCIP settings */
7303 SCIP_Real lazyub /**< the lazy lower bound to be set */
7304 )
7305 {
7306 assert(var != NULL);
7307 assert(var->probindex != -1);
7308 assert(SCIPsetIsFeasGE(set, lazyub, var->glbdom.lb));
7309 assert(SCIPsetIsFeasGE(set, lazyub, var->lazylb));
7310 assert(set != NULL);
7311 assert(var->scip == set->scip);
7312
7313 /* variable should not be in the LP */
7314 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
7315 return SCIP_INVALIDCALL;
7316
7317 var->lazyub = lazyub;
7318
7319 return SCIP_OKAY;
7320 }
7321
7322
7323 /** changes global bound of variable; if possible, adjusts bound to integral value;
7324 * updates local bound if the global bound is tighter
7325 */
SCIPvarChgBdGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_CLIQUETABLE * cliquetable,SCIP_Real newbound,SCIP_BOUNDTYPE boundtype)7326 SCIP_RETCODE SCIPvarChgBdGlobal(
7327 SCIP_VAR* var, /**< problem variable to change */
7328 BMS_BLKMEM* blkmem, /**< block memory */
7329 SCIP_SET* set, /**< global SCIP settings */
7330 SCIP_STAT* stat, /**< problem statistics */
7331 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7332 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7333 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7334 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
7335 SCIP_Real newbound, /**< new bound for variable */
7336 SCIP_BOUNDTYPE boundtype /**< type of bound: lower or upper bound */
7337 )
7338 {
7339 /* apply bound change to the LP data */
7340 switch( boundtype )
7341 {
7342 case SCIP_BOUNDTYPE_LOWER:
7343 return SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
7344 case SCIP_BOUNDTYPE_UPPER:
7345 return SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
7346 default:
7347 SCIPerrorMessage("unknown bound type\n");
7348 return SCIP_INVALIDDATA;
7349 }
7350 }
7351
7352 /** appends LBTIGHTENED or LBRELAXED event to the event queue */
7353 static
varEventLbChanged(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real oldbound,SCIP_Real newbound)7354 SCIP_RETCODE varEventLbChanged(
7355 SCIP_VAR* var, /**< problem variable to change */
7356 BMS_BLKMEM* blkmem, /**< block memory */
7357 SCIP_SET* set, /**< global SCIP settings */
7358 SCIP_LP* lp, /**< current LP data */
7359 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
7360 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
7361 SCIP_Real oldbound, /**< old lower bound for variable */
7362 SCIP_Real newbound /**< new lower bound for variable */
7363 )
7364 {
7365 assert(var != NULL);
7366 assert(var->eventfilter != NULL);
7367 assert(SCIPvarIsTransformed(var));
7368 assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.lb || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
7369 assert(set != NULL);
7370 assert(var->scip == set->scip);
7371
7372 /* check, if the variable is being tracked for bound changes
7373 * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
7374 */
7375 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_LBCHANGED) != 0)
7376 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
7377 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
7378 {
7379 SCIP_EVENT* event;
7380
7381 SCIPsetDebugMsg(set, "issue LBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
7382
7383 SCIP_CALL( SCIPeventCreateLbChanged(&event, blkmem, var, oldbound, newbound) );
7384 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
7385 }
7386
7387 return SCIP_OKAY;
7388 }
7389
7390 /** appends UBTIGHTENED or UBRELAXED event to the event queue */
7391 static
varEventUbChanged(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real oldbound,SCIP_Real newbound)7392 SCIP_RETCODE varEventUbChanged(
7393 SCIP_VAR* var, /**< problem variable to change */
7394 BMS_BLKMEM* blkmem, /**< block memory */
7395 SCIP_SET* set, /**< global SCIP settings */
7396 SCIP_LP* lp, /**< current LP data */
7397 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
7398 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
7399 SCIP_Real oldbound, /**< old upper bound for variable */
7400 SCIP_Real newbound /**< new upper bound for variable */
7401 )
7402 {
7403 assert(var != NULL);
7404 assert(var->eventfilter != NULL);
7405 assert(SCIPvarIsTransformed(var));
7406 assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.ub || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
7407 assert(set != NULL);
7408 assert(var->scip == set->scip);
7409
7410 /* check, if the variable is being tracked for bound changes
7411 * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
7412 */
7413 if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_UBCHANGED) != 0)
7414 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
7415 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
7416 {
7417 SCIP_EVENT* event;
7418
7419 SCIPsetDebugMsg(set, "issue UBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);
7420
7421 SCIP_CALL( SCIPeventCreateUbChanged(&event, blkmem, var, oldbound, newbound) );
7422 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
7423 }
7424
7425 return SCIP_OKAY;
7426 }
7427
7428 /* forward declaration, because both methods call each other recursively */
7429
7430 /* performs the current change in upper bound, changes all parents accordingly */
7431 static
7432 SCIP_RETCODE varProcessChgUbLocal(
7433 SCIP_VAR* var, /**< problem variable to change */
7434 BMS_BLKMEM* blkmem, /**< block memory */
7435 SCIP_SET* set, /**< global SCIP settings */
7436 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7437 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7438 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7439 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7440 SCIP_Real newbound /**< new bound for variable */
7441 );
7442
7443 /** performs the current change in lower bound, changes all parents accordingly */
7444 static
varProcessChgLbLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newbound)7445 SCIP_RETCODE varProcessChgLbLocal(
7446 SCIP_VAR* var, /**< problem variable to change */
7447 BMS_BLKMEM* blkmem, /**< block memory */
7448 SCIP_SET* set, /**< global SCIP settings */
7449 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7450 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7451 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7452 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7453 SCIP_Real newbound /**< new bound for variable */
7454 )
7455 {
7456 SCIP_VAR* parentvar;
7457 SCIP_Real oldbound;
7458 int i;
7459
7460 assert(var != NULL);
7461 assert(set != NULL);
7462 assert(var->scip == set->scip);
7463 assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
7464 || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
7465 || (SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS && (SCIPsetIsIntegral(set, newbound)
7466 || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
7467 || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS);
7468
7469 /* check that the bound is feasible */
7470 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, var->glbdom.ub));
7471 /* adjust bound to integral value if variable is of integral type */
7472 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7473
7474 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7475 {
7476 /* we do not want to exceed the upper bound, which could have happened due to numerics */
7477 newbound = MIN(newbound, var->locdom.ub);
7478
7479 /* we do not want to undercut the global lower bound, which could have happened due to numerics */
7480 newbound = MAX(newbound, var->glbdom.lb);
7481 }
7482 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7483
7484 SCIPsetDebugMsg(set, "process changing lower bound of <%s> from %g to %g\n", var->name, var->locdom.lb, newbound);
7485
7486 if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && var->glbdom.lb != var->locdom.lb ) /*lint !e777*/
7487 newbound = var->glbdom.lb;
7488 else if( SCIPsetIsEQ(set, newbound, var->locdom.lb) && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) ) /*lint !e777*/
7489 return SCIP_OKAY;
7490
7491 /* change the bound */
7492 oldbound = var->locdom.lb;
7493 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->locdom.ub));
7494 var->locdom.lb = newbound;
7495
7496 /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
7497 * once update the statistic
7498 */
7499 if( stat != NULL )
7500 SCIPstatIncrement(stat, set, domchgcount);
7501
7502 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7503 {
7504 /* merges overlapping holes into single holes, moves bounds respectively */
7505 domMerge(&var->locdom, blkmem, set, &newbound, NULL);
7506 }
7507
7508 /* issue bound change event */
7509 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
7510 if( var->eventfilter != NULL )
7511 {
7512 SCIP_CALL( varEventLbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
7513 }
7514
7515 /* process parent variables */
7516 for( i = 0; i < var->nparentvars; ++i )
7517 {
7518 parentvar = var->parentvars[i];
7519 assert(parentvar != NULL);
7520
7521 switch( SCIPvarGetStatus(parentvar) )
7522 {
7523 case SCIP_VARSTATUS_ORIGINAL:
7524 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
7525 break;
7526
7527 case SCIP_VARSTATUS_COLUMN:
7528 case SCIP_VARSTATUS_LOOSE:
7529 case SCIP_VARSTATUS_FIXED:
7530 case SCIP_VARSTATUS_MULTAGGR:
7531 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
7532 return SCIP_INVALIDDATA;
7533
7534 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7535 assert(parentvar->data.aggregate.var == var);
7536 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
7537 {
7538 SCIP_Real parentnewbound;
7539
7540 /* a > 0 -> change lower bound of y */
7541 assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, -oldbound)
7542 || SCIPsetIsFeasEQ(set, parentvar->locdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
7543 || (SCIPsetIsZero(set, parentvar->locdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
7544
7545 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7546 {
7547 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7548 /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
7549 * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
7550 * as a result, the parent's lower bound is set to it's upper bound, and not above
7551 */
7552 if( parentnewbound > parentvar->glbdom.ub )
7553 {
7554 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7555 assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
7556 parentnewbound = parentvar->glbdom.ub;
7557 }
7558 }
7559 else
7560 parentnewbound = newbound;
7561 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7562 }
7563 else
7564 {
7565 SCIP_Real parentnewbound;
7566
7567 /* a < 0 -> change upper bound of y */
7568 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
7569 assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, -oldbound)
7570 || SCIPsetIsFeasEQ(set, parentvar->locdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
7571 || (SCIPsetIsZero(set, parentvar->locdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));
7572
7573 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7574 {
7575 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7576 /* if parent's new upper bound is below its lower bound, then this could be due to numerical difficulties, e.g., if numbers are large
7577 * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
7578 * as a result, the parent's upper bound is set to it's lower bound, and not below
7579 */
7580 if( parentnewbound < parentvar->glbdom.lb )
7581 {
7582 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7583 assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
7584 parentnewbound = parentvar->glbdom.lb;
7585 }
7586 }
7587 else
7588 parentnewbound = -newbound;
7589 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7590 }
7591 break;
7592
7593 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
7594 assert(parentvar->negatedvar != NULL);
7595 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
7596 assert(parentvar->negatedvar->negatedvar == parentvar);
7597 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
7598 parentvar->data.negate.constant - newbound) );
7599 break;
7600
7601 default:
7602 SCIPerrorMessage("unknown variable status\n");
7603 return SCIP_INVALIDDATA;
7604 }
7605 }
7606
7607 return SCIP_OKAY;
7608 }
7609
7610 /** performs the current change in upper bound, changes all parents accordingly */
7611 static
varProcessChgUbLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newbound)7612 SCIP_RETCODE varProcessChgUbLocal(
7613 SCIP_VAR* var, /**< problem variable to change */
7614 BMS_BLKMEM* blkmem, /**< block memory */
7615 SCIP_SET* set, /**< global SCIP settings */
7616 SCIP_STAT* stat, /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
7617 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7618 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7619 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7620 SCIP_Real newbound /**< new bound for variable */
7621 )
7622 {
7623 SCIP_VAR* parentvar;
7624 SCIP_Real oldbound;
7625 int i;
7626
7627 assert(var != NULL);
7628 assert(set != NULL);
7629 assert(var->scip == set->scip);
7630 assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
7631 || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
7632 || (SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS && (SCIPsetIsIntegral(set, newbound)
7633 || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
7634 || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS);
7635
7636 /* check that the bound is feasible */
7637 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, var->glbdom.lb));
7638 /* adjust bound to integral value if variable is of integral type */
7639 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7640
7641 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7642 {
7643 /* we do not want to undercut the lower bound, which could have happened due to numerics */
7644 newbound = MAX(newbound, var->locdom.lb);
7645
7646 /* we do not want to exceed the global upper bound, which could have happened due to numerics */
7647 newbound = MIN(newbound, var->glbdom.ub);
7648 }
7649 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7650
7651 SCIPsetDebugMsg(set, "process changing upper bound of <%s> from %g to %g\n", var->name, var->locdom.ub, newbound);
7652
7653 if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && var->glbdom.ub != var->locdom.ub ) /*lint !e777*/
7654 newbound = var->glbdom.ub;
7655 else if( SCIPsetIsEQ(set, newbound, var->locdom.ub) && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) ) /*lint !e777*/
7656 return SCIP_OKAY;
7657
7658 /* change the bound */
7659 oldbound = var->locdom.ub;
7660 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->locdom.lb));
7661 var->locdom.ub = newbound;
7662
7663 /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
7664 * once update the statistic
7665 */
7666 if( stat != NULL )
7667 SCIPstatIncrement(stat, set, domchgcount);
7668
7669 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7670 {
7671 /* merges overlapping holes into single holes, moves bounds respectively */
7672 domMerge(&var->locdom, blkmem, set, NULL, &newbound);
7673 }
7674
7675 /* issue bound change event */
7676 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
7677 if( var->eventfilter != NULL )
7678 {
7679 SCIP_CALL( varEventUbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
7680 }
7681
7682 /* process parent variables */
7683 for( i = 0; i < var->nparentvars; ++i )
7684 {
7685 parentvar = var->parentvars[i];
7686 assert(parentvar != NULL);
7687
7688 switch( SCIPvarGetStatus(parentvar) )
7689 {
7690 case SCIP_VARSTATUS_ORIGINAL:
7691 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
7692 break;
7693
7694 case SCIP_VARSTATUS_COLUMN:
7695 case SCIP_VARSTATUS_LOOSE:
7696 case SCIP_VARSTATUS_FIXED:
7697 case SCIP_VARSTATUS_MULTAGGR:
7698 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
7699 return SCIP_INVALIDDATA;
7700
7701 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7702 assert(parentvar->data.aggregate.var == var);
7703 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
7704 {
7705 SCIP_Real parentnewbound;
7706
7707 /* a > 0 -> change upper bound of x */
7708 assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, oldbound)
7709 || SCIPsetIsFeasEQ(set, parentvar->locdom.ub,
7710 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7711 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7712 {
7713 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7714 /* if parent's new upper bound is below its lower bound, then this could be due to numerical difficulties, e.g., if numbers are large
7715 * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
7716 * as a result, the parent's upper bound is set to it's lower bound, and not below
7717 */
7718 if( parentnewbound < parentvar->glbdom.lb )
7719 {
7720 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7721 assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
7722 parentnewbound = parentvar->glbdom.lb;
7723 }
7724 }
7725 else
7726 parentnewbound = newbound;
7727 SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7728 }
7729 else
7730 {
7731 SCIP_Real parentnewbound;
7732
7733 /* a < 0 -> change lower bound of x */
7734 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
7735 assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, oldbound)
7736 || SCIPsetIsFeasEQ(set, parentvar->locdom.lb,
7737 oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
7738 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7739 {
7740 parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
7741 /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
7742 * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
7743 * as a result, the parent's lower bound is set to it's upper bound, and not above
7744 */
7745 if( parentnewbound > parentvar->glbdom.ub )
7746 {
7747 /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
7748 assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
7749 parentnewbound = parentvar->glbdom.ub;
7750 }
7751 }
7752 else
7753 parentnewbound = -newbound;
7754 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
7755 }
7756 break;
7757
7758 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
7759 assert(parentvar->negatedvar != NULL);
7760 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
7761 assert(parentvar->negatedvar->negatedvar == parentvar);
7762 SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
7763 parentvar->data.negate.constant - newbound) );
7764 break;
7765
7766 default:
7767 SCIPerrorMessage("unknown variable status\n");
7768 return SCIP_INVALIDDATA;
7769 }
7770 }
7771
7772 return SCIP_OKAY;
7773 }
7774
7775 /** changes current local lower bound of variable; if possible, adjusts bound to integral value; stores inference
7776 * information in variable
7777 */
SCIPvarChgLbLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newbound)7778 SCIP_RETCODE SCIPvarChgLbLocal(
7779 SCIP_VAR* var, /**< problem variable to change */
7780 BMS_BLKMEM* blkmem, /**< block memory */
7781 SCIP_SET* set, /**< global SCIP settings */
7782 SCIP_STAT* stat, /**< problem statistics */
7783 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7784 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7785 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7786 SCIP_Real newbound /**< new bound for variable */
7787 )
7788 {
7789 assert(var != NULL);
7790 assert(blkmem != NULL);
7791 assert(set != NULL);
7792 assert(var->scip == set->scip);
7793
7794 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7795 * of the domain within feastol
7796 */
7797 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));
7798
7799 /* adjust bound to integral value if variable is of integral type */
7800 newbound = adjustedLb(set, SCIPvarGetType(var), newbound);
7801
7802 /* check that the adjusted bound is feasible */
7803 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));
7804
7805 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7806 {
7807 /* we do not want to exceed the upperbound, which could have happened due to numerics */
7808 newbound = MIN(newbound, var->locdom.ub);
7809 }
7810 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7811
7812 SCIPsetDebugMsg(set, "changing lower bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);
7813
7814 if( SCIPsetIsEQ(set, var->locdom.lb, newbound) && (!SCIPsetIsEQ(set, var->glbdom.lb, newbound) || var->locdom.lb == newbound) /*lint !e777*/
7815 && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) ) /*lint !e777*/
7816 return SCIP_OKAY;
7817
7818 /* change bounds of attached variables */
7819 switch( SCIPvarGetStatus(var) )
7820 {
7821 case SCIP_VARSTATUS_ORIGINAL:
7822 if( var->data.original.transvar != NULL )
7823 {
7824 SCIP_CALL( SCIPvarChgLbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
7825 newbound) );
7826 }
7827 else
7828 {
7829 assert(set->stage == SCIP_STAGE_PROBLEM);
7830 SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7831 }
7832 break;
7833
7834 case SCIP_VARSTATUS_COLUMN:
7835 case SCIP_VARSTATUS_LOOSE:
7836 SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7837 break;
7838
7839 case SCIP_VARSTATUS_FIXED:
7840 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7841 return SCIP_INVALIDDATA;
7842
7843 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7844 assert(var->data.aggregate.var != NULL);
7845 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7846 {
7847 SCIP_Real childnewbound;
7848
7849 /* a > 0 -> change lower bound of y */
7850 assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
7851 || SCIPsetIsFeasEQ(set, var->locdom.lb,
7852 var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7853 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7854 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7855 else
7856 childnewbound = newbound;
7857 SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
7858 childnewbound) );
7859 }
7860 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7861 {
7862 SCIP_Real childnewbound;
7863
7864 /* a < 0 -> change upper bound of y */
7865 assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
7866 || SCIPsetIsFeasEQ(set, var->locdom.lb,
7867 var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7868 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7869 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7870 else
7871 childnewbound = -newbound;
7872 SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
7873 childnewbound) );
7874 }
7875 else
7876 {
7877 SCIPerrorMessage("scalar is zero in aggregation\n");
7878 return SCIP_INVALIDDATA;
7879 }
7880 break;
7881
7882 case SCIP_VARSTATUS_MULTAGGR:
7883 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
7884 return SCIP_INVALIDDATA;
7885
7886 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
7887 assert(var->negatedvar != NULL);
7888 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
7889 assert(var->negatedvar->negatedvar == var);
7890 SCIP_CALL( SCIPvarChgUbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
7891 var->data.negate.constant - newbound) );
7892 break;
7893
7894 default:
7895 SCIPerrorMessage("unknown variable status\n");
7896 return SCIP_INVALIDDATA;
7897 }
7898
7899 return SCIP_OKAY;
7900 }
7901
7902 /** changes current local upper bound of variable; if possible, adjusts bound to integral value; stores inference
7903 * information in variable
7904 */
SCIPvarChgUbLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newbound)7905 SCIP_RETCODE SCIPvarChgUbLocal(
7906 SCIP_VAR* var, /**< problem variable to change */
7907 BMS_BLKMEM* blkmem, /**< block memory */
7908 SCIP_SET* set, /**< global SCIP settings */
7909 SCIP_STAT* stat, /**< problem statistics */
7910 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
7911 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
7912 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
7913 SCIP_Real newbound /**< new bound for variable */
7914 )
7915 {
7916 assert(var != NULL);
7917 assert(blkmem != NULL);
7918 assert(set != NULL);
7919 assert(var->scip == set->scip);
7920
7921 /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
7922 * of the domain within feastol
7923 */
7924 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));
7925
7926 /* adjust bound to integral value if variable is of integral type */
7927 newbound = adjustedUb(set, SCIPvarGetType(var), newbound);
7928
7929 /* check that the adjusted bound is feasible */
7930 assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));
7931
7932 if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
7933 {
7934 /* we do not want to undercut the lowerbound, which could have happened due to numerics */
7935 newbound = MAX(newbound, var->locdom.lb);
7936 }
7937 assert(SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPsetIsFeasIntegral(set, newbound));
7938
7939 SCIPsetDebugMsg(set, "changing upper bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);
7940
7941 if( SCIPsetIsEQ(set, var->locdom.ub, newbound) && (!SCIPsetIsEQ(set, var->glbdom.ub, newbound) || var->locdom.ub == newbound) /*lint !e777*/
7942 && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) ) /*lint !e777*/
7943 return SCIP_OKAY;
7944
7945 /* change bounds of attached variables */
7946 switch( SCIPvarGetStatus(var) )
7947 {
7948 case SCIP_VARSTATUS_ORIGINAL:
7949 if( var->data.original.transvar != NULL )
7950 {
7951 SCIP_CALL( SCIPvarChgUbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7952 }
7953 else
7954 {
7955 assert(set->stage == SCIP_STAGE_PROBLEM);
7956 SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7957 }
7958 break;
7959
7960 case SCIP_VARSTATUS_COLUMN:
7961 case SCIP_VARSTATUS_LOOSE:
7962 SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
7963 break;
7964
7965 case SCIP_VARSTATUS_FIXED:
7966 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
7967 return SCIP_INVALIDDATA;
7968
7969 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
7970 assert(var->data.aggregate.var != NULL);
7971 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
7972 {
7973 SCIP_Real childnewbound;
7974
7975 /* a > 0 -> change upper bound of y */
7976 assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
7977 || SCIPsetIsFeasEQ(set, var->locdom.ub,
7978 var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
7979 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7980 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7981 else
7982 childnewbound = newbound;
7983 SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
7984 childnewbound) );
7985 }
7986 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
7987 {
7988 SCIP_Real childnewbound;
7989
7990 /* a < 0 -> change lower bound of y */
7991 assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
7992 || SCIPsetIsFeasEQ(set, var->locdom.ub,
7993 var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
7994 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
7995 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
7996 else
7997 childnewbound = -newbound;
7998 SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
7999 childnewbound) );
8000 }
8001 else
8002 {
8003 SCIPerrorMessage("scalar is zero in aggregation\n");
8004 return SCIP_INVALIDDATA;
8005 }
8006 break;
8007
8008 case SCIP_VARSTATUS_MULTAGGR:
8009 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8010 return SCIP_INVALIDDATA;
8011
8012 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8013 assert(var->negatedvar != NULL);
8014 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8015 assert(var->negatedvar->negatedvar == var);
8016 SCIP_CALL( SCIPvarChgLbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
8017 var->data.negate.constant - newbound) );
8018 break;
8019
8020 default:
8021 SCIPerrorMessage("unknown variable status\n");
8022 return SCIP_INVALIDDATA;
8023 }
8024
8025 return SCIP_OKAY;
8026 }
8027
8028 /** changes current local bound of variable; if possible, adjusts bound to integral value; stores inference
8029 * information in variable
8030 */
SCIPvarChgBdLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_LP * lp,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Real newbound,SCIP_BOUNDTYPE boundtype)8031 SCIP_RETCODE SCIPvarChgBdLocal(
8032 SCIP_VAR* var, /**< problem variable to change */
8033 BMS_BLKMEM* blkmem, /**< block memory */
8034 SCIP_SET* set, /**< global SCIP settings */
8035 SCIP_STAT* stat, /**< problem statistics */
8036 SCIP_LP* lp, /**< current LP data, may be NULL for original variables */
8037 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage, may be NULL for original variables */
8038 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8039 SCIP_Real newbound, /**< new bound for variable */
8040 SCIP_BOUNDTYPE boundtype /**< type of bound: lower or upper bound */
8041 )
8042 {
8043 /* apply bound change to the LP data */
8044 switch( boundtype )
8045 {
8046 case SCIP_BOUNDTYPE_LOWER:
8047 return SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
8048 case SCIP_BOUNDTYPE_UPPER:
8049 return SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
8050 default:
8051 SCIPerrorMessage("unknown bound type\n");
8052 return SCIP_INVALIDDATA;
8053 }
8054 }
8055
8056 /** changes lower bound of variable in current dive; if possible, adjusts bound to integral value */
SCIPvarChgLbDive(SCIP_VAR * var,SCIP_SET * set,SCIP_LP * lp,SCIP_Real newbound)8057 SCIP_RETCODE SCIPvarChgLbDive(
8058 SCIP_VAR* var, /**< problem variable to change */
8059 SCIP_SET* set, /**< global SCIP settings */
8060 SCIP_LP* lp, /**< current LP data */
8061 SCIP_Real newbound /**< new bound for variable */
8062 )
8063 {
8064 assert(var != NULL);
8065 assert(set != NULL);
8066 assert(var->scip == set->scip);
8067 assert(lp != NULL);
8068 assert(SCIPlpDiving(lp));
8069
8070 /* adjust bound for integral variables */
8071 SCIPvarAdjustLb(var, set, &newbound);
8072
8073 SCIPsetDebugMsg(set, "changing lower bound of <%s> to %g in current dive\n", var->name, newbound);
8074
8075 /* change bounds of attached variables */
8076 switch( SCIPvarGetStatus(var) )
8077 {
8078 case SCIP_VARSTATUS_ORIGINAL:
8079 assert(var->data.original.transvar != NULL);
8080 SCIP_CALL( SCIPvarChgLbDive(var->data.original.transvar, set, lp, newbound) );
8081 break;
8082
8083 case SCIP_VARSTATUS_COLUMN:
8084 assert(var->data.col != NULL);
8085 SCIP_CALL( SCIPcolChgLb(var->data.col, set, lp, newbound) );
8086 break;
8087
8088 case SCIP_VARSTATUS_LOOSE:
8089 SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
8090 return SCIP_INVALIDDATA;
8091
8092 case SCIP_VARSTATUS_FIXED:
8093 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8094 return SCIP_INVALIDDATA;
8095
8096 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8097 assert(var->data.aggregate.var != NULL);
8098 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8099 {
8100 SCIP_Real childnewbound;
8101
8102 /* a > 0 -> change lower bound of y */
8103 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8104 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8105 else
8106 childnewbound = newbound;
8107 SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
8108 }
8109 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8110 {
8111 SCIP_Real childnewbound;
8112
8113 /* a < 0 -> change upper bound of y */
8114 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8115 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8116 else
8117 childnewbound = -newbound;
8118 SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
8119 }
8120 else
8121 {
8122 SCIPerrorMessage("scalar is zero in aggregation\n");
8123 return SCIP_INVALIDDATA;
8124 }
8125 break;
8126
8127 case SCIP_VARSTATUS_MULTAGGR:
8128 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8129 return SCIP_INVALIDDATA;
8130
8131 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8132 assert(var->negatedvar != NULL);
8133 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8134 assert(var->negatedvar->negatedvar == var);
8135 SCIP_CALL( SCIPvarChgUbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
8136 break;
8137
8138 default:
8139 SCIPerrorMessage("unknown variable status\n");
8140 return SCIP_INVALIDDATA;
8141 }
8142
8143 return SCIP_OKAY;
8144 }
8145
8146 /** changes upper bound of variable in current dive; if possible, adjusts bound to integral value */
SCIPvarChgUbDive(SCIP_VAR * var,SCIP_SET * set,SCIP_LP * lp,SCIP_Real newbound)8147 SCIP_RETCODE SCIPvarChgUbDive(
8148 SCIP_VAR* var, /**< problem variable to change */
8149 SCIP_SET* set, /**< global SCIP settings */
8150 SCIP_LP* lp, /**< current LP data */
8151 SCIP_Real newbound /**< new bound for variable */
8152 )
8153 {
8154 assert(var != NULL);
8155 assert(set != NULL);
8156 assert(var->scip == set->scip);
8157 assert(lp != NULL);
8158 assert(SCIPlpDiving(lp));
8159
8160 /* adjust bound for integral variables */
8161 SCIPvarAdjustUb(var, set, &newbound);
8162
8163 SCIPsetDebugMsg(set, "changing upper bound of <%s> to %g in current dive\n", var->name, newbound);
8164
8165 /* change bounds of attached variables */
8166 switch( SCIPvarGetStatus(var) )
8167 {
8168 case SCIP_VARSTATUS_ORIGINAL:
8169 assert(var->data.original.transvar != NULL);
8170 SCIP_CALL( SCIPvarChgUbDive(var->data.original.transvar, set, lp, newbound) );
8171 break;
8172
8173 case SCIP_VARSTATUS_COLUMN:
8174 assert(var->data.col != NULL);
8175 SCIP_CALL( SCIPcolChgUb(var->data.col, set, lp, newbound) );
8176 break;
8177
8178 case SCIP_VARSTATUS_LOOSE:
8179 SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
8180 return SCIP_INVALIDDATA;
8181
8182 case SCIP_VARSTATUS_FIXED:
8183 SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
8184 return SCIP_INVALIDDATA;
8185
8186 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8187 assert(var->data.aggregate.var != NULL);
8188 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8189 {
8190 SCIP_Real childnewbound;
8191
8192 /* a > 0 -> change upper bound of y */
8193 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8194 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8195 else
8196 childnewbound = newbound;
8197 SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
8198 }
8199 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8200 {
8201 SCIP_Real childnewbound;
8202
8203 /* a < 0 -> change lower bound of y */
8204 if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
8205 childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
8206 else
8207 childnewbound = -newbound;
8208 SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
8209 }
8210 else
8211 {
8212 SCIPerrorMessage("scalar is zero in aggregation\n");
8213 return SCIP_INVALIDDATA;
8214 }
8215 break;
8216
8217 case SCIP_VARSTATUS_MULTAGGR:
8218 SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
8219 return SCIP_INVALIDDATA;
8220
8221 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8222 assert(var->negatedvar != NULL);
8223 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8224 assert(var->negatedvar->negatedvar == var);
8225 SCIP_CALL( SCIPvarChgLbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
8226 break;
8227
8228 default:
8229 SCIPerrorMessage("unknown variable status\n");
8230 return SCIP_INVALIDDATA;
8231 }
8232
8233 return SCIP_OKAY;
8234 }
8235
8236 /** for a multi-aggregated variable, gives the local lower bound computed by adding the local bounds from all
8237 * aggregation variables, this lower bound may be tighter than the one given by SCIPvarGetLbLocal, since the latter is
8238 * not updated if bounds of aggregation variables are changing
8239 *
8240 * calling this function for a non-multi-aggregated variable is not allowed
8241 */
SCIPvarGetMultaggrLbLocal(SCIP_VAR * var,SCIP_SET * set)8242 SCIP_Real SCIPvarGetMultaggrLbLocal(
8243 SCIP_VAR* var, /**< problem variable */
8244 SCIP_SET* set /**< global SCIP settings */
8245 )
8246 {
8247 int i;
8248 SCIP_Real lb;
8249 SCIP_Real bnd;
8250 SCIP_VAR* aggrvar;
8251 SCIP_Bool posinf;
8252 SCIP_Bool neginf;
8253
8254 assert(var != NULL);
8255 assert(set != NULL);
8256 assert(var->scip == set->scip);
8257 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8258
8259 posinf = FALSE;
8260 neginf = FALSE;
8261 lb = var->data.multaggr.constant;
8262 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8263 {
8264 aggrvar = var->data.multaggr.vars[i];
8265 if( var->data.multaggr.scalars[i] > 0.0 )
8266 {
8267 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);
8268
8269 if( SCIPsetIsInfinity(set, bnd) )
8270 posinf = TRUE;
8271 else if( SCIPsetIsInfinity(set, -bnd) )
8272 neginf = TRUE;
8273 else
8274 lb += var->data.multaggr.scalars[i] * bnd;
8275 }
8276 else
8277 {
8278 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);
8279
8280 if( SCIPsetIsInfinity(set, -bnd) )
8281 posinf = TRUE;
8282 else if( SCIPsetIsInfinity(set, bnd) )
8283 neginf = TRUE;
8284 else
8285 lb += var->data.multaggr.scalars[i] * bnd;
8286 }
8287
8288 /* stop if two diffrent infinities (or a -infinity) were found and return local lower bound of multi aggregated
8289 * variable
8290 */
8291 if( neginf )
8292 return SCIPvarGetLbLocal(var);
8293 }
8294
8295 /* if positive infinity flag was set to true return infinity */
8296 if( posinf )
8297 return SCIPsetInfinity(set);
8298
8299 return (MAX(lb, SCIPvarGetLbLocal(var))); /*lint !e666*/
8300 }
8301
8302 /** for a multi-aggregated variable, gives the local upper bound computed by adding the local bounds from all
8303 * aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbLocal, since the latter is
8304 * not updated if bounds of aggregation variables are changing
8305 *
8306 * calling this function for a non-multi-aggregated variable is not allowed
8307 */
SCIPvarGetMultaggrUbLocal(SCIP_VAR * var,SCIP_SET * set)8308 SCIP_Real SCIPvarGetMultaggrUbLocal(
8309 SCIP_VAR* var, /**< problem variable */
8310 SCIP_SET* set /**< global SCIP settings */
8311 )
8312 {
8313 int i;
8314 SCIP_Real ub;
8315 SCIP_Real bnd;
8316 SCIP_VAR* aggrvar;
8317 SCIP_Bool posinf;
8318 SCIP_Bool neginf;
8319
8320 assert(var != NULL);
8321 assert(set != NULL);
8322 assert(var->scip == set->scip);
8323 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8324
8325 posinf = FALSE;
8326 neginf = FALSE;
8327 ub = var->data.multaggr.constant;
8328 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8329 {
8330 aggrvar = var->data.multaggr.vars[i];
8331 if( var->data.multaggr.scalars[i] > 0.0 )
8332 {
8333 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);
8334
8335 if( SCIPsetIsInfinity(set, bnd) )
8336 posinf = TRUE;
8337 else if( SCIPsetIsInfinity(set, -bnd) )
8338 neginf = TRUE;
8339 else
8340 ub += var->data.multaggr.scalars[i] * bnd;
8341 }
8342 else
8343 {
8344 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);
8345
8346 if( SCIPsetIsInfinity(set, -bnd) )
8347 posinf = TRUE;
8348 else if( SCIPsetIsInfinity(set, bnd) )
8349 neginf = TRUE;
8350 else
8351 ub += var->data.multaggr.scalars[i] * bnd;
8352 }
8353
8354 /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
8355 * variable
8356 */
8357 if( posinf )
8358 return SCIPvarGetUbLocal(var);
8359 }
8360
8361 /* if negative infinity flag was set to true return -infinity */
8362 if( neginf )
8363 return -SCIPsetInfinity(set);
8364
8365 return (MIN(ub, SCIPvarGetUbLocal(var))); /*lint !e666*/
8366 }
8367
8368 /** for a multi-aggregated variable, gives the global lower bound computed by adding the global bounds from all
8369 * aggregation variables, this global bound may be tighter than the one given by SCIPvarGetLbGlobal, since the latter is
8370 * not updated if bounds of aggregation variables are changing
8371 *
8372 * calling this function for a non-multi-aggregated variable is not allowed
8373 */
SCIPvarGetMultaggrLbGlobal(SCIP_VAR * var,SCIP_SET * set)8374 SCIP_Real SCIPvarGetMultaggrLbGlobal(
8375 SCIP_VAR* var, /**< problem variable */
8376 SCIP_SET* set /**< global SCIP settings */
8377 )
8378 {
8379 int i;
8380 SCIP_Real lb;
8381 SCIP_Real bnd;
8382 SCIP_VAR* aggrvar;
8383 SCIP_Bool posinf;
8384 SCIP_Bool neginf;
8385
8386 assert(var != NULL);
8387 assert(set != NULL);
8388 assert(var->scip == set->scip);
8389 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8390
8391 posinf = FALSE;
8392 neginf = FALSE;
8393 lb = var->data.multaggr.constant;
8394 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8395 {
8396 aggrvar = var->data.multaggr.vars[i];
8397 if( var->data.multaggr.scalars[i] > 0.0 )
8398 {
8399 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);
8400
8401 if( SCIPsetIsInfinity(set, bnd) )
8402 posinf = TRUE;
8403 else if( SCIPsetIsInfinity(set, -bnd) )
8404 neginf = TRUE;
8405 else
8406 lb += var->data.multaggr.scalars[i] * bnd;
8407 }
8408 else
8409 {
8410 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);
8411
8412 if( SCIPsetIsInfinity(set, -bnd) )
8413 posinf = TRUE;
8414 else if( SCIPsetIsInfinity(set, bnd) )
8415 neginf = TRUE;
8416 else
8417 lb += var->data.multaggr.scalars[i] * bnd;
8418 }
8419
8420 /* stop if two diffrent infinities (or a -infinity) were found and return global lower bound of multi aggregated
8421 * variable
8422 */
8423 if( neginf )
8424 return SCIPvarGetLbGlobal(var);
8425 }
8426
8427 /* if positive infinity flag was set to true return infinity */
8428 if( posinf )
8429 return SCIPsetInfinity(set);
8430
8431 return (MAX(lb, SCIPvarGetLbGlobal(var))); /*lint !e666*/
8432 }
8433
8434 /** for a multi-aggregated variable, gives the global upper bound computed by adding the global bounds from all
8435 * aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbGlobal, since the latter is
8436 * not updated if bounds of aggregation variables are changing
8437 *
8438 * calling this function for a non-multi-aggregated variable is not allowed
8439 */
SCIPvarGetMultaggrUbGlobal(SCIP_VAR * var,SCIP_SET * set)8440 SCIP_Real SCIPvarGetMultaggrUbGlobal(
8441 SCIP_VAR* var, /**< problem variable */
8442 SCIP_SET* set /**< global SCIP settings */
8443 )
8444 {
8445 int i;
8446 SCIP_Real ub;
8447 SCIP_Real bnd;
8448 SCIP_VAR* aggrvar;
8449 SCIP_Bool posinf;
8450 SCIP_Bool neginf;
8451
8452 assert(var != NULL);
8453 assert(set != NULL);
8454 assert(var->scip == set->scip);
8455 assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);
8456
8457 posinf = FALSE;
8458 neginf = FALSE;
8459 ub = var->data.multaggr.constant;
8460 for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
8461 {
8462 aggrvar = var->data.multaggr.vars[i];
8463 if( var->data.multaggr.scalars[i] > 0.0 )
8464 {
8465 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);
8466
8467 if( SCIPsetIsInfinity(set, bnd) )
8468 posinf = TRUE;
8469 else if( SCIPsetIsInfinity(set, -bnd) )
8470 neginf = TRUE;
8471 else
8472 ub += var->data.multaggr.scalars[i] * bnd;
8473 }
8474 else
8475 {
8476 bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);
8477
8478 if( SCIPsetIsInfinity(set, -bnd) )
8479 posinf = TRUE;
8480 else if( SCIPsetIsInfinity(set, bnd) )
8481 neginf = TRUE;
8482 else
8483 ub += var->data.multaggr.scalars[i] * bnd;
8484 }
8485
8486 /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
8487 * variable
8488 */
8489 if( posinf )
8490 return SCIPvarGetUbGlobal(var);
8491 }
8492
8493 /* if negative infinity flag was set to true return -infinity */
8494 if( neginf )
8495 return -SCIPsetInfinity(set);
8496
8497 return (MIN(ub, SCIPvarGetUbGlobal(var))); /*lint !e666*/
8498 }
8499
8500 /** adds a hole to the original domain of the variable */
SCIPvarAddHoleOriginal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Real left,SCIP_Real right)8501 SCIP_RETCODE SCIPvarAddHoleOriginal(
8502 SCIP_VAR* var, /**< problem variable */
8503 BMS_BLKMEM* blkmem, /**< block memory */
8504 SCIP_SET* set, /**< global SCIP settings */
8505 SCIP_Real left, /**< left bound of open interval in new hole */
8506 SCIP_Real right /**< right bound of open interval in new hole */
8507 )
8508 {
8509 SCIP_Bool added;
8510
8511 assert(var != NULL);
8512 assert(!SCIPvarIsTransformed(var));
8513 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
8514 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
8515 assert(set != NULL);
8516 assert(var->scip == set->scip);
8517 assert(set->stage == SCIP_STAGE_PROBLEM);
8518
8519 SCIPsetDebugMsg(set, "adding original hole (%g,%g) to <%s>\n", left, right, var->name);
8520
8521 if( SCIPsetIsEQ(set, left, right) )
8522 return SCIP_OKAY;
8523
8524 /* the interval should not be empty */
8525 assert(SCIPsetIsLT(set, left, right));
8526
8527 /* the the interval bound should already be adjusted */
8528 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8529 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8530
8531 /* the the interval should lay between the lower and upper bound */
8532 assert(SCIPsetIsGE(set, left, SCIPvarGetLbOriginal(var)));
8533 assert(SCIPsetIsLE(set, right, SCIPvarGetUbOriginal(var)));
8534
8535 /* add domain hole */
8536 SCIP_CALL( domAddHole(&var->data.original.origdom, blkmem, set, left, right, &added) );
8537
8538 /* merges overlapping holes into single holes, moves bounds respectively if hole was added */
8539 if( added )
8540 {
8541 domMerge(&var->data.original.origdom, blkmem, set, NULL, NULL);
8542 }
8543
8544 /**@todo add hole in parent and child variables (just like with bound changes);
8545 * warning! original vars' holes are in original blkmem, transformed vars' holes in transformed blkmem
8546 */
8547
8548 return SCIP_OKAY;
8549 }
8550
8551 /** performs the current add of domain, changes all parents accordingly */
8552 static
varProcessAddHoleGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_Real left,SCIP_Real right,SCIP_Bool * added)8553 SCIP_RETCODE varProcessAddHoleGlobal(
8554 SCIP_VAR* var, /**< problem variable */
8555 BMS_BLKMEM* blkmem, /**< block memory */
8556 SCIP_SET* set, /**< global SCIP settings */
8557 SCIP_STAT* stat, /**< problem statistics */
8558 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8559 SCIP_Real left, /**< left bound of open interval in new hole */
8560 SCIP_Real right, /**< right bound of open interval in new hole */
8561 SCIP_Bool* added /**< pointer to store whether the hole was added */
8562 )
8563 {
8564 SCIP_VAR* parentvar;
8565 SCIP_Real newlb;
8566 SCIP_Real newub;
8567 int i;
8568
8569 assert(var != NULL);
8570 assert(added != NULL);
8571 assert(blkmem != NULL);
8572
8573 /* the interval should not be empty */
8574 assert(SCIPsetIsLT(set, left, right));
8575
8576 /* the interval bound should already be adjusted */
8577 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8578 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8579
8580 /* the interval should lay between the lower and upper bound */
8581 assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
8582 assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));
8583
8584 /* @todo add debugging mechanism for holes when using a debugging solution */
8585
8586 /* add hole to hole list */
8587 SCIP_CALL( domAddHole(&var->glbdom, blkmem, set, left, right, added) );
8588
8589 /* check if the hole is redundant */
8590 if( !(*added) )
8591 return SCIP_OKAY;
8592
8593 /* current bounds */
8594 newlb = var->glbdom.lb;
8595 newub = var->glbdom.ub;
8596
8597 /* merge domain holes */
8598 domMerge(&var->glbdom, blkmem, set, &newlb, &newub);
8599
8600 /* the bound should not be changed */
8601 assert(SCIPsetIsEQ(set, newlb, var->glbdom.lb));
8602 assert(SCIPsetIsEQ(set, newub, var->glbdom.ub));
8603
8604 /* issue bound change event */
8605 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
8606 if( var->eventfilter != NULL )
8607 {
8608 SCIP_CALL( varEventGholeAdded(var, blkmem, set, eventqueue, left, right) );
8609 }
8610
8611 /* process parent variables */
8612 for( i = 0; i < var->nparentvars; ++i )
8613 {
8614 SCIP_Real parentnewleft;
8615 SCIP_Real parentnewright;
8616 SCIP_Bool localadded;
8617
8618 parentvar = var->parentvars[i];
8619 assert(parentvar != NULL);
8620
8621 switch( SCIPvarGetStatus(parentvar) )
8622 {
8623 case SCIP_VARSTATUS_ORIGINAL:
8624 parentnewleft = left;
8625 parentnewright = right;
8626 break;
8627
8628 case SCIP_VARSTATUS_COLUMN:
8629 case SCIP_VARSTATUS_LOOSE:
8630 case SCIP_VARSTATUS_FIXED:
8631 case SCIP_VARSTATUS_MULTAGGR:
8632 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
8633 return SCIP_INVALIDDATA;
8634
8635 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8636 assert(parentvar->data.aggregate.var == var);
8637
8638 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
8639 {
8640 /* a > 0 -> change upper bound of x */
8641 parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8642 parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8643 }
8644 else
8645 {
8646 /* a < 0 -> change lower bound of x */
8647 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
8648
8649 parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8650 parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8651 }
8652 break;
8653
8654 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
8655 assert(parentvar->negatedvar != NULL);
8656 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
8657 assert(parentvar->negatedvar->negatedvar == parentvar);
8658
8659 parentnewright = -left + parentvar->data.negate.constant;
8660 parentnewleft = -right + parentvar->data.negate.constant;
8661 break;
8662
8663 default:
8664 SCIPerrorMessage("unknown variable status\n");
8665 return SCIP_INVALIDDATA;
8666 }
8667
8668 SCIPsetDebugMsg(set, "add global hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));
8669
8670 /* perform hole added for parent variable */
8671 assert(blkmem != NULL);
8672 assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
8673 SCIP_CALL( varProcessAddHoleGlobal(parentvar, blkmem, set, stat, eventqueue,
8674 parentnewleft, parentnewright, &localadded) );
8675 assert(localadded);
8676 }
8677
8678 return SCIP_OKAY;
8679 }
8680
8681 /** adds a hole to the variable's global and local domain */
SCIPvarAddHoleGlobal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_Real left,SCIP_Real right,SCIP_Bool * added)8682 SCIP_RETCODE SCIPvarAddHoleGlobal(
8683 SCIP_VAR* var, /**< problem variable */
8684 BMS_BLKMEM* blkmem, /**< block memory */
8685 SCIP_SET* set, /**< global SCIP settings */
8686 SCIP_STAT* stat, /**< problem statistics */
8687 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8688 SCIP_Real left, /**< left bound of open interval in new hole */
8689 SCIP_Real right, /**< right bound of open interval in new hole */
8690 SCIP_Bool* added /**< pointer to store whether the hole was added */
8691 )
8692 {
8693 SCIP_Real childnewleft;
8694 SCIP_Real childnewright;
8695
8696 assert(var != NULL);
8697 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
8698 assert(blkmem != NULL);
8699 assert(added != NULL);
8700
8701 SCIPsetDebugMsg(set, "adding global hole (%g,%g) to <%s>\n", left, right, var->name);
8702
8703 /* the interval should not be empty */
8704 assert(SCIPsetIsLT(set, left, right));
8705
8706 /* the the interval bound should already be adjusted */
8707 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8708 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8709
8710 /* the the interval should lay between the lower and upper bound */
8711 assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
8712 assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));
8713
8714 /* change bounds of attached variables */
8715 switch( SCIPvarGetStatus(var) )
8716 {
8717 case SCIP_VARSTATUS_ORIGINAL:
8718 if( var->data.original.transvar != NULL )
8719 {
8720 SCIP_CALL( SCIPvarAddHoleGlobal(var->data.original.transvar, blkmem, set, stat, eventqueue,
8721 left, right, added) );
8722 }
8723 else
8724 {
8725 assert(set->stage == SCIP_STAGE_PROBLEM);
8726
8727 SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
8728 if( *added )
8729 {
8730 SCIP_Bool localadded;
8731
8732 SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
8733 }
8734 }
8735 break;
8736
8737 case SCIP_VARSTATUS_COLUMN:
8738 case SCIP_VARSTATUS_LOOSE:
8739 SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
8740 if( *added )
8741 {
8742 SCIP_Bool localadded;
8743
8744 SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
8745 }
8746 break;
8747
8748 case SCIP_VARSTATUS_FIXED:
8749 SCIPerrorMessage("cannot add hole of a fixed variable\n");
8750 return SCIP_INVALIDDATA;
8751
8752 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8753 assert(var->data.aggregate.var != NULL);
8754
8755 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8756 {
8757 /* a > 0 -> change lower bound of y */
8758 childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
8759 childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
8760 }
8761 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
8762 {
8763 childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
8764 childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
8765 }
8766 else
8767 {
8768 SCIPerrorMessage("scalar is zero in aggregation\n");
8769 return SCIP_INVALIDDATA;
8770 }
8771 SCIP_CALL( SCIPvarAddHoleGlobal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
8772 childnewleft, childnewright, added) );
8773 break;
8774
8775 case SCIP_VARSTATUS_MULTAGGR:
8776 SCIPerrorMessage("cannot add a hole of a multi-aggregated variable.\n");
8777 return SCIP_INVALIDDATA;
8778
8779 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
8780 assert(var->negatedvar != NULL);
8781 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
8782 assert(var->negatedvar->negatedvar == var);
8783
8784 childnewright = -left + var->data.negate.constant;
8785 childnewleft = -right + var->data.negate.constant;
8786
8787 SCIP_CALL( SCIPvarAddHoleGlobal(var->negatedvar, blkmem, set, stat, eventqueue,
8788 childnewleft, childnewright, added) );
8789 break;
8790
8791 default:
8792 SCIPerrorMessage("unknown variable status\n");
8793 return SCIP_INVALIDDATA;
8794 }
8795
8796 return SCIP_OKAY;
8797 }
8798
8799 /** performs the current add of domain, changes all parents accordingly */
8800 static
varProcessAddHoleLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_Real left,SCIP_Real right,SCIP_Bool * added)8801 SCIP_RETCODE varProcessAddHoleLocal(
8802 SCIP_VAR* var, /**< problem variable */
8803 BMS_BLKMEM* blkmem, /**< block memory */
8804 SCIP_SET* set, /**< global SCIP settings */
8805 SCIP_STAT* stat, /**< problem statistics */
8806 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8807 SCIP_Real left, /**< left bound of open interval in new hole */
8808 SCIP_Real right, /**< right bound of open interval in new hole */
8809 SCIP_Bool* added /**< pointer to store whether the hole was added, or NULL */
8810 )
8811 {
8812 SCIP_VAR* parentvar;
8813 SCIP_Real newlb;
8814 SCIP_Real newub;
8815 int i;
8816
8817 assert(var != NULL);
8818 assert(added != NULL);
8819 assert(blkmem != NULL);
8820
8821 /* the interval should not be empty */
8822 assert(SCIPsetIsLT(set, left, right));
8823
8824 /* the the interval bound should already be adjusted */
8825 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8826 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8827
8828 /* the the interval should lay between the lower and upper bound */
8829 assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
8830 assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));
8831
8832 /* add hole to hole list */
8833 SCIP_CALL( domAddHole(&var->locdom, blkmem, set, left, right, added) );
8834
8835 /* check if the hole is redundant */
8836 if( !(*added) )
8837 return SCIP_OKAY;
8838
8839 /* current bounds */
8840 newlb = var->locdom.lb;
8841 newub = var->locdom.ub;
8842
8843 /* merge domain holes */
8844 domMerge(&var->locdom, blkmem, set, &newlb, &newub);
8845
8846 /* the bound should not be changed */
8847 assert(SCIPsetIsEQ(set, newlb, var->locdom.lb));
8848 assert(SCIPsetIsEQ(set, newub, var->locdom.ub));
8849
8850 #if 0
8851 /* issue bound change event */
8852 assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
8853 if( var->eventfilter != NULL )
8854 {
8855 SCIP_CALL( varEventLholeAdded(var, blkmem, set, lp, branchcand, eventqueue, left, right) );
8856 }
8857 #endif
8858
8859 /* process parent variables */
8860 for( i = 0; i < var->nparentvars; ++i )
8861 {
8862 SCIP_Real parentnewleft;
8863 SCIP_Real parentnewright;
8864 SCIP_Bool localadded;
8865
8866 parentvar = var->parentvars[i];
8867 assert(parentvar != NULL);
8868
8869 switch( SCIPvarGetStatus(parentvar) )
8870 {
8871 case SCIP_VARSTATUS_ORIGINAL:
8872 parentnewleft = left;
8873 parentnewright = right;
8874 break;
8875
8876 case SCIP_VARSTATUS_COLUMN:
8877 case SCIP_VARSTATUS_LOOSE:
8878 case SCIP_VARSTATUS_FIXED:
8879 case SCIP_VARSTATUS_MULTAGGR:
8880 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
8881 return SCIP_INVALIDDATA;
8882
8883 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8884 assert(parentvar->data.aggregate.var == var);
8885
8886 if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
8887 {
8888 /* a > 0 -> change upper bound of x */
8889 parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8890 parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8891 }
8892 else
8893 {
8894 /* a < 0 -> change lower bound of x */
8895 assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
8896
8897 parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
8898 parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
8899 }
8900 break;
8901
8902 case SCIP_VARSTATUS_NEGATED: /* x = offset - x' -> x' = offset - x */
8903 assert(parentvar->negatedvar != NULL);
8904 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
8905 assert(parentvar->negatedvar->negatedvar == parentvar);
8906
8907 parentnewright = -left + parentvar->data.negate.constant;
8908 parentnewleft = -right + parentvar->data.negate.constant;
8909 break;
8910
8911 default:
8912 SCIPerrorMessage("unknown variable status\n");
8913 return SCIP_INVALIDDATA;
8914 }
8915
8916 SCIPsetDebugMsg(set, "add local hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));
8917
8918 /* perform hole added for parent variable */
8919 assert(blkmem != NULL);
8920 assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
8921 SCIP_CALL( varProcessAddHoleLocal(parentvar, blkmem, set, stat, eventqueue,
8922 parentnewleft, parentnewright, &localadded) );
8923 assert(localadded);
8924 }
8925
8926 return SCIP_OKAY;
8927 }
8928
8929 /** adds a hole to the variable's current local domain */
SCIPvarAddHoleLocal(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_Real left,SCIP_Real right,SCIP_Bool * added)8930 SCIP_RETCODE SCIPvarAddHoleLocal(
8931 SCIP_VAR* var, /**< problem variable */
8932 BMS_BLKMEM* blkmem, /**< block memory */
8933 SCIP_SET* set, /**< global SCIP settings */
8934 SCIP_STAT* stat, /**< problem statistics */
8935 SCIP_EVENTQUEUE* eventqueue, /**< event queue, may be NULL for original variables */
8936 SCIP_Real left, /**< left bound of open interval in new hole */
8937 SCIP_Real right, /**< right bound of open interval in new hole */
8938 SCIP_Bool* added /**< pointer to store whether the hole was added */
8939 )
8940 {
8941 SCIP_Real childnewleft;
8942 SCIP_Real childnewright;
8943
8944 assert(var != NULL);
8945
8946 SCIPsetDebugMsg(set, "adding local hole (%g,%g) to <%s>\n", left, right, var->name);
8947
8948 assert(set != NULL);
8949 assert(var->scip == set->scip);
8950 assert(SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
8951 assert(blkmem != NULL);
8952 assert(added != NULL);
8953
8954 /* the interval should not be empty */
8955 assert(SCIPsetIsLT(set, left, right));
8956
8957 /* the the interval bound should already be adjusted */
8958 assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarGetType(var), left)));
8959 assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarGetType(var), right)));
8960
8961 /* the the interval should lay between the lower and upper bound */
8962 assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
8963 assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));
8964
8965 /* change bounds of attached variables */
8966 switch( SCIPvarGetStatus(var) )
8967 {
8968 case SCIP_VARSTATUS_ORIGINAL:
8969 if( var->data.original.transvar != NULL )
8970 {
8971 SCIP_CALL( SCIPvarAddHoleLocal(var->data.original.transvar, blkmem, set, stat, eventqueue,
8972 left, right, added) );
8973 }
8974 else
8975 {
8976 assert(set->stage == SCIP_STAGE_PROBLEM);
8977 SCIPstatIncrement(stat, set, domchgcount);
8978 SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
8979 }
8980 break;
8981
8982 case SCIP_VARSTATUS_COLUMN:
8983 case SCIP_VARSTATUS_LOOSE:
8984 SCIPstatIncrement(stat, set, domchgcount);
8985 SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
8986 break;
8987
8988 case SCIP_VARSTATUS_FIXED:
8989 SCIPerrorMessage("cannot add domain hole to a fixed variable\n");
8990 return SCIP_INVALIDDATA;
8991
8992 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
8993 assert(var->data.aggregate.var != NULL);
8994
8995 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
8996 {
8997 /* a > 0 -> change lower bound of y */
8998 childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
8999 childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
9000 }
9001 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
9002 {
9003 childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
9004 childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
9005 }
9006 else
9007 {
9008 SCIPerrorMessage("scalar is zero in aggregation\n");
9009 return SCIP_INVALIDDATA;
9010 }
9011 SCIP_CALL( SCIPvarAddHoleLocal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
9012 childnewleft, childnewright, added) );
9013 break;
9014
9015 case SCIP_VARSTATUS_MULTAGGR:
9016 SCIPerrorMessage("cannot add domain hole to a multi-aggregated variable.\n");
9017 return SCIP_INVALIDDATA;
9018
9019 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
9020 assert(var->negatedvar != NULL);
9021 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
9022 assert(var->negatedvar->negatedvar == var);
9023
9024 childnewright = -left + var->data.negate.constant;
9025 childnewleft = -right + var->data.negate.constant;
9026
9027 SCIP_CALL( SCIPvarAddHoleLocal(var->negatedvar, blkmem, set, stat, eventqueue, childnewleft, childnewright, added) );
9028 break;
9029
9030 default:
9031 SCIPerrorMessage("unknown variable status\n");
9032 return SCIP_INVALIDDATA;
9033 }
9034
9035 return SCIP_OKAY;
9036 }
9037
9038 /** resets the global and local bounds of original variable to their original values */
SCIPvarResetBounds(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat)9039 SCIP_RETCODE SCIPvarResetBounds(
9040 SCIP_VAR* var, /**< problem variable */
9041 BMS_BLKMEM* blkmem, /**< block memory */
9042 SCIP_SET* set, /**< global SCIP settings */
9043 SCIP_STAT* stat /**< problem statistics */
9044 )
9045 {
9046 assert(var != NULL);
9047 assert(set != NULL);
9048 assert(var->scip == set->scip);
9049 assert(SCIPvarIsOriginal(var));
9050 /* resetting of bounds on original variables which have a transformed counterpart easily fails if, e.g.,
9051 * the transformed variable has been fixed */
9052 assert(SCIPvarGetTransVar(var) == NULL);
9053
9054 /* copy the original bounds back to the global and local bounds */
9055 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.lb) );
9056 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.ub) );
9057 SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.lb) );
9058 SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.ub) );
9059
9060 /* free the global and local holelists and duplicate the original ones */
9061 /**@todo this has also to be called recursively with methods similar to SCIPvarChgLbGlobal() */
9062 holelistFree(&var->glbdom.holelist, blkmem);
9063 holelistFree(&var->locdom.holelist, blkmem);
9064 SCIP_CALL( holelistDuplicate(&var->glbdom.holelist, blkmem, set, var->data.original.origdom.holelist) );
9065 SCIP_CALL( holelistDuplicate(&var->locdom.holelist, blkmem, set, var->data.original.origdom.holelist) );
9066
9067 return SCIP_OKAY;
9068 }
9069
9070 /** issues a IMPLADDED event on the given variable */
9071 static
varEventImplAdded(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue)9072 SCIP_RETCODE varEventImplAdded(
9073 SCIP_VAR* var, /**< problem variable to change */
9074 BMS_BLKMEM* blkmem, /**< block memory */
9075 SCIP_SET* set, /**< global SCIP settings */
9076 SCIP_EVENTQUEUE* eventqueue /**< event queue */
9077 )
9078 {
9079 SCIP_EVENT* event;
9080
9081 assert(var != NULL);
9082
9083 /* issue IMPLADDED event on variable */
9084 SCIP_CALL( SCIPeventCreateImplAdded(&event, blkmem, var) );
9085 SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
9086
9087 return SCIP_OKAY;
9088 }
9089
9090 /** actually performs the addition of a variable bound to the variable's vbound arrays */
9091 static
varAddVbound(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_BOUNDTYPE vbtype,SCIP_VAR * vbvar,SCIP_Real vbcoef,SCIP_Real vbconstant)9092 SCIP_RETCODE varAddVbound(
9093 SCIP_VAR* var, /**< problem variable x in x <= b*z + d or x >= b*z + d */
9094 BMS_BLKMEM* blkmem, /**< block memory */
9095 SCIP_SET* set, /**< global SCIP settings */
9096 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9097 SCIP_BOUNDTYPE vbtype, /**< type of variable bound (LOWER or UPPER) */
9098 SCIP_VAR* vbvar, /**< variable z in x <= b*z + d or x >= b*z + d */
9099 SCIP_Real vbcoef, /**< coefficient b in x <= b*z + d or x >= b*z + d */
9100 SCIP_Real vbconstant /**< constant d in x <= b*z + d or x >= b*z + d */
9101 )
9102 {
9103 SCIP_Bool added;
9104
9105 /* It can happen that the variable "var" and the variable "vbvar" are the same variable. For example if a variable
9106 * gets aggregated, the variable bounds (vbound) of that variable are copied to the other variable. A variable bound
9107 * variable of the aggregated variable might be the same as the one its gets aggregated too.
9108 *
9109 * If the variable "var" and the variable "vbvar" are the same, the variable bound which should be added here has to
9110 * be redundant. This is the case since an infeasibility should have be detected in the previous methods. As well as
9111 * the bounds of the variable which should be also already be tightened in the previous methods. Therefore, the
9112 * variable bound can be ignored.
9113 *
9114 * From the way the the variable bound system is implemented (detecting infeasibility, tighten bounds), the
9115 * equivalence of the variables should be checked here.
9116 */
9117 if( var == vbvar )
9118 {
9119 /* in this case the variable bound has to be redundant, this means for possible assignments to this variable; this
9120 * can be checked via the global bounds of the variable */
9121 #ifndef NDEBUG
9122 SCIP_Real lb;
9123 SCIP_Real ub;
9124
9125 lb = SCIPvarGetLbGlobal(var);
9126 ub = SCIPvarGetUbGlobal(var);
9127
9128 if(vbtype == SCIP_BOUNDTYPE_LOWER)
9129 {
9130 if( vbcoef > 0.0 )
9131 {
9132 assert(SCIPsetIsGE(set, lb, lb * vbcoef + vbconstant) );
9133 assert(SCIPsetIsGE(set, ub, ub * vbcoef + vbconstant) );
9134 }
9135 else
9136 {
9137 assert(SCIPsetIsGE(set, lb, ub * vbcoef + vbconstant) );
9138 assert(SCIPsetIsGE(set, ub, lb * vbcoef + vbconstant) );
9139 }
9140 }
9141 else
9142 {
9143 assert(vbtype == SCIP_BOUNDTYPE_UPPER);
9144 if( vbcoef > 0.0 )
9145 {
9146 assert(SCIPsetIsLE(set, lb, lb * vbcoef + vbconstant) );
9147 assert(SCIPsetIsLE(set, ub, ub * vbcoef + vbconstant) );
9148 }
9149 else
9150 {
9151 assert(SCIPsetIsLE(set, lb, ub * vbcoef + vbconstant) );
9152 assert(SCIPsetIsLE(set, ub, lb * vbcoef + vbconstant) );
9153 }
9154 }
9155 #endif
9156 SCIPsetDebugMsg(set, "redundant variable bound: <%s> %s %g<%s> %+g\n",
9157 SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);
9158
9159 return SCIP_OKAY;
9160 }
9161
9162 SCIPsetDebugMsg(set, "adding variable bound: <%s> %s %g<%s> %+g\n",
9163 SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);
9164
9165 /* check variable bound on debugging solution */
9166 SCIP_CALL( SCIPdebugCheckVbound(set, var, vbtype, vbvar, vbcoef, vbconstant) ); /*lint !e506 !e774*/
9167
9168 /* perform the addition */
9169 if( vbtype == SCIP_BOUNDTYPE_LOWER )
9170 {
9171 SCIP_CALL( SCIPvboundsAdd(&var->vlbs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
9172 }
9173 else
9174 {
9175 SCIP_CALL( SCIPvboundsAdd(&var->vubs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
9176 }
9177 var->closestvblpcount = -1;
9178
9179 if( added )
9180 {
9181 /* issue IMPLADDED event */
9182 SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
9183 }
9184
9185 return SCIP_OKAY;
9186 }
9187
9188 /** checks whether the given implication is redundant or infeasible w.r.t. the implied variables global bounds */
9189 static
checkImplic(SCIP_SET * set,SCIP_VAR * implvar,SCIP_BOUNDTYPE impltype,SCIP_Real implbound,SCIP_Bool * redundant,SCIP_Bool * infeasible)9190 void checkImplic(
9191 SCIP_SET* set, /**< global SCIP settings */
9192 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9193 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9194 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9195 SCIP_Bool* redundant, /**< pointer to store whether the implication is redundant */
9196 SCIP_Bool* infeasible /**< pointer to store whether the implication is infeasible */
9197 )
9198 {
9199 SCIP_Real impllb;
9200 SCIP_Real implub;
9201
9202 assert(redundant != NULL);
9203 assert(infeasible != NULL);
9204
9205 impllb = SCIPvarGetLbGlobal(implvar);
9206 implub = SCIPvarGetUbGlobal(implvar);
9207 if( impltype == SCIP_BOUNDTYPE_LOWER )
9208 {
9209 *infeasible = SCIPsetIsFeasGT(set, implbound, implub);
9210 *redundant = SCIPsetIsFeasLE(set, implbound, impllb);
9211 }
9212 else
9213 {
9214 *infeasible = SCIPsetIsFeasLT(set, implbound, impllb);
9215 *redundant = SCIPsetIsFeasGE(set, implbound, implub);
9216 }
9217 }
9218
9219 /** applies the given implication, if it is not redundant */
9220 static
applyImplic(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_VAR * implvar,SCIP_BOUNDTYPE impltype,SCIP_Real implbound,SCIP_Bool * infeasible,int * nbdchgs)9221 SCIP_RETCODE applyImplic(
9222 BMS_BLKMEM* blkmem, /**< block memory */
9223 SCIP_SET* set, /**< global SCIP settings */
9224 SCIP_STAT* stat, /**< problem statistics */
9225 SCIP_PROB* transprob, /**< transformed problem */
9226 SCIP_PROB* origprob, /**< original problem */
9227 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9228 SCIP_REOPT* reopt, /**< reoptimization data structure */
9229 SCIP_LP* lp, /**< current LP data */
9230 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9231 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9232 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9233 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9234 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9235 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9236 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9237 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9238 )
9239 {
9240 SCIP_Real implub;
9241 SCIP_Real impllb;
9242
9243 assert(infeasible != NULL);
9244
9245 *infeasible = FALSE;
9246
9247 implub = SCIPvarGetUbGlobal(implvar);
9248 impllb = SCIPvarGetLbGlobal(implvar);
9249 if( impltype == SCIP_BOUNDTYPE_LOWER )
9250 {
9251 if( SCIPsetIsFeasGT(set, implbound, implub) )
9252 {
9253 /* the implication produces a conflict: the problem is infeasible */
9254 *infeasible = TRUE;
9255 }
9256 else if( SCIPsetIsFeasGT(set, implbound, impllb) )
9257 {
9258 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9259 * with the local bound, in this case we need to store the bound change as pending bound change
9260 */
9261 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9262 {
9263 assert(tree != NULL);
9264 assert(transprob != NULL);
9265 assert(SCIPprobIsTransformed(transprob));
9266
9267 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9268 tree, reopt, lp, branchcand, eventqueue, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_LOWER, FALSE) );
9269 }
9270 else
9271 {
9272 SCIP_CALL( SCIPvarChgLbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
9273 }
9274
9275 if( nbdchgs != NULL )
9276 (*nbdchgs)++;
9277 }
9278 }
9279 else
9280 {
9281 if( SCIPsetIsFeasLT(set, implbound, impllb) )
9282 {
9283 /* the implication produces a conflict: the problem is infeasible */
9284 *infeasible = TRUE;
9285 }
9286 else if( SCIPsetIsFeasLT(set, implbound, implub) )
9287 {
9288 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9289 * with the local bound, in this case we need to store the bound change as pending bound change
9290 */
9291 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9292 {
9293 assert(tree != NULL);
9294 assert(transprob != NULL);
9295 assert(SCIPprobIsTransformed(transprob));
9296
9297 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9298 tree, reopt, lp, branchcand, eventqueue, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_UPPER, FALSE) );
9299 }
9300 else
9301 {
9302 SCIP_CALL( SCIPvarChgUbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
9303 }
9304
9305 if( nbdchgs != NULL )
9306 (*nbdchgs)++;
9307 }
9308 }
9309
9310 return SCIP_OKAY;
9311 }
9312
9313 /** actually performs the addition of an implication to the variable's implication arrays,
9314 * and adds the corresponding implication or variable bound to the implied variable;
9315 * if the implication is conflicting, the variable is fixed to the opposite value;
9316 * if the variable is already fixed to the given value, the implication is performed immediately;
9317 * if the implication is redundant with respect to the variables' global bounds, it is ignored
9318 */
9319 static
varAddImplic(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_BOUNDTYPE impltype,SCIP_Real implbound,SCIP_Bool isshortcut,SCIP_Bool * infeasible,int * nbdchgs,SCIP_Bool * added)9320 SCIP_RETCODE varAddImplic(
9321 SCIP_VAR* var, /**< problem variable */
9322 BMS_BLKMEM* blkmem, /**< block memory */
9323 SCIP_SET* set, /**< global SCIP settings */
9324 SCIP_STAT* stat, /**< problem statistics */
9325 SCIP_PROB* transprob, /**< transformed problem */
9326 SCIP_PROB* origprob, /**< original problem */
9327 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9328 SCIP_REOPT* reopt, /**< reoptimization data structure */
9329 SCIP_LP* lp, /**< current LP data */
9330 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9331 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9332 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9333 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9334 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9335 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9336 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9337 SCIP_Bool isshortcut, /**< is the implication a shortcut, i.e., added as part of the transitive closure of another implication? */
9338 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9339 int* nbdchgs, /**< pointer to count the number of performed bound changes, or NULL */
9340 SCIP_Bool* added /**< pointer to store whether an implication was added */
9341 )
9342 {
9343 SCIP_Bool redundant;
9344 SCIP_Bool conflict;
9345
9346 assert(var != NULL);
9347 assert(SCIPvarIsActive(var));
9348 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
9349 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
9350 assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
9351 assert(infeasible != NULL);
9352 assert(added != NULL);
9353
9354 /* check implication on debugging solution */
9355 SCIP_CALL( SCIPdebugCheckImplic(set, var, varfixing, implvar, impltype, implbound) ); /*lint !e506 !e774*/
9356
9357 *infeasible = FALSE;
9358 *added = FALSE;
9359
9360 /* check, if the implication is redundant or infeasible */
9361 checkImplic(set, implvar, impltype, implbound, &redundant, &conflict);
9362 assert(!redundant || !conflict);
9363 if( redundant )
9364 return SCIP_OKAY;
9365
9366 if( var == implvar )
9367 {
9368 /* special cases appear were a bound to a variable implies itself to be outside the bounds:
9369 * x == varfixing => x < 0 or x > 1
9370 */
9371 if( SCIPsetIsLT(set, implbound, 0.0) || SCIPsetIsGT(set, implbound, 1.0) )
9372 conflict = TRUE;
9373 else
9374 {
9375 /* variable implies itself: x == varfixing => x == (impltype == SCIP_BOUNDTYPE_LOWER) */
9376 assert(SCIPsetIsZero(set, implbound) || SCIPsetIsEQ(set, implbound, 1.0));
9377 assert(SCIPsetIsZero(set, implbound) == (impltype == SCIP_BOUNDTYPE_UPPER));
9378 assert(SCIPsetIsEQ(set, implbound, 1.0) == (impltype == SCIP_BOUNDTYPE_LOWER));
9379 conflict = conflict || ((varfixing == TRUE) == (impltype == SCIP_BOUNDTYPE_UPPER));
9380 if( !conflict )
9381 return SCIP_OKAY;
9382 }
9383 }
9384
9385 /* check, if the variable is already fixed */
9386 if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
9387 {
9388 /* if the variable is fixed to the given value, perform the implication; otherwise, ignore the implication */
9389 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
9390 {
9391 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
9392 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
9393 }
9394 return SCIP_OKAY;
9395 }
9396
9397 assert((impltype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsGT(set, implbound, SCIPvarGetLbGlobal(implvar)))
9398 || (impltype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsLT(set, implbound, SCIPvarGetUbGlobal(implvar))));
9399
9400 if( !conflict )
9401 {
9402 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9403
9404 if( SCIPvarIsBinary(implvar) )
9405 {
9406 SCIP_VAR* vars[2];
9407 SCIP_Bool vals[2];
9408
9409 assert(SCIPsetIsFeasEQ(set, implbound, 1.0) || SCIPsetIsFeasZero(set, implbound));
9410 assert((impltype == SCIP_BOUNDTYPE_UPPER) == SCIPsetIsFeasZero(set, implbound));
9411
9412 vars[0] = var;
9413 vars[1] = implvar;
9414 vals[0] = varfixing;
9415 vals[1] = (impltype == SCIP_BOUNDTYPE_UPPER);
9416
9417 /* add the clique to the clique table */
9418 SCIP_CALL( SCIPcliquetableAdd(cliquetable, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
9419 eventqueue, vars, vals, 2, FALSE, &conflict, nbdchgs) );
9420
9421 if( !conflict )
9422 return SCIP_OKAY;
9423 }
9424 else
9425 {
9426 /* add implication x == 0/1 -> y <= b / y >= b to the implications list of x */
9427 SCIPsetDebugMsg(set, "adding implication: <%s> == %u ==> <%s> %s %g\n",
9428 SCIPvarGetName(var), varfixing,
9429 SCIPvarGetName(implvar), impltype == SCIP_BOUNDTYPE_UPPER ? "<=" : ">=", implbound);
9430 SCIP_CALL( SCIPimplicsAdd(&var->implics, blkmem, set, stat, varfixing, implvar, impltype, implbound,
9431 isshortcut, &conflict, added) );
9432 }
9433 }
9434 assert(!conflict || !(*added));
9435
9436 /* on conflict, fix the variable to the opposite value */
9437 if( conflict )
9438 {
9439 SCIPsetDebugMsg(set, " -> implication yields a conflict: fix <%s> == %d\n", SCIPvarGetName(var), !varfixing);
9440
9441 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9442 * with the local bound, in this case we need to store the bound change as pending bound change
9443 */
9444 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9445 {
9446 assert(tree != NULL);
9447 assert(transprob != NULL);
9448 assert(SCIPprobIsTransformed(transprob));
9449
9450 if( varfixing )
9451 {
9452 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9453 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
9454 }
9455 else
9456 {
9457 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9458 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
9459 }
9460 }
9461 else
9462 {
9463 if( varfixing )
9464 {
9465 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
9466 }
9467 else
9468 {
9469 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
9470 }
9471 }
9472 if( nbdchgs != NULL )
9473 (*nbdchgs)++;
9474
9475 return SCIP_OKAY;
9476 }
9477 else if( *added )
9478 {
9479 /* issue IMPLADDED event */
9480 SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
9481 }
9482 else
9483 {
9484 /* the implication was redundant: the inverse is also redundant */
9485 return SCIP_OKAY;
9486 }
9487
9488 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9489
9490 /* check, whether implied variable is binary */
9491 if( !SCIPvarIsBinary(implvar) )
9492 {
9493 SCIP_Real lb;
9494 SCIP_Real ub;
9495
9496 /* add inverse variable bound to the variable bounds of y with global bounds y \in [lb,ub]:
9497 * x == 0 -> y <= b <-> y <= (ub - b)*x + b
9498 * x == 1 -> y <= b <-> y <= (b - ub)*x + ub
9499 * x == 0 -> y >= b <-> y >= (lb - b)*x + b
9500 * x == 1 -> y >= b <-> y >= (b - lb)*x + lb
9501 * for numerical reasons, ignore variable bounds with large absolute coefficient
9502 */
9503 lb = SCIPvarGetLbGlobal(implvar);
9504 ub = SCIPvarGetUbGlobal(implvar);
9505 if( impltype == SCIP_BOUNDTYPE_UPPER )
9506 {
9507 if( REALABS(implbound - ub) <= MAXABSVBCOEF )
9508 {
9509 SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, var,
9510 varfixing ? implbound - ub : ub - implbound, varfixing ? ub : implbound) );
9511 }
9512 }
9513 else
9514 {
9515 if( REALABS(implbound - lb) <= MAXABSVBCOEF )
9516 {
9517 SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, var,
9518 varfixing ? implbound - lb : lb - implbound, varfixing ? lb : implbound) );
9519 }
9520 }
9521 }
9522
9523 return SCIP_OKAY;
9524 }
9525
9526 /** adds transitive closure for binary implication x = a -> y = b */
9527 static
varAddTransitiveBinaryClosureImplic(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_Bool implvarfixing,SCIP_Bool * infeasible,int * nbdchgs)9528 SCIP_RETCODE varAddTransitiveBinaryClosureImplic(
9529 SCIP_VAR* var, /**< problem variable */
9530 BMS_BLKMEM* blkmem, /**< block memory */
9531 SCIP_SET* set, /**< global SCIP settings */
9532 SCIP_STAT* stat, /**< problem statistics */
9533 SCIP_PROB* transprob, /**< transformed problem */
9534 SCIP_PROB* origprob, /**< original problem */
9535 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9536 SCIP_REOPT* reopt, /**< reoptimization data structure */
9537 SCIP_LP* lp, /**< current LP data */
9538 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9539 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9540 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9541 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9542 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9543 SCIP_Bool implvarfixing, /**< fixing b in implication */
9544 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9545 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9546 )
9547 {
9548 SCIP_VAR** implvars;
9549 SCIP_BOUNDTYPE* impltypes;
9550 SCIP_Real* implbounds;
9551 int nimpls;
9552 int i;
9553
9554 *infeasible = FALSE;
9555
9556 /* binary variable: implications of implvar */
9557 nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
9558 implvars = SCIPimplicsGetVars(implvar->implics, implvarfixing);
9559 impltypes = SCIPimplicsGetTypes(implvar->implics, implvarfixing);
9560 implbounds = SCIPimplicsGetBounds(implvar->implics, implvarfixing);
9561
9562 /* if variable has too many implications, the implication graph may become too dense */
9563 i = MIN(nimpls, MAXIMPLSCLOSURE) - 1;
9564
9565 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9566 * implvars[i] is fixed, s.t. the implication y == varfixing -> z <= b / z >= b is deleted; this affects the
9567 * array over which we currently iterate; the only thing that can happen, is that elements of the array are
9568 * deleted; in this case, the subsequent elements are moved to the front; if we iterate from back to front, the
9569 * only thing that can happen is that we add the same implication twice - this does no harm
9570 */
9571 while ( i >= 0 && !(*infeasible) )
9572 {
9573 SCIP_Bool added;
9574
9575 assert(implvars[i] != implvar);
9576
9577 /* we have x == varfixing -> y == implvarfixing -> z <= b / z >= b:
9578 * add implication x == varfixing -> z <= b / z >= b to the implications list of x
9579 */
9580 if( SCIPvarIsActive(implvars[i]) )
9581 {
9582 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
9583 eventqueue, varfixing, implvars[i], impltypes[i], implbounds[i], TRUE, infeasible, nbdchgs, &added) );
9584 assert(SCIPimplicsGetNImpls(implvar->implics, implvarfixing) <= nimpls);
9585 nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
9586 i = MIN(i, nimpls); /* some elements from the array could have been removed */
9587 }
9588 --i;
9589 }
9590
9591 return SCIP_OKAY;
9592 }
9593
9594 /** adds given implication to the variable's implication list, and adds all implications directly implied by this
9595 * implication to the variable's implication list;
9596 * if the implication is conflicting, the variable is fixed to the opposite value;
9597 * if the variable is already fixed to the given value, the implication is performed immediately;
9598 * if the implication is redundant with respect to the variables' global bounds, it is ignored
9599 */
9600 static
varAddTransitiveImplic(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_BOUNDTYPE impltype,SCIP_Real implbound,SCIP_Bool transitive,SCIP_Bool * infeasible,int * nbdchgs)9601 SCIP_RETCODE varAddTransitiveImplic(
9602 SCIP_VAR* var, /**< problem variable */
9603 BMS_BLKMEM* blkmem, /**< block memory */
9604 SCIP_SET* set, /**< global SCIP settings */
9605 SCIP_STAT* stat, /**< problem statistics */
9606 SCIP_PROB* transprob, /**< transformed problem */
9607 SCIP_PROB* origprob, /**< original problem */
9608 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9609 SCIP_REOPT* reopt, /**< reoptimization data structure */
9610 SCIP_LP* lp, /**< current LP data */
9611 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9612 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9613 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9614 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
9615 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
9616 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
9617 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
9618 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
9619 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9620 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
9621 )
9622 {
9623 SCIP_Bool added;
9624
9625 assert(var != NULL);
9626 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
9627 assert(SCIPvarIsActive(var));
9628 assert(implvar != NULL);
9629 assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
9630 assert(infeasible != NULL);
9631
9632 /* add implication x == varfixing -> y <= b / y >= b to the implications list of x */
9633 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
9634 eventqueue, varfixing, implvar, impltype, implbound, FALSE, infeasible, nbdchgs, &added) );
9635
9636 if( *infeasible || var == implvar || !transitive || !added )
9637 return SCIP_OKAY;
9638
9639 assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */
9640
9641 /* add transitive closure */
9642 if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY )
9643 {
9644 SCIP_Bool implvarfixing;
9645
9646 implvarfixing = (impltype == SCIP_BOUNDTYPE_LOWER);
9647
9648 /* binary variable: implications of implvar */
9649 SCIP_CALL( varAddTransitiveBinaryClosureImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
9650 cliquetable, branchcand, eventqueue, varfixing, implvar, implvarfixing, infeasible, nbdchgs) );
9651
9652 /* inverse implication */
9653 if( !(*infeasible) )
9654 {
9655 SCIP_CALL( varAddTransitiveBinaryClosureImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
9656 cliquetable, branchcand, eventqueue, !implvarfixing, var, !varfixing, infeasible, nbdchgs) );
9657 }
9658 }
9659 else
9660 {
9661 /* non-binary variable: variable lower bounds of implvar */
9662 if( impltype == SCIP_BOUNDTYPE_UPPER && implvar->vlbs != NULL )
9663 {
9664 SCIP_VAR** vlbvars;
9665 SCIP_Real* vlbcoefs;
9666 SCIP_Real* vlbconstants;
9667 int nvlbvars;
9668 int i;
9669
9670 nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
9671 vlbvars = SCIPvboundsGetVars(implvar->vlbs);
9672 vlbcoefs = SCIPvboundsGetCoefs(implvar->vlbs);
9673 vlbconstants = SCIPvboundsGetConstants(implvar->vlbs);
9674
9675 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9676 * vlbvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
9677 * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
9678 * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
9679 * is that we add the same implication twice - this does no harm
9680 */
9681 i = nvlbvars-1;
9682 while ( i >= 0 && !(*infeasible) )
9683 {
9684 assert(vlbvars[i] != implvar);
9685 assert(!SCIPsetIsZero(set, vlbcoefs[i]));
9686
9687 /* we have x == varfixing -> y <= b and y >= c*z + d:
9688 * c > 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
9689 * c < 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
9690 *
9691 * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
9692 * SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
9693 * aggregation variable (the one which will stay active);
9694 *
9695 * W.l.o.g. we consider the variable upper bounds for now. Let "vubvar" be a variable upper bound of
9696 * the aggregated variable "aggvar"; During that copying of that variable upper bound variable
9697 * "vubvar" the variable lower and upper bounds of this variable "vubvar" are also considered; note
9698 * that the "aggvar" can be a variable lower bound variable of the variable "vubvar"; Due to that
9699 * situation it can happen that we reach that code place where "vlbvars[i] == aggvar". In particular
9700 * the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
9701 * but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
9702 * have to explicitly check that the active variable has not a variable status
9703 * SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
9704 */
9705 if( SCIPvarIsActive(vlbvars[i]) && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_NEGATED )
9706 {
9707 SCIP_Real vbimplbound;
9708
9709 vbimplbound = (implbound - vlbconstants[i])/vlbcoefs[i];
9710 if( vlbcoefs[i] >= 0.0 )
9711 {
9712 vbimplbound = adjustedUb(set, SCIPvarGetType(vlbvars[i]), vbimplbound);
9713 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9714 branchcand, eventqueue, varfixing, vlbvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
9715 infeasible, nbdchgs, &added) );
9716 }
9717 else
9718 {
9719 vbimplbound = adjustedLb(set, SCIPvarGetType(vlbvars[i]), vbimplbound);
9720 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9721 branchcand, eventqueue, varfixing, vlbvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
9722 infeasible, nbdchgs, &added) );
9723 }
9724 nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
9725 i = MIN(i, nvlbvars); /* some elements from the array could have been removed */
9726 }
9727 --i;
9728 }
9729 }
9730
9731 /* non-binary variable: variable upper bounds of implvar */
9732 if( impltype == SCIP_BOUNDTYPE_LOWER && implvar->vubs != NULL )
9733 {
9734 SCIP_VAR** vubvars;
9735 SCIP_Real* vubcoefs;
9736 SCIP_Real* vubconstants;
9737 int nvubvars;
9738 int i;
9739
9740 nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
9741 vubvars = SCIPvboundsGetVars(implvar->vubs);
9742 vubcoefs = SCIPvboundsGetCoefs(implvar->vubs);
9743 vubconstants = SCIPvboundsGetConstants(implvar->vubs);
9744
9745 /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
9746 * vubvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
9747 * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
9748 * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
9749 * is that we add the same implication twice - this does no harm
9750 */
9751 i = nvubvars-1;
9752 while ( i >= 0 && !(*infeasible) )
9753 {
9754 assert(vubvars[i] != implvar);
9755 assert(!SCIPsetIsZero(set, vubcoefs[i]));
9756
9757 /* we have x == varfixing -> y >= b and y <= c*z + d:
9758 * c > 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
9759 * c < 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
9760 *
9761 * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
9762 * SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
9763 * aggregation variable (the one which will stay active);
9764 *
9765 * W.l.o.g. we consider the variable lower bounds for now. Let "vlbvar" be a variable lower bound of
9766 * the aggregated variable "aggvar"; During that copying of that variable lower bound variable
9767 * "vlbvar" the variable lower and upper bounds of this variable "vlbvar" are also considered; note
9768 * that the "aggvar" can be a variable upper bound variable of the variable "vlbvar"; Due to that
9769 * situation it can happen that we reach that code place where "vubvars[i] == aggvar". In particular
9770 * the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
9771 * but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
9772 * have to explicitly check that the active variable has not a variable status
9773 * SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
9774 */
9775 if( SCIPvarIsActive(vubvars[i]) && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_NEGATED )
9776 {
9777 SCIP_Real vbimplbound;
9778
9779 vbimplbound = (implbound - vubconstants[i])/vubcoefs[i];
9780 if( vubcoefs[i] >= 0.0 )
9781 {
9782 vbimplbound = adjustedLb(set, SCIPvarGetType(vubvars[i]), vbimplbound);
9783 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9784 branchcand, eventqueue, varfixing, vubvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
9785 infeasible, nbdchgs, &added) );
9786 }
9787 else
9788 {
9789 vbimplbound = adjustedUb(set, SCIPvarGetType(vubvars[i]), vbimplbound);
9790 SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
9791 branchcand, eventqueue, varfixing, vubvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
9792 infeasible, nbdchgs, &added) );
9793 }
9794 nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
9795 i = MIN(i, nvubvars); /* some elements from the array could have been removed */
9796 }
9797 --i;
9798 }
9799 }
9800 }
9801
9802 return SCIP_OKAY;
9803 }
9804
9805 /** informs variable x about a globally valid variable lower bound x >= b*z + d with integer variable z;
9806 * if z is binary, the corresponding valid implication for z is also added;
9807 * improves the global bounds of the variable and the vlb variable if possible
9808 */
SCIPvarAddVlb(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_VAR * vlbvar,SCIP_Real vlbcoef,SCIP_Real vlbconstant,SCIP_Bool transitive,SCIP_Bool * infeasible,int * nbdchgs)9809 SCIP_RETCODE SCIPvarAddVlb(
9810 SCIP_VAR* var, /**< problem variable */
9811 BMS_BLKMEM* blkmem, /**< block memory */
9812 SCIP_SET* set, /**< global SCIP settings */
9813 SCIP_STAT* stat, /**< problem statistics */
9814 SCIP_PROB* transprob, /**< transformed problem */
9815 SCIP_PROB* origprob, /**< original problem */
9816 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
9817 SCIP_REOPT* reopt, /**< reoptimization data structure */
9818 SCIP_LP* lp, /**< current LP data */
9819 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
9820 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
9821 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
9822 SCIP_VAR* vlbvar, /**< variable z in x >= b*z + d */
9823 SCIP_Real vlbcoef, /**< coefficient b in x >= b*z + d */
9824 SCIP_Real vlbconstant, /**< constant d in x >= b*z + d */
9825 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
9826 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
9827 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
9828 )
9829 {
9830 assert(var != NULL);
9831 assert(set != NULL);
9832 assert(var->scip == set->scip);
9833 assert(SCIPvarGetType(vlbvar) != SCIP_VARTYPE_CONTINUOUS);
9834 assert(infeasible != NULL);
9835
9836 SCIPsetDebugMsg(set, "adding variable lower bound <%s> >= %g<%s> + %g\n", SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);
9837
9838 *infeasible = FALSE;
9839 if( nbdchgs != NULL )
9840 *nbdchgs = 0;
9841
9842 switch( SCIPvarGetStatus(var) )
9843 {
9844 case SCIP_VARSTATUS_ORIGINAL:
9845 assert(var->data.original.transvar != NULL);
9846 SCIP_CALL( SCIPvarAddVlb(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
9847 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef, vlbconstant, transitive, infeasible, nbdchgs) );
9848 break;
9849
9850 case SCIP_VARSTATUS_COLUMN:
9851 case SCIP_VARSTATUS_LOOSE:
9852 case SCIP_VARSTATUS_FIXED:
9853 /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
9854 SCIP_CALL( SCIPvarGetProbvarSum(&vlbvar, set, &vlbcoef, &vlbconstant) );
9855 SCIPsetDebugMsg(set, " -> transformed to variable lower bound <%s> >= %g<%s> + %g\n", SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);
9856
9857 /* if the vlb coefficient is zero, just update the lower bound of the variable */
9858 if( SCIPsetIsZero(set, vlbcoef) )
9859 {
9860 if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetUbGlobal(var)) )
9861 *infeasible = TRUE;
9862 else if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetLbGlobal(var)) )
9863 {
9864 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9865 * with the local bound, in this case we need to store the bound change as pending bound change
9866 */
9867 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9868 {
9869 assert(tree != NULL);
9870 assert(transprob != NULL);
9871 assert(SCIPprobIsTransformed(transprob));
9872
9873 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9874 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, vlbconstant, SCIP_BOUNDTYPE_LOWER, FALSE) );
9875 }
9876 else
9877 {
9878 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vlbconstant) );
9879 }
9880
9881 if( nbdchgs != NULL )
9882 (*nbdchgs)++;
9883 }
9884 }
9885 else if( var == vlbvar )
9886 {
9887 /* the variables cancels out, the variable bound constraint is either redundant or proves global infeasibility */
9888 if( SCIPsetIsEQ(set, vlbcoef, 1.0) )
9889 {
9890 if( SCIPsetIsPositive(set, vlbconstant) )
9891 *infeasible = TRUE;
9892 return SCIP_OKAY;
9893 }
9894 else
9895 {
9896 SCIP_Real lb = SCIPvarGetLbGlobal(var);
9897 SCIP_Real ub = SCIPvarGetUbGlobal(var);
9898
9899 /* the variable bound constraint defines a new upper bound */
9900 if( SCIPsetIsGT(set, vlbcoef, 1.0) )
9901 {
9902 SCIP_Real newub = vlbconstant / (1.0 - vlbcoef);
9903
9904 if( SCIPsetIsFeasLT(set, newub, lb) )
9905 {
9906 *infeasible = TRUE;
9907 return SCIP_OKAY;
9908 }
9909 else if( SCIPsetIsFeasLT(set, newub, ub) )
9910 {
9911 /* bound might be adjusted due to integrality condition */
9912 newub = adjustedUb(set, SCIPvarGetType(var), newub);
9913
9914 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9915 * with the local bound, in this case we need to store the bound change as pending bound change
9916 */
9917 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9918 {
9919 assert(tree != NULL);
9920 assert(transprob != NULL);
9921 assert(SCIPprobIsTransformed(transprob));
9922
9923 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9924 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
9925 }
9926 else
9927 {
9928 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
9929 }
9930
9931 if( nbdchgs != NULL )
9932 (*nbdchgs)++;
9933 }
9934 }
9935 /* the variable bound constraint defines a new lower bound */
9936 else
9937 {
9938 SCIP_Real newlb;
9939
9940 assert(SCIPsetIsLT(set, vlbcoef, 1.0));
9941
9942 newlb = vlbconstant / (1.0 - vlbcoef);
9943
9944 if( SCIPsetIsFeasGT(set, newlb, ub) )
9945 {
9946 *infeasible = TRUE;
9947 return SCIP_OKAY;
9948 }
9949 else if( SCIPsetIsFeasGT(set, newlb, lb) )
9950 {
9951 /* bound might be adjusted due to integrality condition */
9952 newlb = adjustedLb(set, SCIPvarGetType(var), newlb);
9953
9954 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
9955 * with the local bound, in this case we need to store the bound change as pending bound change
9956 */
9957 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
9958 {
9959 assert(tree != NULL);
9960 assert(transprob != NULL);
9961 assert(SCIPprobIsTransformed(transprob));
9962
9963 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
9964 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
9965 }
9966 else
9967 {
9968 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
9969 }
9970
9971 if( nbdchgs != NULL )
9972 (*nbdchgs)++;
9973 }
9974 }
9975 }
9976 }
9977 else if( SCIPvarIsActive(vlbvar) )
9978 {
9979 SCIP_Real xlb;
9980 SCIP_Real xub;
9981 SCIP_Real zlb;
9982 SCIP_Real zub;
9983 SCIP_Real minvlb;
9984 SCIP_Real maxvlb;
9985
9986 assert(SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_COLUMN);
9987 assert(vlbcoef != 0.0);
9988
9989 minvlb = -SCIPsetInfinity(set);
9990 maxvlb = SCIPsetInfinity(set);
9991
9992 xlb = SCIPvarGetLbGlobal(var);
9993 xub = SCIPvarGetUbGlobal(var);
9994 zlb = SCIPvarGetLbGlobal(vlbvar);
9995 zub = SCIPvarGetUbGlobal(vlbvar);
9996
9997 /* improve global bounds of vlb variable, and calculate minimal and maximal value of variable bound */
9998 if( vlbcoef >= 0.0 )
9999 {
10000 SCIP_Real newzub;
10001
10002 if( !SCIPsetIsInfinity(set, xub) )
10003 {
10004 /* x >= b*z + d -> z <= (x-d)/b */
10005 newzub = (xub - vlbconstant)/vlbcoef;
10006
10007 /* return if the new bound is less than -infinity */
10008 if( SCIPsetIsInfinity(set, REALABS(newzub)) )
10009 return SCIP_OKAY;
10010
10011 if( SCIPsetIsFeasLT(set, newzub, zlb) )
10012 {
10013 *infeasible = TRUE;
10014 return SCIP_OKAY;
10015 }
10016 if( SCIPsetIsFeasLT(set, newzub, zub) )
10017 {
10018 /* bound might be adjusted due to integrality condition */
10019 newzub = adjustedUb(set, SCIPvarGetType(vlbvar), newzub);
10020
10021 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10022 * with the local bound, in this case we need to store the bound change as pending bound change
10023 */
10024 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10025 {
10026 assert(tree != NULL);
10027 assert(transprob != NULL);
10028 assert(SCIPprobIsTransformed(transprob));
10029
10030 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10031 tree, reopt, lp, branchcand, eventqueue, cliquetable, vlbvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10032 }
10033 else
10034 {
10035 SCIP_CALL( SCIPvarChgUbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
10036 }
10037 zub = newzub;
10038
10039 if( nbdchgs != NULL )
10040 (*nbdchgs)++;
10041 }
10042 maxvlb = vlbcoef * zub + vlbconstant;
10043 if( !SCIPsetIsInfinity(set, -zlb) )
10044 minvlb = vlbcoef * zlb + vlbconstant;
10045 }
10046 else
10047 {
10048 if( !SCIPsetIsInfinity(set, zub) )
10049 maxvlb = vlbcoef * zub + vlbconstant;
10050 if( !SCIPsetIsInfinity(set, -zlb) )
10051 minvlb = vlbcoef * zlb + vlbconstant;
10052 }
10053 }
10054 else
10055 {
10056 SCIP_Real newzlb;
10057
10058 if( !SCIPsetIsInfinity(set, xub) )
10059 {
10060 /* x >= b*z + d -> z >= (x-d)/b */
10061 newzlb = (xub - vlbconstant)/vlbcoef;
10062
10063 /* return if the new bound is larger than infinity */
10064 if( SCIPsetIsInfinity(set, REALABS(newzlb)) )
10065 return SCIP_OKAY;
10066
10067 if( SCIPsetIsFeasGT(set, newzlb, zub) )
10068 {
10069 *infeasible = TRUE;
10070 return SCIP_OKAY;
10071 }
10072 if( SCIPsetIsFeasGT(set, newzlb, zlb) )
10073 {
10074 /* bound might be adjusted due to integrality condition */
10075 newzlb = adjustedLb(set, SCIPvarGetType(vlbvar), newzlb);
10076
10077 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10078 * with the local bound, in this case we need to store the bound change as pending bound change
10079 */
10080 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10081 {
10082 assert(tree != NULL);
10083 assert(transprob != NULL);
10084 assert(SCIPprobIsTransformed(transprob));
10085
10086 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10087 tree, reopt, lp, branchcand, eventqueue, cliquetable, vlbvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10088 }
10089 else
10090 {
10091 SCIP_CALL( SCIPvarChgLbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
10092 }
10093 zlb = newzlb;
10094
10095 if( nbdchgs != NULL )
10096 (*nbdchgs)++;
10097 }
10098 maxvlb = vlbcoef * zlb + vlbconstant;
10099 if( !SCIPsetIsInfinity(set, zub) )
10100 minvlb = vlbcoef * zub + vlbconstant;
10101 }
10102 else
10103 {
10104 if( !SCIPsetIsInfinity(set, -zlb) )
10105 maxvlb = vlbcoef * zlb + vlbconstant;
10106 if( !SCIPsetIsInfinity(set, zub) )
10107 minvlb = vlbcoef * zub + vlbconstant;
10108 }
10109 }
10110 if( maxvlb < minvlb )
10111 maxvlb = minvlb;
10112
10113 /* adjust bounds due to integrality of variable */
10114 minvlb = adjustedLb(set, SCIPvarGetType(var), minvlb);
10115 maxvlb = adjustedLb(set, SCIPvarGetType(var), maxvlb);
10116
10117 /* check bounds for feasibility */
10118 if( SCIPsetIsFeasGT(set, minvlb, xub) || (var == vlbvar && SCIPsetIsEQ(set, vlbcoef, 1.0) && SCIPsetIsFeasPositive(set, vlbconstant)) )
10119 {
10120 *infeasible = TRUE;
10121 return SCIP_OKAY;
10122 }
10123 /* improve global lower bound of variable */
10124 if( SCIPsetIsFeasGT(set, minvlb, xlb) )
10125 {
10126 /* bound might be adjusted due to integrality condition */
10127 minvlb = adjustedLb(set, SCIPvarGetType(var), minvlb);
10128
10129 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10130 * with the local bound, in this case we need to store the bound change as pending bound change
10131 */
10132 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10133 {
10134 assert(tree != NULL);
10135 assert(transprob != NULL);
10136 assert(SCIPprobIsTransformed(transprob));
10137
10138 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10139 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, minvlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10140 }
10141 else
10142 {
10143 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, minvlb) );
10144 }
10145 xlb = minvlb;
10146
10147 if( nbdchgs != NULL )
10148 (*nbdchgs)++;
10149 }
10150 minvlb = xlb;
10151
10152 /* improve variable bound for binary z by moving the variable's global bound to the vlb constant */
10153 if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY )
10154 {
10155 /* b > 0: x >= (maxvlb - minvlb) * z + minvlb
10156 * b < 0: x >= (minvlb - maxvlb) * z + maxvlb
10157 */
10158
10159 assert(!SCIPsetIsInfinity(set, maxvlb) && !SCIPsetIsInfinity(set, -minvlb));
10160
10161 if( vlbcoef >= 0.0 )
10162 {
10163 vlbcoef = maxvlb - minvlb;
10164 vlbconstant = minvlb;
10165 }
10166 else
10167 {
10168 vlbcoef = minvlb - maxvlb;
10169 vlbconstant = maxvlb;
10170 }
10171 }
10172
10173 /* add variable bound to the variable bounds list */
10174 if( SCIPsetIsFeasGT(set, maxvlb, xlb) )
10175 {
10176 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
10177 assert(!SCIPsetIsZero(set, vlbcoef));
10178
10179 /* if one of the variables is binary, add the corresponding implication to the variable's implication
10180 * list, thereby also adding the variable bound (or implication) to the other variable
10181 */
10182 if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY )
10183 {
10184 /* add corresponding implication:
10185 * b > 0, x >= b*z + d <-> z == 1 -> x >= b+d
10186 * b < 0, x >= b*z + d <-> z == 0 -> x >= d
10187 */
10188 SCIP_CALL( varAddTransitiveImplic(vlbvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10189 cliquetable, branchcand, eventqueue, (vlbcoef >= 0.0), var, SCIP_BOUNDTYPE_LOWER, maxvlb, transitive,
10190 infeasible, nbdchgs) );
10191 }
10192 else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
10193 {
10194 /* add corresponding implication:
10195 * b > 0, x >= b*z + d <-> x == 0 -> z <= -d/b
10196 * b < 0, x >= b*z + d <-> x == 0 -> z >= -d/b
10197 */
10198 SCIP_Real implbound;
10199 implbound = -vlbconstant/vlbcoef;
10200
10201 /* tighten the implication bound if the variable is integer */
10202 if( SCIPvarIsIntegral(vlbvar) )
10203 {
10204 if( vlbcoef >= 0 )
10205 implbound = SCIPsetFloor(set, implbound);
10206 else
10207 implbound = SCIPsetCeil(set, implbound);
10208 }
10209 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10210 cliquetable, branchcand, eventqueue, FALSE, vlbvar, (vlbcoef >= 0.0 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER),
10211 implbound, transitive, infeasible, nbdchgs) );
10212 }
10213 else
10214 {
10215 SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, vlbvar, vlbcoef, vlbconstant) );
10216 }
10217 }
10218 }
10219 break;
10220
10221 case SCIP_VARSTATUS_AGGREGATED:
10222 /* x = a*y + c: x >= b*z + d <=> a*y + c >= b*z + d <=> y >= b/a * z + (d-c)/a, if a > 0
10223 * y <= b/a * z + (d-c)/a, if a < 0
10224 */
10225 assert(var->data.aggregate.var != NULL);
10226 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
10227 {
10228 /* a > 0 -> add variable lower bound */
10229 SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10230 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef/var->data.aggregate.scalar,
10231 (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10232 }
10233 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
10234 {
10235 /* a < 0 -> add variable upper bound */
10236 SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10237 cliquetable, branchcand, eventqueue, vlbvar, vlbcoef/var->data.aggregate.scalar,
10238 (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10239 }
10240 else
10241 {
10242 SCIPerrorMessage("scalar is zero in aggregation\n");
10243 return SCIP_INVALIDDATA;
10244 }
10245 break;
10246
10247 case SCIP_VARSTATUS_MULTAGGR:
10248 /* nothing to do here */
10249 break;
10250
10251 case SCIP_VARSTATUS_NEGATED:
10252 /* x = offset - x': x >= b*z + d <=> offset - x' >= b*z + d <=> x' <= -b*z + (offset-d) */
10253 assert(var->negatedvar != NULL);
10254 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
10255 assert(var->negatedvar->negatedvar == var);
10256 SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10257 branchcand, eventqueue, vlbvar, -vlbcoef, var->data.negate.constant - vlbconstant, transitive, infeasible,
10258 nbdchgs) );
10259 break;
10260
10261 default:
10262 SCIPerrorMessage("unknown variable status\n");
10263 return SCIP_INVALIDDATA;
10264 }
10265
10266 return SCIP_OKAY;
10267 }
10268
10269 /** informs variable x about a globally valid variable upper bound x <= b*z + d with integer variable z;
10270 * if z is binary, the corresponding valid implication for z is also added;
10271 * updates the global bounds of the variable and the vub variable correspondingly
10272 */
SCIPvarAddVub(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_VAR * vubvar,SCIP_Real vubcoef,SCIP_Real vubconstant,SCIP_Bool transitive,SCIP_Bool * infeasible,int * nbdchgs)10273 SCIP_RETCODE SCIPvarAddVub(
10274 SCIP_VAR* var, /**< problem variable */
10275 BMS_BLKMEM* blkmem, /**< block memory */
10276 SCIP_SET* set, /**< global SCIP settings */
10277 SCIP_STAT* stat, /**< problem statistics */
10278 SCIP_PROB* transprob, /**< transformed problem */
10279 SCIP_PROB* origprob, /**< original problem */
10280 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10281 SCIP_REOPT* reopt, /**< reoptimization data structure */
10282 SCIP_LP* lp, /**< current LP data */
10283 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10284 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10285 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10286 SCIP_VAR* vubvar, /**< variable z in x <= b*z + d */
10287 SCIP_Real vubcoef, /**< coefficient b in x <= b*z + d */
10288 SCIP_Real vubconstant, /**< constant d in x <= b*z + d */
10289 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
10290 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10291 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
10292 )
10293 {
10294 assert(var != NULL);
10295 assert(set != NULL);
10296 assert(var->scip == set->scip);
10297 assert(SCIPvarGetType(vubvar) != SCIP_VARTYPE_CONTINUOUS);
10298 assert(infeasible != NULL);
10299
10300 SCIPsetDebugMsg(set, "adding variable upper bound <%s> <= %g<%s> + %g\n", SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);
10301
10302 *infeasible = FALSE;
10303 if( nbdchgs != NULL )
10304 *nbdchgs = 0;
10305
10306 switch( SCIPvarGetStatus(var) )
10307 {
10308 case SCIP_VARSTATUS_ORIGINAL:
10309 assert(var->data.original.transvar != NULL);
10310 SCIP_CALL( SCIPvarAddVub(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10311 cliquetable, branchcand, eventqueue, vubvar, vubcoef, vubconstant, transitive, infeasible, nbdchgs) );
10312 break;
10313
10314 case SCIP_VARSTATUS_COLUMN:
10315 case SCIP_VARSTATUS_LOOSE:
10316 case SCIP_VARSTATUS_FIXED:
10317 /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
10318 SCIP_CALL( SCIPvarGetProbvarSum(&vubvar, set, &vubcoef, &vubconstant) );
10319 SCIPsetDebugMsg(set, " -> transformed to variable upper bound <%s> <= %g<%s> + %g\n",
10320 SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);
10321
10322 /* if the vub coefficient is zero, just update the upper bound of the variable */
10323 if( SCIPsetIsZero(set, vubcoef) )
10324 {
10325 if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetLbGlobal(var)) )
10326 *infeasible = TRUE;
10327 else if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetUbGlobal(var)) )
10328 {
10329 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10330 * with the local bound, in this case we need to store the bound change as pending bound change
10331 */
10332 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10333 {
10334 assert(tree != NULL);
10335 assert(transprob != NULL);
10336 assert(SCIPprobIsTransformed(transprob));
10337
10338 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10339 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, vubconstant, SCIP_BOUNDTYPE_UPPER, FALSE) );
10340 }
10341 else
10342 {
10343 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vubconstant) );
10344 }
10345
10346 if( nbdchgs != NULL )
10347 (*nbdchgs)++;
10348 }
10349 }
10350 else if( var == vubvar )
10351 {
10352 /* the variables cancels out, the variable bound constraint is either redundant or proves global infeasibility */
10353 if( SCIPsetIsEQ(set, vubcoef, 1.0) )
10354 {
10355 if( SCIPsetIsNegative(set, vubconstant) )
10356 *infeasible = TRUE;
10357 return SCIP_OKAY;
10358 }
10359 else
10360 {
10361 SCIP_Real lb = SCIPvarGetLbGlobal(var);
10362 SCIP_Real ub = SCIPvarGetUbGlobal(var);
10363
10364 /* the variable bound constraint defines a new lower bound */
10365 if( SCIPsetIsGT(set, vubcoef, 1.0) )
10366 {
10367 SCIP_Real newlb = vubconstant / (1.0 - vubcoef);
10368
10369 if( SCIPsetIsFeasGT(set, newlb, ub) )
10370 {
10371 *infeasible = TRUE;
10372 return SCIP_OKAY;
10373 }
10374 else if( SCIPsetIsFeasGT(set, newlb, lb) )
10375 {
10376 /* bound might be adjusted due to integrality condition */
10377 newlb = adjustedLb(set, SCIPvarGetType(var), newlb);
10378
10379 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10380 * with the local bound, in this case we need to store the bound change as pending bound change
10381 */
10382 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10383 {
10384 assert(tree != NULL);
10385 assert(transprob != NULL);
10386 assert(SCIPprobIsTransformed(transprob));
10387
10388 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10389 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10390 }
10391 else
10392 {
10393 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
10394 }
10395
10396 if( nbdchgs != NULL )
10397 (*nbdchgs)++;
10398 }
10399 }
10400 /* the variable bound constraint defines a new upper bound */
10401 else
10402 {
10403 SCIP_Real newub;
10404
10405 assert(SCIPsetIsLT(set, vubcoef, 1.0));
10406
10407 newub = vubconstant / (1.0 - vubcoef);
10408
10409 if( SCIPsetIsFeasLT(set, newub, lb) )
10410 {
10411 *infeasible = TRUE;
10412 return SCIP_OKAY;
10413 }
10414 else if( SCIPsetIsFeasLT(set, newub, ub) )
10415 {
10416 /* bound might be adjusted due to integrality condition */
10417 newub = adjustedUb(set, SCIPvarGetType(var), newub);
10418
10419 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10420 * with the local bound, in this case we need to store the bound change as pending bound change
10421 */
10422 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10423 {
10424 assert(tree != NULL);
10425 assert(transprob != NULL);
10426 assert(SCIPprobIsTransformed(transprob));
10427
10428 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10429 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10430 }
10431 else
10432 {
10433 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
10434 }
10435
10436 if( nbdchgs != NULL )
10437 (*nbdchgs)++;
10438 }
10439 }
10440 }
10441 }
10442 else if( SCIPvarIsActive(vubvar) )
10443 {
10444 SCIP_Real xlb;
10445 SCIP_Real xub;
10446 SCIP_Real zlb;
10447 SCIP_Real zub;
10448 SCIP_Real minvub;
10449 SCIP_Real maxvub;
10450
10451 assert(SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_COLUMN);
10452 assert(vubcoef != 0.0);
10453
10454 minvub = -SCIPsetInfinity(set);
10455 maxvub = SCIPsetInfinity(set);
10456
10457 xlb = SCIPvarGetLbGlobal(var);
10458 xub = SCIPvarGetUbGlobal(var);
10459 zlb = SCIPvarGetLbGlobal(vubvar);
10460 zub = SCIPvarGetUbGlobal(vubvar);
10461
10462 /* improve global bounds of vub variable, and calculate minimal and maximal value of variable bound */
10463 if( vubcoef >= 0.0 )
10464 {
10465 SCIP_Real newzlb;
10466
10467 if( !SCIPsetIsInfinity(set, -xlb) )
10468 {
10469 /* x <= b*z + d -> z >= (x-d)/b */
10470 newzlb = (xlb - vubconstant)/vubcoef;
10471 if( SCIPsetIsFeasGT(set, newzlb, zub) )
10472 {
10473 *infeasible = TRUE;
10474 return SCIP_OKAY;
10475 }
10476 if( SCIPsetIsFeasGT(set, newzlb, zlb) )
10477 {
10478 /* bound might be adjusted due to integrality condition */
10479 newzlb = adjustedLb(set, SCIPvarGetType(vubvar), newzlb);
10480
10481 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10482 * with the local bound, in this case we need to store the bound change as pending bound change
10483 */
10484 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10485 {
10486 assert(tree != NULL);
10487 assert(transprob != NULL);
10488 assert(SCIPprobIsTransformed(transprob));
10489
10490 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10491 tree, reopt, lp, branchcand, eventqueue, cliquetable, vubvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
10492 }
10493 else
10494 {
10495 SCIP_CALL( SCIPvarChgLbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
10496 }
10497 zlb = newzlb;
10498
10499 if( nbdchgs != NULL )
10500 (*nbdchgs)++;
10501 }
10502 minvub = vubcoef * zlb + vubconstant;
10503 if( !SCIPsetIsInfinity(set, zub) )
10504 maxvub = vubcoef * zub + vubconstant;
10505 }
10506 else
10507 {
10508 if( !SCIPsetIsInfinity(set, zub) )
10509 maxvub = vubcoef * zub + vubconstant;
10510 if( !SCIPsetIsInfinity(set, -zlb) )
10511 minvub = vubcoef * zlb + vubconstant;
10512 }
10513 }
10514 else
10515 {
10516 SCIP_Real newzub;
10517
10518 if( !SCIPsetIsInfinity(set, -xlb) )
10519 {
10520 /* x <= b*z + d -> z <= (x-d)/b */
10521 newzub = (xlb - vubconstant)/vubcoef;
10522 if( SCIPsetIsFeasLT(set, newzub, zlb) )
10523 {
10524 *infeasible = TRUE;
10525 return SCIP_OKAY;
10526 }
10527 if( SCIPsetIsFeasLT(set, newzub, zub) )
10528 {
10529 /* bound might be adjusted due to integrality condition */
10530 newzub = adjustedUb(set, SCIPvarGetType(vubvar), newzub);
10531
10532 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10533 * with the local bound, in this case we need to store the bound change as pending bound change
10534 */
10535 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10536 {
10537 assert(tree != NULL);
10538 assert(transprob != NULL);
10539 assert(SCIPprobIsTransformed(transprob));
10540
10541 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10542 tree, reopt, lp, branchcand, eventqueue, cliquetable, vubvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10543 }
10544 else
10545 {
10546 SCIP_CALL( SCIPvarChgUbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
10547 }
10548 zub = newzub;
10549
10550 if( nbdchgs != NULL )
10551 (*nbdchgs)++;
10552 }
10553 minvub = vubcoef * zub + vubconstant;
10554 if( !SCIPsetIsInfinity(set, -zlb) )
10555 maxvub = vubcoef * zlb + vubconstant;
10556 }
10557 else
10558 {
10559 if( !SCIPsetIsInfinity(set, zub) )
10560 minvub = vubcoef * zub + vubconstant;
10561 if( !SCIPsetIsInfinity(set, -zlb) )
10562 maxvub = vubcoef * zlb + vubconstant;
10563 }
10564 }
10565 if( minvub > maxvub )
10566 minvub = maxvub;
10567
10568 /* adjust bounds due to integrality of vub variable */
10569 minvub = adjustedUb(set, SCIPvarGetType(var), minvub);
10570 maxvub = adjustedUb(set, SCIPvarGetType(var), maxvub);
10571
10572 /* check bounds for feasibility */
10573 if( SCIPsetIsFeasLT(set, maxvub, xlb) || (var == vubvar && SCIPsetIsEQ(set, vubcoef, 1.0) && SCIPsetIsFeasNegative(set, vubconstant)) )
10574 {
10575 *infeasible = TRUE;
10576 return SCIP_OKAY;
10577 }
10578
10579 /* improve global upper bound of variable */
10580 if( SCIPsetIsFeasLT(set, maxvub, xub) )
10581 {
10582 /* bound might be adjusted due to integrality condition */
10583 maxvub = adjustedUb(set, SCIPvarGetType(var), maxvub);
10584
10585 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10586 * with the local bound, in this case we need to store the bound change as pending bound change
10587 */
10588 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10589 {
10590 assert(tree != NULL);
10591 assert(transprob != NULL);
10592 assert(SCIPprobIsTransformed(transprob));
10593
10594 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10595 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, maxvub, SCIP_BOUNDTYPE_UPPER, FALSE) );
10596 }
10597 else
10598 {
10599 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, maxvub) );
10600 }
10601 xub = maxvub;
10602
10603 if( nbdchgs != NULL )
10604 (*nbdchgs)++;
10605 }
10606 maxvub = xub;
10607
10608 /* improve variable bound for binary z by moving the variable's global bound to the vub constant */
10609 if( SCIPvarIsBinary(vubvar) )
10610 {
10611 /* b > 0: x <= (maxvub - minvub) * z + minvub
10612 * b < 0: x <= (minvub - maxvub) * z + maxvub
10613 */
10614
10615 assert(!SCIPsetIsInfinity(set, maxvub) && !SCIPsetIsInfinity(set, -minvub));
10616
10617 if( vubcoef >= 0.0 )
10618 {
10619 vubcoef = maxvub - minvub;
10620 vubconstant = minvub;
10621 }
10622 else
10623 {
10624 vubcoef = minvub - maxvub;
10625 vubconstant = maxvub;
10626 }
10627 }
10628
10629 /* add variable bound to the variable bounds list */
10630 if( SCIPsetIsFeasLT(set, minvub, xub) )
10631 {
10632 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
10633 assert(!SCIPsetIsZero(set, vubcoef));
10634
10635 /* if one of the variables is binary, add the corresponding implication to the variable's implication
10636 * list, thereby also adding the variable bound (or implication) to the other variable
10637 */
10638 if( SCIPvarGetType(vubvar) == SCIP_VARTYPE_BINARY )
10639 {
10640 /* add corresponding implication:
10641 * b > 0, x <= b*z + d <-> z == 0 -> x <= d
10642 * b < 0, x <= b*z + d <-> z == 1 -> x <= b+d
10643 */
10644 SCIP_CALL( varAddTransitiveImplic(vubvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10645 cliquetable, branchcand, eventqueue, (vubcoef < 0.0), var, SCIP_BOUNDTYPE_UPPER, minvub, transitive,
10646 infeasible, nbdchgs) );
10647 }
10648 else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
10649 {
10650 /* add corresponding implication:
10651 * b > 0, x <= b*z + d <-> x == 1 -> z >= (1-d)/b
10652 * b < 0, x <= b*z + d <-> x == 1 -> z <= (1-d)/b
10653 */
10654 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10655 cliquetable, branchcand, eventqueue, TRUE, vubvar, (vubcoef >= 0.0 ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER),
10656 (1.0-vubconstant)/vubcoef, transitive, infeasible, nbdchgs) );
10657 }
10658 else
10659 {
10660 SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, vubvar, vubcoef, vubconstant) );
10661 }
10662 }
10663 }
10664 break;
10665
10666 case SCIP_VARSTATUS_AGGREGATED:
10667 /* x = a*y + c: x <= b*z + d <=> a*y + c <= b*z + d <=> y <= b/a * z + (d-c)/a, if a > 0
10668 * y >= b/a * z + (d-c)/a, if a < 0
10669 */
10670 assert(var->data.aggregate.var != NULL);
10671 if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
10672 {
10673 /* a > 0 -> add variable upper bound */
10674 SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10675 cliquetable, branchcand, eventqueue, vubvar, vubcoef/var->data.aggregate.scalar,
10676 (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10677 }
10678 else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
10679 {
10680 /* a < 0 -> add variable lower bound */
10681 SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10682 cliquetable, branchcand, eventqueue, vubvar, vubcoef/var->data.aggregate.scalar,
10683 (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
10684 }
10685 else
10686 {
10687 SCIPerrorMessage("scalar is zero in aggregation\n");
10688 return SCIP_INVALIDDATA;
10689 }
10690 break;
10691
10692 case SCIP_VARSTATUS_MULTAGGR:
10693 /* nothing to do here */
10694 break;
10695
10696 case SCIP_VARSTATUS_NEGATED:
10697 /* x = offset - x': x <= b*z + d <=> offset - x' <= b*z + d <=> x' >= -b*z + (offset-d) */
10698 assert(var->negatedvar != NULL);
10699 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
10700 assert(var->negatedvar->negatedvar == var);
10701 SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10702 branchcand, eventqueue, vubvar, -vubcoef, var->data.negate.constant - vubconstant, transitive, infeasible,
10703 nbdchgs) );
10704 break;
10705
10706 default:
10707 SCIPerrorMessage("unknown variable status\n");
10708 return SCIP_INVALIDDATA;
10709 }
10710
10711 return SCIP_OKAY;
10712 }
10713
10714 /** informs binary variable x about a globally valid implication: x == 0 or x == 1 ==> y <= b or y >= b;
10715 * also adds the corresponding implication or variable bound to the implied variable;
10716 * if the implication is conflicting, the variable is fixed to the opposite value;
10717 * if the variable is already fixed to the given value, the implication is performed immediately;
10718 * if the implication is redundant with respect to the variables' global bounds, it is ignored
10719 */
SCIPvarAddImplic(SCIP_VAR * var,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_CLIQUETABLE * cliquetable,SCIP_BRANCHCAND * branchcand,SCIP_EVENTQUEUE * eventqueue,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_BOUNDTYPE impltype,SCIP_Real implbound,SCIP_Bool transitive,SCIP_Bool * infeasible,int * nbdchgs)10720 SCIP_RETCODE SCIPvarAddImplic(
10721 SCIP_VAR* var, /**< problem variable */
10722 BMS_BLKMEM* blkmem, /**< block memory */
10723 SCIP_SET* set, /**< global SCIP settings */
10724 SCIP_STAT* stat, /**< problem statistics */
10725 SCIP_PROB* transprob, /**< transformed problem */
10726 SCIP_PROB* origprob, /**< original problem */
10727 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10728 SCIP_REOPT* reopt, /**< reoptimization data structure */
10729 SCIP_LP* lp, /**< current LP data */
10730 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10731 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10732 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10733 SCIP_Bool varfixing, /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
10734 SCIP_VAR* implvar, /**< variable y in implication y <= b or y >= b */
10735 SCIP_BOUNDTYPE impltype, /**< type of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
10736 SCIP_Real implbound, /**< bound b in implication y <= b or y >= b */
10737 SCIP_Bool transitive, /**< should transitive closure of implication also be added? */
10738 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10739 int* nbdchgs /**< pointer to store the number of performed bound changes, or NULL */
10740 )
10741 {
10742 assert(var != NULL);
10743 assert(set != NULL);
10744 assert(var->scip == set->scip);
10745 assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
10746 assert(infeasible != NULL);
10747
10748 *infeasible = FALSE;
10749 if( nbdchgs != NULL )
10750 *nbdchgs = 0;
10751
10752 switch( SCIPvarGetStatus(var) )
10753 {
10754 case SCIP_VARSTATUS_ORIGINAL:
10755 assert(var->data.original.transvar != NULL);
10756 SCIP_CALL( SCIPvarAddImplic(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10757 cliquetable, branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible,
10758 nbdchgs) );
10759 break;
10760
10761 case SCIP_VARSTATUS_COLUMN:
10762 case SCIP_VARSTATUS_LOOSE:
10763 /* if the variable is fixed (although it has no FIXED status), and varfixing corresponds to the fixed value of
10764 * the variable, the implication can be applied directly;
10765 * otherwise, add implication to the implications list (and add inverse of implication to the implied variable)
10766 */
10767 if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
10768 {
10769 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
10770 {
10771 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
10772 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
10773 }
10774 }
10775 else
10776 {
10777 SCIP_CALL( SCIPvarGetProbvarBound(&implvar, &implbound, &impltype) );
10778 SCIPvarAdjustBd(implvar, set, impltype, &implbound);
10779 if( SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED )
10780 {
10781 SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10782 branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
10783 }
10784 }
10785 break;
10786
10787 case SCIP_VARSTATUS_FIXED:
10788 /* if varfixing corresponds to the fixed value of the variable, the implication can be applied directly */
10789 if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
10790 {
10791 SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
10792 cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
10793 }
10794 break;
10795
10796 case SCIP_VARSTATUS_AGGREGATED:
10797 /* implication added for x == 1:
10798 * x == 1 && x = 1*z + 0 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
10799 * x == 1 && x = -1*z + 1 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
10800 * implication added for x == 0:
10801 * x == 0 && x = 1*z + 0 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
10802 * x == 0 && x = -1*z + 1 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
10803 *
10804 * use only binary variables z
10805 */
10806 assert(var->data.aggregate.var != NULL);
10807 if( SCIPvarIsBinary(var->data.aggregate.var) )
10808 {
10809 assert( (SCIPsetIsEQ(set, var->data.aggregate.scalar, 1.0) && SCIPsetIsZero(set, var->data.aggregate.constant))
10810 || (SCIPsetIsEQ(set, var->data.aggregate.scalar, -1.0) && SCIPsetIsEQ(set, var->data.aggregate.constant, 1.0)) );
10811
10812 if( var->data.aggregate.scalar > 0 )
10813 {
10814 SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10815 cliquetable, branchcand, eventqueue, varfixing, implvar, impltype, implbound, transitive, infeasible,
10816 nbdchgs) );
10817 }
10818 else
10819 {
10820 SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10821 cliquetable, branchcand, eventqueue, !varfixing, implvar, impltype, implbound, transitive, infeasible,
10822 nbdchgs) );
10823 }
10824 }
10825 break;
10826
10827 case SCIP_VARSTATUS_MULTAGGR:
10828 /* nothing to do here */
10829 break;
10830
10831 case SCIP_VARSTATUS_NEGATED:
10832 /* implication added for x == 1:
10833 * x == 1 && x = -1*z + 1 ==> y <= b or y >= b <==> z <= 0 ==> y <= b or y >= b
10834 * implication added for x == 0:
10835 * x == 0 && x = -1*z + 1 ==> y <= b or y >= b <==> z >= 1 ==> y <= b or y >= b
10836 */
10837 assert(var->negatedvar != NULL);
10838 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
10839 assert(var->negatedvar->negatedvar == var);
10840 assert(SCIPvarIsBinary(var->negatedvar));
10841
10842 if( SCIPvarGetType(var->negatedvar) == SCIP_VARTYPE_BINARY )
10843 {
10844 SCIP_CALL( SCIPvarAddImplic(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10845 cliquetable, branchcand, eventqueue, !varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
10846 }
10847 /* in case one both variables are not of binary type we have to add the implication as variable bounds */
10848 else
10849 {
10850 /* if the implied variable is of binary type exchange the variables */
10851 if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY )
10852 {
10853 SCIP_CALL( SCIPvarAddImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10854 branchcand, eventqueue, (impltype == SCIP_BOUNDTYPE_UPPER) ? TRUE : FALSE, var->negatedvar,
10855 varfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER, varfixing ? 1.0 : 0.0, transitive,
10856 infeasible, nbdchgs) );
10857 }
10858 else
10859 {
10860 /* both variables are not of binary type but are implicit binary; in that case we can only add this
10861 * implication as variable bounds
10862 */
10863
10864 /* add variable lower bound on the negation of var */
10865 if( varfixing )
10866 {
10867 /* (x = 1 => i) z = 0 ii) z = 1) <=> ( i) z = 1 ii) z = 0 => ~x = 1), this is done by adding ~x >= b*z + d
10868 * as variable lower bound
10869 */
10870 SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10871 cliquetable, branchcand, eventqueue, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : -1.0,
10872 (impltype == SCIP_BOUNDTYPE_UPPER) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
10873 }
10874 else
10875 {
10876 /* (x = 0 => i) z = 0 ii) z = 1) <=> ( i) z = 1 ii) z = 0 => ~x = 0), this is done by adding ~x <= b*z + d
10877 * as variable upper bound
10878 */
10879 SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
10880 cliquetable, branchcand, eventqueue, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? -1.0 : 1.0,
10881 (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : 0.0, transitive, infeasible, nbdchgs) );
10882 }
10883
10884 /* add variable bound on implvar */
10885 if( impltype == SCIP_BOUNDTYPE_UPPER )
10886 {
10887 /* (z = 1 => i) x = 0 ii) x = 1) <=> ( i) ~x = 0 ii) ~x = 1 => z = 0), this is done by adding z <= b*~x + d
10888 * as variable upper bound
10889 */
10890 SCIP_CALL( SCIPvarAddVub(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10891 branchcand, eventqueue, var->negatedvar, (varfixing) ? 1.0 : -1.0,
10892 (varfixing) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
10893 }
10894 else
10895 {
10896 /* (z = 0 => i) x = 0 ii) x = 1) <=> ( i) ~x = 0 ii) ~x = 1 => z = 1), this is done by adding z >= b*~x + d
10897 * as variable upper bound
10898 */
10899 SCIP_CALL( SCIPvarAddVlb(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
10900 branchcand, eventqueue, var->negatedvar, (varfixing) ? -1.0 : 1.0, (varfixing) ? 1.0 : 0.0,
10901 transitive, infeasible, nbdchgs) );
10902 }
10903 }
10904 }
10905 break;
10906
10907 default:
10908 SCIPerrorMessage("unknown variable status\n");
10909 return SCIP_INVALIDDATA;
10910 }
10911
10912 return SCIP_OKAY;
10913 }
10914
10915 /** returns whether there is an implication x == varfixing -> y <= b or y >= b in the implication graph;
10916 * implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
10917 * both variables must be active, variable x must be binary
10918 */
SCIPvarHasImplic(SCIP_VAR * var,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_BOUNDTYPE impltype)10919 SCIP_Bool SCIPvarHasImplic(
10920 SCIP_VAR* var, /**< problem variable x */
10921 SCIP_Bool varfixing, /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
10922 SCIP_VAR* implvar, /**< variable y to search for */
10923 SCIP_BOUNDTYPE impltype /**< type of implication y <=/>= b to search for */
10924 )
10925 {
10926 assert(var != NULL);
10927 assert(implvar != NULL);
10928 assert(SCIPvarIsActive(var));
10929 assert(SCIPvarIsActive(implvar));
10930 assert(SCIPvarIsBinary(var));
10931
10932 return var->implics != NULL && SCIPimplicsContainsImpl(var->implics, varfixing, implvar, impltype);
10933 }
10934
10935 /** returns whether there is an implication x == varfixing -> y == implvarfixing in the implication graph;
10936 * implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
10937 * both variables must be active binary variables
10938 */
SCIPvarHasBinaryImplic(SCIP_VAR * var,SCIP_Bool varfixing,SCIP_VAR * implvar,SCIP_Bool implvarfixing)10939 SCIP_Bool SCIPvarHasBinaryImplic(
10940 SCIP_VAR* var, /**< problem variable x */
10941 SCIP_Bool varfixing, /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
10942 SCIP_VAR* implvar, /**< variable y to search for */
10943 SCIP_Bool implvarfixing /**< value of the implied variable to search for */
10944 )
10945 {
10946 assert(SCIPvarIsBinary(implvar));
10947
10948 return SCIPvarHasImplic(var, varfixing, implvar, implvarfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER);
10949 }
10950
10951 /** fixes the bounds of a binary variable to the given value, counting bound changes and detecting infeasibility */
SCIPvarFixBinary(SCIP_VAR * var,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 value,SCIP_Bool * infeasible,int * nbdchgs)10952 SCIP_RETCODE SCIPvarFixBinary(
10953 SCIP_VAR* var, /**< problem variable */
10954 BMS_BLKMEM* blkmem, /**< block memory */
10955 SCIP_SET* set, /**< global SCIP settings */
10956 SCIP_STAT* stat, /**< problem statistics */
10957 SCIP_PROB* transprob, /**< transformed problem */
10958 SCIP_PROB* origprob, /**< original problem */
10959 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
10960 SCIP_REOPT* reopt, /**< reoptimization data structure */
10961 SCIP_LP* lp, /**< current LP data */
10962 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
10963 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
10964 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
10965 SCIP_Bool value, /**< value to fix variable to */
10966 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
10967 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
10968 )
10969 {
10970 assert(var != NULL);
10971 assert(set != NULL);
10972 assert(var->scip == set->scip);
10973 assert(infeasible != NULL);
10974
10975 *infeasible = FALSE;
10976
10977 if( value == FALSE )
10978 {
10979 if( var->glbdom.lb > 0.5 )
10980 *infeasible = TRUE;
10981 else if( var->glbdom.ub > 0.5 )
10982 {
10983 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
10984 * with the local bound, in this case we need to store the bound change as pending bound change
10985 */
10986 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
10987 {
10988 assert(tree != NULL);
10989 assert(transprob != NULL);
10990 assert(SCIPprobIsTransformed(transprob));
10991
10992 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
10993 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
10994 }
10995 else
10996 {
10997 SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
10998 }
10999
11000 if( nbdchgs != NULL )
11001 (*nbdchgs)++;
11002 }
11003 }
11004 else
11005 {
11006 if( var->glbdom.ub < 0.5 )
11007 *infeasible = TRUE;
11008 else if( var->glbdom.lb < 0.5 )
11009 {
11010 /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
11011 * with the local bound, in this case we need to store the bound change as pending bound change
11012 */
11013 if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
11014 {
11015 assert(tree != NULL);
11016 assert(transprob != NULL);
11017 assert(SCIPprobIsTransformed(transprob));
11018
11019 SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
11020 tree, reopt, lp, branchcand, eventqueue, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
11021 }
11022 else
11023 {
11024 SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
11025 }
11026
11027 if( nbdchgs != NULL )
11028 (*nbdchgs)++;
11029 }
11030 }
11031
11032 /* during presolving, the variable should have been removed immediately from all its cliques */
11033 assert(SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING || var->cliquelist == NULL);
11034
11035 return SCIP_OKAY;
11036 }
11037
11038 /** adds the variable to the given clique and updates the list of cliques the binary variable is member of;
11039 * if the variable now appears twice in the clique with the same value, it is fixed to the opposite value;
11040 * if the variable now appears twice in the clique with opposite values, all other variables are fixed to
11041 * the opposite of the value they take in the clique
11042 */
SCIPvarAddClique(SCIP_VAR * var,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 value,SCIP_CLIQUE * clique,SCIP_Bool * infeasible,int * nbdchgs)11043 SCIP_RETCODE SCIPvarAddClique(
11044 SCIP_VAR* var, /**< problem variable */
11045 BMS_BLKMEM* blkmem, /**< block memory */
11046 SCIP_SET* set, /**< global SCIP settings */
11047 SCIP_STAT* stat, /**< problem statistics */
11048 SCIP_PROB* transprob, /**< transformed problem */
11049 SCIP_PROB* origprob, /**< original problem */
11050 SCIP_TREE* tree, /**< branch and bound tree if in solving stage */
11051 SCIP_REOPT* reopt, /**< reoptimization data structure */
11052 SCIP_LP* lp, /**< current LP data */
11053 SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */
11054 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
11055 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
11056 SCIP_Bool value, /**< value of the variable in the clique */
11057 SCIP_CLIQUE* clique, /**< clique the variable should be added to */
11058 SCIP_Bool* infeasible, /**< pointer to store whether an infeasibility was detected */
11059 int* nbdchgs /**< pointer to count the number of performed bound changes, or NULL */
11060 )
11061 {
11062 assert(var != NULL);
11063 assert(set != NULL);
11064 assert(var->scip == set->scip);
11065 assert(SCIPvarIsBinary(var));
11066 assert(infeasible != NULL);
11067
11068 *infeasible = FALSE;
11069
11070 /* get corresponding active problem variable */
11071 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
11072 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
11073 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
11074 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
11075 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
11076 assert(SCIPvarIsBinary(var));
11077
11078 /* only column and loose variables may be member of a clique */
11079 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
11080 {
11081 SCIP_Bool doubleentry;
11082 SCIP_Bool oppositeentry;
11083
11084 /* add variable to clique */
11085 SCIP_CALL( SCIPcliqueAddVar(clique, blkmem, set, var, value, &doubleentry, &oppositeentry) );
11086
11087 /* add clique to variable's clique list */
11088 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );
11089
11090 /* check consistency of cliquelist */
11091 SCIPcliquelistCheck(var->cliquelist, var);
11092
11093 /* if the variable now appears twice with the same value in the clique, it can be fixed to the opposite value */
11094 if( doubleentry )
11095 {
11096 SCIP_CALL( SCIPvarFixBinary(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
11097 eventqueue, cliquetable, !value, infeasible, nbdchgs) );
11098 }
11099
11100 /* if the variable appears with both values in the clique, all other variables of the clique can be fixed
11101 * to the opposite of the value they take in the clique
11102 */
11103 if( oppositeentry )
11104 {
11105 SCIP_VAR** vars;
11106 SCIP_Bool* values;
11107 int nvars;
11108 int i;
11109
11110 nvars = SCIPcliqueGetNVars(clique);
11111 vars = SCIPcliqueGetVars(clique);
11112 values = SCIPcliqueGetValues(clique);
11113 for( i = 0; i < nvars && !(*infeasible); ++i )
11114 {
11115 if( vars[i] == var )
11116 continue;
11117
11118 SCIP_CALL( SCIPvarFixBinary(vars[i], blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
11119 eventqueue, cliquetable, !values[i], infeasible, nbdchgs) );
11120 }
11121 }
11122 }
11123
11124 return SCIP_OKAY;
11125 }
11126
11127 /** adds a filled clique to the cliquelists of all corresponding variables */
SCIPvarsAddClique(SCIP_VAR ** vars,SCIP_Bool * values,int nvars,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_CLIQUE * clique)11128 SCIP_RETCODE SCIPvarsAddClique(
11129 SCIP_VAR** vars, /**< problem variables */
11130 SCIP_Bool* values, /**< values of the variables in the clique */
11131 int nvars, /**< number of problem variables */
11132 BMS_BLKMEM* blkmem, /**< block memory */
11133 SCIP_SET* set, /**< global SCIP settings */
11134 SCIP_CLIQUE* clique /**< clique that contains all given variables and values */
11135 )
11136 {
11137 SCIP_VAR* var;
11138 int v;
11139
11140 assert(vars != NULL);
11141 assert(values != NULL);
11142 assert(nvars > 0);
11143 assert(set != NULL);
11144 assert(blkmem != NULL);
11145 assert(clique != NULL);
11146
11147 for( v = nvars - 1; v >= 0; --v )
11148 {
11149 var = vars[v];
11150 assert(SCIPvarIsBinary(var));
11151 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
11152
11153 /* add clique to variable's clique list */
11154 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, values[v], clique) );
11155
11156 /* check consistency of cliquelist */
11157 SCIPcliquelistCheck(var->cliquelist, var);
11158 }
11159
11160 return SCIP_OKAY;
11161 }
11162
11163 /** adds a clique to the list of cliques of the given binary variable, but does not change the clique
11164 * itself
11165 */
SCIPvarAddCliqueToList(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_Bool value,SCIP_CLIQUE * clique)11166 SCIP_RETCODE SCIPvarAddCliqueToList(
11167 SCIP_VAR* var, /**< problem variable */
11168 BMS_BLKMEM* blkmem, /**< block memory */
11169 SCIP_SET* set, /**< global SCIP settings */
11170 SCIP_Bool value, /**< value of the variable in the clique */
11171 SCIP_CLIQUE* clique /**< clique that should be removed from the variable's clique list */
11172 )
11173 {
11174 assert(var != NULL);
11175 assert(SCIPvarIsBinary(var));
11176 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
11177
11178 /* add clique to variable's clique list */
11179 SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );
11180
11181 return SCIP_OKAY;
11182 }
11183
11184
11185 /** deletes a clique from the list of cliques the binary variable is member of, but does not change the clique
11186 * itself
11187 */
SCIPvarDelCliqueFromList(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_Bool value,SCIP_CLIQUE * clique)11188 SCIP_RETCODE SCIPvarDelCliqueFromList(
11189 SCIP_VAR* var, /**< problem variable */
11190 BMS_BLKMEM* blkmem, /**< block memory */
11191 SCIP_Bool value, /**< value of the variable in the clique */
11192 SCIP_CLIQUE* clique /**< clique that should be removed from the variable's clique list */
11193 )
11194 {
11195 assert(var != NULL);
11196 assert(SCIPvarIsBinary(var));
11197
11198 /* delete clique from variable's clique list */
11199 SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );
11200
11201 return SCIP_OKAY;
11202 }
11203
11204 /** deletes the variable from the given clique and updates the list of cliques the binary variable is member of */
SCIPvarDelClique(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_CLIQUETABLE * cliquetable,SCIP_Bool value,SCIP_CLIQUE * clique)11205 SCIP_RETCODE SCIPvarDelClique(
11206 SCIP_VAR* var, /**< problem variable */
11207 BMS_BLKMEM* blkmem, /**< block memory */
11208 SCIP_CLIQUETABLE* cliquetable, /**< clique table data structure */
11209 SCIP_Bool value, /**< value of the variable in the clique */
11210 SCIP_CLIQUE* clique /**< clique the variable should be removed from */
11211 )
11212 {
11213 assert(var != NULL);
11214 assert(SCIPvarIsBinary(var));
11215
11216 /* get corresponding active problem variable */
11217 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
11218 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
11219 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
11220 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
11221 || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
11222 assert(SCIPvarIsBinary(var));
11223
11224 /* only column and loose variables may be member of a clique */
11225 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
11226 {
11227 /* delete clique from variable's clique list */
11228 SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );
11229
11230 /* delete variable from clique */
11231 SCIPcliqueDelVar(clique, cliquetable, var, value);
11232
11233 /* check consistency of cliquelist */
11234 SCIPcliquelistCheck(var->cliquelist, var);
11235 }
11236
11237 return SCIP_OKAY;
11238 }
11239
11240 /** returns whether there is a clique that contains both given variable/value pairs;
11241 * the variables must be active binary variables;
11242 * if regardimplics is FALSE, only the cliques in the clique table are looked at;
11243 * if regardimplics is TRUE, both the cliques and the implications of the implication graph are regarded
11244 *
11245 * @note a variable with it's negated variable are NOT! in a clique
11246 * @note a variable with itself are in a clique
11247 */
SCIPvarsHaveCommonClique(SCIP_VAR * var1,SCIP_Bool value1,SCIP_VAR * var2,SCIP_Bool value2,SCIP_Bool regardimplics)11248 SCIP_Bool SCIPvarsHaveCommonClique(
11249 SCIP_VAR* var1, /**< first variable */
11250 SCIP_Bool value1, /**< value of first variable */
11251 SCIP_VAR* var2, /**< second variable */
11252 SCIP_Bool value2, /**< value of second variable */
11253 SCIP_Bool regardimplics /**< should the implication graph also be searched for a clique? */
11254 )
11255 {
11256 assert(var1 != NULL);
11257 assert(var2 != NULL);
11258 assert(SCIPvarIsActive(var1));
11259 assert(SCIPvarIsActive(var2));
11260 assert(SCIPvarIsBinary(var1));
11261 assert(SCIPvarIsBinary(var2));
11262
11263 return (SCIPcliquelistsHaveCommonClique(var1->cliquelist, value1, var2->cliquelist, value2)
11264 || (regardimplics && SCIPvarHasImplic(var1, value1, var2, value2 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER)));
11265 }
11266
11267 /** actually changes the branch factor of the variable and of all parent variables */
11268 static
varProcessChgBranchFactor(SCIP_VAR * var,SCIP_SET * set,SCIP_Real branchfactor)11269 SCIP_RETCODE varProcessChgBranchFactor(
11270 SCIP_VAR* var, /**< problem variable */
11271 SCIP_SET* set, /**< global SCIP settings */
11272 SCIP_Real branchfactor /**< factor to weigh variable's branching score with */
11273 )
11274 {
11275 SCIP_VAR* parentvar;
11276 SCIP_Real eps;
11277 int i;
11278
11279 assert(var != NULL);
11280 assert(set != NULL);
11281 assert(var->scip == set->scip);
11282
11283 /* only use positive values */
11284 eps = SCIPsetEpsilon(set);
11285 branchfactor = MAX(branchfactor, eps);
11286
11287 SCIPsetDebugMsg(set, "process changing branch factor of <%s> from %f to %f\n", var->name, var->branchfactor, branchfactor);
11288
11289 if( SCIPsetIsEQ(set, branchfactor, var->branchfactor) )
11290 return SCIP_OKAY;
11291
11292 /* change the branch factor */
11293 var->branchfactor = branchfactor;
11294
11295 /* process parent variables */
11296 for( i = 0; i < var->nparentvars; ++i )
11297 {
11298 parentvar = var->parentvars[i];
11299 assert(parentvar != NULL);
11300
11301 switch( SCIPvarGetStatus(parentvar) )
11302 {
11303 case SCIP_VARSTATUS_ORIGINAL:
11304 /* do not change priorities across the border between transformed and original problem */
11305 break;
11306
11307 case SCIP_VARSTATUS_COLUMN:
11308 case SCIP_VARSTATUS_LOOSE:
11309 case SCIP_VARSTATUS_FIXED:
11310 case SCIP_VARSTATUS_MULTAGGR:
11311 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11312 SCIPABORT();
11313 return SCIP_INVALIDDATA; /*lint !e527*/
11314
11315 case SCIP_VARSTATUS_AGGREGATED:
11316 case SCIP_VARSTATUS_NEGATED:
11317 SCIP_CALL( varProcessChgBranchFactor(parentvar, set, branchfactor) );
11318 break;
11319
11320 default:
11321 SCIPerrorMessage("unknown variable status\n");
11322 SCIPABORT();
11323 return SCIP_ERROR; /*lint !e527*/
11324 }
11325 }
11326
11327 return SCIP_OKAY;
11328 }
11329
11330 /** sets the branch factor of the variable; this value can be used in the branching methods to scale the score
11331 * values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
11332 */
SCIPvarChgBranchFactor(SCIP_VAR * var,SCIP_SET * set,SCIP_Real branchfactor)11333 SCIP_RETCODE SCIPvarChgBranchFactor(
11334 SCIP_VAR* var, /**< problem variable */
11335 SCIP_SET* set, /**< global SCIP settings */
11336 SCIP_Real branchfactor /**< factor to weigh variable's branching score with */
11337 )
11338 {
11339 int v;
11340
11341 assert(var != NULL);
11342 assert(set != NULL);
11343 assert(var->scip == set->scip);
11344 assert(branchfactor >= 0.0);
11345
11346 SCIPdebugMessage("changing branch factor of <%s> from %g to %g\n", var->name, var->branchfactor, branchfactor);
11347
11348 if( SCIPsetIsEQ(set, var->branchfactor, branchfactor) )
11349 return SCIP_OKAY;
11350
11351 /* change priorities of attached variables */
11352 switch( SCIPvarGetStatus(var) )
11353 {
11354 case SCIP_VARSTATUS_ORIGINAL:
11355 if( var->data.original.transvar != NULL )
11356 {
11357 SCIP_CALL( SCIPvarChgBranchFactor(var->data.original.transvar, set, branchfactor) );
11358 }
11359 else
11360 {
11361 assert(set->stage == SCIP_STAGE_PROBLEM);
11362 var->branchfactor = branchfactor;
11363 }
11364 break;
11365
11366 case SCIP_VARSTATUS_COLUMN:
11367 case SCIP_VARSTATUS_LOOSE:
11368 case SCIP_VARSTATUS_FIXED:
11369 SCIP_CALL( varProcessChgBranchFactor(var, set, branchfactor) );
11370 break;
11371
11372 case SCIP_VARSTATUS_AGGREGATED:
11373 assert(var->data.aggregate.var != NULL);
11374 SCIP_CALL( SCIPvarChgBranchFactor(var->data.aggregate.var, set, branchfactor) );
11375 break;
11376
11377 case SCIP_VARSTATUS_MULTAGGR:
11378 assert(!var->donotmultaggr);
11379 for( v = 0; v < var->data.multaggr.nvars; ++v )
11380 {
11381 SCIP_CALL( SCIPvarChgBranchFactor(var->data.multaggr.vars[v], set, branchfactor) );
11382 }
11383 break;
11384
11385 case SCIP_VARSTATUS_NEGATED:
11386 assert(var->negatedvar != NULL);
11387 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11388 assert(var->negatedvar->negatedvar == var);
11389 SCIP_CALL( SCIPvarChgBranchFactor(var->negatedvar, set, branchfactor) );
11390 break;
11391
11392 default:
11393 SCIPerrorMessage("unknown variable status\n");
11394 SCIPABORT();
11395 return SCIP_ERROR; /*lint !e527*/
11396 }
11397
11398 return SCIP_OKAY;
11399 }
11400
11401 /** actually changes the branch priority of the variable and of all parent variables */
11402 static
varProcessChgBranchPriority(SCIP_VAR * var,int branchpriority)11403 SCIP_RETCODE varProcessChgBranchPriority(
11404 SCIP_VAR* var, /**< problem variable */
11405 int branchpriority /**< branching priority of the variable */
11406 )
11407 {
11408 SCIP_VAR* parentvar;
11409 int i;
11410
11411 assert(var != NULL);
11412
11413 SCIPdebugMessage("process changing branch priority of <%s> from %d to %d\n",
11414 var->name, var->branchpriority, branchpriority);
11415
11416 if( branchpriority == var->branchpriority )
11417 return SCIP_OKAY;
11418
11419 /* change the branch priority */
11420 var->branchpriority = branchpriority;
11421
11422 /* process parent variables */
11423 for( i = 0; i < var->nparentvars; ++i )
11424 {
11425 parentvar = var->parentvars[i];
11426 assert(parentvar != NULL);
11427
11428 switch( SCIPvarGetStatus(parentvar) )
11429 {
11430 case SCIP_VARSTATUS_ORIGINAL:
11431 /* do not change priorities across the border between transformed and original problem */
11432 break;
11433
11434 case SCIP_VARSTATUS_COLUMN:
11435 case SCIP_VARSTATUS_LOOSE:
11436 case SCIP_VARSTATUS_FIXED:
11437 case SCIP_VARSTATUS_MULTAGGR:
11438 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11439 SCIPABORT();
11440 return SCIP_INVALIDDATA; /*lint !e527*/
11441
11442 case SCIP_VARSTATUS_AGGREGATED:
11443 case SCIP_VARSTATUS_NEGATED:
11444 SCIP_CALL( varProcessChgBranchPriority(parentvar, branchpriority) );
11445 break;
11446
11447 default:
11448 SCIPerrorMessage("unknown variable status\n");
11449 return SCIP_ERROR;
11450 }
11451 }
11452
11453 return SCIP_OKAY;
11454 }
11455
11456 /** sets the branch priority of the variable; variables with higher branch priority are always preferred to variables
11457 * with lower priority in selection of branching variable
11458 */
SCIPvarChgBranchPriority(SCIP_VAR * var,int branchpriority)11459 SCIP_RETCODE SCIPvarChgBranchPriority(
11460 SCIP_VAR* var, /**< problem variable */
11461 int branchpriority /**< branching priority of the variable */
11462 )
11463 {
11464 int v;
11465
11466 assert(var != NULL);
11467
11468 SCIPdebugMessage("changing branch priority of <%s> from %d to %d\n", var->name, var->branchpriority, branchpriority);
11469
11470 if( var->branchpriority == branchpriority )
11471 return SCIP_OKAY;
11472
11473 /* change priorities of attached variables */
11474 switch( SCIPvarGetStatus(var) )
11475 {
11476 case SCIP_VARSTATUS_ORIGINAL:
11477 if( var->data.original.transvar != NULL )
11478 {
11479 SCIP_CALL( SCIPvarChgBranchPriority(var->data.original.transvar, branchpriority) );
11480 }
11481 else
11482 var->branchpriority = branchpriority;
11483 break;
11484
11485 case SCIP_VARSTATUS_COLUMN:
11486 case SCIP_VARSTATUS_LOOSE:
11487 case SCIP_VARSTATUS_FIXED:
11488 SCIP_CALL( varProcessChgBranchPriority(var, branchpriority) );
11489 break;
11490
11491 case SCIP_VARSTATUS_AGGREGATED:
11492 assert(var->data.aggregate.var != NULL);
11493 SCIP_CALL( SCIPvarChgBranchPriority(var->data.aggregate.var, branchpriority) );
11494 break;
11495
11496 case SCIP_VARSTATUS_MULTAGGR:
11497 assert(!var->donotmultaggr);
11498 for( v = 0; v < var->data.multaggr.nvars; ++v )
11499 {
11500 SCIP_CALL( SCIPvarChgBranchPriority(var->data.multaggr.vars[v], branchpriority) );
11501 }
11502 break;
11503
11504 case SCIP_VARSTATUS_NEGATED:
11505 assert(var->negatedvar != NULL);
11506 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11507 assert(var->negatedvar->negatedvar == var);
11508 SCIP_CALL( SCIPvarChgBranchPriority(var->negatedvar, branchpriority) );
11509 break;
11510
11511 default:
11512 SCIPerrorMessage("unknown variable status\n");
11513 SCIPABORT();
11514 return SCIP_ERROR; /*lint !e527*/
11515 }
11516
11517 return SCIP_OKAY;
11518 }
11519
11520 /** actually changes the branch direction of the variable and of all parent variables */
11521 static
varProcessChgBranchDirection(SCIP_VAR * var,SCIP_BRANCHDIR branchdirection)11522 SCIP_RETCODE varProcessChgBranchDirection(
11523 SCIP_VAR* var, /**< problem variable */
11524 SCIP_BRANCHDIR branchdirection /**< preferred branch direction of the variable (downwards, upwards, auto) */
11525 )
11526 {
11527 SCIP_VAR* parentvar;
11528 int i;
11529
11530 assert(var != NULL);
11531
11532 SCIPdebugMessage("process changing branch direction of <%s> from %u to %d\n",
11533 var->name, var->branchdirection, branchdirection);
11534
11535 if( branchdirection == (SCIP_BRANCHDIR)var->branchdirection )
11536 return SCIP_OKAY;
11537
11538 /* change the branch direction */
11539 var->branchdirection = branchdirection; /*lint !e641*/
11540
11541 /* process parent variables */
11542 for( i = 0; i < var->nparentvars; ++i )
11543 {
11544 parentvar = var->parentvars[i];
11545 assert(parentvar != NULL);
11546
11547 switch( SCIPvarGetStatus(parentvar) )
11548 {
11549 case SCIP_VARSTATUS_ORIGINAL:
11550 /* do not change directions across the border between transformed and original problem */
11551 break;
11552
11553 case SCIP_VARSTATUS_COLUMN:
11554 case SCIP_VARSTATUS_LOOSE:
11555 case SCIP_VARSTATUS_FIXED:
11556 case SCIP_VARSTATUS_MULTAGGR:
11557 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
11558 SCIPABORT();
11559 return SCIP_INVALIDDATA; /*lint !e527*/
11560
11561 case SCIP_VARSTATUS_AGGREGATED:
11562 if( parentvar->data.aggregate.scalar > 0.0 )
11563 {
11564 SCIP_CALL( varProcessChgBranchDirection(parentvar, branchdirection) );
11565 }
11566 else
11567 {
11568 SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
11569 }
11570 break;
11571
11572 case SCIP_VARSTATUS_NEGATED:
11573 SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
11574 break;
11575
11576 default:
11577 SCIPerrorMessage("unknown variable status\n");
11578 SCIPABORT();
11579 return SCIP_ERROR; /*lint !e527*/
11580 }
11581 }
11582
11583 return SCIP_OKAY;
11584 }
11585
11586 /** sets the branch direction of the variable; variables with higher branch direction are always preferred to variables
11587 * with lower direction in selection of branching variable
11588 */
SCIPvarChgBranchDirection(SCIP_VAR * var,SCIP_BRANCHDIR branchdirection)11589 SCIP_RETCODE SCIPvarChgBranchDirection(
11590 SCIP_VAR* var, /**< problem variable */
11591 SCIP_BRANCHDIR branchdirection /**< preferred branch direction of the variable (downwards, upwards, auto) */
11592 )
11593 {
11594 int v;
11595
11596 assert(var != NULL);
11597
11598 SCIPdebugMessage("changing branch direction of <%s> from %u to %d\n", var->name, var->branchdirection, branchdirection);
11599
11600 if( (SCIP_BRANCHDIR)var->branchdirection == branchdirection )
11601 return SCIP_OKAY;
11602
11603 /* change directions of attached variables */
11604 switch( SCIPvarGetStatus(var) )
11605 {
11606 case SCIP_VARSTATUS_ORIGINAL:
11607 if( var->data.original.transvar != NULL )
11608 {
11609 SCIP_CALL( SCIPvarChgBranchDirection(var->data.original.transvar, branchdirection) );
11610 }
11611 else
11612 var->branchdirection = branchdirection; /*lint !e641*/
11613 break;
11614
11615 case SCIP_VARSTATUS_COLUMN:
11616 case SCIP_VARSTATUS_LOOSE:
11617 case SCIP_VARSTATUS_FIXED:
11618 SCIP_CALL( varProcessChgBranchDirection(var, branchdirection) );
11619 break;
11620
11621 case SCIP_VARSTATUS_AGGREGATED:
11622 assert(var->data.aggregate.var != NULL);
11623 if( var->data.aggregate.scalar > 0.0 )
11624 {
11625 SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, branchdirection) );
11626 }
11627 else
11628 {
11629 SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, SCIPbranchdirOpposite(branchdirection)) );
11630 }
11631 break;
11632
11633 case SCIP_VARSTATUS_MULTAGGR:
11634 assert(!var->donotmultaggr);
11635 for( v = 0; v < var->data.multaggr.nvars; ++v )
11636 {
11637 /* only update branching direction of aggregation variables, if they don't have a preferred direction yet */
11638 assert(var->data.multaggr.vars[v] != NULL);
11639 if( (SCIP_BRANCHDIR)var->data.multaggr.vars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
11640 {
11641 if( var->data.multaggr.scalars[v] > 0.0 )
11642 {
11643 SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], branchdirection) );
11644 }
11645 else
11646 {
11647 SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], SCIPbranchdirOpposite(branchdirection)) );
11648 }
11649 }
11650 }
11651 break;
11652
11653 case SCIP_VARSTATUS_NEGATED:
11654 assert(var->negatedvar != NULL);
11655 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
11656 assert(var->negatedvar->negatedvar == var);
11657 SCIP_CALL( SCIPvarChgBranchDirection(var->negatedvar, SCIPbranchdirOpposite(branchdirection)) );
11658 break;
11659
11660 default:
11661 SCIPerrorMessage("unknown variable status\n");
11662 SCIPABORT();
11663 return SCIP_ERROR; /*lint !e527*/
11664 }
11665
11666 return SCIP_OKAY;
11667 }
11668
11669 /** compares the index of two variables, only active, fixed or negated variables are allowed, if a variable
11670 * is negated then the index of the corresponding active variable is taken, returns -1 if first is
11671 * smaller than, and +1 if first is greater than second variable index; returns 0 if both indices
11672 * are equal, which means both variables are equal
11673 */
SCIPvarCompareActiveAndNegated(SCIP_VAR * var1,SCIP_VAR * var2)11674 int SCIPvarCompareActiveAndNegated(
11675 SCIP_VAR* var1, /**< first problem variable */
11676 SCIP_VAR* var2 /**< second problem variable */
11677 )
11678 {
11679 assert(var1 != NULL);
11680 assert(var2 != NULL);
11681 assert(SCIPvarIsActive(var1) || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_FIXED);
11682 assert(SCIPvarIsActive(var2) || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_FIXED);
11683
11684 if( SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED )
11685 var1 = SCIPvarGetNegatedVar(var1);
11686 if( SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED )
11687 var2 = SCIPvarGetNegatedVar(var2);
11688
11689 assert(var1 != NULL);
11690 assert(var2 != NULL);
11691
11692 if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) )
11693 return -1;
11694 else if( SCIPvarGetIndex(var1) > SCIPvarGetIndex(var2) )
11695 return +1;
11696
11697 assert(var1 == var2);
11698 return 0;
11699 }
11700
11701 /** comparison method for sorting active and negated variables by non-decreasing index, active and negated
11702 * variables are handled as the same variables
11703 */
SCIP_DECL_SORTPTRCOMP(SCIPvarCompActiveAndNegated)11704 SCIP_DECL_SORTPTRCOMP(SCIPvarCompActiveAndNegated)
11705 {
11706 return SCIPvarCompareActiveAndNegated((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
11707 }
11708
11709 /** compares the index of two variables, returns -1 if first is smaller than, and +1 if first is greater than second
11710 * variable index; returns 0 if both indices are equal, which means both variables are equal
11711 */
SCIPvarCompare(SCIP_VAR * var1,SCIP_VAR * var2)11712 int SCIPvarCompare(
11713 SCIP_VAR* var1, /**< first problem variable */
11714 SCIP_VAR* var2 /**< second problem variable */
11715 )
11716 {
11717 assert(var1 != NULL);
11718 assert(var2 != NULL);
11719
11720 if( var1->index < var2->index )
11721 return -1;
11722 else if( var1->index > var2->index )
11723 return +1;
11724 else
11725 {
11726 assert(var1 == var2);
11727 return 0;
11728 }
11729 }
11730
11731 /** comparison method for sorting variables by non-decreasing index */
SCIP_DECL_SORTPTRCOMP(SCIPvarComp)11732 SCIP_DECL_SORTPTRCOMP(SCIPvarComp)
11733 {
11734 return SCIPvarCompare((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
11735 }
11736
11737 /** comparison method for sorting variables by non-decreasing objective coefficient */
SCIP_DECL_SORTPTRCOMP(SCIPvarCompObj)11738 SCIP_DECL_SORTPTRCOMP(SCIPvarCompObj)
11739 {
11740 SCIP_Real obj1;
11741 SCIP_Real obj2;
11742
11743 obj1 = SCIPvarGetObj((SCIP_VAR*)elem1);
11744 obj2 = SCIPvarGetObj((SCIP_VAR*)elem2);
11745
11746 if( obj1 < obj2 )
11747 return -1;
11748 else if( obj1 > obj2 )
11749 return +1;
11750 else
11751 return 0;
11752 }
11753
11754 /** hash key retrieval function for variables */
SCIP_DECL_HASHGETKEY(SCIPvarGetHashkey)11755 SCIP_DECL_HASHGETKEY(SCIPvarGetHashkey)
11756 { /*lint --e{715}*/
11757 return elem;
11758 }
11759
11760 /** returns TRUE iff the indices of both variables are equal */
SCIP_DECL_HASHKEYEQ(SCIPvarIsHashkeyEq)11761 SCIP_DECL_HASHKEYEQ(SCIPvarIsHashkeyEq)
11762 { /*lint --e{715}*/
11763 if( key1 == key2 )
11764 return TRUE;
11765 return FALSE;
11766 }
11767
11768 /** returns the hash value of the key */
SCIP_DECL_HASHKEYVAL(SCIPvarGetHashkeyVal)11769 SCIP_DECL_HASHKEYVAL(SCIPvarGetHashkeyVal)
11770 { /*lint --e{715}*/
11771 assert( SCIPvarGetIndex((SCIP_VAR*) key) >= 0 );
11772 return (unsigned int) SCIPvarGetIndex((SCIP_VAR*) key);
11773 }
11774
11775 /** return for given variables all their active counterparts; all active variables will be pairwise different */
SCIPvarsGetActiveVars(SCIP_SET * set,SCIP_VAR ** vars,int * nvars,int varssize,int * requiredsize)11776 SCIP_RETCODE SCIPvarsGetActiveVars(
11777 SCIP_SET* set, /**< global SCIP settings */
11778 SCIP_VAR** vars, /**< variable array with given variables and as output all active
11779 * variables, if enough slots exist
11780 */
11781 int* nvars, /**< number of given variables, and as output number of active variables,
11782 * if enough slots exist
11783 */
11784 int varssize, /**< available slots in vars array */
11785 int* requiredsize /**< pointer to store the required array size for the active variables */
11786 )
11787 {
11788 SCIP_VAR** activevars;
11789 int nactivevars;
11790 int activevarssize;
11791
11792 SCIP_VAR* var;
11793 int v;
11794
11795 SCIP_VAR** tmpvars;
11796 SCIP_VAR** multvars;
11797 int tmpvarssize;
11798 int ntmpvars;
11799 int noldtmpvars;
11800 int nmultvars;
11801
11802 assert(set != NULL);
11803 assert(nvars != NULL);
11804 assert(vars != NULL || *nvars == 0);
11805 assert(varssize >= *nvars);
11806 assert(requiredsize != NULL);
11807
11808 *requiredsize = 0;
11809
11810 if( *nvars == 0 )
11811 return SCIP_OKAY;
11812
11813 nactivevars = 0;
11814 activevarssize = *nvars;
11815 ntmpvars = *nvars;
11816 tmpvarssize = *nvars;
11817
11818 /* temporary memory */
11819 SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
11820 /* coverity[copy_paste_error] */
11821 SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );
11822
11823 noldtmpvars = ntmpvars;
11824
11825 /* sort all variables to combine equal variables easily */
11826 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
11827 for( v = ntmpvars - 1; v > 0; --v )
11828 {
11829 /* combine same variables */
11830 if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
11831 {
11832 --ntmpvars;
11833 tmpvars[v] = tmpvars[ntmpvars];
11834 }
11835 }
11836 /* sort all variables again to combine equal variables later on */
11837 if( noldtmpvars > ntmpvars )
11838 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
11839
11840 /* collect for each variable the representation in active variables */
11841 while( ntmpvars >= 1 )
11842 {
11843 --ntmpvars;
11844 var = tmpvars[ntmpvars];
11845 assert( var != NULL );
11846
11847 switch( SCIPvarGetStatus(var) )
11848 {
11849 case SCIP_VARSTATUS_ORIGINAL:
11850 if( var->data.original.transvar == NULL )
11851 {
11852 SCIPerrorMessage("original variable has no transformed variable attached\n");
11853 SCIPABORT();
11854 return SCIP_INVALIDDATA; /*lint !e527*/
11855 }
11856 tmpvars[ntmpvars] = var->data.original.transvar;
11857 ++ntmpvars;
11858 break;
11859
11860 case SCIP_VARSTATUS_AGGREGATED:
11861 tmpvars[ntmpvars] = var->data.aggregate.var;
11862 ++ntmpvars;
11863 break;
11864
11865 case SCIP_VARSTATUS_NEGATED:
11866 tmpvars[ntmpvars] = var->negatedvar;
11867 ++ntmpvars;
11868 break;
11869
11870 case SCIP_VARSTATUS_LOOSE:
11871 case SCIP_VARSTATUS_COLUMN:
11872 /* check for space in temporary memory */
11873 if( nactivevars >= activevarssize )
11874 {
11875 activevarssize *= 2;
11876 SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
11877 assert(nactivevars < activevarssize);
11878 }
11879 activevars[nactivevars] = var;
11880 nactivevars++;
11881 break;
11882
11883 case SCIP_VARSTATUS_MULTAGGR:
11884 /* x = a_1*y_1 + ... + a_n*y_n + c */
11885 nmultvars = var->data.multaggr.nvars;
11886 multvars = var->data.multaggr.vars;
11887
11888 /* check for space in temporary memory */
11889 if( nmultvars + ntmpvars > tmpvarssize )
11890 {
11891 while( nmultvars + ntmpvars > tmpvarssize )
11892 tmpvarssize *= 2;
11893 SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
11894 assert(nmultvars + ntmpvars <= tmpvarssize);
11895 }
11896
11897 /* copy all multi-aggregation variables into our working array */
11898 BMScopyMemoryArray(&tmpvars[ntmpvars], multvars, nmultvars); /*lint !e866*/
11899
11900 /* get active, fixed or multi-aggregated corresponding variables for all new ones */
11901 SCIPvarsGetProbvar(&tmpvars[ntmpvars], nmultvars);
11902
11903 ntmpvars += nmultvars;
11904 noldtmpvars = ntmpvars;
11905
11906 /* sort all variables to combine equal variables easily */
11907 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
11908 for( v = ntmpvars - 1; v > 0; --v )
11909 {
11910 /* combine same variables */
11911 if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
11912 {
11913 --ntmpvars;
11914 tmpvars[v] = tmpvars[ntmpvars];
11915 }
11916 }
11917 /* sort all variables again to combine equal variables later on */
11918 if( noldtmpvars > ntmpvars )
11919 SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
11920
11921 break;
11922
11923 case SCIP_VARSTATUS_FIXED:
11924 /* no need for memorizing fixed variables */
11925 break;
11926
11927 default:
11928 SCIPerrorMessage("unknown variable status\n");
11929 SCIPABORT();
11930 return SCIP_INVALIDDATA; /*lint !e527*/
11931 }
11932 }
11933
11934 /* sort variable array by variable index */
11935 SCIPsortPtr((void**)activevars, SCIPvarComp, nactivevars);
11936
11937 /* eliminate duplicates and count required size */
11938 v = nactivevars - 1;
11939 while( v > 0 )
11940 {
11941 /* combine both variable since they are the same */
11942 if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
11943 {
11944 --nactivevars;
11945 activevars[v] = activevars[nactivevars];
11946 }
11947 --v;
11948 }
11949 *requiredsize = nactivevars;
11950
11951 if( varssize >= *requiredsize )
11952 {
11953 assert(vars != NULL);
11954
11955 *nvars = *requiredsize;
11956 BMScopyMemoryArray(vars, activevars, nactivevars);
11957 }
11958
11959 SCIPsetFreeBufferArray(set, &tmpvars);
11960 SCIPsetFreeBufferArray(set, &activevars);
11961
11962 return SCIP_OKAY;
11963 }
11964
11965 /** gets corresponding active, fixed, or multi-aggregated problem variables of given variables,
11966 * @note the content of the given array will/might change
11967 */
SCIPvarsGetProbvar(SCIP_VAR ** vars,int nvars)11968 void SCIPvarsGetProbvar(
11969 SCIP_VAR** vars, /**< array of problem variables */
11970 int nvars /**< number of variables */
11971 )
11972 {
11973 int v;
11974
11975 assert(vars != NULL || nvars == 0);
11976
11977 for( v = nvars - 1; v >= 0; --v )
11978 {
11979 assert(vars != NULL);
11980 assert(vars[v] != NULL);
11981
11982 vars[v] = SCIPvarGetProbvar(vars[v]);
11983 assert(vars[v] != NULL);
11984 }
11985 }
11986
11987 /** gets corresponding active, fixed, or multi-aggregated problem variable of a variable */
SCIPvarGetProbvar(SCIP_VAR * var)11988 SCIP_VAR* SCIPvarGetProbvar(
11989 SCIP_VAR* var /**< problem variable */
11990 )
11991 {
11992 SCIP_VAR* retvar;
11993
11994 assert(var != NULL);
11995
11996 retvar = var;
11997
11998 SCIPdebugMessage("get problem variable of <%s>\n", var->name);
11999
12000 while( TRUE ) /*lint !e716 */
12001 {
12002 assert(retvar != NULL);
12003
12004 switch( SCIPvarGetStatus(retvar) )
12005 {
12006 case SCIP_VARSTATUS_ORIGINAL:
12007 if( retvar->data.original.transvar == NULL )
12008 {
12009 SCIPerrorMessage("original variable has no transformed variable attached\n");
12010 SCIPABORT();
12011 return NULL; /*lint !e527 */
12012 }
12013 retvar = retvar->data.original.transvar;
12014 break;
12015
12016 case SCIP_VARSTATUS_LOOSE:
12017 case SCIP_VARSTATUS_COLUMN:
12018 case SCIP_VARSTATUS_FIXED:
12019 return retvar;
12020
12021 case SCIP_VARSTATUS_MULTAGGR:
12022 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12023 if ( retvar->data.multaggr.nvars == 1 )
12024 retvar = retvar->data.multaggr.vars[0];
12025 else
12026 return retvar;
12027 break;
12028
12029 case SCIP_VARSTATUS_AGGREGATED:
12030 retvar = retvar->data.aggregate.var;
12031 break;
12032
12033 case SCIP_VARSTATUS_NEGATED:
12034 retvar = retvar->negatedvar;
12035 break;
12036
12037 default:
12038 SCIPerrorMessage("unknown variable status\n");
12039 SCIPABORT();
12040 return NULL; /*lint !e527*/
12041 }
12042 }
12043 }
12044
12045 /** gets corresponding active, fixed, or multi-aggregated problem variables of binary variables and updates the given
12046 * negation status of each variable
12047 */
SCIPvarsGetProbvarBinary(SCIP_VAR *** vars,SCIP_Bool ** negatedarr,int nvars)12048 SCIP_RETCODE SCIPvarsGetProbvarBinary(
12049 SCIP_VAR*** vars, /**< pointer to binary problem variables */
12050 SCIP_Bool** negatedarr, /**< pointer to corresponding array to update the negation status */
12051 int nvars /**< number of variables and values in vars and negated array */
12052 )
12053 {
12054 SCIP_VAR** var;
12055 SCIP_Bool* negated;
12056 int v;
12057
12058 assert(vars != NULL);
12059 assert(*vars != NULL || nvars == 0);
12060 assert(negatedarr != NULL);
12061 assert(*negatedarr != NULL || nvars == 0);
12062
12063 for( v = nvars - 1; v >= 0; --v )
12064 {
12065 var = &((*vars)[v]);
12066 negated = &((*negatedarr)[v]);
12067
12068 /* get problem variable */
12069 SCIP_CALL( SCIPvarGetProbvarBinary(var, negated) );
12070 }
12071
12072 return SCIP_OKAY;
12073 }
12074
12075
12076 /** gets corresponding active, fixed, or multi-aggregated problem variable of a binary variable and updates the given
12077 * negation status (this means you have to assign a value to SCIP_Bool negated before calling this method, usually
12078 * FALSE is used)
12079 */
SCIPvarGetProbvarBinary(SCIP_VAR ** var,SCIP_Bool * negated)12080 SCIP_RETCODE SCIPvarGetProbvarBinary(
12081 SCIP_VAR** var, /**< pointer to binary problem variable */
12082 SCIP_Bool* negated /**< pointer to update the negation status */
12083 )
12084 {
12085 SCIP_Bool active = FALSE;
12086 #ifndef NDEBUG
12087 SCIP_Real constant = 0.0;
12088 SCIP_Bool orignegated;
12089 #endif
12090
12091 assert(var != NULL);
12092 assert(*var != NULL);
12093 assert(negated != NULL);
12094 assert(SCIPvarIsBinary(*var));
12095
12096 #ifndef NDEBUG
12097 orignegated = *negated;
12098 #endif
12099
12100 while( !active && *var != NULL )
12101 {
12102 switch( SCIPvarGetStatus(*var) )
12103 {
12104 case SCIP_VARSTATUS_ORIGINAL:
12105 if( (*var)->data.original.transvar == NULL )
12106 return SCIP_OKAY;
12107 *var = (*var)->data.original.transvar;
12108 break;
12109
12110 case SCIP_VARSTATUS_LOOSE:
12111 case SCIP_VARSTATUS_COLUMN:
12112 case SCIP_VARSTATUS_FIXED:
12113 active = TRUE;
12114 break;
12115
12116 case SCIP_VARSTATUS_MULTAGGR:
12117 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12118 if ( (*var)->data.multaggr.nvars == 1 )
12119 {
12120 assert( (*var)->data.multaggr.vars != NULL );
12121 assert( (*var)->data.multaggr.scalars != NULL );
12122 assert( SCIPvarIsBinary((*var)->data.multaggr.vars[0]) );
12123 assert(!EPSZ((*var)->data.multaggr.scalars[0], 1e-06));
12124
12125 /* if not all variables were fully propagated, it might happen that a variable is multi-aggregated to
12126 * another variable which needs to be fixed
12127 *
12128 * e.g. x = y - 1 => (x = 0 && y = 1)
12129 * e.g. x = y + 1 => (x = 1 && y = 0)
12130 *
12131 * is this special case we need to return the muti-aggregation
12132 */
12133 if( EPSEQ((*var)->data.multaggr.constant, -1.0, 1e-06) || (EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06) && EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06)) )
12134 {
12135 assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06));
12136 }
12137 else
12138 {
12139 /* @note due to fixations, a multi-aggregation can have a constant of zero and a negative scalar or even
12140 * a scalar in absolute value unequal to one, in this case this aggregation variable needs to be
12141 * fixed to zero, but this should be done by another enforcement; so not depending on the scalar,
12142 * we will return the aggregated variable;
12143 */
12144 if( !EPSEQ(REALABS((*var)->data.multaggr.scalars[0]), 1.0, 1e-06) )
12145 {
12146 active = TRUE;
12147 break;
12148 }
12149
12150 /* @note it may also happen that the constant is larger than 1 or smaller than 0, in that case the
12151 * aggregation variable needs to be fixed to one, but this should be done by another enforcement;
12152 * so if this is the case, we will return the aggregated variable
12153 */
12154 assert(EPSZ((*var)->data.multaggr.constant, 1e-06) || EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06)
12155 || EPSZ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1e-06)
12156 || EPSEQ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1.0, 1e-06));
12157
12158 if( !EPSZ((*var)->data.multaggr.constant, 1e-06) && !EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06) )
12159 {
12160 active = TRUE;
12161 break;
12162 }
12163
12164 assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06) || EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));
12165
12166 if( EPSZ((*var)->data.multaggr.constant, 1e-06) )
12167 {
12168 /* if the scalar is negative, either the aggregation variable is already fixed to zero or has at
12169 * least one uplock (that hopefully will enforce this fixation to zero); can it happen that this
12170 * variable itself is multi-aggregated again?
12171 */
12172 assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06) ?
12173 ((SCIPvarGetUbGlobal((*var)->data.multaggr.vars[0]) < 0.5) ||
12174 SCIPvarGetNLocksUpType((*var)->data.multaggr.vars[0], SCIP_LOCKTYPE_MODEL) > 0) : TRUE);
12175 }
12176 else
12177 {
12178 assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));
12179 #ifndef NDEBUG
12180 constant += (*negated) != orignegated ? -1.0 : 1.0;
12181 #endif
12182
12183 *negated = !(*negated);
12184 }
12185 *var = (*var)->data.multaggr.vars[0];
12186 break;
12187 }
12188 }
12189 active = TRUE; /*lint !e838*/
12190 break;
12191
12192 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
12193 assert((*var)->data.aggregate.var != NULL);
12194 assert(EPSEQ((*var)->data.aggregate.scalar, 1.0, 1e-06) || EPSEQ((*var)->data.aggregate.scalar, -1.0, 1e-06));
12195 assert(EPSLE((*var)->data.aggregate.var->glbdom.ub - (*var)->data.aggregate.var->glbdom.lb, 1.0, 1e-06));
12196 #ifndef NDEBUG
12197 constant += (*negated) != orignegated ? -(*var)->data.aggregate.constant : (*var)->data.aggregate.constant;
12198 #endif
12199
12200 *negated = ((*var)->data.aggregate.scalar > 0.0) ? *negated : !(*negated);
12201 *var = (*var)->data.aggregate.var;
12202 break;
12203
12204 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
12205 assert((*var)->negatedvar != NULL);
12206 #ifndef NDEBUG
12207 constant += (*negated) != orignegated ? -1.0 : 1.0;
12208 #endif
12209
12210 *negated = !(*negated);
12211 *var = (*var)->negatedvar;
12212 break;
12213
12214 default:
12215 SCIPerrorMessage("unknown variable status\n");
12216 return SCIP_INVALIDDATA;
12217 }
12218 }
12219 assert(active == (*var != NULL));
12220
12221 if( active )
12222 {
12223 assert(SCIPvarIsBinary(*var));
12224 assert(EPSZ(constant, 1e-06) || EPSEQ(constant, 1.0, 1e-06));
12225 assert(EPSZ(constant, 1e-06) == ((*negated) == orignegated));
12226
12227 return SCIP_OKAY;
12228 }
12229 else
12230 {
12231 SCIPerrorMessage("active variable path leads to NULL pointer\n");
12232 return SCIP_INVALIDDATA;
12233 }
12234 }
12235
12236 /** transforms given variable, boundtype and bound to the corresponding active, fixed, or multi-aggregated variable
12237 * values
12238 */
SCIPvarGetProbvarBound(SCIP_VAR ** var,SCIP_Real * bound,SCIP_BOUNDTYPE * boundtype)12239 SCIP_RETCODE SCIPvarGetProbvarBound(
12240 SCIP_VAR** var, /**< pointer to problem variable */
12241 SCIP_Real* bound, /**< pointer to bound value to transform */
12242 SCIP_BOUNDTYPE* boundtype /**< pointer to type of bound: lower or upper bound */
12243 )
12244 {
12245 assert(var != NULL);
12246 assert(*var != NULL);
12247 assert(bound != NULL);
12248 assert(boundtype != NULL);
12249
12250 SCIPdebugMessage("get probvar bound %g of type %d of variable <%s>\n", *bound, *boundtype, (*var)->name);
12251
12252 switch( SCIPvarGetStatus(*var) )
12253 {
12254 case SCIP_VARSTATUS_ORIGINAL:
12255 if( (*var)->data.original.transvar == NULL )
12256 {
12257 SCIPerrorMessage("original variable has no transformed variable attached\n");
12258 return SCIP_INVALIDDATA;
12259 }
12260 *var = (*var)->data.original.transvar;
12261 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12262 break;
12263
12264 case SCIP_VARSTATUS_LOOSE:
12265 case SCIP_VARSTATUS_COLUMN:
12266 case SCIP_VARSTATUS_FIXED:
12267 break;
12268
12269 case SCIP_VARSTATUS_MULTAGGR:
12270 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12271 if ( (*var)->data.multaggr.nvars == 1 )
12272 {
12273 assert( (*var)->data.multaggr.vars != NULL );
12274 assert( (*var)->data.multaggr.scalars != NULL );
12275 assert( (*var)->data.multaggr.scalars[0] != 0.0 );
12276
12277 (*bound) /= (*var)->data.multaggr.scalars[0];
12278 (*bound) -= (*var)->data.multaggr.constant/(*var)->data.multaggr.scalars[0];
12279 if ( (*var)->data.multaggr.scalars[0] < 0.0 )
12280 {
12281 if ( *boundtype == SCIP_BOUNDTYPE_LOWER )
12282 *boundtype = SCIP_BOUNDTYPE_UPPER;
12283 else
12284 *boundtype = SCIP_BOUNDTYPE_LOWER;
12285 }
12286 *var = (*var)->data.multaggr.vars[0];
12287 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12288 }
12289 break;
12290
12291 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = x/a - c/a */
12292 assert((*var)->data.aggregate.var != NULL);
12293 assert((*var)->data.aggregate.scalar != 0.0);
12294
12295 (*bound) /= (*var)->data.aggregate.scalar;
12296 (*bound) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12297 if( (*var)->data.aggregate.scalar < 0.0 )
12298 {
12299 if( *boundtype == SCIP_BOUNDTYPE_LOWER )
12300 *boundtype = SCIP_BOUNDTYPE_UPPER;
12301 else
12302 *boundtype = SCIP_BOUNDTYPE_LOWER;
12303 }
12304 *var = (*var)->data.aggregate.var;
12305 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12306 break;
12307
12308 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12309 assert((*var)->negatedvar != NULL);
12310 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12311 assert((*var)->negatedvar->negatedvar == *var);
12312 (*bound) = (*var)->data.negate.constant - *bound;
12313 if( *boundtype == SCIP_BOUNDTYPE_LOWER )
12314 *boundtype = SCIP_BOUNDTYPE_UPPER;
12315 else
12316 *boundtype = SCIP_BOUNDTYPE_LOWER;
12317 *var = (*var)->negatedvar;
12318 SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
12319 break;
12320
12321 default:
12322 SCIPerrorMessage("unknown variable status\n");
12323 return SCIP_INVALIDDATA;
12324 }
12325
12326 return SCIP_OKAY;
12327 }
12328
12329 /** transforms given variable and domain hole to the corresponding active, fixed, or multi-aggregated variable
12330 * values
12331 */
SCIPvarGetProbvarHole(SCIP_VAR ** var,SCIP_Real * left,SCIP_Real * right)12332 SCIP_RETCODE SCIPvarGetProbvarHole(
12333 SCIP_VAR** var, /**< pointer to problem variable */
12334 SCIP_Real* left, /**< pointer to left bound of open interval in hole to transform */
12335 SCIP_Real* right /**< pointer to right bound of open interval in hole to transform */
12336 )
12337 {
12338 assert(var != NULL);
12339 assert(*var != NULL);
12340 assert(left != NULL);
12341 assert(right != NULL);
12342
12343 SCIPdebugMessage("get probvar hole (%g,%g) of variable <%s>\n", *left, *right, (*var)->name);
12344
12345 switch( SCIPvarGetStatus(*var) )
12346 {
12347 case SCIP_VARSTATUS_ORIGINAL:
12348 if( (*var)->data.original.transvar == NULL )
12349 {
12350 SCIPerrorMessage("original variable has no transformed variable attached\n");
12351 return SCIP_INVALIDDATA;
12352 }
12353 *var = (*var)->data.original.transvar;
12354 SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
12355 break;
12356
12357 case SCIP_VARSTATUS_LOOSE:
12358 case SCIP_VARSTATUS_COLUMN:
12359 case SCIP_VARSTATUS_FIXED:
12360 case SCIP_VARSTATUS_MULTAGGR:
12361 break;
12362
12363 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = x/a - c/a */
12364 assert((*var)->data.aggregate.var != NULL);
12365 assert((*var)->data.aggregate.scalar != 0.0);
12366
12367 /* scale back */
12368 (*left) /= (*var)->data.aggregate.scalar;
12369 (*right) /= (*var)->data.aggregate.scalar;
12370
12371 /* shift back */
12372 (*left) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12373 (*right) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
12374
12375 *var = (*var)->data.aggregate.var;
12376
12377 /* check if the interval bounds have to swapped */
12378 if( (*var)->data.aggregate.scalar < 0.0 )
12379 {
12380 SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
12381 }
12382 else
12383 {
12384 SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
12385 }
12386 break;
12387
12388 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12389 assert((*var)->negatedvar != NULL);
12390 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12391 assert((*var)->negatedvar->negatedvar == *var);
12392
12393 /* shift and scale back */
12394 (*left) = (*var)->data.negate.constant - (*left);
12395 (*right) = (*var)->data.negate.constant - (*right);
12396
12397 *var = (*var)->negatedvar;
12398
12399 /* through the negated variable the left and right interval bound have to swapped */
12400 SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
12401 break;
12402
12403 default:
12404 SCIPerrorMessage("unknown variable status\n");
12405 return SCIP_INVALIDDATA;
12406 }
12407
12408 return SCIP_OKAY;
12409 }
12410
12411 /** transforms given variable, scalar and constant to the corresponding active, fixed, or
12412 * multi-aggregated variable, scalar and constant; if the variable resolves to a fixed variable,
12413 * "scalar" will be 0.0 and the value of the sum will be stored in "constant"; a multi-aggregation
12414 * with only one active variable (this can happen due to fixings after the multi-aggregation),
12415 * is treated like an aggregation; if the multi-aggregation constant is infinite, "scalar" will be 0.0
12416 */
SCIPvarGetProbvarSum(SCIP_VAR ** var,SCIP_SET * set,SCIP_Real * scalar,SCIP_Real * constant)12417 SCIP_RETCODE SCIPvarGetProbvarSum(
12418 SCIP_VAR** var, /**< pointer to problem variable x in sum a*x + c */
12419 SCIP_SET* set, /**< global SCIP settings */
12420 SCIP_Real* scalar, /**< pointer to scalar a in sum a*x + c */
12421 SCIP_Real* constant /**< pointer to constant c in sum a*x + c */
12422 )
12423 {
12424 assert(var != NULL);
12425 assert(scalar != NULL);
12426 assert(constant != NULL);
12427
12428 while( *var != NULL )
12429 {
12430 switch( SCIPvarGetStatus(*var) )
12431 {
12432 case SCIP_VARSTATUS_ORIGINAL:
12433 if( (*var)->data.original.transvar == NULL )
12434 {
12435 SCIPerrorMessage("original variable has no transformed variable attached\n");
12436 return SCIP_INVALIDDATA;
12437 }
12438 *var = (*var)->data.original.transvar;
12439 break;
12440
12441 case SCIP_VARSTATUS_LOOSE:
12442 case SCIP_VARSTATUS_COLUMN:
12443 return SCIP_OKAY;
12444
12445 case SCIP_VARSTATUS_FIXED: /* x = c' => a*x + c == (a*c' + c) */
12446 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12447 {
12448 if( SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)) )
12449 {
12450 assert(*scalar != 0.0);
12451 if( (*scalar) * (*var)->glbdom.lb > 0.0 )
12452 (*constant) = SCIPsetInfinity(set);
12453 else
12454 (*constant) = -SCIPsetInfinity(set);
12455 }
12456 else
12457 (*constant) += *scalar * (*var)->glbdom.lb;
12458 }
12459 #ifndef NDEBUG
12460 else
12461 {
12462 assert(!SCIPsetIsInfinity(set, (*constant)) || !((*scalar) * (*var)->glbdom.lb < 0.0 &&
12463 (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
12464 assert(!SCIPsetIsInfinity(set, -(*constant)) || !((*scalar) * (*var)->glbdom.lb > 0.0 &&
12465 (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
12466 }
12467 #endif
12468 *scalar = 0.0;
12469 return SCIP_OKAY;
12470
12471 case SCIP_VARSTATUS_MULTAGGR:
12472 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
12473 if ( (*var)->data.multaggr.nvars == 1 )
12474 {
12475 assert((*var)->data.multaggr.vars != NULL);
12476 assert((*var)->data.multaggr.scalars != NULL);
12477 assert((*var)->data.multaggr.vars[0] != NULL);
12478 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12479 {
12480 /* the multi-aggregation constant can be infinite, if one of the multi-aggregation variables
12481 * was fixed to +/-infinity; ensure that the constant is set to +/-infinity, too, and the scalar
12482 * is set to 0.0, because the multi-aggregated variable can be seen as fixed, too
12483 */
12484 if( SCIPsetIsInfinity(set, (*var)->data.multaggr.constant)
12485 || SCIPsetIsInfinity(set, -((*var)->data.multaggr.constant)) )
12486 {
12487 if( (*scalar) * (*var)->data.multaggr.constant > 0 )
12488 {
12489 assert(!SCIPsetIsInfinity(set, -(*constant)));
12490 (*constant) = SCIPsetInfinity(set);
12491 }
12492 else
12493 {
12494 assert(!SCIPsetIsInfinity(set, *constant));
12495 (*constant) = -SCIPsetInfinity(set);
12496 }
12497 (*scalar) = 0.0;
12498 }
12499 else
12500 (*constant) += *scalar * (*var)->data.multaggr.constant;
12501 }
12502 (*scalar) *= (*var)->data.multaggr.scalars[0];
12503 *var = (*var)->data.multaggr.vars[0];
12504 break;
12505 }
12506 return SCIP_OKAY;
12507
12508 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
12509 assert((*var)->data.aggregate.var != NULL);
12510 assert(!SCIPsetIsInfinity(set, (*var)->data.aggregate.constant)
12511 && !SCIPsetIsInfinity(set, (*var)->data.aggregate.constant));
12512 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12513 (*constant) += *scalar * (*var)->data.aggregate.constant;
12514 (*scalar) *= (*var)->data.aggregate.scalar;
12515 *var = (*var)->data.aggregate.var;
12516 break;
12517
12518 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
12519 assert((*var)->negatedvar != NULL);
12520 assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
12521 assert((*var)->negatedvar->negatedvar == *var);
12522 assert(!SCIPsetIsInfinity(set, (*var)->data.negate.constant)
12523 && !SCIPsetIsInfinity(set, (*var)->data.negate.constant));
12524 if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
12525 (*constant) += *scalar * (*var)->data.negate.constant;
12526 (*scalar) *= -1.0;
12527 *var = (*var)->negatedvar;
12528 break;
12529
12530 default:
12531 SCIPerrorMessage("unknown variable status\n");
12532 SCIPABORT();
12533 return SCIP_INVALIDDATA; /*lint !e527*/
12534 }
12535 }
12536 *scalar = 0.0;
12537
12538 return SCIP_OKAY;
12539 }
12540
12541 /** retransforms given variable, scalar and constant to the corresponding original variable, scalar
12542 * and constant, if possible; if the retransformation is impossible, NULL is returned as variable
12543 */
SCIPvarGetOrigvarSum(SCIP_VAR ** var,SCIP_Real * scalar,SCIP_Real * constant)12544 SCIP_RETCODE SCIPvarGetOrigvarSum(
12545 SCIP_VAR** var, /**< pointer to problem variable x in sum a*x + c */
12546 SCIP_Real* scalar, /**< pointer to scalar a in sum a*x + c */
12547 SCIP_Real* constant /**< pointer to constant c in sum a*x + c */
12548 )
12549 {
12550 SCIP_VAR* parentvar;
12551
12552 assert(var != NULL);
12553 assert(*var != NULL);
12554 assert(scalar != NULL);
12555 assert(constant != NULL);
12556
12557 while( !SCIPvarIsOriginal(*var) )
12558 {
12559 /* if the variable has no parent variables, it was generated during solving and has no corresponding original
12560 * var
12561 */
12562 if( (*var)->nparentvars == 0 )
12563 {
12564 /* negated variables do not need to have a parent variables, and negated variables can exist in original
12565 * space
12566 */
12567 if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_NEGATED &&
12568 ((*var)->negatedvar->nparentvars == 0 || (*var)->negatedvar->parentvars[0] != *var) )
12569 {
12570 *scalar *= -1.0;
12571 *constant -= (*var)->data.negate.constant * (*scalar);
12572 *var = (*var)->negatedvar;
12573
12574 continue;
12575 }
12576 /* if the variables does not have any parent the variables was created during solving and has no original
12577 * counterpart
12578 */
12579 else
12580 {
12581 *var = NULL;
12582
12583 return SCIP_OKAY;
12584 }
12585 }
12586
12587 /* follow the link to the first parent variable */
12588 parentvar = (*var)->parentvars[0];
12589 assert(parentvar != NULL);
12590
12591 switch( SCIPvarGetStatus(parentvar) )
12592 {
12593 case SCIP_VARSTATUS_ORIGINAL:
12594 break;
12595
12596 case SCIP_VARSTATUS_COLUMN:
12597 case SCIP_VARSTATUS_LOOSE:
12598 case SCIP_VARSTATUS_FIXED:
12599 case SCIP_VARSTATUS_MULTAGGR:
12600 SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
12601 return SCIP_INVALIDDATA;
12602
12603 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + b -> y = (x-b)/a, s*y + c = (s/a)*x + c-b*s/a */
12604 assert(parentvar->data.aggregate.var == *var);
12605 assert(parentvar->data.aggregate.scalar != 0.0);
12606 *scalar /= parentvar->data.aggregate.scalar;
12607 *constant -= parentvar->data.aggregate.constant * (*scalar);
12608 break;
12609
12610 case SCIP_VARSTATUS_NEGATED: /* x = b - y -> y = b - x, s*y + c = -s*x + c+b*s */
12611 assert(parentvar->negatedvar != NULL);
12612 assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
12613 assert(parentvar->negatedvar->negatedvar == parentvar);
12614 *scalar *= -1.0;
12615 *constant -= parentvar->data.negate.constant * (*scalar);
12616 break;
12617
12618 default:
12619 SCIPerrorMessage("unknown variable status\n");
12620 return SCIP_INVALIDDATA;
12621 }
12622
12623 assert( parentvar != NULL );
12624 *var = parentvar;
12625 }
12626
12627 return SCIP_OKAY;
12628 }
12629
12630 /** returns whether the given variable is the direct counterpart of an original problem variable */
SCIPvarIsTransformedOrigvar(SCIP_VAR * var)12631 SCIP_Bool SCIPvarIsTransformedOrigvar(
12632 SCIP_VAR* var /**< problem variable */
12633 )
12634 {
12635 SCIP_VAR* parentvar;
12636 assert(var != NULL);
12637
12638 if( !SCIPvarIsTransformed(var) || var->nparentvars < 1 )
12639 return FALSE;
12640
12641 assert(var->parentvars != NULL);
12642 parentvar = var->parentvars[0];
12643 assert(parentvar != NULL);
12644
12645 /* we follow the aggregation tree to the root unless an original variable has been found - the first entries in the parentlist are candidates */
12646 while( parentvar->nparentvars >= 1 && SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL )
12647 parentvar = parentvar->parentvars[0];
12648 assert( parentvar != NULL );
12649
12650 return ( SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_ORIGINAL );
12651 }
12652
12653 /** gets objective value of variable in current SCIP_LP; the value can be different from the objective value stored in
12654 * the variable's own data due to diving, that operate only on the LP without updating the variables
12655 */
SCIPvarGetObjLP(SCIP_VAR * var)12656 SCIP_Real SCIPvarGetObjLP(
12657 SCIP_VAR* var /**< problem variable */
12658 )
12659 {
12660 assert(var != NULL);
12661
12662 /* get bounds of attached variables */
12663 switch( SCIPvarGetStatus(var) )
12664 {
12665 case SCIP_VARSTATUS_ORIGINAL:
12666 assert(var->data.original.transvar != NULL);
12667 return SCIPvarGetObjLP(var->data.original.transvar);
12668
12669 case SCIP_VARSTATUS_COLUMN:
12670 assert(var->data.col != NULL);
12671 return SCIPcolGetObj(var->data.col);
12672
12673 case SCIP_VARSTATUS_LOOSE:
12674 case SCIP_VARSTATUS_FIXED:
12675 return var->obj;
12676
12677 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
12678 assert(var->data.aggregate.var != NULL);
12679 return var->data.aggregate.scalar * SCIPvarGetObjLP(var->data.aggregate.var);
12680
12681 case SCIP_VARSTATUS_MULTAGGR:
12682 SCIPerrorMessage("cannot get the objective value of a multiple aggregated variable\n");
12683 SCIPABORT();
12684 return 0.0; /*lint !e527*/
12685
12686 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12687 assert(var->negatedvar != NULL);
12688 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12689 assert(var->negatedvar->negatedvar == var);
12690 return -SCIPvarGetObjLP(var->negatedvar);
12691
12692 default:
12693 SCIPerrorMessage("unknown variable status\n");
12694 SCIPABORT();
12695 return 0.0; /*lint !e527*/
12696 }
12697 }
12698
12699 /** gets lower bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
12700 * data due to diving or conflict analysis, that operate only on the LP without updating the variables
12701 */
SCIPvarGetLbLP(SCIP_VAR * var,SCIP_SET * set)12702 SCIP_Real SCIPvarGetLbLP(
12703 SCIP_VAR* var, /**< problem variable */
12704 SCIP_SET* set /**< global SCIP settings */
12705 )
12706 {
12707 assert(var != NULL);
12708 assert(set != NULL);
12709 assert(var->scip == set->scip);
12710
12711 /* get bounds of attached variables */
12712 switch( SCIPvarGetStatus(var) )
12713 {
12714 case SCIP_VARSTATUS_ORIGINAL:
12715 assert(var->data.original.transvar != NULL);
12716 return SCIPvarGetLbLP(var->data.original.transvar, set);
12717
12718 case SCIP_VARSTATUS_COLUMN:
12719 assert(var->data.col != NULL);
12720 return SCIPcolGetLb(var->data.col);
12721
12722 case SCIP_VARSTATUS_LOOSE:
12723 case SCIP_VARSTATUS_FIXED:
12724 return var->locdom.lb;
12725
12726 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
12727 assert(var->data.aggregate.var != NULL);
12728 if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set)))
12729 || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set))) )
12730 {
12731 return -SCIPsetInfinity(set);
12732 }
12733 else if( var->data.aggregate.scalar > 0.0 )
12734 {
12735 /* a > 0 -> get lower bound of y */
12736 return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12737 }
12738 else if( var->data.aggregate.scalar < 0.0 )
12739 {
12740 /* a < 0 -> get upper bound of y */
12741 return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12742 }
12743 else
12744 {
12745 SCIPerrorMessage("scalar is zero in aggregation\n");
12746 SCIPABORT();
12747 return SCIP_INVALID; /*lint !e527*/
12748 }
12749
12750 case SCIP_VARSTATUS_MULTAGGR:
12751 /**@todo get the sides of the corresponding linear constraint */
12752 SCIPerrorMessage("getting the bounds of a multiple aggregated variable is not implemented yet\n");
12753 SCIPABORT();
12754 return SCIP_INVALID; /*lint !e527*/
12755
12756 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12757 assert(var->negatedvar != NULL);
12758 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12759 assert(var->negatedvar->negatedvar == var);
12760 return var->data.negate.constant - SCIPvarGetUbLP(var->negatedvar, set);
12761
12762 default:
12763 SCIPerrorMessage("unknown variable status\n");
12764 SCIPABORT();
12765 return SCIP_INVALID; /*lint !e527*/
12766 }
12767 }
12768
12769 /** gets upper bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
12770 * data due to diving or conflict analysis, that operate only on the LP without updating the variables
12771 */
SCIPvarGetUbLP(SCIP_VAR * var,SCIP_SET * set)12772 SCIP_Real SCIPvarGetUbLP(
12773 SCIP_VAR* var, /**< problem variable */
12774 SCIP_SET* set /**< global SCIP settings */
12775 )
12776 {
12777 assert(var != NULL);
12778 assert(set != NULL);
12779 assert(var->scip == set->scip);
12780
12781 /* get bounds of attached variables */
12782 switch( SCIPvarGetStatus(var) )
12783 {
12784 case SCIP_VARSTATUS_ORIGINAL:
12785 assert(var->data.original.transvar != NULL);
12786 return SCIPvarGetUbLP(var->data.original.transvar, set);
12787
12788 case SCIP_VARSTATUS_COLUMN:
12789 assert(var->data.col != NULL);
12790 return SCIPcolGetUb(var->data.col);
12791
12792 case SCIP_VARSTATUS_LOOSE:
12793 case SCIP_VARSTATUS_FIXED:
12794 return var->locdom.ub;
12795
12796 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
12797 assert(var->data.aggregate.var != NULL);
12798 if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set)))
12799 || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set))) )
12800 {
12801 return SCIPsetInfinity(set);
12802 }
12803 if( var->data.aggregate.scalar > 0.0 )
12804 {
12805 /* a > 0 -> get upper bound of y */
12806 return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12807 }
12808 else if( var->data.aggregate.scalar < 0.0 )
12809 {
12810 /* a < 0 -> get lower bound of y */
12811 return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
12812 }
12813 else
12814 {
12815 SCIPerrorMessage("scalar is zero in aggregation\n");
12816 SCIPABORT();
12817 return SCIP_INVALID; /*lint !e527*/
12818 }
12819
12820 case SCIP_VARSTATUS_MULTAGGR:
12821 SCIPerrorMessage("cannot get the bounds of a multi-aggregated variable.\n");
12822 SCIPABORT();
12823 return SCIP_INVALID; /*lint !e527*/
12824
12825 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12826 assert(var->negatedvar != NULL);
12827 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12828 assert(var->negatedvar->negatedvar == var);
12829 return var->data.negate.constant - SCIPvarGetLbLP(var->negatedvar, set);
12830
12831 default:
12832 SCIPerrorMessage("unknown variable status\n");
12833 SCIPABORT();
12834 return SCIP_INVALID; /*lint !e527*/
12835 }
12836 }
12837
12838 /** gets primal LP solution value of variable */
SCIPvarGetLPSol_rec(SCIP_VAR * var)12839 SCIP_Real SCIPvarGetLPSol_rec(
12840 SCIP_VAR* var /**< problem variable */
12841 )
12842 {
12843 assert(var != NULL);
12844
12845 switch( SCIPvarGetStatus(var) )
12846 {
12847 case SCIP_VARSTATUS_ORIGINAL:
12848 if( var->data.original.transvar == NULL )
12849 return SCIP_INVALID;
12850 return SCIPvarGetLPSol(var->data.original.transvar);
12851
12852 case SCIP_VARSTATUS_LOOSE:
12853 return SCIPvarGetBestBoundLocal(var);
12854
12855 case SCIP_VARSTATUS_COLUMN:
12856 assert(var->data.col != NULL);
12857 return SCIPcolGetPrimsol(var->data.col);
12858
12859 case SCIP_VARSTATUS_FIXED:
12860 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
12861 return var->locdom.lb;
12862
12863 case SCIP_VARSTATUS_AGGREGATED:
12864 {
12865 SCIP_Real lpsolval;
12866
12867 assert(var->data.aggregate.var != NULL);
12868 lpsolval = SCIPvarGetLPSol(var->data.aggregate.var);
12869
12870 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
12871 * corresponding infinity value instead of performing an arithmetical transformation (compare method
12872 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
12873 * (or is called by) a public interface method; instead, we only assert that values are finite
12874 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
12875 * positives and negatives if the parameter <numerics/infinity> is modified by the user
12876 */
12877 assert(lpsolval > -SCIP_DEFAULT_INFINITY);
12878 assert(lpsolval < +SCIP_DEFAULT_INFINITY);
12879 return var->data.aggregate.scalar * lpsolval + var->data.aggregate.constant;
12880 }
12881 case SCIP_VARSTATUS_MULTAGGR:
12882 {
12883 SCIP_Real primsol;
12884 int i;
12885
12886 assert(!var->donotmultaggr);
12887 assert(var->data.multaggr.vars != NULL);
12888 assert(var->data.multaggr.scalars != NULL);
12889 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
12890 * assert(var->data.multaggr.nvars >= 2);
12891 */
12892 primsol = var->data.multaggr.constant;
12893 for( i = 0; i < var->data.multaggr.nvars; ++i )
12894 primsol += var->data.multaggr.scalars[i] * SCIPvarGetLPSol(var->data.multaggr.vars[i]);
12895 return primsol;
12896 }
12897 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
12898 assert(var->negatedvar != NULL);
12899 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
12900 assert(var->negatedvar->negatedvar == var);
12901 return var->data.negate.constant - SCIPvarGetLPSol(var->negatedvar);
12902
12903 default:
12904 SCIPerrorMessage("unknown variable status\n");
12905 SCIPABORT();
12906 return SCIP_INVALID; /*lint !e527*/
12907 }
12908 }
12909
12910 /** gets primal NLP solution value of variable */
SCIPvarGetNLPSol_rec(SCIP_VAR * var)12911 SCIP_Real SCIPvarGetNLPSol_rec(
12912 SCIP_VAR* var /**< problem variable */
12913 )
12914 {
12915 SCIP_Real solval;
12916 int i;
12917
12918 assert(var != NULL);
12919
12920 /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
12921 switch( SCIPvarGetStatus(var) )
12922 {
12923 case SCIP_VARSTATUS_ORIGINAL:
12924 return SCIPvarGetNLPSol(var->data.original.transvar);
12925
12926 case SCIP_VARSTATUS_LOOSE:
12927 case SCIP_VARSTATUS_COLUMN:
12928 return var->nlpsol;
12929
12930 case SCIP_VARSTATUS_FIXED:
12931 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var)); /*lint !e777*/
12932 assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var)); /*lint !e777*/
12933 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var)); /*lint !e777*/
12934 return SCIPvarGetLbGlobal(var);
12935
12936 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
12937 solval = SCIPvarGetNLPSol(var->data.aggregate.var);
12938 return var->data.aggregate.scalar * solval + var->data.aggregate.constant;
12939
12940 case SCIP_VARSTATUS_MULTAGGR:
12941 solval = var->data.multaggr.constant;
12942 for( i = 0; i < var->data.multaggr.nvars; ++i )
12943 solval += var->data.multaggr.scalars[i] * SCIPvarGetNLPSol(var->data.multaggr.vars[i]);
12944 return solval;
12945
12946 case SCIP_VARSTATUS_NEGATED:
12947 solval = SCIPvarGetNLPSol(var->negatedvar);
12948 return var->data.negate.constant - solval;
12949
12950 default:
12951 SCIPerrorMessage("unknown variable status\n");
12952 SCIPABORT();
12953 return SCIP_INVALID; /*lint !e527*/
12954 }
12955 }
12956
12957 /** gets pseudo solution value of variable at current node */
12958 static
SCIPvarGetPseudoSol_rec(SCIP_VAR * var)12959 SCIP_Real SCIPvarGetPseudoSol_rec(
12960 SCIP_VAR* var /**< problem variable */
12961 )
12962 {
12963 SCIP_Real pseudosol;
12964 int i;
12965
12966 assert(var != NULL);
12967
12968 switch( SCIPvarGetStatus(var) )
12969 {
12970 case SCIP_VARSTATUS_ORIGINAL:
12971 if( var->data.original.transvar == NULL )
12972 return SCIP_INVALID;
12973 return SCIPvarGetPseudoSol(var->data.original.transvar);
12974
12975 case SCIP_VARSTATUS_LOOSE:
12976 case SCIP_VARSTATUS_COLUMN:
12977 return SCIPvarGetBestBoundLocal(var);
12978
12979 case SCIP_VARSTATUS_FIXED:
12980 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
12981 return var->locdom.lb;
12982
12983 case SCIP_VARSTATUS_AGGREGATED:
12984 {
12985 SCIP_Real pseudosolval;
12986 assert(var->data.aggregate.var != NULL);
12987 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
12988 * corresponding infinity value instead of performing an arithmetical transformation (compare method
12989 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
12990 * (or is called by) a public interface method; instead, we only assert that values are finite
12991 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
12992 * positives and negatives if the parameter <numerics/infinity> is modified by the user
12993 */
12994 pseudosolval = SCIPvarGetPseudoSol(var->data.aggregate.var);
12995 assert(pseudosolval > -SCIP_DEFAULT_INFINITY);
12996 assert(pseudosolval < +SCIP_DEFAULT_INFINITY);
12997 return var->data.aggregate.scalar * pseudosolval + var->data.aggregate.constant;
12998 }
12999 case SCIP_VARSTATUS_MULTAGGR:
13000 assert(!var->donotmultaggr);
13001 assert(var->data.multaggr.vars != NULL);
13002 assert(var->data.multaggr.scalars != NULL);
13003 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13004 * assert(var->data.multaggr.nvars >= 2);
13005 */
13006 pseudosol = var->data.multaggr.constant;
13007 for( i = 0; i < var->data.multaggr.nvars; ++i )
13008 pseudosol += var->data.multaggr.scalars[i] * SCIPvarGetPseudoSol(var->data.multaggr.vars[i]);
13009 return pseudosol;
13010
13011 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13012 assert(var->negatedvar != NULL);
13013 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13014 assert(var->negatedvar->negatedvar == var);
13015 return var->data.negate.constant - SCIPvarGetPseudoSol(var->negatedvar);
13016
13017 default:
13018 SCIPerrorMessage("unknown variable status\n");
13019 SCIPABORT();
13020 return SCIP_INVALID; /*lint !e527*/
13021 }
13022 }
13023
13024 /** gets current LP or pseudo solution value of variable */
SCIPvarGetSol(SCIP_VAR * var,SCIP_Bool getlpval)13025 SCIP_Real SCIPvarGetSol(
13026 SCIP_VAR* var, /**< problem variable */
13027 SCIP_Bool getlpval /**< should the LP solution value be returned? */
13028 )
13029 {
13030 if( getlpval )
13031 return SCIPvarGetLPSol(var);
13032 else
13033 return SCIPvarGetPseudoSol(var);
13034 }
13035
13036 /** remembers the current solution as root solution in the problem variables */
SCIPvarStoreRootSol(SCIP_VAR * var,SCIP_Bool roothaslp)13037 void SCIPvarStoreRootSol(
13038 SCIP_VAR* var, /**< problem variable */
13039 SCIP_Bool roothaslp /**< is the root solution from LP? */
13040 )
13041 {
13042 assert(var != NULL);
13043
13044 var->rootsol = SCIPvarGetSol(var, roothaslp);
13045 }
13046
13047 /** updates the current solution as best root solution of the given variable if it is better */
SCIPvarUpdateBestRootSol(SCIP_VAR * var,SCIP_SET * set,SCIP_Real rootsol,SCIP_Real rootredcost,SCIP_Real rootlpobjval)13048 void SCIPvarUpdateBestRootSol(
13049 SCIP_VAR* var, /**< problem variable */
13050 SCIP_SET* set, /**< global SCIP settings */
13051 SCIP_Real rootsol, /**< root solution value */
13052 SCIP_Real rootredcost, /**< root reduced cost */
13053 SCIP_Real rootlpobjval /**< objective value of the root LP */
13054 )
13055 {
13056 assert(var != NULL);
13057 assert(set != NULL);
13058 assert(var->scip == set->scip);
13059
13060 /* if reduced cost are zero nothing to update */
13061 if( SCIPsetIsDualfeasZero(set, rootredcost) )
13062 return;
13063
13064 /* check if we have already a best combination stored */
13065 if( !SCIPsetIsDualfeasZero(set, var->bestrootredcost) )
13066 {
13067 SCIP_Real currcutoffbound;
13068 SCIP_Real cutoffbound;
13069 SCIP_Real bound;
13070
13071 /* compute the cutoff bound which would improve the corresponding bound with the current stored root solution,
13072 * root reduced cost, and root LP objective value combination
13073 */
13074 if( var->bestrootredcost > 0.0 )
13075 bound = SCIPvarGetUbGlobal(var);
13076 else
13077 bound = SCIPvarGetLbGlobal(var);
13078
13079 currcutoffbound = (bound - var->bestrootsol) * var->bestrootredcost + var->bestrootlpobjval;
13080
13081 /* compute the cutoff bound which would improve the corresponding bound with new root solution, root reduced
13082 * cost, and root LP objective value combination
13083 */
13084 if( rootredcost > 0.0 )
13085 bound = SCIPvarGetUbGlobal(var);
13086 else
13087 bound = SCIPvarGetLbGlobal(var);
13088
13089 cutoffbound = (bound - rootsol) * rootredcost + rootlpobjval;
13090
13091 /* check if an improving root solution, root reduced cost, and root LP objective value is at hand */
13092 if( cutoffbound > currcutoffbound )
13093 {
13094 SCIPsetDebugMsg(set, "-> <%s> update potential cutoff bound <%g> -> <%g>\n",
13095 SCIPvarGetName(var), currcutoffbound, cutoffbound);
13096
13097 var->bestrootsol = rootsol;
13098 var->bestrootredcost = rootredcost;
13099 var->bestrootlpobjval = rootlpobjval;
13100 }
13101 }
13102 else
13103 {
13104 SCIPsetDebugMsg(set, "-> <%s> initialize best root reduced cost information\n", SCIPvarGetName(var));
13105 SCIPsetDebugMsg(set, " -> rootsol <%g>\n", rootsol);
13106 SCIPsetDebugMsg(set, " -> rootredcost <%g>\n", rootredcost);
13107 SCIPsetDebugMsg(set, " -> rootlpobjval <%g>\n", rootlpobjval);
13108
13109 var->bestrootsol = rootsol;
13110 var->bestrootredcost = rootredcost;
13111 var->bestrootlpobjval = rootlpobjval;
13112 }
13113 }
13114
13115 /** returns the solution of the variable in the last root node's relaxation, if the root relaxation is not yet
13116 * completely solved, zero is returned
13117 */
SCIPvarGetRootSol(SCIP_VAR * var)13118 SCIP_Real SCIPvarGetRootSol(
13119 SCIP_VAR* var /**< problem variable */
13120 )
13121 {
13122 SCIP_Real rootsol;
13123 int i;
13124
13125 assert(var != NULL);
13126
13127 switch( SCIPvarGetStatus(var) )
13128 {
13129 case SCIP_VARSTATUS_ORIGINAL:
13130 if( var->data.original.transvar == NULL )
13131 return 0.0;
13132 return SCIPvarGetRootSol(var->data.original.transvar);
13133
13134 case SCIP_VARSTATUS_LOOSE:
13135 case SCIP_VARSTATUS_COLUMN:
13136 return var->rootsol;
13137
13138 case SCIP_VARSTATUS_FIXED:
13139 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13140 return var->locdom.lb;
13141
13142 case SCIP_VARSTATUS_AGGREGATED:
13143 assert(var->data.aggregate.var != NULL);
13144 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13145 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13146 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13147 * (or is called by) a public interface method; instead, we only assert that values are finite
13148 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13149 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13150 */
13151 assert(SCIPvarGetRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
13152 assert(SCIPvarGetRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
13153 return var->data.aggregate.scalar * SCIPvarGetRootSol(var->data.aggregate.var) + var->data.aggregate.constant;
13154
13155 case SCIP_VARSTATUS_MULTAGGR:
13156 assert(!var->donotmultaggr);
13157 assert(var->data.multaggr.vars != NULL);
13158 assert(var->data.multaggr.scalars != NULL);
13159 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13160 * assert(var->data.multaggr.nvars >= 2);
13161 */
13162 rootsol = var->data.multaggr.constant;
13163 for( i = 0; i < var->data.multaggr.nvars; ++i )
13164 rootsol += var->data.multaggr.scalars[i] * SCIPvarGetRootSol(var->data.multaggr.vars[i]);
13165 return rootsol;
13166
13167 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13168 assert(var->negatedvar != NULL);
13169 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13170 assert(var->negatedvar->negatedvar == var);
13171 return var->data.negate.constant - SCIPvarGetRootSol(var->negatedvar);
13172
13173 default:
13174 SCIPerrorMessage("unknown variable status\n");
13175 SCIPABORT();
13176 return SCIP_INVALID; /*lint !e527*/
13177 }
13178 }
13179
13180 /** returns for given variable the reduced cost */
13181 static
getImplVarRedcost(SCIP_VAR * var,SCIP_SET * set,SCIP_Bool varfixing,SCIP_STAT * stat,SCIP_LP * lp)13182 SCIP_Real getImplVarRedcost(
13183 SCIP_VAR* var, /**< problem variable */
13184 SCIP_SET* set, /**< global SCIP settings */
13185 SCIP_Bool varfixing, /**< FALSE if for x == 0, TRUE for x == 1 */
13186 SCIP_STAT* stat, /**< problem statistics */
13187 SCIP_LP* lp /**< current LP data */
13188 )
13189 {
13190 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
13191 {
13192 SCIP_COL* col;
13193 SCIP_Real primsol;
13194 SCIP_BASESTAT basestat;
13195 SCIP_Bool lpissolbasic;
13196
13197 col = SCIPvarGetCol(var);
13198 assert(col != NULL);
13199
13200 basestat = SCIPcolGetBasisStatus(col);
13201 lpissolbasic = SCIPlpIsSolBasic(lp);
13202 primsol = SCIPcolGetPrimsol(col);
13203
13204 if( (lpissolbasic && (basestat == SCIP_BASESTAT_LOWER || basestat == SCIP_BASESTAT_UPPER)) ||
13205 (!lpissolbasic && (SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol) || SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol))) )
13206 {
13207 SCIP_Real redcost = SCIPcolGetRedcost(col, stat, lp);
13208
13209 assert(((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)) ||
13210 (lpissolbasic && basestat == SCIP_BASESTAT_LOWER)) ? (!SCIPsetIsDualfeasNegative(set, redcost) ||
13211 SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE);
13212 assert(((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)) ||
13213 (lpissolbasic && basestat == SCIP_BASESTAT_UPPER)) ? (!SCIPsetIsDualfeasPositive(set, redcost) ||
13214 SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE);
13215
13216 if( (varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_LOWER) ||
13217 (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)))) ||
13218 (!varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_UPPER) ||
13219 (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)))) )
13220 return redcost;
13221 else
13222 return 0.0;
13223 }
13224
13225 return 0.0;
13226 }
13227
13228 return 0.0;
13229 }
13230
13231 #define MAX_CLIQUELENGTH 50
13232 /** returns for the given binary variable the reduced cost which are given by the variable itself and its implication if
13233 * the binary variable is fixed to the given value
13234 */
SCIPvarGetImplRedcost(SCIP_VAR * var,SCIP_SET * set,SCIP_Bool varfixing,SCIP_STAT * stat,SCIP_PROB * prob,SCIP_LP * lp)13235 SCIP_Real SCIPvarGetImplRedcost(
13236 SCIP_VAR* var, /**< problem variable */
13237 SCIP_SET* set, /**< global SCIP settings */
13238 SCIP_Bool varfixing, /**< FALSE if for x == 0, TRUE for x == 1 */
13239 SCIP_STAT* stat, /**< problem statistics */
13240 SCIP_PROB* prob, /**< transformed problem, or NULL */
13241 SCIP_LP* lp /**< current LP data */
13242 )
13243 {
13244 SCIP_Real implredcost;
13245 int ncliques;
13246 int nvars;
13247
13248 assert(SCIPvarIsBinary(var));
13249 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
13250
13251 /* get reduced cost of given variable */
13252 implredcost = getImplVarRedcost(var, set, varfixing, stat, lp);
13253
13254 #ifdef SCIP_MORE_DEBUG
13255 SCIPsetDebugMsg(set, "variable <%s> itself has reduced cost of %g\n", SCIPvarGetName(var), implredcost);
13256 #endif
13257
13258 /* the following algorithm is expensive */
13259 ncliques = SCIPvarGetNCliques(var, varfixing);
13260
13261 if( ncliques > 0 )
13262 {
13263 SCIP_CLIQUE** cliques;
13264 SCIP_CLIQUE* clique;
13265 SCIP_VAR** clqvars;
13266 SCIP_VAR** probvars;
13267 SCIP_VAR* clqvar;
13268 SCIP_Bool* clqvalues;
13269 int* entries;
13270 int* ids;
13271 SCIP_Real redcost;
13272 SCIP_Bool cleanedup;
13273 int nclqvars;
13274 int nentries;
13275 int nids;
13276 int id;
13277 int c;
13278 int v;
13279
13280 assert(prob != NULL);
13281 assert(SCIPprobIsTransformed(prob));
13282
13283 nentries = SCIPprobGetNVars(prob) - SCIPprobGetNContVars(prob) + 1;
13284
13285 SCIP_CALL_ABORT( SCIPsetAllocBufferArray(set, &ids, nentries) );
13286 nids = 0;
13287 SCIP_CALL_ABORT( SCIPsetAllocCleanBufferArray(set, &entries, nentries) );
13288
13289 cliques = SCIPvarGetCliques(var, varfixing);
13290 assert(cliques != NULL);
13291
13292 for( c = ncliques - 1; c >= 0; --c )
13293 {
13294 clique = cliques[c];
13295 assert(clique != NULL);
13296 nclqvars = SCIPcliqueGetNVars(clique);
13297 assert(nclqvars > 0);
13298
13299 if( nclqvars > MAX_CLIQUELENGTH )
13300 continue;
13301
13302 clqvars = SCIPcliqueGetVars(clique);
13303 clqvalues = SCIPcliqueGetValues(clique);
13304 assert(clqvars != NULL);
13305 assert(clqvalues != NULL);
13306
13307 cleanedup = SCIPcliqueIsCleanedUp(clique);
13308
13309 for( v = nclqvars - 1; v >= 0; --v )
13310 {
13311 clqvar = clqvars[v];
13312 assert(clqvar != NULL);
13313
13314 /* ignore binary variable which are fixed */
13315 if( clqvar != var && (cleanedup || SCIPvarIsActive(clqvar)) &&
13316 (SCIPvarGetLbLocal(clqvar) < 0.5 && SCIPvarGetUbLocal(clqvar) > 0.5) )
13317 {
13318 int probindex = SCIPvarGetProbindex(clqvar) + 1;
13319 assert(0 < probindex && probindex < nentries);
13320
13321 #if 0
13322 /* check that the variable was not yet visited or does not appear with two contradicting implications, ->
13323 * can appear since there is no guarantee that all these infeasible bounds were found
13324 */
13325 assert(!entries[probindex] || entries[probindex] == (clqvalues[v] ? probindex : -probindex));
13326 #endif
13327 if( entries[probindex] == 0 )
13328 {
13329 ids[nids] = probindex;
13330 ++nids;
13331
13332 /* mark variable as visited */
13333 entries[probindex] = (clqvalues[v] ? probindex : -probindex);
13334 }
13335 }
13336 }
13337 }
13338
13339 probvars = SCIPprobGetVars(prob);
13340 assert(probvars != NULL);
13341
13342 /* add all implied reduced cost */
13343 for( v = nids - 1; v >= 0; --v )
13344 {
13345 id = ids[v];
13346 assert(0 < id && id < nentries);
13347 assert(entries[id] != 0);
13348 assert(probvars[id - 1] != NULL);
13349 assert(SCIPvarIsActive(probvars[id - 1]));
13350 assert(SCIPvarIsBinary(probvars[id - 1]));
13351 assert(SCIPvarGetLbLocal(probvars[id - 1]) < 0.5 && SCIPvarGetUbLocal(probvars[id - 1]) > 0.5);
13352
13353 if( (entries[id] > 0) != varfixing )
13354 redcost = getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);
13355 else
13356 redcost = -getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);
13357
13358 if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
13359 implredcost += redcost;
13360
13361 /* reset entries clear buffer array */
13362 entries[id] = 0;
13363 }
13364
13365 SCIPsetFreeCleanBufferArray(set, &entries);
13366 SCIPsetFreeBufferArray(set, &ids);
13367 }
13368
13369 #ifdef SCIP_MORE_DEBUG
13370 SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) has implied reduced cost of %g\n", SCIPvarGetName(var), ncliques,
13371 implredcost);
13372 #endif
13373
13374 /* collect non-binary implication information */
13375 nvars = SCIPimplicsGetNImpls(var->implics, varfixing);
13376
13377 if( nvars > 0 )
13378 {
13379 SCIP_VAR** vars;
13380 SCIP_VAR* implvar;
13381 SCIP_COL* col;
13382 SCIP_Real* bounds;
13383 SCIP_BOUNDTYPE* boundtypes;
13384 SCIP_Real redcost;
13385 SCIP_Real lb;
13386 SCIP_Real ub;
13387 SCIP_Bool lpissolbasic;
13388 int v;
13389
13390 vars = SCIPimplicsGetVars(var->implics, varfixing);
13391 boundtypes = SCIPimplicsGetTypes(var->implics, varfixing);
13392 bounds = SCIPimplicsGetBounds(var->implics, varfixing);
13393 lpissolbasic = SCIPlpIsSolBasic(lp);
13394
13395 for( v = nvars - 1; v >= 0; --v )
13396 {
13397 implvar = vars[v];
13398 assert(implvar != NULL);
13399
13400 lb = SCIPvarGetLbLocal(implvar);
13401 ub = SCIPvarGetUbLocal(implvar);
13402
13403 /* ignore binary variable which are fixed or not of column status */
13404 if( SCIPvarGetStatus(implvar) != SCIP_VARSTATUS_COLUMN || SCIPsetIsFeasEQ(set, lb, ub) )
13405 continue;
13406
13407 col = SCIPvarGetCol(implvar);
13408 assert(col != NULL);
13409 redcost = 0.0;
13410
13411 /* solved lp with basis information or not? */
13412 if( lpissolbasic )
13413 {
13414 SCIP_BASESTAT basestat = SCIPcolGetBasisStatus(col);
13415
13416 /* check if the implication is not not yet applied */
13417 if( basestat == SCIP_BASESTAT_LOWER && boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, bounds[v], lb) )
13418 {
13419 redcost = SCIPcolGetRedcost(col, stat, lp);
13420 assert(!SCIPsetIsDualfeasNegative(set, redcost));
13421
13422 if( !varfixing )
13423 redcost *= (lb - bounds[v]);
13424 else
13425 redcost *= (bounds[v] - lb);
13426 }
13427 else if( basestat == SCIP_BASESTAT_UPPER && boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, bounds[v], ub) )
13428 {
13429 redcost = SCIPcolGetRedcost(col, stat, lp);
13430 assert(!SCIPsetIsDualfeasPositive(set, redcost));
13431
13432 if( varfixing )
13433 redcost *= (bounds[v] - ub);
13434 else
13435 redcost *= (ub - bounds[v]);
13436 }
13437 }
13438 else
13439 {
13440 SCIP_Real primsol = SCIPcolGetPrimsol(col);
13441
13442 /* check if the implication is not not yet applied */
13443 if( boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasEQ(set, lb, primsol) && SCIPsetIsFeasGT(set, bounds[v], lb) )
13444 {
13445 redcost = SCIPcolGetRedcost(col, stat, lp);
13446 assert(!SCIPsetIsDualfeasNegative(set, redcost));
13447
13448 if( varfixing )
13449 redcost *= (lb - bounds[v]);
13450 else
13451 redcost *= (bounds[v] - lb);
13452 }
13453 else if( boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasEQ(set, ub, primsol) && SCIPsetIsFeasLT(set, bounds[v], ub) )
13454 {
13455 redcost = SCIPcolGetRedcost(col, stat, lp);
13456 assert(!SCIPsetIsDualfeasPositive(set, redcost));
13457
13458 if( varfixing )
13459 redcost *= (bounds[v] - ub);
13460 else
13461 redcost *= (ub - bounds[v]);
13462 }
13463 }
13464
13465 /* improve implied reduced cost */
13466 if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
13467 implredcost += redcost;
13468 }
13469 }
13470
13471 #ifdef SCIP_MORE_DEBUG
13472 SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) and implications (%d) has implied reduced cost of %g\n",
13473 SCIPvarGetName(var), ncliques, nvars, implredcost);
13474 #endif
13475
13476 return implredcost;
13477 }
13478
13479 /** returns the best solution (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation, if
13480 * the root relaxation is not yet completely solved, zero is returned
13481 */
SCIPvarGetBestRootSol(SCIP_VAR * var)13482 SCIP_Real SCIPvarGetBestRootSol(
13483 SCIP_VAR* var /**< problem variable */
13484 )
13485 {
13486 SCIP_Real rootsol;
13487 int i;
13488
13489 assert(var != NULL);
13490
13491 switch( SCIPvarGetStatus(var) )
13492 {
13493 case SCIP_VARSTATUS_ORIGINAL:
13494 if( var->data.original.transvar == NULL )
13495 return 0.0;
13496 return SCIPvarGetBestRootSol(var->data.original.transvar);
13497
13498 case SCIP_VARSTATUS_LOOSE:
13499 case SCIP_VARSTATUS_COLUMN:
13500 return var->bestrootsol;
13501
13502 case SCIP_VARSTATUS_FIXED:
13503 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13504 return var->locdom.lb;
13505
13506 case SCIP_VARSTATUS_AGGREGATED:
13507 assert(var->data.aggregate.var != NULL);
13508 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
13509 * corresponding infinity value instead of performing an arithmetical transformation (compare method
13510 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
13511 * (or is called by) a public interface method; instead, we only assert that values are finite
13512 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
13513 * positives and negatives if the parameter <numerics/infinity> is modified by the user
13514 */
13515 assert(SCIPvarGetBestRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
13516 assert(SCIPvarGetBestRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
13517 return var->data.aggregate.scalar * SCIPvarGetBestRootSol(var->data.aggregate.var) + var->data.aggregate.constant;
13518
13519 case SCIP_VARSTATUS_MULTAGGR:
13520 assert(!var->donotmultaggr);
13521 assert(var->data.multaggr.vars != NULL);
13522 assert(var->data.multaggr.scalars != NULL);
13523 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13524 * assert(var->data.multaggr.nvars >= 2);
13525 */
13526 rootsol = var->data.multaggr.constant;
13527 for( i = 0; i < var->data.multaggr.nvars; ++i )
13528 rootsol += var->data.multaggr.scalars[i] * SCIPvarGetBestRootSol(var->data.multaggr.vars[i]);
13529 return rootsol;
13530
13531 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13532 assert(var->negatedvar != NULL);
13533 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13534 assert(var->negatedvar->negatedvar == var);
13535 return var->data.negate.constant - SCIPvarGetBestRootSol(var->negatedvar);
13536
13537 default:
13538 SCIPerrorMessage("unknown variable status\n");
13539 SCIPABORT();
13540 return 0.0; /*lint !e527*/
13541 }
13542 }
13543
13544 /** returns the best reduced costs (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation,
13545 * if the root relaxation is not yet completely solved, or the variable was no column of the root LP, SCIP_INVALID is
13546 * returned
13547 */
SCIPvarGetBestRootRedcost(SCIP_VAR * var)13548 SCIP_Real SCIPvarGetBestRootRedcost(
13549 SCIP_VAR* var /**< problem variable */
13550 )
13551 {
13552 assert(var != NULL);
13553
13554 switch( SCIPvarGetStatus(var) )
13555 {
13556 case SCIP_VARSTATUS_ORIGINAL:
13557 if( var->data.original.transvar == NULL )
13558 return SCIP_INVALID;
13559 return SCIPvarGetBestRootRedcost(var->data.original.transvar);
13560
13561 case SCIP_VARSTATUS_LOOSE:
13562 case SCIP_VARSTATUS_COLUMN:
13563 return var->bestrootredcost;
13564
13565 case SCIP_VARSTATUS_FIXED:
13566 case SCIP_VARSTATUS_AGGREGATED:
13567 case SCIP_VARSTATUS_MULTAGGR:
13568 case SCIP_VARSTATUS_NEGATED:
13569 return 0.0;
13570
13571 default:
13572 SCIPerrorMessage("unknown variable status\n");
13573 SCIPABORT();
13574 return 0.0; /*lint !e527*/
13575 }
13576 }
13577
13578 /** returns the best objective value (w.r.t. root reduced cost propagation) of the root LP which belongs the root
13579 * reduced cost which is accessible via SCIPvarGetRootRedcost() or the variable was no column of the root LP,
13580 * SCIP_INVALID is returned
13581 */
SCIPvarGetBestRootLPObjval(SCIP_VAR * var)13582 SCIP_Real SCIPvarGetBestRootLPObjval(
13583 SCIP_VAR* var /**< problem variable */
13584 )
13585 {
13586 assert(var != NULL);
13587
13588 switch( SCIPvarGetStatus(var) )
13589 {
13590 case SCIP_VARSTATUS_ORIGINAL:
13591 if( var->data.original.transvar == NULL )
13592 return SCIP_INVALID;
13593 return SCIPvarGetBestRootLPObjval(var->data.original.transvar);
13594
13595 case SCIP_VARSTATUS_LOOSE:
13596 case SCIP_VARSTATUS_COLUMN:
13597 return var->bestrootlpobjval;
13598
13599 case SCIP_VARSTATUS_FIXED:
13600 case SCIP_VARSTATUS_AGGREGATED:
13601 case SCIP_VARSTATUS_MULTAGGR:
13602 case SCIP_VARSTATUS_NEGATED:
13603 return SCIP_INVALID;
13604
13605 default:
13606 SCIPerrorMessage("unknown variable status\n");
13607 SCIPABORT();
13608 return SCIP_INVALID; /*lint !e527*/
13609 }
13610 }
13611
13612 /** set the given solution as the best root solution w.r.t. root reduced cost propagation in the variables */
SCIPvarSetBestRootSol(SCIP_VAR * var,SCIP_Real rootsol,SCIP_Real rootredcost,SCIP_Real rootlpobjval)13613 void SCIPvarSetBestRootSol(
13614 SCIP_VAR* var, /**< problem variable */
13615 SCIP_Real rootsol, /**< root solution value */
13616 SCIP_Real rootredcost, /**< root reduced cost */
13617 SCIP_Real rootlpobjval /**< objective value of the root LP */
13618 )
13619 {
13620 assert(var != NULL);
13621
13622 var->bestrootsol = rootsol;
13623 var->bestrootredcost = rootredcost;
13624 var->bestrootlpobjval = rootlpobjval;
13625 }
13626
13627 /** stores the solution value as relaxation solution in the problem variable */
SCIPvarSetRelaxSol(SCIP_VAR * var,SCIP_SET * set,SCIP_RELAXATION * relaxation,SCIP_Real solval,SCIP_Bool updateobj)13628 SCIP_RETCODE SCIPvarSetRelaxSol(
13629 SCIP_VAR* var, /**< problem variable */
13630 SCIP_SET* set, /**< global SCIP settings */
13631 SCIP_RELAXATION* relaxation, /**< global relaxation data */
13632 SCIP_Real solval, /**< solution value in the current relaxation solution */
13633 SCIP_Bool updateobj /**< should the objective value be updated? */
13634 )
13635 {
13636 assert(var != NULL);
13637 assert(relaxation != NULL);
13638 assert(set != NULL);
13639 assert(var->scip == set->scip);
13640
13641 /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
13642 switch( SCIPvarGetStatus(var) )
13643 {
13644 case SCIP_VARSTATUS_ORIGINAL:
13645 SCIP_CALL( SCIPvarSetRelaxSol(var->data.original.transvar, set, relaxation, solval, updateobj) );
13646 break;
13647
13648 case SCIP_VARSTATUS_LOOSE:
13649 case SCIP_VARSTATUS_COLUMN:
13650 if( updateobj )
13651 SCIPrelaxationSolObjAdd(relaxation, var->obj * (solval - var->relaxsol));
13652 var->relaxsol = solval;
13653 break;
13654
13655 case SCIP_VARSTATUS_FIXED:
13656 if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
13657 {
13658 SCIPerrorMessage("cannot set relaxation solution value for variable <%s> fixed to %.15g to different value %.15g\n",
13659 SCIPvarGetName(var), var->glbdom.lb, solval);
13660 return SCIP_INVALIDDATA;
13661 }
13662 break;
13663
13664 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13665 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
13666 SCIP_CALL( SCIPvarSetRelaxSol(var->data.aggregate.var, set, relaxation,
13667 (solval - var->data.aggregate.constant)/var->data.aggregate.scalar, updateobj) );
13668 break;
13669 case SCIP_VARSTATUS_MULTAGGR:
13670 SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
13671 return SCIP_INVALIDDATA;
13672
13673 case SCIP_VARSTATUS_NEGATED:
13674 SCIP_CALL( SCIPvarSetRelaxSol(var->negatedvar, set, relaxation, var->data.negate.constant - solval, updateobj) );
13675 break;
13676
13677 default:
13678 SCIPerrorMessage("unknown variable status\n");
13679 return SCIP_INVALIDDATA;
13680 }
13681
13682 return SCIP_OKAY;
13683 }
13684
13685 /** returns the solution value of the problem variable in the relaxation solution
13686 *
13687 * @todo Inline this function - similar to SCIPvarGetLPSol_rec.
13688 */
SCIPvarGetRelaxSol(SCIP_VAR * var,SCIP_SET * set)13689 SCIP_Real SCIPvarGetRelaxSol(
13690 SCIP_VAR* var, /**< problem variable */
13691 SCIP_SET* set /**< global SCIP settings */
13692 )
13693 {
13694 SCIP_Real solvalsum;
13695 SCIP_Real solval;
13696 int i;
13697
13698 assert(var != NULL);
13699 assert(set != NULL);
13700 assert(var->scip == set->scip);
13701
13702 /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
13703 switch( SCIPvarGetStatus(var) )
13704 {
13705 case SCIP_VARSTATUS_ORIGINAL:
13706 return SCIPvarGetRelaxSol(var->data.original.transvar, set);
13707
13708 case SCIP_VARSTATUS_LOOSE:
13709 case SCIP_VARSTATUS_COLUMN:
13710 return var->relaxsol;
13711
13712 case SCIP_VARSTATUS_FIXED:
13713 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var)); /*lint !e777*/
13714 assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var)); /*lint !e777*/
13715 assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var)); /*lint !e777*/
13716 return SCIPvarGetLbGlobal(var);
13717
13718 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13719 solval = SCIPvarGetRelaxSol(var->data.aggregate.var, set);
13720 if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
13721 {
13722 if( var->data.aggregate.scalar * solval > 0.0 )
13723 return SCIPsetInfinity(set);
13724 if( var->data.aggregate.scalar * solval < 0.0 )
13725 return -SCIPsetInfinity(set);
13726 }
13727 return var->data.aggregate.scalar * solval + var->data.aggregate.constant;
13728
13729 case SCIP_VARSTATUS_MULTAGGR:
13730 solvalsum = var->data.multaggr.constant;
13731 for( i = 0; i < var->data.multaggr.nvars; ++i )
13732 {
13733 solval = SCIPvarGetRelaxSol(var->data.multaggr.vars[i], set);
13734 if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
13735 {
13736 if( var->data.multaggr.scalars[i] * solval > 0.0 )
13737 return SCIPsetInfinity(set);
13738 if( var->data.multaggr.scalars[i] * solval < 0.0 )
13739 return -SCIPsetInfinity(set);
13740 }
13741 solvalsum += var->data.multaggr.scalars[i] * solval;
13742 }
13743 return solvalsum;
13744
13745 case SCIP_VARSTATUS_NEGATED:
13746 solval = SCIPvarGetRelaxSol(var->negatedvar, set);
13747 if( SCIPsetIsInfinity(set, solval) )
13748 return -SCIPsetInfinity(set);
13749 if( SCIPsetIsInfinity(set, -solval) )
13750 return SCIPsetInfinity(set);
13751 return var->data.negate.constant - solval;
13752
13753 default:
13754 SCIPerrorMessage("unknown variable status\n");
13755 SCIPABORT();
13756 return SCIP_INVALID; /*lint !e527*/
13757 }
13758 }
13759
13760 /** returns the solution value of the transformed problem variable in the relaxation solution */
SCIPvarGetRelaxSolTransVar(SCIP_VAR * var)13761 SCIP_Real SCIPvarGetRelaxSolTransVar(
13762 SCIP_VAR* var /**< problem variable */
13763 )
13764 {
13765 assert(var != NULL);
13766 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
13767
13768 return var->relaxsol;
13769 }
13770
13771 /** stores the solution value as NLP solution in the problem variable */
SCIPvarSetNLPSol(SCIP_VAR * var,SCIP_SET * set,SCIP_Real solval)13772 SCIP_RETCODE SCIPvarSetNLPSol(
13773 SCIP_VAR* var, /**< problem variable */
13774 SCIP_SET* set, /**< global SCIP settings */
13775 SCIP_Real solval /**< solution value in the current NLP solution */
13776 )
13777 {
13778 assert(var != NULL);
13779 assert(set != NULL);
13780 assert(var->scip == set->scip);
13781
13782 /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
13783 switch( SCIPvarGetStatus(var) )
13784 {
13785 case SCIP_VARSTATUS_ORIGINAL:
13786 SCIP_CALL( SCIPvarSetNLPSol(var->data.original.transvar, set, solval) );
13787 break;
13788
13789 case SCIP_VARSTATUS_LOOSE:
13790 case SCIP_VARSTATUS_COLUMN:
13791 var->nlpsol = solval;
13792 break;
13793
13794 case SCIP_VARSTATUS_FIXED:
13795 if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
13796 {
13797 SCIPerrorMessage("cannot set NLP solution value for variable <%s> fixed to %.15g to different value %.15g\n",
13798 SCIPvarGetName(var), var->glbdom.lb, solval);
13799 SCIPABORT();
13800 return SCIP_INVALIDCALL; /*lint !e527*/
13801 }
13802 break;
13803
13804 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c => y = (x-c)/a */
13805 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
13806 SCIP_CALL( SCIPvarSetNLPSol(var->data.aggregate.var, set, (solval - var->data.aggregate.constant)/var->data.aggregate.scalar) );
13807 break;
13808
13809 case SCIP_VARSTATUS_MULTAGGR:
13810 SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
13811 SCIPABORT();
13812 return SCIP_INVALIDCALL; /*lint !e527*/
13813
13814 case SCIP_VARSTATUS_NEGATED:
13815 SCIP_CALL( SCIPvarSetNLPSol(var->negatedvar, set, var->data.negate.constant - solval) );
13816 break;
13817
13818 default:
13819 SCIPerrorMessage("unknown variable status\n");
13820 SCIPABORT();
13821 return SCIP_ERROR; /*lint !e527*/
13822 }
13823
13824 return SCIP_OKAY;
13825 }
13826
13827 /** returns a weighted average solution value of the variable in all feasible primal solutions found so far */
SCIPvarGetAvgSol(SCIP_VAR * var)13828 SCIP_Real SCIPvarGetAvgSol(
13829 SCIP_VAR* var /**< problem variable */
13830 )
13831 {
13832 SCIP_Real avgsol;
13833 int i;
13834
13835 assert(var != NULL);
13836
13837 switch( SCIPvarGetStatus(var) )
13838 {
13839 case SCIP_VARSTATUS_ORIGINAL:
13840 if( var->data.original.transvar == NULL )
13841 return 0.0;
13842 return SCIPvarGetAvgSol(var->data.original.transvar);
13843
13844 case SCIP_VARSTATUS_LOOSE:
13845 case SCIP_VARSTATUS_COLUMN:
13846 avgsol = var->primsolavg;
13847 avgsol = MAX(avgsol, var->glbdom.lb);
13848 avgsol = MIN(avgsol, var->glbdom.ub);
13849 return avgsol;
13850
13851 case SCIP_VARSTATUS_FIXED:
13852 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
13853 return var->locdom.lb;
13854
13855 case SCIP_VARSTATUS_AGGREGATED:
13856 assert(var->data.aggregate.var != NULL);
13857 return var->data.aggregate.scalar * SCIPvarGetAvgSol(var->data.aggregate.var)
13858 + var->data.aggregate.constant;
13859
13860 case SCIP_VARSTATUS_MULTAGGR:
13861 assert(!var->donotmultaggr);
13862 assert(var->data.multaggr.vars != NULL);
13863 assert(var->data.multaggr.scalars != NULL);
13864 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
13865 * assert(var->data.multaggr.nvars >= 2);
13866 */
13867 avgsol = var->data.multaggr.constant;
13868 for( i = 0; i < var->data.multaggr.nvars; ++i )
13869 avgsol += var->data.multaggr.scalars[i] * SCIPvarGetAvgSol(var->data.multaggr.vars[i]);
13870 return avgsol;
13871
13872 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
13873 assert(var->negatedvar != NULL);
13874 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
13875 assert(var->negatedvar->negatedvar == var);
13876 return var->data.negate.constant - SCIPvarGetAvgSol(var->negatedvar);
13877
13878 default:
13879 SCIPerrorMessage("unknown variable status\n");
13880 SCIPABORT();
13881 return 0.0; /*lint !e527*/
13882 }
13883 }
13884
13885 /** returns solution value and index of variable lower bound that is closest to the variable's value in the given primal solution
13886 * or current LP solution if no primal solution is given; returns an index of -1 if no variable lower bound is available
13887 */
SCIPvarGetClosestVlb(SCIP_VAR * var,SCIP_SOL * sol,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * closestvlb,int * closestvlbidx)13888 void SCIPvarGetClosestVlb(
13889 SCIP_VAR* var, /**< active problem variable */
13890 SCIP_SOL* sol, /**< primal solution, or NULL for LP solution */
13891 SCIP_SET* set, /**< global SCIP settings */
13892 SCIP_STAT* stat, /**< problem statistics */
13893 SCIP_Real* closestvlb, /**< pointer to store the value of the closest variable lower bound */
13894 int* closestvlbidx /**< pointer to store the index of the closest variable lower bound */
13895 )
13896 {
13897 int nvlbs;
13898
13899 assert(var != NULL);
13900 assert(stat != NULL);
13901 assert(set != NULL);
13902 assert(var->scip == set->scip);
13903 assert(closestvlb != NULL);
13904 assert(closestvlbidx != NULL);
13905
13906 *closestvlbidx = -1;
13907 *closestvlb = SCIP_REAL_MIN;
13908
13909 nvlbs = SCIPvarGetNVlbs(var);
13910 if( nvlbs > 0 )
13911 {
13912 SCIP_VAR** vlbvars;
13913 SCIP_Real* vlbcoefs;
13914 SCIP_Real* vlbconsts;
13915 int i;
13916
13917 vlbvars = SCIPvarGetVlbVars(var);
13918 vlbcoefs = SCIPvarGetVlbCoefs(var);
13919 vlbconsts = SCIPvarGetVlbConstants(var);
13920
13921 /* check for cached values */
13922 if( var->closestvblpcount == stat->lpcount && var->closestvlbidx != -1 && sol == NULL)
13923 {
13924 i = var->closestvlbidx;
13925 assert(0 <= i && i < nvlbs);
13926 assert(SCIPvarIsActive(vlbvars[i]));
13927 *closestvlbidx = i;
13928 *closestvlb = vlbcoefs[i] * SCIPvarGetLPSol(vlbvars[i]) + vlbconsts[i];
13929 }
13930 else
13931 {
13932 /* search best VUB */
13933 for( i = 0; i < nvlbs; i++ )
13934 {
13935 if( SCIPvarIsActive(vlbvars[i]) )
13936 {
13937 SCIP_Real vlbsol;
13938
13939 vlbsol = vlbcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vlbvars[i]) : SCIPsolGetVal(sol, set, stat, vlbvars[i])) + vlbconsts[i];
13940 if( vlbsol > *closestvlb )
13941 {
13942 *closestvlb = vlbsol;
13943 *closestvlbidx = i;
13944 }
13945 }
13946 }
13947
13948 if( sol == NULL )
13949 {
13950 /* update cached value */
13951 if( var->closestvblpcount != stat->lpcount )
13952 var->closestvubidx = -1;
13953 var->closestvlbidx = *closestvlbidx;
13954 var->closestvblpcount = stat->lpcount;
13955 }
13956 }
13957 }
13958 }
13959
13960 /** returns solution value and index of variable upper bound that is closest to the variable's value in the given primal solution;
13961 * or current LP solution if no primal solution is given; returns an index of -1 if no variable upper bound is available
13962 */
SCIPvarGetClosestVub(SCIP_VAR * var,SCIP_SOL * sol,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * closestvub,int * closestvubidx)13963 void SCIPvarGetClosestVub(
13964 SCIP_VAR* var, /**< active problem variable */
13965 SCIP_SOL* sol, /**< primal solution, or NULL for LP solution */
13966 SCIP_SET* set, /**< global SCIP settings */
13967 SCIP_STAT* stat, /**< problem statistics */
13968 SCIP_Real* closestvub, /**< pointer to store the value of the closest variable upper bound */
13969 int* closestvubidx /**< pointer to store the index of the closest variable upper bound */
13970 )
13971 {
13972 int nvubs;
13973
13974 assert(var != NULL);
13975 assert(set != NULL);
13976 assert(var->scip == set->scip);
13977 assert(closestvub != NULL);
13978 assert(closestvubidx != NULL);
13979
13980 *closestvubidx = -1;
13981 *closestvub = SCIP_REAL_MAX;
13982
13983 nvubs = SCIPvarGetNVubs(var);
13984 if( nvubs > 0 )
13985 {
13986 SCIP_VAR** vubvars;
13987 SCIP_Real* vubcoefs;
13988 SCIP_Real* vubconsts;
13989 int i;
13990
13991 vubvars = SCIPvarGetVubVars(var);
13992 vubcoefs = SCIPvarGetVubCoefs(var);
13993 vubconsts = SCIPvarGetVubConstants(var);
13994
13995 /* check for cached values */
13996 if( var->closestvblpcount == stat->lpcount && var->closestvubidx != -1 && sol == NULL)
13997 {
13998 i = var->closestvubidx;
13999 assert(0 <= i && i < nvubs);
14000 assert(SCIPvarIsActive(vubvars[i]));
14001 *closestvubidx = i;
14002 *closestvub = vubcoefs[i] * SCIPvarGetLPSol(vubvars[i]) + vubconsts[i];
14003 }
14004 else
14005 {
14006 /* search best VUB */
14007 for( i = 0; i < nvubs; i++ )
14008 {
14009 if( SCIPvarIsActive(vubvars[i]) )
14010 {
14011 SCIP_Real vubsol;
14012
14013 vubsol = vubcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vubvars[i]) : SCIPsolGetVal(sol, set, stat, vubvars[i])) + vubconsts[i];
14014 if( vubsol < *closestvub )
14015 {
14016 *closestvub = vubsol;
14017 *closestvubidx = i;
14018 }
14019 }
14020 }
14021
14022 if( sol == NULL )
14023 {
14024 /* update cached value */
14025 if( var->closestvblpcount != stat->lpcount )
14026 var->closestvlbidx = -1;
14027 var->closestvubidx = *closestvubidx;
14028 var->closestvblpcount = stat->lpcount;
14029 }
14030 }
14031 }
14032 }
14033
14034 /** resolves variable to columns and adds them with the coefficient to the row */
SCIPvarAddToRow(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_PROB * prob,SCIP_LP * lp,SCIP_ROW * row,SCIP_Real val)14035 SCIP_RETCODE SCIPvarAddToRow(
14036 SCIP_VAR* var, /**< problem variable */
14037 BMS_BLKMEM* blkmem, /**< block memory */
14038 SCIP_SET* set, /**< global SCIP settings */
14039 SCIP_STAT* stat, /**< problem statistics */
14040 SCIP_EVENTQUEUE* eventqueue, /**< event queue */
14041 SCIP_PROB* prob, /**< problem data */
14042 SCIP_LP* lp, /**< current LP data */
14043 SCIP_ROW* row, /**< LP row */
14044 SCIP_Real val /**< value of coefficient */
14045 )
14046 {
14047 int i;
14048
14049 assert(var != NULL);
14050 assert(set != NULL);
14051 assert(var->scip == set->scip);
14052 assert(row != NULL);
14053 assert(!SCIPsetIsInfinity(set, REALABS(val)));
14054
14055 SCIPsetDebugMsg(set, "adding coefficient %g<%s> to row <%s>\n", val, var->name, row->name);
14056
14057 if ( SCIPsetIsZero(set, val) )
14058 return SCIP_OKAY;
14059
14060 switch( SCIPvarGetStatus(var) )
14061 {
14062 case SCIP_VARSTATUS_ORIGINAL:
14063 if( var->data.original.transvar == NULL )
14064 {
14065 SCIPerrorMessage("cannot add untransformed original variable <%s> to LP row <%s>\n", var->name, row->name);
14066 return SCIP_INVALIDDATA;
14067 }
14068 SCIP_CALL( SCIPvarAddToRow(var->data.original.transvar, blkmem, set, stat, eventqueue, prob, lp, row, val) );
14069 return SCIP_OKAY;
14070
14071 case SCIP_VARSTATUS_LOOSE:
14072 /* add globally fixed variables as constant */
14073 if( SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub) )
14074 {
14075 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->glbdom.lb) );
14076 return SCIP_OKAY;
14077 }
14078 /* convert loose variable into column */
14079 SCIP_CALL( SCIPvarColumn(var, blkmem, set, stat, prob, lp) );
14080 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
14081 /*lint -fallthrough*/
14082
14083 case SCIP_VARSTATUS_COLUMN:
14084 assert(var->data.col != NULL);
14085 assert(var->data.col->var == var);
14086 SCIP_CALL( SCIProwIncCoef(row, blkmem, set, eventqueue, lp, var->data.col, val) );
14087 return SCIP_OKAY;
14088
14089 case SCIP_VARSTATUS_FIXED:
14090 assert(var->glbdom.lb == var->glbdom.ub); /*lint !e777*/
14091 assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
14092 assert(var->locdom.lb == var->glbdom.lb); /*lint !e777*/
14093 assert(!SCIPsetIsInfinity(set, REALABS(var->locdom.lb)));
14094 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->locdom.lb) );
14095 return SCIP_OKAY;
14096
14097 case SCIP_VARSTATUS_AGGREGATED:
14098 assert(var->data.aggregate.var != NULL);
14099 SCIP_CALL( SCIPvarAddToRow(var->data.aggregate.var, blkmem, set, stat, eventqueue, prob, lp,
14100 row, var->data.aggregate.scalar * val) );
14101 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.aggregate.constant * val) );
14102 return SCIP_OKAY;
14103
14104 case SCIP_VARSTATUS_MULTAGGR:
14105 assert(!var->donotmultaggr);
14106 assert(var->data.multaggr.vars != NULL);
14107 assert(var->data.multaggr.scalars != NULL);
14108 /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
14109 * assert(var->data.multaggr.nvars >= 2);
14110 */
14111 for( i = 0; i < var->data.multaggr.nvars; ++i )
14112 {
14113 SCIP_CALL( SCIPvarAddToRow(var->data.multaggr.vars[i], blkmem, set, stat, eventqueue, prob, lp,
14114 row, var->data.multaggr.scalars[i] * val) );
14115 }
14116 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.multaggr.constant * val) );
14117 return SCIP_OKAY;
14118
14119 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
14120 assert(var->negatedvar != NULL);
14121 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
14122 assert(var->negatedvar->negatedvar == var);
14123 SCIP_CALL( SCIPvarAddToRow(var->negatedvar, blkmem, set, stat, eventqueue, prob, lp, row, -val) );
14124 SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.negate.constant * val) );
14125 return SCIP_OKAY;
14126
14127 default:
14128 SCIPerrorMessage("unknown variable status\n");
14129 return SCIP_INVALIDDATA;
14130 }
14131 }
14132
14133 /* optionally, define this compiler flag to write complete variable histories to a file */
14134 #ifdef SCIP_HISTORYTOFILE
14135 SCIP_Longint counter = 0l;
14136 const char* historypath="."; /* allows for user-defined path; use '.' for calling directory of SCIP */
14137 #include "scip/scip.h"
14138 #endif
14139
14140 /** updates the pseudo costs of the given variable and the global pseudo costs after a change of
14141 * "solvaldelta" in the variable's solution value and resulting change of "objdelta" in the in the LP's objective value
14142 */
SCIPvarUpdatePseudocost(SCIP_VAR * var,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real solvaldelta,SCIP_Real objdelta,SCIP_Real weight)14143 SCIP_RETCODE SCIPvarUpdatePseudocost(
14144 SCIP_VAR* var, /**< problem variable */
14145 SCIP_SET* set, /**< global SCIP settings */
14146 SCIP_STAT* stat, /**< problem statistics */
14147 SCIP_Real solvaldelta, /**< difference of variable's new LP value - old LP value */
14148 SCIP_Real objdelta, /**< difference of new LP's objective value - old LP's objective value */
14149 SCIP_Real weight /**< weight in (0,1] of this update in pseudo cost sum */
14150 )
14151 {
14152 SCIP_Real oldrootpseudocosts;
14153 assert(var != NULL);
14154 assert(set != NULL);
14155 assert(var->scip == set->scip);
14156 assert(stat != NULL);
14157
14158 /* check if history statistics should be collected for a variable */
14159 if( !stat->collectvarhistory )
14160 return SCIP_OKAY;
14161
14162 switch( SCIPvarGetStatus(var) )
14163 {
14164 case SCIP_VARSTATUS_ORIGINAL:
14165 if( var->data.original.transvar == NULL )
14166 {
14167 SCIPerrorMessage("cannot update pseudo costs of original untransformed variable\n");
14168 return SCIP_INVALIDDATA;
14169 }
14170 SCIP_CALL( SCIPvarUpdatePseudocost(var->data.original.transvar, set, stat, solvaldelta, objdelta, weight) );
14171 return SCIP_OKAY;
14172
14173 case SCIP_VARSTATUS_LOOSE:
14174 case SCIP_VARSTATUS_COLUMN:
14175 /* store old pseudo-costs for root LP best-estimate update */
14176 oldrootpseudocosts = SCIPvarGetMinPseudocostScore(var, stat, set, SCIPvarGetRootSol(var));
14177
14178 /* update history */
14179 SCIPhistoryUpdatePseudocost(var->history, set, solvaldelta, objdelta, weight);
14180 SCIPhistoryUpdatePseudocost(var->historycrun, set, solvaldelta, objdelta, weight);
14181 SCIPhistoryUpdatePseudocost(stat->glbhistory, set, solvaldelta, objdelta, weight);
14182 SCIPhistoryUpdatePseudocost(stat->glbhistorycrun, set, solvaldelta, objdelta, weight);
14183
14184 /* update root LP best-estimate */
14185 SCIP_CALL( SCIPstatUpdateVarRootLPBestEstimate(stat, set, var, oldrootpseudocosts) );
14186
14187 /* append history to file */
14188 #ifdef SCIP_HISTORYTOFILE
14189 {
14190 FILE* f;
14191 char filename[256];
14192 SCIP_NODE* currentnode;
14193 SCIP_NODE* parentnode;
14194 currentnode = SCIPgetFocusNode(set->scip);
14195 parentnode = SCIPnodeGetParent(currentnode);
14196
14197 sprintf(filename, "%s/%s.pse", historypath, SCIPgetProbName(set->scip));
14198 f = fopen(filename, "a");
14199 if( NULL != f )
14200 {
14201 fprintf(f, "%lld %s \t %lld \t %lld \t %lld \t %d \t %15.9f \t %.3f\n",
14202 ++counter,
14203 SCIPvarGetName(var),
14204 SCIPnodeGetNumber(currentnode),
14205 parentnode != NULL ? SCIPnodeGetNumber(parentnode) : -1,
14206 SCIPgetNLPIterations(set->scip),
14207 SCIPgetDepth(set->scip),
14208 objdelta,
14209 solvaldelta);
14210 fclose(f);
14211 }
14212 }
14213 #endif
14214 return SCIP_OKAY;
14215
14216 case SCIP_VARSTATUS_FIXED:
14217 SCIPerrorMessage("cannot update pseudo cost values of a fixed variable\n");
14218 return SCIP_INVALIDDATA;
14219
14220 case SCIP_VARSTATUS_AGGREGATED:
14221 assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
14222 SCIP_CALL( SCIPvarUpdatePseudocost(var->data.aggregate.var, set, stat,
14223 solvaldelta/var->data.aggregate.scalar, objdelta, weight) );
14224 return SCIP_OKAY;
14225
14226 case SCIP_VARSTATUS_MULTAGGR:
14227 SCIPerrorMessage("cannot update pseudo cost values of a multi-aggregated variable\n");
14228 return SCIP_INVALIDDATA;
14229
14230 case SCIP_VARSTATUS_NEGATED:
14231 SCIP_CALL( SCIPvarUpdatePseudocost(var->negatedvar, set, stat, -solvaldelta, objdelta, weight) );
14232 return SCIP_OKAY;
14233
14234 default:
14235 SCIPerrorMessage("unknown variable status\n");
14236 return SCIP_INVALIDDATA;
14237 }
14238 }
14239
14240 /** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value */
SCIPvarGetPseudocost(SCIP_VAR * var,SCIP_STAT * stat,SCIP_Real solvaldelta)14241 SCIP_Real SCIPvarGetPseudocost(
14242 SCIP_VAR* var, /**< problem variable */
14243 SCIP_STAT* stat, /**< problem statistics */
14244 SCIP_Real solvaldelta /**< difference of variable's new LP value - old LP value */
14245 )
14246 {
14247 SCIP_BRANCHDIR dir;
14248
14249 assert(var != NULL);
14250 assert(stat != NULL);
14251
14252 switch( SCIPvarGetStatus(var) )
14253 {
14254 case SCIP_VARSTATUS_ORIGINAL:
14255 if( var->data.original.transvar == NULL )
14256 return SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);
14257 else
14258 return SCIPvarGetPseudocost(var->data.original.transvar, stat, solvaldelta);
14259
14260 case SCIP_VARSTATUS_LOOSE:
14261 case SCIP_VARSTATUS_COLUMN:
14262 dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
14263
14264 return SCIPhistoryGetPseudocostCount(var->history, dir) > 0.0
14265 ? SCIPhistoryGetPseudocost(var->history, solvaldelta)
14266 : SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);
14267
14268 case SCIP_VARSTATUS_FIXED:
14269 return 0.0;
14270
14271 case SCIP_VARSTATUS_AGGREGATED:
14272 return SCIPvarGetPseudocost(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);
14273
14274 case SCIP_VARSTATUS_MULTAGGR:
14275 return 0.0;
14276
14277 case SCIP_VARSTATUS_NEGATED:
14278 return SCIPvarGetPseudocost(var->negatedvar, stat, -solvaldelta);
14279
14280 default:
14281 SCIPerrorMessage("unknown variable status\n");
14282 SCIPABORT();
14283 return 0.0; /*lint !e527*/
14284 }
14285 }
14286
14287 /** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value,
14288 * only using the pseudo cost information of the current run
14289 */
SCIPvarGetPseudocostCurrentRun(SCIP_VAR * var,SCIP_STAT * stat,SCIP_Real solvaldelta)14290 SCIP_Real SCIPvarGetPseudocostCurrentRun(
14291 SCIP_VAR* var, /**< problem variable */
14292 SCIP_STAT* stat, /**< problem statistics */
14293 SCIP_Real solvaldelta /**< difference of variable's new LP value - old LP value */
14294 )
14295 {
14296 SCIP_BRANCHDIR dir;
14297
14298 assert(var != NULL);
14299 assert(stat != NULL);
14300
14301 switch( SCIPvarGetStatus(var) )
14302 {
14303 case SCIP_VARSTATUS_ORIGINAL:
14304 if( var->data.original.transvar == NULL )
14305 return SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);
14306 else
14307 return SCIPvarGetPseudocostCurrentRun(var->data.original.transvar, stat, solvaldelta);
14308
14309 case SCIP_VARSTATUS_LOOSE:
14310 case SCIP_VARSTATUS_COLUMN:
14311 dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
14312
14313 return SCIPhistoryGetPseudocostCount(var->historycrun, dir) > 0.0
14314 ? SCIPhistoryGetPseudocost(var->historycrun, solvaldelta)
14315 : SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);
14316
14317 case SCIP_VARSTATUS_FIXED:
14318 return 0.0;
14319
14320 case SCIP_VARSTATUS_AGGREGATED:
14321 return SCIPvarGetPseudocostCurrentRun(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);
14322
14323 case SCIP_VARSTATUS_MULTAGGR:
14324 return 0.0;
14325
14326 case SCIP_VARSTATUS_NEGATED:
14327 return SCIPvarGetPseudocostCurrentRun(var->negatedvar, stat, -solvaldelta);
14328
14329 default:
14330 SCIPerrorMessage("unknown variable status\n");
14331 SCIPABORT();
14332 return 0.0; /*lint !e527*/
14333 }
14334 }
14335
14336 /** gets the variable's (possible fractional) number of pseudo cost updates for the given direction */
SCIPvarGetPseudocostCount(SCIP_VAR * var,SCIP_BRANCHDIR dir)14337 SCIP_Real SCIPvarGetPseudocostCount(
14338 SCIP_VAR* var, /**< problem variable */
14339 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
14340 )
14341 {
14342 assert(var != NULL);
14343 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14344
14345 switch( SCIPvarGetStatus(var) )
14346 {
14347 case SCIP_VARSTATUS_ORIGINAL:
14348 if( var->data.original.transvar == NULL )
14349 return 0.0;
14350 else
14351 return SCIPvarGetPseudocostCount(var->data.original.transvar, dir);
14352
14353 case SCIP_VARSTATUS_LOOSE:
14354 case SCIP_VARSTATUS_COLUMN:
14355 return SCIPhistoryGetPseudocostCount(var->history, dir);
14356
14357 case SCIP_VARSTATUS_FIXED:
14358 return 0.0;
14359
14360 case SCIP_VARSTATUS_AGGREGATED:
14361 if( var->data.aggregate.scalar > 0.0 )
14362 return SCIPvarGetPseudocostCount(var->data.aggregate.var, dir);
14363 else
14364 return SCIPvarGetPseudocostCount(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
14365
14366 case SCIP_VARSTATUS_MULTAGGR:
14367 return 0.0;
14368
14369 case SCIP_VARSTATUS_NEGATED:
14370 return SCIPvarGetPseudocostCount(var->negatedvar, SCIPbranchdirOpposite(dir));
14371
14372 default:
14373 SCIPerrorMessage("unknown variable status\n");
14374 SCIPABORT();
14375 return 0.0; /*lint !e527*/
14376 }
14377 }
14378
14379 /** gets the variable's (possible fractional) number of pseudo cost updates for the given direction,
14380 * only using the pseudo cost information of the current run
14381 */
SCIPvarGetPseudocostCountCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)14382 SCIP_Real SCIPvarGetPseudocostCountCurrentRun(
14383 SCIP_VAR* var, /**< problem variable */
14384 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
14385 )
14386 {
14387 assert(var != NULL);
14388 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14389
14390 switch( SCIPvarGetStatus(var) )
14391 {
14392 case SCIP_VARSTATUS_ORIGINAL:
14393 if( var->data.original.transvar == NULL )
14394 return 0.0;
14395 else
14396 return SCIPvarGetPseudocostCountCurrentRun(var->data.original.transvar, dir);
14397
14398 case SCIP_VARSTATUS_LOOSE:
14399 case SCIP_VARSTATUS_COLUMN:
14400 return SCIPhistoryGetPseudocostCount(var->historycrun, dir);
14401
14402 case SCIP_VARSTATUS_FIXED:
14403 return 0.0;
14404
14405 case SCIP_VARSTATUS_AGGREGATED:
14406 if( var->data.aggregate.scalar > 0.0 )
14407 return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, dir);
14408 else
14409 return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
14410
14411 case SCIP_VARSTATUS_MULTAGGR:
14412 return 0.0;
14413
14414 case SCIP_VARSTATUS_NEGATED:
14415 return SCIPvarGetPseudocostCountCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
14416
14417 default:
14418 SCIPerrorMessage("unknown variable status\n");
14419 SCIPABORT();
14420 return 0.0; /*lint !e527*/
14421 }
14422 }
14423
14424 /** compares both possible directions for rounding the given solution value and returns the minimum pseudo-costs of the variable */
SCIPvarGetMinPseudocostScore(SCIP_VAR * var,SCIP_STAT * stat,SCIP_SET * set,SCIP_Real solval)14425 SCIP_Real SCIPvarGetMinPseudocostScore(
14426 SCIP_VAR* var, /**< problem variable */
14427 SCIP_STAT* stat, /**< problem statistics */
14428 SCIP_SET* set, /**< global SCIP settings */
14429 SCIP_Real solval /**< solution value, e.g., LP solution value */
14430 )
14431 {
14432 SCIP_Real upscore;
14433 SCIP_Real downscore;
14434 SCIP_Real solvaldeltaup;
14435 SCIP_Real solvaldeltadown;
14436
14437 /* LP root estimate only works for variables with fractional LP root solution */
14438 if( SCIPsetIsFeasIntegral(set, solval) )
14439 return 0.0;
14440
14441 /* no min pseudo-cost score is calculated as long as the variable was not initialized in a direction */
14442 if( SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_DOWNWARDS) < 1.0 || SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_UPWARDS) < 1.0 )
14443 return 0.0;
14444
14445 /* compute delta's to ceil and floor of root LP solution value */
14446 solvaldeltaup = SCIPsetCeil(set, solval) - solval;
14447 solvaldeltadown = SCIPsetFloor(set, solval) - solval;
14448
14449 upscore = SCIPvarGetPseudocost(var, stat, solvaldeltaup);
14450 downscore = SCIPvarGetPseudocost(var, stat, solvaldeltadown);
14451
14452 return MIN(upscore, downscore);
14453 }
14454
14455 /** gets the an estimate of the variable's pseudo cost variance in direction \p dir */
SCIPvarGetPseudocostVariance(SCIP_VAR * var,SCIP_BRANCHDIR dir,SCIP_Bool onlycurrentrun)14456 SCIP_Real SCIPvarGetPseudocostVariance(
14457 SCIP_VAR* var, /**< problem variable */
14458 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
14459 SCIP_Bool onlycurrentrun /**< return pseudo cost variance only for current branch and bound run */
14460 )
14461 {
14462 assert(var != NULL);
14463 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14464
14465 switch( SCIPvarGetStatus(var) )
14466 {
14467 case SCIP_VARSTATUS_ORIGINAL:
14468 if( var->data.original.transvar == NULL )
14469 return 0.0;
14470 else
14471 return SCIPvarGetPseudocostVariance(var->data.original.transvar, dir, onlycurrentrun);
14472
14473 case SCIP_VARSTATUS_LOOSE:
14474 case SCIP_VARSTATUS_COLUMN:
14475 if( onlycurrentrun )
14476 return SCIPhistoryGetPseudocostVariance(var->historycrun, dir);
14477 else
14478 return SCIPhistoryGetPseudocostVariance(var->history, dir);
14479
14480 case SCIP_VARSTATUS_FIXED:
14481 return 0.0;
14482
14483 case SCIP_VARSTATUS_AGGREGATED:
14484 if( var->data.aggregate.scalar > 0.0 )
14485 return SCIPvarGetPseudocostVariance(var->data.aggregate.var, dir, onlycurrentrun);
14486 else
14487 return SCIPvarGetPseudocostVariance(var->data.aggregate.var, SCIPbranchdirOpposite(dir), onlycurrentrun);
14488
14489 case SCIP_VARSTATUS_MULTAGGR:
14490 return 0.0;
14491
14492 case SCIP_VARSTATUS_NEGATED:
14493 return SCIPvarGetPseudocostVariance(var->negatedvar, SCIPbranchdirOpposite(dir), onlycurrentrun);
14494
14495 default:
14496 SCIPerrorMessage("unknown variable status\n");
14497 SCIPABORT();
14498 return 0.0; /*lint !e527*/
14499 }
14500 }
14501
14502 /** calculates a confidence bound for this variable under the assumption of normally distributed pseudo costs
14503 *
14504 * The confidence bound \f$ \theta \geq 0\f$ denotes the interval borders \f$ [X - \theta, \ X + \theta]\f$, which contains
14505 * the true pseudo costs of the variable, i.e., the expected value of the normal distribution, with a probability
14506 * of 2 * clevel - 1.
14507 *
14508 * @return value of confidence bound for this variable
14509 */
SCIPvarCalcPscostConfidenceBound(SCIP_VAR * var,SCIP_SET * set,SCIP_BRANCHDIR dir,SCIP_Bool onlycurrentrun,SCIP_CONFIDENCELEVEL clevel)14510 SCIP_Real SCIPvarCalcPscostConfidenceBound(
14511 SCIP_VAR* var, /**< variable in question */
14512 SCIP_SET* set, /**< global SCIP settings */
14513 SCIP_BRANCHDIR dir, /**< the branching direction for the confidence bound */
14514 SCIP_Bool onlycurrentrun, /**< should only the current run be taken into account */
14515 SCIP_CONFIDENCELEVEL clevel /**< confidence level for the interval */
14516 )
14517 {
14518 SCIP_Real confidencebound;
14519
14520 confidencebound = SCIPvarGetPseudocostVariance(var, dir, onlycurrentrun);
14521 if( SCIPsetIsFeasPositive(set, confidencebound) )
14522 {
14523 SCIP_Real count;
14524
14525 if( onlycurrentrun )
14526 count = SCIPvarGetPseudocostCountCurrentRun(var, dir);
14527 else
14528 count = SCIPvarGetPseudocostCount(var, dir);
14529 /* assertion is valid because variance is positive */
14530 assert(count >= 1.9);
14531
14532 confidencebound /= count; /*lint !e414 division by zero can obviously not occur */
14533 confidencebound = sqrt(confidencebound);
14534
14535 /* the actual, underlying distribution of the mean is a student-t-distribution with degrees of freedom equal to
14536 * the number of pseudo cost evaluations of this variable in the respective direction. */
14537 confidencebound *= SCIPstudentTGetCriticalValue(clevel, (int)SCIPsetFloor(set, count) - 1);
14538 }
14539 else
14540 confidencebound = 0.0;
14541
14542 return confidencebound;
14543 }
14544
14545 /** check if the current pseudo cost relative error in a direction violates the given threshold. The Relative
14546 * Error is calculated at a specific confidence level
14547 */
SCIPvarIsPscostRelerrorReliable(SCIP_VAR * var,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real threshold,SCIP_CONFIDENCELEVEL clevel)14548 SCIP_Bool SCIPvarIsPscostRelerrorReliable(
14549 SCIP_VAR* var, /**< variable in question */
14550 SCIP_SET* set, /**< global SCIP settings */
14551 SCIP_STAT* stat, /**< problem statistics */
14552 SCIP_Real threshold, /**< threshold for relative errors to be considered reliable (enough) */
14553 SCIP_CONFIDENCELEVEL clevel /**< a given confidence level */
14554 )
14555 {
14556 SCIP_Real downsize;
14557 SCIP_Real upsize;
14558 SCIP_Real size;
14559 SCIP_Real relerrorup;
14560 SCIP_Real relerrordown;
14561 SCIP_Real relerror;
14562
14563 /* check, if the pseudo cost score of the variable is reliable */
14564 downsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_DOWNWARDS);
14565 upsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_UPWARDS);
14566 size = MIN(downsize, upsize);
14567
14568 /* Pseudo costs relative error can only be reliable if both directions have been tried at least twice */
14569 if( size <= 1.9 )
14570 return FALSE;
14571
14572 /* use the relative error between the current mean pseudo cost value of the candidate and its upper
14573 * confidence interval bound at confidence level of 95% for individual variable reliability.
14574 * this is only possible if we have at least 2 measurements and therefore a valid variance estimate.
14575 */
14576 if( downsize >= 1.9 )
14577 {
14578 SCIP_Real normval;
14579
14580 relerrordown = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_DOWNWARDS, TRUE, clevel);
14581 normval = SCIPvarGetPseudocostCurrentRun(var, stat, -1.0);
14582 normval = MAX(1.0, normval);
14583
14584 relerrordown /= normval;
14585 }
14586 else
14587 relerrordown = 0.0;
14588
14589 if( upsize >= 1.9 )
14590 {
14591 SCIP_Real normval;
14592
14593 relerrorup = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_UPWARDS, TRUE, clevel);
14594 normval = SCIPvarGetPseudocostCurrentRun(var, stat, +1.0);
14595 normval = MAX(1.0, normval);
14596 relerrorup /= normval;
14597 }
14598 else
14599 relerrorup = 0.0;
14600
14601 /* consider the relative error threshold violated, if it is violated in at least one branching direction */
14602 relerror = MAX(relerrorup, relerrordown);
14603
14604 return (relerror <= threshold);
14605 }
14606
14607 /** check if variable pseudo-costs have a significant difference in location. The significance depends on
14608 * the choice of \p clevel and on the kind of tested hypothesis. The one-sided hypothesis, which
14609 * should be rejected, is that fracy * mu_y >= fracx * mu_x, where mu_y and mu_x denote the
14610 * unknown location means of the underlying pseudo-cost distributions of x and y.
14611 *
14612 * This method is applied best if variable x has a better pseudo-cost score than y. The method hypothesizes that y were actually
14613 * better than x (despite the current information), meaning that y can be expected to yield branching
14614 * decisions as least as good as x in the long run. If the method returns TRUE, the current history information is
14615 * sufficient to safely rely on the alternative hypothesis that x yields indeed a better branching score (on average)
14616 * than y.
14617 *
14618 * @note The order of x and y matters for the one-sided hypothesis
14619 *
14620 * @note set \p onesided to FALSE if you are not sure which variable is better. The hypothesis tested then reads
14621 * fracy * mu_y == fracx * mu_x vs the alternative hypothesis fracy * mu_y != fracx * mu_x.
14622 *
14623 * @return TRUE if the hypothesis can be safely rejected at the given confidence level
14624 */
SCIPvarSignificantPscostDifference(SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * varx,SCIP_Real fracx,SCIP_VAR * vary,SCIP_Real fracy,SCIP_BRANCHDIR dir,SCIP_CONFIDENCELEVEL clevel,SCIP_Bool onesided)14625 SCIP_Bool SCIPvarSignificantPscostDifference(
14626 SCIP_SET* set, /**< global SCIP settings */
14627 SCIP_STAT* stat, /**< problem statistics */
14628 SCIP_VAR* varx, /**< variable x */
14629 SCIP_Real fracx, /**< the fractionality of variable x */
14630 SCIP_VAR* vary, /**< variable y */
14631 SCIP_Real fracy, /**< the fractionality of variable y */
14632 SCIP_BRANCHDIR dir, /**< branching direction */
14633 SCIP_CONFIDENCELEVEL clevel, /**< confidence level for rejecting hypothesis */
14634 SCIP_Bool onesided /**< should a one-sided hypothesis y >= x be tested? */
14635 )
14636 {
14637 SCIP_Real meanx;
14638 SCIP_Real meany;
14639 SCIP_Real variancex;
14640 SCIP_Real variancey;
14641 SCIP_Real countx;
14642 SCIP_Real county;
14643 SCIP_Real tresult;
14644 SCIP_Real realdirection;
14645
14646 if( varx == vary )
14647 return FALSE;
14648
14649 countx = SCIPvarGetPseudocostCount(varx, dir);
14650 county = SCIPvarGetPseudocostCount(vary, dir);
14651
14652 /* if not at least 2 measurements were taken, return FALSE */
14653 if( countx <= 1.9 || county <= 1.9 )
14654 return FALSE;
14655
14656 realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);
14657
14658 meanx = fracx * SCIPvarGetPseudocost(varx, stat, realdirection);
14659 meany = fracy * SCIPvarGetPseudocost(vary, stat, realdirection);
14660
14661 variancex = SQR(fracx) * SCIPvarGetPseudocostVariance(varx, dir, FALSE);
14662 variancey = SQR(fracy) * SCIPvarGetPseudocostVariance(vary, dir, FALSE);
14663
14664 /* if there is no variance, the means are taken from a constant distribution */
14665 if( SCIPsetIsFeasEQ(set, variancex + variancey, 0.0) )
14666 return (onesided ? SCIPsetIsFeasGT(set, meanx, meany) : !SCIPsetIsFeasEQ(set, meanx, meany));
14667
14668 tresult = SCIPcomputeTwoSampleTTestValue(meanx, meany, variancex, variancey, countx, county);
14669
14670 /* for the two-sided hypothesis, just take the absolute of t */
14671 if( !onesided )
14672 tresult = REALABS(tresult);
14673
14674 return (tresult >= SCIPstudentTGetCriticalValue(clevel, (int)(countx + county - 2)));
14675 }
14676
14677 /** tests at a given confidence level whether the variable pseudo-costs only have a small probability to
14678 * exceed a \p threshold. This is useful to determine if past observations provide enough evidence
14679 * to skip an expensive strong-branching step if there is already a candidate that has been proven to yield an improvement
14680 * of at least \p threshold.
14681 *
14682 * @note use \p clevel to adjust the level of confidence. For SCIP_CONFIDENCELEVEL_MIN, the method returns TRUE if
14683 * the estimated probability to exceed \p threshold is less than 25 %.
14684 *
14685 * @see SCIP_Confidencelevel for a list of available levels. The used probability limits refer to the one-sided levels
14686 * of confidence.
14687 *
14688 * @return TRUE if the variable pseudo-cost probabilistic model is likely to be smaller than \p threshold
14689 * at the given confidence level \p clevel.
14690 */
SCIPvarPscostThresholdProbabilityTest(SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_Real frac,SCIP_Real threshold,SCIP_BRANCHDIR dir,SCIP_CONFIDENCELEVEL clevel)14691 SCIP_Bool SCIPvarPscostThresholdProbabilityTest(
14692 SCIP_SET* set, /**< global SCIP settings */
14693 SCIP_STAT* stat, /**< problem statistics */
14694 SCIP_VAR* var, /**< variable x */
14695 SCIP_Real frac, /**< the fractionality of variable x */
14696 SCIP_Real threshold, /**< the threshold to test against */
14697 SCIP_BRANCHDIR dir, /**< branching direction */
14698 SCIP_CONFIDENCELEVEL clevel /**< confidence level for rejecting hypothesis */
14699 )
14700 {
14701 SCIP_Real mean;
14702 SCIP_Real variance;
14703 SCIP_Real count;
14704 SCIP_Real realdirection;
14705 SCIP_Real probability;
14706 SCIP_Real problimit;
14707
14708 count = SCIPvarGetPseudocostCount(var, dir);
14709
14710 /* if not at least 2 measurements were taken, return FALSE */
14711 if( count <= 1.9 )
14712 return FALSE;
14713
14714 realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);
14715
14716 mean = frac * SCIPvarGetPseudocost(var, stat, realdirection);
14717 variance = SQR(frac) * SCIPvarGetPseudocostVariance(var, dir, FALSE);
14718
14719 /* if mean is at least threshold, it has at least a 50% probability to exceed threshold, we therefore return FALSE */
14720 if( SCIPsetIsFeasGE(set, mean, threshold) )
14721 return FALSE;
14722
14723 /* if there is no variance, the means are taken from a constant distribution */
14724 if( SCIPsetIsFeasEQ(set, variance, 0.0) )
14725 return SCIPsetIsFeasLT(set, mean, threshold);
14726
14727 /* obtain probability of a normally distributed random variable at given mean and variance to yield at most threshold */
14728 probability = SCIPnormalCDF(mean, variance, threshold);
14729
14730 /* determine a probability limit corresponding to the given confidence level */
14731 switch( clevel )
14732 {
14733 case SCIP_CONFIDENCELEVEL_MIN:
14734 problimit = 0.75;
14735 break;
14736 case SCIP_CONFIDENCELEVEL_LOW:
14737 problimit = 0.875;
14738 break;
14739 case SCIP_CONFIDENCELEVEL_MEDIUM:
14740 problimit = 0.9;
14741 break;
14742 case SCIP_CONFIDENCELEVEL_HIGH:
14743 problimit = 0.95;
14744 break;
14745 case SCIP_CONFIDENCELEVEL_MAX:
14746 problimit = 0.975;
14747 break;
14748 default:
14749 problimit = -1;
14750 SCIPerrorMessage("Confidence level set to unknown value <%d>", (int)clevel);
14751 SCIPABORT();
14752 break;
14753 }
14754
14755 return (probability >= problimit);
14756 }
14757
14758 /** find the corresponding history entry if already existing, otherwise create new entry */
14759 static
findValuehistoryEntry(SCIP_VAR * var,SCIP_Real value,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_HISTORY ** history)14760 SCIP_RETCODE findValuehistoryEntry(
14761 SCIP_VAR* var, /**< problem variable */
14762 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
14763 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
14764 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
14765 SCIP_HISTORY** history /**< pointer to store the value based history, or NULL */
14766 )
14767 {
14768 assert(var != NULL);
14769 assert(blkmem != NULL);
14770 assert(set != NULL);
14771 assert(history != NULL);
14772
14773 (*history) = NULL;
14774
14775 if( var->valuehistory == NULL )
14776 {
14777 SCIP_CALL( SCIPvaluehistoryCreate(&var->valuehistory, blkmem) );
14778 }
14779
14780 SCIP_CALL( SCIPvaluehistoryFind(var->valuehistory, blkmem, set, value, history) );
14781
14782 return SCIP_OKAY;
14783 }
14784
14785 /** check if value based history should be used */
14786 static
useValuehistory(SCIP_VAR * var,SCIP_Real value,SCIP_SET * set)14787 SCIP_Bool useValuehistory(
14788 SCIP_VAR* var, /**< problem variable */
14789 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
14790 SCIP_SET* set /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
14791 )
14792 {
14793 /* check if the domain value is unknown (not specific) */
14794 if( value == SCIP_UNKNOWN ) /*lint !e777*/
14795 return FALSE;
14796
14797 assert(set != NULL);
14798
14799 /* check if value based history should be collected */
14800 if( !set->history_valuebased )
14801 return FALSE;
14802
14803 /* value based history is not collected for binary variable since the standard history already contains all information */
14804 if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
14805 return FALSE;
14806
14807 /* value based history is not collected for continuous variables */
14808 if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
14809 return FALSE;
14810
14811 return TRUE;
14812 }
14813
14814 /** increases VSIDS of the variable by the given weight */
SCIPvarIncVSIDS(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BRANCHDIR dir,SCIP_Real value,SCIP_Real weight)14815 SCIP_RETCODE SCIPvarIncVSIDS(
14816 SCIP_VAR* var, /**< problem variable */
14817 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
14818 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
14819 SCIP_STAT* stat, /**< problem statistics */
14820 SCIP_BRANCHDIR dir, /**< branching direction */
14821 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
14822 SCIP_Real weight /**< weight of this update in VSIDS */
14823 )
14824 {
14825 assert(var != NULL);
14826 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14827
14828 /* check if history statistics should be collected for a variable */
14829 if( !stat->collectvarhistory )
14830 return SCIP_OKAY;
14831
14832 if( SCIPsetIsZero(set, weight) )
14833 return SCIP_OKAY;
14834
14835 switch( SCIPvarGetStatus(var) )
14836 {
14837 case SCIP_VARSTATUS_ORIGINAL:
14838 if( var->data.original.transvar == NULL )
14839 {
14840 SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
14841 return SCIP_INVALIDDATA;
14842 }
14843 SCIP_CALL( SCIPvarIncVSIDS(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
14844 return SCIP_OKAY;
14845
14846 case SCIP_VARSTATUS_LOOSE:
14847 case SCIP_VARSTATUS_COLUMN:
14848 {
14849 SCIPhistoryIncVSIDS(var->history, dir, weight);
14850 SCIPhistoryIncVSIDS(var->historycrun, dir, weight);
14851
14852 if( useValuehistory(var, value, set) )
14853 {
14854 SCIP_HISTORY* history;
14855
14856 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
14857 assert(history != NULL);
14858
14859 SCIPhistoryIncVSIDS(history, dir, weight);
14860 SCIPsetDebugMsg(set, "variable (<%s> %s %g) + <%g> = <%g>\n", SCIPvarGetName(var), dir == SCIP_BRANCHDIR_UPWARDS ? ">=" : "<=",
14861 value, weight, SCIPhistoryGetVSIDS(history, dir));
14862 }
14863
14864 return SCIP_OKAY;
14865 }
14866 case SCIP_VARSTATUS_FIXED:
14867 SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
14868 return SCIP_INVALIDDATA;
14869
14870 case SCIP_VARSTATUS_AGGREGATED:
14871 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
14872
14873 if( var->data.aggregate.scalar > 0.0 )
14874 {
14875 SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
14876 }
14877 else
14878 {
14879 assert(var->data.aggregate.scalar < 0.0);
14880 SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
14881 }
14882 return SCIP_OKAY;
14883
14884 case SCIP_VARSTATUS_MULTAGGR:
14885 SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
14886 return SCIP_INVALIDDATA;
14887
14888 case SCIP_VARSTATUS_NEGATED:
14889 value = 1.0 - value;
14890
14891 SCIP_CALL( SCIPvarIncVSIDS(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
14892 return SCIP_OKAY;
14893
14894 default:
14895 SCIPerrorMessage("unknown variable status\n");
14896 return SCIP_INVALIDDATA;
14897 }
14898 }
14899
14900 /** scales the VSIDS of the variable by the given scalar */
SCIPvarScaleVSIDS(SCIP_VAR * var,SCIP_Real scalar)14901 SCIP_RETCODE SCIPvarScaleVSIDS(
14902 SCIP_VAR* var, /**< problem variable */
14903 SCIP_Real scalar /**< scalar to multiply the VSIDSs with */
14904 )
14905 {
14906 assert(var != NULL);
14907
14908 switch( SCIPvarGetStatus(var) )
14909 {
14910 case SCIP_VARSTATUS_ORIGINAL:
14911 if( var->data.original.transvar == NULL )
14912 {
14913 SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
14914 return SCIP_INVALIDDATA;
14915 }
14916 SCIP_CALL( SCIPvarScaleVSIDS(var->data.original.transvar, scalar) );
14917 return SCIP_OKAY;
14918
14919 case SCIP_VARSTATUS_LOOSE:
14920 case SCIP_VARSTATUS_COLUMN:
14921 {
14922 SCIPhistoryScaleVSIDS(var->history, scalar);
14923 SCIPhistoryScaleVSIDS(var->historycrun, scalar);
14924 SCIPvaluehistoryScaleVSIDS(var->valuehistory, scalar);
14925
14926 return SCIP_OKAY;
14927 }
14928 case SCIP_VARSTATUS_FIXED:
14929 SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
14930 return SCIP_INVALIDDATA;
14931
14932 case SCIP_VARSTATUS_AGGREGATED:
14933 SCIP_CALL( SCIPvarScaleVSIDS(var->data.aggregate.var, scalar) );
14934 return SCIP_OKAY;
14935
14936 case SCIP_VARSTATUS_MULTAGGR:
14937 SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
14938 return SCIP_INVALIDDATA;
14939
14940 case SCIP_VARSTATUS_NEGATED:
14941 SCIP_CALL( SCIPvarScaleVSIDS(var->negatedvar, scalar) );
14942 return SCIP_OKAY;
14943
14944 default:
14945 SCIPerrorMessage("unknown variable status\n");
14946 return SCIP_INVALIDDATA;
14947 }
14948 }
14949
14950 /** increases the number of active conflicts by one and the overall length of the variable by the given length */
SCIPvarIncNActiveConflicts(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BRANCHDIR dir,SCIP_Real value,SCIP_Real length)14951 SCIP_RETCODE SCIPvarIncNActiveConflicts(
14952 SCIP_VAR* var, /**< problem variable */
14953 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
14954 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
14955 SCIP_STAT* stat, /**< problem statistics */
14956 SCIP_BRANCHDIR dir, /**< branching direction */
14957 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
14958 SCIP_Real length /**< length of the conflict */
14959 )
14960 {
14961 assert(var != NULL);
14962 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
14963
14964 /* check if history statistics should be collected for a variable */
14965 if( !stat->collectvarhistory )
14966 return SCIP_OKAY;
14967
14968 switch( SCIPvarGetStatus(var) )
14969 {
14970 case SCIP_VARSTATUS_ORIGINAL:
14971 if( var->data.original.transvar == NULL )
14972 {
14973 SCIPerrorMessage("cannot update conflict score of original untransformed variable\n");
14974 return SCIP_INVALIDDATA;
14975 }
14976 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.original.transvar, blkmem, set, stat, dir, value, length) );
14977 return SCIP_OKAY;
14978
14979 case SCIP_VARSTATUS_LOOSE:
14980 case SCIP_VARSTATUS_COLUMN:
14981 {
14982 SCIPhistoryIncNActiveConflicts(var->history, dir, length);
14983 SCIPhistoryIncNActiveConflicts(var->historycrun, dir, length);
14984
14985 if( useValuehistory(var, value, set) )
14986 {
14987 SCIP_HISTORY* history;
14988
14989 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
14990 assert(history != NULL);
14991
14992 SCIPhistoryIncNActiveConflicts(history, dir, length);
14993 }
14994
14995 return SCIP_OKAY;
14996 }
14997 case SCIP_VARSTATUS_FIXED:
14998 SCIPerrorMessage("cannot update conflict score of a fixed variable\n");
14999 return SCIP_INVALIDDATA;
15000
15001 case SCIP_VARSTATUS_AGGREGATED:
15002 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15003
15004 if( var->data.aggregate.scalar > 0.0 )
15005 {
15006 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, dir, value, length) );
15007 }
15008 else
15009 {
15010 assert(var->data.aggregate.scalar < 0.0);
15011 SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
15012 }
15013 return SCIP_OKAY;
15014
15015 case SCIP_VARSTATUS_MULTAGGR:
15016 SCIPerrorMessage("cannot update conflict score of a multi-aggregated variable\n");
15017 return SCIP_INVALIDDATA;
15018
15019 case SCIP_VARSTATUS_NEGATED:
15020 value = 1.0 - value;
15021
15022 SCIP_CALL( SCIPvarIncNActiveConflicts(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
15023 return SCIP_OKAY;
15024
15025 default:
15026 SCIPerrorMessage("unknown variable status\n");
15027 return SCIP_INVALIDDATA;
15028 }
15029 }
15030
15031 /** gets the number of active conflicts containing this variable in given direction */
SCIPvarGetNActiveConflicts(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15032 SCIP_Longint SCIPvarGetNActiveConflicts(
15033 SCIP_VAR* var, /**< problem variable */
15034 SCIP_STAT* stat, /**< problem statistics */
15035 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15036 )
15037 {
15038 assert(var != NULL);
15039 assert(stat != NULL);
15040 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15041
15042 switch( SCIPvarGetStatus(var) )
15043 {
15044 case SCIP_VARSTATUS_ORIGINAL:
15045 if( var->data.original.transvar == NULL )
15046 return 0;
15047 else
15048 return SCIPvarGetNActiveConflicts(var->data.original.transvar, stat, dir);
15049
15050 case SCIP_VARSTATUS_LOOSE:
15051 case SCIP_VARSTATUS_COLUMN:
15052 return SCIPhistoryGetNActiveConflicts(var->history, dir);
15053
15054 case SCIP_VARSTATUS_FIXED:
15055 return 0;
15056
15057 case SCIP_VARSTATUS_AGGREGATED:
15058 if( var->data.aggregate.scalar > 0.0 )
15059 return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, dir);
15060 else
15061 return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15062
15063 case SCIP_VARSTATUS_MULTAGGR:
15064 return 0;
15065
15066 case SCIP_VARSTATUS_NEGATED:
15067 return SCIPvarGetNActiveConflicts(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15068
15069 default:
15070 SCIPerrorMessage("unknown variable status\n");
15071 SCIPABORT();
15072 return 0; /*lint !e527*/
15073 }
15074 }
15075
15076 /** gets the number of active conflicts containing this variable in given direction
15077 * in the current run
15078 */
SCIPvarGetNActiveConflictsCurrentRun(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15079 SCIP_Longint SCIPvarGetNActiveConflictsCurrentRun(
15080 SCIP_VAR* var, /**< problem variable */
15081 SCIP_STAT* stat, /**< problem statistics */
15082 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15083 )
15084 {
15085 assert(var != NULL);
15086 assert(stat != NULL);
15087 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15088
15089 switch( SCIPvarGetStatus(var) )
15090 {
15091 case SCIP_VARSTATUS_ORIGINAL:
15092 if( var->data.original.transvar == NULL )
15093 return 0;
15094 else
15095 return SCIPvarGetNActiveConflictsCurrentRun(var->data.original.transvar, stat, dir);
15096
15097 case SCIP_VARSTATUS_LOOSE:
15098 case SCIP_VARSTATUS_COLUMN:
15099 return SCIPhistoryGetNActiveConflicts(var->historycrun, dir);
15100
15101 case SCIP_VARSTATUS_FIXED:
15102 return 0;
15103
15104 case SCIP_VARSTATUS_AGGREGATED:
15105 if( var->data.aggregate.scalar > 0.0 )
15106 return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, dir);
15107 else
15108 return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15109
15110 case SCIP_VARSTATUS_MULTAGGR:
15111 return 0;
15112
15113 case SCIP_VARSTATUS_NEGATED:
15114 return SCIPvarGetNActiveConflictsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15115
15116 default:
15117 SCIPerrorMessage("unknown variable status\n");
15118 SCIPABORT();
15119 return 0; /*lint !e527*/
15120 }
15121 }
15122
15123 /** gets the average conflict length in given direction due to branching on the variable */
SCIPvarGetAvgConflictlength(SCIP_VAR * var,SCIP_BRANCHDIR dir)15124 SCIP_Real SCIPvarGetAvgConflictlength(
15125 SCIP_VAR* var, /**< problem variable */
15126 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15127 )
15128 {
15129 assert(var != NULL);
15130 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15131
15132 switch( SCIPvarGetStatus(var) )
15133 {
15134 case SCIP_VARSTATUS_ORIGINAL:
15135 if( var->data.original.transvar == NULL )
15136 return 0.0;
15137 else
15138 return SCIPvarGetAvgConflictlength(var->data.original.transvar, dir);
15139
15140 case SCIP_VARSTATUS_LOOSE:
15141 case SCIP_VARSTATUS_COLUMN:
15142 return SCIPhistoryGetAvgConflictlength(var->history, dir);
15143 case SCIP_VARSTATUS_FIXED:
15144 return 0.0;
15145
15146 case SCIP_VARSTATUS_AGGREGATED:
15147 if( var->data.aggregate.scalar > 0.0 )
15148 return SCIPvarGetAvgConflictlength(var->data.aggregate.var, dir);
15149 else
15150 return SCIPvarGetAvgConflictlength(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15151
15152 case SCIP_VARSTATUS_MULTAGGR:
15153 return 0.0;
15154
15155 case SCIP_VARSTATUS_NEGATED:
15156 return SCIPvarGetAvgConflictlength(var->negatedvar, SCIPbranchdirOpposite(dir));
15157
15158 default:
15159 SCIPerrorMessage("unknown variable status\n");
15160 SCIPABORT();
15161 return 0.0; /*lint !e527*/
15162 }
15163 }
15164
15165 /** gets the average conflict length in given direction due to branching on the variable
15166 * in the current run
15167 */
SCIPvarGetAvgConflictlengthCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)15168 SCIP_Real SCIPvarGetAvgConflictlengthCurrentRun(
15169 SCIP_VAR* var, /**< problem variable */
15170 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15171 )
15172 {
15173 assert(var != NULL);
15174 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15175
15176 switch( SCIPvarGetStatus(var) )
15177 {
15178 case SCIP_VARSTATUS_ORIGINAL:
15179 if( var->data.original.transvar == NULL )
15180 return 0.0;
15181 else
15182 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.original.transvar, dir);
15183
15184 case SCIP_VARSTATUS_LOOSE:
15185 case SCIP_VARSTATUS_COLUMN:
15186 return SCIPhistoryGetAvgConflictlength(var->historycrun, dir);
15187
15188 case SCIP_VARSTATUS_FIXED:
15189 return 0.0;
15190
15191 case SCIP_VARSTATUS_AGGREGATED:
15192 if( var->data.aggregate.scalar > 0.0 )
15193 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, dir);
15194 else
15195 return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15196
15197 case SCIP_VARSTATUS_MULTAGGR:
15198 return 0.0;
15199
15200 case SCIP_VARSTATUS_NEGATED:
15201 return SCIPvarGetAvgConflictlengthCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
15202
15203 default:
15204 SCIPerrorMessage("unknown variable status\n");
15205 SCIPABORT();
15206 return 0.0; /*lint !e527*/
15207 }
15208 }
15209
15210 /** increases the number of branchings counter of the variable */
SCIPvarIncNBranchings(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BRANCHDIR dir,SCIP_Real value,int depth)15211 SCIP_RETCODE SCIPvarIncNBranchings(
15212 SCIP_VAR* var, /**< problem variable */
15213 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15214 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15215 SCIP_STAT* stat, /**< problem statistics */
15216 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15217 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15218 int depth /**< depth at which the bound change took place */
15219 )
15220 {
15221 assert(var != NULL);
15222 assert(stat != NULL);
15223 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15224
15225 /* check if history statistics should be collected for a variable */
15226 if( !stat->collectvarhistory )
15227 return SCIP_OKAY;
15228
15229 switch( SCIPvarGetStatus(var) )
15230 {
15231 case SCIP_VARSTATUS_ORIGINAL:
15232 if( var->data.original.transvar == NULL )
15233 {
15234 SCIPerrorMessage("cannot update branching counter of original untransformed variable\n");
15235 return SCIP_INVALIDDATA;
15236 }
15237 SCIP_CALL( SCIPvarIncNBranchings(var->data.original.transvar, blkmem, set, stat, dir, value, depth) );
15238 return SCIP_OKAY;
15239
15240 case SCIP_VARSTATUS_LOOSE:
15241 case SCIP_VARSTATUS_COLUMN:
15242 {
15243 SCIPhistoryIncNBranchings(var->history, dir, depth);
15244 SCIPhistoryIncNBranchings(var->historycrun, dir, depth);
15245 SCIPhistoryIncNBranchings(stat->glbhistory, dir, depth);
15246 SCIPhistoryIncNBranchings(stat->glbhistorycrun, dir, depth);
15247
15248 if( useValuehistory(var, value, set) )
15249 {
15250 SCIP_HISTORY* history;
15251
15252 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15253 assert(history != NULL);
15254
15255 SCIPhistoryIncNBranchings(history, dir, depth);
15256 }
15257
15258 return SCIP_OKAY;
15259 }
15260 case SCIP_VARSTATUS_FIXED:
15261 SCIPerrorMessage("cannot update branching counter of a fixed variable\n");
15262 return SCIP_INVALIDDATA;
15263
15264 case SCIP_VARSTATUS_AGGREGATED:
15265 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15266
15267 if( var->data.aggregate.scalar > 0.0 )
15268 {
15269 SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, dir, value, depth) );
15270 }
15271 else
15272 {
15273 assert(var->data.aggregate.scalar < 0.0);
15274 SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
15275 }
15276 return SCIP_OKAY;
15277
15278 case SCIP_VARSTATUS_MULTAGGR:
15279 SCIPerrorMessage("cannot update branching counter of a multi-aggregated variable\n");
15280 return SCIP_INVALIDDATA;
15281
15282 case SCIP_VARSTATUS_NEGATED:
15283 value = 1.0 - value;
15284
15285 SCIP_CALL( SCIPvarIncNBranchings(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
15286 return SCIP_OKAY;
15287
15288 default:
15289 SCIPerrorMessage("unknown variable status\n");
15290 return SCIP_INVALIDDATA;
15291 }
15292 }
15293
15294 /** increases the inference sum of the variable by the given weight */
SCIPvarIncInferenceSum(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BRANCHDIR dir,SCIP_Real value,SCIP_Real weight)15295 SCIP_RETCODE SCIPvarIncInferenceSum(
15296 SCIP_VAR* var, /**< problem variable */
15297 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15298 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15299 SCIP_STAT* stat, /**< problem statistics */
15300 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15301 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15302 SCIP_Real weight /**< weight of this update in inference score */
15303 )
15304 {
15305 assert(var != NULL);
15306 assert(stat != NULL);
15307 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15308
15309 /* check if history statistics should be collected for a variable */
15310 if( !stat->collectvarhistory )
15311 return SCIP_OKAY;
15312
15313 switch( SCIPvarGetStatus(var) )
15314 {
15315 case SCIP_VARSTATUS_ORIGINAL:
15316 if( var->data.original.transvar == NULL )
15317 {
15318 SCIPerrorMessage("cannot update inference counter of original untransformed variable\n");
15319 return SCIP_INVALIDDATA;
15320 }
15321 SCIP_CALL( SCIPvarIncInferenceSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
15322 return SCIP_OKAY;
15323
15324 case SCIP_VARSTATUS_LOOSE:
15325 case SCIP_VARSTATUS_COLUMN:
15326 {
15327 SCIPhistoryIncInferenceSum(var->history, dir, weight);
15328 SCIPhistoryIncInferenceSum(var->historycrun, dir, weight);
15329 SCIPhistoryIncInferenceSum(stat->glbhistory, dir, weight);
15330 SCIPhistoryIncInferenceSum(stat->glbhistorycrun, dir, weight);
15331
15332 if( useValuehistory(var, value, set) )
15333 {
15334 SCIP_HISTORY* history;
15335
15336 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15337 assert(history != NULL);
15338
15339 SCIPhistoryIncInferenceSum(history, dir, weight);
15340 }
15341
15342 return SCIP_OKAY;
15343 }
15344 case SCIP_VARSTATUS_FIXED:
15345 SCIPerrorMessage("cannot update inference counter of a fixed variable\n");
15346 return SCIP_INVALIDDATA;
15347
15348 case SCIP_VARSTATUS_AGGREGATED:
15349 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15350
15351 if( var->data.aggregate.scalar > 0.0 )
15352 {
15353 SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
15354 }
15355 else
15356 {
15357 assert(var->data.aggregate.scalar < 0.0);
15358 SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15359 }
15360 return SCIP_OKAY;
15361
15362 case SCIP_VARSTATUS_MULTAGGR:
15363 SCIPerrorMessage("cannot update inference counter of a multi-aggregated variable\n");
15364 return SCIP_INVALIDDATA;
15365
15366 case SCIP_VARSTATUS_NEGATED:
15367 value = 1.0 - value;
15368
15369 SCIP_CALL( SCIPvarIncInferenceSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15370 return SCIP_OKAY;
15371
15372 default:
15373 SCIPerrorMessage("unknown variable status\n");
15374 return SCIP_INVALIDDATA;
15375 }
15376 }
15377
15378 /** increases the cutoff sum of the variable by the given weight */
SCIPvarIncCutoffSum(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_BRANCHDIR dir,SCIP_Real value,SCIP_Real weight)15379 SCIP_RETCODE SCIPvarIncCutoffSum(
15380 SCIP_VAR* var, /**< problem variable */
15381 BMS_BLKMEM* blkmem, /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
15382 SCIP_SET* set, /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
15383 SCIP_STAT* stat, /**< problem statistics */
15384 SCIP_BRANCHDIR dir, /**< branching direction (downwards, or upwards) */
15385 SCIP_Real value, /**< domain value, or SCIP_UNKNOWN */
15386 SCIP_Real weight /**< weight of this update in cutoff score */
15387 )
15388 {
15389 assert(var != NULL);
15390 assert(stat != NULL);
15391 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15392
15393 /* check if history statistics should be collected for a variable */
15394 if( !stat->collectvarhistory )
15395 return SCIP_OKAY;
15396
15397 switch( SCIPvarGetStatus(var) )
15398 {
15399 case SCIP_VARSTATUS_ORIGINAL:
15400 if( var->data.original.transvar == NULL )
15401 {
15402 SCIPerrorMessage("cannot update cutoff sum of original untransformed variable\n");
15403 return SCIP_INVALIDDATA;
15404 }
15405 SCIP_CALL( SCIPvarIncCutoffSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
15406 return SCIP_OKAY;
15407
15408 case SCIP_VARSTATUS_LOOSE:
15409 case SCIP_VARSTATUS_COLUMN:
15410 {
15411 SCIPhistoryIncCutoffSum(var->history, dir, weight);
15412 SCIPhistoryIncCutoffSum(var->historycrun, dir, weight);
15413 SCIPhistoryIncCutoffSum(stat->glbhistory, dir, weight);
15414 SCIPhistoryIncCutoffSum(stat->glbhistorycrun, dir, weight);
15415
15416 if( useValuehistory(var, value, set) )
15417 {
15418 SCIP_HISTORY* history;
15419
15420 SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
15421 assert(history != NULL);
15422
15423 SCIPhistoryIncCutoffSum(history, dir, weight);
15424 }
15425
15426 return SCIP_OKAY;
15427 }
15428 case SCIP_VARSTATUS_FIXED:
15429 SCIPerrorMessage("cannot update cutoff sum of a fixed variable\n");
15430 return SCIP_INVALIDDATA;
15431
15432 case SCIP_VARSTATUS_AGGREGATED:
15433 value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;
15434
15435 if( var->data.aggregate.scalar > 0.0 )
15436 {
15437 SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
15438 }
15439 else
15440 {
15441 assert(var->data.aggregate.scalar < 0.0);
15442 SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15443 }
15444 return SCIP_OKAY;
15445
15446 case SCIP_VARSTATUS_MULTAGGR:
15447 SCIPerrorMessage("cannot update cutoff sum of a multi-aggregated variable\n");
15448 return SCIP_INVALIDDATA;
15449
15450 case SCIP_VARSTATUS_NEGATED:
15451 value = 1.0 - value;
15452
15453 SCIP_CALL( SCIPvarIncCutoffSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
15454 return SCIP_OKAY;
15455
15456 default:
15457 SCIPerrorMessage("unknown variable status\n");
15458 return SCIP_INVALIDDATA;
15459 }
15460 }
15461
15462 /** returns the number of times, a bound of the variable was changed in given direction due to branching */
SCIPvarGetNBranchings(SCIP_VAR * var,SCIP_BRANCHDIR dir)15463 SCIP_Longint SCIPvarGetNBranchings(
15464 SCIP_VAR* var, /**< problem variable */
15465 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15466 )
15467 {
15468 assert(var != NULL);
15469 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15470
15471 switch( SCIPvarGetStatus(var) )
15472 {
15473 case SCIP_VARSTATUS_ORIGINAL:
15474 if( var->data.original.transvar == NULL )
15475 return 0;
15476 else
15477 return SCIPvarGetNBranchings(var->data.original.transvar, dir);
15478
15479 case SCIP_VARSTATUS_LOOSE:
15480 case SCIP_VARSTATUS_COLUMN:
15481 return SCIPhistoryGetNBranchings(var->history, dir);
15482
15483 case SCIP_VARSTATUS_FIXED:
15484 return 0;
15485
15486 case SCIP_VARSTATUS_AGGREGATED:
15487 if( var->data.aggregate.scalar > 0.0 )
15488 return SCIPvarGetNBranchings(var->data.aggregate.var, dir);
15489 else
15490 return SCIPvarGetNBranchings(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15491
15492 case SCIP_VARSTATUS_MULTAGGR:
15493 return 0;
15494
15495 case SCIP_VARSTATUS_NEGATED:
15496 return SCIPvarGetNBranchings(var->negatedvar, SCIPbranchdirOpposite(dir));
15497
15498 default:
15499 SCIPerrorMessage("unknown variable status\n");
15500 SCIPABORT();
15501 return 0; /*lint !e527*/
15502 }
15503 }
15504
15505 /** returns the number of times, a bound of the variable was changed in given direction due to branching
15506 * in the current run
15507 */
SCIPvarGetNBranchingsCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)15508 SCIP_Longint SCIPvarGetNBranchingsCurrentRun(
15509 SCIP_VAR* var, /**< problem variable */
15510 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15511 )
15512 {
15513 assert(var != NULL);
15514 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15515
15516 switch( SCIPvarGetStatus(var) )
15517 {
15518 case SCIP_VARSTATUS_ORIGINAL:
15519 if( var->data.original.transvar == NULL )
15520 return 0;
15521 else
15522 return SCIPvarGetNBranchingsCurrentRun(var->data.original.transvar, dir);
15523
15524 case SCIP_VARSTATUS_LOOSE:
15525 case SCIP_VARSTATUS_COLUMN:
15526 return SCIPhistoryGetNBranchings(var->historycrun, dir);
15527
15528 case SCIP_VARSTATUS_FIXED:
15529 return 0;
15530
15531 case SCIP_VARSTATUS_AGGREGATED:
15532 if( var->data.aggregate.scalar > 0.0 )
15533 return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, dir);
15534 else
15535 return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15536
15537 case SCIP_VARSTATUS_MULTAGGR:
15538 return 0;
15539
15540 case SCIP_VARSTATUS_NEGATED:
15541 return SCIPvarGetNBranchingsCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
15542
15543 default:
15544 SCIPerrorMessage("unknown variable status\n");
15545 SCIPABORT();
15546 return 0; /*lint !e527*/
15547 }
15548 }
15549
15550 /** returns the average depth of bound changes in given direction due to branching on the variable */
SCIPvarGetAvgBranchdepth(SCIP_VAR * var,SCIP_BRANCHDIR dir)15551 SCIP_Real SCIPvarGetAvgBranchdepth(
15552 SCIP_VAR* var, /**< problem variable */
15553 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15554 )
15555 {
15556 assert(var != NULL);
15557 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15558
15559 switch( SCIPvarGetStatus(var) )
15560 {
15561 case SCIP_VARSTATUS_ORIGINAL:
15562 if( var->data.original.transvar == NULL )
15563 return 0.0;
15564 else
15565 return SCIPvarGetAvgBranchdepth(var->data.original.transvar, dir);
15566
15567 case SCIP_VARSTATUS_LOOSE:
15568 case SCIP_VARSTATUS_COLUMN:
15569 return SCIPhistoryGetAvgBranchdepth(var->history, dir);
15570
15571 case SCIP_VARSTATUS_FIXED:
15572 return 0.0;
15573
15574 case SCIP_VARSTATUS_AGGREGATED:
15575 if( var->data.aggregate.scalar > 0.0 )
15576 return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, dir);
15577 else
15578 return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15579
15580 case SCIP_VARSTATUS_MULTAGGR:
15581 return 0.0;
15582
15583 case SCIP_VARSTATUS_NEGATED:
15584 return SCIPvarGetAvgBranchdepth(var->negatedvar, SCIPbranchdirOpposite(dir));
15585
15586 default:
15587 SCIPerrorMessage("unknown variable status\n");
15588 SCIPABORT();
15589 return 0.0; /*lint !e527*/
15590 }
15591 }
15592
15593 /** returns the average depth of bound changes in given direction due to branching on the variable
15594 * in the current run
15595 */
SCIPvarGetAvgBranchdepthCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)15596 SCIP_Real SCIPvarGetAvgBranchdepthCurrentRun(
15597 SCIP_VAR* var, /**< problem variable */
15598 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15599 )
15600 {
15601 assert(var != NULL);
15602 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15603
15604 switch( SCIPvarGetStatus(var) )
15605 {
15606 case SCIP_VARSTATUS_ORIGINAL:
15607 if( var->data.original.transvar == NULL )
15608 return 0.0;
15609 else
15610 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.original.transvar, dir);
15611
15612 case SCIP_VARSTATUS_LOOSE:
15613 case SCIP_VARSTATUS_COLUMN:
15614 return SCIPhistoryGetAvgBranchdepth(var->historycrun, dir);
15615
15616 case SCIP_VARSTATUS_FIXED:
15617 return 0.0;
15618
15619 case SCIP_VARSTATUS_AGGREGATED:
15620 if( var->data.aggregate.scalar > 0.0 )
15621 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var, dir);
15622 else
15623 return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var,
15624 dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
15625
15626 case SCIP_VARSTATUS_MULTAGGR:
15627 return 0.0;
15628
15629 case SCIP_VARSTATUS_NEGATED:
15630 return SCIPvarGetAvgBranchdepthCurrentRun(var->negatedvar,
15631 dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);
15632
15633 default:
15634 SCIPerrorMessage("unknown variable status\n");
15635 SCIPABORT();
15636 return 0.0; /*lint !e527*/
15637 }
15638 }
15639
15640 /** returns the variable's VSIDS score */
SCIPvarGetVSIDS_rec(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15641 SCIP_Real SCIPvarGetVSIDS_rec(
15642 SCIP_VAR* var, /**< problem variable */
15643 SCIP_STAT* stat, /**< problem statistics */
15644 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15645 )
15646 {
15647 assert(var != NULL);
15648 assert(stat != NULL);
15649 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15650
15651 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
15652 return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);
15653
15654 switch( SCIPvarGetStatus(var) )
15655 {
15656 case SCIP_VARSTATUS_ORIGINAL:
15657 if( var->data.original.transvar == NULL )
15658 return 0.0;
15659 else
15660 return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);
15661
15662 case SCIP_VARSTATUS_LOOSE:
15663 case SCIP_VARSTATUS_COLUMN:
15664 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE); /* column case already handled in if condition above */
15665 return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;
15666
15667 case SCIP_VARSTATUS_FIXED:
15668 return 0.0;
15669
15670 case SCIP_VARSTATUS_AGGREGATED:
15671 if( var->data.aggregate.scalar > 0.0 )
15672 return SCIPvarGetVSIDS(var->data.aggregate.var, stat, dir);
15673 else
15674 /* coverity[overrun-local] */
15675 return SCIPvarGetVSIDS(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15676
15677 case SCIP_VARSTATUS_MULTAGGR:
15678 return 0.0;
15679
15680 case SCIP_VARSTATUS_NEGATED:
15681 /* coverity[overrun-local] */
15682 return SCIPvarGetVSIDS(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15683
15684 default:
15685 SCIPerrorMessage("unknown variable status\n");
15686 SCIPABORT();
15687 return 0.0; /*lint !e527*/
15688 }
15689 }
15690
15691 /** returns the variable's VSIDS score only using conflicts of the current run */
SCIPvarGetVSIDSCurrentRun(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15692 SCIP_Real SCIPvarGetVSIDSCurrentRun(
15693 SCIP_VAR* var, /**< problem variable */
15694 SCIP_STAT* stat, /**< problem statistics */
15695 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15696 )
15697 {
15698 assert(var != NULL);
15699 assert(stat != NULL);
15700 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15701
15702 if( dir != SCIP_BRANCHDIR_DOWNWARDS && dir != SCIP_BRANCHDIR_UPWARDS )
15703 {
15704 SCIPerrorMessage("invalid branching direction %d when asking for VSIDS value\n", dir);
15705 return SCIP_INVALID;
15706 }
15707
15708 switch( SCIPvarGetStatus(var) )
15709 {
15710 case SCIP_VARSTATUS_ORIGINAL:
15711 if( var->data.original.transvar == NULL )
15712 return 0.0;
15713 else
15714 return SCIPvarGetVSIDSCurrentRun(var->data.original.transvar, stat, dir);
15715
15716 case SCIP_VARSTATUS_LOOSE:
15717 case SCIP_VARSTATUS_COLUMN:
15718 return SCIPhistoryGetVSIDS(var->historycrun, dir)/stat->vsidsweight;
15719
15720 case SCIP_VARSTATUS_FIXED:
15721 return 0.0;
15722
15723 case SCIP_VARSTATUS_AGGREGATED:
15724 if( var->data.aggregate.scalar > 0.0 )
15725 return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, dir);
15726 else
15727 return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15728
15729 case SCIP_VARSTATUS_MULTAGGR:
15730 return 0.0;
15731
15732 case SCIP_VARSTATUS_NEGATED:
15733 return SCIPvarGetVSIDSCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15734
15735 default:
15736 SCIPerrorMessage("unknown variable status\n");
15737 SCIPABORT();
15738 return 0.0; /*lint !e527*/
15739 }
15740 }
15741
15742 /** returns the number of inferences branching on this variable in given direction triggered */
SCIPvarGetInferenceSum(SCIP_VAR * var,SCIP_BRANCHDIR dir)15743 SCIP_Real SCIPvarGetInferenceSum(
15744 SCIP_VAR* var, /**< problem variable */
15745 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15746 )
15747 {
15748 assert(var != NULL);
15749 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15750
15751 switch( SCIPvarGetStatus(var) )
15752 {
15753 case SCIP_VARSTATUS_ORIGINAL:
15754 if( var->data.original.transvar == NULL )
15755 return 0.0;
15756 else
15757 return SCIPvarGetInferenceSum(var->data.original.transvar, dir);
15758
15759 case SCIP_VARSTATUS_LOOSE:
15760 case SCIP_VARSTATUS_COLUMN:
15761 return SCIPhistoryGetInferenceSum(var->history, dir);
15762
15763 case SCIP_VARSTATUS_FIXED:
15764 return 0.0;
15765
15766 case SCIP_VARSTATUS_AGGREGATED:
15767 if( var->data.aggregate.scalar > 0.0 )
15768 return SCIPvarGetInferenceSum(var->data.aggregate.var, dir);
15769 else
15770 return SCIPvarGetInferenceSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15771
15772 case SCIP_VARSTATUS_MULTAGGR:
15773 return 0.0;
15774
15775 case SCIP_VARSTATUS_NEGATED:
15776 return SCIPvarGetInferenceSum(var->negatedvar, SCIPbranchdirOpposite(dir));
15777
15778 default:
15779 SCIPerrorMessage("unknown variable status\n");
15780 SCIPABORT();
15781 return 0.0; /*lint !e527*/
15782 }
15783 }
15784
15785 /** returns the number of inferences branching on this variable in given direction triggered
15786 * in the current run
15787 */
SCIPvarGetInferenceSumCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)15788 SCIP_Real SCIPvarGetInferenceSumCurrentRun(
15789 SCIP_VAR* var, /**< problem variable */
15790 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15791 )
15792 {
15793 assert(var != NULL);
15794 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15795
15796 switch( SCIPvarGetStatus(var) )
15797 {
15798 case SCIP_VARSTATUS_ORIGINAL:
15799 if( var->data.original.transvar == NULL )
15800 return 0.0;
15801 else
15802 return SCIPvarGetInferenceSumCurrentRun(var->data.original.transvar, dir);
15803
15804 case SCIP_VARSTATUS_LOOSE:
15805 case SCIP_VARSTATUS_COLUMN:
15806 return SCIPhistoryGetInferenceSum(var->historycrun, dir);
15807
15808 case SCIP_VARSTATUS_FIXED:
15809 return 0.0;
15810
15811 case SCIP_VARSTATUS_AGGREGATED:
15812 if( var->data.aggregate.scalar > 0.0 )
15813 return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, dir);
15814 else
15815 return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15816
15817 case SCIP_VARSTATUS_MULTAGGR:
15818 return 0.0;
15819
15820 case SCIP_VARSTATUS_NEGATED:
15821 return SCIPvarGetInferenceSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
15822
15823 default:
15824 SCIPerrorMessage("unknown variable status\n");
15825 SCIPABORT();
15826 return 0.0; /*lint !e527*/
15827 }
15828 }
15829
15830 /** returns the average number of inferences found after branching on the variable in given direction */
SCIPvarGetAvgInferences(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15831 SCIP_Real SCIPvarGetAvgInferences(
15832 SCIP_VAR* var, /**< problem variable */
15833 SCIP_STAT* stat, /**< problem statistics */
15834 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15835 )
15836 {
15837 assert(var != NULL);
15838 assert(stat != NULL);
15839 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15840
15841 switch( SCIPvarGetStatus(var) )
15842 {
15843 case SCIP_VARSTATUS_ORIGINAL:
15844 if( var->data.original.transvar == NULL )
15845 return SCIPhistoryGetAvgInferences(stat->glbhistory, dir);
15846 else
15847 return SCIPvarGetAvgInferences(var->data.original.transvar, stat, dir);
15848
15849 case SCIP_VARSTATUS_LOOSE:
15850 case SCIP_VARSTATUS_COLUMN:
15851 if( SCIPhistoryGetNBranchings(var->history, dir) > 0 )
15852 return SCIPhistoryGetAvgInferences(var->history, dir);
15853 else
15854 {
15855 int nimpls;
15856 int ncliques;
15857
15858 nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
15859 ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
15860 return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistory, dir); /*lint !e790*/
15861 }
15862
15863 case SCIP_VARSTATUS_FIXED:
15864 return 0.0;
15865
15866 case SCIP_VARSTATUS_AGGREGATED:
15867 if( var->data.aggregate.scalar > 0.0 )
15868 return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, dir);
15869 else
15870 return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15871
15872 case SCIP_VARSTATUS_MULTAGGR:
15873 return 0.0;
15874
15875 case SCIP_VARSTATUS_NEGATED:
15876 return SCIPvarGetAvgInferences(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15877
15878 default:
15879 SCIPerrorMessage("unknown variable status\n");
15880 SCIPABORT();
15881 return 0.0; /*lint !e527*/
15882 }
15883 }
15884
15885 /** returns the average number of inferences found after branching on the variable in given direction
15886 * in the current run
15887 */
SCIPvarGetAvgInferencesCurrentRun(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)15888 SCIP_Real SCIPvarGetAvgInferencesCurrentRun(
15889 SCIP_VAR* var, /**< problem variable */
15890 SCIP_STAT* stat, /**< problem statistics */
15891 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15892 )
15893 {
15894 assert(var != NULL);
15895 assert(stat != NULL);
15896 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15897
15898 switch( SCIPvarGetStatus(var) )
15899 {
15900 case SCIP_VARSTATUS_ORIGINAL:
15901 if( var->data.original.transvar == NULL )
15902 return SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir);
15903 else
15904 return SCIPvarGetAvgInferencesCurrentRun(var->data.original.transvar, stat, dir);
15905
15906 case SCIP_VARSTATUS_LOOSE:
15907 case SCIP_VARSTATUS_COLUMN:
15908 if( SCIPhistoryGetNBranchings(var->historycrun, dir) > 0 )
15909 return SCIPhistoryGetAvgInferences(var->historycrun, dir);
15910 else
15911 {
15912 int nimpls;
15913 int ncliques;
15914
15915 nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
15916 ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
15917 return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir); /*lint !e790*/
15918 }
15919
15920 case SCIP_VARSTATUS_FIXED:
15921 return 0.0;
15922
15923 case SCIP_VARSTATUS_AGGREGATED:
15924 if( var->data.aggregate.scalar > 0.0 )
15925 return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, dir);
15926 else
15927 return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
15928
15929 case SCIP_VARSTATUS_MULTAGGR:
15930 return 0.0;
15931
15932 case SCIP_VARSTATUS_NEGATED:
15933 return SCIPvarGetAvgInferencesCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
15934
15935 default:
15936 SCIPerrorMessage("unknown variable status\n");
15937 SCIPABORT();
15938 return 0.0; /*lint !e527*/
15939 }
15940 }
15941
15942 /** returns the number of cutoffs branching on this variable in given direction produced */
SCIPvarGetCutoffSum(SCIP_VAR * var,SCIP_BRANCHDIR dir)15943 SCIP_Real SCIPvarGetCutoffSum(
15944 SCIP_VAR* var, /**< problem variable */
15945 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15946 )
15947 {
15948 assert(var != NULL);
15949 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15950
15951 switch( SCIPvarGetStatus(var) )
15952 {
15953 case SCIP_VARSTATUS_ORIGINAL:
15954 if( var->data.original.transvar == NULL )
15955 return 0;
15956 else
15957 return SCIPvarGetCutoffSum(var->data.original.transvar, dir);
15958
15959 case SCIP_VARSTATUS_LOOSE:
15960 case SCIP_VARSTATUS_COLUMN:
15961 return SCIPhistoryGetCutoffSum(var->history, dir);
15962
15963 case SCIP_VARSTATUS_FIXED:
15964 return 0;
15965
15966 case SCIP_VARSTATUS_AGGREGATED:
15967 if( var->data.aggregate.scalar > 0.0 )
15968 return SCIPvarGetCutoffSum(var->data.aggregate.var, dir);
15969 else
15970 return SCIPvarGetCutoffSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
15971
15972 case SCIP_VARSTATUS_MULTAGGR:
15973 return 0;
15974
15975 case SCIP_VARSTATUS_NEGATED:
15976 return SCIPvarGetCutoffSum(var->negatedvar, SCIPbranchdirOpposite(dir));
15977
15978 default:
15979 SCIPerrorMessage("unknown variable status\n");
15980 SCIPABORT();
15981 return 0; /*lint !e527*/
15982 }
15983 }
15984
15985 /** returns the number of cutoffs branching on this variable in given direction produced in the current run */
SCIPvarGetCutoffSumCurrentRun(SCIP_VAR * var,SCIP_BRANCHDIR dir)15986 SCIP_Real SCIPvarGetCutoffSumCurrentRun(
15987 SCIP_VAR* var, /**< problem variable */
15988 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
15989 )
15990 {
15991 assert(var != NULL);
15992 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
15993
15994 switch( SCIPvarGetStatus(var) )
15995 {
15996 case SCIP_VARSTATUS_ORIGINAL:
15997 if( var->data.original.transvar == NULL )
15998 return 0;
15999 else
16000 return SCIPvarGetCutoffSumCurrentRun(var->data.original.transvar, dir);
16001
16002 case SCIP_VARSTATUS_LOOSE:
16003 case SCIP_VARSTATUS_COLUMN:
16004 return SCIPhistoryGetCutoffSum(var->historycrun, dir);
16005
16006 case SCIP_VARSTATUS_FIXED:
16007 return 0;
16008
16009 case SCIP_VARSTATUS_AGGREGATED:
16010 if( var->data.aggregate.scalar > 0.0 )
16011 return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, dir);
16012 else
16013 return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));
16014
16015 case SCIP_VARSTATUS_MULTAGGR:
16016 return 0;
16017
16018 case SCIP_VARSTATUS_NEGATED:
16019 return SCIPvarGetCutoffSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));
16020
16021 default:
16022 SCIPerrorMessage("unknown variable status\n");
16023 SCIPABORT();
16024 return 0; /*lint !e527*/
16025 }
16026 }
16027
16028 /** returns the average number of cutoffs found after branching on the variable in given direction */
SCIPvarGetAvgCutoffs(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)16029 SCIP_Real SCIPvarGetAvgCutoffs(
16030 SCIP_VAR* var, /**< problem variable */
16031 SCIP_STAT* stat, /**< problem statistics */
16032 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16033 )
16034 {
16035 assert(var != NULL);
16036 assert(stat != NULL);
16037 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16038
16039 switch( SCIPvarGetStatus(var) )
16040 {
16041 case SCIP_VARSTATUS_ORIGINAL:
16042 if( var->data.original.transvar == NULL )
16043 return SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);
16044 else
16045 return SCIPvarGetAvgCutoffs(var->data.original.transvar, stat, dir);
16046
16047 case SCIP_VARSTATUS_LOOSE:
16048 case SCIP_VARSTATUS_COLUMN:
16049 return SCIPhistoryGetNBranchings(var->history, dir) > 0
16050 ? SCIPhistoryGetAvgCutoffs(var->history, dir)
16051 : SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);
16052
16053 case SCIP_VARSTATUS_FIXED:
16054 return 0.0;
16055
16056 case SCIP_VARSTATUS_AGGREGATED:
16057 if( var->data.aggregate.scalar > 0.0 )
16058 return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, dir);
16059 else
16060 return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16061
16062 case SCIP_VARSTATUS_MULTAGGR:
16063 return 0.0;
16064
16065 case SCIP_VARSTATUS_NEGATED:
16066 return SCIPvarGetAvgCutoffs(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16067
16068 default:
16069 SCIPerrorMessage("unknown variable status\n");
16070 SCIPABORT();
16071 return 0.0; /*lint !e527*/
16072 }
16073 }
16074
16075 /** returns the average number of cutoffs found after branching on the variable in given direction in the current run */
SCIPvarGetAvgCutoffsCurrentRun(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)16076 SCIP_Real SCIPvarGetAvgCutoffsCurrentRun(
16077 SCIP_VAR* var, /**< problem variable */
16078 SCIP_STAT* stat, /**< problem statistics */
16079 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
16080 )
16081 {
16082 assert(var != NULL);
16083 assert(stat != NULL);
16084 assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);
16085
16086 switch( SCIPvarGetStatus(var) )
16087 {
16088 case SCIP_VARSTATUS_ORIGINAL:
16089 if( var->data.original.transvar == NULL )
16090 return SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);
16091 else
16092 return SCIPvarGetAvgCutoffsCurrentRun(var->data.original.transvar, stat, dir);
16093
16094 case SCIP_VARSTATUS_LOOSE:
16095 case SCIP_VARSTATUS_COLUMN:
16096 return SCIPhistoryGetNBranchings(var->historycrun, dir) > 0
16097 ? SCIPhistoryGetAvgCutoffs(var->historycrun, dir)
16098 : SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);
16099
16100 case SCIP_VARSTATUS_FIXED:
16101 return 0.0;
16102
16103 case SCIP_VARSTATUS_AGGREGATED:
16104 if( var->data.aggregate.scalar > 0.0 )
16105 return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, dir);
16106 else
16107 return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));
16108
16109 case SCIP_VARSTATUS_MULTAGGR:
16110 return 0.0;
16111
16112 case SCIP_VARSTATUS_NEGATED:
16113 return SCIPvarGetAvgCutoffsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));
16114
16115 default:
16116 SCIPerrorMessage("unknown variable status\n");
16117 SCIPABORT();
16118 return 0.0; /*lint !e527*/
16119 }
16120 }
16121
16122
16123
16124
16125 /*
16126 * information methods for bound changes
16127 */
16128
16129 /** creates an artificial bound change information object with depth = INT_MAX and pos = -1 */
SCIPbdchginfoCreate(SCIP_BDCHGINFO ** bdchginfo,BMS_BLKMEM * blkmem,SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_Real oldbound,SCIP_Real newbound)16130 SCIP_RETCODE SCIPbdchginfoCreate(
16131 SCIP_BDCHGINFO** bdchginfo, /**< pointer to store bound change information */
16132 BMS_BLKMEM* blkmem, /**< block memory */
16133 SCIP_VAR* var, /**< active variable that changed the bounds */
16134 SCIP_BOUNDTYPE boundtype, /**< type of bound for var: lower or upper bound */
16135 SCIP_Real oldbound, /**< old value for bound */
16136 SCIP_Real newbound /**< new value for bound */
16137 )
16138 {
16139 assert(bdchginfo != NULL);
16140
16141 SCIP_ALLOC( BMSallocBlockMemory(blkmem, bdchginfo) );
16142 (*bdchginfo)->oldbound = oldbound;
16143 (*bdchginfo)->newbound = newbound;
16144 (*bdchginfo)->var = var;
16145 (*bdchginfo)->inferencedata.var = var;
16146 (*bdchginfo)->inferencedata.reason.prop = NULL;
16147 (*bdchginfo)->inferencedata.info = 0;
16148 (*bdchginfo)->bdchgidx.depth = INT_MAX;
16149 (*bdchginfo)->bdchgidx.pos = -1;
16150 (*bdchginfo)->pos = 0;
16151 (*bdchginfo)->boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
16152 (*bdchginfo)->boundtype = boundtype; /*lint !e641*/
16153 (*bdchginfo)->inferboundtype = boundtype; /*lint !e641*/
16154 (*bdchginfo)->redundant = FALSE;
16155
16156 return SCIP_OKAY;
16157 }
16158
16159 /** frees a bound change information object */
SCIPbdchginfoFree(SCIP_BDCHGINFO ** bdchginfo,BMS_BLKMEM * blkmem)16160 void SCIPbdchginfoFree(
16161 SCIP_BDCHGINFO** bdchginfo, /**< pointer to store bound change information */
16162 BMS_BLKMEM* blkmem /**< block memory */
16163 )
16164 {
16165 assert(bdchginfo != NULL);
16166
16167 BMSfreeBlockMemory(blkmem, bdchginfo);
16168 }
16169
16170 /** returns the bound change information for the last lower bound change on given active problem variable before or
16171 * after the bound change with the given index was applied;
16172 * returns NULL, if no change to the lower bound was applied up to this point of time
16173 */
SCIPvarGetLbchgInfo(SCIP_VAR * var,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16174 SCIP_BDCHGINFO* SCIPvarGetLbchgInfo(
16175 SCIP_VAR* var, /**< active problem variable */
16176 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16177 SCIP_Bool after /**< should the bound change with given index be included? */
16178 )
16179 {
16180 int i;
16181
16182 assert(var != NULL);
16183 assert(SCIPvarIsActive(var));
16184
16185 /* search the correct bound change information for the given bound change index */
16186 if( after )
16187 {
16188 for( i = var->nlbchginfos-1; i >= 0; --i )
16189 {
16190 assert(var->lbchginfos[i].var == var);
16191 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
16192 assert(var->lbchginfos[i].pos == i);
16193
16194 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16195 if( var->lbchginfos[i].redundant )
16196 return NULL;
16197 assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);
16198
16199 /* if we reached the bound change index, return the current bound change info */
16200 if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->lbchginfos[i].bdchgidx) )
16201 return &var->lbchginfos[i];
16202 }
16203 }
16204 else
16205 {
16206 for( i = var->nlbchginfos-1; i >= 0; --i )
16207 {
16208 assert(var->lbchginfos[i].var == var);
16209 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
16210 assert(var->lbchginfos[i].pos == i);
16211
16212 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16213 if( var->lbchginfos[i].redundant )
16214 return NULL;
16215 assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);
16216
16217 /* if we reached the bound change index, return the current bound change info */
16218 if( SCIPbdchgidxIsEarlier(&var->lbchginfos[i].bdchgidx, bdchgidx) )
16219 return &var->lbchginfos[i];
16220 }
16221 }
16222
16223 return NULL;
16224 }
16225
16226 /** returns the bound change information for the last upper bound change on given active problem variable before or
16227 * after the bound change with the given index was applied;
16228 * returns NULL, if no change to the upper bound was applied up to this point of time
16229 */
SCIPvarGetUbchgInfo(SCIP_VAR * var,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16230 SCIP_BDCHGINFO* SCIPvarGetUbchgInfo(
16231 SCIP_VAR* var, /**< active problem variable */
16232 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16233 SCIP_Bool after /**< should the bound change with given index be included? */
16234 )
16235 {
16236 int i;
16237
16238 assert(var != NULL);
16239 assert(SCIPvarIsActive(var));
16240
16241 /* search the correct bound change information for the given bound change index */
16242 if( after )
16243 {
16244 for( i = var->nubchginfos-1; i >= 0; --i )
16245 {
16246 assert(var->ubchginfos[i].var == var);
16247 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
16248 assert(var->ubchginfos[i].pos == i);
16249
16250 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16251 if( var->ubchginfos[i].redundant )
16252 return NULL;
16253 assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);
16254
16255 /* if we reached the bound change index, return the current bound change info */
16256 if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->ubchginfos[i].bdchgidx) )
16257 return &var->ubchginfos[i];
16258 }
16259 }
16260 else
16261 {
16262 for( i = var->nubchginfos-1; i >= 0; --i )
16263 {
16264 assert(var->ubchginfos[i].var == var);
16265 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
16266 assert(var->ubchginfos[i].pos == i);
16267
16268 /* if we reached the (due to global bounds) redundant bound changes, return NULL */
16269 if( var->ubchginfos[i].redundant )
16270 return NULL;
16271 assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);
16272
16273 /* if we reached the bound change index, return the current bound change info */
16274 if( SCIPbdchgidxIsEarlier(&var->ubchginfos[i].bdchgidx, bdchgidx) )
16275 return &var->ubchginfos[i];
16276 }
16277 }
16278
16279 return NULL;
16280 }
16281
16282 /** returns the bound change information for the last lower or upper bound change on given active problem variable
16283 * before or after the bound change with the given index was applied;
16284 * returns NULL, if no change to the lower/upper bound was applied up to this point of time
16285 */
SCIPvarGetBdchgInfo(SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16286 SCIP_BDCHGINFO* SCIPvarGetBdchgInfo(
16287 SCIP_VAR* var, /**< active problem variable */
16288 SCIP_BOUNDTYPE boundtype, /**< type of bound: lower or upper bound */
16289 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16290 SCIP_Bool after /**< should the bound change with given index be included? */
16291 )
16292 {
16293 if( boundtype == SCIP_BOUNDTYPE_LOWER )
16294 return SCIPvarGetLbchgInfo(var, bdchgidx, after);
16295 else
16296 {
16297 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
16298 return SCIPvarGetUbchgInfo(var, bdchgidx, after);
16299 }
16300 }
16301
16302 /** returns lower bound of variable directly before or after the bound change given by the bound change index
16303 * was applied
16304 *
16305 * @deprecated Please use SCIPgetVarLbAtIndex()
16306 */
SCIPvarGetLbAtIndex(SCIP_VAR * var,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16307 SCIP_Real SCIPvarGetLbAtIndex(
16308 SCIP_VAR* var, /**< problem variable */
16309 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16310 SCIP_Bool after /**< should the bound change with given index be included? */
16311 )
16312 {
16313 SCIP_VARSTATUS varstatus;
16314 assert(var != NULL);
16315
16316 varstatus = SCIPvarGetStatus(var);
16317
16318 /* get bounds of attached variables */
16319 switch( varstatus )
16320 {
16321 case SCIP_VARSTATUS_ORIGINAL:
16322 assert(var->data.original.transvar != NULL);
16323 return SCIPvarGetLbAtIndex(var->data.original.transvar, bdchgidx, after);
16324
16325 case SCIP_VARSTATUS_LOOSE:
16326 case SCIP_VARSTATUS_COLUMN:
16327 if( bdchgidx == NULL )
16328 return SCIPvarGetLbLocal(var);
16329 else
16330 {
16331 SCIP_BDCHGINFO* bdchginfo;
16332
16333 bdchginfo = SCIPvarGetLbchgInfo(var, bdchgidx, after);
16334 if( bdchginfo != NULL )
16335 return SCIPbdchginfoGetNewbound(bdchginfo);
16336 else
16337 return var->glbdom.lb;
16338 }
16339 case SCIP_VARSTATUS_FIXED:
16340 return var->glbdom.lb;
16341
16342 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
16343 assert(var->data.aggregate.var != NULL);
16344 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
16345 * corresponding infinity value instead of performing an arithmetical transformation (compare method
16346 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
16347 * (or is called by) a public interface method; instead, we only assert that values are finite
16348 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
16349 * positives and negatives if the parameter <numerics/infinity> is modified by the user
16350 */
16351 if( var->data.aggregate.scalar > 0.0 )
16352 {
16353 /* a > 0 -> get lower bound of y */
16354 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16355 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16356 return var->data.aggregate.scalar * SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after)
16357 + var->data.aggregate.constant;
16358 }
16359 else if( var->data.aggregate.scalar < 0.0 )
16360 {
16361 /* a < 0 -> get upper bound of y */
16362 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16363 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16364 return var->data.aggregate.scalar * SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after)
16365 + var->data.aggregate.constant;
16366 }
16367 else
16368 {
16369 SCIPerrorMessage("scalar is zero in aggregation\n");
16370 SCIPABORT();
16371 return SCIP_INVALID; /*lint !e527*/
16372 }
16373
16374 case SCIP_VARSTATUS_MULTAGGR:
16375 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
16376 if ( var->data.multaggr.nvars == 1 )
16377 {
16378 assert(var->data.multaggr.vars != NULL);
16379 assert(var->data.multaggr.scalars != NULL);
16380 assert(var->data.multaggr.vars[0] != NULL);
16381
16382 if( var->data.multaggr.scalars[0] > 0.0 )
16383 {
16384 /* a > 0 -> get lower bound of y */
16385 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16386 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16387 return var->data.multaggr.scalars[0] * SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16388 + var->data.multaggr.constant;
16389 }
16390 else if( var->data.multaggr.scalars[0] < 0.0 )
16391 {
16392 /* a < 0 -> get upper bound of y */
16393 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16394 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16395 return var->data.multaggr.scalars[0] * SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16396 + var->data.multaggr.constant;
16397 }
16398 else
16399 {
16400 SCIPerrorMessage("scalar is zero in multi-aggregation\n");
16401 SCIPABORT();
16402 return SCIP_INVALID; /*lint !e527*/
16403 }
16404 }
16405 SCIPerrorMessage("cannot get the bounds of a multi-aggregated variable.\n");
16406 SCIPABORT();
16407 return SCIP_INVALID; /*lint !e527*/
16408
16409 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
16410 assert(var->negatedvar != NULL);
16411 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
16412 assert(var->negatedvar->negatedvar == var);
16413 return var->data.negate.constant - SCIPvarGetUbAtIndex(var->negatedvar, bdchgidx, after);
16414 default:
16415 SCIPerrorMessage("unknown variable status\n");
16416 SCIPABORT();
16417 return SCIP_INVALID; /*lint !e527*/
16418 }
16419 }
16420
16421 /** returns upper bound of variable directly before or after the bound change given by the bound change index
16422 * was applied
16423 *
16424 * @deprecated Please use SCIPgetVarUbAtIndex()
16425 */
SCIPvarGetUbAtIndex(SCIP_VAR * var,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16426 SCIP_Real SCIPvarGetUbAtIndex(
16427 SCIP_VAR* var, /**< problem variable */
16428 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16429 SCIP_Bool after /**< should the bound change with given index be included? */
16430 )
16431 {
16432 SCIP_VARSTATUS varstatus;
16433 assert(var != NULL);
16434
16435 varstatus = SCIPvarGetStatus(var);
16436
16437 /* get bounds of attached variables */
16438 switch( varstatus )
16439 {
16440 case SCIP_VARSTATUS_ORIGINAL:
16441 assert(var->data.original.transvar != NULL);
16442 return SCIPvarGetUbAtIndex(var->data.original.transvar, bdchgidx, after);
16443
16444 case SCIP_VARSTATUS_COLUMN:
16445 case SCIP_VARSTATUS_LOOSE:
16446 if( bdchgidx == NULL )
16447 return SCIPvarGetUbLocal(var);
16448 else
16449 {
16450 SCIP_BDCHGINFO* bdchginfo;
16451
16452 bdchginfo = SCIPvarGetUbchgInfo(var, bdchgidx, after);
16453 if( bdchginfo != NULL )
16454 return SCIPbdchginfoGetNewbound(bdchginfo);
16455 else
16456 return var->glbdom.ub;
16457 }
16458
16459 case SCIP_VARSTATUS_FIXED:
16460 return var->glbdom.ub;
16461
16462 case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c -> y = (x-c)/a */
16463 assert(var->data.aggregate.var != NULL);
16464 /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
16465 * corresponding infinity value instead of performing an arithmetical transformation (compare method
16466 * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
16467 * (or is called by) a public interface method; instead, we only assert that values are finite
16468 * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
16469 * positives and negatives if the parameter <numerics/infinity> is modified by the user
16470 */
16471 if( var->data.aggregate.scalar > 0.0 )
16472 {
16473 /* a > 0 -> get lower bound of y */
16474 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16475 assert(SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16476 return var->data.aggregate.scalar * SCIPvarGetUbAtIndex(var->data.aggregate.var, bdchgidx, after)
16477 + var->data.aggregate.constant;
16478 }
16479 else if( var->data.aggregate.scalar < 0.0 )
16480 {
16481 /* a < 0 -> get upper bound of y */
16482 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16483 assert(SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16484 return var->data.aggregate.scalar * SCIPvarGetLbAtIndex(var->data.aggregate.var, bdchgidx, after)
16485 + var->data.aggregate.constant;
16486 }
16487 else
16488 {
16489 SCIPerrorMessage("scalar is zero in aggregation\n");
16490 SCIPABORT();
16491 return SCIP_INVALID; /*lint !e527*/
16492 }
16493
16494 case SCIP_VARSTATUS_MULTAGGR:
16495 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
16496 if ( var->data.multaggr.nvars == 1 )
16497 {
16498 assert(var->data.multaggr.vars != NULL);
16499 assert(var->data.multaggr.scalars != NULL);
16500 assert(var->data.multaggr.vars[0] != NULL);
16501
16502 if( var->data.multaggr.scalars[0] > 0.0 )
16503 {
16504 /* a > 0 -> get lower bound of y */
16505 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16506 assert(SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16507 return var->data.multaggr.scalars[0] * SCIPvarGetUbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16508 + var->data.multaggr.constant;
16509 }
16510 else if( var->data.multaggr.scalars[0] < 0.0 )
16511 {
16512 /* a < 0 -> get upper bound of y */
16513 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) > -SCIP_DEFAULT_INFINITY);
16514 assert(SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after) < +SCIP_DEFAULT_INFINITY);
16515 return var->data.multaggr.scalars[0] * SCIPvarGetLbAtIndex(var->data.multaggr.vars[0], bdchgidx, after)
16516 + var->data.multaggr.constant;
16517 }
16518 else
16519 {
16520 SCIPerrorMessage("scalar is zero in multi-aggregation\n");
16521 SCIPABORT();
16522 return SCIP_INVALID; /*lint !e527*/
16523 }
16524 }
16525 SCIPerrorMessage("cannot get the bounds of a multiple aggregated variable.\n");
16526 SCIPABORT();
16527 return SCIP_INVALID; /*lint !e527*/
16528
16529 case SCIP_VARSTATUS_NEGATED: /* x' = offset - x -> x = offset - x' */
16530 assert(var->negatedvar != NULL);
16531 assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
16532 assert(var->negatedvar->negatedvar == var);
16533 return var->data.negate.constant - SCIPvarGetLbAtIndex(var->negatedvar, bdchgidx, after);
16534
16535 default:
16536 SCIPerrorMessage("unknown variable status\n");
16537 SCIPABORT();
16538 return SCIP_INVALID; /*lint !e527*/
16539 }
16540 }
16541
16542 /** returns lower or upper bound of variable directly before or after the bound change given by the bound change index
16543 * was applied
16544 *
16545 * @deprecated Please use SCIPgetVarBdAtIndex()
16546 */
SCIPvarGetBdAtIndex(SCIP_VAR * var,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16547 SCIP_Real SCIPvarGetBdAtIndex(
16548 SCIP_VAR* var, /**< problem variable */
16549 SCIP_BOUNDTYPE boundtype, /**< type of bound: lower or upper bound */
16550 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16551 SCIP_Bool after /**< should the bound change with given index be included? */
16552 )
16553 {
16554 if( boundtype == SCIP_BOUNDTYPE_LOWER )
16555 return SCIPvarGetLbAtIndex(var, bdchgidx, after);
16556 else
16557 {
16558 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
16559 return SCIPvarGetUbAtIndex(var, bdchgidx, after);
16560 }
16561 }
16562
16563 /** returns whether the binary variable was fixed at the time given by the bound change index
16564 *
16565 * @deprecated Please use SCIPgetVarWasFixedAtIndex()
16566 */
SCIPvarWasFixedAtIndex(SCIP_VAR * var,SCIP_BDCHGIDX * bdchgidx,SCIP_Bool after)16567 SCIP_Bool SCIPvarWasFixedAtIndex(
16568 SCIP_VAR* var, /**< problem variable */
16569 SCIP_BDCHGIDX* bdchgidx, /**< bound change index representing time on path to current node */
16570 SCIP_Bool after /**< should the bound change with given index be included? */
16571 )
16572 {
16573 assert(var != NULL);
16574 assert(SCIPvarIsBinary(var));
16575
16576 /* check the current bounds first in order to decide at which bound change information we have to look
16577 * (which is expensive because we have to follow the aggregation tree to the active variable)
16578 */
16579 return ((SCIPvarGetLbLocal(var) > 0.5 && SCIPvarGetLbAtIndex(var, bdchgidx, after) > 0.5)
16580 || (SCIPvarGetUbLocal(var) < 0.5 && SCIPvarGetUbAtIndex(var, bdchgidx, after) < 0.5));
16581 }
16582
16583 /** bound change index representing the initial time before any bound changes took place */
16584 static SCIP_BDCHGIDX initbdchgidx = {-2, 0};
16585
16586 /** bound change index representing the presolving stage */
16587 static SCIP_BDCHGIDX presolvebdchgidx = {-1, 0};
16588
16589 /** returns the last bound change index, at which the bounds of the given variable were tightened */
SCIPvarGetLastBdchgIndex(SCIP_VAR * var)16590 SCIP_BDCHGIDX* SCIPvarGetLastBdchgIndex(
16591 SCIP_VAR* var /**< problem variable */
16592 )
16593 {
16594 SCIP_BDCHGIDX* lbchgidx;
16595 SCIP_BDCHGIDX* ubchgidx;
16596
16597 assert(var != NULL);
16598
16599 var = SCIPvarGetProbvar(var);
16600
16601 /* check, if variable is original without transformed variable */
16602 if( var == NULL )
16603 return &initbdchgidx;
16604
16605 /* check, if variable was fixed in presolving */
16606 if( !SCIPvarIsActive(var) )
16607 return &presolvebdchgidx;
16608
16609 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
16610
16611 /* get depths of last bound change information for the lower and upper bound */
16612 lbchgidx = (var->nlbchginfos > 0 && !var->lbchginfos[var->nlbchginfos-1].redundant
16613 ? &var->lbchginfos[var->nlbchginfos-1].bdchgidx : &initbdchgidx);
16614 ubchgidx = (var->nubchginfos > 0 && !var->ubchginfos[var->nubchginfos-1].redundant
16615 ? &var->ubchginfos[var->nubchginfos-1].bdchgidx : &initbdchgidx);
16616
16617 if( SCIPbdchgidxIsEarlierNonNull(lbchgidx, ubchgidx) )
16618 return ubchgidx;
16619 else
16620 return lbchgidx;
16621 }
16622
16623 /** returns the last depth level, at which the bounds of the given variable were tightened;
16624 * returns -2, if the variable's bounds are still the global bounds
16625 * returns -1, if the variable was fixed in presolving
16626 */
SCIPvarGetLastBdchgDepth(SCIP_VAR * var)16627 int SCIPvarGetLastBdchgDepth(
16628 SCIP_VAR* var /**< problem variable */
16629 )
16630 {
16631 SCIP_BDCHGIDX* bdchgidx;
16632
16633 bdchgidx = SCIPvarGetLastBdchgIndex(var);
16634 assert(bdchgidx != NULL);
16635
16636 return bdchgidx->depth;
16637 }
16638
16639 /** returns at which depth in the tree a bound change was applied to the variable that conflicts with the
16640 * given bound; returns -1 if the bound does not conflict with the current local bounds of the variable
16641 */
SCIPvarGetConflictingBdchgDepth(SCIP_VAR * var,SCIP_SET * set,SCIP_BOUNDTYPE boundtype,SCIP_Real bound)16642 int SCIPvarGetConflictingBdchgDepth(
16643 SCIP_VAR* var, /**< problem variable */
16644 SCIP_SET* set, /**< global SCIP settings */
16645 SCIP_BOUNDTYPE boundtype, /**< bound type of the conflicting bound */
16646 SCIP_Real bound /**< conflicting bound */
16647 )
16648 {
16649 int i;
16650
16651 assert(var != NULL);
16652 assert(set != NULL);
16653 assert(var->scip == set->scip);
16654
16655 if( boundtype == SCIP_BOUNDTYPE_LOWER )
16656 {
16657 /* check if the bound is in conflict with the current local bounds */
16658 if( SCIPsetIsLE(set, bound, var->locdom.ub) )
16659 return -1;
16660
16661 /* check if the bound is in conflict with the global bound */
16662 if( SCIPsetIsGT(set, bound, var->glbdom.ub) )
16663 return 0;
16664
16665 /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
16666 assert(var->nubchginfos > 0);
16667 assert(SCIPsetIsGT(set, bound, var->ubchginfos[var->nubchginfos-1].newbound));
16668
16669 /* search for the first conflicting bound change */
16670 for( i = var->nubchginfos-1; i > 0 && SCIPsetIsGT(set, bound, var->ubchginfos[i-1].newbound); --i )
16671 {
16672 assert(var->ubchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
16673 assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
16674 }
16675 assert(SCIPsetIsGT(set, bound, var->ubchginfos[i].newbound)); /* bound change i is conflicting */
16676 assert(i == 0 || SCIPsetIsLE(set, bound, var->ubchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */
16677
16678 /* return the depth at which the first conflicting bound change took place */
16679 return var->ubchginfos[i].bdchgidx.depth;
16680 }
16681 else
16682 {
16683 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
16684
16685 /* check if the bound is in conflict with the current local bounds */
16686 if( SCIPsetIsGE(set, bound, var->locdom.lb) )
16687 return -1;
16688
16689 /* check if the bound is in conflict with the global bound */
16690 if( SCIPsetIsLT(set, bound, var->glbdom.lb) )
16691 return 0;
16692
16693 /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
16694 assert(var->nlbchginfos > 0);
16695 assert(SCIPsetIsLT(set, bound, var->lbchginfos[var->nlbchginfos-1].newbound));
16696
16697 /* search for the first conflicting bound change */
16698 for( i = var->nlbchginfos-1; i > 0 && SCIPsetIsLT(set, bound, var->lbchginfos[i-1].newbound); --i )
16699 {
16700 assert(var->lbchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
16701 assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
16702 }
16703 assert(SCIPsetIsLT(set, bound, var->lbchginfos[i].newbound)); /* bound change i is conflicting */
16704 assert(i == 0 || SCIPsetIsGE(set, bound, var->lbchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */
16705
16706 /* return the depth at which the first conflicting bound change took place */
16707 return var->lbchginfos[i].bdchgidx.depth;
16708 }
16709 }
16710
16711 /** returns whether the first binary variable was fixed earlier than the second one;
16712 * returns FALSE, if the first variable is not fixed, and returns TRUE, if the first variable is fixed, but the
16713 * second one is not fixed
16714 */
SCIPvarWasFixedEarlier(SCIP_VAR * var1,SCIP_VAR * var2)16715 SCIP_Bool SCIPvarWasFixedEarlier(
16716 SCIP_VAR* var1, /**< first binary variable */
16717 SCIP_VAR* var2 /**< second binary variable */
16718 )
16719 {
16720 SCIP_BDCHGIDX* bdchgidx1;
16721 SCIP_BDCHGIDX* bdchgidx2;
16722
16723 assert(var1 != NULL);
16724 assert(var2 != NULL);
16725 assert(SCIPvarIsBinary(var1));
16726 assert(SCIPvarIsBinary(var2));
16727
16728 var1 = SCIPvarGetProbvar(var1);
16729 var2 = SCIPvarGetProbvar(var2);
16730 assert(var1 != NULL);
16731 assert(var2 != NULL);
16732
16733 /* check, if variables are globally fixed */
16734 if( !SCIPvarIsActive(var2) || var2->glbdom.lb > 0.5 || var2->glbdom.ub < 0.5 )
16735 return FALSE;
16736 if( !SCIPvarIsActive(var1) || var1->glbdom.lb > 0.5 || var1->glbdom.ub < 0.5 )
16737 return TRUE;
16738
16739 assert(SCIPvarGetStatus(var1) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_COLUMN);
16740 assert(SCIPvarGetStatus(var2) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_COLUMN);
16741 assert(SCIPvarIsBinary(var1));
16742 assert(SCIPvarIsBinary(var2));
16743 assert(var1->nlbchginfos + var1->nubchginfos <= 1);
16744 assert(var2->nlbchginfos + var2->nubchginfos <= 1);
16745 assert(var1->nlbchginfos == 0 || !var1->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
16746 assert(var1->nubchginfos == 0 || !var1->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */
16747 assert(var2->nlbchginfos == 0 || !var2->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
16748 assert(var2->nubchginfos == 0 || !var2->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */
16749
16750 if( var1->nlbchginfos == 1 )
16751 bdchgidx1 = &var1->lbchginfos[0].bdchgidx;
16752 else if( var1->nubchginfos == 1 )
16753 bdchgidx1 = &var1->ubchginfos[0].bdchgidx;
16754 else
16755 bdchgidx1 = NULL;
16756
16757 if( var2->nlbchginfos == 1 )
16758 bdchgidx2 = &var2->lbchginfos[0].bdchgidx;
16759 else if( var2->nubchginfos == 1 )
16760 bdchgidx2 = &var2->ubchginfos[0].bdchgidx;
16761 else
16762 bdchgidx2 = NULL;
16763
16764 return SCIPbdchgidxIsEarlier(bdchgidx1, bdchgidx2);
16765 }
16766
16767
16768
16769 /*
16770 * Hash functions
16771 */
16772
16773 /** gets the key (i.e. the name) of the given variable */
SCIP_DECL_HASHGETKEY(SCIPhashGetKeyVar)16774 SCIP_DECL_HASHGETKEY(SCIPhashGetKeyVar)
16775 { /*lint --e{715}*/
16776 SCIP_VAR* var = (SCIP_VAR*)elem;
16777
16778 assert(var != NULL);
16779 return var->name;
16780 }
16781
16782
16783
16784
16785 /*
16786 * simple functions implemented as defines
16787 */
16788
16789 /* In debug mode, the following methods are implemented as function calls to ensure
16790 * type validity.
16791 * In optimized mode, the methods are implemented as defines to improve performance.
16792 * However, we want to have them in the library anyways, so we have to undef the defines.
16793 */
16794
16795 #undef SCIPboundchgGetNewbound
16796 #undef SCIPboundchgGetVar
16797 #undef SCIPboundchgGetBoundchgtype
16798 #undef SCIPboundchgGetBoundtype
16799 #undef SCIPboundchgIsRedundant
16800 #undef SCIPdomchgGetNBoundchgs
16801 #undef SCIPdomchgGetBoundchg
16802 #undef SCIPholelistGetLeft
16803 #undef SCIPholelistGetRight
16804 #undef SCIPholelistGetNext
16805 #undef SCIPvarGetName
16806 #undef SCIPvarGetNUses
16807 #undef SCIPvarGetData
16808 #undef SCIPvarSetData
16809 #undef SCIPvarSetDelorigData
16810 #undef SCIPvarSetTransData
16811 #undef SCIPvarSetDeltransData
16812 #undef SCIPvarGetStatus
16813 #undef SCIPvarIsOriginal
16814 #undef SCIPvarIsTransformed
16815 #undef SCIPvarIsNegated
16816 #undef SCIPvarGetType
16817 #undef SCIPvarIsBinary
16818 #undef SCIPvarIsIntegral
16819 #undef SCIPvarIsInitial
16820 #undef SCIPvarIsRemovable
16821 #undef SCIPvarIsDeleted
16822 #undef SCIPvarIsDeletable
16823 #undef SCIPvarMarkDeletable
16824 #undef SCIPvarMarkNotDeletable
16825 #undef SCIPvarIsActive
16826 #undef SCIPvarGetIndex
16827 #undef SCIPvarGetProbindex
16828 #undef SCIPvarGetTransVar
16829 #undef SCIPvarGetCol
16830 #undef SCIPvarIsInLP
16831 #undef SCIPvarGetAggrVar
16832 #undef SCIPvarGetAggrScalar
16833 #undef SCIPvarGetAggrConstant
16834 #undef SCIPvarGetMultaggrNVars
16835 #undef SCIPvarGetMultaggrVars
16836 #undef SCIPvarGetMultaggrScalars
16837 #undef SCIPvarGetMultaggrConstant
16838 #undef SCIPvarGetNegatedVar
16839 #undef SCIPvarGetNegationVar
16840 #undef SCIPvarGetNegationConstant
16841 #undef SCIPvarGetObj
16842 #undef SCIPvarGetLbOriginal
16843 #undef SCIPvarGetUbOriginal
16844 #undef SCIPvarGetHolelistOriginal
16845 #undef SCIPvarGetLbGlobal
16846 #undef SCIPvarGetUbGlobal
16847 #undef SCIPvarGetHolelistGlobal
16848 #undef SCIPvarGetBestBoundGlobal
16849 #undef SCIPvarGetWorstBoundGlobal
16850 #undef SCIPvarGetLbLocal
16851 #undef SCIPvarGetUbLocal
16852 #undef SCIPvarGetHolelistLocal
16853 #undef SCIPvarGetBestBoundLocal
16854 #undef SCIPvarGetWorstBoundLocal
16855 #undef SCIPvarGetBestBoundType
16856 #undef SCIPvarGetWorstBoundType
16857 #undef SCIPvarGetLbLazy
16858 #undef SCIPvarGetUbLazy
16859 #undef SCIPvarGetBranchFactor
16860 #undef SCIPvarGetBranchPriority
16861 #undef SCIPvarGetBranchDirection
16862 #undef SCIPvarGetNVlbs
16863 #undef SCIPvarGetVlbVars
16864 #undef SCIPvarGetVlbCoefs
16865 #undef SCIPvarGetVlbConstants
16866 #undef SCIPvarGetNVubs
16867 #undef SCIPvarGetVubVars
16868 #undef SCIPvarGetVubCoefs
16869 #undef SCIPvarGetVubConstants
16870 #undef SCIPvarGetNImpls
16871 #undef SCIPvarGetImplVars
16872 #undef SCIPvarGetImplTypes
16873 #undef SCIPvarGetImplBounds
16874 #undef SCIPvarGetImplIds
16875 #undef SCIPvarGetNCliques
16876 #undef SCIPvarGetCliques
16877 #undef SCIPvarGetLPSol
16878 #undef SCIPvarGetNLPSol
16879 #undef SCIPvarGetBdchgInfoLb
16880 #undef SCIPvarGetNBdchgInfosLb
16881 #undef SCIPvarGetBdchgInfoUb
16882 #undef SCIPvarGetNBdchgInfosUb
16883 #undef SCIPvarGetValuehistory
16884 #undef SCIPvarGetPseudoSol
16885 #undef SCIPvarCatchEvent
16886 #undef SCIPvarDropEvent
16887 #undef SCIPvarGetVSIDS
16888 #undef SCIPvarGetCliqueComponentIdx
16889 #undef SCIPvarIsRelaxationOnly
16890 #undef SCIPvarMarkRelaxationOnly
16891 #undef SCIPbdchgidxGetPos
16892 #undef SCIPbdchgidxIsEarlierNonNull
16893 #undef SCIPbdchgidxIsEarlier
16894 #undef SCIPbdchginfoGetOldbound
16895 #undef SCIPbdchginfoGetNewbound
16896 #undef SCIPbdchginfoGetVar
16897 #undef SCIPbdchginfoGetChgtype
16898 #undef SCIPbdchginfoGetBoundtype
16899 #undef SCIPbdchginfoGetDepth
16900 #undef SCIPbdchginfoGetPos
16901 #undef SCIPbdchginfoGetIdx
16902 #undef SCIPbdchginfoGetInferVar
16903 #undef SCIPbdchginfoGetInferCons
16904 #undef SCIPbdchginfoGetInferProp
16905 #undef SCIPbdchginfoGetInferInfo
16906 #undef SCIPbdchginfoGetInferBoundtype
16907 #undef SCIPbdchginfoIsRedundant
16908 #undef SCIPbdchginfoHasInferenceReason
16909 #undef SCIPbdchginfoIsTighter
16910
16911
16912 /** returns the new value of the bound in the bound change data */
SCIPboundchgGetNewbound(SCIP_BOUNDCHG * boundchg)16913 SCIP_Real SCIPboundchgGetNewbound(
16914 SCIP_BOUNDCHG* boundchg /**< bound change data */
16915 )
16916 {
16917 assert(boundchg != NULL);
16918
16919 return boundchg->newbound;
16920 }
16921
16922 /** returns the variable of the bound change in the bound change data */
SCIPboundchgGetVar(SCIP_BOUNDCHG * boundchg)16923 SCIP_VAR* SCIPboundchgGetVar(
16924 SCIP_BOUNDCHG* boundchg /**< bound change data */
16925 )
16926 {
16927 assert(boundchg != NULL);
16928
16929 return boundchg->var;
16930 }
16931
16932 /** returns the bound change type of the bound change in the bound change data */
SCIPboundchgGetBoundchgtype(SCIP_BOUNDCHG * boundchg)16933 SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(
16934 SCIP_BOUNDCHG* boundchg /**< bound change data */
16935 )
16936 {
16937 assert(boundchg != NULL);
16938
16939 return (SCIP_BOUNDCHGTYPE)(boundchg->boundchgtype);
16940 }
16941
16942 /** returns the bound type of the bound change in the bound change data */
SCIPboundchgGetBoundtype(SCIP_BOUNDCHG * boundchg)16943 SCIP_BOUNDTYPE SCIPboundchgGetBoundtype(
16944 SCIP_BOUNDCHG* boundchg /**< bound change data */
16945 )
16946 {
16947 assert(boundchg != NULL);
16948
16949 return (SCIP_BOUNDTYPE)(boundchg->boundtype);
16950 }
16951
16952 /** returns whether the bound change is redundant due to a more global bound that is at least as strong */
SCIPboundchgIsRedundant(SCIP_BOUNDCHG * boundchg)16953 SCIP_Bool SCIPboundchgIsRedundant(
16954 SCIP_BOUNDCHG* boundchg /**< bound change data */
16955 )
16956 {
16957 assert(boundchg != NULL);
16958
16959 return boundchg->redundant;
16960 }
16961
16962 /** returns the number of bound changes in the domain change data */
SCIPdomchgGetNBoundchgs(SCIP_DOMCHG * domchg)16963 int SCIPdomchgGetNBoundchgs(
16964 SCIP_DOMCHG* domchg /**< domain change data */
16965 )
16966 {
16967 return domchg != NULL ? domchg->domchgbound.nboundchgs : 0;
16968 }
16969
16970 /** returns a particular bound change in the domain change data */
SCIPdomchgGetBoundchg(SCIP_DOMCHG * domchg,int pos)16971 SCIP_BOUNDCHG* SCIPdomchgGetBoundchg(
16972 SCIP_DOMCHG* domchg, /**< domain change data */
16973 int pos /**< position of the bound change in the domain change data */
16974 )
16975 {
16976 assert(domchg != NULL);
16977 assert(0 <= pos && pos < (int)domchg->domchgbound.nboundchgs);
16978
16979 return &domchg->domchgbound.boundchgs[pos];
16980 }
16981
16982 /** returns left bound of open interval in hole */
SCIPholelistGetLeft(SCIP_HOLELIST * holelist)16983 SCIP_Real SCIPholelistGetLeft(
16984 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
16985 )
16986 {
16987 assert(holelist != NULL);
16988
16989 return holelist->hole.left;
16990 }
16991
16992 /** returns right bound of open interval in hole */
SCIPholelistGetRight(SCIP_HOLELIST * holelist)16993 SCIP_Real SCIPholelistGetRight(
16994 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
16995 )
16996 {
16997 assert(holelist != NULL);
16998
16999 return holelist->hole.right;
17000 }
17001
17002 /** returns next hole in list */
SCIPholelistGetNext(SCIP_HOLELIST * holelist)17003 SCIP_HOLELIST* SCIPholelistGetNext(
17004 SCIP_HOLELIST* holelist /**< hole list pointer to hole of interest */
17005 )
17006 {
17007 assert(holelist != NULL);
17008
17009 return holelist->next;
17010 }
17011
17012 /** returns the name of the variable
17013 *
17014 * @note to change the name of a variable, use SCIPchgVarName() from scip.h
17015 */
SCIPvarGetName(SCIP_VAR * var)17016 const char* SCIPvarGetName(
17017 SCIP_VAR* var /**< problem variable */
17018 )
17019 {
17020 assert(var != NULL);
17021
17022 return var->name;
17023 }
17024
17025 /** gets number of times, the variable is currently captured */
SCIPvarGetNUses(SCIP_VAR * var)17026 int SCIPvarGetNUses(
17027 SCIP_VAR* var /**< problem variable */
17028 )
17029 {
17030 assert(var != NULL);
17031
17032 return var->nuses;
17033 }
17034
17035 /** returns the user data of the variable */
SCIPvarGetData(SCIP_VAR * var)17036 SCIP_VARDATA* SCIPvarGetData(
17037 SCIP_VAR* var /**< problem variable */
17038 )
17039 {
17040 assert(var != NULL);
17041
17042 return var->vardata;
17043 }
17044
17045 /** sets the user data for the variable */
SCIPvarSetData(SCIP_VAR * var,SCIP_VARDATA * vardata)17046 void SCIPvarSetData(
17047 SCIP_VAR* var, /**< problem variable */
17048 SCIP_VARDATA* vardata /**< user variable data */
17049 )
17050 {
17051 assert(var != NULL);
17052
17053 var->vardata = vardata;
17054 }
17055
17056 /** sets method to free user data for the original variable */
SCIPvarSetDelorigData(SCIP_VAR * var,SCIP_DECL_VARDELORIG ((* vardelorig)))17057 void SCIPvarSetDelorigData(
17058 SCIP_VAR* var, /**< problem variable */
17059 SCIP_DECL_VARDELORIG ((*vardelorig)) /**< frees user data of original variable */
17060 )
17061 {
17062 assert(var != NULL);
17063 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17064
17065 var->vardelorig = vardelorig;
17066 }
17067
17068 /** sets method to transform user data of the variable */
SCIPvarSetTransData(SCIP_VAR * var,SCIP_DECL_VARTRANS ((* vartrans)))17069 void SCIPvarSetTransData(
17070 SCIP_VAR* var, /**< problem variable */
17071 SCIP_DECL_VARTRANS ((*vartrans)) /**< creates transformed user data by transforming original user data */
17072 )
17073 {
17074 assert(var != NULL);
17075 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17076
17077 var->vartrans = vartrans;
17078 }
17079
17080 /** sets method to free transformed user data for the variable */
SCIPvarSetDeltransData(SCIP_VAR * var,SCIP_DECL_VARDELTRANS ((* vardeltrans)))17081 void SCIPvarSetDeltransData(
17082 SCIP_VAR* var, /**< problem variable */
17083 SCIP_DECL_VARDELTRANS ((*vardeltrans)) /**< frees user data of transformed variable */
17084 )
17085 {
17086 assert(var != NULL);
17087
17088 var->vardeltrans = vardeltrans;
17089 }
17090
17091 /** sets method to copy this variable into sub-SCIPs */
SCIPvarSetCopyData(SCIP_VAR * var,SCIP_DECL_VARCOPY ((* varcopy)))17092 void SCIPvarSetCopyData(
17093 SCIP_VAR* var, /**< problem variable */
17094 SCIP_DECL_VARCOPY ((*varcopy)) /**< copy method of the variable */
17095 )
17096 {
17097 assert(var != NULL);
17098
17099 var->varcopy = varcopy;
17100 }
17101
17102 /** sets the initial flag of a variable; only possible for original or loose variables */
SCIPvarSetInitial(SCIP_VAR * var,SCIP_Bool initial)17103 SCIP_RETCODE SCIPvarSetInitial(
17104 SCIP_VAR* var, /**< problem variable */
17105 SCIP_Bool initial /**< initial flag */
17106 )
17107 {
17108 assert(var != NULL);
17109
17110 if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
17111 return SCIP_INVALIDCALL;
17112
17113 var->initial = initial;
17114
17115 return SCIP_OKAY;
17116 }
17117
17118 /** sets the removable flag of a variable; only possible for original or loose variables */
SCIPvarSetRemovable(SCIP_VAR * var,SCIP_Bool removable)17119 SCIP_RETCODE SCIPvarSetRemovable(
17120 SCIP_VAR* var, /**< problem variable */
17121 SCIP_Bool removable /**< removable flag */
17122 )
17123 {
17124 assert(var != NULL);
17125
17126 if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
17127 return SCIP_INVALIDCALL;
17128
17129 var->removable = removable;
17130
17131 return SCIP_OKAY;
17132 }
17133
17134 /** gets status of variable */
SCIPvarGetStatus(SCIP_VAR * var)17135 SCIP_VARSTATUS SCIPvarGetStatus(
17136 SCIP_VAR* var /**< problem variable */
17137 )
17138 {
17139 assert(var != NULL);
17140
17141 return (SCIP_VARSTATUS)(var->varstatus);
17142 }
17143
17144 /** returns whether the variable belongs to the original problem */
SCIPvarIsOriginal(SCIP_VAR * var)17145 SCIP_Bool SCIPvarIsOriginal(
17146 SCIP_VAR* var /**< problem variable */
17147 )
17148 {
17149 assert(var != NULL);
17150 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);
17151
17152 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL
17153 || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED
17154 && SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL));
17155 }
17156
17157 /** returns whether the variable belongs to the transformed problem */
SCIPvarIsTransformed(SCIP_VAR * var)17158 SCIP_Bool SCIPvarIsTransformed(
17159 SCIP_VAR* var /**< problem variable */
17160 )
17161 {
17162 assert(var != NULL);
17163 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);
17164
17165 return (SCIPvarGetStatus(var) != SCIP_VARSTATUS_ORIGINAL
17166 && (SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED
17167 || SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_ORIGINAL));
17168 }
17169
17170 /** returns whether the variable was created by negation of a different variable */
SCIPvarIsNegated(SCIP_VAR * var)17171 SCIP_Bool SCIPvarIsNegated(
17172 SCIP_VAR* var /**< problem variable */
17173 )
17174 {
17175 assert(var != NULL);
17176
17177 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17178 }
17179
17180 /** gets type of variable */
SCIPvarGetType(SCIP_VAR * var)17181 SCIP_VARTYPE SCIPvarGetType(
17182 SCIP_VAR* var /**< problem variable */
17183 )
17184 {
17185 assert(var != NULL);
17186
17187 return (SCIP_VARTYPE)(var->vartype);
17188 }
17189
17190 /** returns TRUE if the variable is of binary type; this is the case if:
17191 * (1) variable type is binary
17192 * (2) variable type is integer or implicit integer and
17193 * (i) the lazy lower bound or the global lower bound is greater than or equal to zero
17194 * (ii) the lazy upper bound or the global upper bound is less than or equal to one
17195 */
SCIPvarIsBinary(SCIP_VAR * var)17196 SCIP_Bool SCIPvarIsBinary(
17197 SCIP_VAR* var /**< problem variable */
17198 )
17199 {
17200 assert(var != NULL);
17201
17202 return (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY ||
17203 (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS && MAX(var->glbdom.lb, var->lazylb) >= 0.0 && MIN(var->glbdom.ub, var->lazyub) <= 1.0));
17204 }
17205
17206 /** returns whether variable is of integral type (binary, integer, or implicit integer) */
SCIPvarIsIntegral(SCIP_VAR * var)17207 SCIP_Bool SCIPvarIsIntegral(
17208 SCIP_VAR* var /**< problem variable */
17209 )
17210 {
17211 assert(var != NULL);
17212
17213 return (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS);
17214 }
17215
17216 /** returns whether variable's column should be present in the initial root LP */
SCIPvarIsInitial(SCIP_VAR * var)17217 SCIP_Bool SCIPvarIsInitial(
17218 SCIP_VAR* var /**< problem variable */
17219 )
17220 {
17221 assert(var != NULL);
17222
17223 return var->initial;
17224 }
17225
17226 /** returns whether variable's column is removable from the LP (due to aging or cleanup) */
SCIPvarIsRemovable(SCIP_VAR * var)17227 SCIP_Bool SCIPvarIsRemovable(
17228 SCIP_VAR* var /**< problem variable */
17229 )
17230 {
17231 assert(var != NULL);
17232
17233 return var->removable;
17234 }
17235
17236 /** returns whether the variable was deleted from the problem */
SCIPvarIsDeleted(SCIP_VAR * var)17237 SCIP_Bool SCIPvarIsDeleted(
17238 SCIP_VAR* var /**< problem variable */
17239 )
17240 {
17241 assert(var != NULL);
17242
17243 return var->deleted;
17244 }
17245
17246 /** marks the variable to be deletable, i.e., it may be deleted completely from the problem;
17247 * method can only be called before the variable is added to the problem by SCIPaddVar() or SCIPaddPricedVar()
17248 */
SCIPvarMarkDeletable(SCIP_VAR * var)17249 void SCIPvarMarkDeletable(
17250 SCIP_VAR* var /**< problem variable */
17251 )
17252 {
17253 assert(var != NULL);
17254 assert(var->probindex == -1);
17255
17256 var->deletable = TRUE;
17257 }
17258
17259 /** marks the variable to be not deletable from the problem */
SCIPvarMarkNotDeletable(SCIP_VAR * var)17260 void SCIPvarMarkNotDeletable(
17261 SCIP_VAR* var
17262 )
17263 {
17264 assert(var != NULL);
17265
17266 var->deletable = FALSE;
17267 }
17268
17269 /** marks variable to be deleted from global structures (cliques etc.) when cleaning up
17270 *
17271 * @note: this is not equivalent to marking the variable itself for deletion, this is done by using SCIPvarMarkDeletable()
17272 */
SCIPvarMarkDeleteGlobalStructures(SCIP_VAR * var)17273 void SCIPvarMarkDeleteGlobalStructures(
17274 SCIP_VAR* var /**< problem variable */
17275 )
17276 {
17277 assert(var != NULL);
17278
17279 var->delglobalstructs = TRUE;
17280 }
17281
17282 /** returns whether the variable was flagged for deletion from global structures (cliques etc.) */
SCIPvarIsMarkedDeleteGlobalStructures(SCIP_VAR * var)17283 SCIP_Bool SCIPvarIsMarkedDeleteGlobalStructures(
17284 SCIP_VAR* var /**< problem variable */
17285 )
17286 {
17287 assert(var != NULL);
17288
17289 return var->delglobalstructs;
17290 }
17291
17292 /** returns whether a variable has been introduced to define a relaxation
17293 *
17294 * These variables are only valid for the current SCIP solve round,
17295 * they are not contained in any (checked) constraints, but may be used
17296 * in cutting planes, for example.
17297 * Relaxation-only variables are not copied by SCIPcopyVars and cuts
17298 * that contain these variables are not added as linear constraints when
17299 * restarting or transferring information from a copied SCIP to a SCIP.
17300 * Also conflicts with relaxation-only variables are not generated at
17301 * the moment.
17302 */
SCIPvarIsRelaxationOnly(SCIP_VAR * var)17303 SCIP_Bool SCIPvarIsRelaxationOnly(
17304 SCIP_VAR* var /**< problem variable */
17305 )
17306 {
17307 assert(var != NULL);
17308
17309 return var->relaxationonly;
17310 }
17311
17312 /** marks that this variable has only been introduced to define a relaxation
17313 *
17314 * The variable must not have a coefficient in the objective.
17315 *
17316 * @see SCIPvarIsRelaxationOnly
17317 */
SCIPvarMarkRelaxationOnly(SCIP_VAR * var)17318 void SCIPvarMarkRelaxationOnly(
17319 SCIP_VAR* var /**< problem variable */
17320 )
17321 {
17322 assert(var != NULL);
17323 assert(SCIPvarGetObj(var) == 0.0);
17324
17325 var->relaxationonly = TRUE;
17326 }
17327
17328 /** returns whether variable is allowed to be deleted completely from the problem */
SCIPvarIsDeletable(SCIP_VAR * var)17329 SCIP_Bool SCIPvarIsDeletable(
17330 SCIP_VAR* var
17331 )
17332 {
17333 assert(var != NULL);
17334
17335 return var->deletable;
17336 }
17337
17338 /** returns whether variable is an active (neither fixed nor aggregated) variable */
SCIPvarIsActive(SCIP_VAR * var)17339 SCIP_Bool SCIPvarIsActive(
17340 SCIP_VAR* var /**< problem variable */
17341 )
17342 {
17343 assert(var != NULL);
17344
17345 return (var->probindex >= 0);
17346 }
17347
17348 /** gets unique index of variable */
SCIPvarGetIndex(SCIP_VAR * var)17349 int SCIPvarGetIndex(
17350 SCIP_VAR* var /**< problem variable */
17351 )
17352 {
17353 assert(var != NULL);
17354
17355 return var->index;
17356 }
17357
17358 /** gets position of variable in problem, or -1 if variable is not active */
SCIPvarGetProbindex(SCIP_VAR * var)17359 int SCIPvarGetProbindex(
17360 SCIP_VAR* var /**< problem variable */
17361 )
17362 {
17363 assert(var != NULL);
17364
17365 return var->probindex;
17366 }
17367
17368 /** gets transformed variable of ORIGINAL variable */
SCIPvarGetTransVar(SCIP_VAR * var)17369 SCIP_VAR* SCIPvarGetTransVar(
17370 SCIP_VAR* var /**< problem variable */
17371 )
17372 {
17373 assert(var != NULL);
17374 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);
17375
17376 return var->data.original.transvar;
17377 }
17378
17379 /** gets column of COLUMN variable */
SCIPvarGetCol(SCIP_VAR * var)17380 SCIP_COL* SCIPvarGetCol(
17381 SCIP_VAR* var /**< problem variable */
17382 )
17383 {
17384 assert(var != NULL);
17385 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
17386
17387 return var->data.col;
17388 }
17389
17390 /** returns whether the variable is a COLUMN variable that is member of the current LP */
SCIPvarIsInLP(SCIP_VAR * var)17391 SCIP_Bool SCIPvarIsInLP(
17392 SCIP_VAR* var /**< problem variable */
17393 )
17394 {
17395 assert(var != NULL);
17396
17397 return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN && SCIPcolIsInLP(var->data.col));
17398 }
17399
17400 /** gets aggregation variable y of an aggregated variable x = a*y + c */
SCIPvarGetAggrVar(SCIP_VAR * var)17401 SCIP_VAR* SCIPvarGetAggrVar(
17402 SCIP_VAR* var /**< problem variable */
17403 )
17404 {
17405 assert(var != NULL);
17406 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17407
17408 return var->data.aggregate.var;
17409 }
17410
17411 /** gets aggregation scalar a of an aggregated variable x = a*y + c */
SCIPvarGetAggrScalar(SCIP_VAR * var)17412 SCIP_Real SCIPvarGetAggrScalar(
17413 SCIP_VAR* var /**< problem variable */
17414 )
17415 {
17416 assert(var != NULL);
17417 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17418
17419 return var->data.aggregate.scalar;
17420 }
17421
17422 /** gets aggregation constant c of an aggregated variable x = a*y + c */
SCIPvarGetAggrConstant(SCIP_VAR * var)17423 SCIP_Real SCIPvarGetAggrConstant(
17424 SCIP_VAR* var /**< problem variable */
17425 )
17426 {
17427 assert(var != NULL);
17428 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
17429
17430 return var->data.aggregate.constant;
17431 }
17432
17433 /** gets number n of aggregation variables of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIPvarGetMultaggrNVars(SCIP_VAR * var)17434 int SCIPvarGetMultaggrNVars(
17435 SCIP_VAR* var /**< problem variable */
17436 )
17437 {
17438 assert(var != NULL);
17439 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17440 assert(!var->donotmultaggr);
17441
17442 return var->data.multaggr.nvars;
17443 }
17444
17445 /** gets vector of aggregation variables y of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIPvarGetMultaggrVars(SCIP_VAR * var)17446 SCIP_VAR** SCIPvarGetMultaggrVars(
17447 SCIP_VAR* var /**< problem variable */
17448 )
17449 {
17450 assert(var != NULL);
17451 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17452 assert(!var->donotmultaggr);
17453
17454 return var->data.multaggr.vars;
17455 }
17456
17457 /** gets vector of aggregation scalars a of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIPvarGetMultaggrScalars(SCIP_VAR * var)17458 SCIP_Real* SCIPvarGetMultaggrScalars(
17459 SCIP_VAR* var /**< problem variable */
17460 )
17461 {
17462 assert(var != NULL);
17463 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17464 assert(!var->donotmultaggr);
17465
17466 return var->data.multaggr.scalars;
17467 }
17468
17469 /** gets aggregation constant c of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIPvarGetMultaggrConstant(SCIP_VAR * var)17470 SCIP_Real SCIPvarGetMultaggrConstant(
17471 SCIP_VAR* var /**< problem variable */
17472 )
17473 {
17474 assert(var != NULL);
17475 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
17476 assert(!var->donotmultaggr);
17477
17478 return var->data.multaggr.constant;
17479 }
17480
17481 /** gets the negation of the given variable; may return NULL, if no negation is existing yet */
SCIPvarGetNegatedVar(SCIP_VAR * var)17482 SCIP_VAR* SCIPvarGetNegatedVar(
17483 SCIP_VAR* var /**< negated problem variable */
17484 )
17485 {
17486 assert(var != NULL);
17487
17488 return var->negatedvar;
17489 }
17490
17491 /** gets the negation variable x of a negated variable x' = offset - x */
SCIPvarGetNegationVar(SCIP_VAR * var)17492 SCIP_VAR* SCIPvarGetNegationVar(
17493 SCIP_VAR* var /**< negated problem variable */
17494 )
17495 {
17496 assert(var != NULL);
17497 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17498
17499 return var->negatedvar;
17500 }
17501
17502 /** gets the negation offset of a negated variable x' = offset - x */
SCIPvarGetNegationConstant(SCIP_VAR * var)17503 SCIP_Real SCIPvarGetNegationConstant(
17504 SCIP_VAR* var /**< negated problem variable */
17505 )
17506 {
17507 assert(var != NULL);
17508 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17509
17510 return var->data.negate.constant;
17511 }
17512
17513 /** gets objective function value of variable */
SCIPvarGetObj(SCIP_VAR * var)17514 SCIP_Real SCIPvarGetObj(
17515 SCIP_VAR* var /**< problem variable */
17516 )
17517 {
17518 assert(var != NULL);
17519
17520 return var->obj;
17521 }
17522
17523 /** gets the unchanged objective function value of a variable (ignoring temproray changes performed in probing mode) */
SCIPvarGetUnchangedObj(SCIP_VAR * var)17524 SCIP_Real SCIPvarGetUnchangedObj(
17525 SCIP_VAR* var /**< problem variable */
17526 )
17527 {
17528 assert(var != NULL);
17529
17530 return var->unchangedobj;
17531 }
17532
17533 /** gets corresponding objective value of active, fixed, or multi-aggregated problem variable of given variable
17534 * e.g. obj(x) = 1 this method returns for ~x the value -1
17535 */
SCIPvarGetAggregatedObj(SCIP_VAR * var,SCIP_Real * aggrobj)17536 SCIP_RETCODE SCIPvarGetAggregatedObj(
17537 SCIP_VAR* var, /**< problem variable */
17538 SCIP_Real* aggrobj /**< pointer to store the aggregated objective value */
17539 )
17540 {
17541 SCIP_VAR* probvar = var;
17542 SCIP_Real mult = 1.0;
17543
17544 assert(probvar != NULL);
17545 assert(aggrobj != NULL);
17546
17547 while( probvar != NULL )
17548 {
17549 switch( SCIPvarGetStatus(probvar) )
17550 {
17551 case SCIP_VARSTATUS_ORIGINAL:
17552 case SCIP_VARSTATUS_LOOSE:
17553 case SCIP_VARSTATUS_COLUMN:
17554 (*aggrobj) = mult * SCIPvarGetObj(probvar);
17555 return SCIP_OKAY;
17556
17557 case SCIP_VARSTATUS_FIXED:
17558 assert(SCIPvarGetObj(probvar) == 0.0);
17559 (*aggrobj) = 0.0;
17560 return SCIP_OKAY;
17561
17562 case SCIP_VARSTATUS_MULTAGGR:
17563 /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
17564 if ( probvar->data.multaggr.nvars == 1 )
17565 {
17566 assert( probvar->data.multaggr.vars != NULL );
17567 assert( probvar->data.multaggr.scalars != NULL );
17568 assert( probvar->data.multaggr.vars[0] != NULL );
17569 mult *= probvar->data.multaggr.scalars[0];
17570 probvar = probvar->data.multaggr.vars[0];
17571 break;
17572 }
17573 else
17574 {
17575 SCIP_Real tmpobj;
17576 int v;
17577
17578 (*aggrobj) = 0.0;
17579
17580 for( v = probvar->data.multaggr.nvars - 1; v >= 0; --v )
17581 {
17582 SCIP_CALL( SCIPvarGetAggregatedObj(probvar->data.multaggr.vars[v], &tmpobj) );
17583 (*aggrobj) += probvar->data.multaggr.scalars[v] * tmpobj;
17584 }
17585 return SCIP_OKAY;
17586 }
17587
17588 case SCIP_VARSTATUS_AGGREGATED: /* x = a'*x' + c' => a*x + c == (a*a')*x' + (a*c' + c) */
17589 assert(probvar->data.aggregate.var != NULL);
17590 mult *= probvar->data.aggregate.scalar;
17591 probvar = probvar->data.aggregate.var;
17592 break;
17593
17594 case SCIP_VARSTATUS_NEGATED: /* x = - x' + c' => a*x + c == (-a)*x' + (a*c' + c) */
17595 assert(probvar->negatedvar != NULL);
17596 assert(SCIPvarGetStatus(probvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
17597 assert(probvar->negatedvar->negatedvar == probvar);
17598 mult *= -1.0;
17599 probvar = probvar->negatedvar;
17600 break;
17601
17602 default:
17603 SCIPABORT();
17604 return SCIP_INVALIDDATA; /*lint !e527*/
17605 }
17606 }
17607
17608 return SCIP_INVALIDDATA;
17609 }
17610
17611 /** gets original lower bound of original problem variable (i.e. the bound set in problem creation) */
SCIPvarGetLbOriginal(SCIP_VAR * var)17612 SCIP_Real SCIPvarGetLbOriginal(
17613 SCIP_VAR* var /**< original problem variable */
17614 )
17615 {
17616 assert(var != NULL);
17617 assert(SCIPvarIsOriginal(var));
17618
17619 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
17620 return var->data.original.origdom.lb;
17621 else
17622 {
17623 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17624 assert(var->negatedvar != NULL);
17625 assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
17626
17627 return var->data.negate.constant - var->negatedvar->data.original.origdom.ub;
17628 }
17629 }
17630
17631 /** gets original upper bound of original problem variable (i.e. the bound set in problem creation) */
SCIPvarGetUbOriginal(SCIP_VAR * var)17632 SCIP_Real SCIPvarGetUbOriginal(
17633 SCIP_VAR* var /**< original problem variable */
17634 )
17635 {
17636 assert(var != NULL);
17637 assert(SCIPvarIsOriginal(var));
17638
17639 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
17640 return var->data.original.origdom.ub;
17641 else
17642 {
17643 assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
17644 assert(var->negatedvar != NULL);
17645 assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);
17646
17647 return var->data.negate.constant - var->negatedvar->data.original.origdom.lb;
17648 }
17649 }
17650
17651 /** gets the original hole list of an original variable */
SCIPvarGetHolelistOriginal(SCIP_VAR * var)17652 SCIP_HOLELIST* SCIPvarGetHolelistOriginal(
17653 SCIP_VAR* var /**< problem variable */
17654 )
17655 {
17656 assert(var != NULL);
17657 assert(SCIPvarIsOriginal(var));
17658
17659 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
17660 return var->data.original.origdom.holelist;
17661
17662 return NULL;
17663 }
17664
17665 /** gets global lower bound of variable */
SCIPvarGetLbGlobal(SCIP_VAR * var)17666 SCIP_Real SCIPvarGetLbGlobal(
17667 SCIP_VAR* var /**< problem variable */
17668 )
17669 {
17670 assert(var != NULL);
17671
17672 return var->glbdom.lb;
17673 }
17674
17675 /** gets global upper bound of variable */
SCIPvarGetUbGlobal(SCIP_VAR * var)17676 SCIP_Real SCIPvarGetUbGlobal(
17677 SCIP_VAR* var /**< problem variable */
17678 )
17679 {
17680 assert(var != NULL);
17681
17682 return var->glbdom.ub;
17683 }
17684
17685 /** gets the global hole list of an active variable */
SCIPvarGetHolelistGlobal(SCIP_VAR * var)17686 SCIP_HOLELIST* SCIPvarGetHolelistGlobal(
17687 SCIP_VAR* var /**< problem variable */
17688 )
17689 {
17690 assert(var != NULL);
17691
17692 return var->glbdom.holelist;
17693 }
17694
17695 /** gets best global bound of variable with respect to the objective function */
SCIPvarGetBestBoundGlobal(SCIP_VAR * var)17696 SCIP_Real SCIPvarGetBestBoundGlobal(
17697 SCIP_VAR* var /**< problem variable */
17698 )
17699 {
17700 assert(var != NULL);
17701
17702 if( var->obj >= 0.0 )
17703 return var->glbdom.lb;
17704 else
17705 return var->glbdom.ub;
17706 }
17707
17708 /** gets worst global bound of variable with respect to the objective function */
SCIPvarGetWorstBoundGlobal(SCIP_VAR * var)17709 SCIP_Real SCIPvarGetWorstBoundGlobal(
17710 SCIP_VAR* var /**< problem variable */
17711 )
17712 {
17713 assert(var != NULL);
17714
17715 if( var->obj >= 0.0 )
17716 return var->glbdom.ub;
17717 else
17718 return var->glbdom.lb;
17719 }
17720
17721 /** gets current lower bound of variable */
SCIPvarGetLbLocal(SCIP_VAR * var)17722 SCIP_Real SCIPvarGetLbLocal(
17723 SCIP_VAR* var /**< problem variable */
17724 )
17725 {
17726 assert(var != NULL);
17727
17728 return var->locdom.lb;
17729 }
17730
17731 /** gets current upper bound of variable */
SCIPvarGetUbLocal(SCIP_VAR * var)17732 SCIP_Real SCIPvarGetUbLocal(
17733 SCIP_VAR* var /**< problem variable */
17734 )
17735 {
17736 assert(var != NULL);
17737
17738 return var->locdom.ub;
17739 }
17740
17741 /** gets the current hole list of an active variable */
SCIPvarGetHolelistLocal(SCIP_VAR * var)17742 SCIP_HOLELIST* SCIPvarGetHolelistLocal(
17743 SCIP_VAR* var /**< problem variable */
17744 )
17745 {
17746 assert(var != NULL);
17747
17748 return var->locdom.holelist;
17749 }
17750
17751 /** gets best local bound of variable with respect to the objective function */
SCIPvarGetBestBoundLocal(SCIP_VAR * var)17752 SCIP_Real SCIPvarGetBestBoundLocal(
17753 SCIP_VAR* var /**< problem variable */
17754 )
17755 {
17756 assert(var != NULL);
17757
17758 if( var->obj >= 0.0 )
17759 return var->locdom.lb;
17760 else
17761 return var->locdom.ub;
17762 }
17763
17764 /** gets worst local bound of variable with respect to the objective function */
SCIPvarGetWorstBoundLocal(SCIP_VAR * var)17765 SCIP_Real SCIPvarGetWorstBoundLocal(
17766 SCIP_VAR* var /**< problem variable */
17767 )
17768 {
17769 assert(var != NULL);
17770
17771 if( var->obj >= 0.0 )
17772 return var->locdom.ub;
17773 else
17774 return var->locdom.lb;
17775 }
17776
17777 /** gets type (lower or upper) of best bound of variable with respect to the objective function */
SCIPvarGetBestBoundType(SCIP_VAR * var)17778 SCIP_BOUNDTYPE SCIPvarGetBestBoundType(
17779 SCIP_VAR* var /**< problem variable */
17780 )
17781 {
17782 assert(var != NULL);
17783
17784 if( var->obj >= 0.0 )
17785 return SCIP_BOUNDTYPE_LOWER;
17786 else
17787 return SCIP_BOUNDTYPE_UPPER;
17788 }
17789
17790 /** gets type (lower or upper) of worst bound of variable with respect to the objective function */
SCIPvarGetWorstBoundType(SCIP_VAR * var)17791 SCIP_BOUNDTYPE SCIPvarGetWorstBoundType(
17792 SCIP_VAR* var /**< problem variable */
17793 )
17794 {
17795 assert(var != NULL);
17796
17797 if( var->obj >= 0.0 )
17798 return SCIP_BOUNDTYPE_UPPER;
17799 else
17800 return SCIP_BOUNDTYPE_LOWER;
17801 }
17802
17803 /** gets lazy lower bound of variable, returns -infinity if the variable has no lazy lower bound */
SCIPvarGetLbLazy(SCIP_VAR * var)17804 SCIP_Real SCIPvarGetLbLazy(
17805 SCIP_VAR* var /**< problem variable */
17806 )
17807 {
17808 assert(var != NULL);
17809
17810 return var->lazylb;
17811 }
17812
17813 /** gets lazy upper bound of variable, returns infinity if the variable has no lazy upper bound */
SCIPvarGetUbLazy(SCIP_VAR * var)17814 SCIP_Real SCIPvarGetUbLazy(
17815 SCIP_VAR* var /**< problem variable */
17816 )
17817 {
17818 assert(var != NULL);
17819
17820 return var->lazyub;
17821 }
17822
17823 /** gets the branch factor of the variable; this value can be used in the branching methods to scale the score
17824 * values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
17825 */
SCIPvarGetBranchFactor(SCIP_VAR * var)17826 SCIP_Real SCIPvarGetBranchFactor(
17827 SCIP_VAR* var /**< problem variable */
17828 )
17829 {
17830 assert(var != NULL);
17831
17832 return var->branchfactor;
17833 }
17834
17835 /** gets the branch priority of the variable; variables with higher priority should always be preferred to variables
17836 * with lower priority
17837 */
SCIPvarGetBranchPriority(SCIP_VAR * var)17838 int SCIPvarGetBranchPriority(
17839 SCIP_VAR* var /**< problem variable */
17840 )
17841 {
17842 assert(var != NULL);
17843
17844 return var->branchpriority;
17845 }
17846
17847 /** gets the preferred branch direction of the variable (downwards, upwards, or auto) */
SCIPvarGetBranchDirection(SCIP_VAR * var)17848 SCIP_BRANCHDIR SCIPvarGetBranchDirection(
17849 SCIP_VAR* var /**< problem variable */
17850 )
17851 {
17852 assert(var != NULL);
17853
17854 return (SCIP_BRANCHDIR)var->branchdirection;
17855 }
17856
17857 /** gets number of variable lower bounds x >= b_i*z_i + d_i of given variable x */
SCIPvarGetNVlbs(SCIP_VAR * var)17858 int SCIPvarGetNVlbs(
17859 SCIP_VAR* var /**< problem variable */
17860 )
17861 {
17862 assert(var != NULL);
17863
17864 return SCIPvboundsGetNVbds(var->vlbs);
17865 }
17866
17867 /** gets array with bounding variables z_i in variable lower bounds x >= b_i*z_i + d_i of given variable x;
17868 * the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
17869 */
SCIPvarGetVlbVars(SCIP_VAR * var)17870 SCIP_VAR** SCIPvarGetVlbVars(
17871 SCIP_VAR* var /**< problem variable */
17872 )
17873 {
17874 assert(var != NULL);
17875
17876 return SCIPvboundsGetVars(var->vlbs);
17877 }
17878
17879 /** gets array with bounding coefficients b_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
SCIPvarGetVlbCoefs(SCIP_VAR * var)17880 SCIP_Real* SCIPvarGetVlbCoefs(
17881 SCIP_VAR* var /**< problem variable */
17882 )
17883 {
17884 assert(var != NULL);
17885
17886 return SCIPvboundsGetCoefs(var->vlbs);
17887 }
17888
17889 /** gets array with bounding constants d_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
SCIPvarGetVlbConstants(SCIP_VAR * var)17890 SCIP_Real* SCIPvarGetVlbConstants(
17891 SCIP_VAR* var /**< problem variable */
17892 )
17893 {
17894 assert(var != NULL);
17895
17896 return SCIPvboundsGetConstants(var->vlbs);
17897 }
17898
17899 /** gets number of variable upper bounds x <= b_i*z_i + d_i of given variable x */
SCIPvarGetNVubs(SCIP_VAR * var)17900 int SCIPvarGetNVubs(
17901 SCIP_VAR* var /**< problem variable */
17902 )
17903 {
17904 assert(var != NULL);
17905
17906 return SCIPvboundsGetNVbds(var->vubs);
17907 }
17908
17909 /** gets array with bounding variables z_i in variable upper bounds x <= b_i*z_i + d_i of given variable x;
17910 * the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
17911 */
SCIPvarGetVubVars(SCIP_VAR * var)17912 SCIP_VAR** SCIPvarGetVubVars(
17913 SCIP_VAR* var /**< problem variable */
17914 )
17915 {
17916 assert(var != NULL);
17917
17918 return SCIPvboundsGetVars(var->vubs);
17919 }
17920
17921 /** gets array with bounding coefficients b_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
SCIPvarGetVubCoefs(SCIP_VAR * var)17922 SCIP_Real* SCIPvarGetVubCoefs(
17923 SCIP_VAR* var /**< problem variable */
17924 )
17925 {
17926 assert(var != NULL);
17927
17928 return SCIPvboundsGetCoefs(var->vubs);
17929 }
17930
17931 /** gets array with bounding constants d_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
SCIPvarGetVubConstants(SCIP_VAR * var)17932 SCIP_Real* SCIPvarGetVubConstants(
17933 SCIP_VAR* var /**< problem variable */
17934 )
17935 {
17936 assert(var != NULL);
17937
17938 return SCIPvboundsGetConstants(var->vubs);
17939 }
17940
17941 /** gets number of implications y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
17942 * there are no implications for nonbinary variable x
17943 */
SCIPvarGetNImpls(SCIP_VAR * var,SCIP_Bool varfixing)17944 int SCIPvarGetNImpls(
17945 SCIP_VAR* var, /**< active problem variable */
17946 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
17947 )
17948 {
17949 assert(var != NULL);
17950 assert(SCIPvarIsActive(var));
17951
17952 return SCIPimplicsGetNImpls(var->implics, varfixing);
17953 }
17954
17955 /** gets array with implication variables y of implications y <= b or y >= b for x == 0 or x == 1 of given active
17956 * problem variable x, there are no implications for nonbinary variable x;
17957 * the implications are sorted such that implications with binary implied variables precede the ones with non-binary
17958 * implied variables, and as a second criteria, the implied variables are sorted by increasing variable index
17959 * (see SCIPvarGetIndex())
17960 */
SCIPvarGetImplVars(SCIP_VAR * var,SCIP_Bool varfixing)17961 SCIP_VAR** SCIPvarGetImplVars(
17962 SCIP_VAR* var, /**< active problem variable */
17963 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
17964 )
17965 {
17966 assert(var != NULL);
17967 assert(SCIPvarIsActive(var));
17968
17969 return SCIPimplicsGetVars(var->implics, varfixing);
17970 }
17971
17972 /** gets array with implication types of implications y <= b or y >= b for x == 0 or x == 1 of given active problem
17973 * variable x (SCIP_BOUNDTYPE_UPPER if y <= b, SCIP_BOUNDTYPE_LOWER if y >= b),
17974 * there are no implications for nonbinary variable x
17975 */
SCIPvarGetImplTypes(SCIP_VAR * var,SCIP_Bool varfixing)17976 SCIP_BOUNDTYPE* SCIPvarGetImplTypes(
17977 SCIP_VAR* var, /**< active problem variable */
17978 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
17979 )
17980 {
17981 assert(var != NULL);
17982 assert(SCIPvarIsActive(var));
17983
17984 return SCIPimplicsGetTypes(var->implics, varfixing);
17985 }
17986
17987 /** gets array with implication bounds b of implications y <= b or y >= b for x == 0 or x == 1 of given active problem
17988 * variable x, there are no implications for nonbinary variable x
17989 */
SCIPvarGetImplBounds(SCIP_VAR * var,SCIP_Bool varfixing)17990 SCIP_Real* SCIPvarGetImplBounds(
17991 SCIP_VAR* var, /**< active problem variable */
17992 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
17993 )
17994 {
17995 assert(var != NULL);
17996 assert(SCIPvarIsActive(var));
17997
17998 return SCIPimplicsGetBounds(var->implics, varfixing);
17999 }
18000
18001 /** Gets array with unique ids of implications y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
18002 * there are no implications for nonbinary variable x.
18003 * If an implication is a shortcut, i.e., it was added as part of the transitive closure of another implication,
18004 * its id is negative, otherwise it is nonnegative.
18005 */
SCIPvarGetImplIds(SCIP_VAR * var,SCIP_Bool varfixing)18006 int* SCIPvarGetImplIds(
18007 SCIP_VAR* var, /**< active problem variable */
18008 SCIP_Bool varfixing /**< FALSE for implications for x == 0, TRUE for x == 1 */
18009 )
18010 {
18011 assert(var != NULL);
18012 assert(SCIPvarIsActive(var));
18013
18014 return SCIPimplicsGetIds(var->implics, varfixing);
18015 }
18016
18017 /** gets number of cliques, the active variable is contained in */
SCIPvarGetNCliques(SCIP_VAR * var,SCIP_Bool varfixing)18018 int SCIPvarGetNCliques(
18019 SCIP_VAR* var, /**< active problem variable */
18020 SCIP_Bool varfixing /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
18021 )
18022 {
18023 assert(var != NULL);
18024
18025 return SCIPcliquelistGetNCliques(var->cliquelist, varfixing);
18026 }
18027
18028 /** gets array of cliques, the active variable is contained in */
SCIPvarGetCliques(SCIP_VAR * var,SCIP_Bool varfixing)18029 SCIP_CLIQUE** SCIPvarGetCliques(
18030 SCIP_VAR* var, /**< active problem variable */
18031 SCIP_Bool varfixing /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
18032 )
18033 {
18034 assert(var != NULL);
18035
18036 return SCIPcliquelistGetCliques(var->cliquelist, varfixing);
18037 }
18038
18039 /** gets primal LP solution value of variable */
SCIPvarGetLPSol(SCIP_VAR * var)18040 SCIP_Real SCIPvarGetLPSol(
18041 SCIP_VAR* var /**< problem variable */
18042 )
18043 {
18044 assert(var != NULL);
18045
18046 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18047 return SCIPcolGetPrimsol(var->data.col);
18048 else
18049 return SCIPvarGetLPSol_rec(var);
18050 }
18051
18052 /** gets primal NLP solution value of variable */
SCIPvarGetNLPSol(SCIP_VAR * var)18053 SCIP_Real SCIPvarGetNLPSol(
18054 SCIP_VAR* var /**< problem variable */
18055 )
18056 {
18057 assert(var != NULL);
18058
18059 if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE) )
18060 return var->nlpsol;
18061 else
18062 return SCIPvarGetNLPSol_rec(var);
18063 }
18064
18065 /** return lower bound change info at requested position */
SCIPvarGetBdchgInfoLb(SCIP_VAR * var,int pos)18066 SCIP_BDCHGINFO* SCIPvarGetBdchgInfoLb(
18067 SCIP_VAR* var, /**< problem variable */
18068 int pos /**< requested position */
18069 )
18070 {
18071 assert(pos >= 0);
18072 assert(pos < var->nlbchginfos);
18073
18074 return &var->lbchginfos[pos];
18075 }
18076
18077 /** gets the number of lower bound change info array */
SCIPvarGetNBdchgInfosLb(SCIP_VAR * var)18078 int SCIPvarGetNBdchgInfosLb(
18079 SCIP_VAR* var /**< problem variable */
18080 )
18081 {
18082 return var->nlbchginfos;
18083 }
18084
18085 /** return upper bound change info at requested position */
SCIPvarGetBdchgInfoUb(SCIP_VAR * var,int pos)18086 SCIP_BDCHGINFO* SCIPvarGetBdchgInfoUb(
18087 SCIP_VAR* var, /**< problem variable */
18088 int pos /**< requested position */
18089 )
18090 {
18091 assert(pos >= 0);
18092 assert(pos < var->nubchginfos);
18093
18094 return &var->ubchginfos[pos];
18095 }
18096
18097 /** gets the number upper bound change info array */
SCIPvarGetNBdchgInfosUb(SCIP_VAR * var)18098 int SCIPvarGetNBdchgInfosUb(
18099 SCIP_VAR* var /**< problem variable */
18100 )
18101 {
18102 assert(var != NULL);
18103
18104 return var->nubchginfos;
18105 }
18106
18107 /** returns the value based history for the variable */
SCIPvarGetValuehistory(SCIP_VAR * var)18108 SCIP_VALUEHISTORY* SCIPvarGetValuehistory(
18109 SCIP_VAR* var /**< problem variable */
18110 )
18111 {
18112 assert(var != NULL);
18113
18114 return var->valuehistory;
18115 }
18116
18117 /** gets pseudo solution value of variable */
SCIPvarGetPseudoSol(SCIP_VAR * var)18118 SCIP_Real SCIPvarGetPseudoSol(
18119 SCIP_VAR* var /**< problem variable */
18120 )
18121 {
18122 assert(var != NULL);
18123
18124 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18125 return SCIPvarGetBestBoundLocal(var);
18126 else
18127 return SCIPvarGetPseudoSol_rec(var);
18128 }
18129
18130 /** returns the variable's VSIDS score */
SCIPvarGetVSIDS(SCIP_VAR * var,SCIP_STAT * stat,SCIP_BRANCHDIR dir)18131 SCIP_Real SCIPvarGetVSIDS(
18132 SCIP_VAR* var, /**< problem variable */
18133 SCIP_STAT* stat, /**< problem statistics */
18134 SCIP_BRANCHDIR dir /**< branching direction (downwards, or upwards) */
18135 )
18136 {
18137 assert(var != NULL);
18138
18139 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
18140 return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;
18141 else
18142 return SCIPvarGetVSIDS_rec(var, stat, dir);
18143 }
18144
18145 /** includes event handler with given data in variable's event filter */
SCIPvarCatchEvent(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTTYPE eventtype,SCIP_EVENTHDLR * eventhdlr,SCIP_EVENTDATA * eventdata,int * filterpos)18146 SCIP_RETCODE SCIPvarCatchEvent(
18147 SCIP_VAR* var, /**< problem variable */
18148 BMS_BLKMEM* blkmem, /**< block memory */
18149 SCIP_SET* set, /**< global SCIP settings */
18150 SCIP_EVENTTYPE eventtype, /**< event type to catch */
18151 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
18152 SCIP_EVENTDATA* eventdata, /**< event data to pass to the event handler for the event processing */
18153 int* filterpos /**< pointer to store position of event filter entry, or NULL */
18154 )
18155 {
18156 assert(var != NULL);
18157 assert(set != NULL);
18158 assert(var->scip == set->scip);
18159 assert(var->eventfilter != NULL);
18160 assert((eventtype & ~SCIP_EVENTTYPE_VARCHANGED) == 0);
18161 assert((eventtype & SCIP_EVENTTYPE_VARCHANGED) != 0);
18162 assert(SCIPvarIsTransformed(var));
18163
18164 SCIPsetDebugMsg(set, "catch event of type 0x%" SCIP_EVENTTYPE_FORMAT " of variable <%s> with handler %p and data %p\n",
18165 eventtype, var->name, (void*)eventhdlr, (void*)eventdata);
18166
18167 SCIP_CALL( SCIPeventfilterAdd(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );
18168
18169 return SCIP_OKAY;
18170 }
18171
18172 /** deletes event handler with given data from variable's event filter */
SCIPvarDropEvent(SCIP_VAR * var,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTTYPE eventtype,SCIP_EVENTHDLR * eventhdlr,SCIP_EVENTDATA * eventdata,int filterpos)18173 SCIP_RETCODE SCIPvarDropEvent(
18174 SCIP_VAR* var, /**< problem variable */
18175 BMS_BLKMEM* blkmem, /**< block memory */
18176 SCIP_SET* set, /**< global SCIP settings */
18177 SCIP_EVENTTYPE eventtype, /**< event type mask of dropped event */
18178 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
18179 SCIP_EVENTDATA* eventdata, /**< event data to pass to the event handler for the event processing */
18180 int filterpos /**< position of event filter entry returned by SCIPvarCatchEvent(), or -1 */
18181 )
18182 {
18183 assert(var != NULL);
18184 assert(set != NULL);
18185 assert(var->scip == set->scip);
18186 assert(var->eventfilter != NULL);
18187 assert(SCIPvarIsTransformed(var));
18188
18189 SCIPsetDebugMsg(set, "drop event of variable <%s> with handler %p and data %p\n", var->name, (void*)eventhdlr,
18190 (void*)eventdata);
18191
18192 SCIP_CALL( SCIPeventfilterDel(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );
18193
18194 return SCIP_OKAY;
18195 }
18196
18197 /** returns the position of the bound change index */
SCIPbdchgidxGetPos(SCIP_BDCHGIDX * bdchgidx)18198 int SCIPbdchgidxGetPos(
18199 SCIP_BDCHGIDX* bdchgidx /**< bound change index */
18200 )
18201 {
18202 assert(bdchgidx != NULL);
18203
18204 return bdchgidx->pos;
18205 }
18206
18207 /** returns whether first bound change index belongs to an earlier applied bound change than second one */
SCIPbdchgidxIsEarlierNonNull(SCIP_BDCHGIDX * bdchgidx1,SCIP_BDCHGIDX * bdchgidx2)18208 SCIP_Bool SCIPbdchgidxIsEarlierNonNull(
18209 SCIP_BDCHGIDX* bdchgidx1, /**< first bound change index */
18210 SCIP_BDCHGIDX* bdchgidx2 /**< second bound change index */
18211 )
18212 {
18213 assert(bdchgidx1 != NULL);
18214 assert(bdchgidx1->depth >= -2);
18215 assert(bdchgidx1->pos >= 0);
18216 assert(bdchgidx2 != NULL);
18217 assert(bdchgidx2->depth >= -2);
18218 assert(bdchgidx2->pos >= 0);
18219
18220 return (bdchgidx1->depth < bdchgidx2->depth)
18221 || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
18222 }
18223
18224 /** returns whether first bound change index belongs to an earlier applied bound change than second one;
18225 * if a bound change index is NULL, the bound change index represents the current time, i.e. the time after the
18226 * last bound change was applied to the current node
18227 */
SCIPbdchgidxIsEarlier(SCIP_BDCHGIDX * bdchgidx1,SCIP_BDCHGIDX * bdchgidx2)18228 SCIP_Bool SCIPbdchgidxIsEarlier(
18229 SCIP_BDCHGIDX* bdchgidx1, /**< first bound change index, or NULL */
18230 SCIP_BDCHGIDX* bdchgidx2 /**< second bound change index, or NULL */
18231 )
18232 {
18233 assert(bdchgidx1 == NULL || bdchgidx1->depth >= -2);
18234 assert(bdchgidx1 == NULL || bdchgidx1->pos >= 0);
18235 assert(bdchgidx2 == NULL || bdchgidx2->depth >= -2);
18236 assert(bdchgidx2 == NULL || bdchgidx2->pos >= 0);
18237
18238 if( bdchgidx1 == NULL )
18239 return FALSE;
18240 else if( bdchgidx2 == NULL )
18241 return TRUE;
18242 else
18243 return (bdchgidx1->depth < bdchgidx2->depth)
18244 || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
18245 }
18246
18247 /** returns old bound that was overwritten for given bound change information */
SCIPbdchginfoGetOldbound(SCIP_BDCHGINFO * bdchginfo)18248 SCIP_Real SCIPbdchginfoGetOldbound(
18249 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18250 )
18251 {
18252 assert(bdchginfo != NULL);
18253
18254 return bdchginfo->oldbound;
18255 }
18256
18257 /** returns new bound installed for given bound change information */
SCIPbdchginfoGetNewbound(SCIP_BDCHGINFO * bdchginfo)18258 SCIP_Real SCIPbdchginfoGetNewbound(
18259 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18260 )
18261 {
18262 assert(bdchginfo != NULL);
18263
18264 return bdchginfo->newbound;
18265 }
18266
18267 /** returns variable that belongs to the given bound change information */
SCIPbdchginfoGetVar(SCIP_BDCHGINFO * bdchginfo)18268 SCIP_VAR* SCIPbdchginfoGetVar(
18269 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18270 )
18271 {
18272 assert(bdchginfo != NULL);
18273
18274 return bdchginfo->var;
18275 }
18276
18277 /** returns whether the bound change information belongs to a branching decision or a deduction */
SCIPbdchginfoGetChgtype(SCIP_BDCHGINFO * bdchginfo)18278 SCIP_BOUNDCHGTYPE SCIPbdchginfoGetChgtype(
18279 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18280 )
18281 {
18282 assert(bdchginfo != NULL);
18283
18284 return (SCIP_BOUNDCHGTYPE)(bdchginfo->boundchgtype);
18285 }
18286
18287 /** returns whether the bound change information belongs to a lower or upper bound change */
SCIPbdchginfoGetBoundtype(SCIP_BDCHGINFO * bdchginfo)18288 SCIP_BOUNDTYPE SCIPbdchginfoGetBoundtype(
18289 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18290 )
18291 {
18292 assert(bdchginfo != NULL);
18293
18294 return (SCIP_BOUNDTYPE)(bdchginfo->boundtype);
18295 }
18296
18297 /** returns depth level of given bound change information */
SCIPbdchginfoGetDepth(SCIP_BDCHGINFO * bdchginfo)18298 int SCIPbdchginfoGetDepth(
18299 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18300 )
18301 {
18302 assert(bdchginfo != NULL);
18303
18304 return bdchginfo->bdchgidx.depth;
18305 }
18306
18307 /** returns bound change position in its depth level of given bound change information */
SCIPbdchginfoGetPos(SCIP_BDCHGINFO * bdchginfo)18308 int SCIPbdchginfoGetPos(
18309 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18310 )
18311 {
18312 assert(bdchginfo != NULL);
18313
18314 return bdchginfo->bdchgidx.pos;
18315 }
18316
18317 /** returns bound change index of given bound change information */
SCIPbdchginfoGetIdx(SCIP_BDCHGINFO * bdchginfo)18318 SCIP_BDCHGIDX* SCIPbdchginfoGetIdx(
18319 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18320 )
18321 {
18322 assert(bdchginfo != NULL);
18323
18324 return &bdchginfo->bdchgidx;
18325 }
18326
18327 /** returns inference variable of given bound change information */
SCIPbdchginfoGetInferVar(SCIP_BDCHGINFO * bdchginfo)18328 SCIP_VAR* SCIPbdchginfoGetInferVar(
18329 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18330 )
18331 {
18332 assert(bdchginfo != NULL);
18333 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18334 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18335
18336 return bdchginfo->inferencedata.var;
18337 }
18338
18339 /** returns inference constraint of given bound change information */
SCIPbdchginfoGetInferCons(SCIP_BDCHGINFO * bdchginfo)18340 SCIP_CONS* SCIPbdchginfoGetInferCons(
18341 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18342 )
18343 {
18344 assert(bdchginfo != NULL);
18345 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER);
18346 assert(bdchginfo->inferencedata.reason.cons != NULL);
18347
18348 return bdchginfo->inferencedata.reason.cons;
18349 }
18350
18351 /** returns inference propagator of given bound change information, or NULL if no propagator was responsible */
SCIPbdchginfoGetInferProp(SCIP_BDCHGINFO * bdchginfo)18352 SCIP_PROP* SCIPbdchginfoGetInferProp(
18353 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18354 )
18355 {
18356 assert(bdchginfo != NULL);
18357 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18358
18359 return bdchginfo->inferencedata.reason.prop;
18360 }
18361
18362 /** returns inference user information of given bound change information */
SCIPbdchginfoGetInferInfo(SCIP_BDCHGINFO * bdchginfo)18363 int SCIPbdchginfoGetInferInfo(
18364 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18365 )
18366 {
18367 assert(bdchginfo != NULL);
18368 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18369 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18370
18371 return bdchginfo->inferencedata.info;
18372 }
18373
18374 /** returns inference bound of inference variable of given bound change information */
SCIPbdchginfoGetInferBoundtype(SCIP_BDCHGINFO * bdchginfo)18375 SCIP_BOUNDTYPE SCIPbdchginfoGetInferBoundtype(
18376 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18377 )
18378 {
18379 assert(bdchginfo != NULL);
18380 assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
18381 || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);
18382
18383 return (SCIP_BOUNDTYPE)(bdchginfo->inferboundtype);
18384 }
18385
18386 /** returns the relaxed bound change type */
SCIPbdchginfoGetRelaxedBound(SCIP_BDCHGINFO * bdchginfo)18387 SCIP_Real SCIPbdchginfoGetRelaxedBound(
18388 SCIP_BDCHGINFO* bdchginfo /**< bound change to add to the conflict set */
18389 )
18390 {
18391 return ((SCIP_BOUNDTYPE)(bdchginfo->boundtype) == SCIP_BOUNDTYPE_LOWER ? bdchginfo->var->conflictrelaxedlb : bdchginfo->var->conflictrelaxedub);
18392 }
18393
18394
18395 /** returns whether the bound change information belongs to a redundant bound change */
SCIPbdchginfoIsRedundant(SCIP_BDCHGINFO * bdchginfo)18396 SCIP_Bool SCIPbdchginfoIsRedundant(
18397 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18398 )
18399 {
18400 assert(bdchginfo != NULL);
18401 assert(bdchginfo->redundant == (bdchginfo->oldbound == bdchginfo->newbound)); /*lint !e777*/
18402
18403 return bdchginfo->redundant;
18404 }
18405
18406 /** returns whether the bound change has an inference reason (constraint or propagator), that can be resolved */
SCIPbdchginfoHasInferenceReason(SCIP_BDCHGINFO * bdchginfo)18407 SCIP_Bool SCIPbdchginfoHasInferenceReason(
18408 SCIP_BDCHGINFO* bdchginfo /**< bound change information */
18409 )
18410 {
18411 assert(bdchginfo != NULL);
18412
18413 return ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER)
18414 || ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER
18415 && bdchginfo->inferencedata.reason.prop != NULL);
18416 }
18417
18418 /** for two bound change informations belonging to the same variable and bound, returns whether the first bound change
18419 * has a tighter new bound as the second bound change
18420 */
SCIPbdchginfoIsTighter(SCIP_BDCHGINFO * bdchginfo1,SCIP_BDCHGINFO * bdchginfo2)18421 SCIP_Bool SCIPbdchginfoIsTighter(
18422 SCIP_BDCHGINFO* bdchginfo1, /**< first bound change information */
18423 SCIP_BDCHGINFO* bdchginfo2 /**< second bound change information */
18424 )
18425 {
18426 assert(bdchginfo1 != NULL);
18427 assert(bdchginfo2 != NULL);
18428 assert(bdchginfo1->var == bdchginfo2->var);
18429 assert(bdchginfo1->boundtype == bdchginfo2->boundtype);
18430
18431 return (SCIPbdchginfoGetBoundtype(bdchginfo1) == SCIP_BOUNDTYPE_LOWER
18432 ? bdchginfo1->newbound > bdchginfo2->newbound
18433 : bdchginfo1->newbound < bdchginfo2->newbound);
18434 }
18435